diff --git a/CODE/2KEYFBUF.ASM b/CODE/2KEYFBUF.ASM new file mode 100644 index 0000000..2719ac9 --- /dev/null +++ b/CODE/2KEYFBUF.ASM @@ -0,0 +1,4848 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +IDEAL +P386 +MODEL USE32 FLAT +jumps + +;******************************** Includes ********************************* +INCLUDE "gbuffer.inc" +include "profile.inc" + + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +DATASEG + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + + + + + + global C BigShapeBufferStart:dword + global C UseBigShapeBuffer:dword + global C UseOldShapeDraw:dword + global C TheaterShapeBufferStart:dword + global C IsTheaterShape:dword + global C Single_Line_Trans_Entry:near + global C Next_Line:near + global C MMX_Done:near + global C MMXAvailable:dword + global EndNewShapeJumpTable:byte + global NewShapeJumpTable:dword + + +;********************************************************************************** +; +; Jump tables for new line header system +; +; Each line of shape data now has a header byte which describes the data on the line. +; + +; +; Header byte control bits +; +BLIT_TRANSPARENT =1 +BLIT_GHOST =2 +BLIT_FADING =4 +BLIT_PREDATOR =8 +BLIT_SKIP =16 +BLIT_ALL =BLIT_TRANSPARENT or BLIT_GHOST or BLIT_FADING or BLIT_PREDATOR or BLIT_SKIP + + + struc ShapeHeaderType + + draw_flags dd ? + shape_data dd ? + shape_buffer dd ? + + ends + +; +; Global definitions for routines that draw a single line of a shape +; + global Short_Single_Line_Copy:near + global Single_Line_Trans:near + global Single_Line_Ghost:near + global Single_Line_Ghost_Trans:near + global Single_Line_Fading:near + global Single_Line_Fading_Trans:near + global Single_Line_Ghost_Fading:near + global Single_Line_Ghost_Fading_Trans:near + global Single_Line_Predator:near + global Single_Line_Predator_Trans:near + global Single_Line_Predator_Ghost:near + global Single_Line_Predator_Ghost_Trans:near + global Single_Line_Predator_Fading:near + global Single_Line_Predator_Fading_Trans:near + global Single_Line_Predator_Ghost_Fading:near + global Single_Line_Predator_Ghost_Fading_Trans:near + global Single_Line_Skip:near + + global Single_Line_Single_Fade:near + global Single_Line_Single_Fade_Trans:near + + + +label NewShapeJumpTable dword + +; +; Jumptable for shape line drawing with no flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy +label CriticalFadeRedirections dword + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + +; +; Jumptable for shape line drawing routines with fading, transparent and ghost flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + +; +; Jumptable for shape line drawing with predator flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent and predator flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + + +; +; Jumptable for shape line drawing routines with all flags set +; + +label AllFlagsJumpTable dword + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + +label EndNewShapeJumpTable byte + +CODESEG + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + + + + + +;--------------------------------------------------------------------------- + + + + +;********************************************************************************************* +;* Set_Shape_Header -- create the line header bytes for a shape * +;* * +;* INPUT: Shape width * +;* Shape height * +;* ptr to raw shape data * +;* ptr to shape headers * +;* shape flags * +;* ptr to translucency table * +;* IsTranslucent * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/29/95 10:09AM ST : Created. * +;*===========================================================================================* + + proc Setup_Shape_Header C near + + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height :DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG headers :DWORD + ARG flags :DWORD + ARG Translucent :DWORD + ARG IsTranslucent :DWORD + LOCAL trans_count :DWORD + + pushad + + + mov esi,[src] ;ptr to raw shape data + mov edi,[headers] ;ptr to headers we are going to set up + mov eax,[flags] + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST + mov [(ShapeHeaderType edi).draw_flags],eax ;save old flags in header + add edi,size ShapeHeaderType + mov edx,[pixel_height] ;number of shape lines to scan + +??outer_loop: mov ecx,[pixel_width] ;number of pixels in shape line + xor bl,bl ;flag the we dont know anything about this line yet + mov [trans_count],0 ;we havnt scanned any transparent pixels yet + +; +; Scan one shape line to see what kind of data it contains +; +??inner_loop: xor eax,eax + mov al,[esi] + inc esi + +; +; Check for transparent pixel +; + test al,al + jnz ??not_transp + test [flags],SHAPE_TRANS + jz ??not_transp + or bl,BLIT_TRANSPARENT ;flag that pixel is transparent + inc [trans_count] ;keep count of the number of transparent pixels on the line + jmp ??end_lp + +; +; Check for predator effect on this line +; +??not_transp: test [flags],SHAPE_PREDATOR + jz ??not_pred + or bl,BLIT_PREDATOR + +; +; Check for ghost effects +; +??not_pred: test [flags],SHAPE_GHOST + jz ??not_ghost + push edi + mov edi,[IsTranslucent] + cmp [byte edi+eax],-1 + pop edi + jz ??not_ghost + or bl,BLIT_GHOST + +; +; Check if fading is required +; +??not_ghost: test [flags],SHAPE_FADING + jz ??end_lp + or bl,BLIT_FADING + +??end_lp: dec ecx + jnz ??inner_loop + + +; +; Interpret the info we have collected and decide which routine will be +; used to draw this line +; + xor bh,bh + + test bl,BLIT_TRANSPARENT + jz ??no_transparencies + or bh,BLIT_TRANSPARENT + mov ecx,[pixel_width] + cmp ecx,[trans_count] + jnz ??not_all_trans + +; all pixels in the line were transparent so we dont need to draw it at all + mov bh,BLIT_SKIP + jmp ??got_line_type + +??not_all_trans: +??no_transparencies: + mov al,bl + and al,BLIT_PREDATOR + or bh,al + mov al,bl + and al,BLIT_GHOST + or bh,al + mov al,bl + and al,BLIT_FADING + or bh,al + +; +; Save the line header and do the next line +; +??got_line_type:mov [edi],bh + inc edi + + dec edx + jnz ??outer_loop + + + popad + ret + + endp Setup_Shape_Header + + + + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; + macro next_line + + add edi , [ dest_adjust_width ] ;add in dest modulo + dec edx ;line counter + jz ??real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp [dword eax] ;do the jump + + endm + + + + + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Buffer_Frame_To_Page:NEAR + PROC Buffer_Frame_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + + ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + LOCAL header_pointer :DWORD + LOCAL use_old_draw :DWORD + LOCAL save_ecx :DWORD + LOCAL ShapeJumpTableAddress :DWORD + LOCAL shape_buffer_start :DWORD + + + prologue + cmp [ src ] , 0 + jz ??real_out + + cmp [UseOldShapeDraw],0 + jz ??new_system + + mov [use_old_draw],1 + jmp ??do_args + +??new_system: + ; + ; Save the line attributes pointers and + ; Modify the src pointer to point to the actual image + ; + cmp [UseBigShapeBuffer],0 + jz ??do_args ;just use the old shape drawing system + mov edi,[src] + mov [header_pointer],edi + + mov eax,[BigShapeBufferStart] + cmp [(ShapeHeaderType edi).shape_buffer],0 + jz ??is_ordinary_shape + mov eax,[TheaterShapeBufferStart] +??is_ordinary_shape: + mov [shape_buffer_start],eax + + mov edi,[(ShapeHeaderType edi).shape_data] + add edi,[shape_buffer_start] + mov [src],edi + mov [use_old_draw],0 + + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +??check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je ??check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +??check_trans: + test [ flags ] , SHAPE_TRANS + jz ??check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +??check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz ??check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + + +??check_fading: + ;______________________________________________________________________ + ; If this is the first time through for this shape then + ; set up the shape header + ;______________________________________________________________________ + pushad + + cmp [UseBigShapeBuffer],0 ;no big shape buffer so use old system + jz ??new_shape + cmp [UseOldShapeDraw],0 ;use old shape system flag + jnz ??new_shape + + mov edi,[header_pointer] + cmp [(ShapeHeaderType edi).draw_flags],-1 + jz ??setup_headers + mov eax,[flags] ;Redo the shape headers if this shape was + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST ;initially set up with different flags + cmp eax,[(ShapeHeaderType edi).draw_flags] + jz ??no_header_setup +??new_shape: + mov [use_old_draw],1 + jmp ??no_header_setup + +??setup_headers: + push [IsTranslucent] + push [Translucent] + push [flags] + push [header_pointer] + push [src] + push [pixel_height] + push [pixel_width] + call Setup_Shape_Header + add esp,7*4 + mov [ShapeJumpTableAddress],offset AllFlagsJumpTable + jmp ??headers_set +??no_header_setup: + + xor eax,eax + test [flags],SHAPE_PREDATOR + jz ??not_shape_predator + or al,BLIT_PREDATOR + +??not_shape_predator: + test [flags],SHAPE_FADING + jz ??not_shape_fading + or al,BLIT_FADING + +??not_shape_fading: + + test [flags],SHAPE_TRANS + jz ??not_shape_transparent + or al,BLIT_TRANSPARENT + +??not_shape_transparent: + + test [flags],SHAPE_GHOST + jz ??not_shape_ghost + or al,BLIT_GHOST + +??not_shape_ghost: + + shl eax,7 + add eax,offset NewShapeJumpTable + mov [ShapeJumpTableAddress],eax + + +??headers_set: + popad + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz ??check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [ FadingNum ] , eax + + mov ebx,[ShapeJumpTableAddress] + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Single_Fade + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Single_Fade_Trans + cmp eax,1 + jz ??single_fade + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Fading + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Fading_Trans + +??single_fade: + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +??check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz ??check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge ??check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp ??pred_cont + +??check_range: + and eax , PRED_MASK ; keep entries within bounds + +??pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +??pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +??pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] ; add width + add eax , [ (GraphicViewPort esi) . GVPXAdd ] ; add x add + add eax , [ (GraphicViewPort esi) . GVPPitch ] ; extra pitch of DD surface ST - 9/29/95 1:08PM + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge ??pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz ??setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +??setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , [ BufferFrameTable + ebx ] ; get table value + mov [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + + or al , dl + jz ??do_blit + + mov [use_old_draw],1 + test cl , 1000b + jz ??dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + cld + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + +; +; If the shape needs to be clipped then we cant handle it with the new header systen +; so draw it with the old shape drawer +; + cmp [use_old_draw],0 + jnz ??use_old_stuff + + add [header_pointer],size ShapeHeaderType + mov edx,[pixel_height] + mov ecx,[pixel_width] + mov eax,[header_pointer] + mov al,[eax] + mov [save_ecx],ecx + inc [header_pointer] + and eax,BLIT_ALL + shl eax,2 + add eax,[ShapeJumpTableAddress] + jmp [dword eax] + + +??use_old_stuff: + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + + sub eax , [ x_pixel ] + jle ??real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +??real_out: + epilogue + + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** +global BF_Copy:near + +BF_Copy: + prologue + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??forward_loop_dword + + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + epilogue + + ret + + +;******************************************************************** +;******************************************************************** + + segment code page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +;***************************************************************************** +; Draw a single line with transparent pixels +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Trans: + prologue +??slt_mask_map_lp: ; Pentium pipeline usage + ;Pipe Cycles + mov al,[esi] ;U 1 + inc esi ;Vee 1 + + test al,al ;U 1 + jz ??slt_skip ;Vee 1/5 + +??slt_not_trans:mov [edi],al ;u 1 + + inc edi ;vee 1 + dec ecx ;u 1 + + jnz ??slt_mask_map_lp ;vee (maybe) 1 + +??slt_end_line: epilogue + next_line + + align 32 + +??slt_skip: inc edi + dec ecx + jz ??slt_skip_end_line + + mov al,[esi] + inc esi + test al,al + jz ??slt_skip2 + mov [edi],al + inc edi + dec ecx + jnz ??slt_mask_map_lp + + epilogue + next_line + + align 32 + +??slt_skip2: inc edi + dec ecx + jz ??slt_end_line + +; +; If we have hit two transparent pixels in a row then we go into +; the transparent optimised bit +; +??slt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz ??slt_not_trans;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz ??slt_end_line ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp ??slt_round_again + + + +??slt_skip_end_line: + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; +; We have to align the destination for cards that dont bankswitch correctly +; when you write non-aligned data. +; + align 32 +Long_Single_Line_Copy: + prologue + + rept 3 + test edi,3 + jz ??LSLC_aligned + movsb + dec ecx + endm + +??LSLC_aligned: + mov ebx,ecx + + shr ecx,2 + rep movsd + and ebx,3 + jz ??out + movsb + dec bl + jz ??out + movsb + dec bl + jz ??out + movsb +??out: epilogue + next_line + + + +;***************************************************************************** +; Draw a single short line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; + align 32 +Short_Single_Line_Copy: + prologue + cmp ecx,16 + jge Long_Single_Line_Copy + mov ebx,ecx + rep movsb + mov ecx,ebx + epilogue + next_line + + +;***************************************************************************** +; Skip a line of source that is all transparent +; +; 11/29/95 10:21AM - ST +; + + align 32 +Single_Line_Skip: + prologue + add esi,ecx + add edi,ecx + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 +Single_Line_Ghost: + + prologue + xor eax,eax +??slg_loop: mov al,[esi] + mov ebx,[IsTranslucent] + inc esi + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slg_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +??slg_store_pixel: + mov [edi],al + + inc edi + dec ecx + jnz ??slg_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 +Single_Line_Ghost_Trans: + prologue + xor eax,eax +; cmp ecx,3 +; ja ??slgt4 + +??slgt_loop: mov al,[esi] + inc esi + test al,al + jz ??slgt_transparent + +??slgt_not_transparent: + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgt_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +??slgt_store_pixel: + mov [edi],al + inc edi + dec ecx + jnz ??slgt_loop + epilogue + next_line + + + align 32 + +??slgt_transparent: + inc edi ;1 + dec ecx ;2 + jz ??slgt_out ;1 (not pairable) + +??slgt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz ??slgt_not_transparent ;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz ??slgt_out ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp ??slgt_round_again + +??slgt_out: epilogue + next_line + + + +; +; Optimised video memory access version +; + align 32 + +??slgt4: push edx + mov edx,[edi] + + rept 4 + local slgt4_store1 + local slgt4_trans1 + mov al,[esi] + inc esi + test al,al + jz slgt4_trans1 + + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt4_store1 + + and ebx,0ff00h + mov al,dl + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt4_store1: mov dl,al + +slgt4_trans1: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + lea ecx,[ecx+0fffffffch] + cmp ecx,3 + ja ??slgt4 + test ecx,ecx + jnz ??slgt_loop + + epilogue + next_line + + + + + + + + + + +;***************************************************************************** +; Draw a single line with fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Fading: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +??slf_loop: mov al,[esi] + inc esi + + mov ebp,[esp] + +??slf_fade_loop:mov al,[ebx+eax] + dec ebp + jnz ??slf_fade_loop + + mov [edi],al + inc edi + + dec ecx + jnz ??slf_loop + add esp,4 + pop ebp + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Fading_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +??slft_loop: mov al,[esi] + inc esi + test al,al + jz ??slft_transparent + + mov ebp,[esp] + +??slft_fade_loop: + mov al,[ebx+eax] + dec ebp + jnz ??slft_fade_loop + + mov [edi],al +??slft_transparent: + inc edi + + dec ecx + jnz ??slft_loop + add esp,4 + pop ebp + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Single_Fade: + prologue + xor eax,eax + mov ebx,[FadingTable] + +??slsf_loop: mov al,[esi] + mov al,[ebx+eax] + mov [edi],al + inc esi + inc edi + + dec ecx + jnz ??slsf_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Single_Fade_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + +??slsft_loop: mov al,[esi] + inc esi + test al,al + jz ??slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz ??slsft_loop + epilogue + next_line + + align 32 + +??slsft_transparent: + inc edi + + dec ecx + jz ??slsft_next_line + mov al,[esi] + inc esi + test al,al + jz ??slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz ??slsft_loop + +??slsft_next_line: + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with ghosting and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Ghost_Fading: + + prologue + mov [StashECX],ecx + +??SLGF_loop: xor eax,eax + mov al,[esi] + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgf_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +??slgf_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slgf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slgf_fade_loop + + mov [edi],al + inc esi + inc edi + + dec [StashECX] + jnz ??SLGF_loop + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Ghost_Fading_Trans: + prologue + mov [StashECX],ecx + xor eax,eax + +; cmp ecx,3 +; ja ??slgft4 + +??SLGFT_loop: mov al,[esi] + inc esi + test al,al + jz ??slgft_trans_pixel + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgft_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +??slgft_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slgft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slgft_fade_loop + + mov [edi],al +??slgft_trans_pixel: + inc edi + + dec [StashECX] + jnz ??SLGFT_loop + epilogue + next_line + + + align 32 + +??slgft4: push edx + mov edx,[edi] + + rept 4 + local slgft4_fade + local slgft4_fade_lp + local slgft4_trans + mov al,[esi] + inc esi + test al,al + jz slgft4_trans + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft4_fade + + and ebx,0ff00h + + mov al,dl + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft4_fade: mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft4_fade_lp: mov al,[eax+ebx] + dec ecx + jnz slgft4_fade_lp + + mov dl,al + +slgft4_trans: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + sub [StashECX],4 + jz ??slgft4_out + cmp [StashECX],3 + ja ??slgft4 + jmp ??SLGFT_loop + +??slgft4_out: epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator effect +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator: + + prologue + +??slp_loop: mov al,[esi] + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz ??slp_get_pred + + mov [BFPartialCount] , ebx + jmp ??slp_skip_pixel + +??slp_get_pred: xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +??slp_skip_pixel: + inc esi + inc edi + + dec ecx + jnz ??slp_loop + + epilogue + next_line + + + + +;***************************************************************************** +; Draw a single line with transparent pixels and predator effect +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Trans: + + prologue + +??slpt_loop: mov al,[esi] + inc esi + test al,al + jz ??slpt_skip_pixel + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz ??slpt_get_pred + + mov [BFPartialCount] , ebx + jmp ??slpt_skip_pixel + +??slpt_get_pred:xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE PTR BFPredOffset ] , PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +??slpt_skip_pixel: + inc edi + + dec ecx + jnz ??slpt_loop + + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost: + + prologue + +??slpg_loop: mov al,[esi] + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpg_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpg_check_ghost + +??slpg_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax ] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +??slpg_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpg_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpg_store_pixel: + mov [edi],al + inc esi + inc edi + + dec ecx + jnz ??slpg_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Trans: + prologue + +??slpgt_loop: mov al,[esi] + inc esi + test al,al + jz ??slpgt_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpgt_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgt_check_ghost + +??slpgt_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax ] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +??slpgt_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgt_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgt_store_pixel: + mov [edi],al +??slpgt_transparent: + inc edi + + dec ecx + jnz ??slpgt_loop + + pop ecx + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Fading: + + prologue + mov [StashECX],ecx + +??slpf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpf_get_pred + + mov [BFPartialCount],ebx + jmp ??slpf_do_fading + +??slpf_get_pred:xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +??slpf_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slpf_fade_loop + + mov [edi],al + inc edi + + dec [StashECX] + jnz ??slpf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, fading and predator +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Fading_Trans: + prologue + mov [StashECX],ecx + +??slpft_loop: mov al,[esi] + inc esi + test al,al + jz ??slpft_transparent + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpft_get_pred + + mov [BFPartialCount],ebx + jmp ??slpft_do_fading + +??slpft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +??slpft_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slpft_fade_loop + + mov [edi],al +??slpft_transparent: + inc edi + + dec [StashECX] + jnz ??slpft_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Fading: + + prologue + mov [StashECX],ecx + +??slpgf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh , bh + jnz ??slpgf_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgf_check_ghost + +??slpgf_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +??slpgf_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgf_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgf_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpgf_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz ??slpgf_fade_loop + +??slpgf_store_pixel: + mov [edi],al + inc edi + + dec [StashECX] + jnz ??slpgf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Fading_Trans: + + prologue + mov [StashECX],ecx + +??slpgft_loop: mov al,[esi] + inc esi + test al,al + jz ??slpgft_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh , bh + jnz ??slpgft_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgft_check_ghost + +??slpgft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +??slpgft_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgft_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgft_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpgft_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz ??slpgft_fade_loop + +??slpgft_store_pixel: + mov [edi],al +??slpgft_transparent: + inc edi + + dec [StashECX] + jnz ??slpgft_loop + + epilogue + next_line + + + + + ends ;end of strict alignment segment + + codeseg + + + +global BF_Trans:near + +BF_Trans: + + prologue +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ ??trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +??trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +??trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +??trans_reference: + dec ecx + jge ??trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??trans_loop + epilogue + + ret + +;******************************************************************** +;******************************************************************** + +global BF_Ghost:near +BF_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_reference - ??ghost_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_reference: + dec ecx + jge ??ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Trans:near +BF_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_t_reference - ??ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_t_reference: + dec ecx + jge ??ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Fading:near +BF_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_reference - ??fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Fading_Trans:near +BF_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_t_reference - ??fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Fading:near +BF_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_reference - ??ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Fading_Trans:near +BF_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_t_reference - ??ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator:near +BF_Predator: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_reference - ??predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +??predator_reference: + dec ecx + jge ??predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Trans:near +BF_Predator_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_t_reference - ??predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_t_reference: + dec ecx + jge ??predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost:near +BF_Predator_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_reference - ??predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_reference: + dec ecx + jge ??predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Trans:near +BF_Predator_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_t_reference - ??predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_t_reference: + dec ecx + jge ??predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Fading:near +BF_Predator_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_reference - ??predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Fading_Trans:near +BF_Predator_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_t_reference - ??predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Fading:near +BF_Predator_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_reference - ??predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Fading_Trans:near +BF_Predator_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_t_reference - ??predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + + ENDP Buffer_Frame_To_Page +END + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 16, 1992 * +;* * +;* Last Update : October 2, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* Normal_Draw -- Function that writes a normal pixel line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 +IDEAL_MODE EQU 1 + INCLUDE "wwlib.i" + + ;------------------------------------------------------------------- + ; Extern all the library variables that this module requires + ;------------------------------------------------------------------- + + EXTRN C MaskPage:WORD + EXTRN C BackGroundPage:WORD + + ;------------------------------------------------------------------- + ; Define all the equates that this module requires + ;------------------------------------------------------------------- + +WIN_X EQU 0 ; offset for the x coordinate +WIN_Y EQU 2 ; offset for the y coordinate +WIN_WIDTH EQU 4 ; offset for the window width +WIN_HEIGHT EQU 6 ; offset for the window height +BYTESPERROW EQU 320 ; number of bytes per row + +FLAG_NORMAL EQU 0 ; flag for normal draw + +FLAG_GHOST EQU 1 ; This flag enables the ghost +FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent +FLAG_TRANS EQU 4 ; flag for transparent draw +FLAG_PRIORITY EQU 8 ; flag for priority draw + + ; fx on the above flags + +FLAG_MASK EQU 15 ; used to and of uneeded bits + +SHAPE_NORMAL EQU 0000h ; Standard shape. +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally. +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically. +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) + +SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point. +SHAPE_TRANS EQU 0040h ; has transparency + + +;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num). +;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect. +;SHAPE_COMPACT EQU 0400h ; Never use this bit. +SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing. + +SHAPE_GHOST EQU 1000h ; Transluscent table process. +;SHAPE_SHADOW EQU 2000h ; ???? +;SHAPE_PARTIAL EQU 4000h ; ???? +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table). + + +; MBL MOD 12.1.92 + +CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display +CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit +CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits +NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit + +; END MBL MOD + + + CODESEG + + ; 1 = GHOST (all odd entrys are prefixed with Ghost_) + ; 2 = BLAAAH + ; 4 = Trans (prfx) + ; 8 = Prior (prfx) + + +;--------------------------------------------------------------------------- +; Define the table of different line draw types +;--------------------------------------------------------------------------- + +LineTable DW WSA_Normal_Draw ;0 + DW Ghost_Normal_Draw ;1 + DW 0 ;2 + DW 0 ;3 + + DW Transparent_Draw ;4 + DW Ghost_Transparent_Draw ;5 + DW 0 ;6 + DW 0 ;7 + + DW Priority_Draw ;8 + DW Ghost_Priority_Draw ;9 + DW 0 ;10 + DW 0 ;11 + + DW Priority_Transparent_Draw ;12 + DW Ghost_Priority_Transparent_Draw ;13 + DW 0 ;14 + DW 0 ;15 + + + +;*************************************************************************** +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/16/1992 PWG : Created. * +;*=========================================================================* + PUBLIC C Buffer_Frame_To_LogicPage + PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi + + ;------------------------------------------------------------------- + ; Define the arguements that our program takes. + ;------------------------------------------------------------------- + + ARG x_pixel:WORD ; x pixel position to draw at + ARG y_pixel:WORD ; y pixel position to draw at + ARG pixel_w:WORD ; pixel width of draw region + ARG pixel_h:WORD ; pixel height of draw region + ARG win:WORD ; window to clip around + ARG flags:WORD ; flags that this routine will take + ARG buffer:DWORD ; pointer to the buffer with data + ARG args:WORD + + ;------------------------------------------------------------------- + ; Define the local variables that our program uses + ;------------------------------------------------------------------- + + LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table + LOCAL Translucent:DWORD ; ptr to the actual translucent table + + LOCAL win_x1:WORD ; clip window left x pixel position + LOCAL win_x2:WORD ; clip window right x pixel position + LOCAL win_y1:WORD ; clip window top y pixel position + LOCAL win_y2:WORD ; clip window bottom y pixel position + LOCAL clipleft:WORD ; number of pixels to clip on left + LOCAL clipright:WORD ; number of pixels to clip on right + LOCAL nextline:WORD ; offset to the next line + LOCAL putmiddle:WORD ; routine to call to put the middle + LOCAL maskpage:WORD ; location of the depth masks + LOCAL background:WORD ; location of the background data + LOCAL jflags:WORD ; location of the background data + + LOCAL priority:BYTE ; the priority level of the back + + push fs + + xor ecx,ecx + + ;-------------------------------------------------------------------- + ; Check to see if we have supplied any GHOST tables. + ;-------------------------------------------------------------------- + push di + + mov di,6 + mov [jflags],0 + +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz short ??no_ghost ; if not then skip and do more + + or [jflags],FLAG_GHOST + + les ax,[DWORD PTR buffer + di] + + ; get the "are we really translucent?" table + mov [WORD PTR IsTranslucent],ax + mov [WORD PTR IsTranslucent + 2],es + add ax,0100h ; add to offset for tables + + ; get the "ok we are translucent!!" table + mov [WORD PTR Translucent],ax + mov [WORD PTR Translucent + 2],es + + add di,4 + +??no_ghost: + + pop di + + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [flags],SHAPE_CENTER ; does this need to be centered? + je short ??no_centering ; if not the skip over this stuff + + mov ax,[pixel_w] + mov bx,[pixel_h] + sar ax,1 + sar bx,1 + sub [x_pixel],ax + sub [y_pixel],bx + +??no_centering: + mov ax,[flags] + and ax,SHAPE_PRIORITY+SHAPE_TRANS + cmp ax,SHAPE_PRIORITY+SHAPE_TRANS + jne short ??test_trans + + or [jflags],FLAG_PRIORITY_TRANS + jmp short ??priority + + ;------------------------------------------------------------------- + ; Get the trans information if we need to get it + ;------------------------------------------------------------------- +??test_trans: + test [flags],SHAPE_TRANS ; does this draw use transparencies? + je short ??test_priority ; if not the skip over this junk + + or [jflags],FLAG_TRANS + +??test_priority: + ;------------------------------------------------------------------- + ; Get the priority information if we need to get it + ;------------------------------------------------------------------- + test [flags],SHAPE_PRIORITY ; does this draw use priorities? + je short ??no_priority ; if not the skip over this junk + + or [jflags],FLAG_PRIORITY + +??priority: + mov ax,[BackGroundPage] ; get the background page from ds + mov [background],ax ; and store it on the stack + mov ax,[MaskPage] ; get the mask page from ds + mov [maskpage],ax ; and store it on the stack + mov ax,[WORD PTR buffer + 4]; get the priority level from args + mov [priority],al ; and store it in a local + + ;------------------------------------------------------------------- + ; Get the draw routine that we are going to draw with + ;------------------------------------------------------------------- +??no_priority: +; mov bx,[flags] ; load in the current flags byte +; and bx,FLAG_MASK ; prevent lockup on bad value + mov bx,[jflags] ; load in the jump table flags + shl bx,1 + mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table + mov [putmiddle],ax ; store off the new offset + + ;------------------------------------------------------------------- + ; Get a pointer to the logic page to where we will draw our buffer + ;------------------------------------------------------------------- + push [LogicPage] ; push the current logic page + call FAR PTR Get_Page ; get the physical page address + add sp,2 ; pull the parameter from the stack + mov es,dx ; store the address in the dest + + ;-------------------------------------------------------------------- + ; Point DI to the beginning of the window that we need to look at. + ; that way we can access all of the info through di. + ;-------------------------------------------------------------------- + mov si,OFFSET WindowList ; get the offset of the window list + mov cl,4 ; shift 3 times = multiply by 16 + mov ax,[win] ; get the window number we are using + shl ax,cl ; each window is 8 words long + add si,ax ; add that into the offset of window + + ;-------------------------------------------------------------------- + ; Place all the clipping values on the stack so our function will + ; be truly re-entrant and will not need to shadow these values. + ;-------------------------------------------------------------------- + mov cl,3 ; to convert x to pixel mult by 8 + mov ax,[si + WIN_X] ; get the left clip position + shl ax,cl ; convert to a pixel x position + mov [win_x1],ax ; store the left edge of window + mov [win_x2],ax + + mov ax,[si + WIN_WIDTH] ; get the width of the window + shl ax,cl ; convert to a pixel width + add [win_x2],ax ; add to get the right window edge + + mov ax,[si + WIN_Y] ; get the win y coordinate to clip + mov [win_y1],ax ; and save it onto the stack + + add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord + mov [win_y2],ax ; and save it onto the stack + + test [flags],SHAPE_WIN_REL ; is this window relative? + je short ??get_buffer ; if not the skip over + + mov ax,[win_x1] ; get left edge of window + add [x_pixel],ax ; add to x pixel position + mov ax,[win_y1] ; get top edge of window + add [y_pixel],ax ; add to y pixel position + + ;-------------------------------------------------------------------- + ; Get a pointer to the source buffer so we can handle the clipping + ;-------------------------------------------------------------------- +??get_buffer: + lds si,[buffer] ; get a pointer to the buffer + + ;-------------------------------------------------------------------- + ; Check the top of our shape and clip any lines that are necessary + ;-------------------------------------------------------------------- + mov ax,[y_pixel] ; get the y_pixel draw position + sub ax,[win_y1] ; subtract out the window y top + jns short ??check_bottom ; skip if y below window top + add ax,[pixel_h] ; add in the height of the region + jg short ??clip_top ; if positive then clip top lines + +??jump_exit: + jmp ??exit ; otherwise completely clipped + +??clip_top: + xchg [pixel_h],ax + sub ax,[pixel_h] + add [y_pixel],ax + mul [pixel_w] ; convert to number of bytes to skip + add si,ax ; skip past the necessary bytes + + ;-------------------------------------------------------------------- + ; Check the bottom of our shape and clip it if necessary + ;-------------------------------------------------------------------- +??check_bottom: + mov ax,[win_y2] ; get the bottom y of the window + sub ax,[y_pixel] ; subtract of the y to draw at + js ??jump_exit ; if its signed then nothing to draw + jz ??jump_exit ; if its zero then nothing to draw + + cmp ax,[pixel_h] ; if more room to draw then height + jae short ??clip_x_left ; then go check the left clip + mov [pixel_h],ax ; clip all but amount that will fit + +??clip_x_left: + mov [clipleft],0 ; clear clip on left of region + mov ax,[x_pixel] ; get the pixel x of draw region + sub ax,[win_x1] ; pull out the window coordinate + jns short ??clip_x_right + neg ax ; negate to get amnt to skip in buf + mov [clipleft],ax ; store it in the left clip info + add [x_pixel],ax ; move to the edge of the window + sub [pixel_w],ax ; pull it out of the pixel width + +??clip_x_right: + mov [clipright],0 ; clear clip on right of region + mov ax,[win_x2] ; get the window x of clip region + sub ax,[x_pixel] ; subtract the draw edge of region + js ??jump_exit ; if its negative then get out + jz ??jump_exit ; if its zero then get out + + cmp ax,[pixel_w] ; is space available larger than w + jae short ??draw_prep ; if so then go get drawing + + + xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax) + sub ax,[pixel_w] ; pull out the amount to draw + mov [clipright],ax ; this is the amount to clip on right + +??draw_prep: + push si ; save off source pos in buffer + push ds ; both offset and segment + mov ax,@data + mov ds,ax + mov bx,[y_pixel] + shl bx,1 ; shift left by 1 for word table look + lds si,[YTable] ; get the address of the ytable + mov di,[ds:si+bx] ; look up the multiplied value + pop ds ; restore source pos in buffer + pop si ; both offset and segment + + add di,[x_pixel] ; add in the x pixel position + mov [nextline],di ; save it off in the next line + + ;-------------------------------------------------------------------- + ; Now determine the type of the shape and process it in the proper + ; way. + ;-------------------------------------------------------------------- + mov dx,[pixel_h] + + ; Check to see if the WSA is the screen width and there is no + ; clipping. In this case, then a special single call to the + ; line processing routine is possible. + mov ax,[clipleft] + add ax,[clipright] + jne short ??top_of_loop + cmp [pixel_w],BYTESPERROW + jne short ??top_of_loop + + ;------------------------------------ + ; The width of the WSA is the screen width, so just process as + ; one large WSA line. + mov ax,BYTESPERROW + imul dx + mov cx,ax + call [putmiddle] + jmp short ??exit + + ;------------------------------------ + ; Process line by line. +??top_of_loop: + add si,[clipleft] ; skip whats necessary on left edge + mov cx,[pixel_w] ; get the width we need to draw + + ; Copy the source to the destination as appropriate. This routine can + ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past + ; the end of the data. + call [putmiddle] + + add si,[clipright] ; skip past the left clip + add [nextline],BYTESPERROW + mov di,[nextline] + + dec dx + jnz ??top_of_loop + +??exit: + pop fs + ret + ENDP + + +;*************************************************************************** +;* NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;*=========================================================================* + + PROC NOLANGUAGE WSA_Normal_Draw NEAR + + IF 1 + ; This version is marginally faster than the later version. + mov ax,cx + shr cx,2 + rep movsd + and ax,011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + ret + + ELSE + + shr cx,1 ; convert to words (odd pix in carry) + rep movsw ; write out the needed words + adc cx,0 ; add the carry into cx + rep movsb ; write out the odd byte if any + ret + ENDIF + + ENDP + + +;*************************************************************************** +;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 10/02/1994 JLB : Optimized for 250% speed improvement. * +;*=========================================================================* + PROC NOLANGUAGE Transparent_Draw NEAR + + IF 1 + ; Preserve DX since it is used as a scratch register. + push dx + +??loop: + ; Swap DS:SI and ES:DI back in preparation for the REP SCASB + ; instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Remember the bytes remaining in order to calculate the position + ; of the scan when it stops. + mov bx,cx + + ; Scan looking for a non-zero value in the source buffer. + xor al,al + repe scasb + + ; When the loop ends, if the EQ flag is set then the scanning is + ; complete. Jump to the end of the routine in order to fixup the + ; pointers. + je short ??fini + + ; Advance the destination pointer by the amount necessary to match + ; the source movement. DS:SI points to where data should be written. + add si,bx + inc cx ; SCASB leaves CX one too low, fix it. + dec di ; SCASB leaves DI one byte too far, fix it. + sub si,cx + + ; Scan for the duration of non-zero pixels. This yields a count which + ; is used to copy the source data to the destination. Preserve DI. + mov dx,di + mov bx,cx + repne scasb + mov di,dx + + ; Set BX to equal the number of bytes to copy from source to dest. + inc cx ; SCASB leaves CX one too low, fix it. + sub bx,cx + + ; Move the data from ES:DI to DS:SI for BX bytes. + xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining. + + ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Move the data from source to dest. First try to move double + ; words. Then copy the remainder bytes (if any). Putting jumps in + ; this section doesn't result in any savings -- oh well. + mov ax,cx + shr cx,2 + rep movsd + and ax,0011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + + ; Restore CX with the remaining bytes to process. + mov cx,bx + + ; If there are more bytes to process, then loop back. + or cx,cx + jne short ??loop + +??fini: + ; Swap ES:DI and DS:SI back to original orientation. + mov ax,ds + mov bx,es + mov es,ax + mov ds,bx + xchg di,si + + ; Restore DX and return. + pop dx + ret + + ELSE + +??loop_top: + lodsb + or al,al + jz short ??skip + + mov [es:di],al ; store the pixel to the screen +??skip: + inc di + loop ??loop_top + ret + + ENDIF + + ENDP + + +;*************************************************************************** +;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of * +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are * +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + + mov bl,[gs:di] ; get the mask byte for our pixel + + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + + PROC NOLANGUAGE Ghost_Normal_Draw NEAR + +??loop_top: + lodsb + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Transparent_Draw NEAR + +??loop_top: + lodsb + or al,al + jz short ??skip + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron* +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they* +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + mov bl,[gs:di] ; get the mask byte for our pixel + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + + ret + + ENDP + + END diff --git a/CODE/2KEYFRAM.CPP b/CODE/2KEYFRAM.CPP new file mode 100644 index 0000000..9a02ab6 --- /dev/null +++ b/CODE/2KEYFRAM.CPP @@ -0,0 +1,583 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/2KEYFRAM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + +#define INITIAL_BIG_SHAPE_BUFFER_SIZE 8000000 +#define THEATER_BIG_SHAPE_BUFFER_SIZE 4000000 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char *BigShapeBufferStart = NULL; + char *TheaterShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = FALSE; + bool IsTheaterShape = false; +} +#ifdef FIXIT_SCORE_CRASH +/* +** Global required to fix the score screen crash bug by allowing disabling of uncompressed shapes. +*/ +bool OriginalUseBigShapeBuffer = false; +#endif //FIXIT +char *BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; + +char *TheaterShapeBufferPtr = NULL; +int TotalTheaterShapes = 0; + + +#define MAX_SLOTS 1500 +#define THEATER_SLOT_START 1000 + +char **KeyFrameSlots [MAX_SLOTS]; +int TotalSlotsUsed=0; +int TheaterSlotsUsed = THEATER_SLOT_START; + + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char *shape_data; + int shape_buffer; //1 if shape is in theater buffer +} ShapeHeaderType; + +static int Length; + +void *Get_Shape_Header_Data(void *ptr) +{ + if (UseBigShapeBuffer) { + + ShapeHeaderType *header = (ShapeHeaderType*) ptr; + return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) ); + + } else { + return (ptr); + } +} + +int Get_Last_Frame_Length(void) +{ + return(Length); +} + + +void Reset_Theater_Shapes (void) +{ + /* + ** Delete any previously allocated slots + */ + for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE; +#ifdef FIXIT_SCORE_CRASH + /* + ** Keep track of our original decision about whether to use cached shapes. + ** This is needed for the score screen crash fix. + */ + OriginalUseBigShapeBuffer = UseBigShapeBuffer; +#endif //FIXIT +} + + + + + +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr) +{ +#ifdef FIXIT_SCORE_CRASH + char *ptr; + unsigned long offcurr, offdiff; +#else + char *ptr, *lockptr;//, *uncomp_ptr; + unsigned long offcurr, off16, offdiff; +#endif + unsigned long offset[SUBFRAMEOFFS]; + KeyFrameHeaderType *keyfr; + unsigned short buffsize, currframe, subframe; + unsigned long length = 0; + char frameflags; + unsigned long return_value; + char *temp_shape_ptr; + + // + // valid pointer?? + // + Length = 0; + if ( !dataptr || !buffptr ) { + return(0); + } + + // + // look at header then check that frame to build is not greater + // than total frames + // + keyfr = (KeyFrameHeaderType *) dataptr; + + if ( framenumber >= keyfr->frames ) { + return(0); + } + + + if (UseBigShapeBuffer) { + /* + ** If we havnt yet allocated memory for uncompressed shapes then do so now. + ** + */ + if (!BigShapeBufferStart) { + BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL); + BigShapeBufferPtr = BigShapeBufferStart; + + /* + ** Allocate memory for theater specific uncompressed shapes + */ + TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL); + TheaterShapeBufferPtr = TheaterShapeBufferStart; + } + + /* + ** If we are running out of memory (<10k left) for uncompressed shapes + ** then allocate some more. + */ + if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128000) { + ReallocShapeBufferFlag = TRUE; + } + + /* + ** If this animation was not previously uncompressed then + ** allocate memory to keep the pointers to the uncompressed data + ** for these animation frames + */ + if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER) { + keyfr->x = UNCOMPRESS_MAGIC_NUMBER; + if (IsTheaterShape) { + keyfr->y = TheaterSlotsUsed; + TheaterSlotsUsed++; + } else { + keyfr->y = TotalSlotsUsed; + TotalSlotsUsed++; + } + /* + ** Allocate and clear the memory for the shape info + */ + KeyFrameSlots[keyfr->y]= new char *[keyfr->frames]; + memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4); + } + + /* + ** If this frame was previously uncompressed then just return + ** a pointer to the raw data + */ + if (*(KeyFrameSlots[keyfr->y]+framenumber)) { + if (IsTheaterShape) { + return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } else { + return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } + } + } + + // calc buff size + buffsize = keyfr->width * keyfr->height; + + // get offset into data + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], 12L ); + frameflags = (char)(offset[0] >> 24); + + + if ( (frameflags & KF_KEYFRAME) ) { + + ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { // key delta or delta + + if ( (frameflags & KF_DELTA) ) { + currframe = (unsigned short)offset[1]; + + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + } + + // key frame + offcurr = offset[1] & 0x00FFFFFFL; + + // key delta + offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; + + ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + +#ifndef FIXIT_SCORE_CRASH + off16 = (unsigned long)lockptr & 0x00003FFFL; +#endif + + length = LCW_Uncompress( ptr, buffptr, buffsize ); + + if (length > buffsize) { + return(0); + } + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + if ( (frameflags & KF_DELTA) ) { + // adjust to delta after the keydelta + + currframe++; + subframe = 2; + + while (currframe <= framenumber) { + offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + currframe++; + subframe += 2; + + if ( subframe >= (SUBFRAMEOFFS - 1) && + currframe <= framenumber ) { + Mem_Copy( Add_Long_To_Pointer( dataptr, + (((unsigned long)currframe << 3) + + sizeof(KeyFrameHeaderType)) ), + &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + subframe = 0; + } + } + } + } + + + if (UseBigShapeBuffer) { + /* + ** Save the uncompressed shape data so we dont have to uncompress it + ** again next time its drawn. + ** We keep a space free before the raw shape data so we can add line + ** header info before the shape is drawn for the first time + */ + + if (IsTheaterShape) { + /* + ** Shape is a theater specific shape + */ + return_value = (unsigned long) TheaterShapeBufferPtr; + temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr) { + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart; + TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + /* + ** Align the next shape + */ + if (3 & (unsigned)TheaterShapeBufferPtr) { + TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + + } else { + + + return_value=(unsigned long)BigShapeBufferPtr; + temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr) { + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart; + BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + // Align the next shape + if (3 & (unsigned)BigShapeBufferPtr) { + BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + } + + } else { + return ((unsigned long)buffptr); + } +} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} diff --git a/CODE/2SUPPORT.ASM b/CODE/2SUPPORT.ASM new file mode 100644 index 0000000..4b1499d --- /dev/null +++ b/CODE/2SUPPORT.ASM @@ -0,0 +1,564 @@ +; +; 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 . +; + +; $Header: F:\projects\c&c0\vcs\code\2support.asv 5.0 11 Nov 1996 09:40:36 JOE_BOSTIC $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + GLOBAL C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mov ecx,[(GraphicViewPort ebx).GVPWidth] + add ecx,[(GraphicViewPort ebx).GVPXAdd] + add ecx,[(GraphicViewPort ebx).GVPPitch] + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + mov edx,ecx + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +if 0 + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + GLOBAL C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + GLOBAL C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + GLOBAL C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + GLOBAL C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX +endif + + + DATASEG + +TabA DD 6949350 + DD 4913933 + DD 3474675 + DD 2456966 + DD 1737338 + DD 1228483 + DD 868669 + DD 614242 + DD 434334 + DD 307121 + DD 217167 + DD 153560 + DD 108584 + DD 76780 + DD 54292 + DD 38390 + DD 27146 + DD 19195 + DD 13573 + DD 9598 + DD 6786 + DD 4799 + DD 3393 + DD 2399 + DD 1697 + DD 1200 + DD 848 + DD 600 + DD 424 + DD 300 + DD 212 + DD 150 + DD 106 + +TabB DD 154 + DD 218 + DD 309 + DD 437 + DD 618 + DD 874 + DD 1236 + DD 1748 + DD 2472 + DD 3496 + DD 4944 + DD 6992 + DD 9888 + DD 13983 + DD 19775 + DD 27967 + DD 39551 + DD 55933 + DD 79101 + DD 111866 + DD 158203 + DD 223732 + DD 316405 + DD 447465 + DD 632811 + DD 894929 + DD 1265621 + DD 1789859 + DD 2531243 + DD 3579718 + DD 5062486 + DD 7159436 + DD 10124971 + + CODESEG + +;*********************************************************************************************** +;* Square_Root -- Finds the square root of the fixed pointer parameter. * +;* * +;* INPUT: val -- The fixed point (16:16) value to find the square root of. * +;* * +;* OUTPUT: Returns with the square root of the fixed pointer parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/04/1995 JLB : Adapted. * +;*=============================================================================================*/ +;unsigned Square_Root(unsigned val); + GLOBAL C Square_Root :NEAR + PROC Square_Root C near + USES ebx,edx + + bsr ebx,eax + jz ??zero + + mul [DWORD 4*ebx + OFFSET TabA] + shrd eax,edx,10h + add eax, [4*ebx + OFFSET TabB] +??zero: + ret + + ENDP Square_Root + +;---------------------------------------------------------------------------- + + END + diff --git a/CODE/2TXTPRNT.ASM b/CODE/2TXTPRNT.ASM new file mode 100644 index 0000000..af53028 --- /dev/null +++ b/CODE/2TXTPRNT.ASM @@ -0,0 +1,507 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* MCGA_Print -- Assembly MCGA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +GLOBAL C Buffer_Print : NEAR + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL C FontPtr:DWORD +GLOBAL C FontXSpacing:DWORD +GLOBAL C FontYSpacing:DWORD +GLOBAL C ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + CODESEG + + +;*************************************************************************** +;* MCGA_PRINT -- Assembly MCGA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Buffer_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch of direct draw surface + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Buffer_Print +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Get_Font_Palette_Ptr:NEAR + PROC Get_Font_Palette_Ptr C near + mov eax, OFFSET ColorXlat + ret + ENDP Get_Font_Palette_Ptr + + +END diff --git a/CODE/AADATA.CPP b/CODE/AADATA.CPP new file mode 100644 index 0000000..222406d --- /dev/null +++ b/CODE/AADATA.CPP @@ -0,0 +1,655 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/AADATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste* + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class* + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft obj* + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * AircraftTypeClass::LRotorData = NULL; +void const * AircraftTypeClass::RRotorData = NULL; + +// Badger bomber +static AircraftTypeClass const BadgerPlane( + AIRCRAFT_BADGER, // What kind of aircraft is this. + TXT_BADGER, // Translated text number for aircraft. + "BADR", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Photo recon plane. +static AircraftTypeClass const U2Plane( + AIRCRAFT_U2, // What kind of aircraft is this. + TXT_U2, // Translated text number for aircraft. + "U2", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Mig attack aircraft. +static AircraftTypeClass const MigPlane( + AIRCRAFT_MIG, // What kind of aircraft is this. + TXT_MIG, // Translated text number for aircraft. + "MIG", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xC0, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Yak attack aircraft. +static AircraftTypeClass const YakPlane( + AIRCRAFT_YAK, // What kind of aircraft is this. + TXT_YAK, // Translated text number for aircraft. + "YAK", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Transport helicopter. +static AircraftTypeClass const TransportHeli( + AIRCRAFT_TRANSPORT, // What kind of aircraft is this. + TXT_TRANS, // Translated text number for aircraft. + "TRAN", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + true, // Custom rotor sets for each facing? + true, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Longbow attack helicopter +static AircraftTypeClass const AttackHeli( + AIRCRAFT_LONGBOW, // What kind of aircraft is this. + TXT_HELI, // Translated text number for aircraft. + "HELI", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +// Hind +static AircraftTypeClass const OrcaHeli( + AIRCRAFT_HIND, // What kind of aircraft is this. + TXT_ORCA, // Translated text number for aircraft. + "HIND", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +/*********************************************************************************************** + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * * + * This is the constructor for the aircraft object. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass::AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder + ) : + TechnoTypeClass(RTTI_AIRCRAFTTYPE, + int(airtype), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + false, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + false, + false, + true, + true, + rotation, + SPEED_WINGED), + IsFixedWing(is_fixedwing), + IsLandable(is_landable), + IsRotorEquipped(is_rotorequipped), + IsRotorCustom(is_rotorcustom), + Type(airtype), + Mission(deforder), + Building(building), + LandingSpeed(landingspeed) +{ + /* + ** Forced aircraft overrides from the default. + */ + Speed = SPEED_WINGED; +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * * + * This will allocate an aircraft type class object from the memory pool of that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated aircraft type class object. If there * + * was insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AircraftTypeClass::operator new(size_t) +{ + return(AircraftTypes.Alloc()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * * + * This will return the aircraft type class object back to the special memory pool that * + * it was allocated from. * + * * + * INPUT: pointer -- Pointer to the aircraft type class object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::operator delete(void * pointer) +{ + AircraftTypes.Free((AircraftTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * * + * This will initialize the aircraft type class heap by pre-allocating all known aircraft * + * types. It should be called once and before the rules.ini file is processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Init_Heap(void) +{ + /* + ** These aircraft type class objects must be allocated in the exact order that they + ** are specified in the AircraftSmen enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AircraftTypeClass(TransportHeli); + new AircraftTypeClass(BadgerPlane); + new AircraftTypeClass(U2Plane); + new AircraftTypeClass(MigPlane); + new AircraftTypeClass(YakPlane); + new AircraftTypeClass(AttackHeli); + new AircraftTypeClass(OrcaHeli); +} + + +/*********************************************************************************************** + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * * + * This routine is used to convert an ASCII representation of an aircraft into the * + * matching aircraft type number. This is used by the scenario INI reader code. * + * * + * INPUT: name -- Pointer to ASCII name to translate. * + * * + * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no * + * match could be found, then AIRCRAFT_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftType AircraftTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (AircraftType classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(AIRCRAFT_NONE); +} + + +/*********************************************************************************************** + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. * + * * + * This routine is used to perform the onetime initialization of the aircraft type. This * + * includes primarily the shape and other graphic data loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This goes to disk and also must only be called ONCE. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::One_Time(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + AircraftTypeClass const & uclass = As_Reference(index); + + /* + ** Fetch the supporting data files for the unit. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + } + + LRotorData = MFCD::Retrieve("LROTOR.SHP"); + RRotorData = MFCD::Retrieve("RROTOR.SHP"); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * * + * This routine is used to create an aircraft object that matches the aircraft type. It * + * serves as a shortcut to creating an object using the "new" operator and "if" checks. * + * * + * INPUT: house -- The house owner of the aircraft that is to be created. * + * * + * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be * + * created, then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new AircraftClass(Type, house->Class->House)); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * * + * This routine is used by the scenario editor to prepare for the adding operation. It * + * builds a list of pointers to object types that can be added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Prep_For_Add(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * * + * This routine is used by the scenario editor to display a generic version of the object * + * type. This is displayed in the object selection dialog box. * + * * + * INPUT: x,y -- The coordinates to draw the aircraft at (centered). * + * * + * window -- The window to base the coordinates upon. * + * * + * house -- The owner of this generic aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} +#endif + + +/*********************************************************************************************** + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * * + * This determines the occupation list for the aircraft (if it was landed). * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. * + * * + * WARNINGS: This occupation list is only valid if the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * * + * This routine figures out the overlap list for the aircraft as if it were landed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell offset overlap list for the aircraft. * + * * + * WARNINGS: This overlap list is only valid when the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Overlap_List(void) const +{ + static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * * + * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is * + * the maximum number of passengers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips for this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Max_Pips(void) const +{ + if (PrimaryWeapon != NULL) { + return(5); + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system * + * * + * This routine is used to create and place an aircraft through the normal game system. * + * Since creation of aircraft in this fashion is prohibited, this routine does nothing. * + * * + * INPUT: na * + * * + * OUTPUT: Always returns a failure code (false). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const +{ + return(false); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * * + * This routine will fetch the pixel dimensions of this aircraft type. These dimensions * + * are used to control map refresh and select box rendering. * + * * + * INPUT: width -- Reference to variable that will be filled in with aircraft width. * + * * + * height -- Reference to variable that will be filled in with aircraft height. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Dimensions(int &width, int &height) const +{ + if (Type == AIRCRAFT_BADGER) { + width = 56; + height = 56; + } else { + width = 21; + height = 20; + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * * + * This routine is used to fetch a reference to the aircraft type class object that matches * + * the aircraft type specified. * + * * + * INPUT: aircraft -- The aircraft type to fetch a reference to the type class object of. * + * * + * OUTPUT: Returns with a reference to the type class object of this aircraft type. * + * * + * WARNINGS: Be sure that the aircraft type specified is legal. Illegal values will result * + * in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass & AircraftTypeClass::As_Reference(AircraftType aircraft) +{ + return(*AircraftTypes.Ptr(aircraft)); +} diff --git a/CODE/ABSTRACT.CPP b/CODE/ABSTRACT.CPP new file mode 100644 index 0000000..3744722 --- /dev/null +++ b/CODE/ABSTRACT.CPP @@ -0,0 +1,216 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ABSTRACT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ABSTRACT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * AbstractClass::Distance -- Determines distance to target. * + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * * + * This debug only routine will display various information about this abstract class * + * object to the monochrome screen specified. * + * * + * INPUT: mono -- Pointer to the monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +void AbstractClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(11, 5);mono->Printf("%08X", As_Target()); + mono->Set_Cursor(20, 1);mono->Printf("%08X", Coord); + mono->Set_Cursor(29, 1);mono->Printf("%3d", Height); + if (Owner() != HOUSE_NONE) { + mono->Set_Cursor(1, 3); + mono->Printf("%-18s", Text_String(HouseTypeClass::As_Reference(Owner()).FullName)); + } +} +#endif + + +/*********************************************************************************************** + * AbstractClass::Distance -- Determines distance to target. * + * * + * This will determine the distance (direct line) to the target. The distance is in * + * 'leptons'. This routine is typically used for weapon range checks. * + * * + * INPUT: target -- The target to determine range to. * + * * + * OUTPUT: Returns with the range to the specified target (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1994 JLB : Created. * + *=============================================================================================*/ +int AbstractClass::Distance(TARGET target) const +{ + /* + ** Should subtract a fudge-factor distance for building targets. + */ + BuildingClass * obj = As_Building(target); + int dist = Distance(As_Coord(target)); + + /* + ** If the object is a building the adjust it by the average radius + ** of the object. + */ + if (obj) { + dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + if (dist < 0) dist = 0; + } + + /* + ** Return the distance to the target + */ + return(dist); +} + + +/*********************************************************************************************** + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * * + * This is the constructor for AbstractTypeClass objects. It initializes the INI name and * + * the text name for this object type. * + * * + * INPUT: name -- Text number for the full name of the object. * + * * + * ini -- The ini name for this object type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +AbstractTypeClass::AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini) : + RTTI(rtti), + ID(id), + FullName(name) +{ + strncpy((char *)IniName, ini, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; +} + + +/*********************************************************************************************** + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * * + * This routine is called when the placement coordinate should be fixed up according * + * to any special rules specific to this object type. At this level, no transformation * + * occurs. Derived classes will transform the coordinate as necessary. * + * * + * INPUT: coord -- The proposed coordinate that this object type will be placed down at. * + * * + * OUTPUT: Returns with the adjusted coordinate that the object should be placed down at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return(coord); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * * + * This routine is used to fetch the full name of this object type. The name value * + * returned is actually the index number into the text array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name index number for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Full_Name(void) const +{ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == ((RTTI+1) * 100) + ID) { + return(-(index+1)); + } + } +#endif + return(FullName); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * * + * This returns a bit flag that indicates which houses are allowed to own this object * + * type. At this level, all houses have ownership rights. This routine will be overridden * + * by object types that restrict ownership. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a bit flag indicating which houses have ownership rights. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Get_Ownable(void) const +{ + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + + diff --git a/CODE/ABSTRACT.H b/CODE/ABSTRACT.H new file mode 100644 index 0000000..0189a32 --- /dev/null +++ b/CODE/ABSTRACT.H @@ -0,0 +1,135 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ABSTRACT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ABSTRACT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : January 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +COORDINATE As_Coord(TARGET target); + +class AbstractTypeClass; + +/* +** This class is the base class for all game objects that have an existence on the +** battlefield. +*/ +class AbstractClass +{ + public: + + /* + ** This specifies the type of object and the unique ID number + ** associated with it. The ID number happens to match the index into + ** the object heap appropriate for this object type. + */ + RTTIType RTTI; + int ID; + + /* + ** The coordinate location of the unit. For vehicles, this is the center + ** point. For buildings, it is the upper left corner. + */ + COORDINATE Coord; + + /* + ** This is the height of the object above ground (expressed in leptons). + */ + int Height; + + /* + ** The actual object ram-space is located in arrays in the data segment. This flag + ** is used to indicate which objects are free to be reused and which are currently + ** in use by the game. + */ + unsigned IsActive:1; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + AbstractClass(RTTIType rtti, int id) : RTTI(rtti), ID(id), Coord(0xFFFFFFFFL), Height(0) {}; + AbstractClass(NoInitClass const & x) {x();}; + virtual ~AbstractClass(void) {}; + + /* + ** Query functions. + */ + virtual char const * Name(void) const {return("");} + virtual HousesType Owner(void) const {return HOUSE_NONE;}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + RTTIType What_Am_I(void) const {return(RTTI);}; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass * mono) const; + #endif + + /* + ** Coordinate query support functions. + */ + virtual COORDINATE Center_Coord(void) const {return Coord;}; + virtual COORDINATE Target_Coord(void) const {return Coord;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());}; + DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);}; + DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));}; + DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);}; + int Distance(TARGET target) const; + int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);}; + int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());}; + + /* + ** Object entry and exit from the game system. + */ + virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;}; + + /* + ** AI. + */ + virtual void AI(void) {}; + +}; + + +#endif diff --git a/CODE/ADAMTEMP.MAK b/CODE/ADAMTEMP.MAK new file mode 100644 index 0000000..a4065c6 --- /dev/null +++ b/CODE/ADAMTEMP.MAK @@ -0,0 +1,1847 @@ +# +# 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 . +# + +# $Header: F:\projects\c&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40:38 JOE_BOSTIC $ +#*************************************************************************** +#** 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 * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : March 25, 1993 * +#* * +#* Last Update : March 25, 1993 [JLB] * +#* * +#*-------------------------------------------------------------------------* + + +# Comment out the following line to disable "include file autodependency". +.AUTODEPEND +#.SWAP + +!include "rules.mak" + + +########################################################################## + +MAPFILES = \ + +CACHEMAP = \ + BRIEFING.AUD \ + +LOCALFILES = \ + PROLOG.CPS \ + MAP.AUD \ + TITLE.CPS \ + PALETTE.CPS \ + INTRO.AUD \ + EGOPAL.PAL \ + RULES.INI \ + CREDITS.TXT \ + ALIPAPER.CPS \ + 3POINT.FNT \ + 8POINT.FNT \ + EDITFNT.FNT \ + CONQUER.ENG \ + DEBUG.ENG \ + LED.FNT \ + SNOW.PAL \ + TEMPERAT.PAL \ + INTERIOR.PAL \ + VCR.FNT \ + HOLE0000.LUT \ + HOLE0001.LUT \ + HOLE0002.LUT \ + HOLE0003.LUT \ + HOLE0004.LUT \ + HOLE0005.LUT \ + HOLE0006.LUT \ + HOLE0007.LUT \ + HOLE0008.LUT \ + HOLE0009.LUT \ + HOLE0010.LUT \ + HOLE0011.LUT \ + HOLE0012.LUT \ + HOLE0013.LUT \ + HOLE0014.LUT \ + HOLE0015.LUT \ + HOLE0016.LUT \ + HOLE0017.LUT \ + HOLE0018.LUT \ + HOLE0019.LUT \ + HOLE0020.LUT \ + HOLE0021.LUT \ + HOLE0022.LUT \ + HOLE0023.LUT \ + HOLE0024.LUT \ + HOLE0025.LUT \ + HOLE0026.LUT \ + HOLE0027.LUT \ + HOLE0028.LUT \ + HOLE0029.LUT \ + HOLE0030.LUT \ + HOLE0031.LUT \ + HOLE0032.LUT \ + HOLE0033.LUT \ + HOLE0034.LUT \ + HOLE0035.LUT \ + HOLE0036.LUT \ + HOLE0037.LUT \ + HOLE0038.LUT \ + HOLE0039.LUT \ + HOLE0040.LUT \ + HOLE0041.LUT \ + HOLE0042.LUT \ + HOLE0043.LUT \ + HOLE0044.LUT \ + HOLE0045.LUT \ + HOLE0046.LUT \ + HOLE0047.LUT \ +# TEMPSCOR.FNT \ +# 6POINT.FNT \ +# GRAD6FNT.FNT \ +# SCOREFNT.FNT \ + + +# Files that have counterparts in both high and low resolutions. +# These files will be built into the HIRES.MIX and LORES.MIX files. +HILORES = \ + TRANICON.SHP \ + PIPS.SHP \ + PULSE.SHP \ + ATOMICON.SHP \ + WARPICON.SHP \ + C1.SHP \ + C2.SHP \ + CHAN.SHP \ + DELPHI.SHP \ + E1.SHP \ + E2.SHP \ + E3.SHP \ + E4.SHP \ + E5.SHP \ + E6.SHP \ + E7.SHP \ + EINSTEIN.SHP \ + GNRL.SHP \ + MEDI.SHP \ + SPY.SHP \ + THF.SHP \ + DD-BKGND.SHP \ + DD-BOTM.SHP \ + DD-CRNR.SHP \ + DD-EDGE.SHP \ + DD-LEFT.SHP \ + DD-RIGHT.SHP \ + DD-TOP.SHP \ + 12METFNT.FNT \ + GRAD6FNT.FNT \ + HELP.FNT \ + 6POINT.FNT \ + TYPE.FNT \ + SCOREFNT.FNT \ + 1TNKICON.SHP \ + 2TNKICON.SHP \ + 3TNKICON.SHP \ + 4TNKICON.SHP \ + AFLDICON.SHP \ + AGUNICON.SHP \ + APCICON.SHP \ + APWRICON.SHP \ + ARTYICON.SHP \ + ATEKICON.SHP \ + BADRICON.SHP \ + BARRICON.SHP \ + BRIKICON.SHP \ + BTN-DN.SHP \ + BTN-PL.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + CAICON.SHP \ + CAMICON.SHP \ + CLOCK.SHP \ + DDICON.SHP \ + DOGICON.SHP \ + DOMEICON.SHP \ + DOMFICON.SHP \ + E1ICON.SHP \ + E2ICON.SHP \ + E3ICON.SHP \ + E4ICON.SHP \ + E6ICON.SHP \ + E7ICON.SHP \ + FACFICON.SHP \ + FACTICON.SHP \ + FENCICON.SHP \ + FIXICON.SHP \ + FTURICON.SHP \ + GAPICON.SHP \ + GPSSICON.SHP \ + GUNICON.SHP \ + HARVICON.SHP \ + HBOXICON.SHP \ + HELIICON.SHP \ + HINDICON.SHP \ + HPADICON.SHP \ + INFXICON.SHP \ + IRONICON.SHP \ + JEEPICON.SHP \ + KENNICON.SHP \ + LSTICON.SHP \ + MAP.SHP \ + MCVICON.SHP \ + MEDIICON.SHP \ + MGGICON.SHP \ + MIGICON.SHP \ + MNLYICON.SHP \ + MOUSE.SHP \ + MRJICON.SHP \ + MSLOICON.SHP \ + NATORADR.SHP \ + PBMBICON.SHP \ + PBOXICON.SHP \ + PDOXICON.SHP \ + PINFICON.SHP \ + POWER.SHP \ + POWERBAR.SHP \ + POWRICON.SHP \ + PROCICON.SHP \ + PTICON.SHP \ + REPAIR.SHP \ + SAMICON.SHP \ + SBAGICON.SHP \ + SELL.SHP \ + SIDEBAR.SHP \ + SILOICON.SHP \ + SMIGICON.SHP \ + SONRICON.SHP \ + SOVPAPER.CPS \ + SPEFICON.SHP \ + SPENICON.SHP \ + SPYICON.SHP \ + SSICON.SHP \ + STEKICON.SHP \ + STRIP.SHP \ + STRIPDN.SHP \ + STRIPUP.SHP \ + SYRDICON.SHP \ + SYRFICON.SHP \ + TABS.SHP \ + TENTICON.SHP \ + THFICON.SHP \ + TRUKICON.SHP \ + TSLAICON.SHP \ + U2ICON.SHP \ + USSRRADR.SHP \ + V2RLICON.SHP \ + WEAFICON.SHP \ + WEAPICON.SHP \ + YAKICON.SHP \ + NRADRFRM.SHP \ + URADRFRM.SHP \ + SIDE1NA.SHP \ + SIDE1US.SHP \ + SIDE2NA.SHP \ + SIDE2US.SHP \ + SIDE3NA.SHP \ + SIDE3US.SHP \ + STRIPNA.SHP \ + STRIPUS.SHP \ + +# MOEBICON.SHP \ + +HILORES1 = \ + MECH.SHP \ + SHOK.SHP \ + CARRICON.SHP \ + CTNKICON.SHP \ + DTRKICON.SHP \ + MECHICON.SHP \ + MSUBICON.SHP \ + QTNKICON.SHP \ + SHOKICON.SHP \ + STNKICON.SHP \ + TTNKICON.SHP \ + +# These helper macros substitute the extension so that +# the appropriate art build rule will be invoked. +xLOHILORES = $(HILORES:.SHP=.LOW) +LOHILORES = $(xLOHILORES:.FNT=.LNT) +xHIHILORES = $(HILORES:.SHP=.HI) +HIHILORES = $(xHIHILORES:.FNT=.HNT) + +xLOHILORES1 = $(HILORES1:.SHP=.LOW) +LOHILORES1 = $(xLOHILORES1:.FNT=.LNT) +xHIHILORES1 = $(HILORES1:.SHP=.HI) +HIHILORES1 = $(xHIHILORES1:.FNT=.HNT) + +# +# Files required for hires/Win95 version only +# +# This mix file is cached +# +HIRESFILES = \ + ALIPAPER.PCX \ + PROLOG.PCX \ + SOVPAPER.PCX \ + AFTR_HI.PCX \ + ALY1.PCX \ + APC_HI.PCX \ + APHI0049.PCX \ + BNHI0020.PCX \ + DCHI0040.PCX \ + FRHI0166.PCX \ + LAB.PCX \ + LANDSBRG.PCX \ + MAHI0107.PCX \ + MIG_HI.PCX \ + MTFACTHI.PCX \ + NEEDLE.PCX \ + SOV2.PCX \ + SPY.PCX \ + STALIN.PCX \ + TENT.PCX \ +# ENG_HI.PCX \ + + +CONQUERFILES = \ + PARABOMB.SHP \ + +GENERALMAPFILES = \ + MISSIONS.PKT \ + CSTRIKE.PKT \ + TUTORIAL.INI \ + SCG01EA.INI \ + SCG40EA.INI \ + SCG41EA.INI \ + SCG42EA.INI \ + SCG43EA.INI \ + SCG44EA.INI \ + SCG45EA.INI \ + SCG46EA.INI \ + SCG47EA.INI \ + SCG48EA.INI \ + SCU40EA.INI \ + SCU41EA.INI \ + SCU42EA.INI \ + SCU43EA.INI \ + SCU44EA.INI \ + SCU45EA.INI \ + SCU46EA.INI \ + SCU47EA.INI \ + SCU48EA.INI \ + SCU01EA.INI \ + SCM01EA.INI \ + SCM02EA.INI \ + SCM03EA.INI \ + SCM04EA.INI \ + SCM05EA.INI \ + SCM06EA.INI \ + SCM07EA.INI \ + SCM08EA.INI \ + SCM09EA.INI \ + SCM10EA.INI \ + SCM11EA.INI \ + SCM12EA.INI \ + SCM13EA.INI \ + SCM14EA.INI \ + SCM15EA.INI \ + SCM16EA.INI \ + SCM17EA.INI \ + SCM18EA.INI \ + SCM19EA.INI \ + SCM20EA.INI \ + SCM21EA.INI \ + SCM22EA.INI \ + SCM23EA.INI \ + SCM24EA.INI \ + SCMD0EA.INI \ + SCMD1EA.INI \ + SCMD2EA.INI \ + SCMD3EA.INI \ + SCMD4EA.INI \ + SCMD5EA.INI \ + SCMD6EA.INI \ + SCMD7EA.INI \ + SCMD8EA.INI \ + SCMD9EA.INI \ + SCME0EA.INI \ + SCME1EA.INI \ + SCME2EA.INI \ + SCME3EA.INI \ + SCME4EA.INI \ + SCME5EA.INI \ + SCME6EA.INI \ + SCME7EA.INI \ + SCME8EA.INI \ + SCME9EA.INI \ + SCMF0EA.INI \ + SCMF1EA.INI \ + SCMF2EA.INI \ + SCMF3EA.INI \ + SCMF4EA.INI \ + SCMF5EA.INI \ + SCMF6EA.INI \ + SCMF7EA.INI \ + SCMF8EA.INI \ + SCMF9EA.INI \ + SCMG0EA.INI \ + SCMG1EA.INI \ + SCMG2EA.INI \ + SCMG3EA.INI \ + SCMG4EA.INI \ + SCMG5EA.INI \ + SCMG6EA.INI \ + SCMG7EA.INI \ + SCMG8EA.INI \ + SCMG9EA.INI \ + SCMH0EA.INI \ + SCMH1EA.INI \ + SCMH2EA.INI \ + SCMH3EA.INI \ + SCMH4EA.INI \ + SCMH5EA.INI \ + SCMH6EA.INI \ + SCMH7EA.INI \ + SCMH8EA.INI \ + SCMH9EA.INI \ + SCMI0EA.INI \ + SCMI1EA.INI \ + SCMI2EA.INI \ + SCMI3EA.INI \ + SCMI4EA.INI \ + SCMI5EA.INI \ + SCMI6EA.INI \ + SCMI7EA.INI \ + SCMI8EA.INI \ + SCMI9EA.INI \ + SCMJ0EA.INI \ + SCMJ1EA.INI \ + SCMJ2EA.INI \ + SCMJ3EA.INI \ + SCMJ4EA.INI \ + SCMJ5EA.INI \ + SCMJ6EA.INI \ + SCMJ7EA.INI \ + SCMJ8EA.INI \ + SCMJ9EA.INI \ + SCMK0EA.INI \ + SCMK1EA.INI \ + SCMK2EA.INI \ + SCMK3EA.INI \ + SCMK4EA.INI \ + SCMK5EA.INI \ + SCMK6EA.INI \ + SCMK7EA.INI \ + SCMK8EA.INI \ + SCMK9EA.INI \ + SCML0EA.INI \ + SCML1EA.INI \ + SCML2EA.INI \ + SCML3EA.INI \ + SCML4EA.INI \ + SCML5EA.INI \ + SCML6EA.INI \ + SCML7EA.INI \ + SCML8EA.INI \ + SCML9EA.INI \ + SCMM0EA.INI \ + SCMM1EA.INI \ + SCMM2EA.INI \ + SCMM3EA.INI \ + SCMM4EA.INI \ + SCMM5EA.INI \ + SCMM6EA.INI \ + SCMM7EA.INI \ + SCMM8EA.INI \ + SCMM9EA.INI \ + SCM25EA.INI \ + SCM26EA.INI \ + SCM27EA.INI \ + SCM28EA.INI \ + SCM29EA.INI \ + SCM30EA.INI \ + SCM31EA.INI \ + SCM32EA.INI \ + SCM33EA.INI \ + SCM34EA.INI \ + SCM35EA.INI \ + SCM36EA.INI \ + SCM37EA.INI \ + SCM38EA.INI \ + SCM39EA.INI \ + SCM40EA.INI \ + SCM41EA.INI \ + SCM42EA.INI \ + SCM43EA.INI \ + SCM44EA.INI \ + SCM45EA.INI \ + SCM46EA.INI \ + SCM47EA.INI \ + SCM48EA.INI \ + SCM49EA.INI \ + SCM50EA.INI \ + SCM51EA.INI \ + SCM52EA.INI \ + SCM53EA.INI \ + SCM54EA.INI \ + SCM55EA.INI \ + SCM56EA.INI \ + SCM57EA.INI \ + SCM58EA.INI \ + SCM59EA.INI \ + SCM60EA.INI \ + SCM61EA.INI \ + SCM62EA.INI \ + SCM63EA.INI \ + SCM64EA.INI \ + SCM65EA.INI \ + SCM66EA.INI \ + SCM67EA.INI \ + SCM68EA.INI \ + SCM69EA.INI \ + SCM70EA.INI \ + SCM71EA.INI \ + SCM72EA.INI \ + SCM73EA.INI \ + SCM74EA.INI \ + SCM75EA.INI \ + SCM76EA.INI \ + SCM77EA.INI \ + SCM78EA.INI \ + SCM79EA.INI \ + SCM80EA.INI \ + SCM81EA.INI \ + SCM82EA.INI \ + SCM83EA.INI \ + SCM84EA.INI \ + SCM85EA.INI \ + SCM86EA.INI \ + SCM87EA.INI \ + SCM88EA.INI \ + SCM89EA.INI \ + SCM90EA.INI \ + SCM91EA.INI \ + SCM92EA.INI \ + SCM93EA.INI \ + SCM94EA.INI \ + SCM95EA.INI \ + SCM96EA.INI \ + SCM97EA.INI \ + SCM98EA.INI \ + SCM99EA.INI \ + SCM100EA.INI \ + SCM101EA.INI \ + SCM102EA.INI \ + SCM103EA.INI \ + SCM104EA.INI \ + SCM105EA.INI \ + SCM106EA.INI \ + SCM107EA.INI \ + SCM108EA.INI \ + SCM109EA.INI \ + SCM110EA.INI \ + SCM111EA.INI \ + SCM112EA.INI \ + SCM113EA.INI \ + SCM114EA.INI \ + SCM115EA.INI \ + SCM116EA.INI \ + SCM117EA.INI \ + SCM118EA.INI \ + SCM119EA.INI \ + SCM120EA.INI \ + SCM121EA.INI \ + SCM122EA.INI \ + SCM123EA.INI \ + SCM124EA.INI \ + SCM125EA.INI \ + SCM126EA.INI \ + SCM127EA.INI \ + SCM128EA.INI \ + SCM129EA.INI \ + SCM130EA.INI \ + +NETMAPFILES = \ + +# Files that aren't cached. +GENERALFILES = \ + AFTR_LO.CPS \ + ALY1-LO.CPS \ + APC_LO.CPS \ + APLO0049.CPS \ + BNLO0020.CPS \ + DCLO0040.CPS \ + FRLO0166.CPS \ + LAB-LO.CPS \ + LANDS-LO.CPS \ + MALO0107.CPS \ + MIG_LO.CPS \ + MTFACTLO.CPS \ + NEEDL-LO.CPS \ + SOV2-LO.CPS \ + SPY-LO.CPS \ + STALN-LO.CPS \ + TENT-LO.CPS \ + TITLE.CPS \ + PPAPER.CPS \ + MSAA.WSA \ + MSAB.WSA \ + MSAC.WSA \ + MSAD.WSA \ + MSAE.WSA \ + MSAF.WSA \ + MSAG.WSA \ + MSAH.WSA \ + MSAI.WSA \ + MSAJ.WSA \ + MSAK.WSA \ + MSAL.WSA \ + MSAM.WSA \ + MSAN.WSA \ + MSSA.WSA \ + MSSB.WSA \ + MSSC.WSA \ + MSSD.WSA \ + MSSE.WSA \ + MSSF.WSA \ + MSSG.WSA \ + MSSH.WSA \ + MSSI.WSA \ + MSSJ.WSA \ + MSSK.WSA \ + MSSL.WSA \ + MSSM.WSA \ + MSSN.WSA \ + +INTERIORFILES = \ + BOXES01.INT \ + BOXES02.INT \ + BOXES03.INT \ + BOXES04.INT \ + BOXES05.INT \ + BOXES06.INT \ + BOXES07.INT \ + BOXES08.INT \ + BOXES09.INT \ + XTRA0001.INT \ + XTRA0002.INT \ + XTRA0003.INT \ + XTRA0004.INT \ + XTRA0005.INT \ + XTRA0006.INT \ + XTRA0007.INT \ + XTRA0008.INT \ + XTRA0009.INT \ + XTRA0010.INT \ + XTRA0011.INT \ + XTRA0012.INT \ + XTRA0013.INT \ + XTRA0014.INT \ + XTRA0015.INT \ + XTRA0016.INT \ + CLEAR1.INT \ + MOVEFLSH.INT \ + ARRO0001.INT \ + ARRO0002.INT \ + ARRO0003.INT \ + ARRO0004.INT \ + ARRO0005.INT \ + ARRO0006.INT \ + ARRO0007.INT \ + ARRO0008.INT \ + ARRO0009.INT \ + ARRO0010.INT \ + ARRO0011.INT \ + ARRO0012.INT \ + ARRO0013.INT \ + ARRO0014.INT \ + ARRO0015.INT \ + FLOR0001.INT \ + FLOR0002.INT \ + FLOR0003.INT \ + FLOR0004.INT \ + FLOR0005.INT \ + FLOR0006.INT \ + FLOR0007.INT \ + GFLR0001.INT \ + GFLR0002.INT \ + GFLR0003.INT \ + GFLR0004.INT \ + GFLR0005.INT \ + GSTR0001.INT \ + GSTR0002.INT \ + GSTR0003.INT \ + GSTR0004.INT \ + GSTR0005.INT \ + GSTR0006.INT \ + GSTR0007.INT \ + GSTR0008.INT \ + GSTR0009.INT \ + GSTR0010.INT \ + GSTR0011.INT \ + LWAL0001.INT \ + LWAL0002.INT \ + LWAL0003.INT \ + LWAL0004.INT \ + LWAL0005.INT \ + LWAL0006.INT \ + LWAL0007.INT \ + LWAL0008.INT \ + LWAL0009.INT \ + LWAL0010.INT \ + LWAL0011.INT \ + LWAL0012.INT \ + LWAL0013.INT \ + LWAL0014.INT \ + LWAL0015.INT \ + LWAL0016.INT \ + LWAL0017.INT \ + LWAL0018.INT \ + LWAL0019.INT \ + LWAL0020.INT \ + LWAL0021.INT \ + LWAL0022.INT \ + LWAL0023.INT \ + LWAL0024.INT \ + LWAL0025.INT \ + LWAL0026.INT \ + LWAL0027.INT \ + STRP0001.INT \ + STRP0002.INT \ + STRP0003.INT \ + STRP0004.INT \ + STRP0005.INT \ + STRP0006.INT \ + STRP0007.INT \ + STRP0008.INT \ + STRP0009.INT \ + STRP0010.INT \ + STRP0011.INT \ + WALL0001.INT \ + WALL0002.INT \ + WALL0003.INT \ + WALL0004.INT \ + WALL0005.INT \ + WALL0006.INT \ + WALL0007.INT \ + WALL0008.INT \ + WALL0009.INT \ + WALL0010.INT \ + WALL0011.INT \ + WALL0012.INT \ + WALL0013.INT \ + WALL0014.INT \ + WALL0015.INT \ + WALL0016.INT \ + WALL0017.INT \ + WALL0018.INT \ + WALL0019.INT \ + WALL0020.INT \ + WALL0021.INT \ + WALL0022.INT \ + WALL0023.INT \ + WALL0024.INT \ + WALL0025.INT \ + WALL0026.INT \ + WALL0027.INT \ + WALL0028.INT \ + WALL0029.INT \ + WALL0030.INT \ + WALL0031.INT \ + WALL0032.INT \ + WALL0033.INT \ + WALL0034.INT \ + WALL0035.INT \ + WALL0036.INT \ + WALL0037.INT \ + WALL0038.INT \ + WALL0039.INT \ + WALL0040.INT \ + WALL0041.INT \ + WALL0042.INT \ + WALL0043.INT \ + WALL0044.INT \ + WALL0045.INT \ + WALL0046.INT \ + WALL0047.INT \ + WALL0048.INT \ + WALL0049.INT \ + +# Both the temperate and snow sets have identical template entries. +TEMPERATEFILES = \ + MINE.TEM \ + ICE01.TEM \ + ICE02.TEM \ + ICE03.TEM \ + ICE04.TEM \ + ICE05.TEM \ + MOVEFLSH.TEM \ + BR1X.TEM \ + BR2X.TEM \ + BRIDGE1X.TEM \ + BRIDGE2X.TEM \ + BRIDGE1H.TEM \ + BRIDGE2H.TEM \ + F01.TEM \ + F02.TEM \ + F03.TEM \ + F04.TEM \ + F05.TEM \ + F06.TEM \ + ELECTRO.TEM \ + B1.TEM \ + B2.TEM \ + B3.TEM \ + BIB1.TEM \ + BIB2.TEM \ + BIB3.TEM \ + BR1A.TEM \ + BR1B.TEM \ + BR1C.TEM \ + BR2A.TEM \ + BR2B.TEM \ + BR2C.TEM \ + BR3A.TEM \ + BR3B.TEM \ + BR3C.TEM \ + BR3D.TEM \ + BR3E.TEM \ + BR3F.TEM \ + BRIDGE1.TEM \ + BRIDGE1D.TEM \ + BRIDGE2.TEM \ + BRIDGE2D.TEM \ + CLEAR1.TEM \ + CORPSE1.TEM \ + CORPSE2.TEM \ + CORPSE3.TEM \ + CR1.TEM \ + CR2.TEM \ + CR3.TEM \ + CR4.TEM \ + CR5.TEM \ + CR6.TEM \ + D01.TEM \ + D02.TEM \ + D03.TEM \ + D04.TEM \ + D05.TEM \ + D06.TEM \ + D07.TEM \ + D08.TEM \ + D09.TEM \ + D10.TEM \ + D11.TEM \ + D12.TEM \ + D13.TEM \ + D14.TEM \ + D15.TEM \ + D16.TEM \ + D17.TEM \ + D18.TEM \ + D19.TEM \ + D20.TEM \ + D21.TEM \ + D22.TEM \ + D23.TEM \ + D24.TEM \ + D25.TEM \ + D26.TEM \ + D27.TEM \ + D28.TEM \ + D29.TEM \ + D30.TEM \ + D31.TEM \ + D32.TEM \ + D33.TEM \ + D34.TEM \ + D35.TEM \ + D36.TEM \ + D37.TEM \ + D38.TEM \ + D39.TEM \ + D40.TEM \ + D41.TEM \ + D42.TEM \ + D43.TEM \ + D44.TEM \ + D45.TEM \ + FALLS1.TEM \ + FALLS1A.TEM \ + FALLS2.TEM \ + FALLS2A.TEM \ + FORD1.TEM \ + FORD2.TEM \ + GEM01.TEM \ + GEM02.TEM \ + GEM03.TEM \ + GEM04.TEM \ + GOLD01.TEM \ + GOLD02.TEM \ + GOLD03.TEM \ + GOLD04.TEM \ + HBOX.TEM \ + MSLOMAKE.TEM \ + HBOXMAKE.TEM \ + MSLO.TEM \ + P01.TEM \ + P02.TEM \ + P03.TEM \ + P04.TEM \ + P07.TEM \ + P08.TEM \ + P13.TEM \ + P14.TEM \ + RC01.TEM \ + RC02.TEM \ + RC03.TEM \ + RC04.TEM \ + RF01.TEM \ + RF02.TEM \ + RF03.TEM \ + RF04.TEM \ + RF05.TEM \ + RF06.TEM \ + RF07.TEM \ + RF08.TEM \ + RF09.TEM \ + RF10.TEM \ + RF11.TEM \ + RV01.TEM \ + RV02.TEM \ + RV03.TEM \ + RV04.TEM \ + RV05.TEM \ + RV06.TEM \ + RV07.TEM \ + RV08.TEM \ + RV09.TEM \ + RV10.TEM \ + RV11.TEM \ + RV12.TEM \ + RV13.TEM \ + RV14.TEM \ + RV15.TEM \ + S01.TEM \ + S02.TEM \ + S03.TEM \ + S04.TEM \ + S05.TEM \ + S06.TEM \ + S07.TEM \ + S08.TEM \ + S09.TEM \ + S10.TEM \ + S11.TEM \ + S12.TEM \ + S13.TEM \ + S14.TEM \ + S15.TEM \ + S16.TEM \ + S17.TEM \ + S18.TEM \ + S19.TEM \ + S20.TEM \ + S21.TEM \ + S22.TEM \ + S23.TEM \ + S24.TEM \ + S25.TEM \ + S26.TEM \ + S27.TEM \ + S28.TEM \ + S29.TEM \ + S30.TEM \ + S31.TEM \ + S32.TEM \ + S33.TEM \ + S34.TEM \ + S35.TEM \ + S36.TEM \ + S37.TEM \ + S38.TEM \ + SC1.TEM \ + SC2.TEM \ + SC3.TEM \ + SC4.TEM \ + SC5.TEM \ + SC6.TEM \ + SH01.TEM \ + SH02.TEM \ + SH03.TEM \ + SH04.TEM \ + SH05.TEM \ + SH06.TEM \ + SH07.TEM \ + SH08.TEM \ + SH09.TEM \ + SH10.TEM \ + SH11.TEM \ + SH12.TEM \ + SH13.TEM \ + SH14.TEM \ + SH15.TEM \ + SH16.TEM \ + SH17.TEM \ + SH18.TEM \ + SH19.TEM \ + SH20.TEM \ + SH21.TEM \ + SH22.TEM \ + SH23.TEM \ + SH24.TEM \ + SH25.TEM \ + SH26.TEM \ + SH27.TEM \ + SH28.TEM \ + SH29.TEM \ + SH30.TEM \ + SH31.TEM \ + SH32.TEM \ + SH33.TEM \ + SH34.TEM \ + SH35.TEM \ + SH36.TEM \ + SH37.TEM \ + SH38.TEM \ + SH39.TEM \ + SH40.TEM \ + SH41.TEM \ + SH42.TEM \ + SH43.TEM \ + SH44.TEM \ + SH45.TEM \ + SH46.TEM \ + SH47.TEM \ + SH48.TEM \ + SH49.TEM \ + SH50.TEM \ + SH51.TEM \ + SH52.TEM \ + SH53.TEM \ + SH54.TEM \ + SH55.TEM \ + SH56.TEM \ + T01.TEM \ + T02.TEM \ + T03.TEM \ + T05.TEM \ + T06.TEM \ + T07.TEM \ + T08.TEM \ + T10.TEM \ + T11.TEM \ + T12.TEM \ + T13.TEM \ + T14.TEM \ + T15.TEM \ + T16.TEM \ + T17.TEM \ + TC01.TEM \ + TC02.TEM \ + TC03.TEM \ + TC04.TEM \ + TC05.TEM \ + V01.TEM \ + V02.TEM \ + V03.TEM \ + V04.TEM \ + V05.TEM \ + V06.TEM \ + V07.TEM \ + V08.TEM \ + V09.TEM \ + V10.TEM \ + V11.TEM \ + V12.TEM \ + V13.TEM \ + V14.TEM \ + V15.TEM \ + V16.TEM \ + V17.TEM \ + V18.TEM \ + W1.TEM \ + W2.TEM \ + WC01.TEM \ + WC02.TEM \ + WC03.TEM \ + WC04.TEM \ + WC05.TEM \ + WC06.TEM \ + WC07.TEM \ + WC08.TEM \ + WC09.TEM \ + WC10.TEM \ + WC11.TEM \ + WC12.TEM \ + WC13.TEM \ + WC14.TEM \ + WC15.TEM \ + WC16.TEM \ + WC17.TEM \ + WC18.TEM \ + WC19.TEM \ + WC20.TEM \ + WC21.TEM \ + WC22.TEM \ + WC23.TEM \ + WC24.TEM \ + WC25.TEM \ + WC26.TEM \ + WC27.TEM \ + WC28.TEM \ + WC29.TEM \ + WC30.TEM \ + WC31.TEM \ + WC32.TEM \ + WC33.TEM \ + WC34.TEM \ + WC35.TEM \ + WC36.TEM \ + WC37.TEM \ + WC38.TEM \ + +# Every temperate theater terrain file has a snow theater counterpart. +SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO) + +# Sound effects (Juvenile or Adult) +SFX = \ + +# Generic wave files (never changes). +WAVFILES = \ + AACANON3.AUD \ + BEEPSLCT.AUD \ + BLEEP11.AUD \ + BLEEP12.AUD \ + BLEEP13.AUD \ + BLEEP17.AUD \ + BLEEP5.AUD \ + BLEEP6.AUD \ + BLEEP9.AUD \ + BOMBIT1.AUD \ + BUILD5.AUD \ + BUZZY1.AUD \ + CANNON1.AUD \ + CANNON2.AUD \ + CASHDN1.AUD \ + CASHTURN.AUD \ + CASHUP1.AUD \ + CHRONO2.AUD \ + CHROTNK1.AUD \ + CHUTE1.AUD \ + CMON1.AUD \ + CRMBLE2.AUD \ + DEDMAN1.AUD \ + DEDMAN10.AUD \ + DEDMAN2.AUD \ + DEDMAN3.AUD \ + DEDMAN4.AUD \ + DEDMAN5.AUD \ + DEDMAN6.AUD \ + DEDMAN7.AUD \ + DEDMAN8.AUD \ + DOGG5P.AUD \ + DOGW3PX.AUD \ + DOGW5.AUD \ + DOGW6.AUD \ + DOGW7.AUD \ + DOGY1.AUD \ + EAFFIRM1.AUD \ + EENGIN1.AUD \ + EINAH1.AUD \ + EINOK1.AUD \ + EINYES1.AUD \ + EMOVOUT1.AUD \ + EYESSIR1.AUD \ + FIREBL3.AUD \ + FIRETRT1.AUD \ + FIXIT1.AUD \ + GIRLOKAY.AUD \ + GIRLYEAH.AUD \ + GOTIT1.AUD \ + GRENADE1.AUD \ + GUN11.AUD \ + GUN13.AUD \ + GUN27.AUD \ + GUN5.AUD \ + GUYOKAY1.AUD \ + GUYYEAH1.AUD \ + H2OBOMB2.AUD \ + HEAL2.AUD \ + HYDROD1.AUD \ + INVUL2.AUD \ + IRONCUR9.AUD \ + JBURN1.AUD \ + JCHRGE1.AUD \ + JCRISP1.AUD \ + JDANCE1.AUD \ + JJUICE1.AUD \ + JJUMP1.AUD \ + JLIGHT1.AUD \ + JPOWER1.AUD \ + JSHOCK1.AUD \ + JYES1.AUD \ + KABOOM1.AUD \ + KABOOM12.AUD \ + KABOOM15.AUD \ + KABOOM22.AUD \ + KABOOM25.AUD \ + KABOOM30.AUD \ + KEEPEM1.AUD \ + LAUGH1.AUD \ + LEFTY1.AUD \ + MADCHRG2.AUD \ + MADEXPLO.AUD \ + MAFFIRM1.AUD \ + MBOSS1.AUD \ + MHEAR1.AUD \ + MHOTDIG1.AUD \ + MHOWDY1.AUD \ + MHUH1.AUD \ + MGUNINF1.AUD \ + MINE1.AUD \ + MINEBLO1.AUD \ + MINELAY1.AUD \ + MISSILE1.AUD \ + MISSILE6.AUD \ + MISSILE7.AUD \ + MLAFF1.AUD \ + MMOVOUT1.AUD \ + MRESPON1.AUD \ + MRISE1.AUD \ + MWRENCH1.AUD \ + MYEEHAW1.AUD \ + MYES1.AUD \ + MYESSIR1.AUD \ + ONIT1.AUD \ + PILLBOX1.AUD \ + PLACBLDG.AUD \ + RABEEP1.AUD \ + RADARDN1.AUD \ + RADARON2.AUD \ + RAMENU1.AUD \ + ROKROLL1.AUD \ + SAFFIRM1.AUD \ + SANDBAG2.AUD \ + SCOLDY1.AUD \ + SCOMND1.AUD \ + SHKTROP1.AUD \ + SILENCER.AUD \ + SINDEED1.AUD \ + SKING1.AUD \ + SMOUT1.AUD \ + SOKAY1.AUD \ + SONPULSE.AUD \ + SONWAY1.AUD \ + SPLASH9.AUD \ + SQUISHY2.AUD \ + SUBSHOW1.AUD \ + SWHAT1.AUD \ + SYEAH1.AUD \ + SYESSIR1.AUD \ + TANDETH1.AUD \ + TANK5.AUD \ + TANK6.AUD \ + TESLA1.AUD \ + TORPEDO1.AUD \ + TSLACHG2.AUD \ + TUFFGUY1.AUD \ + TURRET1.AUD \ + WALLKIL2.AUD \ + YEAH1.AUD \ + YES1.AUD \ + YO1.AUD \ + +# Vehicle responses +RESPONSE1 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + REPORT1.AUD \ + VEHIC1.AUD \ + YESSIR1.AUD \ + +# Infantry responses +RESPONSE2 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + NOPROB.AUD \ + OVEROUT.AUD \ + READY.AUD \ + REPORT1.AUD \ + RITAWAY.AUD \ + ROGER.AUD \ + UGOTIT.AUD \ + YESSIR1.AUD \ + +#TSCOREFILES = \ +# cps\record.bin \ +# WIN1.AUD \ +# MAP1.AUD \ + +VARFILES = \ + +SCOREFILES = \ + CREDITS.AUD \ + AWAIT.AUD \ + BIGF226M.AUD \ + CRUS226M.AUD \ + DENSE_R.AUD \ + FAC1226M.AUD \ + FAC2226M.AUD \ + FOGGER1A.AUD \ + HELL226M.AUD \ + MUD1A.AUD \ + RADIO2.AUD \ + ROLLOUT.AUD \ + RUN1226M.AUD \ + SCORE.AUD \ + SMSH226M.AUD \ + SNAKE.AUD \ + TERMINAT.AUD \ + TREN226M.AUD \ + TWIN.AUD \ + VECTOR1A.AUD \ + WORK226M.AUD \ + 2ND_HAND.AUD \ + ARAZIOD.AUD \ + BACKSTAB.AUD \ + CHAOS2.AUD \ + SHUT_IT.AUD \ + TWINMIX1.AUD \ + UNDER3.AUD \ + VR2.AUD \ + BOG.AUD \ + FLOAT_V2.AUD \ + GLOOM.AUD \ + GRNDWIRE.AUD \ + RPT.AUD \ + SEARCH.AUD \ + TRACTION.AUD \ + WASTELND.AUD \ + +SPEECHFILES = \ + STRCKIL1.AUD \ + NOPOWR1.AUD \ + SAVE1.AUD \ + LOAD1.AUD \ + 10MINR.AUD \ + 1MINR.AUD \ + 1OBJMET1.AUD \ + 20MINR.AUD \ + 2MINR.AUD \ + 2OBJMET1.AUD \ + 30MINR.AUD \ + 3MINR.AUD \ + 3OBJMET1.AUD \ + 40MINR.AUD \ + 4MINR.AUD \ + 5MINR.AUD \ + AAPPRO1.AUD \ + AARIVE1.AUD \ + AARIVE1.AUD \ + AARRIVE1.AUD \ + AARRIVN1.AUD \ + AARRIVS1.AUD \ + AARRIVW1.AUD \ + AAVAIL1.AUD \ + ABLDGIN1.AUD \ + AFALLEN1.AUD \ + ALAUNCH1.AUD \ + APREP1.AUD \ + AREADY1.AUD \ + ARMORUP1.AUD \ + ASELECT1.AUD \ + ATLNCH1.AUD \ + ATPREP1.AUD \ + AUNITL1.AUD \ + BASEATK1.AUD \ + BCT1.AUD \ + BLDGINF1.AUD \ + BLDGPRG1.AUD \ + CANCLD1.AUD \ + CHROCHR1.AUD \ + CHRORDY1.AUD \ + CHROYES1.AUD \ + CMDCNTR1.AUD \ + CNTLDED1.AUD \ + COMNDOF1.AUD \ + COMNDOR1.AUD \ + CONSCMP1.AUD \ + CONVLST1.AUD \ + CONVYAP1.AUD \ + CREDIT1.AUD \ + ENMYAPP1.AUD \ + FIREPO1.AUD \ + FLARE1.AUD \ + FLAREE1.AUD \ + FLAREN1.AUD \ + FLARES1.AUD \ + FLAREW1.AUD \ + IRONCHG1.AUD \ + IRONRDY1.AUD \ + KOSYFRE1.AUD \ + KOSYRES1.AUD \ + LOPOWER1.AUD \ + MERCF1.AUD \ + MERCR1.AUD \ + MISNLST1.AUD \ + MISNWON1.AUD \ + MTIMEIN1.AUD \ + NAVYLST1.AUD \ + NEWOPT1.AUD \ + NOBUILD1.AUD \ + NODEPLY1.AUD \ + NOFUNDS1.AUD \ + NOFUNDS1.AUD \ + OBJMET1.AUD \ + OBJNMET1.AUD \ + OBJNRCH1.AUD \ + OBJRCH1.AUD \ + ONHOLD1.AUD \ + OPTERM1.AUD \ + PRIBLDG1.AUD \ + PROGRES1.AUD \ + PULSE1.AUD \ + REINFOR1.AUD \ + REPAIR1.AUD \ + REPAIR1.AUD \ + SATLNCH1.AUD \ + SILOND1.AUD \ + SLCTTGT1.AUD \ + SOVEFAL1.AUD \ + SOVEMP1.AUD \ + SOVFAPP1.AUD \ + SOVFORC1.AUD \ + SOVREIN1.AUD \ + SPYPLN1.AUD \ + STRUCAP1.AUD \ + STRUSLD1.AUD \ + TANYAF1.AUD \ + TANYAR1.AUD \ + TARGFRE1.AUD \ + TARGRES1.AUD \ + TIMERGO1.AUD \ + TIMERNO1.AUD \ + TRAIN1.AUD \ + UNITFUL1.AUD \ + UNITLST1.AUD \ + UNITRDY1.AUD \ + UNITREP1.AUD \ + UNITSLD1.AUD \ + UNITSPD1.AUD \ + XPLOPLC1.AUD \ +# ABLDGC1.AUD \ +# SOVBLDG1.AUD \ +# SOVSTRC1.AUD \ +# SOVUNTD1.AUD \ +# AUNITD1.AUD \ +# ASTRUCD1.AUD \ + +#ALLIESVQ = \ +DUMMYVQ = \ + AAGUN.VQA \ + AFTRMATH.VQA \ + ALLY1.VQA \ + ALLY10.VQA \ + ALLY10B.VQA \ + ALLY11.VQA \ + ALLY12.VQA \ + ALLY14.VQA \ + ALLY2.VQA \ + ALLY4.VQA \ + ALLY5.VQA \ + ALLY6.VQA \ + ALLY8.VQA \ + ALLY9.VQA \ + ALLYEND.VQA \ + ALLYMORF.VQA \ + APCESCPE.VQA \ + ASSESS.VQA \ + BATTLE.VQA \ + 1BINOC.VQA \ + BMAP.VQA \ + BRDGTILT.VQA \ + CRONTEST.VQA \ + CRONFAIL.VQA \ + DESTROYR.VQA \ + DUD.VQA \ + ELEVATOR.VQA \ + FLARE.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + LANDING.VQA \ + MASASSLT.VQA \ + MCV.VQA \ + MCV_LAND.VQA \ + MONTPASS.VQA \ + OILDRUM.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + SHIPSINK.VQA \ + SHORBOM1.VQA \ + SHORBOM2.VQA \ + SHORBOMB.VQA \ + SNOWBOMB.VQA \ + SOVIET1.VQA \ + SOVTSTAR.VQA \ + SPY.VQA \ + TANYA1.VQA \ + TANYA2.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ +# TRAILER.VQA \ + +SOVIETVQ = \ + AAGUN.VQA \ + CRONFAIL.VQA \ + AIRFIELD.VQA \ + ALLY1.VQA \ + ALLYMORF.VQA \ + AVERTED.VQA \ + BEACHEAD.VQA \ + BMAP.VQA \ + BOMBRUN.VQA \ + COUNTDWN.VQA \ + DOUBLE.VQA \ + DPTHCHRG.VQA \ + EXECUTE.VQA \ + FLARE.VQA \ + LANDING.VQA \ + MCVBRDGE.VQA \ + MIG.VQA \ + MOVINGIN.VQA \ + MTNKFACT.VQA \ + NUKESTOK.VQA \ + ONTHPRWL.VQA \ + PERISCOP.VQA \ + PROLOG.VQA \ + RADRRAID.VQA \ + REDINTRO.VQA \ + SEARCH.VQA \ + SFROZEN.VQA \ + SITDUCK.VQA \ + SLNTSRVC.VQA \ + SNOWBOMB.VQA \ + SNSTRAFE.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + SOVFINAL.VQA \ + SOVIET1.VQA \ + SOVIET10.VQA \ + SOVIET11.VQA \ + SOVIET12.VQA \ + SOVIET13.VQA \ + SOVIET14.VQA \ + SOVIET2.VQA \ + SOVIET3.VQA \ + SOVIET4.VQA \ + SOVIET5.VQA \ + SOVIET6.VQA \ + SOVIET7.VQA \ + SOVIET8.VQA \ + SOVIET9.VQA \ + SOVMCV.VQA \ + SOVTSTAR.VQA \ + SPOTTER.VQA \ + STRAFE.VQA \ + TAKE_OFF.VQA \ + TESLA.VQA \ + V2ROCKET.VQA \ +# TRAILER.VQA \ + +ALLIESVQ = \ + AFTRMATH.VQA \ + ALLY1.VQA \ + ALLYMORF.VQA \ + APCESCPE.VQA \ + BATTLE.VQA \ + BMAP.VQA \ + CRONFAIL.VQA \ + DPTHCHRG.VQA \ + EXECUTE.VQA \ + FLARE.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + LANDING.VQA \ + MASASSLT.VQA \ + NUKESTOK.VQA \ + ONTHPRWL.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + SFROZEN.VQA \ + SLNTSRVC.VQA \ + SNOWBOMB.VQA \ + SNOWBASE.VQA \ + SOVMCV.VQA \ + SNSTRAFE.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + SOVIET1.VQA \ + SOVTSTAR.VQA \ + SPY.VQA \ + STRAFE.VQA \ + TESLA.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ + V2ROCKET.VQA \ +# ANTEND.VQA \ +# ANTINTRO.VQA \ + + + +# Files required for hires/Win95 version only +# +# This mix file is not cached +# +NOCACHEHIRESFILES= \ + ENGLISH.VQA \ + $(ALLIESVQ:.VQA=.VQP) \ + $(SOVIETVQ:.VQA=.VQP) \ + + +LINTOBJECTS1 = $(OBJECTS:,=) +LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB) + +# Mixfiles that should reside on the CD-ROM drive. +CD1MIXFILES = \ + CONQUER.MIX \ + + + +# Mixfiles that should reside on the hard drive. +LOCALMIXFILES = \ + EDITOR.MIX \ + HIRES.MIX \ + LOCAL.MIX \ + LORES.MIX \ + NCHIRES.MIX \ + SPEECH.MIX \ + + +# Mixfiles as they appear on the CD and hard drive. + + + +# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING +EXPANDFILES= \ + ANT1.SHP \ + ANT2.SHP \ + ANT3.SHP \ + QUEE.SHP \ + CREDITS.ENG \ + HILL01.TEM \ + ANTBITE.AUD \ + ANTDIE.AUD \ + ANTDIE.SHP \ + LAR1.SHP \ + LAR2.SHP \ + TITLE.PCX \ + MISSION.INI \ + BUZZY1.AUD \ + STAVCMDR.AUD \ + STAVCRSE.AUD \ + STAVYES.AUD \ + STAVMOV.AUD \ + CONQUER.ENG \ + RAMBO1.AUD \ + RAMBO2.AUD \ + RAMBO3.AUD \ + TITLE.CPS \ + TUTORIAL.INI \ + BMAP.VQP \ + ANTEND.VQP \ + ANTINTRO.VQP \ + +# Aftermath expansion files +EXPAND2FILES= \ + CARR.SHP \ + + + +############################################################# +# Rebuilds all the mixfiles. +packfiles: always $(PACKFILES) + +always: + copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u + + +#################################################################### +# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile. +$(.path.cd1)MAIN.MIX: $(CD1MIXFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.cd1)$&.mix + + + + +#################################################################### +# These are the various sub-mixfiles. +CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini + UTILS\MIXFILE -k -h -I$(.path.cps) &&! + $(CONQUERFILES) $(CACHEMAP) +! $(.path.mix)$&.mix + +TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(TEMPERATEFILES) +! $(.path.mix)$&.mix + +SNOW.MIX: $(SNOWFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(SNOWFILES) +! $(.path.mix)$&.mix + +INTERIOR.MIX: $(INTERIORFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(INTERIORFILES) +! $(.path.mix)$&.mix + +GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) +! $(.path.mix)$&.mix + +SCORES.MIX: $(SCOREFILES) + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.mix)$&.mix + +SOUNDS.MIX: $(WAVFILES) $(SFX) + UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +LIMITED.MIX: BLEEP11.AUD + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +MOVIES1.MIX: $(ALLIESVQ) + UTILS\MIXFILE -k -I$(.path.vqa) &&! + $** +! $(.path.mix)$&.mix + + + + + +NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI) + UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&! + $(NOCACHEHIRESFILES) +! $(.path.mix)$&.mix + +LOCAL.MIX: $(LOCALFILES) .\key.ini + UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&! + $(LOCALFILES) +! $(.path.mix)$&.mix + +LORES.MIX: $(LOHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&! + $(LOHILORES) +! $(.path.mix)$&.mix + +HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&! + $(HIRESFILES:.SHP=.HI) $(HIHILORES) +! $(.path.mix)$&.mix + +LORES1.MIX: $(LOHILORES1) .\key.ini + UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&! + $(LOHILORES1) +! $(.path.mix)$&.mix + +HIRES1.MIX: $(HIHILORES1) .\key.ini + UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&! + $(HIHILORES1) +! $(.path.mix)$&.mix + +SPEECH.MIX: $(SPEECHFILES) + UTILS\MIXFILE -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + + +EXPAND.MIX: $(EXPANDFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +EXPAND2.MIX: $(EXPAND2FILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +############################################################# +# Special rule to create the mouse shape (which must be a shape file) +mouse.hi: $(.path.anm)hires\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)hires\mouse.anm; + end; +! $(.path.hi)$&.hi $(SHAPEBUFFSIZE) + +# Special rule to create the mouse shape (which must be a shape file) +mouse.low: $(.path.anm)lores\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)lores\mouse.anm; + end; +! $(.path.low)$&.low $(SHAPEBUFFSIZE) + + +############################################################# +# Special build rule for radar animations so that they won't. +# +NATORADR.HI: $(.path.anm)hires\NATORADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + +USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + + +############################################################# +# Debug text file creation. +debug.eng: debug.txt + utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h + + diff --git a/CODE/ADATA.CPP b/CODE/ADATA.CPP new file mode 100644 index 0000000..8b95fb0 --- /dev/null +++ b/CODE/ADATA.CPP @@ -0,0 +1,2361 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ADATA.CPP 3 3/07/97 4:27p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static AnimTypeClass const AtomBomb( + ANIM_ATOM_BLAST, // Animation number. + "ATOMSFX", // Data name of animation. + 72, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SputDoor( + ANIM_SPUTDOOR, // Animation number. + "SPUTDOOR", // Data name of animation. + 42, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +// Electrocution death anim from Tesla coil +static AnimTypeClass const ElectricDie( + ANIM_ELECT_DIE, // Animation number. + "ELECTRO", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +// Electrocution death anim from Tesla coil for dog +static AnimTypeClass const DogElectricDie( + ANIM_DOG_ELECT_DIE, // Animation number. + "ELECTDOG", // Data name of animation. + 17, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +static AnimTypeClass const SAMN( + ANIM_SAM_N, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNW( + ANIM_SAM_NW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 22, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*1, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMW( + ANIM_SAM_W, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 40, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*2, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSW( + ANIM_SAM_SW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*3, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMS( + ANIM_SAM_S, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 76, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*4, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSE( + ANIM_SAM_SE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 94, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*5, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAME( + ANIM_SAM_E, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 112, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*6, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNE( + ANIM_SAM_NE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 130, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*7, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const LZSmoke( + ANIM_LZ_SMOKE, // Animation number. + "SMOKLAND", // Data name of animation. + 32, // Maximum dimension of animation. + 72, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 72, // Loop start frame number. + 91, // Ending frame of loop back. + -1, // Number of animation stages. + 255, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations. Primarily used on trees and buildings. +*/ +static AnimTypeClass const BurnSmall( + ANIM_BURN_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnMed( + ANIM_BURN_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnBig( + ANIM_BURN_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations that trail into smoke. Used for +** buildings and the gunboat. +*/ +static AnimTypeClass const OnFireSmall( + ANIM_ON_FIRE_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_SMOKE_M +); +static AnimTypeClass const OnFireMed( + ANIM_ON_FIRE_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_SMALL +); +static AnimTypeClass const OnFireBig( + ANIM_ON_FIRE_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_MED +); +static AnimTypeClass const Parachute( + ANIM_PARACHUTE, // Animation number. + "PARACH", // Data name of animation. + 32, // Maximum dimension of animation. + 15, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ParaBomb( + ANIM_PARA_BOMB, // Animation number. + "PARABOMB", // Data name of animation. + 32, // Maximum dimension of animation. + 8, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FBall1( + ANIM_FBALL1, // Animation number. + "FBALL1", // Data name of animation. + 67, // Maximum dimension of animation. + 6, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag1( + ANIM_FRAG1, // Animation number. + "FRAG1", // Data name of animation. + 45, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM30, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit1( + ANIM_VEH_HIT1, // Animation number. + "VEH-HIT1", // Data name of animation. + 30, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit2( + ANIM_VEH_HIT2, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit3( + ANIM_VEH_HIT3, // Animation number. + "VEH-HIT3", // Data name of animation. + 19, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const ArtExp1( + ANIM_ART_EXP1, // Animation number. + "ART-EXP1", // Data name of animation. + 41, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM22, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm1( + ANIM_NAPALM1, // Animation number. + "NAPALM1", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm2( + ANIM_NAPALM2, // Animation number. + "NAPALM2", // Data name of animation. + 41, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm3( + ANIM_NAPALM3, // Animation number. + "NAPALM3", // Data name of animation. + 78, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_LAUNCH, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokePuff( + ANIM_SMOKE_PUFF, // Animation number. + "SMOKEY", // Data name of animation. + 24, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FireBallFade( + ANIM_FBALL_FADE, // Animation number. + "FB2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Piff( + ANIM_PIFF, // Animation number. + "PIFF", // Data name of animation. + 13, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const PiffPiff( + ANIM_PIFFPIFF, // Animation number. + "PIFFPIFF", // Data name of animation. + 20, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire3( + ANIM_FIRE_SMALL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire1( + ANIM_FIRE_MED2, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire4( + ANIM_FIRE_TINY, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire2( + ANIM_FIRE_MED, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const OilFieldBurn( + ANIM_OILFIELD_BURN, // Animation number. + "FLMSPT", // Data name of animation. + 42, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 33, // Loop start frame number. + 99, // Ending frame of loop back. + 66, // Number of animation stages. + 65535, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Gunfire( + ANIM_MUZZLE_FLASH, // Animation number. + "GUNFIRE", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 1, // Number of animation stages. + 1, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokeM( + ANIM_SMOKE_M, // Animation number. + "SMOKE_M", // Data name of animation. + 28, // Maximum dimension of animation. + 30, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 67, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 6, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Mini-gun fire effect -- used by guard towers. +*/ +static AnimTypeClass const GUNN( + ANIM_GUN_N, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNW( + ANIM_GUN_NW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 6, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNW( + ANIM_GUN_W, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 12, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSW( + ANIM_GUN_SW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNS( + ANIM_GUN_S, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSE( + ANIM_GUN_SE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 30, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNE( + ANIM_GUN_E, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 36, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNE( + ANIM_GUN_NE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 42, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const CDeviator( + ANIM_CRATE_DEVIATOR, // Animation number. + "DEVIATOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateArmor( + ANIM_CRATE_ARMOR, // Animation number. + "ARMOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateSpeed( + ANIM_CRATE_SPEED, // Animation number. + "SPEED", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateFPower( + ANIM_CRATE_FPOWER, // Animation number. + "FPOWER", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateTQuake( + ANIM_CRATE_TQUAKE, // Animation number. + "TQUAKE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CDollar( + ANIM_CRATE_DOLLAR, // Animation number. + "DOLLAR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEarth( + ANIM_CRATE_EARTH, // Animation number. + "EARTH", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEmpulse( + ANIM_CRATE_EMPULSE, // Animation number. + "EMPULSE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CInvun( + ANIM_CRATE_INVUN, // Animation number. + "INVUN", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMine( + ANIM_CRATE_MINE, // Animation number. + "MINE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CRapid( + ANIM_CRATE_RAPID, // Animation number. + "RAPID", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CStealth( + ANIM_CRATE_STEALTH, // Animation number. + "STEALTH2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ChronoBox( + ANIM_CHRONO_BOX, // Animation number. + "CHRONBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const GPSBox( + ANIM_GPS_BOX, // Animation number. + "GPSBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const InvulBox( + ANIM_INVUL_BOX, // Animation number. + "INVULBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ParaBox( + ANIM_PARA_BOX, // Animation number. + "PARABOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const SonarBox( + ANIM_SONAR_BOX, // Animation number. + "SONARBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CMissile( + ANIM_CRATE_MISSILE, // Animation number. + "MISSILE2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const MoveFlash( + ANIM_MOVE_FLASH, // Animation number. + "MOVEFLSH", // Data name of animation. + 24, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + true, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const Corpse1( + ANIM_CORPSE1, // Animation number. + "CORPSE1", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse2( + ANIM_CORPSE2, // Animation number. + "CORPSE2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse3( + ANIM_CORPSE3, // Animation number. + "CORPSE3", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Twinkle1( + ANIM_TWINKLE1, // Animation number. + "TWINKLE1", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle2( + ANIM_TWINKLE2, // Animation number. + "TWINKLE2", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle3( + ANIM_TWINKLE3, // Animation number. + "TWINKLE3", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Flak( + ANIM_FLAK, // Animation number. + "FLAK", // Data name of animation. + 8, // Maximum dimension of animation. + 7, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp1( + ANIM_WATER_EXP1, // Animation number. + "H2O_EXP1", // Data name of animation. + 64, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp2( + ANIM_WATER_EXP2, // Animation number. + "H2O_EXP2", // Data name of animation. + 40, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp3( + ANIM_WATER_EXP3, // Animation number. + "H2O_EXP3", // Data name of animation. + 32, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); + + +static AnimTypeClass const MineExp1( + ANIM_MINE_EXP1, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_MINEBLOW, // Sound effect to play. + ANIM_NONE +); + +#ifdef FIXIT_ANTS +static AnimTypeClass const AntDeath( + ANIM_ANT_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE +); +#endif + +/*********************************************************************************************** + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * * + * This is the constructor for static objects that elaborate the various animation types * + * allowed in the game. Each animation in the game is of one of these types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass::AnimTypeClass( + AnimType anim, + char const * name, + int size, + int biggest, + bool istheater, + bool isnormal, + bool iswhitetrans, + bool isscorcher, + bool iscrater, + bool issticky, + bool ground, + bool istrans, + bool isflame, + fixed damage, + int delaytime, + int start, + int loopstart, + int loopend, + int stages, + int loops, + VocType soundid, + AnimType chainto) : + ObjectTypeClass(RTTI_ANIMTYPE, + int(anim), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsNormalized(isnormal), + IsGroundLayer(ground), + IsTranslucent(istrans), + IsWhiteTrans(iswhitetrans), + IsFlameThrower(isflame), + IsScorcher(isscorcher), + IsCraterForming(iscrater), + IsSticky(issticky), + IsTheater(istheater), + Type(anim), + Size(size), + Biggest(biggest), + Damage(damage), + Delay(delaytime), + Start(start), + LoopStart(loopstart), + LoopEnd(loopend), + Stages(stages), + Loops(loops), + Sound(soundid), + ChainTo(chainto) +{ +} + + +/*********************************************************************************************** + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * * + * This routine will allocate an animation type class object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated anim type object. If no anim type * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AnimTypeClass::operator new(size_t) +{ + return(AnimTypes.Alloc()); +} + + +/*********************************************************************************************** + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * * + * This will return the anim type class object back to the memory pool from whence it was * + * previously allocated. * + * * + * INPUT: pointer -- Pointer to the anim type class object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::operator delete(void * pointer) +{ + AnimTypes.Free((AnimTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * * + * This routine is called to initialize the animation type class heap. It allocates all * + * known animation types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init_Heap(void) +{ + /* + ** These anim type class objects must be allocated in the exact order that they + ** are specified in the AnimType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AnimTypeClass(FBall1); + new AnimTypeClass(FireBallFade); + new AnimTypeClass(Frag1); + new AnimTypeClass(VehHit1); + new AnimTypeClass(VehHit2); + new AnimTypeClass(VehHit3); + new AnimTypeClass(ArtExp1); + new AnimTypeClass(Napalm1); + new AnimTypeClass(Napalm2); + new AnimTypeClass(Napalm3); + new AnimTypeClass(SmokePuff); + new AnimTypeClass(Piff); + new AnimTypeClass(PiffPiff); + new AnimTypeClass(Fire3); + new AnimTypeClass(Fire2); + new AnimTypeClass(Fire1); + new AnimTypeClass(Fire4); + new AnimTypeClass(Gunfire); + new AnimTypeClass(SmokeM); + new AnimTypeClass(BurnSmall); + new AnimTypeClass(BurnMed); + new AnimTypeClass(BurnBig); + new AnimTypeClass(OnFireSmall); + new AnimTypeClass(OnFireMed); + new AnimTypeClass(OnFireBig); + new AnimTypeClass(SAMN); + new AnimTypeClass(SAMNE); + new AnimTypeClass(SAME); + new AnimTypeClass(SAMSE); + new AnimTypeClass(SAMS); + new AnimTypeClass(SAMSW); + new AnimTypeClass(SAMW); + new AnimTypeClass(SAMNW); + new AnimTypeClass(GUNN); + new AnimTypeClass(GUNNE); + new AnimTypeClass(GUNE); + new AnimTypeClass(GUNSE); + new AnimTypeClass(GUNS); + new AnimTypeClass(GUNSW); + new AnimTypeClass(GUNW); + new AnimTypeClass(GUNNW); + new AnimTypeClass(LZSmoke); + new AnimTypeClass(CDeviator); + new AnimTypeClass(CDollar); + new AnimTypeClass(CEarth); + new AnimTypeClass(CEmpulse); + new AnimTypeClass(CInvun); + new AnimTypeClass(CMine); + new AnimTypeClass(CRapid); + new AnimTypeClass(CStealth); + new AnimTypeClass(CMissile); + new AnimTypeClass(MoveFlash); + new AnimTypeClass(OilFieldBurn); + new AnimTypeClass(ElectricDie); + new AnimTypeClass(Parachute); + new AnimTypeClass(DogElectricDie); + new AnimTypeClass(Corpse1); + new AnimTypeClass(Corpse2); + new AnimTypeClass(Corpse3); + new AnimTypeClass(SputDoor); + new AnimTypeClass(AtomBomb); + new AnimTypeClass(ChronoBox); + new AnimTypeClass(GPSBox); + new AnimTypeClass(InvulBox); + new AnimTypeClass(ParaBox); + new AnimTypeClass(SonarBox); + new AnimTypeClass(Twinkle1); + new AnimTypeClass(Twinkle2); + new AnimTypeClass(Twinkle3); + new AnimTypeClass(Flak); + new AnimTypeClass(WaterExp1); + new AnimTypeClass(WaterExp2); + new AnimTypeClass(WaterExp3); + new AnimTypeClass(CrateArmor); + new AnimTypeClass(CrateSpeed); + new AnimTypeClass(CrateFPower); + new AnimTypeClass(CrateTQuake); + new AnimTypeClass(ParaBomb); + new AnimTypeClass(MineExp1); +#ifdef FIXIT_ANTS + new AnimTypeClass(AntDeath); +#endif +} + +/*********************************************************************************************** + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * * + * This will load the animation shape data. It is called by the game initialization * + * process. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called ONLY once. * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::One_Time(void) +{ + for (AnimType index = ANIM_FIRST; index < ANIM_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + AnimTypeClass const & anim = As_Reference(index); + + if (!anim.IsTheater) { + + _makepath(fullname, NULL, NULL, As_Reference(index).IniName, ".SHP"); + + #ifndef NDEBUG + RawFileClass file(fullname); + if (file.Is_Available()) { + ((void const *&)As_Reference(index).ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)As_Reference(index).ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)As_Reference(index).ImageData) = MFCD::Retrieve(fullname); + #endif + } + } +} + + +/*********************************************************************************************** + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * * + * This routine will examine all the animation types and for any that are theater * + * specific, it will fetch a pointer to the artwork appropriate for the theater specified. * + * * + * INPUT: theater -- The theater to align the animation artwork with. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine when the theater changes. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (AnimType index = ANIM_FIRST; index < ANIM_COUNT; index++) { + AnimTypeClass const & anim = As_Reference(index); + + if (anim.IsTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + _makepath(fullname, NULL, NULL, anim.IniName, Theaters[theater].Suffix); + ((void const *&)anim.ImageData) = MFCD::Retrieve(fullname); + } + } + } +} + + +/*********************************************************************************************** + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * * + * This will convert the animation type specified into a text name. This name can be used * + * for uniquely identifying the animation. * + * * + * INPUT: anim -- The anim type to convert to a text string. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that identifies this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Anim_Name(AnimType anim) +{ + if (anim == ANIM_NONE) return(""); + + return(AnimTypeClass::As_Reference(anim).IniName); +} + + +/*********************************************************************************************** + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * * + * This routine will convert the animation type specified into a reference to the * + * animation type class object. * + * * + * INPUT: type -- The animation type to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the animation type class object. * + * * + * WARNINGS: Be sure that the animation type specified is legal. If it isn't then the * + * results of this routine are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass & AnimTypeClass::As_Reference(AnimType type) +{ + return(* AnimTypes.Ptr(type)); +} + diff --git a/CODE/ADPCM.CPP b/CODE/ADPCM.CPP new file mode 100644 index 0000000..1c87935 --- /dev/null +++ b/CODE/ADPCM.CPP @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +#include "function.h" + +extern "C" { +#include "soscomp.h" +#include "itable.cpp" +#include "dtable.cpp" + + +void sosCODECInitStream(_SOS_COMPRESS_INFO* info) +{ + info->dwSampleIndex = 0; + info->dwPredicted = 0; +} + + +unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO* info, unsigned long numbytes) +{ + unsigned long token; + long sample; + unsigned int fastindex; + unsigned char *inbuff; + unsigned short *outbuff; + + inbuff = (unsigned char *)info->lpSource; + outbuff = (unsigned short *)info->lpDest; + + // Preload variables before the big loop + fastindex = (unsigned int)info->dwSampleIndex; + sample = info->dwPredicted; + + if (!numbytes) + goto SkipLoop; + + do { + // First nibble + token = *inbuff++; + fastindex += token & 0x0f; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + + // Second nibble + fastindex += token >> 4; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + } while(--numbytes); + +SkipLoop: + + // Put local vars back + info->dwSampleIndex = (unsigned long)fastindex; + info->dwPredicted = sample; + return(numbytes << 2); +} + +} diff --git a/CODE/AIRCRAFT.CPP b/CODE/AIRCRAFT.CPP new file mode 100644 index 0000000..c9a99e6 --- /dev/null +++ b/CODE/AIRCRAFT.CPP @@ -0,0 +1,4375 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/AIRCRAFT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AIRCRAFT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * AircraftClass::Mission_Move -- Handles movement mission. * + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * AircraftClass::New_LZ -- Find a good landing zone. * + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * AircraftClass::Response_Move -- Gives audio response to move request. * + * AircraftClass::Response_Select -- Gives audio response when selected. * + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::operator delete -- Deletes the aircraft object. * + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * * + * Examines the specified object to see if it qualifies to be a civilian evacuation. This * + * can only occur if it is a civilian (or Tanya) and the special evacuation flag has been * + * set in the scenario control structure. * + * * + * INPUT: candidate -- Candidate object to examine for civilian evacuation legality. * + * * + * OUTPUT: bool; Is the specified object considered a civilian that must be auto-evacuated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Counts_As_Civ_Evac(ObjectClass const * candidate) +{ + /* + ** If the candidate pointer is missing, then return with failure code. + */ + if (candidate == NULL) return(false); + + /* + ** Only infantry objects can be considered for civilian evacuation action. + */ + if (candidate->What_Am_I() != RTTI_INFANTRY) return(false); + + /* + ** Working infantry object pointer. + */ + InfantryClass const * inf = (InfantryClass const *)candidate; + + /* + ** Certain infantry types will always be considered a civilian evacuation candidate. These + ** include the special one-time infantry that appear in some missions. + */ + if (*inf == INFANTRY_EINSTEIN || *inf == INFANTRY_GENERAL || *inf == INFANTRY_DELPHI || *inf == INFANTRY_CHAN) return(true); + + /* + ** Consider Tanya to be part of the civilian evacuation logic if the scenario is + ** specially flagged for this. + */ + if (Scen.IsTanyaEvac && *inf == INFANTRY_TANYA) return(true); + + /* + ** If the infantry is not a civilian, then it isn't allowed to be a civilian evacuation. + */ + if (!inf->Class->IsCivilian) return(false); + + /* + ** Technicians look like civilians, but are not considered a legal evacuation candidate. + */ + if (inf->IsTechnician) return(false); + + /* + ** All tests pass, so return the success of the infantry as a civilian evacuation candidate. + */ + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * * + * This routine will allocate an aircraft object from the free aircraft object pool. If * + * there are no free object available, then this routine will fail (return NULL). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were * + * available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void * AircraftClass::operator new(size_t) +{ + void * ptr = Aircraft.Allocate(); + if (ptr) { + ((AircraftClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * AircraftClass::operator delete -- Deletes the aircraft object. * + * * + * This routine will return the aircraft object back to the free aircraft object pool. * + * * + * INPUT: ptr -- Pointer to the aircraft object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::operator delete(void * ptr) +{ + if (ptr) { + ((AircraftClass *)ptr)->IsActive = false; + } + Aircraft.Free((AircraftClass *)ptr); +} + + +/*********************************************************************************************** + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * * + * This routine is the constructor for aircraft objects. An aircraft object can be * + * created and possibly placed into the game system by this routine. * + * * + * INPUT: classid -- The type of aircraft to create. * + * * + * house -- The owner of this aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftClass::AircraftClass(AircraftType classid, HousesType house) : + FootClass(RTTI_AIRCRAFT, Aircraft.ID(this), house), + Class(AircraftTypes.Ptr((int)classid)), + SecondaryFacing(PrimaryFacing), + Passenger(false), + IsLanding(false), + IsTakingOff(false), + IsHovering(false), + Jitter(0), + SightTimer(0), + AttacksRemaining(1) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + Height = FLIGHT_LEVEL; + Strength = Class->MaxStrength; + NavCom = TARGET_NONE; + + /* + ** Keep count of the number of units created. Dont track cargo planes as they are created + ** automatically, not bought. + */ +// if (/*classid != AIRCRAFT_CARGO && */ Session.Type == GAME_INTERNET) { +// House->AircraftTotals->Increment_Unit_Total((int)classid); +// } + +} + + +/*********************************************************************************************** + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * * + * This routine is used to transition the aircraft from the limbo to the non limbo state. * + * It occurs when the aircraft is placed on the map for whatever reason. When it is * + * unlimboed, only then will normal game processing recognize it. * + * * + * INPUT: coord -- The coordinate that the aircraft should appear at. * + * * + * dir -- The direction it should start facing. * + * * + * strength (optional) -- sets initial strength * + * * + * mission (optional) -- sets initial mission * + * * + * OUTPUT: bool; Was the aircraft unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (FootClass::Unlimbo(coord, dir)) { + + if (*this == AIRCRAFT_BADGER || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera)) { + IsALoaner = true; + } + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->AScan |= (1L << Class->Type); + House->ActiveAScan |= (1L << Class->Type); + + /* + ** Hack it so that aircraft that are both passenger and cargo carrying + ** will carry passengers at the expense of ammo. + */ + if (Is_Something_Attached()) { + Ammo = 0; + Passenger = true; + } + + /* + ** Forces the body of the helicopter to face the correct direction. + */ + SecondaryFacing = dir; + + /* + ** Start rotor animation. + */ + if (!Class->IsFixedWing) { + Set_Rate(1); + Set_Stage(0); + } + + /* + ** When starting at flight level, then give it speed. When landed + ** then it must be stationary. + */ + if (Height == FLIGHT_LEVEL) { + Set_Speed(0xFF); + } else { + Set_Speed(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * * + * This will determine what shape number to use for the aircraft in its current state. * + * The shape number can be used for drawing or determine shape rectangle size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the aircraft body. * + * * + * WARNINGS: Some aircraft, particularly helicopters, require other shapes attached to it. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Shape_Number(void) const +{ + int shapenum = 0; + + switch (Class->Rotation) { + case 32: + shapenum = UnitClass::BodyShape[Dir_To_32(SecondaryFacing)]; + break; + + case 16: + shapenum = UnitClass::BodyShape[Dir_To_16(SecondaryFacing)*2]/2; + break; + + case 8: + shapenum = UnitClass::BodyShape[Dir_To_8(SecondaryFacing)*4]/4; + break; + + default: + break; + } + + /* + ** If there is a door on this aircraft (Chinook), then adjust the + ** shape number to match the door open state. + */ + if (!Is_Door_Closed()) { + shapenum = Class->Rotation + Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * * + * This routine is used to display the aircraft object at the coordinates specified. * + * The tactical map display uses this routine for all aircraft rendering. * + * * + * INPUT: x,y -- The coordinates to render the aircraft at. * + * * + * window -- The window that the coordinates are based upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (!shapefile) return; + + int shapenum = Shape_Number(); + + /* + ** Certain aircraft use algorithmic rotation for some stages. Set the + ** rotation value accordingly. A rotation of DIR_N means no rotation at all. + */ + DirType rotation = DIR_N; + if (Class->Rotation == 16) { + rotation = DirType(Rotation16[SecondaryFacing]); + } + +#ifdef TOFIX + /* + ** The orca attack helicopter uses a special shape set when it is travelling + ** forward above a certain speed. + */ + if (*this == AIRCRAFT_HIND && Get_Speed() >= MPH_MEDIUM_FAST) { + shapenum += Class->Rotation; + } +#endif + + /* + ** Helicopters that are flying have a "bobbing" effect. + */ + int jitter = 0; + if (Height == FLIGHT_LEVEL && Get_Speed() < 3) { + static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0}; + jitter = _jitter[::Frame % 16]; + } + + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL); + } + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation); + + /* + ** If this aircraft is equipped with rotor blades, then draw them at this time. + */ + if (Class->IsRotorEquipped) { + Draw_Rotors(x, y+jitter, window); + } + + /* + ** This draws any overlay graphics on the aircraft. + */ + FootClass::Draw_It(x, y-Lepton_To_Pixel(Height), window); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * * + * This routine will draw rotor blades on the aircraft. It is presumed that the aircraft * + * has already been drawn at the X and Y pixel coordinates specified. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to draw the rotor blades. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_Rotors(int x, int y, WindowNumberType window) const +{ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + int shapenum; + + /* + ** The rotor shape number depends on whether the helicopter is idling + ** or not. A landed helicopter uses slow moving "idling" blades. + */ + if (Height == 0) { + shapenum = (Fetch_Stage()%8)+4; + flags = flags | SHAPE_GHOST; + } else { + shapenum = Fetch_Stage()%4; + flags = flags | SHAPE_FADING|SHAPE_PREDATOR; + } + + if (*this == AIRCRAFT_TRANSPORT) { + int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9}; + + /* + ** Dual rotors offset along flight axis. + */ + short xx = x; + short yy = y-Lepton_To_Pixel(Height); + FacingType face = Dir_Facing(SecondaryFacing); + Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]); + CC_Draw_Shape(AircraftTypeClass::RRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2); + CC_Draw_Shape(AircraftTypeClass::LRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + } else { + + /* + ** Single rotor centered about shape. + */ + CC_Draw_Shape(AircraftTypeClass::RRotorData, shapenum, x, ((y-Lepton_To_Pixel(Height))-2), window, flags, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * * + * This routine is used to read the aircraft object data from the INI file buffer * + * specified. This is used by the scenario loader code to interpret the INI file and * + * create the specified objects therein. * + * * + * INPUT: buffer -- Pointer to the INI buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Read_INI(CCINIClass & ini) +{ + AircraftClass * air; // Working unit pointer. + HousesType inhouse; // Unit house. + AircraftType classid; // Unit class. + char buf[128]; + + int counter = ini.Entry_Count(INI_Name()); + for (int index = 0; index < counter; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)-1); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = AircraftTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != AIRCRAFT_NONE) { + + air = new AircraftClass(classid, inhouse); + if (air) { + COORDINATE coord; + int strength; + DirType dir; + + /* + ** Read the raw data. + */ + char * token = strtok(NULL, ","); + if (token) { + strength = atoi(token); + } else { + strength = 0; + } + + token = strtok(NULL, ","); + if (token) { + coord = Cell_Coord((CELL)atoi(token)); + } else { + coord = 0xFFFFFFFFL; + } + + token = strtok(NULL, ","); + if (token) { + dir = (DirType)atoi(token); + } else { + dir = DIR_N; + } + + if (!Map.In_Radar(Coord_Cell(coord))) { + delete air; + } else { + + air->Strength = air->Class->MaxStrength * fixed(strength, 256); + if (air->Unlimbo(coord, dir)) { + air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r"))); + } else { + delete air; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * * + * Hunt AI consists of finding a target and attacking it. If there is no target assigned * + * and this unit doesn't automatically hunt for more targets, then it will change * + * mission to a more passive (land and await further orders) type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of ticks before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Hunt(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + if (TarCom != NavCom) { + Assign_Destination(TarCom); + } + + enum { + LOOK_FOR_TARGET, + TAKE_OFF, + FLY_TO_TARGET, + DROP_BOMBS, + REGROUP + }; + switch (Status) { + + /* + ** Acquiring target stage. + */ + case LOOK_FOR_TARGET: + if (Target_Legal(TarCom)) { + Status = TAKE_OFF; + return(1); + } else { + if (!Team.Is_Valid()) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + + /* + ** If there is no target, then this aircraft should just do its normal thing. + */ + if (!Target_Legal(TarCom) && !Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + } + break; + + /* + ** Make the aircraft take off from the airstrip. + */ + case TAKE_OFF: + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact()/*KO && Map[Coord].Cell_Building() == Contact_With_Whom()*/) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_TARGET; + } + return(1); + + /* + ** Homing in on target stage. + */ + case FLY_TO_TARGET: + switch (Can_Fire(TarCom, 0)) { + case FIRE_FACING: + /* + ** Catch the case where it is tightly circling the target. In that + ** case, increase the delay so that it has a chance to fly away and + ** break the circle cycle. + */ + if (In_Range(TarCom, 0) || Passenger) { + return(TICKS_PER_SECOND * 2); + } + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_AMMO: + Status = REGROUP; + break; + + case FIRE_CANT: + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_OK: + Status = DROP_BOMBS; + return(1); + + default: + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + } + return(TICKS_PER_SECOND/2); + + /* + ** Dropping a stream of bombs phase. + */ + case DROP_BOMBS: + TARGET targ; + switch (Can_Fire(TarCom, 0)) { + case FIRE_OK: + targ = ::As_Target(Coord_Move(Center_Coord(), SecondaryFacing, Weapon_Range(0)-0x0200)); + if (Class->PrimaryWeapon != NULL) { + if (Class->PrimaryWeapon->IsCamera) { + Status = REGROUP; + } else { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** Force the target to be the actual target if this aircraft is + ** equipped with homing projectile. + */ + if (Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->ROT > 0) { + targ = TarCom; + } + } + Fire_At(targ, 0); + if (Class->Is_Two_Shooter()) { + Fire_At(targ, 0); + } + return(Arm); + + case FIRE_RANGE: + case FIRE_FACING: + Status = FLY_TO_TARGET; + return(TICKS_PER_SECOND*4); + + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_CANT: + Status = REGROUP; + break; + + case FIRE_AMMO: + AttacksRemaining--; + Status = REGROUP; + break; + + default: + break; + } + return(1); + + /* + ** Pull away to regroup for possibly another attack or a retreat. + */ + case REGROUP: + if (Ammo == 0) { + AttacksRemaining = 0; + if (Team.Is_Valid()) Team->Remove(this); + Enter_Idle_Mode(); + } + + if (Mission == MISSION_ATTACK || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) || (!AttacksRemaining && !Is_Something_Attached())) { + + if (IsALoaner) { + if (Team) Team->Remove(this); + Assign_Mission(MISSION_RETREAT); + Commence(); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + Commence(); + } else { + Status = LOOK_FOR_TARGET; + } + break; + + default: + break; + } + } else { + if (!Ammo) { + if (Team) Team->Remove(this); + Enter_Idle_Mode(); + } else { + if (!Target_Legal(TarCom)) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + if (!Target_Legal(TarCom)) { + Enter_Idle_Mode(); + return(1); + } + } + + Assign_Mission(MISSION_ATTACK); + return(1); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * * + * This handles the non-graphic AI processing for the aircraft. This usually entails * + * maintenance and other AI functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::AI(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Perform any base class AI processing. If during this process, the aircraft was + ** destroyed, then detect this and bail from this AI routine early. + */ + FootClass::AI(); + if (!IsActive) { + return; + } + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Handle any body rotation at this time. Body rotation can occur even if the + ** flying object is not actually moving. + */ + Rotation_AI(); + + /* + ** Handle any aircraft movement at this time. + */ + Movement_AI(); + + /* + ** Any aircraft that is not in the ground layer must be redrawn. This is a + ** performance hit, but there is no other choice. The cells under an aircraft + ** do not know if there is an aircraft above it. Thus, it cannot flag the + ** aircraft to redraw. As a consequence, all aircraft must redraw. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(); + } + + /* + ** Perform sighting every so often as controlled by the sight timer. + */ + if (IsOwnedByPlayer && Class->SightRange && SightTimer == 0) { + Look(); + SightTimer = TICKS_PER_SECOND; + } + + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Landing_Takeoff_AI()) { + return; + } + + /* + ** Always flag the map draw process to occur if there is an aircraft in the view. + ** This ensures that it will be rendered even if there is nothing else that flagged + ** the map to be redrawn. + */ + if (Map.In_View(Coord_Cell(Coord))) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the + ** aircraft is a non-player produced unit and it has completed its mission. A transport + ** helicopter that has already delivered reinforcements is a good example of this. + */ + if (Edge_Of_World_AI()) { + return; + } +} + + +/*********************************************************************************************** + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * * + * When aircraft are flying, they can overlap quite a number of cells. These cells can * + * be determined from the coordinate where the aircraft is centered and the size of the * + * aircraft's shape. Landed aircraft are a special case and are usually much smaller * + * than when flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that * + * the aircraft overlaps given the aircraft's current state. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftClass::Overlap_List(bool redraw) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static short const _list[] = { + -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), + -1, 0, 1, + (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), + -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), + -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), + REFRESH_EOL + }; + + static short const _listbadger[] = { + -(MAP_CELL_W-2), -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2), + -2, -1, 0, 1, 2, + (MAP_CELL_W-2), (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2), + -((MAP_CELL_W*2)-2), -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), -((MAP_CELL_W*2)+2), + -((MAP_CELL_W*3)-2), -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), -((MAP_CELL_W*3)+2), + REFRESH_EOL + }; + + if (redraw || Height != 0) { +#ifdef PARTIAL + Rect rect; + if (!IsSelected && Class->DimensionData != NULL && Class->IsFixedWing) { + int shapenum = min(Shape_Number(), Get_Build_Frame_Count(Class->Get_Image_Data())-1); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + + /* + ** Increase the rectangle for the aircraft since the aircraft could + ** have its shape algorithmically rotated. + */ + rect.X -= 5; + rect.Y -= 5; + rect.Width += 10; + rect.Height += 10; + + Rect hrect = rect; + + hrect.Y -= Lepton_To_Pixel(Height); + + return(Coord_Spillage_List(Coord, Union(rect, hrect), true)); + } +#endif + + if (*this == AIRCRAFT_BADGER) { + return(_listbadger); + } else { + return(_list); + } + } + return(Class->Overlap_List()); +} + + +/*********************************************************************************************** + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * * + * This routine is used to clear out the aircraft allocation system. It is called in * + * preparation for a scenario load or save game load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Init(void) +{ + Aircraft.Free_All(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * * + * This function is used to handle finding, heading toward, landing, and unloading the * + * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows * + * a different mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks to delay before calling this function again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Unload(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + Assign_Target(NavCom); + return(Mission_Hunt()); + + } else { + enum { + SEARCH_FOR_LZ, + FLY_TO_LZ, + LAND_ON_LZ, + UNLOAD_PASSENGERS, + TAKE_OFF + }; + + switch (Status) { + + /* + ** Search for an appropriate destination spot if one isn't already assigned. + */ + case SEARCH_FOR_LZ: + if (Height == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) { + Status = UNLOAD_PASSENGERS; + } else { + if (!Is_LZ_Clear(NavCom)) { + + FootClass * foot = Attached_Object(); + if (foot != NULL && foot->Team && foot->Team->Class->Origin != -1) { + Assign_Destination(::As_Target(Scen.Waypoint[foot->Team->Class->Origin])); + } else { + Assign_Destination(New_LZ(::As_Target(Scen.Waypoint[WAYPT_REINF]))); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } + } else { + if (Height == FLIGHT_LEVEL) { + Status = FLY_TO_LZ; + } else { + Status = TAKE_OFF; + } + } + } + break; + + /* + ** Fly to destination. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0100) { + SecondaryFacing.Set_Desired(Pose_Dir()); + + if (distance < 0x0010) { + Status = LAND_ON_LZ; + } + return(1); + } else { + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(5); + } + } else { + Status = SEARCH_FOR_LZ; + } + break; + + /* + ** Landing phase. Just delay until landing is complete. At that time, + ** transition to the unloading phase. + */ + case LAND_ON_LZ: + if (IsTakingOff) { + Status = TAKE_OFF; + } else { + if (Process_Landing()) { + Status = UNLOAD_PASSENGERS; + } + } + return(1); + + /* + ** Hold while unloading passengers. When passengers are unloaded the order for this + ** transport gets changed to MISSION_RETREAT. + */ + case UNLOAD_PASSENGERS: + if (!IsTethered) { + if (Is_Something_Attached()) { + FootClass * unit = (FootClass *)Detach_Object(); + + /* + ** First thing is to lift the transport off of the map so that the unlimbo + ** process for the passengers is more likely to succeed. + */ + Map.Pick_Up(Coord_Cell(Coord), this); + + if (!Exit_Object(unit)) { + delete unit; + } + + /* + ** Restore the transport back down on the map. + */ + Map.Place_Down(Coord_Cell(Coord), this); + + if (!Is_Something_Attached()) { + Enter_Idle_Mode(); + } + + } else { + + Enter_Idle_Mode(); + } + } + break; + + /* + ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it + ** will either take off or look for another landing spot to try again. + */ + case TAKE_OFF: { + if (Process_Take_Off()) { + if (Is_Something_Attached()) { + Status = SEARCH_FOR_LZ; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + } else { + Enter_Idle_Mode(); + } + } + return(1); + } + + default: + break; + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * * + * This routine examines the landing zone (as specified by the target parameter) in order * + * to determine if it is free to be landed upon. Call this routine when it is necessary * + * to double check this. Typically this occurs right before a helicopter lands and also * + * when determining the landing zone in the first place. * + * * + * INPUT: target -- The target that is the "landing zone". * + * * + * OUTPUT: bool; Is the landing zone clear for landing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Is_LZ_Clear(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(target)) return(false); + CELL cell = ::As_Cell(target); + if (!Map.In_Radar(cell)) return(false); + + /* + ** If the requested landing location is occupied, then only consider that location + ** legal if the occupying object is in radio contact with the aircraft. This presumes that + ** the two objects know what they are doing. + */ + ObjectClass * object = Map[cell].Cell_Object(); + if (object) { + if (object == this) return(true); + + if (In_Radio_Contact() && Contact_With_Whom() == object) { + return(true); + } + return(false); + } + + if (!Map[cell].Is_Clear_To_Move(SPEED_TRACK, false, false)) return(false); + + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * * + * This routine is used to determine the coordinate to use for sorting the aircraft. This * + * sorting value is used when the aircraft is on the ground. At that time the aircraft * + * must be rendered in proper relationship to the other ground objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground * + * objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Sort_Y(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * * + * This mission will be followed when the aircraft decides that it is time to leave the * + * battle. Typically, this occurs when a loaner transport has dropped off its load or when * + * an attack air vehicle has expended its ordinance. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. * + *=============================================================================================*/ +int AircraftClass::Mission_Retreat(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + if (Class->IsFixedWing && Height < FLIGHT_LEVEL) { + Height += 1; + return(3); + } + return(TICKS_PER_SECOND*10); + } + + enum { + TAKE_OFF, + FACE_MAP_EDGE, + KEEP_FLYING + }; + switch (Status) { + + /* + ** Take off if landed. + */ + case TAKE_OFF: + if (Process_Take_Off()) { + Status = FACE_MAP_EDGE; + } + return(1); + + /* + ** Set facing and speed toward the friendly map edge. + */ + case FACE_MAP_EDGE: + Set_Speed(0xFF); + + /* + ** Take advantage of the fact that the source map edge enumerations happen to + ** occur in a clockwise order and are the first four enumerations of the map + ** edge default for the house. If this value is masked and then shifted, a + ** normalized direction value results. Use this value to head the aircraft + ** toward the "friendly" map edge. + */ + PrimaryFacing.Set_Desired((DirType)((House->Control.Edge & 0x03) << 6)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Status = KEEP_FLYING; + break; + + /* + ** Just do nothing since we are headed toward the map edge. When the edge is + ** reached, the aircraft should be automatically eliminated. + */ + case KEEP_FLYING: + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * * + * This routine is called when the aircraft is to unload a passenger. The passenger must * + * be able to move under its own power. Typical situation is when a transport helicopter * + * is to unload an infantry unit. * + * * + * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. * + * * + * OUTPUT: bool; Was the unit unloaded successfully? * + * * + * WARNINGS: The unload process is merely started by this routine. Radio contact is * + * established with the unloading unit and when the unit is clear of the aircraft * + * the radio contact will be broken and then the aircraft is free to pursue * + * other. * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Exit_Object(TechnoClass * unit) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E}; + CELL cell; + + /* + ** Find a free cell to drop the unit off at. + */ + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]); + if (unit->Can_Enter_Cell(cell) == MOVE_OK) break; + } + + // Should perform a check here to see if no cell could be found. + + /* + ** If the passenger can be placed on the map, then start it moving toward the + ** destination cell and establish radio contact with the transport. This is used + ** to make sure that the transport waits until the passenger is clear before + ** unloading the next passenger or taking off. + */ + if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(cell)); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * * + * Call this routine when a passenger needs to be dropped off by parachute. One passenger * + * is offloaded by a call to this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay time that it is safe to wait before processing any further * + * paradrop actions. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Paradrop_Cargo(void) +{ + FootClass * passenger = Detach_Object(); + if (passenger) { + if (!passenger->Paradrop(Center_Coord())) { + Attach(passenger); + } else { + + /* + ** Play a sound effect of the parachute opening. + */ + Sound_Effect(VOC_CHUTE1, Coord); + + if (Team.Is_Valid()) { + Team->Remove(passenger); + if (passenger->House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + } +// Arm = Rearm_Delay(IsSecondShot); + Arm = 0; + } + } + return(Arm); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * * + * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the * + * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the * + * default speed defined in the BulletTypeClass structure. * + * * + * INPUT: target -- The target that the projectile is heading for. * + * * + * which -- Which weapon to use in the attack. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * AircraftClass::Fire_At(TARGET target, int which) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Passenger aircraft will actually paradrop their cargo instead of + ** firing their weapon. + */ + if (Is_Something_Attached()) { + Paradrop_Cargo(); + return(0); + } + + /* + ** If the weapon is actually a camera, then perform the "snapshot" of the + ** ground instead of normal weapon fire. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) { + if (House->Is_Ally(PlayerPtr)) { + Map.Sight_From(Coord_Cell(Center_Coord()), 9, House, false); + } + Ammo = 0; + Arm = Rearm_Delay(IsSecondShot); + return(0); + } + + + BulletClass * bullet = FootClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Falling bullets move at a speed proportionate to the delivery craft. + */ + if (bullet->Class->IsDropping) { + bullet->Fly_Speed(40, MPH_MEDIUM_SLOW); // TCTC To fix. + } + } + return(bullet); +} + + +/*********************************************************************************************** + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * * + * This routine is used to apply damage to the specified aircraft. This is where any * + * special crash animation will be initiated. * + * * + * INPUT: damage -- Reference to the damage that will be applied to the aircraft. * + * This value will be filled in with the actual damage that was * + * applied. * + * * + * distance -- Distance from the source of the explosion to this aircraft. * + * * + * warhead -- The warhead type that the damage occurs from. * + * * + * source -- Pointer to the originator of the damage. This can be used so that * + * proper "thank you" can be delivered. * + * * + * OUTPUT: Returns with the result of the damage as it affects this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1995 JLB : Created. * + *=============================================================================================*/ +ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Flying aircraft take half damage. + */ + if (Height) { + damage /= 2; + } + + /* + ** Apply the damage to the aircraft. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** Special action is performed if the aircraft is killed -- the cargo is destroyed + ** as well. + */ + switch (res) { + case RESULT_DESTROYED: + Kill_Cargo(source); + Death_Announcement(); + new AnimClass(ANIM_FBALL1, Target_Coord()); + + /* + ** Parachute a survivor if possible. + */ + if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) { + InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House); + if (infantry != NULL) { + if (!infantry->Paradrop(Center_Coord())) { + delete infantry; + } + } + } + + delete this; + break; + + default: + case RESULT_HALF: + break; + } + + return(res); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Move -- Handles movement mission. * + * * + * This state machine routine is used when an aircraft (usually helicopter) is to move * + * from one location to another. It will handle any necessary take off and landing this * + * may require. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames that should elapse before this routine * + * is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + enum { + TAKE_OFF, + FLY_TOWARD_TARGET + }; + + switch (Status) { + int distance; + + case TAKE_OFF: + + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TOWARD_TARGET; + } + return(1); + + case FLY_TOWARD_TARGET: + PrimaryFacing.Set_Desired(Direction(NavCom)); + distance = Distance(NavCom); + + if (distance < 0x00C0) { + MissionType mission = MISSION_GUARD; + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers() ) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + + /* + ** If this aircraft has nowhere else to go, meaning that + ** there is no airfield available, then it has to crash. + */ + if (Is_Target_Cell(NavCom)) { + + if (Process_Landing()) { + Strength = 1; + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(1); + } + return(500); + } + mission = MISSION_MOVE; + } + Assign_Mission(mission); + Commence(); + } else { + if (!Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + return(1); + } + break; + + default: + break; + } + + return(5); + } + + enum { + VALIDATE_LZ, + TAKE_OFF, + FLY_TO_LZ, + LAND + }; + switch (Status) { + + /* + ** Double check and change LZ if necessary. + */ + case VALIDATE_LZ: + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } else { + if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } else { + Status = TAKE_OFF; + } + } + break; + + /* + ** Take off if necessary. + */ + case TAKE_OFF: + if (!Target_Legal(NavCom)) { + Status = VALIDATE_LZ; + } else { + if (Process_Take_Off()) { + + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_LZ; + } + return(1); + } + break; + + /* + ** Fly toward target. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LAND; + } + return(1); + } + +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + SecondaryFacing.Set_Desired(Direction(NavCom)); + + } else { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + if (!Target_Legal(NavCom)) { + Status = LAND; + } + } + return(1); + + /* + ** Land on target. + */ + case LAND: + if (IsTakingOff) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + Status = TAKE_OFF; + } + if (Process_Landing()) { + if (MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + } + return(1); + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * * + * Use this routine when the mission for the aircraft is in doubt. This routine will find * + * an appropriate mission for the aircraft and dispatch it. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Enter_Idle_Mode(bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + MissionType mission = MISSION_GUARD; + if (Class->IsFixedWing) { + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + mission = MISSION_RETREAT; + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + if (Team.Is_Valid()) return; + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (Mission != MISSION_ATTACK && IsALoaner && Ammo == 0 && Class->PrimaryWeapon != NULL) { + mission = MISSION_HUNT; + } else { + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); + Assign_Destination(TARGET_NONE); + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + if (Class->IsFixedWing) { + Status = 0; //BG - reset the mission status to avoid landing on the ground next to the airstrip + if (IsLanding) { + Process_Take_Off(); + } + } + mission = MISSION_ENTER; + } else { + mission = MISSION_RETREAT; + } + } + } + } + + } else { + + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + if (Is_Something_Attached()) { + + /* + ** In the case of a computer controlled helicopter that hold passengers, + ** don't unload when landing. Wait for specific instructions from the + ** controlling team. + */ + if (Team.Is_Valid()) { +// if (Team.Is_Valid() && !House->IsHuman) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + } + } else { + mission = MISSION_RETREAT; + } + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + if (Is_Something_Attached()) { + if (IsALoaner) { + if (Team) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + Assign_Destination(Good_LZ()); + } + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + + if (Class->PrimaryWeapon != NULL) { + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (IsALoaner) { + + /* + ** If it has no ammo, then break off of the team and leave the map. + ** If it can fight, then give it fighting orders. + */ + if (Ammo == 0) { + if (Team.Is_Valid()) Team->Remove(this); + mission = MISSION_RETREAT; + } else { + if (!Team.Is_Valid()) { + mission = MISSION_HUNT; + } + } + + } else { + + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()/* && !ship->In_Radio_Contact()*/) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } else { + if (Team) return; + + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } + } + Assign_Mission(mission); + Commence(); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * * + * This support routine is used when the helicopter is to fly to the destination. It can * + * optionally slow the helicopter down as it approaches the destination. * + * * + * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? * + * * + * OUTPUT: Returns with the distance remaining between the aircraft and the destination. * + * * + * WARNINGS: Because the aircraft can move at a fast speed, the distance to target value * + * will probably never be zero. The likely case will be that the aircraft * + * overshoots the target. * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + * 03/05/1996 JLB : Specifies destination target value. * + *=============================================================================================*/ +int AircraftClass::Process_Fly_To(bool slowdown, TARGET dest) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) slowdown = false; + + COORDINATE coord; + if (Is_Target_Building(dest)) { + coord = As_Building(dest)->Docking_Coord(); + } else { + coord = As_Coord(dest); + } + int distance = Distance(coord); + + PrimaryFacing.Set_Desired(Direction(coord)); + + if (slowdown) { + int speed = min(distance, 0x0300); + speed = Bound(speed/3, 0x0020, 0x00FF); + if (Speed != speed) { + Set_Speed(speed); + } + } + + if (distance < 0x0010) { + if (slowdown) { + Set_Speed(0); + } + distance = 0; + } + return(distance); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. * + * * + * This displays the current status of the aircraft class to the mono monitor. By this * + * display bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Debug_Dump(MonoClass * mono) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_AIRCRAFT)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", AttacksRemaining); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * * + * This routine is used when the player clicks over the speicifed object. It will assign * + * the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action that was nominally determined by the What_Action function. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will alter the game sequence and causes an event packet to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_NOMOVE: + return; + + case ACTION_ENTER: + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE); + break; + + default: + break; + } + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * * + * This routine is used when the player clicks the mouse of the specified cell. It will * + * assign the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action nominally determined by What_Action(). * + * * + * cell -- The cell over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will affect the game sequence and causes an event object to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * * + * This routine is called as a result of player input with the intent to change the * + * mission of the aircraft. * + * * + * INPUT: mission -- The mission requested of the aircraft. * + * * + * target -- The value to assign to the aircraft's targeting computer. * + * * + * dest. -- The value to assign to the aircraft's navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: The mission specified will be executed at an indeterminate future game frame. * + * This is controlled by net/modem propagation delay. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(TargetClass(this), mission, target, destination); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine is used to determine what action will likely be performed if the mouse * + * were clicked over the object specified. The display system calls this routine to * + * control the mouse shape. * + * * + * INPUT: target -- Pointer to the object that the mouse is currently over. * + * * + * OUTPUT: Returns with the action that will occur if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(ObjectClass const * target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(target); + + if (action == ACTION_SELF && !How_Many()) { + action = ACTION_NONE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing && House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_VESSEL && *(VesselClass *)target == VESSEL_CARRIER && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#endif + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_NONE) { + action = What_Action(Coord_Cell(target->Center_Coord())); + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && target->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)target; + if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_ENTER; + } + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine will determine what action would occur if the mouse were clicked over the * + * cell specified. The display system calls this routine to determine what mouse shape * + * to use. * + * * + * INPUT: cell -- The cell over which the mouse is currently positioned. * + * * + * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the * + * specified cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(CELL cell) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + if (action == ACTION_MOVE && Session.Type == GAME_NORMAL && !Map[cell].IsVisible) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * * + * Use this routine to get the desired facing the aircraft should assume when landing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the normal default facing the aircraft should have when landed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 03/04/1996 JLB : Fixed wing aircraft always face down the runway. * + *=============================================================================================*/ +DirType AircraftClass::Pose_Dir(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (*this == AIRCRAFT_TRANSPORT) { + return(DIR_N); + } + if (Class->IsFixedWing) { + return(DIR_E); + } + return(DIR_NE); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * * + * This routine is the state machine that handles the attack mission for aircraft. It will * + * handling homing in on and firing on the target in the aircraft's targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to pass before this routine must be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Fixes brain dead helicopter for Nod scen #7. * + *=============================================================================================*/ +int AircraftClass::Mission_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + return(Mission_Hunt()); + } + + enum { + VALIDATE_AZ, + PICK_ATTACK_LOCATION, + TAKE_OFF, + FLY_TO_POSITION, + FIRE_AT_TARGET, + FIRE_AT_TARGET2, + RETURN_TO_BASE + }; + switch (Status) { + + /* + ** Double check target and validate the attack zone. + */ + case VALIDATE_AZ: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + + /* + ** Pick a good location to attack from. + */ + case PICK_ATTACK_LOCATION: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Assign_Destination(Good_Fire_Location(TarCom)); + if (Target_Legal(NavCom)) { + Status = TAKE_OFF; + } else { + Status = RETURN_TO_BASE; + } + } + break; + + /* + ** Take off (if necessary). + */ + case TAKE_OFF: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + if (Process_Take_Off()) { + Status = FLY_TO_POSITION; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Start flying toward the destination by skewing at first. + ** As the flight progresses, the body will rotate to face + ** the direction of travel. + */ + int diff = SecondaryFacing.Difference(Direction(NavCom)); + diff = Bound(diff, -128, 128); + PrimaryFacing = DirType((int)SecondaryFacing.Current()+diff); + } + return(1); + } + break; + + /* + ** Fly to attack location. + */ + case FLY_TO_POSITION: + if (Target_Legal(TarCom)) { + + /* + ** If the navcom was cleared mysteriously, then try to pick + ** a new attack location. This is a likely event if the player + ** clicks on a new target while in flight to an existing target. + */ + if (!Target_Legal(NavCom)) { + Status = PICK_ATTACK_LOCATION; + return(1); + } + + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0200) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + + if (distance < 0x0010) { + Status = FIRE_AT_TARGET; + Assign_Destination(TARGET_NONE); + } + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + return(1); + } + } else { + Status = RETURN_TO_BASE; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + Status = FIRE_AT_TARGET2; + break; + + case FIRE_REARM: + case FIRE_FACING: + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = FIRE_AT_TARGET2; + } + break; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET2: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_REARM: + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + + if (Ammo) { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } else { + Status = RETURN_TO_BASE; + } + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + if (!In_Range(TarCom)) { + Status = PICK_ATTACK_LOCATION; + } else { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } + } + break; + } + break; + + /* + ** Fly back to landing spot. + */ + case RETURN_TO_BASE: + /* + ** Break off of firing at the target if there is no more + ** point in attacking it this mission. The player will + ** reassign a target for the next mission. + */ + if (!Ammo && (IsALoaner || House->IsHuman)) { + Assign_Target(TARGET_NONE); + } + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + break; + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::New_LZ -- Find a good landing zone. * + * * + * Use this routine to locate a good landing zone that is nearby the location specified. * + * By using this routine it is possible to assign the same landing zone to several * + * aircraft and they will land nearby without conflict. * + * * + * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). * + * * + * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. * + * * + * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is * + * extensive blocking terrain in the vicinity. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::New_LZ(TARGET oldlz) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) { + COORDINATE coord = As_Coord(oldlz); + + /* + ** Scan outward in a series of concentric rings up to certain distance + ** in cells. + */ + for (int radius = 0; radius < Rule.LZScanRadius / CELL_LEPTON_W; radius++) { + FacingType modifier = Random_Pick(FACING_N, FACING_NW); + CELL lastcell = -1; + + /* + ** Perform a radius scan out from the original center location. Try to + ** find a cell that is allowed to be a legal LZ. + */ + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W)); + if (Map.In_Radar(newcell)) { + TARGET newtarget = ::As_Target(newcell); + + if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) { + return(newtarget); + } + lastcell = newcell; + } + } + } + } + return(oldlz); +} + + +/*********************************************************************************************** + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * * + * This routine receives all radio messages directed at this aircraft. It is used to handle * + * all inter-object coordination. Typically, this would be for transport helicopters and * + * other complex landing operations required of helicopters. * + * * + * INPUT: from -- The source of this radio message. * + * * + * message -- The message itself. * + * * + * param -- An optional parameter that may be used to transfer additional * + * data. * + * * + * OUTPUT: Returns with the radio response from the aircraft. * + * * + * WARNINGS: Some radio messages are handled by the base classes. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + switch (message) { + + case RADIO_PREPARED: + if (Target_Legal(TarCom)) return(RADIO_NEGATIVE); + if ((Height == 0 && Ammo == Class->MaxAmmo) || (Height > 0 && Ammo > 0)) return(RADIO_ROGER); + return(RADIO_NEGATIVE); + + /* + ** Something disastrous has happened to the object in contact with. Fall back + ** and regroup. This means that any landing process is immediately aborted. + */ + case RADIO_RUN_AWAY: + if (IsLanding) { + IsLanding = false; + IsTakingOff = true; + } + if (Class->IsFixedWing) { + Assign_Destination(Good_LZ()); + if (!Target_Legal(NavCom)) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_MOVE); + } + } else { + Scatter(0, true); + } + break; + + /* + ** The ground control requests that this specified landing spot be used. + */ + case RADIO_MOVE_HERE: + FootClass::Receive_Message(from, message, param); + if (Is_Target_Building(param)) { + if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) { + return(RADIO_NEGATIVE); + } + Assign_Mission(MISSION_ENTER); + Assign_Destination((TARGET)param); + } else { + Assign_Mission(MISSION_MOVE); + Assign_Destination((TARGET)param); + } + Commence(); + return(RADIO_ROGER); + + /* + ** Ground control is requesting if the aircraft requires navigation direction. + */ + case RADIO_NEED_TO_MOVE: + FootClass::Receive_Message(from, message, param); + if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + Close_Door(5, 4); + } + + /* + ** If a civilian has entered the transport, then the transport will immediately + ** fly off the map. + */ + if (_Counts_As_Civ_Evac(from)) { + Assign_Mission(MISSION_RETREAT); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + FootClass::Receive_Message(from, message, param); + + if (!IsTethered && !IsLanding && !IsTakingOff && Height == 0) { + + Open_Door(5, 4); + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + CELL cell; + /*DirType dir =*/ Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (/*!In_Radio_Contact() &&*/ How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + default: + break; + } + + /* + ** Let the base class take over processing this message. + */ + return(FootClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * * + * This routine is used by the transport helicopter to determine the location where the * + * infantry passengers should line up before loading. * + * * + * INPUT: object -- The object that is trying to load up on this transport. * + * * + * -- Reference to the cell that the passengers should move to before the * + * actual load process may begin. * + * * + * OUTPUT: Returns with the direction that the helicopter should face for the load operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/30/1995 JLB : Revamped to scan all adjacent cells. * + *=============================================================================================*/ +DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + CELL center = Coord_Cell(Center_Coord()); + for (int sweep = FACING_N; sweep < FACING_S; sweep++) { + moveto = Adjacent_Cell(center, FacingType(FACING_S+sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + + moveto = Adjacent_Cell(center, FacingType(FACING_S-sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * * + * This routine is used by the main game state machine processor. This utility routine * + * handles a helicopter as it transitions from landed to flying state. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter reached flight level now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Take_Off(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsLanding = false; + IsTakingOff = true; + + if (Class->IsFixedWing) { + Set_Speed(0xFF); + if (Height == FLIGHT_LEVEL) { + return(true); + } + + } else { + + switch (Height) { + case 0: + Close_Door(5, 4); + PrimaryFacing = SecondaryFacing; + break; + + case FLIGHT_LEVEL/2: + PrimaryFacing.Set_Desired(Direction(NavCom)); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/3): + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Set_Speed(0x20); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/5): + Set_Speed(0x40); + break; + + case FLIGHT_LEVEL: + Set_Speed(0xFF); + IsTakingOff = false; + return(true); + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * * + * This is a support routine that is called by the main state machine routines. This * + * routine is responsible for handling the helicopter as it transitions from flight to * + * landing. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter completely landed now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 03/04/1996 JLB : Handles fixed wing aircraft. * + *=============================================================================================*/ +bool AircraftClass::Process_Landing(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsTakingOff = false; + IsLanding = true; + + if (Class->IsFixedWing) { + int distance = Distance(NavCom); + + if (distance > 0x0100) { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + + switch (Height) { + case 0: + Set_Speed(0); + IsLanding = false; + return(true); + + default: +// if (distance*2 > Class->LandingSpeed) { +// Set_Speed(Class->LandingSpeed); +// } else { +// Set_Speed(distance/2); +// } + + Set_Speed(Class->LandingSpeed / House->AirspeedBias); + break; + } + + } else { + switch (Height) { + case 0: + IsLanding = false; + return(true); + + case FLIGHT_LEVEL/2: + Set_Speed(0); + break; + + case FLIGHT_LEVEL: + break; + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * * + * This routine is used when the passability of a cell needs to be determined. This is * + * necessary when scanning for a location that the aircraft can land. * + * * + * INPUT: cell -- The cell location to check for landing. * + * * + * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Map.In_Radar(cell)) return(MOVE_NO); + + CellClass * cellptr = &Map[cell]; + + ObjectClass const * occupier = cellptr->Cell_Occupier(); + + if (occupier == NULL || + !occupier->Is_Techno() || + ((TechnoClass *)occupier)->House->Is_Ally(House) || + (((TechnoClass *)occupier)->Cloak != CLOAKED && + (ScenarioInit == 0 && (occupier->What_Am_I() != RTTI_BUILDING || !((BuildingClass*)occupier)->Class->IsInvisible)) ) + ) { + + if (!cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false)) return(MOVE_NO); + } + + if (Session.Type == GAME_NORMAL && IsOwnedByPlayer && !cellptr->IsMapped) { + return(MOVE_NO); + } + + return(MOVE_OK); +} + + +/*********************************************************************************************** + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * * + * Given the specified target, this routine will locate a good spot for the aircraft to * + * fire at the target. * + * * + * INPUT: target -- The target that is desired to be attacked. * + * * + * OUTPUT: Returns with the target location of the place that firing should be made from. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 06/14/1995 JLB : Finer resolution on ring scan. * + * 11/02/1996 JLB : Bias fire position to get closer to moving objects. * + *=============================================================================================*/ +TARGET AircraftClass::Good_Fire_Location(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(target)) { + int range = Weapon_Range(0); + COORDINATE tcoord = As_Coord(target); + CELL bestcell = 0; + CELL best2cell = 0; + int bestval = -1; + int best2val = -1; + + /* + ** Try to get closer to a target that is moving. + */ + COORDINATE altcoord = 0; + if (Is_Target_Object(target) && As_Object(target)->Is_Foot()) { + TARGET alttarg = ((FootClass *)As_Object(target))->NavCom; + if (Target_Legal(alttarg)) { + altcoord = As_Coord(alttarg); + } + } + + for (int r = range-0x0100; r > 0x0100; r -= 0x0100) { + for (int face = 0; face < 255; face += 16) { + COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r); + CELL newcell = Coord_Cell(newcoord); + + if (Map.In_Radar(newcell) && (Session.Type != GAME_NORMAL || Map[newcell].IsVisible) && Cell_Seems_Ok(newcell, true)) { + int dist; + if (altcoord != 0) { + dist = ::Distance(newcoord, altcoord); + } else { + dist = Distance(newcoord); + } + if (bestval == -1 || dist < bestval) { + best2val = bestval; + best2cell = bestcell; + bestval = dist; + bestcell = newcell; + } + } + } + if (bestval != -1) break; + } + + if (best2val == -1) { + best2cell = bestcell; + } + + /* + ** If it found a good firing location, then return this location as + ** a target value. + */ + if (bestval != -1) { + if (Percent_Chance(50)) { + return(::As_Target(bestcell)); + } else { + return(::As_Target(best2cell)); + } + } + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * * + * This routine examines the navigation computers of other aircraft in order to see if the * + * specified cell is safe to fly to. The intent of this routine is to avoid unnecessary * + * mid-air collisions. * + * * + * INPUT: cell -- The cell to examine for clear airspace. * + * * + * strict -- Should the scan consider the aircraft, that is making this check, a * + * blocking aircraft. Typically, the aircraft itself is not considered * + * a blockage -- an aircraft can always exist where it is currently * + * located. A strict check is useful for helicopters that need to move * + * around at the slightest provocation. * + * * + * OUTPUT: Is the specified cell free from airspace conflicts? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that no other aircraft are heading to the selected location. If they + ** are, then don't consider the location as valid. + */ + TARGET astarget = ::As_Target(cell); + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * air = Aircraft.Ptr(index); + if (air && (strict || air != this) && !air->IsInLimbo) { + if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) { + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * * + * This routine is used by the render logic to draw the little container "pips". This * + * corresponds to the number of passengers for a transport helicopter or the number of * + * shots remaining for an attack helicopter. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of "pips" to render on the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Pip_Count(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + int retval = 0; + + if (Class->Max_Passengers() > 0) { + retval = How_Many(); + } else { + if (Ammo) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + } + return(retval); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * * + * This routine is used when the aircraft needs to fly for either rearming or repairing. * + * It tries to establish contact with the support building. Once contact is established * + * the ground controller takes care of commanding the aircraft. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/04/1995 JLB : Ground controller gives orders. * + *=============================================================================================*/ +int AircraftClass::Mission_Enter(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + TAKEOFF, + ALTITUDE, + STACK, + DOWNWIND, + CROSSWIND, + TRAVEL, + LANDING + }; + + /* + ** Verify that it has a valid NavCom. If it doesn't then request one from the + ** building this building is trying to land upon. If that fails, then enter + ** idle mode. + */ + if (!Target_Legal(NavCom) && In_Which_Layer() != LAYER_GROUND) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Enter_Idle_Mode(); + return(1); + } + } + + switch (Status) { + case INITIAL: + if (Height < FLIGHT_LEVEL || IsLanding) { + Status = TAKEOFF; + } else { + Status = ALTITUDE; + } + break; + + case TAKEOFF: + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + Status = ALTITUDE; + } + break; + + case ALTITUDE: + /* + ** Establish radio contact with the building this helicopter is trying + ** to land at. + */ + if (In_Radio_Contact()) { + if (!Target_Legal(NavCom)) { + Transmit_Message(RADIO_DOCKING); + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + return(1); + } + } + Status = STACK; + } else { + TechnoClass * tech = As_Techno(NavCom); + if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) { + Transmit_Message(RADIO_HELLO, tech); + Transmit_Message(RADIO_DOCKING); + Status = STACK; + } else { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (tech->What_Am_I() != RTTI_VESSEL) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#else + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); +#endif + } + } + break; + + case STACK: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_STACK)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = DOWNWIND; + } + } else { + Status = DOWNWIND; + } + break; + + case DOWNWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(200); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_DOWNWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = CROSSWIND; + } + } else { + Status = CROSSWIND; + } + break; + + case CROSSWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(140); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_CROSSWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = TRAVEL; + } + } else { + Status = TRAVEL; + } + break; + + case TRAVEL: + Transmit_Message(RADIO_DOCKING); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!In_Radio_Contact() && !Is_Target_Vessel(NavCom)) { +#else + if (!In_Radio_Contact()) { +#endif + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } else { + int distance = Process_Fly_To(true, NavCom); + + if (Class->IsFixedWing) { + + if (distance < 0x0400) { + Status = LANDING; + } + return(1); + + } else { + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Is_Target_Vessel(NavCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + break; + } +#endif + if (distance < 0x0010) { + Status = LANDING; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Is_Target_Vessel(NavCom) && As_Vessel(NavCom)->NavCom) { + Status = TRAVEL; + } +#endif + + } + break; + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + } + return(3); + } + break; + + case LANDING: + if (IsTakingOff && !Class->IsFixedWing) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// If we were trying to land on a carrier and it moved, take off again + if ( As_Vessel(NavCom) && !In_Radio_Contact()) { + Status = INITIAL; + break; + } +#endif + if (Process_Landing()) { + switch (Transmit_Message(RADIO_IM_IN)) { + case RADIO_ROGER: + Assign_Mission(MISSION_GUARD); + break; + + case RADIO_ATTACH: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Contact_With_Whom()->What_Am_I() != RTTI_VESSEL) Limbo(); +#else + Limbo(); +#endif + Contact_With_Whom()->Attach(this); + break; + + default: + Enter_Idle_Mode(); + } + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * AircraftClass::Good_LZ -- Locates a good spot to land. * + * * + * This routine is used when helicopters need a place to land, but there are no obvious * + * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly * + * building if there are no helipads anywhere. In the event that there are no friendly * + * buildings anywhere on the map, then just land right where it is flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target location where this aircraft should land. This value may * + * not be a clear cell, but the normal landing logic will resolve that problem. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::Good_LZ(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Scan through all of the buildings and try to land near + ** the helipad (if there is one) or the nearest friendly building. + */ + CELL bestcell = 0; + int bestdist = -1; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == House) { + int dist = Distance(building); + if (*building == Class->Building) { + dist /= 4; + } + if (bestdist == -1 || dist < bestdist) { + bestdist = dist; + bestcell = Coord_Cell(building->Center_Coord()); + } + } + } + + /* + ** Return with the suitable location if one was found. + */ + if (bestdist != -1) { + return(::As_Target(bestcell)); + } + + /* + ** No good location was found. Just try to land here. + */ + return(::As_Target(Coord_Cell(Coord))); +} + + +/*********************************************************************************************** + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * * + * This routine will set the speed for the aircraft. The speed is specified as a fraction * + * of full speed. * + * * + * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full * + * speed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Set_Speed(int speed) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Set_Speed(speed); + + MPHType sp = MPHType(min(Class->MaxSpeed * SpeedBias * House->AirspeedBias, MPH_LIGHT_SPEED)); + Fly_Speed(speed, sp); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * * + * This routine will determine what direction a projectile would take if it were fired * + * from the aircraft. This is the direction that the aircraft's body is facing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction of projectile fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Fire_Direction(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(SecondaryFacing.Current()); +} + + +/*********************************************************************************************** + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * * + * This is the destructor for aircraft. It will limbo the aircraft if it isn't already * + * and also removes the aircraft from any team it may be attached to. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass::~AircraftClass(void) +{ + if (GameActive && Class) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + AircraftClass::Limbo(); + Class = 0; + } + ID = -1; +} + + +/*********************************************************************************************** + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * * + * This routine will cause the aircraft to move away from its current location and then * + * enter some idle mode. Typically this is called when the aircraft is attacked while on * + * the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Scatter(COORDINATE , bool, bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter) return; + + /* + ** Fixed wing aircraft never scatter. + */ + if (Class->IsFixedWing) return; + + if (IsLanding || Height == 0) { + IsLanding = false; + IsTakingOff = true; + } + Enter_Idle_Mode(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * * + * Aircraft don't like to be in guard mode if in flight. If this situation is detected, * + * then figure out what the aircraft should be doing and go do it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This routine typically calls the normal guard logic for ground units. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 10/10/1995 JLB : Hunts for harvesters that are unescorted. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + + /* + ** If part of a team, then do nothing, since the team + ** handler will take care of giving this aircraft a + ** mission. + */ + if (Team) { + if (Target_Legal(NavCom)) { + Assign_Mission(MISSION_MOVE); + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (Class->PrimaryWeapon == NULL) { + Assign_Destination(::As_Target(Coord_Cell(Coord))); + Assign_Mission(MISSION_MOVE); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + return(1); + } + if (House->IsHuman) return(MissionControl[Mission].Normal_Delay()); + + /* + ** If the aircraft is very badly damaged, then it will search for a + ** repair bay first. + */ + if (House->Available_Money() >= 100 && Health_Ratio() <= Rule.ConditionYellow) { + if (!In_Radio_Contact() || + (Height == 0 && + (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) { + + + BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true); + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft cannot attack anything because of lack of ammo, + ** abort any normal guard logic in order to look for a helipad + ** to rearm. + */ + if (Ammo == 0 && Is_Weapon_Equipped()) { + if (!In_Radio_Contact()) { + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft already has a target, then attack it if possible. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + + /* + ** Transport helicopters don't really do anything but just sit there. + */ + if (!Is_Weapon_Equipped()) { + return(TICKS_PER_SECOND*3); + } + + /* + ** Computer controlled helicopters will defend themselves by bouncing around + ** and looking for a free helipad. + */ + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + /* + ** Perform a special check to hunt for harvesters that are outside of the protective + ** shield of their base. + */ + if (House->State != STATE_ATTACKED) { + TARGET target = House->Find_Juicy_Target(Coord); + + if (Target_Legal(target)) { + Assign_Target(target); + Assign_Mission(MISSION_ATTACK); + } + } + + return(FootClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * * + * This routine handles area guard logic for aircraft. Aircraft require special handling * + * for this mode since they are to guard area only if they are in a position to do so. * + * Otherwise they just defend themselves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard_Area(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + return(FootClass::Mission_Guard_Area()); +} + + +/*********************************************************************************************** + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * * + * This routine is used to give an audio response to an attack order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Move -- Gives audio response to move request. * + * * + * This routine is used to give an audio response to movement orders. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Select -- Gives audio response when selected. * + * * + * This routine is called when an audio response for selection is desired. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Select(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * * + * This routine is used to determine if the aircraft can fire its weapon at the target * + * specified. If it cannot, then the reason why is returned. * + * * + * INPUT: target -- The target that the aircraft might fire upon. * + * * + * which -- The weapon that will be used to fire. * + * * + * OUTPUT: Returns with the reason why it can't fire or with FIRE_OK. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + * 07/11/1996 JLB : Fixed for camera carrying aircraft. * + *=============================================================================================*/ +FireErrorType AircraftClass::Can_Fire(TARGET target, int which) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Passenger && !Is_Something_Attached()) { + return(FIRE_AMMO); + } + + bool camera = (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera); + bool fudge = (Passenger || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->IsParachuted)); + + if (fudge && !camera && !Ammo && !Passenger) { + return(FIRE_AMMO); + } + + /* + ** Passenger aircraft that wish to 'fire' actually are requesting to + ** paradrop or 'throw out' the cargo. This is always allowed if the terrain under the + ** aircraft is generally clear. + */ + if (camera || (fudge && Passenger && Is_Something_Attached())) { + if (Arm != 0) return(FIRE_REARM); + + if (Distance(target) < (camera ? 0x0380 : 0x0200) && Map.In_Radar(Coord_Cell(Center_Coord()))) { +// if (Distance(target) < (camera ? 0x0380 : 0x0280) && Map.In_Radar(Coord_Cell(Center_Coord()))) { + return(FIRE_OK); + } + return(FIRE_RANGE); + } + + FireErrorType canfire = FootClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsFixedWing) { + + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) > (fudge ? 16 : 8)) { + return(FIRE_FACING); + } + } + } + return(canfire); +} + + +/*********************************************************************************************** + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * * + * This routine handles the tricky maneuver of taking off and landing. The process of * + * landing is not entirely safe and thus the aircraft may be destroyed as a consequence. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft destroyed by this process? * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. Be sure to * + * examine the return value and if true, abort all further processing of this * + * aircraft since it is now dead. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Landing_Takeoff_AI(void) +{ + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Is_Door_Closed() && (IsLanding || IsTakingOff)) { + LayerType layer = In_Which_Layer(); + + if (IsLanding) { + Mark(MARK_UP); + if (Height) Height -= Pixel_To_Lepton(1); + if (Height <= 0) { + Height = 0; + IsLanding = false; + Set_Speed(0); + + /* + ** If the NavCom now equals the destination, then clear out the NavCom. + */ + if (Coord_Cell(Center_Coord()) == As_Cell(NavCom)) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If a fixed-wing aircraft just landed on the ground, blow him up + */ + if (Class->IsFixedWing && Mission != MISSION_ENTER) { + Strength = 1; + + int damage = Strength; + Map.Remove(this, layer); + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(true); + } + + if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) { + if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) { + Scatter(0, true); + } + } + } + Mark(MARK_DOWN); + } + if (IsTakingOff) { + Mark(MARK_UP); +// Map.Remove(this, layer); + Height += Pixel_To_Lepton(1); + if (Height >= FLIGHT_LEVEL) { + Height = FLIGHT_LEVEL; + IsTakingOff = false; + } +// Map.Submit(this, In_Which_Layer()); + Mark(MARK_DOWN); + } + + /* + ** Make adjustments for altitude by moving from one layer to another as + ** necessary. + */ + if (layer != In_Which_Layer()) { + + /* + ** When the aircraft is about to enter the ground layer, perform on last + ** check to see if it is legal to enter that location. If not, then + ** start the take off process. Let the normal logic handle this + ** change of plans. + */ + bool ok = true; + if (In_Which_Layer() == LAYER_GROUND && !IsTakingOff && !Class->IsFixedWing) { + if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) { + IsTakingOff = true; + Mark(MARK_UP); + Height += Pixel_To_Lepton(1); + Mark(MARK_DOWN); + ok = false; + } + } + + if (ok) { + + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + /* + ** When the aircraft is close to the ground, it should exist as a ground object. + ** This aspect is controlled by the Place_Down and Pick_Up functions. + */ + if (In_Which_Layer() == LAYER_GROUND) { + Assign_Destination(TARGET_NONE); // Clear the navcom. + Transmit_Message(RADIO_TETHER); + Look(); +// Map.Sight_From(Coord_Cell(Coord), 1, House, false); + } else { + Transmit_Message(RADIO_UNTETHER); + + /* + ** If the navigation computer is not attached to the object this + ** aircraft is in radio contact with, then assume that radio + ** contact is now superfluous. Break radio contact. + */ + if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) { + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * * + * Certain aircraft will be eliminated when they leave the edge of the world presumably * + * after completing their mission. An exception is for aircraft that have been newly * + * created as reinforcements and have not yet completed their mission. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft deleted by this routine? * + * * + * WARNINGS: Be sure to call this routine only once per aircraft per game logic loop. If * + * the return value is true, then abort any further processing of this aircraft * + * since it has been eliminated. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Edge_Of_World_AI(void) +{ + if (!Map.In_Radar(Coord_Cell(Coord))) { + if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) { + + /* + ** Check to see if there are any civilians aboard. If so, then flag the house + ** that the civilian evacuation trigger event has been fulfilled. + */ + while (Is_Something_Attached()) { + FootClass * obj = Detach_Object(); + + /* + ** Flag the owning house that civ evacuation has occurred. + */ + if (_Counts_As_Civ_Evac(obj)) { + obj->House->IsCivEvacuated = true; + } + + if (obj->Team.Is_Valid()) obj->Team->IsLeaveMap = true; + +#ifdef OLD + /* + ** Transport planes that leave can only be because they carry purchased + ** equipment and must be have their cost refunded. + */ + if (*this == AIRCRAFT_CARGO) { + House->Refund_Money(obj->Class_Of().Cost_Of()); + } +#endif + delete obj; + } + if (Team.Is_Valid()) { + Team->IsLeaveMap = true; + } + Stun(); + delete this; + return(true); + } + } else { + IsLocked = true; + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * * + * This routine manages the aircraft movement across the map. If any movement occurred, the * + * aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Movement_AI(void) +{ + /* + ** If for some strange reason, there is a valid NavCom, but this aircraft is not + ** in a movement order, then give it a movement order. + */ + if (Target_Legal(NavCom) && Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + + if (Speed != 0) { + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_UP); + Physics(Coord, PrimaryFacing); + Mark(MARK_DOWN); + } else { + Mark(MARK_CHANGE_REDRAW); + if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { + Mark(MARK_CHANGE_REDRAW); + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * * + * This will process the aircraft visible body and flight model rotation operations. If * + * any rotation occurred, the aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Rotation_AI(void) +{ + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } + if (Class->IsFixedWing) { + SecondaryFacing = PrimaryFacing; + } + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * * + * This is a seldom used function since its only purpose is to be called when an aircraft * + * lands on the ground. * + * * + * INPUT: why -- Why was this per cell process function called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/15/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Per_Cell_Process(PCPType why) +{ + BStart(BENCH_PCP); + FootClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * AircraftClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. Aircraft have their own version of this routine because a fixed-wing plane * + * trying to land will behave poorly if given a new destinatio while it's landing. * * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Assign_Destination(TARGET dest) +{ + assert(IsActive); + if (dest == NavCom) return; + + if (Target_Legal(dest) && Class->IsFixedWing && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { +// if (Target_Legal(dest) /*&& Class->IsFixedWing*/ && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { + +// if (Class->IsFixedWing || As_Cell(dest) != Coord_Cell(Center_Coord())) { + Process_Take_Off(); + Status = 0; +// } + } + FootClass::Assign_Destination(dest); +} + + +/*********************************************************************************************** + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * * + * This examines the aircraft to determine what display layer it should be located * + * in. Fixed wing aircraft must always be in the top layer if they are flying even though * + * they may be low to the ground. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this aircraft resides in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +LayerType AircraftClass::In_Which_Layer(void) const +{ + if (Class->IsFixedWing && Height > 0) { + return(LAYER_TOP); + } + return(FootClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * * + * Aircraft perform a look operation according to their sight range. If the aircraft is * + * on the ground, then it will look a distance of one cell regardless of what its * + * specified sight range is. * + * * + * INPUT: incremental -- Is this an incremental look? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/23/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + if (Height == 0) { + sight_range = 1; + } + + if (sight_range) { + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + } +} diff --git a/CODE/AIRCRAFT.H b/CODE/AIRCRAFT.H new file mode 100644 index 0000000..46d3e88 --- /dev/null +++ b/CODE/AIRCRAFT.H @@ -0,0 +1,248 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/AIRCRAFT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AIRCRAFT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AIRCRAFT_H +#define AIRCRAFT_H + +#include "radio.h" +#include "fly.h" +#include "target.h" + + +/* +** This aircraft class is used for all flying sentient objects. This includes fixed wing +** aircraft as well as helicopters. It excludes bullets even though some bullets might +** be considered to be "flying" in a loose interpretatin of the word. +*/ +class AircraftClass : public FootClass, public FlyClass +{ + public: + /* + ** This is a pointer to the class control structure for the aircraft. + */ + CCPtr Class; + + //----------------------------------------------------------------------------- + static void * operator new(size_t); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *); + operator AircraftType(void) const {return Class->Type;}; + AircraftClass(AircraftType classid, HousesType house); + AircraftClass(NoInitClass const & x) : FootClass(x), FlyClass(x), Class(x), SecondaryFacing(x), SightTimer(x) {}; + virtual ~AircraftClass(void); + + static void Init(void); + + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Hunt(void); + virtual int Mission_Retreat(void); + virtual int Mission_Move(void); + virtual int Mission_Enter(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + + virtual void Assign_Destination(TARGET target); + /* + ** State machine support routines. + */ + bool Process_Take_Off(void); + bool Process_Landing(void); + int Process_Fly_To(bool slowdown, TARGET dest); + + /* + ** Query functions. + */ + virtual LayerType In_Which_Layer(void) const; + virtual DirType Turret_Facing(void) const {return(SecondaryFacing.Current());} + int Shape_Number(void) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual int Pip_Count(void) const; + TARGET Good_Fire_Location(TARGET target) const; + bool Cell_Seems_Ok(CELL cell, bool landing=false) const; + DirType Pose_Dir(void) const; + TARGET Good_LZ(void) const; + virtual DirType Fire_Direction(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + /* + ** Landing zone support functionality. + */ + virtual void Per_Cell_Process(PCPType why); + bool Is_LZ_Clear(TARGET target) const; + TARGET New_LZ(TARGET oldlz) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Look(bool incremental=false); + void Draw_Rotors(int x, int y, WindowNumberType window) const; + virtual int Exit_Object(TechnoClass *); + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Set_Speed(int speed); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which); + + /* + ** AI. + */ + bool Landing_Takeoff_AI(void); + bool Edge_Of_World_AI(void); + void Movement_AI(void); + void Rotation_AI(void); + int Paradrop_Cargo(void); + virtual void AI(void); + virtual void Enter_Idle_Mode(bool initial = false); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static char * INI_Name(void) {return "AIRCRAFT";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + public: + + /* + ** This is the facing used for the body of the aircraft. Typically, this is the same + ** as the PrimaryFacing, but in the case of helicopters, it can be different. + */ + FacingClass SecondaryFacing; + + /* + ** If this is a passenger carrying aircraft then this flag will be set. This is + ** necessary because once the passengers are unloaded, the fact that it was a + ** passenger carrier must still be known. + */ + bool Passenger; + + private: + + /* + ** Aircraft can be in either state of landing, taking off, or in steady altitude. + ** These flags are used to control transition between flying and landing. It is + ** necessary to handle the transition in this manner so that it occurs smoothly + ** during the graphic processing section. + */ + unsigned IsLanding:1; + unsigned IsTakingOff:1; + + /* + ** It is very common for aircraft to be homing in on a target. When this flag is + ** true, the aircraft will constantly adjust its facing toward the TarCom. When the + ** target is very close (one cell away or less), then this flag is automatically cleared. + ** This is because the homing algorithm is designed to get the aircraft to the destination + ** but no more. Checking when this flag is cleared is a way of flagging transition into + ** a new mode. Example: Transport helicopters go into a hovering into correct position + ** mode when the target is reached. + */ + unsigned IsHoming:1; + + /* + ** Helicopters that are about to land must hover into a position exactly above the landing + ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over + ** the TarCom. The facing of the aircraft is not altered by this movement. The affect + ** like the helicopter is hovering and shifting sideways to position over the landing + ** zone. When the position is over the landing zone, then this flag is set to false. + */ + unsigned IsHovering:1; + + /* + ** This is the jitter tracker to be used when the aircraft is a helicopter and + ** is flying. It is most noticeable when the helicopter is hovering. + */ + unsigned char Jitter; + + private: + + /* + ** This timer controls when the aircraft will reveal the terrain around itself. + ** When this timer expires and this aircraft has a sight range, then the + ** look around process will occur. + */ + CDTimerClass SightTimer; + + /* + ** Most attack aircraft can make several attack runs. This value contains the + ** number of attack runs the aircraft has left. When this value reaches + ** zero then the aircraft is technically out of ammo. + */ + char AttacksRemaining; +}; + +#endif diff --git a/CODE/ALLOC.CPP b/CODE/ALLOC.CPP new file mode 100644 index 0000000..665bebe --- /dev/null +++ b/CODE/ALLOC.CPP @@ -0,0 +1,590 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 8*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + +// void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + diff --git a/CODE/ANIM.CPP b/CODE/ANIM.CPP new file mode 100644 index 0000000..fba36b1 --- /dev/null +++ b/CODE/ANIM.CPP @@ -0,0 +1,1108 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ANIM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#define VIC 1 + +/*********************************************************************************************** + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * * + * This routine will convert the supplied ASCII name into the animation type that it * + * represents. * + * * + * INPUT: name -- Pointer to the ASCII name to convert. * + * * + * OUTPUT: Returns with the animation type that matches the name specified. If no match could * + * be found, then ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Anim_From_Name(char const * name) +{ + #ifdef VIC + if (name == NULL) return(ANIM_NONE); + + for (AnimType anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) { + if (stricmp(AnimTypeClass::As_Reference(anim).IniName, name) == 0) { + return(anim); + } + } +#endif + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj != NULL) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = * Anims.Ptr(index); + + if (As_Object(anim.xObject) == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L)); + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/02/1996 JLB : Coordinate based on visual center of object. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(Coord, As_Object(xObject)->Target_Coord())); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Delay) return(false); + if (Map[Center_Coord()].IsVisible) { + IsToDisplay = true; + } +#endif + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +void AnimClass::Draw_It(int x, int y, WindowNumberType window) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (!IsInvisible) { + BStart(BENCH_ANIMS); + + IsTheaterShape = Class->IsTheater; + + void const * shapefile = Get_Image_Data(); + if (shapefile != NULL) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable; + if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable; + if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + if (transtable != NULL) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape. + */ + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, remap, transtable); + } + IsTheaterShape = false; + BEnd(BENCH_ANIMS); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +// ObjectClass::Mark(mark); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + if (IsToDelete) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + if (Class->Type == ANIM_ATOM_BLAST) { + return(OverlapAtom); + } + +#ifdef PARTIAL +IsTheaterShape = Class->IsTheater; + if (Class->Get_Image_Data() != NULL) { + int shapenum = Class->Start + Fetch_Stage(); + int count = Get_Build_Frame_Count(Class->Get_Image_Data()); + shapenum = min(shapenum, count-1); + + if (Class->DimensionData == NULL) { + Class->DimensionData = new Rect [count]; + } + if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); +IsTheaterShape = false; + return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum])); + } + } +IsTheaterShape = false; +#endif +#endif + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(bool) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + static short _simple[] = {REFRESH_EOL}; + +#endif + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + Anims.Free_All(); +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void * AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) : + ObjectClass(RTTI_ANIM, Anims.ID(this)), + Class(AnimTypes.Ptr((int)animnum)), + xObject(TARGET_NONE), + OwnerHouse(HOUSE_NONE), + Loops(1), + IsToDelete(false), + IsBrandNew(true), + IsInvisible(false), + Delay(timedelay), + Accum(0) +{ +#ifdef VIC + if (Class->Stages == -1) { +IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); +IsTheaterShape = false; + + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + if (Class->IsGroundLayer) { + Height = FLIGHT_LEVEL; + } + + AnimClass::Unlimbo(coord); + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false); + } + + Loops = (unsigned char)(max(loop, 1) * Class->Loops); + Loops = (unsigned char)max(Loops, 1); + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Target_Legal(xObject) && As_Object(xObject) != NULL) { + ObjectClass * to = As_Object(xObject); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + for (int index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index == Anims.Count()) { + to->Fire_Out(); + to->Mark(MARK_OVERLAP_UP); + to->IsAnimAttached = false; + to->Mark(MARK_OVERLAP_DOWN); + } + Coord = Coord_Add(to->Center_Coord(), Coord); + xObject = TARGET_NONE; + } + + Limbo(); + } + + xObject = TARGET_NONE; + Class = 0; + ID = -1; + +#endif +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ +#ifdef PARTIAL + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } +#else + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +#endif + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + delete this; + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (xObject != TARGET_NONE && Class->Damage > 0) { + Accum += Class->Damage; + + if (Accum >= 1) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = Accum; + Accum -= damage; + if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + delete this; + return; + } + } + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existence while in plain sight. + */ + if (Class->Biggest && Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { + + Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->Stages == -1) { +IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); +IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + + IsToDelete = false; + Loops = Class->Loops; + Accum = 0; + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + Start(); + } else { + delete this; + } + } + } + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (obj == NULL) return; + assert(obj->IsActive); + + obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + obj->Mark(MARK_OVERLAP_DOWN); + Map.Remove(this, In_Which_Layer()); + xObject = obj->As_Target(); + Map.Submit(this, In_Which_Layer()); + Coord = Coord_Sub(Coord, obj->Target_Coord()); +#endif +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { + return(LAYER_SURFACE); + } + + if (Target_Legal(xObject) || Class->IsGroundLayer) { + return(LAYER_GROUND); + } +#endif + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsequent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/17/1995 JLB : Ion camera added. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + Do_Atom_Damage(OwnerHouse, cell); + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. Usually, these side effects are in the form of other + ** animations. + */ + switch (Class->Type) { + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2)); + } + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2)); + if (newanim != NULL && xObject != TARGET_NONE) { + newanim->Attach_To(As_Object(xObject)); + } + break; + + default: + break; + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject == target && all) { + Map.Remove(this, In_Which_Layer()); + xObject = TARGET_NONE; + IsToDelete = true; + Mark(MARK_UP); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * * + * This routine will apply damage around the ground-zero cell specified. * + * * + * INPUT: ownerhouse -- The owner of this atom bomb. * + * * + * cell -- The ground zero location to apply the atom bomb damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell) +{ +#ifdef VIC + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (ownerhouse != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (building == NULL) building = (BuildingClass *)backup; + } + + int radius; + int rawdamage; + if (Session.Type == GAME_NORMAL) { + radius = 4; + rawdamage = Rule.AtomDamage; + WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back); + } else { + radius = 3; + rawdamage = Rule.AtomDamage/5; + } + + Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE); + Shake_The_Screen(3); + if (Session.Type == GAME_NORMAL) { + GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); + } +#endif +} + + diff --git a/CODE/ANIM.CPP.BAK b/CODE/ANIM.CPP.BAK new file mode 100644 index 0000000..6ef5060 --- /dev/null +++ b/CODE/ANIM.CPP.BAK @@ -0,0 +1,1062 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\anim.cpv 4.78 03 Oct 1996 09:20:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * * + * This routine will convert the supplied ASCII name into the animation type that it * + * represents. * + * * + * INPUT: name -- Pointer to the ASCII name to convert. * + * * + * OUTPUT: Returns with the animation type that matches the name specified. If no match could * + * be found, then ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Anim_From_Name(char const * name) +{ + #ifdef 0 + if (name == NULL) return(ANIM_NONE); + + for (AnimType anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) { + if (stricmp(AnimTypeClass::As_Reference(anim).IniName, name) == 0) { + return(anim); + } + } +#endif + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj != NULL) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = * Anims.Ptr(index); + + if (As_Object(anim.xObject) == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L)); + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/02/1996 JLB : Coordinate based on visual center of object. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(Coord, As_Object(xObject)->Target_Coord())); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Delay) return(false); + if (Map[Center_Coord()].IsVisible) { + IsToDisplay = true; + } +#endif + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +void AnimClass::Draw_It(int x, int y, WindowNumberType window) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (!IsInvisible) { + BStart(BENCH_ANIMS); + + void const * shapefile = Get_Image_Data(); + if (shapefile != NULL) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable; + if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable; + if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + if (transtable != NULL) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape. + */ + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, remap, transtable); + } + BEnd(BENCH_ANIMS); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +// ObjectClass::Mark(mark); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + if (IsToDelete) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + if (Class->Type == ANIM_ATOM_BLAST) { + return(OverlapAtom); + } + +#ifdef PARTIAL + if (Class->Get_Image_Data() != NULL) { + int shapenum = Class->Start + Fetch_Stage(); + int count = Get_Build_Frame_Count(Class->Get_Image_Data()); + shapenum = min(shapenum, count-1); + + if (Class->DimensionData == NULL) { + Class->DimensionData = new Rect [count]; + } + if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); + return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum])); + } + } +#endif +#endif + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(bool) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + static short _simple[] = {REFRESH_EOL}; + +#endif + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + Anims.Free_All(); +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void * AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) : + ObjectClass(RTTI_ANIM, Anims.ID(this)), + Class(AnimTypes.Ptr((int)animnum)), + xObject(TARGET_NONE), + OwnerHouse(HOUSE_NONE), + Loops(1), + IsToDelete(false), + IsBrandNew(true), + IsInvisible(false), + Delay(timedelay), + Accum(0) +{ +#ifdef 0 + if (Class->Stages == -1) { + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + if (Class->IsGroundLayer) { + Height = FLIGHT_LEVEL; + } + + AnimClass::Unlimbo(coord); + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false); + } + + Loops = (unsigned char)(max(loop, 1) * Class->Loops); + Loops = (unsigned char)max(Loops, 1); + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Target_Legal(xObject) && As_Object(xObject) != NULL) { + ObjectClass * to = As_Object(xObject); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + for (int index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index == Anims.Count()) { + to->Fire_Out(); + to->Mark(MARK_OVERLAP_UP); + to->IsAnimAttached = false; + to->Mark(MARK_OVERLAP_DOWN); + } + Coord = Coord_Add(to->Center_Coord(), Coord); + xObject = TARGET_NONE; + } + + Limbo(); + } + + xObject = TARGET_NONE; + Class = 0; + ID = -1; +#endif +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ +#ifdef PARTIAL + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } +#else + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +#endif + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + delete this; + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (xObject != TARGET_NONE && Class->Damage > 0) { + Accum += Class->Damage; + + if (Accum >= 1) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = Accum; + Accum -= damage; + if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + delete this; + return; + } + } + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existence while in plain sight. + */ + if (Class->Biggest && Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { + + Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->Stages == -1) { + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + + IsToDelete = false; + Loops = Class->Loops; + Accum = 0; + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + Start(); + } else { + delete this; + } + } + } + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (obj == NULL) return; + assert(obj->IsActive); + + obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + obj->Mark(MARK_OVERLAP_DOWN); + Map.Remove(this, In_Which_Layer()); + xObject = obj->As_Target(); + Map.Submit(this, In_Which_Layer()); + Coord = Coord_Sub(Coord, obj->Target_Coord()); +#endif +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ +#ifdef 0 + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(xObject) || Class->IsGroundLayer) { + return(LAYER_GROUND); + } + + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ + assert(Anims.ID(this) == ID); + assert(IsActive); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsequent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/17/1995 JLB : Ion camera added. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ + assert(Anims.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + Do_Atom_Damage(OwnerHouse, cell); + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. Usually, these side effects are in the form of other + ** animations. + */ + switch (Class->Type) { + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2)); + } + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2)); + if (newanim != NULL && xObject != TARGET_NONE) { + newanim->Attach_To(As_Object(xObject)); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject == target && all) { + Map.Remove(this, In_Which_Layer()); + xObject = TARGET_NONE; + IsToDelete = true; + Mark(MARK_UP); + } +} + + +/*********************************************************************************************** + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * * + * This routine will apply damage around the ground-zero cell specified. * + * * + * INPUT: ownerhouse -- The owner of this atom bomb. * + * * + * cell -- The ground zero location to apply the atom bomb damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell) +{ + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (ownerhouse != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (building == NULL) building = (BuildingClass *)backup; + } + + int radius; + int rawdamage; + if (Session.Type == GAME_NORMAL) { + radius = 4; + rawdamage = Rule.AtomDamage; + WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back); + } else { + radius = 3; + rawdamage = Rule.AtomDamage/5; + } + + Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE); + Shake_The_Screen(3); + if (Session.Type == GAME_NORMAL) { + GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); + } +} + + diff --git a/CODE/ANIM.H b/CODE/ANIM.H new file mode 100644 index 0000000..a66446f --- /dev/null +++ b/CODE/ANIM.H @@ -0,0 +1,156 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ANIM.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ANIM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : May 30, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ANIM_H +#define ANIM_H + +#include "type.h" + + +/********************************************************************************************** +** This is the class that controls the shape animation objects. Shape animation objects are +** displayed over the top of the game map. Typically, they are used for explosion and fire +** effects. +*/ +class AnimClass : public ObjectClass, public StageClass { + /* + ** This points to the type of animation object this is. + */ + CCPtr Class; + + public: + + AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1); + AnimClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~AnimClass(void); + + operator AnimType(void) const {return Class->Type;}; + + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + void Attach_To(ObjectClass *obj); + void Make_Invisible(void) {IsInvisible = true;}; + static void Do_Atom_Damage(HousesType ownerhouse, CELL cell); + + virtual bool Can_Place_Here(COORDINATE ) const {return true;} + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual bool Render(bool forced) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void AI(void); + virtual void Detach(TARGET target, bool all); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(FileClass & file); + + /* + ** If this animation is attached to an object, then this points to that object. An + ** animation that is attached will follow that object as it moves. This is important + ** for animations such as flames and smoke. + */ + TARGET xObject; + + /* + ** If this animation has an owner, then it will be recorded here. An owner + ** is used when damage is caused by this animation during the middle of its + ** animation. + */ + HousesType OwnerHouse; + + /* + ** This counter tells how many more times the animation should loop before it + ** terminates. + */ + unsigned char Loops; + + protected: + void Middle(void); + void Start(void); + + private: + /* + ** Delete this animation at the next opportunity. This is flagged when the + ** animation is to be prematurely ended as a result of some outside event. + */ + unsigned IsToDelete:1; + + /* + ** If the animation has just been created, then don't do any animation + ** processing until it has been through the render loop at least once. + */ + unsigned IsBrandNew:1; + + /* + ** If this animation is invisible, then this flag will be true. An invisible + ** animation is one that is created for the sole purpose of keeping all + ** machines synchronized. It will not be displayed. + */ + unsigned IsInvisible:1; + + /* + ** Is this animation in a temporary suspended state? If so, then it won't + ** be rendered until this value is zero. The flag will be set to false + ** after the first countdown timer reaches 0. + */ + int Delay; + + /* + ** If this is an animation that damages whatever it is attached to, then this + ** value holds the accumulation of fractional damage points. When the accumulated + ** fractions reach 256, then one damage point is applied to the attached object. + */ + fixed Accum; +}; + + + +#endif diff --git a/CODE/AUDIO.CPP b/CODE/AUDIO.CPP new file mode 100644 index 0000000..9244e09 --- /dev/null +++ b/CODE/AUDIO.CPP @@ -0,0 +1,763 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/AUDIO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Speech_Name -- Fetches the name for the voice specified. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * Voc_Name -- Fetches the name for the sound effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +} ContextType; + +static struct { + char const * Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1 + + /* + ** Infantry and vehicle responses. + */ + {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders" + {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative" + {"EENGIN1", 20, IN_NOVAR}, // VOC_ENG_ENG Engineer: "engineering" + {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting" + {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger" + {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it" + {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir" + + {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound + {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium). + {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short). + {"IRONCUR9", 10, IN_NOVAR}, // VOC_IRON1 + {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out" + {"SONPULSE", 10, IN_NOVAR}, // VOC_SONAR + {"SANDBAG2", 5, IN_NOVAR}, // VOC_SANDBAG sand bag crunch + {"MINEBLO1", 5, IN_NOVAR}, // VOC_MINEBLOW weird mine explosion + {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound. + {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark. + {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine. + {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl. + {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound. + {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound. + {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss. + {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow). + {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast). + {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir" + {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot. + {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect. + {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door. + {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect. + {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled). + {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled). + {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled). + {"SPLASH9", 5, IN_NOVAR}, // VOC_SPLASH water splash + {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp). + {"AACANON3", 1, IN_NOVAR}, + {"TANDETH1", 10, IN_NOVAR}, + {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast). + {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect. + {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch. + {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch. + {"x", 1, IN_NOVAR}, + {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles). + {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound. + {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound. + {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer. + {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot. + {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire. + {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch. + {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire. + {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up. + {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect. + {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect. + {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep. + {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics. + {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect. + {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound. + {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE). + {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion. + {"x", 10, IN_NOVAR}, + {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud). + {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'. + {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble. + {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick. + {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick. + {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound. + {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep. + {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble. + {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble. + {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble. + {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble. + {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence. + {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence. + {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound. + {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake. + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock" + {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha" + {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby" + {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching" + {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got" + {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye" + {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there" + {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me" + {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?" + {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?" + {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up." + {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound. + {"x", 10, IN_NOVAR}, + {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession. + {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound. + {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah" + {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok" + {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes" + {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound + + {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?" + {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir" + {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed" + {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way" + {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country" + {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting" + {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir" + {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative" + {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out" + {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep + + {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?" + {"ANTDIE", 20, IN_NOVAR}, // VOC_ANTDIE + {"ANTBITE", 20, IN_NOVAR}, // VOC_ANTBITE + {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out" + {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok" + {"x", 20, IN_NOVAR}, + {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what" + {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative" +//ADDED VG 2/24/97 + {"STAVCMDR", 20, IN_NOVAR}, + {"STAVCRSE", 20, IN_NOVAR}, + {"STAVYES", 20, IN_NOVAR}, + {"STAVMOV", 20, IN_NOVAR}, + {"BUZZY1", 20, IN_NOVAR}, + {"RAMBO1", 20, IN_NOVAR}, + {"RAMBO2", 20, IN_NOVAR}, + {"RAMBO3", 20, IN_NOVAR}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"MYES1", 20, IN_NOVAR}, // VOC_MECHYES1 Mechanic: "Yes sir!" + {"MHOWDY1", 20, IN_NOVAR}, // VOC_MECHHOWDY1 Mechanic: "Howdy!" + {"MRISE1", 20, IN_NOVAR}, // VOC_MECHRISE1 Mechanic: "Rise 'n shine!" + {"MHUH1", 20, IN_NOVAR}, // VOC_MECHHUH1 Mechanic: "Huh?" + {"MHEAR1", 20, IN_NOVAR}, // VOC_MECHHEAR1 Mechanic: "I Hear Ya!" + {"MLAFF1", 20, IN_NOVAR}, // VOC_MECHLAFF1 Mechanic: guffaw + {"MBOSS1", 20, IN_NOVAR}, // VOC_MECHBOSS1 Mechanic: "Sure Thing, Boss!" + {"MYEEHAW1", 20, IN_NOVAR}, // VOC_MECHYEEHAW1 Mechanic: "Yee Haw!" + {"MHOTDIG1", 20, IN_NOVAR}, // VOC_MECHHOTDIG1 Mechanic: "Hot Diggity Dog!" + {"MWRENCH1", 20, IN_NOVAR}, // VOC_MECHWRENCH1 Mechanic: "I'll get my wrench." + + {"JBURN1", 20, IN_NOVAR}, // VOC_STBURN1 Shock Trooper: "Burn baby burn!" + {"JCHRGE1", 20, IN_NOVAR}, // VOC_STCHRGE1 Shock Trooper: "Fully charged!" + {"JCRISP1", 20, IN_NOVAR}, // VOC_STCRISP1 Shock Trooper: "Extra Crispy!" + {"JDANCE1", 20, IN_NOVAR}, // VOC_STDANCE1 Shock Trooper: "Let's Dance!" + {"JJUICE1", 20, IN_NOVAR}, // VOC_STJUICE1 Shock Trooper: "Got juice?" + {"JJUMP1", 20, IN_NOVAR}, // VOC_STJUMP1 Shock Trooper: "Need a jump?" + {"JLIGHT1", 20, IN_NOVAR}, // VOC_STLIGHT1 Shock Trooper: "Lights out!" + {"JPOWER1", 20, IN_NOVAR}, // VOC_STPOWER1 Shock Trooper: "Power on!" + {"JSHOCK1", 20, IN_NOVAR}, // VOC_STSHOCK1 Shock Trooper: "Shocking!" + {"JYES1", 20, IN_NOVAR}, // VOC_STYES1 Shock Trooper: "Yesssss!" + + {"CHROTNK1", 20, IN_NOVAR}, // VOC_CHRONOTANK1 Chrono tank teleport + {"FIXIT1", 20, IN_NOVAR}, // VOC_MECH_FIXIT1 Mechanic fixes something + {"MADCHRG2", 20, IN_NOVAR}, // VOC_MAD_CHARGE MAD tank charges up + {"MADEXPLO", 20, IN_NOVAR}, // VOC_MAD_EXPLODE MAD tank explodes + {"SHKTROP1", 20, IN_NOVAR}, // VOC_SHOCK_TROOP1 Shock Trooper fires + +#endif +}; + + +/*********************************************************************************************** + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * * + * This will find the corresponding VocType from the ASCII string specified. It does this * + * by finding a root filename that matches the string. * + * * + * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. * + * * + * OUTPUT: Returns with the VocType that matches the string specified. If no match could be * + * found, then VOC_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +VocType Voc_From_Name(char const * name) +{ + if (name == NULL) return(VOC_NONE); + + for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) { + if (stricmp(name, SoundEffectName[voc].Name) == 0) { + return(voc); + } + } + + return(VOC_NONE); +} + + +/*********************************************************************************************** + * Voc_Name -- Fetches the name for the sound effect. * + * * + * This routine returns the descriptive name of the sound effect. Currently, this is just * + * the root of the file name. * + * * + * INPUT: voc -- The VocType that the corresponding name is requested. * + * * + * OUTPUT: Returns with a pointer to the text string the represents the sound effect. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Voc_Name(VocType voc) +{ + if (voc == VOC_NONE) return("none"); + return(SoundEffectName[voc].Name); +} + + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + * 09/15/1996 JLB : Revamped volume logic. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation, HousesType house) +{ + CELL cell_pos = 0; + int pan_value; + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + fixed volume = 1; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W; + fixed dfixed = fixed(distance, 128+64); + dfixed.Sub_Saturate(1); + volume = fixed(1) - dfixed; + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, volume, variation, pan_value, house); +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value, HousesType house) +{ + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Alter the volume according to the game volume setting. + */ + volume = volume * Options.Volume; + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** If there is no forced house, then use the current player + ** act like house. + */ + if (house == HOUSE_NONE) { + house = PlayerPtr->ActLike; + } + + /* + ** Change the extension based on the variation and house accent requested. + */ + if (((1 << house) & HOUSEF_ALLIES) != 0) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } else { + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".R00"; + } else { + ext = ".R02"; + } + } else { + if (variation % 2) { + ext = ".R01"; + } else { + ext = ".R03"; + } + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MFCD::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr != NULL) { + volume.Sub_Saturate(1); + return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value)); + } + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +static char const * Speech[VOX_COUNT] = { + "MISNWON1", // VOX_ACCOMPLISHED mission accomplished + "MISNLST1", // VOX_FAIL your mission has failed + "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress + "CONSCMP1", // VOX_CONSTRUCTION construction complete + "UNITRDY1", // VOX_UNIT_READY unit ready + "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options + "NODEPLY1", // VOX_DEPLOY cannot deploy here + "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed + "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power + "NOFUNDS1", // VOX_NO_CASH insufficient funds + "BCT1", // VOX_CONTROL_EXIT battle control terminated + "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived + "CANCLD1", // VOX_CANCELED canceled + "ABLDGIN1", // VOX_BUILDING building + "LOPOWER1", // VOX_LOW_POWER low power + "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds + "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack + "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more + "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef ENGLISH + "TANK01", // VOX_MADTANK_DEPLOYED M.A.D. Tank Deployed +#else + "none", +#endif +#else + "none", +#endif + "none", // VOX_SOVIET_CAPTURED Allied building captured + "UNITLST1", // VOX_UNIT_LOST unit lost + "SLCTTGT1", // VOX_SELECT_TARGET select target + "ENMYAPP1", // VOX_PREPARE enemy approaching + "SILOND1", // VOX_NEED_MO_CAPACITY silos needed + "ONHOLD1", // VOX_SUSPENDED on hold + "REPAIR1", // VOX_REPAIRING repairing + "none", + "none", + "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost + "none", + "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching + "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived + "none", + "none", + "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated + "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging + "CHRORDY1", // VOX_CHRONO_READY chronosphere ready + "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful + "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack + "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated + "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching + "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost + "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed + "CREDIT1", // VOX_MONEY_STOLEN credits stolen + "NAVYLST1", // VOX_SHIP_LOST naval unit lost + "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched + "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available + "none", + "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching + "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived + "TRAIN1", // VOX_TRAINING training + "AREADY1", // VOX_ABOMB_READY + "ALAUNCH1", // VOX_ABOMB_LAUNCH + "AARRIVN1", // VOX_ALLIES_N + "AARRIVS1", // VOX_ALLIES_S + "AARIVE1", // VOX_ALLIES_E + "AARRIVW1", // VOX_ALLIES_W + "1OBJMET1", // VOX_OBJECTIVE1 + "2OBJMET1", // VOX_OBJECTIVE2 + "3OBJMET1", // VOX_OBJECTIVE3 + "IRONCHG1", // VOX_IRON_CHARGING + "IRONRDY1", // VOX_IRON_READY + "KOSYRES1", // VOX_RESCUED + "OBJNMET1", // VOX_OBJECTIVE_NOT + "FLAREN1", // VOX_SIGNAL_N + "FLARES1", // VOX_SIGNAL_S + "FLAREE1", // VOX_SIGNAL_E + "FLAREW1", // VOX_SIGNAL_W + "SPYPLN1", // VOX_SPY_PLANE + "TANYAF1", // VOX_FREED + "ARMORUP1", // VOX_UPGRADE_ARMOR + "FIREPO1", // VOX_UPGRADE_FIREPOWER + "UNITSPD1", // VOX_UPGRADE_SPEED + "MTIMEIN1", // VOX_MISSION_TIMER + "UNITFUL1", // VOX_UNIT_FULL + "UNITREP1", // VOX_UNIT_REPAIRED + "40MINR", // VOX_TIME_40 + "30MINR", // VOX_TIME_30 + "20MINR", // VOX_TIME_20 + "10MINR", // VOX_TIME_10 + "5MINR", // VOX_TIME_5 + "4MINR", // VOX_TIME_4 + "3MINR", // VOX_TIME_3 + "2MINR", // VOX_TIME_2 + "1MINR", // VOX_TIME_1 + "TIMERNO1", // VOX_TIME_STOP + "UNITSLD1", // VOX_UNIT_SOLD + "TIMERGO1", // VOX_TIMER_STARTED + "TARGRES1", // VOX_TARGET_RESCUED + "TARGFRE1", // VOX_TARGET_FREED + "TANYAR1", // VOX_TANYA_RESCUED + "STRUSLD1", // VOX_STRUCTURE_SOLD + "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN + "SOVEMP1", // VOX_SOVIET_SELECTED + "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN + "OPTERM1", // VOX_OPERATION_TERMINATED + "OBJRCH1", // VOX_OBJECTIVE_REACHED + "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED + "OBJMET1", // VOX_OBJECTIVE_MET + "MERCR1", // VOX_MERCENARY_RESCUED + "MERCF1", // VOX_MERCENARY_FREED + "KOSYFRE1", // VOX_KOSOYGEN_FREED + "FLARE1", // VOX_FLARE_DETECTED + "COMNDOR1", // VOX_COMMANDO_RESCUED + "COMNDOF1", // VOX_COMMANDO_FREED + "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS + "ATPREP1", // VOX_ATOM_PREPPING + "ASELECT1", // VOX_ALLIED_SELECTED + "APREP1", // VOX_ABOMB_PREPPING + "ATLNCH1", // VOX_ATOM_LAUNCHED + "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN + "AAVAIL1", // VOX_ABOMB_AVAILABLE + "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS + "SAVE1", // VOX_MISSION_SAVED + "LOAD1" // VOX_MISSION_LOADED +}; + + +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speech_Name -- Fetches the name for the voice specified. * + * * + * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this * + * would be to build a displayable list of the speech types. The trigger system uses this * + * so that a speech type can be selected. * + * * + * INPUT: speech -- The speech type id to convert to ASCII string. * + * * + * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 JLB : Created. * + *=============================================================================================*/ +char const * Speech_Name(VoxType speech) +{ + if (speech == VOX_NONE) return("none"); + return(Speech[speech]); +} + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +void Speak(VoxType voice) +{ + if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + Speak_AI(); + } +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 10/11/1996 JLB : Handles multiple speech buffers. * + *=============================================================================================*/ +void Speak_AI(void) +{ + static int _index = 0; + if (Debug_Quiet || SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer[_index])) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + + /* + ** Try to find a previously loaded copy of the EVA speech in one of the + ** speech buffers. + */ + void const * speech = NULL; + for (int index = 0; index < ARRAY_SIZE(SpeechRecord); index++) { + if (SpeechRecord[index] == SpeakQueue) break; + } + + /* + ** If a previous copy could not be located, then load the requested + ** voice into the oldest buffer available. + */ + if (speech == NULL) { + _index = (_index + 1) % ARRAY_SIZE(SpeechRecord); + + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + CCFileClass file(name); + if (file.Is_Available() && file.Read(SpeechBuffer[_index], SPEECH_BUFFER_SIZE)) { + speech = SpeechBuffer[_index]; + SpeechRecord[_index] = SpeakQueue; + } + } + + /* + ** Since the speech file was loaded, play it. + */ + if (speech != NULL) { + Play_Sample(speech, 254, Options.Volume * 256); + CurrentVoice = SpeakQueue; + } + + SpeakQueue = VOX_NONE; + } + } +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + Stop_Sample_Playing(SpeechBuffer); +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + if (!Debug_Quiet && SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) { + return(true); + } + return(false); +} diff --git a/CODE/AUDIO.CPP.BAK b/CODE/AUDIO.CPP.BAK new file mode 100644 index 0000000..a653458 --- /dev/null +++ b/CODE/AUDIO.CPP.BAK @@ -0,0 +1,656 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\audio.cpv 4.78 03 Oct 1996 09:20:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 15, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Speech_Name -- Fetches the name for the voice specified. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * Voc_Name -- Fetches the name for the sound effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +} ContextType; + +static struct { + char const * Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1 + + /* + ** Infantry and vehicle responses. + */ + {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders" + {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative" + {"EENGIN1", 20, IN_VAR}, // VOC_ENG_ENG Engineer: "engineering" + {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting" + {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger" + {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it" + {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir" + + {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound + {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium). + {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short). + {"IRONCUR1", 5, IN_NOVAR}, // VOC_IRON1 + {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out" + {"IRONCUR2", 5, IN_NOVAR}, // VOC_IRON2 + {"x", 1, IN_NOVAR}, + {"x", 1, IN_NOVAR}, + {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound. + {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark. + {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine. + {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl. + {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound. + {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound. + {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss. + {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow). + {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast). + {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir" + {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot. + {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect. + {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door. + {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect. + {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled). + {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled). + {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled). + {"x", 1, IN_NOVAR}, + {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp). + {"x", 1, IN_NOVAR}, + {"x", 1, IN_NOVAR}, + {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast). + {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect. + {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch. + {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch. + {"x", 1, IN_NOVAR}, + {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles). + {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound. + {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound. + {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer. + {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot. + {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire. + {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch. + {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire. + {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up. + {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect. + {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect. + {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep. + {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics. + {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect. + {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound. + {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE). + {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion. + {"x", 10, IN_NOVAR}, + {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud). + {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'. + {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble. + {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick. + {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick. + {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound. + {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep. + {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble. + {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble. + {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble. + {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble. + {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence. + {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence. + {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound. + {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake. + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock" + {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha" + {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby" + {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching" + {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got" + {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye" + {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there" + {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me" + {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?" + {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?" + {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up." + {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound. + {"x", 10, IN_NOVAR}, + {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession. + {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound. + {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah" + {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok" + {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes" + {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound + + {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?" + {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir" + {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed" + {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way" + {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country" + {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting" + {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir" + {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative" + {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out" + {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep + + {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?" + {"x", 20, IN_NOVAR}, + {"x", 20, IN_NOVAR}, + {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out" + {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok" + {"x", 20, IN_NOVAR}, + {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what" + {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative" +}; + + +/*********************************************************************************************** + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * * + * This will find the corresponding VocType from the ASCII string specified. It does this * + * by finding a root filename that matches the string. * + * * + * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. * + * * + * OUTPUT: Returns with the VocType that matches the string specified. If no match could be * + * found, then VOC_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +VocType Voc_From_Name(char const * name) +{ + #ifdef 0 + if (name == NULL) return(VOC_NONE); + + for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) { + if (stricmp(name, SoundEffectName[voc].Name) == 0) { + return(voc); + } + } + +#endif + return(VOC_NONE); + +} + + +/*********************************************************************************************** + * Voc_Name -- Fetches the name for the sound effect. * + * * + * This routine returns the descriptive name of the sound effect. Currently, this is just * + * the root of the file name. * + * * + * INPUT: voc -- The VocType that the corresponding name is requested. * + * * + * OUTPUT: Returns with a pointer to the text string the represents the sound effect. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Voc_Name(VocType voc) +{ + if (voc == VOC_NONE) return("none"); + return(SoundEffectName[voc].Name); +} + + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + * 09/15/1996 JLB : Revamped volume logic. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation) +{ + #ifdef 0 + CELL cell_pos = 0; + int pan_value; + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + fixed volume = 1; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W; + fixed dfixed = fixed(distance, 128+64); + dfixed.Sub_Saturate(1); + volume = fixed(1) - dfixed; + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, volume, variation, pan_value); +#endif +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value) +{ + #ifdef 0 + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Alter the volume according to the game volume setting. + */ + volume = volume * Options.Volume; + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MFCD::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr != NULL) { + volume.Sub_Saturate(1); + return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value)); +// } else { +// Mono_Printf("Cannot find '%s'.\n", name); + } +#endif + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +static char const * Speech[VOX_COUNT] = { + "MISNWON1", // VOX_ACCOMPLISHED mission accomplished + "MISNLST1", // VOX_FAIL your mission has failed + "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress + "CONSCMP1", // VOX_CONSTRUCTION construction complete + "UNITRDY1", // VOX_UNIT_READY unit ready + "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options + "NODEPLY1", // VOX_DEPLOY cannot deploy here + "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed + "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power + "NOFUNDS1", // VOX_NO_CASH insufficient funds + "BCT1", // VOX_CONTROL_EXIT battle control terminated + "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived + "CANCLD1", // VOX_CANCELED canceled + "ABLDGIN1", // VOX_BUILDING building + "LOPOWER1", // VOX_LOW_POWER low power + "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds + "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack + "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more + "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected + "none", + "none", // VOX_SOVIET_CAPTURED Allied building captured + "UNITLST1", // VOX_UNIT_LOST unit lost + "SLCTTGT1", // VOX_SELECT_TARGET select target + "ENMYAPP1", // VOX_PREPARE enemy approaching + "SILOND1", // VOX_NEED_MO_CAPACITY silos needed + "ONHOLD1", // VOX_SUSPENDED on hold + "REPAIR1", // VOX_REPAIRING repairing + "none", + "none", + "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost + "none", + "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching + "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived + "none", + "none", + "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated + "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging + "CHRORDY1", // VOX_CHRONO_READY chronosphere ready + "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful + "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack + "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated + "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching + "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost + "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed + "CREDIT1", // VOX_MONEY_STOLEN credits stolen + "NAVYLST1", // VOX_SHIP_LOST naval unit lost + "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched + "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available + "none", + "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching + "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived + "TRAIN1", // VOX_TRAINING training + "AREADY1", // VOX_ABOMB_READY + "ALAUNCH1", // VOX_ABOMB_LAUNCH + "AARRIVN1", // VOX_ALLIES_N + "AARRIVS1", // VOX_ALLIES_S + "AARIVE1", // VOX_ALLIES_E + "AARRIVW1", // VOX_ALLIES_W + "1OBJMET1", // VOX_OBJECTIVE1 + "2OBJMET1", // VOX_OBJECTIVE2 + "3OBJMET1", // VOX_OBJECTIVE3 + "IRONCHG1", // VOX_IRON_CHARGING + "IRONRDY1", // VOX_IRON_READY + "KOSYRES1", // VOX_RESCUED + "OBJNMET1", // VOX_OBJECTIVE_NOT + "FLAREN1", // VOX_SIGNAL_N + "FLARES1", // VOX_SIGNAL_S + "FLAREE1", // VOX_SIGNAL_E + "FLAREW1", // VOX_SIGNAL_W + "SPYPLN1", // VOX_SPY_PLANE + "TANYAF1", // VOX_FREED + "ARMORUP1", // VOX_UPGRADE_ARMOR + "FIREPO1", // VOX_UPGRADE_FIREPOWER + "UNITSPD1", // VOX_UPGRADE_SPEED + "MTIMEIN1", // VOX_MISSION_TIMER + "UNITFUL1", // VOX_UNIT_FULL + "UNITREP1", // VOX_UNIT_REPAIRED + "40MINR", // VOX_TIME_40 + "30MINR", // VOX_TIME_30 + "20MINR", // VOX_TIME_20 + "10MINR", // VOX_TIME_10 + "5MINR", // VOX_TIME_5 + "4MINR", // VOX_TIME_4 + "3MINR", // VOX_TIME_3 + "2MINR", // VOX_TIME_2 + "1MINR", // VOX_TIME_1 + "TIMERNO1", // VOX_TIME_STOP + "UNITSLD1", // VOX_UNIT_SOLD + "TIMERGO1", // VOX_TIMER_STARTED + "TARGRES1", // VOX_TARGET_RESCUED + "TARGFRE1", // VOX_TARGET_FREED + "TANYAR1", // VOX_TANYA_RESCUED + "STRUSLD1", // VOX_STRUCTURE_SOLD + "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN + "SOVEMP1", // VOX_SOVIET_SELECTED + "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN + "OPTERM1", // VOX_OPERATION_TERMINATED + "OBJRCH1", // VOX_OBJECTIVE_REACHED + "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED + "OBJMET1", // VOX_OBJECTIVE_MET + "MERCR1", // VOX_MERCENARY_RESCUED + "MERCF1", // VOX_MERCENARY_FREED + "KOSYFRE1", // VOX_KOSOYGEN_FREED + "FLARE1", // VOX_FLARE_DETECTED + "COMNDOR1", // VOX_COMMANDO_RESCUED + "COMNDOF1", // VOX_COMMANDO_FREED + "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS + "ATPREP1", // VOX_ATOM_PREPPING + "ASELECT1", // VOX_ALLIED_SELECTED + "APREP1", // VOX_ABOMB_PREPPING + "ATLNCH1", // VOX_ATOM_LAUNCHED + "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN + "AAVAIL1", // VOX_ABOMB_AVAILABLE + "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS + "SAVE1", // VOX_MISSION_SAVED + "LOAD1" // VOX_MISSION_LOADED +}; + + +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speech_Name -- Fetches the name for the voice specified. * + * * + * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this * + * would be to build a displayable list of the speech types. The trigger system uses this * + * so that a speech type can be selected. * + * * + * INPUT: speech -- The speech type id to convert to ASCII string. * + * * + * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 JLB : Created. * + *=============================================================================================*/ +char const * Speech_Name(VoxType speech) +{ + if (speech == VOX_NONE) return("none"); + return(Speech[speech]); +} + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +void Speak(VoxType voice) +{ + if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + Speak_AI(); + } +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Speak_AI(void) +{ + static VoxType _last = VOX_NONE; + if (Debug_Quiet || SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer)) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + if (SpeakQueue != _last) { + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + CCFileClass file(name); + if (file.Is_Available() && file.Read(SpeechBuffer, SPEECH_BUFFER_SIZE)) { + Play_Sample(SpeechBuffer, 254, Options.Volume * 256); + CurrentVoice = SpeakQueue; + } + _last = SpeakQueue; + } else { + Play_Sample(SpeechBuffer, 254, Options.Volume * 256); + } + SpeakQueue = VOX_NONE; + } + } +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + Stop_Sample_Playing(SpeechBuffer); +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + if (!Debug_Quiet && SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) { + return(true); + } + return(false); +} diff --git a/CODE/AUDIO.H b/CODE/AUDIO.H new file mode 100644 index 0000000..7f24844 --- /dev/null +++ b/CODE/AUDIO.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\audio.h_v 4.43 05 Jul 1996 17:58:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : AUDIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : June 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "memory.h" + +class AudioClass { + char const * Name; // Name of audio asset. + void const * Data; // Loaded audio data. + int Handle; // Handle of asset (as it is playing). + MemoryClass *Mem; // Pointer to memory handler class. + unsigned IsMIDI:1; // Is this a midi file? + + public: + AudioClass(void); + AudioClass(char const *name, MemoryClass &mem); + virtual ~AudioClass(void); + + bool Load(char const *name = 0); + bool Free(void); + bool Play(int volume = 0xFF); + bool Stop(void); + bool Pause(void); + bool Resume(void); + bool Set_Name(char const *name); + bool Is_Playing(void) const; + bool Is_Loaded(void) const; + bool Is_MIDI(void) const; +}; + +inline AudioClass::AudioClass(void) +{ + Name = 0; + Data = 0; + Mem = 0; + Handle = -1; +}; + +inline AudioClass::AudioClass(char const *name, MemoryClass &mem) +{ + if (mem) { + Mem = &mem; + } else { + Mem = &::Mem; // Uses global default memory handler. + } + Name = strdup(name); + Data = 0; + Handle = -1; +}; + +inline AudioClass::~AudioClass(void) +{ + if (GameActive) { + if (Name) free(Name); + if (Data) Mem->Free(Data); + Name = 0; + Data = 0; + Handle = -1; + } +}; + + +#endif diff --git a/CODE/B64PIPE.CPP b/CODE/B64PIPE.CPP new file mode 100644 index 0000000..353869c --- /dev/null +++ b/CODE/B64PIPE.CPP @@ -0,0 +1,165 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/B64PIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : B64PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64pipe.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * * + * This will take the data submitted and either Base64 encode or decode it (as specified * + * in the pipe's constructor). The nature of Base64 encoding means that the data will * + * grow 30% in size when encoding and decrease by a like amount when decoding. * + * * + * INPUT: source -- Pointer to the data to be translated. * + * * + * length -- The number of bytes to translate. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final end of * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + if (Counter > 0) { + int len = (slen < (fromsize-Counter)) ? slen : (fromsize-Counter); + memmove(&from[Counter], source, len); + Counter += len; + slen -= len; + source = ((char *)source) + len; + + if (Counter == fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(from, fromsize, to, tosize); + } else { + outcount = Base64_Decode(from, fromsize, to, tosize); + } + total += Pipe::Put(to, outcount); + Counter = 0; + } + } + + while (slen >= fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(source, fromsize, to, tosize); + } else { + outcount = Base64_Decode(source, fromsize, to, tosize); + } + source = ((char *)source) + fromsize; + total += Pipe::Put(to, outcount); + slen -= fromsize; + } + + if (slen > 0) { + memmove(from, source, slen); + Counter = slen; + } + + return(total); +} + + +/*********************************************************************************************** + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * * + * If there is any non-processed data accumulated in the holding buffer (quite likely when * + * encoding), then it will be processed and flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Flush(void) +{ + int len = 0; + + if (Counter) { + if (Control == ENCODE) { + int chars = Base64_Encode(PBuffer, Counter, CBuffer, sizeof(CBuffer)); + len += Pipe::Put(CBuffer, chars); + } else { + int chars = Base64_Decode(CBuffer, Counter, PBuffer, sizeof(PBuffer)); + len += Pipe::Put(PBuffer, chars); + } + Counter = 0; + } + len += Pipe::Flush(); + return(len); +} + + + diff --git a/CODE/B64PIPE.H b/CODE/B64PIPE.H new file mode 100644 index 0000000..2512d91 --- /dev/null +++ b/CODE/B64PIPE.H @@ -0,0 +1,92 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/B64PIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : B64PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64PIPE_H +#define B64PIPE_H + +#include "pipe.h" + +/* +** This class performs Base64 encoding/decoding to the data that is piped through. Note that +** encoded data will grow in size by about 30%. The reverse occurs when decoding. +*/ +class Base64Pipe : public Pipe +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Pipe(CodeControl control) : Control(control), Counter(0) {} + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Pipe(Base64Pipe & rvalue); + Base64Pipe & operator = (Base64Pipe const & pipe); +}; + +#endif diff --git a/CODE/B64STRAW.CPP b/CODE/B64STRAW.CPP new file mode 100644 index 0000000..bff6cb4 --- /dev/null +++ b/CODE/B64STRAW.CPP @@ -0,0 +1,117 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/B64STRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : B64STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64straw.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * * + * This routine will fetch the number of bytes requested and perform any conversion as * + * necessary upon the data. The nature of Base 64 encoding means that the data will * + * increase in size by 30% when encoding and decrease in like manner when decoding. * + * * + * INPUT: source -- The buffer to hold the processed data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number is less * + * than requested, then this indicates that the data stream has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Straw::Get(void * source, int slen) +{ + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + /* + ** Process the byte request in code blocks until there are either + ** no more source bytes available or the request has been fulfilled. + */ + while (slen > 0) { + + /* + ** Transfer any processed bytes available to the request buffer. + */ + if (Counter > 0) { + int len = (slen < Counter) ? slen : Counter; + memmove(source, &to[tosize-Counter], len); + Counter -= len; + slen -= len; + source = ((char *)source) + len; + total += len; + } + if (slen == 0) break; + + /* + ** More bytes are needed, so fetch and process another base 64 block. + */ + int incount = Straw::Get(from, fromsize); + if (Control == ENCODE) { + Counter = Base64_Encode(from, incount, to, tosize); + } else { + Counter = Base64_Decode(from, incount, to, tosize); + } + if (Counter == 0) break; + } + + return(total); +} diff --git a/CODE/B64STRAW.H b/CODE/B64STRAW.H new file mode 100644 index 0000000..8153eae --- /dev/null +++ b/CODE/B64STRAW.H @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/B64STRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : B64STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64STRAW_H +#define B64STRAW_H + +#include "straw.h" + +/* +** Performs Base 64 encoding/decoding on the data that is drawn through the straw. Note that +** encoding increases the data size by about 30%. The reverse occurs when decoding. +*/ +class Base64Straw : public Straw +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Straw(CodeControl control) : Control(control), Counter(0) {} + virtual int Get(void * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Straw(Base64Straw & rvalue); + Base64Straw & operator = (Base64Straw const & pipe); +}; + + +#endif diff --git a/CODE/BAR.CPP b/CODE/BAR.CPP new file mode 100644 index 0000000..16b757c --- /dev/null +++ b/CODE/BAR.CPP @@ -0,0 +1,239 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BAR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * ProgressBarClass::Set_Limit -- Set the logic tracking value. * + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "bar.h" +#include "fixed.h" + + +/*********************************************************************************************** + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * * + * This is the constructor for the bargraph object. It establishes the dimensions and * + * coordinate of the bargraph as well as the colors it will use when drawn. * + * * + * INPUT: w,y -- Pixel coordinate of the upper left corner of the bargraph. * + * * + * width,height -- Dimensions of the bargraph. * + * * + * forecolor -- The color to use for the filled portion of the bargraph. * + * * + * backcolor -- The color to use for the non-filled portion of the bargraph. * + * * + * bordercolor -- Optional border color. If not zero, then the bargraph will be * + * outlined with this color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +ProgressBarClass::ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor) : + X(x), + Y(y), + Width(width), + Height(height), + BarColor(forecolor), + BackColor(backcolor), + BorderColor(bordercolor), + CurrentValue(0), + LastDisplayCurrent(0), + IsDrawn(false) +{ +} + + +/*********************************************************************************************** + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * * + * If the bargraph is oriented horizontally, then this function will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this bargraph horizontal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Is_Horizontal(void) const +{ + if (Width > Height) return(true); + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * * + * This will update the value of the bargraph to the fill ratio specified and then * + * redraw it if required. Very small changes to the bargraph value might not result in a * + * visual change. * + * * + * INPUT: value -- The new value to assign to this bargraph. * + * * + * OUTPUT: none * + * * + * WARNINGS: bool; Did this update result in a redraw? * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Update(fixed value) +{ + CurrentValue = value; + + if (!IsDrawn || value - LastDisplayCurrent >= fixed(1, 10)) { + Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * * + * This routine will draw a border around the bargraph if this bargraph has a color * + * specified for the border. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Outline(void) const +{ + if (Is_Outlined()) { + LogicPage->Draw_Line(X, Y, X+Width, Y, BorderColor); + LogicPage->Draw_Line(X, Y, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X, Y+Height, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X+Width, Y, X+Width, Y+Height, BorderColor); + } +} + + +/*********************************************************************************************** + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * * + * This will redraw the entire bargraph. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Redraw(void) const +{ + Hide_Mouse(); + + Outline(); + + /* + ** Determine the inner dimensions of the bargraph. This will be + ** somewhat smaller than indicated if it has a border. + */ + int x = X; + int y = Y; + int w = Width; + int h = Height; + if (Is_Outlined()) { + x += 1; + y += 1; + w -= 2; + h -= 2; + } + + /* + ** The working "length" of the bargraph is dependant on whether the + ** bargraph is horizontal or vertical. + */ + int size = Is_Horizontal() ? w : h; + + /* + ** Determine the number of pixels to fill in the bargraph depending on the + ** size of the internal value. The larger the internal value the more + ** filled the bargraph becomes. + */ + int fill = CurrentValue * size; + + /* + ** Draw the filled portion of the bargraph if there is any pixels to draw. + */ + if (fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x, y, x+fill, y+h, BarColor); + } else { + LogicPage->Fill_Rect(x, y+fill, x+w, y+h, BarColor); + } + } + + /* + ** Draw the unfilled portion of the bargraph if there are any pixels to + ** draw of it. + */ + if (w-fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x+fill, y, x+w, y+h, BackColor); + } else { + LogicPage->Fill_Rect(x, y, x+w, y+fill-1, BackColor); + } + } + + Show_Mouse(); + + ProgressBarClass * me = (ProgressBarClass *)this; + me->LastDisplayCurrent = CurrentValue; + me->IsDrawn = true; +} diff --git a/CODE/BAR.H b/CODE/BAR.H new file mode 100644 index 0000000..6af34ef --- /dev/null +++ b/CODE/BAR.H @@ -0,0 +1,111 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BAR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BAR_H +#define BAR_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include "fixed.h" + + +/* +** This is a manager for a progress (or other) bargraph. Such a graph consists of a fill +** and a background region. The fill percentage of the bargraph is controlled by an +** update value. The bargraph can be optionally outlined. +*/ +class ProgressBarClass +{ + public: + ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor=0); + + bool Update(fixed value); + void Redraw(void) const; + + private: + + void Outline(void) const; + bool Is_Horizontal(void) const; + bool Is_Outlined(void) const {return(BorderColor != 0);} + + /* + ** This is the upper left coordinates of the bargraph. + */ + int X,Y; + + /* + ** This is the dimensions of the bargraph. + */ + int Width, Height; + + /* + ** These are the colors to use when drawing the progress bar. + */ + int BarColor; + int BackColor; + int BorderColor; + + /* + ** This is the current value of the bargraph. + */ + fixed CurrentValue; + + /* + ** This is the current value as of the last time the bargraph was rendered. + */ + fixed LastDisplayCurrent; + + /* + ** If the bargraph has been drawn at least once, then this flag will + ** be true. + */ + unsigned IsDrawn:1; +}; + + +#endif diff --git a/CODE/BASE.CPP b/CODE/BASE.CPP new file mode 100644 index 0000000..121d8e9 --- /dev/null +++ b/CODE/BASE.CPP @@ -0,0 +1,548 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BASE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BASE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * BaseClass::Load -- loads from a saved game file * + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * BaseClass::Read_INI -- INI reading routine * + * BaseClass::Save -- saves to a saved game file * + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * BaseNodeClass::operator != -- inequality operator * + * BaseNodeClass::operator == -- equality operator * + * BaseNodeClass::operator > -- greater-than operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BaseNodeClass::operator == -- equality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * true = equal, false = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator == (BaseNodeClass const & node) +{ + return(Type == node.Type && Cell == node.Cell); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator != -- inequality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator !=(BaseNodeClass const & node) +{ + return(!(*this == node)); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator > -- greater-than operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator > (BaseNodeClass const & ) +{ + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Load -- loads from a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to demand driven data source. * + *=============================================================================================*/ +bool BaseClass::Load(Straw & file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Read in & check the size of this class + */ + if (file.Get(&i, sizeof(i)) != sizeof(i)) { + return(false); + } + + if (i != sizeof(*this)) { + return(false); + } + + /* + ** Read in the House & the number of structures in the base + */ + if (file.Get(&House, sizeof(House)) != sizeof(House)) { + return(false); + } + + if (file.Get(&num_struct, sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Read each node entry & add it to the list + */ + for (i = 0; i < num_struct; i++) { + if (file.Get(&node, sizeof(node)) != sizeof(node)) { + return(false); + } + Nodes.Add(node); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Save -- saves to a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to supply driven data output. * + *=============================================================================================*/ +bool BaseClass::Save(Pipe & file) const +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Write the size of this class + */ + i = sizeof(*this); + file.Put(&i, sizeof(i)); + + /* + ** Write the House & the number of structures in the base + */ + file.Put(&House, sizeof(House)); + + num_struct = Nodes.Count(); + file.Put(&num_struct, sizeof(num_struct)); + + /* + ** Write each node entry + */ + for (i = 0; i < num_struct; i++) { + node = Nodes[i]; + file.Put(&node, sizeof(node)); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * * + * INPUT: * + * index index into base list * + * * + * OUTPUT: * + * true = yes, false = no * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Built(int index) const +{ + if (Get_Building(index) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to already-built building, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/30/1996 JLB : Handle arbitrary overlapper list length. * + *=============================================================================================*/ +BuildingClass * BaseClass::Get_Building(int index) const +{ + ObjectClass * obj[1 + ARRAY_SIZE(Map[(CELL)0].Overlapper)]; + + /* + ** Check the location on the map where this building should be; if it's + ** there, return a pointer to it. + */ + CELL cell = Nodes[index].Cell; + + obj[0] = Map[cell].Cell_Building(); + int count = 1; + for (int xindex = 0; xindex < ARRAY_SIZE(Map[cell].Overlapper); xindex++) { + if (Map[cell].Overlapper[xindex] != NULL) { + obj[count++] = Map[cell].Overlapper[xindex]; + } + } + + BuildingClass * bldg = NULL; + for (int i = 0; i < count; i++) { + if (obj[i] && + Coord_Cell(obj[i]->Coord) == Nodes[index].Cell && + obj[i]->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) { + + bldg = (BuildingClass *)obj[i]; + break; + } + } + + return(bldg); +} + + +/*********************************************************************************************** + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * true = building is a node in the list, false = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Node(BuildingClass const * obj) +{ + if (Get_Node(obj) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to node * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(BuildingClass const * obj) +{ + for (int i = 0; i < Nodes.Count(); i++) { + if (obj->Class->Type == Nodes[i].Type && Coord_Cell(obj->Coord) == Nodes[i].Cell) { + return(&Nodes[i]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * * + * This routine is used to find a matching node the corresponds to the cell specified. * + * * + * INPUT: cell -- The cell to use in finding a match. * + * * + * OUTPUT: Returns a pointer to the matching node if found. If not found, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1996 JLB : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(CELL cell) +{ + for (int index = 0; index < Nodes.Count(); index++) { + if (cell == Nodes[index].Cell) { + return(&Nodes[index]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * * + * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. * + * Otherwise, returns ptr to the next hole in the list of any type. * + * * + * INPUT: * + * type type of building to check for * + * * + * OUTPUT: * + * ptr to a BaseNodeClass, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Next_Buildable(StructType type) +{ + /* + ** Loop through all node entries, returning a pointer to the first + ** un-built one that matches the requested type. + */ + for (int i = 0; i < Nodes.Count(); i++) { + + /* + ** For STRUCT_NONE, return the first hole found + */ + if (type == STRUCT_NONE) { + if (!Is_Built(i)) { + return(&Nodes[i]); + } + + } else { + + /* + ** For a "real" building type, return the first hold for that type + */ + if (Nodes[i].Type==type && !Is_Built(i)) { + return(&Nodes[i]); + } + } + } + + +// If no entry could be found, then create a fake one that will allow +// placement of the building. Make it static and reuse the next time this +// routine is called. + + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Read_INI -- INI reading routine * + * * + * INI entry format: * + * BLDG=COORDINATE * + * BLDG=COORDINATE * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 02/20/1996 JLB : Fixed to know what house to build base from. * + *=============================================================================================*/ +void BaseClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; + char uname[10]; + BaseNodeClass node; // node to add to list + + Mono_Clear_Screen(); + /* + ** First, determine the house of the human player, and set the Base's house + ** accordingly. + */ + House = ini.Get_HousesType(INI_Name(), "Player", PlayerPtr->Class->House); + + /* + ** Read the number of buildings that will go into the base node list + */ + int count = ini.Get_Int(INI_Name(), "Count", 0); + + /* + ** Read each entry in turn, in the same order they were written out. + */ + for (int i = 0; i < count; i++) { + + /* + ** Get an INI entry + */ + sprintf(uname,"%03d",i); + ini.Get_String(INI_Name(), uname, NULL, buf, sizeof(buf)); + + /* + ** Set the node's building type + */ + node.Type = BuildingTypeClass::From_Name(strtok(buf,",")); + + /* + ** Read & set the node's coordinate + */ + node.Cell = atoi(strtok(NULL,",")); + + /* + ** Add this node to the Base's list + */ + Nodes.Add(node); + } +} + + +/*********************************************************************************************** + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * * + * Use this routine to write all prebuild base information to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: If there was any preexisting prebuild base data in the database, it will be * + * be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void BaseClass::Write_INI(CCINIClass & ini) +{ + /* + ** Clear out all existing base data from the ini file. + */ + ini.Clear(INI_Name()); + + if (House != HOUSE_NONE) { + + /* + ** Write out the owner of this buildable list. + */ + ini.Put_HousesType(INI_Name(), "Player", House); + + /* + ** Save the # of buildings in the Nodes list. This is essential because + ** they must be read in the same order they were created, so "000" must be + ** read first, etc. + */ + ini.Put_Int(INI_Name(), "Count", Nodes.Count()); + + /* + ** Write each entry into the INI + */ + for (int i = 0; i < Nodes.Count(); i++) { + char buf[128]; + char uname[10]; + + sprintf(uname,"%03d",i); + sprintf(buf,"%s,%d", + BuildingTypeClass::As_Reference(Nodes[i].Type).IniName, + Nodes[i].Cell); + + ini.Put_String(INI_Name(), uname, buf); + } + } +} diff --git a/CODE/BASE.H b/CODE/BASE.H new file mode 100644 index 0000000..91d3336 --- /dev/null +++ b/CODE/BASE.H @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BASE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BASE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BASE_H +#define BASE_H + + +/**************************************************************************** +** This class defines one "node" in the pre-built base list. Each node +** contains a type of building to build, and the COORDINATE to build it at. +*/ +class BaseNodeClass +{ + public: + BaseNodeClass(void) {}; + BaseNodeClass(StructType building, CELL cell) : Type(building), Cell(cell) {}; + int operator == (BaseNodeClass const & node); + int operator != (BaseNodeClass const & node); + int operator > (BaseNodeClass const & node); + + StructType Type; + CELL Cell; +}; + + +/**************************************************************************** +** This is the class that defines a pre-built base for the computer AI. +** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!) +*/ +class BaseClass +{ + public: + + /* + ** Constructor/Destructor + */ + BaseClass(void) {}; + virtual ~BaseClass() {Nodes.Clear();} + + /* + ** Initialization + */ + void Init(void) {House = HOUSE_NONE; Nodes.Clear();} + + /* + ** The standard suite of load/save support routines + */ + void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "Base";} + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void) {}; + virtual void Decode_Pointers(void) {}; + + /* + ** Tells if the given node has been built or not + */ + bool Is_Built(int index) const; + + /* + ** Returns a pointer to the object for the given node + */ + BuildingClass * Get_Building(int index) const; + + /* + ** Tells if the given building ptr is a node in this base's list. + */ + bool Is_Node(BuildingClass const * obj); + + /* + ** Returns a pointer to the requested node. + */ + BaseNodeClass * Get_Node(BuildingClass const * obj); + BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); } + BaseNodeClass * Get_Node(CELL cell); + + /* + ** Returns a pointer to the next "hole" in the Nodes list. + */ + BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE); + + /* + ** This is the list of "nodes" that define the base. Portions of this + ** list can be pre-built by simply saving those buildings in the INI + ** along with non-base buildings, so Is_Built will return true for them. + */ + DynamicVectorClass Nodes; + + /* + ** This is the house this base belongs to. + */ + HousesType House; +}; + + +#endif + diff --git a/CODE/BASE64.CPP b/CODE/BASE64.CPP new file mode 100644 index 0000000..5e77a85 --- /dev/null +++ b/CODE/BASE64.CPP @@ -0,0 +1,437 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BASE64.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BASE64.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * Base64_Encode -- Encode data into Base 64 format. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "base64.h" +#include + +/* +** This is the magic padding character used to fill out the encoded data to a multiple of +** 4 characters even though the source data is less than necessary to accomplish this. +** The pad character lets the decoder know of this condition and it will compensate +** accordingly. +*/ +static char const * const _pad = "="; + +/* +** This encoder translation table will convert a 6 bit number into an ASCII character. +*/ +static char const * const _encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** The decoder translation table takes an ASCII character and converts it into a +** 6 bit number. +*/ +#define BAD 0xFE // Ignore this character in source data. +#define END 0xFF // Signifies premature end of input data. +static unsigned char const _decoder[256] = { + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,62,BAD,BAD,BAD,63, + 52,53,54,55,56,57,58,59,60,61,BAD,BAD,BAD,END,BAD,BAD, + BAD,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,BAD,BAD,BAD,BAD,BAD, + BAD,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD +}; + +int const PacketChars = 4; + + +/* +** The packet type is used to construct and disect the Base64 data blocks. The data +** consists of three source data bytes mapped onto four 6 bit Base64 code elements. +*/ +typedef union { + struct { +#ifdef BIG_ENDIAN + unsigned char C1; + unsigned char C2; + unsigned char C3; +#else + unsigned char C3; + unsigned char C2; + unsigned char C1; +#endif + unsigned char pad; + } Char; + struct { +#ifdef BIG_ENDIAN + unsigned O1:6; + unsigned O2:6; + unsigned O3:6; + unsigned O4:6; +#else + unsigned O4:6; + unsigned O3:6; + unsigned O2:6; + unsigned O1:6; +#endif + unsigned pad:8; + } SubCode; + unsigned int Raw; +} PacketType; + + +/*********************************************************************************************** + * Base64_Encode -- Encode data into Base 64 format. * + * * + * This will take an arbitrary length of source data and transform it into base 64 format * + * data. Base 64 format has the property of being very portable across text editors and * + * country character encoding schemes. As such it is ideal for e-mail. Note that the output * + * data will be about 33% larger than the source. * + * * + * INPUT: source -- Pointer to the source data to convert. * + * * + * slen -- The number of bytes to encode. * + * * + * dest -- Pointer to the destination buffer that will hold the encoded data. * + * * + * dlen -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the encoded output. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Encode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + /* + ** Process the source data in blocks of three bytes. Fewer than three bytes + ** results in special padding output characters (automatically discarded + ** during the decode process). + */ + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen >= PacketChars) { + + /* + ** Fetch 24 bits of source data. + */ + PacketType packet; + + int pad = 0; + packet.Raw = 0; + packet.Char.C1 = *sptr++; + slen--; + if (slen) { + packet.Char.C2 = *sptr++; + slen--; + } else { + pad++; + } + if (slen) { + packet.Char.C3 = *sptr++; + slen--; + } else { + pad++; + } + + /* + ** Translate and write 4 characters of Base64 data. Pad with pad + ** characters if there is insufficient source data for a full packet. + */ + *dptr++ = _encoder[packet.SubCode.O1]; + *dptr++ = _encoder[packet.SubCode.O2]; + if (pad < 2) { + *dptr++ = _encoder[packet.SubCode.O3]; + } else { + *dptr++ = _pad[0]; + } + if (pad < 1) { + *dptr++ = _encoder[packet.SubCode.O4]; + } else { + *dptr++ = _pad[0]; + } + + dlen -= PacketChars; + total += PacketChars; + } + + /* + ** Add a trailing null as a courtesy measure. + */ + if (dlen > 0) { + *dptr = '\0'; + } + + /* + ** Return with the total number of characters in the output buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * * + * Use this routine to decode base 64 data back into the original data. A property of this * + * decode process is that unrecognized input characters are ignored. This allows mangled * + * source (filled with line breaks or spaces) to be correctly decoded. The decode process * + * terminates when the end of the source data has been reached or the special end of data * + * marker is encountered. * + * * + * INPUT: source -- Pointer to the source data to decode. * + * * + * slen -- The number of bytes in the source data buffer. * + * * + * dest -- Pointer to the destination buffer to be filled with the decoded data. * + * * + * dlen -- The maximum size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. This will * + * always be less than the number of source bytes (usually by about 33%). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Decode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen > 0) { + + PacketType packet; + packet.Raw = 0; + + /* + ** Process input until a full packet has been accumulated or the + ** source is exhausted. + */ + int pcount = 0; + while (pcount < PacketChars && slen > 0) { + unsigned char c = *sptr++; + slen--; + + unsigned char code = _decoder[c]; + + /* + ** An unrecognized character is skipped. + */ + if (code == BAD) continue; + + /* + ** The "=" character signifies the end of data regardless of what + ** the source buffer length value may be. + */ + if (code == END) { + slen = 0; + break; + } + + /* + ** A valid Base64 character was found so add it to the packet + ** data. + */ + switch (pcount) { + case 0: + packet.SubCode.O1 = code; + break; + case 1: + packet.SubCode.O2 = code; + break; + case 2: + packet.SubCode.O3 = code; + break; + case 3: + packet.SubCode.O4 = code; + break; + } + pcount++; + } + + /* + ** A packet block is ready for output into the destination buffer. + */ + *dptr++ = packet.Char.C1; + dlen--; + total++; + if (dlen > 0 && pcount > 2) { + *dptr++ = packet.Char.C2; + dlen--; + total++; + } + if (dlen > 0 && pcount > 3) { + *dptr++ = packet.Char.C3; + dlen--; + total++; + } + } + + /* + ** Return with the total number of characters decoded into the + ** output buffer. + */ + return(total); +} + + +/* +Base64 Content-Transfer-Encoding + +The Base64 Content-Transfer-Encoding is designed to represent arbitrary +sequences of octets in a form that need not be humanly readable. The encoding +and decoding algorithms are simple, but the encoded data are consistently +only about 33 percent larger than the unencoded data. This encoding is +virtually identical to the one used in Privacy Enhanced Mail (PEM) +applications, as defined in RFC 1421. The base64 encoding is adapted from +RFC 1421, with one change: base64 eliminates the "*" mechanism for embedded +clear text. + +A 65-character subset of US-ASCII is used, enabling 6 bits to be represented +per printable character. (The extra 65th character, "=", is used to signify a +special processing function.) + +NOTE: + This subset has the important property that it is represented identically + in all versions of ISO 646, including US ASCII, and all characters in the + subset are also represented identically in all versions of EBCDIC. Other + popular encodings, such as the encoding used by the uuencode utility and + the base85 encoding specified as part of Level 2 PostScript, do not share + these properties, and thus do not fulfill the portability requirements a + binary transport encoding for mail must meet. + +The encoding process represents 24-bit groups of input bits as output strings +of 4 encoded characters. Proceeding from left to right, a 24-bit input group is +formed by concatenating 3 8-bit input groups. These 24 bits are then treated as +4 concatenated 6-bit groups, each of which is translated into a single digit in +the base64 alphabet. When encoding a bit stream via the base64 encoding, the +bit stream must be presumed to be ordered with the most-significant-bit first. +That is, the first bit in the stream will be the high-order bit in the first +byte, and the eighth bit will be the low-order bit in the first byte, and so on. + +Each 6-bit group is used as an index into an array of 64 printable characters. +The character referenced by the index is placed in the output string. These +characters, identified in Table 1, below, are selected so as to be universally +representable, and the set excludes characters with particular significance to +SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries defined in this +document (e.g., "-"). + +Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + +The output stream (encoded bytes) must be represented in lines of no more than +76 characters each. All line breaks or other characters not found in Table 1 +must be ignored by decoding software. In base64 data, characters other than +those in Table 1, line breaks, and other white space probably indicate a +transmission error, about which a warning message or even a message rejection +might be appropriate under some circumstances. + +Special processing is performed if fewer than 24 bits are available at the end +of the data being encoded. A full encoding quantum is always completed at the +end of a body. When fewer than 24 input bits are available in an input group, +zero bits are added (on the right) to form an integral number of 6-bit groups. +Padding at the end of the data is performed using the '=' character. Since all +base64 input is an integral number of octets, only the following cases can +arise: (1) the final quantum of encoding input is an integral multiple of 24 +bits; here, the final unit of encoded output will be an integral multiple of 4 +characters with no "=" padding, (2) the final quantum of encoding input is +exactly 8 bits; here, the final unit of encoded output will be two characters +followed by two "=" padding characters, or (3) the final quantum of encoding +input is exactly 16 bits; here, the final unit of encoded output will be three +characters followed by one "=" padding character. + +Because it is used only for padding at the end of the data, the occurrence of +any '=' characters may be taken as evidence that the end of the data has been +reached (without truncation in transit). No such assurance is possible, +however, when the number of octets transmitted was a multiple of three. + +Any characters outside of the base64 alphabet are to be ignored in +base64-encoded data. The same applies to any illegal sequence of characters in +the base64 encoding, such as "=====" + +Care must be taken to use the proper octets for line breaks if base64 encoding +is applied directly to text material that has not been converted to canonical +form. In particular, text line breaks must be converted into CRLF sequences +prior to base64 encoding. The important thing to note is that this may be done +directly by the encoder rather than in a prior canonicalization step in some +implementations. + +NOTE: + There is no need to worry about quoting apparent encapsulation boundaries + within base64-encoded parts of multipart entities because no hyphen + characters are used in the base64 encoding. + +*/ \ No newline at end of file diff --git a/CODE/BASE64.H b/CODE/BASE64.H new file mode 100644 index 0000000..3a6d945 --- /dev/null +++ b/CODE/BASE64.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BASE64.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BASE64.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +int Base64_Encode(void const * source, int slen, void * dest, int dlen); +int Base64_Decode(void const * source, int slen, void * dest, int dlen); diff --git a/CODE/BBDATA.CPP b/CODE/BBDATA.CPP new file mode 100644 index 0000000..f241c42 --- /dev/null +++ b/CODE/BBDATA.CPP @@ -0,0 +1,300 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BBDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BBDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specif* + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. * + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + + +/*********************************************************************************************** + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * * + * This is basically a constructor for static type objects used by bullets. All bullets * + * are of a type constructed by this routine at game initialization time. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 07/17/1996 JLB : Uses correct default values. * + *=============================================================================================*/ +BulletTypeClass::BulletTypeClass(char const * name) : + ObjectTypeClass( RTTI_BULLETTYPE, + BulletTypes.ID(this), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsHigh(false), + IsShadow(true), + IsArcing(false), + IsDropping(false), + IsInvisible(false), + IsProximityArmed(false), + IsFlameEquipped(false), + IsFueled(false), + IsFaceless(true), + IsInaccurate(false), + IsTranslucent(false), + IsAntiAircraft(false), + IsAntiGround(true), + IsAntiSub(false), + IsDegenerate(false), + IsSubSurface(false), + IsParachuted(false), + IsGigundo(false), + Type(BulletType(ID)), + ROT(0), + Arming(0), + Tumble(0) +{ +} + + +/*********************************************************************************************** + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * * + * This allocates a bullet type object from a special heap that is used just for * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to an allocated block or NULL if the allocation could not * + * occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BulletTypeClass::operator new(size_t) +{ + return(BulletTypes.Alloc()); +} + + +/*********************************************************************************************** + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * * + * This is the counterpart to the operator new function for bullet type objects. It will * + * return the bullet type object back to the special heap used for bullet type object * + * allocation. * + * * + * INPUT: ptr -- Pointer to the bullet type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::operator delete(void * ptr) +{ + BulletTypes.Free((BulletTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * * + * This performs any necessary initialization for the bullet types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::Init_Heap(void) +{ + /* + ** These bullet type class objects must be allocated in the exact order that they + ** are specified in the BulletType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BulletTypeClass("Invisible"); // BULLET_INVISIBLE + new BulletTypeClass("Cannon"); // BULLET_CANNON + new BulletTypeClass("Ack"); // BULLET_ACK + new BulletTypeClass("Torpedo"); // BULLET_TORPEDO + new BulletTypeClass("FROG"); // BULLET_FROG + new BulletTypeClass("HeatSeeker"); // BULLET_HEAT_SEEKER + new BulletTypeClass("LaserGuided"); // BULLET_LASER_GUIDED + new BulletTypeClass("Lobbed"); // BULLET_LOBBED + new BulletTypeClass("Bomblet"); // BULLET_BOMBLET + new BulletTypeClass("Ballistic"); // BULLET_BALLISTIC + new BulletTypeClass("Parachute"); // BULLET_PARACHUTE + new BulletTypeClass("Fireball"); // BULLET_FIREBALL + new BulletTypeClass("LeapDog"); // BULLET_DOG + new BulletTypeClass("Catapult"); // BULLET_CATAPULT + new BulletTypeClass("AAMissile"); // BULLET_AAMISSILE + new BulletTypeClass("GPSSatellite");// BULLET_GPS_SATELLITE + new BulletTypeClass("NukeUp"); // BULLET_NUKE_UP + new BulletTypeClass("NukeDown"); // BULLET_NUKE_DOWN +} + + +/*********************************************************************************************** + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * * + * This routine is used to perform any one time processing for the bullet type class. It * + * handles loading of the shape files. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called before any rendering of bullets occurs and should * + * only be called once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::One_Time(void) +{ + /* + ** Load the bullet shapes. + */ + for (BulletType index = BULLET_FIRST; index < BULLET_COUNT; index++) { + BulletTypeClass const & bullet = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + if (!bullet.IsInvisible) { + _makepath(fullname, NULL, NULL, bullet.GraphicName, ".SHP"); + + #ifdef NDEBUG + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + #else + RawFileClass file(fullname); + + if (file.Is_Available()) { + ((void const *&)bullet.ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + } + #endif + } + } +} + + +/*********************************************************************************************** + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specifi * + * * + * Given a bullet type identifier, this routine will return a reference to the bullet type * + * object it refers to. * + * * + * INPUT: type -- The bullet type identifier to convert to a reference. * + * * + * OUTPUT: Returns with a reference to the bullet type object. * + * * + * WARNINGS: Make sure that the type parameter specified is a valid bullet type. If not, * + * then the results are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletTypeClass & BulletTypeClass::As_Reference(BulletType type) +{ + return(*BulletTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * * + * Use this routine to fetch override information about this bullet type class object * + * from the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to examine. * + * * + * OUTPUT: bool; Was the section for this bullet found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + Arming = ini.Get_Int(Name(), "Arm", Arming); + ROT = ini.Get_Int(Name(), "ROT", ROT); + Tumble = ini.Get_Int(Name(), "Frames", Tumble); + IsHigh = ini.Get_Bool(Name(), "High", IsHigh); + IsShadow = ini.Get_Bool(Name(), "Shadow", IsShadow); + IsArcing = ini.Get_Bool(Name(), "Arcing", IsArcing); + IsDropping = ini.Get_Bool(Name(), "Dropping", IsDropping); + IsInvisible = ini.Get_Bool(Name(), "Inviso", IsInvisible); + IsProximityArmed = ini.Get_Bool(Name(), "Proximity", IsProximityArmed); + IsFlameEquipped = ini.Get_Bool(Name(), "Animates", IsFlameEquipped); + IsFueled = ini.Get_Bool(Name(), "Ranged", IsFueled); + IsInaccurate = ini.Get_Bool(Name(), "Inaccuate", IsInaccurate); + IsAntiAircraft = ini.Get_Bool(Name(), "AA", IsAntiAircraft); + IsAntiGround = ini.Get_Bool(Name(), "AG", IsAntiGround); + IsAntiSub = ini.Get_Bool(Name(), "ASW", IsAntiSub); + IsDegenerate = ini.Get_Bool(Name(), "Degenerates", IsDegenerate); + IsSubSurface = ini.Get_Bool(Name(), "UnderWater", IsSubSurface); + IsParachuted = ini.Get_Bool(Name(), "Parachuted", IsParachuted); + IsFaceless = !ini.Get_Bool(Name(), "Rotates", !IsFaceless); + IsTranslucent = ini.Get_Bool(Name(), "Translucent", IsTranslucent); + IsGigundo = ini.Get_Bool(Name(), "Gigundo", IsGigundo); + ini.Get_String(Name(), "Image", "none", GraphicName, sizeof(GraphicName)); + return(true); + } + return(false); +} diff --git a/CODE/BDATA.CPP b/CODE/BDATA.CPP new file mode 100644 index 0000000..e6b715c --- /dev/null +++ b/CODE/BDATA.CPP @@ -0,0 +1,3834 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BDATA.CPP 2 3/03/97 10:37p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * BuildingTypeClass::Display -- Renders a generic view of building. * + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building * + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obj* + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. * + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * BuildingTypeClass::Width -- Determines width of building in icons. * + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap* + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#define FATSHIP + +#define MCW MAP_CELL_W + +#define XYCELL(x,y) (y*MAP_CELL_W+x) +static short const ExitPyle[] = { + XYCELL(1,2), + XYCELL(2,2), + XYCELL(0,2), + XYCELL(-1,2), + XYCELL(-1,-1), + XYCELL(0,-1), + XYCELL(1,-1), + XYCELL(2,-1), + XYCELL(2,-1), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; + +static short const ExitSub[] = { + XYCELL( 0, 2), + XYCELL( 2, 2), + XYCELL(-1, 2), + XYCELL( 1, 2), + XYCELL( 3, 2) +}; + +static short const ExitWeap[] = { + XYCELL(1,2), + XYCELL(-1,3), + XYCELL(0,3), + XYCELL(1,3), + XYCELL(-2,3), + XYCELL(2,3), + REFRESH_EOL +}; + +static short const ComList[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List000111111[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List0010[] = {MCW, REFRESH_EOL}; +static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List010111100[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), REFRESH_EOL}; +static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1000[] = {0, REFRESH_EOL}; +static short const List101000011[] = {0, 2, (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List1100[] = {0, 1, REFRESH_EOL}; +static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL}; +static short const List11[] = {0, 1, REFRESH_EOL}; +static short const List12[] = {MCW, REFRESH_EOL}; +static short const List1[] = {0, REFRESH_EOL}; +static short const List21[] = {0, 1, REFRESH_EOL}; +static short const List22[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL}; +static short const List22_1100[] = {0, 1, REFRESH_EOL}; +static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL}; +static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL}; +//static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const ListWeap[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL}; +static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL}; +#ifdef FATSHIP +static short const ListSPen[] = {0, 1, 2, MCW, MCW+1, MCW+2, MCW+MCW, MCW+MCW+1, MCW+MCW+2, REFRESH_EOL}; +static short const OListSPen[] = {REFRESH_EOL}; +#else +static short const ListSPen[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const OListSPen[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +#endif +static short const OListWestwood[] = {0, MCW, REFRESH_EOL}; +static short const StoreList[] = {0, REFRESH_EOL}; + +static short const ListFactory[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; + +static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +static short const OListWeap[] = {REFRESH_EOL}; +static short const OComList[] = {1, REFRESH_EOL}; +static short const OList12[] = {0, REFRESH_EOL}; +static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL}; + + +/*************************************************************************** +*/ +static BuildingTypeClass const ClassBarrel( + STRUCT_BARREL, + TXT_BARREL, // NAME: Short name of the structure. + "BARL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarrel3( + STRUCT_BARREL3, + TXT_BARREL, // NAME: Short name of the structure. + "BRL3", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAVMine( + STRUCT_AVMINE, + TXT_AVMINE, // NAME: Short name of the structure. + "MINV", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAPMine( + STRUCT_APMINE, + TXT_APMINE, // NAME: Short name of the structure. + "MINP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassIronCurtain( + STRUCT_IRON_CURTAIN, + TXT_IRON_CURTAIN, // NAME: Short name of the structure. + "IRON", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassForwardCom( + STRUCT_FORWARD_COM, + TXT_FORWARD_COM, // NAME: Short name of the structure. + "FCOM", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedTech( + STRUCT_ADVANCED_TECH, + TXT_ADVANCED_TECH, // NAME: Short name of the structure. + "ATEK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassChronosphere( + STRUCT_CHRONOSPHERE, + TXT_CHRONOSPHERE, // NAME: Short name of the structure. + "PDOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassWeapon( + STRUCT_WEAP, + TXT_WEAPON_FACTORY, // NAME: Short name of the structure. + "WEAP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XY_Coord(CELL_LEPTON_W+(CELL_LEPTON_W/2), CELL_LEPTON_H), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassShipYard( + STRUCT_SHIP_YARD, + TXT_SHIP_YARD, // NAME: Short name of the structure. + "SYRD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSubPen( + STRUCT_SUB_PEN, + TXT_SUB_PEN, // NAME: Short name of the structure. + "SPEN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPillbox( + STRUCT_PILLBOX, + TXT_PILLBOX, // NAME: Short name of the structure. + "PBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCamoPillbox( + STRUCT_CAMOPILLBOX, + TXT_CAMOPILLBOX, // NAME: Short name of the structure. + "HBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTesla( + STRUCT_TESLA, + TXT_TESLA, // NAME: Short name of the structure. + "TSLA", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x00C8, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTurret( + STRUCT_TURRET, + TXT_TURRET, // NAME: Short name of the structure. + "GUN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + (DirType)208, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAAGun( + STRUCT_AAGUN, + TXT_AAGUN, // NAME: Short name of the structure. + "AGUN", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_NE, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFlameTurret( + STRUCT_FLAME_TURRET, + TXT_FLAME_TURRET, // NAME: Short name of the structure. + "FTUR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassConst( + STRUCT_CONST, + TXT_CONST_YARD, // NAME: Short name of the structure. + "FACT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_BUILDINGTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeConst( + STRUCT_FAKECONST, + TXT_FAKE_CONST, // NAME: Short name of the structure. + "FACF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeWeapon( + STRUCT_FAKEWEAP, + TXT_FAKE_WEAP, // NAME: Short name of the structure. + "WEAF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRefinery( + STRUCT_REFINERY, + TXT_REFINERY, // NAME: Short name of the structure. + "PROC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List010111100, // OCCUPYLIST: List of active foundation squares. + (short const *)List101000011 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassStorage( + STRUCT_STORAGE, + TXT_STORAGE, // NAME: Short name of the structure. + "SILO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)StoreList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHelipad( + STRUCT_HELIPAD, + TXT_HELIPAD, // NAME: Short name of the structure. + "HPAD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCommand( + STRUCT_RADAR, + TXT_COMMAND, // NAME: Short name of the structure. + "DOME", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassGapGenerator( + STRUCT_GAP, + TXT_GAP_GENERATOR, // NAME: Short name of the structure. + "GAP", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0010, // OCCUPYLIST: List of active foundation squares. + (short const *)List1 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSAM( + STRUCT_SAM, + TXT_SAM, // NAME: Short name of the structure. + "SAM", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassMissileSilo( + STRUCT_MSLO, + TXT_MSLO, // NAME: Short name of the structure. + "MSLO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAirStrip( + STRUCT_AIRSTRIP, + TXT_AIRSTRIP, // NAME: Short name of the structure. + "AFLD", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPower( + STRUCT_POWER, + TXT_POWER, // NAME: Short name of the structure. + "POWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedPower( + STRUCT_ADVANCED_POWER, + TXT_ADVANCED_POWER, // NAME: Short name of the structure. + "APWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSovietTech( + STRUCT_SOVIET_TECH, + TXT_SOVIET_TECH, // NAME: Short name of the structure. + "STEK", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHospital( + STRUCT_HOSPITAL, + TXT_HOSPITAL, // NAME: Short name of the structure. + "HOSP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBioLab( + STRUCT_BIO_LAB, + TXT_BIO_LAB, // NAME: Short name of the structure. + "BIO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarracks( + STRUCT_BARRACKS, + TXT_BARRACKS, // NAME: Short name of the structure. + "BARR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. +// XYP_COORD(24,47), // Exit point for produced units. + XYP_COORD(18,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTent( + STRUCT_TENT, + TXT_BARRACKS, // NAME: Short name of the structure. + "TENT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassKennel( + STRUCT_KENNEL, + TXT_KENNEL, // NAME: Short name of the structure. + "KENN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(8,16), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. +// (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeShipYard( + STRUCT_FAKE_YARD, + TXT_FAKE_YARD, // NAME: Short name of the structure. + "SYRF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeSubPen( + STRUCT_FAKE_PEN, + TXT_FAKE_PEN, // NAME: Short name of the structure. + "SPEF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeCommand( + STRUCT_FAKE_RADAR, + TXT_FAKE_RADAR, // NAME: Short name of the structure. + "DOMF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRepair( + STRUCT_REPAIR, + TXT_FIX_IT, // NAME: Short name of the structure. + "FIX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFix, // OCCUPYLIST: List of active foundation squares. + (short const *)OListFix // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV01( + STRUCT_V01, + TXT_CIV1, // NAME: Short name of the structure. + "V01", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV02( + STRUCT_V02, + TXT_CIV2, // NAME: Short name of the structure. + "V02", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV03( + STRUCT_V03, + TXT_CIV3, // NAME: Short name of the structure. + "V03", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV04( + STRUCT_V04, + TXT_CIV4, // NAME: Short name of the structure. + "V04", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV05( + STRUCT_V05, + TXT_CIV5, // NAME: Short name of the structure. + "V05", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV06( + STRUCT_V06, + TXT_CIV6, // NAME: Short name of the structure. + "V06", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV07( + STRUCT_V07, + TXT_CIV7, // NAME: Short name of the structure. + "V07", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV08( + STRUCT_V08, + TXT_CIV8, // NAME: Short name of the structure. + "V08", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV09( + STRUCT_V09, + TXT_CIV9, // NAME: Short name of the structure. + "V09", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV10( + STRUCT_V10, + TXT_CIV10, // NAME: Short name of the structure. + "V10", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV11( + STRUCT_V11, + TXT_CIV11, // NAME: Short name of the structure. + "V11", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV12( + STRUCT_V12, + TXT_CIV12, // NAME: Short name of the structure. + "V12", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV13( + STRUCT_V13, + TXT_CIV13, // NAME: Short name of the structure. + "V13", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV14( + STRUCT_V14, + TXT_CIV14, // NAME: Short name of the structure. + "V14", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV15( + STRUCT_V15, + TXT_CIV15, // NAME: Short name of the structure. + "V15", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV16( + STRUCT_V16, + TXT_CIV16, // NAME: Short name of the structure. + "V16", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV17( + STRUCT_V17, + TXT_CIV17, // NAME: Short name of the structure. + "V17", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV18( + STRUCT_V18, + TXT_CIV18, // NAME: Short name of the structure. + "V18", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV19( + STRUCT_PUMP, + TXT_PUMP, // NAME: Short name of the structure. + "V19", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV20( + STRUCT_V20, + TXT_CIV20, // NAME: Short name of the structure. + "V20", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV21( + STRUCT_V21, + TXT_CIV21, // NAME: Short name of the structure. + "V21", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1101, // OCCUPYLIST: List of active foundation squares. + (short const *)List0010 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV22( + STRUCT_V22, + TXT_CIV22, // NAME: Short name of the structure. + "V22", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV23( + STRUCT_V23, + TXT_CIV23, // NAME: Short name of the structure. + "V23", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV24( + STRUCT_V24, + TXT_CIV24, // NAME: Short name of the structure. + "V24", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV25( + STRUCT_V25, + TXT_CIV25, // NAME: Short name of the structure. + "V25", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV26( + STRUCT_V26, + TXT_CIV26, // NAME: Short name of the structure. + "V26", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV27( + STRUCT_V27, + TXT_CIV27, // NAME: Short name of the structure. + "V27", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV28( + STRUCT_V28, + TXT_CIV28, // NAME: Short name of the structure. + "V28", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV29( + STRUCT_V29, + TXT_CIV29, // NAME: Short name of the structure. + "V29", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV30( + STRUCT_V30, + TXT_CIV30, // NAME: Short name of the structure. + "V30", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV31( + STRUCT_V31, + TXT_CIV31, // NAME: Short name of the structure. + "V31", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV32( + STRUCT_V32, + TXT_CIV32, // NAME: Short name of the structure. + "V32", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV33( + STRUCT_V33, + TXT_CIV33, // NAME: Short name of the structure. + "V33", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV34( + STRUCT_V34, + TXT_CIV34, // NAME: Short name of the structure. + "V34", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV35( + STRUCT_V35, + TXT_CIV35, // NAME: Short name of the structure. + "V35", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV36( + STRUCT_V36, + TXT_CIV36, // NAME: Short name of the structure. + "V36", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassV37( + STRUCT_V37, + TXT_CIV37, // NAME: Short name of the structure. + "V37", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_42, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassMission( + STRUCT_MISSION, + TXT_CIVMISS, // NAME: Short name of the structure. + "MISS", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +// Sandbag wall +static BuildingTypeClass const Sandbag( + STRUCT_SANDBAG_WALL, + TXT_SANDBAG_WALL, // NAME: Short name of the structure. + "SBAG", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Cyclone fence +static BuildingTypeClass const Cyclone( + STRUCT_CYCLONE_WALL, + TXT_CYCLONE_WALL, // NAME: Short name of the structure. + "CYCL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Brick wall +static BuildingTypeClass const Brick( + STRUCT_BRICK_WALL, + TXT_BRICK_WALL, // NAME: Short name of the structure. + "BRIK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Barbwire wall +static BuildingTypeClass const Barbwire( + STRUCT_BARBWIRE_WALL, + TXT_BARBWIRE_WALL, // NAME: Short name of the structure. + "BARB", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Wood wall +static BuildingTypeClass const Wood( + STRUCT_WOOD_WALL, + TXT_WOOD_WALL, // NAME: Short name of the structure. + "WOOD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const Fence( + STRUCT_FENCE, + TXT_FENCE, // NAME: Short name of the structure. + "FENC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + + +#ifdef FIXIT_ANTS +static BuildingTypeClass const ClassQueen( + STRUCT_QUEEN, + TXT_NONE, // NAME: Short name of the structure. + "QUEE", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva1( + STRUCT_LARVA1, + TXT_NONE, // NAME: Short name of the structure. + "LAR1", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva2( + STRUCT_LARVA2, + TXT_NONE, // NAME: Short name of the structure. + "LAR2", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +#endif +void const * BuildingTypeClass::WarFactoryOverlay; +void const * LightningShapes; + + +/*********************************************************************************************** + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * * + * This is the constructor used to create the building types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass::BuildingTypeClass( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap) : + TechnoTypeClass(RTTI_BUILDINGTYPE, + int(type), + name, + ininame, + remap, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + is_nominal, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + false, + is_theater, + is_turret_equipped, + is_remappable, + true, + (is_turret_equipped ? 32 : 1), + SPEED_NONE), + IsBase(true), + IsFake(is_fake), + IsBibbed(false), + IsWall(is_wall), + IsSimpleDamage(is_simpledamage), + IsCaptureable(false), + IsRegulated(is_regulated), + IsPowered(false), + IsUnsellable(false), + FoundationFace(foundation), + Adjacent(1), + ToBuild(tobuild), + ExitCoordinate(exitpoint), + ExitList(exitlist), + Type(type), + StartFace(sframe), + Capacity(0), + Power(0), + Drain(0), + Size(size), + OccupyList(sizelist), + OverlapList(overlap), + BuildupData(0) +{ + + Anims[BSTATE_CONSTRUCTION].Start = 0; + Anims[BSTATE_CONSTRUCTION].Count = 1; + Anims[BSTATE_CONSTRUCTION].Rate = 0; + + Anims[BSTATE_IDLE].Start = 0; + Anims[BSTATE_IDLE].Count = 1; + Anims[BSTATE_IDLE].Rate = 0; + + Anims[BSTATE_ACTIVE].Start = 0; + Anims[BSTATE_ACTIVE].Count = 1; + Anims[BSTATE_ACTIVE].Rate = 0; + + Anims[BSTATE_AUX1].Start = 0; + Anims[BSTATE_AUX1].Count = 1; + Anims[BSTATE_AUX1].Rate = 0; + + Anims[BSTATE_AUX2].Start = 0; + Anims[BSTATE_AUX2].Count = 1; + Anims[BSTATE_AUX2].Rate = 0; +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap. * + * * + * This routine will allocate a building type object from the special heap used just for * + * allocation of object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated object. If the allocation could not * + * succeed, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BuildingTypeClass::operator new(size_t) +{ + return(BuildingTypes.Alloc()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap. * + * * + * This will delete a previously allocated building type object. The memory is returned * + * to the special heap that is used for that purpose. * + * * + * INPUT: ptr -- Pointer to the building type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::operator delete(void * ptr) +{ + BuildingTypes.Free((BuildingTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obje * + * * + * This routine performs the necessary heap initializations. Since we know exactly what * + * building type objects will be needed, they are pre-allocated at this time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Heap(void) +{ + /* + ** These building type class objects must be allocated in the exact order that they + ** are specified in the StructType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BuildingTypeClass(ClassAdvancedTech); // STRUCT_ADVANCED_TECH + new BuildingTypeClass(ClassIronCurtain); // STRUCT_IRON_CURTAIN + new BuildingTypeClass(ClassWeapon); // STRUCT_WEAP + new BuildingTypeClass(ClassChronosphere); // STRUCT_CHRONOSPHERE + new BuildingTypeClass(ClassPillbox); // STRUCT_PILLBOX + new BuildingTypeClass(ClassCamoPillbox); // STRUCT_CAMOPILLBOX + new BuildingTypeClass(ClassCommand); // STRUCT_RADAR + new BuildingTypeClass(ClassGapGenerator); // STRUCT_GAP + new BuildingTypeClass(ClassTurret); // STRUCT_TURRET + new BuildingTypeClass(ClassAAGun); // STRUCT_AAGUN + new BuildingTypeClass(ClassFlameTurret); // STRUCT_FLAME_TURRET + new BuildingTypeClass(ClassConst); // STRUCT_CONST + new BuildingTypeClass(ClassRefinery); // STRUCT_REFINERY + new BuildingTypeClass(ClassStorage); // STRUCT_STORAGE + new BuildingTypeClass(ClassHelipad); // STRUCT_HELIPAD + new BuildingTypeClass(ClassSAM); // STRUCT_SAM + new BuildingTypeClass(ClassAirStrip); // STRUCT_AIRSTRIP + new BuildingTypeClass(ClassPower); // STRUCT_POWER + new BuildingTypeClass(ClassAdvancedPower);// STRUCT_ADVANCED_POWER + new BuildingTypeClass(ClassSovietTech); // STRUCT_SOVIET_TECH + new BuildingTypeClass(ClassHospital); // STRUCT_HOSPITAL + new BuildingTypeClass(ClassBarracks); // STRUCT_BARRACKS + new BuildingTypeClass(ClassTent); // STRUCT_TENT + new BuildingTypeClass(ClassKennel); // STRUCT_KENNEL + new BuildingTypeClass(ClassRepair); // STRUCT_REPAIR + new BuildingTypeClass(ClassBioLab); // STRUCT_BIO_LAB + new BuildingTypeClass(ClassMission); // STRUCT_MISSION + new BuildingTypeClass(ClassShipYard); // STRUCT_SHIP_YARD + new BuildingTypeClass(ClassSubPen); // STRUCT_SUB_PEN + new BuildingTypeClass(ClassMissileSilo); // STRUCT_MSLO + new BuildingTypeClass(ClassForwardCom); // STRUCT_FORWARD_COM + new BuildingTypeClass(ClassTesla); // STRUCT_TESLA + new BuildingTypeClass(ClassFakeWeapon); // STRUCT_FAKEWEAP + new BuildingTypeClass(ClassFakeConst); // STRUCT_FAKECONST + new BuildingTypeClass(ClassFakeShipYard); // STRUCT_FAKE_YARD + new BuildingTypeClass(ClassFakeSubPen); // STRUCT_FAKE_PEN + new BuildingTypeClass(ClassFakeCommand); // STRUCT_FAKE_RADAR + new BuildingTypeClass(Sandbag); // STRUCT_SANDBAG_WALL + new BuildingTypeClass(Cyclone); // STRUCT_CYCLONE_WALL + new BuildingTypeClass(Brick); // STRUCT_BRICK_WALL + new BuildingTypeClass(Barbwire); // STRUCT_BARBWIRE_WALL + new BuildingTypeClass(Wood); // STRUCT_WOOD_WALL + new BuildingTypeClass(Fence); // STRUCT_FENCE + new BuildingTypeClass(ClassAVMine); // STRUCT_AVMINE + new BuildingTypeClass(ClassAPMine); // STRUCT_APMINE + new BuildingTypeClass(ClassV01); // STRUCT_V1 + new BuildingTypeClass(ClassV02); // STRUCT_V2 + new BuildingTypeClass(ClassV03); // STRUCT_V3 + new BuildingTypeClass(ClassV04); // STRUCT_V4 + new BuildingTypeClass(ClassV05); // STRUCT_V5 + new BuildingTypeClass(ClassV06); // STRUCT_V6 + new BuildingTypeClass(ClassV07); // STRUCT_V7 + new BuildingTypeClass(ClassV08); // STRUCT_V8 + new BuildingTypeClass(ClassV09); // STRUCT_V9 + new BuildingTypeClass(ClassV10); // STRUCT_V10 + new BuildingTypeClass(ClassV11); // STRUCT_V11 + new BuildingTypeClass(ClassV12); // STRUCT_V12 + new BuildingTypeClass(ClassV13); // STRUCT_V13 + new BuildingTypeClass(ClassV14); // STRUCT_V14 + new BuildingTypeClass(ClassV15); // STRUCT_V15 + new BuildingTypeClass(ClassV16); // STRUCT_V16 + new BuildingTypeClass(ClassV17); // STRUCT_V17 + new BuildingTypeClass(ClassV18); // STRUCT_V18 + new BuildingTypeClass(ClassV19); // STRUCT_PUMP + new BuildingTypeClass(ClassV20); // STRUCT_V20 + new BuildingTypeClass(ClassV21); // STRUCT_V21 + new BuildingTypeClass(ClassV22); // STRUCT_V22 + new BuildingTypeClass(ClassV23); // STRUCT_V23 + new BuildingTypeClass(ClassV24); // STRUCT_V24 + new BuildingTypeClass(ClassV25); // STRUCT_V25 + new BuildingTypeClass(ClassV26); // STRUCT_V26 + new BuildingTypeClass(ClassV27); // STRUCT_V27 + new BuildingTypeClass(ClassV28); // STRUCT_V28 + new BuildingTypeClass(ClassV29); // STRUCT_V29 + new BuildingTypeClass(ClassV30); // STRUCT_V30 + new BuildingTypeClass(ClassV31); // STRUCT_V31 + new BuildingTypeClass(ClassV32); // STRUCT_V32 + new BuildingTypeClass(ClassV33); // STRUCT_V33 + new BuildingTypeClass(ClassV34); // STRUCT_V34 + new BuildingTypeClass(ClassV35); // STRUCT_V35 + new BuildingTypeClass(ClassV36); // STRUCT_V36 + new BuildingTypeClass(ClassV37); // STRUCT_V37 + new BuildingTypeClass(ClassBarrel); // STRUCT_BARREL + new BuildingTypeClass(ClassBarrel3); // STRUCT_BARREL3 + +#ifdef FIXIT_ANTS + new BuildingTypeClass(ClassQueen); // STRUCT_QUEEN + new BuildingTypeClass(ClassLarva1); // STRUCT_LARVA1 + new BuildingTypeClass(ClassLarva2); // STRUCT_LARVA2 +#endif + +} + + +/*********************************************************************************************** + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * * + * This routine is used to do the one time action necessary to handle building type class * + * objects. This entails loading of the building shapes and the brain file used by * + * buildings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should only be called ONCE. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/11/1994 JLB : Updated construction time and frame count logic. * + *=============================================================================================*/ +void BuildingTypeClass::One_Time(void) +{ + static const struct { + StructType Class; // Building class number. + BStateType Stage; // Animation sequence to assign animation range to. + int Start; // Starting frame number. + int Length; // Number of frames (-1 means use all frames). + int Rate; // Rate of animation. + } _anims[] = { + {STRUCT_CHRONOSPHERE, BSTATE_IDLE, 0, 4, 3}, // idling + {STRUCT_CHRONOSPHERE, BSTATE_ACTIVE, 4, 16,3}, // charging up and activating + {STRUCT_MSLO, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_MSLO, BSTATE_ACTIVE, 0, 5, 2}, // door opening + {STRUCT_MSLO, BSTATE_AUX1, 4, 1, 0}, // door held open + {STRUCT_MSLO, BSTATE_AUX2, 5, 3, 2}, // door closing + {STRUCT_CAMOPILLBOX, BSTATE_ACTIVE, 0, 2, 1}, + {STRUCT_GAP, BSTATE_IDLE, 0, 32,3}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_AIRSTRIP, BSTATE_AUX1, 0, 8, 3}, + {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3}, + {STRUCT_TENT, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_TENT, BSTATE_IDLE, 0, 10,3}, +#ifdef FIXIT_ANTS + {STRUCT_QUEEN, BSTATE_IDLE, 0, 10,3}, +#endif + {STRUCT_CONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_FAKECONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4}, + {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4}, + {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2}, + {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_V20, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V21, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V22, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V23, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_IRON_CURTAIN, BSTATE_ACTIVE, 0, 11,3}, + {STRUCT_TESLA, BSTATE_ACTIVE, 0, 10,2}, + }; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + BuildingTypeClass const & building = As_Reference(sindex); + /* + ** Fetch the sidebar cameo image for this building. + */ + if (building.Level != -1) { +// if (building.IsBuildable) { + sprintf(buffer, "%sICON", building.Graphic_Name()); + + if (building.IsFake) { + buffer[3] = 'F'; + } + + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)building.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch the construction animation for this building. + */ + sprintf(buffer, "%sMAKE", building.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + void const * dataptr; + dataptr = MFCD::Retrieve(fullname); + ((void const *&)building.BuildupData) = dataptr; + if (dataptr != NULL) { + int timedelay = 1; + int count = Get_Build_Frame_Count(dataptr); + if (count > 0) { + timedelay = (Rule.BuildupTime * TICKS_PER_MINUTE) / count; + } + building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + + /* + ** Fetch the normal game shape for this building. + */ + _makepath(fullname, NULL, NULL, building.Graphic_Name(), ".SHP"); + ((void const *&)building.ImageData) = MFCD::Retrieve(fullname); + } + + // Try to load weap2.shp and tesla coil's lightning shapes + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP"); + WarFactoryOverlay = MFCD::Retrieve(fullname); + _makepath(fullname, NULL, NULL, (char const *)"LITNING",".SHP"); + LightningShapes = MFCD::Retrieve(fullname); + + /* + ** Install all the special animation sequences for the different building types. + */ + for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) { + As_Reference(_anims[index].Class).Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate); + } + +} + + +/*********************************************************************************************** + * Struct_From_Name -- Find BData structure from its name. * + * * + * This routine will convert an ASCII name for a building class into * + * the actual building class it represents. * + * * + * INPUT: name -- ASCII representation of a building class. * + * * + * OUTPUT: Returns with the actual building class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +StructType BuildingTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (StructType classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(STRUCT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * BuildingTypeClass::Display -- Renders a generic view of building. * + * * + * This routine is used to display a generic representation of the * + * building. Typical use of this occurs with the scenario editor. * + * * + * INPUT: x,y -- Coordinate to display the building (centered). * + * * + * window -- The window the building should be rendered * + * relative to. * + * * + * house -- The house color to use for the building. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + IsTheaterShape = IsTheater; + ptr = Get_Image_Data(); + } + CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a * + * * + * This routine is used to prepare the scenario editor for the addition * + * of a building object to the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface routines. * + *=============================================================================================*/ +void BuildingTypeClass::Prep_For_Add(void) +{ + for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * * + * This routine is used by the scenario editor to create and place buildings on the map. * + * * + * INPUT: cell -- The cell that the building is to be placed upon. * + * * + * house -- The owner of the building. * + * * + * OUTPUT: bool; Was the building successfully created and placed on the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + BuildingClass * ptr; + + ptr = new BuildingClass(Type, house); + if (ptr != NULL) { + return(ptr->Unlimbo(Cell_Coord(cell), DIR_N)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * * + * This routine will create a building object of this type. The building object is in a * + * limbo state. It is presumed that the building object will be unlimboed at the correct * + * place and time. Typical use is when the building is created in a factory situation * + * and will be placed on the map when construction completes. * + * * + * INPUT: house -- Pointer to the house that is to be the owner of the building. * + * * + * OUTPUT: Returns with a pointer to the building. If the building could not be created * + * then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const +{ + HousesType htype = HOUSE_NEUTRAL; + if (house != NULL) { + htype = house->Class->House; + } + return(new BuildingClass(Type, htype)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * * + * This routine will initialize one animation control element for a * + * specified building. This modifies a "const" class and thus must * + * perform some strategic casting to get away with this. * + * * + * INPUT: state -- The animation state to apply these data values to. * + * * + * start -- Starting frame for the building's animation. * + * * + * count -- The number of frames in this animation. * + * * + * rate -- The countdown timer between animation frames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/18/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const +{ + ((int &)Anims[state].Start) = start; + ((int &)Anims[state].Count) = count; + ((int &)Anims[state].Rate) = rate; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * * + * This routine is used to perform any initialization that is custom per theater. * + * Typically, this is fetching the building shape data for those building types that have * + * theater specific art. * + * * + * INPUT: theater -- The theater to base this initialization on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const * classptr = &As_Reference(sindex); + + if (classptr->IsTheater) { + _makepath(fullname, NULL, NULL, classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->ImageData) = MFCD::Retrieve(fullname); + + /* + ** Buildup data is probably theater specific as well. Fetch a pointer to the + ** data at this time as well. + */ + sprintf(fullname, "%sMAKE.%s", classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->BuildupData) = MFCD::Retrieve(fullname); + if (classptr->BuildupData) { + int timedelay = 1; + int count = Get_Build_Frame_Count(classptr->BuildupData); + if (count != 0) { + timedelay = (5 * TICKS_PER_SECOND) / count; + } + classptr->Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * * + * This routine will fetch the dimensions of the building (in pixels). These dimensions are * + * used to render the selection rectangle and the health bar. * + * * + * INPUT: width -- Reference to the pixel width (to be filled in). * + * * + * height -- Reference to the pixel height (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Dimensions(int &width, int &height) const +{ + width = Width() * ICON_PIXEL_W; + width -= (width/5); + height = Height() * ICON_PIXEL_H; + height -= (height/5); +} + + +/*********************************************************************************************** + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * * + * This routine will fetch a reference to the BuildingTypeClass as indicated by the * + * building type number specified. * + * * + * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. * + * * + * OUTPUT: Returns with a reference to the building type class as indicated by the * + * parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass & BuildingTypeClass::As_Reference(StructType type) +{ + return(*BuildingTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * * + * Use this routine to fetch the occupy list pointer for the building. The occupy list is * + * used to determine what cells the building occupies and thus precludes other buildings * + * or objects from using. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells * + * this building occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Occupy_List(bool placement) const +{ + SmudgeType bib = SMUDGE_NONE; + CELL cell=0; + + if (placement && Bib_And_Offset(bib, cell)) { + + SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib); + static short _list[25]; + short * dest = &_list[0]; + + /* + ** Copy the bib overlap list into the working buffer. + */ + short const * src = smudge.Occupy_List(); + while (*src != REFRESH_EOL) { + *dest++ = (*src++) + cell; + } + + /* + ** Append the building occupy list to this working buffer. + */ + src = OccupyList; + while (src && *src != REFRESH_EOL) { + *dest++ = *src++; + } + *dest = REFRESH_EOL; + + return(&_list[0]); + } + + if (OccupyList != NULL) { + return(OccupyList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * * + * This routine will fetch the overlap list for the building. The overlap list is used * + * to determine what cells the building's graphics cover, but is not considered to occupy * + * for movement purposes. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the * + * cells that this building overlaps. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Overlap_List(void) const +{ + if (OverlapList != NULL) { + return(OverlapList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Width -- Determines width of building in icons. * + * * + * Use this routine to determine the width of the building type in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building width in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Width(void) const +{ + static int width[BSIZE_COUNT] = { + 1, + 2, + 1, + 2, + 2, + 3, + 3, + 4, + 5 + }; + return(width[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * * + * Use this routine to find the height of the building in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building height in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Height(bool bib) const +{ + static int height[BSIZE_COUNT] = { + 1, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 5 + }; + return(height[Size] + ((bib && IsBibbed) ? 1 : 0)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * * + * This routine is used to determine what (if any) bib should be used for this building * + * and also the cell offset for the upper left corner of the bib smudge type. * + * * + * INPUT: bib -- Reference to the bib that should be used for this building. * + * * + * cell -- The cell offset for the upper left corner of the bib. This offset is * + * relative to the upper left corner of the building. * + * * + * OUTPUT: Is a bib required for this building? If the result is true, then the correct * + * bib and cell offset will be filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const +{ + bib = SMUDGE_NONE; + + if (IsBibbed) { + switch (Width()) { + case 2: + bib = SMUDGE_BIB3; + break; + + case 3: + bib = SMUDGE_BIB2; + break; + + case 4: + bib = SMUDGE_BIB1; + break; + + default: + bib = SMUDGE_NONE; + break; + } + + /* + ** Adjust the bib position for special buildings that have the bib as part + ** of the building art itself. + */ + if (bib != SMUDGE_NONE) { + cell += ((Height()-1)*MAP_CELL_W); + } + } + return(bib != SMUDGE_NONE); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * * + * Use this routine to determine the maximum number of pips to display on this building * + * when it is rendered. Typically, this is the tiberium capacity divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this building when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Max_Pips(void) const +{ + int maxpips = (Width() * ICON_PIXEL_W) / 4; + return(Bound((int)(Capacity/100), 0, maxpips)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * * + * This routine is used to fetch the real raw base cost of the building. The raw cost * + * is the cost of the building less any free unit that would come with the building * + * if it were built in the normal fashion. Specifically, the helicopter cost is subtracted * + * from the helipad and the harvester cost is subtracted from the refinery. This cost * + * is used for refunding. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the raw (base) cost to build the building of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Raw_Cost(void) const +{ + int cost = TechnoTypeClass::Raw_Cost(); + + if (Type == STRUCT_HELIPAD && !Rule.IsSeparate) { + cost -= (AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost)/2; + } + if (Type == STRUCT_REFINERY) { + cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost; + } + return(cost); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * * + * This routine will fetch the cost to build the building of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Cost_Of(void) const +{ + if (Rule.IsSeparate && Type == STRUCT_HELIPAD) { + return(Raw_Cost()); + } + return(TechnoTypeClass::Cost_Of()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building t * + * * + * This routine is called when a clear space for placement is desired at the cell location * + * specified. Typical use of this routine is by the computer when it wants to build up * + * its base. * + * * + * INPUT: cell -- The cell that the building of this type would like to be placed down at. * + * * + * house -- Pointer to the house that want to clear the foundation zone. * + * * + * OUTPUT: Placement is temporarily blocked, please try again later? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Flush_For_Placement(CELL cell, HouseClass * house) const +{ + bool again = false; + if (cell > 0) { + short const * list = Occupy_List(true); + + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + + if (Map.In_Radar(newcell)) { + TechnoClass * occupier = Map[newcell].Cell_Techno(); + if (occupier != NULL) { + again = true; + if (occupier->House->Is_Ally(house) && occupier->Is_Foot() && !Target_Legal(((FootClass *)occupier)->NavCom)) { + Map[newcell].Incoming(0, true); + } else { +// Base_Is_Attacked(occupier); + } + } + } + } + } + return(again); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * * + * This routine will fetch the building type class data from the INI database file. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the building entry found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + Speed = ini.Get_Bool(Name(), "WaterBound", (Speed == SPEED_FLOAT)) ? SPEED_FLOAT : SPEED_NONE; + Capacity = ini.Get_Int(Name(), "Storage", Capacity); + Adjacent = ini.Get_Int(Name(), "Adjacent", Adjacent); + IsCaptureable = ini.Get_Bool(Name(), "Capturable", IsCaptureable); + IsPowered = ini.Get_Bool(Name(), "Powered", IsPowered); + IsBibbed = ini.Get_Bool(Name(), "Bib", IsBibbed); + IsUnsellable = ini.Get_Bool(Name(), "Unsellable", IsUnsellable); + IsBase = ini.Get_Bool(Name(), "BaseNormal", IsBase); + Power = ini.Get_Int(Name(), "Power", (Power > 0) ? Power : -Drain); + if (Power < 0) { + Drain = -Power; + Power = 0; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * * + * This routine will adjust the specified coordinate so that it will be legal for assignment* + * to this building. All buildings are given a coordinate that is in the upper left corner * + * of a cell. This routine will drop the fractional component of the coordinate. * + * * + * INPUT: coord -- The coordinate to fixup into a legal to assign value. * + * * + * OUTPUT: Returns with a coordinate that can be assigned to the building. * + * * + * WARNINGS: The coordinate is not examined to see if the cell is legal for placing the * + * building. It merely adjusts the coordinate so that is legal at first glance. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * * + * This routine will return the displayable given name for this building type. Normally, * + * this is the official name as well, however in the case of civilian buildings, the * + * name will just be "Civilian Building" unless special options are in place. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the text number of the building type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Full_Name(void) const +{ + if (Debug_Map || Rule.IsNamed || *this < STRUCT_V01 || *this > STRUCT_V37) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN_BUILDING); +} diff --git a/CODE/BENCH.CPP b/CODE/BENCH.CPP new file mode 100644 index 0000000..5ae69ad --- /dev/null +++ b/CODE/BENCH.CPP @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BENCH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BENCH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Benchmark::Begin -- Start the benchmark operation. * + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * Benchmark::End -- Mark the end of a benchmarked operation * + * Benchmark::Reset -- Clear out the benchmark statistics. * + * Benchmark::Value -- Fetch the current average benchmark time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "bench.h" +#include "mpu.h" + + +/*********************************************************************************************** + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * * + * This will construct the benchmark object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +Benchmark::Benchmark(void) : + Average(0), + Counter(0), + TotalCount(0) +{ +} + + +/*********************************************************************************************** + * Benchmark::Reset -- Clear out the benchmark statistics. * + * * + * Use this routine to clear out all the accumulated statistics within this benchmark * + * object. The object is set just as if it was freshly constructed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Reset(void) +{ + Average = 0; + Counter = 0; + TotalCount = 0; +} + + +/*********************************************************************************************** + * Benchmark::Begin -- Start the benchmark operation. * + * * + * Call this routine before the operation to be benchmarked is begun. The corresponding * + * End() function must be called after the operation has completed. * + * * + * INPUT: reset -- Should the entire benchmark object be reset at this time as well? * + * * + * OUTPUT: none * + * * + * WARNINGS: The Begin() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Begin(bool reset) +{ + if (reset) Reset(); + Clock = 0; +} + + +/*********************************************************************************************** + * Benchmark::End -- Mark the end of a benchmarked operation * + * * + * This routine is called at the end of the operation that is being benchmarked. It is * + * important to call this routine as soon as possible after the event being benchmarked * + * has completed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The Being() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::End(void) +{ + unsigned long value = Clock; + + if (Counter == MAXIMUM_EVENT_COUNT) { + Average -= Average / MAXIMUM_EVENT_COUNT; + Average += value; + } else { + Average += value; + Counter++; + } + TotalCount++; +} + + +/*********************************************************************************************** + * Benchmark::Value -- Fetch the current average benchmark time. * + * * + * This routine will take the statistics already accumulated and determine the average * + * time recorded. This value will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the average time that all events tracked by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +unsigned long Benchmark::Value(void) const +{ + if (Counter) { + return(Average / Counter); + } + return(0); +} diff --git a/CODE/BENCH.H b/CODE/BENCH.H new file mode 100644 index 0000000..53007d9 --- /dev/null +++ b/CODE/BENCH.H @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BENCH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BENCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BENCH_H +#define BENCH_H + +#include "mpu.h" +#include "ftimer.h" + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a timer access object that will fetch the internal Pentium +** clock value. +*/ +class PentiumTimerClass +{ + public: + unsigned long operator () (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} + operator unsigned long (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} +}; + + +/* +** A performance tracking tool object. It is used to track elapsed time. Unlike a simple clock, this +** class will keep a running average of the duration. Typical use of this would be to benchmark some +** process that occurs multiple times. By benchmarking an average time, inconsistencies in a particular +** run can be overcome. +*/ +class Benchmark +{ + public: + Benchmark(void); + + void Begin(bool reset=false); + void End(void); + + void Reset(void); + unsigned long Value(void) const; + unsigned long Count(void) const {return(TotalCount);} + + private: + /* + ** The maximum number of events to keep running average of. If + ** events exceed this number, then older events drop off the + ** accumulated time. This number needs to be as small as + ** is reasonable. The larger this number gets, the less magnitude + ** that the benchmark timer can handle. Example; At a value of + ** 256, the magnitude of the timer can only be 24 bits. + */ + enum {MAXIMUM_EVENT_COUNT=256}; + + /* + ** This is the timer the is used to clock the events. + */ + BasicTimerClass Clock; + + /* + ** The total time off all events tracked so far. + */ + unsigned long Average; + + /* + ** The total number of events tracked so far. + */ + unsigned long Counter; + + /* + ** Absolute total number of events (possibly greater than the + ** number of events tracked in the average). + */ + unsigned long TotalCount; +}; + + +#endif diff --git a/CODE/BFILE.MAK b/CODE/BFILE.MAK new file mode 100644 index 0000000..19af58e --- /dev/null +++ b/CODE/BFILE.MAK @@ -0,0 +1,2122 @@ +# +# 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 . +# + +# $Header: F:\projects\c&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40:38 JOE_BOSTIC $ +#*************************************************************************** +#** 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 * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : March 25, 1993 * +#* * +#* Last Update : March 25, 1993 [JLB] * +#* * +#*-------------------------------------------------------------------------* + + +# Comment out the following line to disable "include file autodependency". +.AUTODEPEND +#.SWAP + +!include "rules.mak" + + +########################################################################## + +MAPFILES = \ + +CACHEMAP = \ + BRIEFING.AUD \ + BAR3RED.SHP \ + BAR3BLU.SHP \ + COUNTRYA.SHP \ + COUNTRYE.SHP \ + CREDSA.SHP \ + CREDSU.SHP \ + HISCORE1.SHP \ + HISCORE2.SHP \ + TIME.SHP \ + CLOCK1.AUD \ + COUNTRY4.AUD \ + MAPWIPE2.AUD \ + MAPWIPE5.AUD \ + TONEY10.AUD \ + TONEY4.AUD \ + TONEY7.AUD \ + SFX4.AUD \ + BEEPY6.AUD \ + KEYSTROK.AUD \ + APPEAR1.AUD \ + SCOLD1.AUD \ + COUNTRY1.AUD \ + ALI-TRAN.WSA \ + SOV-TRAN.WSA \ + ALIBACKH.PCX \ + SOVBACKH.PCX \ + BAR3RHR.SHP \ + BAR3BHR.SHP \ + CREDSAHR.SHP \ + CREDSUHR.SHP \ + HISC1-HR.SHP \ + HISC2-HR.SHP \ + TIMEHR.SHP \ + MLTIPLYR.WSA \ + +LOCALFILES = \ + PROLOG.CPS \ + MAP.AUD \ + TITLE.CPS \ + PALETTE.CPS \ + INTRO.AUD \ + EGOPAL.PAL \ + RULES.INI \ + CREDITS.TXT \ + ALIPAPER.CPS \ + 3POINT.FNT \ + 8POINT.FNT \ + EDITFNT.FNT \ + CONQUER.ENG \ + DEBUG.ENG \ + LED.FNT \ + SNOW.PAL \ + TEMPERAT.PAL \ + INTERIOR.PAL \ + VCR.FNT \ + HOLE0000.LUT \ + HOLE0001.LUT \ + HOLE0002.LUT \ + HOLE0003.LUT \ + HOLE0004.LUT \ + HOLE0005.LUT \ + HOLE0006.LUT \ + HOLE0007.LUT \ + HOLE0008.LUT \ + HOLE0009.LUT \ + HOLE0010.LUT \ + HOLE0011.LUT \ + HOLE0012.LUT \ + HOLE0013.LUT \ + HOLE0014.LUT \ + HOLE0015.LUT \ + HOLE0016.LUT \ + HOLE0017.LUT \ + HOLE0018.LUT \ + HOLE0019.LUT \ + HOLE0020.LUT \ + HOLE0021.LUT \ + HOLE0022.LUT \ + HOLE0023.LUT \ + HOLE0024.LUT \ + HOLE0025.LUT \ + HOLE0026.LUT \ + HOLE0027.LUT \ + HOLE0028.LUT \ + HOLE0029.LUT \ + HOLE0030.LUT \ + HOLE0031.LUT \ + HOLE0032.LUT \ + HOLE0033.LUT \ + HOLE0034.LUT \ + HOLE0035.LUT \ + HOLE0036.LUT \ + HOLE0037.LUT \ + HOLE0038.LUT \ + HOLE0039.LUT \ + HOLE0040.LUT \ + HOLE0041.LUT \ + HOLE0042.LUT \ + HOLE0043.LUT \ + HOLE0044.LUT \ + HOLE0045.LUT \ + HOLE0046.LUT \ + HOLE0047.LUT \ +# TEMPSCOR.FNT \ +# 6POINT.FNT \ +# GRAD6FNT.FNT \ +# SCOREFNT.FNT \ + + +# Files that have counterparts in both high and low resolutions. +# These files will be built into the HIRES.MIX and LORES.MIX files. +HILORES = \ + TRANICON.SHP \ + PIPS.SHP \ + PULSE.SHP \ + ATOMICON.SHP \ + WARPICON.SHP \ + C1.SHP \ + C2.SHP \ + CHAN.SHP \ + DELPHI.SHP \ + E1.SHP \ + E2.SHP \ + E3.SHP \ + E4.SHP \ + E5.SHP \ + E6.SHP \ + E7.SHP \ + EINSTEIN.SHP \ + GNRL.SHP \ + MEDI.SHP \ + SPY.SHP \ + THF.SHP \ + DD-BKGND.SHP \ + DD-BOTM.SHP \ + DD-CRNR.SHP \ + DD-EDGE.SHP \ + DD-LEFT.SHP \ + DD-RIGHT.SHP \ + DD-TOP.SHP \ + 12METFNT.FNT \ + GRAD6FNT.FNT \ + HELP.FNT \ + 6POINT.FNT \ + TYPE.FNT \ + SCOREFNT.FNT \ + 1TNKICON.SHP \ + 2TNKICON.SHP \ + 3TNKICON.SHP \ + 4TNKICON.SHP \ + AFLDICON.SHP \ + AGUNICON.SHP \ + APCICON.SHP \ + APWRICON.SHP \ + ARTYICON.SHP \ + ATEKICON.SHP \ + BADRICON.SHP \ + BARRICON.SHP \ + BRIKICON.SHP \ + BTN-DN.SHP \ + BTN-PL.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + CAICON.SHP \ + CAMICON.SHP \ + CLOCK.SHP \ + DDICON.SHP \ + DOGICON.SHP \ + DOMEICON.SHP \ + DOMFICON.SHP \ + E1ICON.SHP \ + E2ICON.SHP \ + E3ICON.SHP \ + E4ICON.SHP \ + E6ICON.SHP \ + E7ICON.SHP \ + FACFICON.SHP \ + FACTICON.SHP \ + FENCICON.SHP \ + FIXICON.SHP \ + FTURICON.SHP \ + GAPICON.SHP \ + GPSSICON.SHP \ + GUNICON.SHP \ + HARVICON.SHP \ + HBOXICON.SHP \ + HELIICON.SHP \ + HINDICON.SHP \ + HPADICON.SHP \ + INFXICON.SHP \ + IRONICON.SHP \ + JEEPICON.SHP \ + KENNICON.SHP \ + LSTICON.SHP \ + MAP.SHP \ + MCVICON.SHP \ + MEDIICON.SHP \ + MGGICON.SHP \ + MIGICON.SHP \ + MNLYICON.SHP \ + MOUSE.SHP \ + MRJICON.SHP \ + MSLOICON.SHP \ + NATORADR.SHP \ + PBMBICON.SHP \ + PBOXICON.SHP \ + PDOXICON.SHP \ + PINFICON.SHP \ + POWER.SHP \ + POWERBAR.SHP \ + POWRICON.SHP \ + PROCICON.SHP \ + PTICON.SHP \ + REPAIR.SHP \ + SAMICON.SHP \ + SBAGICON.SHP \ + SELL.SHP \ + SIDEBAR.SHP \ + SILOICON.SHP \ + SMIGICON.SHP \ + SONRICON.SHP \ + SOVPAPER.CPS \ + SPEFICON.SHP \ + SPENICON.SHP \ + SPYICON.SHP \ + SSICON.SHP \ + STEKICON.SHP \ + STRIP.SHP \ + STRIPDN.SHP \ + STRIPUP.SHP \ + SYRDICON.SHP \ + SYRFICON.SHP \ + TABS.SHP \ + TENTICON.SHP \ + THFICON.SHP \ + TRUKICON.SHP \ + TSLAICON.SHP \ + U2ICON.SHP \ + USSRRADR.SHP \ + V2RLICON.SHP \ + WEAFICON.SHP \ + WEAPICON.SHP \ + YAKICON.SHP \ + NRADRFRM.SHP \ + URADRFRM.SHP \ + SIDE1NA.SHP \ + SIDE1US.SHP \ + SIDE2NA.SHP \ + SIDE2US.SHP \ + SIDE3NA.SHP \ + SIDE3US.SHP \ + STRIPNA.SHP \ + STRIPUS.SHP \ + +# MOEBICON.SHP \ + +HILORES1 = \ + MECH.SHP \ + SHOK.SHP \ + CARRICON.SHP \ + CTNKICON.SHP \ + DTRKICON.SHP \ + MECHICON.SHP \ + MSUBICON.SHP \ + QTNKICON.SHP \ + SHOKICON.SHP \ + STNKICON.SHP \ + TTNKICON.SHP \ + +# These helper macros substitute the extension so that +# the appropriate art build rule will be invoked. +xLOHILORES = $(HILORES:.SHP=.LOW) +LOHILORES = $(xLOHILORES:.FNT=.LNT) +xHIHILORES = $(HILORES:.SHP=.HI) +HIHILORES = $(xHIHILORES:.FNT=.HNT) + +xLOHILORES1 = $(HILORES1:.SHP=.LOW) +LOHILORES1 = $(xLOHILORES1:.FNT=.LNT) +xHIHILORES1 = $(HILORES1:.SHP=.HI) +HIHILORES1 = $(xHIHILORES1:.FNT=.HNT) + +# +# Files required for hires/Win95 version only +# +# This mix file is cached +# +HIRESFILES = \ + ALIPAPER.PCX \ + PROLOG.PCX \ + SOVPAPER.PCX \ + AFTR_HI.PCX \ + ALY1.PCX \ + APC_HI.PCX \ + APHI0049.PCX \ + BNHI0020.PCX \ + DCHI0040.PCX \ + FRHI0166.PCX \ + LAB.PCX \ + LANDSBRG.PCX \ + MAHI0107.PCX \ + MIG_HI.PCX \ + MTFACTHI.PCX \ + NEEDLE.PCX \ + SOV2.PCX \ + SPY.PCX \ + STALIN.PCX \ + TENT.PCX \ +# ENG_HI.PCX \ + + +CONQUERFILES = \ + PARABOMB.SHP \ + RADARFRM.SHP \ + ARMOR.SHP \ + FPOWER.SHP \ + SPEED.SHP \ + TQUAKE.SHP \ + H2O_EXP1.SHP \ + H2O_EXP2.SHP \ + H2O_EXP3.SHP \ + FLAK.SHP \ + EBTN-DN.SHP \ + EBTN-UP.SHP \ + ATOMSFX.SHP \ + TWINKLE1.SHP \ + TWINKLE2.SHP \ + TWINKLE3.SHP \ + CHRONBOX.SHP \ + GPSBOX.SHP \ + INVULBOX.SHP \ + PARABOX.SHP \ + SONARBOX.SHP \ + SPUTNIK.SHP \ + SPUTDOOR.SHP \ + ATOMICDN.SHP \ + ATOMICUP.SHP \ + TYPE.FNT \ + 120MM.SHP \ + 1TNK.SHP \ + 2TNK.SHP \ + 3TNK.SHP \ + 4TNK.SHP \ + 50CAL.SHP \ + AFLD.SHP \ + AGUN.SHP \ + APC.SHP \ + APWR.SHP \ + ART-EXP1.SHP \ + ARTY.SHP \ + ATEK.SHP \ + BADR.SHP \ + BARB.SHP \ + BARL.SHP \ + BARR.SHP \ + BIO.SHP \ + BOMB.SHP \ + BOMBLET.SHP \ + BRIK.SHP \ + BRL3.SHP \ + BURN-L.SHP \ + BURN-M.SHP \ + BURN-S.SHP \ + CA.SHP \ + CYCL.SHP \ + DD.SHP \ + DEVIATOR.SHP \ + DOG.SHP \ + DOGBULLT.SHP \ + DOLLAR.SHP \ + DOME.SHP \ + DRAGON.SHP \ + EARTH.SHP \ + ELECTDOG.SHP \ + EMPULSE.SHP \ + FACT.SHP \ + FB1.SHP \ + FB2.SHP \ + FBALL1.SHP \ + FCOM.SHP \ + FENC.SHP \ + FIRE1.SHP \ + FIRE2.SHP \ + FIRE3.SHP \ + FIRE4.SHP \ + FIX.SHP \ + FLAGFLY.SHP \ + FLMSPT.SHP \ + FPLS.SHP \ + FRAG1.SHP \ + FTNK.SHP \ + FTUR.SHP \ + GAP.SHP \ + GUN.SHP \ + GUNFIRE.SHP \ + HARV.SHP \ + HELI.SHP \ + HIND.SHP \ + HOSP.SHP \ + HPAD.SHP \ + INVUN.SHP \ + IRON.SHP \ + JEEP.SHP \ + KENN.SHP \ + LITNING.SHP \ + LROTOR.SHP \ + LST.SHP \ + MCV.SHP \ + MGG.SHP \ + MGUN.SHP \ + MHQ.SHP \ + MIG.SHP \ + MINE.SHP \ + MINIGUN.SHP \ + MINP.SHP \ + MINV.SHP \ + MISS.SHP \ + MISSILE.SHP \ + MISSILE2.SHP \ + MLRS.SHP \ + MNLY.SHP \ + MRJ.SHP \ + NAPALM1.SHP \ + NAPALM2.SHP \ + NAPALM3.SHP \ + ORCA.SHP \ + PARACH.SHP \ + PATRIOT.SHP \ + PBOX.SHP \ + PDOX.SHP \ + PIFF.SHP \ + PIFFPIFF.SHP \ + POWR.SHP \ + PROC.SHP \ + PT.SHP \ + RAPID.SHP \ + RROTOR.SHP \ + SAM.SHP \ + SAMFIRE.SHP \ + SBAG.SHP \ + SCRATE.SHP \ + SELECT.SHP \ + SHADOW.SHP \ + SILO.SHP \ + SMIG.SHP \ + SMOKEY.SHP \ + SMOKE_M.SHP \ + SMOKLAND.SHP \ + SPEN.SHP \ + SS.SHP \ + SSAM.SHP \ + STEALTH2.SHP \ + STEK.SHP \ + STNK.SHP \ + SYRD.SHP \ + TENT.SHP \ + TRAN.SHP \ + TRANS.ICN \ + TRUK.SHP \ + TSLA.SHP \ + TURR.SHP \ + U2.SHP \ + V19.SHP \ + V2.SHP \ + V2RL.SHP \ + VEH-HIT1.SHP \ + VEH-HIT2.SHP \ + VEH-HIT3.SHP \ + WAKE.SHP \ + WCRATE.SHP \ + WWCRATE.SHP \ + WEAP.SHP \ + WEAP2.SHP \ + WOOD.SHP \ + YAK.SHP \ + AFLDMAKE.SHP \ + AGUNMAKE.SHP \ + APWRMAKE.SHP \ + ATEKMAKE.SHP \ + BARRMAKE.SHP \ + BIOMAKE.SHP \ + DOMEMAKE.SHP \ + FACTMAKE.SHP \ + FIXMAKE.SHP \ + FTURMAKE.SHP \ + GAPMAKE.SHP \ + GUNMAKE.SHP \ + HOSPMAKE.SHP \ + HPADMAKE.SHP \ + IRONMAKE.SHP \ + KENNMAKE.SHP \ + MINPMAKE.SHP \ + MINVMAKE.SHP \ + PBOXMAKE.SHP \ + POWRMAKE.SHP \ + PDOXMAKE.SHP \ + PROCMAKE.SHP \ + PUMPMAKE.SHP \ + SAMMAKE.SHP \ + SILOMAKE.SHP \ + SPENMAKE.SHP \ + STEKMAKE.SHP \ + SYRDMAKE.SHP \ + TENTMAKE.SHP \ + TSLAMAKE.SHP \ + WEAPMAKE.SHP \ + +GENERALMAPFILES = \ + MISSIONS.PKT \ + CSTRIKE.PKT \ + TUTORIAL.INI \ + SCG01EA.INI \ + SCG40EA.INI \ + SCG41EA.INI \ + SCG42EA.INI \ + SCG43EA.INI \ + SCG44EA.INI \ + SCG45EA.INI \ + SCG46EA.INI \ + SCG47EA.INI \ + SCG48EA.INI \ + SCU40EA.INI \ + SCU41EA.INI \ + SCU42EA.INI \ + SCU43EA.INI \ + SCU44EA.INI \ + SCU45EA.INI \ + SCU46EA.INI \ + SCU47EA.INI \ + SCU48EA.INI \ + SCU01EA.INI \ + SCM01EA.INI \ + SCM02EA.INI \ + SCM03EA.INI \ + SCM04EA.INI \ + SCM05EA.INI \ + SCM06EA.INI \ + SCM07EA.INI \ + SCM08EA.INI \ + SCM09EA.INI \ + SCM10EA.INI \ + SCM11EA.INI \ + SCM12EA.INI \ + SCM13EA.INI \ + SCM14EA.INI \ + SCM15EA.INI \ + SCM16EA.INI \ + SCM17EA.INI \ + SCM18EA.INI \ + SCM19EA.INI \ + SCM20EA.INI \ + SCM21EA.INI \ + SCM22EA.INI \ + SCM23EA.INI \ + SCM24EA.INI \ + SCMD0EA.INI \ + SCMD1EA.INI \ + SCMD2EA.INI \ + SCMD3EA.INI \ + SCMD4EA.INI \ + SCMD5EA.INI \ + SCMD6EA.INI \ + SCMD7EA.INI \ + SCMD8EA.INI \ + SCMD9EA.INI \ + SCME0EA.INI \ + SCME1EA.INI \ + SCME2EA.INI \ + SCME3EA.INI \ + SCME4EA.INI \ + SCME5EA.INI \ + SCME6EA.INI \ + SCME7EA.INI \ + SCME8EA.INI \ + SCME9EA.INI \ + SCMF0EA.INI \ + SCMF1EA.INI \ + SCMF2EA.INI \ + SCMF3EA.INI \ + SCMF4EA.INI \ + SCMF5EA.INI \ + SCMF6EA.INI \ + SCMF7EA.INI \ + SCMF8EA.INI \ + SCMF9EA.INI \ + SCMG0EA.INI \ + SCMG1EA.INI \ + SCMG2EA.INI \ + SCMG3EA.INI \ + SCMG4EA.INI \ + SCMG5EA.INI \ + SCMG6EA.INI \ + SCMG7EA.INI \ + SCMG8EA.INI \ + SCMG9EA.INI \ + SCMH0EA.INI \ + SCMH1EA.INI \ + SCMH2EA.INI \ + SCMH3EA.INI \ + SCMH4EA.INI \ + SCMH5EA.INI \ + SCMH6EA.INI \ + SCMH7EA.INI \ + SCMH8EA.INI \ + SCMH9EA.INI \ + SCMI0EA.INI \ + SCMI1EA.INI \ + SCMI2EA.INI \ + SCMI3EA.INI \ + SCMI4EA.INI \ + SCMI5EA.INI \ + SCMI6EA.INI \ + SCMI7EA.INI \ + SCMI8EA.INI \ + SCMI9EA.INI \ + SCMJ0EA.INI \ + SCMJ1EA.INI \ + SCMJ2EA.INI \ + SCMJ3EA.INI \ + SCMJ4EA.INI \ + SCMJ5EA.INI \ + SCMJ6EA.INI \ + SCMJ7EA.INI \ + SCMJ8EA.INI \ + SCMJ9EA.INI \ + SCMK0EA.INI \ + SCMK1EA.INI \ + SCMK2EA.INI \ + SCMK3EA.INI \ + SCMK4EA.INI \ + SCMK5EA.INI \ + SCMK6EA.INI \ + SCMK7EA.INI \ + SCMK8EA.INI \ + SCMK9EA.INI \ + SCML0EA.INI \ + SCML1EA.INI \ + SCML2EA.INI \ + SCML3EA.INI \ + SCML4EA.INI \ + SCML5EA.INI \ + SCML6EA.INI \ + SCML7EA.INI \ + SCML8EA.INI \ + SCML9EA.INI \ + SCMM0EA.INI \ + SCMM1EA.INI \ + SCMM2EA.INI \ + SCMM3EA.INI \ + SCMM4EA.INI \ + SCMM5EA.INI \ + SCMM6EA.INI \ + SCMM7EA.INI \ + SCMM8EA.INI \ + SCMM9EA.INI \ + SCM25EA.INI \ + SCM26EA.INI \ + SCM27EA.INI \ + SCM28EA.INI \ + SCM29EA.INI \ + SCM30EA.INI \ + SCM31EA.INI \ + SCM32EA.INI \ + SCM33EA.INI \ + SCM34EA.INI \ + SCM35EA.INI \ + SCM36EA.INI \ + SCM37EA.INI \ + SCM38EA.INI \ + SCM39EA.INI \ + SCM40EA.INI \ + SCM41EA.INI \ + SCM42EA.INI \ + SCM43EA.INI \ + SCM44EA.INI \ + SCM45EA.INI \ + SCM46EA.INI \ + SCM47EA.INI \ + SCM48EA.INI \ + SCM49EA.INI \ + SCM50EA.INI \ + SCM51EA.INI \ + SCM52EA.INI \ + SCM53EA.INI \ + SCM54EA.INI \ + SCM55EA.INI \ + SCM56EA.INI \ + SCM57EA.INI \ + SCM58EA.INI \ + SCM59EA.INI \ + SCM60EA.INI \ + SCM61EA.INI \ + SCM62EA.INI \ + SCM63EA.INI \ + SCM64EA.INI \ + SCM65EA.INI \ + SCM66EA.INI \ + SCM67EA.INI \ + SCM68EA.INI \ + SCM69EA.INI \ + SCM70EA.INI \ + SCM71EA.INI \ + SCM72EA.INI \ + SCM73EA.INI \ + SCM74EA.INI \ + SCM75EA.INI \ + SCM76EA.INI \ + SCM77EA.INI \ + SCM78EA.INI \ + SCM79EA.INI \ + SCM80EA.INI \ + SCM81EA.INI \ + SCM82EA.INI \ + SCM83EA.INI \ + SCM84EA.INI \ + SCM85EA.INI \ + SCM86EA.INI \ + SCM87EA.INI \ + SCM88EA.INI \ + SCM89EA.INI \ + SCM90EA.INI \ + SCM91EA.INI \ + SCM92EA.INI \ + SCM93EA.INI \ + SCM94EA.INI \ + SCM95EA.INI \ + SCM96EA.INI \ + SCM97EA.INI \ + SCM98EA.INI \ + SCM99EA.INI \ + SCM100EA.INI \ + SCM101EA.INI \ + SCM102EA.INI \ + SCM103EA.INI \ + SCM104EA.INI \ + SCM105EA.INI \ + SCM106EA.INI \ + SCM107EA.INI \ + SCM108EA.INI \ + SCM109EA.INI \ + SCM110EA.INI \ + SCM111EA.INI \ + SCM112EA.INI \ + SCM113EA.INI \ + SCM114EA.INI \ + SCM115EA.INI \ + SCM116EA.INI \ + SCM117EA.INI \ + SCM118EA.INI \ + SCM119EA.INI \ + SCM120EA.INI \ + SCM121EA.INI \ + SCM122EA.INI \ + SCM123EA.INI \ + SCM124EA.INI \ + SCM125EA.INI \ + SCM126EA.INI \ + SCM127EA.INI \ + SCM128EA.INI \ + SCM129EA.INI \ + SCM130EA.INI \ + +NETMAPFILES = \ + +# Files that aren't cached. +GENERALFILES = \ + AFTR_LO.CPS \ + ALY1-LO.CPS \ + APC_LO.CPS \ + APLO0049.CPS \ + BNLO0020.CPS \ + DCLO0040.CPS \ + FRLO0166.CPS \ + LAB-LO.CPS \ + LANDS-LO.CPS \ + MALO0107.CPS \ + MIG_LO.CPS \ + MTFACTLO.CPS \ + NEEDL-LO.CPS \ + SOV2-LO.CPS \ + SPY-LO.CPS \ + STALN-LO.CPS \ + TENT-LO.CPS \ + TITLE.CPS \ + PPAPER.CPS \ + MSAA.WSA \ + MSAB.WSA \ + MSAC.WSA \ + MSAD.WSA \ + MSAE.WSA \ + MSAF.WSA \ + MSAG.WSA \ + MSAH.WSA \ + MSAI.WSA \ + MSAJ.WSA \ + MSAK.WSA \ + MSAL.WSA \ + MSAM.WSA \ + MSAN.WSA \ + MSSA.WSA \ + MSSB.WSA \ + MSSC.WSA \ + MSSD.WSA \ + MSSE.WSA \ + MSSF.WSA \ + MSSG.WSA \ + MSSH.WSA \ + MSSI.WSA \ + MSSJ.WSA \ + MSSK.WSA \ + MSSL.WSA \ + MSSM.WSA \ + MSSN.WSA \ + +INTERIORFILES = \ + BOXES01.INT \ + BOXES02.INT \ + BOXES03.INT \ + BOXES04.INT \ + BOXES05.INT \ + BOXES06.INT \ + BOXES07.INT \ + BOXES08.INT \ + BOXES09.INT \ + XTRA0001.INT \ + XTRA0002.INT \ + XTRA0003.INT \ + XTRA0004.INT \ + XTRA0005.INT \ + XTRA0006.INT \ + XTRA0007.INT \ + XTRA0008.INT \ + XTRA0009.INT \ + XTRA0010.INT \ + XTRA0011.INT \ + XTRA0012.INT \ + XTRA0013.INT \ + XTRA0014.INT \ + XTRA0015.INT \ + XTRA0016.INT \ + CLEAR1.INT \ + MOVEFLSH.INT \ + ARRO0001.INT \ + ARRO0002.INT \ + ARRO0003.INT \ + ARRO0004.INT \ + ARRO0005.INT \ + ARRO0006.INT \ + ARRO0007.INT \ + ARRO0008.INT \ + ARRO0009.INT \ + ARRO0010.INT \ + ARRO0011.INT \ + ARRO0012.INT \ + ARRO0013.INT \ + ARRO0014.INT \ + ARRO0015.INT \ + FLOR0001.INT \ + FLOR0002.INT \ + FLOR0003.INT \ + FLOR0004.INT \ + FLOR0005.INT \ + FLOR0006.INT \ + FLOR0007.INT \ + GFLR0001.INT \ + GFLR0002.INT \ + GFLR0003.INT \ + GFLR0004.INT \ + GFLR0005.INT \ + GSTR0001.INT \ + GSTR0002.INT \ + GSTR0003.INT \ + GSTR0004.INT \ + GSTR0005.INT \ + GSTR0006.INT \ + GSTR0007.INT \ + GSTR0008.INT \ + GSTR0009.INT \ + GSTR0010.INT \ + GSTR0011.INT \ + LWAL0001.INT \ + LWAL0002.INT \ + LWAL0003.INT \ + LWAL0004.INT \ + LWAL0005.INT \ + LWAL0006.INT \ + LWAL0007.INT \ + LWAL0008.INT \ + LWAL0009.INT \ + LWAL0010.INT \ + LWAL0011.INT \ + LWAL0012.INT \ + LWAL0013.INT \ + LWAL0014.INT \ + LWAL0015.INT \ + LWAL0016.INT \ + LWAL0017.INT \ + LWAL0018.INT \ + LWAL0019.INT \ + LWAL0020.INT \ + LWAL0021.INT \ + LWAL0022.INT \ + LWAL0023.INT \ + LWAL0024.INT \ + LWAL0025.INT \ + LWAL0026.INT \ + LWAL0027.INT \ + STRP0001.INT \ + STRP0002.INT \ + STRP0003.INT \ + STRP0004.INT \ + STRP0005.INT \ + STRP0006.INT \ + STRP0007.INT \ + STRP0008.INT \ + STRP0009.INT \ + STRP0010.INT \ + STRP0011.INT \ + WALL0001.INT \ + WALL0002.INT \ + WALL0003.INT \ + WALL0004.INT \ + WALL0005.INT \ + WALL0006.INT \ + WALL0007.INT \ + WALL0008.INT \ + WALL0009.INT \ + WALL0010.INT \ + WALL0011.INT \ + WALL0012.INT \ + WALL0013.INT \ + WALL0014.INT \ + WALL0015.INT \ + WALL0016.INT \ + WALL0017.INT \ + WALL0018.INT \ + WALL0019.INT \ + WALL0020.INT \ + WALL0021.INT \ + WALL0022.INT \ + WALL0023.INT \ + WALL0024.INT \ + WALL0025.INT \ + WALL0026.INT \ + WALL0027.INT \ + WALL0028.INT \ + WALL0029.INT \ + WALL0030.INT \ + WALL0031.INT \ + WALL0032.INT \ + WALL0033.INT \ + WALL0034.INT \ + WALL0035.INT \ + WALL0036.INT \ + WALL0037.INT \ + WALL0038.INT \ + WALL0039.INT \ + WALL0040.INT \ + WALL0041.INT \ + WALL0042.INT \ + WALL0043.INT \ + WALL0044.INT \ + WALL0045.INT \ + WALL0046.INT \ + WALL0047.INT \ + WALL0048.INT \ + WALL0049.INT \ + +# Both the temperate and snow sets have identical template entries. +TEMPERATEFILES = \ + MINE.TEM \ + ICE01.TEM \ + ICE02.TEM \ + ICE03.TEM \ + ICE04.TEM \ + ICE05.TEM \ + MOVEFLSH.TEM \ + BR1X.TEM \ + BR2X.TEM \ + BRIDGE1X.TEM \ + BRIDGE2X.TEM \ + BRIDGE1H.TEM \ + BRIDGE2H.TEM \ + F01.TEM \ + F02.TEM \ + F03.TEM \ + F04.TEM \ + F05.TEM \ + F06.TEM \ + ELECTRO.TEM \ + B1.TEM \ + B2.TEM \ + B3.TEM \ + BIB1.TEM \ + BIB2.TEM \ + BIB3.TEM \ + BR1A.TEM \ + BR1B.TEM \ + BR1C.TEM \ + BR2A.TEM \ + BR2B.TEM \ + BR2C.TEM \ + BR3A.TEM \ + BR3B.TEM \ + BR3C.TEM \ + BR3D.TEM \ + BR3E.TEM \ + BR3F.TEM \ + BRIDGE1.TEM \ + BRIDGE1D.TEM \ + BRIDGE2.TEM \ + BRIDGE2D.TEM \ + CLEAR1.TEM \ + CORPSE1.TEM \ + CORPSE2.TEM \ + CORPSE3.TEM \ + CR1.TEM \ + CR2.TEM \ + CR3.TEM \ + CR4.TEM \ + CR5.TEM \ + CR6.TEM \ + D01.TEM \ + D02.TEM \ + D03.TEM \ + D04.TEM \ + D05.TEM \ + D06.TEM \ + D07.TEM \ + D08.TEM \ + D09.TEM \ + D10.TEM \ + D11.TEM \ + D12.TEM \ + D13.TEM \ + D14.TEM \ + D15.TEM \ + D16.TEM \ + D17.TEM \ + D18.TEM \ + D19.TEM \ + D20.TEM \ + D21.TEM \ + D22.TEM \ + D23.TEM \ + D24.TEM \ + D25.TEM \ + D26.TEM \ + D27.TEM \ + D28.TEM \ + D29.TEM \ + D30.TEM \ + D31.TEM \ + D32.TEM \ + D33.TEM \ + D34.TEM \ + D35.TEM \ + D36.TEM \ + D37.TEM \ + D38.TEM \ + D39.TEM \ + D40.TEM \ + D41.TEM \ + D42.TEM \ + D43.TEM \ + D44.TEM \ + D45.TEM \ + FALLS1.TEM \ + FALLS1A.TEM \ + FALLS2.TEM \ + FALLS2A.TEM \ + FORD1.TEM \ + FORD2.TEM \ + GEM01.TEM \ + GEM02.TEM \ + GEM03.TEM \ + GEM04.TEM \ + GOLD01.TEM \ + GOLD02.TEM \ + GOLD03.TEM \ + GOLD04.TEM \ + HBOX.TEM \ + MSLOMAKE.TEM \ + HBOXMAKE.TEM \ + MSLO.TEM \ + P01.TEM \ + P02.TEM \ + P03.TEM \ + P04.TEM \ + P07.TEM \ + P08.TEM \ + P13.TEM \ + P14.TEM \ + RC01.TEM \ + RC02.TEM \ + RC03.TEM \ + RC04.TEM \ + RF01.TEM \ + RF02.TEM \ + RF03.TEM \ + RF04.TEM \ + RF05.TEM \ + RF06.TEM \ + RF07.TEM \ + RF08.TEM \ + RF09.TEM \ + RF10.TEM \ + RF11.TEM \ + RV01.TEM \ + RV02.TEM \ + RV03.TEM \ + RV04.TEM \ + RV05.TEM \ + RV06.TEM \ + RV07.TEM \ + RV08.TEM \ + RV09.TEM \ + RV10.TEM \ + RV11.TEM \ + RV12.TEM \ + RV13.TEM \ + RV14.TEM \ + RV15.TEM \ + S01.TEM \ + S02.TEM \ + S03.TEM \ + S04.TEM \ + S05.TEM \ + S06.TEM \ + S07.TEM \ + S08.TEM \ + S09.TEM \ + S10.TEM \ + S11.TEM \ + S12.TEM \ + S13.TEM \ + S14.TEM \ + S15.TEM \ + S16.TEM \ + S17.TEM \ + S18.TEM \ + S19.TEM \ + S20.TEM \ + S21.TEM \ + S22.TEM \ + S23.TEM \ + S24.TEM \ + S25.TEM \ + S26.TEM \ + S27.TEM \ + S28.TEM \ + S29.TEM \ + S30.TEM \ + S31.TEM \ + S32.TEM \ + S33.TEM \ + S34.TEM \ + S35.TEM \ + S36.TEM \ + S37.TEM \ + S38.TEM \ + SC1.TEM \ + SC2.TEM \ + SC3.TEM \ + SC4.TEM \ + SC5.TEM \ + SC6.TEM \ + SH01.TEM \ + SH02.TEM \ + SH03.TEM \ + SH04.TEM \ + SH05.TEM \ + SH06.TEM \ + SH07.TEM \ + SH08.TEM \ + SH09.TEM \ + SH10.TEM \ + SH11.TEM \ + SH12.TEM \ + SH13.TEM \ + SH14.TEM \ + SH15.TEM \ + SH16.TEM \ + SH17.TEM \ + SH18.TEM \ + SH19.TEM \ + SH20.TEM \ + SH21.TEM \ + SH22.TEM \ + SH23.TEM \ + SH24.TEM \ + SH25.TEM \ + SH26.TEM \ + SH27.TEM \ + SH28.TEM \ + SH29.TEM \ + SH30.TEM \ + SH31.TEM \ + SH32.TEM \ + SH33.TEM \ + SH34.TEM \ + SH35.TEM \ + SH36.TEM \ + SH37.TEM \ + SH38.TEM \ + SH39.TEM \ + SH40.TEM \ + SH41.TEM \ + SH42.TEM \ + SH43.TEM \ + SH44.TEM \ + SH45.TEM \ + SH46.TEM \ + SH47.TEM \ + SH48.TEM \ + SH49.TEM \ + SH50.TEM \ + SH51.TEM \ + SH52.TEM \ + SH53.TEM \ + SH54.TEM \ + SH55.TEM \ + SH56.TEM \ + T01.TEM \ + T02.TEM \ + T03.TEM \ + T05.TEM \ + T06.TEM \ + T07.TEM \ + T08.TEM \ + T10.TEM \ + T11.TEM \ + T12.TEM \ + T13.TEM \ + T14.TEM \ + T15.TEM \ + T16.TEM \ + T17.TEM \ + TC01.TEM \ + TC02.TEM \ + TC03.TEM \ + TC04.TEM \ + TC05.TEM \ + V01.TEM \ + V02.TEM \ + V03.TEM \ + V04.TEM \ + V05.TEM \ + V06.TEM \ + V07.TEM \ + V08.TEM \ + V09.TEM \ + V10.TEM \ + V11.TEM \ + V12.TEM \ + V13.TEM \ + V14.TEM \ + V15.TEM \ + V16.TEM \ + V17.TEM \ + V18.TEM \ + W1.TEM \ + W2.TEM \ + WC01.TEM \ + WC02.TEM \ + WC03.TEM \ + WC04.TEM \ + WC05.TEM \ + WC06.TEM \ + WC07.TEM \ + WC08.TEM \ + WC09.TEM \ + WC10.TEM \ + WC11.TEM \ + WC12.TEM \ + WC13.TEM \ + WC14.TEM \ + WC15.TEM \ + WC16.TEM \ + WC17.TEM \ + WC18.TEM \ + WC19.TEM \ + WC20.TEM \ + WC21.TEM \ + WC22.TEM \ + WC23.TEM \ + WC24.TEM \ + WC25.TEM \ + WC26.TEM \ + WC27.TEM \ + WC28.TEM \ + WC29.TEM \ + WC30.TEM \ + WC31.TEM \ + WC32.TEM \ + WC33.TEM \ + WC34.TEM \ + WC35.TEM \ + WC36.TEM \ + WC37.TEM \ + WC38.TEM \ + +# Every temperate theater terrain file has a snow theater counterpart. +SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO) + +# Sound effects (Juvenile or Adult) +SFX = \ + +# Generic wave files (never changes). +WAVFILES = \ + AACANON3.AUD \ + BEEPSLCT.AUD \ + BLEEP11.AUD \ + BLEEP12.AUD \ + BLEEP13.AUD \ + BLEEP17.AUD \ + BLEEP5.AUD \ + BLEEP6.AUD \ + BLEEP9.AUD \ + BOMBIT1.AUD \ + BUILD5.AUD \ + BUZZY1.AUD \ + CANNON1.AUD \ + CANNON2.AUD \ + CASHDN1.AUD \ + CASHTURN.AUD \ + CASHUP1.AUD \ + CHRONO2.AUD \ + CHROTNK1.AUD \ + CHUTE1.AUD \ + CMON1.AUD \ + CRMBLE2.AUD \ + DEDMAN1.AUD \ + DEDMAN10.AUD \ + DEDMAN2.AUD \ + DEDMAN3.AUD \ + DEDMAN4.AUD \ + DEDMAN5.AUD \ + DEDMAN6.AUD \ + DEDMAN7.AUD \ + DEDMAN8.AUD \ + DOGG5P.AUD \ + DOGW3PX.AUD \ + DOGW5.AUD \ + DOGW6.AUD \ + DOGW7.AUD \ + DOGY1.AUD \ + EAFFIRM1.AUD \ + EENGIN1.AUD \ + EINAH1.AUD \ + EINOK1.AUD \ + EINYES1.AUD \ + EMOVOUT1.AUD \ + EYESSIR1.AUD \ + FIREBL3.AUD \ + FIRETRT1.AUD \ + FIXIT1.AUD \ + GIRLOKAY.AUD \ + GIRLYEAH.AUD \ + GOTIT1.AUD \ + GRENADE1.AUD \ + GUN11.AUD \ + GUN13.AUD \ + GUN27.AUD \ + GUN5.AUD \ + GUYOKAY1.AUD \ + GUYYEAH1.AUD \ + H2OBOMB2.AUD \ + HEAL2.AUD \ + HYDROD1.AUD \ + INVUL2.AUD \ + IRONCUR9.AUD \ + JBURN1.AUD \ + JCHRGE1.AUD \ + JCRISP1.AUD \ + JDANCE1.AUD \ + JJUICE1.AUD \ + JJUMP1.AUD \ + JLIGHT1.AUD \ + JPOWER1.AUD \ + JSHOCK1.AUD \ + JYES1.AUD \ + KABOOM1.AUD \ + KABOOM12.AUD \ + KABOOM15.AUD \ + KABOOM22.AUD \ + KABOOM25.AUD \ + KABOOM30.AUD \ + KEEPEM1.AUD \ + LAUGH1.AUD \ + LEFTY1.AUD \ + MADCHRG2.AUD \ + MADEXPLO.AUD \ + MAFFIRM1.AUD \ + MBOSS1.AUD \ + MHEAR1.AUD \ + MHOTDIG1.AUD \ + MHOWDY1.AUD \ + MHUH1.AUD \ + MGUNINF1.AUD \ + MINE1.AUD \ + MINEBLO1.AUD \ + MINELAY1.AUD \ + MISSILE1.AUD \ + MISSILE6.AUD \ + MISSILE7.AUD \ + MLAFF1.AUD \ + MMOVOUT1.AUD \ + MRESPON1.AUD \ + MRISE1.AUD \ + MWRENCH1.AUD \ + MYEEHAW1.AUD \ + MYES1.AUD \ + MYESSIR1.AUD \ + ONIT1.AUD \ + PILLBOX1.AUD \ + PLACBLDG.AUD \ + RABEEP1.AUD \ + RADARDN1.AUD \ + RADARON2.AUD \ + RAMENU1.AUD \ + ROKROLL1.AUD \ + SAFFIRM1.AUD \ + SANDBAG2.AUD \ + SCOLDY1.AUD \ + SCOMND1.AUD \ + SHKTROP1.AUD \ + SILENCER.AUD \ + SINDEED1.AUD \ + SKING1.AUD \ + SMOUT1.AUD \ + SOKAY1.AUD \ + SONPULSE.AUD \ + SONWAY1.AUD \ + SPLASH9.AUD \ + SQUISHY2.AUD \ + SUBSHOW1.AUD \ + SWHAT1.AUD \ + SYEAH1.AUD \ + SYESSIR1.AUD \ + TANDETH1.AUD \ + TANK5.AUD \ + TANK6.AUD \ + TESLA1.AUD \ + TORPEDO1.AUD \ + TSLACHG2.AUD \ + TUFFGUY1.AUD \ + TURRET1.AUD \ + WALLKIL2.AUD \ + YEAH1.AUD \ + YES1.AUD \ + YO1.AUD \ + +# Vehicle responses +RESPONSE1 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + REPORT1.AUD \ + VEHIC1.AUD \ + YESSIR1.AUD \ + +# Infantry responses +RESPONSE2 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + NOPROB.AUD \ + OVEROUT.AUD \ + READY.AUD \ + REPORT1.AUD \ + RITAWAY.AUD \ + ROGER.AUD \ + UGOTIT.AUD \ + YESSIR1.AUD \ + +#TSCOREFILES = \ +# cps\record.bin \ +# WIN1.AUD \ +# MAP1.AUD \ + +VARFILES = \ + +SCOREFILES = \ + CREDITS.AUD \ + AWAIT.AUD \ + BIGF226M.AUD \ + CRUS226M.AUD \ + DENSE_R.AUD \ + FAC1226M.AUD \ + FAC2226M.AUD \ + FOGGER1A.AUD \ + HELL226M.AUD \ + MUD1A.AUD \ + RADIO2.AUD \ + ROLLOUT.AUD \ + RUN1226M.AUD \ + SCORE.AUD \ + SMSH226M.AUD \ + SNAKE.AUD \ + TERMINAT.AUD \ + TREN226M.AUD \ + TWIN.AUD \ + VECTOR1A.AUD \ + WORK226M.AUD \ + 2ND_HAND.AUD \ + ARAZIOD.AUD \ + BACKSTAB.AUD \ + CHAOS2.AUD \ + SHUT_IT.AUD \ + TWINMIX1.AUD \ + UNDER3.AUD \ + VR2.AUD \ + BOG.AUD \ + FLOAT_V2.AUD \ + GLOOM.AUD \ + GRNDWIRE.AUD \ + RPT.AUD \ + SEARCH.AUD \ + TRACTION.AUD \ + WASTELND.AUD \ + +SPEECHFILES = \ + STRCKIL1.AUD \ + NOPOWR1.AUD \ + SAVE1.AUD \ + LOAD1.AUD \ + 10MINR.AUD \ + 1MINR.AUD \ + 1OBJMET1.AUD \ + 20MINR.AUD \ + 2MINR.AUD \ + 2OBJMET1.AUD \ + 30MINR.AUD \ + 3MINR.AUD \ + 3OBJMET1.AUD \ + 40MINR.AUD \ + 4MINR.AUD \ + 5MINR.AUD \ + AAPPRO1.AUD \ + AARIVE1.AUD \ + AARIVE1.AUD \ + AARRIVE1.AUD \ + AARRIVN1.AUD \ + AARRIVS1.AUD \ + AARRIVW1.AUD \ + AAVAIL1.AUD \ + ABLDGIN1.AUD \ + AFALLEN1.AUD \ + ALAUNCH1.AUD \ + APREP1.AUD \ + AREADY1.AUD \ + ARMORUP1.AUD \ + ASELECT1.AUD \ + ATLNCH1.AUD \ + ATPREP1.AUD \ + AUNITL1.AUD \ + BASEATK1.AUD \ + BCT1.AUD \ + BLDGINF1.AUD \ + BLDGPRG1.AUD \ + CANCLD1.AUD \ + CHROCHR1.AUD \ + CHRORDY1.AUD \ + CHROYES1.AUD \ + CMDCNTR1.AUD \ + CNTLDED1.AUD \ + COMNDOF1.AUD \ + COMNDOR1.AUD \ + CONSCMP1.AUD \ + CONVLST1.AUD \ + CONVYAP1.AUD \ + CREDIT1.AUD \ + ENMYAPP1.AUD \ + FIREPO1.AUD \ + FLARE1.AUD \ + FLAREE1.AUD \ + FLAREN1.AUD \ + FLARES1.AUD \ + FLAREW1.AUD \ + IRONCHG1.AUD \ + IRONRDY1.AUD \ + KOSYFRE1.AUD \ + KOSYRES1.AUD \ + LOPOWER1.AUD \ + MERCF1.AUD \ + MERCR1.AUD \ + MISNLST1.AUD \ + MISNWON1.AUD \ + MTIMEIN1.AUD \ + NAVYLST1.AUD \ + NEWOPT1.AUD \ + NOBUILD1.AUD \ + NODEPLY1.AUD \ + NOFUNDS1.AUD \ + NOFUNDS1.AUD \ + OBJMET1.AUD \ + OBJNMET1.AUD \ + OBJNRCH1.AUD \ + OBJRCH1.AUD \ + ONHOLD1.AUD \ + OPTERM1.AUD \ + PRIBLDG1.AUD \ + PROGRES1.AUD \ + PULSE1.AUD \ + REINFOR1.AUD \ + REPAIR1.AUD \ + REPAIR1.AUD \ + SATLNCH1.AUD \ + SILOND1.AUD \ + SLCTTGT1.AUD \ + SOVEFAL1.AUD \ + SOVEMP1.AUD \ + SOVFAPP1.AUD \ + SOVFORC1.AUD \ + SOVREIN1.AUD \ + SPYPLN1.AUD \ + STRUCAP1.AUD \ + STRUSLD1.AUD \ + TANYAF1.AUD \ + TANYAR1.AUD \ + TARGFRE1.AUD \ + TARGRES1.AUD \ + TIMERGO1.AUD \ + TIMERNO1.AUD \ + TRAIN1.AUD \ + UNITFUL1.AUD \ + UNITLST1.AUD \ + UNITRDY1.AUD \ + UNITREP1.AUD \ + UNITSLD1.AUD \ + UNITSPD1.AUD \ + XPLOPLC1.AUD \ +# ABLDGC1.AUD \ +# SOVBLDG1.AUD \ +# SOVSTRC1.AUD \ +# SOVUNTD1.AUD \ +# AUNITD1.AUD \ +# ASTRUCD1.AUD \ + +#ALLIESVQ = \ +DUMMYVQ = \ + AAGUN.VQA \ + AFTRMATH.VQA \ + ALLY1.VQA \ + ALLY10.VQA \ + ALLY10B.VQA \ + ALLY11.VQA \ + ALLY12.VQA \ + ALLY14.VQA \ + ALLY2.VQA \ + ALLY4.VQA \ + ALLY5.VQA \ + ALLY6.VQA \ + ALLY8.VQA \ + ALLY9.VQA \ + ALLYEND.VQA \ + ALLYMORF.VQA \ + APCESCPE.VQA \ + ASSESS.VQA \ + BATTLE.VQA \ + 1BINOC.VQA \ + BMAP.VQA \ + BRDGTILT.VQA \ + CRONTEST.VQA \ + CRONFAIL.VQA \ + DESTROYR.VQA \ + DUD.VQA \ + ELEVATOR.VQA \ + FLARE.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + LANDING.VQA \ + MASASSLT.VQA \ + MCV.VQA \ + MCV_LAND.VQA \ + MONTPASS.VQA \ + OILDRUM.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + SHIPSINK.VQA \ + SHORBOM1.VQA \ + SHORBOM2.VQA \ + SHORBOMB.VQA \ + SNOWBOMB.VQA \ + SOVIET1.VQA \ + SOVTSTAR.VQA \ + SPY.VQA \ + TANYA1.VQA \ + TANYA2.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ +# TRAILER.VQA \ + +SOVIETVQ = \ + AAGUN.VQA \ + CRONFAIL.VQA \ + AIRFIELD.VQA \ + ALLY1.VQA \ + ALLYMORF.VQA \ + AVERTED.VQA \ + BEACHEAD.VQA \ + BMAP.VQA \ + BOMBRUN.VQA \ + COUNTDWN.VQA \ + DOUBLE.VQA \ + DPTHCHRG.VQA \ + EXECUTE.VQA \ + FLARE.VQA \ + LANDING.VQA \ + MCVBRDGE.VQA \ + MIG.VQA \ + MOVINGIN.VQA \ + MTNKFACT.VQA \ + NUKESTOK.VQA \ + ONTHPRWL.VQA \ + PERISCOP.VQA \ + PROLOG.VQA \ + RADRRAID.VQA \ + REDINTRO.VQA \ + SEARCH.VQA \ + SFROZEN.VQA \ + SITDUCK.VQA \ + SLNTSRVC.VQA \ + SNOWBOMB.VQA \ + SNSTRAFE.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + SOVFINAL.VQA \ + SOVIET1.VQA \ + SOVIET10.VQA \ + SOVIET11.VQA \ + SOVIET12.VQA \ + SOVIET13.VQA \ + SOVIET14.VQA \ + SOVIET2.VQA \ + SOVIET3.VQA \ + SOVIET4.VQA \ + SOVIET5.VQA \ + SOVIET6.VQA \ + SOVIET7.VQA \ + SOVIET8.VQA \ + SOVIET9.VQA \ + SOVMCV.VQA \ + SOVTSTAR.VQA \ + SPOTTER.VQA \ + STRAFE.VQA \ + TAKE_OFF.VQA \ + TESLA.VQA \ + V2ROCKET.VQA \ +# TRAILER.VQA \ + +ALLIESVQ = \ + AFTRMATH.VQA \ + ALLY1.VQA \ + ALLYMORF.VQA \ + APCESCPE.VQA \ + BATTLE.VQA \ + BMAP.VQA \ + CRONFAIL.VQA \ + DPTHCHRG.VQA \ + EXECUTE.VQA \ + FLARE.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + LANDING.VQA \ + MASASSLT.VQA \ + NUKESTOK.VQA \ + ONTHPRWL.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + SFROZEN.VQA \ + SLNTSRVC.VQA \ + SNOWBOMB.VQA \ + SNOWBASE.VQA \ + SOVMCV.VQA \ + SNSTRAFE.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + SOVIET1.VQA \ + SOVTSTAR.VQA \ + SPY.VQA \ + STRAFE.VQA \ + TESLA.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ + V2ROCKET.VQA \ +# ANTEND.VQA \ +# ANTINTRO.VQA \ + + + +# Files required for hires/Win95 version only +# +# This mix file is not cached +# +NOCACHEHIRESFILES= \ + ENGLISH.VQA \ + $(ALLIESVQ:.VQA=.VQP) \ + $(SOVIETVQ:.VQA=.VQP) \ + + +LINTOBJECTS1 = $(OBJECTS:,=) +LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB) + +# Mixfiles that should reside on the CD-ROM drive. +CD1MIXFILES = \ + CONQUER.MIX \ + EDHI.MIX \ + EDLO.MIX \ + GENERAL.MIX \ + INTERIOR.MIX \ + MOVIES1.MIX \ + SCORES.MIX \ + SNOW.MIX \ + SOUNDS.MIX \ + RUSSIAN.MIX \ + ALLIES.MIX \ + TEMPERAT.MIX \ + + + +# Mixfiles that should reside on the hard drive. +LOCALMIXFILES = \ + EDITOR.MIX \ + HIRES.MIX \ + LOCAL.MIX \ + LORES.MIX \ + NCHIRES.MIX \ + SPEECH.MIX \ + + +# Mixfiles as they appear on the CD and hard drive. +PACKFILES= $(.path.cd1)MAIN.MIX EXPAND2.MIX $(.path.cd1)tobreaki\REDALERT.MIX + + +# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING +EXPANDFILES= \ + ANT1.SHP \ + ANT2.SHP \ + ANT3.SHP \ + QUEE.SHP \ + CREDITS.ENG \ + HILL01.TEM \ + ANTBITE.AUD \ + ANTDIE.AUD \ + ANTDIE.SHP \ + LAR1.SHP \ + LAR2.SHP \ + TITLE.PCX \ + MISSION.INI \ + BUZZY1.AUD \ + STAVCMDR.AUD \ + STAVCRSE.AUD \ + STAVYES.AUD \ + STAVMOV.AUD \ + CONQUER.ENG \ + RAMBO1.AUD \ + RAMBO2.AUD \ + RAMBO3.AUD \ + TITLE.CPS \ + TUTORIAL.INI \ + BMAP.VQP \ + ANTEND.VQP \ + ANTINTRO.VQP \ + +# Aftermath expansion files +EXPAND2FILES= \ + CARR.SHP \ + CTNK.SHP \ + DTRK.SHP \ + MSUB.SHP \ + QTNK.SHP \ + TTNK.SHP \ + STNK.SHP \ + AFTRMATH.INI \ + ANT1.SHP \ + ANT2.SHP \ + ANT3.SHP \ + ANTBITE.AUD \ + ANTDIE.AUD \ + ANTDIE.SHP \ + BUZZY1.AUD \ + CONQUER.ENG \ + CREDITS.TXT \ + HILL01.TEM \ + LAR1.SHP \ + LAR2.SHP \ + MISSION.INI \ + MPLAYER.INI \ + QUEE.SHP \ + STAVCMDR.AUD \ + STAVCRSE.AUD \ + STAVYES.AUD \ + STAVMOV.AUD \ + TANK01.AUD \ + TITLE.PCX \ + TITLE.CPS \ + TUTORIAL.INI \ + BMAP.VQP \ + stup_fix.shp \ + + + +############################################################# +# Rebuilds all the mixfiles. +packfiles: always $(PACKFILES) + +always: + copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u + + +#################################################################### +# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile. +$(.path.cd1)MAIN.MIX: $(CD1MIXFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.cd1)$&.mix + + +# All mixfiles that exist in the local directory are embedded within this mega-mixfile. +$(.path.cd1)install\REDALERT.MIX: $(LOCALMIXFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.cd1)install\$&.mix + + +#################################################################### +# These are the various sub-mixfiles. +CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini + UTILS\MIXFILE -k -h -I$(.path.cps) &&! + $(CONQUERFILES) $(CACHEMAP) +! $(.path.mix)$&.mix + +TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(TEMPERATEFILES) +! $(.path.mix)$&.mix + +SNOW.MIX: $(SNOWFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(SNOWFILES) +! $(.path.mix)$&.mix + +INTERIOR.MIX: $(INTERIORFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(INTERIORFILES) +! $(.path.mix)$&.mix + +GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) +! $(.path.mix)$&.mix + +SCORES.MIX: $(SCOREFILES) + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.mix)$&.mix + +SOUNDS.MIX: $(WAVFILES) $(SFX) + UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +LIMITED.MIX: BLEEP11.AUD + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +MOVIES1.MIX: $(ALLIESVQ) + UTILS\MIXFILE -k -I$(.path.vqa) &&! + $** +! $(.path.mix)$&.mix + + + + + +NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI) + UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&! + $(NOCACHEHIRESFILES) +! $(.path.mix)$&.mix + +LOCAL.MIX: $(LOCALFILES) .\key.ini + UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&! + $(LOCALFILES) +! $(.path.mix)$&.mix + +LORES.MIX: $(LOHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&! + $(LOHILORES) +! $(.path.mix)$&.mix + +HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&! + $(HIRESFILES:.SHP=.HI) $(HIHILORES) +! $(.path.mix)$&.mix + +LORES1.MIX: $(LOHILORES1) .\key.ini + UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&! + $(LOHILORES1) +! $(.path.mix)$&.mix + +HIRES1.MIX: $(HIHILORES1) .\key.ini + UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&! + $(HIHILORES1) +! $(.path.mix)$&.mix + +SPEECH.MIX: $(SPEECHFILES) + UTILS\MIXFILE -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + + +EXPAND.MIX: $(EXPANDFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +EXPAND2.MIX: $(EXPAND2FILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +############################################################# +# Special rule to create the mouse shape (which must be a shape file) +mouse.hi: $(.path.anm)hires\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)hires\mouse.anm; + end; +! $(.path.hi)$&.hi $(SHAPEBUFFSIZE) + +# Special rule to create the mouse shape (which must be a shape file) +mouse.low: $(.path.anm)lores\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)lores\mouse.anm; + end; +! $(.path.low)$&.low $(SHAPEBUFFSIZE) + + +############################################################# +# Special build rule for radar animations so that they won't. +# +NATORADR.HI: $(.path.anm)hires\NATORADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + +USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + + +############################################################# +# Debug text file creation. +debug.eng: debug.txt + utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h + + diff --git a/CODE/BFILE2.MAK b/CODE/BFILE2.MAK new file mode 100644 index 0000000..aa12b21 --- /dev/null +++ b/CODE/BFILE2.MAK @@ -0,0 +1,2098 @@ +# +# 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 . +# + +# $Header: F:\projects\c&c0\vcs\code\bfile.mav 5.0 11 Nov 1996 09:40:38 JOE_BOSTIC $ +#*************************************************************************** +#** 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 * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : March 25, 1993 * +#* * +#* Last Update : March 25, 1993 [JLB] * +#* * +#*-------------------------------------------------------------------------* + + +# Comment out the following line to disable "include file autodependency". +.AUTODEPEND +#.SWAP + +!include "rules.mak" + + +########################################################################## + +MAPFILES = \ + +CACHEMAP = \ + BRIEFING.AUD \ + BAR3RED.SHP \ + BAR3BLU.SHP \ + COUNTRYA.SHP \ + COUNTRYE.SHP \ + CREDSA.SHP \ + CREDSU.SHP \ + HISCORE1.SHP \ + HISCORE2.SHP \ + TIME.SHP \ + CLOCK1.AUD \ + COUNTRY4.AUD \ + MAPWIPE2.AUD \ + MAPWIPE5.AUD \ + TONEY10.AUD \ + TONEY4.AUD \ + TONEY7.AUD \ + SFX4.AUD \ + BEEPY6.AUD \ + KEYSTROK.AUD \ + APPEAR1.AUD \ + SCOLD1.AUD \ + COUNTRY1.AUD \ + ALI-TRAN.WSA \ + SOV-TRAN.WSA \ + ALIBACKH.PCX \ + SOVBACKH.PCX \ + BAR3RHR.SHP \ + BAR3BHR.SHP \ + CREDSAHR.SHP \ + CREDSUHR.SHP \ + HISC1-HR.SHP \ + HISC2-HR.SHP \ + TIMEHR.SHP \ + MLTIPLYR.WSA \ + +LOCALFILES = \ + PROLOG.CPS \ + MAP.AUD \ + TITLE.CPS \ + PALETTE.CPS \ + INTRO.AUD \ + EGOPAL.PAL \ + RULES.INI \ + CREDITS.TXT \ + ALIPAPER.CPS \ + 3POINT.FNT \ + 8POINT.FNT \ + EDITFNT.FNT \ + CONQUER.ENG \ + DEBUG.ENG \ + LED.FNT \ + SNOW.PAL \ + TEMPERAT.PAL \ + INTERIOR.PAL \ + VCR.FNT \ + HOLE0000.LUT \ + HOLE0001.LUT \ + HOLE0002.LUT \ + HOLE0003.LUT \ + HOLE0004.LUT \ + HOLE0005.LUT \ + HOLE0006.LUT \ + HOLE0007.LUT \ + HOLE0008.LUT \ + HOLE0009.LUT \ + HOLE0010.LUT \ + HOLE0011.LUT \ + HOLE0012.LUT \ + HOLE0013.LUT \ + HOLE0014.LUT \ + HOLE0015.LUT \ + HOLE0016.LUT \ + HOLE0017.LUT \ + HOLE0018.LUT \ + HOLE0019.LUT \ + HOLE0020.LUT \ + HOLE0021.LUT \ + HOLE0022.LUT \ + HOLE0023.LUT \ + HOLE0024.LUT \ + HOLE0025.LUT \ + HOLE0026.LUT \ + HOLE0027.LUT \ + HOLE0028.LUT \ + HOLE0029.LUT \ + HOLE0030.LUT \ + HOLE0031.LUT \ + HOLE0032.LUT \ + HOLE0033.LUT \ + HOLE0034.LUT \ + HOLE0035.LUT \ + HOLE0036.LUT \ + HOLE0037.LUT \ + HOLE0038.LUT \ + HOLE0039.LUT \ + HOLE0040.LUT \ + HOLE0041.LUT \ + HOLE0042.LUT \ + HOLE0043.LUT \ + HOLE0044.LUT \ + HOLE0045.LUT \ + HOLE0046.LUT \ + HOLE0047.LUT \ +# TEMPSCOR.FNT \ +# 6POINT.FNT \ +# GRAD6FNT.FNT \ +# SCOREFNT.FNT \ + + +# Files that have counterparts in both high and low resolutions. +# These files will be built into the HIRES.MIX and LORES.MIX files. +HILORES = \ + TRANICON.SHP \ + PIPS.SHP \ + PULSE.SHP \ + ATOMICON.SHP \ + WARPICON.SHP \ + C1.SHP \ + C2.SHP \ + CHAN.SHP \ + DELPHI.SHP \ + E1.SHP \ + E2.SHP \ + E3.SHP \ + E4.SHP \ + E5.SHP \ + E6.SHP \ + E7.SHP \ + EINSTEIN.SHP \ + GNRL.SHP \ + MECH.SHP \ + MEDI.SHP \ + SHOK.SHP \ + SPY.SHP \ + THF.SHP \ + DD-BKGND.SHP \ + DD-BOTM.SHP \ + DD-CRNR.SHP \ + DD-EDGE.SHP \ + DD-LEFT.SHP \ + DD-RIGHT.SHP \ + DD-TOP.SHP \ + 12METFNT.FNT \ + GRAD6FNT.FNT \ + HELP.FNT \ + 6POINT.FNT \ + TYPE.FNT \ + SCOREFNT.FNT \ + 1TNKICON.SHP \ + 2TNKICON.SHP \ + 3TNKICON.SHP \ + 4TNKICON.SHP \ + AFLDICON.SHP \ + AGUNICON.SHP \ + APCICON.SHP \ + APWRICON.SHP \ + ARTYICON.SHP \ + ATEKICON.SHP \ + BADRICON.SHP \ + BARRICON.SHP \ + BRIKICON.SHP \ + BTN-DN.SHP \ + BTN-PL.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + CAICON.SHP \ + CAMICON.SHP \ + CARRICON.SHP \ + CLOCK.SHP \ + CTNKICON.SHP \ + DDICON.SHP \ + DOGICON.SHP \ + DOMEICON.SHP \ + DOMFICON.SHP \ + DTRKICON.SHP \ + E1ICON.SHP \ + E2ICON.SHP \ + E3ICON.SHP \ + E4ICON.SHP \ + E6ICON.SHP \ + E7ICON.SHP \ + FACFICON.SHP \ + FACTICON.SHP \ + FENCICON.SHP \ + FIXICON.SHP \ + FTURICON.SHP \ + GAPICON.SHP \ + GPSSICON.SHP \ + GUNICON.SHP \ + HARVICON.SHP \ + HBOXICON.SHP \ + HELIICON.SHP \ + HINDICON.SHP \ + HPADICON.SHP \ + INFXICON.SHP \ + IRONICON.SHP \ + JEEPICON.SHP \ + KENNICON.SHP \ + LSTICON.SHP \ + MAP.SHP \ + MCVICON.SHP \ + MECHICON.SHP \ + MEDIICON.SHP \ + MGGICON.SHP \ + MIGICON.SHP \ + MNLYICON.SHP \ + MOUSE.SHP \ + MRJICON.SHP \ + MSLOICON.SHP \ + MSUBICON.SHP \ + NATORADR.SHP \ + PBMBICON.SHP \ + PBOXICON.SHP \ + PDOXICON.SHP \ + PINFICON.SHP \ + POWER.SHP \ + POWERBAR.SHP \ + POWRICON.SHP \ + PROCICON.SHP \ + PTICON.SHP \ + QTNKICON.SHP \ + REPAIR.SHP \ + SAMICON.SHP \ + SBAGICON.SHP \ + SELL.SHP \ + SHOKICON.SHP \ + SIDEBAR.SHP \ + SILOICON.SHP \ + SMIGICON.SHP \ + SONRICON.SHP \ + SOVPAPER.CPS \ + SPEFICON.SHP \ + SPENICON.SHP \ + SPYICON.SHP \ + SSICON.SHP \ + STEKICON.SHP \ + STRIP.SHP \ + STRIPDN.SHP \ + STRIPUP.SHP \ + SYRDICON.SHP \ + SYRFICON.SHP \ + TABS.SHP \ + TENTICON.SHP \ + THFICON.SHP \ + TRUKICON.SHP \ + TTNKICON.SHP \ + TSLAICON.SHP \ + U2ICON.SHP \ + USSRRADR.SHP \ + V2RLICON.SHP \ + WEAFICON.SHP \ + WEAPICON.SHP \ + YAKICON.SHP \ + NRADRFRM.SHP \ + URADRFRM.SHP \ + SIDE1NA.SHP \ + SIDE1US.SHP \ + SIDE2NA.SHP \ + SIDE2US.SHP \ + SIDE3NA.SHP \ + SIDE3US.SHP \ + STRIPNA.SHP \ + STRIPUS.SHP \ + +# MOEBICON.SHP \ + +# These helper macros substitute the extension so that +# the appropriate art build rule will be invoked. +xLOHILORES = $(HILORES:.SHP=.LOW) +LOHILORES = $(xLOHILORES:.FNT=.LNT) +xHIHILORES = $(HILORES:.SHP=.HI) +HIHILORES = $(xHIHILORES:.FNT=.HNT) + +# +# Files required for hires/Win95 version only +# +# This mix file is cached +# +HIRESFILES = \ + ALIPAPER.PCX \ + PROLOG.PCX \ + SOVPAPER.PCX \ + AFTR_HI.PCX \ + ALY1.PCX \ + APC_HI.PCX \ + APHI0049.PCX \ + BNHI0020.PCX \ + DCHI0040.PCX \ + FRHI0166.PCX \ + LAB.PCX \ + LANDSBRG.PCX \ + MAHI0107.PCX \ + MIG_HI.PCX \ + MTFACTHI.PCX \ + NEEDLE.PCX \ + SOV2.PCX \ + SPY.PCX \ + STALIN.PCX \ + TENT.PCX \ +# ENG_HI.PCX \ + + +CONQUERFILES = \ + PARABOMB.SHP \ + RADARFRM.SHP \ + ARMOR.SHP \ + FPOWER.SHP \ + SPEED.SHP \ + TQUAKE.SHP \ + H2O_EXP1.SHP \ + H2O_EXP2.SHP \ + H2O_EXP3.SHP \ + FLAK.SHP \ + EBTN-DN.SHP \ + EBTN-UP.SHP \ + ATOMSFX.SHP \ + TWINKLE1.SHP \ + TWINKLE2.SHP \ + TWINKLE3.SHP \ + CHRONBOX.SHP \ + GPSBOX.SHP \ + INVULBOX.SHP \ + PARABOX.SHP \ + SONARBOX.SHP \ + SPUTNIK.SHP \ + SPUTDOOR.SHP \ + ATOMICDN.SHP \ + ATOMICUP.SHP \ + TYPE.FNT \ + 120MM.SHP \ + 1TNK.SHP \ + 2TNK.SHP \ + 3TNK.SHP \ + 4TNK.SHP \ + 50CAL.SHP \ + AFLD.SHP \ + AGUN.SHP \ + APC.SHP \ + APWR.SHP \ + ART-EXP1.SHP \ + ARTY.SHP \ + ATEK.SHP \ + BADR.SHP \ + BARB.SHP \ + BARL.SHP \ + BARR.SHP \ + BIO.SHP \ + BOMB.SHP \ + BOMBLET.SHP \ + BRIK.SHP \ + BRL3.SHP \ + BURN-L.SHP \ + BURN-M.SHP \ + BURN-S.SHP \ + CA.SHP \ + CYCL.SHP \ + DD.SHP \ + DEVIATOR.SHP \ + DOG.SHP \ + DOGBULLT.SHP \ + DOLLAR.SHP \ + DOME.SHP \ + DRAGON.SHP \ + EARTH.SHP \ + ELECTDOG.SHP \ + EMPULSE.SHP \ + FACT.SHP \ + FB1.SHP \ + FB2.SHP \ + FBALL1.SHP \ + FCOM.SHP \ + FENC.SHP \ + FIRE1.SHP \ + FIRE2.SHP \ + FIRE3.SHP \ + FIRE4.SHP \ + FIX.SHP \ + FLAGFLY.SHP \ + FLMSPT.SHP \ + FPLS.SHP \ + FRAG1.SHP \ + FTNK.SHP \ + FTUR.SHP \ + GAP.SHP \ + GUN.SHP \ + GUNFIRE.SHP \ + HARV.SHP \ + HELI.SHP \ + HIND.SHP \ + HOSP.SHP \ + HPAD.SHP \ + INVUN.SHP \ + IRON.SHP \ + JEEP.SHP \ + KENN.SHP \ + LITNING.SHP \ + LROTOR.SHP \ + LST.SHP \ + MCV.SHP \ + MGG.SHP \ + MGUN.SHP \ + MHQ.SHP \ + MIG.SHP \ + MINE.SHP \ + MINIGUN.SHP \ + MINP.SHP \ + MINV.SHP \ + MISS.SHP \ + MISSILE.SHP \ + MISSILE2.SHP \ + MLRS.SHP \ + MNLY.SHP \ + MRJ.SHP \ + NAPALM1.SHP \ + NAPALM2.SHP \ + NAPALM3.SHP \ + ORCA.SHP \ + PARACH.SHP \ + PATRIOT.SHP \ + PBOX.SHP \ + PDOX.SHP \ + PIFF.SHP \ + PIFFPIFF.SHP \ + POWR.SHP \ + PROC.SHP \ + PT.SHP \ + RAPID.SHP \ + RROTOR.SHP \ + SAM.SHP \ + SAMFIRE.SHP \ + SBAG.SHP \ + SCRATE.SHP \ + SELECT.SHP \ + SHADOW.SHP \ + SILO.SHP \ + SMIG.SHP \ + SMOKEY.SHP \ + SMOKE_M.SHP \ + SMOKLAND.SHP \ + SPEN.SHP \ + SS.SHP \ + SSAM.SHP \ + STEALTH2.SHP \ + STEK.SHP \ + STNK.SHP \ + SYRD.SHP \ + TENT.SHP \ + TRAN.SHP \ + TRANS.ICN \ + TRUK.SHP \ + TSLA.SHP \ + TURR.SHP \ + U2.SHP \ + V19.SHP \ + V2.SHP \ + V2RL.SHP \ + VEH-HIT1.SHP \ + VEH-HIT2.SHP \ + VEH-HIT3.SHP \ + WAKE.SHP \ + WCRATE.SHP \ + WWCRATE.SHP \ + WEAP.SHP \ + WEAP2.SHP \ + WOOD.SHP \ + YAK.SHP \ + AFLDMAKE.SHP \ + AGUNMAKE.SHP \ + APWRMAKE.SHP \ + ATEKMAKE.SHP \ + BARRMAKE.SHP \ + BIOMAKE.SHP \ + DOMEMAKE.SHP \ + FACTMAKE.SHP \ + FIXMAKE.SHP \ + FTURMAKE.SHP \ + GAPMAKE.SHP \ + GUNMAKE.SHP \ + HOSPMAKE.SHP \ + HPADMAKE.SHP \ + IRONMAKE.SHP \ + KENNMAKE.SHP \ + MINPMAKE.SHP \ + MINVMAKE.SHP \ + PBOXMAKE.SHP \ + POWRMAKE.SHP \ + PDOXMAKE.SHP \ + PROCMAKE.SHP \ + PUMPMAKE.SHP \ + SAMMAKE.SHP \ + SILOMAKE.SHP \ + SPENMAKE.SHP \ + STEKMAKE.SHP \ + SYRDMAKE.SHP \ + TENTMAKE.SHP \ + TSLAMAKE.SHP \ + WEAPMAKE.SHP \ + +GENERALMAPFILES = \ + MISSIONS.PKT \ + TUTORIAL.INI \ + SCA01EA.INI \ + SCA02EA.INI \ + SCA03EA.INI \ + SCA04EA.INI \ + SCG01EA.INI \ + SCG40EA.INI \ + SCG41EA.INI \ + SCG42EA.INI \ + SCG43EA.INI \ + SCG44EA.INI \ + SCG45EA.INI \ + SCG46EA.INI \ + SCG47EA.INI \ + SCG48EA.INI \ + SCU40EA.INI \ + SCU41EA.INI \ + SCU42EA.INI \ + SCU43EA.INI \ + SCU44EA.INI \ + SCU45EA.INI \ + SCU46EA.INI \ + SCU47EA.INI \ + SCU48EA.INI \ + SCU01EA.INI \ + SCM01EA.INI \ + SCM02EA.INI \ + SCM03EA.INI \ + SCM04EA.INI \ + SCM05EA.INI \ + SCM06EA.INI \ + SCM07EA.INI \ + SCM08EA.INI \ + SCM09EA.INI \ + SCM10EA.INI \ + SCM11EA.INI \ + SCM12EA.INI \ + SCM13EA.INI \ + SCM14EA.INI \ + SCM15EA.INI \ + SCM16EA.INI \ + SCM17EA.INI \ + SCM18EA.INI \ + SCM19EA.INI \ + SCM20EA.INI \ + SCM21EA.INI \ + SCM22EA.INI \ + SCM23EA.INI \ + SCM24EA.INI \ + SCM25EA.INI \ + SCM26EA.INI \ + SCM27EA.INI \ + SCM28EA.INI \ + SCM29EA.INI \ + SCM30EA.INI \ + SCM31EA.INI \ + SCM32EA.INI \ + SCM33EA.INI \ + SCM34EA.INI \ + SCM35EA.INI \ + SCM36EA.INI \ + SCM37EA.INI \ + SCM38EA.INI \ + SCM39EA.INI \ + SCM40EA.INI \ + SCM41EA.INI \ + SCM42EA.INI \ + SCM43EA.INI \ + SCM44EA.INI \ + SCM45EA.INI \ + SCM46EA.INI \ + SCM47EA.INI \ + SCM48EA.INI \ + SCM49EA.INI \ + SCM50EA.INI \ + SCM51EA.INI \ + SCM52EA.INI \ + SCM53EA.INI \ + SCM54EA.INI \ + SCM55EA.INI \ + SCM56EA.INI \ + SCM57EA.INI \ + SCM58EA.INI \ + SCM59EA.INI \ + SCM60EA.INI \ + SCM61EA.INI \ + SCM62EA.INI \ + SCM63EA.INI \ + SCM64EA.INI \ + SCM65EA.INI \ + SCM66EA.INI \ + SCM67EA.INI \ + SCM68EA.INI \ + SCM69EA.INI \ + SCM70EA.INI \ + SCM71EA.INI \ + SCM72EA.INI \ + SCM73EA.INI \ + SCM74EA.INI \ + SCM75EA.INI \ + SCM76EA.INI \ + SCM77EA.INI \ + SCM78EA.INI \ + SCM79EA.INI \ + SCM80EA.INI \ + SCM81EA.INI \ + SCM82EA.INI \ + SCM83EA.INI \ + SCM84EA.INI \ + SCM85EA.INI \ + SCM86EA.INI \ + SCM87EA.INI \ + SCM88EA.INI \ + SCM89EA.INI \ + SCM90EA.INI \ + SCM91EA.INI \ + SCM92EA.INI \ + SCM93EA.INI \ + SCM94EA.INI \ + SCM95EA.INI \ + SCM96EA.INI \ + SCM97EA.INI \ + SCM98EA.INI \ + SCM99EA.INI \ + SCM100EA.INI \ + SCM101EA.INI \ + SCM102EA.INI \ + SCM103EA.INI \ + SCM104EA.INI \ + SCM105EA.INI \ + SCM106EA.INI \ + SCM107EA.INI \ + SCM108EA.INI \ + SCM109EA.INI \ + SCM110EA.INI \ + SCM111EA.INI \ + SCM112EA.INI \ + SCM113EA.INI \ + SCM114EA.INI \ + SCM115EA.INI \ + SCM116EA.INI \ + SCM117EA.INI \ + SCM118EA.INI \ + SCM119EA.INI \ + SCM120EA.INI \ + SCM121EA.INI \ + SCM122EA.INI \ + SCM123EA.INI \ + SCM124EA.INI \ + SCM125EA.INI \ + SCM126EA.INI \ + SCM127EA.INI \ + SCM128EA.INI \ + SCM129EA.INI \ + SCM130EA.INI \ + SCMD0EA.INI \ + SCMD1EA.INI \ + SCMD2EA.INI \ + SCMD3EA.INI \ + SCMD4EA.INI \ + SCMD5EA.INI \ + SCMD6EA.INI \ + SCMD7EA.INI \ + SCMD8EA.INI \ + SCMD9EA.INI \ + SCME0EA.INI \ + SCME1EA.INI \ + SCME2EA.INI \ + SCME3EA.INI \ + SCME4EA.INI \ + SCME5EA.INI \ + SCME6EA.INI \ + SCME7EA.INI \ + SCME8EA.INI \ + SCME9EA.INI \ + SCMF0EA.INI \ + SCMF1EA.INI \ + SCMF2EA.INI \ + SCMF3EA.INI \ + SCMF4EA.INI \ + SCMF5EA.INI \ + SCMF6EA.INI \ + SCMF7EA.INI \ + SCMF8EA.INI \ + SCMF9EA.INI \ + SCMG0EA.INI \ + SCMG1EA.INI \ + SCMG2EA.INI \ + SCMG3EA.INI \ + SCMG4EA.INI \ + SCMG5EA.INI \ + SCMG6EA.INI \ + SCMG7EA.INI \ + SCMG8EA.INI \ + SCMG9EA.INI \ + SCMH0EA.INI \ + SCMH1EA.INI \ + SCMH2EA.INI \ + SCMH3EA.INI \ + SCMH4EA.INI \ + SCMH5EA.INI \ + SCMH6EA.INI \ + SCMH7EA.INI \ + SCMH8EA.INI \ + SCMH9EA.INI \ + SCMI0EA.INI \ + SCMI1EA.INI \ + SCMI2EA.INI \ + SCMI3EA.INI \ + SCMI4EA.INI \ + SCMI5EA.INI \ + SCMI6EA.INI \ + SCMI7EA.INI \ + SCMI8EA.INI \ + SCMI9EA.INI \ + SCMJ0EA.INI \ + SCMJ1EA.INI \ + SCMJ2EA.INI \ + SCMJ3EA.INI \ + SCMJ4EA.INI \ + SCMJ5EA.INI \ + SCMJ6EA.INI \ + SCMJ7EA.INI \ + SCMJ8EA.INI \ + SCMJ9EA.INI \ + SCMK0EA.INI \ + SCMK1EA.INI \ + SCMK2EA.INI \ + SCMK3EA.INI \ + SCMK4EA.INI \ + SCMK5EA.INI \ + SCMK6EA.INI \ + SCMK7EA.INI \ + SCMK8EA.INI \ + SCMK9EA.INI \ + SCML0EA.INI \ + SCML1EA.INI \ + SCML2EA.INI \ + SCML3EA.INI \ + SCML4EA.INI \ + SCML5EA.INI \ + SCML6EA.INI \ + SCML7EA.INI \ + SCML8EA.INI \ + SCML9EA.INI \ + SCMM0EA.INI \ + SCMM1EA.INI \ + SCMM2EA.INI \ + SCMM3EA.INI \ + SCMM4EA.INI \ + SCMM5EA.INI \ + SCMM6EA.INI \ + SCMM7EA.INI \ + SCMM8EA.INI \ + SCMM9EA.INI \ + +NETMAPFILES = \ + +# Files that aren't cached. +GENERALFILES = \ + AFTR_LO.CPS \ + ALY1-LO.CPS \ + APC_LO.CPS \ + APLO0049.CPS \ + BNLO0020.CPS \ + DCLO0040.CPS \ + FRLO0166.CPS \ + LAB-LO.CPS \ + LANDS-LO.CPS \ + MALO0107.CPS \ + MIG_LO.CPS \ + MTFACTLO.CPS \ + NEEDL-LO.CPS \ + SOV2-LO.CPS \ + SPY-LO.CPS \ + STALN-LO.CPS \ + TENT-LO.CPS \ + TITLE.CPS \ + PPAPER.CPS \ + MSAA.WSA \ + MSAB.WSA \ + MSAC.WSA \ + MSAD.WSA \ + MSAE.WSA \ + MSAF.WSA \ + MSAG.WSA \ + MSAH.WSA \ + MSAI.WSA \ + MSAJ.WSA \ + MSAK.WSA \ + MSAL.WSA \ + MSAM.WSA \ + MSAN.WSA \ + MSSA.WSA \ + MSSB.WSA \ + MSSC.WSA \ + MSSD.WSA \ + MSSE.WSA \ + MSSF.WSA \ + MSSG.WSA \ + MSSH.WSA \ + MSSI.WSA \ + MSSJ.WSA \ + MSSK.WSA \ + MSSL.WSA \ + MSSM.WSA \ + MSSN.WSA \ + +INTERIORFILES = \ + BOXES01.INT \ + BOXES02.INT \ + BOXES03.INT \ + BOXES04.INT \ + BOXES05.INT \ + BOXES06.INT \ + BOXES07.INT \ + BOXES08.INT \ + BOXES09.INT \ + XTRA0001.INT \ + XTRA0002.INT \ + XTRA0003.INT \ + XTRA0004.INT \ + XTRA0005.INT \ + XTRA0006.INT \ + XTRA0007.INT \ + XTRA0008.INT \ + XTRA0009.INT \ + XTRA0010.INT \ + XTRA0011.INT \ + XTRA0012.INT \ + XTRA0013.INT \ + XTRA0014.INT \ + XTRA0015.INT \ + XTRA0016.INT \ + CLEAR1.INT \ + MOVEFLSH.INT \ + ARRO0001.INT \ + ARRO0002.INT \ + ARRO0003.INT \ + ARRO0004.INT \ + ARRO0005.INT \ + ARRO0006.INT \ + ARRO0007.INT \ + ARRO0008.INT \ + ARRO0009.INT \ + ARRO0010.INT \ + ARRO0011.INT \ + ARRO0012.INT \ + ARRO0013.INT \ + ARRO0014.INT \ + ARRO0015.INT \ + FLOR0001.INT \ + FLOR0002.INT \ + FLOR0003.INT \ + FLOR0004.INT \ + FLOR0005.INT \ + FLOR0006.INT \ + FLOR0007.INT \ + GFLR0001.INT \ + GFLR0002.INT \ + GFLR0003.INT \ + GFLR0004.INT \ + GFLR0005.INT \ + GSTR0001.INT \ + GSTR0002.INT \ + GSTR0003.INT \ + GSTR0004.INT \ + GSTR0005.INT \ + GSTR0006.INT \ + GSTR0007.INT \ + GSTR0008.INT \ + GSTR0009.INT \ + GSTR0010.INT \ + GSTR0011.INT \ + LWAL0001.INT \ + LWAL0002.INT \ + LWAL0003.INT \ + LWAL0004.INT \ + LWAL0005.INT \ + LWAL0006.INT \ + LWAL0007.INT \ + LWAL0008.INT \ + LWAL0009.INT \ + LWAL0010.INT \ + LWAL0011.INT \ + LWAL0012.INT \ + LWAL0013.INT \ + LWAL0014.INT \ + LWAL0015.INT \ + LWAL0016.INT \ + LWAL0017.INT \ + LWAL0018.INT \ + LWAL0019.INT \ + LWAL0020.INT \ + LWAL0021.INT \ + LWAL0022.INT \ + LWAL0023.INT \ + LWAL0024.INT \ + LWAL0025.INT \ + LWAL0026.INT \ + LWAL0027.INT \ + STRP0001.INT \ + STRP0002.INT \ + STRP0003.INT \ + STRP0004.INT \ + STRP0005.INT \ + STRP0006.INT \ + STRP0007.INT \ + STRP0008.INT \ + STRP0009.INT \ + STRP0010.INT \ + STRP0011.INT \ + WALL0001.INT \ + WALL0002.INT \ + WALL0003.INT \ + WALL0004.INT \ + WALL0005.INT \ + WALL0006.INT \ + WALL0007.INT \ + WALL0008.INT \ + WALL0009.INT \ + WALL0010.INT \ + WALL0011.INT \ + WALL0012.INT \ + WALL0013.INT \ + WALL0014.INT \ + WALL0015.INT \ + WALL0016.INT \ + WALL0017.INT \ + WALL0018.INT \ + WALL0019.INT \ + WALL0020.INT \ + WALL0021.INT \ + WALL0022.INT \ + WALL0023.INT \ + WALL0024.INT \ + WALL0025.INT \ + WALL0026.INT \ + WALL0027.INT \ + WALL0028.INT \ + WALL0029.INT \ + WALL0030.INT \ + WALL0031.INT \ + WALL0032.INT \ + WALL0033.INT \ + WALL0034.INT \ + WALL0035.INT \ + WALL0036.INT \ + WALL0037.INT \ + WALL0038.INT \ + WALL0039.INT \ + WALL0040.INT \ + WALL0041.INT \ + WALL0042.INT \ + WALL0043.INT \ + WALL0044.INT \ + WALL0045.INT \ + WALL0046.INT \ + WALL0047.INT \ + WALL0048.INT \ + WALL0049.INT \ + +# Both the temperate and snow sets have identical template entries. +TEMPERATEFILES = \ + MINE.TEM \ + ICE01.TEM \ + ICE02.TEM \ + ICE03.TEM \ + ICE04.TEM \ + ICE05.TEM \ + MOVEFLSH.TEM \ + BR1X.TEM \ + BR2X.TEM \ + BRIDGE1X.TEM \ + BRIDGE2X.TEM \ + BRIDGE1H.TEM \ + BRIDGE2H.TEM \ + F01.TEM \ + F02.TEM \ + F03.TEM \ + F04.TEM \ + F05.TEM \ + F06.TEM \ + ELECTRO.TEM \ + B1.TEM \ + B2.TEM \ + B3.TEM \ + BIB1.TEM \ + BIB2.TEM \ + BIB3.TEM \ + BR1A.TEM \ + BR1B.TEM \ + BR1C.TEM \ + BR2A.TEM \ + BR2B.TEM \ + BR2C.TEM \ + BR3A.TEM \ + BR3B.TEM \ + BR3C.TEM \ + BR3D.TEM \ + BR3E.TEM \ + BR3F.TEM \ + BRIDGE1.TEM \ + BRIDGE1D.TEM \ + BRIDGE2.TEM \ + BRIDGE2D.TEM \ + CLEAR1.TEM \ + CORPSE1.TEM \ + CORPSE2.TEM \ + CORPSE3.TEM \ + CR1.TEM \ + CR2.TEM \ + CR3.TEM \ + CR4.TEM \ + CR5.TEM \ + CR6.TEM \ + D01.TEM \ + D02.TEM \ + D03.TEM \ + D04.TEM \ + D05.TEM \ + D06.TEM \ + D07.TEM \ + D08.TEM \ + D09.TEM \ + D10.TEM \ + D11.TEM \ + D12.TEM \ + D13.TEM \ + D14.TEM \ + D15.TEM \ + D16.TEM \ + D17.TEM \ + D18.TEM \ + D19.TEM \ + D20.TEM \ + D21.TEM \ + D22.TEM \ + D23.TEM \ + D24.TEM \ + D25.TEM \ + D26.TEM \ + D27.TEM \ + D28.TEM \ + D29.TEM \ + D30.TEM \ + D31.TEM \ + D32.TEM \ + D33.TEM \ + D34.TEM \ + D35.TEM \ + D36.TEM \ + D37.TEM \ + D38.TEM \ + D39.TEM \ + D40.TEM \ + D41.TEM \ + D42.TEM \ + D43.TEM \ + D44.TEM \ + D45.TEM \ + FALLS1.TEM \ + FALLS1A.TEM \ + FALLS2.TEM \ + FALLS2A.TEM \ + FORD1.TEM \ + FORD2.TEM \ + GEM01.TEM \ + GEM02.TEM \ + GEM03.TEM \ + GEM04.TEM \ + GOLD01.TEM \ + GOLD02.TEM \ + GOLD03.TEM \ + GOLD04.TEM \ + HBOX.TEM \ + MSLOMAKE.TEM \ + HBOXMAKE.TEM \ + MSLO.TEM \ + P01.TEM \ + P02.TEM \ + P03.TEM \ + P04.TEM \ + P07.TEM \ + P08.TEM \ + P13.TEM \ + P14.TEM \ + RC01.TEM \ + RC02.TEM \ + RC03.TEM \ + RC04.TEM \ + RF01.TEM \ + RF02.TEM \ + RF03.TEM \ + RF04.TEM \ + RF05.TEM \ + RF06.TEM \ + RF07.TEM \ + RF08.TEM \ + RF09.TEM \ + RF10.TEM \ + RF11.TEM \ + RV01.TEM \ + RV02.TEM \ + RV03.TEM \ + RV04.TEM \ + RV05.TEM \ + RV06.TEM \ + RV07.TEM \ + RV08.TEM \ + RV09.TEM \ + RV10.TEM \ + RV11.TEM \ + RV12.TEM \ + RV13.TEM \ + RV14.TEM \ + RV15.TEM \ + S01.TEM \ + S02.TEM \ + S03.TEM \ + S04.TEM \ + S05.TEM \ + S06.TEM \ + S07.TEM \ + S08.TEM \ + S09.TEM \ + S10.TEM \ + S11.TEM \ + S12.TEM \ + S13.TEM \ + S14.TEM \ + S15.TEM \ + S16.TEM \ + S17.TEM \ + S18.TEM \ + S19.TEM \ + S20.TEM \ + S21.TEM \ + S22.TEM \ + S23.TEM \ + S24.TEM \ + S25.TEM \ + S26.TEM \ + S27.TEM \ + S28.TEM \ + S29.TEM \ + S30.TEM \ + S31.TEM \ + S32.TEM \ + S33.TEM \ + S34.TEM \ + S35.TEM \ + S36.TEM \ + S37.TEM \ + S38.TEM \ + SC1.TEM \ + SC2.TEM \ + SC3.TEM \ + SC4.TEM \ + SC5.TEM \ + SC6.TEM \ + SH01.TEM \ + SH02.TEM \ + SH03.TEM \ + SH04.TEM \ + SH05.TEM \ + SH06.TEM \ + SH07.TEM \ + SH08.TEM \ + SH09.TEM \ + SH10.TEM \ + SH11.TEM \ + SH12.TEM \ + SH13.TEM \ + SH14.TEM \ + SH15.TEM \ + SH16.TEM \ + SH17.TEM \ + SH18.TEM \ + SH19.TEM \ + SH20.TEM \ + SH21.TEM \ + SH22.TEM \ + SH23.TEM \ + SH24.TEM \ + SH25.TEM \ + SH26.TEM \ + SH27.TEM \ + SH28.TEM \ + SH29.TEM \ + SH30.TEM \ + SH31.TEM \ + SH32.TEM \ + SH33.TEM \ + SH34.TEM \ + SH35.TEM \ + SH36.TEM \ + SH37.TEM \ + SH38.TEM \ + SH39.TEM \ + SH40.TEM \ + SH41.TEM \ + SH42.TEM \ + SH43.TEM \ + SH44.TEM \ + SH45.TEM \ + SH46.TEM \ + SH47.TEM \ + SH48.TEM \ + SH49.TEM \ + SH50.TEM \ + SH51.TEM \ + SH52.TEM \ + SH53.TEM \ + SH54.TEM \ + SH55.TEM \ + SH56.TEM \ + T01.TEM \ + T02.TEM \ + T03.TEM \ + T05.TEM \ + T06.TEM \ + T07.TEM \ + T08.TEM \ + T10.TEM \ + T11.TEM \ + T12.TEM \ + T13.TEM \ + T14.TEM \ + T15.TEM \ + T16.TEM \ + T17.TEM \ + TC01.TEM \ + TC02.TEM \ + TC03.TEM \ + TC04.TEM \ + TC05.TEM \ + V01.TEM \ + V02.TEM \ + V03.TEM \ + V04.TEM \ + V05.TEM \ + V06.TEM \ + V07.TEM \ + V08.TEM \ + V09.TEM \ + V10.TEM \ + V11.TEM \ + V12.TEM \ + V13.TEM \ + V14.TEM \ + V15.TEM \ + V16.TEM \ + V17.TEM \ + V18.TEM \ + W1.TEM \ + W2.TEM \ + WC01.TEM \ + WC02.TEM \ + WC03.TEM \ + WC04.TEM \ + WC05.TEM \ + WC06.TEM \ + WC07.TEM \ + WC08.TEM \ + WC09.TEM \ + WC10.TEM \ + WC11.TEM \ + WC12.TEM \ + WC13.TEM \ + WC14.TEM \ + WC15.TEM \ + WC16.TEM \ + WC17.TEM \ + WC18.TEM \ + WC19.TEM \ + WC20.TEM \ + WC21.TEM \ + WC22.TEM \ + WC23.TEM \ + WC24.TEM \ + WC25.TEM \ + WC26.TEM \ + WC27.TEM \ + WC28.TEM \ + WC29.TEM \ + WC30.TEM \ + WC31.TEM \ + WC32.TEM \ + WC33.TEM \ + WC34.TEM \ + WC35.TEM \ + WC36.TEM \ + WC37.TEM \ + WC38.TEM \ + +# Every temperate theater terrain file has a snow theater counterpart. +SNOWFILES = $(TEMPERATEFILES:.TEM=.SNO) + +# Sound effects (Juvenile or Adult) +SFX = \ + +# Generic wave files (never changes). +WAVFILES = \ + AACANON3.AUD \ + BEEPSLCT.AUD \ + BLEEP11.AUD \ + BLEEP12.AUD \ + BLEEP13.AUD \ + BLEEP17.AUD \ + BLEEP5.AUD \ + BLEEP6.AUD \ + BLEEP9.AUD \ + BOMBIT1.AUD \ + BUILD5.AUD \ + BUZZY1.AUD \ + CANNON1.AUD \ + CANNON2.AUD \ + CASHDN1.AUD \ + CASHTURN.AUD \ + CASHUP1.AUD \ + CHRONO2.AUD \ + CHROTNK1.AUD \ + CHUTE1.AUD \ + CMON1.AUD \ + CRMBLE2.AUD \ + DEDMAN1.AUD \ + DEDMAN10.AUD \ + DEDMAN2.AUD \ + DEDMAN3.AUD \ + DEDMAN4.AUD \ + DEDMAN5.AUD \ + DEDMAN6.AUD \ + DEDMAN7.AUD \ + DEDMAN8.AUD \ + DOGG5P.AUD \ + DOGW3PX.AUD \ + DOGW5.AUD \ + DOGW6.AUD \ + DOGW7.AUD \ + DOGY1.AUD \ + EAFFIRM1.AUD \ + EENGIN1.AUD \ + EINAH1.AUD \ + EINOK1.AUD \ + EINYES1.AUD \ + EMOVOUT1.AUD \ + EYESSIR1.AUD \ + FIREBL3.AUD \ + FIRETRT1.AUD \ + FIXIT1.AUD \ + GIRLOKAY.AUD \ + GIRLYEAH.AUD \ + GOTIT1.AUD \ + GRENADE1.AUD \ + GUN11.AUD \ + GUN13.AUD \ + GUN27.AUD \ + GUN5.AUD \ + GUYOKAY1.AUD \ + GUYYEAH1.AUD \ + H2OBOMB2.AUD \ + HEAL2.AUD \ + HYDROD1.AUD \ + INVUL2.AUD \ + IRONCUR9.AUD \ + JBURN1.AUD \ + JCHRGE1.AUD \ + JCRISP1.AUD \ + JDANCE1.AUD \ + JJUICE1.AUD \ + JJUMP1.AUD \ + JLIGHT1.AUD \ + JPOWER1.AUD \ + JSHOCK1.AUD \ + JYES1.AUD \ + KABOOM1.AUD \ + KABOOM12.AUD \ + KABOOM15.AUD \ + KABOOM22.AUD \ + KABOOM25.AUD \ + KABOOM30.AUD \ + KEEPEM1.AUD \ + LAUGH1.AUD \ + LEFTY1.AUD \ + MADCHRG2.AUD \ + MADEXPLO.AUD \ + MAFFIRM1.AUD \ + MBOSS1.AUD \ + MHEAR1.AUD \ + MHOTDIG1.AUD \ + MHOWDY1.AUD \ + MHUH1.AUD \ + MGUNINF1.AUD \ + MINE1.AUD \ + MINEBLO1.AUD \ + MINELAY1.AUD \ + MISSILE1.AUD \ + MISSILE6.AUD \ + MISSILE7.AUD \ + MLAFF1.AUD \ + MMOVOUT1.AUD \ + MRESPON1.AUD \ + MRISE1.AUD \ + MWRENCH1.AUD \ + MYEEHAW1.AUD \ + MYES1.AUD \ + MYESSIR1.AUD \ + ONIT1.AUD \ + PILLBOX1.AUD \ + PLACBLDG.AUD \ + RABEEP1.AUD \ + RADARDN1.AUD \ + RADARON2.AUD \ + RAMENU1.AUD \ + ROKROLL1.AUD \ + SAFFIRM1.AUD \ + SANDBAG2.AUD \ + SCOLDY1.AUD \ + SCOMND1.AUD \ + SHKTROP1.AUD \ + SILENCER.AUD \ + SINDEED1.AUD \ + SKING1.AUD \ + SMOUT1.AUD \ + SOKAY1.AUD \ + SONPULSE.AUD \ + SONWAY1.AUD \ + SPLASH9.AUD \ + SQUISHY2.AUD \ + SUBSHOW1.AUD \ + SWHAT1.AUD \ + SYEAH1.AUD \ + SYESSIR1.AUD \ + TANDETH1.AUD \ + TANK5.AUD \ + TANK6.AUD \ + TESLA1.AUD \ + TORPEDO1.AUD \ + TSLACHG2.AUD \ + TUFFGUY1.AUD \ + TURRET1.AUD \ + WALLKIL2.AUD \ + YEAH1.AUD \ + YES1.AUD \ + YO1.AUD \ + +# Vehicle responses +RESPONSE1 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + REPORT1.AUD \ + VEHIC1.AUD \ + YESSIR1.AUD \ + +# Infantry responses +RESPONSE2 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + NOPROB.AUD \ + OVEROUT.AUD \ + READY.AUD \ + REPORT1.AUD \ + RITAWAY.AUD \ + ROGER.AUD \ + UGOTIT.AUD \ + YESSIR1.AUD \ + +#TSCOREFILES = \ +# cps\record.bin \ +# WIN1.AUD \ +# MAP1.AUD \ + +VARFILES = \ + +SCOREFILES = \ + CREDITS.AUD \ + AWAIT.AUD \ + BIGF226M.AUD \ + CRUS226M.AUD \ + DENSE_R.AUD \ + FAC1226M.AUD \ + FAC2226M.AUD \ + FOGGER1A.AUD \ + HELL226M.AUD \ + MUD1A.AUD \ + RADIO2.AUD \ + ROLLOUT.AUD \ + RUN1226M.AUD \ + SCORE.AUD \ + SMSH226M.AUD \ + SNAKE.AUD \ + TERMINAT.AUD \ + TREN226M.AUD \ + TWIN.AUD \ + VECTOR1A.AUD \ + WORK226M.AUD \ + 2ND_HAND.AUD \ + ARAZIOD.AUD \ + BACKSTAB.AUD \ + CHAOS2.AUD \ + SHUT_IT.AUD \ + TWINMIX1.AUD \ + UNDER3.AUD \ + VR2.AUD \ + BOG.AUD \ + FLOAT_V2.AUD \ + GLOOM.AUD \ + GRNDWIRE.AUD \ + RPT.AUD \ + SEARCH.AUD \ + TRACTION.AUD \ + WASTELND.AUD \ + +SPEECHFILES = \ + STRCKIL1.AUD \ + NOPOWR1.AUD \ + SAVE1.AUD \ + LOAD1.AUD \ + 10MINR.AUD \ + 1MINR.AUD \ + 1OBJMET1.AUD \ + 20MINR.AUD \ + 2MINR.AUD \ + 2OBJMET1.AUD \ + 30MINR.AUD \ + 3MINR.AUD \ + 3OBJMET1.AUD \ + 40MINR.AUD \ + 4MINR.AUD \ + 5MINR.AUD \ + AAPPRO1.AUD \ + AARIVE1.AUD \ + AARIVE1.AUD \ + AARRIVE1.AUD \ + AARRIVN1.AUD \ + AARRIVS1.AUD \ + AARRIVW1.AUD \ + AAVAIL1.AUD \ + ABLDGIN1.AUD \ + AFALLEN1.AUD \ + ALAUNCH1.AUD \ + APREP1.AUD \ + AREADY1.AUD \ + ARMORUP1.AUD \ + ASELECT1.AUD \ + ATLNCH1.AUD \ + ATPREP1.AUD \ + AUNITL1.AUD \ + BASEATK1.AUD \ + BCT1.AUD \ + BLDGINF1.AUD \ + BLDGPRG1.AUD \ + CANCLD1.AUD \ + CHROCHR1.AUD \ + CHRORDY1.AUD \ + CHROYES1.AUD \ + CMDCNTR1.AUD \ + CNTLDED1.AUD \ + COMNDOF1.AUD \ + COMNDOR1.AUD \ + CONSCMP1.AUD \ + CONVLST1.AUD \ + CONVYAP1.AUD \ + CREDIT1.AUD \ + ENMYAPP1.AUD \ + FIREPO1.AUD \ + FLARE1.AUD \ + FLAREE1.AUD \ + FLAREN1.AUD \ + FLARES1.AUD \ + FLAREW1.AUD \ + IRONCHG1.AUD \ + IRONRDY1.AUD \ + KOSYFRE1.AUD \ + KOSYRES1.AUD \ + LOPOWER1.AUD \ + MERCF1.AUD \ + MERCR1.AUD \ + MISNLST1.AUD \ + MISNWON1.AUD \ + MTIMEIN1.AUD \ + NAVYLST1.AUD \ + NEWOPT1.AUD \ + NOBUILD1.AUD \ + NODEPLY1.AUD \ + NOFUNDS1.AUD \ + NOFUNDS1.AUD \ + OBJMET1.AUD \ + OBJNMET1.AUD \ + OBJNRCH1.AUD \ + OBJRCH1.AUD \ + ONHOLD1.AUD \ + OPTERM1.AUD \ + PRIBLDG1.AUD \ + PROGRES1.AUD \ + PULSE1.AUD \ + REINFOR1.AUD \ + REPAIR1.AUD \ + REPAIR1.AUD \ + SATLNCH1.AUD \ + SILOND1.AUD \ + SLCTTGT1.AUD \ + SOVEFAL1.AUD \ + SOVEMP1.AUD \ + SOVFAPP1.AUD \ + SOVFORC1.AUD \ + SOVREIN1.AUD \ + SPYPLN1.AUD \ + STRUCAP1.AUD \ + STRUSLD1.AUD \ + TANYAF1.AUD \ + TANYAR1.AUD \ + TARGFRE1.AUD \ + TARGRES1.AUD \ + TIMERGO1.AUD \ + TIMERNO1.AUD \ + TRAIN1.AUD \ + UNITFUL1.AUD \ + UNITLST1.AUD \ + UNITRDY1.AUD \ + UNITREP1.AUD \ + UNITSLD1.AUD \ + UNITSPD1.AUD \ + XPLOPLC1.AUD \ +# ABLDGC1.AUD \ +# SOVBLDG1.AUD \ +# SOVSTRC1.AUD \ +# SOVUNTD1.AUD \ +# AUNITD1.AUD \ +# ASTRUCD1.AUD \ + +#ALLIESVQ = \ +DUMMYVQ = \ + AAGUN.VQA \ + AFTRMATH.VQA \ + ALLY1.VQA \ + ALLY10.VQA \ + ALLY10B.VQA \ + ALLY11.VQA \ + ALLY12.VQA \ + ALLY14.VQA \ + ALLY2.VQA \ + ALLY4.VQA \ + ALLY5.VQA \ + ALLY6.VQA \ + ALLY8.VQA \ + ALLY9.VQA \ + ALLYEND.VQA \ + ALLYMORF.VQA \ + APCESCPE.VQA \ + ASSESS.VQA \ + BATTLE.VQA \ + 1BINOC.VQA \ + BMAP.VQA \ + BRDGTILT.VQA \ + CRONTEST.VQA \ + CRONFAIL.VQA \ + DESTROYR.VQA \ + DUD.VQA \ + ELEVATOR.VQA \ + FLARE.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + LANDING.VQA \ + MASASSLT.VQA \ + MCV.VQA \ + MCV_LAND.VQA \ + MONTPASS.VQA \ + OILDRUM.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + SHIPSINK.VQA \ + SHORBOM1.VQA \ + SHORBOM2.VQA \ + SHORBOMB.VQA \ + SNOWBOMB.VQA \ + SOVIET1.VQA \ + SOVTSTAR.VQA \ + SPY.VQA \ + TANYA1.VQA \ + TANYA2.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ +# TRAILER.VQA \ + +SOVIETVQ = \ + AAGUN.VQA \ + CRONFAIL.VQA \ + AIRFIELD.VQA \ + ALLY1.VQA \ + ALLYMORF.VQA \ + AVERTED.VQA \ + BEACHEAD.VQA \ + BMAP.VQA \ + BOMBRUN.VQA \ + COUNTDWN.VQA \ + DOUBLE.VQA \ + DPTHCHRG.VQA \ + EXECUTE.VQA \ + FLARE.VQA \ + LANDING.VQA \ + MCVBRDGE.VQA \ + MIG.VQA \ + MOVINGIN.VQA \ + MTNKFACT.VQA \ + NUKESTOK.VQA \ + ONTHPRWL.VQA \ + PERISCOP.VQA \ + PROLOG.VQA \ + RADRRAID.VQA \ + REDINTRO.VQA \ + SEARCH.VQA \ + SFROZEN.VQA \ + SITDUCK.VQA \ + SLNTSRVC.VQA \ + SNOWBOMB.VQA \ + SNSTRAFE.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + SOVFINAL.VQA \ + SOVIET1.VQA \ + SOVIET10.VQA \ + SOVIET11.VQA \ + SOVIET12.VQA \ + SOVIET13.VQA \ + SOVIET14.VQA \ + SOVIET2.VQA \ + SOVIET3.VQA \ + SOVIET4.VQA \ + SOVIET5.VQA \ + SOVIET6.VQA \ + SOVIET7.VQA \ + SOVIET8.VQA \ + SOVIET9.VQA \ + SOVMCV.VQA \ + SOVTSTAR.VQA \ + SPOTTER.VQA \ + STRAFE.VQA \ + TAKE_OFF.VQA \ + TESLA.VQA \ + V2ROCKET.VQA \ +# TRAILER.VQA \ + +ALLIESVQ = \ + SOVIET1.VQA \ + ALLY1.VQA \ + V2ROCKET.VQA \ + BMAP.VQA \ + SNSTRAFE.VQA \ + SOVTSTAR.VQA \ + SOVBATL.VQA \ + SOVCEMET.VQA \ + FLARE.VQA \ + ALLYMORF.VQA \ + SPY.VQA \ + FROZEN.VQA \ + GRVESTNE.VQA \ + CRONFAIL.VQA \ + APCESCPE.VQA \ + EXECUTE.VQA \ + TOOFAR.VQA \ + TRINITY.VQA \ + TESLA.VQA \ + MASASSLT.VQA \ + OVERRUN.VQA \ + PROLOG.VQA \ + REDINTRO.VQA \ + AFTRMATH.VQA \ + PROLOG.VQA \ + SNOWBOMB.VQA \ + LANDING.VQA \ + SFROZEN.VQA \ + ANTEND.VQA \ + ANTINTRO.VQA \ + + + +# Files required for hires/Win95 version only +# +# This mix file is not cached +# +NOCACHEHIRESFILES= \ + ENGLISH.VQA \ + $(ALLIESVQ:.VQA=.VQP) \ + $(SOVIETVQ:.VQA=.VQP) \ + + +LINTOBJECTS1 = $(OBJECTS:,=) +LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB) + +# Mixfiles that should reside on the CD-ROM drive. +CD1MIXFILES = \ + CONQUER.MIX \ + EDHI.MIX \ + EDLO.MIX \ + GENERAL.MIX \ + INTERIOR.MIX \ + MOVIES1.MIX \ + SCORES.MIX \ + SNOW.MIX \ + SOUNDS.MIX \ + RUSSIAN.MIX \ + ALLIES.MIX \ + TEMPERAT.MIX \ + + + +# Mixfiles that should reside on the hard drive. +LOCALMIXFILES = \ + EDITOR.MIX \ + HIRES.MIX \ + LOCAL.MIX \ + LORES.MIX \ + NCHIRES.MIX \ + SPEECH.MIX \ + + +# Mixfiles as they appear on the CD and hard drive. +PACKFILES= $(.path.cd1)MAIN.MIX EXPAND2.MIX $(.path.cd1)tobreaki\REDALERT.MIX + + +# Ant assets SOME ASSETS ARE HERE FOR OVERRIDING +EXPANDFILES= \ + ANT1.SHP \ + ANT2.SHP \ + ANT3.SHP \ + QUEE.SHP \ + CREDITS.ENG \ + HILL01.TEM \ + ANTBITE.AUD \ + ANTDIE.AUD \ + ANTDIE.SHP \ + LAR1.SHP \ + LAR2.SHP \ + TITLE.PCX \ + MISSION.INI \ + BUZZY1.AUD \ + STAVCMDR.AUD \ + STAVCRSE.AUD \ + STAVYES.AUD \ + STAVMOV.AUD \ + CONQUER.ENG \ + RAMBO1.AUD \ + RAMBO2.AUD \ + RAMBO3.AUD \ + TITLE.CPS \ + ANTEND.VQP \ + TUTORIAL.INI \ + ANTINTRO.VQP \ + BMAP.VQP \ + +# Aftermath expansion files +EXPAND2FILES= \ + CARR.SHP \ + CTNK.SHP \ + DTRK.SHP \ + MSUB.SHP \ + QTNK.SHP \ + TTNK.SHP \ + STNK.SHP \ + AFTRMATH.INI \ + ANT1.SHP \ + ANT2.SHP \ + ANT3.SHP \ + ANTBITE.AUD \ + ANTDIE.AUD \ + ANTDIE.SHP \ + BMAP.VQP \ + BUZZY1.AUD \ + CONQUER.ENG \ + CREDITS.ENG \ + HILL01.TEM \ + LAR1.SHP \ + LAR2.SHP \ + MISSION.INI \ + MPLAYER.INI \ + QUEE.SHP \ + STAVCMDR.AUD \ + STAVCRSE.AUD \ + STAVYES.AUD \ + STAVMOV.AUD \ + TITLE.PCX \ + TITLE.CPS \ + TUTORIAL.INI \ + + + +############################################################# +# Rebuilds all the mixfiles. +packfiles: always $(PACKFILES) + +always: + copy f:\projects\c&c0\editor\english\*.mix $(.path.mix) /u + + +#################################################################### +# All mixfiles that exist on the CD-ROM are embedded within this mega-mixfile. +$(.path.cd1)MAIN.MIX: $(CD1MIXFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.cd1)$&.mix + + +# All mixfiles that exist in the local directory are embedded within this mega-mixfile. +$(.path.cd1)install\REDALERT.MIX: $(LOCALMIXFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.cd1)install\$&.mix + + +#################################################################### +# These are the various sub-mixfiles. +CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) .\key.ini + UTILS\MIXFILE -k -h -I$(.path.cps) &&! + $(CONQUERFILES) $(CACHEMAP) +! $(.path.mix)$&.mix + +TEMPERAT.MIX: $(TEMPERATEFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(TEMPERATEFILES) +! $(.path.mix)$&.mix + +SNOW.MIX: $(SNOWFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(SNOWFILES) +! $(.path.mix)$&.mix + +INTERIOR.MIX: $(INTERIORFILES) .\key.ini + UTILS\MIXFILE -h -k -I$(.path.cps) &&! + $(INTERIORFILES) +! $(.path.mix)$&.mix + +GENERAL.MIX: $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) .\key.ini + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $(GENERALFILES) $(GENERALMAPFILES) $(NETMAPFILES) $(MAPFILES) +! $(.path.mix)$&.mix + +SCORES.MIX: $(SCOREFILES) + UTILS\MIXFILE -k -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.mix)$&.mix + +SOUNDS.MIX: $(WAVFILES) $(SFX) + UTILS\MIXFILE -h -k -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +RUSSIAN.MIX: $(RESPONSE1:.AUD=.R00) $(RESPONSE2:.AUD=.R01) $(RESPONSE1:.AUD=.R02) $(RESPONSE2:.AUD=.R03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +LIMITED.MIX: BLEEP11.AUD + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +ALLIES.MIX: $(RESPONSE1:.AUD=.V00) $(RESPONSE2:.AUD=.V01) $(RESPONSE1:.AUD=.V02) $(RESPONSE2:.AUD=.V03) + UTILS\MIXFILE -h -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +MOVIES1.MIX: $(ALLIESVQ) + UTILS\MIXFILE -k -I$(.path.vqa) &&! + $** +! $(.path.mix)$&.mix + + + + + +NCHIRES.MIX: $(NOCACHEHIRESFILES:.SHP=.HI) + UTILS\MIXFILE -k -I$(.path.vqp) -I$(.path.cps) &&! + $(NOCACHEHIRESFILES) +! $(.path.mix)$&.mix + +LOCAL.MIX: $(LOCALFILES) .\key.ini + UTILS\MIXFILE -h -k -E.A6=.AUD -I$(.path.ini) -I$(.path.txt) -I$(.path.cps) &&! + $(LOCALFILES) +! $(.path.mix)$&.mix + +LORES.MIX: $(LOHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.LOW=.SHP -E.LNT=.FNT -I$(.path.cps) &&! + $(LOHILORES) +! $(.path.mix)$&.mix + +HIRES.MIX: $(HIRESFILES:.SHP=.HI) $(HIHILORES) .\key.ini + UTILS\MIXFILE -h -k -E.HI=.SHP -E.HNT=.FNT -I$(.path.cps) &&! + $(HIRESFILES:.SHP=.HI) $(HIHILORES) +! $(.path.mix)$&.mix + +SPEECH.MIX: $(SPEECHFILES) + UTILS\MIXFILE -k -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + + +EXPAND.MIX: $(EXPANDFILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +EXPAND2.MIX: $(EXPAND2FILES) + UTILS\MIXFILE -k -I$(.path.mix) &&! + $** +! $(.path.mix)$&.mix + +############################################################# +# Special rule to create the mouse shape (which must be a shape file) +mouse.hi: $(.path.anm)hires\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)hires\mouse.anm; + end; +! $(.path.hi)$&.hi $(SHAPEBUFFSIZE) + +# Special rule to create the mouse shape (which must be a shape file) +mouse.low: $(.path.anm)lores\mouse.anm + -utils\makeshps $(.path.lbm)palettes\temperat.lbm &&! + &$(.path.anm)lores\mouse.anm; + end; +! $(.path.low)$&.low $(SHAPEBUFFSIZE) + + +############################################################# +# Special build rule for radar animations so that they won't. +# +NATORADR.HI: $(.path.anm)hires\NATORADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +USSRRADR.HI: $(.path.anm)hires\USSRRADR.ANM + utils\newkeyf $** $(.path.hi)$&.hi -l -k + +NATORADR.LOW: $(.path.anm)lores\NATORADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + +USSRRADR.LOW: $(.path.anm)lores\USSRRADR.ANM + utils\newkeyf $** $(.path.low)$&.low -l -k + + +############################################################# +# Debug text file creation. +debug.eng: debug.txt + utils\textmake -b1000 eng\$&.txt $(.path.eng)$&.eng $&.h + + diff --git a/CODE/BFIOFILE.CPP b/CODE/BFIOFILE.CPP new file mode 100644 index 0000000..e5d840f --- /dev/null +++ b/CODE/BFIOFILE.CPP @@ -0,0 +1,987 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BFIOFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * BufferIOFileClass::Close -- Perform a closure of the file. * + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * BufferIOFileClass::Read -- Reads data from the file cache. * + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * BufferIOFileClass::Write -- Writes data to the file cache. * + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "bfiofile.h" +#include + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(char const * filename) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ + BufferIOFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * * + * This is the default constructor for a file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(void) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ +} + + +/*********************************************************************************************** + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * * + * This destructor will free all memory allocated thru using Cache routines. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::~BufferIOFileClass(void) +{ + Free(); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Cache( long size, void * ptr ) +{ + if (Buffer) { + // + // if trying to cache again with size or ptr fail + // + if (size || ptr) { + return( false ); + } else { + return( true ); + } + } + + if ( Is_Available() ) { + FileSize = Size(); + } else { + FileSize = 0; + } + + if (size) { + // + // minimum buffer size for performance + // + if (size < MINIMUM_BUFFER_SIZE) { + size = MINIMUM_BUFFER_SIZE; + + /* + ** Specifying a size smaller than the minimum is an error + ** IF a buffer pointer was also specified. In such a case the + ** system cannot use the buffer. + */ + if (ptr) { + Error(EINVAL); + } + } + + BufferSize = size; + } else { + BufferSize = FileSize; + } + + // + // if size == 0 and a ptr to a buffer is specified then that is invalid. + // if the BufferSize is 0 then this must be a new file and no size was + // specified so exit. + // + if ( (size == 0 && ptr) || !BufferSize) { + return( false ); + } + + if (ptr) { + Buffer = ptr; + } else { + Buffer = new char [BufferSize]; + } + + if (Buffer) { + IsAllocated = true; + IsDiskOpen = false; + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + TrueFileStart = 0; + + // + // the file was checked for availability then set the FileSize + // + if (FileSize) { + long readsize; + int opened = false; + long prevpos = 0; + + + if (FileSize <= BufferSize) { + readsize = FileSize; + } else { + readsize = BufferSize; + } + + if ( Is_Open() ) { + // + // get previous file position + // + prevpos = Seek(0); + + // + // get true file position + // + if ( RawFileClass::Is_Open() ) { + TrueFileStart = RawFileClass::Seek(0); + } else { + TrueFileStart = prevpos; + } + + if (FileSize <= BufferSize) { + // + // if previous position is non-zero seek to the beginning + // + if (prevpos) { + Seek(0, SEEK_SET); + } + + // + // set the buffer position for future reads/writes + // + BufferPos = prevpos; + } else { + BufferFilePos = prevpos; + } + + FilePos = prevpos; + } else { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + long actual = Read(Buffer, readsize); + + if (actual != readsize) { + Error(EIO); + } + + if (opened) { + Close(); + } else { + // + // seek to the previous position in the file + // + Seek(prevpos, SEEK_SET); + } + + IsCached = true; + } + + UseBuffer = true; + return(true); + } + + Error(ENOMEM); + + return(false); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * * + * This routine will free the buffer. By using this in conjunction with the * + * Cache() function, one can maintain tight control of memory usage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Free(void) +{ + if (Buffer) { + if (IsAllocated) { + delete [] Buffer; + IsAllocated = false; + } + + Buffer = 0; + } + + BufferSize = 0; + IsOpen = false; + IsCached = false; + IsChanged = false; + UseBuffer = false; +} + + +/*********************************************************************************************** + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * * + * * + * INPUT: none * + * * + * OUTPUT: false, did not need to write the buffer. * + * true, wrote the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Commit( void ) +{ + long size; + + + if (UseBuffer) { + if (IsChanged) { + size = BufferChangeEnd - BufferChangeBeg; + + if (IsDiskOpen) { + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Seek( TrueFileStart + FilePos, SEEK_SET ); + } else { + RawFileClass::Open(); + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Close(); + } + + IsChanged = false; + return( true ); + } else { + return( false ); + } + } else { + return( false ); + } +} + + +/*********************************************************************************************** + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * * + * Checks for a previous filename and that it is cached. If so, then check the * + * new filename against the old. If they are the same then return that filename. * + * Otherwise, the file object's name is set with just the raw filename as passed * + * to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +char const * BufferIOFileClass::Set_Name(char const * filename) +{ + if ( File_Name() && UseBuffer) { + if ( strcmp(filename, File_Name() ) == 0) { + return( File_Name() ); + } else { + Commit(); + IsCached = false; + } + } + + RawFileClass::Set_Name(filename); + return( File_Name() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/16/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Available(int ) +{ + if (UseBuffer) { + return(true); + } + + return( RawFileClass::Is_Available() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * * + * If part or all of the file is cached, then return that it is opened. A closed file * + * doesn't have a valid pointer. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Open(void) const +{ + if (IsOpen && UseBuffer) { + return( true ); + } + + return( RawFileClass::Is_Open() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return( BufferIOFileClass::Open( rights ) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(int rights) +{ + BufferIOFileClass::Close(); + + if (UseBuffer) { + + BufferRights = rights; // save rights requested for checks later + + if (rights != READ || + (rights == READ && FileSize > BufferSize) ) { + + if (rights == WRITE) { + RawFileClass::Open( rights ); + RawFileClass::Close(); + rights = READ | WRITE; + TrueFileStart = 0; // now writing to single file + } + + if (TrueFileStart) { + UseBuffer = false; + Open( rights ); + UseBuffer = true; + } else { + RawFileClass::Open( rights ); + } + + IsDiskOpen = true; + + if (BufferRights == WRITE) { + FileSize = 0; + } + + } else { + IsDiskOpen = false; + } + + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + IsOpen = true; + } else { + RawFileClass::Open( rights ); + } + + return( true ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Write -- Writes data to the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Write(void const * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if (!Open(WRITE)) { + return(0); + } + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + + if (UseBuffer) { + long sizewritten = 0; + + if (BufferRights != READ) { + while (size) { + long sizetowrite; + + if (size >= (BufferSize - BufferPos) ) { + sizetowrite = (BufferSize - BufferPos); + } else { + sizetowrite = size; + } + + if (sizetowrite != BufferSize) { + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + } + + memmove((char *)Buffer + BufferPos, (char *)buffer + sizewritten, sizetowrite); + + IsChanged = true; + sizewritten += sizetowrite; + size -= sizetowrite; + + if (BufferChangeBeg == -1) { + BufferChangeBeg = BufferPos; + BufferChangeEnd = BufferPos; + } else { + if (BufferChangeBeg > BufferPos) { + BufferChangeBeg = BufferPos; + } + } + + BufferPos += sizetowrite; + + if (BufferChangeEnd < BufferPos) { + BufferChangeEnd = BufferPos; + } + + FilePos = BufferFilePos + BufferPos; + + if (FileSize < FilePos) { + FileSize = FilePos; + } + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizewritten; + } else { + size = RawFileClass::Write(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Read -- Reads data from the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Read(void * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + if (UseBuffer) { + long sizeread = 0; + + if (BufferRights != WRITE) { + while (size) { + long sizetoread; + + if (size >= (BufferSize - BufferPos) ) { + sizetoread = (BufferSize - BufferPos); + } else { + sizetoread = size; + } + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + + memmove((char *)buffer + sizeread, (char *)Buffer + BufferPos, sizetoread); + + sizeread += sizetoread; + size -= sizetoread; + BufferPos += sizetoread; + FilePos = BufferFilePos + BufferPos; + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizeread; + } else { + size = RawFileClass::Read(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Seek(long pos, int dir) +{ + if (UseBuffer) { + bool adjusted = false; + + switch (dir) { + case SEEK_END: + FilePos = FileSize; + break; + + case SEEK_SET: + FilePos = 0; + break; + + case SEEK_CUR: + default: + break; + } + + if (TrueFileStart) { + if (pos >= TrueFileStart) { + pos -= TrueFileStart; + adjusted = true; + } + } + + FilePos += pos; + + if (FilePos < 0) { + FilePos = 0; + } + + if (FilePos > FileSize ) { + FilePos = FileSize; + } + + if (FileSize <= BufferSize) { + BufferPos = FilePos; + } else { + if (FilePos >= BufferFilePos && + FilePos < (BufferFilePos + BufferSize) ) { + BufferPos = FilePos - BufferFilePos; + } else { + Commit(); +// check!! + if (TrueFileStart) { + UseBuffer = false; + Seek(FilePos, SEEK_SET); + UseBuffer = true; + } else { + RawFileClass::Seek(FilePos, SEEK_SET); + } + + IsCached = false; + } + } + + if (TrueFileStart && adjusted) { + return( FilePos + TrueFileStart ); + } + + return( FilePos ); + } + + return( RawFileClass::Seek(pos, dir) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * * + * If part or all of the file is cached, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Size(void) +{ + if (IsOpen && UseBuffer) { + return( FileSize ); + } + + return( RawFileClass::Size() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Close -- Perform a closure of the file. * + * * + * Call Commit() to write the buffer if the file is cached and the buffer has changed, * + * then call lower level Close(). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Close(void) +{ + if (UseBuffer) { + Commit(); + + if (IsDiskOpen) { + + if (TrueFileStart) { + UseBuffer = false; + Close(); + UseBuffer = true; + } else { + RawFileClass::Close(); + } + + IsDiskOpen = false; + } + + IsOpen = false; + } else { + RawFileClass::Close(); + } +} + diff --git a/CODE/BFIOFILE.H b/CODE/BFIOFILE.H new file mode 100644 index 0000000..8244650 --- /dev/null +++ b/CODE/BFIOFILE.H @@ -0,0 +1,95 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BFIOFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : BFIOFILE.H * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BFIOFILE_H +#define BFIOFILE_H + +#include "rawfile.h" + +/* +** This derivation of the raw file class handles buffering the input/output in order to +** achieve greater speed. The buffering is not active by default. It must be activated +** by setting the appropriate buffer through the Cache() function. +*/ +class BufferIOFileClass : public RawFileClass +{ + public: + + BufferIOFileClass(char const *filename); + BufferIOFileClass(void); + virtual ~BufferIOFileClass(void); + + bool Cache( long size=0, void *ptr=NULL ); + void Free( void ); + bool Commit( void ); + virtual char const * Set_Name(char const *filename); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + + enum {MINIMUM_BUFFER_SIZE=1024}; + + private: + + unsigned IsAllocated:1; + unsigned IsOpen:1; + unsigned IsDiskOpen:1; + unsigned IsCached:1; + unsigned IsChanged:1; + unsigned UseBuffer:1; + + int BufferRights; + + void *Buffer; + + long BufferSize; + long BufferPos; + long BufferFilePos; + long BufferChangeBeg; + long BufferChangeEnd; + long FileSize; + long FilePos; + long TrueFileStart; +}; + +#endif diff --git a/CODE/BIGCHECK.CPP b/CODE/BIGCHECK.CPP new file mode 100644 index 0000000..673cb8a --- /dev/null +++ b/CODE/BIGCHECK.CPP @@ -0,0 +1,84 @@ +/* +** 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 . +*/ + +// BigCheck.cpp +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#include "function.h" +#include "bigcheck.h" + +//*********************************************************************************************** +int BigCheckBoxClass::Draw_Me( int forced ) +{ + if (ToggleClass::Draw_Me(forced)) + { + Hide_Mouse(); + + if( !IsOn ) + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 0, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 2, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + else + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 1, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 3, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + + TextPrintType flags = TextFlags; + + RemapControlType* pScheme; + +// if( !IsDisabled ) + pScheme = GadgetClass::Get_Color_Scheme(); +// else +// { +// pScheme = &GreyScheme; +// flags = flags | TPF_MEDIUM_COLOR; +// } + + Conquer_Clip_Text_Print( szCaption, X + BIGCHECK_OFFSETX, Y + BIGCHECK_OFFSETY, pScheme, TBLACK, flags, Width, 0 ); + + Show_Mouse(); + return true; + } + return false; +} + +//*********************************************************************************************** +int BigCheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ +/* if( flags & LEFTPRESS ) + { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } +*/ + return(ToggleClass::Action(flags, key)); +} + +#endif diff --git a/CODE/BIGCHECK.H b/CODE/BIGCHECK.H new file mode 100644 index 0000000..308b580 --- /dev/null +++ b/CODE/BIGCHECK.H @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +// Bigcheck.h +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#ifndef BIGCHECKBOX_H +#define BIGCHECKBOX_H + +#include "toggle.h" + +#define BIGCHECK_OFFSETX 20 +#define BIGCHECK_OFFSETY 0 + +//*********************************************************************************************** +class BigCheckBoxClass : public ToggleClass +{ +public: + BigCheckBoxClass( unsigned id, int x, int y, int w, int h, const char* szCaptionIn, TextPrintType TextFlags, + bool bInitiallyChecked = false ) : + ToggleClass( id, x, y, w, h ), + TextFlags( TextFlags ) + { + szCaption = new char[ strlen( szCaptionIn ) + 1 ]; + strcpy( szCaption, szCaptionIn ); + if( bInitiallyChecked ) + Turn_On(); + IsToggleType = 1; + } + virtual ~BigCheckBoxClass() + { + delete [] szCaption; + } + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + bool Toggle() + { + if( IsOn ) + { + Turn_Off(); + return false; + } + Turn_On(); + return true; + } + +protected: + TextPrintType TextFlags; + char* szCaption; + +}; + +#endif + +#endif diff --git a/CODE/BLOWFISH.CPP b/CODE/BLOWFISH.CPP new file mode 100644 index 0000000..60e2ded --- /dev/null +++ b/CODE/BLOWFISH.CPP @@ -0,0 +1,598 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLOWFISH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLOWFISH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowfishEngine::Decrypt -- Decrypts data using blowfish algorithm. * + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowfish.h" +#include +#include + + +/* +** Byte order controlled long integer. This integer is constructed +** so that character 0 (C0) is the most significant byte of the +** integer. This is biased toward big endian architecture, but that +** just happens to be how the Blowfish algorithm was designed. +*/ +typedef union { + unsigned long Long; + struct { + unsigned char C3; + unsigned char C2; + unsigned char C1; + unsigned char C0; + } Char; +} Int; + + +/*********************************************************************************************** + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * * + * This destructor will clear out the s-box tables so that even if the memory for the * + * class remains, it will contain no compromising data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +BlowfishEngine::~BlowfishEngine(void) +{ + if (IsKeyed) { + Submit_Key(NULL, 0); + } +} + + +/*********************************************************************************************** + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * * + * This routine must be called before any data can be encrypted or decrypted. This routine * + * need only be called when the key is to be changed or set for the first time. Once the * + * key has been set, the engine may be used to encrypt, decrypt, or both operations * + * indefinitely. The key must be 56 bytes or less in length. This is necessary because * + * any keys longer than that will not correctly affect the encryption process. * + * * + * If the key pointer is NULL, then the S-Box tables are reset to identity. This will * + * mask the previous key setting. Use this method to clear the engine after processing in * + * order to gain a measure of security. * + * * + * INPUT: key -- Pointer to the key data block. * + * * + * length -- The length of the submitted key. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming process. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Submit_Key(void const * key, int length) +{ + assert(length <= MAX_KEY_LENGTH); + + /* + ** Initialize the permutation and S-Box tables to a known + ** constant value. + */ + memcpy(P_Encrypt, P_Init, sizeof(P_Init)); + memcpy(P_Decrypt, P_Init, sizeof(P_Init)); + memcpy(bf_S, S_Init, sizeof(S_Init)); + + /* + ** Validate parameters. + */ + if (key == 0 || length == 0) { + IsKeyed = false; + return; + } + + /* + ** Combine the key with the permutation table. Wrap the key + ** as many times as necessary to ensure that the entire + ** permutation table has been modified. The key is lifted + ** into a long by using endian independent means. + */ + int j = 0; + unsigned char const * key_ptr = (unsigned char const *)key; + unsigned long * p_ptr = &P_Encrypt[0]; + for (int index = 0; index < ROUNDS+2; index++) { + unsigned long data = 0; + + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + + *p_ptr++ ^= data; + } + + /* + ** The permutation table must be scrambled by means of the key. This + ** is how the key is factored into the encryption -- by merely altering + ** the permutation (and S-Box) tables. Because this transformation alters + ** the table data WHILE it is using the table data, the tables are + ** thoroughly obfuscated by this process. + */ + unsigned long left = 0x00000000L; + unsigned long right = 0x00000000L; + unsigned long * p_en = &P_Encrypt[0]; // Encryption table. + unsigned long * p_de = &P_Decrypt[ROUNDS+1]; // Decryption table. + for (int p_index = 0; p_index < ROUNDS+2; p_index += 2) { + Sub_Key_Encrypt(left, right); + + *p_en++ = left; + *p_en++ = right; + + *p_de-- = left; + *p_de-- = right; + } + + /* + ** Perform a similar transmutation to the S-Box tables. Also notice that the + ** working 64 bit number is carried into this process from the previous + ** operation. + */ + for (int sbox_index = 0; sbox_index < 4; sbox_index++) { + for (int ss_index = 0; ss_index < UCHAR_MAX+1; ss_index += 2) { + Sub_Key_Encrypt(left, right); + bf_S[sbox_index][ss_index] = left; + bf_S[sbox_index][ss_index + 1] = right; + } + } + + IsKeyed = true; +} + + +/*********************************************************************************************** + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * * + * Use this routine to encrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be encrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: plaintext-- Pointer to the data block to be encrypted. * + * * + * length -- The length of the data block. * + * * + * cyphertext- Pointer to the output buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes encrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only encrypt * + * the plaintext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertext) +{ + if (plaintext == 0 || length == 0) { + return(0); + } + if (cyphertext == 0) cyphertext = (void *)plaintext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(plaintext, cyphertext, P_Encrypt); + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(cyphertext, plaintext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(cyphertext, plaintext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Decrypt -- Decrypt an arbitrary block of data. * + * * + * Use this routine to decrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be decrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: cyphertext- Pointer to the data block to be decrypted. * + * * + * length -- The length of the data block. * + * * + * plaintext-- Pointer to the output buffer that will hold the decrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes decrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only decrypt * + * the cyphertext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintext) +{ + if (cyphertext == 0 || length == 0) { + return(0); + } + if (plaintext == 0) plaintext = (void *)cyphertext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(cyphertext, plaintext, P_Decrypt); + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(plaintext, cyphertext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(plaintext, cyphertext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * * + * This is the main processing routine for encryption and decryption. The algorithm * + * consists of a 16 round Feistal network and uses mathematics from different algebraic * + * groups (strengthens against differential cryptanalysis). The large S-Boxes and the * + * rounds strengthen it against linear cryptanalysis. * + * * + * INPUT: plaintext -- Pointer to the source text (it actually might be a pointer to * + * the cyphertext if this is called as a decryption process). * + * * + * cyphertext -- Pointer to the output buffer that will hold the processed block. * + * * + * ptable -- Pointer to the permutation table. This algorithm will encrypt * + * and decrypt using the same S-Box tables. The encryption control * + * is handled by the permutation table. * + * * + * OUTPUT: none * + * * + * WARNINGS: The source and destination buffers must be 8 bytes long. * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable) +{ + /* + ** Input the left and right halves of the source block such that + ** the byte order is constant regardless of the endian + ** persuasion of the current processor. The blowfish algorithm is + ** biased toward "big endian" architecture and some optimizations + ** could be done for big endian processors in that case. + */ + unsigned char const * source = (unsigned char const *)plaintext; + Int left; + left.Char.C0 = *source++; + left.Char.C1 = *source++; + left.Char.C2 = *source++; + left.Char.C3 = *source++; + + Int right; + right.Char.C0 = *source++; + right.Char.C1 = *source++; + right.Char.C2 = *source++; + right.Char.C3 = *source; + + /* + ** Perform all Feistal rounds on the block. This is the encryption/decryption + ** process. Since there is an exchange that occurs after each round, two + ** rounds are combined in this loop to avoid unnecessary exchanging. + */ + for (int index = 0; index < ROUNDS/2; index++) { + left.Long ^= *ptable++; + right.Long ^= ((( bf_S[0][left.Char.C0] + bf_S[1][left.Char.C1]) ^ bf_S[2][left.Char.C2]) + bf_S[3][left.Char.C3]); + right.Long ^= *ptable++; + left.Long ^= ((( bf_S[0][right.Char.C0] + bf_S[1][right.Char.C1]) ^ bf_S[2][right.Char.C2]) + bf_S[3][right.Char.C3]); + } + + /* + ** The final two longs in the permutation table are processed into the block. + ** The left and right halves are still reversed as a side effect of the last + ** round. + */ + left.Long ^= *ptable++; + right.Long ^= *ptable; + + /* + ** The final block data is output in endian architecture + ** independent format. Notice that the blocks are output as + ** right first and left second. This is to counteract the final + ** superfluous exchange that occurs as a side effect of the + ** encryption rounds. + */ + unsigned char * out = (unsigned char *)cyphertext; + *out++ = right.Char.C0; + *out++ = right.Char.C1; + *out++ = right.Char.C2; + *out++ = right.Char.C3; + + *out++ = left.Char.C0; + *out++ = left.Char.C1; + *out++ = left.Char.C2; + *out = left.Char.C3; +} + + +/*********************************************************************************************** + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * * + * This is the same as the normal process block function but it doesn't have the endian * + * fixup logic. Since this routine is only called for S-Box table generation and it is * + * known that the S-Box initial data is already in local machine endian format, the * + * byte order fixups are not needed. This also has a tendency to speed up S-Box generation * + * as well. * + * * + * INPUT: left -- The left half of the data block. * + * * + * right -- The right half of the data block. * + * * + * OUTPUT: none, but the processed block is stored back into the left and right half * + * integers. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Sub_Key_Encrypt(unsigned long & left, unsigned long & right) +{ + Int l; + l.Long = left; + + Int r; + r.Long = right; + + for (int index = 0; index < ROUNDS; index += 2) { + l.Long ^= P_Encrypt[index]; + r.Long ^= ((( bf_S[0][l.Char.C0] + bf_S[1][l.Char.C1]) ^ bf_S[2][l.Char.C2]) + bf_S[3][l.Char.C3]); + r.Long ^= P_Encrypt[index+1]; + l.Long ^= ((( bf_S[0][r.Char.C0] + bf_S[1][r.Char.C1]) ^ bf_S[2][r.Char.C2]) + bf_S[3][r.Char.C3]); + } + left = r.Long ^ P_Encrypt[ROUNDS+1]; + right = l.Long ^ P_Encrypt[ROUNDS]; +} + + +/* +** These tables have the bytes stored in machine endian format. Because of this, +** a special block cypher routine is needed when the sub-keys are generated. +** This is kludgier than it otherwise should be. However, storing these +** integers in machine independent format would be even more painful. +*/ + +unsigned long const BlowfishEngine::P_Init[BlowfishEngine::ROUNDS+2] = { + 0x243F6A88U,0x85A308D3U,0x13198A2EU,0x03707344U,0xA4093822U,0x299F31D0U,0x082EFA98U,0xEC4E6C89U, + 0x452821E6U,0x38D01377U,0xBE5466CFU,0x34E90C6CU,0xC0AC29B7U,0xC97C50DDU,0x3F84D5B5U,0xB5470917U, + 0x9216D5D9U,0x8979FB1BU +} ; + +unsigned long const BlowfishEngine::S_Init[4][UCHAR_MAX+1] = { + { + 0xD1310BA6U,0x98DFB5ACU,0x2FFD72DBU,0xD01ADFB7U,0xB8E1AFEDU,0x6A267E96U,0xBA7C9045U,0xF12C7F99U, + 0x24A19947U,0xB3916CF7U,0x0801F2E2U,0x858EFC16U,0x636920D8U,0x71574E69U,0xA458FEA3U,0xF4933D7EU, + 0x0D95748FU,0x728EB658U,0x718BCD58U,0x82154AEEU,0x7B54A41DU,0xC25A59B5U,0x9C30D539U,0x2AF26013U, + 0xC5D1B023U,0x286085F0U,0xCA417918U,0xB8DB38EFU,0x8E79DCB0U,0x603A180EU,0x6C9E0E8BU,0xB01E8A3EU, + 0xD71577C1U,0xBD314B27U,0x78AF2FDAU,0x55605C60U,0xE65525F3U,0xAA55AB94U,0x57489862U,0x63E81440U, + 0x55CA396AU,0x2AAB10B6U,0xB4CC5C34U,0x1141E8CEU,0xA15486AFU,0x7C72E993U,0xB3EE1411U,0x636FBC2AU, + 0x2BA9C55DU,0x741831F6U,0xCE5C3E16U,0x9B87931EU,0xAFD6BA33U,0x6C24CF5CU,0x7A325381U,0x28958677U, + 0x3B8F4898U,0x6B4BB9AFU,0xC4BFE81BU,0x66282193U,0x61D809CCU,0xFB21A991U,0x487CAC60U,0x5DEC8032U, + 0xEF845D5DU,0xE98575B1U,0xDC262302U,0xEB651B88U,0x23893E81U,0xD396ACC5U,0x0F6D6FF3U,0x83F44239U, + 0x2E0B4482U,0xA4842004U,0x69C8F04AU,0x9E1F9B5EU,0x21C66842U,0xF6E96C9AU,0x670C9C61U,0xABD388F0U, + 0x6A51A0D2U,0xD8542F68U,0x960FA728U,0xAB5133A3U,0x6EEF0B6CU,0x137A3BE4U,0xBA3BF050U,0x7EFB2A98U, + 0xA1F1651DU,0x39AF0176U,0x66CA593EU,0x82430E88U,0x8CEE8619U,0x456F9FB4U,0x7D84A5C3U,0x3B8B5EBEU, + 0xE06F75D8U,0x85C12073U,0x401A449FU,0x56C16AA6U,0x4ED3AA62U,0x363F7706U,0x1BFEDF72U,0x429B023DU, + 0x37D0D724U,0xD00A1248U,0xDB0FEAD3U,0x49F1C09BU,0x075372C9U,0x80991B7BU,0x25D479D8U,0xF6E8DEF7U, + 0xE3FE501AU,0xB6794C3BU,0x976CE0BDU,0x04C006BAU,0xC1A94FB6U,0x409F60C4U,0x5E5C9EC2U,0x196A2463U, + 0x68FB6FAFU,0x3E6C53B5U,0x1339B2EBU,0x3B52EC6FU,0x6DFC511FU,0x9B30952CU,0xCC814544U,0xAF5EBD09U, + 0xBEE3D004U,0xDE334AFDU,0x660F2807U,0x192E4BB3U,0xC0CBA857U,0x45C8740FU,0xD20B5F39U,0xB9D3FBDBU, + 0x5579C0BDU,0x1A60320AU,0xD6A100C6U,0x402C7279U,0x679F25FEU,0xFB1FA3CCU,0x8EA5E9F8U,0xDB3222F8U, + 0x3C7516DFU,0xFD616B15U,0x2F501EC8U,0xAD0552ABU,0x323DB5FAU,0xFD238760U,0x53317B48U,0x3E00DF82U, + 0x9E5C57BBU,0xCA6F8CA0U,0x1A87562EU,0xDF1769DBU,0xD542A8F6U,0x287EFFC3U,0xAC6732C6U,0x8C4F5573U, + 0x695B27B0U,0xBBCA58C8U,0xE1FFA35DU,0xB8F011A0U,0x10FA3D98U,0xFD2183B8U,0x4AFCB56CU,0x2DD1D35BU, + 0x9A53E479U,0xB6F84565U,0xD28E49BCU,0x4BFB9790U,0xE1DDF2DAU,0xA4CB7E33U,0x62FB1341U,0xCEE4C6E8U, + 0xEF20CADAU,0x36774C01U,0xD07E9EFEU,0x2BF11FB4U,0x95DBDA4DU,0xAE909198U,0xEAAD8E71U,0x6B93D5A0U, + 0xD08ED1D0U,0xAFC725E0U,0x8E3C5B2FU,0x8E7594B7U,0x8FF6E2FBU,0xF2122B64U,0x8888B812U,0x900DF01CU, + 0x4FAD5EA0U,0x688FC31CU,0xD1CFF191U,0xB3A8C1ADU,0x2F2F2218U,0xBE0E1777U,0xEA752DFEU,0x8B021FA1U, + 0xE5A0CC0FU,0xB56F74E8U,0x18ACF3D6U,0xCE89E299U,0xB4A84FE0U,0xFD13E0B7U,0x7CC43B81U,0xD2ADA8D9U, + 0x165FA266U,0x80957705U,0x93CC7314U,0x211A1477U,0xE6AD2065U,0x77B5FA86U,0xC75442F5U,0xFB9D35CFU, + 0xEBCDAF0CU,0x7B3E89A0U,0xD6411BD3U,0xAE1E7E49U,0x00250E2DU,0x2071B35EU,0x226800BBU,0x57B8E0AFU, + 0x2464369BU,0xF009B91EU,0x5563911DU,0x59DFA6AAU,0x78C14389U,0xD95A537FU,0x207D5BA2U,0x02E5B9C5U, + 0x83260376U,0x6295CFA9U,0x11C81968U,0x4E734A41U,0xB3472DCAU,0x7B14A94AU,0x1B510052U,0x9A532915U, + 0xD60F573FU,0xBC9BC6E4U,0x2B60A476U,0x81E67400U,0x08BA6FB5U,0x571BE91FU,0xF296EC6BU,0x2A0DD915U, + 0xB6636521U,0xE7B9F9B6U,0xFF34052EU,0xC5855664U,0x53B02D5DU,0xA99F8FA1U,0x08BA4799U,0x6E85076AU, + },{ + 0x4B7A70E9U,0xB5B32944U,0xDB75092EU,0xC4192623U,0xAD6EA6B0U,0x49A7DF7DU,0x9CEE60B8U,0x8FEDB266U, + 0xECAA8C71U,0x699A17FFU,0x5664526CU,0xC2B19EE1U,0x193602A5U,0x75094C29U,0xA0591340U,0xE4183A3EU, + 0x3F54989AU,0x5B429D65U,0x6B8FE4D6U,0x99F73FD6U,0xA1D29C07U,0xEFE830F5U,0x4D2D38E6U,0xF0255DC1U, + 0x4CDD2086U,0x8470EB26U,0x6382E9C6U,0x021ECC5EU,0x09686B3FU,0x3EBAEFC9U,0x3C971814U,0x6B6A70A1U, + 0x687F3584U,0x52A0E286U,0xB79C5305U,0xAA500737U,0x3E07841CU,0x7FDEAE5CU,0x8E7D44ECU,0x5716F2B8U, + 0xB03ADA37U,0xF0500C0DU,0xF01C1F04U,0x0200B3FFU,0xAE0CF51AU,0x3CB574B2U,0x25837A58U,0xDC0921BDU, + 0xD19113F9U,0x7CA92FF6U,0x94324773U,0x22F54701U,0x3AE5E581U,0x37C2DADCU,0xC8B57634U,0x9AF3DDA7U, + 0xA9446146U,0x0FD0030EU,0xECC8C73EU,0xA4751E41U,0xE238CD99U,0x3BEA0E2FU,0x3280BBA1U,0x183EB331U, + 0x4E548B38U,0x4F6DB908U,0x6F420D03U,0xF60A04BFU,0x2CB81290U,0x24977C79U,0x5679B072U,0xBCAF89AFU, + 0xDE9A771FU,0xD9930810U,0xB38BAE12U,0xDCCF3F2EU,0x5512721FU,0x2E6B7124U,0x501ADDE6U,0x9F84CD87U, + 0x7A584718U,0x7408DA17U,0xBC9F9ABCU,0xE94B7D8CU,0xEC7AEC3AU,0xDB851DFAU,0x63094366U,0xC464C3D2U, + 0xEF1C1847U,0x3215D908U,0xDD433B37U,0x24C2BA16U,0x12A14D43U,0x2A65C451U,0x50940002U,0x133AE4DDU, + 0x71DFF89EU,0x10314E55U,0x81AC77D6U,0x5F11199BU,0x043556F1U,0xD7A3C76BU,0x3C11183BU,0x5924A509U, + 0xF28FE6EDU,0x97F1FBFAU,0x9EBABF2CU,0x1E153C6EU,0x86E34570U,0xEAE96FB1U,0x860E5E0AU,0x5A3E2AB3U, + 0x771FE71CU,0x4E3D06FAU,0x2965DCB9U,0x99E71D0FU,0x803E89D6U,0x5266C825U,0x2E4CC978U,0x9C10B36AU, + 0xC6150EBAU,0x94E2EA78U,0xA5FC3C53U,0x1E0A2DF4U,0xF2F74EA7U,0x361D2B3DU,0x1939260FU,0x19C27960U, + 0x5223A708U,0xF71312B6U,0xEBADFE6EU,0xEAC31F66U,0xE3BC4595U,0xA67BC883U,0xB17F37D1U,0x018CFF28U, + 0xC332DDEFU,0xBE6C5AA5U,0x65582185U,0x68AB9802U,0xEECEA50FU,0xDB2F953BU,0x2AEF7DADU,0x5B6E2F84U, + 0x1521B628U,0x29076170U,0xECDD4775U,0x619F1510U,0x13CCA830U,0xEB61BD96U,0x0334FE1EU,0xAA0363CFU, + 0xB5735C90U,0x4C70A239U,0xD59E9E0BU,0xCBAADE14U,0xEECC86BCU,0x60622CA7U,0x9CAB5CABU,0xB2F3846EU, + 0x648B1EAFU,0x19BDF0CAU,0xA02369B9U,0x655ABB50U,0x40685A32U,0x3C2AB4B3U,0x319EE9D5U,0xC021B8F7U, + 0x9B540B19U,0x875FA099U,0x95F7997EU,0x623D7DA8U,0xF837889AU,0x97E32D77U,0x11ED935FU,0x16681281U, + 0x0E358829U,0xC7E61FD6U,0x96DEDFA1U,0x7858BA99U,0x57F584A5U,0x1B227263U,0x9B83C3FFU,0x1AC24696U, + 0xCDB30AEBU,0x532E3054U,0x8FD948E4U,0x6DBC3128U,0x58EBF2EFU,0x34C6FFEAU,0xFE28ED61U,0xEE7C3C73U, + 0x5D4A14D9U,0xE864B7E3U,0x42105D14U,0x203E13E0U,0x45EEE2B6U,0xA3AAABEAU,0xDB6C4F15U,0xFACB4FD0U, + 0xC742F442U,0xEF6ABBB5U,0x654F3B1DU,0x41CD2105U,0xD81E799EU,0x86854DC7U,0xE44B476AU,0x3D816250U, + 0xCF62A1F2U,0x5B8D2646U,0xFC8883A0U,0xC1C7B6A3U,0x7F1524C3U,0x69CB7492U,0x47848A0BU,0x5692B285U, + 0x095BBF00U,0xAD19489DU,0x1462B174U,0x23820E00U,0x58428D2AU,0x0C55F5EAU,0x1DADF43EU,0x233F7061U, + 0x3372F092U,0x8D937E41U,0xD65FECF1U,0x6C223BDBU,0x7CDE3759U,0xCBEE7460U,0x4085F2A7U,0xCE77326EU, + 0xA6078084U,0x19F8509EU,0xE8EFD855U,0x61D99735U,0xA969A7AAU,0xC50C06C2U,0x5A04ABFCU,0x800BCADCU, + 0x9E447A2EU,0xC3453484U,0xFDD56705U,0x0E1E9EC9U,0xDB73DBD3U,0x105588CDU,0x675FDA79U,0xE3674340U, + 0xC5C43465U,0x713E38D8U,0x3D28F89EU,0xF16DFF20U,0x153E21E7U,0x8FB03D4AU,0xE6E39F2BU,0xDB83ADF7U, + },{ + 0xE93D5A68U,0x948140F7U,0xF64C261CU,0x94692934U,0x411520F7U,0x7602D4F7U,0xBCF46B2EU,0xD4A20068U, + 0xD4082471U,0x3320F46AU,0x43B7D4B7U,0x500061AFU,0x1E39F62EU,0x97244546U,0x14214F74U,0xBF8B8840U, + 0x4D95FC1DU,0x96B591AFU,0x70F4DDD3U,0x66A02F45U,0xBFBC09ECU,0x03BD9785U,0x7FAC6DD0U,0x31CB8504U, + 0x96EB27B3U,0x55FD3941U,0xDA2547E6U,0xABCA0A9AU,0x28507825U,0x530429F4U,0x0A2C86DAU,0xE9B66DFBU, + 0x68DC1462U,0xD7486900U,0x680EC0A4U,0x27A18DEEU,0x4F3FFEA2U,0xE887AD8CU,0xB58CE006U,0x7AF4D6B6U, + 0xAACE1E7CU,0xD3375FECU,0xCE78A399U,0x406B2A42U,0x20FE9E35U,0xD9F385B9U,0xEE39D7ABU,0x3B124E8BU, + 0x1DC9FAF7U,0x4B6D1856U,0x26A36631U,0xEAE397B2U,0x3A6EFA74U,0xDD5B4332U,0x6841E7F7U,0xCA7820FBU, + 0xFB0AF54EU,0xD8FEB397U,0x454056ACU,0xBA489527U,0x55533A3AU,0x20838D87U,0xFE6BA9B7U,0xD096954BU, + 0x55A867BCU,0xA1159A58U,0xCCA92963U,0x99E1DB33U,0xA62A4A56U,0x3F3125F9U,0x5EF47E1CU,0x9029317CU, + 0xFDF8E802U,0x04272F70U,0x80BB155CU,0x05282CE3U,0x95C11548U,0xE4C66D22U,0x48C1133FU,0xC70F86DCU, + 0x07F9C9EEU,0x41041F0FU,0x404779A4U,0x5D886E17U,0x325F51EBU,0xD59BC0D1U,0xF2BCC18FU,0x41113564U, + 0x257B7834U,0x602A9C60U,0xDFF8E8A3U,0x1F636C1BU,0x0E12B4C2U,0x02E1329EU,0xAF664FD1U,0xCAD18115U, + 0x6B2395E0U,0x333E92E1U,0x3B240B62U,0xEEBEB922U,0x85B2A20EU,0xE6BA0D99U,0xDE720C8CU,0x2DA2F728U, + 0xD0127845U,0x95B794FDU,0x647D0862U,0xE7CCF5F0U,0x5449A36FU,0x877D48FAU,0xC39DFD27U,0xF33E8D1EU, + 0x0A476341U,0x992EFF74U,0x3A6F6EABU,0xF4F8FD37U,0xA812DC60U,0xA1EBDDF8U,0x991BE14CU,0xDB6E6B0DU, + 0xC67B5510U,0x6D672C37U,0x2765D43BU,0xDCD0E804U,0xF1290DC7U,0xCC00FFA3U,0xB5390F92U,0x690FED0BU, + 0x667B9FFBU,0xCEDB7D9CU,0xA091CF0BU,0xD9155EA3U,0xBB132F88U,0x515BAD24U,0x7B9479BFU,0x763BD6EBU, + 0x37392EB3U,0xCC115979U,0x8026E297U,0xF42E312DU,0x6842ADA7U,0xC66A2B3BU,0x12754CCCU,0x782EF11CU, + 0x6A124237U,0xB79251E7U,0x06A1BBE6U,0x4BFB6350U,0x1A6B1018U,0x11CAEDFAU,0x3D25BDD8U,0xE2E1C3C9U, + 0x44421659U,0x0A121386U,0xD90CEC6EU,0xD5ABEA2AU,0x64AF674EU,0xDA86A85FU,0xBEBFE988U,0x64E4C3FEU, + 0x9DBC8057U,0xF0F7C086U,0x60787BF8U,0x6003604DU,0xD1FD8346U,0xF6381FB0U,0x7745AE04U,0xD736FCCCU, + 0x83426B33U,0xF01EAB71U,0xB0804187U,0x3C005E5FU,0x77A057BEU,0xBDE8AE24U,0x55464299U,0xBF582E61U, + 0x4E58F48FU,0xF2DDFDA2U,0xF474EF38U,0x8789BDC2U,0x5366F9C3U,0xC8B38E74U,0xB475F255U,0x46FCD9B9U, + 0x7AEB2661U,0x8B1DDF84U,0x846A0E79U,0x915F95E2U,0x466E598EU,0x20B45770U,0x8CD55591U,0xC902DE4CU, + 0xB90BACE1U,0xBB8205D0U,0x11A86248U,0x7574A99EU,0xB77F19B6U,0xE0A9DC09U,0x662D09A1U,0xC4324633U, + 0xE85A1F02U,0x09F0BE8CU,0x4A99A025U,0x1D6EFE10U,0x1AB93D1DU,0x0BA5A4DFU,0xA186F20FU,0x2868F169U, + 0xDCB7DA83U,0x573906FEU,0xA1E2CE9BU,0x4FCD7F52U,0x50115E01U,0xA70683FAU,0xA002B5C4U,0x0DE6D027U, + 0x9AF88C27U,0x773F8641U,0xC3604C06U,0x61A806B5U,0xF0177A28U,0xC0F586E0U,0x006058AAU,0x30DC7D62U, + 0x11E69ED7U,0x2338EA63U,0x53C2DD94U,0xC2C21634U,0xBBCBEE56U,0x90BCB6DEU,0xEBFC7DA1U,0xCE591D76U, + 0x6F05E409U,0x4B7C0188U,0x39720A3DU,0x7C927C24U,0x86E3725FU,0x724D9DB9U,0x1AC15BB4U,0xD39EB8FCU, + 0xED545578U,0x08FCA5B5U,0xD83D7CD3U,0x4DAD0FC4U,0x1E50EF5EU,0xB161E6F8U,0xA28514D9U,0x6C51133CU, + 0x6FD5C7E7U,0x56E14EC4U,0x362ABFCEU,0xDDC6C837U,0xD79A3234U,0x92638212U,0x670EFA8EU,0x406000E0U, + },{ + 0x3A39CE37U,0xD3FAF5CFU,0xABC27737U,0x5AC52D1BU,0x5CB0679EU,0x4FA33742U,0xD3822740U,0x99BC9BBEU, + 0xD5118E9DU,0xBF0F7315U,0xD62D1C7EU,0xC700C47BU,0xB78C1B6BU,0x21A19045U,0xB26EB1BEU,0x6A366EB4U, + 0x5748AB2FU,0xBC946E79U,0xC6A376D2U,0x6549C2C8U,0x530FF8EEU,0x468DDE7DU,0xD5730A1DU,0x4CD04DC6U, + 0x2939BBDBU,0xA9BA4650U,0xAC9526E8U,0xBE5EE304U,0xA1FAD5F0U,0x6A2D519AU,0x63EF8CE2U,0x9A86EE22U, + 0xC089C2B8U,0x43242EF6U,0xA51E03AAU,0x9CF2D0A4U,0x83C061BAU,0x9BE96A4DU,0x8FE51550U,0xBA645BD6U, + 0x2826A2F9U,0xA73A3AE1U,0x4BA99586U,0xEF5562E9U,0xC72FEFD3U,0xF752F7DAU,0x3F046F69U,0x77FA0A59U, + 0x80E4A915U,0x87B08601U,0x9B09E6ADU,0x3B3EE593U,0xE990FD5AU,0x9E34D797U,0x2CF0B7D9U,0x022B8B51U, + 0x96D5AC3AU,0x017DA67DU,0xD1CF3ED6U,0x7C7D2D28U,0x1F9F25CFU,0xADF2B89BU,0x5AD6B472U,0x5A88F54CU, + 0xE029AC71U,0xE019A5E6U,0x47B0ACFDU,0xED93FA9BU,0xE8D3C48DU,0x283B57CCU,0xF8D56629U,0x79132E28U, + 0x785F0191U,0xED756055U,0xF7960E44U,0xE3D35E8CU,0x15056DD4U,0x88F46DBAU,0x03A16125U,0x0564F0BDU, + 0xC3EB9E15U,0x3C9057A2U,0x97271AECU,0xA93A072AU,0x1B3F6D9BU,0x1E6321F5U,0xF59C66FBU,0x26DCF319U, + 0x7533D928U,0xB155FDF5U,0x03563482U,0x8ABA3CBBU,0x28517711U,0xC20AD9F8U,0xABCC5167U,0xCCAD925FU, + 0x4DE81751U,0x3830DC8EU,0x379D5862U,0x9320F991U,0xEA7A90C2U,0xFB3E7BCEU,0x5121CE64U,0x774FBE32U, + 0xA8B6E37EU,0xC3293D46U,0x48DE5369U,0x6413E680U,0xA2AE0810U,0xDD6DB224U,0x69852DFDU,0x09072166U, + 0xB39A460AU,0x6445C0DDU,0x586CDECFU,0x1C20C8AEU,0x5BBEF7DDU,0x1B588D40U,0xCCD2017FU,0x6BB4E3BBU, + 0xDDA26A7EU,0x3A59FF45U,0x3E350A44U,0xBCB4CDD5U,0x72EACEA8U,0xFA6484BBU,0x8D6612AEU,0xBF3C6F47U, + 0xD29BE463U,0x542F5D9EU,0xAEC2771BU,0xF64E6370U,0x740E0D8DU,0xE75B1357U,0xF8721671U,0xAF537D5DU, + 0x4040CB08U,0x4EB4E2CCU,0x34D2466AU,0x0115AF84U,0xE1B00428U,0x95983A1DU,0x06B89FB4U,0xCE6EA048U, + 0x6F3F3B82U,0x3520AB82U,0x011A1D4BU,0x277227F8U,0x611560B1U,0xE7933FDCU,0xBB3A792BU,0x344525BDU, + 0xA08839E1U,0x51CE794BU,0x2F32C9B7U,0xA01FBAC9U,0xE01CC87EU,0xBCC7D1F6U,0xCF0111C3U,0xA1E8AAC7U, + 0x1A908749U,0xD44FBD9AU,0xD0DADECBU,0xD50ADA38U,0x0339C32AU,0xC6913667U,0x8DF9317CU,0xE0B12B4FU, + 0xF79E59B7U,0x43F5BB3AU,0xF2D519FFU,0x27D9459CU,0xBF97222CU,0x15E6FC2AU,0x0F91FC71U,0x9B941525U, + 0xFAE59361U,0xCEB69CEBU,0xC2A86459U,0x12BAA8D1U,0xB6C1075EU,0xE3056A0CU,0x10D25065U,0xCB03A442U, + 0xE0EC6E0EU,0x1698DB3BU,0x4C98A0BEU,0x3278E964U,0x9F1F9532U,0xE0D392DFU,0xD3A0342BU,0x8971F21EU, + 0x1B0A7441U,0x4BA3348CU,0xC5BE7120U,0xC37632D8U,0xDF359F8DU,0x9B992F2EU,0xE60B6F47U,0x0FE3F11DU, + 0xE54CDA54U,0x1EDAD891U,0xCE6279CFU,0xCD3E7E6FU,0x1618B166U,0xFD2C1D05U,0x848FD2C5U,0xF6FB2299U, + 0xF523F357U,0xA6327623U,0x93A83531U,0x56CCCD02U,0xACF08162U,0x5A75EBB5U,0x6E163697U,0x88D273CCU, + 0xDE966292U,0x81B949D0U,0x4C50901BU,0x71C65614U,0xE6C6C7BDU,0x327A140AU,0x45E1D006U,0xC3F27B9AU, + 0xC9AA53FDU,0x62A80F00U,0xBB25BFE2U,0x35BDD2F6U,0x71126905U,0xB2040222U,0xB6CBCF7CU,0xCD769C2BU, + 0x53113EC0U,0x1640E3D3U,0x38ABBD60U,0x2547ADF0U,0xBA38209CU,0xF746CE76U,0x77AFA1C5U,0x20756060U, + 0x85CBFE4EU,0x8AE88DD8U,0x7AAAF9B0U,0x4CF9AA7EU,0x1948C25CU,0x02FB8A8CU,0x01C36AE4U,0xD6EBE1F9U, + 0x90D4F869U,0xA65CDEA0U,0x3F09252DU,0xC208E69FU,0xB74E6132U,0xCE77E25BU,0x578FDFE3U,0x3AC372E6U + } +}; + diff --git a/CODE/BLOWFISH.H b/CODE/BLOWFISH.H new file mode 100644 index 0000000..62107b2 --- /dev/null +++ b/CODE/BLOWFISH.H @@ -0,0 +1,117 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLOWFISH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLOWFISH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : April 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BLOWFISH_H +#define BLOWFISH_H + +#include + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This engine will process data blocks by encryption and decryption. +** The "Blowfish" algorithm is in the public domain. It uses +** a Feistal network (similar to IDEA). It has no known +** weaknesses, but is still relatively new. Blowfish is particularly strong +** against brute force attacks. It is also quite strong against linear and +** differential cryptanalysis. Its weakness is that it takes a relatively +** long time to set up with a new key (1/100th of a second on a P6-200). +** The time to set up a key is equivalent to encrypting 4240 bytes. +*/ +class BlowfishEngine { + public: + BlowfishEngine(void) : IsKeyed(false) {} + ~BlowfishEngine(void); + + void Submit_Key(void const * key, int length); + + int Encrypt(void const * plaintext, int length, void * cyphertext); + int Decrypt(void const * cyphertext, int length, void * plaintext); + + /* + ** This is the maximum key length supported. + */ + enum {MAX_KEY_LENGTH=56}; + + private: + bool IsKeyed; + + void Sub_Key_Encrypt(unsigned long & left, unsigned long & right); + + void Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable); + void Initialize_Tables(void); + + enum { + ROUNDS = 16, // Feistal round count (16 is standard). + BYTES_PER_BLOCK=8 // The number of bytes in each cypher block (don't change). + }; + + /* + ** Initialization data for sub keys. The initial values are constant and + ** filled with a number generated from pi. Thus they are not random but + ** they don't hold a weak pattern either. + */ + static unsigned long const P_Init[(int)ROUNDS+2]; + static unsigned long const S_Init[4][UCHAR_MAX+1]; + + /* + ** Permutation tables for encryption and decryption. + */ + unsigned long P_Encrypt[(int)ROUNDS+2]; + unsigned long P_Decrypt[(int)ROUNDS+2]; + + /* + ** S-Box tables (four). + */ + unsigned long bf_S[4][UCHAR_MAX+1]; +}; + +#endif + diff --git a/CODE/BLOWPIPE.CPP b/CODE/BLOWPIPE.CPP new file mode 100644 index 0000000..25fcccb --- /dev/null +++ b/CODE/BLOWPIPE.CPP @@ -0,0 +1,202 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLOWPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLOWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowpipe.h" +#include +#include + + +/*********************************************************************************************** + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * * + * If there is any pending data in the holding buffer, then this routine will force it to * + * be flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of bytes output at the end final distant pipe * + * segment in the chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Flush(void) +{ + int total = 0; + if (Counter > 0 && BF != NULL) { + total += Pipe::Put(Buffer, Counter); + } + Counter = 0; + total += Pipe::Flush(); + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * * + * This will take the data block specified and process it before passing it on to the next * + * link in the pipe chain. A key must be submitted before this routine will actually perform* + * any processing. Prior to key submission, the data is passed through unchanged. * + * * + * INPUT: source -- Pointer to the buffer that contains the data to pass through. * + * * + * length -- The length of the data in the buffer. * + * * + * OUTPUT: Returns with then actual number of bytes output at the final distant end link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged in any way. + */ + if (BF == NULL) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + /* + ** If there is a partial block accumulated, then tag on the new data to + ** this block and process it if the block is full. Proceed with the bulk + ** processing if there are any left over bytes from this step. This step + ** can be skipped if there are no pending bytes in the buffer. + */ + if (Counter) { + int sublen = (sizeof(Buffer)-Counter < slen) ? (sizeof(Buffer)-Counter) : slen; + memmove(&Buffer[Counter], source, sublen); + Counter += sublen; + source = ((char *)source) + sublen; + slen -= sublen; + + if (Counter == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(Buffer, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + Counter = 0; + } + } + + /* + ** Process the input data in blocks until there is not enough + ** source data to fill a full block of data. + */ + while (slen >= sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(source, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(source, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + source = ((char *)source) + sizeof(Buffer); + slen -= sizeof(Buffer); + } + + /* + ** If there are any left over bytes, then they must be less than the size of + ** the staging buffer. Store the bytes in the staging buffer for later + ** processing. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + + /* + ** Return with the total number of bytes flushed out to the final end of the + ** pipe chain. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * * + * This routine will take the key provided and use it to process the data that passes * + * through this pipe. Prior to a key being submitted, the data passes through the pipe * + * unchanged. * + * * + * INPUT: key -- Pointer to the key data to use. * + * * + * length-- The length of the key. The key length must not be greater than 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowPipe::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} + + + diff --git a/CODE/BLOWPIPE.H b/CODE/BLOWPIPE.H new file mode 100644 index 0000000..47f2d92 --- /dev/null +++ b/CODE/BLOWPIPE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLOWPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLOWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLOWPIPE_H +#define BLOWPIPE_H + +#include "pipe.h" +#include "blowfish.h" + +/* +** Performs Blowfish encryption/decryption on the data stream that is piped +** through this class. +*/ +class BlowPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowPipe(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowPipe(void) {delete BF;BF = NULL;} + virtual int Flush(void); + + virtual int Put(void const * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this pipe unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowPipe(BlowPipe & rvalue); + BlowPipe & operator = (BlowPipe const & pipe); +}; + + +#endif diff --git a/CODE/BLWSTRAW.CPP b/CODE/BLWSTRAW.CPP new file mode 100644 index 0000000..9931811 --- /dev/null +++ b/CODE/BLWSTRAW.CPP @@ -0,0 +1,161 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLWSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowStraw::Get -- Fetch a block of data from the straw. * + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blwstraw.h" +#include +#include + + +/*********************************************************************************************** + * BlowStraw::Get -- Fetch a block of data from the straw. * + * * + * This routine will take a block of data from the straw and process it according to the * + * encrypt/decrypt flag and the key supplied. Prior to a key be supplied, the data passes * + * through this straw unchanged. * + * * + * INPUT: source -- Pointer to the buffer to hold the data being requested. * + * * + * length -- The length of the data being requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If the number * + * returned is less than the number requested, then this indicates that the data * + * source has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowStraw::Get(void * source, int slen) +{ + /* + ** Verify the parameter for legality. + */ + if (source == NULL || slen <= 0) { + return(0); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged. + */ + if (BF == NULL) { + return(Straw::Get(source, slen)); + } + + int total = 0; + + while (slen > 0) { + + /* + ** If there are any left over bytes in the buffer, pass them + ** through first. + */ + if (Counter > 0) { + int sublen = (slen < Counter) ? slen : Counter; + memmove(source, &Buffer[sizeof(Buffer)-Counter], sublen); + Counter -= sublen; + source = ((char *)source) + sublen; + slen -= sublen; + total += sublen; + } + if (slen == 0) break; + + /* + ** Fetch and encrypt/decrypt the next block. + */ + int incount = Straw::Get(Buffer, sizeof(Buffer)); + if (incount == 0) break; + + /* + ** Only full blocks are processed. Partial blocks are + ** merely passed through unchanged. + */ + if (incount == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, incount, Buffer); + } else { + BF->Encrypt(Buffer, incount, Buffer); + } + } else { + memmove(&Buffer[sizeof(Buffer)-incount], Buffer, incount); + } + Counter = incount; + } + + /* + ** Return with the total number of bytes placed into the buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * * + * This will take the key specified and use it to process the data that flows through this * + * straw segment. Prior to a key being submitted, the data will flow through unchanged. * + * * + * INPUT: key -- Pointer to the key to submit. * + * * + * length-- The length of the key. The length must not exceed 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowStraw::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} diff --git a/CODE/BLWSTRAW.H b/CODE/BLWSTRAW.H new file mode 100644 index 0000000..8b5658b --- /dev/null +++ b/CODE/BLWSTRAW.H @@ -0,0 +1,87 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BLWSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BLWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLWSTRAW_H +#define BLWSTRAW_H + +#include "straw.h" +#include "blowfish.h" + + +/* +** Performs Blowfish encryption/decryption to the data that is drawn through this straw. The +** process is controlled by the key which must be submitted to the class before any data +** manipulation will occur. The Blowfish algorithm is symmetric, thus the same key is used +** for encryption as is for decryption. +*/ +class BlowStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowStraw(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowStraw(void) {delete BF;BF = NULL;} + + virtual int Get(void * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this straw unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowStraw(BlowStraw & rvalue); + BlowStraw & operator = (BlowStraw const & straw); +}; + + +#endif diff --git a/CODE/BMP8.CPP b/CODE/BMP8.CPP new file mode 100644 index 0000000..5cad8f4 --- /dev/null +++ b/CODE/BMP8.CPP @@ -0,0 +1,189 @@ +/* +** 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 . +*/ + + +#include "bmp8.h" + +//*********************************************************************************************** +BMP8::~BMP8() +{ + // free resources + if( hBitmap ) + ::DeleteObject( hBitmap ); + if( hPal ) + ::DeleteObject( hPal ); +} + +//*********************************************************************************************** +bool BMP8::Init( const char* szFile, HWND hWnd ) +{ + int i; + char string[128]; + DWORD dwRead; + BITMAPFILEHEADER bitmapHeader; + BITMAPINFOHEADER bitmapInfoHeader; + LPLOGPALETTE lpLogPalette; + char *palData; + HGLOBAL hmem2; + LPVOID lpvBits; + PAINTSTRUCT ps; + HDC hdc; + HPALETTE select; + UINT realize; + RECT rect; + + + // Remember window handle for use later. + this->hWnd = hWnd; + + // Retrieve a handle identifying the file. + HANDLE hFile = ::CreateFile( + szFile, + GENERIC_READ, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + (HANDLE)NULL ); + + if( !hFile ) + return false; + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapHeader, sizeof(BITMAPFILEHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Allocate memory for the BITMAPINFO structure. + HGLOBAL infoHeaderMem = ::GlobalAlloc( GHND, sizeof(BITMAPINFOHEADER) + ((1<bmiHeader.biSize = bitmapInfoHeader.biSize; + lpHeaderMem->bmiHeader.biWidth = bitmapInfoHeader.biWidth; + lpHeaderMem->bmiHeader.biHeight = bitmapInfoHeader.biHeight; + lpHeaderMem->bmiHeader.biPlanes = bitmapInfoHeader.biPlanes; + lpHeaderMem->bmiHeader.biBitCount = bitmapInfoHeader.biBitCount; + lpHeaderMem->bmiHeader.biCompression = bitmapInfoHeader.biCompression; + lpHeaderMem->bmiHeader.biSizeImage = bitmapInfoHeader.biSizeImage; + lpHeaderMem->bmiHeader.biXPelsPerMeter = bitmapInfoHeader.biXPelsPerMeter; + lpHeaderMem->bmiHeader.biYPelsPerMeter = bitmapInfoHeader.biYPelsPerMeter; + lpHeaderMem->bmiHeader.biClrUsed = bitmapInfoHeader.biClrUsed; + lpHeaderMem->bmiHeader.biClrImportant = bitmapInfoHeader.biClrImportant; + + // Retrieve the color table. + // 1 << bitmapInfoHeader.biBitCount == 2 ^ bitmapInfoHeader.biBitCount + ::ReadFile( hFile, lpHeaderMem->bmiColors, ((1<palVersion=0x300; + lpLogPalette->palNumEntries=256; + + palData = (char*)lpHeaderMem->bmiColors; + + for( i = 0; i < 256; i++ ) + { + lpLogPalette->palPalEntry[i].peRed = *palData++; + lpLogPalette->palPalEntry[i].peGreen = *palData++; + lpLogPalette->palPalEntry[i].peBlue = *palData++; + lpLogPalette->palPalEntry[i].peFlags = *palData++; + } + hPal = ::CreatePalette( lpLogPalette ); + delete [] lpLogPalette; + + // Allocate memory for the required number of bytes. + hmem2 = ::GlobalAlloc( GHND, (bitmapHeader.bfSize - bitmapHeader.bfOffBits) ); + + lpvBits = ::GlobalLock( hmem2 ); + + // Retrieve the bitmap data. + ::ReadFile( hFile, lpvBits, (bitmapHeader.bfSize - bitmapHeader.bfOffBits), &dwRead, (LPOVERLAPPED)NULL ); + + // Create a bitmap from the data stored in the .BMP file. + hdc = ::GetDC( hWnd ); + select = ::SelectPalette( hdc, hPal, 0 ); + if( !select ) + return false; + realize = ::RealizePalette( hdc ); + if( realize == GDI_ERROR ) + return false; + + hBMP = ::CreateDIBitmap( hdc, &bitmapInfoHeader, CBM_INIT, lpvBits, lpHeaderMem, DIB_RGB_COLORS ); + ::ReleaseDC( hWnd, hdc ); + + // Unlock the global memory objects and close the .BMP file. + ::GlobalUnlock( infoHeaderMem ); + ::GlobalUnlock( hmem2 ); + ::CloseHandle( hFile ); + + if( !hBMP ) + return false; + + return true; +} + + +bit8 BMP8::drawBmp(void) +{ + // Paint the window (and draw the bitmap). + + PAINTSTRUCT ps; + HDC hdc; + char string[128]; + + InvalidateRect(WindowHandle_,NULL,FALSE); // keep windows from screwing up the + // redrawing (as much). + hdc=BeginPaint(WindowHandle_,&ps); + + //Do palette stuff + HPALETTE select=SelectPalette(ps.hdc,PalHandle_,0); + if (select==NULL) + { + sprintf(string,"Select Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + UINT realize=RealizePalette(ps.hdc); + if (realize==GDI_ERROR) + { + sprintf(string,"Realize Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + + HDC hdcMem = CreateCompatibleDC(ps.hdc); + SelectObject(hdcMem, BitmapHandle_); + BITMAP bm; + GetObject(BitmapHandle_, sizeof(BITMAP), (LPSTR) &bm); + + /// for non-stretching version + ///////BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + RECT clientRect; + GetClientRect(WindowHandle_,&clientRect); + SetStretchBltMode(ps.hdc,COLORONCOLOR); + StretchBlt(ps.hdc,0,0,clientRect.right,clientRect.bottom,hdcMem,0,0,bm.bmWidth, + bm.bmHeight,SRCCOPY); + + + DeleteDC(hdcMem); + EndPaint(WindowHandle_,&ps); + return(TRUE); +} diff --git a/CODE/BMP8.H b/CODE/BMP8.H new file mode 100644 index 0000000..25adc97 --- /dev/null +++ b/CODE/BMP8.H @@ -0,0 +1,43 @@ +/* +** 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 . +*/ + +#ifndef BMP8_H +#define BMP8_H + +//#include +//#include +//#include "wstypes.h" +//#include "winblows.h" + +class BMP8 +{ +public: + BMP8() : hBMP( NULL ), hPal( NULL ), hWnd( NULL ) {} + ~BMP8(); + + bool Init( const char* szFile, HWND hWnd ); + bool Draw(void); // call this from your WM_PAINT message + +private: + HBITMAP hBMP; + HPALETTE hPal; + HWND hWnd; +}; + + +#endif \ No newline at end of file diff --git a/CODE/BUFF.CPP b/CODE/BUFF.CPP new file mode 100644 index 0000000..fe9c29f --- /dev/null +++ b/CODE/BUFF.CPP @@ -0,0 +1,220 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BUFF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BUFF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : September 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Buffer::Buffer -- Constructor for buffer object. * + * Buffer::Buffer -- Copy constructor for buffer object. * + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * Buffer::Reset -- Clears the buffer object to null state. * + * Buffer::operator = -- Assignment operator for the buffer object. * + * Buffer::~Buffer -- Destructor for buffer object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include + + +/*********************************************************************************************** + * Buffer::Buffer -- Constructor for buffer object. * + * * + * This is the normal constructor for a buffer object. The buffer pointer and size are * + * specified as parameters. * + * * + * INPUT: buffer -- Pointer to the buffer. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to construct a Buffer object that has a pointer but a size * + * value of zero. The Buffer object can still be used for its pointer, but it * + * any function that requires a size will fail. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(void * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for char * pointer. +Buffer::Buffer(char * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for void const * pointer. +Buffer::Buffer(void const * buffer, long size) : + BufferPtr((void*)buffer), + Size(size), + IsAllocated(false) +{ +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * * + * This construtor for a buffer object will automatically allocate the bytes necessary * + * to fulfill the size requested. This object is also responsible for deleting the buffer * + * it allocated. * + * * + * INPUT: size -- The size of the buffer to allocated. * + * * + * OUTPUT: none * + * * + * WARNINGS: There is no way to tell if the allocation failed. To verify, call Get_Buffer * + * and compare with NULL. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(long size) : + BufferPtr(NULL), + Size(size), + IsAllocated(false) +{ + if (size > 0) { + BufferPtr = new char[size]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Copy constructor for buffer object. * + * * + * This will make a duplicate of the specified buffer object. The ownership of the pointer * + * remains with the original object. This prevents multiple deletion of the same pointer. * + * * + * INPUT: buffer -- Reference to the buffer object to be dupilcated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(Buffer const & buffer) : + IsAllocated(false) +{ + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; +} + + +/*********************************************************************************************** + * Buffer::operator = -- Assignment operator for the buffer object. * + * * + * This will make a duplicate of the buffer object specified. Any buffer pointed to by the * + * left hand buffer will be lost (possibley freed as a result). * + * * + * INPUT: buffer -- Reference to the right hand buffer object. * + * * + * OUTPUT: Returns with a reference to the copied buffer object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer & Buffer::operator = (Buffer const & buffer) +{ + if (buffer != this) { + if (IsAllocated) { + delete [] BufferPtr; + } + IsAllocated = false; + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; + } + return(*this); +} + + +/*********************************************************************************************** + * Buffer::~Buffer -- Destructor for buffer object. * + * * + * This destructor will free any buffer it is responsible for. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::~Buffer(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * Buffer::Reset -- Clears the buffer object to null state. * + * * + * This routine will bring the buffer object into a null (newly constructed) state. If * + * there was any buffer allocated or referred to by this object, it will be freed or * + * dereferenced as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will free the buffer if it is responsible for doing so when * + * it is no longer referenced. * + * * + * HISTORY: * + * 09/07/1996 JLB : Created. * + *=============================================================================================*/ +void Buffer::Reset(void) +{ + if (IsAllocated) { + delete [] BufferPtr; + } + BufferPtr = NULL; + Size = 0; + IsAllocated = false; +} diff --git a/CODE/BUFF.H b/CODE/BUFF.H new file mode 100644 index 0000000..1beca3f --- /dev/null +++ b/CODE/BUFF.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BUFF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BUFF.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCBUFF_H +#define CCBUFF_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** A general purpose buffer pointer handler object. It holds not only the pointer to the +** buffer, but its size as well. By using this class instead of separate pointer and size +** values, function interfaces and algorithms become simpler to manage and understand. +*/ +class Buffer { + public: + Buffer(char * ptr, long size=0); + Buffer(void * ptr=0, long size=0); + Buffer(void const * ptr, long size=0); + Buffer(long size); + Buffer(Buffer const & buffer); + ~Buffer(void); + + Buffer & operator = (Buffer const & buffer); + operator void * (void) const {return(BufferPtr);} + operator char * (void) const {return((char *)BufferPtr);} + + void Reset(void); + void * Get_Buffer(void) const {return(BufferPtr);} + long Get_Size(void) const {return(Size);} + bool Is_Valid(void) const {return(BufferPtr != 0);} + + protected: + + /* + ** Pointer to the buffer memory. + */ + void * BufferPtr; + + /* + ** The size of the buffer memory. + */ + long Size; + + /* + ** Was the buffer allocated by this class? If so, then this class + ** will be responsible for freeing the buffer. + */ + bool IsAllocated; +}; + + +#endif diff --git a/CODE/BUFFERX.H b/CODE/BUFFERX.H new file mode 100644 index 0000000..a9e1f82 --- /dev/null +++ b/CODE/BUFFERX.H @@ -0,0 +1,101 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BUFFERX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BUFFER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/04/96 * + * * + * Last Update : May 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BUFFERx_H +#define BUFFERx_H + +#include "wwfile.h" + +/* +** This is a transmuter interface designed to aid implementation of compression, encryption, or +** data analysis classes. The Transmuter should be derived into a class that performs the necessary +** processing. +*/ +class Transmuter { + public: + Transmuter(void) : Output(0) {} + virtual ~Transmuter(void) {} + + /* + ** These are the interface function that are used to pass data to the transmuter. The + ** default implementation of these functions do nothing other than pass the data onto + ** the subsequent transmuter. For practical use, these functions should be overloaded to + ** do something more useful. + */ + virtual void Attach(Transmuter * transmuter) {Output = transmuter;} + virtual void Flush(void) {if (Output) Output->Flush();} + virtual void Put(const void * input, unsigned length) {if (Output) Output->Put(input, length);} + + protected: + + /* + ** Pointer to the output transmuter. + */ + Transmuter * Output; +}; + + +class FileTransmuter { + public: + FileTransmuter(FileClass * file = NULL) : OutputFile(file) {} + + virtual void Attach(FileClass * file) {OutputFile = file;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (OutputFile) OutputFile->Write(input, length);} + + protected: + FileClass * OutputFile; +}; + + +class BufferTransmuter { + public: + BufferTransmuter(void * buffer = NULL) : BufferPtr(buffer) {} + + virtual void Attach(void * buffer) {BufferPtr = buffer;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (BufferPtr) {memcpy(BufferPtr, input, length);((char *&)BufferPtr) += length;}} + + protected: + void * BufferPtr; +}; + + +#endif + + diff --git a/CODE/BUGS.TXT b/CODE/BUGS.TXT new file mode 100644 index 0000000..418c724 --- /dev/null +++ b/CODE/BUGS.TXT @@ -0,0 +1,64 @@ +Fixed: +A415,A436,A437,A446,A447,A453,A466,A475,A488, +B171,B326,B407,B443,B530,B550,B566,B578,B600, +B609,B626,B628,B632,B637,B641,B644,B655,B658, +B675,B678,B684,B685,B688,B690,B698,B711,B715, +B726,B730,B731,B729,B732,B733,B738,B743,B744, +B747,B748,B750,B753,B759,B762,B766,B767,B768, +B770,B774,B775,B777,B778,B780,B781,B786,B795, +B797,B798,B805,C1001,C1007,C1011,C1012,C1014, +C1017,C1018,C1020,C1024,C1031,C1037,C1042,C1045, +C1052,C1061,C1054,C288,C487,C543,C546, +C666,C671,C697,C702,C773,C841,C856,C869,C871, +C887,C888,C904,C919,C922,C923,C925,C926 +C927,C930,C932,C936,C938,C939,C942,C945,C947, +C953,C954,C962,C966,C967,C968,C975,C976,C977 +C981,C982 + +Unsolved: +A035,A248,A352,A383,A417,A419,A420,A427,A445,A449, +A450,A455,A456,A457,A469,A470,A477,A478,A480,B005, +B042,B206,B207,B247,B284,B356,B418,B430,B467,B536, +B548,B549,B559,B633,B674,B686,B703,B714,B725,B740, +B745,B785,B787,B794,B796,B799,B802,B809,B817,B823, +C033,C072,C1000,C1002,C1004,C1006,C1008,C1009,C1013,C1016, +C1022,C1026,C1028,C1029,C1030,C1047,C1053,C1054,C1056,C1057, +C1059,C197,C318,C407,C409,C453,C502,C527,C530,C551, +C554,C573,C606,C676,C682,C694,C750,C776,C793,C811, +C821,C834,C867,C872,C877,C881,C896,C898,C901,C921, +C928,C929,C931,C933,C934,C940,C941,C943,C944,C946, +C952,C961,C964,C965,C969,C971,C794,C987,C988,C992, +C997 + +Old / Need more info / non-repeatable (maybe it's fixed now?): +A380,A388,A401,A414,A482,B445,B791, +A342,A402,A413,A434,A443,A460,A461,A462,A463,A468, +A472,A473,A476,A485,A489,A490,B1044,B513,B524,B608 +B721,B773,B789,B807,B812,C570,C633,C654,C744,C778, +C853,C913,C963,C991,C998 + +Modem/Net Bug (for Bill): +A431,A452,A454,A464,A471,A481,A483,A486,A487, +B742,B760,B761,B764,B765,B772,B792,C1025,C1036,C1038 +C883,C886,C895,C950,C951,C956,C993,C995 + +Design Bug (for Erik): +A432,A433,A448,A479,B041,B414,B659,B717,B719,B720, +B722,B723,B727,B728,B735,B736,B737,B741, +B756,B757,B758,B784,B802,B803,B808,B810,B815, +B816,B818,B822,C1010,C1023,C1027,C1035,C1044, +C1062,C534,C568,C637,C663,C918,C920,C924,C948, +C949,C986,C996,C999 + +Setup/Install/README.TXT Bug: +A458,A467,A484,B395,C957,C958,C959 + +Not Bug, repeated bug, suggestion, can't fix, or huh?: +A459,B1024,B218,B507,B522,B541,B546,B592,B593,B596, +B687,B724,B739,B746,B749,B763,B776,B779,B782,B783, +B790,B793,B806,B819,B820,B821,C1003,C1005,C1015,C1019, +C1032,C1033,C1034,C1039,C1040,C1043,C1048, +C1050,C1051,C1055,C1058,C1060,C1063,C336,C377, +C491,C537,C541,C578,C691,C703,C716,C781,C792,C838, +C854,C860,C879,C912,C915,C935,C937,C955,C970,C978, +C979,C980,C983,C984,C985,C990 \ No newline at end of file diff --git a/CODE/BUILDING.CPP b/CODE/BUILDING.CPP new file mode 100644 index 0000000..b47c8b2 --- /dev/null +++ b/CODE/BUILDING.CPP @@ -0,0 +1,5712 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BUILDING.CPP 5 3/13/97 5:18p Joe_b $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BUILDING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * BuildingClass::BuildingClass -- Constructor for buildings. * + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * BuildingClass::Captured -- Captures the building. * + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern.* + * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * BuildingClass::Detach -- Handles target removal from the game system. * + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this buildin* + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * BuildingClass::Mark -- Building interface to map rendering system. * + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * BuildingClass::Mission_Construction -- Handles mission construction. * + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells * + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * BuildingClass::Value -- Determine the value of this building. * + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * BuildingClass::What_Action -- Determines what action will occur. * + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * BuildingClass::delete -- Deallocates building object. * + * BuildingClass::new -- Allocates a building object from building pool. * + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +enum SAMState { + SAM_READY, // Launcher can be facing any direction tracking targets. + SAM_FIRING // Stationary while missile is being fired. +}; + + +/*************************************************************************** +** Center of building offset table. +*/ +COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { + 0x00800080L, + 0x008000FFL, + 0x00FF0080L, + 0x00FF00FFL, + 0x018000FFL, + 0x00FF0180L, + 0x01800180L, + + 0x00FF0200L, + + 0x02800280L, +}; + + +/*********************************************************************************************** + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * * + * This routine handles an incoming message to the building. Messages regulate the * + * various cooperative ventures between buildings and units. This might include such * + * actions as coordinating the construction yard animation with the actual building's * + * construction animation. * + * * + * INPUT: from -- The originator of the message received. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter that might be used to return * + * extra information to the message originator. * + * * + * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/09/1994 JLB : Created. * + * 06/26/1995 JLB : Forces refinery load anim to start immediately. * + * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * + *=============================================================================================*/ +RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** This message is received as a request to attach/load/dock with this building. + ** Verify that this is allowed and return the appropriate response. + */ + case RADIO_CAN_LOAD: + TechnoClass::Receive_Message(from, message, param); + if (!House->Is_Ally(from)) return(RADIO_STATIC); + if (Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION || BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact() && Contact_With_Whom() != from)) return(RADIO_NEGATIVE); + switch (Class->Type) { + case STRUCT_AIRSTRIP: + if (from->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_HELIPAD: + if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_REPAIR: + if (from->What_Am_I() == RTTI_UNIT || (from->What_Am_I() == RTTI_AIRCRAFT)) { + if (Transmit_Message(RADIO_ON_DEPOT, from) != RADIO_ROGER) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + case STRUCT_REFINERY: + if (from->What_Am_I() == RTTI_UNIT && + *((UnitClass *)from) == UNIT_HARVESTER && + (ScenarioInit || !Is_Something_Attached())) { + + return(RADIO_ROGER); + } + break; + + default: + break; + } + return(RADIO_STATIC); + + /* + ** This message is received when the object has attached itself to this + ** building. + */ + case RADIO_IM_IN: + if (Mission == MISSION_DECONSTRUCTION) { + return(RADIO_NEGATIVE); + } + switch (Class->Type) { + case STRUCT_REPAIR: + IsReadyToCommence = true; + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_AIRSTRIP: + case STRUCT_HELIPAD: + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_REFINERY: + Mark(MARK_CHANGE); + from->Assign_Mission(MISSION_UNLOAD); + return(RADIO_ROGER); + + default: + break; + } + break; + + /* + ** Docking maneuver maintenance message. See if new order should be given to the + ** unit trying to dock. + */ + case RADIO_DOCKING: + TechnoClass::Receive_Message(from, message, param); + + /* + ** When in radio contact for loading, the refinery starts + ** flashing the lights. + */ + if (*this == STRUCT_REFINERY && BState != BSTATE_FULL) { + Begin_Mode(BSTATE_FULL); + } + + /* + ** If this building is already in radio contact, then it might + ** be able to satisfy the request to load by bumping off any + ** preoccupying task. + */ + if (*this == STRUCT_REPAIR) { + if (Contact_With_Whom() != from) { + if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) { + if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) { + Transmit_Message(RADIO_RUN_AWAY); + return(RADIO_ROGER); + } +// } else { +// if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { +// param = (long)As_Target(); +// Transmit_Message(RADIO_MOVE_HERE, param, from); +// } + } + } else { + if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) { + return(RADIO_NEGATIVE); + } + } + } + + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + switch (Class->Type) { + case STRUCT_AIRSTRIP: + param = As_Target(); + break; + + case STRUCT_HELIPAD: + param = As_Target(); + break; + + case STRUCT_REPAIR: + Transmit_Message(RADIO_TETHER); + param = ::As_Target(Coord_Cell(Center_Coord())); + break; + + case STRUCT_REFINERY: + param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S))); + break; + } + + /* + ** Tell the harvester to move to the docking pad of the building. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + + /* + ** Since the harvester is already there, tell it to begin the backup + ** procedure now. If it can't, then tell it to get outta here. + */ + Transmit_Message(RADIO_TETHER); + if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { + from->Scatter(NULL, true, true); + } + } + } + return(RADIO_ROGER); + + /* + ** If a transport or harvester is requesting permission to head toward, dock + ** and load/unload, check to make sure that this is allowed given the current + ** state of the building. + */ + case RADIO_ARE_REFINERY: + if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { + return(RADIO_NEGATIVE); + } + return(RADIO_ROGER); + + /* + ** Someone is telling us that it is starting construction. This should only + ** occur if this is a construction yard and a building was just placed on + ** the map. + */ + case RADIO_BUILDING: + Assign_Mission(MISSION_REPAIR); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Someone is telling us that they have finished construction. This should + ** only occur if this is a construction yard and the building that was being + ** constructed has finished. In this case, stop the construction yard + ** animation. + */ + case RADIO_COMPLETE: + if (Mission != MISSION_DECONSTRUCTION) { + Assign_Mission(MISSION_GUARD); + } + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message may occur unexpectedly if the unit in contact with this + ** building is suddenly destroyed. Handle any cleanup necessary. For example, + ** a construction yard should stop its construction animation in this case. + */ + case RADIO_OVER_OUT: + Begin_Mode(BSTATE_IDLE); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message is received when an object has completely left + ** building. Sometimes special cleanup action is required when + ** this event occurs. + */ + case RADIO_UNLOADED: + if (*this == STRUCT_REPAIR) { + if (Distance(from) < 0x0180) { + return(RADIO_ROGER); + } + } + TechnoClass::Receive_Message(from, message, param); + if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + return(RADIO_ROGER); + + default: + break; + } + + /* + ** Pass along the message to the default message handler in the radio itself. + */ + return(TechnoClass::Receive_Message(from, message, param)); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * * + * This utility function will output the current status of the building class to the * + * monochrome screen. It is through this data that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Debug_Dump(MonoClass * mono) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_BUILDING)); + mono->Fill_Attrib(66, 13, 12, 1, IsRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToRebuild ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsAllowedToSell ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 16, 12, 1, IsCharging ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 17, 12, 1, IsCharged ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 18, 12, 1, IsJamming ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 19, 12, 1, IsJammed ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(1, 11); + if (Factory) { + mono->Printf("%s %d%%", Factory->Get_Object()->Class_Of().IniName, (100*Factory->Completion())/FactoryClass::STEP_COUNT); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * * + * This is the low level graphic routine that displays the building at the location * + * specified. * + * * + * INPUT: x,y -- The coordinate to draw the building at. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a clipping window parameter. * + * 07/06/1995 JLB : Handles damaged silos correctly. * + *=============================================================================================*/ +void BuildingClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** Actually draw the building shape. + */ + IsTheaterShape = Class->IsTheater; //Let Build_Frame know if this is a theater specific shape + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window); + IsTheaterShape = false; + + /* + ** Patch for adding overlay onto weapon factory. Only add the overlay if + ** the building has more than 1 hp. Also, if the building's in radio + ** contact, he must be unloading a constructed vehicle, so draw that + ** vehicle before drawing the overlay. + */ + if (BState != BSTATE_CONSTRUCTION) { + + /* + ** A Tethered object is always rendered AFTER the building. + */ + if (IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo && Contact_With_Whom()->What_Am_I() != RTTI_BUILDING) { + TechnoClass * contact = Contact_With_Whom(); + + assert(contact->IsActive); + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } + + /* + ** Draw the weapon factory custom overlay graphic. + */ + if ( (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP)) { + int shapenum = Door_Stage(); + if (Health_Ratio() <= Rule.ConditionYellow) shapenum += 4; + Techno_Draw_Object(Class->WarFactoryOverlay, shapenum, x, y, window); + } + + /* + ** Draw any repair feedback graphic required. + */ + if (IsRepairing && IsWrenchVisible) { + CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + TechnoClass::Draw_It(x, y, window); + + /* + ** If this is a factory that we're spying on, show what it's producing + */ + if (SpiedBy & (1<<(PlayerPtr->Class->House)) && IsSelected) { + + /* + ** Fetch the factory that is associate with this building. For computer controlled buildings, the + ** factory pointer is integral to the building itself. For human controlled buildings, the factory + ** pointer is part of the house structure and must be retrieved from there. + */ + FactoryClass * factory = NULL; + if (House->IsHuman) { + factory = House->Fetch_Factory(Class->ToBuild); + } else { + factory = Factory; + } + + /* + ** If there is a factory associated with this building, then fetch any attached + ** object under production and display its cameo image over the top of this building. + */ + if (factory != NULL) { + TechnoClass * obj = factory->Get_Object(); + if (obj != NULL) { +#ifdef FIXIT_CSII + CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_NORMAL, NULL); +#else + void const * remapper = obj->House->Remap_Table(false, obj->Techno_Type_Class()->Remap); + CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL | ((remapper != NULL) ? SHAPE_FADING : SHAPE_NORMAL), remapper); +#endif + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * * + * This routine will examine the current state of the building and return with the shape * + * number to use. The shape number is subordinate to the building graphic image data. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when rendering this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Shape_Number(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + int shapenum = Fetch_Stage(); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + if (BState == BSTATE_CONSTRUCTION) { + + /* + ** If the building is deconstructing, then the display frame progresses + ** from the end to the beginning. Reverse the shape number accordingly. + */ + if (Mission == MISSION_DECONSTRUCTION) { + shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; + } + + } else { + + /* + ** If this is a camouflaged pill box and it is not owned by the player, then + ** it is displayed with the MEGA-camouflaged imagery. + */ + if ((!IsOwnedByPlayer) && (*this == STRUCT_CAMOPILLBOX)) { + shapenum += 1; + } + + /* + ** The Tesla Coil has a stage value that can be overridden by + ** its current state. + */ + if (*this == STRUCT_TESLA) { + if (IsCharged) { + shapenum = 3; + } else { + if (IsCharging) { + shapenum = Fetch_Stage(); + } else { + shapenum = 0; + } + } + } + + /* + ** Buildings that contain a turret handle their shape determination + ** differently than normal buildings. They need to take into consideration + ** the direction the turret is facing. + */ + if (Class->IsTurretEquipped) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing.Current())]; + + if (*this == STRUCT_SAM) { + + /* + ** SAM sites that are free to rotate fetch their animation frame + ** from the building's turret facing. All other animation stages + ** fetch their frame from the embedded animation sequencer. + */ +// if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_LOCKING) { +// shapenum = Fetch_Stage(); +// } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 35; + } + } else { + if (IsInRecoilState) { + shapenum += 32; + } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 64; + } + } + } else { + + /* + ** If it is a significantly damaged weapons factory, it is shown in + ** the worst state possible. + */ + if (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP) { + shapenum = 0; + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum = 1; + } + + } else { + + /* + ** Special render stage for silos. The stage is dependent on the current + ** Tiberium collected as it relates to Tiberium capacity. + */ + if (*this == STRUCT_STORAGE) { + + int level = 0; + if (House->Capacity) { + level = (House->Tiberium * 5) / House->Capacity; + } + + shapenum += Bound(level, 0, 4); + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 5; + } + + } else { + + /* + ** If below half strenth, then show the damage frames of the + ** building. + */ + if (Health_Ratio() <= Rule.ConditionYellow) { + int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; + int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; + int largest = max(last1, last2); + last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; + largest = max(largest, last2); + last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; + largest = max(largest, last2); + shapenum += largest; + } + } + } + } + } + return(shapenum); +} + + +/*********************************************************************************************** + * BuildingClass::Mark -- Building interface to map rendering system. * + * * + * This routine is used to mark the map cells so that when it renders * + * the underlying icons will also be updated as necessary. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Building is removed. * + * MARK_CHANGE -- Building changes shape. * + * MARK_DOWN -- Building is added. * + * * + * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * + * when the building is already marked down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1994 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added health bar tracking. * + * 12/23/1994 JLB : Calls low level check before proceeding. * + * 01/27/1995 JLB : Special road spacer template added. * + *=============================================================================================*/ +bool BuildingClass::Mark(MarkType mark) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { + short const * offset = Overlap_List(); + short const * occupy = Occupy_List(); + CELL cell = Coord_Cell(Coord); + SmudgeType bib; + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge != NULL) { + smudge->Disown(cell); + delete smudge; + } + } + break; + + case MARK_DOWN: + + /* + ** Special wall logic is handled here. A building that is really a wall + ** gets converted into an overlay wall type when it is placed down. The + ** actual building object itself is destroyed. + */ + if (Class->IsWall) { + switch (Class->Type) { + case STRUCT_BRICK_WALL: + new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); + break; + + case STRUCT_BARBWIRE_WALL: + new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); + break; + + case STRUCT_SANDBAG_WALL: + new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); + break; + + case STRUCT_WOOD_WALL: + new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); + break; + + case STRUCT_CYCLONE_WALL: + new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); + break; + + case STRUCT_FENCE: + new OverlayClass(OVERLAY_FENCE, cell, House->Class->House); + break; + + default: + break; + } + Transmit_Message(RADIO_OVER_OUT); + delete this; + + } else { + if (Can_Enter_Cell(cell) == MOVE_OK) { + /* + ** Determine if a bib is required for this building. If one is, then + ** create and place it. + */ + CELL newcell = cell; + if (Class->Bib_And_Offset(bib, newcell)) { + new SmudgeClass(bib, Cell_Coord(newcell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } + + Map.Place_Down(cell, this); + } else { + return(false); + } + } + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(false)); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * * + * This function is to handle the AI logic for the building. The graphic logic (facing, * + * firing, and animation) is handled elsewhere. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/26/1994 JLB : Handles production. * + * 06/11/1995 JLB : Revamped. * + *=============================================================================================*/ +void BuildingClass::AI(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Process building animation state changes. Transition to a following state + ** if there is one specified and the current animation sequence has expired. + ** This process must occur before mission AI since the mission AI relies on + ** the bstate change to occur immediately before the MissionClass::AI. + */ + Animation_AI(); + + /* + ** If now is a good time to act on a new mission, then do so. This process occurs + ** here because some outside event may have requested a mission change for the building. + ** Such outside requests (player input) must be initiated BEFORE the normal AI process. + */ + if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** Proceed with normal logic processing. This is where the mission processing + ** occurs. This call must be located after the animation sequence makes the + ** transition to the next frame (see above) in order for the mission logic to + ** act at the exact moment of graphic transition BEFORE it has a chance to + ** be displayed. + */ + TechnoClass::AI(); + + /* + ** Bail if the object died in the AI routine. + */ + if (!IsActive) { + return; + } + + /* + ** Building ammo is instantly reloaded. + */ + if (!Ammo) { + Ammo = Class->MaxAmmo; + } + + /* + ** If now is a good time to act on a new mission, then do so. This occurs here because + ** some AI event may have requested a mission change (usually from another mission + ** state machine). This must occur here before it has a chance to render. + */ + if (IsReadyToCommence) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** If a change of animation was requested, then make the change + ** now. The building animation system acts independently but subordinate + ** to the mission state machine system. By performing the animation change-up + ** here, the mission AI system is ensured of immediate visual affect when it + ** decides to change the animation state of the building. + */ + if (QueueBState != BSTATE_NONE) { + if (BState != QueueBState) { + BState = QueueBState; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + } + QueueBState = BSTATE_NONE; + } + + /* + ** If the building's strength has changed, then update the power + ** accordingly. + */ + if (Strength != LastStrength) { + int oldpower = Power_Output(); + LastStrength = Strength; + int newpower = Power_Output(); + House->Adjust_Power(newpower - oldpower); + } + + /* + ** Check to see if the destruction countdown timer is active. If so, then decrement it. + ** When this timer reaches zero, the building is removed from the map. All the explosions + ** are presumed to be in progress at this time. + */ + if (Strength == 0) { + if (CountDown == 0) { + Limbo(); + Drop_Debris(WhomToRepay); + delete this; + } + return; + } + + /* + ** Charging logic. + */ + Charging_AI(); + + /* + ** Handle any repair process that may be going on. + */ + Repair_AI(); + + /* + ** For computer controlled buildings, determine what should be produced and start + ** production accordingly. + */ + Factory_AI(); + + /* + ** Check for demolition timeout. When timeout has expired, the building explodes. + */ + if (IsGoingToBlow && CountDown == 0) { + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay), true); + if (!IsActive) { + return; + } + Mark(MARK_CHANGE); + } + + /* + ** Turret equiped buildings must handle turret rotation logic here. This entails + ** rotating the turret to the desired facing as well as figuring out what that + ** desired facing should be. + */ + Rotation_AI(); + + /* + ** Gap Generators need to scan if they've just become activated, or if + ** the power has just come on enough so they can scan. Also, they need + ** to un-jam if the power has just dropped off. + */ + if (*this == STRUCT_GAP) { + if (Arm == 0) { + IsJamming = false; + Arm = TICKS_PER_MINUTE * Rule.GapRegenInterval + Random_Pick(1, TICKS_PER_SECOND); + } + + if (!IsJamming) { + if (House->Power_Fraction() >= 1) { + Map.Jam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + IsJamming = true; + } + } else { + if (House->Power_Fraction() < 1) { + IsJamming = false; + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + } + } + } + + /* + ** Radar facilities and SAMs need to check for the proximity of a mobile + ** radar jammer. + */ + if ((*this == STRUCT_RADAR || *this == STRUCT_SAM) && (Frame % TICKS_PER_SECOND) == 0) { + IsJammed = false; + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj != NULL && + !obj->IsInLimbo && + !obj->House->Is_Ally(House) && + obj->Class->IsJammer && + Distance(obj) <= Rule.RadarJamRadius) { + + IsJammed = true; + break; + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * * + * Use this routine to transform a building that has been held in limbo * + * state, into one that really exists on the map. Once a building as * + * been unlimboed, then it becomes a normal object in the game world. * + * * + * INPUT: pos -- The position to place the building on the map. * + * * + * dir (optional) -- not used for this class * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: The unlimbo operation might not be successful if the * + * building could not be placed at the location specified. * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 06/07/1994 JLB : Matches virtual function format for base class. * + * 05/09/1995 JLB : Handles wall placement. * + * 06/18/1995 JLB : Checks for wall legality before placing down. * + *=============================================================================================*/ +bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this is a wall type building, then it never gets unlimboed. Instead, it gets + ** converted to an overlay type. + */ + if (Class->IsWall) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + OverlayType otype = OVERLAY_NONE; + switch (Class->Type) { + case STRUCT_SANDBAG_WALL: + otype = OVERLAY_SANDBAG_WALL; + break; + + case STRUCT_CYCLONE_WALL: + otype = OVERLAY_CYCLONE_WALL; + break; + + case STRUCT_BRICK_WALL: + otype = OVERLAY_BRICK_WALL; + break; + + case STRUCT_BARBWIRE_WALL: + otype = OVERLAY_BARBWIRE_WALL; + break; + + case STRUCT_WOOD_WALL: + otype = OVERLAY_WOOD_WALL; + break; + + case STRUCT_FENCE: + otype = OVERLAY_FENCE; + break; + + default: + otype = OVERLAY_NONE; + break; + + } + if (otype != OVERLAY_NONE) { + ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Map[coord].Owner = House->Class->House; + Transmit_Message(RADIO_OVER_OUT); + Map.Sight_From(Coord_Cell(coord), Class->SightRange, House); + delete this; + return(true); + } + } + } + return(false); + } + + /* + ** Normal building unlimbo process. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->BScan |= (1L << Class->Type); + House->ActiveBScan |= (1L << Class->Type); + + /* + ** Recalculate the center point of the house's base. + */ + House->Recalc_Center(); + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Add(this); + + /* + ** Possibly the sidebar will be affected by this addition. + */ + House->IsRecalcNeeded = true; + LastStrength = 0; + + if ((!IsDiscoveredByPlayer && Map[coord].IsVisible) || Session.Type != GAME_NORMAL) { + Revealed(PlayerPtr); + } + if (!House->IsHuman) { + Revealed(House); + } + + if (IsOwnedByPlayer) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if ((Class->Ownable & (HOUSEF_GOOD | HOUSEF_BAD)) != (HOUSEF_GOOD | HOUSEF_BAD)) { + if (Class->Ownable & HOUSEF_GOOD) { + ActLike = HOUSE_GREECE; + } else { + ActLike = HOUSE_USSR; + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * * + * This routine will inflict damage points upon the specified building. * + * It will handle the damage animation and building destruction. Use * + * this routine whenever a building is attacked. * + * * + * INPUT: damage -- Amount of damage to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The kind of damage to inflict. * + * * + * source -- The source of the damage. This is used to change targeting. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: true/false; Was the building destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1991 : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added warhead modifier to damage. * + * 06/03/1994 JLB : Added source of damage as target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 07/15/1995 JLB : Power ratio gets adjusted. * + *=============================================================================================*/ +ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + int shakes; + + if (this != source /*&& !Class->IsInsignificant*/) { + + if (source) { + House->LATime = Frame; + House->LAType = source->What_Am_I(); + House->LAZone = House->Which_Zone(this); + House->LAEnemy = source->Owner(); + + if (!House->Is_Ally(source)) { + House->Enemy = source->Owner(); + } + + Base_Is_Attacked(source); + } + + short const * offset = Occupy_List(); + + /* + ** Memorize who they used to be in radio contact with. + */ + TechnoClass *tech = Contact_With_Whom(); + /* + ** Perform the low level damage assessment. + */ + res = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + switch (res) { + case RESULT_DESTROYED: + + /* + ** Add the building to the base prebuild list if allowed. This will force + ** the computer to rebuild this structure if it can. + */ + if (IsToRebuild && Class->Level != -1 && Base.House == House->Class->House && Base.Get_Node(this) == 0) { +// if (IsToRebuild && Class->IsBuildable && Base.House == House->Class->House && Base.Get_Node(this) == 0) { + Base.Nodes.Add(BaseNodeClass(Class->Type, Coord_Cell(Coord))); + } + + /* + ** Destroy all attached objects. + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + /* + ** If we were in contact with a landed plane, blow the plane up too. + */ + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + int damage = 500; + tech->Take_Damage(damage, 0, WARHEAD_AP, source, forced); + } + + Sound_Effect(VOC_KABOOM22, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + + /* + ** If the building is destroyed, then lots of + ** explosions occur. + */ + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 3)); + } + + shakes = Class->Cost_Of() / 400; + if (shakes) { + Shake_The_Screen(shakes); + } + Sound_Effect(VOC_CRUMBLE, Coord); + if (Mission == MISSION_DECONSTRUCTION) { + CountDown = 0; + Set_Rate(0); + } else { + CountDown = 8; + } + + /* + ** If it is in radio contact and the object seems to be attached, then tell + ** it to run away. + */ + if (In_Radio_Contact() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + Transmit_Message(RADIO_RUN_AWAY); + } + + /* + ** A force destruction will not generate survivors. + */ + if (forced || *this == STRUCT_KENNEL) { + IsSurvivorless = true; + } + + /* + ** Destruction of a radar facility or advanced communications + ** center will cause the spiedby field to change... + */ + if (SpiedBy) { + SpiedBy = 0; + StructType struc = *this; + if (struc == STRUCT_RADAR /* || struc == STRUCT_EYE */) { + Update_Radar_Spied(); + } + } + + /* + ** Destruction of a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** Destruction of a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + /* + ** Destruction of a barrel will cause the surrounding squares to + ** be hit with damage. + */ + if (*this == STRUCT_BARREL || *this == STRUCT_BARREL3) { + COORDINATE center = Center_Coord(); + CELL cellcenter = Coord_Cell(center); + + BulletClass * bullet; + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_N)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_N); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_E)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_E); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_S)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_S); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_W)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_W); + } + } + break; + + case RESULT_HALF: + if (*this == STRUCT_PUMP) { + AnimClass * anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); + if (anim) { + anim->Attach_To(this); + } + } + // Fall into next case. + + case RESULT_MAJOR: + Sound_Effect(VOC_KABOOM1, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + AnimClass * anim = NULL; + + /* + ** Show pieces of fire to indicate that a significant change in + ** damage level has occurred. + */ + if (warhead == WARHEAD_FIRE) { + switch (Random_Pick(0, 5+Class->Width()+Class->Height())) { + case 0: + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 6: + case 7: + case 8: + anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 9: + anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); + break; + + default: + break; + } + } else { + if (Percent_Chance(50)) { + /* + ** Building may catch on fire, but only if it wasn't a + ** renovator that caused the damage. + */ + if (source == NULL || source->What_Am_I() != RTTI_INFANTRY || *(InfantryClass *)source != INFANTRY_RENOVATOR) { + anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + } + /* + ** If the animation was created, then attach it to the building. + */ + if (anim) { + anim->Attach_To(this); + } + } + break; + + case RESULT_NONE: + break; + + case RESULT_LIGHT: + break; + } + + if (source && res != RESULT_NONE) { + + /* + ** If any damage occurred, then inform the house of this fact. If it is the player's + ** house, it might announce this fact. + */ + if (!Class->IsInsignificant) { + House->Attacked(); + } + + /* + ** Save the type of the house that's doing the damage, so if the building burns + ** to death credit can still be given for the kill + */ + WhoLastHurtMe = source->Owner(); + + /* + ** When certain buildings are hit, they "snap out of it" and + ** return fire if they are able and allowed. + */ + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && + !House->Is_Ally(source) && + Class->PrimaryWeapon != NULL && + (!Target_Legal(TarCom) || !In_Range(TarCom))) { + + if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Rule.IsSmartDefense)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Generate a random rotation effect since there is nothing else that this + ** building can do. + */ + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + } + } + } + } + } + + return(res); +} + + +/*********************************************************************************************** + * BuildingClass::new -- Allocates a building object from building pool. * + * * + * This routine will allocate a building slot from the building alloc * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated building. If NULL is * + * returned, then this indicates a failure to allocate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + * 05/17/1994 JLB : Revamped allocation scheme * + * 07/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void * BuildingClass::operator new(size_t ) +{ + void * ptr = Buildings.Allocate(); + if (ptr) { + ((BuildingClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * BuildingClass::delete -- Deallocates building object. * + * * + * This is the memory deallocation operation for a building object. * + * Since buildings are allocated out of a fixed memory block, all that * + * is needed is to flag the unit as inactive. * + * * + * INPUT: ptr -- Pointer to building to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::operator delete(void *ptr) +{ + if (ptr) { + ((BuildingClass *)ptr)->IsActive = false; + } + Buildings.Free((BuildingClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingClass::BuildingClass -- Constructor for buildings. * + * * + * This routine inserts a building into the object tracking system. * + * It is placed into a limbo state unless a location is provided for * + * it to unlimbo at. * + * * + * INPUT: type -- The structure type to make this object. * + * * + * house -- The owner of this building. * + * * + * pos -- The position to unlimbo the building. If -1 is * + * specified, then the building remains in a limbo * + * state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + * 08/07/1995 JLB : Fixed act like value to match expected value. * + *=============================================================================================*/ +BuildingClass::BuildingClass(StructType type, HousesType house) : + TechnoClass(RTTI_BUILDING, Buildings.ID(this), house), + Class(BuildingTypes.Ptr((int)type)), + Factory(0), + ActLike(House->ActLike), + IsToRebuild(false), + IsToRepair(false), + IsAllowedToSell(true), + IsReadyToCommence(false), + IsRepairing(false), + IsWrenchVisible(false), + IsGoingToBlow(false), + IsSurvivorless(false), + IsCharging(false), + IsCharged(false), + IsCaptured(false), + IsJamming(false), + IsJammed(false), + HasFired(false), + HasOpened(false), + CountDown(0), + BState(BSTATE_NONE), + QueueBState(BSTATE_NONE), + WhoLastHurtMe(house), + WhomToRepay(TARGET_NONE), + AnimToTrack(TARGET_NONE), + LastStrength(0), + PlacementDelay(0) +{ + House->Tracking_Add(this); + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + Ammo = Class->MaxAmmo; + + /* + ** If the building could never be built, then it can never be sold either. This + ** is due to the lack of buildup animation. + */ + if (Class->Get_Buildup_Data() != NULL) { +// if (!Class->IsBuildable) { + IsAllowedToSell = false; + } + +// if (Session.Type == GAME_INTERNET) { +// House->BuildingTotals->Increment_Unit_Total( (int) type); +// } + +} + + +/*********************************************************************************************** + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * * + * This destructor for building objects will put the building in limbo if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass::~BuildingClass(void) +{ + if (GameActive && Class) { + if (House) { + House->Tracking_Remove(this); + } + BuildingClass::Limbo(); + } + Class = 0; + + delete (FactoryClass *)Factory; + Factory = 0; + ID = -1; +} + + +/*********************************************************************************************** + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * * + * This routine is called when a building is destroyed. It handles * + * placing the rubble down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * + * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * + *=============================================================================================*/ +void BuildingClass::Drop_Debris(TARGET source) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * offset; + CELL cell; + + /* + ** Generate random survivors from the destroyed building. + */ + cell = Coord_Cell(Coord); + offset = Occupy_List(); + int odds = 2; + if (Target_Legal(WhomToRepay)) odds -= 1; + if (IsCaptured) odds += 6; + int count = How_Many_Survivors(); + while (*offset != REFRESH_EOL) { + CELL newcell; + + newcell = cell + *offset++; + CellClass const * cellptr = &Map[newcell]; + + /* + ** Infantry could run out of a destroyed building. + */ + if (!House->IsToDie && count > 0) { + InfantryClass * i = NULL; + + if (Random_Pick(0, odds) == 1) { + i = NULL; + InfantryType typ = Crew_Type(); + if (typ != INFANTRY_NONE) i = new InfantryClass(typ, House->Class->House); + if (i != NULL) { + if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; + ScenarioInit++; + if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { + count--; + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + i->Scatter(0, true); + if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { + i->Assign_Mission(MISSION_ATTACK); + i->Assign_Target(source); + } else { + if (House->IsHuman) { + i->Assign_Mission(MISSION_GUARD); + } else { + i->Assign_Mission(MISSION_HUNT); + } + } + } else { + delete i; + } + ScenarioInit--; + } + } + } + + /* + ** Smoke and fire only appear on terrestrail cells. They should not appear on + ** rivers, clifs, or water cells. + */ + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + + /* + ** Possibly add some smoke rising from the ashes of the building. + */ + switch (Random_Pick(0, 5)) { + case 0: + case 1: + case 2: + new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); + break; + + default: + break; + } + + /* + ** The building always scars the ground in some fashion. + */ + if (Percent_Chance(25)) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); + } else { + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* + * * + * This interface routine handles when the player clicks on the map while this building * + * is currently selected. This is used to assign an override target to a turret or * + * guard tower. * + * * + * INPUT: target -- The target that was clicked upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK && object != NULL) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + + if (action == ACTION_SELF && Class->Is_Factory()) { + OutList.Add(EventClass(EventClass::PRIMARY, TargetClass(this))); + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * * + * This routine really only serves one purpose -- to allow targeting of the ground for * + * buildings that are equipped with weapons. * + * * + * INPUT: action -- The requested action to perform. * + * * + * cell -- The cell location to perform the action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 10/04/1995 JLB : Handles construction yard undeploy to move logic. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + } + + if (action == ACTION_MOVE && *this == STRUCT_CONST) { + OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(cell))); + OutList.Add(EventClass(EventClass::SELL, TargetClass(this))); + } +} + + +/*********************************************************************************************** + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * * + * Assigning of a target to a building makes sense if the building is one that can attack. * + * This routine would be used to assign the attack target to a turret or guard tower. * + * * + * INPUT: target -- The target that was clicked on while this building was selected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 11/02/1994 JLB : Checks for range before assigning target. * + *=============================================================================================*/ +void BuildingClass::Assign_Target(TARGET target) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && !In_Range(target, 0)) { + target = TARGET_NONE; + } + + TechnoClass::Assign_Target(target); +} + + +/*********************************************************************************************** + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * * + * This routine initializes the building system in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Init(void) +{ + Buildings.Free_All(); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * * + * This function is used to cause an object to exit the building. It is called when a * + * factory produces a vehicle or other mobile object and that object needs to exit the * + * building to join the ranks of a regular unit. Typically, the object is placed down on * + * the map such that it overlaps the building and then it is given a movement order so that * + * it will move to an adjacent free cell. * + * * + * INPUT: base -- Pointer to the object that is to exit the building. * + * * + * OUTPUT: Returns the success rating for the exit attempt; * + * 0 = complete failure (refund money please) * + * 1 = temporarily prevented (try again later please) * + * 2 = successful * + * * + * WARNINGS: The building is placed in radio contact with the object. The object is in a * + * tethered condition. This condition will be automatically broken when the * + * object reaches the adjacent square. * + * * + * HISTORY: * + * 11/28/1994 JLB : Created. * + * 04/10/1995 JLB : Handles building production by computer. * + * 06/17/1995 JLB : Handles refinery exit. * + *=============================================================================================*/ +int BuildingClass::Exit_Object(TechnoClass * base) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!base) return(0); + + TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); + + /* + ** A unit exiting a building is always considered to be "locked". That means, it + ** will be considered as to have legally entered the visible map domain. + */ + base->IsLocked = true; + + /* + ** Find a good cell to unload the object to. The object, probably a vehicle + ** will drive/walk to the adjacent free cell. + */ + CELL cell = 0; + + switch (base->What_Am_I()) { + + case RTTI_AIRCRAFT: + if (!In_Radio_Contact()) { + AircraftClass * air = (AircraftClass *)base; + + air->Height = 0; + ScenarioInit++; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + Transmit_Message(RADIO_HELLO, air); + Transmit_Message(RADIO_TETHER); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } else { + AircraftClass * air = (AircraftClass *)base; + + if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { + cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } else { + cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } + ScenarioInit++; + if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { +//BG air->Assign_Destination(::As_Target(Nearby_Location(air))); +/*BG*/ air->Assign_Destination(::As_Target(air->Nearby_Location(this))); + air->Assign_Mission(MISSION_MOVE); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + case RTTI_VESSEL: + switch (Class->Type) { + case STRUCT_SUB_PEN: + case STRUCT_SHIP_YARD: + ScenarioInit++; + cell = Find_Exit_Cell(base); + if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) { + base->Assign_Mission(MISSION_GUARD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + default: + break; + } + break; + + case RTTI_INFANTRY: + case RTTI_UNIT: + switch (Class->Type) { + case STRUCT_REFINERY: + if (base->What_Am_I() == RTTI_UNIT) { + cell = Coord_Cell(Center_Coord()); + UnitClass * unit = (UnitClass *)base; + + cell = Adjacent_Cell(cell, FACING_SW); + ScenarioInit++; + if (unit->Unlimbo(Cell_Coord(Adjacent_Cell(cell, DIR_S)), DIR_SW_X2)) { + unit->PrimaryFacing = DIR_S; + unit->Assign_Mission(MISSION_HARVEST); + } + ScenarioInit--; + } else { + base->Scatter(0, true); + } + break; + + case STRUCT_WEAP: + if (Mission == MISSION_UNLOAD) { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg->Owner() == Owner() && *bldg == STRUCT_WEAP && bldg != this && bldg->Mission == MISSION_GUARD && !bldg->Factory) { + FactoryClass * temp = Factory; + bldg->Factory = Factory; + Factory = 0; + int retval = (bldg->Exit_Object(base)); + bldg->Factory = 0; + Factory = temp; + return(retval); + } + } + return(1); // fail while we're still unloading previous + } + ScenarioInit++; + if (base->Unlimbo(Exit_Coord(), DIR_S)) { + base->Mark(MARK_UP); + base->Coord = Exit_Coord(); + base->Mark(MARK_DOWN); + Transmit_Message(RADIO_HELLO, base); + Transmit_Message(RADIO_TETHER); + Assign_Mission(MISSION_UNLOAD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + case STRUCT_BARRACKS: + case STRUCT_TENT: + case STRUCT_KENNEL: + + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + + /* + ** Establish radio contact so unload coordination can occur. This + ** radio contact should always succeed. + */ + if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + default: + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + } + break; + + case RTTI_BUILDING: + + if (!House->IsHuman) { + + /* + ** Find the next available spot to place this newly created building. If the + ** building could be placed at the desired location, fine. If not, then this + ** routine will return failure. The calling routine will probably abandon this + ** building in preference to building another. + */ + BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); + COORDINATE coord = 0; + if (node) { + coord = Cell_Coord(node->Cell); + } else { + + /* + ** Find a suitable new spot to place. + */ + coord = House->Find_Build_Location((BuildingClass *)base); + } + + if (coord) { + if (Flush_For_Placement(base, Coord_Cell(coord))) { + return(1); + } + if (base->Unlimbo(coord)) { + if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) { + House->BuildStructure = STRUCT_NONE; + } + return(2); + } + } + } + break; + + default: + break; + } + + /* + ** Failure to exit the object results in a false return value. + */ + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { + switch (Class->ToBuild) { + StructType i; + UnitType u; + InfantryType f; + AircraftType a; + VesselType v; + + case RTTI_VESSELTYPE: + for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) { + if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference(v), ActLike)) { + Map.Add(RTTI_VESSELTYPE, v); + } + } + break; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference(i), ActLike)) { + Map.Add(RTTI_BUILDINGTYPE, i); + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference(u), ActLike)) { + Map.Add(RTTI_UNITTYPE, u); + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference(f), ActLike)) { + if (InfantryTypeClass::As_Reference(f).IsDog) { + if (*this == STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } else { + if (*this != STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference(a), ActLike)) { + Map.Add(RTTI_AIRCRAFTTYPE, a); + } + } + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * * + * This routine is used to perform any fixups necessary when the attached animation has * + * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * + * At that point, normal reload procedures can commence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Fire_Out(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); +} + + +/*********************************************************************************************** + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * * + * This routine will handle the power adjustments for the associated house when the * + * building goes into limbo. This means that its power drain or production is subtracted * + * from the house accumulated totals. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the building limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Limbo(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Remove(this); + House->IsRecalcNeeded = true; + House->Recalc_Center(); + + /* + ** Update the power status of the owner's house. + */ + House->Adjust_Power(-Power_Output()); + House->Adjust_Drain(-Class->Drain); + House->Adjust_Capacity(-Class->Capacity, true); + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + /* + ** This could be a building that builds. If so, then the sidebar may need adjustment. + ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building + ** isn't available. Set it back to false so the rest of the Limbo code works. + ** Otherwise, the sidebar won't properly remove non-available buildables. + */ +// if (IsOwnedByPlayer && !ScenarioInit) { +// IsInLimbo = true; +// Map.Recalc(); +// IsInLimbo = false; +// } + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * * + * This will return the turret facing for this building. Some buildings don't have a * + * visual turret (e.g., pillbox) so they return a turret facing that always faces their * + * current target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current facing of the turret. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Turret_Facing(void) const +{ + if (!Class->IsTurretEquipped && Target_Legal(TarCom)) { + return(::Direction(Center_Coord(), As_Coord(TarCom))); + } + return(PrimaryFacing.Current()); +} + + +/*********************************************************************************************** + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * * + * This routine intercepts the Greatest_Threat function so that it can add the ability * + * to search for ground targets, if this isn't a SAM site. * + * * + * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * + * or THREAT_NORMAL. * + * * + * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * + * returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::Greatest_Threat(ThreatType threat) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + threat = threat | THREAT_RANGE; + +// if (Class->PrimaryWeapon != NULL) { +// if (Class->PrimaryWeapon->Bullet->IsAntiAircraft) { +// threat = threat | THREAT_AIR; +// } +// if (Class->PrimaryWeapon->Bullet->IsAntiGround) { +// threat = threat | THREAT_BUILDINGS|THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES; +// } +// threat = threat | THREAT_RANGE; +// } + return(TechnoClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * * + * This routine is called when construction has finished. Typically, this enables * + * new production options for factories. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + * 06/13/1995 JLB : Added helipad. * + *=============================================================================================*/ +void BuildingClass::Grand_Opening(bool captured) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!HasOpened || captured) { + HasOpened = true; + + /* + ** Adjust the owning house according to the power, drain, and Tiberium capacity that + ** this building has. + */ + House->Adjust_Drain(Class->Drain); + House->Adjust_Capacity(Class->Capacity); + House->IsRecalcNeeded = true; + + /* SPECIAL CASE: + ** Tiberium Refineries get a free harvester. Add a harvester to the + ** reinforcement list at this time. + */ + if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S)); + + UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); + if (unit != NULL) { + + /* + ** Try to place down the harvesters. If it could not be placed, then try + ** to place it in a nearby location. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_W)) { + cell = unit->Nearby_Location(this); + + /* + ** If the harvester could still not be placed, then refund the money + ** to the owner and then bail. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + House->Refund_Money(unit->Class->Cost_Of()); + delete unit; + } + } + } else { + + /* + ** If the harvester could not be created in the first place, then give + ** the full refund price to the owning player. + */ + House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); + } + } + + /* + ** Helicopter pads get a free attack helicopter. + */ + if (!Rule.IsSeparate && *this == STRUCT_HELIPAD && !captured) { + ScenarioInit++; + AircraftClass * air = 0; + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_BAD || House->ActLike == HOUSE_UKRAINE) { + air = new AircraftClass(AIRCRAFT_HIND, House->Class->House); + } else { + air = new AircraftClass(AIRCRAFT_LONGBOW, House->Class->House); + } + if (air) { + air->Height = 0; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + air->Assign_Mission(MISSION_GUARD); + air->Transmit_Message(RADIO_HELLO, this); + Transmit_Message(RADIO_TETHER); + } + } + ScenarioInit--; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * * + * This routine will start, stop, or toggle the repair process. When a building repairs, it * + * occurs incrementally over time. * + * * + * INPUT: control -- Determines how to control the repair process. * + * 0: Turns repair process off (if it was on). * + * 1: Turns repair process on (if it was off). * + * -1:Toggles repair process to other state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (control) { + case -1: + IsRepairing = (IsRepairing == false); + break; + + case 1: + if (IsRepairing) return; + IsRepairing = true; + break; + + case 0: + if (!IsRepairing) return; + IsRepairing = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + VocType soundid = VOC_NONE; + if (IsRepairing) { + if (Strength == Class->MaxStrength) { + soundid = VOC_SCOLD; + } else { + soundid = VOC_CLICK; + if (House->IsPlayerControl) { + Clicked_As_Target(); + } + IsWrenchVisible = true; + } + } else { + soundid = VOC_CLICK; + } + if (House->IsPlayerControl) { + Sound_Effect(soundid, Coord); + } +} + + +/*********************************************************************************************** + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * * + * This routine will initiate or stop the sell back process for a building. It is called * + * when the player clicks on a building when the sell mode is active. * + * * + * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * + * -1 = toggle deconstruction state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Sell_Back(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Get_Buildup_Data()) { + bool decon = false; + switch (control) { + case -1: + decon = (Mission != MISSION_DECONSTRUCTION); + break; + + case 1: + if (Mission == MISSION_DECONSTRUCTION) return; + if (IsGoingToBlow) return; + decon = true; + break; + + case 0: + if (Mission != MISSION_DECONSTRUCTION) return; + decon = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + if (decon) { + Assign_Mission(MISSION_DECONSTRUCTION); + Commence(); + if (House->IsPlayerControl) { + Clicked_As_Target(); + } + } + if (House->IsPlayerControl) { + Sound_Effect(VOC_CLICK); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * * + * This routine will determine what action to perform if the mouse was clicked on the * + * object specified. This determination is used to control the mouse imagery and the * + * function process when the mouse button is pressed. * + * * + * INPUT: object -- Pointer to the object that, if clicked on, will control what action * + * is to be performed. * + * * + * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * + * object specified while the building is currently selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(ObjectClass const * object) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(object); + + if (action == ACTION_SELF) { + int index; + if (Class->Is_Factory() && PlayerPtr == House && *House->Factory_Counter(Class->ToBuild) > 1) { + switch (Class->ToBuild) { + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + action = ACTION_NONE; + if (*this == STRUCT_KENNEL) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_KENNEL) { + action = ACTION_SELF; + break; + } + } + } else { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_INFANTRYTYPE && *bldg != STRUCT_KENNEL) { + action = ACTION_SELF; + break; + } + } + } + break; + + case RTTI_NONE: + action = ACTION_NONE; + break; + + default: + break; + } + + } else { + action = ACTION_NONE; + } + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. Also don't allow targeting if the object is too + ** far away. + */ + if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN || !In_Range(object, 0))) { + action = ACTION_NONE; + } + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines what action will occur. * + * * + * This routine examines the cell specified and returns with the action that will be * + * performed if that cell were clicked upon while the building is selected. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * + * on this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(CELL cell) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(cell); + + if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Rule.IsMCVDeploy)) { + action = ACTION_NONE; + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && Class->PrimaryWeapon != NULL && !Class->PrimaryWeapon->Bullet->IsAntiGround) { +// if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN)) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * * + * This routine will start the building animating. This animation will loop indefinitely * + * until explicitly stopped. * + * * + * INPUT: bstate -- The animation state to initiate. * + * * + * OUTPUT: none * + * * + * WARNINGS: The building graphic state will reflect the first stage of this animation the * + * very next time it is rendered. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/02/1995 JLB : Uses normalize animation rate where applicable. * + *=============================================================================================*/ +void BuildingClass::Begin_Mode(BStateType bstate) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + QueueBState = bstate; + if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { + BState = bstate; + QueueBState = BSTATE_NONE; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + int rate = ctrl->Rate; + if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { + rate = Options.Normalize_Delay(rate); + } + Set_Rate(rate); + Set_Stage(ctrl->Start); + } +} + + +/*********************************************************************************************** + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * * + * This routine is used to set the center coordinate for this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate for the center location for the building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Center_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, CenterOffset[Class->Size])); +} + + +/*********************************************************************************************** + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * * + * This routine will return the coordinate to use when an object wishes to dock with this * + * building. Normally the docking coordinate would be the center of the building. * + * Exceptions to this would be the airfield and helipad. Their docking coordinates are * + * offset to match the building artwork. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to head to when trying to dock with this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Docking_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_HELIPAD) { + return(Coord_Add(Coord, XYP_COORD(24, 18))); + } + if (*this == STRUCT_AIRSTRIP) { + return(Coord_Add(Coord, XYP_COORD(ICON_PIXEL_W + ICON_PIXEL_W/2, 28))); + } + return(TechnoClass::Docking_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * * + * Use this routine to see if the building can fire its weapon. * + * * + * * + * INPUT: target -- The target that firing upon is desired. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * + * returned. Other cases will result in appropriate fire code value that indicates * + * why firing is not allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + FireErrorType canfire = TechnoClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsTurretEquipped) { + int diff = PrimaryFacing.Difference(Direction(TarCom)); + diff = abs(diff); + if (ABS(diff) > (*this == STRUCT_SAM ? 64 : 8)) { +// if (ABS(diff) > 8) { + return(FIRE_FACING); + } + + /* + ** If the turret is rotating then firing must be delayed. + */ +// if (PrimaryFacing.Is_Rotating()) { +// return(FIRE_ROTATING); +// } + } + + /* + ** Certain buildings cannot fire if there is insufficient power. + */ + if (Class->IsPowered && House->Power_Fraction() < 1) { + return(FIRE_BUSY); + } + + /* + ** If an obelisk can fire, check the state of charge. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && !IsCharged) { + return(FIRE_BUSY); + } + } + return(canfire); +} + + +/*********************************************************************************************** + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * * + * This routine will change the primary factory state of this building. The primary * + * factory is the one that units will be produced from (by default). * + * * + * INPUT: none * + * * + * OUTPUT: Is this building NOW the primary factory? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Toggle_Primary(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (IsLeader) { + IsLeader = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { + if (Class->ToBuild == RTTI_INFANTRYTYPE) { + if (*building == STRUCT_KENNEL && *this == STRUCT_KENNEL) { + building->IsLeader = false; + } else { + if (*building != STRUCT_KENNEL && *this != STRUCT_KENNEL) { + building->IsLeader = false; + } + } + } else { + building->IsLeader = false; + } + } + } + IsLeader = true; + if ((HouseClass *)House == PlayerPtr) { + Speak(VOX_PRIMARY_SELECTED); + } + } + Mark(MARK_CHANGE); + return(IsLeader); +} + + +/*********************************************************************************************** + * BuildingClass::Captured -- Captures the building. * + * * + * This routine will change the owner of the building. It handles updating any related * + * game systems as a result. Factories are the most prone to have great game related * + * consequences when captured. This could also affect the sidebar and building ownership. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the capture attempt successful? * + * * + * WARNINGS: Capturing could fail if the house is already owned by the one specified or * + * the building isn't allowed to be captured. * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * + *=============================================================================================*/ +bool BuildingClass::Captured(HouseClass * newowner) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsCaptureable && newowner != House) { +#ifdef TOFIX + switch (Owner()) { + case HOUSE_GOOD: + Speak(VOX_GDI_CAPTURED); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_CAPTURED); + break; + } +#endif + + /* + ** Make sure the capturer isn't spying on his own building, and if + ** it was a radar facility, update the target house's RadarSpied field. + */ + if (SpiedBy & (1<<(newowner->Class->House)) ) { + SpiedBy -= (1<<(newowner->Class->House)); + if (*this == STRUCT_RADAR) { + Update_Radar_Spied(); + } + } + + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + IsJamming = false; + Arm = 0; + } + + /* + ** Add this building to the list of buildings captured this game. For internet stats purposes. + */ + if (Session.Type == GAME_INTERNET) { + newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); + } + + House->Adjust_Power(-Power_Output()); + LastStrength = 0; + House->Adjust_Drain(-Class->Drain); + int booty = House->Adjust_Capacity(-Class->Capacity, true); + + /* + ** If there is something loaded, then it gets captured as well. + */ + TechnoClass * tech = Attached_Object(); + if (tech) tech->Captured(newowner); + + /* + ** If something isn't technically attached, but is sitting on this + ** building for another reason (e.g., helicopter on helipad), then it + ** gets captured as well. + */ + tech = Contact_With_Whom(); + if (tech) { + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && (::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040 || + (tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND)) ) { + tech->Captured(newowner); + } else { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } + } + + /* + ** Abort any computer production in progress. + */ + if (Factory) { + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** Decrement the factory counter for the original owner. + */ + House->Active_Remove(this); + + /* + ** Flag that both owners now need to update their buildable lists. + */ + House->IsRecalcNeeded = true; + newowner->IsRecalcNeeded = true; + HouseClass * oldowner = House; + TARGET tocap = As_Target(); + + IsCaptured = true; + TechnoClass::Captured(newowner); + + oldowner->ToCapture = tocap; + oldowner->Recalc_Center(); + House->Recalc_Center(); + if (House->ToCapture == As_Target()) { + House->ToCapture = TARGET_NONE; + } + + SmudgeType bib; + CELL cell = Coord_Cell(Coord); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } +#ifdef FIXIT_CAPTURE_BIB + if (Session.Type == GAME_NORMAL) { + new SmudgeClass(bib, Cell_Coord(cell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } else { + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); + } +#else + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); +#endif + } + + House->Harvested(booty); + House->Stole(Refund_Amount()); + + /* + ** Increment the factory count for the new owner. + */ + House->Active_Add(this); + + IsRepairing = false; + Grand_Opening(true); + + Mark(MARK_CHANGE); + + /* + ** Perform a look operation when captured if it was the player + ** that performed the capture. + */ + if (House == PlayerPtr) { + Look(false); + } + /* + ** If it was spied upon by the player who just captured it, clear the + ** spiedby flag for that house. + */ + if (SpiedBy & (1 << (newowner->Class->House))) { + SpiedBy &= ~(1 << (newowner->Class->House)); + } + + /* + ** Update the new building's colors on the radar map. + */ + short const * offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + Map.Radar_Pixel(cell); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * * + * The coordinate value returned from this function should be used for sorting purposes. * + * It has special offset adjustment applied so that vehicles don't overlap (as much). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * + *=============================================================================================*/ +COORDINATE BuildingClass::Sort_Y(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_REPAIR) { + return(Coord); + } + if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { + return(Center_Coord()); + } + if (*this == STRUCT_REFINERY) { + return(Center_Coord()); + } + + /* + ** Mines need to bias their sort location such that they are typically drawn + ** before any objects that might overlap them. + */ + if (*this == STRUCT_AVMINE || *this == STRUCT_APMINE) { + return(Coord_Move(Center_Coord(), DIR_N, CELL_LEPTON_H)); + } + + return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * * + * This routine will determine if the building can be placed down at the location * + * specified. * + * * + * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * + * of the building if it were to be placed down. * + * * + * OUTPUT: Returns with the move legality value for placement at the location specified. This * + * will either be MOVE_OK or MOVE_NO. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST && IsDown) { + return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); + } + + if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) { + return(MOVE_NO); + } + + return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * * + * Determines if the player can sell this building. Selling is possible if the building * + * is not currently in construction or deconstruction animation. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building be demolished at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * + * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * + *=============================================================================================*/ +bool BuildingClass::Can_Demolish(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsUnsellable) return(false); + + if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * * + * Buildings that can attack are given this mission. They will wait until a suitable target * + * comes within range and then launch into the attack mission. Buildings that have no * + * weaponry will just sit in this routine forever. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine will be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Guard(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this building has a weapon, then search for a target to attack. When + ** a target is found, switch into attack mode to deal with the threat. + */ + if (Is_Weapon_Equipped()) { + + /* + ** Weapon equipped buildings are ALWAYS ready to launch into another mission if + ** they are sitting around in guard mode. + */ + IsReadyToCommence = true; + + /* + ** If there is no target available, then search for one. + */ + if (!Target_Legal(TarCom)) { + ThreatType threat = THREAT_NORMAL; + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** There is a valid target. Switch into attack mode right away. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + Commence(); + return(1); + } + } else { + + /* + ** This is the very simple state machine that basically does + ** nothing. This is the mode that non weapon equipped buildings + ** are normally in. + */ + enum { + INITIAL_ENTRY, + IDLE + }; + switch (Status) { + case INITIAL_ENTRY: + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + case IDLE: + /* + ** Special case to break out of guard mode if this is a repair + ** facility and there is a customer waiting at the grease pit. + */ + if (*this == STRUCT_REPAIR && + In_Radio_Contact() && + Contact_With_Whom()->Is_Techno() && + ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && + Distance(Contact_With_Whom()) < 0x0040 && + Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + + Assign_Mission(MISSION_REPAIR); + return(1); + } + break; + + default: + break; + } + + if (*this == STRUCT_REPAIR) { + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } else { + return(MissionControl[Mission].Normal_Delay() * 3 + Random_Pick(0, 2)); + } + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Construction -- Handles mission construction. * + * * + * This routine will handle mission construction. When this mission is complete, the * + * building will begin normal operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Construction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_CONSTRUCTION); + Transmit_Message(RADIO_BUILDING); + if (House->IsPlayerControl) { + Sound_Effect(VOC_CONSTRUCTION, Coord); + } + Status = DURING; + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** When construction is complete, then transmit this + ** to the construction yard so that it can stop its + ** construction animation. + */ + Transmit_Message(RADIO_COMPLETE); // "I'm finished." + Transmit_Message(RADIO_OVER_OUT); // "You're free." + Begin_Mode(BSTATE_IDLE); + Grand_Opening(); + Assign_Mission(MISSION_GUARD); + PrimaryFacing = Class->StartFace; + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * * + * This state machine is only used when the building is deconstructing as a result of * + * selling. When this mission is finished, the building will no longer exist. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 08/13/1995 JLB : Enable selling of units on a repair bay. * + * 08/20/1995 JLB : Scatters infantry from scattered starting points. * + *=============================================================================================*/ +int BuildingClass::Mission_Deconstruction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Always force repair off. + */ + Repair(0); + + enum { + INITIAL, + HOLDING, + DURING + }; + switch (Status) { + case INITIAL: + + /* + ** Special check for the repair bay which has the ability to sell + ** whatever is on it. If there is something on the repair bay, then + ** it will be sold. If there is nothing on the repair bay, then + ** the repair bay itself will be sold. + */ + if ( (*this == STRUCT_REPAIR || *this == STRUCT_AIRSTRIP) && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0080) { + TechnoClass * tech = Contact_With_Whom(); + Transmit_Message(RADIO_OVER_OUT); + if (IsOwnedByPlayer) Speak(VOX_UNIT_SOLD); + tech->Sell_Back(1); + Assign_Mission(MISSION_GUARD); + return(1); + } + + /* + ** Selling off a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + IsReadyToCommence = false; + Transmit_Message(RADIO_RUN_AWAY); + Status = HOLDING; + break; + + case HOLDING: + if (!IsTethered) { + + /* + ** The crew will evacuate from the building. The number of crew + ** members leaving is equal to the unrecovered cost of the building + ** divided by 100 (the typical cost of a minigunner infantryman). + */ + if (!Target_Legal(ArchiveTarget) || !Rule.IsMCVDeploy || *this != STRUCT_CONST) { + int count = How_Many_Survivors(); + bool engine = false; + + while (count) { + + /* + ** Ensure that the player only gets ONE engineer and not from a captured + ** construction yard. + */ + InfantryType typ = Crew_Type(); + while (typ == INFANTRY_RENOVATOR && engine) { + typ = Crew_Type(); + } + if (typ == INFANTRY_RENOVATOR) engine = true; + + InfantryClass * infantry = 0; + if (typ != INFANTRY_NONE) infantry = new InfantryClass(typ, House->Class->House); + if (infantry != NULL) { + ScenarioInit++; + COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); + coord = Map[coord].Closest_Free_Spot(coord, false); + + if (infantry->Unlimbo(coord, DIR_N)) { + if (infantry->Class->IsNominal) infantry->IsTechnician = true; + ScenarioInit--; + infantry->Scatter(0, true); + ScenarioInit++; + infantry->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete infantry; + } + ScenarioInit--; + } + count--; + } + } + + if (House->IsPlayerControl) { + Sound_Effect(VOC_CASHTURN, Coord); + } + Status = DURING; + Begin_Mode(BSTATE_CONSTRUCTION); + Detach_All(true); + Transmit_Message(RADIO_OVER_OUT); + IsReadyToCommence = false; + break; + } + Transmit_Message(RADIO_RUN_AWAY); + break; + + case DURING: + if (IsReadyToCommence) { + House->IsRecalcNeeded = true; + if (IsOwnedByPlayer) Speak(VOX_STRUCTURE_SOLD); + + /* + ** Construction yards that deconstruct, really just revert back + ** to an MCV. + */ + if (Target_Legal(ArchiveTarget) && *this == STRUCT_CONST && House->IsHuman && Strength > 0) { + ScenarioInit++; + UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); + ScenarioInit--; + if (unit != NULL) { + + /* + ** Unlimbo the MCV onto the map. The MCV should start in the same + ** health condition that the construction yard was in. + */ + fixed ratio = Health_Ratio(); + int money = Refund_Amount(); + TARGET arch = ArchiveTarget; + COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE)); + + delete this; + + if (unit->Unlimbo(place, DIR_SW)) { + unit->Strength = unit->Class_Of().MaxStrength * ratio; + + /* + ** Lift the move destination from the building and assign + ** it to the unit. + */ + if (Target_Legal(arch)) { + unit->Assign_Destination(arch); + unit->Assign_Mission(MISSION_MOVE); + } + + } else { + + /* + ** If, for some strange reason, the MCV could not be placed on the + ** map, then give the player some money to compensate. + */ + House->Refund_Money(money); + } + } else { + House->Refund_Money(Refund_Amount()); + delete this; + } + + } else { + + /* + ** Selling off a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** A sold building still counts as a kill, but it just isn't directly + ** attributed to the enemy. + */ + WhoLastHurtMe = HOUSE_NONE; + Record_The_Kill(NULL); + + /* + ** The player gets part of the money back for the sell. + */ + House->Refund_Money(Refund_Amount()); + House->Stole(-Refund_Amount()); + Limbo(); + + /* + ** Finally, delete the building from the game. + */ + delete this; + } + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * * + * Buildings that can attack are processed by this attack mission state machine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 02/22/1996 JLB : SAM doesn't lower back into ground. * + *=============================================================================================*/ +int BuildingClass::Mission_Attack(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_SAM) { + switch (Status) { + + /* + ** This is the target tracking state of the launcher. It will rotate + ** to face the current TarCom of the launcher. + */ + case SAM_READY: + if ((Class->IsPowered && House->Power_Fraction() < 1) || IsJammed) { + return(1); + } + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING; + } + } + } + return(1); + + /* + ** The launcher is in the process of firing. + */ + case SAM_FIRING: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Fire_At(TarCom, 1); + Status = SAM_READY; + } + } + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); + + } + + if (!Target_Legal(TarCom)) { + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } + + int primary = What_Weapon_Should_I_Use(TarCom); + IsReadyToCommence = true; + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + case FIRE_CANT: + case FIRE_RANGE: + case FIRE_AMMO: + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + break; + + case FIRE_FACING: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(2); + + case FIRE_REARM: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(Arm); + + case FIRE_BUSY: + return(1); + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, primary); + return(1); + + default: + break; + } + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * * + * This state machine handles the refinery when it unloads the harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Harvest(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, // Dock the Tiberium cannister. + WAIT_FOR_DOCK, // Waiting for docking to complete. + MIDDLE, // Offload "bails" of tiberium. + WAIT_FOR_UNDOCK // Waiting for undocking to complete. + }; + switch (Status) { + case INITIAL: + Status = WAIT_FOR_DOCK; + break; + + case WAIT_FOR_DOCK: + if (IsReadyToCommence) { + IsReadyToCommence = false; + Status = MIDDLE; + } + break; + + case MIDDLE: + if (IsReadyToCommence) { + IsReadyToCommence = false; + + /* + ** Force any bib squatters to scatter. + */ + Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_S)].Incoming(0, true, true); + + FootClass * techno = Attached_Object(); + if (techno) { + int bail = techno->Offload_Tiberium_Bail(); + + if (bail) { + House->Harvested(bail); + if (techno->Tiberium_Load() > 0) { + return(1); + } + } + } + Status = WAIT_FOR_UNDOCK; + } + break; + + case WAIT_FOR_UNDOCK: + if (IsReadyToCommence) { + + /* + ** Detach harvester and go back into idle state. + */ + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * * + * This state machine is used when the building is active in some sort of repair or * + * construction mode. The construction yard will animate. The repair facility will repair * + * anything that it docked on it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 06/25/1995 JLB : Handles repair facility * + * 07/29/1995 JLB : Repair rate is controlled by power rating. * + *=============================================================================================*/ +int BuildingClass::Mission_Repair(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = DURING; + break; + + case DURING: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); + } + + if (*this == STRUCT_REPAIR) { + enum { + INITIAL, + IDLE, + DURING + }; + switch (Status) { + case INITIAL: + { + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Assign_Mission(MISSION_GUARD); + return(1); + } + IsReadyToCommence = false; + int distance = 0x10; + TechnoClass *tech = Contact_With_Whom(); + + /* + ** BG: If the unit to repair is an aircraft, and the aircraft is + ** fixed-wing, and it's landed, be much more liberal with the + ** distance check. Fixed-wing aircraft are very inaccurate with + ** their landings. + */ + if (tech->What_Am_I() == RTTI_AIRCRAFT) { + if ( ((AircraftClass *)tech)->Class->IsFixedWing && + ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + distance = 0x80; + } + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < distance) { + Status = IDLE; + return(TICKS_PER_SECOND/4); + } + break; + } + + case IDLE: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * radio = Contact_With_Whom(); + + if ( ((radio->Health_Ratio() < Rule.ConditionGreen) || + (radio->What_Am_I() == RTTI_UNIT && *(UnitClass *)radio == UNIT_MINELAYER)) + && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { + + /* + ** If the object over the repair bay is marked as useless, then + ** sell it back to get some money. + */ + if (radio->IsUseless) { + if (!radio->House->IsHuman) { + radio->Sell_Back(1); + } + Status = INITIAL; + IsReadyToCommence = true; + } else { + if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + Status = DURING; + Begin_Mode(BSTATE_ACTIVE); + IsReadyToCommence = false; + } + } else { +// Transmit_Message(RADIO_RUN_AWAY); +///*BG*/ if(radio->Health_Ratio() >= Rule.ConditionGreen) { +// Transmit_Message(RADIO_RUN_AWAY); +// } + } + } + break; + + case DURING: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + return(1); + } + + /* + ** Check to see if the repair light blink has completed and the attached + ** unit is not doing something else. If these conditions are favorable, + ** the repair can proceed another step. + */ + if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + IsReadyToCommence = false; + + /* + ** Tell the attached unit to repair one step. It will respond with how + ** it fared. + */ + switch (Transmit_Message(RADIO_REPAIR)) { + + /* + ** The repair step proceeded smoothly. Proceed normally with the + ** repair process. + */ + case RADIO_ROGER: + break; + + /* + ** The repair operation was aborted because of some reason. Presume + ** that the reason is because of low cash. + */ + case RADIO_CANT: + if (IsOwnedByPlayer) Speak(VOX_NO_CASH); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step resulted in a completely repaired unit. + */ + case RADIO_ALL_DONE: + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step could not be completed because this unit is already + ** at full strength. + */ + case RADIO_NEGATIVE: + default: +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (*this == STRUCT_HELIPAD || *this == STRUCT_AIRSTRIP) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { + Begin_Mode(BSTATE_ACTIVE); + Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); + Status = DURING; + return(1); + } + Assign_Mission(MISSION_GUARD); + break; + + case DURING: + if (IsReadyToCommence) { + if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { + Assign_Mission(MISSION_GUARD); + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + return(1); + } else { + fixed pfrac = Saturate(House->Power_Fraction(), 1); + if (pfrac < fixed::_1_2) pfrac = fixed::_1_2; + int time = Inverse(pfrac) * Rule.ReloadRate * TICKS_PER_MINUTE; +// int time = Bound((int)(TICKS_PER_SECOND * Saturate(House->Power_Fraction(), 1)), 0, TICKS_PER_SECOND); +// time = (TICKS_PER_SECOND*3) - time; + IsReadyToCommence = false; + return(time); + } + } + break; + + default: + break; + } + return(3); + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * * + * This handles the Temple of Nod launching its nuclear missile. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int BuildingClass::Mission_Missile(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_ADVANCED_TECH) { + enum { + DOOR_OPENING, + LAUNCH_UP, + SATELLITE_DEPLOY, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building, the missile rising, and smoke broiling. + */ + case DOOR_OPENING: + { +#ifdef FIXIT_VERSION_3 + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + if (sput) { + IsReadyToCommence = false; + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + } +#else + IsReadyToCommence = false; + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + return(1); +#endif + } + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + AnimClass * sput = As_Animation(AnimToTrack); + if (sput) { + if (sput->Fetch_Stage() >= 19) { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + + BulletClass * bullet = new BulletClass(BULLET_GPS_SATELLITE, targ, this, 200, WARHEAD_FIRE, MPH_ROCKET); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Assign_Mission(MISSION_GUARD); + } + } + } + } + return(1); + } + } + + if (*this == STRUCT_MSLO) { + enum { + INITIAL, + DOOR_OPENING, + LAUNCH_UP, + LAUNCH_DOWN, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building. + */ + case INITIAL: + IsReadyToCommence = false; + Begin_Mode(BSTATE_ACTIVE); // open the door + Status = DOOR_OPENING; + return(1); + + /* + ** This polls for the case when the door is actually open and + ** then kicks off the missile smoke. + */ + case DOOR_OPENING: + if (IsReadyToCommence) { + Begin_Mode(BSTATE_AUX1); // hold the door open + Status = LAUNCH_UP; + return(14); + } + return(1); + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + BulletClass * bullet = new BulletClass(BULLET_NUKE_UP, targ, this, 200, WARHEAD_HE, MPH_VERY_FAST); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)28, 0xA0); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Speak(VOX_ABOMB_LAUNCH); + Status = LAUNCH_DOWN; + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + return(6); + } + bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 64; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + return(8 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is in the air, this handles waiting for + ** the missile to be off the screen and then launching one down + ** over the target. + */ + case LAUNCH_DOWN: + { + Begin_Mode(BSTATE_AUX2); // start the door closing + +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + Status = DONE_LAUNCH; + return(6); + } + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + if (bullet) { +#endif + Status = DONE_LAUNCH; + return(6); + } +#ifdef OBSOLETE + } + return(1); +#endif + + /* + ** Once the missile is done launching this handles allowing + ** the building to sit there with its door closed. + */ + case DONE_LAUNCH: + Begin_Mode(BSTATE_IDLE); // keep the door closed. + Assign_Mission(MISSION_GUARD); + return(60); + } + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * * + * This routine will reveal the building to the specified house. It will handle updating * + * the sidebar for player owned buildings. A player owned building that hasn't been * + * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * + * abilities even though it exists on the map for all other purposes. * + * * + * INPUT: house -- The house that this building is being revealed to. * + * * + * OUTPUT: Was this building revealed by this procedure? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Revealed(HouseClass * house) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Revealed(house)) { + + if (!ScenarioInit) { + House->JustBuiltStructure = Class->Type; + House->IsBuiltSomething = true; + } + House->IsRecalcNeeded = true; + + /* + ** Perform any grand opening here so that in the scenarios where a player + ** owned house is not yet revealed, it won't be reflected in the sidebar + ** selection icons. + */ + if (!In_Radio_Contact() && House->IsHuman && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } else { + if (!In_Radio_Contact() && !House->IsHuman && house == House && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * * + * This routine is called when the exact mode of the building isn't known. By examining * + * the building's condition, this routine will assign an appropriate mission. * + * * + * INPUT: initial -- This this being called during scenario init? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Enter_Idle_Mode(bool initial) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then + ** this must be an initial building. Start such buildings in idle state. For other buildings + ** it indicates that it is being placed during game play and thus it must start in + ** the "construction" mission. + */ + MissionType mission = MISSION_GUARD; + + + if (!initial || ScenarioInit || Debug_Map) { + Begin_Mode(BSTATE_IDLE); + mission = MISSION_GUARD; + } else { + Begin_Mode(BSTATE_CONSTRUCTION); + mission = MISSION_CONSTRUCTION; + } + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * * + * This routine will determine the number of pips that should be filled in when rendering * + * the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of pips to display as filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Pip_Count(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Class->Max_Pips() * House->Tiberium_Fraction()); +} + + +/*********************************************************************************************** + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * * + * This routine is called when the building is destroyed by "unnatural" means. Typically * + * as a result of combat. If the building is known to the player, then it should be * + * announced. * + * * + * INPUT: source -- The object most directly responsible for the building's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Death_Announcement(TechnoClass const * source) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (source != NULL && House->IsPlayerControl) { + Speak(VOX_STRUCTURE_DESTROYED); + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * * + * This routine will return with the default direction to use when firing from this * + * building. This is the facing of the turret except for the case of non-turret equipped * + * buildings that have a weapon (e.g., guard tower). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Fire_Direction(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + return(PrimaryFacing.Current()); + } + return(Direction(TarCom)); +} + + +/*********************************************************************************************** + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * * + * Use this routine to fetch the remap table to use. This override function is needed * + * because the default remap table for techno objects presumes the object is a unit. * + * Buildings aren't units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the proper remap table to use for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Remap_Table(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(House->Remap_Table(IsBlushing, Class->Remap)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * * + * This is the unload mission for a building. This really only applies to the weapon's * + * factory, since it needs the sophistication of an unload mission due to the door * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Unload(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_WEAP) { + CELL cell = Coord_Cell(Coord) + Class->ExitList[0]; + COORDINATE coord = Cell_Coord(cell); + CellClass * cellptr = &Map[cell]; + enum { + INITIAL, + CLEAR_BIB, + OPEN, + LEAVE, + CLOSE + }; + enum { + DOOR_STAGES = 5, + DOOR_RATE = 8 + }; + UnitClass * unit; + switch (Status) { + /* + ** Start the door opening. + */ + case INITIAL: +// if (cellptr->Cell_Techno()) { +// cellptr->Incoming(0, true); +// } + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_GUARD); + unit->Commence(); + } + Open_Door(DOOR_RATE, DOOR_STAGES); + Status = CLEAR_BIB; + break; + + /* + ** Now that the occupants can peek out the door, they will tell + ** everyone that could be blocking the way, that they should + ** scatter away. + */ + case CLEAR_BIB: + if (cellptr->Cell_Techno()) { + cellptr->Incoming(0, true, true); + + /* + ** Scatter everything around the weapon's factory door. + */ + for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) { + CellClass * cptr = &cellptr->Adjacent_Cell(f); + if (cptr->Cell_Building() == NULL) { + cptr->Incoming(coord, true, true); + } + } + } else { + Status = OPEN; + } + break; + + /* + ** When the door is finally open and the way is clear, tell the + ** unit to drive out. + */ + case OPEN: + if (Is_Door_Open()) { + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_MOVE); + + if (House->IQ >= Rule.IQGuardArea) { + unit->Assign_Mission(MISSION_GUARD_AREA); + unit->ArchiveTarget = ::As_Target(House->Where_To_Go(unit)); + } + unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, coord); +// unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Adjacent_Cell(Center_Coord(), FACING_S), FACING_S)); + unit->Set_Speed(128); + Status = LEAVE; + } else { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } + } + break; + + /* + ** Wait until the unit has completely left the building. + */ + case LEAVE: + if (!IsTethered) { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } else { + +// if (In_Radio_Contact() && !((FootClass *)Contact_With_Whom())->IsDriving) { +// Transmit_Message(RADIO_OVER_OUT); +// } + + } + break; + + /* + ** Wait while the door closes. + */ + case CLOSE: + if (Is_Door_Closed()) { + Enter_Idle_Mode(); + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + Assign_Mission(MISSION_GUARD); + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * * + * This routine will return the current power output for this building. The power output * + * is adjusted according to the damage level of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current power output for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Power_Output(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Power) { + return(Class->Power * fixed(LastStrength, Class->MaxStrength)); + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Detach -- Handles target removal from the game system. * + * * + * This routine is called when the specified target is about to be removed from the game * + * system. * + * * + * INPUT: target -- The target to be removed from this building's targeting computer. * + * * + * all -- Is the target about to be completely eliminated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach(TARGET target, bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + TechnoClass::Detach(target, all); + if (target == WhomToRepay) { + WhomToRepay = TARGET_NONE; + } + if (target == AnimToTrack) { + AnimToTrack = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * * + * When selling very cheap buildings (such as the silo), a technician will pop out since * + * generating minigunners would be overkill -- the player could use this loophole to * + * gain an advantage. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type that this building will generate as a survivor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType BuildingClass::Crew_Type(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (Class->Type) { + case STRUCT_STORAGE: + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + + case STRUCT_CONST: + if (!IsCaptured && House->IsHuman && Percent_Chance(25)) { + return(INFANTRY_RENOVATOR); + } + break; + + case STRUCT_KENNEL: + if (Percent_Chance(50)) { + return(INFANTRY_DOG); + } else { + return(INFANTRY_NONE); + } + + case STRUCT_TENT: + case STRUCT_BARRACKS: + return(INFANTRY_E1); + + default: + break; + } + return(TechnoClass::Crew_Type()); +} + + +/*********************************************************************************************** + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * * + * When this routine is called, it indicates that the building is about to be destroyed * + * or captured. In such a case any production it may be doing, must be abandoned. * + * * + * INPUT: all -- Is the object about the be completely destroyed? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach_All(bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If it is producing something, then it must be abandoned. + */ + if (Factory) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + if (House) { + FactoryClass * factory = House->Fetch_Factory(Class->ToBuild); + + /* + ** If a factory was found, then temporarily disable this building and then + ** determine if any object that is being produced can still be produced. If + ** not, then the object being produced must be abandoned. + */ + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { + House->Abandon_Production(Class->ToBuild); + } + IsInLimbo = false; + } + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * * + * This routine is used to clear the way for placement of the specified object (usually * + * a building). If there are friendly units blocking the placement area, they are told * + * to scatter. Enemy blocking units are attacked. * + * * + * INPUT: techno -- Pointer to the object that is desired to be placed. * + * * + * cell -- The cell that placement wants to occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1995 JLB : Created. * + * 09/27/1995 JLB : Revised to use type class function. * + *=============================================================================================*/ +bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (techno) { + return (((BuildingTypeClass const &)techno->Class_Of()).Flush_For_Placement(cell, House)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this building * + * * + * This routine is called when the building needs to discharge a unit. It will find a * + * nearby (adjacent) cell that is clear enough for the specified object to enter. Typical * + * use of this routine is when the airfield disgorges its cargo. * + * * + * INPUT: techno -- Pointer to the object that wishes to exit this building. * + * * + * OUTPUT: Returns with the cell number to use for object placement. If no free location * + * could be found, then zero (0) is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 02/20/1996 JLB : Added default case for exit cell calculation. * + *=============================================================================================*/ +CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * ptr; + CELL origin = Coord_Cell(Coord); + + ptr = Class->ExitList; + if (ptr != NULL) { + while (*ptr != REFRESH_EOL) { + CELL cell = origin + *ptr++; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } else { + int x1, x2; + int y1, y2; + CELL cell; + + y1 = -1; + y2 = Class->Height(); + for (x1 = -1; x1 <= Class->Width(); x1++) { + cell = origin + x1 + (y1 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + x1 + (y2 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + + x1 = -1; + x2 = Class->Width(); + for (y1 = -1; y1 <= Class->Height(); y1++) { + cell = origin + (y1 * MAP_CELL_W) + x1; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + (y1 * MAP_CELL_W) + x2; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * * + * This routine answers the question 'can this building be moved?' Typically, only the * + * construction yard can be moved and it does this by undeploying back into a MCV. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building move to a new location under player control? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/04/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Can_Player_Move(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(*this == STRUCT_CONST); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * * + * This routine will return the coordinate where an object that wishes to leave the * + * building will exit at. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that the object should be created at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Exit_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->ExitCoordinate) { + return(Coord_Add(Coord, Class->ExitCoordinate)); + } + return(TechnoClass::Exit_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern. * + * * + * Use this routine to coordinate a landing operation. The specified checkpoint is * + * converted into a cell number. The landing aircraft should fly over that cell and then * + * request the next check point. * + * * + * INPUT: cp -- The check point to convert to a cell number. * + * * + * OUTPUT: Returns with the cell that the aircraft should fly over in order to complete * + * that portion of the landing pattern. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CELL BuildingClass::Check_Point(CheckPointType cp) const +{ + CELL xoffset = 6; // Downwind offset. + CELL yoffset = 5; // Crosswind offset. + CELL cell = Coord_Cell(Center_Coord()); + + switch (cp) { + case CHECK_STACK: + xoffset = 0; + break; + + case CHECK_CROSSWIND: + yoffset = 0; + break; + + case CHECK_DOWNWIND: + default: + break; + } + + if ((Cell_X(cell) - Map.MapCellX) > Map.MapCellWidth/2) { + xoffset = -xoffset; + } + + if ((Cell_Y(cell) - Map.MapCellY) > Map.MapCellHeight/2) { + yoffset = -yoffset; + } + + return(XY_Cell(Cell_X(cell)+xoffset, Cell_Y(cell)+yoffset)); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Radar_Spied - set house's RadarSpied field appropriately. * + * * + * This routine is called when a radar facility is captured or destroyed. It fills in the * + * RadarSpied field of the house based on whether there's a spied-upon radar facility or not* + * * + * INPUT: none * + * * + * OUTPUT: House->RadarSpied field gets set appropriately. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/22/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Update_Radar_Spied(void) +{ + House->RadarSpied = 0; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (*obj == STRUCT_RADAR /* || *obj == STRUCT_EYE */) { + House->RadarSpied |= obj->SpiedBy; + } + } + } + Map.RadarClass::Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * * + * This is the basic scenario initialization of building function. It * + * is called when reading the scenario startup INI file and it handles * + * creation of all specified buildings. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Read_INI(CCINIClass & ini) +{ + BuildingClass * b; // Working unit pointer. + HousesType bhouse; // Building house. + StructType classid; // Building type. + CELL cell; // Cell of building. + char buf[128]; + char * trigname; // building's trigger's name + + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get a building entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + + /* + ** 2nd token: building name. + */ + classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); + + if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { + int strength; + DirType facing; + + /* + ** 3rd token: strength. + */ + strength = atoi(strtok(NULL, ",")); + + /* + ** 4th token: cell #. + */ + cell = atoi(strtok(NULL, ",")); + + /* + ** 5th token: facing. + */ + facing = (DirType)atoi(strtok(NULL, ",")); + + /* + ** 6th token: triggername (can be NULL). + */ + trigname = strtok(NULL, ","); + + bool sellable = false; + char * token_pointer = strtok(NULL, ","); + if (token_pointer) { + sellable = atoi(token_pointer); + } + + bool rebuild = false; + token_pointer = strtok(NULL, ","); + if (token_pointer) { + rebuild = atoi(token_pointer); + } + + b = new BuildingClass(classid, bhouse); + if (b) { + + TriggerTypeClass * tp = TriggerTypeClass::From_Name(trigname); + if (tp) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + b->Trigger = tt; + } + } + b->IsAllowedToSell = sellable; + b->IsToRebuild = rebuild; + b->IsToRepair = rebuild || *b == STRUCT_CONST; + + if (b->Unlimbo(Cell_Coord(cell), facing)) { + strength = min(strength, 0x100); + strength = b->Class->MaxStrength * fixed(strength, 256); + b->Strength = strength; + if (b->Strength > b->Class->MaxStrength-3) b->Strength = b->Class->MaxStrength; + b->IsALemon = false; + } else { + + /* + ** If the building could not be unlimboed on the map, then this indicates + ** a serious error. Delete the building. + */ + delete b; + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * * + * This will store the building data (as it relates to scenario initialization) to the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that the building data will be stored to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing building data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the data out. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (!building->IsInLimbo) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%d", + building->House->Class->IniName, + building->Class->IniName, + building->Health_Ratio()*256, + Coord_Cell(building->Coord), + building->PrimaryFacing.Current(), + building->Trigger.Is_Valid() ? building->Trigger->Class->IniName : "None", + building->IsAllowedToSell, + building->IsToRebuild + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * * + * This routine will determine the "center" location of this building for purposes of * + * targeting. Usually, this location is somewhere near the foundation of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing upon this building (or trying to * + * walk onto it). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Target_Coord(void) const +{ + COORDINATE coord = Center_Coord(); + + if (Class->FoundationFace != FACING_NONE) { + return(Adjacent_Cell(coord, Class->FoundationFace)); + } + return(coord); +} + + +/*********************************************************************************************** + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * * + * Some building (notably the computer controlled ones) can have a factory object attached. * + * This routine handles processing of that factory and also detecting when production * + * should begin in order to initiate production. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Factory_AI(void) +{ + /* + ** Handle any production tied to this building. Only computer controlled buildings have + ** production attached to the building itself. The player uses the sidebar interface for + ** all production control. + */ + if (Factory.Is_Valid() && Factory->Has_Completed() && PlacementDelay == 0) { + TechnoClass * product = Factory->Get_Object(); +// FactoryClass * fact = Factory; + + switch (Exit_Object(product)) { + + /* + ** If the object could not leave the factory, then either request + ** a transport, place the (what must be a) building using another method, or + ** abort the production and refund money. + */ + case 0: + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + break; + + /* + ** Exiting this building is prevented by some temporary blockage. Wait + ** a bit before trying again. + */ + case 1: + PlacementDelay = TICKS_PER_SECOND*3; + break; + + /* + ** The object was successfully sent from this factory. Inform the house + ** tracking logic that the requested object has been produced. + */ + case 2: + switch (product->What_Am_I()) { + case RTTI_VESSEL: + House->JustBuiltVessel = ((VesselClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_UNIT: + House->JustBuiltUnit = ((UnitClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + House->JustBuiltInfantry = ((InfantryClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + House->JustBuiltStructure = ((BuildingClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + House->JustBuiltAircraft = ((AircraftClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + default: + break; + } +// fact->Completed(); + Factory->Completed(); +// delete fact; + delete (FactoryClass *)Factory; + Factory = 0; + break; + + default: + break; + } + } + + /* + ** Pick something to create for this factory. + */ + if (House->IsStarted && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Buildings that produce other objects have special factory logic handled here. + */ + if (Class->ToBuild != RTTI_NONE) { + if (Factory.Is_Valid()) { + + /* + ** If production has halted, then just abort production and make the + ** funds available for something else. + */ + if (PlacementDelay == 0 && !Factory->Is_Building()) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + } else { + + /* + ** Only look to start production if there is at least a small amount of + ** money available. In cases where there is no practical money left, then + ** production can never complete -- don't bother starting it. + */ + if (House->IsStarted && House->Available_Money() > 10) { + TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild, *this == STRUCT_KENNEL); + + /* + ** If a suitable object type was selected for production, then start + ** producing it now. + */ + if (techno != NULL) { + Factory = new FactoryClass; + if (Factory.Is_Valid()) { + if (!Factory->Set(*techno, *House)) { + delete (FactoryClass *)Factory; + Factory = 0; + } else { + House->Production_Begun(Factory->Get_Object()); + Factory->Start(); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * * + * Some buildings have a turret and this routine handles processing the turret rotation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 10/27/1996 JLB : Rotation does not occur if power and no power avail. * + *=============================================================================================*/ +void BuildingClass::Rotation_AI(void) +{ + if (Class->IsTurretEquipped && + Mission != MISSION_CONSTRUCTION && + Mission != MISSION_DECONSTRUCTION && + (!Class->IsPowered || House->Power_Fraction() >= 1)) { + + /* + ** Rotate turret to match desired facing. + */ + if (PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * * + * This handles the special logic required of the charging tesla coil. It requires special * + * processing since its charge up is dependant upon the target and power surplus of the * + * owning house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Charging_AI(void) +{ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && BState != BSTATE_CONSTRUCTION) { + if (Target_Legal(TarCom) && House->Power_Fraction() >= 1) { + if (!IsCharged) { + if (IsCharging) { +// if (stagechange) { + Mark(MARK_CHANGE); + if (Fetch_Stage() >= 9) { + IsCharged = true; + IsCharging = false; + Set_Rate(0); + } +// } + } else if (!Arm) { + IsCharged = false; + IsCharging = true; + Set_Stage(0); + Set_Rate(3); + Sound_Effect(VOC_TESLA_POWER_UP, Coord); + } + } + } else { + if (IsCharging || IsCharged) { + Mark(MARK_CHANGE); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * * + * This routine handle the repair animation and healing logic. It also detects when the * + * (computer controlled) building should begin repair or sell itself. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair_AI(void) +{ + if (House->IQ >= Rule.IQRepairSell && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + /* + ** Possibly start repair process if the building is below half strength. + */ +// unsigned ratio = MIN(House->Smartness, 0x00F0); + if (Can_Repair()) { + if (House->Available_Money() >= Rule.RepairThreshhold) { + if (!House->DidRepair) { + if (!IsRepairing && (IsCaptured || IsToRepair || House->IsHuman || Session.Type != GAME_NORMAL)) { + House->DidRepair = true; // flag that this house did its repair allocation for this frame + Repair(1); + + if (!House->IsHuman) { + House->RepairTimer = Random_Pick((int)(House->RepairDelay * (TICKS_PER_MINUTE/4)), (int)(House->RepairDelay * TICKS_PER_MINUTE * 2)); + } + } + } + } else { + if ((Session.Type != GAME_NORMAL || IsAllowedToSell) && IsTickedOff && House->Control.TechLevel >= Rule.IQSellBack && Random_Pick(0, 50) < House->Control.TechLevel && !Trigger.Is_Valid() && *this != STRUCT_CONST && Health_Ratio() < Rule.ConditionRed) { + Sell_Back(1); + } + } + } + } + + /* + ** If it is repairing, then apply any repair effects as necessary. + */ + if (IsRepairing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0) { + IsWrenchVisible = (IsWrenchVisible == false); + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + /* + ** Check for and expend any necessary monies to continue the repair. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsRepairing = false; + } + } else { + IsRepairing = false; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * * + * This will process the general building animation mechanism. It detects when the * + * building animation sequence has completed and flags the building to perform mission * + * changes as a result. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Animation_AI(void) +{ + bool stagechange = Graphic_Logic(); + bool toloop = false; + + /* + ** Always refresh the SAM site if it has an animation change. + */ + if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); + + if ((!Class->IsTurretEquipped && *this != STRUCT_TESLA) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { + if (stagechange) { + + /* + ** Check for animation end or if special case of MCV deconstructing when it is allowed + ** to convert back into an MCV. + */ + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + /* + ** When the last frame of the current animation sequence is reached, flag that + ** a new mission may be started. This must occur before the animation actually + ** loops so that if a mission change does occur, it will have a chance to change + ** the building graphic before the last frame is replaced by the first frame of + ** the loop. + */ + if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (!Target_Legal(ArchiveTarget) /*Special.IsMCVDeploy*/ && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { + IsReadyToCommence = true; + } + + /* + ** If the animation advances beyond the last frame, then start the animation + ** sequence over from the beginning. + */ + if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { + toloop = true; + } + Mark(MARK_CHANGE); + } else { + if (BState == BSTATE_NONE || Fetch_Rate() == 0) { + IsReadyToCommence = true; + } + } + } + + /* + ** If there is a door that is animating, then it might cause this building + ** to be redrawn. Check for and flag to redraw as necessary. + */ + if (Time_To_Redraw()) { + Clear_Redraw_Flag(); + Mark(MARK_CHANGE); + } + + /* + ** The animation sequence has looped. Restart it and flag this loop condition. + ** This is used to tell the mission system that the animation has completed. It + ** also signals that now is a good time to act on any pending mission. + */ + if (toloop) { + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * * + * This routine is called to determine how many survivors should run from this building * + * when it is either sold or destroyed. Buildings that are captured have fewer survivors. * + * The number of survivors is a portion of the cost of the building divided by the cost * + * of a minigunner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of soldiers that should run from this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::How_Many_Survivors(void) const +{ + if (IsSurvivorless || !Class->IsCrew) return(0); + + int divisor = InfantryTypeClass::As_Reference(INFANTRY_E1).Raw_Cost(); + if (divisor == 0) return(0); + if (IsCaptured) divisor *= 2; + int count = (Class->Raw_Cost() * Rule.SurvivorFraction) / divisor; + return(Bound(count, 1, 5)); +} + + +/*********************************************************************************************** + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * * + * This routine will return with a pointer to the shape data for the building. The shape * + * data is different than normal when the building is undergoing construction and * + * disassembly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Get_Image_Data(void) const +{ + if (BState == BSTATE_CONSTRUCTION) { + return(Class->Get_Buildup_Data()); + } + return(TechnoClass::Get_Image_Data()); +} + + +/*********************************************************************************************** + * BuildingClass::Value -- Determine the value of this building. * + * * + * The value of the building is normally just its ordinary assigned value. However, in the * + * case of fakes, the value is artificially enhanced to match the structure that is * + * being faked. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the point value of the building type. * + * * + * WARNINGS: The point value returned should not be used for scoring, only for target * + * scanning. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Value(void) const +{ + if (Class->IsFake) { + switch (Class->Type) { + case STRUCT_FAKEWEAP: + return(BuildingTypeClass::As_Reference(STRUCT_WEAP).Reward + BuildingTypeClass::As_Reference(STRUCT_WEAP).Risk); + + case STRUCT_FAKECONST: + return(BuildingTypeClass::As_Reference(STRUCT_CONST).Reward + BuildingTypeClass::As_Reference(STRUCT_CONST).Risk); + + case STRUCT_FAKE_YARD: + return(BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Reward + BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Risk); + + case STRUCT_FAKE_PEN: + return(BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Reward + BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Risk); + + case STRUCT_FAKE_RADAR: + return(BuildingTypeClass::As_Reference(STRUCT_RADAR).Reward + BuildingTypeClass::As_Reference(STRUCT_RADAR).Risk); + + default: + break; + } + } + return(TechnoClass::Value()); +} + + +/*********************************************************************************************** + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/20/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Remove_Gap_Effect(void) +{ + // unjam this one's field... + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + if (!House->IsPlayerControl && PlayerPtr->IsGPSActive) { + Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + // and rejam any overlapping buildings' fields + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House && *obj == STRUCT_GAP && obj!=this) { + obj->IsJamming = false; + obj->Arm = 0; +// Map.Jam_From(Coord_Cell(obj->Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + } +} + + +short const * BuildingClass::Overlap_List(bool redraw) const +{ + if ((SpiedBy & (1 << PlayerPtr->Class->House)) != 0 && IsSelected && (*this == STRUCT_BARRACKS || *this == STRUCT_TENT)) { + static short const _list[] = { + -1, 2, (MAP_CELL_W*1)-1, (MAP_CELL_W*1)+2, REFRESH_EOL + }; + return(_list); + } + return(TechnoClass::Overlap_List(redraw)); +} diff --git a/CODE/BUILDING.H b/CODE/BUILDING.H new file mode 100644 index 0000000..ecc5d0b --- /dev/null +++ b/CODE/BUILDING.H @@ -0,0 +1,356 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BUILDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BUILDING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUILDING_H +#define BUILDING_H + +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "bullet.h" +#include "target.h" +#include "factory.h" +#include "techno.h" + +#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory +#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open +#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling + +/**************************************************************************** +** For each instance of a building in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular building. +*/ +class BuildingClass : public TechnoClass +{ + public: + + /* + ** This points to the control data that gives this building its characteristics. + */ + CCPtr Class; + + /* + ** If this building is in the process of producing something, then this + ** will point to the factory manager. + */ + CCPtr Factory; + + /* + ** This is the house that originally owned this factory. Objects buildable + ** by this house type will be produced from this factory regardless of who + ** the current owner is. + */ + HousesType ActLike; + + /* + ** This building should be rebuilt if it is destroyed. This is in spite + ** of the condition of the prebuilt base list. + */ + unsigned IsToRebuild:1; + + /* + ** Is the building allowed to repair itself? + */ + unsigned IsToRepair:1; + + /* + ** If the computer owns this building, then it is allowed to sell it if + ** the situation warrants it. In the other case, it cannot sell the + ** building regardless of conditions. + */ + unsigned IsAllowedToSell:1; + + /* + ** If the building is at a good point to change orders, then this + ** flag will be set to true. + */ + unsigned IsReadyToCommence:1; + + /* + ** If this building is currently spending money to repair itself, then + ** this flag is true. It will automatically be set to false when the building + ** has reached full strength, when money is exhausted, or if the player + ** specifically stops the repair process. + */ + unsigned IsRepairing:1; + + /* + ** If repair is currently in progress and this flag is true, then a wrench graphic + ** will be overlaid on the building to give visual feedback for the repair process. + */ + unsigned IsWrenchVisible:1; + + /* + ** This flag is set when a commando has raided the building and planted + ** plastic explosives. When the CommandoCountDown timer expires, the + ** building takes massive damage. + */ + unsigned IsGoingToBlow:1; + + /* + ** If this building was destroyed by some method that would prevent + ** survivors, then this flag will be true. + */ + unsigned IsSurvivorless:1; + + /* + ** These state control variables are used by the obelisk for the charging + ** animation. + */ + unsigned IsCharging:1; + unsigned IsCharged:1; + + /* + ** A building that has been captured will not contain the full compliment + ** of crew. This is true even if it subsequently gets captured back. + */ + unsigned IsCaptured:1; + + /* + ** Used by the gap generator to decide if it should jam or unjam + */ + unsigned IsJamming:1; + + /* + ** Used by radar facilities to know if they're being jammed by a mobile + ** radar jammer + */ + unsigned IsJammed:1; + + /* + ** Used only by advanced tech center, this keeps track of whether the + ** GPS satellite has been fired or not. + */ + unsigned HasFired:1; + + /* + ** If Grand_Opening was already called for this building, then this + ** flag will be true. By utilizing this flag, multiple inadvertant + ** calls to Grand_Opening won't cause problems. + */ + unsigned HasOpened:1; + + /* + ** Special countdown to destruction value. If the building is destroyed, + ** it won't actually be removed from the map until this value reaches + ** zero. This delay is for cosmetic reasons. + */ + CDTimerClass CountDown; + + /* + ** This is the current animation processing state that the building is + ** in. + */ + BStateType BState; + BStateType QueueBState; + + /* + ** For multiplayer games, this keeps track of the last house to damage + ** this building, so if it burns to death or otherwise gradually dies, + ** proper credit can be given for the kill. + */ + HousesType WhoLastHurtMe; + + /* + ** This is the saboteur responsible for this building's destruction. + */ + TARGET WhomToRepay; + + /* + ** This is a record of the last strength of the building. Every so often, + ** it will compare this strength to the current strength. If there is a + ** discrepancy, then the owner power is adjusted accordingly. + */ + int LastStrength; + + /* + ** This is a target id of an animation we're keeping track of. Examples + ** of this usage are the advanced tech center, which needs to know + ** when the sputdoor animation has reached a certain stage. + */ + TARGET AnimToTrack; + + /* + ** This is the countdown timer that regulates placement retry logic + ** for factory type buildings. + */ + CDTimerClass PlacementDelay; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BuildingClass(StructType type, HousesType house); +#ifdef FIXIT_MULTI_SAVE + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), Factory(x), CountDown(x), PlacementDelay(x) {}; +#else + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), CountDown(x), PlacementDelay(x) {}; +#endif + virtual ~BuildingClass(void); + operator StructType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + TARGET Target_Scan(void); + BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);}; + + /* + ** Query functions. + */ + virtual int Value(void) const; + virtual void const * Get_Image_Data(void) const; + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual InfantryType Crew_Type(void) const; + virtual int Pip_Count(void) const; + virtual bool Can_Player_Move(void) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual bool Can_Demolish(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual DirType Fire_Direction(void) const; + virtual short const * Overlap_List(bool redraw=false) const; + int Shape_Number(void) const; + int Power_Output(void) const; + CELL Check_Point(CheckPointType cp) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Grand_Opening(bool captured = false); + virtual void Update_Buildables(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const; + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual int Exit_Object(TechnoClass * base); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void Fire_Out(void); + void Begin_Mode(BStateType bstate); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Death_Announcement(TechnoClass const * source=0) const; + virtual FireErrorType Can_Fire(TARGET, int which) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual bool Captured(HouseClass * newowner); + void Update_Radar_Spied(void); + + /* + ** AI. + */ + void Charging_AI(void); + void Rotation_AI(void); + void Factory_AI(void); + void Repair_AI(void); + void Animation_AI(void); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int control); + virtual void Sell_Back(int control); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Assign_Target(TARGET target); + virtual bool Toggle_Primary(void); + bool Flush_For_Placement(TechnoClass * techno, CELL cell); + + virtual int Mission_Unload(void); + virtual int Mission_Repair(void); + virtual int Mission_Attack(void); + virtual int Mission_Harvest(void); + virtual int Mission_Guard(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Missile(void); + virtual void Enter_Idle_Mode(bool initial=false); + void Remove_Gap_Effect(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "STRUCTURES";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + private: + void Drop_Debris(TARGET source = TARGET_NONE); + + static COORDINATE const CenterOffset[BSIZE_COUNT]; +}; + +#endif diff --git a/CODE/BULLET.CPP b/CODE/BULLET.CPP new file mode 100644 index 0000000..778d780 --- /dev/null +++ b/CODE/BULLET.CPP @@ -0,0 +1,1070 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BULLET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BULLET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : October 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::AI -- Logic processing for bullet. * + * BulletClass::BulletClass -- Bullet constructor. * + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * BulletClass::delete -- Bullet memory delete. * + * BulletClass::new -- Allocates memory for bullet object. * + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Bullet constructor. * + * * + * This is the constructor for the bullet class. It handles all * + * initialization of the bullet and starting it in motion toward its * + * target. * + * * + * INPUT: id -- The type of bullet this is (could be missile). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 06/20/1994 JLB : Firer is a base class pointer. * + * 12/10/1994 JLB : Auto calculate range optional. * + * 12/12/1994 JLB : Handles small arms as an instantaneous effect. * + * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. * + * 12/31/1994 JLB : Removed range parameter (not needed). * + *=============================================================================================*/ +BulletClass::BulletClass(BulletType id, TARGET target, TechnoClass * payback, int strength, WarheadType warhead, int speed) : + ObjectClass(RTTI_BULLET, Bullets.ID(this)), + Class(BulletTypes.Ptr((int)id)), + Payback(payback), + PrimaryFacing(DIR_N), + IsInaccurate(false), + IsToAnimate(false), + IsLocked(true), + TarCom(target), + MaxSpeed(speed), + Warhead(warhead) +{ + Strength = strength; + Height = FLIGHT_LEVEL; +} + + +/*********************************************************************************************** + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * * + * The bullet destructor must detect if a dog has been attached to this bullet. If so, * + * then the attached dog must be unlimboed back onto the map. This operation is necessary * + * because, unlike other objects, the dog flies with the bullet it fires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletClass::~BulletClass(void) +{ + if (GameActive) { + + /* + ** SPECIAL CASE: + ** The dog is attached to the dog bullet in a limbo state. When the bullet is + ** destroyed, the dog must come back out of limbo at the closest location possible to + ** the bullet. + */ + if (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) { + + InfantryClass * dog = (InfantryClass *)Payback; + if (dog) { + bool unlimbo = false; + DirType dogface = dog->PrimaryFacing; + COORDINATE newcoord = Coord; + + /* + ** Ensure that the coordinate, that the dog is to appear at, is legal. If not, + ** then find a nearby legal location. + */ + if (Can_Enter_Cell(newcoord) != MOVE_OK) { + newcoord = Map.Nearby_Location(Coord_Cell(newcoord), dog->Class->Speed); + } + + /* + ** Try to put the dog down where the target impacted. If we can't + ** put it in that cell, then scan through the adjacent cells, + ** starting with our current heading, until we find a place where + ** we can put him down. If all 8 adjacent cell checks fail, then + ** just delete the dog. + */ + for (int i = -1; i < 8; i++) { + if (i != -1) { + newcoord = Adjacent_Cell(Coord, FacingType(i)); + } + ScenarioInit++; + if (dog->Unlimbo(newcoord, dog->PrimaryFacing)) { + dog->Mark(MARK_DOWN); + dog->Do_Action(DO_DOG_MAUL, true); + if (dog->WasSelected) { + dog->Select(); + } + ScenarioInit--; + unlimbo = true; + break; + } + ScenarioInit--; + } + + Payback = 0; + + if (!unlimbo) { + delete dog; + } + } + } + BulletClass::Limbo(); + } + + Class=0; + Payback=0; +} + + +/*********************************************************************************************** + * BulletClass::new -- Allocates memory for bullet object. * + * * + * This function will "allocate" a block of memory for a bullet object. * + * This memory block is merely lifted from a fixed pool of blocks. * + * * + * INPUT: size -- The size of the memory block needed. * + * * + * OUTPUT: Returns with a pointer to an available bullet object block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void * BulletClass::operator new(size_t ) +{ + void * ptr = Bullets.Allocate(); + if (ptr) { + ((BulletClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * BulletClass::delete -- Bullet memory delete. * + * * + * Since bullets memory is merely "allocated" out of a pool, it never * + * actually gets deleted. * + * * + * INPUT: ptr -- Generic pointer to bullet object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::operator delete(void * ptr) +{ + if (ptr) { + ((BulletClass *)ptr)->IsActive = false; + } + Bullets.Free((BulletClass *)ptr); +} + + +/*********************************************************************************************** + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * * + * This function will determine the cell occupation list and return a pointer to it. Most * + * bullets are small and the list is usually short, but on occasion, it can be a list that * + * rivals the size of regular vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet * + * is over. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 01/05/1995 JLB : Handles projectiles with altitude. * + *=============================================================================================*/ +short const * BulletClass::Occupy_List(bool) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Super-gigundo units use the >= 64 coord spillage list logic. + */ + if (Class->IsGigundo) { + static short _list[] = { + -1, 0, 1, + MAP_CELL_W*1-1, MAP_CELL_W*1, MAP_CELL_W*1+1, + -MAP_CELL_W*1-1, -MAP_CELL_W*1, -MAP_CELL_W*1+1, + MAP_CELL_W*2-1, MAP_CELL_W*2, MAP_CELL_W*2+1, + -MAP_CELL_W*2-1, -MAP_CELL_W*2, -MAP_CELL_W*2+1, + -MAP_CELL_W*3-1, -MAP_CELL_W*3, -MAP_CELL_W*3+1, + REFRESH_EOL + }; + return(_list); +// return(Coord_Spillage_List(Coord, 64)); + } + + /* + ** Flying units need a special adjustment to the spillage list to take into account + ** that the bullet imagery and the shadow are widely separated. + */ + if (Height > 0) { + static short _list[25]; + const short * ptr = Coord_Spillage_List(Coord, 5); + int index = 0; + CELL cell1 = Coord_Cell(Coord); + + while (ptr[index] != REFRESH_EOL) { + _list[index] = ptr[index]; + index++; + } + + COORDINATE coord = Coord_Move(Coord, DIR_N, Height); + CELL cell2 = Coord_Cell(coord); + ptr = Coord_Spillage_List(coord, 5); + while (*ptr != REFRESH_EOL) { + _list[index++] = *ptr + (cell2 - cell1); + ptr++; + } + _list[index] = REFRESH_EOL; + return(_list); + } + + return(Coord_Spillage_List(Coord, 10)); +} + + +/*********************************************************************************************** + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * * + * This routine marks the objects under the bullet so that they will * + * be redrawn. This is necessary as the bullet moves -- objects under * + * its path need to be restored. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Mark(MarkType mark) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (!Class->IsInvisible) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::AI -- Logic processing for bullet. * + * * + * This routine will perform all logic (flight) logic on the bullet. * + * Primarily this is motion, fuse tracking, and detonation logic. Call * + * this routine no more than once per bullet per game tick. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::AI(void) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + COORDINATE coord; + + ObjectClass::AI(); + + if (!IsActive) return; + + /* + ** Ballistic objects are handled here. + */ + bool forced = false; // Forced explosion. + if ((Class->IsArcing || Class->IsDropping) && !IsFalling) { + forced = true; + } + + /* + ** Homing projectiles constantly change facing to face toward the target but + ** they only do so every other game frame (improves game speed and makes + ** missiles not so deadly). + */ + if ((Frame & 0x01) && Class->ROT != 0 && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom))); + } + + /* + ** Move the projectile forward according to its speed + ** and direction. + */ + coord = Coord; + if (Class->IsFlameEquipped) { + if (IsToAnimate) { + if (stricmp(Class->GraphicName, "FB1") == 0) { + new AnimClass(ANIM_FBALL_FADE, coord, 1); + } else { + new AnimClass(ANIM_SMOKE_PUFF, coord, 1); + } + } + IsToAnimate = !IsToAnimate; + } + + /* + ** Handle any body rotation at this time. This process must + ** occur every game fame in order to achieve smooth rotation. + */ + if (PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Rotation_Adjust(Class->ROT); + } + switch (Physics(coord, PrimaryFacing)) { + /* + ** When a projectile reaches the edge of the world, it + ** vanishes from existence -- presumed to explode off + ** map. + */ + case IMPACT_EDGE: + Mark(); + if (Payback != NULL && Class->Type == BULLET_GPS_SATELLITE) { + if (Payback->House == PlayerPtr) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + Map.RadarClass::Flag_To_Redraw(true); + } + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (Payback != NULL && Class->Type == BULLET_NUKE_UP && Payback->House->Control.TechLevel <= 10) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(Payback->House->NukeDest), Payback, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(Payback->House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(Payback->House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + } +#endif + delete this; + break; + + default: + case IMPACT_NONE: + + /* + ** The projectile has moved. Check its fuse. If detonation + ** is signaled, then do so. Otherwise, just move. + */ + case IMPACT_NORMAL: + Mark(); +// if(Class->Type == BULLET_NUKE_DOWN) { +// Render(true); +// } + if (Class->Type == BULLET_NUKE_UP) { + if (Payback != NULL) { + if (Distance(Payback->As_Target()) > 0x0C00) { + delete this; + return; + } + } + } + Coord = coord; + + /* + ** See if the bullet should be forced to explode now in spite of what + ** the fuse would otherwise indicate. Maybe the bullet hit a wall? + */ + if (!forced) { + forced = Is_Forced_To_Explode(Coord); + } + + /* + ** If the bullet is not to explode, then perform normal flight + ** maintenance (usually nothing). Otherwise, explode and then + ** delete the bullet. + */ + if (!forced && (Class->IsDropping || !Fuse_Checkup(Coord))) { + /* + ** Certain projectiles lose strength when they travel. + */ + if (Class->IsDegenerate && Strength > 5) { + Strength--; + } + + } else { + Bullet_Explodes(forced); + delete this; + } + break; + } + +} + + +/*********************************************************************************************** + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * * + * Use this routine to fetch a shape number to use for this bullet object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when drawing this bullet. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int BulletClass::Shape_Number(void) const +{ + int shapenum = 0; + + if (!Class->IsFaceless) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing)]; + } + + /* + ** For tumbling projectiles, fetch offset stage. + */ + if (Class->Tumble > 0) { + shapenum += (long)Frame % Class->Tumble; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * * + * This routine displays the bullet visual at the location specified. * + * * + * INPUT: x,y -- The center coordinate to render the bullet at. * + * * + * window -- The window to clip to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window clipping parameter. * + * 01/08/1995 JLB : Handles translucent colors if necessary. * + *=============================================================================================*/ +void BulletClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Certain projectiles aren't visible. This includes small bullets (which are actually + ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile). + */ + if (Class->IsInvisible) return; + + /* + ** If there is no shape loaded for this object, then + ** it obviously can't be rendered -- just bail. + */ + void const * shapeptr = Get_Image_Data(); + if (shapeptr == NULL) return; + + /* + ** Get the basic shape number for this projectile. + */ + int shapenum = Shape_Number(); + + /* + ** For flying projectiles, draw the shadow and adjust the actual projectile body + ** render position. + */ + if (Height > 0 && Class->IsShadow) { + + if (Class->IsParachuted) { + CC_Draw_Shape(AnimTypeClass::As_Reference(ANIM_PARA_BOMB).Get_Image_Data(), 1, x+Lepton_To_Pixel(Height/2), y+10, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } else { + CC_Draw_Shape(shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } + y -= Lepton_To_Pixel(Height); + } + + /* + ** Draw the main body of the projectile. + */ + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Class->IsTranslucent) { + flags = SHAPE_GHOST; + } + if (Class->IsSubSurface) { + CC_Draw_Shape(shapeptr, shapenum, x, y, window, flags|SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::FadingShade); + } else { + CC_Draw_Shape(shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * * + * This routine will zero out the bullet tracking list and object array in preparation for * + * the start of a new scenario. All bullets cease to exists after this function is * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Init(void) +{ + Bullets.Free_All(); +} + + +/*********************************************************************************************** + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * * + * When an object is removed from the game system, it must be removed all targeting and * + * tracking systems as well. This routine is used to remove the specified object from the * + * bullet. If the object isn't part of this bullet's tracking system, then no action is * + * performed. * + * * + * INPUT: target -- The target to remove from this tracking system. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Detach(TARGET target, bool all) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + ObjectClass * obj = As_Object(target); + if (Payback != NULL && obj == Payback) { + + /* + ** If we're being called as a result of the dog that fired us being put + ** in limbo, then don't detach. If for any other reason, detach. + */ + if (Payback->What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)Payback)->Class->IsDog) { + Payback = 0; + } + } + + if (all && target == TarCom) { + TarCom = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * * + * This routine is used to take a bullet object that is in limbo and transition it to the * + * game system. A bullet object so transitioned, will be drawn and logic processing * + * performed. In effect, it comes into existence. * + * * + * INPUT: coord -- The location where the bullet object is to appear. * + * * + * dir -- The initial facing for the bullet object. * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Try to unlimbo the bullet as far as the base class is concerned. Use the already + ** set direction and strength if the "punt" values were passed in. This allows a bullet + ** to be setup prior to being launched. + */ + if (!Class->IsHigh) { + Height = 0; + } + if (ObjectClass::Unlimbo(coord)) { + Map.Remove(this, In_Which_Layer()); + + COORDINATE tcoord = As_Coord(TarCom); + + /* + ** Homing projectiles (missiles) do NOT override facing. They just fire in the + ** direction specified and let the chips fall where they may. + */ + if (Class->ROT == 0 && !Class->IsDropping) { + dir = Direction(tcoord); + } + + /* + ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever + ** certain weapons are trained upon targets they were never designed to attack. Example: when + ** turrets or anti-tank missiles are fired at infantry. Indirect + ** fire is inherently inaccurate. + */ + if (IsInaccurate || Class->IsInaccurate || + ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Warhead == WARHEAD_AP || Class->IsFueled))) { + + /* + ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard + ** Circular Error of Probability (CEP) algorithm. High speed projectiles usually + ** just overshoot the target by extending the straight line flight. + */ + if (/*Class->ROT != 0 ||*/ Class->IsArcing) { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.HomingScatter); + scatterdist = max(scatterdist, 0); + + dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF); + tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist)); + } else { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.BallisticScatter); + scatterdist = max(scatterdist, 0); + tcoord = Coord_Move(tcoord, dir, Random_Pick(0, scatterdist)); + } + } + + /* + ** For very fast and invisible projectiles, just make the projectile exist at the target + ** location and dispense with the actual flight. + */ + if (MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) { + Coord = tcoord; + } + + /* + ** Set the range equal to either the class defined range or the calculated + ** number of game frames it would take for the projectile to reach the target. + */ + int range = 0xFF; + if (!Class->IsDropping) { + range = (::Distance(tcoord, Coord) / MaxSpeed) + 4; + } + + /* + ** Projectile speed is usually the default value for that projectile, but + ** certain projectiles alter speed according to the distance to the + ** target. + */ + int speed = MaxSpeed; + if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE; + if (Class->IsArcing) { + speed = MaxSpeed + (Distance(tcoord) / 32); + + /* + ** Set minimum speed (i.e., distance) for arcing projectiles. + */ + speed = max(speed, 25); + } + if (!Class->IsDropping) { + Fly_Speed(255, (MPHType)speed); + } + + /* + ** Arm the fuse. + */ + Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming)); + + /* + ** Projectiles that make a ballistic flight to impact point must determine a + ** vertical component for the projectile launch. This is crudely simulated + ** by biasing ground speed according to target distance and then giving + ** enough vertical velocity to keep the projectile airborne for the + ** desired amount of time. The mathematically correct solution would be to + ** calculate launch angle (given fixed projectile velocity) and then derive + ** the vertical and horizontal components. That solution would require a + ** square root and an arcsine lookup table. OUCH! + */ + Riser = 0; + if (Class->IsArcing) { + IsFalling = true; + Height = 1; + Riser = ((Distance(tcoord)/2) / (speed+1)) * Rule.Gravity; + Riser = max(Riser, 10); + } + if (Class->IsDropping) { + IsFalling = true; + Height = FLIGHT_LEVEL; +// Height = Pixel_To_Lepton(24); + Riser = 0; + if (Class->IsParachuted) { + AnimClass * anim = new AnimClass(ANIM_PARA_BOMB, Target_Coord()); +// AnimClass * anim = new AnimClass(ANIM_PARACHUTE, Target_Coord()); + if (anim) { + anim->Attach_To(this); + } + } + } + Map.Submit(this, In_Which_Layer()); + + PrimaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that should be used when firing at the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Target_Coord(void) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Coord)); +} + + +/*********************************************************************************************** + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * * + * This will return the coordinate to use when sorting this bullet in the display list. * + * Typically, this only occurs for bullets in the ground layer. Since bullets are to be * + * seen a bit more than the normal sorting order would otherwise imply, bias the sort * + * value such that bullets will tend to be drawn on top of the objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting this bullet in the display list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Move(Coord, DIR_S, CELL_LEPTON_H/2)); +} + + +/*********************************************************************************************** + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * * + * This examines the bullet to determine what rendering layer it should be in. The * + * normal logic applies unless this is a torpedo. A torpedo is always in the surface * + * layer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the render layer that this bullet should reside in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +LayerType BulletClass::In_Which_Layer(void) const +{ + if (Class->IsSubSurface) { + return(LAYER_SURFACE); + } + return(ObjectClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * * + * This routine will examine the bullet and where it is travelling in order to determine * + * if it should prematurely explode. Typical of this would be when a bullet hits a wall * + * or a torpedo hits a ship -- regardless of where the projectile was originally aimed. * + * * + * INPUT: coord -- The new coordinate to place the bullet at presuming it is forced to * + * explode and a modification of the bullet's coordinate is needed. * + * Otherwise, the coordinate is not modified. * + * * + * OUTPUT: bool; Should the bullet explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Is_Forced_To_Explode(COORDINATE & coord) const +{ + coord = Coord; + CellClass const * cellptr = &Map[coord]; + + /* + ** Check for impact on a wall or other high obstacle. + */ + if (!Class->IsHigh && cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) { + coord = Cell_Coord(Coord_Cell(coord)); + return(true); + } + + /* + ** Check to make sure that underwater projectiles (torpedoes) will not + ** travel in anything but water. + */ + if (Class->IsSubSurface) { + int d = ::Distance(Coord_Fraction(coord), XY_Coord(CELL_LEPTON_W/2, CELL_LEPTON_W/2)); + if (cellptr->Land_Type() != LAND_WATER || (d < CELL_LEPTON_W/3 && cellptr->Cell_Techno() != NULL && cellptr->Cell_Techno() != Payback)) { + + /* + ** Force explosion to be at center of techno object if one is present. + */ + if (cellptr->Cell_Techno() != NULL) { + coord = cellptr->Cell_Techno()->Target_Coord(); + } + + /* + ** However, if the torpedo was blocked by a bridge, then force the + ** torpedo to explode on top of that bridge cell. + */ + if (cellptr->Is_Bridge_Here()) { + coord = Coord_Snap(coord); + } + + return(true); + } + } + + /* + ** Bullets are generally more effective when they are fired at aircraft. + */ + if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) { + return(true); + } + + /* + ** No reason for forced explosion was detected, so return 'false' to + ** indicate that no forced explosion is required. + */ + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * * + * This handles the exploding bullet action. It will generate the animation and the * + * damage as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The bullet should be deleted after this routine is called. * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Bullet_Explodes(bool forced) +{ + /* + ** When the target is reached, explode and do the damage + ** required of it. For homing objects, don't force the explosion to + ** match the target position. Non-homing projectiles adjust position so + ** that they hit the target. This compensates for the error in line of + ** flight logic. + */ + if ( (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) || + (!forced && !Class->IsArcing && Class->ROT == 0 && Fuse_Target())) { + Coord = Fuse_Target(); + } + + /* + ** Non-aircraft targets apply damage to the ground. + */ + if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) { + Explosion_Damage(Coord, Strength, Payback, Warhead); + if (!IsActive) return; + + } else { + + /* + ** Special damage apply for SAM missiles. This is the only way that missile + ** damage affects the aircraft target. + */ + if (Distance(TarCom) < 0x0080) { + AircraftClass * object = As_Aircraft(TarCom); + + int str = Strength; + if (object) object->Take_Damage(str, 0, Warhead, Payback); + } + } + + /* + ** For projectiles that are invisible while travelling toward the target, + ** allow scatter effect for the impact animation. + */ + if (Class->IsInvisible) { + Coord = Coord_Scatter(Coord, 0x0020); + } + + /* + ** Fetch the land type that the explosion will be upon. Special case for + ** flying aircraft targets, their land type will be LAND_NONE. + */ + CellClass const * cellptr = &Map[Coord]; + LandType land = cellptr->Land_Type(); + if (Is_Target_Aircraft(TarCom) && As_Aircraft(TarCom)->In_Which_Layer() == LAYER_TOP) { + land = LAND_NONE; + } + + AnimType anim = Combat_Anim(Strength, Warhead, land); + + /* + ** If it's a water explosion that's going to play, don't play it + ** if its cell is the same as the center cell of the target ship. + */ + if (anim >= ANIM_WATER_EXP1 && anim <= ANIM_WATER_EXP3 && Is_Target_Vessel(TarCom)) { + if (Coord_Cell(Coord) == Coord_Cell(As_Vessel(TarCom)->Center_Coord())) { + anim = (AnimType) (ANIM_VEH_HIT1 + (anim - ANIM_WATER_EXP1)); + } + } + + if (anim != ANIM_NONE) { + AnimClass * aptr = new AnimClass(anim, Coord); + /* + ** Special case trap: if they're making the nuclear explosion, + ** and no anim is available, force the nuclear damage anyway + ** because nuke damage is done in the middle of the animation + ** and if there's no animation, there won't be any damage. + */ + if (!aptr && anim == ANIM_ATOM_BLAST) { + HousesType house = HOUSE_NONE; + if (Payback) { + house = Payback->House->Class->House; + } + AnimClass::Do_Atom_Damage(house, Coord_Cell(Coord)); + } + } + +// if (Payback && Payback->House == PlayerPtr && stricmp(Class->Name(), "GPSSATELLITE") == 0) { + if (Payback && Class->Type == BULLET_GPS_SATELLITE) { + if (Payback->House == PlayerPtr) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + Map.RadarClass::Flag_To_Redraw(true); + } +// Sound_Effect(VOC_SATTACT2); + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +} diff --git a/CODE/BULLET.H b/CODE/BULLET.H new file mode 100644 index 0000000..7ac09f6 --- /dev/null +++ b/CODE/BULLET.H @@ -0,0 +1,149 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/BULLET.H 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BULLET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BULLET_H +#define BULLET_H + +#include "object.h" +#include "fly.h" +#include "fuse.h" + + +class BulletClass : public ObjectClass, + public FlyClass, + public FuseClass +{ + public: + + /* + ** This specifies exactly what kind of bullet this is. All of the static attributes + ** for this bullet is located in the BulletTypeClass pointed to by this variable. + */ + CCPtr Class; + + private: + /* + ** Records who sent this "present" so that an appropriate "thank you" can + ** be returned. + */ + TechnoClass * Payback; + + /* + ** This is the facing that the projectile is travelling. + */ + FacingClass PrimaryFacing; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BulletClass(BulletType id, TARGET target, TechnoClass * Payback, int strength, WarheadType warhead, int speed); +#ifdef FIXIT_MULTI_SAVE + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x), PrimaryFacing(x) {}; +#else + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x) {}; +#endif + virtual ~BulletClass(void); + operator BulletType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + bool Is_Forced_To_Explode(COORDINATE & coord) const; + void Bullet_Explodes(bool forced); + int Shape_Number(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual void Assign_Target(TARGET target) {TarCom = target;}; + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Detach(TARGET target, bool all); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void AI(void); + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const {return Occupy_List(false);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** If this bullet is forced to be inaccurate because of some outside means. A tank + ** firing while moving is a good example. + */ + unsigned IsInaccurate:1; + + private: + // Crude animation flag. + unsigned IsToAnimate:1; + + /* + ** Is this missile allowed to come in from out of bounds? + */ + unsigned IsLocked:1; + + /* + ** This is the target of the projectile. It is especially significant for those projectiles + ** that home in on a target. + */ + TARGET TarCom; + + /* + ** The speed of this projectile. + */ + int MaxSpeed; + + /* + ** The warhead of this projectile. + */ + WarheadType Warhead; +}; + +#endif + diff --git a/CODE/C&CZERO.PJT b/CODE/C&CZERO.PJT new file mode 100644 index 0000000..5200c7c --- /dev/null +++ b/CODE/C&CZERO.PJT @@ -0,0 +1,410 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[Files] +c:\projects\c&c\code\aadata.cpp +c:\projects\c&czero\code\aadata.cpp +c:\projects\c&czero\code\abstract.cpp +c:\projects\c&czero\code\abstract.h +c:\projects\c&czero\code\adata.cpp +c:\projects\c&czero\code\ADPCM.CPP +c:\projects\c&czero\code\aircraft.cpp +c:\projects\c&czero\code\aircraft.h +c:\projects\c&czero\code\alloc.cpp +c:\projects\c&czero\code\anim.cpp +c:\projects\c&czero\code\anim.h +c:\projects\c&czero\code\audio.cpp +c:\projects\c&czero\code\audio.h +c:\projects\c&czero\code\base.cpp +c:\projects\c&czero\code\base.h +c:\projects\c&czero\code\bbdata.cpp +c:\projects\c&czero\code\bdata.cpp +c:\projects\c&czero\code\BFIOFILE.CPP +c:\projects\c&czero\code\BFIOFILE.H +c:\projects\c&czero\code\building.cpp +c:\projects\c&czero\code\building.h +c:\projects\c&czero\code\bullet.cpp +c:\projects\c&czero\code\bullet.h +c:\projects\c&czero\code\cargo.cpp +c:\projects\c&czero\code\cargo.h +c:\projects\c&czero\code\CARRY.CPP +c:\projects\c&czero\code\CARRY.H +c:\projects\c&czero\code\ccfile.cpp +c:\projects\c&czero\code\ccfile.h +c:\projects\c&czero\code\cdata.cpp +c:\projects\c&czero\code\cdfile.cpp +c:\projects\c&czero\code\cdfile.h +c:\projects\c&czero\code\cell.cpp +c:\projects\c&czero\code\cell.h +c:\projects\c&czero\code\checkbox.cpp +c:\projects\c&czero\code\checkbox.h +c:\projects\c&czero\code\cheklist.cpp +c:\projects\c&czero\code\cheklist.h +c:\projects\c&czero\code\colrlist.cpp +c:\projects\c&czero\code\colrlist.h +c:\projects\c&czero\code\combat.cpp +c:\projects\c&czero\code\combuf.cpp +c:\projects\c&czero\code\combuf.h +c:\projects\c&czero\code\compat.h +c:\projects\c&czero\code\comqueue.cpp +c:\projects\c&czero\code\comqueue.h +c:\projects\c&czero\code\confdlg.cpp +c:\projects\c&czero\code\confdlg.h +c:\projects\c&czero\code\connect.cpp +c:\projects\c&czero\code\connect.h +c:\projects\c&czero\code\connmgr.h +c:\projects\c&czero\code\conquer.cpp +c:\projects\c&czero\code\conquer.h +c:\projects\c&czero\code\const.cpp +c:\projects\c&czero\code\control.cpp +c:\projects\c&czero\code\control.h +c:\projects\c&czero\code\coord.cpp +c:\projects\c&czero\code\crc.cpp +c:\projects\c&czero\code\crc.h +c:\projects\c&czero\code\credits.cpp +c:\projects\c&czero\code\credits.h +c:\projects\c&czero\code\crew.cpp +c:\projects\c&czero\code\crew.h +c:\projects\c&czero\code\cwstub.c +c:\projects\c&czero\code\debug.cpp +c:\projects\c&czero\code\debug.h +c:\projects\c&czero\code\defines.h +c:\projects\c&czero\code\descdlg.cpp +c:\projects\c&czero\code\descdlg.h +c:\projects\c&czero\code\dial8.cpp +c:\projects\c&czero\code\dial8.h +c:\projects\c&czero\code\dialog.cpp +c:\projects\c&czero\code\display.cpp +c:\projects\c&czero\code\display.h +c:\projects\c&czero\code\door.cpp +c:\projects\c&czero\code\door.h +c:\projects\c&czero\code\dpmi.cpp +c:\projects\c&czero\code\dpmi.h +c:\projects\c&czero\code\drive.cpp +c:\projects\c&czero\code\drive.h +c:\projects\c&czero\code\drop.cpp +c:\projects\c&czero\code\drop.h +c:\projects\c&czero\code\DTABLE.CPP +c:\projects\c&czero\code\edit.cpp +c:\projects\c&czero\code\edit.h +c:\projects\c&czero\code\ending.cpp +c:\projects\c&czero\code\ending.h +c:\projects\c&czero\code\event.cpp +c:\projects\c&czero\code\event.h +c:\projects\c&czero\code\expand.cpp +c:\projects\c&czero\code\externs.h +c:\projects\c&czero\code\FACE.CPP +c:\projects\c&czero\code\FACE.H +c:\projects\c&czero\code\facing.cpp +c:\projects\c&czero\code\facing.h +c:\projects\c&czero\code\factory.cpp +c:\projects\c&czero\code\factory.h +c:\projects\c&czero\code\findpath.cpp +c:\projects\c&czero\code\flasher.cpp +c:\projects\c&czero\code\flasher.h +c:\projects\c&czero\code\fly.cpp +c:\projects\c&czero\code\fly.h +c:\projects\c&czero\code\foot.cpp +c:\projects\c&czero\code\foot.h +c:\projects\c&czero\code\ftimer.h +c:\projects\c&czero\code\function.h +c:\projects\c&czero\code\fuse.cpp +c:\projects\c&czero\code\fuse.h +c:\projects\c&czero\code\gadget.cpp +c:\projects\c&czero\code\gadget.h +c:\projects\c&czero\code\gamedlg.cpp +c:\projects\c&czero\code\gamedlg.h +c:\projects\c&czero\code\gauge.cpp +c:\projects\c&czero\code\gauge.h +c:\projects\c&czero\code\globals.cpp +c:\projects\c&czero\code\goptions.cpp +c:\projects\c&czero\code\goptions.h +c:\projects\c&czero\code\gscreen.cpp +c:\projects\c&czero\code\gscreen.h +c:\projects\c&czero\code\hdata.cpp +c:\projects\c&czero\code\heap.cpp +c:\projects\c&czero\code\heap.h +c:\projects\c&czero\code\help.cpp +c:\projects\c&czero\code\help.h +c:\projects\c&czero\code\house.cpp +c:\projects\c&czero\code\house.h +c:\projects\c&czero\code\HSV.CPP +c:\projects\c&czero\code\HSV.H +c:\projects\c&czero\code\idata.cpp +c:\projects\c&czero\code\infantry.cpp +c:\projects\c&czero\code\infantry.h +c:\projects\c&czero\code\ini.cpp +c:\projects\c&czero\code\inibin.cpp +c:\projects\c&czero\code\inicode.cpp +c:\projects\c&czero\code\init.cpp +c:\projects\c&czero\code\intro.cpp +c:\projects\c&czero\code\intro.h +c:\projects\c&czero\code\iomap.cpp +c:\projects\c&czero\code\ioobj.cpp +c:\projects\c&czero\code\ipx.cpp +c:\projects\c&czero\code\ipx.h +c:\projects\c&czero\code\ipxaddr.cpp +c:\projects\c&czero\code\ipxaddr.h +c:\projects\c&czero\code\ipxconn.cpp +c:\projects\c&czero\code\ipxconn.h +c:\projects\c&czero\code\ipxgconn.cpp +c:\projects\c&czero\code\ipxgconn.h +c:\projects\c&czero\code\ipxmgr.cpp +c:\projects\c&czero\code\ipxmgr.h +c:\projects\c&czero\code\itable.cpp +c:\projects\c&czero\code\jshell.cpp +c:\projects\c&czero\code\jshell.h +c:\projects\c&czero\code\keyframe.cpp +c:\projects\c&czero\code\layer.cpp +c:\projects\c&czero\code\layer.h +c:\projects\c&czero\code\lcwuncmp.cpp +c:\projects\c&czero\code\led.h +c:\projects\c&czero\code\link.cpp +c:\projects\c&czero\code\link.h +c:\projects\c&czero\code\lint.h +c:\projects\c&czero\code\list.cpp +c:\projects\c&czero\code\list.h +c:\projects\c&czero\code\loaddlg.cpp +c:\projects\c&czero\code\loaddlg.h +c:\projects\c&czero\code\logic.cpp +c:\projects\c&czero\code\logic.h +c:\projects\c&czero\code\map.cpp +c:\projects\c&czero\code\map.h +c:\projects\c&czero\code\mapeddlg.cpp +c:\projects\c&czero\code\mapedit.cpp +c:\projects\c&czero\code\mapedit.h +c:\projects\c&czero\code\mapedplc.cpp +c:\projects\c&czero\code\mapedsel.cpp +c:\projects\c&czero\code\mapedtm.cpp +c:\projects\c&czero\code\mapsel.cpp +c:\projects\c&czero\code\menus.cpp +c:\projects\c&czero\code\message.h +c:\projects\c&czero\code\mission.cpp +c:\projects\c&czero\code\mission.h +c:\projects\c&czero\code\mixfile.cpp +c:\projects\c&czero\code\mixfile.h +c:\projects\c&czero\code\monoc.cpp +c:\projects\c&czero\code\monoc.h +c:\projects\c&czero\code\mouse.cpp +c:\projects\c&czero\code\mouse.h +c:\projects\c&czero\code\mplayer.cpp +c:\projects\c&czero\code\msgbox.cpp +c:\projects\c&czero\code\msgbox.h +c:\projects\c&czero\code\msglist.cpp +c:\projects\c&czero\code\msglist.h +c:\projects\c&czero\code\netdlg.cpp +c:\projects\c&czero\code\nullconn.cpp +c:\projects\c&czero\code\nullconn.h +c:\projects\c&czero\code\nulldlg.cpp +c:\projects\c&czero\code\nullmgr.cpp +c:\projects\c&czero\code\nullmgr.h +c:\projects\c&czero\code\object.cpp +c:\projects\c&czero\code\object.h +c:\projects\c&czero\code\odata.cpp +c:\projects\c&czero\code\options.cpp +c:\projects\c&czero\code\options.h +c:\projects\c&czero\code\overlay.cpp +c:\projects\c&czero\code\overlay.h +c:\projects\c&czero\code\PALETTE.CPP +c:\projects\c&czero\code\PALETTE.H +c:\projects\c&czero\code\phone.h +c:\projects\c&czero\code\power.cpp +c:\projects\c&czero\code\power.h +c:\projects\c&czero\code\profile.cpp +c:\projects\c&czero\code\queue.cpp +c:\projects\c&czero\code\queue.h +c:\projects\c&czero\code\radar.cpp +c:\projects\c&czero\code\radar.h +c:\projects\c&czero\code\radio.cpp +c:\projects\c&czero\code\radio.h +c:\projects\c&czero\code\rand.cpp +c:\projects\c&czero\code\RANDOM.CPP +c:\projects\c&czero\code\RANDOM.H +c:\projects\c&czero\code\rawfile.cpp +c:\projects\c&czero\code\rawfile.h +c:\projects\c&czero\code\region.h +c:\projects\c&czero\code\reinf.cpp +c:\projects\c&czero\code\RGB.CPP +c:\projects\c&czero\code\RGB.H +c:\projects\c&czero\code\ROTBMP.CPP +c:\projects\c&czero\code\ROTBMP.H +c:\projects\c&czero\code\savedlg.h +c:\projects\c&czero\code\saveload.cpp +c:\projects\c&czero\code\scenario.cpp +c:\projects\c&czero\code\SCENARIO.H +c:\projects\c&czero\code\score.cpp +c:\projects\c&czero\code\score.h +c:\projects\c&czero\code\screen.h +c:\projects\c&czero\code\scroll.cpp +c:\projects\c&czero\code\scroll.h +c:\projects\c&czero\code\sdata.cpp +c:\projects\c&czero\code\SESSION.CPP +c:\projects\c&czero\code\SESSION.H +c:\projects\c&czero\code\shapebtn.cpp +c:\projects\c&czero\code\shapebtn.h +c:\projects\c&czero\code\sidebar.cpp +c:\projects\c&czero\code\sidebar.h +c:\projects\c&czero\code\slider.cpp +c:\projects\c&czero\code\slider.h +c:\projects\c&czero\code\smudge.cpp +c:\projects\c&czero\code\smudge.h +c:\projects\c&czero\code\sounddlg.cpp +c:\projects\c&czero\code\sounddlg.h +c:\projects\c&czero\code\special.cpp +c:\projects\c&czero\code\special.h +c:\projects\c&czero\code\SPRITE.CPP +c:\projects\c&czero\code\stage.h +c:\projects\c&czero\code\startup.cpp +c:\projects\c&czero\code\super.cpp +c:\projects\c&czero\code\super.h +c:\projects\c&czero\code\tab.cpp +c:\projects\c&czero\code\tab.h +c:\projects\c&czero\code\TACTION.CPP +c:\projects\c&czero\code\TACTION.H +c:\projects\c&czero\code\target.cpp +c:\projects\c&czero\code\target.h +c:\projects\c&czero\code\tdata.cpp +c:\projects\c&czero\code\team.cpp +c:\projects\c&czero\code\team.h +c:\projects\c&czero\code\teamtype.cpp +c:\projects\c&czero\code\teamtype.h +c:\projects\c&czero\code\techno.cpp +c:\projects\c&czero\code\techno.h +c:\projects\c&czero\code\template.cpp +c:\projects\c&czero\code\template.h +c:\projects\c&czero\code\terrain.cpp +c:\projects\c&czero\code\terrain.h +c:\projects\c&czero\code\TEVENT.CPP +c:\projects\c&czero\code\TEVENT.H +c:\projects\c&czero\code\textbtn.cpp +c:\projects\c&czero\code\textbtn.h +c:\projects\c&czero\code\theme.cpp +c:\projects\c&czero\code\theme.h +c:\projects\c&czero\code\toggle.cpp +c:\projects\c&czero\code\toggle.h +c:\projects\c&czero\code\trigger.cpp +c:\projects\c&czero\code\trigger.h +c:\projects\c&czero\code\txtlabel.cpp +c:\projects\c&czero\code\txtlabel.h +c:\projects\c&czero\code\type.h +c:\projects\c&czero\code\udata.cpp +c:\projects\c&czero\code\unit.cpp +c:\projects\c&czero\code\unit.h +c:\projects\c&czero\code\vdata.cpp +c:\projects\c&czero\code\vector.cpp +c:\projects\c&czero\code\vector.h +c:\projects\c&czero\code\VERSION.CPP +c:\projects\c&czero\code\VERSION.H +c:\projects\c&czero\code\vessel.cpp +c:\projects\c&czero\code\vessel.h +c:\projects\c&czero\code\visudlg.cpp +c:\projects\c&czero\code\visudlg.h +c:\projects\c&czero\code\watcom.h +c:\projects\c&czero\code\wwalloc.h +c:\projects\c&czero\code\wwfile.h +C:\projects\c&czero\code\2KEYFRAM.CPP +C:\projects\c&czero\code\BLOWFISH.CPP +C:\projects\c&czero\code\BLOWFISH.H +C:\projects\c&czero\code\FILEPCX.H +C:\projects\c&czero\code\INT.CPP +C:\projects\c&czero\code\INT.H +C:\projects\c&czero\code\interpal.cpp +C:\projects\c&czero\code\language.h +C:\projects\c&czero\code\MP.CPP +C:\projects\c&czero\code\MP.H +C:\projects\c&czero\code\RNG.H +C:\projects\c&czero\code\SHA.CPP +C:\projects\c&czero\code\SHA.H +C:\projects\c&czero\code\tarcom.cpp +C:\projects\c&czero\code\tcpip.h +C:\projects\c&czero\code\winstub.cpp +c:\projects\c&czero\code\BUFFERX.H +c:\projects\c&czero\code\ini.h +c:\projects\c&czero\code\listnode.h +c:\projects\c&czero\code\rules.cpp +c:\projects\c&czero\code\rules.h +c:\projects\c&czero\code\tarcom.h +c:\projects\c&czero\code\turret.cpp +c:\projects\c&czero\code\turret.h +c:\projects\c&czero\code\warhead.h +c:\projects\c&czero\code\weapon.h + +[State] +SysSetCwd='C:\projects\c&czero\code' +SrchSetFlags=0x000320aa +FileSortMode=0x0 +StateWindowFrame=37,16,923,511,0x62c9f5fa +_StateWindow=0,0,990,647,0x00100018,'C:\projects\c&czero\code\palette.cpp',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,1,10,'',12,255,48,0,7,243,252,253,247,249,247,93,1,400,0,246,252,248,244,247,15,15,15,15,0,0 +_StateBuffer='C:\projects\c&czero\code\palette.cpp',0x0400048e,93,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\jshell.h',0x0400048e,1,2,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\sprite.cpp',0x0400048e,35,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\wwfile.h',0x0400048e,21,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\win32lib\INCLUDE\RAWFILE.H',0x0400048e,1,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\ROTBMP.H',0x0400048e,9,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\rotbmp.cpp',0x0400048e,6,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\function.h',0x0400048e,157,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h',0x0400048e,265,26,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\anim.cpp',0x0400048e,682,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\makefile',0x0400048e,420,1,25,'5 9','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\crc.cpp',0x0400048e,1,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\PALETTE.H',0x0400048e,1,1,25,'4 7','',0x0,'' +_StateHistory=SEARCH,'::Mission_U','Force_','MOUSE_','jshell','WindowsTim','LINKFILE','game.dat','wwflat','wwlib','Bitmap' +_StateHistory=REPLACE,'building','bindex','vindex','iindex','AIRCRAFT','AircraftType','aircraft','aindex','_Scale_To_256','tech' +_StateHistory=XMACRO,'small','sort','SORT','sort','SORT','sort','SORT','sort','SORT','sort' +_StateHistory=FILELIST,'C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h','C:\projects\c&czero\code\function.h','C:\projects\c&czero\code\rotbmp.cpp','C:\projects\c&czero\code\ROTBMP.H','C:\projects\c&czero\code\jshell.h','C:\projects\c&czero\code\palette.cpp','C:\projects\c&czero\code\crc.cpp','C:\projects\c&czero\code\makefile','C:\projects\c&czero\code\PALETTE.H','C:\projects\c&czero\code\anim.cpp' +_StateHistory=EDITFILE,'palette.cpp','crc.cpp','makefile','palette.cpp','palette.h','makefile','anim.cpp','palette.cpp','function.h','jshell.h' +_StateHistory=DIRECTORY,'c:\\','c:\projects\c&c\code','e:\c&czero\code','e:','c:\projects\c&czero\code','c:','C:\PROJECTS\C&Czero\code','c:\projects\C&Czero\code' +_StateHistory=GOTOMARK,'2','1','2','1','2','3','1','2','3','1' +_StateHistory=OUTNAME,'tab.cpp','power.cpp','2txtprnt.asm','display.cpp','scroll.cpp','sidebar.cpp','power.cpp','help.cpp','tab.cpp','mapeddlg.cpp' +_StateHistory=GOTOLINE,'1665','410','93','375','590','231','246','616','384','686' +_StateHistory=WBLOCK,'c:\temp\face.cpp','c:\temp\face.h' +_StateHistory=FILTER,'sort' +_StateHistory=DOSHISTORY,'eset path','ndos','eset path','set path=%PATH%;c:\utils','ts *.cpp Interpolated','n','d:','exit','ts *.cpp Interpolated','exit' +_StateMark=MARK_POSITION,1,'C:\projects\c&czero\wwflat32\INCLUDE\gbuffer.h',265,25,0 +_StateMark=MARK_POSITION,1,'C:\projects\c&czero\code\jshell.h',1,1,0 + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +VCSProject='' +VCSProjectPath='' +VCSProjectLocalPath='' +_RestoreSysFlags=0x6249f5fa, 0xfffffffc + +[Compiler] +TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060 +BrowseSetFile='c:\projects\c&czero\code\c&czero.ptg' +TagSetFile='c:\projects\c&czero\code\c&czero.tag' +CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft Assembler', +CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p' +CompilerAddResponse='Borland C++', +CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p' +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'ftee watcom\binw\wmake %r.obj',209,'ftee m.bat',209,'ftee joemake',209,'g.bat -hansolo cheater -xm -editor',224,'_MSLinkErrorInfo','_MicrosoftErrorInfo','_NMakeErrorInfo','proj.err','c:\projects\c&czero\code' +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p' +CompilerAddResponse='Default Project', +CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft C', +CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','','' +CompilerAddResponse='Script', +CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p' +CompilerAddResponse='Zortech C++', +CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','','' +CompilerAddResponse='Microsoft C(NT-i386)', +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Microsoft C(NT-i386)','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Microsoft C(NT-i386)','.hpp' diff --git a/CODE/CARGO.CPP b/CODE/CARGO.CPP new file mode 100644 index 0000000..a88574b --- /dev/null +++ b/CODE/CARGO.CPP @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CARGO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CARGO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : 10/31/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CargoClass::Attach -- Add unit to cargo hold. * + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * * + * This routine is used to dump the current cargo value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void CargoClass::Debug_Dump(MonoClass * mono) const +{ + if (How_Many()) { + mono->Set_Cursor(63, 3); + mono->Printf("(%d)%04X", How_Many(), Attached_Object()); + } +} +#endif + + +/*********************************************************************************************** + * CargoClass::Attach -- Add unit to cargo hold. * + * * + * This routine will add the specified unit to the cargo hold. The * + * unit will chain to any existing units in the hold. The chaining is * + * in a LIFO order. * + * * + * INPUT: object-- Pointer to the object to attach to the cargo hold. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 10/31/94 JLB : Handles chained objects. * + *=============================================================================================*/ +void CargoClass::Attach(FootClass * object) +{ + /* + ** If there is no object, then no action is necessary. + */ + if (object == NULL) return; + + object->Limbo(); + + /* + ** Attach any existing cargo hold object to the end of the list as indicated by the + ** object pointer passed into this routine. This is necessary because several objects may + ** be attached at one time or several objects may be attached as a result of several calls + ** to this routine. Either case must be handled properly. + */ + ObjectClass * o = object->Next; + while (o != NULL) { + if (o->Next == (void*)NULL) break; + o = o->Next; + } + if (o != NULL) { + o->Next = CargoHold; + } else { + object->Next = CargoHold; + } + + /* + ** Finally, assign the object pointer as the first object attached to this cargo hold. + */ + CargoHold = object; + Quantity = 0; + object = CargoHold; + while (object != NULL) { + Quantity++; + object = (FootClass *)(ObjectClass *)object->Next; + } +} + + +/*********************************************************************************************** + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * * + * This routine will take a unit from the cargo hold and extract it. * + * The unit extracted is the last unit added to the hold. If there * + * is no unit in the hold or the occupant is not a unit, then NULL is * + * returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the unit that has been extracted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Detach_Object(void) +{ + TechnoClass * unit = Attached_Object(); + + if (unit != NULL) { + CargoHold = (FootClass *)(ObjectClass *)unit->Next; + unit->Next = 0; + Quantity--; + } + return((FootClass *)unit); +} + + +/*********************************************************************************************** + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * * + * This routine will return with a pointer to the attached unit if one * + * is present. One would need to know this if this is a transport * + * unit and it needs to unload. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the attached unit. If there is no * + * attached unit, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Attached_Object(void) const +{ + if (Is_Something_Attached()) { + return(CargoHold); + } + return(NULL); +} + + diff --git a/CODE/CARGO.H b/CODE/CARGO.H new file mode 100644 index 0000000..f6a078f --- /dev/null +++ b/CODE/CARGO.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CARGO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CARGO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CARGO_H +#define CARGO_H + +class FootClass; + +/**************************************************************************** +** This class handles the basic cargo logic. +*/ +class CargoClass { + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CargoClass(void) : Quantity(0), CargoHold(0) {}; + CargoClass(NoInitClass const & ) {}; + ~CargoClass(void) {CargoHold=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void) {}; + + int How_Many(void) const {return Quantity;}; + bool Is_Something_Attached(void) const {return (CargoHold != 0);}; + FootClass * Attached_Object(void) const; + FootClass * Detach_Object(void); + void Attach(FootClass * object); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + + /* + ** This is the number of objects attached to this cargo hold. For transporter + ** objects, they might contain more than one object. + */ + unsigned char Quantity; + + /* + ** This is the target value of any attached object. A value of zero indicates + ** that no object is attached. + */ + FootClass * CargoHold; +}; + +#endif + diff --git a/CODE/CARRY.CPP b/CODE/CARRY.CPP new file mode 100644 index 0000000..1535297 --- /dev/null +++ b/CODE/CARRY.CPP @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CARRY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CARRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : May 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * CarryoverClass::Create -- Creates a carried over object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * * + * This is the constructor for a carry over object. Such an object is used to record the * + * object that will be "carried over" into a new scenario at some future time. * + * * + * INPUT: techno -- Pointer to the object that will be carried over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +CarryoverClass::CarryoverClass(TechnoClass * techno) : + RTTI(RTTI_NONE), + Cell(0), + Strength(0), + House(HOUSE_NONE) +{ + if (techno) { + RTTI = techno->What_Am_I(); + + switch (RTTI) { + case RTTI_UNIT: + Type.Unit = ((UnitClass *)techno)->Class->Type; + break; + + case RTTI_BUILDING: + Type.Building = ((BuildingClass *)techno)->Class->Type; + break; + + case RTTI_INFANTRY: + Type.Infantry = ((InfantryClass *)techno)->Class->Type; + break; + + case RTTI_VESSEL: + Type.Vessel = ((VesselClass *)techno)->Class->Type; + break; + + default: + break; + } + + House = techno->Owner(); + Strength = techno->Strength; + Cell = Coord_Cell(techno->Coord); + } +} + + +/*********************************************************************************************** + * CarryoverClass::Create -- Creates a carried over object. * + * * + * Use this routine to convert a carried over object into an actual object that will be * + * placed on the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully created and placed on the map? * + * * + * WARNINGS: This routine might not place the object if the old map location was invalid * + * or there are other barriers to the object's creation and placement. * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +bool CarryoverClass::Create(void) const +{ + TechnoClass * techno = 0; + TechnoTypeClass const * ttype = 0; + + switch (RTTI) { + case RTTI_UNIT: + ttype = &UnitTypeClass::As_Reference(Type.Unit); + techno = new UnitClass(Type.Unit, House); + break; + + case RTTI_INFANTRY: + ttype = &InfantryTypeClass::As_Reference(Type.Infantry); + techno = new InfantryClass(Type.Infantry, House); + break; + + case RTTI_BUILDING: + ttype = &BuildingTypeClass::As_Reference(Type.Building); + techno = new BuildingClass(Type.Building, House); + break; + + case RTTI_VESSEL: + ttype = &VesselTypeClass::As_Reference(Type.Vessel); + techno = new VesselClass(Type.Vessel, House); + break; + } + + if (techno) { + bool oldscen = ScenarioInit; + techno->Strength = Strength; + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = 0; + } + techno->Unlimbo(Cell_Coord(Cell)); + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = oldscen; + } + } + + return(false); +} + diff --git a/CODE/CARRY.H b/CODE/CARRY.H new file mode 100644 index 0000000..fb045d7 --- /dev/null +++ b/CODE/CARRY.H @@ -0,0 +1,84 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CARRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CARRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CARRY_H +#define CARRY_H + +class CarryoverClass : public LinkClass { + public: + CarryoverClass(TechnoClass * techno = 0); + CarryoverClass(NoInitClass const & x) : LinkClass(x) {} + + bool Create(void) const; + + protected: + /* + ** What type of object this is. + */ + RTTIType RTTI; + + /* + ** This is the object type that is to be carried over. The exact nature of + ** this type depends on the RTTI value. Only certain object types are + ** recorded. + */ + union { + StructType Building; + UnitType Unit; + InfantryType Infantry; + VesselType Vessel; + } Type; + + /* + ** The location of the object. + */ + CELL Cell; + + /* + ** The strength of the object at the time is was recorded. + */ + int Strength; + + /* + ** This is the owner of the object. + */ + HousesType House; +}; + + +#endif diff --git a/CODE/CCDDE.CPP b/CODE/CCDDE.CPP new file mode 100644 index 0000000..06a69bb --- /dev/null +++ b/CODE/CCDDE.CPP @@ -0,0 +1,431 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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 - Red Alert * + * * + * File Name : CCDDE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DDE_Callback -- DDE server callback function * + * DDEServerClass::DDEServerClass -- class constructor * + * DDEServerClass::Enable -- Enables the DDE callback * + * DDEServerClass::Disable -- Disables the DDE callback * + * DDEServerClass::~DDEServerClass -- class destructor * + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include +#include "ccdde.h" +#include +#include + +DDEServerClass DDEServer; //Instance of the DDE Server class + +Instance_Class *DDE_Class = NULL; // pointer for client callback + // this *must* be called DDE_Class + +BOOL RA95AlreadyRunning = FALSE; //Was there an instance of Red Alert 95 already running when we started? + +/* +** Misc externs so we dont have to include FUNCTION.H +*/ +extern HWND MainWindow; +extern TimerClass GameTimer; +extern BOOL GameTimerInUse; +extern void WWDebugString (char *string); + + +/*********************************************************************************************** + * DDE_Callback -- DDE server callback function * + * * + * Just acts as a wrapper for the DDEServerClass callback function * + * * + * INPUT: ptr to data from client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:19PM ST : Created * + *=============================================================================================*/ +BOOL CALLBACK DDE_Callback (unsigned char *data, long length) +{ + return (DDEServer.Callback(data, length)); +} + + + + +/*********************************************************************************************** + * DDEServerClass::DDEServerClass -- class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::DDEServerClass(void) +{ + MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet + + //DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); + DDE_Class = new Instance_Class ("REDALERT", "WCHAT"); + + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + + if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ + RA95AlreadyRunning = TRUE; + }else{ + DDE_Class->Register_Server( DDE_Callback ); + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Enable -- Enables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Enable(void) +{ + if (!IsEnabled){ + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Disable -- Disables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Disable(void) +{ + if (IsEnabled){ + DDE_Class->Enable_Callback( FALSE ); + IsEnabled = FALSE; + } +} + + + + + + +/*********************************************************************************************** + * DDEServerClass::~DDEServerClass -- class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::~DDEServerClass(void) +{ + Delete_MPlayer_Game_Info(); + delete( DDE_Class ); +} + + + +/*********************************************************************************************** + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * * + * * + * * + * INPUT: data from DDE client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Data has length and type as first 2 ints * + * * + * HISTORY: * + * 6/8/96 3:21PM ST : Created * + *=============================================================================================*/ +BOOL DDEServerClass::Callback(unsigned char *data, long length) +{ + + /* + ** If the packet length < 0 then this is a special advisory packet + */ + if ( length<0 ) { + + switch( length ) { + + case DDE_ADVISE_CONNECT: + WWDebugString("RA95 - DDE advisory: client connect detected."); + return TRUE; + + case DDE_ADVISE_DISCONNECT: + WWDebugString("RA95 - DDE advisory: client disconnect detected."); + return TRUE; + + default: + WWDebugString("RA95 - DDE advisory: Unknown DDE advise type."); + return FALSE; + } + + }else{ + + /* + ** Packet must be at least the length of the packet type & size fields to be valid + */ + if (length < 2*sizeof(int)) { + WWDebugString ("RA95 - Received invalid packet."); + return (FALSE); + } + + /* + ** Find out what kind of packet this is and its length. + */ + int *packet_pointer = (int *)data; + int actual_length = ntohl(*packet_pointer++); + int packet_type = ntohl(*packet_pointer++); + + /* + ** Strip the ID int from the start of the packet + */ + data += 2*sizeof (int); + length -= 2*sizeof (int); + actual_length -= 2*sizeof (int); + + /* + ** Take the appropriate action for the packet type + */ + switch ( packet_type ){ + + /* + ** This is a packet with the info required for starting a new internet game. This is really + * just C&CSPAWN.INI sent from WChat instead of read from disk. + */ + case DDE_PACKET_START_MPLAYER_GAME: + WWDebugString("RA95 - Received start game packet."); + Delete_MPlayer_Game_Info(); + MPlayerGameInfo = new char [actual_length + 1]; + memcpy (MPlayerGameInfo, data, actual_length); + *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string + MPlayerGameInfoLength = actual_length; + LastHeartbeat = 0; + break; + + case DDE_TICKLE: + WWDebugString("RA95 - Received 'tickle' packet."); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + break; + + case DDE_PACKET_HEART_BEAT: + WWDebugString("RA95 - Received heart beat packet."); + if (GameTimerInUse){ + LastHeartbeat = GameTimer.Time(); + }else{ + LastHeartbeat = 0; + } + break; + + default: + WWDebugString("RA95 - Received unrecognised packet."); + break; + + } + } + + return (TRUE); + +} + + + +/*********************************************************************************************** + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to data in .INI file format * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:23PM ST : Created * + *=============================================================================================*/ +char *DDEServerClass::Get_MPlayer_Game_Info (void) +{ + return (MPlayerGameInfo); +} + + + +/*********************************************************************************************** + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:24PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Delete_MPlayer_Game_Info(void) +{ + if (MPlayerGameInfo){ + delete [] MPlayerGameInfo; + MPlayerGameInfo = NULL; + } +} + + + +/*********************************************************************************************** + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: time since heartbeat * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:05PM ST : Created * + *=============================================================================================*/ +int DDEServerClass::Time_Since_Heartbeat(void) +{ + return (GameTimer.Time() - LastHeartbeat); +} + + + + +/*********************************************************************************************** + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * * + * * + * INPUT: ptr to the data to send * + * length of data * + * packet type identifier * + * * + * OUTPUT: true if packet successfully sent * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:07PM ST : Created * + *=============================================================================================*/ +BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) +{ + if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { + WWDebugString("RA95 - Failed to connect for POKE!"); + return (FALSE); + } + + char *poke_data = new char [length + 2*sizeof(int)]; + + int *poke_data_int = (int*)poke_data; + + *poke_data_int = htonl (length + 2*sizeof(int)); + *(poke_data_int+1)= htonl (packet_type); + + memcpy (poke_data + 8, data, length); + + + if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { + WWDebugString("RA95 - POKE failed!\n"); + DDE_Class->Close_Poke_Connection(); // close down the link + delete poke_data; + return (FALSE); + } + + DDE_Class->Close_Poke_Connection(); // close down the link + + delete poke_data; + + return (TRUE); +} + + +#endif //WIN32 diff --git a/CODE/CCDDE.H b/CODE/CCDDE.H new file mode 100644 index 0000000..c4a6020 --- /dev/null +++ b/CODE/CCDDE.H @@ -0,0 +1,89 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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 - Red Alert * + * * + * File Name : CCDDE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "dde.h" + +class DDEServerClass { + + public: + + DDEServerClass (void); + ~DDEServerClass (void); + + + char *Get_MPlayer_Game_Info (void); //Returns pointer to game info + int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info + BOOL Callback(unsigned char *data, long length); //DDE callback function + void Delete_MPlayer_Game_Info(void); //release the game info memory + void Enable(void); //Enable the DDE callback + void Disable(void); //Disable the DDE callback + int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat + + /* + ** Enumeration for DDE packet types from WChat + */ + enum { + DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options + DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics. + DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there. + DDE_TICKLE, //Message to prompt other app to take focus. + DDE_CONNECTION_FAILED + }; + + + private: + + char *MPlayerGameInfo; //Pointer to game start packet + int MPlayerGameInfoLength; //Length of game start packet. + BOOL IsEnabled; //Flag for DDE callback enable + int LastHeartbeat; // Time since last heartbeat packet was received from WChat + +}; + + +extern DDEServerClass DDEServer; +extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type); + + +#endif //WIN32 diff --git a/CODE/CCFILE.CPP b/CODE/CCFILE.CPP new file mode 100644 index 0000000..4cc104a --- /dev/null +++ b/CODE/CCFILE.CPP @@ -0,0 +1,693 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCFILE.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Error -- Handles displaying a file error message. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const * filename) : + Position(0) +{ + CCFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) : + Position(0) +{ +} + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ + if (!Force_CD_Available(RequiredCD)) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const * buffer, long size) +{ + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Is_Resident()) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void * buffer, long size) +{ + bool opened = false; + + /* + ** If the file isn't currently open, then open it. + */ + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Is_Resident()) { + long maximum = Data.Get_Size() - Position; + + size = maximum < size ? maximum : size; +// size = MIN(maximum, size); + if (size) { + memmove(buffer, (char *)Data + Position, size); +// Mem_Copy((char *)Pointer + Position, buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + + /* + ** If the file was opened by this routine, then close it at this time. + */ + if (opened) Close(); + + /* + ** Return with the number of bytes read. + */ + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + /* + ** When the file is resident, a mere adjustment of the virtual file position is + ** all that is required of a seek. + */ + if (Is_Resident()) { + switch (dir) { + case SEEK_END: + Position = Data.Get_Size(); + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + Position = Position < 0 ? 0 : Position; + Position = Position > Data.Get_Size() ? Data.Get_Size() : Position; +// Position = Bound(Position+pos, 0L, Length); + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 08/05/1996 JLB : Handles returning size of embedded file. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + /* + ** If the file is resident, the the size is already known. Just return the size in this + ** case. + */ + if (Is_Resident()) return(Data.Get_Size()); + + /* + ** If the file is not available as a stand alone file, then search for it in the + ** mixfiles in order to get its size. + */ + if (!CDFileClass::Is_Available()) { + long length = 0; + MFCD::Offset(File_Name(), NULL, NULL, NULL, &length); + return(length); + } + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + /* + ** A file that is open is presumed available. + */ + if (Is_Open()) return(true); + + /* + ** A file that is part of a mixfile is also presumed available. + */ + if (MFCD::Offset(File_Name())) { + return(true); + } + + /* + ** Otherwise a manual check of the file system is required to + ** determine if the file is actually available. + */ + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Is_Resident()) return(true); + + /* + ** Otherwise, go to a lower level to determine if the file is open. + */ + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + new(&Data) ::Buffer; + Position = 0; // Starts at beginning offset. + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MFCD * mixfile = NULL; + void * pointer = NULL; + long length = 0; + long start = 0; + if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) { + + assert(mixfile != NULL); + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (pointer == NULL && mixfile != NULL) { + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + Searching(true); + free(dupfile); + Bias(0); + Bias(start, length); + Seek(0, SEEK_SET); + } else { + new (&Data) ::Buffer(pointer, length); + Position = 0; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************************** + * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +unsigned long CCFileClass::Get_Date_Time(void) +{ + unsigned long datetime; + MFCD * mixfile; + + datetime = CDFileClass::Get_Date_Time(); + + if ( !datetime ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Get_Date_Time() ); + } + // else return 0 indicating no file + } + + return( datetime ); +} + + +/*********************************************************************************************** + * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +bool CCFileClass::Set_Date_Time( unsigned long datetime ) +{ + bool status; + MFCD * mixfile; + + status = CDFileClass::Set_Date_Time( datetime ); + + if ( !status ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) ); + } + // else return 0 indicating no file + } + + return( status ); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + + +static CCFileClass Handles[10]; + +int __cdecl Open_File(char const * file_name, int mode) +{ + for (int index = 0; index < ARRAY_SIZE(Handles); index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(WWERROR); +} + +void __cdecl Close_File(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +long __cdecl Read_File(int handle, void * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +long __cdecl Write_File(int handle, void const * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const * file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(char const * file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +int __cdecl Create_File(char const * file_name) +{ + return(CCFileClass(file_name).Create()); +} + +unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +void * __cdecl Load_Alloc_Data(char const * name, int ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +unsigned long __cdecl File_Size(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +unsigned long __cdecl Seek_File(int handle, long offset, int starting) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +void __cdecl WWDOS_Init(void) +{ +} + +void __cdecl WWDOS_Shutdown(void) +{ +} + +int __cdecl Find_Disk_Number(char const *) +{ + return(0); +} +#endif + +//unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} + +#ifdef WIN32 +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} +#endif + + +void Unfragment_File_Cache(void) +{ +} + + diff --git a/CODE/CCFILE.H b/CODE/CCFILE.H new file mode 100644 index 0000000..2a34322 --- /dev/null +++ b/CODE/CCFILE.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 17, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCFILE_H +#define CCFILE_H + +//#include +#include +#include "mixfile.h" +#include "cdfile.h" +#include "buff.h" + + +/* +** This derived class for file access knows about mixfiles (packed files). It can handle opening +** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on +** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive. +*/ +class CCFileClass : public CDFileClass +{ + public: + CCFileClass(char const * filename); + CCFileClass(void); + virtual ~CCFileClass(void) {Position = 0;}; + + // Delete should be overloaded here as well. Don't allow deletes of mixfiles. + + bool Is_Resident(void) const {return(Data.Get_Buffer() != NULL);} + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int rights=READ) {Set_Name(filename);return Open(rights);}; + virtual int Open(int rights=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + private: + + /* + ** This indicates the file is actually part of a resident image of the mixfile + ** itself. In this case, the embedded file handle is invalid. All file access actually + ** gets routed through the cached version of the file. This is a pointer to the start + ** of the RAM image of the file. + */ + ::Buffer Data; +// void * Pointer; + + /* + ** This is the size of the file if it was embedded in a mixfile. The size must be manually + ** kept track of because the DOS file size is invalid. + */ +// long Length; + + /* + ** This is the current seek position of the file. It is duplicated here if the file is + ** part of a mixfile since the DOS seek position is not accurate. This value will + ** range from zero to the size of the file in bytes. + */ + long Position; + + // Force these to never be invoked. + CCFileClass const & operator = (CCFileClass const & c); + CCFileClass (CCFileClass const & ); +}; + +class MixFileClass; + +#endif diff --git a/CODE/CCINI.CPP b/CODE/CCINI.CPP new file mode 100644 index 0000000..ec46756 --- /dev/null +++ b/CODE/CCINI.CPP @@ -0,0 +1,1487 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCINI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCINI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current databas* + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * CCINIClass::Load -- Load the INI database from the file specified. * + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * CCINIClass::Save -- Save the INI data to the file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the file specified. * + * * + * This routine will load the database from the file specified in much the same manner * + * that the INIClass load function works. However, this class will examine the message * + * digest (if present) and compare it to the actual digest. If they differ, a special * + * return value is used. This will allow verification of the integrity of the ini data. * + * * + * INPUT: file -- Reference to the file that will be read from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: If the file was not read, returns 0. If the file was read ok, returns 1. If the * + * file was read ok, but the digest doesn't verify, returns 2. * + * * + * WARNINGS: If no message digest was present in the INI file, then no verification can * + * be performed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles digest control. * + *=============================================================================================*/ +bool CCINIClass::Load(FileClass & file, bool withdigest) +{ + return(Load(FileStraw(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * * + * This will load the INI database and in the process, it will fetch and verify any * + * message digest present. * + * * + * INPUT: straw -- The data stream to fetch the INI data from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: bool; Was the database loaded ok? (hack: returns "2" if digest doesn't match). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +bool CCINIClass::Load(Straw & file, bool withdigest) +{ + bool ok = INIClass::Load(file); + + Invalidate_Message_Digest(); + if (ok && withdigest) { + + /* + ** If a digest is present, fetch it. + */ + unsigned char digest[20]; + int len = Get_UUBlock("Digest", digest, sizeof(digest)); + if (len > 0) { + Clear("Digest"); + + /* + ** Calculate the message digest for the INI data that was read. + */ + Calculate_Message_Digest(); + + /* + ** If the message digests don't match, then return with the special error code. + */ + if (memcmp(digest, Digest, sizeof(digest)) != 0) { + return(2); + } + } + } + return(ok); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Save the INI data to the file specified. * + * * + * This routine will save the INI data to the file. It will add a message digest so that * + * validity check can be performed when the INI data is subsequently read. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: bool; Was the INI data saved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(FileClass & file, bool withdigest) const +{ + return(Save(FilePipe(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * * + * This routine will pipe the INI data to the pipe segment specified. It is functionally * + * the same as the save operation. A message digest is added to the output data so that * + * validity check can occur during a subsequent read. * + * * + * INPUT: straw -- Reference to the pipe that will receive the output ini data stream. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(Pipe & pipe, bool withdigest) const +{ + if (!withdigest) { + return(INIClass::Save(pipe)); + } + + /* + ** Just in case these entries are present, clear them out. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Calculate what the new digest should be. + */ + ((CCINIClass *)this)->Calculate_Message_Digest(); + + /* + ** Store the actual digest into the INI database. + */ + ((CCINIClass *)this)->Put_UUBlock("Digest", Digest, sizeof(Digest)); + + /* + ** Output the database to the pipe specified. + */ + int length = INIClass::Save(pipe); + + /* + ** Remove the digest from the database. It shouldn't stick around as if it were real data + ** since it isn't really part of the INI database proper. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Finally, return with the total number of bytes send out the pipe. + */ + return(length); +} + + +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * * + * This routine will fetch the lepton value as if it were expressed as cells. Example; * + * a value of 1 would mean 256 in leptons. * + * * + * INPUT: section -- The section identifier to look under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default value to use if the specified section and entry could * + * not be located. * + * * + * OUTPUT: Returns with the lepton value of the section & entry specified. If not found, then * + * the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +LEPTON CCINIClass::Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const +{ + fixed result = Get_Fixed(section, entry, fixed(defvalue, CELL_LEPTON_W)); + return(result * CELL_LEPTON_W); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * * + * This routine will store the lepton value as if it were expressed in cells. Example; * + * A lepton of 128 will be stored as ".5". * + * * + * INPUT: section -- The section identifier to store the value under. * + * * + * entry -- The entry to store the lepton value at. * + * * + * value -- The lepton value to store. * + * * + * OUTPUT: bool; Was the lepton value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Lepton(char const * section, char const * entry, LEPTON value) +{ + return(Put_Fixed(section, entry, fixed(value, CELL_LEPTON_W))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * * + * This routine will fetch the speed value as if it were expressed as a number from 0 * + * to 100. The value of 100 would translate into a speed of 256 leptons per game frame. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default speed value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the speed value. If no entry could be found, then the default value * + * will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +MPHType CCINIClass::Get_MPHType(char const * section, char const * entry, MPHType defvalue) const +{ + int val = Get_Int(section, entry, ((int)defvalue * 100) / 256); + return (MPHType(_Scale_To_256(val))); +} + + +/*********************************************************************************************** + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * * + * Use this routine to store a speed value into the INI database. The number stored will * + * be in a 0..100 format. A speed of 256 leptons per tick would be stored as 100. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the speed value to. * + * * + * value -- The speed value to store. * + * * + * OUTPUT: bool; Was the speed value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_MPHType(char const * section, char const * entry, MPHType value) +{ + return(Put_Int(section, entry, ((int)value * 100) / 256)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * * + * Use this to fetch a house bit array value from the INI database. This value will be * + * various bit positions set (1 << house#) for each house specified in the database. * + * Houses can be specified in series by the house name separated by commas or by the * + * special group names of "soviet", and "allies" to cover the houses that are members of * + * these groups. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default house bitfield to use if the entry could not be found. * + * * + * OUTPUT: Returns with the house bitfield value. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Owners(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long ownable = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + ownable = 0; + char * name = strtok(buffer, ","); + + while (name) { + ownable |= Owner_From_Name(name); + name = strtok(NULL, ","); + } + } + return(ownable); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * * + * Use this routine to store the house bitfield data into the database. The bitfield format * + * matches the format used by the Get_Owners function. Example; if both England and * + * Spain were specified in the bitfield, the entry would be stored as "England,Spain". * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier that is assigned the value. * + * * + * value -- The value to assign to the entry. * + * * + * OUTPUT: bool; Was the entry stored in the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Owners(char const * section, char const * entry, long value) +{ + char buffer[128]; + + buffer[0] = '\0'; + + if ((value & HOUSEF_ALLIES) == HOUSEF_ALLIES) { + strcat(buffer, "allies"); + value &= ~HOUSEF_ALLIES; + } + if ((value & HOUSEF_SOVIET) == HOUSEF_SOVIET) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, "soviet"); + value &= ~HOUSEF_SOVIET; + } + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if ((value & (1 << house)) != 0) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, HouseTypeClass::As_Reference(house).Name()); + } + } + + if (buffer[0] != '\0') { + return(Put_String(section, entry, buffer)); + } + return(true); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * * + * This routine will fetch the armor type from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Th identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the armor type specified in the INI database. If it could not be * + * found, then the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType CCINIClass::Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, ArmorName[defvalue], buffer, sizeof(buffer)); + return(Armor_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * * + * Use this routine to store the specified armor type to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the value at. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the entry stored in the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ArmorType(char const * section, char const * entry, ArmorType value) +{ + return(Put_String(section, entry, ArmorName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * * + * This routine will fetch a voc number from the database. The voc number will either * + * be a valid sound effect or VOC_NONE if no match could be found. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the sound effect (VocType) from the INI database. If the entry could * + * not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VocType CCINIClass::Get_VocType(char const * section, char const * entry, VocType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Voc_Name(defvalue), buffer, sizeof(buffer)); + return(Voc_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * * + * Use this routine to store a voc identifier (stored a the text name of the sound) into * + * the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- The entry to assign the value to. * + * * + * value -- The sound effect to store to the entry. * + * * + * OUTPUT: bool; Was the sound effect entry stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VocType(char const * section, char const * entry, VocType value) +{ + if (value == VOC_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Voc_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * * + * This will fetch an AnimType number from the INI database. The anim is stored as a text * + * name of the art file used for that anim. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default AnimType to use if the entry could not be located. * + * * + * OUTPUT: Returns with the anim type specified in the database. If it could not be found, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +AnimType CCINIClass::Get_AnimType(char const * section, char const * entry, AnimType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Anim_Name(defvalue), buffer, sizeof(buffer)); + return(Anim_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * * + * This routine will store the animation identifier (stored as the text name of the art * + * file it uses) to the INI database. * + * * + * INPUT: section -- The section identifier to place the entry under. * + * * + * entry -- The entry identifier to assign the animation number to. * + * * + * value -- The animation identifier to store with the entry. * + * * + * OUTPUT: bool; Was the animation identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_AnimType(char const * section, char const * entry, AnimType value) +{ + if (value == ANIM_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Anim_Name(value))); +} + + +UnitType CCINIClass::Get_UnitType(char const * section, char const * entry, UnitType defvalue) const +{ + char buffer[128]; + + char const * def = ""; + if (defvalue != UNIT_NONE) { + def = UnitTypeClass::As_Reference(defvalue).Name(); + } + Get_String(section, entry, def, buffer, sizeof(buffer)); + return(UnitTypeClass::From_Name(buffer)); +} + + +bool CCINIClass::Put_UnitType(char const * section, char const * entry, UnitType value) +{ + if (value == UNIT_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, UnitTypeClass::As_Reference(value).Name())); +} + + + +/*********************************************************************************************** + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * * + * This routine will fetch the weapon type from the INI database. The weapon type is * + * stored as a custom identifier string. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default weapon value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the weapon type specified by the entry. If the entry could not be * + * found then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType CCINIClass::Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Weapon_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * * + * Store the weapon identifier (as custom string name) to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier to store the weapon identifier with. * + * * + * value -- The weapon identifier to store. * + * * + * OUTPUT: bool; Was the weapon identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WeaponType(char const * section, char const * entry, WeaponType value) +{ + if (value == WEAPON_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WeaponTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * * + * Will fetch the warhead identifier from the INI database. * + * * + * INPUT: section -- The identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default return value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the found warhead type. If the entry could not be found then the * + * default warhead value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WarheadType CCINIClass::Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (WarheadType wh = WARHEAD_FIRST; wh < WARHEAD_COUNT; wh++) { + if (stricmp(WarheadTypeClass::As_Pointer(wh)->Name(), buffer) == 0) { + return(wh); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * * + * This will store the weapon identifier specified to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the warhead identifier. * + * * + * value -- The warhead identifier to store. * + * * + * OUTPUT: bool; Was the warhead identifier stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WarheadType(char const * section, char const * entry, WarheadType value) +{ + if (value == WARHEAD_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WarheadTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * * + * This routine will fetch the overlay identifier from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the overlay identifier found. If it could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +OverlayType CCINIClass::Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(OverlayTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * * + * Use this routine to store the overlay identifier into the INI database. * + * * + * INPUT: section -- Identifier for to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * value -- The overlay type value to store with the entry. * + * * + * OUTPUT: bool; Was the overlay value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_OverlayType(char const * section, char const * entry, OverlayType value) +{ + assert(value != OVERLAY_NONE); + return(Put_String(section, entry, OverlayTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * * + * Use this routine to fetch the bullet type identifier from the INI database. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default bullet type value to return if the entry could not be * + * located. * + * * + * OUTPUT: Returns with the bullet type identifier found. If the entry could not be found * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +BulletType CCINIClass::Get_BulletType(char const * section, char const * entry, BulletType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (BulletType proj = BULLET_FIRST; proj < BULLET_COUNT; proj++) { + if (stricmp(BulletTypeClass::As_Reference(proj).Name(), buffer) == 0) { +// if (stricmp(ProjectileNames[proj], buffer) == 0) { + return(proj); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * * + * This routine will store the projectile name (as the identifier) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry identifier to store the projectile value with. * + * * + * value -- The projectile identifier to store. * + * * + * OUTPUT: bool; Was the projectile identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_BulletType(char const * section, char const * entry, BulletType value) +{ + if (value == BULLET_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, BulletTypeClass::As_Reference(value).Name())); +// return(Put_String(section, entry, ProjectileNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * * + * Use this routine to fetch an individual house identifier from the INI database. This is * + * somewhat similar to the Get_Owners function but is limited to a single house. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the house identifier if it was found. If not found, then the default * + * value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +HousesType CCINIClass::Get_HousesType(char const * section, char const * entry, HousesType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(HouseTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * * + * Use this routine to store the specified house identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * value -- The house identifier to store in the database. * + * * + * OUTPUT: bool; Was the house identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_HousesType(char const * section, char const * entry, HousesType value) +{ + return(Put_String(section, entry, HouseTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * * + * Fetches the VQ movie name (identifier) from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the VQ movie identifier found. If the entry could not be located, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VQType CCINIClass::Get_VQType(char const * section, char const * entry, VQType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (VQType vq = VQ_FIRST; vq < VQ_COUNT; vq++) { + if (stricmp(buffer, VQName[vq]) == 0) { + return(vq); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * * + * Use this routine to store the VQ movie identifier into the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The VQ movie identifier to store to the INI database. * + * * + * OUTPUT: bool; Was the VQ identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VQType(char const * section, char const * entry, VQType value) +{ + if (value == VQ_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, VQName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * * + * This will fetch the theater identifier from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the theater type found. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TheaterType CCINIClass::Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theater_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * * + * Use this routine to store the theater name to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The theater identifier to store. * + * * + * OUTPUT: bool; Was the theater identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TheaterType(char const * section, char const * entry, TheaterType value) +{ + return(Put_String(section, entry, Theaters[value].Name)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * * + * This routine will fetch the trigger type identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * OUTPUT: Returns with the trigger type pointer if a match was found. No match found will * + * return a NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * CCINIClass::Get_TriggerType(char const * section, char const * entry) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TriggerTypeClass::From_Name(buffer)); + } + return(NULL); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * * + * This routine will store the trigger (as its name) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry name to store the trigger identifier to. * + * * + * value -- The trigger type to store. The trigger name will be stored. * + * * + * OUTPUT: bool; Was the trigger name stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value) +{ + return(Put_String(section, entry, value->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * * + * This routine will fetch the theme identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * defvalue -- The default theme identifier to return if the entry could not be found.* + * * + * OUTPUT: Returns with the theme identifier if it was found. If not found, then the default * + * value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ThemeType CCINIClass::Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theme.From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * * + * This routine will store the specified theme identifier to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier for the entry to store the value to. * + * * + * value -- The theme identifier to store. * + * * + * OUTPUT: bool; Was the theme identifier stored. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ThemeType(char const * section, char const * entry, ThemeType value) +{ + return(Put_String(section, entry, Theme.Base_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * * + * This routine will fetch the source (reinforcement edge) identifier from the INI * + * database. * + * * + * INPUT: section -- Identifier for the section that the entry will be searched under. * + * * + * entry -- Identifier for the entry that will be searched for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the source type of the entry if found. If not found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +SourceType CCINIClass::Get_SourceType(char const * section, char const * entry, SourceType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Source_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * * + * This will store the source type (reinforcement edge) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier of the entry to store the source identifier to. * + * * + * value -- The source (edge) value to store. * + * * + * OUTPUT: bool; Was the source identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_SourceType(char const * section, char const * entry, SourceType value) +{ + return(Put_String(section, entry, SourceName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * * + * This will return with the crate type specified in the INI database. * + * * + * INPUT: section -- Identifier for the section to search under. * + * * + * entry -- The entry to find the matching crate value for. * + * * + * defvalue -- The default crate value to return if the entry could not be found. * + * * + * OUTPUT: Returns with the crate type identified with the specified entry. If the entry * + * could not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +CrateType CCINIClass::Get_CrateType(char const * section, char const * entry, CrateType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Crate_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * * + * This will store the specified crate value to the section and entry specified. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the crate value with. * + * * + * value -- The crate value to store. * + * * + * OUTPUT: bool; Was the crate value stored to the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_CrateType(char const * section, char const * entry, CrateType value) +{ + return(Put_String(section, entry, CrateNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * * + * Fetches the terrain type number from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the terrain type if found. If the entry wasn't found, then the * + * default value will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TerrainType CCINIClass::Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TerrainTypeClass::From_Name(strtok(buffer, ","))); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * * + * This will store the terrain type identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier that the terrain number will be stored to. * + * * + * value -- The terrain type identifier to store. * + * * + * OUTPUT: bool; Was the terrain identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TerrainType(char const * section, char const * entry, TerrainType value) +{ + return(Put_String(section, entry, TerrainTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * * + * This routing will fetch the a list of buildings from the INI database. The buildings * + * are expressed as a comma separated list of building identifiers. The return value is * + * a composite of bits that represent these buildings -- one bit per building type. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to fetch the building list from. * + * * + * defvalue -- The default value to return if the section and entry could not be * + * located. * + * * + * OUTPUT: Returns with the building list (as a bitfield). If the entry could not be * + * found, the the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Buildings(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long pre; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + pre = 0; + char * token = strtok(buffer, ","); + while (token != NULL && *token != '\0') { + StructType building = BuildingTypeClass::From_Name(token); + if (building != STRUCT_NONE) { + pre |= (1L << building); + } + token = strtok(NULL, ","); + } + } else { + pre = defvalue; + } + + return(pre); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * * + * This will store a list of buildings to the INI database. The buildings are listed by * + * their identifier names separated by commas. * + * * + * INPUT: section -- The identifier for the section to store the entry under. * + * * + * entry -- The entry to store the building list to. * + * * + * value -- A list of buildings (in the form of a bit field -- one bit per * + * building type). * + * * + * OUTPUT: Was the building list stored to the INI file? * + * * + * WARNINGS: This is limited to the buildings that can be expressed in a bitfield long. * + * Which means, there can be only a maximum of 32 building types listed and * + * even then, the total line length generated must not exceed 128 bytes. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Buildings(char const * section, char const * entry, long value) +{ + char buffer[128] = ""; + int maxi = (32 < STRUCT_COUNT) ? 32 : STRUCT_COUNT; + + for (StructType index = STRUCT_FIRST; index < maxi; index++) { + if ((value & (1L << index)) != 0) { + + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, BuildingTypeClass::As_Reference(index).IniName); + } + } + + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * * + * This is a shorthand version of the message digest. It calculates the ID number from the * + * message digest itself. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a 32 bit unique identifier number for the INI database. * + * * + * WARNINGS: Since the return value is only 32 bits, it is much less secure than the * + * complete message digest. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +int CCINIClass::Get_Unique_ID(void) const +{ + if (!IsDigestPresent) { + ((CCINIClass *)this)->Calculate_Message_Digest(); + } + + return(CRCEngine()(&Digest[0], sizeof(Digest))); +} + + +/*********************************************************************************************** + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current database * + * * + * This will calculate a new message digest according to the current state of the INI * + * database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If the database is changed in any fashion, this message digest will be rendered * + * obsolete. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Calculate_Message_Digest(void) +{ + /* + ** Calculate the message digest for the INI data that was read. + */ + SHAPipe sha; + INIClass::Save(sha); + sha.Result(Digest); + IsDigestPresent = true; +} + + +/*********************************************************************************************** + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * * + * This flags the message digest as being invalid so that it will be recalculated when * + * needed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Invalidate_Message_Digest(void) +{ + IsDigestPresent = false; +} diff --git a/CODE/CCINI.H b/CODE/CCINI.H new file mode 100644 index 0000000..17fbdbe --- /dev/null +++ b/CODE/CCINI.H @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCINI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCINI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : May 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCINI_H +#define CCINI_H + +#include "ini.h" +#include "fixed.h" +#include "pk.h" + +class TriggerTypeClass; + +/* +** The advanced version of the INI database manager. It handles the C&C expansion types and +** identifiers. In addition, it automatically stores a message digest with the INI data +** so that verification can occur. +*/ +class CCINIClass : public INIClass +{ + public: + CCINIClass(void) : IsDigestPresent(false) {} + + bool Load(FileClass & file, bool withdigest); + bool Load(Straw & file, bool withdigest); + int Save(FileClass & file, bool withdigest) const; + int Save(Pipe & pipe, bool withdigest) const; + + long Get_Buildings(char const * section, char const * entry, long defvalue) const; + UnitType Get_UnitType(char const * section, char const * entry, UnitType defvalue) const; + AnimType Get_AnimType(char const * section, char const * entry, AnimType defvalue) const; + ArmorType Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const; + BulletType Get_BulletType(char const * section, char const * entry, BulletType defvalue) const; + HousesType Get_HousesType(char const * section, char const * entry, HousesType defvalue) const; + LEPTON Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const; + MPHType Get_MPHType(char const * section, char const * entry, MPHType defvalue) const; + OverlayType Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const; + SourceType Get_SourceType(char const * section, char const * entry, SourceType defvalue) const; + TerrainType Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const; + TheaterType Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const; + ThemeType Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const; + TriggerTypeClass * Get_TriggerType(char const * section, char const * entry) const; + VQType Get_VQType(char const * section, char const * entry, VQType defvalue) const; + VocType Get_VocType(char const * section, char const * entry, VocType defvalue) const; + WarheadType Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const; + WeaponType Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const; + long Get_Owners(char const * section, char const * entry, long defvalue) const; + CrateType Get_CrateType(char const * section, char const * entry, CrateType defvalue) const; + + + bool Put_Buildings(char const * section, char const * entry, long value); + bool Put_AnimType(char const * section, char const * entry, AnimType value); + bool Put_UnitType(char const * section, char const * entry, UnitType value); + bool Put_ArmorType(char const * section, char const * entry, ArmorType value); + bool Put_BulletType(char const * section, char const * entry, BulletType value); + bool Put_HousesType(char const * section, char const * entry, HousesType value); + bool Put_Lepton(char const * section, char const * entry, LEPTON value); + bool Put_MPHType(char const * section, char const * entry, MPHType value); + bool Put_VQType(char const * section, char const * entry, VQType value); + bool Put_OverlayType(char const * section, char const * entry, OverlayType value); + bool Put_Owners(char const * section, char const * entry, long value); + bool Put_SourceType(char const * section, char const * entry, SourceType value); + bool Put_TerrainType(char const * section, char const * entry, TerrainType value); + bool Put_TheaterType(char const * section, char const * entry, TheaterType value); + bool Put_ThemeType(char const * section, char const * entry, ThemeType value); + bool Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value); + bool Put_VocType(char const * section, char const * entry, VocType value); + bool Put_WarheadType(char const * section, char const * entry, WarheadType value); + bool Put_WeaponType(char const * section, char const * entry, WeaponType value); + bool Put_CrateType(char const * section, char const * entry, CrateType value); + + int Get_Unique_ID(void) const; + + private: + void Calculate_Message_Digest(void); + void Invalidate_Message_Digest(void); + + bool IsDigestPresent:1; + + /* + ** This is the message digest (SHA) of the INI database that was embedded as part of + ** the INI file. + */ + unsigned char Digest[20]; +}; + +#endif diff --git a/CODE/CCMPATH.CPP b/CODE/CCMPATH.CPP new file mode 100644 index 0000000..9d466cc --- /dev/null +++ b/CODE/CCMPATH.CPP @@ -0,0 +1,421 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCMPATH.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : January 11, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_MPATH -- Performs MPATH-specific initialization * + * Shutdown_MPATH -- Shuts down MPATH connections * + * Connect_MPATH -- Waits for connections to other players * + * Destroy_MPATH_Connection -- Destroys the given connection * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + + +/*************************************************************************** + * Init_MPATH -- Performs MPATH-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_MPATH(void) +{ +#if(MPATH) + //------------------------------------------------------------------------ + // Allocate a packet buffer for MPATH's use + //------------------------------------------------------------------------ + Session.MPathPacket = new char[Session.MPathSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_MPATH_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + MPath->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_MPATH(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_MPATH + + +/*************************************************************************** + * Shutdown_MPATH -- Shuts down MPATH connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_MPATH(void) +{ +#if(MPATH) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.MPathPacket) { + delete [] Session.MPathPacket; + Session.MPathPacket = NULL; + } + + if (MPath) { + delete MPath; + MPath = NULL; + } + + return; + +#endif +} // end of Shutdown_MPATH + + +/*************************************************************************** + * Connect_MPATH -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_MPATH(void) +{ +#if(MPATH) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = MPath->Find_Num_Connections(); + num_found = 0; + Session.NumPlayers = num_players + 1; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + MPath->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (MPath->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->MPathAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->MPathAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_MPATH_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_MPATH_Connection(int id, int error) +{ +#if(MPATH) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),MPath->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),MPath->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the MPATH connection + ------------------------------------------------------------------------*/ + MPath->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_MPATH_Connection + + +/***************************** end of ccmpath.cpp **************************/ diff --git a/CODE/CCPTR.CPP b/CODE/CCPTR.CPP new file mode 100644 index 0000000..580d301 --- /dev/null +++ b/CODE/CCPTR.CPP @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCPTR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCPTR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCPtr::operator > -- Greater than comparison operator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +/* +** These member functions for the CCPtr class cannot be declared inside the +** class definition since they could refer to other objects that themselves +** contain CCPtr objects. The recursive nature of this type of declaration +** is not handled by Watcom, hence the body declaration is dislocated here. +*/ +template +CCPtr::CCPtr(T * ptr) : ID(-1) +{ + if (ptr != NULL) { + ID = ptr->ID; + } +} + + +/*********************************************************************************************** + * CCPtr::operator > -- Greater than comparison operator. * + * * + * This will compare two pointer value to see if the left hand value is greater than the * + * right hand. The values are compared by comparing based on their Name() functions. * + * * + * INPUT: rvalue -- The right handle CCPtr value. * + * * + * OUTPUT: Is the left hand value greater than the right hand value? * + * * + * WARNINGS: The values pointed to by CCPtr must have a Name() function defined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +bool CCPtr::operator > (CCPtr const & rvalue) const +{ + return (stricmp((*this)->Name(), rvalue->Name()) > 0); +} diff --git a/CODE/CCPTR.H b/CODE/CCPTR.H new file mode 100644 index 0000000..d798247 --- /dev/null +++ b/CODE/CCPTR.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CCPTR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCPTR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : June 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCPTR_H +#define CCPTR_H + +/* +** The CCPtr class is designed for a specific purpose. It functions like a pointer except that +** it requires no fixups for saving and loading. If pointer fixups are not an issue, than using +** regular pointers would be more efficient. +*/ +template +class CCPtr +{ + public: + CCPtr(void) : ID(-1) {}; + CCPtr(NoInitClass const & ) {}; + CCPtr(T * ptr); + + operator T * (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && (unsigned)ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + T & operator * (void) const { + assert(Heap != NULL && (unsigned)ID < Heap->Length()); + return(*(T*)(*Heap)[ID]); + } + T * operator -> (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && (unsigned)ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + + bool Is_Valid(void) const {return(ID != -1);} + + bool operator == (CCPtr const & rvalue) const {return(ID == rvalue.ID);} + bool operator != (CCPtr const & rvalue) const {return(ID != rvalue.ID);} + bool operator > (CCPtr const & rvalue) const; + bool operator <= (CCPtr const & rvalue) const {return (rvalue > *this);} + bool operator < (CCPtr const & rvalue) const {return (*this != rvalue && rvalue > *this);} + bool operator >= (CCPtr const & rvalue) const {return (*this == rvalue || rvalue > *this);} + + long Raw(void) const {return(ID);} + void Set_Raw(long value) {ID = value;} + + private: + + static FixedIHeapClass * Heap; + + /* + ** This is the ID number of the object it refers to. By using an ID number, this class can + ** be saved and loaded without pointer fixups. + */ + int ID; +}; + +/* +** These template helper functions tell the compiler what to do in the +** ambiguous case of a CCPtr on one side and a regular type pointer on the +** other side. In such a case the compiler could create a temp CCPtr object +** OR call the conversion operator on the existing CCPtr object. Either way +** is technically valid, but the compiler doesn't know which is better so it +** generates an error. These routines force the conversion operator rather than +** creating a temporary object. This presumes that the conversion operator is +** cheaper than constructing a temporary and that cheaper solutions are desirable. +*/ +template +int operator == (CCPtr & lvalue, T * rvalue) +{ + return((T*)lvalue == rvalue); +} + +template +int operator == (T * lvalue, CCPtr & rvalue) +{ + return(lvalue == (T*)rvalue); +} + +#endif diff --git a/CODE/CCTEN.CPP b/CODE/CCTEN.CPP new file mode 100644 index 0000000..a39c884 --- /dev/null +++ b/CODE/CCTEN.CPP @@ -0,0 +1,603 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCTEN.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : November 27, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_TEN -- Performs TEN-specific initialization * + * Shutdown_TEN -- Shuts down TEN connections * + * Connect_TEN -- Waits for connections to other players * + * Destroy_TEN_Connection -- Destroys the given connection * + * Debug_Mono -- Custom mono prints * + * Send_TEN_Win_Packet -- Sends a win packet to server * + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#if(TEN) +#ifdef WIN32 +#define WINDOWS +#endif +#include "ten.h" +#endif + +void Connect_TEN(void); +void Debug_Mono(void); + +/*************************************************************************** + * Init_TEN -- Performs TEN-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_TEN(void) +{ +#if(TEN) + //------------------------------------------------------------------------ + // Allocate a packet buffer for TEN's use + //------------------------------------------------------------------------ + Session.TenPacket = new char[Session.TenSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_TEN_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + Ten->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_TEN(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_TEN + + +/*************************************************************************** + * Shutdown_TEN -- Shuts down TEN connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_TEN(void) +{ +#if(TEN) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.TenPacket) { + delete [] Session.TenPacket; + Session.TenPacket = NULL; + } + + if (Ten) { + delete Ten; + Ten = NULL; + } + + return; + +#endif +} // end of Shutdown_TEN + + +/*************************************************************************** + * Connect_TEN -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * MPlayerCount must have been initialized at this point. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_TEN(void) +{ +#if(TEN) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = Session.NumPlayers - 1; + num_found = 0; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + Ten->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (Ten->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->TenAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->TenAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_TEN_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy (must be a HousesType) * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_TEN_Connection(int id, int error) +{ +#if(TEN) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ten->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ten->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the TEN connection + ------------------------------------------------------------------------*/ + Ten->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_TEN_Connection + + +/*************************************************************************** + * Debug_Mono -- Custom mono prints * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Debug_Mono(void) +{ +#if(TEN) + int i; + int id; + + Mono_Printf("STATE: # Connections:%d\n",Ten->Num_Connections()); + for (i=0;iNum_Connections();i++) { + id = Ten->Connection_ID(i); + Mono_Printf("Connection %d: Name:%s, ID:%d, Address:%d\n", + i, + Ten->Connection_Name(id), + Ten->Connection_ID(i), + Ten->Connection_Address(id)); + } + +#endif +} + + +/*************************************************************************** + * Send_TEN_Win_Packet -- Sends a win packet to server * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Win_Packet(void) +{ +#if(TEN) + char winbuf[80]; + char idbuf[20]; + int first = 1; + HouseClass *hptr; + int i; + + // + // Build a special text buffer to send to the TEN server. Format: + // "winner 'id id'", where 'id' is the Ten Player ID of each player + // on the winning team. (For TEN, the color index is the player ID.) + // + sprintf(winbuf,"winner '"); + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + if (!first) { + strcat(winbuf," "); + } else { + first = 0; + } + sprintf(idbuf,"%d", Session.Players[i]->Player.Color); + strcat (winbuf, idbuf); + } + } + strcat (winbuf,"' "); + tenArSetPlayerState(winbuf); +#endif // TEN + +} // end of Send_TEN_Win_Packet + + +/*************************************************************************** + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * * + * INPUT: * + * whom name of player we're allying / enemying with * + * ally 1 = we're allying; 0 = we're breaking the alliance * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Alliance(char *whom, int ally) +{ +#if(TEN) + char buf[80]; + + if (ally) { + sprintf(buf,"ally '%s' ",whom); + } else { + sprintf(buf,"enemy '%s' ",whom); + } + + tenArSetPlayerState(buf); + +#else + whom = whom; + ally = ally; +#endif // TEN + +} // end of Send_TEN_Alliance + + +/*************************************************************************** + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Out_Of_Sync(void) +{ +#if(TEN) + tenArSetPlayerState("sync '1' "); +#endif // TEN + +} // end of Send_TEN_Out_Of_Sync + + +/*************************************************************************** + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Packet_Too_Late(void) +{ +#if(TEN) + tenArSetPlayerState("toolate '1' "); +#endif // TEN + +} // end of Send_TEN_Packet_Too_Late + + +/***************************** end of ccten.cpp *****************************/ diff --git a/CODE/CC_ICON.RC b/CODE/CC_ICON.RC new file mode 100644 index 0000000..c9e749c --- /dev/null +++ b/CODE/CC_ICON.RC @@ -0,0 +1,6 @@ + +#define ICON_1 1 + + +ICON_1 ICON "redalert.ico" + diff --git a/CODE/CC_ICON.RH b/CODE/CC_ICON.RH new file mode 100644 index 0000000..206d6bc --- /dev/null +++ b/CODE/CC_ICON.RH @@ -0,0 +1 @@ +#define ICON_1 1 diff --git a/CODE/CDATA.CPP b/CODE/CDATA.CPP new file mode 100644 index 0000000..39cf45c --- /dev/null +++ b/CODE/CDATA.CPP @@ -0,0 +1,3324 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static TemplateTypeClass const Empty( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Clear( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Road01( + TEMPLATE_ROAD01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D01", + TXT_ROAD +); +static TemplateTypeClass const Road02( + TEMPLATE_ROAD02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D02", + TXT_ROAD +); +static TemplateTypeClass const Road03( + TEMPLATE_ROAD03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D03", + TXT_ROAD +); +static TemplateTypeClass const Road04( + TEMPLATE_ROAD04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D04", + TXT_ROAD +); +static TemplateTypeClass const Road05( + TEMPLATE_ROAD05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D05", + TXT_ROAD +); +static TemplateTypeClass const Road06( + TEMPLATE_ROAD06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D06", + TXT_ROAD +); +static TemplateTypeClass const Road07( + TEMPLATE_ROAD07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D07", + TXT_ROAD +); +static TemplateTypeClass const Road08( + TEMPLATE_ROAD08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D08", + TXT_ROAD +); +static TemplateTypeClass const Road09( + TEMPLATE_ROAD09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D09", + TXT_ROAD +); +static TemplateTypeClass const Road10( + TEMPLATE_ROAD10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D10", + TXT_ROAD +); +static TemplateTypeClass const Road11( + TEMPLATE_ROAD11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D11", + TXT_ROAD +); +static TemplateTypeClass const Road12( + TEMPLATE_ROAD12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D12", + TXT_ROAD +); +static TemplateTypeClass const Road13( + TEMPLATE_ROAD13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D13", + TXT_ROAD +); +static TemplateTypeClass const Road14( + TEMPLATE_ROAD14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D14", + TXT_ROAD +); +static TemplateTypeClass const Road15( + TEMPLATE_ROAD15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D15", + TXT_ROAD +); +static TemplateTypeClass const Road16( + TEMPLATE_ROAD16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D16", + TXT_ROAD +); +static TemplateTypeClass const Road17( + TEMPLATE_ROAD17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D17", + TXT_ROAD +); +static TemplateTypeClass const Road18( + TEMPLATE_ROAD18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D18", + TXT_ROAD +); +static TemplateTypeClass const Road19( + TEMPLATE_ROAD19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D19", + TXT_ROAD +); +static TemplateTypeClass const Road20( + TEMPLATE_ROAD20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D20", + TXT_ROAD +); +static TemplateTypeClass const Road21( + TEMPLATE_ROAD21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D21", + TXT_ROAD +); +static TemplateTypeClass const Road22( + TEMPLATE_ROAD22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D22", + TXT_ROAD +); +static TemplateTypeClass const Road23( + TEMPLATE_ROAD23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D23", + TXT_ROAD +); +static TemplateTypeClass const Road24( + TEMPLATE_ROAD24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D24", + TXT_ROAD +); +static TemplateTypeClass const Road25( + TEMPLATE_ROAD25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D25", + TXT_ROAD +); +static TemplateTypeClass const Road26( + TEMPLATE_ROAD26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D26", + TXT_ROAD +); +static TemplateTypeClass const Road27( + TEMPLATE_ROAD27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D27", + TXT_ROAD +); +static TemplateTypeClass const Road28( + TEMPLATE_ROAD28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D28", + TXT_ROAD +); +static TemplateTypeClass const Road29( + TEMPLATE_ROAD29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D29", + TXT_ROAD +); +static TemplateTypeClass const Road30( + TEMPLATE_ROAD30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D30", + TXT_ROAD +); +static TemplateTypeClass const Road31( + TEMPLATE_ROAD31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D31", + TXT_ROAD +); +static TemplateTypeClass const Road32( + TEMPLATE_ROAD32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D32", + TXT_ROAD +); +static TemplateTypeClass const Road33( + TEMPLATE_ROAD33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D33", + TXT_ROAD +); +static TemplateTypeClass const Road34( + TEMPLATE_ROAD34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D34", + TXT_ROAD +); +static TemplateTypeClass const Road35( + TEMPLATE_ROAD35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D35", + TXT_ROAD +); +static TemplateTypeClass const Road36( + TEMPLATE_ROAD36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D36", + TXT_ROAD +); +static TemplateTypeClass const Road37( + TEMPLATE_ROAD37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D37", + TXT_ROAD +); +static TemplateTypeClass const Road38( + TEMPLATE_ROAD38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D38", + TXT_ROAD +); +static TemplateTypeClass const Road39( + TEMPLATE_ROAD39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D39", + TXT_ROAD +); +static TemplateTypeClass const Road40( + TEMPLATE_ROAD40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D40", + TXT_ROAD +); +static TemplateTypeClass const Road41( + TEMPLATE_ROAD41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D41", + TXT_ROAD +); +static TemplateTypeClass const Road42( + TEMPLATE_ROAD42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D42", + TXT_ROAD +); +static TemplateTypeClass const Road43( + TEMPLATE_ROAD43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D43", + TXT_ROAD +); +static TemplateTypeClass const Road44( + TEMPLATE_ROAD44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D44", + TXT_ROAD +); +static TemplateTypeClass const Road45( + TEMPLATE_ROAD45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D45", + TXT_ROAD +); +static TemplateTypeClass const Water( + TEMPLATE_WATER, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W1", + TXT_WATER +); +static TemplateTypeClass const Water2( + TEMPLATE_WATER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W2", + TXT_WATER +); +static TemplateTypeClass const Shore01( + TEMPLATE_SHORE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH01", + TXT_SHORE +); +static TemplateTypeClass const Shore02( + TEMPLATE_SHORE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH02", + TXT_SHORE +); +static TemplateTypeClass const Shore03( + TEMPLATE_SHORE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH03", + TXT_SHORE +); +static TemplateTypeClass const Shore04( + TEMPLATE_SHORE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH04", + TXT_SHORE +); +static TemplateTypeClass const Shore05( + TEMPLATE_SHORE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH05", + TXT_SHORE +); +static TemplateTypeClass const Shore06( + TEMPLATE_SHORE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH06", + TXT_SHORE +); +static TemplateTypeClass const Shore07( + TEMPLATE_SHORE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH07", + TXT_SHORE +); +static TemplateTypeClass const Shore08( + TEMPLATE_SHORE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH08", + TXT_SHORE +); +static TemplateTypeClass const Shore09( + TEMPLATE_SHORE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH09", + TXT_SHORE +); +static TemplateTypeClass const Shore10( + TEMPLATE_SHORE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH10", + TXT_SHORE +); +static TemplateTypeClass const Shore11( + TEMPLATE_SHORE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH11", + TXT_SHORE +); +static TemplateTypeClass const Shore12( + TEMPLATE_SHORE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH12", + TXT_SHORE +); +static TemplateTypeClass const Shore13( + TEMPLATE_SHORE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH13", + TXT_SHORE +); +static TemplateTypeClass const Shore14( + TEMPLATE_SHORE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH14", + TXT_SHORE +); +static TemplateTypeClass const Shore15( + TEMPLATE_SHORE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH15", + TXT_SHORE +); +static TemplateTypeClass const Shore16( + TEMPLATE_SHORE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH16", + TXT_SHORE +); +static TemplateTypeClass const Shore17( + TEMPLATE_SHORE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH17", + TXT_SHORE +); +static TemplateTypeClass const Shore18( + TEMPLATE_SHORE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH18", + TXT_SHORE +); +static TemplateTypeClass const Shore19( + TEMPLATE_SHORE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH19", + TXT_SHORE +); +static TemplateTypeClass const Shore20( + TEMPLATE_SHORE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH20", + TXT_SHORE +); +static TemplateTypeClass const Shore21( + TEMPLATE_SHORE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH21", + TXT_SHORE +); +static TemplateTypeClass const Shore22( + TEMPLATE_SHORE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH22", + TXT_SHORE +); +static TemplateTypeClass const Shore23( + TEMPLATE_SHORE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH23", + TXT_SHORE +); +static TemplateTypeClass const Shore24( + TEMPLATE_SHORE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH24", + TXT_SHORE +); +static TemplateTypeClass const Shore25( + TEMPLATE_SHORE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH25", + TXT_SHORE +); +static TemplateTypeClass const Shore26( + TEMPLATE_SHORE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH26", + TXT_SHORE +); +static TemplateTypeClass const Shore27( + TEMPLATE_SHORE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH27", + TXT_SHORE +); +static TemplateTypeClass const Shore28( + TEMPLATE_SHORE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH28", + TXT_SHORE +); +static TemplateTypeClass const Shore29( + TEMPLATE_SHORE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH29", + TXT_SHORE +); +static TemplateTypeClass const Shore30( + TEMPLATE_SHORE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH30", + TXT_SHORE +); +static TemplateTypeClass const Shore31( + TEMPLATE_SHORE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH31", + TXT_SHORE +); +static TemplateTypeClass const Shore32( + TEMPLATE_SHORE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH32", + TXT_SHORE +); +static TemplateTypeClass const Shore33( + TEMPLATE_SHORE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH33", + TXT_SHORE +); +static TemplateTypeClass const Shore34( + TEMPLATE_SHORE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH34", + TXT_SHORE +); +static TemplateTypeClass const Shore35( + TEMPLATE_SHORE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH35", + TXT_SHORE +); +static TemplateTypeClass const Shore36( + TEMPLATE_SHORE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH36", + TXT_SHORE +); +static TemplateTypeClass const Shore37( + TEMPLATE_SHORE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH37", + TXT_SHORE +); +static TemplateTypeClass const Shore38( + TEMPLATE_SHORE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH38", + TXT_SHORE +); +static TemplateTypeClass const Shore39( + TEMPLATE_SHORE39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH39", + TXT_SHORE +); +static TemplateTypeClass const Shore40( + TEMPLATE_SHORE40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH40", + TXT_SHORE +); +static TemplateTypeClass const Shore41( + TEMPLATE_SHORE41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH41", + TXT_SHORE +); +static TemplateTypeClass const Shore42( + TEMPLATE_SHORE42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH42", + TXT_SHORE +); +static TemplateTypeClass const Shore43( + TEMPLATE_SHORE43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH43", + TXT_SHORE +); +static TemplateTypeClass const Shore44( + TEMPLATE_SHORE44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH44", + TXT_SHORE +); +static TemplateTypeClass const Shore45( + TEMPLATE_SHORE45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH45", + TXT_SHORE +); +static TemplateTypeClass const Shore46( + TEMPLATE_SHORE46, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH46", + TXT_SHORE +); +static TemplateTypeClass const Shore47( + TEMPLATE_SHORE47, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH47", + TXT_SHORE +); +static TemplateTypeClass const Shore48( + TEMPLATE_SHORE48, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH48", + TXT_SHORE +); +static TemplateTypeClass const Shore49( + TEMPLATE_SHORE49, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH49", + TXT_SHORE +); +static TemplateTypeClass const Shore50( + TEMPLATE_SHORE50, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH50", + TXT_SHORE +); +static TemplateTypeClass const Shore51( + TEMPLATE_SHORE51, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH51", + TXT_SHORE +); +static TemplateTypeClass const Shore52( + TEMPLATE_SHORE52, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH52", + TXT_SHORE +); +static TemplateTypeClass const Shore53( + TEMPLATE_SHORE53, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH53", + TXT_SHORE +); +static TemplateTypeClass const Shore54( + TEMPLATE_SHORE54, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH54", + TXT_SHORE +); +static TemplateTypeClass const Shore55( + TEMPLATE_SHORE55, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH55", + TXT_SHORE +); +static TemplateTypeClass const Shore56( + TEMPLATE_SHORE56, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH56", + TXT_SHORE +); +static TemplateTypeClass const Boulder1( + TEMPLATE_BOULDER1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B1", + TXT_SLOPE +); +static TemplateTypeClass const Boulder2( + TEMPLATE_BOULDER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B2", + TXT_SLOPE +); +static TemplateTypeClass const Boulder3( + TEMPLATE_BOULDER3, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B3", + TXT_SLOPE +); +static TemplateTypeClass const Boulder4( + TEMPLATE_BOULDER4, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B4", + TXT_SLOPE +); +static TemplateTypeClass const Boulder5( + TEMPLATE_BOULDER5, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B5", + TXT_SLOPE +); +static TemplateTypeClass const Boulder6( + TEMPLATE_BOULDER6, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B6", + TXT_SLOPE +); +static TemplateTypeClass const Slope01( + TEMPLATE_SLOPE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S01", + TXT_SLOPE +); +static TemplateTypeClass const Slope02( + TEMPLATE_SLOPE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S02", + TXT_SLOPE +); +static TemplateTypeClass const Slope03( + TEMPLATE_SLOPE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S03", + TXT_SLOPE +); +static TemplateTypeClass const Slope04( + TEMPLATE_SLOPE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S04", + TXT_SLOPE +); +static TemplateTypeClass const Slope05( + TEMPLATE_SLOPE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S05", + TXT_SLOPE +); +static TemplateTypeClass const Slope06( + TEMPLATE_SLOPE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S06", + TXT_SLOPE +); +static TemplateTypeClass const Slope07( + TEMPLATE_SLOPE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S07", + TXT_SLOPE +); +static TemplateTypeClass const Slope08( + TEMPLATE_SLOPE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S08", + TXT_SLOPE +); +static TemplateTypeClass const Slope09( + TEMPLATE_SLOPE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S09", + TXT_SLOPE +); +static TemplateTypeClass const Slope10( + TEMPLATE_SLOPE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S10", + TXT_SLOPE +); +static TemplateTypeClass const Slope11( + TEMPLATE_SLOPE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S11", + TXT_SLOPE +); +static TemplateTypeClass const Slope12( + TEMPLATE_SLOPE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S12", + TXT_SLOPE +); +static TemplateTypeClass const Slope13( + TEMPLATE_SLOPE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S13", + TXT_SLOPE +); +static TemplateTypeClass const Slope14( + TEMPLATE_SLOPE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S14", + TXT_SLOPE +); +static TemplateTypeClass const Slope15( + TEMPLATE_SLOPE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S15", + TXT_SLOPE +); +static TemplateTypeClass const Slope16( + TEMPLATE_SLOPE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S16", + TXT_SLOPE +); +static TemplateTypeClass const Slope17( + TEMPLATE_SLOPE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S17", + TXT_SLOPE +); +static TemplateTypeClass const Slope18( + TEMPLATE_SLOPE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S18", + TXT_SLOPE +); +static TemplateTypeClass const Slope19( + TEMPLATE_SLOPE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S19", + TXT_SLOPE +); +static TemplateTypeClass const Slope20( + TEMPLATE_SLOPE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S20", + TXT_SLOPE +); +static TemplateTypeClass const Slope21( + TEMPLATE_SLOPE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S21", + TXT_SLOPE +); +static TemplateTypeClass const Slope22( + TEMPLATE_SLOPE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S22", + TXT_SLOPE +); +static TemplateTypeClass const Slope23( + TEMPLATE_SLOPE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S23", + TXT_SLOPE +); +static TemplateTypeClass const Slope24( + TEMPLATE_SLOPE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S24", + TXT_SLOPE +); +static TemplateTypeClass const Slope25( + TEMPLATE_SLOPE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S25", + TXT_SLOPE +); +static TemplateTypeClass const Slope26( + TEMPLATE_SLOPE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S26", + TXT_SLOPE +); +static TemplateTypeClass const Slope27( + TEMPLATE_SLOPE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S27", + TXT_SLOPE +); +static TemplateTypeClass const Slope28( + TEMPLATE_SLOPE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S28", + TXT_SLOPE +); +static TemplateTypeClass const Slope29( + TEMPLATE_SLOPE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S29", + TXT_SLOPE +); +static TemplateTypeClass const Slope30( + TEMPLATE_SLOPE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S30", + TXT_SLOPE +); +static TemplateTypeClass const Slope31( + TEMPLATE_SLOPE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S31", + TXT_SLOPE +); +static TemplateTypeClass const Slope32( + TEMPLATE_SLOPE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S32", + TXT_SLOPE +); +static TemplateTypeClass const Slope33( + TEMPLATE_SLOPE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S33", + TXT_SLOPE +); +static TemplateTypeClass const Slope34( + TEMPLATE_SLOPE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S34", + TXT_SLOPE +); +static TemplateTypeClass const Slope35( + TEMPLATE_SLOPE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S35", + TXT_SLOPE +); +static TemplateTypeClass const Slope36( + TEMPLATE_SLOPE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S36", + TXT_SLOPE +); +static TemplateTypeClass const Slope37( + TEMPLATE_SLOPE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S37", + TXT_SLOPE +); +static TemplateTypeClass const Slope38( + TEMPLATE_SLOPE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S38", + TXT_SLOPE +); +static TemplateTypeClass const Patch01( + TEMPLATE_PATCH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P01", + TXT_PATCH +); +static TemplateTypeClass const Patch02( + TEMPLATE_PATCH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P02", + TXT_PATCH +); +static TemplateTypeClass const Patch03( + TEMPLATE_PATCH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P03", + TXT_PATCH +); +static TemplateTypeClass const Patch04( + TEMPLATE_PATCH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P04", + TXT_PATCH +); +static TemplateTypeClass const Patch07( + TEMPLATE_PATCH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P07", + TXT_PATCH +); +static TemplateTypeClass const Patch08( + TEMPLATE_PATCH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P08", + TXT_PATCH +); +static TemplateTypeClass const Patch13( + TEMPLATE_PATCH13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P13", + TXT_PATCH +); +static TemplateTypeClass const Patch14( + TEMPLATE_PATCH14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P14", + TXT_PATCH +); +static TemplateTypeClass const Patch15( + TEMPLATE_PATCH15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P15", + TXT_PATCH +); +static TemplateTypeClass const River01( + TEMPLATE_RIVER01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV01", + TXT_RIVER +); +static TemplateTypeClass const River02( + TEMPLATE_RIVER02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV02", + TXT_RIVER +); +static TemplateTypeClass const River03( + TEMPLATE_RIVER03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV03", + TXT_RIVER +); +static TemplateTypeClass const River04( + TEMPLATE_RIVER04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV04", + TXT_RIVER +); +static TemplateTypeClass const River05( + TEMPLATE_RIVER05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV05", + TXT_RIVER +); +static TemplateTypeClass const River06( + TEMPLATE_RIVER06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV06", + TXT_RIVER +); +static TemplateTypeClass const River07( + TEMPLATE_RIVER07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV07", + TXT_RIVER +); +static TemplateTypeClass const River08( + TEMPLATE_RIVER08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV08", + TXT_RIVER +); +static TemplateTypeClass const River09( + TEMPLATE_RIVER09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV09", + TXT_RIVER +); +static TemplateTypeClass const River10( + TEMPLATE_RIVER10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV10", + TXT_RIVER +); +static TemplateTypeClass const River11( + TEMPLATE_RIVER11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV11", + TXT_RIVER +); +static TemplateTypeClass const River12( + TEMPLATE_RIVER12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV12", + TXT_RIVER +); +static TemplateTypeClass const River13( + TEMPLATE_RIVER13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV13", + TXT_RIVER +); +static TemplateTypeClass const River14( + TEMPLATE_RIVER14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV14", + TXT_RIVER +); +static TemplateTypeClass const River15( + TEMPLATE_RIVER15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV15", + TXT_RIVER +); +static TemplateTypeClass const Ford1( + TEMPLATE_FORD1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD1", + TXT_RIVER +); +static TemplateTypeClass const Ford2( + TEMPLATE_FORD2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD2", + TXT_RIVER +); +static TemplateTypeClass const Falls1( + TEMPLATE_FALLS1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1", + TXT_RIVER +); +static TemplateTypeClass const Falls1a( + TEMPLATE_FALLS1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1A", + TXT_RIVER +); +static TemplateTypeClass const Falls2( + TEMPLATE_FALLS2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2", + TXT_RIVER +); +static TemplateTypeClass const Falls2a( + TEMPLATE_FALLS2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2A", + TXT_RIVER +); +static TemplateTypeClass const Bridge1x( + TEMPLATE_BRIDGE1X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1( + TEMPLATE_BRIDGE1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1h( + TEMPLATE_BRIDGE1H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1d( + TEMPLATE_BRIDGE1D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2x( + TEMPLATE_BRIDGE2X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2( + TEMPLATE_BRIDGE2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2h( + TEMPLATE_BRIDGE2H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2d( + TEMPLATE_BRIDGE2D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1ax( + TEMPLATE_BRIDGE_1AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1a( + TEMPLATE_BRIDGE_1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1b( + TEMPLATE_BRIDGE_1B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1c( + TEMPLATE_BRIDGE_1C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2ax( + TEMPLATE_BRIDGE_2AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2a( + TEMPLATE_BRIDGE_2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2b( + TEMPLATE_BRIDGE_2B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2c( + TEMPLATE_BRIDGE_2C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3a( + TEMPLATE_BRIDGE_3A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3b( + TEMPLATE_BRIDGE_3B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3c( + TEMPLATE_BRIDGE_3C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3d( + TEMPLATE_BRIDGE_3D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3e( + TEMPLATE_BRIDGE_3E, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3E", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3f( + TEMPLATE_BRIDGE_3F, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3F", + TXT_BRIDGE +); +static TemplateTypeClass const ShoreCliff01( + TEMPLATE_SHORECLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC01", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff02( + TEMPLATE_SHORECLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC02", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff03( + TEMPLATE_SHORECLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC03", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff04( + TEMPLATE_SHORECLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC04", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff05( + TEMPLATE_SHORECLIFF05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC05", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff06( + TEMPLATE_SHORECLIFF06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC06", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff07( + TEMPLATE_SHORECLIFF07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC07", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff08( + TEMPLATE_SHORECLIFF08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC08", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff09( + TEMPLATE_SHORECLIFF09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC09", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff10( + TEMPLATE_SHORECLIFF10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC10", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff11( + TEMPLATE_SHORECLIFF11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC11", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff12( + TEMPLATE_SHORECLIFF12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC12", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff13( + TEMPLATE_SHORECLIFF13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC13", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff14( + TEMPLATE_SHORECLIFF14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC14", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff15( + TEMPLATE_SHORECLIFF15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC15", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff16( + TEMPLATE_SHORECLIFF16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC16", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff17( + TEMPLATE_SHORECLIFF17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC17", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff18( + TEMPLATE_SHORECLIFF18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC18", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff19( + TEMPLATE_SHORECLIFF19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC19", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff20( + TEMPLATE_SHORECLIFF20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC20", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff21( + TEMPLATE_SHORECLIFF21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC21", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff22( + TEMPLATE_SHORECLIFF22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC22", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff23( + TEMPLATE_SHORECLIFF23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC23", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff24( + TEMPLATE_SHORECLIFF24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC24", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff25( + TEMPLATE_SHORECLIFF25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC25", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff26( + TEMPLATE_SHORECLIFF26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC26", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff27( + TEMPLATE_SHORECLIFF27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC27", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff28( + TEMPLATE_SHORECLIFF28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC28", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff29( + TEMPLATE_SHORECLIFF29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC29", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff30( + TEMPLATE_SHORECLIFF30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC30", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff31( + TEMPLATE_SHORECLIFF31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC31", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff32( + TEMPLATE_SHORECLIFF32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC32", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff33( + TEMPLATE_SHORECLIFF33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC33", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff34( + TEMPLATE_SHORECLIFF34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC34", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff35( + TEMPLATE_SHORECLIFF35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC35", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff36( + TEMPLATE_SHORECLIFF36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC36", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff37( + TEMPLATE_SHORECLIFF37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC37", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff38( + TEMPLATE_SHORECLIFF38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC38", + TXT_SHORE +); +static TemplateTypeClass const Rough01( + TEMPLATE_ROUGH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF01", + TXT_ROCK +); +static TemplateTypeClass const Rough02( + TEMPLATE_ROUGH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF02", + TXT_ROCK +); +static TemplateTypeClass const Rough03( + TEMPLATE_ROUGH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF03", + TXT_ROCK +); +static TemplateTypeClass const Rough04( + TEMPLATE_ROUGH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF04", + TXT_ROCK +); +static TemplateTypeClass const Rough05( + TEMPLATE_ROUGH05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF05", + TXT_ROCK +); +static TemplateTypeClass const Rough06( + TEMPLATE_ROUGH06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF06", + TXT_ROCK +); +static TemplateTypeClass const Rough07( + TEMPLATE_ROUGH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF07", + TXT_ROCK +); +static TemplateTypeClass const Rough08( + TEMPLATE_ROUGH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF08", + TXT_ROCK +); +static TemplateTypeClass const Rough09( + TEMPLATE_ROUGH09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF09", + TXT_ROCK +); +static TemplateTypeClass const Rough10( + TEMPLATE_ROUGH10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF10", + TXT_ROCK +); +static TemplateTypeClass const Rough11( + TEMPLATE_ROUGH11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF11", + TXT_ROCK +); +static TemplateTypeClass const RiverCliff01( + TEMPLATE_RIVERCLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC01", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff02( + TEMPLATE_RIVERCLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC02", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff03( + TEMPLATE_RIVERCLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC03", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff04( + TEMPLATE_RIVERCLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC04", + TXT_RIVER +); + +static TemplateTypeClass const F01( + TEMPLATE_F01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F01", + TXT_RIVER +); +static TemplateTypeClass const F02( + TEMPLATE_F02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F02", + TXT_RIVER +); +static TemplateTypeClass const F03( + TEMPLATE_F03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F03", + TXT_RIVER +); +static TemplateTypeClass const F04( + TEMPLATE_F04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F04", + TXT_RIVER +); +static TemplateTypeClass const F05( + TEMPLATE_F05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F05", + TXT_RIVER +); +static TemplateTypeClass const F06( + TEMPLATE_F06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F06", + TXT_RIVER +); + +static TemplateTypeClass const ARRO0001( + TEMPLATE_ARRO0001, + THEATERF_INTERIOR, + "ARRO0001", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0002( + TEMPLATE_ARRO0002, + THEATERF_INTERIOR, + "ARRO0002", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0003( + TEMPLATE_ARRO0003, + THEATERF_INTERIOR, + "ARRO0003", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0004( + TEMPLATE_ARRO0004, + THEATERF_INTERIOR, + "ARRO0004", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0005( + TEMPLATE_ARRO0005, + THEATERF_INTERIOR, + "ARRO0005", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0006( + TEMPLATE_ARRO0006, + THEATERF_INTERIOR, + "ARRO0006", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0007( + TEMPLATE_ARRO0007, + THEATERF_INTERIOR, + "ARRO0007", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0008( + TEMPLATE_ARRO0008, + THEATERF_INTERIOR, + "ARRO0008", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0009( + TEMPLATE_ARRO0009, + THEATERF_INTERIOR, + "ARRO0009", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0010( + TEMPLATE_ARRO0010, + THEATERF_INTERIOR, + "ARRO0010", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0011( + TEMPLATE_ARRO0011, + THEATERF_INTERIOR, + "ARRO0011", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0012( + TEMPLATE_ARRO0012, + THEATERF_INTERIOR, + "ARRO0012", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0013( + TEMPLATE_ARRO0013, + THEATERF_INTERIOR, + "ARRO0013", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0014( + TEMPLATE_ARRO0014, + THEATERF_INTERIOR, + "ARRO0014", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0015( + TEMPLATE_ARRO0015, + THEATERF_INTERIOR, + "ARRO0015", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0001( + TEMPLATE_FLOR0001, + THEATERF_INTERIOR, + "FLOR0001", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0002( + TEMPLATE_FLOR0002, + THEATERF_INTERIOR, + "FLOR0002", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0003( + TEMPLATE_FLOR0003, + THEATERF_INTERIOR, + "FLOR0003", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0004( + TEMPLATE_FLOR0004, + THEATERF_INTERIOR, + "FLOR0004", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0005( + TEMPLATE_FLOR0005, + THEATERF_INTERIOR, + "FLOR0005", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0006( + TEMPLATE_FLOR0006, + THEATERF_INTERIOR, + "FLOR0006", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0007( + TEMPLATE_FLOR0007, + THEATERF_INTERIOR, + "FLOR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0001( + TEMPLATE_GFLR0001, + THEATERF_INTERIOR, + "GFLR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0002( + TEMPLATE_GFLR0002, + THEATERF_INTERIOR, + "GFLR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0003( + TEMPLATE_GFLR0003, + THEATERF_INTERIOR, + "GFLR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0004( + TEMPLATE_GFLR0004, + THEATERF_INTERIOR, + "GFLR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0005( + TEMPLATE_GFLR0005, + THEATERF_INTERIOR, + "GFLR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0001( + TEMPLATE_GSTR0001, + THEATERF_INTERIOR, + "GSTR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0002( + TEMPLATE_GSTR0002, + THEATERF_INTERIOR, + "GSTR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0003( + TEMPLATE_GSTR0003, + THEATERF_INTERIOR, + "GSTR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0004( + TEMPLATE_GSTR0004, + THEATERF_INTERIOR, + "GSTR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0005( + TEMPLATE_GSTR0005, + THEATERF_INTERIOR, + "GSTR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0006( + TEMPLATE_GSTR0006, + THEATERF_INTERIOR, + "GSTR0006", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0007( + TEMPLATE_GSTR0007, + THEATERF_INTERIOR, + "GSTR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0008( + TEMPLATE_GSTR0008, + THEATERF_INTERIOR, + "GSTR0008", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0009( + TEMPLATE_GSTR0009, + THEATERF_INTERIOR, + "GSTR0009", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0010( + TEMPLATE_GSTR0010, + THEATERF_INTERIOR, + "GSTR0010", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0011( + TEMPLATE_GSTR0011, + THEATERF_INTERIOR, + "GSTR0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0001( + TEMPLATE_LWAL0001, + THEATERF_INTERIOR, + "LWAL0001", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0002( + TEMPLATE_LWAL0002, + THEATERF_INTERIOR, + "LWAL0002", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0003( + TEMPLATE_LWAL0003, + THEATERF_INTERIOR, + "LWAL0003", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0004( + TEMPLATE_LWAL0004, + THEATERF_INTERIOR, + "LWAL0004", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0005( + TEMPLATE_LWAL0005, + THEATERF_INTERIOR, + "LWAL0005", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0006( + TEMPLATE_LWAL0006, + THEATERF_INTERIOR, + "LWAL0006", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0007( + TEMPLATE_LWAL0007, + THEATERF_INTERIOR, + "LWAL0007", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0008( + TEMPLATE_LWAL0008, + THEATERF_INTERIOR, + "LWAL0008", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0009( + TEMPLATE_LWAL0009, + THEATERF_INTERIOR, + "LWAL0009", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0010( + TEMPLATE_LWAL0010, + THEATERF_INTERIOR, + "LWAL0010", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0011( + TEMPLATE_LWAL0011, + THEATERF_INTERIOR, + "LWAL0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0012( + TEMPLATE_LWAL0012, + THEATERF_INTERIOR, + "LWAL0012", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0013( + TEMPLATE_LWAL0013, + THEATERF_INTERIOR, + "LWAL0013", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0014( + TEMPLATE_LWAL0014, + THEATERF_INTERIOR, + "LWAL0014", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0015( + TEMPLATE_LWAL0015, + THEATERF_INTERIOR, + "LWAL0015", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0016( + TEMPLATE_LWAL0016, + THEATERF_INTERIOR, + "LWAL0016", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0017( + TEMPLATE_LWAL0017, + THEATERF_INTERIOR, + "LWAL0017", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0018( + TEMPLATE_LWAL0018, + THEATERF_INTERIOR, + "LWAL0018", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0019( + TEMPLATE_LWAL0019, + THEATERF_INTERIOR, + "LWAL0019", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0020( + TEMPLATE_LWAL0020, + THEATERF_INTERIOR, + "LWAL0020", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0021( + TEMPLATE_LWAL0021, + THEATERF_INTERIOR, + "LWAL0021", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0022( + TEMPLATE_LWAL0022, + THEATERF_INTERIOR, + "LWAL0022", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0023( + TEMPLATE_LWAL0023, + THEATERF_INTERIOR, + "LWAL0023", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0024( + TEMPLATE_LWAL0024, + THEATERF_INTERIOR, + "LWAL0024", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0025( + TEMPLATE_LWAL0025, + THEATERF_INTERIOR, + "LWAL0025", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0026( + TEMPLATE_LWAL0026, + THEATERF_INTERIOR, + "LWAL0026", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0027( + TEMPLATE_LWAL0027, + THEATERF_INTERIOR, + "LWAL0027", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0001( + TEMPLATE_STRP0001, + THEATERF_INTERIOR, + "STRP0001", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0002( + TEMPLATE_STRP0002, + THEATERF_INTERIOR, + "STRP0002", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0003( + TEMPLATE_STRP0003, + THEATERF_INTERIOR, + "STRP0003", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0004( + TEMPLATE_STRP0004, + THEATERF_INTERIOR, + "STRP0004", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0005( + TEMPLATE_STRP0005, + THEATERF_INTERIOR, + "STRP0005", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0006( + TEMPLATE_STRP0006, + THEATERF_INTERIOR, + "STRP0006", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0007( + TEMPLATE_STRP0007, + THEATERF_INTERIOR, + "STRP0007", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0008( + TEMPLATE_STRP0008, + THEATERF_INTERIOR, + "STRP0008", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0009( + TEMPLATE_STRP0009, + THEATERF_INTERIOR, + "STRP0009", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0010( + TEMPLATE_STRP0010, + THEATERF_INTERIOR, + "STRP0010", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0011( + TEMPLATE_STRP0011, + THEATERF_INTERIOR, + "STRP0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0001( + TEMPLATE_WALL0001, + THEATERF_INTERIOR, + "WALL0001", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0002( + TEMPLATE_WALL0002, + THEATERF_INTERIOR, + "WALL0002", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0003( + TEMPLATE_WALL0003, + THEATERF_INTERIOR, + "WALL0003", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0004( + TEMPLATE_WALL0004, + THEATERF_INTERIOR, + "WALL0004", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0005( + TEMPLATE_WALL0005, + THEATERF_INTERIOR, + "WALL0005", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0006( + TEMPLATE_WALL0006, + THEATERF_INTERIOR, + "WALL0006", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0007( + TEMPLATE_WALL0007, + THEATERF_INTERIOR, + "WALL0007", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0008( + TEMPLATE_WALL0008, + THEATERF_INTERIOR, + "WALL0008", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0009( + TEMPLATE_WALL0009, + THEATERF_INTERIOR, + "WALL0009", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0010( + TEMPLATE_WALL0010, + THEATERF_INTERIOR, + "WALL0010", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0011( + TEMPLATE_WALL0011, + THEATERF_INTERIOR, + "WALL0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0012( + TEMPLATE_WALL0012, + THEATERF_INTERIOR, + "WALL0012", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0013( + TEMPLATE_WALL0013, + THEATERF_INTERIOR, + "WALL0013", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0014( + TEMPLATE_WALL0014, + THEATERF_INTERIOR, + "WALL0014", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0015( + TEMPLATE_WALL0015, + THEATERF_INTERIOR, + "WALL0015", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0016( + TEMPLATE_WALL0016, + THEATERF_INTERIOR, + "WALL0016", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0017( + TEMPLATE_WALL0017, + THEATERF_INTERIOR, + "WALL0017", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0018( + TEMPLATE_WALL0018, + THEATERF_INTERIOR, + "WALL0018", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0019( + TEMPLATE_WALL0019, + THEATERF_INTERIOR, + "WALL0019", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0020( + TEMPLATE_WALL0020, + THEATERF_INTERIOR, + "WALL0020", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0021( + TEMPLATE_WALL0021, + THEATERF_INTERIOR, + "WALL0021", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0022( + TEMPLATE_WALL0022, + THEATERF_INTERIOR, + "WALL0022", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0023( + TEMPLATE_WALL0023, + THEATERF_INTERIOR, + "WALL0023", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0024( + TEMPLATE_WALL0024, + THEATERF_INTERIOR, + "WALL0024", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0025( + TEMPLATE_WALL0025, + THEATERF_INTERIOR, + "WALL0025", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0026( + TEMPLATE_WALL0026, + THEATERF_INTERIOR, + "WALL0026", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0027( + TEMPLATE_WALL0027, + THEATERF_INTERIOR, + "WALL0027", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0028( + TEMPLATE_WALL0028, + THEATERF_INTERIOR, + "WALL0028", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0029( + TEMPLATE_WALL0029, + THEATERF_INTERIOR, + "WALL0029", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0030( + TEMPLATE_WALL0030, + THEATERF_INTERIOR, + "WALL0030", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0031( + TEMPLATE_WALL0031, + THEATERF_INTERIOR, + "WALL0031", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0032( + TEMPLATE_WALL0032, + THEATERF_INTERIOR, + "WALL0032", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0033( + TEMPLATE_WALL0033, + THEATERF_INTERIOR, + "WALL0033", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0034( + TEMPLATE_WALL0034, + THEATERF_INTERIOR, + "WALL0034", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0035( + TEMPLATE_WALL0035, + THEATERF_INTERIOR, + "WALL0035", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0036( + TEMPLATE_WALL0036, + THEATERF_INTERIOR, + "WALL0036", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0037( + TEMPLATE_WALL0037, + THEATERF_INTERIOR, + "WALL0037", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0038( + TEMPLATE_WALL0038, + THEATERF_INTERIOR, + "WALL0038", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0039( + TEMPLATE_WALL0039, + THEATERF_INTERIOR, + "WALL0039", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0040( + TEMPLATE_WALL0040, + THEATERF_INTERIOR, + "WALL0040", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0041( + TEMPLATE_WALL0041, + THEATERF_INTERIOR, + "WALL0041", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0042( + TEMPLATE_WALL0042, + THEATERF_INTERIOR, + "WALL0042", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0043( + TEMPLATE_WALL0043, + THEATERF_INTERIOR, + "WALL0043", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0044( + TEMPLATE_WALL0044, + THEATERF_INTERIOR, + "WALL0044", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0045( + TEMPLATE_WALL0045, + THEATERF_INTERIOR, + "WALL0045", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0046( + TEMPLATE_WALL0046, + THEATERF_INTERIOR, + "WALL0046", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0047( + TEMPLATE_WALL0047, + THEATERF_INTERIOR, + "WALL0047", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0048( + TEMPLATE_WALL0048, + THEATERF_INTERIOR, + "WALL0048", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0049( + TEMPLATE_WALL0049, + THEATERF_INTERIOR, + "WALL0049", + TXT_INTERIOR +); + +static TemplateTypeClass const Xtra0001( + TEMPLATE_XTRA0001, + THEATERF_INTERIOR, + "XTRA0001", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0002( + TEMPLATE_XTRA0002, + THEATERF_INTERIOR, + "XTRA0002", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0003( + TEMPLATE_XTRA0003, + THEATERF_INTERIOR, + "XTRA0003", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0004( + TEMPLATE_XTRA0004, + THEATERF_INTERIOR, + "XTRA0004", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0005( + TEMPLATE_XTRA0005, + THEATERF_INTERIOR, + "XTRA0005", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0006( + TEMPLATE_XTRA0006, + THEATERF_INTERIOR, + "XTRA0006", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0007( + TEMPLATE_XTRA0007, + THEATERF_INTERIOR, + "XTRA0007", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0008( + TEMPLATE_XTRA0008, + THEATERF_INTERIOR, + "XTRA0008", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0009( + TEMPLATE_XTRA0009, + THEATERF_INTERIOR, + "XTRA0009", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0010( + TEMPLATE_XTRA0010, + THEATERF_INTERIOR, + "XTRA0010", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0011( + TEMPLATE_XTRA0011, + THEATERF_INTERIOR, + "XTRA0011", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0012( + TEMPLATE_XTRA0012, + THEATERF_INTERIOR, + "XTRA0012", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0013( + TEMPLATE_XTRA0013, + THEATERF_INTERIOR, + "XTRA0013", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0014( + TEMPLATE_XTRA0014, + THEATERF_INTERIOR, + "XTRA0014", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0015( + TEMPLATE_XTRA0015, + THEATERF_INTERIOR, + "XTRA0015", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0016( + TEMPLATE_XTRA0016, + THEATERF_INTERIOR, + "XTRA0016", + TXT_INTERIOR +); + +#ifdef FIXIT_ANTS +static TemplateTypeClass const AntHill( + TEMPLATE_HILL01, + THEATERF_TEMPERATE, + "HILL01", + TXT_ROCK +); +#endif + +/*********************************************************************************************** + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * * + * This is the constructor for the template types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass::TemplateTypeClass( + TemplateType iconset, + int theater, + char const * ininame, + int fullname) : + ObjectTypeClass( + RTTI_TEMPLATETYPE, + int(iconset), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(iconset), + Theater(theater), + Width(0), + Height(0) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * * + * This allocates a template type object from the special heap used for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated template type object. If no object * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * TemplateTypeClass::operator new(size_t) +{ + return(TemplateTypes.Alloc()); +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * * + * This routine will return a template type object back to the special heap it was * + * allocated from. * + * * + * INPUT: ptr -- Pointer to the template type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::operator delete(void * ptr) +{ + TemplateTypes.Free((TemplateTypeClass *)ptr); +} + + +static void _Watcom_Ugh_Hack(void) +{ + (void)new TemplateTypeClass(Road37); // TEMPLATE_ROAD37 + (void)new TemplateTypeClass(Road38); // TEMPLATE_ROAD38 + (void)new TemplateTypeClass(Road39); // TEMPLATE_ROAD39 + (void)new TemplateTypeClass(Road40); // TEMPLATE_ROAD40 + (void)new TemplateTypeClass(Road41); // TEMPLATE_ROAD41 + (void)new TemplateTypeClass(Road42); // TEMPLATE_ROAD42 + (void)new TemplateTypeClass(Road43); // TEMPLATE_ROAD43 + (void)new TemplateTypeClass(Rough01); // TEMPLATE_ROUGH01 + (void)new TemplateTypeClass(Rough02); // TEMPLATE_ROUGH02 + (void)new TemplateTypeClass(Rough03); // TEMPLATE_ROUGH03 + (void)new TemplateTypeClass(Rough04); // TEMPLATE_ROUGH04 + (void)new TemplateTypeClass(Rough05); // TEMPLATE_ROUGH05 + (void)new TemplateTypeClass(Rough06); // TEMPLATE_ROUGH06 + (void)new TemplateTypeClass(Rough07); // TEMPLATE_ROUGH07 + (void)new TemplateTypeClass(Rough08); // TEMPLATE_ROUGH08 + (void)new TemplateTypeClass(Rough09); // TEMPLATE_ROUGH09 + (void)new TemplateTypeClass(Rough10); // TEMPLATE_ROUGH10 + (void)new TemplateTypeClass(Rough11); // TEMPLATE_ROUGH11 + (void)new TemplateTypeClass(Road44); // TEMPLATE_ROAD44 + (void)new TemplateTypeClass(Road45); // TEMPLATE_ROAD45 + (void)new TemplateTypeClass(River14); // TEMPLATE_RIVER14 + (void)new TemplateTypeClass(River15); // TEMPLATE_RIVER15 + (void)new TemplateTypeClass(RiverCliff01); // TEMPLATE_RIVERCLIFF01 + (void)new TemplateTypeClass(RiverCliff02); // TEMPLATE_RIVERCLIFF02 + (void)new TemplateTypeClass(RiverCliff03); // TEMPLATE_RIVERCLIFF03 + (void)new TemplateTypeClass(RiverCliff04); // TEMPLATE_RIVERCLIFF04 + (void)new TemplateTypeClass(Bridge1a); // TEMPLATE_BRIDGE_1A + (void)new TemplateTypeClass(Bridge1b); // TEMPLATE_BRIDGE_1B + (void)new TemplateTypeClass(Bridge1c); // TEMPLATE_BRIDGE_1C + (void)new TemplateTypeClass(Bridge2a); // TEMPLATE_BRIDGE_2A + (void)new TemplateTypeClass(Bridge2b); // TEMPLATE_BRIDGE_2B + (void)new TemplateTypeClass(Bridge2c); // TEMPLATE_BRIDGE_2C + (void)new TemplateTypeClass(Bridge3a); // TEMPLATE_BRIDGE_3A + (void)new TemplateTypeClass(Bridge3b); // TEMPLATE_BRIDGE_3B + (void)new TemplateTypeClass(Bridge3c); // TEMPLATE_BRIDGE_3C + (void)new TemplateTypeClass(Bridge3d); // TEMPLATE_BRIDGE_3D + (void)new TemplateTypeClass(Bridge3e); // TEMPLATE_BRIDGE_3E + (void)new TemplateTypeClass(Bridge3f); // TEMPLATE_BRIDGE_3F + (void)new TemplateTypeClass(F01); // TEMPLATE_F01 + (void)new TemplateTypeClass(F02); // TEMPLATE_F02 + (void)new TemplateTypeClass(F03); // TEMPLATE_F03 + (void)new TemplateTypeClass(F04); // TEMPLATE_F04 + (void)new TemplateTypeClass(F05); // TEMPLATE_F05 + (void)new TemplateTypeClass(F06); // TEMPLATE_F06 + (void)new TemplateTypeClass(ARRO0001); // TEMPLATE_ARRO0001 + (void)new TemplateTypeClass(ARRO0002); // TEMPLATE_ARRO0002 + (void)new TemplateTypeClass(ARRO0003); // TEMPLATE_ARRO0003 + (void)new TemplateTypeClass(ARRO0004); // TEMPLATE_ARRO0004 + (void)new TemplateTypeClass(ARRO0005); // TEMPLATE_ARRO0005 + (void)new TemplateTypeClass(ARRO0006); // TEMPLATE_ARRO0006 + (void)new TemplateTypeClass(ARRO0007); // TEMPLATE_ARRO0007 + (void)new TemplateTypeClass(ARRO0008); // TEMPLATE_ARRO0008 + (void)new TemplateTypeClass(ARRO0009); // TEMPLATE_ARRO0009 + (void)new TemplateTypeClass(ARRO0010); // TEMPLATE_ARRO0010 + (void)new TemplateTypeClass(ARRO0011); // TEMPLATE_ARRO0011 + (void)new TemplateTypeClass(ARRO0012); // TEMPLATE_ARRO0012 + (void)new TemplateTypeClass(ARRO0013); // TEMPLATE_ARRO0013 + (void)new TemplateTypeClass(ARRO0014); // TEMPLATE_ARRO0014 + (void)new TemplateTypeClass(ARRO0015); // TEMPLATE_ARRO0015 + (void)new TemplateTypeClass(FLOR0001); // TEMPLATE_FLOR0001 + (void)new TemplateTypeClass(FLOR0002); // TEMPLATE_FLOR0002 + (void)new TemplateTypeClass(FLOR0003); // TEMPLATE_FLOR0003 + (void)new TemplateTypeClass(FLOR0004); // TEMPLATE_FLOR0004 + (void)new TemplateTypeClass(FLOR0005); // TEMPLATE_FLOR0005 + (void)new TemplateTypeClass(FLOR0006); // TEMPLATE_FLOR0006 + (void)new TemplateTypeClass(FLOR0007); // TEMPLATE_FLOR0007 + (void)new TemplateTypeClass(GFLR0001); // TEMPLATE_GFLR0001 + (void)new TemplateTypeClass(GFLR0002); // TEMPLATE_GFLR0002 + (void)new TemplateTypeClass(GFLR0003); // TEMPLATE_GFLR0003 + (void)new TemplateTypeClass(GFLR0004); // TEMPLATE_GFLR0004 + (void)new TemplateTypeClass(GFLR0005); // TEMPLATE_GFLR0005 + (void)new TemplateTypeClass(GSTR0001); // TEMPLATE_GSTR0001 + (void)new TemplateTypeClass(GSTR0002); // TEMPLATE_GSTR0002 + (void)new TemplateTypeClass(GSTR0003); // TEMPLATE_GSTR0003 + (void)new TemplateTypeClass(GSTR0004); // TEMPLATE_GSTR0004 + (void)new TemplateTypeClass(GSTR0005); // TEMPLATE_GSTR0005 + (void)new TemplateTypeClass(GSTR0006); // TEMPLATE_GSTR0006 + (void)new TemplateTypeClass(GSTR0007); // TEMPLATE_GSTR0007 + (void)new TemplateTypeClass(GSTR0008); // TEMPLATE_GSTR0008 + (void)new TemplateTypeClass(GSTR0009); // TEMPLATE_GSTR0009 + (void)new TemplateTypeClass(GSTR0010); // TEMPLATE_GSTR0010 + (void)new TemplateTypeClass(GSTR0011); // TEMPLATE_GSTR0011 + (void)new TemplateTypeClass(LWAL0001); // TEMPLATE_LWAL0001 + (void)new TemplateTypeClass(LWAL0002); // TEMPLATE_LWAL0002 + (void)new TemplateTypeClass(LWAL0003); // TEMPLATE_LWAL0003 + (void)new TemplateTypeClass(LWAL0004); // TEMPLATE_LWAL0004 + (void)new TemplateTypeClass(LWAL0005); // TEMPLATE_LWAL0005 + (void)new TemplateTypeClass(LWAL0006); // TEMPLATE_LWAL0006 + (void)new TemplateTypeClass(LWAL0007); // TEMPLATE_LWAL0007 + (void)new TemplateTypeClass(LWAL0008); // TEMPLATE_LWAL0008 + (void)new TemplateTypeClass(LWAL0009); // TEMPLATE_LWAL0009 + (void)new TemplateTypeClass(LWAL0010); // TEMPLATE_LWAL0010 + (void)new TemplateTypeClass(LWAL0011); // TEMPLATE_LWAL0011 + (void)new TemplateTypeClass(LWAL0012); // TEMPLATE_LWAL0012 + (void)new TemplateTypeClass(LWAL0013); // TEMPLATE_LWAL0013 + (void)new TemplateTypeClass(LWAL0014); // TEMPLATE_LWAL0014 + (void)new TemplateTypeClass(LWAL0015); // TEMPLATE_LWAL0015 + (void)new TemplateTypeClass(LWAL0016); // TEMPLATE_LWAL0016 + (void)new TemplateTypeClass(LWAL0017); // TEMPLATE_LWAL0017 + (void)new TemplateTypeClass(LWAL0018); // TEMPLATE_LWAL0018 + (void)new TemplateTypeClass(LWAL0019); // TEMPLATE_LWAL0019 + (void)new TemplateTypeClass(LWAL0020); // TEMPLATE_LWAL0020 + (void)new TemplateTypeClass(LWAL0021); // TEMPLATE_LWAL0021 + (void)new TemplateTypeClass(LWAL0022); // TEMPLATE_LWAL0022 + (void)new TemplateTypeClass(LWAL0023); // TEMPLATE_LWAL0023 + (void)new TemplateTypeClass(LWAL0024); // TEMPLATE_LWAL0024 + (void)new TemplateTypeClass(LWAL0025); // TEMPLATE_LWAL0025 + (void)new TemplateTypeClass(LWAL0026); // TEMPLATE_LWAL0026 + (void)new TemplateTypeClass(LWAL0027); // TEMPLATE_LWAL0027 + (void)new TemplateTypeClass(STRP0001); // TEMPLATE_STRP0001 + (void)new TemplateTypeClass(STRP0002); // TEMPLATE_STRP0002 + (void)new TemplateTypeClass(STRP0003); // TEMPLATE_STRP0003 + (void)new TemplateTypeClass(STRP0004); // TEMPLATE_STRP0004 + (void)new TemplateTypeClass(STRP0005); // TEMPLATE_STRP0005 + (void)new TemplateTypeClass(STRP0006); // TEMPLATE_STRP0006 + (void)new TemplateTypeClass(STRP0007); // TEMPLATE_STRP0007 + (void)new TemplateTypeClass(STRP0008); // TEMPLATE_STRP0008 + (void)new TemplateTypeClass(STRP0009); // TEMPLATE_STRP0009 + (void)new TemplateTypeClass(STRP0010); // TEMPLATE_STRP0010 + (void)new TemplateTypeClass(STRP0011); // TEMPLATE_STRP0011 + (void)new TemplateTypeClass(WALL0001); // TEMPLATE_WALL0001 + (void)new TemplateTypeClass(WALL0002); // TEMPLATE_WALL0002 + (void)new TemplateTypeClass(WALL0003); // TEMPLATE_WALL0003 + (void)new TemplateTypeClass(WALL0004); // TEMPLATE_WALL0004 + (void)new TemplateTypeClass(WALL0005); // TEMPLATE_WALL0005 + (void)new TemplateTypeClass(WALL0006); // TEMPLATE_WALL0006 + (void)new TemplateTypeClass(WALL0007); // TEMPLATE_WALL0007 + (void)new TemplateTypeClass(WALL0008); // TEMPLATE_WALL0008 + (void)new TemplateTypeClass(WALL0009); // TEMPLATE_WALL0009 + (void)new TemplateTypeClass(WALL0010); // TEMPLATE_WALL0010 + (void)new TemplateTypeClass(WALL0011); // TEMPLATE_WALL0011 + (void)new TemplateTypeClass(WALL0012); // TEMPLATE_WALL0012 + (void)new TemplateTypeClass(WALL0013); // TEMPLATE_WALL0013 + (void)new TemplateTypeClass(WALL0014); // TEMPLATE_WALL0014 + (void)new TemplateTypeClass(WALL0015); // TEMPLATE_WALL0015 + (void)new TemplateTypeClass(WALL0016); // TEMPLATE_WALL0016 + (void)new TemplateTypeClass(WALL0017); // TEMPLATE_WALL0017 + (void)new TemplateTypeClass(WALL0018); // TEMPLATE_WALL0018 + (void)new TemplateTypeClass(WALL0019); // TEMPLATE_WALL0019 + (void)new TemplateTypeClass(WALL0020); // TEMPLATE_WALL0020 + (void)new TemplateTypeClass(WALL0021); // TEMPLATE_WALL0021 + (void)new TemplateTypeClass(WALL0022); // TEMPLATE_WALL0022 + (void)new TemplateTypeClass(WALL0023); // TEMPLATE_WALL0023 + (void)new TemplateTypeClass(WALL0024); // TEMPLATE_WALL0024 + (void)new TemplateTypeClass(WALL0025); // TEMPLATE_WALL0025 + (void)new TemplateTypeClass(WALL0026); // TEMPLATE_WALL0026 + (void)new TemplateTypeClass(WALL0027); // TEMPLATE_WALL0027 + (void)new TemplateTypeClass(WALL0028); // TEMPLATE_WALL0028 + (void)new TemplateTypeClass(WALL0029); // TEMPLATE_WALL0029 + (void)new TemplateTypeClass(WALL0030); // TEMPLATE_WALL0030 + (void)new TemplateTypeClass(WALL0031); // TEMPLATE_WALL0031 + (void)new TemplateTypeClass(WALL0032); // TEMPLATE_WALL0032 + (void)new TemplateTypeClass(WALL0033); // TEMPLATE_WALL0033 + (void)new TemplateTypeClass(WALL0034); // TEMPLATE_WALL0034 + (void)new TemplateTypeClass(WALL0035); // TEMPLATE_WALL0035 + (void)new TemplateTypeClass(WALL0036); // TEMPLATE_WALL0036 + (void)new TemplateTypeClass(WALL0037); // TEMPLATE_WALL0037 + (void)new TemplateTypeClass(WALL0038); // TEMPLATE_WALL0038 + (void)new TemplateTypeClass(WALL0039); // TEMPLATE_WALL0039 + (void)new TemplateTypeClass(WALL0040); // TEMPLATE_WALL0040 + (void)new TemplateTypeClass(WALL0041); // TEMPLATE_WALL0041 + (void)new TemplateTypeClass(WALL0042); // TEMPLATE_WALL0042 + (void)new TemplateTypeClass(WALL0043); // TEMPLATE_WALL0043 + (void)new TemplateTypeClass(WALL0044); // TEMPLATE_WALL0044 + (void)new TemplateTypeClass(WALL0045); // TEMPLATE_WALL0045 + (void)new TemplateTypeClass(WALL0046); // TEMPLATE_WALL0046 + (void)new TemplateTypeClass(WALL0047); // TEMPLATE_WALL0047 + (void)new TemplateTypeClass(WALL0048); // TEMPLATE_WALL0048 + (void)new TemplateTypeClass(WALL0049); // TEMPLATE_WALL0049 + (void)new TemplateTypeClass(Bridge1h); // TEMPLATE_BRIDGE1H + (void)new TemplateTypeClass(Bridge2h); // TEMPLATE_BRIDGE2H + + (void)new TemplateTypeClass(Bridge1ax); // TEMPLATE_BRIDGE_1AX + (void)new TemplateTypeClass(Bridge2ax); // TEMPLATE_BRIDGE_2AX + (void)new TemplateTypeClass(Bridge1x); // TEMPLATE_BRIDGE1X + (void)new TemplateTypeClass(Bridge2x); // TEMPLATE_BRIDGE2X + + (void)new TemplateTypeClass(Xtra0001); // TEMPLATE_XTRA0001 + (void)new TemplateTypeClass(Xtra0002); // TEMPLATE_XTRA0002 + (void)new TemplateTypeClass(Xtra0003); // TEMPLATE_XTRA0003 + (void)new TemplateTypeClass(Xtra0004); // TEMPLATE_XTRA0004 + (void)new TemplateTypeClass(Xtra0005); // TEMPLATE_XTRA0005 + (void)new TemplateTypeClass(Xtra0006); // TEMPLATE_XTRA0006 + (void)new TemplateTypeClass(Xtra0007); // TEMPLATE_XTRA0007 + (void)new TemplateTypeClass(Xtra0008); // TEMPLATE_XTRA0008 + (void)new TemplateTypeClass(Xtra0009); // TEMPLATE_XTRA0009 + (void)new TemplateTypeClass(Xtra0010); // TEMPLATE_XTRA0010 + (void)new TemplateTypeClass(Xtra0011); // TEMPLATE_XTRA0011 + (void)new TemplateTypeClass(Xtra0012); // TEMPLATE_XTRA0012 + (void)new TemplateTypeClass(Xtra0013); // TEMPLATE_XTRA0013 + (void)new TemplateTypeClass(Xtra0014); // TEMPLATE_XTRA0014 + (void)new TemplateTypeClass(Xtra0015); // TEMPLATE_XTRA0015 + (void)new TemplateTypeClass(Xtra0016); // TEMPLATE_XTRA0016 + +#ifdef FIXIT_ANTS + (void)new TemplateTypeClass(AntHill); // TEMPLATE_ROAD36 +#endif +} + + + +void TemplateTypeClass::Init_Heap(void) +{ + /* + ** These template type class objects must be allocated in the exact order that they + ** are specified in the TemplateType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + (void)new TemplateTypeClass(Clear); // TEMPLATE_CLEAR1 + (void)new TemplateTypeClass(Water); // TEMPLATE_WATER + (void)new TemplateTypeClass(Water2); // TEMPLATE_WATER2 + (void)new TemplateTypeClass(Shore01); // TEMPLATE_SHORE1 + (void)new TemplateTypeClass(Shore02); // TEMPLATE_SHORE2 + (void)new TemplateTypeClass(Shore03); // TEMPLATE_SHORE3 + (void)new TemplateTypeClass(Shore04); // TEMPLATE_SHORE4 + (void)new TemplateTypeClass(Shore05); // TEMPLATE_SHORE5 + (void)new TemplateTypeClass(Shore06); // TEMPLATE_SHORE6 + (void)new TemplateTypeClass(Shore07); // TEMPLATE_SHORE7 + (void)new TemplateTypeClass(Shore08); // TEMPLATE_SHORE8 + (void)new TemplateTypeClass(Shore09); // TEMPLATE_SHORE9 + (void)new TemplateTypeClass(Shore10); // TEMPLATE_SHORE10 + (void)new TemplateTypeClass(Shore11); // TEMPLATE_SHORE11 + (void)new TemplateTypeClass(Shore12); // TEMPLATE_SHORE12 + (void)new TemplateTypeClass(Shore13); // TEMPLATE_SHORE13 + (void)new TemplateTypeClass(Shore14); // TEMPLATE_SHORE14 + (void)new TemplateTypeClass(Shore15); // TEMPLATE_SHORE15 + (void)new TemplateTypeClass(Shore16); // TEMPLATE_SHORE16 + (void)new TemplateTypeClass(Shore17); // TEMPLATE_SHORE17 + (void)new TemplateTypeClass(Shore18); // TEMPLATE_SHORE18 + (void)new TemplateTypeClass(Shore19); // TEMPLATE_SHORE19 + (void)new TemplateTypeClass(Shore20); // TEMPLATE_SHORE20 + (void)new TemplateTypeClass(Shore21); // TEMPLATE_SHORE21 + (void)new TemplateTypeClass(Shore22); // TEMPLATE_SHORE22 + (void)new TemplateTypeClass(Shore23); // TEMPLATE_SHORE23 + (void)new TemplateTypeClass(Shore24); // TEMPLATE_SHORE24 + (void)new TemplateTypeClass(Shore25); // TEMPLATE_SHORE25 + (void)new TemplateTypeClass(Shore26); // TEMPLATE_SHORE26 + (void)new TemplateTypeClass(Shore27); // TEMPLATE_SHORE27 + (void)new TemplateTypeClass(Shore28); // TEMPLATE_SHORE28 + (void)new TemplateTypeClass(Shore29); // TEMPLATE_SHORE29 + (void)new TemplateTypeClass(Shore30); // TEMPLATE_SHORE30 + (void)new TemplateTypeClass(Shore31); // TEMPLATE_SHORE31 + (void)new TemplateTypeClass(Shore32); // TEMPLATE_SHORE32 + (void)new TemplateTypeClass(Shore33); // TEMPLATE_SHORE33 + (void)new TemplateTypeClass(Shore34); // TEMPLATE_SHORE34 + (void)new TemplateTypeClass(Shore35); // TEMPLATE_SHORE35 + (void)new TemplateTypeClass(Shore36); // TEMPLATE_SHORE36 + (void)new TemplateTypeClass(Shore37); // TEMPLATE_SHORE37 + (void)new TemplateTypeClass(Shore38); // TEMPLATE_SHORE38 + (void)new TemplateTypeClass(Shore39); // TEMPLATE_SHORE39 + (void)new TemplateTypeClass(Shore40); // TEMPLATE_SHORE40 + (void)new TemplateTypeClass(Shore41); // TEMPLATE_SHORE41 + (void)new TemplateTypeClass(Shore42); // TEMPLATE_SHORE42 + (void)new TemplateTypeClass(Shore43); // TEMPLATE_SHORE43 + (void)new TemplateTypeClass(Shore44); // TEMPLATE_SHORE44 + (void)new TemplateTypeClass(Shore45); // TEMPLATE_SHORE45 + (void)new TemplateTypeClass(Shore46); // TEMPLATE_SHORE46 + (void)new TemplateTypeClass(Shore47); // TEMPLATE_SHORE47 + (void)new TemplateTypeClass(Shore48); // TEMPLATE_SHORE48 + (void)new TemplateTypeClass(Shore49); // TEMPLATE_SHORE49 + (void)new TemplateTypeClass(Shore50); // TEMPLATE_SHORE50 + (void)new TemplateTypeClass(Shore51); // TEMPLATE_SHORE51 + (void)new TemplateTypeClass(Shore52); // TEMPLATE_SHORE52 + (void)new TemplateTypeClass(Shore53); // TEMPLATE_SHORE53 + (void)new TemplateTypeClass(Shore54); // TEMPLATE_SHORE54 + (void)new TemplateTypeClass(Shore55); // TEMPLATE_SHORE55 + (void)new TemplateTypeClass(Shore56); // TEMPLATE_SHORE56 + (void)new TemplateTypeClass(ShoreCliff01); // TEMPLATE_SHORECLIFF01 + (void)new TemplateTypeClass(ShoreCliff02); // TEMPLATE_SHORECLIFF02 + (void)new TemplateTypeClass(ShoreCliff03); // TEMPLATE_SHORECLIFF03 + (void)new TemplateTypeClass(ShoreCliff04); // TEMPLATE_SHORECLIFF04 + (void)new TemplateTypeClass(ShoreCliff05); // TEMPLATE_SHORECLIFF05 + (void)new TemplateTypeClass(ShoreCliff06); // TEMPLATE_SHORECLIFF06 + (void)new TemplateTypeClass(ShoreCliff07); // TEMPLATE_SHORECLIFF07 + (void)new TemplateTypeClass(ShoreCliff08); // TEMPLATE_SHORECLIFF08 + (void)new TemplateTypeClass(ShoreCliff09); // TEMPLATE_SHORECLIFF09 + (void)new TemplateTypeClass(ShoreCliff10); // TEMPLATE_SHORECLIFF10 + (void)new TemplateTypeClass(ShoreCliff11); // TEMPLATE_SHORECLIFF11 + (void)new TemplateTypeClass(ShoreCliff12); // TEMPLATE_SHORECLIFF12 + (void)new TemplateTypeClass(ShoreCliff13); // TEMPLATE_SHORECLIFF13 + (void)new TemplateTypeClass(ShoreCliff14); // TEMPLATE_SHORECLIFF14 + (void)new TemplateTypeClass(ShoreCliff15); // TEMPLATE_SHORECLIFF15 + (void)new TemplateTypeClass(ShoreCliff16); // TEMPLATE_SHORECLIFF16 + (void)new TemplateTypeClass(ShoreCliff17); // TEMPLATE_SHORECLIFF17 + (void)new TemplateTypeClass(ShoreCliff18); // TEMPLATE_SHORECLIFF18 + (void)new TemplateTypeClass(ShoreCliff19); // TEMPLATE_SHORECLIFF19 + (void)new TemplateTypeClass(ShoreCliff20); // TEMPLATE_SHORECLIFF20 + (void)new TemplateTypeClass(ShoreCliff21); // TEMPLATE_SHORECLIFF21 + (void)new TemplateTypeClass(ShoreCliff22); // TEMPLATE_SHORECLIFF22 + (void)new TemplateTypeClass(ShoreCliff23); // TEMPLATE_SHORECLIFF23 + (void)new TemplateTypeClass(ShoreCliff24); // TEMPLATE_SHORECLIFF24 + (void)new TemplateTypeClass(ShoreCliff25); // TEMPLATE_SHORECLIFF25 + (void)new TemplateTypeClass(ShoreCliff26); // TEMPLATE_SHORECLIFF26 + (void)new TemplateTypeClass(ShoreCliff27); // TEMPLATE_SHORECLIFF27 + (void)new TemplateTypeClass(ShoreCliff28); // TEMPLATE_SHORECLIFF28 + (void)new TemplateTypeClass(ShoreCliff29); // TEMPLATE_SHORECLIFF29 + (void)new TemplateTypeClass(ShoreCliff30); // TEMPLATE_SHORECLIFF30 + (void)new TemplateTypeClass(ShoreCliff31); // TEMPLATE_SHORECLIFF31 + (void)new TemplateTypeClass(ShoreCliff32); // TEMPLATE_SHORECLIFF32 + (void)new TemplateTypeClass(ShoreCliff33); // TEMPLATE_SHORECLIFF33 + (void)new TemplateTypeClass(ShoreCliff34); // TEMPLATE_SHORECLIFF34 + (void)new TemplateTypeClass(ShoreCliff35); // TEMPLATE_SHORECLIFF35 + (void)new TemplateTypeClass(ShoreCliff36); // TEMPLATE_SHORECLIFF36 + (void)new TemplateTypeClass(ShoreCliff37); // TEMPLATE_SHORECLIFF37 + (void)new TemplateTypeClass(ShoreCliff38); // TEMPLATE_SHORECLIFF38 + (void)new TemplateTypeClass(Boulder1); // TEMPLATE_BOULDER1 + (void)new TemplateTypeClass(Boulder2); // TEMPLATE_BOULDER2 + (void)new TemplateTypeClass(Boulder3); // TEMPLATE_BOULDER3 + (void)new TemplateTypeClass(Boulder4); // TEMPLATE_BOULDER4 + (void)new TemplateTypeClass(Boulder5); // TEMPLATE_BOULDER5 + (void)new TemplateTypeClass(Boulder6); // TEMPLATE_BOULDER6 + (void)new TemplateTypeClass(Patch01); // TEMPLATE_PATCH1 + (void)new TemplateTypeClass(Patch02); // TEMPLATE_PATCH2 + (void)new TemplateTypeClass(Patch03); // TEMPLATE_PATCH3 + (void)new TemplateTypeClass(Patch04); // TEMPLATE_PATCH4 + (void)new TemplateTypeClass(Patch07); // TEMPLATE_PATCH7 + (void)new TemplateTypeClass(Patch08); // TEMPLATE_PATCH8 + (void)new TemplateTypeClass(Patch13); // TEMPLATE_PATCH13 + (void)new TemplateTypeClass(Patch14); // TEMPLATE_PATCH14 + (void)new TemplateTypeClass(Patch15); // TEMPLATE_PATCH15 + (void)new TemplateTypeClass(River01); // TEMPLATE_RIVER1 + (void)new TemplateTypeClass(River02); // TEMPLATE_RIVER2 + (void)new TemplateTypeClass(River03); // TEMPLATE_RIVER3 + (void)new TemplateTypeClass(River04); // TEMPLATE_RIVER4 + (void)new TemplateTypeClass(River05); // TEMPLATE_RIVER5 + (void)new TemplateTypeClass(River06); // TEMPLATE_RIVER6 + (void)new TemplateTypeClass(River07); // TEMPLATE_RIVER7 + (void)new TemplateTypeClass(River08); // TEMPLATE_RIVER8 + (void)new TemplateTypeClass(River09); // TEMPLATE_RIVER9 + (void)new TemplateTypeClass(River10); // TEMPLATE_RIVER10 + (void)new TemplateTypeClass(River11); // TEMPLATE_RIVER11 + (void)new TemplateTypeClass(River12); // TEMPLATE_RIVER12 + (void)new TemplateTypeClass(River13); // TEMPLATE_RIVER13 + (void)new TemplateTypeClass(Falls1); // TEMPLATE_FALLS1 + (void)new TemplateTypeClass(Falls1a); // TEMPLATE_FALLS1A + (void)new TemplateTypeClass(Falls2); // TEMPLATE_FALLS2 + (void)new TemplateTypeClass(Falls2a); // TEMPLATE_FALLS2A + (void)new TemplateTypeClass(Ford1); // TEMPLATE_FORD1 + (void)new TemplateTypeClass(Ford2); // TEMPLATE_FORD2 + (void)new TemplateTypeClass(Bridge1); // TEMPLATE_BRIDGE1 + (void)new TemplateTypeClass(Bridge1d); // TEMPLATE_BRIDGE1D + (void)new TemplateTypeClass(Bridge2); // TEMPLATE_BRIDGE2 + (void)new TemplateTypeClass(Bridge2d); // TEMPLATE_BRIDGE2D + (void)new TemplateTypeClass(Slope01); // TEMPLATE_SLOPE1 + (void)new TemplateTypeClass(Slope02); // TEMPLATE_SLOPE2 + (void)new TemplateTypeClass(Slope03); // TEMPLATE_SLOPE3 + (void)new TemplateTypeClass(Slope04); // TEMPLATE_SLOPE4 + (void)new TemplateTypeClass(Slope05); // TEMPLATE_SLOPE5 + (void)new TemplateTypeClass(Slope06); // TEMPLATE_SLOPE6 + (void)new TemplateTypeClass(Slope07); // TEMPLATE_SLOPE7 + (void)new TemplateTypeClass(Slope08); // TEMPLATE_SLOPE8 + (void)new TemplateTypeClass(Slope09); // TEMPLATE_SLOPE9 + (void)new TemplateTypeClass(Slope10); // TEMPLATE_SLOPE10 + (void)new TemplateTypeClass(Slope11); // TEMPLATE_SLOPE11 + (void)new TemplateTypeClass(Slope12); // TEMPLATE_SLOPE12 + (void)new TemplateTypeClass(Slope13); // TEMPLATE_SLOPE13 + (void)new TemplateTypeClass(Slope14); // TEMPLATE_SLOPE14 + (void)new TemplateTypeClass(Slope15); // TEMPLATE_SLOPE15 + (void)new TemplateTypeClass(Slope16); // TEMPLATE_SLOPE16 + (void)new TemplateTypeClass(Slope17); // TEMPLATE_SLOPE17 + (void)new TemplateTypeClass(Slope18); // TEMPLATE_SLOPE18 + (void)new TemplateTypeClass(Slope19); // TEMPLATE_SLOPE19 + (void)new TemplateTypeClass(Slope20); // TEMPLATE_SLOPE20 + (void)new TemplateTypeClass(Slope21); // TEMPLATE_SLOPE21 + (void)new TemplateTypeClass(Slope22); // TEMPLATE_SLOPE22 + (void)new TemplateTypeClass(Slope23); // TEMPLATE_SLOPE23 + (void)new TemplateTypeClass(Slope24); // TEMPLATE_SLOPE24 + (void)new TemplateTypeClass(Slope25); // TEMPLATE_SLOPE25 + (void)new TemplateTypeClass(Slope26); // TEMPLATE_SLOPE26 + (void)new TemplateTypeClass(Slope27); // TEMPLATE_SLOPE27 + (void)new TemplateTypeClass(Slope28); // TEMPLATE_SLOPE28 + (void)new TemplateTypeClass(Slope29); // TEMPLATE_SLOPE29 + (void)new TemplateTypeClass(Slope30); // TEMPLATE_SLOPE30 + (void)new TemplateTypeClass(Slope31); // TEMPLATE_SLOPE31 + (void)new TemplateTypeClass(Slope32); // TEMPLATE_SLOPE32 + (void)new TemplateTypeClass(Slope33); // TEMPLATE_SLOPE33 + (void)new TemplateTypeClass(Slope34); // TEMPLATE_SLOPE34 + (void)new TemplateTypeClass(Slope35); // TEMPLATE_SLOPE35 + (void)new TemplateTypeClass(Slope36); // TEMPLATE_SLOPE36 + (void)new TemplateTypeClass(Slope37); // TEMPLATE_SLOPE37 + (void)new TemplateTypeClass(Slope38); // TEMPLATE_SLOPE38 + (void)new TemplateTypeClass(Road01); // TEMPLATE_ROAD1 + (void)new TemplateTypeClass(Road02); // TEMPLATE_ROAD2 + (void)new TemplateTypeClass(Road03); // TEMPLATE_ROAD3 + (void)new TemplateTypeClass(Road04); // TEMPLATE_ROAD4 + (void)new TemplateTypeClass(Road05); // TEMPLATE_ROAD5 + (void)new TemplateTypeClass(Road06); // TEMPLATE_ROAD6 + (void)new TemplateTypeClass(Road07); // TEMPLATE_ROAD7 + (void)new TemplateTypeClass(Road08); // TEMPLATE_ROAD8 + (void)new TemplateTypeClass(Road09); // TEMPLATE_ROAD9 + (void)new TemplateTypeClass(Road10); // TEMPLATE_ROAD10 + (void)new TemplateTypeClass(Road11); // TEMPLATE_ROAD11 + (void)new TemplateTypeClass(Road12); // TEMPLATE_ROAD12 + (void)new TemplateTypeClass(Road13); // TEMPLATE_ROAD13 + (void)new TemplateTypeClass(Road14); // TEMPLATE_ROAD14 + (void)new TemplateTypeClass(Road15); // TEMPLATE_ROAD15 + (void)new TemplateTypeClass(Road16); // TEMPLATE_ROAD16 + (void)new TemplateTypeClass(Road17); // TEMPLATE_ROAD17 + (void)new TemplateTypeClass(Road18); // TEMPLATE_ROAD18 + (void)new TemplateTypeClass(Road19); // TEMPLATE_ROAD19 + (void)new TemplateTypeClass(Road20); // TEMPLATE_ROAD20 + (void)new TemplateTypeClass(Road21); // TEMPLATE_ROAD21 + (void)new TemplateTypeClass(Road22); // TEMPLATE_ROAD22 + (void)new TemplateTypeClass(Road23); // TEMPLATE_ROAD23 + (void)new TemplateTypeClass(Road24); // TEMPLATE_ROAD24 + (void)new TemplateTypeClass(Road25); // TEMPLATE_ROAD25 + (void)new TemplateTypeClass(Road26); // TEMPLATE_ROAD26 + (void)new TemplateTypeClass(Road27); // TEMPLATE_ROAD27 + (void)new TemplateTypeClass(Road28); // TEMPLATE_ROAD28 + (void)new TemplateTypeClass(Road29); // TEMPLATE_ROAD29 + (void)new TemplateTypeClass(Road30); // TEMPLATE_ROAD30 + (void)new TemplateTypeClass(Road31); // TEMPLATE_ROAD31 + (void)new TemplateTypeClass(Road32); // TEMPLATE_ROAD32 + (void)new TemplateTypeClass(Road33); // TEMPLATE_ROAD33 + (void)new TemplateTypeClass(Road34); // TEMPLATE_ROAD34 + (void)new TemplateTypeClass(Road35); // TEMPLATE_ROAD35 + (void)new TemplateTypeClass(Road36); // TEMPLATE_ROAD36 + + /* + ** Separate out the list of new operator calls. Watcom bombs + ** if they are kept together. + */ + _Watcom_Ugh_Hack(); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * * + * This routine will convert the specified icon number into the appropriate land type. The * + * land type can be determined from the embedded colors in the "control template" section * + * of the original art file. This control information is encoded into the icon data file * + * to be retrieved and interpreted as the program sees fit. The engine only recognizes * + * the first 16 colors as control colors, so the control map color value serves as an * + * index into a simple lookup table. * + * * + * INPUT: icon -- The icon number within this template that is to be examined and used * + * to determine the land type. * + * * + * OUTPUT: Returns with the land type that corresponds to the icon number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/12/1995 JLB : Created. * + *=============================================================================================*/ +LandType TemplateTypeClass::Land_Type(int icon) const +{ + IconsetClass const * icontrol = (IconsetClass const *)Get_Image_Data(); + + if (icontrol != NULL) { + unsigned char const * map = icontrol->Control_Map(); + if (map != NULL) { + static LandType _land[16] = { + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, // Clear + LAND_CLEAR, + LAND_CLEAR, + LAND_BEACH, // Beach + LAND_CLEAR, + LAND_ROCK, // Rock + LAND_ROAD, // Road + LAND_WATER, // Water + LAND_RIVER, // River + LAND_CLEAR, + LAND_CLEAR, + LAND_ROUGH, // Rough + LAND_CLEAR, + }; + + return(_land[map[icon % (icontrol->Map_Width() * icontrol->Map_Height())]]); + } + } + return(LAND_CLEAR); +} + + +/*********************************************************************************************** + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * * + * This routine is used to determine the template number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the template. * + * * + * OUTPUT: Returns with the template number. If the name had no match, * + * then returns with TEMPLATE_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +TemplateType TemplateTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(TEMPLATE_NONE); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the template map and build an * + * occupation list. This list is used to render a template cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the template occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 12/12/1995 JLB : Optimized for direct access to iconset data. * + *=============================================================================================*/ +short const * TemplateTypeClass::Occupy_List(bool) const +{ + static short _occupy[13*8+5]; + short * ptr; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + ptr = &_occupy[0]; + for (int index = 0; index < Width * Height; index++) { + if (*map++ != 0xFF) { + *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + + return((short const *)&_occupy[0]); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * * + * This routine loads the template graphic data for all the template * + * type supported for the specified theater. This routine is called * + * whenever the theater for the scenario is first determined. * + * * + * INPUT: theater -- The theater that the template data is to be * + * loaded for. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk! * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/02/1994 JLB : Only handles iconset loading now (as it should). * + *=============================================================================================*/ +void TemplateTypeClass::Init(TheaterType theater) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + void const * ptr; // Working loaded iconset pointer. + + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + TemplateTypeClass const & tplate = As_Reference(index); + + ((void const *&)tplate.ImageData) = NULL; + if (tplate.Theater & (1< 3 || h > 3); + if (scale) { + x -= (w * ICON_PIXEL_W) / 4; + y -= (h * ICON_PIXEL_H) / 4; + } else { + x -= (w * ICON_PIXEL_W) / 2; + y -= (h * ICON_PIXEL_H) / 2; + } + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + for (index = 0; index < w*h; index++) { + if (map[index] != 0xFF) { + HidPage.Draw_Stamp(iconset, index, 0, 0, NULL, WINDOW_MAIN); + if (scale) { + + HidPage.Scale((*LogicPage), 0, 0, + x + ((index % w)*(ICON_PIXEL_W/2)), + y + ((index / w)*(ICON_PIXEL_H/2)), + ICON_PIXEL_W, ICON_PIXEL_H, + ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL); + + } else { + HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)), + y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H); + } + } + } +} + + +/*********************************************************************************************** + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * * + * This routine prepares a list of template objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a template object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 05/28/1994 JLB : Only handles real templates now. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void TemplateTypeClass::Prep_For_Add(void) +{ + for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * * + * This support routine is used by the scenario editor to add a template object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the template object. * + * * + * OUTPUT: bool; Was the template object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TemplateClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * * + * This routine will create an object of this type. For certain template objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a template at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this template type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TemplateClass(Type, -1)); +} + + +/*********************************************************************************************** + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * * + * This will return a reference to the TemplateTypeClass requested. * + * * + * INPUT: type -- The template type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the template type class specified. * + * * + * WARNINGS: Be sure to pass a valid type parameter. This routine doesn't check it for * + * legality. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass & TemplateTypeClass::As_Reference(TemplateType type) +{ + return(*TemplateTypes.Ptr(type)); +} + + +COORDINATE TemplateTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/CODE/CDFILE.CPP b/CODE/CDFILE.CPP new file mode 100644 index 0000000..eb2faaa --- /dev/null +++ b/CODE/CDFILE.CPP @@ -0,0 +1,720 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CDFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : CDFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * CDFileClass::Open -- Opens the file object -- with path search. * + * CDFileClass::Open -- Opens the file wherever it can be found. * + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * harderr_handler -- Handles hard DOS errors. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "cdfile.h" +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +/* +** Pointer to the first search path record. +*/ +CDFileClass::SearchDriveType * CDFileClass::First = 0; + +int CDFileClass::CurrentCDDrive = 0; +int CDFileClass::LastCDDrive = 0; +char CDFileClass::RawPath[512] = {0}; + +CDFileClass::CDFileClass(char const *filename) : + IsDisabled(false) +{ + CDFileClass::Set_Name(filename); +// memset (RawPath, 0, sizeof(RawPath)); +} + + +CDFileClass::CDFileClass(void) : + IsDisabled(false) +{ +} +extern int Get_CD_Index (int cd_drive, int timeout); + +/*********************************************************************************************** + * harderr_handler -- Handles hard DOS errors. * + * * + * This routine will handle the low level DOS error trapper. Instead of displaying the * + * typical "Abort, Retry, Ignore" message, it simply returns with the failure code. The * + * cause of the error will fail. The likely case would be with disk I/O. * + * * + * INPUT: * + * * + * OUTPUT: Return the failure code. * + * * + * WARNINGS: Do no processing in this routine that could possibly generate another * + * hard error condition. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +int harderr_handler(unsigned int , unsigned int , unsigned int *) +#else +int harderr_handler(unsigned int , unsigned int , unsigned int __far *) +#endif +{ + return(_HARDERR_FAIL); +} + + +/*********************************************************************************************** + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * * + * This routine will examine the drive specified to see if there is a disk inserted. It * + * can be used for floppy drives as well as for the CD-ROM. * + * * + * INPUT: disk -- The drive number to examine. 0=A, 1=B, etc. * + * * + * OUTPUT: bool; Is a disk inserted into the specified drive? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +int cdecl Is_Disk_Inserted(int disk) +{ +#ifndef OBSOLETE + struct find_t fb; + char scan[] = "?:\\*.*"; + + #ifndef WIN32 + _harderr(harderr_handler); // BG: Install hard error handler + #endif + + scan[0] = (char)('A' + disk); + return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0); +#else + struct { + struct { + char Length; + char Unit; + char Function; + char Status; + char Reserved[8]; + } ReqHdr; + char MediaDescriptor; // Media descriptor byte from BPB. + void *Transfer; // Pointer to transfer address block. + short Length; // Number of bytes to transfer. + short Sector; // Starting sector number. + void *Volume; // Pointer to requested volume. + } IOCTLI; + char status[5]; + + memset(IOCTLI, 0, sizeof(IOCTLI)); + IOCTLI.ReqHdr.Length = 26; + IOCTLI.ReqHdr.Unit = 0; // First CD-ROM controlled by this driver. + //IOCTLI.ReqHdr.Unit = 11; // Hard coded for K: + IOCTLI.ReqHdr.Function = 3; // IOCTL read + IOCTLI.Transfer = &status[0]; + IOCTLI.Length = sizeof(status); + status[0] = 6; // Fetch device status code. + _AX = 0x440D; + _CX = 0x0003; + geninterrupt(0x21); + return(!(_AX & (1<<11))); +#endif +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file object -- with path search. * + * * + * This will open the file object, but since the file object could have been constructed * + * with a pathname, this routine will try to find the file first. For files opened for * + * writing, then use the existing filename without performing a path search. * + * * + * INPUT: rights -- The access rights to use when opening the file * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(int rights) +{ + return(BufferIOFileClass::Open(rights)); +} +/*********************************************************************************************** + * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:01AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Refresh_Search_Drives (void) +{ + Clear_Search_Drives(); + Set_Search_Drives(RawPath); +} + +#if 0 +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = false; + int empty = false; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + char const * ptr = strtok(pathlist, ";"); + while (ptr) { + char path[PATH_MAX]; // Working path buffer. + SearchDriveType *srch; // Working pointer to path object. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + */ + if (strncmp(path, "?:", 2) == 0) { + #ifndef WIN32 + GetCDClass temp; + int cd = temp.GetCDDrive(); + #else + int cd = 10; + #endif + found = cd; + empty = !Is_Disk_Inserted(cd); + if (!found || empty) goto nextpath; + path[0] = (char)('A' + cd); + } + + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + if (srch) { + found = true; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } + } + + /* + ** Find the next path string and resubmit. + */ +nextpath: + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} +#endif + + +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + * 05/21/1996 ST : Modified to recognise multiple CD drives * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = FALSE; + int empty = FALSE; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + /* + ** Save the path as it was passed in so we can parse it again later. + ** Check for the case where RawPath was passed in. + */ + if (pathlist != RawPath) { + strcat (RawPath, ";"); + strcat (RawPath, pathlist); + } + + char const * ptr = strtok(pathlist, ";"); + while (ptr != NULL) { + if (strlen(ptr) > 0) { + + char path[PATH_MAX]; // Working path buffer. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + ** Adds an extra entry for each CD drive in the system that has a C&C disc inserted. + ** ST - 5/21/96 4:40PM + */ + if (strncmp(path, "?:", 2) == 0) { + if (CurrentCDDrive) { + found = true; + + /* + ** If the drive has a C&C CD in it then add it to the path + */ + if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0) { + path[0] = (char)(CurrentCDDrive + 'A'); + Add_Search_Drive(path); + } + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + continue; + } + + found = true; + Add_Search_Drive(path); + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} + + +/*********************************************************************************************** + * CDFC::Add_Search_Drive -- Add a new path to the search path list * + * * + * * + * * + * INPUT: path * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 10:12AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Add_Search_Drive(char *path) +{ + SearchDriveType *srch; // Working pointer to path object. + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } +} + + +/*********************************************************************************************** + * CDFC::Set_CD_Drive -- sets the current CD drive letter * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:39AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Set_CD_Drive (int drive) +{ + LastCDDrive = CurrentCDDrive; + CurrentCDDrive = drive; +} + + +/*********************************************************************************************** + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * * + * Use this routine to clear out any previous path(s) set with Set_Search_Drives() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void CDFileClass::Clear_Search_Drives(void) +{ + SearchDriveType * chain; // Working pointer to path chain. + + chain = First; + while (chain) { + SearchDriveType *next; + + next = (SearchDriveType *)chain->Next; + if (chain->Path) { + free((char *)chain->Path); + } + delete chain; + + chain = next; + } + First = 0; +} + + +/*********************************************************************************************** + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * * + * This routine will scan all the directories specified in the path list and if the file * + * was found in one of the directories, it will set the filename to a composite of the * + * correct directory and the filename. It is used to allow path searching when searching * + * for files. Typical use is to support CD-ROM drives. This routine examines the current * + * directory first before scanning through the path list. If after scanning the entire * + * path list, the file still could not be found, then the file object's name is set with * + * just the raw filename as passed to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +char const * CDFileClass::Set_Name(char const *filename) +{ + /* + ** Try to find the file in the current directory first. If it can be found, then + ** just return with the normal file name setting process. Do the same if there is + ** no multi-drive search path. + */ + BufferIOFileClass::Set_Name(filename); + if (IsDisabled || !First || BufferIOFileClass::Is_Available()) return(File_Name()); + + /* + ** Attempt to find the file first. Check the current directory. If not found there, then + ** search all the path specifications available. If it still can't be found, then just + ** fall into the normal raw file filename setting system. + */ + SearchDriveType * srch = First; + + while (srch) { + char path[_MAX_PATH]; + + /* + ** Build a pathname to search for. + */ + strcpy(path, srch->Path); + strcat(path, filename); + + /* + ** Check to see if the file could be found. The low level Is_Available logic will + ** prompt if necessary when the CD-ROM drive has been removed. In all other cases, + ** it will return false and the search process will continue. + */ + BufferIOFileClass::Set_Name(path); + if (BufferIOFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** It wasn't found, so try the next path entry. + */ + srch = (SearchDriveType *)srch->Next; + } + + /* + ** At this point, all path searching has failed. Just set the file name to the + ** plain text passed to this routine and be done with it. + */ + BufferIOFileClass::Set_Name(filename); + return(File_Name()); +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file wherever it can be found. * + * * + * This routine is similar to the RawFileClass open except that if the file is being * + * opened only for READ access, it will search all specified directories looking for the * + * file. If after a complete search the file still couldn't be found, then it is opened * + * using the normal BufferIOFileClass system -- resulting in normal error procedures. * + * * + * INPUT: filename -- Pointer to the override filename to supply for this file object. It * + * would be the base filename (sans any directory specification). * + * * + * rights -- The access rights to use when opening the file. * + * * + * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different * + * than requested. The location of the file can be determined by examining the * + * filename of this file object. The filename will contain the complete * + * pathname used to open the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(char const *filename, int rights) +{ + CDFileClass::Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!filename) { + Error(ENOENT, false); + } + + /* + ** If writing is requested, then multiple drive searching is not performed. + */ + if (IsDisabled || rights == WRITE) { + + BufferIOFileClass::Set_Name( filename ); + return( BufferIOFileClass::Open( rights ) ); + } + + /* + ** Perform normal multiple drive searching for the filename and open + ** using the normal procedure. + */ + Set_Name(filename); + return(BufferIOFileClass::Open(rights)); +} + + +#ifdef NEVER +/* +** Get the drive letters if the CD's online */ +*/ +WORD cdecl GetCDDrive(VOID) +{ + _ES = FP_SEG(&cdDrive[0]); + _BX = FP_OFF(&cdDrive[0]); + _AX = 0x150d; + geninterrupt(0x2F); + return((WORD)(*cdDrive)); +} +#endif + +#if 0 +int Get_CD_Drive(void) +{ +#ifdef WIN32 + return(10); +#else + +#ifdef NEVER + for (int index = 0; index < 26; index++) { + union REGS regs; + + regs.w.ax = 0x150B; + regs.w.bx = 0; + regs.w.cx = index; + int386(0x2F, ®s, ®s); + if (regs.w.bx == 0xADAD) { + return(index); + } + } + return(0); +#else + GetCDClass temp; + return(temp.GetCDDrive()); +#endif +#endif +} + +#endif diff --git a/CODE/CDFILE.H b/CODE/CDFILE.H new file mode 100644 index 0000000..7af0d63 --- /dev/null +++ b/CODE/CDFILE.H @@ -0,0 +1,113 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CDFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood LIbrary * + * * + * File Name : CDFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CDFILE_H +#define CDFILE_H + +#include +#include "bfiofile.h" + +/* +** This class is derived from the BufferIOFileClass. This class adds the functionality of searching +** across multiple directories or drives. It is designed for the typical case of a CD-ROM game +** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM. +** Searching for the file occurs by first examining the current directory. If the file does not +** exist there, then all the paths available are examined in turn until the file can be found. +** For opening files to write, only the current directory is examined. The directory search order +** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path +** string is the same as the DOS path string. +*/ +class CDFileClass : public BufferIOFileClass +{ + public: + CDFileClass(char const *filename); + CDFileClass(void); + virtual ~CDFileClass(void) {}; + + virtual char const * Set_Name(char const *filename); + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + + void Searching(int on) {IsDisabled = !on;}; + + static bool Is_There_Search_Drives(void) {return(First != NULL);}; + static int Set_Search_Drives(char * pathlist); + static void Add_Search_Drive(char *path); + static void Clear_Search_Drives(void); + static void Refresh_Search_Drives(void); + static void Set_CD_Drive(int drive); + static int Get_CD_Drive(void) {return(CurrentCDDrive);}; + static int Get_Last_CD_Drive(void) {return(LastCDDrive);}; + + private: + + /* + ** Is multi-drive searching disabled for this file object? + */ + unsigned IsDisabled:1; + + /* + ** This is the control record for each of the drives specified in the search + ** path. There can be many such search paths available. + */ + typedef struct { + void * Next; // Pointer to next search record. + char const * Path; // Pointer to path string. + } SearchDriveType; + + /* + ** This points to the first path record. + */ + static SearchDriveType * First; + /* + ** This is a copy of the unparsed search path list + */ + static char RawPath[512]; + + /* + ** The drive letter of the current cd drive + */ + static int CurrentCDDrive; + + /* + ** The drive letter of the last used CD drive + */ + static int LastCDDrive; +}; + +#endif + diff --git a/CODE/CELL.CPP b/CODE/CELL.CPP new file mode 100644 index 0000000..5913f21 --- /dev/null +++ b/CODE/CELL.CPP @@ -0,0 +1,3014 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CELL.CPP 4 3/14/97 1:15p Joe_b $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * CellClass::CellClass -- Constructor for cell objects. * + * CellClass::Cell_Building -- Return with building at specified cell. * + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * CellClass::Cell_Coord -- Returns the coordinate of this cell. * + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over* + * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * CellClass::Read -- Reads a particular cell value from a save game file. * + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * + * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/*********************************************************************************************** + * CellClass::CellClass -- Constructor for cell objects. * + * * + * A cell object is constructed into an empty state. It contains no specific objects, * + * templates, or overlays. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/09/1994 JLB : Created. * + * 02/20/1996 JLB : Uses initializer list. * + *=============================================================================================*/ +CellClass::CellClass(void) : + ID(Map.ID(this)), + IsPlot(false), + IsCursorHere(false), + IsMapped(false), + IsVisible(false), + IsWaypoint(false), + IsRadarCursor(false), + IsFlagged(false), + IsToShroud(false), + Jammed(0), + Trigger(NULL), + TType(TEMPLATE_NONE), + TIcon(0), + Overlay(OVERLAY_NONE), + OverlayData(0), + Smudge(SMUDGE_NONE), + SmudgeData(0), + Owner(HOUSE_NONE), + InfType(HOUSE_NONE), + OccupierPtr(0), + Land(LAND_CLEAR) +{ + for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) { + Zones[zone] = 0; + } + Flag.Composite = 0; + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + Overlapper[index] = 0; + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * * + * Use this routine to determine what radar color to render a radar * + * pixel with. This routine is called many many times to render the * + * radar map, so it must be fast. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the color to display the radar pixel with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * + *=============================================================================================*/ +int CellClass::Cell_Color(bool override) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + BuildingClass * object = Cell_Building(); + if (object && !object->Class->IsInvisible) { + return(ColorRemaps[object->House->RemapColor].Bar); + } + + if (override) { + return(TBLACK); + } + if (LastTheater == THEATER_SNOW) { + return(::SnowColor[Land_Type()]); + } else { + return(::GroundColor[Land_Type()]); + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * * + * Returns an object located in the cell. If there is a * + * building present, it returns a pointer to that, otherwise it returns * + * a pointer to one of the units there. If nothing is present in the * + * specified cell, then it returns NULL. * + * * + * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * + * the desired object within the cell. * + * * + * OUTPUT: Returns a pointer to a building or unit located in cell. If * + * nothing present, just returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +TechnoClass * CellClass::Cell_Techno(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object; + COORDINATE click; // Coordinate of click relative to cell corner. + TechnoClass * close = NULL; + long distance = 0; // Recorded closest distance. + + /* + ** Create a coordinate value that represent the pixel location within the cell. This is + ** actually the lower significant bits (leptons) of a regular coordinate value. + */ + click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); + + if (Cell_Occupier()) { + object = Cell_Occupier(); + while (object) { + if (object->Is_Techno()) { + COORDINATE coord = Coord_Fraction(object->Center_Coord()); + long dist = Distance(coord, click); + if (!close || dist < distance) { + close = (TechnoClass *)object; + distance = dist; + } + } + object = object->Next; + } + } + return(close); +} + + +/*************************************************************************** + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * * + * INPUT: RTTIType the RTTI type we are searching for * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 PWG : Created. * + * 06/12/1995 JLB : Returns object class pointer. * + *=========================================================================*/ +ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(rtti != RTTI_NONE); + + ObjectClass * object = Cell_Occupier(); + + while (object != NULL) { + if (object->What_Am_I() == rtti) { + return(object); + } + object = object->Next; + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Cell_Building -- Return with building at specified cell. * + * * + * Given a cell, determine if there is a building associated * + * and return with a pointer to this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building associated with the * + * cell. If there is no building associated, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +BuildingClass * CellClass::Cell_Building(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * * + * This routine is used to determine the terrain object (if any) that * + * overlaps this cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object that overlaps * + * this cell. If there is no terrain object present, then NULL * + * is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass * CellClass::Cell_Terrain(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * * + * This routine is used to determine which object is to be selected * + * by a player click upon the cell. Not all objects that overlap the * + * cell are selectable by the player. This routine sorts out which * + * is which and returns with the appropriate object pointer. * + * * + * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * + * selecting the object within the cell. This plays a role in those cases * + * where several objects (such as infantry) exist within the same cell. * + * * + * OUTPUT: Returns with pointer to the object clickable within the * + * cell. NULL is returned if there is no clickable object * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Object(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * ptr; + + /* + ** Hack so that aircraft landed on helipads can still be selected if directly + ** clicked on. + */ + ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); + if (ptr) { + return(ptr); + } + + ptr = Cell_Techno(x, y); + if (ptr) { + return(ptr); + } + ptr = Cell_Terrain(); + if (ptr) return(ptr); + return(ptr); +} + + +/*********************************************************************************************** + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * * + * This is a low level routine that marks all objects that overlap this * + * cell to be redrawn. It is necessary to call this routine whenever * + * the underlying icon has to be redrawn. * + * * + * INPUT: forced -- Should this redraw be forced even if flags * + * indicate that it would be redundant? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/20/1994 JLB : Simplified to use object pointers. * + * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * + *=============================================================================================*/ +void CellClass::Redraw_Objects(bool forced) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + + if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { + + /* + ** Flag the icon to be redrawn. + */ + Map.Flag_Cell(cell); + + /* + ** Flag the main object in the cell to be redrawn. + */ + if (Cell_Occupier() != NULL) { + ObjectClass * optr = Cell_Occupier(); + while (optr != NULL && optr->IsActive) { + +#ifdef SORTDRAW + if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) { + optr->Mark(MARK_CHANGE); + } +#else + optr->Mark(MARK_CHANGE); +#endif + if (optr->Next != NULL && !optr->Next->IsActive) { + optr->Next = NULL; + } + optr = optr->Next; + } + } + +#ifdef SORTDRAW + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index]) { + assert(Overlapper[index]->IsActive); + if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#else + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + if (!Overlapper[index]->IsActive) { + Overlapper[index] = NULL; + } else { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#endif + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * * + * This determines if the cell can become a proper foundation for * + * building placement. * + * * + * INPUT: loco -- The locomotion of the object trying to consider if this cell is * + * generally clear. Buildings use the value of SPEED_NONE. * + * * + * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/25/1996 JLB : Handles different locomotion types. * + * 10/05/1996 JLB : Checks for crushable walls and crushable object. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Build(SpeedType loco) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** During scenario initialization, passability is always guaranteed. + */ + if (ScenarioInit) return(true); + + /* + ** If there is an object there, then don't allow building. + */ + if (Cell_Object() != NULL) { + return(false); + } + + /* + ** Prevents a building from being placed over a flag object. + */ +#ifdef FIXIT_FLAG_CHECK + if (IsFlagged) { + return(false); + } +#endif + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes. In normal game mode, all overlays are not buildable. + */ + if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) { + return(false); + } + + /* + ** Building over a bib is not allowed. + */ + if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) { + return(false); + } + + /* + ** Building on certain kinds of terrain is prohibited -- bridges in particular. + ** If the locomotion type is SPEED_NONE, then this check is presumed to be + ** for the purposes of building. + */ + if (loco == SPEED_NONE) { + if (Is_Bridge_Here()) { + return(false); + } + + return(::Ground[Land_Type()].Build); + + } else { + + if (::Ground[Land_Type()].Cost[loco] == fixed(0)) { +// if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) { + return(false); + } + return(true); + } +} + + +/*********************************************************************************************** + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * * + * This routine recalculates the ground type in the cell. The speeds the find path * + * algorithm and other determinations of the cell type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 06/20/1994 JLB : Knows about template pointer in cell object. * + *=============================================================================================*/ +void CellClass::Recalc_Attributes(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Special override for interior terrain set so that a non-template or a clear template + ** is equivalent to impassable rock. + */ + if (LastTheater == THEATER_INTERIOR) { + if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) { + Land = LAND_ROCK; + return; + } + } + + /* + ** Check for wall effects. + */ + if (Overlay != OVERLAY_NONE) { + Land = OverlayTypeClass::As_Reference(Overlay).Land; + if (Land != LAND_CLEAR) return; + } + + /* + ** If there is a template associated with this cell, then fetch the + ** land type given the template type and icon number. + */ + if (TType != TEMPLATE_NONE && TType != 255) { + TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType); + Land = ttype->Land_Type(TIcon); + return; + } + + /* + ** No template is the same as clear terrain. + */ + Land = LAND_CLEAR; +} + + +/*********************************************************************************************** + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * * + * This routine is used to mark the cell as being occupied by the specified object. * + * * + * INPUT: object -- The object that is to occupy the cell * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Occupy_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass * optr; + + if (object == NULL) return; + + /* + ** Always add buildings to the end of the occupation chain. This is necessary because + ** the occupation chain is a single list even though buildings occupy more than one + ** cell. If more than one building is allowed to occupy the same cell, then this chain + ** logic will fail. + */ + if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) { + optr = Cell_Occupier(); + while (optr->Next != NULL) { + assert(optr != object); + assert(optr->What_Am_I() != RTTI_BUILDING); + optr = optr->Next; + } + optr->Next = object; + object->Next = 0; + } else { + object->Next = Cell_Occupier(); + OccupierPtr = object; + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsMapped || Session.Type != GAME_NORMAL) { + object->Revealed(PlayerPtr); + } + + /* + ** Special occupy bit set. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = true; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = true; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = true; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * * + * This routine will lift the object from the cell and free the cell to be occupied by * + * another object. Only if the cell was previously marked with the object specified, will * + * the object be lifted off. This routine is the counterpart to Occupy_Down(). * + * * + * INPUT: object -- The object that is being lifted off. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * + *=============================================================================================*/ +void CellClass::Occupy_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + if (object == NULL) return; + + ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain. + + if (optr == object) { + OccupierPtr = object->Next; + object->Next = 0; + } else { + bool found = false; + while (optr != NULL) { + if (optr->Next == object) { + optr->Next = object->Next; + object->Next = 0; + found = true; + break; + } + optr = optr->Next; + } +// assert(found); + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** Special occupy bit clear. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = false; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = false; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = false; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * * + * Most game objects can often have their graphic imagery spill into more than one cell * + * even though they are considered to "occupy" only one cell. All cells overlapped are * + * flagged by this routine. Using this information it is possible to keep the tactical map * + * display correct. * + * * + * INPUT: object -- The object to mark as overlapping this cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 07/04/1995 JLB : Ensures that buildings are always marked down. * + *=============================================================================================*/ +void CellClass::Overlap_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass ** ptr = 0; + + if (!object) return; + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) return; + if (!Overlapper[index]) ptr = &Overlapper[index]; + } + + /* + ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody + ** else out in this case. + */ + if (!ptr && object->What_Am_I() == RTTI_BUILDING) { + for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { + switch (Overlapper[index]->What_Am_I()) { + case RTTI_BUILDING: + case RTTI_TERRAIN: + break; + + default: + Overlapper[index] = object; + index = ARRAY_SIZE(Overlapper); + break; + } + } + } + if (ptr) *ptr = object; + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsMapped) { + object->Revealed(PlayerPtr); + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * * + * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * + * specified unit on the cell. * + * * + * INPUT: object -- The object to remove the overlap flag for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Overlap_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) { + Overlapper[index] = 0; + break; + } + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * * + * This routine will determine if a unit is occupying the cell and if so, return a pointer * + * to it. If there is no unit occupying the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * CellClass::Cell_Unit(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * * + * Call this routine to query and return a pointer to a vessel located in the cell. If * + * there is no vessel present, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the vessel class object if one is present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * CellClass::Cell_Vessel(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((VesselClass*)Cell_Find_Object(RTTI_VESSEL)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * + * * + * This routine examines the cell and returns a pointer to the first infantry unit * + * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * CellClass::Cell_Infantry(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); +} + + +#ifdef SORTDRAW +static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy) +{ + int & px = WindowList[WINDOW_PARTIAL][WINDOWX]; + int & py = WindowList[WINDOW_PARTIAL][WINDOWY]; + int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH]; + int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT]; + int & tx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int & ty = WindowList[WINDOW_TACTICAL][WINDOWY]; + int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + px = cellx + tx; + py = celly + ty; + pw = CELL_PIXEL_W; + ph = CELL_PIXEL_H; + + if (px < tx) { + pw -= tx - px; + px = tx; + } + if (pw < 1) return(false); + + if (py < ty) { + ph -= ty - py; + py = ty; + } + if (ph < 1) return(false); + + if (px + pw > tx + tw) { + pw -= (px + pw) - (tx + tw); + } + if (pw < 1) return(false); + + if (py + ph > ty + th) { + ph -= (py + ph) - (ty + th); + } + if (ph < 1) return(false); + + drawx = drawx - (px-tx); + drawy = drawy - (py-ty); + return(true); +} + + +static int _ocompare(const void * left, const void * right) +{ + COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y(); + COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y(); + if (lcoord < rcoord) return(-1); + if (lcoord > rcoord) return(1); + return(0); +} +#endif + + +/*********************************************************************************************** + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * * + * This is the gruntwork cell rendering code. It draws the cell at the screen location * + * specified. This routine doesn't draw any overlapping or occupying units. It only * + * deals with the ground (cell) layer -- icon level. * + * * + * INPUT: x,y -- The screen coordinates to render the cell imagery at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 08/21/1994 JLB : Revised for simple template objects. * + * 11/01/1994 BRR : Updated placement cursor; draws actual object * + * 11/14/1994 BRR : Added remapping code to show passable areas * + * 12/02/1994 BRR : Added trigger display * + * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * + * 04/25/1995 JLB : Smudges drawn BELOW overlays. * + * 07/22/1996 JLB : Objects added to draw process. * + *=============================================================================================*/ +void CellClass::Draw_It(int x, int y, bool objects) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!objects) { + BStart(BENCH_CELL); + + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + CELL cell = Cell_Number(); + void * remap = NULL; + #ifdef SCENARIO_EDITOR + TemplateTypeClass * tptr; +// TriggerClass * trig; + int i; + char waypt[3]; + #endif + + CellCount++; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + #ifdef CHEAT_KEYS + /* + ** Draw the stamp of the template. + */ + if (Debug_Icon) { + LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); + FontXSpacing -= 2; + Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW, + Cell_Y(cell), Cell_X(cell), + //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1, + Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER], + Overlay, OverlayData + ); + FontXSpacing += 2; + } else { + #endif + + #ifdef SCENARIO_EDITOR + /* + ** Set up the remap table for this icon. + */ + if (Debug_Map && Debug_Passable) { + if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && + Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable + remap = DisplayClass::FadingRed; + } else { + if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable + remap = DisplayClass::FadingGreen; + } else { + remap = DisplayClass::FadingYellow; // moderately passable + } + } + } + #endif + + /* + ** This is the underlying terrain icon. + */ + if (ttype->Get_Image_Data()) { + LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + if (remap) { + LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); + } + } + + #ifdef SCENARIO_EDITOR + /* + ** Draw the map editor's "current" cell. This is the cell that can be + ** assigned attributes such as tag labels. + ** This must be draw before the placement cursor, but after drawing the + ** objects in the cell. + */ + if (Debug_Map && CurrentCell == Cell_Number()) { + LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); + } + #endif + + /* + ** Redraw any smudge. + */ + if (Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); + IsTheaterShape = false; + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map) { + /* + ** Draw the cell's Trigger mnemonic, if it has a trigger + */ + if (Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + + /* + ** Draw the cell's Waypoint designation if there is one. + */ + if (IsWaypoint) { + for (i = 0; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i] == Cell_Number()) { + if (i < 26) { + waypt[0] = 'A' + i; + waypt[1] = 0; + } else { + waypt[0] = 'A' + (i/26)-1; + waypt[1] = 'A' + (i % 26); + waypt[2] = 0; + } + Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, + Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW); + break; + } + } + if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) { + Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) { + Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + } + } + #endif + + /* + ** Draw the placement cursor: + ** - First, draw the hash-mark cursor, so it will appear underneath + ** any cursor being drawn + ** - If the PendingObject is a template, overlay, or smudge, draw it + ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it + */ + if (IsCursorHere) { + SpeedType loco = SPEED_NONE; + if (Map.PendingObjectPtr) { + if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) { + BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr); + loco = obj->Class->Speed; + // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD || + // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT; + } + } + + /* + ** Draw the hash-mark cursor: + */ + if (Map.ProximityCheck && Is_Clear_To_Build(loco)) { + LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL); + } else { + LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL); + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map && Map.PendingObject) { + + switch (Map.PendingObject->What_Am_I()) { + + /* + ** Draw a template: + ** - Compute the icon offset of this cell for this template, using + ** ZoneCell+ZoneOffset to get the upper-left corner of the placement + ** cursor + ** - Draw the icon + */ + case RTTI_TEMPLATETYPE: + tptr = (TemplateTypeClass *)Map.PendingObject; + if (tptr->Get_Image_Data()) { + icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + + (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * + tptr->Width; + LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + } + break; + + /* + ** Draw an overlay; just use the existing 'OverlayData' even though + ** it means nothing. + */ + case RTTI_OVERLAYTYPE: + OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); + break; + + /* + ** Draw a smudge + */ + case RTTI_SMUDGETYPE: + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + break; + + default: + break; + } + } + #endif + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (IsFlagged) { + void const * flag_remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, REMAP_NORMAL); + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); + } + + #ifdef CHEAT_KEYS + } + #endif + BEnd(BENCH_CELL); + } + +#ifdef SORTDRAW + if (objects) { + BStart(BENCH_OBJECTS); + + /* + ** Build a list of objects to draw into a working buffer. There is a + ** big presumption here -- it is presumed that if the cell is to be + ** redrawn, then all objects in the cell should properly be flagged to + ** be redrawn as well. Normally, this isn't a problem, but for subs + ** the IsToDisplay flag MUST REMAIN SET. This is because there is a + ** hack overpass after the cells are redrawn so that subs can be + ** redrawn separately. + */ + ObjectClass * optr[20 + ARRAY_SIZE(Overlapper)]; + int count = 0; + ObjectClass * object = Cell_Occupier(); + while (object != NULL) { + if (!object->IsActive) break; + optr[count] = object; + object->IsToDisplay = true; + object = object->Next; + count++; + } + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + object = Overlapper[index]; + if (object != NULL && object->IsActive) { + object->IsToDisplay = true; + optr[count] = object; + count++; + } + } + + /* + ** Sort the object list so that objects will be drawn from + ** back to front. + */ + switch (count) { + + /* + ** If there are zero or one object, then sorting is + ** unnecessary. + */ + case 0: + case 1: + break; + + /* + ** Two objects can be sorted with a single compare and swap. + */ + case 2: + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + break; + + /* + ** Three objects can be sorted with three compares and swaps. + */ + case 3: + if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[0], optr[2]); + } + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[1], optr[2]); + } + break; + + /* + ** Large number of objects can be effeciently sorted by using + ** a quicksort. + */ + default: + qsort(optr, count, sizeof(optr[0]), _ocompare); + break; + } + + /* + ** Draw any objects that happen to be in or overlapping this cell. + */ + for (index = 0; index < count; index++) { + object = optr[index]; + int xx,yy; + if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) { + if (_Calc_Partial_Window(x, y, xx, yy)) { + object->Draw_It(xx, yy, WINDOW_PARTIAL); + if (Debug_Map) { + object->IsToDisplay = true; + } else { + object->IsToDisplay = false; + } + } + object->IsToDisplay = false; + } + } + BEnd(BENCH_OBJECTS); + } +#endif + +} + + +/*********************************************************************************************** + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * * + * This routine examines the cells around the current one and from this, determines what * + * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * + * for redraw if the icon changed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Concrete_Calc(void) +{ +#ifdef OBSOLETE + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; + static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; + FacingType * ptr; // Working pointer into adjacent cell list. + int index; // Constructed bit index. + int icon; // Icon number. + bool isodd; // Is this for the odd column? + +#define OF_N 0x01 +#define OF_NE 0x02 +#define OF_E 0x04 +#define OF_SE 0x08 +#define OF_S 0x10 + +#define EF_N 0x01 +#define EF_NW 0x10 +#define EF_W 0x08 +#define EF_SW 0x04 +#define EF_S 0x02 + + /* + ** Determine if the even or odd row logic is necessary. + */ + isodd = ((Cell_Number() & 0x01) != 0); + + /* + ** Fetch correct pointer depending on whether this is for an + ** odd or even row. + */ + ptr = (isodd) ? _odd : _even; + + /* + ** Build an index according to the presence of concrete in the special + ** adjacent cells. This is a short list of adjacent cell flags since + ** only 5 adjacent cells need to be examined. The choice of which 5 + ** depends on whether this is for an even or odd column. + */ + index = 0; + for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { + CellClass & cellptr = Adjacent_Cell(*ptr++); + + if (cellptr.Overlay == OVERLAY_CONCRETE) { + index |= (1< 0 && Land == LAND_TIBERIUM) { + if (OverlayData+1 > levels) { + OverlayData -= levels; + reducer = levels; + } else { + Overlay = OVERLAY_NONE; + reducer = OverlayData; + OverlayData = 0; + Recalc_Attributes(); + } + } + return(reducer); +} + + +/*********************************************************************************************** + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * * + * This routine will change the wall shape used for a wall if it's damaged. * + * * + * INPUT: damage -- The number of damage points the wall was hit with. If this value is * + * -1, then the entire wall at this cell will be destroyed. * + * * + * OUTPUT: bool; Was the wall destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BWG : Created. * + * 03/19/1995 JLB : Updates cell information if wall was destroyed. * + * 10/06/1996 JLB : Updates zone as necessary. * + *=============================================================================================*/ +int CellClass::Reduce_Wall(int damage) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (Overlay != OVERLAY_NONE) { + bool destroyed = false; + OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); + + if (wall.IsWall) { + + /* + ** If the damage was great enough to ensure wall destruction, reduce the wall by one + ** level (no more). Otherwise determine wall reduction based on a percentage chance + ** proportional to the damage received and the wall's strength. + */ + if (damage == -1 || damage >= wall.DamagePoints) { + destroyed = true; + } else { + destroyed = Random_Pick(0, wall.DamagePoints) < damage; + } + + /* + ** If the wall is destroyed, destroy it and check for any adjustments to + ** adjacent walls. + */ + if (destroyed) { + OverlayData+=16; + if (damage == -1 || + (OverlayData>>4) >= wall.DamageLevels || + ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) { + + Owner = HOUSE_NONE; + Overlay = OVERLAY_NONE; + OverlayData = 0; + Recalc_Attributes(); + Redraw_Objects(); + Adjacent_Cell(FACING_N).Wall_Update(); + Adjacent_Cell(FACING_W).Wall_Update(); + Adjacent_Cell(FACING_S).Wall_Update(); + Adjacent_Cell(FACING_E).Wall_Update(); + Detach_This_From_All(As_Target()); + + /* + ** The zone calculation changes now for non-crushable zone sensitive + ** travellers. + */ + if (wall.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * * + * INPUT: * + * coord COORDINATE to compute index for * + * * + * OUTPUT: * + * index into StoppingCoord that's closest to this coord * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1994 BR : Created. * + * 12/10/1994 JLB : Uses alternate sub-position algorithm. * + *=============================================================================================*/ +int CellClass::Spot_Index(COORDINATE coord) +{ + COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell. + + /* + ** If the coordinate is close enough to the center of the cell, then return + ** the center position index. + */ + if (Distance(rel, (COORDINATE)0x00800080L) < 60) { + return(0); + } + + /* + ** Since the center cell position has been eliminated, a simple comparison + ** as related to the center of the cell can be used to determine the sub + ** position. Take advantage of the fact that the sub positions are organized + ** from left to right, top to bottom. + */ + int index = 0; + if (Coord_X(rel) > 0x80) index |= 0x01; + if (Coord_Y(rel) > 0x80) index |= 0x02; + return(index+1); +} + + +/*********************************************************************************************** + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * * + * Similar to the CellClass::Free_Spot; this routine finds the spot in * + * the cell closest to the given coordinate, and returns the COORDINATE of * + * that spot if it's available, NULL if it's not. * + * * + * INPUT: * + * coord coordinate to check (only sub cell position examined) * + * * + * any -- If only the closest spot is desired regardless of whether it is free or * + * not, then this parameter will be true. * + * * + * OUTPUT: * + * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell * + * coordinate data portions of the coordinate passed in. Only the lower sub-cell * + * data is altered. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 12/10/1994 JLB : Picks best of closest stopping positions. * + * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * + *=============================================================================================*/ +COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int spot_index = Spot_Index(coord); + + /* + ** This precalculated sequence table records the closest spots to any given spot. Sequential + ** examination of these spots for availability ensures that the closest available one is + ** discovered first. + */ + static unsigned char _sequence[5][4] = { + {1,2,3,4}, + {0,2,3,4}, + {0,1,4,3}, + {0,1,4,2}, + {0,2,3,1} + }; + + /* + ** In the case of the center coordinate being requested, but is occupied, then all other + ** sublocations are equidistant. Instead of picking a static sequence of examination, the + ** order is mixed up by way of this table. + */ + static unsigned char _alternate[4][4] = { + {1,2,3,4}, + {2,3,4,1}, + {3,4,1,2}, + {4,1,2,3}, + }; + coord = Coord_Whole(coord); + + /* + ** Cells occupied by buildings or vehicles don't have any free spots. + */ + if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { + return(NULL); + } + + /* + ** If just the nearest position is desired regardless of whether occupied or not, + ** then just return with the stopping coordinate value. + */ + if (any || Is_Spot_Free(spot_index)) { + return(Coord_Add(coord, StoppingCoordAbs[spot_index])); + } + + /* + ** Scan through all available sub-locations in the cell in order to determine + ** the closest one to the coordinate requested. Use precalculated table so that + ** when the first free position is found, bail. + */ + unsigned char * sequence; + if (spot_index == 0) { + sequence = &_alternate[Random_Pick(0, 3)][0]; + } else { + sequence = &_sequence[spot_index][0]; + } + for (int index = 0; index < 4; index++) { + int pos = *sequence++; + + if (Is_Spot_Free(pos)) { + return(Coord_Add(coord, StoppingCoordAbs[pos])); + } + } + + /* + ** No free spot could be found so return a NULL coordinate. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * * + * This support routine will determine what the clear icon number would be for the cell. * + * The icon number is determined by converting the cell number into an index into a * + * lookup table. This yields what appears to be a randomized map without the necessity of * + * generating and recording randomized map numbers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * + *=============================================================================================*/ +int CellClass::Clear_Icon(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2)); +// return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C)); +} + + +/*********************************************************************************************** + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * * + * This routine is called whenever a great, but slow moving, threat is presented to the * + * occupants of a cell. The occupants will, in most cases, stop what they are doing and * + * try to get out of the way. * + * * + * INPUT: threat -- The coordinate source of the threat. * + * * + * forced -- If this threat is so major that the occupants should stop what * + * they are doing, then this parameter should be set to true. * + * * + * nokidding -- Override the scatter to also affect human controlled objects. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = NULL; + + object = Cell_Occupier(); + while (object != NULL) { + + /* + ** Special check to make sure that friendly units never scatter. + */ + if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) { + object->Scatter(threat, forced, nokidding); + } + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * * + * Use this routine to return a reference to the adjacent cell in the direction specified. * + * * + * INPUT: face -- The direction to use when determining the adjacent cell. * + * * + * OUTPUT: Returns with a reference to the adjacent cell. * + * * + * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +CellClass const & CellClass::Adjacent_Cell(FacingType face) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if ((unsigned)face >= FACING_COUNT) { + return(*this); + } + + CellClass const * ptr = this + AdjacentCell[face]; + if ((unsigned)ptr->Cell_Number() > MAP_CELL_TOTAL) return(*this); + return(*ptr); +} + + +/*************************************************************************** + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/24/1995 PWG : Created. * + *=========================================================================*/ +void CellClass::Adjust_Threat(HousesType house, int threat_value) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int region = Map.Cell_Region(Cell_Number()); + + for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { + if (lp == house) continue; + + HouseClass * house_ptr = HouseClass::As_Pointer(lp); + if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { + house_ptr->Adjust_Threat(region, threat_value); + } + } + if (Debug_Threat) { + Map.Flag_To_Redraw(true); + } +} + + +/*********************************************************************************************** + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * + * * + * This routine will adjust the level of the Tiberium in the cell so that it will * + * smoothly blend with the adjacent Tiberium. This routine should only be called for * + * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * + * pattern. * + * * + * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * + * used. * + * * + * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * + * * + * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * + * increase the net worth of the existing Tiberium. * + * * + * HISTORY: * + * 05/16/1995 JLB : Created. * + * 02/20/1996 JLB : Takes into account the ore type. * + *=============================================================================================*/ +long CellClass::Tiberium_Adjust(bool pregame) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + if (Overlay != OVERLAY_NONE) { + if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { + static int _adj[9] = {0,1,3,4,6,7,8,10,11}; + static int _adjgem[9] = {0,0,0,1,1,1,2,2,2}; + int count = 0; + + /* + ** Mixup the Tiberium overlays so that they don't look the same. + ** Since the type of ore is known, also record the nominal + ** value per step of that ore type. + */ + bool gems = false; + int value = 0; + if (pregame) { + switch (Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + value = Rule.GoldValue; + Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4); + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + gems = true; + value = Rule.GemValue*4; + Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4); + break; + + default: + break; + } + } + + /* + ** Add up all adjacent cells that contain tiberium. + ** (Skip those cells which aren't on the map) + */ + for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { + CellClass & adj = Adjacent_Cell(face); + + if (adj.Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) { + count++; + } + } + + if (gems) { + OverlayData = _adjgem[count]; + OverlayData = min(OverlayData, 2); + } else { + OverlayData = _adj[count]; + } + return((OverlayData+1) * value); + } + } + return(0); +} + + +/*********************************************************************************************** + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * * + * Call this routine whenever an object enters a cell. It will check for the existence * + * of a crate and generate any "goodie" it might contain. * + * * + * INPUT: object -- Pointer to the object that is triggering this crate. * + * * + * OUTPUT: Can the object continue to enter this cell? A false return value means that the * + * cell is now occupied and must not be entered. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + * 07/08/1995 JLB : Added a bunch of goodies to the crates. * + * 06/17/1996 JLB : Revamped for Red Alert * + *=============================================================================================*/ +bool CellClass::Goodie_Check(FootClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { + bool force_mcv = false; + int force_money = 0; + int damage; + COORDINATE coord; + + /* + ** Determine the total number of shares for all the crate powerups. This is used as + ** the base pool to determine the odds from. + */ + int total_shares = 0; + for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) { + total_shares += CrateShares[index]; + } + + /* + ** Pick a random crate powerup according to the shares allotted to each powerup. + ** In solo play, the bonus item is dependant upon the rules control. + */ + CrateType powerup; + if (Session.Type == GAME_NORMAL) { + + /* + ** Solo play has money amount determined by rules.ini file. + */ + force_money = Rule.SoloCrateMoney; + + if (Overlay == OVERLAY_STEEL_CRATE) { + powerup = Rule.SilverCrate; + } + + if (Overlay == OVERLAY_WOOD_CRATE) { + powerup = Rule.WoodCrate; + } + + if (Overlay == OVERLAY_WATER_CRATE) { +//Mono_Printf("%d-%s.\n", __LINE__, __FILE__); + powerup = Rule.WaterCrate; + } + + } else { + int pick = Random_Pick(1, total_shares); + + int share_count = 0; + for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) { + share_count += CrateShares[powerup]; + if (pick <= share_count) break; + } + assert(powerup != CRATE_COUNT); + + /* + ** Depending on what was picked, there might be an alternate goodie if the selected + ** goodie would have no effect. + */ + switch (powerup) { + case CRATE_UNIT: + if (object->House->CurUnits > 50) powerup = CRATE_MONEY; + break; + + case CRATE_SQUAD: + if (object->House->CurInfantry > 100) powerup = CRATE_MONEY; + break; + + case CRATE_DARKNESS: + if (object->House->IsGPSActive) powerup = CRATE_MONEY; + break; + + case CRATE_ARMOR: + if (object->ArmorBias != 1) powerup = CRATE_MONEY; + break; + + case CRATE_SPEED: + if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY; + break; + + case CRATE_FIREPOWER: + if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY; + break; + + case CRATE_REVEAL: + if (object->House->IsVisionary) { + if (object->House->IsGPSActive) { + powerup = CRATE_MONEY; + } else { + powerup = CRATE_DARKNESS; + } + } + break; + + case CRATE_CLOAK: + if (object->IsCloakable) powerup = CRATE_MONEY; + break; + +// case CRATE_HEAL_BASE: +// if (object->House->BScan == 0) powerup = CRATE_UNIT; + + case CRATE_MONEY: + break; + + case CRATE_TIMEQUAKE: + /* + ** For the time quake crate, scan through and count up all the + ** units (and infantry and ships and aircraft) and if either + ** side has very few, allow the time quake. Otherwise, + ** change the crate to money or something. Only do this for + ** multiplay - for solo play, they get what they get. First, + ** check for time - the chance for getting a time quake crate + ** should be very very low when they first start the mission, + ** but as time goes on the chance goes up. + */ + if (Session.Type != GAME_NORMAL) { + int i,ucount; + int minunits = 1000; + bool found = false; + unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE); + if (minutes > 100) minutes = 100; + if (Random_Pick(0,100-(int)minutes) == 0) { + for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) { + ucount = 0; + HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1); + if (hptr != NULL && !hptr->IsDefeated) { + int j; + for( j=0; j < UNIT_COUNT; j++) { + ucount += hptr->QuantityU(j); + } + for( j=0; j < INFANTRY_COUNT; j++) { + ucount += hptr->QuantityI(j); + } + for( j=0; j < AIRCRAFT_COUNT; j++) { + ucount += hptr->QuantityA(j); + } + for( j=0; j < VESSEL_COUNT; j++) { + ucount += hptr->QuantityV(j); + } + int bcount = 0; + for( j=0; j < STRUCT_COUNT; j++) { + bcount += hptr->QuantityB(j); + } + ucount += bcount/2; // weight buildings less + minunits = min(minunits, ucount); + } + } + if (Random_Pick(0, minunits) == minunits) { + found = true; + } + } + + if (!found) { + powerup = CRATE_MONEY; + } + } + break; + } + /* + ** Possibly force it to be an MCV if there is + ** sufficient money and no buildings left. + */ + if ( object->House->BScan == 0 && + object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) && + Session.Options.Bases && + !(object->House->UScan & UNITF_MCV)) { + powerup = CRATE_UNIT; + force_mcv = true; + } + + /* + ** If the powerup is money but there is insufficient money to build a refinery but there is a construction + ** yard available, then force the money to be enough to rebuild the refinery. + */ + if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && + object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) { + + force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias; + } + + /* + ** Special override for water crates so that illegal goodies items + ** won't appear. + */ + if (Overlay == OVERLAY_WATER_CRATE) { + switch (powerup) { + case CRATE_UNIT: + case CRATE_SQUAD: + powerup = CRATE_MONEY; + break; + + default: + break; + } + } + } + + /* + ** Keep track of the number of each type of crate found + */ + if (Session.Type == GAME_INTERNET) { + object->House->TotalCrates->Increment_Unit_Total(powerup); + } + + /* + ** Remove the crate from the map. + */ + Map.Remove_Crate(Cell_Number()); +// Map[Cell_Number()].Overlay = OVERLAY_NONE; + + if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) { + Map.Place_Random_Crate(); + } + + /* + ** Generate any corresponding animation associated with this crate powerup. + */ + if (CrateAnims[powerup] != ANIM_NONE) { + new AnimClass(CrateAnims[powerup], Cell_Coord()); + } + + /* + ** Create the effect requested. + */ + bool tospeak = false; + switch (powerup) { + case CRATE_TIMEQUAKE: + TimeQuake = true; + break; + + /* + ** Give the player money. + */ + case CRATE_MONEY: +crate_money: + if (force_money > 0) { + object->House->Refund_Money(force_money); + } else { + object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900)); + } + break; + + /* + ** Shroud the world in blackness. + */ + case CRATE_DARKNESS: + if (object->House == PlayerPtr) { + Map.Shroud_The_Map(); + } + break; + + /* + ** Reveal the entire map. + */ + case CRATE_REVEAL: + object->House->IsVisionary = true; + if (object->House == PlayerPtr) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Try to create a unit where the crate was. + */ + case CRATE_UNIT: { + UnitTypeClass const * utp = NULL; + + /* + ** Give the player an MCV if he has no base left but does have more than enough + ** money to rebuild a new base. Of course, if he already has an MCV, then don't + ** give him another one. + */ + if (force_mcv) { + utp = &UnitTypeClass::As_Reference(UNIT_MCV); + } + + /* + ** If the player has a base and a refinery, but no harvester, then give him + ** a free one. + */ + if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { + utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + } + + /* + ** Check for special unit type override value. + */ + if (Rule.UnitCrateType != UNIT_NONE) { + utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType); + } + + /* + ** If no unit type has been determined, then pick one at random. + */ + while (utp == NULL) { +#ifdef FIXIT_ANTS +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3)); +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3)); +#endif +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); +#endif + if (utype != UNIT_MCV || Session.Options.Bases) { + utp = &UnitTypeClass::As_Reference(utype); + if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) { + break; + } + utp = NULL; + } + } + + if (utp != NULL) { + UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House); + if (goodie_unit != NULL) { + if (goodie_unit->Unlimbo(Cell_Coord())) { + return(false); + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the crate location. + */ + CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed); + if (goodie_unit->Unlimbo(::Cell_Coord(cell))) { + return(false); + } + delete goodie_unit; + goto crate_money; + } + } + } + break; + + /* + ** Create a squad of miscellaneous composition. + */ + case CRATE_SQUAD: + for (index = 0; index < 5; index++) { + static InfantryType _inf[] = { + INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, + INFANTRY_E2, + INFANTRY_E3, + INFANTRY_RENOVATOR + }; + if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) { + if (index == 0) { + goto crate_money; + } + } + } + return(false); + + /* + ** A one para-bomb mission. + */ + case CRATE_PARA_BOMB: + if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** A one time sonar pulse + */ + case CRATE_SONAR: + if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** A group of explosions are triggered around the crate. + */ + case CRATE_EXPLOSION: + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_HE, 0, true); + } + for (index = 0; index < 5; index++) { + COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); + new AnimClass(ANIM_FBALL1, frag_coord); + damage = CrateData[powerup]; + Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE); + } + break; + + /* + ** A napalm blast is triggered. + */ + case CRATE_NAPALM: + coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); + new AnimClass(ANIM_NAPALM3, coord); + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true); + } + damage = CrateData[powerup]; + Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); + break; + + /* + ** All objects within a certain range will gain the ability to cloak. + */ + case CRATE_CLOAK: + for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IsCloakable = true; + } + } + break; + + /* + ** All of the player's objects heal up. + */ + case CRATE_HEAL_BASE: + if (object->IsOwnedByPlayer) { + Sound_Effect(VOC_HEAL, object->Center_Coord()); + } + for (index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { + obj->Strength = obj->Class_Of().MaxStrength; + } + } + break; + + + case CRATE_ICBM: + if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + case CRATE_ARMOR: + for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) { + fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256)); + ((TechnoClass *)obj)->ArmorBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_ARMOR); + break; + + case CRATE_SPEED: + for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) { + FootClass * foot = (FootClass *)obj; + + fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256); + foot->SpeedBias = val; + if (foot->IsOwnedByPlayer) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_SPEED); + break; + + case CRATE_FIREPOWER: + for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) { + + fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256); + ((TechnoClass *)obj)->FirepowerBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_FIREPOWER); + break; + + case CRATE_INVULN: + for (index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256)); + obj->Mark(MARK_CHANGE); + } + } + break; + + /* + ** A chronal vortex appears targetted at the triggering object. + */ + case CRATE_VORTEX: + if ( !ChronalVortex.Is_Active()) { + ChronalVortex.Appear ( Cell_Coord() ); + ChronalVortex.Set_Target ( (ObjectClass*) object ); + Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord()); + } + break; + + default: + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * * + * This routine will place the house flag at this cell location. * + * * + * INPUT: house -- The house that is having its flag placed here. * + * * + * OUTPUT: Was the flag successfully placed here? * + * * + * WARNINGS: Failure to place means that the cell is impassable for some reason. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Place(HousesType house) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) { + IsFlagged = true; + Owner = house; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * * + * This routine will free the cell of any house flag that may be located there. * + * * + * INPUT: none * + * * + * OUTPUT: Was there a flag here that was removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Remove(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (IsFlagged) { + IsFlagged = false; + Owner = HOUSE_NONE; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * * + * This routine is called when some event would cause a momentary disruption in the * + * cloaking device. All objects that are cloaked in the cell will have their cloaking * + * device shimmer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Shimmer(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = Cell_Occupier(); + + while (object) { + object->Do_Shimmer(); + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * * + * This routine is called when determining general passability for purposes of zone * + * calculation. Only blockages that cannot be circumvented are considered to make a cell * + * impassable. All other obstructions can either be destroyed or are temporary. * + * * + * INPUT: loco -- The locomotion type to use when determining passablility. * + * * + * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? * + * * + * ignorevehicles -- If vehicles should be ignored, then this flag will be true. * + * * + * zone -- If specified, the zone must match this value or else movement is * + * presumed disallowed. * + * * + * check -- This specifies the zone type that this check applies to. * + * * + * OUTPUT: Is the cell generally passable to ground targeting? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. * + * 10/05/1996 JLB : Allows checking for crushable blockages. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Flying objects always consider every cell passable since they can fly over everything. + */ + if (loco == SPEED_WINGED) { + return(true); + } + + /* + ** If a zone was specified, then see if the cell is in a legal + ** zone to allow movement. + */ + if (zone != -1) { + if (zone != Zones[check]) { + return(false); + } + } + + /* + ** Check the occupy bits for passable legality. If ignore infantry is true, then + ** don't consider infnatry. + */ + int composite = Flag.Composite; + if (ignoreinfantry) { + composite &= 0xE0; // Drop the infantry occupation bits. + } + if (ignorevehicles) { + composite &= 0x5F; // Drop the vehicle/building bit. + } + if (composite != 0) { + return(false); + } + + /* + ** Fetch the land type of the cell -- to be modified and used later. + */ + LandType land = Land_Type(); + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes unless this is a wall crushing check or if the checking object + ** can destroy walls. + */ + OverlayTypeClass const * overlay = NULL; + if (Overlay != OVERLAY_NONE) { + overlay = &OverlayTypeClass::As_Reference(Overlay); + } + if (overlay != NULL && overlay->IsWall) { + if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) { + return(false); + } + + /* + ** Crushing objects consider crushable walls as clear rather than the + ** typical LAND_WALL setting. + */ + land = LAND_CLEAR; + } + + /* + ** See if the ground type is impassable to this locomotion type and if + ** so, return the error condition. + */ + if (::Ground[land].Cost[loco] == 0) { + return(false); + } + + /* + ** All checks passed, so this cell must be passable. + */ + return(true); +} + + +/*********************************************************************************************** + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * * + * This routine will examine this cell and if there is a bridge here, it will return * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is there a bridge located in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Is_Bridge_Here(void) const +{ + switch (TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE1D: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE2D: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + case TEMPLATE_BRIDGE_3E: + case TEMPLATE_BRIDGE_3F: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * * + * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can * + * grow only if there is Tiberium already present. It can only grow to a certain level * + * and then all further growth is suspended. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Grow(void) const +{ + if (!Rule.IsTGrowth) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData >= 11) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * * + * This routine will examine the cell and determine if there is sufficient Tiberium * + * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is * + * too low, spreading will not occur. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? * + * * + * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells * + * available to spread to. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Spread(void) const +{ + if (!Rule.IsTSpread) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData <= 6) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * * + * This routine will cause the tiberium to grow in the cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did Tiberium grow in the cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Grow_Tiberium(void) +{ + if (Can_Tiberium_Grow()) { + OverlayData++; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * * + * This routine will cause the Tiberium to spread from this cell into an adjacent (random) * + * cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did the Tiberium spread? * + * * + * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this * + * routine will fail. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Spread_Tiberium(bool forced) +{ + if (!forced) { + if (!Can_Tiberium_Spread()) return(false); + } + FacingType offset = Random_Pick(FACING_N, FACING_NW); + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + CellClass * newcell = &Adjacent_Cell(index+offset); + + if (newcell != NULL && newcell->Can_Tiberium_Germinate()) { + new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); + newcell->OverlayData = 0; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * * + * This routine will examine the cell and determine if Tiberium can start growth in it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Germinate(void) const +{ + if (!Map.In_Radar(Cell_Number())) return(false); + + if (Is_Bridge_Here()) return(false); + + /* + ** Don't allow Tiberium to grow on a cell with a building unless that building is + ** invisible. In such a case, the Tiberium must grow or else the location of the + ** building will be revealed. + */ + BuildingClass const * building = Cell_Building(); + if (building != NULL && !building->Class->IsInvisible) return(false); + + if (!Ground[Land_Type()].Build) return(false); + + if (Overlay != OVERLAY_NONE) return(false); + + return(true); +} diff --git a/CODE/CELL.H b/CODE/CELL.H new file mode 100644 index 0000000..8e8aab2 --- /dev/null +++ b/CODE/CELL.H @@ -0,0 +1,295 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CELL_H +#define CELL_H + +#include "building.h" +#include "unit.h" +#include "template.h" + + +/**************************************************************************** +** Each cell on the map is controlled by the following structure. +*/ +class CellClass +{ + public: + /* + ** This is the ID number of this cell. By placing the ID number here, it doesn't have + ** be calculated. Calculating this number requires a divide and would occur about + ** 5.72031 bijillion times per second. + */ + short ID; + + /* + ** Does this cell need to be updated on the radar map? If something changes in the cell + ** that might change the radar map imagery, then this flag will be set. It gets cleared + ** when the cell graphic is updated to the radar map. + */ + unsigned IsPlot:1; + + /* + ** Does this cell contain the special placement cursor graphic? This graphic is + ** present when selecting a site for building placement. + */ + unsigned IsCursorHere:1; + + /* + ** A mapped cell has some portion of it visible. Maybe it has a shroud piece + ** over it and maybe not. + */ + unsigned IsMapped:1; + + /* + ** A visible cell means that it is completely visible with no shroud over + ** it at all. + */ + unsigned IsVisible:1; + + /* + ** Every cell can be assigned a waypoint. A waypoint can only be assigned + ** to one cell, and vice-versa. This bit simply indicates whether this + ** cell is assigned a waypoint or not. + */ + unsigned IsWaypoint:1; + + /* + ** Is this cell currently under the radar map cursor? If so then it + ** needs to be updated whenever the map is updated. + */ + unsigned IsRadarCursor:1; + + /* + ** If this cell contains a house flag, then this will be true. The actual house + ** flag it contains is specified by the Owner field. + */ + unsigned IsFlagged:1; + + /* + ** This is a working flag used to help keep track of what cells should be + ** shrouded. By using this flag it allows a single pass through the map + ** cells for determining shadow regrowth logic. + */ + unsigned IsToShroud:1; + + /* + ** This records the movement zone for this map. Movement zones share the + ** same number if they are contiguous (terrain consideration only). There + ** are basically two kinds of zones. The difference being determined by + ** walls that can be crushed by movement. A vehicle that can crush walls + ** will only consider the CrushZone. All other terrestrial travellers will + ** use the normal Zone. + */ + unsigned char Zones[MZONE_COUNT]; + + /* + ** This field controls whether an area is being jammed by a gap + ** generator. + */ + unsigned short Jammed; + + /* + ** This is the trigger ID for any trigger that might be attached to + ** this cell. + */ + CCPtr Trigger; + + /* + ** This contains the icon number and set to use for the base + ** of the terrain. All rendering on an icon occurs AFTER the icon + ** specified by this element is rendered. It is the lowest of the low. + */ + TemplateType TType; + unsigned char TIcon; + + /* + ** The second layer of 'terrain' icons is represented by a simple + ** type number and a value byte. This is sufficient for handling + ** concrete and walls. + */ + OverlayType Overlay; + unsigned char OverlayData; + + /* + ** This is used to specify any special 'stain' overlay icon. This + ** typically includes infantry bodies or other temporary marks. + */ + SmudgeType Smudge; + unsigned char SmudgeData; + + /* + ** Smudges and walls need to record ownership values. For walls, this + ** allows adjacent building placement logic to work. For smudges, it + ** allows building over smudges that are no longer attached to buildings + ** in addition to fixing the adjacent placement logic. + */ + HousesType Owner; + + /* + ** This flag tells you what type of infantry currently occupy the + ** cell or are moving into it. + */ + HousesType InfType; + + /* + ** These point to the object(s) that are located in this cell or overlap + ** this cell. + */ + private: + ObjectClass * OccupierPtr; + + public: +#ifdef SORTDRAW + ObjectClass * Overlapper[10]; +#else + ObjectClass * Overlapper[6]; +#endif + + /* + ** This array of bit flags is used to indicate which sub positions + ** within the cell are either occupied or are soon going to be + ** occupied. For vehicles, the cells that the vehicle is passing over + ** will be flagged with the vehicle bit. For infantry, the the sub + ** position the infantry is stopped at or headed toward will be marked. + ** The sub positions it passes over will NOT be marked. + */ + union { + struct { + unsigned Center:1; + unsigned NW:1; + unsigned NE:1; + unsigned SW:1; + unsigned SE:1; + unsigned Vehicle:1; // Reserved for vehicle occupation. + unsigned Monolith:1; // Some immovable blockage is in cell. + unsigned Building:1; // A building of some time (usually blocks movement). + } Occupy; + unsigned char Composite; + } Flag; + + //---------------------------------------------------------------- + CellClass(void); + CellClass(NoInitClass const & x) : Trigger(x) {} + ~CellClass(void) {OccupierPtr=0;} + + int operator == (CellClass const & cell) const {return &cell == this;} + + /* + ** Query functions. + */ + bool Can_Tiberium_Germinate(void) const; + bool Can_Tiberium_Grow(void) const; + bool Can_Tiberium_Spread(void) const; + bool Is_Bridge_Here(void) const; + RTTIType What_Am_I(void) const {return(RTTI_CELL);} + BuildingClass * Cell_Building(void) const; + CELL Cell_Number(void) const {return(ID);} + COORDINATE Cell_Coord(void) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());} + CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));} + CellClass const & Adjacent_Cell(FacingType face) const; + InfantryClass * Cell_Infantry(void) const; + LandType Land_Type(void) const {return(Land);} + ObjectClass * Cell_Find_Object(RTTIType rtti) const; + ObjectClass * Cell_Object(int x=0, int y=0) const; + ObjectClass * Cell_Occupier(void) const {return(OccupierPtr);} + ObjectClass * Fetch_Occupier(void) const; + TARGET As_Target(void) const {return ::As_Target(Cell_Number());} + TechnoClass * Cell_Techno(int x=0, int y=0) const; + TerrainClass * Cell_Terrain(void) const; + UnitClass * Cell_Unit(void) const; + VesselClass * Cell_Vessel(void) const; + bool Goodie_Check(FootClass * object); + bool Is_Clear_To_Build(SpeedType loco = SPEED_TRACK) const; + bool Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone=-1, MZoneType check=MZONE_NORMAL) const; + bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); } + int Cell_Color(bool override=false) const; + int Clear_Icon(void) const; + static int Spot_Index(COORDINATE coord); + + /* + ** Object placement and removal flag operations. + */ + void Occupy_Down(ObjectClass * object); + void Occupy_Up(ObjectClass * object); + void Overlap_Down(ObjectClass * object); + void Overlap_Up(ObjectClass * object); + bool Flag_Place(HousesType house); + bool Flag_Remove(void); + + /* + ** File I/O. + */ + bool Should_Save(void) const; + bool Save(Pipe & file) const; + bool Load(Straw & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Display and rendering controls. + */ + void Draw_It(int x, int y, bool objects=false) const; + void Redraw_Objects(bool forced=false); + void Shimmer(void); + + /* + ** Maintenance calculation support. + */ + bool Grow_Tiberium(void); + bool Spread_Tiberium(bool forced=false); + long Tiberium_Adjust(bool pregame=false); + void Wall_Update(void); + void Concrete_Calc(void); + void Recalc_Attributes(void); + int Reduce_Tiberium(int levels); + int Reduce_Wall(int damage); + void Incoming(COORDINATE threat=0, bool forced=false, bool nokidding=false); + void Adjust_Threat(HousesType house, int threat_value); + + int operator != (CellClass const &) const {return 0;} + + private: + CellClass (CellClass const &) ; + + LandType Land; // The land type of this cell. +}; + +#endif + diff --git a/CODE/CHECKBOX.CPP b/CODE/CHECKBOX.CPP new file mode 100644 index 0000000..cfc7e79 --- /dev/null +++ b/CODE/CHECKBOX.CPP @@ -0,0 +1,102 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CHECKBOX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CHECKBOX.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "checkbox.h" + + +/*********************************************************************************************** + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * * + * This routine will draw the checkbox either filled or empty as necessary. * + * * + * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? * + * * + * OUTPUT: Was the check box rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Draw_Me(int forced) +{ + if (ToggleClass::Draw_Me(forced)) { + + Hide_Mouse(); + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, false); + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY); + if (IsOn) { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, LTGREEN); + } + Show_Mouse(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * * + * This routine will detect if the mouse has been clicked on the checkbox object. If so, * + * the check box state will be toggled. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- The key that resulted in this routine being called. * + * * + * OUTPUT: bool; Should normal processing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } + return(ToggleClass::Action(flags, key)); +} diff --git a/CODE/CHECKBOX.H b/CODE/CHECKBOX.H new file mode 100644 index 0000000..10d8776 --- /dev/null +++ b/CODE/CHECKBOX.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CHECKBOX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CHECKBOX.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : May 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "toggle.h" + +class CheckBoxClass : public ToggleClass +{ + public: + CheckBoxClass(unsigned id, int x, int y) : + ToggleClass(id, x, y, 7, 7) + {}; + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + protected: +}; + +#endif diff --git a/CODE/CHEKLIST.CPP b/CODE/CHEKLIST.CPP new file mode 100644 index 0000000..c35cc41 --- /dev/null +++ b/CODE/CHEKLIST.CPP @@ -0,0 +1,365 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CHEKLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CHEKLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckListClass::Action -- action function for this class * + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * CheckListClass::CheckListClass -- constructor * + * CheckListClass::Check_Item -- [un]checks an items * + * CheckListClass::Draw_Entry -- draws a list box entry * + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spe* + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * CheckListClass::~CheckListClass -- destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CheckListClass::CheckListClass -- constructor * + * * + * INPUT: * + * id control ID for this list box * + * x x-coord * + * y y-coord * + * w width * + * h height * + * flags mouse event flags * + * up ptr to Up-arrow shape * + * down ptr to Down-arrow shape * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + IsReadOnly(false) +{ +} + + +/*********************************************************************************************** + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * * + * This destructor will delete all entries attached to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CheckListClass::~CheckListClass(void) +{ + while (CheckListClass::Count()) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(0); + + ListClass::Remove_Item(0); + delete obj; + } +} + + +/*********************************************************************************************** + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * * + * This routine will add the specified text string to the check list. * + * * + * INPUT: text -- Pointer to the text string to add to the list box. * + * * + * OUTPUT: Returns the index number where the text object was added. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1996 JLB : Created. * + *=============================================================================================*/ +int CheckListClass::Add_Item(char const * text) +{ + CheckObject * obj = new CheckObject(text, false); + return(ListClass::Add_Item((char const *)obj)); +} + + +char const * CheckListClass::Current_Item(void) const +{ + CheckObject * obj = (CheckObject *)ListClass::Current_Item(); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * * + * This routine will find the text associated with the entry specified and return a pointer * + * to that text. * + * * + * INPUT: index -- The entry (index) to fetch a pointer to. * + * * + * OUTPUT: Returns with the text pointer associated with the index specified. * + * * + * WARNINGS: If the index is out of range, then NULL is returned. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * CheckListClass::Get_Item(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * * + * This routine will find the entry that matches the text pointer specified and then * + * delete that entry. * + * * + * INPUT: text -- The text pointer to use to find the exact match in the list. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Remove_Item(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + ListClass::Remove_Item(index); + delete obj; + break; + } + } +} + + +/*********************************************************************************************** + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spec * + * * + * This routine will find the entry that exactly matches the text pointer specified. If * + * found, then that entry will be set as the currently selected index. * + * * + * INPUT: text -- Pointer to the text string to find the match for. * + * * + * OUTPUT: none * + * * + * WARNINGS: If an exact match to the specified text string could not be found, then the * + * currently selected index is not changed. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Set_Selected_Index(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + Set_Selected_Index(index); + break; + } + } +} + + +/*************************************************************************** + * CheckListClass::Check_Item -- [un]checks an items * + * * + * INPUT: * + * index index of item to check or uncheck * + * checked 0 = uncheck, non-zero = check * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +void CheckListClass::Check_Item(int index, bool checked) +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && obj->IsChecked != checked) { + obj->IsChecked = checked; + Flag_To_Redraw(); + } +} + + +/*************************************************************************** + * CheckListClass::Is_Checked -- returns checked state of an item * + * * + * INPUT: * + * index index of item to query * + * * + * OUTPUT: * + * 0 = item is unchecked, 1 = item is checked * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +bool CheckListClass::Is_Checked(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->IsChecked); + } + return(false); +} + + +/*************************************************************************** + * CheckListClass::Action -- action function for this class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Action(unsigned flags, KeyNumType &key) +{ + int rc; + + /* + ** If this is a read-only list, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** Invoke parents Action first, so it can set the SelectedIndex if needed. + */ + rc = ListClass::Action(flags, key); + + /* + ** Now, if this event was a left-press, toggle the checked state of the + ** current item. + */ + if (flags & LEFTPRESS) { + Check_Item(SelectedIndex, !Is_Checked(SelectedIndex)); + } + + return(rc); +} + + +/*************************************************************************** + * CheckListClass::Draw_Entry -- draws a list box entry * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1995 BRR : Created. * + *=========================================================================*/ +void CheckListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (index >= Count()) return; + + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + + if (obj) { + char buffer[100] = ""; + + if (obj->IsChecked) { + buffer[0] = CHECK_CHAR; + } else { + buffer[0] = UNCHECK_CHAR; + } + buffer[1] = ' '; + sprintf(&buffer[2], obj->Text); + + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags, width, Tabs); + } +} + diff --git a/CODE/CHEKLIST.H b/CODE/CHEKLIST.H new file mode 100644 index 0000000..d123649 --- /dev/null +++ b/CODE/CHEKLIST.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CHEKLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CHEKLIST.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * This class behaves just like the standard list box, except that if the * + * first character of a list entry is a space, clicking on it toggles the * + * space with a check-mark ('\3'). This makes each entry in the list box * + * "toggle-able". * + *-------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHEKLIST_H +#define CHEKLIST_H + +#include "list.h" + +class CheckObject +{ + public: + CheckObject(char const * text = 0, bool checked=false) : + Text(text), + IsChecked(checked) + {}; + + char const * Text; + bool IsChecked; +}; + + +class CheckListClass : public ListClass +{ + public: + /* + ** Constructor/Destructor + */ + CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + ~CheckListClass(void); + + virtual int Add_Item(int text) {return ListClass::Add_Item(text);} + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void) const; + virtual char const * Get_Item(int index) const; + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int text) {ListClass::Remove_Item(text);} + virtual void Set_Selected_Index(char const * text); + virtual void Set_Selected_Index(int index) {ListClass::Set_Selected_Index(index);}; + + /* + ** Checkmark utility functions + */ + void Check_Item(int index, bool checked); // sets checked state of item + bool Is_Checked(int index) const; // gets checked state of item + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + /* + ** This defines the ASCII value of the checkmark character & non-checkmark + ** character. + */ + typedef enum CheckListClassEnum { + CHECK_CHAR = '\3', + UNCHECK_CHAR = ' ' + } CheckListClassEnum; + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + bool IsReadOnly; +}; + + +#endif diff --git a/CODE/CLASS.CPP b/CODE/CLASS.CPP new file mode 100644 index 0000000..d912187 --- /dev/null +++ b/CODE/CLASS.CPP @@ -0,0 +1,19 @@ +/* +** 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 . +*/ + + diff --git a/CODE/CO-WC32.LNT b/CODE/CO-WC32.LNT new file mode 100644 index 0000000..e3f749b --- /dev/null +++ b/CODE/CO-WC32.LNT @@ -0,0 +1,67 @@ +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + diff --git a/CODE/COLRLIST.CPP b/CODE/COLRLIST.CPP new file mode 100644 index 0000000..0caea38 --- /dev/null +++ b/CODE/COLRLIST.CPP @@ -0,0 +1,278 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COLRLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : COLRLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : April 19, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ColorListClass::Add_Item -- Adds an item to the list * + * ColorListClass::ColorListClass -- Class constructor * + * ColorListClass::Draw_Entry -- Draws one text line * + * ColorListClass::Remove_Item -- Removes an item from the list * + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * ColorListClass::~ColorListClass -- Class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ColorListClass::ColorListClass -- class constructor * + * * + * INPUT: * + * id button ID * + * x,y upper-left corner, in pixels * + * w,h width, height, in pixels * + * list ptr to array of char strings to list * + * flags flags for mouse, style of listbox * + * up,down pointers to shapes for up/down buttons * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ColorListClass::ColorListClass (int id, int x, int y, int w, int h, + TextPrintType flags, void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + Style(SELECT_HIGHLIGHT), + SelectColor(NULL) +{ +} + + +/*************************************************************************** + * ColorListClass::~ColorListClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +ColorListClass::~ColorListClass(void) +{ + Colors.Clear(); + SelectColor = 0; +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(char const * text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(int text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Remove_Item -- Removes an item from the list * + * * + * INPUT: * + * text ptr to item to remove * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Remove_Item(char const * text) +{ + int index = List.ID(text); + if (index != -1) { + Colors.Delete(index); + ListClass::Remove_Item(text); + } +} + + +/*************************************************************************** + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * * + * INPUT: * + * style style to draw * + * color color to draw the special style in; -1 = use item's color * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Set_Selected_Style(SelectStyleType style, RemapControlType * color) +{ + Style = style; + SelectColor = color; +} + + +/*************************************************************************** + * ColorListClass::Draw_Entry -- Draws one text line * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * color; + + /* + ** Draw a non-selected item in its color + */ + if (!selected) { + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + return; + } + + /* + ** For selected items, choose the right color & style: + */ + if (SelectColor == NULL) { + color = Colors[index]; + } else { + color = SelectColor; + } + + switch (Style) { + /* + ** NONE: Just print the string in its native color + */ + case SELECT_NORMAL: + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, + TextFlags, width, Tabs); + break; + + /* + ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must + ** be set) + */ + case SELECT_HIGHLIGHT: + if (TextFlags & TPF_6PT_GRAD) { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** BOX: Draw a box around the item in the current select color + */ + case SELECT_BOX: + LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** BAR: draw a color bar under the text + */ + case SELECT_BAR: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + LogicPage->Fill_Rect(x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** INVERT: Draw text as the background color on foreground color + */ + case SELECT_INVERT: + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]->Color); + break; + } +} diff --git a/CODE/COLRLIST.H b/CODE/COLRLIST.H new file mode 100644 index 0000000..57f3439 --- /dev/null +++ b/CODE/COLRLIST.H @@ -0,0 +1,87 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COLRLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : COLRLIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COLORLIST_H +#define COLORLIST_H + +#include "list.h" + + +/*************************************************************************** +** This class adds the ability for every list item to have a different color. +*/ +class ColorListClass : public ListClass +{ + public: + /********************************************************************* + ** These enums are the ways a selected item can be drawn + */ + //lint -esym(578,SELECT_NONE) + typedef enum SelectEnum { + SELECT_NORMAL, // selected items aren't drawn differently + SELECT_HIGHLIGHT, // item is highlighted + SELECT_BOX, // draw a box around the item + SELECT_BAR, // draw a bar behind the item + SELECT_INVERT // draw the string inverted + } SelectStyleType; + + ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual ~ColorListClass(void); + + virtual int Add_Item(char const * text, RemapControlType * color = NULL); + virtual int Add_Item(int text, RemapControlType * color = NULL); + virtual void Remove_Item(char const * text); + + virtual void Set_Selected_Style(SelectStyleType style, RemapControlType * color = NULL); + + /* + ** This is the list of colors for each item. + */ + DynamicVectorClass Colors; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This tells how to draw the selected item. + */ + SelectStyleType Style; + RemapControlType * SelectColor; + +}; + +#endif diff --git a/CODE/COMBAT.CPP b/CODE/COMBAT.CPP new file mode 100644 index 0000000..1e25864 --- /dev/null +++ b/CODE/COMBAT.CPP @@ -0,0 +1,425 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COMBAT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : COMBAT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 19, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Combat_Anim -- Determines explosion animation to play. * + * Explosion_Damage -- Inflict an explosion damage affect. * + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * Wide_Area_Damage -- Apply wide area damage to the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * * + * This routine is the core of combat tactics. It implements the * + * affect various armor types have against various weapon types. By * + * careful exploitation of this table, tactical advantage can be * + * obtained. * + * * + * INPUT: damage -- The damage points to process. * + * * + * warhead -- The source of the damage points. * + * * + * armor -- The type of armor defending against the damage. * + * * + * distance -- The distance (in leptons) from the source of the damage. * + * * + * OUTPUT: Returns with the adjusted damage points to inflict upon the * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 04/17/1994 JLB : Always does a minimum of damage. * + * 01/01/1995 JLB : Takes into account distance from damage source. * + * 04/11/1996 JLB : Changed damage fall-off formula for less damage fall-off. * + *=============================================================================================*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance) +{ + if (!damage) return(damage); + + /* + ** If there is no raw damage value to start with, then + ** there can be no modified damage either. + */ + if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0); + + /* + ** Negative damage (i.e., heal) is always applied full strength, but only if the heal + ** effect is close enough. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (distance < 0x008) { + if(warhead != WARHEAD_MECHANICAL && armor == ARMOR_NONE) return(damage); + if(warhead == WARHEAD_MECHANICAL && armor != ARMOR_NONE) return(damage); + } +#else + if (distance < 0x008 && armor == ARMOR_NONE) return(damage); +#endif + return(0); + } + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; + + damage = damage * whead->Modifier[armor]; + + /* + ** Reduce damage according to the distance from the impact point. + */ + if (damage) { + if (!whead->SpreadFactor) { + distance /= PIXEL_LEPTON_W/4; + } else { + distance /= whead->SpreadFactor * (PIXEL_LEPTON_W/2); + } + distance = Bound(distance, 0, 16); + if (distance) { + damage = damage / distance; + } + + /* + ** Allow damage to drop to zero only if the distance would have + ** reduced damage to less than 1/4 full damage. Otherwise, ensure + ** that at least one damage point is done. + */ + if (distance < 4) { + damage = max(damage, Rule.MinDamage); + } + } + + damage = min(damage, Rule.MaxDamage); + return(damage); +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + * 06/18/1996 JLB : Strength could be negative for healing effects. * + *=============================================================================================*/ +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead) +{ + CELL cell; // Cell number under explosion. + ObjectClass * object; // Working object pointer. + ObjectClass * objects[32]; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int count; // Number of vehicle IDs in list. + + if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return; + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; +// range = ICON_LEPTON_W*2; + range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1); + cell = Coord_Cell(coord); + if ((unsigned)cell >= MAP_CELL_TOTAL) return; + + CellClass * cellptr = &Map[cell]; + ObjectClass * impacto = cellptr->Cell_Occupier(); + + /* + ** Fill the list of unit IDs that will have damage + ** assessed upon them. The units can be lifted from + ** the cell data directly. + */ + count = 0; + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + /* + ** Fetch a pointer to the cell to examine. This is either + ** an adjacent cell or the center cell. Damage never spills + ** further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /* + ** Add all objects in this cell to the list of objects to possibly apply + ** damage to. The list stops building when the object pointer list becomes + ** full. Do not include overlapping objects; selection state can affect + ** the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(); + while (object) { + if (!object->IsToDamage && object != source) { + object->IsToDamage = true; + objects[count++] = object; + if (count >= ARRAY_SIZE(objects)) break; + } + object = object->Next; + } + if (count >= ARRAY_SIZE(objects)) break; + } + + /* + ** Sweep through the units to be damaged and damage them. When damaging + ** buildings, consider a hit on any cell the building occupies as if it + ** were a direct hit on the building's center. + */ + for (int index = 0; index < count; index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->IsActive) { + if (object->What_Am_I() == RTTI_BUILDING && impacto == object) { + distance = 0; + } else { + distance = Distance(coord, object->Center_Coord()); + } + if (object->IsDown && !object->IsInLimbo && distance < range) { + int damage = strength; + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + /* + ** If there is a wall present at this location, it may be destroyed. Check to + ** make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsTiberium && whead->IsTiberiumDestroyer) { + cellptr->Reduce_Tiberium(strength / 10); + } + if (optr->IsWall) { + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + Map[cell].Reduce_Wall(strength); + } + } + } + + /* + ** If there is a bridge at this location, then it may be destroyed by the + ** combat damage. + */ + if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B ) { + + if (((warhead == WARHEAD_AP || warhead == WARHEAD_HE) && Random_Pick(1, Rule.BridgeStrength) < strength)) { + Map.Destroy_Bridge_At(cell); + } + } +} + + +/*********************************************************************************************** + * Combat_Anim -- Determines explosion animation to play. * + * * + * This routine is called when a projectile impacts. This routine will determine what * + * animation should be played. * + * * + * INPUT: damage -- The amount of damage this warhead possess (warhead size). * + * * + * warhead -- The type of warhead. * + * * + * land -- The land type that this explosion is over. Sometimes, this makes * + * a difference (especially over water). * + * * + * OUTPUT: Returns with the animation to play. If no animation is to be played, then * + * ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land) +{ + /* + ** For cases of no damage or invalid warhead, don't have any + ** animation effect at all. + */ + if (damage == 0 || warhead == WARHEAD_NONE) { + return(ANIM_NONE); + } + + static AnimType _aplist[] = { + ANIM_VEH_HIT3, // Small fragment throwing explosion -- burn/exp mix. + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_FRAG1, // Medium fragment throwing explosion -- short decay. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _helist[] = { + ANIM_VEH_HIT1, // Small fireball explosion (bulges rightward). + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_ART_EXP1, // Large fragment throwing explosion -- many sparkles. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _firelist[] = { + ANIM_NAPALM1, // Small napalm burn. + ANIM_NAPALM2, // Medium napalm burn. + ANIM_NAPALM3, // Large napalm burn. + }; + + static AnimType _waterlist[] = { + ANIM_WATER_EXP3, + ANIM_WATER_EXP2, + ANIM_WATER_EXP1, + }; + + WarheadTypeClass const * wptr = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * wptr = &Warheads[warhead]; + switch (wptr->ExplosionSet) { + case 6: + return(ANIM_ATOM_BLAST); + + case 2: + if (damage > 15) { + return(ANIM_PIFFPIFF); + } + return(ANIM_PIFF); + + case 4: + if (land == LAND_NONE) return(ANIM_FLAK); +// Fixed math error + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 90), 90)]); + return(_aplist[(ARRAY_SIZE(_aplist)-1) * fixed(min(damage, 90), 90)]); + + case 5: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 130), 130)]); + return(_helist[(ARRAY_SIZE(_helist)-1) * fixed(min(damage, 130), 130)]); + + case 3: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 150), 150)]); + return(_firelist[(ARRAY_SIZE(_firelist)-1) * fixed(min(damage, 150), 150)]); + + case 1: + return(ANIM_PIFF); + + default: + break; + } + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Wide_Area_Damage -- Apply wide area damage to the map. * + * * + * This routine will apply damage to a very wide area on the map. The damage will be * + * spread out from the coordinate specified by the radius specified. The amount of damage * + * will attenuate according to the distance from center. * + * * + * INPUT: coord -- The coordinate that the explosion damage will center about. * + * * + * radius -- The radius of the explosion. * + * * + * damage -- The amount of damage to apply at the center location. * + * * + * source -- Pointer to the purpetrator of the damage (if any). * + * * + * warhead -- The type of warhead that is causing the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int rawdamage, TechnoClass * source, WarheadType warhead) +{ + int cell_radius = (radius + CELL_LEPTON_W-1) / CELL_LEPTON_W; + CELL cell = Coord_Cell(coord); + + for (int x = -cell_radius; x <= cell_radius; x++) { + for (int y = -cell_radius; y <= cell_radius; y++) { + int xpos = Cell_X(cell) + x; + int ypos = Cell_Y(cell) + y; + + /* + ** If the potential damage cell is outside of the map bounds, + ** then don't process it. This unusual check method ensures that + ** damage won't wrap from one side of the map to the other. + */ + if ((unsigned)xpos > MAP_CELL_W) { + continue; + } + if ((unsigned)ypos > MAP_CELL_H) { + continue; + } + CELL tcell = XY_Cell(xpos, ypos); + if (!Map.In_Radar(tcell)) continue; + + int dist_from_center = Distance(XY_Coord(x+cell_radius, y+cell_radius), XY_Coord(cell_radius, cell_radius)); + int damage = rawdamage * Inverse(fixed(cell_radius, dist_from_center)); + Explosion_Damage(Cell_Coord(tcell), damage, source, warhead); + if (warhead == WARHEAD_FIRE && damage > 100) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell)); + } + } + } +} diff --git a/CODE/COMBUF.CPP b/CODE/COMBUF.CPP new file mode 100644 index 0000000..c52a995 --- /dev/null +++ b/CODE/COMBUF.CPP @@ -0,0 +1,1164 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COMBUF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : COMBUF.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : October 23, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommBufferClass::CommBufferClass -- class constructor * + * CommBufferClass::~CommBufferClass -- class destructor * + * CommBufferClass::Init -- initializes this queue * + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * CommBufferClass::Queue_Send -- queues a message for sending * + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * CommBufferClass::Queue_Receive -- queues a received message * + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue* + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * CommBufferClass::Add_Delay -- adds a new delay value for response time* + * CommBufferClass::Avg_Response_Time -- returns average response time * + * CommBufferClass::Max_Response_Time -- returns max response time * + * CommBufferClass::Reset_Response_Time -- resets computations * + * Mono_Debug_Print -- Debug output routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include "combuf.h" +#include "connect.h" // for command names for debug output +#include "wwlib32.h" // to enable mono output + + +/*************************************************************************** + * CommBufferClass::CommBufferClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen, + int extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + MaxExtraSize = extralen; + + //------------------------------------------------------------------------ + // Allocate the queue entries + //------------------------------------------------------------------------ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + SendIndex = new int[numsend]; + ReceiveIndex = new int[numreceive]; + + //------------------------------------------------------------------------ + // Allocate queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + SendQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + SendQueue[i].ExtraBuffer = NULL; + } + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + ReceiveQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + ReceiveQueue[i].ExtraBuffer = NULL; + } + } + + Init(); + +} /* end of CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::~CommBufferClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::~CommBufferClass() +{ + int i; + + //------------------------------------------------------------------------ + // Free queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + if (SendQueue[i].ExtraBuffer) { + delete [] SendQueue[i].ExtraBuffer; + } + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + if (ReceiveQueue[i].ExtraBuffer) { + delete [] ReceiveQueue[i].ExtraBuffer; + } + } + + delete [] SendQueue; + delete [] ReceiveQueue; + + delete [] SendIndex; + delete [] ReceiveIndex; + +} /* end of ~CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Init(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + + ReceiveCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + ReceiveQueue[i].ExtraLen = 0; + + ReceiveIndex[i] = 0; + } + + //------------------------------------------------------------------------ + // Init debug values + //------------------------------------------------------------------------ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugNameCount = 0; + +} /* end of Init */ + + +/*************************************************************************** + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/23/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Init_Send_Queue(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + +} /* end of Init_Send_Queue */ + + +/*************************************************************************** + * CommBufferClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Send(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (SendCount==MaxSend || buflen > MaxPacketSize) + return(0); + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxSend; i++) { + if (SendQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[index].IsActive = 1; // entry is now active + SendQueue[index].IsACK = 0; // entry hasn't been ACK'd + SendQueue[index].FirstTime = 0L; // filled in by Manager when sent + SendQueue[index].LastTime = 0L; // filled in by Manager when sent + SendQueue[index].SendCount = 0L; // filled in by Manager when sent + SendQueue[index].BufLen = buflen; // save buffer size + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(SendQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(SendQueue[index].ExtraBuffer,extrabuf,extralen); + SendQueue[index].ExtraLen = extralen; + } + else { + SendQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + SendIndex[SendCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + SendCount++; + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index "index" of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendIndex[index]].Buffer, + SendQueue[SendIndex[index]].BufLen); + (*buflen) = SendQueue[SendIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,SendQueue[SendIndex[index]].ExtraBuffer, + SendQueue[SendIndex[index]].ExtraLen); + (*extralen) = SendQueue[SendIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[SendIndex[index]].IsActive = 0; + SendQueue[SendIndex[index]].IsACK = 0; + SendQueue[SendIndex[index]].FirstTime = 0L; + SendQueue[SendIndex[index]].LastTime = 0L; + SendQueue[SendIndex[index]].SendCount = 0L; + SendQueue[SendIndex[index]].BufLen = 0; + SendQueue[SendIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < SendCount - 1; i++) { + SendIndex[i] = SendIndex[i + 1]; + } + SendIndex[SendCount - 1] = 0; + SendCount--; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommBufferClass::Get_Send(int index) +{ + if (SendQueue[SendIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&SendQueue[SendIndex[index]]); + } + +} /* end of Get_Send */ + + +/*************************************************************************** + * CommBufferClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Receive(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (ReceiveCount==MaxReceive || buflen > MaxPacketSize) { + return(0); + } + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxReceive; i++) { + if (ReceiveQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[index].IsActive = 1; + ReceiveQueue[index].IsRead = 0; + ReceiveQueue[index].IsACK = 0; + ReceiveQueue[index].BufLen = buflen; + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(ReceiveQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(ReceiveQueue[index].ExtraBuffer,extrabuf,extralen); + ReceiveQueue[index].ExtraLen = extralen; + } + else { + ReceiveQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + ReceiveIndex[ReceiveCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + ReceiveCount++; + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index index of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer, + ReceiveQueue[ReceiveIndex[index]].BufLen); + (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,ReceiveQueue[ReceiveIndex[index]].ExtraBuffer, + ReceiveQueue[ReceiveIndex[index]].ExtraLen); + (*extralen) = ReceiveQueue[ReceiveIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[ReceiveIndex[index]].IsActive = 0; + ReceiveQueue[ReceiveIndex[index]].IsRead = 0; + ReceiveQueue[ReceiveIndex[index]].IsACK = 0; + ReceiveQueue[ReceiveIndex[index]].BufLen = 0; + ReceiveQueue[ReceiveIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < ReceiveCount - 1; i++) { + ReceiveIndex[i] = ReceiveIndex[i + 1]; + } + ReceiveIndex[ReceiveCount - 1] = 0; + ReceiveCount--; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommBufferClass::Get_Receive(int index) +{ + if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&ReceiveQueue[ReceiveIndex[index]]); + } + +} /* end of Get_Receive */ + + +/*************************************************************************** + * CommBufferClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } + else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommBufferClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Configure_Debug(int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + DebugOffset = type_offset; + DebugSize = type_size; + DebugNames = names; + DebugNameStart = namestart; + DebugNameCount = namecount; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // If few enough entries, call the verbose debug version + //------------------------------------------------------------------------ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + if (MaxSend <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } + else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + if (MaxReceive <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } + else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (SendQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], SendQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (ReceiveQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], ReceiveQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + +/************************** end of combuf.cpp ******************************/ + + diff --git a/CODE/COMBUF.H b/CODE/COMBUF.H new file mode 100644 index 0000000..029452e --- /dev/null +++ b/CODE/COMBUF.H @@ -0,0 +1,193 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COMBUF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : COMBUF.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to store outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * * + * This class stores buffers in a non-sequenced order; it allows freeing * + * any entry, so the buffers can be kept clear, even if packets come in * + * out of order. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMBUF_H +#define COMBUF_H + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommBufferClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommBufferClass(int numsend, int numrecieve, int maxlen, + int extralen = 0); + virtual ~CommBufferClass(); + void Init(void); + void Init_Send_Queue(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int type_offset, int type_size, char **names, + int namestart, int namecount); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + int MaxExtraSize; // max size of extra bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + unsigned long SendTotal; // total # added to send queue + int *SendIndex; // array of Send entry indices + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + unsigned long ReceiveTotal; // total # added to receive queue + int *ReceiveIndex; // array of Receive entry indices + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugNameStart; // number of 1st ID + int DebugNameCount; // # of names in array +}; + +#endif + +/**************************** end of combuf.h ******************************/ + diff --git a/CODE/COMINIT.CPP b/CODE/COMINIT.CPP new file mode 100644 index 0000000..73e6608 --- /dev/null +++ b/CODE/COMINIT.CPP @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +// +// If you link with this it will automatically call the COM initialization stuff +// + +#include "cominit.h" +//#include +//#include +//#include +#include +//#include "externs.h" +//#include "text.rh" + +//#include "WolDebug.h" + +ComInit::ComInit() +{ + //HRESULT hRes = CoInitialize(NULL); +// if (SUCCEEDED(hRes)==FALSE) +// exit(0); + HRESULT hRes = OleInitialize(NULL); +} + +ComInit::~ComInit() +{ +// CoUninitialize(); +// debugprint( "pre OleUninitialize\n" ); + OleUninitialize(); +// debugprint( "post OleUninitialize\n" ); +} + +// Creating this instance will setup all COM stuff & do cleanup on program exit +ComInit Global_COM_Initializer; + diff --git a/CODE/COMINIT.H b/CODE/COMINIT.H new file mode 100644 index 0000000..172db63 --- /dev/null +++ b/CODE/COMINIT.H @@ -0,0 +1,34 @@ +/* +** 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 . +*/ + +#ifndef COMINIT_HEADER +#define COMINIT_HEADER + +// +// Link with this to automatically initialize COM at startup +// - See cominit.cpp for more info +// + +class ComInit +{ + public: + ComInit(); + ~ComInit(); +}; + +#endif diff --git a/CODE/COMPAT.H b/CODE/COMPAT.H new file mode 100644 index 0000000..528dcb8 --- /dev/null +++ b/CODE/COMPAT.H @@ -0,0 +1,217 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COMPAT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : COMPAT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/95 * + * * + * Last Update : March 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMPAT_H +#define COMPAT_H + + +#define BuffType BufferClass +//#define movmem(a,b,c) memmove(b,a,c) +#define ShapeBufferSize _ShapeBufferSize + +/*=========================================================================*/ +/* Define some equates for the different graphic routines we will install */ +/* later. */ +/*=========================================================================*/ +#define HIDBUFF ((void *)(0xA0000)) +#define Size_Of_Region(a, b) ((a)*(b)) + +/*=========================================================================*/ +/* Define some Graphic Routines which will only be fixed by these defines */ +/*=========================================================================*/ +#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15) + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. + +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + +#define ERROR_WINDOW 1 +#define ErrorWindow 1 + + +//extern unsigned char *Palette; +extern unsigned char MDisabled; // Is mouse disabled? +extern WORD Hard_Error_Occured; + +/* +** This is the menu control structures. +*/ +typedef enum MenuIndexType { + MENUX, + MENUY, + ITEMWIDTH, + ITEMSHIGH, + MSELECTED, + NORMCOL, + HILITE, + MENUPADDING=0x1000 +} MenuIndexType; + + +#ifdef NEVER +#define BITSPERBYTE 8 +#define MAXSHORT 0x7FFF +#define HIBITS 0x8000 +#define MAXLONG 0x7FFFFFFFL +#define HIBITL 0x80000000 + +#define MAXINT MAXLONG +#define HIBITI HIBITL + +#define DMAXEXP 308 +#define FMAXEXP 38 +#define DMINEXP -307 +#define FMINEXP -37 + +#define MAXDOUBLE 1.797693E+308 +#define MAXFLOAT 3.37E+38F +#define MINDOUBLE 2.225074E-308 +#define MINFLOAT 8.43E-37F + +#define DSIGNIF 53 +#define FSIGNIF 24 + +#define DMAXPOWTWO 0x3FF +#define FMAXPOWTWO 0x7F +#define DEXPLEN 11 +#define FEXPLEN 8 +#define EXPBASE 2 +#define IEEE 1 +#define LENBASE 1 +#define HIDDENBIT 1 +#define LN_MAXDOUBLE 7.0978E+2 +#define LN_MINDOUBLE -7.0840E+2 +#endif + +/* These defines handle the various names given to the same color. */ +#define DKGREEN GREEN +#define DKBLUE BLUE +#define GRAY GREY +#define DKGREY GREY +#define DKGRAY GREY +#define LTGRAY LTGREY + + +class IconsetClass; +#ifndef WIN32 +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; +#endif + +inline int Get_IconSet_MapWidth(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapWidth); + } + return(0); +} + +inline int Get_IconSet_MapHeight(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapHeight); + } + return(0); +} + +inline unsigned char const * Get_IconSet_ControlMap(void const * data) +{ + if (data) { + return((unsigned char const *)((char *)data + ((IControl_Type *)data)->ColorMap)); + } + return(0); +} + +class IconsetClass : protected IControl_Type +{ + public: + /* + ** Query functions. + */ + int Map_Width(void) const {return(MapWidth);}; + int Map_Height(void) const {return(MapHeight);}; + unsigned char * Control_Map(void) {return((unsigned char *)this + ColorMap);}; + unsigned char const * Control_Map(void) const {return((unsigned char const *)this + ColorMap);}; + int Icon_Count(void) const {return(Count);}; + int Pixel_Width(void) const {return(Width);}; + int Pixel_Height(void) const {return(Height);}; + int Total_Size(void) const {return(Size);}; + unsigned char const * Palette_Data(void) const {return((unsigned char const *)this + Palettes);}; + unsigned char * Palette_Data(void) {return((unsigned char *)this + Palettes);}; + unsigned char const * Icon_Data(void) const {return((unsigned char const *)this + Icons);}; + unsigned char * Icon_Data(void) {return((unsigned char *)this + Icons);}; + unsigned char const * Map_Data(void) const {return((unsigned char const *)this + Map);}; + unsigned char * Map_Data(void) {return((unsigned char *)this + Map);}; + unsigned char const * Remap_Data(void) const {return((unsigned char const *)this + Remaps);}; + unsigned char * Remap_Data(void) {return((unsigned char *)this + Remaps);}; + unsigned char const * Trans_Data(void) const {return((unsigned char const *)this + TransFlag);}; + unsigned char * Trans_Data(void) {return((unsigned char *)this + TransFlag);}; + + /* + ** Disallow these operations with an IconsetClass object. + */ + private: + IconsetClass & operator = (IconsetClass const &); + IconsetClass(void); + static void * operator new(size_t); +}; + + +#endif diff --git a/CODE/COMQUEUE.CPP b/CODE/COMQUEUE.CPP new file mode 100644 index 0000000..5f55194 --- /dev/null +++ b/CODE/COMQUEUE.CPP @@ -0,0 +1,1003 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.cpv 4.1 11 Apr 1996 18:28:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : COMQUEUE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * CommQueueClass::Avg_Response_Time -- returns average response time * + * CommQueueClass::CommQueueClass -- class constructor * + * CommQueueClass::Configure_Debug -- sets up special debug values * + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * CommQueueClass::Init -- initializes this queue * + * CommQueueClass::Max_Response_Time -- returns max response time * + * CommQueueClass::Mono_Debug_Print -- Debug output routine * + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * CommQueueClass::Queue_Receive -- queues a received message * + * CommQueueClass::Queue_Send -- queues a message for sending * + * CommQueueClass::Reset_Response_Time -- resets computations * + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * CommQueueClass::~CommQueueClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CommQueueClass::CommQueueClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ** Init variables + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ** Allocate the queue entries + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + /* + ** Allocate queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); +} + + +/*************************************************************************** + * CommQueueClass::~CommQueueClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::~CommQueueClass() +{ + int i; + + /* + ** Free queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; +} + + +/*************************************************************************** + * CommQueueClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Init(void) +{ + int i; + + /* + ** Init data members + */ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + SendNext = 0; + SendEmpty = 0; + + ReceiveCount = 0; + ReceiveNext = 0; + ReceiveEmpty = 0; + + /* + ** Init the queue entries + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + ReceiveQueue[i].BufLen = 0; + } + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + } + + /* + ** Init debug values + */ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; +} + + +/*************************************************************************** + * CommQueueClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Send(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + SendQueue[SendEmpty].IsActive = 1; // entry is now active + SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd + SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].BufLen = buflen; // save buffer size + + /* + ** Copy the packet data + */ + memcpy(SendQueue[SendEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + SendCount++; + SendEmpty++; + if (SendEmpty==MaxSend) { + SendEmpty = 0; + } + + SendTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Send(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (SendCount==0 || SendQueue[SendNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen); + (*buflen) = SendQueue[SendNext].BufLen; + } + + /* + ** Set entry flags + */ + SendQueue[SendNext].IsActive = 0; + SendQueue[SendNext].IsACK = 0; + SendQueue[SendNext].FirstTime = 0L; + SendQueue[SendNext].LastTime = 0L; + SendQueue[SendNext].SendCount = 0L; + SendQueue[SendNext].BufLen = 0; + SendCount--; + SendNext++; + if (SendNext==MaxSend) { + SendNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Next_Send(void) +{ + if (SendCount==0) { + return(NULL); + } else { + return(&SendQueue[SendNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Get_Send(int index) +{ + int i; + + i = SendNext + index; + if (i >= MaxSend) { + i -= MaxSend; + } + + if (SendQueue[i].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Receive(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveEmpty].IsActive = 1; + ReceiveQueue[ReceiveEmpty].IsRead = 0; + ReceiveQueue[ReceiveEmpty].IsACK = 0; + ReceiveQueue[ReceiveEmpty].BufLen = buflen; + + /* + ** Copy the packet data + */ + memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + ReceiveCount++; + ReceiveEmpty++; + if (ReceiveEmpty==MaxReceive) { + ReceiveEmpty = 0; + } + + ReceiveTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Receive(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveNext].Buffer, + ReceiveQueue[ReceiveNext].BufLen); + (*buflen) = ReceiveQueue[ReceiveNext].BufLen; + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveNext].IsActive = 0; + ReceiveQueue[ReceiveNext].IsRead = 0; + ReceiveQueue[ReceiveNext].IsACK = 0; + ReceiveQueue[ReceiveNext].BufLen = 0; + ReceiveCount--; + ReceiveNext++; + if (ReceiveNext==MaxReceive) { + ReceiveNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Next_Receive(void) +{ + if (ReceiveCount==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Get_Receive(int index) +{ + int i; + + i = ReceiveNext + index; + if (i >= MaxReceive) { + i -= MaxReceive; + } + + if (ReceiveQueue[i].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) { + roundoff = 1; + } + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } +} + + +/*************************************************************************** + * CommQueueClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Avg_Response_Time(void) +{ + return(MeanDelay); +} + + +/*************************************************************************** + * CommQueueClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Max_Response_Time(void) +{ + return(MaxDelay); +} + + +/*************************************************************************** + * CommQueueClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; +} + + +/*************************************************************************** + * CommQueueClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; +} + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** If few enough entries, call the verbose debug version + */ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /* + ** Print Receive Queue items + */ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} + + +/*************************************************************************** + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + /* + ** Print an active entry + */ + if (SendQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + + /* + ** Print Receive Queue items + */ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + /* + ** Print an active entry + */ + if (ReceiveQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} + + diff --git a/CODE/COMQUEUE.H b/CODE/COMQUEUE.H new file mode 100644 index 0000000..d5d5d9e --- /dev/null +++ b/CODE/COMQUEUE.H @@ -0,0 +1,193 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.h_v 4.1 11 Apr 1996 18:26:02 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : COMQUEUE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to queue up outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * It allows the application to keep track of how many messages have * + * passed through this queue, in & out, so packets can use this as a * + * unique ID. (If packets have a unique ID, the application can use this * + * to detect re-sends.) * + * * + * The queues act as FIFO buffers (First-In, First-Out). The first entry * + * placed in a queue is the first one read from it, and so on. The * + * controlling application must ensure it places the entries on the queue * + * in the order it wants to access them. * + * * + * The queue is implemented as an array of Queue Entries. Index 0 is the * + * first element placed on the queue, and is the first retrieved. Index * + * 1 is the next, and so on. When Index 0 is retrieved, the next-available* + * entry becomes Index 1. The array is circular; when the end is reached, * + * the indices wrap around to the beginning. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMQUEUE_H +#define COMQUEUE_H + + + +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommQueueClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommQueueClass(int numsend, int numrecieve, int maxlen); + virtual ~CommQueueClass(); + void Init(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen); // remove from Send queue + SendQueueType * Next_Send(void); // ptr to next avail entry + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue + ReceiveQueueType * Next_Receive(void); // ptr to next avail entry + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + int SendNext; // next entry read from queue + int SendEmpty; // next empty spot in queue + unsigned long SendTotal; // total # added to send queue + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + int ReceiveNext; // next entry read from queue + int ReceiveEmpty; // next empty spot in queue + unsigned long ReceiveTotal; // total # added to receive queue + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/*************************** end of comqueue.h *****************************/ + diff --git a/CODE/CONFDLG.CPP b/CODE/CONFDLG.CPP new file mode 100644 index 0000000..7afacc2 --- /dev/null +++ b/CODE/CONFDLG.CPP @@ -0,0 +1,244 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.cpv 4.67 27 Aug 1996 15:46:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONFDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "confdlg.h" + + +bool ConfirmationClass::Process(int text) +{ + return(Process(Text_String(text))); +} + + +/*********************************************************************************************** + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to confirm a deletion. * + * * + * INPUT: char * string - display in edit box. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +bool ConfirmationClass::Process(char const * string) +{ + enum { + NUM_OF_BUTTONS = 2 + }; + + char buffer[80*3]; + int result = true; + int width; + int bwidth, bheight; // button width and height + int height; + int selection = 0; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + strcpy(buffer, string); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + width += 60; + height += 60; + int x = (320 - width) / 2; + int y = (200 - height) / 2; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30); + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + 10, y + height - (bheight + 5), bwidth ); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + width - (bwidth + 10), + y + height - (bheight + 5), bwidth ); + + nobtn.Add_Tail(yesbtn); + + curbutton = 1; + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(yesbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_NO, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(yesbtn); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + result = false; + } + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_CONFIRMATION, x, y, width); + Fancy_Text_Print(buffer, x+20, y+30, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Draw the titles. + */ + yesbtn.Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = yesbtn.Input(); + + /* + ** Process Input. + */ + switch (input) { + case KeyNumType(BUTTON_YES | KN_BUTTON): + selection = BUTTON_YES; + pressed = true; + break; + + case (KN_ESC): + case KeyNumType(BUTTON_NO | KN_BUTTON): + selection = BUTTON_NO; + pressed = true; + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = NUM_OF_BUTTONS - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_YES; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_YES): + result = true; + process = false; + break; + + case (BUTTON_NO): + result = false; + process = false; + break; + + default: + break; + } + + pressed = false; + } + } + return(result); +} diff --git a/CODE/CONFDLG.H b/CODE/CONFDLG.H new file mode 100644 index 0000000..ec272d3 --- /dev/null +++ b/CODE/CONFDLG.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.h_v 4.69 27 Aug 1996 15:43:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONFDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef CONFDLG_H +#define CONFDLG_H + +#include "gadget.h" + +class ConfirmationClass +{ + private: + enum ConfirmationClassEnum { + BUTTON_YES=1, // Button number for "Options menu" + BUTTON_NO // Button number for "Options menu" + }; + + public: + ConfirmationClass(void) { }; + bool Process(char const * string); + bool Process(int text); +}; + +#endif diff --git a/CODE/CONNECT.CPP b/CODE/CONNECT.CPP new file mode 100644 index 0000000..7e87a65 --- /dev/null +++ b/CODE/CONNECT.CPP @@ -0,0 +1,838 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONNECT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Init -- Initializes connection queue to empty * + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Service_Send_Queue -- services the send queue * + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" +#include +#include +#include +#include "connect.h" + +#include "WolDebug.h" + +/* +********************************* Globals *********************************** +*/ +static char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (placed there + by Receive_Packet), and outgoing packets (placed there by Send_Packet). + It can optionally store "extra" bytes, which are stored along with each + packet, but aren't transmitted as part of the packet. If 'extralen' + is 0, the CommBufferClass ignores this parameter. + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen); + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + delete Queue; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void ConnectionClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*------------------------------------------------------------------------ + Set the magic # for the packet + ------------------------------------------------------------------------*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ------------------------------------------------------------------------*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } + else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*------------------------------------------------------------------------ + Now build the packet + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Add it to the queue; don't add any extra data with it. + ------------------------------------------------------------------------*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) { + if (ack_req) { + NumSendAck++; + } + else { + NumSendNoAck++; + } + return(1); + } + else { + return(0); + } + +} /* end of Send_Packet */ + + +/*************************************************************************** + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /*.................................................................. + Get queue entry ptr + ..................................................................*/ + send_entry = Queue->Get_Send(i); + + /*.................................................................. + If ptr is valid, get ptr to its data + ..................................................................*/ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + } + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*..................................................................... + If there's only one slot left, don't tie up the queue with this packet + .....................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + return(0); + } + + /*..................................................................... + Error if we can't queue the packet + .....................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecNoAck++; + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { + /*..................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + .....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { + save_packet = 0; + } + + /*..................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + .....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*..................................................................... + Queue the packet & update our LastSeqID value. + .....................................................................*/ + if (save_packet) { + /*.................................................................. + If there's only one slot left, make sure we only put a packet in it + if this packet will let us increment our LastSeqID; otherwise, we'll + get stuck, forever unable to increment LastSeqID. + ..................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { + return(0); + } + } + + /*.................................................................. + If we can't queue the packet, return; don't send an ACK. + ..................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecAck++; + + /*.................................................................. + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ..................................................................*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................... + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ...............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*..................................................................... + Send an ACK, regardless of whether this was a resend or not. + .....................................................................*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; + Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0); + + return(1); + } + + return(0); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + } + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue: This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + + Service the Receive Queue: This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } + else { + return(0); + } + +} /* end of Service */ + + +/*************************************************************************** + * ConnectionClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /*..................................................................... + Get this queue entry + .....................................................................*/ + send_entry = Queue->Get_Send(i); + + /*..................................................................... + If ACK has been received, unqueue it + .....................................................................*/ + if (send_entry->IsACK) { + + /*.................................................................. + Update this queue's response time + ..................................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /*.................................................................. + Unqueue the packet + ..................................................................*/ + Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + + /*.................................................................. + Send the message + ..................................................................*/ + Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer, + send_entry->ExtraLen); + + /*.................................................................. + Fill in Time fields + ..................................................................*/ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + + /*.................................................................. + Update SendCount + ..................................................................*/ + send_entry->SendCount++; + + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { + return(0); + } + else { + return(1); + } + +} /* end of Service_Send_Queue */ + + +/*************************************************************************** + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + + } + else if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + } + + return(1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ + static struct timeb mytime; // DOS time + unsigned long msec; + +#ifdef WWLIB32_H + + /*------------------------------------------------------------------------ + If the Westwood timer system has been activated, use TickCount's value + ------------------------------------------------------------------------*/ + if (TimerSystemOn) { + return(TickCount); // Westwood Library time + } + /*------------------------------------------------------------------------ + Otherwise, use the DOS timer + ------------------------------------------------------------------------*/ + else { + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + } + +#else + + /*------------------------------------------------------------------------ + If the Westwood library isn't being used, use the DOS timer. + ------------------------------------------------------------------------*/ + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } + else { + return(NULL); + } + +} /* end of Command_Name */ + +/************************** end of connect.cpp *****************************/ + + diff --git a/CODE/CONNECT.CPP.BAK b/CODE/CONNECT.CPP.BAK new file mode 100644 index 0000000..c0dd50a --- /dev/null +++ b/CODE/CONNECT.CPP.BAK @@ -0,0 +1,833 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\connect.cpv 4.76 03 Oct 1996 09:20:28 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Init -- Initializes connection queue to empty * + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Service_Send_Queue -- services the send queue * + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include "connect.h" + +/* +********************************* Globals *********************************** +*/ +static char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (placed there + by Receive_Packet), and outgoing packets (placed there by Send_Packet). + It can optionally store "extra" bytes, which are stored along with each + packet, but aren't transmitted as part of the packet. If 'extralen' + is 0, the CommBufferClass ignores this parameter. + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen); + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + delete Queue; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void ConnectionClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*------------------------------------------------------------------------ + Set the magic # for the packet + ------------------------------------------------------------------------*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ------------------------------------------------------------------------*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } + else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*------------------------------------------------------------------------ + Now build the packet + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Add it to the queue; don't add any extra data with it. + ------------------------------------------------------------------------*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) { + if (ack_req) { + NumSendAck++; + } + else { + NumSendNoAck++; + } + return(1); + } + else { + return(0); + } + +} /* end of Send_Packet */ + + +/*************************************************************************** + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /*.................................................................. + Get queue entry ptr + ..................................................................*/ + send_entry = Queue->Get_Send(i); + + /*.................................................................. + If ptr is valid, get ptr to its data + ..................................................................*/ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + } + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*..................................................................... + If there's only one slot left, don't tie up the queue with this packet + .....................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + return(0); + } + + /*..................................................................... + Error if we can't queue the packet + .....................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecNoAck++; + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { + /*..................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + .....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { + save_packet = 0; + } + + /*..................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + .....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*..................................................................... + Queue the packet & update our LastSeqID value. + .....................................................................*/ + if (save_packet) { + /*.................................................................. + If there's only one slot left, make sure we only put a packet in it + if this packet will let us increment our LastSeqID; otherwise, we'll + get stuck, forever unable to increment LastSeqID. + ..................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { + return(0); + } + } + + /*.................................................................. + If we can't queue the packet, return; don't send an ACK. + ..................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecAck++; + + /*.................................................................. + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ..................................................................*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................... + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ...............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*..................................................................... + Send an ACK, regardless of whether this was a resend or not. + .....................................................................*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; + Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0); + + return(1); + } + + return(0); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + } + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue: This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + + Service the Receive Queue: This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } + else { + return(0); + } + +} /* end of Service */ + + +/*************************************************************************** + * ConnectionClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /*..................................................................... + Get this queue entry + .....................................................................*/ + send_entry = Queue->Get_Send(i); + + /*..................................................................... + If ACK has been received, unqueue it + .....................................................................*/ + if (send_entry->IsACK) { + + /*.................................................................. + Update this queue's response time + ..................................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /*.................................................................. + Unqueue the packet + ..................................................................*/ + Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + + /*.................................................................. + Send the message + ..................................................................*/ + Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer, + send_entry->ExtraLen); + + /*.................................................................. + Fill in Time fields + ..................................................................*/ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + + /*.................................................................. + Update SendCount + ..................................................................*/ + send_entry->SendCount++; + + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { + return(0); + } + else { + return(1); + } + +} /* end of Service_Send_Queue */ + + +/*************************************************************************** + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + + } + else if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + } + + return(1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ + static struct timeb mytime; // DOS time + unsigned long msec; + +#ifdef WWLIB32_H + + /*------------------------------------------------------------------------ + If the Westwood timer system has been activated, use TickCount's value + ------------------------------------------------------------------------*/ + if (TimerSystemOn) { + return(TickCount); // Westwood Library time + } + /*------------------------------------------------------------------------ + Otherwise, use the DOS timer + ------------------------------------------------------------------------*/ + else { + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + } + +#else + + /*------------------------------------------------------------------------ + If the Westwood library isn't being used, use the DOS timer. + ------------------------------------------------------------------------*/ + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } + else { + return(NULL); + } + +} /* end of Command_Name */ + +/************************** end of connect.cpp *****************************/ diff --git a/CODE/CONNECT.H b/CODE/CONNECT.H new file mode 100644 index 0000000..b162f8a --- /dev/null +++ b/CODE/CONNECT.H @@ -0,0 +1,281 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONNECT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * DESCRIPTION: * + * This class represents a single "connection" with another system. It's * + * a pure virtual base class that acts as a framework for other classes. * + * * + * This class contains a CommBufferClass member, which stores received * + * & transmitted packets. The ConnectionClass has virtual functions to * + * handle adding packets to the queue, reading them from the queue, * + * a Send routine for actually sending data, and a Receive_Packet function * + * which is used to tell the connection that a new packet has come in. * + * * + * The virtual Service routines handle all ACK & Retry logic for * + * communicating between this system & another. Thus, any class derived * + * from this class may overload the basic ACK/Retry logic. * + * * + * THE PACKET HEADER: * + * The Connection Classes prefix every packet sent with a header that's * + * local to this class. The header contains a "Magic Number" which should * + * be unique for each product, and Packet "Code", which will tell the * + * receiving end if this is DATA, or an ACK packet, and a packet ID, which * + * is a unique numerical ID for this packet (useful for detecting resends).* + * The header is stored with each packet in the send & receive Queues; * + * it's removed before it's passed back to the application, via * + * Get_Packet() * + * * + * THE CONNECTION MANAGER: * + * It is assumed that there will be a "Connection Manager" class which * + * will handle parsing incoming packets; it will then tell the connection * + * that new packets have come in, and the connection will process them in * + * whatever way it needs to for its protocol (check for resends, handle * + * ACK packets, etc). The job of the connection manager is to parse * + * incoming packets & distribute them to the connections that need to * + * store them (for multi-connection protocols). * + * * + * NOTES ON ACK/RETRY: * + * This class provides a "non-sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * but, the performance is better than a "sequenced" approach. Also, the * + * Packet ID scheme (see below) ensures that the application will read * + * the packets in the proper order. Thus, this class guarantees delivery * + * and order of deliver. * + * * + * Each packet has a unique numerical ID; the ID is set to a count of the * + * number of packets sent. Different count values are provided, for both * + * DATA_ACK & DATA_NOACK packets. This ensures that the counter can be * + * used to detect resends of DATA_ACK packets; the counters for DATA_NOACK * + * packets aren't currently used. Other counters keep track of the * + * last-sequentially-received packet ID (for DATA_ACK packets), so we * + * can check for resends & missed packets, and the last-sequentially-read * + * packet ID, so we can ensure the app reads the packets in order. * + * * + * If the protocol being used already guarantees delivery of packets, * + * no ACK is required for the packets. In this case, the connection * + * class for this protocol can overload the Service routine to avoid * + * sending ACK packets, or the Connection Manager can just mark the * + * packet as ACK'd when it adds it to the Receive Queue for the connection.* + * * + * Derived classes must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef CONNECTION_H +#define CONNECTION_H + +/* +********************************* Includes ********************************** +*/ +#include "combuf.h" + +/* +********************************** Defines ********************************** +*/ +#define CONN_DEBUG 0 +/*--------------------------------------------------------------------------- +This structure is the header prefixed to any packet sent by the application. +MagicNumber: This is a number unique to the application; it's up to the + Receive_Packet routine to check this value, to be sure we're + not getting data from some other product. This value should + be unique for each application. +Code: This will be one of the below-defined codes. +PacketID: This is a unique numerical ID for this packet. The Connection + sets this ID on all packets sent out. +---------------------------------------------------------------------------*/ +typedef struct { + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; +} CommHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + These are the possible values for the Code field of the CommHeaderType: + .....................................................................*/ + enum ConnectionEnum { + PACKET_DATA_ACK, // this is a data packet requiring an ACK + PACKET_DATA_NOACK, // this is a data packet not requiring an ACK + PACKET_ACK, // this is an ACK for a packet + PACKET_COUNT // for computational purposes + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + ConnectionClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen = 0); + virtual ~ConnectionClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int * buflen); + + /*..................................................................... + The main polling routine for the connection. Should be called as often + as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine is used by the retry logic; returns the current time in + 60ths of a second. + .....................................................................*/ + static unsigned long Time (void); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned short Magic_Num (void) { return (MagicNum); } + unsigned long Retry_Delta (void) { return (RetryDelta); } + void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;} + unsigned long Max_Retries (void) { return (MaxRetries); } + void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;} + unsigned long Time_Out (void) { return (Timeout); } + void Set_TimeOut (unsigned long t) { Timeout = t;} + unsigned long Max_Packet_Len (void) { return (MaxPacketLen); } + static char * Command_Name(int command); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue(void); + virtual int Service_Receive_Queue(void); + + /*..................................................................... + This routine actually performs a hardware-dependent data send. It's + pure virtual, so it must be defined by a derived class. The routine + is protected; it's only called by the ACK/Retry logic, not the + application. + .....................................................................*/ + virtual int Send(char *buf, int buflen, void *extrabuf, + int extralen) = 0; + + /*..................................................................... + This is the maximum packet length, including our own internal header. + .....................................................................*/ + int MaxPacketLen; + + /*..................................................................... + Packet staging area; this is where the CommHeaderType gets tacked onto + the application's packet before it's sent. + .....................................................................*/ + char *PacketBuf; + + /*..................................................................... + This is the magic number assigned to this connection. It is the first + few bytes of any transmission. + .....................................................................*/ + unsigned short MagicNum; + + /*..................................................................... + This value determines the time delay before a packet is re-sent. + .....................................................................*/ + unsigned long RetryDelta; + + /*..................................................................... + This is the maximum number of retries allowed for a packet; if this + value is exceeded, the connection is probably broken. + .....................................................................*/ + unsigned long MaxRetries; + + /*..................................................................... + This is the total timeout for this connection; if this time is exceeded + on a packet, the connection is probably broken. + .....................................................................*/ + unsigned long Timeout; + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; + + /*..................................................................... + Names of all packet commands + .....................................................................*/ + static char * Commands[PACKET_COUNT]; +}; + +#endif + +/**************************** end of connect.h *****************************/ diff --git a/CODE/CONNMGR.H b/CODE/CONNMGR.H new file mode 100644 index 0000000..19e298d --- /dev/null +++ b/CODE/CONNMGR.H @@ -0,0 +1,154 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONNMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CONNMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager base class. This is an abstract base * + * class that's just a shell for more functional derived classes. * + * The main job of the Connection Manager classes is to parse a "pool" of * + * incoming packets, which may be from different computers, and distribute * + * those packets to Connection Classes via their Receive_Packet function. * + * * + * This class should be the only access to the network/modem for the * + * application, so if the app needs any functions to access the * + * connections or the queue's, the derived versions of this class should * + * provide them. * + * * + * It's up to the derived class to define: * + * - Service: polling routine; should Service each connection * + * - Init: initialization; should perform hardware-dependent * + * initialization, then Init each connection; this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Send_Message:sends a packet across the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Get_Message: gets a message from the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * * + * If the derived class supports multiple connections, it should provide * + * functions for creating the connections, associating them with a name * + * or ID or both, destroying them, and sending data through all or any * + * connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNMGR_H +#define CONNMGR_H + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONNECTION_NONE = -1 // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/Destructor. These currently do nothing. + .....................................................................*/ + ConnManClass (void) {}; + virtual ~ConnManClass () {}; + + /*..................................................................... + The Service routine: + - Parses incoming packets, and adds them to the Receive Queue for the + Connection Class(s) for this protocol + - Invokes each connection's Service routine; returns an error if the + connection's Service routine indicates an error. + .....................................................................*/ + virtual int Service (void) = 0; + + /*..................................................................... + Sending & receiving data + .....................................................................*/ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE) = 0; + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id) = 0; + + /*..................................................................... + Connection management + .....................................................................*/ + virtual int Num_Connections(void) = 0; + virtual int Connection_ID(int index) = 0; + virtual int Connection_Index(int id) = 0; + + /*..................................................................... + Queue utility routines + .....................................................................*/ + virtual int Global_Num_Send(void) = 0; + virtual int Global_Num_Receive(void) = 0; + virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0; + virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0; + + /*..................................................................... + Timing management + .....................................................................*/ + virtual void Reset_Response_Time(void) = 0; + virtual unsigned long Response_Time(void) = 0; + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) = 0; + + /*..................................................................... + Debugging + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount) = 0; +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh) = 0; +#endif + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This abstract class contains no data members; but a derived class + will contain: + - An instance of one or more derived Connection Classes + - A buffer to store incoming packets + .....................................................................*/ +}; + +#endif diff --git a/CODE/CONQUER.CPP b/CODE/CONQUER.CPP new file mode 100644 index 0000000..e910f3c --- /dev/null +++ b/CODE/CONQUER.CPP @@ -0,0 +1,5558 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONQUER.CPP 6 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * List_Copy -- Makes a copy of a cell offset list. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Owner_From_Name -- Convert an owner name into a bitfield. * + * Play_Movie -- Plays a VQ movie. * + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion. + * Is_Aftermath_Installed -- Function to determine the availability of the AM expansion. + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef TESTCODE +class A { + public: + enum {VAR=1}; +}; + +template +class B { + public: + enum {VAR2=T::VAR}; // this is the line in question. +}; + +B test; +#endif + + + +#include "function.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#else +#include "fakesock.h" +TcpipManagerClass Winsock; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" +#include "vortex.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#define PAGE_RESPOND_KEY KN_RETURN //KN_COMMA +#endif + +#ifdef MPEGMOVIE +#ifdef MCIMPEG +#include "mcimovie.h" +#endif +#include "movie.h" +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user); +#endif + +#define SHAPE_TRANS 0x40 + +void * Get_Shape_Header_Data(void * ptr); +extern bool Spawn_WChat(bool can_launch); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void); +#endif + +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef FIXIT_VERSION_3 // Stalemate games. +extern void Do_Draw(void); +#endif + +#ifdef CHEAT_KEYS +bool bNoMovies = false; +#endif + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(void); +void Keyboard_Process(KeyNumType & input); +static void Message_Input(KeyNumType &input); +static void Color_Cycle(void); +bool Map_Edit_Loop(void); + +extern "C" { + bool UseOldShapeDraw = false; +} + +#ifdef CHEAT_KEYS +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char * string ); +#endif +static void Do_Record_Playback(void); + +void Toggle_Formation(void); + +extern "C" { + extern char * __nheapbeg; +} + +// +// Special module globals for recording and playback +// +char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type +char TeamNumber = 0; // which team was selected? (1-9) +char FormationEvent = 0; // 0 = no event, 1 = formation was toggled + + + /* -----------------10/14/96 7:29PM------------------ + + --------------------------------------------------*/ + +#if(TEN) +void TEN_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +void Main_Game(int argc, char * argv[]) +{ + static bool fade = true; + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + fade = false; + ScenarioInit = 0; // Kludge. + + fade = true; + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + GamePalette.Set(FADE_PALETTE_MEDIUM); + Keyboard->Clear(); + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (Session.Play) { + Hide_Mouse(); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } else { + Show_Mouse(); + } + +#ifdef WIN32 + if (Session.Type == GAME_INTERNET) { + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + } else { +#ifndef WOLAPI_INTEGRATION + DDEServer.Disable(); +#endif // !WOLAPI_INTEGRATION + } +#endif //WIN32 + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + PlayerPtr->Flag_To_Lose(); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + /* + ** Call the game's main loop + */ + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + +/*ifdef FIXIT_VERSION_3 // Stalemate games. + case SDLG_PROPOSE_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_PROPOSE_DRAW)) { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + case SDLG_ACCEPT_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_ACCEPT_DRAW)) { + OutList.Add(EventClass(EventClass::ACCEPT_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; +#endif +*/ + default: + break; + } + } + } +#endif + + +#ifdef WIN32 + /* + ** Send the game stats to WChat if we haven't already done so + */ + if (!GameStatisticsPacketSent && PacketLater) { + Send_Statistics_Packet(); // After game sending if PacketLater set. + } +#endif //WIN32 + + /* + ** Scenario is done; fade palette to black + */ + BlackPalette.Set(FADE_PALETTE_SLOW); + VisiblePage.Clear(); + + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if (Session.Record || Session.Play) { + Session.RecordFile.Close(); + } + + if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) { + if (!Session.Play) { + Modem_Signoff(); + } + } else { + if (Session.Type == GAME_IPX) { + if (!Session.Play) { + Shutdown_Network(); + } + } + } + +#if(TEN) + + if (Session.Type == GAME_TEN) { + Shutdown_TEN(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + Shutdown_MPATH(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // MPATH + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (Session.Play) { + Show_Mouse(); + Session.Type = GAME_NORMAL; + Session.Play = 0; + } +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + if (Special.IsFromWChat) { + Shutdown_Network(); // Clear up the pseudo IPX stuff +#ifndef WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + } + + /* + ** Free the scenario description buffers + */ + Session.Free_Scenario_Descriptions(); +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +void Keyboard_Process(KeyNumType & input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); + +#ifdef WIN32 + /* + ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to + ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed. + */ + + KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT)); + KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT); + + +#else + KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT)); + KeyNumType key = plain; +#endif + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + HousesType h; + + switch (int(input)) { + case int(int(KN_M) | int(KN_SHIFT_BIT)): + case int(int(KN_M) | int(KN_ALT_BIT)): + case int(int(KN_M) | int(KN_CTRL_BIT)): + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + Houses.Ptr(h)->Refund_Money(10000); + } + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +#ifdef CHEAT_KEYS +#ifdef WIN32 + if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) { +#else + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { +#endif + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) { + if (Session.Type == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + } + + if (Debug_Flag && input == KN_SLASH) { + if (Session.Type != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +#endif + + /* + ** Process prerecorded team selection. This will be an additive select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; +#ifdef WIN32 + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; +#else + if (input & KN_SHIFT_BIT) action = 1; + if (input & KN_ALT_BIT) action = 3; + if (input & KN_CTRL_BIT) action = 2; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + if (key != 0 && key == Options.KeyNext) { + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + if (key != 0 && key == Options.KeyPrevious) { + if (action) { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + + + /* + ** All selected units will go into idle mode. + */ + if (key != 0 && key == Options.KeyStop) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to go into guard area mode. + */ + if (key != 0 && key == Options.KeyGuard) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to scatter. + */ + if (key != 0 && key == Options.KeyScatter) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) { + if (CurrentObject.Count()) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif + input = KN_NONE; + } else { + input = Options.KeyBase; + } + } + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + if (key != 0 && key == Options.KeyBase) { + Unselect_All(); + if (PlayerPtr->CurBuildings) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + if (building->IsLeader) break; + } + } + } + if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) { + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + } + if (CurrentObject.Count()) { + Map.Center_Map(); + } else { + if (PlayerPtr->Center != 0) { + Map.Center_Map(PlayerPtr->Center); + } + } + Map.Flag_To_Redraw(true); + input = KN_NONE; + } + + /* + ** Toggle the status of formation for the current team + */ + if (key != 0 && key == Options.KeyFormation) { + Toggle_Formation(); + input = KN_NONE; + } + +#ifdef TOFIX + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + if (input != 0 && input == Options.KeyResign) { + if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + input = KN_NONE; + } +#endif + + /* + ** Handle making and breaking alliances. + */ + if (key != 0 && key == Options.KeyAlliance) { + if (Session.Type != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); + } + } + } + input = KN_NONE; + } + + /* + ** Select all the units on the current display. This is equivalent to + ** drag selecting the whole view. + */ + if (key != 0 && key == Options.KeySelectView) { + Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight)); + input = KN_NONE; + } + + /* + ** Toggles the repair state similarly to pressing the repair button. + */ + if (key != 0 && key == Options.KeyRepair) { + Map.Repair_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the sell state similarly to pressing the sell button. + */ + if (key != 0 && key == Options.KeySell) { + Map.Sell_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the map zoom mode similarly to pressing the map button. + */ + if (key != 0 && key == Options.KeyMap) { + Map.Zoom_Mode_Control(); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar up one slot. + */ + if (key != 0 && key == Options.KeySidebarUp) { + Map.SidebarClass::Scroll(true, -1); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar down one slot. + */ + if (key != 0 && key == Options.KeySidebarDown) { + Map.SidebarClass::Scroll(false, -1); + input = KN_NONE; + } + + /* + ** Brings up the options dialog box. + */ + if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + } + + /* + ** Scrolls the tactical map in the direction specified. + */ + int distance = CELL_LEPTON_W; + if (key != 0 && key == Options.KeyScrollLeft) { + Map.Scroll_Map(DIR_W, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollRight) { + Map.Scroll_Map(DIR_E, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollUp) { + Map.Scroll_Map(DIR_N, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollDown) { + Map.Scroll_Map(DIR_S, distance, true); + input = KN_NONE; + } + + /* + ** Teams are handled by the 10 special team keys. The manual comparison + ** to the KN numbers is because the Windows keyboard driver can vary + ** the base code number for the key depending on the shift or alt key + ** state! + */ + if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) { + Handle_Team(0, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) { + Handle_Team(1, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) { + Handle_Team(2, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) { + Handle_Team(3, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) { + Handle_Team(4, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) { + Handle_Team(5, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) { + Handle_Team(6, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) { + Handle_Team(7, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) { + Handle_Team(8, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) { + Handle_Team(9, action); + input = KN_NONE; + } + + /* + ** Handle the bookmark hotkeys. + */ + if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) { + Handle_View(0, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) { + Handle_View(1, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) { + Handle_View(2, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) { + Handle_View(3, action); + input = KN_NONE; + } + +#ifdef CHEAT_KEYS + if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +void Toggle_Formation(void) { + int team = -1; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + if (team == -1) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +} + + +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=============================================================================================*/ +#pragma off (unreferenced) +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+32]; + int id; + SerialPacketType * serial_packet; + int i; + KeyNumType copy_input; + //char *msg; + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ +#ifdef WOLAPI_INTEGRATION + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + ( ( input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) ) || input == PAGE_RESPOND_KEY ) && + !Session.Messages.Is_Edit()) { +#else + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) { +#endif + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) { + + strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:" + + Session.Messages.Add_Edit (Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) { + /* + ** For a network game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + +#ifdef WOLAPI_INTEGRATION + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan && input != PAGE_RESPOND_KEY ) { +#else + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) { +#endif + id = Ipx.Connection_ID(input - KN_F1); + Session.MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } +#ifdef WOLAPI_INTEGRATION + else if( Session.Type == GAME_INTERNET && pWolapi && !pWolapi->bConnectionDown && input == PAGE_RESPOND_KEY ) + { + if( *pWolapi->szExternalPager ) + { + // Respond to a page from external ww online user that paged me. + // Set MessageAddress to all zeroes, as a flag to ourselves later on. + NetNumType blip; + NetNodeType blop; + memset( blip, 0, 4 ); + memset( blop, 0, 6 ); + Session.MessageAddress = IPXAddressClass( blip, blop ); + + // Tell pWolapi not to reset szExternalPager for the time being. + pWolapi->bFreezeExternalPager = true; + + sprintf( txt, Text_String( TXT_TO ), pWolapi->szExternalPager ); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + Keyboard->Clear(); + } + else + { + Session.Messages.Add_Message( NULL, 0, TXT_WOL_NOTPAGED, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( VOC_SYS_ERROR ); + } + } +#endif + } +#if(TEN) + /* + ** For a TEN game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_TEN && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.TenMessageAddress = -1; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < Ten->Num_Connections() && !Session.ObiWan) { + + id = Ten->Connection_ID(input - KN_F1); + Session.TenMessageAddress = Ten->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),Ten->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // TEN +#if(MPATH) + /* + ** For a MPATH game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_MPATH && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MPathMessageAddress = 0; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < MPath->Num_Connections() && !Session.ObiWan) { + + id = MPath->Connection_ID(input - KN_F1); + Session.MPathMessageAddress = MPath->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),MPath->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // MPATH + } + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + copy_input = input; + rc = Session.Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1 && Session.Type != GAME_NORMAL) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. If the edit message was removed, + ** the map must be force-drawn, since it won't be able to compute the + ** cells to redraw; otherwise, let the map compute the cells to redraw, + ** by not force-drawing it, but just setting the IsToRedraw bit. + */ + if (rc==2 && Session.Type != GAME_NORMAL) { + if (copy_input==KN_ESC) { + Map.Flag_To_Redraw(true); +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + // Just in case user was responding to a page from outside the game, and we had frozen the "szExternalPager". + pWolapi->bFreezeExternalPager = false; +#endif + } else { + Map.Flag_To_Redraw(false); + } + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** Send a message + */ + if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) { + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, Session.Players[0]->Name); + serial_packet->ID = Session.ColorIdx; + + if (rc==3) { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** Send the message, and store this message in our LastMessage + ** buffer; the computer may send us a version of it later. + */ + NullModem.Send_Message(NullModem.BuildBuf, + sizeof(SerialPacketType), 1); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + strcpy(Session.LastMessage, serial_packet->Message.Message); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { +#ifdef WOLAPI_INTEGRATION + NetNumType blip; + NetNodeType blop; + Session.MessageAddress.Get_Address( blip, blop ); + if( blip[0] + blip[1] + blip[2] + blip[3] + blop[0] + blop[1] + blop[2] + blop[3] + blop[4] + blop[5] == 0 ) + { + // This message is a response to the last person that paged me. + if( pWolapi && !pWolapi->bConnectionDown ) // (As connection may have gone down.) + { + pWolapi->Page( pWolapi->szExternalPager, Session.Messages.Get_Edit_Buf(), false ); + pWolapi->bFreezeExternalPager = false; + } + } + else +#endif + { + + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (Session.MessageAddress.Is_Broadcast()) { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &Session.GPacket.Message.Buf[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + *ptr = 'X'; // force it to an odd hack so we know it was broadcast. + Enable_Secret_Units(); + } + #endif + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + &Session.MessageAddress); + Ipx.Service(); + + } + + /* + ** Store this message in our LastMessage buffer; the computer may send + ** us a version of it later. + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + +#if(TEN) + /* + ** TEN game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_TEN) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + Ten->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.TenMessageAddress); + } +#endif // TEN + +#if(MPATH) + /* + ** MPATH game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_MPATH) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + MPath->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.MPathMessageAddress); + } +#endif // MPATH + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); + } +} +#pragma on (unreferenced) + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + * 07/16/1996 JLB : Faster pulsing of white color. * + *=============================================================================================*/ +void Color_Cycle(void) +{ + static CDTimerClass _timer; + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + bool changed = false; + + if (Options.IsPaletteScroll) { + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE]; + InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor); + + /* + ** Pulse the glowing embers between medium and dark red. + */ + InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80); + InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor); + + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer) { + _timer = TIMER_SECOND/4; + + RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1]; + for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) { + InGamePalette[index] = InGamePalette[index-1]; + } + InGamePalette[CYCLE_COLOR_START] = first; + + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + BStart(BENCH_PALETTE); + InGamePalette.Set(); +// Set_Palette(InGamePalette); + BEnd(BENCH_PALETTE); + } + } +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ + /* + ** Music and speech maintenance + */ + if (SampleType) { + Sound_Callback(); + Theme.AI(); + Speak_AI(); + } + + /* + ** Network maintenance. + */ + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + IPX_Call_Back(); + } + + /* + ** Serial game maintenance. + */ + if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) { + NullModem.Service(); + } + +#ifdef WOLAPI_INTEGRATION + // Wolapi maintenance. + if( pWolapi ) + { + if( pWolapi->bInGame ) + { + if( !pWolapi->bConnectionDown && ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT + 700; // Slower pump during games. + if( pWolapi->bConnectionDown ) + { + // Connection to server lost. + Session.Messages.Add_Message( NULL, 0, TXT_WOL_WOLAPIGONE, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( WOLSOUND_LOGOUT ); +// ajw (Wolapi object is now left around, so we can try to send game results.) +// // Kill wolapi. +// pWolapi->UnsetupCOMStuff(); +// delete pWolapi; +// pWolapi = NULL; + } + } + } + else + { + // When showing a modal dialog during chat, this pumping is turned on. It's turned off immediately following. + if( pWolapi->bPump_In_Call_Back && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + } + } +#endif + +#if(TEN) + if (Session.Type == GAME_TEN) { + TEN_Call_Back(); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + MPATH_Call_Back(); + } +#endif // MPATH +} + + +void IPX_Call_Back(void) +{ + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!Session.NetOpen) { + if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) { + + if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ipx.Num_Connections(); i++) { + + int id = Ipx.Connection_ID(i); + + if (Session.GAddress == (*Ipx.Connection_Address(id))) { + Destroy_Connection(id, 0); + } + } + } else { + + /* + ** Process a message from another user. + */ + if (Session.GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!Session.NetProtect) { + msg_ok = true; + } else { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (NewUnitsEnabled && !strncmp(Session.GPacket.Message.Buf,"XECRET UNITS ON ",15)) { + Session.GPacket.Message.Buf[0]='S'; + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } else { + Process_Global_Packet(&Session.GPacket, &Session.GAddress); + } + } + } + } + } + +} + + +#if(TEN) +void TEN_Call_Back(void) +{ + int id; + + Ten->Service(); + + if (Ten->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.TenAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ten->Num_Connections(); i++) { + + id = Ten->Connection_ID(i); + + if (Session.TenAddress == Ten->Connection_Address(id)) { + Destroy_TEN_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // TEN + + +#if(MPATH) +void MPATH_Call_Back(void) +{ + int id; + + MPath->Service(); + + if (MPath->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.MPathAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < MPath->Num_Connections(); i++) { + + id = MPath->Connection_ID(i); + + if (Session.MPathAddress == MPath->Connection_Address(id)) { + Destroy_MPATH_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // MPATH + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const * Language_Name(char const * basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const * name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const * name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + + default: + break; + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will delay an amount of time according to the game speed setting. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Accumulate the number of 'spare' ticks that are frittered away here. + */ + SpareTicks += FrameTimer; + + /* + ** Delay until the frame timer expires. This forces the game loop to be regulated to a + ** speed controlled by the game options slider. + */ + while (FrameTimer) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + KeyNumType input = KN_NONE; + int x, y; + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + Color_Cycle(); + Call_Back(); +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); +#endif //WIN32 + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + +Mono_Set_Cursor(0,0); + + if (!GameActive) return(!GameActive); + +#ifdef WIN32 + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); +#endif + + /* + ** Sync-bug trapping code + */ + if (Frame >= Session.TrapFrame) { + Session.Trap_Object(); + } + + // + // Initialize our AI processing timer + // + Session.ProcessTimer = TickCount; + +#if 1 + if (Session.TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + BStart(BENCH_GAME_FRAME); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // In playback mode, run as fast as possible. + // + if (Session.Play) { + FrameTimer = 0; + } else { +#ifdef FIXIT_VERSION_3 + if( !Session.DesiredFrameRate ) + Session.DesiredFrameRate = 60; // A division by zero was happening (very rare). +#endif + framedelay = TIMER_SECOND / Session.DesiredFrameRate; + FrameTimer = framedelay; + } + } else { + if (Options.GameSpeed != 0) { + FrameTimer = Options.GameSpeed + + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) - + (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0); + } else { + FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0); + } + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!Session.Play) { +#ifdef WIN32 + if (SpecialDialog == SDLG_NONE && GameInFocus) { + WWMouse->Erase_Mouse(&HidPage, TRUE); +#else + if (SpecialDialog == SDLG_NONE) { +#endif + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (Session.Record || Session.Play) { + Do_Record_Playback(); + } + +#ifndef SORTDRAW + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will remain in sync. + */ + DisplayClass::Layer[LAYER_GROUND].Sort(); +#endif + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + TimeQuake = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!PendingTimeQuake) { + TimeQuakeCenter = 0; + } +#endif + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + Session.ProcessTicks += (TickCount - Session.ProcessTimer); + Session.ProcessFrames++; + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + Call_Back(); + + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + +#ifdef WIN32 + + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just won. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + return(!GameActive); + } + if (PlayerLoses) { +#ifdef WIN32 + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just lost. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + return(!GameActive); + } + if (PlayerRestarts) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + return(!GameActive); + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 && + Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + // End game in a draw. + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Help_Text(TXT_NONE); + Do_Draw(); + return(!GameActive); + } +#endif + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { + if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) { + GameActive = 0; + } + Map.Validate(); // give debugger a chance to catch it + } + } + + + +#ifdef WIN32 + if (Debug_MotionCapture) { + static void ** _array = 0; + static int _sequence = 0; + static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE; + + if (_array == NULL) { + _array = new void * [_seqsize]; + memset(_array, '\0', _seqsize * sizeof(void*)); + } + + if (_array == NULL) { + Debug_MotionCapture = false; + } + + static GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + + int size = SeenBuff.Get_Width() * SeenBuff.Get_Height(); + + if (_sequence < _seqsize) { + if (_array[_sequence] == NULL) { + _array[_sequence] = new char[size]; + } + + if (_array[_sequence] != NULL) { + SeenBuff.Blit(temp_page); + memmove(_array[_sequence], temp_page.Get_Buffer(), size); + } + _sequence++; + + } else { + Debug_MotionCapture = false; + + CDFileClass file; + file.Cache(200000); + char filename[30]; + + for (int index = 0; index < _sequence; index++) { + memmove(temp_page.Get_Buffer(), _array[index], size); + sprintf(filename, "cap%04d.pcx", index); + file.Set_Name(filename); + + Write_PCX_File(file, temp_page, & GamePalette); + } + + _sequence = 0; + } + } +#endif + + BEnd(BENCH_GAME_FRAME); + + Sync_Delay(); + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ +#ifdef WIN32 + HiddenPage.Clear(); +#else + HidPage.Clear(); +#endif + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + + +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes) +{ + CCFileClass * file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + + default: + error = 0; + break; + } + + return(error); +} + + +void Rebuild_Interpolated_Palette(unsigned char * interpal) +{ + for (int y=0; y<255; y++) { + for (int x=y+1; x<256; x++) { + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char * InterpolatedPalettes[100]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +/*********************************************************************************************** + * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs * + * * + * * + * * + * INPUT: Name of palette file * + * * + * OUTPUT: Number of palettes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/7/96 9:49AM ST : Created * + *=============================================================================================*/ +int Load_Interpolated_Palettes(char const * filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + + if (!add) { + for (i=0; i < ARRAY_SIZE(InterpolatedPalettes); i++) { + InterpolatedPalettes[i]=NULL; + } + start_palette=0; + } else { + for (start_palette = 0; start_palette < ARRAY_SIZE(InterpolatedPalettes); start_palette++) { + if (!InterpolatedPalettes[start_palette]) break; + } + } + + /* + ** Hack another interpolated palette if the requested one is + ** not present. + */ + if (!file.Is_Available()) { + file.Set_Name("AAGUN.VQP"); + } + + if (file.Is_Available()) { + + file.Open(READ); + file.Read(&num_palettes , 4); + + for (i=0; i < num_palettes; i++) { + InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); + memset (InterpolatedPalettes[i+start_palette], 0, 65536); + for (int y = 0; y < 256; y++) { + file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); + } + + Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); + } + + PalettesRead = TRUE; + file.Close(); + } + PaletteCounter = 0; + return (num_palettes); +} + + +void Free_Interpolated_Palettes(void) +{ + for (int i = 0; i < ARRAY_SIZE(InterpolatedPalettes) ;i++) { + if (InterpolatedPalettes[i]) { + free(InterpolatedPalettes[i]); + InterpolatedPalettes[i]=NULL; + } + } +} + + +/*********************************************************************************************** + * Play_Movie -- Plays a VQ movie. * + * * + * Use this routine to play a VQ movie. It will dispatch the specified movie to the * + * VQ player. The routine will not return until the movie has finished playing. * + * * + * INPUT: name -- The name of the movie file (sans ".VQA"). * + * * + * theme -- The identifier for an optional theme that should be played in the * + * background while this VQ plays. * + * * + * clrscrn -- 'true' if to clear the screen when the movie is over * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Suspend_Audio_Thread(void); +extern void Resume_Audio_Thread(void); + +#ifdef MOVIE640 +extern GraphicBufferClass VQ640; +#endif +#endif +void Play_Movie(char const * name, ThemeType theme, bool clrscrn) + { + #ifdef MPEGMOVIE + //theme = theme; + //clrscrn = clrscrn; + if( Using_DVD() ) + { + if (PlayMpegMovie(name)) + return; + } + #endif + + #ifdef CHEAT_KEYS + // Mono_Printf("Movie: %s\n", name); + #endif //CHEAT_KEYS + /* + ** Don't play movies in editor mode + */ + if (Debug_Map) { + return; + } + #ifdef CHEAT_KEYS + // Mono_Printf("A\n"); + #endif //CHEAT_KEYS + /* + ** Don't play movies in multiplayer mode + */ + if (Session.Type != GAME_NORMAL) { + return; + } + #ifdef CHEAT_KEYS + //Mono_Printf("b\n"); + #endif //CHEAT_KEYS + + if (name) { + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, name, ".VQA"); + #ifdef WIN32 + char palname [_MAX_FNAME+_MAX_EXT]; + _makepath(palname , NULL, NULL, name, ".VQP"); + #endif //WIN32 + #ifdef CHEAT_KEYS + // Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); + #endif + + if (!CCFileClass(fullname).Is_Available()){ + #ifdef CHEAT_KEYS + // Mono_Printf("fullname: %s\n", fullname); + #endif //CHEAT_KEYS + return; + } + /* + ** Reset the anim control structure. + */ + Anim_Init(); + + /* + ** Prepare to play a movie. First hide the mouse and stop any score that is playing. + ** While the score (if any) is fading to silence, fade the palette to black as well. + ** When the palette has finished fading, wait until the score has finished fading + ** before launching the movie. + */ + Hide_Mouse(); + Theme.Queue_Song(theme); + if (PreserveVQAScreen == 0 && !clrscrn) { + BlackPalette.Set(FADE_PALETTE_MEDIUM); + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + PreserveVQAScreen = 0; + Keyboard->Clear(); + + VQAHandle * vqa = NULL; + + + #ifdef WIN32 + #ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 640; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } else { + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + } + #endif + #endif + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + if ((vqa = VQA_Alloc()) != NULL) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, fullname, &AnimControl) == 0) { + Brokeout = false; + #ifdef WIN32 + //Suspend_Audio_Thread(); + #ifdef MOVIE640 + if(!IsVQ640) { + Load_Interpolated_Palettes(palname); + } + #else + Load_Interpolated_Palettes(palname); + #endif + //Set_Palette(BlackPalette); + SysMemPage.Clear(); + InMovie = true; + #endif //WIN32 + VQA_Play(vqa, VQAMODE_RUN); + VQA_Close(vqa); + #ifdef WIN32 + //Resume_Audio_Thread(); + InMovie = FALSE; + #ifdef MOVIE640 + if(!IsVQ640) { + Free_Interpolated_Palettes(); + } + #else + Free_Interpolated_Palettes(); + #endif + IsVQ640 = false; + Set_Primary_Buffer_Format(); + #endif //WIN32 + + /* + ** Any movie that ends prematurely must have the screen + ** cleared to avoid any unexpected palette glitches. + */ + if (Brokeout) { + clrscrn = true; + VisiblePage.Clear(); + Brokeout = false; + } + } else { + #ifndef NDEBUG + bool error = true; + assert(error); + #endif + } + + VQA_Free(vqa); + } else { + assert(vqa != NULL); + } + #ifdef CHEAT_KEYS + //Mono_Printf("d"); + #endif //CHEAT_KEYS + /* + ** Presume that the screen is left in a garbage state as well as the palette + ** being in an unknown condition. Recover from this by clearing the screen and + ** forcing the palette to black. + */ + if (clrscrn) { + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + Show_Mouse(); + } +} + + +void Play_Movie(VQType name, ThemeType theme, bool clrscrn) +{ + if (name != VQ_NONE) { + if (name == VQ_REDINTRO) { + IsVQ640 = true; + } + Play_Movie(VQName[name], theme, clrscrn); + IsVQ640 = false; + } +} + + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +extern LPDIRECTDRAWPALETTE PalettePtr; + +bool PlayMpegMovie(const char* name) + { + char path[MAX_PATH]; + CCFileClass file; + const char* filename; + +#ifdef CHEAT_KEYS + if( bNoMovies ) + return true; +#endif + + sprintf(path, "movies\\%.8s.%.3s", name, "mpg"); + filename = file.Set_Name(path); + + if (!file.Is_Available()) + { + #if(1) + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + sprintf(path, "Couldn't find %s\n", filename); + WWMessageBox().Process(path); + #endif + return false; + } + + // Stop theme music + if (Misc_Focus_Loss_Function) + Misc_Focus_Loss_Function(); + + // Release primary surface + VisiblePage.Un_Init(); + + #ifdef MCIMPEG + if (MciMovie && MpgSettings && (MpgSettings->GetDeviceName() != NULL)) + { + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_NORMAL); + + if (!MciMovie->Open(filename, MpgSettings->GetDeviceName())) + { + WWMessageBox().Process("Couldn't open movie.\n"); + } + else if (!MciMovie->Play(MainWindow)) + { + WWMessageBox().Process("Couldn't play movie.\n"); + } + + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + else + #endif + { + DDSURFACEDESC ddsd; + IDirectDrawSurface* primary = NULL; + bool modeChange = false; + RECT rect; + + if (FAILED(DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 16))) + { + WWMessageBox().Process("Couldn't change display mode.\n"); + } + else + { + // Create primary surface reference + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if (FAILED(DirectDrawObject->CreateSurface(&ddsd, &primary, NULL))) + { + WWMessageBox().Process("Couldn't create primary movie surface.\n"); + } + else + { + rect.top = rect.left = 0; + rect.bottom = ScreenHeight; + rect.right = ScreenWidth; + + MpgSetCallback(MpegCallback, NULL); + MpgPlay(filename, DirectDrawObject, primary, &rect); + + if (primary) + primary->Release(); + + } + + DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 8); + } + } + + // Restore surfaces + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + PaletteSurface->SetPalette(PalettePtr); + AllSurfaces.Set_Surface_Focus(true); + AllSurfaces.Restore_Surfaces(); + return true; + } + + +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user) + { + static IDirectDrawPalette* _palette = NULL; + + user = user; + + switch (cmd) + { + case MPGCMD_ERROR: + WWMessageBox().Process((char const *)data); + break; + + case MPGCMD_INIT: + VisiblePage.Clear(); + break; + + case MPGCMD_CLEANUP: + VisiblePage.Clear(); + + if (_palette != NULL) + { + PaletteSurface->SetPalette(_palette); + _palette->Release(); + _palette = NULL; + } + break; + + case MPGCMD_PALETTE: + if (FAILED(PaletteSurface->GetPalette(&_palette))) + { + WWMessageBox().Process("Couldn't get primary palette.\n"); + } + else + { + if (FAILED(PaletteSurface->SetPalette((IDirectDrawPalette*)data))) + { + WWMessageBox().Process("Couldn't set movie palette.\n"); + } + } + break; + + case MPGCMD_UPDATE: + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) + { + if (Keyboard->Get() == KN_ESC) + { + Keyboard->Clear(); + return MPGRES_QUIT; + } + + Keyboard->Clear(); + } + + if (!GameInFocus) + { + MpgPause(); + + while (!GameInFocus) + { + Check_For_Focus_Loss(); + } + + MpgResume(); + return MPGRES_LOSTFOCUS; + } + break; + + default: + break; + } + + return MPGRES_CONTINUE; + } +#endif + +/*********************************************************************************************** + * Unselect_All -- Causes all selected objects to become unselected. * + * * + * This routine will unselect all objects that are currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void Unselect_All(void) +{ + while (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char * retval = NULL; + char * buffer = NULL; + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + +#if (0) + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL); +#endif + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we don't add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ +#ifdef WIN32 + void * ptr; + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); +#else //WIN#@ + if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) { +#endif //WIN32 + + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { +#ifdef WIN32 + + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); + +#else //WIN32 + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + int getx = (iconx * 24) + (x << 3) + 4; + int gety = (icony * 24) + (y << 3) + 4; + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); +#endif //WIN32 + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). * + * * + * scale -- 24.8 fixed point scale factor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long scale) +{ + int predoffset; +#ifdef WIN32 + unsigned long shape_pointer; +#endif //WIN32 + + + /* + ** Special kludge for E3 to prevent crashes + */ + if ((flags & SHAPE_GHOST) && (!ghostdata)) { + ghostdata = DisplayClass::SpecialGhost; + } + if ((flags & SHAPE_FADING) && (!fadingdata)) { + fadingdata = DisplayClass::FadingShade; + } + + static unsigned char * _xbuffer = 0; + + if (!_xbuffer) { + _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; + } + + if (shapefile != NULL && shapenum != -1) { + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + +#ifdef NEVER + /* + ** Perform a quick clip check against the destination rectangle. + */ + if (flags & SHAPE_CENTER) { + if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return; + if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width/2 < 0) return; + if (y+height/2 < 0) return; + + } else { + if (x >= WindowList[window][WINDOWWIDTH]) return; + if (y >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width < 0) return; + if (y+height < 0) return; + } +#endif + + +#ifdef WIN32 + /* + ** In WIn95, build shape returns a pointer to the shape not its size + */ + shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer); + if (shape_pointer) { + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer); + +#else //WIN32 + if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) { + GraphicViewPortClass draw_window(LogicPage, + WindowList[window][WINDOWX], + WindowList[window][WINDOWY], + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *)_ShapeBuffer; +#endif //WIN32 + + UseOldShapeDraw = false; + /* + ** Rotation and scale handler. + */ + if (rotation != DIR_N || scale != 0x0100) { + + /* + ** Get the raw shape data without the new header and flag to use the old shape drawing + */ + UseOldShapeDraw = true; +#ifdef WIN32 + buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer); +#endif + + if (Debug_Rotate) { + + GraphicBufferClass src(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass dst(width, height, _xbuffer); + Rotate_Bitmap(&src, &dst, rotation); + buffer = _xbuffer; + + } else { + + BitmapClass bm(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass gb(width, height, _xbuffer); + TPoint2D pt(width/2, height/2); + + gb.Scale_Rotate(bm, pt, scale, (256-(rotation-64))); + buffer = _xbuffer; + } + } + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = DisplayClass::SpecialGhost; + } + + predoffset = Frame; + + if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + predoffset = -predoffset; + } + + if (draw_window.Lock()) { + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + draw_window.Unlock(); + } + } + } +} + + +/*********************************************************************************************** + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * * + * This routine will calculate (using brute forced) the minimum rectangle that will * + * enclose the pixels of the shape. This rectangle will be relative to the upper left * + * corner of the maximum shape size. By using this minimum rectangle, it is possible to * + * greatly optimize the map 'dirty rectangle' logic. * + * * + * INPUT: shapedata -- Pointer to the shape data block. * + * * + * shapenum -- The shape number to examine. Each shape would have a different * + * dimension rectangle. * + * * + * OUTPUT: Returns with the rectangle that encloses the shape. * + * * + * WARNINGS: This routine uses brute force and is slow. It is presumed that the results * + * will be cached for subsiquent reuse. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Shape_Dimensions(void const * shapedata, int shapenum) +{ + Rect rect; + + if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) { + return(rect); + } + + char * shape; +#ifdef WIN32 + void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer); + if (sh == NULL) return(rect); +// shape = (char *)sh; + shape = (char *)Get_Shape_Header_Data(sh); +#else + Build_Frame(shapedata, shapenum, _ShapeBuffer); + shape = (char *)_ShapeBuffer; +#endif + + int width = Get_Build_Frame_Width(shapedata); + int height = Get_Build_Frame_Height(shapedata); + + rect.X = 0; + rect.Y = 0; + int xlimit = width-1; + int ylimit = height-1; + + /* + ** Find top edge of the shape. + */ + for (int y = 0; y <= ylimit; y++) { + for (int x = 0; x <= xlimit; x++) { + if (shape[y*width + x] != 0) { + rect.Y = y; + rect.X = x; + y = ylimit+1; + break; + } + } + } + + /* + ** Find bottom edge of the shape. + */ + for (y = ylimit; y >= rect.Y; y--) { + for (int x = xlimit; x >= 0; x--) { + if (shape[y*width + x] != 0) { + rect.Height = (y-rect.Y)+1; + xlimit = x; + y = rect.Y-1; + break; + } + } + } + + /* + ** Find left edge of the shape. + */ + for (int x = 0; x < rect.X; x++) { + for (y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.X = x; + x = rect.X; + break; + } + } + } + + /* + ** Find the right edge of the shape. + */ + for (x = width-1; x >= xlimit; x--) { + for (y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.Width = (x-rect.X)+1; + x = xlimit-1; + break; + } + } + } + + /* + ** Normalize the rectangle around the center of the shape. + */ + rect.X -= width / 2; + rect.Y -= height / 2; + + /* + ** Return with the minimum rectangle that encloses the shape. + */ + return(rect); +} + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference(UnitType(id))); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselTypeClass::As_Reference(VesselType(id))); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference(StructType(id))); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference(InfantryType(id))); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference(AircraftType(id))); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +void VQA_PauseAudio(void); +void Check_VQ_Palette_Set(void); + +extern GraphicBufferClass VQ640; +extern bool IsVQ640; +long VQ_Call_Back(unsigned char *, long ) +{ + int key = 0; + if (Keyboard->Check()) { + key = Keyboard->Get(); + Keyboard->Clear(); + } + Check_VQ_Palette_Set(); +#ifdef MOVIE640 + if(IsVQ640) { + VQ640.Blit(SeenBuff); + } else { + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); + } +#else + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); +#endif + //Call_Back(); + + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus) { + VQA_PauseAudio(); + while (!GameInFocus) { + Check_For_Focus_Loss(); + } + } + return(false); +} + +#else //WIN32 + +long VQ_Call_Back(unsigned char *, long ) +{ + Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + Keyboard->Clear(); + } + return(false); +} +#endif //WIN32 + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + // + // Recording support + // + if (Session.Record) { + TeamNumber = (char)team; + TeamEvent = (char)action + 1; + } + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif //WIn32 + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: { + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = -1; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + } + } + } + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl && + (obj->Group == team) && (obj->IsSelected) ) { + + /* + ** When a team is first created, they're created without a + ** formation offset, so they will not be created in + ** formation. Later, if they're assigned a formation, the + ** XFormOffset & YFormOffset numbers will change to valid + ** offsets, and they'll be formationed. + */ +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) obj->Group = team; + if (obj->Group == team && obj->IsSelected) { +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + } + break; + } + + default: + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < ARRAY_SIZE(Scen.Views)) { + if (action == 0) { + + Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view] - (MAP_CELL_W * 4 * RESFACTOR) - (5*RESFACTOR)))); + +#ifdef WIN32 + /* + ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot. + */ + Map.Flag_To_Redraw (true); +#endif //WIN32 + } else { + Scen.Views[view] = Coord_Cell(Map.TacticalCoord) + (MAP_CELL_W*4*RESFACTOR) + (5*RESFACTOR); + } + } +} + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +static char * _CD_Volume_Label[] = { + "CD1", + "CD2", + "CD3", + "CD4", + // Denzil 4/15/98 + #ifdef DVD + "CD1", // ajw - Pushes RADVD to position 5, to match enum in Force_CD_Available(). 4 will never be returned here. + "RADVD", + #endif +}; +static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label); + + +#ifdef WIN32 +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert or CS * + * 3 = Aftermath + * 5 = DVD + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + * 01/20/97 V.Grippi added CS support * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) + { + char volume_name[128]; + char buffer[128]; + unsigned filename_length; + unsigned misc_dword; + int count = 0; + + CountDownTimerClass timer; + + timer.Set(timeout); + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + for (;;) + { + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + if (GetVolumeInformation ((char const *)buffer, &volume_name[0] , + (unsigned long)sizeof(volume_name), (unsigned long *)NULL , + (unsigned long *)&filename_length, (unsigned long *)&misc_dword, + (char *)NULL, (unsigned long)0)) + { + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], volume_name)) + return(i); + } + } + else + { + if (!count) + count++; + else + return -1; + } + } + else + { + /* + ** Failed to get the volume label on a known CD drive. + ** If this is a CD changer it may require time to swap the disks so dont return + ** immediately if the error is ROR_NOT_READY + */ + if (!timer.Time()) + return -1; + + int val = GetLastError(); + + if (val != ROR_NOT_READY) + return -1; + } + } + } +#else +int Get_CD_Index(int cd_drive, int) + { + char buffer[128]; + + /* + ** We need to do this twice because of the possibilities of a directory + ** being cached. If this is so, it will only be discovered when we + ** actually attempt to read a file from the drive. + */ + if(cd_drive) for (int count = 0; count < 2; count ++) + { + struct find_t ft; + int file; + int open_failed; + + /* + ** Create a path for the cd drive and attempt to read the volume label from + ** it. + */ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + /* + ** If we are able to read the volume label, this is good but not enough. + ** Further verification must be done. + */ + if (!_dos_findfirst(buffer, _A_VOLID, &ft)) + { + /* + ** Since some versions of disk cacheing software will cache the CD's + ** directory tracks, we may think the CD is in the drive when it is + ** actually not. To resolve this we must attempt to open a file on + ** the cd. Opening a file will always update the directory tracks + ** (suposedly). + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file); + + if (!open_failed) + { + _dos_close(file); + + /* + ** Hey some times the stupid dos driver appends a period to the + ** name if it is eight characters long. If the last char is a + ** period then erase it. + */ + if (ft.name[strlen(ft.name)-1] == '.') + { + ft.name[strlen(ft.name)-1] = 0; + } + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], ft.name)) + return (i); + } + } + } + } + + return -1; + } +#endif + + +/*********************************************************************************************** + * Force_CD_Available -- Ensures that specified CD is available. * + * * + * Call this routine when you need to ensure that the specified CD is actually in the * + * CD-ROM drive. * + * * + * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * + * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * + * 0 = CD1 + * 1 = CD2 + * 2 = Counterstrike + * 3 = Aftermath + * 4 = Counterstrike or Aftermath + * 5 = DVD + * -1 = Any CD + * -2 = Local Harddisk + * * + * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * + * the player pressed . * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + * 01/20/1997 V.Grippi added expansion cd message + *=============================================================================================*/ + +typedef enum { + CD_LOCAL = -2, + CD_ANY = -1, + CD_SOVIET = 0, + CD_ALLIED, + CD_COUNTERSTRIKE, + CD_AFTERMATH, + CD_CS_OR_AM, + CD_DVD +} CD_VOLUME; + +#ifdef FIXIT_VERSION_3 + +#ifndef DVD +#error DVD must be defined! +#endif + +bool Force_CD_Available( int cd_desired ) // ajw +{ + static int _last = -1; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + "ALERTE ROUGE DVD", +}; +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1", + "ALARMSTUFE ROT CD2", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + "ALARMSTUFE ROT DVD", + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + "RED ALERT DVD", + }; +#endif + + int new_cd_drive = 0; + int cd_current; + int current_drive; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd_desired == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_current = Get_CD_Index(current_drive, 1*60); + +// debugprint("Get_CD_Index just returned %d\n", cd_current); +// debugprint("We are checking for %d\n", cd_desired); +// debugprint("current_drive = %d\n", current_drive); + + if( Using_DVD() ) + { + // All requested cd indexes get rerouted to the DVD. + cd_desired = CD_DVD; +// if( RequiredCD != -1 ) +// RequiredCD = CD_DVD; // Just seems like a good idea. Not sure if necessary. ajw + } + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the current CD is requested or any CD will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) // Else we have already checked this cd. + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_current = Get_CD_Index(last_drive, 10*60); + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the cd is present or any cd will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + int drive_search_timeout = 2*60; + + for (;;) + { + char buffer[128]; + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + int cd_drive = CDList.Get_Next_CD_Drive(); + cd_current = Get_CD_Index(cd_drive, drive_search_timeout); + + if (cd_current >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + // Require CS or AM + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + if( cd_desired == CD_CS_OR_AM ) + cd_desired = CD_AFTERMATH; + + if( cd_desired == CD_DVD ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + } + else if( cd_desired == CD_COUNTERSTRIKE || cd_desired == CD_AFTERMATH ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd_desired]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd_desired]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd_desired]); + #endif + #endif + } + else if( cd_desired == CD_ANY ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd_desired+1, _cd_name[cd_desired]); + } + else // 0 or 1 + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd_desired+1, _cd_name[cd_desired]); + } + + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); +#ifdef FIXIT_VERSION_3 + while (hidden--) Hide_Mouse(); +#else + Hide_Mouse(); +#endif + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_current; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + if (cd_desired == 4) cd_desired--; + + // ajw - Added condition of cd_desired != 5 to the following if. + // Reason: This was triggering before Init_Secondary_Mixfiles(), which was screwing up the mixfile system somehow. + // + // Since the DVD is the only disk that can possibly be required when Using_DVD(), I never have to reload the mix + // files here, because no other disk could ever have been asked for. And if not Using_DVD(), cd_desired will never + // be equal to 5. So this is safe. + if (cd_desired > -1 && _last != cd_desired && cd_desired != 5) + { + _last = cd_desired; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + else + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + return(true); +} + +#else // FIXIT_VERSION_3 not defined + +bool Force_CD_Available(int cd) +{ + static int _last = -1; +// static char _palette[768]; +// static char _hold[256]; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + + // Denzil 4/15/98 + #ifdef DVD + "ALERTE ROUGE DVD", + #endif +}; + +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1 einlegen", + "ALARMSTUFE ROT CD2 einlegen", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + + // Denzil 4/15/98 + #ifdef DVD + "ALARMSTUFE ROT DVD einlegen", + #endif + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + + // Denzil 4/15/98 + #ifdef DVD + "RED ALERT DVD", + #endif + }; +#endif + + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + + ThemeType theme_playing = THEME_NONE; + +//#ifdef FIXIT_ANTS +// if(Scen.ScenarioName[2] == 'A') +// cd = 2; +//#endif + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + + #ifdef CHEAT_KEYS + // Mono_Printf("Get_CD_Index just returned %d\n", cd_index); + // Mono_Printf("We are checking for %d\n", cd); + // Mono_Printf("current_drive = %d\n", current_drive); + #endif //CHEAT_KEYS + + #ifdef DVD // Denzil + // CD1 and CD2 are ignored, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == CD_CS_OR_AM) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If the current drive is the DVD then requests for CD1 and CD2 are okay + if (cd_index == 4) + { + // CD1, CD2 & DVD requests + if (cd == 0 || cd == 1 || cd == 5) + { + cd_index = cd; + } + } + #endif + + // If the current CD is requested or any CD will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2 disks, force DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If CS or AM was the last drive then use it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If DVD is in drive + if (cd_index == 4) + { + // CD1, CD2 and DVD requests are all on the DVD + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + // If the cd is present or any cd will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) + { + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + cd_drive = CDList.Get_Next_CD_Drive(); + cd_index = Get_CD_Index(cd_drive, drive_search_timeout); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If the disk is CS or AM then request it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + if (cd_index == 4) + { + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + if (cd == cd_index || cd == -1 || cd == -2 ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(cd == 4) cd--; + + // CS or AM + if(cd == 2 || cd == 3) + { + #else + if(cd == 2) + { + #endif + + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd]); + #endif + #endif + } + else + { + #ifdef DVD + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + #else + if (cd == -1 ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]); + } + else + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]); + } + #endif + } + + #ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + #else + GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff); + #endif + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); + Hide_Mouse(); + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_index; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cd == 4) cd--; + #endif +// if (cd > -3 && _last != cd) { + if (cd > -1 && _last != cd) + { + _last = cd; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + + if (CCFileClass("MOVIES1.MIX").Is_Available()) + { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + } + else + { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + } + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + if (theme_playing != THEME_NONE) + { + Theme.Queue_Song(theme_playing); + } + + return(true); + } + + +#endif // FIXIT_VERSION_3 + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass * obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /* + ** Record a game + */ + if (Session.Record) { + + /* + ** Save the map's location + */ + Session.RecordFile.Write(&Map.DesiredTacticalCoord, + sizeof (Map.DesiredTacticalCoord)); + + /* + ** Save the current object list count + */ + count = CurrentObject.Count(); + Session.RecordFile.Write(&count, sizeof(count)); + + /* + ** Save a CRC of the selected-object list. + */ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + Session.RecordFile.Write (&sum, sizeof(sum)); + + /* + ** Save all selected objects. + */ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + Session.RecordFile.Write (&tgt, sizeof(tgt)); + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent)); + Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed)); + Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed)); + Session.RecordFile.Write (&FormMove, sizeof(FormMove)); + Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed)); + Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed)); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } + + /* + ** Play back a game ("attract" mode) + */ + if (Session.Play) { + + /* + ** Read & set the map's location. + */ + if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /* + ** Compute a CRC of the current object-selection list. + */ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /* + ** Load the CRC of the objects on disk; if it doesn't match, select + ** all objects as they're loaded. + */ + Session.RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) { + Unselect_All(); + } + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent)); + if (TeamEvent) { + Handle_Team(TeamNumber, TeamEvent - 1); + } + if (FormationEvent) { + Toggle_Formation(); + } + +Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed)); +Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed)); +Session.RecordFile.Read (&FormMove, sizeof(FormMove)); +Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed)); +Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed)); + /* + ** The map isn't drawn in playback mode, so draw it here. + */ + Map.Render(); + } +} + + +/*********************************************************************************************** + * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. * + * * + * * + * * + * INPUT: Name of file to load * + * * + * OUTPUT: Ptr to loaded file * + * * + * WARNINGS: Caller is responsible for releasing the memory allocated * + * * + * * + * HISTORY: * + * 5/13/96 3:20PM ST : Created * + *=============================================================================================*/ +void * Hires_Load(char * name) +{ + char filename[30]; + int length; + void * return_ptr; + +#ifdef WIN32 + sprintf(filename, "H%s", name); +#else + strcpy(filename, name); +#endif + CCFileClass file (filename); + + if (file.Is_Available()) { + + length = file.Size(); + return_ptr = new char[length]; + file.Read(return_ptr, length); + return (return_ptr); + + } else { + return (NULL); + } +} + + +/*********************************************************************************************** + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * * + * Use this routine to convert an ASCII crate name into a crate type. If no match could * + * be found, then CRATE_MONEY is assumed. * + * * + * INPUT: name -- Pointer to the crate name text to convert into a crate type. * + * * + * OUTPUT: Returns with the crate name converted into a crate type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +CrateType Crate_From_Name(char const * name) +{ + if (name != NULL) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + if (stricmp(name, CrateNames[crate]) == 0) return(crate); + } + } + return(CRATE_MONEY); +} + + +/*********************************************************************************************** + * Owner_From_Name -- Convert an owner name into a bitfield. * + * * + * This will take an owner specification and convert it into a bitfield that represents * + * it. Sometimes this will be just a single house bit, but other times it could be * + * all the allies or soviet house bits combined. * + * * + * INPUT: text -- Pointer to the text to convert into a house bitfield. * + * * + * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with * + * one bit per house type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int Owner_From_Name(char const * text) +{ + int ownable = 0; + if (stricmp(text, "soviet") == 0) { + ownable |= HOUSEF_SOVIET; + } else { + if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) { + ownable |= HOUSEF_ALLIES; + } else { + HousesType h = HouseTypeClass::From_Name(text); + if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) { + ownable |= (1 << h); + } + } + } + return(ownable); +} + + +/*********************************************************************************************** + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * * + * This routine will shake the game screen the number of shakes requested. * + * * + * INPUT: shakes -- The number of shakes to shake the screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 BWG : Created. * + *=============================================================================================*/ +void Shake_The_Screen(int shakes) +{ +#ifdef WIN32 + shakes += shakes; + + Hide_Mouse(); + SeenPage.Blit(HidPage); + int oldyoff = 0; + int newyoff = 0; + while(shakes--) { + int x = TickCount; +// CountDownTimer = 1; + do { + newyoff = Sim_Random_Pick(0,2) - 1; + } while (newyoff == oldyoff); + switch (newyoff) { + case -1: + HidPage.Blit(SeenPage, 0,2, 0,0, 640,398); + break; + case 0: + HidPage.Blit(SeenPage); + break; + case 1: + HidPage.Blit(SeenPage, 0,0, 0,2, 640,398); + break; + } while (x == TickCount); +// } while (CountDownTimer != 0) ; + } + HidPage.Blit(SeenPage); + Show_Mouse(); +#else + Shake_Screen(shakes); +#endif +} + + +/*********************************************************************************************** + * List_Copy -- Makes a copy of a cell offset list. * + * * + * This routine will make a copy of a cell offset list. It will only copy the significant * + * elements of the list limited by the maximum length specified. * + * * + * INPUT: source -- Pointer to a cell offset list. * + * * + * len -- The maximum number of cell offset elements to store in to the * + * destination list pointer. * + * * + * dest -- Pointer to the destination list to store the copy into. * + * * + * OUTPUT: none * + * * + * WARNINGS: Ensure that the destination list is large enough to hold the list copy. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void List_Copy(short const * source, int len, short * dest) +{ + if (dest == NULL || dest == NULL) { + return; + } + + while (len > 0) { + *dest = *source; + if (*dest == REFRESH_EOL) break; + dest++; + source++; + len--; + } +} + + + +#if 0 +// +// Boy, this function sure is crummy +// +void Crummy(int crumb1, int crumb2) +{ + if (Debug_Check_Map && Debug_Heap_Dump) { + Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2); + } +} +#endif + + + +/*********************************************************************************************** + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * This is located under HKEY_LOCAL_MACHINE. + * HISTORY: + * 11/19/98 ajw : Created + *=============================================================================================*/ +const char* Game_Registry_Key() +{ +#ifdef ENGLISH + static char szKey[] = "SOFTWARE\\Westwood\\Red Alert Windows 95 Edition"; +#else +#ifdef GERMAN + static char szKey[] = "SOFTWARE\\Westwood\\Alarmstufe Rot Windows 95 Edition"; +#else + static char szKey[] = "SOFTWARE\\Westwood\\Alerte Rouge version Windows 95"; +#endif +#endif + return szKey; +} + + +/*********************************************************************************************** + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Counterstrike is present * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 11:39PM ST : Created * + *=============================================================================================*/ +bool Is_Counterstrike_Installed (void) +{ + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "CStrikeInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + *=============================================================================================*/ +bool Is_Aftermath_Installed (void) +{ + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "AftermathInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +} +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void) +{ +#if 0 + SecretUnitsEnabled=true; + UnitTypeClass::As_Reference(UNIT_PHASE).Level=10; + VesselTypeClass::As_Reference(VESSEL_CARRIER).Level=10; + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } +#endif +} +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ) +{ + // Calls Force_CD_Available based on type of scenario. szName is assumed to be an official scenario here. + if( Is_Mission_Counterstrike( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 4...\n" ); + return Force_CD_Available( 4 ); + } + else if( Is_Mission_Aftermath( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 3...\n" ); + return Force_CD_Available( 3 ); + } + return true; +} +#endif diff --git a/CODE/CONQUER.CPP.BAK b/CODE/CONQUER.CPP.BAK new file mode 100644 index 0000000..891c8b9 --- /dev/null +++ b/CODE/CONQUER.CPP.BAK @@ -0,0 +1,4185 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.83 24 Oct 1996 12:55:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * List_Copy -- Makes a copy of a cell offset list. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Owner_From_Name -- Convert an owner name into a bitfield. * + * Play_Movie -- Plays a VQ movie. * + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef TESTCODE +class A { + public: + enum {VAR=1}; +}; + +template +class B { + public: + enum {VAR2=T::VAR}; // this is the line in question. +}; + +B test; +#endif + + + +#include "function.h" +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +TcpipManagerClass Winsock; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" +#include "vortex.h" + +#define SHAPE_TRANS 0x40 + +void * Get_Shape_Header_Data(void * ptr); +extern bool Spawn_WChat(void); + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(void); +void Keyboard_Process(KeyNumType & input); +static void Message_Input(KeyNumType &input); +static void Color_Cycle(void); +bool Map_Edit_Loop(void); + +extern "C" { + bool UseOldShapeDraw = false; +} + +#ifdef CHEAT_KEYS +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char * string ); +#endif +static void Do_Record_Playback(void); + +void Toggle_Formation(void); + +extern "C" { + extern char * __nheapbeg; +} + +// +// Special module globals for recording and playback +// +char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type +char TeamNumber = 0; // which team was selected? (1-9) +char FormationEvent = 0; // 0 = no event, 1 = formation was toggled + + + /* -----------------10/14/96 7:29PM------------------ + + --------------------------------------------------*/ + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +void Main_Game(int argc, char * argv[]) +{ + static bool fade = true; + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + fade = false; + ScenarioInit = 0; // Kludge. + + fade = true; + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + GamePalette.Set(FADE_PALETTE_MEDIUM); + Keyboard->Clear(); + + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (Session.Play) { + Hide_Mouse(); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } else { + Show_Mouse(); + } + +#ifdef WIN32 + if (Session.Type == GAME_INTERNET) { + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + } else { + DDEServer.Disable(); + } +#endif //WIN32 + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { + TimeQuake = false; + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + PlayerPtr->Flag_To_Lose(); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { + TimeQuake = false; + /* + **call the game's main loop + */ + //VG_MONO + Mono_Print("About to call Main Loop in Main Game/n/n"); + + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } +#endif + + +#ifdef WIN32 + /* + ** Send the game stats to WChat if we havnt already done so + */ + if (!GameStatisticsPacketSent && PacketLater) { + Send_Statistics_Packet(); + } +#endif //WIN32 + + /* + ** Scenario is done; fade palette to black + */ + BlackPalette.Set(FADE_PALETTE_SLOW); + VisiblePage.Clear(); + + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if (Session.Record || Session.Play) { + Session.RecordFile.Close(); + } + + if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) { + if (!Session.Play) { + Modem_Signoff(); + } + } else { + if (Session.Type == GAME_IPX) { + if (!Session.Play) { + Shutdown_Network(); + } + } + } + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (Session.Play) { + Show_Mouse(); + Session.Type = GAME_NORMAL; + Session.Play = 0; + } +#ifdef WIN32 + if (Special.IsFromWChat) { + Shutdown_Network(); // Clear up the pseudo IPX stuff + Winsock.Close(); + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(); //Will switch back to Wchat. It must be there because its been poking us + } +#endif //WIN32 + } + + /* + ** Free the scenario description buffers + */ + Session.Free_Scenario_Descriptions(); +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +void Keyboard_Process(KeyNumType & input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); + +#ifdef WIN32 + /* + ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to + ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed. + */ + + KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT)); + KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT); + + +#else + KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT)); + KeyNumType key = plain; +#endif + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + HousesType h; + + switch (int(input)) { + case int(int(KN_M) | int(KN_SHIFT_BIT)): + case int(int(KN_M) | int(KN_ALT_BIT)): + case int(int(KN_M) | int(KN_CTRL_BIT)): + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + Houses.Ptr(h)->Refund_Money(10000); + } + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +#ifdef CHEAT_KEYS +#ifdef WIN32 + if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) { +#else + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { +#endif + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) { + if (Session.Type == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + } + + if (Debug_Flag && input == KN_SLASH) { + if (Session.Type != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +#endif + + /* + ** Process prerecorded team selection. This will be an additive select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; +#ifdef WIN32 + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; +#else + if (input & KN_SHIFT_BIT) action = 1; + if (input & KN_ALT_BIT) action = 3; + if (input & KN_CTRL_BIT) action = 2; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + if (key != 0 && key == Options.KeyNext) { + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + if (key != 0 && key == Options.KeyPrevious) { + if (action) { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + + + /* + ** All selected units will go into idle mode. + */ + if (key != 0 && key == Options.KeyStop) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to go into guard area mode. + */ + if (key != 0 && key == Options.KeyGuard) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to scatter. + */ + if (key != 0 && key == Options.KeyScatter) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) { + if (CurrentObject.Count()) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif + input = KN_NONE; + } else { + input = Options.KeyBase; + } + } + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + if (key != 0 && key == Options.KeyBase) { + Unselect_All(); + if (PlayerPtr->CurBuildings) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + if (building->IsLeader) break; + } + } + } + if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) { + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + } + if (CurrentObject.Count()) { + Map.Center_Map(); + } else { + if (PlayerPtr->Center != 0) { + Map.Center_Map(PlayerPtr->Center); + } + } + Map.Flag_To_Redraw(true); + input = KN_NONE; + } + + /* + ** Toggle the status of formation for the current team + */ + if (key != 0 && key == Options.KeyFormation) { + Toggle_Formation(); + input = KN_NONE; + } + +#ifdef TOFIX + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + if (input != 0 && input == Options.KeyResign) { + if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + input = KN_NONE; + } +#endif + + /* + ** Handle making and breaking alliances. + */ + if (key != 0 && key == Options.KeyAlliance) { + if (Session.Type != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); + } + } + } + input = KN_NONE; + } + + /* + ** Select all the units on the current display. This is equivalent to + ** drag selecting the whole view. + */ + if (key != 0 && key == Options.KeySelectView) { + Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight)); + input = KN_NONE; + } + + /* + ** Toggles the repair state similarly to pressing the repair button. + */ + if (key != 0 && key == Options.KeyRepair) { + Map.Repair_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the sell state similarly to pressing the sell button. + */ + if (key != 0 && key == Options.KeySell) { + Map.Sell_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the map zoom mode similarly to pressing the map button. + */ + if (key != 0 && key == Options.KeyMap) { + Map.Zoom_Mode_Control(); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar up one slot. + */ + if (key != 0 && key == Options.KeySidebarUp) { + Map.SidebarClass::Scroll(true, -1); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar down one slot. + */ + if (key != 0 && key == Options.KeySidebarDown) { + Map.SidebarClass::Scroll(false, -1); + input = KN_NONE; + } + + /* + ** Brings up the options dialog box. + */ + if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + } + + /* + ** Scrolls the tactical map in the direction specified. + */ + int distance = CELL_LEPTON_W; + if (key != 0 && key == Options.KeyScrollLeft) { + Map.Scroll_Map(DIR_W, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollRight) { + Map.Scroll_Map(DIR_E, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollUp) { + Map.Scroll_Map(DIR_N, distance, true); + input = KN_NONE; + } + if (key != 0 && key == Options.KeyScrollDown) { + Map.Scroll_Map(DIR_S, distance, true); + input = KN_NONE; + } + + /* + ** Teams are handled by the 10 special team keys. The manual comparison + ** to the KN numbers is because the Windows keyboard driver can vary + ** the base code number for the key depending on the shift or alt key + ** state! + */ + if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) { + Handle_Team(0, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) { + Handle_Team(1, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) { + Handle_Team(2, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) { + Handle_Team(3, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) { + Handle_Team(4, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) { + Handle_Team(5, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) { + Handle_Team(6, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) { + Handle_Team(7, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) { + Handle_Team(8, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) { + Handle_Team(9, action); + input = KN_NONE; + } + + /* + ** Handle the bookmark hotkeys. + */ + if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) { + Handle_View(0, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) { + Handle_View(1, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) { + Handle_View(2, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) { + Handle_View(3, action); + input = KN_NONE; + } + +#ifdef CHEAT_KEYS + if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +void Toggle_Formation(void) { + int team = -1; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + if (team == -1) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +} + + +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=============================================================================================*/ +#pragma off (unreferenced) +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+32]; + int id; + SerialPacketType * serial_packet; + int i; + KeyNumType copy_input; + //char *msg; + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) { + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) { + + strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:" + + Session.Messages.Add_Edit (Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) { + /* + ** For a network game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) { + + id = Ipx.Connection_ID(input - KN_F1); + Session.MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } + } + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + copy_input = input; + rc = Session.Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1 && Session.Type != GAME_NORMAL) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. If the edit message was removed, + ** the map must be force-drawn, since it won't be able to compute the + ** cells to redraw; otherwise, let the map compute the cells to redraw, + ** by not force-drawing it, but just setting the IsToRedraw bit. + */ + if (rc==2 && Session.Type != GAME_NORMAL) { + if (copy_input==KN_ESC) { + Map.Flag_To_Redraw(true); + } else { + Map.Flag_To_Redraw(false); + } + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** Send a message + */ + if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) { + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, Session.Players[0]->Name); + serial_packet->ID = Session.ColorIdx; + + if (rc==3) { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** Send the message, and store this message in our LastMessage + ** buffer; the computer may send us a version of it later. + */ + NullModem.Send_Message(NullModem.BuildBuf, + sizeof(SerialPacketType), 1); + + strcpy(Session.LastMessage, serial_packet->Message.Message); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (Session.MessageAddress.Is_Broadcast()) { + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + &Session.MessageAddress); + Ipx.Service(); + + } + + /* + ** Store this message in our LastMessage buffer; the computer may send + ** us a version of it later. + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); + } +} +#pragma on (unreferenced) + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + * 07/16/1996 JLB : Faster pulsing of white color. * + *=============================================================================================*/ +void Color_Cycle(void) +{ + static CDTimerClass _timer; + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + bool changed = false; + + if (Options.IsPaletteScroll) { + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE]; + InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor); + + /* + ** Pulse the glowing embers between medium and dark red. + */ + InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80); + InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor); + + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer) { + _timer = TIMER_SECOND/4; + + RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1]; + for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) { + InGamePalette[index] = InGamePalette[index-1]; + } + InGamePalette[CYCLE_COLOR_START] = first; + + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + BStart(BENCH_PALETTE); + InGamePalette.Set(); +// Set_Palette(InGamePalette); + BEnd(BENCH_PALETTE); + } + } +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ + /* + ** Music and speech maintenance + */ + if (SampleType) { + Sound_Callback(); + Theme.AI(); + Speak_AI(); + } + + /* + ** Network maintenance. + */ + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + IPX_Call_Back(); + } + + /* + ** Serial game maintenance. + */ + if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) { + NullModem.Service(); + } +} + + +void IPX_Call_Back(void) +{ + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!Session.NetOpen) { + if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) { + + if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ipx.Num_Connections(); i++) { + + int id = Ipx.Connection_ID(i); + + if (Session.GAddress == (*Ipx.Connection_Address(id))) { + + Destroy_Connection(id, 0); + } + } + } else { + + /* + ** Process a message from another user. + */ + if (Session.GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!Session.NetProtect) { + msg_ok = true; + } else { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } else { + Process_Global_Packet(&Session.GPacket, &Session.GAddress); + } + } + } + } + } + +} + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const * Language_Name(char const * basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const * name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const * name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + + default: + break; + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will delay an amount of time according to the game speed setting. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Accumulate the number of 'spare' ticks that are frittered away here. + */ + SpareTicks += FrameTimer; + + /* + ** Delay until the frame timer expires. This forces the game loop to be regulated to a + ** speed controlled by the game options slider. + */ + while (FrameTimer) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + KeyNumType input = KN_NONE; + int x, y; + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + Color_Cycle(); + Call_Back(); +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); +#endif //WIN32 + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + + if (!GameActive) return(!GameActive); + +#ifdef WIN32 + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); +#endif + + /* + ** Sync-bug trapping code + */ + if (Frame >= Session.TrapFrame) { + Session.Trap_Object(); + } + + // + // Initialize our AI processing timer + // + Session.ProcessTimer = TickCount; + +#if 1 + if (Session.TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + BStart(BENCH_GAME_FRAME); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // In playback mode, run as fast as possible. + // + if (Session.Play) { + FrameTimer = 0; + } else { + framedelay = TIMER_SECOND / Session.DesiredFrameRate; + FrameTimer = framedelay; + } + } else { + if (Options.GameSpeed != 0) { + FrameTimer = Options.GameSpeed + + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) - + (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0); + } else { + FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0); + } + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!Session.Play) { +#ifdef WIN32 + if (SpecialDialog == SDLG_NONE && GameInFocus) { + WWMouse->Erase_Mouse(&HidPage, TRUE); +#else + if (SpecialDialog == SDLG_NONE) { +#endif + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (Session.Record || Session.Play) { + Do_Record_Playback(); + } + +#ifndef SORTDRAW + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will remain in sync. + */ + DisplayClass::Layer[LAYER_GROUND].Sort(); +#endif + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + TimeQuake = false; + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + Session.ProcessTicks += (TickCount - Session.ProcessTimer); + Session.ProcessFrames++; + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + Call_Back(); + + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + +#ifdef WIN32 + + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + return(!GameActive); + } + if (PlayerLoses) { +#ifdef WIN32 + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + return(!GameActive); + } + if (PlayerRestarts) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + return(!GameActive); + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { + if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) { + GameActive = 0; + } + Map.Validate(); // give debugger a chance to catch it + } + } + + +#ifdef WIN32 + if (Debug_MotionCapture) { + static void ** _array = 0; + static int _sequence = 0; + static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE; + + if (_array == NULL) { + _array = new void * [_seqsize]; + memset(_array, '\0', _seqsize * sizeof(void*)); + } + + if (_array == NULL) { + Debug_MotionCapture = false; + } + + static GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + + int size = SeenBuff.Get_Width() * SeenBuff.Get_Height(); + + if (_sequence < _seqsize) { + if (_array[_sequence] == NULL) { + _array[_sequence] = new char[size]; + } + + if (_array[_sequence] != NULL) { + SeenBuff.Blit(temp_page); + memmove(_array[_sequence], temp_page.Get_Buffer(), size); + } + _sequence++; + + } else { + Debug_MotionCapture = false; + + CDFileClass file; + file.Cache(200000); + char filename[30]; + + for (int index = 0; index < _sequence; index++) { + memmove(temp_page.Get_Buffer(), _array[index], size); + sprintf(filename, "cap%04d.pcx", index); + file.Set_Name(filename); + + Write_PCX_File(file, temp_page, & GamePalette); + } + + _sequence = 0; + } + } +#endif + + BEnd(BENCH_GAME_FRAME); + + Sync_Delay(); + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ +#ifdef WIN32 + HiddenPage.Clear(); +#else + HidPage.Clear(); +#endif + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + + +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes) +{ + CCFileClass * file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + + default: + error = 0; + break; + } + + return(error); +} + + +void Rebuild_Interpolated_Palette(unsigned char * interpal) +{ + for (int y=0; y<255; y++) { + for (int x=y+1; x<256; x++) { + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char * InterpolatedPalettes[50]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +/*********************************************************************************************** + * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs * + * * + * * + * * + * INPUT: Name of palette file * + * * + * OUTPUT: Number of palettes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/7/96 9:49AM ST : Created * + *=============================================================================================*/ +int Load_Interpolated_Palettes(char const * filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + + if (!add) { + for (i=0; i < 50; i++) { + InterpolatedPalettes[i]=NULL; + } + start_palette=0; + } else { + for (start_palette = 0; start_palette < 50; start_palette++) { + if (!InterpolatedPalettes[start_palette]) break; + } + } + + /* + ** Hack another interpolated palette if the requested one is + ** not present. + */ + if (!file.Is_Available()) { + file.Set_Name("AAGUN.VQP"); + } + + if (file.Is_Available()) { + + file.Open(READ); + file.Read(&num_palettes , 4); + + for (i=0; i < num_palettes; i++) { + InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); + memset (InterpolatedPalettes[i+start_palette], 0, 65536); + for (int y = 0; y < 256; y++) { + file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); + } + + Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); + } + + PalettesRead = TRUE; + file.Close(); + } + PaletteCounter = 0; + return (num_palettes); +} + + +void Free_Interpolated_Palettes(void) +{ + for (int i = 0; i < 50 ;i++) { + if (InterpolatedPalettes[i]) { + free(InterpolatedPalettes[i]); + InterpolatedPalettes[i]=NULL; + } + } +} + + +/*********************************************************************************************** + * Play_Movie -- Plays a VQ movie. * + * * + * Use this routine to play a VQ movie. It will dispatch the specified movie to the * + * VQ player. The routine will not return until the movie has finished playing. * + * * + * INPUT: name -- The name of the movie file (sans ".VQA"). * + * * + * theme -- The identifier for an optional theme that should be played in the * + * background while this VQ plays. * + * * + * clrscrn -- 'true' if to clear the screen when the movie is over * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Suspend_Audio_Thread(void); +extern void Resume_Audio_Thread(void); + +#ifdef MOVIE640 +extern GraphicBufferClass VQ640; +#endif +#endif +void Play_Movie(char const * name, ThemeType theme, bool clrscrn) +{ + /* + ** Don't play movies in editor mode + */ + if (Debug_Map) { + return; + } + + /* + ** Don't play movies in multiplayer mode + */ + if (Session.Type != GAME_NORMAL) { + return; + } + + if (name) { + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, name, ".VQA"); +#ifdef WIN32 + char palname [_MAX_FNAME+_MAX_EXT]; + _makepath(palname , NULL, NULL, name, ".VQP"); +#endif //WIN32 + #ifdef CHEAT_KEYS + Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); + #endif + + if (!CCFileClass(fullname).Is_Available()) return; + + /* + ** Reset the anim control structure. + */ + Anim_Init(); + + /* + ** Prepare to play a movie. First hide the mouse and stop any score that is playing. + ** While the score (if any) is fading to silence, fade the palette to black as well. + ** When the palette has finished fading, wait until the score has finished fading + ** before launching the movie. + */ + Hide_Mouse(); + Theme.Queue_Song(theme); + if (PreserveVQAScreen == 0 && !clrscrn) { + BlackPalette.Set(FADE_PALETTE_MEDIUM); + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + PreserveVQAScreen = 0; + Keyboard->Clear(); + + VQAHandle * vqa = NULL; + + +#ifdef WIN32 +#ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 640; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } else { + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + } +#endif +#endif + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + if ((vqa = VQA_Alloc()) != NULL) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, fullname, &AnimControl) == 0) { + Brokeout = false; +#ifdef WIN32 + //Suspend_Audio_Thread(); +#ifdef MOVIE640 + if(!IsVQ640) { + Load_Interpolated_Palettes(palname); + } +#else + Load_Interpolated_Palettes(palname); +#endif + //Set_Palette(BlackPalette); + SysMemPage.Clear(); + InMovie = true; +#endif //WIN32 + VQA_Play(vqa, VQAMODE_RUN); + VQA_Close(vqa); +#ifdef WIN32 + //Resume_Audio_Thread(); + InMovie = FALSE; +#ifdef MOVIE640 + if(!IsVQ640) { + Free_Interpolated_Palettes(); + } +#else + Free_Interpolated_Palettes(); +#endif + IsVQ640 = false; + Set_Primary_Buffer_Format(); +#endif //WIN32 + + /* + ** Any movie that ends prematurely must have the screen + ** cleared to avoid any unexpected palette glitches. + */ + if (Brokeout) { + clrscrn = true; + VisiblePage.Clear(); + Brokeout = false; + } + } else { + #ifndef NDEBUG + bool error = true; + assert(error); + #endif + } + + VQA_Free(vqa); + } else { + assert(vqa != NULL); + } + + /* + ** Presume that the screen is left in a garbage state as well as the palette + ** being in an unknown condition. Recover from this by clearing the screen and + ** forcing the palette to black. + */ + if (clrscrn) { + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + Show_Mouse(); + } +} + + +void Play_Movie(VQType name, ThemeType theme, bool clrscrn) +{ + if (name != VQ_NONE) { + if (name == VQ_REDINTRO) { + IsVQ640 = true; + } + Play_Movie(VQName[name], theme, clrscrn); + IsVQ640 = false; + } +} + + +/*********************************************************************************************** + * Unselect_All -- Causes all selected objects to become unselected. * + * * + * This routine will unselect all objects that are currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void Unselect_All(void) +{ + while (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char * retval = NULL; + char * buffer = NULL; + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + +#if (0) + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL); +#endif + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we don't add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ +#ifdef WIN32 + void * ptr; + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); +#else //WIN#@ + if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) { +#endif //WIN32 + + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { +#ifdef WIN32 + + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); + +#else //WIN32 + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + int getx = (iconx * 24) + (x << 3) + 4; + int gety = (icony * 24) + (y << 3) + 4; + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); +#endif //WIN32 + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). * + * * + * scale -- 24.8 fixed point scale factor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long scale) +{ + int predoffset; +#ifdef WIN32 + unsigned long shape_pointer; +#endif //WIN32 + + + /* + ** Special kludge for E3 to prevent crashes + */ + if ((flags & SHAPE_GHOST) && (!ghostdata)) { + ghostdata = DisplayClass::SpecialGhost; + } + if ((flags & SHAPE_FADING) && (!fadingdata)) { + fadingdata = DisplayClass::FadingShade; + } + + static unsigned char * _xbuffer = 0; + + if (!_xbuffer) { + _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; + } + + if (shapefile != NULL && shapenum != -1) { + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + +#ifdef NEVER + /* + ** Perform a quick clip check against the destination rectangle. + */ + if (flags & SHAPE_CENTER) { + if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return; + if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width/2 < 0) return; + if (y+height/2 < 0) return; + + } else { + if (x >= WindowList[window][WINDOWWIDTH]) return; + if (y >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width < 0) return; + if (y+height < 0) return; + } +#endif + + +#ifdef WIN32 + /* + ** In WIn95, build shape returns a pointer to the shape not its size + */ + shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer); + if (shape_pointer) { + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer); + +#else //WIN32 + if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) { + GraphicViewPortClass draw_window(LogicPage, + WindowList[window][WINDOWX], + WindowList[window][WINDOWY], + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *)_ShapeBuffer; +#endif //WIN32 + + UseOldShapeDraw = false; + /* + ** Rotation and scale handler. + */ + if (rotation != DIR_N || scale != 0x0100) { + + /* + ** Get the raw shape data without the new header and flag to use the old shape drawing + */ + UseOldShapeDraw = true; +#ifdef WIN32 + buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer); +#endif + + if (Debug_Rotate) { + + GraphicBufferClass src(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass dst(width, height, _xbuffer); + Rotate_Bitmap(&src, &dst, rotation); + buffer = _xbuffer; + + } else { + + BitmapClass bm(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass gb(width, height, _xbuffer); + TPoint2D pt(width/2, height/2); + + gb.Scale_Rotate(bm, pt, scale, (256-(rotation-64))); + buffer = _xbuffer; + } + } + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = DisplayClass::SpecialGhost; + } + + predoffset = Frame; + + if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + predoffset = -predoffset; + } + + if (draw_window.Lock()) { + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(x, y, width, height, buffer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + draw_window.Unlock(); + } + } + } +} + + +/*********************************************************************************************** + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * * + * This routine will calculate (using brute forced) the minimum rectangle that will * + * enclose the pixels of the shape. This rectangle will be relative to the upper left * + * corner of the maximum shape size. By using this minimum rectangle, it is possible to * + * greatly optimize the map 'dirty rectangle' logic. * + * * + * INPUT: shapedata -- Pointer to the shape data block. * + * * + * shapenum -- The shape number to examine. Each shape would have a different * + * dimension rectangle. * + * * + * OUTPUT: Returns with the rectangle that encloses the shape. * + * * + * WARNINGS: This routine uses brute force and is slow. It is presumed that the results * + * will be cached for subsiquent reuse. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Shape_Dimensions(void const * shapedata, int shapenum) +{ + Rect rect; + + if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) { + return(rect); + } + + char * shape; +#ifdef WIN32 + void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer); + if (sh == NULL) return(rect); +// shape = (char *)sh; + shape = (char *)Get_Shape_Header_Data(sh); +#else + Build_Frame(shapedata, shapenum, _ShapeBuffer); + shape = (char *)_ShapeBuffer; +#endif + + int width = Get_Build_Frame_Width(shapedata); + int height = Get_Build_Frame_Height(shapedata); + + rect.X = 0; + rect.Y = 0; + int xlimit = width-1; + int ylimit = height-1; + + /* + ** Find top edge of the shape. + */ + for (int y = 0; y <= ylimit; y++) { + for (int x = 0; x <= xlimit; x++) { + if (shape[y*width + x] != 0) { + rect.Y = y; + rect.X = x; + y = ylimit+1; + break; + } + } + } + + /* + ** Find bottom edge of the shape. + */ + for (y = ylimit; y >= rect.Y; y--) { + for (int x = xlimit; x >= 0; x--) { + if (shape[y*width + x] != 0) { + rect.Height = (y-rect.Y)+1; + xlimit = x; + y = rect.Y-1; + break; + } + } + } + + /* + ** Find left edge of the shape. + */ + for (int x = 0; x < rect.X; x++) { + for (y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.X = x; + x = rect.X; + break; + } + } + } + + /* + ** Find the right edge of the shape. + */ + for (x = width-1; x >= xlimit; x--) { + for (y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.Width = (x-rect.X)+1; + x = xlimit-1; + break; + } + } + } + + /* + ** Normalize the rectangle around the center of the shape. + */ + rect.X -= width / 2; + rect.Y -= height / 2; + + /* + ** Return with the minimum rectangle that encloses the shape. + */ + return(rect); +} + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference(UnitType(id))); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselTypeClass::As_Reference(VesselType(id))); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference(StructType(id))); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference(InfantryType(id))); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference(AircraftType(id))); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +void VQA_PauseAudio(void); +void Check_VQ_Palette_Set(void); + +extern GraphicBufferClass VQ640; +extern bool IsVQ640; +long VQ_Call_Back(unsigned char *, long ) +{ + int key = 0; + if (Keyboard->Check()) { + key = Keyboard->Get(); + Keyboard->Clear(); + } + Check_VQ_Palette_Set(); +#ifdef MOVIE640 + if(IsVQ640) { + VQ640.Blit(SeenBuff); + } else { + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); + } +#else + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); +#endif + //Call_Back(); + + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus) { + VQA_PauseAudio(); + while (!GameInFocus) { + Check_For_Focus_Loss(); + } + } + return(false); +} + +#else //WIN32 + +long VQ_Call_Back(unsigned char *, long ) +{ + Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + Keyboard->Clear(); + } + return(false); +} +#endif //WIN32 + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + // + // Recording support + // + if (Session.Record) { + TeamNumber = (char)team; + TeamEvent = (char)action + 1; + } + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif //WIn32 + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: { + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = -1; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + } + } + } + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl && + (obj->Group == team) && (obj->IsSelected) ) { + + /* + ** When a team is first created, they're created without a + ** formation offset, so they will not be created in + ** formation. Later, if they're assigned a formation, the + ** XFormOffset & YFormOffset numbers will change to valid + ** offsets, and they'll be formationed. + */ +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->IsSelected) obj->Group = team; + if (obj->Group == team && obj->IsSelected) { +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + } + break; + } + + default: + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < sizeof(Scen.Views)/sizeof(Scen.Views[0])) { + if (action == 0) { + Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view]))); +#ifdef WIN32 + /* + ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot. + */ + Map.Flag_To_Redraw (true); +#endif //WIN32 + } else { + Scen.Views[view] = Coord_Cell(Map.TacticalCoord); + } + } +} + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +static char * _CD_Volume_Label[] = { + "CD1", + "CD2", +}; +static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label); + + +#ifdef WIN32 +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert * + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) +{ + char volume_name[128]; + char buffer[128]; + unsigned filename_length; + unsigned misc_dword; + int count = 0; + + CountDownTimerClass timer; + + + + timer.Set(timeout); + + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + for (;;) { + sprintf(buffer, "%c:\\", 'A' + cd_drive); + if (GetVolumeInformation ((char const *)buffer, + &volume_name[0] , + (unsigned long)sizeof(volume_name), + (unsigned long *)NULL , + (unsigned long *)&filename_length, + (unsigned long *)&misc_dword , + (char *)NULL , + (unsigned long)0)) { + + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i=0 ; i<_Num_Volumes ; i++) { + if (!stricmp(_CD_Volume_Label[i], volume_name)) return (i); + } + } else { + if (!count) + count++; + else return -1; + } + } else { + /* + ** Failed to get the volume label on a known CD drive. + ** If this is a CD changer it may require time to swap the disks so dont return + ** immediately if the error is ROR_NOT_READY + */ + if (!timer.Time()) return -1; + int val = GetLastError(); + if (val != ROR_NOT_READY) return -1; + } + } +} +#else +int Get_CD_Index (int cd_drive, int ) +{ + char buffer[128]; + + /* + ** We need to do this twice because of the possibilities of a directory + ** being cached. If this is so, it will only be discovered when we + ** actually attempt to read a file from the drive. + */ + if(cd_drive) for (int count = 0; count < 2; count ++) { + struct find_t ft; + int file; + int open_failed; + + /* + ** Create a path for the cd drive and attempt to read the volume label from + ** it. + */ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + /* + ** If we are able to read the volume label, this is good but not enough. + ** Further verification must be done. + */ + if (!_dos_findfirst(buffer, _A_VOLID, &ft)) { + + /* + ** Since some versions of disk cacheing software will cache the CD's + ** directory tracks, we may think the CD is in the drive when it is + ** actually not. To resolve this we must attempt to open a file on + ** the cd. Opening a file will always update the directory tracks + ** (suposedly). + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file); + if (!open_failed) { + _dos_close(file); + + /* + ** Hey some times the stupid dos driver appends a period to the + ** name if it is eight characters long. If the last char is a + ** period then erase it. + */ + if (ft.name[strlen(ft.name)-1] == '.') { + ft.name[strlen(ft.name)-1] = 0; + } + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i=0 ; i < _Num_Volumes ; i++) { + if (!stricmp(_CD_Volume_Label[i], ft.name)) return (i); + } + } + } + } + return -1; +} +#endif + + +/*********************************************************************************************** + * Force_CD_Available -- Ensures that specified CD is available. * + * * + * Call this routine when you need to ensure that the specified CD is actually in the * + * CD-ROM drive. * + * * + * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * + * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * + * * + * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * + * the player pressed . * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + *=============================================================================================*/ +bool Force_CD_Available(int cd) +{ +#ifndef DEMO + static int _last = -1; +#endif +// static char _palette[768]; +// static char _hold[256]; + static void *font; + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + }; + + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == -2) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + if (cd_index >= 0) { + if (cd == cd_index || cd == -1) { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + + if (!new_cd_drive) { + /* + ** Check the last CD drive we used if its different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isnt the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + if (cd_index >= 0) { + if (cd == cd_index || cd == -1) { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still cant find it prompt the user to insert it. + */ + if (!new_cd_drive) { + + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) { + /* + ** Search all present CD drives for the required disc. + */ + for (int i=0 ; i=0) { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + if (cd == cd_index || cd == -1 || cd == -2) { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + if (cd == -1) { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]); + } else { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]); + } +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff); +#endif + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) { + + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) { + Set_Logic_Page(oldpage); + Hide_Mouse(); + return(false); + } + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_index; + +#ifndef DEMO + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ +// if (cd > -3 && _last != cd) { + if (cd > -1 && _last != cd) { + _last = cd; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + } else { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + } + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } +#endif + + if (theme_playing != THEME_NONE) { + Theme.Queue_Song(theme_playing); + } + + return(true); +} + + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass * obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /* + ** Record a game + */ + if (Session.Record) { + + /* + ** Save the map's location + */ + Session.RecordFile.Write(&Map.DesiredTacticalCoord, + sizeof (Map.DesiredTacticalCoord)); + + /* + ** Save the current object list count + */ + count = CurrentObject.Count(); + Session.RecordFile.Write(&count, sizeof(count)); + + /* + ** Save a CRC of the selected-object list. + */ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + Session.RecordFile.Write (&sum, sizeof(sum)); + + /* + ** Save all selected objects. + */ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + Session.RecordFile.Write (&tgt, sizeof(tgt)); + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent)); +Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed)); +Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed)); +Session.RecordFile.Write (&FormMove, sizeof(FormMove)); +Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed)); +Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed)); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } + + /* + ** Play back a game ("attract" mode) + */ + if (Session.Play) { + + /* + ** Read & set the map's location. + */ + if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /* + ** Compute a CRC of the current object-selection list. + */ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /* + ** Load the CRC of the objects on disk; if it doesn't match, select + ** all objects as they're loaded. + */ + Session.RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) { + Unselect_All(); + } + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent)); + if (TeamEvent) { + Handle_Team(TeamNumber, TeamEvent - 1); + } + if (FormationEvent) { + Toggle_Formation(); + } + +Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed)); +Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed)); +Session.RecordFile.Read (&FormMove, sizeof(FormMove)); +Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed)); +Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed)); + /* + ** The map isn't drawn in playback mode, so draw it here. + */ + Map.Render(); + } +} + + +/*********************************************************************************************** + * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. * + * * + * * + * * + * INPUT: Name of file to load * + * * + * OUTPUT: Ptr to loaded file * + * * + * WARNINGS: Caller is responsible for releasing the memory allocated * + * * + * * + * HISTORY: * + * 5/13/96 3:20PM ST : Created * + *=============================================================================================*/ +void * Hires_Load(char * name) +{ + char filename[30]; + int length; + void * return_ptr; + +#ifdef WIN32 + sprintf(filename, "H%s", name); +#else + strcpy(filename, name); +#endif + CCFileClass file (filename); + + if (file.Is_Available()) { + + length = file.Size(); + return_ptr = new char[length]; + file.Read(return_ptr, length); + return (return_ptr); + + } else { + return (NULL); + } +} + + +/*********************************************************************************************** + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * * + * Use this routine to convert an ASCII crate name into a crate type. If no match could * + * be found, then CRATE_MONEY is assumed. * + * * + * INPUT: name -- Pointer to the crate name text to convert into a crate type. * + * * + * OUTPUT: Returns with the crate name converted into a crate type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +CrateType Crate_From_Name(char const * name) +{ + if (name != NULL) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + if (stricmp(name, CrateNames[crate]) == 0) return(crate); + } + } + return(CRATE_MONEY); +} + + +/*********************************************************************************************** + * Owner_From_Name -- Convert an owner name into a bitfield. * + * * + * This will take an owner specification and convert it into a bitfield that represents * + * it. Sometimes this will be just a single house bit, but other times it could be * + * all the allies or soviet house bits combined. * + * * + * INPUT: text -- Pointer to the text to convert into a house bitfield. * + * * + * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with * + * one bit per house type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int Owner_From_Name(char const * text) +{ + int ownable = 0; + if (stricmp(text, "soviet") == 0) { + ownable |= HOUSEF_SOVIET; + } else { + if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) { + ownable |= HOUSEF_ALLIES; + } else { + HousesType h = HouseTypeClass::From_Name(text); + if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) { + ownable |= (1 << h); + } + } + } + return(ownable); +} + + +/*********************************************************************************************** + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * * + * This routine will shake the game screen the number of shakes requested. * + * * + * INPUT: shakes -- The number of shakes to shake the screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 BWG : Created. * + *=============================================================================================*/ +void Shake_The_Screen(int shakes) +{ +#ifdef WIN32 + shakes += shakes; + + Hide_Mouse(); + SeenPage.Blit(HidPage); + int oldyoff = 0; + int newyoff = 0; + while(shakes--) { + int x = TickCount; +// CountDownTimer = 1; + do { + newyoff = Sim_Random_Pick(0,2) - 1; + } while (newyoff == oldyoff); + switch (newyoff) { + case -1: + HidPage.Blit(SeenPage, 0,2, 0,0, 640,398); + break; + case 0: + HidPage.Blit(SeenPage); + break; + case 1: + HidPage.Blit(SeenPage, 0,0, 0,2, 640,398); + break; + } while (x == TickCount); +// } while (CountDownTimer != 0) ; + } + HidPage.Blit(SeenPage); + Show_Mouse(); +#else + Shake_Screen(shakes); +#endif +} + + +/*********************************************************************************************** + * List_Copy -- Makes a copy of a cell offset list. * + * * + * This routine will make a copy of a cell offset list. It will only copy the significant * + * elements of the list limited by the maximum length specified. * + * * + * INPUT: source -- Pointer to a cell offset list. * + * * + * len -- The maximum number of cell offset elements to store in to the * + * destination list pointer. * + * * + * dest -- Pointer to the destination list to store the copy into. * + * * + * OUTPUT: none * + * * + * WARNINGS: Ensure that the destination list is large enough to hold the list copy. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void List_Copy(short const * source, int len, short * dest) +{ + if (dest == NULL || dest == NULL) { + return; + } + + while (len > 0) { + *dest = *source; + if (*dest == REFRESH_EOL) break; + dest++; + source++; + len--; + } +} + + + +#if 0 +// +// Boy, this function sure is crummy +// +void Crummy(int crumb1, int crumb2) +{ + if (Debug_Check_Map && Debug_Heap_Dump) { + Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2); + } +} +#endif diff --git a/CODE/CONQUER.H b/CODE/CONQUER.H new file mode 100644 index 0000000..ab194da --- /dev/null +++ b/CODE/CONQUER.H @@ -0,0 +1,573 @@ +/* +** 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 . +*/ + +#define TXT_NONE 0 // +#define TXT_CREDIT_FORMAT 1 // %3d.%02d +#define TXT_TIME_FORMAT_HOURS 2 // Temps:%02d:%02d:%02d +#define TXT_TIME_FORMAT_NO_HOURS 3 // Temps:%02d:%02d +#define TXT_BUTTON_SELL 4 // Vente +#define TXT_SELL 5 // Vente structure +#define TXT_BUTTON_REPAIR 6 // R‚paration +#define TXT_YOU 7 // Vous : +#define TXT_ENEMY 8 // Ennemi : +#define TXT_BUILD_DEST 9 // Bƒtiments d‚truits par +#define TXT_UNIT_DEST 10 // Unit‚s d‚truites par +#define TXT_TIB_HARV 11 // Minerai r‚colt‚ par +#define TXT_SCORE_1 12 // Score: %d +#define TXT_YES 13 // Oui +#define TXT_NO 14 // Non +#define TXT_SCENARIO_WON 15 // Mission Accomplie +#define TXT_SCENARIO_LOST 16 // Mission Echou‚e +#define TXT_START_NEW_GAME 17 // Nouvelle partie +#define TXT_INTRO 18 // Intro/Preview +#define TXT_CANCEL 19 // Annuler +#define TXT_ROCK 20 // Rocher +#define TXT_CIVILIAN 21 // Civil +#define TXT_JP 22 // Equipe de confinement +#define TXT_OK 23 // OK +#define TXT_TREE 24 // Arbre +#define TXT_LEFT 25 //  +#define TXT_RIGHT 26 //  +#define TXT_UP 27 //  +#define TXT_DOWN 28 //  +#define TXT_CLEAR 29 // Effacer +#define TXT_WATER 30 // Eau +#define TXT_ROAD 31 // Route +#define TXT_SLOPE 32 // Pente +#define TXT_PATCH 33 // Patch +#define TXT_RIVER 34 // RiviŠre +#define TXT_LOAD_MISSION 35 // Charger Mission +#define TXT_SAVE_MISSION 36 // Sauvegarder Mission +#define TXT_DELETE_MISSION 37 // Effacer Mission +#define TXT_LOAD_BUTTON 38 // Charger +#define TXT_SAVE_BUTTON 39 // Sauvegarder +#define TXT_DELETE_BUTTON 40 // Effacer +#define TXT_GAME_CONTROLS 41 // Contr“les +#define TXT_SOUND_CONTROLS 42 // Son +#define TXT_RESUME_MISSION 43 // Reprendre Mission +#define TXT_VISUAL_CONTROLS 44 // Affichage +#define TXT_QUIT_MISSION 45 // Abandonner Mission +#define TXT_EXIT_GAME 46 // Quitter le jeu +#define TXT_OPTIONS 47 // Options +#define TXT_SQUISH 48 // D‚bris humains +#define TXT_CRATER 49 // CratŠre +#define TXT_SCORCH 50 // Marque de br–lure +#define TXT_BRIGHTNESS 51 // Luminosit‚ : +#define TXT_MUSIC 52 // Musique +#define TXT_VOLUME 53 // Effets sonores +#define TXT_TINT 54 // Teintes : +#define TXT_CONTRAST 55 // Contraste : +#define TXT_SPEED 56 // Vitesse du jeu : +#define TXT_SCROLLRATE 57 // Vitesse d‚filement : +#define TXT_COLOR 58 // Couleur : +#define TXT_RETURN_TO_GAME 59 // Revenir au jeu +#define TXT_ENEMY_SOLDIER 60 // Soldat ennemi +#define TXT_ENEMY_VEHICLE 61 // V‚hicule ennemi +#define TXT_ENEMY_STRUCTURE 62 // Structure ennemie +#define TXT_LTANK 63 // Tank l‚ger +#define TXT_MTANK 64 // Tank lourd +#define TXT_MTANK2 65 // Tank moyen +#define TXT_HTANK 66 // Tank Mammouth +#define TXT_SAM 67 // Missiles SAM +#define TXT_JEEP 68 // Ranger +#define TXT_TRANS 69 // H‚licoptŠre Chinook +#define TXT_HARVESTER 70 // Collecteur minerai +#define TXT_ARTY 71 // Artillerie +#define TXT_E1 72 // Mitrailleurs +#define TXT_E2 73 // Grenadiers +#define TXT_E3 74 // Bazookas +#define TXT_E4 75 // Lance-flammes +#define TXT_HELI 76 // H‚licoptŠre d'assaut +#define TXT_ORCA 77 // Hind +#define TXT_APC 78 // VBT +#define TXT_GUARD_TOWER 79 // Tour de garde +#define TXT_COMMAND 80 // D“me radar +#define TXT_HELIPAD 81 // H‚liport +#define TXT_AIRSTRIP 82 // Piste d'atterrissage +#define TXT_STORAGE 83 // Silo minerai +#define TXT_CONST_YARD 84 // Chantier de construction +#define TXT_REFINERY 85 // Raffinerie de minerai +#define TXT_CIV1 86 // Eglise +#define TXT_CIV2 87 // Chez Hans et Gretel +#define TXT_CIV3 88 // Manoir d'Hewitt +#define TXT_CIV4 89 // Maison de Ricktor +#define TXT_CIV5 90 // Maison de Gretchin +#define TXT_CIV6 91 // La grange +#define TXT_CIV7 92 // Pub Damon +#define TXT_CIV8 93 // Maison de Fran +#define TXT_CIV9 94 // Usine d'instruments +#define TXT_CIV10 95 // Fabricant de jouets +#define TXT_CIV11 96 // Maison de Ludwig +#define TXT_CIV12 97 // Meules de foin +#define TXT_CIV13 98 // Meule de foin +#define TXT_CIV14 99 // Champ de bl‚ +#define TXT_CIV15 100 // Champ en friche +#define TXT_CIV16 101 // Champ de ma‹s +#define TXT_CIV17 102 // Champ de c‚leri +#define TXT_CIV18 103 // Champ de pommes de terre +#define TXT_CIV20 104 // Maison de Sala +#define TXT_CIV21 105 // Maison d'Abdul +#define TXT_CIV22 106 // Le Pub Barjo de Pablo +#define TXT_CIV23 107 // Puits du village +#define TXT_CIV24 108 // Marchand de chameaux +#define TXT_CIV25 109 // Eglise +#define TXT_CIV26 110 // Maison d'Ali +#define TXT_CIV27 111 // Ted le Marchand +#define TXT_CIV28 112 // Maison de Menelik +#define TXT_CIV29 113 // Maison du pasteur John +#define TXT_CIV30 114 // Puits du village +#define TXT_CIV31 115 // Hutte du gu‚risseur +#define TXT_CIV32 116 // Hutte de Rikitikitembo +#define TXT_CIV33 117 // Hutte de Roarke +#define TXT_CIV34 118 // Hutte de Moubasa' +#define TXT_CIV35 119 // Hutte d'Aksoum +#define TXT_CIV36 120 // Hutte de Mambo +#define TXT_CIV37 121 // Le studio +#define TXT_CIVMISS 122 // Centre technologique +#define TXT_TURRET 123 // Tourelle +#define TXT_GUNBOAT 124 // Aviso-torpilleur +#define TXT_MCV 125 // V‚hicule de construction +#define TXT_POWER 126 // Centrale ‚lectrique +#define TXT_ADVANCED_POWER 127 // Centrale ‚lectrique avanc‚e +#define TXT_HOSPITAL 128 // H“pital +#define TXT_BARRACKS 129 // Caserne +#define TXT_PUMP 130 // Pompe +#define TXT_TANKER 131 // P‚trolier +#define TXT_SANDBAG_WALL 132 // Sacs de sable +#define TXT_CYCLONE_WALL 133 // Cl“ture grillag‚e +#define TXT_BRICK_WALL 134 // Mur de b‚ton +#define TXT_BARBWIRE_WALL 135 // Cl“ture barbel‚e +#define TXT_WOOD_WALL 136 // BarriŠre de bois +#define TXT_WEAPON_FACTORY 137 // Usine d'armement +#define TXT_AGUARD_TOWER 138 // Tour de garde avanc‚e +#define TXT_BIO_LAB 139 // Laboratoire biologique +#define TXT_FIX_IT 140 // Centre de service +#define TXT_TAB_SIDEBAR 141 // Contr“les +#define TXT_TAB_BUTTON_CONTROLS 142 // Options +#define TXT_TAB_BUTTON_DATABASE 143 // Base de donn‚es +#define TXT_SHADOW 144 // Terrain inconnu +#define TXT_OPTIONS_MENU 145 // Menu des options +#define TXT_STOP 146 // Stop +#define TXT_PLAY 147 // Lect +#define TXT_SHUFFLE 148 // Al‚at. +#define TXT_REPEAT 149 // R‚p‚ter +#define TXT_MUSIC_VOLUME 150 // Musique : +#define TXT_SOUND_VOLUME 151 // Effets sonores : +#define TXT_ON 152 // Oui +#define TXT_OFF 153 // Non +#define TXT_MULTIPLAYER_GAME 154 // Jeu Multijoueurs +#define TXT_NO_FILES 155 // Pas de fichiers disponibles +#define TXT_DELETE_SINGLE_FILE 156 // Voulez-vous effacer ce +#define TXT_DELETE_MULTIPLE_FILES 157 // Voulez-vous effacer %d +#define TXT_RESET_MENU 158 // D‚faut +#define TXT_CONFIRM_EXIT 159 // Voulez-vous abandonner la +#define TXT_MISSION_DESCRIPTION 160 // Description de la mission +#define TXT_C1 161 // Joe +#define TXT_C2 162 // Barry +#define TXT_C3 163 // Shelly +#define TXT_C4 164 // Maria +#define TXT_C5 165 // Karen +#define TXT_C6 166 // Steve +#define TXT_C7 167 // Phil +#define TXT_C8 168 // Dwight +#define TXT_C9 169 // Erik +#define TXT_EINSTEIN 170 // Prof. Einstein +#define TXT_BIB 171 // Cour +#define TXT_FASTER 172 // Rapide +#define TXT_SLOWER 173 // Lent +#define TXT_AIR_STRIKE 174 // Attaque a‚rienne +#define TXT_STEEL_CRATE 175 // Caisse d'acier +#define TXT_WOOD_CRATE 176 // Caisse de bois +#define TXT_WATER_CRATE 177 // Caisse flottante +#define TXT_FLAG_SPOT 178 // Emplacement du drapeau +#define TXT_UNABLE_READ_SCENARIO 179 // Lecture sc‚nario impossible +#define TXT_ERROR_LOADING_GAME 180 // Erreur de chargement ! +#define TXT_OBSOLETE_SAVEGAME 181 // Sauvegarde obsolŠte +#define TXT_MUSTENTER_DESCRIPTION 182 // Vous devez entrer une +#define TXT_ERROR_SAVING_GAME 183 // Erreur de sauvegarde ! +#define TXT_DELETE_FILE_QUERY 184 // Effacer ce fichier ? +#define TXT_EMPTY_SLOT 185 // [EMPLACEMENT VIDE] +#define TXT_SELECT_MPLAYER_GAME 186 // Choix du jeu Multijoueurs +#define TXT_MODEM_SERIAL 187 // Modem/S‚rie +#define TXT_NETWORK 188 // R‚seau +#define TXT_INIT_NET_ERROR 189 // Initialisation r‚seau +#define TXT_JOIN_NETWORK_GAME 190 // Rejoindre jeu en r‚seau +#define TXT_NEW 191 // Nouveau +#define TXT_JOIN 192 // Joindre +#define TXT_SEND_MESSAGE 193 // Envoi Message +#define TXT_YOUR_NAME 194 // Votre nom : +#define TXT_SIDE_COLON 195 // Camp : +#define TXT_COLOR_COLON 196 // Couleur : +#define TXT_GAMES 197 // Parties +#define TXT_PLAYERS 198 // Joueurs +#define TXT_SCENARIO_COLON 199 // Sc‚nario : +#define TXT_NOT_FOUND 200 // >> NON TROUVE << +#define TXT_START_CREDITS_COLON 201 // Cr‚dits : +#define TXT_BASES_COLON 202 // Bases : +#define TXT_TIBERIUM_COLON 203 // Minerai : +#define TXT_CRATES_COLON 204 // Caisses : +#define TXT_AI_PLAYERS_COLON 205 // Joueurs IA : +#define TXT_REQUEST_DENIED 206 // Demande refus‚e. +#define TXT_UNABLE_PLAY_WAAUGH 207 // Impossible de jouer, +#define TXT_NOTHING_TO_JOIN 208 // Aucune partie disponible ! +#define TXT_NAME_ERROR 209 // Vous devez entrer un nom ! +#define TXT_DUPENAMES_NOTALLOWED 210 // Les noms identiques ne sont +#define TXT_YOURGAME_OUTDATED 211 // La version de votre jeu est +#define TXT_DESTGAME_OUTDATED 212 // Version du jeu de +#define TXT_THATGUYS_GAME 213 // Partie de %s +#define TXT_THATGUYS_GAME_BRACKET 214 // [Partie de %s] +#define TXT_NETGAME_SETUP 215 // Configuration du jeu en +#define TXT_REJECT 216 // Rejeter +#define TXT_CANT_REJECT_SELF 217 // Vous ne pouvez pas vous +#define TXT_SELECT_PLAYER_REJECT 218 // Vous devez s‚lectionner un +#define TXT_BASES 219 // Bases +#define TXT_CRATES 220 // Caisses +#define TXT_AI_PLAYERS 221 // Joueur IA +#define TXT_SCENARIOS 222 // Sc‚narios +#define TXT_CREDITS_COLON 223 // Cr‚dits : +#define TXT_ONLY_ONE 224 // Un seul joueur ? +#define TXT_OOPS 225 // Oops ! +#define TXT_TO 226 // Pour %s : +#define TXT_TO_ALL 227 // Pour tous +#define TXT_MESSAGE 228 // Message : +#define TXT_CONNECTION_LOST 229 // Perte de connexion avec %s +#define TXT_LEFT_GAME 230 // %s a quitt‚ le jeu. +#define TXT_PLAYER_DEFEATED 231 // %s a ‚t‚ vaincu ! +#define TXT_WAITING_CONNECT 232 // Attente de connexion... +#define TXT_NULL_CONNERR_CHECK_CABLES 233 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_REDIALING 234 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_WAITING 235 // Erreur de connexion ! +#define TXT_SELECT_SERIAL_GAME 236 // Choix du jeu en s‚rie +#define TXT_DIAL_MODEM 237 // Appel +#define TXT_ANSWER_MODEM 238 // Attente appel +#define TXT_NULL_MODEM 239 // Null Modem +#define TXT_SETTINGS 240 // ParamŠtres +#define TXT_PORT_COLON 241 // Port : +#define TXT_IRQ_COLON 242 // IRQ : +#define TXT_BAUD_COLON 243 // Bauds : +#define TXT_INIT_STRING 244 // ChaŒne d'initialisation +#define TXT_CWAIT_STRING 245 // ChaŒne d'attente d'appel +#define TXT_TONE_BUTTON 246 // Tonalit‚ +#define TXT_PULSE_BUTTON 247 // Impulsions +#define TXT_HOST_SERIAL_GAME 248 // H“te du jeu en s‚rie +#define TXT_OPPONENT_COLON 249 // Adversaire : +#define TXT_USER_SIGNED_OFF 250 // Utilisateur reparti ! +#define TXT_JOIN_SERIAL_GAME 251 // Rejoindre jeu en s‚rie +#define TXT_PHONE_LIST 252 // R‚pertoire +#define TXT_ADD 253 // Ajouter +#define TXT_EDIT 254 // Editer +#define TXT_DIAL 255 // Appel +#define TXT_DEFAULT 256 // D‚faut +#define TXT_DEFAULT_SETTINGS 257 // D‚faut +#define TXT_CUSTOM_SETTINGS 258 // Autres paramŠtres +#define TXT_PHONE_LISTING 259 // R‚pertoire +#define TXT_NAME_COLON 260 // Nom : +#define TXT_NUMBER_COLON 261 // Num‚ro : +#define TXT_UNABLE_FIND_MODEM 262 // Modem non d‚tect‚. V‚rifiez +#define TXT_NO_CARRIER 263 // Pas de porteuse. +#define TXT_LINE_BUSY 264 // Ligne occup‚e. +#define TXT_NUMBER_INVALID 265 // Num‚ro incorrect. +#define TXT_SYSTEM_NOT_RESPONDING 266 // L'autre systŠme ne r‚pond +#define TXT_OUT_OF_SYNC 267 // Mauvaise synchronisation ! +#define TXT_PACKET_TOO_LATE 268 // Paquet re‡u trop tard ! +#define TXT_PLAYER_LEFT_GAME 269 // L'autre joueur a quitt‚ le +#define TXT_FROM 270 // De %s:%s +#define TXT_SCORE_TIME 271 // TEMPS : +#define TXT_SCORE_LEAD 272 // COMMANDEMENT : +#define TXT_SCORE_EFFI 273 // EFFICACITE : +#define TXT_SCORE_TOTA 274 // SCORE TOTAL : +#define TXT_SCORE_CASU 275 // PERTES : +#define TXT_SCORE_NEUT 276 // NEUTRE : +#define TXT_SCORE_BUIL 277 // BATIMENTS PERDUS +#define TXT_SCORE_BUIL1 278 // BATIMENTS +#define TXT_SCORE_BUIL2 279 // PERDUS : +#define TXT_SCORE_TOP 280 // MEILLEURS SCORES +#define TXT_SCORE_ENDCRED 281 // CREDITS DE FIN : +#define TXT_SCORE_TIMEFORMAT1 282 // %dh %dm +#define TXT_SCORE_TIMEFORMAT2 283 // %dm +#define TXT_DIALING 284 // Appel... +#define TXT_DIALING_CANCELED 285 // Appel annul‚ +#define TXT_WAITING_FOR_CALL 286 // Attente d'appel... +#define TXT_ANSWERING_CANCELED 287 // Attente d'appel annul‚e +#define TXT_E6 288 // Ing‚nieurs +#define TXT_E8 289 // Espion +#define TXT_MODEM_OR_LOOPBACK 290 // Pas de cƒble Null Modem +#define TXT_MAP 291 // Carte +#define TXT_BLOSSOM_TREE 292 // Arbre en fleurs +#define TXT_RESTATE_MISSION 293 // Briefing +#define TXT_COMPUTER 294 // Joueur IA +#define TXT_COUNT 295 // Nombre : +#define TXT_LEVEL 296 // Niveau : +#define TXT_OPPONENT 297 // Adversaire +#define TXT_KILLS_COLON 298 // Vict.: +#define TXT_VIDEO 299 // Vid‚o +#define TXT_C10 300 // Scientifique +#define TXT_CAPTURE_THE_FLAG 301 // Capture drapeau +#define TXT_OBJECTIVE 302 // Objectifs de mission +#define TXT_MISSION 303 // Mission +#define TXT_NO_SAVES 304 // Pas de sauvegardes +#define TXT_CIVILIAN_BUILDING 305 // Bƒtiment civil +#define TXT_TECHNICIAN 306 // Technicien +#define TXT_NO_SAVELOAD 307 // Sauvegarde interdite en +#define TXT_DELPHI 308 // Agent Sp‚cial 1 +#define TXT_TO_REPLAY 309 // Voulez-vous recommencer +#define TXT_RECONN_TO 310 // Reconnexion vers %s. +#define TXT_PLEASE_WAIT 311 // Attendez %02d secondes. +#define TXT_SURRENDER 312 // Voulez-vous vous rendre ? +#define TXT_SEL_TRANS 313 // CHOIX DE LA TRANSMISSION +#define TXT_GAMENAME_MUSTBE_UNIQUE 314 // Les sauvegardes ne peuvent +#define TXT_GAME_IS_CLOSED 315 // Partie ferm‚e. +#define TXT_NAME_MUSTBE_UNIQUE 316 // Les noms doivent ˆtre tous +#define TXT_RECONNECTING_TO 317 // Reconnexion vers %s +#define TXT_WAITING_FOR_CONNECTIONS 318 // Attente de connexions... +#define TXT_TIME_ALLOWED 319 // Temps autoris‚ : %02d +#define TXT_PRESS_ESC 320 // Appuyez sur Echap pour +#define TXT_JUST_YOU_AND_ME 321 // De l'ordinateur : Il ne +#define TXT_CAPTURE_THE_FLAG_COLON 322 // Capture du drapeau : +#define TXT_CHAN 323 // Agent Sp‚cial 2 +#define TXT_HAS_ALLIED 324 // %s s'est alli‚(e) avec %s +#define TXT_AT_WAR 325 // %s d‚clare la guerre … %s +#define TXT_SEL_TARGET 326 // Choisissez un cible +#define TXT_RESIGN 327 // Abandonner +#define TXT_TIBERIUM_FAST 328 // Le minerai pousse trŠs +#define TXT_ANSWERING 329 // R‚ponse en cours... +#define TXT_INITIALIZING_MODEM 330 // Initialisation Modem... +#define TXT_SCENARIOS_DO_NOT_MATCH 331 // Les sc‚narios ne +#define TXT_POWER_OUTPUT 332 // Production d'‚nergie +#define TXT_POWER_OUTPUT_LOW 333 // Production d'‚nergie +#define TXT_CONTINUE 334 // Continuer +#define TXT_QUEUE_FULL 335 // Saturation des donn‚es … +#define TXT_SPECIAL_WARNING 336 // %s a modifi‚ les options de +#define TXT_CD_DIALOG_1 337 // Placez un CD d'Alerte Rouge +#define TXT_CD_DIALOG_2 338 // Placez le CD %d (%s) dans +#define TXT_CD_ERROR1 339 // Alerte Rouge n'a pas +#define TXT_NO_SOUND_CARD 340 // Pas de carte sonore +#define TXT_UNKNOWN 341 // INCONNU +#define TXT_OLD_GAME 342 // (ancien) +#define TXT_NO_SPACE 343 // Espace disque insuffisant +#define TXT_MUST_HAVE_SPACE 344 // Vous devez disposer de %d +#define TXT_RUN_SETUP 345 // Lancez d'abord le programme +#define TXT_WAITING_FOR_OPPONENT 346 // Attente adversaire +#define TXT_SELECT_SETTINGS 347 // Choisissez l'option +#define TXT_PRISON 348 // Prison +#define TXT_GAME_WAS_SAVED 349 // Mission sauvegard‚e +#define TXT_SPACE_CANT_SAVE 350 // Espace disque insuffisant +#define TXT_INVALID_PORT_ADDRESS 351 // Port/Adresse invalide. COM +#define TXT_INVALID_SETTINGS 352 // paramŠtres Port et/ou IRQ +#define TXT_IRQ_ALREADY_IN_USE 353 // IRQ d‚j… utilis‚ +#define TXT_ABORT 354 // Oui +#define TXT_RESTART 355 // Recommencer +#define TXT_RESTARTING 356 // Mission relanc‚e. Attendez +#define TXT_LOADING 357 // Chargement de la mission. +#define TXT_ERROR_IN_INITSTRING 358 // Erreur chaŒne +#define TXT_SHADOW_COLON 359 // Ombre +#define TXT_AVMINE 360 // Mine Anti-V‚hicule +#define TXT_APMINE 361 // Mine Anti-Personnel +#define TXT_NEW_MISSIONS 362 // Nouvelles missions +#define TXT_THIEF 363 // Voleur +#define TXT_MRJ 364 // Brouilleur de radar +#define TXT_GAP_GENERATOR 365 // G‚n‚rateur d'ombre +#define TXT_PILLBOX 366 // Bunker +#define TXT_CAMOPILLBOX 367 // Bunker camoufl‚ +#define TXT_CHRONOSPHERE 368 // ChronosphŠre +#define TXT_ENGLAND 369 // Roy. Uni +#define TXT_GERMANY 370 // Allemagne +#define TXT_SPAIN 371 // Espagne +#define TXT_USSR 372 // URSS +#define TXT_UKRAINE 373 // Ukraine +#define TXT_GREECE 374 // GrŠce +#define TXT_FRANCE 375 // France +#define TXT_TURKEY 376 // Turquie +#define TXT_SHORE 377 // Rivage +#define TXT_PLACE_OBJECT 378 // Choisir objet +#define TXT_SS 379 // Sous-marin +#define TXT_DD 380 // Contre-torpilleur +#define TXT_CA 381 // Croiseur +#define TXT_TRANSPORT 382 // Transport +#define TXT_PT 383 // Aviso-torpilleur +#define TXT_LOBBY 384 // Hall +#define TXT_CHANNEL_GAMES 385 // Parties +#define TXT_SAVING_GAME 386 // Sauvegarder partie... +#define TXT_GAME_FULL 387 // La partie est au complet. +#define TXT_MUST_SELECT_GAME 388 // Vous devez s‚lectionner une +#define TXT_S_PLAYING_S 389 // %s joue contre %s +#define TXT_ONLY_HOST_CAN_MODIFY 390 // Seul l'h“te peut modifier +#define TXT_GAME_CANCELLED 391 // La partie a ‚t‚ annul‚e. +#define TXT_S_FORMED_NEW_GAME 392 // %s a initi‚ une nouvelle +#define TXT_GAME_NOW_IN_PROGRESS 393 // La partie de %s est +#define TXT_TESLA 394 // Bobine de Tesla +#define TXT_MGG 395 // G‚n‚rateur d'ombre mobile +#define TXT_FLAME_TURRET 396 // Tour lance-flammes +#define TXT_AAGUN 397 // Canon Anti-Avion +#define TXT_KENNEL 398 // Niche +#define TXT_SOVIET_TECH 399 // Centre Technique +#define TXT_BADGER 400 // Bombardier +#define TXT_MIG 401 // Mig +#define TXT_YAK 402 // Yak +#define TXT_FENCE 403 // Barbel‚s +#define TXT_MEDIC 404 // M‚decin +#define TXT_SABOTEUR 405 // Saboteur +#define TXT_GENERAL 406 // G‚n‚ral +#define TXT_E7 407 // Tanya +#define TXT_PARA_BOMB 408 // Parabombes +#define TXT_PARA_INFANTRY 409 // Parachutistes +#define TXT_PARA_SABOTEUR 410 // Saboteur parachutiste +#define TXT_SHIP_YARD 411 // Chantier naval +#define TXT_SUB_PEN 412 // Port sous-marin +#define TXT_SCENARIO_OPTIONS 413 // Options Sc‚nario +#define TXT_SPY_MISSION 414 // Avion espion +#define TXT_U2 415 // Avion espion +#define TXT_GUARD_DOG 416 // Chien d'attaque +#define TXT_SPY_INFO 417 // Info Espion +#define TXT_BUILDNGS 418 // Bƒtiments +#define TXT_UNITS 419 // Unit‚s +#define TXT_INFANTRY 420 // Infanterie +#define TXT_AIRCRAFT 421 // Avion +#define TXT_TRUCK 422 // Camion d'approvisionnement +#define TXT_INVUL 423 // Module d'invuln‚rabilit‚ +#define TXT_IRON_CURTAIN 424 // Rideau de Fer +#define TXT_ADVANCED_TECH 425 // Centre technique avanc‚ +#define TXT_V2_LAUNCHER 426 // Lance-roquettes V2 +#define TXT_FORWARD_COM 427 // Poste de commandement +#define TXT_DEMOLITIONER 428 // Bombardeur +#define TXT_MINE_LAYER 429 // Poseur de mines +#define TXT_FAKE_CONST 430 // Chantier de construction +#define TXT_FAKE_WEAP 431 // Usine d'armement leurre +#define TXT_FAKE_YARD 432 // Chantier naval leurre +#define TXT_FAKE_PEN 433 // Port sous-marin leurre +#define TXT_FAKE_RADAR 434 // D“me radar leurre +#define TXT_THEME_BIGF 435 // Bigfoot +#define TXT_THEME_CRUS 436 // La r‚volte +#define TXT_THEME_FAC1 437 // A l'attaque 1 +#define TXT_THEME_FAC2 438 // A l'attaque 2 +#define TXT_THEME_HELL 439 // Marche de l'enfer +#define TXT_THEME_RUN1 440 // Sauve-qui-peut +#define TXT_THEME_SMSH 441 // La d‚bƒcle +#define TXT_THEME_TREN 442 // Tranch‚es +#define TXT_THEME_WORK 443 // Les professionnels +#define TXT_THEME_AWAIT 444 // Attente +#define TXT_THEME_DENSE_R 445 // Dense +#define TXT_THEME_MAP 446 // S‚lection carte +#define TXT_THEME_FOGGER1A 447 // Fogger +#define TXT_THEME_MUD1A 448 // Boue +#define TXT_THEME_RADIO2 449 // Radio 2 +#define TXT_THEME_ROLLOUT 450 // Laminage +#define TXT_THEME_SNAKE 451 // Serpent +#define TXT_THEME_TERMINAT 452 // Extermination +#define TXT_THEME_TWIN 453 // Jumeau +#define TXT_THEME_VECTOR1A 454 // Vecteur +#define TXT_TEAM_MEMBERS 455 // Equipiers +#define TXT_BRIDGE 456 // Pont +#define TXT_BARREL 457 // Baril +#define TXT_GOODGUY 458 // Amical +#define TXT_BADGUY 459 // Ennemi +#define TXT_GOLD 460 // Or +#define TXT_GEMS 461 // Gemmes +#define TXT_TEASER 462 // Film titre +#define TXT_MOVIES 463 // Films +#define TXT_INTERIOR 464 // Int‚rieur +#define TXT_SONAR_PULSE 465 // Signal sonar +#define TXT_MSLO 466 // Silo de missiles +#define TXT_GPS_SATELLITE 467 // Satellite GPS +#define TXT_NUCLEAR_BOMB 468 // Bombe atomique +#define TXT_EASY 469 // Facile +#define TXT_HARD 470 // Difficile +#define TXT_NORMAL 471 // Normal +#define TXT_DIFFICULTY 472 // S‚lectionnez un niveau de +#define TXT_ALLIES 473 // Alli‚s +#define TXT_SOVIET 474 // Soviets +#define TXT_THEME_INTRO 475 // ThŠme Intro +#define TXT_SHADOW_REGROWS 476 // Progr. ombre +#define TXT_ORE_SPREADS 477 // Progr. minerai +#define TXT_THEME_SCORE 478 // Musiques +#define TXT_INTERNET 479 // Internet +#define TXT_ICE 480 // Glace +#define TXT_CRATE 481 // Caisses +#define TXT_SKIRMISH 482 // Escarmouche +#define TXT_CHOOSE 483 // Choisissez votre camp. +#define TXT_MINERALS 484 // Min‚raux pr‚cieux +#define TXT_IGNORE 485 // Ignorer +#define TXT_ERROR_NO_RESP 486 // Erreur - le modem ne r‚pond +#define TXT_ERROR_NO_RESCODE 487 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_INIT 488 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_VERB 489 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_ECHO 490 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_DISABLE 491 // Erreur - Impossible de +#define TXT_ERROR_TOO_MANY 492 // Erreur - Trop d'erreurs +#define TXT_ERROR_ERROR 493 // Erreur - Le modem a +#define TXT_ERROR_TIMEOUT 494 // Erreur - Temps d'attente de +#define TXT_ACCOMPLISHED 495 // Accompli +#define TXT_CLICK_CONTINUE 496 // Cliquez pour continuer +#define TXT_RECEIVING_SCENARIO 497 // R‚ception du sc‚nario de +#define TXT_SENDING_SCENARIO 498 // Envoi du sc‚nario aux +#define TXT_NO_FLOW_CONTROL_RESPONSE 499 // Erreur - Le modem n'a pas +#define TXT_NO_COMPRESSION_RESPONSE 500 // Erreur - Le modem n'a pas +#define TXT_NO_ERROR_CORRECTION_RESPONSE 501 // Erreur - Le modem n'a pas +#define TXT_EXPLAIN_REGISTRATION 502 // Pour jouer … Alerte Rouge +#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 503 // Erreur - Impossible +#define TXT_REGISTER 504 // Enregistrer +#define TXT_ORE_MINE 505 // Gisement Minerai +#define TXT_NO_REGISTERED_MODEM 506 // Aucun modem configur‚ +#define TXT_CHRONOSHIFT 507 // D‚placement chronoporte +#define TXT_UNABLE_TO_OPEN_PORT 508 // Adresse invalide ou en +#define TXT_NO_DIAL_TONE 509 // Pas de tonalit‚. +#define TXT_NO_EXPANSION_SCENARIO 510 // Erreur - L'autre joueur n'a +#define TXT_STAND_BY 511 // Patientez SVP... +#define TXT_THEME_CREDITS 512 // Musique du g‚n‚rique de fin +#define TXT_POWER_AAGUN 513 // Puissance faible: Canon(s) +#define TXT_POWER_TESLA 514 // Puissance faible: Bobine(s) +#define TXT_LOW_POWER 515 // Puissance Faible +#define TXT_COMMANDER 516 // Commandant: +#define TXT_BATTLES_WON 517 // Parties gagn‚es: +#define TXT_MISMATCH 518 // Fichier de donn‚es du jeu +#define TXT_SCENARIO_ERROR 519 // Votre version de jeu +#define TXT_CONNECTING 520 // Connecting +#define TXT_MODEM_INITIALISATION 521 // Initialisation du Modem +#define TXT_DATA_COMPRESSION 522 // Compression des Donn‚es +#define TXT_ERROR_CORRECTION 523 // Correction d'Erreur +#define TXT_HARDWARE_FLOW_CONTROL 524 // Contr“le Mat‚riel du Flux +#define TXT_ADVANCED 525 // Avanc‚ +#define TXT_THEME_2ND_HAND 526 // Seconde main +#define TXT_THEME_ARAZOID 527 // Arazo‹de +#define TXT_THEME_BACKSTAB 528 // Retour … l'envoyeur +#define TXT_THEME_CHAOS2 529 // Chaos2 +#define TXT_THEME_SHUT_IT 530 // Fermez-la ! +#define TXT_THEME_TWINMIX1 531 // Visite de courtoisie +#define TXT_THEME_UNDER3 532 // A couvert +#define TXT_THEME_VR2 533 // VR2 +#define TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING 534 // L'autre systŠme ne r‚pond +#define TXT_ASK_EMERGENCY_SAVE_HUNG_UP 535 // L'autre systŠme a +#define TXT_NO_REG_APP 536 // Alerte Rouge n'a pu +#define TXT_NO_CS_SCENARIOS 537 // Un joueur de la partie n'a +#define TXT_MISSILESUB 538 // Sous-marin MS +#define TXT_SHOCKTROOPER 539 // Electrocuteur +#define TXT_MECHANIC 540 // M‚canicien +#define TXT_CHRONOTANK 541 // Chrono Tank +#define TXT_TESLATANK 542 // Tank Tesla +#define TXT_MAD 543 // Tank M.A.D. +#define TXT_DEMOTRUCK 544 // Camion de d‚molition +#define TXT_PHASETRANSPORT 545 // Transport Cam‚l‚on +#define TXT_THEME_BOG 546 // Mar‚cages +#define TXT_THEME_FLOAT_V2 547 // Volutes +#define TXT_THEME_GLOOM 548 // T‚nŠbres +#define TXT_THEME_GRNDWIRE 549 // Terrain min‚ +#define TXT_THEME_RPT 550 // M‚caniciens 2 +#define TXT_THEME_SEARCH 551 // Battue +#define TXT_THEME_TRACTION 552 // Traction +#define TXT_THEME_WASTELND 553 // Chaos +#define TXT_CARRIER 554 // H‚liport Mobile diff --git a/CODE/CONQUER.IDE b/CODE/CONQUER.IDE new file mode 100644 index 0000000..66bf576 Binary files /dev/null and b/CODE/CONQUER.IDE differ diff --git a/CODE/CONQUER.LNT b/CODE/CONQUER.LNT new file mode 100644 index 0000000..7bb2410 --- /dev/null +++ b/CODE/CONQUER.LNT @@ -0,0 +1,79 @@ +// Watcom C, C++ (32 bit), -si4 -sp4, +// Standard lint options + +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + +//------------------------------------------------------------------ + +-width(0,0) // don't break up message lines + +// 32 bit integer and pointer size is four bytes. +-si4 -sp4 + +// Include directories +-ic:\projects\c&czero\code\watcom\h;..\vq\include;..\gcl510\h diff --git a/CODE/CONST.CPP b/CODE/CONST.CPP new file mode 100644 index 0000000..52adc51 --- /dev/null +++ b/CODE/CONST.CPP @@ -0,0 +1,820 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 20, 1993 * + * * + * Last Update : September 20, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the access passwords used to activate cheat mode, editor mode, +** and special game options. +*/ +long const PlayCodes[] = { + 0xE0792D6D, // Dwight Okahara + 0x90046ECF, // Paul S. Mudra + 0xC3EE9A26, // Frank Klepaki + 0xED382178, // Ed Del Castillo + 0L +}; + +long const CheatCodes[] = { + 0xA0E2AB53, // Joseph Hewitt + 0x00532693, // Mike Lightner + 0x7DDFF824, // Joe Bostic + 0x2CB5CF01, // Phil Gorrow + 0xB5B63531, // Bill Randolph + 0xDFABC23A, // Adam Isgreen + 0x52B19A22, // Erik Yeo + 0xBE79088C, // David Dettmer + 0xB216AE7E, // Barry Green + 0x0E07B213, // Steve Tall + 0L +}; + + +long const EditorCodes[] = { + 0xA2C09326, // Erik Yeo + 0x1F944BB3, // Mike Lightner + 0xDE07154D, // Adam Isgreen + 0x0E07B213, // Steve Tall + 0x16B170B1, // Joe Bostic + 0L +}; + + +/*********************************************************************************************** +** Unit order names. These names correspond to the player selectable orders +** a unit can have. The system initiated orders have no use for the ASCII name +** associated, but they are listed here for completeness sake. +*/ +char const * Missions[MISSION_COUNT] = { + "Sleep", + "Attack", + "Move", + "QMove", + "Retreat", + "Guard", + "Sticky", + "Enter", + "Capture", + "Harvest", + "Area Guard", + "Return", + "Stop", + "Ambush", + "Hunt", + "Unload", + "Sabotage", + "Construction", + "Selling", + "Repair", + "Rescue", + "Missile", + "Harmless" +}; + + +/*************************************************************************** +** Special weapon names. +*/ +#ifdef SCENARIO_EDITOR +char const * const SpecialWeaponName[SPC_COUNT] = { + "Sonar Pulse", + "Nuclear Missile", + "Chronosphere", + "Parachute Bomb", + "Paratroopers", + "Recon Plane", + "Iron Curtain", + "GPS Satellite" +}; +#endif +int const SpecialWeaponHelp[SPC_COUNT] = { + TXT_SONAR_PULSE, + TXT_NUCLEAR_BOMB, + TXT_CHRONOSHIFT, + TXT_PARA_BOMB, + TXT_PARA_INFANTRY, + TXT_SPY_MISSION, + TXT_INVUL, + TXT_GPS_SATELLITE +}; +char const * const SpecialWeaponFile[SPC_COUNT] = { + "SONR", + "ATOM", + "WARP", + "PBMB", + "PINF", + "CAM", + "INFX", + "GPSS" +}; + + +/*************************************************************************** +** Type of quarry to search out and attack. These values are used for team +** attack missions. +*/ +char const * const QuarryName[QUARRY_COUNT] = { + "N/A", + "Anything", + "Buildings - any", + "Harvesters", + "Infantry", + "Vehicles - any", + "Ships - any", + "Factories", + "Base Defenses", + "Base Threats", + "Power Facilities", + "Fake Buildings" +}; + + +/*************************************************************************** +** These are the text names for the formation types. +*/ +char const * const FormationName[FORMATION_COUNT] = { + "None", + + "Tight", + "Loose", + "Wedge North", + "Wedge East", + "Wedge South", + "Wedge West", + "Line N/S", + "Line E/W" +}; + + +/*************************************************************************** +** These are the ASCII names for the reinforcement sources. +*/ +char const * const SourceName[SOURCE_COUNT] = +{ + "North", + "East", + "South", + "West", + "Air" +}; + + +/*************************************************************************** +** These are the text names for the various armor types a unit may possess. +*/ +char const * const ArmorName[ARMOR_COUNT] = { + "none", + "wood", + "light", + "heavy", + "concrete" +}; + + +// HACK ALERT! This unused text string is here to stop Watcom from crashing. There is some +// magic text heap length that causes a crash before the code executes. This dummy string +// changes the text heap length enough to stop the crash. Who knows why, but it works. +char * __test__ = "alskdfjlasdfjkajsdfkja;sldjfklasj9awutreqjfnfdkvnldzlknvadsjgflkasdjfkajsdfas"; + + +/*************************************************************************** +** The list of VQ filenames. +*/ +char const * const VQName[VQ_COUNT] = { + "AAGUN", + "MIG", + "SFROZEN", + "AIRFIELD", + "BATTLE", + "BMAP", + "BOMBRUN", + "DPTHCHRG", + "GRVESTNE", + "MONTPASS", + "MTNKFACT", + "CRONTEST", + "OILDRUM", + "ALLYEND", + "RADRRAID", + "SHIPYARD", + "SHORBOMB", + "SITDUCK", + "SLNTSRVC", + "SNOWBASE", + "EXECUTE", + "REDINTRO", // low res. + "NUKESTOK", + "V2ROCKET", + "SEARCH", + "BINOC", + "ELEVATOR", + "FROZEN", + "MCV", + "SHIPSINK", + "SOVMCV", + "TRINITY", + "ALLYMORF", + "APCESCPE", + "BRDGTILT", + "CRONFAIL", + "STRAFE", + "DESTROYR", + "DOUBLE", + "FLARE", + "SNSTRAFE", + "LANDING", + "ONTHPRWL", + "OVERRUN", + "SNOWBOMB", + "SOVCEMET", + "TAKE_OFF", + "TESLA", + "SOVIET8", + "SPOTTER", + "ALLY1", + "ALLY2", + "ALLY4", + "SOVFINAL", + "ASSESS", + "SOVIET10", + "DUD", + "MCV_LAND", + "MCVBRDGE", + "PERISCOP", + "SHORBOM1", + "SHORBOM2", + "SOVBATL", + "SOVTSTAR", + "AFTRMATH", + "SOVIET11", + "MASASSLT", + "ENGLISH", // High res. + "SOVIET1", + "SOVIET2", + "SOVIET3", + "SOVIET4", + "SOVIET5", + "SOVIET6", + "SOVIET7", + "PROLOG", + "AVERTED", + "COUNTDWN", + "MOVINGIN", + "ALLY10", + "ALLY12", + "ALLY5", + "ALLY6", + "ALLY8", + "TANYA1", + "TANYA2", + "ALLY10B", + "ALLY11", + "ALLY14", + "ALLY9", + "SPY", + "TOOFAR", + "SOVIET12", + "SOVIET13", + "SOVIET9", + "BEACHEAD", + "SOVIET14", + "SIZZLE", + "SIZZLE2", + "ANTEND", + "ANTINTRO" +}; + + +/*************************************************************************** +** Relative coordinate offsets from the center of a cell for each +** of the legal positions that an object in a cell may stop at. Only infantry +** are allowed to stop at other than the center of the cell. +*/ +COORDINATE const StoppingCoordAbs[5] = { + 0x00800080L, // center + 0x00400040L, // upper left + 0x004000C0L, // upper right + 0x00C00040L, // lower left + 0x00C000C0L // lower right +}; + + +/*************************************************************************** +** Converts pixel values (cell relative) into the appropriate lepton (sub cell) +** value. This is used to convert pixel (screen) coordinates into the underlying +** coordinate system. +*/ +unsigned char const Pixel2Lepton[24] = { + 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B, + 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0, + 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5 +}; + + +/*************************************************************************** +** This array is used to index a facing in order to retrieve a cell +** offset that, when added to another cell, will achieve the adjacent cell +** in the indexed direction. +*/ +CELL const AdjacentCell[FACING_COUNT] = { + -(MAP_CELL_W), // North + -(MAP_CELL_W-1), // North East + 1, // East + MAP_CELL_W+1, // South East + MAP_CELL_W, // South + MAP_CELL_W-1, // South West + -1, // West + -(MAP_CELL_W+1) // North West +}; + +COORDINATE const AdjacentCoord[FACING_COUNT] = { + 0xFF000000L, + 0xFF000100L, + 0x00000100L, + 0x01000100L, + 0x01000000L, + 0x0100FF00L, + 0x0000FF00L, + 0xFF00FF00L +}; + + +/*************************************************************************** +** This specifies the odds of receiving the various random crate power +** ups. The odds are expressed as "shares" of 100 percent. +*/ +int CrateShares[CRATE_COUNT] = { + 50, // CRATE_MONEY + 20, // CRATE_UNIT + 3, // CRATE_PARA_BOMB + 1, // CRATE_HEAL_BASE + 3, // CRATE_CLOAK + 5, // CRATE_EXPLOSION + 5, // CRATE_NAPALM + 20, // CRATE_SQUAD + 1, // CRATE_DARKNESS + 1, // CRATE_REVEAL + 3, // CRATE_SONAR + 10, // CRATE_ARMOR + 10, // CRATE_SPEED + 10, // CRATE_FIREPOWER + 1, // CRATE_ICBM + 1, // CRATE_TIMEQUAKE + 3, // CRATE_INVULN + 5 // CRATE_VORTEX +}; + +AnimType CrateAnims[CRATE_COUNT] = { + ANIM_NONE, // CRATE_MONEY + ANIM_NONE, // CRATE_UNIT + ANIM_NONE, // CRATE_PARA_BOMB + ANIM_NONE, // CRATE_HEAL_BASE + ANIM_NONE, // CRATE_CLOAK + ANIM_NONE, // CRATE_EXPLOSION + ANIM_NONE, // CRATE_NAPALM + ANIM_NONE, // CRATE_SQUAD + ANIM_NONE, // CRATE_DARKNESS + ANIM_NONE, // CRATE_REVEAL + ANIM_NONE, // CRATE_SONAR + ANIM_NONE, // CRATE_ARMOR + ANIM_NONE, // CRATE_SPEED + ANIM_NONE, // CRATE_FIREPOWER + ANIM_NONE, // CRATE_ICBM + ANIM_NONE, // CRATE_TIMEQUAKE + ANIM_NONE, // CRATE_INVULN + ANIM_NONE // CRATE_VORTEX +}; + +int CrateData[CRATE_COUNT] = { + 0, // CRATE_MONEY + 0, // CRATE_UNIT + 0, // CRATE_PARA_BOMB + 0, // CRATE_HEAL_BASE + 0, // CRATE_CLOAK + 0, // CRATE_EXPLOSION + 0, // CRATE_NAPALM + 0, // CRATE_SQUAD + 0, // CRATE_DARKNESS + 0, // CRATE_REVEAL + 0, // CRATE_SONAR + 0, // CRATE_ARMOR + 0, // CRATE_SPEED + 0, // CRATE_FIREPOWER + 0, // CRATE_ICBM + 0, // CRATE_TIMEQUAKE + 0, // CRATE_INVULN + 0 // CRATE_VORTEX +}; + +char const * const CrateNames[CRATE_COUNT] = { + "Money", + "Unit", + "ParaBomb", + "HealBase", + "Cloak", + "Explosion", + "Napalm", + "Squad", + "Darkness", + "Reveal", + "Sonar", + "Armor", + "Speed", + "Firepower", + "ICBM", + "TimeQuake", + "Invulnerability", + "ChronalVortex" +}; + + +/*************************************************************************** +** This converts 0..255 facing values into either 8, 16, or 32 facing values. +** Note: a simple shift won't suffice because 0..255 facing values should +** be converted to the CLOSEST appropriate facing, NOT rounded down to the +** nearest facing. +*/ +unsigned char const Facing8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +unsigned char const Facing16[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0 +}; + + +signed char const Rotation16[256] = { + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1 +}; + + +/* +** This table incorporates a compensating factor for the distortion caused +** by 3D-Studio when it tries to render 45% angles. +*/ +unsigned char const Facing32[256] = { + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8, + 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19, + 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24, + 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0 +}; + + +#ifdef OBSOLETE +unsigned char const Facing32[256] = { + 0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8, + 9,9,9,9,9,9,9,9, + 10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11, + 12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13, + 14,14,14,14,14,14,14,14, + 15,15,15,15,15,15,15,15, + 16,16,16,16,16,16,16,16, + 17,17,17,17,17,17,17,17, + 18,18,18,18,18,18,18,18, + 19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20, + 21,21,21,21,21,21,21,21, + 22,22,22,22,22,22,22,22, + 23,23,23,23,23,23,23,23, + 24,24,24,24,24,24,24,24, + 25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26, + 27,27,27,27,27,27,27,27, + 28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29, + 30,30,30,30,30,30,30,30, + 31,31,31,31,31,31,31,31, + 0,0,0,0 +}; +#endif + + +/*************************************************************************** +** These are the movement costs (in ticks at fastest speed) to enter each +** of the given terrain cells. +*/ + +int const GroundColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +#ifdef NEVER +int const GroundColor[LAND_COUNT] = { + 46, // "Clear" terrain. + 44, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + 64, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + WHITE, // "Clear" terrain. + LTGRAY, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + LTGRAY, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; +#endif + +GroundType Ground[LAND_COUNT]; + + +/*************************************************************************** +** These are the names of the theaters. +*/ +TheaterDataType const Theaters[THEATER_COUNT] = { + {"TEMPERATE","TEMPERAT","TEM"}, + {"SNOW","SNOW","SNO"}, + {"INTERIOR","INTERIOR","INT"}, +}; + + +unsigned char const RemapCiv2[256] = { + 0,1,2,3,4,5,6,209,8,9,10,11,12,13,12,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,187,188,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,209, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,167, 13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv4[256] = { + 0,1,2,3,4,5,6,187,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,118,110,119, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,188,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv5[256] = { + 0,1,2,3,4,5,6,109,8,9,10,11,131,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,177,110,178, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,111,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv6[256] = { + 0,1,2,3,4,5,6,120,8,9,10,11,12,13,238,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,236,206,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,111, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv7[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,157,212,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,7, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,118,119,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv8[256] = { + 0,1,2,3,4,5,6,182,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,215,7,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,182, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,198,199,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv9[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,7,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,163,165,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,200, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,111,13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv10[256] = { + 0,1,2,3,4,5,6,137,8,9,10,11,12,13,15,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,129,131,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,137, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,163,165,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapEmber[256] = { +#define CEC CC_EMBER_COLOR + 0,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,BLACK,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC +}; + + +//char const Keys[] = +// "[PublicKey]\n" +// "1=AgkCbXo9sKMHOBk=\n" +//#ifdef CHEAT_KEYS +// "[PrivateKey]\n" +// "1=AggxFU55vc7LYQ==\n" +//#endif +// "\n"; + +char const Keys[] = +"[PublicKey]\n" +"1=AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V\n" +#ifdef CHEAT_KEYS +"[PrivateKey]\n" +"1=AigKVje8mROcR8QixnxUEF5b29Curkq01DNDWCdOG99XBqH79OaCiTCB\n" +#endif +"\n"; diff --git a/CODE/CONTROL.CPP b/CODE/CONTROL.CPP new file mode 100644 index 0000000..6fa9224 --- /dev/null +++ b/CODE/CONTROL.CPP @@ -0,0 +1,226 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONTROL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONTROL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : December 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ControlClass::Action -- Normal action for control gadget objects. * + * ControlClass::ControlClass -- Constructor for control class objects. * + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Constructor for control class objects. * + * * + * This is the normal constructor for control class objects. At this level, it only needs * + * to record the ID number assigned to this button. * + * * + * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then * + * this tells the system that no special ID code should be returned. * + * * + * x,y -- Pixel coordinate of upper left corner of gadget's region. * + * * + * w,h -- Pixel dimensions of the gadget's region. * + * * + * flags -- The input event flags that this gadget recognizes. * + * * + * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the * + * gadget list while the mouse button is held down, if the mouse button was * + * initially clicked over its region. This is the behavior of "normal" * + * buttons in Windows. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) : + GadgetClass(x, y, w, h, flags, sticky), + ID(id), + Peer(0) +{ +} + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * * + * This copy constructor for a control gadget is used create a duplicate gadget that * + * is functionally similar. * + * * + * INPUT: control -- Reference to the gadget that is to be copied. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/05/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(ControlClass const & control) : + GadgetClass(control), + ID(control.ID), + Peer(control.Peer) +{ +} + +/*********************************************************************************************** + * ControlClass::Action -- Normal action for control gadget objects. * + * * + * This function gets called when the input event that this control gadget is looking for * + * occurs. In such a case, the return key code value is changed to the gadget's ID number * + * with the special button bit flag attached. * + * * + * INPUT: flags -- The event that triggered this function call. If this value is NULL, then * + * this is a forced (probably due to the sticky flag) call and the key code * + * is not altered. * + * * + * key -- Reference to the key code that will be returned by the controlling * + * Input() function. * + * * + * OUTPUT: bool; Should further list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Action(unsigned flags, KeyNumType & key) +{ + + /* + ** Only if the flags indicate that a recognized action has occurred, do the + ** normal processing of this gadget and set return value to the gadget ID. + */ + if (flags) { + if (ID) { + key = (KeyNumType)(ID | KN_BUTTON); + } else { + key = KN_NONE; + } + } + + /* + ** If there is a peer link established, inform that gadget of this + ** action call. + */ + if (Peer) { + Peer->Peer_To_Peer(flags, key, *this); + } + + return(GadgetClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * * + * This function will assign another gadget to this one. That other gadget will receive * + * notification of any Action() call to this gadget. Presumably, this is how one gadget * + * can automatically adapt to changes in another. Say for example, a slider bar can affect * + * the list box it is attached to. * + * * + * INPUT: gadget -- The gadget to inform when any Action() function is called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ControlClass::Make_Peer(GadgetClass & gadget) +{ + Peer = &gadget; +} + + +/*********************************************************************************************** + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * * + * This function will query and return with the ID number for this gadget. It is primarily * + * used by the Extract_Gadget() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that * + * no ID was assigned to this gadget. This is a special case since a zero value will * + * never be returned as a pseudo-key as is done with non-zero values. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +unsigned ControlClass::Get_ID(void) const +{ + return(ID); +} + + +/*********************************************************************************************** + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * * + * This is called when the control object might need to be redrawn or when redrawing is * + * necessary. Since at this level of the class hierarchy, no actual drawing occurs, this * + * routine doesn't perform any rendering. It does, however, inform any peer attached * + * object that a Draw_Me function has been called. Presumably, the attached peer gadget * + * might very well need to be redrawn as a result of some action by this gadget. Since this * + * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me * + * for the other gadget will not occur. It must rely on the call by this routine in order * + * to update correctly. A typical example of this would be a slider that is attached to * + * a list box. As the slider is being drug around, the attached list box must be redrawn. * + * * + * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the gadget redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Draw_Me(int forced) +{ + if (Peer) { + Peer->Draw_Me(); + } + return(GadgetClass::Draw_Me(forced)); +} diff --git a/CODE/CONTROL.H b/CODE/CONTROL.H new file mode 100644 index 0000000..078db39 --- /dev/null +++ b/CODE/CONTROL.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CONTROL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CONTROL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "gadget.h" + +/*************************************************************************** + * ControlClass -- Region tracking class * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: 0 = new scenario created, -1 = not * + * WARNINGS: This class is Abstract (cannot make an instance of it) * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=========================================================================*/ +class ControlClass : public GadgetClass +{ + public: + ControlClass(NoInitClass const & x) : GadgetClass(x) {}; + ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); + ControlClass(ControlClass const & control); + + virtual void Make_Peer(GadgetClass & gadget); + + /* + ** Render support function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the ID number for this control gadget. This number is used to generate + ** a special pseudo-key when the gadget detects valid input. + */ + unsigned ID; + + protected: + virtual unsigned Get_ID(void) const; + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This points to the peer button to inform when something happens to this + ** gadget. + */ + GadgetClass * Peer; +}; + +#endif + diff --git a/CODE/COORD.CPP b/CODE/COORD.CPP new file mode 100644 index 0000000..b33da6b --- /dev/null +++ b/CODE/COORD.CPP @@ -0,0 +1,652 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/COORD.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : COORD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + * Support code to handle the coordinate system is located in this module. * + * Routines here will be called QUITE frequently during play and must be * + * as efficient as possible. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * Coord_Cell -- Convert a coordinate into a cell number. * + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * Distance -- Determines the cell distance between two cells. * + * Distance -- Determines the lepton distance between two coordinates. * + * Distance -- Fetch distance between two target values. * + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * Normal_Move_Point -- Moves point with tilt compensation. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Coord_Cell -- Convert a coordinate into a cell number. * + * * + * This routine will convert the specified coordinate value into a cell number. This is * + * useful to determine the map index number into the cell array that corresponds to a * + * particular coordinate. * + * * + * INPUT: coord -- The coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that corresponds to the coordinate specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +CELL Coord_Cell(COORDINATE coord) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell; + cell.Sub.Y = ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell; + return(cell.Cell); +// return(XY_Cell(((COORD_COMPOSITE)coord).Sub.X, ((COORD_COMPOSITE)composite).Sub.Y)); +} + + +/*********************************************************************************************** + * Distance -- Fetch distance between two target values. * + * * + * This routine will determine the lepton distance between the two specified target * + * values. * + * * + * INPUT: target1 -- First target value. * + * * + * target2 -- Second target value. * + * * + * OUTPUT: Returns with the lepton distance between the two target values. * + * * + * WARNINGS: Be sure that the targets are legal before calling this routine. Otherwise, the * + * return value is meaningless. * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +int Distance(TARGET target1, TARGET target2) +{ + return(Distance(As_Coord(target1), As_Coord(target2))); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance(COORDINATE coord1, COORDINATE coord2) +{ + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * * + * This routine will take an arbitrary position and object size and return with a list of * + * cell offsets from the current cell for all cells that are overlapped by the object. The * + * first cell offset is always zero, so to just get the adjacent spill cell list, add one * + * to the return pointer. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * maxsize -- The maximum width/height of the object (pixels). * + * * + * OUTPUT: Returns with a pointer to a spillage list. * + * * + * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values * + * will generate an incomplete overlap list. * + * * + * HISTORY: * + * 11/06/1993 JLB : Created. * + * 03/25/1994 JLB : Added width optimization. * + * 04/29/1994 JLB : Converted to C. * + * 06/03/1994 JLB : Converted to general purpose spillage functionality. * + * 01/07/1995 JLB : Manually calculates spillage list for large objects. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, int maxsize) +{ + static short const _MoveSpillage[(int)FACING_COUNT+1][5] = { + {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N + {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE + {0, 1, REFRESH_EOL, 0, 0}, // E + {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE + {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S + {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW + {0, -1, REFRESH_EOL, 0, 0}, // W + {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW + {0, REFRESH_EOL, 0, 0, 0} // non-moving. + }; + static short _manual[10]; +//; 00 = on axis +//; 01 = below axis +//; 10 = above axis +//; 11 = undefined + static signed char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1}; + int index=0; + int x,y; + + /* + ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table + ** that covers a 5x5 square region. + */ + if (maxsize > ICON_PIXEL_W * 2) { + static short const _gigundo[] = { + -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2), + -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2), + -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2), + ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2), + +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2), + REFRESH_EOL + }; + return(&_gigundo[0]); + } + + /* + ** For very large objects, build the overlap list by hand. This is time consuming, but + ** not nearly as time consuming as drawing even a single cell unnecessarily. + */ + if (maxsize > ICON_PIXEL_W) { + maxsize = min(maxsize, (ICON_PIXEL_W*2))/2; + + x = (ICON_PIXEL_W * Coord_XLepton(coord)) / ICON_LEPTON_W; + y = (ICON_PIXEL_H * Coord_YLepton(coord)) / ICON_LEPTON_H; + int left = x-maxsize; + int right = x+maxsize; + int top = y-maxsize; + int bottom = y+maxsize; + + _manual[index++] = 0; + if (left < 0) _manual[index++] = -1; + if (right >= ICON_PIXEL_W) _manual[index++] = 1; + if (top < 0) _manual[index++] = -MAP_CELL_W; + if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W; + if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1); + if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1; + if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1; + if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1); + _manual[index] = REFRESH_EOL; + return(&_manual[0]); + } + + /* + ** Determine the number of leptons "leeway" allowed this unit. + */ + int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2]; + + x = Coord_XLepton(coord) - 0x0080; + y = Coord_YLepton(coord) - 0x0080; + if (y > posval) index |= 0x08; // Spilling South. + if (y < -posval) index |= 0x04; // Spilling North. + if (x > posval) index |= 0x02; // Spilling East. + if (x < -posval) index |= 0x01; // Spilling West. + + return(&_MoveSpillage[_SpillTable[index]][0]); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * * + * Given a center coordinate and a dirty rectangle, calcuate a cell offset list for * + * determining such things as overlap and redraw logic. Optionally, the center cell * + * location will not be part of the list. * + * * + * INPUT: coord -- The center coordinate that the dirty rectangle is based off of. * + * * + * rect -- Reference to the dirty rectangle. * + * * + * nocenter -- If true, then the center cell offset will not be part of the spillage * + * list returned. This is handy when the center cell is known to be * + * processed by some other method and it can be safely and efficiently * + * ignored by the list generated. * + * * + * OUTPUT: Returns with a pointer to the spillage list that corresponds to the data * + * specified. This is a pointer to a static buffer and as such it will only be valid * + * until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter) +{ + if (!rect.Is_Valid()) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + CELL coordcell = Coord_Cell(coord); + LEPTON x = Coord_X(coord); + LEPTON y = Coord_Y(coord); + + /* + ** Add the rectangle values to the coordinate in order to normalize the start and end + ** corners of the rectangle. The values are now absolute to the real game world rather + ** than relative to the coordinate. + */ + LEPTON_COMPOSITE startx; + LEPTON_COMPOSITE starty; + LEPTON_COMPOSITE endx; + LEPTON_COMPOSITE endy; + startx.Raw = (int)x + (short)Pixel_To_Lepton(rect.X); + starty.Raw = (int)y + (short)Pixel_To_Lepton(rect.Y); + endx.Raw = startx.Raw + Pixel_To_Lepton(rect.Width-1); + endy.Raw = starty.Raw + Pixel_To_Lepton(rect.Height-1); + + /* + ** Determine the upper left and lower right cell indexes. This is a simple conversion from + ** their lepton counterpart. These cells values are used to form the bounding box for the + ** map offset list. + */ + int cellx = startx.Sub.Cell; + int cellx2 = endx.Sub.Cell; + int celly = starty.Sub.Cell; + int celly2 = endy.Sub.Cell; + + /* + ** Generate the spillage list by counting off the rows and colums of the cells + ** that are affected. This is easy since the upper left and lower right corner cells + ** are known. + */ + int count = 0; + static short _spillagelist[128]; + short * ptr = _spillagelist; + for (int yy = celly; yy <= celly2; yy++) { + for (int xx = cellx; xx <= cellx2; xx++) { + short offset = (XY_Cell(xx, yy) - coordcell); + if (!nocenter || offset != 0) { + *ptr++ = offset; + count++; + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + } + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + + /* + ** Cap the list with the end of list marker and then return a pointer + ** to the completed list. + */ + *ptr = REFRESH_EOL; + return(_spillagelist); +} + + +/*********************************************************************************************** + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * * + * This function will move a coordinate in a using SIN and COS arithmetic. * + * * + * INPUT: start -- The starting coordinate. * + * * + * dir -- The direction to move the coordinate. * + * * + * distance -- The distance to move the coordinate position (in leptons). * + * * + * OUTPUT: Returns the new coordinate position. * + * * + * WARNINGS: This routine uses multiplies -- use with caution. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance) +{ +#ifdef NEVER + short x = Coord_X(start); + short y = Coord_Y(start); + + Move_Point(x, y, dir, distance); + return(XY_Coord(x,y)); +#endif + + Move_Point(*(short *)&start, *(((short *)&start)+1), dir, distance); + return(start); +} + + +/*********************************************************************************************** + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * * + * This routine will perform a scatter algorithm on the specified * + * anchor point in order to return with another coordinate that is * + * randomly nearby the original. Typical use of this would be for * + * missile targeting. * + * * + * INPUT: coord -- This is the anchor coordinate. * + * * + * distance -- This is the distance in pixels that the scatter * + * should fall within. * + * * + * lock -- bool; Convert the new coordinate into a center * + * cell based coordinate? * + * * + * OUTPUT: Returns with a new coordinate that is nearby the original. * + * * + * WARNINGS: Maximum pixel scatter distance is 255. * + * * + * HISTORY: * + * 02/01/1992 JLB : Created. * + * 05/13/1992 JLB : Only uses Random(). * + *=============================================================================================*/ +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock) +{ + COORDINATE newcoord; + + newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance); + + if (newcoord & HIGH_COORD_MASK) newcoord = coord; + + if (lock) { + newcoord = Coord_Snap(newcoord); + } + + return(newcoord); +} + +extern int calcx(signed short, short distance); +#pragma aux calcx parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ +// "and eax,0FFFFh"; + +extern int calcy(signed short, short distance); +#pragma aux calcy parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ + "neg eax"; +// "and eax,0FFFFh" \ + +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ + static char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + +#ifdef OBSOLETE + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + x += _DX; +#else + x += calcx(CosTable[dir], distance); +#endif +// asm add [word ptr start],ax + +#ifdef OBSOLETE + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + y += _DX; +#else + y += calcy(SinTable[dir], distance); +#endif +// asm add [word ptr start+2],ax + +} + + +/*********************************************************************************************** + * Normal_Move_Point -- Moves point with tilt compensation. * + * * + * This routine will move the point in the direction and distance specified but it will * + * take into account the tilt of the playing field. Typical use of this routine is to * + * determine positioning as it relates to the playfield. Turrets are a good example of * + * this. * + * * + * INPUT: x,y -- References to the coordinates to adjust. * + * * + * dir -- The direction of the desired movement. * + * * + * distance -- The distance (in coordinate units) to move the point. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1995 JLB : Created. * + *=============================================================================================*/ +// Loss of precision in initializations (8 bits to 7 bits) warning. Hmmm.. can this be fixed? +//lint -e569 +void Normal_Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ + static signed char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static signed char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + + x += calcx(CosTable[dir], distance); + + y += calcy(SinTable[dir] / 2, distance); +} diff --git a/CODE/COORDA.ASM b/CODE/COORDA.ASM new file mode 100644 index 0000000..f8e279f --- /dev/null +++ b/CODE/COORDA.ASM @@ -0,0 +1,134 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Cardinal_To_Fixed :NEAR +GLOBAL Fixed_To_Cardinal :NEAR + + CODESEG + +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Cardinal_To_Fixed(unsigned base, unsigned cardinal); + + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed + + +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Fixed_To_Cardinal(unsigned base, unsigned fixed); + PROC Fixed_To_Cardinal C near + USES edx + + ARG base:DWORD + ARG fixed:DWORD + + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END diff --git a/CODE/CPUID.ASM b/CODE/CPUID.ASM new file mode 100644 index 0000000..4f58f13 --- /dev/null +++ b/CODE/CPUID.ASM @@ -0,0 +1,185 @@ +; +; 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 . +; + +; $Header: F:\projects\c&c0\vcs\code\cpuid.asv 5.0 11 Nov 1996 09:40:28 JOE_BOSTIC $ +;*************************************************************************** +;** 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 I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : MMX.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : May 19th, 1996 * +;* * +;* Last Update : May 19th 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + .586 + .model flat + +; +; Variables externs +; +GLOBAL C CPUType:byte +;externdef C CPUType:byte +GLOBAL C VendorID:byte +;externdef C VendorID:byte + +; +; Function externs +; +GLOBAL C Detect_MMX_Availability:near +;externdef C Detect_MMX_Availability:near + + + .code + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local cputype:byte + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [cputype],al + +@@end_get_cpu: mov al,[cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + +end + diff --git a/CODE/CRATE.CPP b/CODE/CRATE.CPP new file mode 100644 index 0000000..bb17217 --- /dev/null +++ b/CODE/CRATE.CPP @@ -0,0 +1,185 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRATE.CPP 3 3/04/97 3:12p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * * + * This routine will remove the crate from whereever it happens to be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the crate found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Remove_It(void) +{ + if (Is_Valid()) { + Get_Crate(Cell); + Make_Invalid(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * * + * This will create a crate in the cell specified. If the crate could not be crated there * + * then 'false' will be returned. * + * * + * INPUT: cell -- The desired cell to place the crate in. * + * * + * OUTPUT: bool; Was the crate created and placed in the cell? * + * * + * WARNINGS: It is quite possible for the crate not to have been placed. Only the most clear * + * locations are valid for crate placement. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Create_Crate(CELL cell) +{ + /* + ** Remove any existing crate that this crate class is tracking. + */ + Remove_It(); + + /* + ** Try to place a new crate at the cell specified. + */ + if (Put_Crate(cell)) { + Cell = cell; + Timer = Random_Pick(Rule.CrateTime * (TICKS_PER_MINUTE/2), Rule.CrateTime * (TICKS_PER_MINUTE*2)); + Timer.Start(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * * + * This helpter routine will examine the cell and place the appropriate crate type into * + * the cell specified. If the overlay could not be generated, then 'false' is returned. * + * * + * INPUT: cell -- The cell to generate the crate overlay in. * + * * + * OUTPUT: bool; Was the crate overlay generated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + * 10/14/1996 JLB : Takes reference to cell so that tracking can occur. * + *=============================================================================================*/ +bool CrateClass::Put_Crate(CELL & cell) +{ + int old = ScenarioInit; + ScenarioInit = 0; + + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + while (cellptr->Overlay != OVERLAY_NONE && !cellptr->Is_Clear_To_Build(SPEED_FLOAT) && !cellptr->Is_Clear_To_Build(SPEED_FOOT)) { + cell = Map.Pick_Random_Location(); + + if (Percent_Chance(100 * Rule.WaterCrateChance)) { + cell = Map.Nearby_Location(cell, SPEED_FLOAT); + } else { + cell = Map.Nearby_Location(cell, SPEED_TRACK); + } + cellptr = &Map[cell]; + } + + if (cellptr->Is_Clear_To_Build(SPEED_FLOAT)) { + new OverlayClass(OVERLAY_WATER_CRATE, cell); + } else { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + ScenarioInit = old; + return(true); + } + + ScenarioInit = old; + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * * + * This will remove the crate from the cell specified. * + * * + * INPUT: cell -- The cell to examine and remove any crate overlays present. * + * * + * OUTPUT: bool; Was a crate overlay found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Get_Crate(CELL cell) +{ + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + if (cellptr->Overlay == OVERLAY_WOOD_CRATE || + cellptr->Overlay == OVERLAY_STEEL_CRATE || + cellptr->Overlay == OVERLAY_WATER_CRATE) { + + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + return(true); + } + } + return(false); +} diff --git a/CODE/CRATE.H b/CODE/CRATE.H new file mode 100644 index 0000000..97b55b6 --- /dev/null +++ b/CODE/CRATE.H @@ -0,0 +1,79 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRATE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : August 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CRATE_H +#define CRATE_H + +#include "ftimer.h" +#include "jshell.h" + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +class CrateClass { + public: + CrateClass(void) : Timer(NoInitClass()), Cell(-1) {} + void Init(void) {Make_Invalid();} + bool Create_Crate(CELL cell); + bool Is_Here(CELL cell) const {return(Is_Valid() && cell == Cell);} + bool Remove_It(void); + bool Is_Expired(void) const {return(Is_Valid() && Timer == 0);} + bool Is_Valid(void) const {return(Cell != -1);} + + private: + static bool Put_Crate(CELL & cell); + static bool Get_Crate(CELL cell); + + void Make_Invalid(void) {Cell = -1;Timer.Stop();} + + CDTimerClass Timer; + CELL Cell; +}; + + +#endif diff --git a/CODE/CRC.CPP b/CODE/CRC.CPP new file mode 100644 index 0000000..9b4e448 --- /dev/null +++ b/CODE/CRC.CPP @@ -0,0 +1,136 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "crc.h" + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * * + * This routine will take the specified byte of data and submit it to the CRC engine * + * for processing. This routine is designed to be as fast as possible since the typical * + * use of this routine is to feed one of presumably many byte sized chunks of data to the * + * CRC engine. * + * * + * INPUT: datum -- One byte of data to submit to the CRC engine. * + * * + * OUTPUT: none * + * * + * WARNINGS: If possible, use the buffer/size operator to submit data rather than repeated * + * calls to this routine. * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +void CRCEngine::operator() (char datum) +{ + StagingBuffer.Buffer[Index++] = datum; + + if (Index == sizeof(long)) { + CRC = Value(); + StagingBuffer.Composite = 0; + Index = 0; + } +} + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * * + * This routine will submit the specified block to the CRC engine. The block can be of * + * arbitrary length. * + * * + * INPUT: buffer -- Pointer to the buffer that contains the data. The buffer will not * + * be modified. * + * * + * length -- The length of the buffer (in bytes). * + * * + * OUTPUT: Returns with the current CRC value accumulated so far. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +long CRCEngine::operator() (void const * buffer, int length) +{ + if (buffer != NULL && length > 0) { + char const * dataptr = (char const *)buffer; + int bytes_left = length; + + /* + ** If there are any leader bytes (needed to fill the staging buffer) + ** then process those by first using them to fill up the staging + ** buffer. The bulk of the data block will be processed by the high + ** speed longword processing loop. + */ + while (bytes_left && Buffer_Needs_Data()) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + + /* + ** Perform the fast 'bulk' processing by reading long word sized + ** data blocks. + */ + long const * longptr = (long const *)dataptr; + int longcount = bytes_left / sizeof(long); // Whole 'long' elements remaining. + while (longcount--) { + CRC = _lrotl(CRC, 1) + *longptr++; + bytes_left -= sizeof(long); + } + + /* + ** If there are remainder bytes, then process these by adding them + ** to the staging buffer. + */ + dataptr = (char const *)longptr; + while (bytes_left) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + } + + /* + ** Return the current CRC value. + */ + return(Value()); +} diff --git a/CODE/CRC.H b/CODE/CRC.H new file mode 100644 index 0000000..7b758fb --- /dev/null +++ b/CODE/CRC.H @@ -0,0 +1,121 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRC.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRC_H +#define CRC_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a CRC engine class. It will process submitted data and generate a CRC from it. +** Well, actually, the value returned is not a true CRC. However, it shares the same strength +** characteristic and is faster to generate than the traditional CRC. This object is treated like +** a method class. If it is called as a function (using the function operator), it will return +** the CRC value. There are other function operators to submit data for processing. +*/ +class CRCEngine { + public: + + // Constructor for CRC engine (it can have an override initial CRC value). + CRCEngine(long initial=0) : CRC(initial), Index(0) { + StagingBuffer.Composite = 0; + }; + + // Fetches CRC value. + long operator() (void) const {return(Value());}; + + // Submits one byte sized datum to the CRC accumulator. + void operator() (char datum); + + // Submits an arbitrary buffer to the CRC accumulator. + long operator() (void const * buffer, int length); + + // Implicit conversion operator so this object appears like a 'long integer'. + operator long(void) const {return(Value());}; + + protected: + + bool Buffer_Needs_Data(void) const { + return(Index != 0); + }; + + long Value(void) const { + if (Buffer_Needs_Data()) { + return(_lrotl(CRC, 1) + StagingBuffer.Composite); + } + return(CRC); + }; + + /* + ** Current accumulator of the CRC value. This value doesn't take into + ** consideration any pending data in the staging buffer. + */ + long CRC; + + /* + ** This is the sub index into the staging buffer used to keep track of + ** partial data blocks as they are submitted to the CRC engine. + */ + int Index; + + /* + ** This is the buffer that holds the incoming partial data. When the buffer + ** is filled, the value is transformed into the CRC and the buffer is flushed + ** in preparation for additional data. + */ + union { + long Composite; + char Buffer[sizeof(long)]; + } StagingBuffer; +}; + +#endif + diff --git a/CODE/CRCPIPE.CPP b/CODE/CRCPIPE.CPP new file mode 100644 index 0000000..5b0c2fd --- /dev/null +++ b/CODE/CRCPIPE.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRCPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRCPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCPipe::Result -- Fetches the current CRC of the data. * + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcpipe.h" + + +/*********************************************************************************************** + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * * + * This routine will fetch the number of bytes requested from the straw. The data is * + * not modified by this straw segment, but it is examined by the CRC engine in order to * + * keep an accurate CRC of the data that passes through this routine. * + * * + * INPUT: source -- Pointer to the buffer that will hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number is * + * less than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCPipe::Put(void const * source, int slen) +{ + CRC(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * CRCPipe::Result -- Fetches the current CRC of the data. * + * * + * This routine will return the CRC of the data that has passed through the pipe up to * + * this time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCPipe::Result(void) const +{ + return(CRC()); +} + diff --git a/CODE/CRCPIPE.H b/CODE/CRCPIPE.H new file mode 100644 index 0000000..d5c9b16 --- /dev/null +++ b/CODE/CRCPIPE.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRCPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRCPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCPIPE_H +#define CRCPIPE_H + +#include "pipe.h" +#include "crc.h" + +/* +** This class doesn't modify the data being piped through, but it does examine it and build +** a CRC value from the data. +*/ +class CRCPipe : public Pipe +{ + public: + CRCPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCPipe(CRCPipe & rvalue); + CRCPipe & operator = (CRCPipe const & pipe); +}; + +#endif diff --git a/CODE/CRCSTRAW.CPP b/CODE/CRCSTRAW.CPP new file mode 100644 index 0000000..04de153 --- /dev/null +++ b/CODE/CRCSTRAW.CPP @@ -0,0 +1,94 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRCSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRCSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcstraw.h" + + +/*********************************************************************************************** + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * * + * This routine will fetch the number of bytes requested. The data will not be modified * + * by this straw segment, but the CRC engine will examine the data so as to keep an * + * accurate CRC value. * + * * + * INPUT: source -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored in the buffer. If this number is * + * less than that requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + CRC(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * * + * This routine will return the CRC value of the data that has passed through this straw * + * segment. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value of the data this straw segment has seen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCStraw::Result(void) const +{ + return(CRC()); +} diff --git a/CODE/CRCSTRAW.H b/CODE/CRCSTRAW.H new file mode 100644 index 0000000..da9fb9f --- /dev/null +++ b/CODE/CRCSTRAW.H @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CRCSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CRCSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCSTRAW_H +#define CRCSTRAW_H + +#include "straw.h" +#include "crc.h" + +/* +** This class will build a CRC value from the data stream that is drawn through this class. +** The data is not modified, but it is examined as it passes through. +*/ +class CRCStraw : public Straw +{ + public: + CRCStraw(void) {} + virtual int Get(void * source, int slen); + + // Calculate and return the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCStraw(CRCStraw & rvalue); + CRCStraw & operator = (CRCStraw const & pipe); +}; + + +#endif diff --git a/CODE/CREDITS.CPP b/CODE/CREDITS.CPP new file mode 100644 index 0000000..cd25f3f --- /dev/null +++ b/CODE/CREDITS.CPP @@ -0,0 +1,245 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CREDITS.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CREDITS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 17, 1994 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CreditClass::AI -- Handles updating the credit display. * + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * * + * This is the constructor for the credit class object. It merely sets the credit display * + * state to null. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +CreditClass::CreditClass(void) : + Credits(0), + Current(0), + IsToRedraw(false), + IsUp(false), + IsAudible(false), + Countdown(0) +{ +} + + +/*********************************************************************************************** + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * * + * This routine should be called whenever the main game screen is to be updated. It will * + * check to see if the credit display should be redrawn. If so, it will redraw it. * + * * + * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw * + * flag is set? This is typically the case when the screen needs to be * + * redrawn from scratch. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +//#define XX (320-120) +//#define WW 50 +void CreditClass::Graphic_Logic(bool forced) +{ + if (forced || IsToRedraw) { + BStart(BENCH_TABS); + + int xx = SeenBuff.Get_Width() - (120 * RESFACTOR); + + /* + ** Adjust the credits display to be above the sidebar for 640x400 + */ +#ifdef WIN32 + xx += 80 * RESFACTOR; +#endif + + /* + ** Play a sound effect when the money display changes, but only if a sound + ** effect was requested. + */ + if (IsAudible) { + if (IsUp) { + Sound_Effect(VOC_MONEY_UP, fixed(1, 2)); + } else { + Sound_Effect(VOC_MONEY_DOWN, fixed(1, 2)); + } + } + + /* + ** Display the new current value. + */ + TabClass::Draw_Credits_Tab(); +#ifdef WIN32 + Fancy_Text_Print("%ld", xx, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL, Current); +#else + Fancy_Text_Print("%ld", xx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, Current); +#endif //WIN32 + + if (Scen.MissionTimer.Is_Active()) { + long secs = Scen.MissionTimer / TICKS_PER_SECOND; + long mins = secs / 60; + long hours = mins / 60; + secs %= 60; + mins %= 60; + + /* + ** Speak mission timer reminders. + */ + VoxType vox = VOX_NONE; + if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; + if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; + if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; + if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; + if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; + if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; + if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; + if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; + if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; + if (vox != VOX_NONE) { + Speak(vox); + Map.FlasherTimer = 7; + } + +#ifdef WIN32 + if (hours) { + Fancy_Text_Print(TXT_TIME_FORMAT_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, hours, mins, secs); + } else { + Fancy_Text_Print(TXT_TIME_FORMAT_NO_HOURS, 200 * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, mins, secs); + } +#else + if (hours) { + Fancy_Text_Print("%02d:%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, hours, mins, secs); + } else { + Fancy_Text_Print("%02d:%02d", 120 * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_NOSHADOW|TPF_6PT_GRAD|TPF_CENTER|TPF_BRIGHT_COLOR, mins, secs); + } +#endif //WIN32 + } + + IsToRedraw = false; + IsAudible = false; + BEnd(BENCH_TABS); + } +} + + +/*********************************************************************************************** + * CreditClass::AI -- Handles updating the credit display. * + * * + * This routine handles the logic that controls the rate of credit change in the credit * + * display. It doesn't actually redraw the credit display, but will flag it to be redrawn * + * if it detects that a change is to occur. * + * * + * INPUT: forced -- Should the credit display immediately reflect the current credit * + * total for the player? This is usually desired when initially loading * + * a scenario or saved game. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +void CreditClass::AI(bool forced) +{ + static int _last = 0; + + if (!forced && Frame == _last) return; + _last = Frame; + + Credits = PlayerPtr->Available_Money(); + + /* + ** Make sure that the credit counter doesn't drop below zero. + */ + Credits = max(Credits, 0L); + + if (Scen.MissionTimer.Is_Active() || Scen.MissionTimer) { + IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (Current == Credits) return; + + if (forced) { + IsAudible = false; + Current = Credits; + } else { + + if (Countdown) Countdown--; + if (Countdown) return; + + /* + ** Determine the amount to change the display toward the + ** desired value. + */ + int adder = Credits - Current; + + if (adder > 0) { + Countdown = 1; + } else { + Countdown = 3; + } + + adder = ABS(adder); + adder >>= 3; +// adder >>= 4; +// adder >>= 5; + adder = Bound(adder, 1, 71+72); + if (Current > Credits) adder = -adder; + Current += adder; + if (Current-adder != Current) { + IsAudible = true; + IsUp = (adder > 0); + } + } + IsToRedraw = true; + Map.Flag_To_Redraw(false); +} diff --git a/CODE/CREDITS.H b/CODE/CREDITS.H new file mode 100644 index 0000000..c79aa17 --- /dev/null +++ b/CODE/CREDITS.H @@ -0,0 +1,73 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CREDITS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CREDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREDITS_H +#define CREDITS_H + +/**************************************************************************** +** The animating credit counter display is controlled by this class. +*/ +class CreditClass { + public: + long Credits; // Value of credits trying to update display to. + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CreditClass(void); + CreditClass(NoInitClass const & ) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Update(bool forced=false, bool redraw=false); + + void Graphic_Logic(bool forced=false); + void AI(bool forced=false); + + long Current; // Credit value currently displayed. + + unsigned IsToRedraw:1; + unsigned IsUp:1; + unsigned IsAudible:1; + + private: + int Countdown; // Delay between ticks. +}; + +#endif + diff --git a/CODE/CREW.CPP b/CODE/CREW.CPP new file mode 100644 index 0000000..c9fe7e9 --- /dev/null +++ b/CODE/CREW.CPP @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CREW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CREW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/CODE/CREW.H b/CODE/CREW.H new file mode 100644 index 0000000..e8470fa --- /dev/null +++ b/CODE/CREW.H @@ -0,0 +1,70 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CREW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CREW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREW_H +#define CREW_H + + +/**************************************************************************** +** This class handles the basic crew logic. This includes hero tracking, +** crew bail-out, and attached object logic. +*/ +class CrewClass +{ + public: + /* + ** This keeps track of the number of "kills" the unit as accumulated. + ** When it reaches a certain point, the unit improves. + */ + unsigned short Kills; + + /* + ** Constructors, Destructors, and overloaded operators. + */ + CrewClass(void) : Kills(0) {}; + CrewClass(NoInitClass const &) {}; + ~CrewClass(void) {}; + + int Made_A_Kill(void) { + Kills++; + return(Kills); + }; + + private: +}; + +#endif diff --git a/CODE/CSTRAW.CPP b/CODE/CSTRAW.CPP new file mode 100644 index 0000000..250c188 --- /dev/null +++ b/CODE/CSTRAW.CPP @@ -0,0 +1,100 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CacheStraw::Get -- Fetch data from the data source. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "cstraw.h" +#include + + +/*********************************************************************************************** + * CacheStraw::Get -- Fetch data from the data source. * + * * + * This will supply the data quantity requested. It performs a regulating influence on the * + * data requests passed through it. The data is requested from the next straw in the * + * chain such that the data stream is requested in chunks. This serves to lessen the * + * impact of multiple small data requests. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * slen -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the number of data bytes stored into the buffer specified. If this * + * number is less than that requested, it indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1996 JLB : Created. * + *=============================================================================================*/ +int CacheStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + + /* + ** Keep processing the data request until there is no more data to supply or the request + ** has been fulfilled. + */ + while (slen > 0) { + + /* + ** First try to fetch the data from data previously loaded into the buffer. + */ + if (Length > 0) { + int tocopy = (Length < slen) ? Length : slen; + memmove(source, ((char *)BufferPtr.Get_Buffer()) + Index, tocopy); + slen -= tocopy; + Index += tocopy; + total += tocopy; + Length -= tocopy; + source = (char*)source + tocopy; + } + if (slen == 0) break; + + /* + ** Since there is more to be fulfilled yet the holding buffer is empty, + ** refill the buffer with a fresh block of data from the source. + */ + Length = Straw::Get(BufferPtr, BufferPtr.Get_Size()); + Index = 0; + if (Length == 0) break; + } + } + return(total); +} diff --git a/CODE/CSTRAW.H b/CODE/CSTRAW.H new file mode 100644 index 0000000..8d7c65b --- /dev/null +++ b/CODE/CSTRAW.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/CSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CSTRAW_H +#define CSTRAW_H + +#include "straw.h" +#include "buff.h" + +/* +** This class handles transfer of data by perform regulated requests for data from the next +** class in the chain. It performs no translation on the data. By using this segment in a +** straw chain, data throughput can be regulated. This can yield great performance increases +** when dealing with a file source. +*/ +class CacheStraw : public Straw +{ + public: + CacheStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0), Length(0) {} + CacheStraw(int length=4096) : BufferPtr(length), Index(0), Length(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; + int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + CacheStraw(CacheStraw & rvalue); + CacheStraw & operator = (CacheStraw const & pipe); +}; + + + +#endif + diff --git a/CODE/CWSTUB.C b/CODE/CWSTUB.C new file mode 100644 index 0000000..2af8ae0 --- /dev/null +++ b/CODE/CWSTUB.C @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +#include +#include +#include +#include +#include + +char *dos4g_path() +{ + static char *paths_to_check[] = { + "DOS4GPATH", + "PATH" + }; + static char fullpath[80]; + char *dos4gpath; + int i; + + /* If DOS4GPATH points to an executable file name, don't bother + searching any paths for DOS4GW.EXE. + */ + if (dos4gpath = getenv("DOS4GPATH")) { + strlwr(strcpy(fullpath, dos4gpath)); + if (strstr(fullpath, ".exe")) { + return(fullpath); + } + } + for( i = 0; i < sizeof(paths_to_check) / sizeof(paths_to_check[0]); i++ ) { + _searchenv("dos4gw.exe", paths_to_check[i], fullpath); + if (fullpath[0]) { + return( &fullpath ); + } + } + return("dos4gw.exe"); +} + +main( int argc, char *argv[] ) +{ + char *av[4]; + auto char cmdline[128]; + + av[0] = dos4g_path(); /* Locate the DOS/4GW loader */ + av[1] = argv[0]; /* name of executable to run */ + av[2] = getcmd(cmdline); /* command line */ + av[3] = NULL; /* end of list */ +#ifdef VMM + putenv("DOS4GVM=MINMEM#2000 MAXMEM#16000 SWAPMIN#4096 SWAPINC#1024 VIRTUALSIZE#10000 SWAPFILE#CONQUER.SWP DELETESWAP @CONQUER.VMC"); +#endif +#ifdef QUIET + putenv("DOS4G=QUIET"); /* disables DOS/4GW Copyright banner */ +#endif + execvp(av[0], av); + perror(av[0]); + exit(1); /* indicate error */ +} diff --git a/CODE/DDE.CPP b/CODE/DDE.CPP new file mode 100644 index 0000000..ec6d293 --- /dev/null +++ b/CODE/DDE.CPP @@ -0,0 +1,452 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Dynamic Data Encapsulation * + * * + * File Name : DDE.CPP * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Instance_Class::InstanceClass -- class constructor * + * Instance_Class::InstanceClass -- class destructor * + * Instance_Class::Enable_Callback -- enables local processing of pokes * + * Instance_Class::Register_Servers -- registers a local DDE DNS service * + * Instance_Class::Cleanup_App -- currently does nothing * + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * Instance_Class::Open_Poke_Connection -- pokes some data to server * + * Instance_Class::Close_Poke_Connectionp -- closes connection to remote * + * Instance_Class::Poke_Server -- sends a chunk of data to remote * + * Instance_Class::dde_callback -- processes DDE transactions * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 +#include +#include "dde.h" + +/*************************************************************************** + * These are static members of Instance_Class + *=========================================================================*/ + +static DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize +static BOOL Instance_Class::process_pokes; // controls response to pokes +static char Instance_Class::ascii_name[32]; // name of server + +static BOOL CALLBACK (*Instance_Class::callback) ( + LPBYTE pointer, // pointer to received data + long length // length of received data or advisory flag + ) = NULL; + +/*************************************************************************** + * Instance_Class::InstanceClass -- class constructor * + * * + * INPUT: * + * name1 null terminated ASCII client name * + * name1 null terminated ASCII server name * + * * + * OUTPUT: * + * dde_error = TRUE if error occurs when initializing DDE * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 ) +{ + + dde_error = FALSE; // no errors + process_pokes = FALSE; // disable pokes in callback + + id_inst = 0; // set to 0 for first time through + conv_handle = 0; // conversation handle reset + + lstrcpy( ascii_name, name1 ); // keep a record of ASCII name + + if ( DdeInitialize( + (LPDWORD) &id_inst, // instance identifier + dde_callback, + APPCLASS_STANDARD | // filter server messages + CBF_FAIL_SELFCONNECTIONS, // prevent from connecting with self + 0) != DMLERR_NO_ERROR) { // reserved + dde_error = TRUE; // flag an error + } + + local_name = DdeCreateStringHandle( + id_inst, // instance identifier + name1, // string to register + CP_WINANSI); // Windows ANSI code page + + remote_name = DdeCreateStringHandle( + id_inst, // instance identifier + name2, // string to register + CP_WINANSI); // Windows ANSI code page + + poke_topic = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE TOPIC", // System topic + CP_WINANSI); // Windows ANSI code page + + poke_item = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE ITEM", // System topic + CP_WINANSI); // Windows ANSI code page + + system_topic = DdeCreateStringHandle( + id_inst, // instance identifier + SZDDESYS_TOPIC, // System topic + CP_WINANSI); // Windows ANSI code page + +} + +/*************************************************************************** + * Instance_Class::~Instance_Class -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::~Instance_Class() +{ + DdeUninitialize( id_inst ); +} + +/*************************************************************************** + * Instance_Class::Enable_Callback -- enables user callback * + * * + * INPUT: * + * TRUE = enable poke processing * + * FALSE = disable poke processing * + * * + * OUTPUT: * + * echos the input * + * * + * WARNINGS: * + * user callback must be explicitly enabled. Disbabled by default. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Enable_Callback( BOOL flag ) // enable or disable callback +{ + return (process_pokes = flag); + +} + +/*************************************************************************** + * Instance_Class::Register_Server -- registers a local DDE DNS service * + * * + * INPUT: * + * BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl * + * * + * OUTPUT: * + * TRUE == success * + * FALSE == failed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) ) +{ + + if (DdeNameService( id_inst, local_name, 0L, DNS_REGISTER ) != 0L) { + callback = callback_fnc; + return ( TRUE ); + } else { + return ( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * * + * INPUT: * + * name = HSZ string handle of server name. * + * * + * OUTPUT: * + * TRUE == successfully connected to remote * + * FALSE == failed to connect * + * * + * WARNINGS: * + * - Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * - Disconects before exiting. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Test_Server_Running( HSZ name ) +{ + + if( Open_Poke_Connection( name ) == TRUE) { + Close_Poke_Connection(); + return( TRUE ); + } else { + return( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Open_Poke_Connection -- open a connection to server * + * * + * INPUT: * + * name = HSZ server name. * + * * + * OUTPUT: * + * TRUE == successfully opened connection * + * FALSE == failed to connect * + * * + * WARNINGS: * + * Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Open_Poke_Connection( HSZ name ) +{ + conv_handle = DdeConnect( + id_inst, // instance identifier + name, // service name string handle + poke_topic, // topic string handle + (PCONVCONTEXT) NULL);// use default context + + if (conv_handle == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +/*************************************************************************** + * Instance_Class::Close_Poke_Connection -- closes poke connection * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TRUE == successfully closed connection * + * FALSE == failed to close connection for some reason * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Close_Poke_Connection( void ) +{ + if( conv_handle ) { + HCONV temp_handle = conv_handle; + conv_handle = NULL; + return( DdeDisconnect( temp_handle )); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::Poke_Server -- pokes some data to server * + * * + * INPUT: * + * poke_data points to data to send to remote * + * poke_length length of buffer to send * + * * + * OUTPUT: * + * TRUE == successfully poked the data * + * FALSE == failed to connect * + * * + * WARNINGS: * + * has a 3 second timeout (change POKE_TIMEOUT, in milliseconds) * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +#define POKE_TIMEOUT 60*1000 // 60 sec timeout + +BOOL Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length ) +{ + + if( DdeClientTransaction( + + poke_data, // address of data to pass to server + poke_length, // length of data + conv_handle, // handle of conversation + poke_topic, // handle of item name string + CF_TEXT, // no special clipboard data format + XTYP_POKE, // transaction type + POKE_TIMEOUT, // time-out duration (millisecs) + (LPDWORD) NULL // address of transaction result (don't check) + ) == 0) { + + return( FALSE); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::dde_callback -- callback dde event handler * + * * + * INPUT: * + * dde_event transaction type * + * uFmt clipboard data format * + * hconv handle of the conversation * + * hsz1 handle of a string * + * hsz2 handle of a string * + * hdata handle of a global memory object * + * dwData1 transaction-specific data * + * dwData2 transaction-specific data * + * * + * OUTPUT: * + * context specific HDDEDATA object * + * * + * WARNINGS: * + * NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +HDDEDATA CALLBACK Instance_Class::dde_callback( + + UINT dde_event, // transaction type + UINT uFmt, // clipboard data format + HCONV , // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD , // transaction-specific data + DWORD // transaction-specific data + ) +{ + + + if (!Instance_Class::callback){ + return (HDDEDATA) NULL; + } + + switch ( dde_event ) { + + case XTYP_REGISTER: + case XTYP_UNREGISTER: + + return (HDDEDATA) NULL; + + case XTYP_ADVDATA: + return (HDDEDATA) DDE_FACK; + + case XTYP_XACT_COMPLETE: + + return (HDDEDATA) NULL; + + case XTYP_DISCONNECT: + + Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT); + return (HDDEDATA) NULL; + + case XTYP_CONNECT: { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, Instance_Class::ascii_name)) { + return (HDDEDATA) NULL; + } + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) NULL; + } + + Instance_Class::callback( NULL, DDE_ADVISE_CONNECT); + return (HDDEDATA) TRUE; + } + + case XTYP_POKE: + + if (Instance_Class::process_pokes == FALSE ) { + return (HDDEDATA) DDE_FNOTPROCESSED; // processing disabled + } else { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } else if (uFmt == CF_TEXT) { // make sure it's CF_TEXT + + BOOL processed; + BYTE FAR *pdata; + DWORD dw_length; + + if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } + + processed = Instance_Class::callback((LPBYTE) pdata, dw_length); + + DdeUnaccessData( hdata ); + + if (processed == TRUE) { + return (HDDEDATA) DDE_FACK; + } else { + return (HDDEDATA) NULL; + } + + } + } + + default: + return (HDDEDATA) NULL; + } +} + +#endif //WIN32 diff --git a/CODE/DDE.H b/CODE/DDE.H new file mode 100644 index 0000000..1554a9a --- /dev/null +++ b/CODE/DDE.H @@ -0,0 +1,175 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Dynamic Data Encapsulation * + * * + * File Name : DDE.H * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * * + * This is the DDE (Instance_Class) which provides a simple CLIENT/SERVER * + * DDE model for data transactions between Windows applications. * + * This is a fairly naieve implementation allowing only one client/server * + * per Instance_Class object. * + * * + * Typical uses for this class are: * + * * + * i. Robust verification of whether an application is running * + * ii. Data transfer between applications * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* +***************************** Class defines ***************************** +*/ + +#ifndef __DDE_H +#define __DDE_H + +#define DDE_ADVISE_CONNECT -1 // advisory "client has connected" +#define DDE_ADVISE_DISCONNECT -2 // advisory "client has disconnected" + +/* +***************************** Class Declaration ***************************** +*/ + +class Instance_Class { + + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + + /*..................................................................... + Constructor: + - takes null terminated ASCII strings names for client and server + .....................................................................*/ + + Instance_Class( // constructor + LPSTR, // null terminated local sever name string + LPSTR // null terminated remote server name string + ); + + /*..................................................................... + Destructor: + .....................................................................*/ + ~Instance_Class(void); // the destructor + + /*..................................................................... + Send data routine: + - sends an unsolicited packet of data to the remote server + .....................................................................*/ + BOOL Poke_Server( LPBYTE, DWORD); + + /*..................................................................... + Send data routine: + - sets up DNS for the server and registers a user callback to handle + incoming data + .....................................................................*/ + BOOL Register_Server( BOOL CALLBACK (*)(LPBYTE, long)); + + /*..................................................................... + Does a trial connect to the remote server. + - used to determine whether server is alive or not (and thus running) + .....................................................................*/ + BOOL Test_Server_Running( HSZ ); + + /*..................................................................... + Enables user callback (disabled by default) + .....................................................................*/ + BOOL Enable_Callback( BOOL ); // enable or disable callback + + /*..................................................................... + Open a connection for sending data to remote server + .....................................................................*/ + BOOL Open_Poke_Connection( HSZ ); + + /*..................................................................... + Close connection with remote server + .....................................................................*/ + BOOL Close_Poke_Connection( void ); + + // + // static members + // + + /*..................................................................... + User callback - called upon receipt of incoming data (static member!) + .....................................................................*/ + static BOOL CALLBACK (*callback) ( + + LPBYTE pointer, // pointer to received data + long length // if >0 length of received data + // if <0 + // -1 == client connect detected + // -2 == client disconnect detected + ); + + /*..................................................................... + DDE callback, called when DDEML has an event for us + .....................................................................*/ + static HDDEDATA CALLBACK dde_callback( + + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); + HANDLE instance; // this application's instance + HWND hwnd; // valid window handle + + /*..................................................................... + member variables + .....................................................................*/ + + static DWORD id_inst; // instance identifier set by DdeInitialize + static BOOL process_pokes; // controls response to pokes + static char ascii_name[32]; // name of server + + // + // non-static member variables + // + + HSZ remote_name; // string handle for remote server name + HSZ local_name; // string handle for local server name + HSZ system_topic; // string handle for the "system" topic + HSZ poke_topic; // string handle for poking data to server topic + HSZ poke_item; // string handle for poking data to server item + + HCONV conv_handle; // conversation handle + BOOL dde_error; // error flag + +}; + +#endif + + \ No newline at end of file diff --git a/CODE/DEBUG.CPP b/CODE/DEBUG.CPP new file mode 100644 index 0000000..2631bf7 --- /dev/null +++ b/CODE/DEBUG.CPP @@ -0,0 +1,560 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Self_Regulate -- Regulates the logic timer to result in smooth animation. * + * Debug_Key -- Debug mode keyboard processing. * + * Bench_Time -- Convert benchmark timer into descriptive string. * + * Benchmarks -- Display the performance tracking benchmarks. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#include + + +#ifdef CHEAT_KEYS + + +static CDTimerClass DebugTimer; + +int VortexFrame = -1; + +/*********************************************************************************************** + * Debug_Key -- Debug mode keyboard processing. * + * * + * If debugging is enabled, then this routine will be called for every keystroke that the * + * game doesn't recognize. These extra keys usually perform some debugging function. * + * * + * INPUT: input -- The key code that was pressed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Debug_Key(unsigned input) +{ + static int map_x = -1; + static int map_y = -1; + static int map_width = -1; + static int map_height = -1; + + if (!input || input & KN_BUTTON) return; + + /* + ** Processing of normal keystrokes. + */ + if (Debug_Flag) { + + switch (input) { + + case KN_BACKSPACE: + + if (ChronalVortex.Is_Active()) { + ChronalVortex.Disappear(); + } else { + int xxxx = Get_Mouse_X() + Map.TacPixelX; + int yyyy = Get_Mouse_Y() + Map.TacPixelY; + CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy); + ChronalVortex.Appear ( Cell_Coord (cell) ); + } + break; + +#ifdef WIN32 + case KN_J: + Debug_MotionCapture = true; + break; + +#ifdef OBSOLETE + case KN_K: + /* + ** time to create a screen shot using the PCX code (if it works) + */ + if (!Debug_MotionCapture) { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + CDFileClass file; + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + sprintf(filename, "scrsht%02d.pcx", lp); + file.Set_Name(filename); + if (!file.Is_Available()) break; + } + + file.Cache(200000); + Write_PCX_File(file, temp_page, & GamePalette); + Sound_Effect(VOC_BEEP); + } + break; +#endif +#endif + + case KN_P: + { + for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) { + PlayerPtr->SuperWeapon[spc].Enable(true, true); + PlayerPtr->SuperWeapon[spc].Forced_Charge(true); + Map.Add(RTTI_SPECIAL, spc); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + case KN_I: + { + Map.Flash_Power(); + Map.Flash_Money(); + } + break; + + case KN_O: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_B: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_GRAVE: + { + WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE); + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + int damage = 1000; + new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord); + Explosion_Damage(coord, damage, NULL, warhead); + } + break; + + case KN_C: + Debug_Cheat = (Debug_Cheat == false); + PlayerPtr->IsRecalcNeeded = true; + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + if (!ScenarioInit) { + Map.Recalc(); + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + } + break; + + case (int)KN_Z|(int)KN_ALT_BIT: + if (map_x == -1) { + map_x = Map.MapCellX; + map_y = Map.MapCellY; + map_width = Map.MapCellWidth; + map_height = Map.MapCellHeight; + Map.MapCellX = 1; + Map.MapCellY = 1; + Map.MapCellWidth = MAP_CELL_W-2; + Map.MapCellHeight = MAP_CELL_H-2; + } else { + Map.MapCellX = map_x; + Map.MapCellY = map_y; + Map.MapCellWidth = map_width; + Map.MapCellHeight = map_height; + map_x = -1; + map_y = -1; + map_width = -1; + map_height = -1; + } + break; + + case KN_M: + if (Debug_Flag) { + if (MonoClass::Is_Enabled()) { + MonoClass::Disable(); + } else { + MonoClass::Enable(); + } + } + break; + + case (int)KN_W|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Win(); + break; + + case (int)KN_L|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Lose(); + break; + + case KN_DELETE: + if (CurrentObject.Count()) { + Map.Recalc(); + //CurrentObject[0]->Detach_All(); + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)CurrentObject[0])->Sell_Back(1); + } else { + ObjectClass * object = CurrentObject[0]; + object->Unselect(); + object->Limbo(); + delete object; + } + } + break; + + case (int)KN_DELETE|(int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + Map.Recalc(); + int damage = 50; + CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); + } + break; + + case KN_INSERT: + if (CurrentObject.Count()) { + Map.PendingObject = &CurrentObject[0]->Class_Of(); + if (Map.PendingObject) { + Map.PendingHouse = CurrentObject[0]->Owner(); + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + } + } + } + break; + + case KN_LBRACKET: + case KN_F11: + if (MonoPage == DMONO_FIRST) { + MonoPage = DMonoType(DMONO_COUNT-1); + } else { + MonoPage = DMonoType(MonoPage - 1); + } + DebugTimer = 0; + break; + + case KN_RBRACKET: + case KN_F12: + MonoPage = DMonoType(MonoPage + 1); + if (MonoPage == DMONO_COUNT) { + MonoPage = DMONO_FIRST; + } + DebugTimer = 0; + break; + + case KN_V: + case KN_F3: + Debug_Icon = (Debug_Icon == false); + Map.Flag_To_Redraw(true); + break; + + /* + ** Reveal entire map to player. + */ +// case KN_F4: +// if (Session.Type == GAME_NORMAL) { +// Debug_Unshroud = (Debug_Unshroud == false); +// Map.Flag_To_Redraw(true); +// } +// break; + + /* + ** Shows sight and fire range in the form of circles emanating from the currently + ** selected unit. The white circle is for sight range, the red circle is for + ** fire range. + */ + case KN_F7: + if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { + TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); + int sight = ((int)ttype.SightRange)<<8; + int weapon = 0; + if (ttype.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->Range; + Set_Logic_Page(SeenBuff); + COORDINATE center = CurrentObject[0]->Center_Coord(); + COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); + + for (int r = 0; r < 255; r += 10) { + int x,y,x1,y1; + DirType r1 = (DirType)r; + DirType r2 = (DirType)((r+10) & 0xFF); + + if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); + } + if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); + } + } + } + break; + + case ((int)KN_F4 | (int)KN_CTRL_BIT): + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * Bench_Time -- Convert benchmark timer into descriptive string. * + * * + * This routine will take the values of the benchmark timer specified and build a string * + * that displays the average time each event consumed as well as the ranking of how much * + * time that event took (total) during the tracking duration (one second?). * + * * + * INPUT: btype -- The benchmark to convert to a descriptive string. * + * * + * OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. * + * * + * WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid * + * until the next time that this routine is called. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static char const * Bench_Time(BenchType btype) +{ + static char buffer[32]; + + int rootcount = Benches[BENCH_GAME_FRAME].Count(); + if (rootcount == 0) rootcount = 1; + int roottime = Benches[BENCH_GAME_FRAME].Value(); + int count = Benches[btype].Count(); + int time = Benches[btype].Value(); + if (count > 0 && count * time > roottime * rootcount) time = roottime / count; + int percent = 0; + if (roottime != 0 && rootcount != 0) { + percent = ((count * time) * 99) / (roottime * rootcount); + } + if (percent > 99) percent = 99; + sprintf(buffer, "%-2d%% %7d", percent, time); + return(buffer); +} + + +/*********************************************************************************************** + * Benchmarks -- Display the performance tracking benchmarks. * + * * + * This will display the benchmarks for the various processes that are being tracked. The * + * display will indicate the fraction that each process is consuming out of the entire * + * process time as well as the time consumed by each individual event. The total fraction * + * is useful for determing what should be optimized. The individual time is useful for * + * guaging the effectiveness of optimization changes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the display will use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static void Benchmarks(MonoClass * mono) +{ + static bool _first = true; + if (_first) { + _first = false; + mono->Clear(); + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_PERFORMANCE)); + if (Benches == NULL) { + mono->Set_Cursor(20, 15); + mono->Printf(TXT_NO_PENTIUM); + } + } + + if (Benches != NULL) { + mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH)); + mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT)); + mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI)); + mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP)); + mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT)); + mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL)); + mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL)); + mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION)); + + mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL)); + mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS)); + mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS)); + + mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE)); + + mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER)); + mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR)); + mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR)); + mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL)); + mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER)); + mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD)); + mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS)); + mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY)); + + mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value()); + mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value()); + + for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) { + if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset(); + } + } +} + + +/*********************************************************************************************** + * Self_Regulate -- Regulates the logic timer to result in smooth animation * + * * + * The self regulation process checks the number of frames displayed * + * per second and from this determines the amount of time to devote * + * to internal logic processing. By adjusting the time allotted to * + * internal processing, smooth animation can be maintained. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: In order for this routine to work properly it MUST be * + * called every display loop. * + * * + * HISTORY: * + * 07/31/1991 JLB : Created. * + * 07/05/1994 JLB : Handles new monochrome system. * + *=============================================================================================*/ +#define UPDATE_INTERVAL TIMER_SECOND +void Self_Regulate(void) +{ + static ObjectClass * _lastobject = 0; + static bool _first=true; + + if (DebugTimer == 0) { + DebugTimer = UPDATE_INTERVAL; + + if (MonoClass::Is_Enabled()) { + if (_first) { + _first = false; + for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) { + MonoArray[index].Clear(); + } + } + + /* + ** Always update the stress tracking mono display even if it + ** currently isn't visible. + */ + Logic.Debug_Dump(&MonoArray[DMONO_STRESS]); + + MonoClass * mono = &MonoArray[MonoPage]; + mono->Set_Default_Attribute(MonoClass::NORMAL); + mono->View(); + + switch (MonoPage) { + case DMONO_EVENTS: + Benchmarks(mono); + break; + + case DMONO_OBJECT: + mono->Clear(); + + /* + ** Display the status of the currently selected object. + */ + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject) { + _lastobject->Debug_Dump(mono); + } + break; + + case DMONO_STRESS: +#ifdef OBSOLETE + mono->Set_Cursor(0, 20); + mono->Printf( + "Heap size:%10ld \r" + "Largest: %10ld \r" + "Ttl Free: %10ld \r" + "Frag: %10ld \r", + Heap_Size(MEM_NORMAL), + Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) + ); +#endif + break; + + case DMONO_HOUSE: + mono->Clear(); + + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject && _lastobject->Is_Techno()) { + ((TechnoClass *)_lastobject)->House->Debug_Dump(mono); + } + break; + + default: + break; + } + + mono->Set_Cursor(0, 0); + } + } +} +#endif diff --git a/CODE/DEBUG.H b/CODE/DEBUG.H new file mode 100644 index 0000000..b18ada0 --- /dev/null +++ b/CODE/DEBUG.H @@ -0,0 +1,61 @@ +/* +** 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 . +*/ + +#define TXT_CLEAR_MAP 0x3e8 // Clear the map +#define TXT_INHERIT_MAP 0x3e9 // Inherit previous map +#define TXT_SPECIAL_OPTIONS 0x3ea // Select Special Options +#define TXT_VISIBLE_TARGET 0x3eb // Targeting flash visible to +#define TXT_TREE_TARGET 0x3ec // Allow targeting of trees. +#define TXT_MCV_DEPLOY 0x3ed // Allow undeploy of +#define TXT_SMART_DEFENCE 0x3ee // Employ smarter self defense +#define TXT_SLOW_BUILD 0x3ef // Moderate production speed. +#define TXT_THREE_POINT 0x3f0 // Use three point turn logic. +#define TXT_TIBERIUM_GROWTH 0x3f1 // Ore will grow. +#define TXT_TIBERIUM_SPREAD 0x3f2 // Ore will spread. +#define TXT_ROAD_PIECES 0x3f3 // Disable building "bib" +#define TXT_SCATTER 0x3f4 // Allow running from +#define TXT_SHOW_NAMES 0x3f5 // Show true object names. +#define TXT_DEFENDER_ADVANTAGE 0x3f6 // Defender has the advantage. +#define TXT_SEPARATE_HELIPAD 0x3f7 // Allow separate helipad +#define TXT_PASSWORD_CAPTION 0x3f8 // Password Request +#define TXT_PASSWORD_MESSAGE 0x3f9 // Enter Red Alert access code +#define TXT_PASSWORD_ERROR 0x3fa // Access code error detected. +#define TXT_TRY_AGAIN 0x3fb // Try Again +#define TXT_MINE_AWARE 0x3fc // Friendly units avoid +#define TXT_TRIGGER_EDITOR 0x3fd // Trigger Editor +#define TXT_TRIGGER_JUST_EVENT 0x3fe // Just This Event +#define TXT_TRIGGER_AND 0x3ff // ... and ... +#define TXT_TRIGGER_OR 0x400 // ... or ... +#define TXT_TRIGGER_LINKED 0x401 // ... linked ... +#define TXT_TRIGGER_JUST_ACTION 0x402 // Just This Action +#define TXT_TEAM_EDIT 0x403 // Team Editor +#define TXT_SELLABLE 0x404 // Sellable +#define TXT_REBUILD 0x405 // Rebuild +#define TXT_SPEED_BUILD 0x406 // Building constructin time +#define TXT_SCENARIO_ERRORx 0x407 // Scenario authentication +#define TXT_DEBUG_STRESS 0x408 // ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄ +#define TXT_DEBUG_VEHICLE 0x409 // ÚFull +#define TXT_DEBUG_INFANTRY 0x40a // ÚFull +#define TXT_DEBUG_SHIP 0x40b // ÚFull +#define TXT_DEBUG_BUILDING 0x40c // ÚFull +#define TXT_DEBUG_PERFORMANCE 0x40d // Game Objects³ Drawing +#define TXT_DEBUG_AIRCRAFT 0x40e // ÚFull +#define TXT_DEBUG_HOUSE 0x40f // ÚFull Name:ÄÄÄÄÄÄÄÄÂAct +#define TXT_NO_PENTIUM 0x410 // **************************** +#define TXT_SIZE_MAP 0x411 // Size Map +#define TXT_TRUCK_CRATE 0x412 // Trucks drop crate when diff --git a/CODE/DEFINES.H b/CODE/DEFINES.H new file mode 100644 index 0000000..de0009b --- /dev/null +++ b/CODE/DEFINES.H @@ -0,0 +1,3555 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DEFINES.H 4 3/07/97 9:55a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DEFINES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef DEFINES_H +#define DEFINES_H + +/********************************************************************** +** Language control: define the desired language for this build. +*/ +//#define ENGLISH 1 +//#define FRENCH 1 +//#define GERMAN 1 +//#define SPAIN 1 (never used) +// - Language define is now passed in from the makefile. - + +/********************************************************************** +** Controls the nature of the game and its abilities. Only define +** one of these values. +** +** Internal version -- complete with scenario editor. +** Playtest version -- no editor but does have minimal cheat keys. +** Release version -- no editor or cheat keys -- all debugging info removed. +*/ +//#define INTERNAL_VERSION +//#define PLAYTEST_VERSION +#define RELEASE_VERSION + +/********************************************************************** +** ColinM +** Set this to enable dongle protection +*/ +//#define DONGLE + + +// Enable 640x400 VQ movie capability in WIN32 mode +#define MOVIE640 + + +//#if (GERMAN | FRENCH) +//#define BOGUSCD +//#endif + +#define FIXIT_SCORE_CRASH // Fixes score screen crash +#define FIXIT_MULTI_SAVE // Fixes multiplayer save/load +#define FIXIT_NO_COMP_ALLY // Prevent ally with computer +#define FIXIT_DESTNET // Fixes -destnet parameter in Win95 +#define FIXIT_RANDOM_GAME // Fixes random seed at start of multiplayer games +#define FIXIT_FORCE_CD // Forces correct CD load after scenario #1 +#define FIXIT_IP_CRASH // Fixes crash if internet game aborts too quickly +#define FIXIT_IP_STATS // Fixes so vessels show up in internet stat info +#define FIXIT_NAME_OVERRIDE // Allows changing of unit names +#define FIXIT_RADAR_JAMMED // Fixes unjamming by merely starting to build a radar facility +#define FIXIT_CAPTURE_BIB // Fixes so that if fake is captured, you still can't build off of it. +#define FIXIT_BASE_ANNOUNCE // Fixes so player controlled buildings count as base when attacked. +#define FIXIT_APTIVA_MODEM // Fixes crash with Aptiva modem. +#define FIXIT_FLAG_CHECK // Disable placing building over a flag. + +#define FIXIT_ANTS // Adds Ant Units + +#define FIXIT_CSII // Adds Aftermath CounterStrike II units +// ajw 9/28/98 - Note about FIXIT_CSII. Changes seem to have been made for Aftermath ("Counterstrike II") that: a) were +// bug fixes that should never be rolled back, b) change the nature of the game, at least in multi-player. This meant +// that the "Red Alert" executable ( == Counterstrike executable ) could no longer be built. Apparently, at the time, +// this was justified, as it was believed that no further patches to the RA executable would ever be necessary. +// Given that Denzil's DVD changes and my WOLAPI integration are essentially a patch, we've got a problem. +// We've decided to level the field and make sure every who gets or patches to the new version of Red Alert, CS, AM, (and +// their DVD equivalent(s)) will have the same executable. So we're assuming that all of the FIXIT_CSII changes are +// permanent (as, in fact, all prior FIXIT_'s are - makes me wonder why the old non-compiling code has to hang around +// forever), and fixing the code so that the assumption "this is an Aftermath game" is no longer hard-coded, but can +// change at runtime. (Which is what should have been done when Aftermath was created.) +// +#define FIXIT_CARRIER // Adds Aftermath aircraft carrier +#define FIXIT_PHASETRANSPORT // Adds Aftermath cloaking APC +// ajw - Discovered that engineer changing fields were specifically left out of aftrmath.ini, thus this has no effect. +// Engineer changes (and other game rule changes) are in mplayer.ini, which was loaded before aftermath-only mplayer games. +#define FIXIT_ENGINEER // Adds Engineer rules.ini overrides + +//#define FIXIT_FAST_LOAD // Enables faster INI loading + +// These fixes will cause the game to go out of sync. +//#define FIXIT_ENGINEER_CAPTURE // If building not allied, will still capture if engineer not allied with building. +//#define FIXIT_HELI_LANDING // Fixes so new helicopters land at free helipad +//#define FIXIT_MINE_PASSABLE // Fixes units not driving onto mines + +/* Turn on these changes for the 1.08 patch */ +#define FIXIT_PATCH_108 + +#ifdef FIXIT_PATCH_108 +#define STEVES_LOAD_OVERRIDE // Allows loading of CONQUER.ENG instead of from mix file. +#define FIXIT_DIFFICULTY // Fixes no difficulty level for CStrike missions +#define FIXIT_VERSION // Fixes version playability for 1.04, 1.07 & 1.08 +#define FIXIT_MODEM_LOAD_CRASH // Fixes crash after loading a modem game when names are the same +#define FIXIT_PHONELIST_CRASH // Fixes crash when clicking on an empty phonelist +#endif + +// Denotes changes made for version 3 - reunification of all existing versions and undoing of Aftermath divergence. - ajw +#define FIXIT_VERSION_3 +#define DVD + +// Define DVD to turn on RADVD additions/changes - Denzil +#ifdef DVD +//#define INTERNET_OFF +#define MPEGMOVIE +//#define MCIMPEG +#endif + +// Test to see if partial object drawing is any faster. +#define PARTIAL +//#define SORTDRAW + +/********************************************************************** +** If the scenario editor to to be active in this build then uncomment +** the following #define line. +*/ +#ifdef INTERNAL_VERSION +#define SCENARIO_EDITOR +#endif + + +/********************************************************************** +** This define enables the full set of cheat keys and special +** command line options. +*/ +#if defined(INTERNAL_VERSION) || defined(PLAYTEST_VERSION) +#define CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, the special Virgin limited cheat keys +** are enabled. This allows the "cheat" parameter and then only +** allows the ALT-W to win the mission. +*/ +#ifdef PLAYTEST_VERSION +#define VIRGIN_CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, then the network code will be enabled. +*/ +#define NETWORK +#define TIMING_FIX 1 + + +/********************************************************************** +** Define this to 1 to enable MPath-specific code. Do not define +** TEN at the same time. +*/ +#define MPATH 0 + + +/********************************************************************** +** Define this to 1 to enable TEN-specific code. Do not define +** MPATH at the same time. +*/ +#define TEN 0 + + +/********************************************************************** +** If this is defined, the DoList is "mirrored", for memory trasher +** detection. +*/ +#ifdef CHEAT_KEYS +//#define MIRROR_QUEUE +#endif + + +/********************************************************************** +** This define tells the Version Number class to use the date/time-based +** version numbering system. If this define is not set, the actual +** major/minor version numbers will be used. +*/ +//#define DEV_VERSION +//#define DEV_VER_NAME + + +/********************************************************************** +** This define enables a special additional foreign-version-number +** after the other version number, for display purposes only. +*/ +#if !defined(ENGLISH) +#define FOREIGN_VERSION +#define FOREIGN_VERSION_NUMBER 7 +#endif + + +/********************************************************************** +** This is the multiplier factor to convert low resution coordinates +** into their actual resolution counterparts. +*/ +#ifdef WIN32 +#define RESFACTOR 2 +#else +//#undef SCENARIO_EDITOR +#define RESFACTOR 1 +#endif + + +#define SIDEBAR_WID 80 + + +/********************************************************************** +** Optional parameter control for special options. +*/ + +/* +** Enable the set of limited cheat key options. +*/ +#ifdef VIRGIN_CHEAT_KEYS +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif + +/* +** Enable the full set of cheat key options. +*/ +#ifdef CHEAT_KEYS +#ifndef PARM_PLAYTEST +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif +#endif + + +#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL" + + +// +// Allow normal game play in the MPath version +// +#if(MPATH) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +// +// Allow normal game play in the TEN version +// +#if(TEN) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +/********************************************************************** +** Defines for verifying free disk space +*/ +#define INIT_FREE_DISK_SPACE 8388608 +#define SAVE_GAME_DISK_SPACE (INIT_FREE_DISK_SPACE - (1024*4096)) +//#define SAVE_GAME_DISK_SPACE 100000 + + +/********************************************************************** +** This is the complete list of VQs allowed to be played in the game. +*/ +typedef enum VQType { + VQ_NONE=-1, + VQ_AAGUN, + VQ_MIG, + VQ_SFROZEN, + VQ_AIRFIELD, + VQ_BATTLE, + VQ_BMAP, + VQ_BOMBRUN, + VQ_DPTHCHRG, + VQ_GRVESTNE, + VQ_MONTPASS, + VQ_MTNKFACT, + VQ_CRONTEST, + VQ_OILDRUM, + VQ_ALLYEND, + VQ_RADRRAID, + VQ_SHIPYARD, + VQ_SHORBOMB, + VQ_SITDUCK, + VQ_SLNTSRVC, + VQ_SNOWBASE, + VQ_EXECUTE, + VQ_TITLE, // Low res. + VQ_NUKESTOK, + VQ_V2ROCKET, + VQ_SEARCH, + VQ_BINOC, + VQ_ELEVATOR, + VQ_FROZEN, + VQ_MCV, + VQ_SHIPSINK, + VQ_SOVMCV, + VQ_TRINITY, + VQ_ALLYMORF, + VQ_APCESCPE, + VQ_BRDGTILT, + VQ_CRONFAIL, + VQ_STRAFE, + VQ_DESTROYR, + VQ_DOUBLE, + VQ_FLARE, + VQ_SNSTRAFE, + VQ_LANDING, + VQ_ONTHPRWL, + VQ_OVERRUN, + VQ_SNOWBOMB, + VQ_SOVCEMET, + VQ_TAKE_OFF, + VQ_TESLA, + VQ_SOVIET8, + VQ_SPOTTER, + VQ_SCENE1, + VQ_SCENE2, + VQ_SCENE4, + VQ_SOVFINAL, + VQ_ASSESS, + VQ_SOVIET10, + VQ_DUD, + VQ_MCV_LAND, + VQ_MCVBRDGE, + VQ_PERISCOP, + VQ_SHORBOM1, + VQ_SHORBOM2, + VQ_SOVBATL, + VQ_SOVTSTAR, + VQ_AFTRMATH, + VQ_SOVIET11, + VQ_MASASSLT, + VQ_REDINTRO, // High res + VQ_SOVIET1, + VQ_SOVIET2, + VQ_SOVIET3, + VQ_SOVIET4, + VQ_SOVIET5, + VQ_SOVIET6, + VQ_SOVIET7, + VQ_INTRO_MOVIE, + VQ_AVERTED, + VQ_COUNTDWN, + VQ_MOVINGIN, + VQ_ALLIED10, + VQ_ALLIED12, + VQ_ALLIED5, + VQ_ALLIED6, + VQ_ALLIED8, + VQ_TANYA1, + VQ_TANYA2, + VQ_ALLY10B, + VQ_ALLY11, + VQ_ALLY14, + VQ_ALLY9, + VQ_SPY, + VQ_TOOFAR, + VQ_SOVIET12, + VQ_SOVIET13, + VQ_SOVIET9, + VQ_BEACHEAD, + VQ_SOVIET14, + VQ_SIZZLE, + VQ_SIZZLE2, + VQ_ANTEND, + VQ_ANTINTRO, + + VQ_COUNT, + VQ_FIRST=0 +} VQType; + + +/********************************************************************** +** These enumerations are used to implement RTTI. The target system +** uses these and thus there can be no more RTTI types than can fit +** in the exponent of a target value. +*/ +typedef enum RTTIType { + RTTI_NONE=0, + RTTI_AIRCRAFT, + RTTI_AIRCRAFTTYPE, + RTTI_ANIM, + RTTI_ANIMTYPE, + RTTI_BUILDING, + RTTI_BUILDINGTYPE, + RTTI_BULLET, + RTTI_BULLETTYPE, + RTTI_CELL, + RTTI_FACTORY, + RTTI_HOUSE, + RTTI_HOUSETYPE, + RTTI_INFANTRY, + RTTI_INFANTRYTYPE, + RTTI_OVERLAY, + RTTI_OVERLAYTYPE, + RTTI_SMUDGE, + RTTI_SMUDGETYPE, + RTTI_SPECIAL, + RTTI_TEAM, + RTTI_TEAMTYPE, + RTTI_TEMPLATE, + RTTI_TEMPLATETYPE, + RTTI_TERRAIN, + RTTI_TERRAINTYPE, + RTTI_TRIGGER, + RTTI_TRIGGERTYPE, + RTTI_UNIT, + RTTI_UNITTYPE, + RTTI_VESSEL, + RTTI_VESSELTYPE, + + RTTI_COUNT +} RTTIType; + + +/********************************************************************** +** These are the difficulty settings of the game. +*/ +typedef enum DiffType { + DIFF_EASY, + DIFF_NORMAL, + DIFF_HARD, + + DIFF_COUNT, + DIFF_FIRST=0 +} DiffType; + + +/********************************************************************** +** This is the size of the speech buffer. This value should be as large +** as the largest speech sample, plus a few bytes for overhead +** (16 bytes is sufficient). +*/ +#define SPEECH_BUFFER_SIZE 50000L + + +/********************************************************************** +** The theater mixfiles are cached into a buffer of this size. Ensure +** that the size specified is at least as large as the largest +** theater mixfile data block. +*/ +#define THEATER_BUFFER_SIZE 1100000L + + +/********************************************************************** +** This is the size of the shape buffer. This buffer is used as a staging +** buffer for the shape drawing technology. It MUST be as big as the +** largest shape (uncompressed) that will be drawn. If this value is +** changed, be sure to update the makefile and rebuild all of the shape +** data files. +*/ +#define SHAPE_BUFFER_SIZE 65000L + + +/********************************************************************** +** Filenames of the data files it can create at run time. +*/ +#define FAME_FILE_NAME "HALLFAME.DAT" +#define NET_SAVE_FILE_NAME "SAVEGAME.NET" +#define CONFIG_FILE_NAME "REDALERT.INI" + + +/********************************************************************** +** Map controls. The map is composed of square elements called 'cells'. +** All larger elements are build upon these. +*/ + +#define HIGH_COORD_MASK 0x80008000L + +// Size of the map in cells. +#define MAP_CELL_W 128 +#define MAP_CELL_H 128 +#define MAP_CELL_TOTAL (MAP_CELL_W*MAP_CELL_H) + +#define REFRESH_EOL 32767 // This number ends a refresh/occupy offset list. +#define REFRESH_SIDEBAR 32766 // This number flags that sidebar needs refreshing. + + +/**************************************************************************** +** These are custom C&C specific types. The CELL is used for map coordinate +** with cell resolution. The COORDINATE type is used for map coordinates that +** have a lepton resolution. CELL is more efficient when indexing into the map +** and when size is critical. COORDINATE is more efficient when dealing with +** accuracy and object movement. +*/ +typedef unsigned short LEPTON; +typedef union { + LEPTON Raw; + struct { +#ifdef BIG_ENDIAN + unsigned char Cell; + unsigned char Lepton; +#else + unsigned char Lepton; + unsigned char Cell; +#endif + } Sub; +} LEPTON_COMPOSITE; + +typedef unsigned long COORDINATE; +typedef union { + COORDINATE Coord; + struct { +#ifdef BIG_ENDIAN + LEPTON_COMPOSITE Y; + LEPTON_COMPOSITE X; +#else + LEPTON_COMPOSITE X; + LEPTON_COMPOSITE Y; +#endif + } Sub; +} COORD_COMPOSITE; + +typedef signed short CELL; +#define SLUFF_BITS (sizeof(CELL)*CHAR_BIT)-(14) +typedef union { + CELL Cell; + struct { +#ifdef BIG_ENDIAN +#if SLUFF_BITS + /* + ** Unused upper bits will cause problems on a big-endian machine unless they + ** are deliberately accounted for. + */ + unsigned sluff:SLUF_BITS; +#endif + unsigned Y:7; + unsigned X:7; +#else + unsigned X:7; + unsigned Y:7; +#endif + } Sub; +} CELL_COMPOSITE; + +typedef int WAYPOINT; + + +/********************************************************************** +** This is the target composit information. Notice that with an RTTI_NONE +** and an index value of 0, the target value returned is identical with +** TARGET_NONE. This is by design and is necessary. +*/ +typedef long TARGET; + +#define TARGET_MANTISSA 24 // Bits of value precision. +#define TARGET_EXPONENT 8 +typedef union { + TARGET Target; + struct { +#ifdef BIG_ENDIAN + unsigned Exponent:TARGET_EXPONENT; + unsigned Mantissa:TARGET_MANTISSA; +#else + unsigned Mantissa:TARGET_MANTISSA; + unsigned Exponent:TARGET_EXPONENT; +#endif + } Sub; +} TARGET_COMPOSITE; + + +inline TARGET Build_Target(RTTIType kind, int value) +{ + TARGET_COMPOSITE target; + + target.Target = 0; + target.Sub.Exponent = kind; + target.Sub.Mantissa = value; + return(target.Target); +} + + +#define TARGET_NONE ((TARGET)0) + + +/* +** The map is broken down into regions of this specified dimensions. +*/ +#define REGION_WIDTH 4 +#define REGION_HEIGHT 4 +#define MAP_REGION_WIDTH (((MAP_CELL_W + (REGION_WIDTH -1)) / REGION_WIDTH)+2) +#define MAP_REGION_HEIGHT (((MAP_CELL_H + (REGION_WIDTH -1)) / REGION_HEIGHT)+2) +#define MAP_TOTAL_REGIONS (MAP_REGION_WIDTH * MAP_REGION_HEIGHT) + + +/********************************************************************** +** This enumerates the various known fear states for infantry units. +** At these stages, certain events or recovery actions are performed. +*/ +typedef enum FearType { + FEAR_NONE=0, // No fear at all (default state). + FEAR_ANXIOUS=10, // Something makes them scared. + FEAR_SCARED=100, // Scared enough to take cover. + FEAR_PANIC=200, // Run away! Run away! + FEAR_MAXIMUM=255 // Scared to death. +} FearType; + + +/********************************************************************** +** When a moving object moves, the Per_Cell_Process function is called +** at various times during the move. Certain operations must be +** performed at different stages of the move. This enum specifies the +** different conditions under which the Per_Cell_Process function is +** called. +*/ +typedef enum PCPType { + PCP_ROTATION, // When sitting in place and performing rotations. + PCP_DURING, // While moving between two cells. + PCP_END, // When the 'center' of a cell is reached during movement. +} PCPType; + + +/********************************************************************** +** A base is broken up into several zones. This type enumerates the +** various zones. +*/ +typedef enum ZoneType { + ZONE_CORE, // Center of base. + ZONE_NORTH, // North section. + ZONE_EAST, // East section. + ZONE_SOUTH, // South section. + ZONE_WEST, // West section. + + ZONE_COUNT, + ZONE_FIRST=0, + ZONE_NONE=-1 +} ZoneType; + + +/********************************************************************** +** The map is prescanned to mark of movement zones according to certain +** movement characteristics. This enum specifies those characteristics +** and movement zones kept track of. +*/ +typedef enum MZoneType { + MZONE_NORMAL, // Normal terrestrial objects (can't crush walls). + MZONE_CRUSHER, // Can crush crushable wall types. + MZONE_DESTROYER, // Can destroy walls. + MZONE_WATER, // Water based objects. + + MZONE_COUNT, + MZONE_FIRST=0 +} MZoneType; + +#define MZONEF_NORMAL (1<APC or vehicle->Repair facility. + ACTION_SELF, // Self select special case. + ACTION_ATTACK, // Can attack or fire upon it in some fashion. + ACTION_HARVEST, // Special harvest mode. + ACTION_SELECT, // Would change selection to specified object. + ACTION_TOGGLE_SELECT,// Toggles select state of the object. + ACTION_CAPTURE, // The unit will try to capture the object. + ACTION_REPAIR, // The target object should be repaired. + ACTION_SELL, // The target building should be sold back. + ACTION_SELL_UNIT, // The target unit should be sold back. + ACTION_NO_SELL, // No sell or no repair. + ACTION_NO_REPAIR, // No sell or no repair. + ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object. + ACTION_PARA_BOMB, // Parachute bomb strike. + ACTION_PARA_INFANTRY,// Parachute infantry strike. + ACTION_PARA_SABOTEUR,// Parachute saboteur strike. + ACTION_NUKE_BOMB, // That target object should be blasted. + ACTION_AIR_STRIKE, // That target object should be blasted. + ACTION_CHRONOSPHERE, // That target object should be teleported. + ACTION_CHRONO2, // Teleport it to the given coordinates now. + ACTION_IRON_CURTAIN, // That target object should be invulnerable. + ACTION_SPY_MISSION, // Photo recon mission. + ACTION_GUARD_AREA, // Guard the area/object clicked on. + ACTION_HEAL, // Heal the infantryman clicked on. + ACTION_DAMAGE, // Enter and damage building. + ACTION_GREPAIR, // Enter and complete repair building. + ACTION_NO_DEPLOY, + ACTION_NO_ENTER, + ACTION_NO_GREPAIR, + + ACTION_COUNT +} ActionType; + + +/********************************************************************** +** When a unit gets damaged, the result of the damage is returned as +** this type. It can range from no damage taken to complete destruction. +*/ +typedef enum ResultType { + RESULT_NONE, // No damage was taken by the target. + RESULT_LIGHT, // Some damage was taken, but no state change occurred. + RESULT_HALF, // Damaged to below half strength (only returned on transition). + RESULT_MAJOR, // Damaged down to 1 hit point. + RESULT_DESTROYED // Damaged to complete destruction. +} ResultType; + + +#ifdef OBSOLETE +/********************************************************************** +** These are the special concrete control defines. They enumerate the +** sequence order of the concrete icons in the concrete art file. +*/ +// DEBUG === convert this to be zero based so that a nulled cell is the +// default cell. +enum ConcreteEnum { + C_NONE=-1, + C_LEFT=0, + C_RIGHT=1, + C_RIGHT_UPDOWN=2, + C_LEFT_UPDOWN=3, + C_UP_RIGHT=4, + C_UP_LEFT=5, + C_DOWN_RIGHT=6, + C_DOWN_LEFT=7, + C_RIGHT_DOWN=8, + C_LEFT_DOWN=9, + C_RIGHT_UP=10, + C_LEFT_UP=11, + C_UPDOWN_RIGHT=12, + C_UPDOWN_LEFT=13 +}; +#endif + + +/********************************************************************** +** Units that move can move at different speeds. These enumerate the +** different speeds that a unit can move. +*/ +typedef enum MPHType{ + MPH_IMMOBILE=0, + MPH_VERY_SLOW=5, // 2 + MPH_KINDA_SLOW=6, // 3 + MPH_SLOW=8, // 4 + MPH_SLOW_ISH=10, // 5 + MPH_MEDIUM_SLOW=12, // 6 + MPH_MEDIUM=18, // 9 + MPH_MEDIUM_FAST=30, // 12 + MPH_MEDIUM_FASTER=35, // 14 + MPH_FAST=40, // 16 + MPH_ROCKET=60, // 24 + MPH_VERY_FAST=100, // 40 + MPH_LIGHT_SPEED=255 // 100 +} MPHType; + + +/********************************************************************** +** The houses that can be played are listed here. Each has their own +** personality and strengths. +*/ +typedef enum HousesType { + HOUSE_NONE=-1, + HOUSE_SPAIN, // Gold (unremapped) + HOUSE_GREECE, // LtBlue + HOUSE_USSR, // Red + HOUSE_ENGLAND, // Green + HOUSE_UKRAINE, // Orange + HOUSE_GERMANY, // Grey + HOUSE_FRANCE, // Blue + HOUSE_TURKEY, // Brown + HOUSE_GOOD, // Global Defense Initiative + HOUSE_BAD, // Brotherhood of Nod + HOUSE_NEUTRAL, // Civilians + HOUSE_JP, // Disaster Containment Team + HOUSE_MULTI1, // Multi-Player house #1 + HOUSE_MULTI2, // Multi-Player house #2 + HOUSE_MULTI3, // Multi-Player house #3 + HOUSE_MULTI4, // Multi-Player house #4 + HOUSE_MULTI5, // Multi-Player house #5 + HOUSE_MULTI6, // Multi-Player house #6 + HOUSE_MULTI7, // Multi-Player house #7 + HOUSE_MULTI8, // Multi-Player house #8 + HOUSE_COUNT, + HOUSE_FIRST=0 +} HousesType; + +inline HousesType operator++(HousesType &, int); + +#define HOUSEF_ALLIES (HOUSEF_ENGLAND|HOUSEF_SPAIN|HOUSEF_GREECE|HOUSEF_GERMANY|HOUSEF_FRANCE|HOUSEF_TURKEY|HOUSEF_GOOD) +#define HOUSEF_SOVIET (HOUSEF_USSR|HOUSEF_UKRAINE|HOUSEF_BAD) +#define HOUSEF_OTHERS (HOUSEF_NEUTRAL|HOUSEF_JP|HOUSEF_MULTI1|HOUSEF_MULTI2|HOUSEF_MULTI3|HOUSEF_MULTI4|HOUSEF_MULTI5|HOUSEF_MULTI6|HOUSEF_MULTI7|HOUSEF_MULTI8) +#define HOUSEF_NONE 0 + +#define HOUSEF_ENGLAND (1L< 2 players. +*/ +typedef enum ScenarioPlayerEnum +{ + SCEN_PLAYER_NONE = -1, + SCEN_PLAYER_SPAIN, + SCEN_PLAYER_GREECE, + SCEN_PLAYER_USSR, + SCEN_PLAYER_JP, + SCEN_PLAYER_2PLAYER, + SCEN_PLAYER_MPLAYER, + SCEN_PLAYER_COUNT, + SCEN_PLAYER_FIRST = 0 +} ScenarioPlayerType; + +inline ScenarioPlayerType operator++(ScenarioPlayerType &, int); + + +/********************************************************************** +** These are the directional parameters for a scenario. +*/ +typedef enum ScenarioDirEnum +{ + SCEN_DIR_NONE = -1, + SCEN_DIR_EAST, + SCEN_DIR_WEST, + SCEN_DIR_COUNT, + SCEN_DIR_FIRST = 0 +} ScenarioDirType; + +inline ScenarioDirType operator++(ScenarioDirType &, int); + + +/********************************************************************** +** These are the random variations of a scenario. +*/ +typedef enum ScenarioVarEnum +{ + SCEN_VAR_NONE = -1, + SCEN_VAR_A, + SCEN_VAR_B, + SCEN_VAR_C, + SCEN_VAR_D, + SCEN_VAR_COUNT, // comes before the Lose value! + SCEN_VAR_LOSE, + SCEN_VAR_FIRST = 0 +} ScenarioVarType; + +inline ScenarioVarType operator++(ScenarioVarType &, int); + + +/********************************************************************** +** The objects to be drawn on the map are grouped into layers. These +** enumerated values specify those layers. The ground layer is sorted +** from back to front. +*/ +typedef enum LayerType { + LAYER_NONE=-1, + LAYER_SURFACE, // Flat on the ground (no sorting or apparent vertical height). + LAYER_GROUND, // Touching the ground type object (units & buildings). + LAYER_AIR, // Flying above the ground (explosions & flames). + LAYER_TOP, // Topmost layer (aircraft & bullets). + + LAYER_COUNT, + LAYER_FIRST=0 +} LayerType; + +inline LayerType operator++(LayerType &, int); + + +/********************************************************************** +** This enumerates the various bullet types. These types specify bullet's +** visual and explosive characteristics. +*/ +typedef enum BulletType { + BULLET_NONE=-1, + + BULLET_INVISIBLE, + BULLET_CANNON, + BULLET_ACK, + BULLET_TORPEDO, + BULLET_FROG, + BULLET_HEAT_SEEKER, + BULLET_LASER_GUIDED, + BULLET_LOBBED, + BULLET_BOMBLET, + BULLET_BALLISTIC, + BULLET_PARACHUTE, + BULLET_FIREBALL, + BULLET_DOG, + BULLET_CATAPULT, + BULLET_AAMISSILE, + BULLET_GPS_SATELLITE, + BULLET_NUKE_UP, + BULLET_NUKE_DOWN, + + BULLET_COUNT, + BULLET_FIRST=0 +} BulletType; + +inline BulletType operator++(BulletType &, int); + + +/********************************************************************** +** All game buildings (structures) are enumerated here. This includes +** civilian structures as well. +*/ +typedef enum StructType { + STRUCT_NONE=-1, + STRUCT_ADVANCED_TECH, + STRUCT_IRON_CURTAIN, + STRUCT_WEAP, + STRUCT_CHRONOSPHERE, + STRUCT_PILLBOX, + STRUCT_CAMOPILLBOX, + STRUCT_RADAR, + STRUCT_GAP, + STRUCT_TURRET, + STRUCT_AAGUN, + STRUCT_FLAME_TURRET, + STRUCT_CONST, + STRUCT_REFINERY, + STRUCT_STORAGE, + STRUCT_HELIPAD, + STRUCT_SAM, + STRUCT_AIRSTRIP, + STRUCT_POWER, + STRUCT_ADVANCED_POWER, + STRUCT_SOVIET_TECH, + STRUCT_HOSPITAL, + STRUCT_BARRACKS, + STRUCT_TENT, + STRUCT_KENNEL, + STRUCT_REPAIR, + STRUCT_BIO_LAB, + STRUCT_MISSION, + STRUCT_SHIP_YARD, + STRUCT_SUB_PEN, + STRUCT_MSLO, + STRUCT_FORWARD_COM, + STRUCT_TESLA, + + /* + ** All buildings that are never used as a prerequisite + ** for construction, follow this point. Typically, this is + ** limited to civilian structures. Also, the following + ** buildings are NEVER used in the availability bit field + ** record that each house maintains. i.e., STRUCTF_???? + ** bit checking will never occur with the following + ** building types. + */ + STRUCT_FAKEWEAP, + STRUCT_FAKECONST, + STRUCT_FAKE_YARD, + STRUCT_FAKE_PEN, + STRUCT_FAKE_RADAR, + + STRUCT_SANDBAG_WALL, + STRUCT_CYCLONE_WALL, + STRUCT_BRICK_WALL, + STRUCT_BARBWIRE_WALL, + STRUCT_WOOD_WALL, + STRUCT_FENCE, + + STRUCT_AVMINE, + STRUCT_APMINE, + STRUCT_V01, + STRUCT_V02, + STRUCT_V03, + STRUCT_V04, + STRUCT_V05, + STRUCT_V06, + STRUCT_V07, + STRUCT_V08, + STRUCT_V09, + STRUCT_V10, + STRUCT_V11, + STRUCT_V12, + STRUCT_V13, + STRUCT_V14, + STRUCT_V15, + STRUCT_V16, + STRUCT_V17, + STRUCT_V18, + STRUCT_PUMP, + STRUCT_V20, + STRUCT_V21, + STRUCT_V22, + STRUCT_V23, + STRUCT_V24, + STRUCT_V25, + STRUCT_V26, + STRUCT_V27, + STRUCT_V28, + STRUCT_V29, + STRUCT_V30, + STRUCT_V31, + STRUCT_V32, + STRUCT_V33, + STRUCT_V34, + STRUCT_V35, + STRUCT_V36, + STRUCT_V37, + STRUCT_BARREL, + STRUCT_BARREL3, + +#ifdef FIXIT_ANTS + STRUCT_QUEEN, + STRUCT_LARVA1, + STRUCT_LARVA2, +#endif + + STRUCT_COUNT, + STRUCT_FIRST=0 +} StructType; + +inline StructType operator++(StructType &, int); + +#define STRUCTF_NONE 0L +#define STRUCTF_ADVANCED_TECH (1L << STRUCT_ADVANCED_TECH) +#define STRUCTF_IRON_CURTAIN (1L << STRUCT_IRON_CURTAIN) +#define STRUCTF_WEAP (1L << STRUCT_WEAP) +#define STRUCTF_CHRONOSPHERE (1L << STRUCT_CHRONOSPHERE) +#define STRUCTF_PILLBOX (1L << STRUCT_PILLBOX) +#define STRUCTF_CAMOPILLBOX (1L << STRUCT_CAMOPILLBOX) +#define STRUCTF_RADAR (1L << STRUCT_RADAR) +#define STRUCTF_GAP (1L << STRUCT_GAP) +#define STRUCTF_TURRET (1L << STRUCT_TURRET) +#define STRUCTF_AAGUN (1L << STRUCT_AAGUN) +#define STRUCTF_FLAME_TURRET (1L << STRUCT_FLAME_TURRET) +#define STRUCTF_CONST (1L << STRUCT_CONST) +#define STRUCTF_REFINERY (1L << STRUCT_REFINERY) +#define STRUCTF_STORAGE (1L << STRUCT_STORAGE) +#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD) +#define STRUCTF_SAM (1L << STRUCT_SAM) +#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP) +#define STRUCTF_POWER (1L << STRUCT_POWER) +#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER) +#define STRUCTF_SOVIET_TECH (1L << STRUCT_SOVIET_TECH) +#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL) +#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS) +#define STRUCTF_TENT (1L << STRUCT_TENT) +#define STRUCTF_KENNEL (1L << STRUCT_KENNEL) +#define STRUCTF_REPAIR (1L << STRUCT_REPAIR) +#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB) +#define STRUCTF_MISSION (1L << STRUCT_MISSION) +#define STRUCTF_SHIP_YARD (1L << STRUCT_SHIP_YARD) +#define STRUCTF_SUB_PEN (1L << STRUCT_SUB_PEN) +#define STRUCTF_MSLO (1L << STRUCT_MSLO) +#define STRUCTF_FAKECONST (1L << STRUCT_FAKECONST) +#define STRUCTF_FAKEWEAP (1L << STRUCT_FAKEWEAP) + + +/********************************************************************** +** The overlays are enumerated here. An overlay functions similarly to +** a transparent icon. It is placed over the terrain but usually falls +** "under" buildings, trees, and units. +*/ +typedef enum OverlayType { + OVERLAY_NONE=-1, + OVERLAY_SANDBAG_WALL, // Piled sandbags. + OVERLAY_CYCLONE_WALL, // Chain-link fence. + OVERLAY_BRICK_WALL, // Solid concrete wall. + OVERLAY_BARBWIRE_WALL, // Barbed-wire wall. + OVERLAY_WOOD_WALL, // Wooden fence. + OVERLAY_GOLD1, + OVERLAY_GOLD2, + OVERLAY_GOLD3, + OVERLAY_GOLD4, + OVERLAY_GEMS1, + OVERLAY_GEMS2, + OVERLAY_GEMS3, + OVERLAY_GEMS4, + OVERLAY_V12, // Haystacks + OVERLAY_V13, // Haystack + OVERLAY_V14, // Wheat field + OVERLAY_V15, // Fallow field + OVERLAY_V16, // Corn field + OVERLAY_V17, // Celery field + OVERLAY_V18, // Potato field + OVERLAY_FLAG_SPOT, // Flag start location. + OVERLAY_WOOD_CRATE, // Wooden goodie crate. + OVERLAY_STEEL_CRATE, // Steel goodie crate. + OVERLAY_FENCE, // New fangled fence. + OVERLAY_WATER_CRATE, // Water goodie crate. + + OVERLAY_COUNT, + OVERLAY_FIRST=0 +} OverlayType; + +inline OverlayType operator++(OverlayType &, int); + + +/********************************************************************** +** This specifies the infantry in the game. The "E" designation is +** similar to the army classification of enlisted soldiers. +*/ +typedef enum InfantryType{ + INFANTRY_NONE=-1, + INFANTRY_E1, // Mini-gun armed. + INFANTRY_E2, // Grenade thrower. + INFANTRY_E3, // Rocket launcher. + INFANTRY_E4, // Flame thrower equipped. + INFANTRY_RENOVATOR, // Engineer. + INFANTRY_TANYA, // Saboteur. + INFANTRY_SPY, // Spy. + INFANTRY_THIEF, // Thief. + INFANTRY_MEDIC, // Field Medic. + INFANTRY_GENERAL, // Field Marshal. + INFANTRY_DOG, // Soviet attack dog + + INFANTRY_C1, // Civilian + INFANTRY_C2, // Civilian + INFANTRY_C3, // Civilian + INFANTRY_C4, // Civilian + INFANTRY_C5, // Civilian + INFANTRY_C6, // Civilian + INFANTRY_C7, // Civilian + INFANTRY_C8, // Civilian + INFANTRY_C9, // Civilian + INFANTRY_C10, // Nikumba + INFANTRY_EINSTEIN, // Einstein + INFANTRY_DELPHI, // Agent "Delphi" + INFANTRY_CHAN, // Dr. Chan + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CounterStrike II only! + INFANTRY_SHOCK, // Shock Trooper + INFANTRY_MECHANIC, +#endif + + INFANTRY_COUNT, + INFANTRY_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + INFANTRY_RA_COUNT = INFANTRY_SHOCK +#endif +} InfantryType; + +#define INFANTRYF_DOG (1L << INFANTRY_DOG) + +inline InfantryType operator++(InfantryType &, int); + + +/********************************************************************** +** The game units are enumerated here. These include not only traditional +** vehicles, but also hovercraft and gunboats. +*/ +typedef enum UnitType{ + UNIT_NONE=-1, + UNIT_HTANK, // Mammoth tank. + UNIT_MTANK, // Heavy tank. + UNIT_MTANK2, // Medium tank. + UNIT_LTANK, // Light tank ('Bradly'). + UNIT_APC, // APC. + UNIT_MINELAYER, // Mine-laying vehicle. + UNIT_JEEP, // 4x4 jeep replacement. + UNIT_HARVESTER, // Resource gathering vehicle. + UNIT_ARTY, // Artillery unit. + UNIT_MRJ, // Mobile Radar Jammer. + UNIT_MGG, // Mobile Gap Generator + UNIT_MCV, // Mobile construction vehicle. + UNIT_V2_LAUNCHER, // V2 rocket launcher. + UNIT_TRUCK, // Convoy truck + +#ifdef FIXIT_ANTS + UNIT_ANT1, // Warrior ant. + UNIT_ANT2, // Warrior ant. + UNIT_ANT3, // Warrior ant. +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CS II ONLY! + UNIT_CHRONOTANK, // Chrono-shifting tank + UNIT_TESLATANK, // Tesla-equipped tank + UNIT_MAD, // Timequake tank + UNIT_DEMOTRUCK, // Jihad truck +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + UNIT_PHASE, // cloaking APC for special missions +#endif +#endif + + UNIT_COUNT, + UNIT_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + UNIT_RA_COUNT = UNIT_CHRONOTANK +#endif +} UnitType; + +inline UnitType operator++(UnitType &, int); + +#define UNITF_HTANK (1L<id) + + +#define MAX_LOG_LEVEL 10 + +// Maximum number of multi players possible. +#define MAX_PLAYERS 8 // max # of players we can have + + + +#endif diff --git a/CODE/DESCDLG.CPP b/CODE/DESCDLG.CPP new file mode 100644 index 0000000..3700a78 --- /dev/null +++ b/CODE/DESCDLG.CPP @@ -0,0 +1,163 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DESCDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DESCDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DescriptionClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "descdlg.h" + + +/*********************************************************************************************** + * DescriptionClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to "fill-out" a description. * + * * + * INPUT: char *string - return answer here. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void DescriptionClass::Process(char * string) +{ + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, BUTTON_Y); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, 0, BUTTON_Y); + + cancelbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3)*2; + optionsbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3); + optionsbtn.Add_Tail(cancelbtn); + + EditClass edit( + BUTTON_EDIT, + string, + 31, + TPF_6PT_GRAD, + 0, + EDIT_Y, + EDIT_W); + + edit.Set_Focus(); + edit.X = OPTION_X + (OPTION_WIDTH - edit.Width)/2, + optionsbtn.Add_Tail(edit); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT, GadgetClass::LEFTPRESS); + optionsbtn.Add_Tail(dialog); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + optionsbtn.Add_Tail(background); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + Window_Hide_Mouse(WINDOW_EDITOR); + + /* + ** Draw the background + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_BORDER); // has border, raised up + Draw_Caption(TXT_MISSION_DESCRIPTION, OPTION_X, OPTION_Y, OPTION_WIDTH); + + /* + ** Draw the titles + */ + optionsbtn.Draw_All(); + Window_Show_Mouse(); + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = optionsbtn.Input(); + + /* + ** Process Input + */ + switch (input) { + + case KN_RETURN: + case KeyNumType(BUTTON_OPTIONS|KN_BUTTON): + strtrim(string); + process = false; + break; + + case KN_ESC: + case KeyNumType(BUTTON_CANCEL|KN_BUTTON): + string[0]= NULL; + strtrim(string); + process = false; + break; + + case KeyNumType(BUTTON_EDIT|KN_BUTTON): + break; + + default: + break; + } + } +} + diff --git a/CODE/DESCDLG.H b/CODE/DESCDLG.H new file mode 100644 index 0000000..484b4ce --- /dev/null +++ b/CODE/DESCDLG.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DESCDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DESCDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef DESCDLG_H +#define DESCDLG_H + +#include "gadget.h" + +class DescriptionClass +{ + private: + + enum DescriptionClassEnum { + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2) & ~7), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+32, // Title's x pos + TEXT_Y=OPTION_Y+32, // Add 11 for each following line + BUTTON_OPTIONS=1, // Button number for "Ok" + BUTTON_CANCEL, + BUTTON_EDIT, + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + EDIT_Y =OPTION_Y+50, + EDIT_W =180 //204, + }; + + public: + DescriptionClass(void) {}; + void Process(char *string); +}; + +#endif + + diff --git a/CODE/DIAL8.CPP b/CODE/DIAL8.CPP new file mode 100644 index 0000000..c201fc8 --- /dev/null +++ b/CODE/DIAL8.CPP @@ -0,0 +1,319 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DIAL8.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DIAL8.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Dial8Class::Action -- action routine for Dial8Class * + * Dial8Class::Dial8Class -- constructor for the facing dial * + * Dial8Class::Draw_Me -- render routine for Dial8Class * + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Dial8Class::Dial8Class -- constructor for the facing dial * + * * + * INPUT: * + * id button ID * + * x,y,w,h dimensions in window-relative pixels * + * dir numerical initial facing value (0-255); this is the * + * value returned by WWLIB Desired_Facing8() * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +Dial8Class::Dial8Class(int id, int x, int y, int w, int h, DirType dir) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTHELD | LEFTRELEASE, true) +{ + /* + ** Center coordinates. + */ + FaceX = X + (Width / 2); + FaceY = Y + (Height / 2); + + /* + ** Init directions. + */ + Direction = dir; // 0 - 255 + Facing = Dir_Facing(Direction); // 0 - 7 + OldFacing = Facing; // 0 - 7 + + /* + ** Compute the drawing dimensions: a 45-degree angle intersects a unity- + ** radius circle at (.707,.707). Make the decorations 8/10 of the radius, + ** and the line extend to 6/10 of the radius. Use Width/2 for x-radius, + ** Height/2 for y-radius. + */ + FacePoint[0][0] = FaceX; + FacePoint[0][1] = FaceY - (h * 8 / 2) / 10; + + FacePoint[1][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[1][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FacePoint[2][0] = FaceX + (w * 8 / 2) / 10; + FacePoint[2][1] = FaceY; + + FacePoint[3][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[3][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[4][0] = FaceX; + FacePoint[4][1] = FaceY + (h * 8 / 2) / 10; + + FacePoint[5][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[5][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[6][0] = FaceX - (w * 8 / 2) / 10; + FacePoint[6][1] = FaceY; + + FacePoint[7][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[7][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FaceLine[0][0] = FaceX; + FaceLine[0][1] = FaceY - (h * 6 / 2) / 10; + + FaceLine[1][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[1][1] = FaceY - (h * 7 * 6 / 2) / 100; + + FaceLine[2][0] = FaceX + (w * 6 / 2) / 10; + FaceLine[2][1] = FaceY; + + FaceLine[3][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[3][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[4][0] = FaceX; + FaceLine[4][1] = FaceY + (h * 6 / 2) / 10; + + FaceLine[5][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[5][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[6][0] = FaceX - (w * 6 / 2) / 10; + FaceLine[6][1] = FaceY; + + FaceLine[7][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[7][1] = FaceY - (h * 7 * 6 / 2) / 100; +} + + +/*************************************************************************** + * Dial8Class::Action -- activation function for Dial8Class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Action(unsigned flags, KeyNumType &key) +{ + static int is_sel = 0; + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + if (flags & LEFTPRESS) { + is_sel = 1; + } + + /* + ** If left mouse is clicked or held, and the dial has changed its direction, + ** invoke the parent Action routine: + ** GadgetClass::Action handles Sticky processing, & sets IsToRepaint if any + ** flag bits are set. + ** ControlClass::Action handles Peer_To_Peer notification, and substitutes + ** 'key' with the button ID if any flags are set, or 0 if no flags are set + */ + if (flags & LEFTPRESS || ((flags & LEFTHELD) && is_sel)) { + /* + ** Get new dial position (0-255) + */ + Direction = (DirType)Desired_Facing8(FaceX, FaceY, Get_Mouse_X(), Get_Mouse_Y()); + + /* + ** Convert to Facing value (0-7). + */ + Facing = Dir_Facing(Direction); + + /* + ** If it's moved, redraw. + */ + if (Facing!=OldFacing) { + OldFacing = Facing; + ControlClass::Action(flags, key); + return(true); + + } else { + + /* + ** Dial hasn't moved; kill the event & return + */ + key = KN_NONE; + ControlClass::Action(0, key); + return(true); + } + + } else { + + /* + ** Otherwise, no events have occurred; kill the event if it's a LEFTRELEASE, + ** and return + */ + if (flags & LEFTRELEASE) { + key = KN_NONE; + is_sel = 0; + } + return(ControlClass::Action(0, key)); + } +} + + +/*************************************************************************** + * Dial8Class::Draw_Me -- custom render routine for Dial8Class * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state* + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Draw_Me(int forced) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Redraw if parent indicates a redraw is needed + */ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + + /* + ** Draw background & decorations. + */ + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, true); + for (int i=0; i<8; i++) { + Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_RAISED, false); + } + + /* + ** Draw the hand & its shadow. + */ + LogicPage->Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1, scheme->Shadow); + LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1], scheme->Highlight); + + /* + ** Restore the mouse. + */ + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * DirType dial is pointing to * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +DirType Dial8Class::Get_Direction(void) const +{ + return(Direction); +} + + +/*************************************************************************** + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * * + * INPUT: * + * DirType to set dial to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void Dial8Class::Set_Direction(DirType dir) +{ + Direction = dir; + Facing = Dir_Facing(Direction); + OldFacing = Facing; + Flag_To_Redraw(); +} diff --git a/CODE/DIAL8.H b/CODE/DIAL8.H new file mode 100644 index 0000000..4fdb8c2 --- /dev/null +++ b/CODE/DIAL8.H @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DIAL8.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DIAL8.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DIAL8_H +#define DIAL8_H + +class Dial8Class : public ControlClass +{ + public: + /* + ** Constructor/Destructor + */ + Dial8Class(int id, int x, int y, int w, int h, DirType dir); + + /* + ** Get/Set the direction the dial is currently pointing + */ + DirType Get_Direction(void) const; + void Set_Direction(DirType dir); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + protected: + /* + ** Overloaded event processing routine + */ + virtual int Action(unsigned flags, KeyNumType &key); + + private: + int FaceX; // x-coord of center of face + int FaceY; // y-coord of center of face + int FacePoint[8][2]; // coords of the little dial decorations + int FaceLine[8][2]; // coords for drawing the dial hand + DirType Direction; // 0-255 numerical direction of dial + FacingType Facing; // numerical facing direction of dial (0 - 7) + FacingType OldFacing; // previous Facing value + +}; + +#endif + diff --git a/CODE/DIALOG.CPP b/CODE/DIALOG.CPP new file mode 100644 index 0000000..1c5e93c --- /dev/null +++ b/CODE/DIALOG.CPP @@ -0,0 +1,998 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DIALOG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DIALOG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clip_Text_Print -- Prints text with clipping and support. * + * Dialog_Box -- draws a dialog background box * + * Display_Place_Building -- Displays the "place building" dialog box. * + * Display_Select_Target -- Displays the "choose target" prompt. * + * Display_Status -- Display the player scenario status box. * + * Draw_Box -- Displays a highlighted box. * + * Draw_Caption -- Draws a caption on a dialog box. * + * Fancy_Text_Print -- Prints text with a drop shadow. * + * Plain_Text_Print -- Prints text without using a color scheme * + * Redraw_Needed -- Determine if sidebar needs to be redrawn. * + * Render_Bar_Graph -- Renders a specified bargraph. * + * Simple_Text_Print -- Prints text with a drop shadow. * + * Window_Box -- Draws a fancy box over the specified window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "defines.h" //VG 10/17/96 + +unsigned char * Font_Palette(int color); + + +/*********************************************************************************************** + * Dialog_Box -- draws a dialog background box * + * * + * INPUT: * + * x,y,w,h the usual * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/26/1995 BR : Created. * + * 07/31/1996 JLB : Uses shapes to draw the box. * + *=============================================================================================*/ +void Dialog_Box(int x, int y, int w, int h) +{ +// Try to expand the box a little taller and a little wider to make room for +// the dialog box graphics in the DOS version. +#ifndef WIN32 + x = max(0, x-4); + y = max(0, y-4); + w = min(w+8, 320-x); + h = min(h+8, 200-y); +#endif + + WindowList[WINDOW_PARTIAL][WINDOWX] = x; + WindowList[WINDOW_PARTIAL][WINDOWY] = y; + WindowList[WINDOW_PARTIAL][WINDOWWIDTH] = w; + WindowList[WINDOW_PARTIAL][WINDOWHEIGHT] = h; + + /* + ** Always draw to the hidpage and then blit forward. + */ +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); +#endif + + /* + ** Draw the background block. + */ + int cx = w/2; + int cy = h/2; + void const * shapedata = MFCD::Retrieve("DD-BKGND.SHP"); +#ifdef WIN32 + CC_Draw_Shape(shapedata, 0, cx-312, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, cx, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 2, cx-312, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 3, cx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); +#else + CC_Draw_Shape(shapedata, 0, cx-156, cy-96, WINDOW_PARTIAL, SHAPE_WIN_REL); +#endif + /* + ** Draw the side strips. + */ + shapedata = MFCD::Retrieve("DD-EDGE.SHP"); + for (int yy = 0; yy < h; yy += 6) { + CC_Draw_Shape(shapedata, 0, 7*RESFACTOR, yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, w-((7+8)*RESFACTOR), yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + } + + /* + ** Draw the border bars. + */ + shapedata = MFCD::Retrieve("DD-LEFT.SHP"); + CC_Draw_Shape(shapedata, 0, 0, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, 0, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-RIGHT.SHP"); + int rightx = w - (7*RESFACTOR); +#ifndef WIN32 + rightx--; +#endif + CC_Draw_Shape(shapedata, 0, rightx, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, rightx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-BOTM.SHP"); + CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, cx, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + + shapedata = MFCD::Retrieve("DD-TOP.SHP"); + CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 0, cx, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + + /* + ** Draw the corner caps. + */ + shapedata = MFCD::Retrieve("DD-CRNR.SHP"); + CC_Draw_Shape(shapedata, 0, 0, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 1, w-(12*RESFACTOR-1), 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 2, 0, h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + CC_Draw_Shape(shapedata, 3, w-(12*RESFACTOR-1), h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + +#ifdef WIN32 + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff, x, y, x, y, w, h, false); + WWMouse->Erase_Mouse(&HidPage, FALSE); +#else +// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, Map.ShadowPage->Get_Buffer()); + Hide_Mouse(); + HidPage.Blit(SeenBuff); + Show_Mouse(); +// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ((GraphicBufferClass*)Map.Shadow_Address())->Get_Buffer()); +#endif + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * Draw_Box -- Displays a highlighted box. * + * * + * This will draw a highlighted box to the logicpage. It can * + * optionally fill the box with a color as well. This is a low level * + * function and thus, it doesn't do any graphic mode color adjustments. * + * * + * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). * + * * + * w,h -- Width and height of box (in pixels). * + * * + * up -- Is the box rendered in the "up" stated? * + * * + * filled-- Is the box to be filled. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 JLB : Created. * + * 05/30/1992 JLB : Embedded color codes. * + * 07/31/1992 JLB : Depressed option added. * + *=============================================================================================*/ +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // Filler, Shadow, Hilite, Corner colors + + BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = { + { scheme->Background, scheme->Highlight, scheme->Shadow, scheme->Corners}, // Down + { scheme->Background, scheme->Shadow, scheme->Highlight, scheme->Corners}, // Raised + { DKGREY, WHITE, BLACK, DKGREY}, // Disabled down + { DKGREY, BLACK, LTGREY, DKGREY}, // Disabled up + { BLACK, scheme->Box, scheme->Box, BLACK}, // List box + { BLACK, scheme->Box, scheme->Box, BLACK}, // Dialog box + }; + + w--; + h--; + BoxStyleType const &style = ButtonColors[up]; + + if (filled) { + LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler); + } + + switch (up) { + case (BOXSTYLE_BOX): + LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight); + break; + + case (BOXSTYLE_BORDER): + LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight); + break; + + default: + LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow); + LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow); + + LogicPage->Draw_Line(x, y, x+w, y, style.Highlight); + LogicPage->Draw_Line(x, y, x, y+h, style.Highlight); + + LogicPage->Put_Pixel(x, y+h, style.Corner); + LogicPage->Put_Pixel(x+w, y, style.Corner); + break; + } +} + + +/*********************************************************************************************** + * Format_Window_String -- Separates a String into Lines. * + * This function will take a long string and break it up into lines * + * which are not longer then the window width. Any character < ' ' is * + * considered a new line marker and will be replaced by a NULL. * + * * + * INPUT: char *String - string to be formated. * + * int maxlinelen - Max length of any line in pixels. * + * * + * OUTPUT: int - number of lines string is. * + * * + * WARNINGS: The string passed in will be modified - NULLs will be put * + * into each position that will be a new line. * + * * + * HISTORY: * + * 03/27/1992 SB : Created. * + * 05/18/1995 JLB : Greatly revised for new font system. * + * 09/04/1996 BWG : Added '@' is treated as a carriage return for width calculations. * + *=============================================================================================*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + /* + ** Look for special line break character and force a line break when it is + ** discovered. + */ + if (*string == '@') { + *string = '\r'; + } + + // While the current line is less then the max length... + while (linelen < maxlinelen && *string != '\r' && *string != '\0' && *string != '@') { + linelen += Char_Pixel_Width(*string++); + } + + // if the line is to long... + if (linelen >= maxlinelen) { + + /* + ** Back up to an appropriate location to break. + */ + while (*string != ' ' && *string != '\r' && *string != '\0' && *string != '@') { + linelen -= Char_Pixel_Width(*string--); + } + + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *string++ = '\r'; + } + } + return(lines); +} + + +/*********************************************************************************************** + * Window_Box -- Draws a fancy box over the specified window. * + * * + * This routine will draw a fancy (shaded) box over the specified * + * window. This is the effect used to give the polished look to * + * screen rectangles without having to use art. * + * * + * INPUT: window -- Specified window to fill and border. * + * * + * style -- The style to render the window. * + * * + * OUTPUT: none * + * * + * WARNINGS: The rendering is done to the LogicPage. * + * * + * HISTORY: * + * 03/03/1992 JLB : Created. * + * 07/31/1992 JLB : Cool raised border effect. * + * 06/08/1994 JLB : Takes appropriate enumeration parameters. * + *=============================================================================================*/ +void Window_Box(WindowNumberType window, BoxStyleEnum style) +{ + int x = WindowList[window][WINDOWX]; + int y = WindowList[window][WINDOWY]; + int w = WindowList[window][WINDOWWIDTH]; + int h = WindowList[window][WINDOWHEIGHT]; + + /* + ** If it is to be rendered to the seenpage, then + ** hide the mouse. + */ + if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x ,y, x+w, y+h); + + Draw_Box(x, y, w, h, style, true); + + /* + ** Restore the mouse if it has been hidden and return. + */ + if (LogicPage == &SeenBuff) Conditional_Show_Mouse(); +} + + +/*********************************************************************************************** + * Simple_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * The C&C gradient font colors are as follows: * + * 0 transparent (background) * + * 1 foreground color for mono-color fonts only * + * 2 shadow under characters ("drop shadow") * + * 3 shadow all around characters ("full shadow") * + * 4-10 unused * + * 11 top row * + * 12 next row * + * 13 next row * + * 14 next row * + * 15 bottom row * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + *=============================================================================================*/ +void Simple_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag) +{ + static int yspace=0; // Y spacing adjustment for font. + static int xspace=0; // Spacing adjustment for font. + void const * font=0; // Font to use. + int shadow; // Requested shadow value. + unsigned char fontpalette[16]; // Working font palette array. + int forecolor; + + if (fore == NULL) { + fore = &ColorRemaps[PCOLOR_RED]; + } + + /* + ** Init the font palette to the given background color + */ + memset(&fontpalette[0], back, 16); + + forecolor = fore->Color; + + /* + ** A gradient font always requires special fixups for the palette + */ + int point = (flag & (TextPrintType)0x000F); + if (point == TPF_VCR || point == TPF_6PT_GRAD || point == TPF_METAL12 || point == TPF_EFNT || point == TPF_TYPE) { + + /* + ** If a gradient palette is specified, copy the remap table directly, otherwise + ** use the foreground color as the entire font remap color. + */ + if (flag & TPF_USE_GRAD_PAL) { + memcpy(fontpalette, fore->FontRemap, 16); + forecolor = fore->Color; + if (point == TPF_TYPE) { + forecolor = fontpalette[1]; + } + } else { + memset(&fontpalette[4], fore->Color, 12); + forecolor = fore->Color; + } + + /* + ** Medium color: set all font colors to a medium value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_MEDIUM_COLOR) { + forecolor = fore->Color; + memset(&fontpalette[4], fore->Color, 12); + } + + /* + ** Bright color: set all font colors to a bright value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_BRIGHT_COLOR) { + forecolor = fore->Bright; + memset(&fontpalette[4], fore->BrightColor, 12); + } + } + + /* + ** Change the current font if it differs from the font desired. + */ +#ifdef WIN32 + xspace = 1; +#else + xspace = 0; +#endif + yspace = 0; + + switch (point) { + case TPF_SCORE: + font = ScoreFontPtr; + break; + + case TPF_METAL12: + font = Metal12FontPtr; + //xspace += 1; + break; + + case TPF_MAP: + font = MapFontPtr; + xspace -= 1; + break; + + case TPF_VCR: + font = VCRFontPtr; + break; + + case TPF_6PT_GRAD: + font = GradFont6Ptr; + xspace -= 1; + break; + + case TPF_3POINT: + xspace += 1; + font = Font3Ptr; + flag = flag & ~(TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_NOSHADOW); + break; + + case TPF_6POINT: + font = Font6Ptr; + xspace -= 1; + break; + + case TPF_EFNT: + font = EditorFont; +#ifdef WIN32 + yspace += 1; + xspace -= 1; +#endif + xspace -= 1; + break; + + case TPF_8POINT: + font = Font8Ptr; +#ifdef WIN32 + xspace -= 2; + yspace -= 4; +#else + xspace -= 1; + yspace -= 2; +#endif + break; + + case TPF_LED: +#ifdef WIN32 + xspace -= 4; +#else + xspace -= 2; +#endif + font = FontLEDPtr; + break; + + case TPF_TYPE: + font = TypeFontPtr; + xspace -= 1; + +#ifdef WOLAPI_INTEGRATION + xspace -= 2; + yspace += 2; +#else // I am implicitly assuming that TPF_TYPE was no longer being used, before I came along, despite the following. ajw +#ifdef GERMAN + yspace += 4; //VG 10/17/96 +#endif +#endif + + break; + + default: + font = FontPtr; + break; + } + + /* + ** Change the current font palette according to the dropshadow flags. + */ + shadow = (flag & (TPF_NOSHADOW|TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_LIGHTSHADOW)); + switch (shadow) { + + /* + ** The text is rendered plain. + */ + case TPF_NOSHADOW: + fontpalette[2] = back; + fontpalette[3] = back; + xspace -= 1; +#ifdef WIN32 + yspace -= 2; +#else + yspace -= 1; +#endif + break; + + /* + ** The text is rendered with a simple + ** drop shadow. + */ + case TPF_DROPSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Special engraved text look for the options + ** dialog system. + */ + case TPF_LIGHTSHADOW: + fontpalette[2] = ((14 * 16) + 7)+1; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Each letter is surrounded by black. This is used + ** when the text will be over a non-plain background. + */ + case TPF_FULLSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = BLACK; + xspace -= 1; + break; + + default: + break; + } + if (point != TPF_TYPE) { + fontpalette[0] = back; + fontpalette[1] = fore->Color; + } + + /* + ** Set the font and spacing according to the values they should be. + */ + FontXSpacing = xspace; + FontYSpacing = yspace; + Set_Font(font); + Set_Font_Palette(fontpalette); + + /* + ** Display the (centered) message if there is one. + */ + if (text && *text) { + switch (flag & (TPF_CENTER|TPF_RIGHT)) { + case TPF_CENTER: + x -= String_Pixel_Width(text)>>1; + break; + + case TPF_RIGHT: + x -= String_Pixel_Width(text); + break; + + default: + break; + } + + if ((unsigned)x < LogicPage->Get_Width() && (unsigned)y < LogicPage->Get_Height()) { + LogicPage->Print(text, x, y, forecolor, back); +// LogicPage->Print(text, x, y, fore->Color, back); + } + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Text number to print. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 11/29/1994 JLB : Created * + *=============================================================================================*/ +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If the text number is valid, then process it. + */ + if (text != TXT_NONE) { + va_start(arg, flag); + + /* + ** The text string must be locked since the vsprintf function doesn't know + ** how to handle EMS pointers. + */ + char const * tptr = Text_String(text); + vsprintf(buffer, tptr, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are to be changed, since the text number is TXT_NONE. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + * 11/29/1994 JLB : Separated actual draw action. * + *=============================================================================================*/ +void Fancy_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If there is a valid text string pointer then build the final string into the + ** working buffer before sending it to the simple string printing routine. + */ + if (text) { + + /* + ** Since vsprintf doesn't know about EMS pointers, be sure to surround this + ** call with locking code. + */ + va_start(arg, flag); + vsprintf(buffer, text, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are desired to be changed, so call the simple print routine with + ** a NULL text pointer. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Clip_Text_Print -- Prints text with clipping and support. * + * * + * Use this routine to print text that that should be clipped at an arbitrary right margin * + * as well as possibly recognizing characters. Typical users of this routine would * + * be list boxes. * + * * + * INPUT: text -- Reference to the text to print. * + * * + * x,y -- Pixel coordinate of the upper left corner of the text position. * + * * + * fore -- The foreground color to use. * + * * + * back -- The background color to use. * + * * + * flag -- The text print flags to use. * + * * + * width -- The maximum pixel width to draw the text. Extra characters beyond this * + * point will not be printed. * + * * + * tabs -- Optional pointer to a series of pixel tabstop positions. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void Conquer_Clip_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, int width, int const * tabs) +{ + char buffer[512]; + + if (text) { + strcpy(buffer, text); + + /* + ** Set the font and spacing characteristics according to the flag + ** value passed in. + */ + Simple_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, flag); + + char * source = &buffer[0]; + unsigned offset = 0; + int processing = true; + while (processing && offset < width) { + char * ptr = strchr(source, '\t'); + + /* + ** Zap the tab character. It will be processed later. + */ + if (ptr) { + *ptr = '\0'; + } + + if (*source) { + + /* + ** Scan forward until the end of the string is reached or the + ** maximum width, whichever comes first. + */ + int w = 0; + char * bptr = source; + do { + w += Char_Pixel_Width(*bptr++); + } while (*bptr && offset+w < width); + + /* + ** If the maximum width has been exceeded, then remove the last + ** character and signal that further processing is not necessary. + */ + if (offset+w >= width) { + bptr--; + w -= Char_Pixel_Width(*bptr); + *bptr = '\0'; + processing = 0; + } + + /* + ** Print this text block and advance the offset accordingly. + */ + Simple_Text_Print(source, x+offset, y, fore, back, flag); + offset += w; + } + + /* + ** If a was the terminator for this text block, then advance + ** to the next tabstop. + */ + if (ptr) { + if (tabs) { + while (offset > *tabs) { + tabs++; + } + offset = *tabs; + } else { + offset = ((offset+1 / 50) + 1) * 50; + } + source = ptr+1; + } else { + break; + } + } + } +} + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(char const * text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + + +unsigned char * Font_Palette(int color) +{ + static unsigned char _fpalette[16]; + + memset(_fpalette, '\0', sizeof(_fpalette)); + memset(&_fpalette[11], color, 5); + return(_fpalette); +} + + + +/*********************************************************************************************** + * Draw_Caption -- Draws a caption on a dialog box. * + * * + * This routine draws the caption text and any fancy filigree that the dialog may require. * + * * + * INPUT: text -- The text of the caption. This is the text number. * + * * + * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. * + * * + * w -- The width of the dialog box (in pixels). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + *=============================================================================================*/ +void Draw_Caption(int text, int x, int y, int w) +{ + Draw_Caption(Text_String(text), x, y, w); +} + + +void Draw_Caption(char const * text, int x, int y, int w) +{ + /* + ** Draw the caption. + */ + if (text != NULL && *text != '\0') { + if (Debug_Map) { + Fancy_Text_Print(text, w/2 + x, (2 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_EFNT|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } else { + Fancy_Text_Print(text, w/2 + x, (8 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + int length = String_Pixel_Width(text); + LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + (8 * RESFACTOR), (x+(w/2))+(length/2), + y+FontHeight+FontYSpacing + (8 * RESFACTOR), GadgetClass::Get_Color_Scheme()->Box); + } + } +} diff --git a/CODE/DIBAPI.H b/CODE/DIBAPI.H new file mode 100644 index 0000000..59bb133 --- /dev/null +++ b/CODE/DIBAPI.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +#ifndef DIBAPI_H +#define DIBAPI_H +/* + * dibapi.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * BitmapToDIB() - Creates a DIB from a bitmap + * ChangeBitmapFormat() - Changes a bitmap to a specified DIB format + * ChangeDIBFormat() - Changes a DIB's BPP and/or compression format + * CopyScreenToBitmap() - Copies entire screen to a standard Bitmap + * CopyScreenToDIB() - Copies entire screen to a DIB + * CopyWindowToBitmap() - Copies a window to a standard Bitmap + * CopyWindowToDIB() - Copies a window to a DIB + * CreateDIBPalette() - Creates a palette from a DIB + * CreateDIB() - Creates a new DIB + * DestroyDIB() - Deletes DIB when finished using it + * DIBError() - Displays message box with error message + * DIBHeight() - Gets the DIB height + * DIBNumColors() - Calculates number of colors in the DIB's color table + * DIBToBitmap() - Creates a bitmap from a DIB + * DIBWidth() - Gets the DIB width + * FindDIBBits() - Sets pointer to the DIB bits + * GetSystemPalette() - Gets the current palette + * LoadDIB() - Loads a DIB from a file + * PaintBitmap() - Displays standard bitmap in the specified DC + * PaintDIB() - Displays DIB in the specified DC + * PalEntriesOnDevice() - Gets the number of palette entries + * PaletteSize() - Calculates the buffer size required by a palette + * PrintDIB() - Prints the specified DIB + * PrintScreen() - Prints the entire screen + * PrintWindow() - Prints all or part of a window + * SaveDIB() - Saves the specified dib in a file + * + * See the file DIBAPI.TXT for more information about these functions. + * + * ajw added + * LoadDIB_FromMemory() - Loads a DIB from BMP file data located at a location in memory. + * + */ + + +/* Handle to a DIB */ +#define HDIB HANDLE + + +/* Print Area selection */ +#define PW_WINDOW 1 +#define PW_CLIENT 2 + + +/* Print Options selection */ +#define PW_BESTFIT 1 +#define PW_STRETCHTOPAGE 2 +#define PW_SCALE 3 + +/* DIB Macros*/ + +// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits" +// parameter is the bit count for the scanline (biWidth * biBitCount), +// and this macro returns the number of DWORD-aligned bytes needed +// to hold those bits. + +#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + +/* Error constants */ +enum { + ERR_MIN = 0, // All error #s >= this value + ERR_NOT_DIB = 0, // Tried to load a file, NOT a DIB! + ERR_MEMORY, // Not enough memory! + ERR_READ, // Error reading file! + ERR_LOCK, // Error on a GlobalLock()! + ERR_OPEN, // Error opening a file! + ERR_CREATEPAL, // Error creating palette. + ERR_GETDC, // Couldn't get a DC. + ERR_CREATEDDB, // Error create a DDB. + ERR_STRETCHBLT, // StretchBlt() returned failure. + ERR_STRETCHDIBITS, // StretchDIBits() returned failure. + ERR_SETDIBITSTODEVICE, // SetDIBitsToDevice() failed. + ERR_STARTDOC, // Error calling StartDoc(). + ERR_NOGDIMODULE, // Couldn't find GDI module in memory. + ERR_SETABORTPROC, // Error calling SetAbortProc(). + ERR_STARTPAGE, // Error calling StartPage(). + ERR_NEWFRAME, // Error calling NEWFRAME escape. + ERR_ENDPAGE, // Error calling EndPage(). + ERR_ENDDOC, // Error calling EndDoc(). + ERR_SETDIBITS, // Error calling SetDIBits(). + ERR_FILENOTFOUND, // Error opening file in GetDib() + ERR_INVALIDHANDLE, // Invalid Handle + ERR_DIBFUNCTION, // Error on call to DIB function + ERR_MAX // All error #s < this value + }; + + + +/* Function prototypes */ + +HDIB FAR BitmapToDIB (HBITMAP hBitmap, HPALETTE hPal); +HDIB FAR ChangeBitmapFormat (HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal); +HDIB FAR ChangeDIBFormat (HDIB hDIB, WORD wBitCount, + DWORD dwCompression); +HBITMAP FAR CopyScreenToBitmap (LPRECT); +HDIB FAR CopyScreenToDIB (LPRECT); +HBITMAP FAR CopyWindowToBitmap (HWND, WORD); +HDIB FAR CopyWindowToDIB (HWND, WORD); +HPALETTE FAR CreateDIBPalette (HDIB hDIB); +HDIB FAR CreateDIB(DWORD, DWORD, WORD); +WORD FAR DestroyDIB (HDIB); +void FAR DIBError (int ErrNo); +DWORD FAR DIBHeight (LPCSTR lpDIB); +WORD FAR DIBNumColors (LPCSTR lpDIB); +HBITMAP FAR DIBToBitmap (HDIB hDIB, HPALETTE hPal); +DWORD FAR DIBWidth (LPCSTR lpDIB); +LPSTR FAR FindDIBBits (LPCSTR lpDIB); +HPALETTE FAR GetSystemPalette (void); +HDIB FAR LoadDIB (LPSTR); +BOOL FAR PaintBitmap (HDC, LPRECT, HBITMAP, LPRECT, HPALETTE); +BOOL FAR PaintDIB (HDC, LPRECT, HDIB, LPRECT, HPALETTE); +int FAR PalEntriesOnDevice (HDC hDC); +WORD FAR PaletteSize (LPCSTR lpDIB); +WORD FAR PrintDIB (HDIB, WORD, WORD, WORD, LPSTR); +WORD FAR PrintScreen (LPRECT, WORD, WORD, WORD, LPSTR); +WORD FAR PrintWindow (HWND, WORD, WORD, WORD, WORD, LPSTR); +WORD FAR SaveDIB (HDIB, LPSTR); + +// ajw added +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ); + +#endif diff --git a/CODE/DIBFILE.CPP b/CODE/DIBFILE.CPP new file mode 100644 index 0000000..ddd1f49 --- /dev/null +++ b/CODE/DIBFILE.CPP @@ -0,0 +1,703 @@ +/* +** 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 . +*/ + +//******************************************************************* +// +// file.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// SaveDIB() - Saves the specified dib in a file +// LoadDIB() - Loads a DIB from a file +// DestroyDIB() - Deletes DIB when finished using it +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample Files (and/or any modified version) in +// any way you find useful, provided that you agree that +// Microsoft has no warranty obligations or liability for any +// Sample Application Files which are modified. +// +//******************************************************************* + +#include +#include +#include +#include +#include +#include +#include +#include "dibutil.h" +#include "dibapi.h" + +//#include "WolDebug.h" + +/* + * Dib Header Marker - used in writing DIBs to files + */ +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') + +/********************************************************************* + * + * Local Function Prototypes + * + *********************************************************************/ + + +HANDLE ReadDIBFile(int); +BOOL MyRead(int, LPSTR, DWORD); +BOOL SaveDIBFile(void); +BOOL WriteDIB(LPSTR, HANDLE); +DWORD PASCAL MyWrite(int, VOID FAR *, DWORD); + +/************************************************************************* + * + * LoadDIB() + * + * Loads the specified DIB from a file, allocates memory for it, + * and reads the disk file into the memory. + * + * + * Parameters: + * + * LPSTR lpFileName - specifies the file to load a DIB from + * + * Returns: A handle to a DIB, or NULL if unsuccessful. + * + * NOTE: The DIB API were not written to handle OS/2 DIBs; This + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * + *************************************************************************/ + + +HDIB FAR LoadDIB(LPSTR lpFileName) +{ + HDIB hDIB; + int hFile; + OFSTRUCT ofs; + + /* + * Set the cursor to a hourglass, in case the loading operation + * takes more than a sec, the user will know what's going on. + */ + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + if ((hFile = OpenFile(lpFileName, &ofs, OF_READ)) != -1) + { + hDIB = ReadDIBFile(hFile); + _lclose(hFile); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return hDIB; + } + else + { +// DIBError(ERR_FILENOTFOUND); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return NULL; + } +} + + +/************************************************************************* + * + * SaveDIB() + * + * Saves the specified DIB into the specified file name on disk. No + * error checking is done, so if the file already exists, it will be + * written over. + * + * Parameters: + * + * HDIB hDib - Handle to the dib to save + * + * LPSTR lpFileName - pointer to full pathname to save DIB under + * + * Return value: 0 if successful, or one of: + * ERR_INVALIDHANDLE + * ERR_OPEN + * ERR_LOCK + * + * History: + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will not save a file if it is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Taken from DIBVIEW (which was taken + * from SHOWDIB) + * 1/30/92 Mark Bader Fixed problem of writing too many + * bytes to the file + * 6/24/92 Mark Bader Added check for OS/2 DIB + * + *************************************************************************/ + + +WORD FAR SaveDIB(HDIB hDib, LPSTR lpFileName) +{ + BITMAPFILEHEADER bmfHdr; // Header for Bitmap file + LPBITMAPINFOHEADER lpBI; // Pointer to DIB info structure + int fh; // file handle for opened file + OFSTRUCT of; // OpenFile structure + DWORD dwDIBSize; + DWORD dwError; // Error return from MyWrite + + if (!hDib) + return ERR_INVALIDHANDLE; + fh = OpenFile(lpFileName, &of, OF_CREATE | OF_READWRITE); + if (fh == -1) + return ERR_OPEN; + + /* + * Get a pointer to the DIB memory, the first of which contains + * a BITMAPINFO structure + */ + lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib); + if (!lpBI) + return ERR_LOCK; + + // Check to see if we're dealing with an OS/2 DIB. If so, don't + // save it because our functions aren't written to deal with these + // DIBs. + + if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) + { + GlobalUnlock(hDib); + return ERR_NOT_DIB; + } + + /* + * Fill in the fields of the file header + */ + + /* Fill in file type (first 2 bytes must be "BM" for a bitmap) */ + bmfHdr.bfType = DIB_HEADER_MARKER; // "BM" + + // Calculating the size of the DIB is a bit tricky (if we want to + // do it right). The easiest way to do this is to call GlobalSize() + // on our global handle, but since the size of our global memory may have + // been padded a few bytes, we may end up writing out a few too + // many bytes to the file (which may cause problems with some apps, + // like HC 3.0). + // + // So, instead let's calculate the size manually. + // + // To do this, find size of header plus size of color table. Since the + // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains + // the size of the structure, let's use this. + + dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPSTR)lpBI); // Partial Calculation + + // Now calculate the size of the image + + if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) { + + // It's an RLE bitmap, we can't calculate size, so trust the + // biSizeImage field + + dwDIBSize += lpBI->biSizeImage; + } + else { + DWORD dwBmBitsSize; // Size of Bitmap Bits only + + // It's not RLE, so size is Width (DWORD aligned) * Height + + dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * lpBI->biHeight; + + dwDIBSize += dwBmBitsSize; + + // Now, since we have calculated the correct size, why don't we + // fill in the biSizeImage field (this will fix any .BMP files which + // have this field incorrect). + + lpBI->biSizeImage = dwBmBitsSize; + } + + + // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER) + + bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + + /* + * Now, calculate the offset the actual bitmap bits will be in + * the file -- It's the Bitmap file header plus the DIB header, + * plus the size of the color table. + */ + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + + PaletteSize((LPSTR)lpBI); + + /* Write the file header */ + _lwrite(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER)); + + /* + * Write the DIB header and the bits -- use local version of + * MyWrite, so we can write more than 32767 bytes of data + */ + dwError = MyWrite(fh, (LPSTR)lpBI, dwDIBSize); + GlobalUnlock(hDib); + _lclose(fh); + + if (dwError == 0) + return ERR_OPEN; // oops, something happened in the write + else + return 0; // Success code +} + + +/************************************************************************* + * + * DestroyDIB () + * + * Purpose: Frees memory associated with a DIB + * + * Returns: Nothing + * + * History: Date Author Reason + * 9/15/91 Mark Bader Created + * + *************************************************************************/ + + +WORD FAR DestroyDIB(HDIB hDib) +{ + GlobalFree(hDib); + return 0; +} + + +//************************************************************************ +// +// Auxiliary Functions which the above procedures use +// +//************************************************************************ + + +/************************************************************************* + * + * Function: ReadDIBFile (int) + * + * Purpose: Reads in the specified DIB file into a global chunk of + * memory. + * + * Returns: A handle to a dib (hDIB) if successful. + * NULL if an error occurs. + * + * Comments: BITMAPFILEHEADER is stripped off of the DIB. Everything + * from the end of the BITMAPFILEHEADER structure on is + * returned in the global memory handle. + * + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * 6/25/92 Mark Bader Added check for OS/2 DIB + * 7/21/92 Mark Bader Added code to deal with bfOffBits + * field in BITMAPFILEHEADER + * 9/11/92 Mark Bader Fixed Realloc Code to free original mem + * + *************************************************************************/ + +HANDLE ReadDIBFile(int hFile) +{ + BITMAPFILEHEADER bmfHeader; + DWORD dwBitsSize; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + /* + * get length of DIB in bytes for use when reading + */ + + dwBitsSize = filelength(hFile); + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + + hDIB = GlobalAlloc(GMEM_MOVEABLE, + (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); + + if (!hDIB) return NULL; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + if (!lpbi) + { + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file + + if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) + goto ErrExit; + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + goto ErrExit; + + // read the BITMAPINFOHEADER + + if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) + goto ErrExit; + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + goto ErrExit; + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) + * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + + nNumColors * sizeof(RGBQUAD) + + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + goto ErrExitNoUnlock; //MPB + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table + _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) + _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + + if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) + goto OKExit; + + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} + +/************************************************************************* + + Function: MyRead (int, LPSTR, DWORD) + + Purpose: Routine to read files greater than 64K in size. + + Returns: TRUE if successful. + FALSE if an error occurs. + + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + +*************************************************************************/ + + +BOOL MyRead(int hFile, LPSTR lpBuffer, DWORD dwSize) +{ + char huge *lpInBuf = (char huge *)lpBuffer; + int nBytes; + + /* + * Read in the data in 32767 byte chunks (or a smaller amount if it's + * the last chunk of data read) + */ + + while (dwSize) + { + nBytes = (int)(dwSize > (DWORD)32767 ? 32767 : LOWORD (dwSize)); + if (_lread(hFile, (LPSTR)lpInBuf, nBytes) != (WORD)nBytes) + return FALSE; + dwSize -= nBytes; + lpInBuf += nBytes; + } + return TRUE; +} + + +/**************************************************************************** + + FUNCTION : MyWrite(int fh, VOID FAR *pv, DWORD ul) + + PURPOSE : Writes data in steps of 32k till all the data is written. + Normal _lwrite uses a WORD as 3rd parameter, so it is + limited to 32767 bytes, but this procedure is not. + + RETURNS : 0 - If write did not proceed correctly. + number of bytes written otherwise. + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + + ****************************************************************************/ + + +DWORD PASCAL MyWrite(int iFileHandle, VOID FAR *lpBuffer, DWORD dwBytes) +{ + DWORD dwBytesTmp = dwBytes; // Save # of bytes for return value + BYTE huge *hpBuffer = (BYTE huge *)lpBuffer; // make a huge pointer to the data + + /* + * Write out the data in 32767 byte chunks. + */ + + while (dwBytes > 32767) + { + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)32767) != 32767) + return 0; + dwBytes -= 32767; + hpBuffer += 32767; + } + + /* Write out the last chunk (which is < 32767 bytes) */ + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)dwBytes) != (WORD)dwBytes) + return 0; + return dwBytesTmp; +} + +// ajw added +// Added to allow "loading" from a location in memory. +// A modification of ReadDIBFile(), above. +//*********************************************************************************************** +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ) +{ + BITMAPFILEHEADER bmfHeader; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + const unsigned char* const pDataStart = pData; + const unsigned char* pDataEnd = pData + dwBitsSize; // One char past end of "file". + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + +// debugprint( "LoadDIB_FromMemory, GlobalAlloc\n" ); + hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); +// debugprint( "hDIB from GlobalALloc is %i\n", hDIB ); + + if (!hDIB) + { +// debugprint( "LoadDIB_FromMemory error: failed alloc\n" ); + return NULL; + } + +// debugprint( "LoadDIB_FromMemory, lpbi Lock\n" ); + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); +// debugprint( "lpbi is %i\n", lpbi ); + if (!lpbi) + { +// debugprint( "LoadDIB_FromMemory error: failed lock\n" ); + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file +// if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPFILEHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPFILEHEADER %i bytes\n", sizeof( BITMAPFILEHEADER ) ); + memcpy( &bmfHeader, pData, sizeof( BITMAPFILEHEADER ) ); + pData += sizeof( BITMAPFILEHEADER ); + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + { +// debugprint( "LoadDIB_FromMemory error: no BM\n" ); + goto ErrExit; + } + + // read the BITMAPINFOHEADER +// if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPINFOHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size 2\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPINFOHEADER %i bytes\n", sizeof( BITMAPINFOHEADER ) ); + memcpy( lpbi, pData, sizeof( BITMAPINFOHEADER ) ); + pData += sizeof( BITMAPINFOHEADER ); + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + { +// debugprint( "LoadDIB_FromMemory error: lpbi->biSize bad\n" ); + goto ErrExit; + } + + if( lpbi->biCompression != BI_RGB ) + { +// debugprint( "LoadDIB_FromMemory error: Image is compressed\n" ); + goto ErrExit; + } + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + +// debugprint( "biSizeImage is %i. I would say it was %i, because the bpp is %i.\n", lpbi->biSizeImage, ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight, lpbi->biBitCount ); + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); +// debugprint( "LoadDIB_FromMemory, GlobalReAlloc: lpbi->biSize=%i, nNumColors=%i, lpbi->biSizeImage=%i\n", lpbi->biSize, nNumColors,lpbi->biSizeImage ); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + { +// debugprint( "LoadDIB_FromMemory error: realloc failed\n" ); + goto ErrExitNoUnlock; //MPB + } + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table +// _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); +// debugprint( "LoadDIB_FromMemory, memcpy color table %i colors, so %i bytes\n", nNumColors, nNumColors * sizeof(RGBQUAD) ); + memcpy( (LPSTR)(lpbi) + lpbi->biSize, pData, nNumColors * sizeof(RGBQUAD) ); + pData += nNumColors * sizeof(RGBQUAD); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) +// _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + pData = pDataStart + bmfHeader.bfOffBits; + +// debugprint( "bmfHeader.bfOffBits is %i\n", bmfHeader.bfOffBits ); + +// if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) +// goto OKExit; +// debugprint( "Checking that pData(%i) + biSizeImage(%i), which is %i, is equal to pDataEnd(%i)\n", +// pData, lpbi->biSizeImage, pData + lpbi->biSizeImage, pDataEnd ); +// if( pData + lpbi->biSizeImage != pDataEnd ) condition relaxed +// { +// debugprint( "LoadDIB_FromMemory error: bad size 3\n" ); +// goto ErrExit; +// } + +// debugprint( "LoadDIB_FromMemory, memcpy the bits, %i bytes. Image is w %i, h.%i\n", +// lpbi->biSizeImage, lpbi->biWidth, lpbi->biHeight ); +// debugprint( "Writing to lpbi (%i) + offBits (%i)\n", lpbi, offBits ); + + memcpy( (LPSTR)lpbi + offBits, pData, lpbi->biSizeImage ); +// pData += lpbi->biSizeImage; +// if( pData != pDataEnd ) // Should end up one byte past end of data. - condition relaxed +// debugprint( "LoadDIB_FromMemory: ERROR! Ended up at %i instead of %i\n", pData, pDataEnd ); + goto OKExit; + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); +// debugprint( "LoadDIB_FromMemory Error!\n" ); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} diff --git a/CODE/DIBUTIL.CPP b/CODE/DIBUTIL.CPP new file mode 100644 index 0000000..07d0ccd --- /dev/null +++ b/CODE/DIBUTIL.CPP @@ -0,0 +1,1316 @@ +/* +** 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 . +*/ + +//********************************************************************** +// +// dibutil.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// CreateDIB() - Creates new DIB +// FindDIBBits() - Sets pointer to the DIB bits +// DIBWidth() - Gets the width of the DIB +// DIBHeight() - Gets the height of the DIB +// PaletteSize() - Calculates the buffer size required by a palette +// DIBNumColors() - Calculates number of colors in the DIB's color table +// CreateDIBPalette() - Creates a palette from a DIB +// DIBToBitmap() - Creates a bitmap from a DIB +// BitmapToDIB() - Creates a DIB from a bitmap +// PalEntriesOnDevice()- Gets the number of palette entries of a device +// GetSystemPalette() - Returns a handle to the current system palette +// AllocRoomForDIB() - Allocates memory for a DIB +// ChangeDIBFormat() - Changes a DIB's BPP and/or compression format +// ChangeBitmapFormat()- Changes a bitmap to a DIB with specified BPP and +// compression format +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample Files (and/or any modified version) in +// any way you find useful, provided that you agree that +// Microsoft has no warranty obligations or liability for any +// Sample Application Files which are modified. +// +//********************************************************************** + +/* header files */ +#include +#include +#include "dibapi.h" +#include "dibutil.h" +#include + + +/************************************************************************* + * + * CreateDIB() + * + * Parameters: + * + * DWORD dwWidth - Width for new bitmap, in pixels + * DWORD dwHeight - Height for new bitmap + * WORD wBitCount - Bit Count for new DIB (1, 4, 8, or 24) + * + * Return Value: + * + * HDIB - Handle to new DIB + * + * Description: + * + * This function allocates memory for and initializes a new DIB by + * filling in the BITMAPINFOHEADER, allocating memory for the color + * table, and allocating memory for the bitmap bits. As with all + * HDIBs, the header, colortable and bits are all in one contiguous + * memory block. This function is similar to the CreateBitmap() + * Windows API. + * + * The colortable and bitmap bits are left uninitialized (zeroed) in the + * returned HDIB. + * + * + * History: Date Author Reason + * 3/20/92 Mark Bader Created + * + ************************************************************************/ + +HDIB FAR CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount) +{ + BITMAPINFOHEADER bi; // bitmap header + LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HDIB hDIB; + DWORD dwBytesPerLine; // Number of bytes per scanline + + + // Make sure bits per pixel is valid + if (wBitCount <= 1) + wBitCount = 1; + else if (wBitCount <= 4) + wBitCount = 4; + else if (wBitCount <= 8) + wBitCount = 8; + else if (wBitCount <= 24) + wBitCount = 24; + else + wBitCount = 4; // set default value to 4 if parameter is bogus + + // initialize BITMAPINFOHEADER + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = dwWidth; // fill in width from parameter + bi.biHeight = dwHeight; // fill in height from parameter + bi.biPlanes = 1; // must be 1 + bi.biBitCount = wBitCount; // from parameter + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; // 0's here mean "default" + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + // calculate size of memory block required to store the DIB. This + // block should be big enough to hold the BITMAPINFOHEADER, the color + // table, and the bits + + dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth); + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + (dwBytesPerLine * dwHeight); + + // alloc memory block to store our bitmap + hDIB = GlobalAlloc(GHND, dwLen); + + // major bummer if we couldn't get memory block + if (!hDIB) + { + return NULL; + } + + // lock memory and get pointer to it + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // use our bitmap info structure to fill in first part of + // our DIB with the BITMAPINFOHEADER + *lpbi = bi; + + // Since we don't know what the colortable and bits should contain, + // just leave these blank. Unlock the DIB and return the HDIB. + + GlobalUnlock(hDIB); + + /* return handle to the DIB */ + return hDIB; +} + + + +/************************************************************************* + * + * FindDIBBits() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * LPSTR - pointer to the DIB bits + * + * Description: + * + * This function calculates the address of the DIB's bits and returns a + * pointer to the DIB bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +LPSTR FAR FindDIBBits(LPCSTR lpDIB) +{ + return (LPSTR)(lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB)); +} + + +/************************************************************************* + * + * DIBWidth() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - width of the DIB + * + * Description: + * + * This function gets the width of the DIB from the BITMAPINFOHEADER + * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * width field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBWidth(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether Win 3.0 and OS/2) */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB width if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biWidth; + else /* it is an OS/2 DIB, so return its width */ + return (DWORD)lpbmc->bcWidth; +} + + +/************************************************************************* + * + * DIBHeight() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - height of the DIB + * + * Description: + * + * This function gets the height of the DIB from the BITMAPINFOHEADER + * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * height field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBHeight(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether OS/2 or Win 3.0 */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB height if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biHeight; + else /* it is an OS/2 DIB, so return its height */ + return (DWORD)lpbmc->bcHeight; +} + + +/************************************************************************* + * + * PaletteSize() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - size of the color palette of the DIB + * + * Description: + * + * This function gets the size required to store the DIB's palette by + * multiplying the number of colors by the size of an RGBQUAD (for a + * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2- + * style DIB). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR PaletteSize(LPCSTR lpDIB) +{ + /* calculate the size required by the palette */ + if (IS_WIN30_DIB (lpDIB)) + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBQUAD)); + else + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBTRIPLE)); +} + + +/************************************************************************* + * + * DIBNumColors() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - number of colors in the color table + * + * Description: + * + * This function calculates the number of colors in the DIB's color table + * by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style + * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256, + * if 24, no colors in color table. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR DIBNumColors(LPCSTR lpDIB) +{ + WORD wBitCount; // DIB bit count + + /* If this is a Windows-style DIB, the number of colors in the + * color table can be less than the number of bits per pixel + * allows for (i.e. lpbi->biClrUsed can be set to some value). + * If this is the case, return the appropriate value. + */ + + if (IS_WIN30_DIB(lpDIB)) + { + DWORD dwClrUsed; + + dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed; + if (dwClrUsed) + return (WORD)dwClrUsed; + } + + /* Calculate the number of colors in the color table based on + * the number of bits per pixel for the DIB. + */ + if (IS_WIN30_DIB(lpDIB)) + wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; + else + wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; + + /* return number of colors based on bits per pixel */ + switch (wBitCount) + { + case 1: + return 2; + + case 4: + return 16; + + case 8: + return 256; + + default: + return 0; + } +} + + +/************************************************************************* + * + * CreateDIBPalette() + * + * Parameter: + * + * HDIB hDIB - specifies the DIB + * + * Return Value: + * + * HPALETTE - specifies the palette + * + * Description: + * + * This function creates a palette from a DIB by allocating memory for the + * logical palette, reading and storing the colors from the DIB's color table + * into the logical palette, creating a palette from this logical palette, + * and then returning the palette's handle. This allows the DIB to be + * displayed using the best possible colors (important for DIBs with 256 or + * more colors). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +HPALETTE FAR CreateDIBPalette(HDIB hDIB) +{ + LPLOGPALETTE lpPal; // pointer to a logical palette + HANDLE hLogPal; // handle to a logical palette + HPALETTE hPal = NULL; // handle to a palette + int i, wNumColors; // loop index, number of colors in color table + LPSTR lpbi; // pointer to packed-DIB + LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0) + LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (OS/2) + BOOL bWinStyleDIB; // flag which signifies whether this is a Win3.0 DIB + + /* if handle to DIB is invalid, return NULL */ + + if (!hDIB) + return NULL; + + /* lock DIB memory block and get a pointer to it */ + lpbi = (LPSTR)GlobalLock(hDIB); + + /* get pointer to BITMAPINFO (Win 3.0) */ + lpbmi = (LPBITMAPINFO)lpbi; + + /* get pointer to BITMAPCOREINFO (OS/2 1.x) */ + lpbmc = (LPBITMAPCOREINFO)lpbi; + + /* get the number of colors in the DIB */ + wNumColors = DIBNumColors(lpbi); + + /* is this a Win 3.0 DIB? */ + bWinStyleDIB = IS_WIN30_DIB(lpbi); + if (wNumColors) + { + /* allocate memory block for logical palette */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * + wNumColors); + + /* if not enough memory, clean up and return NULL */ + if (!hLogPal) + { + GlobalUnlock(hDIB); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set version and number of palette entries */ + lpPal->palVersion = PALVERSION; + lpPal->palNumEntries = (WORD)wNumColors; + + /* store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB) + * into palette + */ + for (i = 0; i < wNumColors; i++) + { + if (bWinStyleDIB) + { + lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; + lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; + lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + else + { + lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed; + lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen; + lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + } + + /* create the palette and get handle to it */ + hPal = CreatePalette(lpPal); + + /* if error getting handle to palette, clean up and return NULL */ + if (!hPal) + { + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + return NULL; + } + } + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + GlobalUnlock(hDIB); + + /* return handle to DIB's palette */ + return hPal; +} + + +/************************************************************************* + * + * DIBToBitmap() + * + * Parameters: + * + * HDIB hDIB - specifies the DIB to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HBITMAP - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a bitmap from a DIB using the specified palette. + * If no palette is specified, default is used. + * + * NOTE: + * + * The bitmap returned from this funciton is always a bitmap compatible + * with the screen (e.g. same bits/pixel and color planes) rather than + * a bitmap with the same attributes as the DIB. This behavior is by + * design, and occurs because this function calls CreateDIBitmap to + * do its work, and CreateDIBitmap always creates a bitmap compatible + * with the hDC parameter passed in (because it in turn calls + * CreateCompatibleBitmap). + * + * So for instance, if your DIB is a monochrome DIB and you call this + * function, you will not get back a monochrome HBITMAP -- you will + * get an HBITMAP compatible with the screen DC, but with only 2 + * colors used in the bitmap. + * + * If your application requires a monochrome HBITMAP returned for a + * monochrome DIB, use the function SetDIBits(). + * + * Also, the DIBpassed in to the function is not destroyed on exit. This + * must be done later, once it is no longer needed. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 3/27/92 Mark Bader Added comments about resulting + * bitmap format + * + ************************************************************************/ + + +HBITMAP FAR DIBToBitmap(HDIB hDIB, HPALETTE hPal) +{ + LPSTR lpDIBHdr, lpDIBBits; // pointer to DIB header, pointer to DIB bits + HBITMAP hBitmap; // handle to device-dependent bitmap + HDC hDC; // handle to DC + HPALETTE hOldPal = NULL; // handle to a palette + + /* if invalid handle, return NULL */ + + if (!hDIB) + return NULL; + + /* lock memory block and get a pointer to it */ + lpDIBHdr = (LPSTR)GlobalLock(hDIB); + + /* get a pointer to the DIB bits */ + lpDIBBits = FindDIBBits(lpDIBHdr); + + /* get a DC */ + hDC = GetDC(NULL); + if (!hDC) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + return NULL; + } + + /* select and realize palette */ + if (hPal) + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* create bitmap from DIB info. and bits */ + hBitmap = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT, + lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS); + + /* restore previous palette */ + if (hOldPal) + SelectPalette(hDC, hOldPal, FALSE); + + /* clean up */ + ReleaseDC(NULL, hDC); + GlobalUnlock(hDIB); + + /* return handle to the bitmap */ + return hBitmap; +} + + +/************************************************************************* + * + * BitmapToDIB() + * + * Parameters: + * + * HBITMAP hBitmap - specifies the bitmap to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HDIB - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a DIB from a bitmap using the specified palette. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/10/91 Patrick Schreiber Added bits per pixel validation + * and check GetObject return value + * + ************************************************************************/ + + +HDIB FAR BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal) +{ + BITMAP bm; // bitmap structure + BITMAPINFOHEADER bi; // bitmap header + BITMAPINFOHEADER FAR *lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HANDLE hDIB, h; // handle to DIB, temp handle + HDC hDC; // handle to DC + WORD biBits; // bits per pixel + + /* check if bitmap handle is valid */ + + if (!hBitmap) + return NULL; + + /* fill in BITMAP structure, return NULL if it didn't work */ + if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm)) + return NULL; + + /* if no palette is specified, use default palette */ + if (hPal == NULL) + hPal = GetStockObject(DEFAULT_PALETTE); + + /* calculate bits per pixel */ + biBits = (WORD)( bm.bmPlanes * bm.bmBitsPixel ); + + /* make sure bits per pixel is valid */ + if (biBits <= 1) + biBits = 1; + else if (biBits <= 4) + biBits = 4; + else if (biBits <= 8) + biBits = 8; + else /* if greater than 8-bit, force to 24-bit */ + biBits = 24; + + /* initialize BITMAPINFOHEADER */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = bm.bmWidth; + bi.biHeight = bm.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = biBits; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* calculate size of memory block required to store BITMAPINFO */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi); + + /* get a DC */ + hDC = GetDC(NULL); + + /* select and realize our palette */ + hPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* alloc memory block to store our bitmap */ + hDIB = GlobalAlloc(GHND, dwLen); + + /* if we couldn't get memory block */ + if (!hDIB) + { + /* clean up and return NULL */ + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* use our bitmap info. to fill BITMAPINFOHEADER */ + *lpbi = bi; + + /* call GetDIBits with a NULL lpBits param, so it will calculate the + * biSizeImage field for us + */ + GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS); + + /* get the info. returned by GetDIBits and unlock memory block */ + bi = *lpbi; + GlobalUnlock(hDIB); + + /* if the driver did not fill in the biSizeImage field, make one up */ + if (bi.biSizeImage == 0) + bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight; + + /* realloc the buffer big enough to hold all the bits */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage; + h = GlobalReAlloc(hDIB, dwLen, 0); + if (h) + hDIB = h; + else + { + /* clean up and return NULL */ + GlobalFree(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* call GetDIBits with a NON-NULL lpBits param, and actualy get the + * bits this time + */ + if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi + ->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS) == 0) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + bi = *lpbi; + + /* clean up */ + GlobalUnlock(hDIB); + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + /* return handle to the DIB */ + return hDIB; +} + + +/************************************************************************* + * + * PalEntriesOnDevice() + * + * Parameter: + * + * HDC hDC - device context + * + * Return Value: + * + * int - number of palette entries on device + * + * Description: + * + * This function gets the number of palette entries on the specified device + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +int FAR PalEntriesOnDevice(HDC hDC) +{ + int nColors; // number of colors + + /* Find out the number of palette entries on this + * device. + */ + + nColors = GetDeviceCaps(hDC, SIZEPALETTE); + + /* For non-palette devices, we'll use the # of system + * colors for our palette size. + */ + if (!nColors) + nColors = GetDeviceCaps(hDC, NUMCOLORS); + assert(nColors); + return nColors; +} + + +/************************************************************************* + * + * GetSystemPalette() + * + * Parameters: + * + * None + * + * Return Value: + * + * HPALETTE - handle to a copy of the current system palette + * + * Description: + * + * This function returns a handle to a palette which represents the system + * palette. The system RGB values are copied into our logical palette using + * the GetSystemPaletteEntries function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/20/91 Mark Bader Added GetSystemPaletteEntries call + * + ************************************************************************/ + + +HPALETTE FAR GetSystemPalette(void) +{ + HDC hDC; // handle to a DC + static HPALETTE hPal = NULL; // handle to a palette + HANDLE hLogPal; // handle to a logical palette + LPLOGPALETTE lpLogPal; // pointer to a logical palette + int nColors; // number of colors + + /* Find out how many palette entries we want. */ + + hDC = GetDC(NULL); + if (!hDC) + return NULL; + nColors = PalEntriesOnDevice(hDC); // Number of palette entries + + /* Allocate room for the palette and lock it. */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * sizeof( + PALETTEENTRY)); + + /* if we didn't get a logical palette, return NULL */ + if (!hLogPal) + return NULL; + + /* get a pointer to the logical palette */ + lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set some important fields */ + lpLogPal->palVersion = (WORD)PALVERSION; + lpLogPal->palNumEntries = (WORD)nColors; + + /* Copy the current system palette into our logical palette */ + + GetSystemPaletteEntries(hDC, 0, nColors, + (LPPALETTEENTRY)(lpLogPal->palPalEntry)); + + /* Go ahead and create the palette. Once it's created, + * we no longer need the LOGPALETTE, so free it. + */ + + hPal = CreatePalette(lpLogPal); + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + ReleaseDC(NULL, hDC); + + return hPal; +} + + +/************************************************************************* + * + * AllocRoomForDIB() + * + * Parameters: + * + * BITMAPINFOHEADER - bitmap info header stucture + * + * HBITMAP - handle to the bitmap + * + * Return Value: + * + * HDIB - handle to memory block + * + * Description: + * + * This routine takes a BITMAPINOHEADER, and returns a handle to global + * memory which can contain a DIB with that header. It also initializes + * the header portion of the global memory. GetDIBits() is used to determine + * the amount of room for the DIB's bits. The total amount of memory + * needed = sizeof(BITMAPINFOHEADER) + size of color table + size of bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/11/91 Patrick Schreiber Added header and some comments + * + ************************************************************************/ + +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap) +{ + DWORD dwLen; + HANDLE hDIB; + HDC hDC; + LPBITMAPINFOHEADER lpbi; + HANDLE hTemp; + + /* Figure out the size needed to hold the BITMAPINFO structure + * (which includes the BITMAPINFOHEADER and the color table). + */ + + dwLen = bi.biSize + PaletteSize((LPSTR) &bi); + hDIB = GlobalAlloc(GHND,dwLen); + + /* Check that DIB handle is valid */ + if (!hDIB) + return NULL; + + /* Set up the BITMAPINFOHEADER in the newly allocated global memory, + * then call GetDIBits() with lpBits = NULL to have it fill in the + * biSizeImage field for us. + */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + *lpbi = bi; + + hDC = GetDC(NULL); + GetDIBits(hDC, hBitmap, 0, (WORD) bi.biHeight, + NULL, (LPBITMAPINFO) lpbi, DIB_RGB_COLORS); + ReleaseDC(NULL, hDC); + + /* If the driver did not fill in the biSizeImage field, + * fill it in -- NOTE: this is a bug in the driver! + */ + if (lpbi->biSizeImage == 0) + lpbi->biSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) * + lpbi->biHeight; + + /* Get the size of the memory block we need */ + dwLen = lpbi->biSize + PaletteSize((LPSTR) &bi) + lpbi->biSizeImage; + + /* Unlock the memory block */ + GlobalUnlock(hDIB); + + /* ReAlloc the buffer big enough to hold all the bits */ + hTemp = GlobalReAlloc(hDIB,dwLen,0); + if (hTemp) + return hTemp; + else + { + /* Else free memory block and return failure */ + GlobalFree(hDIB); + return NULL; + } +} + + +/************************************************************************* + * + * ChangeDIBFormat() + * + * Parameter: + * + * HDIB - handle to packed-DIB in memory + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert the bits per pixel and/or the compression + * format of the specified DIB. Note: If the conversion was unsuccessful, + * we return NULL. The original DIB is left alone. Don't use code like the + * following: + * + * hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4); + * + * The conversion will fail, but hMyDIB will now be NULL and the original + * DIB will now hang around in memory. We could have returned the old + * DIB, but we wanted to allow the programmer to check whether this + * conversion succeeded or failed. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * + ************************************************************************/ + +HDIB FAR ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression) +{ + HDC hDC; // Handle to DC + HBITMAP hBitmap; // Handle to bitmap + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap info + HDIB hNewDIB = NULL; // Handle to new DIB + HPALETTE hPal, hOldPal; // Handle to palette, prev pal + WORD DIBBPP, NewBPP; // DIB bits per pixel, new bpp + DWORD DIBComp, NewComp;// DIB compression, new compression + + /* Check for a valid DIB handle */ + if (!hDIB) + return NULL; + + /* Get the old DIB's bits per pixel and compression format */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + DIBBPP = ((LPBITMAPINFOHEADER)lpbi)->biBitCount; + DIBComp = ((LPBITMAPINFOHEADER)lpbi)->biCompression; + GlobalUnlock(hDIB); + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure */ + if (wBitCount == 0) + { + NewBPP = DIBBPP; + if ((dwCompression == BI_RLE4 && NewBPP == 4) || + (dwCompression == BI_RLE8 && NewBPP == 8) || + (dwCompression == BI_RGB)) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Save the old DIB's palette */ + hPal = CreateDIBPalette(hDIB); + if (!hPal) + return NULL; + + /* Convert old DIB to a bitmap */ + hBitmap = DIBToBitmap(hDIB, hPal); + if (!hBitmap) + { + DeleteObject(hPal); + return NULL; + } + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* Get a DC and select/realize our palette in it */ + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + if (hNewDIB) + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + + DeleteObject(hBitmap); + DeleteObject(hPal); + + return hNewDIB; +} + + +/************************************************************************* + * + * ChangeBitmapFormat() + * + * Parameter: + * + * HBITMAP - handle to a bitmap + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * HPALETTE - handle to palette + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert a bitmap to the specified bits per pixel + * and compression format. The bitmap and it's palette will remain + * after calling this function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * 12/11/91 Patrick Schreiber Destroy old DIB if conversion was + * successful. + * 12/16/91 Patrick Schreiber Modified from converting DIB to new + * DIB to bitmap to new DIB. Added palette + * parameter. + * + ************************************************************************/ + +HDIB FAR ChangeBitmapFormat(HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal) +{ + HDC hDC; // Screen DC + HDIB hNewDIB=NULL; // Handle to new DIB + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info. header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap header + HPALETTE hOldPal=NULL; // Handle to palette + WORD NewBPP; // New bits per pixel + DWORD NewComp; // New compression format + + /* Check for a valid bitmap handle */ + if (!hBitmap) + return NULL; + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure + */ + if (wBitCount == 0) + { + NewComp = dwCompression; + if (NewComp == BI_RLE4) + NewBPP = 4; + else if (NewComp == BI_RLE8) + NewBPP = 8; + else /* Not enough info */ + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* If we have a palette, get a DC and select/realize it */ + if (hPal) + { + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + } + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + if (hOldPal) + { + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + } + + if (hNewDIB) + { + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + } + + return hNewDIB; +} diff --git a/CODE/DIBUTIL.H b/CODE/DIBUTIL.H new file mode 100644 index 0000000..3360080 --- /dev/null +++ b/CODE/DIBUTIL.H @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +/* + * dibutil.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved. + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * AllocRoomForDIB() - Allocates memory for a DIB + * + */ + + +/* DIB constants */ +#define PALVERSION 0x300 + +/* DIB macros */ +#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER)) +#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left) +#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top) + +/* function prototypes */ +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap); diff --git a/CODE/DISPLAY.CPP b/CODE/DISPLAY.CPP new file mode 100644 index 0000000..d0a6e6d --- /dev/null +++ b/CODE/DISPLAY.CPP @@ -0,0 +1,4509 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DISPLAY.CPP 3 3/09/97 8:04p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DISPLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * DisplayClass::DisplayClass -- Default constructor for display class. * + * DisplayClass::Draw_It -- Draws the tactical map. * + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * DisplayClass::Init_Clear -- Clears the display to a known state. * + * DisplayClass::Init_IO -- Creates the map's button list * + * DisplayClass::Init_Theater -- Theater-specific initialization * + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * DisplayClass::Next_Object -- Searches for next object on display. * + * DisplayClass::One_Time -- Performs any special one time initializations. * + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/* +** These layer control elements are used to group the displayable objects +** so that proper overlap can be obtained. +*/ +LayerClass DisplayClass::Layer[LAYER_COUNT]; + +/* +** Fading tables +*/ +unsigned char DisplayClass::FadingBrighten[256]; +unsigned char DisplayClass::FadingShade[256]; +unsigned char DisplayClass::FadingWayDark[256]; +unsigned char DisplayClass::FadingLight[256]; +unsigned char DisplayClass::FadingGreen[256]; +unsigned char DisplayClass::FadingYellow[256]; +unsigned char DisplayClass::FadingRed[256]; +unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; +unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; +unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; +void const * DisplayClass::TransIconset; +unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::SpecialGhost[2*256]; + +void const * DisplayClass::ShadowShapes; +unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + +/* +** Bit array of cell redraw flags +*/ +BooleanVectorClass DisplayClass::CellRedraw; + +/* +** The main button that intercepts user input to the map +*/ +DisplayClass::TacticalClass DisplayClass::TacButton; + + +static int const TEX_X = 0; +static int const TEX_Y = 6; +static int const TEX_W = 14; + + +/*********************************************************************************************** + * DisplayClass::DisplayClass -- Default constructor for display class. * + * * + * This constructor for the display class just initializes some of the display settings. * + * Most settings are initialized with the correct values at the time that the Init function * + * is called. There are some cases where default values are wise and this routine fills * + * those particular ones in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + *=============================================================================================*/ +DisplayClass::DisplayClass(void) : + TacticalCoord(0), + TacLeptonWidth(0), + TacLeptonHeight(0), + ZoneCell(0), + ZoneOffset(0), + CursorSize(0), + ProximityCheck(false), + PendingObjectPtr(0), + PendingObject(0), + PendingHouse(HOUSE_NONE), + TacPixelX(0), + TacPixelY(0), + DesiredTacticalCoord(0), + IsToRedraw(true), + IsRepairMode(false), + IsSellMode(false), + IsTargettingMode(SPC_NONE), + IsRubberBand(false), + IsTentative(false), + IsShadowPresent(false), + BandX(0), + BandY(0), + NewX(0), + NewY(0) +{ + ShadowShapes = 0; + TransIconset = 0; + + Set_View_Dimensions(0, 8, 320/CELL_PIXEL_W, 200/CELL_PIXEL_H); +} + + +/*********************************************************************************************** + * DisplayClass::One_Time -- Performs any special one time initializations. * + * * + * This routine is called from the game initialization process. It is to perform any one * + * time initializations necessary for the map display system. It allocates the staging * + * buffer needed for the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called ONCE and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Handles layer system now. * + * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * + *=============================================================================================*/ +void DisplayClass::One_Time(void) +{ + MapClass::One_Time(); + + /* + ** Init the CellRedraw bit array. Do not do this in the constructor, since the + ** BooleanVector may not have been constructed yet. + */ + CellRedraw.Resize(MAP_CELL_TOTAL); + + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].One_Time(); + } + + /* + ** Load the generic transparent icon set. + */ + TransIconset = MFCD::Retrieve("TRANS.ICN"); + + #ifndef NDEBUG + RawFileClass file("SHADOW.SHP"); + if (file.Is_Available()) { + ShadowShapes = Load_Alloc_Data(file); + } else { + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + } + #else + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + #endif + + Set_View_Dimensions(0, 8 * RESFACTOR); +} + + +/*********************************************************************************************** + * DisplayClass::Init_Clear -- clears the display to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Clear(void) +{ + MapClass::Init_Clear(); + + /* + ** Clear any object being placed + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + CursorSize = 0; + IsTargettingMode = SPC_NONE; + IsRepairMode = false; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; + + /* + ** Empty all the display's layers + */ + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].Init(); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_IO -- clears & re-builds the map's button list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_IO(void) +{ + MapClass::Init_IO(); + /* + ** Re-attach our buttons to the main map button list, only in non-edit mode. + */ + if (!Debug_Map) { + TacButton.Zap(); + Add_A_Button(TacButton); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * + * * + * INPUT: * + * theater new theater * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + * 05/07/1996 JLB : Added translucent tables. * + *=============================================================================================*/ +void DisplayClass::Init_Theater(TheaterType theater) +{ + char fullname[16]; + static TLucentType const MouseCols[4] = { + {BLACK, BLACK, 110, 0}, + {WHITE, WHITE, 110, 0}, + {LTGREY, LTGREY, 110, 0}, + {DKGREY, DKGREY, 110, 0} + }; + static TLucentType const MagicCols[MAGIC_COL_COUNT] = { + {32,32,110,0}, + {33,33,110,0}, + {34,34,110,0}, + {35,35,110,0}, + {36,36,110,0}, + {37,37,110,0}, + {38,38,110,0}, + {39,39,110,0}, + {BLACK, BLACK, 200, 0}, + {WHITE, BLACK, 40, 0}, + {LTGREY, BLACK, 80, 0}, + {DKGREY, BLACK, 140, 0}, + {LTGREEN, BLACK,130,0} + }; + static TLucentType const WhiteCols[1] = { + {1, WHITE, 80, 0} + }; + static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { + {WHITE+1, BLACK,130,0}, + {WHITE, BLACK,170,0}, + {LTGRAY, BLACK,250,0}, + {DKGRAY, BLACK,250,0} + }; + static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,130,0} + }; + static TLucentType const UShadowColsAir[USHADOW_COL_COUNT] = { + {LTGREEN, WHITE,0,0} + }; + static TLucentType const UShadowColsSnow[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,75,0} + }; + + /* + ** Invoke parent's init routine. + */ + MapClass::Init_Theater(theater); + + /* + ** Save the new theater value + */ + Scen.Theater = theater; + + /* + ** Unload old mixfiles, and cache the new ones + */ + sprintf(fullname, "%s.MIX", Theaters[theater].Root); + +#ifndef WIN32 +LastTheater = THEATER_NONE; +#endif + + if (Scen.Theater != LastTheater) { + if (TheaterData != NULL) { + delete TheaterData; + } + TheaterData = new MFCD(fullname, &FastKey); + assert(TheaterData != NULL); + + bool theaterload = TheaterData->Cache(TheaterBuffer); + assert(theaterload); +// LastTheater = Scen.Theater; + } + + /* + ** Load the custom palette associated with this theater. + ** The fading palettes will have to be generated as well. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + PaletteClass const * ptr = (PaletteClass *)MFCD::Retrieve(fullname); + GamePalette = * ptr; + + OriginalPalette = GamePalette; + + Build_Fading_Table(GamePalette, FadingGreen, GREEN, 110); + + Build_Fading_Table(GamePalette, FadingYellow, YELLOW, 140); + + Build_Fading_Table(GamePalette, FadingRed, RED, 140); + + Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); + + Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); + + Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); + + Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); + + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsAir[0], USHADOW_COL_COUNT, UnitShadowAir); + memcpy(&UnitShadowAir[256], ColorRemaps[PCOLOR_GOLD].RemapTable, sizeof(ColorRemaps[PCOLOR_GOLD].RemapTable)); + if (theater == THEATER_SNOW) { + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsSnow[0], USHADOW_COL_COUNT, UnitShadow); + } else { + Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); + } + + if (theater == THEATER_SNOW) { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 75); + } else { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 130); + } + + Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); + + /* + ** Create the shadow color used by aircraft. + */ + Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); + for (int index = 0; index < 256; index++) { + SpecialGhost[index] = 0; + } + + Make_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); + + Make_Fading_Table(GamePalette, FadingWayDark, DKGRAY, 192); + + /* + ** Adjust the palette according to the visual control option settings. + */ + Options.Fixup_Palette(); +} + + +/*********************************************************************************************** + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * * + * This routine is used to create an overlap list that specifies all the cells that are * + * covered by the specified text string. This overlap list is used to handle map refresh * + * logic. * + * * + * INPUT: text -- Pointer to the text that would appear on the map and must have an * + * overlap list generated. * + * * + * x,y -- The coordinates that the text would appear (upper left corner). * + * * + * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * + * if were displayed at the coordinates specified. The list is actually a series of * + * offsets from the display's upper left corner cell number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 12/07/1994 JLB : Sidebar fixup. * + * 08/13/1995 JLB : Optimized for variable sized help text. * + *=============================================================================================*/ +short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y) const +{ + static short _list[60]; + int count = ARRAY_SIZE(_list); + + if (text != NULL) { + short * ptr = &_list[0]; + int len = String_Pixel_Width(text)+CELL_PIXEL_W; + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); + + /* + ** If the help text would spill into the sidebar, then flag this fact, but + ** shorten the apparent length so that the icon list calculation will + ** function correctly. + */ + if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { + len = right-x; + *ptr++ = REFRESH_SIDEBAR; + count--; + } + + /* + ** Build the list of overlap cell offset values according to the text + ** coordinate and the length. + */ + if (x <= right) { + CELL ul = Click_Cell_Calc(x, y-1); + CELL lr = Click_Cell_Calc(x+len-1, Bound(y+24, TacPixelY, TacPixelY+Lepton_To_Pixel(TacLeptonHeight) - 1)); + + if (ul == -1) ul = Click_Cell_Calc(x, y); + + if (ul != -1 && lr != -1) { + for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { + for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { + *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); + count--; + if (count < 2) break; + } + if (count < 2) break; + } + } + } + + *ptr = REFRESH_EOL; + } + return(_list); +} + + +/*********************************************************************************************** + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * * + * Use this routine to set the tactical map screen coordinates and dimensions. This routine * + * is typically used when the screen size or position changes as a result of the sidebar * + * changing position or appearance. * + * * + * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * + * corner. * + * * + * width -- The width of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * height-- The height of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 06/27/1995 JLB : Adjusts tactical map position if necessary. * + *=============================================================================================*/ +void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) +{ + if (width == -1) { + TacLeptonWidth = Pixel_To_Lepton(SeenBuff.Get_Width()-x); + } else { + TacLeptonWidth = width * CELL_LEPTON_W; + } + + if (height == -1) { + height = (SeenBuff.Get_Height()-y) / CELL_PIXEL_H; + } + TacLeptonHeight = height * CELL_LEPTON_H; + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight); + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * * + * This routine is used to set up the terrain cursor according to the size of the object * + * that is to be placed down. The terrain cursor looks like an arbitrary collection of * + * hatched square overlays. Typical use is when placing buildings. * + * * + * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * + * be marked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1994 JLB : Created. * + * 06/26/1995 JLB : Puts placement cursor into static buffer. * + *=============================================================================================*/ +void DisplayClass::Set_Cursor_Shape(short const * list) +{ + if (CursorSize) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + ZoneOffset = 0; + + if (list) { + int w,h; + static short _list[50]; + + memcpy(_list, list, sizeof(_list)); + CursorSize = _list; + Get_Occupy_Dimensions (w, h, CursorSize); + ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); + Cursor_Mark(ZoneCell+ZoneOffset, true); + } else { + CursorSize = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * house -- The house to base the proximity check upon. Typically this is the * + * player's house, but in multiplay, the computer needs to check for * + * proximity as well. * + * * + * list -- Pointer to the building's offset list. * + * * + * trycell -- The cell to base the offset list on. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + * 10/11/1994 BWG : Added IsProximate check for ore refineries * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const +{ + short const * ptr; + int retval = -1; + bool noradar = false; + bool nomapped = false; + bool shipyard = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = false; + } + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (list == NULL || trycell == 0) { + return(true); + } + + if (object == NULL || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + BuildingTypeClass const * building = (BuildingTypeClass const *)object; + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = list; +// ptr = CursorSize; + CELL cell = trycell; +// CELL cell = ZoneCell; + if (building->Adjacent == 1) { + while (*ptr != REFRESH_EOL && (retval == -1) ) { + cell = trycell + *ptr++; +// cell = ZoneCell + ZoneOffset + *ptr++; + + if (!In_Radar(cell)) { + retval = false; + noradar = true; + break; + } + + for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(newcell)) continue; + + if (!(*this)[newcell].IsMapped) { + nomapped = true; + } + TechnoClass * base = (*this)[newcell].Cell_Techno(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + //BG: Modified so only walls can be placed next to walls - buildings can't. + //JLB: Except for bibs, in which case buildings can be placed next to these. + if (building->IsWall || + ((*this)[newcell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newcell].Smudge).IsBib)) { + + if ((*this)[newcell].Owner == house) { + retval = true; + break; + } + } + + // we've found a building... + if (base != NULL && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == house && ((BuildingClass *)base)->Class->IsBase) { + retval = true; + break; + } + + /* BG: modifications to allow buildings one cell away from other buildings. + ** This is done by scanning each cell that fails the check (hence getting + ** to this point) and looking at the n/s/e/w adjacent cells to see if they + ** have buildings in them. If they do, and they match us, then succeed. + */ + if (retval != -1) break; + + for (FacingType newface = FACING_N; newface < FACING_COUNT; newface++) { + CELL newercell = Adjacent_Cell(newcell, newface); + + if (building->IsWall || + ((*this)[newercell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newercell].Smudge).IsBib)) { + + if ((*this)[newercell].Owner == house) { + retval = true; + break; + } + } + + TechnoClass * newbase = (*this)[newercell].Cell_Techno(); + + // we've found a building... + if (newbase != NULL && newbase->What_Am_I() == RTTI_BUILDING && newbase->House->Class->House == house && ((BuildingClass const *)newbase)->Class->IsBase) { + retval = true; + break; + } + } + if (retval != -1) break; + } + } + } + + if (retval == -1) retval = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = (retval != false); + } + + /* + ** If this object has special dispensation to be placed further than one cell from + ** other regular buildings, then check for this case now. Only bother to check if + ** it hasn't already been given permission to be placed down. + */ + if (!retval && !noradar && object->What_Am_I() == RTTI_BUILDINGTYPE) { + + // For land mines, let's make it check proximity within 10 squares + if (building->Adjacent > 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj != NULL && !obj->IsInLimbo && obj->House->Class->House == house && obj->Class->IsBase) { + int centdist = ::Distance(obj->Center_Coord(), Cell_Coord(cell)); + centdist /= CELL_LEPTON_W; + centdist -= (obj->Class->Width() + obj->Class->Height()) / 2; + if (centdist <= building->Adjacent) { + retval = true; + break; + } + } + } + } + } + + return((bool)retval); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * * + * This routine controls the location, display, and animation of the * + * tactical map cursor. * + * * + * INPUT: pos -- Position to move the cursor do. If -1 is passed then * + * the cursor will just be hidden. If the position * + * passed is the same as the last position passed in, * + * then animation could occur (based on timers). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + * 06/08/1994 JLB : If position is -1, then follow mouse. * + * 02/28/1995 JLB : Forces placement cursor to fit on map. * + *=============================================================================================*/ +CELL DisplayClass::Set_Cursor_Pos(CELL pos) +{ + CELL prevpos; // Last position of cursor (for jump-back reasons). + + /* + ** Follow the mouse position if no cell number is provided. + */ + if (pos == -1) { + pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + } + + if (CursorSize == NULL) { + prevpos = ZoneCell; + ZoneCell = pos; + return(prevpos); + } + + /* + ** Adjusts the position so that the placement cursor is never part way off the + ** tactical map. + */ + int w,h; + Get_Occupy_Dimensions(w, h, CursorSize); + + int x = Cell_X(pos + ZoneOffset); + int y = Cell_Y(pos + ZoneOffset); + + if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); + if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); + if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; + if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) y = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; + pos = XY_Cell(x, y) - ZoneOffset; + + /* + ** This checks to see if NO animation or drawing is to occur and, if so, + ** exits. + */ + if (pos == ZoneCell) return(pos); + + prevpos = ZoneCell; + + /* + ** If the cursor is visible, then handle the graphic update. + ** Otherwise, just update the global position of the cursor. + */ + if (CursorSize != NULL) { + + /* + ** Erase the old cursor (if it exists) AND the cursor is moving. + */ + if (pos != ZoneCell && ZoneCell != -1) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + /* + ** Render the cursor (could just be animation). + */ + if (pos != -1) { + Cursor_Mark(pos+ZoneOffset, true); + } + } + ZoneCell = pos; + ProximityCheck = Passes_Proximity_Check(PendingObject, PendingHouse, CursorSize, ZoneCell+ZoneOffset); + + return(prevpos); +} + + +/*********************************************************************************************** + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * * + * INPUT: * + * w ptr to fill in with height * + * h ptr to fill in with width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/31/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const * list) const +{ + int min_x = MAP_CELL_W; + int max_x = -MAP_CELL_W; + int min_y = MAP_CELL_H; + int max_y = -MAP_CELL_H; + int x,y; + + w = 0; + h = 0; + + if (!list) { + /* + ** Loop through all cell offsets, accumulating max & min x- & y-coords + */ + while (*list != REFRESH_EOL) { + /* + ** Compute x & y coords of the current cell offset. We can't use Cell_X() + ** & Cell_Y(), because they use shifts to compute the values, and if the + ** offset is negative we'll get a bogus coordinate! + */ + x = (*list) % MAP_CELL_W; + y = (*list) / MAP_CELL_H; + + max_x = max(max_x, x); + min_x = min(min_x, x); + max_y = max(max_y, y); + min_y = min(min_y, y); + + list++; + } + + w = max(1, max_x - min_x + 1); + h = min(1, max_y - min_y + 1); + } +} + + +/*********************************************************************************************** + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * * + * This routine will clear or set the cursor display bits on the map. * + * If the bit is set, then the cursor will be rendered on that map * + * icon. * + * * + * INPUT: pos -- Position of the upper left corner of the cursor. * + * * + * on -- Should the bit be turned on? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that every call to set the bits is matched by a * + * corresponding call to clear the bits. * + * * + * HISTORY: * + * 09/04/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DisplayClass::Cursor_Mark(CELL pos, bool on) +{ + CELL const * ptr; + CellClass * cellptr; + + if (pos == -1) return; + + /* + ** For every cell in the CursorSize list, invoke its Redraw_Objects and + ** toggle its IsCursorHere flag + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + if (on) { + cellptr->IsCursorHere = true; + } else { + cellptr->IsCursorHere = false; + } + } + } + + /* + ** For every cell in the PendingObjectPtr's Overlap_List, invoke its + ** Redraw_Objects routine. + */ + if (PendingObjectPtr) { + ptr = PendingObjectPtr->Overlap_List(); + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * * + * This routine is called once per game display frame (15 times per second). It handles * + * the mouse shape tracking and map scrolling as necessary. * + * * + * INPUT: input -- The next key just fetched from the input queue. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * + * set to 0. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1994 JLB : Created. * + * 06/02/1994 JLB : Filters mouse click input. * + * 06/07/1994 JLB : Fixed so template click will behave right. * + * 10/14/1994 JLB : Changing cursor shape over target. * + * 12/31/1994 JLB : Takes mouse coordinates as parameters. * + * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * + *=============================================================================================*/ +void DisplayClass::AI(KeyNumType & input, int x, int y) +{ + if ( + IsRubberBand && + (Get_Mouse_X() < TacPixelX || + Get_Mouse_Y() < TacPixelY || + Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || + Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { + Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); + } + + MapClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * * + * This routine is used to add an arbitrary (but tangible) game object to the map. It will * + * be rendered (made visible) once it is submitted to this function. This function builds * + * the list of game objects that get rendered each frame as necessary. It is possible to * + * submit the game object to different rendering layers. All objects in a layer get drawn * + * at the same time. Using this layer method it becomes possible to have objects "below" * + * other objects. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * layer -- The layer to add the object to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * + *=============================================================================================*/ +void DisplayClass::Submit(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Submit(object, (layer == LAYER_GROUND)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * * + * Every object that is to disappear from the map must be removed from the rendering * + * system. * + * * + * INPUT: object -- The object to remove. * + * * + * layer -- The layer to remove it from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + *=============================================================================================*/ +void DisplayClass::Remove(ObjectClass const * object, LayerType layer) +{ + assert(object != 0); + assert(object->IsActive); + + if (object) { + Layer[layer].Delete((ObjectClass *)object); + } +} + + +/*********************************************************************************************** + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * * + * This routine is used to determine the cell that is located at the * + * screen pixel coordinates given. Typical use is when the player * + * clicks with the mouse on the tactical map. * + * * + * INPUT: x,y -- Screen pixel coordinates. * + * * + * OUTPUT: Returns with cell that is under the coordinates specified. * + * If the coordinate specified is outside of the tactical * + * map, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL DisplayClass::Click_Cell_Calc(int x, int y) const +{ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); + return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); + } + return(-1); +} + + +/*********************************************************************************************** + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * * + * This routine is used to scroll the tactical map view in the desired * + * direction. It can also be used to determine if scrolling would be * + * legal without actually performing any scrolling action. * + * * + * INPUT: facing -- The direction to scroll the tactical map. * + * * + * distance -- The distance in leptons to scroll the map. * + * * + * really -- Should the map actually be scrolled? If false, * + * then only the legality of a scroll is checked. * + * * + * OUTPUT: bool; Would scrolling in the desired direction be possible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/20/1994 JLB : Converted to member function. * + * 08/09/1995 JLB : Added distance parameter. * + * 08/10/1995 JLB : Any direction scrolling. * + *=============================================================================================*/ +bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + /* + ** If the distance is invalid then no further checking is required. Bail + ** with a no-can-do flag. + */ + if (distance == 0) return(false); + FacingType crude = Dir_Facing(facing); + + if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { + if (crude == FACING_SW) facing = DIR_S; + if (crude == FACING_NW) facing = DIR_N; + } + if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { + if (crude == FACING_NW) facing = DIR_W; + if (crude == FACING_NE) facing = DIR_E; + } + if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { + if (crude == FACING_NE) facing = DIR_N; + if (crude == FACING_SE) facing = DIR_S; + } + if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { + if (crude == FACING_SE) facing = DIR_E; + if (crude == FACING_SW) facing = DIR_W; + } + + /* + ** Determine the coordinate that it wants to scroll to. + */ + COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); + + /* + ** Clip the new coordinate to the edges of the game world. + */ + int xx = (int)(short)Coord_X(coord) - (short)Cell_To_Lepton(MapCellX); + int yy = (int)(short)Coord_Y(coord) - (short)Cell_To_Lepton(MapCellY); + bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + if (xx < 0) { + xx = 0; + shifted = true; + } + if (yy < 0) { + yy = 0; + shifted = true; + } + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + /* + ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately + ** reflect the actual distance moved. + */ + if (shifted) { + distance = Distance(TacticalCoord, coord); + } + + /* + ** If the new coordinate is the same as the old, then no scrolling would occur. + */ + if (!distance || coord == TacticalCoord) return(false); + + /* + ** Since the new coordinate is different than the old one, possibly adjust the real + ** tactical map accordingly. + */ + if (really) { + Set_Tactical_Position(coord); + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Scrolled map REQUIRES all top layer units to be redrawn. + */ + for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * * + * This routine is used to flag all cells in the specified list for * + * redrawing. * + * * + * INPUT: cell -- The origin cell that the list is offset from. * + * * + * list -- Pointer to a list of offsets from the origin cell. * + * Each cell so specified is flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is rather slow (by definition). * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 08/01/1994 JLB : Simplified. * + *=============================================================================================*/ +void DisplayClass::Refresh_Cells(CELL cell, short const * list) +{ + short tlist[36]; + + if (*list == REFRESH_SIDEBAR) { + list++; + } + + List_Copy(list, ARRAY_SIZE(tlist), tlist); + short * tt = tlist; + while (*tt != REFRESH_EOL) { + CELL newcell = cell + *tt++; + if (In_Radar(newcell)) { + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell) const +{ + static char const _shadow[256]={ + -1,33, 2, 2,34,37, 2, 2, + 4,26, 6, 6, 4,26, 6, 6, + 35,45,17,17,38,41,17,17, + 4,26, 6, 6, 4,26, 6, 6, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 32,36,25,25,44,40,25,25, + 19,30,20,20,19,30,20,20, + 39,43,29,29,42,46,29,29, + 19,30,20,20,19,30,20,20, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2 + }; + + int index = 0, value = -1; + + /* + ** Don't map cells that are at the top or bottom edge. This solves + ** problem of accessing cells off the top or bottom of the map and into + ** who-knows-what memory. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); +#else + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); +#endif + //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + CellClass const * cellptr = &(*this)[cell]; + + /* + ** Presume solid black if that is what is here already. + */ + if (!cellptr->IsVisible && !cellptr->IsMapped) value = -2; + + if (cellptr->IsMapped /*&& !cellptr->IsVisible*/) { + /* + ** Build an index into the lookup table using all 8 surrounding cells. + ** We're mapping a revealed cell and we only care about the existence + ** of black cells. Bit numbering starts at the upper-right corner and + ** goes around the cell clockwise, so 0x80 = directly north. + */ + cellptr-= MAP_CELL_W + 1; + if (!cellptr->IsMapped) index |= 0x40; + cellptr++; + if (!cellptr->IsMapped) index |= 0x80; + cellptr++; + if (!cellptr->IsMapped) index |= 0x01; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x20; + cellptr += 2; + if (!cellptr->IsMapped) index |= 0x02; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x10; + cellptr++; + if (!cellptr->IsMapped) index |= 0x08; + cellptr++; + if (!cellptr->IsMapped) index |= 0x04; + + value = _shadow[index]; + } + return(value); +} + + +/*********************************************************************************************** + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * * + * This routine maps the specified cell. The cell must not already * + * have been mapped and the mapping player must be the human. * + * This routine will update any adjacent cell map icon as appropriate. * + * * + * INPUT: cell -- The cell to be mapped. * + * * + * house -- The player that is doing the mapping. * + * * + * OUTPUT: bool; Was action taken to map this cell? * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/24/1994 JLB : Takes pointer to HouseClass. * + * 02/20/1996 JLB : Allied units reveal the map for the player. * + *=============================================================================================*/ +bool DisplayClass::Map_Cell(CELL cell, HouseClass * house) +{ + /* + ** First check for the condition where we're spying on a house's radar + ** facility, to see if his mapping is applicable to us. + */ + if (house && house != PlayerPtr) { + if (house->RadarSpied & (1<<(PlayerPtr->Class->House))) house = PlayerPtr; + if (Session.Type == GAME_NORMAL && house->Is_Ally(PlayerPtr)) house = PlayerPtr; + } + + if (house != PlayerPtr || !In_Radar(cell)) return(false); + + CellClass * cellptr = &(*this)[cell]; + + /* + ** Don't bother remapping this cell if it is already mapped. + */ + if (cellptr->IsMapped) { + if (!cellptr->IsVisible) { + cellptr->Redraw_Objects(); + } + return(false); + } + + /* + ** Mark the cell as being mapped. This must be done first because + ** if the IsVisible flag must be set, then it might affect the + ** adjacent cell processing. + */ + cellptr->IsMapped = true; + cellptr->Redraw_Objects(); + if (Cell_Shadow(cell) == -1) { + cellptr->IsVisible = true; + } + + /* + ** Check out all adjacent cells to see if they need + ** to be mapped as well. This is necessary because of the + ** "unique" method of showing shadowed cells. Many combinations + ** are not allowed, and to fix this, just map the cells until + ** all is ok. + */ + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + int shadow; + CELL c; + + c = Adjacent_Cell(cell, dir); + CellClass * cptr = &(*this)[c]; + cptr->Redraw_Objects(); + + if (c != cell && !cptr->IsVisible) { + shadow = Cell_Shadow(c); + + if (shadow == -1) { + if (!cptr->IsMapped) { + Map_Cell(c, house); + } else { + cptr->IsVisible = true; + } + } else { + if (shadow != -2 && !cptr->IsMapped) { + Map_Cell(c, house); + } + } + } + } + + TechnoClass * tech = (*this)[cell].Cell_Techno(); + if (tech) { + tech->Revealed(house); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * * + * This is the routine that figures out the location on the screen for * + * a specified coordinate. It is one of the fundamental routines * + * necessary for rendering the game objects. It performs some quick * + * tests to see if the coordinate is in a visible region and returns * + * this check as a boolean value. * + * * + * INPUT: coord -- The coordinate to check. * + * * + * x,y -- Reference to the pixel coordinates that this * + * coordinate would be when rendered. * + * * + * OUTPUT: bool; Is this coordinate in a visible portion of the map? * + * * + * WARNINGS: If the coordinate is not in a visible portion of the * + * map, then this X and Y parameters are not set. * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 12/15/1994 JLB : Converted to member function. * + * 01/07/1995 JLB : Uses inline functions to extract coord components. * + * 08/09/1995 JLB : Uses new coordinate system. * + *=============================================================================================*/ +#define EDGE_ZONE (CELL_LEPTON_W*2) +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const +{ + if (coord) { + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + + xoff = (xoff+EDGE_ZONE) - xtac; + if ((unsigned)xoff <= TacLeptonWidth + EDGE_ZONE*2) { + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + + yoff = (yoff+EDGE_ZONE) - ytac; + if ((unsigned)yoff <= TacLeptonHeight + EDGE_ZONE*2) { + x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; + y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * + * * + * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * + * be within the region bounded by TacMapX,Y - + TacMapW,H. * + * * + * INPUT: source, dest -- References to the coordinates to check. * + * * + * * + * OUTPUT: bool; Are these coordinates in a visible portion of the map? * + * Returns true if the pushed source & dest are visible, but if neither are * + * within the map, then it returns false. * + * * + * * + * HISTORY: * + * 03/27/1995 BWG : Created. * + *=============================================================================================*/ +bool DisplayClass::Push_Onto_TacMap(COORDINATE & source, COORDINATE & dest) +{ + if (!source || !dest) return(false); + + int x1 = Coord_X(source); + int y1 = Coord_Y(source); + int x2 = Coord_X(dest); + int y2 = Coord_Y(dest); + int left = Coord_X(TacticalCoord); + int right = Coord_X(TacticalCoord) + TacLeptonWidth; + int top = Coord_Y(TacticalCoord); + int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; + + if (x1 < left && x2 < left) return(false); + if (x1 > right && x2 > right) return(false); + if (y1 < top && y2 < top) return(false); + if (y1 > bottom && y2 > bottom) return(false); + + x1 = Bound(x1, left, right); + x2 = Bound(x2, left, right); + y1 = Bound(y1, top, bottom); + y2 = Bound(y2, top, bottom); + + source = XY_Coord(x1, y1); + dest = XY_Coord(x2, y2); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * * + * This routine is used to determine what the player has clicked on. * + * It is passed the cell that the click was on and it then examines * + * the cell and returns with a pointer to the object that is there. * + * * + * INPUT: cell -- The cell that has been clicked upon. * + * * + * x,y -- Optional offsets from the upper left corner of the cell to be used in * + * determining exactly which object in the cell is desired. * + * * + * OUTPUT: Returns with a pointer to the object that is "clickable" in * + * the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) const +{ + return(*this)[cell].Cell_Object(x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Draw_It -- Draws the tactical map. * + * * + * This will draw the tactical map at the recorded position. This * + * routine is used whenever the tactical map moves or needs to be * + * completely redrawn. It will handle making the necessary adjustments * + * to accomodate a moving cursor. * + * * + * INPUT: forced -- bool; force redraw of the entire display? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1991 JLB : Created. (benchmark = 292) * + * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * + * 04/15/1991 JLB : Added actual map reference for terrain (207) * + * 04/16/1991 JLB : _cell2meta converted to int (194) * + * 04/16/1991 JLB : References actual CellIcon[] array (204) * + * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * + * 04/17/1991 JLB : Cell based tactical map rendering (165) * + * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * + * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * + * 04/23/1991 JLB : Map active location cursor (334) * + * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * + * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * + * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * + * 05/12/1992 JLB : Destination page support. * + * 02/14/1994 JLB : Revamped. * + * 05/01/1994 JLB : Converted to member function. * + * 12/15/1994 JLB : Updated to work with display hierarchy. * + * 12/24/1994 JLB : Examines redraw bit intelligently. * + * 12/24/1994 JLB : Combined with old Refresh_Map() function. * + * 01/10/1995 JLB : Rubber band drawing. * + *=============================================================================================*/ + void DisplayClass::Draw_It(bool forced) +{ + int x,y; // Working cell index values. + + MapClass::Draw_It(forced); + + if (IsToRedraw || forced) { + BStart(BENCH_TACTICAL); + IsToRedraw = false; + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + Refresh_Band(); + + /* + ** Mark all cells under the vortex to be redrawn + */ + ChronalVortex.Set_Redraw(); + + + /* + ** If the multiplayer message system is displaying one or more messages, + ** flag all cells covered by the messages to redraw. This will prevent + ** messages from smearing the map if it scrolls. + */ + int num = Session.Messages.Num_Messages(); + if (num > 0) { + for (CELL cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + if (num > 1) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 2) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 3) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + } + + /* + ** Check for a movement of the tactical map. If there has been some + ** movement, then part (or all) of the icons must be redrawn. + */ + if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || + Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { + + int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); + int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); + + int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. + int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; + + int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. + int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. + + if (oldw < 1) forced = true; + if (oldh < 1) forced = true; + + +#ifdef WIN32 //For WIN32 only redraw the edges of the map that move into view + + /* + ** Work out which map edges need to be redrawn + */ + BOOL redraw_right = (oldx < 0) ? true : false; //Right hand edge + BOOL redraw_left = (oldx > 0) ? true : false; //Left hand edge + BOOL redraw_bottom= (oldy < 0) ? true : false; //Bottom edge + BOOL redraw_top = (oldy > 0) ? true : false; //Top edge + + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + /* + ** If hid page is in video memory then blit from the seen page to avoid blitting + ** an overlapped region. + */ + if (HidPage.Get_IsDirectDraw()) { + Hide_Mouse(); + SeenBuff.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + Show_Mouse(); + } else { + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } + + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** + ** Set the 'redraw stamp' bit for any cells that could not be copied. + ** + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + + if (abs(oldx) < 0x25 && abs(oldy) < 0x25) { + + /* + ** The width of the area we redraw depends on the scroll speed + */ + int extra_x = (abs(oldx)>=16) ? 2 : 1; + int extra_y = (abs(oldy)>=16) ? 2 : 1; + + /* + ** Flag the cells across the top of the visible area if required + */ + if (redraw_top) { + for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells across the bottom of the visible area if required + */ + if (redraw_bottom) { + for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the left of the visible area if required + */ + if (redraw_left) { + for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the right of the visible area if required + */ + if (redraw_right) { + for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + } else { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } + + +#else //WIN32 + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } +#endif + + /* + ** If the entire tactical map is forced to be redrawn, then set all the redraw flags + ** and let the normal processing take care of the rest. + */ + if (forced) { + CellRedraw.Set(); + } + + /* + ** The first order of business is to redraw all the underlying icons that are + ** flagged to be redrawn. + */ + if (HidPage.Lock()) { + Redraw_Icons(); + + /* + ** Draw the infantry bodies in this special layer. + */ +// for (int index = 0; index < Anims.Count(); index++) { +// AnimClass * anim = Anims.Ptr(index); +// if (*anim >= ANIM_CORPSE1 && *anim <= ANIM_CORPSE3) { +// anim->Render(forced); +// } +// } + +#ifdef SORTDRAW + Redraw_OIcons(); +#endif + + HidPage.Unlock(); + } + +#ifndef WIN32 + /* + ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom + ** area one line below the screen. This causes the predator effect to work on any + ** shape drawn at the bottom of the screen. + */ + HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); +#endif + + if (HidPage.Lock()) { + + /* + ** Draw the vortex effect over the terrain + */ + ChronalVortex.Render(); + + /* + ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer + ** first and then followed by all the layers in increasing altitude. + */ + BStart(BENCH_OBJECTS); + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + for (int index = 0; index < Layer[layer].Count(); index++) { + ObjectClass * ptr = Layer[layer][index]; + +#ifdef SORTDRAW + /* + ** Techno objects are drawn as part of the cell redraw process since techno + ** objects in the ground layer are handled by the Occupier and Overlapper + ** pointer lists. + */ + if (!Debug_Map && ptr->Is_Techno() && layer == LAYER_GROUND && ((TechnoClass*)ptr)->Visual_Character() == VISUAL_NORMAL) continue; +#endif + +// if (ptr->What_Am_I() == RTTI_ANIM && *((AnimClass*)ptr) >= ANIM_CORPSE1 && *((AnimClass*)ptr) <= ANIM_CORPSE3) { +// continue; +// } + assert(ptr->IsActive); + ptr->Render(forced); + } + } + BEnd(BENCH_OBJECTS); + + //ChronalVortex.Render(); + /* + ** Finally, redraw the shadow overlay as necessary. + */ + BStart(BENCH_SHROUD); + Redraw_Shadow(); + BEnd(BENCH_SHROUD); + } + HidPage.Unlock(); + +#ifdef SORTDRAW + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + Layer[LAYER_GROUND][index]->IsToDisplay = false; + } +#endif + + /* + ** Draw the rubber band over the top of it all. + */ + if (IsRubberBand) { + LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); + } + + /* + ** Clear the redraw flags so that normal redraw flag setting can resume. + */ + CellRedraw.Reset(); + +#ifdef SCENARIO_EDITOR + /* + ** If we're placing an object (PendingObject is non-NULL), and that object + ** is NOT an icon, smudge, or overlay, draw it here. + ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; + ** they're drawn at the upper left coord, so I have to AND the coord value + ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the + ** cell coordinates. + */ + if (Debug_Map && PendingObjectPtr) { + PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); + PendingObjectPtr->Render(true); + } +#endif + BEnd(BENCH_TACTICAL); + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * * + * This routine will redraw all of the terrain icons that are flagged * + * to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + * 06/20/1994 JLB : Uses cell drawing support function. * + * 12/06/1994 JLB : Scans tactical view in separate row/column loops * + * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * + *=============================================================================================*/ +void DisplayClass::Redraw_Icons(void) +{ + IsShadowPresent = false; + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (cellptr->IsMapped || Debug_Unshroud) { + cellptr->Draw_It(xpixel, ypixel); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + if (!cellptr->IsVisible && !Debug_Unshroud) { + IsShadowPresent = true; + } + } + } + } + } +} + + +#ifdef SORTDRAW +void DisplayClass::Redraw_OIcons(void) +{ + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (cellptr->IsMapped || Debug_Unshroud) { + cellptr->Draw_It(xpixel, ypixel, true); + } + } + } + } + } +} +#endif + + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redrawn are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + if (cellptr->IsVisible) continue; + int shadow = -2; + if (cellptr->IsMapped) { + shadow = Cell_Shadow(cell); + } + if (shadow >= 0) { + CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); + } else { + if (shadow != -1) { + int ww = CELL_PIXEL_W; + int hh = CELL_PIXEL_H; + + if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { + LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); + } + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Next_Object -- Searches for next object on display. * + * * + * This utility routine is used to find the "next" object from the object specified. This * + * is typically used when is pressed and the current object shifts. * + * * + * INPUT: object -- The current object to base the "next" calculation off of. * + * * + * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * + * then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Next_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (unsigned uindex = 0; uindex < Layer[LAYER_GROUND].Count(); uindex++) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * * + * This routine will search for the previous object. Previous is defined as the one listed * + * before the specified object in the ground layer. If there is no specified object, then * + * the last object in the ground layer is returned. * + * * + * INPUT: object -- Pointer to the object that "previous" is to be defined from. * + * * + * OUTPUT: Returns with a pointer to the object previous to the specified one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * * + * INPUT: * + * x,y pixel coordinates to convert * + * * + * OUTPUT: * + * COORDINATE of pixel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/09/1994 BR : Created. * + * 12/06/1994 JLB : Uses map dimension variables in display class. * + * 12/10/1994 JLB : Uses union to speed building coordinate value. * + *=============================================================================================*/ +COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) const +{ + /* + ** Normalize the pixel coordinates to be relative to the upper left corner + ** of the tactical map. The coordinates are expressed in leptons. + */ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + /* + ** If pixel coordinate is over the tactical map, then translate it into a coordinate + ** value. If not, then just return with NULL. + */ + if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + return(Coord_Add(TacticalCoord, XY_Coord(x, y))); + } + return(0); +} + + +/*********************************************************************************************** + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * * + * Find a cell meeting the specified requirements. This function is * + * used for scenario reinforcements. * + * * + * INPUT: dir -- Method of picking a map cell. * + * * + * waypoint -- Closest waypoint to use for finding appropriate map edge. * + * * + * cell -- Cell to find closest edge to if waypoint not specified. * + * * + * loco -- The locomotion of the reinforcements that are trying to enter. * + * * + * zonecheck -- Is zone checking required? * + * * + * mzone -- The movement zone type to check against (only if zone checking). * + * * + * OUTPUT: Returns with the calculated cell. If 0, then this indicates * + * that no legal cell was found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/11/1994 JLB : Revamped. * + * 05/18/1994 JLB : Converted to member function. * + * 12/18/1995 JLB : Handles edge preference scan. * + * 06/24/1996 JLB : Removed Dune II legacy code. * + * 06/25/1996 JLB : Rewrote and greatly simplified. * + * 10/05/1996 JLB : Checks for zone and crushable status. * + *=============================================================================================*/ +CELL DisplayClass::Calculated_Cell(SourceType dir, WAYPOINT waypoint, CELL cell, SpeedType loco, bool zonecheck, MZoneType mzone) const +{ + bool vert = false; + bool horz = false; + int x = 0; + int y = 0; + CELL punt = 0; // If all else fails, return this cell location. + int zone = -1; // Tentative zone for legality checking. + + /* + ** Waypoint edge detection for ground based reinforcements that have a waypoint origin are + ** determined by finding the closest map edge to the waypoint. Reinforcement location + ** scanning starts from that position. + */ + CELL trycell = -1; + if (waypoint != -1) { + trycell = Scen.Waypoint[waypoint]; + } + if (trycell == -1) { + trycell = cell; + } + + /* + ** If zone checking is requested, then find the correct zone to use. + */ + if (zonecheck && trycell != -1) { + zone = (*this)[trycell].Zones[mzone]; + } + + /* + ** If the cell or waypoint specified as been detected as legal, then set up the map edge + ** scanning values accordingly. + */ + if (trycell != -1) { + x = Cell_X(trycell) - MapCellX; + x = min(x, (-Cell_X(trycell) + (MapCellX+MapCellWidth))); + + y = Cell_Y(trycell) - MapCellY; + y = min(y, (-Cell_Y(trycell) + (MapCellY+MapCellHeight))); + + if (x < y) { + vert = true; + horz = false; + if ((Cell_X(trycell)-MapCellX) < MapCellWidth/2) { + x = -1; + } else { + x = MapCellWidth; + } + y = Cell_Y(trycell) - MapCellY; + + } else { + + vert = false; + horz = true; + if ((Cell_Y(trycell)-MapCellY) < MapCellHeight/2) { + y = -1; + } else { + y = MapCellHeight; + } + x = Cell_X(trycell) - MapCellX; + } + } + + /* + ** If no map edge can be inferred from the waypoint, then go with the + ** map edge specified by the edge parameter. + */ + if (!vert && !horz) { + switch (dir) { + default: + case SOURCE_NORTH: + horz = true; + y = -1; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_SOUTH: + horz = true; + y = MapCellHeight; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_EAST: + vert = true; + x = MapCellWidth; + y = Random_Pick(0, MapCellHeight-1); + break; + + case SOURCE_WEST: + vert = true; + x = -1; + y = Random_Pick(0, MapCellHeight-1); + break; + } + } + + /* + ** Determine the default reinforcement cell if all else fails. + */ + punt = XY_Cell(x + MapCellX, y + MapCellY); + + /* + ** Scan through the vertical and horizontal edges of the map looking for + ** a relatively clear cell for object placement. The cell scanned is + ** from the edge position specified by the X and Y variables. + */ + if (vert) { + int modifier = (x > MapCellX) ? -1 : 1; + + for (int index = 0; index < MapCellHeight; index++) { + CELL trycell = XY_Cell(x + MapCellX, ((y + index) % MapCellHeight) + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + if (horz) { + int modifier = (y > MapCellY) ? -MAP_CELL_W : MAP_CELL_W; + + for (int index = 0; index < MapCellWidth; index++) { + CELL trycell = XY_Cell(((x + index) % MapCellWidth) + MapCellX, y + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + /* + ** If there was no success in finding a suitable reinforcement edge cell, then return + ** with the default 'punt' cell location. + */ + return(punt); +} + + +/*********************************************************************************************** + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * * + * This routine will check the secified cell (given the specified conditions) and determine * + * if that is a good cell for reinforcement purposes. It checks for passability of the cell * + * as well as zone and whether blocking walls can be crushed. * + * * + * INPUT: outcell -- The cell that is just outside the edge of the map. * + * * + * incell -- The cell that is just inside the edge of the map. * + * * + * loco -- The locomotion type of the reinforcement. * + * * + * zone -- The zone that the eventual movement destination lies. A reinforcement * + * edge must fall within the same zone. * + * * + * mzone -- The zone check type to check against (if zone checking required) * + * * + * OUTPUT: bool; Is the specified cell good for reinforcement purposes? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const +{ + /* + ** If the map edge location is not clear for object placement, then this is not + ** a good cell for reinforcement purposes. + */ + if (!(*this)[outcell].Is_Clear_To_Move(loco, false, false)) { + return(false); + } + + /* + ** If it looks like the on-map cell cannot be driven on to, then return with + ** the failure code. + */ + if (!(*this)[incell].Is_Clear_To_Move(loco, false, false, zone, mzone)) { + return(false); + } + + /* + ** If the reinforcement cell is already occupied, then return a failure code. + */ + if ((*this)[outcell].Cell_Techno() != NULL) { + return(false); + } + if ((*this)[incell].Cell_Techno() != NULL) return(false); + + /* + ** All tests have passed, return with success code. + */ +//Mono_Printf("<%04X>\n", incell);Keyboard->Get(); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * * + * Use this routine to simultaneously select all objects within the coordinate region * + * specified. This routine is used by the multi-select rubber band handler. * + * * + * INPUT: coord1 -- Coordinate of one corner of the selection region. * + * * + * coord2 -- The opposite corner of the selection region. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 04/25/1995 JLB : Limited to non-building type. * + * 03/06/1996 JLB : Allows selection of aircraft with bounding box. * + *=============================================================================================*/ +void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2) +{ + COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; + + coord1 = Coord_Add(tcoord, coord1); + coord2 = Coord_Add(tcoord, coord2); + int x1 = Coord_X(coord1); + int x2 = Coord_X(coord2); + int y1 = Coord_Y(coord1); + int y2 = Coord_Y(coord2); + + /* + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. + */ + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + /* + ** Sweep through all ground layer objects and select the ones within the + ** bounding box. + */ + Unselect_All(); + AllowVoice = true; + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Layer[LAYER_GROUND][index]; + COORDINATE ocoord = obj->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are owned by the player, are allowed to be + ** selected, and are within the bounding box. + */ + HouseClass * hptr = HouseClass::As_Pointer(obj->Owner()); + if ( (hptr != NULL && hptr->IsPlayerControl) && + obj->Class_Of().IsSelectable && + obj->What_Am_I() != RTTI_BUILDING && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + if (obj->Select()) { + AllowVoice = false; + } + } + } + + /* + ** Select any aircraft with the bounding box. + */ + for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { + AircraftClass * aircraft = Aircraft.Ptr(air_index); + COORDINATE ocoord = aircraft->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are owned by the player, are allowed to be + ** selected, and are within the bounding box. + */ + if ( aircraft->House->IsPlayerControl && + aircraft->Class->IsSelectable && + !aircraft->IsSelected && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + + if (aircraft->Select()) { + AllowVoice = false; + } + } + } + + AllowVoice = true; +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * * + * Use this routine to flag all cells that are covered in some fashion by the multi-unit * + * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * + * size or is being removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Refresh_Band(void) +{ + if (IsRubberBand) { + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + int x1 = BandX+TacPixelX; + int y1 = BandY+TacPixelY; + int x2 = NewX+TacPixelX; + int y2 = NewY+TacPixelY; + + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + CELL cell; + for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { + cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * * + * This routine handles the input directed at the tactical map. Since input, in this * + * regard, includes even the presence of the mouse over the tactical map, this routine * + * is called nearly every game frame. It handles adjusting the mouse shape as well as * + * giving orders to units. * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/17/1995 JLB : Created. * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + int x,y; // Sub cell pixel coordinates. + bool shadow; + ObjectClass * object = 0; + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1); + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + if (coord) { + + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + if (object != NULL && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && (((TechnoClass *)object)->Cloak == CLOAKED || ((TechnoClass *)object)->Techno_Type_Class()->IsInvisible)) { + object = NULL; + } + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object != NULL) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + + } else { + + if (object && object->Class_Of().IsSelectable) { + action = ACTION_SELECT; + } + + if (Map.IsRepairMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { + action = ACTION_REPAIR; + } else { + action = ACTION_NO_REPAIR; + } + } + + if (Map.IsSellMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { + if (object->What_Am_I() == RTTI_BUILDING) { + action = ACTION_SELL; + } else { + action = ACTION_SELL_UNIT; + } + } else { + + /* + ** Check to see if the cursor is over an owned wall. + */ + if (Map[cell].Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && + Map[cell].Owner == PlayerPtr->Class->House) { + action = ACTION_SELL; + } else { + action = ACTION_NO_SELL; + } + } + } + + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + action = ACTION_NUKE_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_BOMB) { + action = ACTION_PARA_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_INFANTRY) { + action = ACTION_PARA_INFANTRY; + } + + if (Map.IsTargettingMode == SPC_SPY_MISSION) { + action = ACTION_SPY_MISSION; + } + + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + action = ACTION_IRON_CURTAIN; + } + + if (Map.IsTargettingMode == SPC_CHRONOSPHERE) { + action = ACTION_CHRONOSPHERE; + } + + if (Map.IsTargettingMode == SPC_CHRONO2) { + action = ACTION_CHRONO2; + if (shadow) action = ACTION_NOMOVE; + ObjectClass const * tobject = As_Object(PlayerPtr->UnitToTeleport); + + /* + ** Determine if the object can be teleported to the destination cell. + */ + if (tobject != NULL && tobject->Is_Techno()) { + TechnoClass const * uobject = (TechnoClass const *)tobject; + if (!uobject->Can_Teleport_Here(cell)) { +// if (((UnitClass *)As_Object(PlayerPtr->UnitToTeleport))->Can_Enter_Cell(cell, FACING_NONE) != MOVE_OK) { + action = ACTION_NOMOVE; + } + + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else { // If the object is no longer valid, cancel targetting mode. + action = ACTION_NOMOVE; + Map.IsTargettingMode = SPC_NONE; + } +#endif + } + + if (Map.PendingObject) { + action = ACTION_NONE; + } + } + + /* + ** Move any cursor displayed. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** A right mouse button press cancels the current action or selection. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** Make sure that if the mouse button has been released and the map doesn't know about it, + ** then it must be informed. Do this by faking a mouse release event. + */ + if ((flags & LEFTUP) && Map.IsRubberBand) { + flags |= LEFTRELEASE; + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (!edge) { + if (flags & LEFTUP) { + Map.Mouse_Left_Up(cell, shadow, object, action); + } + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTRELEASE) { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + + /* + ** When the mouse is first pressed on the map, then record the mouse + ** position so that a proper check before going into rubber band + ** mode can be made. Rubber band mode starts when the mouse is + ** held down and moved a certain minimum distance. + */ + if (!edge && (flags & LEFTPRESS)) { + Map.Mouse_Left_Up(cell, shadow, object, action); + Map.Mouse_Left_Press(x, y); + } + + /* + ** While the mouse is being held down, determine if rubber band mode should + ** start. If rubber band mode is already active, then update the size + ** and flag the map to redraw it. + */ + if (flags & LEFTHELD) { + Map.Mouse_Left_Held(x, y); + } + } + + return(GadgetClass::Action(0, key)); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * * + * This routine is called when the right mouse button is pressed. This action is supposed * + * to cancel whatever mode or process is active. If there is nothing to cancel, then it * + * will default to unselecting any units that might be currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Right_Press(void) +{ + if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { + //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + } else { + if (IsRepairMode) { + IsRepairMode = false; + } else { + if (IsSellMode) { + IsSellMode = false; + } else { + if (IsTargettingMode != SPC_NONE) { + IsTargettingMode = SPC_NONE; + } else { + Unselect_All(); + } + } + } + } + + // If it breaks... call 228. + Set_Default_Mouse(MOUSE_NORMAL, Map.IsSmall); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * * + * This routine is called continuously while the mouse is over the tactical map but there * + * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * + * help text. * + * * + * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * + * * + * object -- Pointer to the object that the mouse is currently over (may be NULL). * + * * + * action -- This is the action that the currently selected object (if any) will * + * perform if the left mouse button were clicked at this location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall) +{ + IsTentative = false; + + TARGET target = TARGET_NONE; + if (object != NULL) { + target = object->As_Target(); + } else { + if (cell != -1) { + target = As_Target(cell); + } + } + + /* + ** Don't allow selection of an object that is located in shadowed terrain. + ** In fact, just show the normal move cursor in order to keep the shadowed + ** terrain a mystery. + */ + if (shadow) { + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_NONE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_NO_SELL: + case ACTION_SELL: + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + case ACTION_NOMOVE: + if (CurrentObject.Count() && CurrentObject[0]->What_Am_I() == RTTI_AIRCRAFT) { + Set_Default_Mouse(MOUSE_NO_MOVE, wsmall); + break; + } + // Fall into next case for non aircraft object types. + + default: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + } + } else { + + /* + ** Change the mouse shape according to the default action that will occur + ** if the mouse button were clicked at this location. + */ + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_DAMAGE, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_GREPAIR, wsmall); + break; + + case ACTION_TOGGLE_SELECT: + case ACTION_SELECT: + Set_Default_Mouse(MOUSE_CAN_SELECT, wsmall); + break; + + case ACTION_MOVE: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_ATTACK: + if (Target_Legal(target) && CurrentObject.Count() == 1 && CurrentObject[0]->Is_Techno() && ((TechnoClass *)CurrentObject[0])->In_Range(target, 0)) { + Set_Default_Mouse(MOUSE_STAY_ATTACK, wsmall); + break; + } + // fall into next case. + + case ACTION_HARVEST: + Set_Default_Mouse(MOUSE_CAN_ATTACK, wsmall); + break; + + case ACTION_SABOTAGE: + Set_Default_Mouse(MOUSE_DEMOLITIONS, wsmall); + break; + + case ACTION_ENTER: + case ACTION_CAPTURE: + Set_Default_Mouse(MOUSE_ENTER, wsmall); + break; + + case ACTION_NOMOVE: + Set_Default_Mouse(MOUSE_NO_MOVE, wsmall); + break; + + case ACTION_NO_SELL: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_SELF: + Set_Default_Mouse(MOUSE_DEPLOY, wsmall); + break; + + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_REPAIR, wsmall); + break; + + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_SELL_UNIT, wsmall); + break; + + case ACTION_SELL: + Set_Default_Mouse(MOUSE_SELL_BACK, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + default: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + } + } + + /* + ** Never display help text if the mouse is held over the radar map. + */ + if (wsmall) { + return; + } + + /* + ** Give a generic help message when over shadow terrain. + */ + if (shadow) { +// if (Scen.Scenario < 4) { + Help_Text(TXT_SHADOW); +// } else { +// Help_Text(TXT_NONE); +// } + } else { + + /* + ** If the mouse is held over objects on the map, then help text may + ** pop up that tells what the object is. This call informs the help + ** system of the text name for the object under the mouse. + */ + if (object != NULL) { + int text; + int color = LTGREY; + + /* + ** Fetch the appropriate background color for help text. + */ + if (PlayerPtr->Is_Ally(object)) { + color = GREEN; + } else { + if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { + color = LTGREY; + } else { + color = PINK; + } + } + + /* + ** Fetch the name of the object. If it is an enemy object, then + ** the exact identity is glossed over with a generic text. + */ + text = object->Full_Name(); + if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) { + + if (!((TechnoClass *)object)->House->Is_Ally(PlayerPtr)) { +// if (!PlayerPtr->Is_Ally(object)) { + switch (object->What_Am_I()) { + case RTTI_INFANTRY: + text = TXT_ENEMY_SOLDIER; + break; + + case RTTI_UNIT: + text = TXT_ENEMY_VEHICLE; + break; + + case RTTI_BUILDING: + text = TXT_ENEMY_STRUCTURE; + break; + } + } + } + + if (/*Scen.Scenario > 3 ||*/ object->What_Am_I() != RTTI_TERRAIN) { + Help_Text(text, -1, -1, color); + } else { + Help_Text(TXT_NONE); + } + } else { + if ((*this)[cell].Land_Type() == LAND_TIBERIUM) { + Help_Text(TXT_MINERALS); + } else { + Help_Text(TXT_NONE); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * * + * This routine is called when the left mouse button is released over the tactical map. * + * The release event is the workhorse of the game. Most actions occur at the moment of * + * mouse release. * + * * + * INPUT: cell -- The cell that the mouse is over. * + * * + * x,y -- The mouse pixel coordinate. * + * * + * object -- Pointer to the object that the mouse is over. * + * * + * action -- The action that the currently selected object (if any) will * + * perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 03/27/1995 JLB : Handles sell and repair actions. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall) +{ + if (PendingObjectPtr) { + /* + ** Try to place the pending object onto the map. + */ + if (ProximityCheck) { + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); + } else { + Speak(VOX_DEPLOY); + } + + } else { + + if (IsRubberBand) { + Refresh_Band(); + Select_These(XYP_Coord(BandX, BandY), XYP_Coord(x, y)); + + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + IsRubberBand = false; + IsTentative = false; + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + + } else { + + /* + ** Toggle the select state of the object. + */ + if (action == ACTION_TOGGLE_SELECT) { + if (!object || !CurrentObject.Count() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + action = ACTION_SELECT; + } else { + if (object->IsSelected) { + object->Unselect(); + } else { + object->Select(); + } + } + } + + /* + ** Selection of other object action. + */ + if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->IsSelected)) { + Unselect_All(); + object->Select(); + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + } + + /* + ** If an action was detected as possible, then pass this action event + ** to all selected objects. + */ + if (action != ACTION_NONE && action != ACTION_SELECT) { + + /* + ** Pass the action to all the selected objects. But first, redetermine + ** what action that object should perform. This, seemingly redundant + ** process, is necessary since multiple objects could be selected and each + ** might perform a different action when the click occurs. + */ + bool doflash = true; + AllowVoice = true; + FormMove = false; + FormSpeed = SPEED_WHEEL; + FormMaxSpeed = MPH_LIGHT_SPEED; + + if ( (action == ACTION_MOVE || action == ACTION_NOMOVE) && CurrentObject.Count()) { + + /* + ** Scan all units. If any are selected that shouldn't be, or aren't + ** selected but should be, then this is not a formation move. + */ + int group = 254; // init to invalid group # + + if (CurrentObject[0]->Is_Foot()) { + group = ((FootClass *)CurrentObject[0])->Group; + } + + /* + ** Presume this is a formation move unless something is detected + ** that will prevent it. + */ + FormMove = true; + + /* + ** First scan through all the selected units to make sure that they + ** are all of the same team and have been assigned a particular formation + */ + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tobject = CurrentObject[index]; + + /* + ** Only moveable (i.e., FootClass) objects can ever be in a formation + ** so if a selected object isn't of a FootClass type then it can't be + ** a formation move. + */ + if (tobject->Is_Foot() == false) { + FormMove = false; + break; + } + + /* + ** If the object is not part of the same team as the rest of the + ** selected group, or it just plain has never been assigned a + ** formation offset, then it can't be a formation move. + */ + FootClass const * foot = (FootClass *)tobject; + if (foot->Group != group || foot->XFormOffset == 0x80000000) { + FormMove = false; + break; + } + + /* + ** Determine the formation speed on the presumption that this + ** will turn out to be a formation move. + */ + FormMaxSpeed = foot->Techno_Type_Class()->MaxSpeed; + FormSpeed = foot->Techno_Type_Class()->Speed; + } + + /* + ** Loop through all objects (that can theoretically be part of a team) and + ** if there are any that are part of the currently selected team, but + ** are not currently selected themselves, then this will force this move + ** to NOT be a formation move. + */ + if (FormMove) { + for (index = 0; index < ::Logic.Count(); index++) { + ObjectClass const * obj = ::Logic[index]; + + /* + ** If the object is selected, then it has already been scanned + ** by the previous loop. + */ + if (obj->IsSelected) continue; + + /* + ** Only consider footclass objects. + */ + if (!obj->Is_Foot()) continue; + + FootClass const * foot = (FootClass *)obj; + + /* + ** Only consider objects that are owned by the player. + */ + if (!foot->IsOwnedByPlayer) continue; + + /* + ** If another member of this team has been discovered and + ** it isn't selected, then the formation move cannot take + ** place. + */ + if (foot->Group == group) { + FormMove = false; + break; + } + } + } + } + + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass * tobject = CurrentObject[index]; + + if (object != NULL) { + tobject->Active_Click_With(tobject->What_Action(object), object); + } else { + + /* + ** Trap for formation moves: if this unit is part of a + ** formation (being part of a team qualifies) and they're + ** told to move, adjust the target destination so they stay + ** in formation when they arrive. + */ + CELL newmove = cell; + int whatami = tobject->What_Am_I(); + if (action == ACTION_MOVE && tobject->Is_Foot()) { + int oldisform; + FootClass * foot = (FootClass *)tobject; + oldisform = foot->IsFormationMove; + foot->IsFormationMove = FormMove; + if (FormMove && foot->Group != 255 ) { + newmove = foot->Adjust_Dest(cell); + } + foot->IsFormationMove = oldisform; + } + tobject->Active_Click_With(tobject->What_Action(cell), newmove); + } + AllowVoice = false; + } + AllowVoice = true; + FormMove = false; + + if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { + OutList.Add(EventClass(EventClass::REPAIR, TargetClass(object))); + } + if (action == ACTION_SELL_UNIT && object) { + switch (object->What_Am_I()) { + case RTTI_AIRCRAFT: + case RTTI_UNIT: + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + break; + + default: + break; + } + + } + if (action == ACTION_SELL) { + if (object) { + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + } else { + OutList.Add(EventClass(EventClass::SELLCELL, cell)); +// OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); + } + } + + if (action == ACTION_NUKE_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); + } + + if (action == ACTION_PARA_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_BOMB, cell)); + } + if (action == ACTION_PARA_INFANTRY) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_INFANTRY, cell)); + } + if (action == ACTION_SPY_MISSION) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SPY_MISSION, cell)); + } + if (action == ACTION_IRON_CURTAIN) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_IRON_CURTAIN, cell)); + } + if (action == ACTION_CHRONOSPHERE) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONOSPHERE, cell)); + } + if (action == ACTION_CHRONO2) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONO2, cell)); + } + } + + IsTentative = false; + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * * + * Handle the left mouse button press while over the tactical map. If it isn't is * + * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * + * mouse moves a sufficient distance from this recorded position, then rubber band mode * + * is officially started. * + * * + * INPUT: x,y -- The mouse coordinates at the time of the press. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Press(int x, int y) +{ + if (!IsRepairMode && !IsSellMode && IsTargettingMode == SPC_NONE && !PendingObject) { + IsTentative = true; + BandX = x; + BandY = y; + NewX = x; + NewY = y; + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * * + * This routine is called continuously while the left mouse button is held down over * + * the tactical map. This handles the rubber band mode detection and dragging. * + * * + * INPUT: x,y -- The mouse coordinate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Held(int x, int y) +{ + if (IsRubberBand) { + if (x != NewX || y != NewY) { + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + Refresh_Band(); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } else { + + /* + ** If the mouse is still held down while a tentative extended select is possible, then + ** check to see if the mouse has moved a sufficient distance in order to activate + ** extended select mode. + */ + if (IsTentative) { + + /* + ** The mouse must have moved a minimum distance before rubber band mode can be + ** initiated. + */ + if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { + IsRubberBand = true; + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + for (int index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * * + * This routine is used to set the tactical view position. The requested position is * + * clipped to the map dimensions as necessary. * + * * + * INPUT: coord -- The coordinate desired for the upper left corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Set_Tactical_Position(COORDINATE coord) +{ + /* + ** Bound the desired location to fit the legal map edges. + */ + int xx = (int)Coord_X(coord) - (int)Cell_To_Lepton(MapCellX); + int yy = (int)Coord_Y(coord) - (int)Cell_To_Lepton(MapCellY); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + if (ScenarioInit) { + TacticalCoord = coord; + } + DesiredTacticalCoord = coord; + + IsToRedraw = true; + Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * * + * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/28/1995 JLB : Commented. * + * 06/26/1995 JLB : Fixed building loop. * + * 10/20/1996 JLB : Doesn't wrap. * + *=============================================================================================*/ +void DisplayClass::Compute_Start_Pos(void) +{ + /* + ** Find the summation cell-x & cell-y for all the player's units, infantry, + ** and buildings. Buildings are weighted so that they count 16 times more + ** than units or infantry. + */ + long x = 0; + long y = 0; + long num = 0; + for (int i = 0; i < Infantry.Count(); i++) { + InfantryClass * infp = Infantry.Ptr(i); + if (!infp->IsInLimbo && infp->IsOwnedByPlayer) { + x += (long)Coord_XCell(infp->Coord); + y += (long)Coord_YCell(infp->Coord); + num++; + } + } + + for (i = 0; i < Units.Count(); i++) { + UnitClass * unitp = Units.Ptr(i); + if (!unitp->IsInLimbo && unitp->IsOwnedByPlayer) { + x += (long)Coord_XCell(unitp->Coord); + y += (long)Coord_YCell(unitp->Coord); + num++; + } + } + + for (i = 0; i < Buildings.Count(); i++) { + BuildingClass * bldgp = Buildings.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->IsOwnedByPlayer) { + x += (((long)Coord_XCell(bldgp->Coord)) * 16); + y += (((long)Coord_YCell(bldgp->Coord)) * 16); + num += 16; + } + } + + for (i = 0; i < Vessels.Count(); i++) { + VesselClass * bldgp = Vessels.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->IsOwnedByPlayer) { + x += (((long)Coord_XCell(bldgp->Coord))); + y += (((long)Coord_YCell(bldgp->Coord))); + num++; + } + } + + /* + ** Divide each coord by 'num' to compute the average value + */ + if (num > 0) { + x /= num; + } else { + x = 0; + } + + if (num > 0) { + y /= num; + } else { + y = 0; + } + + /* + ** Tactical position is based on the cell of the upper left corner. Make adjustments + ** and bound the calculated location to the map dimensions. + */ +// x -= 5 * RESFACTOR; +// y -= 4 * RESFACTOR; + if (x < MapCellX + 5*RESFACTOR) x = MapCellX + 5*RESFACTOR; + if (y < MapCellY + 4*RESFACTOR) y = MapCellY + 4*RESFACTOR; + if (x > MapCellX+MapCellWidth - 5*RESFACTOR) x = MapCellX+MapCellWidth - 5*RESFACTOR; + if (y > MapCellY+MapCellHeight - 4*RESFACTOR) y = MapCellY+MapCellHeight - 4*RESFACTOR; + + Scen.Waypoint[WAYPT_HOME] = Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = XY_Cell(x, y); + + Map.Set_Tactical_Position(Coord_Whole(Cell_Coord((Scen.Views[0] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR)))); +// Set_Tactical_Position(Cell_Coord(XY_Cell(x, y))); +} + + +/*********************************************************************************************** + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * * + * This routine will control the sell mode for the player. * + * * + * INPUT: control -- The mode to set the sell state to. * + * 0 = Turn sell mode off. * + * 1 = Turn sell mode on. * + * -1 = Toggle sell mode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Sell_Mode_Control(int control) +{ + bool mode = IsSellMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsSellMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsSellMode && !PendingObject) { + IsRepairMode = false; + if (mode && PlayerPtr->BScan) { + IsSellMode = true; + Unselect_All(); + } else { + IsSellMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * * + * This routine is used to control the repair mode for the player. * + * * + * INPUT: control -- The mode to set the repair to. * + * 0 = Turn repair off. * + * 1 = Turn repair on. * + * -1= Toggle repair state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Repair_Mode_Control(int control) +{ + bool mode = IsRepairMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsRepairMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsRepairMode && !PendingObject) { + IsSellMode = false; + if (mode && PlayerPtr->BScan) { + IsRepairMode = true; + Unselect_All(); + } else { + IsRepairMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * * + * Use this routine to determine if the specified cell is visible on * + * the display. This is a useful fact, since many display operations * + * can be skipped if the cell is not visible. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: bool; Is this cell visible on the display? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DisplayClass::In_View(register CELL cell) const +{ + if (cell & 0xC000) return(false); + + COORDINATE coord = Coord_Whole(Cell_Coord(cell)); + COORDINATE tcoord = Coord_Whole(TacticalCoord); + + if ((unsigned)(Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+CELL_LEPTON_W-1) return(false); + if ((unsigned)(Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+CELL_LEPTON_H-1) return(false); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * * + * Use this routine to find the sub cell spot closest to the coordinate specified that is * + * free from occupation. Typical use of this is for infantry destination calculation. * + * * + * INPUT: coord -- The coordinate to use as the starting point when finding the closest * + * free spot. * + * * + * any -- Ignore occupation and just return the closest sub cell spot? * + * * + * OUTPUT: Returns with the coordinate of the closest free (possibly) sub cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + if (coord & HIGH_COORD_MASK) { + return(0x00800080); + } + return Map[coord].Closest_Free_Spot(coord, any); +} + + +/*********************************************************************************************** + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * * + * Use this routine to determine if the coordinate (rounded to the nearest sub cell * + * position) is free for placement. Typical use of this would be for infantry placement. * + * * + * INPUT: coord -- The coordinate to examine for "freeness". The coordinate is rounded to * + * the nearest free sub cell spot. * + * * + * OUTPUT: Is the sub spot indicated by the coordinate free from previous occupation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Is_Spot_Free(COORDINATE coord) const +{ + if (coord & HIGH_COORD_MASK) { + return(0x00800080); + } + return Map[coord].Is_Spot_Free(CellClass::Spot_Index(coord)); +} + + +/*********************************************************************************************** + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * * + * This routine will average the position of all the selected objects and then center * + * the map about those objects. * + * * + * INPUT: center -- The is an optional center about override coordinate. If specified, * + * then the map will be centered about that coordinate. Otherwise it * + * will center about the average location of all selected objects. * + * * + * OUTPUT: none * + * * + * WARNINGS: The map position changes by this routine. * + * * + * HISTORY: * + * 08/22/1995 JLB : Created. * + * 09/16/1996 JLB : Takes coordinate to center about (as override). * + *=============================================================================================*/ +void DisplayClass::Center_Map(COORDINATE center) +{ + int x = 0; +// unsigned x = 0; + int y = 0; +// unsigned y = 0; + bool centerit = false; + + if (CurrentObject.Count()) { + + for (int index = 0; index < CurrentObject.Count(); index++) { + COORDINATE coord = CurrentObject[index]->Center_Coord(); + + x += Coord_X(coord); + y += Coord_Y(coord); + } + + x /= CurrentObject.Count(); + y /= CurrentObject.Count(); + centerit = true; + } + + if (center != 0L) { + x = Coord_X(center); + y = Coord_Y(center); + centerit = true; + } + + if (centerit) { + x = x - (int)TacLeptonWidth/2; + if (x < Cell_To_Lepton(MapCellX)) x = Cell_To_Lepton(MapCellX); + + y = y - (int)TacLeptonHeight/2; + if (y < Cell_To_Lepton(MapCellY)) y = Cell_To_Lepton(MapCellY); + + Set_Tactical_Position(XY_Coord(x, y)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * * + * This routine will cause the shadow to creep back by one cell. Multiple calls to this * + * routine will result in the shadow becoming more and more invasive until only the sight * + * range of player controlled units will keep the shadow pushed back. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Encroach_Shadow(void) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->IsVisible || !cellptr->IsMapped) continue; + + cellptr->IsToShroud = true; + } + + /* + ** Mark all shadow edge cells to be fully shrouded. All adjacent mapped + ** cell should become partially shrouded. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + if ((*this)[cell].IsToShroud) { + (*this)[cell].IsToShroud = false; + Shroud_Cell(cell); + } + } + + All_To_Look(); + + Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * * + * This routine is called to add the shroud back to the cell specified. Typical of this * + * would be when the shroud is to regenerate. * + * * + * INPUT: cell -- The cell that the shroud is to be regenerated upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: Adjacent cells might be affected by this routine. The affect is determined * + * according to the legality of the partial shadow artwork. In the illegal cases * + * the adjacent cell might become shrouded as well. * + * * + * HISTORY: * + * 10/17/1995 JLB : Created. * + * 06/17/1996 JLB : Modified to handle the new shadow pieces. * + *=============================================================================================*/ +void DisplayClass::Shroud_Cell(CELL cell/*KO, bool shadeit*/) +{ + if (PlayerPtr->IsGPSActive) { + if ( (*this)[cell].Jammed & (1 << PlayerPtr->Class->House) ) { + return; + } + } + if (!In_Radar(cell)) return; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->IsMapped) { + + cellptr->IsMapped = false; + cellptr->IsVisible = false; + cellptr->Redraw_Objects(); + + /* + ** Check adjacent cells. There might be some weird combination of + ** shrouded cells such that more cells must be shrouded in order for + ** this to work. + */ + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + CELL c = Adjacent_Cell(cell, dir); + CellClass * cptr = &(*this)[c]; + + /* + ** If this adjacent cell must be completely shrouded as a result + ** of the map change, yet it isn't already shrouded, then recursively + ** shroud that cell. + */ + if (c != cell) { + cptr->IsVisible = false; + } + + /* + ** Always redraw the cell because, more than likely, the shroud + ** edge will change shape because of the map change. + */ + cptr->Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * * + * This routine is used to read the map control data from the INI * + * file. * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The TriggerClass INI data must have been read before calling this function. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Read_INI(CCINIClass & ini) +{ + /* + ** Read the map dimensions. + */ + char const * const name = "Map"; + int x = ini.Get_Int(name, "X", 1); + int y = ini.Get_Int(name, "Y", 1); + int w = ini.Get_Int(name, "Width", MAP_CELL_W-2); + int h = ini.Get_Int(name, "Height", MAP_CELL_H-2); + +#ifndef FIXIT_VERSION_3 // Map size no longer restricted. + +#ifdef FIXIT_CSII // checked - ajw + if(Session.Type >= GAME_MODEM && Session.Type <= GAME_INTERNET && PlayingAgainstVersion < VERSION_AFTERMATH_CS) { + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + ** BG Note: only do this for multiplayer games against non-AfterMath. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } + } +#else + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } +#endif + +#endif // !FIXIT_VERSION_3 + + Set_Map_Dimensions( x, y, w, h ); + + /* + ** The theater is determined at this point. There is specific data that + ** is custom to this data. Load the custom data (as it related to terrain) + ** at this point. + */ + Scen.Theater = ini.Get_TheaterType(name, "Theater", THEATER_TEMPERATE); + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 + if (Scen.Theater != LastTheater) { + Reset_Theater_Shapes(); + } +#endif //WIN32 + + /* + ** Now that the theater is known, init the entire map hierarchy + */ + Init(Scen.Theater); + + /* + ** Special initializations occur when the theater is known. + */ + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + /* + ** Read the Waypoint entries. + */ + for (int i = 0; i < WAYPT_COUNT; i++) { + char buf[20]; + sprintf(buf, "%d", i); + Scen.Waypoint[i] = ini.Get_Int("Waypoints", buf, -1); + + if (Scen.Waypoint[i] != -1) { + (*this)[Scen.Waypoint[i]].IsWaypoint = 1; + } + } + + /* + ** Set the starting position (do this after Init(), which clears the cells' + ** IsWaypoint flags). + */ + if (Scen.Waypoint[WAYPT_HOME] == -1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + 5*RESFACTOR, MapCellY + 4*RESFACTOR); + } + + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); + + /* + ** Loop through all CellTrigger entries. + */ + int len = ini.Entry_Count("CellTriggers"); + for (int index = 0; index < len; index++) { + + /* + ** Get a cell trigger and cell assignment. + */ + char const * cellentry = ini.Get_Entry("CellTriggers", index); + TriggerTypeClass * tp = ini.Get_TriggerType("CellTriggers", cellentry); + CELL cell = atoi(cellentry); + + if (tp != NULL && !(*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + tt->Cell = cell; + (*this)[cell].Trigger = tt; + } + } + } + + /* + ** Read the map template data. + */ + static char const * const MAPPACK = "MapPack"; + len = ini.Get_UUBlock(MAPPACK, _staging_buffer, sizeof(_staging_buffer)); + BufferStraw bstraw(_staging_buffer, len); + Map.Read_Binary(bstraw); + + LastTheater = Scen.Theater; +} + + +/*********************************************************************************************** + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * * + * This routine will output all the data of this map to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI handler to store the map data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: Any existing map data in the INI database will be replaced by this function. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Write_INI(CCINIClass & ini) +{ + char entry[20]; + + /* + ** Save the map parameters. + */ + static char const * const NAME = "Map"; + ini.Clear(NAME); + ini.Put_TheaterType(NAME, "Theater", Scen.Theater); + ini.Put_Int(NAME, "X", MapCellX); + ini.Put_Int(NAME, "Y", MapCellY); + ini.Put_Int(NAME, "Width", MapCellWidth); + ini.Put_Int(NAME, "Height", MapCellHeight); + + /* + ** Save the Waypoint entries. + */ + static char const * const WAYNAME = "Waypoints"; + ini.Clear(WAYNAME); + for (int i = 0; i < WAYPT_COUNT; i++) { + if (Scen.Waypoint[i] != -1) { + sprintf(entry, "%d", i); + ini.Put_Int(WAYNAME, entry, Scen.Waypoint[i]); + } + } + + /* + ** Save the cell's triggers. + */ + static char const * const CELLTRIG = "CellTriggers"; + ini.Clear(CELLTRIG); + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tp = (*this)[cell].Trigger; + if (tp != NULL) { + + /* + ** Generate entry name. + */ + sprintf(entry, "%d", cell); + + /* + ** Save entry. + */ + ini.Put_TriggerType(CELLTRIG, entry, tp->Class); + } + } + } + + /* + ** Write the map template data out to the ini file. + */ + static char const * const MAPPACK = "MapPack"; + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + int len = Map.Write_Binary(bpipe); + ini.Clear(MAPPACK); + if (len) { + ini.Put_UUBlock(MAPPACK, _staging_buffer, len); + } +} + + +/*********************************************************************************************** + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * * + * This routine will scan through all objects and tell them to look if they are supposed * + * to be able to reveal the map for the player. This routine may be necessary in cases * + * of gap generator reshroud logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::All_To_Look(bool units_only) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (tech->House->IsPlayerControl) { + if (tech->IsDiscoveredByPlayer) { + object->Look(); + } + } else { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)) { + tech->Look(); + } + } + } + } +} + + +void DisplayClass::Constrained_Look(COORDINATE center, LEPTON distance) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + +// if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (tech->House->IsPlayerControl) { + if (tech->IsDiscoveredByPlayer && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + object->Look(); + } + } else { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr) && + Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + + tech->Look(); + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * * + * This will flag the cell to be redrawn. * + * * + * INPUT: cell -- The cell to be flagged. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Flag_Cell(CELL cell) +{ + Flag_To_Redraw(false); + IsToRedraw = true; + CellRedraw[cell] = true; +} diff --git a/CODE/DISPLAY.H b/CODE/DISPLAY.H new file mode 100644 index 0000000..786922d --- /dev/null +++ b/CODE/DISPLAY.H @@ -0,0 +1,321 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DISPLAY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DISPLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 1, 1994 * + * * + * Last Update : May 1, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "map.h" +#include "layer.h" + + +#define ICON_PIXEL_W 24 +#define ICON_PIXEL_H 24 +#define ICON_LEPTON_W 256 +#define ICON_LEPTON_H 256 +#define CELL_PIXEL_W ICON_PIXEL_W +#define CELL_PIXEL_H ICON_PIXEL_H +#define CELL_LEPTON_W ICON_LEPTON_W +#define CELL_LEPTON_H ICON_LEPTON_H + +// ----------------------------------------------------------- +#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W) +#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H) + +#define SIDE_BAR_TAC_WIDTH 10 +#define SIDE_BAR_TAC_HEIGHT 8 + +extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2); + +class DisplayClass: public MapClass +{ + public: + + /* + ** The tactical map display position is indicated by the cell of the + ** upper left hand corner. These should not be altered directly. Use + ** the Set_Tactical_Position function instead. + */ + COORDINATE TacticalCoord; + + /* + ** The dimensions (in cells) of the visible window onto the game map. This tactical + ** map is how the player interacts and views the game world. + */ + LEPTON TacLeptonWidth; + LEPTON TacLeptonHeight; + + /* + ** These layer control elements are used to group the displayable objects + ** so that proper overlap can be obtained. + */ + static LayerClass Layer[LAYER_COUNT]; + + /* + ** This records the position and shape of a placement cursor to display + ** over the map. This cursor is used when placing buildings and also used + ** extensively by the scenario editor. + */ + CELL ZoneCell; + short ZoneOffset; + short const *CursorSize; + bool ProximityCheck; // Is proximity check ok? + + /* + ** This holds the building type that is about to be placed upon the map. + ** It is only valid during the building placement state. The PendingLegal + ** flag is updated as the cursor moves and it reflects the legality of + ** placing the building at the desired location. + */ + ObjectClass * PendingObjectPtr; + ObjectTypeClass const * PendingObject; + HousesType PendingHouse; + + static unsigned char FadingBrighten[256]; + static unsigned char FadingShade[256]; + static unsigned char FadingWayDark[256]; + static unsigned char FadingLight[256]; + static unsigned char FadingGreen[256]; + static unsigned char FadingYellow[256]; + static unsigned char FadingRed[256]; + static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256]; + static unsigned char WhiteTranslucentTable[(1+1)*256]; + static unsigned char MouseTranslucentTable[(4+1)*256]; + static void const *TransIconset; + static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256]; + static unsigned char UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; + static unsigned char SpecialGhost[2*256]; + + //------------------------------------------------------------------------- + DisplayClass(void); + DisplayClass(NoInitClass const & x) : MapClass(x) {}; + + virtual void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** General display/map/interface support functionality. + */ + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + /* + ** Added functionality. + */ + void All_To_Look(bool units_only=false); + void Constrained_Look(COORDINATE coord, LEPTON distance); + void Shroud_Cell(CELL cell/*KO, bool shadeit = false*/); + void Encroach_Shadow(void); + void Center_Map(COORDINATE center=0L); + virtual bool Map_Cell(CELL cell, HouseClass *house); + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false) {}; + virtual MouseType Get_Mouse_Shape(void) const = 0; + virtual bool Scroll_Map(DirType facing, int & distance, bool really); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1); + + /* + ** Pending object placement control. + */ + virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system. + void Cursor_Mark(CELL pos, bool on); + void Set_Cursor_Shape(short const * list); + CELL Set_Cursor_Pos(CELL pos = -1); + void Get_Occupy_Dimensions(int & w, int & h, short const *list) const; + + /* + ** Tactical map only functionality. + */ + virtual void Set_Tactical_Position(COORDINATE coord); + void Refresh_Band(void); + void Select_These(COORDINATE coord1, COORDINATE coord2); + COORDINATE Pixel_To_Coord(int x, int y) const; + bool Coord_To_Pixel(COORDINATE coord, int & x, int & y) const; + bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest); + void Remove(ObjectClass const * object, LayerType layer); + void Submit(ObjectClass const * object, LayerType layer); + CELL Calculated_Cell(SourceType dir, WAYPOINT waypoint=-1, CELL cell=-1, SpeedType loco=SPEED_FOOT, bool zonecheck=true, MZoneType mzone=MZONE_NORMAL) const; + bool In_View(register CELL cell) const; + bool Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const; + ObjectClass * Cell_Object(CELL cell, int x=0, int y=0) const; + ObjectClass * Next_Object(ObjectClass * object) const; + ObjectClass * Prev_Object(ObjectClass * object) const; + int Cell_Shadow(CELL cell) const; + short const * Text_Overlap_List(char const * text, int x, int y) const; + bool Is_Spot_Free(COORDINATE coord) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + void Sell_Mode_Control(int control); + void Repair_Mode_Control(int control); + + virtual void Flag_Cell(CELL cell); + bool Is_Cell_Flagged(CELL cell) const {return CellRedraw.Is_True(cell);}; + + /* + ** Computes starting position based on player's units' Coords. + */ + void Compute_Start_Pos(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + virtual void Mouse_Right_Press(void); + virtual void Mouse_Left_Press(int x, int y); + virtual void Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall = false); + virtual void Mouse_Left_Held(int x, int y); + virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall = false); + + public: + /* + ** This is the pixel offset for the upper left corner of the tactical map. + */ + int TacPixelX; + int TacPixelY; + + /* + ** This is the coordinate that the tactical map should be in at next available opportunity. + */ + COORDINATE DesiredTacticalCoord; + + /* + ** If something in the tactical map is to be redrawn, this flag is set to true. + */ + unsigned IsToRedraw:1; + + /* + ** If the player is currently wielding a wrench (to select buildings for repair), + ** then this flag is true. In such a state, normal movement and combat orders + ** are preempted. + */ + unsigned IsRepairMode:1; + + /* + ** If the player is currently in "sell back" mode, then this flag will be + ** true. While in this mode, anything clicked on will be sold back to the + ** "factory". + */ + unsigned IsSellMode:1; + + /* + ** If the player is currently in ion cannon targeting mode, then this + ** flag will be true. While in this mode, anything clicked on will be + ** be destroyed by the ION cannon. + */ + SpecialWeaponType IsTargettingMode; + + protected: + + /* + ** If it is currently in rubber band mode (multi unit selection), then this + ** flag will be true. While in such a mode, normal input is preempted while + ** the extended selection is in progress. + */ + unsigned IsRubberBand:1; + + /* + ** The moment the mouse is held down, this flag gets set. If the mouse is dragged + ** a sufficient distance while held down, then true rubber band mode selection + ** can begin. Using a minimum distance prevents accidental rubber band selection + ** mode from being initiated. + */ + unsigned IsTentative:1; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static TacticalClass TacButton; + + private: + + /* + ** This is a utility flag that is set during the icon draw process only if there + ** was at least one shadow icon detected that should be redrawn. When the shadow + ** drawing logic is to take place, but this flag is false, then the shadow drawing + ** will be skipped since it would perform no function. + */ + unsigned IsShadowPresent:1; + + /* + ** Rubber band mode consists of stretching a box from the anchor point (specified + ** here) to the current cursor position. + */ + int BandX,BandY; + int NewX,NewY; + + static void const *ShadowShapes; + static unsigned char ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + + void Redraw_Icons(void); + void Redraw_OIcons(void); + void Redraw_Shadow(void); + + /* + ** This bit array is used to flag cells to be redrawn. If the icon needs to + ** be redrawn for a cell, then the corresponding flag will be true. + */ + static BooleanVectorClass CellRedraw; + + bool Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const; +}; + + +#endif diff --git a/CODE/DOOR.CPP b/CODE/DOOR.CPP new file mode 100644 index 0000000..a145bd6 --- /dev/null +++ b/CODE/DOOR.CPP @@ -0,0 +1,202 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DOOR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DOOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 14, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DoorClass::AI -- Handles the door processing logic. * + * DoorClass::Close_Door -- Try to close the unit's door. * + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * DoorClass::Open_Door -- Opens the door for this unit. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * * + * This constructor sets the door to an initial closed state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +DoorClass::DoorClass(void) +{ + State = IS_CLOSED; + IsToRedraw = false; + Stages = 0; +} + + +/*********************************************************************************************** + * DoorClass::AI -- Handles the door processing logic. * + * * + * This routine should be called every game frame. It handles the door closing and opening * + * logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/13/1995 JLB : Created. * + *=============================================================================================*/ +void DoorClass::AI(void) +{ + if (Control.Graphic_Logic()) { + if (Control.Fetch_Stage() >= Stages) { + Control.Set_Rate(0); + switch (State) { + case IS_OPENING: + State = IS_OPEN; + break; + + case IS_CLOSING: + State = IS_CLOSED; + break; + } + } + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * DoorClass::Open_Door -- Opens the door for this unit. * + * * + * This routine will perform the door open operation for this unit. It will control vehicle * + * rotation if necessary. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Was action initiated to open the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Open_Door(int rate, int stages) +{ + switch (State) { + case IS_CLOSED: + case IS_CLOSING: + State = IS_OPENING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Close_Door -- Try to close the unit's door. * + * * + * This routine will attempt to close the unit's door. If the door is already closed or * + * in the process of closing, then no action is performed. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Action was initiated to close the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Close_Door(int rate, int stages) +{ + switch (State) { + case IS_OPEN: + case IS_OPENING: + State = IS_CLOSING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * * + * Use this routine to fetch the current door animation frame number. Frame zero is the * + * closed frame and frame 'N' is the open frame. If the door is in the process of opening * + * or closing, the appropriate frame number is used. 'N' is defined as the number of * + * stages in the animation minus 1 (e.g., a four frame animation will return a door stage * + * number between 0 and 3, inclusive). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the door animation frame number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int DoorClass::Door_Stage(void) const +{ + switch (State) { + case IS_CLOSING: + return((Stages-1) - Control.Fetch_Stage()); + + case IS_CLOSED: + return(0); + + case IS_OPENING: + return(Control.Fetch_Stage()); + + case IS_OPEN: + return(Stages-1); + } + return(0); +} diff --git a/CODE/DOOR.H b/CODE/DOOR.H new file mode 100644 index 0000000..9ad73a6 --- /dev/null +++ b/CODE/DOOR.H @@ -0,0 +1,89 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DOOR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DOOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DOOR_H +#define DOOR_H + +class DoorClass +{ + private: + + /* + ** This is the animation control handler. + */ + StageClass Control; + + /* + ** This is the recorded number of stages of the current + ** door animation process. + */ + unsigned char Stages; + + /* + ** This is the door state. + */ + enum { + IS_CLOSED, // Door is closed. + IS_OPENING, // Door is in the process of opening. + IS_OPEN, // Door is fully open. + IS_CLOSING // Door is in the process of closing. + } State; + + /* + ** If the animation for this door indicates that the object it is + ** attached to should be redrawn, then this flag will be true. + */ + unsigned IsToRedraw:1; + + public: + DoorClass(void); + DoorClass(NoInitClass const & x) : Control(x) {}; + + bool Time_To_Redraw(void) {return(IsToRedraw);}; + void Clear_Redraw_Flag(void) {IsToRedraw = false;}; + void AI(void); + int Door_Stage(void) const; + bool Is_Door_Opening(void) const {return(State == IS_OPENING);}; + bool Is_Door_Closing(void) const {return(State == IS_CLOSING);}; + bool Open_Door(int rate, int stages); + bool Close_Door(int rate, int stages); + bool Is_Door_Open(void) const {return(State == IS_OPEN);}; + bool Is_Door_Closed(void) const {return(State == IS_CLOSED);}; + bool Is_Ready_To_Open(void) const; +}; + +#endif diff --git a/CODE/DPMI.CPP b/CODE/DPMI.CPP new file mode 100644 index 0000000..03c972e --- /dev/null +++ b/CODE/DPMI.CPP @@ -0,0 +1,169 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.cpv 4.41 04 Jul 1996 16:12:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DPMI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef __FLAT__ +#pragma inline +#endif + +//#include "function.h" +#include "dpmi.h" + +#ifndef __FLAT__ + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + if (!size) return; + + unsigned short ssel = src.Selector; + unsigned short dsel = dest.Selector; + + asm { + push es + push ds + + mov si,soffset + mov di,doffset + mov cx,size + mov ax,ssel + mov dx,dsel + mov ds,ax + mov es,dx + } +again: + asm { + mov al,ds:[si] + mov ah,es:[di] + mov ds:[si],ah + mov es:[di],al + inc di + inc si + dec cx + jnz again + + pop ds + pop es + } +} +#endif + + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_swap(char *src, char *dest, int size); + + #pragma aux dss_swap = \ + "again: mov al,[esi]" \ + "mov ah,[edi]" \ + "mov [esi],ah" \ + "stosb" \ + "inc esi" \ + "loop again" \ + parm [esi] [edi] [ecx] \ + modify [ax]; + + if (!size) return; + dss_swap((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} + +#ifdef OBSOLETE +void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_copy(char *src, char *dest, int size); + #pragma aux dss_copy = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copskip1" \ + "rep movsd" \ +"copskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copskip2" \ + "rep movsb" \ +"copskip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + extern void dss_copy_to(void *src, (void *)dest, int size); + + #pragma aux dss_copy_to = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz cop2skip1" \ + "rep movsd" \ +"cop2skip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz cop2skip2" \ + "rep movsb" \ +"cop2skip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_to(src, (void *)(Selector + dest), size); + +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + extern void dss_copy_from(void *dest, (void *)source, int size); + + #pragma aux dss_copy_from = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copfskip1" \ + "rep movsd" \ +"copfskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copfskip2" \ + "rep movsb" \ +"copfskip2:" \ + parm [edi esi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_from(dest, (void *)(Selector + source), size); +} +#endif diff --git a/CODE/DPMI.H b/CODE/DPMI.H new file mode 100644 index 0000000..78a1bf5 --- /dev/null +++ b/CODE/DPMI.H @@ -0,0 +1,175 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.h_v 4.43 05 Jul 1996 17:58:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DPMI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DPMI_Hx +#define DPMI_Hx +#include +#include +#include +#include + + +extern void output(short port, short data); + + +class DOSSegmentClass { + /* + ** This is the selector/segment value. In real mode it is the segment, in protected + ** mode it is the selector (also 16 bits). This value is moved into DS or ES when + ** accessing memory. + ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h) + */ + unsigned int Selector; + + /* + ** These are C equivalents for pushing and popping the DS segment register. By using + ** these, it is possible to create very small code that uses a segment and + ** offset without damaging the DS register. These are especially useful in protected + ** mode, but they are legal in real mode as well. + */ + void Push_DS(void) {/*__emit__(0x1E);*/}; + void Pop_DS(void) {/*__emit__(0x1F);*/}; + + public: + DOSSegmentClass(void); + ~DOSSegmentClass(void); + DOSSegmentClass(unsigned short segment, long size=(1024L*64L)); + + unsigned int Get_Selector(void); + + /* + ** This routine is used to assign where the descriptor actually points to in + ** low DOS memory. In real mode, this is a simple segment assignment and the size + ** is always 64K regardless of what is specified. In protected mode, the segment + ** is used to update the selector and the size can be any length. + ** In Watcom flat mode, it sets Selector == segment<<4 + */ + void Assign(unsigned short segment, long size=(1024L*64L)); + + /* + ** These routines will move the data to/from regular memory and the segment/descriptor + ** memory. + */ + void Copy_To(void *source, int dest, int size); + void Copy_From(void *dest, int source, int size); + void Copy_Word_To(short data, int dest); + void Copy_Byte_To(char data, int dest); + void Copy_DWord_To(long data, int dest); + short Copy_Word_From(int source); + char Copy_Byte_From(int source); + long Copy_DWord_From(int source); + + /* + ** These routines move data around between sections of segmented (descriptor) memory. + ** Typically, this is used when accessing DOS memory in protected mode or when dealing + ** with hard memory areas such as the screen. + */ + static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); + static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); +}; + + +inline DOSSegmentClass::DOSSegmentClass(void) +{ + Selector = 0xB0000; +} + +inline DOSSegmentClass::~DOSSegmentClass(void) +{ +} + +inline void DOSSegmentClass::Copy_Word_To(short data, int dest) +{ + *(short *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_Byte_To(char data, int dest) +{ + *(char *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_DWord_To(long data, int dest) +{ + *(long *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Assign(unsigned short segment, long) +{ + Selector = (long)(segment)<<4L; +} + +inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long) +{ + Assign(segment); +} + +inline void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + memmove((void*)(Selector+dest), source, (unsigned)size); +} + +inline void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + memmove(dest, (void*)(Selector+source), (unsigned)size); +} + +inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) { + memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), (unsigned)size); +} + +inline short DOSSegmentClass::Copy_Word_From(int source) +{ + return *(short*)(Selector+source); +} + +inline char DOSSegmentClass::Copy_Byte_From(int source) +{ + return *(char*)(Selector+source); +} + +inline long DOSSegmentClass::Copy_DWord_From(int source) +{ + return *(long*)(Selector+source); +} + +inline unsigned int DOSSegmentClass::Get_Selector(void) +{ + return Selector; +} +#endif + + diff --git a/CODE/DRIVE.CPP b/CODE/DRIVE.CPP new file mode 100644 index 0000000..b349259 --- /dev/null +++ b/CODE/DRIVE.CPP @@ -0,0 +1,2333 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DRIVE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DRIVE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DriveClass::AI -- Processes unit movement and rotation. * + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * DriveClass::DriveClass -- Constructor for drive class object. * + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * DriveClass::Teleport_To -- Teleport object to specified location. * + * DriveClass::While_Moving -- Processes unit movement. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef NEVER +void test(void) +{ + enum nums {one, two, three}; + + nums x; + nums *ptr; + + ptr = &x; +} +#endif + + +/*********************************************************************************************** + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * * + * This is the voice to play when the unit is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Select(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * * + * This plays the audio feedback when ordering this unit to move to a new destination. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Move(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM, + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * * + * This plays the audio feedback when ordering this unit to attack. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Attack(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * * + * This routine is called when the unit discovers that it should get out of the "hot seat" * + * and move to an adjacent cell. Since the safety of the adjacent cell is not determined * + * before the move begins, it will appear that the unit is just scattering (which it * + * should). * + * * + * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move * + * roughly away from the threat. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + * 09/27/1995 JLB : Revised to never scatter if already moving. * + * 07/09/1996 JLB : Moved to DriveClass so that ships will scatter too. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void DriveClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (MissionControl[Mission].IsParalyzed) return; + + if ((What_Am_I() != RTTI_UNIT || !((UnitClass *)this)->IsDumping) && (!Target_Legal(NavCom) || (nokidding && !IsRotating))) { + if (!Target_Legal(TarCom) || forced || Random_Pick(1, 4) == 1) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat != 0) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Destination(::As_Target(newcell)); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * * + * This routine removes the occupation bits for the vehicle and also handles cleaning up * + * any vehicle reservation bits. After this, it then proceeds with limboing the unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Limbo(void) +{ + if (!IsInLimbo) { + Stop_Driver(); + TrackNumber = -1; + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * * + * This routine will remove the "reservation" flag (if present) when the vehicle is * + * required to stop movement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Stop_Driver(void) +{ + assert(IsActive); + + /* + ** We only need to do something if the vehicle is actually going + ** somewhere. + */ + if (Head_To_Coord()) { + + /* + ** Safe off whether the vehicle is down or not so we know whether + ** we have to put it back down. + */ + int temp = IsDown; + + /* + ** If the vehicle is down, pick it up so it doesn't interfere with + ** our flags. + */ + if (temp) { + Mark(MARK_UP); + } + + /* + ** Call the drive class function which will let us release the + ** reserved track. + */ + Mark_Track(Head_To_Coord(), MARK_UP); + + /* + ** If it was down it should be down when we are done. + */ + if (temp) { + Mark(MARK_DOWN); + } + } + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * * + * This routine will set the vehicle to rotate to the direction specified. For tracked * + * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series * + * of short drives (three point turn) to face the desired direction. * + * * + * INPUT: dir -- The direction that this vehicle should face. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Do_Turn(DirType dir) +{ + assert(IsActive); + + if (dir != PrimaryFacing) { + +#ifdef TOFIX + /* + ** Special rotation track is needed for units that + ** cannot rotate in place. + */ + if (Special.IsThreePoint && TrackNumber == -1 && Techno_Type_Class()->Speed == SPEED_WHEEL) { + int facediff; // Signed difference between current and desired facing. + FacingType face; // Current facing (ordinal value). + + facediff = PrimaryFacing.Difference(dir) >> 5; + facediff = Bound(facediff, -2, 2); + if (facediff) { + face = Dir_Facing(PrimaryFacing); + + IsOnShortTrack = true; + Force_Track(face*FACING_COUNT + (face + facediff), Coord); + + Path[0] = FACING_NONE; + Set_Speed(0xFF); // Full speed. + } + } else { + PrimaryFacing.Set_Desired(dir); + } +#else + PrimaryFacing.Set_Desired(dir); +// IsRotating = true; +#endif + } +} + + +/*********************************************************************************************** + * DriveClass::Teleport_To -- Teleport object to specified location. * + * * + * This will teleport the object to the specified location or as close as possible to it * + * if the destination is blocked. * + * * + * INPUT: cell -- The desired destination cell to teleport to. * + * * + * OUTPUT: bool; Was the teleport successful? * + * * + * WARNINGS: All current activity of this object will be terminated by the teleport. It will * + * arrive at the destination in static guard mode. * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + * 10/31/1996 JLB : Handles flag teleport case. * + *=============================================================================================*/ +bool DriveClass::Teleport_To(CELL cell) +{ + /* + ** All cargo gets destroyed. + */ + if (Rule.IsChronoKill) { + Kill_Cargo(NULL); + } + + Stop_Driver(); + Force_Track(-1, 0); + PrimaryFacing.Set_Current(PrimaryFacing.Desired()); + Transmit_Message(RADIO_OVER_OUT); + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_NONE); + Commence(); + Mark(MARK_UP); + + /* + ** A teleported unit will drop the flag right where it's at. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(((UnitClass *)this)->Flagged)->Flag_Attach(Coord_Cell(Coord)); + } + + if (Can_Enter_Cell(cell) != MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } + Coord = Cell_Coord(cell); + Mark(MARK_DOWN); + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * * + * This override (nuclear bomb) style routine is to be used when a unit needs to start * + * on a movement track but is outside the normal movement system. This occurs when a * + * harvester starts driving off of a refinery. * + * * + * INPUT: track -- The track number to start on. * + * * + * coord -- The coordinate that the unit will end up at when the movement track * + * is completed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Force_Track(int track, COORDINATE coord) +{ + assert(IsActive); + + TrackNumber = track; + TrackIndex = 0; + if (coord != 0) { + Start_Driver(coord); + } +} + + +/*********************************************************************************************** + * DriveClass::DriveClass -- Constructor for drive class object. * + * * + * This will initialize the drive class to its default state. It is called as a result * + * of creating a unit. * + * * + * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1994 JLB : Created. * + *=============================================================================================*/ +DriveClass::DriveClass(RTTIType rtti, int id, HousesType house) : + FootClass(rtti, id, house), + IsMoebius(false), + IsHarvesting(false), + IsTurretLockedDown(false), + IsOnShortTrack(false), + SpeedAccum(0), + MoebiusCountDown(0), + MoebiusCell(0), + TrackNumber(-1), + TrackIndex(0) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * * + * This debug utility function will display the status of the drive class to the mono * + * screen. It is through this information that bugs can be tracked down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(66, 14, 12, 1, IsMoebius ? MonoClass::INVERSE : MonoClass::NORMAL); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * * + * This routine calculates the new coordinate value needed for the * + * smooth turn logic. The adjustment and flag values must be * + * determined prior to entering this routine. * + * * + * INPUT: adj -- The adjustment coordinate as lifted from the * + * correct smooth turn table. * + * * + * dir -- Pointer to dir for possible modification * + * according to the flag bits. * + * * + * OUTPUT: Returns with the coordinate the unit should positioned to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1994 JLB : Created. * + * 07/13/1994 JLB : Converted to member function. * + *=============================================================================================*/ +COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType & dir) +{ + assert(IsActive); + + DirType workdir = dir; + int x,y; + int temp; + TrackControlType flags = TrackControl[TrackNumber].Flag; + + x = Coord_X(adj); + y = Coord_Y(adj); + + if (flags & F_T) { + temp = x; + x = y; + y = temp; + workdir = (DirType)(DIR_W - workdir); + } + + if (flags & F_X) { + x = -x; + workdir = (DirType)-workdir; + } + + if (flags & F_Y) { + y = -y; + workdir = (DirType)(DIR_S - workdir); + } + + dir = workdir; + + return(XY_Coord( (LEPTON)(Coord_X(Head_To_Coord()) + x), (LEPTON)(Coord_Y(Head_To_Coord()) + y))); +} + + +/*********************************************************************************************** + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * * + * This routine is used to set the unit's navigation computer to the * + * specified target. Once the navigation computer is set, the unit * + * will start planning and moving toward the destination. * + * * + * INPUT: target -- The destination target for the unit to head to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** For harvesting type vehicles, it might go into a dock and unload procedure + ** when the harvester is full and an empty refinery is selected as a target. + */ + BuildingClass * b = As_Building(target); + + /* + ** If the player clicked on refinery but it is not busy, then assign + ** it to unload at the refinery. + */ + if (b != NULL && *b == STRUCT_REFINERY && What_Am_I() == RTTI_UNIT && ((UnitTypeClass *)Techno_Type_Class())->IsToHarvest) { + if (Contact_With_Whom() != b && !b->In_Radio_Contact()) { + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + if (Mission != MISSION_ENTER && Mission != MISSION_HARVEST) { + Assign_Mission(MISSION_ENTER); + target = TARGET_NONE; + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } + + /* + ** Set the unit's navigation computer. + */ + FootClass::Assign_Destination(target); + + Path[0] = FACING_NONE; // Force recalculation of path. + if (!IsDriving && Mission != MISSION_UNLOAD) { + Start_Of_Move(); + } +} + + +/*********************************************************************************************** + * DriveClass::While_Moving -- Processes unit movement. * + * * + * This routine is used to process movement for the units as they move. * + * It is called many times for each cell's worth of movement. This * + * routine only applies after the next cell HeadTo has been determined. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DriveClass::While_Moving(void) +{ + assert(IsActive); + + /* + ** Perform quick legality checks. + */ + if (!IsDriving || TrackNumber == -1 || (IsRotating && !Techno_Type_Class()->IsTurretEquipped)) { + SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold. + return(false); + } + + /* + ** If enough movement has accumulated so that the unit can + ** visibly move on the map, then process accordingly. + ** Slow the unit down if he's carrying a flag. + */ + MPHType maxspeed = MPHType(min(Techno_Type_Class()->MaxSpeed * SpeedBias * House->GroundspeedBias, (int)MPH_LIGHT_SPEED)); + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + int actual; // Working movement addition value. + if (((UnitClass *)this)->Flagged != HOUSE_NONE) { + actual = SpeedAccum + ((int)maxspeed/2) * fixed(Speed, 256); + } else { + actual = SpeedAccum + maxspeed * fixed(Speed, 256); + } + + if (actual > PIXEL_LEPTON_W) { + TurnTrackType const * track; // Track control pointer. + TrackType const * ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + /* + ** Skip ahead the number of track steps required (limited only + ** by track length). Set the unit to the new position and + ** flag the unit accordingly. + */ + Mark(MARK_UP); + while (actual > PIXEL_LEPTON_W) { + COORDINATE offset; + DirType dir; + + actual -= PIXEL_LEPTON_W; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + Coord = Smooth_Turn(offset, dir); + + PrimaryFacing.Set(dir); + + /* + ** See if "per cell" processing is necessary. + */ + if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) { + Mark(MARK_DOWN); + Per_Cell_Process(PCP_DURING); + if (!IsActive) { + return(false); + } + Mark(MARK_UP); + } + + /* + ** The unit could "jump tracks". Check to see if the unit should + ** do so. + */ + if (/**this != UNIT_GUNBOAT &&*/ nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) { + TurnTrackType const * newtrack; // Proposed jump-to track. + int tnum; + + tnum = (int)(Dir_Facing(track->Facing) * FACING_COUNT) + (int)nextface; + newtrack = &TrackControl[tnum]; + if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) { + COORDINATE c = Head_To_Coord(); + int oldspeed = Speed; + + c = Adjacent_Cell(c, nextface); + + switch (Can_Enter_Cell(Coord_Cell(c), nextface)) { + case MOVE_OK: + IsOnShortTrack = false; // Shouldn't be necessary, but... + TrackNumber = tnum; + track = newtrack; + + tracknum = track->Track; + TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment. + ptr = RawTracks[tracknum-1].Track; + adj = false; + + Stop_Driver(); + IsDriving = true; + Per_Cell_Process(PCP_END); + IsDriving = false; + if (!IsActive) return(false); + if (Start_Driver(c)) { + Set_Speed(oldspeed); + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } else { + Path[0] = FACING_NONE; + TrackNumber = -1; + actual = 0; + } + break; + + case MOVE_CLOAK: + Map[c].Shimmer(); + break; + + case MOVE_TEMP: +#ifdef TOFIX + if (*this == UNIT_HARVESTER || !House->IsHuman) { +#else + if (!House->IsHuman) { +#endif + Map[c].Incoming(0, true, true); + } + break; + } + } + } + TrackIndex++; + + } else { + actual = 0; + Coord = Head_To_Coord(); + Stop_Driver(); + TrackNumber = -1; + TrackIndex = NULL; + + /* + ** Perform "per cell" activities. + */ + Mark(MARK_DOWN); + Per_Cell_Process(PCP_END); + if (!IsActive) return(false); + Mark(MARK_UP); + + break; + } + } + if (IsActive) { + Mark(MARK_DOWN); + } + } + + /* + ** Replace any remainder back into the unit's movement + ** accumulator to be processed next pass. + */ + SpeedAccum = actual; + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * * + * This routine is called when a unit has mostly or completely * + * entered a cell. The unit might be in the middle of a movement track * + * when this routine is called. It's primary purpose is to perform * + * sighting and other "per cell" activities. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1993 JLB : Created. * + * 03/30/1994 JLB : Revamped for track system. * + * 04/15/1994 JLB : Converted to member function. * + * 06/18/1994 JLB : Converted to virtual function. * + * 06/18/1994 JLB : Distinguishes between center and near-center conditions. * + *=============================================================================================*/ +void DriveClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** Check to see if it has reached its destination. If so, then clear the NavCom + ** regardless of the remaining path list. + */ + if (As_Cell(NavCom) == cell) { + IsTurretLockedDown = false; + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + + Lay_Track(); + } + + FootClass::Per_Cell_Process(why); +} + + +/*********************************************************************************************** + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * * + * This will try to start a unit advancing toward the cell it is * + * facing. It will check for and handle legality and reserving of the * + * necessary cell. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again because * + * initial start operation is temporarily delayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. * + * 03/16/1994 JLB : Revamped for track logic. * + * 04/15/1994 JLB : Converted to member function. * + * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. * + * 07/13/1995 JLB : Handles bumping into cloaked objects. * + * 09/22/1995 JLB : Breaks out of hopeless hunt mode. * + * 07/10/1996 JLB : Sets scan limit if necessary. * + *=============================================================================================*/ +bool DriveClass::Start_Of_Move(void) +{ + assert(IsActive); + + FacingType facing; // Direction movement will commence. + DirType dir; // Desired actual facing toward destination. + int facediff; // Difference between current and desired facing. + int speed; // Speed of unit. + CELL destcell; // Cell of destination. + LandType ground; // Ground unit is entering. + COORDINATE dest; // Destination coordinate. + + facing = Path[0]; + + if (!Target_Legal(NavCom) && facing == FACING_NONE) { + IsTurretLockedDown = false; + Stop_Driver(); + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + return(false); // Why is it calling this routine!?! + } + + /* + ** Reduce the path length if the target is a unit and the + ** range to the unit is less than the precalculated path steps. + */ + if (facing != FACING_NONE) { + int dist; + + if (Is_Target_Vessel(NavCom) || Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) { + dist = Lepton_To_Cell((LEPTON)Distance(NavCom)); + + if (dist < ARRAY_SIZE(Path)) { + Path[dist] = FACING_NONE; + facing = Path[0]; // Maybe needed. + } + } + } + + /* + ** If the path is invalid at this point, then generate one. If + ** generating a new path fails, then abort NavCom. + */ + if (facing == FACING_NONE) { + + /* + ** If after a path search, there is still no valid path, then set the + ** NavCom to null and let the script take care of assigning a new + ** navigation target. + */ + if (PathDelay != 0) { + return(false); + } + + if (!Basic_Path()) { + + /* + ** If the unit is close enough to the target then just stop + ** driving now. This prevents the fidgeting that would occur + ** if they mindlessly kept trying to get to the exact location + ** desired. This is quite necessary since it is typical to move + ** several units with the same mouse click. + */ + if (!Is_On_Priority_Mission() && Distance(NavCom) < Rule.CloseEnoughDistance && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + } else { + /* + ** If a basic path could not be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current()); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + if (TryTryAgain > 0) { + TryTryAgain--; + } else { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + + /* + ** Since the path was blocked, check to make sure that it was completely + ** blocked. If so and it has a valid TarCom and it is out of range of the + ** TarCom, then give this unit a range limit so that it might not pick + ** a "can't reach" target again. + */ + if (!Target_Legal(NavCom) && Target_Legal(TarCom) && !In_Range(TarCom)) { + IsScanLimited = true; + if (Team.Is_Valid()) Team->Scan_Limit(); + Assign_Target(TARGET_NONE); + } + + /* + ** Stop the movement, for now, and let the subsequent logic in later game + ** frames resume movement as appropriate. + */ + Stop_Driver(); + TrackNumber = -1; + IsTurretLockedDown = false; + return(false); + } + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + TryTryAgain = PATH_RETRY; + facing = Path[0]; + } + + /* + ** Determine the coordinate of the next cell to move into. + */ + dest = Adjacent_Cell(Coord, facing); + dir = Facing_Dir(facing); + + /* + ** Set the facing correctly if it isn't already correct. This + ** means starting a rotation track if necessary. + */ + facediff = PrimaryFacing.Difference(dir); + if (facediff) { + + /* + ** Request a change of facing. + */ + Do_Turn(dir); + return(true); + + } else { + + /* NOTE: Beyond this point, actual track assignment can begin. + ** + ** If the cell to move into is impassable (probably for some unexpected + ** reason), then abort the path list and set the speed to zero. The + ** next time this routine is called, a new path will be generated. + */ + destcell = Coord_Cell(dest); + Mark(MARK_UP); + MoveType cando = Can_Enter_Cell(destcell, facing); + Mark(MARK_DOWN); + + if (cando != MOVE_OK) { + + if (Mission == MISSION_MOVE /*KO&& House->IsHuman */&& Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false);//BG + } + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Stop_Driver(); + if (cando != MOVE_MOVING_BLOCK) { + Path[0] = FACING_NONE; // Path is blocked! + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (cando == MOVE_DESTROYABLE) { + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + } + IsNewNavCom = false; + TrackNumber = -1; + return(true); + } + + /* + ** Determine the speed that the unit can travel to the desired square. + */ + ground = Map[destcell].Land_Type(); + speed = Ground[ground].Cost[Techno_Type_Class()->Speed] * 256; + + /* change speed if it's related to a team move */ + if (IsFormationMove) speed = Ground[ground].Cost[FormationSpeed] * 256; + if (!speed) speed = 128; + +#ifdef NEVER + /* + ** Set the jiggle flag if the terrain would cause the unit + ** to jiggle when travelled over. + */ + BaseF &= ~BASEF_JIGGLE; + if (Ground[ground].Jiggle) { + BaseF |= BASEF_JIGGLE; + } +#endif + + /* + ** A damaged unit has a reduced speed. + */ + if (Health_Ratio() <= Rule.ConditionYellow /*(Techno_Type_Class()->MaxStrength>>1) > Strength*/) { + speed -= (speed/4); // Three quarters speed. + } + if ((speed != Speed)/* || !SpeedAdd*/) { + Set_Speed(speed); // Full speed. + } + + /* + ** Reserve the destination cell so that it won't become + ** occupied AS this unit is moving into it. + */ + if (cando != MOVE_OK) { + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + } else { + + Overrun_Square(Coord_Cell(dest), true); + + /* + ** Determine which track to use (based on recorded path). + */ + FacingType nextface = Path[1]; + if (nextface == FACING_NONE) nextface = facing; + + IsOnShortTrack = false; + TrackNumber = facing * FACING_COUNT + (int)nextface; + if (TrackControl[TrackNumber].Track == 0) { + Path[0] = FACING_NONE; + TrackNumber = -1; + return(true); + } else { + if (TrackControl[TrackNumber].Flag & F_D) { + /* + ** If the middle cell of a two cell track contains a crate, + ** the check for goodies before movement starts. + */ + if (!Map[destcell].Goodie_Check(this)) { + cando = MOVE_NO; + if (!IsActive) return(false); + } else { + if (!IsActive) return(false); + dest = Adjacent_Cell(dest, nextface); + destcell = Coord_Cell(dest); + cando = Can_Enter_Cell(destcell); + } + if (!IsActive) return(false); + + if (cando != MOVE_OK) { + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + if (cando == MOVE_DESTROYABLE) { + + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + IsNewNavCom = false; + TrackIndex = 0; + return(true); + } + } else { + memcpy((char*)&Path[0], (char*)&Path[2], CONQUER_PATH_MAX-2); + Path[CONQUER_PATH_MAX-2] = FACING_NONE; + IsPlanningToLook = true; + } + } else { + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + } + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } + } + + IsNewNavCom = false; + TrackIndex = 0; + if (!Start_Driver(dest)) { + TrackNumber = -1; + Path[0] = FACING_NONE; + Set_Speed(0); + } + } + return(false); +} + + +/*********************************************************************************************** + * DriveClass::AI -- Processes unit movement and rotation. * + * * + * This routine is used to process unit movement and rotation. It * + * functions autonomously from the script system. Thus, once a unit * + * is give rotation command or movement path, it will follow this * + * until specifically instructed to stop. The advantage of this * + * method is that it allows smooth movement of units, faster game * + * execution, and reduced script complexity (since actual movement * + * dynamics need not be controlled directly by the scripts). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine relies on the process control bits for the * + * specified unit (for speed reasons). Thus, only setting * + * movement, rotation, or path list will the unit perform * + * any physics. * + * * + * HISTORY: * + * 09/26/1993 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::AI(void) +{ + assert(IsActive); + + FootClass::AI(); + if (!IsActive || Height > 0) return; + + /* + ** Is this a unit that's been teleported using the chronosphere, and if so, + ** has his timer expired such that he needs to teleport back? + */ + if (IsMoebius) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() != RTTI_UNIT || ((UnitClass *)this)->Class->Type != UNIT_CHRONOTANK) { +#endif + if (MoebiusCountDown == 0) { + IsMoebius = false; + Teleport_To(MoebiusCell); + MoebiusCell = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + + /* + ** If the unit is following a track, then continue + ** to do so -- mindlessly. + */ + if (TrackNumber != -1) { + + /* + ** Perform the movement accumulation. + */ + While_Moving(); + if (!IsActive) return; + if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE) && (What_Am_I() != RTTI_UNIT || !((UnitClass*)this)->IsDumping)) { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + + } else { + + /* + ** For tracked units that are rotating in place, perform the rotation now. + */ +#ifdef TOFIX + if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } +#else + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Techno_Type_Class()->ROT * House->GroundspeedBias)) { + Mark(MARK_CHANGE_REDRAW); + } +#endif + if (!IsRotating) { + Per_Cell_Process(PCP_ROTATION); + if (!IsActive) return; + } + + } else { + + /* + ** The unit has no track to follow, but if there + ** is a navigation target or a remaining path, + ** then start on a new track. + */ + if ((Mission != MISSION_GUARD || Target_Legal(NavCom)) && Mission != MISSION_UNLOAD) { + if (Target_Legal(NavCom) || Path[0] != FACING_NONE) { + + /* + ** Double check to make sure that the movement destination is + ** in a zone that this unit can travel to. If not, then abort + ** the navigation target. + */ + if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom))) { + Stop_Driver(); + Assign_Destination(TARGET_NONE); + } else { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + } else { + Stop_Driver(); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * * + * This routine modifies the path of the specified unit so that it * + * will not start out with a rotation. This is necessary for those * + * vehicles that have difficulty with rotating in place. Typically, * + * this includes wheeled vehicles. * + * * + * INPUT: unit -- Pointer to the unit to adjust. * + * * + * path -- Pointer to path structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only units that require a fixup get modified. The * + * modification only occurs, if there is a legal path to * + * do so. * + * * + * HISTORY: * + * 04/03/1994 JLB : Created. * + * 04/06/1994 JLB : Uses path structure. * + * 04/10/1994 JLB : Diagonal smooth turn added. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Fixup_Path(PathType * path) +{ + assert(IsActive); + + FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements. + int facediff; // The facing difference value (0..4 | 0..-4). + static FacingType _path[4][6] = { + {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + static FacingType _dpath[4][6] = { + {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}, + {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + + int index; + int counter; // Path addition + FacingType * ptr; // Path list pointer. + FacingType * ptr2; // Copy of new path list pointer. + FacingType nextpath; // Next path value. + CELL cell; // Working cell value. + bool ok; + + /* + ** Verify that the unit is valid and there is a path problem to resolve. + */ + if (!path || path->Command[0] == FACING_NONE) { + return; + } + + /* + ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns. + */ +#ifdef TOFIX + if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) { +#else + if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) { +// if (What_Am_I() == RTTI_UNIT) { +#endif + return; + } + + /* + ** If the original path starts in the same direction as the unit, then + ** there is no problem to resolve -- abort. + */ + facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5; + + if (!facediff) return; + + if (Dir_Facing(PrimaryFacing) & FACING_NE) { + ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } else { + ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } + ptr2 = ptr; + + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Adjacent_Cell(cell, nextpath); + //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + + /* + ** If veering to the left was not successful, then try veering + ** to the right. This only makes sense if the vehicle is trying + ** to turn 180 degrees. + */ + if (!ok && ABS(facediff) == 4) { + ptr = ptr2; // Pointer to path adjust list. + facediff = -facediff; + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + } + + /* + ** If a legal path addition was created, then install it in place + ** of the first path value. The initial path entry is to be replaced + ** with a sequence of path entries that create smooth turning. + */ + if (ok) { + if (path->Length <= 1) { + memmove((char *)&stage[0], (char*)path->Command, max(counter, 1)); + path->Length = counter; + } else { + + /* + ** Optimize the transition path step from the smooth turn + ** first part as it joins with the rest of the normal + ** path. The normal prefix path steps are NOT to be optimized. + */ + if (counter) { + counter--; + path->Command[0] = stage[counter]; + Optimize_Moves(path, MOVE_OK); + } + + /* + ** If there is more than one prefix path element, then + ** insert the rest now. + */ + if (counter) { + memmove((char*)&path->Command[0], (char*)&path->Command[counter], 40-counter); + memmove((char*)&stage[0], (char*)&path->Command[0], counter); + path->Length += counter; + } + } + path->Command[path->Length] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * * + * This routine handles the track laying for the unit. This entails examining the unit's * + * current location as well as the direction and whether this unit is allowed to lay * + * tracks in the first place. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Lay_Track(void) +{ + assert(IsActive); + +#ifdef NEVER + static IconCommandType * _trackdirs[8] = { + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE, + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE + }; + + if (!(ClassF & CLASSF_TRACKS)) return; + + Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]); +#endif +} + + +/*********************************************************************************************** + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * * + * This routine will ensure that the midpoint (if any) of the track that the unit is * + * following, will be marked according to the mark type specified. * + * * + * INPUT: headto -- The head to coordinate. * + * * + * type -- The type of marking to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Mark_Track(COORDINATE headto, MarkType type) +{ + assert(IsActive); + + int value; + + if (type == MARK_UP) { + value = false; + } else { + value = true; + } + + if (headto) { + if (!IsOnShortTrack && TrackNumber != -1) { + + /* + ** If we have not passed the per cell process point we need + ** to deal with it. + */ + int tracknum = TrackControl[TrackNumber].Track; + if (tracknum) { + TrackType const * ptr = RawTracks[tracknum - 1].Track; + int cellidx = RawTracks[tracknum - 1].Cell; + if (cellidx > -1) { + DirType dir = ptr[cellidx].Facing; + + if (TrackIndex < cellidx && cellidx != -1) { + COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, dir); + Map[offset].Flag.Occupy.Vehicle = value; + } + } + } + } + Map[headto].Flag.Occupy.Vehicle = value; + } +} + + +/*********************************************************************************************** + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * * + * This routine is used to verify that this object is allowed to move. Some objects can * + * be temporarily occupied and thus cannot move until the situation permits. * + * * + * INPUT: direction -- The direction that movement would be desired. * + * * + * OUTPUT: Can the unit move in the direction specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Ok_To_Move(DirType ) const +{ + assert(IsActive); + + return true; +} + + +/*************************************************************************** +** Smooth turn track tables. These are coordinate offsets from the center +** of the destination cell. These are the raw tracks that are modified +** by negating the X and Y portions as necessary. Also for reverse travelling +** direction, the track list can be processed backward. +** +** Track 1 = N +** Track 2 = NE +** Track 3 = N->NE 45 deg (double path consumption) +** Track 4 = N->E 90 deg (double path consumption) +** Track 5 = NE->SE 90 deg (double path consumption) +** Track 6 = NE->N 45 deg (double path consumption) +** Track 7 = N->NE (facing change only) +** Track 8 = NE->E (facing change only) +** Track 9 = N->E (facing change only) +** Track 10= NE->SE (facing change only) +** Track 11= back up into refinery +** Track 12= drive out of refinery +*/ +#pragma warn -ias +DriveClass::TrackType const DriveClass::Track1[24] = { + {0x00F50000L,(DirType)0}, + {0x00EA0000L,(DirType)0}, + {0x00DF0000L,(DirType)0}, + {0x00D40000L,(DirType)0}, + {0x00C90000L,(DirType)0}, + {0x00BE0000L,(DirType)0}, + {0x00B30000L,(DirType)0}, + {0x00A80000L,(DirType)0}, + {0x009D0000L,(DirType)0}, + {0x00920000L,(DirType)0}, + {0x00870000L,(DirType)0}, + {0x007C0000L,(DirType)0}, // Track jump check here. + {0x00710000L,(DirType)0}, + {0x00660000L,(DirType)0}, + {0x005B0000L,(DirType)0}, + {0x00500000L,(DirType)0}, + {0x00450000L,(DirType)0}, + {0x003A0000L,(DirType)0}, + {0x002F0000L,(DirType)0}, + {0x00240000L,(DirType)0}, + {0x00190000L,(DirType)0}, + {0x000E0000L,(DirType)0}, + {0x00030000L,(DirType)0}, + {0x00000000L,(DirType)0} +}; + +DriveClass::TrackType const DriveClass::Track2[] = { + {0x00F8FF08L,(DirType)32}, + {0x00F0FF10L,(DirType)32}, + {0x00E8FF18L,(DirType)32}, + {0x00E0FF20L,(DirType)32}, + {0x00D8FF28L,(DirType)32}, + {0x00D0FF30L,(DirType)32}, + {0x00C8FF38L,(DirType)32}, + {0x00C0FF40L,(DirType)32}, + {0x00B8FF48L,(DirType)32}, + {0x00B0FF50L,(DirType)32}, + {0x00A8FF58L,(DirType)32}, + {0x00A0FF60L,(DirType)32}, + {0x0098FF68L,(DirType)32}, + {0x0090FF70L,(DirType)32}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track3[] = { + {0x01F5FF00L,(DirType)0}, + {0x01EAFF00L,(DirType)0}, + {0x01DFFF00L,(DirType)0}, + {0x01D4FF00L,(DirType)0}, + {0x01C9FF00L,(DirType)0}, + {0x01BEFF00L,(DirType)0}, + {0x01B3FF00L,(DirType)0}, + {0x01A8FF00L,(DirType)0}, + {0x019DFF00L,(DirType)0}, + {0x0192FF00L,(DirType)0}, + {0x0187FF00L,(DirType)0}, + {0x0180FF00L,(DirType)0}, + {0x0175FF00L,(DirType)0}, // Jump entry point here. + {0x016BFF00L,(DirType)0}, + {0x0160FF02L,(DirType)1}, + {0x0155FF04L,(DirType)3}, + {0x014CFF06L,(DirType)4}, + {0x0141FF08L,(DirType)5}, + {0x0137FF0BL,(DirType)7}, + {0x012EFF0FL,(DirType)8}, + {0x0124FF13L,(DirType)9}, + {0x011AFF17L,(DirType)11}, + {0x0110FF1BL,(DirType)12}, + {0x0107FF1FL,(DirType)13}, // Center cell processing here. + {0x00FCFF24L,(DirType)15}, + {0x00F3FF28L,(DirType)16}, + {0x00ECFF2CL,(DirType)17}, + {0x00E0FF32L,(DirType)19}, + {0x00D7FF36L,(DirType)20}, + {0x00CFFF3DL,(DirType)21}, + {0x00C6FF42L,(DirType)23}, + {0x00BAFF49L,(DirType)24}, + {0x00B0FF4DL,(DirType)25}, + {0x00A8FF58L,(DirType)27}, + {0x00A0FF60L,(DirType)28}, + {0x0098FF68L,(DirType)29}, + {0x0090FF70L,(DirType)31}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track4[] = { + {0x00F5FF00L,(DirType)0}, + {0x00EBFF00L,(DirType)0}, + {0x00E0FF00L,(DirType)0}, + {0x00D5FF00L,(DirType)0}, + {0x00CBFF01L,(DirType)0}, + {0x00C0FF03L,(DirType)0}, + {0x00B5FF05L,(DirType)1}, + {0x00ABFF07L,(DirType)1}, + {0x00A0FF0AL,(DirType)2}, + {0x0095FF0DL,(DirType)3}, + {0x008BFF10L,(DirType)4}, + {0x0080FF14L,(DirType)5}, // Track entry here. + {0x0075FF18L,(DirType)8}, + {0x006DFF1CL,(DirType)12}, + {0x0063FF22L,(DirType)16}, + {0x005AFF25L,(DirType)20}, + {0x0052FF2BL,(DirType)23}, + {0x0048FF32L,(DirType)27}, + {0x0040FF37L,(DirType)32}, + {0x0038FF3DL,(DirType)36}, + {0x0030FF46L,(DirType)39}, + {0x002BFF4FL,(DirType)43}, + {0x0024FF58L,(DirType)47}, + {0x0020FF60L,(DirType)51}, + {0x001BFF6DL,(DirType)54}, + {0x0017FF79L,(DirType)57}, + {0x0014FF82L,(DirType)60}, // Track jump here. + {0x0011FF8FL,(DirType)62}, + {0x000DFF98L,(DirType)63}, + {0x0009FFA2L,(DirType)64}, + {0x0006FFACL,(DirType)64}, + {0x0004FFB5L,(DirType)66}, + {0x0003FFC0L,(DirType)64}, + {0x0002FFCBL,(DirType)64}, + {0x0001FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track5[] = { + {0xFFF8FE08L,(DirType)32}, + {0xFFF0FE10L,(DirType)32}, + {0xFFE8FE18L,(DirType)32}, + {0xFFE0FE20L,(DirType)32}, + {0xFFD8FE28L,(DirType)32}, + {0xFFD0FE30L,(DirType)32}, + {0xFFC8FE38L,(DirType)32}, + {0xFFC0FE40L,(DirType)32}, + {0xFFB8FE48L,(DirType)32}, + {0xFFB0FE50L,(DirType)32}, + {0xFFA8FE58L,(DirType)32}, + {0xFFA0FE60L,(DirType)32}, + {0xFF98FE68L,(DirType)32}, + {0xFF90FE70L,(DirType)32}, + {0xFF88FE78L,(DirType)32}, + {0xFF80FE80L,(DirType)32}, // Track entry here. + {0xFF78FE88L,(DirType)32}, + {0xFF71FE90L,(DirType)32}, + {0xFF6AFE97L,(DirType)32}, + {0xFF62FE9FL,(DirType)32}, + {0xFF5AFEA8L,(DirType)32}, + {0xFF53FEB0L,(DirType)35}, + {0xFF4BFEB7L,(DirType)38}, + {0xFF44FEBEL,(DirType)41}, + {0xFF3EFEC4L,(DirType)44}, + {0xFF39FECEL,(DirType)47}, + {0xFF34FED8L,(DirType)50}, + {0xFF30FEE0L,(DirType)53}, + {0xFF2DFEEBL,(DirType)56}, + {0xFF2CFEF5L,(DirType)59}, + {0xFF2BFF00L,(DirType)62}, + {0xFF2CFF0BL,(DirType)66}, + {0xFF2DFF15L,(DirType)69}, + {0xFF30FF1FL,(DirType)72}, + {0xFF34FF28L,(DirType)75}, + {0xFF39FF30L,(DirType)78}, + {0xFF3EFF3AL,(DirType)81}, + {0xFF44FF44L,(DirType)84}, + {0xFF4BFF4BL,(DirType)87}, + {0xFF53FF50L,(DirType)90}, + {0xFF5AFF58L,(DirType)93}, + {0xFF62FF60L,(DirType)96}, + {0xFF6AFF68L,(DirType)96}, + {0xFF71FF70L,(DirType)96}, + {0xFF78FF78L,(DirType)96}, + {0xFF80FF80L,(DirType)96}, // Track jump check here. + {0xFF88FF88L,(DirType)96}, + {0xFF90FF90L,(DirType)96}, + {0xFF98FF98L,(DirType)96}, + {0xFFA0FFA0L,(DirType)96}, + {0xFFA8FFA8L,(DirType)96}, + {0xFFB0FFB0L,(DirType)96}, + {0xFFB8FFB8L,(DirType)96}, + {0xFFC0FFC0L,(DirType)96}, + {0xFFC8FFC8L,(DirType)96}, + {0xFFD0FFD0L,(DirType)96}, + {0xFFD8FFD8L,(DirType)96}, + {0xFFE0FFE0L,(DirType)96}, + {0xFFE8FFE8L,(DirType)96}, + {0xFFF0FFF0L,(DirType)96}, + {0xFFF8FFF8L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track6[] = { + {0x0100FE00L,(DirType)32}, + {0x00F8FE08L,(DirType)32}, + {0x00F0FE10L,(DirType)32}, + {0x00E8FE18L,(DirType)32}, + {0x00E0FE20L,(DirType)32}, + {0x00D8FE28L,(DirType)32}, + {0x00D0FE30L,(DirType)32}, + {0x00C8FE38L,(DirType)32}, + {0x00C0FE40L,(DirType)32}, + {0x00B8FE48L,(DirType)32}, + {0x00B0FE50L,(DirType)32}, + {0x00A8FE58L,(DirType)32}, + {0x00A0FE60L,(DirType)32}, + {0x0098FE68L,(DirType)32}, + {0x0090FE70L,(DirType)32}, + {0x0088FE78L,(DirType)32}, + {0x0080FE80L,(DirType)32}, // Jump entry point here. + {0x0078FE88L,(DirType)32}, + {0x0070FE90L,(DirType)32}, + {0x0068FE98L,(DirType)32}, + {0x0060FEA0L,(DirType)32}, + {0x0058FEA8L,(DirType)32}, + {0x0055FEAEL,(DirType)32}, + {0x004EFEB8L,(DirType)35}, + {0x0048FEC0L,(DirType)37}, + {0x0042FEC9L,(DirType)40}, + {0x003BFED2L,(DirType)43}, + {0x0037FEDAL,(DirType)45}, + {0x0032FEE3L,(DirType)48}, + {0x002BFEEBL,(DirType)51}, + {0x0026FEF5L,(DirType)53}, + {0x0022FEFEL,(DirType)56}, + {0x001CFF08L,(DirType)59}, + {0x0019FF12L,(DirType)61}, + {0x0015FF1BL,(DirType)64}, + {0x0011FF26L,(DirType)64}, + {0x000EFF30L,(DirType)64}, + {0x000BFF39L,(DirType)64}, + {0x0009FF43L,(DirType)64}, + {0x0007FF4EL,(DirType)64}, + {0x0005FF57L,(DirType)64}, + {0x0003FF62L,(DirType)64}, + {0x0001FF6DL,(DirType)64}, + {0x0000FF77L,(DirType)64}, + {0x0000FF80L,(DirType)64}, // Track jump check here. + {0x0000FF8BL,(DirType)64}, + {0x0000FF95L,(DirType)64}, + {0x0000FFA0L,(DirType)64}, + {0x0000FFABL,(DirType)64}, + {0x0000FFB5L,(DirType)64}, + {0x0000FFC0L,(DirType)64}, + {0x0000FFCBL,(DirType)64}, + {0x0000FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track7[] = { + {0x0006FFFFL,(DirType)0}, + {0x000CFFFEL,(DirType)4}, + {0x0011FFFCL,(DirType)8}, + {0x0018FFFAL,(DirType)12}, + {0x001FFFF6L,(DirType)16}, + {0x0024FFF3L,(DirType)19}, + {0x002BFFF0L,(DirType)22}, + {0x0030FFFDL,(DirType)23}, + {0x0035FFEBL,(DirType)24}, + {0x0038FFE8L,(DirType)25}, + {0x003CFFE6L,(DirType)26}, + {0x0040FFE3L,(DirType)27}, + {0x0043FFE0L,(DirType)28}, + {0x0046FFDDL,(DirType)29}, + {0x0043FFDFL,(DirType)30}, + {0x0040FFE1L,(DirType)30}, + {0x003CFFE3L,(DirType)30}, + {0x0038FFE5L,(DirType)30}, + {0x0035FFE7L,(DirType)31}, + {0x0030FFE9L,(DirType)31}, + {0x002BFFEBL,(DirType)31}, + {0x0024FFEDL,(DirType)31}, + {0x001FFFF1L,(DirType)31}, + {0x0018FFF4L,(DirType)32}, + {0x0011FFF7L,(DirType)32}, + {0x000CFFFAL,(DirType)32}, + {0x0006FFFDL,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track8[] = { + {0x0003FFFCL,(DirType)32}, + {0x0006FFF7L,(DirType)36}, + {0x000AFFF1L,(DirType)40}, + {0x000CFFEBL,(DirType)44}, + {0x000DFFE4L,(DirType)46}, + {0x000EFFDCL,(DirType)48}, + {0x000FFFD5L,(DirType)50}, + {0x0010FFD0L,(DirType)52}, + {0x0011FFC9L,(DirType)54}, + {0x0012FFC2L,(DirType)56}, + {0x0011FFC0L,(DirType)58}, + {0x0010FFC2L,(DirType)60}, + {0x000EFFC9L,(DirType)62}, + {0x000CFFCFL,(DirType)64}, + {0x000AFFD5L,(DirType)64}, + {0x0008FFDAL,(DirType)64}, + {0x0006FFE2L,(DirType)64}, + {0x0004FFE9L,(DirType)64}, + {0x0002FFEFL,(DirType)64}, + {0x0001FFF5L,(DirType)64}, + {0x0000FFF9L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track9[] = { + {0xFFF50002L,(DirType)0}, + {0xFFEB0004L,(DirType)2}, + {0xFFE00006L,(DirType)4}, + {0xFFD50009L,(DirType)6}, + {0xFFCE000CL,(DirType)9}, + {0xFFC8000FL,(DirType)11}, + {0xFFC00012L,(DirType)13}, + {0xFFB80015L,(DirType)16}, + {0xFFC00012L,(DirType)18}, + {0xFFC8000EL,(DirType)20}, + {0xFFCE000AL,(DirType)22}, + {0xFFD50004L,(DirType)24}, + {0xFFDE0000L,(DirType)26}, + {0xFFE9FFF8L,(DirType)28}, + {0xFFEEFFF2L,(DirType)30}, + {0xFFF5FFEBL,(DirType)32}, + {0xFFFDFFE1L,(DirType)34}, + {0x0002FFD8L,(DirType)36}, + {0x0007FFD2L,(DirType)39}, + {0x000BFFCBL,(DirType)41}, + {0x0010FFC5L,(DirType)43}, + {0x0013FFBEL,(DirType)45}, + {0x0015FFB7L,(DirType)48}, + {0x0013FFBEL,(DirType)50}, + {0x0011FFC5L,(DirType)52}, + {0x000BFFCCL,(DirType)54}, + {0x0008FFD4L,(DirType)56}, + {0x0005FFDFL,(DirType)58}, + {0x0003FFEBL,(DirType)62}, + {0x0001FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track10[] = { + {0xFFF6000BL,(DirType)32}, + {0xFFF00015L,(DirType)37}, + {0xFFEB0020L,(DirType)42}, + {0xFFE9002BL,(DirType)47}, + {0xFFE50032L,(DirType)52}, + {0xFFE30038L,(DirType)57}, + {0xFFE00040L,(DirType)60}, + {0xFFE20038L,(DirType)62}, + {0xFFE40032L,(DirType)64}, + {0xFFE5002AL,(DirType)68}, + {0xFFE6001EL,(DirType)70}, + {0xFFE70015L,(DirType)72}, + {0xFFE8000BL,(DirType)74}, + {0xFFE90000L,(DirType)76}, + {0xFFE8FFF5L,(DirType)78}, + {0xFFE7FFEBL,(DirType)80}, + {0xFFE6FFE0L,(DirType)82}, + {0xFFE5FFD5L,(DirType)84}, + {0xFFE4FFCEL,(DirType)86}, + {0xFFE2FFC5L,(DirType)88}, + {0xFFE0FFC0L,(DirType)90}, + {0xFFE3FFC5L,(DirType)92}, + {0xFFE5FFCEL,(DirType)94}, + {0xFFE9FFD5L,(DirType)95}, + {0xFFEBFFE0L,(DirType)96}, + {0xFFF0FFEBL,(DirType)96}, + {0xFFF6FFF5L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track11[] = { + {0x01000000L,DIR_SW}, + {0x00F30008L,DIR_SW}, + {0x00E50010L,DIR_SW_X1}, + {0x00D60018L,DIR_SW_X1}, + {0x00C80020L,DIR_SW_X1}, + {0x00B90028L,DIR_SW_X1}, + {0x00AB0030L,DIR_SW_X2}, + {0x009C0038L,DIR_SW_X2}, + {0x008D0040L,DIR_SW_X2}, + {0x007F0048L,DIR_SW_X2}, + {0x00710050L,DIR_SW_X2}, + {0x00640058L,DIR_SW_X2}, + {0x00550060L,DIR_SW_X2}, + + {0x00000000L,DIR_SW_X2} +}; + +DriveClass::TrackType const DriveClass::Track12[] = { + {0xFF550060L,DIR_SW_X2}, + {0xFF640058L,DIR_SW_X2}, + {0xFF710050L,DIR_SW_X2}, + {0xFF7F0048L,DIR_SW_X2}, + {0xFF8D0040L,DIR_SW_X2}, + {0xFF9C0038L,DIR_SW_X2}, + {0xFFAB0030L,DIR_SW_X2}, + {0xFFB90028L,DIR_SW_X1}, + {0xFFC80020L,DIR_SW_X1}, + {0xFFD60018L,DIR_SW_X1}, + {0xFFE50010L,DIR_SW_X1}, + {0xFFF30008L,DIR_SW}, + + {0x00000000L,DIR_SW} +}; + +#if(1) +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(0,-35),DIR_S}, + {XYP_COORD(0,-34),DIR_S}, + {XYP_COORD(0,-33),DIR_S}, + {XYP_COORD(0,-32),DIR_S}, + {XYP_COORD(0,-31),DIR_S}, + {XYP_COORD(0,-30),DIR_S}, + {XYP_COORD(0,-29),DIR_S}, + {XYP_COORD(0,-28),DIR_S}, + {XYP_COORD(0,-27),DIR_S}, + {XYP_COORD(0,-26),DIR_S}, + {XYP_COORD(0,-25),DIR_S}, + {XYP_COORD(0,-24),DIR_S}, + {XYP_COORD(0,-23),DIR_S}, + {XYP_COORD(0,-22),DIR_S}, + {XYP_COORD(0,-21),DIR_S}, + {XYP_COORD(0,-20),DIR_S}, + {XYP_COORD(0,-19),DIR_S}, + {XYP_COORD(0,-18),DIR_S}, + {XYP_COORD(0,-17),DIR_S}, + {XYP_COORD(0,-16),DIR_S}, + {XYP_COORD(0,-15),DIR_S}, + {XYP_COORD(0,-14),DIR_S}, + {XYP_COORD(0,-13),DIR_S}, + {XYP_COORD(0,-12),DIR_S}, + {XYP_COORD(0,-11),DIR_S}, + {XYP_COORD(0,-10),DIR_S}, + {XYP_COORD(0,-9),DIR_S}, + {XYP_COORD(0,-8),DIR_S}, + {XYP_COORD(0,-7),DIR_S}, + {XYP_COORD(0,-6),DIR_S}, + {XYP_COORD(0,-5),DIR_S}, + {XYP_COORD(0,-4),DIR_S}, + {XYP_COORD(0,-3),DIR_S}, + {XYP_COORD(0,-2),DIR_S}, + {XYP_COORD(0,-1),DIR_S}, + + {0x00000000L,DIR_S} +}; +#else +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-17),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-16),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-15),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-14),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-13),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-12),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-11),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-10),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-9),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-8),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-7),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-6),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-5),(DirType)(DIR_SW-9)}, + {XYP_COORD(2,-4),(DirType)(DIR_SW-7)}, + {XYP_COORD(2,-3),(DirType)(DIR_SW-5)}, + {XYP_COORD(1,-2),(DirType)(DIR_SW-3)}, + {XYP_COORD(1,-1),(DirType)(DIR_SW-1)}, + + {0x00000000L,DIR_SW} +}; +#endif + +/* +** There are a limited basic number of tracks that a vehicle can follow. These +** are they. Each track can be interpreted differently but this is controlled +** by the TrackControl structure elaborated elsewhere. +*/ +DriveClass::RawTrackType const DriveClass::RawTracks[13] = { + {Track1, -1, 0, -1}, + {Track2, -1, 0, -1}, + {Track3, 37, 12, 22}, + {Track4, 26, 11, 19}, + {Track5, 45, 15, 31}, + {Track6, 44, 16, 27}, + {Track7, -1, 0, -1}, + {Track8, -1, 0, -1}, + {Track9, -1, 0, -1}, + {Track10, -1, 0, -1}, + {Track11, -1, 0, -1}, + {Track12, -1, 0, -1}, + {Track13, -1, 0, -1} +}; + + +/*************************************************************************** +** Smooth turning control table. Given two directions in a path list, this +** table determines which track to use and what modifying operations need +** be performed on the track data. +*/ +DriveClass::TurnTrackType const DriveClass::TrackControl[67] = { + {1, 0, DIR_N, F_}, // 0-0 + {3, 7, DIR_NE, F_D}, // 0-1 (raw chart) + {4, 9, DIR_E, F_D}, // 0-2 (raw chart) + {0, 0, DIR_SE, F_}, // 0-3 ! + {0, 0, DIR_S, F_}, // 0-4 ! + {0, 0, DIR_SW, F_}, // 0-5 ! + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0 + {2, 0, DIR_NE, F_}, // 1-1 (raw chart) + {6, 8, DIR_E, F_D}, // 1-2 (raw chart) + {5, 10, DIR_SE, F_D}, // 1-3 (raw chart) + {0, 0, DIR_S, F_}, // 1-4 ! + {0, 0, DIR_SW, F_}, // 1-5 ! + {0, 0, DIR_W, F_}, // 1-6 ! + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0 + {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1 + {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3 + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4 + {0, 0, DIR_SW, F_}, // 2-5 ! + {0, 0, DIR_W, F_}, // 2-6 ! + {0, 0, DIR_NW, F_}, // 2-7 ! + {0, 0, DIR_N, F_}, // 3-0 ! + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1 + {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2 + {2, 0, DIR_SE, F_Y}, // 3-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4 + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5 + {0, 0, DIR_W, F_}, // 3-6 ! + {0, 0, DIR_NW, F_}, // 3-7 ! + {0, 0, DIR_N, F_}, // 4-0 ! + {0, 0, DIR_NE, F_}, // 4-1 ! + {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3 + {1, 0, DIR_S, F_Y}, // 4-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5 + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6 + {0, 0, DIR_NW, F_}, // 4-7 ! + {0, 0, DIR_N, F_}, // 5-0 ! + {0, 0, DIR_NE, F_}, // 5-1 ! + {0, 0, DIR_E, F_}, // 5-2 ! + {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4 + {2, 0, DIR_SW, F_T}, // 5-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6 + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0 + {0, 0, DIR_NE, F_}, // 6-1 ! + {0, 0, DIR_E, F_}, // 6-2 ! + {0, 0, DIR_SE, F_}, // 6-3 ! + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5 + {1, 0, DIR_W, F_T}, // 6-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0 + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1 + {0, 0, DIR_E, F_}, // 7-2 ! + {0, 0, DIR_SE, F_}, // 7-3 ! + {0, 0, DIR_S, F_}, // 7-4 ! + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6 + {2, 0, DIR_NW, F_X}, // 7-7 + + {11, 11, DIR_SW, F_}, // Backup harvester into refinery. + {12, 12, DIR_SW_X2, F_}, // Drive back into refinery. + {13, 13, DIR_SW, F_} // Drive out of weapons factory. +}; + + + diff --git a/CODE/DRIVE.H b/CODE/DRIVE.H new file mode 100644 index 0000000..1971c4a --- /dev/null +++ b/CODE/DRIVE.H @@ -0,0 +1,211 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DRIVE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DRIVE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DRIVE_H +#define DRIVE_H + +#include "foot.h" + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class DriveClass : public FootClass +{ + public: + /* + ** If this unit performing harvesting action, then this flag is true. The flag + ** is located here because the other bit flags here give it a free place to + ** reside. + */ + unsigned IsHarvesting:1; + + /* + ** This flag controls whether the unit has been moebius'd into a + ** different location, and whether the MoebiusCountDown timer should be + ** used to take him back where he belongs. + */ + unsigned IsMoebius:1; + + /* + ** This controls how long a unit can exist in its alternate location + ** before being pulled back by the chronosphere into its normal location. + */ + CDTimerClass MoebiusCountDown; + + /* + ** This is the coord the unit will be taken back to once its moebius + ** effect wears off. + */ + CELL MoebiusCell; + + /* + ** Some units must have their turret locked down to face their body direction. + ** When this flag is set, this condition is in effect. This flag is a more + ** accurate check than examining the TrackNumber since the turret may be + ** rotating into position so that a pending track may start. During this process + ** the track number does not indicate anything. + */ + unsigned IsTurretLockedDown:1; + + /* + ** This vehicle could be processing a "short track". A short track is one that + ** doesn't actually go anywhere. Kind of like turning in place. + */ + unsigned IsOnShortTrack:1; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + DriveClass(RTTIType rtti, int id, HousesType house); + DriveClass(NoInitClass const & x) : FootClass(x), MoebiusCountDown(x) {}; + virtual ~DriveClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Teleport_To(CELL cell); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + virtual bool Limbo(void); + void Do_Turn(DirType dir); + virtual void Overrun_Square(CELL , bool =true) {}; + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual bool Ok_To_Move(DirType ) const; + virtual void AI(void); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + void Force_Track(int track, COORDINATE coord); + virtual bool Stop_Driver(void); + + void Mark_Track(COORDINATE headto, MarkType type); + + /********************************************************************** + ** These enumerations are used as working constants that exist only + ** in the DriveClass namespace. + */ + enum DriveClassEnum { + BACKUP_INTO_REFINERY=64, // Track to backup into refinery. + OUT_OF_REFINERY, // Track to leave refinery. + OUT_OF_WEAPON_FACTORY // Track to leave weapons factory. + }; + + /**************************************************************************** + ** Smooth turning tracks are controlled by this structure and these + ** processing bits. + */ + typedef enum TrackControlType { + F_=0x00, // No translation necessary? + F_T=0x01, // Transpose X and Y components? + F_X=0x02, // Reverse X component sign? + F_Y=0x04, // Reverse Y component sign? + F_D=0x08 // Two cell consumption? + } TrackControlType; + + private: + + typedef struct { + char Track; // Which track to use. + char StartTrack; // Track when starting from stand-still. + DirType Facing; // Facing when track has been completed. + DriveClass::TrackControlType Flag; // List processing flag bits. + } TurnTrackType; + + typedef struct { + COORDINATE Offset; // Offset to origin coordinate. + DirType Facing; // Facing (primary track). + } TrackType; + + typedef struct { + TrackType const * Track; // Pointer to track list. + int Jump; // Index where track jumping is allowed. + int Entry; // Entry point if jumping to this track. + int Cell; // Per cell process should occur at this index. + } RawTrackType; + + /* + ** These speed values are used to accumulate movement and then + ** convert them into pixel "steps" that are then translated through + ** the currently running track so that the unit will move. + */ + int SpeedAccum; + + /* + ** This the track control logic (used for ground vehicles only). The 'Track' + ** variable holds the track being followed (0 == not following track). The + ** 'TrackIndex' variable holds the current index into the specified track + ** (starts at 0). + */ + int TrackNumber; + int TrackIndex; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual void Fixup_Path(PathType *path); + bool While_Moving(void); + bool Start_Of_Move(void); + void Lay_Track(void); + COORDINATE Smooth_Turn(COORDINATE adj, DirType & dir); + + static TurnTrackType const TrackControl[67]; + static RawTrackType const RawTracks[13]; + static TrackType const Track13[]; + static TrackType const Track12[]; + static TrackType const Track11[]; + static TrackType const Track10[]; + static TrackType const Track9[]; + static TrackType const Track8[]; + static TrackType const Track7[]; + static TrackType const Track6[]; + static TrackType const Track5[]; + static TrackType const Track4[]; + static TrackType const Track3[]; + static TrackType const Track2[]; + static TrackType const Track1[24]; +}; + +inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType); +inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType); +inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType); + + +#endif diff --git a/CODE/DROP.CPP b/CODE/DROP.CPP new file mode 100644 index 0000000..8c5c55e --- /dev/null +++ b/CODE/DROP.CPP @@ -0,0 +1,219 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DROP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DROP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/24/96 * + * * + * Last Update : January 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "drop.h" + + +DropListClass::DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9*RESFACTOR, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + Fancy_Text_Print("", 0, 0, 0, 0, flags); + EditClass::Height = FontHeight+1; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + +DropListClass & DropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((DropListClass &)EditClass::Add(object)); +} + +DropListClass & DropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((DropListClass &)EditClass::Add_Tail(object)); +} + +DropListClass & DropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((DropListClass &)EditClass::Add_Head(object)); +} + +DropListClass * DropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((DropListClass *)EditClass::Remove()); +} + +int DropListClass::Add_Item(char const * text) +{ + strncpy(String, text, MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(text)); +} + +char const * DropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + +int DropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + +void DropListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Set_Selected_Index(index); + strcpy(String, List.Get_Item(Current_Index())); + } else { + String[0] = '\0'; + } +} + + +void DropListClass::Clear_Focus(void) +{ + Collapse(); +} + +void DropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + +void DropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9*RESFACTOR; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + +void DropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +DropListClass & DropListClass::operator = (DropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +DropListClass::DropListClass(DropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +void DropListClass::Set_Selected_Index(char const * text) +{ + if (text) { + for (int index = 0; index < Count(); index++) { + if (stricmp(text, List.Get_Item(index)) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} + +#ifdef WOLAPI_INTEGRATION +void DropListClass::Flag_To_Redraw(void) +{ + if( IsDropped ) + List.Flag_To_Redraw(); + EditClass::Flag_To_Redraw(); +} +#endif diff --git a/CODE/DROP.H b/CODE/DROP.H new file mode 100644 index 0000000..0f8e791 --- /dev/null +++ b/CODE/DROP.H @@ -0,0 +1,353 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DROP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : DROP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DROP_H +#define DROP_H + +#include "list.h" +#include "edit.h" + +class DropListClass : public EditClass { + public: + DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + virtual ~DropListClass(void) {}; + + virtual DropListClass & Add(LinkClass & object); + virtual DropListClass & Add_Tail(LinkClass & object); + virtual DropListClass & Add_Head(LinkClass & object); + virtual DropListClass * Remove(void); + virtual void Zap(void); + + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual char const * Get_Item(int index) const {return(List.Get_Item(index));}; + +#ifdef WOLAPI_INTEGRATION + virtual void Flag_To_Redraw(void); +#endif + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + DropListClass & operator = (DropListClass const & list); + DropListClass(DropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + ListClass List; +}; + + + + +template +class TDropListClass : public EditClass { + public: + TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + TDropListClass(TDropListClass const & list); + virtual ~TDropListClass(void) {}; + + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual TDropListClass & Add(LinkClass & object); + virtual TDropListClass & Add_Tail(LinkClass & object); + virtual TDropListClass & Add_Head(LinkClass & object); + virtual TDropListClass * Remove(void); + void Zap(void); + + virtual int Add_Item(T text); + virtual T Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual T Get_Item(int index) const {return(List.Get_Item(index));}; + + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + TDropListClass & operator = (TDropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + TListClass List; +}; + + +template +TDropListClass::TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + + +template +TDropListClass & TDropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((TDropListClass &)EditClass::Add(object)); +} + + +template +TDropListClass & TDropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((TDropListClass &)EditClass::Add_Tail(object)); +} + + +template +TDropListClass & TDropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((TDropListClass &)EditClass::Add_Head(object)); +} + + +template +TDropListClass * TDropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((TDropListClass *)EditClass::Remove()); +} + + +template +int TDropListClass::Add_Item(T item) +{ + strncpy(String, item->Description(), MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(item)); +} + + +template +T TDropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + + +template +int TDropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + + +template +void TDropListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Set_Selected_Index(index); + strncpy(String, List.Get_Item(Current_Index())->Description(), MaxLength); + } else { + String[0] = '\0'; + } +} + + +template +void TDropListClass::Clear_Focus(void) +{ + Collapse(); +} + + +template +void TDropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item()->Description(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + + +template +void TDropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + + +template +void TDropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +template +TDropListClass & TDropListClass::operator = (TDropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +template +TDropListClass::TDropListClass(TDropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +template +void TDropListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == List.Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/CODE/DTABLE.CPP b/CODE/DTABLE.CPP new file mode 100644 index 0000000..c4dd9a6 --- /dev/null +++ b/CODE/DTABLE.CPP @@ -0,0 +1,1446 @@ +/* +** 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 . +*/ + + +long DiffTable[] = { + 0, // Index = 0, Token = 0 + 1, // Index = 0, Token = 1 + 3, // Index = 0, Token = 2 + 4, // Index = 0, Token = 3 + 7, // Index = 0, Token = 4 + 8, // Index = 0, Token = 5 + 10, // Index = 0, Token = 6 + 11, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + -1, // Index = 0, Token = 9 + -3, // Index = 0, Token = 10 + -4, // Index = 0, Token = 11 + -7, // Index = 0, Token = 12 + -8, // Index = 0, Token = 13 + -10, // Index = 0, Token = 14 + -11, // Index = 0, Token = 15 + 1, // Index = 1, Token = 0 + 3, // Index = 1, Token = 1 + 5, // Index = 1, Token = 2 + 7, // Index = 1, Token = 3 + 9, // Index = 1, Token = 4 + 11, // Index = 1, Token = 5 + 13, // Index = 1, Token = 6 + 15, // Index = 1, Token = 7 + -1, // Index = 1, Token = 8 + -3, // Index = 1, Token = 9 + -5, // Index = 1, Token = 10 + -7, // Index = 1, Token = 11 + -9, // Index = 1, Token = 12 + -11, // Index = 1, Token = 13 + -13, // Index = 1, Token = 14 + -15, // Index = 1, Token = 15 + 1, // Index = 2, Token = 0 + 3, // Index = 2, Token = 1 + 5, // Index = 2, Token = 2 + 7, // Index = 2, Token = 3 + 10, // Index = 2, Token = 4 + 12, // Index = 2, Token = 5 + 14, // Index = 2, Token = 6 + 16, // Index = 2, Token = 7 + -1, // Index = 2, Token = 8 + -3, // Index = 2, Token = 9 + -5, // Index = 2, Token = 10 + -7, // Index = 2, Token = 11 + -10, // Index = 2, Token = 12 + -12, // Index = 2, Token = 13 + -14, // Index = 2, Token = 14 + -16, // Index = 2, Token = 15 + 1, // Index = 3, Token = 0 + 3, // Index = 3, Token = 1 + 6, // Index = 3, Token = 2 + 8, // Index = 3, Token = 3 + 11, // Index = 3, Token = 4 + 13, // Index = 3, Token = 5 + 16, // Index = 3, Token = 6 + 18, // Index = 3, Token = 7 + -1, // Index = 3, Token = 8 + -3, // Index = 3, Token = 9 + -6, // Index = 3, Token = 10 + -8, // Index = 3, Token = 11 + -11, // Index = 3, Token = 12 + -13, // Index = 3, Token = 13 + -16, // Index = 3, Token = 14 + -18, // Index = 3, Token = 15 + 1, // Index = 4, Token = 0 + 3, // Index = 4, Token = 1 + 6, // Index = 4, Token = 2 + 8, // Index = 4, Token = 3 + 12, // Index = 4, Token = 4 + 14, // Index = 4, Token = 5 + 17, // Index = 4, Token = 6 + 19, // Index = 4, Token = 7 + -1, // Index = 4, Token = 8 + -3, // Index = 4, Token = 9 + -6, // Index = 4, Token = 10 + -8, // Index = 4, Token = 11 + -12, // Index = 4, Token = 12 + -14, // Index = 4, Token = 13 + -17, // Index = 4, Token = 14 + -19, // Index = 4, Token = 15 + 1, // Index = 5, Token = 0 + 4, // Index = 5, Token = 1 + 7, // Index = 5, Token = 2 + 10, // Index = 5, Token = 3 + 13, // Index = 5, Token = 4 + 16, // Index = 5, Token = 5 + 19, // Index = 5, Token = 6 + 22, // Index = 5, Token = 7 + -1, // Index = 5, Token = 8 + -4, // Index = 5, Token = 9 + -7, // Index = 5, Token = 10 + -10, // Index = 5, Token = 11 + -13, // Index = 5, Token = 12 + -16, // Index = 5, Token = 13 + -19, // Index = 5, Token = 14 + -22, // Index = 5, Token = 15 + 1, // Index = 6, Token = 0 + 4, // Index = 6, Token = 1 + 7, // Index = 6, Token = 2 + 10, // Index = 6, Token = 3 + 14, // Index = 6, Token = 4 + 17, // Index = 6, Token = 5 + 20, // Index = 6, Token = 6 + 23, // Index = 6, Token = 7 + -1, // Index = 6, Token = 8 + -4, // Index = 6, Token = 9 + -7, // Index = 6, Token = 10 + -10, // Index = 6, Token = 11 + -14, // Index = 6, Token = 12 + -17, // Index = 6, Token = 13 + -20, // Index = 6, Token = 14 + -23, // Index = 6, Token = 15 + 1, // Index = 7, Token = 0 + 4, // Index = 7, Token = 1 + 8, // Index = 7, Token = 2 + 11, // Index = 7, Token = 3 + 15, // Index = 7, Token = 4 + 18, // Index = 7, Token = 5 + 22, // Index = 7, Token = 6 + 25, // Index = 7, Token = 7 + -1, // Index = 7, Token = 8 + -4, // Index = 7, Token = 9 + -8, // Index = 7, Token = 10 + -11, // Index = 7, Token = 11 + -15, // Index = 7, Token = 12 + -18, // Index = 7, Token = 13 + -22, // Index = 7, Token = 14 + -25, // Index = 7, Token = 15 + 2, // Index = 8, Token = 0 + 6, // Index = 8, Token = 1 + 10, // Index = 8, Token = 2 + 14, // Index = 8, Token = 3 + 18, // Index = 8, Token = 4 + 22, // Index = 8, Token = 5 + 26, // Index = 8, Token = 6 + 30, // Index = 8, Token = 7 + -2, // Index = 8, Token = 8 + -6, // Index = 8, Token = 9 + -10, // Index = 8, Token = 10 + -14, // Index = 8, Token = 11 + -18, // Index = 8, Token = 12 + -22, // Index = 8, Token = 13 + -26, // Index = 8, Token = 14 + -30, // Index = 8, Token = 15 + 2, // Index = 9, Token = 0 + 6, // Index = 9, Token = 1 + 10, // Index = 9, Token = 2 + 14, // Index = 9, Token = 3 + 19, // Index = 9, Token = 4 + 23, // Index = 9, Token = 5 + 27, // Index = 9, Token = 6 + 31, // Index = 9, Token = 7 + -2, // Index = 9, Token = 8 + -6, // Index = 9, Token = 9 + -10, // Index = 9, Token = 10 + -14, // Index = 9, Token = 11 + -19, // Index = 9, Token = 12 + -23, // Index = 9, Token = 13 + -27, // Index = 9, Token = 14 + -31, // Index = 9, Token = 15 + 2, // Index = 10, Token = 0 + 6, // Index = 10, Token = 1 + 11, // Index = 10, Token = 2 + 15, // Index = 10, Token = 3 + 21, // Index = 10, Token = 4 + 25, // Index = 10, Token = 5 + 30, // Index = 10, Token = 6 + 34, // Index = 10, Token = 7 + -2, // Index = 10, Token = 8 + -6, // Index = 10, Token = 9 + -11, // Index = 10, Token = 10 + -15, // Index = 10, Token = 11 + -21, // Index = 10, Token = 12 + -25, // Index = 10, Token = 13 + -30, // Index = 10, Token = 14 + -34, // Index = 10, Token = 15 + 2, // Index = 11, Token = 0 + 7, // Index = 11, Token = 1 + 12, // Index = 11, Token = 2 + 17, // Index = 11, Token = 3 + 23, // Index = 11, Token = 4 + 28, // Index = 11, Token = 5 + 33, // Index = 11, Token = 6 + 38, // Index = 11, Token = 7 + -2, // Index = 11, Token = 8 + -7, // Index = 11, Token = 9 + -12, // Index = 11, Token = 10 + -17, // Index = 11, Token = 11 + -23, // Index = 11, Token = 12 + -28, // Index = 11, Token = 13 + -33, // Index = 11, Token = 14 + -38, // Index = 11, Token = 15 + 2, // Index = 12, Token = 0 + 7, // Index = 12, Token = 1 + 13, // Index = 12, Token = 2 + 18, // Index = 12, Token = 3 + 25, // Index = 12, Token = 4 + 30, // Index = 12, Token = 5 + 36, // Index = 12, Token = 6 + 41, // Index = 12, Token = 7 + -2, // Index = 12, Token = 8 + -7, // Index = 12, Token = 9 + -13, // Index = 12, Token = 10 + -18, // Index = 12, Token = 11 + -25, // Index = 12, Token = 12 + -30, // Index = 12, Token = 13 + -36, // Index = 12, Token = 14 + -41, // Index = 12, Token = 15 + 3, // Index = 13, Token = 0 + 9, // Index = 13, Token = 1 + 15, // Index = 13, Token = 2 + 21, // Index = 13, Token = 3 + 28, // Index = 13, Token = 4 + 34, // Index = 13, Token = 5 + 40, // Index = 13, Token = 6 + 46, // Index = 13, Token = 7 + -3, // Index = 13, Token = 8 + -9, // Index = 13, Token = 9 + -15, // Index = 13, Token = 10 + -21, // Index = 13, Token = 11 + -28, // Index = 13, Token = 12 + -34, // Index = 13, Token = 13 + -40, // Index = 13, Token = 14 + -46, // Index = 13, Token = 15 + 3, // Index = 14, Token = 0 + 10, // Index = 14, Token = 1 + 17, // Index = 14, Token = 2 + 24, // Index = 14, Token = 3 + 31, // Index = 14, Token = 4 + 38, // Index = 14, Token = 5 + 45, // Index = 14, Token = 6 + 52, // Index = 14, Token = 7 + -3, // Index = 14, Token = 8 + -10, // Index = 14, Token = 9 + -17, // Index = 14, Token = 10 + -24, // Index = 14, Token = 11 + -31, // Index = 14, Token = 12 + -38, // Index = 14, Token = 13 + -45, // Index = 14, Token = 14 + -52, // Index = 14, Token = 15 + 3, // Index = 15, Token = 0 + 10, // Index = 15, Token = 1 + 18, // Index = 15, Token = 2 + 25, // Index = 15, Token = 3 + 34, // Index = 15, Token = 4 + 41, // Index = 15, Token = 5 + 49, // Index = 15, Token = 6 + 56, // Index = 15, Token = 7 + -3, // Index = 15, Token = 8 + -10, // Index = 15, Token = 9 + -18, // Index = 15, Token = 10 + -25, // Index = 15, Token = 11 + -34, // Index = 15, Token = 12 + -41, // Index = 15, Token = 13 + -49, // Index = 15, Token = 14 + -56, // Index = 15, Token = 15 + 4, // Index = 16, Token = 0 + 12, // Index = 16, Token = 1 + 21, // Index = 16, Token = 2 + 29, // Index = 16, Token = 3 + 38, // Index = 16, Token = 4 + 46, // Index = 16, Token = 5 + 55, // Index = 16, Token = 6 + 63, // Index = 16, Token = 7 + -4, // Index = 16, Token = 8 + -12, // Index = 16, Token = 9 + -21, // Index = 16, Token = 10 + -29, // Index = 16, Token = 11 + -38, // Index = 16, Token = 12 + -46, // Index = 16, Token = 13 + -55, // Index = 16, Token = 14 + -63, // Index = 16, Token = 15 + 4, // Index = 17, Token = 0 + 13, // Index = 17, Token = 1 + 22, // Index = 17, Token = 2 + 31, // Index = 17, Token = 3 + 41, // Index = 17, Token = 4 + 50, // Index = 17, Token = 5 + 59, // Index = 17, Token = 6 + 68, // Index = 17, Token = 7 + -4, // Index = 17, Token = 8 + -13, // Index = 17, Token = 9 + -22, // Index = 17, Token = 10 + -31, // Index = 17, Token = 11 + -41, // Index = 17, Token = 12 + -50, // Index = 17, Token = 13 + -59, // Index = 17, Token = 14 + -68, // Index = 17, Token = 15 + 5, // Index = 18, Token = 0 + 15, // Index = 18, Token = 1 + 25, // Index = 18, Token = 2 + 35, // Index = 18, Token = 3 + 46, // Index = 18, Token = 4 + 56, // Index = 18, Token = 5 + 66, // Index = 18, Token = 6 + 76, // Index = 18, Token = 7 + -5, // Index = 18, Token = 8 + -15, // Index = 18, Token = 9 + -25, // Index = 18, Token = 10 + -35, // Index = 18, Token = 11 + -46, // Index = 18, Token = 12 + -56, // Index = 18, Token = 13 + -66, // Index = 18, Token = 14 + -76, // Index = 18, Token = 15 + 5, // Index = 19, Token = 0 + 16, // Index = 19, Token = 1 + 27, // Index = 19, Token = 2 + 38, // Index = 19, Token = 3 + 50, // Index = 19, Token = 4 + 61, // Index = 19, Token = 5 + 72, // Index = 19, Token = 6 + 83, // Index = 19, Token = 7 + -5, // Index = 19, Token = 8 + -16, // Index = 19, Token = 9 + -27, // Index = 19, Token = 10 + -38, // Index = 19, Token = 11 + -50, // Index = 19, Token = 12 + -61, // Index = 19, Token = 13 + -72, // Index = 19, Token = 14 + -83, // Index = 19, Token = 15 + 6, // Index = 20, Token = 0 + 18, // Index = 20, Token = 1 + 31, // Index = 20, Token = 2 + 43, // Index = 20, Token = 3 + 56, // Index = 20, Token = 4 + 68, // Index = 20, Token = 5 + 81, // Index = 20, Token = 6 + 93, // Index = 20, Token = 7 + -6, // Index = 20, Token = 8 + -18, // Index = 20, Token = 9 + -31, // Index = 20, Token = 10 + -43, // Index = 20, Token = 11 + -56, // Index = 20, Token = 12 + -68, // Index = 20, Token = 13 + -81, // Index = 20, Token = 14 + -93, // Index = 20, Token = 15 + 6, // Index = 21, Token = 0 + 19, // Index = 21, Token = 1 + 33, // Index = 21, Token = 2 + 46, // Index = 21, Token = 3 + 61, // Index = 21, Token = 4 + 74, // Index = 21, Token = 5 + 88, // Index = 21, Token = 6 + 101, // Index = 21, Token = 7 + -6, // Index = 21, Token = 8 + -19, // Index = 21, Token = 9 + -33, // Index = 21, Token = 10 + -46, // Index = 21, Token = 11 + -61, // Index = 21, Token = 12 + -74, // Index = 21, Token = 13 + -88, // Index = 21, Token = 14 + -101, // Index = 21, Token = 15 + 7, // Index = 22, Token = 0 + 22, // Index = 22, Token = 1 + 37, // Index = 22, Token = 2 + 52, // Index = 22, Token = 3 + 67, // Index = 22, Token = 4 + 82, // Index = 22, Token = 5 + 97, // Index = 22, Token = 6 + 112, // Index = 22, Token = 7 + -7, // Index = 22, Token = 8 + -22, // Index = 22, Token = 9 + -37, // Index = 22, Token = 10 + -52, // Index = 22, Token = 11 + -67, // Index = 22, Token = 12 + -82, // Index = 22, Token = 13 + -97, // Index = 22, Token = 14 + -112, // Index = 22, Token = 15 + 8, // Index = 23, Token = 0 + 24, // Index = 23, Token = 1 + 41, // Index = 23, Token = 2 + 57, // Index = 23, Token = 3 + 74, // Index = 23, Token = 4 + 90, // Index = 23, Token = 5 + 107, // Index = 23, Token = 6 + 123, // Index = 23, Token = 7 + -8, // Index = 23, Token = 8 + -24, // Index = 23, Token = 9 + -41, // Index = 23, Token = 10 + -57, // Index = 23, Token = 11 + -74, // Index = 23, Token = 12 + -90, // Index = 23, Token = 13 + -107, // Index = 23, Token = 14 + -123, // Index = 23, Token = 15 + 9, // Index = 24, Token = 0 + 27, // Index = 24, Token = 1 + 45, // Index = 24, Token = 2 + 63, // Index = 24, Token = 3 + 82, // Index = 24, Token = 4 + 100, // Index = 24, Token = 5 + 118, // Index = 24, Token = 6 + 136, // Index = 24, Token = 7 + -9, // Index = 24, Token = 8 + -27, // Index = 24, Token = 9 + -45, // Index = 24, Token = 10 + -63, // Index = 24, Token = 11 + -82, // Index = 24, Token = 12 + -100, // Index = 24, Token = 13 + -118, // Index = 24, Token = 14 + -136, // Index = 24, Token = 15 + 10, // Index = 25, Token = 0 + 30, // Index = 25, Token = 1 + 50, // Index = 25, Token = 2 + 70, // Index = 25, Token = 3 + 90, // Index = 25, Token = 4 + 110, // Index = 25, Token = 5 + 130, // Index = 25, Token = 6 + 150, // Index = 25, Token = 7 + -10, // Index = 25, Token = 8 + -30, // Index = 25, Token = 9 + -50, // Index = 25, Token = 10 + -70, // Index = 25, Token = 11 + -90, // Index = 25, Token = 12 + -110, // Index = 25, Token = 13 + -130, // Index = 25, Token = 14 + -150, // Index = 25, Token = 15 + 11, // Index = 26, Token = 0 + 33, // Index = 26, Token = 1 + 55, // Index = 26, Token = 2 + 77, // Index = 26, Token = 3 + 99, // Index = 26, Token = 4 + 121, // Index = 26, Token = 5 + 143, // Index = 26, Token = 6 + 165, // Index = 26, Token = 7 + -11, // Index = 26, Token = 8 + -33, // Index = 26, Token = 9 + -55, // Index = 26, Token = 10 + -77, // Index = 26, Token = 11 + -99, // Index = 26, Token = 12 + -121, // Index = 26, Token = 13 + -143, // Index = 26, Token = 14 + -165, // Index = 26, Token = 15 + 12, // Index = 27, Token = 0 + 36, // Index = 27, Token = 1 + 60, // Index = 27, Token = 2 + 84, // Index = 27, Token = 3 + 109, // Index = 27, Token = 4 + 133, // Index = 27, Token = 5 + 157, // Index = 27, Token = 6 + 181, // Index = 27, Token = 7 + -12, // Index = 27, Token = 8 + -36, // Index = 27, Token = 9 + -60, // Index = 27, Token = 10 + -84, // Index = 27, Token = 11 + -109, // Index = 27, Token = 12 + -133, // Index = 27, Token = 13 + -157, // Index = 27, Token = 14 + -181, // Index = 27, Token = 15 + 13, // Index = 28, Token = 0 + 39, // Index = 28, Token = 1 + 66, // Index = 28, Token = 2 + 92, // Index = 28, Token = 3 + 120, // Index = 28, Token = 4 + 146, // Index = 28, Token = 5 + 173, // Index = 28, Token = 6 + 199, // Index = 28, Token = 7 + -13, // Index = 28, Token = 8 + -39, // Index = 28, Token = 9 + -66, // Index = 28, Token = 10 + -92, // Index = 28, Token = 11 + -120, // Index = 28, Token = 12 + -146, // Index = 28, Token = 13 + -173, // Index = 28, Token = 14 + -199, // Index = 28, Token = 15 + 14, // Index = 29, Token = 0 + 43, // Index = 29, Token = 1 + 73, // Index = 29, Token = 2 + 102, // Index = 29, Token = 3 + 132, // Index = 29, Token = 4 + 161, // Index = 29, Token = 5 + 191, // Index = 29, Token = 6 + 220, // Index = 29, Token = 7 + -14, // Index = 29, Token = 8 + -43, // Index = 29, Token = 9 + -73, // Index = 29, Token = 10 + -102, // Index = 29, Token = 11 + -132, // Index = 29, Token = 12 + -161, // Index = 29, Token = 13 + -191, // Index = 29, Token = 14 + -220, // Index = 29, Token = 15 + 16, // Index = 30, Token = 0 + 48, // Index = 30, Token = 1 + 81, // Index = 30, Token = 2 + 113, // Index = 30, Token = 3 + 146, // Index = 30, Token = 4 + 178, // Index = 30, Token = 5 + 211, // Index = 30, Token = 6 + 243, // Index = 30, Token = 7 + -16, // Index = 30, Token = 8 + -48, // Index = 30, Token = 9 + -81, // Index = 30, Token = 10 + -113, // Index = 30, Token = 11 + -146, // Index = 30, Token = 12 + -178, // Index = 30, Token = 13 + -211, // Index = 30, Token = 14 + -243, // Index = 30, Token = 15 + 17, // Index = 31, Token = 0 + 52, // Index = 31, Token = 1 + 88, // Index = 31, Token = 2 + 123, // Index = 31, Token = 3 + 160, // Index = 31, Token = 4 + 195, // Index = 31, Token = 5 + 231, // Index = 31, Token = 6 + 266, // Index = 31, Token = 7 + -17, // Index = 31, Token = 8 + -52, // Index = 31, Token = 9 + -88, // Index = 31, Token = 10 + -123, // Index = 31, Token = 11 + -160, // Index = 31, Token = 12 + -195, // Index = 31, Token = 13 + -231, // Index = 31, Token = 14 + -266, // Index = 31, Token = 15 + 19, // Index = 32, Token = 0 + 58, // Index = 32, Token = 1 + 97, // Index = 32, Token = 2 + 136, // Index = 32, Token = 3 + 176, // Index = 32, Token = 4 + 215, // Index = 32, Token = 5 + 254, // Index = 32, Token = 6 + 293, // Index = 32, Token = 7 + -19, // Index = 32, Token = 8 + -58, // Index = 32, Token = 9 + -97, // Index = 32, Token = 10 + -136, // Index = 32, Token = 11 + -176, // Index = 32, Token = 12 + -215, // Index = 32, Token = 13 + -254, // Index = 32, Token = 14 + -293, // Index = 32, Token = 15 + 21, // Index = 33, Token = 0 + 64, // Index = 33, Token = 1 + 107, // Index = 33, Token = 2 + 150, // Index = 33, Token = 3 + 194, // Index = 33, Token = 4 + 237, // Index = 33, Token = 5 + 280, // Index = 33, Token = 6 + 323, // Index = 33, Token = 7 + -21, // Index = 33, Token = 8 + -64, // Index = 33, Token = 9 + -107, // Index = 33, Token = 10 + -150, // Index = 33, Token = 11 + -194, // Index = 33, Token = 12 + -237, // Index = 33, Token = 13 + -280, // Index = 33, Token = 14 + -323, // Index = 33, Token = 15 + 23, // Index = 34, Token = 0 + 70, // Index = 34, Token = 1 + 118, // Index = 34, Token = 2 + 165, // Index = 34, Token = 3 + 213, // Index = 34, Token = 4 + 260, // Index = 34, Token = 5 + 308, // Index = 34, Token = 6 + 355, // Index = 34, Token = 7 + -23, // Index = 34, Token = 8 + -70, // Index = 34, Token = 9 + -118, // Index = 34, Token = 10 + -165, // Index = 34, Token = 11 + -213, // Index = 34, Token = 12 + -260, // Index = 34, Token = 13 + -308, // Index = 34, Token = 14 + -355, // Index = 34, Token = 15 + 26, // Index = 35, Token = 0 + 78, // Index = 35, Token = 1 + 130, // Index = 35, Token = 2 + 182, // Index = 35, Token = 3 + 235, // Index = 35, Token = 4 + 287, // Index = 35, Token = 5 + 339, // Index = 35, Token = 6 + 391, // Index = 35, Token = 7 + -26, // Index = 35, Token = 8 + -78, // Index = 35, Token = 9 + -130, // Index = 35, Token = 10 + -182, // Index = 35, Token = 11 + -235, // Index = 35, Token = 12 + -287, // Index = 35, Token = 13 + -339, // Index = 35, Token = 14 + -391, // Index = 35, Token = 15 + 28, // Index = 36, Token = 0 + 85, // Index = 36, Token = 1 + 143, // Index = 36, Token = 2 + 200, // Index = 36, Token = 3 + 258, // Index = 36, Token = 4 + 315, // Index = 36, Token = 5 + 373, // Index = 36, Token = 6 + 430, // Index = 36, Token = 7 + -28, // Index = 36, Token = 8 + -85, // Index = 36, Token = 9 + -143, // Index = 36, Token = 10 + -200, // Index = 36, Token = 11 + -258, // Index = 36, Token = 12 + -315, // Index = 36, Token = 13 + -373, // Index = 36, Token = 14 + -430, // Index = 36, Token = 15 + 31, // Index = 37, Token = 0 + 94, // Index = 37, Token = 1 + 157, // Index = 37, Token = 2 + 220, // Index = 37, Token = 3 + 284, // Index = 37, Token = 4 + 347, // Index = 37, Token = 5 + 410, // Index = 37, Token = 6 + 473, // Index = 37, Token = 7 + -31, // Index = 37, Token = 8 + -94, // Index = 37, Token = 9 + -157, // Index = 37, Token = 10 + -220, // Index = 37, Token = 11 + -284, // Index = 37, Token = 12 + -347, // Index = 37, Token = 13 + -410, // Index = 37, Token = 14 + -473, // Index = 37, Token = 15 + 34, // Index = 38, Token = 0 + 103, // Index = 38, Token = 1 + 173, // Index = 38, Token = 2 + 242, // Index = 38, Token = 3 + 313, // Index = 38, Token = 4 + 382, // Index = 38, Token = 5 + 452, // Index = 38, Token = 6 + 521, // Index = 38, Token = 7 + -34, // Index = 38, Token = 8 + -103, // Index = 38, Token = 9 + -173, // Index = 38, Token = 10 + -242, // Index = 38, Token = 11 + -313, // Index = 38, Token = 12 + -382, // Index = 38, Token = 13 + -452, // Index = 38, Token = 14 + -521, // Index = 38, Token = 15 + 38, // Index = 39, Token = 0 + 114, // Index = 39, Token = 1 + 191, // Index = 39, Token = 2 + 267, // Index = 39, Token = 3 + 345, // Index = 39, Token = 4 + 421, // Index = 39, Token = 5 + 498, // Index = 39, Token = 6 + 574, // Index = 39, Token = 7 + -38, // Index = 39, Token = 8 + -114, // Index = 39, Token = 9 + -191, // Index = 39, Token = 10 + -267, // Index = 39, Token = 11 + -345, // Index = 39, Token = 12 + -421, // Index = 39, Token = 13 + -498, // Index = 39, Token = 14 + -574, // Index = 39, Token = 15 + 42, // Index = 40, Token = 0 + 126, // Index = 40, Token = 1 + 210, // Index = 40, Token = 2 + 294, // Index = 40, Token = 3 + 379, // Index = 40, Token = 4 + 463, // Index = 40, Token = 5 + 547, // Index = 40, Token = 6 + 631, // Index = 40, Token = 7 + -42, // Index = 40, Token = 8 + -126, // Index = 40, Token = 9 + -210, // Index = 40, Token = 10 + -294, // Index = 40, Token = 11 + -379, // Index = 40, Token = 12 + -463, // Index = 40, Token = 13 + -547, // Index = 40, Token = 14 + -631, // Index = 40, Token = 15 + 46, // Index = 41, Token = 0 + 138, // Index = 41, Token = 1 + 231, // Index = 41, Token = 2 + 323, // Index = 41, Token = 3 + 417, // Index = 41, Token = 4 + 509, // Index = 41, Token = 5 + 602, // Index = 41, Token = 6 + 694, // Index = 41, Token = 7 + -46, // Index = 41, Token = 8 + -138, // Index = 41, Token = 9 + -231, // Index = 41, Token = 10 + -323, // Index = 41, Token = 11 + -417, // Index = 41, Token = 12 + -509, // Index = 41, Token = 13 + -602, // Index = 41, Token = 14 + -694, // Index = 41, Token = 15 + 51, // Index = 42, Token = 0 + 153, // Index = 42, Token = 1 + 255, // Index = 42, Token = 2 + 357, // Index = 42, Token = 3 + 459, // Index = 42, Token = 4 + 561, // Index = 42, Token = 5 + 663, // Index = 42, Token = 6 + 765, // Index = 42, Token = 7 + -51, // Index = 42, Token = 8 + -153, // Index = 42, Token = 9 + -255, // Index = 42, Token = 10 + -357, // Index = 42, Token = 11 + -459, // Index = 42, Token = 12 + -561, // Index = 42, Token = 13 + -663, // Index = 42, Token = 14 + -765, // Index = 42, Token = 15 + 56, // Index = 43, Token = 0 + 168, // Index = 43, Token = 1 + 280, // Index = 43, Token = 2 + 392, // Index = 43, Token = 3 + 505, // Index = 43, Token = 4 + 617, // Index = 43, Token = 5 + 729, // Index = 43, Token = 6 + 841, // Index = 43, Token = 7 + -56, // Index = 43, Token = 8 + -168, // Index = 43, Token = 9 + -280, // Index = 43, Token = 10 + -392, // Index = 43, Token = 11 + -505, // Index = 43, Token = 12 + -617, // Index = 43, Token = 13 + -729, // Index = 43, Token = 14 + -841, // Index = 43, Token = 15 + 61, // Index = 44, Token = 0 + 184, // Index = 44, Token = 1 + 308, // Index = 44, Token = 2 + 431, // Index = 44, Token = 3 + 555, // Index = 44, Token = 4 + 678, // Index = 44, Token = 5 + 802, // Index = 44, Token = 6 + 925, // Index = 44, Token = 7 + -61, // Index = 44, Token = 8 + -184, // Index = 44, Token = 9 + -308, // Index = 44, Token = 10 + -431, // Index = 44, Token = 11 + -555, // Index = 44, Token = 12 + -678, // Index = 44, Token = 13 + -802, // Index = 44, Token = 14 + -925, // Index = 44, Token = 15 + 68, // Index = 45, Token = 0 + 204, // Index = 45, Token = 1 + 340, // Index = 45, Token = 2 + 476, // Index = 45, Token = 3 + 612, // Index = 45, Token = 4 + 748, // Index = 45, Token = 5 + 884, // Index = 45, Token = 6 + 1020, // Index = 45, Token = 7 + -68, // Index = 45, Token = 8 + -204, // Index = 45, Token = 9 + -340, // Index = 45, Token = 10 + -476, // Index = 45, Token = 11 + -612, // Index = 45, Token = 12 + -748, // Index = 45, Token = 13 + -884, // Index = 45, Token = 14 + -1020, // Index = 45, Token = 15 + 74, // Index = 46, Token = 0 + 223, // Index = 46, Token = 1 + 373, // Index = 46, Token = 2 + 522, // Index = 46, Token = 3 + 672, // Index = 46, Token = 4 + 821, // Index = 46, Token = 5 + 971, // Index = 46, Token = 6 + 1120, // Index = 46, Token = 7 + -74, // Index = 46, Token = 8 + -223, // Index = 46, Token = 9 + -373, // Index = 46, Token = 10 + -522, // Index = 46, Token = 11 + -672, // Index = 46, Token = 12 + -821, // Index = 46, Token = 13 + -971, // Index = 46, Token = 14 + -1120, // Index = 46, Token = 15 + 82, // Index = 47, Token = 0 + 246, // Index = 47, Token = 1 + 411, // Index = 47, Token = 2 + 575, // Index = 47, Token = 3 + 740, // Index = 47, Token = 4 + 904, // Index = 47, Token = 5 + 1069, // Index = 47, Token = 6 + 1233, // Index = 47, Token = 7 + -82, // Index = 47, Token = 8 + -246, // Index = 47, Token = 9 + -411, // Index = 47, Token = 10 + -575, // Index = 47, Token = 11 + -740, // Index = 47, Token = 12 + -904, // Index = 47, Token = 13 + -1069, // Index = 47, Token = 14 + -1233, // Index = 47, Token = 15 + 90, // Index = 48, Token = 0 + 271, // Index = 48, Token = 1 + 452, // Index = 48, Token = 2 + 633, // Index = 48, Token = 3 + 814, // Index = 48, Token = 4 + 995, // Index = 48, Token = 5 + 1176, // Index = 48, Token = 6 + 1357, // Index = 48, Token = 7 + -90, // Index = 48, Token = 8 + -271, // Index = 48, Token = 9 + -452, // Index = 48, Token = 10 + -633, // Index = 48, Token = 11 + -814, // Index = 48, Token = 12 + -995, // Index = 48, Token = 13 + -1176, // Index = 48, Token = 14 + -1357, // Index = 48, Token = 15 + 99, // Index = 49, Token = 0 + 298, // Index = 49, Token = 1 + 497, // Index = 49, Token = 2 + 696, // Index = 49, Token = 3 + 895, // Index = 49, Token = 4 + 1094, // Index = 49, Token = 5 + 1293, // Index = 49, Token = 6 + 1492, // Index = 49, Token = 7 + -99, // Index = 49, Token = 8 + -298, // Index = 49, Token = 9 + -497, // Index = 49, Token = 10 + -696, // Index = 49, Token = 11 + -895, // Index = 49, Token = 12 + -1094, // Index = 49, Token = 13 + -1293, // Index = 49, Token = 14 + -1492, // Index = 49, Token = 15 + 109, // Index = 50, Token = 0 + 328, // Index = 50, Token = 1 + 547, // Index = 50, Token = 2 + 766, // Index = 50, Token = 3 + 985, // Index = 50, Token = 4 + 1204, // Index = 50, Token = 5 + 1423, // Index = 50, Token = 6 + 1642, // Index = 50, Token = 7 + -109, // Index = 50, Token = 8 + -328, // Index = 50, Token = 9 + -547, // Index = 50, Token = 10 + -766, // Index = 50, Token = 11 + -985, // Index = 50, Token = 12 + -1204, // Index = 50, Token = 13 + -1423, // Index = 50, Token = 14 + -1642, // Index = 50, Token = 15 + 120, // Index = 51, Token = 0 + 360, // Index = 51, Token = 1 + 601, // Index = 51, Token = 2 + 841, // Index = 51, Token = 3 + 1083, // Index = 51, Token = 4 + 1323, // Index = 51, Token = 5 + 1564, // Index = 51, Token = 6 + 1804, // Index = 51, Token = 7 + -120, // Index = 51, Token = 8 + -360, // Index = 51, Token = 9 + -601, // Index = 51, Token = 10 + -841, // Index = 51, Token = 11 + -1083, // Index = 51, Token = 12 + -1323, // Index = 51, Token = 13 + -1564, // Index = 51, Token = 14 + -1804, // Index = 51, Token = 15 + 132, // Index = 52, Token = 0 + 397, // Index = 52, Token = 1 + 662, // Index = 52, Token = 2 + 927, // Index = 52, Token = 3 + 1192, // Index = 52, Token = 4 + 1457, // Index = 52, Token = 5 + 1722, // Index = 52, Token = 6 + 1987, // Index = 52, Token = 7 + -132, // Index = 52, Token = 8 + -397, // Index = 52, Token = 9 + -662, // Index = 52, Token = 10 + -927, // Index = 52, Token = 11 + -1192, // Index = 52, Token = 12 + -1457, // Index = 52, Token = 13 + -1722, // Index = 52, Token = 14 + -1987, // Index = 52, Token = 15 + 145, // Index = 53, Token = 0 + 436, // Index = 53, Token = 1 + 728, // Index = 53, Token = 2 + 1019, // Index = 53, Token = 3 + 1311, // Index = 53, Token = 4 + 1602, // Index = 53, Token = 5 + 1894, // Index = 53, Token = 6 + 2185, // Index = 53, Token = 7 + -145, // Index = 53, Token = 8 + -436, // Index = 53, Token = 9 + -728, // Index = 53, Token = 10 + -1019, // Index = 53, Token = 11 + -1311, // Index = 53, Token = 12 + -1602, // Index = 53, Token = 13 + -1894, // Index = 53, Token = 14 + -2185, // Index = 53, Token = 15 + 160, // Index = 54, Token = 0 + 480, // Index = 54, Token = 1 + 801, // Index = 54, Token = 2 + 1121, // Index = 54, Token = 3 + 1442, // Index = 54, Token = 4 + 1762, // Index = 54, Token = 5 + 2083, // Index = 54, Token = 6 + 2403, // Index = 54, Token = 7 + -160, // Index = 54, Token = 8 + -480, // Index = 54, Token = 9 + -801, // Index = 54, Token = 10 + -1121, // Index = 54, Token = 11 + -1442, // Index = 54, Token = 12 + -1762, // Index = 54, Token = 13 + -2083, // Index = 54, Token = 14 + -2403, // Index = 54, Token = 15 + 176, // Index = 55, Token = 0 + 528, // Index = 55, Token = 1 + 881, // Index = 55, Token = 2 + 1233, // Index = 55, Token = 3 + 1587, // Index = 55, Token = 4 + 1939, // Index = 55, Token = 5 + 2292, // Index = 55, Token = 6 + 2644, // Index = 55, Token = 7 + -176, // Index = 55, Token = 8 + -528, // Index = 55, Token = 9 + -881, // Index = 55, Token = 10 + -1233, // Index = 55, Token = 11 + -1587, // Index = 55, Token = 12 + -1939, // Index = 55, Token = 13 + -2292, // Index = 55, Token = 14 + -2644, // Index = 55, Token = 15 + 194, // Index = 56, Token = 0 + 582, // Index = 56, Token = 1 + 970, // Index = 56, Token = 2 + 1358, // Index = 56, Token = 3 + 1746, // Index = 56, Token = 4 + 2134, // Index = 56, Token = 5 + 2522, // Index = 56, Token = 6 + 2910, // Index = 56, Token = 7 + -194, // Index = 56, Token = 8 + -582, // Index = 56, Token = 9 + -970, // Index = 56, Token = 10 + -1358, // Index = 56, Token = 11 + -1746, // Index = 56, Token = 12 + -2134, // Index = 56, Token = 13 + -2522, // Index = 56, Token = 14 + -2910, // Index = 56, Token = 15 + 213, // Index = 57, Token = 0 + 639, // Index = 57, Token = 1 + 1066, // Index = 57, Token = 2 + 1492, // Index = 57, Token = 3 + 1920, // Index = 57, Token = 4 + 2346, // Index = 57, Token = 5 + 2773, // Index = 57, Token = 6 + 3199, // Index = 57, Token = 7 + -213, // Index = 57, Token = 8 + -639, // Index = 57, Token = 9 + -1066, // Index = 57, Token = 10 + -1492, // Index = 57, Token = 11 + -1920, // Index = 57, Token = 12 + -2346, // Index = 57, Token = 13 + -2773, // Index = 57, Token = 14 + -3199, // Index = 57, Token = 15 + 234, // Index = 58, Token = 0 + 703, // Index = 58, Token = 1 + 1173, // Index = 58, Token = 2 + 1642, // Index = 58, Token = 3 + 2112, // Index = 58, Token = 4 + 2581, // Index = 58, Token = 5 + 3051, // Index = 58, Token = 6 + 3520, // Index = 58, Token = 7 + -234, // Index = 58, Token = 8 + -703, // Index = 58, Token = 9 + -1173, // Index = 58, Token = 10 + -1642, // Index = 58, Token = 11 + -2112, // Index = 58, Token = 12 + -2581, // Index = 58, Token = 13 + -3051, // Index = 58, Token = 14 + -3520, // Index = 58, Token = 15 + 258, // Index = 59, Token = 0 + 774, // Index = 59, Token = 1 + 1291, // Index = 59, Token = 2 + 1807, // Index = 59, Token = 3 + 2324, // Index = 59, Token = 4 + 2840, // Index = 59, Token = 5 + 3357, // Index = 59, Token = 6 + 3873, // Index = 59, Token = 7 + -258, // Index = 59, Token = 8 + -774, // Index = 59, Token = 9 + -1291, // Index = 59, Token = 10 + -1807, // Index = 59, Token = 11 + -2324, // Index = 59, Token = 12 + -2840, // Index = 59, Token = 13 + -3357, // Index = 59, Token = 14 + -3873, // Index = 59, Token = 15 + 284, // Index = 60, Token = 0 + 852, // Index = 60, Token = 1 + 1420, // Index = 60, Token = 2 + 1988, // Index = 60, Token = 3 + 2556, // Index = 60, Token = 4 + 3124, // Index = 60, Token = 5 + 3692, // Index = 60, Token = 6 + 4260, // Index = 60, Token = 7 + -284, // Index = 60, Token = 8 + -852, // Index = 60, Token = 9 + -1420, // Index = 60, Token = 10 + -1988, // Index = 60, Token = 11 + -2556, // Index = 60, Token = 12 + -3124, // Index = 60, Token = 13 + -3692, // Index = 60, Token = 14 + -4260, // Index = 60, Token = 15 + 312, // Index = 61, Token = 0 + 936, // Index = 61, Token = 1 + 1561, // Index = 61, Token = 2 + 2185, // Index = 61, Token = 3 + 2811, // Index = 61, Token = 4 + 3435, // Index = 61, Token = 5 + 4060, // Index = 61, Token = 6 + 4684, // Index = 61, Token = 7 + -312, // Index = 61, Token = 8 + -936, // Index = 61, Token = 9 + -1561, // Index = 61, Token = 10 + -2185, // Index = 61, Token = 11 + -2811, // Index = 61, Token = 12 + -3435, // Index = 61, Token = 13 + -4060, // Index = 61, Token = 14 + -4684, // Index = 61, Token = 15 + 343, // Index = 62, Token = 0 + 1030, // Index = 62, Token = 1 + 1717, // Index = 62, Token = 2 + 2404, // Index = 62, Token = 3 + 3092, // Index = 62, Token = 4 + 3779, // Index = 62, Token = 5 + 4466, // Index = 62, Token = 6 + 5153, // Index = 62, Token = 7 + -343, // Index = 62, Token = 8 + -1030, // Index = 62, Token = 9 + -1717, // Index = 62, Token = 10 + -2404, // Index = 62, Token = 11 + -3092, // Index = 62, Token = 12 + -3779, // Index = 62, Token = 13 + -4466, // Index = 62, Token = 14 + -5153, // Index = 62, Token = 15 + 378, // Index = 63, Token = 0 + 1134, // Index = 63, Token = 1 + 1890, // Index = 63, Token = 2 + 2646, // Index = 63, Token = 3 + 3402, // Index = 63, Token = 4 + 4158, // Index = 63, Token = 5 + 4914, // Index = 63, Token = 6 + 5670, // Index = 63, Token = 7 + -378, // Index = 63, Token = 8 + -1134, // Index = 63, Token = 9 + -1890, // Index = 63, Token = 10 + -2646, // Index = 63, Token = 11 + -3402, // Index = 63, Token = 12 + -4158, // Index = 63, Token = 13 + -4914, // Index = 63, Token = 14 + -5670, // Index = 63, Token = 15 + 415, // Index = 64, Token = 0 + 1246, // Index = 64, Token = 1 + 2078, // Index = 64, Token = 2 + 2909, // Index = 64, Token = 3 + 3742, // Index = 64, Token = 4 + 4573, // Index = 64, Token = 5 + 5405, // Index = 64, Token = 6 + 6236, // Index = 64, Token = 7 + -415, // Index = 64, Token = 8 + -1246, // Index = 64, Token = 9 + -2078, // Index = 64, Token = 10 + -2909, // Index = 64, Token = 11 + -3742, // Index = 64, Token = 12 + -4573, // Index = 64, Token = 13 + -5405, // Index = 64, Token = 14 + -6236, // Index = 64, Token = 15 + 457, // Index = 65, Token = 0 + 1372, // Index = 65, Token = 1 + 2287, // Index = 65, Token = 2 + 3202, // Index = 65, Token = 3 + 4117, // Index = 65, Token = 4 + 5032, // Index = 65, Token = 5 + 5947, // Index = 65, Token = 6 + 6862, // Index = 65, Token = 7 + -457, // Index = 65, Token = 8 + -1372, // Index = 65, Token = 9 + -2287, // Index = 65, Token = 10 + -3202, // Index = 65, Token = 11 + -4117, // Index = 65, Token = 12 + -5032, // Index = 65, Token = 13 + -5947, // Index = 65, Token = 14 + -6862, // Index = 65, Token = 15 + 503, // Index = 66, Token = 0 + 1509, // Index = 66, Token = 1 + 2516, // Index = 66, Token = 2 + 3522, // Index = 66, Token = 3 + 4529, // Index = 66, Token = 4 + 5535, // Index = 66, Token = 5 + 6542, // Index = 66, Token = 6 + 7548, // Index = 66, Token = 7 + -503, // Index = 66, Token = 8 + -1509, // Index = 66, Token = 9 + -2516, // Index = 66, Token = 10 + -3522, // Index = 66, Token = 11 + -4529, // Index = 66, Token = 12 + -5535, // Index = 66, Token = 13 + -6542, // Index = 66, Token = 14 + -7548, // Index = 66, Token = 15 + 553, // Index = 67, Token = 0 + 1660, // Index = 67, Token = 1 + 2767, // Index = 67, Token = 2 + 3874, // Index = 67, Token = 3 + 4981, // Index = 67, Token = 4 + 6088, // Index = 67, Token = 5 + 7195, // Index = 67, Token = 6 + 8302, // Index = 67, Token = 7 + -553, // Index = 67, Token = 8 + -1660, // Index = 67, Token = 9 + -2767, // Index = 67, Token = 10 + -3874, // Index = 67, Token = 11 + -4981, // Index = 67, Token = 12 + -6088, // Index = 67, Token = 13 + -7195, // Index = 67, Token = 14 + -8302, // Index = 67, Token = 15 + 608, // Index = 68, Token = 0 + 1825, // Index = 68, Token = 1 + 3043, // Index = 68, Token = 2 + 4260, // Index = 68, Token = 3 + 5479, // Index = 68, Token = 4 + 6696, // Index = 68, Token = 5 + 7914, // Index = 68, Token = 6 + 9131, // Index = 68, Token = 7 + -608, // Index = 68, Token = 8 + -1825, // Index = 68, Token = 9 + -3043, // Index = 68, Token = 10 + -4260, // Index = 68, Token = 11 + -5479, // Index = 68, Token = 12 + -6696, // Index = 68, Token = 13 + -7914, // Index = 68, Token = 14 + -9131, // Index = 68, Token = 15 + 669, // Index = 69, Token = 0 + 2008, // Index = 69, Token = 1 + 3348, // Index = 69, Token = 2 + 4687, // Index = 69, Token = 3 + 6027, // Index = 69, Token = 4 + 7366, // Index = 69, Token = 5 + 8706, // Index = 69, Token = 6 + 10045, // Index = 69, Token = 7 + -669, // Index = 69, Token = 8 + -2008, // Index = 69, Token = 9 + -3348, // Index = 69, Token = 10 + -4687, // Index = 69, Token = 11 + -6027, // Index = 69, Token = 12 + -7366, // Index = 69, Token = 13 + -8706, // Index = 69, Token = 14 + -10045, // Index = 69, Token = 15 + 736, // Index = 70, Token = 0 + 2209, // Index = 70, Token = 1 + 3683, // Index = 70, Token = 2 + 5156, // Index = 70, Token = 3 + 6630, // Index = 70, Token = 4 + 8103, // Index = 70, Token = 5 + 9577, // Index = 70, Token = 6 + 11050, // Index = 70, Token = 7 + -736, // Index = 70, Token = 8 + -2209, // Index = 70, Token = 9 + -3683, // Index = 70, Token = 10 + -5156, // Index = 70, Token = 11 + -6630, // Index = 70, Token = 12 + -8103, // Index = 70, Token = 13 + -9577, // Index = 70, Token = 14 + -11050, // Index = 70, Token = 15 + 810, // Index = 71, Token = 0 + 2431, // Index = 71, Token = 1 + 4052, // Index = 71, Token = 2 + 5673, // Index = 71, Token = 3 + 7294, // Index = 71, Token = 4 + 8915, // Index = 71, Token = 5 + 10536, // Index = 71, Token = 6 + 12157, // Index = 71, Token = 7 + -810, // Index = 71, Token = 8 + -2431, // Index = 71, Token = 9 + -4052, // Index = 71, Token = 10 + -5673, // Index = 71, Token = 11 + -7294, // Index = 71, Token = 12 + -8915, // Index = 71, Token = 13 + -10536, // Index = 71, Token = 14 + -12157, // Index = 71, Token = 15 + 891, // Index = 72, Token = 0 + 2674, // Index = 72, Token = 1 + 4457, // Index = 72, Token = 2 + 6240, // Index = 72, Token = 3 + 8023, // Index = 72, Token = 4 + 9806, // Index = 72, Token = 5 + 11589, // Index = 72, Token = 6 + 13372, // Index = 72, Token = 7 + -891, // Index = 72, Token = 8 + -2674, // Index = 72, Token = 9 + -4457, // Index = 72, Token = 10 + -6240, // Index = 72, Token = 11 + -8023, // Index = 72, Token = 12 + -9806, // Index = 72, Token = 13 + -11589, // Index = 72, Token = 14 + -13372, // Index = 72, Token = 15 + 980, // Index = 73, Token = 0 + 2941, // Index = 73, Token = 1 + 4902, // Index = 73, Token = 2 + 6863, // Index = 73, Token = 3 + 8825, // Index = 73, Token = 4 + 10786, // Index = 73, Token = 5 + 12747, // Index = 73, Token = 6 + 14708, // Index = 73, Token = 7 + -980, // Index = 73, Token = 8 + -2941, // Index = 73, Token = 9 + -4902, // Index = 73, Token = 10 + -6863, // Index = 73, Token = 11 + -8825, // Index = 73, Token = 12 + -10786, // Index = 73, Token = 13 + -12747, // Index = 73, Token = 14 + -14708, // Index = 73, Token = 15 + 1078, // Index = 74, Token = 0 + 3235, // Index = 74, Token = 1 + 5393, // Index = 74, Token = 2 + 7550, // Index = 74, Token = 3 + 9708, // Index = 74, Token = 4 + 11865, // Index = 74, Token = 5 + 14023, // Index = 74, Token = 6 + 16180, // Index = 74, Token = 7 + -1078, // Index = 74, Token = 8 + -3235, // Index = 74, Token = 9 + -5393, // Index = 74, Token = 10 + -7550, // Index = 74, Token = 11 + -9708, // Index = 74, Token = 12 + -11865, // Index = 74, Token = 13 + -14023, // Index = 74, Token = 14 + -16180, // Index = 74, Token = 15 + 1186, // Index = 75, Token = 0 + 3559, // Index = 75, Token = 1 + 5932, // Index = 75, Token = 2 + 8305, // Index = 75, Token = 3 + 10679, // Index = 75, Token = 4 + 13052, // Index = 75, Token = 5 + 15425, // Index = 75, Token = 6 + 17798, // Index = 75, Token = 7 + -1186, // Index = 75, Token = 8 + -3559, // Index = 75, Token = 9 + -5932, // Index = 75, Token = 10 + -8305, // Index = 75, Token = 11 + -10679, // Index = 75, Token = 12 + -13052, // Index = 75, Token = 13 + -15425, // Index = 75, Token = 14 + -17798, // Index = 75, Token = 15 + 1305, // Index = 76, Token = 0 + 3915, // Index = 76, Token = 1 + 6526, // Index = 76, Token = 2 + 9136, // Index = 76, Token = 3 + 11747, // Index = 76, Token = 4 + 14357, // Index = 76, Token = 5 + 16968, // Index = 76, Token = 6 + 19578, // Index = 76, Token = 7 + -1305, // Index = 76, Token = 8 + -3915, // Index = 76, Token = 9 + -6526, // Index = 76, Token = 10 + -9136, // Index = 76, Token = 11 + -11747, // Index = 76, Token = 12 + -14357, // Index = 76, Token = 13 + -16968, // Index = 76, Token = 14 + -19578, // Index = 76, Token = 15 + 1435, // Index = 77, Token = 0 + 4306, // Index = 77, Token = 1 + 7178, // Index = 77, Token = 2 + 10049, // Index = 77, Token = 3 + 12922, // Index = 77, Token = 4 + 15793, // Index = 77, Token = 5 + 18665, // Index = 77, Token = 6 + 21536, // Index = 77, Token = 7 + -1435, // Index = 77, Token = 8 + -4306, // Index = 77, Token = 9 + -7178, // Index = 77, Token = 10 + -10049, // Index = 77, Token = 11 + -12922, // Index = 77, Token = 12 + -15793, // Index = 77, Token = 13 + -18665, // Index = 77, Token = 14 + -21536, // Index = 77, Token = 15 + 1579, // Index = 78, Token = 0 + 4737, // Index = 78, Token = 1 + 7896, // Index = 78, Token = 2 + 11054, // Index = 78, Token = 3 + 14214, // Index = 78, Token = 4 + 17372, // Index = 78, Token = 5 + 20531, // Index = 78, Token = 6 + 23689, // Index = 78, Token = 7 + -1579, // Index = 78, Token = 8 + -4737, // Index = 78, Token = 9 + -7896, // Index = 78, Token = 10 + -11054, // Index = 78, Token = 11 + -14214, // Index = 78, Token = 12 + -17372, // Index = 78, Token = 13 + -20531, // Index = 78, Token = 14 + -23689, // Index = 78, Token = 15 + 1737, // Index = 79, Token = 0 + 5211, // Index = 79, Token = 1 + 8686, // Index = 79, Token = 2 + 12160, // Index = 79, Token = 3 + 15636, // Index = 79, Token = 4 + 19110, // Index = 79, Token = 5 + 22585, // Index = 79, Token = 6 + 26059, // Index = 79, Token = 7 + -1737, // Index = 79, Token = 8 + -5211, // Index = 79, Token = 9 + -8686, // Index = 79, Token = 10 + -12160, // Index = 79, Token = 11 + -15636, // Index = 79, Token = 12 + -19110, // Index = 79, Token = 13 + -22585, // Index = 79, Token = 14 + -26059, // Index = 79, Token = 15 + 1911, // Index = 80, Token = 0 + 5733, // Index = 80, Token = 1 + 9555, // Index = 80, Token = 2 + 13377, // Index = 80, Token = 3 + 17200, // Index = 80, Token = 4 + 21022, // Index = 80, Token = 5 + 24844, // Index = 80, Token = 6 + 28666, // Index = 80, Token = 7 + -1911, // Index = 80, Token = 8 + -5733, // Index = 80, Token = 9 + -9555, // Index = 80, Token = 10 + -13377, // Index = 80, Token = 11 + -17200, // Index = 80, Token = 12 + -21022, // Index = 80, Token = 13 + -24844, // Index = 80, Token = 14 + -28666, // Index = 80, Token = 15 + 2102, // Index = 81, Token = 0 + 6306, // Index = 81, Token = 1 + 10511, // Index = 81, Token = 2 + 14715, // Index = 81, Token = 3 + 18920, // Index = 81, Token = 4 + 23124, // Index = 81, Token = 5 + 27329, // Index = 81, Token = 6 + 31533, // Index = 81, Token = 7 + -2102, // Index = 81, Token = 8 + -6306, // Index = 81, Token = 9 + -10511, // Index = 81, Token = 10 + -14715, // Index = 81, Token = 11 + -18920, // Index = 81, Token = 12 + -23124, // Index = 81, Token = 13 + -27329, // Index = 81, Token = 14 + -31533, // Index = 81, Token = 15 + 2312, // Index = 82, Token = 0 + 6937, // Index = 82, Token = 1 + 11562, // Index = 82, Token = 2 + 16187, // Index = 82, Token = 3 + 20812, // Index = 82, Token = 4 + 25437, // Index = 82, Token = 5 + 30062, // Index = 82, Token = 6 + 34687, // Index = 82, Token = 7 + -2312, // Index = 82, Token = 8 + -6937, // Index = 82, Token = 9 + -11562, // Index = 82, Token = 10 + -16187, // Index = 82, Token = 11 + -20812, // Index = 82, Token = 12 + -25437, // Index = 82, Token = 13 + -30062, // Index = 82, Token = 14 + -34687, // Index = 82, Token = 15 + 2543, // Index = 83, Token = 0 + 7630, // Index = 83, Token = 1 + 12718, // Index = 83, Token = 2 + 17805, // Index = 83, Token = 3 + 22893, // Index = 83, Token = 4 + 27980, // Index = 83, Token = 5 + 33068, // Index = 83, Token = 6 + 38155, // Index = 83, Token = 7 + -2543, // Index = 83, Token = 8 + -7630, // Index = 83, Token = 9 + -12718, // Index = 83, Token = 10 + -17805, // Index = 83, Token = 11 + -22893, // Index = 83, Token = 12 + -27980, // Index = 83, Token = 13 + -33068, // Index = 83, Token = 14 + -38155, // Index = 83, Token = 15 + 2798, // Index = 84, Token = 0 + 8394, // Index = 84, Token = 1 + 13990, // Index = 84, Token = 2 + 19586, // Index = 84, Token = 3 + 25183, // Index = 84, Token = 4 + 30779, // Index = 84, Token = 5 + 36375, // Index = 84, Token = 6 + 41971, // Index = 84, Token = 7 + -2798, // Index = 84, Token = 8 + -8394, // Index = 84, Token = 9 + -13990, // Index = 84, Token = 10 + -19586, // Index = 84, Token = 11 + -25183, // Index = 84, Token = 12 + -30779, // Index = 84, Token = 13 + -36375, // Index = 84, Token = 14 + -41971, // Index = 84, Token = 15 + 3077, // Index = 85, Token = 0 + 9232, // Index = 85, Token = 1 + 15388, // Index = 85, Token = 2 + 21543, // Index = 85, Token = 3 + 27700, // Index = 85, Token = 4 + 33855, // Index = 85, Token = 5 + 40011, // Index = 85, Token = 6 + 46166, // Index = 85, Token = 7 + -3077, // Index = 85, Token = 8 + -9232, // Index = 85, Token = 9 + -15388, // Index = 85, Token = 10 + -21543, // Index = 85, Token = 11 + -27700, // Index = 85, Token = 12 + -33855, // Index = 85, Token = 13 + -40011, // Index = 85, Token = 14 + -46166, // Index = 85, Token = 15 + 3385, // Index = 86, Token = 0 + 10156, // Index = 86, Token = 1 + 16928, // Index = 86, Token = 2 + 23699, // Index = 86, Token = 3 + 30471, // Index = 86, Token = 4 + 37242, // Index = 86, Token = 5 + 44014, // Index = 86, Token = 6 + 50785, // Index = 86, Token = 7 + -3385, // Index = 86, Token = 8 + -10156, // Index = 86, Token = 9 + -16928, // Index = 86, Token = 10 + -23699, // Index = 86, Token = 11 + -30471, // Index = 86, Token = 12 + -37242, // Index = 86, Token = 13 + -44014, // Index = 86, Token = 14 + -50785, // Index = 86, Token = 15 + 3724, // Index = 87, Token = 0 + 11172, // Index = 87, Token = 1 + 18621, // Index = 87, Token = 2 + 26069, // Index = 87, Token = 3 + 33518, // Index = 87, Token = 4 + 40966, // Index = 87, Token = 5 + 48415, // Index = 87, Token = 6 + 55863, // Index = 87, Token = 7 + -3724, // Index = 87, Token = 8 + -11172, // Index = 87, Token = 9 + -18621, // Index = 87, Token = 10 + -26069, // Index = 87, Token = 11 + -33518, // Index = 87, Token = 12 + -40966, // Index = 87, Token = 13 + -48415, // Index = 87, Token = 14 + -55863, // Index = 87, Token = 15 + 4095, // Index = 88, Token = 0 + 12286, // Index = 88, Token = 1 + 20478, // Index = 88, Token = 2 + 28669, // Index = 88, Token = 3 + 36862, // Index = 88, Token = 4 + 45053, // Index = 88, Token = 5 + 53245, // Index = 88, Token = 6 + 61436, // Index = 88, Token = 7 + -4095, // Index = 88, Token = 8 + -12286, // Index = 88, Token = 9 + -20478, // Index = 88, Token = 10 + -28669, // Index = 88, Token = 11 + -36862, // Index = 88, Token = 12 + -45053, // Index = 88, Token = 13 + -53245, // Index = 88, Token = 14 + -61436 // Index = 88, Token = 15 +}; + diff --git a/CODE/DYNAVEC.CPP b/CODE/DYNAVEC.CPP new file mode 100644 index 0000000..0dacb60 --- /dev/null +++ b/CODE/DYNAVEC.CPP @@ -0,0 +1,299 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/DYNAVEC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + * * + * Project Name : Red Alert * + * * + * File Name : DYNAVEC.CPP * + * * + * Programmer : Bill R Randolph * + * * + * Start Date : 09/18/96 * + * * + * Last Update : September 18, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Delete -- Deletes specified index from vector. * + * DynamicVectorClass::Delete -- Remove specified object from vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor * + * DynamicVectorClass::ID -- Find matching value in dynamic vector. * + * DynamicVectorClass::Resize -- Changes size of a dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vector.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +#include +#include + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previously allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previously and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * * + * This routine will add the specified element to the head of the vector. If necessary, * + * the vector will be expanded accordingly. * + * * + * INPUT: object -- Reference to the object to add to the head of this vector. * + * * + * OUTPUT: bool; Was the object added without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + return(Delete(ID(object))); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if ((unsigned)index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +/************************** end of dynavec.cpp *****************************/ diff --git a/CODE/EDIT.CPP b/CODE/EDIT.CPP new file mode 100644 index 0000000..4138b90 --- /dev/null +++ b/CODE/EDIT.CPP @@ -0,0 +1,474 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/EDIT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EDIT.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EditClass::Action -- Handles input events. * + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * EditClass::Draw_Text -- Draws the edit gadget text. * + * EditClass::EditClass -- Normal constructor for edit class object. * + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * EditClass::EditClass -- Normal constructor for edit class object. * + * * + * This is the normal constructor used to create an edit object. * + * * + * INPUT: id -- The ID number for this edit object. This is the ID number that will be * + * returned by the Input() function when the key is pressed if this * + * gadget has the keyboard input focus. * + * * + * text -- Reference to the text buffer that the edit gadget will modify as keyboard * + * input is processed. The value that this buffer contains is the default * + * text displayed. * + * * + * maxlen-- The maximum size of the text buffer specified. This length INCLUDES the * + * trailing null character so a simple sizeof() function call can be used. * + * * + * flags -- These are the text print control flags. It is used to control how the * + * text looks in the edit box. Use the normal TPF_??? flags. * + * * + * x,y -- The pixel coordinates of the upper left corner of the edit gadget area. * + * * + * w,h -- The pixel dimensions of the edit box. If either of these are no provided, * + * or set to -1, then the dimension is determined from the string itself. * + * * + * sytle -- This style flag parameter control what kind of characters are allowed in * + * the edit box. The initial string in the text buffer may contain illegal * + * characters, but they are NOT removed regardless of this parameter. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/05/1995 MML : Created. * + * 01/21/1995 JLB : Modified. * + *=============================================================================================*/ +EditClass::EditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + ControlClass (id, x, y, w, h, LEFTPRESS), String(text) +{ + TextFlags = flags & ~(TPF_CENTER); + EditFlags = style; + Set_Text(text, max_len); + Color = GadgetClass::Get_Color_Scheme(); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + + if (h == -1) { + Height = FontHeight+1; + } + if (w == -1) { + if (strlen(String) > 0) { + Width = String_Pixel_Width(String) + 6; + } else { + Width = ((Char_Pixel_Width('X')+FontXSpacing) * (MaxLength+1)) + 2; + } + } + } + + IsReadOnly = 0; +} + + +/*********************************************************************************************** + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * * + * This default destructor removes the focus setting if it currently has it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/24/1995 JLB : Created. * + *=============================================================================================*/ +EditClass::~EditClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*********************************************************************************************** + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * * + * Use this routine to change the text that this edit gadget refers to. * + * * + * INPUT: text -- Reference to the character array that this edit gadget will be * + * modifying. * + * max_len -- The maximum size of the buffer that will be modified. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Set_Text(char * text, int max_len) +{ + String = text; + MaxLength = max_len-1; + Length = strlen(String); + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * * + * This routine will render the edit box. This will show the box outline as well as any * + * text it may contain. * + * * + * INPUT: forced -- Should the edit box be drawn even if it thinks it doesn't have to? * + * * + * OUTPUT: Was the edit box drawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Background(); + + /* + ** Display the text. + */ + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * EditClass::Action -- Handles input events. * + * * + * This routine will handle all mouse and keyboard events directed at this edit box * + * gadget. For keyboard events, this will insert the characters into the edit box. * + * * + * INPUT: flags -- The event flag that triggered this function call. * + * * + * key -- Reference to the keyboard/mouse event that triggered this function call. * + * * + * OUTPUT: Should the list be processed further? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Action(unsigned flags, KeyNumType & key) +{ + /* + ** If this is a read-only edit box, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** If the left mouse button is pressed over this gadget, then set the focus to + ** this gadget. The event flag is cleared so that no button ID number is returned. + */ + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + /* + ** Handle keyboard events here. Normally, the key is added to the string, but if the + ** RETURN key is pressed, then the button ID number is returned from the Input() + ** function. + */ + if ((flags & KEYBOARD) && Has_Focus()) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + key = (KeyNumType)(key & ~WWKEY_VK_BIT); + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + /* + ** Filter out all special keys except return and backspace + */ if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) + || key == KN_RETURN || key == KN_BACKSPACE) { + + + + if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * * + * This routine will redraw the edit gadget background. The overlaying text is handled by * + * a different routine. The mouse is guaranteed to be hidden when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Background(void) +{ + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); +} + + +/*********************************************************************************************** + * EditClass::Draw_Text -- Draws the edit gadget text. * + * * + * This routine is called when the edit gadget text needs to be drawn. The background has * + * already been drawn by the time this function is called. The mouse is guaranteed to be * + * hidden as well. * + * * + * INPUT: text -- The text to draw in the edit gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Text(char const * text) +{ + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + + +/*********************************************************************************************** + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * * + * This is the gruntwork routine that processes keyboard input to the edit gadget. This * + * routine will be called when keyboard input has been detected and this gadget has the * + * current focus. * + * * + * INPUT: ascii -- The ASCII key code that was fetched from the keyboard buffer. * + * * + * OUTPUT: bool; Should this keyboard input NOT cause the gadget ID number to be returned * + * from the controlling Input() routine? Typically, the return value would be * + * true unless the focus is lost due to the key being pressed. * + * * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool EditClass::Handle_Key(KeyASCIIType ascii) +{ + switch (ascii) { + /* + ** Handle the special case of a non-keyboard event. It is possible that this + ** key code might be passed to this routine if this routine has been overridden + ** and the key event was consumed. + */ + case 0: + break; + + /* + ** If the return key is pressed, then remove the focus from this edit + ** gadget but otherwise let the normal gadget processing proceed. This + ** causes the gadget ID number to be returned from the Input() function + ** so that the controlling program will know that the text can be + ** processed. + */ + case KA_RETURN: + Clear_Focus(); + return(false); + + /* + ** When the BACKSPACE key is pressed, remove the last character in the edit string. + */ + case KA_BACKSPACE: + if (Length) { + Length--; + String[Length] = '\0'; + Flag_To_Redraw(); + } + break; + + /* + ** If the keyboard event was not a recognized special key, then examine to see + ** if it can legally be added to the edit string and do so if possible. + */ + default: + + /* + ** Don't add a character if the length is greater than edit width. + */ + if ((String_Pixel_Width(String) + Char_Pixel_Width(ascii) ) >= (Width-2)) { + break; + } + + /* + ** Don't add a character if the length is already at maximum. + */ + if (Length >= MaxLength) break; + + /* + ** Invisible characters are never added to the string. This is + ** especially true for spaces at the beginning of the string. + */ + if (!isgraph(ascii) && ascii != ' ') break; + if (ascii == ' ' && Length == 0) break; + + /* + ** If this is an upper case only edit gadget, then force the alphabetic + ** character to upper case. + */ + if ((EditFlags & UPPERCASE) && isalpha(ascii)) { + ascii = (KeyASCIIType)toupper(ascii); + } + + if ((!(EditFlags & NUMERIC) || !isdigit(ascii)) && + (!(EditFlags & ALPHA) || !isalpha(ascii)) && + (!(EditFlags & MISC) || isalnum(ascii)) && + ascii != ' ') { + break; + } + + /* + ** The character passed all legality checks, so add it to the edit string + ** and flag this gadget to be redrawn. The manual flag to redraw is needed + ** because the event flag has been cleared. This prevents the gadget's ID + ** number from being returned just because the gadget has been edited. + */ + String[Length++] = ascii; + String[Length] = '\0'; + Flag_To_Redraw(); + break; + } + return(true); +} + + +void EditClass::Set_Focus(void) +{ + Length = 0; + if (String) { + Length = strlen(String); + } + ControlClass::Set_Focus(); +} diff --git a/CODE/EDIT.H b/CODE/EDIT.H new file mode 100644 index 0000000..cc17f0a --- /dev/null +++ b/CODE/EDIT.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/EDIT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EDIT_H +#define EDIT_H + +class EditClass : public ControlClass +{ + public: + typedef enum EditStyle { + ALPHA =0x0001, // Edit accepts alphabetic characters. + NUMERIC =0x0002, // Edit accepts numbers. + MISC =0x0004, // Edit accepts misc graphic characters. + UPPERCASE =0x0008, // Force to upper case. + ALPHANUMERIC=(int)ALPHA|(int)NUMERIC|(int)MISC + } EditStyle; + + EditClass (int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1, EditStyle style=ALPHANUMERIC); + virtual ~EditClass(void); + + virtual void Set_Focus(void); + virtual int Draw_Me(int forced); + virtual void Set_Text(char * text, int max_len); + virtual char * Get_Text(void) {return(String);}; + void Set_Color (RemapControlType * color) { Color = color; } + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + + /* + ** These are the text size and style flags to be used when displaying the text + ** of the edit gadget. + */ + TextPrintType TextFlags; + + /* + ** Input flags that control what characters are allowed in the string. + */ + EditStyle EditFlags; + + /* + ** Pointer to text staging buffer and the maximum length of the string it + ** can contain. + */ + char *String; + int MaxLength; + + /* + ** This is the current length of the string. This length will never exceed the + ** MaxLength allowed. + */ + int Length; + + /* + ** This is the desired color of the edit control. + */ + RemapControlType * Color; + + virtual int Action (unsigned flags, KeyNumType &key); + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + virtual bool Handle_Key(KeyASCIIType ascii); + + private: + int IsReadOnly; +}; + +inline EditClass::EditStyle operator |(EditClass::EditStyle, EditClass::EditStyle); +inline EditClass::EditStyle operator &(EditClass::EditStyle, EditClass::EditStyle); +inline EditClass::EditStyle operator ~(EditClass::EditStyle); + +#endif diff --git a/CODE/EGOS.CPP b/CODE/EGOS.CPP new file mode 100644 index 0000000..7949851 --- /dev/null +++ b/CODE/EGOS.CPP @@ -0,0 +1,988 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/EGOS.CPP 2 3/10/97 3:19p Steve_tall $ */ +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : EGOS.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** List of Ego Class instances +** There will be one instance for each line of text. +*/ +DynamicVectorClass EgoList; + +/* +** Number of slideshow pictures +*/ + +#define NUM_SLIDES 17 + +/* +** Length of time frame is displayed for +*/ +#define FRAME_DELAY 150 + +/* +** Number of frames that palete fade occurs over +*/ +#define FADE_DELAY 37 + +/* +** Names of slideshow pictures to play behind the text +*/ +char SlideNames[NUM_SLIDES][13]={ + + "aftr_hi.pcx", + "aly1.pcx", + "apc_hi.pcx", + "aphi0049.pcx", + "bnhi0020.pcx", + "dchi0040.pcx", + "frhi0166.pcx", + "lab.pcx", + "landsbrg.pcx", + "mahi0107.pcx", + "mig_hi.pcx", + "mtfacthi.pcx", + "needle.pcx", + "sov2.pcx", + "spy.pcx", + "stalin.pcx", + "tent.pcx" +}; + +/* +** Names of low res slideshow pictures to play behind the text +*/ +char LoresSlideNames[NUM_SLIDES][13]={ + "malo0107.cps", + "mig_lo.cps", + "mtfactlo.cps", + "needl-lo.cps", + "sov2-lo.cps", + "spy-lo.cps", + "staln-lo.cps", + "tent-lo.cps", + "aftr_lo.cps", + "aly1-lo.cps", + "apc_lo.cps", + "aplo0049.cps", + "bnlo0020.cps", + "dclo0040.cps", + "frlo0166.cps", + "lab-lo.cps", + "lands-lo.cps" +}; + +/* +** Array of all the palettes required for the slides +*/ +char SlidePals[NUM_SLIDES][256*3]; + +/* +** Array of graphic buffers containing the slides +*/ +GraphicBufferClass *SlideBuffers[NUM_SLIDES]; + +/* +** Original copy of slide (pref in video mem) that we use to undraw the text +*/ +GraphicBufferClass *BackgroundPage; + +/* +** This palette contains both the font palette entries and the slide +** palette. +*/ +PaletteClass ComboPalette; + +/* +** Ptr to the combo palette. +*/ +unsigned char *ComboPalPtr; + +/* +** Lookup table. If an entry is non-zero then it should be faded in/out when the slide changes. +*/ +char PaletteLUT[256]; + +/* +** Height of the strips that are blitted from the slides to the backgound and hid pages. +** We blit in several strips over several frames so as not to impact on the frame rate. +*/ +#define CHUNK_HEIGHT RESFACTOR * 50 + + + +#ifndef WIN32 +extern void Vsync(void); +#pragma aux Vsync modify [edx ebx eax] = \ + "mov edx,03DAh" \ + "mov ebx,[VertBlank]" \ + "and bl,001h" \ + "shl bl,3" \ + "in_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "je in_vbi" \ + "out_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "jne out_vbi" +#endif //WIN32 + + + +/*********************************************************************************************** + * EC::EgoClass -- EgoClass constructor * + * * + * * + * * + * INPUT: x position of text * + * y position of text * + * ptr to text string * + * flags to print text with * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:53PM ST : Created * + *=============================================================================================*/ +EgoClass::EgoClass (int x, int y, char *text, TextPrintType flags) +{ + XPos = x; + YPos = y; + Flags= flags; + Text = new char [strlen (text)+1]; + strcpy (Text, text); +} + + +/*********************************************************************************************** + * EC::~EgoClass -- EgoClass destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:54PM ST : Created * + *=============================================================================================*/ +EgoClass::~EgoClass(void) +{ + delete [] Text; +} + + +/*********************************************************************************************** + * EC::Scroll -- Apply the given distance to the y position of the text. * + * A positive distance scrolls up. * + * * + * * + * INPUT: distance in pixels to scroll up * + * * + * OUTPUT: true if text scrolled beyond the top of the screen * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:55PM ST : Created * + *=============================================================================================*/ +bool EgoClass::Scroll(int distance) +{ + YPos -= distance; + if (YPos < -20) { + return (true); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * EC::Render -- Draws the text to the logic page * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:57PM ST : Created * + *=============================================================================================*/ +void EgoClass::Render (void) +{ + if (YPos < LogicPage->Get_Height() && YPos > -16) { + Fancy_Text_Print(Text, XPos, YPos, GadgetClass::Get_Color_Scheme(), TBLACK, Flags); + } +} + + + +/*********************************************************************************************** + * EC::Wipe -- Wipes the previously rendered text by blitting a rectangle from the given * + * background screen. * + * * + * * + * INPUT: ptr to screen containing original background * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:58PM ST : Created * + *=============================================================================================*/ +void EgoClass::Wipe (GraphicBufferClass *background) +{ + int width = String_Pixel_Width (Text); + int x = XPos; + + if (Flags & TPF_RIGHT) { + x -= width; + }else{ + if (Flags & TPF_CENTER){ + x -= width/2; + } + } + + background->Blit(*LogicPage, x-1, YPos, x-1, YPos, width+2, 7 * RESFACTOR +1, false); +} + + + +/*********************************************************************************************** + * Set_Pal -- Low level palette set * + * * + * * + * * + * INPUT: ptr to palette * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:59PM ST : Created * + *=============================================================================================*/ +void Set_Pal(char *palette) +{ +//#ifndef WIN32 + //Vsync(); + //unsigned char *rgbptr = (unsigned char *) palette; + //outportb(0x03C8, 0); //Start from color 0 + + //for (int index = 0; index < 256; index++) { + // outrgb(rgbptr[index*3], rgbptr[index*3+1], rgbptr[index*3+2]); + //} +//#else //WIN32 + + Set_Palette((void*)palette); +//#endif +} + + +/*********************************************************************************************** + * Slide_Show -- Handles the blitting and fading of the background pictures. * + * * + * The picture frame number is used to trigger blitting and fading events * + * * + * * + * INPUT: picture number * + * frame * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:16AM ST : Created * + *=============================================================================================*/ +void Slide_Show (int slide, int frame) +{ + + /* + ** Temprary storage to save CCPalette to + */ + char save_palette[256*3]; + + + if (frame >= 1 && frame <=4){ + /* + ** Blit in a quarter of the new frame to the background page. + */ + SlideBuffers[slide]->Blit (*BackgroundPage, 0, (frame-1) * CHUNK_HEIGHT, + 0, (frame-1) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame >= 5 && frame <=8 ){ + /* + ** Blit in a quarter of the new frame to the hid page. + */ + BackgroundPage->Blit (HidPage, 0, (frame-5) * CHUNK_HEIGHT, + 0, (frame-5) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame ==9){ + /* + ** Create the combo palette from the font entries and the picture entries. + */ + for (int index = 0 ; index < 256 ; index++ ){ + if (PaletteLUT[index]) { + ComboPalPtr[index*3] = SlidePals[slide][index*3]; + ComboPalPtr[index*3+1] = SlidePals[slide][index*3+1]; + ComboPalPtr[index*3+2] = SlidePals[slide][index*3+2]; + } + } + return; + } + + + if (frame >10 && frame < FADE_DELAY+10){ + /* + ** Fade up the picture in the background. The text colors never fade. + */ + memcpy (save_palette, CCPalette, sizeof(save_palette)); + //CCPalette.Partial_Adjust (MIN (6*(frame-5), 255), ComboPalette, PaletteLUT); + CCPalette.Partial_Adjust (MIN ((255/FADE_DELAY)*(frame-10), 255), ComboPalette, PaletteLUT); + Set_Pal ( (char *) &CCPalette); + if (frame != 9+FADE_DELAY){ + memcpy (CCPalette, save_palette, sizeof(save_palette)); + }else{ + memcpy (CCPalette, CurrentPalette, sizeof (CCPalette)); + } + return; + } + + + if (frame >FRAME_DELAY && frame < FRAME_DELAY+FADE_DELAY){ + /* + ** Fade down the picture in the background. The text colors never fade. + */ + memcpy (save_palette, CCPalette, sizeof(save_palette)); + CCPalette.Partial_Adjust (MIN ((255/FADE_DELAY)*(frame-FRAME_DELAY), 255), PaletteLUT); + if (frame != FRAME_DELAY+FADE_DELAY-1){ + Set_Pal ( (char *) &CCPalette); + memcpy (CCPalette, save_palette, sizeof(save_palette)); + }else{ + /* + ** If this is the last fade down frame then zero the picture palette entries. + */ + unsigned char *ccpalptr = (unsigned char*)CCPalette; + for (int index = 0 ; index < 256 ; index++){ + if (PaletteLUT[index]){ + ccpalptr[index*3] = 0; + ccpalptr[index*3+1] = 0; + ccpalptr[index*3+2] = 0; + } + } + Set_Pal ( (char *) &CCPalette); + } + + } + +} + + + + +/*********************************************************************************************** + * Show_Who_Was_Responsible -- Main function to print the credits. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:20AM ST : Created * + *=============================================================================================*/ +void Show_Who_Was_Responsible (void) +{ + + int i; + int key; + + /* + ** Deault speed of credits scolling. This is the frame delay between pixel scrolls. + */ + static int speed = 3; + + /* + ** In DOS we need to scroll slower so we have a bool that lets us do it every other time + */ +#ifndef WIN32 + bool scroll_now = false; +#endif //WIN32 + + /* + ** Read in the credits file to be displayed + ** + ** Lines of text in CREDITS.TXT are treated as follows.... + ** + ** If the text starts and ends to the left of column 40 then text will be right justified. + ** If the text starts before column 40 and ends after it then it will be centered. + ** If the text starts after column 40 it will be right justified. + */ + CCFileClass creditsfile ("credits.txt"); + if ( !creditsfile.Is_Available()) return; + char *credits = new char [creditsfile.Size()+1]; + creditsfile.Read (credits, creditsfile.Size()); + + /* + ** Initialise the text printing system. + */ + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_GREEN]); + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + /* + ** Miscellaneous stuff for parsing the credits text file. + */ + int length = creditsfile.Size(); + int line = 0; + int column = 0; + char *cptr = credits; + char ch, lastchar, oldchar; + char *strstart, *strparse; + bool gotendstr; + int startcolumn, endcolumn, x; + int y=SeenBuff.Get_Height()+2; + EgoClass *ego; + TextPrintType flags; + + + /* + ** Search through the text file and extract the strings, using each string to create + ** a new EgoClass + */ + do { + /* + ** Search for text + */ + ch = *cptr++; + length --; + + /* + ** Look for a non whitespace character. + */ + switch ( ch ){ + + case 13: + /* + ** Char was carriage return. Go on to the next line starting at column 0. + */ + line++; + column = 0; + break; + + + case 10: + /* + ** Ignore line feed. CR does both. + */ + break; + + /* + ** Space character. Just advance the cursor and move on. + */ + case 32: + column++; + break; + + /* + ** Tab char. Advance to the next tab column. Tabs are every 8 columns. + */ + case 9: + column += 8; + column &= 0xfffffff8; + break; + + default: + /* + ** Found new string. Work out where it ends so we know how to treat it. + */ + lastchar = ch; + strstart = cptr-1; + strparse = cptr-1; + endcolumn = startcolumn = column; + gotendstr = false; + + do { + ch = *strparse++; + switch ( ch ){ + case 9: + case 10: + case 13: + gotendstr = true; + break; + + case 32: + if (lastchar == 32) gotendstr = true; + endcolumn++; + break; + + default: + endcolumn++; + } + if (strparse >= cptr+length) gotendstr = true; + + lastchar = ch; + }while (!gotendstr); + + + if (strparse >= cptr+length) break; + + /* + ** Strip off any trailing space. + */ + if (*(strparse-2) == 32){ + strparse--; + endcolumn -= 2; + } + + + /* + ** If string straddles the center column then center it. + ** + ** If string is on the left hand side then right justify it. + ** + ** If string is on the right hand side then left justify it. + */ + flags = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW; //TPF_NOSHADOW; + + if (startcolumn <40 && endcolumn >40){ + flags = flags | TPF_CENTER; + x = SeenBuff.Get_Width() / 2; + }else{ + if (startcolumn <40){ + flags = flags | TPF_RIGHT; + x = endcolumn *SeenBuff.Get_Width() /80; + }else{ + x = startcolumn * SeenBuff.Get_Width() / 80; + } + } + + /* + ** Temporarily terminate the string. + */ + oldchar = *(strparse-1); + *(strparse-1) = 0; + + /* + ** Create the new class and add it to our list. + */ + ego = new EgoClass (x, y+ line *8 *RESFACTOR, strstart, flags); + + EgoList.Add (ego); + + /* + ** Restore the character that was lost when we added the terminator. + */ + *(strparse-1) = oldchar; + + /* + ** Fix up our outer loop parsing variables. + */ + cptr = strparse; + column += strparse - strstart; + length -= strparse - strstart-1; + + if (ch == 13) { + line++; + column = 0; + }else{ + if (ch == 9){ + column += 7; + column &= 0xfffffff8; + } + } + break; + } + + } while ( length>0 ); + + + /* + ** Work out which palette entries the font needs so we dont fade those colors. + */ + memset (PaletteLUT, 1, sizeof (PaletteLUT)); + int pcolor = PCOLOR_GREEN; + + for (int index = 0; index < 6; index++) { + PaletteLUT[ColorRemaps[pcolor].FontRemap[10+index]] =0; + } + //PaletteLUT[ColorRemaps[pcolor].BrightColor] = 0; + PaletteLUT[ColorRemaps[pcolor].Color] = 0; + PaletteLUT[ColorRemaps[pcolor].Shadow] = 0; + PaletteLUT[ColorRemaps[pcolor].Background] = 0; + PaletteLUT[ColorRemaps[pcolor].Corners] = 0; + PaletteLUT[ColorRemaps[pcolor].Highlight] = 0; + PaletteLUT[ColorRemaps[pcolor].Bright] = 0; + PaletteLUT[ColorRemaps[pcolor].Underline] = 0; + PaletteLUT[ColorRemaps[pcolor].Bar] = 0; + PaletteLUT[ColorRemaps[pcolor].Box] = 0; + + + /* + ** Stop the music. + */ + Theme.Stop(); + + /* + ** Fade to black. + */ + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + + /* + ** Load the reference palette for the font. + */ + //Load_Title_Page(true); +//#ifdef WIN32 +// Load_Picture("EGOPAL.CPS", SysMemPage, SysMemPage, CCPalette, BM_DEFAULT); +//#else //WIN32 +// Load_Picture("EGOPAL.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); +//#endif //WIN32 + + + CCFileClass("EGOPAL.PAL").Read(&CCPalette, sizeof(CCPalette)); + + /* + ** Copy the font palette entries into the combo palette. + */ + PaletteClass credit_palette; + ComboPalPtr = (unsigned char *) &ComboPalette; + unsigned char *creditpal_ptr = (unsigned char *) &credit_palette; + memcpy (ComboPalette, CCPalette, sizeof (ComboPalette)); + + for (index = 0 ; index < 256 ; index++ ){ + if (PaletteLUT[index]) { + ComboPalPtr[index*3] = 0; + ComboPalPtr[index*3+1] = 0; + ComboPalPtr[index*3+2] = 0; + } + } + + /* + ** Clear the Seen Page since we will not be blitting to all of it. + */ + SeenBuff.Clear(); + HidPage.Clear(); + + /* + ** Set the font palette. + */ + memcpy ( CCPalette, ComboPalette, sizeof (ComboPalette) ); + CCPalette.Set(); + + /* + ** Loop through and load up all the slideshow pictures + */ + for (index = 0 ; index < NUM_SLIDES ; index++){ +#ifdef WIN32 + SlideBuffers[index] = new GraphicBufferClass; + SlideBuffers[index]->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); + Load_Title_Screen(&SlideNames[index][0], SlideBuffers[index], (unsigned char*) &SlidePals[index][0]); +#else //WIN32 + SlideBuffers[index] = new GraphicBufferClass (SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + Load_Picture(&LoresSlideNames[index][0], *SlideBuffers[index], *SlideBuffers[index], (unsigned char *)&SlidePals[index][0], BM_DEFAULT); +#endif //WIN32 + } + + /* + ** Create a new graphic buffer to restore the background from. Initialise it to black so + ** we can start scrolling before the first slideshow picture is blitted. + */ +#ifdef WIN32 + BackgroundPage = new GraphicBufferClass; + BackgroundPage->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)(GBC_VIDEOMEM)); +#else //WIN32 + BackgroundPage = new GraphicBufferClass (SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL ); +#endif //WIN32 + + SeenBuff.Blit(*BackgroundPage); + + /* + ** Go away nasty keyboard. + */ + Keyboard->Clear(); + + Set_Logic_Page(HidPage); + + /* + ** Start any old song. + */ + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Queue_Song(THEME_CREDITS); + + /* + ** Init misc timing variables. + */ + int time = TickCount; + int frame = 0; + int picture_frame = 0; + int slide_number = 0; + + Hide_Mouse(); + + /* + ** Save the priority of this process so we can change it back later + */ + //DWORD process_priority = GetPriorityClass(GetCurrentProcess()); + + /* + ** Main scrolling loop. + ** Keeps going until all the EgoClass objects are deleted or esc is pressed. + */ + while ( EgoList.Count() ){ + + frame++; + + /* + ** Once we have been running for a few frames, and Windows has time to do its virtual + ** memory stuff, increase our priority level. + */ + //if (frame == 30){ + // SetPriorityClass (GetCurrentProcess() , HIGH_PRIORITY_CLASS); + //} + + /* + ** Update the slideshow frame and switch to the next picture if its time. + */ + picture_frame++; + + if (picture_frame > FRAME_DELAY+50){ + if (slide_number =0 ; i--){ + EgoList[i]->Wipe(BackgroundPage); + if ( EgoList[i]->Scroll(1) ){ + EgoList.Delete(i); + break; + } + } +#ifndef WIN32 + } +#endif //WIN32 + /* + ** Render all the text strings in their new positions. + */ + if (LogicPage->Lock()){ + for (i=EgoList.Count()-1 ; i>=0 ; i--){ + EgoList[i]->Render(); + } + LogicPage->Unlock(); + } + + if (frame > 1000 && !Theme.Still_Playing()){ + Theme.Queue_Song(THEME_CREDITS); //NONE); + } + + /* + ** Stop calling Theme.AI after a while so a different song doesnt start playing + */ + Call_Back(); +// if (frame <1000 ){ +// Theme.AI(); +// }else{ +// Sound_Callback(); +// } + + /* + ** Kill any spare time before blitting the hid page forward. + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + /* + ** Blit all but the top and bottom of the hid page. This is beacuse the text print doesn't + ** clip vertically and looks ugly when it suddenly appears and disappears. + */ +#ifndef WIN32 + Wait_Vert_Blank(VertBlank); + //Vsync(); +#endif //WIN32 + HidPage.Blit(SeenBuff, 0, 8*RESFACTOR, 0, 8*RESFACTOR, SeenBuff.Get_Width(), SeenBuff.Get_Height() - 16*RESFACTOR, false); + + /* + ** Try and prevent Win95 from swapping out pictures we havnt used yet. + */ +#ifdef WIN32 + if (frame && 3 == 3){ + for (i=slide_number+1 ; iGet_IsDirectDraw() ){ + Force_VM_Page_In ((void*)SlideBuffers[i]->Get_Offset(), SeenBuff.Get_Width() * SeenBuff.Get_Height() ); + } + } + } +#endif //WIN32 + + /* + ** If user hits escape then break. + */ + key = KN_NONE; + if (Keyboard->Check()){ + key = Keyboard->Get(); + if (key == KN_ESC){ + break; + } +#if (0) + if (key == KN_Z){ + speed--; + if (speed <1 ) speed=1; + time = TickCount; + frame = 0; + } + if (key == KN_X){ + speed++; + time = TickCount; + frame = 0; + } +#endif //(0) + + } + + } + + if (key == KN_ESC){ + Theme.Fade_Out(); + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + }else{ + /* + ** Wait for the picture to fade down + */ + while (picture_frame <= FADE_DELAY+FRAME_DELAY){ + if (picture_frame < FRAME_DELAY && picture_frame > 10+FADE_DELAY){ + picture_frame = FRAME_DELAY; + } + frame++; + picture_frame++; + + Slide_Show (slide_number, picture_frame); + + Call_Back(); +// Sound_Callback(); //Theme.AI(); + + /* + ** Kill any spare time + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + } + } + + /* + ** Tidy up. + */ + //SetPriorityClass (GetCurrentProcess() , process_priority); + SeenBuff.Clear(); + + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + Theme.Stop(); + Options.Set_Score_Volume(oldvolume, false); + + for (index = 0 ; index < NUM_SLIDES ; index++){ + delete SlideBuffers[index]; + } + + delete BackgroundPage; + + delete [] credits; + + EgoList.Clear(); +} + + + + + + + + + + + + + + diff --git a/CODE/EGOS.H b/CODE/EGOS.H new file mode 100644 index 0000000..f002656 --- /dev/null +++ b/CODE/EGOS.H @@ -0,0 +1,63 @@ +/* +** 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 . +*/ + + +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : EGOS.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class EgoClass { + + public: + + EgoClass (int x, int y, char *text, TextPrintType flags); + ~EgoClass (); + + bool Scroll (int distance); + void Render (void); + void Wipe (GraphicBufferClass *background); + + + char *Text; + int XPos; + int YPos; + TextPrintType Flags; +}; + + diff --git a/CODE/ENDING.CPP b/CODE/ENDING.CPP new file mode 100644 index 0000000..e3ffd1c --- /dev/null +++ b/CODE/ENDING.CPP @@ -0,0 +1,163 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ENDING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void GDI_Ending(void) +{ +#ifdef NEVER + if (TempleIoned) { + Play_Movie("GDIFINB"); + } else { + Play_Movie("GDIFINA"); + } + + Score.Presentation(); + + if (TempleIoned) { + Play_Movie("GDIEND2"); + } else { + Play_Movie("GDIEND1"); + } + Play_Movie("CC2TEASE"); +#endif +} + + +/*********************************************************************************************** + * Nod_Ending -- play ending movies for Nod players * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 7/10/1995 BWG : Created. * + *=============================================================================================*/ +void Nod_Ending(void) +{ +#ifdef NEVER + static char const _tanpal[]={0x0,0x0,0xED,0x0,0x2C,0x0,0xFB,0x0,0xFD,0x0,0x0,0x0,0x0,0x0,0x52,0x0}; + + char fname[12]; + char * satpic = new char[64000]; + int oldfontxspacing = FontXSpacing; + void const * oldfont; + + Score.Presentation(); + + oldfont = Set_Font(ScoreFontPtr); + + void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL")); + Load_Uncompress(CCFileClass("SATSEL.CPS"), HidPage, HidPage); + memcpy(satpic, HidPage.Get_Buffer(), 64000); + + void * kanefinl = Load_Sample("KANEFINL.AUD"); + void * loopie6m = Load_Sample("LOOPIE6M.AUD"); + + Play_Movie("NODFINAL", THEME_NONE, false); + + Hide_Mouse(); + Wait_Vert_Blank(VertBlank); + Set_Palette(localpal); + memcpy(SeenBuff.Get_Buffer(), satpic, 64000); + Show_Mouse(); + + Keyboard->Clear(); + Play_Sample(kanefinl, 255, 128); + Play_Sample(loopie6m, 255, 128); + + bool mouseshown = false; + bool done = false; + int selection = 1; + bool printedtext = false; + while (!done) { + if (!printedtext && !Is_Sample_Playing(kanefinl)) { + printedtext++; + Alloc_Object(new ScorePrintClass(Text_String(TXT_SEL_TARGET), 0, 180, _tanpal)); + mouseshown = true; + Show_Mouse(); + } + Call_Back_Delay(1); + if (!Keyboard->Check()) { + if (!Is_Sample_Playing(loopie6m)) Play_Sample(loopie6m, 255, 128); + } else { + if (Is_Sample_Playing(kanefinl)) { + Clear_KeyBuffer(); + } else { + int key = Keyboard->Get(); + if ((key & 0xFF) == KN_LMOUSE && !(key & KN_RLSE_BIT)) { + int mousex = MouseQX; + int mousey = MouseQY; + if (mousey >= 22 && mousey <= 177) { + done++; + if (mousex < 160 && mousey < 100) selection = 2; + if (mousex < 160 && mousey >= 100) selection = 3; + if (mousex >= 160 && mousey >= 100) selection = 4; + } + } + } + } + } + if (mouseshown) Hide_Mouse(); + delete satpic; + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + // erase the "choose a target" text + SeenBuff.Fill_Rect(0, 180, 319, 199, 0); + + Hide_Mouse(); + Keyboard->Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + Free_Sample(kanefinl); + Free_Sample(loopie6m); + + sprintf(fname, "NODEND%d", selection); + PreserveVQAScreen = 1; + Play_Movie(fname); + + Play_Movie("CC2TEASE"); +#endif +} diff --git a/CODE/ENDING.H b/CODE/ENDING.H new file mode 100644 index 0000000..313b3e8 --- /dev/null +++ b/CODE/ENDING.H @@ -0,0 +1,43 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ENDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ENDING_H +#define ENDING_H + +void Nod_Ending(void); + +#endif diff --git a/CODE/ENG/CONQUER.BAK b/CODE/ENG/CONQUER.BAK new file mode 100644 index 0000000..305ee30 --- /dev/null +++ b/CODE/ENG/CONQUER.BAK @@ -0,0 +1,1075 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Time:%02d:%02d:%02d +# TXT_TIME_FORMAT_HOURS +Time:%02d:%02d +# TXT_TIME_FORMAT_NO_HOURS +Sell +# TXT_BUTTON_SELL +Sell Structure +# TXT_SELL +Repair +# TXT_BUTTON_REPAIR +You: +# TXT_YOU +Enemy: +# TXT_ENEMY +Buildings Destroyed By +# TXT_BUILD_DEST +Units Destroyed By +# TXT_UNIT_DEST +Ore Harvested By +# TXT_TIB_HARV +Score: %d +# TXT_SCORE_1 +Yes +# TXT_YES +No +# TXT_NO +Mission Accomplished +# TXT_SCENARIO_WON +Mission Failed +# TXT_SCENARIO_LOST +Start New Game +# TXT_START_NEW_GAME +Intro & Sneak Peek +# TXT_INTRO +Cancel +# TXT_CANCEL +Rock +# TXT_ROCK +Civilian +# TXT_CIVILIAN +Containment Team +# TXT_JP +OK +# TXT_OK +Tree +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Clear +# TXT_CLEAR +Water +# TXT_WATER +Road +# TXT_ROAD +Slope +# TXT_SLOPE +Patch +# TXT_PATCH +River +# TXT_RIVER +Load Mission +# TXT_LOAD_MISSION +Save Mission +# TXT_SAVE_MISSION +Delete Mission +# TXT_DELETE_MISSION +Load +# TXT_LOAD_BUTTON +Save +# TXT_SAVE_BUTTON +Delete +# TXT_DELETE_BUTTON +Game Controls +# TXT_GAME_CONTROLS +Sound Controls +# TXT_SOUND_CONTROLS +Resume Mission +# TXT_RESUME_MISSION +Visual Controls +# TXT_VISUAL_CONTROLS +Abort Mission +# TXT_QUIT_MISSION +Exit Game +# TXT_EXIT_GAME +Options +# TXT_OPTIONS +Squish mark +# TXT_SQUISH +Crater +# TXT_CRATER +Scorch Mark +# TXT_SCORCH +BRIGHTNESS: +# TXT_BRIGHTNESS +Music Volume +# TXT_MUSIC +Sound Volume +# TXT_VOLUME +Tint: +# TXT_TINT +Contrast: +# TXT_CONTRAST +Game Speed: +# TXT_SPEED +Scroll Rate: +# TXT_SCROLLRATE +Color: +# TXT_COLOR +Return to game +# TXT_RETURN_TO_GAME +Enemy Soldier +# TXT_ENEMY_SOLDIER +Enemy Vehicle +# TXT_ENEMY_VEHICLE +Enemy Structure +# TXT_ENEMY_STRUCTURE +Light Tank +# TXT_LTANK +Heavy Tank +# TXT_MTANK +Medium Tank +# TXT_MTANK2 +Mammoth Tank +# TXT_HTANK +SAM Site +# TXT_SAM +Ranger +# TXT_JEEP +Chinook Helicopter +# TXT_TRANS +Ore Truck +# TXT_HARVESTER +Artillery +# TXT_ARTY +Rifle Infantry +# TXT_E1 +Grenadier +# TXT_E2 +Rocket Soldier +# TXT_E3 +Flamethrower +# TXT_E4 +Longbow Helicopter +# TXT_HELI +Hind +# TXT_ORCA +APC +# TXT_APC +Guard Tower +# TXT_GUARD_TOWER +Radar Dome +# TXT_COMMAND +Helipad +# TXT_HELIPAD +Airfield +# TXT_AIRSTRIP +Ore Silo +# TXT_STORAGE +Construction Yard +# TXT_CONST_YARD +Ore Refinery +# TXT_REFINERY +Church +# TXT_CIV1 +Han's and Gretel's +# TXT_CIV2 +Hewitt's Manor +# TXT_CIV3 +Ricktor's House +# TXT_CIV4 +Gretchin's House +# TXT_CIV5 +The Barn +# TXT_CIV6 +Damon's pub +# TXT_CIV7 +Fran's House +# TXT_CIV8 +Music Factory +# TXT_CIV9 +Toymaker's +# TXT_CIV10 +Ludwig's House +# TXT_CIV11 +Haystacks +# TXT_CIV12 +Haystack +# TXT_CIV13 +Wheat Field +# TXT_CIV14 +Fallow Field +# TXT_CIV15 +Corn Field +# TXT_CIV16 +Celery Field +# TXT_CIV17 +Potato Field +# TXT_CIV18 +Sala's House +# TXT_CIV20 +Abdul's House +# TXT_CIV21 +Pablo's Wicked Pub +# TXT_CIV22 +Village Well +# TXT_CIV23 +Camel Trader +# TXT_CIV24 +Church +# TXT_CIV25 +Ali's House +# TXT_CIV26 +Trader Ted's +# TXT_CIV27 +Menelik's House +# TXT_CIV28 +Prestor John's House +# TXT_CIV29 +Village Well +# TXT_CIV30 +Witch Doctor's Hut +# TXT_CIV31 +Rikitikitembo's Hut +# TXT_CIV32 +Roarke's Hut +# TXT_CIV33 +Mubasa's Hut +# TXT_CIV34 +Aksum's Hut +# TXT_CIV35 +Mambo's Hut +# TXT_CIV36 +The Studio +# TXT_CIV37 +Technology Center +# TXT_CIVMISS +Turret +# TXT_TURRET +Gunboat +# TXT_GUNBOAT +Mobile Construction Vehicle +# TXT_MCV +Power Plant +# TXT_POWER +Advanced Power Plant +# TXT_ADVANCED_POWER +Hospital +# TXT_HOSPITAL +Barracks +# TXT_BARRACKS +Oil Pump +# TXT_PUMP +Oil Tanker +# TXT_TANKER +Sandbags +# TXT_SANDBAG_WALL +Chain Link Fence +# TXT_CYCLONE_WALL +Concrete Wall +# TXT_BRICK_WALL +Barbwire Fence +# TXT_BARBWIRE_WALL +Wood Fence +# TXT_WOOD_WALL +War Factory +# TXT_WEAPON_FACTORY +Advanced Guard Tower +# TXT_AGUARD_TOWER +Bio-Research Laboratory +# TXT_BIO_LAB +Service Depot +# TXT_FIX_IT +Sidebar +# TXT_TAB_SIDEBAR +Options +# TXT_TAB_BUTTON_CONTROLS +Database +# TXT_TAB_BUTTON_DATABASE +Unrevealed Terrain +# TXT_SHADOW +Options Menu +# TXT_OPTIONS_MENU +Stop +# TXT_STOP +Play +# TXT_PLAY +Shuffle +# TXT_SHUFFLE +Repeat +# TXT_REPEAT +Music volume: +# TXT_MUSIC_VOLUME +Sound volume: +# TXT_SOUND_VOLUME +On +# TXT_ON +Off +# TXT_OFF +Multiplayer Game +# TXT_MULTIPLAYER_GAME +No files available +# TXT_NO_FILES +Do you want to delete this file? +# TXT_DELETE_SINGLE_FILE +Do you want to delete %d files? +# TXT_DELETE_MULTIPLE_FILES +Reset Values +# TXT_RESET_MENU +Do you want to abort the mission? +# TXT_CONFIRM_EXIT +Mission Description +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Barry +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Karen +# TXT_C5 +Steve +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Prof. Einstein +# TXT_EINSTEIN +Road Bib +# TXT_BIB +Faster +# TXT_FASTER +Slower +# TXT_SLOWER +Air Strike +# TXT_AIR_STRIKE +Steel Crate +# TXT_STEEL_CRATE +Wood Crate +# TXT_WOOD_CRATE +Water Crate +# TXT_WATER_CRATE +Flag Location +# TXT_FLAG_SPOT +Unable to read scenario! +# TXT_UNABLE_READ_SCENARIO +Error loading game! +# TXT_ERROR_LOADING_GAME +Obsolete saved game. +# TXT_OBSOLETE_SAVEGAME +You must enter a description! +# TXT_MUSTENTER_DESCRIPTION +Error saving game! +# TXT_ERROR_SAVING_GAME +Delete this file? +# TXT_DELETE_FILE_QUERY +[EMPTY SLOT] +# TXT_EMPTY_SLOT +Select Multiplayer Game +# TXT_SELECT_MPLAYER_GAME +Modem/Serial +# TXT_MODEM_SERIAL +Network +# TXT_NETWORK +Unable to initialize network! +# TXT_INIT_NET_ERROR +Join Network Game +# TXT_JOIN_NETWORK_GAME +New +# TXT_NEW +Join +# TXT_JOIN +Send Message +# TXT_SEND_MESSAGE +Your Name: +# TXT_YOUR_NAME +Your Side: +# TXT_SIDE_COLON +Your Color: +# TXT_COLOR_COLON +Games +# TXT_GAMES +Players +# TXT_PLAYERS +Scenario: +# TXT_SCENARIO_COLON +>> NOT FOUND << +# TXT_NOT_FOUND +Starting Credits: +# TXT_START_CREDITS_COLON +Bases: +# TXT_BASES_COLON +Ore: +# TXT_TIBERIUM_COLON +Crates: +# TXT_CRATES_COLON +AI Players: +# TXT_AI_PLAYERS_COLON +Request denied. +# TXT_REQUEST_DENIED +Unable to play; scenario not found. +# TXT_UNABLE_PLAY_WAAUGH +Nothing to join! +# TXT_NOTHING_TO_JOIN +You must enter a name! +# TXT_NAME_ERROR +Duplicate names are not allowed. +# TXT_DUPENAMES_NOTALLOWED +Your game version is outdated. +# TXT_YOURGAME_OUTDATED +Destination game version is outdated. +# TXT_DESTGAME_OUTDATED +%s's Game +# TXT_THATGUYS_GAME +[%s's Game] +# TXT_THATGUYS_GAME_BRACKET +Network Game Setup +# TXT_NETGAME_SETUP +Reject +# TXT_REJECT +You can't reject yourself! +# TXT_CANT_REJECT_SELF +You must select a player to reject. +# TXT_SELECT_PLAYER_REJECT +Bases +# TXT_BASES +Crates +# TXT_CRATES +AI Players +# TXT_AI_PLAYERS +Scenarios +# TXT_SCENARIOS +Credits: +# TXT_CREDITS_COLON +Only one player? +# TXT_ONLY_ONE +Oops! +# TXT_OOPS +To %s: +# TXT_TO +To All: +# TXT_TO_ALL +Message: +# TXT_MESSAGE +Connection to %s lost! +# TXT_CONNECTION_LOST +%s has left the game. +# TXT_LEFT_GAME +%s has been defeated! +# TXT_PLAYER_DEFEATED +Waiting to Connect... +# TXT_WAITING_CONNECT +Connection error! Check your cables. Attempting to Reconnect... +# TXT_NULL_CONNERR_CHECK_CABLES +Connection error! Redialing... +# TXT_MODEM_CONNERR_REDIALING +Connection error! Waiting for Call... +# TXT_MODEM_CONNERR_WAITING +Select Serial Game +# TXT_SELECT_SERIAL_GAME +Dial Modem +# TXT_DIAL_MODEM +Answer Modem +# TXT_ANSWER_MODEM +Null Modem +# TXT_NULL_MODEM +Settings +# TXT_SETTINGS +Port: +# TXT_PORT_COLON +IRQ: +# TXT_IRQ_COLON +Baud: +# TXT_BAUD_COLON +Init String: +# TXT_INIT_STRING +Call Waiting String: +# TXT_CWAIT_STRING +Tone Dialing +# TXT_TONE_BUTTON +Pulse Dialing +# TXT_PULSE_BUTTON +Host Serial Game +# TXT_HOST_SERIAL_GAME +Opponent: +# TXT_OPPONENT_COLON +User signed off! +# TXT_USER_SIGNED_OFF +Join Serial Game +# TXT_JOIN_SERIAL_GAME +Phone List +# TXT_PHONE_LIST +Add +# TXT_ADD +Edit +# TXT_EDIT +Dial +# TXT_DIAL +Default +# TXT_DEFAULT +Default Settings +# TXT_DEFAULT_SETTINGS +Custom Settings +# TXT_CUSTOM_SETTINGS +Phone Listing +# TXT_PHONE_LISTING +Name: +# TXT_NAME_COLON +Number: +# TXT_NUMBER_COLON +Unable to find modem. Check power and cables. +# TXT_UNABLE_FIND_MODEM +No carrier. +# TXT_NO_CARRIER +Line busy. +# TXT_LINE_BUSY +Number invalid. +# TXT_NUMBER_INVALID +Other system not responding! +# TXT_SYSTEM_NOT_RESPONDING +Games are out of sync! +# TXT_OUT_OF_SYNC +Packet received too late! +# TXT_PACKET_TOO_LATE +Other player has left the game. +# TXT_PLAYER_LEFT_GAME +From %s:%s +# TXT_FROM +TIME: +# TXT_SCORE_TIME +LEADERSHIP: +# TXT_SCORE_LEAD +ECONOMY: +# TXT_SCORE_EFFI +TOTAL SCORE: +# TXT_SCORE_TOTA +CASUALTIES: +# TXT_SCORE_CASU +NEUTRAL: +# TXT_SCORE_NEUT +BUILDINGS LOST +# TXT_SCORE_BUIL +BUILDINGS +# TXT_SCORE_BUIL1 +LOST: +# TXT_SCORE_BUIL2 +TOP SCORES +# TXT_SCORE_TOP +ENDING CREDITS: +# TXT_SCORE_ENDCRED +%dh %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +Dialing... +# TXT_DIALING +Dialing Canceled +# TXT_DIALING_CANCELED +Waiting for Call... +# TXT_WAITING_FOR_CALL +Answering Canceled +# TXT_ANSWERING_CANCELED +Engineer +# TXT_E6 +Spy +# TXT_E8 +Not a Null Modem Cable Attached! It is a modem or loopback cable. +# TXT_MODEM_OR_LOOPBACK +Map +# TXT_MAP +Blossom Tree +# TXT_BLOSSOM_TREE +Briefing +# TXT_RESTATE_MISSION +Computer +# TXT_COMPUTER +Unit Count: +# TXT_COUNT +Tech Level: +# TXT_LEVEL +Opponent +# TXT_OPPONENT +Kills: +# TXT_KILLS_COLON +Video +# TXT_VIDEO +Scientist +# TXT_C10 +Capture The Flag +# TXT_CAPTURE_THE_FLAG +Mission Objective +# TXT_OBJECTIVE +Mission +# TXT_MISSION +No saved games available. +# TXT_NO_SAVES +Civilian Building +# TXT_CIVILIAN_BUILDING +Technician +# TXT_TECHNICIAN +Save game options are not allowed during a multiplayer session. +# TXT_NO_SAVELOAD +Special 1 +# TXT_DELPHI +Would you like to replay this mission? +# TXT_TO_REPLAY +Reconnecting to %s. +# TXT_RECONN_TO +Please wait %02d seconds. +# TXT_PLEASE_WAIT +Do you wish to surrender? +# TXT_SURRENDER +SELECT TRANSMISSION +# TXT_SEL_TRANS +Your game name must be unique. +# TXT_GAMENAME_MUSTBE_UNIQUE +Game is closed. +# TXT_GAME_IS_CLOSED +Your name must be unique. +# TXT_NAME_MUSTBE_UNIQUE +Reconnecting to %s +# TXT_RECONNECTING_TO +Waiting for connections... +# TXT_WAITING_FOR_CONNECTIONS +Time allowed: %02d seconds +# TXT_TIME_ALLOWED +Press ESC to cancel. +# TXT_PRESS_ESC +From Computer: It's just you and me now! +# TXT_JUST_YOU_AND_ME +Capture the Flag: +# TXT_CAPTURE_THE_FLAG_COLON +Special 2 +# TXT_CHAN +%s has allied with %s +# TXT_HAS_ALLIED +%s declares war on %s +# TXT_AT_WAR +Select a target +# TXT_SEL_TARGET +Resign Game +# TXT_RESIGN +Ore grows quickly. +# TXT_TIBERIUM_FAST +Answering... +# TXT_ANSWERING +Initializing Modem... +# TXT_INITIALIZING_MODEM +Scenarios don't match. +# TXT_SCENARIOS_DO_NOT_MATCH +Power Output +# TXT_POWER_OUTPUT +Power Output (low) +# TXT_POWER_OUTPUT_LOW +Continue +# TXT_CONTINUE +Data Queue Overflow +# TXT_QUEUE_FULL +%s changed game options! +# TXT_SPECIAL_WARNING +Please insert a Red Alert CD into the CD-ROM drive. +# TXT_CD_DIALOG_1 +Please insert CD %d (%s) into the CD-ROM drive. +# TXT_CD_DIALOG_2 +Red Alert is unable to detect your CD ROM drive. +# TXT_CD_ERROR1 +No Sound Card Detected +# TXT_NO_SOUND_CARD +UNKNOWN +# TXT_UNKNOWN +(old) +# TXT_OLD_GAME +Insufficient Disk Space to run Red Alert. +# TXT_NO_SPACE +You must have %d megabytes of free disk space. +# TXT_MUST_HAVE_SPACE +Run SETUP program first. +# TXT_RUN_SETUP +Waiting for Opponent +# TXT_WAITING_FOR_OPPONENT +Please select 'Settings' to setup default configuration +# TXT_SELECT_SETTINGS +Prison +# TXT_PRISON +Mission Saved +# TXT_GAME_WAS_SAVED +Insufficient disk space to save a game. Please delete a previous save to free up some disk space and try again. +# TXT_SPACE_CANT_SAVE +Invalid Port/Address. COM 1-4 OR ADDRESS +# TXT_INVALID_PORT_ADDRESS +Invalid Port and/or IRQ settings +# TXT_INVALID_SETTINGS +IRQ already in use +# TXT_IRQ_ALREADY_IN_USE +Abort +# TXT_ABORT +Restart +# TXT_RESTART +Mission is restarting. Please wait... +# TXT_RESTARTING +Mission is loading. Please wait... +# TXT_LOADING +Error in the InitString +# TXT_ERROR_IN_INITSTRING +Shroud: +# TXT_SHADOW_COLON +Anti-Vehicle Mine +# TXT_AVMINE +Anti-Personnel Mine +# TXT_APMINE +New Missions +# TXT_NEW_MISSIONS +Thief +# TXT_THIEF +Radar Jammer +# TXT_MRJ +Gap Generator +# TXT_GAP_GENERATOR +Pillbox +# TXT_PILLBOX +Camo. Pillbox +# TXT_CAMOPILLBOX +Chronosphere +# TXT_CHRONOSPHERE +England +# TXT_ENGLAND +Germany +# TXT_GERMANY +Spain +# TXT_SPAIN +Russia +# TXT_USSR +Ukraine +# TXT_UKRAINE +Greece +# TXT_GREECE +France +# TXT_FRANCE +Turkey +# TXT_TURKEY +Shore +# TXT_SHORE +Select Object +# TXT_PLACE_OBJECT +Submarine +# TXT_SS +Destroyer +# TXT_DD +Cruiser +# TXT_CA +Transport +# TXT_TRANSPORT +Gun Boat +# TXT_PT +Lobby +# TXT_LOBBY +Games +# TXT_CHANNEL_GAMES +Save Game... +# TXT_SAVING_GAME +Game is full. +# TXT_GAME_FULL +You must select a game! +# TXT_MUST_SELECT_GAME +%s playing %s +# TXT_S_PLAYING_S +Only the host can modify this option. +# TXT_ONLY_HOST_CAN_MODIFY +Game was cancelled. +# TXT_GAME_CANCELLED +%s has formed a new game. +# TXT_S_FORMED_NEW_GAME +%s's game is now in progress. +# TXT_GAME_NOW_IN_PROGRESS +Tesla Coil +# TXT_TESLA +Mobile Gap Generator +# TXT_MGG +Flame Tower +# TXT_FLAME_TURRET +AA Gun +# TXT_AAGUN +Kennel +# TXT_KENNEL +Soviet Tech Center +# TXT_SOVIET_TECH +Badger Bomber +# TXT_BADGER +Mig Attack Plane +# TXT_MIG +Yak Attack Plane +# TXT_YAK +Barbed Wire +# TXT_FENCE +Field Medic +# TXT_MEDIC +Saboteur +# TXT_SABOTEUR +General +# TXT_GENERAL +Tanya +# TXT_E7 +Parabombs +# TXT_PARA_BOMB +Paratroopers +# TXT_PARA_INFANTRY +Parachute Saboteur +# TXT_PARA_SABOTEUR +Naval Yard +# TXT_SHIP_YARD +Sub Pen +# TXT_SUB_PEN +Scenario Options +# TXT_SCENARIO_OPTIONS +Spy Plane +# TXT_SPY_MISSION +Spy Plane +# TXT_U2 +Attack Dog +# TXT_GUARD_DOG +Spy Info +# TXT_SPY_INFO +Buildings +# TXT_BUILDNGS +Units +# TXT_UNITS +Infantry +# TXT_INFANTRY +Aircraft +# TXT_AIRCRAFT +Supply Truck +# TXT_TRUCK +Invulnerability Device +# TXT_INVUL +Iron Curtain +# TXT_IRON_CURTAIN +Allied Tech Center +# TXT_ADVANCED_TECH +V2 Rocket +# TXT_V2_LAUNCHER +Forward Command Post +# TXT_FORWARD_COM +Demolitioner +# TXT_DEMOLITIONER +Mine Layer +# TXT_MINE_LAYER +Fake Construction Yard +# TXT_FAKE_CONST +Fake War Factory +# TXT_FAKE_WEAP +Fake Naval Yard +# TXT_FAKE_YARD +Fake Sub Pen +# TXT_FAKE_PEN +Fake Radar Dome +# TXT_FAKE_RADAR +Bigfoot +# TXT_THEME_BIGF +Crush +# TXT_THEME_CRUS +Face the Enemy 1 +# TXT_THEME_FAC1 +Face the Enemy 2 +# TXT_THEME_FAC2 +Hell March +# TXT_THEME_HELL +Run for Your Life +# TXT_THEME_RUN1 +Smash +# TXT_THEME_SMSH +Trenches +# TXT_THEME_TREN +Workmen +# TXT_THEME_WORK +Await +# TXT_THEME_AWAIT +Dense +# TXT_THEME_DENSE_R +Map Selection +# TXT_THEME_MAP +Fogger +# TXT_THEME_FOGGER1A +Mud +# TXT_THEME_MUD1A +Radio 2 +# TXT_THEME_RADIO2 +Roll Out +# TXT_THEME_ROLLOUT +Snake +# TXT_THEME_SNAKE +Terminate +# TXT_THEME_TERMINAT +Twin +# TXT_THEME_TWIN +Vector +# TXT_THEME_VECTOR1A +Team Members +# TXT_TEAM_MEMBERS +Bridge +# TXT_BRIDGE +Barrel +# TXT_BARREL +Friendly +# TXT_GOODGUY +Enemy +# TXT_BADGUY +Gold +# TXT_GOLD +Gems +# TXT_GEMS +Title Movie +# TXT_TEASER +Movies +# TXT_MOVIES +Interior +# TXT_INTERIOR +Sonar Pulse +# TXT_SONAR_PULSE +Missile Silo +# TXT_MSLO +GPS Satellite +# TXT_GPS_SATELLITE +Atom Bomb +# TXT_NUCLEAR_BOMB +Easy +# TXT_EASY +Hard +# TXT_HARD +Normal +# TXT_NORMAL +Please set the difficulty level. It will be used throughout this campaign. +# TXT_DIFFICULTY +Allies +# TXT_ALLIES +Soviet +# TXT_SOVIET +Intro Theme +# TXT_THEME_INTRO +Shroud Regrows +# TXT_SHADOW_REGROWS +Ore Regenerates +# TXT_ORE_SPREADS +Score Theme +# TXT_THEME_SCORE +Internet Game +# TXT_INTERNET +Ice +# TXT_ICE +Crates +# TXT_CRATE +Skirmish +# TXT_SKIRMISH +Choose your side. +# TXT_CHOOSE +Valuable Minerals +# TXT_MINERALS +Ignore +# TXT_IGNORE +Error - modem is not responding. +# TXT_ERROR_NO_RESP +Error - modem did not respond to result code enable command. +# TXT_ERROR_NO_RESCODE +Error - modem did not respond to initialisation string. +# TXT_ERROR_NO_INIT +Error - modem did not respond to 'verbose' command. +# TXT_ERROR_NO_VERB +Error - modem did not respond to 'echo' command. +# TXT_ERROR_NO_ECHO +Error - unable to disable modem auto answer. +# TXT_ERROR_NO_DISABLE +Error - Too many errors initialising modem - Aborting. +# TXT_ERROR_TOO_MANY +Error - Modem returned error status. +# TXT_ERROR_ERROR +Error - TIme out waiting for connect. +# TXT_ERROR_TIMEOUT +Accomplished +# TXT_ACCOMPLISHED +Click to Continue +# TXT_CLICK_CONTINUE +Receiving scenario from host. +# TXT_RECEIVING_SCENARIO +Sending scenario to remote players. +# TXT_SENDING_SCENARIO +Error - Modem failed to respond to flow control command. Your Windows configuration may be incorrect. +# TXT_NO_FLOW_CONTROL_RESPONSE +Error - Modem failed to respond to compression command. Your Windows configuration may be incorrect. +# TXT_NO_COMPRESSION_RESPONSE +Error - Modem failed to respond to error correction command. Your Windows configuration may be incorrect. +# TXT_NO_ERROR_CORRECTION_RESPONSE +To play Red Alert via the internet you must be connected to an internet services provider and be registered with Planet Westwood +# TXT_EXPLAIN_REGISTRATION +Wchat not installed. Please install it from either CD. +# TXT_ERROR_UNABLE_TO_RUN_WCHAT +Register +# TXT_REGISTER +Ore Mine +# TXT_ORE_MINE +No registered modem +# TXT_NO_REGISTERED_MODEM +Chronoshift +# TXT_CHRONOSHIFT +Invalid Port or Port is in use +# TXT_UNABLE_TO_OPEN_PORT +No dial tone. Ensure your modem is connected to the phone line and try again. +# TXT_NO_DIAL_TONE +Error - other player does not have this expansion scenario. +# TXT_NO_EXPANSION_SCENARIO +Please Stand By... +# TXT_STAND_BY +End Credits Theme +# TXT_THEME_CREDITS +Low Power: AA-Guns offline +# TXT_POWER_AAGUN +Low Power: Tesla Coils offline +# TXT_POWER_TESLA +Low Power +# TXT_LOW_POWER +Commander: +# TXT_COMMANDER +Battles Won: +# TXT_BATTLES_WON +Game versions incompatable. To make sure you have the latest version, visit www.westwood.com +# TXT_MISMATCH +Incompatable scenario file detected. The scenario may be corrupt. +# TXT_SCENARIO_ERROR +Connecting... +# TXT_CONNECTING +Modem Initialization +# TXT_MODEM_INITIALISATION +Data Compression +# TXT_DATA_COMPRESSION +Error Correction +# TXT_ERROR_CORRECTION +Hardware Flow Control +# TXT_HARDWARE_FLOW_CONTROL +Advanced +# TXT_ADVANCED +2nd_Hand +# TXT_THEME_2ND_HAND +Arazoid +#TXT_THEME_ARAZOID +BackStab +#TXT_THEME_BACKSTAB +Chaos2 +#TXT_THEME_CHAOS2 +Shut_It +#TXT_THEME_SHUT_IT +TwinMix1 +#TXT_THEME_TWINMIX1 +Under3 +#TXT_THEME_UNDER3 +VR2 +#TXT_THEME_VR2 +The other system is not repsonding. Do you wish to attempt an emergency game save? Both players must save for this to work. +# TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING +The other system hung up. Do you wish to attempt an emergency game save? Both players must save for this to work. # TXT_ASK_EMERGENCY_SAVE +# TXT_ASK_EMERGENCY_SAVE_HUNG_UP + + + + diff --git a/CODE/ENG/CONQUER.TXT b/CODE/ENG/CONQUER.TXT new file mode 100644 index 0000000..bc3e22b --- /dev/null +++ b/CODE/ENG/CONQUER.TXT @@ -0,0 +1,1109 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Time:%02d:%02d:%02d +# TXT_TIME_FORMAT_HOURS +Time:%02d:%02d +# TXT_TIME_FORMAT_NO_HOURS +Sell +# TXT_BUTTON_SELL +Sell Structure +# TXT_SELL +Repair +# TXT_BUTTON_REPAIR +You: +# TXT_YOU +Enemy: +# TXT_ENEMY +Buildings Destroyed By +# TXT_BUILD_DEST +Units Destroyed By +# TXT_UNIT_DEST +Ore Harvested By +# TXT_TIB_HARV +Score: %d +# TXT_SCORE_1 +Yes +# TXT_YES +No +# TXT_NO +Mission Accomplished +# TXT_SCENARIO_WON +Mission Failed +# TXT_SCENARIO_LOST +Start New Game +# TXT_START_NEW_GAME +Intro & Sneak Peek +# TXT_INTRO +Cancel +# TXT_CANCEL +Rock +# TXT_ROCK +Civilian +# TXT_CIVILIAN +Containment Team +# TXT_JP +OK +# TXT_OK +Tree +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Clear +# TXT_CLEAR +Water +# TXT_WATER +Road +# TXT_ROAD +Slope +# TXT_SLOPE +Patch +# TXT_PATCH +River +# TXT_RIVER +Load Mission +# TXT_LOAD_MISSION +Save Mission +# TXT_SAVE_MISSION +Delete Mission +# TXT_DELETE_MISSION +Load +# TXT_LOAD_BUTTON +Save +# TXT_SAVE_BUTTON +Delete +# TXT_DELETE_BUTTON +Game Controls +# TXT_GAME_CONTROLS +Sound Controls +# TXT_SOUND_CONTROLS +Resume Mission +# TXT_RESUME_MISSION +Visual Controls +# TXT_VISUAL_CONTROLS +Abort Mission +# TXT_QUIT_MISSION +Exit Game +# TXT_EXIT_GAME +Options +# TXT_OPTIONS +Squish mark +# TXT_SQUISH +Crater +# TXT_CRATER +Scorch Mark +# TXT_SCORCH +BRIGHTNESS: +# TXT_BRIGHTNESS +Music Volume +# TXT_MUSIC +Sound Volume +# TXT_VOLUME +Tint: +# TXT_TINT +Contrast: +# TXT_CONTRAST +Game Speed: +# TXT_SPEED +Scroll Rate: +# TXT_SCROLLRATE +Color: +# TXT_COLOR +Return to game +# TXT_RETURN_TO_GAME +Enemy Soldier +# TXT_ENEMY_SOLDIER +Enemy Vehicle +# TXT_ENEMY_VEHICLE +Enemy Structure +# TXT_ENEMY_STRUCTURE +Light Tank +# TXT_LTANK +Heavy Tank +# TXT_MTANK +Medium Tank +# TXT_MTANK2 +Mammoth Tank +# TXT_HTANK +SAM Site +# TXT_SAM +Ranger +# TXT_JEEP +Chinook Helicopter +# TXT_TRANS +Ore Truck +# TXT_HARVESTER +Artillery +# TXT_ARTY +Rifle Infantry +# TXT_E1 +Grenadier +# TXT_E2 +Rocket Soldier +# TXT_E3 +Flamethrower +# TXT_E4 +Longbow Helicopter +# TXT_HELI +Hind +# TXT_ORCA +APC +# TXT_APC +Guard Tower +# TXT_GUARD_TOWER +Radar Dome +# TXT_COMMAND +Helipad +# TXT_HELIPAD +Airfield +# TXT_AIRSTRIP +Ore Silo +# TXT_STORAGE +Construction Yard +# TXT_CONST_YARD +Ore Refinery +# TXT_REFINERY +Church +# TXT_CIV1 +Han's and Gretel's +# TXT_CIV2 +Hewitt's Manor +# TXT_CIV3 +Ricktor's House +# TXT_CIV4 +Gretchin's House +# TXT_CIV5 +The Barn +# TXT_CIV6 +Damon's pub +# TXT_CIV7 +Fran's House +# TXT_CIV8 +Music Factory +# TXT_CIV9 +Toymaker's +# TXT_CIV10 +Ludwig's House +# TXT_CIV11 +Haystacks +# TXT_CIV12 +Haystack +# TXT_CIV13 +Wheat Field +# TXT_CIV14 +Fallow Field +# TXT_CIV15 +Corn Field +# TXT_CIV16 +Celery Field +# TXT_CIV17 +Potato Field +# TXT_CIV18 +Sala's House +# TXT_CIV20 +Abdul's House +# TXT_CIV21 +Pablo's Wicked Pub +# TXT_CIV22 +Village Well +# TXT_CIV23 +Camel Trader +# TXT_CIV24 +Church +# TXT_CIV25 +Ali's House +# TXT_CIV26 +Trader Ted's +# TXT_CIV27 +Menelik's House +# TXT_CIV28 +Prestor John's House +# TXT_CIV29 +Village Well +# TXT_CIV30 +Witch Doctor's Hut +# TXT_CIV31 +Rikitikitembo's Hut +# TXT_CIV32 +Roarke's Hut +# TXT_CIV33 +Mubasa's Hut +# TXT_CIV34 +Aksum's Hut +# TXT_CIV35 +Mambo's Hut +# TXT_CIV36 +The Studio +# TXT_CIV37 +Technology Center +# TXT_CIVMISS +Turret +# TXT_TURRET +Gunboat +# TXT_GUNBOAT +Mobile Construction Vehicle +# TXT_MCV +Power Plant +# TXT_POWER +Advanced Power Plant +# TXT_ADVANCED_POWER +Hospital +# TXT_HOSPITAL +Barracks +# TXT_BARRACKS +Oil Pump +# TXT_PUMP +Oil Tanker +# TXT_TANKER +Sandbags +# TXT_SANDBAG_WALL +Chain Link Fence +# TXT_CYCLONE_WALL +Concrete Wall +# TXT_BRICK_WALL +Barbwire Fence +# TXT_BARBWIRE_WALL +Wood Fence +# TXT_WOOD_WALL +War Factory +# TXT_WEAPON_FACTORY +Advanced Guard Tower +# TXT_AGUARD_TOWER +Bio-Research Laboratory +# TXT_BIO_LAB +Service Depot +# TXT_FIX_IT +Sidebar +# TXT_TAB_SIDEBAR +Options +# TXT_TAB_BUTTON_CONTROLS +Database +# TXT_TAB_BUTTON_DATABASE +Unrevealed Terrain +# TXT_SHADOW +Options Menu +# TXT_OPTIONS_MENU +Stop +# TXT_STOP +Play +# TXT_PLAY +Shuffle +# TXT_SHUFFLE +Repeat +# TXT_REPEAT +Music volume: +# TXT_MUSIC_VOLUME +Sound volume: +# TXT_SOUND_VOLUME +On +# TXT_ON +Off +# TXT_OFF +Multiplayer Game +# TXT_MULTIPLAYER_GAME +No files available +# TXT_NO_FILES +Do you want to delete this file? +# TXT_DELETE_SINGLE_FILE +Do you want to delete %d files? +# TXT_DELETE_MULTIPLE_FILES +Reset Values +# TXT_RESET_MENU +Do you want to abort the mission? +# TXT_CONFIRM_EXIT +Mission Description +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Barry +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Karen +# TXT_C5 +Steve +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Prof. Einstein +# TXT_EINSTEIN +Road Bib +# TXT_BIB +Faster +# TXT_FASTER +Slower +# TXT_SLOWER +Air Strike +# TXT_AIR_STRIKE +Steel Crate +# TXT_STEEL_CRATE +Wood Crate +# TXT_WOOD_CRATE +Water Crate +# TXT_WATER_CRATE +Flag Location +# TXT_FLAG_SPOT +Unable to read scenario! +# TXT_UNABLE_READ_SCENARIO +Error loading game! +# TXT_ERROR_LOADING_GAME +Obsolete saved game. +# TXT_OBSOLETE_SAVEGAME +You must enter a description! +# TXT_MUSTENTER_DESCRIPTION +Error saving game! +# TXT_ERROR_SAVING_GAME +Delete this file? +# TXT_DELETE_FILE_QUERY +[EMPTY SLOT] +# TXT_EMPTY_SLOT +Select Multiplayer Game +# TXT_SELECT_MPLAYER_GAME +Modem/Serial +# TXT_MODEM_SERIAL +Network +# TXT_NETWORK +Unable to initialize network! +# TXT_INIT_NET_ERROR +Join Network Game +# TXT_JOIN_NETWORK_GAME +New +# TXT_NEW +Join +# TXT_JOIN +Send Message +# TXT_SEND_MESSAGE +Your Name: +# TXT_YOUR_NAME +Your Side: +# TXT_SIDE_COLON +Your Color: +# TXT_COLOR_COLON +Games +# TXT_GAMES +Players +# TXT_PLAYERS +Scenario: +# TXT_SCENARIO_COLON +>> NOT FOUND << +# TXT_NOT_FOUND +Starting Credits: +# TXT_START_CREDITS_COLON +Bases: +# TXT_BASES_COLON +Ore: +# TXT_TIBERIUM_COLON +Crates: +# TXT_CRATES_COLON +AI Players: +# TXT_AI_PLAYERS_COLON +Request denied. +# TXT_REQUEST_DENIED +Unable to play; scenario not found. +# TXT_UNABLE_PLAY_WAAUGH +Nothing to join! +# TXT_NOTHING_TO_JOIN +You must enter a name! +# TXT_NAME_ERROR +Duplicate names are not allowed. +# TXT_DUPENAMES_NOTALLOWED +Your game version is outdated. +# TXT_YOURGAME_OUTDATED +Destination game version is outdated. +# TXT_DESTGAME_OUTDATED +%s's Game +# TXT_THATGUYS_GAME +[%s's Game] +# TXT_THATGUYS_GAME_BRACKET +Network Game Setup +# TXT_NETGAME_SETUP +Reject +# TXT_REJECT +You can't reject yourself! +# TXT_CANT_REJECT_SELF +You must select a player to reject. +# TXT_SELECT_PLAYER_REJECT +Bases +# TXT_BASES +Crates +# TXT_CRATES +AI Players +# TXT_AI_PLAYERS +Scenarios +# TXT_SCENARIOS +Credits: +# TXT_CREDITS_COLON +Only one player? +# TXT_ONLY_ONE +Oops! +# TXT_OOPS +To %s: +# TXT_TO +To All: +# TXT_TO_ALL +Message: +# TXT_MESSAGE +Connection to %s lost! +# TXT_CONNECTION_LOST +%s has left the game. +# TXT_LEFT_GAME +%s has been defeated! +# TXT_PLAYER_DEFEATED +Waiting to Connect... +# TXT_WAITING_CONNECT +Connection error! Check your cables. Attempting to Reconnect... +# TXT_NULL_CONNERR_CHECK_CABLES +Connection error! Redialing... +# TXT_MODEM_CONNERR_REDIALING +Connection error! Waiting for Call... +# TXT_MODEM_CONNERR_WAITING +Select Serial Game +# TXT_SELECT_SERIAL_GAME +Dial Modem +# TXT_DIAL_MODEM +Answer Modem +# TXT_ANSWER_MODEM +Null Modem +# TXT_NULL_MODEM +Settings +# TXT_SETTINGS +Port: +# TXT_PORT_COLON +IRQ: +# TXT_IRQ_COLON +Baud: +# TXT_BAUD_COLON +Init String: +# TXT_INIT_STRING +Call Waiting String: +# TXT_CWAIT_STRING +Tone Dialing +# TXT_TONE_BUTTON +Pulse Dialing +# TXT_PULSE_BUTTON +Host Serial Game +# TXT_HOST_SERIAL_GAME +Opponent: +# TXT_OPPONENT_COLON +User signed off! +# TXT_USER_SIGNED_OFF +Join Serial Game +# TXT_JOIN_SERIAL_GAME +Phone List +# TXT_PHONE_LIST +Add +# TXT_ADD +Edit +# TXT_EDIT +Dial +# TXT_DIAL +Default +# TXT_DEFAULT +Default Settings +# TXT_DEFAULT_SETTINGS +Custom Settings +# TXT_CUSTOM_SETTINGS +Phone Listing +# TXT_PHONE_LISTING +Name: +# TXT_NAME_COLON +Number: +# TXT_NUMBER_COLON +Unable to find modem. Check power and cables. +# TXT_UNABLE_FIND_MODEM +No carrier. +# TXT_NO_CARRIER +Line busy. +# TXT_LINE_BUSY +Number invalid. +# TXT_NUMBER_INVALID +Other system not responding! +# TXT_SYSTEM_NOT_RESPONDING +Games are out of sync! +# TXT_OUT_OF_SYNC +Packet received too late! +# TXT_PACKET_TOO_LATE +Other player has left the game. +# TXT_PLAYER_LEFT_GAME +From %s:%s +# TXT_FROM +TIME: +# TXT_SCORE_TIME +LEADERSHIP: +# TXT_SCORE_LEAD +ECONOMY: +# TXT_SCORE_EFFI +TOTAL SCORE: +# TXT_SCORE_TOTA +CASUALTIES: +# TXT_SCORE_CASU +NEUTRAL: +# TXT_SCORE_NEUT +BUILDINGS LOST +# TXT_SCORE_BUIL +BUILDINGS +# TXT_SCORE_BUIL1 +LOST: +# TXT_SCORE_BUIL2 +TOP SCORES +# TXT_SCORE_TOP +ENDING CREDITS: +# TXT_SCORE_ENDCRED +%dh %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +Dialing... +# TXT_DIALING +Dialing Canceled +# TXT_DIALING_CANCELED +Waiting for Call... +# TXT_WAITING_FOR_CALL +Answering Canceled +# TXT_ANSWERING_CANCELED +Engineer +# TXT_E6 +Spy +# TXT_E8 +Not a Null Modem Cable Attached! It is a modem or loopback cable. +# TXT_MODEM_OR_LOOPBACK +Map +# TXT_MAP +Blossom Tree +# TXT_BLOSSOM_TREE +Briefing +# TXT_RESTATE_MISSION +Computer +# TXT_COMPUTER +Unit Count: +# TXT_COUNT +Tech Level: +# TXT_LEVEL +Opponent +# TXT_OPPONENT +Kills: +# TXT_KILLS_COLON +Video +# TXT_VIDEO +Scientist +# TXT_C10 +Capture The Flag +# TXT_CAPTURE_THE_FLAG +Mission Objective +# TXT_OBJECTIVE +Mission +# TXT_MISSION +No saved games available. +# TXT_NO_SAVES +Civilian Building +# TXT_CIVILIAN_BUILDING +Technician +# TXT_TECHNICIAN +Save game options are not allowed during a multiplayer session. +# TXT_NO_SAVELOAD +Special 1 +# TXT_DELPHI +Would you like to replay this mission? +# TXT_TO_REPLAY +Reconnecting to %s. +# TXT_RECONN_TO +Please wait %02d seconds. +# TXT_PLEASE_WAIT +Do you wish to surrender? +# TXT_SURRENDER +SELECT TRANSMISSION +# TXT_SEL_TRANS +Your game name must be unique. +# TXT_GAMENAME_MUSTBE_UNIQUE +Game is closed. +# TXT_GAME_IS_CLOSED +Your name must be unique. +# TXT_NAME_MUSTBE_UNIQUE +Reconnecting to %s +# TXT_RECONNECTING_TO +Waiting for connections... +# TXT_WAITING_FOR_CONNECTIONS +Time allowed: %02d seconds +# TXT_TIME_ALLOWED +Press ESC to cancel. +# TXT_PRESS_ESC +From Computer: It's just you and me now! +# TXT_JUST_YOU_AND_ME +Capture the Flag: +# TXT_CAPTURE_THE_FLAG_COLON +Special 2 +# TXT_CHAN +%s has allied with %s +# TXT_HAS_ALLIED +%s declares war on %s +# TXT_AT_WAR +Select a target +# TXT_SEL_TARGET +Resign Game +# TXT_RESIGN +Ore grows quickly. +# TXT_TIBERIUM_FAST +Answering... +# TXT_ANSWERING +Initializing Modem... +# TXT_INITIALIZING_MODEM +Scenarios don't match. +# TXT_SCENARIOS_DO_NOT_MATCH +Power Output +# TXT_POWER_OUTPUT +Power Output (low) +# TXT_POWER_OUTPUT_LOW +Continue +# TXT_CONTINUE +Data Queue Overflow +# TXT_QUEUE_FULL +%s changed game options! +# TXT_SPECIAL_WARNING +Please insert a Red Alert CD into the CD-ROM drive. +# TXT_CD_DIALOG_1 +Please insert CD %d (%s) into the CD-ROM drive. +# TXT_CD_DIALOG_2 +Red Alert is unable to detect your CD ROM drive. +# TXT_CD_ERROR1 +No Sound Card Detected +# TXT_NO_SOUND_CARD +UNKNOWN +# TXT_UNKNOWN +(old) +# TXT_OLD_GAME +Insufficient Disk Space to run Red Alert. +# TXT_NO_SPACE +You must have %d megabytes of free disk space. +# TXT_MUST_HAVE_SPACE +Run SETUP program first. +# TXT_RUN_SETUP +Waiting for Opponent +# TXT_WAITING_FOR_OPPONENT +Please select 'Settings' to setup default configuration +# TXT_SELECT_SETTINGS +Prison +# TXT_PRISON +Mission Saved +# TXT_GAME_WAS_SAVED +Insufficient disk space to save a game. Please delete a previous save to free up some disk space and try again. +# TXT_SPACE_CANT_SAVE +Invalid Port/Address. COM 1-4 OR ADDRESS +# TXT_INVALID_PORT_ADDRESS +Invalid Port and/or IRQ settings +# TXT_INVALID_SETTINGS +IRQ already in use +# TXT_IRQ_ALREADY_IN_USE +Abort +# TXT_ABORT +Restart +# TXT_RESTART +Mission is restarting. Please wait... +# TXT_RESTARTING +Mission is loading. Please wait... +# TXT_LOADING +Error in the InitString +# TXT_ERROR_IN_INITSTRING +Shroud: +# TXT_SHADOW_COLON +Anti-Vehicle Mine +# TXT_AVMINE +Anti-Personnel Mine +# TXT_APMINE +New Missions +# TXT_NEW_MISSIONS +Thief +# TXT_THIEF +Radar Jammer +# TXT_MRJ +Gap Generator +# TXT_GAP_GENERATOR +Pillbox +# TXT_PILLBOX +Camo. Pillbox +# TXT_CAMOPILLBOX +Chronosphere +# TXT_CHRONOSPHERE +England +# TXT_ENGLAND +Germany +# TXT_GERMANY +Spain +# TXT_SPAIN +Russia +# TXT_USSR +Ukraine +# TXT_UKRAINE +Greece +# TXT_GREECE +France +# TXT_FRANCE +Turkey +# TXT_TURKEY +Shore +# TXT_SHORE +Select Object +# TXT_PLACE_OBJECT +Submarine +# TXT_SS +Destroyer +# TXT_DD +Cruiser +# TXT_CA +Transport +# TXT_TRANSPORT +Gun Boat +# TXT_PT +Lobby +# TXT_LOBBY +Games +# TXT_CHANNEL_GAMES +Save Game... +# TXT_SAVING_GAME +Game is full. +# TXT_GAME_FULL +You must select a game! +# TXT_MUST_SELECT_GAME +%s playing %s +# TXT_S_PLAYING_S +Only the host can modify this option. +# TXT_ONLY_HOST_CAN_MODIFY +Game was cancelled. +# TXT_GAME_CANCELLED +%s has formed a new game. +# TXT_S_FORMED_NEW_GAME +%s's game is now in progress. +# TXT_GAME_NOW_IN_PROGRESS +Tesla Coil +# TXT_TESLA +Mobile Gap Generator +# TXT_MGG +Flame Tower +# TXT_FLAME_TURRET +AA Gun +# TXT_AAGUN +Kennel +# TXT_KENNEL +Soviet Tech Center +# TXT_SOVIET_TECH +Badger Bomber +# TXT_BADGER +Mig Attack Plane +# TXT_MIG +Yak Attack Plane +# TXT_YAK +Barbed Wire +# TXT_FENCE +Field Medic +# TXT_MEDIC +Saboteur +# TXT_SABOTEUR +General +# TXT_GENERAL +Tanya +# TXT_E7 +Parabombs +# TXT_PARA_BOMB +Paratroopers +# TXT_PARA_INFANTRY +Parachute Saboteur +# TXT_PARA_SABOTEUR +Naval Yard +# TXT_SHIP_YARD +Sub Pen +# TXT_SUB_PEN +Scenario Options +# TXT_SCENARIO_OPTIONS +Spy Plane +# TXT_SPY_MISSION +Spy Plane +# TXT_U2 +Attack Dog +# TXT_GUARD_DOG +Spy Info +# TXT_SPY_INFO +Buildings +# TXT_BUILDNGS +Units +# TXT_UNITS +Infantry +# TXT_INFANTRY +Aircraft +# TXT_AIRCRAFT +Supply Truck +# TXT_TRUCK +Invulnerability Device +# TXT_INVUL +Iron Curtain +# TXT_IRON_CURTAIN +Allied Tech Center +# TXT_ADVANCED_TECH +V2 Rocket +# TXT_V2_LAUNCHER +Forward Command Post +# TXT_FORWARD_COM +Demolitioner +# TXT_DEMOLITIONER +Mine Layer +# TXT_MINE_LAYER +Fake Construction Yard +# TXT_FAKE_CONST +Fake War Factory +# TXT_FAKE_WEAP +Fake Naval Yard +# TXT_FAKE_YARD +Fake Sub Pen +# TXT_FAKE_PEN +Fake Radar Dome +# TXT_FAKE_RADAR +Bigfoot +# TXT_THEME_BIGF +Crush +# TXT_THEME_CRUS +Face the Enemy 1 +# TXT_THEME_FAC1 +Face the Enemy 2 +# TXT_THEME_FAC2 +Hell March +# TXT_THEME_HELL +Run for Your Life +# TXT_THEME_RUN1 +Smash +# TXT_THEME_SMSH +Trenches +# TXT_THEME_TREN +Workmen +# TXT_THEME_WORK +Await +# TXT_THEME_AWAIT +Dense +# TXT_THEME_DENSE_R +Map Selection +# TXT_THEME_MAP +Fogger +# TXT_THEME_FOGGER1A +Mud +# TXT_THEME_MUD1A +Radio 2 +# TXT_THEME_RADIO2 +Roll Out +# TXT_THEME_ROLLOUT +Snake +# TXT_THEME_SNAKE +Terminate +# TXT_THEME_TERMINAT +Twin +# TXT_THEME_TWIN +Vector +# TXT_THEME_VECTOR1A +Team Members +# TXT_TEAM_MEMBERS +Bridge +# TXT_BRIDGE +Barrel +# TXT_BARREL +Friendly +# TXT_GOODGUY +Enemy +# TXT_BADGUY +Gold +# TXT_GOLD +Gems +# TXT_GEMS +Title Movie +# TXT_TEASER +Movies +# TXT_MOVIES +Interior +# TXT_INTERIOR +Sonar Pulse +# TXT_SONAR_PULSE +Missile Silo +# TXT_MSLO +GPS Satellite +# TXT_GPS_SATELLITE +Atom Bomb +# TXT_NUCLEAR_BOMB +Easy +# TXT_EASY +Hard +# TXT_HARD +Normal +# TXT_NORMAL +Please set the difficulty level. It will be used throughout this campaign. +# TXT_DIFFICULTY +Allies +# TXT_ALLIES +Soviet +# TXT_SOVIET +Intro Theme +# TXT_THEME_INTRO +Shroud Regrows +# TXT_SHADOW_REGROWS +Ore Regenerates +# TXT_ORE_SPREADS +Score Theme +# TXT_THEME_SCORE +Internet Game +# TXT_INTERNET +Ice +# TXT_ICE +Crates +# TXT_CRATE +Skirmish +# TXT_SKIRMISH +Choose your side. +# TXT_CHOOSE +Valuable Minerals +# TXT_MINERALS +Ignore +# TXT_IGNORE +Error - modem is not responding. +# TXT_ERROR_NO_RESP +Error - modem did not respond to result code enable command. +# TXT_ERROR_NO_RESCODE +Error - modem did not respond to initialisation string. +# TXT_ERROR_NO_INIT +Error - modem did not respond to 'verbose' command. +# TXT_ERROR_NO_VERB +Error - modem did not respond to 'echo' command. +# TXT_ERROR_NO_ECHO +Error - unable to disable modem auto answer. +# TXT_ERROR_NO_DISABLE +Error - Too many errors initialising modem - Aborting. +# TXT_ERROR_TOO_MANY +Error - Modem returned error status. +# TXT_ERROR_ERROR +Error - TIme out waiting for connect. +# TXT_ERROR_TIMEOUT +Accomplished +# TXT_ACCOMPLISHED +Click to Continue +# TXT_CLICK_CONTINUE +Receiving scenario from host. +# TXT_RECEIVING_SCENARIO +Sending scenario to remote players. +# TXT_SENDING_SCENARIO +Error - Modem failed to respond to flow control command. Your Windows configuration may be incorrect. +# TXT_NO_FLOW_CONTROL_RESPONSE +Error - Modem failed to respond to compression command. Your Windows configuration may be incorrect. +# TXT_NO_COMPRESSION_RESPONSE +Error - Modem failed to respond to error correction command. Your Windows configuration may be incorrect. +# TXT_NO_ERROR_CORRECTION_RESPONSE +To play Red Alert via the internet you must be connected to an internet services provider and be registered with Planet Westwood +# TXT_EXPLAIN_REGISTRATION +Wchat not installed. Please install it from either CD. +# TXT_ERROR_UNABLE_TO_RUN_WCHAT +Register +# TXT_REGISTER +Ore Mine +# TXT_ORE_MINE +No registered modem +# TXT_NO_REGISTERED_MODEM +Chronoshift +# TXT_CHRONOSHIFT +Invalid Port or Port is in use +# TXT_UNABLE_TO_OPEN_PORT +No dial tone. Ensure your modem is connected to the phone line and try again. +# TXT_NO_DIAL_TONE +Error - other player does not have this expansion scenario. +# TXT_NO_EXPANSION_SCENARIO +Please Stand By... +# TXT_STAND_BY +End Credits Theme +# TXT_THEME_CREDITS +Low Power: AA-Guns offline +# TXT_POWER_AAGUN +Low Power: Tesla Coils offline +# TXT_POWER_TESLA +Low Power +# TXT_LOW_POWER +Commander: +# TXT_COMMANDER +Battles Won: +# TXT_BATTLES_WON +Game versions incompatible. To make sure you have the latest version, visit www.westwood.com +# TXT_MISMATCH +Incompatible scenario file detected. The scenario may be corrupt. +# TXT_SCENARIO_ERROR +Connecting... +# TXT_CONNECTING +Modem Initialization +# TXT_MODEM_INITIALISATION +Data Compression +# TXT_DATA_COMPRESSION +Error Correction +# TXT_ERROR_CORRECTION +Hardware Flow Control +# TXT_HARDWARE_FLOW_CONTROL +Advanced +# TXT_ADVANCED +2nd_Hand +# TXT_THEME_2ND_HAND +Arazoid +# TXT_THEME_ARAZOID +BackStab +# TXT_THEME_BACKSTAB +Chaos2 +# TXT_THEME_CHAOS2 +Shut_It +# TXT_THEME_SHUT_IT +TwinMix1 +# TXT_THEME_TWINMIX1 +Under3 +# TXT_THEME_UNDER3 +VR2 +# TXT_THEME_VR2 +The other system is not responding. Do you wish to attempt an emergency game save? Both players must save for this to work. +# TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING +The other system hung up. Do you wish to attempt an emergency game save? Both players must save for this to work. +# TXT_ASK_EMERGENCY_SAVE_HUNG_UP +Red Alert was unable to run the registration software. You need to install Westwood Chat from the Red Alert CD to register. +# TXT_NO_REG_APP +A player in the game does not have this expansion scenario. +# TXT_NO_CS_SCENARIOS +Missile Sub +# TXT_MISSILESUB +Shock Trooper +# TXT_SHOCKTROOPER +Mechanic +# TXT_MECHANIC +Chrono Tank +# TXT_CHRONOTANK +Tesla Tank +# TXT_TESLATANK +M.A.D. Tank +# TXT_MAD +Demolition Truck +# TXT_DEMOTRUCK +Phase Transport +# TXT_PHASETRANSPORT +Bog +# TXT_THEME_BOG +Floating +# TXT_THEME_FLOAT_V2 +Gloom +# TXT_THEME_GLOOM +Ground Wire +# TXT_THEME_GRNDWIRE +Mech Man 2 +# TXT_THEME_RPT +Search +# TXT_THEME_SEARCH +Traction +# TXT_THEME_TRACTION +Wasteland +# TXT_THEME_WASTELND +Helicarrier +# TXT_CARRIER diff --git a/CODE/ENG/CREDITS.BAK b/CODE/ENG/CREDITS.BAK new file mode 100644 index 0000000..1fbcb4c --- /dev/null +++ b/CODE/ENG/CREDITS.BAK @@ -0,0 +1,192 @@ + Executive Producer Brett W. Sperry + Producer Ed Del Castillo + + Original Concept Brett W. Sperry + Joe Bostic + + Original Story Ron Smith + Ed Del Castillo + + Lead Game Programmers Joe Bostic + Barry Green + Steve Tall + + Game Programmers Bill Randolph + Phil Gorrow + Maria del Mar McCready Legg + + Internet Programmers Jeff Brown + David Aldridge + Matt Thorn + + Translation Programmer Victor Grippi + + Technical Direction Steve Wetherill + Eric Wang + + Designers Adam Isgreen + Michael Lightner + Erik Yeo + + Lead Artists Chris Demers + Matthew Hansel + Joseph B. Hewitt IV + + Artists David T. Potter + Damon Redmond + Paul Wesberry + Brian White + + Screenplay Ron Smith + Adam Isgreen + John Scott Lewinski + + Video Post Production Felix Kupis + Kevin Becquet + + Movie Compression Director Tim C. Fritz + Compression Assistant Patrick Connelly + + Audio Direction Paul S. Mudra + Soundtrack Frank Klepacki + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance James Adkins + John Archer + Lloyd Bell + Chris Bleven + D'Andre Cambell + Errol Campbell + Phillip Castro + Steve Corcoran + Kenny Dunne + Randy Greenback + Abe Hernandez + Chris Holloway + Troy Leonard + Ben Lublin + Levi Luke + Isaiah Myers + Richard Rasmussen + Chris Rubyor + Robert Scheel + Albert Springfield + Mike Smith + Tyler Thackery + + Box Design Matthew Hansel + Thomas Puckett inc. + + Manual Design Victoria Hart + + + Special thanks to all our friends and supporters + within the Virgin empire. Additional thanks to + Tracy Chapman for a new beginning and Greg + Hjelstrom for the amazing chronal vortex. + + + + + The Cast + + Allied Side + Von Esling Arthur Roberts + Stavros Barry Kramer + Albert Einstein John Milford + Tanya Lynne Litteer + Interrogator Dom Magwili + Announcer Gwen Castaldi + + + Allied Soldiers + Finale Ricky Russell + Nick Paulos + Scott Ryan Talley + + Commanders Joe Bostic + Chris Demers + Barry Green + Matthew Hansel + Adam Isgreen + Frank Klepacki + Mike Lightner + Bill Randolph + Philip E. Shelburne + Eric Wang + + + + Soviet Side + Josef Stalin Eugene Dynarski + Kukov Craig Cavanah + Gradenko Alan Terry + Nadia Andrea Robinson + Kane Joe Kucan + + + Soviet Soldiers John Archer + Mike Grayford + Frank Klepacki + Felix Kupis + Chris Rubyor + Erik Yeo + + + + Ingame Voices + E.V.A. Martin Alper + Tanya Lanae Freeborn + Infantry & Units Mike Grayford + Adam Isgreen + Frank Klepacki + Troy Leonard + Dwight Okahara + Chris Rubyor + Eric Wang + Erik Yeo + + + + + Production Crew + + Directed By Joseph D. Kucan + + Costume Design Christie Moeller + + Make-up & Hair Cindy Cline + + Sound Engineer Paul S. Mudra + + Casting Marilee Lear, C.S.A. + + Video Kevin Becquet + + Lighting Eric Gooch + + Lead Production Assistant Paul Bastardo + + Production Assistants Rick Appin + Patience Becquet + Pat Connelly + Jeff Fillhaber + Richard Rasmussen + + Sfx Make-up Philip E. Shelburne + + Special Footage Courtesy Of NBC News Archives Films + Fabulous Footage + + + + + For the latest news, goodies, and updates, + visit our web site at www.westwood.com + + + + + diff --git a/CODE/ENG/CREDITS.TXT b/CODE/ENG/CREDITS.TXT new file mode 100644 index 0000000..b4b9385 --- /dev/null +++ b/CODE/ENG/CREDITS.TXT @@ -0,0 +1,349 @@ + Aftermath Credits + + Executive Producer Brett W. Sperry + + Producer Lewis S. Peterson + + Programmer Barry Green + + Technical Direction Steve Wetherill, Eric Wang + + Solo Play Map Designers: + John Archer, Patrick Connelly, + Adam Isgreen, Michael Lightner, + Erik Yeo + + Multiplay Map Designers: + John Archer, Stuart Bailey, + Nigel Berryman, D'Andre Campbell, + Valerie Carpentier, Cathi Diet, + Keith Ditchburn, Gareth Eke, + Darren Esp, Simon Evers, Rod Gray, + Randy Greenback, Matthew Howe, + Levi Luke, Suzanne Maddison, + Iain McNeil, Graeme Miller, + Lee Morse, David Parsons, + Alex Scarrow, Robert Scheel, + Lawrence So, John Sweeney, + Matthew Tillett, Phillip Veale + + Artists Chris Demers, Matthew Hansel, + Joseph B. Hewitt IV + + Audio Direction Paul S. Mudra + + Soundtrack Frank Klepacki + + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance + Jim Adkins, Lloyd Bell, + Chris Blevens, D'Andre Campbell, + Errol Campbell, Shane Dietrich, + Randy Greenback, Jon Hix, Mark Laity, + Troy Leonard, DeMarlo Lewis, + Levi Luke, Patrick Offord, + Richard Rasmussen, Mike Ruppert, + Steve Shockey, Mike Smith, + Albert Springfield + + Ingame Voices Jeff Fillhaber, Michael Smith + + + + + + Red Alert Credits + + Executive Producer Brett W. Sperry + Producer Ed Del Castillo + + Original Concept Brett W. Sperry + Joe Bostic + + Original Story Ron Smith + Ed Del Castillo + + Lead Game Programmers Joe Bostic + Barry Green + Steve Tall + + Game Programmers Bill Randolph + Phil Gorrow + Maria del Mar McCready Legg + + Internet Programmers Jeff Brown + David Aldridge + Matt Thorn + + Translation Programmer Victor Grippi + + Technical Direction Steve Wetherill + Eric Wang + + Designers Adam Isgreen + Michael Lightner + Erik Yeo + + Lead Artists Chris Demers + Matthew Hansel + Joseph B. Hewitt IV + + Artists David T. Potter + Damon Redmond + Paul Wesberry + Brian White + + Screenplay Ron Smith + Adam Isgreen + John Scott Lewinski + + Video Post Production Felix Kupis + Kevin Becquet + + Movie Compression Director Tim C. Fritz + Compression Assistant Patrick Connelly + + Audio Direction Paul S. Mudra + Soundtrack Frank Klepacki + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance James Adkins + John Archer + Lloyd Bell + Chris Bleven + D'Andre Cambell + Errol Campbell + Phillip Castro + Steve Corcoran + Kenny Dunne + Randy Greenback + Abe Hernandez + Chris Holloway + Troy Leonard + Ben Lublin + Levi Luke + Isaiah Myers + Richard Rasmussen + Chris Rubyor + Robert Scheel + Albert Springfield + Mike Smith + Tyler Thackery + + Box Design Matthew Hansel + Thomas Puckett Inc. + + Manual Design Victoria Hart + + + Special thanks to all our friends and supporters + within the Virgin empire. Additional thanks to + Tracy Chapman for a new beginning and Greg + Hjelstrom for the amazing chronal vortex. + + + + + The Cast + + Allied Side + Von Esling Arthur Roberts + Stavros Barry Kramer + Albert Einstein John Milford + Tanya Lynne Litteer + Interrogator Dom Magwili + Announcer Gwen Castaldi + + + Allied Soldiers + Finale Ricky Russell + Nick Paulos + Scott Ryan Talley + + Commanders Joe Bostic + Chris Demers + Barry Green + Matthew Hansel + Adam Isgreen + Frank Klepacki + Mike Lightner + Bill Randolph + Philip E. Shelburne + Eric Wang + + + + Soviet Side + Josef Stalin Eugene Dynarski + Kukov Craig Cavanah + Gradenko Alan Terry + Nadia Andrea Robinson + Kane Joe Kucan + + + Soviet Soldiers John Archer + Mike Grayford + Frank Klepacki + Felix Kupis + Chris Rubyor + Erik Yeo + + + + Ingame Voices + E.V.A. Martin Alper + Tanya Lanae Freeborn + Infantry & Units Mike Grayford + Adam Isgreen + Frank Klepacki + Troy Leonard + Dwight Okahara + Chris Rubyor + Eric Wang + Erik Yeo + + + + + Production Crew + + Directed By Joseph D. Kucan + + Costume Design Christie Moeller + + Make-up & Hair Cindy Cline + + Sound Engineer Paul S. Mudra + + Casting Marilee Lear, C.S.A. + + Video Kevin Becquet + + Lighting Eric Gooch + + Lead Production Assistant Paul Bastardo + + Production Assistants Rick Appin + Patience Becquet + Pat Connelly + Jeff Fillhaber + Richard Rasmussen + + Sfx Make-up Philip E. Shelburne + + Special Footage Courtesy Of NBC News Archives Films + Fabulous Footage + + + + + + + + + + Counterstrike Credits + + Executive Producer Brett W. Sperry + Producer Ed Del Castillo + + Programmers Victor Grippi + Joe Bostic + Steve Tall + Maria del Mar McCready Legg + + Technical Direction Steve Wetherill + Eric Wang + + Solo Play Map Designers Adam Isgreen + Michael Lightner + Patrick Connelly + + Multiplay Map Designers Iain McNeil + Alex Scarrow + Graeme Miller + Nigel Berryman + Rod Gray + Stuart Bailey + Mat Tillett + Phil Veale + Dave Parsons + Darren Esp + Cameron Strutt + Paul Goodwin + John McNeil + Lee Morse + Adam Isgreen + Michael Lightner + John Archer + Patrick Connelly + Lloyd Bell + D'Andre Campbell + Albert Springfield + Levi Luke + Robert Scheel + + Artists Chris Demers + Matthew Hansel + Shelly Johnson + Frank Mendeola + + Video Kevin Becquet + Patience Becquet + + Audio Direction Paul S. Mudra + + Soundtrack Frank Klepacki + + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance John Archer + Mike Smith + Lloyd Bell + Troy Leonard + Chris Halloway + Randy Greenback + D'Andre Cambell + Errol Campbell + Albert Springfield + James Adkins + Chris Blevens + Levi Luke + Steve Shockey + Isaiah Myers + Ben Lublin + Richard Rasmussen + Robert Scheel + Tom Andrulis + Shane Dietrich + Brandon Hansen + + + + Thanks to Westwood's killer marketing staff for + all their support, Intelligent Games for creating + a bulk of the multiplayer maps, and Brett and + Louis for being born. + + Special thanks to the European Marketing teams + for their support, and to the original Red Alert + team for giving us something to build on. + + + + + For the latest news, goodies, and updates, + visit our web site at www.westwood.com + + + + + + diff --git a/CODE/ENG/DEBUG.BAK b/CODE/ENG/DEBUG.BAK new file mode 100644 index 0000000..02f24cd --- /dev/null +++ b/CODE/ENG/DEBUG.BAK @@ -0,0 +1,278 @@ +Clear the map +# TXT_CLEAR_MAP +Inherit previous map +# TXT_INHERIT_MAP +Select Special Options +# TXT_SPECIAL_OPTIONS +Targeting flash visible to all. +# TXT_VISIBLE_TARGET +Allow targeting of trees. +# TXT_TREE_TARGET +Allow undeploy of construction yard. +# TXT_MCV_DEPLOY +Employ smarter self defense logic. +# TXT_SMART_DEFENCE +Moderate production speed. +# TXT_SLOW_BUILD +Use three point turn logic. +# TXT_THREE_POINT +Ore will grow. +# TXT_TIBERIUM_GROWTH +Ore will spread. +# TXT_TIBERIUM_SPREAD +Disable building "bib" pieces. +# TXT_ROAD_PIECES +Allow running from immediate threats. +# TXT_SCATTER +Show true object names. +# TXT_SHOW_NAMES +Defender has the advantage. +# TXT_DEFENDER_ADVANTAGE +Allow separate helipad purchase +# TXT_SEPARATE_HELIPAD +Password Request +# TXT_PASSWORD_CAPTION +Enter Red Alert access code to gain access. +# TXT_PASSWORD_MESSAGE +Access code error detected. +# TXT_PASSWORD_ERROR +Try Again +# TXT_TRY_AGAIN +Friendly units avoid friendly mines +# TXT_MINE_AWARE +Trigger Editor +# TXT_TRIGGER_EDITOR +Just This Event +# TXT_TRIGGER_JUST_EVENT +... and ... +# TXT_TRIGGER_AND +... or ... +# TXT_TRIGGER_OR +... linked ... +# TXT_TRIGGER_LINKED +Just This Action +# TXT_TRIGGER_JUST_ACTION +Team Editor +# TXT_TEAM_EDIT +Sellable +# TXT_SELLABLE +Rebuild +# TXT_REBUILD +Building constructin time accelerated? +# TXT_SPEED_BUILD +Scenario authentication code missing or invalid. Re-save scenario to correct it. +# TXT_SCENARIO_ERROR +ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄÂCells:ÂTarg:ÄÂS-Bar:ÂPaths:ÄÄÄÄÂSprung Triggers:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃG-Time:ÄÅÄÄÄÄ´ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÙ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃObject Count:´ ³ ³ ³ ³ ³ ³ ³ +³ -Units ³ ³ ³ ³ ³ ³ ³ ³ +³ -Infantry ÃCPU Workload:ÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ -Aircraft ³ ³ +³ -Ships ³ ³ +³ -Buildings³ ³ +³ -Terrain ³ ³ +³ -Bullets ³ ³ +³ -Anims ³ ³ +³ -Teams ³ ³ +³ -Triggers ³ ³ +³ -TrigTypes³ ³ +³ -Factories³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Stress Tracking F12 -> +# TXT_DEBUG_STRESS +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃGems:ÂGold:ÂÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÁÄÄÄÄÄÁÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Is Dumping ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³Teleported ³ +³To Display ³Useless ³Locked ³AI aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Vehicle F12 -> +# TXT_DEBUG_VEHICLE +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃDoing:ÂFear:ÂÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÁÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Technician ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³Stoked ³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³Prone ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Infantry F12 -> +# TXT_DEBUG_INFANTRY +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Repairing ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³ToSelfRepair³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Ship F12 -> +# TXT_DEBUG_SHIP +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃBuilding:ÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÁÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Reparing ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³To Rebuild ³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³To Sell ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³Charging ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³Charged ³ +³To Look ³Unloading ³Formation ³ ³ ³Captured ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´Jamming ³ +³ ³Jammed ³ +³ ³ ³ +³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Building F12 -> +# TXT_DEBUG_BUILDING + Game Objects³ Drawing ³System ³Map Display ³ ³ Game Init ³ +ÕFind-Path:ÍÍØCell:ÍÍÍÍÍÍÍØPalette:ÍÍÍÍØFull ProcessØÍÍÍÍÍÍÍÍÍÍÍÍØRULES.INI:Í͵ +³ ³ ³ ³ ³ ³ ³ +ÃTarget Scan:ÅObjects:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅSidebar:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅSCENARIO.INI´ +³ ³ ³ ³ ³ ³ ³ +ÃAI:ÄÄÄÄÄÄÄÄÄÅAnims:ÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅRadar:ÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃPer-Cell-AIÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅTactical:ÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Object:ÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅPower Bar:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Cell:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅShroud:ÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Wall:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅTabs:ÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅDirectX BlitÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Performance Benchmarks F12 -> +# TXT_DEBUG_PERFORMANCE +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃAttk:ÂÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ +ÃÄÄÄÄÄÁÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³ ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³ ³ +³To Display ³Useless ³Locked ³AI aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Aircraft F12 -> +# TXT_DEBUG_AIRCRAFT +ÚFull Name:ÄÄÄÄÄÄÄÄÂAct Like:ÄÄÄÄÄÄÄÄÂTech:ÂDiff:ÂState:ÂBlock:ÂIQ:ÄÄÂRepair:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃAScan:ÄÄÂAir:ÄÄÄÄÂQ-AirÄÄÂCredits:ÂPower:ÄÅR-Spy:ÂScore:ÄÄÄÂTeam:ÄÄÄÅAlert:ÄÄ´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃBScan:ÄÄÅBuildingÅQ-BldÄÄÅOre:ÄÄÄÄÅDrain:ÄÅP-Targ:ÄÄÄÄÄÄÄÄÄÅTrigger:ÅBorrow:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃUScan:ÄÄÅUnit:ÄÄÄÅQ-UnitÄÅICreds:ÄÅU-Lost:ÅAllies:ÄÂÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÅAttack:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃIScan:ÄÄÅInfantryÅQ-InfÄÄÅCapacityÅB-Lost:ÅRadius:Ä´ ÃAI-Time:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃVScan:ÄÄÅVessel:ÄÅQ-ShipÄÅNorth:ÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÅCenter Cell:ÄÄÄÄÅDamage:Ä´ +³ ³ ³ ³ ³ ³ ³ +ÃWest:ÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÅCore:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅEast:ÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃGlobals:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅSouth:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅEnemy:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Active ³Defeated ³Visionary ³Resigned ³ ³ ³ +³Is Human ³To Die ³Low Ore ³Gave Up ³ ³ ³ +³Player Ctrl ³To Win ³Spied ³Built Smthng³ ³ ³ +³Autocreate ³To Lose ³Thieved ³Auto Base AI³ ³ ³ +³Discovered ³Civ. Evac ³GPS Active ³ ³ ³ ³ +³Max Capacity³Recalc Flag ³Production ³ ³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 House F12 -> +# TXT_DEBUG_HOUSE +*******************************\r** Pentium CPU not detected. **\r******************************* +# TXT_NO_PENTIUM +Size Map +# TXT_SIZE_MAP +Trucks drop crate when destroyed? +# TXT_TRUCK_CRATE diff --git a/CODE/ENG/DEBUG.TXT b/CODE/ENG/DEBUG.TXT new file mode 100644 index 0000000..6d71b78 --- /dev/null +++ b/CODE/ENG/DEBUG.TXT @@ -0,0 +1,278 @@ +Clear the map +# TXT_CLEAR_MAP +Inherit previous map +# TXT_INHERIT_MAP +Select Special Options +# TXT_SPECIAL_OPTIONS +Targeting flash visible to all. +# TXT_VISIBLE_TARGET +Allow targeting of trees. +# TXT_TREE_TARGET +Allow undeploy of construction yard. +# TXT_MCV_DEPLOY +Employ smarter self defense logic. +# TXT_SMART_DEFENCE +Moderate production speed. +# TXT_SLOW_BUILD +Use three point turn logic. +# TXT_THREE_POINT +Ore will grow. +# TXT_TIBERIUM_GROWTH +Ore will spread. +# TXT_TIBERIUM_SPREAD +Disable building "bib" pieces. +# TXT_ROAD_PIECES +Allow running from immediate threats. +# TXT_SCATTER +Show true object names. +# TXT_SHOW_NAMES +Defender has the advantage. +# TXT_DEFENDER_ADVANTAGE +Allow separate helipad purchase +# TXT_SEPARATE_HELIPAD +Password Request +# TXT_PASSWORD_CAPTION +Enter Red Alert access code to gain access. +# TXT_PASSWORD_MESSAGE +Access code error detected. +# TXT_PASSWORD_ERROR +Try Again +# TXT_TRY_AGAIN +Friendly units avoid friendly mines +# TXT_MINE_AWARE +Trigger Editor +# TXT_TRIGGER_EDITOR +Just This Event +# TXT_TRIGGER_JUST_EVENT +... and ... +# TXT_TRIGGER_AND +... or ... +# TXT_TRIGGER_OR +... linked ... +# TXT_TRIGGER_LINKED +Just This Action +# TXT_TRIGGER_JUST_ACTION +Team Editor +# TXT_TEAM_EDIT +Sellable +# TXT_SELLABLE +Rebuild +# TXT_REBUILD +Building constructin time accelerated? +# TXT_SPEED_BUILD +Scenario authentication code missing or invalid. Re-save scenario to correct it. +# TXT_SCENARIO_ERRORx +ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄÂCells:ÂTarg:ÄÂS-Bar:ÂPaths:ÄÄÄÄÂSprung Triggers:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃG-Time:ÄÅÄÄÄÄ´ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÙ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃObject Count:´ ³ ³ ³ ³ ³ ³ ³ +³ -Units ³ ³ ³ ³ ³ ³ ³ ³ +³ -Infantry ÃCPU Workload:ÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ -Aircraft ³ ³ +³ -Ships ³ ³ +³ -Buildings³ ³ +³ -Terrain ³ ³ +³ -Bullets ³ ³ +³ -Anims ³ ³ +³ -Teams ³ ³ +³ -Triggers ³ ³ +³ -TrigTypes³ ³ +³ -Factories³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Stress Tracking F12 -> +# TXT_DEBUG_STRESS +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃGems:ÂGold:ÂÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÁÄÄÄÄÄÁÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Is Dumping ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³Teleported ³ +³To Display ³Useless ³Locked ³AI aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Vehicle F12 -> +# TXT_DEBUG_VEHICLE +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃDoing:ÂFear:ÂÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÁÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Technician ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³Stoked ³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³Prone ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Infantry F12 -> +# TXT_DEBUG_INFANTRY +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Repairing ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³ToSelfRepair³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Ship F12 -> +# TXT_DEBUG_SHIP +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃBuilding:ÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÁÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³Reparing ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³To Rebuild ³ +³To Display ³Useless ³Locked ³AI Aware ³Firing ³To Sell ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³Charging ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³Charged ³ +³To Look ³Unloading ³Formation ³ ³ ³Captured ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´Jamming ³ +³ ³Jammed ³ +³ ³ ³ +³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Building F12 -> +# TXT_DEBUG_BUILDING + Game Objects³ Drawing ³System ³Map Display ³ ³ Game Init ³ +ÕFind-Path:ÍÍØCell:ÍÍÍÍÍÍÍØPalette:ÍÍÍÍØFull ProcessØÍÍÍÍÍÍÍÍÍÍÍÍØRULES.INI:Í͵ +³ ³ ³ ³ ³ ³ ³ +ÃTarget Scan:ÅObjects:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅSidebar:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅSCENARIO.INI´ +³ ³ ³ ³ ³ ³ ³ +ÃAI:ÄÄÄÄÄÄÄÄÄÅAnims:ÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅRadar:ÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃPer-Cell-AIÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅTactical:ÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Object:ÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅPower Bar:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Cell:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅShroud:ÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃEval Wall:ÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅTabs:ÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅDirectX BlitÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Performance Benchmarks F12 -> +# TXT_DEBUG_PERFORMANCE +ÚFull Name:ÄÄÄÄÄÄÄÄÂCoord:ÄÄÂHgt:ÂStr:ÂAce:ÂSpd:ÂAmmoÂArm:ÂGrp:ÂCloak:ÂPrice:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃOwner:ÄÄÄÄÄÄÄÄÄÄÄÄÅTrigger:ÅTarCom:ÄÂSTarCom:ÂPFace:ÅPath:ÄÄÄÄÁÄÄÂTry:ÂDelay:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃCargo:ÄÄÄÂTarget:ÄÅNext:ÄÄÄÅNavCom:ÄÅSNavCom:ÅSFace:ÅPBrk:ÂHeadTo:ÄÂArchive:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃTmr:ÂSt:ÂRt:ÂStageÅRadio:ÄÄÅRadio Message History:ÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ ³ ³ ³ +ÃMission:ÁÄÄÄÁÄÂQueue:ÄÄÄÄÄÄ´ ³ +³ ³ ³ ³ +ÃAttk:ÂÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ +ÃÄÄÄÄÄÁÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Down ³AnimAttached³Primary ³Player Owned³Initiated ³ ³ +³To Damage ³Falling [ ]³Loaner ³Player Aware³Deploying ³ ³ +³To Display ³Useless ³Locked ³AI aware ³Firing ³ ³ +³In Limbo ³Ticked Off ³Recoil ³Lemon ³Rotating ³ ³ +³Selected ³Cloakable ³Tethered ³Invul [ ]³Driving ³ ³ +³To Look ³Unloading ³Formation ³ ³ ³ ³ +ÆTeam:ÍÍÍÑMmbrs:ÑQnt:ÑCenter:ÍÑTarget:ÍØFormation:ÍÍØÍÍÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍ͵ +³ ³ ³ ³ ³ ³ ³Understrngth³Moving ³ +ÃMission:ÁÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄ´FullStrength³ForcedActive³ +³ ³ ³HasBeen ³Reforming ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 Aircraft F12 -> +# TXT_DEBUG_AIRCRAFT +ÚFull Name:ÄÄÄÄÄÄÄÄÂAct Like:ÄÄÄÄÄÄÄÄÂTech:ÂDiff:ÂState:ÂBlock:ÂIQ:ÄÄÂRepair:Ä¿ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃAScan:ÄÄÂAir:ÄÄÄÄÂQ-AirÄÄÂCredits:ÂPower:ÄÅR-Spy:ÂScore:ÄÄÄÂTeam:ÄÄÄÅAlert:ÄÄ´ +³ ³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃBScan:ÄÄÅBuildingÅQ-BldÄÄÅOre:ÄÄÄÄÅDrain:ÄÅP-Targ:ÄÄÄÄÄÄÄÄÄÅTrigger:ÅBorrow:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃUScan:ÄÄÅUnit:ÄÄÄÅQ-UnitÄÅICreds:ÄÅU-Lost:ÅAllies:ÄÂÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÅAttack:Ä´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃIScan:ÄÄÅInfantryÅQ-InfÄÄÅCapacityÅB-Lost:ÅRadius:Ä´ ÃAI-Time:´ +³ ³ ³ ³ ³ ³ ³ ³ ³ +ÃVScan:ÄÄÅVessel:ÄÅQ-ShipÄÅNorth:ÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÅCenter Cell:ÄÄÄÄÅDamage:Ä´ +³ ³ ³ ³ ³ ³ ³ +ÃWest:ÄÄÄÁÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÅCore:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅEast:ÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃGlobals:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅSouth:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅEnemy:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ +³ ³ ³ ³ +ÃÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄ´ +³Is Active ³Defeated ³Visionary ³Resigned ³ ³ ³ +³Is Human ³To Die ³Low Ore ³Gave Up ³ ³ ³ +³Player Ctrl ³To Win ³Spied ³Built Smthng³ ³ ³ +³Autocreate ³To Lose ³Thieved ³Auto Base AI³ ³ ³ +³Discovered ³Civ. Evac ³GPS Active ³ ³ ³ ³ +³Max Capacity³Recalc Flag ³Production ³ ³ ³ ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÙ +<- F11 House F12 -> +# TXT_DEBUG_HOUSE +*******************************\r** Pentium CPU not detected. **\r******************************* +# TXT_NO_PENTIUM +Size Map +# TXT_SIZE_MAP +Trucks drop crate when destroyed? +# TXT_TRUCK_CRATE diff --git a/CODE/ENG/ENG/CONQUER.TXT b/CODE/ENG/ENG/CONQUER.TXT new file mode 100644 index 0000000..bc3e22b --- /dev/null +++ b/CODE/ENG/ENG/CONQUER.TXT @@ -0,0 +1,1109 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Time:%02d:%02d:%02d +# TXT_TIME_FORMAT_HOURS +Time:%02d:%02d +# TXT_TIME_FORMAT_NO_HOURS +Sell +# TXT_BUTTON_SELL +Sell Structure +# TXT_SELL +Repair +# TXT_BUTTON_REPAIR +You: +# TXT_YOU +Enemy: +# TXT_ENEMY +Buildings Destroyed By +# TXT_BUILD_DEST +Units Destroyed By +# TXT_UNIT_DEST +Ore Harvested By +# TXT_TIB_HARV +Score: %d +# TXT_SCORE_1 +Yes +# TXT_YES +No +# TXT_NO +Mission Accomplished +# TXT_SCENARIO_WON +Mission Failed +# TXT_SCENARIO_LOST +Start New Game +# TXT_START_NEW_GAME +Intro & Sneak Peek +# TXT_INTRO +Cancel +# TXT_CANCEL +Rock +# TXT_ROCK +Civilian +# TXT_CIVILIAN +Containment Team +# TXT_JP +OK +# TXT_OK +Tree +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Clear +# TXT_CLEAR +Water +# TXT_WATER +Road +# TXT_ROAD +Slope +# TXT_SLOPE +Patch +# TXT_PATCH +River +# TXT_RIVER +Load Mission +# TXT_LOAD_MISSION +Save Mission +# TXT_SAVE_MISSION +Delete Mission +# TXT_DELETE_MISSION +Load +# TXT_LOAD_BUTTON +Save +# TXT_SAVE_BUTTON +Delete +# TXT_DELETE_BUTTON +Game Controls +# TXT_GAME_CONTROLS +Sound Controls +# TXT_SOUND_CONTROLS +Resume Mission +# TXT_RESUME_MISSION +Visual Controls +# TXT_VISUAL_CONTROLS +Abort Mission +# TXT_QUIT_MISSION +Exit Game +# TXT_EXIT_GAME +Options +# TXT_OPTIONS +Squish mark +# TXT_SQUISH +Crater +# TXT_CRATER +Scorch Mark +# TXT_SCORCH +BRIGHTNESS: +# TXT_BRIGHTNESS +Music Volume +# TXT_MUSIC +Sound Volume +# TXT_VOLUME +Tint: +# TXT_TINT +Contrast: +# TXT_CONTRAST +Game Speed: +# TXT_SPEED +Scroll Rate: +# TXT_SCROLLRATE +Color: +# TXT_COLOR +Return to game +# TXT_RETURN_TO_GAME +Enemy Soldier +# TXT_ENEMY_SOLDIER +Enemy Vehicle +# TXT_ENEMY_VEHICLE +Enemy Structure +# TXT_ENEMY_STRUCTURE +Light Tank +# TXT_LTANK +Heavy Tank +# TXT_MTANK +Medium Tank +# TXT_MTANK2 +Mammoth Tank +# TXT_HTANK +SAM Site +# TXT_SAM +Ranger +# TXT_JEEP +Chinook Helicopter +# TXT_TRANS +Ore Truck +# TXT_HARVESTER +Artillery +# TXT_ARTY +Rifle Infantry +# TXT_E1 +Grenadier +# TXT_E2 +Rocket Soldier +# TXT_E3 +Flamethrower +# TXT_E4 +Longbow Helicopter +# TXT_HELI +Hind +# TXT_ORCA +APC +# TXT_APC +Guard Tower +# TXT_GUARD_TOWER +Radar Dome +# TXT_COMMAND +Helipad +# TXT_HELIPAD +Airfield +# TXT_AIRSTRIP +Ore Silo +# TXT_STORAGE +Construction Yard +# TXT_CONST_YARD +Ore Refinery +# TXT_REFINERY +Church +# TXT_CIV1 +Han's and Gretel's +# TXT_CIV2 +Hewitt's Manor +# TXT_CIV3 +Ricktor's House +# TXT_CIV4 +Gretchin's House +# TXT_CIV5 +The Barn +# TXT_CIV6 +Damon's pub +# TXT_CIV7 +Fran's House +# TXT_CIV8 +Music Factory +# TXT_CIV9 +Toymaker's +# TXT_CIV10 +Ludwig's House +# TXT_CIV11 +Haystacks +# TXT_CIV12 +Haystack +# TXT_CIV13 +Wheat Field +# TXT_CIV14 +Fallow Field +# TXT_CIV15 +Corn Field +# TXT_CIV16 +Celery Field +# TXT_CIV17 +Potato Field +# TXT_CIV18 +Sala's House +# TXT_CIV20 +Abdul's House +# TXT_CIV21 +Pablo's Wicked Pub +# TXT_CIV22 +Village Well +# TXT_CIV23 +Camel Trader +# TXT_CIV24 +Church +# TXT_CIV25 +Ali's House +# TXT_CIV26 +Trader Ted's +# TXT_CIV27 +Menelik's House +# TXT_CIV28 +Prestor John's House +# TXT_CIV29 +Village Well +# TXT_CIV30 +Witch Doctor's Hut +# TXT_CIV31 +Rikitikitembo's Hut +# TXT_CIV32 +Roarke's Hut +# TXT_CIV33 +Mubasa's Hut +# TXT_CIV34 +Aksum's Hut +# TXT_CIV35 +Mambo's Hut +# TXT_CIV36 +The Studio +# TXT_CIV37 +Technology Center +# TXT_CIVMISS +Turret +# TXT_TURRET +Gunboat +# TXT_GUNBOAT +Mobile Construction Vehicle +# TXT_MCV +Power Plant +# TXT_POWER +Advanced Power Plant +# TXT_ADVANCED_POWER +Hospital +# TXT_HOSPITAL +Barracks +# TXT_BARRACKS +Oil Pump +# TXT_PUMP +Oil Tanker +# TXT_TANKER +Sandbags +# TXT_SANDBAG_WALL +Chain Link Fence +# TXT_CYCLONE_WALL +Concrete Wall +# TXT_BRICK_WALL +Barbwire Fence +# TXT_BARBWIRE_WALL +Wood Fence +# TXT_WOOD_WALL +War Factory +# TXT_WEAPON_FACTORY +Advanced Guard Tower +# TXT_AGUARD_TOWER +Bio-Research Laboratory +# TXT_BIO_LAB +Service Depot +# TXT_FIX_IT +Sidebar +# TXT_TAB_SIDEBAR +Options +# TXT_TAB_BUTTON_CONTROLS +Database +# TXT_TAB_BUTTON_DATABASE +Unrevealed Terrain +# TXT_SHADOW +Options Menu +# TXT_OPTIONS_MENU +Stop +# TXT_STOP +Play +# TXT_PLAY +Shuffle +# TXT_SHUFFLE +Repeat +# TXT_REPEAT +Music volume: +# TXT_MUSIC_VOLUME +Sound volume: +# TXT_SOUND_VOLUME +On +# TXT_ON +Off +# TXT_OFF +Multiplayer Game +# TXT_MULTIPLAYER_GAME +No files available +# TXT_NO_FILES +Do you want to delete this file? +# TXT_DELETE_SINGLE_FILE +Do you want to delete %d files? +# TXT_DELETE_MULTIPLE_FILES +Reset Values +# TXT_RESET_MENU +Do you want to abort the mission? +# TXT_CONFIRM_EXIT +Mission Description +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Barry +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Karen +# TXT_C5 +Steve +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Prof. Einstein +# TXT_EINSTEIN +Road Bib +# TXT_BIB +Faster +# TXT_FASTER +Slower +# TXT_SLOWER +Air Strike +# TXT_AIR_STRIKE +Steel Crate +# TXT_STEEL_CRATE +Wood Crate +# TXT_WOOD_CRATE +Water Crate +# TXT_WATER_CRATE +Flag Location +# TXT_FLAG_SPOT +Unable to read scenario! +# TXT_UNABLE_READ_SCENARIO +Error loading game! +# TXT_ERROR_LOADING_GAME +Obsolete saved game. +# TXT_OBSOLETE_SAVEGAME +You must enter a description! +# TXT_MUSTENTER_DESCRIPTION +Error saving game! +# TXT_ERROR_SAVING_GAME +Delete this file? +# TXT_DELETE_FILE_QUERY +[EMPTY SLOT] +# TXT_EMPTY_SLOT +Select Multiplayer Game +# TXT_SELECT_MPLAYER_GAME +Modem/Serial +# TXT_MODEM_SERIAL +Network +# TXT_NETWORK +Unable to initialize network! +# TXT_INIT_NET_ERROR +Join Network Game +# TXT_JOIN_NETWORK_GAME +New +# TXT_NEW +Join +# TXT_JOIN +Send Message +# TXT_SEND_MESSAGE +Your Name: +# TXT_YOUR_NAME +Your Side: +# TXT_SIDE_COLON +Your Color: +# TXT_COLOR_COLON +Games +# TXT_GAMES +Players +# TXT_PLAYERS +Scenario: +# TXT_SCENARIO_COLON +>> NOT FOUND << +# TXT_NOT_FOUND +Starting Credits: +# TXT_START_CREDITS_COLON +Bases: +# TXT_BASES_COLON +Ore: +# TXT_TIBERIUM_COLON +Crates: +# TXT_CRATES_COLON +AI Players: +# TXT_AI_PLAYERS_COLON +Request denied. +# TXT_REQUEST_DENIED +Unable to play; scenario not found. +# TXT_UNABLE_PLAY_WAAUGH +Nothing to join! +# TXT_NOTHING_TO_JOIN +You must enter a name! +# TXT_NAME_ERROR +Duplicate names are not allowed. +# TXT_DUPENAMES_NOTALLOWED +Your game version is outdated. +# TXT_YOURGAME_OUTDATED +Destination game version is outdated. +# TXT_DESTGAME_OUTDATED +%s's Game +# TXT_THATGUYS_GAME +[%s's Game] +# TXT_THATGUYS_GAME_BRACKET +Network Game Setup +# TXT_NETGAME_SETUP +Reject +# TXT_REJECT +You can't reject yourself! +# TXT_CANT_REJECT_SELF +You must select a player to reject. +# TXT_SELECT_PLAYER_REJECT +Bases +# TXT_BASES +Crates +# TXT_CRATES +AI Players +# TXT_AI_PLAYERS +Scenarios +# TXT_SCENARIOS +Credits: +# TXT_CREDITS_COLON +Only one player? +# TXT_ONLY_ONE +Oops! +# TXT_OOPS +To %s: +# TXT_TO +To All: +# TXT_TO_ALL +Message: +# TXT_MESSAGE +Connection to %s lost! +# TXT_CONNECTION_LOST +%s has left the game. +# TXT_LEFT_GAME +%s has been defeated! +# TXT_PLAYER_DEFEATED +Waiting to Connect... +# TXT_WAITING_CONNECT +Connection error! Check your cables. Attempting to Reconnect... +# TXT_NULL_CONNERR_CHECK_CABLES +Connection error! Redialing... +# TXT_MODEM_CONNERR_REDIALING +Connection error! Waiting for Call... +# TXT_MODEM_CONNERR_WAITING +Select Serial Game +# TXT_SELECT_SERIAL_GAME +Dial Modem +# TXT_DIAL_MODEM +Answer Modem +# TXT_ANSWER_MODEM +Null Modem +# TXT_NULL_MODEM +Settings +# TXT_SETTINGS +Port: +# TXT_PORT_COLON +IRQ: +# TXT_IRQ_COLON +Baud: +# TXT_BAUD_COLON +Init String: +# TXT_INIT_STRING +Call Waiting String: +# TXT_CWAIT_STRING +Tone Dialing +# TXT_TONE_BUTTON +Pulse Dialing +# TXT_PULSE_BUTTON +Host Serial Game +# TXT_HOST_SERIAL_GAME +Opponent: +# TXT_OPPONENT_COLON +User signed off! +# TXT_USER_SIGNED_OFF +Join Serial Game +# TXT_JOIN_SERIAL_GAME +Phone List +# TXT_PHONE_LIST +Add +# TXT_ADD +Edit +# TXT_EDIT +Dial +# TXT_DIAL +Default +# TXT_DEFAULT +Default Settings +# TXT_DEFAULT_SETTINGS +Custom Settings +# TXT_CUSTOM_SETTINGS +Phone Listing +# TXT_PHONE_LISTING +Name: +# TXT_NAME_COLON +Number: +# TXT_NUMBER_COLON +Unable to find modem. Check power and cables. +# TXT_UNABLE_FIND_MODEM +No carrier. +# TXT_NO_CARRIER +Line busy. +# TXT_LINE_BUSY +Number invalid. +# TXT_NUMBER_INVALID +Other system not responding! +# TXT_SYSTEM_NOT_RESPONDING +Games are out of sync! +# TXT_OUT_OF_SYNC +Packet received too late! +# TXT_PACKET_TOO_LATE +Other player has left the game. +# TXT_PLAYER_LEFT_GAME +From %s:%s +# TXT_FROM +TIME: +# TXT_SCORE_TIME +LEADERSHIP: +# TXT_SCORE_LEAD +ECONOMY: +# TXT_SCORE_EFFI +TOTAL SCORE: +# TXT_SCORE_TOTA +CASUALTIES: +# TXT_SCORE_CASU +NEUTRAL: +# TXT_SCORE_NEUT +BUILDINGS LOST +# TXT_SCORE_BUIL +BUILDINGS +# TXT_SCORE_BUIL1 +LOST: +# TXT_SCORE_BUIL2 +TOP SCORES +# TXT_SCORE_TOP +ENDING CREDITS: +# TXT_SCORE_ENDCRED +%dh %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +Dialing... +# TXT_DIALING +Dialing Canceled +# TXT_DIALING_CANCELED +Waiting for Call... +# TXT_WAITING_FOR_CALL +Answering Canceled +# TXT_ANSWERING_CANCELED +Engineer +# TXT_E6 +Spy +# TXT_E8 +Not a Null Modem Cable Attached! It is a modem or loopback cable. +# TXT_MODEM_OR_LOOPBACK +Map +# TXT_MAP +Blossom Tree +# TXT_BLOSSOM_TREE +Briefing +# TXT_RESTATE_MISSION +Computer +# TXT_COMPUTER +Unit Count: +# TXT_COUNT +Tech Level: +# TXT_LEVEL +Opponent +# TXT_OPPONENT +Kills: +# TXT_KILLS_COLON +Video +# TXT_VIDEO +Scientist +# TXT_C10 +Capture The Flag +# TXT_CAPTURE_THE_FLAG +Mission Objective +# TXT_OBJECTIVE +Mission +# TXT_MISSION +No saved games available. +# TXT_NO_SAVES +Civilian Building +# TXT_CIVILIAN_BUILDING +Technician +# TXT_TECHNICIAN +Save game options are not allowed during a multiplayer session. +# TXT_NO_SAVELOAD +Special 1 +# TXT_DELPHI +Would you like to replay this mission? +# TXT_TO_REPLAY +Reconnecting to %s. +# TXT_RECONN_TO +Please wait %02d seconds. +# TXT_PLEASE_WAIT +Do you wish to surrender? +# TXT_SURRENDER +SELECT TRANSMISSION +# TXT_SEL_TRANS +Your game name must be unique. +# TXT_GAMENAME_MUSTBE_UNIQUE +Game is closed. +# TXT_GAME_IS_CLOSED +Your name must be unique. +# TXT_NAME_MUSTBE_UNIQUE +Reconnecting to %s +# TXT_RECONNECTING_TO +Waiting for connections... +# TXT_WAITING_FOR_CONNECTIONS +Time allowed: %02d seconds +# TXT_TIME_ALLOWED +Press ESC to cancel. +# TXT_PRESS_ESC +From Computer: It's just you and me now! +# TXT_JUST_YOU_AND_ME +Capture the Flag: +# TXT_CAPTURE_THE_FLAG_COLON +Special 2 +# TXT_CHAN +%s has allied with %s +# TXT_HAS_ALLIED +%s declares war on %s +# TXT_AT_WAR +Select a target +# TXT_SEL_TARGET +Resign Game +# TXT_RESIGN +Ore grows quickly. +# TXT_TIBERIUM_FAST +Answering... +# TXT_ANSWERING +Initializing Modem... +# TXT_INITIALIZING_MODEM +Scenarios don't match. +# TXT_SCENARIOS_DO_NOT_MATCH +Power Output +# TXT_POWER_OUTPUT +Power Output (low) +# TXT_POWER_OUTPUT_LOW +Continue +# TXT_CONTINUE +Data Queue Overflow +# TXT_QUEUE_FULL +%s changed game options! +# TXT_SPECIAL_WARNING +Please insert a Red Alert CD into the CD-ROM drive. +# TXT_CD_DIALOG_1 +Please insert CD %d (%s) into the CD-ROM drive. +# TXT_CD_DIALOG_2 +Red Alert is unable to detect your CD ROM drive. +# TXT_CD_ERROR1 +No Sound Card Detected +# TXT_NO_SOUND_CARD +UNKNOWN +# TXT_UNKNOWN +(old) +# TXT_OLD_GAME +Insufficient Disk Space to run Red Alert. +# TXT_NO_SPACE +You must have %d megabytes of free disk space. +# TXT_MUST_HAVE_SPACE +Run SETUP program first. +# TXT_RUN_SETUP +Waiting for Opponent +# TXT_WAITING_FOR_OPPONENT +Please select 'Settings' to setup default configuration +# TXT_SELECT_SETTINGS +Prison +# TXT_PRISON +Mission Saved +# TXT_GAME_WAS_SAVED +Insufficient disk space to save a game. Please delete a previous save to free up some disk space and try again. +# TXT_SPACE_CANT_SAVE +Invalid Port/Address. COM 1-4 OR ADDRESS +# TXT_INVALID_PORT_ADDRESS +Invalid Port and/or IRQ settings +# TXT_INVALID_SETTINGS +IRQ already in use +# TXT_IRQ_ALREADY_IN_USE +Abort +# TXT_ABORT +Restart +# TXT_RESTART +Mission is restarting. Please wait... +# TXT_RESTARTING +Mission is loading. Please wait... +# TXT_LOADING +Error in the InitString +# TXT_ERROR_IN_INITSTRING +Shroud: +# TXT_SHADOW_COLON +Anti-Vehicle Mine +# TXT_AVMINE +Anti-Personnel Mine +# TXT_APMINE +New Missions +# TXT_NEW_MISSIONS +Thief +# TXT_THIEF +Radar Jammer +# TXT_MRJ +Gap Generator +# TXT_GAP_GENERATOR +Pillbox +# TXT_PILLBOX +Camo. Pillbox +# TXT_CAMOPILLBOX +Chronosphere +# TXT_CHRONOSPHERE +England +# TXT_ENGLAND +Germany +# TXT_GERMANY +Spain +# TXT_SPAIN +Russia +# TXT_USSR +Ukraine +# TXT_UKRAINE +Greece +# TXT_GREECE +France +# TXT_FRANCE +Turkey +# TXT_TURKEY +Shore +# TXT_SHORE +Select Object +# TXT_PLACE_OBJECT +Submarine +# TXT_SS +Destroyer +# TXT_DD +Cruiser +# TXT_CA +Transport +# TXT_TRANSPORT +Gun Boat +# TXT_PT +Lobby +# TXT_LOBBY +Games +# TXT_CHANNEL_GAMES +Save Game... +# TXT_SAVING_GAME +Game is full. +# TXT_GAME_FULL +You must select a game! +# TXT_MUST_SELECT_GAME +%s playing %s +# TXT_S_PLAYING_S +Only the host can modify this option. +# TXT_ONLY_HOST_CAN_MODIFY +Game was cancelled. +# TXT_GAME_CANCELLED +%s has formed a new game. +# TXT_S_FORMED_NEW_GAME +%s's game is now in progress. +# TXT_GAME_NOW_IN_PROGRESS +Tesla Coil +# TXT_TESLA +Mobile Gap Generator +# TXT_MGG +Flame Tower +# TXT_FLAME_TURRET +AA Gun +# TXT_AAGUN +Kennel +# TXT_KENNEL +Soviet Tech Center +# TXT_SOVIET_TECH +Badger Bomber +# TXT_BADGER +Mig Attack Plane +# TXT_MIG +Yak Attack Plane +# TXT_YAK +Barbed Wire +# TXT_FENCE +Field Medic +# TXT_MEDIC +Saboteur +# TXT_SABOTEUR +General +# TXT_GENERAL +Tanya +# TXT_E7 +Parabombs +# TXT_PARA_BOMB +Paratroopers +# TXT_PARA_INFANTRY +Parachute Saboteur +# TXT_PARA_SABOTEUR +Naval Yard +# TXT_SHIP_YARD +Sub Pen +# TXT_SUB_PEN +Scenario Options +# TXT_SCENARIO_OPTIONS +Spy Plane +# TXT_SPY_MISSION +Spy Plane +# TXT_U2 +Attack Dog +# TXT_GUARD_DOG +Spy Info +# TXT_SPY_INFO +Buildings +# TXT_BUILDNGS +Units +# TXT_UNITS +Infantry +# TXT_INFANTRY +Aircraft +# TXT_AIRCRAFT +Supply Truck +# TXT_TRUCK +Invulnerability Device +# TXT_INVUL +Iron Curtain +# TXT_IRON_CURTAIN +Allied Tech Center +# TXT_ADVANCED_TECH +V2 Rocket +# TXT_V2_LAUNCHER +Forward Command Post +# TXT_FORWARD_COM +Demolitioner +# TXT_DEMOLITIONER +Mine Layer +# TXT_MINE_LAYER +Fake Construction Yard +# TXT_FAKE_CONST +Fake War Factory +# TXT_FAKE_WEAP +Fake Naval Yard +# TXT_FAKE_YARD +Fake Sub Pen +# TXT_FAKE_PEN +Fake Radar Dome +# TXT_FAKE_RADAR +Bigfoot +# TXT_THEME_BIGF +Crush +# TXT_THEME_CRUS +Face the Enemy 1 +# TXT_THEME_FAC1 +Face the Enemy 2 +# TXT_THEME_FAC2 +Hell March +# TXT_THEME_HELL +Run for Your Life +# TXT_THEME_RUN1 +Smash +# TXT_THEME_SMSH +Trenches +# TXT_THEME_TREN +Workmen +# TXT_THEME_WORK +Await +# TXT_THEME_AWAIT +Dense +# TXT_THEME_DENSE_R +Map Selection +# TXT_THEME_MAP +Fogger +# TXT_THEME_FOGGER1A +Mud +# TXT_THEME_MUD1A +Radio 2 +# TXT_THEME_RADIO2 +Roll Out +# TXT_THEME_ROLLOUT +Snake +# TXT_THEME_SNAKE +Terminate +# TXT_THEME_TERMINAT +Twin +# TXT_THEME_TWIN +Vector +# TXT_THEME_VECTOR1A +Team Members +# TXT_TEAM_MEMBERS +Bridge +# TXT_BRIDGE +Barrel +# TXT_BARREL +Friendly +# TXT_GOODGUY +Enemy +# TXT_BADGUY +Gold +# TXT_GOLD +Gems +# TXT_GEMS +Title Movie +# TXT_TEASER +Movies +# TXT_MOVIES +Interior +# TXT_INTERIOR +Sonar Pulse +# TXT_SONAR_PULSE +Missile Silo +# TXT_MSLO +GPS Satellite +# TXT_GPS_SATELLITE +Atom Bomb +# TXT_NUCLEAR_BOMB +Easy +# TXT_EASY +Hard +# TXT_HARD +Normal +# TXT_NORMAL +Please set the difficulty level. It will be used throughout this campaign. +# TXT_DIFFICULTY +Allies +# TXT_ALLIES +Soviet +# TXT_SOVIET +Intro Theme +# TXT_THEME_INTRO +Shroud Regrows +# TXT_SHADOW_REGROWS +Ore Regenerates +# TXT_ORE_SPREADS +Score Theme +# TXT_THEME_SCORE +Internet Game +# TXT_INTERNET +Ice +# TXT_ICE +Crates +# TXT_CRATE +Skirmish +# TXT_SKIRMISH +Choose your side. +# TXT_CHOOSE +Valuable Minerals +# TXT_MINERALS +Ignore +# TXT_IGNORE +Error - modem is not responding. +# TXT_ERROR_NO_RESP +Error - modem did not respond to result code enable command. +# TXT_ERROR_NO_RESCODE +Error - modem did not respond to initialisation string. +# TXT_ERROR_NO_INIT +Error - modem did not respond to 'verbose' command. +# TXT_ERROR_NO_VERB +Error - modem did not respond to 'echo' command. +# TXT_ERROR_NO_ECHO +Error - unable to disable modem auto answer. +# TXT_ERROR_NO_DISABLE +Error - Too many errors initialising modem - Aborting. +# TXT_ERROR_TOO_MANY +Error - Modem returned error status. +# TXT_ERROR_ERROR +Error - TIme out waiting for connect. +# TXT_ERROR_TIMEOUT +Accomplished +# TXT_ACCOMPLISHED +Click to Continue +# TXT_CLICK_CONTINUE +Receiving scenario from host. +# TXT_RECEIVING_SCENARIO +Sending scenario to remote players. +# TXT_SENDING_SCENARIO +Error - Modem failed to respond to flow control command. Your Windows configuration may be incorrect. +# TXT_NO_FLOW_CONTROL_RESPONSE +Error - Modem failed to respond to compression command. Your Windows configuration may be incorrect. +# TXT_NO_COMPRESSION_RESPONSE +Error - Modem failed to respond to error correction command. Your Windows configuration may be incorrect. +# TXT_NO_ERROR_CORRECTION_RESPONSE +To play Red Alert via the internet you must be connected to an internet services provider and be registered with Planet Westwood +# TXT_EXPLAIN_REGISTRATION +Wchat not installed. Please install it from either CD. +# TXT_ERROR_UNABLE_TO_RUN_WCHAT +Register +# TXT_REGISTER +Ore Mine +# TXT_ORE_MINE +No registered modem +# TXT_NO_REGISTERED_MODEM +Chronoshift +# TXT_CHRONOSHIFT +Invalid Port or Port is in use +# TXT_UNABLE_TO_OPEN_PORT +No dial tone. Ensure your modem is connected to the phone line and try again. +# TXT_NO_DIAL_TONE +Error - other player does not have this expansion scenario. +# TXT_NO_EXPANSION_SCENARIO +Please Stand By... +# TXT_STAND_BY +End Credits Theme +# TXT_THEME_CREDITS +Low Power: AA-Guns offline +# TXT_POWER_AAGUN +Low Power: Tesla Coils offline +# TXT_POWER_TESLA +Low Power +# TXT_LOW_POWER +Commander: +# TXT_COMMANDER +Battles Won: +# TXT_BATTLES_WON +Game versions incompatible. To make sure you have the latest version, visit www.westwood.com +# TXT_MISMATCH +Incompatible scenario file detected. The scenario may be corrupt. +# TXT_SCENARIO_ERROR +Connecting... +# TXT_CONNECTING +Modem Initialization +# TXT_MODEM_INITIALISATION +Data Compression +# TXT_DATA_COMPRESSION +Error Correction +# TXT_ERROR_CORRECTION +Hardware Flow Control +# TXT_HARDWARE_FLOW_CONTROL +Advanced +# TXT_ADVANCED +2nd_Hand +# TXT_THEME_2ND_HAND +Arazoid +# TXT_THEME_ARAZOID +BackStab +# TXT_THEME_BACKSTAB +Chaos2 +# TXT_THEME_CHAOS2 +Shut_It +# TXT_THEME_SHUT_IT +TwinMix1 +# TXT_THEME_TWINMIX1 +Under3 +# TXT_THEME_UNDER3 +VR2 +# TXT_THEME_VR2 +The other system is not responding. Do you wish to attempt an emergency game save? Both players must save for this to work. +# TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING +The other system hung up. Do you wish to attempt an emergency game save? Both players must save for this to work. +# TXT_ASK_EMERGENCY_SAVE_HUNG_UP +Red Alert was unable to run the registration software. You need to install Westwood Chat from the Red Alert CD to register. +# TXT_NO_REG_APP +A player in the game does not have this expansion scenario. +# TXT_NO_CS_SCENARIOS +Missile Sub +# TXT_MISSILESUB +Shock Trooper +# TXT_SHOCKTROOPER +Mechanic +# TXT_MECHANIC +Chrono Tank +# TXT_CHRONOTANK +Tesla Tank +# TXT_TESLATANK +M.A.D. Tank +# TXT_MAD +Demolition Truck +# TXT_DEMOTRUCK +Phase Transport +# TXT_PHASETRANSPORT +Bog +# TXT_THEME_BOG +Floating +# TXT_THEME_FLOAT_V2 +Gloom +# TXT_THEME_GLOOM +Ground Wire +# TXT_THEME_GRNDWIRE +Mech Man 2 +# TXT_THEME_RPT +Search +# TXT_THEME_SEARCH +Traction +# TXT_THEME_TRACTION +Wasteland +# TXT_THEME_WASTELND +Helicarrier +# TXT_CARRIER diff --git a/CODE/ENG/ENG/CREDITS.TXT b/CODE/ENG/ENG/CREDITS.TXT new file mode 100644 index 0000000..b4b9385 --- /dev/null +++ b/CODE/ENG/ENG/CREDITS.TXT @@ -0,0 +1,349 @@ + Aftermath Credits + + Executive Producer Brett W. Sperry + + Producer Lewis S. Peterson + + Programmer Barry Green + + Technical Direction Steve Wetherill, Eric Wang + + Solo Play Map Designers: + John Archer, Patrick Connelly, + Adam Isgreen, Michael Lightner, + Erik Yeo + + Multiplay Map Designers: + John Archer, Stuart Bailey, + Nigel Berryman, D'Andre Campbell, + Valerie Carpentier, Cathi Diet, + Keith Ditchburn, Gareth Eke, + Darren Esp, Simon Evers, Rod Gray, + Randy Greenback, Matthew Howe, + Levi Luke, Suzanne Maddison, + Iain McNeil, Graeme Miller, + Lee Morse, David Parsons, + Alex Scarrow, Robert Scheel, + Lawrence So, John Sweeney, + Matthew Tillett, Phillip Veale + + Artists Chris Demers, Matthew Hansel, + Joseph B. Hewitt IV + + Audio Direction Paul S. Mudra + + Soundtrack Frank Klepacki + + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance + Jim Adkins, Lloyd Bell, + Chris Blevens, D'Andre Campbell, + Errol Campbell, Shane Dietrich, + Randy Greenback, Jon Hix, Mark Laity, + Troy Leonard, DeMarlo Lewis, + Levi Luke, Patrick Offord, + Richard Rasmussen, Mike Ruppert, + Steve Shockey, Mike Smith, + Albert Springfield + + Ingame Voices Jeff Fillhaber, Michael Smith + + + + + + Red Alert Credits + + Executive Producer Brett W. Sperry + Producer Ed Del Castillo + + Original Concept Brett W. Sperry + Joe Bostic + + Original Story Ron Smith + Ed Del Castillo + + Lead Game Programmers Joe Bostic + Barry Green + Steve Tall + + Game Programmers Bill Randolph + Phil Gorrow + Maria del Mar McCready Legg + + Internet Programmers Jeff Brown + David Aldridge + Matt Thorn + + Translation Programmer Victor Grippi + + Technical Direction Steve Wetherill + Eric Wang + + Designers Adam Isgreen + Michael Lightner + Erik Yeo + + Lead Artists Chris Demers + Matthew Hansel + Joseph B. Hewitt IV + + Artists David T. Potter + Damon Redmond + Paul Wesberry + Brian White + + Screenplay Ron Smith + Adam Isgreen + John Scott Lewinski + + Video Post Production Felix Kupis + Kevin Becquet + + Movie Compression Director Tim C. Fritz + Compression Assistant Patrick Connelly + + Audio Direction Paul S. Mudra + Soundtrack Frank Klepacki + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance James Adkins + John Archer + Lloyd Bell + Chris Bleven + D'Andre Cambell + Errol Campbell + Phillip Castro + Steve Corcoran + Kenny Dunne + Randy Greenback + Abe Hernandez + Chris Holloway + Troy Leonard + Ben Lublin + Levi Luke + Isaiah Myers + Richard Rasmussen + Chris Rubyor + Robert Scheel + Albert Springfield + Mike Smith + Tyler Thackery + + Box Design Matthew Hansel + Thomas Puckett Inc. + + Manual Design Victoria Hart + + + Special thanks to all our friends and supporters + within the Virgin empire. Additional thanks to + Tracy Chapman for a new beginning and Greg + Hjelstrom for the amazing chronal vortex. + + + + + The Cast + + Allied Side + Von Esling Arthur Roberts + Stavros Barry Kramer + Albert Einstein John Milford + Tanya Lynne Litteer + Interrogator Dom Magwili + Announcer Gwen Castaldi + + + Allied Soldiers + Finale Ricky Russell + Nick Paulos + Scott Ryan Talley + + Commanders Joe Bostic + Chris Demers + Barry Green + Matthew Hansel + Adam Isgreen + Frank Klepacki + Mike Lightner + Bill Randolph + Philip E. Shelburne + Eric Wang + + + + Soviet Side + Josef Stalin Eugene Dynarski + Kukov Craig Cavanah + Gradenko Alan Terry + Nadia Andrea Robinson + Kane Joe Kucan + + + Soviet Soldiers John Archer + Mike Grayford + Frank Klepacki + Felix Kupis + Chris Rubyor + Erik Yeo + + + + Ingame Voices + E.V.A. Martin Alper + Tanya Lanae Freeborn + Infantry & Units Mike Grayford + Adam Isgreen + Frank Klepacki + Troy Leonard + Dwight Okahara + Chris Rubyor + Eric Wang + Erik Yeo + + + + + Production Crew + + Directed By Joseph D. Kucan + + Costume Design Christie Moeller + + Make-up & Hair Cindy Cline + + Sound Engineer Paul S. Mudra + + Casting Marilee Lear, C.S.A. + + Video Kevin Becquet + + Lighting Eric Gooch + + Lead Production Assistant Paul Bastardo + + Production Assistants Rick Appin + Patience Becquet + Pat Connelly + Jeff Fillhaber + Richard Rasmussen + + Sfx Make-up Philip E. Shelburne + + Special Footage Courtesy Of NBC News Archives Films + Fabulous Footage + + + + + + + + + + Counterstrike Credits + + Executive Producer Brett W. Sperry + Producer Ed Del Castillo + + Programmers Victor Grippi + Joe Bostic + Steve Tall + Maria del Mar McCready Legg + + Technical Direction Steve Wetherill + Eric Wang + + Solo Play Map Designers Adam Isgreen + Michael Lightner + Patrick Connelly + + Multiplay Map Designers Iain McNeil + Alex Scarrow + Graeme Miller + Nigel Berryman + Rod Gray + Stuart Bailey + Mat Tillett + Phil Veale + Dave Parsons + Darren Esp + Cameron Strutt + Paul Goodwin + John McNeil + Lee Morse + Adam Isgreen + Michael Lightner + John Archer + Patrick Connelly + Lloyd Bell + D'Andre Campbell + Albert Springfield + Levi Luke + Robert Scheel + + Artists Chris Demers + Matthew Hansel + Shelly Johnson + Frank Mendeola + + Video Kevin Becquet + Patience Becquet + + Audio Direction Paul S. Mudra + + Soundtrack Frank Klepacki + + Sound Effects Dwight K. Okahara + + QA Direction Glenn Sperry + + Quality Assurance John Archer + Mike Smith + Lloyd Bell + Troy Leonard + Chris Halloway + Randy Greenback + D'Andre Cambell + Errol Campbell + Albert Springfield + James Adkins + Chris Blevens + Levi Luke + Steve Shockey + Isaiah Myers + Ben Lublin + Richard Rasmussen + Robert Scheel + Tom Andrulis + Shane Dietrich + Brandon Hansen + + + + Thanks to Westwood's killer marketing staff for + all their support, Intelligent Games for creating + a bulk of the multiplayer maps, and Brett and + Louis for being born. + + Special thanks to the European Marketing teams + for their support, and to the original Red Alert + team for giving us something to build on. + + + + + For the latest news, goodies, and updates, + visit our web site at www.westwood.com + + + + + + diff --git a/CODE/ENG/FRE/CONQUER.TXT b/CODE/ENG/FRE/CONQUER.TXT new file mode 100644 index 0000000..e2bd029 --- /dev/null +++ b/CODE/ENG/FRE/CONQUER.TXT @@ -0,0 +1,1111 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Temps:%02d:%02d:%02d +# TXT_TIME_FORMAT_HOURS +Temps:%02d:%02d +# TXT_TIME_FORMAT_NO_HOURS +Vente +# TXT_BUTTON_SELL +Vente structure +# TXT_SELL +R‚paration +# TXT_BUTTON_REPAIR +Vous : +# TXT_YOU +Ennemi : +# TXT_ENEMY +Bƒtiments d‚truits par +# TXT_BUILD_DEST +Unit‚s d‚truites par +# TXT_UNIT_DEST +Minerai r‚colt‚ par +# TXT_TIB_HARV +Score: %d +# TXT_SCORE_1 +Oui +# TXT_YES +Non +# TXT_NO +Mission Accomplie +# TXT_SCENARIO_WON +Mission Echou‚e +# TXT_SCENARIO_LOST +Nouvelle partie +# TXT_START_NEW_GAME +Intro/Preview +# TXT_INTRO +Annuler +# TXT_CANCEL +Rocher +# TXT_ROCK +Civil +# TXT_CIVILIAN +Equipe de confinement +# TXT_JP +OK +# TXT_OK +Arbre +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Effacer +# TXT_CLEAR +Eau +# TXT_WATER +Route +# TXT_ROAD +Pente +# TXT_SLOPE +Patch +# TXT_PATCH +RiviŠre +# TXT_RIVER +Charger Mission +# TXT_LOAD_MISSION +Sauvegarder Mission +# TXT_SAVE_MISSION +Effacer Mission +# TXT_DELETE_MISSION +Charger +# TXT_LOAD_BUTTON +Sauvegarder +# TXT_SAVE_BUTTON +Effacer +# TXT_DELETE_BUTTON +Contr“les +# TXT_GAME_CONTROLS +Son +# TXT_SOUND_CONTROLS +Reprendre Mission +# TXT_RESUME_MISSION +Affichage +# TXT_VISUAL_CONTROLS +Abandonner Mission +# TXT_QUIT_MISSION +Quitter le jeu +# TXT_EXIT_GAME +Options +# TXT_OPTIONS +D‚bris humains +# TXT_SQUISH +CratŠre +# TXT_CRATER +Marque de br–lure +# TXT_SCORCH +Luminosit‚ : +# TXT_BRIGHTNESS +Musique +# TXT_MUSIC +Effets sonores +# TXT_VOLUME +Teintes : +# TXT_TINT +Contraste : +# TXT_CONTRAST +Vitesse du jeu : +# TXT_SPEED +Vitesse d‚filement : +# TXT_SCROLLRATE +Couleur : +# TXT_COLOR +Revenir au jeu +# TXT_RETURN_TO_GAME +Soldat ennemi +# TXT_ENEMY_SOLDIER +V‚hicule ennemi +# TXT_ENEMY_VEHICLE +Structure ennemie +# TXT_ENEMY_STRUCTURE +Tank l‚ger +# TXT_LTANK +Tank lourd +# TXT_MTANK +Tank moyen +# TXT_MTANK2 +Tank Mammouth +# TXT_HTANK +Missiles SAM +# TXT_SAM +Ranger +# TXT_JEEP +H‚licoptŠre Chinook +# TXT_TRANS +Collecteur minerai +# TXT_HARVESTER +Artillerie +# TXT_ARTY +Mitrailleurs +# TXT_E1 +Grenadiers +# TXT_E2 +Bazookas +# TXT_E3 +Lance-flammes +# TXT_E4 +H‚licoptŠre d'assaut +# TXT_HELI +Hind +# TXT_ORCA +VBT +# TXT_APC +Tour de garde +# TXT_GUARD_TOWER +D“me radar +# TXT_COMMAND +H‚liport +# TXT_HELIPAD +Piste d'atterrissage +# TXT_AIRSTRIP +Silo minerai +# TXT_STORAGE +Chantier de construction +# TXT_CONST_YARD +Raffinerie de minerai +# TXT_REFINERY +Eglise +# TXT_CIV1 +Chez Hans et Gretel +# TXT_CIV2 +Manoir d'Hewitt +# TXT_CIV3 +Maison de Ricktor +# TXT_CIV4 +Maison de Gretchin +# TXT_CIV5 +La grange +# TXT_CIV6 +Pub Damon +# TXT_CIV7 +Maison de Fran +# TXT_CIV8 +Usine d'instruments +# TXT_CIV9 +Fabricant de jouets +# TXT_CIV10 +Maison de Ludwig +# TXT_CIV11 +Meules de foin +# TXT_CIV12 +Meule de foin +# TXT_CIV13 +Champ de bl‚ +# TXT_CIV14 +Champ en friche +# TXT_CIV15 +Champ de ma‹s +# TXT_CIV16 +Champ de c‚leri +# TXT_CIV17 +Champ de pommes de terre +# TXT_CIV18 +Maison de Sala +# TXT_CIV20 +Maison d'Abdul +# TXT_CIV21 +Le Pub Barjo de Pablo +# TXT_CIV22 +Puits du village +# TXT_CIV23 +Marchand de chameaux +# TXT_CIV24 +Eglise +# TXT_CIV25 +Maison d'Ali +# TXT_CIV26 +Ted le Marchand +# TXT_CIV27 +Maison de Menelik +# TXT_CIV28 +Maison du pasteur John +# TXT_CIV29 +Puits du village +# TXT_CIV30 +Hutte du gu‚risseur +# TXT_CIV31 +Hutte de Rikitikitembo +# TXT_CIV32 +Hutte de Roarke +# TXT_CIV33 +Hutte de Moubasa' +# TXT_CIV34 +Hutte d'Aksoum +# TXT_CIV35 +Hutte de Mambo +# TXT_CIV36 +Le studio +# TXT_CIV37 +Centre technologique +# TXT_CIVMISS +Tourelle +# TXT_TURRET +Aviso-torpilleur +# TXT_GUNBOAT +V‚hicule de construction mobile +# TXT_MCV +Centrale ‚lectrique +# TXT_POWER +Centrale ‚lectrique avanc‚e +# TXT_ADVANCED_POWER +H“pital +# TXT_HOSPITAL +Caserne +# TXT_BARRACKS +Pompe +# TXT_PUMP +P‚trolier +# TXT_TANKER +Sacs de sable +# TXT_SANDBAG_WALL +Cl“ture grillag‚e +# TXT_CYCLONE_WALL +Mur de b‚ton +# TXT_BRICK_WALL +Cl“ture barbel‚e +# TXT_BARBWIRE_WALL +BarriŠre de bois +# TXT_WOOD_WALL +Usine d'armement +# TXT_WEAPON_FACTORY +Tour de garde avanc‚e +# TXT_AGUARD_TOWER +Laboratoire biologique +# TXT_BIO_LAB +Centre de service +# TXT_FIX_IT +Contr“les +# TXT_TAB_SIDEBAR +Options +# TXT_TAB_BUTTON_CONTROLS +Base de donn‚es +# TXT_TAB_BUTTON_DATABASE +Terrain inconnu +# TXT_SHADOW +Menu des options +# TXT_OPTIONS_MENU +Stop +# TXT_STOP +Lect +# TXT_PLAY +Al‚at. +# TXT_SHUFFLE +R‚p‚ter +# TXT_REPEAT +Musique : +# TXT_MUSIC_VOLUME +Effets sonores : +# TXT_SOUND_VOLUME +Oui +# TXT_ON +Non +# TXT_OFF +Jeu Multijoueurs +# TXT_MULTIPLAYER_GAME +Pas de fichiers disponibles +# TXT_NO_FILES +Voulez-vous effacer ce fichier ? +# TXT_DELETE_SINGLE_FILE +Voulez-vous effacer %d fichiers ? +# TXT_DELETE_MULTIPLE_FILES +D‚faut +# TXT_RESET_MENU +Voulez-vous abandonner la mission ? +# TXT_CONFIRM_EXIT +Description de la mission +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Barry +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Karen +# TXT_C5 +Steve +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Prof. Einstein +# TXT_EINSTEIN +Cour +# TXT_BIB +Rapide +# TXT_FASTER +Lent +# TXT_SLOWER +Attaque a‚rienne +# TXT_AIR_STRIKE +Caisse d'acier +# TXT_STEEL_CRATE +Caisse de bois +# TXT_WOOD_CRATE +Caisse flottante +# TXT_WATER_CRATE +Emplacement du drapeau +# TXT_FLAG_SPOT +Lecture sc‚nario impossible ! +# TXT_UNABLE_READ_SCENARIO +Erreur de chargement ! +# TXT_ERROR_LOADING_GAME +Sauvegarde obsolŠte +# TXT_OBSOLETE_SAVEGAME +Vous devez entrer une description ! +# TXT_MUSTENTER_DESCRIPTION +Erreur de sauvegarde ! +# TXT_ERROR_SAVING_GAME +Effacer ce fichier ? +# TXT_DELETE_FILE_QUERY +[EMPLACEMENT VIDE] +# TXT_EMPTY_SLOT +Choix du jeu Multijoueurs +# TXT_SELECT_MPLAYER_GAME +Modem/S‚rie +# TXT_MODEM_SERIAL +R‚seau +# TXT_NETWORK +Initialisation r‚seau impossible ! +# TXT_INIT_NET_ERROR +Rejoindre jeu en r‚seau +# TXT_JOIN_NETWORK_GAME +Nouveau +# TXT_NEW +Joindre +# TXT_JOIN +Envoi Message +# TXT_SEND_MESSAGE +Votre nom : +# TXT_YOUR_NAME +Camp : +# TXT_SIDE_COLON +Couleur : +# TXT_COLOR_COLON +Parties +# TXT_GAMES +Joueurs +# TXT_PLAYERS +Sc‚nario : +# TXT_SCENARIO_COLON +>> NON TROUVE << +# TXT_NOT_FOUND +Cr‚dits : +# TXT_START_CREDITS_COLON +Bases : +# TXT_BASES_COLON +Minerai : +# TXT_TIBERIUM_COLON +Caisses : +# TXT_CRATES_COLON +Joueurs IA : +# TXT_AI_PLAYERS_COLON +Demande refus‚e. +# TXT_REQUEST_DENIED +Impossible de jouer, sc‚nario non trouv‚. +# TXT_UNABLE_PLAY_WAAUGH +Aucune partie disponible ! +# TXT_NOTHING_TO_JOIN +Vous devez entrer un nom ! +# TXT_NAME_ERROR +Les noms identiques ne sont pas autoris‚s. +# TXT_DUPENAMES_NOTALLOWED +La version de votre jeu est trop ancienne. +# TXT_YOURGAME_OUTDATED +Version du jeu de destination trop ancienne. +# TXT_DESTGAME_OUTDATED +Partie de %s +# TXT_THATGUYS_GAME +[Partie de %s] +# TXT_THATGUYS_GAME_BRACKET +Configuration du jeu en r‚seau +# TXT_NETGAME_SETUP +Rejeter +# TXT_REJECT +Vous ne pouvez pas vous rejeter vous-mˆme ! Vous risquez des conflits d'ego ! +# TXT_CANT_REJECT_SELF +Vous devez s‚lectionner un joueur … rejeter. +# TXT_SELECT_PLAYER_REJECT +Bases +# TXT_BASES +Caisses +# TXT_CRATES +Joueur IA +# TXT_AI_PLAYERS +Sc‚narios +# TXT_SCENARIOS +Cr‚dits : +# TXT_CREDITS_COLON +Un seul joueur ? +# TXT_ONLY_ONE +Oops ! +# TXT_OOPS +Pour %s : +# TXT_TO +Pour tous +# TXT_TO_ALL +Message : +# TXT_MESSAGE +Perte de connexion avec %s ! +# TXT_CONNECTION_LOST +%s a quitt‚ le jeu. +# TXT_LEFT_GAME +%s a ‚t‚ vaincu ! +# TXT_PLAYER_DEFEATED +Attente de connexion... +# TXT_WAITING_CONNECT +Erreur de connexion ! V‚rifiez vos branchements. Nouvel essai de connexion... +# TXT_NULL_CONNERR_CHECK_CABLES +Erreur de connexion ! Rappel du num‚ro... +# TXT_MODEM_CONNERR_REDIALING +Erreur de connexion ! Attente de connexion... +# TXT_MODEM_CONNERR_WAITING +Choix du jeu en s‚rie +# TXT_SELECT_SERIAL_GAME +Appel +# TXT_DIAL_MODEM +Attente appel +# TXT_ANSWER_MODEM +Null Modem +# TXT_NULL_MODEM +ParamŠtres +# TXT_SETTINGS +Port : +# TXT_PORT_COLON +IRQ : +# TXT_IRQ_COLON +Bauds : +# TXT_BAUD_COLON +ChaŒne d'initialisation +# TXT_INIT_STRING +ChaŒne d'attente d'appel +# TXT_CWAIT_STRING +Tonalit‚ +# TXT_TONE_BUTTON +Impulsions +# TXT_PULSE_BUTTON +H“te du jeu en s‚rie +# TXT_HOST_SERIAL_GAME +Adversaire : +# TXT_OPPONENT_COLON +Utilisateur reparti ! +# TXT_USER_SIGNED_OFF +Rejoindre jeu en s‚rie +# TXT_JOIN_SERIAL_GAME +R‚pertoire +# TXT_PHONE_LIST +Ajouter +# TXT_ADD +Editer +# TXT_EDIT +Appel +# TXT_DIAL +D‚faut +# TXT_DEFAULT +D‚faut +# TXT_DEFAULT_SETTINGS +Autres paramŠtres +# TXT_CUSTOM_SETTINGS +R‚pertoire +# TXT_PHONE_LISTING +Nom : +# TXT_NAME_COLON +Num‚ro : +# TXT_NUMBER_COLON +Modem non d‚tect‚. V‚rifiez vos branchements. +# TXT_UNABLE_FIND_MODEM +Pas de porteuse. +# TXT_NO_CARRIER +Ligne occup‚e. +# TXT_LINE_BUSY +Num‚ro incorrect. +# TXT_NUMBER_INVALID +L'autre systŠme ne r‚pond pas ! +# TXT_SYSTEM_NOT_RESPONDING +Mauvaise synchronisation ! +# TXT_OUT_OF_SYNC +Paquet re‡u trop tard ! +# TXT_PACKET_TOO_LATE +L'autre joueur a quitt‚ le jeu. +# TXT_PLAYER_LEFT_GAME +De %s:%s +# TXT_FROM +TEMPS : +# TXT_SCORE_TIME +COMMANDEMENT : +# TXT_SCORE_LEAD +EFFICACITE : +# TXT_SCORE_EFFI +SCORE TOTAL : +# TXT_SCORE_TOTA +PERTES : +# TXT_SCORE_CASU +NEUTRE : +# TXT_SCORE_NEUT +BATIMENTS PERDUS +# TXT_SCORE_BUIL +BATIMENTS +# TXT_SCORE_BUIL1 +PERDUS : +# TXT_SCORE_BUIL2 +MEILLEURS SCORES +# TXT_SCORE_TOP +CREDITS DE FIN : +# TXT_SCORE_ENDCRED +%dh %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +Appel... +# TXT_DIALING +Appel annul‚ +# TXT_DIALING_CANCELED +Attente d'appel... +# TXT_WAITING_FOR_CALL +Attente d'appel annul‚e +# TXT_ANSWERING_CANCELED +Ing‚nieurs +# TXT_E6 +Espion +# TXT_E8 +Pas de cƒble Null Modem connect‚ ! C'est un cƒble de modem ou en boucle. +# TXT_MODEM_OR_LOOPBACK +Carte +# TXT_MAP +Arbre en fleurs +# TXT_BLOSSOM_TREE +Briefing +# TXT_RESTATE_MISSION +Joueur IA +# TXT_COMPUTER +Nombre : +# TXT_COUNT +Niveau : +# TXT_LEVEL +Adversaire +# TXT_OPPONENT +Vict.: +# TXT_KILLS_COLON +Vid‚o +# TXT_VIDEO +Scientifique +# TXT_C10 +Capture drapeau +# TXT_CAPTURE_THE_FLAG +Objectifs de mission +# TXT_OBJECTIVE +Mission +# TXT_MISSION +Pas de sauvegardes disponibles. +# TXT_NO_SAVES +Bƒtiment civil +# TXT_CIVILIAN_BUILDING +Technicien +# TXT_TECHNICIAN +Sauvegarde interdite en mode Multijoueurs. +# TXT_NO_SAVELOAD +Agent Sp‚cial 1 +# TXT_DELPHI +Voulez-vous recommencer cette mission ? +# TXT_TO_REPLAY +Reconnexion vers %s. +# TXT_RECONN_TO +Attendez %02d secondes. +# TXT_PLEASE_WAIT +Voulez-vous vous rendre ? +# TXT_SURRENDER +CHOIX DE LA TRANSMISSION +# TXT_SEL_TRANS +Les sauvegardes ne peuvent pas avoir le mˆme nom. +# TXT_GAMENAME_MUSTBE_UNIQUE +Partie ferm‚e. +# TXT_GAME_IS_CLOSED +Les noms doivent ˆtre tous diff‚rents. +# TXT_NAME_MUSTBE_UNIQUE +Reconnexion vers %s +# TXT_RECONNECTING_TO +Attente de connexions... +# TXT_WAITING_FOR_CONNECTIONS +Temps autoris‚ : %02d secondes +# TXT_TIME_ALLOWED +Appuyez sur Echap pour annuler. +# TXT_PRESS_ESC +De l'ordinateur : Il ne reste plus que toi et moi! +# TXT_JUST_YOU_AND_ME +Capture du drapeau : +# TXT_CAPTURE_THE_FLAG_COLON +Agent Sp‚cial 2 +# TXT_CHAN +%s s'est alli‚(e) avec %s +# TXT_HAS_ALLIED +%s d‚clare la guerre … %s +# TXT_AT_WAR +Choisissez un cible +# TXT_SEL_TARGET +Abandonner +# TXT_RESIGN +Le minerai pousse trŠs vite. +# TXT_TIBERIUM_FAST +R‚ponse en cours... +# TXT_ANSWERING +Initialisation Modem... +# TXT_INITIALIZING_MODEM +Les sc‚narios ne correspondent pas. +# TXT_SCENARIOS_DO_NOT_MATCH +Production d'‚nergie +# TXT_POWER_OUTPUT +Production d'‚nergie (faible) +# TXT_POWER_OUTPUT_LOW +Continuer +# TXT_CONTINUE +Saturation des donn‚es … envoyer +# TXT_QUEUE_FULL +%s a modifi‚ les options de jeu! +# TXT_SPECIAL_WARNING +Placez un CD d'Alerte Rouge dans le lecteur de CD-ROM. +# TXT_CD_DIALOG_1 +Placez le CD %d (%s) dans le lecteur de CD-ROM. +# TXT_CD_DIALOG_2 +Alerte Rouge n'a pas d‚tect‚ votre lecteur de CD-ROM. +# TXT_CD_ERROR1 +Pas de carte sonore d‚tect‚e +# TXT_NO_SOUND_CARD +INCONNU +# TXT_UNKNOWN +(ancien) +# TXT_OLD_GAME +Espace disque insuffisant pour lancer Alerte Rouge. +# TXT_NO_SPACE +Vous devez disposer de %d Mo sur votre disque dur. +# TXT_MUST_HAVE_SPACE +Lancez d'abord le programme SETUP. +# TXT_RUN_SETUP +Attente adversaire +# TXT_WAITING_FOR_OPPONENT +Choisissez l'option 'ParamŠtres' pour d‚finir la configuration par d‚faut +# TXT_SELECT_SETTINGS +Prison +# TXT_PRISON +Mission sauvegard‚e +# TXT_GAME_WAS_SAVED +Espace disque insuffisant pour sauvegarde. Effacez une ancienne sauvegarde pour lib‚rer de la place sur le disque dur et r‚essayez. +# TXT_SPACE_CANT_SAVE +Port/Adresse invalide. COM 1-4 OU ADDRESSE +# TXT_INVALID_PORT_ADDRESS +paramŠtres Port et/ou IRQ invalides +# TXT_INVALID_SETTINGS +IRQ d‚j… utilis‚ +# TXT_IRQ_ALREADY_IN_USE +Oui +# TXT_ABORT +Recommencer +# TXT_RESTART +Mission relanc‚e. Attendez SVP... +# TXT_RESTARTING +Chargement de la mission. Attendez SVP... +# TXT_LOADING +Erreur chaŒne d'initialisation modem +# TXT_ERROR_IN_INITSTRING +Ombre +# TXT_SHADOW_COLON +Mine Anti-V‚hicule +# TXT_AVMINE +Mine Anti-Personnel +# TXT_APMINE +Nouvelles missions +# TXT_NEW_MISSIONS +Voleur +# TXT_THIEF +Brouilleur de radar +# TXT_MRJ +G‚n‚rateur d'ombre +# TXT_GAP_GENERATOR +Bunker +# TXT_PILLBOX +Bunker camoufl‚ +# TXT_CAMOPILLBOX +ChronosphŠre +# TXT_CHRONOSPHERE +Roy. Uni +# TXT_ENGLAND +Allemagne +# TXT_GERMANY +Espagne +# TXT_SPAIN +URSS +# TXT_USSR +Ukraine +# TXT_UKRAINE +GrŠce +# TXT_GREECE +France +# TXT_FRANCE +Turquie +# TXT_TURKEY +Rivage +# TXT_SHORE +Choisir objet +# TXT_PLACE_OBJECT +Sous-marin +# TXT_SS +Contre-torpilleur +# TXT_DD +Croiseur +# TXT_CA +Transport +# TXT_TRANSPORT +Aviso-torpilleur +# TXT_PT +Hall +# TXT_LOBBY +Parties +# TXT_CHANNEL_GAMES +Sauvegarder partie... +# TXT_SAVING_GAME +La partie est au complet. +# TXT_GAME_FULL +Vous devez s‚lectionner une partie! +# TXT_MUST_SELECT_GAME +%s joue contre %s +# TXT_S_PLAYING_S +Seul l'h“te peut modifier cette option. +# TXT_ONLY_HOST_CAN_MODIFY +La partie a ‚t‚ annul‚e. +# TXT_GAME_CANCELLED +%s a initi‚ une nouvelle partie. +# TXT_S_FORMED_NEW_GAME +La partie de %s est maintenant en cours. +# TXT_GAME_NOW_IN_PROGRESS +Bobine de Tesla +# TXT_TESLA +G‚n‚rateur d'ombre mobile +# TXT_MGG +Tour lance-flammes +# TXT_FLAME_TURRET +Canon Anti-Avion +# TXT_AAGUN +Niche +# TXT_KENNEL +Centre Technique +# TXT_SOVIET_TECH +Bombardier +# TXT_BADGER +Mig +# TXT_MIG +Yak +# TXT_YAK +Barbel‚s +# TXT_FENCE +M‚decin +# TXT_MEDIC +Saboteur +# TXT_SABOTEUR +G‚n‚ral +# TXT_GENERAL +Tanya +# TXT_E7 +Parabombes +# TXT_PARA_BOMB +Parachutistes +# TXT_PARA_INFANTRY +Saboteur parachutiste +# TXT_PARA_SABOTEUR +Chantier naval +# TXT_SHIP_YARD +Port sous-marin +# TXT_SUB_PEN +Options Sc‚nario +# TXT_SCENARIO_OPTIONS +Avion espion +# TXT_SPY_MISSION +Avion espion +# TXT_U2 +Chien d'attaque +# TXT_GUARD_DOG +Info Espion +# TXT_SPY_INFO +Bƒtiments +# TXT_BUILDNGS +Unit‚s +# TXT_UNITS +Infanterie +# TXT_INFANTRY +Avion +# TXT_AIRCRAFT +Camion d'approvisionnement +# TXT_TRUCK +Module d'invuln‚rabilit‚ +# TXT_INVUL +Rideau de Fer +# TXT_IRON_CURTAIN +Centre technique avanc‚ +# TXT_ADVANCED_TECH +Lance-roquettes V2 +# TXT_V2_LAUNCHER +Poste de commandement avanc‚ +# TXT_FORWARD_COM +Bombardeur +# TXT_DEMOLITIONER +Poseur de mines +# TXT_MINE_LAYER +Chantier de construction leurre +# TXT_FAKE_CONST +Usine d'armement leurre +# TXT_FAKE_WEAP +Chantier naval leurre +# TXT_FAKE_YARD +Port sous-marin leurre +# TXT_FAKE_PEN +D“me radar leurre +# TXT_FAKE_RADAR +Bigfoot +# TXT_THEME_BIGF +La r‚volte +# TXT_THEME_CRUS +A l'attaque 1 +# TXT_THEME_FAC1 +A l'attaque 2 +# TXT_THEME_FAC2 +Marche de l'enfer +# TXT_THEME_HELL +Sauve-qui-peut +# TXT_THEME_RUN1 +La d‚bƒcle +# TXT_THEME_SMSH +Tranch‚es +# TXT_THEME_TREN +Les professionnels +# TXT_THEME_WORK +Attente +# TXT_THEME_AWAIT +Dense +# TXT_THEME_DENSE_R +S‚lection carte +# TXT_THEME_MAP +Fogger +# TXT_THEME_FOGGER1A +Boue +# TXT_THEME_MUD1A +Radio 2 +# TXT_THEME_RADIO2 +Laminage +# TXT_THEME_ROLLOUT +Serpent +# TXT_THEME_SNAKE +Extermination +# TXT_THEME_TERMINAT +Jumeau +# TXT_THEME_TWIN +Vecteur +# TXT_THEME_VECTOR1A +Equipiers +# TXT_TEAM_MEMBERS +Pont +# TXT_BRIDGE +Baril +# TXT_BARREL +Amical +# TXT_GOODGUY +Ennemi +# TXT_BADGUY +Or +# TXT_GOLD +Gemmes +# TXT_GEMS +Film titre +# TXT_TEASER +Films +# TXT_MOVIES +Int‚rieur +# TXT_INTERIOR +Signal sonar +# TXT_SONAR_PULSE +Silo de missiles +# TXT_MSLO +Satellite GPS +# TXT_GPS_SATELLITE +Bombe atomique +# TXT_NUCLEAR_BOMB +Facile +# TXT_EASY +Difficile +# TXT_HARD +Normal +# TXT_NORMAL +S‚lectionnez un niveau de difficult‚. +Il sera valable tout au long de votre campagne. +# TXT_DIFFICULTY +Alli‚s +# TXT_ALLIES +Soviets +# TXT_SOVIET +ThŠme Intro +# TXT_THEME_INTRO +Progr. ombre +# TXT_SHADOW_REGROWS +Progr. minerai +# TXT_ORE_SPREADS +Musiques +# TXT_THEME_SCORE +Internet +# TXT_INTERNET +Glace +# TXT_ICE +Caisses +# TXT_CRATE +Escarmouche +# TXT_SKIRMISH +Choisissez votre camp. +# TXT_CHOOSE +Min‚raux pr‚cieux +# TXT_MINERALS +Ignorer +# TXT_IGNORE +Erreur - le modem ne r‚pond pas. +# TXT_ERROR_NO_RESP +Erreur - Le modem n'a pas r‚pondu … l'activation du code de r‚sultat. +# TXT_ERROR_NO_RESCODE +Erreur - Le modem n'a pas r‚pondu … la chaŒne d'initialisation. +# TXT_ERROR_NO_INIT +Erreur - Le modem n'a pas r‚pondu … l'activation des codes de r‚sultat d‚taill‚s. +# TXT_ERROR_NO_VERB +Erreur - Le modem n'a pas r‚pondu … la commande du mode '‚cho'. +# TXT_ERROR_NO_ECHO +Erreur - Impossible de d‚sactiver r‚ponse auto du modem. +# TXT_ERROR_NO_DISABLE +Erreur - Trop d'erreurs lors de l'initialisation du modem - Interruption. +# TXT_ERROR_TOO_MANY +Erreur - Le modem a retourn‚ une erreur de param‚trage. +# TXT_ERROR_ERROR +Erreur - Temps d'attente de connexion d‚pass‚. +# TXT_ERROR_TIMEOUT +Accompli +# TXT_ACCOMPLISHED +Cliquez pour continuer +# TXT_CLICK_CONTINUE +R‚ception du sc‚nario de l'h“te. +# TXT_RECEIVING_SCENARIO +Envoi du sc‚nario aux joueurs … distance. +# TXT_SENDING_SCENARIO +Erreur - Le modem n'a pas r‚pondu … la commande de contr“le de flux. Votre configuration de Windows peut ˆtre incorrecte. +# TXT_NO_FLOW_CONTROL_RESPONSE +Erreur - Le modem n'a pas r‚pondu … la d‚sactivation du protocole de compression. Votre configuration de Windows peut ˆtre incorrecte. +# TXT_NO_COMPRESSION_RESPONSE +Erreur - Le modem n'a pas r‚pondu … la d‚sactivation du protocole de correction d'erreurs. Votre configuration de Windows peut ˆtre incorrecte. +# TXT_NO_ERROR_CORRECTION_RESPONSE +Pour jouer … Alerte Rouge via Internet, vous devez ˆtre connect‚ … un serveur Internet et ˆtre inscrit … Planet Westwood. +# TXT_EXPLAIN_REGISTRATION +Erreur - Impossible d'ex‚cuter WChat. +# TXT_ERROR_UNABLE_TO_RUN_WCHAT +Enregistrer +# TXT_REGISTER +Gisement Minerai +# TXT_ORE_MINE +Aucun modem configur‚ +# TXT_NO_REGISTERED_MODEM +D‚placement chronoporte +# TXT_CHRONOSHIFT +Adresse invalide ou en cours d'utilisation +# TXT_UNABLE_TO_OPEN_PORT +Pas de tonalit‚. Assurez-vous que votre modem est connect‚ … la ligne t‚l‚phonique et r‚essayez. +# TXT_NO_DIAL_TONE +Erreur - L'autre joueur n'a pas ce sc‚nario d'extension. +# TXT_NO_EXPANSION_SCENARIO +Patientez SVP... +# TXT_STAND_BY +Musique du g‚n‚rique de fin +# TXT_THEME_CREDITS +Puissance faible: Canon(s) AA neutralis‚(s) +# TXT_POWER_AAGUN +Puissance faible: Bobine(s) de Tesla neutralis‚e(s) +# TXT_POWER_TESLA +Puissance Faible +# TXT_LOW_POWER +Commandant: +# TXT_COMMANDER +Parties gagn‚es: +# TXT_BATTLES_WON +Fichier de donn‚es du jeu d‚tect‚ incompatible. Un fichier de donn‚es du jeu est peut-ˆtre corrompu. +# TXT_MISMATCH +Votre version de jeu n‚cessite une mise … jour. Vous pouvez t‚l‚charger la derniŠre mise … jour sur notre site Web : WWW.WESTWOOD.COM. +#TXT_SCENARIO_ERROR +Connecting +# TXT_CONNECTING +Initialisation du Modem +# TXT_MODEM_INITIALISATION +Compression des Donn‚es +# TXT_DATA_COMPRESSION +Correction d'Erreur +# TXT_ERROR_CORRECTION +Contr“le Mat‚riel du Flux +# TXT_HARDWARE_FLOW_CONTROL +Avanc‚ +# TXT_ADVANCED +Seconde main +# TXT_THEME_2ND_HAND +Arazo‹de +# TXT_THEME_ARAZOID +Retour … l'envoyeur +# TXT_THEME_BACKSTAB +Chaos2 +# TXT_THEME_CHAOS2 +Fermez-la ! +# TXT_THEME_SHUT_IT +Visite de courtoisie +# TXT_THEME_TWINMIX1 +A couvert +# TXT_THEME_UNDER3 +VR2 +# TXT_THEME_VR2 +L'autre systŠme ne r‚pond pas. Voulez-vous tenter une sauvegarde d'urgence ? Tous les joueurs doivent sauvegarder pour que cela fonctionne. +# TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING +L'autre systŠme a raccroch‚. Voulez-vous tenter une sauvegarde d'urgence ? Tous les joueurs doivent sauvegarder pour que cela fonctionne. +# TXT_ASK_EMERGENCY_SAVE_HUNG_UP +Alerte Rouge n'a pu ex‚cuter le programme d'enregistrement. Vous devez installer Westwood Chat … partir du CD d'Alerte Rouge pour vous enregistrer. +# TXT_NO_REG_APP +Un joueur de la partie n'a pas son sc‚nario d'extension. +# TXT_NO_CS_SCENARIOS +Sous-marin MS +# TXT_MISSILESUB +Electrocuteur +# TXT_SHOCKTROOPER +M‚canicien +# TXT_MECHANIC +Chrono Tank +# TXT_CHRONOTANK +Tank Tesla +# TXT_TESLATANK +Tank M.A.D. +# TXT_MAD +Camion de d‚molition +# TXT_DEMOTRUCK +Transport Cam‚l‚on +# TXT_PHASETRANSPORT +Mar‚cages +# TXT_THEME_BOG +Volutes +# TXT_THEME_FLOAT_V2 +T‚nŠbres +# TXT_THEME_GLOOM +Terrain min‚ +# TXT_THEME_GRNDWIRE +M‚caniciens 2 +# TXT_THEME_RPT +Battue +# TXT_THEME_SEARCH +Traction +# TXT_THEME_TRACTION +Chaos +# TXT_THEME_WASTELND +H‚liport Mobile +# TXT_CARRIER + diff --git a/CODE/ENG/FRE/CREDITS.TXT b/CODE/ENG/FRE/CREDITS.TXT new file mode 100644 index 0000000..6599b78 --- /dev/null +++ b/CODE/ENG/FRE/CREDITS.TXT @@ -0,0 +1,246 @@ + Producteur Ex‚cutif Brett W. Sperry + + Producteur Lewis S. Peterson + + Programmeur Barry Green + + Direction Technique Steve Wetherill, Eric Wang + + Concepteurs Cartes Jeu Solo John Archer, Patrick Connelly, + Adam Isgreen, Michael Lightner, + Erik Yeo + + Concepteurs Cartes Multijoueurs John Archer, Stuart Bailey, + Nigel Berryman, D'Andre Campbell, + Valerie Carpentier, Cathi Diet, + Keith Ditchburn, Gareth Eke, + Darren Esp, Simon Evers, Rod Gray, + Randy Greenback, Matthew Howe, + Levi Luke, Suzanne Maddison, + Iain McNeil, Graeme Miller, + Lee Morse, David Parsons, + Alex Scarrow, Robert Scheel, + Lawrence So, John Sweeney, + Matthew Tillett, Phillip Veale + + Graphistes Chris Demers, Matthew Hansel, + Joseph B. Hewitt IV + + Direction Audio Paul S. Mudra + + Musique Frank Klepacki + + Effets Sonores Dwight K. Okahara + + Direction AQ Glenn Sperry + + Assurance Qualit‚ Jim Adkins, Lloyd Bell, + Chris Blevens, D'Andre Campbell, + Errol Campbell, Shane Dietrich, + Randy Greenback, Jon Hix, + Mark Laity, Troy Leonard, + DeMarlo Lewis, Levi Luke, + Patrick Offord, Richard Rasmussen, + Mike Ruppert, Steve Shockey, + Mike Smith, Albert Springfield + + Nouvelles Voix Fran‡aises Emmanuel Curtil, Gilbert L‚vy + + + Alerte Rouge + + Direction de production Brett W. Sperry + Producteur Ed Del Castillo + + Concept original Brett W. Sperry + Joe Bostic + + Histoire originale Ron Smith + Ed Del Castillo + + Programmeurs principaux Joe Bostic + Barry Green + Steve Tall + + Programmeurs Bill Randolph + Phil Gorrow + Maria del Mar McCready Legg + + Programmeurs Internet Jeff Brown + David Aldridge + Matt Thorn + + Programmeur traduction Victor Grippi + + Direction technique Steve Wetherill + Eric Wang + + Cr‚ateurs Brett W. Sperry + Adam Isgreen + Michael Lightner + Erik Yeo + + Artistes principaux Chris Demers + Matthew Hansel + Joseph B. Hewitt IV + + Artistes Paul Wesberry + David T. Potter + Damon Redmond + Brian White + + Sc‚nario Ron Smith + Adam Isgreen + John Scott Lewinski + + Post Prod. Vid‚o Felix Kupis + Kevin Becquet + + Compression Vid‚o Tim C. Fritz + + Asst. Compression Vid‚o Patrick Connelly + + Direction du son Paul S. Mudra + Musique Frank Klepacki + Effets sonores Dwight K. Okahara + + Direction du CQ Glenn Sperry + + Contr“le-qualit‚ Lloyd Bell + Kenny Dunne + Mike Smith + John Archer + Chris Rubyor + D'Andre Cambell + Robert Scheel + Chris Bleven + Levi Luke + Errol Campbell + Abe Hernandez + James Adkins + Richard Rasmussen + Troy Leonard + Ben Lublin + Chris Holloway + Isaiah Myers + Phillip Castro + Albert Springfield + Randy Greenback + Tyler Thackery + + Conception du manuel Victoria Hart + + + + Un grand merci … Danielle Woodyatt, Rosemarie Dalton, + et aux ‚quipes Virgin en Europe. Egalement un grand merci … + Tracy Chapman pour sa chanson "a new beginning". + + + + + Les acteurs + + + camp Alli‚s + + Von Esling Arthur Roberts + Stavros Barry Kramer + Tanya Lynne Litteer + Interrogateur Dom Magwili + Annonceur Gwen Castaldi + + ...et John Milford pour Albert Einstein + + + Soldats alli‚s Ricky Russell + Nick Paulos + Scott Ryan Talley + Joe Bostic + Chris Demers + Barry Green + Matthew Hansel + Adam Isgreen + Frank Klepacki + Mike Lightner + Bill Randolph + Philip E. Shelburne + Eric Wang + + + + camp Soviets + + Kukov Craig Cavanah + Gradenko Alan Terry + Nadia Andrea Robinson + Kane Joe Kucan + + ...et Eugene Dynarski pour Josef Stalin + + + Soldats sovi‚tiques Felix Kupis + Erik Yeo + John Archer + Mike Grayford + Chris Rubyor + + + Voix dans le jeu + + E.V.A. Martin Alper + Tanya Lanae Freeborn + Infanterie Mike Grayford + Adam Isgreen + Troy Leonard + Chris Rubyor + Dwight Okahara + Frank Klepacki + Eric Wang + + Equipe de production + + + R‚alisation Joseph D. Kucan + + Conception des costumes Christie Moeller + + Maquillage & Coiffure Cindy Cline + + Ing‚nieur du son Paul S. Mudra + + Casting Marilee Lear, C.S.A. + + Vid‚o Kevin Becquet + + Eclairage Eric Gooch + + Production principale + + Premier Assistant Paul Bastardo + + Assistants de Production Pat Connelly + Richard Rasmussen + Rick Appin + Jeff Fillhaber + Patience Becquet + + Maquillage Sfx Philip E. Shelburne + + + S‚quences sp‚ciales + + Avec la permission de NBC News Archives Films + Fabulous Footage + + + + + Pour obtenir les derniŠres informations, mises … jour + et plein d'autres surprises, visitez notre site Web : + www.westwood.com + + + + + diff --git a/CODE/ENG/GER/CONQUER.TXT b/CODE/ENG/GER/CONQUER.TXT new file mode 100644 index 0000000..90dbccc --- /dev/null +++ b/CODE/ENG/GER/CONQUER.TXT @@ -0,0 +1,1113 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Zeit: %02d:%02d:%02d +# TXT_TIME_FORMAT_HOURS +Zeit: %02d:%02d +# TXT_TIME_FORMAT_NO_HOURS +Verkaufen +# TXT_BUTTON_SELL +Verkaufen +# TXT_SELL +Reparieren +# TXT_BUTTON_REPAIR +Sie: +# TXT_YOU +Gegner: +# TXT_ENEMY +Geb„ude vernichtet durch +# TXT_BUILD_DEST +Einheiten zerst”rt durch +# TXT_UNIT_DEST +Erz gesammelt durch +# TXT_TIB_HARV +Punkte: %d +# TXT_SCORE_1 +Ja +# TXT_YES +Nein +# TXT_NO +Einsatz erfolgreich +# TXT_SCENARIO_WON +Einsatz fehlgeschlagen +# TXT_SCENARIO_LOST +Neues Spiel starten +# TXT_START_NEW_GAME +Vorschau & Intro +# TXT_INTRO +Zurck +# TXT_CANCEL +Felsen +# TXT_ROCK +Technobot +# TXT_CIVILIAN +Entseuchungstrupp +# TXT_JP +Ok +# TXT_OK +Baum +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Frei +# TXT_CLEAR +Wasser +# TXT_WATER +Strasse +# TXT_ROAD +Hgel +# TXT_SLOPE +Schlamm +# TXT_PATCH +Fluss +# TXT_RIVER +Einsatz laden +# TXT_LOAD_MISSION +Einsatz sichern +# TXT_SAVE_MISSION +Einsatz l”schen +# TXT_DELETE_MISSION +Laden +# TXT_LOAD_BUTTON +Sichern +# TXT_SAVE_BUTTON +L”schen +# TXT_DELETE_BUTTON +Spielsteuerung +# TXT_GAME_CONTROLS +Soundsteuerung +# TXT_SOUND_CONTROLS +Zurck +# TXT_RESUME_MISSION +Optische Steuerung +# TXT_VISUAL_CONTROLS +Einsatz abbrechen +# TXT_QUIT_MISSION +Spiel verlassen +# TXT_EXIT_GAME +Optionen +# TXT_OPTIONS +šberreste +# TXT_SQUISH +Krater +# TXT_CRATER +Brandspuren +# TXT_SCORCH +Helligkeit: +# TXT_BRIGHTNESS +Musiklautst„rke +# TXT_MUSIC +Ger„uschlautst„rke +# TXT_VOLUME +T”nung: +# TXT_TINT +Kontrast: +# TXT_CONTRAST +Spielgeschwindigkeit +# TXT_SPEED +Scrollgeschwindigkeit: +# TXT_SCROLLRATE +Farbe: +# TXT_COLOR +Zurck zum Spiel +# TXT_RETURN_TO_GAME +Gegnerischer Cyborg +# TXT_ENEMY_SOLDIER +Gegnerisches Fahrzeug +# TXT_ENEMY_VEHICLE +Gegnerisches Geb„ude +# TXT_ENEMY_STRUCTURE +Leichter Panzer +# TXT_LTANK +Schwerer Panzer +# TXT_MTANK +Kampfpanzer +# TXT_MTANK2 +Mammutpanzer +# TXT_HTANK +Flak-Stellung +# TXT_SAM +Ranger 4WD Gel„ndewagen +# TXT_JEEP +Helitrans +# TXT_TRANS +Erztransporter +# TXT_HARVESTER +Artillerie +# TXT_ARTY +Cyborg "Schtze" +# TXT_E1 +Cyborg "Grenadier" +# TXT_E2 +Cyborg "Rak-Zero" +# TXT_E3 +Cyborg "Flammenwerfer" +# TXT_E4 +Kampfhelikopter +# TXT_HELI +Hind-Hubschrauber +# TXT_ORCA +BMT +# TXT_APC +Wachturm +# TXT_GUARD_TOWER +Radar +# TXT_COMMAND +Heliport +# TXT_HELIPAD +Flugfeld +# TXT_AIRSTRIP +Erz-Silo +# TXT_STORAGE +Bauhof +# TXT_CONST_YARD +Erz-Raffinerie +# TXT_REFINERY +Kirche +# TXT_CIV1 +Schmidts Wohnung +# TXT_CIV2 +Anwesen der Hewitts +# TXT_CIV3 +Haus von Kerstin +# TXT_CIV4 +Bastis Zuhause +# TXT_CIV5 +Docs Scheune +# TXT_CIV6 +Damons Kneipe +# TXT_CIV7 +Die Farm +# TXT_CIV8 +Musikfabrik +# TXT_CIV9 +Spielzeugladen +# TXT_CIV10 +Ludwigs Haus +# TXT_CIV11 +Heustapel +# TXT_CIV12 +Heustapel +# TXT_CIV13 +Weizenfeld +# TXT_CIV14 +Brachland +# TXT_CIV15 +Maisfeld +# TXT_CIV16 +Selleriefelder +# TXT_CIV17 +Kartoffelfeld +# TXT_CIV18 +Salas Haus +# TXT_CIV20 +Abduls Haus +# TXT_CIV21 +Pablos Verrufene Pinte +# TXT_CIV22 +Dorfbrunnen +# TXT_CIV23 +Kamelh„ndler +# TXT_CIV24 +Kirche +# TXT_CIV25 +Alis Haus +# TXT_CIV26 +Kr„mer Ted +# TXT_CIV27 +Meneliks Heim +# TXT_CIV28 +Prestor Johns Haus +# TXT_CIV29 +Sch”pfbrunnen +# TXT_CIV30 +Htte des Medizinmanns +# TXT_CIV31 +Rikitikitembos Htte +# TXT_CIV32 +Roarkes Htte +# TXT_CIV33 +Mubasas Htte +# TXT_CIV34 +Aksums Htte +# TXT_CIV35 +Mambos Htte +# TXT_CIV36 +Das Studio +# TXT_CIV37 +Technologiezentrum +# TXT_CIVMISS +Geschtzturm +# TXT_TURRET +Rak-Kreuzer +# TXT_GUNBOAT +Mobiles Baufahrzeug +# TXT_MCV +Kraftwerk +# TXT_POWER +Grosskraftwerk +# TXT_ADVANCED_POWER +Krankenhaus +# TXT_HOSPITAL +Cyborgfabrik +# TXT_BARRACKS +™lf”rderpumpe +# TXT_PUMP +™ltanker +# TXT_TANKER +Sandsack-Barriere +# TXT_SANDBAG_WALL +Maschendrahtzaun +# TXT_CYCLONE_WALL +Betonmauer +# TXT_BRICK_WALL +Stacheldrahtzaun +# TXT_BARBWIRE_WALL +Lattenzaun +# TXT_WOOD_WALL +Waffenfabrik +# TXT_WEAPON_FACTORY +Befestigungsturm +# TXT_AGUARD_TOWER +Bio-Forschungslabor +# TXT_BIO_LAB +Werkstatt +# TXT_FIX_IT +Seitenmen +# TXT_TAB_SIDEBAR +Optionen +# TXT_TAB_BUTTON_CONTROLS +Datenbank +# TXT_TAB_BUTTON_DATABASE +Unbekanntes Terrain +# TXT_SHADOW +Optionen-Men +# TXT_OPTIONS_MENU +STOP +# TXT_STOP +SPIEL +# TXT_PLAY +ZUFALL +# TXT_SHUFFLE +ENDLOS +# TXT_REPEAT +Musiklautst„rke: +# TXT_MUSIC_VOLUME +Ger„uschlautst„rke: +# TXT_SOUND_VOLUME +An +# TXT_ON +Aus +# TXT_OFF +Mehrspieler-Modus +# TXT_MULTIPLAYER_GAME +Keine Dateien verfgbar +# TXT_NO_FILES +Wollen Sie diese Datei l”schen? +# TXT_DELETE_SINGLE_FILE +Wollen Sie %d Dateien l”schen? +# TXT_DELETE_MULTIPLE_FILES +Standardwerte +# TXT_RESET_MENU +Wollen Sie den Einsatz abbrechen? +# TXT_CONFIRM_EXIT +Einsatzbeschreibung +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Barry +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Karen +# TXT_C5 +Steve +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Prof. Einstein +# TXT_EINSTEIN +Strasse +# TXT_BIB +Schnell +# TXT_FASTER +Langsam +# TXT_SLOWER +Luftangriff +# TXT_AIR_STRIKE +Stahlbeh„lter +# TXT_STEEL_CRATE +Holzkiste +# TXT_WOOD_CRATE +Wasserbeh„lter +# TXT_WATER_CRATE +Flagge +# TXT_FLAG_SPOT +Kann Szenario nicht lesen! +# TXT_UNABLE_READ_SCENARIO +Fehler beim Laden des Spieles! +# TXT_ERROR_LOADING_GAME +šberholter gespeicherter Spielstand. +# TXT_OBSOLETE_SAVEGAME +Sie mssen eine Beschreibung eingeben! +# TXT_MUSTENTER_DESCRIPTION +Fehler beim Sichern des Spieles! +# TXT_ERROR_SAVING_GAME +Diese Datei l”schen? +# TXT_DELETE_FILE_QUERY +[NOCH FREI] +# TXT_EMPTY_SLOT +Mehrspielermodus w„hlen +# TXT_SELECT_MPLAYER_GAME +Modem/Seriell +# TXT_MODEM_SERIAL +Netzwerk +# TXT_NETWORK +Kann Netzwerk nicht initialisieren! +# TXT_INIT_NET_ERROR +Im Netzwerk mitspielen +# TXT_JOIN_NETWORK_GAME +Neu +# TXT_NEW +Dazu +# TXT_JOIN +Nachricht senden +# TXT_SEND_MESSAGE +Ihr Name: +# TXT_YOUR_NAME +Seite: +# TXT_SIDE_COLON +Farbe: +# TXT_COLOR_COLON +Spielrunden +# TXT_GAMES +Spieler +# TXT_PLAYERS +Szenario: +# TXT_SCENARIO_COLON +>> NICHT GEFUNDEN << +# TXT_NOT_FOUND +Geld beim Spielstart: +# TXT_START_CREDITS_COLON +Sttzpunkte: +# TXT_BASES_COLON +Erz: +# TXT_TIBERIUM_COLON +Kisten: +# TXT_CRATES_COLON +KI-Spieler: +# TXT_AI_PLAYERS_COLON +Anfrage wurde abgelehnt. +# TXT_REQUEST_DENIED +Kann nicht starten; Szenario nicht gefunden. +# TXT_UNABLE_PLAY_WAAUGH +Niemand da zum Mitspielen! +# TXT_NOTHING_TO_JOIN +Sie mssen einen Namen eingeben! +# TXT_NAME_ERROR +Doppelte Namen sind nicht erlaubt. +# TXT_DUPENAMES_NOTALLOWED +Ihre Spielversion ist zu alt und berholt. +# TXT_YOURGAME_OUTDATED +Gegnerische Spielversion ist zu alt und berholt. +# TXT_DESTGAME_OUTDATED +Das Spiel von %s +# TXT_THATGUYS_GAME +[Das Spiel von %s] +# TXT_THATGUYS_GAME_BRACKET +Netzwerk-Spieleinstellungen +# TXT_NETGAME_SETUP +Ablehnen +# TXT_REJECT +He, Du kannst Dich doch nicht selbst ablehnen! Msstest Du doch langsam wissen... +# TXT_CANT_REJECT_SELF +Sie mssen schon aussuchen, wen Sie ablehnen wollen... +# TXT_SELECT_PLAYER_REJECT +Sttzpunkte +# TXT_BASES +Kisten +# TXT_CRATES +KI-Spieler +# TXT_AI_PLAYERS +Szenarien +# TXT_SCENARIOS +Geld: +# TXT_CREDITS_COLON +Nur ein einsamer Spieler? +# TXT_ONLY_ONE +Uuups! +# TXT_OOPS +An %s: +# TXT_TO +An alle: +# TXT_TO_ALL +Nachricht: +# TXT_MESSAGE +Verbindung mit %s abgebrochen! +# TXT_CONNECTION_LOST +%s ist aus dem Spiel ausgestiegen. +# TXT_LEFT_GAME +%s wurde besiegt! +# TXT_PLAYER_DEFEATED +Warte auf Verbindung... +# TXT_WAITING_CONNECT +Verbindungsfehler!\rBitte Kabel berprfen.\rVersuche neue Verbindung... +# TXT_NULL_CONNERR_CHECK_CABLES +Verbindungsabbruch!\rW„hle neu an... +# TXT_MODEM_CONNERR_REDIALING +Verbindungsabbruch!\rWarte auf Anruf... +# TXT_MODEM_CONNERR_WAITING +W„hle seriellen Modus +# TXT_SELECT_SERIAL_GAME +Anrufen +# TXT_DIAL_MODEM +Anruf annehmen +# TXT_ANSWER_MODEM +Null-Modem +# TXT_NULL_MODEM +Einstellungen +# TXT_SETTINGS +Port: +# TXT_PORT_COLON +IRQ: +# TXT_IRQ_COLON +Baud: +# TXT_BAUD_COLON +Init.-Sequenz: +# TXT_INIT_STRING +"Call Waiting"-Sequenz: +# TXT_CWAIT_STRING +Tonwahl +# TXT_TONE_BUTTON +Impulswahl +# TXT_PULSE_BUTTON +Serielles Spiel - Host +# TXT_HOST_SERIAL_GAME +Gegner: +# TXT_OPPONENT_COLON +User hat sich abgemeldet! +# TXT_USER_SIGNED_OFF +Serielles Spiel - Mitspieler +# TXT_JOIN_SERIAL_GAME +Telefonbuch +# TXT_PHONE_LIST +Dazu +# TXT_ADD +Žndern +# TXT_EDIT +W„hlen +# TXT_DIAL +Standard +# TXT_DEFAULT +Standardeinstellungen +# TXT_DEFAULT_SETTINGS +Eigene Einstellungen +# TXT_CUSTOM_SETTINGS +Telefonbuch-Eintrag +# TXT_PHONE_LISTING +Name: +# TXT_NAME_COLON +Nummer: +# TXT_NUMBER_COLON +Kann Modem nicht finden.\rBitte Netzanschluss und Kabel berprfen. +# TXT_UNABLE_FIND_MODEM +Kein Tr„gersignal. +# TXT_NO_CARRIER +Besetzt. +# TXT_LINE_BUSY +Ungltige Nummer. +# TXT_NUMBER_INVALID +Der andere Rechner antwortet nicht! +# TXT_SYSTEM_NOT_RESPONDING +Das Spiel l„uft nicht mehr synchron! +# TXT_OUT_OF_SYNC +Datenpaket zu sp„t angekommen! +# TXT_PACKET_TOO_LATE +Der andere Spieler hat das Spiel beendet. +# TXT_PLAYER_LEFT_GAME +Von %s: %s +# TXT_FROM +ZEIT: +# TXT_SCORE_TIME +FšHRUNG: +# TXT_SCORE_LEAD +EFFIZIENZ: +# TXT_SCORE_EFFI +GESAMTPUNKTE: +# TXT_SCORE_TOTA +VERLUSTE: +# TXT_SCORE_CASU +NEUTRAL: +# TXT_SCORE_NEUT +GEBŽUDE VERLOREN +# TXT_SCORE_BUIL +GEBŽUDE +# TXT_SCORE_BUIL1 +VERLOREN: +# TXT_SCORE_BUIL2 +DIE BESTEN +# TXT_SCORE_TOP +GELD BEI SPIELENDE: +# TXT_SCORE_ENDCRED +%ds %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +W„hle an... +# TXT_DIALING +Anwahl abgebrochen +# TXT_DIALING_CANCELED +Warte auf Anruf... +# TXT_WAITING_FOR_CALL +Annehmen abgebrochen +# TXT_ANSWERING_CANCELED +Invasor +# TXT_E6 +Spion +# TXT_E8 +Kein Nullmodem-Kabel angeschlossen! Das ist ein Modem- oder Schleifenkabel. +# TXT_MODEM_OR_LOOPBACK +Karte +# TXT_MAP +Bltenbaum +# TXT_BLOSSOM_TREE +Einsatzziel +# TXT_RESTATE_MISSION +Computer +# TXT_COMPUTER +Einheiten: +# TXT_COUNT +Tech.-Stufe: +# TXT_LEVEL +Gegner +# TXT_OPPONENT +Zerst”rt: +# TXT_KILLS_COLON +Video +# TXT_VIDEO +Forscher +# TXT_C10 +Erobere die Flagge +# TXT_CAPTURE_THE_FLAG +Einsatzziel +# TXT_OBJECTIVE +Einsatz +# TXT_MISSION +Keine gespeicherten Spielst„nde! +# TXT_NO_SAVES +Geb„ude +# TXT_CIVILIAN_BUILDING +Technobot +# TXT_TECHNICIAN +Im Mehrspielermodus ist kein Zugriff auf gespeicherte Spielst„nde gestattet. +# TXT_NO_SAVELOAD +Spezialagent +# TXT_DELPHI +Wollen Sie diesen Einsatz neu beginnen? +# TXT_TO_REPLAY +Neue Verbindung mit %s. +# TXT_RECONN_TO +Bitte warten Sie %02d Sekunden. +# TXT_PLEASE_WAIT +Wollen Sie etwa kapitulieren? +# TXT_SURRENDER +Nachricht w„hlen +# TXT_SEL_TRANS +Der Spielname darf nur einmal vorkommen. +# TXT_GAMENAME_MUSTBE_UNIQUE +Das Spiel ist geschlossen. +# TXT_GAME_IS_CLOSED +Ihr Name darf nur einmal vorkommen. +# TXT_NAME_MUSTBE_UNIQUE +Neue Verbindung mit %s +# TXT_RECONNECTING_TO +Warte auf Verbindungen... +# TXT_WAITING_FOR_CONNECTIONS +Erlaubte Wartezeit: %02d Sekunden +# TXT_TIME_ALLOWED +ESC drcken, um abzubrechen +# TXT_PRESS_ESC +Vom Rechner: Jetzt gibt's nur noch uns beide! +# TXT_JUST_YOU_AND_ME +Erobere die Flagge: +# TXT_CAPTURE_THE_FLAG_COLON +Spezialagent +# TXT_CHAN +%s hat sich mit %s verbndet +# TXT_HAS_ALLIED +%s erkl„rt %s den Krieg. +# TXT_AT_WAR +Ziel w„hlen +# TXT_SEL_TARGET +Aufgeben +# TXT_RESIGN +Erz w„chst schnell +# TXT_TIBERIUM_FAST +Antworte... +# TXT_ANSWERING +Initialisiere Modem... +# TXT_INITIALIZING_MODEM +Szenarien stimmen nicht berein. +# TXT_SCENARIOS_DO_NOT_MATCH +Energieleistung +# TXT_POWER_OUTPUT +Energieleistung (niedrig) +# TXT_POWER_OUTPUT_LOW +Weiter +# TXT_CONTINUE +Daten-Warteschlange zu voll +# TXT_QUEUE_FULL +%s hat die Spieloptionen ge„ndert! +# TXT_SPECIAL_WARNING +Bitte legen Sie eine 'Alarmstufe Rot'-CD in das CD-ROM-Laufwerk. +# TXT_CD_DIALOG_1 +Bitte legen Sie die CD %d (%s) in das CD-ROM-Laufwerk. +# TXT_CD_DIALOG_2 +Alarmstufe Rot kann Ihr CD-ROM-Laufwerk nicht finden. +# TXT_CD_ERROR1 +Keine Soundkarte entdeckt +# TXT_NO_SOUND_CARD +UNBEKANNT +# TXT_UNKNOWN +(berholt) +# TXT_OLD_GAME +Zuwenig Festplattenplatz fr Alarmstufe Rot. +# TXT_NO_SPACE +Sie brauchen %d Megabytes freien Festplattenplatz. +# TXT_MUST_HAVE_SPACE +Starten Sie erst das SETUP-Programm. +# TXT_RUN_SETUP +Warte auf Gegner +# TXT_WAITING_FOR_OPPONENT +Bitte w„hlen Sie 'Einstellungen', um die Standard-Konfiguration einzustellen. +# TXT_SELECT_SETTINGS +Gef„ngnis +# TXT_PRISON +Einsatz gespeichert +# TXT_GAME_WAS_SAVED +Zuwenig Festplattenplatz zum Speichern des Spielstandes. Bitte einen frher gesicherten Spielstand l”schen, um Platz freizumachen, und erneut versuchen. +# TXT_SPACE_CANT_SAVE +Port/Adresse ungltig. COM 1-4 ODER ADRESSE +# TXT_INVALID_PORT_ADDRESS +Port und/oder IRQ-Einstellung ungltig. +# TXT_INVALID_SETTINGS +IRQ bereits belegt +# TXT_IRQ_ALREADY_IN_USE +Abbruch +# TXT_ABORT +Neustart +# TXT_RESTART +Einsatz startet neu. Bitte warten... +# TXT_RESTARTING +Einsatz wird geladen. Bitte warten... +# TXT_LOADING +Fehler in der Init-Sequenz +# TXT_ERROR_IN_INITSTRING +Schatten: +# TXT_SHADOW_COLON +Panzermine +# TXT_AVMINE +Tretmine +# TXT_APMINE +Neue Eins„tze +# TXT_NEW_MISSIONS +D.I.E.B.-Einheit +# TXT_THIEF +Radarst”rger„t +# TXT_MRJ +Schattengenerator +# TXT_GAP_GENERATOR +Bunker +# TXT_PILLBOX +Tarnbunker +# TXT_CAMOPILLBOX +Chronosph„re +# TXT_CHRONOSPHERE +England +# TXT_ENGLAND +Deutschland +# TXT_GERMANY +Spanien +# TXT_SPAIN +Sowjetunion +# TXT_USSR +Ukraine +# TXT_UKRAINE +Griechenland +# TXT_GREECE +Frankreich +# TXT_FRANCE +Trkei +# TXT_TURKEY +Ufer +# TXT_SHORE +Gegenstand ausw„hlen +# TXT_PLACE_OBJECT +U-Boot +# TXT_SS +Zerst”rer +# TXT_DD +Schlachtkreuzer +# TXT_CA +Transporter +# TXT_TRANSPORT +Kanonenboot +# TXT_PT +Lobby +# TXT_LOBBY +Spiele +# TXT_CHANNEL_GAMES +Spiel sichern... +# TXT_SAVING_GAME +Spiel ist voll. +# TXT_GAME_FULL +Sie mssen ein Spiel ausw„hlen! +# TXT_MUST_SELECT_GAME +%s spielt %s +# TXT_S_PLAYING_S +Nur der Spielleiter kann diese Option „ndern. +# TXT_ONLY_HOST_CAN_MODIFY +Spiel wurde abgebrochen. +# TXT_GAME_CANCELLED +%s hat ein neues Spiel er”ffnet. +# TXT_S_FORMED_NEW_GAME +Das Spiel von %s l„uft jetzt. +# TXT_GAME_NOW_IN_PROGRESS +Teslaspule +# TXT_TESLA +Mobiler Schattengenerator +# TXT_MGG +Flammenturm +# TXT_FLAME_TURRET +Flak +# TXT_AAGUN +Zwinger +# TXT_KENNEL +Technologiezentrum +# TXT_SOVIET_TECH +Lufttransporter +# TXT_BADGER +MiG-Jagdbomber +# TXT_MIG +Yak-Angriffsj„ger +# TXT_YAK +Stacheldraht +# TXT_FENCE +Mechanobot +# TXT_MEDIC +Saboteur +# TXT_SABOTEUR +General +# TXT_GENERAL +Tanya +# TXT_E7 +Fallschirmbomben +# TXT_PARA_BOMB +Fallschirmtruppen +# TXT_PARA_INFANTRY +Fallschirm-Saboteur +# TXT_PARA_SABOTEUR +Werft +# TXT_SHIP_YARD +U-Boot-Werft +# TXT_SUB_PEN +Szenario-Optionen +# TXT_SCENARIO_OPTIONS +Spionageflugzeug +# TXT_SPY_MISSION +Spionageflugzeug +# TXT_U2 +Terminator-Wachhund +# TXT_GUARD_DOG +Spionage-Info +# TXT_SPY_INFO +Geb„ude +# TXT_BUILDNGS +Einheiten +# TXT_UNITS +Infantrie-Cyborgs +# TXT_INFANTRY +Flugzeuge +# TXT_AIRCRAFT +Nachschub-Lkw +# TXT_TRUCK +Unverwundbarkeitsschirm +# TXT_INVUL +Eiserner Vorhang +# TXT_IRON_CURTAIN +Technologiezentrum +# TXT_ADVANCED_TECH +V2-Raketenwerfer +# TXT_V2_LAUNCHER +Kommandoposten +# TXT_FORWARD_COM +Sprengmeister +# TXT_DEMOLITIONER +Minenleger +# TXT_MINE_LAYER +Bauhof-Attrappe +# TXT_FAKE_CONST +Waffenfabrik-Attrappe +# TXT_FAKE_WEAP +Werft-Attrappe +# TXT_FAKE_YARD +U-Boot-Werft-Attrappe +# TXT_FAKE_PEN +Radar-Attrappe +# TXT_FAKE_RADAR +Bigfoot +# TXT_THEME_BIGF +Crush +# TXT_THEME_CRUS +Face the Enemy 1 +# TXT_THEME_FAC1 +Face the Enemy 2 +# TXT_THEME_FAC2 +Hell March +# TXT_THEME_HELL +Run for Your Life +# TXT_THEME_RUN1 +Smash +# TXT_THEME_SMSH +Trenches +# TXT_THEME_TREN +Workmen +# TXT_THEME_WORK +Await +# TXT_THEME_AWAIT +Dense +# TXT_THEME_DENSE_R +Map Selection +# TXT_THEME_MAP +Fogger +# TXT_THEME_FOGGER1A +Mud +# TXT_THEME_MUD1A +Radio 2 +# TXT_THEME_RADIO2 +Roll Out +# TXT_THEME_ROLLOUT +Snake +# TXT_THEME_SNAKE +Terminate +# TXT_THEME_TERMINAT +Twin +# TXT_THEME_TWIN +Vector +# TXT_THEME_VECTOR1A +Team-Cyborgs +# TXT_TEAM_MEMBERS +Brcke +# TXT_BRIDGE +Fass +# TXT_BARREL +Verbndeter +# TXT_GOODGUY +Gegner +# TXT_BADGUY +Gold +# TXT_GOLD +Edelsteine +# TXT_GEMS +Titelfilm +# TXT_TEASER +Filme +# TXT_MOVIES +Innen +# TXT_INTERIOR +Sonar +# TXT_SONAR_PULSE +Raketensilo +# TXT_MSLO +Navigationssatellit +# TXT_GPS_SATELLITE +Atombombe +# TXT_NUCLEAR_BOMB +Leicht +# TXT_EASY +Schwer +# TXT_HARD +Normal +# TXT_NORMAL +Bitte w„hlen Sie die Schwierigkeitsstufe. +Sie bleibt dann w„hrend des gesamten Feldzuges gltig. +# TXT_DIFFICULTY +Allianz +# TXT_ALLIES +Sowjets +# TXT_SOVIET +Vorspannmusik +# TXT_THEME_INTRO +Schatten w„chst +# TXT_SHADOW_REGROWS +Erz breitet sich aus +# TXT_ORE_SPREADS +Punkte-Musik +# TXT_THEME_SCORE +Internet +# TXT_INTERNET +Eis +# TXT_ICE +Kisten +# TXT_CRATE +Gepl„nkel +# TXT_SKIRMISH +Seite ausw„hlen +# TXT_CHOOSE +Wertvolle Mineralien +# TXT_MINERALS +Ignorieren +# TXT_IGNORE +Fehler - Modem antwortet nicht +# TXT_ERROR_NO_RESP +Fehler - Modem antwortet nicht auf 'result code enable'-Befehl +# TXT_ERROR_NO_RESCODE +Fehler - Modem antwortet nicht auf Initialisierungssequenz. +# TXT_ERROR_NO_INIT +Fehler - Modem antwortet nicht auf 'verbose'-Befehl +# TXT_ERROR_NO_VERB +Fehler - Modem antwortet nicht auf 'echo'-Befehl. +# TXT_ERROR_NO_ECHO +Fehler - Modem antwortet nicht auf Befehl 'disable modem auto answer'. +# TXT_ERROR_NO_DISABLE +Fehler - Zu viele Fehler beim Initialisieren des Modems. Abbruch. +# TXT_ERROR_TOO_MANY +Fehler - Modem gibt Fehlermeldung zurck. +# TXT_ERROR_ERROR +Fehler - Zeit abgelaufen beim Warten auf Verbindung. +# TXT_ERROR_TIMEOUT +erfolgreich +# TXT_ACCOMPLISHED +Klicken Sie, um weiterzumachen. +# TXT_CLICK_CONTINUE +Empfange Szenario vom Spielleiter. +# TXT_RECEIVING_SCENARIO +Sende Szenario an Spieler. +# TXT_SENDING_SCENARIO +Fehler - Modem antwortet nicht auf den Befehl 'flow control'. Ihre Windows-Konfiguration k”nnte falsch sein. +# TXT_NO_FLOW_CONTROL_RESPONSE +Fehler - Modem antwortet nicht auf den Befehl 'compression disable'. Ihre Windows-Konfiguration k”nnte falsch sein. +# TXT_NO_COMPRESSION_RESPONSE +Fehler - Modem antwortet nicht auf den Befehl 'error correction disable". Ihre Windows-Konfiguration k”nnte falsch sein. +# TXT_NO_ERROR_CORRECTION_RESPONSE +Um Alarmstufe Rot ber das Internet zu spielen, mssen Sie mit einem Internet-Provider verbunden und beim Planeten Westwood registriert sein. +# TXT_EXPLAIN_REGISTRATION +Fehler - kann WChat nicht starten. +# TXT_ERROR_UNABLE_TO_RUN_WCHAT +Registrieren +# TXT_REGISTER +Erzmine +# TXT_ORE_MINE +Kein eingetragenes Modem +# TXT_NO_REGISTERED_MODEM +Chronoport +# TXT_CHRONOSHIFT +Ungltiger oder besetzter Port +# TXT_UNABLE_TO_OPEN_PORT +Kein W„hlton. Vergewissern Sie sich, dass Ihr Modem an die Telefonleitung angeschlossen ist, und versuchen Sie es erneut. +# TXT_NO_DIAL_TONE +Fehler - der andere Spieler hat dieses Erweiterungsszenario nicht. +# TXT_NO_EXPANSION_SCENARIO +Bitte warten... +# TXT_STAND_BY +The Credits Theme +# TXT_THEME_CREDITS +Energieniveau kritisch: Flak ausser Betrieb +# TXT_POWER_AAGUN +Energieniveau kritisch: Teslaspulen ausser Betrieb +# TXT_POWER_TESLA +Energieniveau kritisch +# TXT_LOW_POWER +Kommandeur: +# TXT_COMMANDER +Siege: +# TXT_BATTLES_WON +Ihre Version des Spiels muá m”glicherweise upgedated werden. Die neuesten +Informationen ber Updates erhalten Sie auf unserer Webseite - +www.westwood.com +# TXT_MISMATCH +Nicht-kompatible Datei mit Spieldaten entdeckt. Diese Spieldatei k”nnte defekt sein. +# TXT_SCENARIO_ERROR +Wird verbunden +# TXT_CONNECTING +Modem-Initialisierung +# TXT_MODEM_INITIALISATION +Datenkompression +# TXT_DATA_COMPRESSION +Fehlerkorrektur +# TXT_ERROR_CORRECTION +Hardware-Datenfluákontrolle +# TXT_HARDWARE_FLOW_CONTROL +Fortgeschritten +# TXT_ADVANCED +2nd_Hand +# TXT_THEME_2ND_HAND +Arazoid +# TXT_THEME_ARAZOID +BackStab +# TXT_THEME_BACKSTAB +Chaos2 +# TXT_THEME_CHAOS2 +Shut_It +# TXT_THEME_SHUT_IT +TwinMix1 +# TXT_THEME_TWINMIX1 +Under3 +# TXT_THEME_UNDER3 +VR2 +# TXT_THEME_VR2 +Der andere Rechner reagiert nicht. M”chten Sie eine Notspeicherung versuchen? Damit das funktionieren kann, mssen beide Spieler sichern. +# TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING +Der andere Rechner hat aufgelegt. M”chten Sie eine Notspeicherung versuchen? Damit das funktionieren kann, mssen beide Spieler sichern. +# TXT_ASK_EMERGENCY_SAVE_HUNG_UP +Alarmstufe Rot kann die Registrierungssoftware nicht starten. Sie mssen Westwood Chat von der Alarmstufe-Rot-CD aus starten, um sich registrieren zu lassen. +# TXT_NO_REG_APP +Ein Spieler in dieser Runde hat dieses Erweiterungsszenario nicht. +# TXT_NO_CS_SCENARIOS +Raketen-U-Boot +# TXT_MISSILESUB +Elektrobot +# TXT_SHOCKTROOPER +Mechaniker +# TXT_MECHANIC +ChronoPanzer +# TXT_CHRONOTANK +TeslaPanzer +# TXT_TESLATANK +M.A.D.-Panzer +# TXT_MAD +Sprengwagen +# TXT_DEMOTRUCK +Tarntransporter +# TXT_PHASETRANSPORT +Bog +# TXT_THEME_BOG +Floating +# TXT_THEME_FLOAT_V2 +Gloom +# TXT_THEME_GLOOM +Ground Wire +# TXT_THEME_GRNDWIRE +Mech Man 2 +# TXT_THEME_RPT +Search +# TXT_THEME_SEARCH +Traction +# TXT_THEME_TRACTION +Wasteland +# TXT_THEME_WASTELND +H‚litransporter +# TXT_CARRIER + diff --git a/CODE/ENG/GER/CREDITS.TXT b/CODE/ENG/GER/CREDITS.TXT new file mode 100644 index 0000000..3748401 --- /dev/null +++ b/CODE/ENG/GER/CREDITS.TXT @@ -0,0 +1,242 @@ + + + Ausfhrender Produzent Brett W. Sperry + + Produzent Lewis S. Peterson + + Programmierung Barry Green + + Technische Leitung Steve Wetherill, Eric Wang + + Design der Solospieler-Karten John Archer, Patrick Connelly, + Adam Isgreen, Michael Lightner, + Erik Yeo + + Design der Mehrspieler-Karten John Archer, Stuart Bailey, + Nigel Berryman, D'Andre Campbell, + Valerie Carpentier, Cathi Diet, + Keith Ditchburn, Gareth Eke, + Darren Esp, Simon Evers, Rod Gray, + Randy Greenback, Matthew Howe, + Levi Luke, Suzanne Maddison, + Iain McNeil, Graeme Miller, + Lee Morse, David Parsons, + Alex Scarrow, Robert Scheel, + Lawrence So, John Sweeney, + Matthew Tillett, Phillip Veale + + Grafik Chris Demers, Matthew Hansel, + Joseph B. Hewitt IV + + Audio-Leitung Paul S. Mudra + + Musik Frank Klepacki + + Ger„uscheffekte Dwight K. Okahara + + Leiter Qualit„tsberwachung Glenn Sperry + + Qualit„tsberwachung Jim Adkins, Lloyd Bell, + Chris Blevens, D'Andre Campbell, + Errol Campbell, Shane Dietrich, + Randy Greenback, Jon Hix, + Mark Laity, Troy Leonard, + DeMarlo Lewis, Levi Luke, + Patrick Offord, Richard Rasmussen, + Mike Ruppert, Steve Shockey, + Mike Smith, Albert Springfield + + Deutsche Version + + Projektleitung Marc Buro + + Sprecher J”rg Mackensen, Thomas Stein + + šbersetzung Rolf D. Busch + + Qualit„tsberwachung Rolf D. Busch + + + + + Alarmstufe Rot + + Ausfhrender Produzent Brett W. Sperry + Produzent Ed Del Castillo + + Original Konzept Brett W. Sperry + Joe Bostic + + Original Story Ron Smith + Ed Del Castillo + + Hauptprogrammierer Joe Bostic + Barry Green + Steve Tall + + Programmierer Phil Gorrow + Maria del Mar McCready Legg + Jeff Brown + Bill Randolph + Matt Thorn + David Aldridge + Steve Wetherill + + Programmierer deutsche Version Victor Grippi + + Technische Leitung Steve Wetherill + Eric Wang + + Design Brett W. Sperry + Adam Isgreen + Michael Lightner + Erik Yeo + + Hauptgrafiker Chris Demers + Matthew Hansel + Joseph Hewitt + + Grafiker Paul Wesberry + David T. Potter + Damon Redmond + Brian White + + Film & Video Eric Gooch + Kevin Becquet + + Video Post Production Felix Kupis + Kevin Becquet + + Screenplay Ron Smith + Adam Isgreen + John Scott Lewinski + + Direktor Audio Paul S. Mudra + Soundtrack Frank Klepacki + Sound Effekte Dwight K. Okahara + + Leitung Qualit„tssicherung Glenn Sperry + + Qualit„tssicherung Lloyd Bell + Kenny Dunne + Mike Smith + John Archer + Chris Rubyor + D'Andre Cambell + Chad Shackelford + Chris Bleven + Levi Luke + Errol Campbell + Abe Hernandez + James Adkins + Richard Rasmussen + Ben Lublin + Chris Holloway + Isaiah Myers + Phillip Castro + Albert Springfield + Steve Corcoran + James Hughes + Randy Greenback + Tyler Thackery + + Verpackungsdesign Matthew Hansel + Thomas Puckett inc. + + Handbuchdesign Victoria Hart + + + + Besonderer Dank an Daniel Woodyatt, Rosemarie Dalton, + und alle Virgins in Europa. Vielen Dank auch an + Tracy Chapman fr einen neuen Anfang. + + + + + Die Schauspieler + + + Allierte Seite + Von Esling Arthur Roberts + Stavros Barry Kramer + Tanya Lynne Litteer + Verh”rspezialist Dom Magwili + Ansagerin Gwen Castaldi + + ...und John Milford als Albert Einstein + + + Allierte Truppen Ricky Russell + Nick Paulos + Scott Ryan Talley + Joe Bostic + Chris Demers + Barry Green + Matthew Hansel + Adam Isgreen + Frank Klepacki + Mike Lightner + Bill Randolph + Philip E. Shelburne + Eric Wang + + + + Sovietische Seite + Kukov Craig Cavanah + Gradenko Alan Terry + Nadia Andrea Robinson + Kane Joe Kucan + + ...und Eugene Dynarski als Josef Stalin + + + Sovietische Truppen Felix Kupis + Erik Yeo + + + Stimmen im Spiel + E.V.A. Martin Alper + Tanya Lanae Freeborn + Truppen Mike Grayford + Adam Isgreen + Troy Leonard + Chris Rubyor + + + Produktions Crew + + Casting Marilee Lear, C.S.A. + + Kostmdesign Christie Moeller + + Make-up & Frisuren Cindy Cline + + Sound Engineer Paul S. Mudra + + Beleuchtung Eric Gooch + + Hauptproduktionsassistent Paul Bastardo + + Produktionsassistenten Pat Connelly + Richard Rasmussen + Rick Appin + Jeff Fillhaber + Patience Becquet + + Video Kevin Becquet + + Sfx Make-up Philip E. Shelburne + + + Spezielles Bildmaterial + Courtesy Of NBC News Archives Films + Fabulous Footage + + + Regie: Joseph D. Kucan + + + + diff --git a/CODE/ENG/MESSAGE.TXT b/CODE/ENG/MESSAGE.TXT new file mode 100644 index 0000000..5fc1bb2 --- /dev/null +++ b/CODE/ENG/MESSAGE.TXT @@ -0,0 +1,57 @@ +# MESSAGE_NONE +You must build a Windtrap to provide power to your base. Without power, your structures will decay. +# MESSAGE_BUILD_WINDTRAP +Concrete: Use concrete to make a sturdy foundation for your structures. +# MESSAGE_STRUCT_CONCRETE +Palace: This is your Palace. +# MESSAGE_STRUCT_PALACE +Light Factory: The Light Factory produces light attack vehicles. +# MESSAGE_STRUCT_LIGHT +Heavy Factory: The Heavy Factory produces tracked vehicles. +# MESSAGE_STRUCT_HEAVY +Hi-Tech Factory: The Hi-Tech Factory produces flying vehicles. +# MESSAGE_STRUCT_HITECH +House IX: The IX Research Facility advances your House's technology. +# MESSAGE_STRUCT_IX +WOR: Wor is used to train your Heavy infantry. +# MESSAGE_STRUCT_WOR +Construction Facility: All structures are built by the construction facility. +# MESSAGE_STRUCT_CONST +Windtrap: The windtrap supplies power to your base. Without power your structures will decay. +# MESSAGE_STRUCT_WINDTRAP +Barracks: The Barracks is used to train your Light infantry. +# MESSAGE_STRUCT_BARRACKS +Startport: The Starport is used to order and receive shipments from C.H.O.A.M. +# MESSAGE_STRUCT_STARPORT +Spice Refinery: The Refinery converts spice into credits. +# MESSAGE_STRUCT_REFINERY +Repair Facility: The Repair Facility is used to repair your vehicles. +# MESSAGE_STRUCT_REPAIR +Wall: The wall is used for passive defense. +# MESSAGE_STRUCT_WALL +Gun Turret: The cannon turret is used for short range active defense. +# MESSAGE_STRUCT_TURRET +Rocket Turret: The rocket/cannon turret is used for both short and medium range active defense. +# MESSAGE_STRUCT_RTURRET +Spice Silo: The Spice silo is used to store refined spice. +# MESSAGE_STRUCT_SILO +Outpost: The Outpost provides radar and aids control of distant vehicles. +# MESSAGE_STRUCT_OUTPOST +There isn't enough open concrete to place this structure. You may proceed, but without enough concrete the building will need repairs. +# MESSAGE_NEED_CONCRETE +Sand: This is sand terrain. Plenty of this stuff on Arrakis, to be sure. +# MESSAGE_SAND +Sand Dunes: These are an ubiquitous feature of Arrakian landscape. +# MESSAGE_DUNE +Rock: This is rock terrain. This valuable terrain is the only place structures can be built. +# MESSAGE_ROCK +Mountain: Mountains on Arrakis are rare (and an inconvenience). +# MESSAGE_MOUNT +Spice Field: This is the Spice, Melange. It is the most precious substance in the universe. +# MESSAGE_SPICE +Structures must be placed on clear rock or concrete and adjacent to another friendly structure. +# MESSAGE_ADJACENT +Search for spice fields to harvest. +# MESSAGE_SEARCH4SPICE +Warning: Sandworms (Shai-Hulud) roam Dune devouring anything on the sand. +# MESSAGE_SANDWORM diff --git a/CODE/EVENT.CPP b/CODE/EVENT.CPP new file mode 100644 index 0000000..77b68c1 --- /dev/null +++ b/CODE/EVENT.CPP @@ -0,0 +1,1050 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/EVENT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : November 10, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EventClass::EventClass -- Construct an id and cell based event. * + * EventClass::EventClass -- Construct simple target type event. * + * EventClass::EventClass -- Constructor for mission change events. * + * EventClass::EventClass -- Constructor for navigation computer events. * + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * EventClass::EventClass -- Constructor for sidebar build events. * + * EventClass::EventClass -- Constructs event to transfer special flags. * + * EventClass::EventClass -- Default constructor for event objects. * + * EventClass::EventClass -- Event for sequencing animations. * + * EventClass::EventClass -- Megamission assigned to unit. * + * EventClass::Execute -- Execute a queued command. * + * EventClass::EventClass -- construct a variable-sized event * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#endif //WIN32 + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +/*************************************************************************** +** Table of what data is really used in the EventClass struct for different +** events. This table must be kept current with the EventType enum. +*/ +unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { + 0, // EMPTY + size_of(EventClass, Data.General ), // ALLY + size_of(EventClass, Data.MegaMission ), // MEGAMISSION + size_of(EventClass, Data.MegaMission_F ), // MEGAMISSION_F + size_of(EventClass, Data.Target ), // IDLE + size_of(EventClass, Data.Target ), // SCATTER + 0, // DESTRUCT + 0, // DEPLOY + size_of(EventClass, Data.Place ), // PLACE + 0, // OPTIONS + size_of(EventClass, Data.General ), // GAMESPEED + size_of(EventClass, Data.Specific ), // PRODUCE + size_of(EventClass, Data.Specific.Type ), // SUSPEND + size_of(EventClass, Data.Specific.Type ), // ABANDON + size_of(EventClass, Data.Target ), // PRIMARY + size_of(EventClass, Data.Special ), // SPECIAL_PLACE + 0, // EXIT + size_of(EventClass, Data.Anim ), // ANIMATION + size_of(EventClass, Data.Target ), // REPAIR + size_of(EventClass, Data.Target ), // SELL + size_of(EventClass, Data.SellCell), // SELLCELL + size_of(EventClass, Data.Options ), // SPECIAL + 0, // FRAMESYNC + 0, // MESSAGE + size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME + size_of(EventClass, Data.FrameInfo ), // FRAMEINFO + 0, // SAVEGAME + size_of(EventClass, Data.NavCom ), // ARCHIVE + size_of(EventClass, Data.Variable.Size), // ADDPLAYER + size_of(EventClass, Data.Timing ), // TIMING + size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME +#ifdef FIXIT_VERSION_3 // Stalemate games. + 0, // PROPOSE_DRAW + 0, // RETRACT_DRAW +#endif +}; + +char * EventClass::EventNames[EventClass::LAST_EVENT] = { + "EMPTY", + "ALLY", + "MEGAMISSION", + "MEGAMISSION_F", + "IDLE", + "SCATTER", + "DESTRUCT", + "DEPLOY", + "PLACE", + "OPTIONS", + "GAMESPEED", + "PRODUCE", + "SUSPEND", + "ABANDON", + "PRIMARY", + "SPECIAL_PLACE", + "EXIT", + "ANIMATION", + "REPAIR", + "SELL", + "SELLCELL", + "SPECIAL", + "FRAMESYNC", + "MESSAGE", + "RESPONSE_TIME", + "FRAMEINFO", + "SAVEGAME", + "ARCHIVE", + "ADDPLAYER", + "TIMING", + "PROCESS_TIME", +#ifdef FIXIT_VERSION_3 // Stalemate games. + "PROPOSE_DRAW", + "RETRACT_DRAW", +#endif +}; + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructs event to transfer special flags. * + * * + * This constructs an event that will transfer the special flags. * + * * + * INPUT: data -- The special flags to be transported to all linked computers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(SpecialClass data) +{ + ID = PlayerPtr->ID; + Type = SPECIAL; + Frame = ::Frame; + Data.Options.Data = data; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct simple target type event. * + * * + * This will construct a generic event that needs only a target parameter. The actual * + * event and target values are specified as parameters. * + * * + * INPUT: type -- The event type to construct. * + * * + * target-- The target value that this event is to apply to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass target) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Target.Whom = target; +} + + +EventClass::EventClass(EventType type, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.SellCell.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Default constructor for event objects. * + * * + * This constructs a simple event object that requires no parameters other than the * + * type of event it is. * + * * + * INPUT: type -- The type of event to construct. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for general-purpose-data events. * + * * + * INPUT: type -- The type of event to construct. * + * val -- data value * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int val) +{ + ID = PlayerPtr->ID; + Type = type; + Data.General.Value = val; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for navigation computer events. * + * * + * Constructor for events that are used to assign the navigation computer. * + * * + * INPUT: type -- The type of event (this constructor can be used by other navigation * + * type events). * + * * + * src -- The object that the event should apply to. * + * * + * dest -- The destination (or target) that the event needs to complete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass src, TargetClass dest) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.NavCom.Whom = src; + Data.NavCom.Where = TargetClass(dest); +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Event for sequencing animations. * + * * + * This constructor is used for animations that must be created through the event system. * + * * + * INPUT: anim -- The animation that will be created. * + * * + * coord -- The location where the animation is to be created. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord) +{ + ID = PlayerPtr->ID; + Type = ANIMATION; + Frame = ::Frame; + Data.Anim.What = anim; + Data.Anim.Owner = owner; + Data.Anim.Where = coord; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION; + Frame = ::Frame; + Data.MegaMission.Whom = src; + Data.MegaMission.Mission = mission; + Data.MegaMission.Target = target; + Data.MegaMission.Destination = destination; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). This variation is used for formation moves. * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * speed -- The formation override speed for this move. * + * * + * maxspeed -- The formation override maximum speed for this move. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION_F; + Frame = ::Frame; + Data.MegaMission_F.Whom = src; + Data.MegaMission_F.Mission = mission; + Data.MegaMission_F.Target = TargetClass(target); + Data.MegaMission_F.Destination = TargetClass(destination); + Data.MegaMission_F.Speed = speed; + Data.MegaMission_F.MaxSpeed = maxspeed; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for sidebar build events. * + * * + * This constructor is used for events that deal with an object type and an object ID. * + * Typically, this is used exclusively by the sidebar. * + * * + * INPUT: type -- The event type of this object. * + * * + * object -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, int id) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Specific.Type = object; + Data.Specific.ID = id; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * * + * This constructor is used for those events that have an object type and associated cell. * + * Typically, this is for building placement after construction has completed. * + * * + * INPUT: type -- The event type for this object. * + * * + * object -- The object type number (actual object is probably inferred from the * + * sidebar data). * + * * + * cell -- The cell location where this event is to occur. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Place.Type = object; + Data.Place.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct an id and cell based event. * + * * + * This constructor is used for those events that require an ID number and a cell location. * + * * + * INPUT: type -- The event type this will be. * + * * + * id -- The arbitrary id number to assign. * + * * + * cell -- The location for this event. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int id, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Special.ID = id; + Data.Special.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- construct a variable-sized event * + * * + * INPUT: * + * ptr ptr to data associated with this event * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/10/1995 BRR : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, void * ptr, unsigned long size) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Variable.Pointer = ptr; + Data.Variable.Size = size; +} + + +/*********************************************************************************************** + * EventClass::Execute -- Execute a queued command. * + * * + * This routine executes an event. The even must already have been confirmed by any * + * remote machine before calling this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void EventClass::Execute(void) +{ + TechnoClass * techno; + AnimClass * anim = 0; + HouseClass * house = 0; +// CELL cell = 0; + char txt[80]; + bool formation = false; +// RTTIType rt; + + if (Debug_Print_Events) { + printf("(%d) Executing %s ID:%d Frame:%d ", + ::Frame, EventNames[Type], ID, Frame); + } + + switch (Type) { + + /* + ** Update the archive target for this building. + */ + case ARCHIVE: + techno = Data.NavCom.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->ArchiveTarget = Data.NavCom.Where; + } + break; + + /* + ** Make or break alliance. + */ + case ALLY: + house = Houses.Raw_Ptr(Data.General.Value); + if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { + Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); + } else { + Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); + } + break; + + /* + ** Special self destruct action requested. This is active in the multiplayer mode. + */ + case DESTRUCT: + Houses.Raw_Ptr(ID)->Flag_To_Die(); + break; + + /* + ** Update the special control flags. This is necessary so that in a multiplay + ** game, all machines will agree on the rules. If these options change during + ** game play, then all players are informed that options have changed. + */ + case SPECIAL: + { + Special = Data.Options.Data; + HouseClass * house = Houses.Raw_Ptr(ID); + + sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); + Session.Messages.Add_Message(NULL, 0, txt, + house->RemapColor, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200); + Map.Flag_To_Redraw(false); + } + break; + + /* + ** Starts or stops repair on the specified object. This event is triggered by the + ** player clicking the repair wrench on a building. + */ + case REPAIR: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->Repair(-1); + } + break; + + /* + ** Tells a building/unit to sell. This event is triggered by the player clicking the + ** sell animating cursor over the building or unit. + */ + case SELL: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { + if (techno->What_Am_I() == RTTI_BUILDING || (techno->What_Am_I() == RTTI_UNIT && Map[techno->Center_Coord()].Cell_Building() != 0)) { + techno->Sell_Back(-1); + } + } else { +// if (Is_Target_Cell(Data.Target.Whom)) { +// Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); +// } + } + break; + + /* + ** Tells the wall at the specified location to sell off. + */ + case SELLCELL: +// cell = Data.SellCell.Cell; + Houses.Raw_Ptr(ID)->Sell_Wall(Data.SellCell.Cell); + break; + + /* + ** This even is used to trigger an animation that is generated as a direct + ** result of player intervention. + */ + case ANIMATION: + anim = new AnimClass(Data.Anim.What, Data.Anim.Where); + if (anim) { + if (Data.Anim.Owner != HOUSE_NONE && PlayerPtr->Class->House != Data.Anim.Owner) { + anim->Make_Invisible(); + } + } + break; + + /* + ** This event will place the specified object at the specified location. + ** The event is used to place newly constructed buildings down on the map. The + ** object type is specified. From this object type, the house can determine the + ** exact factory and real object pointer to use. + */ + case PLACE: + Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); + break; + + /* + ** This event starts production of the specified object type. The house can + ** determine from the type and ID value, what object to begin production on and + ** what factory to use. + */ + case PRODUCE: + Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); + break; + + /* + ** This event is generated when the player puts production on hold. From the + ** object type, the factory can be inferred. + */ + case SUSPEND: + Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); + break; + + /* + ** This event is generated when the player cancels production of the specified + ** object type. From the object type, the exact factory can be inferred. + */ + case ABANDON: + Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); + break; + + /* + ** Toggles the primary factory state of the specified building. + */ + case PRIMARY: + { + BuildingClass * building = Data.Target.Whom.As_Building(); + if (building && building->IsActive) { + building->Toggle_Primary(); + } + } + break; + + /* + ** This is the general purpose mission control event. Most player + ** action routes through this event. It sets a unit's mission, TarCom, + ** and NavCom to the values specified. + */ + case MEGAMISSION_F: + techno = Data.MegaMission_F.Whom.As_Techno(); + if (techno && techno->IsActive && techno->Is_Foot()) { + ((FootClass *)techno)->IsFormationMove = true; + ((FootClass *)techno)->FormationSpeed = Data.MegaMission_F.Speed; + ((FootClass *)techno)->FormationMaxSpeed = Data.MegaMission_F.MaxSpeed; + FormMove = true; + FormSpeed = Data.MegaMission_F.Speed; + FormMaxSpeed = Data.MegaMission_F.MaxSpeed; + formation = true; + } + // Fall thru to next case... + + case MEGAMISSION: + if (Debug_Print_Events) { + printf("Whom:%x Tgt:%x Dest:%x ", + Data.MegaMission.Whom.As_TARGET(), + Data.MegaMission.Target.As_TARGET(), + Data.MegaMission.Destination.As_TARGET()); + } + techno = Data.MegaMission.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && techno->Strength > 0 && !techno->IsInLimbo) { + + /* + ** Fetch a pointer to the object of the mission. If there is an error with + ** this object, such as it is dead, then bail. + */ + ObjectClass * object = NULL; + if (Data.MegaMission.Target.Is_Valid()) { + object = Data.MegaMission.Target.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Target.Invalidate(); + } + } + + /* + ** If the destination target is invalid because the object is dead, then + ** bail from processing this mega mission. + */ + if (Data.MegaMission.Destination.Is_Valid()) { + object = Data.MegaMission.Destination.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Destination.Invalidate(); + } + } + + /* + ** Break any existing tether or team contact, since it is now invalid. + */ + if (!techno->IsTethered) { + techno->Transmit_Message(RADIO_OVER_OUT); + } + if (techno->Is_Foot()) { + if (!formation) ((FootClass *)techno)->IsFormationMove = false; + if (((FootClass *)techno)->Team) { + ((FootClass *)techno)->Team->Remove((FootClass *)techno); + } + } + + if (object != NULL) { + if (PlayerPtr->Is_Ally(techno)) { + object->Clicked_As_Target(); + } + } + + /* + ** Test to see if the navigation target should really be queued rather + ** than assigned to the object. This would be the case if this is a + ** special queued move mission and there is already a valid navigation + ** target for this unit. + */ + bool q = (Data.MegaMission.Mission == MISSION_QMOVE); + + techno->Assign_Mission(Data.MegaMission.Mission); + + if (techno->Is_Foot()) { + ((FootClass*)techno)->SuspendedNavCom = TARGET_NONE; + } + techno->SuspendedTarCom = TARGET_NONE; + + /* + ** Guard area mode is handled with care. The specified target is actually + ** assigned as the location that should be guarded. In addition, the + ** movement destination is immediately set to this new location. + */ + if (Data.MegaMission.Mission == MISSION_GUARD_AREA && techno->Is_Foot()) { + techno->Assign_Target(TARGET_NONE); + techno->Assign_Destination(Data.MegaMission.Target.As_TARGET()); + techno->ArchiveTarget = Data.MegaMission.Target.As_TARGET(); + } else { + if (q && techno->Is_Foot()) { + ((FootClass *)techno)->Queue_Navigation_List(Data.MegaMission.Destination.As_TARGET()); + } else { + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + techno->Assign_Target(Data.MegaMission.Target.As_TARGET()); + techno->Assign_Destination(Data.MegaMission.Destination.As_TARGET()); + } + } + + // + // Special case for ship repairing: If the assigned mission is to + // move, and 'techno' is a Vessel: + // If the destination is a shipyard or sub pen, set the IsToSelfRepair flag + // Otherwise, clear both IsToSelfRepair and IsSelfRepairing + // + RTTIType rt = techno->What_Am_I(); +// rt = Data.MegaMission.Whom; + if (rt == RTTI_VESSEL && techno != NULL && techno->What_Am_I() == RTTI_VESSEL && Data.MegaMission.Mission == MISSION_MOVE) { + VesselClass *ship = (VesselClass *)techno; + if (object != NULL) { + if (object->What_Am_I() == RTTI_BUILDING && +// if ((RTTIType)Data.MegaMission.Destination == RTTI_BUILDING && + (((BuildingClass *)object)->Class->Type == STRUCT_SHIP_YARD || + ((BuildingClass *)object)->Class->Type == STRUCT_SUB_PEN)) { + ship->IsToSelfRepair = true; + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } + +#ifdef NEVER + if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && + Data.MegaMission.Mission == MISSION_GUARD_AREA) { + + ((FootClass *)techno)->ArchiveTarget = Data.MegaMission.Destination; + } +#endif + } + break; + + /* + ** Request that the unit/infantry/aircraft go into idle mode. + */ + case IDLE: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered && techno->What_Am_I() != RTTI_BUILDING) { + techno->Transmit_Message(RADIO_OVER_OUT); + techno->Assign_Destination(TARGET_NONE); + techno->Assign_Target(TARGET_NONE); + techno->Enter_Idle_Mode(); + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + } + break; + + /* + ** Request that the unit/infantry/aircraft scatter from its current location. + */ + case SCATTER: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->Is_Foot() && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + ((FootClass *)techno)->IsScattering = true; + techno->Scatter(0, true, false); + } + break; + + /* + ** If we are placing down the ion cannon blast then lets take + ** care of it. + */ + case SPECIAL_PLACE: + Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); + break; + + /* + ** Exit the game. + ** Give parting message while palette is fading to black. + */ + case EXIT: + Theme.Queue_Song(THEME_NONE); + Stop_Speaking(); + Speak(VOX_CONTROL_EXIT); + while (Is_Speaking()) { + Call_Back(); + } + GameActive = false; + break; + + /* + ** Process the options menu, unless we're playing back a recording. + */ + case OPTIONS: + if (!Session.Play) { + SpecialDialog = SDLG_OPTIONS; + } + break; + + /* + ** Process the options Game Speed + */ + case GAMESPEED: + Options.GameSpeed = Data.General.Value; + break; + + /* + ** Adjust connection timing for multiplayer games + */ + case RESPONSE_TIME: + Session.MaxAhead = Data.FrameInfo.Delay; + break; + + /* + ** Save a multiplayer game (this event is only generated in multiplayer mode) + */ + case SAVEGAME: + /* + ** Show the user what's going on with a message box (but only if + ** we're not already inside a dialog box routine!) + */ + if (SpecialDialog == SDLG_NONE) { + CDTimerClass timer; + //timer.Start(); + timer = TICKS_PER_SECOND * 4; + + WWMessageBox().Process(TXT_SAVING_GAME, TXT_NONE); + + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + + while (timer > 0) { + Call_Back(); + } + + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + else { + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + } + break; + + /* + ** Add a new player to the game: + ** - Form a network connection to him + ** - Add his name, ID, House etc to our list of players + ** - Re-sort the ID array + ** - Place his units on the map + */ + case ADDPLAYER: + int i; + printf("ADDPLAYER EVENT!\n"); + for (i=0;iID) { + delete [] Data.Variable.Pointer; + } + break; + + // + // This event tells all systems to use new timing values. It's like + // RESPONSE_TIME, only it works. It's only used with the + // COMM_MULTI_E_COMP protocol. + // + case TIMING: +#if(TIMING_FIX) + // + // If MaxAhead is about to increase, we're vulnerable to a Packet- + // Received-Too-Late error, if any system generates an event after + // this TIMING event, but before it executes. So, record the + // period of vulnerability's frame start & end values, so we + // can reschedule these events to execute after it's over. + // + if (Data.Timing.MaxAhead > Session.MaxAhead) { + NewMaxAheadFrame1 = Frame; + NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; + } +#endif + Session.DesiredFrameRate = Data.Timing.DesiredFrameRate; + Session.MaxAhead = Data.Timing.MaxAhead; + +#ifndef WOLAPI_INTEGRATION + /* + ** If spawned from WChat then we should be getting poked every minute. If not then + ** deliberately break the max ahead value + */ +#ifdef WIN32 + if (Special.IsFromWChat) { + Session.MaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + if (Debug_Print_Events) { + printf("DesiredFrameRate:%d MaxAhead:%d ", + Session.DesiredFrameRate, + Session.MaxAhead); + } + + break; + + // + // This event tells all systems what the other systems' process + // timing requirements are; it's used to compute a desired frame rate + // for the game. + // + case PROCESS_TIME: + for (i = 0; i < Session.Players.Count(); i++) { + if (ID == Session.Players[i]->Player.ID) { + Session.Players[i]->Player.ProcessTime = Data.ProcessTime.AverageTicks; + break; + } + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case PROPOSE_DRAW: + if( ID == PlayerPtr->ID ) + { + if( Scen.bOtherProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bLocalProposesDraw = true; + break; + } + Scen.bLocalProposesDraw = true; + Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_PROPOSED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + if( Scen.bLocalProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bOtherProposesDraw = true; + break; + } + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + sprintf( szMessage, TXT_WOL_DRAW_PROPOSED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = true; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; + + case RETRACT_DRAW: + if( ID == PlayerPtr->ID ) + { + Scen.bLocalProposesDraw = false; + Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_RETRACTED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + sprintf( szMessage, TXT_WOL_DRAW_RETRACTED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = false; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; +#endif + + /* + ** Default: do nothing. + */ + default: + break; + } + + if (Debug_Print_Events) { + printf("\n"); + } + +} diff --git a/CODE/EVENT.H b/CODE/EVENT.H new file mode 100644 index 0000000..d38beac --- /dev/null +++ b/CODE/EVENT.H @@ -0,0 +1,260 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/EVENT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EVENT_H +#define EVENT_H + +/* +** This event class is used to contain all external game events (things that the player can +** do at any time) so that these events can be transported between linked computers. This +** encapsulation is required in order to ensure that each event affects all computers at the +** same time (same game frame). +** NOTE: If you add or remove an event type, you must also update the globals +** EventLength[] and EventNames[]. +*/ +class EventClass +{ + public: + + /* + ** All external events are identified by these labels. + */ + typedef enum EventType { + EMPTY, + + ALLY, // Make allie of specified house. + MEGAMISSION, // Full change of mission with target and destination. + MEGAMISSION_F, // Full change of mission with target and destination, and formation overrides. + IDLE, // Request to enter idle mode. + SCATTER, // Request to scatter from current location. + DESTRUCT, // Self destruct request (surrender action). + DEPLOY, // MCV is to deploy at current location. + PLACE, // Place building at location specified. + OPTIONS, // Bring up options screen. + GAMESPEED, // Set game speed + PRODUCE, // Start or Resume production. + SUSPEND, // Suspend production. + ABANDON, // Abandon production. + PRIMARY, // Primary factory selected. + SPECIAL_PLACE, // Special target location selected + EXIT, // Exit game. + ANIMATION, // Flash ground as movement feedback. + REPAIR, // Repair specified object. + SELL, // Sell specified object. + SELLCELL, // Sell wall at specified cell. + SPECIAL, // Special options control. + + // Private events. + FRAMESYNC, // Game-connection packet; includes Scenario CRC & sender's frame # + // Used to initiate game connection phase & to reconnect; + // When one of these is received, the receiver knows there are + // no associated commands in this packet. + MESSAGE, // Message to another player (The message is the 40 bytes + // after the event class). + RESPONSE_TIME, // use a new propagation delay value + FRAMEINFO, // Game-heartbeat packet; includes Game CRC & command count + // All packets sent for a frame are prefixed with one of these + SAVEGAME, // allows multiplayer games to save + ARCHIVE, // Updates archive target on specified object. + ADDPLAYER, // Add a new player + + TIMING, // new timing values for all systems to use + PROCESS_TIME, // a system's average processing time, in ticks per frame + +#ifdef FIXIT_VERSION_3 // Stalemate games. + PROPOSE_DRAW, // Players proposes that 2-player game be called a stalemate. + RETRACT_DRAW, // Player retracts proposed draw offer. +#endif + + LAST_EVENT, // one past the last event + } EventType; + + EventType Type; // Type of queue command object. + + /* + ** 'Frame' is the frame that the command should execute on. + ** 27 bits gives over 25 days of playing time without wrapping, + ** at 30 frames per second, so it should be plenty! + */ + unsigned Frame : 26; + + /* + ** House index of the player originating this event + */ + unsigned ID : 5; + + /* + ** This bit tells us if we've already executed this event. + */ + unsigned IsExecuted: 1; + + /* + ** This union contains the specific data that the event requires. + */ + union { + struct { + SpecialClass Data; // The special option flags. + } Options; + struct { + CELL Cell; // The cell to sell wall at. + } SellCell; + struct { + xTargetClass Whom; // The object to apply the event to. + } Target; + struct { + AnimType What; // The animation to create. + HousesType Owner; // The owner of the animation (when it matters). + COORDINATE Where; // The location to place the animation. + } Anim; + struct { + int Value; // general-purpose data + } General; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + } MegaMission; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + SpeedType Speed; // Formation override speed. + MPHType MaxSpeed; // Formation override maximum speed. + } MegaMission_F; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + } Mission; + struct { + xTargetClass Whom; // Whom to apply movement change to. + xTargetClass Where; // Where to set NavCom to. + } NavCom; + struct { + xTargetClass Whom; // Whom to apply attack change to. + xTargetClass Target; // What to set TarCom to. + } TarCom; + struct { + RTTIType Type; + int ID; + } Specific; + struct { + RTTIType Type; + CELL Cell; + } Place; + struct { + int ID; + CELL Cell; + } Special; + + /* + ** This structure is used for FRAMEINFO, FRAMESYNC, and RESPONSE_TIME + ** events; exactly one of these will be sent each frame, whether there's + ** data that frame or not. + ** CRC: the game CRC when this packet was generated; used to detect sync errors + ** CommandCount: # of commands the sender has sent; used to detect missed packets + ** Delay: sender's propagation delay value for this frame + */ + struct { + unsigned long CRC; + unsigned short CommandCount; // # commands sent so far + unsigned char Delay; // propagation delay used this frame + // (Frame - Delay = sender's current frame #) + } FrameInfo; + /* + ** This structure is used for the special variable-length event. This event + ** can be anything the application wants it to be. Its purpose is to allow + ** relatively large amounts of data to be associated with the event, without + ** bloating the size of this union (and thus all other event types). + */ + struct { + void * Pointer; + unsigned long Size; + } Variable; + + // + // This structure sets new timing values for all systems in a multiplayer + // game. This structure replaces the RESPONSE_TIME event for + // the COMM_MULTI_E_COMP protocol. + // + struct { + unsigned short DesiredFrameRate; + unsigned short MaxAhead; + } Timing; + + // + // This structure is transmitted by all systems, and is used to compute + // the "desired" frame rate for the game. + // + struct { + unsigned short AverageTicks; + } ProcessTime; + + } Data; + + //-------------- Constructors --------------------- + EventClass(void) {Type = EMPTY;}; + EventClass(SpecialClass data); + EventClass(EventType type, TargetClass target); + EventClass(EventType type); + EventClass(EventType type, int val); + EventClass(EventType type, CELL cell); + EventClass(EventType type, TargetClass src, TargetClass dest); + EventClass(TargetClass src, MissionType mission, TargetClass target=TARGET_NONE, TargetClass destination=TARGET_NONE); + + EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed); + + EventClass(EventType type, RTTIType object, int id); + EventClass(EventType type, RTTIType object, CELL cell); + EventClass(EventType type, int id, CELL cell); + EventClass(AnimType anim, HousesType owner, COORDINATE coord); + EventClass(void *ptr, unsigned long size); + EventClass(EventType type, void *ptr, unsigned long size); + + // Process the event. + void Execute(void); + + int operator == (EventClass & q) { + return memcmp(this, &q, sizeof(q)) == 0; + }; + + static unsigned char EventLength[LAST_EVENT]; + static char * EventNames[LAST_EVENT]; +}; + +#endif diff --git a/CODE/EXPAND.CPP b/CODE/EXPAND.CPP new file mode 100644 index 0000000..ea613a1 --- /dev/null +++ b/CODE/EXPAND.CPP @@ -0,0 +1,566 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/EXPAND.CPP 7 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EXPAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/03/95 * + * * + * Last Update : Mar 01, 1997 [V.Grippi] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +//#define CS_DEBUG + +#define ARRAYOFFSET 20 + + +/*********************************************************************************************** + * Expansion_CS_Present -- Is the Counterstrike expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if counterstrike installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/5/97 1:59PM ST : Fixed to check for EXPAND.MIX * + *=============================================================================================*/ +bool Expansion_CS_Present(void) +{ + // ajw 9/29/98 + return Is_Counterstrike_Installed(); +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + * Expansion_AM_Present -- Is the Aftermath expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if AfterMath is installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 7/9/97 1:59PM BG : Fixed to check for EXPAND2.MIX * + *=============================================================================================*/ +bool Expansion_AM_Present(void) +{ + // ajw 9/29/98 + return Is_Aftermath_Installed(); +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +} +#endif + + +const char* ExpandNames[] = { + "SCG20EA", + "SCG21EA", + "SCG22EA", + "SCG23EA", + "SCG24EA", + "SCG26EA", + "SCG27EA", + "SCG28EA", + "SCU31EA", + "SCU32EA", + "SCU33EA", + "SCU34EA", + "SCU35EA", + "SCU36EA", + "SCU37EA", + "SCU38EA", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "SCG43EA", // Harbor Reclamation + "SCG41EA", // In the nick of time + "SCG40EA", // Caught in the act + "SCG42EA", // Production Disruption + "SCG47EA", // Negotiations + "SCG45EA", // Monster Tank Madness + "SCG44EA", // Time Flies + "SCG48EA", // Absolut MADness + "SCG46EA", // Pawn + + "SCU43EA", // Testing Grounds + "SCU40EA", // Shock Therapy + "SCU42EA", // Let's Make a Steal + "SCU41EA", // Test Drive + "SCU45EA", // Don't Drink The Water + "SCU44EA", // Situation Critical + "SCU46EA", // Brothers in Arms + "SCU47EA", // Deus Ex Machina + "SCU48EA", // Grunyev Revolution +#endif + NULL + }; + +const char* TestNames2[] = { + "SCG01EA", + "SCG02EA", + "SCG03EA", + "SCG04EA", + "SCG05EA", + "SCG06EA", + "SCG07EA", + "SCG08EA", + "SCU01EA", + "SCU02EA", + "SCU03EA", + "SCU04EA", + "SCU05EA", + "SCU06EA", + "SCU07EA", + "SCU08EA", + "SCU09EA", + NULL +}; + +#ifdef GERMAN +const char* XlatNames[] = { + "Zusammenstoss", + "Unter Tage", + "Kontrollierte Verbrennung", + "Griechenland 1 - Stavros", + "Griechenland 2 - Evakuierung", + "Sibirien 1 - Frische Spuren", + "Sibirien 2 - In der Falle", + "Sibirien 3 - Wildnis", + "Das Feld der Ehre", + "Belagerung", + "Mausefalle", + "Teslas Erbe", + "Soldat Volkov", + "Die Spitze der Welt", + "Paradoxe Gleichung", + "Nukleare Eskalation", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "Ein sicherer Hafen", // "SCG43EA", // Harbor Reclamation + "Zeitkritische Routine", // "SCG41EA", // In the nick of time + "Auf frischer Tat ertappt", // "SCG40EA", // Caught in the act + "Drastischer Baustopp", // "SCG42EA", // Production Disruption + "Harte Verhandlungen", // "SCG47EA", // Negotiations + "Ferngelenktes Kriegsspielzeug",// "SCG45EA", // Monster Tank Madness + "Licht aus", // "SCG44EA", // Time Flies + "Molekulare Kriegsfhrung", // "SCG48EA", // Absolut MADness + "Bauernopfer", // "SCG46EA", // Pawn + + "Testgel„nde", // "SCU43EA", // Testing Grounds + "Schocktherapie", // "SCU40EA", // Shock Therapy + "Der Letzte seiner Art", // "SCU42EA", // Let's Make a Steal + "Probefahrt", // "SCU41EA", // Test Drive + "Schlaftrunk", // "SCU45EA", // Don't Drink The Water + "Der jngste Tag", // "SCU44EA", // Situation Critical + "Waffenbrder", // "SCU46EA", // Brothers in Arms + "Deus Ex Machina", // "SCU47EA", // Deus Ex Machina + "Die Replikanten von Grunyev", // "SCU48EA", // Grunyev Revolution + +#endif + NULL +}; + +#endif + +#ifdef FRENCH +const char* XlatNames[] = { + "Gaz Sarin 1: Ravitaillement Fatal", + "Gaz Sarin 2: En Sous-sol", + "Gaz Sarin 3: Attaque Chirurgicale", + "GrŠce Occup‚e 1: Guerre Priv‚e", + "GrŠce Occup‚e 2: Evacuation", + "Conflit Sib‚rien 1: Traces FraŒches", + "Conflit Sib‚rien 2: Pris au PiŠge", + "Conflit Sib‚rien 3: Terres de Glace", + "Mise … l'Epreuve", + "Assi‚g‚s", + "La SouriciŠre", + "L'H‚ritage de Tesla", + "Tandem de Choc", + "Jusqu'au Sommet du Monde", + "Effets Secondaires", + "Intensification nucl‚aire", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +"Le vieux port", // "SCG43EA", // Harbor Reclamation +"Juste … temps", // "SCG41EA", // In the nick of time +"La main dans le sac", // "SCG40EA", // Caught in the act +"Production interrompue", // "SCG42EA", // Production Disruption +"N‚gociations", // "SCG47EA", // Negotiations +"Tanks en folie!", // "SCG45EA", // Monster Tank Madness +"Le temps passe", // "SCG44EA", // Time Flies +"D‚mence absolue", // "SCG48EA", // Absolut MADness +"Le pion", // "SCG46EA", // Pawn + +"Terrains d'essais", // "SCU43EA", // Testing Grounds +"Th‚rapie de choc", // "SCU40EA", // Shock Therapy +"Au voleur!", // "SCU42EA", // Let's Make a Steal +"Essai de conduite", // "SCU41EA", // Test Drive +"Ne buvez pas la tasse", // "SCU45EA", // Don't Drink The Water +"Situation critique", // "SCU44EA", // Situation Critical +"FrŠres d'armes", // "SCU46EA", // Brothers in Arms +"Deus Ex Machina", // "SCU47EA", // Deus Ex Machina +"La R‚volution de Grunyev",// "SCU48EA", // Grunyev Revolution + +#endif + + NULL, +}; + +#endif + + + +#ifndef WIN32 //VG + + #define OPTION_WIDTH 236 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#error Can never again build without WIN32 defined. + #define OPTION_HEIGHT 162 +#else + #define OPTION_HEIGHT 162 +#endif + #define OPTION_X ((320 - OPTION_WIDTH) / 2) + #define OPTION_Y (200 - OPTION_HEIGHT) / 2 + +#else + + #define OPTION_WIDTH 560 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + #define OPTION_HEIGHT 332 +#else + #define OPTION_HEIGHT 300 +#endif + #define OPTION_X ((640 - OPTION_WIDTH) / 2) + #define OPTION_Y (400 - OPTION_HEIGHT) / 2 +#endif + +struct EObjectClass +{ + HousesType House; + int Scenario; + char Name[128]; + char FullName[128]; +}; + + +/* +** Derived from list class to handle expansion scenario listings. The listings +** are recorded as EObjectClass objects. The data contained specifies the scenario +** number, side, and text description. +*/ +class EListClass : public ListClass +{ + public: + EListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ListClass(id, x, y, w, h, flags, up, down) {}; + + virtual int Add_Object(EObjectClass * obj) { + return(ListClass::Add_Item((char const *)obj)); + } + virtual EObjectClass * Get_Object(int index) const { + return((EObjectClass *)ListClass::Get_Item(index)); + } + virtual EObjectClass * Current_Object(void) { + return((EObjectClass *)ListClass::Current_Item()); + } + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + virtual int Add_Item(char const * text) {return(ListClass::Add_Item(text));}; + virtual int Add_Item(int text) {return(ListClass::Add_Item(text));}; + virtual char const * Current_Item(void) const {return(ListClass::Current_Item());}; + virtual char const * Get_Item(int index) const {return(ListClass::Get_Item(index));}; +}; + + +/*********************************************************************************************** + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * * + * This overrides the normal list class draw action so that the scenario name will be * + * displayed along with the house name. * + * * + * INPUT: index -- The index of the entry that should be drawn. * + * * + * x,y -- Coordinate of upper left region to draw the entry into. * + * * + * width -- Width of region (pixels) to draw the entry. * + * * + * selected -- Is this entry considered selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1995 JLB : Created. * + *=============================================================================================*/ +void EListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + char buffer[128]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int text = TXT_NONE; + if (Get_Object(index)->House == HOUSE_GOOD) { + text = TXT_ALLIES; + } else { + text = TXT_SOVIET; + } + sprintf(buffer, "%s: %s", Text_String(text), Get_Object(index)->Name); + + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, 1); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + +#ifndef WIN32 + Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags & ~(TPF_CENTER), width, Tabs); +#else + Conquer_Clip_Text_Print(buffer, x + 100, y, scheme, TBLACK, flags & ~(TPF_CENTER), width, Tabs); +#endif +} + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ) // If not bCounterstrike, then this was called for Aftermath. +#else +bool Expansion_Dialog(void) +#endif +{ + GadgetClass * buttons = NULL; + +#ifndef WIN32 + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y+OPTION_HEIGHT-15); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y+OPTION_HEIGHT-15); +#else + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y + OPTION_HEIGHT - 50 ); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y + OPTION_HEIGHT - 50 ); +#endif + +#ifndef WIN32 + EListClass list(202, OPTION_X + 20, OPTION_Y+20, OPTION_WIDTH-40, OPTION_HEIGHT-40, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#else + EListClass list(202, OPTION_X+35, OPTION_Y + 30, OPTION_WIDTH-70, OPTION_HEIGHT - 85, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + +#endif + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + /* + ** Add in all the expansion scenarios. + */ + CCFileClass file; + char buffer[128], buffer2[128]; + char * sbuffer = (char*)_ShapeBuffer; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Though disgusted. + for (int index = 20; index < (36+18); index++) { +#else + for (int index = 20; index < 36; index++) { +#endif + +#ifndef CS_DEBUG + strcpy(buffer,ExpandNames[index - 20]); + strcpy(buffer2, ExpandNames[index - 20]); +#else + strcpy(buffer, TestNames2[index]); + strcpy(buffer2, TestNames2[index]); +#endif + if(buffer[0] == NULL) + break; + + strcat(buffer, ".INI"); + strcat(buffer2, ".INI"); + Scen.Set_Scenario_Name(buffer); + Scen.Scenario = index; + file.Set_Name(buffer); +#ifdef FIXIT_VERSION_3 + bool bOk; + if( index < 36 ) + bOk = bCounterstrike; + else + bOk = !bCounterstrike; + + if( bOk && file.Is_Available() ) + { +#else // FIXIT_VERSION_3 + if (file.Is_Available()) { +#endif // FIXIT_VERSION_3 + EObjectClass * obj = new EObjectClass; + switch(buffer[2]){ + + case 'G': + case 'g': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_GOOD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + case 'U': + case 'u': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_BAD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + + default: + break; + } + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + + + while (process) { + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } +#endif + + Call_Back(); + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT ); +#ifdef FIXIT_VERSION_3 + if( bCounterstrike ) + Draw_Caption( TXT_WOL_CS_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + else + Draw_Caption( TXT_WOL_AM_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); +#else + Draw_Caption(TXT_NEW_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); +#endif + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case 200|KN_BUTTON: + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + process = false; + okval = false; + break; + + case (KN_RETURN): + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (index = 0; index < list.Count(); index++) { + delete list.Get_Object(index); + } + + return(okval); + +} + diff --git a/CODE/EXTERNS.H b/CODE/EXTERNS.H new file mode 100644 index 0000000..08f1f18 --- /dev/null +++ b/CODE/EXTERNS.H @@ -0,0 +1,505 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/EXTERNS.H 2 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EXTERNS_H +#define EXTERNS_H + +#include "cell.h" + +#ifdef SCENARIO_EDITOR +#include "mapedit.h" +#endif +#include "techno.h" +#include "type.h" +#include "building.h" +#include "unit.h" +#include "credits.h" +#include "goptions.h" +#include "options.h" +#include "infantry.H" + + +extern char _staging_buffer[32000]; +extern "C" { +void _PRO(void); +} + +/* +** Convenient alias for MixFileClass object. This allows +** easier entry into the code and less clutter. +*/ +typedef MixFileClass MFCD; + +extern bool IsVQ640; +extern unsigned long GameVersion; +extern bool Debug_MotionCapture; +extern bool Debug_Rotate; +extern bool Debug_Quiet; +extern bool Debug_Cheat; +extern bool Debug_Remap; +extern bool Debug_Flag; +extern bool Debug_Lose; +extern bool Debug_Map; +extern bool Debug_Win; +extern bool Debug_Icon; +extern bool Debug_Passable; +extern bool Debug_Unshroud; +extern bool Debug_Threat; +extern bool Debug_Find_Path; +extern bool Debug_Check_Map; +extern bool Debug_Playtest; + +extern bool Debug_Heap_Dump; +extern bool Debug_Smart_Print; +extern bool Debug_Trap_Check_Heap; +extern bool Debug_Modem_Dump; +extern bool Debug_Print_Events; + +extern void const *LightningShapes; + +extern int NewINIFormat; + + +#ifdef FIXIT_ANTS +extern bool AntsEnabled; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool NewUnitsEnabled; +extern bool SecretUnitsEnabled; +extern int MTankDistance; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +extern int CarrierLaunchDelay; +#endif +#endif + +#ifdef FIXIT_NAME_OVERRIDE +extern char const * NameOverride[25]; +extern int NameIDOverride[25]; +#endif + +#ifdef WIN32 +extern bool GameInFocus; +extern unsigned char *InterpolatedPalettes[100]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; +extern int AllDone; +extern HANDLE hInstance; +extern bool InMovie; +extern WinTimerClass * WindowsTimer; +extern WWMouseClass * WWMouse; +extern GraphicBufferClass HiddenPage; +#define SeenPage SeenBuff +extern GraphicBufferClass VisiblePage; +extern GraphicViewPortClass SeenBuff; +extern GraphicBufferClass SysMemPage; +extern LPDIRECTSOUND SoundObject; +extern LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +extern int ScreenWidth; +extern int ScreenHeight; +extern GraphicBufferClass ModeXBuff; + +#else + +extern VideoBufferClass SeenPage; +extern GraphicBufferClass SeenBuff; +extern GraphicBufferClass & VisiblePage; +#endif + + +/* +** Dynamic global variables (these change or are initialized at run time). +*/ +extern MissionControlClass MissionControl[MISSION_COUNT]; +extern char const * TutorialText[225]; +extern Buffer * TheaterBuffer; +extern GetCDClass CDList; +extern CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern CCINIClass AftermathINI; +#endif +extern Benchmark * Benches; +extern int MapTriggerID; +extern int LogicTriggerID; +extern PKey FastKey; +extern PKey SlowKey; +extern RulesClass Rule; +extern KeyboardClass * Keyboard; +extern RandomStraw CryptRandom; +extern RandomClass NonCriticalRandomNumber; +extern CarryoverClass * Carryover; +extern ScenarioClass Scen; +extern RemapControlType ColorRemaps[PCOLOR_COUNT]; +extern RemapControlType MetalScheme; +extern RemapControlType GreyScheme; +extern VersionClass VerNum; +extern bool SlowPalette; +extern bool ScoresPresent; +extern bool AllowVoice; +extern NewConfigType NewConfig; +extern VoxType SpeakQueue; +extern bool PlayerWins; +extern bool PlayerLoses; +extern bool PlayerRestarts; +extern long Frame; +extern VoxType SpeechRecord[2]; +extern void * SpeechBuffer[2]; +extern int PreserveVQAScreen; +extern bool BreakoutAllowed; +extern bool Brokeout; + +extern GameOptionsClass Options; + +extern LogicClass Logic; +#ifdef SCENARIO_EDITOR +extern MapEditClass Map; +#else +extern MouseClass Map; +#endif +extern ScoreClass Score; +extern MonoClass MonoArray[DMONO_COUNT]; +extern MFCD * TheaterData; +extern MFCD * MoviesMix; +extern MFCD * GeneralMix; +extern MFCD * ScoreMix; +extern MFCD * MainMix; +extern MFCD * ConquerMix; +extern ThemeClass Theme; +extern SpecialClass Special; + +/* +** Game object allocation and tracking classes. +*/ +extern TFixedIHeapClass Aircraft; +extern TFixedIHeapClass Anims; +extern TFixedIHeapClass Buildings; +extern TFixedIHeapClass Bullets; +extern TFixedIHeapClass Factories; +extern TFixedIHeapClass Houses; +extern TFixedIHeapClass Infantry; +extern TFixedIHeapClass Overlays; +extern TFixedIHeapClass Smudges; +extern TFixedIHeapClass Teams; +extern TFixedIHeapClass TeamTypes; +extern TFixedIHeapClass Templates; +extern TFixedIHeapClass Terrains; +extern TFixedIHeapClass Triggers; +extern TFixedIHeapClass Units; +extern TFixedIHeapClass Vessels; +extern TFixedIHeapClass TriggerTypes; + +extern TFixedIHeapClass HouseTypes; +extern TFixedIHeapClass BuildingTypes; +extern TFixedIHeapClass AircraftTypes; +extern TFixedIHeapClass InfantryTypes; +extern TFixedIHeapClass BulletTypes; +extern TFixedIHeapClass AnimTypes; +extern TFixedIHeapClass UnitTypes; +extern TFixedIHeapClass VesselTypes; +extern TFixedIHeapClass TemplateTypes; +extern TFixedIHeapClass TerrainTypes; +extern TFixedIHeapClass OverlayTypes; +extern TFixedIHeapClass SmudgeTypes; + +extern FixedIHeapClass * HeapPointers[RTTI_COUNT]; + +extern TFixedIHeapClass Weapons; +extern TFixedIHeapClass Warheads; + +extern QueueClass OutList; +extern QueueClass DoList; + +#ifdef MIRROR_QUEUE +extern QueueClass MirrorList; +#endif + +extern DynamicVectorClass CurrentObject; +extern DynamicVectorClass LogicTriggers; +extern DynamicVectorClass MapTriggers; +extern DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + +extern BaseClass Base; + +/* These variables are used to keep track of the slowest speed of a team */ +extern MPHType TeamMaxSpeed[10]; +extern SpeedType TeamSpeed[10]; +extern bool FormMove; +extern SpeedType FormSpeed; +extern MPHType FormMaxSpeed; + +extern bool IsTanyaDead; +extern bool SaveTanya; + +extern bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool PendingTimeQuake; +extern TARGET TimeQuakeCenter; +extern fixed QuakeUnitDamage; +extern fixed QuakeBuildingDamage; +extern int QuakeInfantryDamage; +extern int QuakeDelay; +extern fixed ChronoTankDuration; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +extern fixed EngineerDamage; // Amount of damage an engineer does +extern fixed EngineerCaptureLevel; // Building damage level before engineer can capture +#endif +#endif + +/* +** Loaded data file pointers. +*/ +extern void const * Metal12FontPtr; +extern void const * MapFontPtr; +extern void const * VCRFontPtr; +extern void const * TypeFontPtr; +extern void const * Font3Ptr; +extern void const * Font6Ptr; +extern void const * EditorFont; +extern void const * Font8Ptr; +extern void const * FontLEDPtr; +extern void const * ScoreFontPtr; +extern void const * GradFont6Ptr; +extern char const * SystemStrings; +extern char const * DebugStrings; + +/* +** Miscellaneous globals. +*/ +extern ChronalVortexClass ChronalVortex; +extern TTimerClass TickCount; +extern bool PassedProximity; // used in display.cpp +extern HousesType Whom; +extern _VQAConfig AnimControl; +extern long SpareTicks; +extern long PathCount; +extern long CellCount; +extern long TargetScan; +extern long SidebarRedraws; +extern DMonoType MonoPage; +extern bool GameActive; +extern bool SpecialFlag; +extern bool ScenarioInit; +extern HouseClass * PlayerPtr; +extern PaletteClass CCPalette; +extern PaletteClass BlackPalette; +extern PaletteClass WhitePalette; +extern PaletteClass GamePalette; +//extern PaletteClass InGamePalette; +#define InGamePalette GamePalette +extern PaletteClass OriginalPalette; +extern PaletteClass ScorePalette; +extern int BuildLevel; +extern unsigned long ScenarioCRC; + +#ifdef FIXIT_VERSION_3 +extern bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +extern unsigned long PlayingAgainstVersion; // Negotiated version number +extern bool Version107InMix; // Is there a v1.07 in the game +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +extern bool bAutoSonarPulse; +#endif + +#ifdef SCENARIO_EDITOR +extern CELL CurrentCell; +#endif + +extern SessionClass Session; +extern NullModemClass NullModem; +extern IPXManagerClass Ipx; + +#if(TEN) +extern TenConnManClass *Ten; +#endif + +#if(MPATH) +extern MPlayerManClass *MPath; +#endif + +#if(TIMING_FIX) +extern int NewMaxAheadFrame1; +extern int NewMaxAheadFrame2; +#endif + +extern int Seed; +extern int CustomSeed; +extern GroundType Ground[LAND_COUNT]; + +/* +** Constant externs (data is not modified during game play). +*/ +extern char const * Missions[MISSION_COUNT]; +extern char const Keys[]; +extern char const * const VQName[VQ_COUNT]; +extern int CrateData[CRATE_COUNT]; +extern char const * const CrateNames[CRATE_COUNT]; +extern int CrateShares[CRATE_COUNT]; +extern AnimType CrateAnims[CRATE_COUNT]; +extern char const * const SpecialWeaponName[SPC_COUNT]; +extern int const SpecialWeaponHelp[SPC_COUNT]; +extern char const * const SpecialWeaponFile[SPC_COUNT]; +extern char const * const ArmorName[ARMOR_COUNT]; +extern char const * const QuarryName[QUARRY_COUNT]; +extern char const * const FormationName[FORMATION_COUNT]; +extern long const PlayCodes[]; +extern long const CheatCodes[]; +//extern char const * const ProjectileNames[]; +extern long const EditorCodes[]; +extern char const * const SourceName[SOURCE_COUNT]; +extern int const GroundColor[LAND_COUNT]; +extern int const SnowColor[LAND_COUNT]; +extern TheaterDataType const Theaters[THEATER_COUNT]; +extern unsigned char const Facing32[256]; +extern unsigned char const Facing16[256]; +extern signed char const Rotation16[256]; +extern unsigned char const Facing8[256]; +extern unsigned char const Pixel2Lepton[24]; +extern COORDINATE const StoppingCoordAbs[5]; +extern CELL const AdjacentCell[FACING_COUNT]; +extern COORDINATE const AdjacentCoord[FACING_COUNT]; +extern unsigned char const RemapCiv2[]; +extern unsigned char const RemapCiv4[]; +extern unsigned char const RemapCiv5[]; +extern unsigned char const RemapCiv6[]; +extern unsigned char const RemapCiv7[]; +extern unsigned char const RemapCiv8[]; +extern unsigned char const RemapCiv9[]; +extern unsigned char const RemapCiv10[]; +extern unsigned char const RemapEmber[]; + +extern int SoundOn; + +#ifdef WIN32 +extern GraphicViewPortClass HidPage; +#else +extern GraphicBufferClass HidPage; +#endif +extern int MenuList[][8]; +extern CDTimerClass FrameTimer; +extern CDTimerClass CountDownTimer; + +extern SpecialDialogType SpecialDialog; + +extern int RequiredCD; +extern int CurrentCD; +extern int MouseInstalled; + + +extern int LogLevel; +extern unsigned long LogLevelTime[ MAX_LOG_LEVEL ]; +extern unsigned long LogLastTime; + +extern class DynamicVectorClass test2; +extern class DynamicVectorClass test3; + +extern bool LogDump_Print; + +extern "C"{ + extern bool IsTheaterShape; +} + +extern void Reset_Theater_Shapes(void); +extern TheaterType LastTheater; +void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); +void Do_Vortex (int x, int y, int frame); + + +/************************************************************ +** Win32 specific externs +*/ +#ifdef WIN32 +extern bool ReadyToQuit; //Are we about to exit cleanly +extern bool InDebugger; //Are we being run from a debugger +void Memory_Error_Handler(void); //Memory error handler function +void WWDebugString (char *string); +#else +extern bool IsTheaterShape; +#endif //WIN32 + + +/************************************************************* +** Internet specific externs +*/ +#ifdef WIN32 + +extern char PlanetWestwoodHandle[]; //Planet WW user name +extern char PlanetWestwoodPassword[]; //Planet WW password +extern char PlanetWestwoodIPAddress[]; //IP of server or other player +extern long PlanetWestwoodPortNumber; //Port number to send to +extern bool PlanetWestwoodIsHost; //Flag true if player has control of game options +extern unsigned long PlanetWestwoodGameID; //Game ID +extern HWND WChatHWND; //Handle to Wchat window. +extern bool GameStatisticsPacketSent; +extern bool ConnectionLost; +extern void *PacketLater; +extern bool SpawnedFromWChat; +extern int ShowCommand; +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +extern int UnitBuildPenalty; + + +#endif //WIN32 + +/* +** From SENDFILE.CPP - externs for scenario file transfers +*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, unsigned int crc, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); +bool Get_Scenario_File_From_Host(char *return_name, int gametype); +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official); + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +extern MCIMovie* MciMovie; +#endif + +#include "mpgset.h" +extern MPGSettings* MpgSettings; +#endif + +#endif + diff --git a/CODE/FACE.CPP b/CODE/FACE.CPP new file mode 100644 index 0000000..30441f0 --- /dev/null +++ b/CODE/FACE.CPP @@ -0,0 +1,227 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * * + * This routine will find the facing (compass direction) from one location to another. * + * Typical use of this is in find path and other 'monster' movement logic. * + * * + * INPUT: x1,y1 -- X and Y coordinates for the source location. The coordinate 0,0 is * + * presumed to be the northwest corner of the map. * + * * + * x2,y2 -- X and Y coordinates for the destination (target) location. * + * * + * OUTPUT: Returns with the facing from the first coordinate to the second coordinate. The * + * value returned will range from 0 being North, increasing clockwise until reaching * + * 255 which is just shy of North in a Westerly direction. * + * * + * WARNINGS: This routine is only accurate to the 8 primary compass directions. It is much * + * faster than the Desired_Facing256() function so it should be used where speed * + * is more important than accuracy. * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +/*********************************************************************************************** + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * * + * This routine will figure the facing from the source coordinate toward the destination * + * coordinate. Typically, this routine is used for movement and other 'monster' logic. It * + * is more accurate than the corresponding Desired_Facing8() function, but is slower. * + * * + * INPUT: srcx, srcy -- The source coordinate to determine the facing from. * + * * + * dstx, dsty -- The destination (or target) coordinate to determine the facing * + * toward. * + * * + * OUTPUT: Returns with the facing from the source coordinate toward the destination * + * coordinate with 0 being North increasing in a clockwise direction. 64 is East, * + * 128 is South, etc. * + * * + * WARNINGS: The coordinate 0,0 is presumed to be in the Northwest corner of the map. * + * Although this routine is fast, it is not as fast as Desired_Facing8(). * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + //lint -e414 Division by zero cannot occur here. + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} diff --git a/CODE/FACE.H b/CODE/FACE.H new file mode 100644 index 0000000..940b7ee --- /dev/null +++ b/CODE/FACE.H @@ -0,0 +1,81 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACE_H +#define FACE_H + +// Enumerations of the facing values returned from Desired_Facing(). +typedef enum DirType { + DIR_MIN=0, + DIR_N=0, + DIR_NE=1<<5, + DIR_E=2<<5, + DIR_SE=3<<5, + DIR_S=4<<5, + DIR_SW=5<<5, + DIR_W=6<<5, + DIR_NW=7<<5, + DIR_MAX=255 +} DirType; + +// Operators that allow simple math with DirType. +inline DirType operator + (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator + (DirType f1, int f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, int f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} + + +// Function prototypes. +DirType Desired_Facing8(int x1, int y1, int x2, int y2); +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty); + +#endif + + + diff --git a/CODE/FACING.CPP b/CODE/FACING.CPP new file mode 100644 index 0000000..d560478 --- /dev/null +++ b/CODE/FACING.CPP @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FacingClass::FacingClass -- Default constructor for the facing class. * + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * FacingClass::Set_Current -- Sets the current rotation value. * + * FacingClass::Set_Desired -- Sets the desired facing value. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "facing.h" + + +/*********************************************************************************************** + * FacingClass::FacingClass -- Default constructor for the facing class. * + * * + * This default constructor merely sets the desired and current facing values to be the * + * same (North). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +FacingClass::FacingClass(void) +{ + CurrentFacing = DIR_N; + DesiredFacing = DIR_N; +} + + +/*********************************************************************************************** + * FacingClass::Set_Desired -- Sets the desired facing value. * + * * + * This routine is used to set the desired facing value without altering the current * + * facing setting. Typical use of this routine is when a vehicle needs to face a * + * direction, but currently isn't facing the correct direction. After this routine is * + * called, it is presumed that subsequent calls to Rotation_Adjust() will result in the * + * eventual alignment of the current facing. * + * * + * INPUT: facing -- The new facing to assign to the desired value. * + * * + * OUTPUT: bool; Did the desired facing value actually change by this routine call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Desired(DirType facing) +{ + if (DesiredFacing != facing) { + DesiredFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Set_Current -- Sets the current rotation value. * + * * + * This routine will set the current rotation value. It is used to override the facing * + * value without adjusting the desired setting. * + * * + * INPUT: facing -- The new facing to assign to the current facing value. * + * * + * OUTPUT: bool; Was the current setting changed by this routine. Failure means that the * + * current setting was already at the value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Current(DirType facing) +{ + if (CurrentFacing != facing) { + CurrentFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * * + * This routine is used when the current and desired facings differ but the current * + * facing should be adjusted toward the desired facing. The amount of rotation to adjust * + * is provided as a rotation rate parameter. Typical use of this routine is for turrets * + * and other vehicle related rotating. * + * * + * INPUT: rate -- The rotation rate to use when adjusting the current facing toward the * + * desired facing. A value of 127 means instantaneous rotation. * + * * + * OUTPUT: bool; Did the rotation result in the current facing transitioning from one * + * 1/32 zone to another? If true, then the owning object most likely will * + * need to be redrawn to reflect the change. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Rotation_Adjust(int rate) +{ + /* + ** Only perform the rotation adjustment if the desired facing is not the + ** same as the current facing. + */ + if (Is_Rotating()) { + rate = min(rate, 127); + + DirType oldfacing = CurrentFacing; + int diff = Difference(); + + /* + ** If the allowed facing change is greater than the difference between + ** the current facing and the desired facing, then just snap the + ** facing to the new value. + */ + if (ABS(diff) < rate) { + CurrentFacing = DesiredFacing; + } else { + + /* + ** Adjust the current facing clockwise or counterclockwise depending + ** on the shortest distance to the desired facing from the current + ** facing. + */ + if (diff < 0) { + CurrentFacing = (DirType)(CurrentFacing - (DirType)rate); + } else { + CurrentFacing = (DirType)(CurrentFacing + (DirType)rate); + } + } + + /* + ** If this facing adjustment caused the current facing to rotate into a + ** new 1/32 rotation zone (likely to cause a redraw), then return + ** this fact with a true value. + */ + return(Dir_To_32(CurrentFacing) != Dir_To_32(oldfacing)); + } + return(false); +} diff --git a/CODE/FACING.H b/CODE/FACING.H new file mode 100644 index 0000000..0d96b56 --- /dev/null +++ b/CODE/FACING.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACING_H +#define FACING_H + +/* +** This is a general facing handler class. It is used in those cases where facing needs to be +** kept track of, but there could also be an associated desired facing. The current facing +** is supposed to transition to the desired state over time. Using this class facilitates this +** processing as well as isolating the rest of the code from the internals. +*/ +class FacingClass +{ + public: + FacingClass(void); + FacingClass(DirType dir) : CurrentFacing(dir), DesiredFacing(dir) {}; + FacingClass(NoInitClass const & ) {}; + + operator DirType (void) const {return(CurrentFacing);}; + + DirType Current(void) const {return(CurrentFacing);}; + DirType Desired(void) const {return(DesiredFacing);}; + + int Set_Desired(DirType facing); + int Set_Current(DirType facing); + + void Set(DirType facing) { + Set_Current(facing); + Set_Desired(facing); + }; + + DirType Get(void) const { return CurrentFacing; } + + int Is_Rotating(void) const {return (DesiredFacing != CurrentFacing);}; + int Difference(void) const {return (int)(signed char)((int)DesiredFacing - (int)CurrentFacing);} + int Difference(DirType facing) const {return (int)(signed char)((int)facing - (int)CurrentFacing);} + int Rotation_Adjust(int rate); + + private: + DirType CurrentFacing; + DirType DesiredFacing; +}; + + +#endif diff --git a/CODE/FACTORY.CPP b/CODE/FACTORY.CPP new file mode 100644 index 0000000..64ec8f1 --- /dev/null +++ b/CODE/FACTORY.CPP @@ -0,0 +1,671 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACTORY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACTORY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FactoryClass::AI -- Process factory production logic. * + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * FactoryClass::Get_Special_Item -- gets factory's spc prod item * + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * FactoryClass::Set -- Assigns a factory to produce an object. * + * FactoryClass::Set -- Force factory to "produce" special object. * + * FactoryClass::Start -- Resumes production after suspension or creation. * + * FactoryClass::Suspend -- Temporarily stop production. * + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * * + * This brings the factory into a null state. It is called when a factory object is * + * created. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::FactoryClass(void) : + RTTI(RTTI_FACTORY), + ID(Factories.ID(this)), + IsSuspended(false), + IsDifferent(false), + Balance(0), + OriginalBalance(0), + Object(0), + SpecialItem(SPC_NONE), + House(0) +{ + Set_Rate(0); + Set_Stage(0); +} + + +/*********************************************************************************************** + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * * + * This cleans up a factory object in preparation for deletion. If there is currently * + * an object in production, it is abandoned and money is refunded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::~FactoryClass(void) +{ + if (GameActive) { + Abandon(); + } +} + + +/*********************************************************************************************** + * FactoryClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the factory list and objects. This routine is typically * + * used in preparation for a new scenario load. All factory are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Init(void) +{ + Factories.Free_All(); +} + + +/*********************************************************************************************** + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * * + * This routine allocates a factory from the free factory pool. If there is no more room * + * to allocate a factory, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to the newly allocated factory object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void * FactoryClass::operator new(size_t) +{ + void * ptr = Factories.Allocate(); + if (ptr) { + ((FactoryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * * + * This returns the factory object back to the factory allocation pool. The factory is then * + * available to be allocated. * + * * + * INPUT: ptr -- Pointer to the factory object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::operator delete(void * ptr) +{ + if (ptr) { + ((FactoryClass *)ptr)->IsActive = false; + } + Factories.Free((FactoryClass *)ptr); +} + + +/*********************************************************************************************** + * FactoryClass::AI -- Process factory production logic. * + * * + * This routine should be called once per game tick. It handles the production process. * + * As production proceeds, money is deducted from the owner object's house. When production * + * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * + * required after that point. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 01/04/1995 JLB : Uses exact installment payment method. * + *=============================================================================================*/ +void FactoryClass::AI(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended && (Object != NULL || SpecialItem)) { + for (int index = 0; index < 1; index++) { + if (!Has_Completed() && Graphic_Logic() ) { + IsDifferent = true; + + int cost = Cost_Per_Tick(); + + cost = min(cost, Balance); + + /* + ** Enough time has expired so that another production step can occur. + ** If there is insufficient funds, then go back one production step and + ** continue the countdown. The idea being that by the time the next + ** production step occurs, there may be sufficient funds available. + */ + if (cost > House->Available_Money()) { + Set_Stage(Fetch_Stage()-1); + } else { + House->Spend_Money(cost); + Balance -= cost; + } + + /* + ** If the production has completed, then suspend further production. + */ + if (Fetch_Stage() == STEP_COUNT) { + IsSuspended = true; + Set_Rate(0); + House->Spend_Money(Balance); + Balance = 0; + } + } + } + } +} + + +/*********************************************************************************************** + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * * + * Use this routine to determine if production has advanced at least one step. By using * + * this function, intelligent rendering may be performed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the production process advanced one step since the last time this * + * function was called? * + * * + * WARNINGS: This function clears the changed status flag as a side effect. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Changed(void) +{ + assert(Factories.ID(this) == ID); + + bool changed = IsDifferent; + IsDifferent = false; + return(changed); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Assigns a factory to produce an object. * + * * + * This routine initializes a factory to produce the object specified. The desired object * + * type is created and placed in suspended animation (limbo) until such time as production * + * completes. Production is not actually started by this routine. An explicit call to * + * Start() is required to begin production. * + * * + * INPUT: object -- Reference to the object type class that is to be produced. * + * * + * house -- Reference to the owner of the object to be produced. * + * * + * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * + * that the object could not be created. This is catastrophic and in such * + * cases, the factory object should be deleted. * + * * + * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * + * the factory means that the factory is useless and should be deleted. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) +{ + assert(Factories.ID(this) == ID); + + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + Object = (TechnoClass *)object.Create_One_Of(&house); + + /* + ** Buildings that are constructed, will default to rebuilding on so that + ** repair can commence and base rebuilding can occur. + */ + if (!house.IsHuman && Object != NULL && Object->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)Object)->IsToRebuild = true; + } + + if (Object) { + House = Object->House; + Balance = object.Cost_Of() * house.CostBias; + Object->PurchasePrice = Balance; + } + + /* + ** If all was set up successfully, then return true. + */ + return(Object != NULL); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Fills a factory with an already completed object. * + * * + * This routine is called when a produced object is in placement mode but then placement * + * is suspended. The object must then return to the factory as if it were newly completed * + * and awaiting removal. * + * * + * INPUT: object -- The object to return to the factory. * + * * + * OUTPUT: none * + * * + * WARNINGS: This will abandon any current object being produced at the factory in order * + * to set the new object into it. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Set(TechnoClass & object) +{ + assert(Factories.ID(this) == ID); + + Abandon(); + Object = &object; + House = Object->House; + Balance = 0; + Set_Rate(0); + Set_Stage(STEP_COUNT); + IsDifferent = true; + IsSuspended = true; +} + + +/*********************************************************************************************** + * FactoryClass::Suspend -- Temporarily stop production. * + * * + * This routine will suspend production until a subsequent call to Start() or Abandon(). * + * Typical use of this function is when the player puts production on hold or when there * + * is insufficient funds. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * + * factory was empty or production was already stopped (or never started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Suspend(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended) { + IsSuspended = true; + Set_Rate(0); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Start -- Resumes production after suspension or creation. * + * * + * This function will start the production process. It works for newly created factory * + * objects, as well as if production had been suspended previously. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production started? A false return value means that the factory is * + * empty or there is insufficient credits to begin production. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Start(void) +{ + assert(Factories.ID(this) == ID); + + if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { + if (House->Available_Money() >= Cost_Per_Tick()) { + int time; + + if (Object) { + time = Object->Time_To_Build(); +// } else { +// time = TICKS_PER_MINUTE * 5; + } + + /* + ** Adjust time according to IQ setting of computer controlled house. The + ** build time will range from double normal time at the slowest to + ** just normal time at the fastest. + */ + if (!House->IsHuman && Rule.Diff[House->Difficulty].IsBuildSlowdown) { + time = time * Inverse(fixed(House->IQ+Rule.MaxIQ, Rule.MaxIQ*2)); + } + + int rate = time / Bound(House->Power_Fraction(), fixed(1, 16), fixed(1)); +// int frac = House->Power_Fraction(); +// frac = Bound(frac, 0x0010, 0x0100); +// int rate = (time*256) / frac; + + rate /= STEP_COUNT; + rate = Bound(rate, 1, 255); + + Set_Rate(rate); + IsSuspended = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * * + * This routine is used when construction is to be abandoned and current money spend is * + * to be refunded. This function effectively clears out this factory of all record of the * + * producing object so that it may either be deleted or started anew with the Set() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * + * factory was not producing any object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Abandon(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + + if (Object) { + /* + ** Refund all money expended so far, back to the owner of the object under construction. + */ + int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; + House->Refund_Money(money - Balance); + Balance = 0; + + /* + ** Delete the object under construction. + */ + ScenarioInit++; + delete Object; + Object = NULL; + ScenarioInit--; + } + if (SpecialItem) { + SpecialItem = SPC_NONE; + } + + /* + ** Set the factory back to the idle and empty state. + */ + Set_Rate(0); + Set_Stage(0); + IsSuspended = true; + IsDifferent = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * * + * Use this routine to determine what animation (or completion step) the factory is * + * currently on. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a completion step number between 0 (uncompleted), to STEP_COUNT (completed)* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Completion(void) +{ + assert(Factories.ID(this) == ID); + + return(Fetch_Stage()); +} + + +/*********************************************************************************************** + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * * + * Use this routine to examine the factory object in order to determine if the associated * + * object has completed production and is awaiting placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + return(true); + } + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * * + * This routine gets the pointer to the currently constructing object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object undergoing construction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * FactoryClass::Get_Object(void) const +{ + assert(Factories.ID(this) == ID); + + return(Object); +} + + +/*************************************************************************** + * FactoryClass::Get_Special_Item -- gets factory spc prod item * + * * + * INPUT: none * + * * + * OUTPUT: int the item the factory is currently working on * + * * + * HISTORY: * + * 05/05/1995 PWG : Created. * + *=========================================================================*/ +int FactoryClass::Get_Special_Item(void) const +{ + assert(Factories.ID(this) == ID); + + return(SpecialItem); +} + + +/*********************************************************************************************** + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * * + * Use this routine to determine the cost per game "tick" to produce the object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of credits necessary to advance production one game tick. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Cost_Per_Tick(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + int steps = STEP_COUNT - Fetch_Stage(); + if (steps) { + return(Balance / steps); + } + return(Balance); + } + return(0); +} + + +/*********************************************************************************************** + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * * + * This routine is called after production completes, and the object produced has been * + * placed into the game. It resets the factory for deletion or starting of new production. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * + * any completed object. An immediate second call to this routine will also * + * yield false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + Object = NULL; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + SpecialItem = SPC_NONE; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + return(false); +} + + diff --git a/CODE/FACTORY.H b/CODE/FACTORY.H new file mode 100644 index 0000000..90d2f89 --- /dev/null +++ b/CODE/FACTORY.H @@ -0,0 +1,146 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FACTORY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FACTORY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : December 26, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "stage.h" + +class FactoryClass : private StageClass +{ + public: + RTTIType RTTI; + int ID; + + + FactoryClass(void); + FactoryClass(NoInitClass const & x) : StageClass(x) {}; + ~FactoryClass(void); + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + static void Init(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Abandon(void); + bool Completed(void); + bool Has_Changed(void); + bool Has_Completed(void); + bool Is_Building(void) const {return(Fetch_Rate() != 0);}; + bool Set(TechnoTypeClass const & object, HouseClass & house); + bool Set(int const & type, HouseClass & house); + bool Start(void); + bool Suspend(void); + int Completion(void); + TechnoClass * Get_Object(void) const; + int Get_Special_Item(void) const; + void AI(void); + void Set(TechnoClass & object); + HouseClass * Get_House(void) {return(House);}; + char const * Name(void) {return("Factory");} + + /* + ** This flag is used to maintain the pool of factory class objects. If the object has + ** been allocated, then this flag is true. Otherwise, the object is free to be + ** allocated. + */ + unsigned IsActive:1; + + enum StepCountEnum { + STEP_COUNT=54 // Number of steps to break production down into. + }; + protected: + + int Cost_Per_Tick(void); + + private: + + /* + ** If production is temporarily suspended, then this flag will be true. A factory + ** is suspended when it is first created, when production has completed, and when + ** explicitly instructed to Suspend() production. Suspended production is not + ** abandoned. It may be resumed with a call to Start(). + */ + unsigned IsSuspended:1; + + /* + ** If the AI process detected that the production process has advanced far enough + ** that a change in the building animation would occur, this flag will be true. + ** Examination of this flag (through the Has_Changed function) allows intelligent + ** updating of any production graphic. + */ + unsigned IsDifferent:1; + + /* + ** This records the balance due on the current production item. This value will + ** be reduced as production proceeds. It will reach zero the moment production has + ** finished. Using this method ensures that the total production cost will be EXACT + ** regardless of the number of installment payments that are made. + */ + int Balance; + int OriginalBalance; + + /* + ** This is the object that is being produced. It is held in a state of limbo while + ** undergoing production. Since the object is created at the time production is + ** started, it is always available when production completes. + */ + TechnoClass * Object; + + /* + ** If the factory is not producing an object and is instead producing + ** a special item, then special item will be set. + */ + int SpecialItem; + + /* + ** The factory has to be doing production for one house or another. + ** The house pointer will point to whichever house it is being done + ** for. + */ + HouseClass * House; +}; + +#endif diff --git a/CODE/FAKESOCK.H b/CODE/FAKESOCK.H new file mode 100644 index 0000000..3c23ec6 --- /dev/null +++ b/CODE/FAKESOCK.H @@ -0,0 +1,53 @@ +/* +** 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 . +*/ + + +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : FAKESOCK.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 6th, 1996 * + * * + * Last Update : August 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Stub replacement for the Winsock interface in DOS * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WIN32 + +class TcpipManagerClass { + + public: + inline BOOL Get_Connected(void) {return (FALSE);} + +}; + +extern TcpipManagerClass Winsock; +#endif diff --git a/CODE/FIELD.CPP b/CODE/FIELD.CPP new file mode 100644 index 0000000..2b3473a --- /dev/null +++ b/CODE/FIELD.CPP @@ -0,0 +1,214 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include "field.h" + +FieldClass::FieldClass(char *id, char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, char *data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Convert the variables to host order. This needs to be converted so + // the switch statement does compares on the data that follows. + // + Size = ntohs(Size); + + DataType = ntohs(DataType); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} + diff --git a/CODE/FIELD.H b/CODE/FIELD.H new file mode 100644 index 0000000..20e0c67 --- /dev/null +++ b/CODE/FIELD.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __FIELD_H +#define __FIELD_H + +#include +#include + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass { + + public: + friend class PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + void *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + +#endif diff --git a/CODE/FILE.CPP b/CODE/FILE.CPP new file mode 100644 index 0000000..fa6ab16 --- /dev/null +++ b/CODE/FILE.CPP @@ -0,0 +1,18 @@ +/* +** 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 . +*/ + diff --git a/CODE/FILEPCX.H b/CODE/FILEPCX.H new file mode 100644 index 0000000..3091ccb --- /dev/null +++ b/CODE/FILEPCX.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/CODE/FINDPATH.CPP b/CODE/FINDPATH.CPP new file mode 100644 index 0000000..d79fe8f --- /dev/null +++ b/CODE/FINDPATH.CPP @@ -0,0 +1,1294 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FINDPATH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FINDPATH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 25, 1995 [PWG] * + * * + * The path algorithm works by following a LOS path to the target. If it * + * collides with an impassable spot, it uses an Edge following routine to * + * get around it. The edge follower moves along the edge in a clockwise or * + * counter clockwise fashion until finding the destination spot. The * + * destination is determined by Find_Path. It is the first passable that * + * can be reached (so it will handle the doughnut case, where there is * + * a passable in the center of an unreachable area). * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Path_Overlap -- clears the path overlap list * + * Find_Path -- Find a path from point a to point b. * + * Find_Path_Cell -- Finds a given cell on a specified path * + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * Get_New_XY -- Get the new x,y based on current position and direction. * + * Optimize_Moves -- Optimize the move list. * + * Set_Path_Overlap -- Sets the overlap bit for given cell * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include + +/* +** When an edge search is started, it can be performed CLOCKwise or +** COUNTERCLOCKwise direction. +*/ +#define CLOCK (FacingType)1 // Clockwise. +#define COUNTERCLOCK (FacingType)-1 // Counterclockwise. + +/* +** If defined, diagonal moves are allowed, else no diagonals. +*/ +#define DIAGONAL + +/* +** This is the marker to signify the end of the path list. +*/ +#define END FACING_NONE + +/* +** "- 1" test for bit manipulation. +*/ +#define TEST + +/* +** If memory is more important than speed, set this define to +** true. It will then perform intermediate optimizations to get the most +** milage out of a limited movement list staging area. If this value +** is true then it figures paths a bit more intelligently. +*/ +#define SAVEMEM true + +/* +** Modify this macro so that given two cell values, it will return +** a value between 0 and 7, with 0 being North and moving +** clockwise (just like map degrees). +*/ +#define CELL_FACING(a, b) Dir_Facing(::Direction((a), (b))) + + +/*-------------------------------------------------------------------------*/ +/* +** Cells values are really indexes into the 'map'. The following value is +** the X width of the map. +*/ +#define MODULO MAP_CELL_W + +/* +** Maximum lookahead cells. Twice this value in bytes will be +** reserved on the stack. The smaller this number, the faster the processing. +*/ +#define MAX_MLIST_SIZE 300 +#define THREAT_THRESHOLD 5 + + +#define MAX_PATH_EDGE_FOLLOW 400 + +#ifdef NEVER +typedef enum { + FACING_N, // North + FACING_NE, // North-East + FACING_E, // East + FACING_SE, // South-East + FACING_S, // South + FACING_SW, // South-West + FACING_W, // West + FACING_NW, // North-West + + FACING_COUNT // Total of 8 directions (0..7). +} FacingType; +#endif + + +/*-------------------------------------------------------------------------*/ +//static bool DrawPath; + +inline FacingType Opposite(FacingType face) +{ + return( (FacingType) (face ^ 4)); +} + + +inline static FacingType Next_Direction(FacingType facing, FacingType dir) +{ + facing = facing + dir; + #ifndef DIAGONAL + facing = (FacingType)(facing & 0x06); + #endif + return(facing); +} + +/*=========================================================================*/ +/* Define a couple of variables which are private to the module they are */ +/* declared in. */ +/*=========================================================================*/ +static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path +static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path +static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path + + +//static CELL MoveMask = 0; +static CELL DestLocation; +static CELL StartLocation; + +/*************************************************************************** + * Point_Relative_To_Line -- Relation between a point and a line * + * * + * If a point is on a line then the following function holds true: * + * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * + * line (x1,z1),(x2,z2). * + * If the right side is > then the left side then the point is on one * + * side of the line and if the right side is < the the left side, then* + * the point is on the other side of the line. By subtracting one side* + * from the other we can determine on what side (if any) the point is on* + * by testing the side of the resulting subtraction. * + * * + * INPUT: * + * int x - x pos of point. * + * int z - z pos of point. * + * int x1 - x pos of first end of line segment. * + * int z1 - z pos of first end of line segment. * + * int x1 - x pos of second end of line segment. * + * int z1 - z pos of second end of line segment. * + * * + * OUTPUT: * + * Assuming (x1,z1) is north, (x2,z2) is south: * + * 0 : point is on line. * + * > 0 : point is east of line. * + * < 0 : point is west of line. * + * * + * WARNINGS: * + * Remember that int means that assumes 16 bits of precision. * + * * + * HISTORY: * + * 10/28/1994 SKB : Created. * + *=========================================================================*/ +int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) +{ + return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); +} + + +/*************************************************************************** + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * * + * While in the midst of the Follow Edge logic, it is possible (due to the * + * fact that we support diagonal movement) to begin looping around a * + * column of some type. The Unravel loop function will scan backward * + * through the list and fixup the path to try to prevent the loop. * + * * + * INPUT: path - pointer to the generated path so we can pull the * + * commands out of it. * + * cell - the cell we tried to enter that generated the * + * double overlap condition. * + * dir - the direction we tried to enter from when we * + * generated the double overlap condition * + * startx - the start x position of this path segment * + * starty - the start y position of this path segment * + * destx - the dest x position for this path segment * + * desty - the dest y position for this path segment * + * * + * OUTPUT: TRUE - loop has been successfully unravelled * + * FALSE - loop can not be unravelled so abort follow edge * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/25/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Unravel_Loop(PathType * path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) +{ + /* + ** Walk back to the actual cell before we advanced our position + */ + FacingType curr_dir = dir; + CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); + int idx = path->Length; // start at the last position + FacingType * list = &path->Command[idx-1]; // point to the last command + int checkx; + int checky; + int last_was_line = false; + + /* + ** loop backward through the list searching for a point that is + ** on the line. If the point was a diagonal move then adjust + ** it. + */ + while (idx) { + checkx = Cell_X(curr_pos); + checky = Cell_Y(curr_pos); + + if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { + + /* + ** We have now found a point on the line. Now we must check to see + ** if we left the line on a diagonal. If we did then we need to fix + ** it up. + */ + if (curr_dir & 1 && curr_pos != path->LastFixup) { + cell = curr_pos; + dir = *(list-1); + path->Length = idx; + path->LastFixup = curr_pos; + return(true); + } + + last_was_line = !last_was_line; + } + + /* + ** Since this cell will not be in the list, then pull out its cost + */ + path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); + + /* + ** Remove this cells flag from the overlap list for the path + */ +#ifdef TEST + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31))); +#else + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); +#endif + + /* + ** Adjust to the next list position and direction. + */ + curr_dir = *list--; + curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); + idx--; + } + + /* + ** If we can't modify the list to eliminate the problem, then we have + ** a larger problem in that we have deleted all of the cells in the + ** list. + */ + return(false); +} + + +/*************************************************************************** + * Register_Cell -- registers a cell on our path and check for backtrack * + * * + * This function adds a new cell to our path. If the cell has already * + * been recorded as part of our path, then this function moves back down * + * the list truncating it at the point we registered that cell. This * + * function will eliminate all backtracking from the list. * + * * + * INPUT: long * list - the list to set the overlap bit for * + * CELL cell - the cell to mark on the overlap list * + * * + * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * + * * + * HISTORY: * + * 05/23/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Register_Cell(PathType * path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType * list; + int pos = cell >> 5; +#ifdef TEST + int bit = (cell & 31); +#else + int bit = (cell & 31) - 1; +#endif + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + path->Length--; + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + int idx = 0; + list = path->Command; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words). + ** + ** PWG 8/16/95 - However there is no sense searching the list if + ** the cell we have overlapped on is the cell we + ** started in. + */ + + if (pos != cell) { + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + idx++; + list++; + } + newlen = idx; + } + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} + + +/*********************************************************************************************** + * Find_Path -- Find a path from point a to point b. * + * * + * INPUT: int source x,y, int destination x,y, char *final moves * + * array to store moves, int maximum moves we may attempt * + * * + * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * + * not reach the destination * + * * + * WARNINGS: This algorithm assumes that the target is NOT situated * + * inside an impassable. If this case may arise, the do-while * + * statement inside the inner while (true) must be changed * + * to include a check to se if the next_x,y is equal to the * + * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + *=============================================================================================*/ +PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, MoveType threshhold) +{ + CELL source = Coord_Cell(Coord); // Source expressed as cell + static PathType path; // Main path control. + CELL next; // Next cell to enter + CELL startcell; // Cell we started in + FacingType direction; // Working direction of look ahead. + FacingType newdir; // Tentative facing value. + + bool left=false, // Was leftward path legal? + right=false; // Was rightward path legal? + + int len; // Length of detour command list. + int unit_threat; // Calculated unit threat rating + int cost; // Cost to enter the square + FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. + moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. + PathType pleft,pright; // Path control structures. + PathType * which; // Which path to actually use. + int threat; + int threat_stage; + + /* + ** If we have been provided an illegal place to store our final moves + ** then forget it. + */ + if (!final_moves) return(NULL); + + BStart(BENCH_FINDPATH); + + PathCount++; + + if (Team && Team->Class->IsRoundAbout) { + unit_threat = (Team) ? Team->Risk : Risk(); + threat_stage = 0; + threat = 0; + } else { + unit_threat = threat = -1; + } + + StartLocation = source; + DestLocation = dest; + + /* + ** Initialize the path structure so that we can keep track of the + ** path. + */ + path.Start = source; + path.Cost = 0; + path.Length = 0; + path.Command = final_moves; + path.Command[0] = END; + path.Overlap = MainOverlap; + path.LastOverlap = -1; + path.LastFixup = -1; + + memset(path.Overlap, 0, sizeof(MainOverlap)); + + /* + ** Clear the over lap list and then make sure that our starting position is marked + ** on the overlap list. (Otherwise the harvesters will drive in circles... ) + */ +#ifdef TEST + path.Overlap[source >> 5] |= (1 << ((source & 31))); +#else + path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); +#endif + + startcell = source; + + /* + ** Account for trailing end of list command, so reduce the maximum + ** allowed legal commands to reflect this. + */ + maxlen--; + + /* + ** As long as there is room to put commands in the movement command list, + ** then put commands in it. We build the path using the following + ** methodology. + ** + ** 1. Scan through the desired straight line path until we either hit an + ** impassable or have created a valid path. + ** + ** 2. If we have hit an impassable, walk through the impassable to make + ** sure that there is a passable on the other side. If there is not + ** and we can not change the impassable, then this list is dead. + ** + ** 3. Walk around the impassable on both the left and right edges and + ** take the shorter of the two paths. + ** + ** 4. Taking the new location as our start location start again with + ** step #1. + */ + while (path.Length < maxlen) { + +top_of_list: + /* + ** Have we reached the destination already? If so abort any further + ** command building. + */ + if (startcell == dest) { + break; + } + + /* + ** Find the absolute correct direction to reach the next straight + ** line cell and what cell it is. + */ + direction = CELL_FACING(startcell, dest); + next = Adjacent_Cell(startcell, direction); + + /* + ** If we can move here, then make this our next move. + */ + cost = Passable_Cell(next, direction, threat, threshhold); + if (cost) { + Register_Cell(&path, next, direction, cost, threshhold); + } else { + /* + ** If the impassable location is actually the destination, + ** then stop here and consider this "good enough". + */ + if (next == dest) break; + + /* + ** We could not move to the next cell, so follow through the + ** impassable until we find a passable spot that can be reached. + ** Once we find a passable, figure out the shortest path to it. + ** Since we have variable passable conditions this is not as + ** simple as it used to be. The limiter loop below allows us to + ** step through ten doughnuts before we give up. + */ + for (int limiter = 0; limiter < 5; limiter++) { + + /* + ** Get the next passable position by zipping through the + ** impassable positions until a passable position is found + ** or the destination is reached. + */ + for (;;) { + + /* + ** Move one step closer toward destination. + */ + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + + /* + ** If the cell is passable then we have been completely + ** successful. If the cell is not passable then continue. + */ + if (Passable_Cell(next, FACING_NONE, threat, threshhold)) { +// if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { + break; + } + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + } + + /* + ** Try to find a path to the passable position by following + ** the edge of the blocking object in both CLOCKwise and + ** COUNTERCLOCKwise fashions. + */ + int follow_len = maxlen + (maxlen >> 1); + + Mem_Copy(&path, &pleft, sizeof(PathType)); + pleft.Command = &moves_left[0]; + pleft.Overlap = LeftOverlap; + Mem_Copy(path.Command, pleft.Command, path.Length); + Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left)/sizeof(moves_left[0]), threshhold); +// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); + + if (left) { + follow_len = min(maxlen, pleft.Length + (pleft.Length >> 1)); + } + + Mem_Copy(&path, &pright, sizeof(PathType)); + pright.Command = &moves_right[0]; + pright.Overlap = RightOverlap; + Mem_Copy(path.Command, pright.Command, path.Length); + Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right)/sizeof(moves_right[0]), threshhold); +// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); + + /* + ** If we could find a path, break from this loop. Otherwise this + ** means that we have found a "hole" of passable terrain that + ** cannot be reached by normal means. Scan forward looking for + ** the other side of the "doughnut". + */ + if (left || right) break; + + /* + ** If no path can be found to the intermediate cell, then + ** presume we have found a doughnut of some sort. Scan + ** forward until the next impassable is found and then + ** process this loop again. + */ + do { + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + } while (Passable_Cell(next, newdir, threat, threshhold)); + } + + if (!left && !right) break; + + /* + ** We found a path around the impassable locations, so figure out + ** which one was the smallest and copy those moves into the + ** path.Command array. + */ + which = &pleft; + if (right) { + which = &pright; + if (left) { + if (pleft.Length < pright.Length) { + which = &pleft; + } else { + which = &pright; + } + } + } + + /* + ** Record as much as possible of the shorter of the two + ** paths. The trailing EOL command is not copied because + ** this may not be the end of the find path logic. + */ + len = which->Length; + len = min(len, maxlen); + if (len > 0) { + memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); + memcpy(&path.Command[0], &which->Command[0], len * sizeof(FacingType)); + path.Length = len; + path.Cost = which->Cost; + path.LastOverlap = -1; + path.LastFixup = -1; + } else { + break; + } + } + startcell = next; + } + +end_of_list: + /* + ** Poke in the stop command. + */ + if (path.Length < maxlen) { + path.Command[path.Length++] = END; + } + + /* + ** Optimize the move list but only necessary if + ** diagonal moves are allowed. + */ + #ifdef DIAGONAL + Optimize_Moves(&path, threshhold); + #endif + + BEnd(BENCH_FINDPATH); + + return(&path); +} + + +/*********************************************************************************************** + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * * + * INPUT: start -- cell to head from * + * * + * target -- Target cell to head to. * + * * + * path -- Pointer to path list structure. * + * * + * search -- Direction of search (1=clock, -1=counterclock). * + * * + * olddir -- Facing impassible direction from start. * + * * + * callback -- Function pointer for determining if a cell is * + * passable or not. * + * * + * OUTPUT: bool: Could a path be found to the desired cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized & commented. * + *=============================================================================================*/ +bool FootClass::Follow_Edge(CELL start, CELL target, PathType * path, FacingType search, FacingType olddir, int threat, int , int max_cells, MoveType threshhold) +{ + FacingType newdir; // Direction of facing before surrounding cell check. + CELL oldcell, // Current cell. + newcell; // Tentative new cell. + int cost; // Working cost value. + int startx; + int starty; + int online=true; + int targetx; + int targety; + int oldval = 0; + int cellcount=0; + int forceout = false; + FacingType firstdir = (FacingType)-1; + CELL firstcell = -1; + bool stepped_off_line = false; + startx = Cell_X(start); + starty = Cell_Y(start); + targetx = Cell_X(target); + targety = Cell_Y(target); + + if (!path) return(false); + path->LastOverlap = -1; + path->LastFixup = -1; + + #ifndef DIAGONAL + /* + ** The edge following algorithm doesn't "do" diagonals. Force initial facing + ** to be an even 90 degree value. Adjust it in the direction it should be + ** rotating. + */ + if (olddir & 0x01) { + olddir = Next_Direction(olddir, search); + } + #endif + + newdir = Next_Direction(olddir, search); + oldcell = start; + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** Continue until we find our target, find our original starting spot, + ** or run out of moves. + */ + while (path->Length < max_cells) { + + /* + ** Look in all the adjacent cells to determine a passable one that + ** most closely matches the desired direction (working in the specified + ** direction). + */ + newdir = olddir; + for (;;) { + bool forcefail; // Is failure forced? + + forcefail = false; + + #ifdef DIAGONAL + /* + ** Rotate 45/90 degrees in desired direction. + */ + newdir = Next_Direction(newdir, search); + + /* + ** If facing a diagonal we must check the next 90 degree location + ** to make sure that we don't walk right by the destination. This + ** will happen if the destination it is at the corner edge of an + ** impassable that we are moving around. + */ + if (newdir & FACING_NE) { + CELL checkcell; // Non-diagonal check cell. + //int x,y; + + checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); + + if (checkcell == target) { + + /* + ** This only works if in fact, it is possible to move to the + ** cell from the current location. + */ + cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); + if (cost) { + /* + ** YES! The destination is at the corner of an impassable, so + ** set the direction to point directly at it and then the + ** scanning will terminate later. + */ + newdir = Next_Direction(newdir, search); + newcell = Adjacent_Cell(oldcell, newdir); + break; + } + } + + /* + ** Perform special diagonal check. If the edge follower would cross the + ** diagonal or fall on the diagonal line from the source, then consider + ** that cell impassible. Otherwise, the find path algorithm will fail + ** when there are two impassible locations located on a diagonal + ** that is lined up between the source and destination location. + ** + ** P.S. It might help if you check the right cell rather than using + ** the value that just happened to be in checkcell. + */ + + checkcell = Adjacent_Cell(oldcell, newdir); + + int checkx = Cell_X(checkcell); + int checky = Cell_Y(checkcell); + int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); + if (checkval && !online) { + forcefail = ((checkval ^ oldval) < 0); + } else { + forcefail = false; + } + /* + ** The only exception to the above is when we are directly backtracking + ** because we could be trying to escape from a culdesack! + */ + if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { + forcefail = false; + } + } + + #else + newdir = Next_Direction(newdir, search*2); + #endif + + /* + ** If we have just checked the same heading we started with, + ** we are surrounded by impassable characters and we exit. + */ + if (newdir == olddir) { + return(false); + } + + /* + ** Get the new cell. + */ + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** If we found a passable position, this is where we should move. + */ + if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { + break; + } else { + if (newcell == target) { + forceout = true; + break; + } + } + + } + + /* + ** Record the direction. + */ + if (!forceout) { + /* + ** Mark the cell because this is where we need to be. If register + ** cell fails then the list has been shortened and we need to adjust + ** the new direction. + */ + if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { + /* + ** The only reason we could not register a cell is that we are in + ** a looping situation. So we need to try and unravel the loop if + ** we can. + */ + if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { + return(false); + } + /* + ** Since we need to eliminate a diagonal we must pretend the upon + ** attaining this square, we were moving turned further in the + ** search direction then we really were. + */ + newdir = Next_Direction(newdir, (FacingType)(search*2)); + } + /* + ** Find out which side of the line this cell is on. If it is on + ** a side, then store off that side. + */ + int newx = Cell_X(newcell); + int newy = Cell_Y(newcell); + int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); + if (val) { + oldval = val; + online = false; + } else { + online = true; + } + cellcount++; + if (cellcount == MAX_PATH_EDGE_FOLLOW) { + return(false); + } + } + + /* + ** If we have found the target spot, we are done. + */ + if (newcell == target) { + path->Command[path->Length] = END; + return(true); + } + + /* + ** If we make a full circle back to our original spot, get out. + */ + if (newcell == firstcell && newdir == firstdir) { + return(false); + } + + if (firstcell == -1) { + firstcell = newcell; + firstdir = newdir; + } + + /* + ** Because we moved, our facing is now incorrect. We want to face toward + ** the impassable edge we are following (well, not actually toward, but + ** a little past so that we can turn corners). We have to turn 45/90 degrees + ** more than expected in anticipation of the pending 45/90 degree turn at + ** the start of this loop. + */ + #ifdef DIAGONAL + olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); + #else + olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); + #endif + oldcell = newcell; + } + + /* + ** The maximum search path is exhausted... abort with a failure. + */ + return(false); +} + + +/*********************************************************************************************** + * Optimize_Moves -- Optimize the move list. * + * * + * INPUT: char *moves to optimize * + * * + * OUTPUT: none (list is optimized) * + * * + * WARNINGS: EMPTY moves are used to hold the place of eliminated * + * commands. Also, NEVER call this routine with a list that * + * contains illegal commands. The list MUST be terminated * + * with a EOL command * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized and commented. * + *=============================================================================================*/ +#define EMPTY (FacingType)-2 +int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) +//int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) +{ + /* + ** Facing command pair adjustment table. Compare the facing difference between + ** the two commands. 0 means no optimization is possible. 3 means backtracking + ** so eliminate both commands. Any other value adjusts the first command facing. + */ +#ifdef DIAGONAL + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. +#else + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; +#endif + FacingType * cmd1, // Floating first command pointer. + * cmd2, // Floating second command pointer. + newcmd; // Calculated new optimized command. + FacingType newdir; // Tentative new direction for smoothing. + CELL cell; // Working cell (as it moves along path). + + /* + ** Abort if there is any illegal parameter. + */ + if (!path || !path->Command) return(0); + + /* + ** Optimization loop -- start scanning with the + ** first pair of commands (if there are at least two + ** in the command list). + */ + path->Command[path->Length] = END; // Force end of list. + + if (path->Length == 0) return(0); + + cell = path->Start; + if (path->Length > 1) { + cmd2 = path->Command + 1; + while (*cmd2 != END) { + + /* + ** Set the cmd1 pointer to point to the valid command closest, but + ** previous to cmd2. Be sure not to go previous to the head of the + ** command list. + */ + cmd1 = cmd2-1; + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + + /* + ** If there isn't any valid previous command, then bump the + ** cmd pointers to the next command pair and continue... + */ + if (*cmd1 == EMPTY) { + cmd2++; + continue; + } + + /* + ** Fetch precalculated command change value. 0 means leave + ** command set alone, 3 means backtrack and eliminate two + ** commands. Any other value is new direction and eliminate + ** one command. + */ + newcmd = (FacingType)(*cmd2 - *cmd1); + if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); + newcmd = _trans[newcmd]; + + /* + ** Check for backtracking. If this occurs, then eliminate the + ** two commands. This is the easiest optimization. + */ + if (newcmd == FACING_SE) { + *cmd1 = EMPTY; + *cmd2++ = EMPTY; + continue; + } + + /* + ** If an optimization code was found the process it. The command is a facing + ** offset to more directly travel toward the immediate destination cell. + */ + if (newcmd) { + + /* + ** Optimizations differ when dealing with diagonals. Especially when dealing + ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can + ** only be optimized if the intervening cell is passable. The distance travelled + ** is the same, but the path is less circuitous. + */ + if (*cmd1 & FACING_NE) { + + /* + ** Diagonal optimizations are always only 45 + ** degree adjustments. + */ + newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); + + /* + ** Diagonal 90 degree changes can be smoothed, although + ** the path isn't any shorter. + */ + if (ABS((int)newcmd) == 1) { + if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { + *cmd2 = newdir; + *cmd1 = newdir; + } + // BOB 16.12.92 + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + continue; + } + } else { + newdir = Next_Direction(*cmd1, newcmd); + } + + /* + ** Allow shortening turn only on right angle moves that are based on + ** 90 degrees. Always allow 135 degree optimizations. + */ + *cmd2 = newdir; + *cmd1 = EMPTY; + + /* + ** Backup what it thinks is the current cell. + */ + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + if (*cmd1 != EMPTY) { + cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); + } else { + cell = path->Start; + } + continue; + } + + /* + ** Since we could not make an optimization, we move our + ** head pointer forward. + */ + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + } + } + + /* + ** Pack the command list to remove any EMPTY command entries. + */ + cmd1 = path->Command; + cmd2 = path->Command; + cell = path->Start; + path->Cost = 0; + path->Length = 0; + while (*cmd2 != END) { + if (*cmd2 != EMPTY) { + cell = Adjacent_Cell(cell, *cmd2); + path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); + path->Length++; + *cmd1++ = *cmd2; + } + cmd2++; + } + path->Length++; + *cmd1 = END; + return(path->Length); +} + + +CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) +{ + FacingType dir; + CELL next; + int lp; + + dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; + + /* + ** Loop through the different acceptable distances. + */ + for (int dist = start; dist < max; dist ++) { + + /* + ** Move to the starting location. + */ + next = dst; + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir); + } + + if (dir & 1) { + /* + ** If our direction is diagonal than we need to check + ** only one side which is as long as both of the old sides + ** together. + */ + for (lp = 0; lp < dist << 1; lp ++) { + next = Adjacent_Cell(next, dir + 3); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } else { + /* + ** If our direction is not diagonal than we need to check two + ** sides so that we are checking a corner like location. + */ + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 2); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 4); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } + } + return(-1); +} + + + + +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +{ + MoveType move = Can_Enter_Cell(cell, face); + + if (move < MOVE_MOVING_BLOCK && Distance(Cell_Coord(cell)) > 0x0100) threshhold = MOVE_MOVING_BLOCK; + + if (move > threshhold) return(0); + + if (Session.Type == GAME_NORMAL) { + if (threat != -1) { + if (::Distance(Cell_Coord(cell), Cell_Coord(DestLocation)) > (THREAT_THRESHOLD * CELL_LEPTON_W)) { +// if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + } + + static int _value[MOVE_COUNT] = { + 1, // MOVE_OK + 1, // MOVE_CLOAK + 3, // MOVE_MOVING_BLOCK + 8, // MOVE_DESTROYABLE + 10, // MOVE_TEMP + 0 // MOVE_NO + }; + return(_value[move]); +} + diff --git a/CODE/FIXED.CPP b/CODE/FIXED.CPP new file mode 100644 index 0000000..5bf3679 --- /dev/null +++ b/CODE/FIXED.CPP @@ -0,0 +1,248 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FIXED.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FIXED.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/20/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "fixed.h" +#include +#include +#include +#include + + +/* +** These are some handy fixed point constants. Using these constants instead of manually +** constructing them is not only faster, but more readable. +*/ +const fixed fixed::_1_2(1, 2); // 1/2 +const fixed fixed::_1_3(1, 3); // 1/3 +const fixed fixed::_1_4(1, 4); // 1/4 +const fixed fixed::_3_4(3, 4); // 3/4 +const fixed fixed::_2_3(2, 3); // 2/3 + + +fixed::fixed(int numerator, int denominator) +{ + if (denominator == 0) { + Data.Raw = 0; + } else { + Data.Raw = (unsigned short)((unsigned)(numerator * 256) / (unsigned)denominator); + } +} + + +/*********************************************************************************************** + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * * + * This will parse the ASCII initialization string into a fixed point number. * + * The source string can be a conventional fixed point representation (e.g., "1.0", ".25") * + * or a percent value (e.g. "100%", "25%", "150%"). For percent values, the trailing "%" * + * is required. * + * * + * INPUT: ascii -- Pointer to the ascii source to translate into a fixed point number. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to specify an ASCII string that has more precision and * + * magnitude than can be represented by the fixed point number. In such a case, * + * the resulting value is undefined. * + * * + * HISTORY: * + * 06/20/1996 JLB : Created. * + *=============================================================================================*/ +fixed::fixed(char const * ascii) +{ + /* + ** If there is no valid pointer, then default to zero value. This takes care of any + ** compiler confusion that would call this routine when the programmer wanted the + ** integer parameter constructor to be called. + */ + if (ascii == NULL) { + Data.Raw = 0; + return; + } + + /* + ** The whole part (if any) always starts with the first legal characters. + */ + char const * wholepart = ascii; + + /* + ** Skip any leading white space. + */ + while (isspace(*ascii)) { + ascii++; + } + + /* + ** Determine if the number is expressed as a percentage. Detect this by + ** seeing if there is a trailing "%" character. + */ + char const * tptr = ascii; + while (isdigit(*tptr)) { + tptr++; + } + + /* + ** Percentage value is specified as a whole number but is presumed to be + ** divided by 100 to get mathematical fixed point percentage value. + */ + if (*tptr == '\%') { + Data.Raw = (unsigned short)((atoi(ascii) * 256) / 100); + } else { + + Data.Composite.Whole = Data.Composite.Fraction = 0; + if (wholepart && *wholepart != '.') { + Data.Composite.Whole = (unsigned char)atoi(wholepart); + } + + char * fracpart = strchr(ascii, '.'); + if (fracpart) fracpart++; + if (fracpart) { + int frac = atoi(fracpart); + + int len = 0; + int base = 1; + char const * fptr = fracpart; + while (isdigit(*fptr)) { + fptr++; + len++; + base *= 10; + } + + Data.Composite.Fraction = (unsigned char)((256 * frac) / base); + } + } +} + + +/*********************************************************************************************** + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * * + * Use this routine to convert this fixed point number into an ASCII null terminated * + * string. This is the counterpart to the fixed point constructor that takes an ASCII * + * string. * + * * + * INPUT: buffer -- Pointer to the buffer to hold the fixed point ASCII string. * + * * + * maxlen -- The length of the buffer. * + * * + * OUTPUT: Returns with the number of characters placed in the buffer. The trailing null is * + * not counted in this total. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int fixed::To_ASCII(char * buffer, int maxlen) const +{ + if (buffer == NULL) return(0); + + /* + ** Determine the whole and fractional parts of the number. The fractional + ** part number is the value in 1000ths. + */ + int whole = Data.Composite.Whole; + int frac = ((int)Data.Composite.Fraction * 1000) / 256; + char tbuffer[32]; + + /* + ** If there number consists only of a whole part, then the number is simply + ** printed into the buffer. If there is a fractional part, then there + ** will be a decimal place followed by up to three digits of accuracy for the + ** fractional component. + */ + if (frac == 0) { + sprintf(tbuffer, "%d", whole); + } else { + sprintf(tbuffer, "%d.%02d", whole, frac); + + char * ptr = &tbuffer[strlen(tbuffer)-1]; + while (*ptr == '0') { + *ptr = '\0'; + ptr--; + } + } + + /* + ** If no maximum length to the output buffer was specified, then presume the + ** output buffer is just long enough to store the number and the trailing + ** zero. + */ + if (maxlen == -1) { + maxlen = strlen(tbuffer)+1; + } + + /* + ** Fill the output buffer with the ASCII number. + */ + strncpy(buffer, tbuffer, maxlen); + + /* + ** Return with the number of ASCII characters placed into the output buffer. + */ + int len = strlen(tbuffer); + if (len < maxlen-1) return(len); + return(maxlen-1); +} + + +/*********************************************************************************************** + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * * + * This number will be converted into an ASCII string (using a static buffer) and the * + * string pointer will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the ASCII representation of this fixed point number. * + * * + * WARNINGS: As with all static return pointers, the pointer is valid only until such time * + * as this routine is called again. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +char const * fixed::As_ASCII(void) const +{ + static char buffer[32]; + + To_ASCII(buffer, sizeof(buffer)); + return(buffer); +} diff --git a/CODE/FIXED.H b/CODE/FIXED.H new file mode 100644 index 0000000..0e68daf --- /dev/null +++ b/CODE/FIXED.H @@ -0,0 +1,232 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FIXED.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FIXED.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/19/96 * + * * + * Last Update : June 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef FIXED_H +#define FIXED_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +//#pragma warning 604 9 +//#pragma warning 595 9 + +/* +** This is a very simple fixed point class that functions like a regular integral type. However +** it is under certain restrictions. The whole part must not exceed 255. The fractional part is +** limited to an accuracy of 1/256. It cannot represent or properly handle negative values. It +** really isn't all that fast (if an FPU is guaranteed to be present than using "float" might be +** more efficient). It doesn't detect overflow or underflow in mathematical or bit-shift operations. +** +** Take careful note that the normal mathematical operators return integers and not fixed point +** values if either of the components is an integer. This is the normal C auto-upcasting rule +** as it would apply presuming that integers are considered to be of higher precision than +** fixed point numbers. This allows the result of these operators to generate values with greater +** magnitude than is normally possible if the result were coerced into a fixed point number. +** If the result should be fixed point, then ensure that both parameters are fixed point. +** +** Note that although integers are used as the parameters in the mathematical operators, this +** does not imply that negative parameters are supported. The use of integers is as a convenience +** to the programmer -- constant integers are presumed signed. If unsigned parameters were +** specified, then the compiler would have ambiguous conversion situation in the case of constant +** integers (e.g. 1, 10, 32, etc). This is most important for the constructor when dealing with the +** "0" parameter case. In that situation the compiler might interpret the "0" as a null pointer rather +** than an unsigned integer. There should be no adverse consequences of using signed integer parameters +** since the precision/magnitude of these integers far exceeds the fixed point component counterparts. +** +** Note that when integer values are returns from the arithmetic operators, the value is rounded +** to the nearest whole integer value. This differs from normal integer math that always rounds down. +*/ +class fixed +{ + public: + // The default constructor must not touch the data members in any way. + fixed(void) {} + + // Convenient constructor if numerator and denominator components are known. + fixed(int numerator, int denominator); + + // Conversion constructor to get fixed point from integer. + fixed(int value) {Data.Composite.Fraction = 0;Data.Composite.Whole = (unsigned char)value;} + + // Constructor if ASCII image of number is known. + fixed(char const * ascii); + + // Convert to integer when implicitly required. + operator unsigned (void) const {return(((unsigned)Data.Raw+(256/2)) / 256);} + + /* + ** The standard operators as they apply to in-place operation. + */ + fixed & operator *= (fixed const & rvalue) {Data.Raw = (unsigned short)(((int)Data.Raw * rvalue.Data.Raw) / 256);return(*this);} + fixed & operator *= (int rvalue) {Data.Raw = (unsigned short)(Data.Raw * rvalue);return(*this);} + fixed & operator /= (fixed const & rvalue) {if (rvalue.Data.Raw != 0 && rvalue.Data.Raw != 256) Data.Raw = (unsigned short)(((int)Data.Raw * 256) / rvalue);return(*this);} + fixed & operator /= (int rvalue) {if (rvalue) Data.Raw = (unsigned short)((unsigned)Data.Raw / rvalue);return(*this);} + fixed & operator += (fixed const & rvalue) {Data.Raw += rvalue.Data.Raw;return(*this);} + fixed & operator -= (fixed const & rvalue) {Data.Raw -= rvalue.Data.Raw;return(*this);} + + /* + ** The standard "My Dear Aunt Sally" operators. The integer versions of multiply + ** and divide are more efficient than using the fixed point counterparts. + */ +// const fixed operator * (fixed const & rvalue) const {return(fixed(*this) *= rvalue);} + const fixed operator * (fixed const & rvalue) const {fixed temp = *this;temp.Data.Raw = (unsigned short)(((int)temp.Data.Raw * (int)rvalue.Data.Raw) / 256);return(temp);} + const int operator * (int rvalue) const {return ((((unsigned)Data.Raw * rvalue) + (256/2)) / 256);} +// const fixed operator / (fixed const & rvalue) const {return(fixed(*this) /= rvalue);} + const fixed operator / (fixed const & rvalue) const {fixed temp = *this;if (rvalue.Data.Raw != 0 && rvalue.Data.Raw != 256) temp.Data.Raw = (unsigned short)(((int)temp.Data.Raw * 256) / rvalue.Data.Raw);return(temp);} + const int operator / (int rvalue) const {if (rvalue) return(((unsigned)Data.Raw+(256/2)) / ((unsigned)rvalue*256));return(*this);} +// const fixed operator + (fixed const & rvalue) const {return(fixed(*this) += rvalue);} + const fixed operator + (fixed const & rvalue) const {fixed temp = *this;temp += rvalue;return(temp);} + const int operator + (int rvalue) const {return((((unsigned)Data.Raw+(256/2))/256) + rvalue);} +// const fixed operator - (fixed const & rvalue) const {return(fixed(*this) -= rvalue);} + const fixed operator - (fixed const & rvalue) const {fixed temp = *this;temp -= rvalue;return(temp);} + const int operator - (int rvalue) const {return((((unsigned)Data.Raw+(256/2))/256) - rvalue);} + + /* + ** The Shift operators are more efficient than using multiplies or divides by power-of-2 numbers. + */ + fixed & operator >>= (unsigned rvalue) {Data.Raw >>= rvalue;return(*this);} + fixed & operator <<= (unsigned rvalue) {Data.Raw <<= rvalue;return(*this);} + const fixed operator >> (unsigned rvalue) const {fixed temp = *this;temp >>= rvalue;return(temp);} + const fixed operator << (unsigned rvalue) const {fixed temp = *this;temp <<= rvalue;return(temp);} + + /* + ** The full set of comparison operators. + */ + bool operator == (fixed const & rvalue) const {return(Data.Raw == rvalue.Data.Raw);} + bool operator != (fixed const & rvalue) const {return(Data.Raw != rvalue.Data.Raw);} + bool operator < (fixed const & rvalue) const {return(Data.Raw < rvalue.Data.Raw);} + bool operator > (fixed const & rvalue) const {return(Data.Raw > rvalue.Data.Raw);} + bool operator <= (fixed const & rvalue) const {return(Data.Raw <= rvalue.Data.Raw);} + bool operator >= (fixed const & rvalue) const {return(Data.Raw >= rvalue.Data.Raw);} + bool operator ! (void) const {return(Data.Raw == 0);} + + /* + ** Comparison to integers requires consideration of fractional component. + */ + bool operator < (int rvalue) const {return(Data.Raw < (rvalue*256));} + bool operator > (int rvalue) const {return(Data.Raw > (rvalue*256));} + bool operator <= (int rvalue) const {return(Data.Raw <= (rvalue*256));} + bool operator >= (int rvalue) const {return(Data.Raw >= (rvalue*256));} + bool operator == (int rvalue) const {return(Data.Raw == (rvalue*256));} + bool operator != (int rvalue) const {return(Data.Raw != (rvalue*256));} + + /* + ** Friend functions to handle the alternate positioning of fixed and integer parameters. + */ + friend const int operator * (int lvalue, fixed const & rvalue) {return(rvalue * lvalue);} + friend const int operator / (int lvalue, fixed const & rvalue) {if (rvalue.Data.Raw == 0 || rvalue.Data.Raw == 256) return (lvalue); return(((unsigned)(lvalue * 256)+(256/2)) / rvalue.Data.Raw);} + friend const int operator + (int lvalue, fixed const & rvalue) {return(rvalue + lvalue);} + friend const int operator - (int lvalue, fixed const & rvalue) {return((((lvalue*256) - rvalue.Data.Raw) + (256/2)) / 256);} + friend bool operator < (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) < rvalue.Data.Raw);} + friend bool operator > (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) > rvalue.Data.Raw);} + friend bool operator <= (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) <= rvalue.Data.Raw);} + friend bool operator >= (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) >= rvalue.Data.Raw);} + friend bool operator == (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) == rvalue.Data.Raw);} + friend bool operator != (unsigned lvalue, fixed const & rvalue) {return((lvalue*256) != rvalue.Data.Raw);} + friend int operator *= (int & lvalue, fixed const & rvalue) {lvalue = lvalue * rvalue;return(lvalue);} + friend int operator /= (int & lvalue, fixed const & rvalue) {lvalue = lvalue / rvalue;return(lvalue);} + friend int operator += (int & lvalue, fixed const & rvalue) {lvalue = lvalue + rvalue;return(lvalue);} + friend int operator -= (int & lvalue, fixed const & rvalue) {lvalue = lvalue - rvalue;return(lvalue);} + + /* + ** Helper functions to handle simple and common operations on fixed point numbers. + */ + void Round_Up(void) {Data.Raw += (unsigned short)(256-1);Data.Composite.Fraction = 0;} + void Round_Down(void) {Data.Composite.Fraction = 0;} + void Round(void) {if (Data.Composite.Fraction >= 256/2) Round_Up();Round_Down();} + void Saturate(unsigned capvalue) {if (Data.Raw > (capvalue*256)) Data.Raw = (unsigned short)(capvalue*256);} + void Saturate(fixed const & capvalue) {if (*this > capvalue) *this = capvalue;} + void Sub_Saturate(unsigned capvalue) {if (Data.Raw >= (capvalue*256)) Data.Raw = (unsigned short)((capvalue*256)-1);} + void Sub_Saturate(fixed const & capvalue) {if (*this >= capvalue) Data.Raw = (unsigned short)(capvalue.Data.Raw-1);} + void Inverse(void) {*this = fixed(1) / *this;} + + /* + ** Friend helper functions that work in the typical C fashion of passing the object to + ** be processed as a parameter to the function. + */ + friend const fixed Round_Up(fixed const & value) {fixed temp = value; temp.Round_Up();return(temp);} + friend const fixed Round_Down(fixed const & value) {fixed temp = value; temp.Round_Down();return(temp);} + friend const fixed Round(fixed const & value) {fixed temp = value; temp.Round();return(temp);} + friend const fixed Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Inverse(fixed const & value) {fixed temp = value;temp.Inverse();return(temp);} + + /* + ** Conversion of the fixed point number into an ASCII string. + */ + int To_ASCII(char * buffer, int maxlen=-1) const; + char const * As_ASCII(void) const; + + /* + ** Helper constants that provide some convenient fixed point values. + */ + static const fixed _1_2; + static const fixed _1_3; + static const fixed _1_4; + static const fixed _3_4; + static const fixed _2_3; + + private: + union { + struct { +#ifdef BIG_ENDIAN + unsigned char Whole; + unsigned char Fraction; +#else + unsigned char Fraction; + unsigned char Whole; +#endif + } Composite; + unsigned short Raw; + } Data; +}; + + +#endif diff --git a/CODE/FLASHER.CPP b/CODE/FLASHER.CPP new file mode 100644 index 0000000..374826f --- /dev/null +++ b/CODE/FLASHER.CPP @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FLASHER.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FLASHER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * * + * This utility function will output the current status of the FlasherClass to the mono * + * screen. It is through this display that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void FlasherClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(50, 7); + mono->Printf("%2d", FlashCount); +} +#endif + + +/*********************************************************************************************** + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * * + * The ability for an object to flash is controlled by this logic processing routine. It * + * should be called once per game tick per unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the associated object be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/20/1994 JLB : Is now independent of object it represents. * + *=============================================================================================*/ +bool FlasherClass::Process(void) +{ + if (FlashCount) { + FlashCount--; + IsBlushing = false; + + if (FlashCount & 0x01) { + IsBlushing = true; + } + return(true); + } + return(false); +} + + diff --git a/CODE/FLASHER.H b/CODE/FLASHER.H new file mode 100644 index 0000000..d2daffa --- /dev/null +++ b/CODE/FLASHER.H @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FLASHER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FLASHER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : May 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLASHER_H +#define FLASHER_H + +class FlasherClass { + public: + /* + ** When this object is targeted, it will flash a number of times. This is the + ** flash control number. It counts down to zero and then stops. Odd values + ** cause the object to be rendered in a lighter color. + */ + unsigned FlashCount:7; + + /* + ** When an object is targeted, it flashes several times to give visual feedback + ** to the player. Every other game "frame", this flag is true until the flashing + ** is determined to be completed. + */ + unsigned IsBlushing:1; + + FlasherClass(void) {FlashCount = 0; IsBlushing = false;}; + FlasherClass(NoInitClass const & ) {}; + ~FlasherClass(void) {}; + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + bool Process(void); +}; + +#endif diff --git a/CODE/FLY.CPP b/CODE/FLY.CPP new file mode 100644 index 0000000..b685ea4 --- /dev/null +++ b/CODE/FLY.CPP @@ -0,0 +1,134 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FLY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FLY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : June 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * FlyClass::Physics -- Performs vector physics (movement). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FlyClass::Physics -- Performs vector physics (movement). * + * * + * This routine performs movement (vector) physics. It takes the * + * specified location and moves it according to the facing and speed * + * of the vector. It returns the status of the move. * + * * + * INPUT: coord -- Reference to the coordinate that the vector will * + * be applied to. * + * * + * OUTPUT: Returns with the status of the vector physics. This could * + * range from no effect, to exiting the edge of the world. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + * 06/05/1995 JLB : Simplified to just do movement. * + *=============================================================================================*/ +ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing) +{ + if (SpeedAdd != MPH_IMMOBILE) { + int actual = (int)SpeedAdd + SpeedAccum; + div_t result = div(actual, PIXEL_LEPTON_W); + SpeedAccum = result.rem; + actual -= result.rem; + COORDINATE old = coord; + + /* + ** If movement occurred that is at least one + ** pixel, then check update the coordinate and + ** check for edge of world collision. + */ + if (result.quot) { + COORDINATE newcoord; // New working coordinate. + newcoord = Coord_Move(coord, facing, actual); + /* + ** If no movement occurred, then presume it hasn't moved at all + ** and return immediately with this indication. + */ + if (newcoord == coord) { + return(IMPACT_NONE); + } + + /* + ** Remember the new position. + */ + coord = newcoord; + + /* + ** If the new coordinate is off the edge of the world, then report + ** this. + */ + if (newcoord & HIGH_COORD_MASK /*|| !Map.In_Radar(Coord_Cell(newcoord))*/) { +// if (!Map.In_Radar(Coord_Cell(newcoord))) { + coord = old; + return(IMPACT_EDGE); + } + + return(IMPACT_NORMAL); + } + } + return(IMPACT_NONE); +} + + +/*********************************************************************************************** + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * * + * This sets the speed of the projectile. It basically functions like a throttle value * + * where 0 equals stop and 255 equals maximum speed (whatever that is for the particular * + * object). * + * * + * INPUT: speed -- Speed setting from 0 to 255. * + * * + * maximum -- The maximum speed of the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + * 07/26/1994 JLB : Added maximum speed as guiding value. * + *=============================================================================================*/ +void FlyClass::Fly_Speed(int speed, MPHType maximum) +{ + SpeedAdd = (MPHType)( maximum * fixed(speed, 256)); +// SpeedAdd = (MPHType)Fixed_To_Cardinal((int)maximum, speed); +} + + diff --git a/CODE/FLY.H b/CODE/FLY.H new file mode 100644 index 0000000..28573cd --- /dev/null +++ b/CODE/FLY.H @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FLY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FLY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLY_H +#define FLY_H + +typedef enum ImpactType { + IMPACT_NONE, // No movement (of significance) occurred. + IMPACT_NORMAL, // Some (non eventful) movement occurred. + IMPACT_EDGE // The edge of the world was reached. +} ImpactType; + + +/**************************************************************************** +** Flying objects are handled by this class definition. +*/ +class FlyClass { + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FlyClass(void) : SpeedAccum(0), SpeedAdd(MPH_IMMOBILE) {}; + FlyClass(NoInitClass const &) {}; + ~FlyClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Fly_Speed(int speed, MPHType maximum); + ImpactType Physics(COORDINATE &coord, DirType facing); + MPHType Get_Speed(void) const {return(SpeedAdd);}; + + private: + /* + ** Object movement consists of incrementing the accumulator until enough "distance" + ** has accumulated so that moving the object becomes reasonable. + */ + unsigned SpeedAccum; // Lepton accumulator. + MPHType SpeedAdd; // Lepton add (per frame). +}; + +#endif diff --git a/CODE/FOOT.CPP b/CODE/FOOT.CPP new file mode 100644 index 0000000..a412d92 --- /dev/null +++ b/CODE/FOOT.CPP @@ -0,0 +1,2549 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FOOT.CPP 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FOOT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FootClass::AI -- Handle general movement AI. * + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * FootClass::Body_Facing -- Set the body rotation/facing. * + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * FootClass::Death_Announcement -- Announces the death of a unit. * + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * FootClass::Detach -- Detaches a target from tracking systems. * + * FootClass::Detach_All -- Removes this object from the game system. * + * FootClass::Enters_Building -- When unit enters a building for some reason. * + * FootClass::FootClass -- Normal constructor for the foot class object. * + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority* + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * FootClass::Mark -- Unit interface to map rendering system. * + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * FootClass::Mission_Capture -- Handles the capture mission. * + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * FootClass::Override_Mission -- temporarily overrides a units mission * + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * FootClass::Restore_Mission -- Restores an overridden mission * + * FootClass::Sell_Back -- Causes this object to be sold back. * + * FootClass::Set_Speed -- Initiate unit movement physics. * + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * FootClass::Take_Damage -- Handles taking damage to this object. * + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FootClass::FootClass -- Default constructor for foot class objects. * + * * + * This is the default constructor for FootClass objects. It sets the foot class values to * + * their default starting settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/23/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(RTTIType rtti, int id, HousesType house) : + TechnoClass(rtti, id, house), + IsScanLimited(false), + IsInitiated(false), + IsNewNavCom(false), + IsPlanningToLook(false), + IsDeploying(false), + IsFiring(false), + IsRotating(false), + IsDriving(false), + IsUnloading(false), + IsFormationMove(false), + IsNavQueueLoop(false), + IsScattering(false), + Speed(0), + SpeedBias(1), + XFormOffset(0x80000000), + YFormOffset(0x80000000), + NavCom(TARGET_NONE), + SuspendedNavCom(TARGET_NONE), + Team(0), + Group(255), + Member(0), + PathThreshhold(MOVE_CLOAK), + PathDelay(0), + TryTryAgain(PATH_RETRY), + BaseAttackTimer(0), + FormationSpeed(SPEED_FOOT), + FormationMaxSpeed(MPH_IMMOBILE), + HeadToCoord(0) +{ + Path[0] = FACING_NONE; + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * * + * This routine is used to output the current status of the foot class to the mono * + * monitor. Through this display bugs may be tracked down or eliminated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 07/04/1995 JLB : Handles aircraft special case. * + *=============================================================================================*/ +void FootClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(53, 13, 12, 1, IsInitiated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsPlanningToLook ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 14, 12, 1, IsDeploying ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 15, 12, 1, IsFiring ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 16, 12, 1, IsRotating ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 17, 12, 1, IsDriving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 18, 12, 1, IsUnloading ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsFormationMove ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(45, 1);mono->Printf("%02X", Speed); + if (NavCom) { + mono->Set_Cursor(29, 5); + mono->Printf("%08X", NavCom); + } + if (SuspendedNavCom) { + mono->Set_Cursor(38, 5); + mono->Printf("%08X", SuspendedNavCom); + } + + if (Team) Team->Debug_Dump(mono); + if (Group != 255) { + mono->Set_Cursor(59, 1);mono->Printf("%d", Group); + } + + static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; + for (int index = 0; index < min(12, ARRAY_SIZE(Path)); index++) { + mono->Set_Cursor(54+index, 3); + mono->Printf("%s", _p2c[((ABS((int)Path[index]+1)) % ARRAY_SIZE(_p2c))]); + } + mono->Set_Cursor(54, 5);mono->Printf("%2d", PathThreshhold); + mono->Set_Cursor(72, 3);mono->Printf("%4d", (long)PathDelay); + mono->Set_Cursor(67, 3);mono->Printf("%3d", TryTryAgain); + if (HeadToCoord) { + mono->Set_Cursor(60, 5);mono->Printf("%08X", HeadToCoord); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * FootClass::Set_Speed -- Initiate unit movement physics. * + * * + * This routine is used to set a unit's velocity control structure. * + * The game will then process the unit's movement during the momentum * + * physics calculation. * + * * + * INPUT: unit -- Pointer to the unit to alter. * + * * + * speed -- Throttle setting (0=stop, 255=full throttle). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 09/24/1993 JLB : Revised for faster speed. * + * 04/02/1994 JLB : Revised for new system. * + * 04/15/1994 JLB : Converted to member function. * + * 07/21/1994 JLB : Simplified. * + *=============================================================================================*/ +void FootClass::Set_Speed(int speed) +{ + assert(IsActive); + + speed &= 0xFF; + ((unsigned char &)Speed) = speed; +} + + +/*********************************************************************************************** + * FootClass::Mark -- Unit interface to map rendering system. * + * * + * This routine is the interface function for units as they relate to * + * the map rendering system. Whenever a unit's imagery changes, this * + * function is called. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Unit is removed. * + * MARK_CHANGE -- Unit alters image but doesn't move. * + * MARK_DOWN -- Unit is overlaid onto existing icons. * + * * + * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * + * down when it is already down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 12/23/1994 JLB : Performs low level check before processing. * + *=============================================================================================*/ +bool FootClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { +// short list[32]; + CELL cell = Coord_Cell(Coord); + +#ifndef PARTIAL + if (In_Which_Layer() != LAYER_GROUND && (mark == MARK_UP || mark == MARK_DOWN)) mark = MARK_CHANGE; +#endif + + /* + ** Inform the map of the refresh, occupation, and overlap + ** request. + */ + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List()); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * * + * This is a common routine used by both infantry and other ground travelling units. It * + * will fill in the unit's basic path to the NavCom destination. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * + * be found or the terrain prohibits the unit's movement. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Basic_Path(void) +{ + assert(IsActive); + + PathType * path; // Pointer to path control structure. + CELL cell; + int skip_path = false; + + Path[0] = FACING_NONE; + + if (Target_Legal(NavCom)) { + cell = As_Cell(NavCom); + + /* + ** When the navigation computer is set to a location that is impassible, then + ** find a nearby cell that can be entered and try to head toward that instead. + ** EXCEPT when that cell is very close -- then just bail. + */ + int dist = Distance(NavCom); + int checkdist = Team.Is_Valid() ? Rule.StrayDistance : Rule.CloseEnoughDistance; + if (Can_Enter_Cell(cell) > MOVE_CLOAK && dist > checkdist) { + CELL cell2 = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + if (cell2 != 0 && ::Distance(Cell_Coord(cell), Cell_Coord(cell2)) < dist) cell = cell2; + } + + if (What_Am_I() == RTTI_INFANTRY) { + CELL mycell = Coord_Cell(Center_Coord()); + ObjectClass * obj = Map[mycell].Cell_Occupier(); + while (obj) { + if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)obj; + if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { + if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { + Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); + } else { + Mem_Copy(inf->Path, Path, sizeof(Path)); + } + if (Path[0] != FACING_NONE) { + skip_path = true; + } + break; + } + } + obj = obj->Next; + } + } + + if (!skip_path) { + Mark(MARK_UP); + Path[0] = FACING_NONE; // Probably not necessary, but... + + /* + ** Try to find a path to the destination. If a failure occurs, then keep trying + ** with greater determination until either a complete failure occurs, or a decent + ** path was found. + */ + bool found1=false; // Found a best path yet? + PathType path1; + FacingType workpath1[200]; // Staging area for path list. +// FacingType workpath2[200]; // Staging area for path list. + MoveType maxtype = MOVE_TEMP; + if (!House->IsHuman) { + maxtype = MOVE_TEMP; +// maxtype = MOVE_DESTROYABLE; + } else { + + /* + ** For simple movement missions by the human player, then don't + ** consider friendly units as passable if close to the destination. + ** This will prevent a human controlled unit from just sitting next + ** to a destination just because there is another friendly unit + ** occupying the destination location. + */ + if (Mission == MISSION_MOVE && Distance(NavCom) < Rule.CloseEnoughDistance) { + maxtype = MOVE_DESTROYABLE; + } + } + + /* + ** Try to find a path to the destination. If there is a path + ** failure, then try a more severe path method until the + ** maximum severity is reached. + */ + for (;;) { + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), PathThreshhold); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + break; + } + + /* + ** A valid path was not found. Try the next greater path severity + ** level if the severity can be increased. If not, then consider this + ** a total failure. + */ + PathThreshhold++; + if (PathThreshhold > maxtype) break; + } + +#ifdef NEVER + /* + ** Determine if ANY path could be calculated by first examining the most + ** aggressive case. If this fails, then no path will succeed. Further + ** scanning is unnecessary. + */ + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + + /* + ** Scan for the best path possible. If this succeeds, then do a simple + ** comparison with the most aggressive path. If they are very close, then + ** go with the best (easiest) path method. + */ + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } else { + + /* + ** The easiest path method didn't result in a satisfactory path. Scan through + ** the rest of the path options, looking for the best one. + */ + for (MoveType move = (MoveType)(MOVE_CLOAK+1); move < (MoveType)(maxtype-1); move++) { +// for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype-1; move++) { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } +#endif + + /* + ** If a good path was found, then record it in the object's path + ** list. + */ + if (found1) { + Fixup_Path(&path1); + memcpy(&Path[0], &workpath1[0], min(path->Length, (int)sizeof(Path))); + } + + Mark(MARK_DOWN); + } + + PathDelay = Rule.PathDelay * TICKS_PER_MINUTE; + if (Path[0] != FACING_NONE) return(true); + + /* + ** If a basic path couldn't be determined, then abort the navigation process. + */ + Stop_Driver(); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * * + * This simple AI script handles moving the vehicle to its desired destination. Since * + * simple movement is handled directly by the engine, this routine merely waits until * + * the unit has reached its destination, and then causes the unit to enter idle mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 10/02/1996 JLB : Player controlled or human owned units don't scan for targets. * + *=============================================================================================*/ +int FootClass::Mission_Move(void) +{ + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + return(1); + } +// if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman) { + if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman && (!Team.Is_Valid() || !Team->Class->IsSuicide)) { + Target_Something_Nearby(THREAT_RANGE); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Capture -- Handles the capture mission. * + * * + * Capture missions are nearly the same as normal movement missions. The only difference * + * is that the final destination is handled in a special way so that it is not marked as * + * impassable. This allows the object (usually infantry) the ability to walk onto the * + * object and thus capture it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Capture(void) +{ + assert(IsActive); + + /* + ** If there is a valid TarCom but the NavCom isn't set, then set the NavCom accordingly. + */ + if (Is_Target_Building(TarCom) && !Target_Legal(NavCom) && What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber) { + Assign_Destination(TarCom); + } + + if (!Target_Legal(NavCom) /*&& !In_Radio_Contact()*/) { + Enter_Idle_Mode(); + if (Map[Center_Coord()].Cell_Building()) { + Scatter(0, true); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * * + * This AI routine handles heading to within range of the target and then firing upon * + * it until it is destroyed. If the target is destroyed, then the unit will change * + * missions to match its "idle mode" of operation (usually guarding). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Attack(void) +{ + assert(IsActive); + if (Target_Legal(TarCom)) { + Approach_Target(); + } else { + Enter_Idle_Mode(); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard -- Handles the AI for guarding in place. * + * * + * Units that are performing stationary guard duty use this AI process. They will sit * + * still and target any enemies that get within range. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Guard(void) +{ + assert(IsActive); + + if (!Target_Something_Nearby(THREAT_RANGE)) { + Random_Animate(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_VESSEL) { + switch (((VesselClass *)this)->Class->Type) { + case VESSEL_DD: + case VESSEL_PT: + dtime = MissionControl[Mission].AA_Delay(); + break; + + case VESSEL_CA: + dtime *= 2; + break; + + default: + break; + } + } + if (What_Am_I() == RTTI_INFANTRY) { + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + } + + switch (((InfantryClass *)this)->Class->Type) { + case INFANTRY_E1: + case INFANTRY_E3: + dtime = MissionControl[Mission].AA_Delay(); + break; + + default: + break; + } + } + + return((Arm != 0) ? (int)Arm : (dtime+Random_Pick(0, 2))); +} + + +/*********************************************************************************************** + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * * + * This routine is the default hunt order for game objects. It handles searching for a * + * nearby object and heading toward it. The act of targeting will cause it to attack * + * the target it selects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the game tick delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Hunt(void) +{ + assert(IsActive); + if (!Target_Something_Nearby(THREAT_NORMAL)) { +#if(0) +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this == INFANTRY_GENERAL && House->Class->House==HOUSE_UKRAINE && Scen.Scenario==47) { + for(int index=0; index < Buildings.Count(); index++) { + if(Buildings.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Buildings.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Units.Count(); index++) { + if(Units.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Units.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Infantry.Count(); index++) { + if(Infantry.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Infantry.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Aircraft.Count(); index++) { + if(Aircraft.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Aircraft.Ptr(index)->As_Target()); + break; + } + } + } +#endif +#endif + Random_Animate(); + } else { + if (What_Am_I() == RTTI_INFANTRY && ( ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_RENOVATOR || ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_THIEF) ) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + } else { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber && Is_Target_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + } else { + Approach_Target(); + } + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * * + * This is the counterpart routine to the Start_Driver function. It clears the driving * + * status flags and destination coordinate record. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Greatly simplified. * + *=============================================================================================*/ +bool FootClass::Stop_Driver(void) +{ + assert(IsActive); + + if (HeadToCoord) { + HeadToCoord = NULL; + Set_Speed(0); + IsDriving = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * * + * Before a unit can move it must be started by this routine. This routine handles * + * reserving the cell and setting the driving flag. * + * * + * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * + * away from the unit's current location. * + * * + * OUTPUT: bool; Was driving initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Uses simple spot index finder. * + *=============================================================================================*/ +bool FootClass::Start_Driver(COORDINATE &headto) +{ + assert(IsActive); + + Stop_Driver(); + if (headto) { + HeadToCoord = headto; + IsDriving = true; + + /* + ** Check for crate goodie finder here. + */ + if (Map[headto].Goodie_Check(this)) { + return(true); + } + if (!IsActive) return(false); + + HeadToCoord = NULL; + IsDriving = false; + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * * + * This routine will determine the sort coordinate for foot class object. This coordinate * + * is usually the coordinate of the object. The exception is if the object is tethered. * + * In this case (presumes offloading to the north), the sorting coordinate is adjusted * + * so that the object will be drawn on top of the transport unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * + *=============================================================================================*/ +COORDINATE FootClass::Sort_Y(void) const +{ + assert(IsActive); + + if (IsUnloading) { + return(Coord_Add(Coord, 0x01000000L)); + } + if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { + return(Coord_Add(Coord, 0x01000000L)); + } + return(Coord_Add(Coord, 0x00300000L)); +} + + +/*********************************************************************************************** + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * * + * This routine clears the units' navigation computer in preparation for removal from the * + * game. This is probably called as a result of unit destruction in combat. Clearing the * + * navigation computer ensures that the normal AI process won't start it moving again while * + * the object is undergoing any death animations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Stun(void) +{ + assert(IsActive); + + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + Stop_Driver(); + TechnoClass::Stun(); +} + + +/*********************************************************************************************** + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * * + * This routine will set the navigation computer to approach the target indicated by the * + * targeting computer. It is through this function that the unit nears the target so * + * that weapon firing may occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/13/1994 JLB : Made part of TechnoClass. * + * 12/22/1994 JLB : Enhanced search algorithm. * + * 05/20/1995 JLB : Always approaches if the object is off the map. * + *=============================================================================================*/ +void FootClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + /* + ** If the target is too far away then head toward it. + */ + int maxrange = Weapon_Range(primary); +// int maxrange = max(Weapon_Range(0), Weapon_Range(1)); + + if (!Target_Legal(NavCom) && (!In_Range(TarCom, primary) || !IsLocked)) { +// if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { + + /* + ** If the object that we are attacking is a building adjust the unit's + ** max range so that people can stand far away from the buildings and + ** hit them. + */ + BuildingClass * obj = As_Building(TarCom); + if (obj) { + maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + } + + /* + ** Adjust the max range of an infantry unit for where he is standing + ** in the room. + */ + maxrange -= 0x00B7; +#ifdef OBSOLETE + if (What_Am_I() == RTTI_INFANTRY) { + maxrange -= 0x0111; + } else { + maxrange -= 0x00B7; + } +#endif + maxrange = max(maxrange, 0); + + COORDINATE tcoord = ::As_Coord(TarCom); + COORDINATE trycoord = 0; + CELL tcell = Coord_Cell(tcoord); + CELL trycell = tcell; + DirType dir = Direction256(tcoord, Center_Coord()); + bool found = false; + + /* + ** Sweep through the cells between the target and the unit, looking for + ** a cell that the unit can enter but which is also within weapon range + ** of the target. If after a reasonable search, no appropriate cell could + ** be found, then the target will be assigned as the movement destination + ** and let "the chips fall where they may." + */ + for (int range = maxrange; range > 0x0080; range -= 0x0100) { + static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; + + for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { + trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); + + if (::Distance(trycoord, tcoord) < range) { + trycell = Coord_Cell(trycoord); + if (Map.In_Radar(trycell) && Map[trycell].Is_Clear_To_Move(Techno_Type_Class()->Speed, false, false, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)) { +// if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { + found = true; + break; + } + } + } + if (found) break; + } + + /* + ** If a suitable intermediate location was found, then head toward it. + ** Otherwise, head toward the enemy unit directly. + */ + if (found) { + Assign_Destination(::As_Target(trycell)); + } else { + + trycell = Map.Nearby_Location(trycell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + Assign_Destination(::As_Target(trycell)); +// Assign_Destination(TarCom); + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * * + * This mission routine causes the unit to scan for targets out to twice its weapon range * + * from the home point. If a target was found, then it will be attacked. The unit will * + * chase the target until it gets up to to its weapon range from the home position. * + * In that case, it will return to home position and start scanning for another target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with time delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 07/27/1995 JLB : Greatly simplified. * + *=============================================================================================*/ +int FootClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { + Assign_Mission(MISSION_HARVEST); + return(1+Random_Pick(1, 10)); + } + + /* + ** Ensure that the archive target is valid. + */ + if (!Target_Legal(ArchiveTarget)) { + ArchiveTarget = ::As_Target(Coord); + } + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && What_Am_I() == RTTI_INFANTRY && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + /* + ** Make sure that the unit has not strayed too far from the home position. + ** If it has, then race back to it. + */ + int maxrange = Threat_Range(1)/2; + + if (!IsFiring && !Target_Legal(NavCom) && Distance(ArchiveTarget) > maxrange) { + Assign_Target(TARGET_NONE); + Assign_Destination(ArchiveTarget); + } + + if (!Target_Legal(TarCom)) { + COORDINATE old = Coord; + Coord = As_Coord(ArchiveTarget); + Target_Something_Nearby(THREAT_AREA); + Coord = old; + if (Target_Legal(TarCom)) { + return(1); + } + Random_Animate(); + } else { + Approach_Target(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_AIRCRAFT) { + dtime *= 2; + } + return(dtime + Random_Pick(1, 5)); +} + + +/*********************************************************************************************** + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * * + * This routine will make sure that the home position for the foot class object gets * + * reset. This is necessary since the home position may change depending on the unit's * + * transition between limbo and non-limbo condition. * + * * + * INPUT: coord -- The coordinate to unlimbo the unit at. * + * * + * dir -- The initial direction to give the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + /* + ** Try to unlimbo the unit. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Mobile units are always revealed to the house that owns them. + */ + Revealed(House); + + /* + ** Start in a still (non-moving) state. + */ + Path[0] = FACING_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Take_Damage -- Handles taking damage to this object. * + * * + * This routine intercepts the damage assigned to this object and if this object is * + * a member of a team, it informs the team that the damage has occurred. The team may * + * change it's priority or action based on this event. * + * * + * INPUT: damage -- The damage points inflicted on the unit. * + * * + * distance -- The distance from the point of damage to the unit itself. * + * * + * warhead -- The type of damage that is inflicted. * + * * + * source -- The perpetrator of the damage. By knowing who caused the damage, * + * the team know's who to "get even with". * + * * + * OUTPUT: Returns with the result type of the damage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(IsActive); + + ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + + if (result != RESULT_NONE && Team) { + + Team->Took_Damage(this, result, source); + + } else { + + if (result != RESULT_DESTROYED && result != RESULT_NONE) { + + /* + ** Determine if the target that is currently being attacked has a weapon that can + ** do harm to a ground based unit. This information is needed so that an appropriate + ** response will occur when damage is taken. + */ +// bool tweap = false; +// if (As_Techno(TarCom)) { +// tweap = (As_Techno(TarCom)->Techno_Type_Class()->PrimaryWeapon != NULL); +// } + + /* + ** This ensures that if a unit is in sticky mode, then it will snap out of + ** it when it takes damage. + */ + if (source != NULL && MissionControl[Mission].IsNoThreat && !MissionControl[Mission].IsZombie) { + Enter_Idle_Mode(); + } + + /* + ** If this object is not part of a team and it can retaliate for the damage, then have + ** it try to do so. This prevents it from just sitting there and taking damage. + */ + if (Is_Allowed_To_Retaliate(source)) { + + int primary = What_Weapon_Should_I_Use(source->As_Target()); + if (In_Range(source, primary) || !House->IsHuman) { + Assign_Target(source->As_Target()); + } + + if (Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + /* + ** Simple retaliation cannot occur because the source of the damage + ** is too far away. If scatter logic is enabled, then scatter now. + */ + if (!Target_Legal(TarCom) && !Target_Legal(NavCom) && Rule.IsScatter) { + Scatter(0, true); + } + + } else { + + /* + ** If this object isn't doing anything important, then scatter. + */ + if (MissionControl[Mission].IsScatter && !IsTethered && !IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && What_Am_I() != RTTI_AIRCRAFT && What_Am_I() != RTTI_VESSEL) { + if (!House->IsHuman || Rule.IsScatter) { + Scatter(0, true); + } + } + } + } + } + return(result); +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * * + * At this level, the object is known to have the ability to attack or move to the * + * target specified (in theory). Perform the attack or move as indicated. * + * * + * INPUT: target -- The target clicked upon that will precipitate action. * + * * + * OUTPUT: Returns with the type of action performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(IsActive); + assert(object != NULL); + + switch (action) { + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + if (What_Am_I() == RTTI_INFANTRY && + ((InfantryClass *)this)->Class->IsBomber && + object->What_Am_I() == RTTI_BUILDING && + !House->Is_Ally(object)) { + + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } else { + Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); + } + } + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD); + break; + + case ACTION_ATTACK: + if (Can_Player_Fire()) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + break; + + case ACTION_ENTER: + if (Can_Player_Move() && object && object->Is_Techno() /*&& !((RadioClass *)object)->In_Radio_Contact()*/) { + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_SABOTAGE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NOMOVE: + case ACTION_MOVE: + if (Can_Player_Move()) { + + TARGET targ = object->As_Target(); + + /* + ** If the destination object is not the same zone, then pick a nearby location. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT && Techno_Type_Class()->Speed != SPEED_WINGED && Map[Coord].Zones[Techno_Type_Class()->MZone] != Map[object->Center_Coord()].Zones[Techno_Type_Class()->MZone]) { + +#ifdef FIXIT_MINE_PASSABLE + // Fixes units not driving onto mines. + if (Can_Enter_Cell(Coord_Cell(object->Center_Coord())) > MOVE_OK) { + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); + } +#else + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, targ); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * * + * This routine performs the action requested when the left mouse button was clicked over * + * a cell. Typically, this is just a move command. * + * * + * INPUT: action -- The predetermined action that should occur. * + * * + * cell -- The cell number that the action should occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(IsActive); + + action = What_Action(cell); + switch (action) { + case ACTION_HARVEST: + Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); + break; + + case ACTION_MOVE: + if (AllowVoice) { + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + // Fall into next case. + + case ACTION_NOMOVE: + if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].IsVisible) { + + /* + ** Find the closest same-zoned cell to where the unit currently is. + ** This will allow the unit to come as close to the destination cell + ** as is reasonably possible, when clicking on an impassable cell + ** (as is likely when clicking in the shroud.) It looks for the + ** nearest cell using an expanding-radius box, and ignores cells + ** off the edge of the map. + */ + CellClass const * cellptr = &Map[::As_Cell(::As_Target(Center_Coord()))]; + if (What_Am_I() != RTTI_AIRCRAFT) { + + if (Can_Enter_Cell(Coord_Cell(Center_Coord())) == MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + } else { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } +#ifdef OBSOLETE + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + + /* + ** Engineer attempting to capture bridge to repair it + */ + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_SABOTAGE: + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, ::As_Target(cell) ); + break; + } +} + + +/*********************************************************************************************** + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * * + * This routine is called as this object moves from cell to cell. When the center of the * + * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * + * the path to the distance to the target. This forces path recalculation in an effort to * + * avoid units passing each other. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/08/1995 JLB : Handles generic enter trigger event. * + * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * + *=============================================================================================*/ +void FootClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + + IsScattering = false; + + /* + ** Clear any unloading flag if necessary. + */ + IsUnloading = false; + + /* + ** If adjacent to an enemy techno that has the ability to reveal a sub, + ** then shimmer the cloaked object. + */ + if (Cloak == CLOAKED) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); + + if (Map.In_Radar(cell)) { + TechnoClass const * techno = Map[cell].Cell_Techno(); + + if (techno && !techno->House->Is_Ally(this) && techno->Techno_Type_Class()->IsScanner) { + Do_Shimmer(); + break; + } + } + } + } + + /* + ** Shorten the path if the target is now within weapon range of this + ** unit and this unit is on an attack type mission. + */ + if (Target_Legal(TarCom) && (What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)this)->Class->IsDog)) { + int primary = What_Weapon_Should_I_Use(TarCom); + bool inrange = In_Range(TarCom, primary); + TechnoClass const * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Foot()) { + inrange = In_Range(((FootClass const *)techno)->Likely_Coord(), primary); + } + + if ((Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + } + } + + /* + ** Trigger event associated with the player entering the cell. + */ + if (Cloak != CLOAKED) { + TriggerClass * trigger = Map[Coord].Trigger; + if (trigger != NULL) { + trigger->Spring(TEVENT_PLAYER_ENTERED, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + + /* + ** Check for horizontal trigger crossing. + */ + int x = Cell_X(Coord_Cell(Coord)); + int y = Cell_Y(Coord_Cell(Coord)); + for (int index = 0; index < Map.MapCellWidth; index++) { + trigger = Map[XY_Cell(index+Map.MapCellX, y)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_HORIZONTAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_HORIZONTAL)) { + trigger->Spring(TEVENT_CROSS_HORIZONTAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for vertical trigger crossing. + */ + for (index = 0; index < Map.MapCellHeight; index++) { + trigger = Map[XY_Cell(x, index+Map.MapCellY)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_VERTICAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_VERTICAL)) { + trigger->Spring(TEVENT_CROSS_VERTICAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for zone entry trigger events. + */ + for (MapTriggerID = 0; MapTriggerID < MapTriggers.Count(); MapTriggerID++) { + trigger = MapTriggers[MapTriggerID]; + if (trigger->Class->Event1.Event == TEVENT_ENTERS_ZONE || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_ENTERS_ZONE)) { + if (Map[trigger->Cell].Zones[Techno_Type_Class()->MZone] == Map[Coord].Zones[Techno_Type_Class()->MZone]) { + trigger->Spring(TEVENT_ENTERS_ZONE, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** If any of these triggers cause this unit to be destroyed, then + ** stop all further processing for this unit. + */ + if (!IsActive) return; + } + +#ifdef OBSOLETE + /* + ** Flag any gap generators to re-draw + */ + for (int index = 0; index IsInLimbo && (HouseClass *)obj->House != PlayerPtr) { + int dist = Distance(obj) / CELL_LEPTON_W; + if (dist < (6 + Rule.GapShroudRadius) ) { + // if (dist < (6 + obj->Class->SightRange) ) { + obj->IsJamming = false; // lie so it'll re-jam now + } + } + } +#endif + + } + + TechnoClass::Per_Cell_Process(why); +} + + +/*************************************************************************** + * FootClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedNavCom = NavCom; + TechnoClass::Override_Mission(mission, tarcom, navcom); + + Assign_Destination(navcom); +} + + +/*************************************************************************** + * FootClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Restore_Mission(void) +{ + assert(IsActive); + + if (TechnoClass::Restore_Mission()) { + Assign_Destination(SuspendedNavCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * * + * This routine handles radio message that are related to movement. These are used for * + * complex coordinated maneuvers. * + * * + * INPUT: from -- Pointer to the originator of this radio message. * + * * + * message -- The radio message that is being received. * + * * + * param -- The optional parameter (could be a movement destination). * + * * + * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * + * response is RADIO_ROGER. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Answers if this object is located on top of a service depot. + */ + case RADIO_ON_DEPOT: + if (Map[Center_Coord()].Cell_Building() != NULL) { + BuildingClass const * building = Map[Center_Coord()].Cell_Building(); + if (*building == STRUCT_REPAIR) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** Intercept the repair request and if this object is moving, then no repair + ** is possible. + */ + case RADIO_REPAIR: + if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); + break; + + /* + ** Something bad has happened to the object in contact with. Abort any coordinated + ** activity with this object. Basically, ... run away! Run away! + */ + case RADIO_RUN_AWAY: + if (In_Radio_Contact()) { + if (NavCom == Contact_With_Whom()->As_Target()) { + Assign_Destination(TARGET_NONE); + } + } + if (Mission == MISSION_SLEEP) { + Assign_Mission(MISSION_GUARD); + Commence(); + } + if (Mission == MISSION_ENTER) { + Assign_Mission(MISSION_GUARD); + } + if (!IsRotating && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } + break; + + /* + ** Checks to see if this unit needs to move somewhere. If it is already in motion, + ** then it doesn't need further movement instructions. + */ + case RADIO_NEED_TO_MOVE: + param = (long)NavCom; + if (!Target_Legal(NavCom)) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Radio request to move to location specified. Typically this is used + ** for complex loading and unloading missions. + */ + case RADIO_MOVE_HERE: + if (NavCom != (TARGET)param) { + if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { + return(RADIO_YEA_NOW_WHAT); + } else { + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + Assign_Destination((TARGET)param); + Shorten_Mission_Timer(); + } + } + return(RADIO_ROGER); + + /* + ** Requests if this unit is trying to cooperatively load up. Typically, this occurs + ** for passengers and when vehicles need to be repaired. + */ + case RADIO_TRYING_TO_LOAD: + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + } + break; + } + return(TechnoClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * * + * This mission handler will cooperatively coordinate the object to maneuver into the * + * object it is in radio contact with. This is used by infantry when they wish to load * + * into an APC as well as by vehicles when they wish to enter a repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 JLB : Created. * + * 09/22/1995 JLB : Modified to handle the "on hold" condition. * + *=============================================================================================*/ +int FootClass::Mission_Enter(void) +{ + assert(IsActive); + + /* + ** Find out who to coordinate with. If in radio contact, then this the transporter is + ** defined. If not in radio contact, then try the archive target value to see if that + ** is suitable. + */ + TechnoClass * contact = Contact_With_Whom(); + if (contact == NULL) { + contact = As_Techno(ArchiveTarget); + } + + /* + ** If in contact, then let the transporter handle the movement coordination. + */ + if (contact != NULL) { + + /* + ** If the transport says to "bug off", then abort the enter mission. The transport may + ** likely say all is 'ok' with the "RADIO ROGER", then try again later. + */ + if (Transmit_Message(RADIO_DOCKING, contact) != RADIO_ROGER && !IsTethered) { + Transmit_Message(RADIO_OVER_OUT); + Enter_Idle_Mode(); + } + + } else { + + /* + ** Since there is no potential object to enter, then abort this + ** mission with some default standby mission. + */ + Enter_Idle_Mode(); + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * * + * This routine will assign the specified target to the navigation computer. No legality * + * checks are performed. * + * * + * INPUT: target -- The target value to assign to the navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + NavCom = target; + + /* + ** Presume that the easiest path is tried first. As the findpath proceeds, when + ** a failure occurs, this threshhold will be increased until path failure + ** cannot be prevent. At this point, all movement should cease. + */ + PathThreshhold = MOVE_CLOAK; +} + + +/*********************************************************************************************** + * FootClass::Detach_All -- Removes this object from the game system. * + * * + * This routine will remove this object from the game system. This routine is called when * + * this object is about to be deleted. All other objects should no longer reference this * + * object in that case. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach_All(bool all) +{ + assert(IsActive); + + if (Team && !ScenarioInit) { + Team->Remove(this); + Team = NULL; + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * * + * This routine is called when the house determines that it should attack the specified * + * target. This routine will determine if it can attack the target specified and if so, * + * the amount of power it can throw at it. This returned power value is used to allow * + * intelligent distribution of retaliation. * + * * + * INPUT: target -- The target that this object just might be assigned to attack and thus * + * how much power it can bring to bear should be returned. * + * * + * OUTPUT: Returns with the amount of power that this object can bring to bear against the * + * potential target specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Rescue_Mission(TARGET tarcom) +{ + assert(IsActive); + + /* + ** If the target specified is not legal, then it cannot be attacked. Always return + ** zero in this case. + */ + if (!Target_Legal(tarcom)) return(0); + + /* + ** If the unit is already assigned to destroy the tarcom then we need + ** to return a negative value which tells the computer to lower the + ** desired threat rating. + */ + if (TarCom == tarcom) { + return(-Risk()); + } + + /* + ** If the unit is currently attacking a target that has a weapon then we + ** cannot abandon it as it will destroy us if we return to base. + */ + if (Target_Legal(TarCom)) { + TechnoClass * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Weapon_Equipped()) { + return(0); + } + } + + /* + ** If the unit is in a harvest mission or is currently attacking + ** something, or is not very effective, then it will be of no help + ** at all. + */ + if (Team.Is_Valid() || Mission == MISSION_HARVEST || !Risk()) { + return(0); + } + + /* + ** Find the distance to the target modified by the range. If the + ** the distance is 0, then things are ok. + */ + int dist = Distance(tarcom) - Weapon_Range(0); + int threat = Risk() * 1024; + int speed = -1; + if (dist > 0) { + + /* + ** Next we need to figure out how fast the unit moves because this + ** decreases the distance penalty. + */ + speed = max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); + + int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; + + /* + ** Finally modify the threat by the distance the unit is away. + */ + threat = max(threat/ratio, 1); + } + return(threat); +} + + +/*********************************************************************************************** + * FootClass::Death_Announcement -- Announces the death of a unit. * + * * + * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * + * * + * INPUT: source -- The perpetrator of this death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Death_Announcement(TechnoClass const * ) const +{ + assert(IsActive); + + if (IsOwnedByPlayer) { + if (What_Am_I() == RTTI_VESSEL) { + Speak(VOX_SHIP_LOST); + } else { + Speak(VOX_UNIT_LOST); + } + } +} + + +/*********************************************************************************************** + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * * + * This routine will return with the greatest threat (best target) for this object. For * + * movable ground object, they won't automatically return ANY target if this object is * + * cloaked. Otherwise, cloaking is relatively useless. * + * * + * INPUT: method -- The request method (bit flags) to use when scanning for a target. * + * * + * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * + * TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 07/10/1996 JLB : Handles scan range limitation. * + *=============================================================================================*/ +TARGET FootClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + /* + ** If the scan is forced to be limited, then limit the scan now. + */ + if (IsScanLimited) { + method = method & ~THREAT_AREA; + method = method | THREAT_RANGE; + } + + /* + ** If this object can cloak, then it won't select a target automatically. + */ + if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { + return(TARGET_NONE); + } + + if (!(method & (THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_TIBERIUM|THREAT_BOATS|THREAT_CIVILIANS|THREAT_POWER|THREAT_FAKES|THREAT_FACTORIES|THREAT_BASE_DEFENSE))) { + if (What_Am_I() != RTTI_VESSEL) { + method = method | THREAT_GROUND; + } else { + method = method | THREAT_BOATS|THREAT_GROUND; + } + } + + /* + ** Perform the search for the target. + */ + TARGET target = TechnoClass::Greatest_Threat(method); + + /* + ** If no target could be located and this object is under scan range + ** restrictions, then this restriction must be lifted now. + */ + if (IsScanLimited && target == TARGET_NONE) { + IsScanLimited = false; + } + + /* + ** Return with final target found. + */ + return(target); +} + + +/*********************************************************************************************** + * FootClass::Detach -- Detaches a target from tracking systems. * + * * + * This routine will detach the specified target from the tracking systems of this object. * + * It will be removed from the navigation computer and any queued mission record. * + * * + * INPUT: target -- The target to be removed from this object. * + * * + * all -- Is the unit really about to be eliminated? If this is true then even * + * friendly contact (i.e., radio) must be eliminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 07/24/1996 JLB : Removes target from NavQueue list. * + *=============================================================================================*/ +void FootClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + + TechnoClass::Detach(target, all); + + if (!SpecialFlag) { + if (ArchiveTarget == target) { + ArchiveTarget = TARGET_NONE; + } + } + + if (SuspendedNavCom == target) { + SuspendedNavCom = TARGET_NONE; + SuspendedMission = MISSION_NONE; + } + + /* + ** If the navigation computer is assigned to the target, then the navigation + ** computer must be cleared. + */ + if (NavCom == target) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + Restore_Mission(); + } + + /* + ** Remove the target from the NavQueue list as well. + */ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == target) { + NavQueue[index] = TARGET_NONE; + if (index < ARRAY_SIZE(NavQueue)-1) { + memmove(&NavQueue[index], &NavQueue[index+1], ((ARRAY_SIZE(NavQueue)-index)-1) * sizeof(NavQueue[0])); + index--; + } + } + } + + /* + ** If targeting the specified object and this unit is obviously heading + ** toward the target to get within range, then abort the path. + */ + if (TarCom == target && House->IsHuman) { + Path[0] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * * + * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * + * from the object. This function is overridden for those objects that can contain * + * Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded from the object. * + * * + * WARNINGS: This routine must be called multiple times in order to completely offload the * + * Tiberium. When this routine return 0, all Tiberium has been offloaded. * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * * + * This routine examines the specified cell to see if the object can enter it. This * + * function is to be overridden for objects that could have the possibility of not being * + * allowed to enter the cell. Typical objects at the FootClass level always return * + * MOVE_OK. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The direction that this cell might be entered from. * + * * + * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * + * blockage. There are various other values that represent other blockage types. * + * The value returned will indicated the most severe reason why entry into the cell * + * is blocked. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const +{ + assert(IsActive); + + return MOVE_OK; +} + + +/*********************************************************************************************** + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * * + * This routine determines if it is legal to sell the object back. A foot class object can * + * only be sold back if it is sitting on a repair bay. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully sold back? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Can_Demolish(void) const +{ + assert(IsActive); + + switch (What_Am_I()) { + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + if (In_Radio_Contact() && + Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && + *((BuildingClass *)Contact_With_Whom()) == STRUCT_REPAIR && + Distance(Contact_With_Whom()) < 0x0080) { + + return(true); + } + break; + + default: + break; + } + return(TechnoClass::Can_Demolish()); +} + + +/*********************************************************************************************** + * FootClass::Sell_Back -- Causes this object to be sold back. * + * * + * When an object is sold back, a certain amount of money is refunded to the owner and then * + * the object is removed from the game system. * + * * + * INPUT: control -- The action to perform. The only supported action is "1", which means * + * to sell back. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Sell_Back(int control) +{ + assert(IsActive); + + if (control != 0) { + if (House == PlayerPtr) { + Speak(VOX_UNIT_SOLD); + Sound_Effect(VOC_CASHTURN); + } + House->Refund_Money(Refund_Amount()); + Stun(); + Limbo(); + delete this; + } +} + + +/*********************************************************************************************** + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * * + * This routine comes in handy when determining where a travelling object will be at * + * when considering the amount of time it would take for a normal unit to travel one cell. * + * Using this information, an intelligent "approach target" logic can be employed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate the object is at or soon will be. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE FootClass::Likely_Coord(void) const +{ + assert(IsActive); + + if (Head_To_Coord()) { + return(Head_To_Coord()); + } + return(Target_Coord()); +} + + +/*********************************************************************************************** + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * * + * This routine modify the specified cell if the unit is part of a formation. The * + * adjustment will take into consideration the formation relative offset from the * + * (presumed) center cell specified. * + * * + * INPUT: cell -- The cell to presume as the desired center point of the formation. * + * * + * OUTPUT: Returns with the cell that should be used as the actual destination. If this * + * object is part of a formation, then the cell location will be appropriately * + * adjusted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +CELL FootClass::Adjust_Dest(CELL cell) const +{ + assert(IsActive); + + if (IsFormationMove) { + int xdest = Cell_X(cell); + int ydest = Cell_Y(cell); + + int newx = Bound(XFormOffset + xdest, Map.MapCellX, Map.MapCellX + Map.MapCellWidth -1); + int newy = Bound(YFormOffset + ydest, Map.MapCellY, Map.MapCellY + Map.MapCellHeight -1); + + cell = XY_Cell(newx, newy); + } + return(cell); +} + + +/*********************************************************************************************** + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * * + * This routine will process the navigation queue. If the queue is present and valid and * + * there is currently no navigation target assigned to this object, then the first entry * + * of the queue will be assigned. The remaining entries will move down. If the queue is * + * to be processed as a circular list, then the first entry is appended to the end. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine might end up assigning a movement destination. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Handle_Navigation_List(void) +{ + /* + ** The navigation queue only needs to be processed if there is + ** currently no navigation target for this object. + */ + if (!Target_Legal(NavCom)) { + TARGET target = NavQueue[0]; + + /* + ** Check to see if the navigation queue even exists and + ** has at least one valid entry. If it does, then process it by + ** assigning the object's NavCom to the first entry on the list. + */ + if (Target_Legal(target)) { + Assign_Destination(target); + memmove(&NavQueue[0], &NavQueue[1], sizeof(NavQueue)-sizeof(NavQueue[0])); + NavQueue[ARRAY_SIZE(NavQueue)-1] = TARGET_NONE; + + /* + ** If the navigation queue is to loop (indefinately), then append the + ** target value from the first part to the end of the queue. + */ + if (IsNavQueueLoop) { + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == TARGET_NONE) { + NavQueue[index] = target; + break; + } + } + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * * + * This routine will append the destination target to the object's NavQueue list. After * + * doing so, if the object is not doing anything important, then it will be started on * + * that destination. This is functionally the same as Assign_Destination, but it stores * + * the target to the NavQueue first. * + * * + * INPUT: target -- The movement target destination to append the queue. * + * * + * OUTPUT: none * + * * + * WARNINGS: The queue is of finite size and any queue requests that would exceed that size * + * are ignored. If there are no queue entries pending and the unit is not * + * otherwise occupied, then the queue target might be carried directly into the * + * NavCom. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Queue_Navigation_List(TARGET target) +{ + if (Target_Legal(target)) { + int count; + for (count = 0; count < ARRAY_SIZE(NavQueue); count++) { + if (!Target_Legal(NavQueue[count])) break; + } + + /* + ** If the target is this object itself, then this indicates that the + ** queue list is to be processed as a loop. Otherwise, just tack the + ** navigation target to the end of the list. + */ + if (target == As_Target() && count > 0) { + IsNavQueueLoop = true; + } else { + if (count == 0) { + IsNavQueueLoop = false; + } + if (count < ARRAY_SIZE(NavQueue)) { + NavQueue[count] = target; + } + } + + /* + ** If this object isn't doing anything, then start acting on the + ** navigation queue now. + */ + if (!Target_Legal(NavCom) && Mission == MISSION_GUARD) { + Enter_Idle_Mode(); + } + } +} + + +/*********************************************************************************************** + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * * + * This routine will clear out any values in the navigation queue. This is the preferred * + * way of aborting a navigation queue for a unit. If the unit is already travelling, it * + * won't be interrupted by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This will clear the navigation list but not the navigation computer. Thus a * + * unit will still travel to its current immediate destination. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Clear_Navigation_List(void) +{ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * * + * This routine will determine if this object has permission to leave the map and thus * + * leave the game. Typical objects with this permission are transports used to drop of * + * reinforcements. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this object have permission to travel off the map edge and leave the * + * game? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Allowed_To_Leave_Map(void) const +{ + /* + ** If the unit hasn't entered the map yet, then don't allow leave the game. + */ + if (!IsLocked) return(false); + + /* + ** A unit that isn't marked as a loaner is a gift to the player. Such objects can never + ** leave the map unless they are part of a team that gives it special permision. + */ + if (!IsALoaner && Mission != MISSION_RETREAT && (!Team.Is_Valid() || !Team->Is_Leaving_Map())) return(false); + + return(true); +} + + +/*********************************************************************************************** + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * * + * This will examine this object to determine if it is suitable as a team recruit. Some * + * objects are disqualified if they are otherwise premptively occupied. * + * * + * INPUT: house -- Pointer to the house that is trying to recruit this object. * + * * + * OUTPUT: bool; Is this object suitable for recruitment by a team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Recruitable(HouseClass const * house) const +{ + /* + ** If not of the correct house presuasion, then recruitment is not allowed. + */ + if (house != NULL && house != House) { + return(false); + } + + /* + ** If the object is not a playing member of the game, then don't consider it available. + */ + if (IsInLimbo) { + return(false); + } + + /* + ** If it is already part of another team, then it is not available for + ** general recruitment. + */ + if (Team.Is_Valid()) { + return(false); + } + + /* + ** If it is currently in a mission the precludes recruitment into a team, then + ** return with this information. + */ + if (!Is_Recruitable_Mission(Mission)) { + return(false); + } + + /* + ** It was not disqualified for general team recruitment, so return that + ** it is available. + */ + return(true); +} + + +/*********************************************************************************************** + * FootClass::AI -- Handle general movement AI. * + * * + * This basically just sees if this object is within weapon range of the target and if * + * so, it will stop movement so that firing may commence. This prevents the occasional * + * case of an attacker driving right up to the defender before firing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::AI(void) +{ + TechnoClass::AI(); + +// FootClass::Per_Cell_Process does this function already. +#ifdef OBSOLETE + if (IsActive) { + if (!IsScattering && !IsTethered && !IsInLimbo && What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && In_Range(TarCom)) { + Assign_Destination(TARGET_NONE); + } + } +#endif +} + + +/*********************************************************************************************** + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority. * + * * + * Some objects are on an important mission that must succeed. If the object is on such * + * a mission, then it will be more aggressive in its movement action. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object on a priority mission? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_On_Priority_Mission(void) const +{ + if (Mission == MISSION_ENTER) return(true); + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * * + * This will try to make this mobile object leave the map. It does this by assigning a * + * movement destination that is located off the edge of the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Retreat(void) +{ + assert(IsActive); + + enum { + FIND_EDGE, + TRAVELLING + }; + + switch (Status) { + + /* + ** Find a suitable edge to travel to and then assign destination there. + */ + case FIND_EDGE: + if (Target_Legal(NavCom)) { + Status = TRAVELLING; + } else { + + CELL cell = 0; + + /* + ** If this is part of a team, then pick the edge where the team as likely + ** entered from. + */ + if (Team.Is_Valid() && Team->Class->Origin != -1) { + cell = Map.Calculated_Cell(House->Control.Edge, Team->Class->Origin, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + /* + ** If an edge hasn't been found, then try to find one that is not based on any + ** team information. + */ + if (cell == 0) { + cell = Map.Calculated_Cell(House->Control.Edge, -1, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + assert(cell == 0); // An edge cell must be found! + + Assign_Destination(::As_Target(cell)); + Status = TRAVELLING; + } + break; + + /* + ** While travelling, monitor that all is proceeding according to plan. + */ + case TRAVELLING: + if (!Target_Legal(NavCom)) { + Status = FIND_EDGE; + } + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + diff --git a/CODE/FOOT.H b/CODE/FOOT.H new file mode 100644 index 0000000..9a0cb4f --- /dev/null +++ b/CODE/FOOT.H @@ -0,0 +1,383 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FOOT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FOOT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FOOT_H +#define FOOT_H + +#include "target.h" +#include "type.h" +#include "techno.h" +#include "ftimer.h" + +class UnitClass; +class BuildingClass; + + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class FootClass : public TechnoClass +{ + public: + /* + ** This flag controls whether a range limiting effect should be in place. If + ** true, then target scanning will be limited to the range of the object + ** regardless of what was requested from the target scanning logic. This value + ** is used for ships so that they won't permanently stick on a an attack mission + ** for a target they can never get within range of. This value will toggle when + ** a path cannot be generated and the target is not within range. It will also + ** toggle when path limiting is true, but there is not target found within + ** the limited range. + */ + unsigned IsScanLimited:1; + + /* + ** If this unit has officially joined the team's group, then this flag is + ** true. A newly assigned unit to a team is not considered part of the + ** team until it actually reaches the location where the team is. By + ** using this flag, it allows a team to continue to intelligently attack + ** a target without falling back to regroup the moment a distant member + ** joins. + */ + unsigned IsInitiated:1; + + /* + ** When the player gives this object a navigation target AND that target + ** does not result in any movement of the unit, then a beep should be + ** sounded. This typically occurs when selecting an invalid location for + ** movement. This flag is cleared if any movement was able to be performed. + ** It never gets set for computer controlled units. + */ + unsigned IsNewNavCom:1; + + /* + ** There are certain cases where a unit should perform a full scan rather than + ** the more efficient "ring scan". This situation occurs when a unit first + ** appears on the map or when it finishes a multiple cell movement track. + */ + unsigned IsPlanningToLook:1; + + /* + ** Certain units have the ability to metamorphize into a building. When this + ** operation begins, certain processes must occur. During these operations, this + ** flag will be true. This ensures that any necessary special case code gets + ** properly executed for this unit. + */ + unsigned IsDeploying:1; + + /* + ** This flag tells the system that the unit is doing a firing animation. This is + ** critical to the firing logic. + */ + unsigned IsFiring:1; + + /* + ** This unit could be either rotating its body or rotating its turret. During the + ** process of rotation, this flag is set. By examining this flag, unnecessary logic + ** can be avoided. + */ + unsigned IsRotating:1; + + /* + ** If this object is current driving to a short range destination, this flag is + ** true. A short range destination is either the next cell or the end of the + ** current "curvy" track. An object that is driving is not allowed to do anything + ** else until it reaches its destination. The exception is when infantry wish to + ** head to a different destination, they are allowed to start immediately. + */ + unsigned IsDriving:1; + + /* + ** If this object is unloading from a hover transport, then this flag will be + ** set to true. This handles the unusual case of an object disembarking from the + ** hover lander yet not necessarily tethered but still located in an overlapping + ** position. This flag will be cleared automatically when the object moves to the + ** center of a cell. + */ + unsigned IsUnloading:1; + + /* + ** If this object is part of a formation, this bit will be set. The + ** formation only occurs when every member of a team is selected, and + ** only those members of the team are the ones selected. + */ + unsigned IsFormationMove:1; + + /* + ** If the navigation movement queue is to be looped rather than consumed, then + ** this flag will be true. By looping, the unit will travel through the locations + ** in the queue indefinately. + */ + unsigned IsNavQueueLoop:1; + + /* + ** If this object is scattering, then this flag will be true. While true, the + ** NavCom should not be arbitrarily changed. This flag will automatcially be + ** cleared when the object moves one cell. + */ + unsigned IsScattering:1; + + /* + ** This is the "throttle setting" of the unit. It is a fractional value with 0 = stop + ** and 255 = full speed. + */ + int Speed; + + /* + ** This is the override speed adjuster. Normally, this is a fixed point + ** value of 0x0100, but it can be modified by crate powerups. + */ + fixed SpeedBias; + + /* + ** For units in a formation, these values represent the distance from + ** the target destination where the unit should move to. For example, + ** in a horizontal line formation, XFormOffset would be set to some + ** value, and YFormOffset would be zero. + */ + int XFormOffset; + int YFormOffset; + + /* + ** + ** This is the desired destination of the unit. The unit will attempt to head + ** toward this target (avoiding intervening obstacles). + */ + TARGET NavCom; + TARGET SuspendedNavCom; + + /* + ** A sequence of move destinations can be given to a unit. The sequence is + ** stores as an array of movement targets. The list is terminated with a + ** TARGET_NONE. + */ + TARGET NavQueue[10]; + + /* + ** This points to the team that "owns" this object. This pointer is used to + ** quickly process the team when this object is the source of the change. An + ** example would be if this object were to be destroyed, it would inform the + ** team of this fact by using this pointer. + */ + CCPtr Team; + + /* + ** If this object is part of a pseudo-team that the player is managing, then + ** this will be set to the team number (0 - 9). If it is not part of any + ** pseudo-team, then the number will be -1. + */ + unsigned char Group; + + /* + ** This points to the next member in the team that this object is part of. This + ** is used to quickly process each team member when the team class is the source + ** of the change. An example would be if the team decided that everyone is going + ** to move to a new location, it would inform each of the objects by chaining + ** through this pointer. + */ + FootClass * Member; + + /* + ** Since all objects derived from this class move according to a path list. + ** This is the path list. It specifies, as a simple list of facings, the + ** path that the object should follow in order to reach its destination. + ** This path list is limited in size, so it might require several generations + ** of path lists before the ultimate destination is reached. The game logic + ** handles regenerating the path list as necessary. + */ + FacingType Path[CONQUER_PATH_MAX]; + + /* + ** This value keeps track of how serious the unit is in trying to reach the + ** destination specified. As blockages arise, this threshold will rise + ** unit it reaches the point of complete failure. When that event occurs, the + ** unit will realize that it cannot get to its specified destination and will + ** try to perform some other action instead. + */ + MoveType PathThreshhold; + + /* + ** When there is a complete findpath failure, this timer is initialized so + ** that a findpath won't be calculated until this timer expires. + */ + CDTimerClass PathDelay; + enum {PATH_RETRY=10}; + int TryTryAgain; // Number of retry attempts remaining. + + /* + ** If the object has recently attacked a base, then this timer will not + ** have expired yet. It is used so a building does not keep calling + ** for help from the same attacker. + */ + CDTimerClass BaseAttackTimer; + + /* + ** For formation moves, this will be the override speed. + */ + SpeedType FormationSpeed; + + /* + ** For formation moves, this will be the override maximum speed. + */ + MPHType FormationMaxSpeed; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FootClass(NoInitClass const & x) : TechnoClass(x), Team(x), PathDelay(x), BaseAttackTimer(x) {}; + FootClass(RTTIType rtti, int id, HousesType house); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Basic_Path(void); + + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Can_Demolish(void) const; + bool Is_Recruitable(HouseClass const * house=NULL) const; + bool Is_On_Priority_Mission(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Likely_Coord(void) const; + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + COORDINATE Head_To_Coord(void) const {return (HeadToCoord);}; + virtual bool Start_Driver(COORDINATE &headto); + virtual bool Stop_Driver(void); + virtual void Assign_Destination(TARGET target); + bool Is_Allowed_To_Leave_Map(void) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Mark(MarkType mark=MARK_CHANGE); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Stun(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Death_Announcement(TechnoClass const * source=0) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual void Sell_Back(int control); + virtual int Offload_Tiberium_Bail(void); + virtual TARGET Greatest_Threat(ThreatType method) const; + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual int Mission_Retreat(void); + virtual int Mission_Enter(void); + virtual int Mission_Move(void); + virtual int Mission_Capture(void); + virtual int Mission_Attack(void); + virtual int Mission_Guard(void); + virtual int Mission_Hunt(void); + virtual int Mission_Guard_Area(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + void Handle_Navigation_List(void); + void Queue_Navigation_List(TARGET target); + void Clear_Navigation_List(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Approach_Target(void); + virtual void Fixup_Path(PathType *) {}; + virtual void Set_Speed(int speed); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + int Optimize_Moves(PathType *path, MoveType threshhold); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + CELL Adjust_Dest(CELL cell) const; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + CELL Safety_Point(CELL src, CELL dst, int start, int max); + int Rescue_Mission(TARGET tarcom); + + private: + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + void Debug_Draw_Map(char const * txt, CELL start, CELL dest, bool pause); + void Debug_Draw_Path(PathType *path); + bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); + bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); + bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); + + /* + ** This is the coordinate that the unit is heading to + ** as an immediate destination. This coordinate is never further + ** than once cell (or track) from the unit's location. When this coordinate + ** is reached, then the next location in the path list becomes the + ** next HeadTo coordinate. + */ + COORDINATE HeadToCoord; +}; + +#endif + + diff --git a/CODE/FOREIGN.TXT b/CODE/FOREIGN.TXT new file mode 100644 index 0000000..9eff576 --- /dev/null +++ b/CODE/FOREIGN.TXT @@ -0,0 +1,42 @@ +For German version: + +call the batch file GERMAN.BAT. This will copy the appropriate conquer.h and +credits.txt files where they need to be, and set the proper network aliases +for drives S: and T:. It also deletes all the object modules, and ends by +briefing up DEFINES.H. You should comment out //#define ENGLISH and //#define +FRENCH, and un-comment #define GERMAN. Also, change the release flag to make +sure that #define RELEASE_VERSION is the only version being made, and make +sure that //#define DONGLE is commented out. + +Make sure that the german maps and such are all up to date, by copying the +maps to the directory GERMAPS, and then building the maps by calling +MAKEGER.BAT EXPAND2.MIX and MAKEGER GENERAL.MIX. If GENERAL changes, then +you also have to rebuild v:\projects\c&c0\cstrike2\cdger\cd4\main.mix. If +EXPAND2.MIX changes, you have to copy s:t\m\expand2.mix to the patch +directory, f:\projects\c&c0\setup\aftermth\patch\german\ra200. + +After building RA95.EXE and GAME.DAT, also copy them up to the patch +directory. + +The .MIX files that changed are MOVIES1, EXPAND2, SOUNDS, and GENERAL. There +may have been others, but that's probably about it. + +Note, German version has several different art files, primarily removing +blood from the infantry/civilians and removing some movies. Also different +is PIPS.ANM, with words for HOLD, READY, and PRIMARY. + + + +For French version: + +Follow all the above steps, but substitute FRENCH.BAT and MAKEFRE.BAT. The +final destination directory is CDFRE instead of CDGER, of course, and the +patch directory is FRENCH instead of GERMAN, again of course. + +If MAKEFRE locks up processing sound files, use MAKER instead +of MAKE. + +Note, French version uses most of the same art files. Different +is PIPS.ANM, with words for HOLD, READY, and PRIMARY. Also different are all +the hi-res version's sidebar icons. German has its own icons as well. + diff --git a/CODE/FTIMER.H b/CODE/FTIMER.H new file mode 100644 index 0000000..784e9b6 --- /dev/null +++ b/CODE/FTIMER.H @@ -0,0 +1,713 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FTIMER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FTIMER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/16/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * BasicTimerClass::operator () -- Function operator for timer object. * + * BasicTimerClass::operator long -- Conversion to long operator. * + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * TTimerClass::operator () -- Function operator for timer object. * + * TTimerClass::operator long -- Conversion operator for timer object. * + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * CDTimerClass::operator () -- Function operator for the count down timer. * + * CDTimerClass::operator long -- Conversion to long operator function. * + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * TTimerClass::Value -- Returns with the current value of the timer. * + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FTIMER_H +#define FTIMER_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +/* +** This is a timer class that watches a constant rate timer (specified by the parameter +** type class) and provides basic timer functionality. It is possible to set the start value +** WITHOUT damaging or otherwise affecting any other timer that may be built upon the same +** specified timer class object. Treat an object of this type as if it were a "magic" integral +** long that automatically advances at the speed of the timer class object controlling it. +*/ +// Let lint know that non-virtual destructor is OK for this class. +//lint -esym(1509,BasicTimerClass) +template +class BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + BasicTimerClass(unsigned long set=0); + BasicTimerClass(NoInitClass const & ); + + ~BasicTimerClass(void); + + // Fetch current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + protected: + T Timer; // Timer regulator (ticks at constant rate). + unsigned long Started; // Time started. +}; + + +template +inline BasicTimerClass::BasicTimerClass(NoInitClass const & ) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * * + * This is the constructor for the basic timer class object. It sets the timer counting * + * up from zero at the rate of the controlling timer class object. * + * * + * INPUT: set -- Alternate initial start value for the counter. If not specified, then * + * the timer is assumed to start at zero and count upwards. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +//lint -esym(1403,BasicTimerClass::Timer) +//lint -esym(1403,BasicTimerClass::Timer) +template +inline BasicTimerClass::BasicTimerClass(unsigned long set) : + Started(Timer()-set) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * * + * The destructor for the basic timer object doesn't have to do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::~BasicTimerClass(void) +{ +} + + +template +inline unsigned long BasicTimerClass::Value(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator long -- Conversion to long operator. * + * * + * This conversion operator allows the basic timer object to function in much the same * + * manner as the integral "long" type. One can assign a long with a timer object and the * + * actual value of the timer is extracted from the object and used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::operator unsigned long(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator allows the timer to also serve as the parameter type class for * + * additional timer objects. This allows one to instantiate a controlling timer class that * + * can control (e.g., turn on or off) all timers that are based upon it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long BasicTimerClass::operator () (void) const +{ + return(Timer()-Started); +} + + +/* +** This timer class functions similarly to the basic timer class. In addition to the +** normal timer operation, this class has the ability to be stopped and started at +** will. If you have no need to start or stop the timer, then use the basic timer +** class instead. +*/ +template +class TTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + TTimerClass(unsigned long set=0); + TTimerClass(NoInitClass const & x); + + ~TTimerClass(void) {}; + + // Fetches current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + private: + unsigned long Accumulated; // Total accumulated ticks. +}; + + +template +inline TTimerClass::TTimerClass(NoInitClass const & x) : + BasicTimerClass(x) +{ +} + + +/*********************************************************************************************** + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * * + * This is the constructor for the advanced timer class object. This object class can start * + * or stop the timer under user control. * + * * + * INPUT: set -- The initial value to set the timer to. If no value is specified, then * + * the timer is assumed to start from zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::TTimerClass(unsigned long set) : + BasicTimerClass(set), + Accumulated(0) +{ +} + + +/*********************************************************************************************** + * TTimerClass::Value -- Returns with the current value of the timer. * + * * + * This routine will return with the current value of the timer. It takes into account * + * whether the timer has stopped or not so as to always return the correct value regardless * + * of that condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current value of the timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::Value(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator long -- Conversion operator for timer object. * + * * + * This conversion operator allows this timer object to function as an "rvalue" of a "long" * + * type. This is consistent with the integral "long" value. It is possible to assign a * + * timer object to a long and have the long initialized with the current value of the * + * timer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::operator unsigned long(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator for the timer class allows this timer class to be used as the * + * template parameter for other timer class objects. With this ability, one can control * + * several timers (e.g., start or stop them) by using a single controlling timer class * + * that other timers are instantiated from. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::operator () (void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * * + * This routine will stop (pause) the timer from further increments. To cause the timer * + * to begin anew, call the Start() function. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + Accumulated += BasicTimerClass::operator unsigned long(); + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * * + * This routine will resume a timer that was previously stopped with the Stop() function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Start(void) +{ + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * * + * Since this timer can be paused, this routine is used to examine the timer to see if it * + * is currently paused or active. If the timer is active, then the return value will be * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this timer currently active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool TTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + + +/* +** This timer counts down from the specified (or constructed) value down towards zero. +** The countdown rate is controlled by the timer object specified. This timer object can +** be started or stopped. It can also be tested to see if it has expired or not. An expired +** count down timer is one that has value of zero. You can treat this class object as if it +** were an integral "magic" long that automatically counts down toward zero. +*/ +template +class CDTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + CDTimerClass(unsigned long set=0); + CDTimerClass(NoInitClass const & x); + + ~CDTimerClass(void); + + // Fetches current value of count down timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + private: + unsigned long DelayTime; // Ticks remaining before countdown timer expires. +}; + + +template +inline CDTimerClass::CDTimerClass(NoInitClass const & x) : + BasicTimerClass(x) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * * + * This is the constructor for the count down timer object. The optional starting value * + * can be used to initiate the timer. Because of this constructor it is possible to assign * + * a long to a count down timer object in order to begin the countdown process. * + * * + * INPUT: set -- The initial starting value for the countdown timer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::CDTimerClass(unsigned long set) : + BasicTimerClass(0), + DelayTime(set) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * * + * The destructor for the count down timer object does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::~CDTimerClass(void) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * * + * Use this routine to fetch the current value of the timer. It takes into consideration * + * whether the timer has been stopped or not. It returns the correct value regardless of * + * this condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the correct value of this count down timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::Value(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator long -- Conversion to long operator function. * + * * + * This conversion operator allows the count down timer object to be used as if it were * + * a "magic" long that automatically counted downward at the controller class tick rate. * + * The count down object can be used in any place that an rvalue long could be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::operator unsigned long(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator () -- Function operator for the count down timer. * + * * + * This is the function operator for the count down timer object. By supporting this * + * function operator, this class (or one derived from this class) could be used as the * + * controlling timer to the timer templates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::operator () (void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * * + * This routine is used to stop (pause) the count down timer object. A timer object paused * + * in this fashion will be resumed by a call to Start() or by assigning a new count down * + * value to the timer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + DelayTime = *this; + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * * + * This routine is used to start (resume) the count down timer that was previously stopped * + * with the Stop() function. The timer will also resume when a new timer value is assigned. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Start(void) +{ + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * * + * Because the timer object counting can be stopped, this routine is used to determine * + * if the timer is currently paused or active. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the timer currently active? * + * * + * WARNINGS: Note that if the timer has counted down to zero, then it may be active, but * + * the value will, naturally, not change. * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool CDTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + +#endif diff --git a/CODE/FUNCTION.H b/CODE/FUNCTION.H new file mode 100644 index 0000000..35eb922 --- /dev/null +++ b/CODE/FUNCTION.H @@ -0,0 +1,1031 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FUNCTION.H 2 3/13/97 2:05p Steve_tall $*/ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +/* +** !!!DEFINE!!! "NDEBUG" if the assertion code is to be !!!REMOVED!!! from the project. +*/ +#define NDEBUG + +#pragma warn -hid + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÁÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + ÚÄÁÄÄÄÄÄÄÄÄÄ¿ + ³ ³ + ³ VesselClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ VesselTypeClass + ³ ³ + AircraftTypeClass InfantryTypeClass +#endif + + +#include "watcom.h" +#include "lint.h" + + +#ifdef WIN32 +//#define WIN32_LEAN_AND_MEAN +#include +#define WWFILE_H +#define RAWFILE_H +#define MONOC_H + +#define _MAX_NAME _MAX_FNAME + +#endif + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +#define FILE_H +#define WWMEM_H + +#ifndef WIN32 +#define TIMER_H +#endif + +#ifdef WIN32 +#include "key.h" +#endif + +#include +#include "mpu.h" +#include "bench.h" +#include "rect.h" +#include "jshell.h" +#include "buff.h" +#include "face.h" +#include "random.h" +#include "crc.h" +#include "compat.h" +#include "fixed.h" +#include "base64.h" +#include "pipe.h" +#include "xpipe.h" +#include "ramfile.h" +#include "lcw.h" +#include "lzw.h" +#include "lcwpipe.h" +#include "lzwpipe.h" +#include "lzopipe.h" +#include "crcpipe.h" +#include "shapipe.h" +#include "b64pipe.h" +#include "straw.h" +#include "xstraw.h" +#include "b64straw.h" +#include "lcwstraw.h" +#include "lzwstraw.h" +#include "lzostraw.h" +#include "crcstraw.h" +#include "shastraw.h" +#include "rndstraw.h" + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#ifdef WIN32 +#define int386x(a,b,c,d) 0 +#define int386(a,b,c) 0 +#endif + + +/* +** VQ player specific includes. +*/ +#include +#include + +extern bool GameActive; +extern long LParam; + +#include +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +#include "debug.h" +#include "special.h" +#include "defines.h" +#include "ccini.h" +#include "ccptr.h" +#include "bar.h" + +/* +** Greenleaf specific includes. +*/ +#include +#include + + +extern long Frame; +CELL Coord_Cell(COORDINATE coord); + +#include "utracker.h" +#include "crate.h" +#include "rules.h" +#include "ini.h" +#include "int.h" +#include "pk.h" +#include "pkpipe.h" +#include "pkstraw.h" +#include "sha.h" +#include "blowfish.h" +#include "blowpipe.h" +#include "blwstraw.h" +#include "language.h" +#include "hsv.h" +#include "rgb.h" +#include "palette.h" +#include "version.h" +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "statbtn.h" +#include "slider.h" +#include "list.h" +#include "drop.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "loaddlg.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "warhead.h" +#include "weapon.h" +#include "trigtype.h" +#include "teamtype.h" // Team type objects. +#include "taction.h" +#include "tevent.h" +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // map editor class +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "vessel.h" // Sea unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "carry.h" +#include "scenario.h" +#include "msglist.h" // Multiplayer chat message system +#include "session.h" // Multiplayer session class +#include "phone.h" // Phone list manager +#include "ipxmgr.h" // IPX connection manager +#include "nullmgr.h" // Modem connection manager +#include "readline.h" +#include "vortex.h" +#include "egos.h" +#ifdef WIN32 +#include "pcx.h" +#endif + +#if(TEN) +#include "tenmgr.h" +#endif + +#if(MPATH) +#ifdef WIN32 +#include "mpmgrw.h" +#else +#include "mpmgrd.h" +#endif +#endif + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +bool InitDDraw(void); +bool PlayMpegMovie(const char* name); +#endif + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + +#ifdef WIN32 + +/* +** For WIN32, replace the assert macro so we get an error on the debugger screen +** where we can see it. +** +*/ +#ifndef __BORLANDC__ +#ifdef assert +#undef assert +#endif //assert +void Assert_Failure (char *expression, int line, char *file); + +#ifdef NDEBUG + #define assert(__ignore) ((void)0) +#else + #define assert(expr) ((expr)?(void)0:Assert_Failure(#expr,__LINE__,__FILE__)) +#endif //NDEBUG + +#endif //__BORLANDC__ + + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); +extern void Rebuild_Interpolated_Palette(unsigned char *interpal); +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage ,int cap); +#endif + +/* +** ADATA.CPP +*/ +char const * Anim_Name(AnimType anim); + +/* +** AIRCRAFT.CPP +*/ +bool Building_Check(void); + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); +AnimType Anim_From_Name(char const * name); + +/* +** AUDIO.CPP +*/ +VocType Voc_From_Name(char const * name); +char const * Speech_Name(VoxType speech); +char const * Voc_Name(VocType voc); +int Sound_Effect(VocType voc, fixed volume=1, int variation=1, signed short panvalue=0, HousesType house=HOUSE_NONE); +void Speak(VoxType voice); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord, int variation=1, HousesType house=HOUSE_NONE); +bool Is_Speaking(void); + +/* +** CDFILE.CPP +*/ +#ifdef WIN32 +int harderr_handler(unsigned, unsigned, unsigned *); +#else +int harderr_handler(unsigned, unsigned, unsigned __far *); +#endif + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead); +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land); +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int damage, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void List_Copy(short const * source, int len, short * dest); +int Get_CD_Index (int cd_drive, int timeout); +int Owner_From_Name(char const * text); +CrateType Crate_From_Name(char const * name); +Rect const Shape_Dimensions(void const * shapedata, int shapenum); +void IPX_Call_Back(void); +bool Is_Counterstrike_Installed (void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Aftermath_Installed (void); +#endif + + +#if(TEN) +void Ten_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true); +void Play_Movie(VQType name, ThemeType theme=THEME_NONE, bool clrscrn=true); +bool Main_Loop(void); +TheaterType Theater_From_Name(char const * name); +void Main_Game(int argc, char * argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const * basename); +SourceType Source_From_Name(char const * name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, DirType rotation=DIR_N, long scale=0x0100); +void Go_Editor(bool flag); +long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes); +char *CC_Get_Shape_Filename(void const * shapeptr ); +void CC_Add_Shape_To_Global(void const * shapeptr, char * filename, char code ); +void Bubba_Print(char * format,...); +void Heap_Dump_Check(char * string ); +void Dump_Heap_Pointers(void); +unsigned long Disk_Space_Available(void); +void * Hires_Load(char * name); +void Shake_The_Screen(int shakes); + +/* +** COORD.CPP +*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter=true); +void Normal_Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +int Distance(TARGET target1, TARGET target2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); +void Draw_Caption(char const * text, int x, int y, int w); +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, RemapControlType * fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, int width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag); +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Plain_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); + +/* +** DISPLAY.CPP +*/ + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); +bool Expansion_CS_Present(void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Expansion_AM_Present(void); +#endif +/* +** FINDPATH.CPP +*/ +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ + +/* +** INI.CPP +*/ +void Write_Scenario_INI(char *root); +bool Read_Scenario_INI(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); +void Assign_Houses(void); + +/* +** INIBIN.CPP +*/ +unsigned long Ini_Binary_Version( void ); +bool Read_Scenario_INB( CCFileClass *file, char *root, bool fresh ); +bool Valid_Scenario_INB( CCFileClass *file ); + +/* +** INICODE.CPP +*/ +bool Read_Scenario_INI_Write_INB( char *root, bool fresh ); + +/* +** INIT.CPP +*/ +void Load_Title_Page(bool visible=false); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + +/* +** JSHELL.CPP +*/ +int Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette, PicturePlaneType format); +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void *dest, int color, int frac); +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac); + +/* +** KEYFBUFF.ASM +*/ +extern "C" { + long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +} + +/* +** KEYFRAME.CPP +*/ +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Clear_Listbox(ListClass *list); +void Clear_Vector(DynamicVectorClass *vector); +void Computer_Message(void); +int Surrender_Dialog(int text); +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text); +bool Determine_If_Using_DVD(); +bool Using_DVD(); +#endif +int Abort_Dialog(void); + +#if(TEN) +int Read_TEN_Game_Options(void); +#endif // TEN + +#if(MPATH) +int Read_MPATH_Game_Options(void); +#endif // MPATH + +#if(TEN) +/* +** CCTEN.CPP +*/ +int Init_TEN(void); +void Shutdown_TEN(void); +void Connect_TEN(void); +void Destroy_TEN_Connection(int id, int error); +void Send_TEN_Win_Packet(void); +void Send_TEN_Alliance(char *whom, int ally); +void Send_TEN_Out_Of_Sync(void); +void Send_TEN_Packet_Too_Late(void); +#endif // TEN + +#if(MPATH) +/* +** CCMPATH.CPP +*/ +int Init_MPATH(void); +void Shutdown_MPATH(void); +void Connect_MPATH(void); +void Destroy_MPATH_Connection(int id, int error); +#endif // MPATH + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(bool skirmish=false); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); +void Log_Start_Time( char *string ); +void Log_End_Time( char *string ); +void Log_Time( char *string ); +void Log_Start_Nest_Time( char *string ); +void Log_End_Nest_Time( char *string ); + +/* +** OBJECT.CPP +*/ + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char const * profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char * profile); + +char *Read_Bin_Buffer( void ); +bool Read_Bin_Init( char *buffer, int length ); +int Read_Bin_Length( char *buffer ); +bool Read_Bin_Num( void *num, int length, char *buffer ); +int Read_Bin_Pos( char *buffer ); +int Read_Bin_PosSet( unsigned int pos, char *buffer ); +bool Read_Bin_String( char *string, char *buffer ); + +char *Write_Bin_Buffer( void ); +bool Write_Bin_Init( char *buffer, int length ); +int Write_Bin_Length( char *buffer ); +bool Write_Bin_Num( void *num, int length, char *buffer ); +int Write_Bin_Pos( char *buffer ); +int Write_Bin_PosSet( unsigned int pos, char *buffer ); +bool Write_Bin_String( char *string, int length, char *buffer ); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TargetClass whom, TARGET target); +bool Queue_Destination(TargetClass whom, TARGET target); +bool Queue_Mission(TargetClass whom, MissionType mission); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RANDOM.CPP +*/ + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass const * team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger=INFANTRY_NONE); + +/* +** ROTBMP.CPP +*/ +int Rotate_Bitmap(GraphicViewPortClass *srcvp,GraphicViewPortClass *destvp,int angle); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(Straw & file); +bool Save_Misc_Values(Pipe & file); +bool Load_MPlayer_Values(Straw & file); +bool Save_MPlayer_Values(Pipe & file); +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep); +bool Load_Game(int id); +bool Read_Object (void * ptr, int base_size, int class_size, FileClass & file, void * vtable); +bool Save_Game(int id, char const * descr, bool bargraph=false); +bool Write_Object (void * ptr, int class_size, FileClass & file); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); + +/* +** SCENARIO.CPP +*/ +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var); +void Post_Load_Game(int load_net); +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); +int BGMessageBox(char const *text, int button1, int button2); + +/* +** SCORE.CPP +*/ +char const * Map_Selection(void); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(bool simple=false); +char const * Fetch_Password(int caption, int message, int btext=TXT_OK); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath=false); +#else +int Fetch_Difficulty(void); +#endif + +/* +** STARTUP.CPP +*/ +void Print_Error_End_Exit(char *string); +void Emergency_Exit ( int code ); + +/* +** SUPPORT.ASM +*/ + +/* +** TARGET.CPP +*/ +TechnoTypeClass const * As_TechnoType(TARGET target); +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target); +AnimClass * As_Animation(TARGET target); +BuildingClass * As_Building(TARGET target); +BulletClass * As_Bullet(TARGET target); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target); +TeamClass * As_Team(TARGET target); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target); +TriggerClass * As_Trigger(TARGET target); +TriggerTypeClass * As_TriggerType(TARGET target); +UnitClass * As_Unit(TARGET target); +VesselClass * As_Vessel(TARGET target); +inline bool Target_Legal(TARGET target) {return(target != TARGET_NONE);}; +ObjectClass * As_Object(TARGET target); + +/* +** TEAMTYPE.CPP +*/ +NeedType TeamMission_Needs(TeamMissionType tmtype); + +/* +** TRACKER.CPP +*/ +void Detach_This_From_All(TARGET target, bool all=true); + +/* +** TRIGGER.CPP +*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** VERSION.CPP +*/ +char const * Version_Name(void); + +/* +** WEAPON.CPP +*/ +WeaponType Weapon_From_Name(char const * name); +ArmorType Armor_From_Name(char const * name); + +/* +** Winstub.cpp +*/ +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette); + +/* +** Egos.CPP +*/ +void Show_Who_Was_Responsible (void); + +#include "inline.h" + +/* +** These declarations ensure that the templates will be expanded for these specified +** types. Doing this is required because some of the body functions for this template class +** are located in a .CPP module. Doing so results in faster compilation but requires declarations +** such as this for all types that will be required. There are no actual objects of with these +** names ever created, however there are other objects of this type (with different names) that +** are created. +*/ +extern CCPtr y1; +extern CCPtr y2; +extern CCPtr y3; +extern CCPtr y4; +extern CCPtr y5; +extern CCPtr y6; +extern CCPtr y7; +extern CCPtr y8; +extern CCPtr y9; +extern CCPtr y10; +extern CCPtr y11; +extern CCPtr y12; +extern CCPtr y13; +extern CCPtr y14; +extern CCPtr y15; +extern CCPtr y16; +extern CCPtr y17; +extern CCPtr y18; +extern CCPtr y19; +extern CCPtr y20; +extern CCPtr y21; +extern CCPtr y22; +extern CCPtr y23; +extern CCPtr y24; +extern CCPtr y25; +extern CCPtr y26; +extern CCPtr y27; +class DynamicVectorClass >; +extern DynamicVectorClass > y002; +class DynamicVectorClass >; +extern DynamicVectorClass > y001; +class DynamicVectorClass; +extern DynamicVectorClass xxx1; +class DynamicVectorClass; +extern DynamicVectorClass xxx2; +class DynamicVectorClass; +extern DynamicVectorClass xxx3; +class DynamicVectorClass; +extern DynamicVectorClass whatever; + +#endif diff --git a/CODE/FUNCTION.I b/CODE/FUNCTION.I new file mode 100644 index 0000000..3696d50 --- /dev/null +++ b/CODE/FUNCTION.I @@ -0,0 +1,37 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : FUNCTION.I * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : November 10, 1993 * +;* * +;* Last Update : November 10, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + diff --git a/CODE/FUSE.CPP b/CODE/FUSE.CPP new file mode 100644 index 0000000..64c3b65 --- /dev/null +++ b/CODE/FUSE.CPP @@ -0,0 +1,197 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FUSE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FuseClass::FuseClass -- Constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 BRR : Created. Gosh, what a lotta work. * + *=============================================================================================*/ +FuseClass::FuseClass(void) +{ + Timer = 0; + Arming = 0; + HeadTo = 0; + Proximity = 0; +} + + +/*********************************************************************************************** + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * * + * This starts a fuse. Fuses are proximity detonation variety but * + * can be modified to have a minimum time to elapse before detonation * + * and a maximum time to exist before detonation. Typically, the * + * timing values are used for missiles that have a minimum arming * + * distance and a limited amount of fuel. * + * * + * INPUT: location -- The coordinate where the projectile start. This * + * is needed for proper proximity tracking. * + * * + * target -- The actual impact point. Fuses are based on real * + * word coordinates. * + * * + * time -- The maximum time that the fuse may work before * + * explosion is forced. * + * * + * arming -- The minimum time that must elapse before the * + * fuse may explode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Arm_Fuse(COORDINATE location, COORDINATE target, int timeto, int arming) +{ + timeto = max(timeto, arming); + Timer = min(timeto, 0xFF); + Arming = min(arming, 0xFF); + HeadTo = target; + Proximity = Distance(location, target); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * * + * This will process the fuse and update the internal clocks as well * + * as check to see if the fuse should trigger (explode) or not. * + * * + * INPUT: newlocation -- The new location of the fuse. This is needed * + * to determine proximity explosions. * + * * + * OUTPUT: bool; Was the fuse triggered to explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +bool FuseClass::Fuse_Checkup(COORDINATE newlocation) +{ + int proximity; + + /* + ** Always decrement the fuse timer. + */ + if (Timer) Timer--; + + /* + ** If the arming countdown has not expired, then do nothing. + */ + if (Arming) { + Arming--; + } else { + + /* + ** If the timer has run out, then the warhead explodes. + */ + if (!Timer) return(true); + + proximity = Distance(newlocation, HeadTo); + if (proximity < 0x0010) return(true); + if (proximity < ICON_LEPTON_W && proximity > Proximity) { + return(true); + } + Proximity = proximity; + } + return(false); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * * + * Use this routine to output the fuse class data to the save game file specified. * + * * + * INPUT: file -- The file to output the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Write(FileClass & file) +{ + file.Write(&Timer, sizeof(Timer)); + file.Write(&Arming, sizeof(Arming)); + file.Write(&HeadTo, sizeof(HeadTo)); + file.Write(&Proximity, sizeof(Proximity)); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * * + * Use this routine to input the fuse class data from the save game file specified. * + * * + * INPUT: file -- The file to input the data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Read(FileClass & file) +{ + file.Read(&Timer, sizeof(Timer)); + file.Read(&Arming, sizeof(Arming)); + file.Read(&HeadTo, sizeof(HeadTo)); + file.Read(&Proximity, sizeof(Proximity)); +} + + diff --git a/CODE/FUSE.H b/CODE/FUSE.H new file mode 100644 index 0000000..79cc587 --- /dev/null +++ b/CODE/FUSE.H @@ -0,0 +1,92 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/FUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : FUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUSE_H +#define FUSE_H + +/**************************************************************************** +** The fuse is used by projectiles to determine whether detonation should +** occur. This is usually determined by tracking the distance to the +** designated target reaches zero or when the timer expires. +*/ +class FuseClass { + public: + FuseClass(void); + FuseClass(NoInitClass const &) {}; + ~FuseClass(void) {}; + + void Arm_Fuse(COORDINATE location, COORDINATE target, int time=0xFF, int arming=0); + bool Fuse_Checkup(COORDINATE newlocation); + void Fuse_Write(FileClass & file); + void Fuse_Read(FileClass & file); + COORDINATE Fuse_Target(void); + + /* + ** Fuses can detonate if enough time has elapsed. This value counts + ** down. When it reaches zero, detonation occurs. + */ + unsigned char Timer; + + private: + + /* + ** Some fuses need a certain amount of time before detonation can + ** occur. This counts down and when it reaches zero, normal fuse + ** detonation checking can occur. + */ + unsigned char Arming; + + /* + ** This is the designated impact point of the projectile. The fuse + ** will trip when the closest point to this location has been reached. + */ + COORDINATE HeadTo; + + /* + ** This is the running proximity value to the impact point. This value + ** will progressively get smaller. Detonation occurs when it reaches + ** zero or when it starts to grow larger. + */ + short Proximity; +}; + +inline COORDINATE FuseClass::Fuse_Target(void) +{ + return(HeadTo); +} + +#endif diff --git a/CODE/GADGET.CPP b/CODE/GADGET.CPP new file mode 100644 index 0000000..9e2d4d5 --- /dev/null +++ b/CODE/GADGET.CPP @@ -0,0 +1,872 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GADGET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GADGET.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : 01/03/95 * + * * + * Last Update : August 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GadgetClass::Action -- Base action for gadget. * + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * GadgetClass::Disable -- Disables the gadget from input processing. * + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * GadgetClass::Enable -- Enables the gadget. * + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This records the current gadget the the gadget system is "stuck on". Such a +** gadget will be processed to the exclusion of all others until the mouse button +** is no longer pressed. +*/ +GadgetClass * GadgetClass::StuckOn = 0; + +/* +** This is a copy of a pointer to the last list used by the gadget input system. +** If a change of list is detected, then all gadgets are forced to be redrawn. +*/ +GadgetClass * GadgetClass::LastList = 0; + + +/* +** This points to the gadget that is intercepting all keyboard events. +*/ +GadgetClass * GadgetClass::Focused = 0; + + +/* +** This points to the current color scheme for drawing all gadgets. +*/ +static RemapControlType _GreyScheme = {15}; +RemapControlType * GadgetClass::ColorScheme = &_GreyScheme; + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * * + * This is the normal constructor for gadget objects. A gadget object is only concerned * + * with the region on the screen to considered "its own" as well as the flags that tell * + * what mouse action should be recognized when the mouse is over this screen area. * + * * + * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * + * will be "owned" by this gadget. * + * * + * w,h -- Width and height (in pixels) of this gadget's region. * + * * + * flags -- The flags (mouse conditions) that will cause this gadget's action * + * function to be called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) : + X(x), Y(y), Width(w), Height(h), IsToRepaint(false), IsSticky(sticky), IsDisabled(false), Flags(flags) +{ + if (IsSticky) { + Flags |= LEFTPRESS|LEFTRELEASE; + } +} + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * * + * This is the copy constructor for the gadget object. It will try to duplicate the * + * righthand gadget. * + * * + * INPUT: gadget -- Reference to the initilization gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(GadgetClass const & gadget) : + X(gadget.X), + Y(gadget.Y), + Width(gadget.Width), + Height(gadget.Height), + IsToRepaint(gadget.IsToRepaint), + IsSticky(gadget.IsSticky), + IsDisabled(gadget.IsDisabled), + Flags(gadget.Flags) +{ +} + + +/*********************************************************************************************** + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * * + * This is the destructor for the gadget object. It will clear the focus from this gadget * + * if this gadget currently has the focus. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass::~GadgetClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } + + if (this == StuckOn) { + StuckOn = NULL; + } + + if (this == LastList) { + LastList = NULL; + } + + if (this == Focused) { + Focused = NULL; + } +} + + +/*************************************************************************** + * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * + * area and the appropriate flag is set, then call Action(). * + * * + * INPUT: int key, int mousex, int mousey * + * * + * OUTPUT: true or false * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +{ + /* + ** Set flags to match only those events that occur AND are being looked for. If + ** the result is NULL, then we know that this button should be ignored. + */ + flags &= Flags; + + /* + ** If keyboard input should be processed by this "gadget" and keyboard input is + ** detected, then always call the action function. It is up to the action function + ** in this case to either ignore the keyboard input or not. + ** + ** For mouse actions, check to see if the mouse is in the region of the button + ** before calling the associated action function. This is the typical action for + ** buttons. + */ + if (this == StuckOn || + (flags & KEYBOARD) || + (flags && (unsigned)(mousex - X) < Width && (unsigned)(mousey - Y) < Height)) { + + return(Action(flags, key)); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Enable -- Enables the gadget. * + * * + * This function enables the gadget. An enabled gadget will be processed for input * + * purposes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Enable(void) +{ + IsDisabled = false; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Disable -- Disables the gadget from input processing. * + * * + * This routine will disable the gadget. A disabled gadget might be rendered, but is * + * ignored for input processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Disable(void) +{ + IsDisabled = true; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * * + * Use this routine if an individual gadget needs to be removed from the list of gadgets. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * + * gadget wasn't in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Remove(void) +{ + Clear_Focus(); + return(GadgetClass *)LinkClass::Remove(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * * + * This returns with the next gadget's pointer. It is identical to the base Get_Next() * + * function, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the next gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Next(void) const +{ + return(GadgetClass*)LinkClass::Get_Next(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * * + * This routine will return the previous gadget in the list. It is identical to the base * + * function Get_Prev, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Prev(void) const +{ + return(GadgetClass*)LinkClass::Get_Prev(); +} + + +/*********************************************************************************************** + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * * + * This function will delete all gadgets in the list. It is the counterpart to the * + * Create_One_Of functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Any references to these gadget become invalidated by this routine. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Delete_List(void) +{ + GadgetClass * g = this; + + /* + ** Move to head of the list. + */ + while (g->Get_Prev()) { + g = g->Get_Prev(); + } + + /* + ** First delete all the gadgets following the first one. The reason the first one + ** is kept around is that sometimes deleting one gadget will result in related gadgets + ** in the same list also being deleted. The first gadget will always contain the + ** correct gadget pointer. + */ + while (g) { + g->Clear_Focus(); + + GadgetClass * temp = g; + g = g->Get_Next(); + delete temp; + } +} + + +/*********************************************************************************************** + * GadgetClass::Action -- Base action for gadget. * + * * + * This handles the base level action that a gadget performs when a qualifying input event * + * is detected. This sets the redraw flag and returns true (to stop further processing). * + * If no qualifying input event was detected, but this routine was called anyway, then * + * don't perform any action. The call to this routine, in that case, must have been forced * + * for some other reason. * + * * + * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * + * no qualifying event occurred. * + * * + * OUTPUT: bool; Should further gadget list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Action(unsigned flags, KeyNumType &) +{ + /* + ** If any of the event flags are active, then this indicates that something probably + ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that + ** any sticky flags are cleared up. + */ + if (flags) { + IsToRepaint = true; + Sticky_Process(flags); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * * + * At this level, there is no actual rendering taking place with the call to Draw_Me, but * + * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * + * returns true, they will perform their custom rendering. * + * * + * INPUT: forced -- Is this redraw forced by outside circumstances? * + * * + * OUTPUT: bool; Should the gadget imagery be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Draw_Me(int forced) +{ + if (forced || IsToRepaint) { + IsToRepaint = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * * + * Use this function to cause all gadget in the list to be redrawn regardless of the state * + * of the IsToRepaint flag. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +void GadgetClass::Draw_All(bool forced) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + gadget->Draw_Me(forced); + gadget = gadget->Get_Next(); + } +} + + +/*************************************************************************** + * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* + * * + * INPUT: none. * + * * + * OUTPUT: key pressed. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +KeyNumType GadgetClass::Input(void) +{ + int mousex, mousey; + KeyNumType key; + unsigned flags; + int forced = false; + + /* + ** Record this list so that a forced redraw only occurs the FIRST time the + ** gadget list is passed to this routine. + */ + if (LastList != this) { + LastList = this; + forced = true; + StuckOn = NULL; + Focused = NULL; + } + + /* + ** Fetch any pending keyboard input. + */ + key = Keyboard->Check(); + if (key != 0) { + key = Keyboard->Get(); + } + +#ifdef WIN32 +#ifdef CHEAT_KEYS + if (key == KN_K && !Debug_Map && (Debug_Flag || Debug_Playtest)) { + /* + ** time to create a screen shot using the PCX code (if it works) + */ + if (!Debug_MotionCapture) { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + CDFileClass file; + char filename[30]; + +// Hide_Mouse(); + SeenBuff.Blit(temp_page); +// Show_Mouse(); + for (int lp = 0; lp < 99; lp ++) { + sprintf(filename, "scrsht%02d.pcx", lp); + file.Set_Name(filename); + if (!file.Is_Available()) break; + } + + file.Cache(200000); + Write_PCX_File(file, temp_page, & GamePalette); + Sound_Effect(VOC_BEEP); + } + } +#endif +#endif + + /* + ** For mouse button clicks, the mouse position is actually held in the MouseQ... + ** globals rather than their normal Mouse... globals. This is because we need to + ** know the position of the mouse at the exact instant when the click occurred + ** rather the the mouse position at the time we get around to this function. + */ + if (((key&0xFF) == KN_LMOUSE) || ((key&0xFF) == KN_RMOUSE)) { + mousex = Keyboard->MouseQX; + mousey = Keyboard->MouseQY; + } else { + mousex = Get_Mouse_X(); + mousey = Get_Mouse_Y(); + } + + /* + ** Set the mouse button state flags. These will be passed to the individual + ** buttons so that they can determine what action to perform (if any). + */ + flags = 0; + if (key) { + if (key == KN_LMOUSE) { + flags |= LEFTPRESS; + } + if (key == KN_RMOUSE) { + flags |= RIGHTPRESS; + } + if (key == (KN_LMOUSE | KN_RLSE_BIT)) { + flags |= LEFTRELEASE; + } + if (key == (KN_RMOUSE | KN_RLSE_BIT)) { + flags |= RIGHTRELEASE; + } + } + + /* + ** If the mouse wasn't responsible for this key code, then it must be from + ** the keyboard. Flag this fact. + */ + if (key && !flags) { + flags |= KEYBOARD; + } + + /* + ** Mouse button up or down action is ignored if there is a keyboard event. This + ** allows keyboard events to fall through normally even if the mouse is over a + ** gadget that is flagged for LEFTUP or RIGHTUP. + */ + if (!key) { + + /* + ** Check for the mouse being held down. We can't use the normal input system + ** for this, so we must examine the actual current state of the mouse + ** buttons. As a side note, if we determine that the mouse button isn't being + ** held down, then we automatically know that it must be up -- set the flag + ** accordingly. + */ + if (Keyboard->Down(KN_LMOUSE)) { + flags |= LEFTHELD; + } else { + flags |= LEFTUP; + } + if (Keyboard->Down(KN_RMOUSE)) { + flags |= RIGHTHELD; + } else { + flags |= RIGHTUP; + } + } + + /* + ** If "sticky" processing is active, then only process the stuck gadget. + */ + if (StuckOn) { + StuckOn->Draw_Me(false); + GadgetClass * oldstuck = StuckOn; + StuckOn->Clicked_On(key, flags, mousex, mousey); + if (StuckOn) { + StuckOn->Draw_Me(false); + } else { + oldstuck->Draw_Me(false); + } + } else { + + /* + ** If there is a gadget that has the keyboard focus, then route all keyboard + ** events to it. + */ + if (Focused && (flags & KEYBOARD)) { + Focused->Draw_Me(false); + Focused->Clicked_On(key, flags, mousex, mousey); + if (Focused) { + Focused->Draw_Me(false); + } + } else { + + /* + ** Sweep through all the buttons in the chain and pass the current button state + ** and keyboard input data to them. These routines will detect whether they should + ** perform some action and return a flag to this effect. They also have the option + ** of changing the key value so that an appropriate return value is use for this + ** processing routine. + */ + GadgetClass * next_button = this; + while (next_button != NULL) { + + /* + ** Maybe redraw the button if it needs to or is being forced to redraw. + */ + next_button->Draw_Me(forced); + + if (!next_button->IsDisabled) { + + /* + ** Process this button. If the button was recognized and action was + ** performed, then bail from further processing (speed reasons?). + */ + if (next_button->Clicked_On(key, flags, mousex, mousey)) { + + /* + ** Some buttons will require repainting when they perform some action. + ** Do so at this time. + */ + next_button->Draw_Me(false); + break; + } + } + + next_button = next_button->Get_Next(); + } + } + } + return(key); +} + + +/*********************************************************************************************** + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * * + * This examines the gadget list looking for on that has the same ID as specified. If that * + * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * + * or ones derived from it can have an ID value, we know that the returned pointer is at * + * least of the ControlClass type. * + * * + * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * + * a NULL will always be returned. * + * * + * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * + * If no matching gadget was found, then NULL is returned. * + * * + * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * + * will return a pointer to the first one only. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass * GadgetClass::Extract_Gadget(unsigned id) +{ + GadgetClass * g = this; + + if (id != 0) { + while (g != NULL) { + if (g->Get_ID() == id) { + return((ControlClass *)g); + } + g = g->Get_Next(); + } + } + return(0); +} + + +/*********************************************************************************************** + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * * + * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * + * Draw_Me function called at the next available opportunity. Usually, this is the next * + * time the Input() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Flag_To_Redraw(void) +{ + IsToRepaint = true; +} + + +/*********************************************************************************************** + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * * + * This function examines the event flags and handles any "sticky" processing required. * + * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * + * be processed to the exclusion of all other gadgets while the mouse button is held * + * down. * + * * + * INPUT: flags -- The event flags that triggered the call to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Sticky_Process(unsigned flags) +{ + if (IsSticky && (flags & LEFTPRESS)) { + StuckOn = this; + } + if (StuckOn == this && (flags & LEFTRELEASE)) { + StuckOn = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * * + * This will set the focus to this gadget regardless of any current focus setting. If there * + * is another gadget that has focus, it will have its focus cleared before this gadget will * + * get the focus. A focused gadget is one that has all keyboard input routed to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Focus(void) +{ + if (Focused) { + Focused->Flag_To_Redraw(); + Focused->Clear_Focus(); + } + Flags |= KEYBOARD; + Focused = this; +} + + +/*********************************************************************************************** + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * * + * Use this function to clear the focus for the gadget. If the gadget doesn't currently * + * have focus, then this routine will do nothing. For added functionality, overload this * + * virtual function so that gadget specific actions may be take when focus is lost. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Clear_Focus(void) +{ + if (Focused == this) { + Flags &= ~KEYBOARD; + Focused = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * * + * If this object has the keyboard focus, then this routine will return true. When the * + * gadget has keyboard focus, all keyboard events get routed to the gadget. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this gadget have the keyboard focus? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool GadgetClass::Has_Focus(void) +{ + return(this == Focused); +} + + +/*********************************************************************************************** + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * * + * This function is mostly for supporting HidPage drawing. If it returns true, it means * + * the application needs to re-blit the HidPage forward, after calling the list's Input(). * + * * + * INPUT: none * + * * + * OUTPUT: true = an item needs redrawing, false = no items need redrawing * + * * + * WARNINGS: It is assumed 'this' is the head of the list. * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +int GadgetClass::Is_List_To_Redraw(void) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + if (gadget->IsToRepaint) { + return (true); + } + gadget = gadget->Get_Next(); + } + return (false); +} + + +/*********************************************************************************************** + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * * + * This routine helps with moving a gadget's location. It will set the gadgets upper * + * left corner to the pixel location specified. * + * * + * INPUT: x,y -- The X and Y position to put the gadget's upper left corner to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Position(int x, int y) +{ + X = x; + Y = y; +} + diff --git a/CODE/GADGET.H b/CODE/GADGET.H new file mode 100644 index 0000000..7b8def0 --- /dev/null +++ b/CODE/GADGET.H @@ -0,0 +1,257 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GADGET.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GADGET.H * + * * + * Programmer : Maria del Mar McCready Legg * + * * + * Start Date : January 3, 1995 * + * * + * Last Update : January 3, 1995 [MML] * + * * + * * + * LinkClass [This is the linked list manager class. It keeps a record * + * ³ of the next and previous gadget in the list. It is possible * + * ³ delete a gadget out of the middle of the list with this * + * ³ class.] * + * ³ * + * GadgetClass [The is the basic gadget class. It handles processing of * + * ³ input events and dispatching the appropriate functions. * + * ³ All gadgets must be derived from this class.] * + * ÃÄÄÄÄ¿ * + * ³ ³ * + * ³ ListClass [This list class functions like a list box does in Windows. It * + * ³ keeps track of a list of text strings. This list can be * + * ³ scrolled and an item selected. If the list becomes larger than * + * ³ can be completely displayed, it will automatically create a * + * ³ slider (at the right edge) to manage the scrolling.] * + * ³ * + * ControlClass [This class adds the concept of giving an ID number to the * + * ³ gadget. This ID can then be returned from the Input() * + * ³ function as if it were a pseudo-keystroke. Additionally, * + * ³ the ability to inform another button that this button has * + * ³ been actioned is allowed. This ability allows one button * + * ³ to watch what happens to another button. Example: a list * + * ³ box gadget can tell when an attached slider has been * + * ³ touched.] * + * ÚÄÄÄÄÄÄÄÅÄÄÄÄ¿ * + * ³ ³ ³ * + * ³ ³ GaugeClass [This class looks similar to Windows slider, but has * + * ³ ³ ³ a different controlling logic. There is no thumb and * + * ³ ³ ³ it serves as a simple variable control setting. This * + * ³ ³ ³ is analogous to a volume slider.] * + * ³ ³ ³ * + * ³ ³ SliderClass [The slider class is similar to the typical Windows slider. It * + * ³ ³ has a current setting, a thumb, and a controllable scale. This * + * ³ ³ is the object created to handle a scrolling list box.] * + * ³ ³ * + * ³ EditClass * + * ³ * + * ³ * + * ToggleClass [The toggle class is used for buttons that have an image and behave just * + * ³ like the buttons in Windows do. That is, they have a separate visual for * + * ³ when they are pressed and raised. They are officially triggered (return * + * ³ their ID number) when the mouse button is released while over the button. * + * ³ This class doesn't perform any rendering itself. It merely provides the * + * ³ logic so that the derived classes will function correctly.] * + * ÚÄÁÄÄÄÄ¿ * + * ³ ³ * + * ³ TextButtonClass [The text button functions like a normal Windows style button, but * + * ³ the imagery is based on text that is displayed on the button. A * + * ³ typical example would be the "OK" or "Cancel" buttons.] * + * ³ * + * ShapeButtonClass [The shape buttons is similar to the TextButton but instead of text * + * being used to give the button its imagery, an actual shape is used * + * instead. This allows graphic buttons. These are similar to the up/down * + * arrows seen in a Windows slider.] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GADGET_H +#define GADGET_H + +#include "link.h" + +class ControlClass; + +class GadgetClass : public LinkClass +{ + public: + typedef enum FlagEnum { + LEFTPRESS = 0x0001, // Left mouse button press. + LEFTHELD = 0x0002, // Left mouse button is being held down. + LEFTRELEASE = 0x0004, // Left mouse button released. + LEFTUP = 0x0008, // Left mouse button is being held up. + RIGHTPRESS = 0x0010, // Right mouse button press. + RIGHTHELD = 0x0020, // Right mouse button is being held down. + RIGHTRELEASE = 0x0040, // Right mouse button released. + RIGHTUP = 0x0080, // Right mouse button is being held up. + KEYBOARD = 0x0100 // Keyboard input processing (maybe). + } FlagEnum; + + GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky=false); + GadgetClass(NoInitClass const & x) : LinkClass(x) {}; + GadgetClass(void) {}; + GadgetClass(GadgetClass const & gadget); + virtual ~GadgetClass(void); + + /* + ** Gadget list management functions. + */ + virtual KeyNumType Input(void); + virtual void Draw_All(bool forced=true); + virtual void Delete_List(void); + virtual ControlClass * Extract_Gadget(unsigned id); + virtual void Flag_List_To_Redraw(void) {LastList = 0;}; + virtual GadgetClass * Remove(void); + virtual GadgetClass * Get_Next(void) const; + virtual GadgetClass * Get_Prev(void) const; + + /* + ** Manages individual gadget states and actions. + */ + virtual void Disable(void); + virtual void Enable(void); + virtual unsigned Get_ID(void) const {return 0;}; + virtual void Flag_To_Redraw(void); + virtual void Peer_To_Peer(unsigned , KeyNumType & , ControlClass & ) {}; + virtual void Set_Focus(void); + virtual void Clear_Focus(void); + virtual bool Has_Focus(void); + virtual int Is_List_To_Redraw(void); + virtual bool Is_To_Redraw(void) {return (IsToRepaint);} + virtual void Set_Position(int x, int y); + + /* + ** General render function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** Sets the current color scheme + */ + static void Set_Color_Scheme(RemapControlType *scheme) + { ColorScheme = scheme; } + + static RemapControlType * Get_Color_Scheme(void) + { return (ColorScheme); } + + /* + ** This is the coordinates and dimensions of the gadget region. These are in + ** absolute screen pixel coordinates. + */ + int X; + int Y; + int Width; + int Height; + + protected: + + /* + ** Processes the event flags so that if this gadget needs to "stick" or + ** "unstick", it will be properly flagged. Call this function if you are + ** going to clear the button press flags before calling the base class + ** Action() function. Otherwise, calling this function manually, is + ** unnecessary since the base class Action() function already does so. + */ + virtual void Sticky_Process(unsigned flags); + + /* + ** This is the action function that will be called whenever the flags and mouse + ** input indicates. This is the main method by which this button performs a useful + ** function. + */ + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This is a record of the last list passed to the Input() function. If a list + ** different than the last recorded one is detected, then the draw function is + ** called for every gadget in the list. This causes all buttons to be redrawn the + ** fire time Input() is called without forced a manual call to Draw_All(). + */ + static GadgetClass * LastList; + + /* + ** This points to the gadget that has the keyboard focus. All keyboard only + ** events are fed to this gadget to the exclusion of all others. + */ + static GadgetClass * Focused; + + /* + ** This button should call the Draw_Me function because some graphic element needs + ** to be redrawn. This flag is set by default if the Action function is called. + */ + unsigned IsToRepaint:1; + + public: // HACK HACK HACK.. this is here because the sidebar buttons are static. + /* + ** A sticky button is one that is processed to the exclusion of all other buttons + ** IF the mouse was pressed down while over this button and the mouse continues + ** to remain pressed. This is the standard behavior for all normal Windows style + ** buttons. + */ + unsigned IsSticky:1; + + // ajw - Publicized StuckOn 7/30/98 (was protected) + /* + ** If there is a sticky button being processed, then this will point to it. A sticky + ** button is one that will ONLY be processed while the mouse button is being + ** held down. + */ + static GadgetClass * StuckOn; + + protected: + + /* + ** If the button is disabled, then it won't be processed by the input function. It will + ** have its Draw_Me function called as necessary. In order to not display the button + ** at all, the appropriate draw function should perform no action -- just return. Or, + ** just remove the button from the list. + */ + unsigned IsDisabled:1; + + /* + ** These are the action flags that are used to determine when the action function + ** should be called. Example: If this gadget only wants the action button called when + ** the left mouse button is pressed over the its region, then the flag will be set + ** to LEFTPRESS. + */ + unsigned Flags; + + /* + ** This is the current color scheme; it must be initialized by the app. + */ + static RemapControlType *ColorScheme; + + private: + virtual int Clicked_On(KeyNumType & key, unsigned flags, int x, int y); +}; + +inline GadgetClass::FlagEnum operator |(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +inline GadgetClass::FlagEnum operator &(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +inline GadgetClass::FlagEnum operator ~(GadgetClass::FlagEnum); + + +#endif diff --git a/CODE/GAMEDLG.CPP b/CODE/GAMEDLG.CPP new file mode 100644 index 0000000..74d8621 --- /dev/null +++ b/CODE/GAMEDLG.CPP @@ -0,0 +1,520 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GAMEDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GAMEDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "gamedlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#define GERMAN_OFFSET_Y 4 //VG + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); +#endif + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 12/31/1994 MML : Created. * + *=============================================================================================*/ +void GameControlsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 232 * RESFACTOR; // dialog width + int d_dialog_h = 141 * RESFACTOR; // dialog height + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + int d_top_margin = 25 * RESFACTOR; + + int d_txt6_h = (6 * RESFACTOR) + 1; // ht of 6-pt text + int d_margin1 = (5 * RESFACTOR); // large margin + int d_margin2 = (2 * RESFACTOR); // small margin + + int d_speed_w = d_dialog_w - (34 * RESFACTOR); + int d_speed_h = 6 * RESFACTOR; + int d_speed_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h; +#endif + + int d_scroll_w = d_dialog_w - (34 * RESFACTOR); + int d_scroll_h = 6 * RESFACTOR; + int d_scroll_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h; +#endif + + int d_visual_w = d_dialog_w - (40 * RESFACTOR); + int d_visual_h = 9 * RESFACTOR; + int d_visual_x = d_dialog_x + (20 * RESFACTOR); + int d_visual_y = d_scroll_y + d_scroll_h + d_txt6_h + (d_margin1 * 2); + + int d_sound_w = d_dialog_w - (40 * RESFACTOR); + int d_sound_h = (9 * RESFACTOR); + int d_sound_x = d_dialog_x + (20 * RESFACTOR); + int d_sound_y = d_visual_y + d_visual_h + d_margin1; + + int d_ok_w = 20 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_cx - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + +#ifdef WOLAPI_INTEGRATION + int d_wol_x = d_sound_x; + int d_wol_y = d_sound_y + d_sound_h + d_margin1; + int d_wol_w = d_sound_w; + int d_wol_h = d_sound_h; + + bool bShowWolapi = ( pWolapi && !pWolapi->bConnectionDown ); + if( bShowWolapi ) + { + // Enlarge dialog and shift ok button down. + d_dialog_h += d_wol_h + d_margin1; + d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + //d_ok_y += d_wol_h + d_margin1; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + } +#endif + + /* + ** Button Enumerations + */ +#ifdef WOLAPI_INTEGRATION + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_WOLAPI, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#else + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#endif + + /* + ** Dialog variables + */ + KeyNumType input; + + int gamespeed = Options.GameSpeed; + int scrollrate = Options.ScrollRate; + int selection; + bool pressed = false; + int curbutton = 0; + TextButtonClass *buttons[BUTTON_COUNT - BUTTON_FIRST]; + TextPrintType style; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands; // button list + + SliderClass gspeed_btn(BUTTON_SPEED, d_speed_x, d_speed_y, d_speed_w, d_speed_h, true); + SliderClass scrate_btn(BUTTON_SCROLLRATE, d_scroll_x, d_scroll_y, d_scroll_w, d_scroll_h, true); + TextButtonClass visual_btn(BUTTON_VISUAL, TXT_VISUAL_CONTROLS, TPF_BUTTON, d_visual_x, d_visual_y, d_visual_w, d_visual_h); + TextButtonClass sound_btn(BUTTON_SOUND, TXT_SOUND_CONTROLS, TPF_BUTTON, d_sound_x, d_sound_y, d_sound_w, d_sound_h); + TextButtonClass okbtn(BUTTON_OK, TXT_OPTIONS_MENU, TPF_BUTTON, d_ok_x, d_ok_y); + okbtn.X = (SeenBuff.Get_Width()-okbtn.Width)/2; + +#ifdef WOLAPI_INTEGRATION + TextButtonClass wol_btn( BUTTON_WOLAPI, TXT_WOL_OPTTITLE, TPF_BUTTON, d_wol_x, d_wol_y, d_wol_w, d_wol_h ); +#endif + + /* + ** Various Inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build button list + */ + commands = &okbtn; + gspeed_btn.Add_Tail(*commands); + scrate_btn.Add_Tail(*commands); + visual_btn.Add_Tail(*commands); + sound_btn.Add_Tail(*commands); +#ifdef WOLAPI_INTEGRATION + if( bShowWolapi ) + wol_btn.Add_Tail(*commands); +#endif + /* + ** Init button states + ** For sliders, the thumb ranges from 0 - (maxval-1), so to convert the + ** thumb value to a real-world value: + ** val = (MAX - slider.Get_Value()) - 1; + ** and, + ** slider.Set_Value(-(val + 1 - MAX)); + */ + gspeed_btn.Set_Maximum(OptionsClass::MAX_SPEED_SETTING); // varies from 0 - 7 + gspeed_btn.Set_Thumb_Size(1); + gspeed_btn.Set_Value((OptionsClass::MAX_SPEED_SETTING-1) - gamespeed); + + scrate_btn.Set_Maximum(OptionsClass::MAX_SCROLL_SETTING); // varies from 0 - 7 + scrate_btn.Set_Thumb_Size(1); + scrate_btn.Set_Value((OptionsClass::MAX_SCROLL_SETTING-1) - scrollrate); + + /* + ** Fill array of button ptrs. + */ + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = &visual_btn; + buttons[3] = &sound_btn; +#ifdef WOLAPI_INTEGRATION + buttons[4] = &wol_btn; + buttons[5] = &okbtn; +#else + buttons[4] = &okbtn; +#endif + /* + ** Processing loop. + */ + bool process = true; + bool display = true; + bool refresh = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + Map.Flag_To_Redraw(true); + Map.Render(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_GAME_CONTROLS, d_dialog_x, d_dialog_y, d_dialog_w); + Show_Mouse(); + display = false; + refresh = true; + } + + if (refresh) { + Hide_Mouse(); + + /* + ** Label the game speed slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SPEED, d_speed_x, d_speed_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print(TXT_SLOWER, d_speed_x, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_FASTER, d_speed_x + d_speed_w, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + /* + ** Label the scroll rate slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SCROLLRATE, d_scroll_x, d_scroll_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print (TXT_SLOWER, d_scroll_x, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print (TXT_FASTER, d_scroll_x + d_scroll_w, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + commands->Draw_All(); + + Show_Mouse(); + refresh = false; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + case (BUTTON_SPEED | KN_BUTTON): + curbutton = (BUTTON_SPEED - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_SCROLLRATE | KN_BUTTON): + curbutton = (BUTTON_SCROLLRATE - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_VISUAL | KN_BUTTON): + selection = BUTTON_VISUAL; + pressed = true; + break; + + case (BUTTON_SOUND | KN_BUTTON): + selection = BUTTON_SOUND; + pressed = true; + break; + + case (BUTTON_OK | KN_BUTTON): + selection = BUTTON_OK; + pressed = true; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOLAPI | KN_BUTTON): + selection = BUTTON_WOLAPI; + pressed = true; + break; +#endif + + case (KN_ESC): + process = false; + break; + + case (KN_LEFT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(1); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(1); + } + break; + + case (KN_RIGHT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(0); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(0); + } + break; + + case (KN_UP): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton--; // Skip over missing button. + } +#endif + if (curbutton < 0) { + curbutton = (BUTTON_COUNT - BUTTON_FIRST - 1); + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_DOWN): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton++; // Skip over missing button. + } +#endif + if (curbutton > (BUTTON_COUNT - BUTTON_FIRST - 1) ) { + curbutton = 0; + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_FIRST; + pressed = true; + break; + + default: + break; + } + + /* + ** Perform some action. Either to exit the dialog or bring up another. + */ + if (pressed) { + + /* + ** Record the new options slider settings. + ** The GameSpeed data member MUST NOT BE SET HERE! It will cause multiplayer + ** games to go out of sync. It's set by virtue of the event being executed. + */ + if (gamespeed != ((OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value()) ) { + gamespeed = (OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value(); + OutList.Add(EventClass(EventClass::GAMESPEED, gamespeed)); + } + + if (scrollrate != ((OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value()) ) { + scrollrate = (OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value(); + Options.ScrollRate = scrollrate; + } + process = false; + + /* + ** Save the settings in such a way that the GameSpeed is only set during + ** the save process; restore it when we're done, so multiplayer games don't + ** go out of sync. + */ + if (Session.Type == GAME_NORMAL) { + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + } else { + int old = Options.GameSpeed; // save orig value + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + Options.GameSpeed = old; // restore old value + } + + /* + ** Possibly launch into another dialog if so directed. + */ + switch (selection) { + case (BUTTON_VISUAL): + VisualControlsClass().Process(); + process = true; + display = true; + refresh = true; + break; + + case (BUTTON_SOUND): + if (!SoundType) { + WWMessageBox().Process(Text_String(TXT_NO_SOUND_CARD)); + process = true; + display = true; + refresh = true; + } else { + SoundControlsClass().Process(); + process = true; + display = true; + refresh = true; + } + break; + +#ifdef WOLAPI_INTEGRATION + case BUTTON_WOLAPI: + if( WOL_Options_Dialog( pWolapi, true ) ) + { + // The game ended while in this dialog. + process = false; + } + else + { + process = true; + display = true; + refresh = true; + } + break; +#endif + + case (BUTTON_OK): + break; + } + + pressed = false; + } + } +} + + diff --git a/CODE/GAMEDLG.H b/CODE/GAMEDLG.H new file mode 100644 index 0000000..126203f --- /dev/null +++ b/CODE/GAMEDLG.H @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GAMEDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GAMEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAMEDLG_H +#define GAMEDLG_H + +#include "gadget.h" + +class GameControlsClass +{ + public: + GameControlsClass(void) {}; + void Process(void); +}; + + +#endif + diff --git a/CODE/GAUGE.CPP b/CODE/GAUGE.CPP new file mode 100644 index 0000000..96a7a61 --- /dev/null +++ b/CODE/GAUGE.CPP @@ -0,0 +1,539 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GAUGE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GAUGE.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GaugeClass::Action -- Handles input events for the gauge. * + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * GaugeClass::Set_Value -- Set the value of the gauge. * + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * GaugeClass::GaugeClass -- class constructor * + * * + * INPUT: id -- button ID * + * * + * x,y -- upper-left corner, in pixels * + * * + * w,h -- width, height, in pixels * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +GaugeClass::GaugeClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTHELD|LEFTPRESS|LEFTRELEASE, true) +{ + Set_Maximum(255); + Set_Value(0); + + HasThumb = true; + IsHorizontal = (w > h); + IsColorized = true; + + ClickDiff = 0; +} + + +/*********************************************************************************************** + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * * + * This routine will set the maximum value for the gauge. This is the largest value that * + * the current setting may reach. The ability to change this allows the gauge to use and * + * return values that are convenient for the programmer's current needs. * + * * + * INPUT: value -- The value to use as the gauge maximum. * + * * + * OUTPUT: bool; Was the gauge maximum changed? A false indicates that the specified value * + * already matches the current maximum. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Maximum(int value) +{ + if (value != MaxValue) { + MaxValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Set_Value -- Set the value of the gauge. * + * * + * This routine will set the current value for the gauge. This value is clipped to the * + * limits of the gauge maximum. * + * * + * INPUT: value -- The value to set at the new current value. * + * * + * OUTPUT: bool; Was the current setting changed? A false indicates that the setting * + * specified is the same as what was already there. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Value(int value) +{ + value = Bound(value, 0, MaxValue); + if (value != CurValue) { + CurValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * * + * Use this routine to convert the specified pixel offset into a gauge value. This is used * + * in translating mouse clicks into a corresponding setting for the gauge. * + * * + * INPUT: pixel -- The pixel offset form the start of the gauge. * + * * + * OUTPUT: Returns with the setting value in gauge coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Pixel_To_Value(int pixel) +{ + int maximum; + + if (IsHorizontal) { + pixel -= X+1; + maximum = Width; + } else { + pixel -= Y+1; + maximum = Height; + } + maximum -= 2; + pixel = Bound(pixel, 0, maximum); + return(MaxValue * fixed(pixel, maximum)); +// return(Fixed_To_Cardinal(MaxValue, Cardinal_To_Fixed(maximum, pixel))); +} + + +/*********************************************************************************************** + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * * + * Use this routine to convert the specified gauge value into a pixel offset from the * + * star of the gauge. This is used for thumb positioning. * + * * + * INPUT: value -- The value to convert to a pixel offset. * + * * + * OUTPUT: Returns with the pixel offset of the specified value from the start of the * + * gauge. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Value_To_Pixel(int value) +{ + int maximum; + int start; + if (IsHorizontal) { + maximum = Width; + start = X; + } else { + maximum = Height; + start = Y; + } + maximum -= 2; + return(start + maximum * fixed(value, MaxValue)); +// return(start + Fixed_To_Cardinal(maximum, Cardinal_To_Fixed(MaxValue, value))); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * * + * OUTPUT: bool; Was the gauge redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + if (IsColorized) { + int middle = Value_To_Pixel(CurValue); + int color = GadgetClass::Get_Color_Scheme()->Bright; + if (IsHorizontal) { + if (middle >= (X + 1)) + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, color); + } else { + if (middle >= (Y + 1)) + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, color); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Action -- Handles input events for the gauge. * + * * + * This routine will handle input event processing for the gauge. It will adjust the * + * current setting of the gauge according to the mouse position. * + * * + * INPUT: flags -- The input event that is the reason for this function call. * + * key -- The key code that caused the event. * + * * + * OUTPUT: bool; Was the even recognized, processed, and no further gadget scanning is * + * desired (for this pass). * + * * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there's no thumb on this gauge, it's a display-only device. + */ + if (!HasThumb) { + return(false); + } + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + /* + ** If the thumb is currently being "dragged around", then update the slider + ** position according to the mouse position. In all other cases, ignore the + ** button being held down. + */ + if ((flags & LEFTPRESS) || ((flags & LEFTHELD) && StuckOn == this)) { + + /* + ** Compute the difference between where we clicked, and the edge of + ** the thumb (only if we clicked on the thumb.) + */ + if (flags & LEFTPRESS) { + int curpix = Value_To_Pixel(CurValue); + int clickpix = (IsHorizontal ? Get_Mouse_X() : Get_Mouse_Y()); + + if ( (clickpix > curpix) && (clickpix - curpix) < Thumb_Pixels()) { + ClickDiff = (clickpix - curpix); + } else { + ClickDiff = 0; + } + + int testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + + /* + ** Correct for round-down errors in Pixel_To_Value() and + ** Value_To_Pixel(); make ClickDiff exactly right so that + ** at this point, Get_Mouse_n() - ClickDiff converts to + ** CurValue. + */ + while (testval < CurValue && ClickDiff > 0) { + ClickDiff--; + testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + } + } + + /* + ** If no change occurred in the gauge, just call Control's Action routine, + ** but turn off the flags so it won't fill in 'key' with the button ID. + ** Thus, no button ID will be returned by Input. + */ + if (!Set_Value(Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff))) { + + flags &= ~(LEFTHELD|LEFTRELEASE|LEFTPRESS); + ControlClass::Action(0, key); + key = KN_NONE; + return(true); + } + + } else { + + /* + ** Ignore the left mouse button being held down if this gauge is not + ** currently in "sticky" mode. This allows processing of the LEFTPRESS + ** by any derived classes such that this gauge can be more closely + ** controlled. + */ + flags &= ~LEFTHELD; + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Thumb -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: none. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +void GaugeClass::Draw_Thumb(void) +{ + int x = Value_To_Pixel(CurValue); + + if ((x + 4) > Value_To_Pixel(MaxValue)) { + x = Value_To_Pixel(MaxValue) - 2; + } + + if (x < X) { + x = X; + } + + if (IsHorizontal) { + Draw_Box(x, Y, 4, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, x, Width, 4, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * TriColorGaugeClass::TriColorGaugeClass -- Constructor for 3 color (red\yellow\green) gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: See below. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +TriColorGaugeClass::TriColorGaugeClass(unsigned id, int x, int y, int w, int h) + : GaugeClass(id, x, y, w, h) +{ + RedLimit = 0; // maximum value for red + YellowLimit = 0; // maximum value for yellow +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Red_Limit -- Set the value for the red area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Red_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value > YellowLimit) { +// RedLimit = YellowLimit; +// YellowLimit = value; +// } else { + RedLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Yellow_Limit -- Set the value for the yellow area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Yellow_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value < RedLimit) { +// YellowLimit = RedLimit; +// RedLimit = value; +// } else { + YellowLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Draw_Me -- Draw the tri color gauge. * + * * + * INPUT: int forced -- draw or not? * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, (IsDisabled ? BOXSTYLE_RAISED : BOXSTYLE_DOWN), true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + int red = Value_To_Pixel(RedLimit); + int yellow = Value_To_Pixel(YellowLimit); + int middle = Value_To_Pixel(CurValue); + + if (CurValue <= RedLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, PINK); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, PINK); + } + } else if (CurValue > RedLimit && CurValue <= YellowLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, middle, Y+Height-2, YELLOW); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, middle, YELLOW); + } + } else if (CurValue > YellowLimit && CurValue <= MaxValue) { + + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, yellow, Y+Height-2, YELLOW); + LogicPage->Fill_Rect(yellow, Y+1, middle, Y+Height-2, GREEN); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, yellow, YELLOW); + LogicPage->Fill_Rect(X+1, yellow, X+Width-2, middle, GREEN); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + diff --git a/CODE/GAUGE.H b/CODE/GAUGE.H new file mode 100644 index 0000000..f174d9e --- /dev/null +++ b/CODE/GAUGE.H @@ -0,0 +1,109 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GAUGE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GAUGE.H * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAUGE_H +#define GAUGE_H + +class GaugeClass : public ControlClass +{ + public: + + GaugeClass(unsigned id, int x, int y, int w, int h); + + virtual int Draw_Me(int forced=false); + virtual int Set_Maximum(int value); + virtual int Set_Value(int value); + virtual int Get_Value(void) const {return (CurValue);}; + virtual void Use_Thumb(int value) { HasThumb = value ? true : false; }; + + virtual int Thumb_Pixels(void) { return (4);} + + /* + ** If this gauge has a color to the left of the current setting, then this + ** flag will be true. + */ + unsigned IsColorized:1; + + protected: + + /* + ** If a thumb is desired, set to true. + */ + unsigned HasThumb:1; + + /* + ** Is this a horizontal slider? + */ + unsigned IsHorizontal:1; + + int MaxValue; // maximum value (in application units) + int CurValue; // index of 1st displayed string in box + // (in application units) + + /* + ** This value records the difference between where the user clicked + ** and the edge of the thumb, so that the thumb follows the mouse + ** with the proper offset. + */ + int ClickDiff; + + protected: + virtual void Draw_Thumb(void); + virtual int Action(unsigned flags, KeyNumType &key); + virtual int Pixel_To_Value(int pixel); + virtual int Value_To_Pixel(int value); +}; + + + +class TriColorGaugeClass : public GaugeClass +{ + public: + TriColorGaugeClass(unsigned id, int x, int y, int w, int h); + virtual int Draw_Me(int forced); + virtual int Set_Red_Limit(int value); + virtual int Set_Yellow_Limit(int value); + + protected: + int RedLimit; // maximum value for red + int YellowLimit; // maximum value for yellow +}; + + + + +#endif diff --git a/CODE/GETCPU.CPP b/CODE/GETCPU.CPP new file mode 100644 index 0000000..4ebb543 --- /dev/null +++ b/CODE/GETCPU.CPP @@ -0,0 +1,104 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GETCPU.CPP 1 3/03/97 10:24a Joe_bostic $*/ +/*********************************************************************************************** + *** 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 : GETCPU * + * * + * File Name : GETCPU.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 6/26/96 * + * * + * Last Update : June 26th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Example of interface to assembly language code to find CPU type * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Get_CPU_Type -- interface to ASM detection code * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//#define WIN32 +//#include +#include +#include + + +#define bool int + +/* +** Prototypes for linkage to assembly module +*/ +extern "C" { + bool __cdecl Detect_MMX_Availability (void); + + extern char CPUType; + extern char VendorID; +} + + +/*********************************************************************************************** + * Get_CPU_Type -- Find out what kind of CPU we are running on * + * * + * * + * * + * INPUT: int - reference to cpu type * + * bool - reference to mmx availability flag * + * char* - ptr to buffer to receive chip vendor info * + * int - length of above buffer * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/26/96 10:15AM ST : Created * + *=============================================================================================*/ +void Get_CPU_Type(int & cpu_type, bool & mmx, char * vendor_id, int vendor_id_length) +{ + /* + ** Call the asm CPU detection code + */ + mmx = Detect_MMX_Availability(); + + /* + ** Return the promised results + */ + cpu_type = (int)CPUType; + char * vendor_ptr = &VendorID; + strncpy(vendor_id, vendor_ptr, vendor_id_length); +} + + + + + + + + + + diff --git a/CODE/GLOBALS.CPP b/CODE/GLOBALS.CPP new file mode 100644 index 0000000..b573661 --- /dev/null +++ b/CODE/GLOBALS.CPP @@ -0,0 +1,805 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/GLOBALS.CPP 2 3/10/97 6:22p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +bool IsVQ640 = false; +unsigned long GameVersion = 0; +bool Debug_MotionCapture = false; +bool Debug_Rotate = false; // Rotation algorithm control. +bool Debug_Quiet = false; +bool Debug_Cheat = false; +bool Debug_Remap = false; +bool Debug_Icon = false; +bool Debug_Flag = false; +bool Debug_Lose = false; +bool Debug_Win = false; +bool Debug_Map = false; // true = map editor mode +bool Debug_Passable = false; // true = show passable/impassable terrain +bool Debug_Unshroud = false; // true = hide the shroud +bool Debug_Threat = false; +bool Debug_Find_Path = false; +bool Debug_Check_Map = false; // true = validate the map each frame +bool Debug_Playtest = false; + +bool Debug_Heap_Dump = false; // true = print the Heap Dump +bool Debug_Smart_Print = false; // true = print everything that calls Smart_Printf +bool Debug_Trap_Check_Heap = false; // true = check the Heap +bool Debug_Modem_Dump = false; // true = print the Modem Stuff +bool Debug_Print_Events = false; // true = print event & packet processing + +TFixedIHeapClass Aircraft; +TFixedIHeapClass Anims; +TFixedIHeapClass Buildings; +TFixedIHeapClass Bullets; +TFixedIHeapClass Factories; +TFixedIHeapClass Houses; +TFixedIHeapClass Infantry; +TFixedIHeapClass Overlays; +TFixedIHeapClass Smudges; +TFixedIHeapClass Teams; +TFixedIHeapClass TeamTypes; +TFixedIHeapClass Templates; +TFixedIHeapClass Terrains; +TFixedIHeapClass Triggers; +TFixedIHeapClass Units; +TFixedIHeapClass Vessels; +TFixedIHeapClass TriggerTypes; + +TFixedIHeapClass HouseTypes; +TFixedIHeapClass BuildingTypes; +TFixedIHeapClass AircraftTypes; +TFixedIHeapClass InfantryTypes; +TFixedIHeapClass BulletTypes; +TFixedIHeapClass AnimTypes; +TFixedIHeapClass UnitTypes; +TFixedIHeapClass VesselTypes; +TFixedIHeapClass TemplateTypes; +TFixedIHeapClass TerrainTypes; +TFixedIHeapClass OverlayTypes; +TFixedIHeapClass SmudgeTypes; + + +/* +** These are the instantiate static heap pointers for the various +** CCPtr class objects that are allowed to exist. If the linker generates +** an error about a missing heap pointer, then this indicates that CCPtr objects +** for that type are not allowed. For every case of a TFixedIHeap manager of +** game objects, then a CCPtr can be instantiated for it. +*/ +template FixedIHeapClass * CCPtr::Heap = &Aircraft; +template FixedIHeapClass * CCPtr::Heap = &Anims; +template FixedIHeapClass * CCPtr::Heap = &Buildings; +template FixedIHeapClass * CCPtr::Heap = &Bullets; +template FixedIHeapClass * CCPtr::Heap = &Factories; +template FixedIHeapClass * CCPtr::Heap = &Houses; +template FixedIHeapClass * CCPtr::Heap = &Infantry; +template FixedIHeapClass * CCPtr::Heap = &Overlays; +template FixedIHeapClass * CCPtr::Heap = &Smudges; +template FixedIHeapClass * CCPtr::Heap = &Teams; +template FixedIHeapClass * CCPtr::Heap = &TeamTypes; +template FixedIHeapClass * CCPtr::Heap = &Templates; +template FixedIHeapClass * CCPtr::Heap = &Terrains; +template FixedIHeapClass * CCPtr::Heap = &Triggers; +template FixedIHeapClass * CCPtr::Heap = &TriggerTypes; + +template FixedIHeapClass * CCPtr::Heap = &HouseTypes; +template FixedIHeapClass * CCPtr::Heap = &BuildingTypes; +template FixedIHeapClass * CCPtr::Heap = &AircraftTypes; +template FixedIHeapClass * CCPtr::Heap = &InfantryTypes; +template FixedIHeapClass * CCPtr::Heap = &BulletTypes; +template FixedIHeapClass * CCPtr::Heap = &AnimTypes; +template FixedIHeapClass * CCPtr::Heap = &UnitTypes; +template FixedIHeapClass * CCPtr::Heap = &VesselTypes; +template FixedIHeapClass * CCPtr::Heap = &TemplateTypes; +template FixedIHeapClass * CCPtr::Heap = &TerrainTypes; +template FixedIHeapClass * CCPtr::Heap = &OverlayTypes; +template FixedIHeapClass * CCPtr::Heap = &SmudgeTypes; + + +/* These variables are used to keep track of the slowest speed of a team */ +MPHType TeamMaxSpeed[10]; +SpeedType TeamSpeed[10]; +bool FormMove; +SpeedType FormSpeed; +MPHType FormMaxSpeed; + + +char _staging_buffer[32000]; + +/* +** Global flag for the life of Tanya. If this flag is set, she is +** no longer available. +*/ +bool IsTanyaDead; +bool SaveTanya; + +#ifdef FIXIT_ANTS +bool AntsEnabled = false; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool NewUnitsEnabled = false; +bool SecretUnitsEnabled = false; +int MTankDistance = 15; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +int CarrierLaunchDelay = 60; +#endif +#endif + +int NewINIFormat = 0; + +bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool PendingTimeQuake; +TARGET TimeQuakeCenter; +fixed QuakeUnitDamage=0x300; +fixed QuakeBuildingDamage=0x300; +int QuakeInfantryDamage=25; +int QuakeDelay; +fixed ChronoTankDuration=0x300; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +fixed EngineerDamage=0x55; // Amount of damage an engineer does +fixed EngineerCaptureLevel=0x40; // Building damage level before engineer can capture +#endif +#endif + +#ifdef WIN32 +unsigned short Hard_Error_Occured = 0; +WWMouseClass * WWMouse = NULL; +GraphicBufferClass SysMemPage(DEFAULT_SCREEN_WIDTH, 200, (void*)NULL); +WinTimerClass * WindowsTimer=NULL; +int ScreenWidth=640; +int ScreenHeight=400; +GraphicBufferClass ModeXBuff; +bool InMovie = FALSE; //Are we currently playing a VQ movie? +HANDLE hInstance; +int AllDone; + + +/*************************************************************************** +** This is true if the game is the currently in focus windows app +** +*/ +bool GameInFocus = false; + +#endif + + +/*************************************************************************** +** Encryption keys. +*/ +PKey FastKey; +#ifdef CHEAT_KEYS +PKey SlowKey; +#endif + +#ifdef FIXIT_NAME_OVERRIDE +/*************************************************************************** +** This is where the name overrides for the units will reside. +*/ +char const * NameOverride[25]; +int NameIDOverride[25]; +#endif + +/*************************************************************************** +** These are the mission control structures. They hold the information about +** how the missions should behave in the system. +*/ +MissionControlClass MissionControl[MISSION_COUNT]; + + +/*************************************************************************** +** There are various tutorial messages that can appear in the game. These +** are called upon by number and pointed to by this array. +*/ +char const * TutorialText[225]; + + +/*************************************************************************** +** This holds the rules database. The rules database won't change during the +** program's run, but may need to be referenced intermitently. +*/ +CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +CCINIClass AftermathINI; +#endif + +/*************************************************************************** +** This points to the benchmark objects that are allocated only if the +** machine is running on a Pentium and this is a debug version. +*/ +Benchmark * Benches; + + +/*************************************************************************** +** General rules that control the game. +*/ +RulesClass Rule; + + +/*************************************************************************** +** All keyboard input is routed through the object pointed to by this +** keyboard class pointer. +*/ +KeyboardClass * Keyboard; + + +/*************************************************************************** +** Remap control array. This is used to hold the remap +** tables for the various possible player colors, and the color schemes +** for dialogs. +*/ +RemapControlType ColorRemaps[PCOLOR_COUNT]; + +/* +** Special remap scheme for font that hs to print over metallic tabs +*/ +RemapControlType MetalScheme; + +/* +** This remap table is for special purposes. It consists of dark grey shades, +** and is used for dimming things out. +*/ +RemapControlType GreyScheme; + + +/*************************************************************************** +** This is the source of the random numbers used in the game. This controls +** the game logic and thus must be in sync with any networked machines. +*/ +RandomClass NonCriticalRandomNumber; +RandomStraw CryptRandom; + + +/*************************************************************************** +** This is a list of all selected objects (for this map). The support functions +** are used to control access to this list. Do not modify it directly. +*/ +DynamicVectorClass CurrentObject; + + +/*************************************************************************** +** This is the game version. +*/ +VersionClass VerNum; + + +/*************************************************************************** +** This is the VQ animation controller structure. It is filled in by reading +** the PLAYER.INI and overridden through program control. +*/ +VQAConfig AnimControl; + +int PreserveVQAScreen; // Used for screen mode transition control. +bool BreakoutAllowed = true; // "true" if aborting of movies is allowed. +bool Brokeout; // Was the movie broken out of? +bool SlowPalette = false; // Slow palette flag set? + + +/*************************************************************************** +** These are the movie names to use for mission briefing, winning, and losing +** sequences. They are read from the INI file. +*/ +ScenarioClass Scen; + + +/*************************************************************************** +** This is the pending speech sample to play. This sample will be played +** at the first opportunity. +*/ +VoxType SpeakQueue = VOX_NONE; + + +/*************************************************************************** +** This records if the score (music) file is present. If not, then much of +** the streaming score system can be disabled. +*/ +bool ScoresPresent; + + +/*************************************************************************** +** This flag will control whether there is a response from game units. +** By carefully controlling this global, multiple responses are suppressed +** when a large group of infantry is given the movement order. +*/ +bool AllowVoice = true; + + +/*************************************************************************** +** This is the current frame number. This number is guaranteed to count +** upward at the rate of one per game logic process. The target rate is 15 +** per second. This value is saved and restored with the saved game. +*/ +long Frame = 0; + + +/*************************************************************************** +** These globals are constantly monitored to determine if the player +** has won or lost. They get set according to the trigger events associated +** with the scenario. +*/ +bool PlayerWins; +bool PlayerLoses; +bool PlayerRestarts; + +/* +** This flag is set if the player neither wins nor loses; it's mostly for +** multiplayer mode. +*/ +bool PlayerAborts; + + +/*************************************************************************** +** This is the pointer for the speech staging buffer. This buffer is used +** to hold the currently speaking voice data. Since only one speech sample +** is played at a time, this buffer is only as big as the largest speech +** sample that can be played. +*/ +void * SpeechBuffer[2]; +VoxType SpeechRecord[2]; + + +/*************************************************************************** +** The theater specific mixfiles are cached into the buffer pointed to by +** this global. +*/ +Buffer * TheaterBuffer; + + +/*************************************************************************** +** This is a running accumulation of the number of ticks that were unused. +** This accumulates into a useful value that contributes to a +** histogram of game performance. +*/ +long SpareTicks; +long PathCount; // Number of findpaths called. +long CellCount; // Number of cells redrawn. +long TargetScan; // Number of target scans. +long SidebarRedraws; // Number of sidebar redraws. + + +/*************************************************************************** +** This is the monochrome debug page array. The various monochrome data +** screens are located here. +*/ +MonoClass MonoArray[DMONO_COUNT]; +DMonoType MonoPage = DMONO_STRESS; // The current page. + + +/*************************************************************************** +** This holds the theater specific mixfiles. +*/ +MFCD * TheaterData = 0; +MFCD * MoviesMix = 0; +MFCD * GeneralMix = 0; +MFCD * ScoreMix = 0; +MFCD * MainMix = 0; +MFCD * ConquerMix = 0; + + +/*************************************************************************** +** This is the options control class. The options control such things as +** game speed, visual controls, and other user settings. +*/ +GameOptionsClass Options; + + +/*************************************************************************** +** Logic processing is controlled by this element. It handles both graphic +** and AI logic. +*/ +LogicClass Logic; + + +/*************************************************************************** +** This handles the background music. +*/ +ThemeClass Theme; + + +/*************************************************************************** +** This is the main control class for the map. +*/ +#ifdef SCENARIO_EDITOR +MapEditClass Map; +#else +MouseClass Map; +#endif + + +/************************************************************************** +** The running game score is handled by this class (and member functions). +*/ +ScoreClass Score; + + +/*************************************************************************** +** The running credit display is controlled by this class (and member +** functions. +*/ +CreditClass CreditDisplay; + + +/************************************************************************** +** This class records the special command override options that C&C +** supports. +*/ +SpecialClass Special; + + +bool PassedProximity; // used in display.cpp + + +/*************************************************************************** +** This is the scenario data for the currently loaded scenario. +** These variables should all be set together. +*/ +HousesType Whom; // Initial command line house choice. +bool ScenarioInit; +bool SpecialFlag = false; + + +/*************************************************************************** +** This value tells the sidebar what items it's allowed to add. The +** lower the value, the simpler the sidebar will be. This value is the +** displayed value for tech level in the multiplay dialogs. It remaps to +** the in-game rules.ini tech levels. +*/ +int BuildLevel = 10; // Buildable level (1 = simplest) + + +/*************************************************************************** +** The various tutor and dialog messages are located in the data block +** referenced by this pointer. +*/ +char const * SystemStrings; +char const * DebugStrings; + + +/*************************************************************************** +** The game plays as long as this var is true. +*/ +bool GameActive; + + +/*************************************************************************** +** This is a scratch variable that is used to when a reference is needed to +** a long, but the value wasn't supplied to a function. This is used +** specifically for the default reference value. As such, it is not stable. +*/ +long LParam; + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** +** The currently-selected cell for the Scenario Editor +*/ +CELL CurrentCell = 0; +#endif + + +/*************************************************************************** +** Most of the text in the game will use the six point font. These are the +** pointers to the fonts. If it is NULL, then the font hasn't been loaded +** yet. +*/ +void const * Metal12FontPtr; //Font for use on in-game tabs in hires +void const * MapFontPtr; // Standard very small font. +void const * TypeFontPtr; // Teletype font for mission briefings. +void const * Font3Ptr; // Standard very small font. +void const * Font6Ptr; // Standard small font. +void const * EditorFont; // Font used for scenario editor. +void const * Font8Ptr; // 8 point proportional. +void const * FontLEDPtr; // LED fixed point font. +void const * VCRFontPtr; // VCR font pointer. +void const * ScoreFontPtr; // font for score & map selection screens +void const * GradFont6Ptr; // gradient 6 point font pointer. + + +/*************************************************************************** +** This is the house that the human player is currently playing. +*/ +HouseClass * PlayerPtr; + + +/*************************************************************************** +** Special palettes for MCGA mode goes here. These palette buffers are used +** for pictures that do not use the game palette or are used for fading to +** black. +*/ +PaletteClass CCPalette; +PaletteClass GamePalette; +//PaletteClass InGamePalette; +PaletteClass BlackPalette(RGBClass(0, 0, 0)); +PaletteClass WhitePalette(RGBClass(RGBClass::MAX_VALUE, RGBClass::MAX_VALUE, RGBClass::MAX_VALUE)); +PaletteClass OriginalPalette; +PaletteClass ScorePalette; + + +/*************************************************************************** +** These are the event queues. One is for holding events until they are ready to be +** sent to the remote computer for processing. The other list is for incoming events +** that need to be executed when the correct frame has been reached. +*/ +QueueClass OutList; +QueueClass DoList; + +#ifdef MIRROR_QUEUE +QueueClass MirrorList; +#endif + + +/*************************************************************************** +** These are arrays/lists of trigger pointers for each cell & the houses. +*/ +DynamicVectorClass HouseTriggers[HOUSE_COUNT]; +DynamicVectorClass MapTriggers; +int MapTriggerID; +DynamicVectorClass LogicTriggers; +int LogicTriggerID; + + +/*************************************************************************** +** This is the list of BuildingTypes that define the AI's base. +*/ +BaseClass Base; + + +/*************************************************************************** +** This is the list of carry over objects. These objects are part of the +** pseudo saved game that might be carried along with the current saved +** game. +*/ +CarryoverClass * Carryover; + + +/*************************************************************************** +** This value is computed every time a new scenario is loaded; it's a +** CRC of the INI and binary map files. +*/ +unsigned long ScenarioCRC; + + +/*************************************************************************** +** This class manages data specific to multiplayer games. +*/ +SessionClass Session; +#if(TIMING_FIX) +// +// These values store the min & max frame #'s for when MaxAhead >>increases<<. +// If MaxAhead increases, and the other systems free-run to the new MaxAhead +// value, they may miss an event generated after the MaxAhead event was sent, +// but before it executed, since it will have been scheduled with the older, +// shorter MaxAhead value. This will cause a Packet_Received_Too_Late error. +// The frames from the point where the new MaxAhead takes effect, up to that +// frame Plus the new MaxAhead, represent a "period of vulnerability"; any +// events received that are scheduled to execute during this period should +// be re-scheduled for after that period. +// +int NewMaxAheadFrame1; +int NewMaxAheadFrame2; +#endif + +#ifdef FIXIT_VERSION_3 +bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +unsigned long PlayingAgainstVersion; // Negotiated version number +bool Version107InMix; // Is there a v1.07 in the game +#endif + +/*************************************************************************** +** This is the null modem manager class. Declaring this class doesn't +** perform any allocations; +*/ +NullModemClass NullModem ( + 16, // number of send entries + 16, // number of receive entries + (MAX_SERIAL_PACKET_SIZE / sizeof(EventClass) ) * sizeof(EventClass) + sizeof( CommHeaderType ), + 0x1234); // Magic number must have each digit unique + // and different from the queue magic number + + + +/*************************************************************************** +** This is the network IPX manager class. It handles multiple remote +** connections. Declaring this class doesn't perform any allocations; +** the class itself is 140 bytes. +*/ +//IPXManagerClass Ipx ( +// MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets +// ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), +// 10, // # entries in Global Queue +// 8, // # entries in Private Queues +// VIRGIN_SOCKET, // Socket ID # +// IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + +IPXManagerClass Ipx ( + MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets + ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), + 160, // # entries in Global Queue + 32, // # entries in Private Queues + VIRGIN_SOCKET, // Socket ID # + IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + + +#if(TEN) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - TEN connection ID's are equal to the HousesType for that player. +** - The TEN internal player ID is used to determine the player's color. +** - Ten's broadcast destination address -1 +*/ +TenConnManClass *Ten = NULL; + +#endif + + +#if(MPATH) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - MPATH connection ID's are equal to the HousesType for that player. +** - The player's color is read from the OPTIONS.INI file +** - MPath's broadcast destination address is 0 +*/ +MPlayerManClass *MPath = NULL; + +#endif + + +/*************************************************************************** +** This is the random-number seed; it's synchronized between systems for +** multiplayer games. +*/ +int Seed = 0; + + +/*************************************************************************** +** If this value is non-zero, use it as the random # seed instead; this should +** help reproduce some bugs. +*/ +int CustomSeed = 0; + +int WindowList[][8] = { +/* xbyte, ypixel, bytewid, pixelht, cursor color, bkgd color, cursor x, cursor y */ + + /* do not change the first 2 entries!! they are necc. to the system */ + + {0,0,40*8*RESFACTOR,200*RESFACTOR,WHITE,BLACK,0,0}, /* screen window */ + {1*8,75,38*8,100,WHITE,BLACK,0,0}, /* DOS Error window */ + + // Tactical map. + {0, 0, 40*8*RESFACTOR, 200*RESFACTOR, WHITE,LTGREY,0,0}, + + // Initial menu window. + {12*8, 199-42, 16*8, 42, LTGREY, DKGREY, 0, 0}, + + // Sidebar clipping window. + {0,0,0,0,0,0,0,0}, + + // Scenario editor window. + {5*8, 30, 30*8, 140, 0, 0, 0, 0}, + + // Partial object draw sub-window. + {0,0,0,0,WHITE,BLACK,0,0} +}; + + +/* X,Y,Item Width,Items High,Selected,Norm Color,Sel Color,zero */ +int MenuList[][8]={ + {1, 3, 12, 3, 0, WHITE, PINK, 0}, +}; + + +#ifdef WIN32 +GraphicBufferClass VisiblePage; +GraphicBufferClass HiddenPage; +GraphicViewPortClass SeenBuff(&VisiblePage, 0, 0, 640, 480); +GraphicViewPortClass HidPage(&HiddenPage, 0, 0, 640, 480); +#else +GraphicBufferClass HidPage(DEFAULT_SCREEN_WIDTH, 201, (void*)NULL); +GraphicBufferClass SeenBuff(320, 200, (void *)0xA0000L); +VideoBufferClass SeenPage; +GraphicBufferClass & VisiblePage = SeenBuff; +#endif + + +#ifdef WIN32 +#else +#endif + +int SoundOn; +CDTimerClass FrameTimer; +CDTimerClass CountDownTimer; + +NewConfigType NewConfig; +TheaterType LastTheater = THEATER_NONE; //Lets us know when theater type changes. + + +/*************************************************************************** +** This flag is for popping up dialogs that call the main loop. +*/ +SpecialDialogType SpecialDialog = SDLG_NONE; + + +int RequiredCD = -1; +int CurrentCD = -1; +int MouseInstalled; + +// +// Variables for helping track how much time goes bye in routines +// +int LogLevel = 0; +unsigned long LogLevelTime[ MAX_LOG_LEVEL ] = { 0 }; +unsigned long LogLastTime = 0; +bool LogDump_Print = false; // true = print the Log time Stuff + + +/*************************************************************************** +** Tick Count global timer object. +*/ +TTimerClass TickCount = 0; + + +/*************************************************************************** +** Win32 specific globals +*/ +#ifdef WIN32 + +bool InDebugger = false; +bool ReadyToQuit = false; + +#else +bool IsTheaterShape = false; // must be defined only if not Win32 +#endif //WIN32 + +GetCDClass CDList; +int UnitBuildPenalty = 100; + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +MCIMovie* MciMovie = NULL; +#endif + +#include "mpgset.h" +MPGSettings* MpgSettings = NULL; +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +bool bAutoSonarPulse = false; +#endif diff --git a/CODE/GOPTIONS.CPP b/CODE/GOPTIONS.CPP new file mode 100644 index 0000000..1d38f4e --- /dev/null +++ b/CODE/GOPTIONS.CPP @@ -0,0 +1,624 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/GOPTIONS.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "goptions.h" +#include "loaddlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#include "gamedlg.h" +#include "textbtn.h" +#include "descdlg.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +bool RedrawOptionsMenu; + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + * 06/23/1995 JLB : Handles restating the mission objective. * + * 07/27/1995 JLB : Adjusts menu for multiplay mode. * + *=============================================================================================*/ +void GameOptionsClass::Process(void) +{ + static struct { + int ID; // Button ID to use. + int Text; // Text number to use for this button. + bool Multiplay; // Allowed in multiplayer version? + } _constants[] = { + {BUTTON_LOAD, TXT_LOAD_MISSION, false}, +#ifdef FIXIT_MULTI_SAVE + {BUTTON_SAVE, TXT_SAVE_MISSION, true}, +#else + {BUTTON_SAVE, TXT_SAVE_MISSION, false}, +#endif + {BUTTON_DELETE, TXT_DELETE_MISSION, true}, + {BUTTON_GAME, TXT_GAME_CONTROLS, true}, + {BUTTON_QUIT, TXT_QUIT_MISSION, true}, +#ifdef FIXIT_VERSION_3 // Stalemate games. + {BUTTON_DRAW, TXT_OK, true}, +#endif + {BUTTON_RESUME, TXT_RESUME_MISSION, true}, + {BUTTON_RESTATE, TXT_RESTATE_MISSION, false}, + }; + + /* + ** Variables. + */ + TextButtonClass * buttons = 0; + int selection; + bool pressed; +#ifdef FIXIT_VERSION_3 // Stalemate games. + int curbutton = 7; +#else + int curbutton = 6; +#endif + int y; + TextButtonClass * buttonsel[ARRAY_SIZE(_constants)]; + static int num_buttons = sizeof(_constants)/sizeof(_constants[0]); + + + int num_players = 0; + int i; + + // + // Compute the number of real players in the game; only allow saves + // if there are more than 1. + // + for (i = 0; i < Session.Players.Count(); i++) { + if (!(HouseClass::As_Pointer(Session.Players[i]->Player.ID)->IsDefeated)) { + num_players++; + } + } + + + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list for all of the buttons for this dialog. + */ + int maxwidth = 0; + + for (int index = 0; index < num_buttons ; index++ ) { + int text = _constants[index].Text; + buttonsel[index] = NULL; + + if (Session.Type != GAME_NORMAL && !_constants[index].Multiplay) { + continue; + } + + if ( (Session.Type == GAME_SKIRMISH || + Session.Type == GAME_INTERNET) && text == TXT_SAVE_MISSION) { + continue; + } + +#ifdef FIXIT_VERSION_3 + if (Session.Type != GAME_NORMAL && ( num_players < 2 ) && + text == TXT_SAVE_MISSION) { + continue; + } +#else +#ifdef FIXIT_MULTI_SAVE + if (Session.Type != GAME_NORMAL && (num_players < 2 || PlayingAgainstVersion == VERSION_RED_ALERT_104) && + text == TXT_SAVE_MISSION) { + continue; + } +#endif //FIXIT_MULTI_SAVE +#endif + + if (Session.Type == GAME_SKIRMISH && text == TXT_DELETE_MISSION) { + continue; + } + + if (Session.Type != GAME_NORMAL && text == TXT_DELETE_MISSION) { + text = TXT_RESIGN; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if (index < 6) { +#else + if (index < 5) { +#endif + y = (SeenBuff.Get_Height() - OptionHeight)/2 + ButtonY + ((OButtonHeight+2) * index); + } else { + y = OptionY + ButtonResumeY; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + TextButtonClass* g; + if( _constants[index].ID == BUTTON_DRAW ) + { + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 ) + { + if( Scen.bLocalProposesDraw ) + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_RETRACT_DRAW, TPF_BUTTON, 0, y ); + else + continue; // Game will end now anyway. + } + else + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_PROPOSE_DRAW, TPF_BUTTON, 0, y ); + else + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_ACCEPT_DRAW, TPF_BUTTON, 0, y ); + } + } + else + continue; + } + else + g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#else + TextButtonClass * g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#endif + + if (g->Width > maxwidth) { + maxwidth = g->Width; + } + if (buttons == NULL) { + buttons = g; + } else { + g->Add_Tail(*buttons); + } + + buttonsel[index] = g; + } + + /* + ** BG: In skirmish mode, there is no 'restate' button, so we have to + ** backtrack through the list to find the last valid button. + */ + while(!buttonsel[curbutton-1]) curbutton--; + + buttonsel[curbutton-1]->Turn_On(); + + /* + ** Force all button lengths to match the maximum length of the widest button. + */ + GadgetClass * g = buttons; + while (g != NULL) { + g->Width = max(maxwidth, 90 * RESFACTOR); + g->X = OptionX+(OptionWidth-g->Width)/2; + g = g->Get_Next(); + } +//#ifdef FRENCH +// buttonsel[BUTTON_RESUME-1]->Width = 110 * RESFACTOR; +// buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR) - 5; +//#else + buttonsel[BUTTON_RESUME-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR); +//#endif + + if (Session.Type == GAME_NORMAL) { + buttonsel[BUTTON_RESTATE-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESTATE-1]->X = OptionX+OptionWidth-(buttonsel[BUTTON_RESTATE-1]->Width+(17 * RESFACTOR)); + } + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + (new GadgetClass(OptionX, OptionY, OptionWidth, OptionHeight, GadgetClass::LEFTPRESS))->Add_Tail(*buttons); + + /* + ** This cause a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to game button. + */ + (new ControlClass(BUTTON_RESUME, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS))->Add_Tail(*buttons); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display || RedrawOptionsMenu) { + + /* + ** Redraw the map. + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + /* + ** Reset up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OptionX, OptionY, OptionWidth, OptionHeight); + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(OptionX, OptionY, OptionWidth, OptionHeight); + + /* + ** Draw the arrows border if requested. + */ + Draw_Caption(TXT_OPTIONS, OptionX, OptionY, OptionWidth); + + /* + ** Display the version number at the bottom of the dialog box. + */ +#ifndef WIN32 + Fancy_Text_Print("%s\rV%s", + (OptionX+OptionWidth)-(17 * RESFACTOR), + OptionY+OptionHeight-((Session.Type == GAME_NORMAL) ? (32 * RESFACTOR) : (24 * RESFACTOR)), + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Scen.ScenarioName, + Version_Name()); + +#else + Fancy_Text_Print("%s\rV%s", + (OptionX+OptionWidth)-(25 * RESFACTOR), + OptionY+OptionHeight-((Session.Type == GAME_NORMAL) ? (32 * RESFACTOR) : (24 * RESFACTOR)), + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Scen.ScenarioName, + Version_Name()); +#endif + + buttons->Draw_All(); + TabClass::Hilite_Tab(0); + Show_Mouse(); + display = false; + RedrawOptionsMenu = false; + } + + /* + ** Get user input. + */ + KeyNumType input = buttons->Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_RESTATE | KN_BUTTON): + selection = BUTTON_RESTATE; + pressed = true; + break; + + case (BUTTON_LOAD | KN_BUTTON): + selection = BUTTON_LOAD; + pressed = true; + break; + + case (BUTTON_SAVE | KN_BUTTON): + selection = BUTTON_SAVE; + pressed = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + selection = BUTTON_DELETE; + pressed = true; + break; + + case (BUTTON_QUIT | KN_BUTTON): + selection = BUTTON_QUIT; + pressed = true; + break; + + case (BUTTON_GAME | KN_BUTTON): + selection = BUTTON_GAME; + pressed = true; + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case (BUTTON_DRAW | KN_BUTTON): + selection = BUTTON_DRAW; + pressed = true; + break; +#endif + + case (KN_ESC): + case (BUTTON_RESUME | KN_BUTTON): + selection = BUTTON_RESUME; + pressed = true; + break; + + case (KN_UP): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton--; + if (curbutton < 1) curbutton = num_buttons; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton++; + if ( curbutton > num_buttons ) curbutton = 1; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + buttonsel[curbutton-1]->IsPressed = true; + buttonsel[curbutton-1]->Draw_Me(true); + selection = curbutton; + pressed = true; + Keyboard->Clear(); + break; + + default: + break; + } + + if (pressed) { + + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton = selection; + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + + switch (selection) { + case BUTTON_RESTATE: + display = true; + if (!Restate_Mission(Scen.ScenarioName, TXT_VIDEO, TXT_RESUME_MISSION/*KOTXT_OPTIONS*/)) { + BreakoutAllowed = true; + Play_Movie(Scen.BriefMovie); + + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + + Map.Flag_To_Redraw(true); + Theme.Queue_Song(THEME_PICK_ANOTHER); + process = false; + } else { + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + process = false; + } + break; + + case (BUTTON_LOAD): + display = true; + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + process = false; + } + break; + + case (BUTTON_SAVE): + display = true; + if (Session.Type == GAME_NORMAL) { + LoadOptionsClass(LoadOptionsClass::SAVE).Process(); + + } else { + OutList.Add(EventClass(EventClass::SAVEGAME)); + process = false; + } + break; + + case (BUTTON_DELETE): + display = true; + if (Session.Type != GAME_NORMAL) { + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + process = false; + } else { + LoadOptionsClass(LoadOptionsClass::WWDELETE).Process(); + } + break; + + case (BUTTON_QUIT): + if (Session.Type == GAME_NORMAL) { + switch (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_ABORT, TXT_CANCEL, TXT_RESTART)) { + case 1: + display = true; + break; + + case 0: + process = false; + Queue_Exit(); + break; + + case 2: + PlayerRestarts = true; + process = false; + break; + } + } else { + if (Surrender_Dialog(TXT_CONFIRM_EXIT)) { + process = false; + Queue_Exit(); + } else { + display = true; + } + //if (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_YES, TXT_NO) == 0) { + //process = false; + //Queue_Exit(); + //} else { + //display = true; + //} + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case BUTTON_DRAW: + if( Scen.bLocalProposesDraw ) + { + // Retract draw offer. + OutList.Add(EventClass(EventClass::RETRACT_DRAW)); + process = false; + } + else + { + if( !Scen.bOtherProposesDraw ) + { + // Propose a draw? + if( Surrender_Dialog( TXT_WOL_PROPOSE_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + else + { + // Accept a draw? + if( Surrender_Dialog( TXT_WOL_ACCEPT_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + } + break; +#endif + + case (BUTTON_GAME): + display = true; + GameControlsClass().Process(); + break; + + case (BUTTON_RESUME): + Save_Settings(); + process = false; + display = true; + break; + } + + pressed = false; + buttonsel[curbutton-1]->IsPressed = false; + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + } + } + + /* + ** Clean up and re-enter the game. + */ + buttons->Delete_List(); + + /* + ** Redraw the map. + */ + Keyboard->Clear(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + +void GameOptionsClass::Adjust_Variables_For_Resolution(void) +{ + OptionWidth = (216+8) * RESFACTOR; +#ifdef FIXIT_VERSION_3 // Stalemate games. + OptionHeight = 111 * RESFACTOR; +#else + OptionHeight = 100 * RESFACTOR; +#endif + OptionX = ((SeenBuff.Get_Width() - OptionWidth) / 2); + OptionY = ((SeenBuff.Get_Height() - OptionHeight) / 2); + ButtonWidth = 130 * RESFACTOR; + OButtonHeight = 9 * RESFACTOR; + CaptionYPos = 5 * RESFACTOR; + ButtonY = 21 * RESFACTOR; + Border1Len = 72 * RESFACTOR; + Border2Len = 16 * RESFACTOR; + ButtonResumeY = (OptionHeight - (19 * RESFACTOR)); +} diff --git a/CODE/GOPTIONS.H b/CODE/GOPTIONS.H new file mode 100644 index 0000000..e448191 --- /dev/null +++ b/CODE/GOPTIONS.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GOPTIONS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GOPTIONS_H +#define GOPTIONS_H + +#include "options.h" +#include "gadget.h" + + +class GameOptionsClass : public OptionsClass { + enum GameOptionsButtonEnum { + BUTTON_LOAD=1, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_GAME, + BUTTON_QUIT, +#ifdef FIXIT_VERSION_3 // Stalemate games. + BUTTON_DRAW, +#endif + BUTTON_RESUME, + BUTTON_RESTATE, + + BUTTON_COUNT + }; + + enum GameOptionsEnum { + OPTION_WIDTH=(216+8), + OPTION_HEIGHT=100, + OPTION_X=((320 - (216+8)) / 2), + OPTION_Y=((200 - 100) / 2), +#ifdef FRENCH + BUTTON_WIDTH=142, +#else + BUTTON_WIDTH=130, +#endif + NUMBER_OF_BUTTONS=6, // ajw Not used. + CAPTION_Y_POS=5, + BUTTON_Y=21, + BORDER1_LEN=72, + BORDER2_LEN=16, + BUTTON_RESUME_Y=(100-15) + }; + + public: + GameOptionsClass(void): OptionsClass () { }; + void Adjust_Variables_For_Resolution(void); + void Process(void); + + private: + int OptionWidth; + int OptionHeight; + int OptionX; + int OptionY; + int ButtonWidth; + int OButtonHeight; + int CaptionYPos; + int ButtonY; + int Border1Len; + int Border2Len; + int ButtonResumeY; + +}; + +#endif diff --git a/CODE/GSCREEN.CPP b/CODE/GSCREEN.CPP new file mode 100644 index 0000000..5b7eef9 --- /dev/null +++ b/CODE/GSCREEN.CPP @@ -0,0 +1,487 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GSCREEN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GSCREEN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * GScreenClass::Input -- Fetches input and processes gadgets. * + * GScreenClass::One_Time -- Handles one time class setups. * + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +GadgetClass * GScreenClass::Buttons = 0; + +GraphicBufferClass * GScreenClass::ShadowPage = 0; + + +/*********************************************************************************************** + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * * + * This constructor merely sets the display system, so that it will redraw the first time * + * the render function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +GScreenClass::GScreenClass(void) +{ + IsToUpdate = true; + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::One_Time -- Handles one time class setups. * + * * + * This routine (and all those that overload it) must perform truly one-time initialization. * + * Such init's would normally be done in the constructor, but other aspects of the game may * + * not have been initialized at the time the constructors are called (such as the file system, * + * the display, or other WWLIB subsystems), so many initializations should be deferred to the * + * One_Time init's. * + * * + * Any variables set in this routine should be declared as static, so they won't be modified * + * by the load/save process. Non-static variables will be over-written by a loaded game. * + * * + * This function allocates the shadow buffer that is used for quick screen updates. If * + * there were any data files to load, they would be loaded at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::One_Time(void) +{ + /* + ** Allocate the screen shadow page. This page is used to reduce access to the + ** actual screen memory. It contains a duplicate of what the SEENPAGE is. + */ + Buttons = 0; + ShadowPage = new GraphicBufferClass(320, 200); + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } +} + + +/*********************************************************************************************** + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * * + * This routine shouldn't be overloaded. It's the main map initialization routine, and will * + * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * + * routine results in calling every initialization routine in the entire map hierarchy. * + * * + * INPUT: * + * theater theater to initialize to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init(TheaterType theater) +{ + Init_Clear(); + Init_IO(); + Init_Theater(theater); +} + + +/*********************************************************************************************** + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * * + * This routine (and those that overload it) clears any buffers and variables to a known * + * state. It assumes that all buffers are allocated & valid. The map should be displayable * + * after calling this function, and should draw basically an empty display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Clear(void) +{ + /* + ** Clear the ShadowPage & HidPage to force a complete shadow blit. + */ + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } + + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * * + * This routine (and those that overload it) performs any theater-specific initializations * + * needed. This will include setting the palette, setting up remap tables, etc. This routine * + * only needs to be called when the theater has changed. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Theater(TheaterType ) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_IO(void) +{ + /* + ** Reset the button list. This means that any other elements of the map that need + ** buttons must attach them after this routine is called! + */ + Buttons = 0; +} + + +/*********************************************************************************************** + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * * + * This function is used to flag the display system whether any rendering is needed. The * + * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * + * has changed and the individual Draw_It functions must be called. When a sub system * + * determines that it needs to render something local to itself, it would call this routine * + * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * + * this routine will be called with a true parameter. * + * * + * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * + * * + * OUTPUT: none * + * * + * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * + * Render() function is called, the appropriate drawing steps will be performed. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Flag_To_Redraw(bool complete) +{ + IsToUpdate = true; + if (complete) { + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * GScreenClass::Input -- Fetches input and processes gadgets. * + * * + * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * + * system. * + * * + * INPUT: key -- Reference to the key code (for future examination). * + * * + * x,y -- Reference to mouse coordinates (for future examination). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Input(KeyNumType & key, int & x, int & y) +{ + key = Keyboard->Check(); + + x = Keyboard->Mouse_X(); + y = Keyboard->Mouse_Y(); + + if (Buttons != NULL) { + + /* + ** If any buttons need redrawing, they will do so in the Input routine, and + ** they should draw themselves to the HidPage. So, flag ourselves for a Blit + ** to show the newly drawn buttons. + */ + if (Buttons->Is_List_To_Redraw()) { + Flag_To_Redraw(false); + } + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); +#endif + + key = Buttons->Input(); + + Set_Logic_Page(oldpage); + + } else { + + if (key != 0) { + key = Keyboard->Get(); + } + } + + AI(key, x, y); +} + + +/*********************************************************************************************** + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * * + * This will add a gadget to the game input system. The gadget will be processed in * + * subsequent calls to the GScreenClass::Input() function. * + * * + * INPUT: gadget -- Reference to the gadget that will be added to the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Add_A_Button(GadgetClass & gadget) +{ + /* + ** If this gadget is already in the list, remove it before adding it in: + ** - If 1st gadget in list, use Remove_A_Button to remove it, to reset the + ** value of 'Buttons' appropriately + ** - Otherwise, just call the Remove function for that gadget to remove it + ** from any list it may be in + */ + if (Buttons == &gadget) { + Remove_A_Button(gadget); + } else { + gadget.Remove(); + } + + /* + ** Now add the gadget to our list: + ** - If there are not buttons, start the list with this one + ** - Otherwise, add it to the tail of the existing list + */ + if (Buttons) { + gadget.Add_Tail(*Buttons); + } else { + Buttons = &gadget; + } +} + + +/*********************************************************************************************** + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * * + * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * + * will be invalid! * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Remove_A_Button(GadgetClass & gadget) +{ + Buttons = gadget.Remove(); +} + + +/*********************************************************************************************** + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * * + * This routine should be called in the main game loop (once every game frame). It will * + * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * + * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * + * copied to the visible page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This actually updates the graphic display. As a result it can take quite a * + * while to perform. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Render(void) +{ + //This is unnessasary surely? ST - 10/16/96 2:30PM + //if (Buttons && Buttons->Is_List_To_Redraw()) { + // IsToRedraw = true; + //} + + + if (IsToUpdate || IsToRedraw) { + BStart(BENCH_GSCREEN_RENDER); + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + + if (IsToRedraw) { + Hide_Mouse(); + SeenPage.To_Buffer(0, 0, 320, 200, ShadowPage); + Show_Mouse(); + } +#endif + + Draw_It(IsToRedraw); + + if (Buttons) Buttons->Draw_All(false); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the Editor's buttons + */ + if (Debug_Map) { + if (Buttons) { + Buttons->Draw_All(); + } + } +#endif + /* + ** Draw the multiplayer message system to the Hidpage at this point. + ** This way, they'll Blit along with the rest of the map. + */ + if (Session.Messages.Num_Messages() > 0) { + Session.Messages.Set_Width( + Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); + } + Session.Messages.Draw(); + + Blit_Display(); + IsToUpdate = false; + IsToRedraw = false; + + BEnd(BENCH_GSCREEN_RENDER); + Set_Logic_Page(oldpage); + } +} + + +/*********************************************************************************************** + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * * + * This routine is used to copy the correct display from the HIDPAGE * + * to the SEENPAGE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + *=============================================================================================*/ +extern "C" { + void ModeX_Blit (GraphicBufferClass * source); +} + +void GScreenClass::Blit_Display(void) +{ + BStart(BENCH_BLIT_DISPLAY); + #ifdef WIN32 + if (SeenBuff.Get_Width()!=320) { + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); + WWMouse->Erase_Mouse(&HidPage, FALSE); + } else { + ModeX_Blit(&HiddenPage); + } + #else + Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ShadowPage->Get_Buffer()); + #endif + BEnd(BENCH_BLIT_DISPLAY); +} + + diff --git a/CODE/GSCREEN.H b/CODE/GSCREEN.H new file mode 100644 index 0000000..d897626 --- /dev/null +++ b/CODE/GSCREEN.H @@ -0,0 +1,134 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/GSCREEN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : GSCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GSCREEN_H +#define GSCREEN_H + +#include "function.h" +#include "cell.h" + +class GScreenClass +{ + public: + + GScreenClass(void); + GScreenClass(NoInitClass const &) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time initializations + virtual void Init(TheaterType = THEATER_NONE); // Inits everything + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** Player I/O is routed through here. It is called every game tick. + */ + virtual void Input(KeyNumType & key, int & x, int & y); + virtual void AI(KeyNumType &, int, int) {}; + virtual void Add_A_Button(GadgetClass & gadget); + virtual void Remove_A_Button(GadgetClass & gadget); + + /* + ** Called when map needs complete updating. + */ + virtual void Flag_To_Redraw(bool complete=false); + + /* + ** Render maintenance routine (call every game tick). Probably no need + ** to override this in derived classes. + */ + virtual void Render(void); + + /* + ** Is called when actual drawing is required. This is the function to + ** override in derived classes. + */ + virtual void Draw_It(bool =false) {}; + + /* + ** This moves the hidpage up to the seenpage. + */ + virtual void Blit_Display(void); + + /* + ** Changes the mouse shape as indicated. + */ + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall) = 0; + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall) = 0; + virtual void Revert_Mouse_Shape(void) = 0; + virtual void Mouse_Small(bool wsmall) = 0; + + /* + ** Misc routines. + */ + virtual void * Shadow_Address(void) {return(ShadowPage);}; + + /* + ** This points to the buttons that are used for input. All of the derived classes will + ** attached their specific buttons to this list. + */ + static GadgetClass * Buttons; + + private: + + /* + ** If the entire map is required to redraw, then this flag is true. This flag + ** is set by the Flag_To_Redraw function. Typically, this occurs when the screen + ** has been trashed or is first created. + */ + unsigned IsToRedraw:1; + + /* + ** If only a sub-system of the map must be redrawn, then this flag will be set. + ** An example of something that would set this flag would be an animating icon + ** in the sidebar. In such a case, complete redrawing of the entire display is not + ** necessary, but the Draw_It function should still be called so that the appropriate + ** class can perform it's rendering. + */ + unsigned IsToUpdate:1; + + /* + ** Pointer to an exact copy of the visible graphic page. This copy is used to speed + ** display rendering by using an only-update-changed-pixels algorithm. + */ + static GraphicBufferClass * ShadowPage; +}; + +#endif diff --git a/CODE/HDATA.CPP b/CODE/HDATA.CPP new file mode 100644 index 0000000..e31f94f --- /dev/null +++ b/CODE/HDATA.CPP @@ -0,0 +1,526 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 22, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * HouseTypeClass::One_Time -- One-time initialization * + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static HouseTypeClass const HouseEngland( + HOUSE_ENGLAND, + "England", // NAME: House name. + TXT_ENGLAND, // FULLNAME: Translated house name. + "ENG", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'E' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGermany( + HOUSE_GERMANY, + "Germany", // NAME: House name. + TXT_GERMANY, // FULLNAME: Translated house name. + "GER", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseFrance( + HOUSE_FRANCE, + "France", // NAME: House name. + TXT_FRANCE, // FULLNAME: Translated house name. + "FRA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'F' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUkraine( + HOUSE_UKRAINE, + "Ukraine", // NAME: House name. + TXT_UKRAINE, // FULLNAME: Translated house name. + "UKA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'K' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUSSR( + HOUSE_USSR, + "USSR", // NAME: House name. + TXT_USSR, // FULLNAME: Translated house name. + "RED", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'U' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGreece( + HOUSE_GREECE, + "Greece", // NAME: House name. + TXT_GREECE, // FULLNAME: Translated house name. + "GRE", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseTurkey( + HOUSE_TURKEY, + "Turkey", // NAME: House name. + TXT_TURKEY, // FULLNAME: Translated house name. + "TRK", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'T' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseSpain( + HOUSE_SPAIN, + "Spain", // NAME: House name. + TXT_SPAIN, // FULLNAME: Translated house name. + "SPN", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'S' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGood( + HOUSE_GOOD, + "GoodGuy", // NAME: House name. + TXT_GOODGUY, // FULLNAME: Translated house name. + "GDI", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseBad( + HOUSE_BAD, + "BadGuy", // NAME: House name. + TXT_BADGUY, // FULLNAME: Translated house name. + "NOD", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'B' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseCivilian( + HOUSE_NEUTRAL, + "Neutral", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "CIV", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'C' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseJP( + HOUSE_JP, + "Special", // NAME: House name. + TXT_JP, // FULLNAME: Translated house name. + "JP", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'J' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti1( + HOUSE_MULTI1, + "Multi1", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP1", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti2( + HOUSE_MULTI2, + "Multi2", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP2", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti3( + HOUSE_MULTI3, + "Multi3", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP3", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti4( + HOUSE_MULTI4, + "Multi4", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP4", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti5( + HOUSE_MULTI5, + "Multi5", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP5", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti6( + HOUSE_MULTI6, + "Multi6", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP6", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti7( + HOUSE_MULTI7, + "Multi7", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP7", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti8( + HOUSE_MULTI8, + "Multi8", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP8", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +#ifdef OBSOLETE +HouseTypeClass const * const HouseTypeClass::Pointers[HOUSE_COUNT] = { + &HouseSpain, + &HouseGreece, + &HouseUSSR, + &HouseEngland, + &HouseUkraine, + &HouseGermany, + &HouseFrance, + &HouseTurkey, + &HouseGood, + &HouseBad, + &HouseCivilian, + &HouseJP, + &HouseMulti1, + &HouseMulti2, + &HouseMulti3, + &HouseMulti4, + &HouseMulti5, + &HouseMulti6, + &HouseMulti7, + &HouseMulti8, +}; +#endif + + +/*********************************************************************************************** + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * * + * This is the constructor for house type objects. This object holds the constant data * + * for the house type. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass::HouseTypeClass( + HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix) : + AbstractTypeClass(RTTI_HOUSETYPE, house, fullname, ini), +// RTTI(RTTI_HOUSETYPE), +// ID(house), + House(house), +// IniName(ini), +// FullName(fullname), + Lemon(lemon), + RemapColor(remapcolor), + Prefix(prefix), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1) +{ + strncpy(Suffix, ext, 3); + Suffix[3] = '\0'; +} + + +/*********************************************************************************************** + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * * + * This will allocate a house type object from the special heap that is used to maintain * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the newly allocated house type object. * + * * + * WARNINGS: If there is insufficient room, this routine may return NULL. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void * HouseTypeClass::operator new(size_t) +{ + return(HouseTypes.Alloc()); +} + + +/*********************************************************************************************** + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * * + * This will return the house type object specified back into the special heap that * + * is used to maintain house type objects. * + * * + * INPUT: ptr -- Pointer to the house type object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::operator delete(void * ptr) +{ + HouseTypes.Free((HouseTypeClass *)ptr); +} + + +/*********************************************************************************************** + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * * + * This will preallocate all the house types. They must be allocated in a particular order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called only once at the beginning of the game. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::Init_Heap(void) +{ + /* + ** These house type class objects must be allocated in the exact order that they + ** are specified in the HousesType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new HouseTypeClass(HouseSpain); + new HouseTypeClass(HouseGreece); + new HouseTypeClass(HouseUSSR); + new HouseTypeClass(HouseEngland); + new HouseTypeClass(HouseUkraine); + new HouseTypeClass(HouseGermany); + new HouseTypeClass(HouseFrance); + new HouseTypeClass(HouseTurkey); + new HouseTypeClass(HouseGood); + new HouseTypeClass(HouseBad); + new HouseTypeClass(HouseCivilian); + new HouseTypeClass(HouseJP); + new HouseTypeClass(HouseMulti1); + new HouseTypeClass(HouseMulti2); + new HouseTypeClass(HouseMulti3); + new HouseTypeClass(HouseMulti4); + new HouseTypeClass(HouseMulti5); + new HouseTypeClass(HouseMulti6); + new HouseTypeClass(HouseMulti7); + new HouseTypeClass(HouseMulti8); +} + + +/*********************************************************************************************** + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * * + * This routine will convert the ASCII house name specified into a * + * real house number. Typically, this is used when processing a * + * scenario INI file. * + * * + * INPUT: name -- ASCII name of house to process. * + * * + * OUTPUT: Returns with actual house number represented by the ASCII * + * name specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +HousesType HouseTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (stricmp(As_Reference(house).IniName, name) == 0) { +// if (stricmp(Pointers[house]->IniName, name) == 0) { + return(house); + } + } + } + return(HOUSE_NONE); +} + + +/*********************************************************************************************** + * HouseTypeClass::One_Time -- One-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/21/1994 JLB : Converted to member function. * + * 06/19/1996 JLB : Converted to regular heap class management. * + *=============================================================================================*/ +void HouseTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * * + * Use this routine to fetch a reference to the house number specified. * + * * + * INPUT: house -- The house number (HousesType) to look up. * + * * + * OUTPUT: Returns with a reference to the HouseTypeClass object that matches the house * + * number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass & HouseTypeClass::As_Reference(HousesType house) +{ + return(*HouseTypes.Ptr(house)); +} + + +/*********************************************************************************************** + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * * + * Use this routine to fetch the remap table assigned to this house. The remap table is * + * what gives the house's units/buildings their distinctive color. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +unsigned char const * HouseTypeClass::Remap_Table(void) const +{ + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * * + * This routine will fetch the rules controllable values for the house type from the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to fetch the house control values from. * + * * + * OUTPUT: bool; Was the house section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + FirepowerBias = ini.Get_Fixed(Name(), "Firepower", FirepowerBias); + GroundspeedBias = ini.Get_Fixed(Name(), "Groundspeed", GroundspeedBias); + AirspeedBias = ini.Get_Fixed(Name(), "Airspeed", AirspeedBias); + ArmorBias = ini.Get_Fixed(Name(), "Armor", ArmorBias); + ROFBias = ini.Get_Fixed(Name(), "ROF", ROFBias); + CostBias = ini.Get_Fixed(Name(), "Cost", CostBias); + BuildSpeedBias = ini.Get_Fixed(Name(), "BuildTime", BuildSpeedBias); + return(true); + } + return(false); +} diff --git a/CODE/HEADER.MAC b/CODE/HEADER.MAC new file mode 100644 index 0000000..ea443db --- /dev/null +++ b/CODE/HEADER.MAC @@ -0,0 +1,20 @@ +%home%tof + +%home%tof/* $Header:$ */ +/*********************************************************************************************** + ** 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 : %qProject Name$%col96* + * * + * File Name : %-r%-e%col96* + * * + * Programmer : %eProgrammer$%col96* + * * + * Start Date : %date%col96* + * * + * Last Update : %date%col96* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ diff --git a/CODE/HEAP.CPP b/CODE/HEAP.CPP new file mode 100644 index 0000000..327a370 --- /dev/null +++ b/CODE/HEAP.CPP @@ -0,0 +1,632 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HEAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HEAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : May 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * FixedIHeapClass::Free -- Frees an object in the heap. * + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * TFixedIHeapClass::Load -- Loads all active objects * + * TFixedIHeapClass::Save -- Saves all active objects * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "heap.h" +#include +#include +#include +#include +#include + + +/*********************************************************************************************** + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * * + * This is the normal constructor used for the heap manager class. This initializes * + * the class but doesn't yet assign actual heap memory to this manager. That is handled * + * by the Set_Heap() function. * + * * + * INPUT: size -- The size of the individual sub-blocks in this heap. This value is * + * typically the size of some class or structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: The heap must first be assigned a block of memory to manage before it can * + * be used. * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::FixedHeapClass(int size) : + IsAllocated(false), + Size(size), + TotalCount(0), + ActiveCount(0), + Buffer(0) +{ +} + + +/*********************************************************************************************** + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * * + * This is the default constructor for the heap manager class. It handles freeing the * + * memory assigned to this heap. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::~FixedHeapClass(void) +{ + FixedHeapClass::Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * * + * This routine is used to assign a memory heap to this object. A memory heap so assigned * + * will start with all sub-blocks unallocated. After this routine is called, normal * + * allocation and freeing may occur. This routine will allocate necessary memory if the * + * buffer parameter is NULL. * + * * + * INPUT: count -- The number of objects that this heap should manage. * + * * + * buffer -- Pointer to pre-allocated buffer that this manager will use. If this * + * parameter is NULL, then memory will be automatically allocated. * + * * + * OUTPUT: bool; Was the heap successfully initialized? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Set_Heap(int count, void * buffer) +{ + /* + ** Clear out the old heap data. + */ + Clear(); + + /* + ** If there is no size to the objects in the heap, then this block memory + ** handler can NEVER function. Return with a failure condition. + */ + if (!Size) return(false); + + /* + ** If there is no count specified, then this indicates that the heap should + ** be disabled. + */ + if (!count) return(true); + + /* + ** Initialize the free boolean vector and the buffer for the actual + ** allocation objects. + */ + if (FreeFlag.Resize(count)) { + if (!buffer) { + buffer = new char[count * Size]; + if (!buffer) { + FreeFlag.Clear(); + return(false); + } + IsAllocated = true; + } + Buffer = buffer; + TotalCount = count; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * * + * Finds the first available sub-block in the heap and returns a pointer to it. The sub- * + * block is marked as allocated by this routine. If there are no more sub-blocks * + * available, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated sub-block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedHeapClass::Allocate(void) +{ + if (ActiveCount < TotalCount) { + int index = FreeFlag.First_False(); + + if (index != -1) { + ActiveCount++; + FreeFlag[index] = true; + return((*this)[index]); + } + } + return(0); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * * + * Use this routine to free a previously allocated sub-block in the heap. * + * * + * INPUT: pointer -- A pointer to the sub-block to free. This is the same pointer that * + * was returned from the Allocate() function. * + * * + * OUTPUT: bool; Was the deallocation successful? Failure could indicate a pointer that * + * doesn't refer to this heap or a null pointer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free(void * pointer) +{ + if (pointer && ActiveCount) { + int index = ID(pointer); + + if ((unsigned)index < TotalCount) { + if (FreeFlag[index]) { + ActiveCount--; + FreeFlag[index] = false; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * * + * Use this routine to convert a pointer (returned by Allocate) into the sub-block * + * index number. This index number can be used as a form of identifier for the block. * + * * + * INPUT: pointer -- A pointer to the sub-block to convert into an ID number. * + * * + * OUTPUT: Returns with the index (ID) number for the sub-block specified. This number will * + * range between 0 and the sub-block max -1. If -1 is returned, then the pointer * + * was invalid. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::ID(void const * pointer) const +{ + if (pointer && Size) { + return((int)(((char *)pointer - (char *)Buffer) / Size)); + } + return(-1); +} + + +/*********************************************************************************************** + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * * + * This routine is used to bring the heap manager back into a non-functioning state. All * + * memory allocated by this manager is freed. Any previous pointers to allocated blocks * + * from this heap are now invalid. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedHeapClass::Clear(void) +{ + /* + ** Free the old buffer (if present). + */ + if (Buffer && IsAllocated) { + delete[] Buffer; + } + Buffer = 0; + IsAllocated = false; + ActiveCount = 0; + TotalCount = 0; + FreeFlag.Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * * + * This routine will free all previously allocated objects out of the heap. Use this * + * routine to ensure that the heap is empty. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of all objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free_All(void) +{ + ActiveCount = 0; + FreeFlag.Reset(); + return(true); +} + + +///////////////////////////////////////////////////////////////////// + + +/*********************************************************************************************** + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * * + * Use this routine to free all previously allocated objects in the heap. This routine will * + * also clear out the allocated object vector as well. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free_All(void) +{ + ActivePointers.Delete_All(); + return(FixedHeapClass::Free_All()); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * * + * This routine will clear the entire heap. All memory that was allocation, will be freed * + * by this routine. After calling this routine, the heap must either be resized or * + * a new heap memory block specifically attached, before it can be used again. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedIHeapClass::Clear(void) +{ + FixedHeapClass::Clear(); + ActivePointers.Clear(); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * * + * This routine will set the heap to use the buffer specified. Use this routine when a * + * pre-allocated buffer is to be used for the heap. A heap that is assigned in this * + * manner cannot be resized. * + * * + * INPUT: count -- The number of objects that the buffer pointer can be used to track. * + * * + * buffer -- Pointer to the buffer to use when keeping track of the objects. * + * * + * OUTPUT: Was the heap assigned successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Set_Heap(int count, void * buffer) +{ + Clear(); + if (FixedHeapClass::Set_Heap(count, buffer)) { + ActivePointers.Resize(count); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * * + * This routine will allocate an object located in the heap. If no free object space * + * could be found, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated object memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedIHeapClass::Allocate(void) +{ + void * ptr = FixedHeapClass::Allocate(); + if (ptr) { + ActivePointers.Add(ptr); + memset (ptr, 0, Size); + } + return(ptr); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Free -- Frees an object in the heap. * + * * + * This routine is used to free an object in the heap. Freeing is accomplished by marking * + * the object's memory as free to be reallocated. The object is also removed from the * + * allocated object pointer vector. * + * * + * INPUT: pointer -- Pointer to the object that is to be removed from the heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free(void * pointer) +{ + if (FixedHeapClass::Free(pointer)) { + ActivePointers.Delete(pointer); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * * + * Ths logical ID number of a memory block is the index number of the block as if the * + * heap consisted only of valid allocated blocks. This knowledge comes in handy when * + * the real index number must be anticipated before a memory block packing process. * + * * + * INPUT: pointer -- Pointer to an allocated block in the heap. * + * * + * OUTPUT: Returns with the logical index number of this block. The number returned must not * + * be used as a regular index into the heap until such time as the heap has been * + * compacted (by some means or another) without modifying the block order. * + * * + * WARNINGS: Runs in linear time. * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Logical_ID(void const * pointer) const +{ + if (pointer != NULL) { + for (int index = 0; index < Count(); index++) { + if (Active_Ptr(index) == pointer) { + return(index); + } + } + } + return(-1); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + * 03/12/1996 JLB : Uses in-place new operator for virtual table control. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(Pipe & file) const +{ + /* + ** Save the number of instances of this class + */ + file.Put(&ActiveCount, sizeof(ActiveCount)); + + /* + ** Save each instance of this class + */ + for (int i = 0; i < ActiveCount; i++) { + + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + int idx = ID(Ptr(i)); + file.Put(&idx, sizeof(idx)); + + /* + ** Save the object itself + */ + file.Put(Ptr(i), sizeof(T)); + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(Straw & file) +{ + int i; // loop counter + int idx; // object index + T * ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Get(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Get(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + file.Get(ptr, sizeof(T)); + new(ptr) T(NoInitClass()); +// if (!ptr->Load(file)) { +// return(false); +// } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} diff --git a/CODE/HEAP.H b/CODE/HEAP.H new file mode 100644 index 0000000..1de1e18 --- /dev/null +++ b/CODE/HEAP.H @@ -0,0 +1,198 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HEAP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HEAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : February 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HEAP_H +#define HEAP_H + +#include "vector.h" + +/************************************************************************** +** This is a block memory management handler. It is used when memory is to +** be treated as a series of blocks of fixed size. This is similar to an +** array of integral types, but unlike such an array, the memory blocks +** are anonymously. This facilitates the use of this class when overloading +** the new and delete operators for a normal class object. +*/ +class FixedHeapClass +{ + public: + FixedHeapClass(int size); + virtual ~FixedHeapClass(void); + + int Count(void) const {return ActiveCount;}; + int Length(void) const {return TotalCount;}; + int Avail(void) const {return TotalCount-ActiveCount;}; + + virtual int ID(void const * pointer) const; + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + void * operator[](int index) {return ((char *)Buffer) + (index * Size);}; + void const * operator[](int index) const {return ((char *)Buffer) + (index * Size);}; + + protected: + /* + ** If the memory block buffer was allocated by this class, then this flag + ** will be true. The block must be deallocated by this class if true. + */ + unsigned IsAllocated:1; + + /* + ** This is the size of each sub-block within the buffer. + */ + int Size; + + /* + ** This records the absolute number of sub-blocks in the buffer. + */ + int TotalCount; + + /* + ** This is the total blocks allocated out of the heap. This number + ** will never exceed Count. + */ + int ActiveCount; + + /* + ** Pointer to the heap's memory buffer. + */ + void * Buffer; + + /* + ** This is a boolean vector array of allocation flag bits. + */ + BooleanVectorClass FreeFlag; + + private: + // The assignment operator is not supported. + FixedHeapClass & operator = (FixedHeapClass const &); + + // The copy constructor is not supported. + FixedHeapClass(FixedHeapClass const &); +}; + + +/************************************************************************** +** This template serves only as an interface to the heap manager class. By +** using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedHeapClass : public FixedHeapClass +{ + public: + TFixedHeapClass(void) : FixedHeapClass(sizeof(T)) {}; + virtual ~TFixedHeapClass(void) {}; + + + virtual int ID(T const * pointer) const {return FixedHeapClass::ID(pointer);}; + virtual T * Alloc(void) {return (T*)FixedHeapClass::Allocate();}; + virtual int Free(T * pointer) {return(FixedHeapClass::Free(pointer));}; + + T & operator[](int index) {return *(T *)(((char *)Buffer) + (index * Size));}; + T const & operator[](int index) const {return *(T*)(((char *)Buffer) + (index * Size));}; +}; + + +/************************************************************************** +** This is a derivative of the fixed heap class. This class adds the +** ability to quickly iterate through the active (allocated) objects. Since the +** active array is a sequence of pointers, the overhead of this class +** is 4 bytes per potential allocated object (be warned). +*/ +class FixedIHeapClass : public FixedHeapClass +{ + public: + FixedIHeapClass(int size) : FixedHeapClass(size) {}; + virtual ~FixedIHeapClass(void) {}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + virtual int Logical_ID(void const * pointer) const; + virtual int Logical_ID(int id) const {return(Logical_ID((*this)[id]));} + + virtual void * Active_Ptr(int index) {return ActivePointers[index];}; + virtual void const * Active_Ptr(int index) const {return ActivePointers[index];}; + + /* + ** This is an array of pointers to allocated objects. Using this array + ** to control iteration through the objects ensures a minimum of processing. + ** It also allows access to this array so that custom sorting can be + ** performed. + */ + DynamicVectorClass ActivePointers; +}; + + +/************************************************************************** +** This template serves only as an interface to the iteratable heap manager +** class. By using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +class FileClass; +template +class TFixedIHeapClass : public FixedIHeapClass +{ + public: + TFixedIHeapClass(void) : FixedIHeapClass(sizeof(T)) {}; + virtual ~TFixedIHeapClass(void) {}; + + virtual int ID(T const * pointer) const {return FixedIHeapClass::ID(pointer);}; + virtual int Logical_ID(T const * pointer) const {return(FixedIHeapClass::Logical_ID(pointer));} + virtual int Logical_ID(int id) const {return(FixedIHeapClass::Logical_ID(id));} + virtual T * Alloc(void) {return (T*)FixedIHeapClass::Allocate();}; + virtual int Free(T * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Free(void * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Save(Pipe & file) const; + virtual int Load(Straw & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual T * Ptr(int index) const {return (T*)FixedIHeapClass::ActivePointers[index];}; + virtual T * Raw_Ptr(int index) {return (T*)((*this)[index]);}; +}; + + +#endif + + diff --git a/CODE/HELP.CPP b/CODE/HELP.CPP new file mode 100644 index 0000000..125323f --- /dev/null +++ b/CODE/HELP.CPP @@ -0,0 +1,416 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HELP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HELP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * HelpClass::HelpClass -- Default constructor for the help processor. * + * HelpClass::Help_AI -- Handles the help text logic. * + * HelpClass::Help_Text -- Assigns text as the current help text. * + * HelpClass::Init_Clear -- Sets help system to a known state. * + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the holding buffer for the text overlap list. This buffer must be in the near +** data segment. It will be filled in by the Set_Text() function. +*/ +short const HelpClass::OverlapList[60] = { + REFRESH_EOL +}; + +char const * HelpClass::HelpText; + + +/*********************************************************************************************** + * HelpClass::HelpClass -- Default constructor for the help processor. * + * * + * The help processor is initialized by this routine. It merely sets up the help engine * + * to the default state. The default state will not display any help text. Call the * + * Help_Text() function to enable help processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +HelpClass::HelpClass(void) : + HelpX(0), + HelpY(0), + HelpWidth(0), + IsRight(false), + Cost(0), + X(0), + Y(0), + DrawX(0), + DrawY(0), + Width(0), + Text(TXT_NONE), + Color(LTGREY), + CountDownTimer(0) +{ +} + + +/*********************************************************************************************** + * HelpClass::Init_Clear -- Sets help system to a known state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Init_Clear(void) +{ + TabClass::Init_Clear(); + + Set_Text(TXT_NONE); +} + + +/*********************************************************************************************** + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * * + * Use this routine to fetch an offset list for the cells under the text displayed. If * + * there is no text displayed, then the list will consist of just the terminator code. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the offset list for the help text overlap. The offset * + * list is based on the tactical map upper left corner cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +short const * HelpClass::Overlap_List(void) const +{ + if (Text == TXT_NONE || CountDownTimer) { + ((short &)(OverlapList[0])) = REFRESH_EOL; + } + return(OverlapList); +} + + +/*********************************************************************************************** + * HelpClass::Help_AI -- Handles the help text logic. * + * * + * This routine handles tracking the mouse position to see if the mouse remains stationary * + * for the required amount of time. If the time requirement has been met, then it flags * + * the help system to display the help text the next time the Draw_Help() function is * + * called. * + * * + * INPUT: key -- Keyboard input code. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called once and only once per game frame (15 times per * + * second). * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinates as passed in. * + *=============================================================================================*/ +void HelpClass::AI(KeyNumType &key, int x, int y) +{ + if (!CountDownTimer && !IsRight && (x != X || y != Y)) { + Help_Text(TXT_NONE); + } + + /* + ** Process the countdown timer only if it hasn't already expired and there is + ** a real help text message to display. + */ + if (CountDownTimer && !HelpText && Text != TXT_NONE) { + + /* + ** If the mouse has moved, then reset the timer since a moving mouse is not + ** supposed to bring up the help text. + */ + if (!IsRight && (X != x || Y != y)) { + X = x; + Y = y; + CountDownTimer = HELP_DELAY; + Help_Text(TXT_NONE); + } else { + + /* + ** If the delay has expired, then the text must be drawn. Build the help text + ** overlay list at this time. Better to do it now, when we KNOW it is needed, then + ** to do it earlier when it might not be needed. + */ + Set_Text(Text); + } + } + + TabClass::AI(key, x, y); +} + + +/*********************************************************************************************** + * HelpClass::Help_Text -- Assigns text as the current help text. * + * * + * Use this routine to change the help text that will pop up if the cursor isn't moved * + * for the help delay duration. Call this routine as often as desired. * + * * + * INPUT: text -- The text number for the help text to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Help_Text(int text, int x, int y, int color, bool quick) +{ + if (text != Text) { + + /* + ** If there is an existing text message, then flag the map to redraw the underlying + ** icons so that the text message is erased. + */ + if (Text != TXT_NONE) { + Refresh_Cells(Coord_Cell(TacticalCoord), &OverlapList[0]); + } + + /* + ** Record the position of the mouse. This recorded position will be used to determine + ** if the mouse has moved. A moving mouse prevents the help text from popping up. + */ + X = x; + if (x == -1) X = Get_Mouse_X(); + Y = y; + if (y == -1) Y = Get_Mouse_Y(); + IsRight = (y != -1) || (x != -1); + + if (quick) { + CountDownTimer = 1; + } else { + CountDownTimer = HELP_DELAY; + } + + /* + ** All help text prints in the same color for E3 + */ + //Color = color; + color = color; + Color = HELP_TEXT_COLOR; + Text = text; + Cost = 0; + } +} + + +/*********************************************************************************************** + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * * + * This function will print the help text if it thinks it should. The timer and text * + * message can control whether this occurs. If there is no help text or the countdown timer * + * has not expired, then no text will be printed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Draw_It(bool forced) +{ + TabClass::Draw_It(forced); + + forced = false; // TCTCTCTC + if (Text != TXT_NONE && (forced || !CountDownTimer)) { + + if (LogicPage->Lock()) { + Plain_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY-1, DrawX+Width+1, DrawY+FontHeight, Color); + + if (Cost) { + char buffer[15]; + sprintf(buffer, "$%d", Cost); + int width = String_Pixel_Width(buffer); + + Plain_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY+FontHeight, DrawX+width+1, DrawY+FontHeight+FontHeight-1, Color); + LogicPage->Draw_Line(DrawX, DrawY+FontHeight, DrawX+min(width+1, Width)-1, DrawY+FontHeight, BLACK); + } + LogicPage->Unlock(); + } + } +} + + +/*********************************************************************************************** + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * * + * This routine is used to build the overlap list -- used for icon refreshing. It also * + * determines if the text can fit on the screen and makes adjustments so that it will. * + * * + * INPUT: text -- The text number to set the help system to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/11/1994 JLB : Won't draw past tactical map edges. * + *=============================================================================================*/ +void HelpClass::Set_Text(int text) +{ + if (text != TXT_NONE) { + Text = text; + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_MAP|TPF_NOSHADOW); + Width = String_Pixel_Width(Text_String(Text)); + if (IsRight) { + DrawX = X - Width; + DrawY = Y; + } else { + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth) - 3*RESFACTOR; + int bottom = TacPixelY + Lepton_To_Pixel(TacLeptonHeight) - 1*RESFACTOR; + + DrawX = X+X_OFFSET; + DrawY = Y+Y_OFFSET; + if (DrawX + Width > right) { + DrawX -= (DrawX+Width) - right; + } + if (DrawY + 10*RESFACTOR > bottom) { + DrawY -= (DrawY+10*RESFACTOR) - bottom; + } + if (DrawX < TacPixelX+1) DrawX = TacPixelX+1; + if (DrawY < TacPixelY+1) DrawY = TacPixelY+1; + } + memcpy((void*)OverlapList, Text_Overlap_List(Text_String(Text), DrawX-1, DrawY), sizeof(OverlapList)); + *(short *)&OverlapList[ARRAY_SIZE(OverlapList)-1] = REFRESH_EOL; + } +} + + +/*********************************************************************************************** + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * * + * This routine intercepts the map scrolling request and then makes sure that if, in fact, * + * the map is going to scroll, then reset and erase the help text so that it doesn't * + * mess up the display. * + * * + * INPUT: facing -- The direction to scroll (unused by this routine). * + * * + * really -- If the scroll is actually going to occur, rather than just be examined * + * for legality, then this parameter will be true. If this parameter is * + * true, then the help text is reset. * + * * + * OUTPUT: Returns if it can, or did, scroll in the requested direction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +bool HelpClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (really) { + Help_Text(TXT_NONE); + } + return(TabClass::Scroll_Map(facing, distance, really)); +} + + +/*********************************************************************************************** + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * * + * Use this routine after the Help_Text() function to activate the second line. The second * + * line displays a cost. Typically, this is used by the sidebar to display the cost of the * + * specified item. * + * * + * INPUT: cost -- The cost to associate with this help text. If this value is zero, then * + * no second line is displayed, so don't pass in zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/09/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Cost(int cost) +{ + Cost = cost; +} + + +/*********************************************************************************************** + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * * + * This routine will set the position of the tactical map. At this class level, it merely * + * makes sure that the help text disappears when this happens. The lower level classes * + * actually change the map's position. * + * * + * INPUT: coord -- The new coordinate to make the upper left corner of the visible display. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Tactical_Position(COORDINATE coord) +{ + if (TacticalCoord != coord) { + Help_Text(TXT_NONE); + } + TabClass::Set_Tactical_Position(coord); +} diff --git a/CODE/HELP.H b/CODE/HELP.H new file mode 100644 index 0000000..c1ef046 --- /dev/null +++ b/CODE/HELP.H @@ -0,0 +1,142 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HELP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HELP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HELP_H +#define HELP_H + +#include "tab.h" + +#define HELP_TEXT_COLOR 80 //158 //Goldy/orange + +class HelpClass: public TabClass +{ + public: + HelpClass(void); + HelpClass(NoInitClass const & x) : TabClass(x) {}; + + /* + ** Initialization + */ + virtual void Init_Clear(void); // Clears all to known state + + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Scroll_Map(DirType facing, int &distance, bool really); + virtual void Set_Tactical_Position(COORDINATE coord); + + void Help_Text(int text, int x=-1, int y=-1, int color=LTGREY, bool quick=false); + void Set_Cost(int cost); + short const * Overlap_List(void) const; + + private: + + static char const *HelpText; + int HelpX; + int HelpY; + int HelpWidth; + + + void Set_Text(int text); + + /* + ** If the help text is right justified (as with the help text that pops up over the + ** sidebar icons), then this flag is set to true. + */ + unsigned IsRight:1; + + /* + ** If the optional second line of text that displays cost is desired, then this + ** value will be non-zero. Typically, this is true when the help text is associated + ** with one of the sidebar construction icons. + */ + int Cost; + + /* + ** This is the recorded position of the cursor at the time the help text + ** pops up. The help text is rendered as an offset from this pixel position. + */ + int X; + int Y; + + /* + ** This is the draw X and Y coordinate. This position is relative to the X and + ** Y coordinates but adjusted for screen edges as necessary. + */ + int DrawX; + int DrawY; + + /* + ** The width of the help text (in pixels) is stored here. This is a convenience + ** since calculating the width takes a bit of time. + */ + int Width; + + /* + ** The text number of the help text to display is held here. If no text is to be + ** displayed, then this value will be TXT_NONE. + */ + int Text; + + /* + ** This is the background color to use for the help text. It can change according + ** to the message displayed. + */ + int Color; + + /* + ** This countdown timer controls when the help text will pop up. If the mouse + ** remains stationary while this countdown timer expires, then the help text + ** will pop up. + */ + CDTimerClass CountDownTimer; + + /* + ** This is a calculated cell offset list (from the Map.TacticalCell) that indicates + ** which cells are under the help text and thus which cells need to be redrawn if + ** the help text is to be erased. + */ + static short const OverlapList[60]; + + enum HelpClassEnum { + HELP_DELAY=TIMER_SECOND*1, // The countdown timer delay before help text pops up. + Y_OFFSET=0, // The Y pixel offset from cursor for help text print. + X_OFFSET=12 // The X pixel offset from cursor for help text print. + }; +}; + + +#endif diff --git a/CODE/HOUSE.CPP b/CODE/HOUSE.CPP new file mode 100644 index 0000000..7630fae --- /dev/null +++ b/CODE/HOUSE.CPP @@ -0,0 +1,7765 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : November 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseClass::AI -- Process house logic. * + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * HouseClass::AI_Attack -- Handles offensive attack logic. * + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * HouseClass::AI_Building -- Determines what building to build. * + * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. * + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * HouseClass::AI_Money_Check -- Handles money production logic. * + * HouseClass::AI_Power_Check -- Handle the power situation. * + * HouseClass::AI_Unit -- Determines what unit to build next. * + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * HouseClass::Attacked -- Lets player know if base is under attack. * + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * HouseClass::Blowup_All -- blows up everything * + * HouseClass::Can_Build -- General purpose build legality checker. * + * HouseClass::Clobber_All -- removes all objects for this house * + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * HouseClass::Expert_AI -- Handles expert AI processing. * + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * HouseClass::Find_Building -- Finds a building of specified type. * + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * HouseClass::HouseClass -- Constructor for a house object. * + * HouseClass::Init -- init's in preparation for new scenario * + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * HouseClass::Make_Ally -- Make the specified house an ally. * + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * HouseClass::Production_Begun -- Records that production has begun. * + * HouseClass::Read_INI -- Reads house specific data from INI. * + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * HouseClass::Spend_Money -- Removes money from the house. * + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * HouseClass::Tally_Score -- Fills in the score system for this round * + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * HouseClass::delete -- Deallocator function for a house object. * + * HouseClass::new -- Allocator for a house class. * + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * HouseClass::~HouseClass -- Default destructor for a house object. * + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +//#include "WolDebug.h" + +TFixedIHeapClass HouseClass::BuildChoice; + +int TFixedIHeapClass::Save(Pipe &) const +{ + return(true); +} + +int TFixedIHeapClass::Load(Straw &) +{ + return(0); +} + +void TFixedIHeapClass::Code_Pointers(void) +{ +} + +void TFixedIHeapClass::Decode_Pointers(void) +{ +} + +extern bool RedrawOptionsMenu; + +/*********************************************************************************************** + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * * + * This operator will automatically convert from a houses class object into the HousesType * + * enumerated value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the object's HousesType value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass::operator HousesType(void) const +{ + assert(Houses.ID(this) == ID); + + return(Class->House); +} + + +/*********************************************************************************************** + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * * + * This will calculate the current tiberium (gold) load as a ratio of the maximum storage * + * capacity. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Tiberium_Fraction(void) const +{ + if (Tiberium == 0) { + return(0); + } + return(fixed(Tiberium, Capacity)); +} + + +/*********************************************************************************************** + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * * + * Use this routine to convert a house number into the house pointer that it represents. * + * A simple index into the Houses template array is not sufficient, since the array order * + * is arbitrary. An actual scan through the house object is required in order to find the * + * house object desired. * + * * + * INPUT: house -- The house type number to look up. * + * * + * OUTPUT: Returns with a pointer to the house object that the house number represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass * HouseClass::As_Pointer(HousesType house) +{ + if (house != HOUSE_NONE) { + for (int index = 0; index < Houses.Count(); index++) { + if (Houses.Ptr(index)->Class->House == house) { + return(Houses.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * * + * This basically calls the constructor for each of the houses in the game. All other * + * data specific to the house is initialized when the scenario is loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::One_Time(void) +{ + BuildChoice.Set_Heap(STRUCT_COUNT); +} + + +/*********************************************************************************************** + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * * + * The handicap rating will affect combat, movement, and production for the house. It can * + * either make it more or less difficult for the house (controlled by the handicap value). * + * * + * INPUT: handicap -- The handicap value to assign to this house. The default value for * + * a house is DIFF_NORMAL. * + * * + * OUTPUT: Returns with the old handicap value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + * 10/22/1996 JLB : Uses act like value for multiplay only. * + *=============================================================================================*/ +DiffType HouseClass::Assign_Handicap(DiffType handicap) +{ + DiffType old = Difficulty; + Difficulty = handicap; + + if (Session.Type != GAME_NORMAL) { + HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike); + FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias; + ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias; + CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } else { + FirepowerBias = Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = Rule.Diff[handicap].ArmorBias; + ROFBias = Rule.Diff[handicap].ROFBias; + CostBias = Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } + + return(old); +} + + + +#ifdef CHEAT_KEYS + +void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const +{ + mono->Set_Cursor(x, y); + mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense); +} + + +/*********************************************************************************************** + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * * + * This utility function will output the current status of the house class to the mono * + * screen. Through this information bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_HOUSE)); + + mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name()); + mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name()); + mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel); + mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty); + mono->Set_Cursor(52, 1);mono->Printf("%2d", State); + mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage); + mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ); + mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer); + + mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan); + mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name()); + mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft); + mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits); + mono->Set_Cursor(37, 3);mono->Printf("%5d", Power); + mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied); + mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal); + mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime); + mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime); + + mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan); + mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name()); + mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings); + mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium); + mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain); + mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]); + mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime); + mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime); + + mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan); + mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name()); + mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits); + mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits); + mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost); + mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies); + mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack); + + mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan); + mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name()); + mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry); + mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity); + mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost); + mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W); + mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer); + + mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan); + mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name()); + mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels); + mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center)); + mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime); + + + for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) { + mono->Set_Cursor(1+index, 15); + if (Scen.GlobalFlags[index] != 0) { + mono->Print("1"); + } else { + mono->Print("0"); + } + if (index >= 24) break; + } + if (Enemy != HOUSE_NONE) { + char const * name = ""; + name = HouseClass::As_Pointer(Enemy)->Name(); + mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name()); + } + + Print_Zone_Stats(27, 11, ZONE_NORTH, mono); + Print_Zone_Stats(27, 13, ZONE_CORE, mono); + Print_Zone_Stats(27, 15, ZONE_SOUTH, mono); + Print_Zone_Stats(1, 13, ZONE_WEST, mono); + Print_Zone_Stats(53, 13, ZONE_EAST, mono); + + mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * HouseClass::new -- Allocator for a house class. * + * * + * This is the allocator for a house class. Since there can be only * + * one of each type of house, this is allocator has restricted * + * functionality. Any attempt to allocate a house structure for a * + * house that already exists, just returns a pointer to the previously * + * allocated house. * + * * + * INPUT: house -- The house to allocate a class object for. * + * * + * OUTPUT: Returns with a pointer to the allocated class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void * HouseClass::operator new(size_t) +{ + void * ptr = Houses.Allocate(); + if (ptr) { + ((HouseClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * HouseClass::delete -- Deallocator function for a house object. * + * * + * This function marks the house object as "deallocated". Such a * + * house object is available for reallocation later. * + * * + * INPUT: ptr -- Pointer to the house object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::operator delete(void * ptr) +{ + if (ptr) { + ((HouseClass *)ptr)->IsActive = false; + } + Houses.Free((HouseClass *)ptr); +} + + +/*********************************************************************************************** + * HouseClass::HouseClass -- Constructor for a house object. * + * * + * This function is the constructor and it marks the house object * + * as being allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +#define VOX_NOT_READY VOX_NONE +HouseClass::HouseClass(HousesType house) : + RTTI(RTTI_HOUSE), + ID(Houses.ID(this)), + Class(HouseTypes.Ptr(house)), + Difficulty(Scen.CDifficulty), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1), + RepairDelay(0), + BuildDelay(0), + ActLike(Class->House), + IsHuman(false), + IsPlayerControl(false), + IsStarted(false), + IsAlerted(false), + IsBaseBuilding(false), + IsDiscovered(false), + IsMaxedOut(false), + IsDefeated(false), + IsToDie(false), + IsToLose(false), + IsToWin(false), + IsCivEvacuated(false), + IsRecalcNeeded(true), + IsVisionary(false), + IsTiberiumShort(false), + IsSpied(false), + IsThieved(false), + IsGPSActive(false), + IsBuiltSomething(false), + IsResigner(false), + IsGiverUpper(false), + IsParanoid(false), + IsToLook(true), + DidRepair(false), + IQ(Control.IQ), + State(STATE_BUILDUP), + JustBuiltStructure(STRUCT_NONE), + JustBuiltInfantry(INFANTRY_NONE), + JustBuiltUnit(UNIT_NONE), + JustBuiltAircraft(AIRCRAFT_NONE), + JustBuiltVessel(VESSEL_NONE), + Blockage(0), + RepairTimer(0), + AlertTime(0), + BorrowedTime(0), + BScan(0), + ActiveBScan(0), + OldBScan(0), + UScan(0), + ActiveUScan(0), + OldUScan(0), + IScan(0), + ActiveIScan(0), + OldIScan(0), + AScan(0), + ActiveAScan(0), + OldAScan(0), + VScan(0), + ActiveVScan(0), + OldVScan(0), + CreditsSpent(0), + HarvestedCredits(0), + StolenBuildingsCredits(0), + CurUnits(0), + CurBuildings(0), + CurInfantry(0), + CurVessels(0), + CurAircraft(0), + Tiberium(0), + Credits(0), + Capacity(0), + AircraftTotals(NULL), + InfantryTotals(NULL), + UnitTotals(NULL), + BuildingTotals(NULL), + VesselTotals(NULL), + DestroyedAircraft(NULL), + DestroyedInfantry(NULL), + DestroyedUnits(NULL), + DestroyedBuildings(NULL), + DestroyedVessels(NULL), + CapturedBuildings(NULL), + TotalCrates(NULL), + AircraftFactories(0), + InfantryFactories(0), + UnitFactories(0), + BuildingFactories(0), + VesselFactories(0), + Power(0), + Drain(0), + AircraftFactory(-1), + InfantryFactory(-1), + UnitFactory(-1), + BuildingFactory(-1), + VesselFactory(-1), + FlagLocation(TARGET_NONE), + FlagHome(0), + UnitsLost(0), + BuildingsLost(0), + WhoLastHurtMe(house), + Center(0), + Radius(0), + LATime(0), + LAType(RTTI_NONE), + LAZone(ZONE_NONE), + LAEnemy(HOUSE_NONE), + ToCapture(TARGET_NONE), + RadarSpied(0), + PointTotal(0), + PreferredTarget(QUARRY_ANYTHING), + Attack(0), + Enemy(HOUSE_NONE), + AITimer(0), + UnitToTeleport(0), + BuildStructure(STRUCT_NONE), + BuildUnit(UNIT_NONE), + BuildInfantry(INFANTRY_NONE), + BuildAircraft(AIRCRAFT_NONE), + BuildVessel(VESSEL_NONE), + NukeDest(0), + Allies(0), + DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay), + TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay), + TriggerTime(0), + SpeakAttackDelay(1), + SpeakPowerDelay(1), + SpeakMoneyDelay(1), + SpeakMaxedDelay(1), + RemapColor(Class->RemapColor) +{ + /* + ** Explicit in-place construction of the super weapons is + ** required here because the default constructor for super + ** weapons must serve as a no-initialization constructor (save/load reasons). + */ + new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + + memset(UnitsKilled, '\0', sizeof(UnitsKilled)); + memset(BuildingsKilled, '\0', sizeof(BuildingsKilled)); + memset(BQuantity, '\0', sizeof(BQuantity)); + memset(UQuantity, '\0', sizeof(UQuantity)); + memset(IQuantity, '\0', sizeof(IQuantity)); + memset(AQuantity, '\0', sizeof(AQuantity)); + memset(VQuantity, '\0', sizeof(VQuantity)); + strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name. + HouseTriggers[house].Clear(); + memset((void *)&Regions[0], 0x00, sizeof(Regions)); + Make_Ally(house); + Assign_Handicap(Scen.CDifficulty); + + /* + ** Set the time of the first AI attack. + */ + Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + + if (Session.Type == GAME_INTERNET) { + AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); + InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); + UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); + BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); + VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT); + + DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); + DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); + DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); + DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT); + + CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + TotalCrates = new UnitTrackerClass ( CRATE_COUNT ); + } +} + + +/*********************************************************************************************** + * HouseClass::~HouseClass -- House class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/6/96 4:48PM ST : Created * + *=============================================================================================*/ +HouseClass::~HouseClass (void) +{ + Class = 0; + + if (Session.Type == GAME_INTERNET) { + delete AircraftTotals; + delete InfantryTotals; + delete UnitTotals; + delete BuildingTotals; + delete VesselTotals; + + delete DestroyedAircraft; + delete DestroyedInfantry; + delete DestroyedUnits; + delete DestroyedBuildings; + delete DestroyedVessels; + + delete CapturedBuildings; + delete TotalCrates; + } +} + + +/*********************************************************************************************** + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * * + * This is the default constructor that initializes all the values to their default * + * settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +HouseStaticClass::HouseStaticClass(void) : + IQ(0), + TechLevel(1), + Allies(0), + MaxUnit(Rule.UnitMax/6), + MaxBuilding(Rule.BuildingMax/6), + MaxInfantry(Rule.InfantryMax/6), + MaxVessel(Rule.VesselMax/6), + MaxAircraft(Rule.UnitMax/6), + InitialCredits(0), + Edge(SOURCE_NORTH) +{ +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- General purpose build legality checker. * + * * + * This routine is called when it needs to be determined if the specified object type can * + * be built by this house. Production and sidebar maintenance use this routine heavily. * + * * + * INPUT: type -- Pointer to the type of object that legality is to be checked for. * + * * + * house -- This is the house to check for legality against. Note that this might * + * not be 'this' house since the check could be from a captured factory. * + * Captured factories build what the original owner of them could build. * + * * + * OUTPUT: Can the specified object be built? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * + * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. * + * 11/04/1996 JLB : Computer uses prerequisite record. * + *=============================================================================================*/ +bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const +{ + assert(Houses.ID(this) == ID); + assert(type != NULL); + + /* + ** An object with a prohibited tech level availability will never be allowed, regardless + ** of who requests it. + */ + if (((TechnoTypeClass const *)type)->Level == -1) return(false); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** If this is a CounterStrike II-only unit, and we're playing a multiplayer + ** game in 'downshifted' mode against CounterStrike or Red Alert, then + ** don't allow building this unit. + */ + if (!NewUnitsEnabled) { + switch(type->What_Am_I()) { + case RTTI_INFANTRYTYPE: + if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT) + return(false); + break; + case RTTI_UNITTYPE: + if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT) + return(false); + break; + case RTTI_VESSELTYPE: + if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT) + return(false); + break; + default: + break; + } + } +#endif + + /* + ** The computer can always build everything. + */ + if (!IsHuman && Session.Type == GAME_NORMAL) return(true); + + /* + ** Special hack to get certain objects to exist for both sides in the game. + */ + int own = type->Get_Ownable(); + + /* + ** Check to see if this owner can build the object type specified. + */ + if (((1L << house) & own) == 0) { + return(false); + } + + /* + ** Perform some equivalency fixups for the building existence flags. + */ + long flags = ActiveBScan; + + /* + ** The computer records prerequisite buildings because it can't relay on the + ** sidebar to keep track of this information. + */ + if (!IsHuman) { + flags = OldBScan; + } + + int pre = ((TechnoTypeClass const *)type)->Prerequisite; + + /* + ** Advanced power also serves as a prerequisite for normal power. + */ + if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; + + /* + ** Either tech center counts as a prerequisite. + */ + if (Session.Type != GAME_NORMAL) { + if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH; + } + + int level = Control.TechLevel; +#ifdef CHEAT_KEYS + if (Debug_Cheat) { + level = 98; + pre = 0; + } +#endif + + /* + ** See if the prerequisite requirements have been met. + */ + return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= level); +} + + +/*************************************************************************** + * HouseClass::Init -- init's in preparation for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 12/17/1994 JLB : Resets tracker bits. * + *=========================================================================*/ +void HouseClass::Init(void) +{ + Houses.Free_All(); + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseTriggers[index].Clear(); + } +} + + +/*********************************************************************************************** + * HouseClass::AI -- Process house logic. * + * * + * This handles the AI for the house object. It should be called once per house per game * + * tick. It processes all house global tasks such as low power damage accumulation and * + * house specific trigger events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * + *=============================================================================================*/ +void HouseClass::AI(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** If base building has been turned on by a trigger, then force the house to begin + ** production and team creation as well. This is also true if the IQ is high enough to + ** being base building. + */ + if (IsBaseBuilding || IQ >= Rule.IQProduction) { + IsBaseBuilding = true; + IsStarted = true; + IsAlerted = true; + } + + /* + ** Check to see if the house wins. + */ + if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) { + IsToWin = false; + if (this == PlayerPtr) { + PlayerWins = true; + } else { + PlayerLoses = true; + } + } + + /* + ** Check to see if the house loses. + */ + if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) { + IsToLose = false; + if (this == PlayerPtr) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + } + + /* + ** Check to see if all objects of this house should be blown up. + */ + if (IsToDie && BorrowedTime == 0) { + IsToDie = false; + Blowup_All(); + } + + /* + ** Double check power values to correct illegal conditions. It is possible to + ** get a power output of negative (one usually) as a result of damage sustained + ** and the fixed point fractional math involved with power adjustments. If the + ** power rating drops below zero, then make it zero. + */ + Power = max(Power, 0); + Drain = max(Drain, 0); + + /* + ** If the base has been alerted to the enemy and should be attacking, then + ** see if the attack timer has expired. If it has, then create the attack + ** teams. + */ + if (IsAlerted && AlertTime == 0) { + + /* + ** Adjusted to reduce maximum number of teams created. + */ + int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1)); + for (int index = 0; index < maxteams; index++) { + TeamTypeClass const * ttype = Suggested_New_Team(true); + if (ttype != NULL) { + ScenarioInit++; + ttype->Create_One_Of(); + ScenarioInit--; + } + } + AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); +// int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2); +// int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2); +// AlertTime = Random_Pick(mintime, maxtime); + } + + /* + ** If this house's flag waypoint is a valid cell, see if there's + ** someone sitting on it. If so, make the scatter. If they refuse, + ** blow them up. + */ + if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) { + + TechnoClass * techno = Map[FlagHome].Cell_Techno(); + if (techno != NULL) { + bool moving = false; + if (techno->Is_Foot()) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + if (!moving) { + techno->Scatter(0, true, true); + } + + /* + ** If the techno doesn't have a valid NavCom, he's not moving, + ** so blow him up. + */ + if (techno->Is_Foot()) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + /* + ** If the techno wasn't an infantry or unit (ie he's a building), + ** or he refuses to move, blow him up + */ + if (!moving) { + int count = 0; + while (!(techno->IsInLimbo) && count++ < 5) { + int damage = techno->Strength; + techno->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + } + } + } + + /* + ** Create teams for this house if necessary. + ** (Use the same timer for some extra capture-the-flag logic.) + */ + if (!IsAlerted && !TeamTime) { + + TeamTypeClass const * ttype = Suggested_New_Team(false); + if (ttype) { + ttype->Create_One_Of(); + } + + TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE; + } + + /* + ** If there is insufficient power, then all buildings that are above + ** half strength take a little bit of damage. + */ + if (DamageTime == 0) { + + /* + ** When the power is below required, then the buildings will take damage over + ** time. + */ + if (Power_Fraction() < 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass & b = *Buildings.Ptr(index); + + if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) { + // BG: Only damage buildings that require power, to keep the + // land mines from blowing up under low-power conditions + if (b.Class->Drain) { + int damage = 1; + b.Take_Damage(damage, 0, WARHEAD_AP, 0); + } + } + } + } + DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay; + } + + /* + ** If there are no more buildings to sell, then automatically cancel the + ** sell mode. + */ + if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) { + Map.Sell_Mode_Control(0); + } + + /* + ** Various base conditions may be announced to the player. Typically, this would be + ** low tiberium capacity or low power. + */ + if (PlayerPtr == this) { + + if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) { + Speak(VOX_NEED_MO_MONEY); + Map.Flash_Money(); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + } + + if (SpeakMaxedDelay == 0 && IsMaxedOut) { + IsMaxedOut = false; + if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + } + } + if (SpeakPowerDelay == 0 && Power_Fraction() < 1) { + if (ActiveBScan & STRUCTF_CONST) { + Speak(VOX_LOW_POWER); + SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + Map.Flash_Power(); + + char const * text = NULL; + if (BQuantity[STRUCT_AAGUN] > 0) { + text = Text_String(TXT_POWER_AAGUN); + } + if (BQuantity[STRUCT_TESLA] > 0) { + text = Text_String(TXT_POWER_TESLA); + } + if (text == NULL) { + text = Text_String(TXT_LOW_POWER); + } + if (text != NULL) { + Session.Messages.Add_Message(NULL, 0, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + } + } + + /* + ** If there is a flag associated with this house, then mark it to be + ** redrawn. + */ + if (Target_Legal(FlagLocation)) { + UnitClass * unit = As_Unit(FlagLocation); + if (unit) { + unit->Mark(MARK_CHANGE); + } else { + CELL cell = As_Cell(FlagLocation); + Map[cell].Redraw_Objects(); + } + } + + bool is_time = false; + + /* + ** Triggers are only checked every so often. If the trigger timer has expired, + ** then set the trigger processing flag. + */ + if (TriggerTime == 0 || IsBuiltSomething) { + is_time = true; + TriggerTime = TICKS_PER_MINUTE/10; + IsBuiltSomething = false; + } + + /* + ** Process any super weapon logic required. + */ + Super_Weapon_Handler(); + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Scen.AutoSonarTimer == 0 ) + { + // If house has nothing but subs left, do an automatic sonar pulse to reveal them. + if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw + { + int iCount = 0; + for( int i = 0; i != STRUCT_COUNT-3; ++i ) + { + iCount += BQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != UNIT_RA_COUNT-3; ++i ) + { + iCount += UQuantity[ i ]; + } + if( !iCount ) + { + // ajw - Found bug - house's civilians are not removed from IQuantity when they die. + // Workaround... + for( i = 0; i <= INFANTRY_DOG; ++i ) + { + iCount += IQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != AIRCRAFT_COUNT; ++i ) + { + iCount += AQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != VESSEL_RA_COUNT; ++i ) + { + if( i != VESSEL_SS ) + iCount += VQuantity[ i ]; + } + if( !iCount ) + { + // Do the ping. + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + bAutoSonarPulse = true; + } + } + } + } + } + } + } +#endif + + /* + ** Special win/lose check for multiplayer games; by-passes the + ** trigger system. We must wait for non-zero frame, because init + ** may not properly set IScan etc for each house; you have to go + ** through each object's AI before it will be properly set. + */ + if (Session.Type != GAME_NORMAL && !IsDefeated && + !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) { + MPlayer_Defeated(); + } + + /* + ** Try to spring all events attached to this house. The triggers will check + ** for themselves if they actually need to be sprung or not. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + if (HouseTriggers[Class->House][index]->Spring() && index > 0) { + index--; + continue; + } + } + + /* + ** If a radar facility is not present, but the radar is active, then turn the radar off. + ** The radar also is turned off when the power gets below 100% capacity. + */ + if (PlayerPtr == this) { + bool jammed = Map.Is_Radar_Active(); + + /* + ** Find if there are any radar facilities, and if they're jammed or not + */ + + if (IsGPSActive) { + jammed = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); +#ifdef FIXIT_RADAR_JAMMED + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) { +#else + if (building && building->House == PlayerPtr) { +#endif + if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) { + if (!building->IsJammed) { + jammed = false; + break; + } + } + } + } + } + + if (Map.Get_Jammed() != jammed) { + Map.RadarClass::Flag_To_Redraw(true); + } + Map.Set_Jammed(jammed); +// Need to add in here where we activate it when only GPS is active. + if (Map.Is_Radar_Active()) { + if (ActiveBScan & STRUCTF_RADAR) { + if (Power_Fraction() < 1 && !IsGPSActive) { + Map.Radar_Activate(0); + } + } else { + if (!IsGPSActive) { + Map.Radar_Activate(0); + } + } + + } else { + if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) { + if (Power_Fraction() >= 1 || IsGPSActive) { + Map.Radar_Activate(1); + } + } else { + if (Map.Is_Radar_Existing()) { + Map.Radar_Activate(4); + } + } + } + } + + /* + ** Perform any expert system AI processing. + */ + if (IsBaseBuilding && AITimer == 0) { + AITimer = Expert_AI(); + } + + if (!IsBaseBuilding && State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } + + AI_Building(); + AI_Unit(); + AI_Vessel(); + AI_Infantry(); + AI_Aircraft(); + + + /* + ** If the production possibilities need to be recalculated, then do so now. This must + ** occur after the scan bits have been properly updated. + */ + if (PlayerPtr == this && IsRecalcNeeded) { + IsRecalcNeeded = false; + Map.Recalc(); + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) { + + if (PlayerPtr == building->House) { + building->Update_Buildables(); + } + } + } + } + + /* + ** See if it's time to re-set the can-repair flag + */ + if (DidRepair && RepairTimer == 0) { + DidRepair = false; + } + + if (this == PlayerPtr && IsToLook) { + IsToLook = false; + Map.All_To_Look(); + } +} + + +/*********************************************************************************************** + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * * + * This handles any super weapons assigned to this house. It also performs any necessary * + * maintenance that the super weapons require. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Super_Weapon_Handler(void) +{ + /* + ** Perform all super weapon AI processing. This just checks to see if + ** the graphic needs changing for the special weapon and updates the + ** sidebar as necessary. + */ + for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) { + SuperClass * super = &SuperWeapon[special]; + + if (super->Is_Present()) { + + /* + ** Perform any charge-up logic for the super weapon. If the super + ** weapon is owned by the player and a graphic change is detected, then + ** flag the sidebar to be redrawn so the player will see the change. + */ + if (super->AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + + /* + ** Repeating super weapons that require power will be suspended if there + ** is insufficient power available. + */ + if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) { + super->Suspend(Power_Fraction() < 1); + } + } + } + + /* + ** Check to see if they have launched the GPS, but subsequently lost their + ** tech center. If so, remove the GPS, and shroud the map. + */ + if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) { + IsGPSActive = false; + if (IsPlayerControl) { + Map.Shroud_The_Map(); + } + } + + /* + ** Check to see if the GPS Satellite should be removed from the sidebar + ** because of outside circumstances. The advanced technology facility + ** being destroyed is a good example of this. Having fired the satellite + ** is another good example, because it's a one-shot item. + */ + if (SuperWeapon[SPC_GPS].Is_Present()) { + if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) { + /* + ** Remove the missile capability when there is no advanced tech facility. + */ + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + /* + ** Auto-fire the GPS satellite if it's charged up. + */ + if (SuperWeapon[SPC_GPS].Is_Ready()) { + SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr); + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) { + bldg->HasFired = true; + bldg->Assign_Mission(MISSION_MISSILE); + break; + } + } + } + } + } else { + /* + ** If there is no GPS satellite present, but there is a GPS satellite + ** facility available, then make the GPS satellite available as well. + */ + if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 && + !IsGPSActive && + Control.TechLevel >= Rule.GPSTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + bool canfire = false; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) { + if (!bldg->HasFired) { + canfire = true; + break; + } + } + } + + if (canfire) { + SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_GPS); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the chronosphere should be removed from the sidebar + ** because of outside circumstances. The chronosphere facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the chronosphere when there is no chronosphere facility. + ** Note that this will not remove the one time created chronosphere. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) { + if (this == PlayerPtr) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) { + if (Map.IsTargettingMode == SPC_CHRONO2) { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + } else { + Map.IsTargettingMode = SPC_NONE; + } + } else { + Map.IsTargettingMode = SPC_NONE; + } + } +#else + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || + Map.IsTargettingMode == SPC_CHRONO2) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no chronosphere present, but there is a chronosphere + ** facility available, then make the chronosphere available as well. + */ + if ((ActiveBScan & STRUCTF_CHRONOSPHERE) && +// (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) && + Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level && +// Control.TechLevel >= Rule.ChronoTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + /* + ** Check to see if the iron curtain should be removed from the sidebar + ** because of outside circumstances. The iron curtain facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the iron curtain when there is no iron curtain facility. + ** Note that this will not remove the one time created iron curtain. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no iron curtain present, but there is an iron curtain + ** facility available, then make the iron curtain available as well. + */ + if ((ActiveBScan & STRUCTF_IRON_CURTAIN) && + (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + /* + ** Check to see if the sonar pulse should be removed from the sidebar + ** because of outside circumstances. The spied-upon enemy sub pen + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) { + int usspy = 1 << (Class->House); + bool present = false; + bool powered = false; + for (int q = 0; q < Buildings.Count() && !powered; q++) { + BuildingClass * bldg = Buildings.Ptr(q); + if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->SpiedBy & usspy) ) { + present = true; + powered = !(bldg->House->Power_Fraction() < 1); + } + } + if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the sonar pulse when there is no spied-upon enemy sub pen. + ** Note that this will not remove the one time created sonar pulse. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } + } + + /* + ** Check to see if the nuclear weapon should be removed from the sidebar + ** because of outside circumstances. The missile silos + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the nuke when there is no missile silo. + ** Note that this will not remove the one time created nuke. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } else { + /* + ** Allow the computer to fire the nuclear weapon when the weapon is + ** ready and the owner is the computer. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + } + } + + } else { + /* + ** If there is no nuclear missile present, but there is a missile + ** silo available, then make the missile available as well. + */ + if ((ActiveBScan & STRUCTF_MSLO) && + ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + + if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_SPY_MISSION].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Map.Column[1].Flag_To_Redraw(); + } + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_SPY_MISSION); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) { + SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false); + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_BOMB].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_BOMB); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) { + SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false); + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_INFANTRY); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) { + SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false); + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY); + Map.Column[1].Flag_To_Redraw(); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Attacked -- Lets player know if base is under attack. * + * * + * Call this function whenever a building is attacked (with malice). This function will * + * then announce to the player that his base is under attack. It checks to make sure that * + * this is referring to the player's house rather than the enemy's. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Attacked(void) +{ + assert(Houses.ID(this) == ID); + +#ifdef FIXIT_BASE_ANNOUNCE + if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) { +#else + if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) { +#endif + Speak(VOX_BASE_UNDER_ATTACK); + SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + + /* + ** If there is a trigger event associated with being attacked, process it + ** now. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * * + * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * + * all storage capable buildings for the house. Harvested Tiberium adds to the credit * + * value of the house, but only up to the maximum storage capacity that the house can * + * currently maintain. * + * * + * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Harvested(unsigned tiberium) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + + Tiberium += tiberium; + if (Tiberium > Capacity) { + Tiberium = Capacity; + IsMaxedOut = true; + } + HarvestedCredits += tiberium; + Silo_Redraw_Check(oldtib, Capacity); +} + + +/*********************************************************************************************** + * HouseClass::Stole -- Accounts for the value of a captured building. * + * * + * Use this routine whenever a building is captured. It keeps track of the cost of the * + * building for use in the scoring routine, because you get an 'economy' boost for the * + * value of the stolen building (but you don't get the credit value for it.) * + * * + * INPUT: worth -- The worth of the building we captured (stole). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/05/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Stole(unsigned worth) +{ + assert(Houses.ID(this) == ID); + + StolenBuildingsCredits += worth; +} + + +/*********************************************************************************************** + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * * + * Use this routine to determine the total credit value of the house. This is the sum of * + * the harvested Tiberium in storage and the initial unspent cash reserves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total credit value of the house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +long HouseClass::Available_Money(void) const +{ + assert(Houses.ID(this) == ID); + + return(Tiberium + Credits); +} + + +/*********************************************************************************************** + * HouseClass::Spend_Money -- Removes money from the house. * + * * + * Use this routine to extract money from the house. Typically, this is a result of * + * production spending. The money is extracted from available cash reserves first. When * + * cash reserves are exhausted, then Tiberium is consumed. * + * * + * INPUT: money -- The amount of money to spend. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/20/1995 JLB : Spends Tiberium before spending cash. * + *=============================================================================================*/ +void HouseClass::Spend_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + if (money > Tiberium) { + money -= (unsigned)Tiberium; + Tiberium = 0; + Credits -= money; + } else { + Tiberium -= money; + } + Silo_Redraw_Check(oldtib, Capacity); + CreditsSpent += money; +} + + +/*********************************************************************************************** + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * * + * Use this routine when money needs to be refunded back to the house. This can occur when * + * construction is aborted. At this point, the exact breakdown of Tiberium or initial * + * credits used for the orignal purchase is lost. Presume as much of the money is in the * + * form of Tiberium as storage capacity will allow. * + * * + * INPUT: money -- The number of credits to refund back to the house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/01/1995 JLB : Refunded money is never lost * + *=============================================================================================*/ +void HouseClass::Refund_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + Credits += money; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * * + * Use this routine to adjust the maximum storage capacity for the house. This storage * + * capacity will limit the number of Tiberium credits that can be stored at any one time. * + * * + * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * + * * + * inanger -- Is this a forced adjustment to capacity due to some hostile event? * + * * + * OUTPUT: Returns with the number of Tiberium credits lost. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Adjust_Capacity(int adjust, bool inanger) +{ + assert(Houses.ID(this) == ID); + + long oldcap = Capacity; + int retval = 0; + + Capacity += adjust; + Capacity = max(Capacity, 0L); + if (Tiberium > Capacity) { + retval = Tiberium - Capacity; + Tiberium = Capacity; + if (!inanger) { + Refund_Money(retval); + retval = 0; + } else { + IsMaxedOut = true; + } + } + Silo_Redraw_Check(Tiberium, oldcap); + return(retval); +} + + +/*********************************************************************************************** + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * * + * Call this routine when either the capacity or tiberium levels change for a house. This * + * routine will determine if the aggregate tiberium storage level will result in the * + * silos changing their imagery. If this is detected, then all the silos for this house * + * are flagged to be redrawn. * + * * + * INPUT: oldtib -- Pre-change tiberium level. * + * * + * oldcap -- Pre-change tiberium storage capacity. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) +{ + assert(Houses.ID(this) == ID); + + int oldratio = 0; + if (oldcap) oldratio = (oldtib * 5) / oldcap; + int newratio = 0; + if (Capacity) newratio = (Tiberium * 5) / Capacity; + + if (oldratio != newratio) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { + b->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * * + * This routine will determine if the house number specified is a ally to this house. * + * * + * INPUT: house -- The house number to check to see if it is an ally. * + * * + * OUTPUT: Is the house an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(HousesType house) const +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE) { + return(((1<Class->House)); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * * + * This routine will examine the specified object and return whether it is an ally or not. * + * * + * INPUT: object -- The object to examine to see if it is an ally. * + * * + * OUTPUT: Is the specified object an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (object) { + return(Is_Ally(object->Owner())); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Make_Ally -- Make the specified house an ally. * + * * + * This routine will make the specified house an ally to this house. An allied house will * + * not be considered a threat or potential target. * + * * + * INPUT: house -- The house to make an ally of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 08/08/1995 JLB : Breaks off combat when ally commences. * + * 10/17/1995 JLB : Added reveal base when allied. * + *=============================================================================================*/ +void HouseClass::Make_Ally(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (Is_Allowed_To_Ally(house)) { + + Allies |= (1L << house); + + /* + ** Don't consider the newfound ally to be an enemy -- of course. + */ + if (Enemy == house) { + Enemy = HOUSE_NONE; + } + + if (ScenarioInit) { + Control.Allies |= (1L << house); + } + + if (Session.Type != GAME_NORMAL && !ScenarioInit) { + HouseClass * hptr = HouseClass::As_Pointer(house); + + /* + ** An alliance with another human player will cause the computer + ** players (if present) to become paranoid. + */ + if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) { +// if (hptr != NULL && hptr->IsHuman) { +// if (!hptr->IsHuman) { +// hptr->Make_Ally(Class->House); +// } + Computer_Paranoid(); + } + + char buffer[80]; + + /* + ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure + ** that fighting will most likely stop when the cease fire begins. + */ + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + + if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) { + TARGET target = ((TechnoClass *)object)->TarCom; + if (Target_Legal(target) && As_Techno(target) != NULL) { + if (Is_Ally(As_Techno(target))) { + ((TechnoClass *)object)->Assign_Target(TARGET_NONE); + } + } + } + } + + /* + ** Cause all structures to be revealed to the house that has been + ** allied with. + */ + if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b && !b->IsInLimbo && (HouseClass *)b->House == this) { + Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false); + } + } + } + + if (IsHuman) { + sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_HAS_ALLIED), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[((HouseClass::As_Pointer(house))->Class->House) - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + } + +#if(TEN) + // + // Notify the TEN server of the new alliance + // + if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(hptr->IniName, 1); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the new alliance + // + //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(hptr->IniName, 1); + //} +#endif // MPATH + + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * * + * This routine will flag the house specified so that it will be an enemy to this house. * + * Enemy houses are legal targets for attack. * + * * + * INPUT: house -- The house to make an enemy of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/27/1995 JLB : Making war is a bilateral action. * + *=============================================================================================*/ +void HouseClass::Make_Enemy(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE && Is_Ally(house)) { + HouseClass * enemy = HouseClass::As_Pointer(house); + Allies &= ~(1L << house); + + if (ScenarioInit) { + Control.Allies &= !(1L << house); + } + + /* + ** Breaking an alliance is a bilateral event. + */ + if (enemy != NULL && enemy->Is_Ally(this)) { + enemy->Allies &= ~(1L << Class->House); + + if (ScenarioInit) { + Control.Allies &= ~(1L << Class->House); + } + } + + if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) { + char buffer[80]; + + sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + Map.Flag_To_Redraw(false); + +#if(TEN) + // + // Notify the TEN server of the broken alliance + // + if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(enemy->IniName, 0); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the broken alliance + // + //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(enemy->IniName, 0); + //} +#endif // MPATH + + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * * + * This routine will return with the remap table to use when displaying an object owned * + * by this house. If the object is blushing (flashing), then the lightening remap table is * + * always used. The "unit" parameter allows proper remap selection for those houses that * + * have a different remap table for buildings or units. * + * * + * INPUT: blushing -- Is the object blushing (flashing)? * + * * + * remap -- The remap control value to use. * + * REMAP_NONE No remap pointer returned at all. * + * REMAP_NORMAL Return the remap pointer for this house. * + * REMAP_ALTERNATE (Nod solo play only -- forces red remap). * + * Multiplay returns same as REMAP_NORMAL * + * * + * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/25/1995 JLB : Uses remap control value. * + *=============================================================================================*/ +unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const +{ + assert(Houses.ID(this) == ID); + + if (blushing) return(&Map.FadingLight[0]); + + if (remap == REMAP_NONE) return(0); + + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * * + * This routine examines the house condition and returns with the team that it thinks * + * should be created. The units that are not currently a member of a team are examined * + * to determine the team needed. * + * * + * INPUT: alertcheck -- Select from the auto-create team list. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) +{ + assert(Houses.ID(this) == ID); + + return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck)); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * * + * This routine is called when the threat rating for a region needs to change. The region * + * and threat adjustment are provided. * + * * + * INPUT: region -- The region that adjustment is to occur on. * + * * + * threat -- The threat adjustment to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Threat(int region, int threat) +{ + assert(Houses.ID(this) == ID); + + static int _val[] = { + -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, + -1, 0, 1, + MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 + }; + static int _thr[] = { + 2, 1, 2, + 1, 0, 1, + 2, 1, 2 + }; + int neg; + int * val = &_val[0]; + int * thr = &_thr[0]; + + if (threat < 0) { + threat = -threat; + neg = true; + } else { + neg = false; + } + + for (int lp = 0; lp < 9; lp ++) { + Regions[region + *val].Adjust_Threat(threat >> *thr, neg); + val++; + thr++; + } +} + + +/*********************************************************************************************** + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * * + * This routine is called from the event system. It will start production for the object * + * type specified. This will be reflected in the sidebar as well as the house factory * + * tracking variables. * + * * + * INPUT: type -- The type of object to begin production on. * + * * + * id -- The subtype of object. * + * * + * OUTPUT: Returns with the reason why, or why not, production was started. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/21/1996 JLB : Handles max object case. * + *=============================================================================================*/ +ProdFailType HouseClass::Begin_Production(RTTIType type, int id) +{ + assert(Houses.ID(this) == ID); + int result = true; + bool initial_start = false; + FactoryClass * fptr; + TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); + + fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code, unless we are restarting production. + */ + if (fptr != NULL) { + if (fptr->Is_Building()) { + return(PROD_CANT); + } + } else { + fptr = new FactoryClass(); + if (!fptr) return(PROD_CANT); + Set_Factory(type, fptr); + result = fptr->Set(*tech, *this); + initial_start = true; + } + + if (result) { + fptr->Start(); + + /* + ** Link this factory to the sidebar so that proper graphic feedback + ** can take place. + */ + if (PlayerPtr == this) { + Map.Factory_Link(fptr->ID, type, id); + } + return(PROD_OK); + } + + delete fptr; + return(PROD_CANT); +} + + +/*********************************************************************************************** + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * * + * This routine is called from the event system whenever the production of the specified * + * type needs to be suspended. The suspended production will be reflected in the sidebar * + * as well as in the house control structure. * + * * + * INPUT: type -- The type of object that production is being suspended for. * + * * + * OUTPUT: Returns why, or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Suspend_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Actually suspend the production. + */ + fptr->Suspend(); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * * + * This routine is called from the event system whenever production must be abandoned for * + * the type specified. This will remove the factory and pending object from the sidebar as * + * well as from the house factory record. * + * * + * INPUT: type -- The object type that production is being suspended for. * + * * + * OUTPUT: Returns the reason why or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Abandon_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If there is no factory to abandon, then return with a failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.Abandon_Production(type, fptr->ID); + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + + /* + ** Abandon production of the object. + */ + fptr->Abandon(); + Set_Factory(type, NULL); + delete fptr; + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * * + * This routine will pick a good target to fire the special weapon specified. * + * * + * INPUT: id -- The special weapon id to fire. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 PWG : Created. * + *=============================================================================================*/ +void HouseClass::Special_Weapon_AI(SpecialWeaponType id) +{ + assert(Houses.ID(this) == ID); + + /* + ** Loop through all of the building objects on the map + ** and see which ones are available. + */ + BuildingClass * bestptr = NULL; + int best = -1; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + /* + ** If the building is valid, not in limbo, not in the process of + ** being destroyed and not our ally, then we can consider it. + */ + if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { + if (Percent_Chance(90) && (b->Value() > best || best == -1)) { + best = b->Value(); + bestptr = b; + } + } + } + + if (bestptr) { + CELL cell = Coord_Cell(bestptr->Center_Coord()); + Place_Special_Blast(id, cell); + } +} + + +/*********************************************************************************************** + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * * + * This routine will create a blast effect at the cell specified. This is the result of * + * the special weapons. * + * * + * INPUT: id -- The special weapon id number. * + * * + * cell -- The location where the special weapon attack is to occur. * + * * + * OUTPUT: Was the special weapon successfully fired at the location specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented. * + * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * + * 07/28/1995 JLB : Revamped to use super weapon class controller. * + *=============================================================================================*/ +bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) +{ + assert(Houses.ID(this) == ID); + + BuildingClass * launchsite = 0; + AnimClass * anim = 0; + switch (id) { + case SPC_SONAR_PULSE: + // Automatically discharge the sonar pulse and uncloak all subs. + if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) { + SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr); + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.Activate_Pulse(); + } + Sound_Effect(VOC_SONAR); + IsRecalcNeeded = true; + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { +#else + if (*sub == VESSEL_SS) { +#endif + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + } + break; + + case SPC_NUCLEAR_BOMB: + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) { + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(cell); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.IsTargettingMode = SPC_NONE; + } + } + } else { + + /* + ** Search for a suitable launch site for this missile. + */ + launchsite = Find_Building(STRUCT_MSLO); + + /* + ** If a launch site was found, then proceed with the normal launch + ** sequence. + */ + if (launchsite) { + launchsite->Assign_Mission(MISSION_MISSILE); + launchsite->Commence(); + NukeDest = cell; + } + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + } + break; + + case SPC_PARA_INFANTRY: + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) { + + TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF"); + if (ttype == NULL) { + ttype = new TeamTypeClass; + if (ttype != NULL) { + strcpy(ttype->IniName, "@PINF"); + ttype->IsTransient = true; + ttype->IsPrebuilt = false; + ttype->IsReinforcable = false; + ttype->Origin = WAYPT_SPECIAL; + ttype->MissionCount = 1; + ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT; + ttype->MissionList[0].Data.Value = WAYPT_SPECIAL; + ttype->ClassCount = 2; + ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers(); + ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + ttype->Members[1].Quantity = 1; + ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER); + } + } + + if (ttype != NULL) { + ttype->House = Class->House; + Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT); + Do_Reinforcements(ttype); + } + +// Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + break; + + case SPC_SPY_MISSION: + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell)); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + break; + + case SPC_PARA_BOMB: + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + break; + + case SPC_IRON_CURTAIN: + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech) { + switch (tech->What_Am_I()) { + case RTTI_UNIT: + case RTTI_BUILDING: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND; + } +#endif + tech->Mark(MARK_CHANGE); + Sound_Effect(VOC_IRON1, tech->Center_Coord()); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr); + break; + default: + break; + } + } + + IsRecalcNeeded = true; + } + break; + + case SPC_CHRONOSPHERE: + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech && Is_Ally(tech)) { + if (tech->What_Am_I() == RTTI_UNIT || + tech->What_Am_I() == RTTI_INFANTRY || +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) { +#else + (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) { +#endif + + if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + bool porthim = true; + if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) { + porthim = false; + } + if (porthim) { +#endif + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_CHRONO2; + } + UnitToTeleport = tech->As_Target(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + } + break; + + case SPC_CHRONO2: + { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + CELL oldcell = cell; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#else + if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#endif + /* + ** Destroy any infantryman that gets teleported + */ + if (tech->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)tech; + inf->Mark(MARK_UP); + inf->Coord = Cell_Coord(cell); + inf->Mark(MARK_DOWN); + int damage = inf->Strength; + inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->Assign_Target(tech->As_Target()); +#endif + } else { + /* + ** Warp the unit to the new location. + */ + DriveClass * drive = (DriveClass *)tech; + drive->MoebiusCell = Coord_Cell(drive->Coord); + oldcell = drive->MoebiusCell; + drive->Teleport_To(cell); + drive->IsMoebius = true; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->IsMoebius = false; + } + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + } +#else + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; +#endif + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONO, drive->Coord); + } + } + UnitToTeleport = TARGET_NONE; + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) { +#endif + SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + IsRecalcNeeded = true; + + /* + ** Now set a percentage chance that a time quake will occur. + */ + if (!TimeQuake) { + TimeQuake = Percent_Chance(Rule.QuakeChance * 100); + } + + /* + ** Now set a percentage chance that a chronal vortex will appear. It + ** might appear where the object teleported to or it might appear + ** where it teleported from -- random chance. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Don't allow a vortex if the teleportation was due to a chrono tank. + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) +#endif + if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) { + int x = Random_Pick(0, Map.MapCellWidth-1); + int y = Random_Pick(0, Map.MapCellHeight-1); + ChronalVortex.Appear(XY_Cell(Map.MapCellX + x, Map.MapCellY + y)); + +// if (Percent_Chance(50)) { +// ChronalVortex.Appear(Cell_Coord(oldcell)); +// } else { +// ChronalVortex.Appear(Cell_Coord(cell)); +// } + } + + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * * + * This routine is called when a building has been produced and now must be placed on * + * the map. When the player clicks on the map, this routine is ultimately called when the * + * event passes through the event queue system. * + * * + * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * + * * + * * + * cell -- The location to place the object on the map. * + * * + * OUTPUT: Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Place_Object(RTTIType type, CELL cell) +{ + assert(Houses.ID(this) == ID); + + TechnoClass * tech = 0; + FactoryClass * factory = Fetch_Factory(type); + + /* + ** Only if there is a factory active for this type, can it be "placed". + ** In the case of a missing factory, then this request is completely bogus -- + ** ignore it. This might occur if, between two events to exit the same + ** object, the mouse was clicked on the sidebar to start building again. + ** The second placement event should NOT try to place the object that is + ** just starting construction. + */ + if (factory && factory->Has_Completed()) { + tech = factory->Get_Object(); + + if (cell == -1) { + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + +#ifdef FIXIT_HELI_LANDING + /* + ** Try to find a place for the object to appear from. For helicopters, it has the + ** option of finding a nearby helipad if no helipads are free. + */ + TechnoClass * builder = pending->Who_Can_Build_Me(false, false); + if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) { + builder = pending->Who_Can_Build_Me(true, false); + + } +#else + bool intheory = false; + if (pending->What_Am_I() == RTTI_AIRCRAFT) { + + /* + ** BG hack - helicopters don't need a specific building to + ** emerge from, in fact, they'll land next to a building if + ** need be. + */ + if( !((AircraftClass *)pending)->Class->IsFixedWing) { + intheory = true; + } + } + TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false); +#endif + + if (builder != NULL && builder->Exit_Object(pending)) { + + /* + ** Since the object has left the factory under its own power, delete + ** the production manager tied to this slot in the sidebar. Its job + ** has been completed. + */ + factory->Completed(); + Abandon_Production(type); + switch (pending->What_Am_I()) { + case RTTI_UNIT: + JustBuiltUnit = ((UnitClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_VESSEL: + JustBuiltVessel = ((VesselClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + JustBuiltStructure = ((BuildingClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + } + } else { + /* + ** The object could not leave under it's own power. Just wait + ** until the player tries to place the object again. + */ + return(false); + } + } + + } else { + if (tech) { + TechnoClass * builder = tech->Who_Can_Build_Me(false, false); + if (builder) { + + builder->Transmit_Message(RADIO_HELLO, tech); + if (tech->Unlimbo(Cell_Coord(cell))) { + factory->Completed(); + Abandon_Production(type); + + if (PlayerPtr == this) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN); + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + } + return(true); + } else { + if (this == PlayerPtr) { + Speak(VOX_DEPLOY); + } + } + builder->Transmit_Message(RADIO_OVER_OUT); + } + return(false); + + } else { + + // Play a bad sound here? + return(false); + } + } + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * * + * This routine will inform the display system that building placement mode has begun. * + * The cursor will be created that matches the layout of the building shape. * + * * + * INPUT: builder -- The factory that is building this object. * + * * + * object -- The building that is going to be placed down on the map. * + * * + * OUTPUT: Was the building placement mode successfully initiated? * + * * + * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * + * is affected. * + * * + * HISTORY: * + * 05/04/1995 JLB : Created. * + * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * + *=============================================================================================*/ +bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) +{ + assert(Houses.ID(this) == ID); + + if (this == PlayerPtr && !Map.PendingObject && builder && object) { + /* + ** Ensures that object selection doesn't remain when + ** building placement takes place. + */ + Unselect_All(); + + Map.Repair_Mode_Control(0); + Map.Sell_Mode_Control(0); + + Map.PendingObject = object->Class; + Map.PendingObjectPtr = object; + Map.PendingHouse = Class->House; + + Map.Set_Cursor_Shape(object->Occupy_List(true)); + Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); + builder->Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*************************************************************************** + * HouseClass::Clobber_All -- removes all objects for this house * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine removes the house itself, so the multiplayer code * + * must not rely on there being "empty" houses lying around. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Clobber_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this) { + delete ::Aircraft.Ptr(i); + i--; + } + } + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this) { + delete ::Units.Ptr(i); + i--; + } + } + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this) { + delete ::Vessels.Ptr(i); + i--; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this) { + delete Infantry.Ptr(i); + i--; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this) { + delete Buildings.Ptr(i); + i--; + } + } + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->Class->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } + for (i = 0; i < TriggerTypes.Count(); i++) { + if (TriggerTypes.Ptr(i)->House == Class->House) { + delete TriggerTypes.Ptr(i); + i--; + } + } + + delete this; +} + + +/*********************************************************************************************** + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * * + * This routine is called when an object is to be removed from the game system. If the * + * specified object is part of the house tracking system, then it will be removed. * + * * + * INPUT: target -- The target value of the object that is to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Detach(TARGET target, bool ) +{ + assert(Houses.ID(this) == ID); + + if (ToCapture == target) { + ToCapture = TARGET_NONE; + } + + if (Is_Target_Trigger(target)) { + HouseTriggers[ID].Delete(As_Trigger(target)); + } +} + + +/*********************************************************************************************** + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * * + * This routine will examine the enemy houses and if there is a building owned by one * + * of those house, true will be returned. * + * * + * INPUT: btype -- The building type to check for. * + * * + * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const +{ + assert(Houses.ID(this) == ID); + + int bflag = 1L << btype; + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseClass * house = HouseClass::As_Pointer(index); + + if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * * + * This routine will examine the house status and return with a techno type pointer to * + * the object type that it thinks should be created. The type is restricted to match the * + * type specified. Typical use of this routine is by computer controlled factories. * + * * + * INPUT: objecttype -- The type of object to restrict the scan for. * + * * + * kennel -- Is this from a kennel? There are special hacks to ensure that only * + * dogs can be produced from a kennel. * + * * + * OUTPUT: Returns with a pointer to a techno type for the object type that should be * + * created. If no object should be created, then NULL is returned. * + * * + * WARNINGS: This is a time consuming routine. Only call when necessary. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const +{ + assert(Houses.ID(this) == ID); + + TechnoTypeClass const * techno = NULL; + + switch (objecttype) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (BuildAircraft != AIRCRAFT_NONE) { + return(&AircraftTypeClass::As_Reference(BuildAircraft)); + } + return(NULL); + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + if (BuildVessel != VESSEL_NONE) { + return(&VesselTypeClass::As_Reference(BuildVessel)); + } + return(NULL); + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (BuildUnit != UNIT_NONE) { + return(&UnitTypeClass::As_Reference(BuildUnit)); + } + return(NULL); + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (BuildInfantry != INFANTRY_NONE) { + if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL); + if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL); + return(&InfantryTypeClass::As_Reference(BuildInfantry)); + } + return(NULL); + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); + } + return(techno); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * * + * This routine will remove the flag attached to the specified target object or cell. * + * Call this routine before placing the object down. This is called inherently by the * + * the Flag_Attach() functions. * + * * + * INPUT: target -- The target that the flag was attached to but will be removed from. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully removed from the specified target? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Remove(TARGET target, bool set_home) +{ + assert(Houses.ID(this) == ID); + + int rc; + + if (Target_Legal(target)) { + + /* + ** Remove the flag from a unit + */ + UnitClass * object = As_Unit(target); + if (object) { + rc = object->Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + + } else { + + /* + ** Remove the flag from a cell + */ + CELL cell = As_Cell(target); + if (Map.In_Radar(cell)) { + rc = Map[cell].Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + } + } + + /* + ** Handle the flag home cell: + ** If 'set_home' is set, clear the home value & the cell's overlay + */ + if (set_home) { + if (FlagHome != 0) { + Map[FlagHome].Overlay = OVERLAY_NONE; + Map.Flag_Cell(FlagHome); + FlagHome = 0; + } + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * * + * This routine will attach the house flag to the location specified. If the location * + * cannot contain the flag, then a suitable nearby location will be selected. * + * * + * INPUT: cell -- The desired cell location to place the flag. * + * * + * set_home -- if true, resets the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully placed? * + * * + * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * + * Check the FlagLocation value to determine the final cell resting spot. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 10/08/1996 JLB : Uses map nearby cell scanning handler. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(CELL cell, bool set_home) +{ + assert(Houses.ID(this) == ID); + + bool rc; + bool clockwise; + + /* + ** Randomly decide if we're going to search cells clockwise or counter- + ** clockwise + */ + clockwise = Percent_Chance(50); + + /* + ** Only continue if this cell is a legal placement cell. + */ + if (Map.In_Radar(cell)) { + + /* + ** If the flag already exists, then it must be removed from the object + ** it is attached to. + */ + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the cell specified. If it can't be placed, then pick + ** a nearby cell where it can be placed. + */ + CELL newcell = cell; + rc = Map[newcell].Flag_Place(Class->House); + if (!rc) { + newcell = Map.Nearby_Location(cell, SPEED_TRACK); + if (newcell != 0) { + rc = Map[newcell].Flag_Place(Class->House); + } + +#ifdef OBSOLETE + /* + ** Loop for increasing distance from the desired cell. + ** For each distance, randomly pick a starting direction. Between + ** this and the clockwise/counterclockwise random value, the flag + ** should appear to be placed fairly randomly. + */ + for (int dist = 1; dist < 32; dist++) { + FacingType fcounter; + FacingType rot; + + /* + ** Clockwise search. + */ + if (clockwise) { + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot++; + if (rot > FACING_NW) rot = FACING_N; + } + } else { + + /* + ** Counter-clockwise search + */ + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot--; + if (rot < FACING_N) + rot = FACING_NW; + } + } + } +#endif + } + + /* + ** If we've found a spot for the flag, place the flag at the new cell. + ** if 'set_home' is set, OR this house has no current flag home cell, + ** mark that cell as this house's flag home cell. + */ + if (rc) { + FlagLocation = As_Target(newcell); + + if (set_home || FlagHome == 0) { + Map[newcell].Overlay = OVERLAY_FLAG_SPOT; + Map[newcell].OverlayData = 0; + Map[newcell].Recalc_Attributes(); + FlagHome = newcell; + } + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * * + * This routine will attach the house flag to the specified unit. This routine is called * + * when a unit drives over a cell containing a flag. * + * * + * INPUT: object -- Pointer to the object that the house flag is to be attached to. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag attached successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) +{ + assert(Houses.ID(this) == ID); + + if (object && !object->IsInLimbo) { + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the object. + */ + object->Flag_Attach(Class->House); + FlagLocation = object->As_Target(); + return(true); + } + return(false); +} + + +/*************************************************************************** + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/25/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::MPlayer_Defeated(void) +{ + assert(Houses.ID(this) == ID); + + char txt[80]; + int i,j; + unsigned char id; + HouseClass * hptr; + HouseClass * hptr2; + int num_alive; + int num_humans; + int all_allies; + + /* + ** Set the defeat flag for this house + */ + IsDefeated = true; + + /* + ** If this is a computer controlled house, then all computer controlled + ** houses become paranoid. + */ + if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) { + Computer_Paranoid(); + } + + /* + ** Remove this house's flag & flag home cell + */ + if (Special.IsCaptureTheFlag) { + if (FlagLocation) { + Flag_Remove(FlagLocation, true); + } else { + if (FlagHome != 0) { + Flag_Remove(FlagHome, true); + } + } + } + + /* + ** If this is me: + ** - Set MPlayerObiWan, so I can only send messages to all players, and + ** not just one (so I can't be obnoxiously omnipotent) + ** - Reveal the map + ** - Add my defeat message + */ + if (PlayerPtr == this) { + Session.ObiWan = 1; + Debug_Unshroud = true; + HidPage.Clear(); + Map.Flag_To_Redraw(true); + + /* + ** Pop up a message showing that I was defeated + */ + sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + + } else { + + /* + ** If it wasn't me, find out who was defeated + */ + if (IsHuman) { + sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + + Session.Messages.Add_Message(NULL, 0, txt, RemapColor, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + RedrawOptionsMenu = true; + } + } + + /* + ** Find out how many players are left alive. + */ + num_alive = 0; + num_humans = 0; + for (i = 0; i < Session.MaxPlayers; i++) { + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (hptr && !hptr->IsDefeated) { + if (hptr->IsHuman) { + num_humans++; + } + num_alive++; + } + } + + /* + ** If all the houses left alive are allied with each other, then in reality + ** there's only one player left: + */ + all_allies = 1; + for (i = 0; i < Session.MaxPlayers; i++) { + + /* + ** Get a pointer to this house + */ + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (!hptr || hptr->IsDefeated) + continue; + + /* + ** Loop through all houses; if there's one left alive that this house + ** isn't allied with, then all_allies will be false + */ + for (j = 0; j < Session.MaxPlayers; j++) { + hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); + if (!hptr2) { + continue; + } + + if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { + all_allies = 0; + break; + } + } + if (!all_allies) { + break; + } + } + + /* + ** If all houses left are allies, set 'num_alive' to 1; game over. + */ + if (all_allies) { + num_alive = 1; + } + + /* + ** If there's only one human player left or no humans left, the game is over: + ** - Determine whether this player wins or loses, based on the state of the + ** player's IsDefeated flag + ** - Find all players' indices in the Session.Score array + ** - Tally up scores for this game + */ + if (num_alive == 1 || num_humans == 0) { + if (PlayerPtr->IsDefeated) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + + /* + ** Add up the scores + */ + Tally_Score(); + + /* + ** Destroy all the IPX connections, since we have to go through the rest + ** of the Main_Loop() before we detect that the game is over, and we'll + ** end up waiting for frame sync packets from the other machines. + */ + if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) { + i = 0; + while (Ipx.Num_Connections() && (i++ < 1000) ) { + id = Ipx.Connection_ID(0); + Ipx.Delete_Connection(id); + } + Session.NumPlayers = 0; + } + +#if(TEN) + // + // Tell the TEN server who won + // + if (Session.Type == GAME_TEN) { + Send_TEN_Win_Packet(); + } +#endif // TEN + +#if(MPATH) + // + // Tell the MPATH server who won + // + if (Session.Type == GAME_MPATH) { + FILE *fp; + + fp = fopen("winner.txt","wt"); + if (fp) { + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + fprintf(fp,"%s\n",hptr->IniName); + } + } + fclose(fp); + } + } +#endif // MPATH + + } + + /* + ** Be sure our messages get displayed, even if we're about to exit. + */ + Map.Render(); +} + + +/*************************************************************************** + * HouseClass::Tally_Score -- Fills in the score system for this round * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::Tally_Score(void) +{ + HousesType house; + HousesType house2; + HouseClass * hptr; + int score_index; + int i,j,k; + int max_index; + int max_count; + int count; + + /* + ** Loop through all houses, tallying up each player's score + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + hptr = HouseClass::As_Pointer(house); + /* + ** Skip this house if it's not human. + */ + if (!hptr || !hptr->IsHuman) { + continue; + } + /* + ** Now find out where this player is in the score array + */ + score_index = -1; + for (i = 0; i < Session.NumScores; i++) { + if (!stricmp(hptr->IniName, Session.Score[i].Name)) { + score_index = i; + break; + } + } + + /* + ** If the index is still -1, the name wasn't found; add a new entry. + */ + if (score_index == -1) { + /* + ** Just add this player to the end of the array, if there's room + */ + if (Session.NumScores < MAX_MULTI_NAMES) { + score_index = Session.NumScores; + Session.NumScores++; + } + /* + ** If there's not room, we have to remove somebody. + ** For each player in the scores array, count the # of '-1' entries + ** from this game backwards; the one with the most is the one that + ** hasn't played the longest; replace him with this new guy. + */ + else { + max_index = 0; + max_count = 0; + for (j = 0; j < Session.NumScores; j++) { + count = 0; + for (k = Session.NumScores - 1; k >= 0; k--) { + if (Session.Score[j].Kills[k]==-1) { + count++; + } + else { + break; + } + } + if (count > max_count) { + max_count = count; + max_index = j; + } + } + score_index = max_index; + } + + /* + ** Initialize this new score entry + */ + Session.Score[score_index].Wins = 0; + strcpy (Session.Score[score_index].Name, hptr->IniName); + for (j = 0; j < MAX_MULTI_GAMES; j++) + Session.Score[score_index].Kills[j] = -1; + } + + /* + ** Init this player's Kills to 0 (-1 means he didn't play this round; + ** 0 means he played but got no kills). + */ + Session.Score[score_index].Kills[Session.CurGame] = 0; + + /* + ** Init this player's color to his last-used color index + */ + Session.Score[score_index].Color = hptr->RemapColor; + + /* + ** If this house was undefeated, it must have been the winner. + ** (If no human houses are undefeated, the computer won.) + */ + if (!hptr->IsDefeated) { + Session.Score[score_index].Wins++; + Session.Winner = score_index; + } + + /* + ** Tally up all kills for this player + */ + for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) { + Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2]; + Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2]; + } + } +} + + +/*************************************************************************** + * HouseClass::Blowup_All -- blows up everything * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + * 05/07/1996 JLB : Handles ships. * + *=========================================================================*/ +void HouseClass::Blowup_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + int damage; + UnitClass * uptr; + InfantryClass * iptr; + BuildingClass * bptr; + int count; + WarheadType warhead; + + /* + ** Find everything owned by this house & blast it with a huge amount of damage + ** at zero range. Do units before infantry, so the units' drivers are killed + ** too. Using Explosion_Damage is like dropping a big bomb right on the + ** object; it will also damage anything around it. + */ + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { + uptr = ::Units.Ptr(i); + + /* + ** Some units can't be killed with one shot, so keep damaging them until + ** they're gone. The unit will destroy itself, and put an infantry in + ** its place. When the unit destroys itself, decrement 'i' since + ** its pointer will be removed from the active pointer list. + */ + count = 0; + while (::Units.Ptr(i)==uptr && uptr->Strength) { + damage = uptr->Strength; + uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5 && uptr->IsActive) { + delete uptr; + break; + } + } + i--; + } + } + + /* + ** Destroy all aircraft owned by this house. + */ + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { + AircraftClass * aptr = ::Aircraft.Ptr(i); + + damage = aptr->Strength; + aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!aptr->IsActive) { + i--; + } + } + } + + /* + ** Destroy all vessels owned by this house. + */ + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) { + VesselClass * vptr = ::Vessels.Ptr(i); + + damage = vptr->Strength; + vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!vptr->IsActive) { + i--; + } + } + } + + /* + ** Buildings don't delete themselves when they die; they shake the screen + ** and begin a countdown, so don't decrement 'i' when it's destroyed. + */ + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { + bptr = Buildings.Ptr(i); + + count = 0; + while (Buildings.Ptr(i)==bptr && bptr->Strength) { + damage = bptr->Strength; + bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5) { + delete bptr; + break; + } + } + } + } + + /* + ** Infantry don't delete themselves when they die; they go into a death- + ** animation sequence, so there's no need to decrement 'i' when they die. + ** Infantry should die by different types of warheads, so their death + ** anims aren't all synchronized. + */ + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { + iptr = Infantry.Ptr(i); + + count = 0; + while (Infantry.Ptr(i)==iptr && iptr->Strength) { + damage = iptr->Strength; + warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE); + iptr->Take_Damage(damage, 0, warhead, NULL, true); + + count++; + if (count > 5) { + delete iptr; + break; + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * * + * When this routine is called, the house will blow up after a period of time. Typically * + * this is called when the flag is captured or the HQ destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to blow up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Die(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToDie = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToDie); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * * + * When this routine is called, the house will be declared the winner after a period of * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to win? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Win(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToWin = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToWin); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * * + * When this routine is called, it will spell the doom of this house. In a short while * + * all of the object owned by this house will explode. Typical use of this routine is when * + * the flag has been captured or the command vehicle has been destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Has the doom been initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Lose(void) +{ + assert(Houses.ID(this) == ID); + + IsToWin = false; + if (!IsToDie && !IsToLose) { + IsToLose = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToLose); +} + + +/*********************************************************************************************** + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * * + * This routine is called when initializing the color and remap data for this house. The * + * primary user of this routine is the multiplayer version of the game, especially for * + * saving & loading multiplayer games. * + * * + * INPUT: color -- The color of this house. * + * * + * house -- The house that this should act like. * + * * + * credits -- The initial credits to assign to this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) +{ + assert(Houses.ID(this) == ID); + + Credits = Control.InitialCredits = credits; + RemapColor = color; + ActLike = house; +} + + +/*********************************************************************************************** + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * * + * Use this routine to fetch the current power output as a fixed point fraction. The * + * value 0x0100 is 100% power. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with power rating as a fixed pointer number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1995 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Power_Fraction(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power >= Drain || Drain == 0) return(1); + + if (Power) { + return(fixed(Power, Drain)); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * * + * This routine will try to sell the wall at the specified location. If there is a wall * + * present and it is owned by this house, then it can be sold. * + * * + * INPUT: cell -- The cell that wall selling is desired. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + * 11/02/1996 JLB : Checks unsellable bit for wall type. * + *=============================================================================================*/ +void HouseClass::Sell_Wall(CELL cell) +{ + assert(Houses.ID(this) == ID); + + if ((unsigned)cell > 0) { + OverlayType overlay = Map[cell].Overlay; + + if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { + OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); + + if (optr.IsWall) { + BuildingTypeClass const * btype = NULL; + switch (overlay) { + case OVERLAY_SANDBAG_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL); + break; + + case OVERLAY_CYCLONE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL); + break; + + case OVERLAY_BRICK_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL); + break; + + case OVERLAY_BARBWIRE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL); + break; + + case OVERLAY_WOOD_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL); + break; + + case OVERLAY_FENCE: + btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE); + break; + + default: + break; + } + if (btype != NULL && !btype->IsUnsellable) { + + if (PlayerPtr == this) { + Sound_Effect(VOC_CASHTURN); + } + + Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); + Map[cell].Overlay = OVERLAY_NONE; + Map[cell].OverlayData = 0; + Map[cell].Owner = HOUSE_NONE; + Map[cell].Wall_Update(); + Map[cell].Recalc_Attributes(); + Map[cell].Redraw_Objects(); + Map.Radar_Pixel(cell); + Detach_This_From_All(::As_Target(cell), true); + + if (optr.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * * + * This routine is called when a construction yard needs to know what to build next. It * + * will either examine the prebuilt base list or try to figure out what to build next * + * based on the current game situation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building type class to build. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Building -- Finds a building of specified type. * + * * + * This routine is used to find a building of the specified type. This is particularly * + * useful for when some event requires a specific building instance. The nuclear missile * + * launch is a good example. * + * * + * INPUT: type -- The building type to scan for. * + * * + * zone -- The zone that the building must be located in. If no zone specific search * + * is desired, then pass ZONE_NONE. * + * * + * OUTPUT: Returns with a pointer to the building type requested. If there is no building * + * of the type requested, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + * 10/02/1995 JLB : Allows for zone specifics. * + *=============================================================================================*/ +BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Only scan if we KNOW there is at least one building of the type + ** requested. + */ + if (BQuantity[type] > 0) { + + /* + ** Search for a suitable launch site for this missile. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == type) { + if (zone == ZONE_NONE || Which_Zone(b) == zone) { + return(b); + } + } + } + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * * + * This routine is used to find a suitable building location for the building specified. * + * The auto base building logic uses this when building the base for the computer. * + * * + * INPUT: building -- Pointer to the building that needs to be placed down. * + * * + * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable * + * locations, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const +{ + assert(Houses.ID(this) == ID); + + int zonerating[ZONE_COUNT]; + struct { + int AntiAir; // Average air defense for the base. + int AntiArmor; // Average armor defense for the base. + int AntiInfantry; // Average infantry defense for the base. + } zoneinfo = {0,0,0}; + int antiair = building->Anti_Air(); + int antiarmor = building->Anti_Armor(); + int antiinfantry = building->Anti_Infantry(); + bool adj = true; + + /* + ** Never place combat buildings adjacent to each other. This is partly + ** because combat buildings don't have a bib and jamming will occur as well + ** as because spacing defensive buildings out will yield a better + ** defense. + */ + if (antiair || antiarmor || antiinfantry) { + adj = false; + } + + /* + ** Determine the average zone strengths for the base. This value is + ** used to determine what zones are considered under or over strength. + */ + for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { + zoneinfo.AntiAir += ZoneInfo[z].AirDefense; + zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense; + zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense; + } + zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH; + + /* + ** Give each zone a rating for value. The higher the value the more desirable + ** to place the specified building in that zone. Factor the average value of + ** zone defense such that more weight is given to zones that are very under + ** defended. + */ + memset(&zonerating[0], '\0', sizeof(zonerating)); + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + int diff; + + diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiair, diff); + } + + diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiarmor, diff); + } + + diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiinfantry, diff); + } + } + + /* + ** Now that each zone has been given a desirability rating, find the zone + ** with the greatest value and try to place the building in that zone. + */ + ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST); + int largest = 0; + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + if (zonerating[z] > largest) { + zone = z; + largest = zonerating[z]; + } + } + + CELL zcell = Find_Cell_In_Zone(building, zone); + if (zcell) { + return(Cell_Coord(zcell)); + } + + /* + ** Could not build in preferred zone, so try building in any zone. + */ + static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST}; + int start = Random_Pick(0, ARRAY_SIZE(_zones)-1); + for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) { + ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)]; + zcell = Find_Cell_In_Zone(building, tryzone); + if (zcell) return(zcell); + } + + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * * + * This routine will average the location of the base and record the center point. The * + * recorded center point is used to determine such things as how far the base is spread * + * out and where to protect the most. This routine should be called whenever a building * + * is created or destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/28/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Center(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** First presume that there is no base. If there is a base, then these values will be + ** properly filled in below. + */ + Center = 0; + Radius = 0; + for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { + ZoneInfo[zone].AirDefense = 0; + ZoneInfo[zone].ArmorDefense = 0; + ZoneInfo[zone].InfantryDefense = 0; + } + + /* + ** Only process the center base size/position calculation if there are buildings to + ** consider. When no buildings for this house are present, then no processing need + ** occur. + */ + if (CurBuildings > 0) { + int x = 0; + int y = 0; + int count = 0; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + + /* + ** Give more "weight" to buildings that cost more. The presumption is that cheap + ** buildings don't affect the base disposition as much as the more expensive + ** buildings do. + */ + int weight = (b->Class->Cost_Of() / 1000)+1; + for (int i = 0; i < weight; i++) { + x += Coord_X(b->Center_Coord()); + y += Coord_Y(b->Center_Coord()); + count++; + } + } + } + + /* + ** This second check for quantity of buildings is necessary because the first + ** check against CurBuildings doesn't take into account if the building is in + ** limbo, but for base calculation, the limbo state disqualifies a building + ** from being processed. Thus, CurBuildings may indicate a base, but count may + ** not match. + */ + if (count > 0) { + x /= count; + y /= count; + +#ifdef NEVER + /* + ** Bias the center of the base away from the edges of the map. + */ + LEPTON left = Cell_To_Lepton(Map.MapCellX + 10); + LEPTON top = Cell_To_Lepton(Map.MapCellY + 10); + LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10); + LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10); + if (x < left) x = left; + if (x > right) x = right; + if (y < top) y = top; + if (y > bottom) y = bottom; +#endif + + Center = XY_Coord(x, y); + } + + /* + ** If there were any buildings discovered as legal to consider as part of the base, + ** then figure out the general average radius of the building disposition as it + ** relates to the center of the base. + */ + if (count > 1) { + int radius = 0; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + radius += Distance(Center, b->Center_Coord()); + } + } + Radius = max(radius / count, 2 * CELL_LEPTON_W); + + /* + ** Determine the relative strength of each base defense zone. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + ZoneType z = Which_Zone(b); + + if (z != ZONE_NONE) { + ZoneInfo[z].ArmorDefense += b->Anti_Armor(); + ZoneInfo[z].AirDefense += b->Anti_Air(); + ZoneInfo[z].InfantryDefense += b->Anti_Infantry(); + } + } + } + + } else { + Radius = 0x0200; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Expert_AI -- Handles expert AI processing. * + * * + * This routine is called when the computer should perform expert AI processing. This * + * method of AI is categorized as an "Expert System" process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This is relatively time consuming -- call periodically. * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Expert_AI(void) +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = 0; + bool stop = false; + int time = TICKS_PER_SECOND * 10; + + /* + ** If the current enemy no longer has a base or is defeated, then don't consider + ** that house a threat anymore. Clear out the enemy record and then try + ** to find a new enemy. + */ + if (Enemy != HOUSE_NONE) { + HouseClass * h = HouseClass::As_Pointer(Enemy); + + if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) { + Enemy = HOUSE_NONE; + } + } + + /* + ** If there is no enemy assigned to this house, then assign one now. The + ** enemy that is closest is picked. However, don't pick an enemy if the + ** base has not been established yet. + */ + if (ActiveBScan && Center && Attack == 0) { + int close = 0; + HousesType enemy = HOUSE_NONE; + int maxunit = 0; + int maxinfantry = 0; + int maxvessel = 0; + int maxaircraft = 0; + int maxbuilding = 0; + int enemycount = 0; + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) { + + /* + ** Perform a special restriction check to ensure that no enemy is chosen if + ** there is even one enemy that has not established a base yet. This will + ** ensure an accurate first pick for enemy since the distance to base + ** value can be determined. + */ + if (!h->IsStarted) { + enemy = HOUSE_NONE; + break; + } + + /* + ** Keep track of the number of buildings and units owned by the + ** enemy. This is used to bring up the maximum allowed to match. + */ + maxunit += h->CurUnits; + maxbuilding += h->CurBuildings; + maxinfantry += h->CurInfantry; + maxvessel += h->CurVessels; + maxaircraft += h->CurAircraft; + enemycount++; + + /* + ** Determine a priority value based on distance to the center of the + ** candidate base. The higher the value, the better the candidate house + ** is to becoming the preferred enemy for this house. + */ + int value = ((MAP_CELL_W*2)-Distance(Center, h->Center)); + value *= 2; + + /* + ** In addition to distance, record the number of kills directed + ** against this house. The enemy that does more damage might be + ** considered a greater threat. + */ + value += h->BuildingsKilled[Class->House]*5; + value += h->UnitsKilled[Class->House]; + + /* + ** Factor in the relative sizes of the bases. An enemy that has a + ** larger base will be considered a bigger threat. Conversely, a + ** smaller base is considered a lesser threat. + */ + value += h->CurUnits - CurUnits; + value += h->CurBuildings - CurBuildings; + value += (h->CurInfantry - CurInfantry)/4; + + /* + ** Whoever last attacked is given a little more priority as + ** a potential designated enemy. + */ + if (house == LAEnemy) { + value += 100; + } + +#ifdef OBSOLETE + /* + ** Human players are a given preference as the target. + */ + if (h->IsHuman) { + value *= 2; + } +#endif + + /* + ** Compare the calculated value for this candidate house and if it is + ** greater than the previously recorded maximum, record this house as + ** the prime candidate for enemy. + */ + if (value > close) { + enemy = house; + close = value; + } + } + } + + /* + ** Record this closest enemy base as the first enemy to attack. + */ + Enemy = enemy; + + /* + ** Up the maximum allowed units and buildings to match a rough average + ** of what the enemies are allowed. + */ + if (enemycount) { + maxunit /= enemycount; + maxbuilding /= enemycount; + maxinfantry /= enemycount; + maxvessel /= enemycount; + maxaircraft /= enemycount; + } + + if (Control.MaxBuilding < maxbuilding + 10) { + Control.MaxBuilding = maxbuilding + 10; + } + if (Control.MaxUnit < maxunit + 10) { + Control.MaxUnit = maxunit + 10; + } + if (Control.MaxInfantry < maxinfantry + 10) { + Control.MaxInfantry = maxinfantry + 10; + } + if (Control.MaxVessel < maxvessel + 10) { + Control.MaxVessel = maxvessel + 10; + } + if (Control.MaxAircraft < maxaircraft + 10) { + Control.MaxAircraft = maxaircraft + 10; + } + } + + /* + ** House state transition check occurs here. Transitions that occur here are ones + ** that relate to general base condition rather than specific combat events. + ** Typically, this is limited to transitions between normal buildup mode and + ** broke mode. + */ + if (State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } else { + if (State == STATE_BUILDUP) { + if (Available_Money() < 25) { + State = STATE_BROKE; + } + } + if (State == STATE_BROKE) { + if (Available_Money() >= 25) { + State = STATE_BUILDUP; + } + } + if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) { + State = STATE_BUILDUP; + } + if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) { + State = STATE_ATTACKED; + } + } + + /* + ** Records the urgency of all actions possible. + */ + UrgencyType urgency[STRATEGY_COUNT]; + + for (StrategyType strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + urgency[strat] = URGENCY_NONE; + + switch (strat) { + case STRATEGY_BUILD_POWER: + urgency[strat] = Check_Build_Power(); + break; + + case STRATEGY_BUILD_DEFENSE: + urgency[strat] = Check_Build_Defense(); + break; + + case STRATEGY_BUILD_INCOME: + urgency[strat] = Check_Build_Income(); + break; + + case STRATEGY_FIRE_SALE: + urgency[strat] = Check_Fire_Sale(); + break; + + case STRATEGY_BUILD_ENGINEER: + urgency[strat] = Check_Build_Engineer(); + break; + + case STRATEGY_BUILD_OFFENSE: + urgency[strat] = Check_Build_Offense(); + break; + + case STRATEGY_RAISE_MONEY: + urgency[strat] = Check_Raise_Money(); + break; + + case STRATEGY_RAISE_POWER: + urgency[strat] = Check_Raise_Power(); + break; + + case STRATEGY_LOWER_POWER: + urgency[strat] = Check_Lower_Power(); + break; + + case STRATEGY_ATTACK: + urgency[strat] = Check_Attack(); + break; + + default: + urgency[strat] = URGENCY_NONE; + break; + } + } + + /* + ** Performs the action required for each of the strategies that share + ** the most urgent category. Stop processing if any strategy at the + ** highest urgency performed any action. This is because higher urgency + ** actions tend to greatly affect the lower urgency actions. + */ + for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) { + bool acted = false; + + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + if (urgency[strat] == u) { + switch (strat) { + case STRATEGY_BUILD_POWER: + acted |= AI_Build_Power(u); + break; + + case STRATEGY_BUILD_DEFENSE: + acted |= AI_Build_Defense(u); + break; + + case STRATEGY_BUILD_INCOME: + acted |= AI_Build_Income(u); + break; + + case STRATEGY_FIRE_SALE: + acted |= AI_Fire_Sale(u); + break; + + case STRATEGY_BUILD_ENGINEER: + acted |= AI_Build_Engineer(u); + break; + + case STRATEGY_BUILD_OFFENSE: + acted |= AI_Build_Offense(u); + break; + + case STRATEGY_RAISE_MONEY: + acted |= AI_Raise_Money(u); + break; + + case STRATEGY_RAISE_POWER: + acted |= AI_Raise_Power(u); + break; + + case STRATEGY_LOWER_POWER: + acted |= AI_Lower_Power(u); + break; + + case STRATEGY_ATTACK: + acted |= AI_Attack(u); + break; + + default: + break; + } + } + } + } + + return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2)); +} + + +UrgencyType HouseClass::Check_Build_Power(void) const +{ + assert(Houses.ID(this) == ID); + + fixed frac = Power_Fraction(); + UrgencyType urgency = URGENCY_NONE; + + if (frac < 1 && Can_Make_Money()) { + urgency = URGENCY_LOW; + + /* + ** Very low power condition is considered a higher priority. + */ + if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM; + + /* + ** When under attack and there is a need for power in defense, + ** then consider power building a higher priority. + */ + if (State == STATE_THREATENED || State == STATE_ATTACKED) { + if (BScan | (STRUCTF_CHRONOSPHERE)) { + urgency = URGENCY_HIGH; + } + } + + } + return(urgency); +} + + +UrgencyType HouseClass::Check_Build_Defense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that base defense + ** should be given. The more vulnerable the base is, the higher + ** the urgency this routine should return. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Offense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that offensive + ** weaponry should be given. Surplus money or a very strong + ** defense will cause the offensive urgency to increase. + */ + return(URGENCY_NONE); +} + +/* +** Determines what the attack state of the base is. The higher the state, +** the greater the immediate threat to base defense is. +*/ +UrgencyType HouseClass::Check_Attack(void) const +{ + assert(Houses.ID(this) == ID); + + if (Frame > TICKS_PER_MINUTE && Attack == 0) { + if (State == STATE_ATTACKED) { + return(URGENCY_LOW); + } + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Income(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should determine if income processing buildings + ** should be constructed and at what urgency. The lower the money, + ** the lower the refineries, or recent harvester losses should + ** cause a greater urgency to be returned. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Fire_Sale(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** If there are no more factories at all, then sell everything off because the game + ** is basically over at this point. + */ + if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Engineer(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should check to see what urgency that the production of + ** engineers should be. If a friendly building has been captured or the + ** enemy has weak defenses, then building an engineer would be a priority. + */ + return(URGENCY_NONE); +} + + +/* +** Checks to see if money is critically low and something must be done +** to immediately raise cash. +*/ +UrgencyType HouseClass::Check_Raise_Money(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + if (Available_Money() < 100) { + urgency = URGENCY_LOW; + } + if (Available_Money() < 2000 && !Can_Make_Money()) { + urgency++; + } + + return(urgency); +} + +/* +** Checks to see if power is very low and if so, a greater urgency to +** build more power is returned. +*/ +UrgencyType HouseClass::Check_Lower_Power(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power > Drain+300) { + return(URGENCY_LOW); + } + return(URGENCY_NONE); +} + +/* +** This routine determines if there is a power emergency. Such an +** emergency might require selling of structures in order to free +** up power. This might occur if the base is being attacked and there +** are defenses that require power, but are just short of having +** enough. +*/ +UrgencyType HouseClass::Check_Raise_Power(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + + if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) { +// if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) { + urgency = URGENCY_MEDIUM; + if (State == STATE_ATTACKED) { + urgency++; + } + } + return(urgency); +} + + +bool HouseClass::AI_Attack(UrgencyType ) +{ + assert(Houses.ID(this) == ID); + + bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33)); + bool forced = (CurBuildings == 0); + int index; + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * a = Aircraft.Ptr(index); + + if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) { + if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + a->Assign_Mission(MISSION_HUNT); + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * u = Units.Ptr(index); + + if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) { + if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + u->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this unit is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) { + u->ArchiveTarget = ::As_Target(Where_To_Go(u)); + } + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * i = Infantry.Ptr(index); + + if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) { + if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) { + i->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this soldier is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) { + i->ArchiveTarget = ::As_Target(Where_To_Go(i)); + } + } + } + } + Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + return(true); +} + + +/* +** Given the specified urgency, build a power structure to meet +** this need. +*/ +bool HouseClass::AI_Build_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +/* +** Given the specified urgency, build base defensive structures +** according to need and according to existing base disposition. +*/ +bool HouseClass::AI_Build_Defense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build offensive units according +** to need and according to the opponents base defenses. +*/ +bool HouseClass::AI_Build_Offense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build income producing +** structures according to need. +*/ +bool HouseClass::AI_Build_Income(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +bool HouseClass::AI_Fire_Sale(UrgencyType urgency) +{ + assert(Houses.ID(this) == ID); + + if (CurBuildings && urgency == URGENCY_CRITICAL) { + Fire_Sale(); + Do_All_To_Hunt(); + return(true); + } + return(false); +} + +/* +** Given the specified urgency, build an engineer. +*/ +bool HouseClass::AI_Build_Engineer(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, sell of some power since +** there appears to be excess. +*/ +bool HouseClass::AI_Lower_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = Find_Building(STRUCT_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + + b = Find_Building(STRUCT_ADVANCED_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * * + * This routine is called when the computer needs to raise power by selling off buildings. * + * Usually this occurs because of some catastrophe that has lowered power levels to * + * the danger zone. * + * * + * INPUT: urgency -- The urgency that the power needs to be raised. This controls what * + * buildings will be sold. * + * * + * OUTPUT: bool; Was a building sold to raise power? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Power(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_TESLA, URGENCY_HIGH} + }; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + BuildingClass * b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * * + * This routine handles the situation where the computer desperately needs cash but cannot * + * wait for normal harvesting to raise it. Buildings must be sold. * + * * + * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, * + * the more important the buildings that can be sold become. * + * * + * OUTPUT: bool; Was a building sold to raise cash? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Money(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_STORAGE,URGENCY_LOW}, + {STRUCT_REPAIR,URGENCY_LOW}, + {STRUCT_TESLA,URGENCY_MEDIUM}, + {STRUCT_HELIPAD,URGENCY_MEDIUM}, + {STRUCT_POWER,URGENCY_HIGH}, + {STRUCT_AIRSTRIP,URGENCY_HIGH}, +// {STRUCT_WEAP,URGENCY_HIGH}, +// {STRUCT_BARRACKS,URGENCY_HIGH}, +// {STRUCT_TENT,URGENCY_HIGH}, + {STRUCT_CONST,URGENCY_CRITICAL} + }; + BuildingClass * b = 0; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +#ifdef NEVER + +/*********************************************************************************************** + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * * + * This logic is used to maintain a base defense. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Base_Defense(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** Check to find if any zone of the base is over defended. Such zones should have + ** some of their defenses sold off to make better use of the money. + */ + + /* + ** Make sure that the core defense is only about 1/2 of the perimeter defense average. + */ + int average = 0; + for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { + average += ZoneInfo[z].AirDefense; + average += ZoneInfo[z].ArmorDefense; + average += ZoneInfo[z].InfantryDefense; + } + average /= (ZONE_COUNT-ZONE_NORTH); + + /* + ** If the core value is greater than the average, then sell off some of the + ** inner defensive structures. + */ + int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense; + if (core >= average) { + static StructType _stype[] = { + STRUCT_GTOWER, + STRUCT_TURRET, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_TESLA, + STRUCT_SAM + }; + BuildingClass * b; + + for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) { + b = Find_Building(_stype[index], ZONE_CORE); + if (b) { + b->Sell_Back(1); + break; + } + } + } + + /* + ** If the enemy doesn't have any offensive air capability, then sell off any + ** SAM sites. Only do this when money is moderately low. + */ + if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) { + + /* + ** Scan to find if ANY human opponents have aircraft or a helipad. If one + ** is found then consider that opponent to have a valid air threat potential. + ** Don't sell off SAM sites in that case. + */ + bool nothreat = true; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * house = HouseClass::As_Pointer(h); + + if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) { + if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) { + nothreat = false; + break; + } + } + } + } + + return(TICKS_PER_SECOND*5); +} +#endif + + +/*********************************************************************************************** + * HouseClass::AI_Building -- Determines what building to build. * + * * + * This routine handles the general case of determining what building to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + * 11/03/1996 JLB : Tries to match aircraft of enemy * + *=============================================================================================*/ +int HouseClass::AI_Building(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL && Base.House == Class->House) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + BuildStructure = node->Type; + } + } + + if (IsBaseBuilding) { + /* + ** Don't suggest anything to build if the base is already big enough. + */ + int quant = 0; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass const * hptr = HouseClass::As_Pointer(h); + + if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) { + quant = hptr->CurBuildings; + } + } + quant += Rule.BaseSizeAdd; + +// TCTC -- Should multiply largest player base by some rational number. +// if (CurBuildings >= quant) return(TICKS_PER_SECOND); + + BuildChoice.Free_All(); + BuildChoiceClass * choiceptr; + StructType stype = STRUCT_NONE; + int money = Available_Money(); + int level = Control.TechLevel; + bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0); + BuildingTypeClass const * b = NULL; + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + level = Control.TechLevel; + + /* + ** Try to build a power plant if there is insufficient power and there is enough + ** money available. + */ + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a refinery if there isn't one already available. + */ + unsigned int current = BQuantity[STRUCT_REFINERY]; + if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < Rule.RefineryLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY); + if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always make sure there is a barracks available, but only if there + ** will be sufficient money to train troopers. + */ + current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT]; + if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < Rule.BarracksLimit && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TENT); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** Try to build one dog house. + */ + current = BQuantity[STRUCT_KENNEL]; + if (current < 1 && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Try to build one gap generator. + */ + current = BQuantity[STRUCT_GAP]; + if (current < 1 && Power_Fraction() >= 1 && hasincome) { + b = &BuildingTypeClass::As_Reference(STRUCT_GAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** A source of combat vehicles is always needed, but only if there will + ** be sufficient money to build vehicles. + */ + current = BQuantity[STRUCT_WEAP]; + if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < Rule.WarLimit && (money > 2000 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_WEAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always build up some base defense. + */ + current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET]; + if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < Rule.DefenseLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + if (Percent_Chance(50)) { + b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Build some air defense. + */ + current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN]; + if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < Rule.AALimit) { + + /* + ** Building air defense only makes sense if the opponent has aircraft + ** of some kind. + */ + bool airthreat = false; + int threat_quantity = 0; + if (enemy != NULL && enemy->AScan != 0) { + airthreat = true; + threat_quantity = enemy->CurAircraft; + } + if (!airthreat) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && !Is_Ally(house) && h->AScan != 0) { + airthreat = true; + break; + } + } + } + + if (airthreat) { + + if (BQuantity[STRUCT_RADAR] == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_RADAR); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type); + } + } + } + + b = &BuildingTypeClass::As_Reference(STRUCT_SAM); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Advanced base defense would be good. + */ + current = BQuantity[STRUCT_TESLA]; + if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < Rule.TeslaLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_TESLA); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a tech center as soon as possible. + */ + current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH]; + if (current < 1) { + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** A helipad would be good. + */ + current = BQuantity[STRUCT_HELIPAD]; + if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < Rule.HelipadLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** An airstrip would be good. + */ + current = BQuantity[STRUCT_AIRSTRIP]; + if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < Rule.AirstripLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + +#ifdef OLD + /* + ** Build a repair bay if there isn't one already available. + */ + current = BQuantity[STRUCT_REPAIR]; + if (current == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR); + if (Can_Build(b, ActLike) && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } +#endif + + /* + ** Pick the choice that is the most urgent. + */ + UrgencyType best = URGENCY_NONE; + int bestindex; + for (int index = 0; index < BuildChoice.Count(); index++) { + if (BuildChoice.Ptr(index)->Urgency > best) { + bestindex = index; + best = BuildChoice.Ptr(index)->Urgency; + } + } + if (best != URGENCY_NONE) { + BuildStructure = BuildChoice.Ptr(bestindex)->Structure; + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Unit -- Determines what unit to build next. * + * * + * This routine handles the general case of determining what units to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of games frames to delay before calling this routine again.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Unit(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND); + if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND); + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. + */ + if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) { + if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= Control.TechLevel) { + BuildUnit = UNIT_HARVESTER; + return(TICKS_PER_SECOND); + } + } + + if (Session.Type == GAME_NORMAL) { + + int counter[UNIT_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)memtype)->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)memtype)->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those objects that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildUnit = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + + int counter[UNIT_COUNT]; + int total = 0; + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index); + if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) { + if (utype->PrimaryWeapon != NULL) { + counter[index] = 20; + } else { + counter[index] = 1; + } + } else { + counter[index] = 0; + } + total += counter[index]; + } + + if (total > 0) { + int choice = Random_Pick(0, total-1); + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (choice < counter[index]) { + BuildUnit = index; + break; + } + choice -= counter[index]; + } + } + } + + return(TICKS_PER_SECOND); +} + + +int HouseClass::AI_Vessel(void) +{ + assert(Houses.ID(this) == ID); + if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND); + + if (CurVessels >= Control.MaxVessel) { + return(TICKS_PER_SECOND); + } + + if (Session.Type == GAME_NORMAL) { + + int counter[VESSEL_COUNT]; + if (Session.Type == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= Control.TechLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int vindex = 0; vindex < Vessels.Count(); vindex++) { + VesselClass * unit = Vessels.Ptr(vindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + VesselType bestlist[VESSEL_COUNT]; + for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildVessel = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + BuildVessel = VESSEL_NONE; + } + + return(TICKS_PER_SECOND); +} + + + +/*********************************************************************************************** + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * * + * This routine handles the general case of determining what infantry unit to build * + * next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before being called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Infantry(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND); + if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL) { + TechnoTypeClass const * techno = 0; + int counter[INFANTRY_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0); + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type; +// counter[subtype] = 1; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = min(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + + if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) { + if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + int pick = Random_Pick(0, bestcount-1); + BuildInfantry = bestlist[pick]; + } + + } + + if (IsBaseBuilding) { + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + /* + ** This structure is used to keep track of the list of infantry types that should be + ** built. The infantry type and the value assigned to it is recorded. + */ + struct { + InfantryType Type; // Infantry type. + int Value; // Relative value assigned. + } typetrack[INFANTRY_COUNT]; + int count = 0; + int total = 0; + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= Control.TechLevel) { + typetrack[count].Value = 0; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility. + int clipindex = index; + if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT; + if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#else + if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#endif + + switch (index) { + case INFANTRY_E1: + typetrack[count].Value = 3; + break; + + case INFANTRY_E2: + typetrack[count].Value = 5; + break; + + case INFANTRY_E3: + typetrack[count].Value = 2; + break; + + case INFANTRY_E4: + typetrack[count].Value = 5; + break; + + case INFANTRY_RENOVATOR: + if (CurInfantry > 5) { + typetrack[count].Value = 1 - max(IQuantity[index], 0); + } + break; + + case INFANTRY_TANYA: + typetrack[count].Value = 1 - max(IQuantity[index], 0); + break; + + default: + typetrack[count].Value = 0; + break; + } + } + + if (typetrack[count].Value > 0) { + typetrack[count].Type = index; + total += typetrack[count].Value; + count++; + } + } + } + + /* + ** If there is at least one choice, then pick it. The object picked + ** is influenced by the weight (value) assigned to it. This is accomplished + ** by picking a number between 0 and the total weight value. The appropriate + ** infantry object that matches the number picked is then selected to be built. + */ + if (count > 0) { + int pick = Random_Pick(0, total-1); + for (int index = 0; index < count; index++) { + if (pick < typetrack[index].Value) { + BuildInfantry = typetrack[index].Type; + break; + } + pick -= typetrack[index].Value; + } + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * * + * This routine is used to determine the general case of what aircraft to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frame to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Aircraft(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsHuman && IQ >= Rule.IQAircraft) { + if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND); + if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND); + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_LONGBOW; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_HIND; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_MIG; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_YAK; + return(TICKS_PER_SECOND); + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::Production_Begun -- Records that production has begun. * + * * + * This routine is used to inform the Expert System that production of the specified object * + * has begun. This allows the AI to proceed with picking another object to begin production * + * on. * + * * + * INPUT: product -- Pointer to the object that production has just begun on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Production_Begun(TechnoClass const * product) +{ + assert(Houses.ID(this) == ID); + + if (product != NULL) { + switch (product->What_Am_I()) { + case RTTI_UNIT: + if (*((UnitClass*)product) == BuildUnit) { + BuildUnit = UNIT_NONE; + } + break; + + case RTTI_VESSEL: + if (*((VesselClass*)product) == BuildVessel) { + BuildVessel = VESSEL_NONE; + } + break; + + case RTTI_INFANTRY: + if (*((InfantryClass*)product) == BuildInfantry) { + BuildInfantry = INFANTRY_NONE; + } + break; + + case RTTI_BUILDING: + if (*((BuildingClass*)product) == BuildStructure) { + BuildStructure = STRUCT_NONE; + } + break; + + case RTTI_AIRCRAFT: + if (*((AircraftClass*)product) == BuildAircraft) { + BuildAircraft = AIRCRAFT_NONE; + } + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * * + * This routine informs the Expert System that the specified object is no longer part of * + * this house's inventory. This occurs when the object is destroyed or captured. * + * * + * INPUT: techno -- Pointer to the object to remove from the tracking systems of this * + * house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Remove(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + int type; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings--; + BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_AIRCRAFT: + CurAircraft--; + AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_INFANTRY: + CurInfantry--; + if (!((InfantryClass *)techno)->IsTechnician) { + type = ((InfantryTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT; +#endif + IQuantity[type]--; + } + break; + + case RTTI_UNIT: + CurUnits--; + type = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT; +#endif + UQuantity[type]--; + break; + + case RTTI_VESSEL: + CurVessels--; + type = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT; +#endif + VQuantity[type]--; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * * + * This function is called when the specified object is now available as part of the house's* + * inventory. This occurs when the object is newly produced and also when it is captured * + * by this house. * + * * + * INPUT: techno -- Pointer to the object that is now part of the house inventory. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Add(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + StructType building; + AircraftType aircraft; + InfantryType infantry; + UnitType unit; + VesselType vessel; + int quant; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings++; + building = ((BuildingTypeClass const &)techno->Class_Of()).Type; + BQuantity[building]++; + BScan |= (1L << building); + if (Session.Type == GAME_INTERNET) { + BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_AIRCRAFT: + CurAircraft++; + aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type; + AQuantity[aircraft]++; + AScan |= (1L << aircraft); + if (Session.Type == GAME_INTERNET) { + AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_INFANTRY: + CurInfantry++; + infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type; + if (!((InfantryClass *)techno)->IsTechnician) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = infantry; + if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT; + IQuantity[quant]++; +#else + IQuantity[infantry]++; +#endif + if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) { + InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + IScan |= (1L << infantry); + } + break; + + case RTTI_UNIT: + CurUnits++; + unit = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = unit; + if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT; + UQuantity[quant]++; +#else + UQuantity[unit]++; +#endif + UScan |= (1L << unit); + if (Session.Type == GAME_INTERNET) { + UnitTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_VESSEL: + CurVessels++; + vessel = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = vessel; + if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT; + VQuantity[quant]++; +#else + VQuantity[vessel]++; +#endif + VScan |= (1L << vessel); + if (Session.Type == GAME_INTERNET) { + VesselTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * * + * Use this routine to fetch a pointer to the variable that holds the number of factories * + * that can produce the specified object type. This is a helper routine used when * + * examining the number of factories as well as adjusting their number. * + * * + * INPUT: rtti -- The RTTI of the object that could be produced. * + * * + * OUTPUT: Returns with the number of factories owned by this house that could produce the * + * object of the type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int * HouseClass::Factory_Counter(RTTIType rtti) +{ + switch (rtti) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitFactories); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselFactories); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftFactories); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryFactories); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingFactories); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * * + * This routine will recognize the specified object as having been removed from active * + * duty. * + * * + * INPUT: techno -- Pointer to the object to remove from active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Remove(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr - 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * * + * This routine will recognize the specified object as having entered active duty. Any * + * abilities granted to the house by that object are now available. * + * * + * INPUT: techno -- Pointer to the object that is entering active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Add(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr + 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * * + * This routine will determine what zone the specified coordinate lies in with respect to * + * this house's base. A location that is too distant from the base, even though it might * + * be a building, is not considered part of the base and returns ZONE_NONE. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * OUTPUT: Returns with the base zone that the specified coordinate lies in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + if (coord == 0) return(ZONE_NONE); + + int distance = Distance(Center, coord); + if (distance <= Radius) return(ZONE_CORE); + if (distance > Radius*4) return(ZONE_NONE); + + DirType facing = Direction(Center, coord); + if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH); + if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST); + if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH); + return(ZONE_WEST); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * * + * Use this routine to determine what zone the specified object lies in. * + * * + * INPUT: object -- Pointer to the object that will be checked for zone occupation. * + * * + * OUTPUT: Returns with the base zone that the object lies in. For objects that are too * + * distant from the center of the base, ZONE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (!object) return(ZONE_NONE); + return(Which_Zone(object->Center_Coord())); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * * + * This routine is used to determine what base zone the specified cell is in. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far * + * away. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(CELL cell) const +{ + assert(Houses.ID(this) == ID); + + return(Which_Zone(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * * + * This routine will go through all game objects and reset the existence bits for the * + * owning house. This method ensures that if the object exists, then the corresponding * + * existence bit is also set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Attributes(void) +{ + /* + ** Clear out all tracking values that will be filled in by this + ** routine. This allows the filling in process to not worry about + ** old existing values. + */ + for (int index = 0; index < Houses.Count(); index++) { + HouseClass * house = Houses.Ptr(index); + + if (house != NULL) { + house->BScan = 0; + house->ActiveBScan = 0; + house->IScan = 0; + house->ActiveIScan = 0; + house->UScan = 0; + house->ActiveUScan = 0; + house->AScan = 0; + house->ActiveAScan = 0; + house->VScan = 0; + house->ActiveVScan = 0; + } + } + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + unit->House->UScan |= (1L << unit->Class->Type); + if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + if (!unit->IsInLimbo) { + unit->House->ActiveUScan |= (1L << unit->Class->Type); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + infantry->House->IScan |= (1L << infantry->Class->Type); + if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + if (!infantry->IsInLimbo) { + infantry->House->ActiveIScan |= (1L << infantry->Class->Type); + infantry->House->OldIScan |= (1L << infantry->Class->Type); + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + aircraft->House->AScan |= (1L << aircraft->Class->Type); + if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + if (!aircraft->IsInLimbo) { + aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type); + aircraft->House->OldAScan |= (1L << aircraft->Class->Type); + } + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->Class->Type < 32) { + building->House->BScan |= (1L << building->Class->Type); + if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + if (!building->IsInLimbo) { + building->House->ActiveBScan |= (1L << building->Class->Type); + building->House->OldBScan |= (1L << building->Class->Type); + } + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + vessel->House->VScan |= (1L << vessel->Class->Type); + if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) { + if (!vessel->IsInLimbo) { + vessel->House->ActiveVScan |= (1L << vessel->Class->Type); + vessel->House->OldVScan |= (1L << vessel->Class->Type); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * * + * This routine is used to find the cell that is closest to the center point of the * + * zone specified. Typical use of this routine is for building and unit placement so that * + * they can "cover" the specified zone. * + * * + * INPUT: zone -- The zone that the center point is to be returned. * + * * + * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Zone_Cell(ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + switch (zone) { + case ZONE_CORE: + return(Coord_Cell(Center)); + + case ZONE_NORTH: + return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3))); + + case ZONE_EAST: + return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3))); + + case ZONE_WEST: + return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3))); + + case ZONE_SOUTH: + return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * * + * This function is called for every new unit produced or delivered in order to determine * + * where the unit should "hang out" to await further orders. The best area for the * + * unit to loiter is returned as a cell location. * + * * + * INPUT: object -- Pointer to the object that needs to know where to go. * + * * + * OUTPUT: Returns with the cell that the unit should move to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + * 11/04/1996 JLB : Simplified to use helper functions * + *=============================================================================================*/ +CELL HouseClass::Where_To_Go(FootClass const * object) const +{ + assert(Houses.ID(this) == ID); + assert(object != NULL); + + ZoneType zone; // The zone that the object should go to. + if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) { + zone = ZONE_CORE; + } else { + zone = Random_Pick(ZONE_NORTH, ZONE_WEST); + } + + CELL cell = Random_Cell_In_Zone(zone); + assert(cell != 0); + + return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL)); +} + + +/*********************************************************************************************** + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * * + * This routine is used to find targets out in the field and away from base defense. * + * Typical of this would be the attack helicopters and the roving attack bands of * + * hunter killers. * + * * + * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. * + * * + * OUTPUT: Returns with a suitable target to attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + UnitClass * best = 0; + int value = 0; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) { + int val = Distance(coord, unit->Center_Coord()); + + if (unit->Anti_Air()) val *= 2; + + if (*unit == UNIT_HARVESTER) val /= 2; + + if (value == 0 || val < value) { + value = val; + best = unit; + } + } + } + if (best) { + return(best->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * * + * Call this routine to fetch the total quantity of aircraft of the type specified that is * + * owned by this house. * + * * + * INPUT: aircraft -- The aircraft type to check the quantity of. * + * * + * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this * + * house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(AircraftType aircraft) +{ + return(AQuantity[aircraft]); +} + + +/*********************************************************************************************** + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * * + * This is the counterpart to the Set_Factory function. It will return with a factory * + * pointer that is associated with the object type specified. * + * * + * INPUT: rtti -- The RTTI of the object type to find the factory for. * + * * + * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the * + * object type specified. * + * * + * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy * + * producing another unit of that category. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const +{ + int factory_index = -1; + + switch (rtti) { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = InfantryFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = AircraftFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = VesselFactory; + break; + + default: + factory_index = -1; + break; + } + + /* + ** Fetch the actual pointer to the factory object. If there is + ** no object factory that matches the specified rtti type, then + ** null is returned. + */ + if (factory_index != -1) { + return(Factories.Raw_Ptr(factory_index)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * * + * Call this routine when a factory has been created and it now must be passed on to the * + * house for tracking purposes. The house maintains several factory pointers and this * + * routine will ensure that the factory pointer gets stored correctly. * + * * + * INPUT: rtti -- The RTTI of the object the factory it to manufacture. * + * * + * factory -- The factory object pointer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory) +{ + int * factory_index = 0; + + assert(rtti != RTTI_NONE); + + switch (rtti) { + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = &UnitFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = &InfantryFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = &VesselFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = &BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = &AircraftFactory; + break; + } + + assert(factory_index != NULL); + + /* + ** Assign the factory to the appropriate slot. For the case of clearing + ** the factory out, then -1 is assigned. + */ + if (factory != NULL) { + *factory_index = factory->ID; + } else { + *factory_index = -1; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * * + * This routine will count the number of factories owned by this house that can build * + * objects of the specified type. * + * * + * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. * + * * + * OUTPUT: Returns with the number of factories that can build the object type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Factory_Count(RTTIType rtti) const +{ + int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti); + if (ptr != NULL) { + return(*ptr); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * * + * This will return the total number of buildings of that type owned by this house. * + * * + * INPUT: building -- The building type to check. * + * * + * OUTPUT: Returns with the number of buildings of that type owned by this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(StructType building) +{ + return(BQuantity[building]); +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +void HouseClass::Read_INI(CCINIClass & ini) +{ + HouseClass * p; // Pointer to current player data. + char const * hname; // Pointer to house name. + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + + p = new HouseClass(index); + p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario); + p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding); + p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit); + p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry); + p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel); + if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit; + p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100; + p->Credits = p->Control.InitialCredits; + + int iq = ini.Get_Int(hname, "IQ", 0); + if (iq > Rule.MaxIQ) iq = 1; + p->IQ = p->Control.IQ = iq; + + p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH); + p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false); + + int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL)); + p->Make_Ally(index); + p->Make_Ally(HOUSE_NEUTRAL); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if ((owners & (1 << h)) != 0) { + p->Make_Ally(h); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * * + * This routine will write out all data necessary to recreate it in anticipation of a * + * new scenario. All houses (that are active) will have their scenario type data written * + * out. * + * * + * INPUT: ini -- Reference to the INI database to write the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Write_INI(CCINIClass & ini) +{ + /* + ** The identity house control object. Only if the house value differs from the + ** identity, will the data be written out. + */ + HouseStaticClass control; + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p != NULL) { + char const * name = p->Class->IniName; + + ini.Clear(name); + if (i >= HOUSE_MULTI1) continue; + + if (p->Control.InitialCredits != control.InitialCredits) { + ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100)); + } + + if (p->Control.Edge != control.Edge) { + ini.Put_SourceType(name, "Edge", p->Control.Edge); + } + + if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) { + ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit); + } + + if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) { + ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry); + } + + if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) { + ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding); + } + + if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) { + ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel); + } + + if (p->Control.TechLevel != control.TechLevel) { + ini.Put_Int(name, "TechLevel", p->Control.TechLevel); + } + + if (p->Control.IQ != control.IQ) { + ini.Put_Int(name, "IQ", p->Control.IQ); + } + + if (p->IsPlayerControl != false && p != PlayerPtr) { + ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl); + } + + ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL))); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * * + * This routine will examine the current yak and mig situation verses airfields. If there * + * are equal aircraft to airfields, then this routine will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_No_YakMig(void) const +{ + int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG]; + + /* + ** Adjust the quantity down one if there is an aircraft in production. This will + ** allow production to resume after being held. + */ + FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT); + if (factory != NULL && factory->Get_Object() != NULL) { + AircraftClass const * air = (AircraftClass const *)factory->Get_Object(); + if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) { + quantity -= 1; + } + } + + if (quantity >= BQuantity[STRUCT_AIRSTRIP]) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * * + * This is a special hack check routine to see if the object type and id specified is * + * prevented from being produced. The Yak and the Mig are so prevented if there would be * + * insufficient airfields for them to land upon. * + * * + * INPUT: rtti -- The RTTI type of the value specified. * + * * + * value -- The type number (according to the RTTI type specified). * + * * + * OUTPUT: bool; Is production of this object prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const +{ + if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) { + return(Is_No_YakMig()); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * * + * This routine will sell back all buildings owned by this house. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a fire sale performed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Fire_Sale(void) +{ + if (CurBuildings > 0) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) { + b->Sell_Back(1); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * * + * This routine will cause all combatants of this house to go into hunt mode. The effect of * + * this is to throw everything this house has to muster at the enemies of this house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 10/02/1996 JLB : Handles aircraft too. * + *=============================================================================================*/ +void HouseClass::Do_All_To_Hunt(void) const +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == this && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + + if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) { + if (vessel->Team) vessel->Team->Remove(vessel); + vessel->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) { + if (aircraft->Team) aircraft->Team->Remove(aircraft); + aircraft->Assign_Mission(MISSION_HUNT); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * * + * Use this routine to determine if this house is legally allowed to ally with the * + * house specified. There are many reason why an alliance is not allowed. Typically, this * + * is when there would be no more opponents left to fight or if this house has been * + * defeated. * + * * + * INPUT: house -- The house that alliance with is desired. * + * * + * OUTPUT: bool; Is alliance with the house specified prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Allowed_To_Ally(HousesType house) const +{ + /* + ** Is not allowed to ally with a house that is patently invalid, such + ** as one that is illegally defined. + */ + if (house == HOUSE_NONE) { + return(false); + } + + /* + ** One cannot ally twice with the same house. + */ + if (Is_Ally(house)) { + return(false); + } + + /* + ** If the scenario is being set up, then alliances are always + ** allowed. No further checking is required. + */ + if (ScenarioInit) { + return(true); + } + + /* + ** Alliances (outside of scneario init time) are allowed only if + ** this is a multiplayer game. Otherwise, they are prohibited. + */ + if (Session.Type == GAME_NORMAL) { + return(false); + } + + /* + ** When the house is defeated, it can no longer make alliances. + */ + if (IsDefeated) { + return(false); + } + +#ifdef FIXIT_VERSION_3 + // Fix to prevent ally with computer. + if ( !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#else // FIXIT_VERSION_3 +#ifdef FIXIT_NO_COMP_ALLY + // Fix to prevent ally with computer. + if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#endif +#endif // FIXIT_VERSION_3 + + /* + ** Count the number of active houses in the game as well as the + ** number of existing allies with this house. + */ + int housecount = 0; + int allycount = 0; + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr = HouseClass::As_Pointer(house2); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) { + housecount++; + if (Is_Ally(hptr)) { + allycount++; + } + } + } + + /* + ** Alliance is not allowed if there wouldn't be any enemies left to + ** fight. + */ + if (housecount == allycount+1) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * * + * This routine will cause the computer players to become suspicious of the human * + * players and thus the computer players will band together in order to defeat the * + * human players. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Computer_Paranoid(void) +{ + + /* + ** Loop through every computer controlled house and make allies with all other computer + ** controlled houses and then make enemies with all other human controlled houses. + */ + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) { + hptr->IsParanoid = true; + + /* + ** Break alliance with every human it is allied with and make friends with + ** any other computer players. + */ + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr2 = HouseClass::As_Pointer(house2); + if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) { + if (hptr2->IsHuman) { + hptr->Make_Enemy(house2); + } else { + hptr->Make_Ally(house2); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * * + * This routine will update the power output value of the house. It will cause any buildgins* + * that need to be redrawn to do so. * + * * + * INPUT: adjust -- The amount to adjust the power output value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Power(int adjust) +{ + Power += adjust; + + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * * + * This routine will update the drain value of the house. It will cause any buildings that * + * need to be redraw to do so. * + * * + * INPUT: adjust -- The amount to adjust the drain (positive means more drain). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Drain(int adjust) +{ + Drain += adjust; + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/11/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Update_Spied_Power_Plants(void) +{ + int count = CurrentObject.Count(); + if (count) { + for (int index = 0; index < count; index++) { + ObjectClass const * tech = CurrentObject[index]; + if (tech && tech->What_Am_I()==RTTI_BUILDING) { + BuildingClass *bldg = (BuildingClass *)tech; + if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) { + if ( bldg->SpiedBy & (1<<(PlayerPtr->Class->House)) ) { + bldg->Mark(MARK_CHANGE); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * * + * Use this routine to determine where the specified object should go if it were to go * + * some random (but legal) location within the zone specified. * + * * + * INPUT: techno -- The object that is desirous of going into the zone specified. * + * * + * zone -- The zone to find a location within. * + * * + * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If * + * no valid location could be found, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + * 11/04/1996 JLB : Not so strict on zone requirement. * + *=============================================================================================*/ +CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const +{ + if (techno == NULL) return(0); + + int bestval = -1; + int bestcell = 0; + TechnoTypeClass const * ttype = techno->Techno_Type_Class(); + + /* + ** Pick a random location within the zone specified. + */ + CELL trycell = Random_Cell_In_Zone(zone); + + short const * list = NULL; + if (techno->What_Am_I() == RTTI_BUILDING) { + list = techno->Occupy_List(true); + } + + /* + ** Find a legal placement position as close as possible to the picked location while still + ** remaining within the zone. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { +// if (Map.In_Radar(cell)) { + if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) { + bool ok = ttype->Legal_Placement(cell); + + /* + ** Another (adjacency) check is required for buildings. + */ + if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) { + ok = false; + } + + if (ok) { + int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell)); + if (bestval == -1 || dist < bestval) { + bestval = dist; + bestcell = cell; + } + } + } + } + + /* + ** Return the best location to move to. + */ + return(bestcell); +} + + +/*********************************************************************************************** + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * * + * This routine will pick a random cell within the zone specified. The pick will be * + * clipped to the map edge when necessary. * + * * + * INPUT: zone -- The zone to pick a cell from. * + * * + * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the * + * map, then a cell in the core zone is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/04/1996 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const +{ + COORDINATE coord = 0; + int maxdist = 0; + switch (zone) { + case ZONE_CORE: + coord = Coord_Scatter(Center, Random_Pick(0, Radius), true); + break; + + case ZONE_NORTH: + maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_EAST: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_SOUTH: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_WEST: + maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + } + + /* + ** Double check that the location is valid and if so, convert it into a cell + ** number. + */ + CELL cell; + if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) { + if (zone == ZONE_CORE) { + + /* + ** Finding a cell within the core failed, so just pick the center + ** cell. This cell is guaranteed to be valid. + */ + cell = Coord_Cell(Center); + } else { + + /* + ** If the edge fails, then try to find a cell within the core. + */ + cell = Random_Cell_In_Zone(ZONE_CORE); + } + } else { + cell = Coord_Cell(coord); + } + + /* + ** If the randomly picked location is not in the legal map area, then clip it to + ** the legal map area. + */ + if (!Map.In_Radar(cell)) { + int x = Cell_X(cell); + int y = Cell_Y(cell); + + if (x < Map.MapCellX) x = Map.MapCellX; + if (y < Map.MapCellY) y = Map.MapCellY; + if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1; + if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1; + cell = XY_Cell(x, y); + } + return(cell); +} diff --git a/CODE/HOUSE.H b/CODE/HOUSE.H new file mode 100644 index 0000000..311f03c --- /dev/null +++ b/CODE/HOUSE.H @@ -0,0 +1,915 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HOUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : May 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HOUSE_H +#define HOUSE_H + +#include "type.h" +#include "region.h" +#include "vector.h" + +class TriggerClass; +class FootClass; +class FactoryClass; + +#define HOUSE_NAME_MAX 12 + + +/**************************************************************************** +** Certain aspects of the house "country" are initially set by the scenario +** control file. This information is static for the duration of the current +** scenario, but is dynamic between scenarios. As such, it can't be placed in +** the static HouseTypeClass structure, but is embedded into the house +** class instead. +*/ +class HouseStaticClass { + public: + HouseStaticClass(void); + HouseStaticClass(NoInitClass const & ) {}; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value is zero is presumed for human controlled houses. + */ + int IQ; + + /* + ** This is the buildable tech level for this house. This value is used + ** for when the computer is deciding what objects to build. + */ + int TechLevel; + + /* + ** This is the original ally specification to use at scenario + ** start. Various forces during play may adjust the ally state + ** of this house. + */ + int Allies; + + /* + ** This is the maximum number allowed to be built by this house. The + ** value depends on the scenario being played. + */ + unsigned MaxUnit; + unsigned MaxBuilding; + unsigned MaxInfantry; + unsigned MaxVessel; + unsigned MaxAircraft; + + /* + ** This records the initial credits assigned to this house when the scenario + ** was loaded. + */ + long InitialCredits; + + /* + ** For generic (unspecified) reinforcements, they arrive by a common method. This + ** specifies which method is to be used. + */ + SourceType Edge; +}; + + +/**************************************************************************** +** Player control structure. Each player (computer or human) has one of +** these structures associated. These are located in a global array. +*/ +class HouseClass { + public: + RTTIType RTTI; + int ID; + + /* + ** Pointer to the HouseTypeClass that this house is "owned" by. + ** All constant data for a house type is stored in that class. + */ + CCPtr Class; + + /* + ** This is the handicap (difficulty level) assigned to this house. + */ + DiffType Difficulty; + + /* + ** Override handicap control values. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + fixed RepairDelay; + fixed BuildDelay; + + /* + ** The initial house data as loaded from the scenario control file is + ** stored here. Although this data changes for each scenario, it remains + ** static for the duration of the current scenario. + */ + HouseStaticClass Control; + + /* + ** This is the house type that this house object should act like. This + ** value controls production choices and radar cover plate imagery. + */ + HousesType ActLike; + + /* + ** Is this player active? Usually that answer is true, but for civilians, it + ** might possibly be false. + */ + unsigned IsActive:1; + + /* + ** If this house is controlled by the player, then this flag will be true. The + ** computer controls all other active houses. + */ + unsigned IsHuman:1; + + /* + ** If the player can control units of this house even if the player doesn't + ** own units of this house, then this flag will be true. + */ + unsigned IsPlayerControl:1; + + /* + ** This flag enables production. If the flag is false, production is disabled. + ** By timing when this flag gets set, the player can be given some breathing room. + */ + unsigned IsStarted:1; + + /* + ** When alerted, the house will create teams of the special "auto" type and + ** will generate appropriate units to fill those team types. + */ + unsigned IsAlerted:1; + + /* + ** If automatic base building is on, then this flag will be set to true. + */ + unsigned IsBaseBuilding:1; + + /* + ** If the house has been discovered, then this flag will be set + ** to true. However, the trigger even associated with discovery + ** will only be executed during the next house AI process. + */ + unsigned IsDiscovered:1; + + /* + ** If Tiberium storage is maxed out, then this flag will be set. At some point + ** the player is told of this fact and then this flag is cleared. This allows the + ** player to be told, but only occasionally rather than continuously. + */ + unsigned IsMaxedOut:1; + + /* + ** If this house is played by a human in a multiplayer game, this flag + ** keeps track of whether this house has been defeated or not. + */ + unsigned IsDefeated:1; + + /* + ** These flags are used in conjunction with the BorrowedTime timer. When + ** that timer expires and one of these flags are set, then that event is + ** applied to the house. This allows a dramatic pause between the event + ** trigger and the result. + */ + unsigned IsToDie:1; + unsigned IsToWin:1; + unsigned IsToLose:1; + + /* + ** This flag is set when a transport carrying a civilian has been + ** successfully evacuated. It is presumed that a possible trigger + ** event will be sprung by this event. + */ + unsigned IsCivEvacuated:1; + + /* + ** If potentially something changed that might affect the sidebar list of + ** buildable objects, then this flag indicates that at the first LEGAL opportunity, + ** the sidebar will be recalculated. + */ + unsigned IsRecalcNeeded:1; + + /* + ** If the map has been completely revealed to the player, then this flag + ** will be set to true. By examining this flag, a second "reveal all map" + ** crate won't be given to the player. + */ + unsigned IsVisionary:1; + + /* + ** This flag is set to true when the house has determined that + ** there is insufficient Tiberium to keep the harvesters busy. + ** In such a case, the further refinery/harvester production + ** should cease. This is one of the first signs that the endgame + ** has begun. + */ + unsigned IsTiberiumShort:1; + + /* + ** These flags are used for the general house trigger events of being + ** spied and thieved. The appropriate flag will be set when the event + ** occurs. + */ + unsigned IsSpied:1; + unsigned IsThieved:1; + + /* + ** This flag is used to control non-human repairing of buildings. Each + ** house gets to repair one building per loop, and this flag controls + ** whether this house has 'spent' its repair option this time through. + */ + unsigned DidRepair:1; + + /* + ** This flag is used to control whether or not this house has the GPS + ** satellite in orbit. If the satellite's there, they have unlimited + ** radar and the map is fully revealed. + */ + unsigned IsGPSActive:1; + + /* + ** If the JustBuilt??? variable has changed, then this flag will + ** be set to true. + */ + unsigned IsBuiltSomething:1; + + /* + ** Did this house lose via resignation? + */ + unsigned IsResigner:1; + + /* + ** Did this house lose because the player quit? + */ + unsigned IsGiverUpper:1; + + /* + ** If this computer controlled house has reason to be mad at humans, + ** then this flag will be true. Such a condition prevents alliances with + ** a human and encourages the computers players to ally amongst themselves. + */ + unsigned IsParanoid:1; + + /* + ** A gap generator shrouded cells and all units of this house must perform + ** a look just in case their look radius intersects the shroud area. + */ + unsigned IsToLook:1; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value of zero indicates that the player controls everything. + */ + int IQ; + + /* + ** This records the current state of the base. This state is used to control + ** what action the base will perform and directly affects production and + ** unit disposition. The state will change according to time and combat + ** events. + */ + StateType State; + + /* + ** These super weapon control objects are used to control the recharge + ** and availability of these special weapons to this house. + */ + SuperClass SuperWeapon[SPC_COUNT]; + + /* + ** This is a record of the last building that was built. For buildings that + ** were built as a part of scenario creation, it will be the last one + ** discovered. + */ + StructType JustBuiltStructure; + InfantryType JustBuiltInfantry; + UnitType JustBuiltUnit; + AircraftType JustBuiltAircraft; + VesselType JustBuiltVessel; + + /* + ** This records the number of triggers associated with this house that are + ** blocking a win condition. A win will only occur if all the blocking + ** triggers have been deleted. + */ + int Blockage; + + /* + ** For computer controlled houses, there is an artificial delay between + ** performing repair actions. This timer regulates that delay. If the + ** timer has not expired, then no repair initiation is allowed. + */ + CDTimerClass RepairTimer; + + /* + ** This timer controls the computer auto-attack logic. When this timer expires + ** and the house has been alerted, then it will create a set of attack + ** teams. + */ + CDTimerClass AlertTime; + + /* + ** This timer is used to handle the delay between some catastrophic + ** event trigger and when it is actually carried out. + */ + CDTimerClass BorrowedTime; + + /* + ** This is the last working scan bits for buildings. If a building is + ** active and owned by this house, it will have a bit set in this element + ** that corresponds to the building type number. Since this value is + ** accumulated over time, the "New" element contains the under-construction + ** version. + */ + unsigned long BScan; + unsigned long ActiveBScan; + unsigned long OldBScan; + + /* + ** This is the last working scan bits for units. For every existing unit + ** type owned by this house, a corresponding bit is set in this element. As + ** the scan bits are being constructed, they are built into the "New" element + ** and then duplicated into the regular element at the end of every logic cycle. + */ + unsigned long UScan; + unsigned long ActiveUScan; + unsigned long OldUScan; + + /* + ** Infantry type existence bits. Similar to unit and building bits. + */ + unsigned long IScan; + unsigned long ActiveIScan; + unsigned long OldIScan; + + /* + ** Aircraft type existence bits. Similar to unit and building bits. + */ + unsigned long AScan; + unsigned long ActiveAScan; + unsigned long OldAScan; + + /* + ** Vessel type existence bits. Similar to unit and building bits. + */ + unsigned long VScan; + unsigned long ActiveVScan; + unsigned long OldVScan; + + /* + ** Record of gains and losses for this house during the course of the + ** scenario. + */ + unsigned CreditsSpent; + unsigned HarvestedCredits; + int StolenBuildingsCredits; + + /* + ** This is the running count of the number of units owned by this house. This + ** value is used to keep track of ownership limits. + */ + unsigned CurUnits; + unsigned CurBuildings; + unsigned CurInfantry; + unsigned CurVessels; + unsigned CurAircraft; + + /* + ** This is the running total of the number of credits this house has accumulated. + */ + long Tiberium; + long Credits; + long Capacity; + + /* + ** Stuff to keep track of the total number of units built by this house. + */ + UnitTrackerClass * AircraftTotals; + UnitTrackerClass * InfantryTotals; + UnitTrackerClass * UnitTotals; + UnitTrackerClass * BuildingTotals; + UnitTrackerClass * VesselTotals; + + /* + ** Total number of units destroyed by this house + */ + UnitTrackerClass * DestroyedAircraft; + UnitTrackerClass * DestroyedInfantry; + UnitTrackerClass * DestroyedUnits; + UnitTrackerClass * DestroyedBuildings; + UnitTrackerClass * DestroyedVessels; + + /* + ** Total number of enemy buildings captured by this house + */ + UnitTrackerClass * CapturedBuildings; + + /* + ** Total number of crates found by this house + */ + UnitTrackerClass * TotalCrates; + + /* + ** Records the number of infantry and vehicle factories active. This value is + ** used to regulate the speed of production. + */ + int AircraftFactories; + int InfantryFactories; + int UnitFactories; + int VesselFactories; + int BuildingFactories; + + /* + ** This is the accumulation of the total power and drain factors. From these + ** values a ratio can be derived. This ratio is used to control the rate + ** of building decay. + */ + int Power; // Current power output. + int Drain; // Power consumption. + + /* + ** For human controlled houses, only one type of unit can be produced + ** at any one instant. These factory objects control this production. + */ + int AircraftFactory; + int InfantryFactory; + int UnitFactory; + int VesselFactory; + int BuildingFactory; + + /* + ** This target value specifies where the flag is located. It might be a cell + ** or it might be an object. + */ + TARGET FlagLocation; + + /* + ** This is the flag-home-cell for this house. This is where we must bring + ** another house's flag back to, to defeat that house. + */ + CELL FlagHome; + + /* + ** For multiplayer games, each house needs to keep track of how many + ** objects of each other house they've killed. + */ + unsigned UnitsKilled[HOUSE_COUNT]; + unsigned UnitsLost; + unsigned BuildingsKilled[HOUSE_COUNT]; + unsigned BuildingsLost; + + /* + ** This keeps track of the last house to destroy one of my units. + ** It's used for scoring multiplayer games. + */ + HousesType WhoLastHurtMe; + + /* + ** This records information about the location and size of + ** the base. + */ + COORDINATE Center; // Center of the base. + int Radius; // Average building distance from center (leptons). + struct { + int AirDefense; + int ArmorDefense; + int InfantryDefense; + } ZoneInfo[ZONE_COUNT]; + + /* + ** This records information about the last time a building of this + ** side was attacked. This information is used to determine proper + ** response. + */ + int LATime; // Time of attack. + RTTIType LAType; // Type of attacker. + ZoneType LAZone; // Last zone that was attacked. + HousesType LAEnemy; // Owner of attacker. + + /* + ** This target value is the building that must be captured as soon as possible. + ** Typically, this will be one of the buildings of this house that has been + ** captured and needs to be recaptured. + */ + TARGET ToCapture; + + /* + ** This value shows who is spying on this house's radar facilities. + ** This is used for the other side to be able to update their radar + ** map based on the cells that this house's units reveal. + */ + int RadarSpied; + + /* + ** Running score, based on units destroyed and units lost. + */ + int PointTotal; + + /* + ** This is the targeting directions for when this house gets a + ** special weapon. + */ + QuarryType PreferredTarget; + + private: + /* + ** Tracks number of each building type owned by this house. Even if the + ** building is in construction, it will be reflected in this total. + */ +#ifdef FIXIT_ANTS + int BQuantity[STRUCT_COUNT-3]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int UQuantity[UNIT_RA_COUNT-3]; +#else + int UQuantity[UNIT_COUNT-3]; +#endif +#else + int BQuantity[STRUCT_COUNT]; + int UQuantity[UNIT_COUNT]; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int IQuantity[INFANTRY_RA_COUNT]; +#else + int IQuantity[INFANTRY_COUNT]; +#endif + int AQuantity[AIRCRAFT_COUNT]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int VQuantity[VESSEL_RA_COUNT]; +#else + int VQuantity[VESSEL_COUNT]; +#endif + + /* + ** This timer keeps track of when an all out attack should be performed. + ** When this timer expires, send most of this house's units in an + ** attack. + */ + CDTimerClass Attack; + + public: + /* + ** This records the overriding enemy that the computer will try to + ** destroy. Typically, this is the last house to attack, but can be + ** influenced by nearness. + */ + HousesType Enemy; + + /* + ** The house expert system is regulated by this timer. Each computer controlled + ** house will process the Expert System AI at intermittent intervals. Not only will + ** this distribute the overhead more evenly, but will add variety to play. + */ + CDTimerClass AITimer; + + /* + ** For the moebius effect, this is a pointer to the unit that we + ** selected to teleport. Only one teleporter should be active per house. + */ + TARGET UnitToTeleport; + + /* + ** This elaborates the suggested objects to construct. When the specified object + ** is constructed, then this corresponding value will be reset to nill state. The + ** expert system decides what should be produced, and then records the + ** recommendation in these variables. + */ + StructType BuildStructure; + UnitType BuildUnit; + InfantryType BuildInfantry; + AircraftType BuildAircraft; + VesselType BuildVessel; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + HouseClass(HousesType house); + HouseClass(NoInitClass const & x) : Class(x), Control(x), AlertTime(x), BorrowedTime(x), Attack(x), AITimer(x), DamageTime(x), TeamTime(x), TriggerTime(x), SpeakAttackDelay(x), SpeakPowerDelay(x), SpeakMoneyDelay(x), SpeakMaxedDelay(x) {}; + ~HouseClass(void); + operator HousesType(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + CELL Random_Cell_In_Zone(ZoneType zone) const; + static void Computer_Paranoid(void); + bool Is_Allowed_To_Ally(HousesType house) const; + void Do_All_To_Hunt(void) const; + void Super_Weapon_Handler(void); + int * Factory_Counter(RTTIType rtti); + int Factory_Count(RTTIType rtti) const; + DiffType Assign_Handicap(DiffType handicap); + TARGET Find_Juicy_Target(COORDINATE coord) const; + void Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const; + CELL Where_To_Go(FootClass const * object) const; + CELL Zone_Cell(ZoneType zone) const; + ZoneType Which_Zone(COORDINATE coord) const; + ZoneType Which_Zone(ObjectClass const * object) const; + ZoneType Which_Zone(CELL cell) const; + CELL Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const; + ProdFailType Begin_Production(RTTIType type, int id); + ProdFailType Suspend_Production(RTTIType type); + ProdFailType Abandon_Production(RTTIType type); + bool Place_Object(RTTIType type, CELL cell); + bool Manual_Place(BuildingClass * builder, BuildingClass * object); + void Special_Weapon_AI(SpecialWeaponType id); + bool Place_Special_Blast(SpecialWeaponType id, CELL cell); + bool Flag_Attach(CELL cell, bool set_home = false); + bool Flag_Attach(UnitClass * object, bool set_home = false); + bool Flag_Remove(TARGET target, bool set_home = false); + void Init_Data(PlayerColorType color, HousesType house, int credits); + COORDINATE Find_Build_Location(BuildingClass * building) const; + BuildingClass * Find_Building(StructType type, ZoneType zone=ZONE_NONE) const; + char const * Name(void) const {return(Class->Name());} + + bool Fire_Sale(void); + bool Is_Hack_Prevented(RTTIType rtti, int value) const; + bool Is_No_YakMig(void) const; + int Expert_AI(void); + void Production_Begun(TechnoClass const * rtti); + void Sell_Wall(CELL cell); + bool Flag_To_Die(void); + bool Flag_To_Win(void); + bool Flag_To_Lose(void); + void Make_Ally(HousesType house); + void Make_Ally(ObjectClass * object) {if (object) Make_Ally(object->Owner());}; + void Make_Enemy(HousesType house); + void Make_Enemy(ObjectClass * object) {if (object) Make_Enemy(object->Owner());}; + bool Is_Ally(HousesType house) const; + bool Is_Ally(HouseClass const * house) const; + bool Is_Ally(ObjectClass const * object) const; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void); + bool Can_Build(RTTIType rtti, int type, HousesType house) const; + + // Factory controls. + FactoryClass * Fetch_Factory(RTTIType rtti) const; + void Set_Factory(RTTIType rtti, FactoryClass * factory); + + bool Can_Build(ObjectTypeClass const * type, HousesType house) const; + + int Get_Quantity(AircraftType aircraft); + int Get_Quantity(StructType building); + unsigned char const * Remap_Table(bool blushing=false, RemapType remap=REMAP_NORMAL) const; + + TechnoTypeClass const * Suggest_New_Object(RTTIType objectype, bool kennel=false) const; + BuildingTypeClass const * Suggest_New_Building(void) const; + void Recalc_Center(void); + bool Does_Enemy_Building_Exist(StructType) const; + void Harvested(unsigned tiberium); + void Stole(unsigned worth); + long Available_Money(void) const; + void Spend_Money(unsigned money); + void Refund_Money(unsigned money); + void Attacked(void); + void Adjust_Power(int adjust); + void Adjust_Drain(int adjust); + void Update_Spied_Power_Plants(void); + int Adjust_Capacity(int adjust, bool inanger=false); + fixed Power_Fraction(void) const; + fixed Tiberium_Fraction(void) const; + void Begin_Production(void) {IsStarted = true;}; + TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + void Adjust_Threat(int region, int threat); + void Tracking_Remove(TechnoClass const * techno); + void Tracking_Add(TechnoClass const * techno); + void Active_Remove(TechnoClass const * techno); + void Active_Add(TechnoClass const * techno); + + UrgencyType Check_Attack(void) const; + UrgencyType Check_Build_Power(void) const; + UrgencyType Check_Build_Defense(void) const; + UrgencyType Check_Build_Offense(void) const; + UrgencyType Check_Build_Income(void) const; + UrgencyType Check_Fire_Sale(void) const; + UrgencyType Check_Build_Engineer(void) const; + UrgencyType Check_Raise_Money(void) const; + UrgencyType Check_Raise_Power(void) const; + UrgencyType Check_Lower_Power(void) const; + + bool AI_Attack(UrgencyType urgency); + bool AI_Build_Power(UrgencyType urgency) const; + bool AI_Build_Defense(UrgencyType urgency) const; + bool AI_Build_Offense(UrgencyType urgency) const; + bool AI_Build_Income(UrgencyType urgency) const; + bool AI_Fire_Sale(UrgencyType urgency); + bool AI_Build_Engineer(UrgencyType urgency) const; + bool AI_Raise_Money(UrgencyType urgency) const; + bool AI_Raise_Power(UrgencyType urgency) const; + bool AI_Lower_Power(UrgencyType urgency) const; + + bool Can_Make_Money(void) const { + return(Available_Money() > 300 || (BScan & STRUCTF_REFINERY)); + }; + + static void Init(void); + static void One_Time(void); + static HouseClass * As_Pointer(HousesType house); + static void Recalc_Attributes(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static void Read_Flag_INI(char *buffer); + static void Write_Flag_INI(char *buffer); + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Special house actions. + */ + void Detach(TARGET target, bool all); + + /* + ** This vector holds the recorded status of the map regions. It is through + ** this region information that team paths are calculated. + */ + RegionClass Regions[MAP_TOTAL_REGIONS]; + + /* + ** This count down timer class decrements and then changes + ** the Atomic Bomb state. + */ + CELL NukeDest; + + /* + ** This routine completely removes this house & all its objects from the game. + */ + void Clobber_All(void); + + /* + ** This routine blows up everything in this house. Fun! + */ + void Blowup_All(void); + + /* + ** This routine gets called in multiplayer games when every unit, building, + ** and infantry for a house is destroyed. + */ + void MPlayer_Defeated(void); + + /* + ** When the game's over, this routine assigns everyone their score. + */ + void Tally_Score(void); + + friend class MapEditClass; + + private: + void Silo_Redraw_Check(long oldtib, long oldcap); + int AI_Building(void); + int AI_Unit(void); + int AI_Vessel(void); + int AI_Infantry(void); + int AI_Aircraft(void); + + /* + ** This is a bit field record of all the other houses that are allies with + ** this house. It is presumed that any house that isn't an ally, is therefore + ** an enemy. A house is always considered allied with itself. + */ + unsigned Allies; + + /* + ** General low-power related damaged is doled out whenever this timer + ** expires. + */ + CDTimerClass DamageTime; + + /* + ** Team creation is done whenever this timer expires. + */ + CDTimerClass TeamTime; + + /* + ** This controls the rate that the trigger time logic is processed. + */ + CDTimerClass TriggerTime; + + /* + ** At various times, the computer may announce the player's condition. The following + ** variables are used as countdown timers so that these announcements are paced + ** far enough apart to reduce annoyance. + */ + CDTimerClass SpeakAttackDelay; + CDTimerClass SpeakPowerDelay; + CDTimerClass SpeakMoneyDelay; + CDTimerClass SpeakMaxedDelay; + + /* + ** This structure is used to record a build request as determined by + ** the house AI processing. Higher priority build requests take precidence. + */ + struct BuildChoiceClass { + static void * operator new(size_t, void * ptr) {return(ptr);}; + UrgencyType Urgency; // The urgency of the build request. + StructType Structure; // The type of building to produce. + + BuildChoiceClass(UrgencyType u, StructType s) : Urgency(u), Structure(s) {}; + BuildChoiceClass(NoInitClass const & ) {}; + int Save(Pipe &) const {return(true);}; + int Load(Straw &) {return(true);}; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + }; + + static TFixedIHeapClass BuildChoice; + + + /* + ** These values are for multiplay only. + */ + public: + /* + ** For multiplayer games, each house instance has a remap table; the table + ** in the HousesTypeClass isn't used. This variable is set to the remap + ** table for the color the player wants to play. + */ + PlayerColorType RemapColor; + + /* + ** This is the name ("handle") the player has chosen for himself. + */ + char IniName[HOUSE_NAME_MAX]; + +#ifdef WOLAPI_INTEGRATION + // For Internet games only, unchanging name of player when game began. + // This name does not get changed to "Computer" if computer takes over for player. + char InitialName[HOUSE_NAME_MAX]; +#endif + + int QuantityB(int index) {return(BQuantity[index]);} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int QuantityU(int index) { + if(index >= UNIT_RA_COUNT) index -= UNIT_RA_COUNT; + return(UQuantity[index]); + } + int QuantityI(int index) { + if(index >= INFANTRY_RA_COUNT) index -= INFANTRY_RA_COUNT; + return(IQuantity[index]); + } + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) { + if(index >= VESSEL_RA_COUNT) index -= VESSEL_RA_COUNT; + return(VQuantity[index]); + } +#else + int QuantityU(int index) {return(UQuantity[index]);} + int QuantityI(int index) {return(IQuantity[index]);} + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) {return(VQuantity[index]);} +#endif + +}; + +#endif + diff --git a/CODE/HSV.CPP b/CODE/HSV.CPP new file mode 100644 index 0000000..02b6117 --- /dev/null +++ b/CODE/HSV.CPP @@ -0,0 +1,211 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HSV.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HSV.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * HSVClass::Set -- Set the palette for this color object. * + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "hsv.h" +#include "rgb.h" + +HSVClass const HSVClass::BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * * + * This routine will adjust the HSV color object toward the color of the specified HSV * + * object. Typical users of this would be palette morphing or fading routines. * + * * + * INPUT: ratio -- The ratio to move the HSV object toward the color specified. A value * + * of zero means no movement at all. A value of 255 means move completely * + * toward the specified color (changed completely). * + * * + * hsv -- A reference to the color that the current HSV object is to change * + * toward. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Adjust(int ratio, HSVClass const & hsv) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = hsv.Value_Component() - Value_Component(); + Value = Value_Component() + (value * ratio) / 256; + + int saturation = hsv.Saturation_Component() - Saturation_Component(); + Saturation = Saturation_Component() + (saturation * ratio) / 256; + + int hue = hsv.Hue_Component() - Hue_Component(); + Hue = Hue_Component() + (hue * ratio) / 256; +} + + +/*********************************************************************************************** + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * * + * This routine will determine a color difference between two HSV objects. The difference * + * has no particular meaning other that larger numbers meaning greater difference. * + * * + * INPUT: hsv -- The other HSV object to compare this HSV object to. * + * * + * OUTPUT: Returns with a relative distance (in arbitrary units) between this HSV object and * + * the HSV object supplied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +int HSVClass::Difference(HSVClass const & hsv) const +{ + int hue = (int)Hue - (int)hsv.Hue; + if (hue < 0) hue = -hue; + + int saturation = (int)Saturation - (int)hsv.Saturation; + if (saturation < 0) saturation = -saturation; + + int value = (int)Value - (int)hsv.Value; + if (value < 0) value = -value; + + return(hue*hue + saturation*saturation + value*value); +} + + +/*********************************************************************************************** + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * * + * This conversion operator will convert the HSV object into an RGB object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implied) of the RGBClass object that most closely * + * matches this HSVClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +HSVClass::operator RGBClass (void) const +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Temporary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + int hue = Hue_Component(); + int saturation = Saturation_Component(); + int value = Value_Component(); + int red, green, blue; + + + hue *= 6; + f = hue % 255; + + // Set up possible red, green and blue values. + values[1] = + values[2] = value; + + // + // The following lines of code change + // values[3] = (v * (255 - ( (s * f) / 255) )) / 255; + // values[4] = values[5] = (v * (255 - s)) / 255; + // values[6] = (v * (255 - (s * (255 - f)) / 255)) / 255; + // so that the are rounded divides. + // + + tmp = (saturation * f) / 255; + values[3] = (value * (255 - tmp)) / 255; + + values[4] = + values[5] = (value * (255 - saturation)) / 255; + + tmp = 255 - (saturation * (255 - f)) / 255; + values[6] = (value * tmp) / 255; + + + // This should not be rounded. + i = hue / 255; + + i += (i > 4) ? -4 : 2; + red = values[i]; + + i += (i > 4) ? -4 : 2; + blue = values[i]; + + i += (i > 4) ? -4 : 2; + green = values[i]; + + return(RGBClass(red, green, blue)); +} + + +/*********************************************************************************************** + * HSVClass::Set -- Set the palette for this color object. * + * * + * The palette will be set for this color object. Use this routine to set an arbitrary * + * color index with the HSVClass object. * + * * + * INPUT: color -- The color index to change. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Set(int color) const +{ + RGBClass rgb = *this; + rgb.Set(color); +} diff --git a/CODE/HSV.H b/CODE/HSV.H new file mode 100644 index 0000000..ec6fb05 --- /dev/null +++ b/CODE/HSV.H @@ -0,0 +1,81 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/HSV.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : HSV.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef HSV_H +#define HSV_H + +class RGBClass; +class HSVClass; + +/* +** Each color entry is represented by this class. It holds the values for the color +** attributes. The values are recorded in a range from 0 to 255 with 255 being the +** maximum. +*/ +class HSVClass +{ + private: + static HSVClass const BlackColor; + + public: + HSVClass(void) : Hue(0), Saturation(0), Value(0) {}; + HSVClass(unsigned char hue, unsigned char saturation, unsigned char value) : + Hue(hue), + Saturation(saturation), + Value(value) + {}; + operator RGBClass (void) const; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, HSVClass const & hsv); + int Difference(HSVClass const & hsv) const; + int Hue_Component(void) const {return(Hue);}; + int Saturation_Component(void) const {return(Saturation);}; + int Value_Component(void) const {return(Value);}; + void Set(int color) const; + + private: + unsigned char Hue; + unsigned char Saturation; + unsigned char Value; +}; + +#endif diff --git a/CODE/ICONLIST.CPP b/CODE/ICONLIST.CPP new file mode 100644 index 0000000..6c39f34 --- /dev/null +++ b/CODE/ICONLIST.CPP @@ -0,0 +1,910 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// Iconlist.cpp - created by ajw 07/07/98 + +// IconListClass is ListClass plus the option to include an icon on each line entry, +// the option to have the class maintain its own copies of strings passed to it +// for display, and the option to limit the maximum number of these strings that are +// kept (entries are removed from the top when this maximum is reached). +// Also added: multiple item selection capability. Note that the old selection code +// runs as normal, but it simply not used when it comes time to display. +// Also added: if mem. allocation is being done by this, the ability to break new items +// into multiple lines of text is enabled. +// Also added: extra data can be invisibly stored with each item, if memory allocation is +// being done by this. +// Extra data included 3 item preceding icons, 1 fixed position icon, an extra string, +// an extra void pointer, and a color remapping value. + +#include "iconlist.h" +#include "dibapi.h" + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +//*********************************************************************************************** +IconListClass::IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, + bool bResponsibleForStringAlloc, int iSelectionType, int iMaxItemsSaved ) : + ListClass( id, x, y, w, h, flags, up, down ) +{ + // If bResponsibleForStringAlloc, COPIES of strings are stored in the list. Deletion is + // handled by this class. Icons are different - the caller is responsible for what's on + // the other end of the pointer. + bDoAlloc = bResponsibleForStringAlloc; + // iSelectionType = 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + if( iSelectionType < 0 || iSelectionType > 2 ) iSelectionType = 1; + iSelectType = iSelectionType; + // If iMaxItemsSaved is 0, there is no limit to the number of text lines. The list can grow forever. + // Otherwise items are deleted from the head of the list when the maximum is passed. + // iMaxItemsSaved only applies when bResponsibleForStringAlloc. + iMaxItems = iMaxItemsSaved; +} + +//*********************************************************************************************** +IconListClass::~IconListClass( void ) +{ + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } +} + +/*********************************************************************************************** + * IconListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * pIcon -- Pointer to the shape to add. + * IconKind -- Indicates what type of image pIcon points to. + * szExtraDataString -- Extra string data that gets copied and stored along with item. + * szExtraDataPtr -- Extra data that gets stored along with item. + * pColorRemap -- Points to a color remapping used when drawing the item. + * + * OUTPUT: Returns new item index. * + * WARNINGS: none * + * HISTORY: 07/07/1998 ajw : Created. * + *=============================================================================================*/ +int IconListClass::Add_Item(char const * text) +{ + return Add_Item( text, NULL, NULL, ICON_SHAPE ); +} + +int IconListClass::Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + if( text ) + { + if( bDoAlloc ) + { + int iRetVal; + + char* szText = new char[ strlen( text ) + 51 ]; // 50 extra chars added for line breaks later. + strcpy( szText, text ); + + int iWidthMax, iHeight; + // Stupid usage of globals for font stuff... + if( TextFlags == TPF_TYPE ) + { + void* pFontBefore = Set_Font( TypeFontPtr ); + DWORD FontXSpacingBefore = FontXSpacing; + FontXSpacing = -2; + + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + + Set_Font( pFontBefore ); + FontXSpacing = FontXSpacingBefore; // Just in case it matters... Doubt it. + } + else + { + // Currently never called. Test well if you use IconList with a font other than TPF_TYPE, + // as the character spacing globals get set weirdly, I've found. + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + } + + // Each break character causes a line to be added to list. + char szBreakchars[] = "\r\n\v\f"; + char* szToken; + char* szNextChar = szText; + szToken = strtok( szText, szBreakchars ); + while( szToken ) + { + while( szNextChar < szToken ) + { + // We expected szToken to begin at szNextChar. Since it doesn't, extra break + // characters must have been removed by strtok as they were adjacent. We want + // a line break for every break character, so add lines for each space that + // szNextChar is off by. + szNextChar++; + Add_Item_Detail( " ", szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + } + iRetVal = Add_Item_Detail( szToken, szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + + // Expect next token two chars after the end of this one. + szNextChar = szToken + strlen( szToken ) + 1; + + // Get next token. + szToken = strtok( NULL, szBreakchars ); + } + delete [] szText; + return iRetVal; // Last value returned by ListClass::Add_Item + } + else + { + // Add one item to list. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + return ListClass::Add_Item( text ); + } + } + else + { + // (no text for new item) + if( pIcon0 || pIcon1 || pIcon2 ) + { + // Note: Cannot add an entry without text unless string allocation is being handled by me. + // Otherwise, because we want the icon to show up, create a blank entry for the ListClass. + if( bDoAlloc ) + { + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create new string, essentially blank. + char* szText = new char[2]; + strcpy( szText, " " ); + return ListClass::Add_Item( szText ); + } + else + // Cannot add entry, as text is blank and ListClass::Add_Item will do nothing. + // The Icon we want will not show up. + return List.Count() - 1; + } + else + return ListClass::Add_Item( text ); + } +} + +//*********************************************************************************************** +int IconListClass::Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, + void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ) +{ + // Broken out of above function as it is repeated. + + // Add one item to list. + // Too many entries? + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create icon entry. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; // ajw - Question: repeat the icon for each entry? make it optional? + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraData; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + // Create text entry. + // Copy text to new string. + char* szTextBit = new char[ strlen( szToken ) + 1 ]; + strcpy( szTextBit, szToken ); + return ListClass::Add_Item( szTextBit ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text ) +{ + return Add_Item( Text_String(text), NULL, NULL, ICON_SHAPE ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + return Add_Item( Text_String(text), szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, + pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( char const * text ) +{ + if( text ) + Remove_Item( List.ID(text) ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( int index ) +{ + if( (unsigned)index < List.Count() ) + { + delete (IconList_ItemExtras*)ExtrasList[ index ]; + ExtrasList.Delete( index ); + if( bDoAlloc ) + // Delete alloc'ed string. + delete [] (char*)List[index]; + ListClass::Remove_Item( index ); + + // I should probably put this in ListClass:Remove_Item(), as it seems clearly to be + // missing, but I want to only affect my own new code, to not introduce possible bugs. + // Shift the selected index if appropriate... + if( SelectedIndex >= index ) + { + SelectedIndex--; + if( SelectedIndex < 0 ) + SelectedIndex = 0; + } + } +} + +/*********************************************************************************************** + * IconListClass::Draw_Entry -- Calls ListClass::Draw_Entry, then adds icon. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 07/07/1998 ajw: Created. * + *=============================================================================================*/ + +#define PREICONGAP 1 +#define ICONTEXTGAP 2 + +void IconListClass::Draw_Entry( int index, int x, int y, int width, int selected ) +{ + IconList_ItemExtras* pExtras = (IconList_ItemExtras*)ExtrasList[ index ]; + + int xText = x; + // ajw If I end up needing to use SHAPEs for icons, figure out shape width here and offset x. + bool bIconsPresent = false; + for( int iIcon = 0; iIcon != 3; iIcon++ ) + if( pExtras->pIcon[ iIcon ] && pExtras->IconKind[ iIcon ] == ICON_DIB ) + { + // Push text over to accommodate icon. + int iWidthIcon = PREICONGAP + DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + xText += iWidthIcon; + width -= iWidthIcon; + bIconsPresent = true; + } + if( bIconsPresent ) + { + xText += ICONTEXTGAP; + width -= ICONTEXTGAP; + } + + RemapControlType* pRemap = pExtras->pColorRemap; + if( !pRemap ) + { + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + // See local version of this same hack, below. + int TempTabs; + const int* TabsSave; + if( Tabs ) + { + TempTabs = *Tabs - ( xText - x ); + TabsSave = Tabs; + Tabs = &TempTabs; + } + switch( iSelectType ) + { + case 0: + // Don't draw any items selected (even if they are, really, in ListClass). + ListClass::Draw_Entry( index, xText, y, width, false ); + break; + case 1: + ListClass::Draw_Entry( index, xText, y, width, selected ); + break; + case 2: + // Ignore 'selected' parameter. We use our own records. + ListClass::Draw_Entry( index, xText, y, width, pExtras->bMultiSelected ); + break; + } + // Restore Tabs. + if( Tabs ) + Tabs = TabsSave; + } + else + { + // Use different color remapping. + // This is largely copied straight from ListClass::Draw_Entry()... + + TextPrintType flags = TextFlags; + + bool bShowSelected; + + switch( iSelectType ) + { + case 0: + bShowSelected = false; + break; + case 1: + bShowSelected = selected; + break; + case 2: + bShowSelected = pExtras->bMultiSelected; + break; + } + + if( bShowSelected ) + { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect( xText, y, xText + width - 1, y + LineHeight - 1, pRemap->Shadow ); + } + else + { + if (!(flags & TPF_USE_GRAD_PAL)) + { + flags = flags | TPF_MEDIUM_COLOR; + } + } + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + if( Tabs ) + { + int tab = *Tabs - ( xText - x ); + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, &tab ); + } + else + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, NULL ); + } + + // Draw fixed position icon. + if( pExtras->FixedIcon.pIcon ) + { + if( pExtras->FixedIcon.IconKind == ICON_SHAPE ) + CC_Draw_Shape( pExtras->FixedIcon.pIcon, 0, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + CC_Draw_DIB( (char*)pExtras->FixedIcon.pIcon, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, pExtras->FixedIcon.iWidth, WINDOW_MAIN ); + } + + // Draw variable position left-of-text icons. + for( iIcon = 0; iIcon != 3; iIcon++ ) + { + if( pExtras->pIcon[ iIcon ] ) + { + x += PREICONGAP; + if( pExtras->IconKind[ iIcon ] == ICON_SHAPE ) + CC_Draw_Shape( pExtras->pIcon[ iIcon ], 0, x, y, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + { + CC_Draw_DIB( (char*)pExtras->pIcon[ iIcon ], x, y, 9999, WINDOW_MAIN ); + x += DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + } + } + } +} + +//*********************************************************************************************** +int IconListClass::Action(unsigned flags, KeyNumType & key) +{ + // Overriding of function is for the sake of MultiSelecting only. + if( iSelectType == 2 ) + { + if( !( flags & LEFTRELEASE ) ) + { + if( !( flags & KEYBOARD ) ) + { + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + int iSelected = CurrentTopIndex + index; + iSelected = min( iSelected, List.Count() - 1 ); + if( iSelected >= 0 ) + ((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected = + !((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected; + } + } + } + return ListClass::Action( flags, key ); +} + +//*********************************************************************************************** +// * IconListClass::Show_Last_Item -- Scrolls listbox down to ensure that last entry is visible. +// ajw 07/09/98 +void IconListClass::Show_Last_Item() +{ + int iItemLast = List.Count() - 1; + if( iItemLast - LineCount + 1 != CurrentTopIndex ) + { + Flag_To_Redraw(); + Set_View_Index( iItemLast - LineCount + 1 ); + } +} + +//*********************************************************************************************** +bool IconListClass::bItemIsMultiSelected( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected; + else + return false; +} + +//*********************************************************************************************** +void IconListClass::MultiSelect( int index, bool bSelect ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected = bSelect; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_ExtraDataString( int index ) const +{ + // Returns const pointer to the hidden "extra data" string that can be associated with each item. + // This is NULL if no extra data was assigned. + if( index < ExtrasList.Count() && index > -1 ) + { + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szExtraData; + } + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataString( int index, const char* szNewString ) +{ + if( index < ExtrasList.Count() && index > -1 ) + { + IconList_ItemExtras* pItemExtra = (IconList_ItemExtras*)ExtrasList[ index ]; + if( pItemExtra->szExtraData ) + { + // Delete the existing string. + delete [] pItemExtra->szExtraData; + } + if( szNewString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szNewString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szNewString ); + } + else + pItemExtra->szExtraData = NULL; + } +} + +//*********************************************************************************************** +void* IconListClass::Get_Item_ExtraDataPtr( int index ) const +{ + // Returns the hidden "extra data" void pointer that can be associated with each item. + // This is NULL if no value was assigned. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataPtr( int index, void* pNewValue ) +{ + // Sets the hidden "extra data" void pointer that can be associated with each item. + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData = pNewValue; +} + +//*********************************************************************************************** +const IconList_ItemExtras* IconListClass::Get_ItemExtras( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return (IconList_ItemExtras*)ExtrasList[ index ]; + else + return NULL; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_Help( int index ) const +{ + // Returns pointer to the string allocated for tooltip help. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szHelp; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Clear() +{ + // Removes all items from list. + + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + ExtrasList.Clear(); + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } + + List.Clear(); + Remove_Scroll_Bar(); + CurrentTopIndex = 0; +} + +//*********************************************************************************************** +RemapControlType* IconListClass::Get_Item_Color( int index ) +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_Color( int index, RemapControlType* pColorRemap ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap = pColorRemap; +} + +//*********************************************************************************************** +int IconListClass::Find( const char* szItemToFind ) +{ + // Returns -1 if szItemToFind is not found as the text BEGINNING one of the list entries, else index of item. + // Compare is case-sensitive. + for( int i = 0; i < List.Count(); i++ ) + { + if( strncmp( List[ i ], szItemToFind, strlen( szItemToFind ) ) == 0 ) + return i; + } + return -1; +} + +//*********************************************************************************************** +int IconListClass::FindColor( RemapControlType* pColorRemap ) +{ + // Returns -1 if no items of specified color are found, else first index. Assumes colorptr == colorptr is a valid equality test. + for( int i = 0; i < List.Count(); i++ ) + { + if( Get_Item_Color( i ) == pColorRemap ) + return i; + } + return -1; +} + +//*********************************************************************************************** +bool IconListClass::Set_Item( unsigned int index, const char* szText ) +{ + // Resets the text string allocated for an item. + if( !bDoAlloc || index >= List.Count() ) + return false; + + // Delete alloc'ed string. + delete [] (char*)List[ index ]; + + // Copy text to new string. + char* szTextNew = new char[ strlen( szText ) + 1 ]; + strcpy( szTextNew, szText ); + + // Reassign List's ptr. + List[ index ] = szTextNew; + + return true; +} + +//*********************************************************************************************** +bool IconListClass::Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ) +{ + if( index >= List.Count() ) + return false; + + // Sets one of the left-aligned icons. + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pIcon[ iIconNumber ] = pIcon; + ( (IconList_ItemExtras*)ExtrasList[ index ] )->IconKind[ iIconNumber ] = IconKind; + return true; +} + +//*********************************************************************************************** +int IconListClass::GetRealWidth() // sigh +{ + if( IsScrollActive ) + return Width + ScrollGadget.Width; + return Width; +} + +//*********************************************************************************************** +void IconListClass::Resize( int x, int y, int w, int h ) +{ + Remove_Scroll_Bar(); // If there is one. + + X = x; + Y = y; + Width = w; + Height = h; + + Set_Position( x, y ); + + LineCount = (h-1) / LineHeight; + + + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + Flag_To_Redraw(); +} + +//*********************************************************************************************** +int IconListClass::IndexUnderMouse() +{ + // Returns index of line that mouse is currently over, or -1 for mouse not hitting valid index. + // Assumes that x position of mouse is already known to be over the iconlist. + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight + CurrentTopIndex; + if( index > List.Count() - 1 || index < 0 ) + return -1; + return index; +} + +//*********************************************************************************************** +int IconListClass::OffsetToIndex( int iIndex, int y ) +{ + // Finds the current offset of item iIndex from the current top view index, in pixels, and add it to y. + return y + ( iIndex - CurrentTopIndex ) * LineHeight; +} + +//*********************************************************************************************** +//*********************************************************************************************** +// * Format_Window_String_New +// Functions like Format_Window_String except it fixes an infinite loop bug that occurred when strings +// lacked suitable break points, eliminates the '@' as an escape character, and operates differently +// in that it leaves the original string along, writing results instead to a second string parameter, +// that is iExtraChars longer than the original string. This is all a big hack so that I can insert +// extra break characters when a break in a long single word has to be made. +// Hey - it's better than an infinite loop that forces you to reset your machine, as in the original code... + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + // While the current line is less then the max length... + *szReturn = *string; + linelen += Char_Pixel_Width( *string ); + while ( linelen < maxlinelen && *string != '\r' && *string != '\0' ) + { + *++szReturn = *++string; + linelen += Char_Pixel_Width( *string ); + } + + // if the line is too long... + if (linelen >= maxlinelen) + { + /* + ** Back up to an appropriate location to break. + */ + const char* stringOverEnd = string; + while( linelen > 0 && *string != ' ' && *string != '\r' && *string != '\0' ) + { + linelen -= Char_Pixel_Width(*string--); + } + if( linelen <= 0 ) + { + // We could not find a nice break point. + // Go back one char from over-the-end point and add in a break there. + string = stringOverEnd - 1; + if( iExtraChars > 0 ) + iExtraChars--; // One less to make use of later. + else + // We've used up all our extras characters. + // Put in a break below by wiping out a valid char here. + szReturn--; + } + else + { + // Back up szReturn to same location. + szReturn -= ( stringOverEnd - string ); + } + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *szReturn++ = '\r'; + string++; + } + } + return(lines); +} + +//*********************************************************************************************** +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ) +{ + // A very basic DIB drawing routine. No clipping. No edge of window overrun checking. + // If iWidth is too large, default width of dib is used. + // If iWidth is negative, dib isn't drawn. + if( pDIB && iWidth >= 0 ) + { + + int iWidthDIB = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + const char* pBits = FindDIBBits( pDIB ); + + int iSrcPitch = ( iWidthDIB + 3 ) & ~3; + + if( iWidth > iWidthDIB ) + iWidth = iWidthDIB; + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iDestPitch = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineDest = (char*)draw_window.Get_Offset() + xDest + ( yDest + iHeight - 1 ) * iDestPitch; + + const char* pLineSrc = pBits; + for( int y = 0; y != iHeight; y++ ) + { + char* pDest = pLineDest; + const char* pSrc = pLineSrc; + for( int x = 0; x != iWidth; x++ ) + { + *pDest++ = *pSrc++; + } + pLineDest -= iDestPitch; + pLineSrc += iSrcPitch; + } + draw_window.Unlock(); + } + } +// else +// debugprint( "CC_Draw_DIB bad case ------------ pDib %i, iWidth %i\n", pDIB, iWidth ); +} + + +#endif diff --git a/CODE/ICONLIST.H b/CODE/ICONLIST.H new file mode 100644 index 0000000..7741107 --- /dev/null +++ b/CODE/ICONLIST.H @@ -0,0 +1,192 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * IconListClass -- Exactly like ListClass, but displays an icon as well + * (actually a 'shape' image), left-aligned, covering + * any text that happens to be there... + * Also, I've added the option of making this class + * responsible for the mem alloc. of the strings, and + * an automatic limiting of entries to a set maximum. + * * + * HISTORY: 07/07/1998 ajw : Created, largely in hack mode. * + *=========================================================================*/ + +#ifndef ICONLIST_H +#define ICONLIST_H + +#include "function.h" +#include "vector.h" + +enum ICONKIND +{ + ICON_SHAPE = 0, // pIcon points to a shape. + ICON_DIB // pIcon points to DIBitmap data. +}; + +struct FIXEDICON // For putting icons in list entries at a specific fixed offset. +{ + void* pIcon; + ICONKIND IconKind; + int xOffset; + int yOffset; + int iWidth; +}; + +struct IconList_ItemExtras +{ + IconList_ItemExtras() : bMultiSelected( false ), szHelp( NULL ), szExtraData( NULL ), pvExtraData( NULL ) + { + pIcon[0] = NULL; + pIcon[1] = NULL; + pIcon[2] = NULL; + } + virtual ~IconList_ItemExtras() + { + delete [] szHelp; + delete [] szExtraData; + } + + bool bMultiSelected; // True if selected when bMultiSelect is on. + void* pIcon[3]; // Icon that appears before an item. + ICONKIND IconKind[3]; // Specifies what kind of image data pIcon points to. + char* szHelp; // Tooltip help string that can be associated with item. Allocated and deleted here. + char* szExtraData; // Extra string that can be associated with item. Allocated and deleted here. + void* pvExtraData; // Hidden pointer that can be associated with item. + RemapControlType* pColorRemap; // Pointer to a color remap, or null for default colored text. + FIXEDICON FixedIcon; +}; + +class IconListClass : public ListClass +{ + public: + IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, bool bResponsibleForStringAlloc = FALSE, int iSelectionType = 1, int iMaxItemsSaved = 0 ); +// IconListClass( const IconListClass& list ); + virtual ~IconListClass( void ); + + virtual int Add_Item( char const * text ); + virtual int Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + + virtual int Add_Item( int text ); + virtual int Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + +// virtual int Add_Scroll_Bar(void); +// virtual void Bump(int up); +// virtual int Count(void) const {return List.Count();}; +// virtual int Current_Index(void) const; +// virtual char const * Current_Item(void) const; +// virtual int Draw_Me(int forced); +// virtual char const * Get_Item(int index) const; +// virtual int Step_Selected_Index(int forward); +// virtual void Flag_To_Redraw(void); + +// virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item( char const * text ); + virtual void Remove_Item( int ); +// virtual int Remove_Scroll_Bar(void); +// virtual void Set_Selected_Index(int index); +// virtual void Set_Selected_Index(char const * text); +// virtual void Set_Tabs(int const * tabs); +// virtual int Set_View_Index(int index); +// virtual void Step(int up); +// virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ +// virtual LinkClass & Add(LinkClass & object); +// virtual LinkClass & Add_Tail(LinkClass & object); +// virtual LinkClass & Add_Head(LinkClass & object); +// virtual GadgetClass * Remove(void); + + virtual void Show_Last_Item(); + virtual bool bItemIsMultiSelected( int index ) const; + virtual void MultiSelect( int index, bool bSelect ); + virtual const char* Get_Item_ExtraDataString( int index ) const; + virtual void Set_Item_ExtraDataString( int index, const char* szNewString ); + virtual void* Get_Item_ExtraDataPtr( int index ) const; + virtual void Set_Item_ExtraDataPtr( int index, void* pNewValue ); + const char* Get_Item_Help( int index ) const; + virtual RemapControlType* Get_Item_Color( int index ); + virtual void Set_Item_Color( int index, RemapControlType* pColorRemap ); + virtual const IconList_ItemExtras* Get_ItemExtras( int index ) const; + virtual void Clear(); + virtual int Get_View_Index() { return CurrentTopIndex; } + bool bScrollBeingDragged() + { + // Returns true if the scroll bar of the list is being dragged by the user. + return ( GadgetClass::StuckOn == &ScrollGadget ); + } + + virtual int Find( const char* szItemToFind ); + virtual int FindColor( RemapControlType* pColorRemap ); + + virtual bool Set_Item( unsigned int index, const char* szText ); + virtual bool Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ); + + virtual int GetRealWidth(); + virtual void Resize( int x, int y, int w, int h ); + virtual int IndexUnderMouse(); + virtual int OffsetToIndex( int iIndex, int y ); + + virtual int SetSelectType( int iSelectTypeNew ) + { + // Provided to enable horrible hacks, mainly involved with dealing with ListClass's inability + // to have no item selected... + int iSelectTypeOld = iSelectType; + iSelectType = iSelectTypeNew; + return iSelectTypeOld; + } + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry( int index, int x, int y, int width, int selected ); + + virtual int Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ); + + // The list of Icons. + //DynamicVectorClass IconList; + //DynamicVectorClass< IconList_ItemExtras* > ExtrasList; ajw: creates hellacious linking problems + DynamicVectorClass< void* > ExtrasList; + + bool bDoAlloc; // True if I am responsible for mem. allocation/deletion of strings. +// bool bMultiSelect; // True if we are using the multiple item selection feature. + int iSelectType; // 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + int iMaxItems; // Number of items to limit list to, if bDoAlloc is true. +}; + +#endif + +#endif diff --git a/CODE/IDATA.CPP b/CODE/IDATA.CPP new file mode 100644 index 0000000..a0a29cc --- /dev/null +++ b/CODE/IDATA.CPP @@ -0,0 +1,1374 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IDATA.CPP 3 3/16/97 10:16p Joe_b $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : IDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * InfantryTypeClass::Get_Cameo_Data -- Fetches the small cameo shape for sidebar strip. * + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry obj* + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + +static DoInfoStruct DogDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // NA + {8, 6, 6}, // DO_WALK + {104, 14,14}, // DO_FIRE_WEAPON + {0, 0, 0}, // DO_LIE_DOWN // NA + {56, 6, 6}, // DO_CRAWL + {0, 0, 0}, // DO_GET_UP + {104, 14,14}, // DO_FIRE_PRONE + {216, 18,0}, // DO_IDLE1 + {216, 18,0}, // DO_IDLE2 + {235, 7, 0}, // DO_GUN_DEATH + {242, 9, 0}, // DO_EXPLOSION_DEATH + {242, 9, 0}, // DO_EXPLOSION2_DEATH + {242, 9, 0}, // DO_GRENADE_DEATH + {251, 14,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {106, 12,14}, // DO_DOG_MAUL +}; + +static DoInfoStruct E1DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1, 8}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 6, 8}, // DO_FIRE_PRONE + {256, 16,0}, // DO_IDLE1 + {272, 16,0}, // DO_IDLE2 + {382-94, 8, 0}, // DO_GUN_DEATH + {398-94, 8, 0}, // DO_EXPLOSION_DEATH + {398-94, 8, 0}, // DO_EXPLOSION2_DEATH + {406-94, 12,0}, // DO_GRENADE_DEATH + {418-94, 18,0}, // DO_FIRE_DEATH + {436-94, 3, 3}, // DO_GESTURE1 + {460-94, 3, 3}, // DO_SALUTE1 + {484-94, 3, 3}, // DO_GESTURE2 + {508-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E2DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {288, 1, 12}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 20,20}, // DO_FIRE_WEAPON + {224, 2, 2}, // DO_LIE_DOWN + {240, 4, 4}, // DO_CRAWL + {272, 2, 2}, // DO_GET_UP + {288, 8, 12}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E3DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1,10}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 10,10}, // DO_FIRE_PRONE + {272, 16,0}, // DO_IDLE1 + {288, 16,0}, // DO_IDLE2 + {398-94, 8, 0}, // DO_GUN_DEATH + {414-94, 8, 0}, // DO_EXPLOSION_DEATH + {414-94, 8, 0}, // DO_EXPLOSION2_DEATH + {422-94, 12,0}, // DO_GRENADE_DEATH + {434-94, 18,0}, // DO_FIRE_DEATH + {452-94, 3, 3}, // DO_GESTURE1 + {476-94, 3, 3}, // DO_SALUTE1 + {500-94, 3, 3}, // DO_GESTURE2 + {524-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E4DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {256, 1,16}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 16,16}, // DO_FIRE_WEAPON + {192, 2, 2}, // DO_LIE_DOWN + {208, 4, 4}, // DO_CRAWL + {240, 2, 2}, // DO_GET_UP + {256, 16,16}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E6DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {82, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {67, 2, 2}, // DO_LIE_DOWN + {82, 4, 4}, // DO_CRAWL + {114, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {130, 16,0}, // DO_IDLE1 + {130, 16,0}, // DO_IDLE2 + {146, 8, 0}, // DO_GUN_DEATH + {154, 8, 0}, // DO_EXPLOSION_DEATH + {162, 8, 0}, // DO_EXPLOSION2_DEATH + {162, 12,0}, // DO_GRENADE_DEATH + {182, 18,0}, // DO_FIRE_DEATH + {200, 3, 3}, // DO_GESTURE1 + {224, 3, 3}, // DO_SALUTE1 + {200, 3, 3}, // DO_GESTURE2 + {224, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E7DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {128, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 7, 7}, // DO_FIRE_WEAPON + {113, 2, 2}, // DO_LIE_DOWN + {128, 4, 4}, // DO_CRAWL + {161, 2, 2}, // DO_GET_UP + {176, 7, 7}, // DO_FIRE_PRONE + {232, 17,0}, // DO_IDLE1 + {249, 13,0}, // DO_IDLE2 + {262, 8, 0}, // DO_GUN_DEATH + {270, 8, 0}, // DO_EXPLOSION_DEATH + {278, 8, 0}, // DO_EXPLOSION2_DEATH + {286, 12,0}, // DO_GRENADE_DEATH + {298, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +//Spy +static DoInfoStruct SpyDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {144, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 8, 8}, // DO_FIRE_PRONE + {256, 14,0}, // DO_IDLE1 + {270, 18,0}, // DO_IDLE2 + {288, 8, 0}, // DO_GUN_DEATH + {296, 8, 0}, // DO_EXPLOSION_DEATH + {304, 8, 0}, // DO_EXPLOSION2_DEATH + {312, 12,0}, // DO_GRENADE_DEATH + {324, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E9DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {72, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {56, 2, 2}, // DO_LIE_DOWN + {72, 4, 4}, // DO_CRAWL + {108, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {120, 19,0}, // DO_IDLE1 + {120, 19,0}, // DO_IDLE2 + {139, 8, 0}, // DO_GUN_DEATH + {147, 8, 0}, // DO_EXPLOSION_DEATH + {155, 8, 0}, // DO_EXPLOSION2_DEATH + {163, 12,0}, // DO_GRENADE_DEATH + {175, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct MedicDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {130, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 28,0}, // DO_FIRE_WEAPON + {114, 2, 2}, // DO_LIE_DOWN + {130, 4, 4}, // DO_CRAWL + {162, 2, 2}, // DO_GET_UP + {56, 28,0}, // DO_FIRE_PRONE + {178, 15,0}, // DO_IDLE1 + {178, 15,0}, // DO_IDLE2 + {193, 8, 0}, // DO_GUN_DEATH + {210, 8, 0}, // DO_EXPLOSION_DEATH + {202, 8, 0}, // DO_EXPLOSION2_DEATH + {217, 12,0}, // DO_GRENADE_DEATH + {229, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct GeneralDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {104, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 4, 4}, // DO_FIRE_WEAPON + {88, 2, 2}, // DO_LIE_DOWN + {104, 4, 4}, // DO_CRAWL + {136, 2, 2}, // DO_GET_UP + {152, 4, 4}, // DO_FIRE_PRONE + {184, 26,0}, // DO_IDLE1 + {184, 26,0}, // DO_IDLE2 + {210, 8, 0}, // DO_GUN_DEATH + {226, 8, 0}, // DO_EXPLOSION_DEATH + {218, 8, 0}, // DO_EXPLOSION2_DEATH + {234, 12,0}, // DO_GRENADE_DEATH + {246, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct CivilianDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-85, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {205-85, 4, 4}, // DO_FIRE_PRONE + {189-85, 10,0}, // DO_IDLE1 + {199-85, 6, 0}, // DO_IDLE2 + {152, 8, 0}, // DO_GUN_DEATH + {160, 8, 0}, // DO_EXPLOSION_DEATH + {160, 8, 0}, // DO_EXPLOSION2_DEATH + {168, 12,0}, // DO_GRENADE_DEATH + {180, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct EinsteinDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-92, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {0, 0, 0}, // DO_FIRE_PRONE + {104, 16,0}, // DO_IDLE1 + {104, 16,0}, // DO_IDLE2 + {212-92, 8, 0}, // DO_GUN_DEATH + {220-92, 8, 0}, // DO_EXPLOSION_DEATH + {228-92, 12,0}, // DO_EXPLOSION2_DEATH + {228-92, 12,0}, // DO_GRENADE_DEATH + {240-92, 17,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +// Attack dogs +static InfantryTypeClass const Dog( + INFANTRY_DOG, // Infantry type number. + TXT_GUARD_DOG, // Translate name number for infantry type. + "DOG", // INI name for infantry. + 0x0015, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + DogDoControls, + 1, // Frame of projectile launch. + 1, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Minigunners +static InfantryTypeClass const E1( + INFANTRY_E1, // Infantry type number. + TXT_E1, // Translate name number for infantry type. + "E1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E1DoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Grenadiers +static InfantryTypeClass const E2( + INFANTRY_E2, // Infantry type number. + TXT_E2, // Translate name number for infantry type. + "E2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E2DoControls, + 14, // Frame of projectile launch. + 6, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Bazooka +static InfantryTypeClass const E3( + INFANTRY_E3, // Infantry type number. + TXT_E3, // Translate name number for infantry type. + "E3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E3DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Flamethrower +static InfantryTypeClass const E4( + INFANTRY_E4, // Infantry type number. + TXT_E4, // Translate name number for infantry type. + "E4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Renovator +static InfantryTypeClass const E6( + INFANTRY_RENOVATOR, // Infantry type number. + TXT_E6, // Translate name number for infantry type. + "E6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E6DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Spy +static InfantryTypeClass const E8( + INFANTRY_SPY, // Infantry type number. + TXT_E8, // Translate name number for infantry type. + "SPY", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + SpyDoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Thief +static InfantryTypeClass const E9( + INFANTRY_THIEF, // Infantry type number. + TXT_THIEF, // Translate name number for infantry type. + "THF", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E9DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Tanya +static InfantryTypeClass const E7( + INFANTRY_TANYA, // Infantry type number. + TXT_E7, // Translate name number for infantry type. + "E7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_COMMANDO, // Transport pip shape/color to use. + E7DoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Medic( + INFANTRY_MEDIC, // Infantry type number. + TXT_MEDIC, // Translate name number for infantry type. + "MEDI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const General( + INFANTRY_GENERAL, // Infantry type number. + TXT_GENERAL, // Translate name number for infantry type. + "GNRL", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + GeneralDoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Civilians +static InfantryTypeClass const C1( + INFANTRY_C1, // Infantry type number. + TXT_C1, // Translate name number for infantry type. + "C1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C2( + INFANTRY_C2, // Infantry type number. + TXT_C2, // Translate name number for infantry type. + "C2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv2 // pointer to override remap table +); + +static InfantryTypeClass const C3( + INFANTRY_C3, // Infantry type number. + TXT_C3, // Translate name number for infantry type. + "C3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C4( + INFANTRY_C4, // Infantry type number. + TXT_C4, // Translate name number for infantry type. + "C4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv4 // pointer to override remap table +); + +static InfantryTypeClass const C5( + INFANTRY_C5, // Infantry type number. + TXT_C5, // Translate name number for infantry type. + "C5", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv5 // pointer to override remap table +); + +static InfantryTypeClass const C6( + INFANTRY_C6, // Infantry type number. + TXT_C6, // Translate name number for infantry type. + "C6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv6 // pointer to override remap table +); + +static InfantryTypeClass const C7( + INFANTRY_C7, // Infantry type number. + TXT_C7, // Translate name number for infantry type. + "C7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv7 // pointer to override remap table +); + +static InfantryTypeClass const C8( + INFANTRY_C8, // Infantry type number. + TXT_C8, // Translate name number for infantry type. + "C8", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv8 // pointer to override remap table +); + +static InfantryTypeClass const C9( + INFANTRY_C9, // Infantry type number. + TXT_C9, // Translate name number for infantry type. + "C9", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv9 // pointer to override remap table +); + +// Nikoomba +static InfantryTypeClass const C10( + INFANTRY_C10, // Infantry type number. + TXT_C10, // Translate name number for infantry type. + "C10", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv10 // pointer to override remap table +); + +static InfantryTypeClass const Einstein( + INFANTRY_EINSTEIN, // Infantry type number. + TXT_EINSTEIN, // Translate name number for infantry type. + "EINSTEIN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + 0, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Delphi( + INFANTRY_DELPHI, // Infantry type number. + TXT_DELPHI, // Translate name number for infantry type. + "DELPHI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const DrChan( + INFANTRY_CHAN, // Infantry type number. + TXT_CHAN, // Translate name number for infantry type. + "CHAN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + +// Shock Trooper +static InfantryTypeClass const ShockTrooper( + INFANTRY_SHOCK, // Infantry type number. + TXT_SHOCKTROOPER, // Translate name number for infantry type. + "SHOK", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + + +static InfantryTypeClass const Mechanic( + INFANTRY_MECHANIC, // Infantry type number. + TXT_MECHANIC, // Translate name number for infantry type. + "MECH", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); +#endif + +/*********************************************************************************************** + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * * + * This routine will construct the infantry type objects. It is use to create the static * + * infantry types that are used to give each of the infantry objects their characteristics. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 02/16/1996 JLB : Greatly simplified. * + *=============================================================================================*/ +InfantryTypeClass::InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * control, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap) + : TechnoTypeClass(RTTI_INFANTRYTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + 0x0000, + 0x0000, + 0x0000, + is_nominal, + true, + true, + true, + false, + false, + is_theater, + false, + true, + true, + 8, + SPEED_FOOT), + IsFemale(is_female), + IsCrawling(is_crawling), + IsCapture(false), + IsFraidyCat(false), + IsCivilian(is_civilian), + IsBomber(false), + IsDog(false), + IsRemapOverride(is_remap_override), + Type(type), + Pip(pip), + DoControls(control), + FireLaunch(firelaunch), + ProneLaunch(pronelaunch), + OverrideRemap(override_remap) +{ + /* + ** Forced infantry overrides from the default. + */ + IsCrushable = true; + IsScanner = true; + IsRepairable = false; + IsCrew = false; + Speed = SPEED_FOOT; +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * * + * This will allocate an infantry type class object from the special memory pool of that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the infantry type class object allocated. If there was * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void * InfantryTypeClass::operator new(size_t) +{ + return(InfantryTypes.Alloc()); +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * * + * This will return a previously allocated infantry type class object back to the memory * + * pool from whence it came. * + * * + * INPUT: pointer -- The pointer to the infantry type class object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::operator delete(void * pointer) +{ + InfantryTypes.Free((InfantryTypeClass *)pointer); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * * + * This will pre-allocate all known infantry types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Init_Heap(void) +{ + /* + ** These infantry type class objects must be allocated in the exact order that they + ** are specified in the InfantryType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new InfantryTypeClass(E1); + new InfantryTypeClass(E2); + new InfantryTypeClass(E3); + new InfantryTypeClass(E4); + new InfantryTypeClass(E6); + new InfantryTypeClass(E7); + new InfantryTypeClass(E8); + new InfantryTypeClass(E9); + new InfantryTypeClass(Medic); + new InfantryTypeClass(General); + new InfantryTypeClass(Dog); + new InfantryTypeClass(C1); + new InfantryTypeClass(C2); + new InfantryTypeClass(C3); + new InfantryTypeClass(C4); + new InfantryTypeClass(C5); + new InfantryTypeClass(C6); + new InfantryTypeClass(C7); + new InfantryTypeClass(C8); + new InfantryTypeClass(C9); + new InfantryTypeClass(C10); + new InfantryTypeClass(Einstein); + new InfantryTypeClass(Delphi); + new InfantryTypeClass(DrChan); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new InfantryTypeClass(ShockTrooper); + new InfantryTypeClass(Mechanic); +#endif +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * * + * This creates an infantry object, but does not attempt to place it on the map. It is * + * typically used by the scenario editor when an object is needed, but the location has * + * not yet been specified for where it should appear on the map. * + * * + * INPUT: house -- The owner of the infantry object. * + * * + * OUTPUT: Returns with a pointer to the created infantry object. If an object could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * InfantryTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new InfantryClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * * + * This routine is used by the scenario editor to create and place an infantry object onto * + * the map at the location specified. * + * * + * INPUT: cell -- The cell location to place the infantry object at. * + * * + * house -- The owner of the infantry object. * + * * + * OUTPUT: bool; Was the infantry object successfully created and placed at the location * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + InfantryClass * i = new InfantryClass(Type, house); + if (i != NULL) { + COORDINATE coord = Map[cell].Closest_Free_Spot(Cell_Coord(cell)); + if (coord) { + return(i->Unlimbo(coord, DIR_E)); + } else { + delete i; + } + } + return(false); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * * + * This routine will return with a cell offset occupation list for a generic infantry * + * object. This is typically just a single cell since infantry are never bigger than one * + * cell and this routine presumes the infantry is located in the center of the cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a cell offset list for the infantry object as if it were located * + * in the center of a cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + + return(&_list[0]); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * * + * This routine is used by the scenario editor to display a generic representation of the * + * infantry object for the scenario editor. It simply draws a single (nice profile) view * + * of the infantry type. * + * * + * INPUT: x,y -- The display coordinates to render the infantry object at. * + * * + * window -- The window that the display coordinates are relative to. * + * * + * house -- The house colors to use when rendering this infantry object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + if (house != HOUSE_NONE) { + + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 2; + } + + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * * + * This routine will prepare the scenario editor so that the infantry objects appear on * + * the object list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Prep_For_Add(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + Map.Add_To_List(&As_Reference(index)); + } +} +#endif + + +/*********************************************************************************************** + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * * + * This routine is used to convert the infantry ASCII name as specified into an infantry * + * type number. This is called from the INI reader routine in the process if creating the * + * infantry objects needed for the scenario. * + * * + * INPUT: name -- The ASCII name to convert into an infantry type number. * + * * + * OUTPUT: Returns with the infantry type number that corresponds to the infantry ASCII name * + * specified. If no match could be found, then INFANTRY_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryType InfantryTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (InfantryType classid = INFANTRY_FIRST; classid < INFANTRY_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(INFANTRY_NONE); +} + + +/*********************************************************************************************** + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * * + * This routine will perform one time processing for the infantry type system. This is * + * generally restricted to loading of the infantry shape data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::One_Time(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + InfantryTypeClass const * uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass->Graphic_Name(), ".SHP"); + + #ifndef NDEBUG + RawFileClass sfile(fullname); + if (sfile.Is_Available()) { + ((void const *&)uclass->ImageData) = Load_Alloc_Data(sfile); + } else { + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + #endif + + /* + ** The small build image icon sized shapes are always generic. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%.4sICON", uclass->Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + + #ifndef NDEBUG + RawFileClass ifile(fullname); + if (ifile.Is_Available()) { + ((void const *&)uclass->CameoData) = Load_Alloc_Data(ifile); + } else { + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + #endif + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name text number for this infantry type. It examines * + * the special custom name flag to determine whether the custom name or the generic name * + * is to be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with text number for the name to give this infantry type object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryTypeClass::Full_Name(void) const +{ + if (Debug_Map || !IsNominal || Rule.IsNamed || Type == INFANTRY_C10 || Type == INFANTRY_DELPHI || Type == INFANTRY_EINSTEIN) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN); +} + + +/*********************************************************************************************** + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * * + * Use this routine to convert an infantry type number into a reference to the infantry * + * type class object it represents. * + * * + * INPUT: type -- The infantry type number to convert into a infantry type class object. * + * * + * OUTPUT: Returns with a reference to the infantry type class object specified. * + * * + * WARNINGS: Be sure that the type parameter is legal, otherwise the results are undefined. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +InfantryTypeClass & InfantryTypeClass::As_Reference(InfantryType type) +{ + return(*InfantryTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * * + * This routine will retrieve the override values for this infantry type class object from * + * the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to retrieve the data from. * + * * + * OUTPUT: bool; Was the infantry section for this type found and data retrieved from it? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsFraidyCat = ini.Get_Bool(Name(), "Fraidycat", IsFraidyCat); + IsCapture = ini.Get_Bool(Name(), "Infiltrate", IsCapture); + IsBomber = ini.Get_Bool(Name(), "C4", IsBomber); + IsDog = ini.Get_Bool(Name(), "IsCanine", IsDog); + if (IsBomber) IsCapture = true; + if (IsDog) IsLeader = false; + return(true); + } + return(false); +} + + +void InfantryTypeClass::Dimensions(int & width, int & height) const +{ +#ifdef WIN32 + width = 14; + height = 20; +#else + width = 12; + height = 16; +#endif +} diff --git a/CODE/INFANTRY.CPP b/CODE/INFANTRY.CPP new file mode 100644 index 0000000..6f2ef6a --- /dev/null +++ b/CODE/INFANTRY.CPP @@ -0,0 +1,4150 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INFANTRY.CPP 2 3/03/97 10:35p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INFANTRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : October 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * InfantryClass::Draw_It -- Draws a unit object. * + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * InfantryClass::Init -- Initialize the infantry object system. * + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle* + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't o* + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; + + +/*************************************************************************** +** This is the array of constant data associated with infantry maneuvers. It +** specifies the frame rate as well as if the animation can be aborted. +*/ +// interruptible, mobile, randomstart, rate +DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { + {true, false, false, 0}, // DO_STAND_READY + {true, false, false, 0}, // DO_STAND_GUARD + {true, false, false, 0}, // DO_PRONE + {true, true, true, 2}, // DO_WALK + {true, false, false, 1}, // DO_FIRE_WEAPON + {false, true, false, 2}, // DO_LIE_DOWN + {true, true, true, 2}, // DO_CRAWL + {false, false, false, 3}, // DO_GET_UP + {true, false, false, 1}, // DO_FIRE_PRONE + {true, false, false, 2}, // DO_IDLE1 + {true, false, false, 2}, // DO_IDLE2 + {false, false, false, 2}, // DO_GUN_DEATH + {false, false, false, 2}, // DO_EXPLOSION_DEATH + {false, false, false, 2}, // DO_EXPLOSION2_DEATH + {false, false, false, 2}, // DO_GRENADE_DEATH + {false, false, false, 2}, // DO_FIRE_DEATH + {false, false, false, 2}, // DO_GESTURE1 + {false, false, false, 2}, // DO_SALUTE1 + {false, false, false, 2}, // DO_GESTURE2 + {false, false, false, 2}, // DO_SALUTE2 + {false, false, false, 2}, // DO_DOG_MAUL +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * * + * This routine is used by the debug version to display pertinent information about the * + * infantry unit. * + * * + * INPUT: mono -- The monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Debug_Dump(MonoClass * mono) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + + mono->Print(Text_String(TXT_DEBUG_INFANTRY)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", Doing); + mono->Set_Cursor(8, 11);mono->Printf("%3d", Fear); + + mono->Fill_Attrib(66, 13, 12, 1, IsTechnician ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsStoked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsProne ? MonoClass::INVERSE : MonoClass::NORMAL); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * * + * This is the constructor used when creating an infantry unit. All values are required * + * except for facing and position. If these are absent, then the infantry is created in * + * a state of limbo -- not placed upon the map. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass::InfantryClass(InfantryType classid, HousesType house) : + FootClass(RTTI_INFANTRY, Infantry.ID(this), house), + Class(InfantryTypes.Ptr((int)classid)), + Doing(DO_NOTHING), + Comment(0), + IsTechnician(false), + IsStoked(false), + IsProne(false), + IsZoneCheat(false), + WasSelected(false), + Fear(FEAR_NONE) +{ + House->Tracking_Add(this); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + IsCloakable = Class->IsCloakable; +#endif + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->Is_Two_Shooter()) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Strength = Class->MaxStrength; + + /* + ** Civilians carry much less ammo than soldiers do. + */ + Ammo = Class->MaxAmmo; +} + + +/*********************************************************************************************** + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * * + * This is the default destructor for infantry type units. It will put the infantry into * + * a limbo state if it isn't already in that state and the game is still active. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +InfantryClass::~InfantryClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * * + * This will allocate an infantry object from the infantry object free pool. If there is * + * no available slot, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * + * allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * InfantryClass::operator new(size_t) +{ + void * ptr = Infantry.Allocate(); + if (ptr != NULL) { + ((InfantryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * * + * This routine is used return an infantry object back to the system. * + * * + * INPUT: ptr -- Pointer to the infantry object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((InfantryClass *)ptr)->IsActive = false; + } + Infantry.Free((InfantryClass *)ptr); +} + + +/*********************************************************************************************** + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * * + * This routine applies the damage specified to the infantry object. It is possible that * + * this routine will DESTROY the infantry unit in the process. * + * * + * INPUT: damage -- The damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The warhead type that is inflicting the damage. * + * * + * source -- Who is responsible for inflicting the damage. * + * * + * OUTPUT: bool; Was the infantry unit destroyed by this damage? * + * * + * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * + * for this in the code that follows the call to Take_Damage(). * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 03/31/1995 JLB : Revenge factor. * + *=============================================================================================*/ +ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Prone infantry take only half damage, but never below one damage point. + */ + if (IsProne && damage > 0) { + damage = damage * Rule.ProneDamageBias; + } + + /* + ** If we're taking damage from a dog, we have to decide if we're the + ** target of the dog. Dogs don't spill collateral damage onto anyone + ** else, so if we're the target of a valid dog, take full damage, but if + ** we're not the target, or the dog doesn't exist, then take no damage. + */ + if (source != NULL && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** hack for dog: if you're hit by a dog, and you're the target, your + ** damage gets upped to max. + */ + + if (res == RESULT_NONE) return(res); + + if (res == RESULT_DESTROYED) { + if (*this == INFANTRY_TANYA) { + IsTanyaDead = true; + } + Death_Announcement(source); + Stop_Driver(); + Stun(); + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + VocType sound; + VocType altsound; + sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM11); + altsound = VOC_YELL1; + if (*this == INFANTRY_TANYA) { + sound = altsound = VOC_TANYA_DIE; + } + if (Class->IsDog) { + sound = altsound = VOC_DOG_HURT; + } + + /* + ** The type of warhead determines the animation the infantry + ** will perform when killed. + */ + bool delthis = false; + TARGET us = As_Target(); + switch (WarheadTypeClass::As_Pointer(warhead)->InfantryDeath) { + default: + case 0: + delthis = true; + break; + + case 1: + Sound_Effect(sound, Coord); + Do_Action(DO_GUN_DEATH, true); + break; + + case 2: + Sound_Effect(sound, Coord); + Do_Action(DO_EXPLOSION_DEATH, true); + break; + + case 3: + Sound_Effect(sound, Coord); + Do_Action(DO_GRENADE_DEATH, true); + break; + + case 4: + Sound_Effect(altsound, Coord); + Do_Action(DO_FIRE_DEATH, true); + break; + + case 5: + Sound_Effect(sound, Coord); + AnimType anim = ANIM_ELECT_DIE; + if (Class->IsDog) anim = ANIM_DOG_ELECT_DIE; + new AnimClass(anim, Coord); + delthis = true; + break; + } + + if (delthis) { + delete this; + } + return(res); + } + + /* + ** When infantry gets hit, it gets scared. + */ + if (res != RESULT_DESTROYED) { + COORDINATE source_coord = (source) ? source->Coord : NULL; + + /* + ** If an engineer is damaged and it is just sitting there, then tell it + ** to go do something since it will definitely die if it doesn't. + */ + if (!House->IsHuman && *this == INFANTRY_RENOVATOR && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { + Assign_Mission(MISSION_HUNT); + } + + if (source != NULL) { + Scatter(source_coord); + } + + if (source != NULL && Fear < FEAR_SCARED) { + if (Class->IsFraidyCat) { + Fear = FEAR_PANIC; + } else { + Fear = FEAR_SCARED; + } + } else { + + /* + ** Increase the fear of the infantry by a bit. The fear increases more + ** quickly if the infantry is damaged. + */ + int morefear = FEAR_ANXIOUS; + if (Health_Ratio() > Rule.ConditionRed) morefear /= 2; + if (Health_Ratio() > Rule.ConditionYellow) morefear /= 2; + Fear = FearType(min((int)Fear + morefear, FEAR_MAXIMUM)); + } + } + return(res); +} + + +/*********************************************************************************************** + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * * + * This will determine the shape number to use for this infantry soldier. The shape number * + * is relative to the shape file associated with this infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number for this infantry object to be used when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Shape_Number(void) const +{ + /* + ** Fetch the shape pointer to use for the infantry. This is controlled by what + ** choreograph sequence the infantry is performing, it's facing, and whether it + ** is prone. + */ + DoType doit = Doing; + if (doit == DO_NOTHING) doit = DO_STAND_READY; + + /* + ** The infantry shape is always modulo the number of animation frames + ** of the action stage that the infantry is doing. + */ + int shapenum = Fetch_Stage() % max(Class->DoControls[doit].Count, 1); + + /* + ** If facing makes a difference, then the shape number will be incremented + ** by the facing accordingly. + */ + if (Class->DoControls[doit].Jump) { + shapenum += HumanShape[Dir_To_32(PrimaryFacing.Current())] * Class->DoControls[doit].Jump; + } + + /* + ** Finally, the shape number is biased according to the starting frame number for + ** that action in the infantry shape file. + */ + shapenum += Class->DoControls[doit].Frame; + + /* + ** Return with the final infantry shape number. + */ + return(shapenum); +} + + +/*********************************************************************************************** + * InfantryClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Converted to infantry support. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class by seeing if there is shape imagery for it. If + ** there is no shape image, then it certainly can't be drawn -- bail. + */ + void const * shapefile = Get_Image_Data(); + + if (shapefile == NULL) return; + + y += 4; + x -= 2; + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window); + + FootClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * * + * This routine will handle any special operations that need to be performed once each * + * cell travelled. This includes radioing a transport that it is now clear and the * + * transport is free to leave. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 03/01/1995 JLB : Capture building options. * + * 05/31/1995 JLB : Capture is always successful now. * + *=============================================================================================*/ +void InfantryClass::Per_Cell_Process(PCPType why) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + CellClass * cellptr = &Map[Coord]; + + if (why == PCP_END) { + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** capture, then capture it. + */ + if (Mission == MISSION_CAPTURE) { + TechnoClass * tech = cellptr->Cell_Building(); + + if (tech == NULL) tech = cellptr->Cell_Techno(); + if (tech != NULL && (tech->As_Target() == NavCom || tech->As_Target() == TarCom)) { + if (*this == INFANTRY_RENOVATOR) { + + /* + ** An engineer will either mega-repair a friendly or allied + ** building or it will damage/capture an enemy building. Whether + ** it damages or captures depends on how badly damaged the + ** enemy building is. + */ +#ifdef FIXIT_ENGINEER_CAPTURE + if (House->Is_Ally(tech)) { +#else + if (tech->House->Is_Ally(House)) { +#endif + tech->Renovate(); + } else { + bool iscapturable = false; + if (tech->What_Am_I() == RTTI_BUILDING) { + iscapturable = ((BuildingClass *)tech)->Class->IsCaptureable; + } +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) { +#else + if (tech->Health_Ratio() <= Rule.ConditionRed && iscapturable) { +#endif + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + tech->House->IsThieved = true; + tech->Captured(House); + } else { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + int damage = min( (tech->Techno_Type_Class()->MaxStrength) * EngineerDamage, tech->Strength-1); +#else + int damage = min( (tech->Techno_Type_Class()->MaxStrength) / 3, tech->Strength-1); +#endif + tech->Take_Damage(damage, 0, WARHEAD_HE, this, true); + } + BEnd(BENCH_PCP); + delete this; + return; + } + + } else { + if (*this != INFANTRY_SPY && tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + if (*this == INFANTRY_SPY) { + int housespy = (1 << (House->Class->House)); +// tech->House->IsSpied = true; + + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_SPIED, this); + } + + if (IsOwnedByPlayer) Speak(VOX_BUILDING_INFILTRATED); + + tech->Mark(MARK_OVERLAP_UP); + tech->SpiedBy |= housespy; + tech->Mark(MARK_OVERLAP_DOWN); + if (tech->What_Am_I() == RTTI_BUILDING) { + StructType build = *(BuildingClass *)tech; + if (build == STRUCT_RADAR /* || build == STRUCT_EYE */ ) { + tech->House->RadarSpied |= housespy; + } + // If they're spying on a sub pen, give 'em a sonar pulse + if (build == STRUCT_SUB_PEN) { + House->SuperWeapon[SPC_SONAR_PULSE].Enable(false, true, false); + if (IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + } else { + + if (*this == INFANTRY_THIEF) { // Thief just raided a storage facility + tech->House->IsThieved = true; + + if (tech->What_Am_I() == RTTI_BUILDING) { + BuildingClass * bldg = (BuildingClass *)tech; + if (bldg->Class->Capacity) { + + /* + ** If we just raided a storage facility (refinery or silo) + ** then give the thief up to half the capacity of the + ** storage facility. + */ + if (IsOwnedByPlayer || bldg->IsOwnedByPlayer) Speak(VOX_MONEY_STOLEN); +#ifdef OBSOLETE + long capacity = bldg->Class->Capacity * 256; + capacity /= (bldg->House->Tiberium+1); + int bldgcap = bldg->Class->Capacity; + + long cash = (bldgcap * 256) / (capacity+1); + if (cash > (bldgcap / 2)) cash = bldgcap / 2; +#else + long cash = bldg->House->Available_Money() / 2; +#endif + bldg->House->Spend_Money(cash); + House->Refund_Money(cash); + } + } + } + } + } + BEnd(BENCH_PCP); + delete this; + return; + + } else { + + #ifdef OBSOLETE + // are we trying to repair a bridge? + if (Is_Target_Cell(TarCom) ) { + CELL cell = Coord_Cell(Coord); + if (cell == ::As_Cell(NavCom)) { + TemplateType tt = cellptr->TType; + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + int h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D) { + new TemplateClass(TemplateType(cellptr->TType-1), cell); + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } else { + + // Trying to repair multi-segment bridge. Look for the + // start tile, then fix it, and determine the direction to + // go in and repair it all that way. + TemplateType newtt = TEMPLATE_BRIDGE_1A; + int xmov = -1; // coords to move to for next template + int ymov = 2; + bool valid = false; + switch (tt) { + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_1C: + valid = true; + break; + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_2C: + newtt = TEMPLATE_BRIDGE_2A; + xmov = 2; + ymov = -1; + valid = true; + break; + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + newtt = TEMPLATE_BRIDGE_3A; + valid = true; + break; + case TEMPLATE_BRIDGE_3E: + newtt = TEMPLATE_BRIDGE_3A; + xmov = 2; + ymov = -1; + valid = true; + break; + } + + // Did we find a valid repairable bridge piece? + if (valid) { + bool doing = true; + while (doing) { + new TemplateClass(TemplateType(newtt), cell); + cell += (MAP_CELL_W * ymov) + xmov; + if (xmov < 0) { + xmov = -1; + ymov = 1; + } else { + xmov = 1; + ymov = -1; + } + cellptr = &Map[cell]; + tt = cellptr->TType; + if ((tt >= TEMPLATE_BRIDGE_3B && tt <= TEMPLATE_BRIDGE_3F) || + tt == TEMPLATE_BRIDGE_1B || tt == TEMPLATE_BRIDGE_1C || + tt == TEMPLATE_BRIDGE_2B || tt == TEMPLATE_BRIDGE_2C ) { + + if (tt >= TEMPLATE_BRIDGE_3B) { + newtt = TEMPLATE_BRIDGE_3A; + } else { + if (tt < TEMPLATE_BRIDGE_2A) { + newtt = TEMPLATE_BRIDGE_1A; + } else { + newtt = TEMPLATE_BRIDGE_2A; + } + } + icon = cellptr->TIcon; + w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + } else { + doing = false; + } + } + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } + } + } + } else { + #endif + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + if (Map[Coord].Cell_Building()) { + Scatter(0, true); + } + } + #ifdef OBSOLETE + } + #endif + } + } + + /* + ** Infantry entering a transport vehicle will break radio contact + ** at attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno != NULL && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** sabotage, then sabotage it. + */ + if (Mission == MISSION_SABOTAGE) { + BuildingClass * building = cellptr->Cell_Building(); + if (building != NULL && building->As_Target() == NavCom) { + if (!building->IronCurtainCountDown && building->Mission != MISSION_DECONSTRUCTION) { + building->IsGoingToBlow = true; + building->Clicked_As_Target((Rule.C4Delay * TICKS_PER_MINUTE) / 2); + building->CountDown = Rule.C4Delay * TICKS_PER_MINUTE; + building->WhomToRepay = As_Target(); + } + NavCom = TARGET_NONE; + Do_Uncloak(); + Arm = Rearm_Delay(true); + Scatter(building->Center_Coord(), true, true); // RUN AWAY! + BEnd(BENCH_PCP); + return; + } else { + if (::As_Target(Coord_Cell(Center_Coord())) == NavCom) { + Explosion_Damage(Coord, Rule.BridgeStrength, this, WARHEAD_HE); + + Stop_Driver(); + Scatter(Adjacent_Cell(Coord, PrimaryFacing), true, true); + Assign_Mission(MISSION_MOVE); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + if (!Target_Legal(NavCom) || Map[As_Cell(NavCom)].Land_Type() == LAND_WATER) { + Mark(MARK_DOWN); // Needed only so that Tanya will get destroyed by the explosion. + } + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + + Mark(MARK_DOWN); + } + } + } + + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (IsTethered) { + Transmit_Message(RADIO_UNLOADED); + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE) { + Do_Action(DO_GESTURE1); + } else { + Do_Action(DO_GESTURE2); + } + + /* + ** Special voice play. + */ + if (*this == INFANTRY_TANYA) { + Sound_Effect(VOC_TANYA_LAUGH, Coord); + } + + /* + ** If the cell is now full of infantry, tell them all to scatter + ** in order to make room for more. + */ + if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { + cellptr->Incoming(0, true, true); +// cellptr->Incoming(0, true); + } + } + + /* + ** When the infantry reaches the center of the cell, it may begin a new mission. + */ + if (MissionQueue == MISSION_NONE && !Target_Legal(NavCom) && !Target_Legal(TarCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + } + Commence(); + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = cellptr->Cell_Building(); + if (bldng != NULL && *bldng == STRUCT_APMINE) { + /* + ** Show the animation and get rid of the land mine + */ + COORDINATE blcoord = bldng->Center_Coord(); + new AnimClass(Combat_Anim(Rule.APMineDamage, WARHEAD_HE, cellptr->Land_Type()), blcoord); + delete bldng; + int damage; + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj != NULL && !obj->IsInLimbo) { + int dist = ::Distance(obj->Coord, blcoord); + if (dist <= 0xC0) { + damage = Rule.APMineDamage; + obj->Take_Damage(damage, 0, WARHEAD_HE); + } + } + } + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + Look(true); + +#if 1 +/* +** If after all is said and done, the unit finishes its move on an impassable cell, then +** it must presume that it is in the case of a unit driving onto a bridge that blows up +** before the unit completes it's move. In such a case the unit should have been destroyed +** anyway, so blow it up now. +*/ +LandType land = Map[Coord].Land_Type(); +if (!IsDriving && !Class->IsBomber && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; +} +#endif + + } + + if (IsActive) { + FootClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * * + * This is a support routine that removes the target specified from any targeting or * + * navigation computers. When a target is destroyed or removed from the game system, * + * the target must be removed from any tracking systems of the other units. This routine * + * handles removal for infantry units. * + * * + * INPUT: target -- The target to remove from the infantry unit's tracking systems. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Detach(TARGET target, bool all) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (TarCom == target) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + FootClass::Detach(target, all); +} + + +/*********************************************************************************************** + * InfantryClass::Init -- Initialize the infantry object system. * + * * + * This routine will force the infantry object system into its empty initial state. It * + * is called when the scenario needs to be cleared in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Init(void) +{ + Infantry.Free_All(); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * * + * This routine updates the infantry's navigation computer so that the infantry will * + * travel to the destination target specified. * + * * + * INPUT: target -- The target to have the infantry unit move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Destination(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Special flag so that infantry will start heading in the right direction immediately. + */ + if (IsDriving && !IsFormationMove && Target_Legal(target) && Map[Center_Coord()].Is_Clear_To_Move(Class->Speed, true, false)) { + Stop_Driver(); + } + + /* + ** When telling an infantry soldier to move to a location twice, then this + ** means that movement is more important than safety. Get up and run! + */ + if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat && !Class->IsDog) { + Do_Action(DO_GET_UP); + } + + /* + ** If telling a dog to attack a human, start the dog running + */ + TechnoClass * tech = As_Techno(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + FootClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * * + * This routine will update the infantry's targeting computer so that it will try to * + * attack the target specified. This might result in it moving to be within range and thus * + * also cause adjustment of the navigation computer. * + * * + * INPUT: target -- The target that this infantry should attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 06/30/1995 JLB : Tries to capture target if possible. * + *=============================================================================================*/ +void InfantryClass::Assign_Target(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Path[0] = FACING_NONE; + if (Class->IsDog) { + if (::As_Object(target) && ::As_Object(target)->What_Am_I() != RTTI_INFANTRY) { + target = TARGET_NONE; + } + } + FootClass::Assign_Target(target); + + /* + ** If this is an infantry that can only capture, then also assign its destination to the + ** target specified. + */ + if (!Target_Legal(NavCom) && Class->IsCapture && !Is_Weapon_Equipped()) { + BuildingClass const * building = As_Building(target); + if (building != NULL && building->Class->IsCaptureable) { + Assign_Destination(target); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * * + * This routine is used to handle the non-graphic AI processing the infantry requires. * + * Call this routine ONCE per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::AI(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + FootClass::AI(); + + if (!IsActive) { + return; + } + + if (IsUnloading) Mark(MARK_CHANGE_REDRAW); + + /* + ** Infantry that are not on the ground should always be redrawn. Such is + ** the case when they are parachuting to the ground. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(MARK_CHANGE); + } + + /* + ** Special hack to make sure that if this infantry is in firing animation, but the + ** stage class isn't set, then abort the firing flag. + */ + if (IsFiring && Fetch_Rate() == 0) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Do_Action(DO_STAND_READY); + Mark(MARK_OVERLAP_DOWN); + } + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsFiring && !IsFalling && !IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + + /* + ** Special hack to make sure the dog never attacks a cell. + */ + if (Class->IsDog && Target_Legal(TarCom) && Is_Target_Cell(TarCom)) { + Assign_Target(TARGET_NONE); + } + + /* + ** Handle any infantry fear logic or related actions. + */ + Fear_AI(); + + /* + ** Special victory dance action. + */ + if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment == 0) { + IsStoked = false; + Do_Action(Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2); + } + + /* + ** Determine if this infantry unit should fire off an + ** attack or not. + */ + Firing_AI(); + + /* + ** Handle the completion of the animation sequence. + */ + Doing_AI(); + + /* + ** Perform movement operations at this time. + */ + Movement_AI(); +} + + +/*********************************************************************************************** + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * * + * This routine is used to examine the cell specified and determine if the infantry is * + * allowed to enter it. It is used by the path finding algorithm. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the type of blockage in the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** If we are moving into an illegal cell, then we can't do that. + */ + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** If moving off the edge of the map, then consider that an illegal move. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + CellClass * cellptr = &Map[cell]; + + /* + ** Walls are considered impassable for infantry UNLESS the wall has a hole + ** in it. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (otype.IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (otype.IsWall) { + if ((cellptr->OverlayData / 16) != otype.DamageLevels) { + + /* + ** If the wall can be destroyed, then return this fact instead of + ** a complete failure to enter. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->Is_Wall_Destroyer()) { + return(MOVE_DESTROYABLE); + } + return(MOVE_NO); + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + MoveType retval = MOVE_OK; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** Always allow movement if the cell is the object to be captured or sabotaged. + */ + if (((Mission == MISSION_ENTER && In_Radio_Contact()) || Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && + (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { + + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target() && Is_Target_Unit(ArchiveTarget)) { + return(MOVE_NO); + } + + /* + ** If object is a land mine, allow movement + */ + if (obj->What_Am_I() == RTTI_BUILDING) { + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) { + obj = obj->Next; + continue; + } else { + if (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House)) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) { + obj = obj->Next; + continue; + } + } + } + } + + /* + ** Special case check so that a landed aircraft that is in radio contact, will not block + ** a capture attempt. It is presumed that this case happens when a helicopter is landed + ** at a helipad. + */ +// if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ +// if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT || obj->What_Am_I() == RTTI_UNIT) { +// if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { +// return(MOVE_OK); +// } +// } + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Allied objects block movement using different rules than for enemy + ** objects. + */ + if (House->Is_Ally(obj) || ScenarioInit) { + switch (obj->What_Am_I()) { + + /* + ** A unit blocks as either a moving blockage or a stationary temp blockage. + ** This depends on whether the unit is currently moving or not. + */ + case RTTI_UNIT: + if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + break; + + /* + ** Aircraft and buildings always block movement. If for some reason there is an + ** allied terrain object, that blocks movement as well. + */ + case RTTI_TERRAIN: + case RTTI_AIRCRAFT: + case RTTI_BUILDING: + return(MOVE_NO); + + default: + break; + } + + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** Any non-allied blockage is considered impassible if the infantry + ** is not equipped with a weapon. + */ + if (Combat_Damage() <= 0) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the infantry is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: +#ifdef OBSOLETE + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + case RTTI_INFANTRY: + if ( *(InfantryClass *)obj == INFANTRY_SPY && !Class->IsDog) { + retval = MOVE_TEMP; + break; + } + // otherwise, fall thru. + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } +// } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If foot soldiers cannot travel on the cell -- consider it impassable. + */ + if (retval == MOVE_OK && !IsTethered && Ground[cellptr->Land_Type()].Cost[SPEED_FOOT] == 0) { + +#ifdef OBSOLETE + /* + ** Special case - if it's an engineer, and the cell under consideration + ** is his NavCom, and his mission is mission_capture, then he's most + ** likely moving to his final destination to repair a bridge, so we + ** should let him. + */ + if (*this == INFANTRY_RENOVATOR && Is_Target_Cell(TarCom) && (cell == ::As_Cell(NavCom)) && (cellptr->TType == TEMPLATE_BRIDGE1D || cellptr->TType == TEMPLATE_BRIDGE2D || (cellptr->TType >= TEMPLATE_BRIDGE_1C && cellptr->TType <= TEMPLATE_BRIDGE_3E) ) ) { + return(MOVE_OK); + } +#endif + return(MOVE_NO); + } + + /* + ** if a unit has the cell reserved then we just can't go in there. + */ + if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { + return(MOVE_NO); + } + + /* + ** if a block of infantry has the cell reserved then there are two + ** possibilities... + */ + if (cellptr->InfType != HOUSE_NONE) { + if (House->Is_Ally(cellptr->InfType)) { + if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } + } else { + if (Combat_Damage() > 0) { + if (retval < MOVE_DESTROYABLE) { + retval = MOVE_DESTROYABLE; + } + } else { + return(MOVE_NO); + } + } + } + + /* + ** If it is still ok to move the infantry, then perform the last check + ** to see if the cell is already full of infantry. + */ + if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { + return(MOVE_NO); + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * * + * This is a rendering support routine that will return a pointer to a list of cell offsets * + * that specify the cells the infantry unit is currently overlapping (graphic wise) but * + * is not considered to occupy. This list is used to update the map display. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * + * occupy. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef PARTIAL +short const * InfantryClass::Overlap_List(bool redraw) const +#else +short const * InfantryClass::Overlap_List(bool ) const +#endif +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsDog) { + return(Coord_Spillage_List(Coord, 24 + (Doing == DO_DOG_MAUL ? 40 : 0) + (Doing >= DO_GUN_DEATH && Doing <= DO_FIRE_DEATH ? 40 : 0) )); + } else { + + /* + ** The default infantry rectangle will be as large as the largest shape the infantry + ** can be. + */ + +#ifdef PARTIAL + Rect rect(-16, -24, 32, 36); + + /* + ** If this is for a visual change redraw, then the overlap list will be based + ** on the actual dimensions of the shape data. If the dimensions have already + ** been calculated then use them, otherwise, use the default large rectangle + ** previously created. + */ + if (Height == 0 && !IsSelected && redraw && Class->DimensionData != NULL) { + int shapenum = Shape_Number(); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + rect.Y += 4; + rect.X -= 2; + } + return(Coord_Spillage_List(Coord, rect, true)); +#else + + static Rect rect(-16, -24, 32, 36); + return(Coord_Spillage_List(Coord, rect, true)); +#endif + +// return(Coord_Spillage_List(Coord, 24 /*+ ((Doing > DO_WALK || IsSelected)?12:0)*/ )); + } +} + + +/*********************************************************************************************** + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * * + * Determines if the infantry unit can fire on the target. If it can't fire, then the * + * reason why is returned. * + * * + * INPUT: target -- The target to determine if the infantry can fire upon. * + * * + * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * + * can't, why not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 06/27/1995 JLB : Flame thrower can fire while prone now. * + *=============================================================================================*/ +FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Don't allow firing if the infantry is still firing on previous target. + */ +// if (IsFiring) return(FIRE_REARM); + + /* + ** If a medic is shooting at a healed target, let's declare the target + ** illegal so he won't be constantly healing healed infantrymen. + */ + if (Combat_Damage() < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TechnoClass * targ = As_Techno(target); +#else + InfantryClass * targ = As_Infantry(target); +#endif + if (targ == NULL || targ->Health_Ratio() >= Rule.ConditionGreen) { + return(FIRE_ILLEGAL); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (IsDriving || (Target_Legal(NavCom) && Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { + return(FIRE_MOVING); + } + + return(FootClass::Can_Fire(target, which)); +} + + +/*********************************************************************************************** + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * * + * Use this routine when the infantry unit as accomplished its task and needs to find * + * something to do. The default behavior is to enter some idle state such as guarding. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Enter_Idle_Mode(bool ) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + } else { + + Handle_Navigation_List(); + + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsZombie || MissionControl[Mission].IsParalyzed) { + return; + } + + if (Class->IsDog) { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + ArchiveTarget = ::As_Target(Coord_Cell(Center_Coord())); + } + } else { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + if (Is_Weapon_Equipped()) { + order = MISSION_GUARD_AREA; + } else { + order = MISSION_GUARD; + } + } + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * * + * This routine is the random animator initiator for infantry units. This routine should * + * be called regularly. On occasion, it will cause the infantry to go into an idle * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 12/13/1994 JLB : Does random facing change. * + * 07/02/1995 JLB : Nikoomba special effects. * + *=============================================================================================*/ +bool InfantryClass::Random_Animate(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Is_Ready_To_Random_Animate()) { + IdleTimer = Random_Pick(Rule.RandomAnimateTime * (TICKS_PER_MINUTE/2), Rule.RandomAnimateTime * (TICKS_PER_MINUTE*2)); + + /* + ** Scared infantry will always follow the golden rule of civilians; + ** "When in darkness or in doubt, run in circles, scream, and shout!" + */ + if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { + Scatter(NULL, true); + return(true); + } + + switch (Random_Pick(0, 10)) { + case 0: + if (Class->IsDog) { + Do_Action(DO_IDLE1); + } + break; + + case 1: + Do_Action(DO_SALUTE1); + break; + + case 2: + Do_Action(DO_SALUTE2); + break; + + case 3: + Do_Action(DO_GESTURE1); + break; + + case 4: + Do_Action(DO_GESTURE2); + break; + + case 5: + Do_Action(DO_IDLE1); + break; + + case 6: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + break; + + case 7: + Do_Action(DO_IDLE2); + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!IsSelected && IsOwnedByPlayer && *this == INFANTRY_TANYA && Sim_Random_Pick(0, 2) == 0) { + Sound_Effect(VOC_TANYA_SHAKE, Coord); + } + break; + + /* + ** On occasion, civilian types will wander about. + */ + case 8: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!House->IsHuman && Class->IsFraidyCat) { + Scatter(NULL, true); + } + break; + + case 9: + case 10: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * * + * This routine is used when the infantry should scatter to a nearby cell. Scattering * + * occurs as an occasional consequence of being fired upon. It is one of the features * + * that makes infantry so "charming". * + * * + * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * + * scatter. If the threat isn't from a particular direction, then this * + * parameter will be NULL. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/12/1994 JLB : Flame thrower infantry always scatter. * + * 08/02/1996 JLB : Added the nokidding parameter * + *=============================================================================================*/ +void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** A unit that is in the process of going somewhere will never scatter. + */ + if (IsDriving) forced = false; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + /* + ** If the infantry is currently engaged in legitimate combat, then don't + ** scatter unless forced to. + */ + if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; + + /* + ** Don't scatter if performing an action that can't be interrupted. + */ + if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; + + /* + ** For human players, don't scatter the infantry, if the special + ** flag has not been enabled that allows infantry scatter. + */ + if (!Rule.IsScatter && !nokidding && House->IsHuman && !forced && !Team.Is_Valid()) return; + + if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { + FacingType toface; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 4)-2); + } else { + COORDINATE coord = Coord_Fraction(Center_Coord()); + + if (coord != 0x00800080L) { + toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + toface = toface + FacingType(Random_Pick(0, 4)-2); + } + + CELL newcell = 0; + CELL altcell = 0; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + FacingType newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + if (altcell == 0) altcell = newcell; + if (!Map[newcell].Is_Bridge_Here()) break; +// Assign_Mission(MISSION_MOVE); +// Assign_Destination(::As_Target(newcell)); + } + } + if (face == FACING_COUNT) { + newcell = 0; + } + + if (newcell == 0) { + newcell = altcell; + } + + if (newcell != 0) { + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(newcell)); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * * + * This starts the infantry into a choreographed animation sequence. These sequences can * + * be as simple as standing up or lying down, but can also be complex, such as dying or * + * performing some idle animation. * + * * + * INPUT: todo -- The choreographed sequence to start. * + * * + * force -- Force starting this animation even if the current animation is flagged * + * as uninterruptible. This is necessary for death animations. * + * * + * OUTPUT: bool; Was the animation started? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Do_Action(DoType todo, bool force) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (todo == DO_NOTHING || Class->DoControls[todo].Count == 0) { + return(false); + } + + if (*this == INFANTRY_SPY && todo >= DO_GESTURE1) { + todo = (DoType)(DO_IDLE1 + Random_Pick(0,1)); + } + + if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { + Mark(MARK_OVERLAP_UP); + Doing = todo; + Mark(MARK_OVERLAP_DOWN); + if (todo == DO_IDLE1 || todo == DO_IDLE2) { + Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); + } else { + Set_Rate(MasterDoControls[Doing].Rate); + } + Set_Stage(0); + + /* + ** Kludge to make sure that if infantry is in the dying animation, it isn't still + ** moving as well. + */ + if (Strength == 0) { + Stop_Driver(); + } + + /* + ** Since the animation sequence might be interrupted. Set any flags + ** necessary so that if interrupted, the affect on the infantry is + ** still accomplished. + */ + switch (todo) { + case DO_LIE_DOWN: + IsProne = true; + break; + + case DO_GET_UP: + IsProne = false; + break; + + default: + break; + } + + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * * + * This is used to stop the infantry from animating in movement. This function will stop * + * the infantry moving and revert it to either a prone or standing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Stop_Driver(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Head_To_Coord()) { + + /* + ** Remove the "reservation" bit in the destination location. + */ + Clear_Occupy_Bit(Head_To_Coord()); + } + + /* + ** Set the occupation bit at the current location. + */ + Set_Occupy_Bit(Coord); + + if (Class->IsDog) { + Do_Action(DO_STAND_READY); + } else { + if (IsProne) { + Do_Action(DO_PRONE); + } else { + Do_Action(DO_STAND_READY); + } + } + + if (Can_Enter_Cell(Coord_Cell(Coord)) == MOVE_OK) { + IsZoneCheat = false; + } else { + IsZoneCheat = true; + } + + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * * + * Use this routine to being the infantry moving toward the destination specified. The * + * destination is first checked to see if there is a free spot available. Then the infantry * + * reserves that spot and begins movement toward it. * + * * + * INPUT: headto -- The coordinate location desired for the infantry to head to. * + * * + * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * + * the specified destination could not contain the infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + * 05/14/1995 JLB : Tries to move to closest spot possible. * + * 05/15/1995 JLB : Uses closest spot if moving onto transport. * + *=============================================================================================*/ +bool InfantryClass::Start_Driver(COORDINATE & headto) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + COORDINATE old = headto; + + /* + ** Convert the head to coordinate to a legal sub-position location. + */ + headto = Map[headto].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); + if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { + headto = Map[old].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); + } + + /* + ** If the infantry started moving, then fixup the occupation bits. + */ + if (headto && FootClass::Start_Driver(headto)) { + if (!IsActive) return(false); + + /* + ** Remove the occupation bit from the infantry's current location. + */ + Clear_Occupy_Bit(Coord); + + /* + ** Set the occupation bit for the new headto location. + */ + Set_Occupy_Bit(headto); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * * + * This routine will clean up the infantry occupation bits (as necessary) as well as stop * + * the infantry movement process when it gets limboed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Limbo(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + Stop_Driver(); + + Clear_Occupy_Bit(Coord); + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * * + * Use this routine when the infantry unit wishes to fire a projectile. This routine * + * will launch the projectile and perform any other necessary infantry specific operations. * + * * + * INPUT: target -- The target of the attack. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * + * NULL is returned. If there is already the maximum bullet objects in play, then * + * this could happen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * InfantryClass::Fire_At(TARGET target, int which) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + + BulletClass * bullet = FootClass::Fire_At(target, which); + if (bullet != NULL && !IsInLimbo) { + + /* + ** For fraidycat infantry that run out of ammo, always go into + ** a maximum fear state at that time. + */ + if (Class->IsFraidyCat && !Ammo) { + Fear = FEAR_MAXIMUM; + if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { + Assign_Mission(MISSION_GUARD); + } + } + } + return(bullet); +} + + +/*********************************************************************************************** + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * * + * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * + * ensure that the coordinate is a legal subposition. * + * * + * INPUT: coord -- The coordinate to unlimbo the infantry at. * + * * + * facing -- The desired initial facing for the infantry unit. * + * * + * strength -- The desired initial strength for the infantry unit. * + * * + * mission -- The desired initial mission for the infantry unit. * + * * + * OUTPUT: bool; Was the infantry unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that the infantry start in a legal position on the map. + */ + coord = Map[coord].Closest_Free_Spot(coord, ScenarioInit); + if (coord == NULL) { + return(false); + } + + if (FootClass::Unlimbo(coord, facing)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->IScan |= (1L << Class->Type); + House->ActiveIScan |= (1L << Class->Type); + + /* + ** If there is no sight range, then this object isn't discovered by the player unless + ** it actually appears in a cell mapped by the player. + */ + if (Class->SightRange == 0) { + IsDiscoveredByPlayer = false; + } + + Set_Occupy_Bit(coord); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * * + * This routine intercepts the Greatest_Threat request and adds the appropriate target * + * types to search for. For regular infantry, this consists of all the ground types. For * + * rocket launching infantry, this also includes aircraft. * + * * + * INPUT: threat -- The basic threat control value. * + * * + * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * + * target could be found, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 09/28/1995 JLB : Engineers try to recapture buildings first. * + *=============================================================================================*/ +TARGET InfantryClass::Greatest_Threat(ThreatType threat) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Engineers consider only buildings that can be captured as being a threat. All others + ** are ignored. If there is a building that needs to be recaptured and it is nearby + ** then automatically head toward it to recapture it. + */ + if (!House->IsHuman && Class->IsCapture && !Is_Weapon_Equipped()) { + if (House->ToCapture != TARGET_NONE && Distance(House->ToCapture) < 0x0F00) { + return(House->ToCapture); + } + threat = threat | THREAT_CAPTURE; + } + + if (!Is_Weapon_Equipped()) { + if (!Class->IsCapture && *this != INFANTRY_RENOVATOR && *this != INFANTRY_SPY && *this != INFANTRY_THIEF) { + return(TARGET_NONE); + } + } + + /* + ** Special hack to make Tanya not auto-fire if controlled by a + ** human player. + */ + if (*this == INFANTRY_TANYA && House->IsHuman) { + return(TARGET_NONE); + } + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + + /* + ** Organic weapon types don't consider anything but infantry to be a threat. Such + ** weapon types would be the dog jaw and the medic first aid kit. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->WarheadPtr->IsOrganic) { + threat = threat & ~(THREAT_BUILDINGS|THREAT_VEHICLES|THREAT_BOATS|THREAT_AIR); + } + + /* + ** Human controlled infantry don't automatically fire upon buildings. + */ + if (Is_Weapon_Equipped() && House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + + /* + ** If this is a bomber type, then allow buildings to be considered a threat. + */ + if (Class->IsBomber && !House->IsHuman) { + threat = threat | THREAT_BUILDINGS; + } + + /* + ** Special hack: if it's a thief, then the only possible objects to + ** consider are tiberium-processing objects (silos & refineries). + */ + if (*this == INFANTRY_THIEF) { + threat = threat | THREAT_CAPTURE | THREAT_TIBERIUM; +// threat = (ThreatType)(THREAT_CAPTURE | THREAT_TIBERIUM); + } + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * * + * This routine handles playing an audio response as a result of the player selecting the * + * infantry unit. This occurs prior to giving it an order and may not be followed by any * + * order at all. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Select(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response = VOC_NONE; + if (Class->IsFemale) { + response = VOC_GIRL_YEAH; + } else { + response = VOC_GUY_YEAH; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_YES,VOC_ENG_ENG}; + static VocType _ein_response[] = {VOC_E_AH}; + static VocType _dog_response[] = {VOC_DOG_YES}; + static VocType _spy_response[] = {VOC_SPY_COMMANDER,VOC_SPY_YESSIR}; + static VocType _medic_response[] = {VOC_MED_REPORTING,VOC_MED_YESSIR}; + static VocType _tanya_response[] = {VOC_TANYA_YEA,VOC_TANYA_YES,VOC_TANYA_WHATS}; + static VocType _thief_response[] = {VOC_THIEF_YEA,VOC_THIEF_WHAT}; + static VocType _default_response[] = {VOC_ACKNOWL,VOC_REPORT,VOC_REPORT,VOC_YESSIR,VOC_YESSIR,VOC_READY,VOC_AWAIT}; + static VocType _stavros[] = {VOC_STAVCMDR,VOC_STAVYES}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic_response[] = {VOC_MECHHOWDY1,VOC_MECHHUH1,VOC_MECHLAFF1}; + static VocType _shock_response[] = {VOC_STYES1,VOC_STJUMP1,VOC_STJUICE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic_response; + size = ARRAY_SIZE(_mechanic_response); + break; + case INFANTRY_SHOCK: + response = _shock_response; + size = ARRAY_SIZE(_shock_response); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * * + * When the infantry is given the order to move, this routine handles the audio response * + * generated by the infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Move(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _dog_response[] = {VOC_DOG_BARK}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_ROCK}; +#else + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_GIVE}; +#endif + static VocType _thief_response[] = {VOC_THIEF_MOVEOUT,VOC_THIEF_OKAY,VOC_THIEF_AFFIRM}; + static VocType _default_response[] = {VOC_ROGER,VOC_RIGHT_AWAY,VOC_UGOTIT,VOC_AFFIRM,VOC_AFFIRM}; + static VocType _stavros[] = {VOC_STAVMOV,VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYES1,VOC_MECHRISE1,VOC_MECHHEAR1,VOC_MECHBOSS1}; + static VocType _shock[] = {VOC_STPOWER1,VOC_STDANCE1,VOC_STCHRGE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; + +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * * + * When the player gives an infantry unit the order to attack, this routine handles * + * the audio response by that unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _dog_response[] = {VOC_DOG_GROWL2}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH}; +#else + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH,VOC_TANYA_ROCK}; +#endif + static VocType _thief_response[] = {VOC_NONE}; + static VocType _default_response[] = {VOC_RIGHT_AWAY,VOC_AFFIRM,VOC_AFFIRM,VOC_UGOTIT,VOC_NO_PROB,VOC_YESSIR,VOC_YESSIR,VOC_YESSIR}; + static VocType _stavros[] = {VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYEEHAW1,VOC_MECHHOTDIG1,VOC_MECHWRENCH1}; + static VocType _shock[] = {VOC_STLIGHT1,VOC_STBURN1,VOC_STCRISP1,VOC_STSHOCK1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * * + * This routine checks to see if the infantry unit can capture the specified object rather * + * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * + * * + * INPUT: object -- The object that the mouse is currently over. * + * * + * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(ObjectClass const * object) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + assert(object != NULL); + + ActionType action = FootClass::What_Action(object); + + /* + ** If this is an engineer/renovator, we have to make some adjustments. + ** If the cursor is over an enemy building, return action-none. If it's + ** over a friendly building, we have to return action-capture so he can + ** renovate it. + ** However, abort the whole thing if the building is a barrel or mine. + */ + if (*this == INFANTRY_RENOVATOR && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + BuildingClass const * bldg = (BuildingClass *)object; + if (bldg->Class->IsRepairable) { + if (House->Is_Ally(bldg)) { + if (bldg->Health_Ratio() == 1) { + return(ACTION_NO_GREPAIR); + } + return(ACTION_GREPAIR); + } else { + + if (bldg->Class->IsCaptureable) { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (bldg->Health_Ratio() <= EngineerCaptureLevel) { +#else + if (bldg->Health_Ratio() <= Rule.ConditionRed) { +#endif + return(ACTION_CAPTURE); + } + return(ACTION_DAMAGE); + } + +// if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Class->IsCaptureable) { + } + } + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. In CSII, if this is a mechanic and the + ** cursor's over a friendly vehicle, execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (House->Is_Ally(object)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (object->What_Am_I() == RTTI_INFANTRY && object != this && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (object->What_Am_I() == RTTI_UNIT || object->What_Am_I() == RTTI_AIRCRAFT) ) ) { + + if (object->Health_Ratio() < Rule.ConditionGreen) { +// If it's a mechanic force-moving into an APC, don't try to heal it. + if(*this == INFANTRY_MECHANIC && object->What_Am_I() == RTTI_UNIT && *(UnitClass *)object == UNIT_APC && (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)) ) { + } else { + return(ACTION_HEAL); + } + } + } +#else + if(object->What_Am_I() == RTTI_INFANTRY && object != this) { + if (object->Health_Ratio() < Rule.ConditionGreen) { + return(ACTION_HEAL); + } + } +#endif + if(!object->Is_Techno() || !((TechnoClass *)object)->Techno_Type_Class()->Max_Passengers()) { + if (action == ACTION_GUARD_AREA || action == ACTION_MOVE) { + return(action); + } + return(ACTION_SELECT); + } + } else { + return(ACTION_NOMOVE); + } + } + +#ifdef OBSOLETE + /* + ** See if it's a thief attacking an enemy vehicle, let him CAPTURE it. + */ + if (*this == INFANTRY_THIEF && object->What_Am_I() == RTTI_UNIT) { + if (((UnitClass *)object)->House != House) { + return(ACTION_CAPTURE); + } + } +#endif + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK && object->What_Am_I() != RTTI_INFANTRY) { + action = ACTION_NONE; + } + + /* + ** See if it's a commando, and if he's attacking a building, + ** have him return ACTION_SABOTAGE instead + */ + if (Class->IsBomber && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * obj = (BuildingClass *)object; + /* + ** Hack: Tanya should shoot barrels, bomb other structures. + */ + if (obj->Class->IsRepairable) { +// if (*obj != STRUCT_BARREL && *obj != STRUCT_BARREL3) { + return(ACTION_SABOTAGE); + } else { + return(ACTION_ATTACK); + } + } + + /* + ** See if this infantry is trying to move onto where a land mine is. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** There is no self-select action available for infantry types. + */ + if (action == ACTION_SELF) { + action = ACTION_NONE; + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (object->What_Am_I() != RTTI_VESSEL || *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + + if (Class->IsCapture && action == ACTION_ATTACK) { + if (!House->Is_Ally(object) && ( +//Disable capturing of helicopters (object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || + (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable) ) + ) { + + if (*this == INFANTRY_THIEF && (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->Capacity == 0)) { + action = ACTION_NONE; + } else { + + /* + ** If we're trying to capture a building, make sure we can get + ** to it. Find an adjacent cell that's the same zone as us. + ** The target circumstance is a naval yard that doesn't touch + ** the shore - a total island. In that case, we can't capture + ** it, so we shouldn't show the action-capture cursor. + */ + action = ACTION_CAPTURE; + if (object->What_Am_I() == RTTI_BUILDING) { + CELL cell = ::As_Cell(object->As_Target()); + int targzone = Map[::As_Cell(As_Target())].Zones[Class->MZone]; + short const *list = ((BuildingClass *)object)->Class->Occupy_List(false); + bool found = false; + while (*list != REFRESH_EOL && !found) { + CELL newcell = cell + *list++; + for (FacingType i=FACING_N; i < FACING_COUNT; i++) { + if (Map[Adjacent_Cell(newcell, i)].Zones[Class->MZone] == targzone) { + found = true; + break; + } + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + } else { + if (!Is_Weapon_Equipped()) { + action = ACTION_NONE; + } + } + } + + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * * + * This routine is called when the player clicks over an object while this infantry soldier * + * is selected. Capture attempts are prohibited if the infantry cannot capture. The * + * command might respond if told to sabotage something. * + * * + * INPUT: action -- The action that is nominally to be performed. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_GREPAIR: + case ACTION_DAMAGE: + case ACTION_CAPTURE: + action = ACTION_CAPTURE; + break; + + case ACTION_HEAL: + action = ACTION_ATTACK; + break; + +// case ACTION_ENTER: +// action = ACTION_MOVE; +// break; + + case ACTION_SABOTAGE: + case ACTION_ATTACK: + case ACTION_GUARD_AREA: + case ACTION_MOVE: + action = action; + break; + + default: +// action = ACTION_NONE; + break; + } + + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * * + * INPUT: CELL - the cell we are setting the bit in * + * * + * int - the spot index we are setting the bit for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Set the occupy position for the spot that we passed in + */ + Map[cell].Flag.Composite |= (1 << spot_index); + + /* + ** Record the type of infantry that now owns the cell + */ + Map[cell].InfType = Owner(); +} + + +/*************************************************************************** + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Clear the occupy bit for the infantry in that cell + */ + Map[cell].Flag.Composite &= ~(1 << spot_index); + + /* + ** If he was the last infantry recorded in the cell then + ** remove the infantry ownership flag. + */ + if (!(Map[cell].Flag.Composite & 0x1F)) { + Map[cell].InfType = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * * + * This routine will return with the full name (as a text number) for this infantry * + * unit. Typically, this is the normal name, but in cases of civilian type survivors from * + * a building explosion, it might be a technician instead. In such a case, the special * + * technician name number is returned instead. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name to use for this infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/28/1996 JLB : Spy returns "enemy soldier" text name. * + *=============================================================================================*/ +int InfantryClass::Full_Name(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (IsTechnician) { + return(TXT_TECHNICIAN); + } + + if (*this == INFANTRY_SPY && !House->IsPlayerControl) { + return(TXT_E1); + } + + return(Class->Full_Name()); +} + + +/*********************************************************************************************** + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * * + * This routine intercepts the normal attack mission and if an engineer is detected and the * + * target is a building, then the engineer will be automatically assigned the capture * + * mission. In other cases, the normal attack logic will proceed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + * 04/15/1996 BWG : Engineers can only attack their own house's buildings now. * + * 05/29/1996 JLB : Engineers can now damage/capture enemy buildings. * + *=============================================================================================*/ +int InfantryClass::Mission_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsBomber && As_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + if (Class->IsCapture && As_Building(TarCom) != NULL) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + return(1); + } + + return(FootClass::Mission_Attack()); +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * * + * This routine will determine what action to perform if the mouse was clicked on the cell * + * specified. This is just a courier function since the lower level classes actually * + * perform the work. The need for this routine at this level is due to the existence of * + * a similarly named function at this level as well. C++ namespace rules require this * + * function courier to be in place or an error will result. * + * * + * INPUT: cell -- The cell that the mouse might be clicked upon. * + * * + * OUTPUT: Returns with the action that would be given to this infantry unit if the mouse * + * were clicked at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(CELL cell) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK) { + action = ACTION_NONE; + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (action == ACTION_ATTACK) { + action = ACTION_NOMOVE; + } + } + + /* + ** Demolitioners may destroy a bridge + */ + if (Class->IsBomber && action == ACTION_MOVE && !Special.IsCaptureTheFlag) { + switch (Map[cell].TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: +// case TEMPLATE_BRIDGE_3A: +// case TEMPLATE_BRIDGE_3B: + return(ACTION_SABOTAGE); + } + } + +#ifdef OBSOLETE + /* + ** Engineers may repair a destroyed bridge. + */ + if (*this == INFANTRY_RENOVATOR && action == ACTION_NOMOVE) { + /* + ** If they're pointing on the wrong side of the bridge, ignore it + ** 'cause we can't get there. + */ + TemplateType tt = Map[cell].TType; + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D || + tt == TEMPLATE_BRIDGE_1C || tt == TEMPLATE_BRIDGE_2C || + (tt >= TEMPLATE_BRIDGE_3C && tt <= TEMPLATE_BRIDGE_3E) ) { + /* + ** We know they're pointing at a destroyed bridge cell. If the cell + ** they're pointing at is surrounded by impassables, return this + ** cell as impassable. But, if any cell surrounding this cell is + ** passable, return that this is a capturable cell. + */ + if (Map[cell].Land_Type() == LAND_ROCK) { + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + int y = Cell_Y(cell); + if (y) { + LandType above = Map[(CELL)(cell-(MAP_CELL_W-1))].Land_Type(); + if (above == LAND_CLEAR || above == LAND_ROAD) { + if (Map[(CELL)(cell-(MAP_CELL_W-1))].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + if (y < MAP_CELL_H) { + LandType below = Map[(CELL)(cell + MAP_CELL_W-1)].Land_Type(); + if (below == LAND_CLEAR || below == LAND_ROAD) { + if (Map[(CELL)(cell+MAP_CELL_W-1)].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + } + return(ACTION_NOMOVE); + } + } +#endif + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * * + * This routine will return a reference to the infantry type class object that describes * + * this infantry's characteristics. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the InfantryTypeClass object associated with this * + * infantry object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & InfantryClass::Class_Of(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Read_INI(CCINIClass & ini) +{ + InfantryClass * infantry; // Working infantry pointer. + HousesType inhouse; // Infantry house. + InfantryType classid; // Infantry class. + char buf[128]; + char * validation; + DirType dir; + TriggerTypeClass * tp; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get an infantry entry + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); + if (inhouse != HOUSE_NONE) { + + /* + ** 2nd token: infantry type name. + */ + classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); + + if (classid != INFANTRY_NONE) { + + infantry = new InfantryClass(classid, inhouse); + if (infantry != NULL) { + + /* + ** 3rd token: strength. + */ + int strength = atoi(strtok(NULL, ",\n\r")); + + /* + ** 4th token: cell #. + */ + CELL cell = atoi(strtok(NULL, ",\n\r")); + COORDINATE coord = Cell_Coord(cell); + + /* + ** 5th token: cell sub-location. + */ + int sub = atoi(strtok(NULL, ",")); + coord = Coord_Add(Coord_Whole(coord), StoppingCoordAbs[ sub ]); + + /* + ** Fetch the mission and facing. + */ + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + validation = strtok(NULL, ",\n\r"); + if (validation) { + dir = (DirType)atoi(validation); + validation = strtok(NULL, ",\n\r"); + if (validation) { + tp = TriggerTypeClass::From_Name(validation); + } else { + tp = NULL; + } + } else { + dir = (DirType)0; + tp = NULL; + } + + infantry->Trigger = NULL; + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + infantry->Trigger = tt; + } + } + + if (infantry->Unlimbo(coord, dir)) { + infantry->Strength = infantry->Class_Of().MaxStrength * fixed(strength, 256); + if (infantry->Strength > infantry->Class->MaxStrength-3) infantry->Strength = infantry->Class->MaxStrength; +// infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); + if (Session.Type == GAME_NORMAL || infantry->House->IsHuman) { + infantry->Assign_Mission(mission); + infantry->Commence(); + } else { + infantry->Enter_Idle_Mode(); + } + } else { + + /* + ** If the infantry could not be unlimboed, then this is a big error. + ** Delete the infantry. + */ + delete infantry; + } + } + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * * + * This will store all the infantry objects to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the infantry data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing infantry data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the infantry data out. + */ + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (!infantry->IsInLimbo) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", + infantry->House->Class->IniName, + infantry->Class->IniName, + infantry->Health_Ratio()*256, + Coord_Cell(infantry->Coord), + CellClass::Spot_Index(infantry->Coord), + MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? + infantry->MissionQueue : infantry->Mission), + infantry->PrimaryFacing.Current(), + infantry->Trigger.Is_Valid() ? infantry->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * * + * Use this routine to handle the fear logic for this infantry. It will slowly increase * + * the bravery of the infantry as well as cause it to stand up or lie down as appropriate. * + * It will even handle the special fraidy cat logic for civilian infantry. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once per game logic loop per infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Fear_AI(void) +{ + /* + ** After a time, the infantry will gain courage. + */ + if (Fear > 0) { + + Fear--; + + /* + ** When an armed civilian becomes unafraid, he will then reload + ** another clip into his pistol. + */ + if (Fear == 0 && Ammo == 0 && Is_Weapon_Equipped()) { + Ammo = Class->MaxAmmo; + } + + /* + ** Stand up if brave and lie down if afraid. + */ + if (IsProne) { + if (Fear < FEAR_ANXIOUS) { + Do_Action(DO_GET_UP); + } + } else { + + /* + ** Drop to the ground if anxious. Don't drop to the ground while moving + ** and the special elite flag is active. + */ + if (!Class->IsDog && Height == 0 && Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving))) { + Do_Action(DO_LIE_DOWN); + } + } + } + + /* + ** When in darkness or in doubt, + ** run in circles, scream, and shout. + */ + if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsFalling && !IsDriving && !Target_Legal(NavCom)) { + Scatter(0, true); + } +} + + +/*********************************************************************************************** + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * * + * This routine will detect when the infantry has left the edge of the world and will * + * delete it as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit deleted by this routine? * + * * + * WARNINGS: Be sure the check the return value and if true, abort any further processing * + * for this infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Edge_Of_World_AI(void) +{ + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Team.Is_Valid() && IsLocked) Team->IsLeaveMap = true; + + if (!Team.Is_Valid() && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * * + * This will examine the infantry and determine what firing action is required. It will * + * search for targets, starting firing animations, and launch bullets as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Firing_AI(void) +{ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + if (!IsFiring) { + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + if (Combat_Damage(primary) < 0) { + ObjectClass * targ= As_Object(TarCom); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (targ) { + if( (targ->What_Am_I() == RTTI_INFANTRY && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (targ->What_Am_I() == RTTI_AIRCRAFT || targ->What_Am_I() == RTTI_UNIT )) ) { + + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } + } else { + Assign_Target(TARGET_NONE); + } +#else + if (targ && targ->What_Am_I() == RTTI_INFANTRY) { + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } else { + Assign_Target(TARGET_NONE); + } +#endif + } + break; + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + /* + ** Start firing animation. + */ + if (IsProne) { + Do_Action(DO_FIRE_PRONE); + } else { + Do_Action(DO_FIRE_WEAPON); + } + + Mark(MARK_OVERLAP_UP); + IsFiring = true; + Mark(MARK_OVERLAP_DOWN); + + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + + /* + ** If the target is in range, and the NavCom is the same, then just + ** stop and keep firing. + */ + if (TarCom == NavCom) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + break; + } + } + + /* + ** If in the middle of firing animation, then only + ** process that. Infantry cannot fire and move simultaneously. + ** At some point in the firing animation process, a projectile + ** will be launched. When the required animation frames have + ** been completed, the firing animation stops. + */ + int firestage = Class->FireLaunch; + if (IsProne) firestage = Class->ProneLaunch; + + if (IsFiring && Fetch_Stage() == firestage) { + Fire_At(TarCom, primary); + + /* + ** Run away from slowly approaching projectiles. + */ + if (Class->PrimaryWeapon->MaxSpeed < Rule.Incoming) { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** If it's a dog, get rid of him (he'll be re-created when he hits) + */ + if (Class->IsDog) { + WasSelected = IsSelected; + ScenarioInit++; + Limbo(); + ScenarioInit--; + } + } + } else { + if (IsFiring) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * * + * Infantry can be in one of many different animation sequences. At the conclusion of each * + * sequence, the infantry will quite likely transition to a new animation state. This * + * routine handles detecting when that trasition should occur and starting the infantry * + * into its new state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Doing_AI(void) +{ + if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { + switch (Doing) { + default: + if (IsDriving) { + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } + } else { + if (Class->IsDog) { + Do_Action(DO_STAND_READY, true); + } else { + if (IsProne) { + Do_Action(DO_PRONE, true); + } else { + Do_Action(DO_STAND_READY, true); + } + } + } + break; + + case DO_DOG_MAUL: + Do_Action(DO_STAND_READY, true); + break; + + case DO_GUN_DEATH: + case DO_EXPLOSION_DEATH: + case DO_EXPLOSION2_DEATH: + case DO_GRENADE_DEATH: + case DO_FIRE_DEATH: + if (Fetch_Stage() >= Class->DoControls[Doing].Count) { + if (Doing == DO_GUN_DEATH && !Class->IsDog && Height==0) { + new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_GRENADE_DEATH && !Class->IsDog && Height==0) { + new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-10, 3))); + } + if (Doing == DO_EXPLOSION_DEATH && !Class->IsDog && Height==0) { + new AnimClass(ANIM_CORPSE3, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_EXPLOSION2_DEATH && !Class->IsDog && Height==0) { + new AnimClass(ANIM_CORPSE2, Center_Coord()); + } + delete this; + return; + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * * + * It examines the infantry state and determines what movement action should be initiated * + * or processed. It handles the actual movement of the infantry as well as any path finding * + * or infantry startup logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Movement_AI(void) +{ + /* + ** Special hack check to ensure that infantry will never get stuck in a movement order if + ** there is no place to go. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } + + if (!IsFiring && !IsFalling && Doing != DO_DOG_MAUL) { + if (!IsDriving) { + + /* + ** When in guard mode, never allow a valid navcom. + */ + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { + Assign_Destination(TARGET_NONE); +// if (IsTethered) Scatter(0, true); + } + + /* + ** Double check to make sure it doesn't have a movement destination into a zone + ** that it can't travel to. In such a case, abort the movement process by clearing + ** the navigation computer. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && !IsDriving && !IsTethered && Target_Legal(NavCom) && IsLocked && Map[Coord].Zones[Class->MZone] != Map[As_Cell(NavCom)].Zones[Class->MZone]) { +// hack: if it's tanya, spy, or engineer, let 'em move there anyway. + if (!Class->IsCapture && Mission != MISSION_ENTER) { +// if (*this != INFANTRY_TANYA && *this != INFANTRY_SPY && *this != INFANTRY_RENOVATOR) { + Assign_Destination(TARGET_NONE); + } + } + + /* + ** A head to coordinate is needed. If there is no path + ** available, then create one. + */ + if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { + + /* + ** Determine if the next cell in the list is available + ** to be entered. If not, then abort the path and try + ** again. + */ + if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { + Path[0] = FACING_NONE; + } + + /* + ** Check to see if the target is closer than expected. This occurs + ** when heading toward a moving object and that object is heading + ** toward the unit. Shorten the precalculated path to be no longer + ** than the distance to the target. + */ + int d = Lepton_To_Cell(Distance(NavCom)); + if (d < CONQUER_PATH_MAX) { + Path[d] = FACING_NONE; + } + + /* + ** Find a path to follow if one isn't already calculated. + */ + if (Path[0] == FACING_NONE) { + + /* + ** Calculate the path from the current location to the + ** destination indicated by the navigation computer. If there + ** was a fundamental error with finding a path, then this + ** indicates that basic path & movement logic needs to be + ** aborted. + */ + if (PathDelay != 0) { + return; + } + if (!Basic_Path()) { + + /* + ** Check to ensure that if a computer controlled unit is in + ** hunt mode, but cannot reach the target it would like to, + ** abort the target tracking and let the normal hunt logic + ** assign a new one. + */ + if (!House->IsHuman && Mission == MISSION_HUNT) { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + } else { + + /* + ** If the infantry unit is close enough to the target, then + ** tell it to stop. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !IsTethered) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** Update the try try again counter so that this + ** infantry unit will try again at a later time. + */ + if (TryTryAgain) { + TryTryAgain--; + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + /* + ** Abort the target and destination process since the path + ** could not be found. In such a case, processing should stop + ** or else the game will bog down with repeated path failures. + ** Only perform the abort of the target is in a different zone. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && IsLocked && Target_Legal(NavCom) && Map[As_Cell(NavCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Destination(TARGET_NONE); + } + if (IsLocked && Target_Legal(TarCom) && Map[As_Cell(TarCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Target(TARGET_NONE); + } + } + } + } + Stop_Driver(); + return; + } + TryTryAgain = PATH_RETRY; + } + + /* + ** Determine the coordinate to head to based on the infantry's + ** current location and the next location in the path. + */ + COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); + CELL acell = Coord_Cell(acoord); + + if (Can_Enter_Cell(acell) != MOVE_OK) { + + if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered /*&& House->IsHuman*/ && Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { + if (Map[acell].Cell_Object()) { + if (!House->Is_Ally(Map[acell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); + } + } + } + } + + Path[0] = FACING_NONE; + Stop_Driver(); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + } else { + if (Start_Driver(acoord)) { + if (!IsActive) return; + PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); + if (IsFormationMove) { + Set_Speed(Ground[Map[Coord].Land_Type()].Cost[FormationSpeed] * 256); + } else { + Set_Speed(0xFF); + } + + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } + } + } + } + + } else { + + /* + ** The infantry knows where it should be headed, so head there. Check + ** to see if the infantry is "close enough" to the desired location that + ** it should just consider itself to have arrived. In this case, force + ** the infantry to the destination location and mark this path step + ** as complete. + */ + Mark(MARK_UP); + if (Distance(Head_To_Coord()) < 0x0010) { + + memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); + Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; + Coord = Head_To_Coord(); + Per_Cell_Process(PCP_END); + if (!IsActive || IsInLimbo) return; + + Stop_Driver(); + if (!IsActive || IsInLimbo) return; + + if (Coord_Cell(Coord) == As_Cell(NavCom)) { + NavCom = TARGET_NONE; + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + //Stop_Driver(); + Path[0] = FACING_NONE; + } + } else { + int movespeed = Speed; + + /* + ** When prone, the infantry moves at half speed or double + ** speed. This depends on whether the infantry actually has + ** prone animation stages. Civilians don't, and so they + ** run instead. + */ + if (Class->IsDog && Target_Legal(TarCom)) { + movespeed *= 2; + } + + if (IsProne && !Class->IsDog) { + if ((Class->IsFraidyCat && !Class->IsCrawling) ) { + movespeed = Speed*2; + } else { + movespeed = Speed/2; + } + } + + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + + /* + ** Advance the infantry as far as it should go. + */ + MPHType maxspeed = MPHType(min(Class->MaxSpeed * SpeedBias * House->GroundspeedBias, MPH_LIGHT_SPEED)); + + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + Coord = Coord_Move(Coord, Direction(Head_To_Coord()), maxspeed * fixed(movespeed, 256)); + } + Mark(MARK_DOWN); + } + IsNewNavCom = false; + } +} + + +/*********************************************************************************************** + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * * + * The image data for the infantry differs from normal if this is a spy. A spy always * + * appears like a minigunner to the non-owning players. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the image data to use for this infantry soldier. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * InfantryClass::Get_Image_Data(void) const +{ + if (!IsOwnedByPlayer && *this == INFANTRY_SPY) { + return(MFCD::Retrieve("E1.SHP")); + } + return(TechnoClass::Get_Image_Data()); +} + + +/*********************************************************************************************** + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle * + * * + * This routine will examine this infantry and determine if it is allowed and ready to * + * perform an idle animation. The conditions under which idle animations can be performed * + * are restrictive. Hence this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this infantry ready to do an idle animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Is_Ready_To_Random_Animate(void) const +{ + /* + ** See if the base classes (more rudimentary checking) determines that idle animations + ** cannot occur. If they cannot, then return with the failure code. + */ + if (!FootClass::Is_Ready_To_Random_Animate()) { + return(false); + } + + /* + ** While the infantry is in the air (such as when paradropping), it won't be allowed + ** to idle animate. + */ + if (Height > 0) { + return(false); + } + + /* + ** When the infantry is walking or otherwise engauged in travel, it won't idle animate. + */ + if (IsDriving) { + return(false); + } + + /* + ** When prone, idle animations cannot occur. This is primarily because there are no prone + ** idle animations. + */ + if (IsProne) { + return(false); + } + + /* + ** When firing, the infantry should not perform any idle animations. + */ + if (IsFiring) { + return(false); + } + + /* + ** Only if the infantry is in guard or ready stance is idle animations allowed. This is + ** because the idle animations start and end with these frames. + */ + if (Doing != DO_STAND_GUARD && Doing != DO_STAND_READY) { + return(false); + } + + /* + ** Since no reason was found to indicate it is not a good time to idle + ** animate, then it must be a good time to do so. + */ + return(true); +} + + +/*********************************************************************************************** + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * * + * This routine will paradrop this soldier at the location specified. It will cause the * + * soldier to hunt if controlled by the computer and to guard if controlledy by the * + * human. * + * * + * INPUT: coord -- The coordinate to paradrop the soldier to. * + * * + * OUTPUT: bool; Was the paradrop successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Paradrop(COORDINATE coord) +{ + if (FootClass::Paradrop(coord)) { + if (House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + return(true); + } + return(false); +} diff --git a/CODE/INFANTRY.H b/CODE/INFANTRY.H new file mode 100644 index 0000000..fa08df5 --- /dev/null +++ b/CODE/INFANTRY.H @@ -0,0 +1,225 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INFANTRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INFANTRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INFANTRY_H +#define INFANTRY_H + + +class InfantryClass : public FootClass +{ + public: + CCPtr Class; + + /* + ** If the infantry is undergoing some choreographed animation sequence, then + ** this holds the particular sequence number. The frame of animation is kept + ** track of by the regular frame tracking system. When performing an animation + ** sequence, the infantry cannot perform anything else (even move). + */ + DoType Doing; + + /* + ** Certain infantry will either perform some comment or say something after an + ** amount of time has expired subsequent to an significant event. This is the + ** timer the counts down. + */ + CDTimerClass Comment; + + /* + ** If this civilian is actually a technician, then this flag will be true. + ** It should only be set for the civilian type infantry. Typically, the + ** technician appears after a building is destroyed. + */ + unsigned IsTechnician:1; + + /* + ** If the infantry just performed some feat, then it may respond with an action. + ** This flag will be true if an action is to be performed when the Comment timer + ** has expired. + */ + unsigned IsStoked:1; + + /* + ** This flag indicates if the infantry unit is prone. Prone infantry become that way + ** when they are fired upon. Infantry in the prone position are less vulnerable to + ** combat. + */ + unsigned IsProne:1; + + /* + ** If the infantry is allowed to move one cell from one zone to another, then this + ** flag will be true. It exists only so that when a bridge is destroyed, the bomb + ** placer is allowed to run from the destroyed bridge cell back onto a real cell. + */ + unsigned IsZoneCheat:1; + + /* + ** This flag is set for the dogs, when they launch into bullet mode. + ** it's to remember if the unit was selected, and if it was, then + ** when the dog is re-enabled, he'll reselect himself. + */ + unsigned WasSelected:1; + + /* + ** The fear rating of this infantry unit. The more afraid the infantry, the more + ** likely it is to panic and seek cover. + */ + FearType Fear; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + InfantryClass(InfantryType classid, HousesType house); + InfantryClass(NoInitClass const & x) : FootClass(x), Class(x), Comment(x) {}; + virtual ~InfantryClass(void); + operator InfantryType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Destination(TARGET); + + /* + ** Query functions. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + void const * Get_Image_Data(void) const; + int Shape_Number(void) const; + virtual ObjectTypeClass const & Class_Of(void) const; + virtual int Full_Name(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType facing); + virtual bool Paradrop(COORDINATE coord); + virtual bool Limbo(void); + virtual void Detach(TARGET target, bool all); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw = false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell) {FootClass::Active_Click_With(action, cell);} + + /* + ** Combat related. + */ + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual BulletClass * Fire_At(TARGET target, int which); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual void Assign_Target(TARGET); + void Set_Occupy_Bit(COORDINATE coord) {Set_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Set_Occupy_Bit(CELL cell, int spot_index); + void Clear_Occupy_Bit(COORDINATE coord) {Clear_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Clear_Occupy_Bit(CELL cell, int spot_index); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual void AI(void); + void Fear_AI(void); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual int Mission_Attack(void); + bool Edge_Of_World_AI(void); + void Firing_AI(void); + void Doing_AI(void); + void Movement_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "INFANTRY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Movement and animation. + */ + virtual bool Do_Action(DoType todo, bool force=false); + virtual bool Random_Animate(void); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + /* + ** Translation table to convert facing into infantry shape number. This special + ** table is needed since several facing stages are reused and flipped about the Y + ** axis. + */ + static int const HumanShape[32]; + + private: + + static DoStruct const MasterDoControls[DO_COUNT]; +}; + +#endif diff --git a/CODE/INI.CPP b/CODE/INI.CPP new file mode 100644 index 0000000..96203f5 --- /dev/null +++ b/CODE/INI.CPP @@ -0,0 +1,1284 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * INIClass::Find_Entry -- Find specified entry within section. * + * INIClass::Find_Section -- Find the specified section within the INI data. * + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name* + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * INIClass::Load -- Load INI data from the file specified. * + * INIClass::Load -- Load the INI data from the data stream (straw). * + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * INIClass::Put_PKey -- Stores the key to the INI database. * + * INIClass::Put_String -- Output a string to the section and entry specified. * + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * INIClass::Save -- Save the ini data to the file specified. * + * INIClass::Save -- Saves the INI data to a pipe stream. * + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * INIClass::~INIClass -- Destructor for INI handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include "ini.h" +#include "readline.h" +#include "xpipe.h" +#include "b64pipe.h" +#include "xstraw.h" +#include "b64straw.h" + + +#ifdef FIXIT_FAST_LOAD +#include "cstraw.h" +#endif + + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + + +/*********************************************************************************************** + * INIClass::~INIClass -- Destructor for INI handler. * + * * + * This is the destructor for the INI class. It handles deleting all of the allocations * + * it might have done. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::~INIClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * * + * This routine is used to clear out the section specified. If no section is specified, * + * then the entire INI data is cleared out. Optionally, this routine can be used to clear * + * out just an individual entry in the specified section. * + * * + * INPUT: section -- Pointer to the section to clear out [pass NULL to clear all]. * + * * + * entry -- Pointer to optional entry specifier. If this parameter is specified, * + * then only this specific entry (if found) will be cleared. Otherwise, * + * the entire section specified will be cleared. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 08/21/1996 JLB : Optionally clears section too. * + * 11/02/1996 JLB : Updates the index list. * + *=============================================================================================*/ +bool INIClass::Clear(char const * section, char const * entry) +{ + if (section == NULL) { + SectionList.Delete(); + SectionIndex.Clear(); + } else { + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + if (entry != NULL) { + INIEntry * entptr = secptr->Find_Entry(entry); + if (entptr != NULL) { + /* + ** Remove the entry from the entry index list. + */ + secptr->EntryIndex.Remove_Index(entptr->Index_ID()); + + delete entptr; + } + } else { + /* + ** Remove this section index from the section index list. + */ + SectionIndex.Remove_Index(secptr->Index_ID()); + + delete secptr; + } + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load INI data from the file specified. * + * * + * Use this routine to load the INI class with the data from the specified file. * + * * + * INPUT: file -- Reference to the file that will be used to fill up this INI manager. * + * * + * OUTPUT: bool; Was the file loaded successfully? * + * * + * WARNINGS: This routine allocates memory. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Load(FileClass & file) +{ + return(Load(FileStraw(file))); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load the INI data from the data stream (straw). * + * * + * This will fetch data from the straw and build an INI database from it. * + * * + * INPUT: straw -- The straw that the data will be provided from. * + * * + * OUTPUT: bool; Was the database loaded ok? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_FAST_LOAD +bool INIClass::Load(Straw & ffile) +#else +bool INIClass::Load(Straw & file) +#endif +{ + bool end_of_file = false; + char buffer[MAX_LINE_LENGTH]; + +#ifdef FIXIT_FAST_LOAD + CacheStraw file; + file.Get_From(ffile); +#endif + + /* + ** Prescan until the first section is found. + */ + while (!end_of_file) { + Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (end_of_file) return(false); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + } + + /* + ** Process a section. The buffer is prefilled with the section name line. + */ + while (!end_of_file) { + + buffer[0] = ' '; + char * ptr = strchr(buffer, ']'); + if (ptr) *ptr = '\0'; + strtrim(buffer); + INISection * secptr = new INISection(strdup(buffer)); + if (secptr == NULL) { + Clear(); + return(false); + } + + /* + ** Read in the entries of this section. + */ + while (!end_of_file) { + + /* + ** If this line is the start of another section, then bail out + ** of the entry loop and let the outer section loop take + ** care of it. + */ + int len = Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + + /* + ** Determine if this line is a comment or blank line. Throw it out if it is. + */ + Strip_Comments(buffer); + if (len == 0 || buffer[0] == ';' || buffer[0] == '=') continue; + + /* + ** The line isn't an obvious comment. Make sure that there is the "=" character + ** at an appropriate spot. + */ + char * divider = strchr(buffer, '='); + if (!divider) continue; + + /* + ** Split the line into entry and value sections. Be sure to catch the + ** "=foobar" and "foobar=" cases. These lines are ignored. + */ + *divider++ = '\0'; + strtrim(buffer); + if (!strlen(buffer)) continue; + + strtrim(divider); + if (!strlen(divider)) continue; + + INIEntry * entryptr = new INIEntry(strdup(buffer), strdup(divider)); + if (entryptr == NULL) { + delete secptr; + Clear(); + return(false); + } + + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + secptr->EntryList.Add_Tail(entryptr); + } + + /* + ** All the entries for this section have been parsed. If this section is blank, then + ** don't bother storing it. + */ + if (secptr->EntryList.Is_Empty()) { + delete secptr; + } else { + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + SectionList.Add_Tail(secptr); + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Save -- Save the ini data to the file specified. * + * * + * Use this routine to save the ini data to the file specified. All existing data in the * + * file, if it was present, is replaced. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * OUTPUT: bool; Was the data written to the file? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(FileClass & file) const +{ + return(Save(FilePipe(file))); +} + + +/*********************************************************************************************** + * INIClass::Save -- Saves the INI data to a pipe stream. * + * * + * This routine will output the data of the INI file to a pipe stream. * + * * + * INPUT: pipe -- Reference to the pipe stream to pump the INI image to. * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(Pipe & pipe) const +{ + int total = 0; + + INISection * secptr = SectionList.First(); + while (secptr && secptr->Is_Valid()) { + + /* + ** Output the section identifier. + */ + total += pipe.Put("[", 1); + total += pipe.Put(secptr->Section, strlen(secptr->Section)); + total += pipe.Put("]", 1); + total += pipe.Put("\r\n", strlen("\r\n")); + + /* + ** Output all the entries and values in this section. + */ + INIEntry * entryptr = secptr->EntryList.First(); + while (entryptr && entryptr->Is_Valid()) { + total += pipe.Put(entryptr->Entry, strlen(entryptr->Entry)); + total += pipe.Put("=", 1); + total += pipe.Put(entryptr->Value, strlen(entryptr->Value)); + total += pipe.Put("\r\n", strlen("\r\n")); + + entryptr = entryptr->Next(); + } + + /* + ** After the last entry in this section, output an extra + ** blank line for readability purposes. + */ + total += pipe.Put("\r\n", strlen("\r\n")); + + secptr = secptr->Next(); + } + total += pipe.End(); + + return(total); +} + + +/*********************************************************************************************** + * INIClass::Find_Section -- Find the specified section within the INI data. * + * * + * This routine will scan through the INI data looking for the section specified. If the * + * section could be found, then a pointer to the section control data is returned. * + * * + * INPUT: section -- The name of the section to search for. Don't enclose the name in * + * brackets. Case is NOT sensitive in the search. * + * * + * OUTPUT: Returns with a pointer to the INI section control structure if the section was * + * found. Otherwise, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +INIClass::INISection * INIClass::Find_Section(char const * section) const +{ + if (section != NULL) { + + long crc = CRCEngine()(section, strlen(section)); + + if (SectionIndex.Is_Present(crc)) { + return(SectionIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * * + * This routine will scan through all the sections in the INI data and return a count * + * of the number it found. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of sections recorded in the INI data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Section_Count(void) const +{ + return(SectionIndex.Count()); +} + + +/*********************************************************************************************** + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * * + * This routine will examine the section specified and return with the number of entries * + * associated with it. * + * * + * INPUT: section -- Pointer to the section that will be examined. * + * * + * OUTPUT: Returns with the number entries in the specified section. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Entry_Count(char const * section) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->EntryIndex.Count()); + } + return(0); +} + + +/*********************************************************************************************** + * INIClass::Find_Entry -- Find specified entry within section. * + * * + * This support routine will find the specified entry in the specified section. If found, * + * a pointer to the entry control structure will be returned. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- Pointer to the entry name to search for. * + * * + * OUTPUT: If the entry was found, then a pointer to the entry control structure will be * + * returned. Otherwise, NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::Find_Entry(char const * section, char const * entry) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->Find_Entry(entry)); + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name. * + * * + * This will return the identifier name for the entry under the section specified. The * + * ordinal number specified is used to determine which entry to retrieve. The entry * + * identifier is the text that appears to the left of the "=" character. * + * * + * INPUT: section -- The section to use. * + * * + * index -- The ordinal number to use when fetching an entry name. * + * * + * OUTPUT: Returns with a pointer to the entry name. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +char const * INIClass::Get_Entry(char const * section, int index) const +{ + INISection * secptr = Find_Section(section); + + if (secptr != NULL && index < secptr->EntryIndex.Count()) { + INIEntry * entryptr = secptr->EntryList.First(); + + while (entryptr != NULL && entryptr->Is_Valid()) { + if (index == 0) return(entryptr->Entry); + index--; + entryptr = entryptr->Next(); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * * + * Use this routine to store an arbitrary length binary block of data into the INI database.* + * This routine will covert the data into displayable form and then break it into lines * + * that are stored in sequence to the section. A section used to store data in this * + * fashion can not be used for any other entries. * + * * + * INPUT: section -- The section identifier to place the data into. * + * * + * block -- Pointer to the block of binary data to store. * + * * + * len -- The length of the binary data. * + * * + * OUTPUT: bool; Was the data stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_UUBlock(char const * section, void const * block, int len) +{ + if (section == NULL || block == NULL || len < 1) return(false); + + Clear(section); + + BufferStraw straw(block, len); + Base64Straw bstraw(Base64Straw::ENCODE); + bstraw.Get_From(straw); + + int counter = 1; + + for (;;) { + char buffer[71]; + char sbuffer[32]; + + int length = bstraw.Get(buffer, sizeof(buffer)-1); + buffer[length] = '\0'; + if (length == 0) break; + + sprintf(sbuffer, "%d", counter); + Put_String(section, sbuffer, buffer); + counter++; + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * * + * This routine will take all the entries in the specified section and decompose them into * + * a binary block of data that will be stored into the buffer specified. By using this * + * routine [and the Put_UUBLock counterpart], arbitrary blocks of binary data may be * + * stored in the INI file. A section processed by this routine can contain no other * + * entries than those put there by a previous call to Put_UUBlock. * + * * + * INPUT: section -- The section name to process. * + * * + * block -- Pointer to the buffer that will hold the retrieved data. * + * * + * len -- The length of the buffer. The retrieved data will not fill past this * + * limit. * + * * + * OUTPUT: Returns with the number of bytes decoded into the buffer specified. * + * * + * WARNINGS: If the number of bytes retrieved exactly matches the length of the buffer * + * specified, then you might have a condition of buffer "overflow". * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_UUBlock(char const * section, void * block, int len) const +{ + if (section == NULL) return(0); + + Base64Pipe b64pipe(Base64Pipe::DECODE); + BufferPipe bpipe(block, len); + + b64pipe.Put_To(&bpipe); + + int total = 0; + int counter = Entry_Count(section); + for (int index = 0; index < counter; index++) { + char buffer[128]; + + int length = Get_String(section, Get_Entry(section, index), "=", buffer, sizeof(buffer)); + int outcount = b64pipe.Put(buffer, length); + total += outcount; + } + total += b64pipe.End(); + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * * + * This routine will take an arbitrarily long block of text and store it into the INI * + * database. The text is broken up into lines and each line is then stored as a numbered * + * entry in the specified section. A section used to store text in this way can not be used * + * to hold any other entries. The text is presumed to contain space characters scattered * + * throughout it and that one space between words and sentences is natural. * + * * + * INPUT: section -- The section to place the text block into. * + * * + * text -- Pointer to a null terminated text string that holds the block of * + * text. The length can be arbitrary. * + * * + * OUTPUT: bool; Was the text block placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_TextBlock(char const * section, char const * text) +{ + if (section == NULL) return(false); + + Clear(section); + + int index = 1; + while (text != NULL && *text != NULL) { + + char buffer[128]; + + strncpy(buffer, text, 75); + buffer[75] = '\0'; + + char b[32]; + sprintf(b, "%d", index); + + /* + ** Scan backward looking for a good break position. + */ + int count = strlen(buffer); + if (count > 0) { + if (count >= 75) { + while (count) { + char c = buffer[count]; + + if (isspace(c)) break; + count--; + } + + if (count == 0) { + break; + } else { + buffer[count] = '\0'; + } + } + + strtrim(buffer); + Put_String(section, b, buffer); + index++; + text = ((char *)text) + count; + } else { + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * * + * This will take all entries in the specified section and format them into a block of * + * normalized text. That is, text with single spaces between each concatenated line. All * + * entries in the specified section are processed by this routine. Use Put_TextBlock to * + * build the entries in the section. * + * * + * INPUT: section -- The section name to process. * + * * + * buffer -- Pointer to the buffer that will hold the complete text. * + * * + * len -- The length of the buffer specified. The text will, at most, fill this * + * buffer with the last character being forced to null. * + * * + * OUTPUT: Returns with the number of characters placed into the buffer. The trailing null * + * is not counted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_TextBlock(char const * section, char * buffer, int len) const +{ + if (len <= 0) return(0); + + buffer[0] = '\0'; + if (len <= 1) return(0); + + int elen = Entry_Count(section); + int total = 0; + for (int index = 0; index < elen; index++) { + + /* + ** Add spacers between lines of fetched text. + */ + if (index > 0) { + *buffer++ = ' '; + len--; + total++; + } + + Get_String(section, Get_Entry(section, index), "", buffer, len); + + int partial = strlen(buffer); + total += partial; + buffer += partial; + len -= partial; + if (len <= 1) break; + } + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * * + * Use this routine to store an integer value into the section and entry specified. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier used for the integer number. * + * * + * number -- The integer number to store in the database. * + * * + * format -- The format to store the integer. The format is generally only a * + * cosmetic affect. The Get_Int operation will interpret the value the * + * same regardless of what format was used to store the integer. * + * * + * 0 : plain decimal digit * + * 1 : hexadecimal digit (trailing "h") * + * 2 : hexadecimal digit (leading "$") * + * * + * OUTPUT: bool; Was the number stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +bool INIClass::Put_Int(char const * section, char const * entry, int number, int format) +{ + char buffer[MAX_LINE_LENGTH]; + + switch (format) { + default: + case 0: + sprintf(buffer, "%d", number); + break; + + case 1: + sprintf(buffer, "%Xh", number); + break; + + case 2: + sprintf(buffer, "$%X", number); + break; + } + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * * + * This routine will fetch an integer value from the entry and section specified. If no * + * entry could be found, then the default value will be returned instead. * + * * + * INPUT: section -- The section name to search under. * + * * + * entry -- The entry name to search for. * + * * + * defvalue -- The default value to use if the specified entry could not be found. * + * * + * OUTPUT: Returns with the integer value specified in the INI database or else returns the * + * default value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +int INIClass::Get_Int(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + + if (*entryptr->Value == '$') { + sscanf(entryptr->Value, "$%x", &defvalue); + } else { + if (tolower(entryptr->Value[strlen(entryptr->Value)-1]) == 'h') { + sscanf(entryptr->Value, "%xh", &defvalue); + } else { + defvalue = atoi(entryptr->Value); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * * + * This routine is similar to the Put_Int routine, but the number is stored as a hexadecimal* + * number. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier to tag to the integer number specified. * + * * + * number -- The number to assign the the specified entry and placed in the * + * specified section. * + * * + * OUTPUT: bool; Was the number placed into the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Hex(char const * section, char const * entry, int number) +{ + char buffer[MAX_LINE_LENGTH]; + + sprintf(buffer, "%X", number); + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. The * + * value is interpreted as a hexadecimal number and then returned. If no entry could be * + * found, then the default value is returned instead. * + * * + * INPUT: section -- The section identifier to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the integer value from the specified section and entry. If no entry * + * could be found, then the default value will be returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_Hex(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + sscanf(entryptr->Value, "%x", &defvalue); + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_String -- Output a string to the section and entry specified. * + * * + * This routine will put an arbitrary string to the section and entry specified. Any * + * previous matching entry will be replaced. * + * * + * INPUT: section -- The section identifier to place the string under. * + * * + * entry -- The entry identifier to identify this string [placed under the section]* + * * + * string -- Pointer to the string to assign to this entry. * + * * + * OUTPUT: bool; Was the entry assigned without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +bool INIClass::Put_String(char const * section, char const * entry, char const * string) +{ + if (section == NULL || entry == NULL) return(false); + + INISection * secptr = Find_Section(section); + + if (secptr == NULL) { + secptr = new INISection(strdup(section)); + if (secptr == NULL) return(false); + SectionList.Add_Tail(secptr); + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + } + + /* + ** Remove the old entry if found. + */ + INIEntry * entryptr = secptr->Find_Entry(entry); + if (entryptr != NULL) { + secptr->EntryIndex.Remove_Index(entryptr->Index_ID()); + delete entryptr; + } + + /* + ** Create and add the new entry. + */ + if (string != NULL && strlen(string) > 0) { + entryptr = new INIEntry(strdup(entry), strdup(string)); + + if (entryptr == NULL) { + return(false); + } + secptr->EntryList.Add_Tail(entryptr); + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * * + * This will retrieve the entire text to the right of the "=" character. The text is * + * found by finding a matching entry in the section specified. If no matching entry could * + * be found, then the default value will be stored in the output string buffer. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- If no entry could be found, then this text will be returned. * + * * + * buffer -- Output buffer to store the retrieved string into. * + * * + * size -- The size of the output buffer. The maximum string length that could * + * be retrieved will be one less than this length. This is due to the * + * forced trailing zero added to the end of the string. * + * * + * OUTPUT: Returns with the length of the string retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const +{ + /* + ** Verify that the parameters are nominally legal. + */ + if (buffer != NULL && size > 0) { + buffer[0] = '\0'; + } + if (buffer == NULL || !size || section == NULL || entry == NULL) return(0); + + /* + ** Fetch the entry string if it is present. If not, then the normal default + ** value will be used as the entry value. + */ + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr) { + if (entryptr->Value) { + defvalue = entryptr->Value; + } + } + + /* + ** Fill in the buffer with the entry value and return with the length of the string. + */ + if (defvalue == NULL) { + buffer[0] = '\0'; + return(0); + } else { + strncpy(buffer, defvalue, size); + buffer[size-1] = '\0'; + strtrim(buffer); + return(strlen(buffer)); + } +} + + +/*********************************************************************************************** + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * * + * Use this routine to place a boolean value into the INI database. The boolean value will * + * be stored as "yes" or "no". * + * * + * INPUT: section -- The section to place the entry and boolean value into. * + * * + * entry -- The entry identifier to tag to the boolean value. * + * * + * value -- The boolean value to place into the database. * + * * + * OUTPUT: bool; Was the boolean value placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Bool(char const * section, char const * entry, bool value) +{ + if (value) { + return(Put_String(section, entry, "yes")); + } else { + return(Put_String(section, entry, "no")); + } +} + + +/*********************************************************************************************** + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. If * + * one is found, the value is interpreted as a boolean value and then returned. In the case * + * of no matching entry, the default value will be returned instead. The boolean value * + * is interpreted using the standard boolean conventions. e.g., "Yes", "Y", "1", "True", * + * "T" are all consider to be a TRUE boolean value. * + * * + * INPUT: section -- The section to search under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if no matching entry could be located. * + * * + * OUTPUT: Returns with the boolean value of the specified section and entry. If no match * + * then the default boolean value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Get_Bool(char const * section, char const * entry, bool defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + switch (toupper(*entryptr->Value)) { + case 'Y': + case 'T': + case '1': + return(true); + + case 'N': + case 'F': + case '0': + return(false); + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * * + * This routine scans the supplied entry for the section specified. This is used for * + * internal database maintenance. * + * * + * INPUT: entry -- The entry to scan for. * + * * + * OUTPUT: Returns with a pointer to the entry control structure if the entry was found. * + * Otherwise it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::INISection::Find_Entry(char const * entry) const +{ + if (entry != NULL) { + int crc = CRCEngine()(entry, strlen(entry)); + if (EntryIndex.Is_Present(crc)) { + return(EntryIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_PKey -- Stores the key to the INI database. * + * * + * The key stored to the database will have both the exponent and modulus portions saved. * + * Since the fast key only requires the modulus, it is only necessary to save the slow * + * key to the database. However, storing the slow key stores the information necessary to * + * generate the fast and slow keys. Because public key encryption requires one key to be * + * completely secure, only store the fast key in situations where the INI database will * + * be made public. * + * * + * INPUT: key -- The key to store the INI database. * + * * + * OUTPUT: bool; Was the key stored to the database? * + * * + * WARNINGS: Store the fast key for public INI database availability. Store the slow key if * + * the INI database is secure. * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_PKey(PKey const & key) +{ + char buffer[512]; + + int len = key.Encode_Modulus(buffer); + Put_UUBlock("PublicKey", buffer, len); + + len = key.Encode_Exponent(buffer); + Put_UUBlock("PrivateKey", buffer, len); + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * * + * This routine will fetch the key from the INI database. The key fetched is controlled by * + * the parameter. There are two choices of key -- the fast or slow key. * + * * + * INPUT: fast -- Should the fast key be retrieved? The fast key has the advantage of * + * requiring only the modulus value. * + * * + * OUTPUT: Returns with the key retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey INIClass::Get_PKey(bool fast) const +{ + PKey key; + char buffer[512]; + + /* + ** When retrieving the fast key, the exponent is a known constant. Don't parse the + ** exponent from the database. + */ + if (fast) { + BigInt exp = PKey::Fast_Exponent(); + exp.DEREncode((unsigned char *)buffer); + key.Decode_Exponent(buffer); + } else { + Get_UUBlock("PrivateKey", buffer, sizeof(buffer)); + key.Decode_Exponent(buffer); + } + + Get_UUBlock("PublicKey", buffer, sizeof(buffer)); + key.Decode_Modulus(buffer); + + return(key); +} + + +/*********************************************************************************************** + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * * + * This routine will examine the section and entry specified and interpret the value * + * as if it were a fixed point number. The format of the fixed point number can be * + * percentage (e.g. 100%) or a floating point number (e.g., 1.7). * + * * + * INPUT: section -- Pointer to the section identifier to look under. * + * * + * entry -- Pointer to the entry identifier to examine. * + * * + * defvalue -- If the section and entry could not be found, then this value will * + * be returned. * + * * + * OUTPUT: Returns with the fixed point number that occurs at the section and entry * + * specified. If it could not be found, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +fixed INIClass::Get_Fixed(char const * section, char const * entry, fixed defvalue) const +{ + char buffer[128]; + fixed retval = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + retval = fixed(buffer); + } + return(retval); +} + + +/*********************************************************************************************** + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * * + * Use this routine to output a fixed point number to the database. The entry will be * + * placed in the section and entry specified. If there was any existing entry, it will * + * be replaced. * + * * + * INPUT: section -- Pointer to the section identifier. * + * * + * entry -- Pointer to the entry identifier to use for this value. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the data stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Fixed(char const * section, char const * entry, fixed value) +{ + return(Put_String(section, entry, value.As_ASCII())); +} + + +/*********************************************************************************************** + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * * + * This routine will scan the string (text line) supplied and if any comment portions are * + * found, they will be trimmed off. Leading and trailing blanks are also removed. * + * * + * INPUT: buffer -- Pointer to the null terminate string to be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void INIClass::Strip_Comments(char * buffer) +{ + if (buffer != NULL) { + char * comment = strchr(buffer, ';'); + if (comment) { + *comment = '\0'; + strtrim(buffer); + } + } +} diff --git a/CODE/INI.H b/CODE/INI.H new file mode 100644 index 0000000..d6b77b6 --- /dev/null +++ b/CODE/INI.H @@ -0,0 +1,162 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/15/96 * + * * + * Last Update : May 15, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INI_H +#define INI_H + +#include +#include "listnode.h" +#include "pipe.h" +#include "wwfile.h" +#include "pk.h" +#include "fixed.h" +#include "crc.h" +#include "search.h" + +/* +** This is an INI database handler class. It handles a database with a disk format identical +** to the INI files commonly used by Windows. +*/ +class INIClass { + public: + INIClass(void) {} + ~INIClass(void); + + /* + ** Fetch and store INI data. + */ + bool Load(FileClass & file); + bool Load(Straw & file); + int Save(FileClass & file) const; + int Save(Pipe & file) const; + + /* + ** Erase all data within this INI file manager. + */ + bool Clear(char const * section = 0, char const * entry = 0); + + int Line_Count(char const * section) const; + bool Is_Loaded(void) const {return(!SectionList.Is_Empty());} + int Size(void) const; + bool Is_Present(char const * section, char const * entry = 0) const {if (entry == 0) return(Find_Section(section) != 0);return(Find_Entry(section, entry) != 0);} + + /* + ** Fetch the number of sections in the INI file or verify if a specific + ** section is present. + */ + int Section_Count(void) const; + bool Section_Present(char const * section) const {return(Find_Section(section) != NULL);} + + /* + ** Fetch the number of entries in a section or get a particular entry in a section. + */ + int Entry_Count(char const * section) const; + char const * Get_Entry(char const * section, int index) const; + + /* + ** Get the various data types from the section and entry specified. + */ + int Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const; + int Get_Int(char const * section, char const * entry, int defvalue=0) const; + int Get_Hex(char const * section, char const * entry, int defvalue=0) const; + bool Get_Bool(char const * section, char const * entry, bool defvalue=false) const; + int Get_TextBlock(char const * section, char * buffer, int len) const; + int Get_UUBlock(char const * section, void * buffer, int len) const; + PKey Get_PKey(bool fast) const; + fixed Get_Fixed(char const * section, char const * entry, fixed defvalue) const; + + /* + ** Put a data type to the section and entry specified. + */ + bool Put_Fixed(char const * section, char const * entry, fixed value); + bool Put_String(char const * section, char const * entry, char const * string); + bool Put_Hex(char const * section, char const * entry, int number); + bool Put_Int(char const * section, char const * entry, int number, int format=0); + bool Put_Bool(char const * section, char const * entry, bool value); + bool Put_TextBlock(char const * section, char const * text); + bool Put_UUBlock(char const * section, void const * block, int len); + bool Put_PKey(PKey const & key); + + protected: + enum {MAX_LINE_LENGTH=128}; + + /* + ** The value entries for the INI file are stored as objects of this type. + ** The entry identifier and value string are combined into this object. + */ + struct INIEntry : Node { + INIEntry(char * entry = 0, char * value = 0) : Entry(entry), Value(value) {} + ~INIEntry(void) {free(Entry);Entry = 0;free(Value);Value = 0;} + int Index_ID(void) const {return(CRCEngine()(Entry, strlen(Entry)));}; + + char * Entry; + char * Value; + }; + + /* + ** Each section (bracketed) is represented by an object of this type. All entries + ** subordinate to this section are attached. + */ + struct INISection : Node { + INISection(char * section) : Section(section) {} + ~INISection(void) {free(Section);Section = 0;EntryList.Delete();} + INIEntry * Find_Entry(char const * entry) const; + int Index_ID(void) const {return(CRCEngine()(Section, strlen(Section)));}; + + char * Section; + List EntryList; + IndexClassEntryIndex; + }; + + /* + ** Utility routines to help find the appropriate section and entry objects. + */ + INISection * Find_Section(char const * section) const; + INIEntry * Find_Entry(char const * section, char const * entry) const; + static void Strip_Comments(char * buffer); + + /* + ** This is the list of all sections within this INI file. + */ + List SectionList; + + IndexClass SectionIndex; +}; + + +#endif diff --git a/CODE/INIBIN.CPP b/CODE/INIBIN.CPP new file mode 100644 index 0000000..c42fd6f --- /dev/null +++ b/CODE/INIBIN.CPP @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\inibin.cpv 4.40 04 Jul 1996 16:14:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INIBIN.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : October 20, 1995 * + * * + * Last Update : November 7, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/CODE/INICODE.CPP b/CODE/INICODE.CPP new file mode 100644 index 0000000..a411bd4 --- /dev/null +++ b/CODE/INICODE.CPP @@ -0,0 +1,366 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\inicode.cpv 4.38 03 Jul 1996 05:14:04 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : INICODE.CPP * + * * + * Programmer : David R Dettmer * + * * + * Start Date : November 7, 1995 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef TOFIX + +void Get_Scenario_Digest(char * digest, char * buffer) +{ + char buf[128]; // Working string staging buffer. + char stage[sizeof(BigInt)*2]; + + char * stage_ptr = &stage[0]; + int len = strlen(buffer) + 2; + char * tbuffer = buffer + len; + + WWGetPrivateProfileString("DIGEST", NULL, NULL, tbuffer, sizeof(_staging_buffer)-len, buffer); + stage[0] = '\0'; + while (*tbuffer != '\0') { + + WWGetPrivateProfileString("DIGEST", tbuffer, NULL, buf, sizeof(buf)-1, buffer); + strcat(stage, buf); + tbuffer += strlen(tbuffer)+1; + } + + len = strlen(stage); + char * dbuffer = &stage[0]; + tbuffer = &stage[0]; + for (int index = 0; index < len/2; index++) { + int c; + if (isdigit(*tbuffer)) { + c = (*tbuffer) - '0'; + } else { + c = 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + c <<= 4; + if (isdigit(*tbuffer)) { + c |= (*tbuffer) - '0'; + } else { + c |= 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + *dbuffer++ = c; + } + + /* + ** Decode and decrypt the number. + */ + BigInt hash = 0; + hash.DERDecode((unsigned char*)stage); + + BigInt d; + d = d.Decode_ASCII(KEY_D); + BigInt n; + n = n.Decode_ASCII(KEY_N); + + hash = hash.exp_b_mod_c(d, n); + + memcpy(digest, &hash, 20); + + buffer = strstr(buffer, "[DIGEST]"); + if (buffer) { + *buffer = '\0'; + } +} + + +bool Read_Scenario_INI_Write_INB( char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char *binbuf; // Scenario.inb staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + char buf[256]; // Working string staging buffer. + char scenarioname[40]; + int len; + unsigned char val; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_staging_buffer; + memset(buffer, '\0', sizeof(_staging_buffer)); + + /* + ** Create scenario filename and read the file. + ** The previous routine verifies that the file is available. + */ + + sprintf(fname,"%s.INI",root); + CCFileClass file(fname); + + file.Read(buffer, sizeof(_staging_buffer)-1); + + /* + ** Fetch and slice off any message digest attached. + */ + char digest[20]; + Get_Scenario_Digest(digest, buffer); + + char real_digest[20]; + SHAEngine digest_engine; + digest_engine.Hash(buffer, strlen(buffer)); + digest_engine.Result(real_digest); + + if (memcmp(digest, real_digest, sizeof(real_digest)) != 0) { + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } + + sprintf(fname,"%s.INB",root); + file.Set_Name(fname); + file.Cache(16384); + file.Open(WRITE); + + unsigned long crc = Ini_Binary_Version(); + + file.Write( (char *)&crc, sizeof(crc) ); + + binbuf = (char *)Alloc( sizeof(_staging_buffer), MEM_NORMAL ); + + if (binbuf) { + Write_Bin_Init( binbuf, sizeof(_staging_buffer) ); + } else { + Print_Error_End_Exit( "Unable to alloc space for writing INB" ); + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Name", "", scenarioname, sizeof(scenarioname), buffer); + WWGetPrivateProfileString("Basic", "Intro", "x", Scen.IntroMovie, sizeof(Scen.IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", Scen.BriefMovie, sizeof(Scen.BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", Scen.WinMovie, sizeof(Scen.WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", Scen.LoseMovie, sizeof(Scen.LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", Scen.ActionMovie, sizeof(Scen.ActionMovie), buffer); + Scen.IsToCarryOver = WWGetPrivateProfileInt("Basic", "ToCarryOver", 0, buffer); + Scen.IsToInherit = WWGetPrivateProfileInt("Basic", "ToInherit", 0, buffer); + + Write_Bin_String( scenarioname, strlen(scenarioname), binbuf ); + Write_Bin_String( Scen.IntroMovie, strlen(Scen.IntroMovie), binbuf ); + Write_Bin_String( Scen.BriefMovie, strlen(Scen.BriefMovie), binbuf ); + Write_Bin_String( Scen.WinMovie, strlen(Scen.WinMovie), binbuf ); + Write_Bin_String( Scen.LoseMovie, strlen(Scen.LoseMovie), binbuf ); + Write_Bin_String( Scen.ActionMovie, strlen(Scen.ActionMovie), binbuf ); + + /* + ** Fetch the transition theme for this scenario. + */ + Scen.TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + Scen.TransitTheme = Theme.From_Name(buf); + + WWGetPrivateProfileString( "Basic", "Player", "Greece", buf, 127, buffer); + Scen.PlayerHouse = HouseTypeClass::From_Name(buf); + if (Scen.PlayerHouse >= HOUSE_MULTI1) { + Scen.PlayerHouse = HOUSE_GREECE; + } +// TCTC To Fix? +// Scen.CarryOverPercent = WWGetPrivateProfileInt( "Basic", "CarryOverMoney", 100, buffer); + Scen.CarryOverCap = WWGetPrivateProfileInt( "Basic", "CarryOverCap", -1, buffer); + Scen.Percent = WWGetPrivateProfileInt( "Basic", "Percent", 0, buffer); + + Write_Bin_Num( &Scen.TransitTheme, sizeof(Scen.TransitTheme), binbuf ); + Write_Bin_Num( &Scen.PlayerHouse, sizeof(Scen.PlayerHouse), binbuf ); + Write_Bin_Num( &Scen.CarryOverPercent, 1, binbuf ); + Write_Bin_Num( &Scen.CarryOverCap, 2, binbuf ); + Write_Bin_Num( &Scen.Percent, 1, binbuf ); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + Scen.CarryOverPercent = Cardinal_To_Fixed(100, Scen.CarryOverPercent); + + PlayerPtr = HouseClass::As_Pointer( Scen.PlayerHouse ); + assert(PlayerPtr != NULL); + PlayerPtr->IsHuman = true; + + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = MIN(Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent), (Scen.CarryOverCap * 100) ); + } else { + carryover = Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { + + Assign_Houses(); + } + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary(root, &ScenarioCRC)) { + return( false ); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + VesselClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &Scen.BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, 255, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + len = Write_Bin_Length( binbuf ); + if (len != -1) { + file.Write( binbuf, len ); + } + + Free( binbuf ); + file.Close(); + + return(true); +} + +#endif + + + + + + + + + diff --git a/CODE/INIT.CPP b/CODE/INIT.CPP new file mode 100644 index 0000000..cd13c8f --- /dev/null +++ b/CODE/INIT.CPP @@ -0,0 +1,3617 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INIT.CPP 8 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Bootstrap -- Perform the initial bootstrap procedure. * + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * Init_Color_Remaps -- Initialize the text remap tables. * + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * Init_Fonts -- Initialize all the game font pointers. * + * Init_Game -- Main game initialization routine. * + * Init_Heaps -- Initialize the game heaps and buffers. * + * Init_Keys -- Initialize the cryptographic keys. * + * Init_Mouse -- Initialize the mouse system. * + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * Init_Random -- Initializes the random-number generator * + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * Load_Recording_Values -- Loads recording values from recording file * + * Load_Title_Page -- Load the background art for the title page. * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#include "WSPIPX.h" +#include "internet.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX + +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include "ccdde.h" + +#include + +#ifdef DONGLE +#include "cbn_.h" +#endif + +#ifdef MPEGMOVIE // Denzil 6/25/98 +#include "mpgset.h" +#endif + +RemapControlType SidebarScheme; + +#include "WolDebug.h" + +#ifdef CHEAT_KEYS +extern bool bNoMovies; +#endif + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool sequenced=false); +static void Init_Color_Remaps(void); +static void Init_Heaps(void); +static void Init_Expansion_Files(void); +static void Init_One_Time_Systems(void); +static void Init_Fonts(void); +static void Init_CDROM_Access(void); +static void Init_Bootstrap_Mixfiles(void); +static void Init_Secondary_Mixfiles(void); +static void Init_Mouse(void); +static void Bootstrap(void); +//static void Init_Authorization(void); +static void Init_Bulk_Data(void); +static void Init_Keys(void); + +extern int UnitBuildPenalty; + +extern "C" { +extern long RandNumb; +} +#ifndef WIN32 +static int UsePageFaultHandler = 1; // 1 = install PFH +#endif //WIN32 + +//extern int SimRandIndex; +void Init_Random(void); + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode + +bool Load_Recording_Values(CCFileClass & file); +bool Save_Recording_Values(CCFileClass & file); + +#ifdef WOLAPI_INTEGRATION +extern int WOL_Main(); +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ); +#endif + +extern bool Is_Mission_Counterstrike (char *file_name); + +/*********************************************************************************************** + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * * + * This loads and displays the prolog page that is displayed before the prolog movie * + * is played. This page is necessary because there is much loading that occurs before * + * the prolog movie is played and looking at a picture is better than looking at a blank * + * screen. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Load_Prolog_Page(void) +{ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen("PROLOG.PCX", &HidPage, CCPalette); + HidPage.Blit(SeenPage); +#else + Load_Picture("PROLOG.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + HidPage.Blit(SeenPage); +#endif + CCPalette.Set(); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE! * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +#include "sha.h" +//#include +bool Init_Game(int , char * []) +{ + /* + ** Allocate the benchmark tracking objects only if the machine and + ** compile flags indicate. + */ + #ifdef CHEAT_KEYS + if (Processor() >= 2) { + Benches = new Benchmark [BENCH_COUNT]; + } + #endif + + /* + ** Initialize the encryption keys. + */ + Init_Keys(); + + /* + ** Bootstrap as much as possible before error-prone initializations are + ** performed. This bootstrap process will enable the error message + ** handler to function. + */ + Bootstrap(); + + /* + ** Check for an initialize a working mouse pointer. Display error and bail if + ** no mouse driver is installed. + */ + Init_Mouse(); + + /* + ** Initialize access to the CD-ROM and ensure that the CD is inserted. This can, and + ** most likely will, result in a visible prompt. + */ + Init_CDROM_Access(); + + if (Special.IsFromInstall) { + Load_Prolog_Page(); + } + + /* + ** Register and cache any secondary mixfiles. + */ + Init_Secondary_Mixfiles(); + + /* + ** This is a special hack to initialize the heaps that must be in place before the + ** rules file is processed. These heaps should properly be allocated as a consequence + ** of processing the rules.ini file, but that is a bit beyond the capabilities of + ** the rule parser routine (currently). + */ + HouseTypes.Set_Heap(HOUSE_COUNT); + BuildingTypes.Set_Heap(STRUCT_COUNT); + AircraftTypes.Set_Heap(AIRCRAFT_COUNT); + InfantryTypes.Set_Heap(INFANTRY_COUNT); + BulletTypes.Set_Heap(BULLET_COUNT); + AnimTypes.Set_Heap(ANIM_COUNT); + UnitTypes.Set_Heap(UNIT_COUNT); + VesselTypes.Set_Heap(VESSEL_COUNT); + TemplateTypes.Set_Heap(TEMPLATE_COUNT); + TerrainTypes.Set_Heap(TERRAIN_COUNT); + OverlayTypes.Set_Heap(OVERLAY_COUNT); + SmudgeTypes.Set_Heap(SMUDGE_COUNT); + + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + /* + ** Find and process any rules for this game. + */ + if (RuleINI.Load(CCFileClass("RULES.INI"), false)) { + Rule.Process(RuleINI); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Aftermath runtime change 9/29/98 + // This is safe to do, as only rules for aftermath units are included in this ini. + if (Is_Aftermath_Installed() == true) { + if (AftermathINI.Load(CCFileClass("AFTRMATH.INI"), false)) { + Rule.Process(AftermathINI); + } + } +#endif + + Session.MaxPlayers = Rule.MaxPlayers; + + /* + ** Initialize the game object heaps as well as other rules-dependant buffer allocations. + */ + Init_Heaps(); + + /* + ** Initialize the animation system. + */ + Anim_Init(); + +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } +#endif +#endif + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if( Using_DVD() ) + { + #ifdef MCIMPEG + MciMovie = new MCIMovie(MainWindow); + #endif + MpgSettings = new MPGSettings(NULL); //RawFileClass(CONFIG_FILE_NAME)); + } + #endif + + /* + ** Play the startup animation. + */ + if (!Special.IsFromInstall && !Special.IsFromWChat) { + VisiblePage.Clear(); +// Mono_Printf("Playing Intro\n"); + Play_Intro(); + memset(CurrentPalette, 0x01, 768); + WhitePalette.Set(); + } else { + memset(CurrentPalette, 0x01, 768); + } + + /* + ** Initialize the text remap tables. + */ + Init_Color_Remaps(); + + /* + ** Get authorization to access the game. + */ +// Init_Authorization(); +// Show_Mouse(); + + /* + ** Set the logic page to the seenpage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** If not automatically launching into the intro, then display the title + ** page while the bulk data is cached. + */ + if (!Special.IsFromInstall) { + Load_Title_Page(true); + + Hide_Mouse(); + Fancy_Text_Print(TXT_STAND_BY, 160*RESFACTOR, 120*RESFACTOR, &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + Show_Mouse(); + + CCPalette.Set(FADE_PALETTE_SLOW); + Call_Back(); + } + + /* + ** Initialize the bulk data. This takes the longest time and must be performed once + ** before the regular game starts. + */ + Init_Bulk_Data(); + + /* + ** Initialize the multiplayer score values + */ + Session.GamesPlayed = 0; + Session.NumScores = 0; + Session.CurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + Session.Score[i].Name[0] = '\0'; + Session.Score[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + Session.Score[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + GamePalette = CCPalette; +// InGamePalette = CCPalette; + OriginalPalette = CCPalette; + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + return(true); +} + +#ifdef WINSOCK_IPX // Steve Tall missed this one - ajw +extern bool Get_Broadcast_Addresses (void); +#endif + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +bool Select_Game(bool fade) +{ + // Enums in Select_Game() must match order of buttons in Main_Menu(). +#ifdef FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO_CS, // Expansion scenario to play. + SEL_NEW_SCENARIO_AM, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#else // FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + SEL_INTERNET, +#endif //WIN32 +//#if defined(MPEGMOVIE) // Denzil 6/25/98 +// SEL_MOVIESETTINGS, +//#endif + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#endif // FIXIT_VERSION_3 + + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + +#ifdef DONGLE + /* These where added by ColinM for the dongle checking */ + short iRet = 0; + unsigned short iPortNr = 1; /* automatic port scan enabled */ + unsigned char cSCodeSER[] = "\x41\x42"; + unsigned long ulIdRet = 0; + unsigned char cBoxName[]= "\x00\x00"; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int cdcheck = 0; + bool cs = Is_Counterstrike_Installed(); +#endif + +// #ifndef DVD // Denzil - We want the menu screen ajw No we don't +// if (Special.IsFromInstall) { +// display = false; +// } +// #endif + + Show_Mouse(); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; // Assume new units disabled, unless specifically .INI enabled or multiplayer negotiations enable it. +#endif + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + #ifdef MIRROR_QUEUE + MirrorList.Init(); + #endif + OutList.Init(); + Frame = 0; + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + PlayerWins = false; + PlayerLoses = false; + Session.ObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; + Session.DesiredFrameRate = 30; +#if(TIMING_FIX) + NewMaxAheadFrame1 = 0; + NewMaxAheadFrame2 = 0; +#endif + +/* ColinM added to check for dongle */ +#ifdef DONGLE + iRet = CbN_BoxReady( iPortNr , cBoxName); + if (cBoxName[0] != 0xc5 && cBoxName[0] != 0xc9) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } + + iRet = CbN_ReadSER( iPortNr, cSCodeSER, &ulIdRet); + if (ulIdRet != 0xa0095) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + Session.Score[i].Kills[Session.CurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (Session.Type == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + Theme.Queue_Song(THEME_CRUS); + + /* + ** If we're playing back a recording, load all pertinent values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (Session.Play && Session.RecordFile.Is_Available()) { + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else + Session.Play = false; + } + +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat) { + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } +#endif //WIN32 +#endif + + while (process) { + + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Page(); + GamePalette = CCPalette; + + HidPage.Blit(SeenPage); +// if (fade) { +// WhitePalette.Set(); +// CCPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// fade = false; +// } else { + CCPalette.Set(); +// } + + Set_Logic_Page(SeenBuff); + display = false; + Show_Mouse(); + } + + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall) selection = SEL_START_NEW_GAME; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat and our start game + ** packet has already arrived + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + } else { + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480) { + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } +#endif //WIN32 +#endif + +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + selection = SEL_MULTIPLAYER_GAME; // We are returning from a game. +#endif + + if (selection == SEL_NONE) { +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + + /* + ** Pick an expansion scenario. + */ +#ifdef FIXIT_VERSION_3 + case SEL_NEW_SCENARIO_CS: + case SEL_NEW_SCENARIO_AM: +#else // FIXIT_VERSION_3 + case SEL_NEW_SCENARIO: +#endif // FIXIT_VERSION_3 + Scen.CarryOverMoney = 0; + IsTanyaDead = false; + SaveTanya = false; + +#ifdef FIXIT_VERSION_3 + if( selection == SEL_NEW_SCENARIO_CS ) + { + if(!Force_CD_Available(2)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( true )){ + selection = SEL_NONE; + break; + } + } + else + { + if(!Force_CD_Available(3)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( false )){ + selection = SEL_NONE; + break; + } + } +#else // FIXIT_VERSION_3 + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cs) { + cdcheck = 2; + } + if (Is_Aftermath_Installed()) { + if (!cdcheck) { + cdcheck = 3; + } else { + cdcheck = 4; // special case: means check for 3 or 4 + } + } + if(!Force_CD_Available(cdcheck)) { + return(false); + } + #else + if(!Force_CD_Available(2)) { + return(false); + } + #endif + if(!Expansion_Dialog()){ + selection = SEL_NONE; + break; + } +#endif // FIXIT_VERSION_3 + +#ifdef FIXIT_DIFFICULTY + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch (Fetch_Difficulty(cdcheck >= 3)) { + #else + switch (Fetch_Difficulty()) { + #endif + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } +#endif + Theme.Fade_Out(); + Theme.Queue_Song(THEME_FIRST); + Session.Type = GAME_NORMAL; + process = false; + break; + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + if (Special.IsFromInstall) { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } else { + switch (Fetch_Difficulty()) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } + Scen.CarryOverMoney = 0; + BuildLevel = 10; + IsTanyaDead = false; + SaveTanya = false; + Whom = HOUSE_GOOD; + + if (!Special.IsFromInstall) { +#ifdef FIXIT_ANTS + if (AntsEnabled) { + Scen.Set_Scenario_Name("SCA01EA.INI"); + } else { +#endif +#ifdef FIXIT_VERSION_3 + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_CANCEL, TXT_SOVIET)) { + case 2: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + default: + selection = SEL_NONE; + continue; +#else + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_SOVIET)) { + case 1: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + default: +#endif + case 0: + Scen.Set_Scenario_Name("SCG01EA.INI"); + break; + + } +#ifdef FIXIT_ANTS + } +#endif + Theme.Fade_Out(); + Load_Title_Page(); + } else { + Theme.Fade_Out(); +#ifdef DVD // Denzil ajw Presumably a bug fix. + Choose_Side(); + Hide_Mouse(); +#else + Hide_Mouse(); + Choose_Side(); +#endif + if (CurrentCD == 0) { + Scen.Set_Scenario_Name("SCG01EA.INI"); + } else { + Scen.Set_Scenario_Name("SCU01EA.INI"); + } + } + + Session.Type = GAME_NORMAL; + process = false; + break; + +#ifndef FIXIT_VERSION_3 // Removed button from main menu. + #if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Internet game is requested + */ + case SEL_INTERNET: + /* + ** Only call up the internet menu code if we dont already have connect info from WChat + */ + if (!DDEServer.Get_MPlayer_Game_Info()) { + if (Do_The_Internet_Menu_Thang() && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + display = false; + Session.Type = GAME_INTERNET; + } else { + selection = SEL_NONE; + display = true; + } + } else { + Check_From_WChat(NULL); + display = false; + Session.Type = GAME_INTERNET; + selection = SEL_MULTIPLAYER_GAME; + } + break; + #endif //WIN32 +#endif + +// #if defined(MPEGMOVIE) // Denzil 6/25/98 +// case SEL_MOVIESETTINGS: +// MpgSettings->Dialog(); +// display = true; +// selection = SEL_NONE; +// break; +// #endif + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + Theme.Queue_Song(THEME_FIRST); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'Session.Type' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: +#ifdef WOLAPI_INTEGRATION + if( !pWolapi ) + { +#endif + switch (Session.Type) { + + /* + ** If 'Session.Type' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_SKIRMISH: + if ( !Com_Scenario_Dialog(true) ) { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + else + { + // Ever hits? Session.Type set to GAME_SKIRMISH without user selecting in Select_MPlayer_Game()? +#ifdef FIXIT_VERSION_3 + // If mission is Counterstrike, CS CD will be required. But aftermath units require AM CD. + bAftermathMultiplayer = Is_Aftermath_Installed() && !Is_Mission_Counterstrike( Scen.ScenarioName ); + // ajw I'll bet this was needed before also... + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); +#endif + } + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: + if ( Session.Type != GAME_SKIRMISH && NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((Session.Type == GAME_NULL_MODEM && + Session.ModemType == MODEM_NULL_HOST) || + (Session.Type == GAME_MODEM && + Session.ModemType == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle being spawned from WChat. Internet play based on IPX code. + */ + case GAME_INTERNET: // ajw No longer hit. + if (Special.IsFromWChat) { + /* + ** Give myself focus. + */ + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + +#ifdef WINSOCK_IPX + + if (PacketTransport ) delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert ( PacketTransport != NULL); + + if (PacketTransport->Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + } else { + delete PacketTransport; + PacketTransport = NULL; + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + +#else //WINSOCK_IPX + + WWDebugString ("RA95 - About to initialise Winsock.\n"); + if (Winsock.Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + WWDebugString ("RA95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + if (Server) { + Winsock.Start_Server(); + } else { + Winsock.Start_Client(); + } + + + /* + ** Flush out any pending packets from a previous game. + */ + WWDebugString ("RA95 - About to flush packet queue.\n"); + WWDebugString ("RA95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + WWDebugString ("RA95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + WWDebugString ("RA95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)) { + + WWDebugString ("RA95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()) {}; + WWDebugString ("RA95 - Ready to check for more packets.\n"); + + } + WWDebugString ("RA95 - About to delete scrap memory.\n"); + delete temp_buffer; + + + + } else { + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()) { + WWDebugString ("RA95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + } else { + Read_Game_Options( "C&CSPAWN.INI" ); + } +#ifdef WINSOCK_IPX + WWDebugString ("RA95 - About to set addresses.\n"); + PacketTransport->Set_Broadcast_Address (PlanetWestwoodIPAddress); +#endif //WINSOCK_IPX + if (PlanetWestwoodIsHost) { + + WWDebugString ("RA95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()) { + WWDebugString ("RA95 - Server_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } else { + WWDebugString ("RA95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()) { + WWDebugString ("RA95 - Client_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } + + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + } +#ifdef WOLAPI_INTEGRATION + } // if( !pWolapi ) + + if( pWolapi ) + Session.Type = GAME_INTERNET; +#endif +//debugprint( "Session.Type = %i\n", Session.Type ); + switch (Session.Type) { + /* + ** Modem, Null-Modem or internet + */ + case GAME_MODEM: + case GAME_NULL_MODEM: +#ifndef WOLAPI_INTEGRATION + case GAME_INTERNET: +#endif + case GAME_SKIRMISH: + Theme.Fade_Out(); + process = false; +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + break; + +#ifdef WOLAPI_INTEGRATION // implies also WINSOCK_IPX + case GAME_INTERNET: + if( PacketTransport ) + delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert( PacketTransport != NULL ); + if( PacketTransport->Init() ) + { + switch( WOL_Main() ) + { + case 1: + // Start game. +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + break; + case 0: + // User cancelled. + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + break; + case -1: + // Patch was downloaded. Exit app. + Theme.Fade_Out(); + BlackPalette.Set( FADE_PALETTE_SLOW ); + return false; + } + } + else + { + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + } + break; +#endif + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + WWDebugString ("RA95 - Game type is IPX.\n"); + /* + ** Init network system & remote-connect + */ +#ifdef WINSOCK_IPX + if (PacketTransport ) delete PacketTransport; +// if (WWMessageBox().Process("Select a protocol to use for network play.", "UDP", "IPX")) { + PacketTransport = new IPXInterfaceClass; + assert ( PacketTransport != NULL); +// }else{ +// PacketTransport = new UDPInterfaceClass; //IPXInterfaceClass; +// assert ( PacketTransport != NULL); +// if (!Get_Broadcast_Addresses()) { +// Session.Type = GAME_NORMAL; +// display = true; +// selection = SEL_NONE; +// delete PacketTransport; +// PacketTransport = NULL; +// break; +// } +// } + +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + if (Session.Type == GAME_IPX && Init_Network() && Remote_Connect()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_NONE; +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#endif //WINSOCK_IPX + } + break; + +#if(TEN) + /* + ** TEN: jump straight into the game + */ + case GAME_TEN: + if (Init_TEN()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { + WWMessageBox().Process("Unable to initialize TEN!"); + //Prog_End(); + Emergency_Exit(1); + } + break; +#endif // TEN + +#if(MPATH) + /* + ** MPATH: jump straight into the game + */ + case GAME_MPATH: + if (Init_MPATH()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { + WWMessageBox().Process("Unable to initialize MPATH!"); + //Prog_End(); + Emergency_Exit(1); + } + break; +#endif // MPATH + + } + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + if (Debug_Flag) { + Play_Intro(Debug_Flag); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, true); // no transition picture to briefing + Keyboard->Clear(); + Play_Movie(VQ_SIZZLE, THEME_NONE, true); + Play_Movie(VQ_SIZZLE2, THEME_NONE, true); +// Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); // has transitino picture to briefing + } + Theme.Queue_Song(THEME_CRUS); + display = true; + fade = true; + selection = SEL_NONE; + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: + Theme.Fade_Out(); + BlackPalette.Set(FADE_PALETTE_SLOW); + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (Session.Attract && Session.RecordFile.Is_Available()) { + Session.Play = true; + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else { + Session.Play = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode to load scenario + */ + Scen.Set_Scenario_Name("SCG01EA.INI"); + } + + /* + ** Don't carry stray keystrokes into game. + */ + Keyboard->Clear(); + + /* + ** Initialize the random number generator(s) + */ + Init_Random(); + + /* + ** Save initialization values if we're recording this game. + */ + if (Session.Record) { + if (Session.RecordFile.Open(WRITE)) { + Save_Recording_Values(Session.RecordFile); + } else { + Session.Record = false; + } + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch(Session.Type) { + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: +#ifdef FIXIT_VERSION_3 + if( !bAftermathMultiplayer ) { +#else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS) { +#endif + NewUnitsEnabled = SecretUnitsEnabled = false; + } else { + NewUnitsEnabled = true; + } +// debugprint( "Non Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); + break; + case GAME_INTERNET: + if( !pWolapi ) + { +// debugprint( "pWolapi is null on internet game!" ); + Fatal( "pWolapi is null on internet game!" ); + } + //if( pWolapi->bEnableNewAftermathUnits ) + if( bAftermathMultiplayer ) + NewUnitsEnabled = true; + else + NewUnitsEnabled = SecretUnitsEnabled = false; +// debugprint( "Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); + break; + default: + break; + } +#endif + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded && !Session.LoadGame) { +// if (Debug_Map) { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, SCEN_VAR_A); +// } else { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir); +// } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + } + Show_Mouse(); +//Mono_Printf("About to call Start Scenario with %s\n", Scen.ScenarioName); + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + if (Special.IsFromInstall) Show_Mouse(); + Special.IsFromInstall = false; + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + Session.Messages.Init( + Map.TacPixelX, Map.TacPixelY, // x,y for messages + 6, // max # msgs + MAX_MESSAGE_LENGTH-14, // max msg length + 7 * RESFACTOR, // font height in pixels + -1, -1, // x,y for edit line (appears above msgs) + 0,//BG 1, // enable edit overflow + 20, // min, + MAX_MESSAGE_LENGTH - 14, // max for trimming overflow +#ifdef WIN32 + Lepton_To_Pixel(Map.TacLeptonWidth)); // Width in pixels of buffer +#else + (320-SIDEBAR_WID)); // Width in pixels of buffer +#endif + + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + !Session.Play) { + if (Session.Type == GAME_TEN) { +#if(TEN) + Session.Create_TEN_Connections(); +#endif // TEN + } else if (Session.Type == GAME_MPATH) { +#if(MPATH) + Session.Create_MPATH_Connections(); +#endif + } else { + Session.Create_Connections(); + } + } + + + /* + ** If this isnt an internet game that set the unit build rate to its default value + */ + if (Session.Type != GAME_INTERNET){ + UnitBuildPenalty = 100; + } + + /* + ** Hide the SeenPage; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + Call_Back(); + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + Show_Mouse(); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } +#endif //WIN32 + Map.Flag_To_Redraw(); + Call_Back(); + Map.Render(); + +#ifdef WOLAPI_INTEGRATION + + //ajw debugging only +// debugprint( "Debugging Session...\n" ); +// debugprint( "Session.Players count is %i.\n", Session.Players.Count() ); + for (i = 0; i < Session.Players.Count(); i++) + { + NetNumType net; + NetNodeType node; + Session.Players[i]->Address.Get_Address( net, node ); +// debugprint( "Player %i, %s, color %i, ip %i.%i.%i.%i.%i.%i\n", i, Session.Players[i]->Name, +// Session.Players[i]->Player.Color, node[0], node[1], node[2], node[3], node[4], node[5] ); + } +// debugprint( "PlanetWestwoodPortNumber is %i\n", PlanetWestwoodPortNumber ); + +#endif + + return(true); +} + + +/*********************************************************************************************** + * Play_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + * 05/08/1996 JLB : Modified for Red Alert and direction control. * + *=============================================================================================*/ +static void Play_Intro(bool sequenced) +{ + static VQType _counter = VQ_FIRST; + + Keyboard->Clear(); + if (sequenced) { + if (_counter <= VQ_FIRST) _counter = VQ_COUNT; + if (_counter == VQ_COUNT) _counter--; + if (_counter == VQ_REDINTRO) _counter--; + if (_counter == VQ_TITLE) _counter--; + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQType(_counter--), THEME_NONE); + +// Show_Mouse(); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef WIN32 + Play_Movie(VQ_REDINTRO, THEME_NONE, false); +#else + Play_Movie(VQ_TITLE, THEME_NONE, false); +#endif + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +#ifdef MOVIE640 +GraphicBufferClass VQ640(640, 400, (void *)NULL); +#endif +#endif +void Anim_Init(void) +{ + +#ifdef WIN32 + + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; +//AnimControl.DrawFlags |= VQACFGF_NODRAW; +//BG - M. Grayford says turn this off AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); +#ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 640; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } +#endif + AnimControl.Vmode = 0; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } + AnimControl.SoundObject = SoundObject; + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#else //WIN32 + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = MCGA_MODE; + AnimControl.VBIBit = VertBlank; + AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_HMIINIT|VQAOPTF_CAPTIONS|VQAOPTF_EVA; +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + AnimControl.DigiCard = NewConfig.DigitCard; + AnimControl.HMIBufSize = 8192; + AnimControl.DigiHandle = Get_Digi_Handle(); + AnimControl.Volume = 0x00FF; + AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } + + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#endif //WIN32 +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char * argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + string = strupr(argv[index]); + + /* + ** Print usage text only if requested. + */ + if (stricmp("/?", string) == 0 || stricmp("-?", string) == 0 || stricmp("-h", string) == 0 || stricmp("/h", string) == 0) { + /* + ** Unrecognized command line parameter... Display usage + ** and then exit. + */ + puts(TEXT_OPTIONS); + return(false); + } + + + bool processed = true; + long ob = Obfuscate(string); + + /* + ** Check to see if the parameter is a cheat enabling one. + */ + long const * optr = &CheatCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Playtest = true; + Debug_Flag = true; + break; + } + } + + /* + ** Check to see if the parameter is a cheat enabling one. + */ + optr = &PlayCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Playtest = true; + Debug_Flag = true; + break; + } + } + + /* + ** Check to see if the parameter is a scenario editor + ** enabling one. + */ + optr = &EditorCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + Debug_Playtest = true; + break; + } + } + + switch (ob) { + +#ifdef VIRGIN_CHEAT_KEYS + case PARM_PLAYTEST: + Debug_Playtest = true; + break; +#endif + + /* + ** Special flag - is C&C being run from the install program? + */ + case PARM_INSTALL: + Special.IsFromInstall = true; +// If uncommented, will disable the key during the first movie run. +// BreakoutAllowed = false; + break; + +#if(TEN) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + +#if(MPATH) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } + +#if (0) + /* + ** Build speed modifier + */ + if (strstr (string, "-UNITRATE:")){ + int unit_rate; + sscanf (string, "-UNITRATE:%d", &unit_rate); + UnitBuildPenalty = unit_rate; + } +#endif //(0) + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8, "."); + while (p) { + int x; + + sscanf(p, "%x", &x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL, "."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + Session.NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + Session.NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + Session.Attract = true; + continue; + } + + +#ifdef WIN32 + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string, "-480")) { + ScreenHeight = 480; + continue; + } + + /* + ** Check for spawn from WChat + */ +#ifndef FIXIT_VERSION_3 // WChat eliminated. + if (strstr(string,"-WCHAT")){ + SpawnedFromWChat = true; + } +#endif + +#endif + +#ifdef CHEAT_KEYS + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } + +#ifndef WIN32 + /* + ** Don't install Page Fault Handler (MUST use this for debugger) + */ + if (stricmp(string, "-NOPFS") == 0) { + UsePageFaultHandler = 0; + continue; + } +#endif + +#endif + + +#if(TEN) + /* + ** Enable TEN + */ + if (strstr(string, "TEN")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_TEN; + Special.IsFromInstall = false; + // + // Create the Ten network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + Ten = new TenConnManClass(); + Ten->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + Ten->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // TEN + +#if(MPATH) + /* + ** Enable MPATH + */ + if (strstr(string, "MPATH")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_MPATH; + Special.IsFromInstall = false; + // + // Create the MPath network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + MPath = new MPlayerManClass(); + MPath->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + MPath->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // MPATH + + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + + /* + ** look for passed-in video mode to default to + */ +#ifndef WIN32 + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif + +#ifdef CHEAT_KEYS + if (strstr(string,"-NOMOVIES")){ + bNoMovies = true; + } +#endif + + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + MonoClass::Enable(); + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; + + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + Session.Record = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + Session.Play = 1; + break; + + /* + ** Print lots of debug stuff about events & packets + */ + case 'P': + Debug_Print_Events = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + + default: + puts(TEXT_INVALID); + return(false); + } + + } + + continue; + } + } + return(true); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * This algorithm is cryptographically categorized as a "one way hash". * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cryptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[128]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + for (int index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + +/*********************************************************************************************** + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * * + * This routine is used to create a hash value from a data block. The algorithm is similar * + * to a CRC, but is faster. * + * * + * INPUT: buffer -- Pointer to a buffer of data to be 'hashed'. * + * * + * len -- The length of the buffer to compute the hash upon. * + * * + * OUTPUT: Returns with a 32bit hash value calculated from the specified buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +extern "C" { +long Calculate_CRC(void * buffer, long len) +{ + return(CRCEngine()(buffer, len)); +} +} + + +/*************************************************************************** + * Init_Random -- Initializes the random-number generator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +void Init_Random(void) +{ + #ifdef WIN32 + + /* + ** Gather some "random" bits from the system timer. Actually, only the + ** low order millisecond bits are secure. The other bits could be + ** easily guessed from the system clock (most clocks are fairly accurate + ** and thus predictable). + */ + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + CryptRandom.Seed_Bit(t.wSecond); + CryptRandom.Seed_Bit(t.wSecond>>1); + CryptRandom.Seed_Bit(t.wSecond>>2); + CryptRandom.Seed_Bit(t.wSecond>>3); + CryptRandom.Seed_Bit(t.wSecond>>4); + CryptRandom.Seed_Bit(t.wMinute); + CryptRandom.Seed_Bit(t.wMinute>>1); + CryptRandom.Seed_Bit(t.wMinute>>2); + CryptRandom.Seed_Bit(t.wMinute>>3); + CryptRandom.Seed_Bit(t.wMinute>>4); + CryptRandom.Seed_Bit(t.wHour); + CryptRandom.Seed_Bit(t.wDay); + CryptRandom.Seed_Bit(t.wDayOfWeek); + CryptRandom.Seed_Bit(t.wMonth); + CryptRandom.Seed_Bit(t.wYear); + #else + + /* + ** Gather some "random" bits from the DOS mode timer. + */ + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + CryptRandom.Seed_Byte(t.time); + #endif + +#ifdef FIXIT_MULTI_SAVE + // + // If we've loaded a multiplayer save game, return now; the random # + // class is loaded along with ScenarioClass. + // + if (Session.LoadGame) { + return; + } + + // + // If we're playing a recording, the Seed is loaded in + // Load_Recording_Values(). Just init the random # and return. + // + if (Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#else + /* + ** Do nothing if we've loaded a multiplayer game, or we're playing back + ** a recording; the random number generator is initialized by loading + ** the game. + */ + if (Session.LoadGame || Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#endif // FIXIT_MULTI_SAVE + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH && + !Session.Play) { + + /* + ** Set the optional user-specified seed + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } else { + srand(time(NULL)); + Seed = rand(); + } + } + + /* + ** Initialize the random-number generators + */ + Scen.RandomNumber = Seed; + RandNumb = Seed; +} + + +/*********************************************************************************************** + * Load_Title_Page -- Load the background art for the title page. * + * * + * This routine will load the background art in a machine independent format. There is * + * different art required for the hi-res and lo-res versions of the game. * + * * + * INPUT: visible -- Should the title page art be copied to the visible page by this * + * routine? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the mouse is hidden if the image is to be copied to the visible page. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +void Load_Title_Page(bool visible) +{ +#ifdef WIN32 + Load_Title_Screen("TITLE.PCX", &HidPage, CCPalette); + if (visible) { + HidPage.Blit(SeenPage); + } +#else + Load_Picture("TITLE.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + if (visible) { + HidPage.Blit(SeenPage); + } +#endif +} + + +/*********************************************************************************************** + * Init_Color_Remaps -- Initialize the text remap tables. * + * * + * There are various color scheme remap tables that are dependant upon the color remap * + * information embedded within the palette control file. This routine will fetch that * + * data and build the text remap tables as indicated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Color_Remaps(void) +{ + /* + ** Setup the remap tables. PALETTE.CPS contains a special set of pixels in + ** the upper-left corner. Each row of 16 pixels is one range of colors. The + ** first row represents unity (the default color units are drawn in); rows + ** after that are the remap colors. + */ + +#ifdef WIN32 + + SysMemPage.Clear(); + Load_Picture("PALETTE.CPS", SysMemPage, SysMemPage, NULL, BM_DEFAULT); + SysMemPage.Blit(HidPage); +#else + Load_Picture("PALETTE.CPS", HidPage, HidPage, NULL, BM_DEFAULT); +#endif + for (PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++) { + + unsigned char * ptr = ColorRemaps[pcolor].RemapTable; + + for (int color = 0; color < 256; color++) { + ptr[color] = color; + } + + for (int index = 0; index < 16; index++) { + ptr[HidPage.Get_Pixel(index, 0)] = HidPage.Get_Pixel(index, pcolor); + } + for (index = 0; index < 6; index++) { + ColorRemaps[pcolor].FontRemap[10+index] = HidPage.Get_Pixel(2+index, pcolor); + } + ColorRemaps[pcolor].BrightColor = WHITE; +// ColorRemaps[pcolor].BrightColor = HidPage.Get_Pixel(1, pcolor); + ColorRemaps[pcolor].Color = HidPage.Get_Pixel(4, pcolor); + + ColorRemaps[pcolor].Shadow = HidPage.Get_Pixel(10, pcolor); + ColorRemaps[pcolor].Background = HidPage.Get_Pixel(9, pcolor); + ColorRemaps[pcolor].Corners = HidPage.Get_Pixel(7, pcolor); + ColorRemaps[pcolor].Highlight = HidPage.Get_Pixel(4, pcolor); + ColorRemaps[pcolor].Bright = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Underline = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Bar = HidPage.Get_Pixel(6, pcolor); + + /* + ** This must grab from column 4 because the multiplayer color dialog palette counts + ** on this to be true. + */ + ColorRemaps[pcolor].Box = HidPage.Get_Pixel(4, pcolor); + } + + /* + ** Now do the special dim grey scheme + */ + for (int color = 0; color < 256; color++) { + GreyScheme.RemapTable[color] = color; + } + for (int index = 0; index < 6; index++) { + GreyScheme.FontRemap[10+index] = HidPage.Get_Pixel(9+index, PCOLOR_GREY) & 0x00FF; + } + GreyScheme.BrightColor = HidPage.Get_Pixel(3, PCOLOR_GREY) & 0x00FF; + GreyScheme.Color = HidPage.Get_Pixel(7, PCOLOR_GREY) & 0x00FF; + + GreyScheme.Shadow = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(15, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Background = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(14, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Corners = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(13, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Highlight = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(9, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bright = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Underline = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bar = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Box = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + + /* + ** Set up the metallic remap table for the font that prints over the tabs + */ + memset ((void*)&MetalScheme, 4, sizeof(MetalScheme)); + for (int color_counter = 0; color_counter < 16; color_counter++) { + MetalScheme.FontRemap[color_counter] = color_counter; + } + MetalScheme.FontRemap[1] = 128; + MetalScheme.FontRemap[2] = 12; + MetalScheme.FontRemap[3] = 13; + MetalScheme.FontRemap[4] = 14; + MetalScheme.Color = 128; + MetalScheme.Background = 0; + MetalScheme.Underline = 128; + + /* + ** Set up the font remap table for the mission briefing font + */ + for (int colr = 0; colr < 16; colr++) { + ColorRemaps[PCOLOR_TYPE].FontRemap[colr] = HidPage.Get_Pixel(colr, PCOLOR_TYPE); + } + + ColorRemaps[PCOLOR_TYPE].Shadow = 11; + ColorRemaps[PCOLOR_TYPE].Background = 10; + ColorRemaps[PCOLOR_TYPE].Corners = 10; + ColorRemaps[PCOLOR_TYPE].Highlight = 9; + ColorRemaps[PCOLOR_TYPE].Bright = 15; + ColorRemaps[PCOLOR_TYPE].Underline = 11; + ColorRemaps[PCOLOR_TYPE].Bar = 11; + ColorRemaps[PCOLOR_TYPE].Box = 10; + ColorRemaps[PCOLOR_TYPE].BrightColor = 15; + ColorRemaps[PCOLOR_TYPE].Color = 9; + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); +// GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_BLUE]); +} + + +/*********************************************************************************************** + * Init_Heaps -- Initialize the game heaps and buffers. * + * * + * This routine will allocate the game heaps and buffers. The rules file has already been * + * processed by the time that this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Heaps(void) +{ + /* + ** Initialize the game object heaps. + */ + Vessels.Set_Heap(Rule.VesselMax); + Units.Set_Heap(Rule.UnitMax); + Factories.Set_Heap(Rule.FactoryMax); + Terrains.Set_Heap(Rule.TerrainMax); + Templates.Set_Heap(Rule.TemplateMax); + Smudges.Set_Heap(Rule.SmudgeMax); + Overlays.Set_Heap(Rule.OverlayMax); + Infantry.Set_Heap(Rule.InfantryMax); + Bullets.Set_Heap(Rule.BulletMax); + Buildings.Set_Heap(Rule.BuildingMax); + Anims.Set_Heap(Rule.AnimMax); + Aircraft.Set_Heap(Rule.AircraftMax); + Triggers.Set_Heap(Rule.TriggerMax); + TeamTypes.Set_Heap(Rule.TeamTypeMax); + Teams.Set_Heap(Rule.TeamMax); + Houses.Set_Heap(HOUSE_MAX); + TriggerTypes.Set_Heap(Rule.TrigTypeMax); +// Weapons.Set_Heap(Rule.WeaponMax); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + SpeechBuffer[index] = new char [SPEECH_BUFFER_SIZE]; + SpeechRecord[index] = VOX_NONE; + assert(SpeechBuffer[index] != NULL); + } + + /* + ** Allocate the theater buffer block. + */ + TheaterBuffer = new Buffer(THEATER_BUFFER_SIZE); + assert(TheaterBuffer != NULL); +} + + +/*********************************************************************************************** + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * * + * This routine will search for and register/cache any override mixfiles found. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Expansion_Files(void) +{ + + /* + ** Before all else, cache any additional mixfiles. + */ + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + MFCD::Cache(ptr); + } while (!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + } while (!_dos_findnext(&ff)); + } +} + + +/*********************************************************************************************** + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * * + * This performs the one-time processing required after the bulk data has been cached but * + * before the game actually starts. Typically, this routine extracts pointers to all the * + * embedded data sub-files within the main game data mixfile. This routine must be called * + * AFTER the bulk data has been cached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine AFTER the bulk data has been cached. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_One_Time_Systems(void) +{ + Call_Back(); + Map.One_Time(); + Logic.One_Time(); + Options.One_Time(); + Session.One_Time(); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + VesselTypeClass::One_Time(); + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); +} + + +/*********************************************************************************************** + * Init_Fonts -- Initialize all the game font pointers. * + * * + * This routine is used to fetch pointers to the game fonts. The mixfile containing these * + * fonts must have been previously cached. This routine is a necessary prerequisite to * + * displaying any dialogs or printing any text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Fonts(void) +{ + Metal12FontPtr = MFCD::Retrieve("12METFNT.FNT"); + MapFontPtr = MFCD::Retrieve("HELP.FNT"); + Font6Ptr = MFCD::Retrieve("6POINT.FNT"); + GradFont6Ptr = MFCD::Retrieve("GRAD6FNT.FNT"); + EditorFont = MFCD::Retrieve("EDITFNT.FNT"); + Font8Ptr = MFCD::Retrieve("8POINT.FNT"); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MFCD::Retrieve("3POINT.FNT"); + ScoreFontPtr = MFCD::Retrieve("SCOREFNT.FNT"); + FontLEDPtr = MFCD::Retrieve("LED.FNT"); + VCRFontPtr = MFCD::Retrieve("VCR.FNT"); + TypeFontPtr = MFCD::Retrieve("8POINT.FNT"); //("TYPE.FNT"); //VG 10/17/96 +} + + +/*********************************************************************************************** + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * * + * This routine is called to setup the CD-ROM access or emulation handler. It will ensure * + * that the appropriate CD-ROM is present (dependant on the RequiredCD global). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The fonts, palettes, and other bootstrap systems must have been initialized * + * prior to calling this routine since this routine will quite likely display * + * a dialog box requesting the appropriate CD be inserted. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_CDROM_Access(void) +{ + VisiblePage.Clear(); + HidPage.Clear(); + +#ifdef FIXIT_VERSION_3 + // Determine if we're going to be running from a DVD. + // The entire session will either require a DVD, or the regular CDs. Never both. + // Call Using_DVD() to determine which case it is. + // Here we set the value that Using_DVD() returns. + Determine_If_Using_DVD(); + // Force_CD_Available() is modified when Using_DVD() is true so that all requests become requests for the DVD. +#endif + + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** This call is needed because of a side effect of this function. It will examine the + ** CD-ROMs attached to this computer and set the appropriate status values. Without this + ** call, the "?:\\" could not be filled in correctly. + */ + Force_CD_Available(-1); + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + error = CCFileClass::Set_Search_Drives("?:\\"); + switch (error) { + case 1: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + + case 2: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + if (WWMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + VisiblePage.Clear(); + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + + RequiredCD = -1; + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +} + + +/*********************************************************************************************** + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * * + * This routine will register the initial mixfiles that are required to display error * + * messages and get input from the player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine before any dialogs would be displayed to the * + * player. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bootstrap_Mixfiles(void) +{ + int temp = RequiredCD; + RequiredCD = -2; + +#ifdef WOLAPI_INTEGRATION + CCFileClass fileWolapiMix( "WOLAPI.MIX" ); + if( fileWolapiMix.Is_Available() ) + { + new MFCD( "WOLAPI.MIX", &FastKey ); + MFCD::Cache( "WOLAPI.MIX" ); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + CCFileClass file2("EXPAND2.MIX"); + if (file2.Is_Available()) { + new MFCD("EXPAND2.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND2.MIX"); + assert(ok); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + bool ok1; + #ifdef WIN32 + new MFCD("HIRES1.MIX", &FastKey); + ok1 = MFCD::Cache("HIRES1.MIX"); + assert(ok1); + #else + new MFCD("LORES1.MIX", &FastKey); + ok1 = MFCD::Cache("LORES1.MIX"); + assert(ok1); + #endif +#endif + +#ifdef FIXIT_ANTS // Ok. ajw + CCFileClass file("EXPAND.MIX"); + if (file.Is_Available()) { + new MFCD("EXPAND.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND.MIX"); + assert(ok); + } +#endif + + new MFCD("REDALERT.MIX", &FastKey); + + /* + ** Bootstrap enough of the system so that the error dialog box can successfully + ** be displayed. + */ + new MFCD("LOCAL.MIX", &FastKey); // Cached. + bool ok = MFCD::Cache("LOCAL.MIX"); + assert(ok); + +#ifdef WIN32 + new MFCD("HIRES.MIX", &FastKey); + ok = MFCD::Cache("HIRES.MIX"); + assert(ok); + + new MFCD("NCHIRES.MIX", &FastKey); //Non-cached hires stuff incl VQ palettes +#else + new MFCD("LORES.MIX", &FastKey); + ok = MFCD::Cache("LORES.MIX"); + assert(ok); +#endif //WIN32 + + RequiredCD = temp; +} + + +/*********************************************************************************************** + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * * + * This routine is used to register the mixfiles that are needed for main menu processing. * + * Call this routine before the main menu is display and processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +//#define DENZIL_MIXEXTRACT +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outfile); +#endif + +static void Init_Secondary_Mixfiles(void) +{ + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + + //Denzil extract mixfile + #ifdef DENZIL_MIXEXTRACT + #if(0) + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES1.MIX", "o:\\projects\\radvd\\data\\extract\\movies1.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #else + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES2.MIX", "o:\\projects\\radvd\\data\\extract\\movies2.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #endif + #endif + + /* + ** Inform the file system of the various MIX files. + */ + ConquerMix = new MFCD("CONQUER.MIX", &FastKey); // Cached. +// new MFCD("TRANSIT.MIX", &FastKey); + + if (GeneralMix == NULL) GeneralMix = new MFCD("GENERAL.MIX", &FastKey); // Never cached. + + if (CCFileClass("MOVIES1.MIX").Is_Available()) { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); // Never cached. + } else { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); // Never cached. + } + assert(MoviesMix != NULL); + + /* + ** Register the score mixfile. + */ + ScoresPresent = true; + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + + /* + ** These are sound card specific, but the install program would have + ** copied the correct versions to the hard drive. + */ + new MFCD("SPEECH.MIX", &FastKey); // Never cached. + new MFCD("SOUNDS.MIX", &FastKey); // Cached. + new MFCD("RUSSIAN.MIX", &FastKey); // Cached. + new MFCD("ALLIES.MIX", &FastKey); // Cached. +} + + +/*********************************************************************************************** + * Bootstrap -- Perform the initial bootstrap procedure. * + * * + * This routine will load and initialize the game engine such that a dialog box could be * + * displayed. Because this is very critical, call this routine before any other game * + * initialization code. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Bootstrap(void) +{ + BlackPalette.Set(); + + /* + ** Be sure to short circuit the CD-ROM check if there is a CD-ROM override + ** path. + */ + if (CCFileClass::Is_There_Search_Drives()) { + RequiredCD = -2; + } + + /* + ** Process the message loop until we are in focus. We need to be in focus to read pixels from + ** the screen. + */ + #ifdef WIN32 + do { + Keyboard->Check(); + } while (!GameInFocus); + AllSurfaces.SurfacesRestored = false; + #endif + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); + + /* + ** Register and make resident all local mixfiles with particular emphasis + ** on the mixfiles that are necessary to display and error messages and + ** process further initialization. + */ + Init_Bootstrap_Mixfiles(); + + /* + ** Initialize the resident font pointers. + */ + Init_Fonts(); + +#ifndef WIN32 + /* + ** Install the hard error handler. + */ + _harderr(harderr_handler); // BG: Install hard error handler + + /* + ** Install a Page Fault handler + */ + if (UsePageFaultHandler) { + Install_Page_Fault_Handle(); + } +#endif + + /* + ** Setup the keyboard processor in preparation for the game. + */ + #ifdef WIN32 + Keyboard->Clear(); + #else + Keyboard_Attributes_Off(BREAKON | SCROLLLOCKON | TRACKEXT | PAUSEON | CTRLSON | CTRLCON | FILTERONLY | TASKSWITCHABLE); + Keyboard_Attributes_On(PASSBREAKS); + Keyboard->Clear(); + #endif + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ +#ifdef STEVES_LOAD_OVERRIDE + RawFileClass strings ("CONQUER.ENG"); + if (strings.Is_Available()){ + SystemStrings = new char [strings.Size()]; + strings.Read((void*)SystemStrings, strings.Size()); + }else{ + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); + } +#else + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); +#endif + DebugStrings = (char const *)MFCD::Retrieve("DEBUG.ENG"); + + /* + ** Default palette initialization. + */ + memmove((unsigned char *)&GamePalette[0], (void *)MFCD::Retrieve("TEMPERAT.PAL"), 768L); + WhitePalette[0] = BlackPalette[0]; +// GamePalette.Set(); + + /* + ** Initialize expansion files (if present). Expansion files must be located + ** in the current directory. + */ + Init_Expansion_Files(); + + SidebarScheme.Background = BLACK; + SidebarScheme.Corners = LTGREY; + SidebarScheme.Shadow = DKGREY; + SidebarScheme.Highlight = WHITE; + SidebarScheme.Color = LTGREY; + SidebarScheme.Bright = WHITE; + SidebarScheme.BrightColor = WHITE; + SidebarScheme.Box = LTGREY; + GadgetClass::Set_Color_Scheme(&SidebarScheme); +} + + +/*********************************************************************************************** + * Init_Mouse -- Initialize the mouse system. * + * * + * This routine will ensure that a valid mouse driver is present and a working mouse * + * pointer can be displayed. The mouse is hidden when this routine exits. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Mouse(void) +{ + /* + ** Since there is no mouse shape currently available we need + ** to set one of our own. + */ +#ifdef WIN32 + ShowCursor(false); +#endif + if (MouseInstalled) { + void const * temp_mouse_shapes = MFCD::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, Extract_Shape(temp_mouse_shapes, 0)); + while (Get_Mouse_State() > 1) Show_Mouse(); + } + } else { + char buffer[255]; + GamePalette.Set(); + GamePalette.Set(); + sprintf(buffer, TEXT_NO_MOUSE); + VisiblePage.Clear(); + WWMessageBox().Process(buffer, TXT_OK); + //Prog_End(); + Emergency_Exit(1); + } + + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); + while (Get_Mouse_State() > 1) Show_Mouse(); + Call_Back(); + Hide_Mouse(); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * * + * This is a development routine that restricts access to the game by way of passwords. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Authorization(void) +{ + if (Special.IsFromInstall) return; + + Load_Title_Page(); +#ifdef WIN32 + Wait_Vert_Blank(); +#else //WIN32 + Init_Delay(); + Wait_Vert_Blank(VertBlank); +#endif //WIN32 + + CCPalette.Set(); +// Set_Palette(Palette); + HidPage.Blit(SeenPage); + Show_Mouse(); + + /* + ** Fetch the type of game to be played. This will be either + ** C&C:Red Alert or C&C:Plus. + */ +//tryagain: + + bool ok = Debug_Flag; + int counter = 3; + + if (Debug_Flag) ok = true; + + /* + ** C&C:Red Alert requires a password for legal entry. Try (three times) to get a correct + ** password. If not found, then try again. + */ + bool skipper = false; +#ifdef NEVER + while (!ok && counter) { + SmartPtr str = Fetch_Password(TXT_PASSWORD_CAPTION, TXT_PASSWORD_MESSAGE, TXT_OK); + SmartPtr lptr = &CheatCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &EditorCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &PlayCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + skipper = true; + break; + } + } + + if (ok) break; + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Delay(TIMER_SECOND*(4-counter)*1); + if (WWMessageBox().Process(TXT_PASSWORD_ERROR, TXT_TRY_AGAIN, TXT_CANCEL)) { + goto tryagain; + } + + counter--; + if (counter == 0) goto tryagain; + } +#endif + + if (!skipper) { + CCPalette.Set(); + } + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Call_Back(); +} +#endif + + +/*********************************************************************************************** + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * * + * This routine is called to handle the time consuming process of game initialization. * + * The title page will be displayed when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will take a very long time. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bulk_Data(void) +{ + /* + ** Cache the main game data. This operation can take a very long time. + */ + MFCD::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MFCD::Cache("SOUNDS.MIX"); + MFCD::Cache("RUSSIAN.MIX"); + MFCD::Cache("ALLIES.MIX"); + } + Call_Back(); + + /* + ** Fetch the tutorial message data. + */ + INIClass ini; + ini.Load(CCFileClass("TUTORIAL.INI")); + for (int index = 0; index < ARRAY_SIZE(TutorialText); index++) { + TutorialText[index] = NULL; + + char buffer[128]; + char num[10]; + sprintf(num, "%d", index); + if (ini.Get_String("Tutorial", num, "", buffer, sizeof(buffer))) { + TutorialText[index] = strdup(buffer); + } + } + + /* + ** Perform one-time game system initializations. + */ + Init_One_Time_Systems(); +} + + +/*********************************************************************************************** + * Init_Keys -- Initialize the cryptographic keys. * + * * + * This routine will initialize the fast cryptographic key. It will also initialize the * + * slow one if this is a scenario editor version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Keys(void) +{ + RAMFileClass file((void*)Keys, strlen(Keys)); + INIClass ini; + ini.Load(file); + + FastKey = ini.Get_PKey(true); +#ifdef SCENARIO_EDITOR + SlowKey = ini.Get_PKey(false); +#endif +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_Recording_Values(CCFileClass & file) +{ + Session.Save(file); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Write(&Seed, sizeof(Seed)); + file.Write(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Write(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Write(&Whom, sizeof(Whom)); + file.Write(&Special, sizeof(SpecialClass)); + file.Write(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_Recording_Values(CCFileClass & file) +{ + Session.Load(file); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Read(&Seed, sizeof(Seed)); + file.Read(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Read(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Read(&Whom, sizeof(Whom)); + file.Read(&Special, sizeof(SpecialClass)); + file.Read(&Options, sizeof(GameOptionsClass)); + return (true); +} + +extern "C" { +void __PRO(void) { +// printf("_pro\n"); +} +} + +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outname) + { + CCFileClass inFile(filename); + CCFileClass outFile(outname); + + inFile.Open(); + outFile.Open(WRITE); + + void* buffer = malloc(32768); + + if (buffer) + { + unsigned long size = inFile.Size(); + unsigned long bytes; + + while (size > 0) + { + bytes = inFile.Read(buffer, 32768); + outFile.Write(buffer, bytes); + size -= bytes; + } + + free(buffer); + } + } +#endif + + +#ifdef FIXIT_VERSION_3 + +bool bUsingDVD = false; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +bool Is_DVD_Installed() +{ + bool bInstalled; + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "DVD", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + + return bInstalled; +} + +//*********************************************************************************************** +bool Determine_If_Using_DVD() +{ + // Determines if the user has a DVD currently available. If they do, we'll use it throughout the + // session. Else we won't check for it again and will always ask for CDs. + if( Is_DVD_Installed() ) + { + if( Force_CD_Available( 5 ) ) + { + bUsingDVD = true; + } + else + { + // User hit cancel. Allow things to progress normally. They will be prompted for + // a Red Alert disk as usual. + bUsingDVD = false; + } + } + else + bUsingDVD = false; + + return bUsingDVD; +} + +//*********************************************************************************************** +bool Using_DVD() +{ + return bUsingDVD; +} + +#endif diff --git a/CODE/INIT.CPP.BAK b/CODE/INIT.CPP.BAK new file mode 100644 index 0000000..c3f8c84 --- /dev/null +++ b/CODE/INIT.CPP.BAK @@ -0,0 +1,2758 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\init.cpv 4.82 24 Oct 1996 12:53:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Bootstrap -- Perform the initial bootstrap procedure. * + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * Init_Color_Remaps -- Initialize the text remap tables. * + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * Init_Fonts -- Initialize all the game font pointers. * + * Init_Game -- Main game initialization routine. * + * Init_Heaps -- Initialize the game heaps and buffers. * + * Init_Keys -- Initialize the cryptographic keys. * + * Init_Mouse -- Initialize the mouse system. * + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * Init_Random -- Initializes the random-number generator * + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * Load_Recording_Values -- Loads recording values from recording file * + * Load_Title_Page -- Load the background art for the title page. * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#ifdef WIN32 +#include "tcpip.h" +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include "ccdde.h" + +#include + +RemapControlType SidebarScheme; + + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool sequenced=false); +static void Init_Color_Remaps(void); +static void Init_Heaps(void); +static void Init_Expansion_Files(void); +static void Init_One_Time_Systems(void); +static void Init_Fonts(void); +static void Init_CDROM_Access(void); +static void Init_Bootstrap_Mixfiles(void); +static void Init_Secondary_Mixfiles(void); +static void Init_Mouse(void); +static void Bootstrap(void); +//static void Init_Authorization(void); +static void Init_Bulk_Data(void); +static void Init_Keys(void); + +extern "C" { +extern long RandNumb; +} +#ifndef WIN32 +static int UsePageFaultHandler = 1; // 1 = install PFH +#endif //WIN32 + +//extern int SimRandIndex; +void Init_Random(void); + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode + +bool Load_Recording_Values(CCFileClass & file); +bool Save_Recording_Values(CCFileClass & file); + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE!  + * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +#include "sha.h" +//#include +bool Init_Game(int , char * []) +{ + + //must set international locale up front + //V.Grippi 10/22/96 + //#ifdef WIN32 + //#ifdef German + // setlocale(LC_ALL, "German"); + //#endif + //#endif + + /* + ** Allocate the benchmark tracking objects only if the machine and + ** compile flags indicate. + */ + #ifdef CHEAT_KEYS + if (Processor() >= 2) { + Benches = new Benchmark [BENCH_COUNT]; + } + #endif + + /* + ** Initialize the encryption keys. + */ + Init_Keys(); + + /* + ** Bootstrap as much as possible before error-prone initializations are + ** performed. This bootstrap process will enable the error message + ** handler to function. + */ + //VG_MONO + Mono_Print("about to BOOTSTRAP ourselves!!!!\n"); + Bootstrap(); + + /* + ** Check for an initialize a working mouse pointer. Display error and bail if + ** no mouse driver is installed. + */ + Init_Mouse(); + + /* + ** Initialize access to the CD-ROM and ensure that the CD is inserted. This can, and + ** most likely will, result in a visible prompt. + */ + Init_CDROM_Access(); + + /* + ** Register and cache any secondary mixfiles. + */ + //VG_MONO + Mono_Print("about to call init secondary mixfiles\n"); + Init_Secondary_Mixfiles(); + + /* + ** This is a special hack to initialize the heaps that must be in place before the + ** rules file is processed. These heaps should properly be allocated as a consequence + ** of processing the rules.ini file, but that is a bit beyond the capabilities of + ** the rule parser routine (currently). + */ + HouseTypes.Set_Heap(HOUSE_COUNT); + BuildingTypes.Set_Heap(STRUCT_COUNT); + AircraftTypes.Set_Heap(AIRCRAFT_COUNT); + InfantryTypes.Set_Heap(INFANTRY_COUNT); + BulletTypes.Set_Heap(BULLET_COUNT); + AnimTypes.Set_Heap(ANIM_COUNT); + UnitTypes.Set_Heap(UNIT_COUNT); + VesselTypes.Set_Heap(VESSEL_COUNT); + TemplateTypes.Set_Heap(TEMPLATE_COUNT); + TerrainTypes.Set_Heap(TERRAIN_COUNT); + OverlayTypes.Set_Heap(OVERLAY_COUNT); + SmudgeTypes.Set_Heap(SMUDGE_COUNT); + + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + /* + ** Find and process any rules for this game. + */ + if (RuleINI.Load(CCFileClass("RULES.INI"), false)) { + Rule.Process(RuleINI); + } + + Session.MaxPlayers = Rule.MaxPlayers; + + /* + ** Initialize the game object heaps as well as other rules-dependant buffer allocations. + */ + Init_Heaps(); + + /* + ** Initialize the animation system. + */ + Anim_Init(); + + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } + + /* + ** Play the startup animation. + */ + if (!Special.IsFromInstall && !Special.IsFromWChat) { + VisiblePage.Clear(); + Play_Intro(); + memset(CurrentPalette, 0x01, 768); + WhitePalette.Set(); + } else { + memset(CurrentPalette, 0x01, 768); + } + + /* + ** Initialize the text remap tables. + */ + Init_Color_Remaps(); + + /* + ** Get authorization to access the game. + */ +// Init_Authorization(); +// Show_Mouse(); + + /* + ** If not automatically launching into the intro, then display the title + ** page while the bulk data is cached. + */ + if (!Special.IsFromInstall) { +// Hide_Mouse(); + Load_Title_Page(true); + CCPalette.Set(FADE_PALETTE_SLOW); +// HidPage.Blit(SeenPage); +// Show_Mouse(); + Call_Back(); + } + + /* + ** Initialize the bulk data. This takes the longest time and must be performed once + ** before the regular game starts. + */ + Init_Bulk_Data(); + + /* + ** Initialize the multiplayer score values + */ + Session.GamesPlayed = 0; + Session.NumScores = 0; + Session.CurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + Session.Score[i].Name[0] = '\0'; + Session.Score[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + Session.Score[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + GamePalette = CCPalette; +// InGamePalette = CCPalette; + OriginalPalette = CCPalette; + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + return(true); +} + + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +bool Select_Game(bool fade) +{ + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game +#ifdef WIN32 + SEL_INTERNET, +#endif //WIN32 + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + + if (Special.IsFromInstall) { + display = false; + } + + Show_Mouse(); + +#ifdef WIN32 + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); +#endif //WIN32 + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + #ifdef MIRROR_QUEUE + MirrorList.Init(); + #endif + OutList.Init(); + Frame = 0; + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + PlayerWins = false; + PlayerLoses = false; + Session.ObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; + Session.DesiredFrameRate = 30; + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + Session.Score[i].Kills[Session.CurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (Session.Type == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + Theme.Queue_Song(THEME_CRUS); + + /* + ** If we're playing back a recording, load all pertinent values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (Session.Play && Session.RecordFile.Is_Available()) { + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else + Session.Play = false; + } + +#ifdef WIN32 + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat) { + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } +#endif //WIN32 + + while (process) { + + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Page(); + GamePalette = CCPalette; + + HidPage.Blit(SeenPage); +// if (fade) { +// WhitePalette.Set(); +// CCPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// fade = false; +// } else { + CCPalette.Set(); +// } + + Set_Logic_Page(SeenBuff); + display = false; + Show_Mouse(); + } + + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall) selection = SEL_START_NEW_GAME; + +#ifdef WIN32 + /* + ** Handle case where we were spawned from Wchat and our start game + ** packet has already arrived + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + } else { + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480) { + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } +#endif //WIN32 + + if (selection == SEL_NONE) { + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + + /* + ** Pick an expansion scenario. + */ + case SEL_NEW_SCENARIO: + Scen.CarryOverMoney = 0; + IsTanyaDead = false; + SaveTanya = false; + Expansion_Dialog(); + Theme.Fade_Out(); + Theme.Queue_Song(THEME_FIRST); + Session.Type = GAME_NORMAL; + process = false; + break; + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + if (Special.IsFromInstall) { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } else { + switch (Fetch_Difficulty()) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } + Scen.CarryOverMoney = 0; + BuildLevel = 1; + IsTanyaDead = false; + SaveTanya = false; + Whom = HOUSE_GOOD; + + if (!Special.IsFromInstall) { + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_SOVIET)) { + default: + case 0: + Scen.Set_Scenario_Name("SCG01EA.INI"); + break; + + case 1: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + } + Theme.Fade_Out(); + Load_Title_Page(); + } else { + Theme.Fade_Out(); + Hide_Mouse(); + Choose_Side(); + if (CurrentCD == 0) { + Scen.Set_Scenario_Name("SCG01EA.INI"); + } else { + Scen.Set_Scenario_Name("SCU01EA.INI"); + } + } + + Session.Type = GAME_NORMAL; + process = false; + break; + +#ifdef WIN32 + /* + ** Internet game is requested + */ + case SEL_INTERNET: + /* + ** Only call up the internet menu code if we dont already have connect info from WChat + */ + if (!DDEServer.Get_MPlayer_Game_Info()) { + if (Do_The_Internet_Menu_Thang() && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + display = false; + Session.Type = GAME_INTERNET; + } else { + selection = SEL_NONE; + display = true; + } + } else { + Check_From_WChat(NULL); + display = false; + Session.Type = GAME_INTERNET; + selection = SEL_MULTIPLAYER_GAME; + } + break; +#endif //WIN32 + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + Theme.Queue_Song(THEME_FIRST); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'Session.Type' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: + switch (Session.Type) { + + /* + ** If 'Session.Type' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_SKIRMISH: + if ( !Com_Scenario_Dialog(true) ) { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: + if ( Session.Type != GAME_SKIRMISH && NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((Session.Type == GAME_NULL_MODEM && + Session.ModemType == MODEM_NULL_HOST) || + (Session.Type == GAME_MODEM && + Session.ModemType == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#ifdef WIN32 + /* + ** Handle being spawned from WChat. Internet play based on IPX code. + */ + case GAME_INTERNET: + if (Special.IsFromWChat) { + /* + ** Give myself focus. + */ + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + + OutputDebugString ("RA95 - About to initialise Winsock.\n"); + if (Winsock.Init()) { + OutputDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + OutputDebugString ("RA95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + OutputDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + if (Server) { + Winsock.Start_Server(); + } else { + Winsock.Start_Client(); + } + + + /* + ** Flush out any pending packets from a previous game. + */ + OutputDebugString ("RA95 - About to flush packet queue.\n"); + OutputDebugString ("RA95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + OutputDebugString ("RA95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + OutputDebugString ("RA95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)) { + + OutputDebugString ("RA95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()) {}; + OutputDebugString ("RA95 - Ready to check for more packets.\n"); + + } + OutputDebugString ("RA95 - About to delete scrap memory.\n"); + delete temp_buffer; + + + + } else { + OutputDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + OutputDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()) { + OutputDebugString ("RA95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + } else { + Read_Game_Options( "C&CSPAWN.INI" ); + } + + if (Server) { + + OutputDebugString ("RA95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()) { + OutputDebugString ("RA95 - Server_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } else { + OutputDebugString ("RA95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()) { + OutputDebugString ("RA95 - Client_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } + + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //WIN32 + + } + + switch (Session.Type) { + /* + ** Modem, Null-Modem or internet + */ + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_INTERNET: + case GAME_SKIRMISH: + Theme.Fade_Out(); + process = false; + Options.ScoreVolume = 0; + break; + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + /* + ** Init network system & remote-connect + */ + if (Session.Type == GAME_IPX && Init_Network() && Remote_Connect()) { + Options.ScoreVolume = 0; + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_NONE; + } + break; + } + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + if (Debug_Flag) { + Play_Intro(Debug_Flag); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, true); // no transition picture to briefing +// Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); // has transitino picture to briefing + } + Theme.Queue_Song(THEME_CRUS); + display = true; + fade = true; + selection = SEL_NONE; + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: + Theme.Fade_Out(); + BlackPalette.Set(FADE_PALETTE_SLOW); + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (Session.Attract && Session.RecordFile.Is_Available()) { + Session.Play = true; + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else { + Session.Play = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode to load scenario + */ + Scen.Set_Scenario_Name("SCG01EA.INI"); + } + + /* + ** Don't carry stray keystrokes into game. + */ + Keyboard->Clear(); + + /* + ** Initialize the random number generator(s) + */ + //VG_MONO + Mono_Print("about to call Init_Random, COOL!!\n"); + Init_Random(); + + /* + ** Save initialization values if we're recording this game. + */ + if (Session.Record) { + if (Session.RecordFile.Open(WRITE)) { + Save_Recording_Values(Session.RecordFile); + } else { + Session.Record = false; + } + } + + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded && !Session.LoadGame) { +// if (Debug_Map) { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, SCEN_VAR_A); +// } else { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir); +// } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + //VG_MONO + Mono_Print("about tocall hiddenpage clear\n"); + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + } + Show_Mouse(); + //VG_MONO + Mono_Print("about to call start_scenario\n"); + + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + if (Special.IsFromInstall) Show_Mouse(); + Special.IsFromInstall = false; + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + Session.Messages.Init( + Map.TacPixelX, Map.TacPixelY, // x,y for messages + 6, // max # msgs + MAX_MESSAGE_LENGTH-14, // max msg length + 7 * RESFACTOR, // font height in pixels + -1, -1, // x,y for edit line (appears above msgs) +0,//BG 1, // enable edit overflow + 20, // min, + MAX_MESSAGE_LENGTH - 14, // max for trimming overflow +#ifdef WIN32 + Lepton_To_Pixel(Map.TacLeptonWidth)); // Width in pixels of buffer +#else + (320-SIDEBAR_WID)); // Width in pixels of buffer +#endif + + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && !Session.Play) { + Session.Create_Connections(); + } + + /* + ** Hide the SeenPage; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + //VG_MONO + Mono_Print("about to call Call_Back \n"); + Call_Back(); + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + Show_Mouse(); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + /* + ** Sidebar is always active in hi-res. + */ + //VG_MONO + Mono_Print("about to call map sidebar class::activate\n"); + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } +#endif //WIN32 + Map.Flag_To_Redraw(); + Call_Back(); + //VG_MONO + Mono_Print("about to call Map.Render \n"); + Map.Render(); + Mono_Print("just returned from Map.Render \n"); + + return(true); +} + + +/*********************************************************************************************** + * Pla +y_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + * 05/08/1996 JLB : Modified for Red Alert and direction control. * + *=============================================================================================*/ +static void Play_Intro(bool sequenced) +{ + static VQType _counter = VQ_FIRST; + + Keyboard->Clear(); + if (sequenced) { + if (_counter <= VQ_FIRST) _counter = VQ_COUNT; + if (_counter == VQ_COUNT) _counter--; + if (_counter == VQ_REDINTRO) _counter--; + if (_counter == VQ_TITLE) _counter--; + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQType(_counter--), THEME_NONE); +// Show_Mouse(); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef WIN32 + Play_Movie(VQ_REDINTRO, THEME_NONE, false); +#else + Play_Movie(VQ_TITLE, THEME_NONE, false); +#endif + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +#ifdef MOVIE640 +GraphicBufferClass VQ640(640, 400, (void *)NULL); +#endif +#endif +void Anim_Init(void) +{ + +#ifdef WIN32 + + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; +//AnimControl.DrawFlags |= VQACFGF_NODRAW; +//BG - M. Grayford says turn this off AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); +#ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 640; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } +#endif + AnimControl.Vmode = 0; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } + AnimControl.SoundObject = SoundObject; + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#else //WIN32 + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = MCGA_MODE; + AnimControl.VBIBit = VertBlank; + AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_HMIINIT|VQAOPTF_CAPTIONS|VQAOPTF_EVA; +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + AnimControl.DigiCard = NewConfig.DigitCard; + AnimControl.HMIBufSize = 8192; + AnimControl.DigiHandle = Get_Digi_Handle(); + AnimControl.Volume = 0x00FF; + AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } + + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#endif //WIN32 +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char * argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + string = strupr(argv[index]); + + /* + ** Print usage text only if requested. + */ + if (stricmp("/?", string) == 0 || stricmp("-?", string) == 0 || stricmp("-h", string) == 0 || stricmp("/h", string) == 0) { + /* + ** Unrecognized command line parameter... Display usage + ** and then exit. + */ + puts(TEXT_OPTIONS); + return(false); + } + + + bool processed = true; + long ob = Obfuscate(string); + + /* + ** Check to see if the parameter is a cheat enabling one. + */ + long const * optr = &CheatCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Playtest = true; + Debug_Flag = true; + break; + } + } + + /* + ** Check to see if the parameter is a cheat enabling one. + */ + optr = &PlayCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Playtest = true; + Debug_Flag = true; + break; + } + } + + /* + ** Check to see if the parameter is a scenario editor + ** enabling one. + */ + optr = &EditorCodes[0]; + while (*optr) { + if (*optr++ == ob) { + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + Debug_Playtest = true; + break; + } + } + + switch (ob) { + +#ifdef VIRGIN_CHEAT_KEYS + case PARM_PLAYTEST: + Debug_Playtest = true; + break; +#endif + + /* + ** Special flag - is C&C being run from the install program? + */ + case PARM_INSTALL: + Special.IsFromInstall = true; + BreakoutAllowed = false; + break; + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8, "."); + while (p) { + int x; + + sscanf(p, "%x", &x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL, "."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + Session.NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + Session.NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + Session.Attract = true; + continue; + } + + +#ifdef WIN32 + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string, "-480")) { + ScreenHeight = 480; + continue; + } +#endif + +#ifdef CHEAT_KEYS + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } + +#ifndef WIN32 + /* + ** Don't install Page Fault Handler (MUST use this for debugger) + */ + if (stricmp(string, "-NOPFS") == 0) { + UsePageFaultHandler = 0; + continue; + } +#endif + +#endif + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + + /* + ** look for passed-in video mode to default to + */ +#ifndef WIN32 + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + MonoClass::Enable(); + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; + + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + Session.Record = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + Session.Play = 1; + break; + + /* + ** Print lots of debug stuff about events & packets + */ + case 'P': + Debug_Print_Events = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + + default: + puts(TEXT_INVALID); + return(false); + } + + } + + continue; + } + } + return(true); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * This algorithm is cryptographically categorized as a "one way hash". * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cryptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[128]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + for (int index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + +/*********************************************************************************************** + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * * + * This routine is used to create a hash value from a data block. The algorithm is similar * + * to a CRC, but is faster. * + * * + * INPUT: buffer -- Pointer to a buffer of data to be 'hashed'. * + * * + * len -- The length of the buffer to compute the hash upon. * + * * + * OUTPUT: Returns with a 32bit hash value calculated from the specified buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +extern "C" { +long Calculate_CRC(void * buffer, long len) +{ + return(CRCEngine()(buffer, len)); +} +} + + +/*************************************************************************** + * Init_Random -- Initializes the random-number generator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +void Init_Random(void) +{ + #ifdef WIN32 + + /* + ** Gather some "random" bits from the system timer. Actually, only the + ** low order millisecond bits are secure. The other bits could be + ** easily guessed from the system clock (most clocks are fairly accurate + ** and thus predictable). + */ + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + CryptRandom.Seed_Bit(t.wSecond); + CryptRandom.Seed_Bit(t.wSecond>>1); + CryptRandom.Seed_Bit(t.wSecond>>2); + CryptRandom.Seed_Bit(t.wSecond>>3); + CryptRandom.Seed_Bit(t.wSecond>>4); + CryptRandom.Seed_Bit(t.wMinute); + CryptRandom.Seed_Bit(t.wMinute>>1); + CryptRandom.Seed_Bit(t.wMinute>>2); + CryptRandom.Seed_Bit(t.wMinute>>3); + CryptRandom.Seed_Bit(t.wMinute>>4); + CryptRandom.Seed_Bit(t.wHour); + CryptRandom.Seed_Bit(t.wDay); + CryptRandom.Seed_Bit(t.wDayOfWeek); + CryptRandom.Seed_Bit(t.wMonth); + CryptRandom.Seed_Bit(t.wYear); + #else + + /* + ** Gather some "random" bits from the DOS mode timer. + */ + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + CryptRandom.Seed_Byte(t.time); + #endif + + /* + ** Do nothing if we've loaded a multiplayer game, or we're playing back + ** a recording; the random number generator is initialized by loading + ** the game. + */ + if (Session.LoadGame || Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH && + !Session.Play) { + + /* + ** Set the optional user-specified seed + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } else { + srand(time(NULL)); + Seed = rand(); + } + } + + /* + ** Initialize the random-number generators + */ + Scen.RandomNumber = Seed; + RandNumb = Seed; +} + + +/*********************************************************************************************** + * Load_Title_Page -- Load the background art for the title page. * + * * + * This routine will load the background art in a machine independent format. There is * + * different art required for the hi-res and lo-res versions of the game. * + * * + * INPUT: visible -- Should the title page art be copied to the visible page by this * + * routine? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the mouse is hidden if the image is to be copied to the visible page. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +void Load_Title_Page(bool visible) +{ +#ifdef WIN32 + Load_Title_Screen("TITLE.PCX", &HidPage, CCPalette); + if (visible) { + HidPage.Blit(SeenPage); + } +#else + Load_Picture("TITLE.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + if (visible) { + HidPage.Blit(SeenPage); + } +#endif +} + + +/*********************************************************************************************** + * Init_Color_Remaps -- Initialize the text remap tables. * + * * + * There are various color scheme remap tables that are dependant upon the color remap * + * information embedded within the palette control file. This routine will fetch that * + * data and build the text remap tables as indicated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Color_Remaps(void) +{ + /* + ** Setup the remap tables. PALETTE.CPS contains a special set of pixels in + ** the upper-left corner. Each row of 16 pixels is one range of colors. The + ** first row represents unity (the default color units are drawn in); rows + ** after that are the remap colors. + */ + +#ifdef WIN32 + + SysMemPage.Clear(); + Load_Picture("PALETTE.CPS", SysMemPage, SysMemPage, NULL, BM_DEFAULT); + SysMemPage.Blit(HidPage); +#else + Load_Picture("PALETTE.CPS", HidPage, HidPage, NULL, BM_DEFAULT); +#endif + for (PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++) { + + unsigned char * ptr = ColorRemaps[pcolor].RemapTable; + + for (int color = 0; color < 256; color++) { + ptr[color] = color; + } + + for (int index = 0; index < 16; index++) { + ptr[HidPage.Get_Pixel(index, 0)] = HidPage.Get_Pixel(index, pcolor); + } + for (index = 0; index < 6; index++) { + ColorRemaps[pcolor].FontRemap[10+index] = HidPage.Get_Pixel(2+index, pcolor); + } + ColorRemaps[pcolor].BrightColor = HidPage.Get_Pixel(2, pcolor); + ColorRemaps[pcolor].Color = HidPage.Get_Pixel(4, pcolor); + + ColorRemaps[pcolor].Shadow = HidPage.Get_Pixel(10, pcolor); + ColorRemaps[pcolor].Background = HidPage.Get_Pixel(9, pcolor); + ColorRemaps[pcolor].Corners = HidPage.Get_Pixel(8, pcolor); + ColorRemaps[pcolor].Highlight = HidPage.Get_Pixel(4, pcolor); + ColorRemaps[pcolor].Bright = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Underline = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Bar = HidPage.Get_Pixel(6, pcolor); + ColorRemaps[pcolor].Box = HidPage.Get_Pixel(4, pcolor); + } + + /* + ** Now do the special dim grey scheme + */ + for (int color = 0; color < 256; color++) { + GreyScheme.RemapTable[color] = color; + } + for (int index = 0; index < 6; index++) { + GreyScheme.FontRemap[10+index] = HidPage.Get_Pixel(9+index, PCOLOR_GREY) & 0x00FF; + } + GreyScheme.BrightColor = HidPage.Get_Pixel(3, PCOLOR_GREY) & 0x00FF; + GreyScheme.Color = HidPage.Get_Pixel(7, PCOLOR_GREY) & 0x00FF; + + GreyScheme.Shadow = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(15, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Background = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(14, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Corners = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(13, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Highlight = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(9, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bright = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Underline = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bar = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Box = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + + /* + ** Set up the metallic remap table for the font that prints over the tabs + */ + memset ((void*)&MetalScheme, 4, sizeof(MetalScheme)); + for (int color_counter = 0; color_counter < 16; color_counter++) { + MetalScheme.FontRemap[color_counter] = color_counter; + } + MetalScheme.FontRemap[1] = 128; + MetalScheme.FontRemap[2] = 12; + MetalScheme.FontRemap[3] = 13; + MetalScheme.FontRemap[4] = 14; + MetalScheme.Color = 128; + MetalScheme.Background = 0; + MetalScheme.Underline = 128; + + /* + ** Set up the font remap table for the mission briefing font + */ + for (int colr = 0; colr < 16; colr++) { + ColorRemaps[PCOLOR_TYPE].FontRemap[colr] = HidPage.Get_Pixel(colr, PCOLOR_TYPE); + } + + ColorRemaps[PCOLOR_TYPE].Shadow = 11; + ColorRemaps[PCOLOR_TYPE].Background = 10; + ColorRemaps[PCOLOR_TYPE].Corners = 10; + ColorRemaps[PCOLOR_TYPE].Highlight = 9; + ColorRemaps[PCOLOR_TYPE].Bright = 15; + ColorRemaps[PCOLOR_TYPE].Underline = 11; + ColorRemaps[PCOLOR_TYPE].Bar = 11; + ColorRemaps[PCOLOR_TYPE].Box = 10; + ColorRemaps[PCOLOR_TYPE].BrightColor = 15; + ColorRemaps[PCOLOR_TYPE].Color = 9; + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_BLUE]); +} + + +/*********************************************************************************************** + * Init_Heaps -- Initialize the game heaps and buffers. * + * * + * This routine will allocate the game heaps and buffers. The rules file has already been * + * processed by the time that this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Heaps(void) +{ + /* + ** Initialize the game object heaps. + */ + Vessels.Set_Heap(Rule.VesselMax); + Units.Set_Heap(Rule.UnitMax); + Factories.Set_Heap(Rule.FactoryMax); + Terrains.Set_Heap(Rule.TerrainMax); + Templates.Set_Heap(Rule.TemplateMax); + Smudges.Set_Heap(Rule.SmudgeMax); + Overlays.Set_Heap(Rule.OverlayMax); + Infantry.Set_Heap(Rule.InfantryMax); + Bullets.Set_Heap(Rule.BulletMax); + Buildings.Set_Heap(Rule.BuildingMax); + Anims.Set_Heap(Rule.AnimMax); + Aircraft.Set_Heap(Rule.AircraftMax); + Triggers.Set_Heap(Rule.TriggerMax); + TeamTypes.Set_Heap(Rule.TeamTypeMax); + Teams.Set_Heap(Rule.TeamMax); + Houses.Set_Heap(HOUSE_MAX); + TriggerTypes.Set_Heap(Rule.TrigTypeMax); +// Weapons.Set_Heap(Rule.WeaponMax); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + SpeechBuffer[index] = new char [SPEECH_BUFFER_SIZE]; + SpeechRecord[index] = VOX_NONE; + assert(SpeechBuffer[index] != NULL); + } + + /* + ** Allocate the theater buffer block. + */ + TheaterBuffer = new Buffer(THEATER_BUFFER_SIZE); + assert(TheaterBuffer != NULL); +} + + +/*********************************************************************************************** + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * * + * This routine will search for and register/cache any override mixfiles found. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Expansion_Files(void) +{ + /* + ** Before all else, cache any additional mixfiles. + */ + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + MFCD::Cache(ptr); + } while (!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + } while (!_dos_findnext(&ff)); + } +} + + +/*********************************************************************************************** + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * * + * This performs the one-time processing required after the bulk data has been cached but * + * before the game actually starts. Typically, this routine extracts pointers to all the * + * embedded data sub-files within the main game data mixfile. This routine must be called * + * AFTER the bulk data has been cached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine AFTER the bulk data has been cached. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_One_Time_Systems(void) +{ + Call_Back(); + Map.One_Time(); + Logic.One_Time(); + Options.One_Time(); + Session.One_Time(); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + VesselTypeClass::One_Time(); + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); +} + + +/*********************************************************************************************** + * Init_Fonts -- Initialize all the game font pointers. * + * * + * This routine is used to fetch pointers to the game fonts. The mixfile containing these * + * fonts must have been previously cached. This routine is a necessary prerequisite to * + * displaying any dialogs or printing any text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Fonts(void) +{ + Metal12FontPtr = MFCD::Retrieve("12METFNT.FNT"); + MapFontPtr = MFCD::Retrieve("HELP.FNT"); + Font6Ptr = MFCD::Retrieve("6POINT.FNT"); + GradFont6Ptr = MFCD::Retrieve("GRAD6FNT.FNT"); + EditorFont = MFCD::Retrieve("EDITFNT.FNT"); + Font8Ptr = MFCD::Retrieve("8POINT.FNT"); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MFCD::Retrieve("3POINT.FNT"); + ScoreFontPtr = MFCD::Retrieve("SCOREFNT.FNT"); + FontLEDPtr = MFCD::Retrieve("LED.FNT"); + VCRFontPtr = MFCD::Retrieve("VCR.FNT"); + TypeFontPtr = MFCD::Retrieve("8POINT.FNT"); //("TYPE.FNT"); //VG 10/17/96 +} + + +/*********************************************************************************************** + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * * + * This routine is called to setup the CD-ROM access or emulation handler. It will ensure * + * that the appropriate CD-ROM is present (dependant on the RequiredCD global). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The fonts, palettes, and other bootstrap systems must have been initialized * + * prior to calling this routine since this routine will quite likely display * + * a dialog box requesting the appropriate CD be inserted. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_CDROM_Access(void) +{ + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** This call is needed because of a side effect of this function. It will examine the + ** CD-ROMs attached to this computer and set the appropriate status values. Without this + ** call, the "?:\\" could not be filled in correctly. + */ + Force_CD_Available(-1); + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + error = CCFileClass::Set_Search_Drives("?:\\"); + switch (error) { + case 1: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End(); + exit(EXIT_FAILURE); + + case 2: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + if (WWMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + VisiblePage.Clear(); + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + + RequiredCD = -1; + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +} + + +/*********************************************************************************************** + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * * + * This routine will register the initial mixfiles that are required to display error * + * messages and get input from the player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine before any dialogs would be displayed to the * + * player. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bootstrap_Mixfiles(void) +{ + int temp = RequiredCD; + RequiredCD = -2; + + new MFCD("REDALERT.MIX", &FastKey); + + /* + ** Bootstrap enough of the system so that the error dialog box can successfully + ** be displayed. + */ + new MFCD("LOCAL.MIX", &FastKey); // Cached. + bool ok = MFCD::Cache("LOCAL.MIX"); + assert(ok); + +#ifdef WIN32 + new MFCD("HIRES.MIX", &FastKey); + ok = MFCD::Cache("HIRES.MIX"); + assert(ok); + + new MFCD("NCHIRES.MIX", &FastKey); //Non-cached hires stuff incl VQ palettes +#else + new MFCD("LORES.MIX", &FastKey); + ok = MFCD::Cache("LORES.MIX"); + assert(ok); +#endif //WIN32 + + RequiredCD = temp; +} + + +/*********************************************************************************************** + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * * + * This routine is used to register the mixfiles that are needed for main menu processing. * + * Call this routine before the main menu is display and processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Secondary_Mixfiles(void) +{ + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + + /* + ** Inform the file system of the various MIX files. + */ + ConquerMix = new MFCD("CONQUER.MIX", &FastKey); // Cached. +// new MFCD("TRANSIT.MIX", &FastKey); + + if (GeneralMix == NULL) GeneralMix = new MFCD("GENERAL.MIX", &FastKey); // Never cached. + + if (CCFileClass("MOVIES1.MIX").Is_Available()) { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); // Never cached. + } else { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); // Never cached. + } + assert(MoviesMix != NULL); + + /* + ** Register the score mixfile. + */ + ScoresPresent = true; + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + + /* + ** These are sound card specific, but the install program would have + ** copied the correct versions to the hard drive. + */ + new MFCD("SPEECH.MIX", &FastKey); // Never cached. + new MFCD("SOUNDS.MIX", &FastKey); // Cached. + new MFCD("RUSSIAN.MIX", &FastKey); // Cached. + new MFCD("ALLIES.MIX", &FastKey); // Cached. +} + + +/*********************************************************************************************** + * Bootstrap -- Perform the initial bootstrap procedure. * + * * + * This routine will load and initialize the game engine such that a dialog box could be * + * displayed. Because this is very critical, call this routine before any other game * + * initialization code. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Bootstrap(void) +{ + BlackPalette.Set(); + + /* + ** Be sure to short circuit the CD-ROM check if there is a CD-ROM override + ** path. + */ + if (CCFileClass::Is_There_Search_Drives()) { + RequiredCD = -2; + } + + /* + ** Process the message loop until we are in focus. We need to be in focus to read pixels from + ** the screen. + */ + #ifdef WIN32 + do { + Keyboard->Check(); + } while (!GameInFocus); + AllSurfaces.SurfacesRestored = false; + #endif + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); + + /* + ** Register and make resident all local mixfiles with particular emphasis + ** on the mixfiles that are necessary to display and error messages and + ** process further initialization. + */ + Init_Bootstrap_Mixfiles(); + + /* + ** Initialize the resident font pointers. + */ + Init_Fonts(); + +#ifndef WIN32 + /* + ** Install the hard error handler. + */ + _harderr(harderr_handler); // BG: Install hard error handler + + /* + ** Install a Page Fault handler + */ + if (UsePageFaultHandler) { + Install_Page_Fault_Handle(); + } +#endif + + /* + ** Setup the keyboard processor in preparation for the game. + */ + #ifdef WIN32 + Keyboard->Clear(); + #else + Keyboard_Attributes_Off(BREAKON | SCROLLLOCKON | TRACKEXT | PAUSEON | CTRLSON | CTRLCON | FILTERONLY | TASKSWITCHABLE); + Keyboard_Attributes_On(PASSBREAKS); + Keyboard->Clear(); + #endif + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); + DebugStrings = (char const *)MFCD::Retrieve("DEBUG.ENG"); + + /* + ** Default palette initialization. + */ + memmove((unsigned char *)&GamePalette[0], (void *)MFCD::Retrieve("TEMPERAT.PAL"), 768L); + WhitePalette[0] = BlackPalette[0]; +// GamePalette.Set(); + + /* + ** Initialize expansion files (if present). Expansion files must be located + ** in the current directory. + */ + Init_Expansion_Files(); + + SidebarScheme.Background = BLACK; + SidebarScheme.Corners = LTGREY; + SidebarScheme.Shadow = DKGREY; + SidebarScheme.Highlight = WHITE; + SidebarScheme.Color = LTGREY; + SidebarScheme.Bright = WHITE; + SidebarScheme.BrightColor = WHITE; + SidebarScheme.Box = LTGREY; + GadgetClass::Set_Color_Scheme(&SidebarScheme); +} + + +/*********************************************************************************************** + * Init_Mouse -- Initialize the mouse system. * + * * + * This routine will ensure that a valid mouse driver is present and a working mouse * + * pointer can be displayed. The mouse is hidden when this routine exits. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Mouse(void) +{ + /* + ** Since there is no mouse shape currently available we need + ** to set one of our own. + */ +#ifdef WIN32 + ShowCursor(false); +#endif + if (MouseInstalled) { + void const * temp_mouse_shapes = MFCD::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, Extract_Shape(temp_mouse_shapes, 0)); + while (Get_Mouse_State() > 1) Show_Mouse(); + } + } else { + char buffer[255]; + GamePalette.Set(); + GamePalette.Set(); + sprintf(buffer, TEXT_NO_MOUSE); + VisiblePage.Clear(); + WWMessageBox().Process(buffer, TXT_OK); + Prog_End(); + exit(1); + } + + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); + while (Get_Mouse_State() > 1) Show_Mouse(); + Call_Back(); + Hide_Mouse(); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * * + * This is a development routine that restricts access to the game by way of passwords. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Authorization(void) +{ + if (Special.IsFromInstall) return; + + Load_Title_Page(); +#ifdef WIN32 + Wait_Vert_Blank(); +#else //WIN32 + Init_Delay(); + Wait_Vert_Blank(VertBlank); +#endif //WIN32 + + CCPalette.Set(); +// Set_Palette(Palette); + HidPage.Blit(SeenPage); + Show_Mouse(); + + /* + ** Fetch the type of game to be played. This will be either + ** C&C:Red Alert or C&C:Plus. + */ +//tryagain: + + bool ok = Debug_Flag; + int counter = 3; + + if (Debug_Flag) ok = true; + + /* + ** C&C:Red Alert requires a password for legal entry. Try (three times) to get a correct + ** password. If not found, then try again. + */ + bool skipper = false; +#ifdef NEVER + while (!ok && counter) { + SmartPtr str = Fetch_Password(TXT_PASSWORD_CAPTION, TXT_PASSWORD_MESSAGE, TXT_OK); + SmartPtr lptr = &CheatCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &EditorCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &PlayCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + skipper = true; + break; + } + } + + if (ok) break; + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Delay(TIMER_SECOND*(4-counter)*1); + if (WWMessageBox().Process(TXT_PASSWORD_ERROR, TXT_TRY_AGAIN, TXT_CANCEL)) { + goto tryagain; + } + + counter--; + if (counter == 0) goto tryagain; + } +#endif + + if (!skipper) { + CCPalette.Set(); + } + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Call_Back(); +} +#endif + + +/*********************************************************************************************** + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * * + * This routine is called to handle the time consuming process of game initialization. * + * The title page will be displayed when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will take a very long time. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bulk_Data(void) +{ + /* + ** Cache the main game data. This operation can take a very long time. + */ + MFCD::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MFCD::Cache("SOUNDS.MIX"); + MFCD::Cache("RUSSIAN.MIX"); + MFCD::Cache("ALLIES.MIX"); + } + Call_Back(); + + /* + ** Fetch the tutorial message data. + */ + INIClass ini; + ini.Load(CCFileClass("TUTORIAL.INI")); + for (int index = 0; index < ARRAY_SIZE(TutorialText); index++) { + TutorialText[index] = NULL; + + char buffer[128]; + char num[10]; + sprintf(num, "%d", index); + if (ini.Get_String("Tutorial", num, "", buffer, sizeof(buffer))) { + TutorialText[index] = strdup(buffer); + } + } + + /* + ** Perform one-time game system initializations. + */ + Init_One_Time_Systems(); +} + + +/*********************************************************************************************** + * Init_Keys -- Initialize the cryptographic keys. * + * * + * This routine will initialize the fast cryptographic key. It will also initialize the * + * slow one if this is a scenario editor version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Keys(void) +{ + RAMFileClass file((void*)Keys, strlen(Keys)); + INIClass ini; + ini.Load(file); + + FastKey = ini.Get_PKey(true); +#ifdef SCENARIO_EDITOR + SlowKey = ini.Get_PKey(false); +#endif +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_Recording_Values(CCFileClass & file) +{ + Session.Save(file); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Write(&Seed, sizeof(Seed)); + file.Write(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Write(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Write(&Whom, sizeof(Whom)); + file.Write(&Special, sizeof(SpecialClass)); + file.Write(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_Recording_Values(CCFileClass & file) +{ + Session.Load(file); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Read(&Seed, sizeof(Seed)); + file.Read(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Read(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Read(&Whom, sizeof(Whom)); + file.Read(&Special, sizeof(SpecialClass)); + file.Read(&Options, sizeof(GameOptionsClass)); + return (true); +} + +extern "C" { +void __PRO(void) { +// printf("_pro\n"); +} +} diff --git a/CODE/INLINE.H b/CODE/INLINE.H new file mode 100644 index 0000000..5153183 --- /dev/null +++ b/CODE/INLINE.H @@ -0,0 +1,1069 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INLINE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INLINE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/21/96 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Cell_Coord -- Convert a cell to a coordinate value. * + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * Cell_X -- Fetch the X cell component from the cell value. * + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * Coord_Add -- Adds coordinates together. * + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * Coord_Mid -- Finds the midpoint between two coordinates. * + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * Coord_Sub -- Subtracts one coordinate from another. * + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * Dir_Facing -- Convert a DirType into a FacingType value. * + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * Direction -- Calculates the DirType from one cell to another. * + * Direction -- Determines the facing value from one coordinate to another. * + * Direction256 -- Calculate the facing value from one coordinate to another. * + * Direction8 -- Fetches the direction from one coordinate to another. * + * Distance -- Finds the distance between two arbitrary points. * + * Facing_Dir -- Convert a FacingType into a DirType. * + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * Percent_Chance -- Calculate a percentage chance event. * + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * Random_Pick -- Pick a random number in a specified range. * + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * Text_String -- Convert a text number into a text pointer. * + * XYP_COORD -- Convert pixel components into a coordinate value. * + * XYP_Coord -- Combine pixel values into a coordinate. * + * XY_Cell -- Create a cell from X and Y cell components. * + * XY_Coord -- Convert X Y lepton components into a COORD. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INLINE_H +#define INLINE_H + + +/*********************************************************************************************** + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * * + * Use this routine to convert the specified lepton value into it's pixel corresponding * + * value. The pixel value returned will be the closest pixel value to the lepton value. It * + * will round up or down as necessary. * + * * + * INPUT: lepton -- The lepton value to convert into a pixel value. * + * * + * OUTPUT: Returns with the lepton value rounded to the nearest pixel component. * + * * + * WARNINGS: Precision is not maintained by this routine. Thus, if a value is converted to * + * pixel and then back to leptons, the value will probably not be the same. * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Pixel(LEPTON lepton) +{ + return (((int)(signed short)lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2)) / ICON_LEPTON_W; +} + + +/*********************************************************************************************** + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * * + * This routine will take the specified pixel value and convert it into lepton value. * + * * + * INPUT: pixel -- The pixel value to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the pixel value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Pixel_To_Lepton(int pixel) +{ + return (LEPTON)(((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2)) / ICON_PIXEL_W); +} + + +/*********************************************************************************************** + * XY_Coord -- Convert X Y lepton components into a COORD. * + * * + * This routine will take the specified X and Y lepton components and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y lepton components to combine. * + * * + * OUTPUT: Returns with a coordinate value that is created from the X and Y lepton components.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XY_Coord(LEPTON x, LEPTON y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = x; + coord.Sub.Y.Raw = y; + return(coord.Coord); +} + + +/*********************************************************************************************** + * XYP_COORD -- Convert pixel components into a coordinate value. * + * * + * This routine will take the specified pixel components and convert and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y pixel components to coerce into a coordinate value. * + * * + * OUTPUT: Returns with the coordinate value that matches the pixel values specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_COORD(int x, int y) +{ + return(XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y))); +} + + +/*********************************************************************************************** + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * * + * This routine will extract the X cell component from the coordinate value specified and * + * return the value. * + * * + * INPUT: coord -- The coordinate value to extract the X component from. * + * * + * OUTPUT: Returns with the X cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_XCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * * + * This routine will extract the Y cell component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate to extract the Y cell from. * + * * + * OUTPUT: Returns with just the Y cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_YCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell); +} + + +/*********************************************************************************************** + * XY_Cell -- Create a cell from X and Y cell components. * + * * + * This routine will construct a cell value by taking the X and Y cell value components * + * and combining them appropriately. * + * * + * INPUT: x,y -- The X and Y cell components to combine. * + * * + * OUTPUT: Returns with the CELL value created from the specified components. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL XY_Cell(int x, int y) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = x; + cell.Sub.Y = y; + return(cell.Cell); +} + + +/*********************************************************************************************** + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * * + * This routine will take the cell distance specified and convert it into a lepton distance.* + * * + * INPUT: cell_distance -- The distance in cells to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the cell distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Cell_To_Lepton(int cell_distance) +{ + LEPTON_COMPOSITE lepton; + lepton.Sub.Cell = (unsigned char)cell_distance; + lepton.Sub.Lepton = 0; + return(lepton.Raw); +} + + +/*********************************************************************************************** + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * * + * This routine will convert the specified lepton distance into the closest cell distance * + * possible. This might require rounding up or down as necessary. * + * * + * INPUT: lepton_distance -- The lepton distance to convert. * + * * + * OUTPUT: Returns with the cell distance that most closely corresponds to the lepton * + * distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Cell(LEPTON lepton_distance) +{ + if (((LEPTON_COMPOSITE &)lepton_distance).Sub.Lepton >= (CELL_LEPTON_W/2)) { + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell + 1); + } + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * * + * This routine will extract the X lepton component from the coordinate. * + * * + * INPUT: coord -- The coordinate to extract the X lepton equivalent from. * + * * + * OUTPUT: Returns with the X lepton portion of the coordinate value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_X(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Raw); +} + + +/*********************************************************************************************** + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * * + * This routine will extract the Y lepton component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate value to dissect. * + * * + * OUTPUT: Returns with the Y lepton component from the specified coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_Y(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Raw); +} + + +/*********************************************************************************************** + * Cell_X -- Fetch the X cell component from the cell value. * + * * + * This routine will extract the X cell component from the cell value specified. * + * * + * INPUT: cell -- The cell to extract. * + * * + * OUTPUT: Returns with the X cell component portion of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_X(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.X); +} + + +/*********************************************************************************************** + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * * + * This routine will extract the Y cell component from the cell value. * + * * + * INPUT: cell -- The cell value to extract from. * + * * + * OUTPUT: Returns with the Y cell component of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_Y(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.Y); +} + + +/*********************************************************************************************** + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * * + * This routine will extract just the X sub cell lepton component from the coordinate * + * specified. * + * * + * INPUT: coord -- The coordinate value to extract from. * + * * + * OUTPUT: Returns with the X lepton component of the coordinate that is part of the cell. * + * Thus, a coordinate that exactly lines up on the left edge of a cell would return * + * zero. One that exactly lines up on the right edge would return CELL_LEPTON_W. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_XLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton); +} + + +/*********************************************************************************************** + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * * + * This routine will extract the sub-cell Y lepton portion of the coordinate. * + * * + * INPUT: coord -- The coordinate to dissect. * + * * + * OUTPUT: Returns with just the Y lepton portion of the coordinate and only for the sub-cell * + * it refers to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_YLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton); +} + + +/*********************************************************************************************** + * XYP_Coord -- Combine pixel values into a coordinate. * + * * + * This will convert X and Y pixel values into a coordinate. Primarily this is used for * + * converting mouse clicks into coordinate values. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to convert. Origin is upper left corner. * + * * + * OUTPUT: Returns with the coordinate that most closely corresponds to the pixel values * + * specified. * + * * + * WARNINGS: The coordinate is relative to the upper left corner (0,0). To conver the * + * coordinate to a game relative one, it must be biased by the display coordinate * + * of the tactical map and the screen position of the tactical display. * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_Coord(int x, int y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = Pixel_To_Lepton(x); + coord.Sub.Y.Raw = Pixel_To_Lepton(y); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Cell_Coord -- Convert a cell to a coordinate value. * + * * + * This routine will convert the specified cell into a coordinat value. The coordinate * + * will refer to the center of the cell specified. * + * * + * INPUT: cell -- The cell to convert into a coordinate. * + * * + * OUTPUT: Returns with the coordinate that refers to the center of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Cell_Coord(CELL cell) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.X); + coord.Sub.X.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + coord.Sub.Y.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.Y); + coord.Sub.Y.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * * + * This routine will take the specified coordinate and force it to refer to the center of * + * the cell. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with the specified coordinate after it has been modified to refer to the * + * center of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Snap(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = CELL_LEPTON_W/2; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = CELL_LEPTON_W/2; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * * + * Use this routine to discard the cell components of the coordinate, leaving only the * + * sub-cell component. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with just the sub-cell components intact from the supplied coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Fraction(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * * + * This routine will discard the sub-cell components, leaving only the whole cell portion. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with only the whole cell components of the coordinate intact. The * + * resulting coordinate will refer to the upper left corner of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Whole(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Add -- Adds coordinates together. * + * * + * This routine will add one coordinate to another. Actually, it adds the X and Y components* + * separately (signed) and then recombines them back into a coordinate. * + * * + * INPUT: coord1 -- One coordinate to add. * + * * + * coord2 -- The other coordinate to add. * + * * + * OUTPUT: Returns with the logical add of the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Sub -- Subtracts one coordinate from another. * + * * + * This routine will subtract one coordinate from the other. The coordinates are broken * + * up into their X and Y components, the subtraction is performed, and then they are * + * recombined back into a coordinate to be returned. * + * * + * INPUT: coord1 -- The coordinate to be subtracted from. * + * * + * coord2 -- The coordinate to subtract. * + * * + * OUTPUT: Returns with the result of subtracting coord2 from coord1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Mid -- Finds the midpoint between two coordinates. * + * * + * This will find the coordinate that is exactly between the two coordinates specified. * + * * + * INPUT: coord1 -- The first coordinate. * + * * + * coord2 -- The second coordinate. * + * * + * OUTPUT: Returns with the midpoint between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.X.Raw) / 2); + coord.Sub.Y.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.Y.Raw) / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Dir_Facing -- Convert a DirType into a FacingType value. * + * * + * Use this routine to convert the specified DirType value into the closest FacingType * + * value that matches it. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with a FacingType value that most closely matches the DirType specified. * + * * + * WARNINGS: Precision of direction is lost by this transformation. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_Facing(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing+0x10)&0xFF)>>5); +} + + +/*********************************************************************************************** + * Facing_Dir -- Convert a FacingType into a DirType. * + * * + * This will conver the specified FacingType value into the DirType that exactly matches * + * it. * + * * + * INPUT: facing -- The FacingType to convert. * + * * + * OUTPUT: Returns with the DirType that exactly matches the facing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Facing_Dir(FacingType facing) +{ + return (DirType)((int)facing << 5); +} + + +/*********************************************************************************************** + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * * + * Use this routine to convert a DirType into a 0 through 15 value. * + * * + * INPUT: facing -- The DirType to convert into a 0..15 value. * + * * + * OUTPUT: Returns with the facing converted into a value where 0 equals North, 4 equals * + * East, 8 equals South, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_16(DirType facing) +{ + return Facing16[facing]; +} + + +/*********************************************************************************************** + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * * + * This will convert the DirType specified, into a 0 through 31 value where zero is North, * + * and rotates clockwise. The return value is baised to take into consideration the * + * distortion caused by 3D studio upon the game vehicle objects. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the facing converted into a value from zero to 31. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_32(DirType facing) +{ + return Facing32[facing]; +} + + +/*********************************************************************************************** + * Direction256 -- Calculate the facing value from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate that facing will be calcuated to. * + * * + * OUTPUT: Returns with the DirType that is the facing from coord1 to coord2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Determines the facing value from one coordinate to another. * + * * + * This will determine the DirType from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate to calculate facing to. * + * * + * OUTPUT: Returns with the DirType that represents the facing from coordinate 1 to coordinate* + * 2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction8 -- Fetches the direction from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. The return value * + * is of limited accuracy, but the calculation is fast. * + * * + * INPUT: coord1 -- The coordinate to calculate the facing from. * + * * + * coord2 -- The coordinate to figure the facing to. * + * * + * OUTPUT: Returns with the DirType to get from coordinate 1 to coordinate 2. * + * * + * WARNINGS: The return DirType is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Calculates the DirType from one cell to another. * + * * + * This routine will calculate the facing to get from one cell to another. Since dealing * + * with cells is much less precise than with coordinates, the return value is only * + * accurate to 8 facings. * + * * + * INPUT: cell1 -- The cell to calculate the DirType from. * + * * + * cell2 -- The cell to calculate the DirType to. * + * * + * OUTPUT: Returns with the DirType to get from the first cell to the second. * + * * + * WARNINGS: The return value is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(CELL cell1, CELL cell2) +{ + return (Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) +{ + return (Coord_Snap(Coord_Add(AdjacentCoord[(int)dir & 0x07], coord))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) +{ + return Adjacent_Cell(coord, Dir_Facing(dir)); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, FacingType dir) +{ + return (CELL)(cell + AdjacentCell[dir]); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, DirType dir) +{ + return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]); +} + + +/*********************************************************************************************** + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * * + * This routine will convert a DirType value into a facing number from 0 to 7. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the DirType converted to a number from 0 to 7 with 0 being North and * + * rotating clockwise. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_To_8(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing|0x10))>>5); +} + + +/*********************************************************************************************** + * Text_String -- Convert a text number into a text pointer. * + * * + * This routine will convert text numbers (as generated elsewhere) into an actual text * + * pointer that can be used for normal purposes. * + * * + * INPUT: string -- The text number to extract a pointer to. * + * * + * OUTPUT: Returns with a pointer to the text that represents the text number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline char const * Text_String(int string) +{ +#ifdef FIXIT_NAME_OVERRIDE + if (string < 0 && abs(string) < ARRAY_SIZE(NameOverride)) { + return(NameOverride[-(string+1)]); + } +#endif + + if (string < 1000) return(Extract_String(SystemStrings, string)); + return(Extract_String(DebugStrings, string-1000)); +} + + +/*********************************************************************************************** + * Random_Pick -- Pick a random number in a specified range. * + * * + * This routine is used to pick a game influencing random number between (inclusive) the * + * range specified. * + * * + * INPUT: a -- Low limit of range to pick from. * + * * + * b -- High limit of range to pick from. * + * * + * OUTPUT: Returns with a random number picked between (inclusive) the range of values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +template inline T Random_Pick(T a, T b) +{ + return T(Scen.RandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Percent_Chance -- Calculate a percentage chance event. * + * * + * This will calculate a percentage chance and return with 'true' as likely as the * + * chance value would occur (or less) on a random pick from 1 to 100. Thus a * + * Percent_Chance(50) would return 'true' 50 percent of the time. Percent_Chance(25) would * + * return 'true' 25% of the time, etc. * + * * + * INPUT: percent -- The percent value to calculate the chance upon. * + * * + * OUTPUT: Returns with 'true' in the same percentage as the percentage number supplied. * + * * + * WARNINGS: This affects the game syncronization random number generator and should be used * + * for those events that could affect the game engine. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Percent_Chance(int percent) +{ + return (Scen.RandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * * + * Use this routine to pick a random number such that it will be used so that it won't * + * actually affect the outcome of the game. It is critical to use this routine for any * + * random need that won't be needed on other machines in a multiplayer game. The result * + * can be freely used as long as it doesn't affect the outcome of the game. * + * * + * INPUT: a -- Low range of the random number to pick. * + * * + * b -- High range of the random number to pick. * + * * + * OUTPUT: Returns with a random number between (inclusive) the range limit values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +extern RandomClass NonCriticalRandomNumber; +template inline T Sim_Random_Pick(T a, T b) +{ + return T(NonCriticalRandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * * + * This routine is similar to the normal Percent_Chance() routine except that it doesn't * + * alter the main random number gerenator sequence. As such, this routine should be used * + * for those events that should have a random character, but will either not affect the * + * game engine or are only calculated on one machine in a multiplayer game. * + * * + * INPUT: percent -- The percent chance to calculate the possible return of 'true' on. * + * * + * OUTPUT: Returns 'true' with the same percentage chance as the percent number specified. * + * A percent value of 50 means 50%, 25 means 25%, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Sim_Percent_Chance(int percent) +{ + return (NonCriticalRandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Distance -- Finds the distance between two arbitrary points. * + * * + * This finds the (Dragon Strike) distance between two arbitrary points in flat space. * + * It does this by adding 1/2 the smaller absolute axis difference to the other absolute * + * axis distance. The result is rough but quick to calculate. * + * * + * INPUT: x1,y1 -- Coordinate location for point 1. * + * * + * x2,y2 -- Coordinate location for point 2. * + * * + * OUTPUT: Returns with the rough distance between the two points. The value returned is * + * expressed in the same units as the parameters were specified in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +inline int Distance(int x1, int y1, int x2, int y2) +{ + int diff1 = y1 - y2; + if (diff1 < 0) diff1 = -diff1; + int diff2 = x1 - x2; + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +#endif + diff --git a/CODE/INT.CPP b/CODE/INT.CPP new file mode 100644 index 0000000..deff3bb --- /dev/null +++ b/CODE/INT.CPP @@ -0,0 +1,46 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "mp.h" +#include "int.h" + +int bignum::Error = 0; +bool bignum::Carry = false; +bool bignum::Borrow = false; +bignum bignum::Remainder; + diff --git a/CODE/INT.H b/CODE/INT.H new file mode 100644 index 0000000..87e634a --- /dev/null +++ b/CODE/INT.H @@ -0,0 +1,304 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INT_H +#define INT_H + +#include +#include +#include +#include "mp.h" +#include "straw.h" + +#pragma warn -inl + +template +class Int { + public: + + /* + ** Constructors and initializers. + */ + Int(void) {XMP_Init(®[0], 0, PRECISION);} + Int(unsigned long value) {XMP_Init(®[0], value, PRECISION);} + + void Randomize(Straw & rng, int bitcount) {XMP_Randomize(®[0], rng, bitcount, PRECISION);} + void Randomize(Straw & rng, const Int & minval, const Int & maxval) {XMP_Randomize(®[0], rng, minval, maxval, PRECISION); reg[0] |= 1;} + + /* + ** Convenient conversion operators to get at the underlying array of + ** integers. Big number math is basically manipulation of arbitrary + ** length arrays. + */ + operator digit * () {return & reg[0];} + operator const digit * () const {return & reg[0];} + + /* + ** Array access operator (references bit position). Bit 0 is the first bit. + */ + bool operator[](unsigned bit) const {return(XMP_Test_Bit(®[0], bit));} + + /* + ** Unary operators. + */ + Int & operator ++ (void) {XMP_Inc(®[0], PRECISION);return(*this);} + Int & operator -- (void) {XMP_Dec(®[0], PRECISION);return(*this);} + int operator ! (void) const {return(XMP_Test_Eq_Int(®[0], 0, PRECISION));} + Int operator ~ (void) {XMP_Not(®[0], PRECISION);return(*this);} + Int operator - (void) const {Int a = *this;a.Negate();return (a);} + + /* + ** Attribute query functions. + */ + int ByteCount(void) const {return(XMP_Count_Bytes(®[0], PRECISION));} + int BitCount(void) const {return(XMP_Count_Bits(®[0], PRECISION));} + bool Is_Negative(void) const {return(XMP_Is_Negative(®[0], PRECISION));} + unsigned MaxBitPrecision() const {return PRECISION*(sizeof(unsigned long)*CHAR_BIT);} + bool IsSmallPrime(void) const {return(XMP_Is_Small_Prime(®[0], PRECISION));} + bool SmallDivisorsTest(void) const {return(XMP_Small_Divisors_Test(®[0], PRECISION));} + bool FermatTest(unsigned rounds) const {return(XMP_Fermat_Test(®[0], rounds, PRECISION));} + bool IsPrime(void) const {return(XMP_Is_Prime(®[0], PRECISION));} + bool RabinMillerTest(Straw & rng, unsigned int rounds) const {return(XMP_Rabin_Miller_Test(rng, ®[0], rounds, PRECISION));} + + /* + ** 'in-place' binary operators. + */ + Int & operator += (const Int & number) {Carry = XMP_Add(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator -= (const Int & number) {Borrow = XMP_Sub(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator *= (const Int & multiplier) {Remainder = *this;Error=XMP_Signed_Mult(®[0], Remainder, multiplier, PRECISION);return(*this);} + Int & operator /= (const Int & t) {*this = (*this) / t;return *this;} + Int & operator %= (const Int & t) {*this = (*this) % t;return *this;} + Int & operator <<= (int bits) {XMP_Shift_Left_Bits(®[0], bits, PRECISION);return *this;} + Int & operator >>= (int bits) {XMP_Shift_Right_Bits(®[0], bits, PRECISION);return *this;} + + /* + ** Mathematical binary operators. + */ + Int operator + (const Int & number) const {Int term;Carry = XMP_Add(term, ®[0], number, 0, PRECISION);return(term);} + Int operator + (unsigned short b) const {Int result;Carry=XMP_Add_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator + (digit b, const Int & a) {return(Int(b) + a);} + Int operator - (const Int & number) const {Int term;Borrow = XMP_Sub(term, ®[0], number, 0, PRECISION);return(term);} + Int operator - (unsigned short b) const {Int result;Borrow = XMP_Sub_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator - (digit b, const Int & a) {return(Int(b) - a);} + Int operator * (const Int & multiplier) const {Int result;Error=XMP_Signed_Mult(result, ®[0], multiplier, PRECISION);return result;} + Int operator * (unsigned short b) const {Int result;Error=XMP_Unsigned_Mult_Int(result, ®[0], b, PRECISION);return(result);} +// friend Int operator * (digit b, const Int & a) {return(Int(b) * a);} + Int operator / (const Int & divisor) const {Int quotient = *this;XMP_Signed_Div(Remainder, quotient, ®[0], divisor, PRECISION);return (quotient);} + Int operator / (unsigned long b) const {return(*this / Int(b));} + Int operator / (unsigned short divisor) const {Int quotient;Error=XMP_Unsigned_Div_Int(quotient, ®[0], divisor, PRECISION);return(quotient);} +// friend Int operator / (digit a, const Int & b) {return(Int(a) / b);} + Int operator % (const Int & divisor) const {Int remainder;XMP_Signed_Div(remainder, Remainder, ®[0], divisor, PRECISION);return(remainder);} + Int operator % (unsigned long b) const {return(*this % Int(b));} + unsigned short operator % (unsigned short divisor) const {return(XMP_Unsigned_Div_Int(Remainder, ®[0], divisor, PRECISION));} +// friend Int operator % (digit a, const Int & b) {return(Int(a) % b);} + + /* + ** Bitwise binary operators. + */ + Int operator >> (int bits) const {Int result = *this; XMP_Shift_Right_Bits(result, bits, PRECISION);return result;} + Int operator << (int bits) const {Int result = *this; XMP_Shift_Left_Bits(result, bits, PRECISION);return result;} + + /* + ** Comparison binary operators. + */ + int operator == (const Int &b) const {return (memcmp(®[0], &b.reg[0], (MAX_BIT_PRECISION/CHAR_BIT))==0);} + int operator != (const Int& b) const {return !(*this == b);} + int operator > (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) > 0);} + int operator >= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) >= 0);} + int operator < (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) < 0);} + int operator <= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) <= 0);} + + /* + ** Misc. mathematical and logical functions. + */ + void Negate(void) {XMP_Neg(®[0], PRECISION);} + Int Abs(void) {XMP_Abs(®[0], PRECISION);return(*this);} + Int times_b_mod_c(Int const & multiplier, Int const & modulus) const { + Int result; + Error=xmp_stage_modulus(modulus, PRECISION); + Error=XMP_Mod_Mult(result, ®[0], multiplier, PRECISION); + XMP_Mod_Mult_Clear(PRECISION); + return result; + } + + Int exp_b_mod_c(const Int & e, const Int & m) const { + Int result; + Error=xmp_exponent_mod(result, ®[0], e, m, PRECISION); + return result; + } + + + static Int Unsigned_Mult(Int const & multiplicand, Int const & multiplier) {Int product;Error=XMP_Unsigned_Mult(&product.reg[0], &multiplicand.reg[0], &multiplier.reg[0], PRECISION);return(product);} + static void Unsigned_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {Error=XMP_Unsigned_Div(remainder, quotient, dividend, divisor, PRECISION);} + static void Signed_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {XMP_Signed_Div(remainder, quotient, dividend, divisor, PRECISION);} + Int Inverse(const Int & modulus) const {Int result;XMP_Inverse_A_Mod_B(result, ®[0], modulus, PRECISION);return(result);} + + static Int Decode_ASCII(char const * string) {Int result;XMP_Decode_ASCII(string, result, PRECISION);return(result);} + + // Number (sign independand) inserted into buffer. + int Encode(unsigned char *output) const {return(XMP_Encode(output, ®[0], PRECISION));} + int Encode(unsigned char * output, unsigned length) const {return(XMP_Encode(output, length, ®[0], PRECISION));} + void Signed_Decode(const unsigned char * from, int frombytes) {XMP_Signed_Decode(®[0], from, frombytes, PRECISION);} + void Unsigned_Decode(const unsigned char * from, int frombytes) {XMP_Unsigned_Decode(®[0], from, frombytes, PRECISION);} + + // encode Int using Distinguished Encoding Rules, returns size of output + int DEREncode(unsigned char * output) const {return(XMP_DER_Encode(®[0], output, PRECISION));} + void DERDecode(const unsigned char *input) {XMP_DER_Decode(®[0], input, PRECISION);} + + // Friend helper functions. + friend Int Generate_Prime(Straw & rng, int pbits, Int const * = 0); + friend Int Gcd(const Int & a, const Int & b); +// friend bool NextPrime(Int & p, const Int & max, bool blumInt=false); +// friend Int a_exp_b_mod_pq(const Int & a, const Int & ep, const Int & eq, const Int & p, const Int & q, const Int & u); + + static int Error; + + // Carry result from last addition. + static bool Carry; + + // Borrow result from last subtraction. + static bool Borrow; + + // Remainder value from the various division routines. + static Int Remainder; + + + private: + digit reg[PRECISION]; + + + struct RemainderTable + { + RemainderTable(const Int & p) : HasZeroEntry(false) + { + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] = p % primeTable[i]; + } + } + bool HasZero() const {return(HasZeroEntry);} + void Increment(unsigned short increment = 1) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += increment; + while (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + void Increment(const RemainderTable & rtQ) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += rtQ.table[i]; + if (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + + bool HasZeroEntry; + unsigned short table[ARRAY_SIZE(primeTable)]; + }; + +}; + + +template +T Gcd(const T & a, const T & n) +{ + T g[3]={n, a, 0UL}; + + unsigned int i = 1; + while (!!g[i%3]) { + g[(i+1)%3] = g[(i-1)%3] % g[i%3]; + i++; + } + return g[(i-1)%3]; +} + + + +#pragma warning 604 9 +#pragma warning 595 9 +template +T Generate_Prime(Straw & rng, int pbits, T const *) +{ + T minQ = (T(1UL) << (unsigned short)(pbits-(unsigned short)2)); + T maxQ = ((T(1UL) << (unsigned short)(pbits-(unsigned short)1)) - (unsigned short)1); + + T q; + T p; + + do { + q.Randomize(rng, minQ, maxQ); + p = (q*2) + (unsigned short)1; + + T::RemainderTable rtQ(q); + T::RemainderTable rtP(p); + + while (rtQ.HasZero() || rtP.HasZero() || !q.IsPrime() || !p.IsPrime()) { + q += 2; + p += 4; + if (q > maxQ) break; + + rtQ.Increment(2); + rtP.Increment(4); + } + } while (q > maxQ); + + return(p); +} + + + + + + +typedef Int bignum; +typedef Int BigInt; + + + +//BigInt Gcd(const BigInt & a, const BigInt & n); +//BigInt Generate_Prime(RandomNumberGenerator & rng, int pbits, BigInt const * dummy); + +#endif + diff --git a/CODE/INTERNET.CPP b/CODE/INTERNET.CPP new file mode 100644 index 0000000..9e959d3 --- /dev/null +++ b/CODE/INTERNET.CPP @@ -0,0 +1,962 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/INTERNET.CPP 6 3/17/97 1:05a Steve_tall $ */ +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : INTERNET.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Miscellaneous junk related to H2H internet connection. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * Check_From_WChat -- Interprets start game packet from WChat * + * Read_Game_Options -- Read the game setup options from the wchat packet * + * Is_User_WChat_Registered -- retrieve the users wchat entry from registry * + * Spawn_WChat -- spawns or switches focus to wchat * + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "ccdde.h" + + +extern bool SpawnedFromWChat; + +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(void); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +#endif + +/*************************************************************************** +** Internet specific globals +*/ +char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name +char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password +char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player +long PlanetWestwoodPortNumber = 1234; //Port number to send to +bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options +unsigned long PlanetWestwoodGameID; //Game ID +unsigned long PlanetWestwoodStartTime; //Time that game was started +HWND WChatHWND = 0; //Handle to Wchat window. +bool GameStatisticsPacketSent; //Flag that game stats have been sent to wchat +bool ConnectionLost; //Flag that the connection to the other player was lost +int WChatMaxAhead; +int WChatSendRate; +bool SpawnedFromWChat; +int ShowCommand; + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +/*********************************************************************************************** + * Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it. * + * C&CSPAWN.INI is now sent to us by WCHAT via DDE * + * * + * * + * INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 1:44PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +void Check_From_WChat(char *wchat_name) +{ + + char default_string[] = {"Error"}; + char key_string[256]; + char *ini_file; + RawFileClass wchat_file; + + /* + ** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from + ** the DDE server. + */ + if (wchat_name){ + ini_file = new char [8192]; + }else{ + ini_file = DDEServer.Get_MPlayer_Game_Info(); + +#ifdef NEVER + /* + ** Save it to disk as well so I can see it + */ + RawFileClass anotherfile ("FROMCHAT.TXT"); + anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length()); +#endif + + } + + if (wchat_name){ + wchat_file.Set_Name(wchat_name); + } + + if (!wchat_name || wchat_file.Is_Available()){ + + /* + ** Read the ini file from disk if we founf it there + */ + if (wchat_name){ + wchat_file.Read(ini_file, wchat_file.Size()); + } + + /* + ** Get the IP address + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Address", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + strcpy (PlanetWestwoodIPAddress, key_string); + + + + /* + ** Get the port number + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Port", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + PlanetWestwoodPortNumber = atol(key_string); + + + /* + ** Get host or client + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Host", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + if (strchr (key_string, '1')){ + PlanetWestwoodIsHost = true; + }else{ + PlanetWestwoodIsHost = false; + } + + + Special.IsFromWChat = true; + } + + if (wchat_name) delete ini_file; + +} +#endif // !WOLAPI_INTEGRATION + + +/*************************************************************************** + * Read_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * name of C&CSPAWN.INI file. Null if data should be got from DDE server* * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: \ * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(char *name) +{ + char *buffer; + + char filename[256] = {"INVALID.123"}; + + if (name){ + strcpy (filename, name); + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file (filename); + + if (name && !file.Is_Available()) { + return(0); + } else { + if (name){ + buffer = new char [8192]; // INI staging buffer pointer. + memset(buffer, '\0', 8192); + file.Read(buffer, 8192-1); + file.Close(); + }else{ + buffer = DDEServer.Get_MPlayer_Game_Info(); + } + } + + /*------------------------------------------------------------------------ + Get the player's name + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle), buffer); + strcpy(Session.GameName, Session.Handle); + Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); + Session.PrefColor = Session.ColorIdx; + int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); + Session.House = (HousesType) ((int)HOUSE_USSR + temp); + + Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); + Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); + Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); + Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); + Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); + BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); + Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); + Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); + Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + + PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + + Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription), + buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + + if (PlanetWestwoodIsHost){ + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed then force the CD + ** to be there. + ** + */ + if (Session.Options.ScenarioIndex == -1) { + //WWDebugString ("RA95I - Session.Options.ScenarioIndex == -1\n"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) { +#else + if ( Expansion_CS_Present() ) { +#endif + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index=Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + //WWDebugString ("RA95I - CounterStrike CD not in drive\n"); + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + //WWDebugString ("RA95I - Returned from Force_CD_Available()\n"); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + + /* + ** See if that scenario is available now. Its fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + //if (Session.Options.ScenarioIndex == -1) + // WWDebugString ("RA95I - Session.Options.ScenarioIndex is still -1\n"); + + } + } + + + Options.GameSpeed = 0; + + + //MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + + if (name) delete buffer; + return (1); + +} +#endif // !WOLAPI_INTEGRATION + + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + +void Just_Path(char *path, char *destpath) +{ + char *terminator = NULL; //He'll be back. + + strcpy (destpath, path); + terminator = strrchr (destpath, '\\'); + if (terminator){ + *terminator = 0; + } +} + + + + + +/*********************************************************************************************** + * Is_User_WChat_Registered -- retrieve the users wchat entry from the registry * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if users wchat entry was found in the registry * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:13PM ST : Created * + *=============================================================================================*/ +bool Is_User_WChat_Registered(char *buffer, int buffer_len) +{ + HKEY key; + char user_handle[256]; + DWORD user_handle_size = sizeof (user_handle); + char user_pword[256]; + DWORD user_pword_size = sizeof (user_pword); + + + /* + ** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there + */ + key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE); + + if (key){ + key = Get_Registry_Sub_Key (key, "Nick1", TRUE); + if (key){ + + if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){ + + if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){ + + /* + ** If the first char of the users name is non-numberic and there is a password + ** then return success + */ + if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){ + RegCloseKey( key ); + return (TRUE); + } + } + } + } + + RegCloseKey ( key ); + } + + + + /* + ** Check HKEY_LOCAL_MACKINE/Software + */ + user_handle_size = sizeof (user_handle); + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + memcpy (buffer, user_handle, min(buffer_len, user_handle_size)); + + /* + ** If the first char of the users name is non-numeric then return success + */ + if (user_handle[0] < '0' || user_handle[0] > '9'){ + return (TRUE); + }else{ + return (FALSE); + } +} + + + +/*********************************************************************************************** + * Spawn_WChat -- spawns or switches focus to wchat * + * * + * * + * * + * INPUT: can launch. If set then we are allowed to launch WChat if not already running * + * * + * OUTPUT: True if wchat was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Poke_WChat(void); +bool Spawn_WChat(bool can_launch) +{ + WWDebugString ("RA95 - In Spawn_WChat.\n"); + char packet[10] = {"Hello"}; + HWND chat_window = NULL; + + /* + ** See if WChat is already running... + */ + if (WChatHWND && IsWindow (WChatHWND) ){ + chat_window = WChatHWND; + }else{ + chat_window = FindWindow ( "OWL_Window", "Westwood Chat" ); + } + + if (chat_window){ + /* + ** WChat is already running. Minimize myself then try to give it focus. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + + /* + ** Send chat a tickle message so it knows to send the game stats to the server. + */ + if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) { + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + } + + //Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + /* + ** Give the focus to WChat + */ + SetForegroundWindow ( chat_window ); + ShowWindow ( chat_window, SW_RESTORE ); + return(true); + } + + + /* + ** Fail if we aren't allowed to launch wchat and we couldnt find its window. + */ + if (!can_launch) return (false); + + + /* + ** Find where WChat was installed to + */ + HKEY key; + char wchat_loc[256]; + DWORD wchat_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "WChat", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + char justpath [256]; + Just_Path(wchat_loc, justpath); + + /* + ** We found WChat in the registry. Minimize myself then try to spawn it. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + + if (success){ + return (true); + }else{ + ShowWindow (MainWindow, SW_RESTORE); + while ( Keyboard->Check() ) {}; + return (false); + } +} + +#endif //#ifndef WOLAPI_INTEGRATION + +/*********************************************************************************************** + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: True if app was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Spawn_Registration_App(void) +{ + + /* + ** Find where inetreg was installed to + */ + + HKEY key; + char inetreg_loc[256]; + DWORD inetreg_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + char justpath [256]; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + Just_Path(inetreg_loc, justpath); + + BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + if (success){ + //WaitForSingleObject (process_info.hProcess, 1000*10000); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_RESTORE ); + } + return (success); + +} + +#endif //#ifndef WOLAPI_INTEGRATION + + +/*********************************************************************************************** + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 8:30PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Do_The_Internet_Menu_Thang(void) +{ + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + char packet[10] = {"Hello"}; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int width; + int height; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //buttons = &cancelbtn; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + char users_name[256]; + int buffer_len = sizeof (users_name); + bool process; + bool display; + KeyNumType input; + + + if (!Special.IsFromWChat && !SpawnedFromWChat){ + /* + ** If the user is registered with Planet Westwood then spawn WChat. + */ + if (Is_User_WChat_Registered(users_name, buffer_len)){ + GameStatisticsPacketSent = false; + if (!Spawn_WChat(true)){ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + WWMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK); + LogicPage->Clear(); + return(false); + } + }else{ + /* + ** OK, whatever, just run WChat anyway. + */ + if (!Spawn_WChat (true)){ + + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + + //WWMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_CANCEL); + WWMessageBox().Process(TXT_NO_REG_APP, TXT_CANCEL); + } + Load_Title_Page(true); + return(false); + } + } + + /* + ** + ** User is registered and we spawned WChat. Wait for a game start message from WChat. + ** + */ + + process = true; + display = true; + + while (process){ + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored = FALSE; + display = true; + } + + if (display) { + Set_Logic_Page(SeenBuff); + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + //cancelbtn.Zap(); + //buttons = &cancelbtn; + + /* + .................... Rebuild the button list .................... + */ + //buttons->Draw_All(); + cancelbtn.Draw_Me(true); + + Show_Mouse(); + display = false; + } + + + /* + ** See if the game start packet has arrived from wchat yet. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + return(true); + } + + //input = buttons->Input(); + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + } + + } + + return (false); +} + +#endif //#ifndef WOLAPI_INTEGRATION + + + +#endif //WIN32 diff --git a/CODE/INTERNET.H b/CODE/INTERNET.H new file mode 100644 index 0000000..fe3b416 --- /dev/null +++ b/CODE/INTERNET.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/Internet.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 7 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTERNET_H +#define INTERNET_H + +#define IP_ADDRESS_MAX 40 + + +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +int Read_Game_Options(char *name); + +extern char PlanetWestwoodIPAddress[IP_ADDRESS_MAX]; +extern long PlanetWestwoodPortNumber; +extern bool PlanetWestwoodIsHost; + +#endif diff --git a/CODE/INTERPAL.CPP b/CODE/INTERPAL.CPP new file mode 100644 index 0000000..3204eaa --- /dev/null +++ b/CODE/INTERPAL.CPP @@ -0,0 +1,464 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INTERPAL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INTERPAL.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : December 7th 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This module contains functions to allow use of old 320x200 animations on a 640x400 screen * + * * + * Functions: * + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * Write_Interpolation_Palette -- writes an interpolation palette to disk * + * Create_Palette_Interpolation_Table -- build the palette interpolation table * + * Increase_Palette_Luminance -- increase the contrast of a palette * + * Interpolate_2X_Scale -- Stretch a 320x200 graphic buffer into 640x400 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +BOOL InterpolationPaletteChanged = FALSE; +extern "C" { +extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Double (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} + +#define SIZE_OF_PALETTE 256 +extern "C"{ + unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + unsigned char * InterpolationPalette; +} + + + +/*********************************************************************************************** + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Read_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (palette_file.Is_Available()) { + palette_file.Open(READ); + palette_file.Read(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + InterpolationPaletteChanged = FALSE; + } +} + + +/*********************************************************************************************** + * Write_Interpolation_Palette -- writes an interpolation palette table to disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Write_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (!palette_file.Is_Available()) { + palette_file.Open(WRITE); + palette_file.Write(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + } +} + + + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + +// Asm_Create_Palette_Interpolation_Table(); + + #if (1) + + int i; + int j; + int p; + unsigned char * first_palette_ptr; + unsigned char * second_palette_ptr; + unsigned char * match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) InterpolationPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) InterpolationPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) InterpolationPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + PaletteInterpolationTable[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + #endif + InterpolationPaletteChanged = FALSE; + return; + +} + + + + + + + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * cap value for colours * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char * palette , int red_percentage , int green_percentage , int blue_percentage , int cap) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iGet_IsDirectDraw()) { + if (!source->Lock()) { + if (dest == &SeenBuff) Show_Mouse(); + return; + } + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()) { + if (!dest->Lock()) { + if (source_locked) { + source->Unlock(); + } + if (dest == &SeenBuff) Show_Mouse(); + return; + } + dest_locked = TRUE; + } + + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + /* + ** Call the appropriate assembly language copy routine + */ +#if (1) + switch (CopyType) { + case 0: + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 1: + Asm_Interpolate_Line_Double( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 2: + Asm_Interpolate_Line_Interpolate( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + } +#endif + +#if (0) + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = PaletteInterpolationTable[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + last_dest_ptr += dest_width; + dest_ptr = last_dest_ptr; + } + } + +#endif + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + if (dest == &SeenBuff) Show_Mouse(); + +//BG long *longptr = (long *)&PaletteInterpolationTable[0][0]; +//BG Mono_Printf("Clock cycles: %08x\n",*longptr); +} +#endif + + + + + diff --git a/CODE/INTRO.CPP b/CODE/INTRO.CPP new file mode 100644 index 0000000..46ccf7e --- /dev/null +++ b/CODE/INTRO.CPP @@ -0,0 +1,303 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INTRO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +VQAHandle * Open_Movie(char * name); +VQAHandle * Open_Movie(char * name) +{ + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + VQAHandle * vqa = VQA_Alloc(); + if (vqa) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, name, &AnimControl) != 0) { + VQA_Free(vqa); + vqa = 0; + } + } + return(vqa); +} + + +/*********************************************************************************************** + * Choose_Side -- play the introduction movies, select house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 5/08/1995 BWG : Created. * + *=============================================================================================*/ +void Choose_Side(void) // ajw - In RA, all this did was play a movie. Denzil is using it in its original sense. +{ + + Whom = HOUSE_GOOD; + + if (Special.IsFromInstall) { + #ifdef DVD // Denzil + if( Using_DVD() ) + { + Hide_Mouse(); + Load_Title_Page(); + GamePalette = CCPalette; + HidPage.Blit(SeenPage); + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + Show_Mouse(); + + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_SOVIET)) + { + case 0: + CurrentCD = 0; + break; + + case 1: + CurrentCD = 1; + break; + } + + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_SLOW); + SeenPage.Clear(); + } + #endif + + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); + } + +// Scen.ScenPlayer = SCEN_PLAYER_GREECE; + +#ifdef OBSOLETE + static char const _yellowpal[]={0x0,0x0,0xC9,0x0,0xBA,0x0,0x93,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0xEE,0x0}; + static char const _redpal[] ={0x0,0x0,0xA8,0x0,0xD9,0x0,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + static char const _graypal[] ={0x0,0x0,0x17,0x0,0x10,0x0,0x12,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + void * anim; + VQAHandle * gdibrief=0, * nodbrief=0; + void const * staticaud, * oldfont; + void const * speechg, * speechn, * speech; + int statichandle, speechhandle, speechplaying = 0; + int oldfontxspacing = FontXSpacing; + int setpalette = 0; + + int frame = 0, endframe = 255, selection = 0, lettersdone = 0; + + Hide_Mouse(); +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + + Call_Back(); + + staticaud = Load_Alloc_Data(CCFileClass("STRUGGLE.AUD")); + speechg = Load_Alloc_Data(CCFileClass("GDI_SLCT.AUD")); + speechn = Load_Alloc_Data(CCFileClass("NOD_SLCT.AUD")); + +// staticaud = MixFileClass::Retrieve("STRUGGLE.AUD"); +// speechg = MixFileClass::Retrieve("GDI_SLCT.AUD"); +// speechn = MixFileClass::Retrieve("NOD_SLCT.AUD"); + + anim = Open_Animation("CHOOSE.WSA", NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), Palette); + Call_Back(); + + nodbrief = Open_Movie("NOD1PRE.VQA"); + Call_Back(); + gdibrief = Open_Movie("GDI1.VQA"); + + if (Special.IsFromInstall) { + Set_Video_Mode(MCGA_MODE); + PreserveVQAScreen = 1; +// Hide_Mouse(); + Play_Movie("INTRO2", THEME_NONE, false); + Show_Mouse(); + } + + HidPage.Clear(); + if (!Special.IsFromInstall) { + SeenPage.Clear(); +// Set_Palette(Palette); + Palette.Set(); + } else { + setpalette = 1; + } + + statichandle = Play_Sample(staticaud, 255, 64); + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME, 0, 180, _yellowpal)); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME2, 0, 187, _yellowpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_NOD_NAME, 180, 180, _redpal)); + +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 57, 190, _graypal)); +#else +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 103, 194, _graypal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS, 103, 190, _graypal)); +#endif +#endif + Keyboard->Clear(); + + while (endframe != frame || (speechplaying && Is_Sample_Playing(speech)) ) { + Animate_Frame(anim, HidPage, frame++); + Hide_Mouse(); + if (setpalette) { + Wait_Vert_Blank(VertBlank); + //Set_Palette(Palette); + Palette.Set(); + setpalette = 0; + } + HidPage.Blit(SeenPage, 0, 22, 0, 22, 320, 156); + Show_Mouse(); + + if (!Is_Sample_Playing(staticaud)) statichandle = Play_Sample(staticaud, 255, 64); + Call_Back_Delay(3); // delay only if haven't clicked + + /* keep the mouse hidden until the letters are thru printing */ + if (!lettersdone) { + lettersdone = true; + for (int i=0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) lettersdone = 0; + if (lettersdone) { + Show_Mouse(); + } + } + if (frame >= Get_Animation_Frame_Count(anim)) frame = 0; + if (Keyboard->Check() && endframe == 255) { + if ((Keyboard->Get() & 0xFF) == KN_LMOUSE) { + if ((MouseQY > 48) && (MouseQY < 150)) { + if ((MouseQX > 18) && (MouseQX < 148)) { + + // Chose GDI + Whom = HOUSE_GOOD; + ScenPlayer = SCEN_PLAYER_GDI; + endframe = 0; + speechhandle = Play_Sample(speechg); + speechplaying = true; + speech = speechg; + } else if ((MouseQX > 160) && (MouseQX < 300)) { + // Chose Nod + selection = 1; + endframe = 14; + Whom = HOUSE_BAD; + ScenPlayer = SCEN_PLAYER_NOD; + speechhandle = Play_Sample(speechn); + speechplaying = true; + speech = speechn; + } + } + } + } + } + + Hide_Mouse(); + Close_Animation(anim); + + // erase the "choose side" text + SeenBuff.Fill_Rect(0, 180, 319, 199, 0); + + Keyboard->Clear(); + + /* play the scenario 1 briefing movie */ + if (Whom == HOUSE_GOOD) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + if (gdibrief) { +#ifdef CHEAT_KEYS +#else + VQA_Play(gdibrief, VQAMODE_RUN); +#endif + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + } else { + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + if (nodbrief) { +#ifdef CHEAT_KEYS +#else + VQA_Play(nodbrief, VQAMODE_RUN); +#endif + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + } + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + if (Whom == HOUSE_GOOD) { + /* + ** Make sure the screen's fully clear after the movie plays + */ + SeenPage.Clear(); + + BlackPalette.Adjust(WhitePalette, 0x08); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + +// memset(BlackPalette, 0x01, 768); +// Set_Palette(BlackPalette); +// memset(BlackPalette, 0x00, 768); + } else { + PreserveVQAScreen = 1; + } + Free(staticaud); + Free(speechg); + Free(speechn); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; +#endif +} diff --git a/CODE/INTRO.H b/CODE/INTRO.H new file mode 100644 index 0000000..6377122 --- /dev/null +++ b/CODE/INTRO.H @@ -0,0 +1,43 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/INTRO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTRO_H +#define INTRO_H + +void Choose_Side(void); + +#endif diff --git a/CODE/IOMAP.CPP b/CODE/IOMAP.CPP new file mode 100644 index 0000000..b4da699 --- /dev/null +++ b/CODE/IOMAP.CPP @@ -0,0 +1,500 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IOMAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : IOMAP.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : March 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * All map-related loading/saving routines should go in this module, so it can be overlayed. * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * CellClass::Load -- Reads from a save game file. * + * CellClass::Save -- Write to a save game file. * + * CellClass::Should_Save -- Should the cell be written to disk? * + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Load -- Loads from a save game file. * + * MouseClass::Save -- Saves to a save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CellClass::Should_Save -- Should the cell be written to disk? * + * * + * This function will determine if the cell needs to be written to disk. Any cell that * + * contains special data should be written to disk. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should this cell's data be written to disk? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Should_Save(void) const +{ + static CellClass const _identity_cell; + + return(memcmp(&_identity_cell, this, sizeof(*this)) != 0); +} + + +/*********************************************************************************************** + * CellClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Load(Straw & file) +{ + file.Get(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Save(Pipe & file) const +{ + file.Put(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Code_Pointers(void) +{ + if (Cell_Occupier() != NULL) { + OccupierPtr = (ObjectClass *)OccupierPtr->As_Target(); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL && Overlapper[index]->IsActive) { + Overlapper[index] = (ObjectClass *)Overlapper[index]->As_Target(); + } else { + Overlapper[index] = NULL; + } + } +} + + +/*********************************************************************************************** + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Decode_Pointers(void) +{ + if (OccupierPtr != NULL) { + OccupierPtr = As_Object((TARGET)OccupierPtr); + assert(OccupierPtr != NULL); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + Overlapper[index] = As_Object((TARGET)Overlapper[index]); + assert(Overlapper[index] != NULL); + } + } +} + + +/*********************************************************************************************** + * MouseClass::Load -- Loads from a save game file. * + * * + * Loading the map is very complicated. Here are the steps: * + * - Read the Theater for this save-game * + * - call Init_Theater to perform theater-specific inits * + * - call Free_Cells to free the cell array, because loading the map object will overwrite * + * the pointer to the cell array * + * - read the map object from disk * + * - call Alloc_Cells to re-allocate the cell array * + * - call Init_Cells to set the cells to a known state, because not every cell will be loaded * + * - read the cell objects into the cell array * + * - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be * + * called to restore the map's button list to the proper state. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool MouseClass::Load(Straw & file) +{ + /* + ** Load Theater: Even though this value is located in the DisplayClass, + ** it must be loaded first so initialization can be done before any other + ** map data is loaded. If initialization isn't done first, data read from + ** disk will be over-written when initialization occurs. This code must + ** go in the most-derived Map class. + */ + TheaterType theater; + if (file.Get(&theater, sizeof(theater)) != sizeof(theater)) { + return(false); + } + +#ifdef WIN32 +LastTheater = THEATER_NONE; +#endif + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 +// if (theater != LastTheater) { + Reset_Theater_Shapes(); +// } +#endif //WIN32 + + /* + ** Init display mixfiles + */ + Init_Theater(theater); + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + //LastTheater = Scen.Theater; + + /* + ** Free the cell array, because we're about to overwrite its pointers + */ + Free_Cells(); + + /* + ** Read the entire map object in. Only read in sizeof(MouseClass), so if we're + ** in editor mode, none of the map editor object is read in. + */ + file.Get(this, sizeof(*this)); +#ifdef SCENARIO_EDITOR + new(this) MapEditClass(NoInitClass()); +#else + new(this) MouseClass(NoInitClass()); +#endif + + /* + ** Reallocate the cell array + */ + Alloc_Cells(); + + /* + ** Init all cells to empty + */ + Init_Cells(); + + /* + ** Read # cells saved + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Read cells + */ + for (int index = 0; index < count; index++) { + CELL cell = 0; + if (file.Get(&cell, sizeof(cell)) != sizeof(cell)) { + return(false); + } + + if (!(*this)[cell].Load(file)) { + return(false); + } + } + + LastTheater = Scen.Theater; + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Save -- Save to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/26/1996 JLB : Cleaned up. * + *=============================================================================================*/ +bool MouseClass::Save(Pipe & file) const +{ + /* + ** Save Theater >first< + */ + TheaterType theater = Scen.Theater; + file.Put(&theater, sizeof(theater)); + + file.Put(this, sizeof(*this)); + + /* + ** Count how many cells will be saved. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + count++; + } + cellptr++; + } + + /* + ** write out count of the cells. + */ + file.Put(&count, sizeof(count)); + + /* + ** Save cells that need it + */ + cellptr = &(*this)[(CELL)0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + file.Put(&cell, sizeof(cell)); + cellptr->Save(file); + count--; + } + cellptr++; + } + + if (count != 0) return(false); + + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Code_Pointers(void) +{ + /* + ** Code PendingObjectPtr. + */ + if (PendingObjectPtr) { + PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target(); + } + + /* + ** Chain to parent. + */ + MapClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Decode_Pointers(void) +{ + /* + ** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd + ** have to reference PendingObjectPtr->Class_Of(), and the object that + ** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't + ** decode PendingObjectPtr, we can't set the placement cursor shape here + ** either. These have to be done as last-minute fixups. + */ + if (PendingObjectPtr) { + PendingObjectPtr = As_Object((TARGET)PendingObjectPtr); + assert(PendingObjectPtr != NULL); + } + + /* + ** Chain to parent. + */ + MapClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Code_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Code_Pointers(); + cellptr++; + } +} + + +/*********************************************************************************************** + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Decode_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Decode_Pointers(); + cellptr++; + } +} + + diff --git a/CODE/IOOBJ.CPP b/CODE/IOOBJ.CPP new file mode 100644 index 0000000..10a7586 --- /dev/null +++ b/CODE/IOOBJ.CPP @@ -0,0 +1,899 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IOOBJ.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : IOOBJ.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : May 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Load -- Reads from a save game file. * + * LayerClass::Save -- Write to a save game file. * + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * ReinforcementClass::Code_Pointers -- codes class's pointers for load/save * + * ReinforcementClass::Decode_Pointers -- decodes pointers for load/save * + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Code_Pointers(void) +{ + /* + ** Code the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = (TechnoTypeClass *)Members[i].Class->As_Target(); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Decode_Pointers(void) +{ + /* + ** Decode the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = As_TechnoType((TARGET)Members[i].Class); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 05/13/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Code_Pointers(void) +{ + /* + ** Code the 'Member' + */ + if (Member) { + Member = (FootClass *)Member->As_Target(); + } +} + + +/*********************************************************************************************** + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Decode_Pointers(void) +{ + /* + ** Decode the 'Member' + */ + if (Member) { + Member = (FootClass *)As_Techno((TARGET)Member); + assert(Member != NULL); + } +} + + +/*********************************************************************************************** + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Code_Pointers(void) +{ + Action1.Code_Pointers(); + Action2.Code_Pointers(); +} + + +/*********************************************************************************************** + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Decode_Pointers(void) +{ + Action1.Decode_Pointers(); + Action2.Decode_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Code_Pointers(void) +{ + /* + ** Code 'Payback' + */ + if (Payback) { + Payback = (TechnoClass *)Payback->As_Target(); + } + + /* + ** Chain to parent + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Decode_Pointers(void) +{ + /* + ** Decode 'Payback' + */ + if (Payback) { + Payback = As_Techno((TARGET)Payback); + assert(Payback != NULL); + } + + /* + ** Chain to parent + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Code_Pointers(void) +{ + if (Object) { + Object = (TechnoClass *)Object->As_Target(); + } + + ((HouseClass *&)House) = (HouseClass *)House->Class->House; +} + + +/*********************************************************************************************** + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Decode_Pointers(void) +{ + if (Object) { + Object = As_Techno((TARGET)Object); + assert(Object != NULL); + } + + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)House); + assert(House != NULL); +} + + +/*********************************************************************************************** + * LayerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Load(Straw & file) +{ + /* + ** Read # elements in the layer + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Clear the array + */ + Clear(); + + /* + ** Read in all array elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr; + if (file.Get(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) { + return(false); + } + Add(ptr); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Save(Pipe & file) const +{ + /* + ** Save # array elements + */ + int count = Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save all elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr = (*this)[index]; + file.Put(&ptr, sizeof(ObjectClass *)); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Code_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + assert(obj != NULL); + (*this)[index] = (ObjectClass *)(obj->As_Target()); + } +} + + +/*********************************************************************************************** + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Decode_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + TARGET target = (TARGET)(*this)[index]; + (*this)[index] = (ObjectClass *)As_Object(target); + assert((*this)[index] != NULL); + } +} + + +/*********************************************************************************************** + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Decode_Pointers(void) +{ + /* + ** Re-assign the house's remap table (for multiplayer game loads) + ** Loading the house from disk will have over-written the house's RemapTable, so + ** Init_Data() is called to reset it to a valid pointer. + */ + Init_Data(RemapColor, ActLike, Credits); +} + + +/*********************************************************************************************** + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Code_Pointers(void) +{ + RealTime.Stop(); +} + + +/*********************************************************************************************** + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Decode_Pointers(void) +{ + RealTime.Start(); +} + + +/*********************************************************************************************** + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Code_Pointers(void) +{ + if (Member != NULL && Member->IsActive) { + Member = (FootClass *)Member->As_Target(); + } else { + Member = TARGET_NONE; + } + + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Decode_Pointers(void) +{ + if ((TARGET)Member != TARGET_NONE) { + Member = (FootClass *)As_Techno((TARGET)Member); + assert(Member != NULL); + } + + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Code_Pointers(void) +{ + /* + ** Code 'Radio' + */ + if (Radio) { + Radio = (RadioClass *)Radio->As_Target(); + } + + MissionClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Decode_Pointers(void) +{ + /* + ** Decode 'Radio' + */ + if (Radio) { + Radio = As_Techno((TARGET)Radio); + assert(Radio != NULL); + } + + MissionClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Code_Pointers(void) +{ + CargoClass::Code_Pointers(); + RadioClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Decode_Pointers(void) +{ + CargoClass::Decode_Pointers(); + RadioClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Code_Pointers(void) +{ + /* + ** Code 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)CargoHold->As_Target(); + } +} + + +/*********************************************************************************************** + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Decode_Pointers(void) +{ + /* + ** Decode 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)As_Techno((TARGET)CargoHold); + assert(CargoHold != NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Code_Pointers(void) +{ + if (Next) { + Next = (ObjectClass *)Next->As_Target(); + } +} + + +/*********************************************************************************************** + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Decode_Pointers(void) +{ + if (Next) { + Next = As_Object((TARGET)Next); + assert(Next != NULL); + } +} + diff --git a/CODE/IPX.CPP b/CODE/IPX.CPP new file mode 100644 index 0000000..5bcc666 --- /dev/null +++ b/CODE/IPX.CPP @@ -0,0 +1,1164 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPX.CPP * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 15, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Pitfalls: * + * - Never try to use a closed socket; always check the return code from * + * IPX_Open_Socket(). * + * - Always give IPX an outstanding ECB for listening, before you send. * + * - It turns out that IPX is pretty bad about saving registers, so if * + * you have any register variables in your program, they may get * + * trashed. To circumvent this, all functions in this module save & * + * restore the registers before invoking any IPX or NETX function. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * IPX_Close_Socket -- closes an open socket * + * IPX_Get_Connection_Number -- gets local Connection Number * + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * IPX_Get_User_ID -- gets user ID from Connection Number * + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * IPX_Send_Packet -- commands IPX to send a packet * + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * IPX_Cancel_Event -- cancels an operation in progress * + * Let_IPX_Breath -- gives IPX some CPU time * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include +#include "ipx.h" + +#ifdef WIN32 +#include "ipx95.h" +#endif //WIN32 + + +// Turn off "expression is not meaningful". +#pragma warning 628 9 + + +/*************************************************************************** + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1994 BR : Created. * + *=========================================================================*/ +int IPX_SPX_Installed(void) +{ +#ifdef WIN32 + +#ifdef TIBERIAN_SUN + return(false); +#else + if ( Load_IPX_Dll () ){ + return ( IPX_Initialise() ); + }else{ + return(false); + } +#endif + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Init all registers to 0's + //------------------------------------------------------------------------ + memset (®s, 0, sizeof(regs)); + segread (&sregs); + memset (&rmi, 0, sizeof(rmi)); + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call, function 0x300 + //------------------------------------------------------------------------ + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x002f; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the real-mode interrupt handler. + // To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke + // interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, + // AL will be 0xff, and ES:DI will contain the IPX/SPX function address. + //------------------------------------------------------------------------ + rmi.eax = 0x00007a00; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // If IPX isn't there, return 0 + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) != 0xff) { + return(0); + } + + //------------------------------------------------------------------------ + // Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. + // If SPX is present, AL will be 0xff. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = 0x00000010; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // SPX is installed; return '2' + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) == 0xff) { + return(2); + } + + //------------------------------------------------------------------------ + // SPX is not installed; return '1' + //------------------------------------------------------------------------ + return(1); +#endif //WIN32 + +} /* end of IPX_SPX_Installed */ + + +/*************************************************************************** + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * * + * INPUT: * + * socket the socket number to open * + * * + * OUTPUT: * + * 0 = OK * + * -1 = IPX not installed * + * 0xfe = socket table is full * + * 0xff = socket is already open * + * * + * WARNINGS: * + * The application must define its socket number carefully. Use * + * values from 0x4000 to 0x8000 for custom socket numbers. The app * + * must know its own socket number as well as the socket number of * + * a destination workstation. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ + +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Open_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int rc; + + //------------------------------------------------------------------------ + // Open the socket: + // DX = socket number + // AL = 0 for short-lived socket, 0xff for long-lived socket + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF (&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_OPEN_SOCKET; // function code + rmi.edx = socket; // desired socket # + rmi.eax = 0x00ff; // make this a long-lived socket + + //........................................................................ + // call DPMI + //........................................................................ + int386x (DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0xff); + + return(rc); + +} /* end of IPX_Open_Socket */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Close_Socket -- closes an open socket * + * * + * INPUT: * + * socket socket number to close * + * * + * OUTPUT: * + * 0 = ok, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Close_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Close the socket: + // DX = socket number + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_CLOSE_SOCKET; + rmi.edx = socket; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(0); + +} /* end of IPX_Close_Socket */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_Connection_Number -- gets local Connection Number * + * * + * This Novell call will the return the user's local "Connection Number". * + * This value will be 0 if the user isn't logged into Novell, so this * + * routine can be used to detect if other calls (such as Get_Local_Target) * + * will be OK. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Connection Number, 0 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Connection_Number(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int num; + + //------------------------------------------------------------------------ + // Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local + // connection number into AL. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000dc00; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + num = rmi.eax & 0x00ff; + + return(num); + +} /* end of IPX_Get_Connection_Number */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * * + * This gets the Connection Number for the given User ID. Since a user * + * may be logged in more than once, this just returns the first connection * + * found and ignores the others. * + * * + * INPUT: * + * username name of the user to get the Connection Number for * + * * + * OUTPUT: * + * first-found Connection Number for that user, 0 if user not logged in * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_1st_Connection_Num (char * username) +{ + struct request_buffer { + unsigned short len; // username length + 5 + unsigned char buffer_type; // ConnectionNum = 0x15 + unsigned short object_type; // set ot 0x0100 + unsigned char name_len; // length of username + char name [48]; // copy of username + unsigned short reserved; + }; + struct reply_buffer { + unsigned short len; + unsigned char number_connections; // will be 0 - 100 + unsigned char connection_num [100]; // array of connection numbers + unsigned short reserved[2]; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + int num_conns; // # connections returned + int conn_num; // connection number + int rc; + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = (unsigned short)(strlen(username) + 5); + reqbuf->buffer_type = 0x15; + reqbuf->object_type = 0x0100; + reqbuf->name_len = (unsigned char) strlen(username); + strcpy(reqbuf->name, username); + reqbuf->reserved = reqbuf->reserved; // prevent compiler warning + replybuf->len = 101; + replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning + replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Stash the 1st connection number + //------------------------------------------------------------------------ + rc = (rmi.eax & 0x00ff); // if AL !=0, error + num_conns = replybuf->number_connections; // # times user is logged in + conn_num = (int )replybuf->connection_num[0]; // 1st connection # + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // Return error if function failed, or user not logged in + //------------------------------------------------------------------------ + if (rc != 0 || num_conns==0) { + return(0); + } + else { + return(conn_num); + } + +} /* end of IPX_Get_1st_Connection_Num */ + +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * * + * Once you've obtained a Connection Number from IPX_Get_Connection_Number * + * or IPX_Get_1st_Connection_Num, use this function to translate it into * + * a Network Number and Node Address; then, place those numbers in the * + * IPX header for outgoing packets. * + * * + * INPUT: * + * connection_number Connection Number to translate * + * network_number ptr: will hold Network Number * + * physical_node ptr: will hold Node Address * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * If connection_number is 0 and NETX isn't running, this routine * + * will just put garbage into the network_number and physical_node. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Internet_Address(int connection_number, + unsigned char * network_number, unsigned char * physical_node) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // Internet = 0x13 + unsigned char connection_number; // Conn. Number to translate + }; + struct reply_buffer { + unsigned short len; + unsigned char network_number [4]; // filled in by IPX + unsigned char physical_node [6]; // filled in by IPX + unsigned short server_socket; // filled in by IPX, but don't use! + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x13; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = 12; + replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning + replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning + replybuf->server_socket = replybuf->server_socket; // suppress warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + memcpy(network_number, replybuf->network_number, 4); + memcpy(physical_node, replybuf->physical_node, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_Internet_Address */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_User_ID -- gets user ID from Connection Number * + * * + * INPUT: * + * connection_number Connection Number to get User ID for * + * user_id ptr to buffer to put User ID into; * + * size must be >= 48 chars * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_User_ID(int connection_number, char * user_id) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // 0x16 = UserID buffer type + unsigned char connection_number; // Connection Number to get ID for + }; + struct reply_buffer { + unsigned short len; + unsigned char object_id[4]; + unsigned char object_type[2]; + char object_name[48]; + char login_time[7]; + unsigned short reserved; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x16; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = sizeof(struct reply_buffer) - 2; + replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings + replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings + replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings + replybuf->reserved = replybuf->reserved; // suppress warnings + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Fill in the caller's buffer with the user name + //------------------------------------------------------------------------ + strncpy(user_id, replybuf->object_name, 48); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_User_ID */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are "listening" on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas to * + * store the incoming data in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of data buffer, for the packet * + * Packet[1].Length: size of the data buffer * + * * + * When the packet is received, ECBType.CompletionCode will be 0 if * + * successful. Otherwise, some error occurred. * + * * + * You should initialize the ECB to 0's before filling it in. * + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * + * * + * OUTPUT: * + * 0 = OK, IPX error otherwise * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Listen_For_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_LISTEN_FOR_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(rmi.eax & 0x00ff); + +} /* end of IPX_Listen_For_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Send_Packet -- commands IPX to send a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are sending on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas the * + * outgoing data is stored in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of buffer containing data to send * + * Packet[1].Length: size of the data buffer * + * ImmediateAddress: must be filled in with the node address of * + * the bridge that will route the message; * + * fill this in by calling IPX_Get_Local_Target * + * * + * Also, you must fill in the IPXHeaderType with certain values: * + * PacketType: set to 4 for IPX * + * DestNetworkNumber: Network Number of the destination system * + * DestNetworkNode: Node Address of the destination system * + * DestNetworkSocket: the destination system's socket to send to; * + * this doesn't have to be the same as the * + * socket opened on the local machine. * + * * + * You should initialize the ECB & IPXHeader to 0's before filling them in.* + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB * + * * + * OUTPUT: * + * none. This function doesn't return anything; the caller must check * + * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void IPX_Send_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_SEND_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of IPX_Send_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * * + * Use this function to fill in the ECB's ImmediateAddress field when * + * sending a packet. The Immediate Address is the node address of the * + * bridge that must route the message. If there is no bridge, it's * + * filled in with the destination Node Address. In either case, it * + * will contain the proper value for sending. * + * * + * INPUT: * + * dest_network destination Network Number * + * dest_node destination Node Address * + * dest_socket destination Socket Number * + * bridge_address field to fill in with Immediate Address * + * * + * OUTPUT: * + * 0 = OK, error otherwise * + * * + * WARNINGS: * + * If the Connection Number is 0 (user not logged in), dest_network * + * and dest_node will be garbage, and this routine will probably crash. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + struct request_buffer { + unsigned char network_number [4]; + unsigned char physical_node [6]; + unsigned short socket; + }; + struct reply_buffer { + unsigned char local_target [6]; + }; + unsigned int rc; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + memcpy(reqbuf->network_number, dest_network, 4); + memcpy(reqbuf->physical_node, dest_node, 6); + reqbuf->socket = dest_socket; + + //------------------------------------------------------------------------ + // Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_GET_LOCAL_TARGET; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + memcpy(bridge_address, replybuf->local_target, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(rc); + +} /* end of IPX_Get_Local_Target */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Cancel_Event -- cancels an operation in progress * + * * + * INPUT: * + * ecb_ptr pointer to ECB event to cancel * + * * + * OUTPUT: * + * ??? * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Cancel_Event(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + unsigned int rc; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_CANCEL_EVENT; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + + return(rc); + +} /* end of IPX_Cancel_Event */ +#endif //WIN32 + +/*************************************************************************** + * Let_IPX_Breath -- gives IPX some CPU time * + * * + * Use this function if you're polling the ECB's InUse flag, waiting * + * for it to go to 0: * + * * + * while ECBType.InUse * + * Let_IPX_Breath(); * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void Let_IPX_Breath(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_RELINQUISH_CONTROL; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of Let_IPX_Breath */ +#endif //WIN32 +/**************************** end of ipx.cpp *******************************/ diff --git a/CODE/IPX.H b/CODE/IPX.H new file mode 100644 index 0000000..358eb59 --- /dev/null +++ b/CODE/IPX.H @@ -0,0 +1,191 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPX.H * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 14, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPX_H +#define IPX_H + +/* +******************************** Structures ********************************* +*/ +typedef unsigned char NetNumType[4]; +typedef unsigned char NetNodeType[6]; +typedef char UserID[48]; + +/*--------------------------------------------------------------------------- +This is the IPX Packet structure. It's followed by the data itself, which +can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +field; annotation of 'APP' means the application must set the field. +NOTE: All header fields are ordered high-byte,low-byte. +---------------------------------------------------------------------------*/ +typedef struct IPXHEADER { + unsigned short CheckSum; // IPX: Not used; always 0xffff + unsigned short Length; // IPX: Total size, incl header & data + unsigned char TransportControl; // IPX: # bridges message crossed + unsigned char PacketType; // APP: Set to 4 for IPX (5 for SPX) + unsigned char DestNetworkNumber[4]; // APP: destination Network Number + unsigned char DestNetworkNode[6]; // APP: destination Node Address + unsigned short DestNetworkSocket; // APP: destination Socket Number + unsigned char SourceNetworkNumber[4]; // IPX: source Network Number + unsigned char SourceNetworkNode[6]; // IPX: source Node Address + unsigned short SourceNetworkSocket; // IPX: source Socket Number +} IPXHeaderType; + +/*--------------------------------------------------------------------------- +This is the IPX Event Control Block. It serves as a communications area +between IPX and the application for a single IPX operation. You should set +up a separate ECB for each IPX operation you perform. +---------------------------------------------------------------------------*/ +typedef struct ECB { + void *Link_Address; + void (*Event_Service_Routine)(void); // APP: event handler (NULL=none) + unsigned char InUse; // IPX: 0 = event complete + unsigned char CompletionCode; // IPX: event's return code + unsigned short SocketNumber; // APP: socket to send data through + unsigned short ConnectionID; // returned by Listen (???) + unsigned short RestOfWorkspace; + unsigned char DriverWorkspace[12]; + unsigned char ImmediateAddress[6]; // returned by Get_Local_Target + unsigned short PacketCount; + struct { + void *Address; + unsigned short Length; + } Packet[2]; +} ECBType; + + +/*--------------------------------------------------------------------------- +This structure is used for calling DPMI function 0x300, Call-Real-Mode- +Interrupt. It passes register values to & from the interrupt handler. +---------------------------------------------------------------------------*/ +typedef struct { + long edi; + long esi; + long ebp; + long Reserved; + long ebx; + long edx; + long ecx; + long eax; + short Flags; + short es; + short ds; + short fs; + short gs; + short ip; + short cs; + short sp; + short ss; +} RMIType; + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +These defines are for the IPX functions. The function number is specified +by placing it in BX when IPX is called. There are two ways to invoke IPX: +use interrupt 0x7a, or use a function whose address is obtained by calling +interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +This is the preferred method, since other apps are known to use int 0x7a. +---------------------------------------------------------------------------*/ +#define IPX_OPEN_SOCKET 0x0000 +#define IPX_CLOSE_SOCKET 0x0001 +#define IPX_GET_LOCAL_TARGET 0x0002 +#define IPX_SEND_PACKET 0x0003 +#define IPX_LISTEN_FOR_PACKET 0x0004 +#define IPX_SCHEDULE_EVENT 0x0005 +#define IPX_CANCEL_EVENT 0x0006 +#define IPX_GET_INTERVAL_MARKER 0x0008 +#define IPX_GET_INTERNETWORK_ADDRESS 0x0009 +#define IPX_RELINQUISH_CONTROL 0x000A +#define IPX_DISCONNECT_FROM_TARGET 0x000B + +/*--------------------------------------------------------------------------- +These defines are for various IPX error codes: +---------------------------------------------------------------------------*/ +#define IPXERR_CONNECTION_SEVERED 0x00ec +#define IPXERR_CONNECTION_FAILED 0x00ed +#define IPXERR_NO_CONNECTION 0x00ee +#define IPXERR_CONNECTION_TABLE_FULL 0x00ef +#define IPXERR_NO_CANCEL_ECB 0x00f9 +#define IPXERR_NO_PATH 0x00fa +#define IPXERR_ECB_INACTIVE 0x00fc +#define IPXERR_INVALID_PACKET_LENGTH 0x00fd +#define IPXERR_SOCKET_TABLE_FULL 0x00fe +#define IPXERR_SOCKET_ERROR 0x00ff + +/*--------------------------------------------------------------------------- +These defines are for various interrupt vectors and DPMI functions: +---------------------------------------------------------------------------*/ +#define IPX_INT 0x007a +#define DPMI_INT 0x0031 +#define DPMI_ALLOC_DOS_MEM 0x0100 +#define DPMI_FREE_DOS_MEM 0x0101 +#define DPMI_CALL_REAL_INT 0x0300 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/* +******************************** Prototypes ********************************* +*/ +/*--------------------------------------------------------------------------- +NOTE: All routines return 0 for a success code and one of the above IPX +error codes if there's an error, EXCEPT: +- IPX_SPX_Installed (0 = not installed) +- Get_Connection_Number / Get_1st_Connection_Number (0 = no connection) +---------------------------------------------------------------------------*/ +/* +.................................. ipx.cpp .................................. +*/ +int IPX_SPX_Installed(void); +int IPX_Open_Socket(unsigned short socket); +int IPX_Close_Socket(unsigned short socket); +int IPX_Get_Connection_Number(void); +int IPX_Get_1st_Connection_Num (char *username); +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node); +int IPX_Get_User_ID(int connection_number, char *user_id); +int IPX_Listen_For_Packet(struct ECB *ecb_ptr); +void IPX_Send_Packet(struct ECB *ecb_ptr); +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address); +int IPX_Cancel_Event(struct ECB *ecb_ptr); +void Let_IPX_Breath(void); + +#endif + +/***************************** end of ipx.h ********************************/ + diff --git a/CODE/IPX95.CPP b/CODE/IPX95.CPP new file mode 100644 index 0000000..60e7d59 --- /dev/null +++ b/CODE/IPX95.CPP @@ -0,0 +1,201 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : July 10th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Windows 95 equivelent functions from IPX.CPP * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 + +#include "ipx95.h" + +/* +** Instance handle for the THIPX32 .DLL +*/ +HINSTANCE IpxDllInstance = NULL; + +/* +** Function pointers +*/ +//extern "C" { + IPXInitialiseType IPX_Initialise = NULL; + IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95 = NULL; + IPXShutDown95Type IPX_Shut_Down95 = NULL; + IPXSendPacket95Type IPX_Send_Packet95 = NULL; + IPXBroadcastPacket95Type IPX_Broadcast_Packet95 = NULL; + IPXStartListening95Type IPX_Start_Listening95 = NULL; + IPXOpenSocket95Type IPX_Open_Socket95 = NULL; + IPXCloseSocket95Type IPX_Close_Socket95 = NULL; + IPXGetConnectionNumber95Type IPX_Get_Connection_Number95 = NULL; + IPXGetLocalTarget95 IPX_Get_Local_Target95 = NULL; +//} + +char const *FunctionNames[] = { + "_IPX_Initialise", + "_IPX_Get_Outstanding_Buffer95", + "_IPX_Shut_Down95", + "_IPX_Send_Packet95", + "_IPX_Broadcast_Packet95", + "_IPX_Start_Listening95", + "_IPX_Open_Socket95", + "_IPX_Close_Socket95", + "_IPX_Get_Connection_Number95", + "_IPX_Get_Local_Target95", + NULL +}; + +extern void Get_OS_Version (void); +bool WindowsNT = false; + + +/*********************************************************************************************** + * Load_IPX_Dll -- Loads the THIPX32 DLL * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if DLL loaded * + * * + * WARNINGS: This call will fail under NT due to a side effect of loading the THIPX32.DLL * + * which causes the 16 bit DLL THIPX16.DLL to load. * + * * + * HISTORY: * + * 4/1/97 11:40AM ST : Created * + *=============================================================================================*/ +bool Load_IPX_Dll (void) +{ + Get_OS_Version(); + if (WindowsNT) return (false); + + SetErrorMode( SEM_NOOPENFILEERRORBOX ); + IpxDllInstance = LoadLibrary ( "THIPX32.DLL" ); + SetErrorMode ( 0 ); + + if ( IpxDllInstance ){ + + const char *function_name; + unsigned long *fptr = (unsigned long *) &IPX_Initialise; + int count = 0; + + do { + function_name = FunctionNames[count]; + if (function_name){ + *fptr = (unsigned long) GetProcAddress (IpxDllInstance, function_name); + assert (*fptr != NULL); + fptr++; + count++; + } + + } while ( function_name ); + + return (true); + + }else{ + return (false); + } +} + + + +/*********************************************************************************************** + * Unload_IPX_Dll -- Frees the THIPX32 library * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 2:37PM ST : Created * + *=============================================================================================*/ +void Unload_IPX_Dll (void) +{ + if (IpxDllInstance){ + FreeLibrary (IpxDllInstance); + IpxDllInstance = NULL; + } +} + + +int IPX_Open_Socket(unsigned short socket) +{ + return ( IPX_Open_Socket95((int)socket)); +} + +int IPX_Close_Socket(unsigned short socket) +{ + IPX_Close_Socket95((int)socket); + return (0); +} + + +int IPX_Get_Connection_Number(void) +{ + return (IPX_Get_Connection_Number95()); +} + + + +int IPX_Broadcast_Packet(unsigned char * buf, int buflen) +{ + return(IPX_Broadcast_Packet95(buf, buflen)); +} + +extern "C"{ + extern void __cdecl Int3(void); +} + +int IPX_Get_Local_Target(unsigned char * dest_network, unsigned char * dest_node, + unsigned short dest_socket, unsigned char * bridge_address) +{ + //Int3(); + return (IPX_Get_Local_Target95(dest_network, dest_node, dest_socket, bridge_address)); +} + + +#endif //WIN32 diff --git a/CODE/IPX95.H b/CODE/IPX95.H new file mode 100644 index 0000000..1e90b83 --- /dev/null +++ b/CODE/IPX95.H @@ -0,0 +1,102 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** Types for function pointers +*/ +typedef BOOL __stdcall (*IPXInitialiseType) (void); +typedef BOOL __stdcall (*IPXGetOutstandingBuffer95Type) (unsigned char*); +typedef void __stdcall (*IPXShutDown95Type) (void); +typedef int __stdcall (*IPXSendPacket95Type) (unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); +typedef int __stdcall (*IPXBroadcastPacket95Type) (unsigned char *, int); +typedef BOOL __stdcall (*IPXStartListening95Type) (void); +typedef int __stdcall (*IPXOpenSocket95Type) (int); +typedef void __stdcall (*IPXCloseSocket95Type) (int); +typedef int __stdcall (*IPXGetConnectionNumber95Type) (void); +typedef int __stdcall (*IPXGetLocalTarget95) (unsigned char *, unsigned char*, unsigned short, unsigned char*); + + + +/* +** Function pointers +*/ +//extern "C"{ + extern IPXInitialiseType IPX_Initialise; + extern IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95; + extern IPXShutDown95Type IPX_Shut_Down95; + extern IPXSendPacket95Type IPX_Send_Packet95; + extern IPXBroadcastPacket95Type IPX_Broadcast_Packet95; + extern IPXStartListening95Type IPX_Start_Listening95; + extern IPXOpenSocket95Type IPX_Open_Socket95; + extern IPXCloseSocket95Type IPX_Close_Socket95; + extern IPXGetConnectionNumber95Type IPX_Get_Connection_Number95; + extern IPXGetLocalTarget95 IPX_Get_Local_Target95; +//} + +/* +** Functions +*/ +bool Load_IPX_Dll (void); +void Unload_IPX_Dll (void); + + +#if (0) +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void); + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); + extern void __stdcall IPX_Shut_Down95(void); + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); + extern BOOL __stdcall IPX_Start_Listening95(void); + extern int __stdcall IPX_Open_Socket95(int socket); + extern void __stdcall IPX_Close_Socket95(int socket); + extern int __stdcall IPX_Get_Connection_Number95(void); + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*); +} +#endif //(0) + +extern bool WindowsNT; diff --git a/CODE/IPXADDR.CPP b/CODE/IPXADDR.CPP new file mode 100644 index 0000000..2f80ef6 --- /dev/null +++ b/CODE/IPXADDR.CPP @@ -0,0 +1,508 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXADDR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXADDR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXAddressClass::IPXAddressClass -- class constructor * + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * IPXAddressClass::Set_Address -- sets the IPX address values * + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * IPXAddressClass::operator== -- overloaded comparison operator * + * IPXAddressClass::operator!= -- overloaded comparison operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include "ipxaddr.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#endif //WINSOCK_IPX + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor * + * * + * This default constructor generates a broadcast address. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(void) +{ + NetworkNumber[0] = 0xff; + NetworkNumber[1] = 0xff; + NetworkNumber[2] = 0xff; + NetworkNumber[3] = 0xff; + NodeAddress[0] = 0xff; + NodeAddress[1] = 0xff; + NodeAddress[2] = 0xff; + NodeAddress[3] = 0xff; + NodeAddress[4] = 0xff; + NodeAddress[5] = 0xff; + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber, net, 4); + memcpy(NodeAddress, node, 6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * * + * This form of the constructor takes an IPX header as an argument. It * + * extracts the address from the Source address fields in the header. * + * * + * INPUT: * + * header Header from which to extract the address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(IPXHeaderType *header) +{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * * + * This routine extracts the source addresses from the given IPX header. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(IPXHeaderType *header) +{ +#ifdef WINSOCK_IPX + ProtocolEnum protocol = PROTOCOL_IPX; + if ( PacketTransport ) protocol = PacketTransport->Get_Protocol(); + + switch ( protocol ) { + + case PROTOCOL_IPX: + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + break; + + case PROTOCOL_UDP: + unsigned char *addr = (unsigned char*) header; + memset (NodeAddress, 0, 6); + memcpy (NodeAddress, addr, 4); + memset (NetworkNumber, 0, 4); + break; + } +#else //WINSOCK_IPX + + if (header) { + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + } else { + /* + ** Address is meaningless when using winsock + */ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 1, 6); + } + +#endif //WINSOCK_IPX + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(NetNumType net, NetNodeType node) +{ + memcpy(net,NetworkNumber,4); + memcpy(node,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(IPXHeaderType *header) +{ + memcpy(header->DestNetworkNumber,NetworkNumber,4); + memcpy(header->DestNetworkNode,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::Is_Broadcast(void) +{ + if ( NetworkNumber[0] == 0xff && + NetworkNumber[1] == 0xff && + NetworkNumber[2] == 0xff && + NetworkNumber[3] == 0xff && + NodeAddress[0] == 0xff && + NodeAddress[1] == 0xff && + NodeAddress[2] == 0xff && + NodeAddress[3] == 0xff && + NodeAddress[4] == 0xff && + NodeAddress[5] == 0xff) { + return(1); + } + else { + return(0); + } + +} /* end of Is_Broadcast */ + + +/*************************************************************************** + * IPXAddressClass::operator== -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = not equal, 1 = equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator == (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(1); + } + else { + return(0); + } + + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(1); + } + else { + return(0); + } + } + +} /* end of operator== */ + + +/*************************************************************************** + * IPXAddressClass::operator!= -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = equal, 1 = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator != (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(0); + } + else { + return(1); + } + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(0); + } + else { + return(1); + } + } + +} /* end of operator!= */ + + +/*************************************************************************** + * IPXAddressClass::operator > -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator > (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) > 0); + +} /* end of operator> */ + + +/*************************************************************************** + * IPXAddressClass::operator < -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator < (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) < 0); + +} /* end of operator< */ + + +/*************************************************************************** + * IPXAddressClass::operator >= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator >= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) >= 0); + +} /* end of operator>= */ + + +/*************************************************************************** + * IPXAddressClass::operator <= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator <= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) <= 0); + +} /* end of operator<= */ + +/************************** end of ipxaddr.cpp *****************************/ diff --git a/CODE/IPXADDR.H b/CODE/IPXADDR.H new file mode 100644 index 0000000..e14fc2a --- /dev/null +++ b/CODE/IPXADDR.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXADDR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXADDR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + * This class is useful for any IPX-related code. It's just a utility * + * to help manage those annoying IPX address fields. This class lets you * + * compare addresses, copy addresses to & from the IPX header, etc. * + * * + * The class has no virtual functions, so you can treat this class just * + * like a data structure; it can be loaded & saved, and even transmitted * + * across the net. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXADDR_H +#define IPXADDR_H + +#include "ipx.h" // for NetNumType & NetNodeType + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXAddressClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructors: + .....................................................................*/ + IPXAddressClass(void); + IPXAddressClass(NetNumType net, NetNodeType node); + IPXAddressClass(IPXHeaderType *header); + + /*..................................................................... + Set the address from explicit variables, or from the SOURCE values + in an IPX packet header. + .....................................................................*/ + void Set_Address(NetNumType net, NetNodeType node); + void Set_Address(IPXHeaderType *header); + /*..................................................................... + Get the address values explicitly, or copy them into the DESTINATION + values in an IPX packet header. + .....................................................................*/ + void Get_Address (NetNumType net, NetNodeType node); + void Get_Address(IPXHeaderType *header); + + /*..................................................................... + Tells if this address is a broadcast address + .....................................................................*/ + int Is_Broadcast(void); + + /*..................................................................... + Overloaded operators: + .....................................................................*/ + int operator == (IPXAddressClass & addr); + int operator != (IPXAddressClass & addr); + int operator > (IPXAddressClass &addr); + int operator < (IPXAddressClass &addr); + int operator >= (IPXAddressClass &addr); + int operator <= (IPXAddressClass &addr); + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /* + --------------------------- Private Interface ---------------------------- + */ + private: + NetNumType NetworkNumber; + NetNodeType NodeAddress; +}; + +#endif + +/**************************** end of ipxaddr.h *****************************/ diff --git a/CODE/IPXCONN.CPP b/CODE/IPXCONN.CPP new file mode 100644 index 0000000..bec768f --- /dev/null +++ b/CODE/IPXCONN.CPP @@ -0,0 +1,877 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXCONN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXConnClass::IPXConnClass -- class constructor * + * IPXConnClass::~IPXConnClass -- class destructor * + * IPXConnClass::Init -- hardware-specific initialization routine * + * IPXConnClass::Configure -- One-time initialization routine * + * IPXConnClass::Start_Listening -- commands IPX to listen * + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * IPXConnClass::Open_Socket -- opens communications socket * + * IPXConnClass::Close_Socket -- closes the socket * + * IPXConnClass::Send_To -- sends the packet to the given address * + * IPXConnClass::Broadcast -- broadcasts the given packet * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include +#include +#include +#include "ipxconn.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" + +#else + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 +#endif //WINSOCK_IPX + + + +/* +********************************* Globals *********************************** +*/ +unsigned short IPXConnClass::Socket; +int IPXConnClass::ConnectionNum; +ECBType * IPXConnClass::ListenECB; +IPXHeaderType * IPXConnClass::ListenHeader; +char * IPXConnClass::ListenBuf; +ECBType * IPXConnClass::SendECB; +IPXHeaderType * IPXConnClass::SendHeader; +char * IPXConnClass::SendBuf; +long IPXConnClass::Handler; +int IPXConnClass::Configured = 0; +int IPXConnClass::SocketOpen = 0; +int IPXConnClass::Listening = 0; +int IPXConnClass::PacketLen; + + +/*************************************************************************** + * IPXConnClass::IPXConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * address address of destination (NULL = no address) * + * id connection's unique numerical ID * + * name connection's name * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXConnClass::IPXConnClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 2, // retry delta + -1, // max retries + 60, // timeout + extralen) // (currently, this is only used by the Global Channel) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + if (address) + Address = (*address); + ID = id; + strcpy (Name, name); + +#ifdef WINSOCK_IPX + Address.Get_Address(net,node); + memcpy(ImmediateAddress,node,6); + Immed_Set = 0; +#else + if ( !Winsock.Get_Connected() ) { + /*------------------------------------------------------------------------ + If our Address field is an actual address (ie NULL wasn't passed to the + constructor), pre-compute the ImmediateAddress value for the SendECB. + This allows pre-computing of the ImmediateAddress for all connections + created after Configure() is called. + ------------------------------------------------------------------------*/ + if (!Address.Is_Broadcast() && Configured==1) { + Address.Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + if (IPX_Get_Local_Target (net, node, Socket, ImmediateAddress)!=0) { + memcpy(ImmediateAddress,node,6); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(ImmediateAddress,node,6); + } + + Immed_Set = 1; + } + else { + memset (ImmediateAddress, 0, 6); + Immed_Set = 0; + } + } +#endif //WINSOCK_IPX +} /* end of IPXConnClass */ + + +/*************************************************************************** + * IPXConnClass::Init -- hardware-specific initialization routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Init (void) +{ + /*------------------------------------------------------------------------ + Invoke the parent's Init routine + ------------------------------------------------------------------------*/ + ConnectionClass::Init(); + +} /* end of Init */ + + +/*************************************************************************** + * IPXConnClass::Configure -- One-time initialization routine * + * * + * This routine sets up static members that are shared by all IPX * + * connections (ie those variables used by the Send/Listen/Broadcast * + * routines). * + * * + * INPUT: * + * socket socket ID for sending & receiving * + * conn_num local IPX Connection Number (0 = not logged in) * + * listen_ecb ptr to ECBType for listening * + * send_ecb ptr to ECBType for sending * + * listen_header ptr to IPXHeaderType for listening * + * send_header ptr to IPXHeaderType for sending * + * listen_buf ptr to buffer for listening * + * send_buf ptr to buffer for sending * + * handler_rm_ptr REAL-MODE pointer to event service routine * + * (high word = segment, low word = offset) * + * maxpacketlen max packet size to listen for * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - All pointers must be protected-mode pointers, but must point to * + * DOS real-mode memory (except the Handler segment/offset) * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Configure (unsigned short socket, int conn_num, + ECBType *listen_ecb, ECBType *send_ecb, IPXHeaderType *listen_header, + IPXHeaderType *send_header, char *listen_buf, char *send_buf, + long handler_rm_ptr, int maxpacketlen) +{ + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + Socket = socket; + ConnectionNum = conn_num; + ListenECB = listen_ecb; + SendECB = send_ecb; + ListenHeader = listen_header; + SendHeader = send_header; + ListenBuf = listen_buf; + SendBuf = send_buf; + Handler = handler_rm_ptr; + PacketLen = maxpacketlen; + + Configured = 1; + +} /* end of Configure */ + + +/*************************************************************************** + * IPXConnClass::Start_Listening -- commands IPX to listen * + * * + * This routine may be used to start listening in polled mode (if the * + * ECB's Event_Service_Routine is NULL), or in interrupt mode; it's * + * up to the caller to fill the ECB in. If in polled mode, Listening * + * must be restarted every time a packet comes in. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - The ListenECB must have been properly filled in by the IPX Manager.* + * - Configure must be called before calling this routine. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Start_Listening(void) +{ +#ifdef WIN32 + +#ifdef WINSOCK_IPX + /* + ** Open the socket. + */ + if (!Open_Socket(Socket)) + return(false); + + /* + ** start listening on the socket. + */ + if ( PacketTransport->Start_Listening () ) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } + +#else + if (Winsock.Get_Connected ()) return (true); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + if (IPX_Start_Listening95()) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } +#endif //WINSOCK_IPX + +#else //WIN32 + + void *hdr_ptr; + unsigned long hdr_val; + void *buf_ptr; + unsigned long buf_val; + int rc; + + /*------------------------------------------------------------------------ + Don't do a thing unless we've been configured, and we're not listening. + ------------------------------------------------------------------------*/ + if (Configured==0 || Listening==1) { + return(0); + } + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) { + return(0); + } + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(ListenECB, 0, sizeof(ECBType)); + memset(ListenHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)ListenHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + + buf_val = (unsigned long)ListenBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in the ECB + ------------------------------------------------------------------------*/ + ListenECB->SocketNumber = Socket; + ListenECB->PacketCount = 2; + ListenECB->Packet[0].Address = hdr_ptr; + ListenECB->Packet[0].Length = sizeof(IPXHeaderType); + ListenECB->Packet[1].Address = buf_ptr; + ListenECB->Packet[1].Length = (unsigned short)PacketLen; + + ((long &)ListenECB->Event_Service_Routine) = Handler; + + /*------------------------------------------------------------------------ + Command IPX to listen + ------------------------------------------------------------------------*/ + rc = IPX_Listen_For_Packet(ListenECB); + if (rc!=0) { + Close_Socket(Socket); + return(0); + } + else { + Listening = 1; + return(1); + } + +#endif //WIN32 + +} /* end of Start_Listening */ + + +/*************************************************************************** + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - This routine MUST NOT be called if IPX is not listening already! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Stop_Listening(void) +{ +#ifdef WINSOCK_IPX + if ( PacketTransport ) PacketTransport->Stop_Listening(); + Listening = 0; + + // All done. + return(1); +#else + /*------------------------------------------------------------------------ + Don't do anything unless we're already Listening. + ------------------------------------------------------------------------*/ + if (Listening==0) { + return(0); + } + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Listening = 0; + return (true); + } else { + IPX_Shut_Down95(); + Close_Socket(Socket); + } + +#else //WIN32 + + /*------------------------------------------------------------------------ + Shut IPX down. + ------------------------------------------------------------------------*/ + IPX_Cancel_Event(ListenECB); + Close_Socket(Socket); + +#endif //WIN32 + + Listening = 0; + + /*------------------------------------------------------------------------ + All done. + ------------------------------------------------------------------------*/ + return(1); +#endif //WINSOCK_IPX + +} /* end of Stop_Listening */ + + +/*************************************************************************** + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send(char *buf, int buflen, void *, int) +{ + /*------------------------------------------------------------------------ + Invoke our own Send_To routine, filling in our Address as the destination. + ------------------------------------------------------------------------*/ + if (Immed_Set) { + return(Send_To (buf, buflen, &Address, ImmediateAddress)); + } + else { + return(Send_To (buf, buflen, &Address, NULL)); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXConnClass::Open_Socket -- opens communications socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Open_Socket(unsigned short socket) +{ + int rc; +#ifdef WINSOCK_IPX + rc = PacketTransport->Open_Socket(socket); + + SocketOpen = rc; + return ( rc ); + +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 1; + return (true); + } + + SocketOpen = 0; + + /*------------------------------------------------------------------------ + Try to open a listen socket. The socket may have been left open by + a previously-crashed program, so ignore the state of the SocketOpen + flag for this call; use IPX to determine if the socket was already open. + ------------------------------------------------------------------------*/ + rc = IPX_Open_Socket(socket); + if (rc) { + + /*..................................................................... + If already open, close & reopen it + .....................................................................*/ + if (rc==IPXERR_SOCKET_ERROR) { +#ifdef WIN32 + WWDebugString ("Error -- Specified socket is already open"); +#endif //WIN32 + IPX_Close_Socket(socket); + rc = IPX_Open_Socket(socket); + } + + /*.................................................................. + Still can't open: return error + ..................................................................*/ + if (rc) { + return(0); + } + } + + SocketOpen = 1; + + return(1); +#endif //WINSOCK_IPX + +} /* end of Open_Socket */ + + +/*************************************************************************** + * IPXConnClass::Close_Socket -- closes the socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Calling this routine when the sockets aren't open may crash! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Close_Socket(unsigned short socket) +{ +#ifdef WINSOCK_IPX + socket = socket; + PacketTransport->Close_Socket(); + SocketOpen = 0; +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 0; + return; + } + + /*------------------------------------------------------------------------ + Never, ever, ever, under any circumstances whatsoever, close a socket + that isn't open. You'll regret it forever (or until at least until + you're through rebooting, which, if you're on a Pentium is the same + thing). + ------------------------------------------------------------------------*/ + if (SocketOpen==1) { + IPX_Close_Socket(socket); + } + + SocketOpen = 0; +#endif //WINSOCK_IPX +} /* end of Close_Socket */ + + +/*************************************************************************** + * IPXConnClass::Send_To -- sends the packet to the given address * + * * + * The "ImmediateAddress" field of the SendECB must be filled in with the * + * address of a bridge, or the node address of the destination if there * + * is no bridge. The NETX call to find this address will always crash * + * if NETX isn't loaded (ConnectionNum is 0), so this case is trapped & * + * prevented. * + * Also, if the address of this IPX connection is known when the * + * constructor is called, and Configure has been called, Get_Local_Target * + * is called to precompute the ImmediateAddress; this case is detected & * + * if the value is already computed, it's just memcpy'd over. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address Address to send to * + * immed ImmediateAddress value, NULL if none * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed) +{ +#ifdef WINSOCK_IPX + + immed = immed; + assert ( immed == NULL ); + PacketTransport->WriteTo ( (void*)buf, buflen, (void*) address ); + return (true); + +#else //WINSOCK_IPX + NetNumType net; + NetNodeType node; + int rc; + +#ifdef WIN32 + + unsigned char send_address[6]; + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return (true); + } + + if (immed) { + memcpy(send_address, immed, 6); +#ifdef FIXIT_DESTNET + // fixes DESTNET + address->Get_Address(net,node); +#else + // breaks DESTNET + memcpy(node, immed, 6); + memset (net, 0, sizeof(net) ); +#endif + } else { + address->Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, &send_address[0]); + if (rc!=0) { + return(false); + } + } else { + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(send_address,node,6); + } + } + + return (IPX_Send_Packet95(&send_address[0], (unsigned char*)buf, buflen, (unsigned char*)net, (unsigned char*)node)); + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + + /*------------------------------------------------------------------------ + Get the bridge address + ------------------------------------------------------------------------*/ + if (immed) { + memcpy(SendECB->ImmediateAddress, immed, 6); + } + else { + address->Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, + SendECB->ImmediateAddress); + if (rc!=0) { + return(0); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(SendECB->ImmediateAddress,node,6); + } + } + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + address->Get_Address(SendHeader); // fill in header addresses + SendHeader->DestNetworkSocket = Socket; // destination socket id + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) + Let_IPX_Breath(); + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX + +} /* end of Send_To */ + + +/*************************************************************************** + * IPXConnClass::Broadcast -- broadcasts the given packet * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Broadcast(char *buf, int buflen) +{ +#ifdef WINSOCK_IPX + PacketTransport->Broadcast (buf, buflen); + return (true); + +#else //WINSOCK_IPX + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return(true); + } else { + return (IPX_Broadcast_Packet95((unsigned char*)buf, buflen)); + } + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + SendECB->ImmediateAddress[0] = 0xff; + SendECB->ImmediateAddress[1] = 0xff; + SendECB->ImmediateAddress[2] = 0xff; + SendECB->ImmediateAddress[3] = 0xff; + SendECB->ImmediateAddress[4] = 0xff; + SendECB->ImmediateAddress[5] = 0xff; + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + SendHeader->DestNetworkNumber[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNumber[1] = 0xff; + SendHeader->DestNetworkNumber[2] = 0xff; + SendHeader->DestNetworkNumber[3] = 0xff; + SendHeader->DestNetworkNode[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNode[1] = 0xff; + SendHeader->DestNetworkNode[2] = 0xff; + SendHeader->DestNetworkNode[3] = 0xff; + SendHeader->DestNetworkNode[4] = 0xff; + SendHeader->DestNetworkNode[5] = 0xff; + SendHeader->DestNetworkSocket = Socket; // destination socket # + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) { + Let_IPX_Breath(); + } + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX +} /* end of Broadcast */ + +/************************** end of ipxconn.cpp *****************************/ + diff --git a/CODE/IPXCONN.H b/CODE/IPXCONN.H new file mode 100644 index 0000000..c3f5418 --- /dev/null +++ b/CODE/IPXCONN.H @@ -0,0 +1,206 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for IPX communications. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * SequencedConnClass. It guarantees order of delivery of packets. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXCONN_H +#define IPXCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "ipxaddr.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONN_NAME_MAX = 40 // max # chars allowed for connection name + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXConnClass(int numsend, int numrecieve, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen = 0); + virtual ~IPXConnClass () {}; + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + The Configure function is for configuring all connections at once. + It's static because it doesn't apply to any specific connection, but + all of them. + .....................................................................*/ + static void Configure(unsigned short socket, + int conn_num, ECBType *listen_ecb, ECBType *send_ecb, + IPXHeaderType *listen_header, IPXHeaderType *send_header, + char *listen_buf, char *send_buf, long handler_rm_ptr, + int maxpacketlen); + + /*..................................................................... + These routines tell IPX to start listening for packets, and to stop + listening for packets. They're static because they affect all + connections at once (there's no way to turn listening on for only one + connection; it's all or nothing). + .....................................................................*/ + static int Start_Listening (void); + static int Stop_Listening (void); + + /*..................................................................... + The Destination IPX Address for this connection + .....................................................................*/ + IPXAddressClass Address; + + /*..................................................................... + The "Immediate" (Bridge) address for this connection, and a flag + telling if the address has been precomputed. + .....................................................................*/ + NetNodeType ImmediateAddress; + int Immed_Set; + + /*..................................................................... + Each IPX Connection can have a Name & Unique numerical ID + .....................................................................*/ + int ID; + char Name[CONN_NAME_MAX]; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + /*..................................................................... + These are the routines that access IPX. Open_Socket & Close_Socket are + static because they're called by Start_Listening & Stop_Listening. + Send_To & Broadcast are static since they're direct interfaces to IPX, + and there's only one IPX instance running. + .....................................................................*/ + static int Open_Socket(unsigned short socket); + static void Close_Socket(unsigned short socket); + static int Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed); + static int Broadcast(char *buf, int buflen); + + /*..................................................................... + The socket ID for this connection + .....................................................................*/ + static unsigned short Socket; + + /*..................................................................... + User's local Connection # (0 = not logged in) + .....................................................................*/ + static int ConnectionNum; + + /*..................................................................... + This is a static version of MaxPacketLen, which is the size of the + app's packets, plus our internal CommHeaderType. It's used in the + Start_Listening routine. + .....................................................................*/ + static int PacketLen; + + /*..................................................................... + Variables for Listening (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *ListenECB; + static IPXHeaderType *ListenHeader; + static char *ListenBuf; + + /*..................................................................... + Variables for Sending (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *SendECB; + static IPXHeaderType *SendHeader; + static char *SendBuf; + + /*..................................................................... + This is a REAL-MODE pointer to the event-service-routine for IPX. + If it's 0, IPX will operate in polled mode. Otherwise, the high word + must contain the segment, and the low word must contain the offset. + CS will be the high word value when the routine is called. (Requiring + the segment/offset to be computed by the caller gives the caller + control over CS.) + .....................................................................*/ + static long Handler; + + /*..................................................................... + This status flag tells us if Configure() has been called or not. + .....................................................................*/ + static int Configured; + + /*..................................................................... + This status flag tells us if the socket has been opened or not. + .....................................................................*/ + static int SocketOpen; + + /*..................................................................... + This status flag tells us if Start_Listening() has been called or not. + .....................................................................*/ + static int Listening; +}; + +#endif + +/*************************** end of ipxconn.h ******************************/ diff --git a/CODE/IPXGCONN.CPP b/CODE/IPXGCONN.CPP new file mode 100644 index 0000000..6b5ac3c --- /dev/null +++ b/CODE/IPXGCONN.CPP @@ -0,0 +1,533 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXGCONN.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXGCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : July 6, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* + * IPXGlobalConnClass::Send -- sends a packet * + * IPXGlobalConnClass::Service_Receive_Queue -- services receive queue * + * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include +#include +#include "ipxgconn.h" + + +/*************************************************************************** + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * * + * This routine chains to the parent constructor, but it adjusts the size * + * of the packet by the added bytes in the GlobalHeaderType structure. * + * This forces the parent classes to allocate the proper sized PacketBuf * + * for outgoing packets, and to set MaxPacketLen to the proper value. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * product_id unique ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, + int maxlen, unsigned short product_id) : + IPXConnClass (numsend, numreceive, + maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), + GLOBAL_MAGICNUM, // magic number for this connection + NULL, // IPX Address (none) + 0, // Connection ID + "", // Connection Name + sizeof (IPXAddressClass)) // extra storage for the sender's address +{ + int i; + + ProductID = product_id; + IsBridge = 0; + + for (i = 0; i < 4; i++) { + LastPacketID[i] = 0xffffffff; + } + LastRXIndex = 0; + +} /* end of IPXGlobalConnClass */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a GlobalHeaderType and * + * queues the resulting packet into the Send Queue. The packet's * + * MagicNumber, Code, PacketID, destination Address and ProductID are set * + * here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address address to send the packet to (NULL = Broadcast) * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req) +{ + IPXAddressClass dest_addr; + + /*------------------------------------------------------------------------ + Store the packet's Magic Number + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + If this is a ACK-required packet, sent to a specific system, mark it as + ACK-required; otherwise, mark as no-ACK-required. + ------------------------------------------------------------------------*/ + if (ack_req && address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; + } + else { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; + } + + /*------------------------------------------------------------------------ + Fill in the packet ID. This will have very limited meaning; it only + allows us to determine if an ACK packet we receive later goes with this + packet; it doesn't let us detect re-sends of other systems' packets. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); + + /*------------------------------------------------------------------------ + Set the product ID for this packet. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; + + /*------------------------------------------------------------------------ + Set this packet's destination address. If no address is specified, use + a Broadcast address (which IPXAddressClass's default constructor creates). + ------------------------------------------------------------------------*/ + if (address != NULL) { + dest_addr = (*address); + } + + /*------------------------------------------------------------------------ + Copy the application's data + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Queue it, along with the destination address + ------------------------------------------------------------------------*/ + return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType), + &dest_addr, sizeof (IPXAddressClass))); + +} /* end of Send_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes GlobalHeaderType) * + * buflen length of buffer to process * + * address the address of the sender (the IPX Manager class must * + * extract this from the IPX Header of the received packet.) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, + IPXAddressClass *address) +{ + GlobalHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + GlobalHeaderType *entry_data; // ptr to queue entry data + GlobalHeaderType ackpacket; // ACK packet to send + int i; + int resend; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (GlobalHeaderType *)buf; + if (packet->Header.MagicNumber!=MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Header.Code) { + //..................................................................... + // DATA_ACK: Check for a resend by comparing the source address & + // ID of this packet with our last 4 received packets. + // Send an ACK for the packet, regardless of whether it's a resend + // or not. + //..................................................................... + case PACKET_DATA_ACK: + { + //.................................................................. + // Check for a resend + //.................................................................. + resend = 0; + for (i = 0; i < 4; i++) { + if (i >= Queue->Receive_Total()) { + break; + } + if ((*address)==LastAddress[i] && + packet->Header.PacketID==LastPacketID[i]) { + resend = 1; + break; + } + } + + bool send_ack = true; + + //.................................................................. + // If it's not a resend, queue it; then, record the sender's address + // & the packet ID for future resend detection. + //.................................................................. + if (!resend) { + if (Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass))) { + LastAddress[LastRXIndex] = (*address); + LastPacketID[LastRXIndex] = packet->Header.PacketID; + LastRXIndex++; + if (LastRXIndex >= 4) { + LastRXIndex = 0; + } + }else{ + //.................................................................. + // Don't send an ack if we didn't have room to store the packet. + //.................................................................. + send_ack = false; + } + } + + + //.................................................................. + // Send an ACK for this packet + //.................................................................. + if (send_ack) { + ackpacket.Header.MagicNumber = MagicNum; + ackpacket.Header.Code = PACKET_ACK; + ackpacket.Header.PacketID = packet->Header.PacketID; + ackpacket.ProductID = ProductID; + Send ((char *)&ackpacket, sizeof(GlobalHeaderType), + address, sizeof(IPXAddressClass)); + } + + + break; + } + /*..................................................................... + DATA_NOACK: Queue this message, along with the sender's address. + Don't bother checking for a Re-Send, since the other system will only + send this packet once. + .....................................................................*/ + case PACKET_DATA_NOACK: + Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass)); + break; + + /*..................................................................... + ACK: If this ACK is for any of my packets, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: + for (i = 0; i < Queue->Num_Send(); i++) { + /*............................................................... + Get queue entry ptr + ...............................................................*/ + send_entry = Queue->Get_Send(i); + + /*............................................................... + If ptr is valid, get ptr to its data + ...............................................................*/ + entry_data = (GlobalHeaderType *)(send_entry->Buffer); + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->Header.PacketID==entry_data->Header.PacketID && + entry_data->Header.Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + } + + return(1); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * address filled in with sender's address * + * product_id filled in with sender's ProductID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet; + int packetlen; // size of received packet + + /*------------------------------------------------------------------------ + Return if nothing to do + ------------------------------------------------------------------------*/ + if (Queue->Num_Receive() == 0) { + return(0); + } + + /*------------------------------------------------------------------------ + Get ptr to the next available entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Get_Receive(0); + + /*------------------------------------------------------------------------ + Read it if it's un-read + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + + /*..................................................................... + Mark as read + .....................................................................*/ + rec_entry->IsRead = 1; + + /*..................................................................... + Copy data packet + .....................................................................*/ + packet = (GlobalHeaderType *)(rec_entry->Buffer); + packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); + } + (*buflen) = packetlen; + (*product_id) = packet->ProductID; + (*address) = (*((IPXAddressClass *)(rec_entry->ExtraBuffer))); + + return(1); + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send -- sends a packet * + * * + * This routine gets invoked by NonSequencedConn, when it's processing * + * the Send & Receive Queues. The buffer provided will already have the * + * GlobalHeaderType header embedded in it. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * extrabuf extra buffer to send * + * extralen length of extra buffer * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send(char *buf, int buflen, void *extrabuf, int ) +{ + IPXAddressClass *addr; + int rc; + + /*------------------------------------------------------------------------ + Extract the packet's embedded IPX address + ------------------------------------------------------------------------*/ + addr = (IPXAddressClass *)extrabuf; + + /*------------------------------------------------------------------------ + If it's a broadcast address, broadcast it + ------------------------------------------------------------------------*/ + if (addr->Is_Broadcast()) { + return(Broadcast (buf, buflen)); + } + + /*------------------------------------------------------------------------ + Otherwise, send it + ------------------------------------------------------------------------*/ + else { + if (IsBridge && !memcmp (addr, BridgeNet, 4)) { + rc = Send_To (buf, buflen, addr, BridgeNode); + } + else { + rc = Send_To (buf, buflen, addr, NULL); + } + return (rc); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXGlobalConnClass::Service_Receive_Queue -- services the receive queue * + * * + * This routine is necessary because the regular ConnectionClass checks * + * for sequential packet ID's before removing them from the receive queue; * + * this class cannot do that. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Service_Receive_Queue (void) +{ + int i; + ReceiveQueueType *rec_entry; // ptr to receive entry header + + //------------------------------------------------------------------------ + // Remove all dead packets: If a packet's been read, throw it away. + //------------------------------------------------------------------------ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + + return (1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * Set_Bridge -- Sets up connection to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) +{ +#ifdef WINSOCK_IPX + + if (Configured) { + bridge = bridge; + IsBridge = 0; + } + +#else //WINSOCK_IPX + if (Configured) { + memcpy (BridgeNet, bridge, 4); + memset (BridgeNode, 0xff, 6); + + if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { + IsBridge = 1; + } + else { + IsBridge = 0; + } + } +#endif //WINSOCK_IPX + +} /* end of Set_Bridge */ + + +/************************** end of ipxgconn.cpp ****************************/ diff --git a/CODE/IPXGCONN.H b/CODE/IPXGCONN.H new file mode 100644 index 0000000..d9be565 --- /dev/null +++ b/CODE/IPXGCONN.H @@ -0,0 +1,206 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXGCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXGCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 11, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class is a special type of IPX Connection. It can talk to more * + * than one system at a time. It can Broadcast packets to all systems, * + * or send a packet to one individual system. The packets it sends to * + * individual systems can be DATA_NOACK or DATA_ACK packets, but the * + * packets broadcast have to be DATA_NOACK packets. This class is for * + * only the crudest "Who-are-you" type of network communications. Once * + * the IPX Address of another system is identified, a "real" IPX * + * Connection should be created, & further communications done through it. * + * * + * This means that the packet ID field no longer can be used to detect * + * resends, since the receive queue may receive a lot more packets than * + * we send out. So, re-sends cannot be detected; the application must be * + * designed so that it can handle multiple copies of the same packet. * + * * + * The class uses a slightly different header from the normal Connections; * + * this header includes the ProductID of the sender, so multiple * + * applications can share the same socket, but by using different product * + * ID's, can distinguish between their own packets & others. * + * * + * Because of this additional header, and because Receive ACK/Retry logic * + * is different (we can't detect resends), the following routines are * + * overloaded: * + * Send_Packet: must embed the product ID into the packet header * + * Receive_Packet: must detect resends via the LastAddress & * + * LastPacketID arrays. This class doesn't ACK * + * packets until Service_Receive_Queue is called; * + * the parent classes ACK the packet when it's * + * received. * + * Get_Packet: extracts the product ID from the header; * + * doesn't care about reading packets in order * + * Send is capable of broadcasting the packet, or sending * + * to a specific address * + * * + * This class also has the ability to cross a Novell Network Bridge. * + * You provide the class with the bridge address, and subsequent * + * broadcasts are sent across the bridge as well as to the local network. * + * Address-specific sends contain the destination network's address, * + * so they cross a bridge automatically; it's just the broadcasts * + * that need to know the bridge address. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXGLOBALCONN_H +#define IPXGLOBALCONN_H + + +#include "ipxconn.h" + + +/* +********************************** Defines ********************************** +*/ +//--------------------------------------------------------------------------- +// This is the header for Global Connection messages. It includes the usual +// "standard" header that the other connections do; but it also includes an +// IPX address field, so the application can get the address of the sender +// of this message. This address field must be provided in by the IPX +// Connection Manager class, when it calls this class's Receive_Packet +// function. +//--------------------------------------------------------------------------- +typedef struct { + CommHeaderType Header; + unsigned short ProductID; +} GlobalHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXGlobalConnClass : public IPXConnClass +{ + //------------------------------------------------------------------------ + // Public Interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Some useful enums: + //..................................................................... + enum GlobalConnectionEnum { + //.................................................................. + // This is the magic number for all Global Connections. Having the + // same magic number across products lets us ID different products + // on the net. If you change the fundamental connection protocol, + // you must use a different magic number. + //.................................................................. + //GLOBAL_MAGICNUM = 0x1234, // used for C&C 1 + GLOBAL_MAGICNUM = 0x1235, // used for C&C 0 + //.................................................................. + // These are the values used for the ProductID field in the Global + // Message structure. It also should be the Magic Number used for + // the private connections within that product. + // This list should be continually updated & kept current. Never + // ever ever use an old product ID for your product! + //.................................................................. + COMMAND_AND_CONQUER = 0xaa01, + COMMAND_AND_CONQUER0 = 0xaa00 + }; + + //..................................................................... + // Constructor/destructor. + //..................................................................... + IPXGlobalConnClass (int numsend, int numrecieve, int maxlen, + unsigned short product_id); + virtual ~IPXGlobalConnClass () {}; + + //..................................................................... + // Send/Receive routines. + //..................................................................... + virtual int Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req); + virtual int Receive_Packet (void * buf, int buflen, + IPXAddressClass *address); + virtual int Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id); + + //..................................................................... + // This is for telling the connection it can cross a bridge. + //..................................................................... + void Set_Bridge (NetNumType bridge); + + //..................................................................... + // The Product ID for this product. + //..................................................................... + unsigned short ProductID; + + //..................................................................... + // This describes the address of a bridge we have to cross. This class + // supports crossing only one bridge. Storing the bridge's network + // number allows us to obtain its local target address only once, then + // re-use it. + //..................................................................... + NetNumType BridgeNet; + NetNodeType BridgeNode; + int IsBridge; + + //------------------------------------------------------------------------ + // Protected Interface + //------------------------------------------------------------------------ + protected: + + //..................................................................... + // This is the overloaded Send routine declared in ConnectionClass, and + // used in SequencedConnClass. This special version sends to the address + // stored in the extra buffer within the Queue. + //..................................................................... + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + //..................................................................... + // This routine is overloaded from SequencedConnClass, because the + // Global Connection needs to ACK its packets differently from the + // other connections. + //..................................................................... + virtual int Service_Receive_Queue (void); + + private: + //..................................................................... + // Since we can't detect resends by using the PacketID (since we're + // receiving packets from a variety of sources, all using different + // ID's), we'll have to remember the last 'n' packet addresses & id's + // for comparison purposes. + // Note that, if network traffic is heavy, it's still possible for an + // app to receive the same packet twice! + //..................................................................... + IPXAddressClass LastAddress[4]; // array of last 4 addresses + unsigned long LastPacketID[4]; // array of last 4 packet ID's + int LastRXIndex; // index of next avail pos +}; + +#endif + +/*************************** end of ipxgconn.h *****************************/ diff --git a/CODE/IPXMGR.CPP b/CODE/IPXMGR.CPP new file mode 100644 index 0000000..a45aa85 --- /dev/null +++ b/CODE/IPXMGR.CPP @@ -0,0 +1,2068 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXMGR.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 4, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXManagerClass::IPXManagerClass -- class constructor * + * IPXManagerClass::~IPXManagerClass -- class destructor * + * IPXManagerClass::Init -- initialization routine * + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * IPXManagerClass::Create_Connection -- creates a new connection * + * IPXManagerClass::Delete_Connection -- deletes a connection * + * IPXManagerClass::Num_Connections -- gets the # of connections * + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * IPXManagerClass::Connection_Name -- gets name for given connection * + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * IPXManagerClass::Connection_Index -- gets given connection's index * + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * IPXManagerClass::Get_Private_Message -- Polls Private Message queue * + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * IPXManagerClass::Global_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Global_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Private_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Private_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include +#include "ipxmgr.h" +#include "wwlib32.h" // to enable mono output + +#ifdef WINSOCK_IPX + +#include "WSProto.h" +#include "WSPIPX.h" + +#else //WINSOCK_IPX + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +// Turn off "expression is not meaningful". +#pragma warning 628 9 + +#include "WolDebug.h" + +/*************************************************************************** + * IPXManagerClass::IPXManagerClass -- class constructor * + * * + * INPUT: * + * glb_maxlen Global Channel maximum packet length * + * pvt_maxlen Private Channel maximum packet length * + * socket socket ID to use * + * product_id a unique numerical ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::IPXManagerClass (int glb_maxlen, int pvt_maxlen, + int glb_num_packets, int pvt_num_packets, unsigned short socket, + unsigned short product_id) +{ + int i; +#ifdef WINSOCK_IPX + /* + ** Find out if Packet protocol services are available through Winsock. + */ + if ( PacketTransport ) { + delete PacketTransport; + PacketTransport = NULL; + } + PacketTransport = new WinsockInterfaceClass; + assert ( PacketTransport != NULL ); + + if ( PacketTransport->Init() ){ + IPXStatus = 1; + }else{ + IPXStatus = 0; + } + delete PacketTransport; + PacketTransport = NULL; + +#else //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Initialize data members + //------------------------------------------------------------------------ + //........................................................................ + // IPXStatus = 1 if IPX is installed, 0 if not + //........................................................................ + if (IPX_SPX_Installed()==0) { + IPXStatus = 0; + } + else { + IPXStatus = 1; + } +#endif //WINSOCK_IPX + + //........................................................................ + // Set listening state flag to off + //........................................................................ + Listening = 0; + + //........................................................................ + // No memory has been alloc'd yet + //........................................................................ + RealMemAllocd = 0; + + //........................................................................ + // Set max packet sizes, for allocating real-mode memory + //........................................................................ + Glb_MaxPacketLen = glb_maxlen; + Glb_NumPackets = glb_num_packets; + Pvt_MaxPacketLen = pvt_maxlen; + Pvt_NumPackets = pvt_num_packets; + + //........................................................................ + // Save the app's product ID + //........................................................................ + ProductID = product_id; + + //........................................................................ + // Save our socket ID number + //........................................................................ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + + //------------------------------------------------------------------------ + // Get the user's IPX local connection number + //------------------------------------------------------------------------ + ConnectionNum = 0; +#ifndef WINSOCK_IPX + if (IPXStatus) { + ConnectionNum = IPX_Get_Connection_Number(); + } +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Init connection states + //------------------------------------------------------------------------ + NumConnections = 0; + CurConnection = 0; + for (i = 0; i < CONNECT_MAX; i++) { + Connection[i] = 0; + } + GlobalChannel = 0; + + SendOverflows = 0; + ReceiveOverflows = 0; + BadConnection = CONNECTION_NONE; + + //------------------------------------------------------------------------ + // Init timing parameters + //------------------------------------------------------------------------ + RetryDelta = 2; // 2 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 60; // report bad connection after 1 second + +} /* end of IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::~IPXManagerClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::~IPXManagerClass() +{ + int i; + + //------------------------------------------------------------------------ + // Stop all IPX events + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free all protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + //------------------------------------------------------------------------ + // Free all real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 +#ifndef WINSOCK_IPX + Unload_IPX_Dll(); +#endif +#endif +#endif +} /* end of ~IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::Init -- initialization routine * + * * + * This routine allocates memory, & initializes variables * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Init() +{ + int i; + + if (Session.Type != GAME_INTERNET) { + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Stop Listening + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free Real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + + } else { + /* + ** Pretend IPX is available for Internet games whether it is or not + */ + IPXStatus = 1; + } + + //------------------------------------------------------------------------ + // Free protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + if (Session.Type != GAME_INTERNET) { + //------------------------------------------------------------------------ + // Allocate real-mode memory + //------------------------------------------------------------------------ + if (!Alloc_RealMode_Mem()) return(0); + RealMemAllocd = 1; + } + + //------------------------------------------------------------------------ + // Allocate the Global Channel + //------------------------------------------------------------------------ + GlobalChannel = new IPXGlobalConnClass (Glb_NumPackets, Glb_NumPackets, + Glb_MaxPacketLen, ProductID); + if (!GlobalChannel) { + return(0); + } + GlobalChannel->Init(); + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + + //------------------------------------------------------------------------ + // Configure the IPX Connections + //------------------------------------------------------------------------ + IPXConnClass::Configure(Socket, ConnectionNum, ListenECB, SendECB, + FirstHeaderBuf, SendHeader, FirstDataBuf, SendBuf, Handler, PacketLen); + + //------------------------------------------------------------------------ + // Start Listening + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (!IPXConnClass::Start_Listening()) return(0); + } + Listening = 1; + + return(1); + +} /* end of Init */ + + +/*************************************************************************** + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = IPX is installed; 0 = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Is_IPX(void) +{ + return(IPXStatus); + +} /* end of Is_IPX */ + + +/*************************************************************************** + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters for all existing connections, and * + * all connections created from now on. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + int i; + + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + if (GlobalChannel) { + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + } + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Set_Retry_Delta (RetryDelta); + Connection[i]->Set_Max_Retries (MaxRetries); + Connection[i]->Set_TimeOut (Timeout); + } + +} /* end of Set_Timing */ + + +/*************************************************************************** + * IPXManagerClass::Create_Connection -- creates a new connection * + * * + * INPUT: * + * id application-specific numerical ID for this connection * + * node ptr to IPXNodeIDType (name & address) * + * address IPX address for this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * Never create a connection with an 'id' of -1. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Create_Connection(int id, char *name, + IPXAddressClass *address) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no more room + //------------------------------------------------------------------------ + if (NumConnections==CONNECT_MAX) { + return(0); + } + + //------------------------------------------------------------------------ + // Create new connection + //------------------------------------------------------------------------ + Connection[NumConnections] = new IPXConnClass(Pvt_NumPackets, + Pvt_NumPackets, Pvt_MaxPacketLen, ProductID, address, id, name); + if (!Connection[NumConnections]) { + return(0); + } + + Connection[NumConnections]->Init (); + Connection[NumConnections]->Set_Retry_Delta (RetryDelta); + Connection[NumConnections]->Set_Max_Retries (MaxRetries); + Connection[NumConnections]->Set_TimeOut (Timeout); + + NumConnections++; + + return(1); + +} /* end of Create_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Delete_Connection -- deletes a connection * + * * + * INPUT: * + * id ID of connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Delete_Connection(int id) +{ + int i,j; + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no connections to delete + //------------------------------------------------------------------------ + if (NumConnections==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop through all connections + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + //..................................................................... + // If a match, delete it + //..................................................................... + if (Connection[i]->ID==id) { + delete Connection[i]; + + //.................................................................. + // Move array elements back one index + //.................................................................. + for (j = i; j < NumConnections - 1; j++) { + Connection[j] = Connection[j+1]; + } + + //.................................................................. + // Adjust counters + //.................................................................. + NumConnections--; + if (CurConnection >= NumConnections) + CurConnection = 0; + return(1); + } + } + + //------------------------------------------------------------------------ + // No match; error + //------------------------------------------------------------------------ + return(0); + +} /* end of Delete_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Num_Connections -- gets the # of connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of connections * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Num_Connections(void) +{ + return(NumConnections); + +} /* end of Num_Connections */ + + +/*************************************************************************** + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * * + * INPUT: * + * index index of connection to retrieve * + * * + * OUTPUT: * + * ID for that connection, CONNECTION_NONE if invalid index * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(Connection[index]->ID); + } + else { + return(CONNECTION_NONE); + } +} /* end of Connection_ID */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Name -- retrieves name for given connection * + * * + * INPUT: * + * id ID of connection to get name of * + * * + * OUTPUT: * + * ptr to connection's name, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +char *IPXManagerClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(Connection[i]->Name); + } + } + + return(NULL); + +} /* end of Connection_Name */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * * + * INPUT: * + * id ID of connection to get address for * + * * + * OUTPUT: * + * pointer to IXPAddressClass, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +IPXAddressClass * IPXManagerClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(&Connection[i]->Address); + } + } + + return(NULL); + +} /* end of Connection_Address */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Index -- gets given connection's index * + * * + * INPUT: * + * ID to retrieve index for * + * * + * OUTPUT: * + * index for this connection, CONNECTION_NONE if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(i); + } + } + + return(CONNECTION_NONE); + +} /* end of Connection_Index */ + + +/*************************************************************************** + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * * + * INPUT: * + * index connection index * + * id new connection ID * + * name new connection name * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Connection_Parms(int index, int id, char *name) +{ + if (index >= NumConnections) + return; + + Connection[index]->ID = id; + strcpy(Connection[index]->Name,name); + +} /* end of Set_Connection_Parms */ + + +/*************************************************************************** + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buf * + * ack_req 1 = ACK required; 0 = no ACK required * + * address address to send to; NULL = broadcast * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Global_Message(void *buf, int buflen, + int ack_req, IPXAddressClass *address) +{ + int rc; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + rc = GlobalChannel->Send_Packet (buf, buflen, address, ack_req); + if (!rc) { + SendOverflows++; + } + + return(rc); + +} /* end of Send_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * * + * INPUT: * + * buf buffer to store received packet * + * buflen length of data placed in 'buf' * + * address IPX address of sender * + * product_id product ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Global_Message(void *buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + return(GlobalChannel->Get_Packet (buf, buflen, address, product_id)); + +} /* end of Get_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of 'buf' * + * conn_id connection ID to send to (CONNECTION_NONE = all) * + * ack_req 1 = ACK required; 0 = no ACK required * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Private_Message(void *buf, int buflen, int ack_req, + int conn_id) +{ + int i; // loop counter + int connect_idx; // index of channel to send to, if specified + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Send the message to all connections + //------------------------------------------------------------------------ + if (conn_id==CONNECTION_NONE) { + //..................................................................... + // Check for room in all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() == + Connection[i]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + } + + //..................................................................... + // Send packet to all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + Connection[i]->Send_Packet (buf, buflen, ack_req); + } + return(1); + } + + //------------------------------------------------------------------------ + // Send the message to the specified connection + //------------------------------------------------------------------------ + else { + connect_idx = Connection_Index (conn_id); + if (connect_idx == CONNECTION_NONE) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Check for room in the connection + //..................................................................... + if (Connection[connect_idx]->Queue->Num_Send() == + Connection[connect_idx]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Send the packet to that connection + //..................................................................... + Connection[connect_idx]->Send_Packet (buf, buflen, ack_req); + return(1); + } + +} /* end of Send_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Private_Message -- Polls the Private Message queue * + * * + * INPUT: * + * buf buffer to store incoming packet * + * buflen length of data placed in 'buf' * + * conn_id filled in with connection ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Private_Message(void *buf, int *buflen, int *conn_id) +{ + int i; + int rc; + int c_id; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Safety check: ensure CurConnection is in range. + //------------------------------------------------------------------------ + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //------------------------------------------------------------------------ + // Scan all connections for a received packet, starting with 'CurConnection' + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + + //..................................................................... + // Check this connection for a packet + //..................................................................... + rc = Connection[CurConnection]->Get_Packet (buf, buflen); + c_id = Connection[CurConnection]->ID; + + //..................................................................... + // Increment CurConnection to the next connection index + //..................................................................... + CurConnection++; + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //..................................................................... + // If we got a packet, return the connection ID + //..................................................................... + if (rc) { + (*conn_id) = c_id; + return(1); + } + } + + return(0); + +} /* end of Get_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Service(void) +{ + int rc = 1; + int i; + CommHeaderType *packet; + int packetlen; + IPXAddressClass address; + +#ifdef WINSOCK_IPX + + + unsigned char temp_receive_buffer[1024]; + int temp_receive_buffer_len; + int temp_address_len; + + + char temp_address [128]; + + if ( PacketTransport ) { + + do { + temp_receive_buffer_len = sizeof (temp_receive_buffer); + temp_address_len = sizeof (temp_address); + packetlen = PacketTransport->Read ( temp_receive_buffer, temp_receive_buffer_len, temp_address, temp_address_len ); + if ( packetlen ) { + CurDataBuf = (char*)temp_receive_buffer; + address = *((IPXAddressClass*) temp_address); + + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + /* + ** Put the packet in the Global Queue + */ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + + /* + ** Find the Private Queue that this packet is for + */ + bool found_address = false; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + found_address = true; + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + if( Session.Type == GAME_INTERNET ) + { + /* + ** This packet came from an unknown source. If it looks like one of our players + ** packets then it might be from a player whos IP has changed. + */ + if (!found_address) { + if (packet->Code == ConnectionClass::PACKET_DATA_NOACK){ + /* + ** Magic number and packet code are valid. It's probably a C&C packet. + */ + EventClass *event = (EventClass*) (((char*) packet) + sizeof (CommHeaderType)); + + /* + ** If this is a framesync packet then grab the address and match it to an existing player. + */ + if (event->Type == EventClass::FRAMESYNC) { + int id = event->ID; + + assert (id != PlayerPtr->ID); + for ( int i=1 ; iPlayer.ID == id) { + + int iConnectionIndex = Connection_Index(id); + if( iConnectionIndex != CONNECTION_NONE ) // (else Create_Connections() has not yet been called) + { + /* + ** Found a likely candidate. Update his address. It should be OK to drop this + ** packet since it's a framesync packet and will will pick up the next one. + */ + Session.Players[i]->Address = address; + Connection[iConnectionIndex]->Address = address; + } + break; + } + } + } + } + } + } + } + } + } + + } while (packetlen); + + } + + + + +#else //WINSOCK_IPX + +#ifdef WIN32 + + unsigned char temp_receive_buffer[1024]; + int recv_length; + + if (Winsock.Get_Connected() || Special.IsFromWChat) { + + if (!Winsock.Get_Connected()) return (0); + + /* + ** This is an internet connection so get the packets from winsock + */ + while ((recv_length = Winsock.Read(temp_receive_buffer, 1024))!=0) { + + CurHeaderBuf = NULL; + CurDataBuf = (char*)&temp_receive_buffer[0]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length; + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + } else { + + while (IPX_Get_Outstanding_Buffer95(&temp_receive_buffer[0])) { + + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; + CurDataBuf = (char*)&temp_receive_buffer[sizeof(IPXHeaderType)]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + } + } + + +#else //WIN32 + + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop until there are no more packets to process. + //------------------------------------------------------------------------ + while (1) { + + //..................................................................... + // Check the BufferFlags for the "current" buffer; if it's empty, + // break; out of the loop. + //..................................................................... + if (BufferFlags[CurIndex]==0) { + break; + } + + //..................................................................... + // Compute the length of the packet (byte-swap the length in the IPX hdr) + //..................................................................... + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + //..................................................................... + // Extract the sender's address from the IPX header + //..................................................................... + address.Set_Address (CurHeaderBuf); + + //..................................................................... + // Examine the Magic Number of the received packet to determine if this + // packet goes into the Global Queue, or into one of the Private Queues + //..................................................................... + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + //.................................................................. + // Put the packet in the Global Queue + //.................................................................. + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) { + ReceiveOverflows++; + } + } + + //..................................................................... + // Find the Private Queue that this packet is for + //..................................................................... + else if (packet->MagicNumber == ProductID) { + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) { + ReceiveOverflows++; + } + break; + } + } + } + + //..................................................................... + // Set the current BufferFlags to 0 (since we've cleaned out this buffer) + //..................................................................... + BufferFlags[CurIndex] = 0; + + //..................................................................... + // Go to the next packet buffer + //..................................................................... + CurIndex++; + CurHeaderBuf = (IPXHeaderType *)(((char *)CurHeaderBuf) + FullPacketLen); + CurDataBuf = ((char *)CurDataBuf) + FullPacketLen; + if (CurIndex >= NumBufs) { + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + CurIndex = 0; + } + } + +#endif //WIN32 +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Service all connections. If a connection reports that it's gone "bad", + // report an error to the caller. If it's the Global Channel, un-queue the + // send entry that's holding things up. This will keep the Global Channel + // from being clogged by one un-ACK'd outgoing packet. + //------------------------------------------------------------------------ + if (GlobalChannel) { + if (!GlobalChannel->Service()) { + GlobalChannel->Queue->UnQueue_Send (NULL, NULL, 0); + rc = 0; + } + } + for (i = 0; i < NumConnections; i++) { + if (!Connection[i]->Service()) { + rc = 0; + BadConnection = Connection[i]->ID; + } + } + + if (rc) { + BadConnection = CONNECTION_NONE; + } + + return(rc); + +} /* end of Service */ + +/*************************************************************************** + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ID of bad connection; CONNECTION_NONE if none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Bad_Connection(void) +{ + return(BadConnection); + +} /* end of Get_Bad_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Send Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Send(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Send()); + +} /* end of Global_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Receive(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Receive()); + +} /* end of Global_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * # entries in the Private Send Queue * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Send(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Send()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() > maxnum) { + maxnum = Connection[i]->Queue->Num_Send(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * id ID of connection to query * + * * + * OUTPUT: * + * # entries in the Private Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Receive(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) + return(0); + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Receive()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Receive() > maxnum) { + maxnum = Connection[i]->Queue->Num_Receive(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * * + * INPUT: * + * socket new socket ID to use * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not call this function after communications have started; you * + * must call it before calling Init(). * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Socket(unsigned short socket) +{ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + +} /* end of Set_Socket */ + + +/*************************************************************************** + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * largest avg response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Response_Time(void) +{ + unsigned long resp; + unsigned long maxresp = 0; + int i; + + for (i = 0; i < NumConnections; i++) { + resp = Connection[i]->Queue->Avg_Response_Time(); + if (resp > maxresp) { + maxresp = resp; + } + } + + return(maxresp); + +} /* end of Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * avg global channel response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Global_Response_Time(void) +{ + if (GlobalChannel) { + return (GlobalChannel->Queue->Avg_Response_Time()); + } + else { + return (0); + } + +} /* end of Global_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Reset_Response_Time(void) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Queue->Reset_Response_Time(); + } + + if (GlobalChannel) + GlobalChannel->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * buf ptr * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void * IPXManagerClass::Oldest_Send(void) +{ + int i,j; + unsigned long time; + unsigned long mintime = 0xffffffff; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < NumConnections; i++) { + + send_entry = NULL; + + for (j = 0; j < Connection[i]->Queue->Num_Send(); j++) { + send_entry = Connection[i]->Queue->Get_Send(j); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && + send_entry->IsACK == 0) { + break; + } + else { + send_entry = NULL; + } + } + } + + if (send_entry!=NULL) { + + time = send_entry->FirstTime; + + if (time < mintime) { + mintime = time; + buf = send_entry->Buffer; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Bridge(NetNumType bridge) +{ + if (GlobalChannel) { + GlobalChannel->Set_Bridge(bridge); + } + +} /* end of Set_Bridge */ + + +/*************************************************************************** + * IPXManagerClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical value of 1st name in the array * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Configure_Debug(int index, int type_offset, + int type_size, char **names, int namestart, int namecount) +{ + if (index == -1) { + GlobalChannel->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + else if (Connection[index]) { + Connection[index]->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * * + * INPUT: * + * index index of connection to display (-1 = Global Channel) * + * refresh 1 = complete screen refresh * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Mono_Debug_Print(int index, int refresh) +{ +#ifdef WWLIB32_H + char txt[80]; + int i; + + if (index == -1) + GlobalChannel->Queue->Mono_Debug_Print (refresh); + + else if (Connection[index]) + Connection[index]->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (20,1); + Mono_Printf ("IPX Queue:"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (43,1); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,2); + Mono_Printf ("Receive Overflows:"); + + } + + Mono_Set_Cursor (32,1); + Mono_Printf ("%d",index); + + Mono_Set_Cursor (32,2); + if (index == -1) { + Mono_Printf ("%d ", GlobalChannel->Queue->Avg_Response_Time()); + } + else { + Mono_Printf ("%d ", Connection[index]->Queue->Avg_Response_Time()); + } + + Mono_Set_Cursor (59,1); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", ReceiveOverflows); + + for (i = 0; i < NumBufs; i++) { + if (BufferFlags[i]) { + txt[i] = 'X'; + } + else { + txt[i] = '_'; + } + } + txt[i] = 0; + Mono_Set_Cursor ((80-NumBufs)/2,3); + Mono_Printf ("%s",txt); + +#else + index = index; + refresh = refresh; +#endif + +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Alloc_RealMode_Mem(void) +{ + +#ifdef WIN32 + return (1); +#else + + union REGS regs; + struct SREGS sregs; + int size; // required size of allocation + unsigned char *realmode; // start addresses of real-mode data + int realmodelen; // length of real-mode data + unsigned long func_val; + char *p; // for parsing buffer + int i; + + //------------------------------------------------------------------------ + // Compute # of buffers we need to allocate, & the max size of each one + //------------------------------------------------------------------------ + NumBufs = Glb_NumPackets + (Pvt_NumPackets * CONNECT_MAX); + + PacketLen = Glb_MaxPacketLen + sizeof (GlobalHeaderType); + if (Pvt_MaxPacketLen + sizeof (CommHeaderType) > PacketLen) + PacketLen = Pvt_MaxPacketLen + sizeof (CommHeaderType); + + FullPacketLen = PacketLen + sizeof(IPXHeaderType); + + //------------------------------------------------------------------------ + // Compute the size of everything we'll ever need, allocate it in one big + // chunk. The memory is used as follows: + // - Real-mode assembly IPX callback routine, plus its data, + // (which includes the ListenECB) + // - Array of IPX Packet buffers (IPXHeader plus data buffer) + // - SendECB: ECB for sending + // - SendHeader: IPX Header for sending + // - SendBuf: Packet buffer for sending + // - BufferFlags: 1 byte for each incoming packet buffer; 1=in use, 0=free + //------------------------------------------------------------------------ + realmode = (unsigned char *)Get_RM_IPX_Address(); + realmodelen = Get_RM_IPX_Size(); + size = realmodelen + // assembly routine & its data + (FullPacketLen * NumBufs) + // array of packet buffers + sizeof(ECBType) + // SendECB + FullPacketLen + // SendHeader & SendBuf + NumBufs; // BufferFlags + if (size > 65535) { + return(0); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory for the ECB, IPXHeader & packet buffers: + // AX = 0x100 + // BX = # paragraphs to allocate + // - if Success, AX = real-mode segment, DX = selector + // - if Failure, carry flag is set + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = ((size + 15) >> 4); // # paragraphs to allocate + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Save the values of the returned segment & selector + //------------------------------------------------------------------------ + Selector = regs.w.dx; + Segment = regs.w.ax; + RealMemSize = size; + RealModeData = (RealModeDataType *)(((long)Segment) << 4); + + //------------------------------------------------------------------------ + // Lock the memory (since we're servicing interrupts with it) + // AX = 0x600 + // BX:CX = starting linear address of memory to lock + // SI:DI = size of region to lock (in bytes) + // - If Failure, carry flag is set. + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + return(0); + } + + //------------------------------------------------------------------------ + // Copy the Real-mode code into our memory buffer + //------------------------------------------------------------------------ + p = (char *)(((long)Segment) << 4); + memcpy (p,realmode,realmodelen); + p += realmodelen; + + //------------------------------------------------------------------------ + // Compute & save the entry point for the real-mode packet handler + //------------------------------------------------------------------------ + func_val = (unsigned long)RealModeData; + Handler = (((func_val & 0xffff0) << 12) | + ((func_val & 0x000f) + RealModeData->FuncOffset)); + + //------------------------------------------------------------------------ + // Fill in buffer pointers + //------------------------------------------------------------------------ + ListenECB = &(RealModeData->ListenECB); + + FirstHeaderBuf = (IPXHeaderType *)p; + FirstDataBuf = (((char *)FirstHeaderBuf) + sizeof(IPXHeaderType)); + CurIndex = 0; + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + p += FullPacketLen * NumBufs; + + SendECB = (ECBType *)p; + p += sizeof (ECBType); + + SendHeader = (IPXHeaderType *)p; + p += sizeof (IPXHeaderType); + + SendBuf = (char *)p; + p += PacketLen; + + BufferFlags = (char *)p; + + //------------------------------------------------------------------------ + // Fill in the real-mode routine's data (The ECB will be filled in when we + // command IPX to Listen). + //------------------------------------------------------------------------ + RealModeData->NumBufs = (short)NumBufs; + RealModeData->BufferFlags = (char *) + ((((long)BufferFlags & 0xffff0) << 12) | + ((long)BufferFlags & 0x0000f)); + RealModeData->PacketSize = (short)FullPacketLen; + RealModeData->FirstPacketBuf = (IPXHeaderType *) + ((((long)FirstHeaderBuf & 0xffff0) << 12) | + ((long)FirstHeaderBuf & 0x0000f)); + RealModeData->CurIndex = 0; + RealModeData->CurPacketBuf = RealModeData->FirstPacketBuf; + RealModeData->Semaphore = 0; + RealModeData->ReEntrantCount = 0; + + //------------------------------------------------------------------------ + // Init state of all buffers to empty + //------------------------------------------------------------------------ + for (i = 0; i < NumBufs; i++) { + BufferFlags[i] = 0; + } + + //------------------------------------------------------------------------ + // Check the start & end markers in the real-mode memory area + //------------------------------------------------------------------------ + if (RealModeData->Marker1 != 0x1111 || + RealModeData->Marker2 != 0x2222) { + Free_RealMode_Mem(); + return(0); + } + else { + return(1); + } +#endif //WIN32 +} /* end of Alloc_Realmode_Mem */ + + +/*************************************************************************** + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Free_RealMode_Mem(void) +{ + +#ifdef WIN32 + + return (1); + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + int rc = 1; + + //------------------------------------------------------------------------ + // Unlock the memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + rc = 0; + } + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + + return(rc); +#endif //WIN32 + +} /* end of Free_Realmode_Mem */ + +/*************************** end of ipxmgr.cpp *****************************/ diff --git a/CODE/IPXMGR.H b/CODE/IPXMGR.H new file mode 100644 index 0000000..236978f --- /dev/null +++ b/CODE/IPXMGR.H @@ -0,0 +1,397 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/IPXMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : IPXMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for IPX network communications. It * + * creates, manages, & orchestrates multiple IPX connections, as well as * + * the "global" connection ("Global Channel"), which can talk to any * + * system on the net. * + * * + * Use the Global Channel to query systems for their names, ID's, & * + * IPX addresses. Then, create a Private Connection with each system * + * that joins your game, and use the Private Channel to send game packets * + * (the private channel will perform somewhat faster, & gives you better * + * control than the Global Channel; it can detect retries, and the Global * + * Channel can't). * + * * + * HOW THIS CLASS WORKS: * + * This class has to set up an IPX Event Service Routine in low (DOS) * + * memory. So, it uses DPMI to allocate & lock a chunk of DOS memory; * + * this memory is used for all incoming packet buffers, the outgoing * + * packet buffer, and the actual code for the event handler. The real- * + * mode handler code & this class share a portion of memory that's mapped * + * into a "RealModeDataType" structure. As packets come in, the handler * + * points IPX to the next available packet buffer & restarts listening; * + * it sets a flag to tell this class that a packet is present at that * + * buffer slot. This class must read all the packets & determine which * + * connection they go with (the Global Channel, or one of the Private * + * Channels). This parsing is done in the Service routine for this class. * + * * + * Constructor: Just inits some variables, checks to see if IPX is there * + * Destructor: Complete shutdown; stops IPX listening, frees all memory * + * Init: Should only be called once (but can be called more); * + * allocates all memory, creates the Global Channel * + * connection, starts IPX listening. By not placing this * + * step in the constructor, the app can control when * + * listening actually starts; also, you don't get a bunch * + * of allocations just by declaring an IPXManagerClass * + * instance. You have to call Init() for the allocations * + * to occur. * + * Connection utilities: Create & manage Private Connections. Each * + * connection has its own IPX address, numerical ID, and * + * character name (presumably the name of the other * + * player). * + * Send/Get_Global_Message: adds a packet to the Global Connection queue, * + * or reads from the queue. The caller should check the * + * ProductID value from returned packets to be sure it's * + * talking to the right product. * + * Send/Get_Private_Message: adds a packet to a Private Connection queue, * + * or reads from the queue * + * Service: Checks the Real-Mode-Memory packet array to see if any * + * new packets have come in; if they have, it parses them * + * & distributes them to the right connection queue. The * + * queue's Service routine handles ACK'ing or Resending * + * packets. * + * * + * Here's a memory map of the Real-Mode memory block. 'N' is the number * + * of packet buffers allocated in low memory: * + * * + * ---------------------------------- * + * | Shared-memory data | * + * |--------------------------------| * + * | Real-mode event handler code | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 0 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 1 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 2 | * + * |--------------------------------| * + * | . . . | * + * |--------------------------------| * + * | IPX Header & Packet Buffer N | * + * |--------------------------------| * + * | Send Event Control Block | * + * |--------------------------------| * + * | Send IPX Header | * + * |--------------------------------| * + * | Send Packet Buffer | * + * |--------------------------------| * + * | Flags Array [N] | * + * ---------------------------------- * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXMANAGER_H +#define IPXMANAGER_H + + +/* +********************************* Includes ********************************** +*/ +#include "ipxconn.h" +#include "ipxgconn.h" +#include "ipxaddr.h" +#include "connmgr.h" + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is Virgin Interactive Entertainment's registered socket ID. +---------------------------------------------------------------------------*/ +#define VIRGIN_SOCKET 0x8813 + +/*--------------------------------------------------------------------------- +This is the maximum number of IPX connections supported. Just change this +value to support more. +---------------------------------------------------------------------------*/ +#define CONNECT_MAX 7 + +/*--------------------------------------------------------------------------- +These routines report the location & length of the real-mode routine, as +it's stored in protected-mode memory. +---------------------------------------------------------------------------*/ +extern "C" { + void * __cdecl Get_RM_IPX_Address(void); + long __cdecl Get_RM_IPX_Size(void); +} + +/* +***************************** Class Declaration ***************************** +*/ +class IPXManagerClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXManagerClass (int glb_maxlen, int pvt_maxlen, int glb_num_packets, + int pvt_num_packets, unsigned short socket, unsigned short product_id); + virtual ~IPXManagerClass (); // stop listening + + /*..................................................................... + Initialization routines. + .....................................................................*/ + int Init (void); + int Is_IPX(void); + virtual void Set_Timing (unsigned long retrydelta, unsigned long maxretries, + unsigned long timeout); + void Set_Bridge(NetNumType bridge); + + /*..................................................................... + These routines control creation of the "Connections" (data queues) for + each remote system. + .....................................................................*/ + int Create_Connection(int id, char *name, IPXAddressClass *address); + int Delete_Connection(int id); + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + char *Connection_Name(int id); + IPXAddressClass * Connection_Address(int id); + virtual int Connection_Index(int id); + void Set_Connection_Parms(int index, int id, char *name); + + /*..................................................................... + This is how the application sends & receives messages. + .....................................................................*/ + int Send_Global_Message (void *buf, int buflen, int ack_req = 0, + IPXAddressClass *address = NULL); + int Get_Global_Message (void *buf, int *buflen, IPXAddressClass *address, + unsigned short *product_id); + + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, int *conn_id); + + /*..................................................................... + The main polling routine; should be called as often as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine reports which connection has an error on it. + .....................................................................*/ + int Get_Bad_Connection(void); + + /*..................................................................... + Queue utility routines. The application can determine how many + messages are in the send/receive queues. + .....................................................................*/ + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + /*..................................................................... + This routine changes the socket ID assigned the IPX Manager when it + was constructed. Do not call this function after calling Init()! + The Socket ID should be known by both ends of the communications before + any packets are sent. + .....................................................................*/ + void Set_Socket(unsigned short socket); + + /*..................................................................... + Routines to return the largest average queue response time, and to + reset the response time for all queues. + .....................................................................*/ + virtual unsigned long Response_Time(void); + unsigned long Global_Response_Time(void); + virtual void Reset_Response_Time(void); + + /*..................................................................... + This routine returns a pointer to the oldest non-ACK'd buffer I've sent. + .....................................................................*/ + void * Oldest_Send(void); + + /*..................................................................... + Debug routines + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + These routines allocate & free the DOS Real-mode memory block. + .....................................................................*/ + int Alloc_RealMode_Mem(void); + int Free_RealMode_Mem(void); + + /*..................................................................... + Misc variables + .....................................................................*/ + unsigned int IPXStatus : 1; // 0 = no IPX, 1 = IPX found + unsigned int Listening : 1; // 1 = Listening is on + unsigned int RealMemAllocd : 1; // 1 = Real-mode memory has been alloc'd + + /*..................................................................... + Packet Sizes, used for allocating real-mode memory + .....................................................................*/ + int Glb_MaxPacketLen; // Global Channel maximum packet size + int Glb_NumPackets; // # Global send/receive packets + int Pvt_MaxPacketLen; // Private Channel maximum packet size + int Pvt_NumPackets; // # Private send/receive packets + + /*..................................................................... + The ProductID is used in the Global Channel's packet header, and it's + used for the Private Channels' Magic Number. + .....................................................................*/ + unsigned short ProductID; // product ID + + /*..................................................................... + The Socket ID, and local Novell Connection Number + .....................................................................*/ + unsigned short Socket; // Our socket ID for sending/receiving + int ConnectionNum; // local connection #, 0=not logged in + + /*..................................................................... + Array of connection queues + .....................................................................*/ + IPXConnClass * Connection[CONNECT_MAX]; // array of connection object ptrs + int NumConnections; // # connection objects in use + IPXGlobalConnClass *GlobalChannel; // the Global Channel + + /*..................................................................... + Current queue for polling for received packets + .....................................................................*/ + int CurConnection; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /*--------------------------------------------------------------------- + Real-mode memory pointers and such + ---------------------------------------------------------------------*/ + /*..................................................................... + This is a structure that mirrors data in real-mode memory: + .....................................................................*/ + typedef struct { + short Marker1; // the byte ID marker + ECBType ListenECB; // the Listening ECB + short NumBufs; // # of buffers we're giving to the handler + char *BufferFlags; // array of buffer-avail flags + short PacketSize; // size of packet including IPX header + IPXHeaderType *FirstPacketBuf; // ptr to 1st packet buffer + short CurIndex; // handler's current packet index + IPXHeaderType *CurPacketBuf; // handler's current packet buf + short FuncOffset; // contains offset of code + char Semaphore; // prevents re-entrancy + short ReEntrantCount; // times we've been called re-entrantly + short StackPtr; // real-mode stack pointer + short StackSeg; // real-mode stack segment + short StackPtr_int; // internal stack pointer + short StackSeg_int; // internal stack segment + short StackCheck; // stack check value (0x1234) + short Stack[256]; // actual stack space + short StackSpace; // label for top of stack + short Marker2; // the byte ID marker + } RealModeDataType; + + /*..................................................................... + The number & size of packet buffers in low memory + .....................................................................*/ + int NumBufs; // # packet buffers allocated + int PacketLen; // size of packet without IPX header + int FullPacketLen; // size of packet including IPX header + + /*..................................................................... + Selector & Segment of the DOS allocation; + Size of the allocation; + Ptr to the real-mode assembly data area + .....................................................................*/ + unsigned short Selector; // selector of DOS allocation pointer + unsigned short Segment; // real-mode segment of DOS allocation + int RealMemSize; // size of real mode memory allocated + RealModeDataType *RealModeData; // assembly routine & its data + + /*..................................................................... + This is a real-mode pointer to the address of the real-mode assembly + entry point. + .....................................................................*/ + long Handler; + + /*..................................................................... + Event Control Block for listening; contained within the real-mode + assembly routine's data area + .....................................................................*/ + ECBType *ListenECB; // ECB for listening + + /*..................................................................... + ptr to the 1st header & data buffers in the packet buffer array + .....................................................................*/ + IPXHeaderType *FirstHeaderBuf; // array of packet headers & buffers + char *FirstDataBuf; // 1st data buffer area + + /*..................................................................... + Current packet index & ptrs for parsing packets + .....................................................................*/ + int CurIndex; // Current packet index, for reading + IPXHeaderType *CurHeaderBuf; // Current packet ptr, for reading + char *CurDataBuf; // Current actual data ptr + + /*..................................................................... + ECB, header, & buffer for sending + .....................................................................*/ + ECBType *SendECB; // ECB for sending + IPXHeaderType *SendHeader; // Header for sending + char *SendBuf; // buffer for sending + + /*..................................................................... + Flags indicating whether a buffer contains data or not (1 = full) + The IPXManager must clear this flag; the real-mode routine will set it. + .....................................................................*/ + char *BufferFlags; // array of rx-buffer-avail flags + + /*..................................................................... + Various Statistics + .....................................................................*/ + int SendOverflows; + int ReceiveOverflows; + int BadConnection; +}; + +#endif + +/*************************** end of ipxmgr.h *******************************/ diff --git a/CODE/IPXPROT.ASM b/CODE/IPXPROT.ASM new file mode 100644 index 0000000..62b310a --- /dev/null +++ b/CODE/IPXPROT.ASM @@ -0,0 +1,113 @@ +; +; 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 . +; + + +;!!!!!!!!!!!!!!!!!!! lock the allocation + +;*************************************************************************** +;** 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 : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +GLOBAL C Get_RM_IPX_Address:NEAR +GLOBAL C Get_RM_IPX_Size:NEAR + +;********************************* Data ************************************ + DATASEG + +LABEL RealBinStart BYTE +include "obj\win32\ipxreal.ibn" +LABEL RealBinEnd BYTE + +;********************************* Data ************************************ + CODESEG + +;*************************************************************************** +;* Get_RM_IPX_Address -- Return address of real mode code for copy. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* VOID * to the address of the real mode IPX code * +;* * +;* PROTO: * +;* VOID *Get_RM_IPX_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* Get_RM_IPX_Size -- return size of real mode IPX code. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* LONG size of the real mode IPX code * +;* * +;* PROTO: * +;* LONG Get_RM_IPX_Size(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + END + +;************************** End of handler.asm ***************************** diff --git a/CODE/IPXREAL.ASM b/CODE/IPXREAL.ASM new file mode 100644 index 0000000..4ed4e3f --- /dev/null +++ b/CODE/IPXREAL.ASM @@ -0,0 +1,319 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +MODEL LARGE +P386N +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +GLOBAL IPXHandler:FAR + + +;********************************* Code ************************************ + CODESEG + + +;--------------------------------------------------------------------------- +; The markers let the application verify that it's mapping this memory +; correctly. +;--------------------------------------------------------------------------- +Marker1 DW 1111h ; placeholder to find data start + +;--------------------------------------------------------------------------- +; This is the IPX Event Control Block: +;--------------------------------------------------------------------------- +ECB_LinkAddress DD ? +ECB_EventServiceRoutine DD ? ; Event Handler ptr +ECB_InUse DB ? ; 0 = event is complete +ECB_CompletionCode DB ? ; 0 = OK, IPX error otherwise +ECB_SocketNumber DW ? ; socket to listen/send on +ECB_ConnectionID DW ? +ECB_RestOfWorkspace DW ? +ECB_DriverWorkSpace DB 12 DUP (?) +ECB_ImmediateAddress DB 6 DUP (?) ; bridge address +ECB_PacketCount DW ? ; # data areas (2) +ECB_HeaderAddress DD ? ; ptr to IPX header buffer +ECB_HeaderLength DW ? ; length of IPX header buffer +ECB_PacketAddress DD ? ; ptr to packet buffer +ECB_PacketLength DW ? ; length of packet buffer + +;--------------------------------------------------------------------------- +; The rest of the variables are for telling IPX which buffer to store the +; next incoming packet in. They must be initialized by the application. +;--------------------------------------------------------------------------- +NumBufs DW 0 ; # buffers provided by app +BufferFlags DD 0 ; array of in-use flags (1 = in use) +PacketSize DW 0 ; total size of 1 buf (incl IPX hdr) +FirstPacketBuf DD 0 ; ptr to 1st packet buffer +CurIndex DW 0 ; current packet/flag index +CurPacketBuf DD 0 ; ptr to current packet buf +FuncOffset DW StartLabel ; offset of our routine + +;--------------------------------------------------------------------------- +; These values are for preventing re-entrancy; they're currently not used. +;--------------------------------------------------------------------------- +Semaphore DB 0 ; prevents re-entrancy +ReEntrantCount DW 0 ; times we've been called re-entrantly + +;--------------------------------------------------------------------------- +; Local stack space +;--------------------------------------------------------------------------- +StackPtr DW 0 ; saved copy of stack ptr +StackSeg DW 0 ; saved copy of stack seg +StackPtr_int DW 0 ; our internal stack ptr +StackSeg_int DW 0 ; our internal stack seg +StackCheck DW 1234h ; check for stack overflow + DW 256 DUP (0) ; stack storage space +StackSpace DW 0 ; label for our stack space + +;--------------------------------------------------------------------------- +; These bytes mark the end of the real-mode data area +;--------------------------------------------------------------------------- +Marker2 DW 2222h ; placeholder to find data end + + +;*************************************************************************** +;* IPXHandler -- IPX callback routine * +;* * +;* This routine is assembled as a stand-alone executable, then loaded * +;* into low DOS memory by a protected-mode application. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 04/07/1995 BRR : Created. * +;*=========================================================================* + label StartLabel + PROC IPXHandler C FAR USES + + ;................................................................... + ; Turn off interrupts; make sure memory copies go forward + ;................................................................... + pushf + cli + cld + + ;................................................................... + ; Set up segment registers to point DS to CS + ;................................................................... + push ds + push ax + mov ax,cs + mov ds,ax + + ;................................................................... + ; Set up our local stack; save SS & SP first. + ;................................................................... + mov [StackSeg],ss + mov [StackPtr],sp + mov [StackPtr_int], OFFSET StackSpace + mov [StackSeg_int], SEG StackSpace + lss sp, [DWORD PTR StackPtr_int] + + + ;................................................................... + ; Save all registers + ;................................................................... + pushad + push es + + ;................................................................... + ; If we've been called re-entrantly, just exit + ;................................................................... + cmp [Semaphore],0 + jz ??Start_Handler + add [ReEntrantCount],1 + jmp ??Exit_Handler + +??Start_Handler: + ;................................................................... + ; Set our semaphore + ;................................................................... + mov [Semaphore],1 + + ;------------------------------------------------------------------- + ; Set 'CurIndex' to the index of the next-available receive buffer, + ; and 'CurPacketBuf to the next-available packet buffer + ;------------------------------------------------------------------- + ;................................................................... + ; Get 'CurIndex' & increment it. Wrap to 0 if we reach 'NumBufs' + ; Since I'm treating 'CurPacketBuf' as a long integer (and not as + ; a segment:offset), the entire data area can't be larger than 64K. + ;................................................................... + mov dx,[CurIndex] ; DX = CurIndex + mov eax,[CurPacketBuf] ; EAX = current packet buffer addr + inc dx ; DX = next index + add ax,[PacketSize] ; EAX = next buffer ptr + cmp dx,[NumBufs] ; see if DX is past # buffers + jb ??Get_Flag + mov dx,0 ; wrap to 1st index + mov eax,[FirstPacketBuf] ; wrap to 1st packet buffer + +??Get_Flag: + ;................................................................... + ; Get the next buffer-in-use flag; if it's 0, load [CurIndex] with + ; the value of SI (the next index). If it's 1, skip the updating of + ; the index, flag & buffer ptr. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ;................................................................... + les di,[BufferFlags] ; ES:DI = BufferFlags address + mov bx,di ; BX = DI + new CurIndex + add bx,dx + + cmp [BYTE PTR es:bx],0 ; compare next flag to 0 (avail) + jne ??Set_ECB ; if not avail, skip setting new values + + ;................................................................... + ; The next buffer is available; so, set this buffer's In-Use flag + ; to 1, and move on to the next buffer. Do not set this buffer's + ; flag to 1 until we move on to the next buffer, to prevent the + ; application from reading the currently-in-use packet buffer. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ; ES:DI = BufferFlags address + ;................................................................... + mov bx,di ; BX = DI + old CurIndex + add bx,[CurIndex] + mov [BYTE PTR es:bx],1 ; set old BufferFlags value to in-use + + mov [CurIndex],dx ; save new index + mov [CurPacketBuf],eax ; save new packet address + + ;------------------------------------------------------------------- + ; Set up the Event Control Block to tell IPX to start listening. + ; The following entries are filled in by the app, and should be left + ; alone: + ; - EventServiceRoutine + ; - SocketNumber + ; The rest should be re-initialized. Note that EBX is now pointing + ; to an unavailable buffer if the next buffer was already in use; + ; so it must be reloaded with the correct buffer address from + ; [CurPacketBuf]. + ;------------------------------------------------------------------- +??Set_ECB: + mov [ECB_LinkAddress],0 ; default + mov [ECB_InUse],0 ; default + mov [ECB_CompletionCode],0 ; default + mov [ECB_ConnectionID],0 ; default + mov [ECB_RestOfWorkspace],0 ; default + mov [ECB_DriverWorkSpace],0 ; default + mov [ECB_ImmediateAddress],0 ; default + mov [ECB_PacketCount],2 ; use 2 data areas + mov ebx,[CurPacketBuf] ; get current buffer address + mov [ECB_HeaderAddress],ebx ; set header address + mov [ECB_HeaderLength],30 ; size of IPX header + add ebx,30 ; point to past the header + mov [ECB_PacketAddress],ebx ; set packet data address + mov ax,[PacketSize] ; get size of one buffer + sub ax,30 ; remove size of IPX header + mov [ECB_PacketLength],ax ; set size of packet data + + ;------------------------------------------------------------------- + ; Clear the IPX header for this packet + ;------------------------------------------------------------------- + les di,[ECB_HeaderAddress] ; ES:DI = IPX Listen Header + mov cx,30 ; (30 bytes = size of header) + mov al,0 + rep stosb ; clear to 0's + + ;------------------------------------------------------------------- + ; Command IPX to start listening again. + ;------------------------------------------------------------------- + mov bx,4 ; IPX code for Listen + mov ax,ds ; ES = segment of ListenECB + mov es,ax + mov ax,OFFSET ECB_LinkAddress + mov si,ax ; ES:SI = address of ECB + int 07ah ; call IPX interrupt + + ;................................................................... + ; Clear our semaphore + ;................................................................... + mov [Semaphore],0 + +??Exit_Handler: + ;................................................................... + ; Pop values from our local stack + ;................................................................... + pop es + popad + + ;................................................................... + ; Check our stack-check value; if the stack has overflowed, generate + ; a debugger break. + ;................................................................... + cmp [StackCheck],1234h + je ??Restore_Stack + int 3 + + ;................................................................... + ; Restore the stack to its previous value + ;................................................................... +??Restore_Stack: + lss sp, [DWORD PTR StackPtr] + + ;................................................................... + ; Pop the rest of the registers + ;................................................................... + pop ax + pop ds + + popf + + ret + +ENDP IPXHandler + +END + +;************************** End of handler.asm ***************************** diff --git a/CODE/ITABLE.CPP b/CODE/ITABLE.CPP new file mode 100644 index 0000000..e5617a8 --- /dev/null +++ b/CODE/ITABLE.CPP @@ -0,0 +1,1446 @@ +/* +** 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 . +*/ + + +unsigned short IndexTable[] = { + 0, // Index = 0, Token = 0 + 0, // Index = 0, Token = 1 + 0, // Index = 0, Token = 2 + 0, // Index = 0, Token = 3 + 32, // Index = 0, Token = 4 + 64, // Index = 0, Token = 5 + 96, // Index = 0, Token = 6 + 128, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + 0, // Index = 0, Token = 9 + 0, // Index = 0, Token = 10 + 0, // Index = 0, Token = 11 + 32, // Index = 0, Token = 12 + 64, // Index = 0, Token = 13 + 96, // Index = 0, Token = 14 + 128, // Index = 0, Token = 15 + 0, // Index = 1, Token = 0 + 0, // Index = 1, Token = 1 + 0, // Index = 1, Token = 2 + 0, // Index = 1, Token = 3 + 48, // Index = 1, Token = 4 + 80, // Index = 1, Token = 5 + 112, // Index = 1, Token = 6 + 144, // Index = 1, Token = 7 + 0, // Index = 1, Token = 8 + 0, // Index = 1, Token = 9 + 0, // Index = 1, Token = 10 + 0, // Index = 1, Token = 11 + 48, // Index = 1, Token = 12 + 80, // Index = 1, Token = 13 + 112, // Index = 1, Token = 14 + 144, // Index = 1, Token = 15 + 16, // Index = 2, Token = 0 + 16, // Index = 2, Token = 1 + 16, // Index = 2, Token = 2 + 16, // Index = 2, Token = 3 + 64, // Index = 2, Token = 4 + 96, // Index = 2, Token = 5 + 128, // Index = 2, Token = 6 + 160, // Index = 2, Token = 7 + 16, // Index = 2, Token = 8 + 16, // Index = 2, Token = 9 + 16, // Index = 2, Token = 10 + 16, // Index = 2, Token = 11 + 64, // Index = 2, Token = 12 + 96, // Index = 2, Token = 13 + 128, // Index = 2, Token = 14 + 160, // Index = 2, Token = 15 + 32, // Index = 3, Token = 0 + 32, // Index = 3, Token = 1 + 32, // Index = 3, Token = 2 + 32, // Index = 3, Token = 3 + 80, // Index = 3, Token = 4 + 112, // Index = 3, Token = 5 + 144, // Index = 3, Token = 6 + 176, // Index = 3, Token = 7 + 32, // Index = 3, Token = 8 + 32, // Index = 3, Token = 9 + 32, // Index = 3, Token = 10 + 32, // Index = 3, Token = 11 + 80, // Index = 3, Token = 12 + 112, // Index = 3, Token = 13 + 144, // Index = 3, Token = 14 + 176, // Index = 3, Token = 15 + 48, // Index = 4, Token = 0 + 48, // Index = 4, Token = 1 + 48, // Index = 4, Token = 2 + 48, // Index = 4, Token = 3 + 96, // Index = 4, Token = 4 + 128, // Index = 4, Token = 5 + 160, // Index = 4, Token = 6 + 192, // Index = 4, Token = 7 + 48, // Index = 4, Token = 8 + 48, // Index = 4, Token = 9 + 48, // Index = 4, Token = 10 + 48, // Index = 4, Token = 11 + 96, // Index = 4, Token = 12 + 128, // Index = 4, Token = 13 + 160, // Index = 4, Token = 14 + 192, // Index = 4, Token = 15 + 64, // Index = 5, Token = 0 + 64, // Index = 5, Token = 1 + 64, // Index = 5, Token = 2 + 64, // Index = 5, Token = 3 + 112, // Index = 5, Token = 4 + 144, // Index = 5, Token = 5 + 176, // Index = 5, Token = 6 + 208, // Index = 5, Token = 7 + 64, // Index = 5, Token = 8 + 64, // Index = 5, Token = 9 + 64, // Index = 5, Token = 10 + 64, // Index = 5, Token = 11 + 112, // Index = 5, Token = 12 + 144, // Index = 5, Token = 13 + 176, // Index = 5, Token = 14 + 208, // Index = 5, Token = 15 + 80, // Index = 6, Token = 0 + 80, // Index = 6, Token = 1 + 80, // Index = 6, Token = 2 + 80, // Index = 6, Token = 3 + 128, // Index = 6, Token = 4 + 160, // Index = 6, Token = 5 + 192, // Index = 6, Token = 6 + 224, // Index = 6, Token = 7 + 80, // Index = 6, Token = 8 + 80, // Index = 6, Token = 9 + 80, // Index = 6, Token = 10 + 80, // Index = 6, Token = 11 + 128, // Index = 6, Token = 12 + 160, // Index = 6, Token = 13 + 192, // Index = 6, Token = 14 + 224, // Index = 6, Token = 15 + 96, // Index = 7, Token = 0 + 96, // Index = 7, Token = 1 + 96, // Index = 7, Token = 2 + 96, // Index = 7, Token = 3 + 144, // Index = 7, Token = 4 + 176, // Index = 7, Token = 5 + 208, // Index = 7, Token = 6 + 240, // Index = 7, Token = 7 + 96, // Index = 7, Token = 8 + 96, // Index = 7, Token = 9 + 96, // Index = 7, Token = 10 + 96, // Index = 7, Token = 11 + 144, // Index = 7, Token = 12 + 176, // Index = 7, Token = 13 + 208, // Index = 7, Token = 14 + 240, // Index = 7, Token = 15 + 112, // Index = 8, Token = 0 + 112, // Index = 8, Token = 1 + 112, // Index = 8, Token = 2 + 112, // Index = 8, Token = 3 + 160, // Index = 8, Token = 4 + 192, // Index = 8, Token = 5 + 224, // Index = 8, Token = 6 + 256, // Index = 8, Token = 7 + 112, // Index = 8, Token = 8 + 112, // Index = 8, Token = 9 + 112, // Index = 8, Token = 10 + 112, // Index = 8, Token = 11 + 160, // Index = 8, Token = 12 + 192, // Index = 8, Token = 13 + 224, // Index = 8, Token = 14 + 256, // Index = 8, Token = 15 + 128, // Index = 9, Token = 0 + 128, // Index = 9, Token = 1 + 128, // Index = 9, Token = 2 + 128, // Index = 9, Token = 3 + 176, // Index = 9, Token = 4 + 208, // Index = 9, Token = 5 + 240, // Index = 9, Token = 6 + 272, // Index = 9, Token = 7 + 128, // Index = 9, Token = 8 + 128, // Index = 9, Token = 9 + 128, // Index = 9, Token = 10 + 128, // Index = 9, Token = 11 + 176, // Index = 9, Token = 12 + 208, // Index = 9, Token = 13 + 240, // Index = 9, Token = 14 + 272, // Index = 9, Token = 15 + 144, // Index = 10, Token = 0 + 144, // Index = 10, Token = 1 + 144, // Index = 10, Token = 2 + 144, // Index = 10, Token = 3 + 192, // Index = 10, Token = 4 + 224, // Index = 10, Token = 5 + 256, // Index = 10, Token = 6 + 288, // Index = 10, Token = 7 + 144, // Index = 10, Token = 8 + 144, // Index = 10, Token = 9 + 144, // Index = 10, Token = 10 + 144, // Index = 10, Token = 11 + 192, // Index = 10, Token = 12 + 224, // Index = 10, Token = 13 + 256, // Index = 10, Token = 14 + 288, // Index = 10, Token = 15 + 160, // Index = 11, Token = 0 + 160, // Index = 11, Token = 1 + 160, // Index = 11, Token = 2 + 160, // Index = 11, Token = 3 + 208, // Index = 11, Token = 4 + 240, // Index = 11, Token = 5 + 272, // Index = 11, Token = 6 + 304, // Index = 11, Token = 7 + 160, // Index = 11, Token = 8 + 160, // Index = 11, Token = 9 + 160, // Index = 11, Token = 10 + 160, // Index = 11, Token = 11 + 208, // Index = 11, Token = 12 + 240, // Index = 11, Token = 13 + 272, // Index = 11, Token = 14 + 304, // Index = 11, Token = 15 + 176, // Index = 12, Token = 0 + 176, // Index = 12, Token = 1 + 176, // Index = 12, Token = 2 + 176, // Index = 12, Token = 3 + 224, // Index = 12, Token = 4 + 256, // Index = 12, Token = 5 + 288, // Index = 12, Token = 6 + 320, // Index = 12, Token = 7 + 176, // Index = 12, Token = 8 + 176, // Index = 12, Token = 9 + 176, // Index = 12, Token = 10 + 176, // Index = 12, Token = 11 + 224, // Index = 12, Token = 12 + 256, // Index = 12, Token = 13 + 288, // Index = 12, Token = 14 + 320, // Index = 12, Token = 15 + 192, // Index = 13, Token = 0 + 192, // Index = 13, Token = 1 + 192, // Index = 13, Token = 2 + 192, // Index = 13, Token = 3 + 240, // Index = 13, Token = 4 + 272, // Index = 13, Token = 5 + 304, // Index = 13, Token = 6 + 336, // Index = 13, Token = 7 + 192, // Index = 13, Token = 8 + 192, // Index = 13, Token = 9 + 192, // Index = 13, Token = 10 + 192, // Index = 13, Token = 11 + 240, // Index = 13, Token = 12 + 272, // Index = 13, Token = 13 + 304, // Index = 13, Token = 14 + 336, // Index = 13, Token = 15 + 208, // Index = 14, Token = 0 + 208, // Index = 14, Token = 1 + 208, // Index = 14, Token = 2 + 208, // Index = 14, Token = 3 + 256, // Index = 14, Token = 4 + 288, // Index = 14, Token = 5 + 320, // Index = 14, Token = 6 + 352, // Index = 14, Token = 7 + 208, // Index = 14, Token = 8 + 208, // Index = 14, Token = 9 + 208, // Index = 14, Token = 10 + 208, // Index = 14, Token = 11 + 256, // Index = 14, Token = 12 + 288, // Index = 14, Token = 13 + 320, // Index = 14, Token = 14 + 352, // Index = 14, Token = 15 + 224, // Index = 15, Token = 0 + 224, // Index = 15, Token = 1 + 224, // Index = 15, Token = 2 + 224, // Index = 15, Token = 3 + 272, // Index = 15, Token = 4 + 304, // Index = 15, Token = 5 + 336, // Index = 15, Token = 6 + 368, // Index = 15, Token = 7 + 224, // Index = 15, Token = 8 + 224, // Index = 15, Token = 9 + 224, // Index = 15, Token = 10 + 224, // Index = 15, Token = 11 + 272, // Index = 15, Token = 12 + 304, // Index = 15, Token = 13 + 336, // Index = 15, Token = 14 + 368, // Index = 15, Token = 15 + 240, // Index = 16, Token = 0 + 240, // Index = 16, Token = 1 + 240, // Index = 16, Token = 2 + 240, // Index = 16, Token = 3 + 288, // Index = 16, Token = 4 + 320, // Index = 16, Token = 5 + 352, // Index = 16, Token = 6 + 384, // Index = 16, Token = 7 + 240, // Index = 16, Token = 8 + 240, // Index = 16, Token = 9 + 240, // Index = 16, Token = 10 + 240, // Index = 16, Token = 11 + 288, // Index = 16, Token = 12 + 320, // Index = 16, Token = 13 + 352, // Index = 16, Token = 14 + 384, // Index = 16, Token = 15 + 256, // Index = 17, Token = 0 + 256, // Index = 17, Token = 1 + 256, // Index = 17, Token = 2 + 256, // Index = 17, Token = 3 + 304, // Index = 17, Token = 4 + 336, // Index = 17, Token = 5 + 368, // Index = 17, Token = 6 + 400, // Index = 17, Token = 7 + 256, // Index = 17, Token = 8 + 256, // Index = 17, Token = 9 + 256, // Index = 17, Token = 10 + 256, // Index = 17, Token = 11 + 304, // Index = 17, Token = 12 + 336, // Index = 17, Token = 13 + 368, // Index = 17, Token = 14 + 400, // Index = 17, Token = 15 + 272, // Index = 18, Token = 0 + 272, // Index = 18, Token = 1 + 272, // Index = 18, Token = 2 + 272, // Index = 18, Token = 3 + 320, // Index = 18, Token = 4 + 352, // Index = 18, Token = 5 + 384, // Index = 18, Token = 6 + 416, // Index = 18, Token = 7 + 272, // Index = 18, Token = 8 + 272, // Index = 18, Token = 9 + 272, // Index = 18, Token = 10 + 272, // Index = 18, Token = 11 + 320, // Index = 18, Token = 12 + 352, // Index = 18, Token = 13 + 384, // Index = 18, Token = 14 + 416, // Index = 18, Token = 15 + 288, // Index = 19, Token = 0 + 288, // Index = 19, Token = 1 + 288, // Index = 19, Token = 2 + 288, // Index = 19, Token = 3 + 336, // Index = 19, Token = 4 + 368, // Index = 19, Token = 5 + 400, // Index = 19, Token = 6 + 432, // Index = 19, Token = 7 + 288, // Index = 19, Token = 8 + 288, // Index = 19, Token = 9 + 288, // Index = 19, Token = 10 + 288, // Index = 19, Token = 11 + 336, // Index = 19, Token = 12 + 368, // Index = 19, Token = 13 + 400, // Index = 19, Token = 14 + 432, // Index = 19, Token = 15 + 304, // Index = 20, Token = 0 + 304, // Index = 20, Token = 1 + 304, // Index = 20, Token = 2 + 304, // Index = 20, Token = 3 + 352, // Index = 20, Token = 4 + 384, // Index = 20, Token = 5 + 416, // Index = 20, Token = 6 + 448, // Index = 20, Token = 7 + 304, // Index = 20, Token = 8 + 304, // Index = 20, Token = 9 + 304, // Index = 20, Token = 10 + 304, // Index = 20, Token = 11 + 352, // Index = 20, Token = 12 + 384, // Index = 20, Token = 13 + 416, // Index = 20, Token = 14 + 448, // Index = 20, Token = 15 + 320, // Index = 21, Token = 0 + 320, // Index = 21, Token = 1 + 320, // Index = 21, Token = 2 + 320, // Index = 21, Token = 3 + 368, // Index = 21, Token = 4 + 400, // Index = 21, Token = 5 + 432, // Index = 21, Token = 6 + 464, // Index = 21, Token = 7 + 320, // Index = 21, Token = 8 + 320, // Index = 21, Token = 9 + 320, // Index = 21, Token = 10 + 320, // Index = 21, Token = 11 + 368, // Index = 21, Token = 12 + 400, // Index = 21, Token = 13 + 432, // Index = 21, Token = 14 + 464, // Index = 21, Token = 15 + 336, // Index = 22, Token = 0 + 336, // Index = 22, Token = 1 + 336, // Index = 22, Token = 2 + 336, // Index = 22, Token = 3 + 384, // Index = 22, Token = 4 + 416, // Index = 22, Token = 5 + 448, // Index = 22, Token = 6 + 480, // Index = 22, Token = 7 + 336, // Index = 22, Token = 8 + 336, // Index = 22, Token = 9 + 336, // Index = 22, Token = 10 + 336, // Index = 22, Token = 11 + 384, // Index = 22, Token = 12 + 416, // Index = 22, Token = 13 + 448, // Index = 22, Token = 14 + 480, // Index = 22, Token = 15 + 352, // Index = 23, Token = 0 + 352, // Index = 23, Token = 1 + 352, // Index = 23, Token = 2 + 352, // Index = 23, Token = 3 + 400, // Index = 23, Token = 4 + 432, // Index = 23, Token = 5 + 464, // Index = 23, Token = 6 + 496, // Index = 23, Token = 7 + 352, // Index = 23, Token = 8 + 352, // Index = 23, Token = 9 + 352, // Index = 23, Token = 10 + 352, // Index = 23, Token = 11 + 400, // Index = 23, Token = 12 + 432, // Index = 23, Token = 13 + 464, // Index = 23, Token = 14 + 496, // Index = 23, Token = 15 + 368, // Index = 24, Token = 0 + 368, // Index = 24, Token = 1 + 368, // Index = 24, Token = 2 + 368, // Index = 24, Token = 3 + 416, // Index = 24, Token = 4 + 448, // Index = 24, Token = 5 + 480, // Index = 24, Token = 6 + 512, // Index = 24, Token = 7 + 368, // Index = 24, Token = 8 + 368, // Index = 24, Token = 9 + 368, // Index = 24, Token = 10 + 368, // Index = 24, Token = 11 + 416, // Index = 24, Token = 12 + 448, // Index = 24, Token = 13 + 480, // Index = 24, Token = 14 + 512, // Index = 24, Token = 15 + 384, // Index = 25, Token = 0 + 384, // Index = 25, Token = 1 + 384, // Index = 25, Token = 2 + 384, // Index = 25, Token = 3 + 432, // Index = 25, Token = 4 + 464, // Index = 25, Token = 5 + 496, // Index = 25, Token = 6 + 528, // Index = 25, Token = 7 + 384, // Index = 25, Token = 8 + 384, // Index = 25, Token = 9 + 384, // Index = 25, Token = 10 + 384, // Index = 25, Token = 11 + 432, // Index = 25, Token = 12 + 464, // Index = 25, Token = 13 + 496, // Index = 25, Token = 14 + 528, // Index = 25, Token = 15 + 400, // Index = 26, Token = 0 + 400, // Index = 26, Token = 1 + 400, // Index = 26, Token = 2 + 400, // Index = 26, Token = 3 + 448, // Index = 26, Token = 4 + 480, // Index = 26, Token = 5 + 512, // Index = 26, Token = 6 + 544, // Index = 26, Token = 7 + 400, // Index = 26, Token = 8 + 400, // Index = 26, Token = 9 + 400, // Index = 26, Token = 10 + 400, // Index = 26, Token = 11 + 448, // Index = 26, Token = 12 + 480, // Index = 26, Token = 13 + 512, // Index = 26, Token = 14 + 544, // Index = 26, Token = 15 + 416, // Index = 27, Token = 0 + 416, // Index = 27, Token = 1 + 416, // Index = 27, Token = 2 + 416, // Index = 27, Token = 3 + 464, // Index = 27, Token = 4 + 496, // Index = 27, Token = 5 + 528, // Index = 27, Token = 6 + 560, // Index = 27, Token = 7 + 416, // Index = 27, Token = 8 + 416, // Index = 27, Token = 9 + 416, // Index = 27, Token = 10 + 416, // Index = 27, Token = 11 + 464, // Index = 27, Token = 12 + 496, // Index = 27, Token = 13 + 528, // Index = 27, Token = 14 + 560, // Index = 27, Token = 15 + 432, // Index = 28, Token = 0 + 432, // Index = 28, Token = 1 + 432, // Index = 28, Token = 2 + 432, // Index = 28, Token = 3 + 480, // Index = 28, Token = 4 + 512, // Index = 28, Token = 5 + 544, // Index = 28, Token = 6 + 576, // Index = 28, Token = 7 + 432, // Index = 28, Token = 8 + 432, // Index = 28, Token = 9 + 432, // Index = 28, Token = 10 + 432, // Index = 28, Token = 11 + 480, // Index = 28, Token = 12 + 512, // Index = 28, Token = 13 + 544, // Index = 28, Token = 14 + 576, // Index = 28, Token = 15 + 448, // Index = 29, Token = 0 + 448, // Index = 29, Token = 1 + 448, // Index = 29, Token = 2 + 448, // Index = 29, Token = 3 + 496, // Index = 29, Token = 4 + 528, // Index = 29, Token = 5 + 560, // Index = 29, Token = 6 + 592, // Index = 29, Token = 7 + 448, // Index = 29, Token = 8 + 448, // Index = 29, Token = 9 + 448, // Index = 29, Token = 10 + 448, // Index = 29, Token = 11 + 496, // Index = 29, Token = 12 + 528, // Index = 29, Token = 13 + 560, // Index = 29, Token = 14 + 592, // Index = 29, Token = 15 + 464, // Index = 30, Token = 0 + 464, // Index = 30, Token = 1 + 464, // Index = 30, Token = 2 + 464, // Index = 30, Token = 3 + 512, // Index = 30, Token = 4 + 544, // Index = 30, Token = 5 + 576, // Index = 30, Token = 6 + 608, // Index = 30, Token = 7 + 464, // Index = 30, Token = 8 + 464, // Index = 30, Token = 9 + 464, // Index = 30, Token = 10 + 464, // Index = 30, Token = 11 + 512, // Index = 30, Token = 12 + 544, // Index = 30, Token = 13 + 576, // Index = 30, Token = 14 + 608, // Index = 30, Token = 15 + 480, // Index = 31, Token = 0 + 480, // Index = 31, Token = 1 + 480, // Index = 31, Token = 2 + 480, // Index = 31, Token = 3 + 528, // Index = 31, Token = 4 + 560, // Index = 31, Token = 5 + 592, // Index = 31, Token = 6 + 624, // Index = 31, Token = 7 + 480, // Index = 31, Token = 8 + 480, // Index = 31, Token = 9 + 480, // Index = 31, Token = 10 + 480, // Index = 31, Token = 11 + 528, // Index = 31, Token = 12 + 560, // Index = 31, Token = 13 + 592, // Index = 31, Token = 14 + 624, // Index = 31, Token = 15 + 496, // Index = 32, Token = 0 + 496, // Index = 32, Token = 1 + 496, // Index = 32, Token = 2 + 496, // Index = 32, Token = 3 + 544, // Index = 32, Token = 4 + 576, // Index = 32, Token = 5 + 608, // Index = 32, Token = 6 + 640, // Index = 32, Token = 7 + 496, // Index = 32, Token = 8 + 496, // Index = 32, Token = 9 + 496, // Index = 32, Token = 10 + 496, // Index = 32, Token = 11 + 544, // Index = 32, Token = 12 + 576, // Index = 32, Token = 13 + 608, // Index = 32, Token = 14 + 640, // Index = 32, Token = 15 + 512, // Index = 33, Token = 0 + 512, // Index = 33, Token = 1 + 512, // Index = 33, Token = 2 + 512, // Index = 33, Token = 3 + 560, // Index = 33, Token = 4 + 592, // Index = 33, Token = 5 + 624, // Index = 33, Token = 6 + 656, // Index = 33, Token = 7 + 512, // Index = 33, Token = 8 + 512, // Index = 33, Token = 9 + 512, // Index = 33, Token = 10 + 512, // Index = 33, Token = 11 + 560, // Index = 33, Token = 12 + 592, // Index = 33, Token = 13 + 624, // Index = 33, Token = 14 + 656, // Index = 33, Token = 15 + 528, // Index = 34, Token = 0 + 528, // Index = 34, Token = 1 + 528, // Index = 34, Token = 2 + 528, // Index = 34, Token = 3 + 576, // Index = 34, Token = 4 + 608, // Index = 34, Token = 5 + 640, // Index = 34, Token = 6 + 672, // Index = 34, Token = 7 + 528, // Index = 34, Token = 8 + 528, // Index = 34, Token = 9 + 528, // Index = 34, Token = 10 + 528, // Index = 34, Token = 11 + 576, // Index = 34, Token = 12 + 608, // Index = 34, Token = 13 + 640, // Index = 34, Token = 14 + 672, // Index = 34, Token = 15 + 544, // Index = 35, Token = 0 + 544, // Index = 35, Token = 1 + 544, // Index = 35, Token = 2 + 544, // Index = 35, Token = 3 + 592, // Index = 35, Token = 4 + 624, // Index = 35, Token = 5 + 656, // Index = 35, Token = 6 + 688, // Index = 35, Token = 7 + 544, // Index = 35, Token = 8 + 544, // Index = 35, Token = 9 + 544, // Index = 35, Token = 10 + 544, // Index = 35, Token = 11 + 592, // Index = 35, Token = 12 + 624, // Index = 35, Token = 13 + 656, // Index = 35, Token = 14 + 688, // Index = 35, Token = 15 + 560, // Index = 36, Token = 0 + 560, // Index = 36, Token = 1 + 560, // Index = 36, Token = 2 + 560, // Index = 36, Token = 3 + 608, // Index = 36, Token = 4 + 640, // Index = 36, Token = 5 + 672, // Index = 36, Token = 6 + 704, // Index = 36, Token = 7 + 560, // Index = 36, Token = 8 + 560, // Index = 36, Token = 9 + 560, // Index = 36, Token = 10 + 560, // Index = 36, Token = 11 + 608, // Index = 36, Token = 12 + 640, // Index = 36, Token = 13 + 672, // Index = 36, Token = 14 + 704, // Index = 36, Token = 15 + 576, // Index = 37, Token = 0 + 576, // Index = 37, Token = 1 + 576, // Index = 37, Token = 2 + 576, // Index = 37, Token = 3 + 624, // Index = 37, Token = 4 + 656, // Index = 37, Token = 5 + 688, // Index = 37, Token = 6 + 720, // Index = 37, Token = 7 + 576, // Index = 37, Token = 8 + 576, // Index = 37, Token = 9 + 576, // Index = 37, Token = 10 + 576, // Index = 37, Token = 11 + 624, // Index = 37, Token = 12 + 656, // Index = 37, Token = 13 + 688, // Index = 37, Token = 14 + 720, // Index = 37, Token = 15 + 592, // Index = 38, Token = 0 + 592, // Index = 38, Token = 1 + 592, // Index = 38, Token = 2 + 592, // Index = 38, Token = 3 + 640, // Index = 38, Token = 4 + 672, // Index = 38, Token = 5 + 704, // Index = 38, Token = 6 + 736, // Index = 38, Token = 7 + 592, // Index = 38, Token = 8 + 592, // Index = 38, Token = 9 + 592, // Index = 38, Token = 10 + 592, // Index = 38, Token = 11 + 640, // Index = 38, Token = 12 + 672, // Index = 38, Token = 13 + 704, // Index = 38, Token = 14 + 736, // Index = 38, Token = 15 + 608, // Index = 39, Token = 0 + 608, // Index = 39, Token = 1 + 608, // Index = 39, Token = 2 + 608, // Index = 39, Token = 3 + 656, // Index = 39, Token = 4 + 688, // Index = 39, Token = 5 + 720, // Index = 39, Token = 6 + 752, // Index = 39, Token = 7 + 608, // Index = 39, Token = 8 + 608, // Index = 39, Token = 9 + 608, // Index = 39, Token = 10 + 608, // Index = 39, Token = 11 + 656, // Index = 39, Token = 12 + 688, // Index = 39, Token = 13 + 720, // Index = 39, Token = 14 + 752, // Index = 39, Token = 15 + 624, // Index = 40, Token = 0 + 624, // Index = 40, Token = 1 + 624, // Index = 40, Token = 2 + 624, // Index = 40, Token = 3 + 672, // Index = 40, Token = 4 + 704, // Index = 40, Token = 5 + 736, // Index = 40, Token = 6 + 768, // Index = 40, Token = 7 + 624, // Index = 40, Token = 8 + 624, // Index = 40, Token = 9 + 624, // Index = 40, Token = 10 + 624, // Index = 40, Token = 11 + 672, // Index = 40, Token = 12 + 704, // Index = 40, Token = 13 + 736, // Index = 40, Token = 14 + 768, // Index = 40, Token = 15 + 640, // Index = 41, Token = 0 + 640, // Index = 41, Token = 1 + 640, // Index = 41, Token = 2 + 640, // Index = 41, Token = 3 + 688, // Index = 41, Token = 4 + 720, // Index = 41, Token = 5 + 752, // Index = 41, Token = 6 + 784, // Index = 41, Token = 7 + 640, // Index = 41, Token = 8 + 640, // Index = 41, Token = 9 + 640, // Index = 41, Token = 10 + 640, // Index = 41, Token = 11 + 688, // Index = 41, Token = 12 + 720, // Index = 41, Token = 13 + 752, // Index = 41, Token = 14 + 784, // Index = 41, Token = 15 + 656, // Index = 42, Token = 0 + 656, // Index = 42, Token = 1 + 656, // Index = 42, Token = 2 + 656, // Index = 42, Token = 3 + 704, // Index = 42, Token = 4 + 736, // Index = 42, Token = 5 + 768, // Index = 42, Token = 6 + 800, // Index = 42, Token = 7 + 656, // Index = 42, Token = 8 + 656, // Index = 42, Token = 9 + 656, // Index = 42, Token = 10 + 656, // Index = 42, Token = 11 + 704, // Index = 42, Token = 12 + 736, // Index = 42, Token = 13 + 768, // Index = 42, Token = 14 + 800, // Index = 42, Token = 15 + 672, // Index = 43, Token = 0 + 672, // Index = 43, Token = 1 + 672, // Index = 43, Token = 2 + 672, // Index = 43, Token = 3 + 720, // Index = 43, Token = 4 + 752, // Index = 43, Token = 5 + 784, // Index = 43, Token = 6 + 816, // Index = 43, Token = 7 + 672, // Index = 43, Token = 8 + 672, // Index = 43, Token = 9 + 672, // Index = 43, Token = 10 + 672, // Index = 43, Token = 11 + 720, // Index = 43, Token = 12 + 752, // Index = 43, Token = 13 + 784, // Index = 43, Token = 14 + 816, // Index = 43, Token = 15 + 688, // Index = 44, Token = 0 + 688, // Index = 44, Token = 1 + 688, // Index = 44, Token = 2 + 688, // Index = 44, Token = 3 + 736, // Index = 44, Token = 4 + 768, // Index = 44, Token = 5 + 800, // Index = 44, Token = 6 + 832, // Index = 44, Token = 7 + 688, // Index = 44, Token = 8 + 688, // Index = 44, Token = 9 + 688, // Index = 44, Token = 10 + 688, // Index = 44, Token = 11 + 736, // Index = 44, Token = 12 + 768, // Index = 44, Token = 13 + 800, // Index = 44, Token = 14 + 832, // Index = 44, Token = 15 + 704, // Index = 45, Token = 0 + 704, // Index = 45, Token = 1 + 704, // Index = 45, Token = 2 + 704, // Index = 45, Token = 3 + 752, // Index = 45, Token = 4 + 784, // Index = 45, Token = 5 + 816, // Index = 45, Token = 6 + 848, // Index = 45, Token = 7 + 704, // Index = 45, Token = 8 + 704, // Index = 45, Token = 9 + 704, // Index = 45, Token = 10 + 704, // Index = 45, Token = 11 + 752, // Index = 45, Token = 12 + 784, // Index = 45, Token = 13 + 816, // Index = 45, Token = 14 + 848, // Index = 45, Token = 15 + 720, // Index = 46, Token = 0 + 720, // Index = 46, Token = 1 + 720, // Index = 46, Token = 2 + 720, // Index = 46, Token = 3 + 768, // Index = 46, Token = 4 + 800, // Index = 46, Token = 5 + 832, // Index = 46, Token = 6 + 864, // Index = 46, Token = 7 + 720, // Index = 46, Token = 8 + 720, // Index = 46, Token = 9 + 720, // Index = 46, Token = 10 + 720, // Index = 46, Token = 11 + 768, // Index = 46, Token = 12 + 800, // Index = 46, Token = 13 + 832, // Index = 46, Token = 14 + 864, // Index = 46, Token = 15 + 736, // Index = 47, Token = 0 + 736, // Index = 47, Token = 1 + 736, // Index = 47, Token = 2 + 736, // Index = 47, Token = 3 + 784, // Index = 47, Token = 4 + 816, // Index = 47, Token = 5 + 848, // Index = 47, Token = 6 + 880, // Index = 47, Token = 7 + 736, // Index = 47, Token = 8 + 736, // Index = 47, Token = 9 + 736, // Index = 47, Token = 10 + 736, // Index = 47, Token = 11 + 784, // Index = 47, Token = 12 + 816, // Index = 47, Token = 13 + 848, // Index = 47, Token = 14 + 880, // Index = 47, Token = 15 + 752, // Index = 48, Token = 0 + 752, // Index = 48, Token = 1 + 752, // Index = 48, Token = 2 + 752, // Index = 48, Token = 3 + 800, // Index = 48, Token = 4 + 832, // Index = 48, Token = 5 + 864, // Index = 48, Token = 6 + 896, // Index = 48, Token = 7 + 752, // Index = 48, Token = 8 + 752, // Index = 48, Token = 9 + 752, // Index = 48, Token = 10 + 752, // Index = 48, Token = 11 + 800, // Index = 48, Token = 12 + 832, // Index = 48, Token = 13 + 864, // Index = 48, Token = 14 + 896, // Index = 48, Token = 15 + 768, // Index = 49, Token = 0 + 768, // Index = 49, Token = 1 + 768, // Index = 49, Token = 2 + 768, // Index = 49, Token = 3 + 816, // Index = 49, Token = 4 + 848, // Index = 49, Token = 5 + 880, // Index = 49, Token = 6 + 912, // Index = 49, Token = 7 + 768, // Index = 49, Token = 8 + 768, // Index = 49, Token = 9 + 768, // Index = 49, Token = 10 + 768, // Index = 49, Token = 11 + 816, // Index = 49, Token = 12 + 848, // Index = 49, Token = 13 + 880, // Index = 49, Token = 14 + 912, // Index = 49, Token = 15 + 784, // Index = 50, Token = 0 + 784, // Index = 50, Token = 1 + 784, // Index = 50, Token = 2 + 784, // Index = 50, Token = 3 + 832, // Index = 50, Token = 4 + 864, // Index = 50, Token = 5 + 896, // Index = 50, Token = 6 + 928, // Index = 50, Token = 7 + 784, // Index = 50, Token = 8 + 784, // Index = 50, Token = 9 + 784, // Index = 50, Token = 10 + 784, // Index = 50, Token = 11 + 832, // Index = 50, Token = 12 + 864, // Index = 50, Token = 13 + 896, // Index = 50, Token = 14 + 928, // Index = 50, Token = 15 + 800, // Index = 51, Token = 0 + 800, // Index = 51, Token = 1 + 800, // Index = 51, Token = 2 + 800, // Index = 51, Token = 3 + 848, // Index = 51, Token = 4 + 880, // Index = 51, Token = 5 + 912, // Index = 51, Token = 6 + 944, // Index = 51, Token = 7 + 800, // Index = 51, Token = 8 + 800, // Index = 51, Token = 9 + 800, // Index = 51, Token = 10 + 800, // Index = 51, Token = 11 + 848, // Index = 51, Token = 12 + 880, // Index = 51, Token = 13 + 912, // Index = 51, Token = 14 + 944, // Index = 51, Token = 15 + 816, // Index = 52, Token = 0 + 816, // Index = 52, Token = 1 + 816, // Index = 52, Token = 2 + 816, // Index = 52, Token = 3 + 864, // Index = 52, Token = 4 + 896, // Index = 52, Token = 5 + 928, // Index = 52, Token = 6 + 960, // Index = 52, Token = 7 + 816, // Index = 52, Token = 8 + 816, // Index = 52, Token = 9 + 816, // Index = 52, Token = 10 + 816, // Index = 52, Token = 11 + 864, // Index = 52, Token = 12 + 896, // Index = 52, Token = 13 + 928, // Index = 52, Token = 14 + 960, // Index = 52, Token = 15 + 832, // Index = 53, Token = 0 + 832, // Index = 53, Token = 1 + 832, // Index = 53, Token = 2 + 832, // Index = 53, Token = 3 + 880, // Index = 53, Token = 4 + 912, // Index = 53, Token = 5 + 944, // Index = 53, Token = 6 + 976, // Index = 53, Token = 7 + 832, // Index = 53, Token = 8 + 832, // Index = 53, Token = 9 + 832, // Index = 53, Token = 10 + 832, // Index = 53, Token = 11 + 880, // Index = 53, Token = 12 + 912, // Index = 53, Token = 13 + 944, // Index = 53, Token = 14 + 976, // Index = 53, Token = 15 + 848, // Index = 54, Token = 0 + 848, // Index = 54, Token = 1 + 848, // Index = 54, Token = 2 + 848, // Index = 54, Token = 3 + 896, // Index = 54, Token = 4 + 928, // Index = 54, Token = 5 + 960, // Index = 54, Token = 6 + 992, // Index = 54, Token = 7 + 848, // Index = 54, Token = 8 + 848, // Index = 54, Token = 9 + 848, // Index = 54, Token = 10 + 848, // Index = 54, Token = 11 + 896, // Index = 54, Token = 12 + 928, // Index = 54, Token = 13 + 960, // Index = 54, Token = 14 + 992, // Index = 54, Token = 15 + 864, // Index = 55, Token = 0 + 864, // Index = 55, Token = 1 + 864, // Index = 55, Token = 2 + 864, // Index = 55, Token = 3 + 912, // Index = 55, Token = 4 + 944, // Index = 55, Token = 5 + 976, // Index = 55, Token = 6 + 1008, // Index = 55, Token = 7 + 864, // Index = 55, Token = 8 + 864, // Index = 55, Token = 9 + 864, // Index = 55, Token = 10 + 864, // Index = 55, Token = 11 + 912, // Index = 55, Token = 12 + 944, // Index = 55, Token = 13 + 976, // Index = 55, Token = 14 + 1008, // Index = 55, Token = 15 + 880, // Index = 56, Token = 0 + 880, // Index = 56, Token = 1 + 880, // Index = 56, Token = 2 + 880, // Index = 56, Token = 3 + 928, // Index = 56, Token = 4 + 960, // Index = 56, Token = 5 + 992, // Index = 56, Token = 6 + 1024, // Index = 56, Token = 7 + 880, // Index = 56, Token = 8 + 880, // Index = 56, Token = 9 + 880, // Index = 56, Token = 10 + 880, // Index = 56, Token = 11 + 928, // Index = 56, Token = 12 + 960, // Index = 56, Token = 13 + 992, // Index = 56, Token = 14 + 1024, // Index = 56, Token = 15 + 896, // Index = 57, Token = 0 + 896, // Index = 57, Token = 1 + 896, // Index = 57, Token = 2 + 896, // Index = 57, Token = 3 + 944, // Index = 57, Token = 4 + 976, // Index = 57, Token = 5 + 1008, // Index = 57, Token = 6 + 1040, // Index = 57, Token = 7 + 896, // Index = 57, Token = 8 + 896, // Index = 57, Token = 9 + 896, // Index = 57, Token = 10 + 896, // Index = 57, Token = 11 + 944, // Index = 57, Token = 12 + 976, // Index = 57, Token = 13 + 1008, // Index = 57, Token = 14 + 1040, // Index = 57, Token = 15 + 912, // Index = 58, Token = 0 + 912, // Index = 58, Token = 1 + 912, // Index = 58, Token = 2 + 912, // Index = 58, Token = 3 + 960, // Index = 58, Token = 4 + 992, // Index = 58, Token = 5 + 1024, // Index = 58, Token = 6 + 1056, // Index = 58, Token = 7 + 912, // Index = 58, Token = 8 + 912, // Index = 58, Token = 9 + 912, // Index = 58, Token = 10 + 912, // Index = 58, Token = 11 + 960, // Index = 58, Token = 12 + 992, // Index = 58, Token = 13 + 1024, // Index = 58, Token = 14 + 1056, // Index = 58, Token = 15 + 928, // Index = 59, Token = 0 + 928, // Index = 59, Token = 1 + 928, // Index = 59, Token = 2 + 928, // Index = 59, Token = 3 + 976, // Index = 59, Token = 4 + 1008, // Index = 59, Token = 5 + 1040, // Index = 59, Token = 6 + 1072, // Index = 59, Token = 7 + 928, // Index = 59, Token = 8 + 928, // Index = 59, Token = 9 + 928, // Index = 59, Token = 10 + 928, // Index = 59, Token = 11 + 976, // Index = 59, Token = 12 + 1008, // Index = 59, Token = 13 + 1040, // Index = 59, Token = 14 + 1072, // Index = 59, Token = 15 + 944, // Index = 60, Token = 0 + 944, // Index = 60, Token = 1 + 944, // Index = 60, Token = 2 + 944, // Index = 60, Token = 3 + 992, // Index = 60, Token = 4 + 1024, // Index = 60, Token = 5 + 1056, // Index = 60, Token = 6 + 1088, // Index = 60, Token = 7 + 944, // Index = 60, Token = 8 + 944, // Index = 60, Token = 9 + 944, // Index = 60, Token = 10 + 944, // Index = 60, Token = 11 + 992, // Index = 60, Token = 12 + 1024, // Index = 60, Token = 13 + 1056, // Index = 60, Token = 14 + 1088, // Index = 60, Token = 15 + 960, // Index = 61, Token = 0 + 960, // Index = 61, Token = 1 + 960, // Index = 61, Token = 2 + 960, // Index = 61, Token = 3 + 1008, // Index = 61, Token = 4 + 1040, // Index = 61, Token = 5 + 1072, // Index = 61, Token = 6 + 1104, // Index = 61, Token = 7 + 960, // Index = 61, Token = 8 + 960, // Index = 61, Token = 9 + 960, // Index = 61, Token = 10 + 960, // Index = 61, Token = 11 + 1008, // Index = 61, Token = 12 + 1040, // Index = 61, Token = 13 + 1072, // Index = 61, Token = 14 + 1104, // Index = 61, Token = 15 + 976, // Index = 62, Token = 0 + 976, // Index = 62, Token = 1 + 976, // Index = 62, Token = 2 + 976, // Index = 62, Token = 3 + 1024, // Index = 62, Token = 4 + 1056, // Index = 62, Token = 5 + 1088, // Index = 62, Token = 6 + 1120, // Index = 62, Token = 7 + 976, // Index = 62, Token = 8 + 976, // Index = 62, Token = 9 + 976, // Index = 62, Token = 10 + 976, // Index = 62, Token = 11 + 1024, // Index = 62, Token = 12 + 1056, // Index = 62, Token = 13 + 1088, // Index = 62, Token = 14 + 1120, // Index = 62, Token = 15 + 992, // Index = 63, Token = 0 + 992, // Index = 63, Token = 1 + 992, // Index = 63, Token = 2 + 992, // Index = 63, Token = 3 + 1040, // Index = 63, Token = 4 + 1072, // Index = 63, Token = 5 + 1104, // Index = 63, Token = 6 + 1136, // Index = 63, Token = 7 + 992, // Index = 63, Token = 8 + 992, // Index = 63, Token = 9 + 992, // Index = 63, Token = 10 + 992, // Index = 63, Token = 11 + 1040, // Index = 63, Token = 12 + 1072, // Index = 63, Token = 13 + 1104, // Index = 63, Token = 14 + 1136, // Index = 63, Token = 15 + 1008, // Index = 64, Token = 0 + 1008, // Index = 64, Token = 1 + 1008, // Index = 64, Token = 2 + 1008, // Index = 64, Token = 3 + 1056, // Index = 64, Token = 4 + 1088, // Index = 64, Token = 5 + 1120, // Index = 64, Token = 6 + 1152, // Index = 64, Token = 7 + 1008, // Index = 64, Token = 8 + 1008, // Index = 64, Token = 9 + 1008, // Index = 64, Token = 10 + 1008, // Index = 64, Token = 11 + 1056, // Index = 64, Token = 12 + 1088, // Index = 64, Token = 13 + 1120, // Index = 64, Token = 14 + 1152, // Index = 64, Token = 15 + 1024, // Index = 65, Token = 0 + 1024, // Index = 65, Token = 1 + 1024, // Index = 65, Token = 2 + 1024, // Index = 65, Token = 3 + 1072, // Index = 65, Token = 4 + 1104, // Index = 65, Token = 5 + 1136, // Index = 65, Token = 6 + 1168, // Index = 65, Token = 7 + 1024, // Index = 65, Token = 8 + 1024, // Index = 65, Token = 9 + 1024, // Index = 65, Token = 10 + 1024, // Index = 65, Token = 11 + 1072, // Index = 65, Token = 12 + 1104, // Index = 65, Token = 13 + 1136, // Index = 65, Token = 14 + 1168, // Index = 65, Token = 15 + 1040, // Index = 66, Token = 0 + 1040, // Index = 66, Token = 1 + 1040, // Index = 66, Token = 2 + 1040, // Index = 66, Token = 3 + 1088, // Index = 66, Token = 4 + 1120, // Index = 66, Token = 5 + 1152, // Index = 66, Token = 6 + 1184, // Index = 66, Token = 7 + 1040, // Index = 66, Token = 8 + 1040, // Index = 66, Token = 9 + 1040, // Index = 66, Token = 10 + 1040, // Index = 66, Token = 11 + 1088, // Index = 66, Token = 12 + 1120, // Index = 66, Token = 13 + 1152, // Index = 66, Token = 14 + 1184, // Index = 66, Token = 15 + 1056, // Index = 67, Token = 0 + 1056, // Index = 67, Token = 1 + 1056, // Index = 67, Token = 2 + 1056, // Index = 67, Token = 3 + 1104, // Index = 67, Token = 4 + 1136, // Index = 67, Token = 5 + 1168, // Index = 67, Token = 6 + 1200, // Index = 67, Token = 7 + 1056, // Index = 67, Token = 8 + 1056, // Index = 67, Token = 9 + 1056, // Index = 67, Token = 10 + 1056, // Index = 67, Token = 11 + 1104, // Index = 67, Token = 12 + 1136, // Index = 67, Token = 13 + 1168, // Index = 67, Token = 14 + 1200, // Index = 67, Token = 15 + 1072, // Index = 68, Token = 0 + 1072, // Index = 68, Token = 1 + 1072, // Index = 68, Token = 2 + 1072, // Index = 68, Token = 3 + 1120, // Index = 68, Token = 4 + 1152, // Index = 68, Token = 5 + 1184, // Index = 68, Token = 6 + 1216, // Index = 68, Token = 7 + 1072, // Index = 68, Token = 8 + 1072, // Index = 68, Token = 9 + 1072, // Index = 68, Token = 10 + 1072, // Index = 68, Token = 11 + 1120, // Index = 68, Token = 12 + 1152, // Index = 68, Token = 13 + 1184, // Index = 68, Token = 14 + 1216, // Index = 68, Token = 15 + 1088, // Index = 69, Token = 0 + 1088, // Index = 69, Token = 1 + 1088, // Index = 69, Token = 2 + 1088, // Index = 69, Token = 3 + 1136, // Index = 69, Token = 4 + 1168, // Index = 69, Token = 5 + 1200, // Index = 69, Token = 6 + 1232, // Index = 69, Token = 7 + 1088, // Index = 69, Token = 8 + 1088, // Index = 69, Token = 9 + 1088, // Index = 69, Token = 10 + 1088, // Index = 69, Token = 11 + 1136, // Index = 69, Token = 12 + 1168, // Index = 69, Token = 13 + 1200, // Index = 69, Token = 14 + 1232, // Index = 69, Token = 15 + 1104, // Index = 70, Token = 0 + 1104, // Index = 70, Token = 1 + 1104, // Index = 70, Token = 2 + 1104, // Index = 70, Token = 3 + 1152, // Index = 70, Token = 4 + 1184, // Index = 70, Token = 5 + 1216, // Index = 70, Token = 6 + 1248, // Index = 70, Token = 7 + 1104, // Index = 70, Token = 8 + 1104, // Index = 70, Token = 9 + 1104, // Index = 70, Token = 10 + 1104, // Index = 70, Token = 11 + 1152, // Index = 70, Token = 12 + 1184, // Index = 70, Token = 13 + 1216, // Index = 70, Token = 14 + 1248, // Index = 70, Token = 15 + 1120, // Index = 71, Token = 0 + 1120, // Index = 71, Token = 1 + 1120, // Index = 71, Token = 2 + 1120, // Index = 71, Token = 3 + 1168, // Index = 71, Token = 4 + 1200, // Index = 71, Token = 5 + 1232, // Index = 71, Token = 6 + 1264, // Index = 71, Token = 7 + 1120, // Index = 71, Token = 8 + 1120, // Index = 71, Token = 9 + 1120, // Index = 71, Token = 10 + 1120, // Index = 71, Token = 11 + 1168, // Index = 71, Token = 12 + 1200, // Index = 71, Token = 13 + 1232, // Index = 71, Token = 14 + 1264, // Index = 71, Token = 15 + 1136, // Index = 72, Token = 0 + 1136, // Index = 72, Token = 1 + 1136, // Index = 72, Token = 2 + 1136, // Index = 72, Token = 3 + 1184, // Index = 72, Token = 4 + 1216, // Index = 72, Token = 5 + 1248, // Index = 72, Token = 6 + 1280, // Index = 72, Token = 7 + 1136, // Index = 72, Token = 8 + 1136, // Index = 72, Token = 9 + 1136, // Index = 72, Token = 10 + 1136, // Index = 72, Token = 11 + 1184, // Index = 72, Token = 12 + 1216, // Index = 72, Token = 13 + 1248, // Index = 72, Token = 14 + 1280, // Index = 72, Token = 15 + 1152, // Index = 73, Token = 0 + 1152, // Index = 73, Token = 1 + 1152, // Index = 73, Token = 2 + 1152, // Index = 73, Token = 3 + 1200, // Index = 73, Token = 4 + 1232, // Index = 73, Token = 5 + 1264, // Index = 73, Token = 6 + 1296, // Index = 73, Token = 7 + 1152, // Index = 73, Token = 8 + 1152, // Index = 73, Token = 9 + 1152, // Index = 73, Token = 10 + 1152, // Index = 73, Token = 11 + 1200, // Index = 73, Token = 12 + 1232, // Index = 73, Token = 13 + 1264, // Index = 73, Token = 14 + 1296, // Index = 73, Token = 15 + 1168, // Index = 74, Token = 0 + 1168, // Index = 74, Token = 1 + 1168, // Index = 74, Token = 2 + 1168, // Index = 74, Token = 3 + 1216, // Index = 74, Token = 4 + 1248, // Index = 74, Token = 5 + 1280, // Index = 74, Token = 6 + 1312, // Index = 74, Token = 7 + 1168, // Index = 74, Token = 8 + 1168, // Index = 74, Token = 9 + 1168, // Index = 74, Token = 10 + 1168, // Index = 74, Token = 11 + 1216, // Index = 74, Token = 12 + 1248, // Index = 74, Token = 13 + 1280, // Index = 74, Token = 14 + 1312, // Index = 74, Token = 15 + 1184, // Index = 75, Token = 0 + 1184, // Index = 75, Token = 1 + 1184, // Index = 75, Token = 2 + 1184, // Index = 75, Token = 3 + 1232, // Index = 75, Token = 4 + 1264, // Index = 75, Token = 5 + 1296, // Index = 75, Token = 6 + 1328, // Index = 75, Token = 7 + 1184, // Index = 75, Token = 8 + 1184, // Index = 75, Token = 9 + 1184, // Index = 75, Token = 10 + 1184, // Index = 75, Token = 11 + 1232, // Index = 75, Token = 12 + 1264, // Index = 75, Token = 13 + 1296, // Index = 75, Token = 14 + 1328, // Index = 75, Token = 15 + 1200, // Index = 76, Token = 0 + 1200, // Index = 76, Token = 1 + 1200, // Index = 76, Token = 2 + 1200, // Index = 76, Token = 3 + 1248, // Index = 76, Token = 4 + 1280, // Index = 76, Token = 5 + 1312, // Index = 76, Token = 6 + 1344, // Index = 76, Token = 7 + 1200, // Index = 76, Token = 8 + 1200, // Index = 76, Token = 9 + 1200, // Index = 76, Token = 10 + 1200, // Index = 76, Token = 11 + 1248, // Index = 76, Token = 12 + 1280, // Index = 76, Token = 13 + 1312, // Index = 76, Token = 14 + 1344, // Index = 76, Token = 15 + 1216, // Index = 77, Token = 0 + 1216, // Index = 77, Token = 1 + 1216, // Index = 77, Token = 2 + 1216, // Index = 77, Token = 3 + 1264, // Index = 77, Token = 4 + 1296, // Index = 77, Token = 5 + 1328, // Index = 77, Token = 6 + 1360, // Index = 77, Token = 7 + 1216, // Index = 77, Token = 8 + 1216, // Index = 77, Token = 9 + 1216, // Index = 77, Token = 10 + 1216, // Index = 77, Token = 11 + 1264, // Index = 77, Token = 12 + 1296, // Index = 77, Token = 13 + 1328, // Index = 77, Token = 14 + 1360, // Index = 77, Token = 15 + 1232, // Index = 78, Token = 0 + 1232, // Index = 78, Token = 1 + 1232, // Index = 78, Token = 2 + 1232, // Index = 78, Token = 3 + 1280, // Index = 78, Token = 4 + 1312, // Index = 78, Token = 5 + 1344, // Index = 78, Token = 6 + 1376, // Index = 78, Token = 7 + 1232, // Index = 78, Token = 8 + 1232, // Index = 78, Token = 9 + 1232, // Index = 78, Token = 10 + 1232, // Index = 78, Token = 11 + 1280, // Index = 78, Token = 12 + 1312, // Index = 78, Token = 13 + 1344, // Index = 78, Token = 14 + 1376, // Index = 78, Token = 15 + 1248, // Index = 79, Token = 0 + 1248, // Index = 79, Token = 1 + 1248, // Index = 79, Token = 2 + 1248, // Index = 79, Token = 3 + 1296, // Index = 79, Token = 4 + 1328, // Index = 79, Token = 5 + 1360, // Index = 79, Token = 6 + 1392, // Index = 79, Token = 7 + 1248, // Index = 79, Token = 8 + 1248, // Index = 79, Token = 9 + 1248, // Index = 79, Token = 10 + 1248, // Index = 79, Token = 11 + 1296, // Index = 79, Token = 12 + 1328, // Index = 79, Token = 13 + 1360, // Index = 79, Token = 14 + 1392, // Index = 79, Token = 15 + 1264, // Index = 80, Token = 0 + 1264, // Index = 80, Token = 1 + 1264, // Index = 80, Token = 2 + 1264, // Index = 80, Token = 3 + 1312, // Index = 80, Token = 4 + 1344, // Index = 80, Token = 5 + 1376, // Index = 80, Token = 6 + 1408, // Index = 80, Token = 7 + 1264, // Index = 80, Token = 8 + 1264, // Index = 80, Token = 9 + 1264, // Index = 80, Token = 10 + 1264, // Index = 80, Token = 11 + 1312, // Index = 80, Token = 12 + 1344, // Index = 80, Token = 13 + 1376, // Index = 80, Token = 14 + 1408, // Index = 80, Token = 15 + 1280, // Index = 81, Token = 0 + 1280, // Index = 81, Token = 1 + 1280, // Index = 81, Token = 2 + 1280, // Index = 81, Token = 3 + 1328, // Index = 81, Token = 4 + 1360, // Index = 81, Token = 5 + 1392, // Index = 81, Token = 6 + 1408, // Index = 81, Token = 7 + 1280, // Index = 81, Token = 8 + 1280, // Index = 81, Token = 9 + 1280, // Index = 81, Token = 10 + 1280, // Index = 81, Token = 11 + 1328, // Index = 81, Token = 12 + 1360, // Index = 81, Token = 13 + 1392, // Index = 81, Token = 14 + 1408, // Index = 81, Token = 15 + 1296, // Index = 82, Token = 0 + 1296, // Index = 82, Token = 1 + 1296, // Index = 82, Token = 2 + 1296, // Index = 82, Token = 3 + 1344, // Index = 82, Token = 4 + 1376, // Index = 82, Token = 5 + 1408, // Index = 82, Token = 6 + 1408, // Index = 82, Token = 7 + 1296, // Index = 82, Token = 8 + 1296, // Index = 82, Token = 9 + 1296, // Index = 82, Token = 10 + 1296, // Index = 82, Token = 11 + 1344, // Index = 82, Token = 12 + 1376, // Index = 82, Token = 13 + 1408, // Index = 82, Token = 14 + 1408, // Index = 82, Token = 15 + 1312, // Index = 83, Token = 0 + 1312, // Index = 83, Token = 1 + 1312, // Index = 83, Token = 2 + 1312, // Index = 83, Token = 3 + 1360, // Index = 83, Token = 4 + 1392, // Index = 83, Token = 5 + 1408, // Index = 83, Token = 6 + 1408, // Index = 83, Token = 7 + 1312, // Index = 83, Token = 8 + 1312, // Index = 83, Token = 9 + 1312, // Index = 83, Token = 10 + 1312, // Index = 83, Token = 11 + 1360, // Index = 83, Token = 12 + 1392, // Index = 83, Token = 13 + 1408, // Index = 83, Token = 14 + 1408, // Index = 83, Token = 15 + 1328, // Index = 84, Token = 0 + 1328, // Index = 84, Token = 1 + 1328, // Index = 84, Token = 2 + 1328, // Index = 84, Token = 3 + 1376, // Index = 84, Token = 4 + 1408, // Index = 84, Token = 5 + 1408, // Index = 84, Token = 6 + 1408, // Index = 84, Token = 7 + 1328, // Index = 84, Token = 8 + 1328, // Index = 84, Token = 9 + 1328, // Index = 84, Token = 10 + 1328, // Index = 84, Token = 11 + 1376, // Index = 84, Token = 12 + 1408, // Index = 84, Token = 13 + 1408, // Index = 84, Token = 14 + 1408, // Index = 84, Token = 15 + 1344, // Index = 85, Token = 0 + 1344, // Index = 85, Token = 1 + 1344, // Index = 85, Token = 2 + 1344, // Index = 85, Token = 3 + 1392, // Index = 85, Token = 4 + 1408, // Index = 85, Token = 5 + 1408, // Index = 85, Token = 6 + 1408, // Index = 85, Token = 7 + 1344, // Index = 85, Token = 8 + 1344, // Index = 85, Token = 9 + 1344, // Index = 85, Token = 10 + 1344, // Index = 85, Token = 11 + 1392, // Index = 85, Token = 12 + 1408, // Index = 85, Token = 13 + 1408, // Index = 85, Token = 14 + 1408, // Index = 85, Token = 15 + 1360, // Index = 86, Token = 0 + 1360, // Index = 86, Token = 1 + 1360, // Index = 86, Token = 2 + 1360, // Index = 86, Token = 3 + 1408, // Index = 86, Token = 4 + 1408, // Index = 86, Token = 5 + 1408, // Index = 86, Token = 6 + 1408, // Index = 86, Token = 7 + 1360, // Index = 86, Token = 8 + 1360, // Index = 86, Token = 9 + 1360, // Index = 86, Token = 10 + 1360, // Index = 86, Token = 11 + 1408, // Index = 86, Token = 12 + 1408, // Index = 86, Token = 13 + 1408, // Index = 86, Token = 14 + 1408, // Index = 86, Token = 15 + 1376, // Index = 87, Token = 0 + 1376, // Index = 87, Token = 1 + 1376, // Index = 87, Token = 2 + 1376, // Index = 87, Token = 3 + 1408, // Index = 87, Token = 4 + 1408, // Index = 87, Token = 5 + 1408, // Index = 87, Token = 6 + 1408, // Index = 87, Token = 7 + 1376, // Index = 87, Token = 8 + 1376, // Index = 87, Token = 9 + 1376, // Index = 87, Token = 10 + 1376, // Index = 87, Token = 11 + 1408, // Index = 87, Token = 12 + 1408, // Index = 87, Token = 13 + 1408, // Index = 87, Token = 14 + 1408, // Index = 87, Token = 15 + 1392, // Index = 88, Token = 0 + 1392, // Index = 88, Token = 1 + 1392, // Index = 88, Token = 2 + 1392, // Index = 88, Token = 3 + 1408, // Index = 88, Token = 4 + 1408, // Index = 88, Token = 5 + 1408, // Index = 88, Token = 6 + 1408, // Index = 88, Token = 7 + 1392, // Index = 88, Token = 8 + 1392, // Index = 88, Token = 9 + 1392, // Index = 88, Token = 10 + 1392, // Index = 88, Token = 11 + 1408, // Index = 88, Token = 12 + 1408, // Index = 88, Token = 13 + 1408, // Index = 88, Token = 14 + 1408 // Index = 88, Token = 15 +}; + diff --git a/CODE/JOEMAKE.BAT b/CODE/JOEMAKE.BAT new file mode 100644 index 0000000..2cb0625 --- /dev/null +++ b/CODE/JOEMAKE.BAT @@ -0,0 +1,74 @@ +@echo off +timer +:*************************************************************************** +:* * +:* Network make batch file: compiles using network slaves * +:* * +:* Required mods to user's AUTOEXEC: * +:* - PATH should include the SLAVES directory, so make can find NETEXEC * +:* - WWLIB should point to user's WWLIB32 installation * +:* - WATCOM should point to the local WATCOM root directory * +:* * +:* Required changes to this batch file: * +:* - NETWHO should be set to the user's name * +:* - CCLOCALDIR & CCNETDIR should point to the local & net CC dirs * +:* * +:*************************************************************************** +:------------------------- set up environment ------------------------------ +set NETWHO=joeb +set CCLOCALDIR=c:\projects\c&czero +set CCNETDIR=f:\projects\c&c0\slaves\%NETWHO%\c&c0 +set WWLIBNET=f:\projects\c&c0\slaves\%NETWHO%\wwlib32 +set WATDIR=c:\projects\c&czero\code\watcom +set NETWORK=1 + +:------------------------- substitute drives ------------------------------- +subst o: /d >&NUL +subst r: /d >&NUL +subst q: /d >&NUL +subst o: %WATDIR% +subst r: %CCLOCALDIR% +subst q: %WWLIB% + +:----------------------- Set Watcom's environment -------------------------- +set watcom=o: +set include=o:\h;o:\h\win;q:\include;..\vq\include;.; +set wwflat=q: +REM set wwvcs=g:\library\wwlib32\pvcswat +set DOS16M=@0--8mm + +:---------------------- update network source files ------------------------ +copy /s q:\include\*.h %WWLIBNET%\include /U +copy r:\vq\include\vqm32\*.h %CCNETDIR%\vq\include\vqm32 /U /S +copy r:\vq\include\vqa32\*.h %CCNETDIR%\vq\include\vqa32 /U /S +copy r:\code\*.cpp r:\code\*.h r:\code\*.asm r:\code\*.i r:\code\*.inc r:\code\makefile r:\code\*.lnk %CCNETDIR%\code /U +copy r:\code\watcom\*.* %CCNETDIR%\code\watcom /U +copy r:\code\watcom\binb\*.* %CCNETDIR%\code\watcom\binb /U +copy r:\code\watcom\binw\*.* %CCNETDIR%\code\watcom\binw /U +copy r:\code\watcom\binnt\*.* %CCNETDIR%\code\watcom\binnt /U +copy /s r:\code\watcom\h\*.* %CCNETDIR%\code\watcom\h /U +REM copy /s r:\code\watcom\*.* %CCNETDIR%\code\watcom /U +REM copy r:\code\*.h %CCNETDIR%\code /U +REM copy r:\code\*.asm %CCNETDIR%\code /U +REM copy r:\code\*.i %CCNETDIR%\code /U +REM copy r:\code\*.inc %CCNETDIR%\code /U +REM copy r:\code\makefile %CCNETDIR%\code /U + +:----------------------------- Fire up wmake ------------------------------- +netexec /s +wmake /c %1 %2 %3 %4 %5 %6 %7 %8 %9 +if errorlevel 1 goto makerr +goto endit + +:makerr +if exist %CCNETDIR%\code\netmake.err type %CCNETDIR%\code\netmake.err + +:endit +set NETWORK= + +:---------------------------- Get the objects ------------------------------ +copy %CCNETDIR%\code\obj\*.obj r:\code\obj /U + +:*************************************************************************** +timer + diff --git a/CODE/JSHELL.CPP b/CODE/JSHELL.CPP new file mode 100644 index 0000000..4b8e1df --- /dev/null +++ b/CODE/JSHELL.CPP @@ -0,0 +1,660 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/JSHELL.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : JSHELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 2, 1994 * + * * + * Last Update : May 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Build_Translucent_Table -- Creates a translucent control table. * + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * Fatal -- General purpose fatal error handler. * + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * Set_Window -- Sets the window dimensions to that specified. * + * Small_Icon -- Create a small icon from a big one. * + * Translucent_Table_Size -- Determines the size of a translucent table. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wwfile.h" + + +/*********************************************************************************************** + * Small_Icon -- Create a small icon from a big one. * + * * + * This routine will extract the specified icon from the icon data file and convert that * + * icon into a small (3x3) representation. Typical use of this mini-icon is for the radar * + * map. * + * * + * INPUT: iconptr -- Pointer to the icon data file. * + * * + * iconnum -- The embedded icon number to convert into a small image. * + * * + * OUTPUT: Returns with a pointer to the small icon imagery. This is exactly 9 bytes long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + *=============================================================================================*/ +void * Small_Icon(void const * iconptr, int iconnum) +{ + static unsigned char _icon[9]; + IControl_Type const * iptr = (IControl_Type const *)iconptr; + unsigned char * data; + + if (iconptr) { + iconnum = ((char *)((char *)iptr + iptr->Map))[iconnum]; + data = &((unsigned char *)((unsigned char *)iptr + iptr->Icons))[iconnum*(24*24)]; +// data = &iptr->Icons[iconnum*(24*24)]; + + for (int index = 0; index < 9; index++) { + int _offsets[9] = { + 4+4*24, + 12+4*24, + 20+4*24, + 4+12*24, + 12+12*24, + 20+12*24, + 4+20*24, + 12+20*24, + 20+20*24 + }; + _icon[index] = data[_offsets[index]]; + } + } + + return(_icon); +} + + +/*********************************************************************************************** + * Set_Window -- Sets the window dimensions to that specified. * + * * + * Use this routine to set the windows dimensions to the coordinates and dimensions * + * specified. * + * * + * INPUT: x -- Window X pixel position. * + * * + * y -- Window Y pixel position. * + * * + * w -- Window width in pixels. * + * * + * h -- Window height in pixels. * + * * + * OUTPUT: none * + * * + * WARNINGS: The X and width values are truncated to an even 8 pixel boundary. This is * + * the same as stripping off the lower 3 bits. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void Set_Window(int window, int x, int y, int w, int h) +{ + WindowList[window][WINDOWWIDTH] = w; + WindowList[window][WINDOWHEIGHT] = h; + WindowList[window][WINDOWX] = x; + WindowList[window][WINDOWY] = y; +} + + +/*********************************************************************************************** + * Fatal -- General purpose fatal error handler. * + * * + * This is a very simple general purpose fatal error handler. It goes directly to text * + * mode, prints the error, and then aborts with a failure code. * + * * + * INPUT: message -- The text message to display. * + * * + * ... -- Any optional parameters that are used in formatting the message. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine never returns. The game exits immediately. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void Fatal(char const * message, ...) +{ + va_list va; + + va_start(va, message); + //Prog_End(); + vfprintf(stderr, message, va); + Mono_Printf(message); + Emergency_Exit(EXIT_FAILURE); +} + + +#ifdef NEVER +void File_Fatal(char const * message) +{ + //Prog_End(); + perror(message); + Emergency_Exit(EXIT_FAILURE); +} +#endif + + + +/*********************************************************************************************** + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * * + * This is the C++ counterpart to the Load_Uncompress function. It will load the file * + * specified into the graphic buffer indicated and uncompress it. * + * * + * INPUT: file -- The file to load and uncompress. * + * * + * uncomp_buff -- The graphic buffer that initial loading will use. * + * * + * dest_buff -- The buffer that will hold the uncompressed data. * + * * + * reserved_data -- This is an optional pointer to a buffer that will hold any * + * reserved data the compressed file may contain. This is * + * typically a palette. * + * * + * OUTPUT: Returns with the size of the uncompressed data in the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void * reserved_data) +{ + unsigned short size; + void * sptr = uncomp_buff.Get_Buffer(); + void * dptr = dest_buff.Get_Buffer(); + int opened = false; + CompHeaderType header; + + /* + ** The file must be opened in order to be read from. If the file + ** isn't opened, then open it. Record this fact so that it can be + ** restored to its closed state at the end. + */ + if (!file.Is_Open()) { + if (!file.Open()) { + return(0); + } + opened = true; + } + + /* + ** Read in the size of the file (supposedly). + */ + file.Read(&size, sizeof(size)); + + /* + ** Read in the header block. This block contains the compression type + ** and skip data (among other things). + */ + file.Read(&header, sizeof(header)); + size -= sizeof(header); + + /* + ** If there are skip bytes then they must be processed. Either read + ** them into the buffer provided or skip past them. No check is made + ** to ensure that the reserved data buffer is big enough (watch out!). + */ + if (header.Skip) { + size -= header.Skip; + if (reserved_data) { + file.Read(reserved_data, header.Skip); + } else { + file.Seek(header.Skip, SEEK_CUR); + } + header.Skip = 0; + } + + /* + ** Determine where is the proper place to load the data. If both buffers + ** specified are identical, then the data should be loaded at the end of + ** the buffer and decompressed at the beginning. + */ + if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) { + sptr = (char *)sptr + uncomp_buff.Get_Size()-(size+sizeof(header)); + } + + /* + ** Read in the bulk of the data. + */ + Mem_Copy(&header, sptr, sizeof(header)); + file.Read((char *)sptr + sizeof(header), size); + + /* + ** Decompress the data. + */ + size = (unsigned int) Uncompress_Data(sptr, dptr); + + /* + ** Close the file if necessary. + */ + if (opened) { + file.Close(); + } + return((long)size); +} + + +int Load_Picture(char const * filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char * palette, PicturePlaneType ) +{ + return(Load_Uncompress(CCFileClass(filename), scratchbuf, destbuf, palette ) / 8000); +} + + +/*********************************************************************************************** + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * * + * This is the C++ replacement for the Load_Alloc_Data function. It will allocate the * + * memory big enough to hold the file and then read the file into it. * + * * + * INPUT: file -- The file to read. * + * * + * mem -- The memory system to use for allocation. * + * * + * OUTPUT: Returns with a pointer to the allocated and filled memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void * Load_Alloc_Data(FileClass &file) +{ + void * ptr = 0; + long size = file.Size(); + + ptr = new char [size]; + if (ptr) { + file.Read(ptr, size); + } + return(ptr); +} + + +/*********************************************************************************************** + * Translucent_Table_Size -- Determines the size of a translucent table. * + * * + * Use this routine to determine how big the translucent table needs * + * to be given the specified number of colors. This value is typically * + * used when allocating the buffer for the translucent table. * + * * + * INPUT: count -- The number of colors that are translucent. * + * * + * OUTPUT: Returns the size of the translucent table. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +long Translucent_Table_Size(int count) +{ + return(256L + (256L * count)); +} + + +/*********************************************************************************************** + * Build_Translucent_Table -- Creates a translucent control table. * + * * + * The table created by this routine is used by Draw_Shape (GHOST) to * + * achieve a translucent affect. The original color of the shape will * + * show through. This differs from the fading effect, since that * + * affect only alters the background color toward a single destination * + * color. * + * * + * INPUT: palette -- Pointer to the control palette. * + * * + * control -- Pointer to array of structures that control how * + * the translucent table will be built. * + * * + * count -- The number of entries in the control array. * + * * + * buffer -- Pointer to buffer to place the translucent table. * + * If NULL is passed in, then the buffer will be * + * allocated. * + * * + * OUTPUT: Returns with pointer to the translucent table. * + * * + * WARNINGS: This routine is exceedingly slow. Use sparingly. * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +void * Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control && palette) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +/*********************************************************************************************** + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * * + * This routine will build a translucent (fading) table to remap colors into the shadow * + * color region of the palette. Shadow colors are not affected by this translucent table. * + * This means that a shape can be overlapped any number of times and the imagery will * + * remain deterministic (and constant). * + * * + * INPUT: palette -- Pointer to the palette to base the translucent process on. * + * * + * control -- Pointer to special control structure that specifies the * + * target color, and percentage of fade. * + * * + * count -- The number of colors to be remapped (entries in the control array). * + * * + * buffer -- Pointer to the staging buffer that will hold the translucent table * + * data. If this parameter is NULL, then an appropriate sized table * + * will be allocated. * + * * + * OUTPUT: Returns with a pointer to the translucent table data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +void * Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + + if (count && control) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (int index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + *ptr++ = palette.Closest_Color(trycolor); + } + } + return(dest); +} + + +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; +// HSVClass desthsv = palette[color]; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** If this color should not be remapped, then it will be stored as a remap + ** to itself. This is effectively no remap. + */ + if (index > PaletteClass::COLOR_COUNT-16 || index == 0) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + int best = -1; + int bvalue = 0; + for (int id = PaletteClass::COLOR_COUNT-16; id < PaletteClass::COLOR_COUNT-1; id++) { + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + *ptr++ = best; + } + } + } + return(dest); +} + + +#ifdef OBSOLETE +//int Desired_Facing8(int x1, int y1, int x2, int y2) +DirType xDesired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +//int Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +DirType xDesired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} +#endif diff --git a/CODE/JSHELL.H b/CODE/JSHELL.H new file mode 100644 index 0000000..fd6d23a --- /dev/null +++ b/CODE/JSHELL.H @@ -0,0 +1,473 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/JSHELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : JSHELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef JSHELL_H +#define JSHELL_H + + +#include + +#ifdef WIN32 +//#define getch Get_Key_Num +//#define kbhit Check_Key_Num +#include "key.h" +#else +#include +#endif + +/* +** Interface class to the keyboard. This insulates the game from library vagaries. Most +** notable being the return values are declared as "int" in the library whereas C&C +** expects it to be of KeyNumType. +*/ +#ifdef WIN32 +//#define KeyNumType int +//#define KeyASCIIType int + +//lint -esym(1725,KeyboardClass::MouseQX,KeyboardClass::MouseQY) +struct KeyboardClass : public WWKeyboardClass + +#else +struct KeyboardClass +#endif +{ + + /* + ** This flag is used to indicate whether the WW library has taken over + ** the keyboard or not. If not, then the normal console input + ** takes precedence. + */ + unsigned IsLibrary; + +#ifndef WIN32 + int &MouseQX; + int &MouseQY; + + KeyboardClass() : + IsLibrary(true), + MouseQX(::MouseQX), + MouseQY(::MouseQY) + {} + KeyNumType Get(void) {return (IsLibrary ? (KeyNumType)Get_Key_Num() : (KeyNumType)getch());}; + KeyNumType Check(void) {return (IsLibrary ? (KeyNumType)Check_Key_Num() : (KeyNumType)kbhit());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)KN_To_KA(key));}; + void Clear(void) {if (IsLibrary) Clear_KeyBuffer();}; + int Down(KeyNumType key) {return(Key_Down(key));}; +#else + KeyboardClass() : IsLibrary(true) {} + KeyNumType Get(void) {return ((KeyNumType)WWKeyboardClass::Get());}; + KeyNumType Check(void) {return ((KeyNumType)WWKeyboardClass::Check());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)WWKeyboardClass::To_ASCII(key));}; + void Clear(void) {WWKeyboardClass::Clear();}; + int Down(KeyNumType key) {return(WWKeyboardClass::Down(key));}; +#endif + + int Mouse_X(void) {return(Get_Mouse_X());}; + int Mouse_Y(void) {return(Get_Mouse_Y());}; +}; + + +/* +** These templates allow enumeration types to have simple bitwise +** arithmatic performed. The operators must be instatiated for the +** enumerated types desired. +*/ +template inline T operator ++(T & a) +{ + a = (T)((int)a + (int)1); + return(a); +} +template inline T operator ++(T & a, int) +{ + T aa = a; + a = (T)((int)a + (int)1); + return(aa); +} +template inline T operator --(T & a) +{ + a = (T)((int)a - (int)1); + return(a); +} +template inline T operator --(T & a, int) +{ + T aa = a; + a = (T)((int)a - (int)1); + return(aa); +} +template inline T operator |(T t1, T t2) +{ + return((T)((int)t1 | (int)t2)); +} +template inline T operator &(T t1, T t2) +{ + return((T)((int)t1 & (int)t2)); +} +template inline T operator ~(T t1) +{ + return((T)(~(int)t1)); +} + +#ifndef WIN32 +template inline T min(T value1, T value2) +{ + if (value1 < value2) { + return(value1); + } + return(value2); +} +int min(int, int); +long min(long, long); + +template inline T max(T value1, T value2) +{ + if (value1 > value2) { + return(value1); + } + return(value2); +} +int max(int, int); +long max(long, long); +#endif + +template inline void swap(T &value1, T &value2) +{ + T temp = value1; + value1 = value2; + value2 = temp; +} +int swap(int, int); +long swap(long, long); + +template inline +T Bound(T original, T minval, T maxval) +{ + if (original < minval) return(minval); + if (original > maxval) return(maxval); + return(original); +}; +int Bound(signed int, signed int, signed int); +unsigned Bound(unsigned, unsigned, unsigned); +long Bound(long, long, long); + +template +T _rotl(T X, int n) +{ + return((T)(( ( ( X ) << n ) | ( ( X ) >> ( (sizeof(T)*8) - n ) ) ))); +} + + +/* +** This macro serves as a general way to determine the number of elements +** within an array. +*/ +#define ARRAY_LENGTH(x) int(sizeof(x)/sizeof(x[0])) +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/* +** The shape flags are likely to be "or"ed together and other such bitwise +** manipulations. These instatiated operator templates allow this. +*/ +inline ShapeFlags_Type operator |(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator &(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator ~(ShapeFlags_Type); + + +void Set_Bit(void * array, int bit, int value); +#pragma aux Set_Bit parm [esi] [ecx] [eax] \ + modify [esi ebx] = \ + "mov ebx,ecx" \ + "shr ebx,5" \ + "and ecx,01Fh" \ + "btr [esi+ebx*4],ecx" \ + "or eax,eax" \ + "jz ok" \ + "bts [esi+ebx*4],ecx" \ + "ok:" + +int Get_Bit(void const * array, int bit); +#pragma aux Get_Bit parm [esi] [eax] \ + modify [esi ebx] \ + value [eax] = \ + "mov ebx,eax" \ + "shr ebx,5" \ + "and eax,01Fh" \ + "bt [esi+ebx*4],eax" \ + "setc al" + +int First_True_Bit(void const * array); +#pragma aux First_True_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +int First_False_Bit(void const * array); +#pragma aux First_False_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "not ebx" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +#ifdef OBSOLETE +extern int Bound(int original, int min, int max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jl okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "jg okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jl okmax" \ + "mov eax,ecx" \ + "okmax:" + +extern unsigned Bound(unsigned original, unsigned min, unsigned max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jb okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "ja okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jb okmax" \ + "mov eax,ecx" \ + "okmax:" +#endif + + +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +#pragma aux Fixed_To_Cardinal parm [eax] [edx] \ + modify [edx] \ + value [eax] = \ + "mul edx" \ + "add eax,080h" \ + "test eax,0FF000000h" \ + "jz ok" \ + "mov eax,000FFFFFFh" \ + "ok:" \ + "shr eax,8" + + +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +#pragma aux Cardinal_To_Fixed parm [ebx] [eax] \ + modify [edx] \ + value [eax] = \ + "or ebx,ebx" \ + "jz fini" \ + "shl eax,8" \ + "xor edx,edx" \ + "div ebx" \ + "fini:" + + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + + +/* +** Timer objects that fetch the appropriate timer value according to +** the type of timer they are. +*/ +extern long Frame; +class FrameTimerClass +{ + public: + long operator () (void) const {return(Frame);}; + operator long (void) const {return(Frame);}; +}; + + +#ifndef WIN32 +extern bool TimerSystemOn; +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); +} +//bool Init_Timer_System(unsigned int freq, int partial=false); +bool Remove_Timer_System(void); +#else +extern WinTimerClass * WindowsTimer; +#endif + +#ifndef SYSTEM_TIMER_CLASS +#define SYSTEM_TIMER_CLASS +class SystemTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + #else + long operator () (void) const {return(Get_System_Tick_Count());}; + operator long (void) const {return(Get_System_Tick_Count());}; + #endif +}; +#endif + + +class UserTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + #else + long operator () (void) const {return(Get_User_Tick_Count());}; + operator long (void) const {return(Get_User_Tick_Count());}; + #endif +}; + + +template +void Bubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (array[index] > array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (*array[index] > *array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PNBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (stricmp(array[index]->Name(), array[index+1]->Name()) > 0) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +class SmartPtr +{ + public: + SmartPtr(NoInitClass const &) {} + SmartPtr(T * realptr = 0) : Pointer(realptr) {} + SmartPtr(SmartPtr const & rvalue) : Pointer(rvalue.Pointer) {} + ~SmartPtr(void) {Pointer = 0;} + + operator T * (void) const {return(Pointer);} + + operator long (void) const {return((long)Pointer);} + + SmartPtr operator ++ (int) {assert(Pointer != 0);SmartPtr temp = *this;++Pointer;return(temp);} + SmartPtr & operator ++ (void) {assert(Pointer != 0);++Pointer;return(*this);} + SmartPtr operator -- (int) {assert(Pointer != 0);SmartPtr temp = *this;--Pointer;return(temp);} + SmartPtr & operator -- (void) {assert(Pointer != 0);--Pointer;return(*this);} + + SmartPtr & operator = (SmartPtr const & rvalue) {Pointer = rvalue.Pointer;return(*this);} + T * operator -> (void) const {assert(Pointer != 0);return(Pointer);} + T & operator * (void) const {assert(Pointer != 0);return(*Pointer);} + + private: + T * Pointer; +}; + + +#endif diff --git a/CODE/KEY.CPP b/CODE/KEY.CPP new file mode 100644 index 0000000..8cbc0e2 --- /dev/null +++ b/CODE/KEY.CPP @@ -0,0 +1,758 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/KEY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages* + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboar* + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keybo* + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "key.h" + +#include "monoc.h" + +//void Message_Loop(void); + +//WWKeyboardClass * _Kbd = NULL; + + +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) : + MouseQX(0), + MouseQY(0), + Head(0), + Tail(0) +{ +// _Kbd = this; + + memset(KeyState, '\0', sizeof(KeyState)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + + unsigned short temp = Fetch_Element(); + if (Is_Mouse_Key(temp)) { + MouseQX = Fetch_Element(); + MouseQY = Fetch_Element(); + } + return(temp); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * * + * This checks the specified key code to see if it refers to the mouse buttons. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the key a mouse button key? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Mouse_Key(unsigned short key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + * 09/24/1996 JLB : Converted to new style keyboard system. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Check(void) const +{ + ((WWKeyboardClass *)this)->Fill_Buffer_From_System(); + if (Is_Buffer_Empty()) return(false); + return(Peek_Element()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Get(void) +{ + while (!Check()) {} // wait for key in buffer + return (Buff_Get()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put(unsigned short key) +{ + if (!Is_Buffer_Full()) { + Put_Element(key); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Key_Message(unsigned short vk_key, bool release) +{ + /* + ** Get the status of all of the different keyboard modifiers. Note, only pay attention + ** to numlock and caps lock if we are dealing with a key that is affected by them. Note + ** that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + ** would be incompatible with the dos version. + */ + if (!Is_Mouse_Key(vk_key)) { + if (((GetKeyState(VK_SHIFT) & 0x8000) != 0) || + ((GetKeyState(VK_CAPITAL) & 0x0008) != 0) || + ((GetKeyState(VK_NUMLOCK) & 0x0008) != 0)) { + + vk_key |= WWKEY_SHIFT_BIT; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + vk_key |= WWKEY_CTRL_BIT; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + vk_key |= WWKEY_ALT_BIT; + } + } + + if (release) { + vk_key |= WWKEY_RLS_BIT; + } + + /* + ** Finally use the put command to enter the key into the keyboard + ** system. + */ + return(Put(vk_key)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer. * + * * + * This routine will store the mouse type event into the keyboard buffer. It also checks * + * to ensure that there is enough room in the buffer so that partial mouse events won't * + * be recorded. * + * * + * INPUT: vk_key -- The mouse key message itself. * + * * + * x,y -- The mouse coordinates at the time of the event. * + * * + * release -- Is this a mouse button release? * + * * + * OUTPUT: bool; Was the event stored sucessfully into the keyboard buffer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release) +{ + if (Available_Buffer_Room() >= 3 && Is_Mouse_Key(vk_key)) { + Put_Key_Message(vk_key, release); + Put((unsigned short)x); + Put((unsigned short)y); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * * + * This routine will convert the key code specified into an ASCII value. This takes into * + * consideration the language and keyboard mapping of the host Windows system. * + * * + * INPUT: key -- The key code to convert into ASCII. * + * * + * OUTPUT: Returns with the key converted into ASCII. If the key has no ASCII equivalent, * + * then '\0' is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +char WWKeyboardClass::To_ASCII(unsigned short key) +{ + /* + ** Released keys never translate into an ASCII value. + */ + if (key & WWKEY_RLS_BIT) { + return('\0'); + } + + /* + ** Set the KeyState buffer to reflect the shift bits stored in the key value. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0x80; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0x80; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0x80; + } + + /* + ** Ask windows to translate the key into an ASCII equivalent. + */ + char buffer[10]; + int result = 1; + int scancode = 0; + + scancode = MapVirtualKey(key & 0xFF, 0); + result = ToAscii((UINT)(key & 0xFF), (UINT)scancode, (PBYTE)KeyState, (LPWORD)buffer, (UINT)0); + + /* + ** Restore the KeyState buffer back to pristine condition. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0; + } + + /* + ** If Windows could not perform the translation as expected, then + ** return with a null ASCII value. + */ + if (result != 1) { + return('\0'); + } + + return(buffer[0]); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * * + * This routine will examine the key specified to see if it is currently being held down. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the specified key currently being held down? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +WWKeyboardClass::Down(unsigned short key) +{ + return(GetAsyncKeyState(key & 0xFF)); +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * * + * This routine will extract the next pending element in the keyboard queue. If there is * + * no element available, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the element extracted from the queue. An empty queue is signified * + * by a 0 return value. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Fetch_Element(void) +{ + unsigned short val = 0; + if (Head != Tail) { + val = Buffer[Head]; + + Head = (Head + 1) % ARRAY_SIZE(Buffer); + } + return(val); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * * + * This routine will examine and return with the next element in the keyboard buffer but * + * it will not alter or remove that element. Use this routine to see what is pending in * + * the keyboard queue. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next element in the keyboard queue. If the keyboard buffer is * + * empty, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Peek_Element(void) const +{ + if (!Is_Buffer_Empty()) { + return(Buffer[Head]); + } + return(0); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * * + * This will put one keyboard data element into the keyboard buffer. Typically, this data * + * is a key code, but it might be mouse coordinates. * + * * + * INPUT: val -- The data element to add to the keyboard buffer. * + * * + * OUTPUT: bool; Was the keyboard element added successfully? A failure would indicate that * + * the keyboard buffer is full. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Element(unsigned short val) +{ + if (!Is_Buffer_Full()) { + int temp = (Tail+1) % ARRAY_SIZE(Buffer); + Buffer[Tail] = val; + Tail = temp; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * * + * This routine will examine the keyboard buffer to determine if it is completely * + * full of queued keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer completely full? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Full(void) const +{ + if ((Tail + 1) % ARRAY_SIZE(Buffer) == Head) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * * + * This routine will examine the keyboard buffer to see if it contains no events at all. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer currently without any pending events queued? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Empty(void) const +{ + if (Head == Tail) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages. * + * * + * This routine will extract and process any windows messages in the windows message * + * queue. It is presumed that the normal message handler will call the keyboard * + * message processing function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Fill_Buffer_From_System(void) +{ + if (!Is_Buffer_Full()) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!GetMessage( &msg, NULL, 0, 0 )) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + + +/*********************************************************************************************** + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * * + * This routine will clear the keyboard buffer of all pending keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Clear(void) +{ + /* + ** Extract any windows pending keyboard message events and then clear out the keyboard + ** buffer. + */ + Fill_Buffer_From_System(); + Head = Tail; + + /* + ** Perform a second clear to handle the rare case of the keyboard buffer being full and there + ** still remains keyboard related events in the windows message queue. + */ + Fill_Buffer_From_System(); + Head = Tail; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboard * + * * + * This routine will examine the Windows message specified. If the message relates to an * + * event that the keyboard input system needs to process, then it will be processed * + * accordingly. * + * * + * INPUT: window -- Handle to the window receiving the message. * + * * + * message -- The message number of this event. * + * * + * wParam -- The windows specific word parameter (meaning depends on message). * + * * + * lParam -- The windows specific long word parameter (meaning is message dependant)* + * * + * OUTPUT: bool; Was this keyboard message recognized and processed? A 'false' return value * + * means that the message should be processed normally. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam) +{ + bool processed = false; + + /* + ** Examine the message to see if it is one that should be processed. Only keyboard and + ** pertinant mouse messages are processed. + */ + switch (message) { + + /* + ** System key has been pressed. This is the normal keyboard event message. + */ + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if (wParam == VK_SCROLL) { + Stop_Execution(); + } else { + Put_Key_Message((unsigned short)wParam); + } + processed = true; + break; + + /* + ** The key has been released. This is the normal key release message. + */ + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message((unsigned short)wParam, true); + processed = true; + break; + + /* + ** Press of the left mouse button. + */ + case WM_LBUTTONDOWN: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the left mouse button. + */ + case WM_LBUTTONUP: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Double click of the left mouse button. Fake this into being + ** just a rapid click of the left button twice. + */ + case WM_LBUTTONDBLCLK: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Press of the middle mouse button. + */ + case WM_MBUTTONDOWN: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the middle mouse button. + */ + case WM_MBUTTONUP: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Middle button double click gets translated into two + ** regular middle button clicks. + */ + case WM_MBUTTONDBLCLK: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Right mouse button press. + */ + case WM_RBUTTONDOWN: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Right mouse button release. + */ + case WM_RBUTTONUP: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Translate a double click of the right button + ** into being just two regular right button clicks. + */ + case WM_RBUTTONDBLCLK: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** If the message is not pertinant to the keyboard system, + ** then do nothing. + */ + default: + break; + } + + /* + ** If this message has been processed, then pass it on to the system + ** directly. + */ + if (processed) { + DefWindowProc(window, message, wParam, lParam); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keyboa * + * * + * This examines the keyboard buffer queue and determine how many elements are available * + * for use before the buffer becomes full. Typical use of this would be when inserting * + * mouse events that require more than one element. Such an event must detect when there * + * would be insufficient room in the buffer and bail accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of elements that may be stored in to the keyboard buffer * + * before it becomes full and cannot accept any more elements. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Available_Buffer_Room(void) const +{ + int avail; + if (Head == Tail) { + avail = ARRAY_SIZE(Buffer); + } + if (Head < Tail) { + avail = Tail - Head; + } + if (Head > Tail) { + avail = (Tail + ARRAY_SIZE(Buffer)) - Head; + } + return(avail); +} diff --git a/CODE/KEY.H b/CODE/KEY.H new file mode 100644 index 0000000..cb9ea83 --- /dev/null +++ b/CODE/KEY.H @@ -0,0 +1,659 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/KEY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#ifndef WIN32 +#include "..\wwflat32\include\keyboard.h" +#else + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + bool Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Put_Element(unsigned short val); + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + bool Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release = false); + int Available_Buffer_Room(void) const; + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + int Head; + int Tail; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +typedef enum KeyASCIIType { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +} KeyASCIIType; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0 = VK_0, + KN_1 = VK_1, + KN_2 = VK_2, + KN_3 = VK_3, + KN_4 = VK_4, + KN_5 = VK_5, + KN_6 = VK_6, + KN_7 = VK_7, + KN_8 = VK_8, + KN_9 = VK_9, + KN_A = VK_A, + KN_B = VK_B, + KN_BACKSLASH = VK_NONE_DC, + KN_BACKSPACE = VK_BACK, + KN_C = VK_C, + KN_CAPSLOCK = VK_CAPITAL, + KN_CENTER = VK_CLEAR, + KN_COMMA = VK_NONE_BC, + KN_D = VK_D, + KN_DELETE = VK_DELETE, + KN_DOWN = VK_DOWN, + KN_DOWNLEFT = VK_END, + KN_DOWNRIGHT = VK_NEXT, + KN_E = VK_E, + KN_END = VK_END, + KN_EQUAL = VK_NONE_BB, + KN_ESC = VK_ESCAPE, + KN_E_DELETE = VK_DELETE, + KN_E_DOWN = VK_NUMPAD2, + KN_E_END = VK_NUMPAD1, + KN_E_HOME = VK_NUMPAD7, + KN_E_INSERT = VK_INSERT, + KN_E_LEFT = VK_NUMPAD4, + KN_E_PGDN = VK_NUMPAD3, + KN_E_PGUP = VK_NUMPAD9, + KN_E_RIGHT = VK_NUMPAD6, + KN_E_UP = VK_NUMPAD8, + KN_F = VK_F, + KN_F1 = VK_F1, + KN_F10 = VK_F10, + KN_F11 = VK_F11, + KN_F12 = VK_F12, + KN_F2 = VK_F2, + KN_F3 = VK_F3, + KN_F4 = VK_F4, + KN_F5 = VK_F5, + KN_F6 = VK_F6, + KN_F7 = VK_F7, + KN_F8 = VK_F8, + KN_F9 = VK_F9, + KN_G = VK_G, + KN_GRAVE = VK_NONE_C0, + KN_H = VK_H, + KN_HOME = VK_HOME, + KN_I = VK_I, + KN_INSERT = VK_INSERT, + KN_J = VK_J, + KN_K = VK_K, + KN_KEYPAD_ASTERISK= VK_MULTIPLY, + KN_KEYPAD_MINUS = VK_SUBTRACT, + KN_KEYPAD_PLUS = VK_ADD, + KN_KEYPAD_RETURN = VK_RETURN, + KN_KEYPAD_SLASH = VK_DIVIDE, + KN_L = VK_L, + KN_LALT = VK_MENU, + KN_LBRACKET = VK_NONE_DB, + KN_LCTRL = VK_CONTROL, + KN_LEFT = VK_LEFT, + KN_LMOUSE = VK_LBUTTON, + KN_LSHIFT = VK_SHIFT, + KN_M = VK_M, + KN_MINUS = VK_NONE_BD, + KN_N = VK_N, + KN_NUMLOCK = VK_NUMLOCK, + KN_O = VK_O, + KN_P = VK_P, + KN_PAUSE = VK_PAUSE, + KN_PERIOD = VK_NONE_BE, + KN_PGDN = VK_NEXT, + KN_PGUP = VK_PRIOR, + KN_PRNTSCRN = VK_PRINT, + KN_Q = VK_Q, + KN_R = VK_R, + KN_RALT = VK_MENU, + KN_RBRACKET = VK_NONE_DD, + KN_RCTRL = VK_CONTROL, + KN_RETURN = VK_RETURN, + KN_RIGHT = VK_RIGHT, + KN_RMOUSE = VK_RBUTTON, + KN_RSHIFT = VK_SHIFT, + KN_S = VK_S, + KN_SCROLLLOCK = VK_SCROLL, + KN_SEMICOLON = VK_NONE_BA, + KN_SLASH = VK_NONE_BF, + KN_SPACE = VK_SPACE, + KN_SQUOTE = VK_NONE_DE, + KN_T = VK_T, + KN_TAB = VK_TAB, + KN_U = VK_U, + KN_UP = VK_UP, + KN_UPLEFT = VK_HOME, + KN_UPRIGHT = VK_PRIOR, + KN_V = VK_V, + KN_W = VK_W, + KN_X = VK_X, + KN_Y = VK_Y, + KN_Z = VK_Z, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +} KeyNumType; + + +//extern WWKeyboardClass *_Kbd; +#endif + +#endif diff --git a/CODE/KEYBOARD.CPP b/CODE/KEYBOARD.CPP new file mode 100644 index 0000000..4b40951 --- /dev/null +++ b/CODE/KEYBOARD.CPP @@ -0,0 +1,469 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.74 23 Sep 1996 12:36:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : September 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +#include "monoc.h" + +//void Message_Loop(void); + +WWKeyboardClass * _Kbd = NULL; + + +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) : + MouseQX(0), + MouseQY(0), + Head(0), + Tail(0) +// MState(0), +// Conditional(0), +// CurrentCursor(0) +{ + _Kbd = this; + + memset(KeyState, '\0', sizeof(KeyState)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + + unsigned short temp = Fetch_Element(); + if (Is_Mouse_Key(temp)) { + MouseQX = Fetch_Element(); + MouseQY = Fetch_Element(); + } + return(temp); +} + + +bool WWKeyboardClass::Is_Mouse_Key(unsigned short key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + * 09/24/1996 JLB : Converted to new style keyboard system. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Check(void) const +{ + ((WWKeyboardClass *)this)->Fill_Buffer_From_System(); + if (Is_Buffer_Empty()) return(false); + return(Peek_Element()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Get(void) +{ + while (!Check()) {} // wait for key in buffer + return (Buff_Get()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put(unsigned short key) +{ + if (!Is_Buffer_Full()) { + Put_Element(key); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Key_Message(unsigned short vk_key, bool release) +{ + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. Note + // that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + // would be incompatible with the dos version. + // + if (!Is_Mouse_Key(vk_key)) { + if (((GetKeyState(VK_SHIFT) & 0x8000) != 0) || + ((GetKeyState(VK_CAPITAL) & 0x0008) != 0) || + ((GetKeyState(VK_NUMLOCK) & 0x0008) != 0)) { + + vk_key |= WWKEY_SHIFT_BIT; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + vk_key |= WWKEY_CTRL_BIT; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + vk_key |= WWKEY_ALT_BIT; + } + } + + if (release) { + vk_key |= WWKEY_RLS_BIT; + } + + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key)); + + + +} + + +char WWKeyboardClass::To_ASCII(unsigned short key) +{ + /* + ** Released keys never translate into an ASCII value. + */ + if (key & WWKEY_RLS_BIT) { + return('\0'); + } + + /* + ** Set the KeyState buffer to reflect the shift bits stored in the key value. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0x80; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0x80; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0x80; + } + + /* + ** Ask windows to translate the key into an ASCII equivalent. + */ + char buffer[10]; + int result = 1; + int scancode = 0; + char override = '\0'; + + switch (key & 0xFF) { +// case KN_RETURN: +// override = KA_RETURN; +// break; + +// case KN_BACKSPACE: +// override = KA_BACKSPACE; +// break; + + default: + scancode = MapVirtualKey(key & 0xFF, 0); + result = ToAscii((UINT)(key & 0xFF), (UINT)scancode, (PBYTE)KeyState, (LPWORD)buffer, (UINT)0); + break; + } + + /* + ** Restore the KeyState buffer back to pristine condition. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0; + } + + /* + ** If Windows could not perform the translation as expected, then + ** return with a null ASCII value. + */ + if (result != 1) { + return('\0'); + } + + if (override != 0) { + return(override); + } + + return(buffer[0]); +} + + +WWKeyboardClass::Down(unsigned short key) +{ + return(GetAsyncKeyState(key & 0xFF)); +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + + + +unsigned short WWKeyboardClass::Fetch_Element(void) +{ + unsigned short val = 0; + if (Head != Tail) { + val = Buffer[Head]; + + Head = (Head + 1) % ARRAY_SIZE(Buffer); + } + return(val); +} + + +unsigned short WWKeyboardClass::Peek_Element(void) const +{ + if (!Is_Buffer_Empty()) { + return(Buffer[Head]); + } + return(0); +} + + +bool WWKeyboardClass::Put_Element(unsigned short val) +{ + if (!Is_Buffer_Full()) { + int temp = (Tail+1) % ARRAY_SIZE(Buffer); + Buffer[Tail] = val; + Tail = temp; + return(true); + } + return(false); +} + + +bool WWKeyboardClass::Is_Buffer_Full(void) const +{ + if ((Tail + 1) % ARRAY_SIZE(Buffer) == Head) { + return(true); + } + return(false); +} + + +bool WWKeyboardClass::Is_Buffer_Empty(void) const +{ + if (Head == Tail) { + return(true); + } + return(false); +} + + +void WWKeyboardClass::Fill_Buffer_From_System(void) +{ + if (!Is_Buffer_Full()) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + if (!GetMessage( &msg, NULL, 0, 0 )) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + + +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message((unsigned short)wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message((unsigned short)wParam, true); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_LBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_MBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + Put_Key_Message(VK_RBUTTON, true); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + +// case WM_MOUSEMOVE: +// if (CurrentCursor) +// SetCursor(CurrentCursor); +// break; + } +} diff --git a/CODE/KEYBOARD.H b/CODE/KEYBOARD.H new file mode 100644 index 0000000..1086ab7 --- /dev/null +++ b/CODE/KEYBOARD.H @@ -0,0 +1,652 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.74 23 Sep 1996 12:36:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Put_Element(unsigned short val); + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + int Head; + int Tail; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +}; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0 = VK_0, + KN_1 = VK_1, + KN_2 = VK_2, + KN_3 = VK_3, + KN_4 = VK_4, + KN_5 = VK_5, + KN_6 = VK_6, + KN_7 = VK_7, + KN_8 = VK_8, + KN_9 = VK_9, + KN_A = VK_A, + KN_B = VK_B, + KN_BACKSLASH = VK_NONE_DC, + KN_BACKSPACE = VK_BACK, + KN_C = VK_C, + KN_CAPSLOCK = VK_CAPITAL, + KN_CENTER = VK_CLEAR, + KN_COMMA = VK_NONE_BC, + KN_D = VK_D, + KN_DELETE = VK_DELETE, + KN_DOWN = VK_DOWN, + KN_DOWNLEFT = VK_END, + KN_DOWNRIGHT = VK_NEXT, + KN_E = VK_E, + KN_END = VK_END, + KN_EQUAL = VK_NONE_BB, + KN_ESC = VK_ESCAPE, + KN_E_DELETE = VK_DELETE, + KN_E_DOWN = VK_NUMPAD2, + KN_E_END = VK_NUMPAD1, + KN_E_HOME = VK_NUMPAD7, + KN_E_INSERT = VK_INSERT, + KN_E_LEFT = VK_NUMPAD4, + KN_E_PGDN = VK_NUMPAD3, + KN_E_PGUP = VK_NUMPAD9, + KN_E_RIGHT = VK_NUMPAD6, + KN_E_UP = VK_NUMPAD8, + KN_F = VK_F, + KN_F1 = VK_F1, + KN_F10 = VK_F10, + KN_F11 = VK_F11, + KN_F12 = VK_F12, + KN_F2 = VK_F2, + KN_F3 = VK_F3, + KN_F4 = VK_F4, + KN_F5 = VK_F5, + KN_F6 = VK_F6, + KN_F7 = VK_F7, + KN_F8 = VK_F8, + KN_F9 = VK_F9, + KN_G = VK_G, + KN_GRAVE = VK_NONE_C0, + KN_H = VK_H, + KN_HOME = VK_HOME, + KN_I = VK_I, + KN_INSERT = VK_INSERT, + KN_J = VK_J, + KN_K = VK_K, + KN_KEYPAD_ASTERISK= VK_MULTIPLY, + KN_KEYPAD_MINUS = VK_SUBTRACT, + KN_KEYPAD_PLUS = VK_ADD, + KN_KEYPAD_RETURN = VK_RETURN, + KN_KEYPAD_SLASH = VK_DIVIDE, + KN_L = VK_L, + KN_LALT = VK_MENU, + KN_LBRACKET = VK_NONE_DB, + KN_LCTRL = VK_CONTROL, + KN_LEFT = VK_LEFT, + KN_LMOUSE = VK_LBUTTON, + KN_LSHIFT = VK_SHIFT, + KN_M = VK_M, + KN_MINUS = VK_NONE_BD, + KN_N = VK_N, + KN_NUMLOCK = VK_NUMLOCK, + KN_O = VK_O, + KN_P = VK_P, + KN_PAUSE = VK_PAUSE, + KN_PERIOD = VK_NONE_BE, + KN_PGDN = VK_NEXT, + KN_PGUP = VK_PRIOR, + KN_PRNTSCRN = VK_PRINT, + KN_Q = VK_Q, + KN_R = VK_R, + KN_RALT = VK_MENU, + KN_RBRACKET = VK_NONE_DD, + KN_RCTRL = VK_CONTROL, + KN_RETURN = VK_RETURN, + KN_RIGHT = VK_RIGHT, + KN_RMOUSE = VK_RBUTTON, + KN_RSHIFT = VK_SHIFT, + KN_S = VK_S, + KN_SCROLLLOCK = VK_SCROLL, + KN_SEMICOLON = VK_NONE_BA, + KN_SLASH = VK_NONE_BF, + KN_SPACE = VK_SPACE, + KN_SQUOTE = VK_NONE_DE, + KN_T = VK_T, + KN_TAB = VK_TAB, + KN_U = VK_U, + KN_UP = VK_UP, + KN_UPLEFT = VK_HOME, + KN_UPRIGHT = VK_PRIOR, + KN_V = VK_V, + KN_W = VK_W, + KN_X = VK_X, + KN_Y = VK_Y, + KN_Z = VK_Z, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +} KeyNumType; + + +extern WWKeyboardClass *_Kbd; + +#endif diff --git a/CODE/KEYFBUFF.ASM b/CODE/KEYFBUFF.ASM new file mode 100644 index 0000000..1a2aa62 --- /dev/null +++ b/CODE/KEYFBUFF.ASM @@ -0,0 +1,2739 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "gbuffer.inc" + + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +DATASEG + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + +CODESEG + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + +;--------------------------------------------------------------------------- + +global _Int3:near +proc _Int3 near + int 3 + ret +endp + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Buffer_Frame_To_Page:NEAR + PROC Buffer_Frame_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + + ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + + + cmp [ src ] , 0 + jz ??real_out + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +??check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je ??check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +??check_trans: + test [ flags ] , SHAPE_TRANS + jz ??check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +??check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz ??check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- +??check_fading: + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz ??check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [ FadingNum ] , eax + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +??check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz ??check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge ??check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp ??pred_cont + +??check_range: + and eax , PRED_MASK ; keep entries within bounds + +??pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +??pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +??pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] ; add width + add eax , [ (GraphicViewPort esi) . GVPXAdd ] ; add x add + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge ??pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz ??setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +??setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , [ BufferFrameTable + ebx ] ; get table value + mov [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + cld + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + + sub eax , [ x_pixel ] + jle ??real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +??real_out: + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** + +BF_Copy: + + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??forward_loop_dword + + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Trans: + +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ ??trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +??trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +??trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +??trans_reference: + dec ecx + jge ??trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??trans_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_reference - ??ghost_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_reference: + dec ecx + jge ??ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_t_reference - ??ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_t_reference: + dec ecx + jge ??ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_reference - ??fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_t_reference - ??fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_reference - ??ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_t_reference - ??ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_reference - ??predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +??predator_reference: + dec ecx + jge ??predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_t_reference - ??predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_t_reference: + dec ecx + jge ??predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_reference - ??predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_reference: + dec ecx + jge ??predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_t_reference - ??predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_t_reference: + dec ecx + jge ??predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_reference - ??predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_t_reference - ??predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_reference - ??predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_t_reference - ??predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + + ENDP Buffer_Frame_To_Page +END + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 16, 1992 * +;* * +;* Last Update : October 2, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* Normal_Draw -- Function that writes a normal pixel line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 +IDEAL_MODE EQU 1 + INCLUDE "wwlib.i" + + ;------------------------------------------------------------------- + ; Extern all the library variables that this module requires + ;------------------------------------------------------------------- + + EXTRN C MaskPage:WORD + EXTRN C BackGroundPage:WORD + + ;------------------------------------------------------------------- + ; Define all the equates that this module requires + ;------------------------------------------------------------------- + +WIN_X EQU 0 ; offset for the x coordinate +WIN_Y EQU 2 ; offset for the y coordinate +WIN_WIDTH EQU 4 ; offset for the window width +WIN_HEIGHT EQU 6 ; offset for the window height +BYTESPERROW EQU 320 ; number of bytes per row + +FLAG_NORMAL EQU 0 ; flag for normal draw + +FLAG_GHOST EQU 1 ; This flag enables the ghost +FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent +FLAG_TRANS EQU 4 ; flag for transparent draw +FLAG_PRIORITY EQU 8 ; flag for priority draw + + ; fx on the above flags + +FLAG_MASK EQU 15 ; used to and of uneeded bits + +SHAPE_NORMAL EQU 0000h ; Standard shape. +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally. +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically. +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) + +SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point. +SHAPE_TRANS EQU 0040h ; has transparency + + +;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num). +;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect. +;SHAPE_COMPACT EQU 0400h ; Never use this bit. +SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing. + +SHAPE_GHOST EQU 1000h ; Transluscent table process. +;SHAPE_SHADOW EQU 2000h ; ???? +;SHAPE_PARTIAL EQU 4000h ; ???? +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table). + + +; MBL MOD 12.1.92 + +CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display +CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit +CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits +NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit + +; END MBL MOD + + + CODESEG + + ; 1 = GHOST (all odd entrys are prefixed with Ghost_) + ; 2 = BLAAAH + ; 4 = Trans (prfx) + ; 8 = Prior (prfx) + + +;--------------------------------------------------------------------------- +; Define the table of different line draw types +;--------------------------------------------------------------------------- + +LineTable DW WSA_Normal_Draw ;0 + DW Ghost_Normal_Draw ;1 + DW 0 ;2 + DW 0 ;3 + + DW Transparent_Draw ;4 + DW Ghost_Transparent_Draw ;5 + DW 0 ;6 + DW 0 ;7 + + DW Priority_Draw ;8 + DW Ghost_Priority_Draw ;9 + DW 0 ;10 + DW 0 ;11 + + DW Priority_Transparent_Draw ;12 + DW Ghost_Priority_Transparent_Draw ;13 + DW 0 ;14 + DW 0 ;15 + + + +;*************************************************************************** +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/16/1992 PWG : Created. * +;*=========================================================================* + PUBLIC C Buffer_Frame_To_LogicPage + PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi + + ;------------------------------------------------------------------- + ; Define the arguements that our program takes. + ;------------------------------------------------------------------- + + ARG x_pixel:WORD ; x pixel position to draw at + ARG y_pixel:WORD ; y pixel position to draw at + ARG pixel_w:WORD ; pixel width of draw region + ARG pixel_h:WORD ; pixel height of draw region + ARG win:WORD ; window to clip around + ARG flags:WORD ; flags that this routine will take + ARG buffer:DWORD ; pointer to the buffer with data + ARG args:WORD + + ;------------------------------------------------------------------- + ; Define the local variables that our program uses + ;------------------------------------------------------------------- + + LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table + LOCAL Translucent:DWORD ; ptr to the actual translucent table + + LOCAL win_x1:WORD ; clip window left x pixel position + LOCAL win_x2:WORD ; clip window right x pixel position + LOCAL win_y1:WORD ; clip window top y pixel position + LOCAL win_y2:WORD ; clip window bottom y pixel position + LOCAL clipleft:WORD ; number of pixels to clip on left + LOCAL clipright:WORD ; number of pixels to clip on right + LOCAL nextline:WORD ; offset to the next line + LOCAL putmiddle:WORD ; routine to call to put the middle + LOCAL maskpage:WORD ; location of the depth masks + LOCAL background:WORD ; location of the background data + LOCAL jflags:WORD ; location of the background data + + LOCAL priority:BYTE ; the priority level of the back + + push fs + + xor ecx,ecx + + ;-------------------------------------------------------------------- + ; Check to see if we have supplied any GHOST tables. + ;-------------------------------------------------------------------- + push di + + mov di,6 + mov [jflags],0 + +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz short ??no_ghost ; if not then skip and do more + + or [jflags],FLAG_GHOST + + les ax,[DWORD PTR buffer + di] + + ; get the "are we really translucent?" table + mov [WORD PTR IsTranslucent],ax + mov [WORD PTR IsTranslucent + 2],es + add ax,0100h ; add to offset for tables + + ; get the "ok we are translucent!!" table + mov [WORD PTR Translucent],ax + mov [WORD PTR Translucent + 2],es + + add di,4 + +??no_ghost: + + pop di + + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [flags],SHAPE_CENTER ; does this need to be centered? + je short ??no_centering ; if not the skip over this stuff + + mov ax,[pixel_w] + mov bx,[pixel_h] + sar ax,1 + sar bx,1 + sub [x_pixel],ax + sub [y_pixel],bx + +??no_centering: + mov ax,[flags] + and ax,SHAPE_PRIORITY+SHAPE_TRANS + cmp ax,SHAPE_PRIORITY+SHAPE_TRANS + jne short ??test_trans + + or [jflags],FLAG_PRIORITY_TRANS + jmp short ??priority + + ;------------------------------------------------------------------- + ; Get the trans information if we need to get it + ;------------------------------------------------------------------- +??test_trans: + test [flags],SHAPE_TRANS ; does this draw use transparencies? + je short ??test_priority ; if not the skip over this junk + + or [jflags],FLAG_TRANS + +??test_priority: + ;------------------------------------------------------------------- + ; Get the priority information if we need to get it + ;------------------------------------------------------------------- + test [flags],SHAPE_PRIORITY ; does this draw use priorities? + je short ??no_priority ; if not the skip over this junk + + or [jflags],FLAG_PRIORITY + +??priority: + mov ax,[BackGroundPage] ; get the background page from ds + mov [background],ax ; and store it on the stack + mov ax,[MaskPage] ; get the mask page from ds + mov [maskpage],ax ; and store it on the stack + mov ax,[WORD PTR buffer + 4]; get the priority level from args + mov [priority],al ; and store it in a local + + ;------------------------------------------------------------------- + ; Get the draw routine that we are going to draw with + ;------------------------------------------------------------------- +??no_priority: +; mov bx,[flags] ; load in the current flags byte +; and bx,FLAG_MASK ; prevent lockup on bad value + mov bx,[jflags] ; load in the jump table flags + shl bx,1 + mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table + mov [putmiddle],ax ; store off the new offset + + ;------------------------------------------------------------------- + ; Get a pointer to the logic page to where we will draw our buffer + ;------------------------------------------------------------------- + push [LogicPage] ; push the current logic page + call FAR PTR Get_Page ; get the physical page address + add sp,2 ; pull the parameter from the stack + mov es,dx ; store the address in the dest + + ;-------------------------------------------------------------------- + ; Point DI to the beginning of the window that we need to look at. + ; that way we can access all of the info through di. + ;-------------------------------------------------------------------- + mov si,OFFSET WindowList ; get the offset of the window list + mov cl,4 ; shift 3 times = multiply by 16 + mov ax,[win] ; get the window number we are using + shl ax,cl ; each window is 8 words long + add si,ax ; add that into the offset of window + + ;-------------------------------------------------------------------- + ; Place all the clipping values on the stack so our function will + ; be truly re-entrant and will not need to shadow these values. + ;-------------------------------------------------------------------- + mov cl,3 ; to convert x to pixel mult by 8 + mov ax,[si + WIN_X] ; get the left clip position + shl ax,cl ; convert to a pixel x position + mov [win_x1],ax ; store the left edge of window + mov [win_x2],ax + + mov ax,[si + WIN_WIDTH] ; get the width of the window + shl ax,cl ; convert to a pixel width + add [win_x2],ax ; add to get the right window edge + + mov ax,[si + WIN_Y] ; get the win y coordinate to clip + mov [win_y1],ax ; and save it onto the stack + + add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord + mov [win_y2],ax ; and save it onto the stack + + test [flags],SHAPE_WIN_REL ; is this window relative? + je short ??get_buffer ; if not the skip over + + mov ax,[win_x1] ; get left edge of window + add [x_pixel],ax ; add to x pixel position + mov ax,[win_y1] ; get top edge of window + add [y_pixel],ax ; add to y pixel position + + ;-------------------------------------------------------------------- + ; Get a pointer to the source buffer so we can handle the clipping + ;-------------------------------------------------------------------- +??get_buffer: + lds si,[buffer] ; get a pointer to the buffer + + ;-------------------------------------------------------------------- + ; Check the top of our shape and clip any lines that are necessary + ;-------------------------------------------------------------------- + mov ax,[y_pixel] ; get the y_pixel draw position + sub ax,[win_y1] ; subtract out the window y top + jns short ??check_bottom ; skip if y below window top + add ax,[pixel_h] ; add in the height of the region + jg short ??clip_top ; if positive then clip top lines + +??jump_exit: + jmp ??exit ; otherwise completely clipped + +??clip_top: + xchg [pixel_h],ax + sub ax,[pixel_h] + add [y_pixel],ax + mul [pixel_w] ; convert to number of bytes to skip + add si,ax ; skip past the necessary bytes + + ;-------------------------------------------------------------------- + ; Check the bottom of our shape and clip it if necessary + ;-------------------------------------------------------------------- +??check_bottom: + mov ax,[win_y2] ; get the bottom y of the window + sub ax,[y_pixel] ; subtract of the y to draw at + js ??jump_exit ; if its signed then nothing to draw + jz ??jump_exit ; if its zero then nothing to draw + + cmp ax,[pixel_h] ; if more room to draw then height + jae short ??clip_x_left ; then go check the left clip + mov [pixel_h],ax ; clip all but amount that will fit + +??clip_x_left: + mov [clipleft],0 ; clear clip on left of region + mov ax,[x_pixel] ; get the pixel x of draw region + sub ax,[win_x1] ; pull out the window coordinate + jns short ??clip_x_right + neg ax ; negate to get amnt to skip in buf + mov [clipleft],ax ; store it in the left clip info + add [x_pixel],ax ; move to the edge of the window + sub [pixel_w],ax ; pull it out of the pixel width + +??clip_x_right: + mov [clipright],0 ; clear clip on right of region + mov ax,[win_x2] ; get the window x of clip region + sub ax,[x_pixel] ; subtract the draw edge of region + js ??jump_exit ; if its negative then get out + jz ??jump_exit ; if its zero then get out + + cmp ax,[pixel_w] ; is space available larger than w + jae short ??draw_prep ; if so then go get drawing + + + xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax) + sub ax,[pixel_w] ; pull out the amount to draw + mov [clipright],ax ; this is the amount to clip on right + +??draw_prep: + push si ; save off source pos in buffer + push ds ; both offset and segment + mov ax,@data + mov ds,ax + mov bx,[y_pixel] + shl bx,1 ; shift left by 1 for word table look + lds si,[YTable] ; get the address of the ytable + mov di,[ds:si+bx] ; look up the multiplied value + pop ds ; restore source pos in buffer + pop si ; both offset and segment + + add di,[x_pixel] ; add in the x pixel position + mov [nextline],di ; save it off in the next line + + ;-------------------------------------------------------------------- + ; Now determine the type of the shape and process it in the proper + ; way. + ;-------------------------------------------------------------------- + mov dx,[pixel_h] + + ; Check to see if the WSA is the screen width and there is no + ; clipping. In this case, then a special single call to the + ; line processing routine is possible. + mov ax,[clipleft] + add ax,[clipright] + jne short ??top_of_loop + cmp [pixel_w],BYTESPERROW + jne short ??top_of_loop + + ;------------------------------------ + ; The width of the WSA is the screen width, so just process as + ; one large WSA line. + mov ax,BYTESPERROW + imul dx + mov cx,ax + call [putmiddle] + jmp short ??exit + + ;------------------------------------ + ; Process line by line. +??top_of_loop: + add si,[clipleft] ; skip whats necessary on left edge + mov cx,[pixel_w] ; get the width we need to draw + + ; Copy the source to the destination as appropriate. This routine can + ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past + ; the end of the data. + call [putmiddle] + + add si,[clipright] ; skip past the left clip + add [nextline],BYTESPERROW + mov di,[nextline] + + dec dx + jnz ??top_of_loop + +??exit: + pop fs + ret + ENDP + + +;*************************************************************************** +;* NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;*=========================================================================* + + PROC NOLANGUAGE WSA_Normal_Draw NEAR + + IF 1 + ; This version is marginally faster than the later version. + mov ax,cx + shr cx,2 + rep movsd + and ax,011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + ret + + ELSE + + shr cx,1 ; convert to words (odd pix in carry) + rep movsw ; write out the needed words + adc cx,0 ; add the carry into cx + rep movsb ; write out the odd byte if any + ret + ENDIF + + ENDP + + +;*************************************************************************** +;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 10/02/1994 JLB : Optimized for 250% speed improvement. * +;*=========================================================================* + PROC NOLANGUAGE Transparent_Draw NEAR + + IF 1 + ; Preserve DX since it is used as a scratch register. + push dx + +??loop: + ; Swap DS:SI and ES:DI back in preparation for the REP SCASB + ; instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Remember the bytes remaining in order to calculate the position + ; of the scan when it stops. + mov bx,cx + + ; Scan looking for a non-zero value in the source buffer. + xor al,al + repe scasb + + ; When the loop ends, if the EQ flag is set then the scanning is + ; complete. Jump to the end of the routine in order to fixup the + ; pointers. + je short ??fini + + ; Advance the destination pointer by the amount necessary to match + ; the source movement. DS:SI points to where data should be written. + add si,bx + inc cx ; SCASB leaves CX one too low, fix it. + dec di ; SCASB leaves DI one byte too far, fix it. + sub si,cx + + ; Scan for the duration of non-zero pixels. This yields a count which + ; is used to copy the source data to the destination. Preserve DI. + mov dx,di + mov bx,cx + repne scasb + mov di,dx + + ; Set BX to equal the number of bytes to copy from source to dest. + inc cx ; SCASB leaves CX one too low, fix it. + sub bx,cx + + ; Move the data from ES:DI to DS:SI for BX bytes. + xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining. + + ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Move the data from source to dest. First try to move double + ; words. Then copy the remainder bytes (if any). Putting jumps in + ; this section doesn't result in any savings -- oh well. + mov ax,cx + shr cx,2 + rep movsd + and ax,0011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + + ; Restore CX with the remaining bytes to process. + mov cx,bx + + ; If there are more bytes to process, then loop back. + or cx,cx + jne short ??loop + +??fini: + ; Swap ES:DI and DS:SI back to original orientation. + mov ax,ds + mov bx,es + mov es,ax + mov ds,bx + xchg di,si + + ; Restore DX and return. + pop dx + ret + + ELSE + +??loop_top: + lodsb + or al,al + jz short ??skip + + mov [es:di],al ; store the pixel to the screen +??skip: + inc di + loop ??loop_top + ret + + ENDIF + + ENDP + + +;*************************************************************************** +;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of * +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are * +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + + mov bl,[gs:di] ; get the mask byte for our pixel + + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + + PROC NOLANGUAGE Ghost_Normal_Draw NEAR + +??loop_top: + lodsb + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Transparent_Draw NEAR + +??loop_top: + lodsb + or al,al + jz short ??skip + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron* +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they* +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + mov bl,[gs:di] ; get the mask byte for our pixel + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + + ret + + ENDP + + END diff --git a/CODE/KEYFBUFF.ASM.BAK b/CODE/KEYFBUFF.ASM.BAK new file mode 100644 index 0000000..1a2aa62 --- /dev/null +++ b/CODE/KEYFBUFF.ASM.BAK @@ -0,0 +1,2739 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "gbuffer.inc" + + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +DATASEG + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + +CODESEG + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + +;--------------------------------------------------------------------------- + +global _Int3:near +proc _Int3 near + int 3 + ret +endp + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Buffer_Frame_To_Page:NEAR + PROC Buffer_Frame_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + + ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + + + cmp [ src ] , 0 + jz ??real_out + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +??check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je ??check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +??check_trans: + test [ flags ] , SHAPE_TRANS + jz ??check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +??check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz ??check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- +??check_fading: + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz ??check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [ FadingNum ] , eax + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +??check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz ??check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge ??check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp ??pred_cont + +??check_range: + and eax , PRED_MASK ; keep entries within bounds + +??pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +??pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +??pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] ; add width + add eax , [ (GraphicViewPort esi) . GVPXAdd ] ; add x add + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge ??pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz ??setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +??setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , [ BufferFrameTable + ebx ] ; get table value + mov [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + cld + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + + sub eax , [ x_pixel ] + jle ??real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +??real_out: + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** + +BF_Copy: + + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??forward_loop_dword + + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Trans: + +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ ??trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +??trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +??trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +??trans_reference: + dec ecx + jge ??trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??trans_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_reference - ??ghost_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_reference: + dec ecx + jge ??ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_t_reference - ??ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_t_reference: + dec ecx + jge ??ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_reference - ??fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_t_reference - ??fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_reference - ??ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Ghost_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_t_reference - ??ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_reference - ??predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +??predator_reference: + dec ecx + jge ??predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_t_reference - ??predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_t_reference: + dec ecx + jge ??predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_reference - ??predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_reference: + dec ecx + jge ??predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_t_reference - ??predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_t_reference: + dec ecx + jge ??predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_reference - ??predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_t_reference - ??predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Fading: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_reference - ??predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_loop + + ret + + +;******************************************************************** +;******************************************************************** + +BF_Predator_Ghost_Fading_Trans: + + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_t_reference - ??predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_t_loop + + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + + ENDP Buffer_Frame_To_Page +END + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 16, 1992 * +;* * +;* Last Update : October 2, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* Normal_Draw -- Function that writes a normal pixel line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 +IDEAL_MODE EQU 1 + INCLUDE "wwlib.i" + + ;------------------------------------------------------------------- + ; Extern all the library variables that this module requires + ;------------------------------------------------------------------- + + EXTRN C MaskPage:WORD + EXTRN C BackGroundPage:WORD + + ;------------------------------------------------------------------- + ; Define all the equates that this module requires + ;------------------------------------------------------------------- + +WIN_X EQU 0 ; offset for the x coordinate +WIN_Y EQU 2 ; offset for the y coordinate +WIN_WIDTH EQU 4 ; offset for the window width +WIN_HEIGHT EQU 6 ; offset for the window height +BYTESPERROW EQU 320 ; number of bytes per row + +FLAG_NORMAL EQU 0 ; flag for normal draw + +FLAG_GHOST EQU 1 ; This flag enables the ghost +FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent +FLAG_TRANS EQU 4 ; flag for transparent draw +FLAG_PRIORITY EQU 8 ; flag for priority draw + + ; fx on the above flags + +FLAG_MASK EQU 15 ; used to and of uneeded bits + +SHAPE_NORMAL EQU 0000h ; Standard shape. +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally. +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically. +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) + +SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point. +SHAPE_TRANS EQU 0040h ; has transparency + + +;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num). +;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect. +;SHAPE_COMPACT EQU 0400h ; Never use this bit. +SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing. + +SHAPE_GHOST EQU 1000h ; Transluscent table process. +;SHAPE_SHADOW EQU 2000h ; ???? +;SHAPE_PARTIAL EQU 4000h ; ???? +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table). + + +; MBL MOD 12.1.92 + +CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display +CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit +CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits +NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit + +; END MBL MOD + + + CODESEG + + ; 1 = GHOST (all odd entrys are prefixed with Ghost_) + ; 2 = BLAAAH + ; 4 = Trans (prfx) + ; 8 = Prior (prfx) + + +;--------------------------------------------------------------------------- +; Define the table of different line draw types +;--------------------------------------------------------------------------- + +LineTable DW WSA_Normal_Draw ;0 + DW Ghost_Normal_Draw ;1 + DW 0 ;2 + DW 0 ;3 + + DW Transparent_Draw ;4 + DW Ghost_Transparent_Draw ;5 + DW 0 ;6 + DW 0 ;7 + + DW Priority_Draw ;8 + DW Ghost_Priority_Draw ;9 + DW 0 ;10 + DW 0 ;11 + + DW Priority_Transparent_Draw ;12 + DW Ghost_Priority_Transparent_Draw ;13 + DW 0 ;14 + DW 0 ;15 + + + +;*************************************************************************** +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/16/1992 PWG : Created. * +;*=========================================================================* + PUBLIC C Buffer_Frame_To_LogicPage + PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi + + ;------------------------------------------------------------------- + ; Define the arguements that our program takes. + ;------------------------------------------------------------------- + + ARG x_pixel:WORD ; x pixel position to draw at + ARG y_pixel:WORD ; y pixel position to draw at + ARG pixel_w:WORD ; pixel width of draw region + ARG pixel_h:WORD ; pixel height of draw region + ARG win:WORD ; window to clip around + ARG flags:WORD ; flags that this routine will take + ARG buffer:DWORD ; pointer to the buffer with data + ARG args:WORD + + ;------------------------------------------------------------------- + ; Define the local variables that our program uses + ;------------------------------------------------------------------- + + LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table + LOCAL Translucent:DWORD ; ptr to the actual translucent table + + LOCAL win_x1:WORD ; clip window left x pixel position + LOCAL win_x2:WORD ; clip window right x pixel position + LOCAL win_y1:WORD ; clip window top y pixel position + LOCAL win_y2:WORD ; clip window bottom y pixel position + LOCAL clipleft:WORD ; number of pixels to clip on left + LOCAL clipright:WORD ; number of pixels to clip on right + LOCAL nextline:WORD ; offset to the next line + LOCAL putmiddle:WORD ; routine to call to put the middle + LOCAL maskpage:WORD ; location of the depth masks + LOCAL background:WORD ; location of the background data + LOCAL jflags:WORD ; location of the background data + + LOCAL priority:BYTE ; the priority level of the back + + push fs + + xor ecx,ecx + + ;-------------------------------------------------------------------- + ; Check to see if we have supplied any GHOST tables. + ;-------------------------------------------------------------------- + push di + + mov di,6 + mov [jflags],0 + +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz short ??no_ghost ; if not then skip and do more + + or [jflags],FLAG_GHOST + + les ax,[DWORD PTR buffer + di] + + ; get the "are we really translucent?" table + mov [WORD PTR IsTranslucent],ax + mov [WORD PTR IsTranslucent + 2],es + add ax,0100h ; add to offset for tables + + ; get the "ok we are translucent!!" table + mov [WORD PTR Translucent],ax + mov [WORD PTR Translucent + 2],es + + add di,4 + +??no_ghost: + + pop di + + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [flags],SHAPE_CENTER ; does this need to be centered? + je short ??no_centering ; if not the skip over this stuff + + mov ax,[pixel_w] + mov bx,[pixel_h] + sar ax,1 + sar bx,1 + sub [x_pixel],ax + sub [y_pixel],bx + +??no_centering: + mov ax,[flags] + and ax,SHAPE_PRIORITY+SHAPE_TRANS + cmp ax,SHAPE_PRIORITY+SHAPE_TRANS + jne short ??test_trans + + or [jflags],FLAG_PRIORITY_TRANS + jmp short ??priority + + ;------------------------------------------------------------------- + ; Get the trans information if we need to get it + ;------------------------------------------------------------------- +??test_trans: + test [flags],SHAPE_TRANS ; does this draw use transparencies? + je short ??test_priority ; if not the skip over this junk + + or [jflags],FLAG_TRANS + +??test_priority: + ;------------------------------------------------------------------- + ; Get the priority information if we need to get it + ;------------------------------------------------------------------- + test [flags],SHAPE_PRIORITY ; does this draw use priorities? + je short ??no_priority ; if not the skip over this junk + + or [jflags],FLAG_PRIORITY + +??priority: + mov ax,[BackGroundPage] ; get the background page from ds + mov [background],ax ; and store it on the stack + mov ax,[MaskPage] ; get the mask page from ds + mov [maskpage],ax ; and store it on the stack + mov ax,[WORD PTR buffer + 4]; get the priority level from args + mov [priority],al ; and store it in a local + + ;------------------------------------------------------------------- + ; Get the draw routine that we are going to draw with + ;------------------------------------------------------------------- +??no_priority: +; mov bx,[flags] ; load in the current flags byte +; and bx,FLAG_MASK ; prevent lockup on bad value + mov bx,[jflags] ; load in the jump table flags + shl bx,1 + mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table + mov [putmiddle],ax ; store off the new offset + + ;------------------------------------------------------------------- + ; Get a pointer to the logic page to where we will draw our buffer + ;------------------------------------------------------------------- + push [LogicPage] ; push the current logic page + call FAR PTR Get_Page ; get the physical page address + add sp,2 ; pull the parameter from the stack + mov es,dx ; store the address in the dest + + ;-------------------------------------------------------------------- + ; Point DI to the beginning of the window that we need to look at. + ; that way we can access all of the info through di. + ;-------------------------------------------------------------------- + mov si,OFFSET WindowList ; get the offset of the window list + mov cl,4 ; shift 3 times = multiply by 16 + mov ax,[win] ; get the window number we are using + shl ax,cl ; each window is 8 words long + add si,ax ; add that into the offset of window + + ;-------------------------------------------------------------------- + ; Place all the clipping values on the stack so our function will + ; be truly re-entrant and will not need to shadow these values. + ;-------------------------------------------------------------------- + mov cl,3 ; to convert x to pixel mult by 8 + mov ax,[si + WIN_X] ; get the left clip position + shl ax,cl ; convert to a pixel x position + mov [win_x1],ax ; store the left edge of window + mov [win_x2],ax + + mov ax,[si + WIN_WIDTH] ; get the width of the window + shl ax,cl ; convert to a pixel width + add [win_x2],ax ; add to get the right window edge + + mov ax,[si + WIN_Y] ; get the win y coordinate to clip + mov [win_y1],ax ; and save it onto the stack + + add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord + mov [win_y2],ax ; and save it onto the stack + + test [flags],SHAPE_WIN_REL ; is this window relative? + je short ??get_buffer ; if not the skip over + + mov ax,[win_x1] ; get left edge of window + add [x_pixel],ax ; add to x pixel position + mov ax,[win_y1] ; get top edge of window + add [y_pixel],ax ; add to y pixel position + + ;-------------------------------------------------------------------- + ; Get a pointer to the source buffer so we can handle the clipping + ;-------------------------------------------------------------------- +??get_buffer: + lds si,[buffer] ; get a pointer to the buffer + + ;-------------------------------------------------------------------- + ; Check the top of our shape and clip any lines that are necessary + ;-------------------------------------------------------------------- + mov ax,[y_pixel] ; get the y_pixel draw position + sub ax,[win_y1] ; subtract out the window y top + jns short ??check_bottom ; skip if y below window top + add ax,[pixel_h] ; add in the height of the region + jg short ??clip_top ; if positive then clip top lines + +??jump_exit: + jmp ??exit ; otherwise completely clipped + +??clip_top: + xchg [pixel_h],ax + sub ax,[pixel_h] + add [y_pixel],ax + mul [pixel_w] ; convert to number of bytes to skip + add si,ax ; skip past the necessary bytes + + ;-------------------------------------------------------------------- + ; Check the bottom of our shape and clip it if necessary + ;-------------------------------------------------------------------- +??check_bottom: + mov ax,[win_y2] ; get the bottom y of the window + sub ax,[y_pixel] ; subtract of the y to draw at + js ??jump_exit ; if its signed then nothing to draw + jz ??jump_exit ; if its zero then nothing to draw + + cmp ax,[pixel_h] ; if more room to draw then height + jae short ??clip_x_left ; then go check the left clip + mov [pixel_h],ax ; clip all but amount that will fit + +??clip_x_left: + mov [clipleft],0 ; clear clip on left of region + mov ax,[x_pixel] ; get the pixel x of draw region + sub ax,[win_x1] ; pull out the window coordinate + jns short ??clip_x_right + neg ax ; negate to get amnt to skip in buf + mov [clipleft],ax ; store it in the left clip info + add [x_pixel],ax ; move to the edge of the window + sub [pixel_w],ax ; pull it out of the pixel width + +??clip_x_right: + mov [clipright],0 ; clear clip on right of region + mov ax,[win_x2] ; get the window x of clip region + sub ax,[x_pixel] ; subtract the draw edge of region + js ??jump_exit ; if its negative then get out + jz ??jump_exit ; if its zero then get out + + cmp ax,[pixel_w] ; is space available larger than w + jae short ??draw_prep ; if so then go get drawing + + + xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax) + sub ax,[pixel_w] ; pull out the amount to draw + mov [clipright],ax ; this is the amount to clip on right + +??draw_prep: + push si ; save off source pos in buffer + push ds ; both offset and segment + mov ax,@data + mov ds,ax + mov bx,[y_pixel] + shl bx,1 ; shift left by 1 for word table look + lds si,[YTable] ; get the address of the ytable + mov di,[ds:si+bx] ; look up the multiplied value + pop ds ; restore source pos in buffer + pop si ; both offset and segment + + add di,[x_pixel] ; add in the x pixel position + mov [nextline],di ; save it off in the next line + + ;-------------------------------------------------------------------- + ; Now determine the type of the shape and process it in the proper + ; way. + ;-------------------------------------------------------------------- + mov dx,[pixel_h] + + ; Check to see if the WSA is the screen width and there is no + ; clipping. In this case, then a special single call to the + ; line processing routine is possible. + mov ax,[clipleft] + add ax,[clipright] + jne short ??top_of_loop + cmp [pixel_w],BYTESPERROW + jne short ??top_of_loop + + ;------------------------------------ + ; The width of the WSA is the screen width, so just process as + ; one large WSA line. + mov ax,BYTESPERROW + imul dx + mov cx,ax + call [putmiddle] + jmp short ??exit + + ;------------------------------------ + ; Process line by line. +??top_of_loop: + add si,[clipleft] ; skip whats necessary on left edge + mov cx,[pixel_w] ; get the width we need to draw + + ; Copy the source to the destination as appropriate. This routine can + ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past + ; the end of the data. + call [putmiddle] + + add si,[clipright] ; skip past the left clip + add [nextline],BYTESPERROW + mov di,[nextline] + + dec dx + jnz ??top_of_loop + +??exit: + pop fs + ret + ENDP + + +;*************************************************************************** +;* NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;*=========================================================================* + + PROC NOLANGUAGE WSA_Normal_Draw NEAR + + IF 1 + ; This version is marginally faster than the later version. + mov ax,cx + shr cx,2 + rep movsd + and ax,011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + ret + + ELSE + + shr cx,1 ; convert to words (odd pix in carry) + rep movsw ; write out the needed words + adc cx,0 ; add the carry into cx + rep movsb ; write out the odd byte if any + ret + ENDIF + + ENDP + + +;*************************************************************************** +;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 10/02/1994 JLB : Optimized for 250% speed improvement. * +;*=========================================================================* + PROC NOLANGUAGE Transparent_Draw NEAR + + IF 1 + ; Preserve DX since it is used as a scratch register. + push dx + +??loop: + ; Swap DS:SI and ES:DI back in preparation for the REP SCASB + ; instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Remember the bytes remaining in order to calculate the position + ; of the scan when it stops. + mov bx,cx + + ; Scan looking for a non-zero value in the source buffer. + xor al,al + repe scasb + + ; When the loop ends, if the EQ flag is set then the scanning is + ; complete. Jump to the end of the routine in order to fixup the + ; pointers. + je short ??fini + + ; Advance the destination pointer by the amount necessary to match + ; the source movement. DS:SI points to where data should be written. + add si,bx + inc cx ; SCASB leaves CX one too low, fix it. + dec di ; SCASB leaves DI one byte too far, fix it. + sub si,cx + + ; Scan for the duration of non-zero pixels. This yields a count which + ; is used to copy the source data to the destination. Preserve DI. + mov dx,di + mov bx,cx + repne scasb + mov di,dx + + ; Set BX to equal the number of bytes to copy from source to dest. + inc cx ; SCASB leaves CX one too low, fix it. + sub bx,cx + + ; Move the data from ES:DI to DS:SI for BX bytes. + xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining. + + ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Move the data from source to dest. First try to move double + ; words. Then copy the remainder bytes (if any). Putting jumps in + ; this section doesn't result in any savings -- oh well. + mov ax,cx + shr cx,2 + rep movsd + and ax,0011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + + ; Restore CX with the remaining bytes to process. + mov cx,bx + + ; If there are more bytes to process, then loop back. + or cx,cx + jne short ??loop + +??fini: + ; Swap ES:DI and DS:SI back to original orientation. + mov ax,ds + mov bx,es + mov es,ax + mov ds,bx + xchg di,si + + ; Restore DX and return. + pop dx + ret + + ELSE + +??loop_top: + lodsb + or al,al + jz short ??skip + + mov [es:di],al ; store the pixel to the screen +??skip: + inc di + loop ??loop_top + ret + + ENDIF + + ENDP + + +;*************************************************************************** +;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of * +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are * +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + + mov bl,[gs:di] ; get the mask byte for our pixel + + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + + PROC NOLANGUAGE Ghost_Normal_Draw NEAR + +??loop_top: + lodsb + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Transparent_Draw NEAR + +??loop_top: + lodsb + or al,al + jz short ??skip + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron* +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they* +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + mov bl,[gs:di] ; get the mask byte for our pixel + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + + ret + + ENDP + + END diff --git a/CODE/KEYFRAME.CPP b/CODE/KEYFRAME.CPP new file mode 100644 index 0000000..c0e5ce2 --- /dev/null +++ b/CODE/KEYFRAME.CPP @@ -0,0 +1,390 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/KEYFRAME.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * Programmer : David Dettmer * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + + + +#ifdef WIN32 +#define INITIAL_BIG_SHAPE_BUFFER_SIZE 20000000 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char * BigShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = FALSE; +} +char * BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; + + + +char ** KeyFrameSlots [1000]; +int TotalSlotsUsed=0; + + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char * shape_data; +} ShapeHeaderType; + +static int Length; + +void * Get_Shape_Header_Data(void * ptr) +{ + if (UseBigShapeBuffer) { + return((void *)(((ShapeHeaderType *)ptr)->shape_data+(long)BigShapeBufferStart)); + } else { + return (ptr); + } +} + +int Get_Last_Frame_Length(void) +{ + return(Length); +} + + + +void Reallocate_Big_Shape_Buffer(void) +{ + if (ReallocShapeBufferFlag) { + BigShapeBufferLength += 2000000; //Extra 2 Mb of uncompressed shape space + BigShapeBufferPtr -= (unsigned)BigShapeBufferStart; + BigShapeBufferStart = (char*)Resize_Alloc(BigShapeBufferStart, BigShapeBufferLength); + BigShapeBufferPtr += (unsigned)BigShapeBufferStart; + ReallocShapeBufferFlag = FALSE; + } +} + + + + +void Check_Use_Compressed_Shapes (void) +{ + MEMORYSTATUS mem_info; + + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + + UseBigShapeBuffer = (mem_info.dwTotalPhys > 12*1024*1024) ? TRUE : FALSE; +} + +#endif + + +unsigned long Build_Frame(void const * dataptr, unsigned short framenumber, void * buffptr) +{ +#ifdef FIXIT_SCORE_CRASH + char * ptr; + unsigned long offcurr, offdiff; +#else + char * ptr, * lockptr; + unsigned long offcurr, off16, offdiff; +#endif + unsigned long offset[SUBFRAMEOFFS]; + KeyFrameHeaderType * keyfr; + unsigned short buffsize, currframe, subframe; + unsigned long length = 0; + char frameflags; + + // + // valid pointer?? + // + if ( !dataptr || !buffptr ) { + return(0); + } + + // + // look at header then check that frame to build is not greater + // than total frames + // + keyfr = (KeyFrameHeaderType *) dataptr; + + if ( framenumber >= keyfr->frames ) { + return(0); + } + + // calc buff size + buffsize = keyfr->width * keyfr->height; + + // get offset into data + ptr = (char *)dataptr + (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)); + Mem_Copy( ptr, &offset[0], 12L ); + frameflags = (char)(offset[0] >> 24); + + if ( (frameflags & KF_KEYFRAME) ) { + + ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { // key delta or delta + + if ( (frameflags & KF_DELTA) ) { + currframe = (unsigned short)offset[1]; + + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + } + + // key frame + offcurr = offset[1] & 0x00FFFFFFL; + + // key delta + offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; + + ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + +#ifndef FIXIT_SCORE_CRASH + off16 = (unsigned long)lockptr & 0x00003FFFL; +#endif + + length = LCW_Uncompress( ptr, buffptr, buffsize ); + + if (length > buffsize) { + return(0); + } + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + +#ifdef NEVER + // check for LCW'd rsd + + if ( ((offset[1] >> 24) & KF_LCW) ) { + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { + length = buffsize; + Apply_XOR_Delta( buffptr, Add_Long_To_Pointer( ptr, offdiff ) ); + } +#else + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); +#endif + + if ( (frameflags & KF_DELTA) ) { + // adjust to delta after the keydelta + + currframe++; + subframe = 2; + + while (currframe <= framenumber) { + offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; + +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + +#ifdef NEVER + // check for LCW'd rsd + + if ( ((offset[1] >> 24) & KF_LCW) ) { + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { + length = buffsize; + Apply_XOR_Delta( buffptr, Add_Long_To_Pointer( ptr, offdiff ) ); + } +#else + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); +#endif + + currframe++; + subframe += 2; + + if ( subframe >= (SUBFRAMEOFFS - 1) && + currframe <= framenumber ) { + Mem_Copy( Add_Long_To_Pointer( dataptr, + (((unsigned long)currframe << 3) + + sizeof(KeyFrameHeaderType)) ), + &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + subframe = 0; + } + } + } + } + return(length); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const * dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const * dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const * dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const * dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const * dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} diff --git a/CODE/LANGUAGE.H b/CODE/LANGUAGE.H new file mode 100644 index 0000000..3f31796 --- /dev/null +++ b/CODE/LANGUAGE.H @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + + +#ifdef ENGLISH +#define TEXT_ERROR_TIMER "Error - Timer system failed to initialise due to system instability. You need to restart Windows." +#define TEXT_SHORT_TITLE "Red Alert" +#define TEXT_MEMORY_ERROR "Error - out of memory." +#define TEXT_ABORT "Abort" +#define TEXT_DDRAW_ERROR "Error - Unable to allocate primary video buffer - aborting." +#define TEXT_TITLE "Command & Conquer : Red Alert" +#define TEXT_VIDEO_ERROR "Error - Unable to set the video mode." +#define TEXT_INSUFFICIENT "Insufficient Disk Space to run Red Alert.\n" +#define TEXT_MUST_HAVE "You must have %d megabytes of free disk space." +#define TEXT_CRITICALLY_LOW "Warning - you are critically low on free disk space for saving games. Do you want to play Red Alert anyway?" +#define TEXT_NO_RAM "Insufficient RAM available.\n" +#define TEXT_USE_START_MENU "\n\rIf you have Windows 95 running you should start Red Alert\r\nDOS version via the Windows 95 Start Menu.\n\rPress any key.\n\r" +#define TEXT_SETUP_FIRST "Run SETUP program first.\n" +#define TEXT_NO_MOUSE "Red Alert is unable to detect your mouse driver." +#define TEXT_FILE_ERROR "FILE ERROR" +#define TEXT_PRESS_KEY "Press any key to retry." +#define TEXT_ESC_KEY "Press to exit program." +#define TEXT_TO_EXIT "Press any key to exit program." +#define TEXT_INVALID "Invalid option switch.\n" +#define TEXT_MAP_ERROR "Map Error!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continue" +#define TEXT_OPTIONS "Red Alert (c) 1996, Westwood Studios\r\n" \ + "Parameters:\r\n" \ + " -DESTNET = Specify Network Number of destination system\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Network Socket ID (0 - 16383)\n" \ + " -STEALTH = Hide multiplayer names (\"Boss mode\")\r\n" \ + " -MESSAGES = Allow messages from outside this game.\r\n" \ + "\r\n" +#endif + + +#ifdef GERMAN +#define TEXT_ERROR_TIMER "Fehler - das Timer-System konnte aufgrund einer Instabilit„t des Systems nicht initialisiert werden. Bitte starten Sie Windows neu." +#define TEXT_SHORT_TITLE "C&C:AR" +#define TEXT_MEMORY_ERROR "Fehler - Kein Speicher mehr." +#define TEXT_ABORT "Abbrechen" +#define TEXT_DDRAW_ERROR "Fehler - Kann prim„ren Videopuffer nicht finden - Abbruch." +#define TEXT_TITLE "Command & Conquer : Alarmstufe Rot" +#define TEXT_VIDEO_ERROR "Fehler - Kann Grafikmodus nicht einstellen." +#define TEXT_INSUFFICIENT "Nicht genug Festplattenplatz fr Command & Conquer:AR.\n" +#define TEXT_MUST_HAVE "Sie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_CRITICALLY_LOW "Nicht genug Festplattenplatz fr Command & Conquer:AR.\nSie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_NO_RAM "Zuwenig Hauptspeicher verfgbar.\n" +#define TEXT_USE_START_MENU "\n\rWenn auf Ihrem Rechner Windows 95 läuft,\n\rstarten Sie die DOS-Version von Alarmstufe Rot\n\rbitte über das Start-Menü von Windows 95.\n\rZum Weitermachen beliebige Taste drücken.\n\r" +#define TEXT_SETUP_FIRST "Bitte erst das SETUP-Programm starten.\n" +#define TEXT_NO_MOUSE "C&C:AR kann Ihren Maustreiber nicht finden..." +#define TEXT_FILE_ERROR "DATEIFEHLER" +#define TEXT_PRESS_KEY "Beliebige Taste drcken fr erneuten Versuch." +#define TEXT_ESC_KEY " drcken, um das Programm zu verlassen." +#define TEXT_TO_EXIT "Beliebige Taste drcken, um das Programm zu verlassen." +#define TEXT_INVALID "Ungltiger Parameter.\n" +#define TEXT_MAP_ERROR "Kartenfehler!" +#define TEXT_STOP "Halt" +#define TEXT_CONTINUE "Weiter" +#define TEXT_OPTIONS "C&C: Alarmstufe Rot (c) 1996, Westwood Studios\r\n" \ + "Parameter:\r\n" \ + " -DESTNET = Netzwerkkennung des Zielrechners festlegen\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Kennung des Netzwerk-Sockets (0 - 16383)\n" \ + " -STEALTH = Namen im Mehrspieler-Modus verstecken (\"Boss-Modus\")\r\n" \ + " -MESSAGES = Mitteilungen von ausserhalb des Spiels zulassen\r\n" \ + "\r\n" +#endif + + +#ifdef FRENCH +#define TEXT_ERROR_TIMER "Error - L'horloge système n'a pas pu s'initialiser en raison de l'instabilit‚ du sytème. Vous devez red‚marrer Windows." +#define TEXT_SHORT_TITLE "Alerte Rouge" +#define TEXT_MEMORY_ERROR "Erreur - Plus de m‚moire." +#define TEXT_ABORT "Interrompre" +#define TEXT_DDRAW_ERROR " Erreur - Impossible d'allouer le tampon vid‚o principal - Interruption." +#define TEXT_TITLE "Command & Conquer : Alerte Rouge" +#define TEXT_VIDEO_ERROR " Erreur - Impossible d'‚tablir le mode vid‚o." +#define TEXT_INSUFFICIENT "Espace disque insuffisant pour lancer Command & Conquer.\n" +#define TEXT_MUST_HAVE "Vous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_CRITICALLY_LOW "Espace disque insuffisant pour lancer Command & Conquer.\nVous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_NO_RAM "M‚moire vive (RAM) insuffisante.\n" +#define TEXT_USE_START_MENU "\n\rSi vous ˆtes dans Windows 95 vous devez lancer Alerte Rouge\r\nDOS … partir du menu D‚marrer de Windows 95.\n\rAppuyez sur n'importe quelle touche.\n\r" +#define TEXT_SETUP_FIRST "Lancez d'abord le programme de configuration SETUP.\n" +#define TEXT_NO_MOUSE "Alerte Rouge ne peut pas d‚tecter votre gestionnaire de souris." +#define TEXT_FILE_ERROR "ERREUR DE FICHIER" +#define TEXT_PRESS_KEY "Appuyez sur une touche pour recommencer." +#define TEXT_ESC_KEY "Appuyez sur Echap pour quitter le programme." +#define TEXT_TO_EXIT "Appuyez sur une touche pour quitter le programme." +#define TEXT_INVALID "Commande d'option invalide.\n" +#define TEXT_MAP_ERROR "Erreur de carte!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continuer" +#define TEXT_OPTIONS "Alerte Rouge (c) 1996, Westwood Studios\r\n" \ + "Paramètres:\r\n" \ + " -DESTNET = Sp‚cifier le num‚ro de r‚seau du systŠme de destination\r\n" \ + " (Syntaxe: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = ID Socket r‚seau (0  16383)\r\n" \ + " -STEALTH = Cacher les noms en mode multijoueurs (\"Mode Boss\")\r\n" \ + " -MESSAGES = Autorise les messages ext‚rieurs … ce jeu.\r\n" \ + "\r\n" +#endif + diff --git a/CODE/LAYER.CPP b/CODE/LAYER.CPP new file mode 100644 index 0000000..6fa1340 --- /dev/null +++ b/CODE/LAYER.CPP @@ -0,0 +1,163 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LAYER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LAYER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : March 10, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LayerClass::Sort -- Perform an incremental sort pass on the layer's objects. * + * LayerClass::Sorted_Add -- Adds object in sorted order to layer. * + * LayerClass::Submit -- Adds an object to a layer list. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "layer.h" + + +/*********************************************************************************************** + * LayerClass::Submit -- Adds an object to a layer list. * + * * + * This routine is used to add an object to the layer list. If the list is full, then the * + * object is not added. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Allows sorted insert. * + * 01/02/1995 JLB : Fixed to work with EMSListOf template. * + *=============================================================================================*/ +bool LayerClass::Submit(ObjectClass const * object, bool sort) +{ + /* + ** Add the object to the layer. Either at the end (if "sort" is false) or at the + ** appropriately sorted position. + */ + if (sort) { + return(Sorted_Add(object)); + } + return(Add((ObjectClass *)object)); +} + + +/*********************************************************************************************** + * LayerClass::Sort -- Handles sorting the objects in the layer. * + * * + * This routine is used if the layer objects must be sorted and sorting is to occur now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Don't call this routine too often since it does take a bit of time to * + * execute. It is a single pass binary sort and thus isn't horribly slow, * + * but it does take some time. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 03/10/1995 JLB : Uses comparison operator. * + *=============================================================================================*/ +void LayerClass::Sort(void) +{ + for (int index = 0; index < Count()-1; index++) { + if (*(*this)[index+1] < *(*this)[index]) { + ObjectClass * temp; + + temp = (*this)[index+1]; + (*this)[index+1] = (*this)[index]; + (*this)[index] = temp; + } + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Sorted_Add -- Adds object in sorted order to vector. * + * * + * Use this routine to add an object to the vector but it will be inserted in sorted * + * order. This depends on the ">" operator being defined for the vector object. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added to the vector successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +int LayerClass::Sorted_Add(ObjectClass const * const object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the right sorted position. + */ + for (int index = 0; index < ActiveCount; index++) { + if ((*(*this)[index]) > (*object)) { + break; + } + } + + /* + ** Make room if the insertion spot is not at the end of the vector. + */ + for (int i = ActiveCount-1; i >= index; i--) { + (*this)[i+1] = (*this)[i]; + } + (*this)[index] = (ObjectClass *)object; + ActiveCount++; + return(true); +} + + diff --git a/CODE/LAYER.H b/CODE/LAYER.H new file mode 100644 index 0000000..e9b252b --- /dev/null +++ b/CODE/LAYER.H @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LAYER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LAYER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : May 31, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LAYER_H +#define LAYER_H + +#include "vector.h" + +class ObjectClass; + +class LayerClass : public DynamicVectorClass +{ + public: + + //----------------------------------------------------------------- + void Sort(void); + bool Submit(ObjectClass const * object, bool sort=false); + int Sorted_Add(ObjectClass const * const object); + + + virtual void Init(void) {Clear();}; + virtual void One_Time(void) {}; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/CODE/LCW.CPP b/CODE/LCW.CPP new file mode 100644 index 0000000..69b202a --- /dev/null +++ b/CODE/LCW.CPP @@ -0,0 +1,162 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/*************************************************************************** + * 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, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* 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++; + } + } + } + } + } +} diff --git a/CODE/LCW.H b/CODE/LCW.H new file mode 100644 index 0000000..848a08e --- /dev/null +++ b/CODE/LCW.H @@ -0,0 +1,49 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LCW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCW_H +#define LCW_H + + +int LCW_Uncomp(void const * source, void * dest, unsigned long length=0); + +extern "C" { +int __cdecl LCW_Comp(void const * source, void * dest, int length); +} + +#endif diff --git a/CODE/LCWCOMP.ASM b/CODE/LCWCOMP.ASM new file mode 100644 index 0000000..c262c70 --- /dev/null +++ b/CODE/LCWCOMP.ASM @@ -0,0 +1,284 @@ +; +; 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 . +; + +; $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 + \ No newline at end of file diff --git a/CODE/LCWPIPE.CPP b/CODE/LCWPIPE.CPP new file mode 100644 index 0000000..9154c8f --- /dev/null +++ b/CODE/LCWPIPE.CPP @@ -0,0 +1,313 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LCWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lcwpipe.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * * + * This will initialize the LCWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::LCWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::~LCWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LCW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < (sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LCW_Uncomp(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LCW_Comp(Buffer, Buffer2, BlockSize); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LCW_Comp(source, Buffer2, BlockSize); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LCW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LCW_Comp(Buffer, Buffer2, Counter); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/CODE/LCWPIPE.H b/CODE/LCWPIPE.H new file mode 100644 index 0000000..f05958e --- /dev/null +++ b/CODE/LCWPIPE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LCWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWPIPE_H +#define LCWPIPE_H + +#include "pipe.h" + + +/* +** Performs LCW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LCWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWPipe(CompControl, int blocksize=1024*8); + virtual ~LCWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWPipe(LCWPipe & rvalue); + LCWPipe & operator = (LCWPipe const & pipe); +}; + + +#endif diff --git a/CODE/LCWSTRAW.CPP b/CODE/LCWSTRAW.CPP new file mode 100644 index 0000000..506c3c7 --- /dev/null +++ b/CODE/LCWSTRAW.CPP @@ -0,0 +1,179 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LCWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWStraw::Get -- Fetch data through the LCW processor. * + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lcwstraw.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * * + * This will initialize the LCW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::LCWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::~LCWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWStraw::Get -- Fetch data through the LCW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LCW_Uncomp(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LCW_Comp(Buffer, &Buffer2[sizeof(BlockHeader)], BlockHeader.UncompCount); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/CODE/LCWSTRAW.H b/CODE/LCWSTRAW.H new file mode 100644 index 0000000..e9607df --- /dev/null +++ b/CODE/LCWSTRAW.H @@ -0,0 +1,106 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LCWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWSTRAW_H +#define LCWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LCW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LCWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWStraw(CompControl control, int blocksize=1024*8); + virtual ~LCWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWStraw(LCWStraw & rvalue); + LCWStraw & operator = (LCWStraw const & pipe); +}; + + +#endif diff --git a/CODE/LCWUNCMP.CPP b/CODE/LCWUNCMP.CPP new file mode 100644 index 0000000..dcd4295 --- /dev/null +++ b/CODE/LCWUNCMP.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LCWUNCMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern "C" { + +/*************************************************************************** + * LCW_UNCOMPRESS -- 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. * + *=========================================================================*/ +unsigned long __cdecl LCW_Uncompress (void * source, void * dest, unsigned long ) +//unsigned long LCW_Uncompress (void * source, void * dest, unsigned long length) +{ + unsigned char * source_ptr, * dest_ptr, * copy_ptr, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* 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++; + } + } + } + } + } +} + +} diff --git a/CODE/LED.H b/CODE/LED.H new file mode 100644 index 0000000..f14136c --- /dev/null +++ b/CODE/LED.H @@ -0,0 +1,46 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\led.h_v 4.42 04 Jul 1996 16:10:40 JOE_BOSTIC $ */ + +#ifndef LED_H +#define LED_H + +class LEDClass +{ + public: + typedef enum ControlType { + LED_NOCHANGE, // Do nothing (just query). + LED_OFF, // Turn LED off. + LED_ON, // Turn LED on. + LED_TOGGLE // Toggle LED state. + } ControlType; + + protected: + static int Shift_Control(ControlType control, char bit); + + public: + static int Scroll_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x01);}; + static int Caps_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x02);}; + static int Num_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x04);}; + + private: + static void Send_To_Keyboard(unsigned char val); +}; + +#endif diff --git a/CODE/LIBRARY.TXT b/CODE/LIBRARY.TXT new file mode 100644 index 0000000..77761a4 --- /dev/null +++ b/CODE/LIBRARY.TXT @@ -0,0 +1,106 @@ +Guys, in the interests of proceeding with a meaningful discussion I have decided to distill the coding standards group down even more, in fact to this email group. I have collated input from some of you into this message for further discussion "offline". We'll meet again (with the group of 7) on Feb 22 to discuss the revised "coding standards" document in its entirety rather than going through it piecemeal. I think this will lead to much more expeditious results. We will refer to our standard as the "Westwood Coding Standard". This can (and will) differ from K&R and we should not allow ourselves to get hung up on this distinction. We are building a set of rules which is contemporary and practical, it is our coding standard and we can do what we like with it. We will endeavor to limit ourselves to the bounds of reason, however. This group has enough experience to ensure that. + +GOAL: We should define a rigid set of parameters for source code layout for the following reasons: + +A. clarity -- esp. for someone unfamiliar with the code. +B. consistency -- structure and scope should be clear by merely +examining structure/indenting without forcing the reader to analyze the code. i.e. no structure/indent exceptions. +C. maintainability -- strive for minimal coding traps or hidden +gotchas. Since maintenance coding could be done by someone who is unfamiliar with the code, it is very important that +structure/indenting rules don't force additional burdens. +D. ease of use -- structure and brace rules should be easy to +type and edit. Programmers are generally reluctant to perform unnecessary typing and recognizing this, is a legitimate concern. + +RULES: +1. Indenting should consistently indicate code structure. In other words, it should be possible to immediately detect the line that controls execution of a code block by merely scanning back until the first line of indent change. This addresses goals A, B, and C. + +GOOD EXAMPLE: +switch(a) { +case X: +break; +case Y: +break; +} + +BAD EXAMPLE: +switch(a) { +case X: +break; +case Y: +break; +} + + +2. Braces placement should tightly coupled to the controlling statement. In other words, the brace should not be distant from the "if", "else", "do", or "switch". By far the most difficult to maintain is separating the "else" from the preceding "}". The greater the distance between the "}" and the "else", the greater the obscurity of the code's structure and flow. + +GOOD EXAMPLE: +if (a) { +bla; +} else { +bla; +} + +BAD EXAMPLE: +if (a) { +bla; +} +... +... +... +// This code block is intimately tied to the preceding "if", // but the separation obscures this. Worse yet, depending on +// the size of the comments, the "else" may not even be visible // on the screen. This violates goals A and C. +else { +bla; +} + +However, + +if (a) { +statement1; +} else if (b) { +statement 2; +} else if (c) { +statement 3; +} + +is preferable to: + +if (a) { +statement1; +} else { +if (b) { +statement 2; +} else { +if (c) { +statement 3; +} +} +} + +Even though this violates A: It is not clear because if the if statements are complicated they will consistently go off the right edge of the screen, obsucuring your view of them... + +This also seems to be a slight variation of B: The scope of the if statements could all be the same. There is no inherent need to change scope, so changing scope promotes extra unneeded lines (you can't see as much code this way). + +3. Code lines with the same level of indention should have minimal interdependence. In other words, it should be possible to insert lines in a block of similarly indented code with minimal to no danger of breakage. This addresses goals A, B, C, and D. The second example in rule #2 demonstrates the problem. By looking at the "if" it would appear that code could be safely added past the "}", however, this would cause an error. The programmer cannot trust the indent level in this example and is thus forced to examine the subsequent code in order to look for this trap. + +4. Place the braces on the same line with the control structure that it is attached to. This cuts down on the number of code lines without obscuring the structure. Less code lines means more code is visible on the screen at any one time. This addresses goals A, C, and to some extend D. This also inherently solves rule #2. + +5. Always enclose code blocks in braces after an "if", "else", "do", or "while" statement. Doing so helps with code maintenance (goal C) and makes the control structures consistently coded (goal B). + +GOOD EXAMPLE: +if (a) { +bla; +} + +BAD EXAMPLE: +if (a) +bla; + +...or... + +if (a) +if (b) +bla; +else // Confusing as to which "if" it is attached to. +bla; + diff --git a/CODE/LINK.CPP b/CODE/LINK.CPP new file mode 100644 index 0000000..b130bbd --- /dev/null +++ b/CODE/LINK.CPP @@ -0,0 +1,401 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LINK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LINK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LinkClass::Add -- This object adds itself to the given list * + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * LinkClass::Get_Next -- Fetches the next object in list. * + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * LinkClass::Head_Of_List -- Finds the head of the list. * + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * LinkClass::Remove -- Removes the specified object from the list. * + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * LinkClass::Zap -- Forces the link pointers to NULL. * + * LinkClass::operator= -- Assignment operator for linked list class object. * + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "link.h" + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * * + * This copy constructor, unlike the assignment operator, doesn't have to deal with an * + * already initialized and legal link object to the left of the "=". It merely puts the * + * destination object into the same list as the source object. * + * * + * INPUT: link -- Reference to the object on the right of the "=". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(LinkClass const & link) : + Next(0), Prev(0) +{ + /* + ** Add this object to the same list that the copy object + ** resides in. + */ + Add((LinkClass &)link); +} + + +/*********************************************************************************************** + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * * + * This default destructor will remove the object from any linked list it may be part of. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::~LinkClass(void) +{ + Remove(); +} + + +/*********************************************************************************************** + * LinkClass::Zap -- Forces the link pointers to NULL. * + * * + * This routine will "zap" out the link pointers. This is usually necessary when the link * + * pointers start in an undefined state, but we KNOW that they aren't pointing to anything * + * valid. In such a case it becomes necessary to zap them so that when the object is added * + * to a list, it will be added correctly. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void LinkClass::Zap(void) +{ + Next = 0; + Prev = 0; +} + + +/*********************************************************************************************** + * LinkClass::operator= -- Assignment operator for linked list class object. * + * * + * The assignment operator makes sure that the previous and next pointers remain valid. * + * Because this class only consists of pointers, the assignment operator doesn't actually * + * transfer any data from the source object. It merely makes the destination object part * + * of the same list as the source object. In essence, this is transferring information * + * but not the actual values. * + * * + * If the destination object is already part of another list, it is removed from that list * + * before being added to the source object's list. This ensures that either list remains * + * in a valid condition. * + * * + * INPUT: link -- The object to the right of the "=" operator. * + * * + * OUTPUT: Returns a reference to the rightmost object -- per standard assignment rules. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::operator = (LinkClass const & link) +{ + if (&link == this) return(*this); + + Remove(); + Add((LinkClass &)link); + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Get_Next -- Fetches the next object in list. * + * * + * This routine will return with a pointer to the next object in the list. If there are * + * no more objects, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to next object in list or NULL if at end of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Next(void) const +{ + return(Next); +} + + +/*********************************************************************************************** + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * * + * Use this routine to get a pointer to the previous object in the linked list. If there * + * are no previous objects (such as at the head of the list), then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous object in the list or NULL if none. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Prev(void) const +{ + return(Prev); +} + + +/*********************************************************************************************** + * LinkClass::Head_Of_List -- Finds the head of the list. * + * * + * Use this routine to scan for and return a reference to the object at the head of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Head_Of_List(void) +{ + LinkClass * link = this; + while (link->Prev) { + link = link->Prev; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * * + * Use this routine to scan for and return a reference to the object at the end of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the end of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Tail_Of_List(void) +{ + LinkClass * link = this; + while (link->Next) { + link = link->Next; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Add -- This object adds itself to the given list * + * * + * Use this routine to add a link object to the list, but to be added right after the * + * given link. This allows inserting a link in the middle of the chain. A quite necessary * + * ability if the chain is order dependant (e.g., the gadget system). * + * * + * INPUT: list -- gadget object to add this one to * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Save ptr to next gadget. + */ + ptr = list.Next; + + /* + ** Link myself in after 'list'. + */ + list.Next = this; + Prev = &list; + + /* + ** Link myself to next gadget, if there is one. + */ + Next = ptr; + if (ptr) { + ptr->Prev = this; + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * * + * INPUT: list -- the list to make myself the head of * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Head(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Head_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Prev = this; + Next = ptr; + Prev = NULL; + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: the head of the list * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Tail(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Tail_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Next = this; + Prev = ptr; + Next = NULL; + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Remove(void) +{ + LinkClass * head = &Head_Of_List(); + LinkClass * tail = &Tail_Of_List(); + + if (Prev) { + Prev->Next = Next; + } + if (Next) { + Next->Prev = Prev; + } + Prev = 0; + Next = 0; + + if (head==this) { + if (tail==this) { + return(0); + } + return(&tail->Head_Of_List()); + } + return(head); +} + + diff --git a/CODE/LINK.H b/CODE/LINK.H new file mode 100644 index 0000000..1ae4d0f --- /dev/null +++ b/CODE/LINK.H @@ -0,0 +1,75 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LINK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LINK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LINK_H +#define LINK_H + + +/* +** This implements a simple linked list. It is possible to add, remove, and traverse the +** list. Since this is a doubly linked list, it is possible to remove an entry from the +** middle of an existing list. +*/ +class LinkClass +{ + public: + LinkClass(NoInitClass const &) {}; + LinkClass(void) : Next(0), Prev(0) {}; + virtual ~LinkClass(void); + + virtual LinkClass * Get_Next(void) const; + virtual LinkClass * Get_Prev(void) const; + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual LinkClass & Head_Of_List(void); + virtual LinkClass & Tail_Of_List(void); + virtual void Zap(void); + virtual LinkClass * Remove(void); + + LinkClass & operator=(LinkClass const & link); // Assignment operator. + LinkClass(LinkClass const & link); // Copy constructor. + + private: + /* + ** Pointers to previous and next link objects in chain. + */ + LinkClass * Next; + LinkClass * Prev; +}; + +#endif diff --git a/CODE/LINT.H b/CODE/LINT.H new file mode 100644 index 0000000..921f8b2 --- /dev/null +++ b/CODE/LINT.H @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LINT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LINT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/28/96 * + * * + * Last Update : March 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// overloading data member (e.g., IsToRedraw) +//lint -e1516 + +// function distinguishing error (actually not an error when conversion operators are used). +//lint -e1053 + + +/* +** The current version of Lint doesn't know how to deal with conversion operators. It +** erroneously generates this error message as a result. Unfortunately, we have to throw +** out the baby with the bathwater on this one. +*/ +//lint -e58 -e56 + +/* +** Hiding a non virtual member function is generally ok. +*/ +//lint -e1511 + +/* +** The warning about not initializing a member during construction is +** valid. However, C&C takes advantage of this by using the in place +** new operator during the load game process. Disable the warning, but +** grudgingly. +*/ +//lint -e1401 + +/* +** Disable warning about non virtual base class destructor for these objects. +*/ +//lint -esym(1509,GraphicBufferClass,GraphicViewPortClass,BufferClass,VideoViewPortClass,GetCDClass) +//lint -esym(1509,BasicTimerClass,FlyClass,FuseClass,StageClass,FlasherClass,CargoClass,DoorClass) +//lint -esym(1509,AbstractTypeClass) + +/* +** The "unusual cast" warning is the result of derived casting. This is +** because there is no "dynamic_cast<>" implemented in Watcom (version 10.5). +*/ +//lint -e740 -e571 + +/* +** "function not previously declared inline" is probably not worth noting. +*/ +//lint -e1727 + +/* +** Allow sub-integer loss of precision. This will allow assigning ints to +** chars without warning. +*/ +//lint -e734 + +/* +** Shifting an integer left is always ok. +*/ +//lint -e701 -e703 + +/* +** Allow repeated include files. The body of the include file should be +** coded to ensure that it is processed only once. +*/ +//lint -e537 + +/* +** Implicitly converting an enum to an integer is almost always ok. +*/ +//lint -e641 + +/* +** It is possible to have template functions for "++" and "--" that +** don't require one of the parameters to be a class object. +*/ +//lint -e1042 + +/* +** Redundant declarations are ok. They are a bit harder to maintain, +** but they facilitate keeping modules less interdependant (include +** file wise). +*/ +//lint -e963 -e762 -e763 + +/* +** Not having a default constructor is ok. +*/ +//lint -e1712 + +/* +** Private constructors are ok. In fact, they are necessary if the +** class object must never be instantiated outside of the class +** itself. +*/ +//lint -e1704 + +/* +** Ignoring the return value from a function is ok. It is very +** common for certain side-effect type functions. +*/ +//lint -e534 + +/* +** Implicitly converting from a signed to an unsigned parameter (or +** visa versa) is ok. +*/ +//lint -e732 -e502 -e713 -e737 -eau + +/* +** Allow functions to overload and hide base functions. This is a +** technique of inheritance that handles function parameter changes. +*/ +//lint -e1411 + +/* +** If a switch statement doesn't have a case for every value (enums) +** but it does have a "default" case, then don't warn about it. +*/ +//lint -e788 + +/* +** If bitwize arithmetic is performed on compatible enumeration types, +** then don't complain. Many enums are used in this fashion. +*/ +//lint -e655 -e656 + +/* +** If a data member is not explicitly initialized in the initializer +** list, this is ok. +*/ +//lint -e1542 + +/* +** Calling "new" when not part of an assignment operation can be valid. This +** is true if the "new" operator has been overloaded and the class keeps +** track of itself. +*/ +//lint -e522 + +/* +** A class that is zero bytes long is ok. This is how method classes +** usually work. +*/ +//lint -e1501 + +/* +** Boolean passed to function is ok. +*/ +//lint -e730 + +/* +** Signed/unsigned mix with relational... ignore for now. +*/ +//lint -e574 + diff --git a/CODE/LIST.CPP b/CODE/LIST.CPP new file mode 100644 index 0000000..bd3c96d --- /dev/null +++ b/CODE/LIST.CPP @@ -0,0 +1,971 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LIST.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 23, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ListClass::Add -- This object adds itself to the given list * + * ListClass::Add_Head -- This gadget makes itself the head of the given list. * + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * ListClass::Add_Item -- Adds an item to the list box. * + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * ListClass::Add_Tail -- Add myself to the end of the given list. * + * ListClass::Bump -- Bumps the list box up/down one "page". * + * ListClass::Current_Index -- Fetches the current selected index. * + * ListClass::Current_Item -- Fetches pointer to current item string. * + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * ListClass::Draw_Me -- Draws the listbox. * + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * ListClass::Remove -- Removes the specified object from the list. * + * ListClass::Remove_Item -- Remove specified text from list box. * + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * ListClass::Step -- Moves the list view one line in direction specified. * + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * ListClass::~ListClass -- Destructor for list class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ListClass::ListClass -- class constructor * + * * + * INPUT: id button ID * + * * + * x,y upper-left corner, in pixels * + * * + * w,h width, height, in pixels * + * * + * list ptr to array of char strings to list* + * * + * flags, style flags for mouse, style of listbox * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ListClass::ListClass (int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + TextFlags = flags; + IsScrollActive = false; + Tabs = 0; + SelectedIndex = 0; + CurrentTopIndex = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + + +ListClass::ListClass(ListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + + +void ListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + + +/*********************************************************************************************** + * ListClass::~ListClass -- Destructor for list class objects. * + * * + * This is the destructor for list objects. It handles removing anything it might have * + * allocated. This is typically the scroll bar. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ListClass::~ListClass(void) +{ + Remove_Scroll_Bar(); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(char const * text) +{ + if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * * + * This will add the text as specified by the text number provided, to the list box. * + * The string is added to the end of the list. * + * * + * INPUT: text -- The text number for the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: Once a string is added to the list box in this fashion, there is no method of * + * retrieving the text number as it relates to any particular index in the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(int text) +{ + if (text != TXT_NONE) { + Add_Item(Text_String(text)); + } + return(List.Count() - 1); +} + + +void ListClass::Remove_Item(int index) +{ + if ((unsigned)index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + + +/*********************************************************************************************** + * ListClass::Remove_Item -- Remove specified text from list box. * + * * + * This routine will remove the specified text string from the list box. * + * * + * INPUT: text -- Pointer to the string to remove. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text pointer passed into this routine MUST be the same text pointer that * + * was used to add the string to the list. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Remove_Item(char const * text) +{ + if (text) { + Remove_Item(List.ID(text)); + } +} + + +/*************************************************************************** + * ListClass::Action -- If clicked on, do this! * + * * + * INPUT: int flags -- combination of mouse flags indicating * + * what action to take. * + * * + * OUTPUT: bool result. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +int ListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + if (SelectedIndex == -1) SelectedIndex = 0; + } + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ListClass::Draw_Me -- Draws the listbox. * + * * + * This routine will render the listbox. * + * * + * INPUT: forced -- Should the listbox be redrawn even if it already thinks it doesn't * + * need to be? This is true when something outside of the gadget system * + * has trashed the screen. * + * * + * OUTPUT: Was the listbox redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Bump -- Bumps the list box up/down one "page". * + * * + * Use this routine to adjust the "page" that is being viewed in the list box. The view * + * will move up or down (as specified) one page (screen full) of text strings. * + * * + * INPUT: up -- Should the adjustment be up? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step -- Moves the list view one line in direction specified. * + * * + * This routine will move the current view "page" one line in the direction specified. * + * * + * INPUT: up -- Should the view be moved upward? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * * + * This routine will fetch an item string from the list box. The item fetched can be any * + * one of the ones in the list. * + * * + * INPUT: index -- The index to examine and return the text pointer from. * + * * + * OUTPUT: Returns with the text pointer to the string at the index position specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Get_Item(int index) const +{ + if (List.Count() == 0) { + return NULL; + } + index = min(index, List.Count()-1); + return(List[index]); +} + + +/*********************************************************************************************** + * ListClass::Current_Item -- Fetches pointer to current item string. * + * * + * This routine will fetch a pointer to the currently selected item's text. * + * * + * INPUT: none * + * * + * OUTPUT: Return with pointer to currently selected text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Current_Item(void) const +{ + if (List.Count() <= SelectedIndex) { + return(0); + } + return(List[SelectedIndex]); +} + + +/*********************************************************************************************** + * ListClass::Current_Index -- Fetches the current selected index. * + * * + * This routine will fetch the index number for the currently selected line. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the index of the currently selected line. This ranges from zero to * + * the number of items in the list minus one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + + +/*********************************************************************************************** + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * * + * key -- The key value at the time of the event. * + * * + * whom -- Which gadget is being touched. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + + +/*********************************************************************************************** + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * * + * This routine is used to set the line that will be at the top of the list view. This is * + * how the view can be scrolled up and down. This does not affect the currently selected * + * item. * + * * + * INPUT: index -- The line (index) to move to the top of the list view. * + * * + * OUTPUT: bool; Was the view actually changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, max(0, List.Count() - LineCount)); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * * + * This routine will add a scroll bar (with matching arrows) to the list box. They are * + * added to the right edge and cause the interior of the list box to become narrower. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar added? * + * * + * WARNINGS: The list box becomes narrower when the scroll bar is added. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * * + * Use this routine to remove any attached scroll bar to this list box. If the scroll bar * + * is not present, then no action occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * * + * This sets the tab stop list to be used for text printing. It specifies a series of * + * pixel offsets for each tab stop. The offsets are from the starting pixel position that * + * the text begins at. * + * * + * INPUT: tabs -- Pointer to a list of tab pixel offsets. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only a pointer to the tabs is recorded by the ListClass object. Make sure that * + * the list remains intact for the duration of the existence of this object. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + + +/*********************************************************************************************** + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, scheme, TBLACK, flags, width, Tabs); + +} + + +/*********************************************************************************************** + * ListClass::Add -- Adds myself to list immediately after given object * + * * + * Adds the list box to the chain, immediately after the given object. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * * + * INPUT: object -- Pointer to the object to be added right after this one. * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Head -- Adds myself to head of the given list * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Tail -- Adds myself to tail of given list * + * * + * Adds the list box to the tail of the give chain. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: none * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * ListClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * ListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + + +/*********************************************************************************************** + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * * + * This routine will set the top line of the listbox to the index value specified. * + * * + * INPUT: index -- The index to set the top of the listbox to. * + * * + * OUTPUT: none * + * * + * WARNINGS: The requested index may be adjusted to fit within legal parameters. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 01/23/1996 JLB : Forces selected index to always be zero for a null list. * + *=============================================================================================*/ +void ListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } else { + SelectedIndex = 0; + } +} + + +/*********************************************************************************************** + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * * + * This routine will scroll the top line of the listbox in the direction specified. * + * * + * INPUT: step -- The direction (and amount) to adjust the listbox. If negative value, then * + * the top line is scrolled upward. * + * * + * OUTPUT: Returns with the original top line index number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + + +void ListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + + +void ListClass::Set_Selected_Index(char const * text) +{ + if (text && List.Count() > 0) { + for (int index = 0; index < List.Count(); index++) { + if (stricmp(List[index], text) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} diff --git a/CODE/LIST.H b/CODE/LIST.H new file mode 100644 index 0000000..f1d803a --- /dev/null +++ b/CODE/LIST.H @@ -0,0 +1,802 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LIST.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LIST_H +#define LIST_H + +#include "control.h" +#include "shapebtn.h" +#include "slider.h" + + +/*************************************************************************** + * ListClass -- Like a Windows ListBox structure * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class ListClass : public ControlClass +{ + public: + ListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + ListClass(ListClass const & list); + virtual ~ListClass(void); + + virtual int Add_Item(char const * text); + virtual int Add_Item(int text); + virtual int Add_Scroll_Bar(void); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual char const * Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual char const * Get_Item(int index) const; + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const *Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. The pointers + ** are stored in EMS. The text that is pointed to may also be in EMS. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + + +template +class TListClass : public ControlClass +{ + public: + TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + TListClass(TListClass const & list); + virtual ~TListClass(void); + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual int Add_Item(T text); + virtual int Add_Scroll_Bar(void); + virtual void Insert_Item(T item); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual T Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + virtual T Get_Item(int index) const {return(List[index]);}; + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(T); + virtual void Remove_Index(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const * Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + +template +TListClass::TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true), + TextFlags(flags), + Tabs(0), + IsScrollActive(false), + SelectedIndex(0), + CurrentTopIndex(0) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + +template +TListClass::TListClass(TListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + +template +void TListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + +template +TListClass::~TListClass(void) +{ + Remove_Scroll_Bar(); +} + +template +void TListClass::Insert_Item(T item) +{ + if (Current_Index() >= Count()) { + List.Add(item); + } else { + List.Add(item); + + /* + ** Move all trailing items upward. + */ + for (int index = List.Count()-1; index >= Current_Index(); index--) { + List[index+1] = List[index]; + } + + /* + ** Insert the new item into the location at the current index. + */ + List[Current_Index()] = item; + } +} + + +template +int TListClass::Add_Item(T text) +{ +// if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } +// } + return(List.Count() - 1); +} + +template +void TListClass::Remove_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + +template +void TListClass::Remove_Item(T text) +{ + Remove_Index(List.ID(text)); +} + +template +int TListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + } + } + return(ControlClass::Action(flags, key)); +} + +template +int TListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box(X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + List[line]->Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// List[index].Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + +template +void TListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +template +void TListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +#ifdef NEVER +template +T TListClass::Get_Item(int index) const +{ + index = min(index, List.Count()); + return(List[index]); +} +#endif + +template +T TListClass::Current_Item(void) const +{ + static T _temp; + if (List.Count() <= SelectedIndex) { + return(_temp); + } + return(List[SelectedIndex]); +} + +template +int TListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + +template +void TListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + +template +int TListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, List.Count() - LineCount); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + +template +int TListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + +template +int TListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + +template +void TListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + +#ifdef NEVER +template +void TListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index], x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} +#endif + +template +LinkClass & TListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + +template +LinkClass & TListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + +template +LinkClass & TListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + +template +GadgetClass * TListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + +template +void TListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } +} + +template +int TListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + +template +void TListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + +template +void TListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/CODE/LISTNODE.H b/CODE/LISTNODE.H new file mode 100644 index 0000000..023f2aa --- /dev/null +++ b/CODE/LISTNODE.H @@ -0,0 +1,173 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LISTNODE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LISTNODE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/16/96 * + * * + * Last Update : May 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LISTNODE_H +#define LISTNODE_H + +#include +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#pragma warn -inl + +/* +** This is a doubly linked list node. Typical use of this node is to derive +** objects from this node. The interface class for this node can be used for +** added convenience. +*/ +class GenericList; +class GenericNode { + public: + GenericNode(void) : NextNode(0), PrevNode(0) {} + ~GenericNode(void) {Unlink();} + GenericNode(GenericNode & node) {node.Link(this);} + GenericNode & operator = (GenericNode & node) { + if (&node != this) { + node.Link(this); + } + return(*this); + } + + void Unlink(void) { + if (Is_Valid()) { + PrevNode->NextNode = NextNode; + NextNode->PrevNode = PrevNode; + PrevNode = 0; + NextNode = 0; + } + } + + GenericList * Main_List(void) const { + GenericNode const * node = this; + while (node->PrevNode) { + node = PrevNode; + } + return((GenericList *)this); + } + void Link(GenericNode * node) { + assert(node != NULL); + node->Unlink(); + node->NextNode = NextNode; + node->PrevNode = this; + if (NextNode) NextNode->PrevNode = node; + NextNode = node; + } + + GenericNode * Next(void) const {return(NextNode);} + GenericNode * Prev(void) const {return(PrevNode);} + bool Is_Valid(void) const {return(this != NULL && NextNode != NULL && PrevNode != NULL);} + + protected: + GenericNode * NextNode; + GenericNode * PrevNode; +}; + + +/* +** This is a generic list handler. It manages N generic nodes. Use the interface class +** to the generic list for added convenience. +*/ +class GenericList { + public: + GenericList(void) { + FirstNode.Link(&LastNode); + } + + GenericNode * First(void) const {return(FirstNode.Next());} + GenericNode * Last(void) const {return(LastNode.Prev());} + bool Is_Empty(void) const {return(!FirstNode.Next()->Is_Valid());} + void Add_Head(GenericNode * node) {FirstNode.Link(node);} + void Add_Tail(GenericNode * node) {LastNode.Prev()->Link(node);} + void Delete(void) {while (FirstNode.Next()->Is_Valid()) delete FirstNode.Next();} + + protected: + GenericNode FirstNode; + GenericNode LastNode; + + private: + GenericList(GenericList & list); + GenericList & operator = (GenericList const &); +}; + + + +/* +** This node class serves only as an "interface class" for the normal node +** object. In order to use this interface class you absolutely must be sure +** that the node is the root base object of the "class T". If it is true that the +** address of the node is the same as the address of the "class T", then this +** interface class will work. You can usually ensure this by deriving the +** class T object from this node. +*/ +template class List; +template +class Node : public GenericNode { + public: + List * Main_List(void) const {return((List *)GenericNode::Main_List());} + T * Next(void) const {return((T *)GenericNode::Next());} + T * Prev(void) const {return((T *)GenericNode::Prev());} + bool Is_Valid(void) const {return(GenericNode::Is_Valid());} +}; + + +/* +** This is an "interface class" for a list of nodes. The rules for the class T object +** are the same as the requirements required of the node class. +*/ +template +class List : public GenericList { + public: + T * First(void) const {return((T*)GenericList::First());} + T * Last(void) const {return((T*)GenericList::Last());} +}; + + +#endif diff --git a/CODE/LOADDLG.CPP b/CODE/LOADDLG.CPP new file mode 100644 index 0000000..d766ddb --- /dev/null +++ b/CODE/LOADDLG.CPP @@ -0,0 +1,784 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LOADDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LOADDLG.CPP * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * LoadOptionsClass::Process -- main processing routine * + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * LoadOptionsClass::Compare -- for qsort * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for unlink + + +/*********************************************************************************************** + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * * + * INPUT: * + * style style for this load/save dialog (LOAD/SAVE/DELETE) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::LoadOptionsClass(LoadStyleType style) +{ + Style = style; + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::~LoadOptionsClass() +{ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Process -- main processing routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * false = User cancelled, true = operation completed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 250 * RESFACTOR; // dialog width + int d_dialog_h = 156 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); // centered x-coord + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int d_list_w = d_dialog_w - (x_margin * 2); + int d_list_h = 104 * RESFACTOR; + int d_list_x = d_dialog_x + x_margin; + int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin; + + int d_edit_w = d_dialog_w - (x_margin * 2); + int d_edit_h = 13 * RESFACTOR; + int d_edit_x = d_dialog_x + x_margin; + int d_edit_y = d_list_y + d_list_h - (30 * RESFACTOR) + d_margin + d_txt8_h; + +#if (GERMAN | FRENCH) + int d_button_w = 50 * RESFACTOR; +#else + int d_button_w = 40 * RESFACTOR; +#endif + int d_button_h = 13 * RESFACTOR; + int d_button_x = d_dialog_cx - d_button_w - d_margin; + int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 60 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_LOAD = 100, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_CANCEL, + BUTTON_LIST, + BUTTON_EDIT, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + int list_ht = d_list_h; // adjusted list box height + + /* + ** Other Variables + */ + int btn_txt; // text on the 'OK' button + int btn_id; // ID of 'OK' button + int caption; // dialog caption + int game_idx = 0; // index of game to save/load/etc + int game_num = 0; // file number of game to load/save/etc + char game_descr[DESCRIP_MAX] = {0}; // save-game description + char fname[_MAX_NAME+_MAX_EXT]; // for generating filename to delete + int rc; // return code + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + switch (Style) { + case LOAD: + btn_txt = TXT_LOAD_BUTTON; + btn_id = BUTTON_LOAD; + caption = TXT_LOAD_MISSION; + break; + + case SAVE: + btn_txt = TXT_SAVE_BUTTON; + btn_id = BUTTON_SAVE; + caption = TXT_SAVE_MISSION; + list_ht -= 30; + break; + + default: + btn_txt = TXT_DELETE_BUTTON; + btn_id = BUTTON_DELETE; + caption = TXT_DELETE_MISSION; + break; + } + + TextButtonClass button (btn_id, btn_txt, TPF_BUTTON, d_button_x, d_button_y, d_button_w); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + + ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht, + TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + EditClass editbtn (BUTTON_EDIT, game_descr, sizeof(game_descr)-4, TPF_6PT_GRAD|TPF_NOSHADOW, d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + Fill_List(&listbtn); + + /* + ** Do nothing if list is empty. + */ + if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) { + Clear_List(&listbtn); + WWMessageBox().Process(TXT_NO_SAVES); + return(false); + } + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + listbtn.Add_Tail(*commands); + if (Style == SAVE) { + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + } + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + cancel = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w); + + if (Style == SAVE) { + Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx, + d_edit_y - d_txt8_h, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER); + } + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus if this is the save dialog. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime && Style == SAVE) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN || input == (BUTTON_EDIT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + switch (Style) { + case SAVE: + input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON); + cancelbtn.Turn_Off(); +// cancelbtn.IsOn = false; + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_SAVE); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case LOAD: + input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_LOAD); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case WWDELETE: + input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + } + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_LOAD | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (Files[game_idx]->Valid) { + + /* + ** Start a timer before we load the game + */ + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_LOADING, TXT_NONE); + Theme.Fade_Out(); + rc = Load_Game(game_num); + + /* + ** Make sure the message says on the screen at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + if (!rc) { + WWMessageBox().Process(TXT_ERROR_LOADING_GAME); + } else { + Speak(VOX_LOAD1); + while (Is_Speaking()) { + Call_Back(); + } + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); +// Set_Palette(GamePalette); + Show_Mouse(); + process = false; + } + } else { + WWMessageBox().Process(TXT_OBSOLETE_SAVEGAME); + } + break; + + /* + ** Save: Save the game & exit the dialog + */ + case (BUTTON_EDIT | KN_BUTTON): + + case (BUTTON_SAVE | KN_BUTTON): + if (!strlen(game_descr)) { + WWMessageBox().Process(TXT_MUSTENTER_DESCRIPTION); + firsttime = true; + display = true; + break; + } + game_idx = listbtn.Current_Index(); + if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) { + WWMessageBox().Process(TXT_SPACE_CANT_SAVE); + firsttime = true; + display = true; + break; + } + + game_num = Files[game_idx]->Num; + if (!Save_Game(game_num, game_descr)) { + WWMessageBox().Process(TXT_ERROR_SAVING_GAME); + } else { + Speak(VOX_SAVE1); + while (Is_Speaking()) { + Call_Back(); + } + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE); + + /* + ** Delay to let the user read the message + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + } + process = false; + break; + + /* + ** Delete: delete the file & stay in the dialog, to allow the user + ** to delete multiple files. + */ + case (BUTTON_DELETE | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (WWMessageBox().Process(TXT_DELETE_FILE_QUERY, TXT_YES, TXT_NO)==0) { + sprintf(fname, "SAVEGAME.%03d", game_num); + unlink(fname); + Clear_List(&listbtn); + Fill_List(&listbtn); + if (listbtn.Count() == 0) { + process = false; + } else { + ToggleClass * toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { +// toggle->IsOn = false; + toggle->Turn_Off(); + toggle->IsPressed = false; + toggle->Flag_To_Redraw(); + } + } + } + display = true; + break; + + /* + ** If the user clicks on the list, see if the there is a new current + ** item; if so, and if we're in SAVE mode, copy the list item into + ** the save-game description field. + */ + case (BUTTON_LIST | KN_BUTTON): + if (Style != SAVE) { + break; + } + + if (listbtn.Count() && listbtn.Current_Index() != game_idx) { + game_idx = listbtn.Current_Index(); + + /* + ** Copy the game's description, UNLESS it's the empty slot; if + ** it is, set the edit buffer to empty. + */ + if (game_idx != 0) { + strcpy(game_descr, listbtn.Get_Item(game_idx)); + + /* + ** Strip any leading parenthesis off of the description. + */ + if (game_descr[0] == '(') { + char * ptr = strchr(game_descr, ')'); + if (ptr != NULL) { + strcpy(game_descr, ptr+1); + strtrim(game_descr); + } + } + + } else { + game_descr[0] = 0; + } + editbtn.Set_Text(game_descr, 40); + } + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + Clear_List(&listbtn); + + if (cancel) return(false); + + return(true); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * * + * This step is essential, because it frees all the strings allocated for list items. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void LoadOptionsClass::Clear_List(ListClass * list) +{ + /* + ** For every item in the list, free its buffer & remove it from the list. + */ + int j = list->Count(); + for (int i = 0; i < j; i++) { + list->Remove_Item(list->Get_Item(0)); + } + + /* + ** Clear the array of game numbers + */ + for (i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 06/25/1995 JLB : Shows which saved games are "(old)". * + *=============================================================================================*/ +void LoadOptionsClass::Fill_List(ListClass * list) +{ + FileEntryClass * fdata; // for adding entries to 'Files' + char descr[DESCRIP_MAX+32]; + unsigned scenario; // scenario # + HousesType house; // house + struct find_t ff; // for _dos_findfirst + int id; + + /* + ** Make sure the list is empty + */ + Clear_List(list); + + /* + ** Add the Empty Slot entry + */ + if (Style == SAVE) { + fdata = new FileEntryClass; + strcpy(fdata->Descr, Text_String(TXT_EMPTY_SLOT)); + fdata->DateTime = 0xffffffff; // will always be first + Files.Add(fdata); + } + + /* + ** Find all savegame files + */ + int rc = _dos_findfirst("SAVEGAME.*", _A_NORMAL, &ff); + + while (!rc) { + + if (stricmp(ff.name, NET_SAVE_FILE_NAME) != 0) { + + /* + ** Extract the game ID from the filename + */ + id = Num_From_Ext(ff.name); + + /* + ** get the game's info; if success, add it to the list + */ + bool ok = Get_Savefile_Info(id, descr, &scenario, &house); + + fdata = new FileEntryClass; + + fdata->Descr[0] = '\0'; + if (!ok) { + strcpy(fdata->Descr, Text_String(TXT_OLD_GAME)); + } else { + if (house == HOUSE_USSR || house == HOUSE_UKRAINE) { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_SOVIET)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_SOVIET)); +#endif + } else { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_ALLIES)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_ALLIES)); +#endif + } + } + strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1); + fdata->Valid = ok; + fdata->Scenario = scenario; + fdata->House = house; + fdata->Num = id; + fdata->DateTime = (((unsigned long)ff.wr_date) << 16) | (unsigned long)ff.wr_time; + Files.Add(fdata); + } + + /* + ** Find the next file + */ + rc = _dos_findnext(&ff); + } + + /* + ** If saving a game, determine a unique file ID for the empty slot + */ + if (Style == SAVE) { + /* + ** Find an un-used number to associate with the Empty Slot by looking in + ** GameNum for each number from 0 to 'N', where 'N' is the # of entries + ** in the list; if any number isn't found, use that number; otherwise, + ** use 'N + 1'. + */ + for (int i = 0; i < Files.Count(); i++) { // i = the # we're searching for + id = -1; // mark as 'not found' + for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's + if (Files[j]->Num==i) { // if found, mark as found + id = j; + break; + } + } + if (id == -1) break; // if ID not found, use this one + } + + Files[0]->Num = i; // set the empty slot's ID + } + + /* + ** Now sort the list in order of Date/Time (newest first, oldest last) + */ + qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare); + + /* + ** Now add every file's name to the list box + */ + for (int i = 0; i < Files.Count(); i++) { + list->Add_Item(Files[i]->Descr); + } +} + + +/*********************************************************************************************** + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * * + * INPUT: * + * fname filename to parse * + * * + * OUTPUT: * + * File number for this name. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Num_From_Ext(char * fname) +{ + char ext[_MAX_EXT]; + + _splitpath(fname, NULL, NULL, NULL, ext); + int num = atoi(ext + 1); // skip the '.' + return(num); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Compare(const void * p1, const void * p2) +{ + class FileEntryClass * fe1, * fe2; + + fe1 = *((class FileEntryClass **)p1); + fe2 = *((class FileEntryClass **)p2); + + if (fe1->DateTime > fe2->DateTime) return(-1); + if (fe1->DateTime < fe2->DateTime) return(1); + return(0); +} + diff --git a/CODE/LOADDLG.H b/CODE/LOADDLG.H new file mode 100644 index 0000000..5895e8a --- /dev/null +++ b/CODE/LOADDLG.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LOADDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LOADDLG.H * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : March 19, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOADDLG_H +#define LOADDLG_H + +class FileEntryClass { + public: + char Descr[80]; // save-game description + unsigned Scenario; // scenario # + HousesType House; // house + int Num; // save file number (from the extension) + unsigned long DateTime; // date/time stamp of file + bool Valid; // Is the scenario valid? +}; + +class LoadOptionsClass +{ + public: + /* + ** This defines the style of the dialog + */ + typedef enum OperationModeEnum { + NONE = 0, + LOAD, + SAVE, + WWDELETE + } LoadStyleType; + + LoadOptionsClass (LoadStyleType style = LoadOptionsClass::NONE); + ~LoadOptionsClass (); + int Process (void); + + + protected: + /* + ** Internal routines + */ + void Clear_List (ListClass *list); // clears the list & game # array + void Fill_List (ListClass *list); // fills the list & game # array + int Num_From_Ext (char *fname); // translates filename to file # + static int Compare(const void *p1, const void *p2); // for qsort() + + /* + ** This is the requested style of the dialog + */ + LoadStyleType Style; + + /* + ** This is an array of pointers to FileEntryClass objects. These objects + ** are allocated on the fly as files are found, and pointers to them are + ** added to the vector list. Thus, all the objects must be free'd before + ** the vector list is cleared. This list is used for sorting the files + ** by date/time. + */ + DynamicVectorClass Files; +}; + + +#endif + diff --git a/CODE/LOGIC.CPP b/CODE/LOGIC.CPP new file mode 100644 index 0000000..92040b3 --- /dev/null +++ b/CODE/LOGIC.CPP @@ -0,0 +1,417 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LOGIC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LOGIC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 27, 1993 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LogicClass::AI -- Handles AI logic processing for game objects. * + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "logic.h" +#include "vortex.h" + +static unsigned FramesPerSecond=0; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * * + * This is a debugging support routine. It displays the current state of the logic class * + * to the monochrome monitor. It assumes that it is being called once per second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per second. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 01/26/1996 JLB : Prints game time value. * + *=============================================================================================*/ +void LogicClass::Debug_Dump(MonoClass * mono) const +{ + #define RECORDCOUNT 40 + #define RECORDHEIGHT 21 + static int _framecounter = 0; + + static bool first = true; + if (first) { + first = false; + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_STRESS)); + } + +//mono->Set_Cursor(0,0);mono->Printf("%d", AllowVoice); + + + _framecounter++; + mono->Set_Cursor(1, 1);mono->Printf("%ld", (long)Scen.Timer); + mono->Set_Cursor(10, 1);mono->Printf("%3d", FramesPerSecond); + mono->Set_Cursor(1, 3);mono->Printf("%02d:%02d:%02d", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND); + + mono->Set_Cursor(1, 11);mono->Printf("%3d", Units.Count()); + mono->Set_Cursor(1, 12);mono->Printf("%3d", Infantry.Count()); + mono->Set_Cursor(1, 13);mono->Printf("%3d", Aircraft.Count()); + mono->Set_Cursor(1, 14);mono->Printf("%3d", Vessels.Count()); + mono->Set_Cursor(1, 15);mono->Printf("%3d", Buildings.Count()); + mono->Set_Cursor(1, 16);mono->Printf("%3d", Terrains.Count()); + mono->Set_Cursor(1, 17);mono->Printf("%3d", Bullets.Count()); + mono->Set_Cursor(1, 18);mono->Printf("%3d", Anims.Count()); + mono->Set_Cursor(1, 19);mono->Printf("%3d", Teams.Count()); + mono->Set_Cursor(1, 20);mono->Printf("%3d", Triggers.Count()); + mono->Set_Cursor(1, 21);mono->Printf("%3d", TriggerTypes.Count()); + mono->Set_Cursor(1, 22);mono->Printf("%3d", Factories.Count()); + + SpareTicks = min((long)SpareTicks, (long)TIMER_SECOND); + + /* + ** CPU utilization record. + */ + mono->Sub_Window(15, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%3d%%", ((TIMER_SECOND-SpareTicks)*100) / TIMER_SECOND); + + /* + ** Update the frame rate log. + */ + mono->Sub_Window(22, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", FramesPerSecond); + + /* + ** Update the findpath calc record. + */ + mono->Sub_Window(50, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", PathCount); + PathCount = 0; + + /* + ** Update the cell redraw record. + */ + mono->Sub_Window(29, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", CellCount); + CellCount = 0; + + /* + ** Update the target scan record. + */ + mono->Sub_Window(36, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", TargetScan); + TargetScan = 0; + + /* + ** Sidebar redraw record. + */ + mono->Sub_Window(43, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", SidebarRedraws); + SidebarRedraws = 0; + + /* + ** Update the CPU utilization chart. + */ + mono->Sub_Window(15, 13, 63, 10); + mono->Pan(1); + mono->Sub_Window(15, 13, 64, 10); + int graph = RECORDHEIGHT * fixed(TIMER_SECOND-SpareTicks, TIMER_SECOND); + for (int row = 1; row < RECORDHEIGHT; row += 2) { + static char _barchar[4] = {' ', 220, 0, 219}; + char str[2]; + int index = 0; + + index |= (graph >= row) ? 0x01 : 0x00; + index |= (graph >= row+1) ? 0x02: 0x00; + + str[1] = '\0'; + str[0] = _barchar[index]; + mono->Text_Print(str, 62, 9-(row/2)); + } + mono->Sub_Window(); + + + SpareTicks = 0; + FramesPerSecond = 0; +} +#endif + + +/*********************************************************************************************** + * LogicClass::AI -- Handles AI logic processing for game objects. * + * * + * This routine is used to perform the AI processing for all game objects. This includes * + * all houses, factories, objects, and teams. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * + * 12/23/1994 JLB : Ensures that no object gets skipped if it was deleted. * + *=============================================================================================*/ +void LogicClass::AI(void) +{ + int index; + + FramesPerSecond++; + + /* + ** Fading to B&W or color due to the chronosphere is handled here. + */ + Scen.Do_Fade_AI(); + + /* + ** Handle any general timer trigger events. + */ + for (LogicTriggerID = 0; LogicTriggerID < LogicTriggers.Count(); LogicTriggerID++) { + TriggerClass * trig = LogicTriggers[LogicTriggerID]; + + /* + ** Global changed trigger event might be triggered. + */ + if (Scen.IsGlobalChanged) { + if (trig->Spring(TEVENT_GLOBAL_SET)) continue; + if (trig->Spring(TEVENT_GLOBAL_CLEAR)) continue; + } + + /* + ** Bridge change event. + */ + if (Scen.IsBridgeChanged) { + if (trig->Spring(TEVENT_ALL_BRIDGES_DESTROYED)) continue; + } + + /* + ** General time expire trigger events can be sprung without warning. + */ + if (trig->Spring(TEVENT_TIME)) continue; + + /* + ** The mission timer expiration trigger event might spring if the timer is active + ** but at a value of zero. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + if (trig->Spring(TEVENT_MISSION_TIMER_EXPIRED)) continue; + } + } + + /* + ** Clean up any status values that were maintained only for logic trigger + ** purposes. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + Scen.MissionTimer.Stop(); + Map.Flag_To_Redraw(true); // Used only to cause tabs to redraw in new state. + } + Scen.IsGlobalChanged = false; + Scen.IsBridgeChanged = false; + /* + ** Shadow creeping back over time is handled here. + */ + if (Special.IsShadowGrow && Rule.ShroudRate != 0 && Scen.ShroudTimer == 0) { + Scen.ShroudTimer = TICKS_PER_MINUTE * Rule.ShroudRate; + Map.Encroach_Shadow(); + } + + /* + ** Team AI is processed. + */ + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->AI(); + } + + /* + ** If there's a time quake, handle it here. + */ + if (TimeQuake) { + Sound_Effect(VOC_KABOOM15); + Shake_The_Screen(8); + } + + ChronalVortex.AI(); + /* + ** AI for all sentient objects is processed. + */ + for (index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + + BStart(BENCH_AI); + obj->AI(); + BEnd(BENCH_AI); + + if (TimeQuake && obj != NULL && obj->IsActive && !obj->IsInLimbo && obj->Strength) { + int damage = obj->Class_Of().MaxStrength * Rule.QuakeDamagePercent; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (TimeQuakeCenter) { + if(::Distance(obj->As_Target(),TimeQuakeCenter)/256 < MTankDistance) { + switch(obj->What_Am_I()) { + case RTTI_INFANTRY: + damage = QuakeInfantryDamage; + break; + case RTTI_BUILDING: + damage = QuakeBuildingDamage * obj->Class_Of().MaxStrength; + break; + default: + damage = QuakeUnitDamage * obj->Class_Of().MaxStrength; + break; + } + if (damage) { + obj->Clicked_As_Target(); + new AnimClass(ANIM_MINE_EXP1, obj->Center_Coord()); + } + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } else { + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } +#else + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); +#endif + } + /* + ** If the object was destroyed in the process of performing its AI, then + ** adjust the index so that no object gets skipped. + */ + if (obj != (*this)[index]) { + index--; + } + } + HouseClass::Recalc_Attributes(); + + /* + ** Map related logic is performed. + */ + Map.Logic(); + + /* + ** Factory processing is performed. + */ + for (index = 0; index < Factories.Count(); index++) { + Factories.Ptr(index)->AI(); + } + + /* + ** House processing is performed. + */ +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + { + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } + else + { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } +#else // AI() is called redundantly 12 times in multiplayer games here. ajw + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Session.Type != GAME_NORMAL && Scen.AutoSonarTimer == 0 ) + { + if( bAutoSonarPulse ) + { + Map.Activate_Pulse(); + Sound_Effect(VOC_SONAR); + bAutoSonarPulse = false; + } +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40; + Scen.AutoSonarTimer = AUTOSONAR_PERIOD; + } +#endif +} + + +/*********************************************************************************************** + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * * + * This routine is called when the specified target object is about to be removed from the * + * game system and all references to it must be severed. The only thing that the logic * + * system looks for in this case is to see if the target refers to a trigger and if so, * + * it scans through the trigger list and removes all references to it. * + * * + * INPUT: target -- The target to remove from the sytem. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void LogicClass::Detach(TARGET target, bool ) +{ + /* + ** Remove any triggers from the logic trigger list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + if (As_Trigger(target) == LogicTriggers[index]) { + LogicTriggers.Delete(index); + index--; + } + } + } +} + diff --git a/CODE/LOGIC.H b/CODE/LOGIC.H new file mode 100644 index 0000000..87c44b3 --- /dev/null +++ b/CODE/LOGIC.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LOGIC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LOGIC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 29, 1994 * + * * + * Last Update : May 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "layer.h" + +/*********************************************************************************************** +** Game logic processing is controlled by this class. The graphic and AI logic is handled +** separately so that on slower machines, the graphic display is least affected. +*/ +class LogicClass : public LayerClass +{ + public: + void AI(void); + void Detach(TARGET target, bool all=true); + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif +}; +#endif diff --git a/CODE/LZO.H b/CODE/LZO.H new file mode 100644 index 0000000..44cff94 --- /dev/null +++ b/CODE/LZO.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZO_H +#define _LZO_H + +#include "lzoconf.h" + +int lzo1x_1_compress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp wrkmem ); + + +int lzo1x_decompress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp ); + + + + + +#endif diff --git a/CODE/LZO1X.H b/CODE/LZO1X.H new file mode 100644 index 0000000..ef005a8 --- /dev/null +++ b/CODE/LZO1X.H @@ -0,0 +1,110 @@ +/* +** 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 . +*/ + +/* lzo1x.h -- public interface of the LZO1X compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZO1X_H +#define __LZO1X_H + +#include "lzoconf.h" + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// +************************************************************************/ + +/* Memory required for the wrkmem parameter. + * When the required size is 0, you can also pass a NULL pointer. + */ + +#define LZO1X_MEM_COMPRESS ((lzo_uint) (16384L * sizeof(lzo_byte *))) +#define LZO1X_MEM_DECOMPRESS (0) + + +/* fast decompression */ +LZO_EXTERN(int) +lzo1x_decompress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + +/* safe decompression with overrun testing */ +LZO_EXTERN(int) +lzo1x_decompress_x ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + + +/*********************************************************************** +// +************************************************************************/ + +LZO_EXTERN(int) +lzo1x_1_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/*********************************************************************** +// better compression ratio at the cost of more memory and time +************************************************************************/ + +#define LZO1X_999_MEM_COMPRESS ((lzo_uint) (14 * 16384L * sizeof(short))) + +LZO_EXTERN(int) +lzo1x_999_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/CODE/LZO1X_C.CPP b/CODE/LZO1X_C.CPP new file mode 100644 index 0000000..31cdcd7 --- /dev/null +++ b/CODE/LZO1X_C.CPP @@ -0,0 +1,349 @@ +/* +** 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 . +*/ + +/* lzo1x_c.c -- standalone LZO1X-1 compressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#define NDEBUG +#include +#include "lzo_conf.h" + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define M1_MAX_OFFSET 0x0400 +#if defined(LZO1X) +#define M2_MAX_OFFSET 0x0800 +#elif defined(LZO1Y) +#define M2_MAX_OFFSET 0x0400 +#endif +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + + +#define _DV2(p,shift1,shift2) \ + (((( (lzo_uint)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0]) +#define DVAL_NEXT(dv,p) \ + dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint)(p[2]) << (2*5))) +#define _DV(p,shift) _DV2(p,shift,shift) +#define DVAL_FIRST(dv,p) dv = _DV((p),5) +#define _DINDEX(dv,p) ((40799u * (dv)) >> 5) +#define DINDEX(dv,p) (((_DINDEX(dv,p)) & 0x3fff) << 0) +#define UPDATE_D(dict,cycle,dv,p) dict[ DINDEX(dv,p) ] = (p) +#define UPDATE_I(dict,cycle,index,p) dict[index] = (p) + + +/*********************************************************************** +// compress a block of data. +************************************************************************/ + +static int do_compress(const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + register const lzo_byte *ip; + lzo_uint dv; + lzo_byte *op; + const lzo_byte * const in_end = in + in_len; + const lzo_byte * const ip_end = in + in_len - 9 - 4; + const lzo_byte *ii; + const lzo_bytepp const dict = (const lzo_bytepp) wrkmem; + + op = out; + ip = in; + ii = ip; + + DVAL_FIRST(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + + while (1) { + register const lzo_byte *m_pos; + lzo_uint m_len; + lzo_ptrdiff_t m_off; + lzo_uint lit; + + lzo_uint dindex = DINDEX(dv,ip); + m_pos = dict[dindex]; + UPDATE_I(dict,cycle,dindex,ip); + + + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) { + } +#if defined(LZO_UNALIGNED_OK_2) + else + if (* (unsigned short *) m_pos != * (unsigned short *) ip) +#else + else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } else { + if (m_pos[2] == ip[2]) { + lit = ip - ii; + m_pos += 3; + if (m_off <= M2_MAX_OFFSET) + goto match; + + /* better compression, but slower */ + if (lit == 3) { + assert(op - 2 > out); op[-2] |= LZO_BYTE(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + + if (*m_pos == ip[3]) { + goto match; + } + } else { + /* still need a better way for finding M1 matches */ + } + } + + + /* a literal */ + ++ip; + if (ip >= ip_end) { + break; + } + DVAL_NEXT(dv,ip); + continue; + + + /* a match */ + +match: + + /* store current literal run */ + if (lit > 0) { + register lzo_uint t = lit; + + if (t <= 3) { + assert(op - 2 > out); + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + + do { + *op++ = *ii++; + } while (--t > 0); + } + + + /* code the match */ +code_match: + assert(ii == ip); + ip += 3; + if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ || + *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) + { + --ip; + m_len = ip - ii; + assert(m_len >= 3); assert(m_len <= 8); + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); + } else { + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; + } + } + } else { + const lzo_byte *end; + end = in_end; + while (ip < end && *m_pos == *ip) { + m_pos++; + ip++; + } + m_len = (ip - ii); + assert(m_len >= 3); + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) { + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + } else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + if (m_len <= 9) { + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + } else { + m_len -= 9; + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11)); +m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = LZO_BYTE(m_len); + } + } + +m3_m4_offset: + *op++ = LZO_BYTE((m_off & 63) << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + + ii = ip; + if (ip >= ip_end) { + break; + } + DVAL_FIRST(dv,ip); + } + + /* store final literal run */ + if (in_end - ii > 0) { + register lzo_uint t = in_end - ii; + + if (op == out && t <= 238) { + *op++ = LZO_BYTE(17 + t); + } else { + if (t <= 3) { + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + } + do { + *op++ = *ii++; + } while (--t > 0); + } + + *out_len = op - out; + return LZO_E_OK; +} + + +/*********************************************************************** +// public entry point +************************************************************************/ + +int lzo1x_1_compress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + lzo_byte *op = out; + int r = LZO_E_OK; + + if (in_len <= 0) + *out_len = 0; + else if (in_len <= 9 + 4) + { + *op++ = LZO_BYTE(17 + in_len); + do *op++ = *in++; while (--in_len > 0); + *out_len = op - out; + } + else + r = do_compress(in,in_len,out,out_len,wrkmem); + + if (r == LZO_E_OK) + { + op = out + *out_len; + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + *out_len += 3; + } + + return r; +} + + +/* +vi:ts=4 +*/ diff --git a/CODE/LZO1X_D.CPP b/CODE/LZO1X_D.CPP new file mode 100644 index 0000000..4b67434 --- /dev/null +++ b/CODE/LZO1X_D.CPP @@ -0,0 +1,206 @@ +/* +** 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 . +*/ + +/* lzo1x_d.c -- standalone LZO1X decompressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#define NDEBUG +#include + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + +#if 1 +# define TEST_IP 1 +#else +# define TEST_IP (ip < ip_end) +#endif + + +/*********************************************************************** +// decompress a block of data. +************************************************************************/ + +int lzo1x_decompress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp ) +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; + register const lzo_byte *m_pos; + const lzo_byte * const ip_end = in + in_len; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + goto first_literal_run; + } + + while (TEST_IP) + { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + if (t == 0) + { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + /* copy literals */ + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; +first_literal_run: + do *op++ = *ip++; while (--t > 0); + + + t = *ip++; + + if (t >= 16) + goto match; +#if defined(LZO1X) + m_pos = op - 1 - 0x800; +#elif defined(LZO1Y) + m_pos = op - 1 - 0x400; +#endif + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++; + goto match_done; + + + /* handle matches */ + while (TEST_IP) + { + if (t < 16) /* a M1 match */ + { + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; + } + else + { +match: + if (t >= 64) /* a M2 match */ + { + m_pos = op - 1; +#if defined(LZO1X) + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#endif + } + else if (t >= 32) /* a M3 match */ + { + t &= 31; + if (t == 0) + { + t = 31; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } + else /* a M4 match */ + { + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) + { + t = 7; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +match_done: + t = ip[-2] & 3; + if (t == 0) + break; + /* copy literals */ + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } + } + + /* ip == ip_end and no EOF code was found */ + + //Unreachable - ST 9/5/96 5:07PM + //*out_len = op - out; + //return (ip == ip_end ? LZO_E_EOF_NOT_FOUND : LZO_E_ERROR); + +eof_found: + assert(t == 1); + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : LZO_E_ERROR); +} + + +/* +vi:ts=4 +*/ diff --git a/CODE/LZOCONF.H b/CODE/LZOCONF.H new file mode 100644 index 0000000..54fce63 --- /dev/null +++ b/CODE/LZOCONF.H @@ -0,0 +1,217 @@ +/* +** 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 . +*/ + +/* lzoconf.h -- configuration for the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZOCONF_H +#define __LZOCONF_H + +#define LZO_VERSION 0x0200 +#define LZO_VERSION_STRING "0.20" +#define LZO_VERSION_DATE "11 Aug 1996" + + +#include /* CHAR_BIT, UINT_MAX, ULONG_MAX */ +#if !defined(CHAR_BIT) || (CHAR_BIT != 8) +# error invalid CHAR_BIT +#endif + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// defines +************************************************************************/ + +#if defined(__MSDOS__) || defined(MSDOS) +# define __LZO_MSDOS +# if (UINT_MAX < 0xffffffffL) +# define __LZO_MSDOS16 +# endif +#endif + + +/*********************************************************************** +// integral and pointer types +************************************************************************/ + +/* Unsigned type with 32 bits or more */ +#if (UINT_MAX >= 0xffffffffL) + typedef unsigned int lzo_uint; + typedef int lzo_int; +# define LZO_UINT_MAX UINT_MAX +#elif (ULONG_MAX >= 0xffffffffL) + typedef unsigned long lzo_uint; + typedef long lzo_int; +# define LZO_UINT_MAX ULONG_MAX +#else +# error lzo_uint +#endif + + +/* Memory model that allows to access memory at offsets of lzo_uint. + * Huge pointers (16 bit MSDOS) are somewhat slow, but they work + * fine and I really don't care about 16 bit compiler + * optimizations nowadays. + */ +#if (LZO_UINT_MAX <= UINT_MAX) +# define __LZO_MMODEL +#elif defined(__LZO_MSDOS16) +# define __LZO_MMODEL huge +# define __LZO_ENTRY __cdecl +#else +# error __LZO_MMODEL +#endif + + +/* no typedef here because of const-pointer issues */ +#define lzo_byte unsigned char __LZO_MMODEL +#define lzo_voidp void __LZO_MMODEL * +#define lzo_bytep unsigned char __LZO_MMODEL * +#define lzo_uintp lzo_uint __LZO_MMODEL * +#define lzo_intp lzo_int __LZO_MMODEL * +#define lzo_voidpp lzo_voidp __LZO_MMODEL * +#define lzo_bytepp lzo_bytep __LZO_MMODEL * + + +/* Unsigned type that can store all bits of a lzo_voidp */ +typedef unsigned long lzo_ptr_t; + +/* Align a pointer on a boundary that is a multiple of 'size' */ +#define LZO_ALIGN(ptr,size) \ + ((lzo_voidp) (((lzo_ptr_t)(ptr) + (size)-1) & ~((lzo_ptr_t)((size)-1)))) + + +/*********************************************************************** +// function types +************************************************************************/ + +//#ifdef __cplusplus +//# define LZO_EXTERN_C extern "C" +//#else +# define LZO_EXTERN_C extern +//#endif + + +#if !defined(__LZO_ENTRY) /* calling convention */ +# define __LZO_ENTRY +#endif +#if !defined(__LZO_EXPORT) /* DLL export (and maybe size) information */ +# define __LZO_EXPORT +#endif + +#if !defined(LZO_EXTERN) +# define LZO_EXTERN(_rettype) LZO_EXTERN_C _rettype __LZO_ENTRY __LZO_EXPORT +#endif + + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_compress_t) ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_decompress_t)( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/* a progress indicator callback function */ +typedef void __LZO_ENTRY +(__LZO_EXPORT *lzo_progress_callback_t)(lzo_uint,lzo_uint); + + +/*********************************************************************** +// error codes and prototypes +************************************************************************/ + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_NOT_COMPRESSIBLE (-2) /* not used right now */ +#define LZO_E_EOF_NOT_FOUND (-3) +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_OUT_OF_MEMORY (-7) /* not used right now */ + + +/* this should be the first function you call. Check the return code ! */ +LZO_EXTERN(int) lzo_init(void); + +/* version functions (useful for shared libraries) */ +LZO_EXTERN(unsigned) lzo_version(void); +LZO_EXTERN(const char *) lzo_version_string(void); + +/* string functions */ +LZO_EXTERN(int) +lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memset(lzo_voidp _s, int _c, lzo_uint _len); + +/* checksum functions */ +LZO_EXTERN(lzo_uint) +lzo_adler32(lzo_uint _adler, const lzo_byte *_buf, lzo_uint _len); + +/* misc. */ +LZO_EXTERN(int) lzo_assert(int _expr); +LZO_EXTERN(int) _lzo_config_check(void); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/CODE/LZOPIPE.CPP b/CODE/LZOPIPE.CPP new file mode 100644 index 0000000..1381852 --- /dev/null +++ b/CODE/LZOPIPE.CPP @@ -0,0 +1,321 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZOPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZO processor pipe. * + * LZWPipe::Put -- Send some data through the LZO processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZO pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzopipe.h" +#include "lzo.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZOPipe::LZOPipe -- Constructor for the LZO processor pipe. * + * * + * This will initialize the LZOPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::LZOPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZOPipe::~LZOPipe -- Deconstructor for the LZO pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::~LZOPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOPipe::Put -- Send some data through the LZO processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZO processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < (sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + unsigned int length = sizeof (Buffer2); + lzo1x_decompress ((unsigned char*)Buffer, BlockHeader.CompCount, (unsigned char*)Buffer2, &length, NULL); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)source, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZOPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZO decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, Counter, (unsigned char *)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/CODE/LZOPIPE.H b/CODE/LZOPIPE.H new file mode 100644 index 0000000..465c9d5 --- /dev/null +++ b/CODE/LZOPIPE.H @@ -0,0 +1,104 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZOPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZOPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOPIPE_H +#define LZOPIPE_H + +#include "pipe.h" + + +/* +** Performs LZO compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZOPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOPipe(CompControl, int blocksize=1024*8); + virtual ~LZOPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOPipe(LZOPipe & rvalue); + LZOPipe & operator = (LZOPipe const & pipe); +}; + + +#endif diff --git a/CODE/LZOSTRAW.CPP b/CODE/LZOSTRAW.CPP new file mode 100644 index 0000000..7404d64 --- /dev/null +++ b/CODE/LZOSTRAW.CPP @@ -0,0 +1,184 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZOSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZOSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZOStraw::Get -- Fetch data through the LZO processor. * + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * * + * This will initialize the LZO straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::LZOStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::~LZOStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOStraw::Get -- Fetch data through the LZO processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *staging_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + unsigned int length = sizeof(Buffer); + lzo1x_decompress ((unsigned char*)staging_buffer, BlockHeader.CompCount, (unsigned char*)Buffer, &length, NULL); + delete [] staging_buffer; + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + char *dictionary = new char [64*1024]; + unsigned int length = sizeof (Buffer2) - sizeof (BlockHeader); + lzo1x_1_compress ((unsigned char*)Buffer, BlockHeader.UncompCount, (unsigned char*)(&Buffer2[sizeof(BlockHeader)]), &length, dictionary); + BlockHeader.CompCount = (unsigned short)length; + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/CODE/LZOSTRAW.H b/CODE/LZOSTRAW.H new file mode 100644 index 0000000..2591585 --- /dev/null +++ b/CODE/LZOSTRAW.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZoSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZOSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOSTRAW_H +#define LZOSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZO compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZOStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOStraw(CompControl control, int blocksize=1024*8); + virtual ~LZOStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOStraw(LZOStraw & rvalue); + LZOStraw & operator = (LZOStraw const & pipe); +}; + + +#endif diff --git a/CODE/LZO_CONF.H b/CODE/LZO_CONF.H new file mode 100644 index 0000000..45efc70 --- /dev/null +++ b/CODE/LZO_CONF.H @@ -0,0 +1,286 @@ +/* +** 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 . +*/ + +/* lzo_conf.h -- main internal configuration file for the the LZO library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the library and is subject + to change. + */ + + +#ifndef __LZO_CONF_H +#define __LZO_CONF_H + +#ifndef __LZOCONF_H +# include +#endif + + +/*********************************************************************** +// compiler specific defines +************************************************************************/ + +/* need Borland C 4.0 or above because of huge-pointer bugs */ +#if defined(__LZO_MSDOS16) && defined(__TURBOC__) +# if (__TURBOC__ < 0x452) +# error You need a newer compiler version +# endif +#endif + +#if defined(__LZO_MSDOS) || defined(__i386__) || defined(__386__) +# if !defined(__LZO_i386) +# define __LZO_i386 +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#include /* ptrdiff_t, size_t */ +#include /* memcpy, memmove, memcmp, memset */ + +#if 0 && !defined(assert) +# error not included +#endif + +#if defined(__BOUNDS_CHECKING_ON) +# include +#else +# define BOUNDS_CHECKING_OFF_DURING(stmt) stmt +# define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) +#endif + +/* ptrdiff_t */ +#if (UINT_MAX >= 0xffffffffL) + typedef ptrdiff_t lzo_ptrdiff_t; +#else + typedef long lzo_ptrdiff_t; +#endif + + +#ifdef __cplusplus +# define LZO_UNUSED(parm) +#else +# define LZO_UNUSED(parm) parm +#endif + + +#if !defined(__inline__) && !defined(__GNUC__) +# if defined(__cplusplus) +# define __inline__ inline +# else +# define __inline__ /* nothing */ +# endif +#endif + + +/*********************************************************************** +// compiler and architecture specific stuff +************************************************************************/ + +/* Some defines that indicate if memory can be accessed at unaligned + * addresses. You should also test that this is actually faster if + * it is allowed by your system. + */ + +#if 1 && defined(__LZO_i386) +# if !defined(LZO_UNALIGNED_OK_2) +# define LZO_UNALIGNED_OK_2 +# endif +# if !defined(LZO_UNALIGNED_OK_4) +# define LZO_UNALIGNED_OK_4 +# endif +#endif + + +#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK) +# define LZO_UNALIGNED_OK +# endif +#endif + + +/* Definitions for byte order, according to significance of bytes, from low + * addresses to high addresses. The value is what you get by putting '4' + * in the most significant byte, '3' in the second most significant byte, + * '2' in the second least significant byte, and '1' in the least + * significant byte. + */ + +#define LZO_LITTLE_ENDIAN 1234 +#define LZO_BIG_ENDIAN 4321 +#define LZO_PDP_ENDIAN 3412 + +/* The byte order is only needed if we use LZO_UNALIGNED_OK */ +#if !defined(LZO_BYTE_ORDER) +# if defined(__LZO_i386) +# define LZO_BYTE_ORDER LZO_LITTLE_ENDIAN +# elif defined(__mc68000__) +# define LZO_BYTE_ORDER LZO_BIG_ENDIAN +# elif defined(__BYTE_ORDER) +# define LZO_BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#if defined(LZO_UNALIGNED_OK) +# if !defined(LZO_BYTE_ORDER) +# error LZO_BYTE_ORDER is not defined +# elif (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \ + (LZO_BYTE_ORDER != LZO_BIG_ENDIAN) +# error invalid LZO_BYTE_ORDER +# endif +#endif + + +/*********************************************************************** +// optimization +************************************************************************/ + +/* gcc 2.6.3 and gcc 2.7.2 have a bug */ +#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY + +/* Help the optimizer with register allocation. + * Don't activate this macro for a fair comparision with other algorithms. + */ +#if 1 && defined(NDEBUG) && !defined(__BOUNDS_CHECKING_ON) +# if defined(__GNUC__) && defined(__i386__) +# if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY) +# define LZO_OPTIMIZE_GNUC_i386 +# endif +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define LZO_BYTE(x) ((unsigned char) (x)) + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) + +#define lzo_sizeof(x) ((lzo_uint) (sizeof(x))) + +#define LZO_HIGH(x) ((lzo_uint) (sizeof(x)/sizeof(*(x)))) + +/* this always fits into 16 bits */ +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_LSIZE(bits) (1ul << (bits)) +#define LZO_LMASK(bits) (LZO_LSIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + + +/*********************************************************************** +// ANSI C preprocessor macros +************************************************************************/ + +#define _LZO_STRINGIZE(x) #x +#define _LZO_MEXPAND(x) _LZO_STRINGIZE(x) + +/* concatenate */ +#define _LZO_CONCAT2(a,b) a ## b +#define _LZO_CONCAT3(a,b,c) a ## b ## c +#define _LZO_CONCAT4(a,b,c,d) a ## b ## c ## d +#define _LZO_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e + +/* expand and concatenate (by using one level of indirection) */ +#define _LZO_ECONCAT2(a,b) _LZO_CONCAT2(a,b) +#define _LZO_ECONCAT3(a,b,c) _LZO_CONCAT3(a,b,c) +#define _LZO_ECONCAT4(a,b,c,d) _LZO_CONCAT4(a,b,c,d) +#define _LZO_ECONCAT5(a,b,c,d,e) _LZO_CONCAT5(a,b,c,d,e) + + +/*********************************************************************** +// +************************************************************************/ + +/* Generate compressed data in a deterministic way. + * This is fully portable, and compression can be faster as well. + * A reason NOT to be deterministic is when the block size is + * very small (e.g. 8kB) or the dictionary is big, because + * then the initialization of the dictionary becomes a relevant + * magnitude for compression speed. + */ +#define LZO_DETERMINISTIC + + +/*********************************************************************** +// +************************************************************************/ + +#if 0 +/* This line causes problems on some architectures */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_off = ip - m_pos) > max_offset )) + +#else +/* This is the safe (but slower) version */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = ip - m_pos) > max_offset) +#endif + + +/* m_pos may point anywhere... + * This marco is probably a good candidate for architecture specific problems. + * Try casting the pointers to lzo_ptr_t before comparing them. + */ +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_pos < in || (m_off = ip - m_pos) <= 0 || m_off > max_offset) )) + + + +#endif /* already included */ + +/* +vi:ts=4 +*/ + diff --git a/CODE/LZW.CPP b/CODE/LZW.CPP new file mode 100644 index 0000000..b5e96f5 --- /dev/null +++ b/CODE/LZW.CPP @@ -0,0 +1,539 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Child_Node -- Find a matching dictionary entry. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include + +#include "xstraw.h" +#include "xpipe.h" +#include "buff.h" +#include "lzw.h" + + +LZWEngine::LZWEngine(void) +{ + Reset(); +} + + +void LZWEngine::Reset(void) +{ + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); + } +} + +int LZWEngine::Compress(Buffer const & input, Buffer const & output) +{ + BufferStraw instraw(input); + BufferPipe outpipe(output); + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + + string_code = 0; + if (instraw.Get(&string_code, sizeof(char)) == 0) { + string_code = END_OF_STREAM; + } + + for (;;) { + + /* + ** Fetch a character from the source data stream. If exhausted, + ** then break out of the process loop so that the final code + ** can be written out. + */ + unsigned char character; + if (instraw.Get(&character, sizeof(character)) == 0) break; + + /* + ** See if there is a match for the current code and current + ** character. A match indicates that there is already a + ** dictionary entry that fully represents the character + ** sequence. + */ + int index = Find_Child_Node(string_code, character); + + /* + ** If a code match was found, then set the current code + ** value to this code value that represents the concatenation + ** of the previous code value and the current character. + */ + if (index != -1 && dict[index].CodeValue != -1) { + string_code = dict[index].CodeValue; + } else { + + /* + ** Since no exact match was found, then create a new code + ** entry that represents the current code and character + ** value concatenated. This presumes there is room in the + ** code table. + */ + if (index != -1 && next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code, string_code, character); + next_code++; + } + + /* + ** Output the code to the compression stream and reset the + ** current code value to match the current character. This + ** has the effect of clearing out the current character + ** sequence scan in preparation for building a new one. It + ** also ensures that the character will be written out. + */ + outcount += outpipe.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + + outcount += outpipe.Put(&string_code, sizeof(string_code)); + if (string_code != END_OF_STREAM) { + string_code = END_OF_STREAM; + outcount += outpipe.Put(&string_code, sizeof(string_code)); + } + + return(outcount); +} + + +int LZWEngine::Uncompress(Buffer const & input, Buffer const & output) +{ + int outcount = 0; + BufferStraw instraw(input); + BufferPipe outpipe(output); + + CodeType old_code; + if (instraw.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + unsigned char character = (unsigned char)old_code; + outcount += outpipe.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (instraw.Get(&new_code, sizeof(new_code)) == 0) break; + + if (new_code == END_OF_STREAM) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += outpipe.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + + +int LZWEngine::Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +int LZWEngine::Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +int LZWEngine::Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Uncompress(inbuff, outbuff)); +} + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Compress(inbuff, outbuff)); +} + + + + + +#ifdef NEVER + + +/* + * Constants used throughout the program. BITS defines how many bits + * will be in a code. TABLE_SIZE defines the size of the dictionary + * table. + */ +#define BITS 12 +#define MAX_CODE ( ( 1 << BITS ) - 1 ) +#define TABLE_SIZE 5021 +#define END_OF_STREAM 256 +#define FIRST_CODE 257 +#define UNUSED -1 + +typedef unsigned short CodeType; + +/* + * This data structure defines the dictionary. Each entry in the dictionary + * has a code value. This is the code emitted by the compressor. Each + * code is actually made up of two pieces: a parent_code, and a + * character. Code values of less than 256 are actually plain + * text codes. + */ +struct CodeClass +{ + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} +}; +CodeClass dict[TABLE_SIZE]; + +char decode_stack[TABLE_SIZE]; + +inline int Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +/*********************************************************************************************** + * Find_Child_Node -- Find a matching dictionary entry. * + * * + * This hashing routine is responsible for finding the table location * + * for a string/character combination. The table index is created * + * by using an exclusive OR combination of the prefix and character. * + * This code also has to check for collisions, and handles them by * + * jumping around in the table. * + * * + * INPUT: parent_code -- The code of the parent string sequence. * + * * + * character -- The current character. * + * * + * OUTPUT: Returns with the index for the matching dictionary entry. If no matching index * + * could be found, then it returns with the index to an unused dictionary entry. If * + * there are also no unused entries in the dictionary, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/28/1996 JLB : Created. * + *=============================================================================================*/ +static int Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +/* + * This routine decodes a string from the dictionary, and stores it + * in the decode_stack data structure. It returns a count to the + * calling program of how many characters were placed in the stack. + */ +static int Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +/* + * The compressor is short and simple. It reads in new symbols one + * at a time from the input file. It then checks to see if the + * combination of the current symbol and the current code are already + * defined in the dictionary. If they are not, they are added to the + * dictionary, and we start over with a new one symbol code. If they + * are, the code for the combination of the code and character becomes + * our new code. + */ + +int LZW_Compress(Buffer & inbuff, Buffer & outbuff) +{ + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); +// dict[i].code_value = UNUSED; + } + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + for (;;) { + char character; + + if (input.Get(&character, sizeof(character)) == 0) break; + + int index = Find_Child_Node(string_code, character); + + if (index == -1) { + string_code = character; + outcount += output.Put(&string_code, sizeof(string_code)); + } else { + + if (dict[index].CodeValue != -1) { + string_code = dict[ index ].CodeValue; + } else { + if (next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code++, string_code, character); + } + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + } + + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = END_OF_STREAM; + outcount += output.Put(&string_code, sizeof(string_code)); + + return(outcount); +} + + +/* + * The file expander operates much like the encoder. It has to + * read in codes, the convert the codes to a string of characters. + * The only catch in the whole operation occurs when the encoder + * encounters a CHAR+STRING+CHAR+STRING+CHAR sequence. When this + * occurs, the encoder outputs a code that is not presently defined + * in the table. This is handled as an exception. + */ +int LZW_Uncompress(Buffer & inbuff, Buffer & outbuff) +{ + int outcount = 0; + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + CodeType old_code; + if (input.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + char character = (char)old_code; + outcount += output.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (input.Get(&new_code, sizeof(new_code)) == 0) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += output.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + +#endif diff --git a/CODE/LZW.H b/CODE/LZW.H new file mode 100644 index 0000000..2e4a71a --- /dev/null +++ b/CODE/LZW.H @@ -0,0 +1,89 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZW_H +#define _LZW_H + +#include "buff.h" + +class LZWEngine +{ + public: + LZWEngine(void); + + int Compress(Buffer const & input, Buffer const & output); + int Uncompress(Buffer const & input, Buffer const & output); + + void Reset(void); + + private: + typedef short CodeType; + struct CodeClass { + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + enum {UNUSED=-1}; + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} + }; + + enum { + BITS=12, + MAX_CODE=((1 << BITS ) - 1), + FIRST_CODE=257, + END_OF_STREAM=256, + TABLE_SIZE=5021 + }; + CodeClass dict[TABLE_SIZE]; + + char decode_stack[TABLE_SIZE]; + + int Find_Child_Node(CodeType parent_code, char child_character); + int Decode_String(char * ptr, CodeType code); + static int Make_LZW_Hash(CodeType code, char character); +}; + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff); +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff); + +#endif diff --git a/CODE/LZWOTRAW.CPP b/CODE/LZWOTRAW.CPP new file mode 100644 index 0000000..144e7a2 --- /dev/null +++ b/CODE/LZWOTRAW.CPP @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZWOTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *stageing_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + lz01x_decompress (ptr, BlockHeader.CompCount, Buffer, sizeof(Buffer), NULL); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + int len = sizeof (Buffer2) - sizeof (BlockHeader); + char *dictionary = new char [64*1024]; + lzo1x_1_compress (Buffer, BlockHeader.UncompCount, &Buffer2[sizeof(BlockHeader)], &BlockHeader.CompCount, dictionary); + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/CODE/LZWPIPE.CPP b/CODE/LZWPIPE.CPP new file mode 100644 index 0000000..0493174 --- /dev/null +++ b/CODE/LZWPIPE.CPP @@ -0,0 +1,315 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzwpipe.h" +#include "lzw.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * * + * This will initialize the LZWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::LZWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::~LZWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < (sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LZW_Uncompress(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LZW_Compress(::Buffer(Buffer, BlockSize), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LZW_Compress(::Buffer((void*)source, BlockSize), Buffer2); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LZW_Compress(::Buffer(Buffer, Counter), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/CODE/LZWPIPE.H b/CODE/LZWPIPE.H new file mode 100644 index 0000000..d6cf12f --- /dev/null +++ b/CODE/LZWPIPE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWPIPE_H +#define LZWPIPE_H + +#include "pipe.h" + + +/* +** Performs LZW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWPipe(CompControl, int blocksize=1024*8); + virtual ~LZWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWPipe(LZWPipe & rvalue); + LZWPipe & operator = (LZWPipe const & pipe); +}; + + +#endif diff --git a/CODE/LZWSTRAW.CPP b/CODE/LZWSTRAW.CPP new file mode 100644 index 0000000..a81b374 --- /dev/null +++ b/CODE/LZWSTRAW.CPP @@ -0,0 +1,180 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzwstraw.h" +#include "lzw.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LZW_Uncompress(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LZW_Compress(::Buffer(Buffer, BlockHeader.UncompCount), &Buffer2[sizeof(BlockHeader)]); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/CODE/LZWSTRAW.H b/CODE/LZWSTRAW.H new file mode 100644 index 0000000..94fc8d9 --- /dev/null +++ b/CODE/LZWSTRAW.H @@ -0,0 +1,106 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/LZWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : LZWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWSTRAW_H +#define LZWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWStraw(CompControl control, int blocksize=1024*8); + virtual ~LZWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWStraw(LZWStraw & rvalue); + LZWStraw & operator = (LZWStraw const & pipe); +}; + + +#endif diff --git a/CODE/MAKEFILE b/CODE/MAKEFILE new file mode 100644 index 0000000..23bb1ad --- /dev/null +++ b/CODE/MAKEFILE @@ -0,0 +1,755 @@ +# +# 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 . +# + +#* $Header$ +#*********************************************************************************************** +#*** 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 * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : 03/02/95 * +#* * +#* Last Update : March 2, 1995 [JLB] * +#* * +#*---------------------------------------------------------------------------------------------* +#* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +.SILENT +.OPTIMIZE +.ERASE + +#--------------------------------------------------------------------------- +# Working values depending on what target executable will be created. +#--------------------------------------------------------------------------- + +!ifdef GERMAN +LANGUAGE=GERMAN +!else +!ifdef FRENCH +LANGUAGE=FRENCH +!else +LANGUAGE=ENGLISH +!endif +!endif + +!ifdef WIN32 +WWFLAT=..\win32lib +WWOBJ=obj\win32\$(LANGUAGE) +LINKFILE=win95.lnk +CC=..\watcom\binnt\wpp386 +LIB=..\watcom\binnt\wlib +!else +WWFLAT=..\wwflat32 +WWOBJ=obj\dos +LINKFILE=conquer.lnk +CC=..\watcom\binnt\wpp386 +LIB=..\watcom\binnt\wlib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: . +.c: . +.cpp: . +.h: . +.obj: $(WWOBJ) +.lib: $(WWFLAT)\lib +.exe: ..\run + + +#=========================================================================== +# Compiler and assembler flags. +#=========================================================================== +!ifdef WIN32 +CC_CFG = /d0 # No debugging information ( else use /d1 ) +CC_CFG += /DWIN32=1 +CC_CFG += /D_WIN32 +CC_CFG += /DWOLAPI_INTEGRATION +CC_CFG += /DWINSOCK_IPX +CC_CFG += /D$(LANGUAGE)=1 +CC_CFG += /i=..\dxsdk\inc +CC_CFG += /i=..\watcom\h\nt # NT include directory. +CC_CFG += /i=..\watcom\H # Normal Watcom include directory. +CC_CFG += /i=$(WWFLAT)\INCLUDE # Normal library include directory. +CC_CFG += /i=..\winvq\include # Includes player (VQ) directory. +CC_CFG += /bt=NT +CC_CFG += /otxan +CC_CFG += /5r # Pentium optimized register calling conventions. +!else +CC_CFG = /d1 # Partial debug (line numbers only) +CC_CFG += /i=..\watcom\H # Normal Watcom include directory. +CC_CFG += /i=$(WWFLAT)\INCLUDE # Normal library include directory. +CC_CFG += /i=..\vq\include # Includes player (VQ) directory. +CC_CFG += /DDOS4G # Must be defined for Greenleaf +CC_CFG += /5s # Pentium optimized stack calling conventions. +CC_CFG += /DGF_WATCOM_S # Must be defined for Greenleaf with /3s +CC_CFG += /bt=DOS +!endif + +CC_CFG += /i=..\gcl510\H # Includes Greenleaf headers. +CC_CFG += /of+ # Generate traceable stack frames. +CC_CFG += /zp1 # Pack structures on byte boundary. +CC_CFG += /s # Remove stack check calls. +CC_CFG += /j # char is now signed. +CC_CFG += /fh=$(WWOBJ)\conquer.pch # Use precompiled headers. +CC_CFG += /fhq +CC_CFG += /we # Treat all warnings as errors. +CC_CFG += /w8 # Most warnings enabled. +CC_CFG += /ri # char and shorts are returned as int. +CC_CFG += /zq # Operate quietly. + +ASM_CFG = /i$(WWFLAT)\INCLUDE # Include directory. +ASM_CFG += /zd # Debugging information line numbers. +ASM_CFG += /t # Quiet operation. +ASM_CFG += /m # Allow multiple passes. +ASM_CFG += /w+ # Enable maximum warnings. +ASM_CFG += /jJUMPS # Enable jump optimizations. +ASM_CFG += /ml # Case sensitivity on code. + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: .AUTODEPEND + echo Compiling $< + *$(CC) $(CC_CFG) -fo$(WWOBJ)\$^. $< + +.cpp.obj: .AUTODEPEND + echo Compiling $< + *$(CC) $(CC_CFG) -fo$(WWOBJ)\$^. $< + +.asm.obj: + echo Assembling $< + utils\tasm $(ASM_CFG) $<, $(WWOBJ)\$^. + +#--------------------------------------------------------------------------- +# Object modules. +#--------------------------------------------------------------------------- +OBJECTS = & + AADATA.OBJ & + ABSTRACT.OBJ & + ADATA.OBJ & + AIRCRAFT.OBJ & + ANIM.OBJ & + AUDIO.OBJ & + BAR.OBJ & + BASE.OBJ & + BBDATA.OBJ & + BDATA.OBJ & + BUILDING.OBJ & + BULLET.OBJ & + CARGO.OBJ & + CARRY.OBJ & + CCFILE.OBJ & + CCINI.OBJ & + CCPTR.OBJ & + CDATA.OBJ & + CELL.OBJ & + CHECKBOX.OBJ & + CHEKLIST.OBJ & + COLRLIST.OBJ & + COMBAT.OBJ & + COMBUF.OBJ & + CONNECT.OBJ & + CONQUER.OBJ & + CONST.OBJ & + CONTROL.OBJ & + COORD.OBJ & + CRATE.OBJ & + CREDITS.OBJ & + CREW.OBJ & + DEBUG.OBJ & + DIAL8.OBJ & + DIALOG.OBJ & + DISPLAY.OBJ & + DOOR.OBJ & + DRIVE.OBJ & + DROP.OBJ & + DYNAVEC.OBJ & + EDIT.OBJ & + EGOS.OBJ & + ENDING.OBJ & + EVENT.OBJ & + EXPAND.OBJ & + FACE.OBJ & + FACING.OBJ & + FACTORY.OBJ & + FINDPATH.OBJ & + FLASHER.OBJ & + FLY.OBJ & + FOOT.OBJ & + FUSE.OBJ & + GADGET.OBJ & + GAMEDLG.OBJ & + GAUGE.OBJ & + GLOBALS.OBJ & + GOPTIONS.OBJ & + GSCREEN.OBJ & + HDATA.OBJ & + HEAP.OBJ & + HELP.OBJ & + HOUSE.OBJ & + IDATA.OBJ & + INFANTRY.OBJ & + INI.OBJ & + INIT.OBJ & + INTRO.OBJ & + IOMAP.OBJ & + IOOBJ.OBJ & + IPX.OBJ & + IPXADDR.OBJ & + IPXCONN.OBJ & + IPXGCONN.OBJ & + IPXMGR.OBJ & + IPXPROT.OBJ & + JSHELL.OBJ & + LAYER.OBJ & + LINK.OBJ & + LIST.OBJ & + LOADDLG.OBJ & + LOGIC.OBJ & + MAP.OBJ & + MAPEDDLG.OBJ & + MAPEDIT.OBJ & + MAPEDPLC.OBJ & + MAPEDTM.OBJ & + MAPSEL.OBJ & + MENUS.OBJ & + MISSION.OBJ & + MOUSE.OBJ & + MPLAYER.OBJ & + MSGBOX.OBJ & + MSGLIST.OBJ & + NETDLG.OBJ & + NULLCONN.OBJ & + NULLDLG.OBJ & + NULLMGR.OBJ & + OBJECT.OBJ & + ODATA.OBJ & + OPTIONS.OBJ & + OVERLAY.OBJ & + POWER.OBJ & + PROFILE.OBJ & + QUEUE.OBJ & + RADAR.OBJ & + RADIO.OBJ & + REINF.OBJ & + RULES.OBJ & + SAVELOAD.OBJ & + SCENARIO.OBJ & + SCORE.OBJ & + SCROLL.OBJ & + SDATA.OBJ & + SESSION.OBJ & + SHAPEBTN.OBJ & + SIDEBAR.OBJ & + SLIDER.OBJ & + SMUDGE.OBJ & + SOUNDDLG.OBJ & + SPECIAL.OBJ & + STARTUP.OBJ & + STATBTN.OBJ & + SUPER.OBJ & + TAB.OBJ & + TACTION.OBJ & + TARGET.OBJ & + TDATA.OBJ & + TEAM.OBJ & + TEAMTYPE.OBJ & + TECHNO.OBJ & + TEMPLATE.OBJ & + TERRAIN.OBJ & + TEVENT.OBJ & + TEXTBTN.OBJ & + THEME.OBJ & + TOGGLE.OBJ & + TRACKER.OBJ & + TRIGGER.OBJ & + TRIGTYPE.OBJ & + TXTLABEL.OBJ & + UDATA.OBJ & + UNIT.OBJ & + UTRACKER.OBJ & + VDATA.OBJ & + VECTOR.OBJ & + VERSION.OBJ & + VESSEL.OBJ & + VISUDLG.OBJ & + VORTEX.OBJ & + WARHEAD.OBJ & + WEAPON.OBJ & + MCIMOVIE.OBJ & + MCI.OBJ & + MPGSET.OBJ & + ICONLIST.OBJ & + WOL_MAIN.OBJ & + WOL_CHAT.OBJ & + RAWOLAPI.OBJ & + WOLAPIOB.OBJ & + W95TRACE.OBJ & + COMINIT.OBJ & + WOL_LOGN.OBJ & + WOLEDIT.OBJ & + PASSEDIT.OBJ & + SEDITDLG.OBJ & + DIBFILE.OBJ & + DIBUTIL.OBJ & + WOL_GSUP.OBJ & + UDPADDR.OBJ & + WSPROTO.OBJ & + WSPUDP.OBJ & + WSPIPX.OBJ & + _WSPROTO.OBJ & + TOOLTIP.OBJ & + WOL_OPT.OBJ & + WOL_CGAM.OBJ & + BIGCHECK.OBJ & + WOL_DNLD.OBJ & + WOLSTRNG.OBJ + + +# Files that are candidates for library submission, +# since they depend on the westwood library to a great +# degree +LIBFILES = & + ROTBMP.OBJ & + SPRITE.OBJ & + +# Self contained technology modules +TECHFILES = & + LZO1X_C.OBJ & + LZO1X_D.OBJ & + LZOPIPE.OBJ & + LZOSTRAW.OBJ & + LZW.OBJ & + LZWPIPE.OBJ & + LZWSTRAW.OBJ & + BUFF.OBJ & + BENCH.OBJ & + RECT.OBJ & + MPU.OBJ & + READLINE.OBJ & + PKPIPE.OBJ & + PKSTRAW.OBJ & + XSTRAW.OBJ & + XPIPE.OBJ & + RNDSTRAW.OBJ & + PK.OBJ & + BLWSTRAW.OBJ & + STRAW.OBJ & + B64STRAW.OBJ & + LCWSTRAW.OBJ & + CRCSTRAW.OBJ & + SHASTRAW.OBJ & + RAMFILE.OBJ & + INI.OBJ & + LCWPIPE.OBJ & + LCWCOMP.OBJ & + LCW.OBJ & + CRCPIPE.OBJ & + SHAPIPE.OBJ & + PIPE.OBJ & + BLOWPIPE.OBJ & + B64PIPE.OBJ & + BASE64.OBJ & + FIXED.OBJ & + MIXFILE.OBJ & + CDFILE.OBJ & + BFIOFILE.OBJ & + RAWFILE.OBJ & + MP.OBJ & + INT.OBJ & + MONOC.OBJ & + RANDOM.OBJ & + RGB.OBJ & + HSV.OBJ & + PALETTE.OBJ & + BLOWFISH.OBJ & + SHA.OBJ & + CRC.OBJ & + SENDFILE.OBJ + +!ifdef WIN32 +OBJECTS += 2KEYFBUF.OBJ & + CPUID.OBJ & + GETCPU.OBJ & + INTERPAL.OBJ & + WINASM.OBJ & + WINSTUB.OBJ & + 2TXTPRNT.OBJ & + WRITEPCX.OBJ & + IPX95.OBJ & + 2KEYFRAM.OBJ & + TCPIP.OBJ & + INTERNET.OBJ & + DDE.OBJ & + CCDDE.OBJ & + STATS.OBJ & + PACKET.OBJ & + KEY.OBJ & + FIELD.OBJ + +!else +OBJECTS += KEYFBUFF.OBJ & + TXTPRNT.OBJ & + KEYFRAME.OBJ +!endif + +!ifdef WIN32 +PROJ_LIBS = & + win32lib.lib + +!else +PROJ_LIBS = & + wwflat32.lib +!endif + +VQ_LIBS = & + vqa32wp.lib & + vqm32wp.lib + +GCL_LIBS = & + gclfr3s.lib + +MEMCHECK_LIBS = & + MCA3S.LIB + +############################################################################ +# Pre-compilation process. Move old files to backup directory. +.BEFORE + -if exist *.bak move *.bak bak + -if exist $(WWOBJ)\*.pch del $(WWOBJ)\*.pch + +# After make has completed. +.AFTER + +# If there is an abnormal termination in make process (e.g., error in compile). +.ERROR + + +############################################################################# +# Default target +!ifdef WIN32 +all: ra95.exe +!else +all: game.dat +!endif + + +############################################################################# +# Builds the JSHELL.LIB file. +$(WWOBJ)\jshell.lib: $(LIBFILES) $(WWOBJ)\jshell.lnk + $(LIB) -c $^@ @$(WWOBJ)\jshell.lnk + +$(WWOBJ)\jshell.lnk: makefile + %create $^@ + for %index in ($(LIBFILES)) do %append $^@ -+ $(WWOBJ)\%index + +############################################################################# +# Builds the TECH.LIB file. +$(WWOBJ)\tech.lib: $(TECHFILES) $(WWOBJ)\tech.lnk + $(LIB) -c $^@ @$(WWOBJ)\tech.lnk + +$(WWOBJ)\tech.lnk: makefile + %create $^@ + for %index in ($(TECHFILES)) do %append $^@ -+ $(WWOBJ)\%index + +############################################################################# +# Builds the stub replacement program. +CWSTUB.OBJ: CWSTUB.C + *wcc /i=..\watcom\h /dQUIET /dVMM /ms /zQ -fo$(WWOBJ)\$^. $< + +CWSTUB.EXE: CWSTUB.OBJ + *watcom\binw\wlink system dos file $(WWOBJ)\cwstub.obj name cwstub.exe option quiet library \wat\lib386\dos\clibs.lib, \wat\lib386\math87s.lib, \wat\lib386\dos\emu87.lib + + +############################################################################# +# Build the EXE +game.dat: $(WWOBJ)\tech.lib $(WWOBJ)\jshell.lib $(OBJECTS) $(LINKFILE) $(TECHFILE) + -Echo "dos.exe" linking phase. + ..\watcom\binw\wlink name ..\run\dos.exe @$(LINKFILE) + -Echo Binding phase. + -..\watcom\binw\wstrip -n -q ..\run\dos.exe ..\run\temp.tmp + -..\watcom\4gwbind ..\watcom\4gwpro.exe ..\run\temp.tmp ..\run\game.dat -vmon +# -copy ..\run\game.dat C:\westwood\redalert +# -copy ..\run\game.dat C:\westwood\ra +# -copy ..\run\dos.exe C:\westwood\redalert +# -copy ..\run\game.dat ..\cd\install + -Echo "game.dat" executable completed. + +ra95.exe: $(WWOBJ)\tech.lib $(WWOBJ)\jshell.lib $(OBJECTS) $(LINKFILE) mpgdll.lib + Echo "ra95.exe" linking phase. [$(LANGUAGE)] +# nwlink name ..\run\$@ @$(LINKFILE) +# ajw - Steve Tall replaced wlink with nwlink. Watcom fix allows more debug symbols in exe, so allows debugging in WD. +# ..\watcom\binw\wlink name ..\run\$@ @$(LINKFILE) + linker\nwlink name ..\run\$@ @$(LINKFILE) +# ..\watcom\binnt\WRC cc_icon ..\run\$@ +# copy ..\run\ra95.exe C:\westwood\redalert +# copy ..\run\ra95.exe C:\westwood\ra + Echo "ra95.exe" executable completed. [$(LANGUAGE)] + + +############################################################################# +# This creates the linker command file for the DOS version. +conquer.lnk : makefile + %create $^@ + %append $^@ system dos4g + %append $^@ option stack=128k + %append $^@ option redefsok + %append $^@ option quiet + %append $^@ option map + %append $^@ option eliminate + %append $^@ option caseexact + %append $^@ debug all + %append $^@ library $(WWOBJ)\jshell.lib + %append $^@ library $(WWOBJ)\tech.lib + for %index in ($(OBJECTS)) do %append $^@ file $(WWOBJ)\%index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(WWFLAT)\lib\%index + for %index in ($(VQ_LIBS)) do %append $^@ library ..\vq\lib\%index + for %index in ($(GCL_LIBS)) do %append $^@ library ..\gcl510\w10\%index +# %append $^@ debug watcom all + + +############################################################################# +# This creates the linker command file for the Windows 95 version. +win95.lnk : makefile + %create $^@ + %append $^@ system win95 + %append $^@ option redefsok + %append $^@ option quiet + %append $^@ option map + %append $^@ option eliminate + %append $^@ option caseexact + %append $^@ option stack=128k +# %append $^@ debug watcom all +# %append $^@ debug all + for %index in ($(OBJECTS)) do %append $^@ file $(WWOBJ)\%index + %append $^@ library $(WWOBJ)\jshell.lib + %append $^@ library $(WWOBJ)\tech.lib + %append $^@ library $(WWFLAT)\lib\win32lib.lib + %append $^@ library ..\winvq\lib\vqa32wp.lib + %append $^@ library ..\winvq\lib\vqm32wp.lib + %append $^@ library ipx\wwipx32.lib + %append $^@ library ..\dxsdk\lib\dxguid.lib + %append $^@ library ..\dxsdk\lib\ddraw.lib + %append $^@ library ..\dxsdk\lib\dsound.lib + %append $^@ library $(WWFLAT)\lib\keyboard.lib + %append $^@ library mpgdll.lib + %append $^@ library ..\dxmedia\lib\amstrmid.lib + %append $^@ library ..\dxmedia\lib\strmbasd.lib +# %append $^@ library ..\watcom\lib386\nt\uuid.lib + %append $^@ library uuid.lib + +############################################################# +# Update source and art to network. +update: pre .SYMBOLIC + -copy i:\cd1\*.* f:\projects\c&c0\cd\win95\cd1 /s /u + -copy i:\cd2\*.* f:\projects\c&c0\cd\win95\cd2 /s /u +# -copy i:\cd1\*.* f:\projects\c&c0\cd\dos\cd1 /s /u +# -copy i:\cd2\*.* f:\projects\c&c0\cd\dos\cd2 /s /u + -copy ..\art\ingame\*.* f:\projects\c&c0\art\ingame /u /v /s + -copy ..\audio\sfx\*.* f:\projects\c&c0\audio\ingame\sfx /u /v /s + -copy *.* f:\projects\c&c0\code /v /s /u + -copy ..\wwflat32\*.* f:\projects\c&c0\wwflat32 /v /s /u + -copy ..\win32lib\*.* f:\projects\c&c0\win32lib /v /s /u + -copy ..\vq\*.* f:\projects\c&c0\vq /v /s /u + -copy ..\winvq\*.* f:\projects\c&c0\winvq /v /s /u + +pre: .SYMBOLIC + -copy f:\projects\c&c0\editor\english\*.exe i:\cd1\install /u + -copy f:\projects\c&c0\editor\english\*.exe i:\cd2\install /u + -copy f:\projects\c&c0\editor\english\edit.dat i:\cd1\install /u + -copy f:\projects\c&c0\editor\english\edit.dat i:\cd2\install /u + -copy ..\run\rules.ini ..\maps /u + -copy ..\run\rules.ini f:\projects\c&c0\maps /u + -watcom\binw\wstrip -n -q ..\run\ra95.exe ..\run\ra95.exe + -copy ..\run\ra95.exe i:\cd1\install /u + -copy ..\run\ra95.exe i:\cd2\install /u + -copy ..\run\game.dat i:\cd1\install /u + -copy ..\run\game.dat i:\cd2\install /u + packlist SETUP.LST + -copy setup.pkg i:\cd1\setup95 /u + -copy setup.pkg i:\cd2\setup95 /u + -mkdir f:\projects\c&c0\playtest\%_DATE + -copy ..\run\ra95.exe f:\projects\c&c0\playtest\%_DATE /u /v + -copy ..\run\game.dat f:\projects\c&c0\playtest\%_DATE /u /v + -copy *.map f:\projects\c&c0\playtest\%_DATE /u /v + -copy *.cpp *.h f:\projects\c&c0\playtest\%_DATE /u /v + + +############################################################################# +# Explicit rules to build the master zip files (used by Codewrite merge). +BILL_R.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\bill_r.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\bill_r.zip *.* eng\conquer.txt + +MARIA_L.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\maria_l.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\maria_l.zip *.* eng\conquer.txt + +BARRY_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\barry_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\barry_g.zip *.* eng\conquer.txt + +PHIL_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\phil_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\phil_g.zip *.* eng\conquer.txt + +DAVID_D.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\david_d.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\david_d.zip *.* eng\conquer.txt + +BILL_P.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\bill_p.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\bill_p.zip *.* eng\conquer.txt + +STEVE_T.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\steve_t.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\steve_t.zip *.* eng\conquer.txt + +VICTOR_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c0\victor_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c0\victor_g.zip *.* eng\conquer.txt + +# Special "mega-zip" process. +JOE_B.ZIP: pkzip.dat .SYMBOLIC + -pkzip -rp -u -xcps\*.* -x@pkzip.dat f:\projects\c&c0\joe_b.zip + + +############################################################################# +# Rebuilds the master zip control file. This is used by the zip process. +PKZIP.DAT: makefile .SYMBOLIC + %create $^@ + %append $^@ *.000 + %append $^@ *.@@@ + %append $^@ *.bak + %append $^@ *.bat + %append $^@ *.cfg + %append $^@ *.csm + %append $^@ *.dat + %append $^@ *.def + %append $^@ *.doc + %append $^@ *.dsw + %append $^@ *.err + %append $^@ *.ewp + %append $^@ *.ext + %append $^@ *.i + %append $^@ *.ide + %append $^@ *.lnk + %append $^@ *.log + %append $^@ *.lst + %append $^@ *.mac + %append $^@ *.map + %append $^@ *.mbt + %append $^@ *.mk + %append $^@ *.mk1 + %append $^@ *.mrt + %append $^@ *.obj + %append $^@ *.obr + %append $^@ *.out + %append $^@ *.pch + %append $^@ *.pfs + %append $^@ *.pgp + %append $^@ *.pif + %append $^@ *.pjt + %append $^@ *.prf + %append $^@ *.pro + %append $^@ *.ptg + %append $^@ *.rc + %append $^@ *.rep + %append $^@ *.rpt + %append $^@ *.rst + %append $^@ *.sym + %append $^@ *.tag + %append $^@ *.td + %append $^@ *.td + %append $^@ *.tgt + %append $^@ *.tmp + %append $^@ *.tr + %append $^@ *.tr + %append $^@ *.txt + %append $^@ *.vec + %append $^@ *.wpj + %append $^@ *.zip + %append $^@ *.~* + %append $^@ an_prefs + %append $^@ state.rst + + +#-------------------------------------------------------------------------- +# The IPX assembly object files are created in a special way: +# IPXREAL is the real-mode code that gets stuffed into memory by protected- +# mode code. It's assembled, then converted into a big header file by +# the 'EBN' utility. +# IPXPROT is the protected-mode code that includes IPXREAL.IBN, and +# provides routines to let C++ read the code's address & size. +#-------------------------------------------------------------------------- +$(WWOBJ)\ipxreal.ibn: $(WWOBJ)\ipxreal.obj + %create $^*.rsp + %append $^*.rsp $(WWOBJ)\$^&.obj + %append $^*.rsp $(WWOBJ)\$^&.exe + %append $^*.rsp $(WWOBJ)\$^&.map + utils\tlink @$^*.rsp + utils\tdstrip $(WWOBJ)\ipxreal.exe + utils\ebn $(WWOBJ)\ipxreal.exe + +$(WWOBJ)\ipxreal.obj: ipxreal.asm + utils\tasm /zn /la /ml /m2 ipxreal.asm, $(WWOBJ)\ipxreal.obj + +ipxprot.obj: $(WWOBJ)\ipxreal.ibn ipxprot.asm + utils\tasm $(ASM_CFG) ipxprot.asm, $(WWOBJ)\ipxprot.obj +# copy $(WWOBJ)\ipxprot.obj d:obj /U + + +ipx.obj: ipx.cpp function.h + echo Compiling $[. + *$(CC) $(CC_CFG) -zz -fo$(WWOBJ)\$^. $[*.cpp + +ipxmgr.obj: ipxmgr.cpp function.h + echo Compiling $[. + *$(CC) $(CC_CFG) -zz -fo$(WWOBJ)\$^. $[*.cpp + +ipxconn.obj: ipxconn.cpp function.h + echo Compiling $[. + *$(CC) $(CC_CFG) -zz -fo$(WWOBJ)\$^. $[*.cpp + +ipx95.obj: ipx95.cpp function.h + echo Compiling $[. + *$(CC) $(CC_CFG) -zz -fo$(WWOBJ)\$^. $[*.cpp + +winstub.obj: winstub.cpp ipx95.h tcpip.h function.h + echo Compiling $[. + *$(CC) $(CC_CFG) -zz -fo$(WWOBJ)\$^. $[*.cpp + + + +#**************************** End of makefile ****************************** diff --git a/CODE/MAKE_ALL.BAT b/CODE/MAKE_ALL.BAT new file mode 100644 index 0000000..72949aa --- /dev/null +++ b/CODE/MAKE_ALL.BAT @@ -0,0 +1,4 @@ +SET WATCOM=C:\rawolapi\SOURCE\WATCOM +WMAKE WIN32=1 /a +WMAKE WIN32=1 GERMAN=1 /a +WMAKE WIN32=1 FRENCH=1 /a \ No newline at end of file diff --git a/CODE/MAP.CPP b/CODE/MAP.CPP new file mode 100644 index 0000000..db50069 --- /dev/null +++ b/CODE/MAP.CPP @@ -0,0 +1,2135 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAP.CPP 3 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * MapClass::Detach -- Remove specified object from map references. * + * MapClass::In_Radar -- Is specified cell in the radar map? * + * MapClass::Init -- clears all cells * + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * MapClass::Logic -- Handles map related logic functions. * + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * MapClass::One_Time -- Performs special one time initializations for the map. * + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * MapClass::Pick_Up -- Removes specified object from the map. * + * MapClass::Place_Down -- Places the specified object onto the map. * + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * MapClass::Set_Map_Dimensions -- Initialize the map. * + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * MapClass::Validate -- validates every cell on the map * + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W +int const MapClass::RadiusOffset[] = { + /* 0 */ 0, + /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, + /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, + /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, + /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, + /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, + /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, + /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, + /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, + /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, + /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, + (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, +}; + +int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; + + +CellClass * BlubCell; + +/*********************************************************************************************** + * MapClass::One_Time -- Performs special one time initializations for the map. * + * * + * This routine is used by the game initialization function in order to perform any one * + * time initializations required for the map. This includes allocation of the map and * + * setting up its default dimensions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine MUST be called once and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/01/1994 BR : Added CellTriggers initialization * + *=============================================================================================*/ +void MapClass::One_Time(void) +{ + GScreenClass::One_Time(); + + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); +} + + +/*********************************************************************************************** + * MapClass::Init_Clear -- clears the map & buffers to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Clear(void) +{ + GScreenClass::Init_Clear(); + Init_Cells(); + TiberiumScan = 0; + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + Crates[index].Init(); + } +} + + +/*********************************************************************************************** + * MapClass::Alloc_Cells -- allocates the cell array * + * * + * This routine should be called at One_Time, and after loading the Map object from a save * + * game, but prior to loading the cell objects. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Alloc_Cells(void) +{ + /* + ** Assume that whatever the contents of the VectorClass are is garbage + ** (it may have been loaded from a save-game file), so zero it out first. + */ + new (&Array) VectorClass; + Array.Resize(Size); +} + + +/*********************************************************************************************** + * MapClass::Free_Cells -- frees the cell array * + * * + * This routine is used by the Load_Game routine to free the map's cell array before loading * + * the map object from disk; the array is then re-allocated & cleared before the cell objects * + * are loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Free_Cells(void) +{ + Array.Clear(); +} + + +/*********************************************************************************************** + * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * + * * + * This routine is used by Init_Clear to set the cells to a known state; it's also used by * + * the Load_Game routine to init all cells before loading a set of cells from disk, so it * + * needs to be called separately from the other Init_xxx() routines. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Cells(void) +{ + TotalValue = 0; + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + new (&Array[index]) CellClass; + } +} + + +/*********************************************************************************************** + * MapClass::Set_Map_Dimensions -- Set map dimensions. * + * * + * This routine is used to set the legal limits and position of the * + * map as it relates to the overall map array. Typically, this is * + * called by the scenario loading code. * + * * + * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * + * of the map. * + * * + * w,h -- The width and height of the legal map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + MapCellX = x; + MapCellY = y; + MapCellWidth = w; + MapCellHeight = h; +} + + +/*********************************************************************************************** + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * * + * This routine is used to reveal the cells around a specific location. * + * Typically, as a unit moves or is deployed, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the sighting originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * incremental-- Is this an incremental sighting. In other * + * words, has this function been called before where * + * the center coordinate is no more than one cell * + * distant from the last time? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1992 JLB : Created. * + * 03/08/1994 JLB : Updated to use sight table and incremental flag. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MapClass::Sight_From(CELL cell, int sightrange, HouseClass * house, bool incremental) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > 10) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + if (incremental) { + if (sightrange > 2) { + ptr += RadiusCount[sightrange-3]; + count -= RadiusCount[sightrange-3]; + } + } + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Map the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + if (!(*this)[newcell].IsMapped) { + Map.Map_Cell(newcell, house); + } + } +} + + +/*********************************************************************************************** + * MapClass::Shroud_From -- cloak a radius of cells * + * * + * This routine is used to shroud the cells around a specific location. * + * Typically, as a gap generator refreshes (when Encroach_Shadow() is called) this routine's* + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the shrouding originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::Shroud_From(CELL cell, int sightrange) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Shroud the cell. + */ + Map.Shroud_Cell(newcell); + } +} + + +/*********************************************************************************************** + * MapClass::Jam_From -- Mark as jammed the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::Jam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.Jam_Cell(newcell, house/*KO, false*/); + } + +// PlayerPtr->IsToLook = true; + if (!house->IsPlayerControl) { + Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W); + } + +#ifdef OBSOLETE + /* + ** The objects on the map need to perform a manual look operation if they happen + ** to have their sight range overlap the gap radius. + */ + if (!house->IsPlayerControl) { +// if (house != PlayerPtr) { + + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Map.Layer[LAYER_GROUND][index]; + if (object && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->IsDiscoveredByPlayer && + (tech->Distance(As_Target(cell)) / CELL_LEPTON_W) <= tech->Techno_Type_Class()->SightRange + Rule.GapShroudRadius && + (tech->House->IsPlayerControl || + (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)))) { + + object->Look(); + } + } + } + } +#endif + +#ifdef OBSOLETE + /* + ** Here all the player's vehicles will perform a look if they're within + ** the shadow. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * unit = Infantry.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * unit = Vessels.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + } +#endif + +} + + +/*********************************************************************************************** + * MapClass::UnJam_From -- Remove jamming on the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::UnJam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.UnJam_Cell(newcell, house); + } +} + + +/*********************************************************************************************** + * MapClass::In_Radar -- Is specified cell in the radar map? * + * * + * This determines if the specified cell can be within the navigable * + * bounds of the map. Technically, this means, any cell that can be * + * scanned by radar. If a cell returns false from this function, then * + * the player could never move to or pass over this cell. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: bool; Is this cell possible to be displayed on radar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/01/1994 JLB : Speeded up. * + *=============================================================================================*/ +bool MapClass::In_Radar(CELL cell) const +{ + /* + ** If the cell value is WAY out of range, then it obviously can't be part of the game + ** playfield. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) return(false); + + /* + ** If the cell is off the left or right edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_X(cell) - MapCellX) >= (unsigned)MapCellWidth) return(false); + + /* + ** If the cell is off the top or bottom edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_Y(cell) - MapCellY) >= (unsigned)MapCellHeight) return(false); + + return(true); +} + + +/*********************************************************************************************** + * MapClass::Place_Down -- Places the specified object onto the map. * + * * + * This routine is used to place an object onto the map. It updates the "occupier" of the * + * cells that this object covers. The cells are determined from the Occupy_List function * + * provided by the object. Only one cell can have an occupier and this routine is the only * + * place that sets this condition. * + * * + * INPUT: cell -- The cell to base object occupation around. * + * * + * object -- The object to place onto the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Place_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Down(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Pick_Up -- Removes specified object from the map. * + * * + * The object specified is removed from the map by this routine. This will remove the * + * occupation flag for all the cells that the object covers. The cells that are covered * + * are determined from the Occupy_List function. * + * * + * INPUT: cell -- The cell that the object is centered about. * + * * + * object -- Pointer to the object that will be removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Pick_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Up(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * * + * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * * + * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * * + * This routine will clean up anything necessary with the presumption that the map has * + * been freshly created. Such things to clean up include various tiberium concentrations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the total credit value of the tiberium on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/13/1995 JLB : Returns total tiberium worth. * + * 02/15/1995 JLB : Optimal scan. * + *=============================================================================================*/ +long MapClass::Overpass(void) +{ + long value = 0; + + /* + ** Smooth out Tiberium. Cells that are not surrounded by other tiberium + ** will be reduced in density. + */ + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = (MapCellY+y) * MAP_CELL_W + (MapCellX+x); + value += (*this)[cell].Tiberium_Adjust(true); + (*this)[cell].Recalc_Attributes(); + } + } + return(value); +} + + +/*********************************************************************************************** + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * * + * This stores the template data from the map to the output pipe specified. The template * + * data consists of the template type number and template icon number for every cell on * + * the map. The output is organized in such a way so as to get maximum compression. * + * * + * INPUT: pipe -- Reference to the output pipe that will receive the map template data. * + * * + * OUTPUT: Returns with the total number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Write_Binary(Pipe & pipe) +{ + int total = 0; + + LCWPipe comp(LCWPipe::COMPRESS); + comp.Put_To(&pipe); + + CellClass * cellptr = &Array[0]; + for (int i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + + cellptr = &Array[0]; + for (i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr++; + } + + return(total); +} + + +/*********************************************************************************************** + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * * + * This routine will retrieve the map template data from the straw specified. * + * * + * INPUT: straw -- Reference to the straw that will supply the map template data. * + * * + * OUTPUT: bool; Was the template data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Read_Binary(Straw & straw) +{ + LCWStraw decomp(LCWStraw::DECOMPRESS); + decomp.Get_From(&straw); + + CELL cell; + CellClass * cellptr; + switch (NewINIFormat) { + default: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + + case 0: + case 1: + case 2: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + } + return(true); +} + + +/*********************************************************************************************** + * MapClass::Logic -- Handles map related logic functions. * + * * + * Manages tiberium growth and spread. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + * 07/09/1995 JLB : Handles two directional scan. * + * 08/01/1995 JLB : Gives stronger weight to blossom trees. * + *=============================================================================================*/ +void MapClass::Logic(void) +{ + /* + ** Crate regeneration is handled here. + */ + if (Session.Type != GAME_NORMAL && Session.Options.Goodies) { + + /* + ** Find any crate that has expired and then regenerate it at a new + ** spot. + */ + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Expired()) { + Crates[index].Remove_It(); + Place_Random_Crate(); + } + } + } + + /* + ** Bail early if there is no allowed growth or spread of Tiberium. + */ + if (!Rule.IsTGrowth && !Rule.IsTSpread) return; + + /* + ** Scan another block of the map in order to accumulate the potential + ** Tiberium cells that can grow or spread. + */ + int subcount = MAP_CELL_TOTAL / (Rule.GrowthRate * TICKS_PER_MINUTE); + subcount = max(subcount, 1); + for (int index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { + CELL cell = index; + if (In_Radar(cell)) { + CellClass * ptr = &(*this)[cell]; + + /* + ** Tiberium cells can grow. + */ + if (ptr->Can_Tiberium_Grow()) { + + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumGrowthExcess) <= TiberiumGrowthCount) { + if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { + TiberiumGrowth[TiberiumGrowthCount++] = cell; + } else { + TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; + } + } + TiberiumGrowthExcess++; + } + + /* + ** Heavy Tiberium growth can spread. + */ + if (ptr->Can_Tiberium_Spread()) { + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumSpreadExcess) <= TiberiumSpreadCount) { + if (TiberiumSpreadCount < ARRAY_SIZE(TiberiumSpread)) { + TiberiumSpread[TiberiumSpreadCount++] = cell; + } else { + TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; + } + } + TiberiumSpreadExcess++; + } + } + + subcount--; + if (subcount == 0) break; + } + TiberiumScan = index; + + /* + ** When the entire map has been processed, proceed with tiberium (ore) growth + ** and spread action. + */ + if (TiberiumScan >= MAP_CELL_TOTAL) { + TiberiumScan = 0; + + /* + ** Growth logic. + */ + if (TiberiumGrowthCount) { + for (int i = 0; i < TiberiumGrowthCount; i++) { + CELL cell = TiberiumGrowth[i]; + CellClass * newcell = &(*this)[cell]; + newcell->Grow_Tiberium(); + } + } + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + + /* + ** Spread logic. + */ + if (TiberiumSpreadCount) { + for (int i = 0; i < TiberiumSpreadCount; i++) { + Map[TiberiumSpread[i]].Spread_Tiberium(); + } + } + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * * + * Use this routine to determine what region a particular cell lies in. * + * * + * INPUT: cell -- The cell number to examine. * + * * + * OUTPUT: Returns with the region that the specified cell occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 JLB : Created. * + *=============================================================================================*/ +int MapClass::Cell_Region(CELL cell) +{ + return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); +} + + +/*************************************************************************** + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * * + * INPUT: CELL cell - the cell number to check * + * HouseType house - the house to check * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1995 PWG : Created. * + *=========================================================================*/ +int MapClass::Cell_Threat(CELL cell, HousesType house) +{ + int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); + if (!threat && Map[cell].IsVisible) { + threat = 1; + } + return(threat); +} + + +/*********************************************************************************************** + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * * + * This routine will place a crate at a random location on the map. This routine will only * + * make a limited number of attempts to place and if unsuccessful, it will not place any. * + * * + * INPUT: none * + * * + * OUTPUT: Was a crate successfully placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Place_Random_Crate(void) +{ + /* + ** Find a crate index that is free for assignment. If there are + ** no free slots, then return with failure to place crate. + */ + int crateindex = 0; + for (crateindex = 0; crateindex < ARRAY_SIZE(Crates); crateindex++) { + if (!Crates[crateindex].Is_Valid()) break; + } + if (crateindex == ARRAY_SIZE(Crates)) { + return(false); + } + + /* + ** Give a good effort to scan for and place a crate down on the map. + */ + for (int index = 0; index < 1000; index++) { + CELL cell = Map.Pick_Random_Location(); + + if (Crates[crateindex].Create_Crate(cell)) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * * + * This will examine the cell and remove any crates there. * + * * + * INPUT: cell -- The cell to examine for crates and remove from. * + * * + * OUTPUT: bool; Was a crate found at the location specified and was it removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Remove_Crate(CELL cell) +{ + if (Session.Type != GAME_NORMAL) { + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Here(cell)) { + return(Crates[index].Remove_It()); + } + } + } + +// if (Session.Type == GAME_NORMAL) { + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsCrate) { + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + return(true); + } +// } else { +// for (int index = 0; index < ARRAY_SIZE(Crates); index++) { +// if (Crates[index].Is_Here(cell)) { +// return(Crates[index].Remove_It()); +// } +// } +// } + return(false); +} + + +/*************************************************************************** + * MapClass::Validate -- validates every cell on the map * + * * + * This is a debugging routine, designed to detect memory trashers that * + * alter the map. This routine is slow, but thorough. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = map is OK, false = an error was found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +int MapClass::Validate(void) +{ + CELL cell; + TemplateType ttype; + unsigned char ticon; + TemplateTypeClass const *tclass; + unsigned char map[13*8]; + OverlayType overlay; + SmudgeType smudge; + ObjectClass * obj; + LandType land; + int i; + +BlubCell = &Array[797]; + +if (BlubCell->Overlapper[1]) { + obj = BlubCell->Overlapper[1]; + if (obj) { + if (obj->IsInLimbo) + obj = obj; + } +} + + /* + ** Check every cell on the map, even those that aren't displayed, + ** in the hopes of detecting a memory trasher. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + /* + ** Validate Template & Icon data + */ + ttype = (*this)[cell].TType; + ticon = (*this)[cell].TIcon; + if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) + return(false); + + /* + ** To validate the icon value, we have to get a copy of the template's + ** "icon map"; this map will have 0xff's in spots where there is no + ** icon. If the icon value is out of range or points to an invalid spot, + ** return an error. + */ + if (ttype != TEMPLATE_NONE) { + tclass = &TemplateTypeClass::As_Reference(ttype); + ticon = (*this)[cell].TIcon; + Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, tclass->Width * tclass->Height); + if (ticon < 0 || ticon >= (tclass->Width * tclass->Height) || map[ticon]==0xff) { + return (false); + } + } + + /* + ** Validate Overlay + */ + overlay = (*this)[cell].Overlay; + if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) { + return(false); + } + + /* + ** Validate Smudge + */ + smudge = (*this)[cell].Smudge; + if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) { + return(false); + } + + /* + ** Validate LandType + */ + land = (*this)[cell].Land_Type(); + if (land < LAND_CLEAR || land >= LAND_COUNT) { + return(false); + } + + /* + ** Validate Occupier + */ + obj = (*this)[cell].Cell_Occupier(); + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + + /* + ** Validate Overlappers + */ + for (i = 0; i < ARRAY_SIZE((*this)[cell].CellClass::Overlapper); i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + } + } + + return (true); +} + + +/*********************************************************************************************** + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * * + * This routine is used by the mouse input processing code to find a clickable object * + * close to coordinate specified. This is for targeting as well as selection determination. * + * * + * INPUT: coord -- The coordinate to scan for close object from. * + * * + * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * + * * + * WARNINGS: There could be a cloaked object at the location, but it won't be considered * + * if it is not owned by the player. * + * * + * HISTORY: * + * 08/20/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * MapClass::Close_Object(COORDINATE coord) const +{ + ObjectClass * object = 0; + int distance = 0; + CELL cell = Coord_Cell(coord); + + /* + ** Scan through current and adjacent cells, looking for the + ** closest object (within reason) to the specified coordinate. + */ + static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; + for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + + /* + ** Examine the cell for close object. Make sure that the cell actually is a + ** legal one. + */ + CELL newcell = cell + _offsets[index]; + if (In_Radar(newcell)) { + + /* + ** Search through all objects that occupy this cell and then + ** find the closest object. Check against any previously found object + ** to ensure that it is actually closer. + */ + ObjectClass * o = Array[newcell].Cell_Occupier(); + while (o != NULL) { + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { + int d=-1; + if (o->What_Am_I() == RTTI_BUILDING) { + d = Distance(coord, Cell_Coord(newcell)); + if (d > 0x00B5) d = -1; + } else { + d = Distance(coord, o->Center_Coord()); + } + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = o; + } + } + o = o->Next; + } + } + } + + /* + ** Only return the object if it is within 1/4 cell distance from the specified + ** coordinate. + */ + if (object && distance > 0xB5) { + object = 0; + } + return(object); +} + + +/*********************************************************************************************** + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * * + * This routine will rescan the map and fill in the zone values for each of the cells. * + * All cells that are contiguous are given the same zone number. * + * * + * INPUT: method -- The method to recalculate the zones upon. If 1 then recalc non * + * crushable zone. If 2 then recalc crushable zone. If 3, then * + * recalc both zones. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming routine. Call it as infrequently as possible. It must * + * be called whenever something that would affect contiguousness occurs. Example: * + * when a bridge is built or destroyed. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Zone_Reset(int method) +{ + /* + ** Zero out all zones to a null state. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (method & MZONEF_NORMAL) { + Array[index].Zones[MZONE_NORMAL] = 0; + } + if (method & MZONEF_CRUSHER) { + Array[index].Zones[MZONE_CRUSHER] = 0; + } + if (method & MZONEF_DESTROYER) { + Array[index].Zones[MZONE_DESTROYER] = 0; + } + if (method & MZONEF_WATER) { + Array[index].Zones[MZONE_WATER] = 0; + } + } + + /* + ** Normal zone recalculation. + */ + if (method & MZONEF_NORMAL) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_NORMAL)) { + zone++; + } + } + } + + /* + ** Crushable wall recalculation. + */ + if (method & MZONEF_CRUSHER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_CRUSHER)) { + zone++; + } + } + } + + /* + ** Wall destroyer zone recalculation. + */ + if (method & MZONEF_DESTROYER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_DESTROYER)) { + zone++; + } + } + } + + /* + ** Water based zone recalcuation. + */ + if (method & MZONEF_WATER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_WATER)) { + zone++; + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * * + * This routine is used to fill a zone value into the map. The map is "flood filled" from * + * the cell specified. All adjacent (8 connected) and generally passable terrain cells are * + * given the zone number specified. This routine checks for legality before filling * + * occurs. The routine is safe to call even if the legality of the cell is unknown at the * + * time of the call. * + * * + * INPUT: cell -- The cell to begin filling from. * + * * + * zone -- The zone number to assign to all adjacent cells. * + * * + * check -- The zone type to check against. * + * * + * OUTPUT: Returns with the number of cells marked by this routine. * + * * + * WARNINGS: This routine is slow and recursive. Only use when necessary. * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 10/05/1996 JLB : Examines crushable walls. * + *=============================================================================================*/ +int MapClass::Zone_Span(CELL cell, int zone, MZoneType check) +{ + int filled = 0; + int xbegin = Cell_X(cell); + int xend = xbegin; + int y = Cell_Y(cell); + + /* + ** Perform some preliminary legality checks. If the cell specified + ** is illegal, then no further processing is necessary. + */ + if (y < MapCellY || y >= MapCellY+MapCellHeight || xbegin < MapCellX || xbegin >= MapCellX+MapCellWidth) { + return(0); + } + + /* + ** Find the full extent of the current span by first scanning leftward + ** until a boundary is reached. + */ + for (; xbegin >= MapCellX; xbegin--) { + CellClass * cellptr = &(*this)[XY_Cell(xbegin, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + + /* + ** Special short circuit code to bail from this entire routine if + ** it was called for a cell that is not a legal candidate for + ** zone marking. This eliminates redundant processing and allows this + ** routine to be called for illegal cells without causing an error. + */ + if (xbegin == Cell_X(cell)) return(0); + + /* + ** Otherwise break out of the left scan since a boundary was discovered. + */ + xbegin++; + break; + } + } + xbegin = max(xbegin, MapCellX); + + /* + ** Scan rightward until a boundary is reached. This will then define the + ** extent of the current span. + */ + for (; xend < MapCellX+MapCellWidth; xend++) { + CellClass * cellptr = &(*this)[XY_Cell(xend, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + xend--; + break; + } + } + xend = min(xend, MapCellX+MapCellWidth-1); + + /* + ** At this point we know the bounds of the current span. Fill in the zone values + ** for the entire span. + */ + for (int x = xbegin; x <= xend; x++) { + (*this)[XY_Cell(x, y)].Zones[check] = zone; + filled++; + } + + /* + ** Now scan the upper and lower shadow rows. If any of these rows contain + ** candidate cells, then recursively call the span process for them. Take + ** note that the adjacent span scanning starts one cell wider on each + ** end of the scan. This is necessary because diagonals are considered + ** adjacent. + */ + for (x = xbegin-1; x <= xend; x++) { + filled += Zone_Span(XY_Cell(x, y-1), zone, check); + filled += Zone_Span(XY_Cell(x, y+1), zone, check); + } + return(filled); +} + + +/*********************************************************************************************** + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * * + * This routine is used to find a location that probably will be ok to move to that is * + * located as close as possible to the specified cell. The computer uses this when it has * + * determined the ideal location for an object, but then needs to give a valid movement * + * destination to a unit. * + * * + * INPUT: cell -- The cell that scanning should radiate out from. * + * * + * zone -- The zone that must be matched to find a legal location (value of -1 means * + * any zone will do). * + * * + * * + * check -- The type of zone to check against. Only valid if a zone value is given. * + * * + * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * + * to the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check) const +{ + CELL topten[10]; + int count = 0; + int xx = Cell_X(cell); + int yy = Cell_Y(cell); + + /* + ** Determine the limits of the scanning in the four directions so that + ** it won't scan past the edge of the world. + */ + int left = xx - MapCellX; + int right = (MapCellWidth - left) - 1; + int top = yy - MapCellY; + int bottom = (MapCellHeight - top) - 1; + + /* + ** Radiate outward from the specified location, looking for the closest + ** location that is generally clear. + */ + for (int radius = 0; radius < MAP_CELL_W/2; radius++) { + CELL newcell; + CellClass const * cellptr; + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + if (x >= -left && radius <= top) { + newcell = XY_Cell(xx+x, yy-radius); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + if (x <= right && radius <= bottom) { + newcell = XY_Cell(xx+x, yy+radius); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + + if (count == ARRAY_SIZE(topten)) break; + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y <= radius-1; y++) { + if (y >= -top && radius <= left) { + newcell = XY_Cell(xx-radius, yy+y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + if (y <= bottom && radius <= right) { + newcell = XY_Cell(xx+radius, yy+y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + + if (count > 0) break; + } + + if (count > 0) { + return(topten[Frame % count]); + } + return(0); +} + + +/*********************************************************************************************** + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * * + * This routine is used to determine what base the specified cell is close to and what * + * zone of that base the cell lies in. This routine is particularly useful in letting the * + * computer know when the player targets a destination near a computer's base. * + * * + * INPUT: cell -- The cell that is to be checked. * + * * + * house -- Reference to the house type number. This value will be set if a base * + * was found nearby the specified cell. * + * * + * zone -- The zone that the cell is located in IF the cell is near a base. * + * * + * * + * OUTPUT: Was a base near the specified cell found? If not, then the 'house' and 'zone' * + * reference values are left in an undefined state and the return value will be * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Base_Region(CELL cell, HousesType & house, ZoneType & zone) const +{ + if ((unsigned)cell < MAP_CELL_TOTAL && In_Radar(cell)) { + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + + if (h && h->IsActive && !h->IsDefeated && h->Center) { + zone = h->Which_Zone(cell); + if (zone != ZONE_NONE) { + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * * + * This routine will destroy the bridge at the location specified. * + * * + * INPUT: cell -- A cell that can uniquely identify the bridge. * + * * + * OUTPUT: bool; Was the bridge destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Destroy_Bridge_At(CELL cell) +{ + if (In_Radar(cell) && !Special.IsCaptureTheFlag) { + CellClass * cellptr = &(*this)[cell]; + TemplateType ttype = cellptr->TType; + + if (ttype == TEMPLATE_BRIDGE1 || ttype == TEMPLATE_BRIDGE2) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + + if (ttype == TEMPLATE_BRIDGE1) { + new TemplateClass(TEMPLATE_BRIDGE1H, cell); + } else { + new TemplateClass(TEMPLATE_BRIDGE2H, cell); + } + + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + w/2 + (h/2)*MAP_CELL_W)); + } + + if (ttype == TEMPLATE_BRIDGE1H || ttype == TEMPLATE_BRIDGE2H) { + int icon = cellptr->TIcon; + int bridge_w = TemplateTypeClass::As_Reference(ttype).Width; + int bridge_h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % bridge_w; + cell -= MAP_CELL_W * (icon / bridge_w); + + if (ttype == TEMPLATE_BRIDGE1H) { + new TemplateClass(TEMPLATE_BRIDGE1D, cell); + } else { + new TemplateClass(TEMPLATE_BRIDGE2D, cell); + } + + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + bridge_w/2 + (bridge_h/2)*MAP_CELL_W)); + Map.Zone_Reset(MZONEF_ALL); + + /* + ** Now, loop through all the bridge cells and find anyone standing + ** on a destroyed part (which is now river), and nuke them. + */ + for (int y = 0; y < bridge_h; y++) { + for (int x = 0; x < bridge_w; x++) { + CellClass * bridge_cell = &(*this)[cell]; + if (bridge_cell->TType == ttype) { + + /* + ** Any unit that is firing on the bridge at this location, will stop + ** firing because the bridge has been destroyed. + */ + Detach_This_From_All(As_Target(cell), true); + + ObjectClass * obj = bridge_cell->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + } + cell++; + } + cell += MAP_CELL_W - bridge_w; + } + Shake_The_Screen(3); + + return(true); + } else { + /* + ** All this code is for the multi-part bridges. + */ + if (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3E) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + switch (ttype) { + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + ttype++; + new TemplateClass(TemplateType(ttype), cell); + break; + } + + /* + ** If we were a middle piece that just got blown up, update the + ** adjoining pieces to make sure they're shaped properly. + */ + if (ttype == TEMPLATE_BRIDGE_3C) { + // check the template below us, at x-1, y+1 + CELL cell2 = cell + (MAP_CELL_W - 1); + CellClass * celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + // It was also destroyed. Update us and it. + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3D), cell); + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3E), cell2); + } + + // Now check the template above us, at x+1, y-1. + cell2 = cell - (MAP_CELL_W - 1); + celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + if (cellptr->TType == TEMPLATE_BRIDGE_3D) { + // if we're already one-sided, turn us to all water + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3F), cell); + } else { + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3E), cell); + } + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3D), cell2); + } + Map.Zone_Reset(MZONEF_ALL); + } + + /* + ** If we're an end bridge piece, update the adjoining piece to + ** be the proper shape. + */ + if (cellptr->TType == TEMPLATE_BRIDGE_1C) { + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + + // Point to the template below us, x-1, y+2 + CELL cell2 = cell + (MAP_CELL_W * 2) - 1; + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3E), cell2); + break; + case TEMPLATE_BRIDGE_3D: + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3F), cell2); + break; + } + } else { + if (cellptr->TType == TEMPLATE_BRIDGE_2C) { + // Point to the template above us, x+2, y-1 + CELL cell2 = cell - (MAP_CELL_W - 2); + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3D), cell2); + break; + case TEMPLATE_BRIDGE_3E: + new TemplateClass(TemplateType(TEMPLATE_BRIDGE_3F), cell2); + break; + } + } + } + if (cellptr->TType == TEMPLATE_BRIDGE_1C || + cellptr->TType == TEMPLATE_BRIDGE_2C || + (cellptr->TType >= TEMPLATE_BRIDGE_3C && cellptr->TType <= TEMPLATE_BRIDGE_3E)) { + int x, y, tdata = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + CellClass * ptr = &(*this)[(CELL)(cell + x)]; + if (ptr->TType == cellptr->TType || ptr->Land_Type() == LAND_RIVER || ptr->Land_Type() == LAND_WATER) { + Detach_This_From_All(As_Target((CELL)(cell+tdata)), true); + + ObjectClass * obj = ptr->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + + } + tdata++; + } + cell += MAP_CELL_W; + } + Shake_The_Screen(3); + Map.Zone_Reset(MZONEF_ALL); + return(true); + } + Shake_The_Screen(3); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Detach -- Remove specified object from map references. * + * * + * This routine will take the object (represented by a target value) and remove all * + * references to it from the map. Typically, this is used to remove trigger reference. * + * * + * INPUT: target -- The target object to remove from the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +void MapClass::Detach(TARGET target, bool) +{ + /* + ** Remove this trigger from the map zone/line tracking list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < MapTriggers.Count(); index++) { + if (MapTriggers[index] == As_Trigger(target)) { + MapTriggers.Delete(index); + break; + } + } + + /* + ** Loop through all cells; remove any reference to this trigger + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger == As_Trigger(target)) { + (*this)[cell].Trigger = NULL; + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * * + * This will examine the entire map and return the number of bridges that are intact. An * + * intact bridge is one that units can travel over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of intact bridges on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Intact_Bridge_Count(void) const +{ + /* + ** Count all non-destroyed bridges on the map. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + switch (cellptr->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + if (cellptr->TIcon == 6) { + count++; + } + break; + + default: + break; + } + + cellptr++; + } + + return(count); +} + + +/*********************************************************************************************** + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * * + * This routine will pick a random location on the map. It performs no legality checking * + * other than forcing the cell to be on the map proper. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1996 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Pick_Random_Location(void) const +{ + int x = Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1); + int y = Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1); + + return(XY_Cell(x, y)); +} + +/*********************************************************************************************** + * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 BWG : Created. * + *=============================================================================================*/ +void MapClass::Shroud_The_Map(void) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->IsMapped || cellptr->IsVisible) { + cellptr->Redraw_Objects(); + /* + ** BG: remove "ring of darkness" around edge of map. + */ + int x = Cell_X(cell); + int y = Cell_Y(cell); + if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && + y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { + cellptr->IsMapped = false; + cellptr->IsVisible = false; + } + } + } + for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { + ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; + if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == PlayerPtr) { + layer_object->Look(); + } + } + Flag_To_Redraw(true); +} diff --git a/CODE/MAP.H b/CODE/MAP.H new file mode 100644 index 0000000..0b21b95 --- /dev/null +++ b/CODE/MAP.H @@ -0,0 +1,180 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAP_H +#define MAP_H + +#include "gscreen.h" +#include "crate.h" + +class MapClass: public GScreenClass +{ + public: + + MapClass(void) {}; + MapClass(NoInitClass const & x) : GScreenClass(x), Array(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // Theater-specific inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Alloc_Cells(void); // Allocates buffers + virtual void Free_Cells(void); // Frees buffers + virtual void Init_Cells(void); // Frees buffers + + /*-------------------------------------------------------- + ** Main functions that deal with groupings of cells within the map or deals with the cell + ** as it relates to the map - not what the cell contains. + */ + CELL Pick_Random_Location(void) const; + int Intact_Bridge_Count(void) const; + bool Base_Region(CELL cell, HousesType & house, ZoneType & zone) const; + CELL Nearby_Location(CELL cell, SpeedType speed, int zone=-1, MZoneType check=MZONE_NORMAL) const; + ObjectClass * Close_Object(COORDINATE coord) const; + virtual void Detach(ObjectClass * ) {}; + int Cell_Region(CELL cell); + int Cell_Threat(CELL cell, HousesType house); + bool In_Radar(CELL cell) const; + void Sight_From(CELL cell, int sightrange, HouseClass *house, bool incremental=false); + void Jam_From(CELL cell, int jamrange, HouseClass *house); + void Shroud_From(CELL cell, int sightrange); + void UnJam_From(CELL cell, int jamrange, HouseClass *house); + void Place_Down(CELL cell, ObjectClass * object); + void Pick_Up(CELL cell, ObjectClass * object); + void Overlap_Down(CELL cell, ObjectClass * object); + void Overlap_Up(CELL cell, ObjectClass * object); + bool Read_Binary(Straw & straw); + int Write_Binary(Pipe & pipe); + bool Place_Random_Crate(void); + bool Remove_Crate(CELL cell); + bool Zone_Reset(int method); + bool Zone_Cell(CELL cell, int zone); + int Zone_Span(CELL cell, int zone, MZoneType check); + bool Destroy_Bridge_At(CELL cell); + void Detach(TARGET target, bool all=true); + void Shroud_The_Map(void); + + long Overpass(void); + + virtual void Logic(void); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Debug routine + */ + int Validate(void); + + /* + ** This is the dimensions and position of the sub section of the global map. + ** It is this region that appears on the radar map and constrains normal + ** movement. + */ + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + + /* + ** This is the total value of all harvestable Tiberium on the map. + */ + long TotalValue; + + CellClass & operator [] (COORDINATE coord) {return(Array[Coord_Cell(coord)]);}; + CellClass & operator [] (CELL cell) {return(Array[cell]);}; + CellClass const & operator [] (COORDINATE coord) const {return(Array[Coord_Cell(coord)]);}; + CellClass const & operator [] (CELL cell) const {return(Array[cell]);}; + int ID(CellClass const * ptr) {return(Array.ID(ptr));}; + int ID(CellClass const & ptr) {return(Array.ID(ptr));}; + + protected: + + /* + ** This is the array of cell objects. + */ + VectorClass Array; + + /* + ** These are the size dimensions of the underlying array of cell objects. + ** This is the dimensions of the "map" that the tactical view is + ** restricted to. + */ + int XSize; + int YSize; + int Size; + + static int const RadiusCount[11]; + static int const RadiusOffset[]; + + /* + ** This specifies the information for the various crates in the game. + */ + CrateClass Crates[256]; + + private: + friend class CellClass; + + /* + ** Tiberium growth potential cells are recorded here. + */ + CELL TiberiumGrowth[MAP_CELL_W/2]; + int TiberiumGrowthCount; + int TiberiumGrowthExcess; + + /* + ** List of cells that are full enough strength that they could spread + ** Tiberium to adjacent cells. + */ + CELL TiberiumSpread[MAP_CELL_W/2]; + int TiberiumSpreadCount; + int TiberiumSpreadExcess; + + /* + ** This is the current cell number in the incremental map scan process. + */ + CELL TiberiumScan; + + enum MapEnum {SCAN_AMOUNT=MAP_CELL_TOTAL}; +}; + +#endif diff --git a/CODE/MAPEDDLG.CPP b/CODE/MAPEDDLG.CPP new file mode 100644 index 0000000..b36627f --- /dev/null +++ b/CODE/MAPEDDLG.CPP @@ -0,0 +1,2708 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : MAPEDDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Map Editor dialogs & main menu options * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Handle_Triggers -- processes the trigger dialogs * + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * MapEditClass::New_Scenario -- creates a new scenario * + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * MapEditClass::Select_Trigger -- lets user select a trigger * + * MapEditClass::Size_Map -- lets user set size & location of map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::New_Scenario -- creates a new scenario * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Prompts user for map size * + * - Initializes the scenario by calling Clear_Scenario(), which calls * + * everybody's Init() routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = new scenario created, -1 = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::New_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + HousesType house; + + /* + ** Force the house save value to match the player house. + */ + if (PlayerPtr) { + switch (PlayerPtr->Class->House) { + case HOUSE_SPAIN: + player = SCEN_PLAYER_SPAIN; + break; + + case HOUSE_GREECE: + player = SCEN_PLAYER_GREECE; + break; + + default: + case HOUSE_USSR: + player = SCEN_PLAYER_USSR; + break; + } + } + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("New Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + ScenarioInit++; + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Create houses + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + new HouseClass(house); + } + + switch (player) { + case SCEN_PLAYER_MPLAYER: + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI1); + PlayerPtr->IsHuman = true; + LastHouse = HOUSE_MULTI1; + break; + + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Init the entire map + */ +// Init_Clear(); + Fill_In_Data(); + + /* + ** Prompt for map size + */ + Size_Map(-1, -1, 30, 30); + + /* + ** Set the Home & Reinforcement Cells to the center of the map + */ + Scen.Waypoint[WAYPT_REINF] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + (*this)[TacticalCoord].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + + Set_Tactical_Position(Cell_Coord(Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR) - (5 * RESFACTOR))); + ScenarioInit--; + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Loads the INI file for that scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Load_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + NodeNameType * who; // node to add to Players + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Load Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Read_Scenario_Ini() must be able to set PlayerPtr to the right house: + ** - Reading the INI will create the house objects + ** - PlayerPtr must be set before any Techno objects are created + ** - For GDI or NOD scenarios, PlayerPtr is set by reading the INI; + ** but for multiplayer, it's set via the Players vector; so, here we have + ** to set various multiplayer variables to fool the Assign_Houses() routine + ** into working properly. + */ + if (player == SCEN_PLAYER_MPLAYER) { + Clear_Vector(&Session.Players); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Session.NumPlayers = 1; + LastHouse = HOUSE_MULTI1; + } else { +#ifdef NEVER + if (ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } else { +#endif + LastHouse = HOUSE_GOOD; + } + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Read the INI + */ + if (Read_Scenario_INI(Scen.ScenarioName) == 0) { + if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'G'){ + WWMessageBox().Process("Please insert Red Alert CD1"); + }else if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'U') + WWMessageBox().Process("Please insert Red Alert CD2"); + else + WWMessageBox().Process("Unable to read scenario!"); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + Fill_In_Data(); + GamePalette.Set(); +// Set_Palette(GamePalette); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Saves the INI file for this scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = error/cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Save_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; +// FILE * fp; +// char fname[13]; + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Save Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Warning if scenario already exists + */ +// Scen.Set_Scenario_Name(scen_num, player, dir, var); +// fp = fopen(fname, "rb"); +// if (fp) { +// fclose(fp); +// rc = WWMessageBox().Process("File exists. Replace?", TXT_YES, TXT_NO); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// Render(); +// if (rc==1) { +// return(-1); +// } +// } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Player may have changed from GDI to NOD, so change playerptr accordingly + */ + switch (player) { + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Write the INI + */ + Write_Scenario_INI(Scen.ScenarioName); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * * + * Prompts user for: * + * - House (GDI, NOD) * + * - Scenario # * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Caption ³ * + * ³ ³ * + * ³ Scenario ___ ³ * + * ³ Version ___ ³ * + * ³ ³ * + * ³ [East] [West] ³ * + * ³ ³ * + * ³ [ GDI ] ³ * + * ³ [ NOD ] ³ * + * ³ [Multi-Player] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * caption string to use as a title * + * scen_nump output: ptr to scenario # * + * playerp output: ptr to player type * + * dirp output: ptr to direction * + * varp output: ptr to variation * + * multi 1 = allow to change single/multiplayer; 0 = not * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 09/04/1996 JLB : Simplified * + *=========================================================================*/ +int MapEditClass::Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 200, // dialog width + D_DIALOG_H = 164, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_SCEN_W = 45, // Scenario # width + D_SCEN_H = 9, // Scenario # height + D_SCEN_X = D_DIALOG_CX + 5, // Scenario # x + D_SCEN_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, // Scenario # y + + D_VARA_W = 13, // Version A width + D_VARA_H = 9, // Version A height + D_VARA_X = D_DIALOG_CX - (D_VARA_W * 5) / 2, // Version A x + D_VARA_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version A y + + D_VARB_W = 13, // Version B width + D_VARB_H = 9, // Version B height + D_VARB_X = D_VARA_X + D_VARA_W, // Version B x + D_VARB_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version B y + + D_VARC_W = 13, // Version C width + D_VARC_H = 9, // Version C height + D_VARC_X = D_VARB_X + D_VARB_W, // Version C x + D_VARC_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version C y + + D_VARD_W = 13, // Version D width + D_VARD_H = 9, // Version D height + D_VARD_X = D_VARC_X + D_VARC_W, // Version D x + D_VARD_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version D y + + D_VARLOSE_W = 13, // Version Lose width + D_VARLOSE_H = 9, // Version Lose height + D_VARLOSE_X = D_VARD_X + D_VARD_W, // Version Lose x + D_VARLOSE_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version Lose y + + D_EAST_W = 50, // EAST width + D_EAST_H = 9, // EAST height + D_EAST_X = D_DIALOG_CX - D_EAST_W - 5, // EAST x + D_EAST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_WEST_W = 50, // WEST width + D_WEST_H = 9, // WEST height + D_WEST_X = D_DIALOG_CX + 5, // WEST x + D_WEST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_GDI_W = 90, // GDI width + D_GDI_H = 9, // GDI height + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), // GDI x + D_GDI_Y = D_EAST_Y + D_EAST_H + D_MARGIN, // GDI y + + D_NOD_W = 90, // NOD width + D_NOD_H = 9, // NOD height + D_NOD_X = D_DIALOG_CX - (D_NOD_W / 2), // NOD x + D_NOD_Y = D_GDI_Y + D_GDI_H, // NOD y + + D_NEU_W = 90, // Neutral width + D_NEU_H = 9, // Neutral height + D_NEU_X = D_DIALOG_CX - (D_NOD_W / 2), // Neutral x + D_NEU_Y = D_NOD_Y + D_NOD_H, // Neutral y + + D_MPLAYER_W = 90, // Multi-Player width + D_MPLAYER_H = 9, // Multi-Player height + D_MPLAYER_X = D_DIALOG_CX - (D_MPLAYER_W / 2), // Multi-Player x + D_MPLAYER_Y = D_NEU_Y + D_NEU_H, // Multi-Player y + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN+15), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN+15), // Cancel y + + }; + + /* + ** Button enumerations + */ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_MPLAYER, + BUTTON_EAST, + BUTTON_WEST, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SCENARIO, + BUTTON_VAR_A, + BUTTON_VAR_B, + BUTTON_VAR_C, + BUTTON_VAR_D, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + + /* + ** Other Variables + */ + char scen_buf[10]={0}; // buffer for editing scenario # + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + EditClass editbtn (BUTTON_SCENARIO, scen_buf, 5, TPF_EFNT|TPF_NOSHADOW, D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::ALPHANUMERIC); +#else + EditClass editbtn (BUTTON_SCENARIO, scen_buf, 5, TPF_EFNT|TPF_NOSHADOW, D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::NUMERIC); +#endif + + TextButtonClass varabtn(BUTTON_VAR_A, "A", TPF_EBUTTON, D_VARA_X, D_VARA_Y, D_VARA_W, D_VARA_H); + TextButtonClass varbbtn(BUTTON_VAR_B, "B", TPF_EBUTTON, D_VARB_X, D_VARB_Y, D_VARB_W, D_VARB_H); + TextButtonClass varcbtn(BUTTON_VAR_C, "C", TPF_EBUTTON, D_VARC_X, D_VARC_Y, D_VARC_W, D_VARC_H); + TextButtonClass vardbtn(BUTTON_VAR_D, "D", TPF_EBUTTON, D_VARD_X, D_VARD_Y, D_VARD_W, D_VARD_H); + TextButtonClass gdibtn(BUTTON_GDI, "North (Spain)", TPF_EBUTTON, D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + TextButtonClass nodbtn(BUTTON_NOD, "South (Greece)", TPF_EBUTTON, D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + TextButtonClass neubtn(BUTTON_NEUTRAL, HouseTypeClass::As_Reference(HOUSE_USSR).IniName, TPF_EBUTTON, D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + TextButtonClass playermbtn(BUTTON_MPLAYER, "Multiplayer", TPF_EBUTTON, D_MPLAYER_X, D_MPLAYER_Y, D_MPLAYER_W, D_MPLAYER_H); + TextButtonClass eastbtn(BUTTON_EAST, "East", TPF_EBUTTON, D_EAST_X, D_EAST_Y, D_EAST_W, D_EAST_H); + TextButtonClass westbtn(BUTTON_WEST, "West", TPF_EBUTTON, D_WEST_X, D_WEST_Y, D_WEST_W, D_WEST_H); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (scen_nump < 100) { + sprintf(scen_buf, "%d", scen_nump); // init edit buffer + } else { + char first = scen_nump / 36; + char second = scen_nump % 36; + scen_buf[0] = first + 'A'; +//Mono_Printf("picking map, scen# = %d, first = %c, second = %d (numeric)\n",scen_nump, scen_buf[0],second);Keyboard->Get();Keyboard->Get(); + if (second < 10) { + scen_buf[1] = second + '0'; + } else { + scen_buf[1] = (second-10) + 'A'; + } + scen_buf[2] = 0; + } +#else + sprintf(scen_buf, "%d", scen_nump); // init edit buffer +#endif + editbtn.Set_Text(scen_buf, 5); + + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + switch (varp) { + case SCEN_VAR_A: + varabtn.Turn_On(); + break; + + case SCEN_VAR_B: + varbbtn.Turn_On(); + break; + + case SCEN_VAR_C: + varcbtn.Turn_On(); + break; + + case SCEN_VAR_D: + vardbtn.Turn_On(); + break; + } + + /* + ** Create the button list + */ + commands = &editbtn; + varabtn.Add_Tail(*commands); + varbbtn.Add_Tail(*commands); + varcbtn.Add_Tail(*commands); + vardbtn.Add_Tail(*commands); + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neubtn.Add_Tail(*commands); + playermbtn.Add_Tail(*commands); + eastbtn.Add_Tail(*commands); + westbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Init the button states + */ + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + neubtn.Turn_Off(); + playermbtn.Turn_Off(); + if (playerp == SCEN_PLAYER_MPLAYER) { + playermbtn.Turn_On(); + } else { + if (PlayerPtr) { + switch (PlayerPtr->Class->House) { + case HOUSE_SPAIN: + gdibtn.Turn_On(); + break; + + case HOUSE_GREECE: + nodbtn.Turn_On(); + break; + + case HOUSE_USSR: + neubtn.Turn_On(); + break; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } else { + switch (Scen.ScenarioName[2]) { + case 'G': + gdibtn.Turn_On(); + break; + + case 'U': + nodbtn.Turn_On(); + break; + + case 'M': + playermbtn.Turn_On(); + break; + } +#endif + } + } + + eastbtn.Turn_Off(); + westbtn.Turn_Off(); + if (dirp == SCEN_DIR_EAST) { + eastbtn.Turn_On(); + } else { + westbtn.Turn_On(); + } + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(caption, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + Fancy_Text_Print("Scenario", D_DIALOG_CX - 5, D_SCEN_Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); + commands->Draw_All(); + Show_Mouse(); + + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + + /* + ** Handle a click on one of the scenario variation group buttons. + */ + case (BUTTON_VAR_A | KN_BUTTON): + case (BUTTON_VAR_B | KN_BUTTON): + case (BUTTON_VAR_C | KN_BUTTON): + case (BUTTON_VAR_D | KN_BUTTON): + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + switch (input) { + case (BUTTON_VAR_A | KN_BUTTON): + varp = SCEN_VAR_A; + varabtn.Turn_On(); + break; + + case (BUTTON_VAR_B | KN_BUTTON): + varp = SCEN_VAR_B; + varbbtn.Turn_On(); + break; + + case (BUTTON_VAR_C | KN_BUTTON): + varp = SCEN_VAR_C; + varcbtn.Turn_On(); + break; + + case (BUTTON_VAR_D | KN_BUTTON): + varp = SCEN_VAR_D; + vardbtn.Turn_On(); + break; + } + break; + + /* + ** Handle a click on the east/west variation group. + */ + case (BUTTON_EAST | KN_BUTTON): + case (BUTTON_WEST | KN_BUTTON): + westbtn.Turn_Off(); + eastbtn.Turn_Off(); + switch (input) { + case (BUTTON_EAST | KN_BUTTON): + dirp = SCEN_DIR_EAST; + eastbtn.Turn_On(); + break; + + case (BUTTON_WEST | KN_BUTTON): + dirp = SCEN_DIR_WEST; + westbtn.Turn_On(); + break; + } + break; + + + /* + ** Handle a click on one of the player category + ** group buttons. + */ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MPLAYER | KN_BUTTON): + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + neubtn.Turn_Off(); + playermbtn.Turn_Off(); + switch (input) { + case (BUTTON_GDI | KN_BUTTON): + playerp = SCEN_PLAYER_SPAIN; + gdibtn.Turn_On(); + break; + + case (BUTTON_NOD | KN_BUTTON): + playerp = SCEN_PLAYER_GREECE; + nodbtn.Turn_On(); + break; + + case (BUTTON_NEUTRAL | KN_BUTTON): + playerp = SCEN_PLAYER_USSR; + neubtn.Turn_On(); + break; + + case (BUTTON_MPLAYER | KN_BUTTON): + playerp = SCEN_PLAYER_MPLAYER; + playermbtn.Turn_On(); + break; + } + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case (BUTTON_SCENARIO | KN_BUTTON): + break; + + default: + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Save selections & return + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (scen_buf[0] <= '9' && scen_buf[1] <= '9') { + scen_nump = atoi(scen_buf); + } else { + char first = scen_buf[0]; + char second = scen_buf[1]; + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scen_nump = (first * 36) + second; +//Mono_Printf("Converted to: %d, %d = %d\n",first, second, scen_nump);Keyboard->Get();Keyboard->Get(); + } +#else + scen_nump = atoi(scen_buf); +#endif + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Size_Map -- lets user set size & location of map * + * * + * Lets the user select a side of the map and expand/shrink it to the * + * desired size, or move the whole map around the available map area. * + * * + * The entire available map area is displayed, but the map is limited such * + * that there's always one blank cell around the map; this lets objects * + * properly exit the screen, since they have a blank undisplayed cell to * + * exit onto. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Clear Terrain ³ * + * ³ ³ ³ Water ³ * + * ³ ³ ³ Tiberium ³ * + * ³ ³ ³ Rock/Wall/Road ³ * + * ³ ³ (Map Area) ³ GDI Unit ³ * + * ³ ³ ³ NOD Unit ³ * + * ³ ³ ³ Neutral Unit ³ * + * ³ ³ ³ Terrain Object ³ * + * ³ ³ ³ Starting Cell ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ X Y Width Height ³ * + * ³ ## ## ## ## ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * x,y,w,h: initial size parameters (-1 = center the thing) * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Size_Map(int x, int y, int w, int h) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 350, // dialog width + D_DIALOG_H = 225, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_BORD_X1 = D_DIALOG_X + 45, +// D_BORD_X1 = D_DIALOG_X + (D_DIALOG_W / 2 - MAP_CELL_W) / 2, + D_BORD_Y1 = D_DIALOG_Y + 25, + D_BORD_X2 = D_BORD_X1 + MAP_CELL_W + 1, + D_BORD_Y2 = D_BORD_Y1 + MAP_CELL_H + 1, + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_X + 45, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN + 10), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (35 + D_CANCEL_W), // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN + 10), // Cancel y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_OK=100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MAP, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map board, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables: + */ + RedrawType display; // requested redraw level + bool cancel = false; // true = user cancels + KeyNumType input; // user input + int grabbed = 0; // 1=TLeft,2=TRight,3=BRight,4=BLeft + int map_x1; // map coords x1, pixel coords + int map_x2; // map coords x2, pixel coords + int map_y1; // map coords y1, pixel coords + int map_y2; // map coords y2, pixel coords + int delta1, delta2; // mouse-click proximity + int mx,my; // last-saved mouse coords +// char txt[40]; + int txt_x,txt_y; // for displaying text +// unsigned index; // for drawing map symbology + CELL cell; // for drawing map symbology + int color; // for drawing map symbology + ObjectClass * occupier; // cell's occupier + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass * commands = NULL; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Set up the actual map area relative to the map's border coords + */ + if (x==-1) { + map_x1 = D_BORD_X1 + (MAP_CELL_W - w) / 2 + 1; + } else { + map_x1 = D_BORD_X1 + x + 1; + } + + if (y==-1) { + map_y1 = D_BORD_Y1 + (MAP_CELL_H - h) / 2 + 1; + } else { + map_y1 = D_BORD_Y1 + y + 1; + } + + map_x2 = map_x1 + w - 1; + map_y2 = map_y1 + h - 1; + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main processing loop + */ + display = REDRAW_ALL; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Redraw the background, map border, key, and coord labels + */ + if (display >= REDRAW_BACKGROUND) { + + /* + ** Background + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_SIZE_MAP, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the map border + */ + if (LogicPage->Lock()) { + LogicPage->Draw_Rect(D_BORD_X1, D_BORD_Y1, D_BORD_X2, D_BORD_Y2, scheme->Shadow); +// for (index = D_BORD_X1; index < D_BORD_X2; +// index += (320/ICON_PIXEL_W)) { +// LogicPage->Put_Pixel(index, D_BORD_Y1-1, scheme->Shadow); +// LogicPage->Put_Pixel(index, D_BORD_Y2+1, scheme->Shadow); +// } +// for (index = D_BORD_Y1; index < D_BORD_Y2-8; +// index += (200/ICON_PIXEL_H)) { +// LogicPage->Put_Pixel(D_BORD_X1-1, index, scheme->Shadow); +// LogicPage->Put_Pixel(D_BORD_X2+1, index, scheme->Shadow); +// } + + /* + ** Draw the map "key" + */ + txt_x = D_BORD_X2 + 15; + txt_y = D_BORD_Y1; + Plain_Text_Print("Clear Terrain", txt_x, txt_y, GroundColor[LAND_CLEAR], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Water", txt_x, txt_y, GroundColor[LAND_WATER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Tiberium", txt_x, txt_y, GroundColor[LAND_TIBERIUM], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Rock", txt_x, txt_y, GroundColor[LAND_ROCK], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Wall", txt_x, txt_y, GroundColor[LAND_WALL], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Beach", txt_x, txt_y, GroundColor[LAND_BEACH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Rough", txt_x, txt_y, GroundColor[LAND_ROUGH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("River", txt_x, txt_y, GroundColor[LAND_RIVER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("GDI Unit", txt_x, txt_y, YELLOW, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Nod Unit", txt_x, txt_y, RED, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Neutral Unit", txt_x, txt_y, PURPLE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Terrain Object", txt_x, txt_y, DKGREEN, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + txt_y += 8; + Plain_Text_Print("Starting Cell", txt_x, txt_y, WHITE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); + + /* + ** Draw the coordinate labels + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 43; + Fancy_Text_Print(" X", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Y", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Width", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print(" Height", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + LogicPage->Unlock(); + } + + /* + ** Redraw the buttons + */ + commands->Flag_List_To_Redraw(); + } + + /* + ** Redraw the map symbology & location + */ + if (display >= REDRAW_MAP) { + + if (LogicPage->Lock()) { + + /* + ** Erase the map interior + */ + LogicPage->Fill_Rect(D_BORD_X1 + 1, D_BORD_Y1 + 1, D_BORD_X2 - 1, D_BORD_Y2 - 1, BLACK); + + /* + ** Draw Land map symbols (use color according to Ground[] array). + */ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier == NULL) { + color = GroundColor[(*this)[cell].Land_Type()]; + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ** Draw the actual map location + */ + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, WHITE); + switch (grabbed) { + case 1: + LogicPage->Draw_Line(map_x1, map_y1, map_x1 + 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x1, map_y1, map_x1, map_y1 + 5, BLUE); + break; + + case 2: + LogicPage->Draw_Line(map_x2, map_y1, map_x2 - 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x2, map_y1, map_x2, map_y1 + 5, BLUE); + break; + + case 3: + LogicPage->Draw_Line(map_x2, map_y2, map_x2 - 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x2, map_y2, map_x2, map_y2 - 5, BLUE); + break; + + case 4: + LogicPage->Draw_Line(map_x1, map_y2, map_x1 + 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x1, map_y2, map_x1, map_y2 - 5, BLUE); + break; + + case 5: + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, BLUE); + break; + + default: + break; + } + + /* + ** Draw Unit map symbols (Use the radar map color according to + ** that specified in the house type class object. + ** DKGREEN = terrain object + */ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier) { + color = DKGREEN; + if (occupier && occupier->Owner() != HOUSE_NONE) { + color = ColorRemaps[HouseClass::As_Pointer(occupier->Owner())->RemapColor].Color; + } + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ** Draw Home location + */ + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(Scen.Waypoint[WAYPT_HOME]) + 1, D_BORD_Y1 + Cell_Y(Scen.Waypoint[WAYPT_HOME]) + 1, WHITE); + + /* + ** Erase old coordinates + */ +// LogicPage->Fill_Rect( D_DIALOG_X + 7, +// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22, +// D_DIALOG_X + D_DIALOG_W - 7, +// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22 + 10, BLACK); + + /* + ** Draw the coordinates + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 32; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x1 - D_BORD_X1 - 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y1 - D_BORD_Y1 - 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x2 - map_x1 + 1); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y2 - map_y1 + 1); + + LogicPage->Unlock(); + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Process user input + */ + input = commands->Input(); + + /* + ** Normal button processing: This is done when the mouse button is NOT + ** being held down ('grabbed' is 0). + */ + if (grabbed == 0) { + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case KN_LMOUSE: + /* + ** Grab top left + */ + delta1 = abs(Keyboard->MouseQX - map_x1); + delta2 = abs(Keyboard->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 1; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab top right + */ + delta1 = abs(Keyboard->MouseQX - map_x2); + delta2 = abs(Keyboard->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 2; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab bottom right + */ + delta1 = abs(Keyboard->MouseQX - map_x2); + delta2 = abs(Keyboard->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 3; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab bottom left + */ + delta1 = abs(Keyboard->MouseQX - map_x1); + delta2 = abs(Keyboard->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 4; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + break; + } + + /* + ** Grab the whole map + */ + delta1 = abs(Keyboard->MouseQX - ((map_x1 + map_x2) / 2)); + delta2 = abs(Keyboard->MouseQY - ((map_y1 + map_y2) / 2)); + if (delta1 < (map_x2 - map_x1) / 4 && + delta2 < (map_y2 - map_y1) / 4) { + grabbed = 5; + mx = Keyboard->MouseQX; + my = Keyboard->MouseQY; + display = REDRAW_MAP; + } + break; + + default: + break; + } + } else { + + /* + ** Mouse motion processing: This is done while the left mouse button IS + ** being held down. + ** - First, check for the button release; if detected, un-grab + ** - Then, handle mouse motion. WWLIB doesn't pass through a KN_MOUSE_MOVE + ** value while the button is being held down, so this case must be + ** trapped as a default. + */ + switch (input) { + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + grabbed = 0; + display = REDRAW_MAP; + break; + + default: + delta1 = Get_Mouse_X() - mx; + delta2 = Get_Mouse_Y() - my; + if (delta1==0 && delta2==0) { + break; + } + + /* + ** Move top left + */ + if (grabbed==1) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move top right + */ + if (grabbed==2) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move bottom right + */ + if (grabbed==3) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move bottom left + */ + if (grabbed==4) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ** Move whole map + */ + if (grabbed==5) { + if (map_x1 + delta1 > D_BORD_X1 + 1 && map_x2 + delta1 < D_BORD_X2 - 1) { + map_x1 += delta1; + map_x2 += delta1; + } + + if (map_y1 + delta2 > D_BORD_Y1 + 1 && map_y2 + delta2 < D_BORD_Y2 - 1) { + map_y1 += delta2; + map_y2 += delta2; + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + break; + } + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Save selections + */ + MapCellX = map_x1 - D_BORD_X1 - 1; + MapCellY = map_y1 - D_BORD_Y1 - 1; + MapCellWidth = map_x2 - map_x1 + 1; + MapCellHeight = map_y2 - map_y1 + 1; + + /* + ** Clip Home Cell to new map size + */ + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) < MapCellX) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) > MapCellX + MapCellWidth - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth - 1, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) < MapCellY) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) > MapCellY + MapCellHeight - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY + MapCellHeight - 1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * * + * Edits the house specific and general scenario options. * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + * 02/13/1996 JLB : Revamped to new system. * + *=========================================================================*/ +int MapEditClass::Scenario_Dialog(void) +{ + TheaterType orig_theater = Scen.Theater; // original theater + HousesType house = PlayerPtr->Class->House; + HousesType newhouse = house; + HouseStaticClass hdata[HOUSE_COUNT]; + + /* + ** Fill in the house data for each house that exists. + */ + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr) { + hdata[h] = hptr->Control; + } + } + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 320 * RESFACTOR, + D_DIALOG_H = 200 * RESFACTOR, + D_DIALOG_X = ((320 * RESFACTOR - D_DIALOG_W) / 2), + D_DIALOG_Y = ((200 * RESFACTOR - D_DIALOG_H) / 2), + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + 15 * RESFACTOR, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 15 * RESFACTOR, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+15*RESFACTOR), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - 15*RESFACTOR + }; + + /* + ** Button enumerations: + */ + enum { + LIST_THEATER=100, + BUTTON_DESCRIPTION, + BUTTON_ALLIES, + BUTTON_CONTROL, + BUTTON_SMARTIES, + BUTTON_BASE, + BUTTON_NOSPYPLANE, + BUTTON_INHERIT, + BUTTON_TIMER, + BUTTON_THEME, + BUTTON_RECORD, + BUTTON_EVAC, + BUTTON_MONEYTIB, + BUTTON_TECH, + BUTTON_TRUCKCRATE, + BUTTON_ENDOFGAME, + BUTTON_SKIPSCORE, + BUTTON_ONETIME, + BUTTON_NOMAPSEL, + BUTTON_HOUSE, + BUTTON_CREDITS, + BUTTON_SOURCE, + BUTTON_MAXUNIT, + BUTTON_INTRO, + BUTTON_BRIEFING, + BUTTON_ACTION, + BUTTON_WIN, + BUTTON_LOSE, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + ControlClass * commands = NULL; // the button list + + /* + ** Theater choice drop down list. + */ + char theatertext[45] = ""; + DropListClass theaterbtn(LIST_THEATER, theatertext, sizeof(theatertext)-1, + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+30, 65, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TheaterType t = THEATER_FIRST; t < THEATER_COUNT; t++) { + theaterbtn.Add_Item(Theaters[t].Name); + } + theaterbtn.Set_Selected_Index(orig_theater); + + char description[DESCRIP_MAX] = ""; + strcpy(description, Scen.Description); + EditClass desc(BUTTON_DESCRIPTION, description, sizeof(description), TPF_EFNT|TPF_NOSHADOW, theaterbtn.X+theaterbtn.Width+15, theaterbtn.Y, 160); + + /* + ** Button that tells if this scenario should inherit buildings from the previous. + */ + CheckBoxClass inherit(BUTTON_INHERIT, theaterbtn.X+theaterbtn.Width+15+250, theaterbtn.Y); + if (Scen.IsToInherit) { + inherit.Turn_On(); + } else { + inherit.Turn_Off(); + } + + /* + ** Records scenario disposition into holding slot. + */ + CheckBoxClass record(BUTTON_RECORD, inherit.X, inherit.Y+8); + if (Scen.IsToCarryOver) { + record.Turn_On(); + } else { + record.Turn_Off(); + } + + /* + ** Should Tanya/civilian be automatically evacuated? + */ + CheckBoxClass tanya(BUTTON_EVAC, record.X, record.Y+8); + if (Scen.IsTanyaEvac) { + tanya.Turn_On(); + } else { + tanya.Turn_Off(); + } + + /* + ** End of game with with scenario? + */ + CheckBoxClass endofgame(BUTTON_ENDOFGAME, tanya.X, tanya.Y+8); + if (Scen.IsEndOfGame) { + endofgame.Turn_On(); + } else { + endofgame.Turn_Off(); + } + + /* + ** Timer inherit logic. + */ + CheckBoxClass timercarry(BUTTON_TIMER, endofgame.X, endofgame.Y+8); + if (Scen.IsInheritTimer) { + timercarry.Turn_On(); + } else { + timercarry.Turn_Off(); + } + + /* + ** Disable spy plane option? + */ + CheckBoxClass nospyplane(BUTTON_NOSPYPLANE, timercarry.X, timercarry.Y+8); + if (Scen.IsNoSpyPlane) { + nospyplane.Turn_On(); + } else { + nospyplane.Turn_Off(); + } + + /* + ** Skip the score screen? + */ + CheckBoxClass skipscore(BUTTON_SKIPSCORE, nospyplane.X, nospyplane.Y+8); + if (Scen.IsSkipScore) { + skipscore.Turn_On(); + } else { + skipscore.Turn_Off(); + } + + /* + ** Skip the map selection screen for next mission. Presume goes to + ** variation "B"? + */ + CheckBoxClass nomapsel(BUTTON_NOMAPSEL, skipscore.X, skipscore.Y+8); + if (Scen.IsNoMapSel) { + nomapsel.Turn_On(); + } else { + nomapsel.Turn_Off(); + } + + /* + ** Return to main menu after mission completes? + */ + CheckBoxClass onetime(BUTTON_ONETIME, nomapsel.X, nomapsel.Y+8); + if (Scen.IsOneTimeOnly) { + onetime.Turn_On(); + } else { + onetime.Turn_Off(); + } + + /* + ** Trucks carry a wood crate? + */ + CheckBoxClass truckcrate(BUTTON_TRUCKCRATE, onetime.X, onetime.Y+8); + if (Scen.IsTruckCrate) { + truckcrate.Turn_On(); + } else { + truckcrate.Turn_Off(); + } + + /* + ** Transfer credits into tiberium storage at scenario start? + */ + CheckBoxClass moneytib(BUTTON_MONEYTIB, truckcrate.X, truckcrate.Y+8); + if (Scen.IsMoneyTiberium) { + moneytib.Turn_On(); + } else { + moneytib.Turn_Off(); + } + + /* + ** Intro movie name. + */ + char introtext[_MAX_FNAME+_MAX_EXT]; + DropListClass intro(BUTTON_INTRO, introtext, sizeof(introtext), + TPF_EFNT|TPF_NOSHADOW, + theaterbtn.X, theaterbtn.Y+theaterbtn.Height+24, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + intro.Add_Item(""); + for (VQType v = VQ_FIRST; v < VQ_COUNT; v++) { + intro.Add_Item(VQName[v]); + } + intro.Set_Selected_Index((int)Scen.IntroMovie + 1); + + /* + ** Briefing movie name. + */ + char brieftext[_MAX_FNAME+_MAX_EXT]; + DropListClass briefing(BUTTON_BRIEFING, brieftext, sizeof(brieftext), + TPF_EFNT|TPF_NOSHADOW, + intro.X+intro.Width+10, intro.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + briefing.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + briefing.Add_Item(VQName[v]); + } + briefing.Set_Selected_Index((int)Scen.BriefMovie + 1); + + char actiontext[_MAX_FNAME+_MAX_EXT]; + DropListClass action(BUTTON_ACTION, actiontext, sizeof(actiontext), + TPF_EFNT|TPF_NOSHADOW, + briefing.X+briefing.Width+10, briefing.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + action.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + action.Add_Item(VQName[v]); + } + action.Set_Selected_Index((int)Scen.ActionMovie + 1); + + char wintext[_MAX_FNAME+_MAX_EXT]; + DropListClass win(BUTTON_WIN, wintext, sizeof(wintext), + TPF_EFNT|TPF_NOSHADOW, + action.X+action.Width+10, action.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + win.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + win.Add_Item(VQName[v]); + } + win.Set_Selected_Index((int)Scen.WinMovie + 1); + + char losetext[_MAX_FNAME+_MAX_EXT]; + DropListClass lose(BUTTON_LOSE, losetext, sizeof(losetext), + TPF_EFNT|TPF_NOSHADOW, + win.X+win.Width+10, win.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + lose.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + lose.Add_Item(VQName[v]); + } + lose.Set_Selected_Index((int)Scen.LoseMovie + 1); + + /* + ** House choice list. + */ + ListClass housebtn(BUTTON_HOUSE, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+105, 55, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + } + housebtn.Set_Selected_Index(PlayerPtr->Class->House); + + /* + ** Base house choice drop down list. + */ + char basetext[35]; + DropListClass basebtn(BUTTON_BASE, basetext, sizeof(basetext), + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+80, 65, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + basebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + } + if (Base.House != HOUSE_NONE) { + basebtn.Set_Selected_Index(Base.House); + } + + /* + ** Opening scenario theme. + */ + char themetext[65]; + DropListClass themebtn(BUTTON_THEME, themetext, sizeof(themetext), + TPF_EFNT|TPF_NOSHADOW, + basebtn.X+basebtn.Width+15*RESFACTOR, basebtn.Y, 85, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + themebtn.Add_Item(""); + for (ThemeType th = THEME_FIRST; th < THEME_COUNT; th++) { + themebtn.Add_Item(Theme.Full_Name(th)); + } + if (Scen.TransitTheme != THEME_NONE) { + themebtn.Set_Selected_Index(Scen.TransitTheme+1); + } else { + themebtn.Set_Selected_Index(0); + } + + /* + ** Build level (technology). + */ + SliderClass techlevel(BUTTON_TECH, housebtn.X+housebtn.Width+15, housebtn.Y, 100, 8); + techlevel.Set_Maximum(16); + + char statictechbuff[15]; + StaticButtonClass techstatic(0, "999", TPF_EFNT|TPF_NOSHADOW, techlevel.X+techlevel.Width-20, techlevel.Y-7); + + /* + ** Starting credits. + */ + SliderClass creditbtn(BUTTON_CREDITS, housebtn.X+housebtn.Width+15, techlevel.Y+20, 100, 8); + creditbtn.Set_Maximum(201); + + char staticcreditbuff[15]; + StaticButtonClass creditstatic(0, "999999999", TPF_EFNT|TPF_NOSHADOW, creditbtn.X+creditbtn.Width-50, creditbtn.Y-7); + + /* + ** Maximum unit/infantry slider. + */ + SliderClass maxunit(BUTTON_MAXUNIT, housebtn.X+housebtn.Width+15, creditbtn.Y+20, 100, 8); + maxunit.Set_Maximum(501); + + char staticmaxunitbuff[15]; + StaticButtonClass maxunitstatic(0, "999999", TPF_EFNT|TPF_NOSHADOW, maxunit.X+maxunit.Width-30, maxunit.Y-7); + + /* + ** Source of ground delivery reinforcements. + */ + char sourcetext[25] = ""; + ListClass sourcebtn(BUTTON_SOURCE, + housebtn.X+housebtn.Width+15, maxunit.Y+20, 100, 7*4, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SourceType source = SOURCE_FIRST; source <= SOURCE_WEST; source++) { + sourcebtn.Add_Item(SourceName[source]); + } + + /* + ** Smartness lider. + */ + SliderClass smarties(BUTTON_SMARTIES, sourcebtn.X, sourcebtn.Y+sourcebtn.Height+15, 35, 8); + smarties.Set_Maximum(Rule.MaxIQ+1); + + char staticsmartiesbuff[15]; + StaticButtonClass smartiesstatic(0, "9999", TPF_EFNT|TPF_NOSHADOW, smarties.X+smarties.Width-20, smarties.Y-7); + + /* + ** List box of who is allied with whom. + */ + CheckListClass allies(BUTTON_ALLIES, + techlevel.X+techlevel.Width+5, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (hdata[house].Allies & (1L << h)) { + allies.Check_Item(h, true); + } + } + allies.Set_Selected_Index(0); + + /* + ** List box of who the player can control. + */ + CheckListClass control(BUTTON_CONTROL, + allies.X+allies.Width+10, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + control.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (HouseClass::As_Pointer(h)->IsPlayerControl) { + control.Check_Item(h, true); + } + } + control.Set_Selected_Index(0); + + /* + ** Create the ubiquitous "ok" and "cancel" buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Create the list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + theaterbtn.Add_Tail(*commands); + themebtn.Add_Tail(*commands); + housebtn.Add_Tail(*commands); + techlevel.Add_Tail(*commands); + techstatic.Add_Tail(*commands); + sourcebtn.Add_Tail(*commands); + creditbtn.Add_Tail(*commands); + creditstatic.Add_Tail(*commands); + maxunitstatic.Add_Tail(*commands); + moneytib.Add_Tail(*commands); + smartiesstatic.Add_Tail(*commands); + allies.Add_Tail(*commands); + control.Add_Tail(*commands); + maxunit.Add_Tail(*commands); + nospyplane.Add_Tail(*commands); + skipscore.Add_Tail(*commands); + nomapsel.Add_Tail(*commands); + onetime.Add_Tail(*commands); + inherit.Add_Tail(*commands); + timercarry.Add_Tail(*commands); + tanya.Add_Tail(*commands); + record.Add_Tail(*commands); + truckcrate.Add_Tail(*commands); + endofgame.Add_Tail(*commands); + briefing.Add_Tail(*commands); + intro.Add_Tail(*commands); + action.Add_Tail(*commands); + win.Add_Tail(*commands); + lose.Add_Tail(*commands); + basebtn.Add_Tail(*commands); + smarties.Add_Tail(*commands); + desc.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool housechange = true; + bool display = true; + bool process = true; + bool cancel = false; // true = user cancels + bool dotext = true; // display the text. + bool fetch = false; // Fetch data from dialog into tracking structure. + //Set_Logic_Page(SeenBuff); + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + if (housechange) { + HouseStaticClass * hstatic = &hdata[newhouse]; + creditbtn.Set_Value(hstatic->InitialCredits/100); + techlevel.Set_Value(hstatic->TechLevel); + sourcebtn.Set_Selected_Index(hstatic->Edge); + maxunit.Set_Value(hstatic->MaxUnit + hstatic->MaxInfantry); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Check_Item(h, hstatic->Allies & (1L << h)); + } + smarties.Set_Value(hstatic->IQ); + + house = newhouse; + housechange = false; + display = true; + } + + /* + ** Refresh display if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_SCENARIO_OPTIONS, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Display the text that doesn't need drawing except when the entire dialog + ** needs to be redrawn. + */ + Fancy_Text_Print("Tech Level =", techlevel.X, techlevel.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Credits =", creditbtn.X, creditbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Unit Max =", maxunit.X, maxunit.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("IQ =", smarties.X, smarties.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Prebuild Base:", basebtn.X, basebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Theater:", theaterbtn.X, theaterbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Scenario Name:", desc.X, desc.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Country:", housebtn.X, housebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Home Edge:", sourcebtn.X, sourcebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Allies:", allies.X, allies.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Plyr Control:", control.X, control.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Theme:", themebtn.X, themebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Intro:", intro.X, intro.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Briefing:", briefing.X, briefing.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Action:", action.X, action.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Win:", win.X, win.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Lose:", lose.X, lose.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Store scenario?", record.X+10, record.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Inherit stored scenario?", inherit.X+10, inherit.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Auto evac. Tanya (civilian)?", tanya.X+10, tanya.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Last mission of game?", endofgame.X+10, endofgame.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Inherit mission timer from last scenario?", timercarry.X+10, timercarry.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Disable spy plane?", nospyplane.X+10, nospyplane.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Skip the score screen?", skipscore.X+10, skipscore.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("No map selection (force var 'B')?", nomapsel.X+10, nomapsel.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Return to main menu after scenario finishes?", onetime.X+10, onetime.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Truck carries wood crate?", truckcrate.X+10, truckcrate.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + Fancy_Text_Print("Initial money is transferred to silos?", moneytib.X+10, moneytib.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + + theaterbtn.Collapse(); + themebtn.Collapse(); + intro.Collapse(); + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + commands->Draw_All(true); + Show_Mouse(); + display = false; + dotext = true; + } + + /* + ** Display the text of the buttons that could change their text as a + ** result of slider interaction. + */ + if (dotext) { + dotext = false; + Hide_Mouse(); + + sprintf(statictechbuff, "%2d", techlevel.Get_Value()); + techstatic.Set_Text(statictechbuff); + techstatic.Draw_Me(); + + sprintf(staticcreditbuff, "$%-7d", creditbtn.Get_Value() * 100); + creditstatic.Set_Text(staticcreditbuff); + creditstatic.Draw_Me(); + + sprintf(staticmaxunitbuff, "%4d", maxunit.Get_Value()); + maxunitstatic.Set_Text(staticmaxunitbuff); + maxunitstatic.Draw_Me(); + + sprintf(staticsmartiesbuff, "%2d", smarties.Get_Value()); + smartiesstatic.Set_Text(staticsmartiesbuff); + smartiesstatic.Draw_Me(); + + Show_Mouse(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case BUTTON_ALLIES|KN_BUTTON: + allies.Check_Item(house, true); + break; + + case BUTTON_CONTROL|KN_BUTTON: + control.Check_Item(house, true); + break; + + case BUTTON_THEME|KN_BUTTON: + case BUTTON_INTRO|KN_BUTTON: + case BUTTON_BRIEFING|KN_BUTTON: + case BUTTON_ACTION|KN_BUTTON: + case BUTTON_WIN|KN_BUTTON: + case BUTTON_LOSE|KN_BUTTON: + case BUTTON_BASE|KN_BUTTON: + case LIST_THEATER|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + themebtn.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + display = true; + break; + + case BUTTON_SMARTIES|KN_BUTTON: + case BUTTON_MAXUNIT|KN_BUTTON: + case BUTTON_CREDITS|KN_BUTTON: + case BUTTON_TECH|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + themebtn.Collapse(); + theaterbtn.Collapse(); + dotext = true; + break; + + case BUTTON_HOUSE|KN_BUTTON: + newhouse = HousesType(housebtn.Current_Index()); + housechange = true; + briefing.Collapse(); + action.Collapse(); + themebtn.Collapse(); + win.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + fetch = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + fetch = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + if (fetch) { + fetch = false; + HouseStaticClass * hstatic = &hdata[house]; + + Base.House = HousesType(basebtn.Current_Index()); + hstatic->InitialCredits = creditbtn.Get_Value() * 100; + hstatic->Edge = SourceType(sourcebtn.Current_Index()); + hstatic->TechLevel = techlevel.Get_Value(); + hstatic->MaxUnit = maxunit.Get_Value()/2; + hstatic->MaxInfantry = maxunit.Get_Value()/2; + hstatic->IQ = smarties.Get_Value(); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if (allies.Is_Checked(h)) { + hstatic->Allies |= (1L << h); + } else { + hstatic->Allies &= ~(1L << h); + } + } + } + } + + /* + ** Redraw the map + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Copy the dialog data back into the appropriate game data locations. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr != NULL) { + hptr->Control = hdata[h]; + hptr->Allies = hdata[h].Allies; + + if (control.Is_Checked(h)) { + hptr->IsPlayerControl = true; + } else { + hptr->IsPlayerControl = false; + } + } + } + PlayerPtr->IsPlayerControl = true; + strncpy(Scen.Description, desc.Get_Text(), sizeof(Scen.Description)); + Scen.Description[sizeof(Scen.Description)-1] = '\0'; + Scen.IntroMovie = VQType(intro.Current_Index()-1); + Scen.BriefMovie = VQType(briefing.Current_Index()-1); + Scen.ActionMovie = VQType(action.Current_Index()-1); + Scen.WinMovie = VQType(win.Current_Index()-1); + Scen.LoseMovie = VQType(lose.Current_Index()-1); + Scen.IsToInherit = inherit.IsOn; + Scen.IsToCarryOver = record.IsOn; + Scen.IsTanyaEvac = tanya.IsOn; + Scen.IsEndOfGame = endofgame.IsOn; + Scen.IsInheritTimer = timercarry.IsOn; + Scen.IsNoSpyPlane = nospyplane.IsOn; + Scen.IsSkipScore = skipscore.IsOn; + Scen.IsNoMapSel = nomapsel.IsOn; + Scen.IsOneTimeOnly = onetime.IsOn; + Scen.IsTruckCrate = truckcrate.IsOn; + Scen.IsMoneyTiberium = moneytib.IsOn; + Scen.TransitTheme = ThemeType(themebtn.Current_Index()-1); + + /* + ** Change the theater: + ** - 1st set the Theater global + ** - scan all cells to check their TType for compatibility with the new + ** theater; if not compatible, set TType to TEMPLATE_NONE & TIcon to 0 + ** - Then, re-initialize the TypeClasses for the new Theater + */ + TheaterType theater = TheaterType(theaterbtn.Current_Index()); + if (theater != orig_theater) { + + unsigned char theater_mask; // template/terrain mask + TerrainClass * terrain; // cell's terrain pointer + + /* + ** Loop through all cells + */ + for (CELL i = 0; i < MAP_CELL_TOTAL; i++) { + + /* + ** If this cell has a template icon & that template isn't compatible + ** with this theater, set the icon to NONE + */ + if ((*this)[i].TType != TEMPLATE_NONE) { + theater_mask = TemplateTypeClass::As_Reference((*this)[i].TType).Theater; + if ( (theater_mask & (1 << theater))==0) { + (*this)[i].TType = TEMPLATE_NONE; + (*this)[i].TIcon = 0; + } + } + + /* + ** If this cell has terrain in it, and that terrain isn't compatible + ** with this theater, delete the terrain object. + */ + terrain = (*this)[i].Cell_Terrain(); + if (terrain != NULL) { + theater_mask = terrain->Class->Theater; + if ( (theater_mask & (1<Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + + /* + ** 'New' + */ + if (rc==2) { + + /* + ** Create a new trigger + */ + CurTrigger = new TriggerTypeClass(); + if (CurTrigger) { + + /* + ** delete it if user cancels + */ + if (!CurTrigger->Edit()) { + delete CurTrigger; + CurTrigger = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more triggers available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTrigger) { + Detach_This_From_All(CurTrigger->As_Target(), true); + delete CurTrigger; + //CurTrigger->Remove(); + CurTrigger = NULL; + Changed = 1; + } + } + } + + /* + ** Let the CurTrigger global exist if the trigger can be placed on the + ** ground or on a game object. + */ + if (CurTrigger && !(CurTrigger->Attaches_To() & (ATTACH_OBJECT|ATTACH_CELL))) { + CurTrigger = NULL; + } +} + + +/*************************************************************************** + * MapEditClass::Select_Trigger -- lets user select a trigger * + * * + * CurTrigger can be NULL when this function is called. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name Event Action House Team ³³ ³ * + * ³ ³ Name Event Action House Team ÃÄ´ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sort trigger list. * + *=========================================================================*/ +int MapEditClass::Select_Trigger(void) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_TXT8_H = 11, + D_MARGIN = 35, + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-10, + D_LIST_H = D_DIALOG_H-70, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 25, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + }; + + /* + ** Button enumerations: + */ + enum { + TRIGGER_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables: + */ + bool edit_trig = false; // true = user wants to edit + bool new_trig = false; // true = user wants to new + bool del_trig = false; // true = user wants to new + int i; // loop counter + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TListClass > triggerlist(TRIGGER_LIST, D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + TextButtonClass editbtn(BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + TextButtonClass newbtn(BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + TextButtonClass deletebtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in the list box + */ + for (i = 0; i < TriggerTypes.Count(); i++) { + triggerlist.Add_Item(TriggerTypes.Ptr(i)); + } + + PNBubble_Sort(&triggerlist[0], triggerlist.Count()); + + if (CurTrigger) { + triggerlist.Set_Selected_Index(CurTrigger); + } else { + triggerlist.Set_Selected_Index(0); + } + + /* + ** Set CurTrigger if it isn't + */ + if (TriggerTypes.Count()==0) { + CurTrigger = NULL; + } else { + CurTrigger = triggerlist.Current_Item(); +// if (!CurTrigger) { +// CurTrigger = &*triggerlist.Current_Item(); +// } + } + + /* + ** Create the list + */ + commands = &triggerlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if requested. + */ + if (display /*&& LogicPage->Lock()*/) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + commands->Flag_List_To_Redraw(); + commands->Draw_All(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + CurTrigger = &*triggerlist.Current_Item(); +// CurTrigger = (TriggerTypeClass *)&*triggerlist.Current_Item(); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTrigger) { // only allow if there's one selected + process = false; + edit_trig = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_trig = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_trig = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (edit_trig) return(1); + if (new_trig) return(2); + if (del_trig) return(3); + return(0); +} + + +#endif diff --git a/CODE/MAPEDIT.CPP b/CODE/MAPEDIT.CPP new file mode 100644 index 0000000..9e26329 --- /dev/null +++ b/CODE/MAPEDIT.CPP @@ -0,0 +1,2173 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDIT.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : MAPEDIT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor overloaded routines & utility routines * + *-------------------------------------------------------------------------* + * Map Editor modules: * + * (Yes, they're all one huge class.) * + * mapedit.cpp: overloaded routines, utility routines * + * mapeddlg.cpp: map editor dialogs, most of the main menu options * + * mapedplc.cpp: object-placing routines * + * mapedsel.cpp: object-selection & manipulation routines * + * mapedtm.cpp: team-editing routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::AI -- The map editor's main logic * + * MapEditClass::Read_INI -- overloaded Read_INI function * + * MapEditClass::AI_Menu -- menu of AI options * + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * MapEditClass::Clear_List -- clears the internal chooseable object list* + * MapEditClass::Cycle_House -- finds next valid house for object type * + * MapEditClass::Draw_It -- overloaded Redraw routine * + * MapEditClass::Fatal -- exits with error message * + * MapEditClass::Main_Menu -- main menu processor for map editor * + * MapEditClass::MapEditClass -- class constructor * + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * MapEditClass::One_Time -- one-time initialization * + * MapEditClass::Verify_House -- sees if given house can own given obj * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + +/* +** Array of all missions supported by the map editor +*/ +MissionType MapEditClass::MapEditMissions[] = { + MISSION_GUARD, + MISSION_STICKY, + MISSION_HARMLESS, + MISSION_HARVEST, + MISSION_GUARD_AREA, + MISSION_RETURN, + MISSION_AMBUSH, + MISSION_HUNT, + MISSION_SLEEP, +}; +#define NUM_EDIT_MISSIONS (sizeof(MapEditClass::MapEditMissions) / sizeof(MapEditClass::MapEditMissions[0])) + + +/* +** For menu processing +*/ +extern int UnknownKey; // in menus.cpp + +char MapEditClass::HealthBuf[20]; + + +/*************************************************************************** + * MapEditClass::MapEditClass -- class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +MapEditClass::MapEditClass(void) +{ + /* + ** Init data members. + */ +// ScenVar = SCEN_VAR_A; + ObjCount = 0; + LastChoice = 0; + LastHouse = HOUSE_GOOD; + GrabbedObject = 0; + for (int i=0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + TypeOffset[i] = 0; + } + Scen.Waypoint[WAYPT_HOME] = 0; + CurrentCell = 0; + CurTeam = NULL; + CurTrigger = NULL; + Changed = 0; + LMouseDown = 0; + BaseBuilding = false; +// BasePercent = 100; +} + + +/*************************************************************************** + * MapEditClass::One_Time -- one-time initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/02/1995 BR : Created. * + *=========================================================================*/ +void MapEditClass::One_Time(void) +{ + MouseClass::One_Time(); + + /* + ** The map: a single large "button" + */ +#ifdef WIN32 + MapArea = new ControlClass(MAP_AREA, 0, 8, 640-8, 400-8, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#else + MapArea = new ControlClass(MAP_AREA, 0, 8, 312, 192, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#endif + + /* + ** House buttons + */ + HouseList = new ListClass(POPUP_HOUSELIST, POPUP_HOUSE_X, POPUP_HOUSE_Y, POPUP_HOUSE_W, POPUP_HOUSE_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseList->Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + + /* + ** The mission list box + */ + MissionList = new ListClass(POPUP_MISSIONLIST, + POPUP_MISSION_X, POPUP_MISSION_Y, POPUP_MISSION_W, POPUP_MISSION_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int i = 0; i < NUM_EDIT_MISSIONS; i++) { + MissionList->Add_Item(MissionClass::Mission_Name(MapEditMissions[i])); + } + + /* + ** The health bar + */ + HealthGauge = new TriColorGaugeClass(POPUP_HEALTHGAUGE, + POPUP_HEALTH_X, POPUP_HEALTH_Y, POPUP_HEALTH_W, POPUP_HEALTH_H); + HealthGauge->Use_Thumb(true); + HealthGauge->Set_Maximum(0x100); + HealthGauge->Set_Red_Limit(0x3f - 1); + HealthGauge->Set_Yellow_Limit(0x7f - 1); + + /* + ** The health text label + */ + HealthBuf[0] = 0; + HealthText = new TextLabelClass(HealthBuf, + POPUP_HEALTH_X + POPUP_HEALTH_W / 2, + POPUP_HEALTH_Y + POPUP_HEALTH_H + 1, + GadgetClass::Get_Color_Scheme(), + TPF_CENTER | TPF_FULLSHADOW | TPF_EFNT); + + /* + ** Building attribute buttons. + */ + Sellable = new TextButtonClass(POPUP_SELLABLE, TXT_SELLABLE, TPF_EBUTTON, 320-65, 200-25, 60); + Rebuildable = new TextButtonClass(POPUP_REBUILDABLE, TXT_REBUILD, TPF_EBUTTON, 320-65, 200-15, 60); + + /* + ** The facing dial + */ + FacingDial = new Dial8Class(POPUP_FACINGDIAL, POPUP_FACEBOX_X, + POPUP_FACEBOX_Y, POPUP_FACEBOX_W, POPUP_FACEBOX_H, (DirType)0); + + /* + ** The base percent-built slider & its label + */ + BaseGauge = new GaugeClass(POPUP_BASEPERCENT, POPUP_BASE_X, POPUP_BASE_Y, POPUP_BASE_W, POPUP_BASE_H); + BaseLabel = new TextLabelClass ("Base:", POPUP_BASE_X - 3, POPUP_BASE_Y, GadgetClass::Get_Color_Scheme(), + TPF_RIGHT | TPF_NOSHADOW | TPF_EFNT); + BaseGauge->Set_Maximum(100); + BaseGauge->Set_Value(Scen.Percent); +} + + +/*********************************************************************************************** + * MapeditClass::Init_IO -- Reinitializes the radar map at scenario start. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Init_IO(void) +{ + /* + ** For normal game mode, jump to the parent's Init routine. + */ + if (!Debug_Map) { + + MouseClass::Init_IO(); + + } else { + + /* + ** For editor mode, add the map area to the button input list + */ + Buttons = 0; + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + } +} + + +/*************************************************************************** + * MapEditClass::Clear_List -- clears the internal chooseable object list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Clear_List(void) +{ + /* + ** Set # object type ptrs to 0, set NumType for each type to 0 + */ + ObjCount = 0; + for (int i = 0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + } +} + + +/*************************************************************************** + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * * + * Use this routine to add an object to the game object selection list. * + * This list is used by the Add_Object function. All items located in the * + * list will appear and be chooseable by that function. Make sure to * + * clear the list before adding a sequence of items to it. Clearing * + * the list is accomplished by the Clear_List() function. * + * * + * INPUT: * + * object ptr to ObjectTypeClass to add * + * * + * OUTPUT: * + * bool: was the object added to the list? A failure could occur if * + * NULL were passed in or the list is full. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/04/1994 JLB : Created. * + *=========================================================================*/ +bool MapEditClass::Add_To_List(ObjectTypeClass const * object) +{ + /* + ** Add the object if there's room. + */ + if (object && ObjCount < MAX_EDIT_OBJECTS) { + Objects[ObjCount++] = object; + + /* + ** Update type counters. + */ + switch (object->What_Am_I()) { + case RTTI_TEMPLATETYPE: + NumType[0]++; + break; + + case RTTI_OVERLAYTYPE: + NumType[1]++; + break; + + case RTTI_SMUDGETYPE: + NumType[2]++; + break; + + case RTTI_TERRAINTYPE: + NumType[3]++; + break; + + case RTTI_UNITTYPE: + NumType[4]++; + break; + + case RTTI_INFANTRYTYPE: + NumType[5]++; + break; + + case RTTI_VESSELTYPE: + NumType[6]++; + break; + + case RTTI_BUILDINGTYPE: + NumType[7]++; + break; + + case RTTI_AIRCRAFTTYPE: + NumType[8]++; + break; + } + return(true); + } + + return(false); +} + + +/*************************************************************************** + * MapEditClass::AI -- The map editor's main logic * + * * + * This routine overloads the parent's (DisplayClass) AI function. * + * It checks for any input specific to map editing, and calls the parent * + * AI routine to handle scrolling and other mainstream map stuff. * + * * + * If this detects one of its special input keys, it sets 'input' to 0 * + * before calling the parent AI routine; this prevents input conflict. * + * * + * SUPPORTED INPUT: * + * General: * + * F2/RMOUSE: main menu * + * F6: toggles show-passable mode * + * HOME: go to the Home Cell (scenario's start position)* + * SHIFT-HOME: set the Home Cell to the current TacticalCell* + * ESC: exits to DOS * + * Object Placement: * + * INSERT: go into placement mode * + * ESC: exit placement mode * + * LEFT/RIGHT: prev/next placement object * + * PGUP/PGDN: prev/next placement category * + * HOME: 1st placement object (clear template) * + * h/H: toggle house of placement object * + * LMOUSE: place the placement object * + * MOUSE MOTION: "paint" with the placement object * + * Object selection: * + * LMOUSE: select & "grab" current object * + * If no object is present where the mouse is * + * clicked, the current object is de-selected * + * If the same object is clicked on, it stays * + * selected. Also displays the object-editing * + * gadgets. * + * LMOUSE RLSE: release currently-grabbed object * + * MOUSE MOTION: if an object is grabbed, moves the object * + * SHIFT|ALT|ARROW: moves object in that direction * + * DELETE deletes currently-selected object * + * Object-editing controls: * + * POPUP_GDI: makes GDI the owner of this object * + * POPUP_NOD: makes NOD the owner of this object * + * POPUP_MISSIONLIST: sets that mission for this object * + * POPUP_HEALTHGAUGE: sets that health value for this object * + * POPUP_FACINGDIAL: sets the object's facing * + * * + * Changed is set when you: * + * - place an object * + * - move a grabbed object * + * - delete an object * + * - size the map * + * - create a new scenario * + * Changed is cleared when you: * + * - Save the scenario * + * - Load a scenario * + * - Play the scenario * + * * + * INPUT: * + * input KN_ value, 0 if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI(KeyNumType & input, int x, int y) +{ + int rc; + MissionType mission; + int strength; + CELL cell; + int i; + int found; // for removing a waypoint label + int waypt_idx; // for labelling a waypoint + BaseNodeClass * node; // for removing from an AI Base + HousesType house; + char wayname[4]; + + /* + ** Trap 'F2' regardless of whether we're in game or editor mode + */ + if (Debug_Flag) { + if ((input == KN_F2 && Session.Type == GAME_NORMAL) || input == (KN_F2 | KN_CTRL_BIT)) { + ScenarioInit = 0; + + /* + ** If we're in editor mode & Changed is set, prompt for saving changes + */ + if (Debug_Map && Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort game + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + } else { + Changed = 0; + Go_Editor(!Debug_Map); + } + } else { + + /* + ** User doesn't want to save + */ + Go_Editor(!Debug_Map); + } + } else { + /* + ** If we're in game mode, set Changed to 0 (so if we didn't save our + ** changes above, they won't keep coming back to haunt us with continual + ** Save Changes? prompts!) + */ + if (!Debug_Map) { + Changed = 0; + } + BaseGauge->Set_Value(Scen.Percent); + Go_Editor(!Debug_Map); + } + } + } + + /* + ** For normal game mode, jump to the parent's AI routine. + */ + if (!Debug_Map) { + MouseClass::AI(input, x, y); + return; + } + + ::Frame++; + + /* + ** Do special mouse processing if the mouse is over the map + */ + if (Get_Mouse_X() > TacPixelX && Get_Mouse_X() < + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() > TacPixelY && Get_Mouse_Y() < + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + /* + ** When the mouse moves over a scrolling edge, ScrollClass changes its + ** shape to the appropriate arrow or NO symbol; it's our job to change it + ** back to normal (or whatever the shape is set to by Set_Default_Mouse()) + ** when it re-enters the map area. + */ + if (CurTrigger) { + Override_Mouse_Shape(MOUSE_CAN_MOVE); + } else { + Override_Mouse_Shape(MOUSE_NORMAL); + } + } + + /* + ** Set 'ZoneCell' to track the mouse cursor around over the map. Do this + ** even if the map is scrolling. + */ + if (Get_Mouse_X() >= TacPixelX && Get_Mouse_X() <= + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() >= TacPixelY && Get_Mouse_Y() <= + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + cell = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + if (cell != -1) { + Set_Cursor_Pos(cell); + if (PendingObject) { + Flag_To_Redraw(true); + } + } + } + + /* + ** Check for mouse motion while left button is down. + */ + rc = Mouse_Moved(); + if (LMouseDown && rc) { + + /* + ** "Paint" mode: place current object, and restart placement + */ + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Move the currently-grabbed object + */ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } + } + } + } + + /* + ** Trap special editing keys; if one is detected, set 'input' to 0 to + ** prevent a conflict with parent's AI(). + */ + switch (input) { + /* + ** F2/RMOUSE = pop up main menu + */ + case KN_RMOUSE: + + /* + ** Turn off placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + } + + /* + ** Turn off trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + } + + /* + ** Unselect object & hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + Main_Menu(); + input = KN_NONE; + break; + + /* + ** F6 = toggle passable/impassable display + */ + case KN_F6: + Debug_Passable = (Debug_Passable == false); + HidPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /* + ** INSERT = go into object-placement mode + */ + case KN_INSERT: + if (!PendingObject) { + /* + ** Unselect current object, hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + + /* + ** Go into placement mode + */ + Start_Placement(); + } + input = KN_NONE; + break; + + /* + ** ESC = exit placement mode, or exit to DOS + */ + case KN_ESC: + + /* + ** Exit object placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + input = KN_NONE; + break; + } else { + + /* + ** Exit trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + input = KN_NONE; + break; + } else { + rc = WWMessageBox().Process("Exit Scenario Editor?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User doesn't want to exit; return to editor + */ + if (rc==1) { + input = KN_NONE; + break; + } + + /* + ** If changed, prompt for saving + */ + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort exit + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + break; + } else { + Changed = 0; + } + } + } + } + } + //Prog_End(); + Emergency_Exit(0); + break; + + /* + ** LEFT = go to previous placement object + */ + case KN_LEFT: + if (PendingObject) { + Place_Prev(); + } + input = KN_NONE; + break; + + /* + ** RIGHT = go to next placement object + */ + case KN_RIGHT: + if (PendingObject) { + Place_Next(); + } + input = KN_NONE; + break; + + /* + ** PGUP = go to previous placement category + */ + case KN_PGUP: + if (PendingObject) { + Place_Prev_Category(); + } + input = KN_NONE; + break; + + /* + ** PGDN = go to next placement category + */ + case KN_PGDN: + if (PendingObject) { + Place_Next_Category(); + } + input = KN_NONE; + break; + + /* + ** HOME = jump to first placement object, or go to Home Cell + */ + case KN_HOME: + if (PendingObject) { + Place_Home(); + } else { + + /* + ** Set map position + */ + ScenarioInit++; + Set_Tactical_Position(Scen.Waypoint[WAYPT_HOME]); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + input = KN_NONE; + break; + + /* + ** SHIFT-HOME: set new Home Cell position + */ + case ((int)KN_HOME | (int)KN_SHIFT_BIT): + if (CurrentCell != 0) { + + /* + ** Unflag the old Home Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_HOME]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_HOME && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + + /* + ** Now set the new Home cell + */ +// Scen.Waypoint[WAYPT_HOME] = Coord_Cell(TacticalCoord); +// (*this)[TacticalCoord].IsWaypoint = 1; +// Flag_Cell(Coord_Cell(TacticalCoord)); + Scen.Waypoint[WAYPT_HOME] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + + Changed = 1; + input = KN_NONE; +} + break; + + /* + ** SHIFT-R: set new Reinforcement Cell position. Don't allow setting + ** the Reinf. Cell to the same as the Home Cell (for display purposes.) + */ + case ((int)KN_R | (int)KN_SHIFT_BIT): + if (CurrentCell==0 || CurrentCell==Scen.Waypoint[WAYPT_HOME]) { + break; + } + + /* + ** Unflag the old Reinforcement Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_REINF]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_REINF && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + /* + ** Now set the new Reinforcement cell + */ + Scen.Waypoint[WAYPT_REINF] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + Changed = 1; + input = KN_NONE; + break; + + /* + ** ALT-Letter: Label a waypoint cell + */ + case ((int)KN_A | (int)KN_ALT_BIT): + case ((int)KN_B | (int)KN_ALT_BIT): + case ((int)KN_C | (int)KN_ALT_BIT): + case ((int)KN_D | (int)KN_ALT_BIT): + case ((int)KN_E | (int)KN_ALT_BIT): + case ((int)KN_F | (int)KN_ALT_BIT): + case ((int)KN_G | (int)KN_ALT_BIT): + case ((int)KN_H | (int)KN_ALT_BIT): + case ((int)KN_I | (int)KN_ALT_BIT): + case ((int)KN_J | (int)KN_ALT_BIT): + case ((int)KN_K | (int)KN_ALT_BIT): + case ((int)KN_L | (int)KN_ALT_BIT): + case ((int)KN_M | (int)KN_ALT_BIT): + case ((int)KN_N | (int)KN_ALT_BIT): + case ((int)KN_O | (int)KN_ALT_BIT): + case ((int)KN_P | (int)KN_ALT_BIT): + case ((int)KN_Q | (int)KN_ALT_BIT): + case ((int)KN_R | (int)KN_ALT_BIT): + case ((int)KN_S | (int)KN_ALT_BIT): + case ((int)KN_T | (int)KN_ALT_BIT): + case ((int)KN_U | (int)KN_ALT_BIT): + case ((int)KN_V | (int)KN_ALT_BIT): + case ((int)KN_W | (int)KN_ALT_BIT): + case ((int)KN_X | (int)KN_ALT_BIT): + case ((int)KN_Y | (int)KN_ALT_BIT): + case ((int)KN_Z | (int)KN_ALT_BIT): + if (CurrentCell != 0) { +#ifdef WIN32 + waypt_idx = (input & ~KN_ALT_BIT) - KN_A; +#else + waypt_idx = KN_To_KA(input & 0x00ff) - KA_a; +#endif + Update_Waypoint(waypt_idx); + } + input = KN_NONE; + break; + + /* + ** ALT-. : Designate an extended (2-letter) waypoint name + */ + case KN_PERIOD: + case ((int)KN_PERIOD | (int)KN_ALT_BIT): + if (CurrentCell != 0 && Get_Waypoint_Name(wayname)) { + int waynm = 0; + if (strlen(wayname)) { + wayname[0] = toupper(wayname[0]); + wayname[1] = toupper(wayname[1]); + if (wayname[0] >= 'A' && wayname[0] <= 'Z') { + waynm = wayname[0] - 'A'; + if (wayname[1] >= 'A' && wayname[1] <= 'Z') { + waynm = (waynm+1)*26 + (wayname[1] - 'A'); + } + if (waynm < WAYPT_HOME) Update_Waypoint(waynm); + } + } + } + input = KN_NONE; + break; + +#ifdef OBSOLETE + /* + ** ALT-1-4: Designate a cell as a capture-the-flag cell. + */ + case ((int)KN_1 | (int)KN_ALT_BIT): + case ((int)KN_2 | (int)KN_ALT_BIT): + case ((int)KN_3 | (int)KN_ALT_BIT): + case ((int)KN_4 | (int)KN_ALT_BIT): + + /* + ** If there's a current cell, place the flag & waypoint there. + */ + if (CurrentCell != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); +// waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house)) { + HouseClass::As_Pointer(house)->Flag_Attach(CurrentCell, true); + } + } else { + + /* + ** If there's a current object, attach the flag to it and clear the + ** waypoint. + */ + if (CurrentObject[0] != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house) && CurrentObject[0]->What_Am_I() == RTTI_UNIT) { + HouseClass::As_Pointer(house)->Flag_Attach((UnitClass *)CurrentObject[0], true); + } + } + } + input = KN_NONE; + break; +#endif + + /* + ** ALT-Space: Remove a waypoint designation + */ + case ((int)KN_SPACE | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + + /* + ** Loop through letter waypoints; if this cell is one of them, + ** clear that waypoint. + */ + for (i = 0 ; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i]==CurrentCell) + Scen.Waypoint[i] = -1; + } + + /* + ** Loop through flag home values; if this cell is one of them, clear + ** that waypoint. + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(HOUSE_MULTI1 + i); + if (HouseClass::As_Pointer(house) && + CurrentCell == HouseClass::As_Pointer(house)->FlagHome) + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(CurrentCell), true); + } + + /* + ** If there are no more waypoints on this cell, clear the cell's + ** waypoint designation. + */ + if (Scen.Waypoint[WAYPT_HOME]!=CurrentCell && + Scen.Waypoint[WAYPT_REINF]!=CurrentCell) + (*this)[CurrentCell].IsWaypoint = 0; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /* + ** 'H' = toggle current placement object's house + */ + case KN_H: + case ((int)KN_H | (int)KN_SHIFT_BIT): + if (PendingObject) { + Toggle_House(); + } + input = KN_NONE; + break; + + /* + ** Left-mouse click: + ** Button DOWN: + ** - Toggle LMouseDown + ** - If we're in placement mode, try to place the current object + ** - If success, re-enter placement mode + ** - Otherwise, try to select an object, and "grab" it if there is one + ** - If no object, then select that cell as the "current" cell + ** Button UP: + ** - Toggle LMouseDown + ** - release any grabbed object + */ + case ((int)MAP_AREA | (int)KN_BUTTON): + + /* + ** Left Button DOWN + */ + if (Keyboard->Down(KN_LMOUSE)) { + LMouseDown = 1; + + /* + ** Placement mode: place an object + */ + if (PendingObject) { + if (Place_Object()==0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Place a trigger + */ + if (CurTrigger) { + Place_Trigger(); + Changed = 1; + } else { + + /* + ** Select an object or a cell + ** Check for double-click + */ + if (CurrentObject.Count() && + ((TickCount - LastClickTime) < 15)) { + ; // stub + + } else { + /* + ** Single-click: select object + */ + if (Select_Object()==0) { + CurrentCell = 0; + Grab_Object(); + } else { + + /* + ** No object: select the cell + */ + CurrentCell = Click_Cell_Calc(Keyboard->MouseQX, Keyboard->MouseQY); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + } + } + LastClickTime = TickCount(); + input = KN_NONE; + } else { + + /* + ** Left Button UP + */ + LMouseDown = 0; + GrabbedObject = 0; + input = KN_NONE; + } + break; + + /* + ** SHIFT-ALT-Arrow: move the current object + */ + case (int)KN_UP | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_DOWN | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_LEFT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_RIGHT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + CurrentObject[0]->Move(KN_To_Facing(input)); + Changed = 1; + } + input = KN_NONE; + break; + + /* + ** DELETE: delete currently-selected object + */ + case KN_DELETE: + + /* + ** Delete currently-selected object's trigger, or the object + */ + if (CurrentObject.Count()) { + + /* + ** Delete trigger + */ + if (CurrentObject[0]->Trigger.Is_Valid()) { + CurrentObject[0]->Trigger = NULL; + } else { + /* + ** If the current object is part of the AI's Base, remove it + ** from the Base's Node list. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && + Base.Is_Node((BuildingClass *)CurrentObject[0])) { + node = Base.Get_Node((BuildingClass *)CurrentObject[0]); + Base.Nodes.Delete(*node); + } + + /* + ** Delete current object + */ + delete CurrentObject[0]; + + /* + ** Hide the popup controls + */ + Popup_Controls(); + } + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } else { + + /* + ** Remove trigger from current cell + */ + if (CurrentCell) { + if ((*this)[CurrentCell].Trigger.Is_Valid()) { + (*this)[CurrentCell].Trigger = NULL; +// CellTriggers[CurrentCell] = NULL; + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + } + input = KN_NONE; + break; + + /* + ** TAB: select next object on the map + */ + case KN_TAB: + Select_Next(); + input = KN_NONE; + break; + + /* + ** Object-Editing button: House Button + */ + case POPUP_HOUSELIST|KN_BUTTON: + /* + ** Determine the house desired by examining the currently + ** selected index in the house list gadget. + */ + house = HousesType(((ListClass *)Buttons->Extract_Gadget(POPUP_HOUSELIST))->Current_Index()); + + /* + ** If that house doesn't own this object, try to transfer it + */ + if (CurrentObject[0]->Owner() != house) { + if (Change_House(house)) { + Changed = 1; + } + } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); + HidPage.Clear(); + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + +// case (POPUP_GDI | KN_BUTTON): +// case (POPUP_NOD | KN_BUTTON): +// case (POPUP_NEUTRAL | KN_BUTTON): +// case (POPUP_MULTI1 | KN_BUTTON): +// case (POPUP_MULTI2 | KN_BUTTON): +// case (POPUP_MULTI3 | KN_BUTTON): +// case (POPUP_MULTI4 | KN_BUTTON): +// +// /* +// ** Convert input value into a house value; assume HOUSE_GOOD is 0 +// */ +// house = (HousesType)( (input & (~KN_BUTTON)) - POPUP_FIRST); +// +// /* +// ** If that house doesn't own this object, try to transfer it +// */ +// if (CurrentObject[0]->Owner()!=house) { +// if (Change_House(house)) { +// Changed = 1; +// } +// } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// input = KN_NONE; +// break; + + case POPUP_SELLABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsAllowedToSell = (building->IsAllowedToSell == false); + building->Mark(MARK_CHANGE); + } + if (building->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + } + break; + + case POPUP_REBUILDABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsToRebuild = (building->IsToRebuild == false); + building->Mark(MARK_CHANGE); + } + if (building->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + } + break; + + /* + ** Object-Editing button: Mission + */ + case (POPUP_MISSIONLIST | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ** Set new mission + */ + mission = MapEditMissions[MissionList->Current_Index()]; + if (CurrentObject[0]->Get_Mission() != mission) { + ((TechnoClass *)CurrentObject[0])->Set_Mission(mission); + Changed = 1; + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Health + */ + case (POPUP_HEALTHGAUGE | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Derive strength from current gauge reading + */ + strength = CurrentObject[0]->Class_Of().MaxStrength * fixed(HealthGauge->Get_Value(), 256); +// strength = Fixed_To_Cardinal((unsigned)CurrentObject[0]->Class_Of().MaxStrength, (unsigned)HealthGauge->Get_Value()); + + /* + ** Clip to 1 + */ + if (strength <= 0) { + strength = 1; + } + + /* + ** Set new strength + */ + if (strength != CurrentObject[0]->Strength) { + CurrentObject[0]->Strength = strength; + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + + /* + ** Update text label + */ + sprintf(HealthBuf, "%d", strength); + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_FACINGDIAL | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Set new facing + */ + if (FacingDial->Get_Direction() != + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Get()) { + + /* + ** Set body's facing + */ + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Set(FacingDial->Get_Direction()); + + /* + ** Set turret facing, if there is one + */ + if (CurrentObject[0]->What_Am_I()==RTTI_UNIT) { + ((UnitClass *)CurrentObject[0])->SecondaryFacing.Set(FacingDial->Get_Direction()); + } + + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_BASEPERCENT | KN_BUTTON): + if (BaseGauge->Get_Value() != Scen.Percent) { + Scen.Percent = BaseGauge->Get_Value(); + Build_Base_To(Scen.Percent); + HidPage.Clear(); + Flag_To_Redraw(true); + } + input = KN_NONE; + break; + + default: + break; + } + + /* + ** Call parent's AI routine + */ + MouseClass::AI(input, x, y); +} + + +/*************************************************************************** + * MapEditClass::Draw_It -- overloaded Redraw routine * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Draw_It(bool forced) +{ + char const * label; + char buf[40]; + char const * tptr; + + MouseClass::Draw_It(forced); + + if (!Debug_Map) { + return; + } + + /* + ** Display the total value of all Tiberium on the map. + */ + Fancy_Text_Print("Tiberium=%ld ", 0, 0, GadgetClass::Get_Color_Scheme(), + BLACK, TPF_EFNT | TPF_NOSHADOW, TotalValue); + + /* + ** If there are no object controls displayed, just invoke parent's Redraw + ** and return. + */ + if (!Buttons) { + return; + } + + /* + ** Otherwise, if 'display' is set, invoke the parent's Redraw to refresh + ** the HIDPAGE; then, update the buttons & text labels onto HIDPAGE; + ** then invoke the parent's Redraw to blit the HIDPAGE to SEENPAGE. + */ + if (forced) { + + /* + ** Update the text labels + */ + if (CurrentObject.Count()) { + /* + ** Display the object's name & ID + */ + label = Text_String(CurrentObject[0]->Full_Name()); + tptr = label; + sprintf(buf, "%s (%d)", tptr, CurrentObject[0]->As_Target()); + + /* + ** print the label + */ + Fancy_Text_Print (buf, 160, 0, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_EFNT); + } + } +} + + +/*************************************************************************** + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * * + * Reports whether the mouse has moved or not. This varies based on the * + * type of object currently selected. If there's an infantry object * + * selected, mouse motion counts even within a cell; for all other types,* + * mouse motion counts only if the mouse changes cells. * + * * + * The reason this routine is needed is to prevent Paint-Mode from putting* + * gobs of trees and such into the same cell if the mouse moves just * + * a little bit. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Mouse_Moved(void) +{ + static int old_mx = 0; + static int old_my = 0; + static CELL old_zonecell = 0; + const ObjectTypeClass * objtype = NULL; + bool retcode = false; + + /* + ** Return if no motion + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { + return(false); + } + + /* + ** Get a ptr to ObjectTypeClass + */ + if (PendingObject) { + objtype = PendingObject; + } else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } + + /* + ** Infantry: mouse moved if any motion at all + */ + if (objtype->What_Am_I() == RTTI_INFANTRYTYPE) { + retcode = true; + } else { + /* + ** Others: mouse moved only if cell changed + */ + if (old_zonecell!=ZoneCell) { + retcode = true; + } else { + retcode = false; + } + } + + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(retcode); +} + + +/*************************************************************************** + * MapEditClass::Main_Menu -- main menu processor for map editor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Main_Menu(void) +{ + char const * _menus[MAX_MAIN_MENU_NUM + 1]; + int selection; // option the user picks + bool process; // menu stays up while true + int rc; + + /* + ** Fill in menu items + */ + _menus[0] = "New Scenario"; + _menus[1] = "Load Scenario"; + _menus[2] = "Save Scenario"; + _menus[3] = "Size Map"; + _menus[4] = "Add Game Object"; + _menus[5] = "Scenario Options"; + _menus[6] = "AI Options"; + _menus[7] = "Play Scenario"; + _menus[8] = NULL; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + ** Process selection + */ + switch (selection) { + + /* + ** New scenario + */ + case 0: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (New_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 1; + } + process = false; + break; + + /* + ** Load scenario + */ + case 1: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (Load_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 0; + } + process = false; + break; + + /* + ** Save scenario + */ + case 2: + if (Save_Scenario() == 0) { + Changed = 0; + } + process = false; + break; + + /* + ** Edit map size + */ + case 3: + if (Size_Map(MapCellX, MapCellY, MapCellWidth, MapCellHeight)==0) { + process = false; + Changed = 1; + } + break; + + /* + ** Add an object + */ + case 4: + if (Placement_Dialog() == 0) { + Start_Placement(); + process = false; + } + break; + + /* + ** Scenario options + */ + case 5: + if (Scenario_Dialog() == 0) { + Changed = 1; + process = false; + } + break; + + /* + ** Other options + */ + case 6: + AI_Menu(); + process = false; + break; + + /* + ** Test-drive this scenario + */ + case 7: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO, TXT_CANCEL); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc == 2) return; + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + Changed = 0; + Debug_Map = false; + Start_Scenario(Scen.ScenarioName); + return; + } + } + + /* + ** Restore the display: + ** - Clear HIDPAGE to erase any spurious drawing done by the menu system + ** - Invoke Flag_To_Redraw to tell DisplayClass to re-render the whole screen + ** - Invoke Redraw() to update the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::AI_Menu -- menu of AI options * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + char const * _menus[MAX_AI_MENU_NUM + 1]; + + /* + ** Fill in menu strings + */ + _menus[0] = "Pre-Build a Base"; + _menus[1] = "Edit Triggers"; + _menus[2] = "Edit Teams"; + _menus[3] = NULL; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + ** Process selection + */ + switch (selection) { + /* + ** Pre-Build a Base + */ + case 0: + Start_Base_Building(); + process = false; + break; + + /* + ** Trigger Editing + */ + case 1: + Handle_Triggers(); + /* + ** Go into trigger placement mode + */ + if (CurTrigger) { + Start_Trigger_Placement(); + } + process = false; + break; + + /* + ** Team Editing + */ + case 2: + Handle_Teams("Teams"); + process = false; + break; + } + } +} + + +/*************************************************************************** + * MapEditClass::Verify_House -- is this objtype ownable by this house? * + * * + * INPUT: * + * house house to check * + * objtype ObjectTypeClass to check * + * * + * OUTPUT: * + * 0 = isn't ownable, 1 = it is * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Verify_House(HousesType house, ObjectTypeClass const * objtype) +{ + /* + ** Verify that new house can own this object + */ + return((objtype->Get_Ownable() & (1 << house)) != 0); +} + + +/*************************************************************************** + * MapEditClass::Cycle_House -- finds next valid house for object type * + * * + * INPUT: * + * objtype ObjectTypeClass ptr to get house for * + * curhouse current house value to start with * + * * + * OUTPUT: * + * HousesType that's valid for this object type * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +HousesType MapEditClass::Cycle_House(HousesType curhouse, ObjectTypeClass const *) +{ + HousesType count; // prevents an infinite loop + + /* + ** Loop through all house types, starting with the one after 'curhouse'; + ** return the first one that's valid + */ + count = HOUSE_NONE; + while (1) { + + /* + ** Go to next house + */ + curhouse++; + if (curhouse == HOUSE_COUNT) { + curhouse = HOUSE_FIRST; + } + + /* + ** Count # iterations; don't go forever + */ + count++; + if (count == HOUSE_COUNT) { + curhouse = HOUSE_NONE; + break; + } + + /* + ** Break if this is a valid house + */ +// if (HouseClass::As_Pointer(curhouse) && Verify_House(curhouse, objtype)) { + break; +// } + } + + return(curhouse); +} + + +/*************************************************************************** + * MapEditClass::Fatal -- exits with error message * + * * + * INPUT: * + * code tells which message to display; this minimizes the * + * use of character strings in the code. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Fatal(int txt) +{ + //Prog_End(); + printf("%s\n", txt); + Emergency_Exit(EXIT_FAILURE); +} + + +bool MapEditClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (Debug_Map) { + /* + ** The popup gadgets require the entire map to be redrawn if we scroll. + */ + if (really) { + Flag_To_Redraw(true); + } + } + return(MouseClass::Scroll_Map(facing, distance, really)); +} + + +#ifdef OBSOLETE +void MapEditClass::Flag_To_Redraw(bool complete) +{ + MouseClass::Flag_To_Redraw(complete); +} +#endif + + +void MapEditClass::Detach(ObjectClass * object) +{ + if (GrabbedObject == object) { + GrabbedObject = 0; + } +} + +bool MapEditClass::Get_Waypoint_Name(char wayptname[]) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 100, // dialog width + D_DIALOG_H = 56, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_EDIT_W = D_DIALOG_W - (D_MARGIN * 2), + D_EDIT_H = 13, + D_EDIT_X = D_DIALOG_X + D_MARGIN, + D_EDIT_Y = D_DIALOG_Y + 20, + + D_BUTTON_X = D_DIALOG_X + D_MARGIN, + D_BUTTON_Y = D_DIALOG_Y + 40, + D_BUTTON_W = 40, + D_BUTTON_H = 13, + + D_CANCEL_X = D_DIALOG_X + 53, + D_CANCEL_Y = D_DIALOG_Y + 40, + D_CANCEL_W = 40, + D_CANCEL_H = 13, + + }; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_EDIT, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + wayptname[0] = 0; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TextButtonClass button (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_BUTTON_X, D_BUTTON_Y, D_BUTTON_W); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W); + EditClass editbtn (BUTTON_EDIT, wayptname, 3, TPF_EFNT|TPF_NOSHADOW, D_EDIT_X, D_EDIT_Y, D_EDIT_W, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + + /* + ** Main Processing Loop. + */ + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else if (Main_Loop()) { + process = false; + cancel = true; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); +// Draw_Caption(caption, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN) { + input = (KeyNumType)(BUTTON_OK|KN_BUTTON); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_OK | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + process = false; + cancel = false; + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + cancel = true; + process = false; + break; + + default: + break; + } + } + + Map.Flag_To_Redraw(true); + if (cancel) return(false); + + return(true); +} + +void MapEditClass::Update_Waypoint(int waypt_idx) +{ + CELL cell; + + /* + ** Unflag cell for this waypoint if there is one + */ + cell = Scen.Waypoint[waypt_idx]; + if (cell != -1) { + if (Scen.Waypoint[WAYPT_HOME] != cell && Scen.Waypoint[WAYPT_REINF] != cell) { + (*this)[cell].IsWaypoint = 0; + } + Flag_Cell(cell); + } + Scen.Waypoint[waypt_idx] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Changed = 1; + Flag_Cell(CurrentCell); +} + + +/*************************************************************************** + * MapEditClass::Read_INI -- overloaded Read_INI function * + * * + * Overloading this function gives the map editor a chance to initialize * + * certain values every time a new INI is read. * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Read_INI(CCINIClass & ini) +{ + /* + ** Invoke parent's Read_INI + */ + Mono_Printf("We are in Read_INI\n"); + + MouseClass::Read_INI(ini); + BaseGauge->Set_Value(Scen.Percent); + + Mono_Clear_Screen(); + Mono_Printf("Scen.Percent = %d", Scen.Percent); + +// BaseGauge->Set_Value(Scen.Percent); +} + + +void MapEditClass::Write_INI(CCINIClass & ini) +{ + MouseClass::Write_INI(ini); +// ini.Put_Int("Basic", "Percent", Scen.Percent); +} + +#endif + +#include "mapedsel.cpp" + diff --git a/CODE/MAPEDIT.H b/CODE/MAPEDIT.H new file mode 100644 index 0000000..58fb3fe --- /dev/null +++ b/CODE/MAPEDIT.H @@ -0,0 +1,344 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDIT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MAPEDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 14, 1994 * + * * + * Last Update : May 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * This class is derived from the normal display map class. It exists * + * only to allow editing and adding items to the map. * + *---------------------------------------------------------------------------------------------* + * House-setting functions: The editor contains several house maintenance routines: * + * Verify_House: tells if the given ObjectType can be owned by the given HousesType * + * Cycle_House: Finds the next valid house for the given ObjectType; used when a new object * + * can't be owned by the current editor HousesType. * + * Change_House: attempts to change the owner of the currently-selected object * + * Toggle_House: cycles the HousesType of a pending placement object * + * Set_House_Buttons: sets house buttons in accordance with the given HousesType * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAPEDIT_H +#define MAPEDIT_H + +#include "function.h" + +/* +** This is the maximum # of ObjectTypeClasses the editor has to deal with. +*/ +enum MapEdit1Enum { + MAX_EDIT_OBJECTS = // max # of ObjectTypeClasses allowed + (int)TEMPLATE_COUNT + + (int)OVERLAY_COUNT + + (int)SMUDGE_COUNT + + (int)TERRAIN_COUNT + + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)VESSEL_COUNT + + (int)STRUCT_COUNT, + + MAX_TEAM_CLASSES = // max # ObjectTypeClasses for a team + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT, + +// NUM_EDIT_MISSIONS = 6, // # missions that can be assigned an object + + NUM_EDIT_CLASSES = 9, // # different classes (templates, terrain, etc) + + MAX_MAIN_MENU_NUM = 8, + MAX_MAIN_MENU_LEN = 20, + + MAX_AI_MENU_NUM = 6, + MAX_AI_MENU_LEN = 20, + + POPUP_HOUSE_X=10, + POPUP_HOUSE_Y=100, + POPUP_HOUSE_W=60, + POPUP_HOUSE_H=(190-100), + +// POPUP_GDI_W = 50, +// POPUP_GDI_H = 9, +// POPUP_GDI_X = 10, +// POPUP_GDI_Y = 160, + +// POPUP_NOD_W = 50, +// POPUP_NOD_H = 9, +// POPUP_NOD_X = 10, +// POPUP_NOD_Y = 169, + +// POPUP_NEUTRAL_W = 50, +// POPUP_NEUTRAL_H = 9, +// POPUP_NEUTRAL_X = 10, +// POPUP_NEUTRAL_Y = 178, + +// POPUP_MULTI1_W = 25, +// POPUP_MULTI1_H = 9, +// POPUP_MULTI1_X = 10, +// POPUP_MULTI1_Y = 160, + +// POPUP_MULTI2_W = 25, +// POPUP_MULTI2_H = 9, +// POPUP_MULTI2_X = 35, +// POPUP_MULTI2_Y = 160, + +// POPUP_MULTI3_W = 25, +// POPUP_MULTI3_H = 9, +// POPUP_MULTI3_X = 10, +// POPUP_MULTI3_Y = 169, + +// POPUP_MULTI4_W = 25, +// POPUP_MULTI4_H = 9, +// POPUP_MULTI4_X = 35, +// POPUP_MULTI4_Y = 169, + + POPUP_MISSION_W = 80, + POPUP_MISSION_H = 40, + POPUP_MISSION_X = 70, + POPUP_MISSION_Y = 150, + + POPUP_FACEBOX_W = 30, + POPUP_FACEBOX_H = 30, + POPUP_FACEBOX_X = 160, + POPUP_FACEBOX_Y = 160, + + POPUP_HEALTH_W = 50, + POPUP_HEALTH_H = 10, + POPUP_HEALTH_X = 200, + POPUP_HEALTH_Y = 170, + + POPUP_BASE_W = 50, + POPUP_BASE_H = 8, + POPUP_BASE_X = 300 - 50, + POPUP_BASE_Y = 0 +}; + +/* +** These are the button ID's for the pop-up object-editing gizmos. +** The house button ID's must be sequential, with a 1-to-1 correspondence to +** the HousesType values. +*/ +enum MapEditButtonIDEnum{ + POPUP_SPAIN=500, + POPUP_FIRST=POPUP_SPAIN, + POPUP_GREECE, + POPUP_USSR, + POPUP_ENGLAND, + POPUP_ITALY, + POPUP_GERMANY, + POPUP_FRANCE, + POPUP_TURKEY, + POPUP_HOUSELIST, // House selection list. + POPUP_SELLABLE, // Allowed to sell. + POPUP_REBUILDABLE, // Allowed to rebuild. + POPUP_MISSIONLIST, // list box for missions + POPUP_HEALTHGAUGE, // health of object + POPUP_FACINGDIAL, // object's facing + POPUP_BASEPERCENT, // Base's percent-built slider + MAP_AREA, // map as a click-able thingy + BUTTON_FLAG=0x8000 +}; + + +class TeamTypeClass; + +class MapEditClass : public MouseClass +{ + /* + ** Public Interface + */ + public: + + /* + ** mapedit.cpp + */ + MapEditClass(void); + MapEditClass(NoInitClass const & x) : MouseClass(x) {}; + bool Get_Waypoint_Name(char wayptname[]); + void Update_Waypoint(int waypt_index); + + virtual void One_Time(void); // One-time init + virtual void Init_IO(void); // Inits button list + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool forced = true); + virtual bool Scroll_Map(DirType facing, int & distance, bool really=true); + virtual void Read_INI(CCINIClass & ini); + virtual void Write_INI(CCINIClass & ini); + virtual void Detach(ObjectClass * object); + void Detach(TARGET target, bool all=true) {MouseClass::Detach(target, all);} + void Clear_List(void); + bool Add_To_List(ObjectTypeClass const *object); + void Main_Menu(void); + void AI_Menu(void); + bool Mouse_Moved(void); + bool Verify_House(HousesType house, ObjectTypeClass const * objtype); + HousesType Cycle_House(HousesType curhouse, ObjectTypeClass const * objtype); +// int Trigger_Needs_Team(TriggerClass *trigger); + void Fatal(int txt); + + /* + ** mapeddlg.cpp + */ + int New_Scenario(void); + int Load_Scenario(void); + int Save_Scenario(void); + int Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp); + int Size_Map(int x, int y, int w, int h); + int Scenario_Dialog(void); + void Handle_Triggers(void); + int Select_Trigger(void); + + /* + ** mapedplc.cpp + */ + int Placement_Dialog(void); + void Start_Placement(void); + int Place_Object(void); + void Cancel_Placement(void); + void Place_Next(void); + void Place_Prev(void); + void Place_Next_Category(void); + void Place_Prev_Category(void); + void Place_Home(void); + void Toggle_House(void); + void Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id); + void Start_Trigger_Placement(void); + void Stop_Trigger_Placement(void); + void Place_Trigger(void); + void Start_Base_Building(void); + void Cancel_Base_Building(void); + void Build_Base_To(int percent); + + /* + ** mapedsel.cpp + */ + int Select_Object(void); + void Select_Next(void); + void Popup_Controls(void); + void Grab_Object(void); + int Move_Grabbed_Object(void); + bool Change_House(HousesType newhouse); + + /* + ** mapedtm.cpp + */ + void Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house); + void Handle_Teams(char const * caption); + int Select_Team(char const * caption); + int Team_Members(HousesType house); + + /* + ** Private Interface + */ + private: + /* + ** This is the last-requested variation of a loaded/saved/new scenario. + */ +// ScenarioVarType ScenVar; + + /* + ** Array of all TypeClasses the user can add to the map; cleared by + ** Clear_List(), added to by Add_To_List() + */ + ObjectTypeClass const * Objects[MAX_EDIT_OBJECTS]; + int ObjCount; // # of objects in the Objects array + + /* + ** Last-selected object to place, and last-selected house of object + */ + int LastChoice; // index of item user picked last + HousesType LastHouse; // house of last item picked + + /* + ** Variables for grabbing/moving objects + */ + ObjectClass * GrabbedObject; // object "grabbed" with mouse + CELL GrabOffset; // offset to grabbed obj's upper-left + unsigned long LastClickTime; // time of last LMOUSE click + + /* + ** Number of each type of object in Objects, so we can switch categories + */ + int NumType[NUM_EDIT_CLASSES]; // # of each type of class: + // 0 = Template + // 1 = Overlay + // 2 = Smudge + // 3 = Terrain + // 4 = Unit + // 5 = Infantry + // 6 = Vessels + // 7 = Building + // 8 = Aircraft + + /* + ** The offset of each type of object within the Objects[] array + */ + int TypeOffset[NUM_EDIT_CLASSES]; // offsets within Objects[] + + /* + ** The "current" trigger for point-and-click trigger setting + */ + TriggerTypeClass * CurTrigger; // current trigger + + /* + ** The "current" team type for editing & associating with a trigger + */ + TeamTypeClass * CurTeam; // current team + + /* + ** Bitfields for flags & such + */ + unsigned Changed : 1; // 1 = changes are unsaved + unsigned LMouseDown : 1; // 1 = left mouse is held down + unsigned BaseBuilding : 1; // 1 = we're in base-building mode + + /* + ** Variables for pre-building a base + */ +// int BasePercent; // Percentage the base will be built + + /* + ** Variables for supporting the object-editing controls at screen bottom + */ + ListClass * HouseList; + ListClass * MissionList; + TriColorGaugeClass *HealthGauge; + Dial8Class *FacingDial; + ControlClass *MapArea; + TextLabelClass *HealthText; + TextButtonClass * Sellable; + TextButtonClass * Rebuildable; + static char HealthBuf[20]; + GaugeClass *BaseGauge; + TextLabelClass *BaseLabel; + static MissionType MapEditMissions[]; +}; + +#endif diff --git a/CODE/MAPEDPLC.CPP b/CODE/MAPEDPLC.CPP new file mode 100644 index 0000000..c9c7b82 --- /dev/null +++ b/CODE/MAPEDPLC.CPP @@ -0,0 +1,1824 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDPLC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : MAPEDPLC.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-placement routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Build_Base_To -- builds the AI base to the given percent* + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * MapEditClass::Cancel_Placement -- cancels placement mode * + * MapEditClass::Place_Home -- homes the placement object * + * MapEditClass::Place_Next -- while placing object, goes to next * + * MapEditClass::Place_Next_Category -- places next object category * + * MapEditClass::Place_Object -- attempts to place the current object * + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * MapEditClass::Place_Prev_Category -- places previous object category * + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * MapEditClass::Start_Base_Building -- starts base-building mode * + * MapEditClass::Start_Placement -- enters placement mode * + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode* + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * MapEditClass::Toggle_House -- toggles current placement object's house* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * * + * This function sets LastChoice & LastHouse to the values chosen * + * by the user. It's up to the caller to call Start_Placement to enter * + * placement mode. * + * This routine does not modify PendingObject or PendingHouse. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ [GDI] [NOD] [Neutral] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ [Template] ³ * + * ³ ³ ³ [Overlay ] ³ * + * ³ ³ ³ [Smudge ] ³ * + * ³ ³ ³ [Terrain ] ³ * + * ³ ³ (Object picture) ³ [Unit ] ³ * + * ³ ³ ³ [Infantry] ³ * + * ³ ³ ³ [Aircraft] ³ * + * ³ ³ ³ [Building] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄ¿ ³ * + * ³ [<-] [->] ³(Grid)³ ³ * + * ³ ³ ³ ³ * + * ³ [OK] [Cancel] ÀÄÄÄÄÄÄÙ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 12/13/1995 JLB : Fixed house buttons to handle expanded house list. * + * 05/12/1996 JLB : Handles hi-res. * + *=========================================================================*/ +int MapEditClass::Placement_Dialog(void) +{ + HousesType house; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 180, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 11, + D_MARGIN = 7, + + D_PICTURE_W = 152, // must be divisible by 8! + D_PICTURE_H = 105, + D_PICTURE_X = D_DIALOG_X + 35, // must start on a byte boundary! + D_PICTURE_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + D_PICTURE_CX = D_PICTURE_X + D_PICTURE_W / 2, + + D_GDI_W = 65, + D_GDI_H = 9, + D_GDI_X = D_PICTURE_X+D_PICTURE_W+5, + D_GDI_Y = D_PICTURE_Y, + + D_LEFT_W = 45, + D_LEFT_H = 9, + D_LEFT_X = D_PICTURE_CX - 5 - D_LEFT_W, + D_LEFT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_RIGHT_W = 45, + D_RIGHT_H = 9, + D_RIGHT_X = D_PICTURE_CX + 5, + D_RIGHT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_TEMPLATE_W = 70, + D_TEMPLATE_H = 9, + D_TEMPLATE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TEMPLATE_W - 30, + D_TEMPLATE_Y = D_PICTURE_Y, + + D_OVERLAY_W = 70, + D_OVERLAY_H = 9, + D_OVERLAY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_OVERLAY_W - 30, + D_OVERLAY_Y = D_TEMPLATE_Y + D_TEMPLATE_H, + + D_SMUDGE_W = 70, + D_SMUDGE_H = 9, + D_SMUDGE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_SMUDGE_W - 30, + D_SMUDGE_Y = D_OVERLAY_Y + D_OVERLAY_H, + + D_TERRAIN_W = 70, + D_TERRAIN_H = 9, + D_TERRAIN_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TERRAIN_W - 30, + D_TERRAIN_Y = D_SMUDGE_Y + D_SMUDGE_H, + + D_UNIT_W = 70, + D_UNIT_H = 9, + D_UNIT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_UNIT_W - 30, + D_UNIT_Y = D_TERRAIN_Y + D_TERRAIN_H, + + D_INFANTRY_W = 70, + D_INFANTRY_H = 9, + D_INFANTRY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_INFANTRY_W - 30, + D_INFANTRY_Y = D_UNIT_Y + D_UNIT_H, + + D_AIRCRAFT_W = 70, + D_AIRCRAFT_H = 9, + D_AIRCRAFT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIRCRAFT_W - 30, + D_AIRCRAFT_Y = D_INFANTRY_Y + D_INFANTRY_H, + + D_BUILDING_W = 70, + D_BUILDING_H = 9, + D_BUILDING_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_BUILDING_W - 30, + D_BUILDING_Y = D_AIRCRAFT_Y + D_AIRCRAFT_H, + + D_AIR_W = 70, + D_AIR_H = 9, + D_AIR_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIR_W - 30, + D_AIR_Y = D_BUILDING_Y + D_BUILDING_H, + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_PICTURE_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN - 15, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_PICTURE_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN - 15, + + GRIDSIZE = 10, + GRIDBLOCK_W = 3, + GRIDBLOCK_H = 3, + D_GRID_X = D_DIALOG_X + D_DIALOG_W - (GRIDSIZE * GRIDBLOCK_W) - D_MARGIN - 35, + D_GRID_Y = D_DIALOG_Y + D_DIALOG_H - (GRIDSIZE * GRIDBLOCK_H) - D_MARGIN - 35, + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_GDI=100, + BUTTON_HOUSE, + BUTTON_NEXT, + BUTTON_PREV, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_TEMPLATE, + BUTTON_OVERLAY, + BUTTON_SMUDGE, + BUTTON_TERRAIN, + BUTTON_UNIT, + BUTTON_INFANTRY, + BUTTON_AIRCRAFT, + BUTTON_BUILDING, + BUTTON_AIR, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + const ObjectTypeClass * curobj; // Working object pointer. + int x,y; // for drawing the grid + KeyNumType input; // user input + short const * occupy; // ptr into object's OccupyList + int cell; // cell index for parsing OccupyList + int i; + int typeindex; // index of class type + + /* + ** Buttons + */ + ControlClass * commands; + + ListClass housebtn(BUTTON_HOUSE, + D_GDI_X, D_GDI_Y, 60, 8*16, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + house = HOUSE_FIRST; + + TextButtonClass nextbtn(BUTTON_NEXT, TXT_RIGHT, TPF_EBUTTON, D_RIGHT_X, D_RIGHT_Y, D_RIGHT_W, D_RIGHT_H); + TextButtonClass prevbtn(BUTTON_PREV, TXT_LEFT, TPF_EBUTTON, D_LEFT_X, D_LEFT_Y, D_LEFT_W, D_LEFT_H); + TextButtonClass okbtn(BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, "Cancel", TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + TextButtonClass templatebtn(BUTTON_TEMPLATE, "Template", TPF_EBUTTON, D_TEMPLATE_X, D_TEMPLATE_Y, D_TEMPLATE_W, D_TEMPLATE_H); + TextButtonClass overlaybtn(BUTTON_OVERLAY, "Overlay", TPF_EBUTTON, D_OVERLAY_X, D_OVERLAY_Y, D_OVERLAY_W, D_OVERLAY_H); + TextButtonClass smudgebtn(BUTTON_SMUDGE, "Smudge", TPF_EBUTTON, D_SMUDGE_X, D_SMUDGE_Y, D_SMUDGE_W, D_SMUDGE_H); + TextButtonClass terrainbtn(BUTTON_TERRAIN, "Terrain", TPF_EBUTTON, D_TERRAIN_X, D_TERRAIN_Y, D_TERRAIN_W, D_TERRAIN_H); + TextButtonClass unitbtn(BUTTON_UNIT, "Unit", TPF_EBUTTON, D_UNIT_X, D_UNIT_Y, D_UNIT_W, D_UNIT_H); + TextButtonClass infantrybtn(BUTTON_INFANTRY, "Infantry", TPF_EBUTTON, D_INFANTRY_X, D_INFANTRY_Y, D_INFANTRY_W, D_INFANTRY_H); + TextButtonClass aircraftbtn(BUTTON_AIRCRAFT, "Ships", TPF_EBUTTON, D_AIRCRAFT_X, D_AIRCRAFT_Y, D_AIRCRAFT_W, D_AIRCRAFT_H); + TextButtonClass buildingbtn(BUTTON_BUILDING, "Building", TPF_EBUTTON, D_BUILDING_X, D_BUILDING_Y, D_BUILDING_W, D_BUILDING_H); + TextButtonClass airbtn(BUTTON_AIR, "Aircraft", TPF_EBUTTON, D_AIR_X, D_AIR_Y, D_AIR_W, D_AIR_H); + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. (Skip aircraft, since they won't be used in the editor.) + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + /* + ** Compute offset of each class type in the Objects array + */ + TypeOffset[0] = 0; + for (i = 1; i < NUM_EDIT_CLASSES; i++) { + TypeOffset[i] = TypeOffset[i-1] + NumType[i-1]; + } + + /* + ** Return if no objects to place + */ + if (!ObjCount) { + return(-1); + } + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + if (LastChoice >= ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; // current object to choose + + commands = &nextbtn; + housebtn.Add_Tail(*commands); + prevbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + templatebtn.Add_Tail(*commands); + overlaybtn.Add_Tail(*commands); + smudgebtn.Add_Tail(*commands); + terrainbtn.Add_Tail(*commands); + unitbtn.Add_Tail(*commands); + infantrybtn.Add_Tail(*commands); + aircraftbtn.Add_Tail(*commands); + buildingbtn.Add_Tail(*commands); + airbtn.Add_Tail(*commands); + + /* + ** Make sure the recorded house selection matches the house list + ** box selection. + */ + LastHouse = HousesType(housebtn.Current_Index()); + + /* + ** Main processing loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_PLACE_OBJECT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Display the current object: + ** - save the current window dimensions + ** - adjust the window size to the actual drawable area + ** - draw the shape + ** - reset the window dimensions + */ + WindowList[WINDOW_EDITOR][WINDOWX] = D_PICTURE_X; + WindowList[WINDOW_EDITOR][WINDOWY] = D_PICTURE_Y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + Draw_Box(D_PICTURE_X, D_PICTURE_Y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, false); + curobj->Display(WinW/2, WinH>>1, WINDOW_EDITOR, LastHouse); +// curobj->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, LastHouse); + + /* + ** Erase the grid + */ + LogicPage->Fill_Rect(D_GRID_X - GRIDBLOCK_W * 2, D_GRID_Y, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, BLACK); + + /* + ** Draw a box for every cell occupied + */ + occupy = curobj->Occupy_List(); + while ( (*occupy) != REFRESH_EOL) { + cell = (*occupy); + occupy++; + x = D_GRID_X + ((cell % MAP_CELL_W) * GRIDBLOCK_W); + y = D_GRID_Y + ((cell / MAP_CELL_W) * GRIDBLOCK_H); + LogicPage->Fill_Rect(x, y, x + GRIDBLOCK_W - 1, y + GRIDBLOCK_H - 1, scheme->Bright); + } + + /* + ** Draw the grid itself + */ + for (y = 0; y <= GRIDSIZE; y++) { + for (x = 0; x <= GRIDSIZE; x++) { + LogicPage->Draw_Line(D_GRID_X + x * GRIDBLOCK_W, D_GRID_Y, + D_GRID_X + x * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, scheme->Shadow); + } + LogicPage->Draw_Line(D_GRID_X, D_GRID_Y + y * GRIDBLOCK_H, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, D_GRID_Y + y * GRIDBLOCK_H, + scheme->Shadow); + } + + /* + ** Print the object's label from the class's Full_Name(). + ** Warning: Text_String returns an EMS pointer, so standard string + ** functions won't work! + */ + Fancy_Text_Print (curobj->Full_Name(), + D_PICTURE_CX, D_PICTURE_Y + D_MARGIN, scheme, TBLACK, + TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw buttons + ** Figure out which class category we're in & highlight that button + ** This updates 'typeindex', which is used below, and it also updates + ** the category button states. + */ + i = 0; + for (typeindex = 0; typeindex < NUM_EDIT_CLASSES; typeindex++) { + i += NumType[typeindex]; + if (LastChoice < i) break; + } + templatebtn.Turn_Off(); + overlaybtn.Turn_Off(); + smudgebtn.Turn_Off(); + terrainbtn.Turn_Off(); + unitbtn.Turn_Off(); + infantrybtn.Turn_Off(); + aircraftbtn.Turn_Off(); + airbtn.Turn_Off(); + buildingbtn.Turn_Off(); + switch (typeindex + BUTTON_TEMPLATE) { + case BUTTON_TEMPLATE: + templatebtn.Turn_On(); + break; + + case BUTTON_OVERLAY: + overlaybtn.Turn_On(); + break; + + case BUTTON_SMUDGE: + smudgebtn.Turn_On(); + break; + + case BUTTON_TERRAIN: + terrainbtn.Turn_On(); + break; + + case BUTTON_UNIT: + unitbtn.Turn_On(); + break; + + case BUTTON_INFANTRY: + infantrybtn.Turn_On(); + break; + + case BUTTON_AIRCRAFT: + aircraftbtn.Turn_On(); + break; + + case BUTTON_AIR: + airbtn.Turn_On(); + break; + + case BUTTON_BUILDING: + buildingbtn.Turn_On(); + break; + } + + /* + ** Redraw buttons + */ + commands->Draw_All(); + Show_Mouse(); + display = false; + + } + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process user input + */ + switch (input) { + + /* + ** GDI House + */ + case (BUTTON_HOUSE | KN_BUTTON): + house = HousesType(housebtn.Current_Index()); + + /* + ** Set flags & buttons + */ + LastHouse = house; + display = true; + break; + + /* + ** Next in list + */ + case (KN_RIGHT): + case (BUTTON_NEXT | KN_BUTTON): + /* + ** Increment to next obj + */ + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; + + nextbtn.Turn_Off(); + display = true; + break; + + /* + ** Previous in list + */ + case (KN_LEFT): + case (BUTTON_PREV | KN_BUTTON): + + /* + ** Decrement to prev obj + */ + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount-1; + } + curobj = Objects[LastChoice]; + prevbtn.Turn_Off(); + display = true; + break; + + /* + ** Select a class type + */ + case (BUTTON_TEMPLATE | KN_BUTTON): + case (BUTTON_OVERLAY | KN_BUTTON): + case (BUTTON_SMUDGE | KN_BUTTON): + case (BUTTON_TERRAIN | KN_BUTTON): + case (BUTTON_UNIT | KN_BUTTON): + case (BUTTON_INFANTRY | KN_BUTTON): + case (BUTTON_AIRCRAFT | KN_BUTTON): + case (BUTTON_BUILDING | KN_BUTTON): + case (BUTTON_AIR | KN_BUTTON): + + /* + ** Find index of class + */ + typeindex = input - (BUTTON_TEMPLATE | KN_BUTTON); + + /* + ** If no objects of that type, do nothing + */ + if (NumType[typeindex]==0) { + display = true; + break; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Next category + */ + case KN_PGDN: + typeindex++; + if (typeindex==NUM_EDIT_CLASSES) { + typeindex = 0; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Previous category + */ + case KN_PGUP: + typeindex--; + if (typeindex < 0) { + typeindex = NUM_EDIT_CLASSES - 1; + } + + /* + ** Set current object + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** Jump to 1st choice + */ + case KN_HOME: + LastChoice = 0; + + /* + ** Set current object + */ + curobj = Objects[LastChoice]; + display = true; + break; + + /* + ** OK + */ + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /* + ** Cancel + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Start_Placement -- enters placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Placement(void) +{ + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. These must be added in the same order expected by the + ** object selection dialog (same as button order). + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + /* + ** Compute offset of each class type in the Objects array + */ + TypeOffset[0] = 0; + for (int i = 1; i < NUM_EDIT_CLASSES; i++) { + TypeOffset[i] = TypeOffset[i-1] + NumType[i-1]; + } + + /* + ** Create the placement object: + ** - For normal placement mode, use the last-used index into Objects + ** (LastChoice), and the last-used house (LastHouse). + ** - For base-building mode, force the object to be a building, and use the + ** House specified in the Base object + */ + if (!BaseBuilding) { + if (LastChoice >= ObjCount) { + LastChoice = ObjCount - 1; + } + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } else { + if (LastChoice < TypeOffset[7]) { + LastChoice = TypeOffset[7]; + } + if (LastChoice >= ObjCount) { + LastChoice = ObjCount - 1; + } + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse = Base.House; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } + + /* + ** Error if no more objects available + */ + if (!PendingObjectPtr) { + WWMessageBox().Process("No more objects of this type available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + PendingObject = NULL; + if (BaseBuilding) { + Cancel_Base_Building(); + } + return; + } + + /* + ** Set the placement cursor + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(PendingObject->Occupy_List()); +} + + +/*************************************************************************** + * MapEditClass::Place_Object -- attempts to place the current object * + * * + * Placement of "real" objects is simply checked via their Unlimbo routine.* + * Placement of templates is more complex: * + * - for every cell in the template's OccupyList, check for objects * + * already in that cell by looking at the cell's OccupyList & * + * OverlapList * + * - "lift" all the objects in the cell by Mark'ing them * + * - temporarily place the template in that cell * + * - try to Unlimbo all the objects that were in the cell. If any * + * objects fail to Unlimbo onto that template, the template cannot * + * be placed here * + * * + * It is assumed that the object being placed is a "new" object; the * + * object's strength & mission are not set during Unlimbo. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = unable to place * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Place_Object(void) +{ + CELL template_cell; // cell being checked for template + COORDINATE obj_coord; // coord of occupier object + int okflag; // OK to place a template? + short const * occupy; // ptr into template's OccupyList + ObjectClass * occupier; // occupying object + TemplateType save_ttype; // for saving cell's TType + unsigned char save_ticon; // for saving cell's TIcon +// BaseNodeClass node; // for adding to an AI Base + + /* + ** Placing a template: + ** - first lift up any objects in the cell + ** - place the template, and try to replace the objects; if they won't go + ** back, the template can't go there + */ + //ScenarioInit++; + if (PendingObject->What_Am_I() == RTTI_TEMPLATETYPE) { + + /* + ** Loop through all cells this template will occupy + */ + okflag = true; + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Check this cell for an occupier + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + if ((*this)[template_cell].Cell_Occupier()) { + occupier = (*this)[template_cell].Cell_Occupier(); + + /* + ** Save object's coordinates + */ + obj_coord = occupier->Coord; + + /* + ** Place the object in limbo + */ + occupier->Mark(MARK_UP); + + /* + ** Set the cell's template values + */ + save_ttype = (*this)[template_cell].TType; + save_ticon = (*this)[template_cell].TIcon; + (*this)[template_cell].TType = + ((TemplateTypeClass *)PendingObject)->Type; + (*this)[template_cell].TIcon = Cell_X(*occupy) + Cell_Y(*occupy) * + ((TemplateTypeClass *)PendingObject)->Width; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Try to put the object back down + */ + if (occupier->Can_Enter_Cell(Coord_Cell(obj_coord)) != MOVE_OK) { + okflag = false; + } + + /* + ** Put everything back the way it was + */ + (*this)[template_cell].TType = save_ttype; + (*this)[template_cell].TIcon = save_ticon; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Major error if can't replace the object now + */ + occupier->Mark(MARK_DOWN); + } + occupy++; + } + + /* + ** If it's still OK after ALL THAT, place the template + */ + if (okflag) { + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Loop through all cells occupied by this template, and clear the + ** smudge & overlay. + */ + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Get cell for this occupy item + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + + /* + ** Clear smudge & overlay + */ + (*this)[template_cell].Overlay = OVERLAY_NONE; + (*this)[template_cell].OverlayData = 0; + (*this)[template_cell].Smudge = SMUDGE_NONE; + + /* + ** make adjacent cells recalc attrib's + */ + (*this)[template_cell].Recalc_Attributes(); + (*this)[template_cell].Wall_Update(); + (*this)[template_cell].Concrete_Calc(); + + occupy++; + } + + /* + ** Set flags etc + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + TotalValue = Overpass(); + Flag_To_Redraw(false); + return(0); + } + + /* + ** Failure to deploy results in a returned failure code. + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Not OK; return error + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Placing infantry: Infantry can go into cell sub-positions, so find the + ** sub-position closest to the mouse & put him there + */ + if (PendingObject->What_Am_I() == RTTI_INFANTRYTYPE) { + + /* + ** Find cell sub-position + */ + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + obj_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + } else { + obj_coord = NULL; + } + + /* + ** No free spots; don't place the object + */ + if (obj_coord == NULL) { + //ScenarioInit--; + return(-1); + } + + /* + ** Unlimbo the object + */ + if (PendingObjectPtr->Unlimbo(obj_coord)) { + ((InfantryClass *)PendingObjectPtr)->Set_Occupy_Bit(obj_coord); +// Map[obj_coord].Flag.Composite |= +// (1 << CellClass::Spot_Index(obj_coord)); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + //ScenarioInit--; + return(-1); + } + + /* + ** Placing an object + */ + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Update the Tiberium computation if we're placing an overlay + */ + if (PendingObject->What_Am_I() == RTTI_OVERLAYTYPE && + ((OverlayTypeClass *)PendingObject)->IsTiberium) { + TotalValue = Overpass(); + Flag_To_Redraw(false); + } + + /* + ** If we're building a base, add this building to the base's Node list. + */ + if (BaseBuilding && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE) { +// node.Type = ((BuildingTypeClass *)PendingObject)->Type; +// node.Cell = Coord_Cell(PendingObjectPtr->Coord); + Base.Nodes.Add(BaseNodeClass(((BuildingTypeClass *)PendingObject)->Type, Coord_Cell(PendingObjectPtr->Coord))); + } + + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + return(-1); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Placement -- cancels placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Placement(void) +{ + /* + ** Delete the placement object + */ + delete PendingObjectPtr; + PendingObject = 0; + PendingObjectPtr = 0; + PendingHouse = HOUSE_NONE; + + /* + ** Restore cursor shape + */ + Set_Cursor_Shape(0); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next -- while placing object, goes to next * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Increments LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * incrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + /* + ** Go to next object in Objects list + */ + LastChoice++; + if (LastChoice == ObjCount) { + + /* + ** If we're in normal placement mode, wrap to the 1st object; + ** if we're in base-building mode, wrap to the 1st building + */ + if (!BaseBuilding) { + LastChoice = 0; + } else { + LastChoice = TypeOffset[7]; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Decrements LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * decrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Go to prev object in Objects list + */ + LastChoice--; + + /* + ** If we're in normal placement mode, wrap at the 1st object. + ** If we're building a base, wrap at the 1st building. + */ + if (!BaseBuilding) { + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } else { + if (LastChoice < TypeOffset[7]) { + LastChoice = ObjCount - 1; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next_Category -- places next category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to next category in Objects list + */ + i = LastChoice; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i++; + if (i == ObjCount) { + i = 0; + } + } + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev_Category -- places previous category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to prev category in Objects list + */ + i = LastChoice; + + /* + ** Scan for start of this category + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + i--; + if (i < 0) i = ObjCount-1; + LastChoice = i; + + /* + ** Scan for the previous category + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + i++; + if (i >= ObjCount) i = 0; + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Home -- homes the placement object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Home(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + /* + ** Loop until we create a valid object + */ + LastChoice = 0; + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ + if (!Verify_House(LastHouse, Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); + } + + /* + ** Create placement object + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Toggle_House -- toggles current placement object's house * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Toggle_House(void) +{ + TechnoClass *tp; + + /* + ** Don't allow this command if we're building a base; the only valid + ** house for base-building is the one assigned to the base. + */ + if (BaseBuilding) { + return; + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!PendingObjectPtr->Is_Techno()) { + return; + } + + /* + ** Select the house that will own this object + */ + LastHouse = Cycle_House(PendingObjectPtr->Owner(), PendingObject); + + /* + ** Change the house + */ + tp = (TechnoClass *)PendingObjectPtr; + tp->House = HouseClass::As_Pointer(LastHouse); + + /* + ** Set house variables to new house + */ + PendingHouse = LastHouse; +} + + +/*************************************************************************** + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * * + * Looks in the given button list for the given GDI, NOD & Neutral button * + * id's. Sets the On/Off state of the buttons based on the given house, * + * only if that button is found in the list. * + * * + * INPUT: * + * house house to set buttons to * + * btnlist ptr to button list to search * + * base_id button ID for GDI; assumes other id's are sequential* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + * 01/26/1996 JLB : Uses new house selection list method. * + *=========================================================================*/ +void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass *, int ) +//void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass * btnlist, int base_id) +{ + HouseList->Set_Selected_Index(house); + +#ifdef NEVER + HousesType h; + int id; + TextButtonClass * btn; + + /* + ** Loop through all houses, searching the button list for each one. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + + /* + ** Compute the desired button ID; get a pointer to the button + */ + id = (int)h + base_id; + btn = (TextButtonClass *)btnlist->Extract_Gadget(id); + if (btn) { + + /* + ** If this house value is the desired one, turn the button on; + ** otherwise, turn it off. + */ + if (h == house) { + btn->Turn_On(); + } else { + btn->Turn_Off(); + } + } + } +#endif +} + + +/*************************************************************************** + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Trigger_Placement(void) +{ + Set_Default_Mouse(MOUSE_CAN_MOVE); + Override_Mouse_Shape(MOUSE_CAN_MOVE); +} + + +/*************************************************************************** + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Stop_Trigger_Placement(void) +{ + CurTrigger = NULL; + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); +} + + +/*************************************************************************** + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Trigger(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** Assign trigger to an object + */ + AttachType a1 = CurTrigger->Attaches_To(); + if (object && (a1 & ATTACH_OBJECT) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + if (tt) { + object->Trigger = tt; + } + } + } else { + + /* + ** Assign trigger to a cell + */ + if ((a1 & ATTACH_CELL) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + Map[cell].Trigger = tt; + } +// CellTriggers[cell] = CurTrigger; + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Start_Base_Building -- starts base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Base_Building(void) +{ + /* + ** Fully build the base so the user can edit it + */ + Build_Base_To(100); + + /* + ** Start placement mode + */ + BaseBuilding = true; + Start_Placement(); + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Base_Building(void) +{ + /* + ** Build the base to the proper amount + */ + Build_Base_To(Scen.Percent); + + /* + ** Cancel placement mode + */ + Cancel_Placement(); + BaseBuilding = false; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Build_Base_To -- builds the AI base to the given percent * + * * + * INPUT: * + * percent percentage to build base to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Base_To(int percent) +{ + int i; + int num_buildings; + BuildingTypeClass const * objtype; + BuildingClass * obj; + + //ScenarioInit++; + + /* + ** Completely dismantle the base, so we start at a known point + */ + for (i = 0; i < Base.Nodes.Count(); i++) { + if (Base.Is_Built(i)) { + obj = Base.Get_Building(i); + delete obj; + } + } + + /* + ** Compute number of buildings to build + */ + num_buildings = (Base.Nodes.Count() * percent) / 100; + + /* + ** Build the base to the desired amount + */ + for (i = 0; i < num_buildings; i++) { + /* + ** Get a ptr to the type of building to build, create one, and unlimbo it. + */ + objtype = &BuildingTypeClass::As_Reference(Base.Nodes[i].Type); + obj = (BuildingClass *)objtype->Create_One_Of(HouseClass::As_Pointer(Base.House)); + + /* + ** If unlimbo fails, error out + */ + ScenarioInit++; + if (!obj->Unlimbo(Cell_Coord(Base.Nodes[i].Cell))) { + delete obj; + WWMessageBox().Process("Unable to build base!"); + ScenarioInit--; + return; + } + ScenarioInit--; + } + + //ScenarioInit--; +} + + +#endif diff --git a/CODE/MAPEDSEL.CPP b/CODE/MAPEDSEL.CPP new file mode 100644 index 0000000..b346744 --- /dev/null +++ b/CODE/MAPEDSEL.CPP @@ -0,0 +1,588 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : MAPEDSEL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : April 30, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-selection & manipulation routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Change_House -- changes CurrentObject's house * + * MapEditClass::Grab_Object -- grabs the current object * + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls* + * MapEditClass::Select_Next -- selects next object on the map * + * MapEditClass::Select_Object -- selects an object for processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * Select_Object -- selects an object for processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object selected, -1 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Object(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + int rc=0; + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** If no object, unselect the current one + */ + if (!object) { + if (CurrentObject.Count()) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + + /* + ** Turn off object controls + */ + Popup_Controls(); + } + rc = -1; + } else { + + /* + ** Select object only if it's different + */ + if (!CurrentObject.Count() || (CurrentObject.Count() && object != CurrentObject[0])) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + object->Select(); + + /* + ** Set mouse shape back to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show the popup controls + */ + Popup_Controls(); + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + + return(rc); +} + + +/*************************************************************************** + * MapEditClass::Select_Next -- selects next object on the map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Select_Next(void) +{ + ObjectClass * obj; + CELL obj_cell; + int smap_w; // screen map width in icons + int smap_h; // screen map height in icons + int cell_x; // cell-x of next object + int cell_y; // cell-y of next object + int tcell_x; // cell-x of TacticalCell + int tcell_y; // cell-y of TacticalCell + + /* + ** Get next object on the map + */ + obj = Map.Next_Object(CurrentObject[0]); + + if (obj) { + /* + ** Unselect current object if there is one + */ + Unselect_All(); + + /* + ** Select this object + */ + obj->Select(); + } + + /* + ** Restore mouse shape to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show pop-up controls + */ + Popup_Controls(); + + /* + ** Make sure object is shown on the screen + */ + /* + ** compute screen map dimensions + */ + smap_w = Lepton_To_Cell(TacLeptonWidth); + smap_h = Lepton_To_Cell(TacLeptonHeight); + + /* + ** compute x,y of object's cell + */ + obj_cell = Coord_Cell(CurrentObject[0]->Coord); + cell_x = Cell_X(obj_cell); + cell_y = Cell_Y(obj_cell); + tcell_x = Coord_XCell(TacticalCoord); + tcell_y = Coord_YCell(TacticalCoord); + + /* + ** If object is off-screen, move map + */ + if (cell_x < tcell_x) { + tcell_x = cell_x; + } else { + if (cell_x >= tcell_x + smap_w) { + tcell_x = cell_x - smap_w + 1; + } + } + + if (cell_y < tcell_y) { + tcell_y = cell_y; + } else { + if (cell_y >= tcell_y + smap_h) { + tcell_y = cell_y - smap_h + 1; + } + } + + ScenarioInit++; + Set_Tactical_Position(XY_Coord(Cell_To_Lepton(tcell_x), Cell_To_Lepton(tcell_y))); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls * + * * + * Call this routine whenever the CurrentObject changes. The routine will * + * selectively enable or disable the popup controls based on whether * + * CurrentObject is NULL, or if it's a Techno object, or what type of * + * Techno object it is. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + * 04/30/1996 JLB : Revamped for new buttons and stuff. * + *=========================================================================*/ +void MapEditClass::Popup_Controls(void) +{ + const TechnoTypeClass * objtype = NULL; + HousesType owner; // object's current owner + int mission_index; // object's current mission + int strength; // object's 0-255 strength value + int i; + + /* + ** Remove all buttons from GScreen's button list (so none of them provide + ** input any more); then, destroy the list by Zapping each button. Then, + ** we'll have to add at least the MapArea button back to the Input button + ** list before we return, plus any other buttons to process input for. We + ** always must add MapArea LAST in the list, so it doesn't intercept the + ** other buttons' input. + */ + Remove_A_Button(*HouseList); + Remove_A_Button(*MissionList); + Remove_A_Button(*HealthGauge); + Remove_A_Button(*HealthText); + Remove_A_Button(*FacingDial); + Remove_A_Button(*BaseGauge); + Remove_A_Button(*BaseLabel); + Remove_A_Button(*MapArea); + + Remove_A_Button(*Sellable); + Remove_A_Button(*Rebuildable); + + /* + ** If no current object, hide the list + */ + if (!CurrentObject.Count()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + /* + ** If not Techno, no need for editing buttons + */ + if (!CurrentObject[0]->Is_Techno()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + objtype = (TechnoTypeClass const *)&CurrentObject[0]->Class_Of(); + + /* + ** Get object's current values + */ + owner = CurrentObject[0]->Owner(); + mission_index = 0; + for (i = 0; i < NUM_EDIT_MISSIONS; i++) { + if (CurrentObject[0]->Get_Mission() == MapEditMissions[i]) { + mission_index = i; + } + } + strength = CurrentObject[0]->Health_Ratio()*256; + + switch (objtype->What_Am_I()) { + case RTTI_VESSELTYPE: + case RTTI_UNITTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_AIRCRAFTTYPE: + MissionList->Set_Selected_Index(mission_index); + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + FacingDial->Set_Direction(((TechnoClass *)CurrentObject[0])->PrimaryFacing); + + /* + ** Make the list. + */ + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*MissionList); + Add_A_Button(*HealthText); + Add_A_Button(*FacingDial); + break; + + case RTTI_BUILDINGTYPE: + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*HealthText); + + Add_A_Button(*Sellable); + if (((BuildingClass*)CurrentObject[0])->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + Add_A_Button(*Rebuildable); + if (((BuildingClass*)CurrentObject[0])->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + + if (objtype->IsTurretEquipped) { + FacingDial->Set_Direction(((TechnoClass *) CurrentObject[0])->PrimaryFacing); + Add_A_Button(*FacingDial); + } + break; + } + + /* + ** Add the map area last, so it's "underneath" the other buttons, and won't + ** intercept input for those buttons. + */ + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); +} + + +/*************************************************************************** + * MapEditClass::Grab_Object -- grabs the current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Grab_Object(void) +{ + CELL cell; + + if (CurrentObject.Count()) { + GrabbedObject = CurrentObject[0]; + + /* + ** Find out which cell 'ZoneCell' is in relation to the object's current cell + */ + cell = Coord_Cell(GrabbedObject->Coord); + GrabOffset = cell - ZoneCell; + } +} + + +/*************************************************************************** + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object moved, -1 = it didn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Move_Grabbed_Object(void) +{ + COORDINATE new_coord = 0; + int retval = -1; + + /* + ** Lift up the object + */ + GrabbedObject->Mark(MARK_UP); + + /* + ** If infantry, use a free spot in this cell + */ + if (GrabbedObject->Is_Infantry()) { + + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + new_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + + /* + ** Clear the occupied bit in this infantry's cell. + */ + ((InfantryClass *)GrabbedObject)->Clear_Occupy_Bit(GrabbedObject->Coord); + } else { + new_coord = NULL; + } + + } else { + + /* + ** Non-infantry: use cell's center coordinate + */ + new_coord = Cell_Coord(ZoneCell + GrabOffset); + + if (GrabbedObject->What_Am_I() == RTTI_BUILDING || + GrabbedObject->What_Am_I() == RTTI_TERRAIN) { + + new_coord = Coord_Whole(new_coord); + } + + /* + ** Try to place object at new coordinate + */ + if (GrabbedObject->Can_Enter_Cell(Coord_Cell(new_coord)) != MOVE_OK) { + new_coord = NULL; + } + } + if (new_coord != NULL) { + + /* + ** If this object is part of the AI's Base list, change the coordinate + ** in the Base's Node list. + */ + if (GrabbedObject->What_Am_I()==RTTI_BUILDING && + Base.Get_Node((BuildingClass *)GrabbedObject)) + Base.Get_Node((BuildingClass *)GrabbedObject)->Cell = Coord_Cell(new_coord); + + GrabbedObject->Coord = new_coord; + retval = 0; + } + GrabbedObject->Mark(MARK_DOWN); + + /* + ** For infantry, set the bit in its new cell marking that spot as occupied. + */ + if (GrabbedObject->Is_Infantry()) { + ((InfantryClass *)GrabbedObject)->Set_Occupy_Bit(new_coord); + } + + /* + ** Re-select the object, and reset the mouse pointer + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + Flag_To_Redraw(true); + + return(retval); +} + + +/*************************************************************************** + * MapEditClass::Change_House -- changes CurrentObject's house * + * * + * INPUT: * + * newhouse house to change to * + * * + * OUTPUT: * + * 1 = house was changed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Change_House(HousesType newhouse) +{ + TechnoClass *tp; + + /* + ** Return if no current object + */ + if (!CurrentObject.Count()) { + return(false); + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!CurrentObject[0]->Is_Techno()) { + return(false); + } + + /* + ** You can't change the house if the object is part of the AI's Base. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && Base.Is_Node((BuildingClass *)CurrentObject[0])) { + return(false); + } + + /* + ** Verify that the target house exists + */ + if (HouseClass::As_Pointer(newhouse)==NULL) { + return(false); + } + + /* + ** Verify that this is a valid owner + */ +// if (!Verify_House(newhouse, &CurrentObject[0]->Class_Of())) { +// return(false); +// } + + /* + ** Change the house + */ + tp = (TechnoClass *)CurrentObject[0]; + tp->House = HouseClass::As_Pointer(newhouse); + + tp->IsOwnedByPlayer = false; + if (tp->House == PlayerPtr) { + tp->IsOwnedByPlayer = true; + } + + return(true); +} + + +#endif diff --git a/CODE/MAPEDTM.CPP b/CODE/MAPEDTM.CPP new file mode 100644 index 0000000..7e2a438 --- /dev/null +++ b/CODE/MAPEDTM.CPP @@ -0,0 +1,945 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPEDTM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : MAPEDTM.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : May 7, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * MapEditClass::Select_Team -- user selects a team from a list * + * MapEditClass::Team_Members -- user picks makeup of a team * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Handle_Teams(char const * caption) +{ + int rc; + + /* + ** Team dialog processing loop: + ** - Invoke the team selection dialog. If a team's selected, break + ** & return + ** - If user wants to edit the current team, do so + ** - If user wants to create new team, new a TeamTypeClass & edit it + ** - If user wants to delete team, delete the current team + ** - Keep looping until 'OK' + */ + for (;;) { + + /* + ** Select team + */ + rc = Select_Team(caption); + + /* + ** 'OK'; break + */ + if (rc == 0) { + break; + } else { + + /* + ** 'Edit' + */ + if (rc == 1 && CurTeam) { + if (CurTeam->Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** 'New' + */ + if (rc == 2) { + + /* + ** Create a new team + */ + CurTeam = new TeamTypeClass(); + if (CurTeam) { + /* + ** delete it if user cancels + */ + if (!CurTeam->Edit()) { + delete CurTeam; + CurTeam = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more teams available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } else { + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTeam) { + Detach_This_From_All(CurTeam->As_Target(), true); + delete CurTeam; + //CurTeam->Remove(); + CurTeam = NULL; + } + } + } + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Team -- user selects a team from a list * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sorted team list. * + *=========================================================================*/ +int MapEditClass::Select_Team(char const * ) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, // dialog width + D_DIALOG_H = 250, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 25, // margin width/height + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-20, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 20, + D_LIST_H = (D_DIALOG_H-50)-D_LIST_Y, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + + /* + ** Button enumerations: + */ + enum { + TEAM_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables + */ + bool edit_team = false; // true = user wants to edit + bool new_team = false; // true = user wants to new + bool del_team = false; // true = user wants to new + static int tabs[] = {35, 60, 80, 100}; // list box tab stops + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands = NULL; // the button list + + TListClass > teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W); + TextButtonClass newbtn (BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W); + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W); + TextButtonClass okbtn (BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in team names + */ + for (int index = 0; index < TeamTypes.Count(); index++) { + teamlist.Add_Item(TeamTypes.Ptr(index)); + } + + PNBubble_Sort(&teamlist[0], teamlist.Count()); + + if (!CurTeam || !CurTeam->IsActive) CurTeam = NULL; + + if (CurTeam) { + teamlist.Set_Selected_Index(CurTeam); + CurTeam = teamlist.Current_Item(); + } else { + teamlist.Set_Selected_Index(0); + if (TeamTypes.Count()) { + CurTeam = teamlist.Current_Item(); + } + } + + /* + ** Create the list + */ + commands = &teamlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ** Init tab stops for list + */ + teamlist.Set_Tabs(tabs); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + commands->Draw_All(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + CurTeam = teamlist.Current_Item(); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (teamlist.Count()) { + process = false; + edit_team = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_team = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_team = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (edit_team) return(1); + if (new_team) return(2); + if (del_team) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Team_Members -- user picks makeup of a team * + * * + * Team members are rendered in a 24 x 24 area; the Window coordinates * + * have to be set to this area when the object's 'Display()' routine is * + * called. Thus, the dialog's window coords have to be divisible by * + * 24. The height of the dialog is computed based on how many objects * + * there are in it. * + * * + * 10 pixels are left between rows of objects, so the # of that type of * + * object can be displayed underneath the object. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Members ³ * + * ³ ³ * + * ³ ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * house house to display objects for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine uses HIDBUFF for data storage. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +//#define TEENSY_WEENSY +/* +** Dialog & button dimensions +*/ +enum { + D_DIALOG_W = 640, + D_DIALOG_X = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT6_H = 7, + D_MARGIN = 7, + +#ifdef TEENSY_WEENSY + D_PICTURE_W = 32, + D_PICTURE_H = 24, +#else + D_PICTURE_W = 64, + D_PICTURE_H = 48, +#endif + D_ROW_H = (D_PICTURE_H + 3), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_CX - 5 - D_OK_W, + D_OK_Y = 0, + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = 0, + +}; + +int MapEditClass::Team_Members(HousesType house) +{ + /* + ** Button enumerations: + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + ** (highest enum is the lowest layer). Each section of the map checks + ** the requested redraw level to see if it's supposed to draw; if it's + ** >= its level, it redraws. + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + RedrawType display; // requested redraw level + bool process; // loop while true + + /* + ** Dialog variables + */ + KeyNumType input; // user input + bool cancel = false; // true = user cancels + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team display variables + */ + const TechnoTypeClass **teamclass; // array of team classes + int *teamcount; // array of class counts + int numcols; // # units displayed horizontally + int numrows; // # units displayed vertically + + /* + ** Dialog dimensions. + */ + int dlg_y; + int dlg_h; // dialog height + int msg_y; // y-coord for object names + + int curclass = -1; // current index into 'teamclass'; can be invalid! + // (is based on current mouse position) + int numclasses; // current # classes in the team (limited to <=5) + int maxclasses; // max # classes available + int i,j; + + /* + ** Values for timing when mouse held down. + */ + int lheld = 0; + int rheld = 0; + long tdelay[3] = {5, 20, 0}; + int tindex = 0; + long heldtime; + + /* + ** Buttons. + */ + ControlClass * commands; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Set up the team data arrays (ObjectTypeClass pointers & count) + */ +#ifdef WIN32 + teamclass = (const TechnoTypeClass **)SysMemPage.Get_Buffer(); + teamcount = (int *)SysMemPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#else + teamclass = (const TechnoTypeClass **)HidPage.Get_Buffer(); + teamcount = (int *)HidPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#endif + + /* + ** Fill in the ObjectTypeClass array with all available object type ptrs, + ** checking to be sure this house can own the object + */ + i = 0; + for (InfantryType i_id = INFANTRY_FIRST; i_id < INFANTRY_COUNT; i_id++) { + teamclass[i] = &InfantryTypeClass::As_Reference(i_id); + i++; + } + + for (AircraftType a_id = AIRCRAFT_FIRST; a_id < AIRCRAFT_COUNT; a_id++) { + teamclass[i] = &AircraftTypeClass::As_Reference(a_id); + i++; + } + + for (UnitType u_id = UNIT_FIRST; u_id < UNIT_COUNT; u_id++) { + teamclass[i] = &UnitTypeClass::As_Reference(u_id); + i++; + } + + for (VesselType v_id = VESSEL_FIRST; v_id < VESSEL_COUNT; v_id++) { + teamclass[i] = &VesselTypeClass::As_Reference(v_id); + i++; + } + + /* + ** Save max # classes. + */ + maxclasses = i; + + /* + ** Fill in the 'count' array with data from the current team: + ** - For every class in the current team, find that class type in the + ** 'teamclass' array & set its count value + */ + for (j = 0; j < maxclasses; j++) { + teamcount[j] = 0; + } + + /* + ** Loop through all classes in the team. + */ + for (i = 0; i < CurTeam->ClassCount; i++) { + + /* + ** Find this class in our array. + */ + for (j = 0; j < maxclasses; j++) { + + /* + ** Set the count; detect a match between the team's class & the + ** 'teamclass' array entry by comparing the actual pointers; typeid + ** won't work because E1 & E2 are the same type class. + */ + if (CurTeam->Members[i].Class == teamclass[j]) { + teamcount[j] = CurTeam->Members[i].Quantity; + break; + } + } + } + numclasses = CurTeam->ClassCount; + + /* + ** Set up the dialog dimensions based on number of classes we have to draw + ** + ** Compute picture rows & cols. + */ + numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + numrows = (maxclasses + numcols - 1) / numcols; + + /* + ** Dialog's height = top margin + label + picture rows + margin + label + margin + btn + */ + dlg_h = 400; + dlg_y = 0; + msg_y = dlg_y+dlg_h - 26 - 15; + + okbtn.Y = dlg_y + dlg_h - D_MARGIN - D_OK_H - 15; + cancelbtn.Y = dlg_y + dlg_h - D_MARGIN - D_CANCEL_H - 15; + + /* + ** Draw to SeenPage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Make sure 'house' is valid. + */ +// if (house!=HOUSE_GOOD && house!=HOUSE_BAD && house != HOUSE_MULTI1 && +// house != HOUSE_MULTI2 && house != HOUSE_MULTI3 && house != HOUSE_MULTI4 ) { +// if (Scen.ScenPlayer == SCEN_PLAYER_MPLAYER) { +// house = HOUSE_MULTI1; +// } else { +// house = HOUSE_GOOD; +// } +// } + + /* + ** Create the list. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ** Display the constant background of this dialog. + */ + Dialog_Box(D_DIALOG_X, dlg_y, D_DIALOG_W, dlg_h); + Draw_Caption(TXT_TEAM_MEMBERS, D_DIALOG_X, dlg_y, D_DIALOG_W); + + /* + ** Draw the objects. + */ + for (i = 0; i < maxclasses; i++) { + + /* + ** Display the object along with any count value for it. + */ + Draw_Member(teamclass[i], i, teamcount[i], house); + } + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + + /* + ** Mouse buttons set or clear 'held' values + */ + case (KN_LMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + lheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case (KN_RMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + rheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + lheld = 0; + break; + + case ((int)KN_RMOUSE | (int)KN_RLSE_BIT): + rheld = 0; + break; + + /* + ** OK: save values & return. + */ + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + /* + ** Cancel: abort & return. + */ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + /* + ** Compute new 'curclass' based on mouse position. + */ + i = (Get_Mouse_X() - 32 - D_DIALOG_X) / D_PICTURE_W + + ((Get_Mouse_Y() - (dlg_y+8+11)) / D_ROW_H) * numcols; + + /* + ** If it's changed, update class label. + */ + if (i != curclass) { + + curclass = i; + + /* + ** Clear out the previously printed name of the item. + */ + Hide_Mouse(); + LogicPage->Fill_Rect(D_DIALOG_X + 32, msg_y, D_DIALOG_X + D_DIALOG_W - 64, msg_y + D_TXT6_H, BLACK); + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, scheme, TBLACK, TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + + /* + ** Force buttons to not be held. + */ + lheld = 0; + rheld = 0; + Show_Mouse(); + } + break; + } + + /* + ** Check for a 'held' mouse button; if it's down, and the correct + ** amount of time has gone by, increment/decrement the count for the + ** current class. + */ + if (lheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + heldtime = TickCount; + if (tindex) { + tindex--; + } + + /* + ** Detect addition of a new class. + */ + if (teamcount[curclass]==0) { + + /* + ** Don't allow more classes than we can handle. + */ + if (numclasses == TeamTypeClass::MAX_TEAM_CLASSCOUNT) { + continue; + } + numclasses++; + } + teamcount[curclass]++; + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + + } else { + + if (rheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + if (tindex) { + tindex--; + } + heldtime = TickCount; + + if (teamcount[curclass] > 0) { + teamcount[curclass]--; + + /* + ** Detect removal of a class. + */ + if (teamcount[curclass] == 0) { + numclasses--; + } + } + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + } + } + } + + /* + ** Copy data into team. + */ + if (!cancel) { + CurTeam->ClassCount = numclasses; + i = 0; // current team class index + for (j = 0; j < maxclasses; j++) { + if (teamcount[j] > 0) { + CurTeam->Members[i].Quantity = teamcount[j]; + CurTeam->Members[i].Class = teamclass[j]; + i++; + } + } + } + + /* + ** Redraw the display. + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) return(-1); + return(0); +} + + +/*********************************************************************************************** + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * * + * This routine will display the cameo image of the potential team member. In the corner, * + * it will show the current quantity of this member for the current team being edited. * + * * + * INPUT: ptr -- Pointer to the member object type. * + * * + * index -- The index into the team dialog box array of selectable objects. This is * + * used to determine the correct X and Y offsets to draw. * + * * + * quant -- The quantity number to display in the corner of the image. * + * * + * house -- The owner of this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1995 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house) +{ + int numcols = (D_DIALOG_W - 64) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int dlg_y = 0; + int x = D_DIALOG_X + 32 + col * D_PICTURE_W; + int y = dlg_y + 8 + 13 + row * D_ROW_H; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Change the window to this box. + */ + WindowList[WINDOW_EDITOR][WINDOWX] = x; + WindowList[WINDOW_EDITOR][WINDOWY] = y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, true); + ptr->Display(WinW/2, WinH>>1, WINDOW_EDITOR, house); + if (quant > 0) { + Fancy_Text_Print("%d", x+1, y+1, scheme, TBLACK, TPF_8POINT|TPF_DROPSHADOW, quant); +// Fancy_Text_Print("%d", x+1, y+D_PICTURE_H-8, scheme, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_DROPSHADOW, quant); + } + Show_Mouse(); +} + + +#endif diff --git a/CODE/MAPSEL.CPP b/CODE/MAPSEL.CPP new file mode 100644 index 0000000..126968d --- /dev/null +++ b/CODE/MAPSEL.CPP @@ -0,0 +1,399 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MAPSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MAPSEL.CPP * + * * + * Programmer : Barry W. Green * + * * + * Start Date : April 17, 1995 * + * * + * Last Update : April 27, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Bit_It_In -- Pixel fade graphic copy. * + * Map_Selection -- Starts the whole process of selecting next map to go to * + * Print_Statistics -- Prints statistics on country selected * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void Cycle_Call_Back_Delay(int time, PaletteClass &pal); +extern int ControlQ; + +int Mouse_Over_Spot(int house, int scenario); +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot); +//VG for ant mission progression +const char* antmission[] = {NULL, "SCA01EA.INI", "SCA02EA.INI", "SCA03EA.INI", "SCA04EA.INI"}; + +struct point { + int x; + int y; +} const MapCoords[2][14][3] = { + { + {{185,123},{ -1, -1},{ -1, -1}}, + {{173,112},{ -1, -1},{ -1, -1}}, + {{196,100},{200,112},{ -1, -1}}, + {{175,113},{ -1, -1},{ -1, -1}}, + {{187, 91},{202, 93},{206,105}}, + {{207,161},{212,172},{ -1, -1}}, + {{172, 92},{ -1, -1},{ -1, -1}}, + {{132,119},{146,125},{ -1, -1}}, + {{199, 73},{205, 86},{ -1, -1}}, + {{236,114},{ -1, -1},{ -1, -1}}, + {{219, 64},{225, 76},{ -1, -1}}, + {{256, 69},{ -1, -1},{ -1, -1}}, + {{262, 77},{ -1, -1},{ -1, -1}}, + {{249, 97},{ -1, -1},{ -1, -1}} + }, +// Soviet coords + { + {{178,105},{ -1, -1},{ -1, -1}}, + {{163,101},{163,113},{ -1, -1}}, + {{160, 89},{ -1, -1},{ -1, -1}}, + {{142,101},{142,117},{ -1, -1}}, + {{212,163},{ -1, -1},{ -1, -1}}, + {{155,133},{171,144},{ -1, -1}}, + {{216,103},{ -1, -1},{ -1, -1}}, + {{132,145},{154,154},{ -1, -1}}, + {{122,117},{ -1, -1},{ -1, -1}}, + {{117,130},{ -1, -1},{ -1, -1}}, + {{ 99,107},{109,146},{ -1, -1}}, + {{134,125},{ -1, -1},{ -1, -1}}, + {{ 32,156},{ 46,171},{ -1, -1}}, + {{108, 97},{ -1, -1},{ -1, -1}} + } +}; + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/*********************************************************************************************** + * Map_Selection -- Starts the whole process of selecting next map to go to * + * * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 BWG : Created. * + *=============================================================================================*/ +extern int CopyType; +#ifndef WIN32 +extern short StreamLowImpact; +#endif +char const * Map_Selection(void) +{ + static char scenarioname[_MAX_FNAME+_MAX_EXT]; + +#ifdef FIXIT_ANTS + if (AntsEnabled) { + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + return(scenarioname); + } + +#endif + char _filename[]="MSAA.WSA"; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); + + _filename[2] = house ? 'S' : 'A'; + _filename[3] = Scen.Scenario + 'A'; + PaletteClass mappalette; + + int scenario = Scen.Scenario; + int selection; + static CDTimerClass timer; + int start = 0; + int count = 0; + int delay = 0; + int xspot = 0; + int yspot = 0; + + void const * appear1 = MFCD::Retrieve("MAPWIPE2.AUD"); + void const * bleep11 = MFCD::Retrieve("BLEEP11.AUD"); + void const * country4 = MFCD::Retrieve("MAPWIPE5.AUD"); + void const * toney7 = MFCD::Retrieve("TONEY7.AUD"); + void const * bleep17 = MFCD::Retrieve("BLEEP17.AUD"); + + void const * scold1 = MFCD::Retrieve("TONEY4.AUD"); + void const * country1 = MFCD::Retrieve("TONEY10.AUD"); + +#ifdef WIN32 + GraphicBufferClass *pseudoseenbuff = new GraphicBufferClass(320, 200, (void*)NULL); +#endif + +// fixed oldvolume = Options.ScoreVolume; +// Options.Set_Score_Volume(fixed(4, 10)); + Theme.Queue_Song(THEME_MAP); + + void *anim = Open_Animation(_filename, NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), mappalette); + + Keyboard->Clear(); + SeenPage.Clear(); + mappalette.Set(FADE_PALETTE_FAST, Call_Back); + +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); + for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + HidPage.Clear(); + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + + + int frame = 1; + StreamLowImpact = true; +#ifdef WIN32 + Play_Sample(appear1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(appear1, 255, Options.Normalize_Volume(55)); +#endif + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + CopyType = 1; + Animate_Frame(anim, *pseudoseenbuff, frame++); + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + switch(frame) { + case 16: +#ifdef WIN32 + Play_Sample(bleep11, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep11, 255, Options.Normalize_Volume(55)); +#endif + break; + case 30: +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(55)); +#endif + break; + case 51: +#ifdef WIN32 + Play_Sample(toney7, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(toney7, 255, Options.Normalize_Volume(55)); +#endif + break; + case 61: +#ifdef WIN32 + Play_Sample(bleep17, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep17, 255, Options.Normalize_Volume(55)); +#endif + break; + } + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); + Show_Mouse(); + Keyboard->Clear(); + + bool done = 0; + MouseType shape = MOUSE_NORMAL; + while (!done) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; + } +#endif + Cycle_Call_Back_Delay(1, mappalette); + int choice = Mouse_Over_Spot(house, scenario); + if (choice == -1) { + shape = MOUSE_NORMAL; + } else { + shape = MOUSE_CAN_ATTACK; + } + + Set_Mouse(shape, start, count, delay, xspot, yspot); + if (timer == 0) { + frame++; + frame %= count; + timer = delay; + Set_Mouse_Cursor(xspot, yspot, Extract_Shape(MouseClass::MouseShapes, start + frame)); + } + if (Keyboard->Check()) { + if ((Keyboard->Get() & 0x10FF) == KN_LMOUSE) { + if (choice != -1) { + done = 1; + selection = choice; +#ifdef WIN32 + Play_Sample(country1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country1, 255, Options.Normalize_Volume(50)); +#endif + } else { +#ifdef WIN32 + Play_Sample(scold1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(scold1, 255, Options.Normalize_Volume(50)); +#endif + } + } + } + } + + Hide_Mouse(); + + /* + ** Restore the mouse to normal shape before leaving this routine. + */ + Set_Mouse(MOUSE_NORMAL, start, count, delay, xspot, yspot); + Set_Mouse_Cursor(xspot, yspot, Extract_Shape(MouseClass::MouseShapes, start)); + + Keyboard->Clear(); +// BlackPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// SeenPage.Clear(); + + Fancy_Text_Print(TXT_STAND_BY, 160 * RESFACTOR, 190 * RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Create the new scenario filename from the selection. The filename is + ** derived from the previous filename but it has the scenario number + ** incremented and the chosen variation set. + */ + + //V.G. added so Ant Missions would progress + if(Scen.ScenarioName[2] == 'A'){ + int antnum = Scen.Scenario++; + if(antnum > 4) antnum = 1; + strcpy(scenarioname, antmission[antnum]); + } + else{ + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + scenarioname[6] = 'A' + selection; + } + Theme.Fade_Out(); +// Options.Set_Score_Volume(oldvolume); + +// Scen.ScenVar = (ScenarioVarType)selection; +//Mono_Printf("Chose variant %d \n", selection); + return(scenarioname); +} + +int Mouse_Over_Spot(int house, int scenario) +{ + int retval = -1; + for (int selection = 0; selection < 3 && MapCoords[house][scenario][selection].x != -1; selection++) { + int mousex = Get_Mouse_X() / RESFACTOR; + int mousey = Get_Mouse_Y() / RESFACTOR; + if (mousex >= MapCoords[house][scenario][selection].x && + mousey >= MapCoords[house][scenario][selection].y && + mousex <= MapCoords[house][scenario][selection].x+11 && + mousey <= MapCoords[house][scenario][selection].y+9) { + + retval = selection; + break; + } + } + return(retval); +} +void Cycle_Call_Back_Delay(int time, PaletteClass &pal) +{ + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + + while(time--) { + /* + ** Process the fading white color. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + pal[254] = GamePalette[WHITE]; + pal[254].Adjust(val, BlackColor); + + pal.Set(); + } + Call_Back_Delay(1); + } +} + +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot) +{ + switch(shape) { + case MOUSE_NORMAL: + start = 0; + count = 1; + delay = 0; + xspot = 0; + yspot = 0; + break; + default: + start = 21; + count = 8; + delay = 4; + xspot = 14; + yspot = 11; + break; + } +} diff --git a/CODE/MCI.CPP b/CODE/MCI.CPP new file mode 100644 index 0000000..833c51d --- /dev/null +++ b/CODE/MCI.CPP @@ -0,0 +1,365 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* FILE +* MCI.cpp +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include "mci.h" + +/**************************************************************************** +* +* NAME +* GetDeviceCount() +* +* DESCRIPTION +* +* INPUTS +* NONE +* +* RESULT +* Count - Number of MCI device entries +* +****************************************************************************/ + +unsigned int MCI::GetDeviceCount(void) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + unsigned int count; + + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)&count; + sysInfo.dwRetSize = sizeof(count); + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_QUANTITY, (DWORD)&sysInfo); + + if (rc) + return 0; + + return count; + } + + +/**************************************************************************** +* +* NAME +* GetDeviceName(entry, name) +* +* DESCRIPTION +* +* INPUTS +* Entry - Entry number to get name for. +* Name - On return; device entry name +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::GetDeviceName(unsigned int item, char* buffer) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + + // Get device name + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)buffer; + sysInfo.dwRetSize = 63; + sysInfo.dwNumber = item; + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_NAME, (DWORD)&sysInfo); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetProductName(MCIDEVICEID id, char* buffer) + { + MCIERROR rc; + MCI_INFO_PARMS info; + + // Get device product name + memset(&info, 0, sizeof(info)); + info.lpstrReturn = (LPSTR)buffer; + info.dwRetSize = 63; + + rc = mciSendCommand(id, MCI_INFO, MCI_WAIT | MCI_INFO_PRODUCT, + (DWORD)&info); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* OpenDevice(name) +* +* DESCRIPTION +* +* INPUTS +* Name - Device name to open +* +* RESULT +* DeviceID - ID of opened device, 0 if error. +* +****************************************************************************/ + +MCIDEVICEID MCI::OpenDevice(const char* name) + { + MCIERROR rc; + MCI_OPEN_PARMS open; + + memset(&open, 0, sizeof(open)); + open.lpstrDeviceType = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE, (DWORD)&open); + + if (rc) + return 0; + + return (open.wDeviceID); + } + + +void MCI::CloseDevice(MCIDEVICEID id) + { + MCI_GENERIC_PARMS close; + + close.dwCallback = (DWORD)NULL; + + if (id) + mciSendCommand(id, MCI_CLOSE, MCI_WAIT, (DWORD)&close); + } + + +/**************************************************************************** +* +* NAME +* GetDeviceDescription +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetDeviceDescription(const char* name, MCIDevice* caps) + { + MCIDEVICEID id; + unsigned long result; + + // Copy the name + strncpy(caps->name, name, 63); + + if ((id = OpenDevice(name)) == 0) + return false; + + // Get device product name + GetProductName(id, caps->description); + + // Get device type + if (GetCapability(id, MCI_GETDEVCAPS_DEVICE_TYPE, &result)) + caps->type = result; + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_EJECT, &result)) + caps->canEject = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_PLAY, &result)) + caps->canPlay = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_RECORD, &result)) + caps->canRecord = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_SAVE, &result)) + caps->canSave = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_COMPOUND_DEVICE, &result)) + caps->usesDevElem = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_AUDIO, &result)) + caps->hasAudio = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_VIDEO, &result)) + caps->hasVideo = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_USES_FILES, &result)) + caps->reqElemFile = ((result) ? true : false); + + CloseDevice(id); + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result) + { + MCIERROR rc; + MCI_GETDEVCAPS_PARMS devCaps; + + memset(&devCaps, 0, sizeof(devCaps)); + devCaps.dwItem = capItem; + rc = mciSendCommand(id, MCI_GETDEVCAPS, MCI_WAIT|MCI_GETDEVCAPS_ITEM, + (DWORD)&devCaps); + + if (rc) + return false; + + *result = devCaps.dwReturn; + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +const char* MCI::GetDeviceTypeName(unsigned long type) + { + static struct _DeviceType {unsigned long typeID; const char* typeName;} + _deviceTypeNames[] = + { + {MCI_DEVTYPE_ANIMATION, "Animation"}, + {MCI_DEVTYPE_CD_AUDIO, "CD Audio"}, + {MCI_DEVTYPE_DAT, "DAT"}, + {MCI_DEVTYPE_DIGITAL_VIDEO, "Digital Video"}, + {MCI_DEVTYPE_OTHER, "Other"}, + {MCI_DEVTYPE_OVERLAY, "Overlay"}, + {MCI_DEVTYPE_SCANNER, "Scanner"}, + {MCI_DEVTYPE_SEQUENCER, "MIDI Sequencer"}, + {MCI_DEVTYPE_VCR, "VCR"}, + {MCI_DEVTYPE_VIDEODISC, "VideoDisc"}, + {MCI_DEVTYPE_WAVEFORM_AUDIO, "Wave Audio"}, + {0, NULL}, + }; + + int i = 0; + + while (_deviceTypeNames[i].typeID != 0) + { + if (_deviceTypeNames[i].typeID == type) + return _deviceTypeNames[i].typeName; + + i++; + } + + return NULL; + } + + +/**************************************************************************** +* +* NAME +* MCIEnumerate(callack, context) +* +* DESCRIPTION +* +* INPUTS +* Callback - +* Context - +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::EnumerateDevices(MCIEnumCB* callback, void* context) + { + DWORD count; + DWORD i; + char name[64]; + MCIDevice device; + + // Get the number of devices + count = GetDeviceCount(); + + // Do for each device + for (i = 1; i <= count; i++) + { + GetDeviceName(i, name); + memset(&device, 0, sizeof(device)); + + if (GetDeviceDescription(name, &device)) + { + if (!callback(&device, context)) + break; + } + } + + return true; + } +#endif // MCIMPEG + diff --git a/CODE/MCI.H b/CODE/MCI.H new file mode 100644 index 0000000..a38046b --- /dev/null +++ b/CODE/MCI.H @@ -0,0 +1,108 @@ +/* +** 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 . +*/ + +#ifndef _MCI_H_ +#define _MCI_H_ +/**************************************************************************** +* +* FILE +* MCI.H +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include "watcom.h" + +/* MCIDevice - MCI device capabilities and description + * + * name - Name used to open device. + * description - Product description + * type - Device type + * canEject - Can eject media flag + * canPlay - Can playback media + * canRecord - Can record media + * canSave - Can save media + * usesDevElem - Uses device element + * hasAudio - Media supports audio + * hasVideo - Media supports video + * reqElemFile - Requires element file + */ +typedef struct _MCIDevice + { + char name[64]; + char description[64]; + unsigned long type; + bool canEject; + bool canPlay; + bool canRecord; + bool canSave; + bool usesDevElem; + bool hasAudio; + bool hasVideo; + bool reqElemFile; + } MCIDevice; + +/* MCI enumeration callback definition */ +typedef bool (MCIEnumCB)(MCIDevice* desc, void*); + +class MCI + { + public: + // Open MCI device + MCIDEVICEID OpenDevice(const char* name); + void CloseDevice(MCIDEVICEID id); + + // Enumerate devices + bool EnumerateDevices(MCIEnumCB* callback, void* context); + + // Get number of MCI devices name in registry or [MCI] section + // of system.ini + unsigned int GetDeviceCount(void); + + // Get device name from registry or [MCI] section of system.ini + bool GetDeviceName(unsigned int item, char* buffer); + + // Get general device description + bool GetDeviceDescription(const char* name, MCIDevice* caps); + + // Get type name (IE: Digital Video) from type ID (IE: MCI_DEVTYPE_DIGITAL_VIDEO) + const char* GetDeviceTypeName(unsigned long type); + + // Get device product name + bool GetProductName(MCIDEVICEID id, char* buffer); + + // Get device capability + bool GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result); + }; + +#endif // MCIMPEG +#endif // _MCI_H_ diff --git a/CODE/MCIMOVIE.CPP b/CODE/MCIMOVIE.CPP new file mode 100644 index 0000000..2ce2c65 --- /dev/null +++ b/CODE/MCIMOVIE.CPP @@ -0,0 +1,352 @@ +/* +** 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 . +*/ + +#include "function.h" + +#ifdef MCIMPEG +#include "mcimovie.h" +#include + +/**************************************************************************** +* +* NAME +* MCIMovie - Constructor +* +* DESCRIPTION +* +* INPUTS +* HInstance - Application instance handle +* +* RESULT +* NONE +* +****************************************************************************/ + +MCIMovie::MCIMovie(HWND mainWindow) + : mMainWindow(mainWindow), mMCIWindow(NULL), mName(NULL), mDeviceID(0) + { + mWidth = mHeight = 0; + } + + +/**************************************************************************** +* +* NAME +* ~MCIMovie - Destructor +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +MCIMovie::~MCIMovie() + { + // Stop any playing movie + Close(); + + // Free name + if (mName != NULL) + free(mName); + } + + +/**************************************************************************** +* +* NAME +* Open() +* +* DESCRIPTION +* Open the media file in preparation for playback. +* +* INPUTS +* NONE +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Open(const char* name, const char* device) + { + MCIERROR rc; + MCI_DGV_RECT_PARMS rectParm; + MCI_BREAK_PARMS breakParm; + + // Stop any currently playing movie + Close(); + + // Copy the movie name for our use + if (mName != NULL) + free(mName); + + mName = strdup(name); + + if (device == NULL) + device = "mpegvideo"; + + // Setup open parameters + memset((void*)&mOpenParm, 0, sizeof(mOpenParm)); + mOpenParm.dwCallback = NULL; + mOpenParm.lpstrDeviceType = device; + mOpenParm.lpstrElementName = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | + MCI_OPEN_ELEMENT, (DWORD)&mOpenParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + // Set device ID + mDeviceID = mOpenParm.wDeviceID; + + // Retrieve movie dimensions + rectParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_WHERE, MCI_WAIT | MCI_DGV_WHERE_SOURCE, + (DWORD)&rectParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + mWidth = rectParm.rc.right - rectParm.rc.left; + mHeight = rectParm.rc.bottom - rectParm.rc.top; + + // Set break key to escape + breakParm.dwCallback = NULL; + breakParm.nVirtKey = VK_ESCAPE; + breakParm.hwndBreak = mMainWindow; + + rc = mciSendCommand(mDeviceID, MCI_BREAK, MCI_WAIT | MCI_BREAK_HWND | + MCI_BREAK_KEY, (DWORD)&breakParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Play - Play the specified movie. +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Play(HWND window) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + // Provide window for playback + if (AttachWindow(window)) + { + // Size the video area + if (SizeDestination()) + { + // Start playing + memset((void*)&mPlayParm, 0, sizeof(mPlayParm)); + mPlayParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_PLAY, MCI_WAIT, (DWORD)&mPlayParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + Close(); + } + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Pause +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Pause(void) + { + if (mDeviceID == 0) + return false; + + if (mciSendCommand(mDeviceID, MCI_PAUSE, 0, (DWORD)NULL)) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* Stop +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Close(void) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + rc = mciSendCommand(mDeviceID, MCI_CLOSE, 0, (DWORD)NULL); + mDeviceID = 0; + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* SizeDestination +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::SizeDestination(void) + { + MCIERROR rc; + MCI_DGV_PUT_PARMS putParm; + RECT rect; + + if (mMCIWindow == NULL) + return false; + + GetClientRect(mMCIWindow, &rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect + 1); + + putParm.dwCallback = NULL; + putParm.rc.left = rect.left; + putParm.rc.top = rect.top; + putParm.rc.right = rect.right; + putParm.rc.bottom = rect.bottom; + + rc = mciSendCommand(mDeviceID, MCI_PUT, MCI_WAIT | MCI_DGV_RECT | + MCI_DGV_PUT_DESTINATION, (DWORD)&putParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* AttachWindow +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::AttachWindow(HWND window) + { + MCIERROR rc; + MCI_DGV_WINDOW_PARMS winParm; + + mMCIWindow = window; + + memset((void*)&winParm, 0, sizeof(winParm)); + winParm.dwCallback = NULL; + winParm.hWnd = window; + winParm.nCmdShow = SW_SHOW; + + rc = mciSendCommand(mDeviceID, MCI_WINDOW, MCI_WAIT| MCI_DGV_WINDOW_HWND | + MCI_DGV_WINDOW_STATE, (DWORD)&winParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + +#endif // MCIMPEG + diff --git a/CODE/MCIMOVIE.H b/CODE/MCIMOVIE.H new file mode 100644 index 0000000..17bf7a8 --- /dev/null +++ b/CODE/MCIMOVIE.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +#ifndef _MCIMOVIE_H_ +#define _MCIMOVIE_H_ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include +#include "watcom.h" + +class MCIMovie + { + public: + MCIMovie(HWND mainWindow); + ~MCIMovie(); + + bool Open(const char* name, const char* device); + bool Play(HWND window); + bool Pause(void); + bool Close(void); + + LONG GetWidth(void) + {return ((mDeviceID) ? mWidth : 0);} + + LONG GetHeight(void) + {return ((mDeviceID) ? mHeight : 0);} + + protected: + HWND mMainWindow; // Application window + HWND mMCIWindow; // Callback window + char *mName; + UINT mDeviceID; + MCI_OPEN_PARMS mOpenParm; + MCI_PLAY_PARMS mPlayParm; + + // Video stream dimension + LONG mWidth, mHeight; + + private: + bool SizeDestination(void); + bool AttachWindow(HWND window); + + static int mRegistered; + static WNDCLASS mWndClass; + static HINSTANCE mInstance; + }; + +#endif // MCIMPEG +#endif // _MCIMOVIE_H_ diff --git a/CODE/MEMCHECK.H b/CODE/MEMCHECK.H new file mode 100644 index 0000000..0ec45af --- /dev/null +++ b/CODE/MEMCHECK.H @@ -0,0 +1,2605 @@ +/* +** 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 . +*/ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# + + MemCheck 3.0 Professional for DOS + + Copyright (c) 1990-1994, StratosWare Corporation. + All rights reserved. + + 1-800-WE-DEBUG + + Note to Developers: + -------------------- + This file should be #included AFTER any other #includes in + each source file which is to be memory checked, and BEFORE + any code that performs any operations on allocated pointers. + If it isn't, MemCheck will not pick up source file and line + information for intercepted functions. + + The MCCONFIG.EXE utility distributed with MemCheck 3.0 + will do this safely and quickly for you. + + Most specifically, this header file MUST NOT come before + any prototypes of routines that MemCheck intercepts, like + malloc(), free(), strcpy(), and so on. + + The Final Cut: + --------------- + To ENTIRELY remove MemCheck from your code, just #define + the constant "NOMEMCHECK", or equivalently, "NOMC". + + This header file will then automatically 'evaporate' all + MemCheck 3.0 calls. This is MUCH PREFERABLE to placing + #if-#endif's around the header file's inclusion, as in + + #ifdef DEBUG // DON'T DO THIS! + #include + #endif + + Using the "#ifdef DEBUG" as above doesn't allow the + MemCheck header file to evaporate the MemCheck 3.0 API + calls you may have placed in your code, like mc_startcheck() + and mc_endcheck(). You would then have to surround + each MemCheck API call with additional #if-#endif's. + + Modification History + + WHO WHEN WHAT + KWB 07/28/93 Gussy for beta + KWB 09/11/93 Add new placement overload, NEW() C++ stuff + KWB 11/08/93 Final QA/QC for initial release + KWB 12/02/93 LINT -save added + KWB 02/19/94 Fixed function inlining holes, added macros + for underscore func variants under MSC 7+ + KWB 07/08/94 Added defines for BC extender (_CC_POWERPACK_) + KWB 07/09/94 Added special case for STACKTOP, END under PowerPack + KWB 08/04/94 Added cdecl modifier to stklen, atopsp etc. decls + KWB 08/11/94 Associated _CC32_ with _PROTECTED_ in ccdefs section; + Changed method of determining compiler model, + e.g. _CC_MSC6_ from if == to if >= + Associated DOSX286 with _PROTECTED_ for Phar Lap + Added MC_SET_EXCEPTF, mc_set/get_exceptf() + KWB 08/17/94 Added new[] support filtering (_CPP_ANSI20_) + KWB 08/18/94 Changed _MCFARCALL to _MCFARGLUE + KWB 09/13/94 Added erf_printf as alias for erf_stdout + KWB 09/14/94 Added endf_summary + KWB 09/21/94 Added MCCRITF and mc_set_critf() & related + KWB 10/10/94 Added #if !defined(setmem) etc. for BC DPMI32 + KWB 10/11/94 Added _CC_BORLANDx_ comp def, 'x' = major ver + +*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + + +/* Avoid multiple inclusions */ +#ifndef _MEMCHECK_H_ +#define _MEMCHECK_H_ + +/* Prevent inclusion in Windows(tm) compilations */ +#if defined(_Windows) || defined(WINDOWS) || defined(_WINDOWS) +# if !defined (__DPMI16__) && !defined (__DPMI32__) && !defined (DOSX286) +# error DOS version of MemCheck header file #included! +# endif +#endif + +/* Shorthand equivalence, V2.0 backwards compatibility... */ +#if defined (NOMC) || defined (NOMEMCHK) +# define NOMEMCHECK +#endif + +/* C++ new interception -- see note later and + read the MemCheck 3.0 User's Manual, section + "Integration With C++." Uncommenting the next line + and following simple instructions allows seamless + transmission of the exact file and line location + of new's in your source code. +*/ +/* #define NEW_OVERLOADED */ + + +/* *** Backwards compatibility with V2.0 *** */ + +#define mc_isactive mc_is_active /* standardize naming... */ +#define mc_getmode mc_get_mode +#define mc_errorstatus mc_error_flags +#define mc_verify_memory mc_check_buffers + +#define MC_USEDISK MC_USING_DISK +#define MC_USEMEM MC_USING_MEMORY + + +/* *** Backwards compatibility with V2.1 *** */ + +#define MCLINENO MCSL /* "MemCheck Source Line" */ +#define MFUNC ERF /* error reporting function */ + +#define mc_set_msgfunc mc_set_erf /* "Message funcs" are now */ +#define mc_get_msgfunc mc_get_erf /* universally referred to as */ +#define mc_error_status mc_error_flags /* "error reporting functions"*/ + +/* Maintain source code compatibility with version 2.1. + Buffer registration is simplified to + just calling "mc_register", regardless of model. + Same with buffer checking, e.g. "mc_check_far" + and "mc_check_near" are rolled into "mc_check". +*/ +#define mc_register_near(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_register_far(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_unregister_near(p) mc_unregister((void _MCFAR *)(p)) +#define mc_unregister_far(p) mc_unregister((void _MCFAR *)(p)) +#define mc_check_near(p) mc_check((void _MCFAR *)(p)) +#define mc_check_far(p) mc_check((void _MCFAR *)(p)) + +/* Error Number Definitions + Returned by mc_endcheck() and mc_error_flags(). + In MemCheck 3.0, there's now a global error number much + like the "errno" variable in standard C. +*/ +#define MCE_NO_ERROR 0 /* it's debugging time & all well */ +#define MCE_NULL_SOURCE 1 /* null source ptr on copy */ +#define MCE_NULL_DEST 2 /* null dest ptr on copy */ +#define MCE_UNDERWRITE 3 /* allocated buf underwritten (front) */ +#define MCE_OVERWRITE 4 /* allocated buf overwritten (end) */ +#define MCE_LEAK 5 /* forgot to free alloc'd memory */ +#define MCE_LEAKAGE MCE_LEAK +#define MCE_UNFREED_MEMORY MCE_LEAK +#define MCE_NULL_PTR_ASSIGN 6 /* assigned data through null ptr */ +#define MCE_BAD_STACK_PTR 7 /* bad stack pointer */ +#define MCE_STACK_OVERWRITE 8 /* copy trashes stack frame */ +#define MCE_INTERNAL 9 /* internal error msg */ +#define MCE_OVERLAPPING_COPY 10 /* source overlaps dest on copy */ +#define MCE_INVALID_PTR 11 /* bad ptr on free, realloc */ +#define MCE_DEST_OVERWRITE 12 /* copy too big for dest buffer */ +#define MCE_OUT_OF_MEMORY 13 /* out of memory */ +#define MCE_OOM MCE_OUT_OF_MEMORY +#define MCE_GPF_PTR 14 /* ptr caused GPF */ + + +/* MemCheck Error Flags + + The MemCheck error flag is just an unsigned long + (specifically, a MCEFLAGS typedef) with "sticky" bits + corresponding to the above MCE_... error numbers. + You can use the error flags macro MC_EFLAG(e) to + test the global MC_Errno double flag word. +*/ +/* Returns a long word with the e-th bit set */ +#define MC_EFLAG(e) ( (e) ? ((MCEFLAGS)1 << (e-1)) : (MCEFLAGS)0) + +#define EFLAG_NULL_PTR MC_EFLAG(MCE_NULL_DEST) +#define EFLAG_BAD_PTR MC_EFLAG(MCE_UNALLOCED_PTR) +#define EFLAG_FRONT_MAGIC MC_EFLAG(MCE_UNDERWRITE) +#define EFLAG_BACK_MAGIC MC_EFLAG(MCE_OVERWRITE) +#define EFLAG_PTRS_NOT_FREED MC_EFLAG(MCE_LEAK) +#define EFLAG_TRACK_FILE 0 /*obsolete in 3.0+*/ +#define EFLAG_NULL_ASSIGN MCE_FLAG(MCE_NULL_PTR_ASSIGN) + +/* *** End Compatibility Section *** */ + + +/* *** MemCheck Compiler Constant Definitions *** */ + +/* + Compiler Defines + -------- ------- + Microsoft _CC_MSC_, _CC_MSC_COMPATIBLE_ + V8.x _CC_MSC8_ + V7.x _CC_MSC7_ + V6.x _CC_MSC6_ + V5.x _CC_MSC5_ + Borland* _CC_BORLAND_, _CC_BCC_ + V3.x _CC_BORLAND3_ + V4.x _CC_BORLAND4_ + PowerPack/16 _CC_POWERPACK_, _CC_POWERPACK16_ + PowerPack/32 _CC_POWERPACK_, _CC_POWERPACK32_, _CC32_ + WATCOM _CC_WATCOM_, _CC_MSC_COMPATIBLE_ + WAT/386 _CC_WATCOM32_, _CC32_ + + * Borland has no good way of determining compiler + version. __BORLANDC__ returns some truly funky + hex constant that "will increase in future versions." + + Define Meaning + ------ -------- + _CC32_ * 32-bit compile + _PROTECTED_ 16- or 32-bit protected mode compile + LCODE Defined if large code model + LDATA Defined if large data model + STACKTOP Highest stack address (top) + STACKEND Lowest stack address + STACKLEN Stack length (top - end) + _CPP_ANSI20_ Compiler supports C++ 2.0, e.g. new[] + + * If _CC32_ is defined, _PROTECTED_ is also defined +*/ + + +#ifndef _CCDEFS_H_ +#define _CCDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* NOTE: Microsoft C 5.x users have to "#define _MSC_VER 500" + at the top of this file. +*/ +#if defined(_MSC_VER) +/* + _MSC_VER Microsoft C version; currently defined as 700. + M_I86 _M_I86 Member of the I86 processor family. + M_I86mM _M_I86mM Memory model type: + = T Tiny + S Small (default) + C Compact model + M Medium model + L Large model + H Huge model + Identifiers defined by /AT, /AS, /AC, /AM, + /AL, and /AH, respectively. + _MSDOS MS-DOS operating system. + _QC Microsoft QuickC Compiler. + _WINDLL Windows protected-mode dynamic-link library + is selected with /GD. + _WINDOWS Windows protected-mode is selected with /GA, + /Gn, /GW, /Mq, or /GD. +*/ +# define _CC_MSC_ +# define _CC_MSC_COMPATIBLE_ + +# if (_MSC_VER >= 800) +# define _CC_MSC8_ +# elif (_MSC_VER >= 700) +# define _CC_MSC7_ +# elif (_MSC_VER >= 600) +# define _CC_MSC6_ +# elif (_MSC_VER >= 500) /* see note above */ +# define _CC_MSC5_ +# else +# error MemCheck.h: unrecognized version of Microsoft compiler! +# endif + +#elif defined(__BORLANDC__) +# define _CC_BORLAND_ /* Borland product */ +# define _CC_BCC_ /* Borland C compiler */ + + /* Major compiler rev */ +# if (__BORLANDC__ >= 0x0450) +# define _CC_BORLAND4_ +# elif (__BORLANDC__ >= 0x400) +# define _CC_BORLAND3_ +# else + /* not needed */ +# endif + + /* Borland 4.0 PowerPack BCC.EXE defines (-WX): + __DPMI16__ _Windows + + With -WXD, -WXDE: + __DLL__ __DPMI16__ _Windows + + Borland 4.0 PowerPack BCC21.EXE defines (-WX): + __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + + With -WXD: + __DLL__ __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + */ +# if defined(__DPMI16__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK16_ +# define _PROTECTED_ +# endif +# if defined(__DPMI32__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK32_ +# define _CC32_ /* flat model */ +# endif + +/* Turbo C++ */ +#elif defined(MCTCP) /* homebrew */ +# define _CC_BORLAND_ /* Borland product */ +# define _CC_TCP_ /* Turbo C++ */ + +#elif defined(__TURBOC__) +/* + __TURBOC__ Gives the current Turbo C version + number, a hexadecimal number. Version + 1.0 id 0x1000; version 1.2 is 0x0102, etc. + __TINY__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__, __HUGE__ + Model defintions + __MSDOS__ Signals that we're not yet in the + twenty-first century +*/ +# define _CC_BORLAND_ /* Borland C product */ +# define _CC_TCC_ /* Turbo C/C++ compiler */ + + +#elif defined(_INTELC32_) +/* + _INTELC32_ has the value 1. + _ARCHITECTURE_ is 386 if the nomod486 pragma is ineffect, + 486 otherwise. +*/ +# define _CC_INTEL_ /* Intel Code Builder */ +# define _CC_MSC_COMPATIBLE_ +# define _CC32_ /* flat model */ + +#elif defined(__WATCOMC__) +/* + __WATCOMC__ Used to determine if the WATCOM C + or C/386 compiler is compiling + __386__ identifies the target machine as an + Intel 80386 under the WATCOM C/386 compiler + MSDOS Signals that we're not yet in the + twenty-first century + __FLAT__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__ + Model defintions (flat is default) + Also id's MSC-compatible model defines + + 8.5 and later: + __WINDOWS__ Identifies 16-bit Windows ("zw" or "zW" opts) + __WINDOWS_386__ 32-bit Microsoft Windows "zW" opt + __NETWARE_386__ Novell Netware 386, defined by the + Novell/Watcom C + __OS2__ IBM OS/2-hosted version of Watcom +*/ +# define _CC_WATCOM_ /* Watcom C product */ +# define _CC_MSC_COMPATIBLE_ +# ifdef __386__ +# define _CC32_ /* flat model */ +# define _CC_WATCOM32_ +# endif + + +#elif defined(__STDC__) /* Standard ANSI C */ +# define _CC_ANSI_ +# define _CC32_ /* no segmentation via far/near */ +# define far +# define near +# define huge +# define cdecl + +/* Avoids parameter mismatches from _far, etc. */ +# define _far +# define _near +# define _huge +# define _cdecl + +#else /* UNSUPPORTED / UNRECOGNIZED COMPILER */ + +#error MemCheck 3.0: unrecognized compiler +/* + If you're using Microsoft C5.1, you must + define the constant _MSC_VER, e.g. + + #define _MSC_VER 500 + + Place this statement at the top of this + header file or in your project header file + BEFORE the MemCheck header file is included. + + Microsoft C 5.0 is NOT supported (it's non-ANSI). +*/ + +#endif /* compiler constant definitions */ + +/* END of _CC..._ name setups; ripple & alias */ + +/* Sheer utility & avoidance of !_CC32_ */ +#ifndef _CC32_ +# define _CC16_ +#endif + +/* 32-bit compilers are always protected mode */ +#ifdef _CC32_ +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* CC32 */ + +/* Phar Lap support */ +#ifdef DOSX286 +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* DOSX286 */ + +/* C++ 2.0 compliance: _CPP_ANSI20_ */ +#if defined(_CC_BORLAND_) + /* Only BC++ 4.x and later, but Borland has + seriously mangled version info (__BORLANDC__) */ +# if defined (__BCPLUSPLUS__) +# if (__BCPLUSPLUS__ > 0x0310) /* after 3.1 */ +# define _CPP_ANSI20_ +# endif +# elif defined(_CC_POWERPACK_) +# define _CPP_ANSI20_ +# endif +#elif defined (_CC_MSC_) + /* Current release through 8.x doesn't support new[] */ +#elif defined (_CC_WATCOM_) +# if (__WATCOMC__ >= 1000) /* 10.x */ +# define _CPP_ANSI20_ +# endif +#endif + + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Code and Data Size Constants *** + LCODE not used by this header file. + LDATA *is* used, however. + + May be commented out if these constants are already defined. +*/ + +/* #ifndef LCODE */ +#if defined(M_I86MM) || defined(M_I86LM) || defined(M_I86HM) +# define LCODE 1 +#elif defined(__MEDIUM__) || defined(__LARGE__) || defined(__HUGE__) +# define LCODE 1 +#endif +/* #endif */ + +#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__) +# define LDATA 1 +#elif defined(M_I86CM) || defined(M_I86LM) || defined(M_I86HM) +# define LDATA 1 +#endif + +/* Macro "aliases" */ + +#ifndef _LCODE +# ifdef LCODE +# define _LCODE LCODE +# endif +#endif + +#ifndef _LDATA +# ifdef LDATA +# define _LDATA LDATA +# endif +#endif + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Physical Stack Address *** */ + +#if defined(_CC_BORLAND_) /* -------------------------- */ + + /* + BORLAND RTL Notes: + ;---------------------------------------------------------------------- + ; In large data models, the stack is in its own segment. The stack + ; starts at SS:__stklen and grows down to SS:0. + ; + ; In small data models, the stack is in the DGROUP, and grows down + ; to meet the heap. The end of the heap is marked by ___brklvl. + (KWB: Note that the brklvl is adjusted upwards until it meets + _stklen...) + ;---------------------------------------------------------------------- + */ + +# define STACKSLOP 256 + extern unsigned cdecl _stklen; + +# if defined(_CC_POWERPACK_) +# define STACKTOP (mc_stacktop()) +# define STACKEND (mc_stackend()) +# else /* not P-Pack */ +# ifdef LDATA + /* Compact, Large, Huge Models ... + + The stack starts at SS:_stklen and + grows downward to SS:0 + */ +# define STACKTOP ((unsigned) _stklen) +# define STACKEND ((unsigned) 0 + STACKSLOP) + +# else + /* Small, Medium Models ... + + The stack starts at SS:0xFFFE and grows + downwards _stklen bytes. + */ +# define STACKTOP ((unsigned) 0xFFFE) +# define STACKEND (STACKTOP - _stklen + STACKSLOP) +# endif +# endif /* not PowerPack */ + +#elif defined (_CC_MSC_) /* ------------------------------- */ + + extern char cdecl end; /* end of stack */ + extern unsigned cdecl _atopsp; /* top of stack */ + +# define STACKTOP _atopsp +# define STACKSLOP 256 + +# ifdef LDATA + /* If in far data, stack could be in its own + seg. tho not usually. see /FARSTACK */ +# define STACKEND ((unsigned) ((unsigned long)&end) + STACKSLOP) +# else + /* If near data, must be in DS; use near ptr */ +# define STACKEND ((unsigned)&end + STACKSLOP) +# endif + +#elif defined (_CC_WATCOM_) /* ------------------------------- */ + + extern unsigned _STACKLOW; /* end of stack */ + extern unsigned _STACKTOP; /* top of stack */ + +# define STACKTOP (_STACKTOP) +# define STACKSLOP 256 + +# ifdef LDATA +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# else +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# endif + +#else /* Unknown compiler ---------------- */ + +#error Unknown compiler at __FILE__(__LINE__) + +#endif /* defining stack top, end */ + +/*******************************************************************/ +/*******************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _ccdefs already #included */ + +/* End CCDEFS */ + + +#if !defined (NULL) /* pull in stdio.h if not already */ +# include +#endif + +/* Backup... sometimes NULL defined in other headers */ +#if !defined (_IOFBF) /* pull in stdio.h if not already */ +# include +#endif + + +/* *** MemCheck Constants *** */ + +/* Standard from MemCheck 3.0 onwards. + Access major version and revision + via mc_version() and mc_revision() macros. +*/ +#define _MC_VERSION 0x0300 /* welcome 3.0 the powerful */ + +#define mc_version() ((int)((_MC_VERSION & 0xFF00) >> 8)) +#define mc_revision() ((int)(_MC_VERSION & 0x00FF)) + +#if defined (_CC32_) /* 32-bit Intel target */ +#define PRT_FP "0x%p" +#else +#define PRT_FP "%Fp" +#endif + +/* *** MCID Macro *** */ + +/* Allows later flexibility in assigning mapping... + Also makes MCIDs formulaic +*/ +#define _MCID(f) (MCID)(__paste(MCID_,f)) + +/* + MemCheck Function ID's (MCID's) + + These are the indices used to retrieve information + about specific runtime library calls. +*/ +/* --- MEMCHECK INTERNAL FUNCTIONS --- */ + +#define _MCID_FIRST_INTERNAL 0 /* index of first internal func */ + +#define MCID_mc_startcheck 0 +#define MCID_mc_endcheck 1 +#define MCID_mc_check_buffers 2 +#define MCID_mc_check 3 +#define MCID_mc_register 4 +#define MCID_mc_unregister 5 +#define MCID_mc_set_alignsize 6 +#define MCID_mc_set_checkbytes 7 +#define MCID_mc_nullcheck 8 +#define MCID_mc_breakpoint 9 +#define MCID_mc_debug 10 +#define MCID_mc_set_location 11 +#define MCID_mc_stack_trace 12 +#define MCID_mc_report 13 + +#define _MCID_LAST_INTERNAL 14 /* Set = to last internal ID */ + + +/* *************** STANDARD C ROUTINES ******************* */ + +#define _MCID_FIRST_ANSI (_MCID_LAST_INTERNAL+1) + +#define MCID_calloc (_MCID_FIRST_ANSI + 0) +#define MCID_free (_MCID_FIRST_ANSI + 1) +#define MCID_malloc (_MCID_FIRST_ANSI + 2) +#define MCID_memcpy (_MCID_FIRST_ANSI + 3) +#define MCID_memmove (_MCID_FIRST_ANSI + 4) +#define MCID_memset (_MCID_FIRST_ANSI + 5) +#define MCID_realloc (_MCID_FIRST_ANSI + 6) +#define MCID_sprintf (_MCID_FIRST_ANSI + 7) +#define MCID_strcat (_MCID_FIRST_ANSI + 8) +#define MCID_strcpy (_MCID_FIRST_ANSI + 9) +#define MCID_strncat (_MCID_FIRST_ANSI + 10) +#define MCID_strncpy (_MCID_FIRST_ANSI + 11) +#define MCID_vsprintf (_MCID_FIRST_ANSI + 12) + +#define _MCID_LAST_ANSI MCID_vsprintf /* define to last ANSI */ + + +/* *************** MICROSOFT C ******************* */ + +#if defined(_CC_MSC_COMPATIBLE_) + +#define _MCID_FIRST_MSC (_MCID_LAST_ANSI + 1) + +# define MCID__expand (_MCID_FIRST_MSC + 0) +# define MCID__ffree (_MCID_FIRST_MSC + 1) +# define MCID__fcalloc (_MCID_FIRST_MSC + 2) +# define MCID__fmalloc (_MCID_FIRST_MSC + 3) +# define MCID__fmsize (_MCID_FIRST_MSC + 4) +# define MCID__frealloc (_MCID_FIRST_MSC + 5) +# define MCID__fexpand (_MCID_FIRST_MSC + 6) +# define MCID__msize (_MCID_FIRST_MSC + 7) + +# define MCID__fmemmove (_MCID_FIRST_MSC + 8) +# define MCID__fmemcpy (_MCID_FIRST_MSC + 9) +# define MCID__fmemset (_MCID_FIRST_MSC + 10) +# define MCID__fmemccpy (_MCID_FIRST_MSC + 11) +# define MCID__fstrcat (_MCID_FIRST_MSC + 12) +# define MCID__fstrncat (_MCID_FIRST_MSC + 13) +# define MCID__fstrcpy (_MCID_FIRST_MSC + 14) +# define MCID__fstrncpy (_MCID_FIRST_MSC + 15) +# define MCID__fstrdup (_MCID_FIRST_MSC + 16) +# define MCID__fstrset (_MCID_FIRST_MSC + 17) +# define MCID__fstrnset (_MCID_FIRST_MSC + 18) + +# define MCID__nfree (_MCID_FIRST_MSC + 19) +# define MCID__nmalloc (_MCID_FIRST_MSC + 20) +# define MCID__ncalloc (_MCID_FIRST_MSC + 21) +# define MCID__nrealloc (_MCID_FIRST_MSC + 22) +# define MCID__nexpand (_MCID_FIRST_MSC + 23) +# define MCID__nmsize (_MCID_FIRST_MSC + 24) +# define MCID__nstrdup (_MCID_FIRST_MSC + 25) + +# define MCID__dos_setvect (_MCID_FIRST_MSC + 26) +# define MCID__getdcwd (_MCID_FIRST_MSC + 27) + +/* Here starts the Great ANSI Divide. + MSC6 and earlier have no underscores; + MSC7 and later *have* underscores to emphasize + departure from ANSI... +*/ +#if defined(_CC_MSC_) && !defined (MSC6) /* not MSC6-compatible */ + +# define MCID__getcwd (_MCID_FIRST_MSC + 28) +# define MCID__cgets (_MCID_FIRST_MSC + 29) +# define MCID__halloc (_MCID_FIRST_MSC + 30) +# define MCID__hfree (_MCID_FIRST_MSC + 31) +# define MCID__memccpy (_MCID_FIRST_MSC + 32) +# define MCID__strdup (_MCID_FIRST_MSC + 33) +# define MCID__strnset (_MCID_FIRST_MSC + 34) +# define MCID__strset (_MCID_FIRST_MSC + 35) +# define MCID__swab (_MCID_FIRST_MSC + 36) +# define MCID__tempnam (_MCID_FIRST_MSC + 37) + +#else /*** MSC6 and before; WATCOM ***/ + +/* No leading underscores */ + +# define MCID_getcwd (_MCID_FIRST_MSC + 28) +# define MCID_cgets (_MCID_FIRST_MSC + 29) +# define MCID_halloc (_MCID_FIRST_MSC + 30) +# define MCID_hfree (_MCID_FIRST_MSC + 31) +# define MCID_memccpy (_MCID_FIRST_MSC + 32) +# define MCID_strdup (_MCID_FIRST_MSC + 33) +# define MCID_strnset (_MCID_FIRST_MSC + 34) +# define MCID_strset (_MCID_FIRST_MSC + 35) +# define MCID_swab (_MCID_FIRST_MSC + 36) +# define MCID_tempnam (_MCID_FIRST_MSC + 37) + +#endif /* MSC6 ANSI calls */ + +# define MCID_new (_MCID_FIRST_MSC + 38) +# define MCID_delete (_MCID_FIRST_MSC + 39) +# define MCID__fullpath (_MCID_FIRST_MSC + 40) + +#endif /* Microsoft C-compatible calls */ + + +/* *************** BORLAND C ******************* */ + +#if defined (_CC_BORLAND_) + +#define _MCID_FIRST_BC (_MCID_LAST_ANSI + 1) + +# define MCID__fmemmove (_MCID_FIRST_BC + 0) +# define MCID__fmemcpy (_MCID_FIRST_BC + 1) +# define MCID__fmemset (_MCID_FIRST_BC + 2) +# define MCID__fmemccpy (_MCID_FIRST_BC + 3) +# define MCID__fstrcat (_MCID_FIRST_BC + 4) +# define MCID__fstrncat (_MCID_FIRST_BC + 5) +# define MCID__fstrcpy (_MCID_FIRST_BC + 6) +# define MCID__fstrncpy (_MCID_FIRST_BC + 7) +# define MCID__fstrdup (_MCID_FIRST_BC + 8) +# define MCID__fstrset (_MCID_FIRST_BC + 9) +# define MCID__fstrnset (_MCID_FIRST_BC + 10) + +# define MCID__dos_setvect (_MCID_FIRST_BC + 11) +# define MCID__getdcwd (_MCID_FIRST_BC + 12) + +# define MCID_getcwd (_MCID_FIRST_BC + 13) +# define MCID_cgets (_MCID_FIRST_BC + 14) +# define MCID_memccpy (_MCID_FIRST_BC + 15) +# define MCID_strdup (_MCID_FIRST_BC + 16) +# define MCID_strnset (_MCID_FIRST_BC + 17) +# define MCID_strset (_MCID_FIRST_BC + 18) +# define MCID_swab (_MCID_FIRST_BC + 19) +# define MCID_tempnam (_MCID_FIRST_BC + 20) + +# define MCID_farmalloc (_MCID_FIRST_BC + 21) +# define MCID_farrealloc (_MCID_FIRST_BC + 22) +# define MCID_farfree (_MCID_FIRST_BC + 23) +# define MCID_farcalloc (_MCID_FIRST_BC + 24) +# define MCID_movmem (_MCID_FIRST_BC + 25) +# define MCID_setmem (_MCID_FIRST_BC + 26) +# define MCID_setvect (_MCID_FIRST_BC + 27) +# define MCID_stpcpy (_MCID_FIRST_BC + 28) +# define MCID__fmovmem (_MCID_FIRST_BC + 29) +# define MCID__fsetmem (_MCID_FIRST_BC + 30) +# define MCID_new (_MCID_FIRST_BC + 31) +# define MCID_delete (_MCID_FIRST_BC + 32) +# define MCID__fullpath (_MCID_FIRST_BC + 33) + +#endif + + +/* + 'TOUCH' macro so high warning levels don't generate + 'unreferenced variable' warnings, especially when + making Production libraries... All MemCheck code + compiles without a whymper. +*/ +#if defined (_CC_WATCOM_) +# define TOUCH(var) var = var +#elif defined (_CC_BORLAND4_) +# define TOUCH(var) var = var +#else +# define TOUCH(var) if (var) +#endif + + +/* Default log name used by stock erf_logfile() and variants... */ +#define MEMCHECK_LOG "MEMCHECK.LOG" + +#define MAX_MEMORY 1000 /* 1000K is more than ever possible */ + +/* User-Modifiable Defaults */ + +#define D_CheckByteCt sizeof(int) /* word size is default */ +#define D_AlignSize sizeof(int) /* align returned memory ptrs */ + +/* Number of bytes to copy from null segment (to determine null + pointer assignments) +*/ +#define D_NULLCHECK_BYTES_FAR 16 /* at 0000:0000 (far NULL) */ +#define D_NULLCHECK_BYTES_NEAR 16 /* at DS:0000 (near NULL) */ +#define MAX_NULLCHECK_BYTES_FAR 1024 /* extent of irupt vect tbl */ +#define MAX_NULLCHECK_BYTES_NEAR 66 /* reserved in DS */ + +/* Unroll the double-negative */ +/* + Debugging code specific to MemCheck can be + conditionally compiled by placing it within + #if-#endif sections: (NOTE that this is NOT + required when just using API functions) + + #ifdef MEMCHECK + + void _MCCALLBACK trackf_special (int op, MEMRECP memrecp) + { + (... your custom callback code ...) + } + + #endif + + instead of the more arcane + + #ifndef NOMEMCHECK + : + #endif + + (Both approaches work equally well, however...) +*/ +#ifndef NOMEMCHECK /* MemCheck active */ +#define MEMCHECK +#endif + + +/* *** Calling Conventions *** */ + +#if !defined (_CC_ANSI_) +#define _MCAPI pascal /* MemCheck API functions */ +#define _FASTAPI pascal /* speed-critical functions */ +#define _MCCDECL cdecl /* MemCheck varargs API */ +#define _MCCALLBACK cdecl /* callback functions */ +#define _MCVAR cdecl /* MemCheck global variable */ +#else +#define _MCAPI /* MemCheck API functions */ +#define _FASTAPI /* speed-critical functions */ +#define _MCCDECL /* MemCheck varargs API */ +#define _MCCALLBACK /* callback functions */ +#define _MCVAR /* MemCheck global variable */ +#endif + +#if !defined(_CC_WATCOM_) +# define _RTL _MCCDECL /* RTL calling conv */ +#else +# define _RTL /* RTL calling conv */ + +/* WATCOM C++ does not currently (2/17/94) + accept "cdecl" as a modifier on variables... +*/ +# undef _MCVAR +# define _MCVAR +#endif /* _CC_WATCOM_ */ + +/* 32-bit compiler-independent stuff */ +#if !defined(_CC32_) +#define _MCFAR far +#define _MCFARCALL far +#define _MCNEAR near +#define _MCNEARCALL near +#define _MCHUGE huge +#else +#define _MCFAR +#define _MCFARCALL +#define _MCNEAR +#define _MCNEARCALL +#define _MCHUGE +#endif /* _CC32_ */ + +/* + MSC declares the following routines as "far"... + So does Borland. WATCOM does not; define glue. + + _fstrset _fstrnset _fstrcpy + _fstrncpy _fstrcat _fstrncat + _fmemset _fmemmove _fmemccpy +*/ +#if !defined(_CC_WATCOM_) +# define _MCFARGLUE far +#else +# define _MCFARGLUE +#endif + + +/* Microsoft C7 and later will not have + have a malloc_mc, only _fmalloc_mc; likewise + with free_mc. + The RTLMALLOC and RTLFREE macros are used + to refer to a generically present back-end malloc + and free. +*/ +#if defined (_CC_MSC_) +# if defined (LDATA) +# define RTLMALLOC RTL(_fmalloc) +# define RTLFREE RTL(_ffree) +# else +# define RTLMALLOC RTL(_nmalloc) +# define RTLFREE RTL(_nfree) +# endif +#else /* non-MSC */ +# define RTLMALLOC RTL(malloc) +# define RTLFREE RTL(free) +#endif + + +/* WATCOM defines its atexit funcs as a "register", + which causes a param type mismatch. + _ATEXITFUNC calling convention smooths this out. +*/ +#if defined (_CC_WATCOM_) +# define _ATEXITFUNC +#else +# define _ATEXITFUNC _MCCDECL +#endif + + +/* MemCheck Tracking Mode + + Returned by mc_get_mode(). + Indicates whether information on each allocation + is being stored in memory or on disk. +*/ +#define MC_USING_MEMORY 1 +#define MC_USING_DISK 2 + + +/* Min, max orders for each node in the B-tree */ + +#define BT_ORDER_MIN 5 +#define BT_ORDER_MAX 255 /* maximum tree order */ +#define BT_ORDER_DEFAULT 19 /* default tree order */ + +/* + Returned by mc_get_speed(). + Pass as params to mc_set_speed(). +*/ +#define MC_RUN_NORMAL 1 +#define MC_RUN_FAST 2 + +/* For mc_report(): + "Flags" field of the MEMREC structure + is set to REPORT_START or REPORT_END + to indicate begin and end of report. + + NOTE: If REPORT_START or REPORT_END conflicts + with defines in your project, just comment + them out and use the MC_... variants instead. +*/ +#define REPORT_START (MRFLAGS)0xFE +#define REPORT_END (MRFLAGS)0xFD + +#define MC_REPORT_START (MRFLAGS)0xFE /* alternates in case of conflict */ +#define MC_REPORT_END (MRFLAGS)0xFD + + +/* + Maximum number of breakpoints that + can be set via mc_breakpoint(). +*/ +#define MC_MAX_BREAKS 50 + + +/* "Optype" parameter on Tracking function callback. */ +#define TRACKF_ADD 1 /* record being added to tree */ +#define TRACKF_DEL 2 /* record being deleted from tree */ + +/* Used for the mcflags field of MEMREC to indicate + whether file & line are exact or approximate +*/ +#define MRFLAG_EXACT_LOC ( (MRFLAGS) 0x01) + +/* + Set if the values for a MEMREC are already converted + to "user" values. +*/ +#define MRFLAG_USER_SPECS ( (MRFLAGS) 0x02) +#define MRFLAG_NO_CHECKBYTES ( (MRFLAGS) 0x04) + +/* Alternate name */ +#define mc_message mc_debug + +/* + Parameter to mc_check_transfer() that + specifies that the given data transfer function cannot + have overlapping source & destination. + (MCID's are unsigned bytes.) +*/ +#define MCF_NO_OVERLAP ((unsigned)0x8000) +#define NO_OVERLAP(mcid) ((mcid) | MCF_NO_OVERLAP) + +/* Parameter to mc_check_transfer indicating that + the found memory record is not needed */ +#define NO_MEMREC ((MEMRECP)NULL) +#define NOMEMREC NO_MEMREC + +/* Parameter to mc_check_transfer indicating that + there is no source pointer operand associated + with the data transfer being checked: e.g. memset. */ +#define NO_SOURCE ((void _MCFAR *)0xFFFFFFFA) + + +/* *** TYPEDEFS *** */ + +typedef char * MCSF; /* MemCheck source file */ +typedef unsigned int MCSL; /* MemCheck source line */ +typedef unsigned char MCID; /* MemCheck function ID */ + +typedef unsigned long MCEFLAGS; /* MemCheck error flags */ +typedef void _MCFAR * MCPTR; /* type of ptr stored in tree */ +typedef unsigned char MRFLAGS; /* flags in MEMRECORD */ +typedef unsigned long MCFLAGS; /* MemCheck settings flags */ + +/* MemCheck Rocket allocator prototypes */ +typedef void _MCFAR * (_MCFAR *ROCKETALLOCF) (size_t); +typedef void (_MCFAR *ROCKETFREEF) (void _MCFAR *); + +#pragma pack(1) +/* + Memory Tracking Structure (MEMREC) + + This is the data structure for buffers being + tracked by MemCheck. +*/ +typedef struct MemRecord + { + MCPTR ptr; /* heap/registered ptr */ + MCID mcid; /* MemCheck function ID */ + MRFLAGS flags; /* internal MC flags */ + unsigned long allocno; /* cardinality of allocation */ + unsigned long size; /* size of block */ + MCSF file; /* source file */ + MCSL line; /* source line */ + + } MEMREC, _MCFAR *MEMRECP; + + +/* *** SETTINGS *** */ +/* These are values that describe the life of a MemCheck run. */ + +typedef struct MCSETTINGS { + /* + Bit Flag What + --- --------------- ----------------------------------- + 0 MCF_ACTIVE MemCheck active or off + 1 MCF_FAST_MODE Fast mode or normal + 2 MCF_PROTECTED_MODE Protected mode or real + 3 MCF_FAR_NULL_CHECK Check for far NULL ptr assigns * + 4 MCF_NEAR_NULL_CHECK Check for far NULL ptr assigns * + 5 MCF_STANDARD_STACK Standard stack frame * + 6 MCF_AUTOINIT Start up automatically * + 7 MCF_CLEAR_ON_FREE Clear buffers when freed + 8 MCF_DISK_ROCKET Use DiskRocket options + 9 MCF_IDX_IN_MEMORY Use memory only for Rocket indexes * + (only if DiskRocket linked) + 10 MCF_SOURCE_ONLY Intercept in source code only + + 11 - 31 Reserved + */ + MCFLAGS Flags; /* Main settings flags */ + + unsigned short MaxMem; /* Max mem for tree usage, in K */ + unsigned short NearNullBytes; /* bytes to check in near null */ + unsigned short FarNullBytes; /* " " " " far " */ + unsigned char CheckByteCt; /* check byte count */ + unsigned char AlignSize; /* alignment boundary size */ + char TrackingDir[36]; /* Rocket stores temp files here */ + + } MCSETTINGS, *MCSETTINGSP; + + +/* Random 32-bit .CFG file sentinel */ +#define MC_CFG_FILE_SENTINEL ( (unsigned long) 0x10F23BC4 ) + +typedef struct MCCfgInfo { + + unsigned long sentinel; /* always MC_CFG_FILE_SENTINEL */ + MCSETTINGS MemCheckSettings; /* saved by user */ + + } MCCFGINFO, *MCCFGINFOP; + + +#ifndef _CC32_ + +/* 16-bit exception stack */ +typedef struct { + + unsigned xRetIP; + unsigned xRetCS; + unsigned xErr; + unsigned xIP; + unsigned xCS; + unsigned xFlags; + unsigned xSP; + unsigned xSS; + + } MCEXCEPTINFO; + +#else + +/* 32-bit exception stack */ +typedef struct { + + unsigned long xRetEIP; + unsigned short xRsvd1; + unsigned short xRetCS; + unsigned long xErr; + unsigned long xEIP; + unsigned short xRsvd2; + unsigned short xCS; + unsigned long xFlags; + unsigned long xESP; + unsigned short xRsvd3; + unsigned short xSS; + + } MCEXCEPTINFO; + +#endif /* _CC32_ */ + +/* Values for MCCRITSECT.action */ +#define MCCS_ENTER_SECTION 0 +#define MCCS_LEAVE_SECTION 1 + +#define MCCS_ACTION(pMCCS) (pMCCS->nAction) +#define MCCS_ENTER(pMCCS) ((*(pMCCS->pLocked))++) /* inc flag */ +#define MCCS_LEAVE(pMCCS) ((*(pMCCS->pLocked))--) /* dec flag */ + +/* + Critical section object - ptr to this passed to crit sect callback + WARNING: access these fields ONLY via the MCCS_...() macros. + To do otherwise subjects you to changes in implementation + of the underlying coordination of critical section locking. +*/ +typedef struct { + int nAction; /* MCCS_ENTER/LEAVE_SECTION */ + int * pLocked; /* # times entered */ + unsigned long ulRsvd; /* internal use */ + } MCCRITSECT; + +#pragma pack() + + +#define MC_FEXIT ( (MCID) 0xFF ) + + +/* Error Reporting Function typedef */ +#ifndef _ERF_DEFINED +#define _ERF_DEFINED +typedef void (_MCCALLBACK *ERF) (char *); +#endif + + +/* *** Callback Functions *** */ + +/* Interception callback (on every interception) */ +typedef void (_MCCALLBACK * GLOBALF) (void); + +/* Called whenever nodes added to or deleted from MC database */ +typedef void (_MCCALLBACK *TRACKF) (int, MEMRECP); + +/* User-definable check function to add to transfer checking */ +typedef void (_MCCALLBACK * CHECKF) ( + int , /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); + +/* Funcs called at startup or shutdown */ +typedef void (_MCCALLBACK *STARTF) (void); +typedef void (_MCCALLBACK *ENDF) (void); + +/* Report function type passed to mc_report() */ +typedef void (_MCCALLBACK *REPORTF) (MEMRECP); + +/* Additional heap pointer verification (troubleshoot only) */ +typedef int (_MCCALLBACK *VERIFYF) (void _MCFAR *); + +typedef void (*MCVOIDFP) (void); + +/* Exception handler */ +typedef void (_MCCALLBACK _MCFAR *MCEXCEPTF) (void); + +/* Multitasking enter/exit critical section callback. + Specify as param to mc_set_critf() at beginning of program; + callback function will be called with MCCS_ENTER_SECTION + on entry to MemCheck, or MCCS_LEAVE_SECTION on exit. + + NOT TO BE CONFUSED WITH A MEMCHECK "GLOBAL" FUNCTION. + Global functions (GLOBALF's) are used to perform any + actions on the interception of a runtime function; + CRITF's must be used ONLY to serialize access to MemCheck. +*/ +typedef void (_MCCALLBACK * MCCRITF) (MCCRITSECT *); + + +/* Stack Frame Handler + + You can define your own function to + record, analyze, or inspect each stack frame + when mc_stack_trace() is called. + + You *cannot* modify ANY of the values passed + in, as the "const" typing indicates. If you need to + modify a value, make a copy. See the MemCheck 3.0 + documentation for more details on stack frame handlers. +*/ + +typedef void (_MCFAR _MCCDECL *_SSFRAMEHANDLER ) ( + short const , /* AX: near/far/error flag */ + unsigned short const , /* CX: near (default) rtn CS */ + unsigned short const , /* ES: far rtn CS */ + unsigned const , /* DI: rtn offset from stack */ + short const /* DX: frame count */ + ); + +/* Values for "flag" constant parameter to a + stack frame handler. +*/ +#define TRACE_BAD_FRAME 0x00 /* couldn't recognize frame */ +#define TRACE_FAR_CALL 0x01 /* frame represents a far call */ +#define TRACE_NEAR_CALL 0x02 /* " " " near " */ +#define TRACE_BAD_CHAIN 0x03 /* frame BP chewed up */ +#define TRACE_BEGIN 0x80 /* signals begin walk */ +#define TRACE_END 0x81 /* signals end of walk */ + + +/* MC Settings Structure, "flags" member: */ +#define MCF_ACTIVE (MCFLAGS)(0x01) +#define MCF_FAST_MODE (MCFLAGS)(0x02) +#define MCF_PROTECTED_MODE (MCFLAGS)(0x04) +#define MCF_FAR_NULL_CHECK (MCFLAGS)(0x08) +#define MCF_NEAR_NULL_CHECK (MCFLAGS)(0x10) +#define MCF_STANDARD_STACK (MCFLAGS)(0x20) +#define MCF_AUTOINIT (MCFLAGS)(0x40) +#define MCF_CLEAR_ON_FREE (MCFLAGS)(0x80) +#define MCF_DISK_ROCKET (MCFLAGS)(0x100) +#define MCF_IDX_IN_MEMORY (MCFLAGS)(0x200) +#define MCF_SOURCE_ONLY (MCFLAGS)(0x400) + + +/* *** Conditional Compilation *** */ + +/* -------------------------------------------------------------- + If MEMCHECK is not being `compiled out' (via definition + of the constant NOMEMCHECK), include this section... +-------------------------------------------------------------- */ + +#if !defined(MEMCHECK) + +/* Include Section for `MemCheck Not Active' */ + +/* ***************************** + MemCheck Not Active Section + ***************************** + + This section completely removes or + "evaporates" all MemCheck function references + from your projects when you compile with + NOMEMCHECK #defined. + + There's no need to remove any MemCheck + headers or statements from your code + to produce a full production version + of your application. + + o + ooo + ooooooo + ooooooooo + ooooooooooo + ooo + ooo + ooo + ooo + */ + +#ifndef MEMCHECK_MODULE + +/* Evaporate all MemCheck 3.0 API + statements to do nothing, safely... */ + +# define mc_alloc_count() 0L +# define mc_blocks_allocated() 0L +# define mc_blocks_freed() 0L +# define mc_breakpoint(fi) 0 +# define mc_bytes_allocated() 0L +# define mc_bytes_freed() 0L +# define mc_check(p) 0 +# define mc_check_buffers() 0 +# define mc_cur_file() "No file" +# define mc_cur_line() 0 +# define mc_debug(s) +# define mc_debugf(_args) +# define mc_debug_on() +# define mc_debug_off() +# define mc_endcheck() (MCEFLAGS)0 +# define mc_errno() MCE_NO_ERROR +# define mc_error_flags() (MCEFLAGS)0 +# define mc_error_text(e) "MemCheck not active" +# define mc_except_text(e) "MemCheck not active" +# define mc_file() "No file" +# define mc_find_buffer(p,mr) 0 +# define mc_func() (MCID)0 +# define mc_func_name(mcid) ("") +# define mc_get_erf() (ERF)NULL +# define mc_get_mode() 0 +# define mc_get_speed() 0 +# define mc_in_source() 0 +# define mc_is_active() 0 +# define mc_line() 0 +# define mc_load_debugger() +# define mc_location_text() "MemCheck not active" +# define mc_memory_leaked() 0L +# define mc_memrec() (MEMRECP)NULL +# define mc_memrec_text() "MemCheck not active" +# define mc_msg_continued() 0 +# define mc_nullcheck() 0 +# define mc_null_snapshot() +# define mc_register(p,s) +# define mc_report(_f) +# define mc_set_alignsize(_s) +# define mc_set_breakfile(_f) +# define mc_set_checkbytes(_cb) +# define mc_set_checkf(_f) (CHECKF)NULL +# define mc_set_critf(_f) +# define mc_set_endf(erf) (ENDF)NULL +# define mc_set_erf(erf) (ERF)NULL +# define mc_set_globalf(_f) (GLOBALF)NULL +# define mc_set_globalexitf(_f) (GLOBALF)NULL +# define mc_set_speed(_s) +# define mc_set_location() +# define mc_set_trackf(_f) (TRACKF)NULL +# define mc_set_tracefile(_f) +# define mc_set_tree_order(_q) +# define mc_set_track_dir(_dir) +# define mc_source_ptr() (MCPTR)NULL +# define mc_stack_trace(_memo) 0 +# define mc_startcheck(_erf) +# define mc_unregister(p) +# define mc_set_exceptf(f) +# define mc_get_exceptf() ((MCEXCEPTF)NULL) + +/* *** Stock error reporting functions *** */ +# define erf_default(_msg) +# define erf_standard(_msg) +# define erf_logfile(_msg) +# define erf_log_only(_msg) +# define erf_trace(_msg) + +/* Internal Helpers */ +# define _direct_output(_s) +# define _mcsl(_f,_l) +# define _mcsl_delete(_f,_l) +# define _mcsl_new(_f,_l) +# define _mcslx(_f,_l,_s) +# define _mc_set_delflag() +# define _mc_set_location(_f,_l) +# define _mc_set_newflag() + +/* Link-time compileouts */ +# define MC_SET_AUTOINIT(_toggle) +# define MC_SET_CHECK_FREQ(_freq) +# define MC_SET_CHECKF(_f) +# define MC_SET_CRITF(_f) +# define MC_SET_ENDF(_f) +# define MC_SET_ENV_VAR(_envvarname) +# define MC_SET_ERF(_f) +# define MC_SET_GLOBALF(_f) +# define MC_SET_GLOBALEXITF(_f) +# define MC_SET_LOGFILE(_logfilename) +# define MC_SET_PANIC_BUFFERS(_q) +# define MC_SET_SSFH(_f) +# define MC_SET_STARTF(_f) +# define MC_SET_TRACKF(_f) +# define MC_SET_VERIFYF(_f) + +/* Back-end direct */ +#define RTL(_f) _f + +/* *** C++ *** */ +#ifdef __cplusplus + +#define NEW(_object) new _object +#define DELETE(_object) delete _object +#define DELETE_ARR(_arr) delete[] _arr + +#define cpp_malloc(_s) malloc(_s) +#define cpp_calloc(_n,_s) calloc(_n,_s) +#define cpp_free(_p) free(_p) + +/* Borland C++ */ +#define cpp_farmalloc(_s) farmalloc(_s) +#define cpp_farfree(_fp) farfree(_fp) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) _fmalloc(_s) +#define cpp__ffree(_fp) _ffree(_fp) + +#endif /* C++ */ + +#endif /* not MEMCHECK_MODULE */ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#else /* MEMCHECK is defined */ + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#pragma message ("MemCheck V3.0") + +/* + ************************* + MemCheck Active Section + ************************* + + The rest of this header file deals with + MemCheck's being compiled into an application. + + */ + +/* Specify that vars and funcs are straight C.. */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* *** ANSI Location Defines *** */ + +#define _MC_NO_FILE ((MCSF) 0) /* just in case... */ +#define _MC_NO_LINE ((MCSL) 0) + + +/* Allow for the possibility of _MCSF_ being + defined to reference a single, static module + filename. This prevents a multiplicity of + static filenames getting added to the DGROUP, e.g. + + static char *_thisfile = __FILE__; + #define _MCSF_ ((MCSF)thisfile) + #include + + This is only needed under MSC pre-VC++. + Borland has "-d" Merge duplicate strings. + VC++ has "/Gf" Elim duplicate strings. +*/ +#if !defined (_MCSF_) +# ifdef __FILE__ +# define _MCSF_ (MCSF)__FILE__ +# else +# define _MCSF_ _MC_NO_FILE +# endif +#endif + +#ifdef __LINE__ +# define _MCSL_ (MCSL)__LINE__ +#else +# define _MCSL_ _MC_NO_LINE +#endif + + +/* *** Standard ANSI C Includes *** + + For va_list function call args + Inclusion of this header won't change + the behavior of host code. +*/ +#if !defined (va_start) /* avoid multiple inclusion... */ +# include +#endif + + +/* *** Compiler-specific includes *** */ + +/*lint -save -e537 Repeated include files (if necessary) */ +#if defined(_CC_MSC_COMPATIBLE_) + +# if !defined (_INC_MALLOC) /* C7.x and later optimization */ +# include +# endif + +# if !defined (_INC_STRING) /* C7.x and later optimization */ +# include +# endif + +#elif defined(_CC_BORLAND_) + +# if !defined (__ALLOC_H) +# include +# endif + +/* String functions must be proto'd before pragmas */ +# if !defined (__STRING_H) +# include +# endif + +#endif /* Compiler-specific includes */ +/*lint -restore */ + +#if defined (_CC_POWERPACK32_) +extern void cdecl mcinitfp_startcheck (void); +extern void cdecl mcexitfp_endcheck (void); +#pragma startup mcinitfp_startcheck 16 +#pragma exit mcexitfp_endcheck 16 +#endif + +/***************************************/ +/* *** MemCheck 3.0 API Prototypes *** */ +/***************************************/ + +/* Internal helper macro - proto shorthand */ +#define _LOCP MCSF,MCSL + +extern unsigned long _MCAPI mc_alloc_count (void); +extern unsigned long _MCAPI mc_blocks_allocated (void); +extern unsigned long _MCAPI mc_blocks_freed (void); +extern unsigned long _MCAPI mc_bytes_allocated (void); +extern unsigned long _MCAPI mc_bytes_freed (void); +extern int _MCAPI mc_check (void _MCFAR *); +extern int _MCAPI mc_check_buffers (void); +extern MCSF _MCAPI mc_cur_file (void); +extern MCSL _MCAPI mc_cur_line (void); +extern void _MCCDECL mc_debugv (const char *, ...); +extern void _MCAPI mc_debug (const char *); +extern MCEFLAGS _MCAPI mc_endcheck (void); +extern MCEFLAGS _MCAPI mc_error_flags (void); +extern char * _MCAPI mc_error_text (int); +extern int _MCAPI mc_errno (void); +extern char * _MCAPI mc_except_text (unsigned); +extern MCSF _MCAPI mc_file (void); +extern int _MCAPI mc_find_buffer(void _MCFAR *realptr,MEMRECP memrecp); +extern MCID _MCAPI mc_func (void); +extern char * _MCAPI mc_func_name(MCID); +extern ERF _MCAPI mc_get_erf (void); +extern MCEXCEPTF _MCAPI mc_get_exceptf (void); +extern int _MCAPI mc_get_mode (void); +extern int _MCAPI mc_get_speed (void); +extern char * _MCAPI mc_get_tracefile (void); +extern int _MCAPI mc_in_source (void); +extern int _MCAPI mc_is_active (void); +extern MCSL _MCAPI mc_line (void); +extern char * _MCAPI mc_location_text (void); +#define mc_load_debugger() _asm int 3 +extern unsigned long _MCAPI mc_memory_leaked (void); +extern char * _MCAPI mc_memrec_text (MEMRECP); +extern MEMRECP _MCAPI mc_memrec (void); +extern int _MCAPI mc_msg_continued (void); +extern int _MCAPI mc_nullcheck (void); +extern void _MCAPI mc_null_snapshot (void); +extern void _MCAPI mc_register (void _MCFAR *, unsigned long); +extern void _MCAPI mc_report (REPORTF); +extern void _MCAPI mc_set_alignsize (unsigned int); +extern void _MCAPI mc_set_breakfile (char *); +extern void _MCAPI mc_set_checkbytes (unsigned int); +extern CHECKF _MCAPI mc_set_checkf (CHECKF); +extern void _MCAPI mc_set_critf (MCCRITF); +extern ENDF _MCAPI mc_set_endf (ENDF); +extern ERF _MCAPI mc_set_erf (ERF); +extern MCEXCEPTF _MCAPI mc_set_exceptf (MCEXCEPTF); +extern GLOBALF _MCAPI mc_set_globalf (GLOBALF); +extern GLOBALF _MCAPI mc_set_globalexitf (GLOBALF); +#define mc_set_location() _mc_set_location(_MCSF_,_MCSL_) +extern MCPTR _MCAPI mc_source_ptr (void); +extern void _MCAPI mc_set_speed (int); +extern void _MCAPI mc_set_tracefile (char *); +extern void _MCAPI mc_set_track_dir (char *); +extern TRACKF _MCAPI mc_set_trackf (TRACKF); +extern void _MCAPI mc_set_tree_order (int); +extern int _MCAPI mc_stack_trace (char *); +extern void _MCAPI mc_startcheck (_LOCP, ERF); +extern void _ATEXITFUNC mc_endcheck_at_exit (void); +extern void _MCAPI mc_unregister (void _MCFAR *); + +/* Debugging versions of the MemCheck library only */ +extern void _MCAPI mc_debug_on (void); +extern void _MCAPI mc_debug_off (void); +extern int _MCCALLBACK mc_search_heap (void _MCFAR *); + +/* *** INTERNAL API HELPERS *** */ +extern void _MCAPI _mc_set_location (_LOCP); +extern void _FASTAPI _mcsl (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcslx (MCSF,MCSL,size_t); /* location run-ahead */ +extern void _FASTAPI _mcsl_new (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcsl_delete (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mc_set_newflag (void); /* new's a'comin' */ +extern void _FASTAPI _mc_set_delflag (void); /* delete's a'comin' */ + +/* Misc - uses INT 9 to output directly to screen */ +#if !defined (_CC_WATCOM32_) +extern void _MCCDECL _MCFAR _direct_output (char _MCFAR *); +#else +#define _direct_output(s) printf ("%s\n", s) /* ALERT */ +#endif + +/* + mc_breakpoint() is now a MemCheck Tracking function (TRACKF). + Tracking functions get called every time + MemCheck adds or deletes from its database. +*/ +#define mc_breakpoint(_f) \ + mc_set_trackf( (mc_set_breakfile (_f), trackf_breakpoint) ) +#define mc_breakpoint_trace(_f) \ + mc_set_trackf( (mc_set_tracefile (_f), trackf_breakpoint_trace) ) + + +/* *** Advanced-user API extenders *** */ + +/* extern int _MCAPI mc_find_buffer(void _MCFAR *, MEMRECP); */ +extern int _MCAPI mc_check_transfer( + void _MCFAR *, + void _MCFAR *, + unsigned long, + unsigned, + unsigned, + MEMRECP); + +/* mc_get_settings + + Write your own "get settings" routine + to override the one shipped with MemCheck. + You can hard-wire any settings you like, e.g. + always ON for versions of your app shipped to + testers/QA stations, etc. +*/ +extern void _MCCALLBACK mc_get_settings (MCSETTINGS *); + + +/* *** Callbacks / Functionality Extenders *** + + Function Type Called... + -------------- ------------------------------ + Error reporting To handle each MemCheck error message + Global Interception On each MemCheck interception + Checking On every data transfer check + Tracking On every allocation/deallocation + Start On mc_startcheck or AutoInit + End At mc_endcheck or MemCheck shutdown + + Refer to your MemCheck 3.0 manual for further details. + + *** STOCK FUNCTIONS *** + These functions are available in the MemCheck + libraries as "ready-made" for your programming + pleasure in the categories above. +*/ + +/* *** Stock error reporting functions *** */ + +extern void _MCCALLBACK erf_default (char *); +extern void _MCCALLBACK erf_standard (char *); +extern void _MCCALLBACK erf_logfile (char *); +extern void _MCCALLBACK erf_log_only (char *); +extern void _MCCALLBACK erf_trace (char *); +extern void _MCCALLBACK erf_trace_all (char *); +extern void _MCCALLBACK erf_trace_obj (char *); +extern void _MCCALLBACK erf_stdout (char *); +extern void _MCCALLBACK erf_stderr (char *); +extern void _MCCALLBACK erf_find_leaks (char *); + +#define erf_printf erf_stdout /* alias*/ + +/* *** Stock Tracking Functions *** */ + +extern void _MCCALLBACK trackf_default (int, MEMRECP); +extern void _MCCALLBACK trackf_all (int, MEMRECP); +extern void _MCCALLBACK trackf_all_2 (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint_trace (int, MEMRECP); +extern void _MCCALLBACK trackf_big_alloc (int, MEMRECP); + +/* *** Stock End Functions *** */ + +extern void _MCCALLBACK endf_default (void); /* does nothing */ +extern void _MCCALLBACK endf_info (void); /* write run info to log */ +extern void _MCCALLBACK endf_alert (void); /* warn if run errs */ +extern void _MCCALLBACK endf_summary (void); /* warn if run errs */ + +/* *** Stock Start functions *** */ + +extern void _MCCALLBACK startf_default (void); /* does nothing */ +extern void _MCCALLBACK startf_info (void); /* write options to log */ + +/* *** Stock Check Functions *** */ + +extern void _MCCALLBACK checkf_default (int,void _MCFAR *,long); +extern void _MCCALLBACK checkf_dataseg ( + int, /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); +extern void _MCCALLBACK checkf_verify_heap (int,void _MCFAR *,long); + +/* *** Stock Global Interception Functions *** */ + +extern void _MCCALLBACK globalf_default (void); /* does nothing */ +extern void _MCCALLBACK globalf_supercheck (void); +extern void _MCCALLBACK globalf_check_buffers (void); +extern void _MCCALLBACK globalf_heapcheck (void); + +/* *** Stock Report Functions *** */ +extern void _MCCALLBACK reportf_default (MEMRECP); + +/* *** Stock Exception Handlers *** */ +extern void _MCCALLBACK _MCFAR exceptf_default (void); + +/* *** Stock Stack Frame Handlers *** */ +extern void _MCFAR _MCCDECL ssfh_info ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_fast ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + /* int const _flag, */ + +extern void _MCFAR _MCCDECL ssfh_standard ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_debug ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +/* */ +extern unsigned int _MCAPI mc_stacktop (void); /* high address */ +extern unsigned int _MCAPI mc_stackend (void); /* low address */ + + +/* Function external variables. + + These are used effectively with MemCheck 3.0's AutoInit + setting. Under AutoInit, MemCheck fires itself up automatically + on its first interception. Under these circumstances, + there's no chance to have changed any defaults (like the + ERF or error reporting function). These variables provide + a link-level method of setting these functions: + + #include + : + // Sets custom erf at link-time + MC_SET_ERF (erf_custom_startup); + : +*/ +/* *** GLOBALS *** */ + +extern ERF _MCVAR MC_ERF; /* error reporting func ptr */ +extern CHECKF _MCVAR MC_CheckF; /* transfer check func */ +extern MCCRITF _MCVAR MC_CritF; /* crit section enter/exit */ +extern GLOBALF _MCVAR MC_GlobalF; /* global interception callback */ +extern GLOBALF _MCVAR MC_GlobalExitF; /* called on exit interception */ +extern TRACKF _MCVAR MC_TrackF; /* alloc/dealloc callback */ +extern STARTF _MCVAR MC_StartF; /* startup callback */ +extern ENDF _MCVAR MC_EndF; /* shutdown callback */ + +extern VERIFYF _MCVAR MC_VerifyF; /* troubleshooting */ + +extern char * _MCVAR MC_LogFile; /* log file name used */ +extern char _MCVAR MC_UserAutoInit; +extern int _MCVAR MC_CheckFreq; /* for globalf_supercheck() et al */ +extern char * _MCVAR MC_EnvVar; /* Env var to detect 'active' */ +extern unsigned short _MCVAR MC_DataSeg; /* DS value */ + +extern int _MCVAR MC_MaxTraceDepth; +extern char * _MCVAR MCST_Desc; /* trace descrip to mc_..trc() */ + +extern MCSETTINGS _MCVAR MC_DefaultSettings; /* default settings */ +extern MCSETTINGS _MCVAR MC_Settings; /* real settings-- + USE WITH CARE!!! */ + +extern MCVOIDFP _MCVAR MC_PMMap1; /* p-mode func in map seg 1 */ + +/* Protected mode exception handling */ +extern unsigned char _MCVAR MC_ExceptList[]; /* exceptions to handle */ +extern MCEXCEPTINFO _MCVAR MC_ExceptInfo; /* in exception */ +extern MCEXCEPTF _MCVAR MC_ExceptF; /* installed hdler */ + +/* Rocket Guidance Systems */ +extern ROCKETALLOCF _MCVAR MC_RocketAllocF; +extern ROCKETFREEF _MCVAR MC_RocketFreeF; +extern unsigned char _MCVAR MC_PanicBufCount; /* anti-tree failure */ + +/* This char is used to fill freed buffers + if the "ClearOnFree" option in effect. + Default buffer clear char is 0. +*/ +extern unsigned char _MCVAR MC_FreedBufferFillChar; + +/* Link-time defaults. + + These macros are "covers" to insulate you, the developer, + from the underlying implementation, as well as to provide + such bennies as compiling clean out of your code when + NOMEMCHECK or NOMC is defined. + + Use instead of accessing vars directly! + + To use, place the following in ONE MODULE e.g. your main module + (any *one* module will work fine) after the MemCheck + header file has been included: + + #include + MC_SET_...(params); + + For example, to change the default log file that MemCheck + uses at runtime to C:\MYDEV\MYPROG.LOG: + + #include + MC_SET_LOGFILE ("C:\\MYDEV\\MPROG.LOG"); + + Most of these macros have runtime function equivalents, + such as mc_set_erf() for MC_SET_ERF(), etc. Notable + exceptions to this are the following values that + must generally have link-time initializations: + + MC_SET_LOGFILE() + MC_SET_AUTOINIT() + MC_SET_STARTF() +*/ +#define MC_SET_AUTOINIT(_toggle) \ + char _MCVAR MC_UserAutoInit = (_toggle); +#define MC_SET_CHECKF(_f) \ + CHECKF _MCVAR MC_CheckF = (_f) +#define MC_SET_CHECK_FREQ(_freq) \ + int _MCVAR MC_CheckFreq = (_freq) +#define MC_SET_CRITF(_f) \ + MCCRITF _MCVAR MC_CritF = (_f) +#define MC_SET_ENDF(_f) \ + ENDF _MCVAR MC_EndF = (_f) +#define MC_SET_ENV_VAR(_envvarname) \ + char * _MCVAR MC_EnvVar = (_envvarname) +#define MC_SET_ERF(_f) \ + ERF _MCVAR MC_ERF = (_f) +#define MC_SET_EXCEPTF(_f) \ + MCEXCEPTF _MCVAR MC_ExceptF = (_f) +#define MC_SET_GLOBALF(_f) \ + GLOBALF _MCVAR MC_GlobalF = (_f) +#define MC_SET_GLOBALEXITF(_f) \ + GLOBALF _MCVAR MC_GlobalExitF = (_f) +#define MC_SET_LOGFILE(_f) \ + char * _MCVAR MC_LogFile = (_f) +#define MC_SET_PANIC_BUFFERS(_q) \ + unsigned char _MCVAR MC_PanicBufCount = (_q) +#define MC_SET_SSFH(_f) \ + _SSFRAMEHANDLER _MCVAR near MC_SFrameHandler = (_f) +#define MC_SET_STARTF(_f) \ + STARTF _MCVAR MC_StartF = (_f) +#define MC_SET_TRACKF(_f) \ + TRACKF _MCVAR MC_TrackF = (_f) +#define MC_SET_VERIFYF(_f) \ + VERIFYF _MCVAR MC_VerifyF = (_f) + +/* Use the MC_BEGIN_EXCEPTLIST, MC_HANDLE_EXCEPTION, + and MC_END_EXCEPTLIST macros to change the exceptions + MemCheck handles in protected mode by default. + + Usage (exactly as typed): + #include + : + MC_BEGIN_EXCEPTLIST + MC_HANDLE_EXCEPTION (0x0) + MC_HANDLE_EXCEPTION (0xD) + MC_END_EXCEPTLIST + + NOTE: + To turn off MemCheck's exception handling completely, use + + MC_SET_EXCEPTF(NULL); + + instead of trying to define an empty EXCEPTLIST... +*/ +#define MC_BEGIN_EXCEPTLIST \ + unsigned char _MCVAR MC_ExceptList[] = { +#define MC_HANDLE_EXCEPTION(e) \ + (unsigned char)(e), +#define MC_END_EXCEPTLIST \ + (unsigned char)0xFF }; /* 0xFF MUST end list */ + +/* ------------- End MemCheck 3.0 Library Calls --------------- */ + +/* Formulaic rogue varargs interceptions; + most host-code-compatible method... + "Are you experienced?" + + "It is better to be mugged than + to live in fear." - Anon. +*/ +#define _VA_DEF(f,r,p) \ + typedef r (_RTL *p_##f) p; \ + extern p_##f _MCAPI _loc_##f (_LOCP); + +/* Declare sprintf helper function */ +_VA_DEF(sprintf,int,(char *, const char *, ...)) + + /* * * * * * * * * * * * * * * * * * * * * * * + ************************* + Back-End RTL + ************************* +*/ + +/* *** Back-end functions *** */ + +/* Macro to access true back-end RTL. + Used internally by the MemCheck API functions. +*/ +#define __paste(x,y) x ## y +#define RTL(func) __paste(func,_mc) + +/* Macro to encapsulate the declaration of + the renamed (zapped) back-end rtl +*/ +#define _RTLDECL(f,rctype,params) \ + extern rctype _RTL RTL(f) params + + +/* For the conversion that MSC underwent + from C 6 to 7, where non-ANSI calls + have underbars +*/ +#if defined (_CC_MSC_) && !defined (MSC6) +#if (_MSC_VER >= 700) +# define _C7A +#endif +#endif + +#ifdef _C7A +#define C7ANSI(func) _##func +#else +#define C7ANSI(func) func +#endif + +#undef _C7A + + +/* ---------------------------------------------- */ +/* These are the renamed ("zapped") RTL functions */ +/* ---------------------------------------------- */ + +/* *** ANSI *** */ + +_RTLDECL(malloc, void *, (size_t)); +_RTLDECL(calloc, void *, (size_t, size_t)); +_RTLDECL(realloc, void *, (void *, size_t)); +_RTLDECL(free, void, (void *)); +_RTLDECL(memcpy, void *, (void *,const void *,size_t)); +_RTLDECL(memmove, void *, (void *,const void *,size_t)); +_RTLDECL(memset, void *, (void *,int,size_t)); +_RTLDECL(strcpy, char *, (char *,const char *)); +_RTLDECL(strncpy, char *, (char *,const char *,size_t)); +_RTLDECL(strcat, char *, (char *,const char *)); +_RTLDECL(strncat, char *, (char *,const char *,size_t)); +_RTLDECL(vsprintf, int, (char *,const char *,va_list)); +_RTLDECL(sprintf, int, (char *,const char *,...)); + +#if !defined (_CC_ANSI_) +/* *** MSC *** */ + +/* WATCOM doesn't support these... */ +#if !defined(_CC32_) +_RTLDECL(_fmalloc, void far *, (size_t)); +_RTLDECL(_fcalloc, void far *, (size_t, size_t)); +_RTLDECL(_ffree, void, (void far *)); +_RTLDECL(_fmsize, size_t, (void far *)); +#endif + +_RTLDECL(_nmalloc, void _MCNEAR *,(size_t)); +_RTLDECL(_nfree, void, (void _MCNEAR *)); + +/* *** Borland *** */ + +#if !defined(_CC_POWERPACK32_) +_RTLDECL(farmalloc, void _MCFAR *, (unsigned long)); +_RTLDECL(farcalloc, void _MCFAR *, (unsigned long, unsigned long)); +_RTLDECL(farfree, void, (void _MCFAR *)); + +/* *** General Porpoise *** */ + +_RTLDECL(_fmemset, void far * _MCFARGLUE, (void far *,int,size_t)); +_RTLDECL(_fmemcpy, void far * _MCFARGLUE, (void far *,const void far *,size_t )); +_RTLDECL(_fstrcpy, char far * _MCFARGLUE, (char far *,const void far *)); +#endif /* not _CC_POWERPACK32_ */ + +#endif /* not STDC/ANSI */ + +/***************************************************************** + * -------- Function Call Interception Definitions --------- * + *****************************************************************/ + +#ifndef MEMCHECK_MODULE + +/* + This section targets user's code only +*/ + +/* Func interceptors... */ +#define _INTERCEPT(_f) (_mcsl(_MCSF_,_MCSL_),_f) +#define _VA_INTERCEPT(_f) (*_loc_##_f(_MCSF_,_MCSL_)) +#define _SETLOC(_f) (mc_set_location(),_f) + +/* NOTE near _mcsl with #if (_MCC_NEAR_INTERCEPT == 0) */ + +/* + MC_NO_TRANSFER_SIZE is used to eliminate errors or warnings + like "sizeof returns 0" or "Not allowed type in sizeof ". + These occur for unsized variables declared like + + extern unsigned char gHelpString[]; + + The optimal solution is to "size" the extern, e.g. + + extern unsigned char gHelpString[80]; + + but where this may not be practical, MC_NO_TRANSFER_SIZE may + be defined on a module-by-module OR project-wide basis. +*/ +#ifdef MC_NO_XFER_SIZE /* beta compat */ +# define MC_NO_TRANSFER_SIZE +#endif +#ifdef NO_TRANSFER_SIZE /* alternate */ +# define MC_NO_TRANSFER_SIZE +#endif + +#if defined (MC_NO_TRANSFER_SIZE) +# define _INTERCEPTX(_f,_d) _INTERCEPT(_f) +#else /* standard; transmit sizeof dest */ +# define _INTERCEPTX(_f,_d) (_mcslx(_MCSF_,_MCSL_,sizeof(_d)),_f) +#endif + + +/* Intrinsic Function Disabling + + It's important to disable function inlining for + all intercepted functions. +*/ + +#if defined(_CC_MSC_) + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros... +*/ +#pragma function(strcat) +#pragma function(strcpy) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(strset) + +#if defined(_MSC_VER) +#if (_MSC_VER >= 700) +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) +#pragma function(_fstrset) +#pragma function(_strset) +#endif +#endif /* defined _MSC_VER */ + +#elif defined(_CC_BORLAND_) + +/* Turbo C not like pragmae */ +#if !defined (_CC_TCC_) + +/* Eliminate duplicate strings. + This can save a bit of space in large + programs particularly, since each call to + MemCheck references an otherwise separate + copy of the current filename. +*/ +#pragma option -d + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros, for one... +*/ +#pragma intrinsic -strcat +#pragma intrinsic -strncat +#pragma intrinsic -strcpy +#pragma intrinsic -strncpy +#pragma intrinsic -stpcpy +#pragma intrinsic -strset +#pragma intrinsic -strnset +#pragma intrinsic -memcpy +#pragma intrinsic -memset + +#endif /* not Turbo C */ + +/* end Borland compiler intrinsics */ + +#elif defined (_CC_WATCOM_) + +/* NOTE: unfortunately, WATCOM C/C++ compilers + force inlining of the strcpy() function regardless + of whether you want it inlined or not, all the time. + So this pragma, while it should ensure that + strcpy() is a function call, does not... :{ + + So we take other measures below: see _mcwatcom_strcpy() +*/ +#pragma function(strcpy) + +#pragma function(strcat) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) + +#endif + +/* End disable function inlining */ + + +/*lint -save -e652 Define of symbol declared previously */ +#if defined (MC_NO_INTERCEPT) +#define NO_INTERCEPT +#endif + +#if !defined (NO_INTERCEPT) + +/* *** ANSI Standard C *** */ + +#define calloc(n,_sz) _INTERCEPT(calloc(n,_sz)) +#define malloc(_sz) _INTERCEPT(malloc(_sz)) +#define realloc(p,s) _INTERCEPT(realloc(p,s)) +#define free(p) _INTERCEPT(free(p)) + +#define memcpy(d,s,n) _INTERCEPTX(memcpy(d,s,n),d) +#define memmove(d,s,n) _INTERCEPTX(memmove(d,s,n),d) +#define memset(p,c,n) _INTERCEPTX(memset(p,c,n),p) +#define strcat(s1,s2) _INTERCEPTX(strcat(s1,s2),s1) +#if defined(_CC_WATCOM_) + /* WATCOM forces inlining of strcpy()... see note above */ +# define strcpy(d,s) _INTERCEPTX(_mcwatcom_strcpy(d,s),d) + extern char * _RTL _mcwatcom_strcpy (char *, const char *); +#else +# define strcpy(d,s) _INTERCEPTX(strcpy(d,s),d) +#endif +#define strncat(s1,s2,n) _INTERCEPTX(strncat(s1,s2,n),s1) +#define strncpy(d,s,n) _INTERCEPTX(strncpy(d,s,n),d) +#define vsprintf(s,f,a) _INTERCEPTX(vsprintf(s,f,a),s) + +/* #define sprintf _VA_INTERCEPT(sprintf) */ +#ifndef _lint +#define sprintf _INTERCEPT(sprintf) +#endif + +#if defined(_CC_MSC_COMPATIBLE_) /* *** Microsoft C *** */ + +#define _expand(_p,_s) _INTERCEPT(_expand(_p,_s)) +#define _fcalloc(n,_sz) _INTERCEPT(_fcalloc(n,_sz)) +#define _fexpand(_p,_s) _INTERCEPT(_fexpand(_p,_s)) +#define _ffree(p) _INTERCEPT(_ffree(p)) +#define _fmalloc(_sz) _INTERCEPT(_fmalloc(_sz)) +#define _frealloc(p,s) _INTERCEPT(_frealloc(p,s)) +#define _fmsize(p) _INTERCEPT(_fmsize(p)) +#define _msize(p) _INTERCEPT(_msize(p)) +#define _nfree(p) _INTERCEPT(_nfree(p)) +#define _nmalloc(_sz) _INTERCEPT(_nmalloc(_sz)) +#define _nrealloc(p,s) _INTERCEPT(_nrealloc(p,s)) +#define _ncalloc(n,_sz) _INTERCEPT(_ncalloc(n,_sz)) +#define _nexpand(_p,_s) _INTERCEPT(_nexpand(_p,_s)) +#define _nmsize(p) _INTERCEPT(_nmsize(p)) +#define _nstrdup(s) _INTERCEPT(_nstrdup(s)) +/* #define halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define _halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define hfree(p) _INTERCEPT(hfree(p)) */ +/* #define _hfree(p) _INTERCEPT(hfree(p)) */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define _cgets(s) _INTERCEPTX(_cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#define _memccpy(d,s,c,n) _INTERCEPTX(_memccpy(d,s,c,n),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define _strdup(s) _INTERCEPT(_strdup(s)) +#define _strnset(s,c,n) _INTERCEPTX(_strnset(s,c,n),s) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define _strset(s,c) _INTERCEPTX(_strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define _swab(s,d,n) _INTERCEPTX(_swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) +#define _tempnam(d,pfx) _INTERCEPT(_tempnam(d,pfx)) + +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) +#define _fullpath(b,p,n) _INTERCEPTX(_fullpath(b,p,n),b) + +/* ----- END Microsoft C/C++ interceptions ----- */ + +#elif defined (_CC_BORLAND_) /* *** Borland C/C++ *** */ + +#ifndef _CC_POWERPACK32_ +#define farfree(p) _INTERCEPT(farfree(p)) +#define farmalloc(s) _INTERCEPT(farmalloc(s)) +#define farcalloc(n,s) _INTERCEPT(farcalloc(n,s)) +#define farrealloc(p,s) _INTERCEPT(farrealloc(p,s)) +#endif /* not _CC_POWERPACK32_ */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#if !defined(movmem) +#define movmem(s,d,l) _INTERCEPTX(movmem(s,d,l),d) +#endif +#if !defined(setmem) +#define setmem(d,c,v) _INTERCEPTX(setmem(d,c,v),d) +#endif +#define setvect(i,v) _INTERCEPT(setvect(i,v)) +#define stpcpy(d,s) _INTERCEPTX(stpcpy(d,s),d) +#define _stpcpy(d,s) _INTERCEPTX(_stpcpy(d,s),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) + +#ifndef _CC_POWERPACK32_ +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fmovmem(s,d,l) _INTERCEPTX(_fmovmem(s,d,l),s) +#define _fsetmem(d,c,v) _INTERCEPTX(_fsetmem(d,c,v),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) +#endif /* not _CC_POWERPACK32_ */ + +/* +#define freemem(g) _INTERCEPT(freemem(g)) +#define vsscanf(d,f,a) _INTERCEPTX(vsscanf(d,f,a),d) +*/ + +/* ----- END Borland C/C++ interceptions ----- */ + +#else + +#error Unknown compiler in MemCheck.h + +#endif /* Compiler-specific Function Mapping Section */ + +/* Location Transmitters + + You can add any non-intercepted functions to + this bunch... Just updates MemCheck's file and line + information via mc_set_location(), which is thousands + of times faster than anything that does I/O. + The only time this section could be a problem is + if the header file is included before any other header + files which prototype these routines. + + Borland's TD (Turbo Debugger) also has problems here (see note). +*/ +#ifndef _lint /* LINT not like */ + +/* Borland's Turbo Debugger gets confoosed and executes + a `Run' instead of a `Step' when _SETLOC macro is used... +*/ +#if !defined (_CC_BORLAND_) +#if 1 /* Change this to '0' to omit this section */ + +#define printf _SETLOC(printf) + +#define fopen _SETLOC(fopen) +#define fprintf _SETLOC(fprintf) +#define fread _SETLOC(fread) +#define fwrite _SETLOC(fwrite) +#define fclose _SETLOC(fclose) + +#define system _SETLOC(system) +#define exec _SETLOC(exec) +#define spawnl _SETLOC(spawnl) +#define spawnlp _SETLOC(spawnlp) +#define spawnle _SETLOC(spawnle) +#define spawnlpe _SETLOC(spawnlpe) +#define spawnv _SETLOC(spawnv) +#define spawnvp _SETLOC(spawnvp) +#define spawnve _SETLOC(spawnve) +#define spawnvpe _SETLOC(spawnvpe) + +#endif /* end location transmission section */ +#endif /* not Borland C++ */ +#endif /* not def _lint */ + + +/* **** THIRD-PARTY MAPPINGS **** */ + +/* Vermont Views V3.xx + + The following code will transmit the exact file + and line of any mem_get() and mem_free() calls to + MemCheck, so that it can report on the location where + these functions are called, instead of the location of + the calloc() or free(). + + If you've used MCCONFIG to configure the Vermont Views source + code, you *must* either NOT include the MemCheck header file + in the MEM_GET.C and MEM_FREE.C modules, or, if you do, then + #define NO_INTERCEPT beforehand, e.g. + + Module MEM_GET.C ... + : + #define NO_INTERCEPT + #include + : + + MCCONFIG may be used to configure even the shrouded + Vermont Views source code. + + See also: TechNote "Using MemCheck 3.0 Professional + With Vermont Views", available on the StratosWare + BBS (313) 996-2993 as VIEWS.TXT, or by fax. +*/ +#if defined (VV_SYS) /* should do the trick */ +# define mem_get(s) _INTERCEPT(mem_get(s)) +# define mem_free(p) _INTERCEPT(mem_free(p)) +#endif + + +/* **** APPLICATION-SPECIFIC MAPPINGS **** */ + +/* + If your application uses allocation "cover" routines, + MemCheck will by default report errors and leaks by + the file and line of the malloc or free within the + cover module. To get MemCheck to report by file and + line where the cover function is actually called, follow + the instructtions in MemCheck TechNote "Transmitting File + and Line to MemCheck 3.0 Professional Through Cover Functions." + + This is where you can place the cover function macros. +*/ + + +/* end APPLICATION-SPECIFIC MAPPINGS */ + +#endif /* not NO_INTERCEPT */ + +/* Calls that xmit source file, line number if called in source */ +/* *** MemCheck API file & line transmittal *** */ +#define mc_startcheck(erf) mc_startcheck(_MCSF_,_MCSL_,erf) +#define mc_stack_trace(_memo) _INTERCEPT(mc_stack_trace(_memo)) +#define mc_debug(s) _INTERCEPT(mc_debug(s)) +#define mc_debugf(arglist) mc_debugv arglist +#define mc_debugv _mcsl(_MCSF_,_MCSL_),mc_debugv +#define mc_endcheck() _INTERCEPT(mc_endcheck()) +#define mc_check_buffers() _INTERCEPT(mc_check_buffers()) +#define mc_check(p) _INTERCEPT(mc_check(p)) +#define mc_register(p,s) _INTERCEPT(mc_register(p,s)) +#define mc_unregister(p) _INTERCEPT(mc_unregister(p)) +#define mc_nullcheck() _INTERCEPT(mc_nullcheck()) +#define mc_report(f) _INTERCEPT(mc_report(f)) + +/*lint -restore 652 Define of symbol declared prev */ + +#endif /* not MEMCHECK_MODULE, function interceptions */ + + +/* End "C" call wrapper */ +#ifdef __cplusplus +} + + +/* C++ MemCheck Class + + This class can be used as an alternative to + AutoInit, or to placing the mc_startcheck() and + mc_endcheck() calls in your main() program. + Just declaring an object of class 'MemCheck' + will start MemCheck up; usually you will place + this 'above' any other global or statically declared + C++ objects in your main module. + + Here are some examples of starting MemCheck up + via object mechanics: + + MemCheck On; + MemCheck Active; + MemCheck Rules; + + Use your imagination! Note that if AutoInit is ON, + any calls to mc_startcheck() and mc_endcheck() are + ignored. +*/ +#if !defined (NO_INTERCEPT) /* must not have this def'd */ + +/* This class def causes a warning under MSC if not used */ +#if !defined (_CC_MSC_) + +class MemCheck { +public: + MemCheck () { mc_startcheck (NULL); } + ~MemCheck () { mc_endcheck (); } +}; + +#endif + +#endif /* NO_INTERCEPT */ + + +/* *** For use in new and delete modules only *** */ +/* + Replace 'mallocs' with 'cpp_mallocs', etc. + In new and delete modules, NO_INTERCEPT should be #defined, e.g. + + #define NO_INTERCEPT + #include + : + void * operator new ( size_t size ) + { + if (!size) size = 1; + return (cpp_malloc (size)); + } + etc. +*/ +#define cpp_malloc(_s) (_mc_set_newflag(), malloc(_s)) +#define cpp_calloc(_n,_s) (_mc_set_newflag(), calloc(_n,_s)) +#define cpp_free(_p) (_mc_set_delflag(), free(_p)) + +/* Borland C++ */ +#define cpp_farmalloc(_s) (_mc_set_newflag(), farmalloc(_s)) +#define cpp_farfree(_fp) (_mc_set_delflag(), farfree(_fp)) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) (_mc_set_newflag(), _fmalloc(_s)) +#define cpp__ffree(_fp) (_mc_set_delflag(), _ffree(_fp)) + + +/* C++ */ +#if !defined (NO_INTERCEPT) +#if !defined (NO_CPP) + +/* + This method is off by default, because it + requires definition of a new operator like: + + void * new (size_t size, char *file, int lineno); + + Such a new operator is included in your SOURCE\CPP + directory. To have this method used for all modules, + #define NEW_OVERLOADED at the top of this header file + or in your project #include file, BEFORE the MemCheck + header file is #included. + + The substitutions for the new operator + may not work in all situations. To disable + MemCheck's interception of new on a module-by- + module basis by undefining NEW_OVERLOADED. +*/ +#if defined (NEW_OVERLOADED) + +/* Method 1: Placement Operators + + Use placement operators to trap file and line location transparently + on calls to new. + + Thanks for this tip to Dan Saks, + C and C++ writer, author, teacher, and columnist--buy his books. + He came through when no one else had a clue! + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with an overloaded new placement operator. +*/ + +/* Declare overloaded new with placement operators */ +void *operator new (size_t _sz, char *file, int lineno); +#if defined (_CPP_ANSI20_) +/* Array version; only under supporting compilers + COMMENT LINE OUT if it causes a compile error. +*/ +void *operator new[] (size_t _sz, char *file, int lineno); +#endif + +#if !defined (_CC_MSC_) +#define new new((char *)__FILE__,(int)__LINE__) +#else +#define new new(__FILE__,__LINE__) +#endif + +/* NOTE: + This placement operator interception syntax has been + known to cause syntax errors (in VC++ 1.0) for no apparent reason + on statements like + + Domain *d = new Domain (); + + Workaround is to change line (sorry!) to equivalent + + Domain *d = new Domain; +*/ + +/* Backwards compatibility with the V2.1 C++ macros */ +#ifndef NEW +#define NEW(_object) new _object +#endif +#ifdef DELETE +#define DELETE(_object) delete _object +#endif +#define DELETE_ARR(_arr) delete[] _arr + +#else /* !NEW_OVERLOADED - end of Placement Operator intercept */ + +/* New and Delete Interception, Method 2: NEW() and DELETE() + + The NEW() and DELETE() macros may be used to transmit file + and line of new and delete. These macros, which require + modification of source code, i.e. "NEW(object)" for "new object", + should probably be used only if the above overloaded new does + not work for your code base. + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with NEW() and DELETE(). + + If calling, please have your MemCheck serial number handy. +*/ +#ifndef NEW +#define NEW(_object) (_mcsl_new(_MCSF_,_MCSL_), new _object) +#endif +#ifndef DELETE /* WINNT.H under BC DPMI32 defines DELETE */ +#define DELETE(_object) (_mcsl_delete(_MCSF_,_MCSL_), delete _object) +#endif +#define DELETE_ARR(_arr) (_mcsl_delete(_MCSF_,_MCSL_), delete[] _arr) + +#endif /* !NEW_OVERLOADED */ + +#define delete _mcsl_delete(_MCSF_,_MCSL_), delete + + +/* *** FAILURES *** */ + +/* These macros failed in the purpose of + intercepting new transparently in some + situation or other. +*/ + +/* Failed on " * new expr " (TV) */ +/* #define new (mc_set_location(),0) ? NULL : new */ + +/* Failed on " x = new Object " (TV) */ +/* #define new ((mc_set_location(),0) ? NULL : new) */ +/* #define new new (mc_set_location(),0) ? NULL : */ + +#endif /* !NO_CPP */ +#endif /* NO_INTERCEPT */ +#endif /* cplusplus */ +/******** End C++ ************/ + + +#endif /* End of Section for MEMCHECK Defined */ + +/* -------------------------------------------------------------------------- */ + +#endif /* _MEMCHECK_H_ */ + + +/******************************** + * End of MemCheck 3.0 Header * + ********************************/ + diff --git a/CODE/MENUS.CPP b/CODE/MENUS.CPP new file mode 100644 index 0000000..0f20fca --- /dev/null +++ b/CODE/MENUS.CPP @@ -0,0 +1,1012 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MENUS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MENUS.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : Oct. 24, 1996 Victor Grippi * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Main_Menu -- Menu processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#else //WIN32 +#include +#endif + +/***************************** +** Function prototypes +******************************/ + +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2); +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index); +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc); + +int UnknownKey; + +PRIVATE int MenuUpdate=1; +PRIVATE int MenuSkip; + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +/*=========================================================================*/ +/* SELECT_TO_ENTRY: */ +/* */ +/* This routine converts a selection to the correct string entry. It */ +/* does this by search through a long bitfield starting at position index */ +/* until it finds the correct conversion to entries. */ +/* */ +/* INPUTS: int selection from menu, long the bit field to search, int */ +/* the starting index within the bit field. */ +/* RETURNS: int the index into the table of entries */ +/*=========================================================================*/ +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index) +{ + int placement; + + if (bitfield==0xFFFFFFFFL) /* if all bits are set */ + return(select); /* then it as is */ + + placement=0; /* current pos zero */ + while (select) { /* while still ones */ + if (bitfield & (1L<<(placement+index))) /* if this flagged then */ + select--; /* decrement counter */ + placement++; /* and we moved a place */ + } + while (!(bitfield & (1L<<(placement+index)))) { + placement++; + } + + return(placement); /* return the position */ +} + + +/*=========================================================================*/ +/* FLASH_LINE: */ +/* */ +/* This routine will flash the line at the desired location for the */ +/* menu routine. It is way cool awesome! */ +/* */ +/* INPUTS: char *text, int x position on line, int y position, char */ +/* normal foreground color, char hilight foreground color, char */ +/* background color */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc) +{ + int loop; + + for (loop=0;loop<3;loop++) { + Hide_Mouse(); + Plain_Text_Print(text, xpix, ypix, hfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Delay(2); + Plain_Text_Print(text, xpix, ypix, nfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); + Delay(2); + } +} + +/*=========================================================================*/ +/* COORDINATES_IN_REGION: */ +/* */ +/* Test to see if a given pair of coordinates are within the given */ +/* rectangular region. */ +/* */ +/* INPUTS: int x to be tested, int y to be tested, int left x pos, */ +/* int top y pos, int right x pos, int bottom y pos */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2) +{ + return((x>=inx1)&&(x<=inx2)&&(y>=iny1)&&(y<=iny2)); +} + +#ifdef NEVER +/*=========================================================================*/ +/* FIND_MENU_ITEMS: */ +/* */ +/* This routine finds the real total items in a menu when certain items */ +/* may be disabled by bit fields and the like. This is done by looping */ +/* through the fields, starting at the position passed in index and */ +/* counting the number of bits that are set. */ +/* */ +/* INPUTS: int the maximum number of items possible on the menu, long */ +/* the bit field of enabled and disabled items, char the index */ +/* point to start at within the list. */ +/* RETURNS: int the total number of items in the menu */ +/*=========================================================================*/ + int Find_Menu_Items(int maxitems, unsigned long field, char index) + { + int loop,ctr; + + if (field==0xFFFFFFFFL) /* if all bits are set */ + return(maxitems); /* then maxitems set */ + + for (loop=ctr=0;loopClear(); +} + + +/*=========================================================================*/ +/* CHECK_MENU: */ +/* */ +/* */ +/* */ +/* INPUTS: */ +/* RETURNS: */ +/*=========================================================================*/ +int Check_Menu(int menu, char const * text[], char *, long field, int index) +{ + int maxitem,select,key,menuy,menux; + int mx1,mx2,my1,my2,tempy; + int drawy,menuskip,halfskip; + int normcol,litcol,item,newitem,idx; + int * menuptr; + + //selection++; /* get rid of warning */ + + menuptr = &MenuList[menu][0]; /* get pointer to menu */ + maxitem = menuptr[ITEMSHIGH]-1; /* find max items */ + newitem = item = menuptr[MSELECTED]%(maxitem+1); /* find selected */ + select = -1; /* no selection made */ + menuskip = FontHeight+MenuSkip; /* calc new font height */ + halfskip = MenuSkip>>1; /* adjustment for menus */ + + menuy = WinY+menuptr[MENUY]; /* get the absolute */ + menux = (WinX+menuptr[MENUX]); /* coords of menu */ + normcol = menuptr[NORMCOL]; + litcol = menuptr[HILITE]; + + /* + ** Fetch a pending keystroke from the buffer if there is a keystroke + ** present. If no keystroke is pending then simple mouse tracking will + ** be done. + */ + key = 0; + UnknownKey = 0; + if (Keyboard->Check()) { +#ifdef WIN32 + key = (Keyboard->Get() & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT) ); /* mask off all but release bit */ +#else + key = (Keyboard->Get()&0x08FF); /* mask off all but release bit */ +#endif + } + + /* + ** if we are using the mouse and it is installed, then find the mouse + ** coordinates of the menu and if we are not somewhere on the menu get + ** the heck outta here. If we are somewhere on the menu, then figure + ** out the new selected item, and continue forward. + */ + mx1=(WinX)+(menuptr[MENUX]*FontWidth); /* get menu coords */ + my1=(WinY)+(menuptr[MENUY])-halfskip; /* from the menu */ + mx2=mx1+(menuptr[ITEMWIDTH]*FontWidth)-1; /* structure as */ + my2=my1+(menuptr[ITEMSHIGH]*menuskip)-1; /* necessary */ + + tempy=Get_Mouse_Y(); + if (Coordinates_In_Region(Get_Mouse_X(), tempy, mx1, my1, mx2, my2)&& MenuUpdate) { + newitem=(tempy-my1)/menuskip; + } + + switch (key) { + + case KN_UP: /* if the key moves up */ + newitem--; /* new item up one */ + if (newitem<0) /* if invalid new item */ + newitem=maxitem; /* put at list bottom */ + break; + case KN_DOWN: /* if key moves down */ + newitem++; /* new item down one */ + if (newitem>maxitem) /* if new item past */ + newitem=0; /* list end, clear */ + break; + case KN_HOME: /* if top of list key */ + case KN_PGUP: /* is selected then */ + newitem=0; /* new item = top */ + break; + case KN_END: /* if bottom of list is */ + case KN_PGDN: /* selected then */ + newitem=maxitem; /* new item = bottom */ + break; + + /* + ** Handle mouse button press. Set selection and then fall into the + ** normal menu item select logic. + */ + case KN_RMOUSE: + case KN_LMOUSE: + if (Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, mx1, my1, mx2, my2)) { + newitem = (Keyboard->MouseQY - my1) / menuskip; + } else { + UnknownKey = key; // Pass the unprocessed button click back. + break; + } + + /* + ** Normal menu item select logic. Will flash line and exit with menu + ** selection number. + */ + case KN_RETURN: /* if a selection is */ + case KN_SPACE: /* made with key */ + case KN_CENTER: + select=newitem; /* flag it made. */ + break; + + case 0: + break; + + /* + ** When no key was pressed or an unknown key was pressed, set the + ** global record of the key and exit normally. + ** EXCEPTION: If the key matches the first letter of any of the + ** menu entries, then presume it as a selection of + ** that entry. + */ + default: + for (idx = 0; idx < menuptr[ITEMSHIGH]; idx++) { + if (toupper(*(text[Select_To_Entry(idx, field, index)])) == toupper(Keyboard->To_ASCII((KeyNumType)(key&0x0FF)))) { + newitem = select = idx; + break; + } + } + UnknownKey = key; + break; + } + + if (newitem!=item) { + Hide_Mouse(); + idx=Select_To_Entry(item, field, index); + drawy=menuy+(item*menuskip); + Plain_Text_Print(text[idx], menux, drawy, normcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + idx=Select_To_Entry(newitem, field, index); + drawy=menuy+(newitem*menuskip); + Plain_Text_Print(text[idx], menux, drawy, litcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); /* resurrect the mouse */ + } + + if (select!=-1) { + idx=Select_To_Entry(select, field, index); + Hide_Mouse(); /* get rid of the mouse */ + drawy=menuy+(newitem*menuskip); + Flash_Line(text[idx], menux, drawy, normcol, litcol, TBLACK); + Show_Mouse(); + select=idx; + } + + menuptr[MSELECTED]=newitem; /* update menu select */ + + return(select); +} + + +/*************************************************************************** + * Do_Menu -- Generic menu processor. * + * * + * This helper function displays a menu of specified entries and waits * + * for the player to make a selection. If a selection is made, then * + * a whole number (starting at 0) is returned matching the entry * + * selected. If ESC is pressed, then -1 is returned. * + * * + * INPUT: strings -- A pointer to an array of pointers to text strings. * + * Each entry in the list will be a menu entry that * + * can be selected. * + * * + * blue -- Should the special blue color be used to display * + * the menu? * + * * + * OUTPUT: Returns with the cardinal number of the selected menu entry. * + * If ESC was pressed, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=========================================================================*/ +int Do_Menu(char const ** strings, bool ) +{ + int count; // Number of entries in this menu. + int length; // The width of the menu (in pixels). + char const ** ptr; // Working menu text pointer. + int selection; // Selection from user. + + if (!strings) return(-1); + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + + /* + ** Determine the number of entries in this string. + */ + ptr = strings; + count = 0; + while (*ptr++) { + count++; + } + MenuList[0][ITEMSHIGH] = count; + + /* + ** Determine the width of the menu by finding the length of the + ** longest menu entry. + */ + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_8POINT|TPF_DROPSHADOW); + length = 0; + ptr = strings; + while (*ptr) { + length = max(length, (int)String_Pixel_Width(*ptr)); + ptr++; + } + length += 7; + MenuList[0][ITEMWIDTH] = length >> 3; + + /* + ** Adjust the window values to match the size of the + ** specified menu. + */ + WindowList[WINDOW_MENU][WINDOWWIDTH] = (MenuList[0][ITEMWIDTH] + 2) * 8; + WindowList[WINDOW_MENU][WINDOWX] = (19 - (length >> 4)) * 8; + WindowList[WINDOW_MENU][WINDOWY] = 174 - (unsigned)(MenuList[0][ITEMSHIGH] * (FontHeight+FontYSpacing)); + WindowList[WINDOW_MENU][WINDOWHEIGHT] = MenuList[0][ITEMSHIGH] * FontHeight + 5 /*11*/; + + /* + ** Display the menu. + */ + Change_Window((int)WINDOW_MENU); + Show_Mouse(); + Window_Box(WINDOW_MENU, BOXSTYLE_RAISED); + Setup_Menu(0, strings, 0xFFFFL, 0, 0); + + Keyboard->Clear(); + selection = -1; + UnknownKey = 0; + while (selection == -1) { + Call_Back(); + selection = Check_Menu(0, strings, NULL, 0xFFL, 0); + if (UnknownKey != 0 || UnknownKey == KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) break; + } + Keyboard->Clear(); + Hide_Mouse(); + + HidPage.Blit(SeenPage); +//WindowList[WINDOW_MAIN][2] = SeenBuff.Get_Width();//BG + Change_Window((int)WINDOW_MAIN); + Map.Flag_To_Redraw(true); + return(selection); +} + + +/*************************************************************************** + * Main_Menu -- Menu processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * index of item selected, -1 if time out * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/17/1995 BRR : Created. * + *=========================================================================*/ +int Main_Menu(unsigned long ) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 152 * RESFACTOR; +#ifdef FIXIT_VERSION_3 + int d_dialog_h = 100 * RESFACTOR; +#else +//#ifdef WIN32 //Extra 'Internet' option on WIN32 menu +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + int d_dialog_h = 100 * RESFACTOR; +#else +// #if defined(MPEGMOVIE) // Denzil 6/25/98 - Video settings +// int d_dialog_h = 100 * RESFACTOR; +// #else + int d_dialog_h = 80 * RESFACTOR; +// #endif +#endif //WIN32 +#endif //FIXIT_VERSION_3 + int d_dialog_x = 85 * RESFACTOR; + int d_dialog_y = 75 * RESFACTOR; + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_start_w = 118 * RESFACTOR; + int d_start_h = 9 * RESFACTOR; + int d_start_x = 102 * RESFACTOR; +#ifndef FIXIT_VERSION_3 // Removed button from main menu. +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - no internet play + int d_internet_w = 118 * RESFACTOR; + int d_internet_h = 9 * RESFACTOR; + int d_internet_x = 102 * RESFACTOR; +#endif //WIN32 +#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// int d_movie_w = 118 * RESFACTOR; +// int d_movie_h = 9 * RESFACTOR; +// int d_movie_x = 102 * RESFACTOR; +//#endif + + int d_load_w = 118 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = 102 * RESFACTOR; + + int d_multi_w = 118 * RESFACTOR; + int d_multi_h = 9 * RESFACTOR; + int d_multi_x = 102 * RESFACTOR; + + int d_intro_w = 118 * RESFACTOR; + int d_intro_h = 9 * RESFACTOR; + int d_intro_x = 102 * RESFACTOR; + + int d_exit_w = 118 * RESFACTOR; //changed value to 118 V.Grippi + int d_exit_h = 9 * RESFACTOR; + int d_exit_x = 102 *RESFACTOR; //Added V.Grippi + + int starty = d_dialog_y + (12 * RESFACTOR); + +//#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play +//#ifndef FIXIT_VERSION_3 + static int max_buttons = 7; +//#else +// static int max_buttons = 6; +//#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// max_buttons++; +//#endif + /* + ** Button enumerations: + */ + // Enums in Select_Game() must match order of buttons in Main_Menu(). +#ifdef FIXIT_VERSION_3 + enum { + BUTTON_EXPAND=100, // (CS) + BUTTON_EXPAND_AM, + BUTTON_START, + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; +#else // FIXIT_VERSION_3 + enum { + BUTTON_EXPAND=100, + BUTTON_START, +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + BUTTON_INTERNET, +#endif //WIN32 +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// BUTTON_MOVIE, +//#endif + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; +#endif // FIXIT_VERSION_3 + + /* + ** Dialog variables: + */ +#ifdef FIXIT_VERSION_3 + bool bExpansionCS = Expansion_CS_Present(); + bool bExpansionAM = Expansion_AM_Present(); +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + bool expansions = Expansion_CS_Present() | Expansion_AM_Present(); +#else + bool expansions = Expansion_CS_Present(); +#endif +#endif + KeyNumType input; // input from user + int retval; // return value + int curbutton; + TextButtonClass * buttons[7]; + unsigned long starttime; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + +#ifdef FIXIT_VERSION_3 + int ystep = 14 * RESFACTOR; + if( bExpansionCS ) + { + if( bExpansionAM ) + ystep = 12 * RESFACTOR; + else + ystep = 13 * RESFACTOR; + } + else if( bExpansionAM ) + ystep = 13 * RESFACTOR; + + TextButtonClass expandbtnCS( BUTTON_EXPAND, TXT_WOL_CS_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h ); + if( bExpansionCS ) + starty += ystep; + TextButtonClass expandbtnAM( BUTTON_EXPAND_AM, TXT_WOL_AM_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h ); + if( bExpansionAM ) + starty += ystep; +#else + int ystep = 12 * RESFACTOR; + if (expansions) ystep = 10 * RESFACTOR; + + TextButtonClass expandbtn (BUTTON_EXPAND, TXT_NEW_MISSIONS, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h); + if (expansions) starty += ystep; +#endif + + TextButtonClass startbtn(BUTTON_START, TXT_START_NEW_GAME, TPF_BUTTON, d_start_x, starty, d_start_w, d_start_h); + starty += ystep; +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - no internet play + TextButtonClass internetbutton (BUTTON_INTERNET, TXT_INTERNET, TPF_BUTTON, d_internet_x, starty, d_internet_w, d_internet_h); + starty += ystep; +#endif //WIN32 +#endif + +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// TextButtonClass moviebutton(BUTTON_MOVIE, "Movie Settings", TPF_BUTTON, d_movie_x, starty, d_movie_w, d_movie_h); +// starty += ystep; +//#endif //WIN32 + + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_MISSION, TPF_BUTTON, d_load_x, starty, d_load_w, d_load_h); + starty += ystep; + + TextButtonClass multibtn(BUTTON_MULTI, TXT_MULTIPLAYER_GAME, TPF_BUTTON, d_multi_x, starty, d_multi_w, d_multi_h); + starty += ystep; + + TextButtonClass introbtn(BUTTON_INTRO, TXT_INTRO, TPF_BUTTON, d_intro_x, starty, d_intro_w, d_intro_h); + starty += ystep; + + TextButtonClass exitbtn(BUTTON_EXIT, TXT_EXIT_GAME, TPF_BUTTON, + d_exit_x, starty, d_exit_w, d_exit_h); + starty += ystep; + + /* + ** Initialize + */ + if (RequiredCD != -2) { + RequiredCD = -1; + Force_CD_Available(RequiredCD); + } + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + starttime = TickCount; + + /* + ** Create the list + */ + commands = &startbtn; +#ifdef FIXIT_VERSION_3 + if( bExpansionCS ) + expandbtnCS.Add_Tail(*commands); + if( bExpansionAM ) + expandbtnAM.Add_Tail(*commands); +#else + if (expansions) { + expandbtn.Add_Tail(*commands); + } +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No internet play + internetbutton.Add_Tail(*commands); +#endif //WIN32 +#endif +//#if defined(MPEGMOVIE) // Denzil 6/26/98 Video settings +// moviebutton.Add_Tail(*commands); +//#endif + loadbtn.Add_Tail(*commands); + multibtn.Add_Tail(*commands); + introbtn.Add_Tail(*commands); + exitbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ +#ifdef FIXIT_VERSION_3 + curbutton = bExpansionCS ? 0 : ( bExpansionAM ? 1 : 2 ); + + buttons[0] = &expandbtnCS; + buttons[1] = &expandbtnAM; + buttons[2] = &startbtn; + buttons[3] = &loadbtn; + buttons[4] = &multibtn; + buttons[5] = &introbtn; + buttons[6] = &exitbtn; +#else + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + + buttons[0] = &expandbtn; + buttons[1] = &startbtn; + buttons[2] = &internetbutton; + buttons[3] = &loadbtn; + buttons[4] = &multibtn; + buttons[5] = &introbtn; + buttons[6] = &exitbtn; +#endif + + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Play_Song(THEME_INTRO); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + while (process) { + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } +#endif + + /* + ** If timeout expires, bail + */ +// if (timeout && TickCount - starttime > timeout) { +// retval = -1; +// process = false; +// } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + /* + ** Display the title and text overlay for the menu. + */ + Set_Logic_Page(HidPage); +// Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); +// Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + commands->Draw_All(); +#ifdef FIXIT_VERSION_3 + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(5 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); +#else +#ifndef WIN32 + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(8 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); + +#else + Fancy_Text_Print("V%s", d_dialog_x+d_dialog_w- (18 * RESFACTOR), + d_dialog_y+d_dialog_h-(11 * RESFACTOR), GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); + +#endif +#endif + + /* + ** Copy the menu to the visible page. + */ + Hide_Mouse(); + HidPage.Blit(SeenPage); + Show_Mouse(); + + Set_Logic_Page(SeenBuff); + display = false; + } + + /* + ** Get and process player input. + */ + input = commands->Input(); + +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - No Internet play + /* + ** Check to see if WChat has told us to start playing an internet game + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + retval = BUTTON_INTERNET - BUTTON_EXPAND; + process = false; + input = KN_NONE; + } +#endif //WIN32 +#endif + + /* + ** If there is input, then take this opportunity to seed some bits + ** to the cryptographic random number generator. + */ + if (input != 0) { + #ifdef WIN32 + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + #else + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + #endif + } + + /* + ** Dispatch the input to be processed. + */ + switch (input) { + case (BUTTON_EXPAND | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifdef FIXIT_VERSION_3 + case (BUTTON_EXPAND_AM | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#endif + + case (BUTTON_START | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifndef FIXIT_VERSION_3 + #if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + case (BUTTON_INTERNET | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + #endif //WIN32 +#endif + +// #if defined(MPEGMOVIE) +// case (BUTTON_MOVIE | KN_BUTTON): +// retval = (input & 0x7FFF) - BUTTON_EXPAND; +// process = false; +// break; +// #endif + + case (BUTTON_LOAD | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_MULTI | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_INTRO | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_EXIT | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#if (0) + case KN_BACKSPACE: + Show_Who_Was_Responsible (); + display = true; + Theme.Play_Song(THEME_INTRO); + break; +#endif //(0) + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; +#ifdef FIXIT_VERSION_3 + switch( curbutton ) + { + case -1: + curbutton = max_buttons - 1; + break; + case 0: + if( !bExpansionCS ) + curbutton = max_buttons - 1; + break; + case 1: + if( !bExpansionAM ) + { + if( bExpansionCS ) + curbutton = 0; + else + curbutton = max_buttons - 1; + } + break; + } +#else + if (expansions) { + if (curbutton < 0) { + curbutton = max_buttons-1; + } + } else { + if (curbutton < 1) { + curbutton = max_buttons-1; + } + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; +#ifdef FIXIT_VERSION_3 + if( curbutton == max_buttons ) + { + if( bExpansionCS ) + curbutton = 0; + else if( bExpansionAM ) + curbutton = 1; + else + curbutton = 2; + } + else if( curbutton == 1 && !bExpansionAM ) + curbutton = 2; +#else + if (curbutton > (max_buttons - 1)) { + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + retval = curbutton; + process = false; + break; + + + + case KN_LMOUSE: + if (Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, + + 9*RESFACTOR, 10*RESFACTOR, + 79*RESFACTOR, 24*RESFACTOR)){ + Show_Who_Was_Responsible(); + display = true; + Theme.Play_Song(THEME_INTRO); + + break; + } +#ifdef FIXIT_ANTS + #ifdef FIXIT_PATCH_108 + if (Is_Counterstrike_Installed() == true) + { + #endif + if ((Keyboard->Down(KN_LSHIFT) || Keyboard->Down(KN_RSHIFT)) && Coordinates_In_Region(Keyboard->MouseQX, Keyboard->MouseQY, 260*RESFACTOR, 0, 320*RESFACTOR, 50*RESFACTOR)) { + AntsEnabled = true; + process = false; +#ifdef FIXIT_VERSION_3 + retval = 2; // To match SEL_START_NEW_GAME +#else + retval = 1; +#endif + } + #ifdef FIXIT_PATCH_108 + } + #endif +#endif + + + default: + break; + } + } + + Options.Set_Score_Volume(oldvolume, false); + + return(retval); +} diff --git a/CODE/MESSAGE.H b/CODE/MESSAGE.H new file mode 100644 index 0000000..cdb6be4 --- /dev/null +++ b/CODE/MESSAGE.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + +#define MESSAGE_NONE 0 // +#define MESSAGE_BUILD_WINDTRAP 1 // You must build a Windtrap +#define MESSAGE_STRUCT_CONCRETE 2 // Concrete: Use concrete to +#define MESSAGE_STRUCT_PALACE 3 // Palace: This is your +#define MESSAGE_STRUCT_LIGHT 4 // Light Factory: The Light +#define MESSAGE_STRUCT_HEAVY 5 // Heavy Factory: The Heavy +#define MESSAGE_STRUCT_HITECH 6 // Hi-Tech Factory: The +#define MESSAGE_STRUCT_IX 7 // House IX: The IX Research +#define MESSAGE_STRUCT_WOR 8 // WOR: Wor is used to train +#define MESSAGE_STRUCT_CONST 9 // Construction Facility: All +#define MESSAGE_STRUCT_WINDTRAP 10 // Windtrap: The windtrap +#define MESSAGE_STRUCT_BARRACKS 11 // Barracks: The Barracks is +#define MESSAGE_STRUCT_STARPORT 12 // Startport: The Starport is +#define MESSAGE_STRUCT_REFINERY 13 // Spice Refinery: The +#define MESSAGE_STRUCT_REPAIR 14 // Repair Facility: The Repair +#define MESSAGE_STRUCT_WALL 15 // Wall: The wall is used for +#define MESSAGE_STRUCT_TURRET 16 // Gun Turret: The cannon +#define MESSAGE_STRUCT_RTURRET 17 // Rocket Turret: The +#define MESSAGE_STRUCT_SILO 18 // Spice Silo: The Spice silo +#define MESSAGE_STRUCT_OUTPOST 19 // Outpost: The Outpost +#define MESSAGE_NEED_CONCRETE 20 // There isn't enough open +#define MESSAGE_SAND 21 // Sand: This is sand terrain. +#define MESSAGE_DUNE 22 // Sand Dunes: These are an +#define MESSAGE_ROCK 23 // Rock: This is rock terrain. +#define MESSAGE_MOUNT 24 // Mountain: Mountains on +#define MESSAGE_SPICE 25 // Spice Field: This is the +#define MESSAGE_ADJACENT 26 // Structures must be placed +#define MESSAGE_SEARCH4SPICE 27 // Search for spice fields to +#define MESSAGE_SANDWORM 28 // Warning: Sandworms diff --git a/CODE/MISSION.CPP b/CODE/MISSION.CPP new file mode 100644 index 0000000..49b0d66 --- /dev/null +++ b/CODE/MISSION.CPP @@ -0,0 +1,573 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MISSION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MISSION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : September 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MissionClass::AI -- Processes order script. * + * MissionClass::Assign_Mission -- Give an order to a unit. * + * MissionClass::Commence -- Start script with new order. * + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * MissionClass::Restore_Mission -- Restores overridden mission * + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a te* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * * + * This is the default constructor for the mission class object. It sets the mission * + * handler into a default -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + * 03/01/1996 JLB : Uses initializer lists. * + *=============================================================================================*/ +MissionClass::MissionClass(RTTIType rtti, int id) : + ObjectClass(rtti, id), + Mission(MISSION_NONE), + SuspendedMission(MISSION_NONE), + MissionQueue(MISSION_NONE), + Status(0), + Timer(0) +{ +} + + +/*********************************************************************************************** + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * * + * These are the stub routines that handle the mission logic. They do nothing at this * + * level. Derived classes will override these routine as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this mission * + * handler again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +int MissionClass::Mission_Sleep(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Ambush(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Attack(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Capture(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard_Area(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Harvest(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Move(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Retreat(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Return(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Stop(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Unload(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Enter(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Construction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Deconstruction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Repair(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Missile(void) {return TICKS_PER_SECOND*30;}; + + +/*********************************************************************************************** + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * * + * Use this routine to set the current mission for this object. This routine will blast * + * over the current mission, bypassing the queue method. Call it when the mission needs * + * to be changed immediately. * + * * + * INPUT: mission -- The mission to set to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Set_Mission(MissionType mission) +{ + assert(IsActive); + + Mission = mission; + MissionQueue = MISSION_NONE; +} + + +/*********************************************************************************************** + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * * + * Use this routine to fetch the mission that this object is CURRENTLY acting under. The * + * mission queue may be filled with a imminent mission change, but this routine does not * + * consider that. It only returns the CURRENT mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the mission that this unit is currently following. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionType MissionClass::Get_Mission(void) const +{ + assert(IsActive); + + return(Mission == MISSION_NONE ? MissionQueue : Mission); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * * + * This is a debugging function that dumps this class' status to the monochrome screen * + * for review. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(1, 9);mono->Printf("%-14s", MissionClass::Mission_Name(Mission)); + mono->Set_Cursor(16, 9);mono->Printf("%-12s", MissionClass::Mission_Name(MissionQueue)); + mono->Set_Cursor(1, 7);mono->Printf("%3d", (long)Timer); + mono->Set_Cursor(6, 7);mono->Printf("%2d", Status); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * MissionClass::AI -- Processes order script. * + * * + * This routine will process the order script for as much time as * + * possible or until a script delay is detected. This routine should * + * be called for every unit once per game loop (if possible). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/25/1995 JLB : Added new missions. * + *=============================================================================================*/ +void MissionClass::AI(void) +{ + assert(IsActive); + + ObjectClass::AI(); + + /* + ** If this is the kind of object that is "paralyzed with fear" while it is above + ** ground level (such as when be paradropped), it will perform no mission AI + ** processing. + */ + if ((What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) && Height > 0) { + return; + } + + /* + ** This is the script AI equivalent processing. + */ + BStart(BENCH_MISSION); + if (Timer == 0 && Strength > 0) { + switch (Mission) { + default: + Timer = Mission_Sleep(); + break; + + case MISSION_HARMLESS: + case MISSION_SLEEP: + Timer = Mission_Sleep(); + break; + + case MISSION_STICKY: + case MISSION_GUARD: + Timer = Mission_Guard(); + break; + + case MISSION_ENTER: + Timer = Mission_Enter(); + break; + + case MISSION_CONSTRUCTION: + Timer = Mission_Construction(); + break; + + case MISSION_DECONSTRUCTION: + Timer = Mission_Deconstruction(); + break; + + case MISSION_CAPTURE: + case MISSION_SABOTAGE: + Timer = Mission_Capture(); + break; + + case MISSION_QMOVE: + case MISSION_MOVE: + Timer = Mission_Move(); + break; + + case MISSION_ATTACK: + Timer = Mission_Attack(); + break; + + case MISSION_RETREAT: + Timer = Mission_Retreat(); + break; + + case MISSION_HARVEST: + Timer = Mission_Harvest(); + break; + + case MISSION_GUARD_AREA: + Timer = Mission_Guard_Area(); + break; + + case MISSION_RETURN: + Timer = Mission_Return(); + break; + + case MISSION_STOP: + Timer = Mission_Stop(); + break; + + case MISSION_AMBUSH: + Timer = Mission_Ambush(); + break; + + case MISSION_HUNT: + case MISSION_RESCUE: + Timer = Mission_Hunt(); + break; + +// case MISSION_TIMED_HUNT: +// Timer = Mission_Timed_Hunt(); +// break; + + case MISSION_UNLOAD: + Timer = Mission_Unload(); + break; + + case MISSION_REPAIR: + Timer = Mission_Repair(); + break; + + case MISSION_MISSILE: + Timer = Mission_Missile(); + break; + } + } + BEnd(BENCH_MISSION); +} + + +/*********************************************************************************************** + * MissionClass::Commence -- Start script with new order. * + * * + * This routine will start script processing according to any queued * + * order it may have. If there is no queued order, then this routine * + * does nothing. Call this routine whenever the unit is in a good * + * position to change its order (such as when it is stopped). * + * * + * INPUT: none * + * * + * OUTPUT: Did the mission actually change? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 07/14/1994 JLB : Simplified. * + * 06/17/1995 JLB : Returns success flag. * + *=============================================================================================*/ +bool MissionClass::Commence(void) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + Mission = MissionQueue; + MissionQueue = MISSION_NONE; + + /* + ** Force immediate state machine processing at the first state machine state value. + */ + Timer = 0; + Status = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Assign_Mission -- Give an order to a unit. * + * * + * This routine will assign the specified mission to the mission queue for this object. * + * The actual mission logic will then be performed at the first available and legal * + * opportunity. * + * * + * INPUT: order -- Mission to give the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MissionClass::Assign_Mission(MissionType order) +{ + assert(IsActive); + + /* + ** Ensure that a MISSION_QMOVE is translated into a MISSION_MOVE. + */ + if (order == MISSION_QMOVE) order = MISSION_MOVE; + + if (order != MISSION_NONE && Mission != order) { + MissionQueue = order; + } +} + + +/*********************************************************************************************** + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * * + * This routine is used to convert an ASCII order name into the actual * + * order number it represents. Typically, this is used when processing * + * a scenario INI file. * + * * + * INPUT: name -- The ASCII order name to process. * + * * + * OUTPUT: Returns with the actual order number that the ASCII name * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/22/1994 JLB : Converted to static member function. * + *=============================================================================================*/ +MissionType MissionClass::Mission_From_Name(char const * name) +{ + MissionType order; + + if (name) { + for (order = MISSION_FIRST; order < MISSION_COUNT; order++) { + if (stricmp(Missions[order], name) == 0) { + return(order); + } + } + } + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * * + * Use this routine to convert a mission number into the ASCII string that represents * + * it. Typical use of this is when generating an INI file. * + * * + * INPUT: mission -- The mission number to convert. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that represents the mission type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +char const * MissionClass::Mission_Name(MissionType mission) +{ + if (mission != MISSION_NONE) { + return(Missions[mission]); + } + return("None"); +} + + +/*********************************************************************************************** + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +void MissionClass::Override_Mission(MissionType mission, TARGET, TARGET) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + SuspendedMission = MissionQueue; + } else { + SuspendedMission = Mission; + } + + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * MissionClass::Restore_Mission -- Restores overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +bool MissionClass::Restore_Mission(void) +{ + assert(IsActive); + + if (SuspendedMission != MISSION_NONE) { + Assign_Mission(SuspendedMission); + SuspendedMission= MISSION_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a tea * + * * + * Some missions preclude recruitment into a team. This routine will examine the mission * + * specified and if not allowed for a team, it will return false. * + * * + * INPUT: mission -- The mission type to examine. * + * * + * OUTPUT: bool; Is an object following this mission allowed to be recruited into a team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool MissionClass::Is_Recruitable_Mission(MissionType mission) const +{ + if (mission == MISSION_NONE) { + return(true); + } + return(MissionControl[mission].IsRecruitable); +} + + + +MissionControlClass::MissionControlClass(void) : + Mission(MISSION_NONE), + IsNoThreat(false), + IsZombie(false), + IsRecruitable(true), + IsParalyzed(false), + IsRetaliate(true), + IsScatter(true), + Rate(".016"), + AARate(".016") +{ +} + + +char const * MissionControlClass::Name(void) const +{ + if (Mission == MISSION_NONE) { + return(""); + } + return(Missions[Mission]); +} + + + +bool MissionControlClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsNoThreat = ini.Get_Bool(Name(), "NoThreat", IsNoThreat); + IsZombie = ini.Get_Bool(Name(), "Zombie", IsZombie); + IsRecruitable = ini.Get_Bool(Name(), "Recruitable", IsRecruitable); + IsParalyzed = ini.Get_Bool(Name(), "Paralyzed", IsParalyzed); + IsRetaliate = ini.Get_Bool(Name(), "Retaliate", IsRetaliate); + IsScatter = ini.Get_Bool(Name(), "Scatter", IsScatter); + Rate = ini.Get_Fixed(Name(), "Rate", Rate); + AARate = ini.Get_Fixed(Name(), "AARate", 0); + if (AARate == 0) { + AARate = Rate; + } + return(true); + } + return(false); +} diff --git a/CODE/MISSION.H b/CODE/MISSION.H new file mode 100644 index 0000000..ae4b18d --- /dev/null +++ b/CODE/MISSION.H @@ -0,0 +1,202 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MISSION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MISSION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISSION_H +#define MISSION_H + +#include "object.h" +#include "monoc.h" + +/**************************************************************************** +** This handles order assignment and tracking. The order is used to guide +** overall AI processing. +*/ +class MissionClass : public ObjectClass +{ + public: + + /* + ** This the tactical strategy to use. It is used by the unit script. This + ** is a general guide for unit AI processing. + */ + MissionType Mission; + MissionType SuspendedMission; + + /* + ** The order queue is used for orders that should take effect when the vehicle + ** has reached the center point of a cell. The queued order number is +1 when stored here + ** so that 0 will indicated there is no queued order. + */ + MissionType MissionQueue; + + int Status; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + MissionClass(RTTIType rtti, int id); + MissionClass(NoInitClass const & x) : ObjectClass(x), Timer(x) {}; + virtual ~MissionClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + void Shorten_Mission_Timer(void) {Timer = 0;} + virtual MissionType Get_Mission(void) const; + virtual void Assign_Mission(MissionType mission); + virtual bool Commence(void); + virtual void AI(void); + + /* + ** Support functions. + */ + virtual int Mission_Sleep(void); + virtual int Mission_Ambush(void); + virtual int Mission_Attack(void); + virtual int Mission_Capture(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); +// virtual int Mission_Timed_Hunt(void); + virtual int Mission_Move(void); + virtual int Mission_Retreat(void); + virtual int Mission_Return(void); + virtual int Mission_Stop(void); + virtual int Mission_Unload(void); + virtual int Mission_Enter(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Repair(void); + virtual int Mission_Missile(void); + virtual void Set_Mission(MissionType mission); + static bool Is_Recruitable_Mission(MissionType mission); + + static char const * Mission_Name(MissionType order); + static MissionType Mission_From_Name(char const *name); + virtual void Override_Mission(MissionType mission, TARGET, TARGET); + virtual bool Restore_Mission(void); + + private: + + /* + ** This the thread processing timer. When this value counts down to zero, then + ** more script processing may occur. + */ + CDTimerClass Timer; +}; + + +/**************************************************************************** +** This is the mission control (pun) that controls how each mission behaves +** when it comes to interacting with the game world. Example; some +** missions allow the object to scatter from threats, while others require +** the object to remain in place. This kind of characteristics are specfied +** by this class. +*/ +class MissionControlClass +{ + public: + MissionControlClass(void); + + bool Read_INI(CCINIClass & ini); + int Normal_Delay(void) const {return(TICKS_PER_MINUTE * Rate);} + int AA_Delay(void) const {return(TICKS_PER_MINUTE * AARate);} + + /* + ** This is the mission identifier that this mission represents. + */ + MissionType Mission; + + char const * Name(void) const; + + /* + ** If the object should not be considered a threat when it + ** comes to target scanning, then this will be true. + */ + unsigned IsNoThreat:1; + + /* + ** If objects in this mission should avoid targeting the enemy and + ** also avoid responding to the enemy, then this will be true. + */ + unsigned IsZombie:1; + + /* + ** An ojbect that can be recruited into a team must be on a mission + ** of this type. + */ + unsigned IsRecruitable:1; + + /* + ** If the object can behave normally except that it cannot + ** move to another location, then this flag will be true. + */ + unsigned IsParalyzed:1; + + /* + ** If an object on this mission is damaged, it is allowed to + ** retaliate? + */ + unsigned IsRetaliate:1; + + /* + ** Is the object allowed to scatter from immediate threats? + */ + unsigned IsScatter:1; + + /* + ** This specifies the time to delay between calls to the mission handler for those cases + ** where the delay could be indefinate. The exception would be when timing is critical. + ** Typical use of this would be to regulate the delay between mundane mission processing + ** in order to achieve less game overhead. + */ + fixed Rate; + + /* + ** Anti-Aircraft buildings (and units) in guard or guard area mode will use this override + ** delay interval instead of the normal "Rate" value. + */ + fixed AARate; +}; + + +#endif diff --git a/CODE/MIXFILE.CPP b/CODE/MIXFILE.CPP new file mode 100644 index 0000000..0e6fe0a --- /dev/null +++ b/CODE/MIXFILE.CPP @@ -0,0 +1,580 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MIXFILE.CPP 2 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + * * + * Modified by Vic Grippi for WwXlat Tool 10/14/96 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * MixFileClass::Free -- Uncaches a cached mixfile. * + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.* + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "mixfile.h" + +#include "cdfile.h" +extern MFCD temp; + + +//template int Compare(T const *obj1, T const *obj2) { +// if (*obj1 < *obj2) return(-1); +// if (*obj1 > *obj2) return(1); +// return(0); +//}; + + +/* +** This is the pointer to the first mixfile in the list of mixfiles registered +** with the mixfile system. +*/ +template +List > MixFileClass::List; + + +/*********************************************************************************************** + * MixFileClass::Free -- Uncaches a cached mixfile. * + * * + * Use this routine to uncache a mixfile that has been cached. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. * + * * + * OUTPUT: bool; Was the mixfile found and freed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Free(char const * filename) +{ + MixFileClass * ptr = Finder(filename); + + if (ptr) { + ptr->Free(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * * + * This destructor will free all memory allocated by this mixfile and will remove it from * + * the system. A mixfile removed in this fashion must be created anew in order to be * + * subsequent used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 01/06/1995 JLB : Puts mixfile header table into EMS. * + *=============================================================================================*/ +template +MixFileClass::~MixFileClass(void) +{ + /* + ** Deallocate any allocated memory. + */ + if (Filename) { + free((char *)Filename); + } + if (Data != NULL && IsAllocated) { + delete [] Data; + IsAllocated = false; + } + Data = NULL; + + if (HeaderBuffer != NULL) { + delete [] HeaderBuffer; + HeaderBuffer = NULL; + } + + /* + ** Unlink this mixfile object from the chain. + */ + Unlink(); +} + + +/*********************************************************************************************** + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * * + * This is the constructor for the mixfile object. It takes a filename and a memory * + * handler object and registers the mixfile object with the system. The index block is * + * allocated and loaded from disk by this routine. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles compressed file header. * + *=============================================================================================*/ +template +MixFileClass::MixFileClass(char const * filename, PKey const * key) : + IsDigest(false), + IsEncrypted(false), + IsAllocated(false), + Filename(0), + Count(0), + DataSize(0), + DataStart(0), + HeaderBuffer(0), + Data(0) +{ + /* + ** Check to see if the file is available. If it isn't, then + ** no further processing is needed or possible. + */ + if (!Force_CD_Available(RequiredCD)) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } + + T file(filename); // Working file object. + Filename = strdup(file.File_Name()); + FileStraw fstraw(file); + PKStraw pstraw(PKStraw::DECRYPT, CryptRandom); + Straw * straw = &fstraw; + + if (!file.Is_Available()) return; + + /* + ** Stuctures used to hold the various file headers. + */ + FileHeader fileheader; + struct { + short First; // Always zero for extended mixfile format. + short Second; // Bitfield of extensions to this mixfile. + } alternate; + + /* + ** Fetch the first bit of the file. From this bit, it is possible to detect + ** whether this is an extended mixfile format or the plain format. An + ** extended format may have extra options or data layout. + */ + int got = straw->Get(&alternate, sizeof(alternate)); + + /* + ** Detect if this is an extended mixfile. If so, then see if it is encrypted + ** and/or has a message digest attached. Otherwise, just retrieve the + ** plain mixfile header. + */ + if (alternate.First == 0) { + IsDigest = ((alternate.Second & 0x01) != 0); + IsEncrypted = ((alternate.Second & 0x02) != 0); + + if (IsEncrypted) { + pstraw.Key(key); + pstraw.Get_From(&fstraw); + straw = &pstraw; + } + straw->Get(&fileheader, sizeof(fileheader)); + + } else { + memmove(&fileheader, &alternate, sizeof(alternate)); + straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate)); + } + + Count = fileheader.count; + DataSize = fileheader.size; +//BGMono_Printf("Mixfileclass %s DataSize: %08x \n",filename,DataSize);Get_Key(); + /* + ** Load up the offset control array. If RAM is exhausted, then the mixfile is invalid. + */ + HeaderBuffer = new SubBlock [Count]; + if (HeaderBuffer == NULL) return; + straw->Get(HeaderBuffer, Count * sizeof(SubBlock)); + + /* + ** The start of the embedded mixfile data will be at the current file offset. + ** This should be true even if the file header has been encrypted because the file + ** header was cleverly written with just the sufficient number of padding bytes so + ** that this condition would be true. + */ + DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart; +// DataStart = file.Seek(0, SEEK_CUR); + + /* + ** Attach to list of mixfiles. + */ + List.Add_Tail(this); +} + + +/*********************************************************************************************** + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * * + * This routine will return with a pointer to the specified data file if the file resides * + * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident * + * file directly rather than going through the process of pseudo disk access. This will * + * save both time and RAM. * + * * + * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. * + * * + * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +template +void const * MixFileClass::Retrieve(char const * filename) +{ + void * ptr = 0; + Offset(filename, &ptr); + return(ptr); +}; + + +/*********************************************************************************************** + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * * + * This routine will scan through all registered mixfiles and return with a pointer to * + * the matching mixfile. If no mixfile could be found that matches the name specified, * + * then NULL is returned. * + * * + * INPUT: filename -- Pointer to the filename to search for. * + * * + * OUTPUT: Returns with a pointer to the matching mixfile -- if found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 06/08/1996 JLB : Only compares filename and extension. * + *=============================================================================================*/ +template +MixFileClass * MixFileClass::Finder(char const * filename) +{ + MixFileClass * ptr = List.First(); + while (ptr->Is_Valid()) { + char path[_MAX_PATH]; + char name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + /* + ** Strip the drive and path (if present) off of the filename + ** in the mixfile list. This enables a simple comparison to the + ** filename specified. The filename specified won't have a path attached and + ** the full pathname in the mixfile list WILL have a path attached. Hence, this + ** stripping of the path is necessary. + */ + _splitpath(ptr->Filename, NULL, NULL, name, ext); + _makepath(path, NULL, NULL, name, ext); + + if (stricmp(path, filename) == 0) { + return(ptr); + } + ptr = ptr->Next(); + } + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * * + * This routine will cache the mixfile, specified by name, into RAM. * + * * + * INPUT: filename -- The name of the mixfile that should be cached. * + * * + * OUTPUT: bool; Was the cache successful? * + * * + * WARNINGS: This routine could go to disk for a very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(char const * filename, Buffer const * buffer) +{ + MixFileClass * mixer = Finder(filename); + + if (mixer != NULL) { + return(mixer->Cache(buffer)); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * * + * This load the mixfile data into ram for this mixfile object. This is the counterpart * + * to the Free() function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles attached message digest. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(Buffer const * buffer) +{ + /* + ** If the mixfile is already cached, then no action needs to be performed. + */ + if (Data != NULL) return(true); + + /* + ** If a buffer was supplied (and it is big enough), then use it as the data block + ** pointer. Otherwise, the data block must be allocated. + */ + if (buffer != NULL) { + if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) { + Data = buffer->Get_Buffer(); + } + } else { + Data = new char [DataSize]; + IsAllocated = true; + } + + /* + ** If there is a data buffer to fill, then fill it now. + */ + if (Data != NULL) { + T file(Filename); + + FileStraw fstraw(file); + Straw * straw = &fstraw; + + /* + ** If a message digest is attached, then link a SHA straw segment to the data + ** stream so that the actual SHA can be compared with the attached one. + */ + SHAStraw sha; + if (IsDigest) { + sha.Get_From(fstraw); + straw = &sha; + } + + /* + ** Bias the file to the actual start of the data. This is necessary because the + ** real data starts some distance (not so easily determined) from the beginning of + ** the real file. + */ + file.Open(READ); + file.Bias(0); + file.Bias(DataStart); + + /* + ** Fetch the whole mixfile data in one step. If the number of bytes retrieved + ** does not equal that requested, then this indicates a serious error. + */ + long actual = straw->Get(Data, DataSize); + if (actual != DataSize) { + delete [] Data; + Data = NULL; + file.Error(EIO); + return(false); + } + + /* + ** If there is a digest attached to this mixfile, then read it in and + ** compare it to the generated digest. If they don't match, then + ** return with the "failure to cache" error code. + */ + if (IsDigest) { + char digest1[20]; + char digest2[20]; + sha.Result(digest2); + fstraw.Get(digest1, sizeof(digest1)); + if (memcmp(digest1, digest2, sizeof(digest1)) != 0) { + delete [] Data; + Data = NULL; + return(false); + } + } + + return(true); + } + IsAllocated = false; + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * * + * This routine will free the (presumably large) raw data block, but leave the index * + * block intact. By using this in conjunction with the Cache() function, one can maintain * + * tight control of memory usage. If the index block is desired to be freed, then the * + * mixfile object must be deleted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +void MixFileClass::Free(void) +{ + if (Data != NULL && IsAllocated) { + delete [] Data; + } + Data = NULL; + IsAllocated = false; +} + + +int compfunc(void const * ptr1, void const * ptr2) +{ + if (*(long const *)ptr1 < *(long const *)ptr2) return(-1); + if (*(long const *)ptr1 > *(long const *)ptr2) return(1); + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * * + * This routine will take the filename specified and search through the mixfile system * + * looking for it. If the file was found, then the mixfile it was found in, the offset * + * from the start of the mixfile, and the size of the embedded file will be returned. * + * Using this method it is possible for the CCFileClass system to process it as a normal * + * file. * + * * + * INPUT: filename -- The filename to search for. * + * * + * realptr -- Stores a pointer to the start of the file in memory here. If the * + * file is not in memory, then NULL is stored here. * + * * + * mixfile -- The pointer to the corresponding mixfile is placed here. If no * + * mixfile was found that contains the file, then NULL is stored here. * + * * + * offset -- The starting offset from the beginning of the parent mixfile is * + * stored here. * + * * + * size -- The size of the embedded file is stored here. * + * * + * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist * + * and can be opened. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) const +{ + MixFileClass * ptr; + + if (filename == NULL) { +assert(filename != NULL);//BG + return(false); + } + + /* + ** Create the key block that will be used to binary search for the file. + */ + long crc = Calculate_CRC(strupr((char *)filename), strlen(filename)); + SubBlock key; + key.CRC = crc; + + /* + ** Sweep through all registered mixfiles, trying to find the file in question. + */ + ptr = List.First(); + while (ptr->Is_Valid()) { + SubBlock * block; + + /* + ** Binary search for the file in this mixfile. If it is found, then extract the + ** appropriate information and store it in the locations provided and then return. + */ + block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc); + if (block != NULL) { + if (mixfile != NULL) *mixfile = ptr; + if (size != NULL) *size = block->Size; + if (realptr != NULL) *realptr = NULL; + if (offset != NULL) *offset = block->Offset; + if (realptr != NULL && ptr->Data != NULL) { + *realptr = (char *)ptr->Data + block->Offset; + } + if (ptr->Data == NULL && offset != NULL) { + *offset += ptr->DataStart; + } + return(true); + } + + /* + ** Advance to next mixfile. + */ + ptr = ptr->Next(); + } + + /* + ** All the mixfiles have been examined but no match was found. Return with the non success flag. + */ +assert(1);//BG + return(false); +} + diff --git a/CODE/MIXFILE.H b/CODE/MIXFILE.H new file mode 100644 index 0000000..f828bf5 --- /dev/null +++ b/CODE/MIXFILE.H @@ -0,0 +1,133 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MIXFILE.H 1 3/03/97 10:25a Joe_bostic $ */ + +#ifndef MIXFILE_H +#define MIXFILE_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include +#include "listnode.h" +#include "pk.h" +#include "buff.h" + +template +class MixFileClass : public Node +{ + public: + char const * Filename; // Filename of mixfile. + + + MixFileClass(char const *filename, PKey const * key); + ~MixFileClass(void); + + static bool Free(char const *filename); + void Free(void); + bool Cache(Buffer const * buffer = NULL); + static bool Cache(char const *filename, Buffer const * buffer=NULL); + static bool Offset(char const *filename, void ** realptr = 0, MixFileClass ** mixfile = 0, long * offset = 0, long * size = 0); + static void const * Retrieve(char const *filename); + + struct SubBlock { + long CRC; // CRC code for embedded file. + long Offset; // Offset from start of data section. + long Size; // Size of data subfile. + + int operator < (SubBlock & two) const {return (CRC < two.CRC);}; + int operator > (SubBlock & two) const {return (CRC > two.CRC);}; + int operator == (SubBlock & two) const {return (CRC == two.CRC);}; + }; + + private: + static MixFileClass * Finder(char const * filename); + long Offset(long crc, long * size = 0) const; + + /* + ** If this mixfile has an attached message digest, then this flag + ** will be true. The digest is checked only when the mixfile is + ** cached. + */ + unsigned IsDigest:1; + + /* + ** If the header to this mixfile has been encrypted, then this flag + ** will be true. Although the header of the mixfile may be encrypted, + ** the attached data files are not. + */ + unsigned IsEncrypted:1; + + /* + ** If the cached memory block was allocated by this routine, then this + ** flag will be true. + */ + unsigned IsAllocated:1; + + /* + ** This is the initial file header. It tells how many files are embedded + ** within this mixfile and the total size of all embedded files. + */ + typedef struct { + short count; + long size; + } FileHeader; + + /* + ** The number of files within the mixfile. + */ + int Count; + + /* + ** This is the total size of all the data file embedded within the mixfile. + ** It does not include the header or digest bytes. + */ + long DataSize; + + /* + ** Start of raw data in within the mixfile. + */ + long DataStart; + + /* + ** Points to the file header control block array. Each file in the mixfile will + ** have an entry in this table. The entries are sorted by their (signed) CRC value. + */ + SubBlock * HeaderBuffer; + + /* + ** If the mixfile has been cached, then this points to the cached data. + */ + void * Data; // Pointer to raw data. + + static List List; +}; + +#endif diff --git a/CODE/MONOC.CPP b/CODE/MONOC.CPP new file mode 100644 index 0000000..20c9794 --- /dev/null +++ b/CODE/MONOC.CPP @@ -0,0 +1,1132 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MONOC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::Pan -- Scroll the window right or left. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Print -- Simple print of text number. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Printf -- Prints formatted text using text string number. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::Text_Print -- Simple text printing from text number. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * Mono_Print -- Prints simple text to monochrome screen. * + * Mono_Printf -- Prints formatted text to visible page. * + * Mono_Text_Print -- Prints text to location specified. * + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "watcom.h" +#include "monoc.h" + +#include "function.h" + +#include +#include +#include +#include +#include +#include + + + +extern void output(short port, short data); +#pragma aux output parm [dx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" + + +bool MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES]; +//MonoClass::MonoPageType * MonoClass::MonoRAM = (MonoClass::MonoPageType *) 0xB0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +#ifdef NEVER +template +T min(T a, T b) { + if (a < b) return(a); + return(b); +} + +template +T max(T a, T b) { + if (a > b) return(a); + return(b); +} +#endif + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) : + X(0), + Y(0), + Attrib(NORMAL), + Page(0), + SubX(0), + SubY(0), + SubW(COLUMNS), + SubH(LINES) +{ + int index; + + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Pan -- Scroll the window right or left. * + * * + * This routine will scroll the window to the right or left as indicated by the number of * + * rows. * + * * + * INPUT: cols -- The number of columns to pan the window. Positive numbers pan to the left * + * while negative numbers pan to the right. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Pan(int cols) +{ + if (cols == 0) return; + + if (abs(cols) >= SubW) { + Clear(); + return; + } + + CellType cell; + cell.Character = ' '; + cell.Attribute = Attrib; + + if (cols > 0) { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX], &Page_Ptr()->Data[index][SubX+cols], sizeof(CellType)*(SubW-cols)); + for (int cc = SubX+SubW-cols; cc < SubX+SubW; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } else { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX-cols], &Page_Ptr()->Data[index][SubX], sizeof(CellType)*(SubW+cols)); + for (int cc = SubX; cc < SubX-cols; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } + Set_Cursor(X-cols, Y); +} + + +/*********************************************************************************************** + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * * + * This routine is used to partition the monochrome screen so that only a sub-window will * + * be processed. By using this, a separate rectangle of the screen can be cleared, * + * scrolled, panned, or printed into. * + * * + * INPUT: x,y -- The upper left corner of the new sub-window. * + * * + * w,h -- Dimensions of the sub-window specified in characters. * + * * + * OUTPUT: none * + * * + * WARNINGS: The parameters are clipped as necessary. * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Sub_Window(int x, int y, int w, int h) +{ + /* + ** Undo any sub window adjustments to the cursor position. + */ + X += SubX; + Y += SubY; + + /* + ** Ensure parameters are legal. + */ + x = min(x, COLUMNS-1); + x = max(x, 0); + y = min(y, LINES-1); + y = max(y, 0); + if (w == -1) w = COLUMNS-x; + if (h == -1) h = LINES-y; + + /* + ** Assign the new sub-region. + */ + SubX = x; + SubY = y; + SubW = w; + SubH = h; + + /* + ** Move the cursor (if necessary) so that it falls within that region. + */ + int xx = X; + int yy = Y; + xx = min(xx, SubX+SubW-1); + xx = max(xx, SubX); + yy = min(yy, SubY+SubH-1); + yy = max(yy, SubY); + Set_Cursor(xx-SubX, yy-SubY); +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, MonoAttribute attrib, BoxStyleType thick) +{ + CellType cell; + MonoAttribute oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + w = min(w, SubW-x); + w = max(w, 1); + h = min(h, SubH-y); + h = max(h, 1); + + x += SubX; + y += SubY; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Page_Ptr()->Data[y][x+xpos+1] = cell; + cell.Character = CharData[thick].BottomEdge; + Page_Ptr()->Data[y+h-1][x+xpos+1] = cell; + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Page_Ptr()->Data[y+ypos+1][x] = cell; + cell.Character = CharData[thick].RightEdge; + Page_Ptr()->Data[y+ypos+1][x+w-1] = cell; + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Page_Ptr()->Data[y][x] = cell; + cell.Character = CharData[thick].UpperRight; + Page_Ptr()->Data[y][x+w-1] = cell; + cell.Character = CharData[thick].BottomRight; + Page_Ptr()->Data[y+h-1][x+w-1] = cell; + cell.Character = CharData[thick].BottomLeft; + Page_Ptr()->Data[y+h-1][x] = cell; + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + + X = x; + Y = y; + + if (!Enabled) return; + + /* + ** Update the visible cursor position only if the this mono page is the currently + ** visible one. + */ + int pos = ((y+SubY)*COLUMNS)+(x+SubX); + if (Page == 0) { + output(CONTROL_PORT, (short)(0x0E|(pos&0xFF00))); + output(CONTROL_PORT, (short)(0x0F|(pos<<8))); + } +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + if (!Enabled) return; + + Set_Cursor(0, 0); + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int rows = 0; rows < SubH; rows++) { + for (int cols = 0; cols < SubW; cols++) { + Page_Ptr()->Data[rows+SubX][cols+SubY] = cell; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * * + * This routine will give the specified attribute to the characters within the block * + * but will not change the characters themselves. You can use this routine to change the * + * underline, blink, or inverse characteristics of text. * + * * + * INPUT: x,y -- The upper left coordinate of the region to change. * + * * + * w,h -- The dimensions of the region to change (in characters). * + * * + * attrib -- The attribute to fill into the region specified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib) +{ + if (!w || !h || (unsigned)x >= SubW || (unsigned)h >= SubH || (unsigned)x+w > SubW || (unsigned)y+h > SubH) return; + + for (int rows = y; rows < y+h; rows++) { + for (int cols = x; cols < x+w; cols++) { + Page_Ptr()->Data[rows+SubY][cols+SubX].Attribute = attrib; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + if (!Enabled || lines <= 0) return; + + if (abs(lines) >= SubH) { + Clear(); + return; + } + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + if (lines > 0) { + for (int row = 0; row < SubH-lines; row++) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row+1][SubX], SubW*sizeof(CellType)); + } + for (int frow = SubH-lines; frow < SubH; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } else { + for (int row = SubH-1; row >= -lines; row--) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row-1][SubX], SubW*sizeof(CellType)); + } + for (int frow = 0; frow < -lines; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } + + Set_Cursor(X, Y-lines); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints formatted text using text string number. * + * * + * This routine will take the given text string number and print the formatted text to * + * the monochrome screen. * + * * + * INPUT: text -- The text number to convert into real text (by way of external function). * + * * + * ... -- Additional parameters as needed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(int text, ...) +{ + va_list va; + + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const * ptr) +{ + int startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; + while (*text) { + + cell.Character = *text; + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (cell.Character) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(startcol, Y+1); + break; + + /* + ** The TAB character is not currently handled. Convert it to + ** a space instead. + */ + case '\t': + cell.Character = ' '; + // fall into normal print case. + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge it is moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + Page_Ptr()->Data[SubY+Y][SubX+X] = cell; + + if (X < SubW-1) { + Set_Cursor(X+1, Y); + break; + } + // Fall into newline case. + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(0, Y+1); + break; + } + text++; + } +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Simple text printing from text number. * + * * + * This will print the text (represented by the text number) to the location on the * + * monochrome screen specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * x,y -- The coordinates to begin the printing at. * + * * + * attrib-- The character attribute to use while printing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(int text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + if (text != 0) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + + +/*********************************************************************************************** + * MonoClass::Print -- Simple print of text number. * + * * + * Prints text represented by the text number specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memmove(Page_Ptr(), src.Page_Ptr(), sizeof(MonoPageType)); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + MonoClass * displace = Get_Current(); + if (displace) { + for (int line = 0; line < LINES; line++) { + for (int col = 0; col < COLUMNS; col++) { + CellType temp = Page_Ptr()->Data[line][col]; + Page_Ptr()->Data[line][col] = Raw_Ptr(0)->Data[line][col]; + Raw_Ptr(0)->Data[line][col] = temp; + } + } + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memmove(Raw_Ptr(0), Page_Ptr(), sizeof(MonoPageType)); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + + +/*********************************************************************************************** + * Mono_Printf -- Prints formatted text to visible page. * + * * + * This routine will print formatted text (with parameters) to the visible monochrome page * + * at whatever the current cursor location is. * + * * + * INPUT: string -- The string to use as the main text and formatting control string. * + * * + * ... -- Any additional parameters required by the formatting string. * + * * + * OUTPUT: Returns with the number of characters written to the display. * + * * + * WARNINGS: The total size of the formatted text must not exceed 256 characters. * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Printf(char const * string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +/*********************************************************************************************** + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * * + * This routine will clear the currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + + +/*********************************************************************************************** + * Mono_Text_Print -- Prints text to location specified. * + * * + * This routine will print the specified text to the location indicated. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * x,y -- The coordinate to print the text at. * + * * + * attrib-- The attribute to use when printing the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (MonoClass::MonoAttribute)attrib); + } +} + + +/*********************************************************************************************** + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * * + * Use this routine to draw a rectangle to the monochrome screen. The dimensions, attribute,* + * and line style are controlled by parameters. * + * * + * INPUT: x,y -- Coordinate of upper left corner of the box to draw. * + * * + * w,h -- The width and height of the box to draw. * + * * + * attrib-- The attribute to use when drawing the box. * + * * + * thick -- The line drawing style to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (MonoClass::MonoAttribute)attrib, (MonoClass::BoxStyleType)thick); + } +} + + +/*********************************************************************************************** + * Mono_Print -- Prints simple text to monochrome screen. * + * * + * This is the non-formatting print to the monochrome screen. * + * * + * INPUT: text -- Pointer to the text that will be printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + + +/*********************************************************************************************** + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * * + * Use this routine to get the current cursor X position. This only applies to the * + * currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with X position of cursor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +/*********************************************************************************************** + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * * + * This routine will fetch the current Y cursor position for the monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Y position of the monochrome page. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + diff --git a/CODE/MONOC.H b/CODE/MONOC.H new file mode 100644 index 0000000..78c2e2e --- /dev/null +++ b/CODE/MONOC.H @@ -0,0 +1,240 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MONOC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_Hx +#define MONOC_Hx + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class MonoClass { + public: + enum MonoClassPageEnums { + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + MAX_MONO_PAGES=16 // Maximum RAM pages on mono card. + }; + + typedef enum MonoAttribute { + INVISIBLE=0x00, // Black on black. + UNDERLINE=0x01, // Underline. + BLINKING=0x90, // Blinking white on black. + NORMAL=0x02, // White on black. + INVERSE=0x70, // Black on white. + } MonoAttribute; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = true;}; + static void Disable(void) {Enabled = false;}; + static bool Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Sub_Window(int x=0, int y=0, int w=-1, int h=-1); + void Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib); + void Draw_Box(int x, int y, int w, int h, MonoAttribute attrib=NORMAL, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(MonoAttribute attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, MonoAttribute attrib=NORMAL); + void Text_Print(int text, int x, int y, MonoAttribute attrib=NORMAL); + void View(void); + void Scroll(int lines=1); + void Pan(int cols=1); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + int Get_Width(void) const {return(SubW);}; + int Get_Height(void) const {return(SubH);}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + + /* + ** Cursor coordinate (relative to sub-window). + */ + int X; + int Y; + + /* + ** Default attribute to use when printing text. + */ + MonoAttribute Attrib; + + /* + ** The current physical page that this mono class object refers to. + */ + int Page; + + /* + ** Sub window coordinates. + */ + int SubX; + int SubY; + int SubW; + int SubH; + + /* + ** Pointer to the monochrome RAM. + */ +// static MonoPageType * MonoRAM; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + struct BoxDataType { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + }; + static BoxDataType const CharData[4]; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + struct CellType { + unsigned char Character; // Character to display. + unsigned char Attribute; // Attribute. + }; + + struct MonoPageType { + CellType Data[LINES][COLUMNS]; + }; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + SIZE_OF_PAGE=(int)LINES*(int)COLUMNS*sizeof(CellType) // Entire page size. + }; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** Fetches pointers to the appropriate mono RAM. + */ + MonoPageType * Raw_Ptr(int page) const { + return &((MonoPageType *)0xB0000)[page]; + } + MonoPageType * Page_Ptr(void) const { + return(Raw_Ptr(Page)); + } + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static bool Enabled; +}; + +#ifndef WIN32 +int Mono_Printf(int string, ...); +#else +extern void Mono_Set_Cursor(int x, int y); +extern int Mono_Printf(int string, ...); +extern int Mono_Printf(char const * string, ...); +extern void Mono_Clear_Screen(void); +extern void Mono_Text_Print(void const *text, int x, int y, int attrib); +extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +extern void Mono_Print(void const *text); +extern int Mono_X(void); +extern int Mono_Y(void); +#endif + +#endif + diff --git a/CODE/MOUSE.CPP b/CODE/MOUSE.CPP new file mode 100644 index 0000000..90ea0ad --- /dev/null +++ b/CODE/MOUSE.CPP @@ -0,0 +1,392 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MOUSE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MouseClass::AI -- Process player input as it relates to the mouse * + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This points to the loaded mouse shapes. +*/ +void const * MouseClass::MouseShapes; + +/* +** This is the timer that controls the mouse animation. It is always at a fixed +** rate so it uses the constant system timer. +*/ +CDTimerClass MouseClass::Timer = 0; + + +/*********************************************************************************************** + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * * + * This is the default constructor for the mouse handling class. It merely sets up the * + * mouse system to its default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +MouseClass::MouseClass(void) : + IsSmall(false), + CurrentMouseShape(MOUSE_NORMAL), + NormalMouseShape(MOUSE_NORMAL), + Frame(0) +{ +} + + +/*********************************************************************************************** + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * * + * This routine is used to inform the display system as to which mouse shape is desired. * + * * + * INPUT: mouse -- The mouse shape number to set the mouse to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Set_Default_Mouse(MouseType mouse, bool size) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + NormalMouseShape = mouse; + Override_Mouse_Shape(mouse, size); +} + + +/*********************************************************************************************** + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * * + * Use this routine to cancel the effects of Override_Mouse_Shape(). It will revert the * + * mouse back to the original shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/27/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Revert_Mouse_Shape(void) +{ + Override_Mouse_Shape(NormalMouseShape, false); +} + + +/*********************************************************************************************** + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * * + * This routine is called to change the mouse sizing override. If the mouse can change * + * size to that specified, then the mouse imagery will be changed. If a change of imagery * + * cannot occur (due to lack of appropriate artwork), then no action will be performed. * + * * + * INPUT: small -- Should the mouse be made small? If not, then it will be made large. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Mouse_Small(bool wsmall) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (IsSmall == wsmall) { + return; + } + + IsSmall = wsmall; + + if (wsmall) { + if (control->SmallFrame != -1) { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->SmallFrame + Frame/4)); + } else { + Set_Mouse_Cursor(MouseControl[MOUSE_NORMAL].X, MouseControl[MOUSE_NORMAL].Y, Extract_Shape(MouseShapes, MOUSE_NORMAL)); + } + } else { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->StartFrame + Frame/4)); + } +} + + +/*********************************************************************************************** + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * * + * This routine is used to alter the shape of the mouse as needed. * + * Typical mouse shape change occurs when scrolling the map or * + * selecting targets. * + * * + * INPUT: mouse -- The mouse shape number to use. * + * * + * OUTPUT: bool; Was the mouse shape changed? * + * * + * WARNINGS: This is not intended to be used as a means to hide the * + * mouse. Nor will it work correctly if the mouse shape * + * file hasn't been loaded. * + * * + * HISTORY: * + * 03/10/1994 JLB : Created. * + * 06/03/1994 JLB : Made into member function. * + * 12/24/1994 JLB : Added small control parameter. * + *=============================================================================================*/ +#ifdef WIN32 +void Block_Mouse(GraphicBufferClass *buffer); +void Unblock_Mouse(GraphicBufferClass *buffer); +#endif + +bool MouseClass::Override_Mouse_Shape(MouseType mouse, bool wsmall) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + MouseStruct const * control = &MouseControl[mouse]; + static bool startup = false; + int baseshp; + + /* + ** Only certain mouse shapes have a small counterpart. If the requested mouse + ** shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wsmall = false; + } + + /* + ** If the mouse shape is going to change, then inform the mouse driver of the + ** change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wsmall != IsSmall)))) { + startup = true; + + Timer = control->FrameRate; + Frame = 0; + + baseshp = (wsmall) ? control->SmallFrame : control->StartFrame; + if (baseshp == -1) { + baseshp = control->StartFrame; + } + + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseshp)); + CurrentMouseShape = mouse; + IsSmall = wsmall; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MouseClass::AI -- Process player input as it relates to the mouse * + * * + * This routine will is to be called once per game tick and is passed the player keyboard * + * or mouse input code. It processes this code and updates the mouse shape as appropriate. * + * * + * INPUT: input -- The player input code as returned from Keyboard->Get(). * + * * + * x,y -- The mouse coordinate values to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 03/27/1995 JLB : New animation control. * + * 05/28/1995 JLB : Moderates animation so is more steady regardless of speed. * + * 06/30/1995 JLB : Uses constant timer system. * + *=============================================================================================*/ +void MouseClass::AI(KeyNumType &input, int x, int y) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (control->FrameRate && Timer == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer = control->FrameRate; + + if (!IsSmall || control->SmallFrame != -1) { + int baseframe = (IsSmall) ? control->SmallFrame : control->StartFrame; + if (baseframe == -1) baseframe = control->StartFrame; + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseframe + Frame)); + } + } + + ScrollClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * * + * Use this routine to load the mouse data file and perform any other necessary one time * + * preparations for the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine ONCE. * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::One_Time(void) +{ + ScrollClass::One_Time(); + + /* + ** Override the mouse shape file with the one in the current directory, but only if there + ** is an override file available. + */ + #ifndef NDEBUG + RawFileClass file("MOUSE.SHP"); + if (file.Is_Available()) { + MouseShapes = Load_Alloc_Data(file); + } else { + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + } + #else + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + #endif +} + + +/*********************************************************************************************** + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * * + * This routine will reset the mouse handling system. Typically, this routine is called * + * when preparing for the beginning of a new scenario. * + * * + * INPUT: theater -- The theater that the scenario will take place. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Init_Clear(void) +{ + ScrollClass::Init_Clear(); + IsSmall = false; + NormalMouseShape = MOUSE_NORMAL; +} + + +/* +** This array of structures is used to control the mouse animation +** sequences. +*/ +//#ifdef WIN32 +//#define WD 45 +//#define HT 36 +//#else +#define WD 29 +#define HT 23 +//#endif + +MouseClass::MouseStruct MouseClass::MouseControl[MOUSE_COUNT] = { + {0, 1, 0, 80, 0, 0}, // MOUSE_NORMAL + {1, 1, 0, -1, WD/2, 0}, // MOUSE_N + {2, 1, 0, -1, WD, 0}, // MOUSE_NE + {3, 1, 0, -1, WD, HT/2}, // MOUSE_E + {4, 1, 0, -1, WD, HT}, // MOUSE_SE + {5, 1, 0, -1, WD/2, HT}, // MOUSE_S + {6, 1, 0, -1, 0, HT}, // MOUSE_SW + {7, 1, 0, -1, 0, HT/2}, // MOUSE_W + {8, 1, 0, -1, 0, 0}, // MOUSE_NW + + {124, 1, 0, -1, WD/2, 0}, // MOUSE_NO_N + {125, 1, 0, -1, WD, 0}, // MOUSE_NO_NE + {126, 1, 0, -1, WD, HT/2}, // MOUSE_NO_E + {127, 1, 0, -1, WD, HT}, // MOUSE_NO_SE + {128, 1, 0, -1, WD/2, HT}, // MOUSE_NO_S + {129, 1, 0, -1, 0, HT}, // MOUSE_NO_SW + {130, 1, 0, -1, 0, HT/2}, // MOUSE_NO_W + {131, 1, 0, -1, 0, 0}, // MOUSE_NO_NW + + {14, 1, 0, 33, WD/2, HT/2}, // MOUSE_NO_MOVE + {10, 4, 4, 29, WD/2, HT/2}, // MOUSE_CAN_MOVE + {113, 3, 4, 142, WD/2, HT/2}, // MOUSE_ENTER + {59, 9, 4, -1, WD/2, HT/2}, // MOUSE_DEPLOY + {15, 6, 4, -1, WD/2, HT/2}, // MOUSE_CAN_SELECT + {21, 8, 4, 134, WD/2, HT/2}, // MOUSE_CAN_ATTACK + {68, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_BACK + {148, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_UNIT + {35, 24, 2, -1, WD/2, HT/2}, // MOUSE_REPAIR + {120, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + {119, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_SELL_BACK + {81, 1, 0, 145, WD/2, HT/2}, // MOUSE_RADAR_CURSOR + {90, 7, 4, -1, WD/2, HT/2}, // MOUSE_NUCLEAR_BOMB + {82, 8, 2, 213, WD/2, HT/2}, // MOUSE_AIR_STRIKE + {116, 3, 4, 121, WD/2, HT/2}, // MOUSE_DEMOLITIONS + {147, 1, 0, 146, WD/2, HT/2}, // MOUSE_AREA_GUARD + {160, 4, 4, 194, WD/2, HT/2}, // MOUSE_HEAL + {164, 3, 4, 167, WD/2, HT/2}, // MOUSE_DAMAGE + {170, 24, 2, -1, WD/2, HT/2}, // MOUSE_GREPAIR + {195, 8, 4, 203, WD/2, HT/2}, // MOUSE_STAY_ATTACK + {211, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_DEPLOY + {212, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_ENTER + {213, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + + {97, 8, 3, -1, WD/2, HT/2}, // MOUSE_CHRONO_SELECT + {105, 8, 2, -1, WD/2, HT/2}, // MOUSE_CHRONO_DEST + +}; diff --git a/CODE/MOUSE.H b/CODE/MOUSE.H new file mode 100644 index 0000000..26a000b --- /dev/null +++ b/CODE/MOUSE.H @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MOUSE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "stage.h" +#include "scroll.h" + +class MouseClass: public ScrollClass +{ + public: + MouseClass(void); + MouseClass(NoInitClass const & x) : ScrollClass(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall=false); + virtual void Revert_Mouse_Shape(void); + virtual MouseType Get_Mouse_Shape(void) const {return NormalMouseShape;}; + virtual void Mouse_Small(bool wsmall); + + /* + ** File I/O. + */ + virtual bool Load(Straw & file); + virtual bool Save(Pipe & file) const; + + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall = false); + + /* + ** This allows the tactical map input gadget access to change the + ** mouse shapes. + */ + friend class TacticalClass; + + /* + ** This points to the loaded mouse shapes. + */ + static void const * MouseShapes; + + private: + + /* + ** This type is used to control the frames and rates of the mouse + ** pointer. Some mouse pointers are actually looping animations. + */ + typedef struct MouseStruct + { + int StartFrame; // Starting frame number. + int FrameCount; // Number of animation frames. + int FrameRate; // Frame delay between changing frames. + int SmallFrame; // Start frame number for small version (if any). + int X,Y; // Hotspot X and Y offset. + } MouseStruct; + + /* + ** The control frames and rates for the various mouse pointers are stored + ** in this static array. + */ + static MouseStruct MouseControl[MOUSE_COUNT]; + + public: + /* + ** If the small representation of the mouse is active, then this flag is true. + */ + unsigned IsSmall:1; + + + private: + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseType CurrentMouseShape; + MouseType NormalMouseShape; + + /* + ** For animating mouse shapes, this controls the frame and animation rate. + */ + static CDTimerClass Timer; + int Frame; +}; + + +#endif diff --git a/CODE/MOVIE.H b/CODE/MOVIE.H new file mode 100644 index 0000000..857c8b7 --- /dev/null +++ b/CODE/MOVIE.H @@ -0,0 +1,73 @@ +/* +** 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 . +*/ + +#ifndef _MPGMOVIE_H_ +#define _MPGMOVIE_H_ +/**************************************************************************** +* +* FILE +* MpgMovie.h +* +* DESCRIPTION +* Movie playback using DirectShow Multimedia streaming and DirectDraw +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 27, 1998 +* +****************************************************************************/ + +#include +#include + +#ifdef MPGEXPORT +#define DLLCALL __declspec(dllexport) +#else +#define DLLCALL __declspec(dllimport) +#endif + +typedef enum + { + MPGCMD_ERROR = -1, + MPGCMD_INIT = 0, + MPGCMD_CLEANUP, + MPGCMD_PALETTE, + MPGCMD_UPDATE + } MPG_CMD; + +typedef enum + { + MPGRES_QUIT = -1, + MPGRES_CONTINUE = 0, + MPGRES_LOSTFOCUS, + } MPG_RESPONSE; + +typedef MPG_RESPONSE (far __stdcall *LPMPGCALLBACK)(MPG_CMD cmd, LPVOID data, LPVOID user); + +extern "C" + { + DLLCALL void __stdcall MpgPlay(const char* name, IDirectDraw* dd, + IDirectDrawSurface* surface, RECT* dstRect); + DLLCALL void __stdcall MpgPause(void); + DLLCALL void __stdcall MpgResume(void); + DLLCALL void __stdcall MpgSetCallback(LPMPGCALLBACK callback, LPVOID user); + } + +#endif // _MPGMOVIE_H_ diff --git a/CODE/MP.CPP b/CODE/MP.CPP new file mode 100644 index 0000000..11c3534 --- /dev/null +++ b/CODE/MP.CPP @@ -0,0 +1,2732 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * memrev -- Reverse the byte order of the buffer specified. * + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * XMP_Add -- Add two MP numbers with a carry option. * + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * XMP_Compare -- Compare one MP number with another. * + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * XMP_Dec -- Decrement the MP number by one. * + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * XMP_Double_Mul -- Double precision MP multiply. * + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * XMP_Inc -- Increment an MP number by one. * + * XMP_Init -- Initialize an MP number to a starting value. * + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * XMP_Move -- Assign one MP number to another. * + * XMP_Neg -- Negate the specified MP number. * + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * XMP_Randomize -- Generate a random MP number. * + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include "mp.h" + + +#ifndef __BORLANDC__ +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define _USERENTRY +#endif + + +/*********************************************************************************************** + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * * + * This utility routine will determine the number of precision bytes exist in the long * + * integer specified. There are some optimizations that can occur if the byte precision * + * is known. * + * * + * INPUT: value -- The value of the long integer that the byte precision will be calculated * + * for. * + * * + * OUTPUT: Returns with the number of bytes that the long integer requires (at a minimum) * + * to cover the precision of the integer. The minimum value will be 1, the maximum * + * will be 4. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +static int _Byte_Precision(unsigned long value) +{ + int byte_count; + for (byte_count = sizeof(value); byte_count; byte_count--) { + if (value >> ((byte_count-1)*8)) break; + } + return(byte_count); +} + + +/*********************************************************************************************** + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * * + * This routine will output the length of the block using Distinguished Encoding Rules. * + * The rest of the block must be filled in as appropriate. For data blocks that are less * + * than 128 bytes long, the header consists of only one byte. Longer buffers lengths * + * can consume up to 5 bytes (depends on magnitude of the length value). * + * * + * INPUT: length -- The length of the data block to be output. * + * * + * output -- Pointer to the memory block that will be set up. * + * * + * OUTPUT: Returns with the number of bytes (header) that was used to store the length * + * value. Subsequent data must be placed after the header. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output) +{ + assert(output != NULL); + + int header_length = 0; + + if (length <= SCHAR_MAX) { + output[header_length++] = (unsigned char)length; + } else { + output[header_length++] = (unsigned char)(_Byte_Precision(length) | 0x80); + for (int byte_counter = _Byte_Precision(length); byte_counter; --byte_counter) { + output[header_length++] = (unsigned char)(length >> ((byte_counter-1)*8)); + } + } + return(header_length); +} + + +/*********************************************************************************************** + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * * + * This routine is used to encode a number into a buffer using Distinguished Encoding * + * Rules. The number of bytes used will be, typically, two bytes more than the number of * + * precision bytes in the number. * + * * + * INPUT: from -- Pointer to the multi-precision number. * + * * + * output -- Pointer to the buffer that will hold the DER encoded number. * + * * + * precision-- The precision of the multi-precision number. * + * * + * OUTPUT: Returns with the number of bytes used in the output buffer. * + * * + * WARNINGS: Make sure the buffer is big enough to hold the DER encoded number. For safety * + * make sure it is precision+6 bytes long. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision) +{ + assert(from != NULL); + assert(output != NULL); + assert(precision > 0); + + unsigned char buffer[MAX_UNIT_PRECISION*sizeof(digit)+1]; + int header_count = 0; + + unsigned number_count = XMP_Encode(buffer, from, precision); + + output[header_count++] = 0x02; + header_count += XMP_DER_Length_Encode(number_count, &output[header_count]); + memcpy(&output[header_count], buffer, number_count); + + return(header_count+number_count); +} + + +/*********************************************************************************************** + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * * + * Use this routine to decode a Distinguished Encoding Rules number into a multi-precision * + * number. This is the counterpart function to the XMP_DER_Encode() function. * + * * + * INPUT: result -- The buffer the hold the result MP number. * + * * + * input -- Pointer to the DER encoded number. * + * * + * precision -- The precision of the MP number. This is the maximum precision the * + * DER number can be. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision) +{ + assert(result != NULL); + assert(input != NULL); + assert(precision > 0); + + if (*input++ == 0x02) { + unsigned byte_count; + + if ((*input & 0x80) == 0) { + byte_count = *input++; + } else { + int length = *input++ & 0x7f; + if (length > 2) return; + byte_count = *input++; + if (length > 1) byte_count = (byte_count << 8) | *input++; + } + if (byte_count <= (precision * sizeof(digit))) { + XMP_Signed_Decode(result, input, byte_count, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer. * + * * + * This routine will encode an multi-precision number into a buffer of specified length. * + * The number of stored in "big endian" format with appropriate sign extension. * + * * + * INPUT: to -- Pointer to the buffer to place the number. * + * * + * tobytes -- The number of bytes to use in the destination buffer. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes placed into the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(tobytes > 0); + assert(precision > 0); + + unsigned frombytes = precision * sizeof(digit); + unsigned char filler = (unsigned char)(XMP_Is_Negative(from, precision) ? 0xff : 0); + + int index; + for (index = 0; index < (int)(tobytes-frombytes); index++) { + *to++ = filler; + } + + const unsigned char * fptr = ((const unsigned char *)from) + min(tobytes, frombytes); + for (index = 0; index < (int)min(tobytes, frombytes); index++) { + *to++ = *--fptr; + } + + return(tobytes); +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * * + * This routine will encode the MP number into the specified buffer. The number will be * + * encoded using the least number of bytes possible. * + * * + * INPUT: to -- The buffer to encode the MP number into. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes used in the destination buffer to hold the * + * encoded number. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the encoded MP number. * + * A safe size would be the precision plus one. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +#pragma warning 364 9 +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(precision > 0); + + bool is_negative = XMP_Is_Negative(from, precision); + unsigned char filler = (unsigned char)(is_negative ? 0xff : 0); + unsigned char * number_ptr; + + unsigned char * const end = (unsigned char *)from; + for (number_ptr = (unsigned char *)end + precision - 1; number_ptr > (unsigned char *)end; number_ptr--) { + if (*number_ptr != filler) break; + } + + unsigned index = 0; + if (((*number_ptr & 0x80) && !is_negative) || (!(*number_ptr & 0x80) && is_negative)) { + to[index++] = filler; + } + + to[index++] = *number_ptr; + + while (number_ptr != end) { + to[index++] = *--number_ptr; + } + return(index); +} + + +/*********************************************************************************************** + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * * + * Use this routine to convert a coded number back into an MP number. The coded number * + * is presumed to be signed. * + * * + * INPUT: result -- Pointer to the buffer that will hold the decoded MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes consumed by the encoded MP number. * + * * + * precision -- The precision of the MP number (maximum) of the result. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the precision is sufficient to hold the decoded MP number. * + * Otherwise, the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + unsigned char filler = (unsigned char)((*from & 0x80) ? 0xff : 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = filler; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * * + * Use this routine to decode a MP number and treat it as if it were unsigned. * + * * + * INPUT: result -- Pointer to the buffer to hold the result MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes in the encoded number. * + * * + * precision-- The precision of the result MP number -- maximum precision. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the result MP precision is sufficient to hold the decoded number or * + * else the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = '\0'; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * * + * This routine will return with the precision of the MP number expressed as bytes. The * + * MP number is presumed unsigned. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The precision of the MP number to examine. * + * * + * OUTPUT: Returns with the minimum number of bytes consumed by this MP number. * + * * + * WARNINGS: Passing a signed MP number to this routine will return an artificially greater * + * precision than it really is. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Significance(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + number += precision; + do { + if (*(--number)) break; + } while (--precision); + return(precision); +} + + +/*********************************************************************************************** + * XMP_Inc -- Increment an MP number by one. * + * * + * This will increment the MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to increment. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps around the maximum precision, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inc(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + if (++(*number)) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Dec -- Decrement the MP number by one. * + * * + * Use this routine to decrement the specified MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to decrement. * + * * + * precision-- The precision of the MP number to decrement. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps below zero, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Dec(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + *number -= 1; + if ((*number) != ~(digit)0) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Neg -- Negate the specified MP number. * + * * + * This routine will negate (reverse sign) of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to negate. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Neg(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + XMP_Not(number, precision); + XMP_Inc(number, precision); +} + + +/*********************************************************************************************** + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * * + * This will perform the absolute value function on the specified MP number. That is, if * + * the MP number is negative, it will be transformed into a positive number. If the number * + * is already positive, then it will be left alone. * + * * + * INPUT: number -- Pointer to the MP number to ABS. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Abs(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + if (XMP_Is_Negative(number, precision)) { + XMP_Neg(number, precision); + } +} + + +/*********************************************************************************************** + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * * + * Use this routine to perform a right shift of the MP number by the number of bits * + * specified. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift upon. * + * * + * bits -- The number of bits to shift. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is an unsigned shift. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Right_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + number += precision; + digit carry = 0; + while (precision--) { + number--; + digit temp = *number; + *number = carry; + carry = temp; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + number += precision; + digit carry = 0; + digit bitmask = (1L << bits) - 1; + int unbits = UNITSIZE - bits; + + while (precision--) { + number--; + digit temp = *number & bitmask; + *number >>= bits; + *number |= carry << unbits; + carry = temp; + } + return; + } + + /* + ** General purpose slow right. + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number + digits_to_shift) >> bits_to_shift) | (*(number + (digits_to_shift + 1)) << (UNITSIZE - bits_to_shift)); + number++; + } + + if (digits_to_shift < precision) { + *number = (*(number + digits_to_shift) >> bits_to_shift); + number++; + } + + for (index= 0; index < min(digits_to_shift, precision); index++) { + *number++ = 0; + } +} + + +/*********************************************************************************************** + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * * + * Use this routine to perform a left shift of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift operation on. * + * * + * bits -- The number of bits to shift the MP number leftward. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Left_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + digit carry = 0; + while (precision--) { + digit temp = *number; + *number = carry; + carry = temp; + number++; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + digit carry = 0; + digit bitmask = ~(((digit)-1) >> bits); + int unbits = UNITSIZE - bits; /* shift bits must be <= UNITSIZE */ + + while (precision--) { + digit temp = *number & bitmask; + *number = (*number << bits) | (carry >> unbits); + carry = temp; + number++; + } + return; + } + + /* + ** General purpose slow left; + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + number += precision-1; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number - digits_to_shift) << bits_to_shift) | (*(number - (digits_to_shift + 1)) >> (UNITSIZE - bits_to_shift)); + number--; + } + + if (digits_to_shift < precision) { + *number = (*(number - digits_to_shift) << bits_to_shift); + number--; + } + + for (index = 0; index < min(digits_to_shift, precision); index++) { + *number-- = 0; + } +} + + +/*********************************************************************************************** + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * * + * This routine will rotate the MP number to the left by one bit. The rotation passes bits * + * through a "carry" bit position. The initial value of this "carry" bit is passed to the * + * routine and the final value is returned as the result. * + * * + * INPUT: number -- Pointer to the MP number to perform the left rotate upon. * + * * + * carry -- The initial value of the "carry" bit. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: Returns with the final value of the carry bit. This is the the bit value of the * + * upper most bit of the MP number prior to the rotate operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rotate_Left(digit * number, bool carry, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + while (precision--) { + bool temp = ((*number & UPPER_MOST_BIT) != 0); + *number = (*number << 1); + if (carry) *number = *number + 1; + carry = temp; + number++; + } + return carry; +} + + +/*********************************************************************************************** + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * * + * Perform a bitwise NOT operation. * + * * + * INPUT: number -- Pointer to the MP number to operate on. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Not(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + for (int index = 0; index < precision; index++) { + *number = ~(*number); + number++; + } +} + + +/*********************************************************************************************** + * XMP_Init -- Initialize an MP number to a starting value. * + * * + * This will initialize (assign) a number to an MP number. The initial value is limited * + * to the precision allowed by a DIGIT type. * + * * + * INPUT: number -- Pointer to the MP number to initialize. * + * * + * value -- Initial integer value to assign to the MP number. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Init(digit * number, digit value, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + memset(number, '\0', precision * sizeof(digit)); + *number = value; +} + + +/*********************************************************************************************** + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * * + * This routine will count the maximum number of bits used by this MP number. The result * + * could be referred to as the "bit precision" of the MP number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The (digit) precision of the MP number. * + * * + * OUTPUT: Returns with the number of significant bits in the MP number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Count_Bits(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + int sub_precision = XMP_Significance(number, precision); + if (!sub_precision) return(0); + int total_bit_count = XMP_Digits_To_Bits(sub_precision); + number += sub_precision-1; + digit high_bit_mask = UPPER_MOST_BIT; + + while (!((*number) & high_bit_mask)) { + high_bit_mask >>= 1; + total_bit_count--; + } + + return(total_bit_count); +} + + +/*********************************************************************************************** + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * * + * This routine will scan the MP number and determine the number of bytes needed to * + * represent the MP number. Consider the result the "byte precision" of the number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- Precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes required to represent the precision of the number.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Count_Bytes(const digit * number, int precision) +{ + unsigned char * ptr = (unsigned char *)number; + int count = 0; + for (unsigned index = 0; index < precision*sizeof(digit); index++) { + if (!*ptr) break; + count++; + ptr++; + } + return(count); +} + + +/*********************************************************************************************** + * XMP_Move -- Assign one MP number to another. * + * * + * This will move one MP number over the top of another. * + * * + * INPUT: dest -- Destination MP number (will get clobbered). * + * * + * source -- Source MP number. * + * * + * precision-- Precision of both MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Both MP numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Move(digit * dest, digit const * source, int precision) +{ + memcpy(dest, source, precision * sizeof(digit)); +} + + +/*********************************************************************************************** + * XMP_Compare -- Compare one MP number with another. * + * * + * This routine will compare two MP numbers. It will return a value indicating which MP * + * number is greater or if they are equal. * + * * + * INPUT: left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns -1 if the left_number is less than the right_number. * + * Returns 1 if the left_number is greater than the right number. * + * Returns 0 if both numbers are identical. * + * * + * WARNINGS: Both numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Compare(const digit * left_number, const digit * right_number, int precision) +{ + left_number += precision-1; + right_number += precision-1; + do { + if (*left_number < *right_number) return -1; + if (*left_number > *right_number) return 1; + left_number--; + right_number--; + } while (--precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Add -- Add two MP numbers with a carry option. * + * * + * Use this routine to add one MP number to another. There is an optional "carry" value * + * that (when true) will add an additional 1 to the result. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. This can be the * + * same value as the left_number or right_number pointers. * + * * + * left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * carry -- Optional carry flag (typically this will be false). * + * * + * precision -- The precision of the numbers involved. * + * * + * OUTPUT: Returns with the carry flag after the addition. If the value is true then an * + * overflow occurred. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add(digit * result, const digit * left_number, const digit * right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + *right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number++; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * * + * This routine will add an integer number to an MP number. There is an optional carry * + * parameter. If the carry flag is true, and additional 1 will be added to the result. * + * This routine is much faster than adding two MP numbers together. * + * * + * INPUT: result -- Pointer to the result MP number. This pointer can be the same as * + * the left_number parameter. * + * * + * left_number -- Pointer to the left hand MP number. * + * * + * right_number-- The integer number to add to the left hand number. * + * * + * carry -- Input carry flag. If this is true, then an additional one will be * + * added to the result. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns with the result carry flag. A true value means the addition overflowed. * + * * + * WARNINGS: All MP numbers must share the same precision. Negative numbers are not * + * supported. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add_Int(digit * result, const digit * left_number, digit right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number = 0; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * * + * This routine will subtract one MP number from another. There is an optional borrow * + * flag that can be specified. * + * * + * INPUT: result -- Pointer to the MP number that will hold the result. This pointer * + * can be the same as the left_number or right_number parameters. * + * * + * left_number -- The left hand number (value will be subtracted from this). * + * * + * right_number-- The right hand number (the value to subtract from the left number) * + * * + * borrow -- The optional borrow flag. If this flag is true, the an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow result flag. If the value is true, then an underflow * + * occurred during subtraction. * + * * + * WARNINGS: All MP numbers must share the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub(digit * result, const digit * left_number, const digit * right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + const unsigned short * right_number_ptr = (const unsigned short *)right_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - (digit) *right_number_ptr - (digit) borrow; + right_number_ptr++; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * * + * This will subtract an integer from the specified MP number. There is an optional borrow * + * flag available. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * left_number -- Pointer to the MP number that will be subtracted FROM. * + * * + * right_number-- The integer to subtract from the left hand number. * + * * + * borrow -- The optional borrow flag. If this value is true, then an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow flag of the result. If this value is true, then an * + * underflow occurred during subtraction. * + * * + * WARNINGS: The precision must be identical between the MP numbers involved. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub_Int(digit * result, const digit * left_number, unsigned short right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - right_number - borrow; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + + right_number = 0; + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * * + * This routine will multiply two MP numbers together. The result will have the sum of the * + * significance of the two. * + * * + * INPUT: prod -- Pointer to the product MP buffer that will hold the result. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product will fit within the precision of the result. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Init(prod, 0, precision); + + /* + ** Multiplying by zero is always a zero product. + */ + if (XMP_Test_Eq_Int(multiplicand, 0, precision) || XMP_Test_Eq_Int(multiplier, 0, precision)) { + return 0; + } + + int total_bit_count = XMP_Count_Bits(multiplier, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + multiplier += sub_precision; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(prod, 1, precision); + + if ((*(multiplier-1)) & high_bit_mask) { + XMP_Add(prod, prod, multiplicand, 0, precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + multiplier--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * * + * This is a very fast multiply since the multiplier is just an integer integral. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that is the multiplicand. * + * * + * multiplier -- The integral integer that is the multiplier. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fit in a signed integer (although it isn't a signed value). * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision) +{ + const unsigned short * m2 = (const unsigned short *)multiplicand; + unsigned short * pr = (unsigned short *)prod; + unsigned long carry = 0; + for (int i = 0; i < precision*2; ++i) { + unsigned long p = (((unsigned long)multiplier) * *m2) + carry;; + *pr = (unsigned short) p; + carry = p >> 16; + m2++; + pr++; + } + + /* Add carry to the next higher word of product / dividend */ +// *pr += (unsigned short)carry; + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * * + * This will multiply the specified integer with the MP number. It is a much faster * + * multiply than when multiplying two MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that serves as the multiplicand. * + * * + * multiplier -- The simple integral integer used as the multiplier. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fist within a signed short integer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + } else { + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * * + * This routine will perform a multiply between two signed MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is not a very fast routine. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, abs_multiplicand, abs_multiplier, precision); + } else { + XMP_Unsigned_Mult(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, multiplicand, abs_multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * * + * This routine performs a fast divide of the specified MP dividend by a simple * + * short integer. The division is an UNSIGNED division however. * + * * + * INPUT: quotient -- Pointer to the MP number buffer where the quotient will go. * + * * + * dividend -- Pointer to the MP number that serves as the dividend. * + * * + * divisor -- The simple signed short integer that serves as the divisor. * + * * + * precision -- The precision that is used by the MP numbers involved. * + * * + * OUTPUT: Returns with the remainder of the division. * + * * + * WARNINGS: This is an UNSIGNED divide even. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision) +{ + if (!divisor) return 0; /* zero divisor means divide error */ + + unsigned short remainder = 0; + + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + digit const * dividend_ptr = dividend + (digit_precision-1); + + if (!digit_precision) return(0); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + digit * quotient_ptr = quotient + (digit_precision-1); + + while (total_bit_count--) { + remainder <<= 1; + + if ((*dividend_ptr) & high_bit_mask) remainder++; + + if (remainder >= divisor) { + remainder -= divisor; + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + --dividend_ptr; + --quotient_ptr; + } + } + + return(remainder); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * * + * This will perform the (dog slow) divide of one MP number into another. Because of the * + * slowness of this routine, both the quotient and the remainder are available as a * + * result of the operation. * + * * + * INPUT: remainder -- Pointer to the MP buffer that will hold the remainder of the divide.* + * * + * quotient -- Pointer to the MP buffer that will hold the quotient of the divide. * + * * + * dividend -- The MP dividend (numerator) number. * + * * + * divisor -- The MP divisor (denominator) number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + // check for divide by zero. + if (XMP_Test_Eq_Int(divisor, 0, precision)) return(-1); + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + if (!digit_precision) return(0); + + digit const * dividend_ptr = dividend + (digit_precision-1); + digit * quotient_ptr = quotient + (digit_precision-1); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + + if (((*dividend_ptr) & high_bit_mask) != 0) { + XMP_Inc(remainder, precision); + } + + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + dividend_ptr--; + quotient_ptr--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * * + * This will perform a signed divide (very very slow) of one MP number into another. * + * Because of the slow nature of this routine, both the quotient and the remainder are * + * available as results. * + * * + * INPUT: remainder -- Pointer to the buffer that will hold the remainder of the divide. * + * * + * quotient -- Pointer to the buffer that will hold the quotient of the divide. * + * * + * dividend -- The dividend (numerator) MP number. * + * * + * divisor -- The divisor (denominator) MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + bool negative = false; + + digit scratch_dividend[MAX_UNIT_PRECISION]; + XMP_Move(scratch_dividend, dividend, precision); + + digit scratch_divisor[MAX_UNIT_PRECISION]; + XMP_Move(scratch_divisor, divisor, precision); + + if (XMP_Is_Negative(scratch_dividend, precision)) { + XMP_Neg(scratch_dividend, precision); + negative = !negative; + } + + if (XMP_Is_Negative(scratch_divisor, precision)) { + XMP_Neg(scratch_divisor, precision); + negative = !negative; + } + + XMP_Unsigned_Div(remainder, quotient, scratch_dividend, scratch_divisor, precision); + + if (negative) { + XMP_Neg(quotient, precision); + if (!XMP_Test_Eq_Int(remainder, 0, precision)) { + XMP_Dec(quotient, precision); + XMP_Neg(remainder, precision); + XMP_Add(remainder, remainder, scratch_divisor, 0, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * * + * This is a utility routine that will perform an inverse on the MP number and then * + * perform a modulus of that number by another MP number. There are some algorithms that * + * require this process. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * number -- The MP number that will be inverted then modulo-ized. * + * * + * modulus -- The MP number to modulus the first number by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision) +{ + digit g[3][MAX_UNIT_PRECISION]; + XMP_Move(g[0], modulus, precision); + XMP_Move(g[1], number, precision); + + digit v[3][MAX_UNIT_PRECISION]; + XMP_Init(v[0], 0, precision); + XMP_Init(v[1], 1, precision); + + digit y[MAX_UNIT_PRECISION]; + + int i; + for (i = 1; !XMP_Test_Eq_Int(g[i%3], 0, precision); i++) { + XMP_Unsigned_Div(g[(i+1)%3], y, g[(i-1)%3], g[i%3], precision); + + XMP_Unsigned_Mult(result, v[i%3], y, precision); + XMP_Sub(v[(i+1)%3], v[(i-1)%3], result, 0, precision); + } + + if (XMP_Is_Negative(v[(i-1)%3], precision)) { + XMP_Add(v[(i-1)%3], v[(i-1)%3], modulus, 0, precision); + } + + XMP_Move(result, v[(i-1)%3], precision); +} + + +/*********************************************************************************************** + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * * + * Use this routine to determine the inverse of the specified MP number. The inverse is * + * defined as 1/number. * + * * + * INPUT: result -- Pointer to the result MP number buffer. * + * * + * number -- The number to be inverted. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision) +{ + digit remainder[MAX_UNIT_PRECISION]; + + if (XMP_Test_Eq_Int(divisor, 0, precision)) return -1; /* zero divisor means divide error */ + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + /* normalize and compute number of bits in quotient first */ + unsigned total_bit_count = XMP_Count_Bits(divisor, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count + 1); /* bitmask within a single digit */ + int sub_precision = XMP_Bits_To_Digits(total_bit_count + 1); + + XMP_Set_Bit(remainder, total_bit_count - 1); + + /* rescale quotient to precision of divisor bits */ + quotient += sub_precision-1; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + quotient--; + } + } + + XMP_Init(remainder, 0, precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * * + * This routine will convert a supplied ASCII string into an MP number. * + * * + * INPUT: str -- Pointer to the ASCII string that will be converted. * + * * + * mpn -- Pointer to the MP number buffer that will be initialized. * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision) +{ + /* + ** Initialize the multiprecision number to zero. From this point + ** onward, this object can be manipulated as a regular number. + ** This is, in fact, what is done as the ascii string is parsed + ** into a working number. + */ + XMP_Init(mpn, 0, precision); + + /* + ** No string or zero length is considered '0'. + */ + if (!str) return; + int i = strlen(str); + if (i == 0) return; + + unsigned short radix; /* base 2-16 */ + switch (toupper(str[i-1])) { /* classify radix select suffix character */ + case '.': + radix = 10; + break; + + case 'H': + radix = 16; + break; + + case 'O': + radix = 8; + break; + + case 'B': /* caution! 'b' is a hex digit! */ + radix = 2; + break; + + default: + radix = 10; + break; + } + + bool minus = (*str == '-'); + if (minus) str++; + + digit c; + while ((c = (unsigned char)*str++) != 0) { + if (c == ',') continue; /* allow commas in number */ + + /* + ** If not a hexadecimal (highest base) digit then it is + ** clearly the end of the processable string. Bail out + ** of the scan loop. + */ + if (!isxdigit((char)c)) break; + + /* + ** Convert the character into an integer number 0 through 15. + */ + if (isdigit((char)c)) { + c -= '0'; + } else { + c = (unsigned char)(toupper((char)c) - 'A') + 10; + } + + /* + ** If the integer digit is greater than the radix, then we + ** know that further processing should stop. This is the + ** end of the number string. + */ + if (c >= radix) break; /* scan terminated by any non-digit */ + + + XMP_Unsigned_Mult_Int(mpn, mpn, radix, precision); + XMP_Add_Int(mpn, mpn, c, 0, precision); + } + if (minus) { + XMP_Neg(mpn, precision); + } +} + + +/*********************************************************************************************** + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * * + * Multiply the single-word multiplier times the multiprecision integer * + * in multiplicand, accumulating result in prod. The resulting * + * multiprecision prod will be 1 word longer than the multiplicand. * + * multiplicand is double precision words long. We add into prod, so caller * + * should zero it out first. For best results, this time-critical * + * function should be implemented in assembly. * + * NOTE: Unlike other functions in the multiprecision arithmetic * + * library, both multiplicand and prod are pointing at the LSB, * + * regardless of byte order of the machine. On an 80x86, this makes * + * no difference. But if this assembly function is implemented * + * on a 680x0, it becomes important. * + * * + * Note that this has been modified from the previous version to allow * + * better support for Smith's modmult: * + * The final carry bit is added to the existing product * + * array, rather than simply stored. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand -- Pointer to the multiplicand MP number. * + * * + * multiplier -- The short integer used as the multiplier. * + * * + * precision -- The precision of the MP number used. * + * * + * OUTPUT: none * + * * + * WARNINGS: The carry (if any) is added into the integer one beyond the end of the * + * product buffer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Hybrid_Mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision) +{ + unsigned long carry = 0; + for (int i = 0; i < precision; ++i) { + unsigned long p = (unsigned long)multiplier * *multiplicand++; + p += *prod + carry; + *prod++ = (unsigned short) p; + carry = p >> 16; + } + + /* Add carry to the next higher word of product / dividend */ + *prod += (unsigned short) carry; +} + + +/*********************************************************************************************** + * XMP_Double_Mul -- Double precision MP multiply. * + * * + * This will perform a double precision multiply of MP numbers. This means that the product * + * will be twice the precision of the components. * + * * + * INPUT: prod -- Pointer to the result buffer. This buffer must be able to hold * + * double the precision specified. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier number. * + * * + * precision -- The precision of the two component MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product buffer can hold a double precision number. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + /* + ** Clear out the double precision product buffer. + */ + XMP_Init(prod, 0, precision*2); + + const unsigned short * multiplier_ptr = (const unsigned short *) multiplier; + unsigned short * product_ptr = (unsigned short *) prod; + + // Multiply multiplicand by each word in multiplier, accumulating prod. + for (int i = 0; i < precision*2; ++i) { + XMP_Hybrid_Mul(product_ptr++, (unsigned short *)multiplicand, *multiplier_ptr++, precision*2); + } +} + + + +static int _modulus_shift; // number of bits for recip scaling +static unsigned short _reciprical_high_digit; // MSdigit of scaled recip +static unsigned short _reciprical_low_digit; // LSdigit of scaled recip + +static int _modulus_sub_precision; // length of modulus in MULTUNITs +static int _modulus_bit_count; // number of modulus significant bits +static digit _scratch_modulus[MAX_UNIT_PRECISION]; // modulus + +// The double precision modulus staging buffer. +static digit _double_staging_number[MAX_UNIT_PRECISION * 2 + 2]; + +// most significant digits of modulus. +static digit _mod_quotient[4]; +static digit _mod_divisor[4]; + + +/*********************************************************************************************** + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * * + * Calculate the reciprocal of modulus with a precision of two MULTUNITs. * + * Assumes that precision has already been adjusted to the * + * size of the modulus, plus SLOP_BITS. * + * * + * Note: This routine was designed to work with large values and * + * doesn't have the necessary testing or handling to work with a * + * modulus having less than three significant digits. For such cases, * + * the separate multiply and modulus routines can be used. * + * * + * INPUT: modulus -- Pointer to the modulus number. * + * * + * precision-- The precision of the modulus number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Prepare_Modulus(const digit * n_modulus, int precision) +{ + XMP_Move(_scratch_modulus, n_modulus, precision); + + _modulus_bit_count = XMP_Count_Bits(_scratch_modulus, precision); + _modulus_sub_precision = (_modulus_bit_count + 16 - 1) / 16; + + /* + ** Keep 2*16 bits in _mod_divisor. + ** This will (normally) result in a reciprocal of 2*16+1 bits. + */ + int sub_precision = XMP_Significance(_scratch_modulus, precision); // significant digits in modulus + XMP_Move(_mod_divisor, &_scratch_modulus[sub_precision-2], 2); + _modulus_shift = XMP_Count_Bits(_mod_divisor, 2) - 2 * 16; + XMP_Shift_Right_Bits(_mod_divisor, _modulus_shift, 2); + + XMP_Reciprocal(_mod_quotient, _mod_divisor, 2); + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + + /* Reduce to: 0 < _modulus_shift <= 16 */ + _modulus_shift = ((_modulus_shift + (16 - 1)) % 16) + 1; + + /* round up */ + XMP_Inc(_mod_quotient, 2); + if (XMP_Count_Bits(_mod_quotient, 2) > 2 * 16) { + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + _modulus_shift--; /* now 0 <= _modulus_shift <= 16 */ + } + unsigned short * mpm = (unsigned short *) _mod_quotient; + _reciprical_low_digit = *mpm++; + _reciprical_high_digit = *mpm; + + return 0; +} + + +/*********************************************************************************************** + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * * + * This routine will combine a multiply and a modulus operation. This takes advantage of * + * a tremendous speed advantage possible if these two processes are combined rather than * + * being performed separately. * + * * + * INPUT: prod -- Pointer to the MP buffer that will hold the result. * + * * + * multiplicand-- The number to multiply. * + * * + * multiplier -- The number to multiply by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The modulus must already have been prepared by the routine XMP_Prepare_Modulus. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Double_Mul(_double_staging_number, multiplicand, multiplier, precision); + + int double_precision = precision * 2 + 1; + + _double_staging_number[double_precision - 1] = 0; /* leading 0 digit */ + + /* + ** We now start working with MULTUNITs. + ** Determine the most significant MULTUNIT of the product so we don't + ** have to process leading zeros in our divide loop. + */ + int dmi = XMP_Significance(_double_staging_number, double_precision) * 2; // number of significant MULTUNITs in product + + if (dmi >= _modulus_sub_precision) { + + /* Make dividend negative. This allows the use of mp_single_mul to + ** "subtract" the product of the modulus and the trial divisor + ** by actually adding to a negative dividend. + ** The one's complement of the dividend is used, since it causes + ** a zero value to be represented as all ones. This facilitates + ** testing the result for possible overflow, since a sign bit + ** indicates that no adjustment is necessary, and we should not + ** attempt to adjust if the result of the addition is zero. + */ + XMP_Inc(_double_staging_number, double_precision); + XMP_Neg(_double_staging_number, double_precision); + + int nqd = dmi + 1 - _modulus_sub_precision; // number of quotient digits remaining to be generated + + /* Set msb, lsb, and normal ptrs of dividend */ + unsigned short * dmph = ((unsigned short *)_double_staging_number) + dmi + 1; // points to one higher than precision would indicate + unsigned short * dmpl = dmph - _modulus_sub_precision; + + /* + ** Divide loop. + ** Each iteration computes the next quotient MULTUNIT digit, then + ** multiplies the divisor (modulus) by the quotient digit and adds + ** it to the one's complement of the dividend (equivalent to + ** subtracting). If the product was greater than the remaining dividend, + ** we get a non-negative result, in which case we subtract off the + ** modulus to get the proper negative remainder. + */ + for (; nqd; nqd--) { + --dmph; + --dmpl; + + unsigned short q = mp_quo_digit(dmph); // trial quotient digit + if (q > 0) { + XMP_Hybrid_Mul(dmpl, (unsigned short *)_scratch_modulus, q, precision*2); + + /* Perform correction if q too large. + ** This rarely occurs. + */ + if (!(*dmph & SEMI_UPPER_MOST_BIT)) { + unsigned short * dmp = dmpl; + if (XMP_Sub((unsigned long *)dmp, (unsigned long *)dmp, _scratch_modulus, false, precision)) { + (*dmph)--; + } + } + } + } + + /* d contains the one's complement of the remainder. */ + XMP_Neg(_double_staging_number, precision); + XMP_Dec(_double_staging_number, precision); + } + + XMP_Move(prod, _double_staging_number, precision); + return (0); +} + + +/*********************************************************************************************** + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * * + * Smith's mp_modmult function leaves some internal arrays in memory, * + * so we have to call modmult_burn() at the end of mp_exponent_mod. * + * This is so that no cryptographically sensitive data is left in memory * + * after the program exits. * + * * + * INPUT: precision -- The precision of the numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Mod_Mult_Clear(int precision) +{ + XMP_Init(_scratch_modulus, 0, precision); + XMP_Init(_double_staging_number, 0, precision); + XMP_Init(_mod_quotient, 0, ARRAY_SIZE(_mod_quotient)); + XMP_Init(_mod_divisor, 0, ARRAY_SIZE(_mod_divisor)); + _modulus_shift = _modulus_bit_count = 0; + _reciprical_high_digit = _reciprical_low_digit = 0; + _modulus_sub_precision = /*mutemp =*/ 0; +} + + +/* +** The function mp_quo_digit is the heart of Smith's modulo reduction, +** which uses a form of long division. It computes a trial quotient +** "digit" (MULTUNIT-sized digit) by multiplying the three most +** significant MULTUNITs of the dividend by the two most significant +** MULTUNITs of the reciprocal of the modulus. Note that this function +** requires that 16 * 2 <= sizeof(unsigned long). +** +** An important part of this technique is that the quotient never be +** too small, although it may occasionally be too large. This was +** done to eliminate the need to check and correct for a remainder +** exceeding the divisor. It is easier to check for a negative +** remainder. The following technique rarely needs correction for +** MULTUNITs of at least 16 bits. +** +** The following routine has two implementations: +** +** Parameter: dividend - points to the most significant MULTUNIT +** of the dividend. Note that dividend actually contains the +** one's complement of the actual dividend value (see comments for +** XMP_Mod_Mult). +** +** Return: the trial quotient digit resulting from dividing the first +** three MULTUNITs at dividend by the upper two MULTUNITs of the +** modulus. +*/ +unsigned short mp_quo_digit(unsigned short * dividend) +{ + unsigned long q, q0, q1, q2; + + /* + * Compute the least significant product group. + * The last terms of q1 and q2 perform upward rounding, which is + * needed to guarantee that the result not be too small. + */ + q1 = (dividend[-2] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit + _reciprical_high_digit; + q2 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit + (1L << 16); + q0 = (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the middle significant product group. */ + q1 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit; + q2 = (dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit; + q = (q0 >> 16) + (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the most significant term and add in the others */ + q = (q >> (16 - 2)) + (((dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit) << 1); + q >>= _modulus_shift; + + /* Prevent overflow and then wipe out the intermediate results. */ + return (unsigned short) min(q, (unsigned long)(1L << 16) - 1); +} + + +/* +** Russian peasant combined exponentiation/modulo algorithm. +** Calls modmult instead of mult. +** Computes: expout = (expin**exponent) mod modulus +** WARNING: All the arguments must be less than the modulus! +*/ +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision) +{ + digit product[MAX_UNIT_PRECISION]; + + XMP_Init(expout, 1, precision); + if (XMP_Test_Eq_Int(exponent_ptr, 0, precision)) { + if (XMP_Test_Eq_Int(expin, 0, precision)) { + return -1; /* 0 to the 0th power means return error */ + } + return 0; /* otherwise, zero exponent means expout is 1 */ + } + + if (XMP_Test_Eq_Int(modulus, 0, precision)) { + return -2; /* zero modulus means error */ + } + + if (XMP_Compare(expin, modulus, precision) >= 0) { + return -3; /* if expin >= modulus, return error */ + } + + if (XMP_Compare(exponent_ptr, modulus, precision) >= 0) { + return -4; /* if exponent >= modulus, return error */ + } + + /* set smallest optimum precision for this modulus */ + int limited_precision = XMP_Significance(modulus, precision); + + if (XMP_Prepare_Modulus(modulus, limited_precision)) { + return -5; /* unstageable modulus (STEWART algorithm) */ + } + + /* normalize and compute number of bits in exponent first */ +// int exp_precision = XMP_Significance(exponent_ptr, limited_precision); +// if (!exp_precision) return(0); +// int bits = XMP_Digits_To_Bits(exp_precision); +// exponent_ptr += (exp_precision-1); +// digit high_bit_mask = UPPER_MOST_BIT; +// while (! ((*exponent_ptr) & high_bit_mask)) { +// high_bit_mask >>= 1; +// bits--; +// } + + int total_bit_count = XMP_Count_Bits(exponent_ptr, limited_precision); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + exponent_ptr += (sub_precision-1); + + /* We can "optimize out" the first modsquare and modmult: */ + total_bit_count--; /* We know for sure at this point that bits>0 */ + + XMP_Move(expout, expin, limited_precision); + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + while (total_bit_count--) { + XMP_Mod_Mult(product, expout, expout, limited_precision); + + if (((*exponent_ptr) & high_bit_mask)) { + XMP_Mod_Mult(expout, product, expin, limited_precision); + } else { + XMP_Move(expout, product, limited_precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + } + + XMP_Init(product, 0, limited_precision); + XMP_Mod_Mult_Clear(limited_precision); /* ask mp_modmult to also burn its own evidence */ + + return 0; +} + + +/*********************************************************************************************** + * memrev -- Reverse the byte order of the buffer specified. * + * * + * This routine will reverse the byte order in the buffer specified. * + * * + * INPUT: buffer -- Pointer to the buffer that will be reversed. * + * * + * length -- The length of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void memrev(char * buffer, size_t length) +{ + char * r2 = &(buffer[length - 1]); + while (buffer < r2) { + char b = *buffer; + *buffer++ = *r2; + *r2-- = b; + } +} + + +int _USERENTRY pfunc(const void * pkey, const void * base) +{ + if (*(unsigned short *)pkey < *(unsigned short *)base) return(-1); + if (*(unsigned short *)pkey > *(unsigned short *)base) return(1); + return(0); +} + + +/*********************************************************************************************** + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * * + * This routine will compare the MP number against all known small prime numbers. It will * + * return true if a match was found. * + * * + * INPUT: candidate -- Pointer to MP number that is to be tested. * + * * + * precision -- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the MP number a member of the small prime community? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Small_Prime(const digit * candidate, int precision) +{ + /* + ** If the number is too large for comparison to the known small primes table, then + ** bail immediately. + */ + if (XMP_Significance(candidate, precision) > 1) return(false); + if (*candidate > primeTable[ARRAY_SIZE(primeTable)-1]) return false; + + unsigned long * ptr = (unsigned long *)bsearch(&candidate, &primeTable[0], ARRAY_SIZE(primeTable), sizeof(primeTable[0]), pfunc); + return(ptr != NULL); +} + + +/*********************************************************************************************** + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * * + * This test for primality will divide an MP number by the set of small primes. If any of * + * these numbers divides evenly into the candidate number, then it is known that the * + * candidate is NOT prime. * + * * + * INPUT: candidate -- Pointer to the MP number that is to be tested. * + * * + * precision -- The precision of the MP number/ * + * * + * OUTPUT: bool; Did the MP number pass the small divisors test? * + * * + * WARNINGS: If the MP number passes, it doesn't mean that it is prime, just that is hasn't * + * yet been proven to be not prime. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Small_Divisors_Test(const digit * candidate, int precision) +{ + digit quotient[MAX_UNIT_PRECISION]; + + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + if (XMP_Unsigned_Div_Int(quotient, candidate, primeTable[i], precision) == 0) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * * + * This is a more expensive but thorough test for primality. The aggressiveness of this * + * test can be controlled by the number of rounds specified. Four rounds is usually * + * sufficient. * + * * + * INPUT: candidate -- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of rounds to test the MP number (keep it small). * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: bool; Was the number not proven to be not prime. A FALSE means that it is not * + * prime. A TRUE means that it might be prime. * + * * + * WARNINGS: This takes a bit of time. The time it takes is directly controlled by the * + * number of rounds specified. Keep the number of rounds as small as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision) +{ + assert(rounds < ARRAY_SIZE(primeTable)); + + digit term[MAX_UNIT_PRECISION]; + XMP_Move(term, candidate_prime, precision); + XMP_Dec(term, precision); + + for (unsigned i = 0; i < rounds; i++) { + // if ((x**(p-1)) mod p) != 1, then p is not prime + digit result[MAX_UNIT_PRECISION]; + + digit small_prime[MAX_UNIT_PRECISION]; + XMP_Init(small_prime, primeTable[i], precision); + + xmp_exponent_mod(result, small_prime, term, candidate_prime, precision); + + if (!XMP_Test_Eq_Int(result, 1, precision)) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * * + * This test for primality is even more expensive the Fermat's Little Theorem. It doesn't * + * prove that a number is prime, but it can prove that it is not prime. * + * * + * INPUT: rng -- Reference to to a random number generator. * + * * + * candidate-- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of test rounds to perform. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? A FALSE means that the number is * + * not prime. A TRUE means that it might be. * + * * + * WARNINGS: This routine takes a long time. Use as few rounds as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision) +{ + digit wminus1[MAX_UNIT_PRECISION]; + XMP_Sub_Int(wminus1, w, 1, 0, precision); + + unsigned maxbitprecision = precision * sizeof(digit) * 8; + unsigned a; + for (a = 0; a < maxbitprecision; a++) { + if (XMP_Test_Bit(wminus1, a)) { + break; + } + } + + digit m[MAX_UNIT_PRECISION]; + XMP_Move(m, wminus1, precision); + XMP_Shift_Right_Bits(wminus1, a, precision); + + for (int i = 0; i < rounds; i++) { + digit b[MAX_UNIT_PRECISION]; + digit temp[MAX_UNIT_PRECISION]; + XMP_Init(temp, 2, precision); + XMP_Randomize(b, rng, temp, wminus1, precision); + + digit z[MAX_UNIT_PRECISION]; + xmp_exponent_mod(z, b, m, w, precision); + + if (XMP_Test_Eq_Int(z, 1, precision) || XMP_Compare(z, wminus1, precision) == 0) { + continue; // passes this round + } + + int j; + for (j = 1; j < a; j++) { + digit t2[MAX_UNIT_PRECISION]; + xmp_exponent_mod(t2, z, temp, w, precision); + + if (XMP_Compare(t2, wminus1, precision) == 0) { + break; // passed this round + } + if (XMP_Test_Eq_Int(z, 1, precision)) { + return false; + } + } + if (j == a) { + return false; + } + } + return true; +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number. * + * * + * This routine will generate a random MP number with the number of bits precision * + * specified. This is the starting point for generating large random prime numbers. It is * + * very important that the random number generated is truly random. * + * * + * INPUT: result -- Pointer to the buffer that will hold the MP number. * + * * + * rng -- Reference to a random number generator. * + * * + * total_bits-- The number of bits precision that the MP number must have. * + * * + * precision-- The precision of the MP number to be generated (maximum) * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, int total_bits, int precision) +{ + assert(XMP_Bits_To_Digits(total_bits) <= MAX_UNIT_PRECISION); + + total_bits = min(total_bits, precision * 32); + + unsigned nbytes = total_bits/8 + 1; + + XMP_Init(result, 0, precision); + rng.Get(result, nbytes); + + ((unsigned char *)result)[nbytes-1] &= (unsigned char)(~((~0) << (total_bits % 8))); +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * * + * This routine will generate a random MP number but it will be bounded by the minimum * + * and maximum MP numbers specified. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * rng -- Reference to a random number generator to use. * + * * + * minval -- Minimum value allowed. * + * * + * maxval -- Maximum value allowed. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, digit const * minval, digit const * maxval, int precision) +{ + digit range[MAX_UNIT_PRECISION]; + XMP_Sub(range, maxval, minval, 0, precision); + unsigned int bit_count = XMP_Count_Bits(range, precision); + do { + XMP_Randomize(result, rng, bit_count, precision); + } while (XMP_Compare(result, range, precision) > 0); + + XMP_Add(result, result, minval, 0, precision); +} + + +/*********************************************************************************************** + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * * + * This routine will perform some checks to try and determine if the specified MP number * + * is a prime number. The result of this test is not 100% conclusive, but it is pretty * + * darn close. * + * * + * INPUT: prime -- Pointer to a candidate number to test for primality. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? If FALSE, then the number is * + * not prime. If TRUE, then it might be. * + * * + * WARNINGS: This can take a very very very very very long time. Especially for the larger * + * numbers. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Prime(digit const * prime, int precision) +{ + /* + ** Even numbers are ALWAYS not prime. + */ + if (!(*prime & 0x01)) return(false); + + /* + ** Compare the prime number against the exhaustive list of prime + ** numbers below 14 bits in size. If it finds a match, then + ** the number is a known prime. + */ + if (XMP_Is_Small_Prime(prime, precision)) return(true); + + /* + ** Perform the small divisors test. This is not exhaustive, but + ** will weed out a large percentage of non-prime numbers. + */ + if (!XMP_Small_Divisors_Test(prime, precision)) return(false); + + /* + ** Perform Fermat's Little Theorum on the candidate prime. Run + ** the theorum for several rounds to ensure a high degree of + ** confidence. + */ + if (!XMP_Fermat_Test(prime, 2, precision)) return(false); + + /* + ** If all of the above tests have not confirmed primality nor + ** confirmed non-primality, presume that the number must be prime. + */ + return(true); +} + + +/* +** Complete list of all prime numbers that are less than 32719 (inclusive). +*/ +unsigned short primeTable[3511] = { + 0x0002,0x0003,0x0005,0x0007,0x000B,0x000D,0x0011,0x0013,0x0017,0x001D,0x001F,0x0025,0x0029,0x002B,0x002F,0x0035, + 0x003B,0x003D,0x0043,0x0047,0x0049,0x004F,0x0053,0x0059,0x0061,0x0065,0x0067,0x006B,0x006D,0x0071,0x007F,0x0083, + 0x0089,0x008B,0x0095,0x0097,0x009D,0x00A3,0x00A7,0x00AD,0x00B3,0x00B5,0x00BF,0x00C1,0x00C5,0x00C7,0x00D3,0x00DF, + 0x00E3,0x00E5,0x00E9,0x00EF,0x00F1,0x00FB,0x0101,0x0107,0x010D,0x010F,0x0115,0x0119,0x011B,0x0125,0x0133,0x0137, + 0x0139,0x013D,0x014B,0x0151,0x015B,0x015D,0x0161,0x0167,0x016F,0x0175,0x017B,0x017F,0x0185,0x018D,0x0191,0x0199, + 0x01A3,0x01A5,0x01AF,0x01B1,0x01B7,0x01BB,0x01C1,0x01C9,0x01CD,0x01CF,0x01D3,0x01DF,0x01E7,0x01EB,0x01F3,0x01F7, + 0x01FD,0x0209,0x020B,0x021D,0x0223,0x022D,0x0233,0x0239,0x023B,0x0241,0x024B,0x0251,0x0257,0x0259,0x025F,0x0265, + 0x0269,0x026B,0x0277,0x0281,0x0283,0x0287,0x028D,0x0293,0x0295,0x02A1,0x02A5,0x02AB,0x02B3,0x02BD,0x02C5,0x02CF, + 0x02D7,0x02DD,0x02E3,0x02E7,0x02EF,0x02F5,0x02F9,0x0301,0x0305,0x0313,0x031D,0x0329,0x032B,0x0335,0x0337,0x033B, + 0x033D,0x0347,0x0355,0x0359,0x035B,0x035F,0x036D,0x0371,0x0373,0x0377,0x038B,0x038F,0x0397,0x03A1,0x03A9,0x03AD, + 0x03B3,0x03B9,0x03C7,0x03CB,0x03D1,0x03D7,0x03DF,0x03E5,0x03F1,0x03F5,0x03FB,0x03FD,0x0407,0x0409,0x040F,0x0419, + 0x041B,0x0425,0x0427,0x042D,0x043F,0x0443,0x0445,0x0449,0x044F,0x0455,0x045D,0x0463,0x0469,0x047F,0x0481,0x048B, + 0x0493,0x049D,0x04A3,0x04A9,0x04B1,0x04BD,0x04C1,0x04C7,0x04CD,0x04CF,0x04D5,0x04E1,0x04EB,0x04FD,0x04FF,0x0503, + 0x0509,0x050B,0x0511,0x0515,0x0517,0x051B,0x0527,0x0529,0x052F,0x0551,0x0557,0x055D,0x0565,0x0577,0x0581,0x058F, + 0x0593,0x0595,0x0599,0x059F,0x05A7,0x05AB,0x05AD,0x05B3,0x05BF,0x05C9,0x05CB,0x05CF,0x05D1,0x05D5,0x05DB,0x05E7, + 0x05F3,0x05FB,0x0607,0x060D,0x0611,0x0617,0x061F,0x0623,0x062B,0x062F,0x063D,0x0641,0x0647,0x0649,0x064D,0x0653, + 0x0655,0x065B,0x0665,0x0679,0x067F,0x0683,0x0685,0x069D,0x06A1,0x06A3,0x06AD,0x06B9,0x06BB,0x06C5,0x06CD,0x06D3, + 0x06D9,0x06DF,0x06F1,0x06F7,0x06FB,0x06FD,0x0709,0x0713,0x071F,0x0727,0x0737,0x0745,0x074B,0x074F,0x0751,0x0755, + 0x0757,0x0761,0x076D,0x0773,0x0779,0x078B,0x078D,0x079D,0x079F,0x07B5,0x07BB,0x07C3,0x07C9,0x07CD,0x07CF,0x07D3, + 0x07DB,0x07E1,0x07EB,0x07ED,0x07F7,0x0805,0x080F,0x0815,0x0821,0x0823,0x0827,0x0829,0x0833,0x083F,0x0841,0x0851, + 0x0853,0x0859,0x085D,0x085F,0x0869,0x0871,0x0883,0x089B,0x089F,0x08A5,0x08AD,0x08BD,0x08BF,0x08C3,0x08CB,0x08DB, + 0x08DD,0x08E1,0x08E9,0x08EF,0x08F5,0x08F9,0x0905,0x0907,0x091D,0x0923,0x0925,0x092B,0x092F,0x0935,0x0943,0x0949, + 0x094D,0x094F,0x0955,0x0959,0x095F,0x096B,0x0971,0x0977,0x0985,0x0989,0x098F,0x099B,0x09A3,0x09A9,0x09AD,0x09C7, + 0x09D9,0x09E3,0x09EB,0x09EF,0x09F5,0x09F7,0x09FD,0x0A13,0x0A1F,0x0A21,0x0A31,0x0A39,0x0A3D,0x0A49,0x0A57,0x0A61, + 0x0A63,0x0A67,0x0A6F,0x0A75,0x0A7B,0x0A7F,0x0A81,0x0A85,0x0A8B,0x0A93,0x0A97,0x0A99,0x0A9F,0x0AA9,0x0AAB,0x0AB5, + 0x0ABD,0x0AC1,0x0ACF,0x0AD9,0x0AE5,0x0AE7,0x0AED,0x0AF1,0x0AF3,0x0B03,0x0B11,0x0B15,0x0B1B,0x0B23,0x0B29,0x0B2D, + 0x0B3F,0x0B47,0x0B51,0x0B57,0x0B5D,0x0B65,0x0B6F,0x0B7B,0x0B89,0x0B8D,0x0B93,0x0B99,0x0B9B,0x0BB7,0x0BB9,0x0BC3, + 0x0BCB,0x0BCF,0x0BDD,0x0BE1,0x0BE9,0x0BF5,0x0BFB,0x0C07,0x0C0B,0x0C11,0x0C25,0x0C2F,0x0C31,0x0C41,0x0C5B,0x0C5F, + 0x0C61,0x0C6D,0x0C73,0x0C77,0x0C83,0x0C89,0x0C91,0x0C95,0x0C9D,0x0CB3,0x0CB5,0x0CB9,0x0CBB,0x0CC7,0x0CE3,0x0CE5, + 0x0CEB,0x0CF1,0x0CF7,0x0CFB,0x0D01,0x0D03,0x0D0F,0x0D13,0x0D1F,0x0D21,0x0D2B,0x0D2D,0x0D3D,0x0D3F,0x0D4F,0x0D55, + 0x0D69,0x0D79,0x0D81,0x0D85,0x0D87,0x0D8B,0x0D8D,0x0DA3,0x0DAB,0x0DB7,0x0DBD,0x0DC7,0x0DC9,0x0DCD,0x0DD3,0x0DD5, + 0x0DDB,0x0DE5,0x0DE7,0x0DF3,0x0DFD,0x0DFF,0x0E09,0x0E17,0x0E1D,0x0E21,0x0E27,0x0E2F,0x0E35,0x0E3B,0x0E4B,0x0E57, + 0x0E59,0x0E5D,0x0E6B,0x0E71,0x0E75,0x0E7D,0x0E87,0x0E8F,0x0E95,0x0E9B,0x0EB1,0x0EB7,0x0EB9,0x0EC3,0x0ED1,0x0ED5, + 0x0EDB,0x0EED,0x0EEF,0x0EF9,0x0F07,0x0F0B,0x0F0D,0x0F17,0x0F25,0x0F29,0x0F31,0x0F43,0x0F47,0x0F4D,0x0F4F,0x0F53, + 0x0F59,0x0F5B,0x0F67,0x0F6B,0x0F7F,0x0F95,0x0FA1,0x0FA3,0x0FA7,0x0FAD,0x0FB3,0x0FB5,0x0FBB,0x0FD1,0x0FD3,0x0FD9, + 0x0FE9,0x0FEF,0x0FFB,0x0FFD,0x1003,0x100F,0x101F,0x1021,0x1025,0x102B,0x1039,0x103D,0x103F,0x1051,0x1069,0x1073, + 0x1079,0x107B,0x1085,0x1087,0x1091,0x1093,0x109D,0x10A3,0x10A5,0x10AF,0x10B1,0x10BB,0x10C1,0x10C9,0x10E7,0x10F1, + 0x10F3,0x10FD,0x1105,0x110B,0x1115,0x1127,0x112D,0x1139,0x1145,0x1147,0x1159,0x115F,0x1163,0x1169,0x116F,0x1181, + 0x1183,0x118D,0x119B,0x11A1,0x11A5,0x11A7,0x11AB,0x11C3,0x11C5,0x11D1,0x11D7,0x11E7,0x11EF,0x11F5,0x11FB,0x120D, + 0x121D,0x121F,0x1223,0x1229,0x122B,0x1231,0x1237,0x1241,0x1247,0x1253,0x125F,0x1271,0x1273,0x1279,0x127D,0x128F, + 0x1297,0x12AF,0x12B3,0x12B5,0x12B9,0x12BF,0x12C1,0x12CD,0x12D1,0x12DF,0x12FD,0x1307,0x130D,0x1319,0x1327,0x132D, + 0x1337,0x1343,0x1345,0x1349,0x134F,0x1357,0x135D,0x1367,0x1369,0x136D,0x137B,0x1381,0x1387,0x138B,0x1391,0x1393, + 0x139D,0x139F,0x13AF,0x13BB,0x13C3,0x13D5,0x13D9,0x13DF,0x13EB,0x13ED,0x13F3,0x13F9,0x13FF,0x141B,0x1421,0x142F, + 0x1433,0x143B,0x1445,0x144D,0x1459,0x146B,0x146F,0x1471,0x1475,0x148D,0x1499,0x149F,0x14A1,0x14B1,0x14B7,0x14BD, + 0x14CB,0x14D5,0x14E3,0x14E7,0x1505,0x150B,0x1511,0x1517,0x151F,0x1525,0x1529,0x152B,0x1537,0x153D,0x1541,0x1543, + 0x1549,0x155F,0x1565,0x1567,0x156B,0x157D,0x157F,0x1583,0x158F,0x1591,0x1597,0x159B,0x15B5,0x15BB,0x15C1,0x15C5, + 0x15CD,0x15D7,0x15F7,0x1607,0x1609,0x160F,0x1613,0x1615,0x1619,0x161B,0x1625,0x1633,0x1639,0x163D,0x1645,0x164F, + 0x1655,0x1669,0x166D,0x166F,0x1675,0x1693,0x1697,0x169F,0x16A9,0x16AF,0x16B5,0x16BD,0x16C3,0x16CF,0x16D3,0x16D9, + 0x16DB,0x16E1,0x16E5,0x16EB,0x16ED,0x16F7,0x16F9,0x1709,0x170F,0x1723,0x1727,0x1733,0x1741,0x175D,0x1763,0x1777, + 0x177B,0x178D,0x1795,0x179B,0x179F,0x17A5,0x17B3,0x17B9,0x17BF,0x17C9,0x17CB,0x17D5,0x17E1,0x17E9,0x17F3,0x17F5, + 0x17FF,0x1807,0x1813,0x181D,0x1835,0x1837,0x183B,0x1843,0x1849,0x184D,0x1855,0x1867,0x1871,0x1877,0x187D,0x187F, + 0x1885,0x188F,0x189B,0x189D,0x18A7,0x18AD,0x18B3,0x18B9,0x18C1,0x18C7,0x18D1,0x18D7,0x18D9,0x18DF,0x18E5,0x18EB, + 0x18F5,0x18FD,0x1915,0x191B,0x1931,0x1933,0x1945,0x1949,0x1951,0x195B,0x1979,0x1981,0x1993,0x1997,0x1999,0x19A3, + 0x19A9,0x19AB,0x19B1,0x19B5,0x19C7,0x19CF,0x19DB,0x19ED,0x19FD,0x1A03,0x1A05,0x1A11,0x1A17,0x1A21,0x1A23,0x1A2D, + 0x1A2F,0x1A35,0x1A3F,0x1A4D,0x1A51,0x1A69,0x1A6B,0x1A7B,0x1A7D,0x1A87,0x1A89,0x1A93,0x1AA7,0x1AAB,0x1AAD,0x1AB1, + 0x1AB9,0x1AC9,0x1ACF,0x1AD5,0x1AD7,0x1AE3,0x1AF3,0x1AFB,0x1AFF,0x1B05,0x1B23,0x1B25,0x1B2F,0x1B31,0x1B37,0x1B3B, + 0x1B41,0x1B47,0x1B4F,0x1B55,0x1B59,0x1B65,0x1B6B,0x1B73,0x1B7F,0x1B83,0x1B91,0x1B9D,0x1BA7,0x1BBF,0x1BC5,0x1BD1, + 0x1BD7,0x1BD9,0x1BEF,0x1BF7,0x1C09,0x1C13,0x1C19,0x1C27,0x1C2B,0x1C2D,0x1C33,0x1C3D,0x1C45,0x1C4B,0x1C4F,0x1C55, + 0x1C73,0x1C81,0x1C8B,0x1C8D,0x1C99,0x1CA3,0x1CA5,0x1CB5,0x1CB7,0x1CC9,0x1CE1,0x1CF3,0x1CF9,0x1D09,0x1D1B,0x1D21, + 0x1D23,0x1D35,0x1D39,0x1D3F,0x1D41,0x1D4B,0x1D53,0x1D5D,0x1D63,0x1D69,0x1D71,0x1D75,0x1D7B,0x1D7D,0x1D87,0x1D89, + 0x1D95,0x1D99,0x1D9F,0x1DA5,0x1DA7,0x1DB3,0x1DB7,0x1DC5,0x1DD7,0x1DDB,0x1DE1,0x1DF5,0x1DF9,0x1E01,0x1E07,0x1E0B, + 0x1E13,0x1E17,0x1E25,0x1E2B,0x1E2F,0x1E3D,0x1E49,0x1E4D,0x1E4F,0x1E6D,0x1E71,0x1E89,0x1E8F,0x1E95,0x1EA1,0x1EAD, + 0x1EBB,0x1EC1,0x1EC5,0x1EC7,0x1ECB,0x1EDD,0x1EE3,0x1EEF,0x1EF7,0x1EFD,0x1F01,0x1F0D,0x1F0F,0x1F1B,0x1F39,0x1F49, + 0x1F4B,0x1F51,0x1F67,0x1F75,0x1F7B,0x1F85,0x1F91,0x1F97,0x1F99,0x1F9D,0x1FA5,0x1FAF,0x1FB5,0x1FBB,0x1FD3,0x1FE1, + 0x1FE7,0x1FEB,0x1FF3,0x1FFF,0x2011,0x201B,0x201D,0x2027,0x2029,0x202D,0x2033,0x2047,0x204D,0x2051,0x205F,0x2063, + 0x2065,0x2069,0x2077,0x207D,0x2089,0x20A1,0x20AB,0x20B1,0x20B9,0x20C3,0x20C5,0x20E3,0x20E7,0x20ED,0x20EF,0x20FB, + 0x20FF,0x210D,0x2113,0x2135,0x2141,0x2149,0x214F,0x2159,0x215B,0x215F,0x2173,0x217D,0x2185,0x2195,0x2197,0x21A1, + 0x21AF,0x21B3,0x21B5,0x21C1,0x21C7,0x21D7,0x21DD,0x21E5,0x21E9,0x21F1,0x21F5,0x21FB,0x2203,0x2209,0x220F,0x221B, + 0x2221,0x2225,0x222B,0x2231,0x2239,0x224B,0x224F,0x2263,0x2267,0x2273,0x2275,0x227F,0x2285,0x2287,0x2291,0x229D, + 0x229F,0x22A3,0x22B7,0x22BD,0x22DB,0x22E1,0x22E5,0x22ED,0x22F7,0x2303,0x2309,0x230B,0x2327,0x2329,0x232F,0x2333, + 0x2335,0x2345,0x2351,0x2353,0x2359,0x2363,0x236B,0x2383,0x238F,0x2395,0x23A7,0x23AD,0x23B1,0x23BF,0x23C5,0x23C9, + 0x23D5,0x23DD,0x23E3,0x23EF,0x23F3,0x23F9,0x2405,0x240B,0x2417,0x2419,0x2429,0x243D,0x2441,0x2443,0x244D,0x245F, + 0x2467,0x246B,0x2479,0x247D,0x247F,0x2485,0x249B,0x24A1,0x24AF,0x24B5,0x24BB,0x24C5,0x24CB,0x24CD,0x24D7,0x24D9, + 0x24DD,0x24DF,0x24F5,0x24F7,0x24FB,0x2501,0x2507,0x2513,0x2519,0x2527,0x2531,0x253D,0x2543,0x254B,0x254F,0x2573, + 0x2581,0x258D,0x2593,0x2597,0x259D,0x259F,0x25AB,0x25B1,0x25BD,0x25CD,0x25CF,0x25D9,0x25E1,0x25F7,0x25F9,0x2605, + 0x260B,0x260F,0x2615,0x2627,0x2629,0x2635,0x263B,0x263F,0x264B,0x2653,0x2659,0x2665,0x2669,0x266F,0x267B,0x2681, + 0x2683,0x268F,0x269B,0x269F,0x26AD,0x26B3,0x26C3,0x26C9,0x26CB,0x26D5,0x26DD,0x26EF,0x26F5,0x2717,0x2719,0x2735, + 0x2737,0x274D,0x2753,0x2755,0x275F,0x276B,0x276D,0x2773,0x2777,0x277F,0x2795,0x279B,0x279D,0x27A7,0x27AF,0x27B3, + 0x27B9,0x27C1,0x27C5,0x27D1,0x27E3,0x27EF,0x2803,0x2807,0x280D,0x2813,0x281B,0x281F,0x2821,0x2831,0x283D,0x283F, + 0x2849,0x2851,0x285B,0x285D,0x2861,0x2867,0x2875,0x2881,0x2897,0x289F,0x28BB,0x28BD,0x28C1,0x28D5,0x28D9,0x28DB, + 0x28DF,0x28ED,0x28F7,0x2903,0x2905,0x2911,0x2921,0x2923,0x293F,0x2947,0x295D,0x2965,0x2969,0x296F,0x2975,0x2983, + 0x2987,0x298F,0x299B,0x29A1,0x29A7,0x29AB,0x29BF,0x29C3,0x29D5,0x29D7,0x29E3,0x29E9,0x29ED,0x29F3,0x2A01,0x2A13, + 0x2A1D,0x2A25,0x2A2F,0x2A4F,0x2A55,0x2A5F,0x2A65,0x2A6B,0x2A6D,0x2A73,0x2A83,0x2A89,0x2A8B,0x2A97,0x2A9D,0x2AB9, + 0x2ABB,0x2AC5,0x2ACD,0x2ADD,0x2AE3,0x2AEB,0x2AF1,0x2AFB,0x2B13,0x2B27,0x2B31,0x2B33,0x2B3D,0x2B3F,0x2B4B,0x2B4F, + 0x2B55,0x2B69,0x2B6D,0x2B6F,0x2B7B,0x2B8D,0x2B97,0x2B99,0x2BA3,0x2BA5,0x2BA9,0x2BBD,0x2BCD,0x2BE7,0x2BEB,0x2BF3, + 0x2BF9,0x2BFD,0x2C09,0x2C0F,0x2C17,0x2C23,0x2C2F,0x2C35,0x2C39,0x2C41,0x2C57,0x2C59,0x2C69,0x2C77,0x2C81,0x2C87, + 0x2C93,0x2C9F,0x2CAD,0x2CB3,0x2CB7,0x2CCB,0x2CCF,0x2CDB,0x2CE1,0x2CE3,0x2CE9,0x2CEF,0x2CFF,0x2D07,0x2D1D,0x2D1F, + 0x2D3B,0x2D43,0x2D49,0x2D4D,0x2D61,0x2D65,0x2D71,0x2D89,0x2D9D,0x2DA1,0x2DA9,0x2DB3,0x2DB5,0x2DC5,0x2DC7,0x2DD3, + 0x2DDF,0x2E01,0x2E03,0x2E07,0x2E0D,0x2E19,0x2E1F,0x2E25,0x2E2D,0x2E33,0x2E37,0x2E39,0x2E3F,0x2E57,0x2E5B,0x2E6F, + 0x2E79,0x2E7F,0x2E85,0x2E93,0x2E97,0x2E9D,0x2EA3,0x2EA5,0x2EB1,0x2EB7,0x2EC1,0x2EC3,0x2ECD,0x2ED3,0x2EE7,0x2EEB, + 0x2F05,0x2F09,0x2F0B,0x2F11,0x2F27,0x2F29,0x2F41,0x2F45,0x2F4B,0x2F4D,0x2F51,0x2F57,0x2F6F,0x2F75,0x2F7D,0x2F81, + 0x2F83,0x2FA5,0x2FAB,0x2FB3,0x2FC3,0x2FCF,0x2FD1,0x2FDB,0x2FDD,0x2FE7,0x2FED,0x2FF5,0x2FF9,0x3001,0x300D,0x3023, + 0x3029,0x3037,0x303B,0x3055,0x3059,0x305B,0x3067,0x3071,0x3079,0x307D,0x3085,0x3091,0x3095,0x30A3,0x30A9,0x30B9, + 0x30BF,0x30C7,0x30CB,0x30D1,0x30D7,0x30DF,0x30E5,0x30EF,0x30FB,0x30FD,0x3103,0x3109,0x3119,0x3121,0x3127,0x312D, + 0x3139,0x3143,0x3145,0x314B,0x315D,0x3161,0x3167,0x316D,0x3173,0x317F,0x3191,0x3199,0x319F,0x31A9,0x31B1,0x31C3, + 0x31C7,0x31D5,0x31DB,0x31ED,0x31F7,0x31FF,0x3209,0x3215,0x3217,0x321D,0x3229,0x3235,0x3259,0x325D,0x3263,0x326B, + 0x326F,0x3275,0x3277,0x327B,0x328D,0x3299,0x329F,0x32A7,0x32AD,0x32B3,0x32B7,0x32C9,0x32CB,0x32CF,0x32D1,0x32E9, + 0x32ED,0x32F3,0x32F9,0x3307,0x3325,0x332B,0x332F,0x3335,0x3341,0x3347,0x335B,0x335F,0x3367,0x336B,0x3373,0x3379, + 0x337F,0x3383,0x33A1,0x33A3,0x33AD,0x33B9,0x33C1,0x33CB,0x33D3,0x33EB,0x33F1,0x33FD,0x3401,0x340F,0x3413,0x3419, + 0x341B,0x3437,0x3445,0x3455,0x3457,0x3463,0x3469,0x346D,0x3481,0x348B,0x3491,0x3497,0x349D,0x34A5,0x34AF,0x34BB, + 0x34C9,0x34D3,0x34E1,0x34F1,0x34FF,0x3509,0x3517,0x351D,0x352D,0x3533,0x353B,0x3541,0x3551,0x3565,0x356F,0x3571, + 0x3577,0x357B,0x357D,0x3581,0x358D,0x358F,0x3599,0x359B,0x35A1,0x35B7,0x35BD,0x35BF,0x35C3,0x35D5,0x35DD,0x35E7, + 0x35EF,0x3605,0x3607,0x3611,0x3623,0x3631,0x3635,0x3637,0x363B,0x364D,0x364F,0x3653,0x3659,0x3661,0x366B,0x366D, + 0x368B,0x368F,0x36AD,0x36AF,0x36B9,0x36BB,0x36CD,0x36D1,0x36E3,0x36E9,0x36F7,0x3701,0x3703,0x3707,0x371B,0x373F, + 0x3745,0x3749,0x374F,0x375D,0x3761,0x3775,0x377F,0x378D,0x37A3,0x37A9,0x37AB,0x37C9,0x37D5,0x37DF,0x37F1,0x37F3, + 0x37F7,0x3805,0x380B,0x3821,0x3833,0x3835,0x3841,0x3847,0x384B,0x3853,0x3857,0x385F,0x3865,0x386F,0x3871,0x387D, + 0x388F,0x3899,0x38A7,0x38B7,0x38C5,0x38C9,0x38CF,0x38D5,0x38D7,0x38DD,0x38E1,0x38E3,0x38FF,0x3901,0x391D,0x3923, + 0x3925,0x3929,0x392F,0x393D,0x3941,0x394D,0x395B,0x396B,0x3979,0x397D,0x3983,0x398B,0x3991,0x3995,0x399B,0x39A1, + 0x39A7,0x39AF,0x39B3,0x39BB,0x39BF,0x39CD,0x39DD,0x39E5,0x39EB,0x39EF,0x39FB,0x3A03,0x3A13,0x3A15,0x3A1F,0x3A27, + 0x3A2B,0x3A31,0x3A4B,0x3A51,0x3A5B,0x3A63,0x3A67,0x3A6D,0x3A79,0x3A87,0x3AA5,0x3AA9,0x3AB7,0x3ACD,0x3AD5,0x3AE1, + 0x3AE5,0x3AEB,0x3AF3,0x3AFD,0x3B03,0x3B11,0x3B1B,0x3B21,0x3B23,0x3B2D,0x3B39,0x3B45,0x3B53,0x3B59,0x3B5F,0x3B71, + 0x3B7B,0x3B81,0x3B89,0x3B9B,0x3B9F,0x3BA5,0x3BA7,0x3BAD,0x3BB7,0x3BB9,0x3BC3,0x3BCB,0x3BD1,0x3BD7,0x3BE1,0x3BE3, + 0x3BF5,0x3BFF,0x3C01,0x3C0D,0x3C11,0x3C17,0x3C1F,0x3C29,0x3C35,0x3C43,0x3C4F,0x3C53,0x3C5B,0x3C65,0x3C6B,0x3C71, + 0x3C85,0x3C89,0x3C97,0x3CA7,0x3CB5,0x3CBF,0x3CC7,0x3CD1,0x3CDD,0x3CDF,0x3CF1,0x3CF7,0x3D03,0x3D0D,0x3D19,0x3D1B, + 0x3D1F,0x3D21,0x3D2D,0x3D33,0x3D37,0x3D3F,0x3D43,0x3D6F,0x3D73,0x3D75,0x3D79,0x3D7B,0x3D85,0x3D91,0x3D97,0x3D9D, + 0x3DAB,0x3DAF,0x3DB5,0x3DBB,0x3DC1,0x3DC9,0x3DCF,0x3DF3,0x3E05,0x3E09,0x3E0F,0x3E11,0x3E1D,0x3E23,0x3E29,0x3E2F, + 0x3E33,0x3E41,0x3E57,0x3E63,0x3E65,0x3E77,0x3E81,0x3E87,0x3EA1,0x3EB9,0x3EBD,0x3EBF,0x3EC3,0x3EC5,0x3EC9,0x3ED7, + 0x3EDB,0x3EE1,0x3EE7,0x3EEF,0x3EFF,0x3F0B,0x3F0D,0x3F37,0x3F3B,0x3F3D,0x3F41,0x3F59,0x3F5F,0x3F65,0x3F67,0x3F79, + 0x3F7D,0x3F8B,0x3F91,0x3FAD,0x3FBF,0x3FCD,0x3FD3,0x3FDD,0x3FE9,0x3FEB,0x3FF1,0x3FFD,0x401B,0x4021,0x4025,0x402B, + 0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9,0x40B1,0x40B7,0x40BD,0x40DB, + 0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B,0x413F,0x4159,0x4165,0x416B, + 0x4177,0x417B,0x4193,0x41AB,0x41B7,0x41BD,0x41BF,0x41CB,0x41E7,0x41EF,0x41F3,0x41F9,0x4205,0x4207,0x4219,0x421F, + 0x4223,0x4229,0x422F,0x4243,0x4253,0x4255,0x425B,0x4261,0x4273,0x427D,0x4283,0x4285,0x4289,0x4291,0x4297,0x429D, + 0x42B5,0x42C5,0x42CB,0x42D3,0x42DD,0x42E3,0x42F1,0x4307,0x430F,0x431F,0x4325,0x4327,0x4333,0x4337,0x4339,0x434F, + 0x4357,0x4369,0x438B,0x438D,0x4393,0x43A5,0x43A9,0x43AF,0x43B5,0x43BD,0x43C7,0x43CF,0x43E1,0x43E7,0x43EB,0x43ED, + 0x43F1,0x43F9,0x4409,0x440B,0x4417,0x4423,0x4429,0x443B,0x443F,0x4445,0x444B,0x4451,0x4453,0x4459,0x4465,0x446F, + 0x4483,0x448F,0x44A1,0x44A5,0x44AB,0x44AD,0x44BD,0x44BF,0x44C9,0x44D7,0x44DB,0x44F9,0x44FB,0x4505,0x4511,0x4513, + 0x452B,0x4531,0x4541,0x4549,0x4553,0x4555,0x4561,0x4577,0x457D,0x457F,0x458F,0x45A3,0x45AD,0x45AF,0x45BB,0x45C7, + 0x45D9,0x45E3,0x45EF,0x45F5,0x45F7,0x4601,0x4603,0x4609,0x4613,0x4625,0x4627,0x4633,0x4639,0x463D,0x4643,0x4645, + 0x465D,0x4679,0x467B,0x467F,0x4681,0x468B,0x468D,0x469D,0x46A9,0x46B1,0x46C7,0x46C9,0x46CF,0x46D3,0x46D5,0x46DF, + 0x46E5,0x46F9,0x4705,0x470F,0x4717,0x4723,0x4729,0x472F,0x4735,0x4739,0x474B,0x474D,0x4751,0x475D,0x476F,0x4771, + 0x477D,0x4783,0x4787,0x4789,0x4799,0x47A5,0x47B1,0x47BF,0x47C3,0x47CB,0x47DD,0x47E1,0x47ED,0x47FB,0x4801,0x4807, + 0x480B,0x4813,0x4819,0x481D,0x4831,0x483D,0x4847,0x4855,0x4859,0x485B,0x486B,0x486D,0x4879,0x4897,0x489B,0x48A1, + 0x48B9,0x48CD,0x48E5,0x48EF,0x48F7,0x4903,0x490D,0x4919,0x491F,0x492B,0x4937,0x493D,0x4945,0x4955,0x4963,0x4969, + 0x496D,0x4973,0x4997,0x49AB,0x49B5,0x49D3,0x49DF,0x49E1,0x49E5,0x49E7,0x4A03,0x4A0F,0x4A1D,0x4A23,0x4A39,0x4A41, + 0x4A45,0x4A57,0x4A5D,0x4A6B,0x4A7D,0x4A81,0x4A87,0x4A89,0x4A8F,0x4AB1,0x4AC3,0x4AC5,0x4AD5,0x4ADB,0x4AED,0x4AEF, + 0x4B07,0x4B0B,0x4B0D,0x4B13,0x4B1F,0x4B25,0x4B31,0x4B3B,0x4B43,0x4B49,0x4B59,0x4B65,0x4B6D,0x4B77,0x4B85,0x4BAD, + 0x4BB3,0x4BB5,0x4BBB,0x4BBF,0x4BCB,0x4BD9,0x4BDD,0x4BDF,0x4BE3,0x4BE5,0x4BE9,0x4BF1,0x4BF7,0x4C01,0x4C07,0x4C0D, + 0x4C0F,0x4C15,0x4C1B,0x4C21,0x4C2D,0x4C33,0x4C4B,0x4C55,0x4C57,0x4C61,0x4C67,0x4C73,0x4C79,0x4C7F,0x4C8D,0x4C93, + 0x4C99,0x4CCD,0x4CE1,0x4CE7,0x4CF1,0x4CF3,0x4CFD,0x4D05,0x4D0F,0x4D1B,0x4D27,0x4D29,0x4D2F,0x4D33,0x4D41,0x4D51, + 0x4D59,0x4D65,0x4D6B,0x4D81,0x4D83,0x4D8D,0x4D95,0x4D9B,0x4DB1,0x4DB3,0x4DC9,0x4DCF,0x4DD7,0x4DE1,0x4DED,0x4DF9, + 0x4DFB,0x4E05,0x4E0B,0x4E17,0x4E19,0x4E1D,0x4E2B,0x4E35,0x4E37,0x4E3D,0x4E4F,0x4E53,0x4E5F,0x4E67,0x4E79,0x4E85, + 0x4E8B,0x4E91,0x4E95,0x4E9B,0x4EA1,0x4EAF,0x4EB3,0x4EB5,0x4EC1,0x4ECD,0x4ED1,0x4ED7,0x4EE9,0x4EFB,0x4F07,0x4F09, + 0x4F19,0x4F25,0x4F2D,0x4F3F,0x4F49,0x4F63,0x4F67,0x4F6D,0x4F75,0x4F7B,0x4F81,0x4F85,0x4F87,0x4F91,0x4FA5,0x4FA9, + 0x4FAF,0x4FB7,0x4FBB,0x4FCF,0x4FD9,0x4FDB,0x4FFD,0x4FFF,0x5003,0x501B,0x501D,0x5029,0x5035,0x503F,0x5045,0x5047, + 0x5053,0x5071,0x5077,0x5083,0x5093,0x509F,0x50A1,0x50B7,0x50C9,0x50D5,0x50E3,0x50ED,0x50EF,0x50FB,0x5107,0x510B, + 0x510D,0x5111,0x5117,0x5123,0x5125,0x5135,0x5147,0x5149,0x5171,0x5179,0x5189,0x518F,0x5197,0x51A1,0x51A3,0x51A7, + 0x51B9,0x51C1,0x51CB,0x51D3,0x51DF,0x51E3,0x51F5,0x51F7,0x5209,0x5213,0x5215,0x5219,0x521B,0x521F,0x5227,0x5243, + 0x5245,0x524B,0x5261,0x526D,0x5273,0x5281,0x5293,0x5297,0x529D,0x52A5,0x52AB,0x52B1,0x52BB,0x52C3,0x52C7,0x52C9, + 0x52DB,0x52E5,0x52EB,0x52FF,0x5315,0x531D,0x5323,0x5341,0x5345,0x5347,0x534B,0x535D,0x5363,0x5381,0x5383,0x5387, + 0x538F,0x5395,0x5399,0x539F,0x53AB,0x53B9,0x53DB,0x53E9,0x53EF,0x53F3,0x53F5,0x53FB,0x53FF,0x540D,0x5411,0x5413, + 0x5419,0x5435,0x5437,0x543B,0x5441,0x5449,0x5453,0x5455,0x545F,0x5461,0x546B,0x546D,0x5471,0x548F,0x5491,0x549D, + 0x54A9,0x54B3,0x54C5,0x54D1,0x54DF,0x54E9,0x54EB,0x54F7,0x54FD,0x5507,0x550D,0x551B,0x5527,0x552B,0x5539,0x553D, + 0x554F,0x5551,0x555B,0x5563,0x5567,0x556F,0x5579,0x5585,0x5597,0x55A9,0x55B1,0x55B7,0x55C9,0x55D9,0x55E7,0x55ED, + 0x55F3,0x55FD,0x560B,0x560F,0x5615,0x5617,0x5623,0x562F,0x5633,0x5639,0x563F,0x564B,0x564D,0x565D,0x565F,0x566B, + 0x5671,0x5675,0x5683,0x5689,0x568D,0x568F,0x569B,0x56AD,0x56B1,0x56D5,0x56E7,0x56F3,0x56FF,0x5701,0x5705,0x5707, + 0x570B,0x5713,0x571F,0x5723,0x5747,0x574D,0x575F,0x5761,0x576D,0x5777,0x577D,0x5789,0x57A1,0x57A9,0x57AF,0x57B5, + 0x57C5,0x57D1,0x57D3,0x57E5,0x57EF,0x5803,0x580D,0x580F,0x5815,0x5827,0x582B,0x582D,0x5855,0x585B,0x585D,0x586D, + 0x586F,0x5873,0x587B,0x588D,0x5897,0x58A3,0x58A9,0x58AB,0x58B5,0x58BD,0x58C1,0x58C7,0x58D3,0x58D5,0x58DF,0x58F1, + 0x58F9,0x58FF,0x5903,0x5917,0x591B,0x5921,0x5945,0x594B,0x594D,0x5957,0x595D,0x5975,0x597B,0x5989,0x5999,0x599F, + 0x59B1,0x59B3,0x59BD,0x59D1,0x59DB,0x59E3,0x59E9,0x59ED,0x59F3,0x59F5,0x59FF,0x5A01,0x5A0D,0x5A11,0x5A13,0x5A17, + 0x5A1F,0x5A29,0x5A2F,0x5A3B,0x5A4D,0x5A5B,0x5A67,0x5A77,0x5A7F,0x5A85,0x5A95,0x5A9D,0x5AA1,0x5AA3,0x5AA9,0x5ABB, + 0x5AD3,0x5AE5,0x5AEF,0x5AFB,0x5AFD,0x5B01,0x5B0F,0x5B19,0x5B1F,0x5B25,0x5B2B,0x5B3D,0x5B49,0x5B4B,0x5B67,0x5B79, + 0x5B87,0x5B97,0x5BA3,0x5BB1,0x5BC9,0x5BD5,0x5BEB,0x5BF1,0x5BF3,0x5BFD,0x5C05,0x5C09,0x5C0B,0x5C0F,0x5C1D,0x5C29, + 0x5C2F,0x5C33,0x5C39,0x5C47,0x5C4B,0x5C4D,0x5C51,0x5C6F,0x5C75,0x5C77,0x5C7D,0x5C87,0x5C89,0x5CA7,0x5CBD,0x5CBF, + 0x5CC3,0x5CC9,0x5CD1,0x5CD7,0x5CDD,0x5CED,0x5CF9,0x5D05,0x5D0B,0x5D13,0x5D17,0x5D19,0x5D31,0x5D3D,0x5D41,0x5D47, + 0x5D4F,0x5D55,0x5D5B,0x5D65,0x5D67,0x5D6D,0x5D79,0x5D95,0x5DA3,0x5DA9,0x5DAD,0x5DB9,0x5DC1,0x5DC7,0x5DD3,0x5DD7, + 0x5DDD,0x5DEB,0x5DF1,0x5DFD,0x5E07,0x5E0D,0x5E13,0x5E1B,0x5E21,0x5E27,0x5E2B,0x5E2D,0x5E31,0x5E39,0x5E45,0x5E49, + 0x5E57,0x5E69,0x5E73,0x5E75,0x5E85,0x5E8B,0x5E9F,0x5EA5,0x5EAF,0x5EB7,0x5EBB,0x5ED9,0x5EFD,0x5F09,0x5F11,0x5F27, + 0x5F33,0x5F35,0x5F3B,0x5F47,0x5F57,0x5F5D,0x5F63,0x5F65,0x5F77,0x5F7B,0x5F95,0x5F99,0x5FA1,0x5FB3,0x5FBD,0x5FC5, + 0x5FCF,0x5FD5,0x5FE3,0x5FE7,0x5FFB,0x6011,0x6023,0x602F,0x6037,0x6053,0x605F,0x6065,0x606B,0x6073,0x6079,0x6085, + 0x609D,0x60AD,0x60BB,0x60BF,0x60CD,0x60D9,0x60DF,0x60E9,0x60F5,0x6109,0x610F,0x6113,0x611B,0x612D,0x6139,0x614B, + 0x6155,0x6157,0x615B,0x616F,0x6179,0x6187,0x618B,0x6191,0x6193,0x619D,0x61B5,0x61C7,0x61C9,0x61CD,0x61E1,0x61F1, + 0x61FF,0x6209,0x6217,0x621D,0x6221,0x6227,0x623B,0x6241,0x624B,0x6251,0x6253,0x625F,0x6265,0x6283,0x628D,0x6295, + 0x629B,0x629F,0x62A5,0x62AD,0x62D5,0x62D7,0x62DB,0x62DD,0x62E9,0x62FB,0x62FF,0x6305,0x630D,0x6317,0x631D,0x632F, + 0x6341,0x6343,0x634F,0x635F,0x6367,0x636D,0x6371,0x6377,0x637D,0x637F,0x63B3,0x63C1,0x63C5,0x63D9,0x63E9,0x63EB, + 0x63EF,0x63F5,0x6401,0x6403,0x6409,0x6415,0x6421,0x6427,0x642B,0x6439,0x6443,0x6449,0x644F,0x645D,0x6467,0x6475, + 0x6485,0x648D,0x6493,0x649F,0x64A3,0x64AB,0x64C1,0x64C7,0x64C9,0x64DB,0x64F1,0x64F7,0x64F9,0x650B,0x6511,0x6521, + 0x652F,0x6539,0x653F,0x654B,0x654D,0x6553,0x6557,0x655F,0x6571,0x657D,0x658D,0x658F,0x6593,0x65A1,0x65A5,0x65AD, + 0x65B9,0x65C5,0x65E3,0x65F3,0x65FB,0x65FF,0x6601,0x6607,0x661D,0x6629,0x6631,0x663B,0x6641,0x6647,0x664D,0x665B, + 0x6661,0x6673,0x667D,0x6689,0x668B,0x6695,0x6697,0x669B,0x66B5,0x66B9,0x66C5,0x66CD,0x66D1,0x66E3,0x66EB,0x66F5, + 0x6703,0x6713,0x6719,0x671F,0x6727,0x6731,0x6737,0x673F,0x6745,0x6751,0x675B,0x676F,0x6779,0x6781,0x6785,0x6791, + 0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839,0x683B,0x683F,0x6845,0x684B, + 0x684D,0x6857,0x6859,0x685D,0x6863,0x6869,0x686B,0x6871,0x6887,0x6899,0x689F,0x68B1,0x68BD,0x68C5,0x68D1,0x68D7, + 0x68E1,0x68ED,0x68EF,0x68FF,0x6901,0x690B,0x690D,0x6917,0x6929,0x692F,0x6943,0x6947,0x6949,0x694F,0x6965,0x696B, + 0x6971,0x6983,0x6989,0x6997,0x69A3,0x69B3,0x69B5,0x69BB,0x69C1,0x69C5,0x69D3,0x69DF,0x69E3,0x69E5,0x69F7,0x6A07, + 0x6A2B,0x6A37,0x6A3D,0x6A4B,0x6A67,0x6A69,0x6A75,0x6A7B,0x6A87,0x6A8D,0x6A91,0x6A93,0x6AA3,0x6AC1,0x6AC9,0x6AE1, + 0x6AE7,0x6B05,0x6B0F,0x6B11,0x6B23,0x6B27,0x6B2D,0x6B39,0x6B41,0x6B57,0x6B59,0x6B5F,0x6B75,0x6B87,0x6B89,0x6B93, + 0x6B95,0x6B9F,0x6BBD,0x6BBF,0x6BDB,0x6BE1,0x6BEF,0x6BFF,0x6C05,0x6C19,0x6C29,0x6C2B,0x6C31,0x6C35,0x6C55,0x6C59, + 0x6C5B,0x6C5F,0x6C65,0x6C67,0x6C73,0x6C77,0x6C7D,0x6C83,0x6C8F,0x6C91,0x6C97,0x6C9B,0x6CA1,0x6CA9,0x6CAF,0x6CB3, + 0x6CC7,0x6CCB,0x6CEB,0x6CF5,0x6CFD,0x6D0D,0x6D0F,0x6D25,0x6D27,0x6D2B,0x6D31,0x6D39,0x6D3F,0x6D4F,0x6D5D,0x6D61, + 0x6D73,0x6D7B,0x6D7F,0x6D93,0x6D99,0x6DA5,0x6DB1,0x6DB7,0x6DC1,0x6DC3,0x6DCD,0x6DCF,0x6DDB,0x6DF7,0x6E03,0x6E15, + 0x6E17,0x6E29,0x6E33,0x6E3B,0x6E45,0x6E75,0x6E77,0x6E7B,0x6E81,0x6E89,0x6E93,0x6E95,0x6E9F,0x6EBD,0x6EBF,0x6EE3, + 0x6EE9,0x6EF3,0x6EF9,0x6EFB,0x6F0D,0x6F11,0x6F17,0x6F1F,0x6F2F,0x6F3D,0x6F4D,0x6F53,0x6F61,0x6F65,0x6F79,0x6F7D, + 0x6F83,0x6F85,0x6F8F,0x6F9B,0x6F9D,0x6FA3,0x6FAF,0x6FB5,0x6FBB,0x6FBF,0x6FCB,0x6FCD,0x6FD3,0x6FD7,0x6FE3,0x6FE9, + 0x6FF1,0x6FF5,0x6FF7,0x6FFD,0x700F,0x7019,0x701F,0x7027,0x7033,0x7039,0x704F,0x7051,0x7057,0x7063,0x7075,0x7079, + 0x7087,0x708D,0x7091,0x70A5,0x70AB,0x70BB,0x70C3,0x70C7,0x70CF,0x70E5,0x70ED,0x70F9,0x70FF,0x7105,0x7115,0x7121, + 0x7133,0x7151,0x7159,0x715D,0x715F,0x7163,0x7169,0x7183,0x7187,0x7195,0x71AD,0x71C3,0x71C9,0x71CB,0x71D1,0x71DB, + 0x71E1,0x71EF,0x71F5,0x71FB,0x7207,0x7211,0x7217,0x7219,0x7225,0x722F,0x723B,0x7243,0x7255,0x7267,0x7271,0x7277, + 0x727F,0x728F,0x7295,0x729B,0x72A3,0x72B3,0x72C7,0x72CB,0x72CD,0x72D7,0x72D9,0x72E3,0x72EF,0x72F5,0x72FD,0x7303, + 0x730D,0x7321,0x732B,0x733D,0x7357,0x735B,0x7361,0x737F,0x7381,0x7385,0x738D,0x7393,0x739F,0x73AB,0x73BD,0x73C1, + 0x73C9,0x73DF,0x73E5,0x73E7,0x73F3,0x7415,0x741B,0x742D,0x7439,0x743F,0x7441,0x745D,0x746B,0x747B,0x7489,0x748D, + 0x749B,0x74A7,0x74AB,0x74B1,0x74B7,0x74B9,0x74DD,0x74E1,0x74E7,0x74FB,0x7507,0x751F,0x7525,0x753B,0x753D,0x754D, + 0x755F,0x756B,0x7577,0x7589,0x758B,0x7591,0x7597,0x759D,0x75A1,0x75A7,0x75B5,0x75B9,0x75BB,0x75D1,0x75D9,0x75E5, + 0x75EB,0x75F5,0x75FB,0x7603,0x760F,0x7621,0x762D,0x7633,0x763D,0x763F,0x7655,0x7663,0x7669,0x766F,0x7673,0x7685, + 0x768B,0x769F,0x76B5,0x76B7,0x76C3,0x76DB,0x76DF,0x76F1,0x7703,0x7705,0x771B,0x771D,0x7721,0x772D,0x7735,0x7741, + 0x774B,0x7759,0x775D,0x775F,0x7771,0x7781,0x77A7,0x77AD,0x77B3,0x77B9,0x77C5,0x77CF,0x77D5,0x77E1,0x77E9,0x77EF, + 0x77F3,0x77F9,0x7807,0x7825,0x782B,0x7835,0x783D,0x7853,0x7859,0x7861,0x786D,0x7877,0x7879,0x7883,0x7885,0x788B, + 0x7895,0x7897,0x78A1,0x78AD,0x78BF,0x78D3,0x78D9,0x78DD,0x78E5,0x78FB,0x7901,0x7907,0x7925,0x792B,0x7939,0x793F, + 0x794B,0x7957,0x795D,0x7967,0x7969,0x7973,0x7991,0x7993,0x79A3,0x79AB,0x79AF,0x79B1,0x79B7,0x79C9,0x79CD,0x79CF, + 0x79D5,0x79D9,0x79F3,0x79F7,0x79FF,0x7A05,0x7A0F,0x7A11,0x7A15,0x7A1B,0x7A23,0x7A27,0x7A2D,0x7A4B,0x7A57,0x7A59, + 0x7A5F,0x7A65,0x7A69,0x7A7D,0x7A93,0x7A9B,0x7A9F,0x7AA1,0x7AA5,0x7AED,0x7AF5,0x7AF9,0x7B01,0x7B17,0x7B19,0x7B1D, + 0x7B2B,0x7B35,0x7B37,0x7B3B,0x7B4F,0x7B55,0x7B5F,0x7B71,0x7B77,0x7B8B,0x7B9B,0x7BA1,0x7BA9,0x7BAF,0x7BB3,0x7BC7, + 0x7BD3,0x7BE9,0x7BEB,0x7BEF,0x7BF1,0x7BFD,0x7C07,0x7C19,0x7C1B,0x7C31,0x7C37,0x7C49,0x7C67,0x7C69,0x7C73,0x7C81, + 0x7C8B,0x7C93,0x7CA3,0x7CD5,0x7CDB,0x7CE5,0x7CED,0x7CF7,0x7D03,0x7D09,0x7D1B,0x7D1D,0x7D33,0x7D39,0x7D3B,0x7D3F, + 0x7D45,0x7D4D,0x7D53,0x7D59,0x7D63,0x7D75,0x7D77,0x7D8D,0x7D8F,0x7D9F,0x7DAD,0x7DB7,0x7DBD,0x7DBF,0x7DCB,0x7DD5, + 0x7DE9,0x7DED,0x7DFB,0x7E01,0x7E05,0x7E29,0x7E2B,0x7E2F,0x7E35,0x7E41,0x7E43,0x7E47,0x7E55,0x7E61,0x7E67,0x7E6B, + 0x7E71,0x7E73,0x7E79,0x7E7D,0x7E91,0x7E9B,0x7E9D,0x7EA7,0x7EAD,0x7EB9,0x7EBB,0x7ED3,0x7EDF,0x7EEB,0x7EF1,0x7EF7, + 0x7EFB,0x7F13,0x7F15,0x7F19,0x7F31,0x7F33,0x7F39,0x7F3D,0x7F43,0x7F4B,0x7F5B,0x7F61,0x7F63,0x7F6D,0x7F79,0x7F87, + 0x7F8D,0x7FAF,0x7FB5,0x7FC3,0x7FC9,0x7FCD,0x7FCF +}; diff --git a/CODE/MP.H b/CODE/MP.H new file mode 100644 index 0000000..3dea0ca --- /dev/null +++ b/CODE/MP.H @@ -0,0 +1,176 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MP_H +#define MP_H + +//lint -e740 -e534 -e537 -e760 + +//lint -d_LINT=1 +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include "straw.h" +#include + +extern unsigned short primeTable[3511]; + + +#define digit unsigned long +#define signeddigit signed long +#define LOG_UNITSIZE 5 +#define UNITSIZE 32 +#define UPPER_MOST_BIT 0x80000000L +#define SEMI_UPPER_MOST_BIT 0x8000 +#define SEMI_MASK ((unsigned short)~0) +#define MAX_BIT_PRECISION 2048 +#define MAX_UNIT_PRECISION (MAX_BIT_PRECISION/UNITSIZE) +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + + +int XMP_Significance(const digit * r, int precision); +void XMP_Inc(digit * r, int precision); +void XMP_Dec(digit * r, int precision); +void XMP_Neg(digit * r, int precision); +void XMP_Abs(digit * r, int precision); +void XMP_Shift_Right_Bits(digit * r1, int bits, int precision); +void XMP_Shift_Left_Bits(digit * r1, int bits, int precision); +bool XMP_Rotate_Left(digit * r1, bool carry, int precision); +void XMP_Not(digit * digit_ptr, int precision); +void XMP_Init(digit * r, digit value, int precision); +unsigned XMP_Count_Bits(const digit * r, int precision); +int XMP_Count_Bytes(const digit * r, int precision); +void XMP_Move(digit * dest, digit const * source, int precision); +int XMP_Compare(const digit * r1, const digit * r2, int precision); +bool XMP_Add(digit * result, const digit * r1, const digit * r2, bool carry, int precision); +bool XMP_Add_Int(digit * result, const digit * r1, digit r2, bool carry, int precision); +bool XMP_Sub(digit * result, const digit * r1, const digit * r2, bool borrow, int precision); +bool XMP_Sub_Int(digit * result, const digit * r1, unsigned short r2, bool borrow, int precision); +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision); +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision); +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision); +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision); +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision); +void xmp_single_mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision); +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int xmp_stage_modulus(const digit * n_modulus, int precision); +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +void XMP_Mod_Mult_Clear(int precision); +unsigned short mp_quo_digit(unsigned short * dividend); +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision); +bool XMP_Is_Small_Prime(const digit * candidate, int precision); +bool XMP_Small_Divisors_Test(const digit * candidate, int precision); +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision); +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision); +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision); +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision); +void XMP_Randomize(digit * result, Straw & rng, int nbits, int precision); +void XMP_Randomize(digit * result, Straw & rng, digit const * min, digit const * max, int precision); +bool XMP_Is_Prime(digit const * prime, int precision); +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision); +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output); +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision); +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision); + + + +inline int XMP_Digits_To_Bits(int digits) +{ + return(digits << LOG_UNITSIZE); +} + + +inline int XMP_Bits_To_Digits(int bits) +{ + return ((bits + (UNITSIZE-1)) / UNITSIZE); +} + + +inline digit XMP_Bits_To_Mask(int bits) +{ + if (!bits) return(0); + return(1 << ((bits-1) % UNITSIZE)); +} + + +inline bool XMP_Is_Negative(const digit * r, int precision) +{ + return((signeddigit) *(r + (precision-1)) < 0); +} + + +inline bool XMP_Test_Eq_Int(digit const * r, int i, int p) +{ + return( (*r == i ) && XMP_Significance(r,p) <= 1 ); +} + + +inline void XMP_Set_Bit(digit * r, unsigned bit) +{ + r[bit >> LOG_UNITSIZE] |= ((digit)1 << (bit & (UNITSIZE-1))); +} + +inline bool XMP_Test_Bit(const digit * r, unsigned bit) +{ + return (r[bit >> LOG_UNITSIZE] & ((digit)1 << (bit & (UNITSIZE-1)))); +} + + + +// Misc functions. +void memrev(char * buffer, size_t length); + +#endif diff --git a/CODE/MPGSET.CPP b/CODE/MPGSET.CPP new file mode 100644 index 0000000..5b8faac --- /dev/null +++ b/CODE/MPGSET.CPP @@ -0,0 +1,609 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* FILE +* MpgSet.cpp +* +* DESCRIPTION +* Mpeg movie settings manager +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* June 30, 1998 +* +****************************************************************************/ + +#include "function.h" + +#ifdef DVD +#include "mpgset.h" + +#ifdef MCIMPEG +bool EnumMCI(MCIDevice* desc, void* context); +#endif + +/**************************************************************************** +* +* NAME +* MPGSettings(DeviceName) +* +* DESCRIPTION +* Default constructor +* +* INPUTS +* DeviceName - Initial device to use for MPG playback (NULL = DXMedia) +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::MPGSettings(const char* deviceName) + : mDeviceName(NULL) + { + SetDeviceName(deviceName); + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + #endif + } + + +MPGSettings::MPGSettings(FileClass& file) + : mDeviceName(NULL) + { + INIClass ini; + char buffer[256]; + char* device = NULL; + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + // Enumerate all the MCI devices that can play a movie + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + + #endif + + // Retrieve the user specified device from the config file + buffer[0] = '\0'; + + if (ini.Load(file)) + { + ini.Get_String("MovieSettings", "Device", "Default", buffer, sizeof(buffer)); + } + + // If there is a specification in the config and it isn't the default + if ((strlen(buffer) != 0) && (stricmp(buffer, "Default") != 0)) + { + #ifdef MCIMPEG + // Search for selection + for (int i = 0; i < mCount; i++) + { + if (stricmp(buffer, mMCIDevices[i].name) == 0) + { + device = mMCIDevices[i].name; + break; + } + } + #endif + } + + SetDeviceName(device); + } + + +/**************************************************************************** +* +* NAME +* ~MPGSettings +* +* DESCRIPTION +* Destructor +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::~MPGSettings(void) + { + if (mDeviceName) + free(mDeviceName); + + #ifdef MCIMPEG + if (mMCIDevices) + delete[] mMCIDevices; + #endif + } + + +/**************************************************************************** +* +* NAME +* SetDeviceName(DeviceName) +* +* DESCRIPTION +* Change current device used for mpeg playback +* +* INPUTS +* DeviceName - Device name type (IE: mpegvideo) +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::SetDeviceName(const char* deviceName) + { + if (mDeviceName) + free(mDeviceName); + + mDeviceName = NULL; + + if (deviceName) + mDeviceName = strdup(deviceName); + } + + +bool MPGSettings::Save(FileClass& file) + { + INIClass ini; + + if (ini.Load(file)) + { + const char* device = GetDeviceName(); + + if (device) + { + ini.Put_String("MovieSettings", "Device", device); + } + else + { + ini.Put_String("MovieSettings", "Device", "Default"); + } + + ini.Save(file); + return true; + } + + return false; + } + + +/**************************************************************************** +* +* NAME +* Dialog() +* +* DESCRIPTION +* Mpeg playback settings dialog +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::Dialog(void) + { + // Dialog & button dimensions + int d_dialog_w = 200 *RESFACTOR; + int d_dialog_h = 100 *RESFACTOR; + int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = 70 * RESFACTOR; + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 7 *RESFACTOR; + int d_margin = 7 *RESFACTOR; + + int d_okay_w = 40 *RESFACTOR; + int d_okay_h = 9 *RESFACTOR; + int d_okay_x = d_dialog_x + d_margin + 20; + int d_okay_y = ((d_dialog_y + d_dialog_h) - (d_okay_h + 20)); + + int d_test_w = 40 *RESFACTOR; + int d_test_h = 9 *RESFACTOR; + int d_test_x = (d_dialog_cx - (d_test_w / 2)); + int d_test_y = ((d_dialog_y + d_dialog_h) - (d_test_h + 20)); + + int d_cancel_w = 40 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = ((d_dialog_x + d_dialog_w) - (d_cancel_w + d_margin + 20)); + int d_cancel_y = ((d_dialog_y + d_dialog_h) - (d_cancel_h + 20)); + + int d_method_w = 165 * RESFACTOR; + int d_method_h = 50 * RESFACTOR; + int d_method_x = (d_dialog_cx - (d_method_w / 2)); + int d_method_y = (d_dialog_y + 40); + + // Button enumerations: + enum + { + BUTTON_OKAY = 100, + BUTTON_TEST, + BUTTON_CANCEL, + BUTTON_METHOD, + NUM_OF_BUTTONS = 4, + }; + + int num_of_buttons = NUM_OF_BUTTONS; + + // Redraw values: in order from "top" to "bottom" layer of the dialog + typedef enum + { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + // Dialog variables: + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + int i; + char* origDevice = NULL; + + // Buttons + ControlClass * commands = NULL; // the button list + + TextButtonClass okaybtn(BUTTON_OKAY, TXT_OK, TPF_BUTTON, + d_okay_x, d_okay_y, d_okay_w, d_okay_h); + + TextButtonClass testbtn(BUTTON_TEST, "Test", TPF_BUTTON, + d_test_x, d_test_y, d_test_w, d_test_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ListClass method(BUTTON_METHOD, d_method_x, d_method_y, d_method_w, + d_method_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + // Initialize + Set_Logic_Page(SeenBuff); + + // Create the list + commands = &okaybtn; + testbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + method.Add_Tail(*commands); + + // Fill array of button ptrs + curbutton = 0; + buttons[0] = &okaybtn; + buttons[1] = &testbtn; + buttons[2] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + // Add device to device control + method.Add_Item("DirectX Media (Default)"); + + #ifdef MCIMPEG + for (i = 0; i < mCount; i++) + { + if (mMCIDevices[i].description != NULL) + { + method.Add_Item(mMCIDevices[i].description); + } + } + #endif + + method.Set_Selected_Index(0); + + #ifdef MCIMPEG + // Search for current selection + if (GetDeviceName()) + { + for (i = 0; i < mCount; i++) + { + if (stricmp(GetDeviceName(), mMCIDevices[i].name) == 0) + { + method.Set_Selected_Index(i + 1); + break; + } + } + } + #endif + + // Save original device selection + if (GetDeviceName()) + origDevice = strdup(GetDeviceName()); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + + while (process) + { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) + { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + // Invoke game callback + Call_Back(); + + // Refresh display if needed + if (display) + { + Hide_Mouse(); + + if (display >= REDRAW_BACKGROUND) + { + // Refresh the backdrop + Load_Title_Page(true); + CCPalette.Set(); + + // Draw the background + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption("Movie Settings", d_dialog_x, d_dialog_y, d_dialog_w); + } + + // Redraw buttons + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Get user input + input = commands->Input(); + + // Process input + switch (input) + { + case (BUTTON_OKAY | KN_BUTTON): + selection = BUTTON_OKAY; + pressed = true; + break; + + case (BUTTON_TEST | KN_BUTTON): + selection = BUTTON_TEST; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + + if (curbutton > (num_of_buttons - 1)) + curbutton = 0; + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_OKAY; + pressed = true; + break; + + default: + break; + } + + if (pressed) + { + // to make sure the selection is correct in case they used the mouse + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton = selection - BUTTON_OKAY; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) + { + case (BUTTON_TEST): + buttons[curbutton]->IsPressed = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + Theme.Fade_Out(); + Hide_Mouse(); + VisiblePage.Clear(); + PlayMpegMovie("acrop"); + Keyboard->Clear(); + Show_Mouse(); + Theme.Queue_Song(THEME_CRUS); + display = REDRAW_ALL; + + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + buttons[0]->Turn_On(); + curbutton = 0; + break; + + case (BUTTON_OKAY): + process = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + { + RawFileClass file(CONFIG_FILE_NAME); + Save(file); + } + break; + + case (BUTTON_CANCEL): + process = false; + SetDeviceName(origDevice); + break; + } + + pressed = false; + } + } + + if (origDevice) + free(origDevice); + } + + +#ifdef MCIMPEG +/**************************************************************************** +* +* NAME +* EnumMCI(DeviceDesc, Context) +* +* DESCRIPTION +* MCI device enumeration callback +* +* INPUTS +* DeviceDesc - MCI device description +* Context - User defined context variable +* +* RESULT +* Continue - Continue with next device flag +* +****************************************************************************/ + +bool EnumMCI(MCIDevice* desc, void* context) + { + MPGSettings* mpgset = (MPGSettings*)context; + + // Digital video device type? + if (desc->type == MCI_DEVTYPE_DIGITAL_VIDEO) + { + if (MciMovie) + { + CCFileClass file; + const char* filename; + + filename = file.Set_Name("movies\\acrop.mpg"); + + if (!file.Is_Available()) + { + char buffer[256]; + sprintf(buffer, "Couldn't test MCI device %s\n", desc->name); + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(buffer); + Hide_Mouse(); + VisiblePage.Clear(); + return true; + } + + if (MciMovie->Open(filename, desc->name)) + { + MciMovie->Close(); + memcpy((void*)&mpgset->mMCIDevices[mpgset->mCount], (void*)desc, + sizeof(MCIDevice)); + mpgset->mCount++; + } + } + } + + return true; + } +#endif +#endif diff --git a/CODE/MPGSET.H b/CODE/MPGSET.H new file mode 100644 index 0000000..e5b8b2e --- /dev/null +++ b/CODE/MPGSET.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef _MPGSETTINGS_H_ +#define _MPGSETTINGS_H_ + +#include "function.h" + +#ifdef DVD +#include "mci.h" +#include "rawfile.h" + +class MPGSettings + { + public: + MPGSettings(const char* deviceName); + MPGSettings(FileClass& file); + virtual ~MPGSettings(void); + + void SetDeviceName(const char* device); + const char* GetDeviceName(void) const + {return mDeviceName;} + bool Save(FileClass& file); + void Dialog(void); + + char* mDeviceName; + + #ifdef MCIMPEG + MCI mMCI; + unsigned int mCount; + MCIDevice* mMCIDevices; + #endif + }; + +#endif // DVD +#endif // _MPGSETTINGS_H_ diff --git a/CODE/MPLAYER.CPP b/CODE/MPLAYER.CPP new file mode 100644 index 0000000..22ad48e --- /dev/null +++ b/CODE/MPLAYER.CPP @@ -0,0 +1,1249 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MPLAYER.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MPLAYER.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 14, 1995 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * Clear_Listbox -- clears the given list box * + * Clear_Vector -- clears the given NodeNameType vector * + * Computer_Message -- "sends" a message from the computer * + * Garble_Message -- "garbles" a message * + * Surrender_Dialog -- Prompts user for surrendering * + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#endif + +/*********************************************************************************************** + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_NORMAL, GAME_MODEM, etc. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +GameType Select_MPlayer_Game (void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 190 *RESFACTOR; +#ifdef WOLAPI_INTEGRATION + int d_dialog_h = 89 * RESFACTOR; // ajw + int d_dialog_y = (((255 * RESFACTOR) - d_dialog_h) / 2); +#else + int d_dialog_h = 78 *RESFACTOR; + int d_dialog_y = 90 * RESFACTOR; +#endif + int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 7 *RESFACTOR; + int d_margin = 7 *RESFACTOR; + + int d_modemserial_w = 80 *RESFACTOR; + int d_modemserial_h = 9 *RESFACTOR; + int d_modemserial_x = d_dialog_cx - d_modemserial_w / 2; + int d_modemserial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_skirmish_w = 80 *RESFACTOR; + int d_skirmish_h = 9 *RESFACTOR; + int d_skirmish_x = d_dialog_cx - d_skirmish_w / 2; + int d_skirmish_y = d_modemserial_y + d_modemserial_h + 2*RESFACTOR; + + int d_ipx_w = 80 *RESFACTOR; + int d_ipx_h = 9 *RESFACTOR; + int d_ipx_x = d_dialog_cx - d_ipx_w / 2; + int d_ipx_y = d_skirmish_y + d_skirmish_h + 2*RESFACTOR; + +#ifdef WOLAPI_INTEGRATION + // ajw 7/2/98 - added button + int d_wol_w = 80 * RESFACTOR; + int d_wol_h = 9 * RESFACTOR; + int d_wol_x = d_dialog_cx - d_wol_w / 2; + int d_wol_y = d_ipx_y + d_ipx_h + 2*RESFACTOR; +#endif + + int d_cancel_w = 60 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; +#ifdef WOLAPI_INTEGRATION + int d_cancel_y = d_wol_y + d_wol_h + d_margin; +#else + int d_cancel_y = d_ipx_y + d_ipx_h + d_margin; +#endif + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + //------------------------------------------------------------------------ + // Button enumerations: + //------------------------------------------------------------------------ + enum { + BUTTON_MODEMSERIAL = 100, + BUTTON_SKIRMISH, + BUTTON_IPX, +#ifdef WOLAPI_INTEGRATION + BUTTON_WOL, // ajw +#endif + BUTTON_CANCEL, + +#ifdef WOLAPI_INTEGRATION + NUM_OF_BUTTONS = 5, // ajw +#else + NUM_OF_BUTTONS = 4, +#endif + }; + + int num_of_buttons = NUM_OF_BUTTONS - (Ipx.Is_IPX() ? 0 : 1); + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables: + //------------------------------------------------------------------------ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + GameType retval; // return value + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + + //------------------------------------------------------------------------ + // If IPX not active then do only the modem serial dialog + //------------------------------------------------------------------------ +// if ( !Ipx.Is_IPX() ) { +// return( Select_Serial_Dialog() ); +// } + + + TextButtonClass modemserialbtn (BUTTON_MODEMSERIAL, TXT_MODEM_SERIAL, TPF_BUTTON, + d_modemserial_x, d_modemserial_y, d_modemserial_w, d_modemserial_h); + + TextButtonClass skirmishbtn (BUTTON_SKIRMISH, TXT_SKIRMISH, TPF_BUTTON, + d_skirmish_x, d_skirmish_y, d_skirmish_w, d_skirmish_h); + + TextButtonClass ipxbtn (BUTTON_IPX, TXT_NETWORK, TPF_BUTTON, + d_ipx_x, d_ipx_y, d_ipx_w, d_ipx_h); + +#ifdef WOLAPI_INTEGRATION + // ajw + TextButtonClass wolbtn(BUTTON_WOL, TXT_WOL_INTERNETBUTTON, TPF_BUTTON, + d_wol_x, d_wol_y, d_wol_w, d_wol_h); +#endif + + if(!Ipx.Is_IPX()) { + d_cancel_y = d_ipx_y; + d_dialog_h -= d_cancel_h; + } + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + VisiblePage.Blit(seen_buff_save); +#endif + //------------------------------------------------------------------------ + // Create the list + //------------------------------------------------------------------------ + commands = &modemserialbtn; + skirmishbtn.Add_Tail(*commands); + if(Ipx.Is_IPX()) { + ipxbtn.Add_Tail(*commands); + } +#ifdef WOLAPI_INTEGRATION + wolbtn.Add_Tail(*commands); // ajw +#endif + cancelbtn.Add_Tail(*commands); + + //------------------------------------------------------------------------ + // Fill array of button ptrs + //------------------------------------------------------------------------ + curbutton = 0; + buttons[0] = &modemserialbtn; + buttons[1] = &skirmishbtn; + if(Ipx.Is_IPX()) { + buttons[2] = &ipxbtn; +#ifdef WOLAPI_INTEGRATION + buttons[3] = &wolbtn; // ajw + buttons[4] = &cancelbtn; +#else + buttons[3] = &cancelbtn; +#endif + } else { +#ifdef WOLAPI_INTEGRATION + buttons[2] = &wolbtn; // ajw + buttons[3] = &cancelbtn; +#else + buttons[2] = &cancelbtn; +#endif + } + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + seen_buff_save.Blit(VisiblePage); + display = REDRAW_ALL; + } + #endif + + //..................................................................... + // Invoke game callback + //..................................................................... + Call_Back(); + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + //............................................................... + // Refresh the backdrop + //............................................................... + Load_Title_Page(true); + CCPalette.Set(); + + //............................................................... + // Draw the background + //............................................................... + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_SELECT_MPLAYER_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_MODEMSERIAL | KN_BUTTON): + selection = BUTTON_MODEMSERIAL; + pressed = true; + break; + + case (BUTTON_SKIRMISH | KN_BUTTON): + selection = BUTTON_SKIRMISH; + pressed = true; + break; + + case (BUTTON_IPX | KN_BUTTON): + selection = BUTTON_IPX; + pressed = true; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOL | KN_BUTTON): // ajw + selection = BUTTON_WOL; + pressed = true; + break; +#endif + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (num_of_buttons - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_MODEMSERIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + + //.................................................................. + // to make sure the selection is correct in case they used the mouse + //.................................................................. + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_MODEMSERIAL; + if(selection == BUTTON_CANCEL && !Ipx.Is_IPX()) curbutton--; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_MODEMSERIAL): + + //............................................................ + // Pop up the modem/serial/com port dialog + //............................................................ + retval = Select_Serial_Dialog(); + + if (retval != GAME_NORMAL) { + process = false; + } else { + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + } + break; + + case (BUTTON_SKIRMISH): + Session.Type = GAME_SKIRMISH; + if (Com_Scenario_Dialog(true)) { + retval = GAME_SKIRMISH; + process = false; +#ifdef FIXIT_VERSION_3 + bAftermathMultiplayer = Is_Aftermath_Installed(); + // ajw I'll bet this was needed before also... + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); +#endif + } else { + buttons[curbutton]->IsPressed = false; + Session.Type = GAME_NORMAL; + display = REDRAW_ALL; + } + break; + + case (BUTTON_IPX): + retval = GAME_IPX; + process = false; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOL): // ajw + retval = GAME_INTERNET; + process = false; + break; +#endif + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } + return(retval); + +} /* end of Select_MPlayer_Game */ + + +/*************************************************************************** + * Clear_Listbox -- clears the given list box * + * * + * This routine assumes the items in the given list box are character * + * buffers; it deletes each item in the list, then clears the list. * + * * + * INPUT: * + * list ptr to listbox * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Listbox(ListClass * list) +{ + char * item; + + //------------------------------------------------------------------------ + // Clear the list box + //------------------------------------------------------------------------ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + list->Flag_To_Redraw(); + +} // end of Clear_Listbox + + +/*************************************************************************** + * Clear_Vector -- clears the given NodeNameType vector * + * * + * INPUT: * + * vector ptr to vector to clear * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Vector(DynamicVectorClass * vector) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the 'Players' Vector + //------------------------------------------------------------------------ + for (i = 0; i < vector->Count(); i++) { + delete (*vector)[i]; + } + vector->Clear(); + +} // end of Clear_Vector + + +/*************************************************************************** + * Computer_Message -- "sends" a message from the computer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +void Computer_Message(void) +{ +#ifdef NEVER + int color; + HousesType house; + HouseClass * ptr; + + //------------------------------------------------------------------------ + // Find the computer house that the message will be from + //------------------------------------------------------------------------ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + //..................................................................... + // Decode this house's color + //..................................................................... + color = ptr->RemapColor; + + //..................................................................... + // We now have a 1/4 chance of echoing one of the human players' + // messages back. + //..................................................................... + if (Percent_Chance(25)) { + + //.................................................................. + // Now we have a 1/3 chance of garbling the human message. + //.................................................................. + if (Percent_Chance(33)) { + Garble_Message(Session.LastMessage); + } + + //.................................................................. + // Only add the message if there is one to add. + //.................................................................. + if (strlen(Session.LastMessage)) { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Session.LastMessage, + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + else { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Text_String(TXT_COMP_MSG1 + Random_Pick(0, 12)), + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return; + } +#endif +} /* end of Computer_Message */ + + +#ifdef NEVER +/*************************************************************************** + * Garble_Message -- "garbles" a message * + * * + * INPUT: * + * buf buffer to garble; stores output message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +static void Garble_Message(char * buf) +{ + char txt[80]; + char punct[20]; // for punctuation + char * p; // working ptr + int numwords; // # words in the phrase + char * words[40]; // ptrs to various words in the phrase + int i,j; + + //------------------------------------------------------------------------ + // Pull off any trailing punctuation + //------------------------------------------------------------------------ + p = buf + strlen(buf) - 1; + while (1) { + if (p < buf) + break; + if (p[0]=='!' || p[0]=='.' || p[0]=='?') { + p--; + } + else { + p++; + break; + } + if (strlen(p) >= (sizeof(punct) - 1) ) { + break; + } + } + strcpy (punct, p); + p[0] = 0; + + for (i = 0; i < 40; i++) { + words[i] = NULL; + } + + //------------------------------------------------------------------------ + // Copy the original buffer + //------------------------------------------------------------------------ + strcpy(txt, buf); + + //------------------------------------------------------------------------ + // Split it up into words + //------------------------------------------------------------------------ + p = strtok (txt, " "); + numwords = 0; + while (p) { + words[numwords] = p; + numwords++; + p = strtok (NULL, " "); + } + + //------------------------------------------------------------------------ + // Now randomly put the words back. Don't use the real random-number + // generator, since different machines will have different LastMessage's, + // and will go out of sync. + //------------------------------------------------------------------------ + buf[0] = 0; + for (i = 0; i < numwords; i++) { + j = Sim_IRandom(0, numwords); + if (words[j] == NULL) { // this word has been used already + i--; + continue; + } + strcat(buf, words[j]); + words[j] = NULL; + if (i < numwords-1) + strcat(buf, " "); + } + strcat(buf, punct); + +} /* end of Garble_Message */ +#endif + + +/*************************************************************************** + * Surrender_Dialog -- Prompts user for surrendering * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = user cancels, 1 = user wants to surrender. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(int text) +{ + return Surrender_Dialog( Text_String( text ) ); +} +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text) +#else +int Surrender_Dialog(int text) +#endif +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 240*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + D_DIALOG_X = ((320*RESFACTOR - D_DIALOG_W) / 2),// centered x-coord + D_DIALOG_Y = ((200*RESFACTOR - D_DIALOG_H) / 2),// centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_OK_W = 45*RESFACTOR, // OK width + D_OK_H = 9*RESFACTOR, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5*RESFACTOR, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN*2, // OK y + + D_CANCEL_W = 45*RESFACTOR, // Cancel width + D_CANCEL_H = 9*RESFACTOR, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5*RESFACTOR, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN*2, // Cancel y + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + buttons[0] = &okbtn; + buttons[1] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... +#ifdef FIXIT_VERSION_3 // Stalemate games. + Fancy_Text_Print(text, + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(Text_String(text), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_OK | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_CANCEL | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +/*************************************************************************** + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = user confirms abort, 0 = user cancels * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +int Abort_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 170*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + D_DIALOG_X = ((320*RESFACTOR - D_DIALOG_W) / 2),// centered x-coord + D_DIALOG_Y = ((200*RESFACTOR - D_DIALOG_H) / 2),// centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_YES_W = 45*RESFACTOR, // YES width + D_YES_H = 9*RESFACTOR, // YES height + D_YES_X = D_DIALOG_CX - D_YES_W - 5*RESFACTOR, // YES x + D_YES_Y = D_DIALOG_Y + D_DIALOG_H - D_YES_H - D_MARGIN*2, // YES y + + D_NO_W = 45*RESFACTOR, // Cancel width + D_NO_H = 9*RESFACTOR, // Cancel height + D_NO_X = D_DIALOG_CX + 5*RESFACTOR, // Cancel x + D_NO_Y = D_DIALOG_Y + D_DIALOG_H - D_NO_H - D_MARGIN*2, // Cancel y + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_YES = 100, + BUTTON_NO, + }; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, TPF_BUTTON, D_YES_X, D_YES_Y, D_YES_W, D_YES_H); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, TPF_BUTTON, D_NO_X, D_NO_Y, D_NO_W, D_NO_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &yesbtn; + nobtn.Add_Tail(*commands); + + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... + Fancy_Text_Print(Text_String(TXT_CONFIRM_EXIT), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_YES | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_NO | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +#if(TEN) +/*************************************************************************** + * Read_TEN_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_TEN_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + if (Session.TenPlayerID == -1) { + return (0); + } + Session.ColorIdx = (PlayerColorType)Session.TenPlayerID; + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + Session.NumPlayers = ini.Get_Int("Options", "Players", 2); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * Read_MPATH_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_MPATH_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + Session.ColorIdx = (PlayerColorType)ini.Get_Int("Options", "Color", 0); + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // MPATH + +/************************** end of mplayer.cpp *****************************/ diff --git a/CODE/MPLIB.CPP b/CODE/MPLIB.CPP new file mode 100644 index 0000000..2fcdc3f --- /dev/null +++ b/CODE/MPLIB.CPP @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +#include "types.h" +#include "mgenord.h" +#include "magic.h" +#include "rtq.h" +#include +#include +#include +#include "mplib.h" + +#define CHUNNEL_INT 0x48 + +typedef union REGS REGISTERS; + +void +Yield(void) +{ + REGISTERS regs; + + regs.w.ax = 0x1680; + int386(0x2f, ®s, ®s); +} + +void +PostWindowsMessage(void) +{ + REGISTERS regs; + + regs.x.eax = DPMIAPI_POST_WINDOWS_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = 0; + regs.x.ecx = 0; + int386(CHUNNEL_INT, ®s, ®s); +} + +int MGenGetQueueCtr(int qNo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETQUEUECTR_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qNo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +RTQ_NODE *MGenMoveTo(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MOVENODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetNode(int q) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = q; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetMasterNode(unsigned *size) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MASTERNODE_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + *size = regs.x.ecx; + + return (RTQ_NODE *) regs.x.eax; +} + +int MGenFlushNodes(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_FLUSHNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenMCount(unsigned lowerOrderBits, unsigned upperOrderBits) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MCOUNT_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = lowerOrderBits; + regs.x.ecx = upperOrderBits; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenSanityCheck(void) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_SANITYCHECK_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + \ No newline at end of file diff --git a/CODE/MPLPC.CPP b/CODE/MPLPC.CPP new file mode 100644 index 0000000..f475df5 --- /dev/null +++ b/CODE/MPLPC.CPP @@ -0,0 +1,127 @@ +/* +** 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 . +*/ + +extern "C" { +#include "lpc.h" +#include "types.h" +#include "services.h" +#include "rtq.h" +#include +#include +}; +#include "mplib.h" + + +#define IDLE_QUEUE 6 +#define REC_QUEUE 7 +#define SEND_QUEUE 8 + +void +SetLPCData(LPCData *lpc) +{ + lpc->version = 1; + lpc->sizeOfArgs = 0; + lpc->service = LPC_NOSERVICE; +} + +void +GetGameDef(void *gameDef, int* len) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + + if (!n) { + *len = 0; + return; // can't get service! + } + + p = (LPCData *) n->rtqDatum; + SetLPCData(p); + p->service = LPC_GENGETGAMEDEF; + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + + // make call + PostWindowsMessage(); + + // wait for return + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) n->rtqDatum; + + if (r->sizeOfReturn > *len || r->error != LPC_NOERROR) { + *len = 0; + return; + } + + *len = r->sizeOfReturn; + memcpy(gameDef, r->Data, r->sizeOfReturn); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); +} + +int +LPCGetMPAddr(void) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + int retVal; + + p = (LPCData *) n->rtqDatum; + SetLPCData(p); + p->service = LPC_GETMPADDR; + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) n->rtqDatum; + + if (r->sizeOfReturn != sizeof(int) || r->error != LPC_NOERROR) { + return -1; + } + + retVal = *((int *) r->Data); + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + return retVal; +} + +void +NullLPC(void) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + + p = (LPCData *) n->rtqDatum; + SetLPCData(p); + p->service = LPC_NULLSERVICE; + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); +} + \ No newline at end of file diff --git a/CODE/MPMGRD.CPP b/CODE/MPMGRD.CPP new file mode 100644 index 0000000..fc11b6f --- /dev/null +++ b/CODE/MPMGRD.CPP @@ -0,0 +1,366 @@ +/* +** 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 . +*/ + +#include "mpmgrd.h" + +extern "C" { +#include +#include +#include +#include +#include "types.h" +#include "rtq.h" +#include "services.h" +} +#include "mplib.h" +#include "mplpc.h" + + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +typedef struct { + DWORD address; + char Data[1]; +} packet; + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +#define FREEQUEUE 0 +#define DOSWORKQUEUE 1 +#define WINWORKQUEUE 2 +#define WINSENDQUEUE 3 +#define DOSPENDINGQUEUE 4 +#define WINWORKQUEUE2 5 + +// 6, 7, 8, taken up by LPC services + +#define GDOSWORKQUEUE 14 +#define GWINWORKQUEUE 15 +#define GWINSENDQUEUE 16 +#define GDOSPENDINGQUEUE 17 +#define GWINWORKQUEUE2 18 + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + unsigned size; + + MGenGetMasterNode(&size); + if (size != sizeof(RTQ_NODE)) { + exit(-234); + } + _myAddr = LPCGetMPAddr(); + _nConnections = 0; + + for (int i = 0; i < CONNECT_MAX; i++) { + _Connections[i] = 0; + strcpy(_Names[i], ""); + } +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int /* ack_req */, + int conn_id) +{ + RTQ_NODE *n; + int idx = Connection_Index(conn_id); + + if (_nConnections == 0) { + return (STATUS_OK); + } + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (conn_id == CONNECTION_NONE) { + p->address = BROADCAST_ADDR; + } else { + p->address = _Connections[idx]; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, WINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + RTQ_NODE *n; + int i; + + if ((n = MGenMoveTo(DOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + *conn_id = CONNECTION_NONE; + for (i = 0; i < _nConnections; i++) { + if (p->address == _Connections[i]) { + (*conn_id) = _ID[i]; + break; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int /*ack_req*/, + int address) +{ + RTQ_NODE *n; + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (address == 0) { + p->address = BROADCAST_ADDR; + } else { + p->address = address; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, GWINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + RTQ_NODE *n; + + if ((n = MGenMoveTo(GDOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + if (address) { + if (p->address == BROADCAST_ADDR) { + *address = 0; + } else { + *address = p->address; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + _Connections[_nConnections] = address; + _ID[_nConnections] = id; + strcpy(_Names[_nConnections], name); + _nConnections++; + return STATUS_OK; +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == -1) + return 0; + + for (i = idx; i < _nConnections - 1; i++) { + _Connections[i] = _Connections[i+1]; + _ID[i] = _ID[i+1]; + strcpy (_Names[i], _Names[i+1]); + } + _nConnections--; + return STATUS_OK; +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (NULL); + } + + return _Names[idx]; +} + +int +MPlayerManClass::Connection_Address(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (0); + } + + return _Connections[idx]; +} + +int +MPlayerManClass::Num_Connections(void) +{ + return _nConnections; +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return _ID[index]; +} + +int +MPlayerManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < _nConnections; i++) { + if (_ID[i] == id) { + return i; + } + } + + return -1; +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return MGenGetQueueCtr(DOSPENDINGQUEUE); +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return STATUS_OK; +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + TGAMEDEF game_def; + int sz = sizeof(game_def); + + GetGameDef(&game_def, &sz); + + return (game_def.numPlayers - 1); + +} + + +void MPlayerManClass::Flush_All(void) +{ + MGenFlushNodes(DOSPENDINGQUEUE, FREEQUEUE); + MGenFlushNodes(GDOSPENDINGQUEUE, FREEQUEUE); +} + diff --git a/CODE/MPMGRD.H b/CODE/MPMGRD.H new file mode 100644 index 0000000..3d962dc --- /dev/null +++ b/CODE/MPMGRD.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + int _myAddr; + int _Connections[CONNECT_MAX]; + int _ID[CONNECT_MAX]; + char _Names[CONNECT_MAX][MAX_NAME_LEN]; + int _nConnections; +}; + +#endif // mpmgr_h + + \ No newline at end of file diff --git a/CODE/MPMGRW.CPP b/CODE/MPMGRW.CPP new file mode 100644 index 0000000..207e72a --- /dev/null +++ b/CODE/MPMGRW.CPP @@ -0,0 +1,298 @@ +/* +** 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 . +*/ + +#ifndef MP_LOAD_DLL_DYNAMICALLY +#define MP_LOAD_DLL_DYNAMICALLY +#endif + +#include "mpmgrw.h" + +extern "C" { +#include +#include +#include +#include +} + +#include + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +#define PRIVATE 0 +#define PUBLIC 1 + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef void __cdecl (*MPlayerInit_Type)(void); +typedef void __cdecl (*MPlayerDestroy_Type)(void); +typedef int __cdecl (*Send_Private_Message_Type)(void *buf, + int buflen, + int ack_req, + int conn_id); +typedef int __cdecl (*Get_Private_Message_Type)(void *buf, int *buflen, + int *conn_id); +typedef int __cdecl (*Send_Global_Message_Type)(void *buf, int buflen, int ack_req, + int addr); +typedef int __cdecl (*Get_Global_Message_Type)(void *buf, int *buflen, int *address); +typedef int __cdecl (*Create_Connection_Type)(int id, char *name, int address); +typedef int __cdecl (*Delete_Connection_Type)(int id); +typedef char * __cdecl (*Connection_Name_Type)(int id); +typedef int __cdecl (*Connection_Address_Type)(int id); +typedef int __cdecl (*Num_Connections_Type)(void); +typedef int __cdecl (*Connection_ID_Type)(int id); +typedef int __cdecl (*Connection_Index_Type)(int id); +typedef int __cdecl (*Init_Type)(void); +typedef int __cdecl (*Find_Num_Connections_Type)(void); + +MPlayerInit_Type MPlayerManCreate; +MPlayerDestroy_Type MPlayerManDestroy; +Send_Private_Message_Type Send_Private_Message_DLL; +Get_Private_Message_Type Get_Private_Message_DLL; +Send_Global_Message_Type Send_Global_Message_DLL; +Get_Global_Message_Type Get_Global_Message_DLL; +Create_Connection_Type Create_Connection_DLL; +Delete_Connection_Type Delete_Connection_DLL; +Connection_Name_Type Connection_Name_DLL; +Connection_Address_Type Connection_Address_DLL; +Num_Connections_Type Num_Connections_DLL; +Connection_ID_Type Connection_ID_DLL; +Connection_Index_Type Connection_Index_DLL; +Init_Type Init_DLL; +Find_Num_Connections_Type Find_Num_Connections_DLL; + + +FARPROC MPGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + FARPROC ret=GetProcAddress(hModule, lpProcName); + if (!ret) { + char msg[255]; + sprintf(msg, "Unable to load function %s from %s", + lpProcName, "RA95MP.DLL"); + MessageBox(0, msg, "Warning", MB_OK); + } + return ret; +} + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + HMODULE lib; + + lib = LoadLibrary("ra95mp.dll"); + if (lib != 0) { + MPlayerManCreate = (MPlayerInit_Type) MPGetProcAddress(lib, + "MPlayerManCreate"); + MPlayerManDestroy = (MPlayerDestroy_Type) MPGetProcAddress(lib, + "MPlayerManDestroy"); + Send_Private_Message_DLL = (Send_Private_Message_Type) MPGetProcAddress(lib, + "Send_Private_Message"); + Get_Private_Message_DLL = (Get_Private_Message_Type) MPGetProcAddress(lib, + "Get_Private_Message"); + Send_Global_Message_DLL = (Send_Global_Message_Type) MPGetProcAddress(lib, + "Send_Global_Message"); + Get_Global_Message_DLL = (Get_Global_Message_Type) MPGetProcAddress(lib, + "Get_Global_Message"); + Create_Connection_DLL = (Create_Connection_Type) MPGetProcAddress(lib, + "Create_Connection"); + Delete_Connection_DLL = (Delete_Connection_Type) MPGetProcAddress(lib, + "Delete_Connection"); + Connection_Name_DLL = (Connection_Name_Type) MPGetProcAddress(lib, + "Connection_Name"); + Connection_Address_DLL = (Connection_Address_Type) MPGetProcAddress(lib, + "Connection_Address"); + Num_Connections_DLL = (Num_Connections_Type) MPGetProcAddress(lib, + "Num_Connections"); + Connection_ID_DLL = (Connection_ID_Type) MPGetProcAddress(lib, + "Connection_ID"); + Connection_Index_DLL = (Connection_Index_Type) MPGetProcAddress(lib, + "Connection_Index"); + Init_DLL = (Init_Type) MPGetProcAddress(lib, + "Init"); + Find_Num_Connections_DLL = (Find_Num_Connections_Type) MPGetProcAddress(lib, + "Find_Num_Connections"); + } else { + MessageBox(0, "RA95MP.DLL not found!", "Warning", MB_OK); + exit(0); + } + + if (MPlayerManCreate != 0) { + MPlayerManCreate(); + } +} + +MPlayerManClass::~MPlayerManClass() +{ + MPlayerManDestroy(); +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int ack_req, + int conn_id) +{ + return Send_Private_Message_DLL(buf, buflen, ack_req, conn_id); +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + return Get_Private_Message_DLL(buf, buflen, conn_id); +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int ack_req, + int addr) +{ + return Send_Global_Message_DLL(buf, buflen, ack_req, addr); +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + return Get_Global_Message_DLL(buf, buflen, address); +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + return Create_Connection_DLL(id, name, address); +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + return Delete_Connection_DLL(id); +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + return Connection_Name_DLL(id); +} + +int +MPlayerManClass::Connection_Address(int id) +{ + return Connection_Address_DLL(id); +} + +int +MPlayerManClass::Num_Connections(void) +{ + return Num_Connections_DLL(); +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return Connection_ID_DLL(index); +} + +int +MPlayerManClass::Connection_Index(int id) +{ + return Connection_Index_DLL(id); +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return 0; +// return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return 0; +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return Init_DLL(); +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + return Find_Num_Connections_DLL(); +} + + +void MPlayerManClass::Flush_All(void) +{ +} + diff --git a/CODE/MPMGRW.H b/CODE/MPMGRW.H new file mode 100644 index 0000000..059d31a --- /dev/null +++ b/CODE/MPMGRW.H @@ -0,0 +1,88 @@ +/* +** 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 . +*/ + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" +#include "mplayer.h" +#include + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + ~MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + //HGULP _gulp; + //HGULP _pgulp; + //GAMEDEF _gameDef; + //int _myAddr; + //int _Connections[CONNECT_MAX]; + //int _ID[CONNECT_MAX]; + //char _Names[CONNECT_MAX][MAX_NAME_LEN]; + //int _nConnections; +}; + +#endif // mpmgr_h + diff --git a/CODE/MPU.CPP b/CODE/MPU.CPP new file mode 100644 index 0000000..7b63f12 --- /dev/null +++ b/CODE/MPU.CPP @@ -0,0 +1,48 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MPU.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MPU.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mpu.h" + + +#ifdef __BORLANDC__ +unsigned long __cdecl Get_CPU_Clock(unsigned long & high) +{ + __asm db 0fh,031h + __asm mov [high],edx + return(_EAX); +} +#endif diff --git a/CODE/MPU.H b/CODE/MPU.H new file mode 100644 index 0000000..9e0f0e4 --- /dev/null +++ b/CODE/MPU.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MPU.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MPU.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/15/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MPU_H +#define MPU_H + + +/*********************************************************************************************** + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * * + * This routine will return the internal Pentium clock accumulator. This accumulator is * + * incremented every clock tick. Since this clock value can get very very large, the value * + * returned is in 64 bits. The low half is returned directly, the high half is stored in * + * location specified. * + * * + * INPUT: high -- Reference to the high value of the 64 bit clock number. * + * * + * OUTPUT: Returns with the low half of the CPU clock value. * + * * + * WARNINGS: This instruction is only available on Pentium or later processors. * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +unsigned long Get_CPU_Clock(unsigned long & high); +#ifndef __BORLANDC__ +#pragma aux Get_CPU_Clock parm [esi] \ + modify [edx] \ + value [eax] = \ + "db 0fh,031h" \ + "mov [esi],edx" +#endif + +#endif diff --git a/CODE/MSGBOX.CPP b/CODE/MSGBOX.CPP new file mode 100644 index 0000000..01221ee --- /dev/null +++ b/CODE/MSGBOX.CPP @@ -0,0 +1,518 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MSGBOX.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMessageBox::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + + +#ifdef FIXIT_VERSION_3 +bool cancel_current_msgbox = false; +#endif + +/*********************************************************************************************** + * WWMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int WWMessageBox::Process(const char * msg, const char * b1txt, const char * b2txt, const char * b3txt, bool preserve) +{ +#define BUFFSIZE (511) + char buffer[BUFFSIZE]; + bool retval; + bool process; // loop while true + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + void * back; + BOOL display; // display level + int realval[5]; + +#ifndef WIN32 + int preservex,preservey,preservew,preserveh; +#endif + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + if (b1txt != NULL && *b1txt == '\0') b1txt = NULL; + if (b2txt != NULL && *b2txt == '\0') b2txt = NULL; + if (b3txt != NULL && *b3txt == '\0') b3txt = NULL; + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt != NULL) { + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2 * RESFACTOR); + bwidth = max((String_Pixel_Width(b1txt) + (8 * RESFACTOR)), (30 * RESFACTOR)); + + if (b2txt != NULL) { + numbuttons = 2; + bwidth = max((String_Pixel_Width( b2txt ) + (8 * RESFACTOR)), bwidth); + + if (b3txt != NULL) { + numbuttons = 3; + } + + } else { + numbuttons = 1; + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-1); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int width; + int height; + int lines = Format_Window_String(buffer, 255 * RESFACTOR, width, height); + TextPrintType tpf = TPF_TEXT; + + width = max(width, (90 * RESFACTOR)); + width += 40 * RESFACTOR; + height += (numbuttons == 0) ? (40 * RESFACTOR) : (60 * RESFACTOR); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + int printx = x + (20 * RESFACTOR); + + /* + ** Special hack to center a one line dialog box text. + */ + if (lines == 1) { + printx = x + width/2; + tpf = tpf | TPF_CENTER; + } + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + #ifdef WIN32 + VisiblePage.Blit(seen_buff_save); + #endif + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + //DOS BUILD GERMAN BUTTONS NEED TO ONE ON TOP OF THE OTHER VG 11/6/96 + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Center button. + */ + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Right button. + */ + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, 0, y + height - (bheight + (15 * RESFACTOR))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + memset(buttons, '\0', sizeof(buttons)); + if (numbuttons > 0) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else { + if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { +#ifndef WIN32 + preservex = max(0, x-4); + preservey = max(0, y-4); + preservew = min(width+8, 320-preservex); + preserveh = min(height+8, 200-preservey); + back = new char[preservew * preserveh]; + SeenBuff.To_Buffer(preservex, preservey, preservew, preserveh, back, preservew * preserveh); +#else + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); +#endif + } + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + seen_buff_save.Blit(VisiblePage); + display = true; + } + #endif + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); +#ifdef FIXIT_VERSION_3 + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = KN_ESC; + } +#endif + switch (input) { + case (KN_ESC): + selection = realval[numbuttons-1]; + pressed = true; + +#ifdef NEVER + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } +#endif + break; + + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (BUTTON_2|BUTTON_FLAG): + if (numbuttons > 2) { + selection = realval[2]; + } else { + selection = realval[1]; + } + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = realval[curbutton]; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + break; + } + + if (pressed) { + + TextButtonClass * toggle; + /* + ** Turn all the buttons off. + */ + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_1); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_2); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_3); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + + /* + ** Turn on and depress the button that was selected. + */ + if (selection == BUTTON_1 || selection == BUTTON_2 || selection == BUTTON_3) { + TextButtonClass * toggle = (TextButtonClass *)buttonlist->Extract_Gadget(selection); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + } + Hide_Mouse(); + buttonlist->Draw_All(true); + Show_Mouse(); + + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()) { + #ifdef WIN32 + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + #else + MCGA_Buffer_To_Page(preservex, preservey, preservew, preserveh, back, &SeenBuff); + #endif + } + SeenBuff.Unlock(); + + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int WWMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int WWMessageBox::Process(char const * msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/CODE/MSGBOX.CPP.BAK b/CODE/MSGBOX.CPP.BAK new file mode 100644 index 0000000..cd296b3 --- /dev/null +++ b/CODE/MSGBOX.CPP.BAK @@ -0,0 +1,509 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\msgbox.cpv 4.83 29 Oct 1996 23:49:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMessageBox::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + + +/*********************************************************************************************** + * WWMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int WWMessageBox::Process(const char * msg, const char * b1txt, const char * b2txt, const char * b3txt, bool preserve) +{ +#define BUFFSIZE (511) + char buffer[BUFFSIZE]; + bool retval; + bool process; // loop while true + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + void * back; + BOOL display; // display level + int realval[5]; + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + if (*b1txt == '\0') b1txt = 0; + if (*b2txt == '\0') b2txt = 0; + if (*b3txt == '\0') b3txt = 0; + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt != NULL) { + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2 * RESFACTOR); + bwidth = max((String_Pixel_Width(b1txt) + (8 * RESFACTOR)), (30 * RESFACTOR)); + + if (b2txt != NULL) { + numbuttons = 2; + bwidth = max((String_Pixel_Width( b2txt ) + (8 * RESFACTOR)), bwidth); + + if (b3txt != NULL) { + numbuttons = 3; + } + + } else { + numbuttons = 1; + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-1); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int width; + int height; + int lines = Format_Window_String(buffer, 255 * RESFACTOR, width, height); + TextPrintType tpf = TPF_TEXT; + + width = max(width, (90 * RESFACTOR)); + width += 40 * RESFACTOR; + height += (numbuttons == 0) ? (40 * RESFACTOR) : (60 * RESFACTOR); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + int printx = x + (20 * RESFACTOR); + + /* + ** Special hack to center a one line dialog box text. + */ + if (lines == 1) { + printx = x + width/2; + tpf = tpf | TPF_CENTER; + } + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + #ifdef WIN32 + VisiblePage.Blit(seen_buff_save); + #endif + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + + //DOS BUILD GERMAN BUTTONS NEED TO ONE ON TOP OF THE OTHER VG 11/6/96 + + #if (GERMAN) && (!WIN32) + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Center button. + */ + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + #endif + + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Center button. + */ + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Right button. + */ + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, 0, y + height - (bheight + (15 * RESFACTOR))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + memset(buttons, '\0', sizeof(buttons)); + if (numbuttons > 0) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else { + if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); + } + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + seen_buff_save.Blit(VisiblePage); + display = true; + } + #endif + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); + switch (input) { + case (KN_ESC): + selection = realval[numbuttons-1]; + pressed = true; + +#ifdef NEVER + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } +#endif + break; + + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (BUTTON_2|BUTTON_FLAG): + if (numbuttons > 2) { + selection = realval[2]; + } else { + selection = realval[1]; + } + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = realval[curbutton]; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + break; + } + + if (pressed) { + +//#ifdef NEVER + TextButtonClass * toggle; + /* + ** Turn all the buttons off. + */ + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_1); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_2); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_3); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + +//#endif + /* + ** Turn on and depress the button that was selected. + */ + if (selection == BUTTON_1 || selection == BUTTON_2 || selection == BUTTON_3) { + TextButtonClass * toggle = (TextButtonClass *)buttonlist->Extract_Gadget(selection); + if (toggle != NULL) { +// toggle->Turn_On(); + toggle->IsOn = true; + toggle->IsPressed = true; + } + } + Hide_Mouse(); + buttonlist->Draw_All(true); + Show_Mouse(); + + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()) { + #ifdef WIN32 + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + #else + MCGA_Buffer_To_Page(x, y, width, height, back, &SeenBuff); + #endif + } + SeenBuff.Unlock(); + + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int WWMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int WWMessageBox::Process(char const * msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/CODE/MSGBOX.H b/CODE/MSGBOX.H new file mode 100644 index 0000000..434ad4a --- /dev/null +++ b/CODE/MSGBOX.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MSGBOX.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGBOX_H +#define MSGBOX_H + +#include "jshell.h" + +class WWMessageBox +{ + int Caption; + + public: + WWMessageBox(int caption=TXT_NONE) {Caption = caption;}; + int Process(const char *msg, const char *b1txt, const char *b2txt=NULL, const char *b3txt=NULL, bool preserve=false); + int Process(int msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); + int Process(char const *msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); +}; + +#endif + diff --git a/CODE/MSGLIST.CPP b/CODE/MSGLIST.CPP new file mode 100644 index 0000000..f0de6fe --- /dev/null +++ b/CODE/MSGLIST.CPP @@ -0,0 +1,1399 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MSGLIST.CPP 2 3/04/97 2:52p Joe_bostic $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : MSGLIST.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : March 4, 1997 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MessageListClass::MessageListClass -- constructor * + * MessageListClass::~MessageListClass -- destructor * + * MessageListClass::Init -- Inits message system, sets options * + * MessageListClass::Add_Message -- displays the given message * + * MessageListClass::Get_Message -- retrieves given message * + * MessageListClass::Get_Label -- retrieves given text label * + * MessageListClass::Concat_Message -- concats the given message * + * MessageListClass::Add_Edit -- Adds editable string to message list * + * MessageListClass::Remove_Edit -- removes the edit field * + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * MessageListClass::Manage -- Manages multiplayer messages * + * MessageListClass::Input -- Handles input for sending messages * + * MessageListClass::Draw -- Draws the messages * + * MessageListClass::Num_Messages -- returns # messages in the list * + * MessageListClass::Set_Width -- sets allowable width of messages * + * MessageListClass::Trim_Message -- trims chars off start of message * + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * MessageListClass::Reset -- Reset so no messages are visible. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/**************************** Globals **************************************/ + + +/*************************************************************************** + * MessageListClass::MessageListClass -- constructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::MessageListClass(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init all data members + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = 0; + MessageY = 0; + MaxMessages = 0; + MaxChars = 0; + Height = 0; + + EnableOverflow = 0; + AdjustEdit = 0; + IsEdit = 0; + EditX = 0; + EditY = 0; + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + OverflowStart = 0; + OverflowEnd = 0; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + +} // end of MessageListClass + + +/*************************************************************************** + * MessageListClass::~MessageListClass -- destructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::~MessageListClass() +{ + Init(0,0,0,0,0,0,0,0,0,0); + +} // end of ~MessageListClass + + +/*************************************************************************** + * MessageListClass::Init -- Inits message system, sets options * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, NOT including edit message * + * maxchars max # characters allowed per message * + * height pixel height of a line of text * + * edit_x x-coord of edit field; -1 = put at the top of the * + * other messages * + * edit_y y-coord of edit field; -1 = put at the top of the * + * other messages * + * overflow_on true = enable the overflow typing feature * + * over_start start index for overflow processing * + * over_end end index for overflow processing * + * width pixel width of message buffer * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Init(int x, int y, int max_msg, int maxchars, + int height, int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width) +{ + TextLabelClass * txtlabel; + int i; + + Width = width; + + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = x; + MessageY = y; + + MaxMessages = max_msg; + if (MaxMessages > MAX_NUM_MESSAGES) + MaxMessages = MAX_NUM_MESSAGES; + + MaxChars = maxchars; + if (MaxChars > MAX_MESSAGE_LENGTH) + MaxChars = MAX_MESSAGE_LENGTH; + + Height = height; + + //------------------------------------------------------------------------ + // Init the edit field variables. If edit_x or edit_y is -1, place the + // edit field above the other messages; otherwise, place it at the desired + // coords. + //------------------------------------------------------------------------ + EnableOverflow = overflow_on; + IsEdit = 0; + if (edit_x == -1 || edit_y == -1) { + AdjustEdit = 1; + EditX = x; + EditY = y; + } + else { + AdjustEdit = 0; + EditX = edit_x; + EditY = edit_y; + } + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + + //------------------------------------------------------------------------ + // Init the overflow processing indices + //------------------------------------------------------------------------ + OverflowStart = over_start; + OverflowEnd = over_end; + if (OverflowEnd >= MaxChars) { + OverflowEnd = MaxChars - 1; + } + if (OverflowStart >= OverflowEnd) { + OverflowStart = OverflowEnd - 1; + } + +} // end of Init + + +/*********************************************************************************************** + * MessageListClass::Reset -- Reset so no messages are visible. * + * * + * This routine will reset the message list tracker so that any displayed messages are * + * cleared. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/04/1997 JLB : Created. * + *=============================================================================================*/ +void MessageListClass::Reset(void) +{ + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + TextLabelClass * txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (int index = 0; index < MAX_NUM_MESSAGES; index++) { + BufferAvail[index] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + EditLabel = 0; + IsEdit = 0; +} + + +/*************************************************************************** + * MessageListClass::Add_Message -- displays the given message * + * * + * INPUT: * + * name name of sender, NULL = none * + * id numerical ID for this message * + * txt text to display * + * color color to draw text in * + * style style to use * + * timeout # of ticks the thing is supposed to last (-1 = forever) * + * * + * OUTPUT: * + * ptr to new TextLabelClass object. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + * 10/16/1996 JLB : Audio feedback added. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Message(char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout) +{ + TextLabelClass * txtlabel; + int i; + int found; + char message[MAX_MESSAGE_LENGTH + 30]; + char temp[MAX_MESSAGE_LENGTH + 30]; + int print_this_pass; + char save = 0; + int mess_start; + + //------------------------------------------------------------------------ + // Combine the name & message text, if there's a name given + //------------------------------------------------------------------------ + if (name) { + sprintf(message, "%s:%s", name, txt); + } else { + strcpy(message, txt); + } + + //------------------------------------------------------------------------ + // Check that printing this wont overrun the width of the print area on screen + //------------------------------------------------------------------------ + + print_this_pass = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[color], TBLACK, style); + int wid = String_Pixel_Width(message); + if (wid >= Width-8) { + //------------------------------------------------------------------------ + // Bugger. Its too long. Loop through and find out how many chars we can print + //------------------------------------------------------------------------ + if (name) { + sprintf (temp, "%s:", name); + mess_start = strlen (name)+1; + } else { + mess_start = 0; + } + for (int i=1 ; i= Width-8) { + print_this_pass = mess_start + i-1; + break; + } + } + + //------------------------------------------------------------------------ + // Prematurely terminate the string so it doesn't all print. + // We will re-enter at the end to print the rest. + //------------------------------------------------------------------------ + if (print_this_pass) { + save = message [print_this_pass]; + message [print_this_pass] = 0; + } + } + + + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if ( (MaxMessages > 0) && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + + if (txtlabel==NULL) + return(NULL); + + //..................................................................... + // Remove this message from the list; mark its buffer as being available. + //..................................................................... + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // Create the message + //------------------------------------------------------------------------ + txtlabel = new TextLabelClass (message, MessageX, MessageY, + &ColorRemaps[color], style); + if (timeout==-1) { + txtlabel->UserData1 = 0; + } + else { + txtlabel->UserData1 = TickCount + timeout; + } + txtlabel->UserData2 = id; + + //------------------------------------------------------------------------ + // Find a buffer to store our message in; if there are none, don't add the + // message. + //------------------------------------------------------------------------ + found = 0; + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (BufferAvail[i]) { + BufferAvail[i] = 0; + memset (MessageBuffers[i],0,MAX_MESSAGE_LENGTH + 30); + strcpy (MessageBuffers[i],message); + txtlabel->Text = MessageBuffers[i]; + found = 1; + break; + } + } + if (!found) { + delete txtlabel; + return (NULL); + } + + Sound_Effect(VOC_INCOMING_MESSAGE); + + //------------------------------------------------------------------------ + // Attach the message to our list + //------------------------------------------------------------------------ + if (MessageList) { + txtlabel->Add_Tail (*MessageList); + } + else { + MessageList = txtlabel; + } + + //------------------------------------------------------------------------ + // Recompute all messages' y-coordinate values + //------------------------------------------------------------------------ + Compute_Y(); + + //------------------------------------------------------------------------ + // If we terminated the string before the end then we need to reenter to + // add a new message with the rest of the string. + //------------------------------------------------------------------------ + if (save) { + message [print_this_pass] = save; + Add_Message (name, id, &message [print_this_pass], color, style, timeout); + } + + return(txtlabel); + +} // end of Add_Message + + +/*************************************************************************** + * MessageListClass::Get_Message -- retrieves given message * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Message(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg->Text); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Message + + +/*************************************************************************** + * MessageListClass::Get_Label -- retrieves given text label * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Get_Label(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Label + + +/*************************************************************************** + * MessageListClass::Concat_Message -- concats the given message * + * * + * INPUT: * + * name name of sender; NULL = none * + * id ID of message to concatenate to * + * txt text to concatenate onto existing message * + * timeout new timeout for message * + * * + * OUTPUT: * + * 1 = OK, 0 = error (id or name not found) * + * * + * WARNINGS: * + * If the required message doesn't exist, this routine does nothing. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Concat_Message(char const * name, int id, char const * txt, int timeout) +{ + int min_chars; + int max_chars; + char * msg; + TextLabelClass * tlabel; + int found; + + //------------------------------------------------------------------------ + // If no name is given, or the concatenation feature is turned off, + // don't concatenate the message + //------------------------------------------------------------------------ + if (!name || !EnableOverflow) { + return (0); + } + + //------------------------------------------------------------------------ + // Scan through all active messages, searching for one with a matching + // name & ID + //------------------------------------------------------------------------ + found = 0; + if (MessageList) { + tlabel = MessageList; + while (tlabel) { + if (tlabel->UserData2 == id && + !memcmp(tlabel->Text,name,strlen(name))) { + found = 1; + break; + } + tlabel = (TextLabelClass *)tlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // name and ID not found; return + //------------------------------------------------------------------------ + if (!found) { + return (0); + } + + //------------------------------------------------------------------------ + // set a pointer to the text string, plus the name and colon + //------------------------------------------------------------------------ + msg = tlabel->Text + strlen(name) + 1; + + //------------------------------------------------------------------------ + // If there's room enough in the message, just add the given string + //------------------------------------------------------------------------ + if ( (strlen(msg) + strlen(txt)) < MaxChars) { + + //--------------------------------------------------------------------- + // We need to trim the message if there is no room to draw it + //--------------------------------------------------------------------- + char *concat_test = new char [MaxChars+1]; + Fancy_Text_Print(TXT_NONE, 0, 0, tlabel->Color, TBLACK, tlabel->Style); + int name_width = String_Pixel_Width(tlabel->Text) - String_Pixel_Width(msg); + int width; + + strcpy (concat_test, msg); + strcat (concat_test, txt); + width = String_Pixel_Width(concat_test) + name_width; + min_chars = 10; + + while (width >= Width-8){ + + max_chars = strlen (msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + + Trim_Message (NULL, msg, min_chars, max_chars, 0); + + strcpy (concat_test, msg); + strcat (concat_test, txt); + + width = String_Pixel_Width(concat_test) + name_width; + }; + + delete [] concat_test; + + strcat (msg,txt); + } + + //------------------------------------------------------------------------ + // Otherwise, trim off some characters from the beginning of the + // message. Trim off at least enough to leave room for the new text. + // Trim from left to right to remove the minimum required text. + //------------------------------------------------------------------------ + else { + min_chars = (strlen(msg) + strlen(txt)) - MaxChars; + max_chars = strlen(msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + Trim_Message (NULL, msg, min_chars, max_chars, 0); + strcat (msg, txt); + } + + //------------------------------------------------------------------------ + // Set the new timeout value for the message + //------------------------------------------------------------------------ + if (timeout==-1) { + tlabel->UserData1 = 0; + } + else { + tlabel->UserData1 = TickCount + timeout; + } + + return (1); + +} // end of Concat_Message + + + +/*********************************************************************************************** + * MessageListClass::Set_Edit_Focus -- Give the gadget system focus to the edit box * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +void MessageListClass::Set_Edit_Focus (void) +{ + if (IsEdit) EditLabel->Set_Focus(); +} + + +/*********************************************************************************************** + * MessageListClass::Has_Edit_Focus -- Find out if the edit box has the input focus * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +bool MessageListClass::Has_Edit_Focus (void) +{ + if (IsEdit){ + return (EditLabel->Has_Focus()); + }else{ + return(false); + } +} + + + +/*************************************************************************** + * MessageListClass::Add_Edit -- Adds editable string to message list * + * * + * INPUT: * + * color color of edit message * + * style style of edit message * + * to string: who to send to; NULL = none * + * cursor character to use as a cursor; 0 = none * + * * + * OUTPUT: * + * ptr to new TextLabelClass * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Edit(PlayerColorType color, + TextPrintType style, char * to, char cursor, int width) +{ + int i; + TextLabelClass * txtlabel; + + //------------------------------------------------------------------------ + // Do nothing if we're already in "edit" mode + //------------------------------------------------------------------------ + if (IsEdit) { + EditLabel->Set_Focus(); + return(NULL); + } + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if (AdjustEdit && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // If no 'to' field was passed in, ignore it + //------------------------------------------------------------------------ + if (!to) { + to = ""; + } + + //------------------------------------------------------------------------ + // Set the cursor character + //------------------------------------------------------------------------ + CursorChar = cursor; + + //------------------------------------------------------------------------ + // Initialize the buffer positions; create a new text label object + //------------------------------------------------------------------------ + memset (EditBuf, 0, sizeof(EditBuf)); + strcpy (EditBuf, to); + OverflowBuf[0] = 0; + EditCurPos = EditInitPos = strlen(to); + EditLabel = new TextLabelClass (EditBuf, EditX, EditY, + &ColorRemaps[color], style); + + Width = width; + + if (EditLabel) { + IsEdit = 1; + EditLabel->Set_Focus(); + } + else { + IsEdit = 0; + } + + //------------------------------------------------------------------------ + // If the edit field appears over the message list, recompute the y-value + // for all messages. Also, adjust MaxMessages down by one, since there + // is now one less slot available. + //------------------------------------------------------------------------ + if (AdjustEdit) { + Compute_Y(); + MaxMessages--; + } + + return(EditLabel); + +} // end of Add_Edit + + +/*************************************************************************** + * MessageListClass::Remove_Edit -- removes the edit field * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/06/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Remove_Edit(void) +{ + //------------------------------------------------------------------------ + // If the edit field is active, delete it + //------------------------------------------------------------------------ + if (IsEdit) { + IsEdit = 0; + delete EditLabel; + + //..................................................................... + // If the edit field appears over the message list, recompute the + // y-value for all messages. Adjust MaxMessages back up, since there + // is now a new available slot. + //..................................................................... + if (AdjustEdit) { + Compute_Y(); + MaxMessages++; + } + } + +} // end if Remove_Edit + + +/*************************************************************************** + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to edit buffer, minus the "To:" header * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Edit_Buf(void) +{ + return(EditBuf + EditInitPos); + +} // end of Get_Edit_Buf + + +/*************************************************************************** + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * * + * INPUT: * + * color color to set edit label to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Edit_Color(PlayerColorType color) +{ + if (IsEdit) { + EditLabel->Color = &ColorRemaps[color]; + } + +} // end of Set_Edit_Color + + +/*************************************************************************** + * MessageListClass::Manage -- Manages multiplayer messages * + * * + * If this routine returns TRUE, the caller should update the display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 0 = no change has occurred, 1 = changed * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Manage (void) +{ + TextLabelClass * txtlabel; + TextLabelClass * next; + int changed = 0; + int i; + + //------------------------------------------------------------------------ + // Loop through all messages + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + + //..................................................................... + // If this message's time is up, remove it from the list + //..................................................................... + if (txtlabel->UserData1 != 0 && TickCount > txtlabel->UserData1) { + + //.................................................................. + // Save the next ptr in the list; remove this entry + //.................................................................. + next = (TextLabelClass *)txtlabel->Get_Next(); + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) { + BufferAvail[i] = 1; + } + } + delete txtlabel; + changed = 1; + txtlabel = next; + } + else { + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // If a changed has been made, recompute the y-coord of all messages + //------------------------------------------------------------------------ + if (changed) { + Compute_Y(); + } + + return(changed); + +} // end of Manage + + +/*************************************************************************** + * MessageListClass::Input -- Handles input for sending messages * + * * + * INPUT: * + * input key value to process * + * * + * OUTPUT: * + * 1 = caller should redraw the message list (no need to complete * + * refresh, though) * + * 2 = caller should completely refresh the display. * + * 3 = caller should send the edit message. * + * (sets 'input' to 0 if it processes it.) * + * 4 = caller should send the Overflow buffer * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Input(KeyNumType &input) +{ + KeyASCIIType ascii; + int retcode = 0; + int numchars; + + //------------------------------------------------------------------------ + // Do nothing if nothing to do. + //------------------------------------------------------------------------ + if (input == KN_NONE) { + return(0); + } + + //------------------------------------------------------------------------ + // Leave mouse events alone. + //------------------------------------------------------------------------ + if ( (input & (~KN_RLSE_BIT))==KN_LMOUSE || + (input & (~KN_RLSE_BIT))==KN_RMOUSE) { + return(0); + } + + //------------------------------------------------------------------------ + // If we're in 'edit mode', handle keys + //------------------------------------------------------------------------ + if (IsEdit) { + + + ascii = (KeyASCIIType)(Keyboard->To_ASCII(input) & 0x00ff); + +#ifdef WIN32 + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((input & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + input = (KeyNumType)(input & ~WWKEY_VK_BIT); + + } else { + /* + ** Filter out all special keys except return, escape and backspace + */ + if ((!(input & WWKEY_VK_BIT) && !(input & KN_BUTTON) + && ascii >= ' ' && ascii <= 127) + || (input & 0xff)== (KN_RETURN & 0xff) + || (input & 0xff)== (KN_BACKSPACE & 0xff) + || (input & 0xff)== (KN_ESC & 0xff) ) { + + //ascii = (KeyASCIIType)(Keyboard->To_ASCII(input)); + } else { + input = KN_NONE; + return (0); + } + } +#endif //WIN32 + + + + switch (ascii) { + //.................................................................. + // ESC = abort message + //.................................................................. + case KA_ESC & 0xff: + Remove_Edit(); + retcode = 2; + input = KN_NONE; + break; + + //.................................................................. + // RETURN = send the message. + // Add a space to the end, in case another message gets concatenated + // onto this one after we send it; then, they won't be mushed + // together. + //.................................................................. + case KA_RETURN & 0xff: + if (EditCurPos == EditInitPos) { + retcode = 0; + input = KN_NONE; + break; + } + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + EditBuf[EditCurPos] = ' '; + EditCurPos++; + EditBuf[EditCurPos] = 0; + } + Remove_Edit(); + retcode = 3; + input = KN_NONE; + break; + + //.................................................................. + // BACKSPACE = remove a character + //.................................................................. + case KA_BACKSPACE & 0xff: + if (EditCurPos > EditInitPos) { + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 2; + } + input = KN_NONE; + EditLabel->Set_Focus(); + break; + + //.................................................................. + // default: add a character. Reserve the last buffer position for + // null. (EditCurPos - EditInitPos) is the buffer index # of the + // next character, after the "To:" prefix. + //.................................................................. + default: + EditLabel->Set_Focus(); + bool overflowed = false; + if (ascii >= ' ' && ascii <= 127) { + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 1; + + /* + ** Verify that the additional character would not overrun the on screen edit box. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, EditLabel->Color, TBLACK, EditLabel->Style); + int width = String_Pixel_Width(EditBuf); + if (width >= Width-10) { + overflowed = true; + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 0; + } + } else { + //............................................................ + // If there's no room in the buffer, and overflow is enabled, + // trim the extra characters off (from right to left, to + // remove the max possible characters), and then add the new + // character in. + //............................................................ + overflowed = true; + } + + if (/*BGEnableOverflow &&*/ overflowed) { + numchars = Trim_Message (OverflowBuf, EditBuf + EditInitPos, + OverflowStart,OverflowEnd, 1); + EditCurPos -= numchars; + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 4; + } + } + input = KN_NONE; + break; + } + } + + return(retcode); + +} // end of Input + + +/*************************************************************************** + * MessageListClass::Draw -- draws messages * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Draw(void) +{ + char txt[2] = {0,0}; + + if (IsEdit) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + EditLabel->Draw_Me(true); + + if (CursorChar && (EditCurPos - EditInitPos) < (MaxChars - 1) && EditLabel->Has_Focus()) { + txt[0] = CursorChar; + Fancy_Text_Print(txt, + EditLabel->X + String_Pixel_Width(EditLabel->Text), + EditLabel->Y, + EditLabel->Color, + TBLACK, + EditLabel->Style); + } + + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + if (MessageList) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + MessageList->Draw_All(); + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + +} // end of Draw + + +/*************************************************************************** + * MessageListClass::Num_Messages -- returns # messages in the list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of messages, including the edit field if it's above the messages * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Num_Messages(void) +{ + GadgetClass * gadg; + int num; + + num = 0; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + num++; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit && AdjustEdit) { + num++; + } + + return (num); + +} // end of Num_Messages + + +/*************************************************************************** + * MessageListClass::Set_Width -- sets allowable width of messages * + * * + * INPUT: * + * width pixel width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Width(int width) +{ + GadgetClass * gadg; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + ((TextLabelClass *)gadg)->PixWidth = width; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit) { + EditLabel->PixWidth = width; + } + +} // end of Set_Width + + +/*************************************************************************** + * MessageListClass::Trim_Message -- trims chars off start of message * + * * + * INPUT: * + * dest buffer to store removed characters in; NULL = none * + * src text buffer to trim * + * min_chars min # chars that must be trimmed off * + * max_chars max # chars allowed to trim * + * scandir 0 = left-to-right, 1 = right-to-left * + * * + * OUTPUT: * + * # characters removed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Trim_Message(char * dest, char * src, int min_chars, + int max_chars, int scandir) +{ + int i; + int len; + int found; + + //------------------------------------------------------------------------ + // validate parameters + //------------------------------------------------------------------------ + if (min_chars <= 0) { + return(0); + } + + len = strlen (src); + if (max_chars > len) { + max_chars = len; + } + + //------------------------------------------------------------------------ + // find 1st available white space; if there is none, just trim off + // 'min_chars' characters. 'i' will be the number of chars to trim. + // The chars removed will include the white space. + //------------------------------------------------------------------------ + found = 0; + //........................................................................ + // scan from left to right + //........................................................................ + if (scandir == 0) { + for (i = min_chars; i <= max_chars; i++) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // scan from right to left + //........................................................................ + else { + for (i = max_chars; i >= min_chars; i--) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // If no whitespace was found, just set 'i' to the min # characters + //........................................................................ + if (!found) { + i = min_chars; + } + + //------------------------------------------------------------------------ + // Save trimmed characters in the dest buffer, if there is one + //------------------------------------------------------------------------ + if (dest) { + memcpy (dest, src, i); + dest[i] ='\0'; + } + + //------------------------------------------------------------------------ + // Shift characters over in the source buffer + //------------------------------------------------------------------------ + memmove (src, src + i, len - i + 1); + + return (i); + +} // end of Trim_Message + + +/*************************************************************************** + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Compute_Y(void) +{ + GadgetClass * gadg; + int y; + + //------------------------------------------------------------------------ + // If the editable message is attached to the message list, 'AdjustEdit' + // will be set; so, adjust all y-values downward one line. Otherwise, + // the editable message has its own screen coordinates. + //------------------------------------------------------------------------ + if (IsEdit && AdjustEdit) { + y = MessageY + Height; + } + else { + y = MessageY; + } + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + +} // end of Compute_Y + + +/*************************** end of msglist.cpp ****************************/ diff --git a/CODE/MSGLIST.H b/CODE/MSGLIST.H new file mode 100644 index 0000000..f6091e9 --- /dev/null +++ b/CODE/MSGLIST.H @@ -0,0 +1,214 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/MSGLIST.H 2 3/04/97 2:53p Joe_bostic $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : MSGLIST.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : May 22, 1995 [BRR] * + * * + * Initializing: * + * Call Init(), giving it the coords of the upper-left corner to display * + * the messages, the coordinates to display the editable message, max # * + * messages allowed, max # chars per message, and the pixel height of * + * the font. MaxChars should be less than or equal to the define * + * MAX_MESSAGE_LENGTH. * + * * + * Displaying a message: * + * Call Add_Message(); the buffer you pass in is copied into a static * + * buffer in this class. Each message is given a timeout; after this * + * time, the message is removed from the displayed list. * + * Each message also has a 2-byte ID, which can be used to identify the + * sender. + * * + * Editing a message: * + * Call Add_Edit(), giving it the color & style to print in. An * + * optional "To xxx" prefix is allowed. The edit message can appear in * + * the message list itself (it's placed first), or at a separate screen * + * location, depending on the values passed to Init(). * + * * + * Updating messages: * + * The Input() routine passes new keys to the editable message; this * + * routine's return code tells the app whether to redraw the messages, * + * and whether to send it the edit message. As the user types * + * characters into the edit field, if he overflows the max storage * + * for the edit field, the field is parsed & the first few words * + * removed; these words are put into the Overflow buffer, and the app * + * is told so; the app should send this buffer across to the destination.* + * * + * - MessageList is a gadget list of all current messages * + * - MessageX & Y are the upper left corner of the 1st message * + * - MaxMessages is the max # of messages allowed, including the editable * + * message; 0 = no limit. * + * - MaxChars is the max # of chars allowed per message * + * - Height is the pixel height of a single line * + * - EditLabel points to the textmessage gadget for the current editable * + * field. EditBuf points to the char buffer being edited. EditInitPos * + * & EditCurPos define buffer index positions. * + * - EditSendAddress is the IPX Address to send the message to when RETURN * + * is pressed. * + * * + * The low word in the UserData field in the TextLabelClass tells what the * + * timeout for each message is (0 = none); the high byte of the UserData * + * field is used for the text message ID. * + * When a message's timeout expires, it's deleted. When a new message * + * is added, the top message is deleted if MPlayerMaxMessages is exceeded. * + * * + * The Edit-able message is never deleted until ESC or RETURN is pressed. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGLIST_H +#define MSGLIST_H + +//*************************************************************************** +// Defines +//*************************************************************************** +//--------------------------------------------------------------------------- +// Max length of inter-player message buffers. Messages.Init() should specify +// a value <= this. For editing messages, the "to" field length is added to +// this length to generate the entire editable message length. For displayed +// messages, a "From" prefix length should be added to this value to generate +// the entire max displayable message length. +//--------------------------------------------------------------------------- +#define MAX_MESSAGE_LENGTH 120 + +//--------------------------------------------------------------------------- +// Max # of allowed messages at one time +//--------------------------------------------------------------------------- +#define MAX_NUM_MESSAGES 14 + +//*************************************************************************** +// Forward declarations +//*************************************************************************** +class TextLabelClass; + +//*************************************************************************** +// Class declaration +//*************************************************************************** +class MessageListClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + MessageListClass (void); + ~MessageListClass (); + + //..................................................................... + // Initialization + //..................................................................... + void Reset(void); + void Init (int x, int y, int max_msg, int maxchars, int height, + int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width = 640); + TextLabelClass * Add_Message (char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout); + int Concat_Message (char const * name, int id, char const * txt, int timeout); + + //..................................................................... + // Message access utility routines + //..................................................................... + char * Get_Message (int id); + TextLabelClass * Get_Label (int id); + + //..................................................................... + // Message-editing support routines + //..................................................................... + TextLabelClass * Add_Edit(PlayerColorType color, TextPrintType style, + char *to, char cursor = 0, int width = 640); + void Remove_Edit (void); + char * Get_Edit_Buf (void); + char * Get_Overflow_Buf (void) {return (OverflowBuf);} + void Clear_Overflow_Buf (void) {OverflowBuf[0] = 0;} + int Is_Edit(void) {return (IsEdit);} + void Set_Edit_Color(PlayerColorType color); + + //..................................................................... + // Maintenance routines + //..................................................................... + int Manage (void); + int Input (KeyNumType &input); + void Draw(void); + int Num_Messages(void); + void Set_Width(int width); + void Set_Edit_Focus(void); + bool Has_Edit_Focus(void); + + private: + + //..................................................................... + // Message parsing + //..................................................................... + int Trim_Message(char *dest, char *src, int min_chars, int max_chars, + int scandir); + + //..................................................................... + // Compute the y-coord of the message list + //..................................................................... + void Compute_Y(void); + + //..................................................................... + // Private Data + //..................................................................... + TextLabelClass * MessageList; // list of messages + int MessageX; // x-coord of upper-left + int MessageY; // y-coord of upper-left + int MaxMessages; // max messages allowed + int MaxChars; // max allowed chars per message + int Height; // height in pixels + + //..................................................................... + // Data for the edit field: the edit field will either appear at + // exact coordinates specified by the application, or it will appear + // vertically above the other messages. + //..................................................................... + unsigned EnableOverflow : 1; // 1 = enable overflow feature + unsigned IsEdit : 1; // 1 = there's an edit field + unsigned AdjustEdit : 1; // 1 = edit field appears over msgs + int EditX; // x-coord of edit field + int EditY; // y-coord of edit field + TextLabelClass *EditLabel; // ptr to current edit label + char EditBuf[MAX_MESSAGE_LENGTH + 30]; // buffer for editable message + char OverflowBuf[MAX_MESSAGE_LENGTH + 30]; // overflow area + int EditCurPos; // current edit position + int EditInitPos; // initial edit position + char CursorChar; // character to use a cursor + int OverflowStart; // 1st index for overflow trimming + int OverflowEnd; // last index for overflow trimming + int Width; // Maximum width in pixels of editable string + + //..................................................................... + // Buffers provided for messages. They must be long enough for + // both the message, and for the "To" prefix on edited messages, or + // the "Name:" prefix on received messages. + //..................................................................... + char MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; + char BufferAvail[MAX_NUM_MESSAGES]; +}; + +#endif +/**************************** end of msglist.h *****************************/ + diff --git a/CODE/NETDLG.CPP b/CODE/NETDLG.CPP new file mode 100644 index 0000000..22c2a9e --- /dev/null +++ b/CODE/NETDLG.CPP @@ -0,0 +1,7824 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/NETDLG.CPP 13 10/13/97 2:20p Steve_t $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : December 12, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_CHAT_ANNOUNCE * + * Chat: unique id of the local node, so I can tell * + * if this chat announcement is from myself * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house, color, & version range * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * Reject.Why: tells why we got rejected * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Init_Network -- initializes network stuff * + * Shutdown_Network -- shuts down network stuff * + * Process_Global_Packet -- responds to remote queries * + * Destroy_Connection -- destroys the given connection * + * Remote_Connect -- handles connecting this user to others * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Unjoin_Game -- Cancels joining a game * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Net_New_Dialog -- lets user start a new game * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// Warning - Most disgusting cpp file of all time. ajw + +#include "function.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#include "ccdde.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 + +#include +#include + +#include "WolDebug.h" + +#define SHOW_MONO 0 +//#define OLDWAY 1 + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +#endif +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +//--------------------------------------------------------------------------- +// The possible states of the join-game dialog +//--------------------------------------------------------------------------- +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting + JOIN_GAME_START_LOAD, // the game we've joined is starting; load saved game +} JoinStateType; + +//--------------------------------------------------------------------------- +// The possible return codes from Get_Join_Responses() +//--------------------------------------------------------------------------- +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game formed, or is now open + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +//--------------------------------------------------------------------------- +// The possible reasons we're rejected from joining a game +//--------------------------------------------------------------------------- +typedef enum { + REJECT_DUPLICATE_NAME, // player's name is a duplicate + REJECT_GAME_FULL, // game is full + REJECT_VERSION_TOO_OLD, // joiner's version is too old + REJECT_VERSION_TOO_NEW, // joiner's version is too new + REJECT_BY_OWNER, // game owner clicked "reject" + REJECT_DISBANDED, // game was disbanded + REJECT_MISMATCH, // "rules.ini" file mismatch. +} RejectType; + +#ifdef ENGLISH +char const *EngMisStr[] = { + "Coastal Influence (Med)", + "Middle Mayhem (Sm)", + "Equal Opportunity (Sm)", + "Marooned II (Med)", + "Keep off the Grass (Sm)", + "Isle of Fury (Lg)", + "Ivory Wastelands (Sm)", + "Shallow Grave (Med)", + "North By Northwest (Lg)", + "First Come, First Serve (Sm)", + "Island Hoppers (Sm)", + "Raraku (Lg)", + "Central Conflict (Lg)", + "Combat Alley (Med)", + "Island Wars (Lg)", + "Desolation (Lg)", + "No Escape (Med)", + "No Man's Land (Med)", + "Normandy (Med)", + "Pond Skirmish (Med)", + "Ridge War (Med)", + "A Path Beyond (Lg)", + "Dugout Isle (Med)", + "Treasure Isle (Med)", + + "Africa (Lg)", + "Alaska Anarchy (Lg)", + "All that Glitters... (Lg)", + "Apre's Peace (Lg)", + "Antartica (Lg)", + "Armourgarden (Lg)", + "Austraila (Med)", + "Barrier to Entry (Lg)", + "Bavarian Blast (Med)", + "Be Shore (Med)", + "Bearing Straits (Med)", + "Blow Holes (Lg)", + "Bonsai (Sm)", + "Brother Stalin (Lg)", + "Bullseye (Lg)", + "C&C (Med)", + "Camos Canyon (Med)", + "Camos Coves (Lg)", + "Camos Cross (Lg)", + "Camos Crossing (Sm)", + "Central Arena (Lg)", + "Canyon River (Med)", + "Crossroads (Sm)", + "Czech Mate (Lg)", + "Dday (Med)", + "Disaster Central (Lg)", + "Docklands (Med)", + "East Coast (Med)", + "Eastern Seaboard (Lg)", + "Finger Lake (Lg)", + "Fjords (Med)", + "Floodlands (Lg)", + "Forest under fire (Lg)", + "Four Corners (Lg)", + "Frostbit Fjords (Lg)", + "Glenboig (Sm)", + "Hell Frozen Over (Lg)", + "India (Lg)", + "Indirect Fire (Lg)", + "Island Wars II (Lg)", + "Italy (Lg)", + "Kabalo (Lg)", + "King of the Hills (Lg)", + "Lake Divide (Med)", + "Lakelands (Lg)", + "Land Ladder (Lg)", + "Lotsa Lakes (Lg)", + "Lunar Battlefield (Lg Special)", + "Malibu Fields (Med)", + "Marshland (Med)", + "MyLai Delta (Med)", + "Natural Harbor (Med)", + "No Way Out (Lg)", + "Normandy Landing (Lg)", + "Ore Wars (Med)", + "Oz (Lg)", + "Pilgrim Fathers II (Lg)", + "Pip's Ice Tea (Med)", + "Polar Panic (Lg)", + "Ponds (Med)", + "Putney (Lg)", + "Return to Zion (Lg)", + "Ring of Land (Lg)", + "River Basin (Lg)", + "River Delta (Med)", + "River Islands (Med)", + "River Maze (Sm)", + "Rivers (Sm)", + "Run the Gauntlet (Med)", + "Scappa Flow (Lg)", + "Siberian Slaughter (Lg)", + "Sleepy Valley (Sm)", + "Snake River (Lg)", + "Snow Wars (Lg)", + "Snowball fight (Lg)", + "Snowy Island (Lg)", + "So Near So Far (Sm)", + "South America (Lg)", + "Spring Line (Lg)", + "Star (Lg)", + "Straighter & Narrower (Sm)", + "TerrainSpotting (Sm)", + "The Bay (Lg)", + "The Garden (Lg)", + "The Great Lakes (Med)", + "The Ice Arena (Lg)", + "The Lake District (Lg)", + "The Linked lands (Lg)", + "The Mississippi (Med)", + "The Sticky Bit (Lg)", + "The Valley (Med)", + "The Woods Today (Lg)", + "Things to Come (Lg)", + "Tiger Core (Sm)", + "To the Core (Sm)", + "Tournament Hills (Lg)", + "Tropical Storm (Med)", + "Tundra Trouble (Lg)", + "Uk (Med)", + "Undiscovered Country (Sm)", + "United States (Med)", + "Volcano (Sm)", + "Wastelands (Lg)", + "Water Works (Sm)", + "World Map (Med)", + "Zambezi (Lg)", + + "A Pattern of Islands (Lg 8 players)", + "Arena Valley Extreme (Mega 8 players)", + "Around the Rim (Sm 4 players)", + "Ashes to Ashes (Lg 6 players)", + "Artic Wasteland (Mega 8 players)", + "Badajoz (Med 4 players)", + "Baptism of Fire (Lg 6 players)", + "Big Fish, Small Pond (Lg 6 players)", + "Blue Lakes (Lg 8 players)", + "Booby Traps (Mega 8 players)", + "Bridgehead (Lg 6 players)", + "Butterfly Bay (Lg 6 players)", + "Central Conflict Extreme (Mega 8 players)", + "Circles of Death (Mega 8 players)", + "Cold Front (Med 6 players)", + "Cold Pass (Med 4 players)", + "Combat Zones (Mega 8 players)", + "Conflict Cove (Sm 4 players)", + "Culloden Moor (Med 8 players)", + "Damnation Alley (Mega 8 players)", + "Death Valley (Mega 8 players)", + "Deep Six (Mega 8 players)", + "Destruction Derby (Mega 8 players)", + "Diamonds Aren't Forever (Mega 8 players)", + "Elysium (Sm 4 players)", + "Equal Shares (Lg 4 players)", + "Frost Bitten (Mega 8 players)", + "Frozen Valley (Med 6 players)", + "Gettysburg (Sm 4 players)", + "Glacial Valley (Sm 4 players)", + "Gold Coast (Med 6 players)", + "Gold Rush (Lg 4 players)", + "Habitat (Lg 4 players)", + "Hades Frozen Over (Sm 4 players)", + "Hamburger Hill (Mega 8 players)", + "Hastings (Sm 4 players)", + "Hell's Pass (Med 6 players)", + "Holy Grounds (Mega 8 players)", + "Ice Bergs (Med 6 players)", + "Ice Station (Lg 6 players)", + "Ice Queen (Lg 4 players)", + "In the Sun (Med 6 players)", + "Innocents? (Mega 8 players)", + "Islands (Med 8 players)", + "Island Plateau (Lg 4 players)", + "Island Wars Extreme (Mega 8 players)", + "Kananga (Med 6 players)", + "King of the Hills Extreme (Mega 8 players)", + "Lake Land (Lg 8 players)", + "Land Locked (Lg 8 players)", + "Lanes (Med 8 players)", + "Leipzip (Sm 4 players)", + "Meander (Lg 8 players)", + "Mekong (Med 8 players)", + "Middle Ground (Med 8 players)", + "Naval Conquests (Mega 8 players)", + "On your Marks (Med 4 players)", + "Open Warfare (Mega 8 players)", + "Ore Gardens (Lg 8 players)", + "Potholes (Mega 8 players)", + "Puddles (Med 4 players)", + "Random Violence (Mega 8 players)", + "Revenge (Med 8 players)", + "Rias (Med 8 players)", + "River Crossing (Sm 4 players)", + "River Rampage (Mega 8 players)", + "River Rapids (Lg 6 players)", + "Rivers Wild (Mega 8 players)", + "Rorkes Drift (Lg 4 players)", + "Seaside (Med 4 players)", + "Shades (Med 8 players)", + "Smuggler's Cove (Lg 6 players)", + "Snow Garden (Sm 2 players)", + "Stalingrad (Sm 4 players)", + "Sticks & Stones (Med 4 players)", + "Strathearn Valley (Lg 6 players)", + "Super Bridgehead (Mega 8 players)", + "Super Mekong (Mega 8 players)", + "Super Ore Gardens (Mega 8 players)", + "Switch (Med 4 players)", + "The Berg (Mega 8 players)", + "The Boyne (Med 4 players)", + "The Bulge (Sm 4 players)", + "The Cauldron (Lg 6 players)", + "The Finger (Lg 6 players)", + "The Hills Have Eyes (Mega 8 players)", + "The Keyes (Med 6 players)", + "The Lakes (Med 8 players)", + "The Neck (Med 6 players)", + "The Web (Lg 6 players)", + "To the Core (Lg 4 players)", + "Trafalgar (Lg 4 players)", + "Twin Rivers (Sm 4 players)", + "Umtumbo Gorge (Lg 4 players)", + "Watch Your Step Extreme (Mega 8 players)", + "Waterfalls (Lg 8 players)", + "Waterloo Revisited (Lg 6 players)", + "Water Werks (Mega 8 players)", + "Warlord's Lake (Sm 4 players)", + "Zama (Sm 4 players)", + + NULL +}; +#endif + +#ifdef GERMAN +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Weg ins Jenseits (Gr)", + "Central Conflict (Lg)", "Der zentrale Konflikt (Gr)", + "Coastal Influence (Med)", "Sturm an der Kste (Mit)", + "Combat Alley (Med)", "Boulevard der Schlachten (Mit)", + "Desolation (Lg)", "Verwstung (Gr)", + "Dugout Isle (Med)", "Buddelschiff (Mit)", + "Equal Opportunity (Sm)", "Gleiche Chancen (Kl)", + "First Come, First Serve (Sm)", "Wer zuerst kommt... (Kl)", + "Island Hoppers (Sm)", "Inselspringen (Kl)", + "Island Wars (Lg)", "Der Krieg der Eilande (Gr)", + "Isle of Fury (Lg)", "Insel des Zorns (Gr)", + "Ivory Wastelands (Sm)", "Elfenbeinwste (Kl)", + "Keep off the Grass (Sm)", "Rasen betreten verboten (Kl)", + "Marooned II (Med)", "Gestrandet (Mit)", + "Middle Mayhem (Sm)", "Mittelsmann (Kl)", + "No Escape (Med)", "Kein Entrinnen (Mit)", + "No Man's Land (Med)", "Niemandsland (Mit)", + "Normandy (Med)", "Normandie (Mit)", + "North By Northwest (Lg)", "Nord auf Nordwest (Gr)", + "Pond Skirmish (Med)", "Teichgepl„nkel (Mit)", + "Raraku (Lg)", "Raraku (Gr)", + "Ridge War (Med)", "Das Tal der Cyborgs (Mit)", + "Shallow Grave (Med)", "Ein enges Grab (Mit)", + "Treasure Isle (Med)", "Die Schatzinsel (Mit)", + + "Africa (Lg)", "Afrika (Gr)", + "Alaska Anarchy (Lg)", "Anarchie in Alaska (Gr)", + "All that Glitters... (Lg)", "Alles was gl„nzt... (Gr)", + "Apre's Peace (Lg)", "Apres Frieden (Gr)", + "Antartica (Lg)", "Antarktica (Gr)", + "Armourgarden (Lg)", "Garten der Panzer (Gr)", + "Austraila (Med)", "Koalaland (Mit)", + "Barrier to Entry (Lg)", "Zutritt verboten (Gr)", + "Bavarian Blast (Med)", "Bayrische Blasmusik (Mit)", + "Be Shore (Med)", "Strandl„ufer (Mit)", + "Bearing Straits (Med)", "Die Heringstrasse (Mit)", + "Blow Holes (Lg)", "L”cheriger K„se (Gr)", + "Bonsai (Sm)", "Bonsai (Kl)", + "Brother Stalin (Lg)", "Brderchen Stalin (Gr)", + "Bullseye (Lg)", "Bullseye (Gr)", + "C&C (Med)", "C&C (Mit)", + "Camos Canyon (Med)", "Camos-Canyon (Mit)", + "Camos Coves (Lg)", "Camos-Grotte (Gr)", + "Camos Cross (Lg)", "Camos-Kreuz (Gr)", + "Camos Crossing (Sm)", "Camos-Kreuzweg (Kl)", + "Central Arena (Lg)", "Spielplatz des Teufels (Gr)", + "Canyon River (Med)", "Canyonfluss (Mit)", + "Crossroads (Sm)", "Kreuzung (Kl)", + "Czech Mate (Lg)", "Tschechische Er”ffnung (Gr)", + "Dday (Med)", "D-Day (Mit)", + "Disaster Central (Lg)", "Endstation Schweinebucht (Gr)", + "Docklands (Med)", "Docklands (Mit)", + "East Coast (Med)", "Ostkste (Mit)", + "Eastern Seaboard (Lg)", "Die Passage nach Osten (Gr)", + "Finger Lake (Lg)", "Fingersee (Gr)", + "Fjords (Med)", "Fjorde (Mit)", + "Floodlands (Lg)", "Land unter! (Gr)", + "Forest under fire (Lg)", "Waldsterben im Feuer (Gr)", + "Four Corners (Lg)", "Viereck (Gr)", + "Frostbit Fjords (Lg)", "Frostbeulenfjord (Gr)", + "Glenboig (Sm)", "Glenboig (Kl)", + "Hell Frozen Over (Lg)", "Winter in der H”lle (Gr)", + "India (Lg)", "Indien (Gr)", + "Indirect Fire (Lg)", "Indirekter Beschuss (Gr)", + "Island Wars II (Lg)", "Krieg der Inseln (Gr)", + "Italy (Lg)", "Italien (Gr)", + "Kabalo (Lg)", "Kabalo (Gr)", + "King of the Hills (Lg)", "K”nig des Maulwurfshgels (Gr)", + "Lake Divide (Med)", "Wasserscheide (Mit)", + "Lakelands (Lg)", "Seenplatte (Gr)", + "Land Ladder (Lg)", "Das Leiterspiel (Gr)", + "Lotsa Lakes (Lg)", "Mehr Seen (Gr)", + "Lunar Battlefield (Lg Special)", "Schlachtfeld Mond (Gr Spezial)", + "Malibu Fields (Med)", "Malibu (Mit)", + "Marshland (Med)", "Schlammschlacht (Mit)", + "MyLai Delta (Med)", "Das Delta von My Lai (Mit)", + "Natural Harbor (Med)", "Natrlicher Hafen (Mit)", + "No Way Out (Lg)", "Kein Entkommen (Gr)", + "Normandy Landing (Lg)", "Landung in der Normandie (Gr)", + "Ore Wars (Med)", "Die Erz-Kriege (Mit)", + "Oz (Lg)", "Das Land Oz (Gr)", + "Pilgrim Fathers II (Lg)", "Die Grnderv„ter (Gr)", + "Pip's Ice Tea (Med)", "Pips Eistee (Mit)", + "Polar Panic (Lg)", "Panik am Pol (Gr)", + "Ponds (Med)", "Tmpelspringer (Mit)", + "Putney (Lg)", "Putney (Gr)", + "Return to Zion (Lg)", "Rckkehr nach Zion (Gr)", + "Ring of Land (Lg)", "Der Landring (Gr)", + "River Basin (Lg)", "Flusslauf (Gr)", + "River Delta (Med)", "Flussdelta (Mit)", + "River Islands (Med)", "Flussinsel (Mit)", + "River Maze (Sm)", "Flussgewirr (Kl)", + "Rivers (Sm)", "Flsse (Kl)", + "Run the Gauntlet (Med)", "Spiessrutenlauf (Mit)", + "Scappa Flow (Lg)", "Scapa Flow (Gr)", + "Siberian Slaughter (Lg)", "Sibirisches Gemetzel (Gr)", + "Sleepy Valley (Sm)", "Tal der Ahnungslosen (Kl)", + "Snake River (Lg)", "Am Schlangenfluss (Gr)", + "Snow Wars (Lg)", "Krieg der Flocken (Gr)", + "Snowball fight (Lg)", "Schneeballschlacht (Gr)", + "Snowy Island (Lg)", "Schneeinsel (Gr)", + "So Near So Far (Sm)", "So nah und doch so fern (Kl)", + "South America (Lg)", "Sdamerika (Gr)", + "Spring Line (Lg)", "Frhlingsgefhle (Gr)", + "Star (Lg)", "Stern (Gr)", + "Straighter & Narrower (Sm)", "Enger & schmaler (Kl)", + "TerrainSpotting (Sm)", "TerrainSpotting (Kl)", + "The Bay (Lg)", "Die Bucht (Gr)", + "The Garden (Lg)", "Der Garten (Gr)", + "The Great Lakes (Med)", "Die Grossen Seen (Mit)", + "The Ice Arena (Lg)", "Eisarena (Gr)", + "The Lake District (Lg)", "Kalte Seenplatte (Gr)", + "The Linked lands (Lg)", "Die verbundenen L„nder (Gr)", + "The Mississippi (Med)", "Grsse von Tom Sawyer (Mit)", + "The Sticky Bit (Lg)", "Der klebrige Teil (Gr)", + "The Valley (Med)", "Das Tal (Mit)", + "The Woods Today (Lg)", "Waldl„ufer (Gr)", + "Things to Come (Lg)", "Was die Zukunft bringt (Gr)", + "Tiger Core (Sm)", "Das Herz des Tigers (Kl)", + "To the Core (Sm)", "Mitten ins Herz (Kl)", + "Tournament Hills (Lg)", "Hgel der Entscheidung (Gr)", + "Tropical Storm (Med)", "Tropenstrme (Mit)", + "Tundra Trouble (Lg)", "Tauziehen in der Tundra (Gr)", + "Uk (Med)", "GB (Mit)", + "Undiscovered Country (Sm)", "Unentdecktes Land (Kl)", + "United States (Med)", "US (Mit)", + "Volcano (Sm)", "Vulkan (Kl)", + "Wastelands (Lg)", "Wstenei (Gr)", + "Water Works (Sm)", "Wasserwerke (Kl)", + "World Map (Med)", "Weltkarte (Kl)", + "Zambezi (Lg)", "Sambesi (Gr)", + +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Inselmuster (gross, 8 Spieler)", + "Arena Valley Extreme (Mega 8 players)", "Arenatal (sehr gross, 8 Spieler)", + "Around the Rim (Sm 4 players)", "Um die Kante (klein, 4 Spieler)", + "Ashes to Ashes (Lg 6 players)", "Asche zu Asche (gross, 6 Spieler)", + "Artic Wasteland (Mega 8 players)", "Arktische Wste (sehr gross, 8 Spieler)", + "Badajoz (Med 4 players)", "Badjoz (mittelgross, 4 Spieler)", + "Baptism of Fire (Lg 6 players)", "Feuertaufe (gross, 6 Spieler)", + "Big Fish, Small Pond (Lg 6 players)", "Grosser Fisch im kleinen Teich (gross, 6 Spieler)", + "Blue Lakes (Lg 8 players)", "Die blauen Seen (gross, 8 Spieler)", + "Booby Traps (Mega 8 players)", "Vorsicht, Falle! (sehr gross, 8 Spieler)", + "Bridgehead (Lg 6 players)", "Brckenkopf im Niemandsland (gross, 6 Spieler)", + "Butterfly Bay (Lg 6 players)", "Schmetterlingsbucht (gross, 6 Spieler)", + "Central Conflict Extreme (Mega 8 players)", "Zentraler Konflikt fr K”nner (sehr gross, 8 Spieler)", + "Circles of Death (Mega 8 players)", "Todeskreise (sehr gross, 8 Spieler)", + "Cold Front (Med 6 players)", "Kaltfront ( mittelgross, 6 Spieler)", + "Cold Pass (Med 4 players)", "Cooler Pass (mittelgross, 4 Spieler)", + "Combat Zones (Mega 8 players)", "Kampfgebiete (sehr gross, 8 Spieler)", + "Conflict Cove (Sm 4 players)", "H”hlenkonflikt (klein, 4 Spieler)", + "Culloden Moor (Med 8 players)", "Culloden-Moor (mittelgross, 8 Spieler)", + "Damnation Alley (Mega 8 players)", "Strasse der Verdammten (sehr gross, 8 Spieler)", + "Death Valley (Mega 8 players)", "Tal des Todes (sehr gross, 8 Spieler)", + "Deep Six (Mega 8 players)", "Tiefe Sechs (sehr gross, 8 Spieler)", + "Destruction Derby (Mega 8 players)", "Destruction Derby (sehr gross, 8 Spieler)", + "Diamonds Aren't Forever (Mega 8 players)", "Verg„ngliche Diamanten (sehr gross, 8 Spieler)", + "Elysium (Sm 4 players)", "Elysium (klein, 4 Spieler)", + "Equal Shares (Lg 4 players)", "Gleiche Anteile (gross, 4 Spieler)", + "Frost Bitten (Mega 8 players)", "Frostbrand (sehr gross, 8 Spieler)", + "Frozen Valley (Med 6 players)", "Eisiges Tal (mittelgross, 6 Spieler)", + "Gettysburg (Sm 4 players)", "Gettysburg (klein, 4 Spieler)", + "Glacial Valley (Sm 4 players)", "Gletschertal (klein, 4 Spieler)", + "Gold Coast (Med 6 players)", "Goldkste (mittelgross, 6 Spieler)", + "Gold Rush (Lg 4 players)", "Goldrausch (gross, 4 Spieler)", + "Habitat (Lg 4 players)", "Habitat (gross, 4 Spieler)", + "Hades Frozen Over (Sm 4 players)", "Frostschutz fr die H”lle (klein, 4 Spieler)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (sehr gross, 8 Spieler)", + "Hastings (Sm 4 players)", "Hastings (klein, 4 Spieler)", + "Hell's Pass (Med 6 players)", "H”llenpass (mittelgross, 6 Spieler)", + "Holy Grounds (Mega 8 players)", "Heiliger Boden (sehr gross, 8 Spieler)", + "Ice Bergs (Med 6 players)", "Eisberge (mittelgross, 6 Spieler)", + "Ice Station (Lg 6 players)", "Eisstation (gross, 6 Spieler)", + "Ice Queen (Lg 4 players)", "Eisk”nigin (gross, 4 Spieler)", + "In the Sun (Med 6 players)", "Unter der Sonne (mittelgross, 6 Spieler)", + "Innocents? (Mega 8 players)", "Unschuldig? Wer? (sehr gross, 8 Spieler)", + "Islands (Med 8 players)", "Inseln im Nebel (mittelgross, 8 Spieler)", + "Island Plateau (Lg 4 players)", "Inselplateau (gross, 4 Spieler)", + "Island Wars Extreme (Mega 8 players)", "Extremes Inselspringen (sehr gross, 8 Spieler)", + "Kananga (Med 6 players)", "Kananga (mittelgross, 6 Spieler)", + "King of the Hills Extreme (Mega 8 players)", "K”nig des Maulwurfshgels (sehr gross, 8 Spieler)", + "Lake Land (Lg 8 players)", "Seenland (gross, 8 Spieler)", + "Land Locked (Lg 8 players)", "Das Verschlossene Land (gross, 8 Spieler)", + "Lanes (Med 8 players)", "Gassenjungen (mittelgross, 8 Spieler)", + "Leipzip (Sm 4 players)", "Leipzig (klein, 4 Spieler)", + "Meander (Lg 8 players)", "M„ander (gross, 8 Spieler)", + "Mekong (Med 8 players)", "Mekong (mittelgross, 8 Spieler)", + "Middle Ground (Med 8 players)", "Mittelsmann (mittelgross, 8 Spieler)", + "Naval Conquests (Mega 8 players)", "Kommt zur Marine, haben sie gesagt (sehr gross, 8 Spieler)", + "On your Marks (Med 4 players)", "Auf die Pl„tze (mittelgross, 4 Spieler)", + "Open Warfare (Mega 8 players)", "Offener Schlagabtausch (sehr gross, 8 Spieler)", + "Ore Gardens (Lg 8 players)", "Erzparadies (gross, 8 Spieler)", + "Potholes (Mega 8 players)", "Schlagl”cher (sehr gross, 8 Spieler)", + "Puddles (Med 4 players)", "Pftzen (mittelgross, 4 Spieler)", + "Random Violence (Mega 8 players)", "Unberechenbare Gewalt (sehr gross, 8 Spieler)", + "Revenge (Med 8 players)", "Rache (mittelgross, 8 Spieler)", + "Rias (Med 8 players)", "Kabul (mittelgross, 8 Spieler)", + "River Crossing (Sm 4 players)", "Die Furt (klein, 4 Spieler)", + "River Rampage (Mega 8 players)", "Flussfahrt (sehr gross, 8 Spieler)", + "River Rapids (Lg 6 players)", "Stromschnellen (gross, 6 Spieler)", + "Rivers Wild (Mega 8 players)", "Wildwasser (sehr gross, 8 Spieler)", + "Rorkes Drift (Lg 4 players)", "Rorkes Drift (gross, 4 Spieler)", + "Seaside (Med 4 players)", "Strandleben (mittelgross, 4 Spieler)", + "Shades (Med 8 players)", "Schattenreich (mittelgross, 8 Spieler)", + "Smuggler's Cove (Lg 6 players)", "Schmugglerh”hle (gross, 6 Spieler)", + "Snow Garden (Sm 2 players)", "Schneegest”ber (klein, 2 Spieler)", + "Stalingrad (Sm 4 players)", "Stalingrad (klein, 4 Spieler)", + "Sticks & Stones (Med 4 players)", "Holz und Steine (mittelgross, 4 Spieler)", + "Strathearn Valley (Lg 6 players)", "Das Tal von Strathearn (gross, 6 Spieler)", + "Super Bridgehead (Mega 8 players)", "Super-Brckenkopf (sehr gross, 8 Spieler)", + "Super Mekong (Mega 8 players)", "Super-Mekong (sehr gross, 8 Spieler)", + "Super Ore Gardens (Mega 8 players)", "Super-Erzparadies (sehr gross, 8 Spieler)", + "Switch (Med 4 players)", "Schalter (mittelgross, 4 Spieler)", + "The Berg (Mega 8 players)", "Der Berg (sehr gross, 8 Spieler)", + "The Boyne (Med 4 players)", "Boyne (mittelgross, 4 Spieler)", + "The Bulge (Sm 4 players)", "Die W”lbung (klein, 4 Spieler)", + "The Cauldron (Lg 6 players)", "Der Kessel (gross, 6 Spieler)", + "The Finger (Lg 6 players)", "Der Finger (gross, 6 Spieler)", + "The Hills Have Eyes (Mega 8 players)", "Die Hgel haben Augen (sehr gross, 8 Spieler)", + "The Keyes (Med 6 players)", "Ein Sumpf (mittelgross, 6 Spieler)", + "The Lakes (Med 8 players)", "Die Seen (mittelgross, 8 Spieler)", + "The Neck (Med 6 players)", "Der Hals (mittelgross, 6 Spieler)", + "The Web (Lg 6 players)", "Das Netz (gross, 6 Spieler)", + "To the Core (Lg 4 players)", "Mitten ins Herz (gross, 4 Spieler)", + "Trafalgar (Lg 4 players)", "Trafalgar (gross, 4 Spieler)", + "Twin Rivers (Sm 4 players)", "Zwillingsstr”me (klein, 4 Spieler)", + "Umtumbo Gorge (Lg 4 players)", "Die Umtumbo-Schlucht (gross, 4 Spieler)", + "Watch Your Step Extreme (Mega 8 players)", "Vorsicht, Lebensgefahr (sehr gross, 8 Spieler)", + "Waterfalls (Lg 8 players)", "Wasserfall (gross, 8 Spieler)", + "Waterloo Revisited (Lg 6 players)", "Zu Besuch in Waterloo (gross, 6 Spieler)", + "Water Werks (Mega 8 players)", "Wasserwerk (sehr gross, 8 Spieler)", + "Warlord's Lake (Sm 4 players)", "Der See des Kriegsgottes (klein, 4 Spieler)", + "Zama (Sm 4 players)", "Zama (klein, 4 Spieler)", +//#endif + NULL +}; +#endif +#ifdef FRENCH +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Le Passage (Max)", + "Central Conflict (Lg)", "Conflit Central (Max)", + "Coastal Influence (Med)", "Le Chant des Canons (Moy)", + "Combat Alley (Med)", "Aux Armes! (Moy)", + "Desolation (Lg)", "D‚solation (Max)", + "Dugout Isle (Med)", "L'Ile Maudite (Moy)", + "Equal Opportunity (Sm)", "A Chances Egales (Min)", + "First Come, First Serve (Sm)", "La Loi du Plus Fort (Min)", + "Island Hoppers (Sm)", "D'une Ile … l'autre (Min)", + "Island Wars (Lg)", "Guerres Insulaires (Max)", + "Isle of Fury (Lg)", "L'Ile de la Furie(Max)", + "Ivory Wastelands (Sm)", "Terres d'Ivoire (Min)", + "Keep off the Grass (Sm)", "Hors de mon Chemin (Min)", + "Marooned II (Med)", "Isolement II (Moy)", + "Middle Mayhem (Sm)", "Chaos Interne (Min)", + "No Escape (Med)", "Le PiŠge (Moy)", + "No Man's Land (Med)", "No Man's Land (Moy)", + "Normandy (Med)", "Normandie (Moy)", + "North By Northwest (Lg)", "Nord, Nord-Ouest (Max)", + "Pond Skirmish (Med)", "Bain de Sang (Moy)", + "Raraku (Lg)", "Raraku (Max)", + "Ridge War (Med)", "Guerre au Sommet (Moy)", + "Shallow Grave (Med)", "La Saveur de la Mort (Moy)", + "Treasure Isle (Med)", "L'Ile au Tr‚sor (Moy)", + + "Africa (Lg)", "Afrique (Max)", + "Alaska Anarchy (Lg)", "Anarchie en Alaska (Max)", + "All that Glitters... (Lg)", "Tout ce qui brille... (Max)", + "Apre's Peace (Lg)", "Une Paix Durement N‚goci‚e... (Max)", + "Antartica (Lg)", "Antarctique (Max)", + "Armourgarden (Lg)", "La Guerre des Blind‚s (Max)", + "Austraila (Med)", "Australie (Moy)", + "Barrier to Entry (Lg)", "BarriŠre … l'Entr‚e (Max)", + "Bavarian Blast (Med)", "Tonnerre Bavarois (Moy)", + "Be Shore (Med)", "Plages Menac‚es (Moy)", + "Bearing Straits (Med)", "Droit Devant ! (Moy)", + "Blow Holes (Lg)", "CratŠres (Max)", + "Bonsai (Sm)", "Bonsa‹ (Min)", + "Brother Stalin (Lg)", "FrŠre Staline (Max)", + "Bullseye (Lg)", "L'oeil du Taureau (Max)", + "C&C (Med)", "C&C (Moy)", + "Camos Canyon (Med)", "Le Canyon (Moy)", + "Camos Coves (Lg)", "Criques (Max)", + "Camos Cross (Lg)", "La Croix de Guerre (Max)", + "Camos Crossing (Sm)", "La Crois‚e des Chemins (Min)", + "Central Arena (Lg)", "L'ArŠne Diabolique (Max)", + "Canyon River (Med)", "Au Milieu Coule Une RiviŠre (Moy)", + "Crossroads (Sm)", "Carrefours (Min)", + "Czech Mate (Lg)", "TchŠque et Mat (Max)", + "Dday (Med)", "Le Jour J (Moy)", + "Disaster Central (Lg)", "D‚sastre Central (Max)", + "Docklands (Med)", "L'Enfer des Docks (Moy)", + "East Coast (Med)", "C“te Est (Moy)", + "Eastern Seaboard (Lg)", "Rivages de l'Est (Max)", + "Finger Lake (Lg)", "Le Lac de tous les Dangers (Max)", + "Fjords (Med)", "Fjords (Moy)", + "Floodlands (Lg)", "Campagne Lacustre (Max)", + "Forest under fire (Lg)", "Forˆt en flammes (Max)", + "Four Corners (Lg)", "4 Coins (Max)", + "Frostbit Fjords (Lg)", "Fjords Gel‚s (Max)", + "Glenboig (Sm)", "Glenboig (Min)", + "Hell Frozen Over (Lg)", "Enfer de Glace Max)", + "India (Lg)", "Inde (Max)", + "Indirect Fire (Lg)", "Attaque Indirecte (Max)", + "Island Wars II (Lg)", "Guerres Insulaires II (Max)", + "Italy (Lg)", "Italie (Max)", + "Kabalo (Lg)", "Kabalo (Max)", + "King of the Hills (Lg)", "Le Roi des Montagnes (Max)", + "Lake Divide (Med)", "La Guerre du Lac (Moy)", + "Lakelands (Lg)", "Terres Submerg‚es (Max)", + "Land Ladder (Lg)", "Jusqu'au Sommet (Max)", + "Lotsa Lakes (Lg)", "Terres de Lacs (Max)", + "Lunar Battlefield (Lg Special)", "Combat Lunaire (Max Sp‚cial)", + "Malibu Fields (Med)", "Les Champs de Malibu (Moy)", + "Marshland (Med)", "Mar‚cages (Moy)", + "MyLai Delta (Med)", "Le Delta Mylai (Moy)", + "Natural Harbor (Med)", "Port Naturel (Moy)", + "No Way Out (Lg)", "Sans Issue (Max)", + "Normandy Landing (Lg)", "Le D‚barquement (Max)", + "Ore Wars (Med)", "La Guerre du Minerai (Moy)", + "Oz (Lg)", "Oz (Max)", + "Pilgrim Fathers II (Lg)", "Les PŠlerins 2 (Max)", + "Pip's Ice Tea (Med)", "Les Tranch‚es de Glace (Moy)", + "Polar Panic (Lg)", "Panique Polaire (Max)", + "Ponds (Med)", "Les Etangs (Moy)", + "Putney (Lg)", "La Meilleure D‚fense... (Max)", + "Return to Zion (Lg)", "Retour … Sion (Max)", + "Ring of Land (Lg)", "Le Cycle Infernal (Max)", + "River Basin (Lg)", "Confrontation Navale (Max)", + "River Delta (Med)", "Le Delta (Moy)", + "River Islands (Med)", "C“tes … Surveiller de PrŠs (Moy)", + "River Maze (Sm)", "Labyrinthe Fluvial (Min)", + "Rivers (Sm)", "RiviŠres (Min)", + "Run the Gauntlet (Med)", "Relevons le D‚fi ! (Moy)", + "Scappa Flow (Lg)", "Combats Sanglants (Max)", + "Siberian Slaughter (Lg)", "Carnage Sib‚rien (Max)", + "Sleepy Valley (Sm)", "La Vall‚e Endormie (Min)", + "Snake River (Lg)", "La RiviŠre aux Serpents (Max)", + "Snow Wars (Lg)", "Guerres de Neige (Max)", + "Snowball fight (Lg)", "Bataille de Boules de Neige (Max)", + "Snowy Island (Lg)", "L'Ile sous la Neige (Max)", + "So Near So Far (Sm)", "Si Loin, Si Proche (Min)", + "South America (Lg)", "Am‚rique du Sud (Max)", + "Spring Line (Lg)", "Ligne de Front (Max)", + "Star (Lg)", "Etoile (Max)", + "Straighter & Narrower (Sm)", "L'Entonnoir (Min)", + "TerrainSpotting (Sm)", "TerrainSpotting (Min)", + "The Bay (Lg)", "La Baie (Max)", + "The Garden (Lg)", "Le Jardin (Max)", + "The Great Lakes (Med)", "Les Grands Lacs (Moy)", + "The Ice Arena (Lg)", "L'ArŠne de Glace (Max)", + "The Lake District (Lg)", "Un Lac Imprenable (Max)", + "The Linked lands (Lg)", "Passages … Gu‚ (Max)", + "The Mississippi (Med)", "Mississippi (Moy)", + "The Sticky Bit (Lg)", "Marasme (Max)", + "The Valley (Med)", "La Vall‚e (Moy)", + "The Woods Today (Lg)", "Aujoud'hui: la Mort ! (Max)", + "Things to Come (Lg)", "D‚nouement Incertain (Max)", + "Tiger Core (Sm)", "Le Coeur du Tigre (Min)", + "To the Core (Sm)", "Le Coeur du Conflit (Min)", + "Tournament Hills (Lg)", "Combat en Altitude (Max)", + "Tropical Storm (Med)", "Ouragan Tropical (Moy)", + "Tundra Trouble (Lg)", "La Toundra (Max)", + "Uk (Med)", "Royaume Uni (Moy)", + "Undiscovered Country (Sm)", "Terre Inconnue (Min)", + "United States (Med)", "Etats Unis (Moy)", + "Volcano (Sm)", "Le Volcan (Min)", + "Wastelands (Lg)", "Terres D‚sol‚es (Max)", + "Water Works (Sm)", "Jeux d'Eau (Min)", + "World Map (Med)", "Carte du Monde (Moy)", + "Zambezi (Lg)", "ZambŠze (Max)", +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Archipel (Max. 8 joueurs)", + "Arena Valley Extreme (Mega 8 players)", "La Vall‚e de l'arŠne (XL 8 joueurs)", + "Around the Rim (Sm 4 players)", "Autour de la crˆte (Min. 4 joueurs)", + "Ashes to Ashes (Lg 6 players)", "R‚duit en cendres (Max. 6 joueurs)", + "Artic Wasteland (Mega 8 players)", "D‚solation arctique (XL 8 joueurs)", + "Badajoz (Med 4 players)", "Badjoz (Moy. 4 joueurs)", + "Baptism of Fire (Lg 6 players)", "Baptˆme du feu (Max. 6 joueurs)", + "Big Fish, Small Pond (Lg 6 players)", "Gros poisson, Min. Mare (Max. 6 joueurs)", + "Blue Lakes (Lg 8 players)", "Lacs bleus (Max. 8 joueurs)", + "Booby Traps (Mega 8 players)", "PiŠges (XL 8 joueurs)", + "Bridgehead (Lg 6 players)", "Tˆte de pont (Max. 6 joueurs)", + "Butterfly Bay (Lg 6 players)", "La baie du papillon (Max. 6 joueurs)", + "Central Conflict Extreme (Mega 8 players)", "Conflit central extrˆme (XL 8 joueurs)", + "Circles of Death (Mega 8 players)", "Les cercles de la mort (XL 8 joueurs)", + "Cold Front (Med 6 players)", "Front froid ( Moy. 6 joueurs)", + "Cold Pass (Med 4 players)", "La Passe Glac‚e (Moy. 4 joueurs)", + "Combat Zones (Mega 8 players)", "Zones de combat (XL 8 joueurs)", + "Conflict Cove (Sm 4 players)", "La Crique du conflit (Min. 4 joueurs)", + "Culloden Moor (Med 8 players)", "La Lande de Culloden (Moy. 8 joueurs)", + "Damnation Alley (Mega 8 players)", "Le chemin de la damnation (XL 8 joueurs)", + "Death Valley (Mega 8 players)", "La vall‚e de la mort (XL 8 joueurs)", + "Deep Six (Mega 8 players)", "Six de profondeur (XL 8 joueurs)", + "Destruction Derby (Mega 8 players)", "Stock car (XL 8 joueurs)", + "Diamonds Aren't Forever (Mega 8 players)", "Les diamants ne sont pas ‚ternels (XL 8 joueurs)", + "Elysium (Sm 4 players)", "Elys‚e (Min. 4 joueurs)", + "Equal Shares (Lg 4 players)", "Parts ‚gales (Max. 4 joueurs)", + "Frost Bitten (Mega 8 players)", "Engelures (XL 8 joueurs)", + "Frozen Valley (Med 6 players)", "La Vall‚e glac‚e (Moy. 6 joueurs)", + "Gettysburg (Sm 4 players)", "Gettysburg (Min. 4 joueurs)", + "Glacial Valley (Sm 4 players)", "Vall‚e de glace (Min. 4 joueurs)", + "Gold Coast (Med 6 players)", "La c“te dor‚e (Moy. 6 joueurs)", + "Gold Rush (Lg 4 players)", "La ru‚e vers l'or (Max. 4 joueurs)", + "Habitat (Lg 4 players)", "Habitat (Max. 4 joueurs)", + "Hades Frozen Over (Sm 4 players)", "Les enfers glac‚s (Min. 4 joueurs)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (XL 8 joueurs)", + "Hastings (Sm 4 players)", "Hastings (Min. 4 joueurs)", + "Hell's Pass (Med 6 players)", "La route de l'enfer (Moy. 6 joueurs)", + "Holy Grounds (Mega 8 players)", "Terres saintes (XL 8 joueurs)", + "Ice Bergs (Med 6 players)", "Icebergs (Moy. 6 joueurs)", + "Ice Station (Lg 6 players)", "Station glac‚e (Max. 6 joueurs)", + "Ice Queen (Lg 4 players)", "Reine des glaces (Max. 4 joueurs)", + "In the Sun (Med 6 players)", "Sous le soleil (Moy. 6 joueurs)", + "Innocents? (Mega 8 players)", "Innocents ? (XL 8 joueurs)", + "Islands (Med 8 players)", "Iles (Moy. 8 joueurs)", + "Island Plateau (Lg 4 players)", "Plateau des Œles (Max. 4 joueurs)", + "Island Wars Extreme (Mega 8 players)", "Guerres insulaires extrˆme (XL 8 joueurs)", + "Kananga (Med 6 players)", "Kananga (Moy. 6 joueurs)", + "King of the Hills Extreme (Mega 8 players)", "Roi des collines extrˆme (XL 8 joueurs)", + "Lake Land (Lg 8 players)", "Paysage lacustre (Max. 8 joueurs)", + "Land Locked (Lg 8 players)", "Enclave (Max. 8 joueurs)", + "Lanes (Med 8 players)", "Le parcours du combattant (Moy. 8 joueurs)", + "Leipzip (Sm 4 players)", "Leipzig (Min. 4 joueurs)", + "Meander (Lg 8 players)", "M‚andre (Max. 8 joueurs)", + "Mekong (Med 8 players)", "M‚kong (Moy. 8 joueurs)", + "Middle Ground (Med 8 players)", "Plateau m‚dian (Moy. 8 joueurs)", + "Naval Conquests (Mega 8 players)", "Conquˆtes navales (XL 8 joueurs)", + "On your Marks (Med 4 players)", "A vos marques (Moy. 4 joueurs)", + "Open Warfare (Mega 8 players)", "Guerre ouverte (XL 8 joueurs)", + "Ore Gardens (Lg 8 players)", "Jardins de minerai (Max. 8 joueurs)", + "Potholes (Mega 8 players)", "Nids de poules (XL 8 joueurs)", + "Puddles (Med 4 players)", "Flaques (Moy. 4 joueurs)", + "Random Violence (Mega 8 players)", "Violence al‚atoire (XL 8 joueurs)", + "Revenge (Med 8 players)", "Vengeance (Moy. 8 joueurs)", + "Rias (Med 8 players)", "Rias (Moy. 8 joueurs)", + "River Crossing (Sm 4 players)", "Passage … gu‚ (Min. 4 joueurs)", + "River Rampage (Mega 8 players)", "RiviŠre d‚chaŒn‚e (XL 8 joueurs)", + "River Rapids (Lg 6 players)", "Rapides (Max. 6 joueurs)", + "Rivers Wild (Mega 8 players)", "RiviŠres sauvages (XL 8 joueurs)", + "Rorkes Drift (Lg 4 players)", "L'Exode de Rorkes (Max. 4 joueurs)", + "Seaside (Med 4 players)", "C“te (Moy. 4 joueurs)", + "Shades (Med 8 players)", "Ombres (Moy. 8 joueurs)", + "Smuggler's Cove (Lg 6 players)", "La Crique du contrebandier (Max. 6 joueurs)", + "Snow Garden (Sm 2 players)", "Jardin de neige (Min. 2 joueurs)", + "Stalingrad (Sm 4 players)", "Stalingrad (Min. 4 joueurs)", + "Sticks & Stones (Med 4 players)", "Bƒton & Roches (Moy. 4 joueurs)", + "Strathearn Valley (Lg 6 players)", "La Vall‚e de Strathearn (Max. 6 joueurs)", + "Super Bridgehead (Mega 8 players)", "Super tˆte de pont (XL 8 joueurs)", + "Super Mekong (Mega 8 players)", "Super M‚kong (XL 8 joueurs)", + "Super Ore Gardens (Mega 8 players)", "Super jardin de minerai (XL 8 joueurs)", + "Switch (Med 4 players)", "Permutation (Moy. 4 joueurs)", + "The Berg (Mega 8 players)", "Le Berg (XL 8 joueurs)", + "The Boyne (Med 4 players)", "Le Boyne (Moy. 4 joueurs)", + "The Bulge (Sm 4 players)", "Le bombement (Min. 4 joueurs)", + "The Cauldron (Lg 6 players)", "Le chaudron (Max. 6 joueurs)", + "The Finger (Lg 6 players)", "Le doigt (Max. 6 joueurs)", + "The Hills Have Eyes (Mega 8 players)", "Les collines ont des yeux (XL 8 joueurs)", + "The Keyes (Med 6 players)", "Les Keyes (Moy. 6 joueurs)", + "The Lakes (Med 8 players)", "Les lacs (Moy. 8 joueurs)", + "The Neck (Med 6 players)", "Le goulot (Moy. 6 joueurs)", + "The Web (Lg 6 players)", "La toile (Max. 6 joueurs)", + "To the Core (Lg 4 players)", "Jusqu'au cour (Max. 4 joueurs)", + "Trafalgar (Lg 4 players)", "Trafalgar (Max. 4 joueurs)", + "Twin Rivers (Sm 4 players)", "Les deux riviŠres (Min. 4 joueurs)", + "Umtumbo Gorge (Lg 4 players)", "La Gorge de Umtumbo (Max. 4 joueurs)", + "Watch Your Step Extreme (Mega 8 players)", "Pas-…-pas extrˆme (XL 8 joueurs)", + "Waterfalls (Lg 8 players)", "Chutes d'eau (Max. 8 joueurs)", + "Waterloo Revisited (Lg 6 players)", "Waterloo II (Max. 6 joueurs)", + "Water Werks (Mega 8 players)", "Jeux d'eau (XL 8 joueurs)", + "Warlord's Lake (Sm 4 players)", "Le lac du guerrier (Min. 4 joueurs)", + "Zama (Sm 4 players)", "Zama (Min. 4 joueurs)", +//#endif + NULL +}; +#endif + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color); +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len); +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init = 0); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, + ListClass *gamelist, ColorListClass *playerlist, int join_index, + char *my_name, RejectType *why); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used); +void Start_WWChat(ColorListClass *playerlist); +int Update_WWChat(void); + + +#define PCOLOR_BROWN PCOLOR_GREY + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; +#ifdef WINSOCK_IPX + assert ( PacketTransport != NULL ); +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // This call allocates all necessary queue buffers, allocates Real-mode + // memory, and commands IPX to start listening on the Global Channel. + //------------------------------------------------------------------------ + if (!Ipx.Init()) { + return(false); + } + + //------------------------------------------------------------------------ + // Set up the IPX manager to cross a bridge + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (Session.IsBridge) { + Session.BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ +// +// Note: The thought behind this section of code was that if the program +// terminates early, without an EventClass::EXIT event, it still needs to +// tell the other systems that it's gone, so it would send a SIGN_OFF packet. +// BUT, this causes a sync bug if the systems are running slow and this system +// is running ahead of the others; it will send the NET_SIGN_OFF >>before<< +// the other system execute their EventClass::EXIT event, and the other systems +// will kill the connection at some random Frame # & turn my stuff over to +// the computer possibly at different times. +// BRR, 10/29/96 +// +#if 0 + //------------------------------------------------------------------------ + // If the Players vector contains at least one name, send a sign-off + // packet. If 'Players' is empty, I have no name, so there's no point + // in sending a sign-off. + //------------------------------------------------------------------------ + if (Session.Players.Count()) { + //..................................................................... + // Build a sign-off packet & send it + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + } + + //..................................................................... + // Wait for the packets to finish going out (or the Global Channel + // times out) + //..................................................................... + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + } +#endif + + //------------------------------------------------------------------------ + // If I was in a game, I'm not now, so clear the game name + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + +} /* end of Shutdown_Network */ + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * Session.GameName must have been filled in before this function can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + //------------------------------------------------------------------------ + // If our Players vector is empty, just return. + //------------------------------------------------------------------------ + if (Session.Players.Count()==0) { + return (true); + } + + //------------------------------------------------------------------------ + // Another system asking what game this is + //------------------------------------------------------------------------ + if (packet->Command==NET_QUERY_GAME && Session.NetStealth==0) { + + //..................................................................... + // If the game is closed, let every player respond, and let the sender of + // the query sort it all out. This way, if the game's host exits the game, + // the game still shows up on other players' dialogs. + // If the game is open, only the game owner may respond. + //..................................................................... + if (strlen(Session.GameName) > 0 && ((!Session.NetOpen) || + (Session.NetOpen && + !strcmp(Session.Players[0]->Name,Session.GameName)))) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, Session.GameName); + mypacket.GameInfo.IsOpen = Session.NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } + + //------------------------------------------------------------------------ + // Another system asking what player I am + //------------------------------------------------------------------------ + else if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, Session.GameName) && + (strlen(Session.GameName) > 0) && Session.NetStealth==0) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); // changed DRD 9/26 + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, Session.Players[0]->Name); + mypacket.PlayerInfo.House = Session.House; + mypacket.PlayerInfo.Color = Session.ColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(Session.GameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + + return(false); + +} /* end of Process_Global_Packet */ + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player * + * on this connection * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + if (Debug_Print_Events) { + printf("Destroying connection for house %d (%s)\n", + id,HouseClass::As_Pointer((HousesType)id)->IniName); + } + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + //------------------------------------------------------------------------ + // Create a message to display to the user + //------------------------------------------------------------------------ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + //------------------------------------------------------------------------ + // Delete the IPX connection + //------------------------------------------------------------------------ + Ipx.Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + //------------------------------------------------------------------------ + // If we're the last player left, tell the user. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // Init my game name to 0-length, since I haven't joined any game yet. + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Keep looping until something useful happens. + //------------------------------------------------------------------------ + while (1) { + //..................................................................... + // Pop up the network Join/New dialog + //..................................................................... + rc = Net_Join_Dialog(); + + //..................................................................... + // -1 = user selected Cancel + //..................................................................... + if (rc==-1) { + Session.NetStealth = stealth; + Session.NetOpen = 0; + return(false); + } + + //..................................................................... + // 0 = user has joined an existing game; save values & return + //..................................................................... + else if (rc==0) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } + + //..................................................................... + // 1 = user requests New Network Game + //..................................................................... + else if (rc==1) { + //.................................................................. + // Pop up the New Network Game dialog; if user selects OK, return + // 'true'; otherwise, return to the Join Dialog. + //.................................................................. + if (Net_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + +} /* end of Remote_Connect */ + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 17 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_name_w = 70 *RESFACTOR; + int d_name_h = 9 *RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + (2*RESFACTOR); + +#ifdef OLDWAY + int d_gdi_w = 40 *RESFACTOR; + int d_gdi_h = 9 *RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 *RESFACTOR; + int d_nod_h = 9 *RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 *RESFACTOR; + int d_color_h = 9 *RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_gamelist_w = 155 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 - 2*RESFACTOR; + int d_gamelist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +//BG int d_playerlist_w = 113 *RESFACTOR; + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_playerlist_w - 2*RESFACTOR); + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_count_w = 25 *RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + d_margin2; + + int d_level_w = 25 *RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 *RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 *RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_level_h; + + int d_options_w = d_playerlist_w; + int d_options_h = ((5 * 6) + 4) *RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin2 - (2*RESFACTOR); + + int d_message1_w = d_dialog_w - (d_margin1 * 2) + 4*RESFACTOR; + int d_message1_h = (14 * d_txt6_h) +3*RESFACTOR; + int d_message1_x = d_dialog_x + (d_dialog_w-d_message1_w)/2; + int d_message1_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message1_h); + + int d_message2_w = d_message1_w; + int d_message2_h = (8 * d_txt6_h) + 3*RESFACTOR; + int d_message2_x = d_message1_x; + int d_message2_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message2_h); + +#ifdef FRENCH //VG2 + int d_join_w = 60 *RESFACTOR; +#else + int d_join_w = 40 *RESFACTOR; +#endif + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_dialog_y + d_dialog_h - d_join_h - 8*RESFACTOR; + + int d_cancel_w = 50 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_join_y; + +#ifdef FRENCH + int d_new_w = 60 *RESFACTOR; +#else + int d_new_w = 40 *RESFACTOR; +#endif + int d_new_h = 9 *RESFACTOR; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_join_y; + + int d_send_w = d_message1_w; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_message1_x; + int d_send_y = d_message1_y + d_message1_h; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {71 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + char txt[128]; + char const *p; + int parms_received = 0; // 1 = game options received + int found; + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + int goto_lobby; + bool messages_have_focus = true; // Gadget focus starts on the message system + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w); +#else + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, TPF_BUTTON, d_new_x, d_new_y, d_new_w); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + StaticButtonClass descrip(0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + 16*RESFACTOR, d_name_y, d_dialog_w - 32*RESFACTOR, d_txt6_h+1); + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color(&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color(&ColorRemaps[Session.ColorIdx]); + } + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Rule.IsMPBasesOn); + optionlist.Check_Item(1, Rule.IsMPTiberiumGrow); + optionlist.Check_Item(2, Rule.IsMPCrates); + optionlist.Check_Item(3, Rule.IsMPCaptureTheFlag); + optionlist.Check_Item(4, Rule.IsMPShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { +// for (HousesType house = HOUSE_FIRST; house <= HOUSE_TURKEY; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Session.Options.AIPlayers); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif +#ifdef WIN32 +//char *fred; +#endif + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + } + } + + /* + ** Collapse the country list if we are going to redraw the game list + */ + if (gamelist.Is_To_Redraw() && housebtn.IsDropped) { + housebtn.Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + housebtn.Collapse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + //............................................................... + // For game-browsing, label the name, side, & color buttons: + //............................................................... + if (joinstate < JOIN_CONFIRMED) { + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x + d_gdi_w, + d_gdi_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, + d_house_x + (d_house_w / 2), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + Fancy_Text_Print(TXT_COLOR_COLON, + d_dialog_x + ((d_dialog_w / 4) * 3), + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + } else { + //............................................................... + // If we're joined to a game, just print the player's name & side. + //............................................................... +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (txt, Text_String(TXT_S_PLAYING_S), namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + Fancy_Text_Print(txt,d_dialog_cx, d_dialog_y + d_margin2 + (1*RESFACTOR), + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx], + TBLACK, TPF_CENTER | TPF_TEXT); + } + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); +#ifdef OLDWAY + gdibtn.Zap(); + nodbtn.Zap(); +#else + housebtn.Zap(); +#endif + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + countgauge.Zap(); + levelgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + staticcount.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + //............................................................... + // Only add the name edit field, the House, Join & New buttons if + // we're doing nothing, or we've just been rejected. + //............................................................... + if (joinstate < JOIN_CONFIRMED) { +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } else { + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + staticcount.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + descrip.Add_Tail(*commands); + } + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS && joinstate < JOIN_CONFIRMED) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the message system; erase old messages first + //.................................................................. + if (display >= REDRAW_MESSAGE) { + if (joinstate==JOIN_CONFIRMED) { + Draw_Box(d_message2_x, d_message2_y, d_message2_w, d_message2_h, BOXSTYLE_BOX, true); + } else { + Draw_Box(d_message1_x, d_message1_y, d_message1_w, d_message1_h, BOXSTYLE_BOX, true); + } + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received && joinstate >= JOIN_CONFIRMED) { + + //............................................................... + // Scenario title + //............................................................... +// LogicPage->Fill_Rect(d_dialog_x + 16 *RESFACTOR, d_name_y, d_dialog_x + d_dialog_w - 16 *RESFACTOR, d_name_y + d_txt6_h, BLACK); + + p = Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { + + // EW - Scenario language translation goes here!!!!!!!! VG + for (int ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[ii])) { + #if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[ii+1]); + #else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + descrip.Set_Text(txt); + +// sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +// descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, scheme, BLACK, TPF_TEXT | TPF_CENTER, p, Session.Options.ScenarioDescription); + } else { + sprintf(txt, "%s %s", p, Text_String(TXT_NOT_FOUND)); + descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER, p, Text_String(TXT_NOT_FOUND)); + } + //............................................................... + // Unit count, tech level, credits, ai players + //............................................................... +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, d_count_x + d_count_w + 35 *RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + Fancy_Text_Print(TXT_COUNT, d_count_x - 2 *RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticcount.Set_Text(txt); + staticcount.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2 *RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2 *RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2 *RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2 *RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2 *RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + if (input & KN_BUTTON) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // Mouse Click: + // If we're joined to a game, display an error if the user tries to + // modify a read-only control. + // If user clicks on a color button: + // - If we've joined a game, don't allow a new color selection + // - otherwise, select that color + // - Change the color of the user's name & message field to match + // the newly-selected color. + //.................................................................. + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) { + if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + break; + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color (&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + } + name_edt.Flag_To_Redraw(); + + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + + display = REDRAW_COLORS; + } + break; + + //.................................................................. + // User clicks on the game list: + //.................................................................. + case (BUTTON_GAMELIST | KN_BUTTON): + //............................................................... + // Handle a double-click + //............................................................... + if (lastclick_timer < 30 && gamelist.Current_Index() == lastclick_idx) { + + //............................................................ + // If we're in a game, & the item clicked on is a different + // game, un-join the game we're in. + //............................................................ + if ((joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) && + lastclick_idx != game_index) { + if (gamelist.Current_Index() == 0) { + goto_lobby = 1; + } else { + goto_lobby = 0; + } + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, goto_lobby, d_message1_x, d_message1_y, d_txt6_h, + d_send_x, d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + + //......................................................... + // Clear the Player vector & the player list box, since + // our game_index has changed. + //......................................................... + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + } + //............................................................ + // If we clicked on another game, join that game. + //............................................................ + if (joinstate != JOIN_CONFIRMED && + joinstate != JOIN_WAIT_CONFIRM && lastclick_idx > 0) { + gamelist.Set_Selected_Index(lastclick_idx); + game_index = lastclick_idx; + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + //............................................................ + // Otherwise, we must have joined the lobby + //............................................................ + if (game_index == 0) { + Clear_Listbox (&playerlist); + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + display = REDRAW_ALL; + } + } else { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = gamelist.Current_Index(); + + //............................................................ + // If we've joined a game, don't allow the selected item to + // change + //............................................................ + if (joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) { + gamelist.Set_Selected_Index(game_index); + } + + //............................................................ + // If we're not in a game, and the user clicks on a different + // entry, clear the player list & send a player query; + // init the click timer, to detect a double-click of this item. + //............................................................ + else if (gamelist.Current_Index() != game_index) { + + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + break; + +#ifdef OLDWAY + //.................................................................. + // House Buttons: set the player's desired House + //.................................................................. + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; +#else +#endif + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_MESSAGE; + } + break; + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + } + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, d_message1_x, d_message1_y, d_txt6_h, d_send_x, + d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } else { + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + process = false; + rc = -1; + } + break; + + //.................................................................. + // NEW: bail out with return code 1 + //.................................................................. + case (BUTTON_NEW | KN_BUTTON): + //............................................................... + // Force user to enter a name + //............................................................... + if (strlen(namebuf)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + + //............................................................... + // Ensure name is unique + //............................................................... + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!stricmp(Session.Games[i]->Name, namebuf)) { + found = 1; + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAMENAME_MUSTBE_UNIQUE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + } + + if (found) { + break; + } + + //............................................................... + // Save player & game name + //............................................................... + strcpy(Session.Handle,namebuf); + strcpy(Session.GameName,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Service keyboard input for any message being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + // (We have to redraw the edit line, to erase the cursor.) + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + // Rather than setting 'display', which would redraw all msgs, + // we only need to erase & redraw the edit box here. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'Input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, namebuf); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // If we're joined in a game, send the message to every player + // in our player list. Skip the local system (index 0). + //............................................................ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + } else { + //............................................................ + // Otherwise, send the message to all players in our chat list. + //............................................................ + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + if (Obfuscate(Session.GPacket.Message.Buf) == 0x72A47EF6) { + Session.WWChat = 1; + Clear_Listbox (&playerlist); + Start_WWChat(&playerlist); + } + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + display = REDRAW_MESSAGE; + } + +#ifdef OBSOLETE + // + // This is for the old drop-down list of houses, not used any more. + // + if (housebtn.IsDropped) { + isdropped = 1; + } else if (isdropped) { + display = REDRAW_ALL; + } +#endif + + break; + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) { + load_game = 1; + } + + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + + bool ready_packet_was_sent = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + + /* + ** If the scenario that the host wants to play doesn't exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I don't have. Request that the host sends the + ** scenario to me provided it's not an official scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + + Ipx.Set_Timing (30, (unsigned long) -1, 600); + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + rc = 0; + process = false; + + } else if (joinstate==JOIN_CONFIRMED) { + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (d_message2_x + 1 *RESFACTOR, d_message2_y + 1 *RESFACTOR, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + } else if (joinstate==JOIN_REJECTED) { + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_REQUEST_DENIED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + + item = NULL; + if (why==REJECT_DUPLICATE_NAME) { + item = (char *)Text_String(TXT_NAME_MUSTBE_UNIQUE); + } + else if (why==REJECT_GAME_FULL) { + item = (char *)Text_String(TXT_GAME_FULL); + } + else if (why==REJECT_VERSION_TOO_OLD) { + item = (char *)Text_String(TXT_YOURGAME_OUTDATED); + } + else if (why==REJECT_VERSION_TOO_NEW) { + item = (char *)Text_String(TXT_DESTGAME_OUTDATED); + } + else if (why==REJECT_MISMATCH) { + item = (char *)Text_String(TXT_MISMATCH); + } + else if (why==REJECT_DISBANDED) { + item = (char *)Text_String(TXT_GAME_CANCELLED); + } + if (item) { + Session.Messages.Add_Message(NULL, 0, item, PCOLOR_BROWN, TPF_TEXT, 1200); + } + + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + display = REDRAW_ALL; + + } + } else if (event == EV_GAME_OPTIONS) { + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } else { + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + optionlist.Check_Item(0,Session.Options.Bases); + optionlist.Check_Item(1,Session.Options.Tiberium); + optionlist.Check_Item(2,Session.Options.Goodies); + optionlist.Check_Item(3,Special.IsCaptureTheFlag); + optionlist.Check_Item(4,Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } else if (event == EV_MESSAGE) { + //..................................................................... + // Draw an incoming message + //..................................................................... + display = REDRAW_MESSAGE; + Sound_Effect(VOC_INCOMING_MESSAGE); + } else if (event == EV_NEW_GAME) { + //..................................................................... + // If a new game has formed, or an existing game has changed state + // (from open to closed or closed to open), redraw the message system. + //..................................................................... + display = REDRAW_MESSAGE; + } else if (event == EV_NEW_PLAYER || event == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + } else if (event == EV_GAME_SIGNOFF) { + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + gamelist.Flag_To_Redraw(); + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + + //..................................................................... + // If I've changed my name or color, make sure those changes go into + // the Chat vector. + //..................................................................... + strcpy(Session.Chat[0]->Name, namebuf); + Session.Chat[0]->Chat.Color = Session.ColorIdx; + if (Session.Chat[0]->Chat.Color == PCOLOR_DIALOG_BLUE) { + Session.Chat[0]->Chat.Color = PCOLOR_REALLY_BLUE; + } + + //..................................................................... + // Clean out the chat vector. If we find a node that we haven't heard + // from in 6 seconds, delete that node. + // If we haven't heard from a node in 5 seconds, send him a request + // for a chat announcement; he then has 1 second to reply. + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + if (TickCount - Session.Chat[i]->Chat.LastTime > 360) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + } else if (TickCount - Session.Chat[i]->Chat.LastTime > 300 && + Session.Chat[i]->Chat.LastChance == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Name[0] = 0; + Session.GPacket.Command = NET_CHAT_REQUEST; + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &(Session.Chat[i]->Address)); + Ipx.Service(); + Session.Chat[i]->Chat.LastChance = 1; + } + } + + //..................................................................... + // Manage the Lobby list: + // If the user has selected the 1st Game ("Lobby"), the names of all + // users in the Chat area show up in the Players list box. + // Users can be changing their names and their colors at any time, so + // we scan the Chat list each time to see if anything's changed; if + // so, we redraw the player list. + // (If WWChat is on, the Chat list is ignored, and the playerlist + // contains custom names.) + //..................................................................... + if (game_index == 0) { + if (!Session.WWChat) { + while (Session.Chat.Count() > playerlist.Count()) { + item = new char [MPLAYER_NAME_MAX]; + item[0] = 0; + playerlist.Add_Item(item); + playerlist.Flag_To_Redraw(); + } + while (playerlist.Count() > Session.Chat.Count()) { + item = (char *)playerlist.Get_Item(0); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + for (i = 0; i < Session.Chat.Count(); i++) { + if (stricmp(Session.Chat[i]->Name,playerlist.Get_Item(i)) || + &ColorRemaps[ (Session.Chat[i]->Chat.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.Chat[i]->Chat.Color] != + playerlist.Colors[i]) { + + playerlist.Colors[i] = + &ColorRemaps[Session.Chat[i]->Chat.Color]; + if (playerlist.Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist.Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + strcpy((char *)playerlist.Get_Item(i), Session.Chat[i]->Name); + playerlist.Flag_To_Redraw(); + } + } + } else { + if (stricmp(Session.Chat[0]->Name,playerlist.Get_Item(0)) || + &ColorRemaps[Session.Chat[0]->Chat.Color] != + playerlist.Colors[0]) { + playerlist.Colors[0] = &ColorRemaps[Session.Chat[0]->Chat.Color]; + strcpy((char *)playerlist.Get_Item(0), Session.Chat[0]->Name); + playerlist.Flag_To_Redraw(); + } + if (Update_WWChat()) { + display = REDRAW_MESSAGE; + } + } + } + + } + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + starttime = TickCount; + while (TickCount - starttime < i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ +// Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, +// Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Remove the chat edit box + //------------------------------------------------------------------------ + Session.Messages.Remove_Edit(); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +} /* end of Net_Join_Dialog */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color) +{ + //------------------------------------------------------------------------ + // Validate join_index + //------------------------------------------------------------------------ + if (join_index < 1) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_MUST_SELECT_GAME), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + if ( (Session.Games.Count()<=1) || join_index > Session.Games.Count()) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NOTHING_TO_JOIN), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // Force user to enter a name + //------------------------------------------------------------------------ + if (strlen(playername)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // The game must be open + //------------------------------------------------------------------------ + if (!Session.Games[join_index]->Game.IsOpen) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAME_IS_CLOSED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return (false); + } + + //------------------------------------------------------------------------ + // Send packet to game's owner + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_JOIN; + strcpy (Session.GPacket.Name, playername); + Session.GPacket.PlayerInfo.House = house; + Session.GPacket.PlayerInfo.Color = color; +#ifdef FIXIT_VERSION_3 + // Guest sends host his version. + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + if( Is_Aftermath_Installed() ) + { +// debugprint( "Guest tells host 'I have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version() | 0x80000000; + } + else + { +// debugprint( "Guest tells host 'I don't have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); + } +#else + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); +#endif + Session.GPacket.PlayerInfo.MaxVersion = VerNum.Max_Version(); + Session.GPacket.PlayerInfo.CheatCheck = RuleINI.Get_Unique_ID(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[join_index]->Address)); + + return(true); + +} /* end of Request_To_Join */ + + +/*************************************************************************** + * Unjoin_Game -- Cancels joining a game * + * * + * INPUT: * + * namebuf current player name * + * joinstate current join state * + * gamelist ListBox of game names * + * playerlist ListBox of player names * + * game_index index in 'gamelist' of game we're leaving * + * goto_lobby true = we're going to the lobby * + * msg_x message system x-coord * + * msg_y message system y-coord * + * msg_h message system char height * + * send_x message system send x-coord * + * send_y message system send y-coord * + * msg_len message system max msg length * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/12/1995 BRR : Created. * + *=========================================================================*/ +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len) +{ + int i; + char *item; + + //------------------------------------------------------------------------ + // Fill in a SIGN_OFF packet + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + + //------------------------------------------------------------------------ + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //------------------------------------------------------------------------ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + if (joinstate == JOIN_WAIT_CONFIRM || joinstate == JOIN_CONFIRMED) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[game_index]->Address)); + } + + //------------------------------------------------------------------------ + // Re-init the message system to its new larger size + //------------------------------------------------------------------------ + Session.Messages.Init (msg_x + 1, msg_y + 1, 14, + msg_len, msg_h, send_x + 1, send_y + 1, 1, + 20, msg_len - 5); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_'); + + //------------------------------------------------------------------------ + // Remove myself from the player list, and reset my game name + //------------------------------------------------------------------------ + if (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + } + + if (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // Highlight "Lobby" on the Game list, Announce I'm ready to chat + //------------------------------------------------------------------------ + if (goto_lobby) { + gamelist->Set_Selected_Index(0); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + } + +} // end of Unjoin_Game + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * joinstate our current joinstate * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * chatnow if 1, will immediately send the chat announcement * + * myname user's name * + * init initialize the timers * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init) +{ + //........................................................................ + // These values control the timeouts for sending various types of packets; + // they're designed such that they'll rarely occur simultaneously. + //........................................................................ + enum { + GAME_QUERY_TIME = 120, + PLAYER_QUERY_TIME = 35, + CHAT_ANNOUNCE_TIME = 83, + }; + static CDTimerClass game_timer; // time between NET_QUERY_GAME's + static CDTimerClass player_timer; // time between NET_QUERY_PLAYERS's + static CDTimerClass chat_timer; // time between NET_CHAT_ANNOUNCE's + + + //------------------------------------------------------------------------ + // Initialize timers + //------------------------------------------------------------------------ + if (init) { + game_timer = GAME_QUERY_TIME; + player_timer = PLAYER_QUERY_TIME; + chat_timer = CHAT_ANNOUNCE_TIME; + } + + //------------------------------------------------------------------------ + // Send the game-name query if the time has expired, or we're told to do + // it right now + //------------------------------------------------------------------------ + if (!game_timer || gamenow) { + + game_timer = GAME_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the player query for the game currently clicked on, if the time has + // expired and there is a currently-selected game, or we're told to do it + // right now + //------------------------------------------------------------------------ + if ( ((curgame > 0) && (curgame < Session.Games.Count()) && + !player_timer) || playernow) { + + player_timer = PLAYER_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_PLAYER; + strcpy (Session.GPacket.Name, Session.Games[curgame]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the chat announcement + //------------------------------------------------------------------------ + if ((!chat_timer && joinstate!=JOIN_CONFIRMED) || chatnow) { + + chat_timer = CHAT_ANNOUNCE_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy (Session.GPacket.Name, myname); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * Session.House (from NET_CONFIRM_JOIN) * + * Session.ColorIdx (from NET_CONFIRM_JOIN) * + * Session.Options.Bases (from NET_GAME_OPTIONS) * + * Session.Options.Tiberium (from NET_GAME_OPTIONS) * + * Session.Options.Goodies (from NET_GAME_OPTIONS) * + * Session.Options.Ghosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * my_name name of local system * + * why ptr: filled in with reason for rejection from a game * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index, char *my_name, RejectType *why) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) + return(EV_NONE); + + //------------------------------------------------------------------------ + // If we're joined in a game, handle the packet in a standard way; otherwise, + // don't answer standard queries. + //------------------------------------------------------------------------ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&Session.GPacket,&Session.GAddress)!=0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // NET_ANSWER_GAME: Another system is answering our GAME query, so add that + // system to our list box if it's new. + //------------------------------------------------------------------------ + if (Session.GPacket.Command==NET_ANSWER_GAME) { + + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name)) { + found = 1; + + //............................................................... + // If name was found, update the node's time stamp & IsOpen flag. + //............................................................... + Session.Games[i]->Game.LastTime = TickCount; + if (Session.Games[i]->Game.IsOpen != Session.GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME), + Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + Session.Games[i]->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + + //............................................................ + // If this game has gone from closed to open, copy the + // responder's address into our Game slot, since the guy + // responding to this must be game owner. + //............................................................ + if (Session.Games[i]->Game.IsOpen) { + Session.Games[i]->Address = Session.GAddress; + } + + //............................................................ + // If we're in chat mode, print a message that the state of + // this game has changed. + //............................................................ + if (*joinstate < JOIN_CONFIRMED) { + if (Session.Games[i]->Game.IsOpen) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_FORMING); + } + else { + sprintf(txt,Text_String(TXT_GAME_NOW_IN_PROGRESS), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_CLOSED); + } + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + retcode = EV_NEW_GAME; + } + } + break; + } + } + + //..................................................................... + // name not found (or addresses are different); add it to 'Games' + //..................................................................... + if (found==0) { + + //.................................................................. + // Create a new node structure, fill it in, add it to 'Games' + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount; + Session.Games.Add (who); + + //.................................................................. + // Create a string for "xxx's Game", leaving room for brackets around + // the string if it's a closed game + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + gamelist->Add_Item(item); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this game is open, display a message stating that it's + // now available. + //.................................................................. + if (Session.GPacket.GameInfo.IsOpen && (*joinstate) < JOIN_CONFIRMED) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.GPacket.Name); + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_GAME_FORMING); + } + + retcode = EV_NEW_GAME; + } + } + + //------------------------------------------------------------------------ + // NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add + // it to our player list box & the Player Vector if it's new + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_ANSWER_PLAYER) { + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // If the address is already present, re-copy their name, color & + // house into the existing entry, in case they've changed it without + // our knowledge; set the 'found' flag so we won't create a new entry. + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + strcpy(Session.Players[i]->Name, Session.GPacket.Name); + Session.Players[i]->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players[i]->Player.Color = Session.GPacket.PlayerInfo.Color; + + playerlist->Colors[i] = + &ColorRemaps[Session.GPacket.PlayerInfo.Color]; + + if (playerlist->Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist->Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + + found = 1; + break; + } + } + + //..................................................................... + // Don't add this player if he's not part of the game that's selected. + //..................................................................... + i = gamelist->Current_Index(); + if (Session.Games.Count() && Session.GPacket.PlayerInfo.NameCRC != + Compute_Name_CRC(Session.Games[i]->Name)) { + found = 1; + } + + //..................................................................... + // Don't add this player if it's myself. (We must check the name + // since the address of myself in 'Players' won't be valid.) + //..................................................................... + if (!strcmp (my_name,Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // name not found, or address didn't match; add to player list box + // & Players Vector + //..................................................................... + if (found==0) { + //.................................................................. + // Create & add a node to the Vector + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + who->Player.Color = Session.GPacket.PlayerInfo.Color; + Session.Players.Add (who); + + //.................................................................. + // Create & add a string to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this player has joined our game, play a special sound. + //.................................................................. + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_JOINED); + } + + retcode = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us + // as being confirmed, and start answering queries from other systems + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (Session.GameName, Session.GPacket.Name); + Session.House = Session.GPacket.PlayerInfo.House; + Session.ColorIdx = Session.GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + // the dialog state to its first pop-up state. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_REJECT_JOIN) { + //..................................................................... + // If we're confirmed in a game, broadcast a sign-off to tell all other + // systems that I'm no longer a part of any game; this way, I'll be + // properly removed from their dialogs. + //..................................................................... + if ( (*joinstate) == JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name,my_name); + + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Session.GameName[0] = 0; + + //.................................................................. + // remove myself from the player list + //.................................................................. + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + + (*joinstate) = JOIN_REJECTED; + (*why) = REJECT_BY_OWNER; + retcode = EV_STATE_CHANGE; + } + //..................................................................... + // If we're waiting for confirmation & got rejected, tell the user why + //..................................................................... + else if ((*joinstate) == JOIN_WAIT_CONFIRM) { + (*why) = (RejectType)Session.GPacket.Reject.Why; + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_GAME_OPTIONS: The game owner has changed the game options & is + // sending us the new values. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED || (*joinstate)==JOIN_WAIT_CONFIRM) { + Session.Options.Credits = Session.GPacket.ScenarioInfo.Credits; + Session.Options.Bases = Session.GPacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = Session.GPacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = Session.GPacket.ScenarioInfo.IsGoodies; +// Session.Options.Ghosts = Session.GPacket.ScenarioInfo.IsGhosties; + Session.Options.AIPlayers = Session.GPacket.ScenarioInfo.AIPlayers; + BuildLevel = Session.GPacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = Session.GPacket.ScenarioInfo.UnitCount; + Seed = Session.GPacket.ScenarioInfo.Seed; + Special = Session.GPacket.ScenarioInfo.Special; + Options.GameSpeed = Session.GPacket.ScenarioInfo.GameSpeed; + +#ifdef FIXIT_VERSION_3 + // Guest receives game version number from host. + // Added to the transmitted version number is a bit indicating presence of Aftermath expansion. + unsigned long lVersion = Session.GPacket.ScenarioInfo.Version & ~0x80000000; // Actual version number. + Session.CommProtocol = VerNum.Version_Protocol( lVersion ); + bAftermathMultiplayer = Session.GPacket.ScenarioInfo.Version & 0x80000000; +// if( bAftermathMultiplayer ) +// debugprint( "Guest hears host say 'This is an Aftermath game'\n" ); +// else +// debugprint( "Guest hears host say 'This is NOT an Aftermath game'\n" ); +#else + Session.CommProtocol = VerNum.Version_Protocol(Session.GPacket.ScenarioInfo.Version); + PlayingAgainstVersion = Session.GPacket.ScenarioInfo.Version; +#endif + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Rule.IsTGrowth = 1; + Special.IsTSpread = 1; + Rule.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Rule.IsTGrowth = 0; + Special.IsTSpread = 0; + Rule.IsTSpread = 0; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, Session.GPacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, Session.GPacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest, sizeof (Session.GPacket.ScenarioInfo.FileDigest)); +#else + strcpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = Session.GPacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = Session.GPacket.ScenarioInfo.FileLength; + + retcode = EV_GAME_OPTIONS; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // both the game list & player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + //..................................................................... + // Remove this name from the list of games + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name) && + Session.Games[i]->Address==Session.GAddress) { + + //............................................................... + // If the system signing off is the currently-selected list + // item, clear the player list since that game is no longer + // forming. + //............................................................... + if (i==gamelist->Current_Index()) { + Clear_Listbox (playerlist); + Clear_Vector (&Session.Players); + } + + //............................................................... + // If the system signing off was the owner of our game, mark + // ourselves as rejected + //............................................................... + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + (*why) = REJECT_DISBANDED; + } + + //............................................................... + // Set my return code + //............................................................... + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } + else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + //............................................................... + // Remove game name from game list + //............................................................... + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + + //..................................................................... + // Remove this name from the list of players + //..................................................................... + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + playerlist->Flag_To_Redraw(); + + //............................................................... + // If this player has left our game, play a special sound. + //............................................................... + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_LEFT); + } + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + //..................................................................... + // Remove this name from the chat list + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Chat[i]->Address==Session.GAddress) { + + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + } + + //------------------------------------------------------------------------ + // NET_GO: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; + Session.HostAddress = Session.GAddress; + } + } + + //------------------------------------------------------------------------ + // NET_LOADGAME: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_LOADGAME) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START_LOAD; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_ANNOUNCE: Someone is ready to chat; add them to our list, if + // they aren't already on it, and it's not myself. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_ANNOUNCE) { + found = 0; + //..................................................................... + // If this packet is from myself, don't add it to the list + //..................................................................... + if (Session.GPacket.Chat.ID == Session.UniqueID) { + found = 1; + } + //..................................................................... + // Otherwise, see if we already have this address stored in our list + // If so, update that node's time values & name (in case the user + // changed it), and return. + //..................................................................... + else { + for (i = 0; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + strcpy (Session.Chat[i]->Name, Session.GPacket.Name); + Session.Chat[i]->Chat.LastTime = TickCount; + Session.Chat[i]->Chat.LastChance = 0; + Session.Chat[i]->Chat.Color = Session.GPacket.Chat.Color; + found = 1; + break; + } + } + } + //..................................................................... + // Add a new node to the list + //..................................................................... + if (!found) { + who = new NodeNameType; + strcpy (who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Chat.LastTime = TickCount; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_REQUEST: Someone is requesting a CHAT_ANNOUNCE from us; send + // one to him directly. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_REQUEST) { + if ((*joinstate) != JOIN_WAIT_CONFIRM && (*joinstate) != JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy(Session.GPacket.Name, my_name); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &Session.GAddress); + + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + //..................................................................... + // If we're in a game, the sender must be in our game. + //..................................................................... + if ( (*joinstate)==JOIN_CONFIRMED) { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + } + //..................................................................... + // Otherwise, we're in the chat room; display any old message. + //..................................................................... + else { + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + + retcode = EV_MESSAGE; + } + + //------------------------------------------------------------------------ + // NET_PING: Someone is pinging me to get a response time measure (will only + // happen after I've joined a game). Do nothing; the IPX Manager will handle + // sending an ACK, and updating the response time measurements. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + //------------------------------------------------------------------------ + // Default case: nothing happened. (This case will be hit every time I + // receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + //------------------------------------------------------------------------ + else { + retcode = EV_NONE; + } + + return(retcode); + +} /* end of Get_Join_Responses */ + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ + typedef enum { + NUM_MESSAGES = 10 + } NumMessagesType; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + +//BG int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_w = 124*RESFACTOR; + int d_playerlist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR; + + int d_scenariolist_w = 162*RESFACTOR; + int d_scenariolist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - (5*RESFACTOR); + int d_scenariolist_y = d_playerlist_y; + + int d_reject_w = 55*RESFACTOR; + int d_reject_h = 9*RESFACTOR; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_count_y = d_reject_y + d_reject_h /*KO+ d_margin2*/; + + int d_level_w = 25*RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25*RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106*RESFACTOR; + int d_options_h = ((5 * 6) + 4)*RESFACTOR; + int d_options_x = d_scenariolist_x + ((d_scenariolist_w - d_options_w) / 2); + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (NUM_MESSAGES * d_txt6_h) + 3*RESFACTOR; // 10 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message_h); +// int d_message_y = d_options_y + d_options_h + d_margin1; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9*RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 50*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + int d_cancel_w = 50*RESFACTOR; + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, 60*RESFACTOR); + + StaticButtonClass staticunit(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + if (loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE +// loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init dialog values, only the first time through + //------------------------------------------------------------------------ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = + (SessionClass::CountMax[Session.Options.Bases] + + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = 0; + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = 0; + + //------------------------------------------------------------------------ + // Init scenario description list box + //------------------------------------------------------------------------ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + + break; + } + } + if ( EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + { + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected +#ifdef FIXIT_VERSION_3 + bAftermathMultiplayer = Is_Aftermath_Installed(); +// debugprint( "Host decides that, initially, bAftermathMultiplayer is %i\n", bAftermathMultiplayer ); +#else + PlayingAgainstVersion = VerNum.Version_Number(); +#endif + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the message display system + //------------------------------------------------------------------------ + Session.Messages.Init(d_message_x + 1*RESFACTOR, d_message_y + 1*RESFACTOR, NUM_MESSAGES, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1*RESFACTOR, d_send_y + 1*RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + /* + ** Zap, Zap, Zap + */ + playerlist.Zap(); + scenariolist.Zap(); + rejectbtn.Zap(); + staticunit.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + countgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + levelgauge.Zap(); + optionlist.Zap(); + okbtn.Zap(); + cancelbtn.Zap(); + loadbtn.Zap(); + + + /* + ** Hack hack, hack + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) { +#else + if (loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { +#endif +#ifdef FIXIT_MULTI_SAVE + loadbtn.Add_Tail(*commands); +#endif + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2*RESFACTOR, d_count_y, d_count_x + d_count_w + 35*RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticunit.Set_Text(txt); + staticunit.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2*RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2*RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // New Scenario selected. + //.................................................................. +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + break; + +#else // FIXIT_VERSION_3 + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + }else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + } else { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + } + break; +#endif // FIXIT_VERSION_3 + + //.................................................................. + // Reject the currently-selected player (don't allow rejecting myself, + // who will be the first entry in the list) + //.................................................................. + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + + if (index == 0) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_CANT_REJECT_SELF), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + + } else if (index < 0 || index >= playerlist.Count()) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_SELECT_PLAYER_REJECT), + PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 1, + &(Session.Players[index]->Address)); + break; + + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case (BUTTON_OPTIONS | KN_BUTTON): + if (Special.IsCaptureTheFlag != optionlist.Is_Checked(3) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != optionlist.Is_Checked(0)) { + Session.Options.Bases = optionlist.Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + optionlist.Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsCaptureTheFlag = optionlist.Is_Checked(3); + Special.IsShadowGrow = optionlist.Is_Checked(4); + + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // OK: exit loop with TRUE status + //.................................................................. + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + while (TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Players.Count() > 1 ) { +// if (Session.Players.Count() + Session.Options.AIPlayers > 1 ) { + rc = TRUE; + process = FALSE; + } else { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_ONE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) { + load_game = 1; + } else { + load_game = 0; + } + break; + + //.................................................................. + // CANCEL: send a SIGN_OFF, bail out with error code + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // Broadcast my sign-off over a bridged network if there is one + //............................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Re-draw the messages & service keyboard input for any message + // being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Handle); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // Send the message to every player in our player list, except + // myself. + //............................................................ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + // If there's no message with this ID already displayed, just + // add a new message; if there is one, concatenate it. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + display = REDRAW_MESSAGE; + } + break; + } + + //..................................................................... + // Process incoming packets + //..................................................................... +#ifndef FIXIT_VERSION_3 + int oldversion = PlayingAgainstVersion; +#endif + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } +#ifdef FIXIT_VERSION_3 // All scenarios now allowable for download, regardless of if CS scen. or 126x126 scen. + if (display < REDRAW_PARMS) display = REDRAW_PARMS; +#else // FIXIT_VERSION_3 + if (oldversion == PlayingAgainstVersion){ + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + }else{ + + /* + ** If a CS scenario was selected and we now have a red alert only player + ** in the mix then deselect the cs scenario. + */ + #ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && + #else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && + #endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #ifdef FIXIT_CSII // checked - ajw + /* + ** If an AM mega scenario was selected and we now have a non-AM + ** player in the mix then deselect the mega scenario. + */ + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #endif + display = REDRAW_BACKGROUND; + } +#endif // FIXIT_VERSION_3 + + transmit = 1; + } else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else if (whahoppa == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + display = REDRAW_PARMS; + } + transmit = 1; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; +#ifdef FIXIT_VERSION_3 + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + // Host encodes whether or not this is an Aftermath game in the highest bit. + if( bAftermathMultiplayer ) + { +// debugprint( "Host tells guests 'This is an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version() | 0x80000000; + } + else + { +// debugprint( "Host tells guests 'This is NOT an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + } +#else + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); +#endif + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = max( (Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response."); +#endif + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*10; // Wait for 10 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); + +} /* end of Net_New_Dialog */ + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * color_used array of color-used flags * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_PLAYER_SIGNOFF = a player has left * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + unsigned long version; // version # to use + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // Try to handle the packet in a standard way + //------------------------------------------------------------------------ + if (Process_Global_Packet(&Session.GPacket,&Session.GAddress) != 0) { + return(EV_NONE); + } else if (Session.GPacket.Command==NET_QUERY_JOIN) { + //------------------------------------------------------------------------ + // NET_QUERY_JOIN: + //------------------------------------------------------------------------ + //..................................................................... + // See if this name is unique: + // - If the name matches, but the address is different, reject this player + // - If the name & address match, this packet must be a re-send of a + // previous request; in this case, do nothing. The other player must have + // received my CONFIRM_JOIN packet (since it was sent with an ACK + // required), so we can ignore this resend. + //..................................................................... + found = 0; + resend = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (!strcmp(Session.Players[i]->Name,Session.GPacket.Name)) { + if (Session.Players[i]->Address != Session.GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + //..................................................................... + // If his name is the same as mine, treat it like a duplicate name + //..................................................................... + if (!strcmp (Session.Players[0]->Name, Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // Reject if name is a duplicate + //..................................................................... + if (found) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_DUPLICATE_NAME; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // Reject if there are too many players + //..................................................................... + else if ( (Session.Players.Count() >= Session.MaxPlayers) && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_GAME_FULL; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + /* + ** Don't allow joining if the rules.ini file doesn't appear to match. + */ + else if (Session.GPacket.PlayerInfo.CheatCheck != RuleINI.Get_Unique_ID() && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_MISMATCH; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // If this packet is NOT a resend, accept the player. Grant him the + // requested color if possible. + //..................................................................... + else if (!resend) { + //.................................................................. + // Check the player's version range against our own, to see if + // there's an overlap region + //.................................................................. +#ifdef FIXIT_VERSION_3 + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + bool bGuestHasAftermath = Session.GPacket.PlayerInfo.MinVersion & 0x80000000; + if( bGuestHasAftermath ) +// debugprint( "Host hears guest say 'I have Aftermath'\n" ); + ; + else + { +// debugprint( "Host hears guest say 'I don't have Aftermath'\n" ); + if( bAftermathMultiplayer ) + { +// debugprint( "Host decides this is no longer an Aftermath game!\n" ); + bAftermathMultiplayer = false; + } + } + + Session.GPacket.PlayerInfo.MinVersion &= ~0x80000000; // Strip special bit. +#endif + version = VerNum.Clip_Version (Session.GPacket.PlayerInfo.MinVersion, + Session.GPacket.PlayerInfo.MaxVersion); + +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + +// TCTCTC save off version number + + //.................................................................. + // Reject player if his version is too old + //.................................................................. + if (version == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_OLD; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //.................................................................. + // Reject player if his version is too new + //.................................................................. + else if (version == 0xffffffff) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_NEW; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + //.................................................................. + // If the player is accepted, our mutually-accepted version may be + // different; set the CommProtocol accordingly. + //.................................................................. + else { + Session.CommProtocol = VerNum.Version_Protocol(version); + } + + //.................................................................. + // Add node to the Vector list + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players.Add (who); + + //.................................................................. + // Set player's color; if requested color isn't used, give it to him; + // otherwise, give him the 1st available color. Mark the color we + // give him as used. + //.................................................................. + if (color_used[Session.GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = Session.GPacket.PlayerInfo.Color; + } + else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (color_used[i]==0) { + who->Player.Color = (PlayerColorType)i; + break; + } + } + } + color_used[who->Player.Color] = 1; + + //.................................................................. + // Add player name to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, + Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // Send a confirmation packet + //.................................................................. + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CONFIRM_JOIN; + strcpy(Session.GPacket.Name,Session.Handle); + Session.GPacket.PlayerInfo.House = who->Player.House; + Session.GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + + //.................................................................. + // Play a special sound. + //.................................................................. + Sound_Effect(VOC_PLAYER_JOINED); + + retval = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // the player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (!strcmp (Session.Players[i]->Name, Session.GPacket.Name) && + Session.Players[i]->Address==Session.GAddress) { + + //............................................................... + // Remove from the list box + //............................................................... + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + + //............................................................... + // Mark his color as available + //............................................................... + color_used[Session.Players[i]->Player.Color] = 0; + + //............................................................... + // Delete from the Vector list + //............................................................... + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + //............................................................... + // Play a special sound. + //............................................................... + Sound_Effect(VOC_PLAYER_LEFT); + + retval = EV_PLAYER_SIGNOFF; + + break; + } + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + retval = EV_MESSAGE; + } + + return(retval); + +} /* end of Get_NewGame_Responses */ + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); + +} /* end of Compute_Name_CRC */ + + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int d_txt6_h = 6 * RESFACTOR+1; + int d_margin = 5 * RESFACTOR; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + fresh = true; + } +#endif + + //------------------------------------------------------------------------ + // Draw the dialog from scratch + //------------------------------------------------------------------------ + if (fresh) { + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + switch ( Session.Type) { + case GAME_IPX: + case GAME_INTERNET: + case GAME_MODEM: + case GAME_NULL_MODEM: + + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + break; + +#if(TEN) + case GAME_TEN: + if (reconn) { + id = Ten->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ten->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // TEN + +#if(MPATH) + case GAME_MPATH: + if (reconn) { + id = MPath->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + MPath->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // MPATH + } + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = max(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + +#ifdef FIXIT_VERSION_3 + char szNewCancelMessage[ 300 ]; + sprintf( szNewCancelMessage, "%s%s", buf3, TXT_WOL_CANCELMEANSFORFEIT ); + + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + { + w = max(String_Pixel_Width(szNewCancelMessage), w); // * RESFACTOR; why was it ever multiplied by this!!!? + w += (d_margin * 12); + } + else + { + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); + } +#else + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); +#endif + + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*RESFACTOR - (w / 2); + y = 100*RESFACTOR - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*RESFACTOR, y + (d_margin * 2), scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + Fancy_Text_Print (szNewCancelMessage, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + + Show_Mouse(); + } + //------------------------------------------------------------------------ + // Just update the timeout value on the dialog + //------------------------------------------------------------------------ + else { + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + + int fillx = 160*RESFACTOR - (String_Pixel_Width (buf2) / 2) -6; + LogicPage->Fill_Rect (fillx, y + (d_margin * 2) + d_txt6_h + d_margin, + fillx + String_Pixel_Width (buf2) + 12, + y + (d_margin * 2) + d_txt6_h + d_margin + d_txt6_h +1*RESFACTOR, + BLACK); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, BLACK, TPF_CENTER | TPF_TEXT); + + Show_Mouse(); + } +} /* end of Net_Reconnect_Dialog */ + +#define MAX_CHAT_NAME 12 +#define MAX_CHAT_PHRASE 45 + +struct WWPerson { + char Name[MAX_CHAT_NAME]; + char Phrase[MAX_CHAT_PHRASE]; + PlayerColorType Color; + unsigned long LastTime; +}; + +struct WWPerson WWPersons[] = { + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,105,115,110,39,116,32,116,104,105,115,32,99,111,111,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,119,97,110,116,32,115,111,109,101,32,115,101,97,102,111,111,100,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {71,114,101,97,116,46,32,74,117,115,116,32,103,114,101,97,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {87,111,110,100,101,114,102,117,108,46,32,80,101,114,102,101,99,116,44,32,105,110,32,102,97,99,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {89,111,117,32,99,97,108,108,32,116,104,105,115,32,65,73,63,32,32,83,104,101,101,115,104,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {66,105,108,108,115,32,114,117,108,101,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {84,97,107,101,32,116,104,105,115,32,111,117,116,44,32,110,111,119,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {87,104,121,32,99,97,110,39,116,32,121,111,117,32,102,105,116,32,97,110,121,32,109,111,114,101,32,108,101,116,116,101,114,115,32,111,110,32,116,104,101,32,108,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,73,32,119,97,110,116,32,116,111,32,116,97,108,107,32,116,111,32,121,111,117,32,97,98,111,117,116,32,116,104,105,115,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,99,111,109,101,32,116,111,32,109,121,32,111,102,102,105,99,101,44,32,110,111,119,33,32,32,91,99,114,97,99,107,33,93,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,115,116,97,112,108,101,114,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,116,104,101,115,97,117,114,117,115,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,112,101,110,99,105,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {72,101,121,32,83,116,101,118,101,44,32,99,97,110,32,119,101,32,109,101,101,116,32,105,110,32,121,111,117,114,32,111,102,102,105,99,101,63,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {83,116,101,118,101,44,32,105,116,39,108,108,32,111,110,108,121,32,116,97,107,101,32,97,32,109,105,110,117,116,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,111,98,32,83,0,0,0,0,0,0,0,}, + {77,105,108,111,44,32,100,105,97,108,32,50,52,57,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,119,105,116,104,32,97,110,32,69,45,76,45,83,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,115,98,97,114,102,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,102,98,117,116,116,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,115,111,111,111,32,119,101,105,114,100,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {68,117,104,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {72,101,121,44,32,105,116,32,119,111,114,107,101,100,32,111,110,32,109,121,32,99,111,109,112,117,116,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,32,105,115,32,99,108,101,97,114,108,121,32,97,32,119,97,115,116,101,32,111,102,32,109,101,109,111,114,121,46,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {72,109,109,44,32,73,32,115,101,101,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {65,104,104,44,32,121,101,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {78,111,116,32,97,110,111,116,104,101,114,32,105,110,115,116,97,108,108,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {72,65,32,72,65,32,72,65,32,72,65,32,72,65,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,114,101,97,108,108,121,32,99,111,111,108,33,32,32,71,111,115,104,32,103,117,121,115,33,32,32,87,111,119,33,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {87,111,119,33,32,32,89,111,117,32,103,117,121,115,32,97,114,101,32,116,104,101,32,103,114,101,97,116,101,115,116,33,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {71,114,97,121,102,105,115,104,32,102,111,114,32,108,117,110,99,104,32,97,103,97,105,110,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,108,97,109,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,39,115,32,98,117,103,103,101,100,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {83,104,105,112,32,105,116,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,87,0,0,0,0,0,}, + {79,75,32,101,118,101,114,121,111,110,101,44,32,111,117,116,32,111,102,32,109,121,32,111,102,102,105,99,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,99,104,97,116,32,112,114,111,103,114,97,109,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,110,105,110,106,97,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {73,32,119,101,97,114,32,100,101,115,105,103,110,101,114,32,106,101,97,110,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {72,101,121,32,66,105,108,108,44,32,116,104,105,115,32,116,104,105,110,103,32,107,101,101,112,115,32,99,114,97,25,26,24,104,105,110,103,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,98,101,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,119,111,114,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,78,0,0,0,0,0,0,}, + {83,111,117,110,100,115,32,108,105,107,101,32,97,32,100,114,105,118,101,114,32,112,114,111,98,108,101,109,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,99,111,116,116,32,66,0,0,0,0,0,}, + {73,32,110,101,101,100,32,116,104,105,115,32,102,111,114,32,76,79,76,50,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,84,0,0,0,0,0,}, + {84,104,105,115,32,115,101,101,109,115,32,114,97,116,104,101,114,32,115,105,108,108,121,44,32,97,99,116,117,97,108,108,121,46,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,117,108,32,77,0,0,0,0,0,0,}, + {78,111,32,46,46,46,46,32,32,87,32,65,32,89,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,80,0,0,0,0,0,0,}, + {65,72,32,72,65,32,72,65,32,72,65,33,33,32,32,72,65,72,65,33,33,32,32,65,72,72,32,72,65,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {83,116,97,108,105,110,32,107,105,108,108,101,100,32,109,121,32,102,97,116,104,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {73,32,104,111,112,101,32,121,111,117,39,114,101,32,112,114,111,117,100,32,111,102,32,121,111,117,114,115,101,108,102,44,32,66,105,108,108,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,103,32,87,105,108,108,121,0,0,0,}, + {65,110,121,98,111,100,121,32,102,101,114,32,97,32,115,112,105,116,116,105,110,39,32,99,111,110,116,101,115,116,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,99,32,87,0,0,0,0,0,0,}, + {75,97,114,97,116,101,32,105,115,32,103,111,111,100,32,98,117,116,32,121,111,117,32,117,115,101,32,105,116,32,102,111,114,32,101,118,105,108,33,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {84,101,100,32,77,0,0,0,0,0,0,0,}, + {72,97,46,32,32,70,117,110,110,121,46,32,32,84,104,105,115,32,105,115,32,102,117,110,110,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,97,109,111,110,32,82,0,0,0,0,0,}, + {73,32,98,108,97,109,101,32,116,104,101,32,70,114,101,110,99,104,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,119,105,103,104,116,32,79,0,0,0,0,}, + {79,104,44,32,109,97,110,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {73,111,110,32,99,97,110,110,111,110,32,114,101,97,100,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {83,101,108,101,99,116,32,116,97,114,103,101,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,117,99,101,32,74,0,0,0,0,0,}, + {73,32,97,109,32,116,104,101,32,71,101,110,105,101,32,111,102,32,116,104,101,32,108,97,109,112,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {76,97,117,114,97,32,87,0,0,0,0,0,}, + {71,111,32,97,119,97,121,44,32,66,105,108,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,97,118,105,100,32,68,0,0,0,0,0,}, + {72,109,109,109,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,105,0,0,0,0,0,}, + {67,97,108,108,32,109,101,32,78,97,116,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,101,110,122,105,108,32,76,0,0,0,0,}, + {84,104,105,115,32,119,111,117,108,100,32,98,101,32,98,101,116,116,101,114,32,111,110,32,116,104,101,32,77,97,99,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,114,101,103,32,72,0,0,0,0,0,0,}, + {66,117,116,32,100,111,101,115,32,105,116,32,102,105,116,32,105,110,116,111,32,50,32,77,66,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,110,97,116,104,97,110,32,76,0,0,}, + {73,32,116,104,105,110,107,32,73,32,110,101,101,100,32,97,32,104,97,105,114,99,117,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {65,100,97,109,32,73,0,0,0,0,0,0,}, + {87,104,97,116,101,118,101,114,46,32,32,66,101,101,102,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,117,114,116,32,79,0,0,0,0,0,0,}, + {70,105,120,32,105,116,32,121,111,117,114,115,101,108,102,46,32,32,73,39,109,32,98,117,115,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,115,101,112,104,32,72,0,0,0,0,}, + {60,73,99,121,32,103,108,97,114,101,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,116,116,32,72,0,0,0,0,0,0,}, + {73,39,109,32,110,101,118,101,114,32,103,111,111,100,32,119,105,116,104,32,99,108,101,118,101,114,32,111,110,32,116,104,101,32,115,112,111,116,46,46,46,0,0,0,0,},PCOLOR_GREEN,0}, + { {67,104,114,105,115,32,82,0,0,0,0,0,}, + {73,32,108,111,118,101,32,109,121,32,106,111,98,46,32,32,89,101,97,104,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,116,32,67,0,0,0,0,0,0,0,}, + {73,39,109,32,97,32,112,114,111,102,101,115,115,105,111,110,97,108,32,121,111,100,101,108,108,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {73,97,110,32,76,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,97,99,107,32,77,0,0,0,0,0,0,}, + {73,32,108,105,118,101,32,97,98,111,117,116,32,97,32,98,108,111,99,107,32,102,114,111,109,32,116,104,101,32,112,114,111,112,111,115,101,100,32,115,105,116,101,46,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,66,0,0,0,0,0,0,}, + {84,104,101,121,32,97,108,108,32,108,111,111,107,32,108,105,107,101,32,97,110,116,115,32,102,114,111,109,32,117,112,32,104,101,114,101,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, +}; + +CDTimerClass wwperson_timer; + +void Start_WWChat(ColorListClass *playerlist) +{ + char *item; + int i; + HousesType house; + + //------------------------------------------------------------------------ + // Ensure a different sequence each time + //------------------------------------------------------------------------ + Scen.RandomNumber = rand(); + + //------------------------------------------------------------------------ + // Add myself to the player list + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + //------------------------------------------------------------------------ + // Add everyone else to the list + //------------------------------------------------------------------------ + for (i = 0; i < sizeof(WWPersons) / sizeof(struct WWPerson); i++) { + //..................................................................... + // Add the 1st entry to the list no matter what; for entries after the + // 1st, only add the name if it's different from the previous name. + //..................................................................... + if (i==0 || strcmp(WWPersons[i].Name, WWPersons[i-1].Name)) { + WWPersons[i].Color = (PlayerColorType)(Random_Pick(0,(int)(PCOLOR_LAST - 1))); + if (Percent_Chance(50)) { + house = HOUSE_GREECE; + } else { + house = HOUSE_USSR; + } +// house = (HousesType)Random_Pick((int)HOUSE_GOOD,(int)HOUSE_BAD); + item = new char [MPLAYER_NAME_MAX + 64]; + if (house != HOUSE_USSR && house != HOUSE_UKRAINE) { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_SOVIET)); + } + playerlist->Add_Item(item, + (WWPersons[i].Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[WWPersons[i].Color]); + } + //..................................................................... + // If this entry's name is the same as the previous, copy the color + // value from the previous entry. + //..................................................................... + else if (i > 0) { + WWPersons[i].Color = WWPersons[i-1].Color; + } + + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + +} // end of Start_WWChat + + +int Update_WWChat(void) +{ + int i; + int j; + + //------------------------------------------------------------------------ + // Do nothing if it's too soon. + //------------------------------------------------------------------------ + if (wwperson_timer > 0) { + return(0); + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + + //------------------------------------------------------------------------ + // Even after the timer's gone off, there's only a 1/8 chance we'll + // print a message. + //------------------------------------------------------------------------ + if (Percent_Chance(12)) { + return(0); + } + + //------------------------------------------------------------------------ + // Randomly select a message to "send"; ensure we pick a message that + // hasn't been recently displayed. + //------------------------------------------------------------------------ + j = sizeof(WWPersons) / sizeof(struct WWPerson); + i = Random_Pick (0, j - 1); + if ((TickCount - WWPersons[i].LastTime) < 1800 && + WWPersons[i].LastTime != 0) { + return(0); + } + + Session.Messages.Add_Message (WWPersons[i].Name, + 0, + WWPersons[i].Phrase, WWPersons[i].Color, + TPF_TEXT, -1); + WWPersons[i].LastTime = TickCount; + + return (1); + +} // end of Update_WWChat + + +#if(0) +/*****************************************************************************/ +void Start_Logging(void) +{ + FILE *fp; + static char *ColorNames[6] = { + "Yellow", + "Red", + "BlueGreen", + "Orange", + "Green", + "Blue", + }; + int i; + int id; + HousesType house; + char *housenames[] = { + "ALLIES", + "SOVIET", + "Neutral", + "Special", + "Multi1", + "Multi2", + "Multi3", + "Multi4", + }; + time_t t; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + /* + ** Print game header + */ + t = time(NULL); + fprintf (fp,"==============================================================\n"); + fprintf (fp,"Date: %s",ctime(&t)); + fprintf (fp,"Scenario: %s\n", + Session.Scenarios[Session.Options.ScenarioIndex].Description()); + fprintf (fp,"Total # players: %d\n",Session.NumPlayers); + fprintf (fp,"==============================================================\n"); + + /* + ** Print connection info + */ + + /* + ** Print player data + */ + fprintf(fp,"Local Player:\n"); + fprintf (fp," Name:%s\n", Session.Handle); + fprintf (fp," Color:%s\n", ColorNames[Session.ColorIdx]); + fprintf (fp," House:%s\n", housenames[Session.House]); + fprintf (fp,"\n"); + + for (i = 0; i < Ipx.Num_Connections(); i++) { + id = Ipx.Connection_ID(i); + house = (HouseClass)id; + fprintf(fp,"Connection %d:\n",i); + fprintf (fp," Name:%s\n", Ipx.Connection_Name(id)); + fprintf (fp," Color:%s\n", + ColorNames[HouseClass::As_Pointer(house)->RemapColor); + fprintf (fp," House:%s\n", housenames[house]); + fprintf (fp,"\n"); + } + + /* + ** Print game options + */ + fprintf(fp,"Game Options:\n"); + fprintf(fp," Bases:%d\n",Session.Options.Bases); + fprintf(fp," Credits:%d\n",Session.Options.Credits); + fprintf(fp," Tiberium:%d\n",Session.Options.Tiberium); + fprintf(fp," Crates:%d\n",Session.Options.Goodies); + fprintf(fp," AI Players:%d\n",Session.Options.AIPlayers); + fprintf(fp," Build Level:%d\n",BuildLevel); + fprintf(fp," Unit Count:%d\n",Session.Options.UnitCount); + fprintf(fp," Seed:%d\n",Seed); + fprintf (fp,"\n"); + + fclose(fp); + +} /* end of Start_Logging */ + + +void Log_Message(char *msg) +{ + FILE *fp; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + fprintf(fp,"%s (Frame:%d)\n",msg,Frame); + fclose(fp); + flushall(); + +} /* end of Log_Message */ +#endif + + + + + +#ifndef WOLAPI_INTEGRATION // Rest of file ifdeffed out. + + + + + +extern bool Spawn_WChat(bool can_launch); + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³.³ ³ Green Acres ³.³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Brown Sewers ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + + int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3)*RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i; + char *item; + int tabs[] = {77}; // tabs for player list box + int optiontabs[] = {8}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + JoinEventType whahoppa; // event generated by received packets + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int width; + int height; + + bool player_joined = false; + CountDownTimerClass join_timer; + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Rule.IsTGrowth = Special.IsTGrowth; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Special.IsTSpread; + transmit = 0; + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ +// randomize(); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Players.Count() > 1) { + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined) { + player_joined = true; + join_timer.Set (3*60, true); + break; + } else { + if (join_timer.Time()) break; + } + + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = MAX(Ipx.Global_Response_Time() * 2, 60*2); + while (TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Solo || Session.Players.Count() > 1 || Session.Options.Ghosts) { + rc = TRUE; + process = FALSE; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + Sound_Effect(VOC_SYS_ERROR); + } + //if (input==(BUTTON_LOAD | KN_BUTTON)) + // load_game = 1; + break; + } + break; + + } + + //..................................................................... + // Process incoming packets + //..................................................................... + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + transmit = 1; + } + else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + // ajw - I don't understand why this check is done here and not later. + if ( !file.Is_Available() ) { + + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed and + ** the file name matches a counterstrike map then force the CD + ** to be there. + ** + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ( Expansion_CS_Present() || Expansion_AM_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName) && index!=2 && index!=3) { + needcd = true; + RequiredCD = 2; + } + if (Is_Mission_Aftermath(Session.ScenarioFileName) && index!=3) { + needcd = true; + RequiredCD = 3; + } + if (needcd) { +#else + if ( Expansion_CS_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + int scen_num; + sscanf ( Session.ScenarioFileName, "SCM%02d", &scen_num ); + + int current_drive = CCFileClass::Get_CD_Drive(); + if ( scen_num>24 && Get_CD_Index(current_drive, 1*60) != 2){ + RequiredCD = 2; +#endif + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + /* + ** See if that file is available now. Its fatal if it isnt. + */ + if ( !file.Is_Available() ) { + + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Clear the Players vector if we're not starting a game. + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + return (false); + } + } + + + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + //Session.GPacket.ScenarioInfo.IsGhosties = Session.Options.Ghosts; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = MAX( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = MAX( (Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); + + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*30; // Wait for 30 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + + WWDebugString ("RA95 - Exited response wait loop.\n"); + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 +} /* end of Net_Fake_New_Dialog */ + + + + + + + + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³.³ ³ Peter Parker GDI ³.³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_Join_Dialog(void) +{ + +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_gamelist_w = 160 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high +// int d_gamelist_x = d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_x = 500*RESFACTOR; //d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_y = 50 + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = 50+ d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_GAMELIST = 100, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {77 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + int parms_received = 0; // 1 = game options received + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + int ready_to_go = 0; + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + int width; + int height; + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, 0, 0, d_txt6_h, 0, + 0, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + else { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + process = false; + rc = -1; + } + break; + + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + default: + if (joinstate == JOIN_NOTHING && gamelist.Count() >1 ) { + gamelist.Set_Selected_Index(1); //lastclick_idx); + game_index = 1; + strcpy (Session.Handle,namebuf); + //Session.House = (HousesType)housebtn.Current_Index(); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) load_game = 1; + + bool ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + CCFileClass testfile ( Session.ScenarioFileName ); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** See if that scenario is available now. Its pretty fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + } + } + } + + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided its not an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + + rc = 0; + process = false; + } + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + else if (joinstate==JOIN_CONFIRMED) { + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 12]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); +#if (0) + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (0,0, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, 0, 0, 1, + 20, MAX_MESSAGE_LENGTH - 5); + Session.Messages.Add_Edit(Session.ColorIdx, TPF_TEXT | TPF_BRIGHT_COLOR, NULL, '_'); +#endif //(0) + } + + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + else if (joinstate==JOIN_REJECTED) { + Sound_Effect(VOC_SYS_ERROR); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + + } + } // end of state change + + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + else if (event == EV_GAME_OPTIONS) { + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } + + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + else if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + } + +#if (0) + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + if (ready_to_go && Session.Players.Count() == 2) { + rc = 0; + process = false; + } +#endif //(0) + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + else { + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); +///* +//** Hack to fake a scenario name as if it had been sent over the connection. +//*/ +//sprintf(Scen.ScenarioName, "SCM%02dEA.INI", Session.Options.ScenarioIndex+1); + +// Scen.Scenario = Session.Options.ScenarioIndex; + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = MAX(Ipx.Global_Response_Time() * 2, 60*2); + starttime = TickCount; + while (TickCount - starttime < i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ + //Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + // Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 + +} /* end of Net_Join_Dialog */ + + + + + + + + + + +bool Server_Remote_Connect(void) +{ + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + if (!Net_Fake_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + return (false); + } + + Session.NetOpen = 0; + Session.NetStealth = stealth; + Session.Write_MultiPlayer_Settings (); + return (true); + +} /* end of Server_Remote_Connect */ + + + + + + + +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + rc = Net_Fake_Join_Dialog(); + Session.Write_MultiPlayer_Settings (); + + Session.NetStealth = stealth; + Session.NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } + +} /* end of Client_Remote_Connect */ + + + +/*************************** end of netdlg.cpp *****************************/ + +#endif //#ifndef WOLAPI_INTEGRATION diff --git a/CODE/NOSEQCON.CPP b/CODE/NOSEQCON.CPP new file mode 100644 index 0000000..7b12529 --- /dev/null +++ b/CODE/NOSEQCON.CPP @@ -0,0 +1,690 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.cpv 1.10 01 Mar 1996 18:08:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * NonSequencedConnClass::Receive_Packet -- adds packet to receive queue * + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::NonSequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen); +} + + +/*************************************************************************** + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::~NonSequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NonSequencedConnClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); +} + + +/*************************************************************************** + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { +// Smart_Printf( "Packet ack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendAck++; + } else { +// Smart_Printf( "Packet noack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendNoAck++; + } + return(true); + } else { +// Smart_Printf( "Packet not Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + return(false); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { +// Smart_Printf( "Bad Magic Number\n" ); + return(false); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Get_Send(i); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Received ACK for %d \n", packet->PacketID ); + send_entry->IsACK = 1; + break; + } + } + } + +//{ +// if (i == Queue->Num_Send() ) { +// Smart_Printf( "Received bad ACK for %d \n", packet->PacketID ); +// } +//} + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*--------------------------------------------------------------------- + If there's only one slot left, don't tie up the queue with this packet + ---------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { +// Smart_Printf( "Only one slot left don't tie up with DATA NOACK packet %d \n", packet->PacketID ); + return(false); + } + + /*--------------------------------------------------------------------- + Error if we can't queue the packet + ---------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "Can't Queue the packet %d \n", packet->PacketID ); + return(false); + } + +// Smart_Printf( "Queued DATA NOACK for %d \n", packet->PacketID ); + NumRecNoAck++; + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Looking at ID %d, LastSeqID=%d \n", packet->PacketID, LastSeqID ); + /*.................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + ....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { +// Smart_Printf( "Older than oldest\n" ); + save_packet = 0; + } + /*.................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + ....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { +// Smart_Printf( "It's a resend\n" ); + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*--------------------------------------------------------------------- + Queue the packet & update our LastSeqID value. + ---------------------------------------------------------------------*/ + if (save_packet) { + /*------------------------------------------------------------------ + If there's only one slot left, make sure we only put a packet in it if + this packet will let us increment our LastSeqID; otherwise, we'll get + stuck, forever unable to increment LastSeqID. + ------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { +// Smart_Printf( "One slot left not what we looking for max=%d,num=%d \n", +// Queue->Max_Receive(), Queue->Num_Receive() ); + return(0); + } + } + + /*------------------------------------------------------------------ + If we can't queue the packet, return; don't send an ACK. + ------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "unable to queue packet\n" ); + return(0); + } + + NumRecAck++; + + /*------------------------------------------------------------------ + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ------------------------------------------------------------------*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................ + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*--------------------------------------------------------------------- + Send an ACK, regardless of whether this was a resend or not. + ---------------------------------------------------------------------*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; +// Smart_Printf( "Sending ACK for %d \n", packet->PacketID ); + Send ((char *)&ackpacket, sizeof(CommHeaderType)); + + return(true); + + } else { +// Smart_Printf( "invalid packet type %d \n", packet->Code ); + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + } + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ------------------------- Get this queue entry ------------------------ + */ + send_entry = Queue->Get_Send(i); + /* + ---------------- If ACK has been received, unqueue it ----------------- + */ + if (send_entry->IsACK) { + /* + ................ Update this queue's response time ................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + /* + ....................... unqueue the packet ......................... + */ + Queue->UnQueue_Send(NULL,NULL,i); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ +#if(0) +{ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (send_entry->SendCount) { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Resending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Resending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } else { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Sending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Sending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } +} +#endif + Send (send_entry->Buffer, send_entry->BufLen); + + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { +// Smart_Printf( "Max Retries!!! %d !!! \n", MaxRetries ); + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { +// Smart_Printf( "Timed out!!! Time %d, Timeout %d, buflen %d !!! \n", +// (send_entry->LastTime - send_entry->FirstTime), Timeout, +// send_entry->BufLen ); + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { +// Smart_Printf( "Connection going bad!!! \n" ); + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } else { + if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } + } + } + } + + return(true); +} + + diff --git a/CODE/NOSEQCON.H b/CODE/NOSEQCON.H new file mode 100644 index 0000000..d54d0da --- /dev/null +++ b/CODE/NOSEQCON.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.h_v 1.13 01 Mar 1996 17:32:42 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : NOSEQCON.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Non-Sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * however, the performance is better than the Sequenced approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NONSEQCONN_H +#define NONSEQCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "combuf.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class NonSequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NonSequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~NonSequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; +}; + +#endif + + diff --git a/CODE/NULLCONN.CPP b/CODE/NULLCONN.CPP new file mode 100644 index 0000000..b484fdb --- /dev/null +++ b/CODE/NULLCONN.CPP @@ -0,0 +1,285 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/NULLCONN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : NULLCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : April 20, 1995 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemConnClass::NullModemConnClass -- class constructor * + * NullModemConnClass::~NullModemConnClass -- class destructor * + * NullModemConnClass::Init -- hardware-dependent initialization * + * NullModemConnClass::Send -- hardware-dependent packet sending * + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#endif //WIN32 +#include +#include +#include "nullconn.h" + + +/*************************************************************************** + * NullModemConnClass::NullModemConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # send queue entries * + * numreceive desired # send receive entries * + * maxlen max length of application's packets * + * magicnum application-defined magic # for the packets * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::NullModemConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 60, // Retry Delta Time + -1, // Max Retries (-1 means ignore this timeout parameter) + 1200) // Timeout: 20 seconds +{ + /*------------------------------------------------------------------------ + Pre-set the port value to NULL, so Send won't send until we've been Init'd + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + /*------------------------------------------------------------------------ + Allocate the Send Buffer; the parent constructor has set MaxPacketLen, + so we can use it in our computation. + ------------------------------------------------------------------------*/ + SendBuf = new char [ Actual_Max_Packet() ]; + +} /* end of NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::~NullModemConnClass -- class destructor * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::~NullModemConnClass () +{ + delete [] SendBuf; + +} /* end of ~NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::Init -- hardware-dependent initialization * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +#ifdef WIN32 +void NullModemConnClass::Init (HANDLE port_handle) +{ + ConnectionClass::Init(); + PortHandle = port_handle; +} +#else //WIN32 +void NullModemConnClass::Init (PORT *port) +{ + ConnectionClass::Init(); + Port = port; +} /* end of Init */ +#endif //WIN32 + +/*************************************************************************** + * NullModemConnClass::Send -- hardware-dependent packet sending * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 1 = OK, 0 = error * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Send (char *buf, int buflen, void *, int ) +{ + int *ibuf; + SerialHeaderType *header; + unsigned long sendlen; + + /*------------------------------------------------------------------------ + Error if we haven't been properly initialized + ------------------------------------------------------------------------*/ +#ifdef WIN32 + if ( PortHandle == NULL ) return(false); + +#else //WIN32 + int status; + if ( Port == NULL ) { + return(0); + } +#endif //WIN32 + + /*------------------------------------------------------------------------ + Package the data into the Send Buffer + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *) SendBuf; + header->MagicNumber = PACKET_SERIAL_START; + header->Length = (short) buflen; + header->MagicNumber2 = PACKET_SERIAL_VERIFY; + + sendlen = sizeof( SerialHeaderType ); + memcpy (SendBuf + sendlen, buf, buflen); + sendlen += buflen; + ibuf = (int *)(SendBuf + sendlen); + *ibuf = Compute_CRC( buf, buflen ); + sendlen += sizeof( int ); + + *(SendBuf + sendlen) = '\r'; + sendlen += 1; + + /*------------------------------------------------------------------------ + Send the data + ------------------------------------------------------------------------*/ +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); + return (true); + +#else //WIN32 + status = WriteBuffer( Port, SendBuf, sendlen ); + if ( status == ASSUCCESS ) { + return(1); + } + else { + return(0); + } +#endif //WIN32 + +} /* end of Send */ + + +/*************************************************************************** + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * * + * INPUT: * + * buf buffer to compute CRC for * + * buflen length of buffer in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Compute_CRC (char *buf, int buflen) +{ + unsigned int sum, hibit; + + sum = 0; + for (int i = 0; i < buflen; i++) { + if ( sum & 0x80000000 ) { // check hi bit to rotate into low bit + hibit = 1; + } + else { + hibit = 0; + } + + sum <<= 1; + sum += (hibit + (unsigned char)buf[i]); + } + + return((int)sum); + +} /* end of Compute_CRC */ + + +/*************************************************************************** + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * number of bytes used for communications only. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/20/1995 DRD : Created. * + *=========================================================================*/ +int NullModemConnClass::Packet_Overhead_Size ( void ) +{ + /*------------------------------------------------------------------------ + short for Null Modem Magic Number + short for Null Modem length of packet + int for Null Modem CRC check + CommHeaderType for Queued packets + ------------------------------------------------------------------------*/ + return( (PACKET_SERIAL_OVERHEAD_SIZE + sizeof(CommHeaderType)) ); + +} /* end of Packet_Overhead_Size */ + +/************************** end of nullconn.cpp ****************************/ diff --git a/CODE/NULLCONN.H b/CODE/NULLCONN.H new file mode 100644 index 0000000..9ca1b00 --- /dev/null +++ b/CODE/NULLCONN.H @@ -0,0 +1,150 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/NULLCONN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : NULLCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for a NULL-Modem connection. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the non-sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * ConnectionClass. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLCONN_H +#define NULLCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "commlib.h" + +/* +********************************** Defines ********************************** +*/ +#define PACKET_SERIAL_START 0xDABD +#define PACKET_SERIAL_VERIFY 0xDEAF + +#define PACKET_SERIAL_OVERHEAD_SIZE (sizeof( SerialHeaderType ) + sizeof( SerialCRCType )) + +typedef struct { + unsigned short MagicNumber; + unsigned short Length; + unsigned short MagicNumber2; +} SerialHeaderType; + +typedef struct { + int SerialCRC; +} SerialCRCType; + + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NullModemConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum); + virtual ~NullModemConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ +#ifdef WIN32 + void Init (HANDLE port_handle); +#else //WIN32 + void Init (PORT *port); +#endif //WIN32 + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned long Actual_Max_Packet (void) { return (MaxPacketLen + + (sizeof(SerialHeaderType)) + sizeof(int) + sizeof (char)); } + + /*..................................................................... + This routine computes a CRC value for the given buffer. + .....................................................................*/ + static int Compute_CRC(char *buf, int buflen); + + /*..................................................................... + This routine returns the number of bytes extra added the packet + for communication. + .....................................................................*/ + static int Packet_Overhead_Size( void ); + + /* + --------------------------- Private Interface ---------------------------- + */ + protected: + /*..................................................................... + This routine actually performs a hardware-dependent data send. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); +#ifdef WIN32 + /* + ** This is the winsoze port handle + */ + HANDLE PortHandle; +#else //WIN32 + /*..................................................................... + This is the PORT value used by the GreenLeaf calls. + .....................................................................*/ + PORT *Port; +#endif //WIN32 + + /*..................................................................... + This buffer is a staging area for data sent out; it includes the + packet sent by the parent class (which includes the application's + packet, plus the CommHeaderType header), plus: + - 2-byte buffer start ID + - 2-byte length + - 4-byte CRC value (at the end of the buffer) + This is the actual packet that gets sent across the serial line. + .....................................................................*/ + char *SendBuf; +}; + +#endif + +/************************** end of nullconn.h ******************************/ + diff --git a/CODE/NULLDLG.CPP b/CODE/NULLDLG.CPP new file mode 100644 index 0000000..5cf06aa --- /dev/null +++ b/CODE/NULLDLG.CPP @@ -0,0 +1,7827 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/NULLDLG.CPP 14 3/17/97 1:05a Steve_tall $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : Jan. 21, 1997 [V.Grippi] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Destroy_Null_Connection -- destroys the given connection * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * Init_Null_Modem -- Initializes Null Modem communications * + * Init_String_Compare -- for qsort * + * Phone_Compare -- for qsort * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Reconnect_Null_Modem -- allows user to reconnect * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef FIXIT_RANDOM_GAME +#include "time.h" +#endif +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +ModemRegistryEntryClass *ModemRegistry = NULL; //Ptr to modem registry data +#endif //WIN32 + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath( char* file_name ); +#endif + +//#include "WolDebug.h" + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +//extern char const *ForMisStr[]; +extern char const *EngMisStr[]; + + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +PlayerColorType TheirColor; +HousesType TheirHouse; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#define PCOLOR_BROWN PCOLOR_GREY + + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 0, 8, 1, + settings->HardwareFlowControl ) ) { + return (true); + } else { + return (false); + } + +#else //WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 'N', 8, 1, + settings->HardwareFlowControl) ) { + NullModem.Change_IRQ_Priority( settings->IRQ ); + + return(true); + } else { + return(false); + } +#endif //WIN32 +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!Session.Play) { + if (Session.Type == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + NullModem.Change_IRQ_Priority( 0 ); // reset priority of interrupts + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!Session.Play) { + /* + ** Send a sign-off packet + */ + memset (&event, 0, sizeof(EventClass)); + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount; + while ( (TickCount - starttime) < 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + Load_Title_Page(true); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20 * RESFACTOR, y + 25 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + +#ifdef WIN32 + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()) {} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); +#endif //WIN32 + + + /* + ** Check for a packet. If we detect one, the other system has already been + ** started. Wait 1/2 sec for him to receive my ACK, then exit with success. + ** Note: The initial time must be a little longer than the resend delay. + ** Just in case we just missed the packet. + */ + starttime = TickCount; + while ( TickCount - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /* + ** Send a packet across. As long as Num_Send() is non-zero, the other system + ** hasn't received it yet. + */ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.ScenarioInfo.Seed = TickCount; + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while (TickCount - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + break; + } + } + } + } + + starttime = TickCount; + + /* + ** Main Processing Loop + */ + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = 0; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (Session.ModemType) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + Show_Mouse(); + + /* + ** Main Processing Loop + */ + starttime = lastmsgtime = TickCount; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + + /* + ** Resend our message if it's time + */ + if (TickCount - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount; + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /* + ** Check for an incoming message + */ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + if (ReceivePacket.Command == SERIAL_CONNECT) { + + // are we getting our own packets back?? + + if (ReceivePacket.ID == Session.ColorIdx) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /* + ** OK, we got our message; now we have to make certain the other + ** guy gets his, so send him one with an ACK required. + */ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player being * + * "destroyed". * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + + if ( Session.NumPlayers == 1 ) { + return; + } + + /* + ** Do nothing if the house isn't human. + */ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /* + ** Create a message to display to the user + */ + txt[0] = '\0'; + switch (error) { + case 1: + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + break; + + case 0: + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + break; + + case -1: + NullModem.Delete_Connection(); + break; + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + /* + ** Remove this player from the Players vector + */ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /* + ** If we're the last player left, tell the user. + */ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 160 * RESFACTOR; // dialog width + int d_dialog_h = 94 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = 80*RESFACTOR; +// int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_dial_w = 90 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 * RESFACTOR; + int d_answer_h = 9 * RESFACTOR; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 * RESFACTOR; + int d_nullmodem_h = 9 * RESFACTOR; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 * RESFACTOR; + int d_settings_h = 9 * RESFACTOR; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /* + ** Button Enumerations + */ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*RESFACTOR}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, TPF_BUTTON, d_answer_x, d_answer_y, d_answer_w, d_answer_h); + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, TPF_BUTTON, d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, TPF_BUTTON, d_settings_x, d_settings_y, d_settings_w, d_settings_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + if (Session.SerialDefaults.Port == 0 || + Session.SerialDefaults.IRQ == -1 || + Session.SerialDefaults.Baud == -1) { + selectsettings = true; + + } else if ( NullModem.Detect_Port( &Session.SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + + /* + ** Create the list + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Main Processing Loop + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ** Refresh the backdrop + */ + Load_Title_Page(true); + /* + ** Draw the background + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + /* + ** Draw the labels + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + commands->Draw_All(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Remote-connect + */ + } else if ( Phone_Dialog() ) { + if (Session.PhoneBook[Session.CurPhoneIdx]->Settings.Port == 0) { + settings = &Session.SerialDefaults; + } else { + settings = &(Session.PhoneBook[Session.CurPhoneIdx]->Settings); + } +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + Session.CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, Session.PhoneBook[ Session.CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + Session.ModemType = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &Session.SerialDefaults; +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + Session.ModemType = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Otherwise, remote-connect; save values if we're recording + */ + } else if ( Init_Null_Modem( &Session.SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + Session.ModemType = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + Session.ModemType = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + WWMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &Session.SerialDefaults ) ) { + Session.Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (Session.SerialDefaults.Port != 0 && + Session.SerialDefaults.IRQ != -1 && + Session.SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &Session.SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + + return( retval ); +} + + + + + + +#ifdef WIN32 +/*********************************************************************************************** + * Advanced_Modem_Settings -- Allows to user to set additional modem settings * + * * + * * + * * + * INPUT: current settings * + * * + * OUTPUT: modified settings * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/16/96 2:29PM ST : Created * + *=============================================================================================*/ +Advanced_Modem_Settings (SerialSettingsType *settings) +{ + + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +#if (FRENCH | GERMAN) + int d_dialog_w = 440; // dialog width +#else + int d_dialog_w = 340; // dialog width +#endif + + int d_dialog_h = 200; // dialog height + int d_dialog_x = 320 - d_dialog_w/2; // dialog x-coord + int d_dialog_y = 200 - d_dialog_h/ 4; // dialog y-coord + + + int d_compression_w = 50; + int d_compression_h = 18; + int d_compression_x = d_dialog_x + d_dialog_w/2 +40; + int d_compression_y = d_dialog_y + 40; + + int d_errorcorrection_w = 50; + int d_errorcorrection_h = 18; + int d_errorcorrection_x = d_dialog_x + d_dialog_w/2 +40; + int d_errorcorrection_y = d_dialog_y + 65; + + int d_hardwareflowcontrol_w = 50; + int d_hardwareflowcontrol_h = 18; + int d_hardwareflowcontrol_x = d_dialog_x + d_dialog_w/2 +40; + int d_hardwareflowcontrol_y = d_dialog_y + 90; + + int d_default_w = 100; + int d_default_h = 18; + int d_default_x = d_dialog_x + d_dialog_w / 2 - d_default_w / 2; + int d_default_y = d_dialog_y + d_dialog_h - 70; + + int d_ok_w = 100; + int d_ok_h = 18; + int d_ok_x = d_dialog_x + d_dialog_w/2 - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - 40; + + enum { + BUTTON_COMPRESSION = 100, + BUTTON_ERROR_CORRECTION, + BUTTON_HARDWARE_FLOW_CONTROL, + BUTTON_DEFAULT, + BUTTON_OK, + }; + + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND, + } RedrawType; + + /* + ** Yes/No strings + */ + char compress_text [16]; + char correction_text [16]; + char flowcontrol_text[16]; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Initialise the button text + */ + strcpy (compress_text, settings->Compression ? Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + + /* + ** Create the buttons + */ + TextButtonClass compressionbutton(BUTTON_COMPRESSION, compress_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_compression_x, d_compression_y, d_compression_w, d_compression_h); + + TextButtonClass errorcorrectionbutton(BUTTON_ERROR_CORRECTION, correction_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_errorcorrection_x, d_errorcorrection_y, d_errorcorrection_w, d_errorcorrection_h); + + TextButtonClass hardwareflowcontrolbutton(BUTTON_HARDWARE_FLOW_CONTROL, flowcontrol_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_hardwareflowcontrol_x, d_hardwareflowcontrol_y, d_hardwareflowcontrol_w, d_hardwareflowcontrol_h); + + TextButtonClass defaultbutton(BUTTON_DEFAULT, TXT_DEFAULT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass okbutton(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + + /* + ** Misc. variables. + */ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + GadgetClass *commands; // button list + + + commands = &okbutton; + defaultbutton.Add_Tail(*commands); + compressionbutton.Add_Tail(*commands); + errorcorrectionbutton.Add_Tail(*commands); + hardwareflowcontrolbutton.Add_Tail(*commands); + + + /* + ** Main process loop + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_MODEM_INITIALISATION, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_DATA_COMPRESSION, + d_compression_x - 26, d_compression_y + 2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_ERROR_CORRECTION, + d_errorcorrection_x - 26, d_errorcorrection_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_HARDWARE_FLOW_CONTROL, + d_hardwareflowcontrol_x -26, d_hardwareflowcontrol_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + compressionbutton.Flag_To_Redraw(); + errorcorrectionbutton.Flag_To_Redraw(); + hardwareflowcontrolbutton.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_COMPRESSION | KN_BUTTON): + settings->Compression = settings->Compression ^ 1; + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_ERROR_CORRECTION | KN_BUTTON): + settings->ErrorCorrection = settings->ErrorCorrection ^ 1; + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_HARDWARE_FLOW_CONTROL | KN_BUTTON): + settings->HardwareFlowControl = settings->HardwareFlowControl ^ 1; + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_DEFAULT | KN_BUTTON): + settings->Compression = false; + settings->ErrorCorrection = false; + settings->HardwareFlowControl = true; + + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } +} +#endif //WIN32 + + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 5 * RESFACTOR; // margin width/height + + int d_portlist_w = 80 * RESFACTOR + 80*(RESFACTOR-1); //Port list wider in hires + int d_portlist_h = 33 * RESFACTOR; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); +#ifdef WIN32 + d_portlist_x = 0x45; +#endif + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 * RESFACTOR; + +#ifdef WIN32 + int d_port_w = d_portlist_w; + int d_port_x = 0x45; +#else + int d_port_w = (5 * 6 * RESFACTOR) + 3 * RESFACTOR; + +// int d_port_x = d_portlist_x + 29 * RESFACTOR; +#ifdef FRENCH //VG2 + int d_port_x = (d_portlist_x + 29 * RESFACTOR) + 5; +#else + int d_port_x = d_portlist_x + 29 * RESFACTOR; +#endif + +#endif //WIN32 + int d_port_h = 9 * RESFACTOR; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_irqlist_w = 80 * RESFACTOR; + int d_irqlist_h = 33 * RESFACTOR; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_irq_h = 9 * RESFACTOR; + int d_irq_x = d_irqlist_x + 25 * RESFACTOR; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 * RESFACTOR; + int d_baudlist_h = 33 * RESFACTOR; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); +#ifdef WIN32 + d_baudlist_x -= 32; +#endif + int d_baudlist_y = d_irqlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_baud_h = 9 * RESFACTOR; + int d_baud_x = d_baudlist_x + 29 * RESFACTOR; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 8 * RESFACTOR + 3 * RESFACTOR; + int d_initstrlist_h = 21 * RESFACTOR; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_portlist_y + d_portlist_h + ((d_margin + d_txt6_h) * 2) + d_margin + 4; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_initstr_h = 9 * RESFACTOR; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + + int d_add_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_add_x = d_dialog_cx - (d_add_w / 2); +#else + int d_add_x = (d_dialog_cx - (d_add_w / 2)) + 30; +#endif + int d_add_h = 9 * RESFACTOR; + int d_add_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_delete_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_delete_x = (d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2)) + 10; +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_h = 9 * RESFACTOR; + int d_delete_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstrlist_h = 27 * RESFACTOR; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstr_h = 9 * RESFACTOR; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 * RESFACTOR; + int d_tone_h = 9 * RESFACTOR; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 * RESFACTOR; + int d_pulse_h = 9 * RESFACTOR; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + +#ifdef FRENCH + int d_save_w = 80 * RESFACTOR; +#else + int d_save_w = 40 * RESFACTOR; +#endif + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_x + (d_dialog_w / 5) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 4*RESFACTOR; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 4) / 5) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 4*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_advanced_w = 50*RESFACTOR; +#else + int d_advanced_w = 40*RESFACTOR; +#endif + int d_advanced_h = 9*RESFACTOR; + int d_advanced_x = d_dialog_x + ((d_dialog_w) / 2) - (d_advanced_w / 2); + int d_advanced_y = d_dialog_y + d_dialog_h - d_advanced_h - d_margin - 4 *RESFACTOR; + + /* + ** Button Enumerations + */ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_ADVANCED, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[4] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8", + }; + + static char custom_port[10 + MODEM_NAME_MAX] = {"CUSTOM - ????"}; + + + +#ifndef WIN32 // No IRQ dialog in Win version + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; + +#endif //WIN32 + +#ifdef WIN32 + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char modemnames[10][MODEM_NAME_MAX]; + +#else //WIN32 + static char *baudname[5] = { + "9600", + "14400", + "19200", + "28800", + "38400" + }; +#endif //WIN32 + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) + int port_custom_index = 4; //index of custom entry in port list + int irq_index = 1; // index of currently-selected irq (default = 3) + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + bool firsttime = true; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, portbuf, PORTBUF_MAX, + TPF_TEXT, d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#ifndef WIN32 + EditClass irq_edt (BUTTON_IRQ, irqbuf, IRQBUF_MAX, TPF_TEXT, d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#endif //WIN32 + + EditClass baud_edt (BUTTON_BAUD, baudbuf, BAUDBUF_MAX, TPF_TEXT, d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + ListClass baudlist(BUTTON_BAUDLIST, d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + EditClass initstr_edt (BUTTON_INITSTR, initstrbuf, INITSTRBUF_MAX, TPF_TEXT, d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + ListClass initstrlist(BUTTON_INITSTRLIST, d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + EditClass cwaitstr_edt (BUTTON_CWAITSTR, cwaitstrbuf, CWAITSTRBUF_MAX, TPF_TEXT, d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, TPF_BUTTON, d_tone_x, d_tone_y, d_tone_w, d_tone_h); + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, TPF_BUTTON, d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#ifdef WIN32 + TextButtonClass advancedbutton(BUTTON_ADVANCED, TXT_ADVANCED, TPF_BUTTON, d_advanced_x, d_advanced_y, d_advanced_w, d_advanced_h); +#endif //WIN32 + /* + ** Various Inits + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { +#ifdef WIN32 + tempsettings.Baud = 19200; +#else //WIN32 + tempsettings.Baud = 9600; +#endif //WIN32 + } + + + /* + ** Set the current indices + */ +#ifndef WIN32 + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //WIN32 + +#ifdef WIN32 + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else if (tempsettings.Baud == 19200) { + baud_index = 1; + } else if (tempsettings.Baud == 28800) { + baud_index = 2; + } else if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } +#else //WIN32 + if (tempsettings.Baud == 9600) { + baud_index = 0; + } else if (tempsettings.Baud == 14400) { + baud_index = 1; + } else if (tempsettings.Baud == 19200) { + baud_index = 2; + } else if (tempsettings.Baud == 28800) { + baud_index = 3; + } else { + baud_index = 4; + } +#endif //WIN32 + sprintf (baudbuf, "%d", tempsettings.Baud); + + /* + ** Set up the port list box & edit box + */ + for (i = 0; i < 4; i++) { + portlist.Add_Item( portname[ i ] ); + } + +#ifdef WIN32 + /* + ** Loop through the first 10 possible modem entries in the registry. Frankly, its just + ** tough luck if the user has more than 10 modems attached! + */ + if (ModemRegistry) { + delete ModemRegistry; + } + int modems_found = 0; + for (i=0 ; i<10 ; i++) { + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()) { + strncpy (modemnames[modems_found], ModemRegistry->Get_Modem_Name(), MODEM_NAME_MAX); + portlist.Add_Item( modemnames [modems_found++] ); + port_custom_index ++; + } + delete ModemRegistry; + } + ModemRegistry = NULL; + +#endif //WIN32 + + portlist.Add_Item ( custom_port ); + + + /* + ** Work out the current port index + */ + port_index = -1; +#ifdef WIN32 + if (tempsettings.ModemName[0]) { + for ( i=0 ; i= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Dialog & Field labels + */ + Draw_Caption(TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PORT_COLON, d_port_x - 3 * RESFACTOR, d_port_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#ifndef WIN32 + Fancy_Text_Print(TXT_IRQ_COLON, d_irq_x - 3 * RESFACTOR, d_irq_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#endif //WIN32 + Fancy_Text_Print(TXT_BAUD_COLON, d_baud_x - 3 * RESFACTOR, d_baud_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + + Fancy_Text_Print(TXT_INIT_STRING, d_initstr_x, d_initstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CWAIT_STRING, d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + /* + ** Redraw buttons + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif //WIN32 + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); +#ifdef WIN32 + advancedbutton.Flag_To_Redraw(); +#endif //WIN32 + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + if ( firsttime ) { +// port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ** Process input + */ + switch (input) { + +#ifdef WIN32 + case (BUTTON_ADVANCED | KN_BUTTON): + Advanced_Modem_Settings (&tempsettings); + display = REDRAW_ALL; + break; +#endif //WIN32 + + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //WIN32 + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + + } else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: +#ifdef WIN32 + if (portbuf[3] <= '9' && portbuf[3] >'0') { + portbuf[4] = 0; + port_index = port_custom_index; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + break; + } +#endif //WIN32 + WWMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } +#ifndef WIN32 + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //WIN32 + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + port_edt.Clear_Focus(); + + // auto select the irq for port +#ifndef WIN32 + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //WIN32 + } else { + if (port_index == port_custom_index) { + /* + ** This is the custom entry + */ + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + } else { + /* + ** Must be a modem name entry so just copy iy + */ + strncpy (portbuf, item, PORTBUF_MAX); + } + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + } else if (port_index < port_custom_index) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + +#ifndef WIN32 + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; +#endif //WIN32 + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( Session.InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /* + ** Add a new InitString entry + */ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + Session.InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + if (item == Session.InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, Session.InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( Session.InitStrings.Count() && initstr_index != -1) { + Session.InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + tempsettings.ModemName[0] = 0; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + tempsettings.ModemName[0] = 0; + break; + + default: + if (port_index == port_custom_index) { +#ifdef WIN32 + strncpy ( tempsettings.ModemName, portbuf, MODEM_NAME_MAX ); + tempsettings.Port = 1; +#else //WIN32 + sscanf( portbuf, "%x", &tempsettings.Port ); + tempsettings.ModemName[0] = 0; +#endif //WIN32 + } else { + /* + ** Must be a modem name index + */ + strcpy (tempsettings.ModemName, portlist.Current_Item()); + tempsettings.Port = 1; + } + break; + } + + +#ifndef WIN32 + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //WIN32 + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = Session.CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + + } else if (dpstatus == PORT_INVALID) { +#ifdef WIN32 + WWMessageBox().Process( TXT_UNABLE_TO_OPEN_PORT ); +#else //WIN32 + WWMessageBox().Process( TXT_INVALID_SETTINGS ); +#endif //WIN32 + firsttime = true; + display = REDRAW_ALL; + + } else if (dpstatus == PORT_IRQ_INUSE) { + WWMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = true; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&Session.InitStrings[0]), Session.InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, Session.InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else if (curidx >= list->Count() ) { + curidx = 0; + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, Session.InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. + * 01/21/97 V.Grippi added check for CS before sending scenario file * + *=============================================================================================*/ +int Com_Scenario_Dialog(bool skirmish) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 7 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + 1 * RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h*/; + + int d_scenariolist_w = 162 * RESFACTOR; + int d_scenariolist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + + if (skirmish) { + d_scenariolist_h *= 2; + } + + + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - 5*RESFACTOR; + int d_scenariolist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR; + + if (skirmish) { + d_scenariolist_x = d_dialog_x + (d_dialog_w-d_scenariolist_w)/2; + } + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_count_y = d_playerlist_y + d_playerlist_h + (d_margin1 * 2) - 2 * RESFACTOR; + + if (skirmish) { + d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + } + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_dialog_x + d_dialog_w - 149 * RESFACTOR; + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (8 * d_txt6_h) + 3 * RESFACTOR; // 4 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + 2*RESFACTOR; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 45 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - RESFACTOR*6; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - RESFACTOR*6; + + int d_load_w = 45 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - RESFACTOR*6; + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_PLAYERLIST, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + BUTTON_DIFFICULTY, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int playertabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + bool transmit; // 1 = re-transmit new game options + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + unsigned long version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static bool first_time = true; + bool oppscorescreen = false; + bool gameoptions = Session.Type == GAME_SKIRMISH; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + CCFileClass loadfile ("SAVEGAME.NET"); + bool load_game = false; // 1 = load a saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + bool messages_have_focus = true; // Gadget focus starts on the message system + + Set_Logic_Page(SeenBuff); + + CDTimerClass kludge_timer; // Timer to allow a wait after client joins + // game before game can start + bool ok_button_added = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass * commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, d_load_w, d_load_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + SliderClass difficulty(BUTTON_DIFFICULTY, d_name_x, optionlist.Y + optionlist.Height + d_margin1 + d_margin1, d_dialog_w - (d_name_x-d_dialog_x)*2, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + difficulty.Set_Maximum(5); + difficulty.Set_Value(2); + } else { + difficulty.Set_Maximum(3); + difficulty.Set_Value(1); + } + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + if (!skirmish) { + playerlist.Add_Tail(*commands); + } else { + difficulty.Add_Tail(*commands); + } + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + if (Session.Type == GAME_SKIRMISH){ + okbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + if (!skirmish && loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE + //Load button added only when other player has arrived + //loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Special.IsShadowGrow = Rule.IsMPShadowGrow; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = false; + } + + /*........................................................................ + Init button states + ........................................................................*/ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + if (!skirmish) { + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + } + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsShadowGrow); + if (!skirmish) { + optionlist.Check_Item(4, Special.IsCaptureTheFlag); + } + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + int maxp = Rule.MaxPlayers - 2; +// int maxp = Rule.MaxPlayers - (skirmish ? 1 : 2); + aiplayersgauge.Set_Maximum(maxp); + + if (skirmish) { + if ( Session.Options.AIPlayers > 7 ) { + Session.Options.AIPlayers = 7; + } + Session.Options.AIPlayers = max(Session.Options.AIPlayers, 1); + }else{ + if ( Session.Options.AIPlayers > 6 ) { + Session.Options.AIPlayers = 6; + } + } + + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = //Session.Options.Tiberium; + Rule.IsTGrowth = //Session.Options.Tiberium; + Special.IsTSpread = //Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (int j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + break; + } + } + if (EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ +#ifdef FIXIT_RANDOM_GAME + srand(time(NULL)); + Seed = rand(); +#else +// randomize(); +// Seed = rand(); +#endif + + /*........................................................................ + Init the message display system + ........................................................................*/ + if (!skirmish) { + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1, d_send_y + 1, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + } + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + TPF_TEXT, -1); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + } + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + if (!skirmish) { + NullModem.Reset_Response_Time(); // clear response time + } + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount; + + + /* + ** Those easily offended should avert their eyes from the following line. And whatever + ** you do, dont search for 'goto'. + */ +oh_dear_its_a_label: + + while (process) { + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Mono_Debug_Print(0); + } + #endif + + if (!skirmish){ + if (!ok_button_added && gameoptions && kludge_timer == 0){ + okbtn.Add_Tail(*commands); + ok_button_added = true; +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) + { + loadbtn.Add_Tail( *commands ); + } +#else +#ifdef FIXIT_MULTI_SAVE + if ( loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { + loadbtn.Add_Tail ( *commands ); + } +#endif +#endif + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + } + } + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (!skirmish) { + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + if (!skirmish) { + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + Fancy_Text_Print(TXT_EASY, difficulty.X, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_HARD, difficulty.X + difficulty.Width, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_RIGHT|TPF_TEXT); + Fancy_Text_Print(TXT_NORMAL, difficulty.X + difficulty.Width/2, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_CENTER|TPF_TEXT); + } + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE && !skirmish) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { + +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2, d_count_y, d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); +// Fancy_Text_Print("%d ", d_count_x + d_count_w + 3 * RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT, Session.Options.UnitCount); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 3 * RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); +// Fancy_Text_Print("%d", d_credits_x + d_credits_w + 2 * RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT, Session.Options.Credits); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); +// Fancy_Text_Print("%d", d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT, Session.Options.AIPlayers); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (!skirmish) { + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + display = REDRAW_MESSAGE; + } + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + if (input & KN_BUTTON) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + + Session.PrefColor = (PlayerColorType)((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx); + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + display = REDRAW_BACKGROUND; + transmit = true; + break; +#endif + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; + +#else // FIXIT_VERSION_3 Whoever duplicated Netdlg into Nulldlg should be shot. Wasn't it enough? + + Abandon all hope ye who hit enter here. + + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ( !skirmish && (PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if ( !skirmish && PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + } else + if ( !skirmish && PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + }else{ + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; +#endif + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + SessionClass::CountMin[Session.Options.Bases]; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = ((Session.Options.Credits + 250) / 500) * 500; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + { + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + int humans = 2; // Two humans. + if (skirmish) { + Session.Options.AIPlayers += 1; // Always one forced AI player. + humans = 1; // One human. + // if (Session.Options.AIPlayers == 0) { + // Session.Options.AIPlayers = 1; + // aiplayersgauge.Set_Value(0); + // } + } + if (Session.Options.AIPlayers+humans >= Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - humans; + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + } + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + break; + } + + //------------------------------------------------------------------ + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //------------------------------------------------------------------ + case (BUTTON_OPTIONS | KN_BUTTON): + if (!skirmish && Special.IsCaptureTheFlag != optionlist.Is_Checked(4) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != optionlist.Is_Checked(0)) { + Session.Options.Bases = optionlist.Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + if (!skirmish) optionlist.Check_Item(4, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsShadowGrow = optionlist.Is_Checked(3); + if (!skirmish) { + Special.IsCaptureTheFlag = optionlist.Is_Checked(4); + } + + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + rc = true; + process = false; + + // force transmitting of game options packet one last time + + transmit = true; + transmittime = 0; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) + load_game = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!skirmish) { + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + } + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (skirmish) { + transmit = false; + } + + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ScenarioInfo.Credits = Session.Options.Credits; + SendPacket.ScenarioInfo.IsBases = Session.Options.Bases; + SendPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + SendPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + SendPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + SendPacket.ScenarioInfo.BuildLevel = BuildLevel; + SendPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + SendPacket.ScenarioInfo.Seed = Seed; + SendPacket.ScenarioInfo.Special = Special; + SendPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + SendPacket.ID = Session.ModemType; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (SendPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + SendPacket.ScenarioInfo.FileLength = file.Size(); + +#ifdef WOLAPI_INTEGRATION + strcpy( SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(SendPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)SendPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof SendPacket.ScenarioInfo.FileDigest); + SendPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if (!skirmish && (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (!skirmish && NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + kludge_timer = 2*60; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + transmit = true; + + parms_received = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process (TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + } else { + + + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists + .........................................................*/ + if (playerlist.Count()==0) { + //...................................................... + // Add two strings to the player list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[TheirColor]); + } + + //......................................................... + // Ensure the player list has the latest, greatest copy of + // our names & colors. Do this every time we receive an + // options packet. + //......................................................... + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : TheirColor]; + + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ScenarioInfo.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if (!skirmish && (TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + if (!skirmish) { + NullModem.Service(); + } + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = skirmish ? 1 : 2; + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + /* + ** Fetch the difficulty setting when in skirmish mode. + */ + if (skirmish) { + int diff = difficulty.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2); + switch (diff) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } else { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } + + if (!skirmish) { + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + TheirName[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(TheirName, "2"); + } + } + #endif + + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + } + + /*..................................................................... + Send all players a GO packet. + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + if (load_game) { + SendPacket.Command = SERIAL_LOADGAME; + } else { + SendPacket.Command = SERIAL_GO; + } + + if (!skirmish) { + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { + ; + } else if (SendPacket.ScenarioInfo.ResponseTime < theirresponsetime) { + SendPacket.ScenarioInfo.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + if (!skirmish) { + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((SendPacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) + ); + } else { + Session.MaxAhead = max( (SendPacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + SendPacket.ID = Session.ModemType; + + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + /* + ** Wait for the go response. This will be either a 'GO' reply, a + ** request for the scenario to be sent or a reply to say that the scenario + ** cant be played. + */ +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); +#endif + do { + NullModem.Service(); + + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + +#ifdef FIXIT_VERSION_3 + if (ReceivePacket.Command == SERIAL_READY_TO_GO) + { + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } + break; + } +#else + if (ReceivePacket.Command == SERIAL_READY_TO_GO) break; +#endif + + if (ReceivePacket.Command == SERIAL_NO_SCENARIO) { + WWMessageBox().Process(TXT_NO_EXPANSION_SCENARIO, TXT_CANCEL); + /* + ** We have to recover from this somehow so.... + */ + process = true; + display = REDRAW_ALL; + lastmsgtime = TickCount; + goto oh_dear_its_a_label; + } + + if (ReceivePacket.Command == SERIAL_REQ_SCENARIO) { +#ifdef WIN32 + WWDebugString ("RA95 - About to call 'Send_Remote_File'.\n"); + +#endif + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + Send_Remote_File (Scen.ScenarioName, 0); + + break; + } + } + + } while ( !Keyboard->Check() ); + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + } + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + if (!skirmish) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + } + + if (!skirmish) Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings(); + } + + if (load_game && !skirmish) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); +} + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Find_Local_Scenario -- finds the file name of the scenario with matching attributes * + * * + * * + * * + * INPUT: ptr to Scenario description * + * ptr to Scenario filename to fix up * + * length of file for trivial rejection of scenario files * + * ptr to digest. Digests must match. * + * * + * * + * OUTPUT: true if scenario is available locally * + * * + * WARNINGS: We need to reject files that don't match exactly because scenarios with the same * + * description can exist on both machines but have different contents. For example * + * there will be lots of scenarios called 'my map' and 'crap' and 'aaaaaa'. * + * * + * HISTORY: * + * 8/23/96 12:36PM ST : Created * + *=============================================================================================*/ +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official) +{ +//FILE *fp; +//fp = fopen("findscen.txt","wt"); +//debugprint("looking for local scenario: description = %s, name=%s, length=%d, digest=%s, official=%d\n", description, filename, length, digest, official); + + char digest_buffer[32]; + /* + ** Scan through the scenario list looking for scenarios with matching descriptions. + */ + for (int index = 0; index < Session.Scenarios.Count(); index++) { + +//debugprint( "Checking against scenario: %s\n", Session.Scenarios[index]->Description()); + if (!strcmp (Session.Scenarios[index]->Description(), description)) { +//debugprint("found matching description.\n"); + CCFileClass file (Session.Scenarios[index]->Get_Filename()); + + /* + ** Possible rejection on the basis of availability. + */ + if (file.Is_Available()) { +//debugprint("file is available.\n"); + /* + ** Possible rejection on the basis of size. + */ + if (file.Size() == length) { +//debugprint("length matches.\n"); + /* + ** We don't know the digest for 'official' scenarios so assume its correct + */ + if (!official) { +//debugprint("!official.\n"); + /* + ** Possible rejection on the basis of digest + */ + INIClass ini; + ini.Load(file); + ini.Get_String ("Digest", "1", "No digest here mate. Nope.", digest_buffer, sizeof (digest_buffer) ); + } +//debugprint("digest = %s, digest_buffer = %s.\n", digest, digest_buffer); +#ifdef FIXIT_CSII // checked - ajw 9/28/98. But don't know why this happens. Because of autodownload? + /* + ** If this is an aftermath scenario then ignore the digest and return success. + */ + if ( Is_Mission_Aftermath ((char*)Session.Scenarios[index]->Get_Filename()) ) { +//debugprint("a 1match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } +#endif + + /* + ** This must be the same scenario. Copy the name and return true. + */ + if (official || !strcmp (digest, digest_buffer)) { +//debugprint("a match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } + } + } +// else +// debugprint("file not available '%s'.\n", Session.Scenarios[index]->Get_Filename()); + + } + } +//debugprint("failed match.\n"); + /* + ** Couldnt find the scenario locally. Return failure. + */ + return (false); +} + + + + + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * The 'Players' vector is filled in by this routine, when the game starts; this * + * is for the Assign_Houses routine, which expects this vector to contain all * + * players' names & houses & colors. Other than that, the Players vector, Games * + * vector, and Chat vector aren't used at all by this routine. The Game & Players * + * list boxes are filled in manually in the processing loop. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 2 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin1 + d_margin2 + d_txt6_h + 1*RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; + +#else //OLDWAY + + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; + +#endif //OLDWAY + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + int d_color_y = d_name_y; + + int d_scenario_y = d_name_y + d_name_h + d_margin2; + + int d_gamelist_w = 160 * RESFACTOR; + int d_gamelist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_gamelist_y = d_scenario_y + d_txt6_h + d_margin2 + d_txt6_h + d_margin2; + +//BG int d_playerlist_w = 112 * RESFACTOR; + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w - 5*RESFACTOR; + int d_playerlist_y = d_gamelist_y; + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + (d_margin1 * 2) - d_margin2; + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 * RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 112 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin1 - d_margin2; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (7 * d_txt6_h) + 3 * RESFACTOR; // 7 rows high + int d_message_x = d_gamelist_x;//d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + d_margin2/*KO + d_margin1*/; + + int d_send_w = d_message_w; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_message_x; + int d_send_y = d_message_y + d_message_h; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_send_y + d_send_h/*KO + d_margin2*/; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else //OLDWAY + BUTTON_HOUSE, +#endif //OLDWAY + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name +//BG int playertabs[] = {77}; // tabs for player list box + int playertabs[] = {71*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for options list box + bool transmit; // 1 = re-transmit new game options + bool first; // 1 = no packets received yet + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + unsigned long version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + bool load_game = false; // 1 = load saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + char *p; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + bool messages_have_focus = true; + bool ready_packet_was_sent = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else //OLDWAY + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif //OLDWAY + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else //OLDWAY + housebtn.Add_Tail(*commands); +#endif //OLDWAY + + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else //OLDWAY + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif //OLDWAY + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-2); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + transmit = true; + first = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 7, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 * RESFACTOR, d_send_y + 1 * RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + Session.WWChat = 0; + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + TPF_TEXT, -1); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount; + + bool process = true; // process while true + while (process) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print (TXT_COUNT, d_count_x - 2 * RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2 * RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_CREDITS_COLON, d_credits_x - 2 * RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1 * RESFACTOR, d_color_y + 1 * RESFACTOR, + cbox_x[i] + 1 * RESFACTOR + d_color_w - 2 * RESFACTOR, d_color_y + 1 * RESFACTOR + d_color_h - 2 * RESFACTOR, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print(txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + /*............................................................ + Scenario description + ............................................................*/ + //LogicPage->Fill_Rect(d_dialog_x + 16*RESFACTOR, d_scenario_y, + // d_dialog_x + d_dialog_w - 16*RESFACTOR, d_scenario_y + d_txt6_h, BLACK); + + p = (char *)Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { +// sprintf(txt,"%s %s",p, Session.Options.ScenarioDescription); +// Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + // EW - Scenario language translation goes here!!!!!!!! VG + for (i = 0; EngMisStr[i] != NULL; i++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[i])) { +#if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[i+1]); +#else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +#endif + break; + } + } + if (EngMisStr[i] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER); + } + + //......................................................... + // Unit count, tech level, credits + //......................................................... + //LogicPage->Fill_Rect(d_count_x + d_count_w + 2 * RESFACTOR, d_count_y, + // d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, + // BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + KeyNumType input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + changed = true; + + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (Session.PrefColor == TheirColor) + break; + } + Session.ColorIdx = Session.PrefColor; + + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + strcpy (Session.Handle, namebuf); + transmit = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } else if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else //OLDWAY + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + transmit = true; + //display = REDRAW_BACKGROUND; + break; +#endif //OLDWAY + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if ((TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message(&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while ( (TickCount - starttime) < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (Session.ColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = true; + transmittime = 0; + + Session.ColorIdx = (PlayerColorType)(TheirColor + 1); + if (Session.ColorIdx >= 6) { + Session.ColorIdx = PCOLOR_FIRST; + } + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + Session.Options.Credits = ReceivePacket.ScenarioInfo.Credits; + Session.Options.Bases = ReceivePacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = ReceivePacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = ReceivePacket.ScenarioInfo.IsGoodies; + Session.Options.AIPlayers = ReceivePacket.ScenarioInfo.AIPlayers; + BuildLevel = ReceivePacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = ReceivePacket.ScenarioInfo.UnitCount; + Seed = ReceivePacket.ScenarioInfo.Seed; + Special = ReceivePacket.ScenarioInfo.Special; + Options.GameSpeed = ReceivePacket.ScenarioInfo.GameSpeed; + + if (Session.Options.Tiberium) { + Special.IsTGrowth = true; + Rule.IsTGrowth = true; + Special.IsTSpread = true; + Rule.IsTSpread = true; + } else { + Special.IsTGrowth = false; + Rule.IsTGrowth = false; + Special.IsTSpread = false; + Rule.IsTSpread = false; + } + + //......................................................... + // Adjust the gauges + //......................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //......................................................... + // Update the options list box + //......................................................... + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + /* + ** If the scenario name changed then we need to redraw the whole lot. + */ + if (strcmp (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario)) { + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, ReceivePacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest, sizeof( ReceivePacket.ScenarioInfo.FileDigest )); +#else + strcpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = ReceivePacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = ReceivePacket.ScenarioInfo.FileLength; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process(TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists, then transmit our options + to him. + .........................................................*/ + if (first) { + //...................................................... + // Add a string to the game list, and two to the player + // list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; + gamelist.Add_Item(item); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]); + + first = false; + transmit = true; + transmittime = 0; + } + + //......................................................... + // Ensure the game list & player list have the latest, + // greatest copy of our names & colors. Do this every time + // we receive an options packet. + //......................................................... + item = (char *)gamelist.Get_Item(0); + sprintf(item,Text_String(TXT_THATGUYS_GAME), + TheirName); + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); + +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]; + + gamelist.Flag_To_Redraw(); + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_LOADGAME): + load_game = true; + case (SERIAL_GO): + + ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + #ifdef WIN32 + WWDebugString ("RA95 - Counterstrike CD is not in drive\n"); + #endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** Make sure we dont time out because of the disk swap + */ + lastmsgtime = TickCount; + } + } + } + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if (!ready_packet_was_sent) { + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided it isnt an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + /* + ** We dont have the scenario and we dont want to request that it gets + ** sent because its an official one. + ** Print up a message saying we cant play this scenario and reply to the + ** host, telling him to select another. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_NO_SCENARIO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + NullModem.Service(); + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + display = REDRAW_ALL; + lastmsgtime = TickCount; + break; + } else { +#endif +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 0)) { + rc = false; + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + lastmsgtime = TickCount; + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + + /* + ** Fall through here... + */ + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((ReceivePacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } else { + Session.MaxAhead = max( (ReceivePacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + + process = false; + rc = true; + if (ReceivePacket.Command == SERIAL_LOADGAME) + load_game = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ((TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = 2; + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + namebuf[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(namebuf, "2"); + } + } + #endif + + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + who = new NodeNameType; + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ +// while (optionlist.Count()>0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + while (gamelist.Count()>0) { + item = (char *)gamelist.Get_Item(0); + delete [] item; + gamelist.Remove_Item(item); + } + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); +//#ifdef WIN32 +// Load_Uncompress(CCFileClass("TITLE.CPS"), SysMemPage, SysMemPage, CCPalette); +// SysMemPage.Scale(SeenPage); +//#else +// Load_Uncompress(CCFileClass("TITLE.CPS"), HidPage, HidPage, CCPalette); +// HidPage.Blit(SeenPage); +//#endif + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 * RESFACTOR; // dialog width + int d_dialog_h = 160 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_phonelist_w = 248 * RESFACTOR; + int d_phonelist_h = 87 * RESFACTOR; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11 * RESFACTOR; + + int d_add_w = 45 * RESFACTOR; + int d_add_h = 9 * RESFACTOR; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45 * RESFACTOR; + int d_edit_h = 9 * RESFACTOR; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45 * RESFACTOR; + int d_delete_h = 9 * RESFACTOR; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_numedit_h = 9 * RESFACTOR; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*RESFACTOR, 207*RESFACTOR}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + bool changed = false; // 1 = save changes to INI file + bool firsttime = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ListClass phonelist(BUTTON_PHONELIST, d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, TPF_BUTTON, d_edit_x, d_edit_y, d_edit_w, d_edit_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + EditClass numedit (BUTTON_NUMEDIT, phone_num, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + firsttime = true; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + phonelist.Draw_Me (true); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + #ifdef FIXIT_PHONELIST_CRASH + if (Session.CurPhoneIdx != -1) { + #endif + if (phonelist.Current_Index() != Session.CurPhoneIdx) { + Session.CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = true; + } + #ifdef FIXIT_PHONELIST_CRASH + } + #endif + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*Session.PhoneBook[Session.CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*Session.PhoneBook[Session.CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (Session.PhoneBook[Session.CurPhoneIdx] == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + Session.PhoneBook.Delete (Session.CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = true; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (Session.CurPhoneIdx == -1 || + strcmp( Session.PhoneBook[Session.CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = true; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + } + } + changed = true; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&Session.PhoneBook[0]), Session.PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( Session.PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, Session.PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( Session.PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( Session.PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, Session.PhoneBook[i]->Number ); + } else { + strncpy( phonenum, Session.PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (Session.PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + Session.PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || Session.CurPhoneIdx < -1) { + Session.CurPhoneIdx = -1; + } else { + if (Session.CurPhoneIdx >= list->Count() ) { + Session.CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (Session.CurPhoneIdx > -1) { + strcpy (buf, Session.PhoneBook[Session.CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( Session.CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 * RESFACTOR; // dialog width + int d_dialog_h = 110 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 * RESFACTOR; + int d_name_y = d_dialog_y + 25 * RESFACTOR; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_number_h = 9 * RESFACTOR; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 * RESFACTOR; + int d_number_y = d_name_y + d_name_h + d_margin; + + int d_default_w = 130 * RESFACTOR; + int d_default_h = 9 * RESFACTOR; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + + int d_custom_w = 130 * RESFACTOR; + int d_custom_h = 9 * RESFACTOR; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + + int d_save_w = 55 * RESFACTOR; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h - 5*RESFACTOR; + + int d_cancel_w = 55 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h - 5*RESFACTOR; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + bool custom = false; + bool firsttime = true; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit(BUTTON_NAME, namebuf, PhoneEntryClass::PHONE_MAX_NAME, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + EditClass numedit(BUTTON_NUMBER, numbuf, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, TPF_BUTTON, d_default_x, d_default_y, d_default_w, d_default_h); + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, TPF_BUTTON, d_custom_x, d_custom_y, d_custom_w, d_custom_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = Session.SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + bool process = true; // process while true + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print (TXT_NAME_COLON, d_name_x - 5 * RESFACTOR, d_name_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + Fancy_Text_Print (TXT_NUMBER_COLON, d_number_x - 5 * RESFACTOR, d_number_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + KeyNumType input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = false; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = true; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + + + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); +// WWMessageBox().Process( "Error in the InitString." ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + + /* + ** Completely disable audio. This is required for MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + WWMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + WWMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + Session.ModemService = false; + WWMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Completely disable audio. This is required for some MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + +// case DIAL_BUSY: +// WWMessageBox().Process(TXT_LINE_BUSY); +// connected = false; +// break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_CANCELED: + WWMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s",buf); + } else { + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + if (Debug_Modem_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + +void Log_Start_Time( char *string ) +{ +// LogDump_Print = true; + + LogLevel = 0; + LogLevelTime[ LogLevel ] = LogLastTime = TickCount; + + Smart_Printf( "start tick=%d, %s \n", LogLastTime, string ); +} + + +void Log_End_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + while (LogLevel >= 0) { + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel-- ]; + Smart_Printf( "end tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + } + + LogDump_Print = false; +} + + +void Log_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + + Smart_Printf( "tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + LogLastTime = currtime; +} + + +void Log_Start_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + Smart_Printf( "start ntick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel >= (MAX_LOG_LEVEL - 1) ) { + Smart_Printf( "Could not start another nesting Maxed at %d,%d!-! \n", + LogLevel, (MAX_LOG_LEVEL - 1) ); + } else { + LogLevelTime[ ++LogLevel ] = currtime; + } + + LogLastTime = currtime; +} + + +void Log_End_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel <= 0) { + Smart_Printf( "Could not end another nesting Mined at %d,%d!-! \n", + LogLevel, 0 ); + LogLevel = 0; + } + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel ]; + Smart_Printf( "end ntick=%d, ticks=%d, secs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel) { + LogLevel--; + } + + LogLastTime = currtime; +} diff --git a/CODE/NULLMGR.CPP b/CODE/NULLMGR.CPP new file mode 100644 index 0000000..94f2eaa --- /dev/null +++ b/CODE/NULLMGR.CPP @@ -0,0 +1,2620 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/NULLMGR.CPP 2 3/07/97 6:40p Steve_tall $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : NULLMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : May 1, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemClass::NullModemClass -- class constructor * + * NullModemClass::~NullModemClass -- class destructor * + * NullModemClass::Init -- initialization * + * NullModemClass::Send_Message -- sends a message * + * NullModemClass::Get_Message -- polls the Queue for a message * + * NullModemClass::Service -- main polling routine * + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * NullModemClass::Reset_Response_Time -- Resets response time computatio* + * NullModemClass::Oldest_Send -- Returns ptr to oldest unACK'd send buf * + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * NullModemClass::Dial_Modem -- dials a number passed * + * NullModemClass::Answer_Modem -- waits for call and answers * + * NullModemClass::Hangup_Modem -- hangs up the modem * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +extern ModemRegistryEntryClass *ModemRegistry; +#endif //WIN32 +#include "i86.h" + + +// Turn off "expression is not meaningful". +#pragma warning 628 9 + + +// the following line was taken from Greenleaf's +// because of other define conflicts + +#define ESC 27 +#define NOKEY 0xffff +#define INIT_COMMAND_RETRIES 2 + +// this time is in milliseconds + +#define DEFAULT_TIMEOUT 1500 + +// +// the following is for a fix around a greenleaf bug +// where they do not check for the value of abortkey +// to determine whether or not they call the abort modem function. +// +extern "C" { + extern void (*_AbortModemFunctionPtr)(int); +} + +static void (*NullModemClass::OrigAbortModemFunc)(int); + +static KeyNumType NullModemClass::Input; +static GadgetClass *NullModemClass::Commands; // button list + +/* +** Ugly hack: this string stores the string received from the modem +*/ +char ModemRXString[80]; + + +/*************************************************************************** + * NullModemClass::NullModemClass -- class constructor * + * * + * INPUT: * + * numsend # desired entries for the send queue * + * numreceive # desired entries for the receive queue * + * maxlen application's max packet length * + * magicnum application-specific magic # (so we don't * + * accidentally end up talking to another one of our own * + * products using the same protocol) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::NullModemClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum) : ConnManClass() +{ + /*------------------------------------------------------------------------ + Init Port to NULL; we haven't opened Greenleaf yet. + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + + Connection = NULL; + + NumSend = numsend; + NumReceive = numreceive; + MaxLen = maxlen; + MagicNum = magicnum; + + RXBuf = 0; + BuildBuf = 0; + + EchoSize = 500; + EchoBuf = 0; + + OldIRQPri = -1; + + ModemVerboseOn = false; // default true + ModemEchoOn = false; // default true + ModemWaitCarrier = 50000; // default 50 * 1000ms = 50 secs + ModemCarrierDetect = 600; // default 6 * 100ms = .6 secs + ModemCarrierLoss = 1400; // default 14 * 100ms = 1.4 secs + ModemHangupDelay = 20000; // default 20 * 1000ms = 20 secs + ModemGuardTime = 1000; // default 50 * 20ms = 1 sec + ModemEscapeCode = '+'; // default ASCII 43 + + SendOverflows = 0; + ReceiveOverflows = 0; + CRCErrors = 0; + + NumConnections = 0; + + /*------------------------------------------------------------------------ + Init timing parameters + ------------------------------------------------------------------------*/ + RetryDelta = 60; // 60 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 1200; // report bad connection after 20 seconds + +} /* end of NullModemClass */ + + +/*************************************************************************** + * NullModemClass::~NullModemClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::~NullModemClass () +{ + Delete_Connection(); + +} /* end of ~NullModemClass */ + + +/*************************************************************************** + * NullModemClass::Init -- initialization * + * * + * INPUT: * + * port address * + * irq 2-15 * + * dev_name name of communications device (win32 only) * + * baud 300, 1200, 9600, etc * + * parity 'O' (odd), 'E' (even), 'N' (none), 'S' (space), * + * 'M' (mark) * + * wordlength 5, 6, 7, or 8 * + * stopbits 1 or 2 * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 10/9/1996 ST : Modified to take device name in win32 * + *=========================================================================*/ +int NullModemClass::Init (int port, int irq, char *dev_name, int baud, char parity, int wordlen, int stopbits, int flowcontrol) +{ + int com; + +#ifdef WIN32 + + /* + ** Get rid of the 'no reference to' warning + */ + irq = irq; + + /* + ** Make sure the port is closed before we start + */ + if (PortHandle) { + CloseHandle (PortHandle); + PortHandle = NULL; + } + + if (!Connection) { + +#else //WIN32 + + int irqnum; + int address; + int status; + + /* + ** Get rid of the 'no reference to' warning + */ + dev_name = dev_name; + + if (Port) { + PortClose(Port); + Port = NULL; + }else { + +#endif //WIN32 + + /*------------------------------------------------------------------------ + Init our Connection + ------------------------------------------------------------------------*/ + Connection = new NullModemConnClass (NumSend, NumReceive, MaxLen, + MagicNum); + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + + /*--------------------------------------------------------------------- + Allocate our packet parsing buffer; make it the same # of packets as the + # of receive queue entries the application has requested. Use the + "Actual" maximum packet size, given from the connection; this allows for + both headers that get added to the packet. + ---------------------------------------------------------------------*/ + RXSize = Connection->Actual_Max_Packet() * NumReceive; + RXBuf = new char [RXSize]; + + BuildBuf = new char [MaxLen]; + + EchoBuf = new char [ EchoSize ]; + } + + RXCount = 0; + EchoCount = 0; + + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch (port) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + +#ifdef WIN32 + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + } + + /* + ** Shift up the baud rate to sensible values + */ +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + char *device; + + switch ( port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = dev_name; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++ ){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (dev_name, ModemRegistry->Get_Modem_Name() )){ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + device = NULL; + } + + + + /* + ** Open the com port + */ + PortHandle = SerialPort->Serial_Port_Open (device, baud, parity, wordlen, stopbits, flowcontrol); + if (PortHandle == INVALID_HANDLE_VALUE) { + Shutdown(); + return(false); + } + + Connection->Init (PortHandle); + +#else //WIN32 + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (port != address || irq != irqnum) { + + status = FastSetPortHardware( com, irq, port ); + + if (status < ASSUCCESS) { + Mono_Printf( "Unable to set Com port status %d\n", status ); + Mono_Printf( "Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } else { + Mono_Printf( "Changed Com port number %d to address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "No changes to Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "Com port number %d\n", com + 1 ); + } + + if (status != ASSUCCESS) { + Delete_Connection(); + return(false); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, baud, parity, wordlen, stopbits); + + if (Port->status != ASSUCCESS) { + Shutdown(); + return(false); + } + +// UseRtsCts( Port, 1 ); // use RTS CTS hardware flow control + + /*------------------------------------------------------------------------ + Init the Connection + ------------------------------------------------------------------------*/ + Connection->Init(Port); + //because Watcom is so stupid + flowcontrol = flowcontrol; + +#endif //WIN32 + + NumConnections = 1; + + return(true); +} + + +/*********************************************************************************************** + * NMC::Num_Connections -- returns NumConnections member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: NumConnections * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Num_Connections( void ) +{ + return(NumConnections); +} + + +/*********************************************************************************************** + * NMC::Delete_Connection -- removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Delete_Connection( void ) +{ + if (Connection) { + delete Connection; + Connection = NULL; + } + + if (RXBuf) { + delete [] RXBuf; + RXBuf = NULL; + } + + if (BuildBuf) { + delete [] BuildBuf; + BuildBuf = NULL; + } + + if (EchoBuf) { + delete [] EchoBuf; + EchoBuf = NULL; + } + + NumConnections = 0; + + return( true ); +} /* end of Delete_Connection */ + + + +/*********************************************************************************************** + * NMC::Init_Send_Queue -- Initialises the connections send queue * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:46AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Init_Send_Queue( void ) +{ + + /*------------------------------------------------------------------------ + Init the send queue + ------------------------------------------------------------------------*/ + if ( Connection ) { + Connection->Queue->Init_Send_Queue(); + } + + return(true); +} + + + +/*********************************************************************************************** + * NMC::Detect_Port -- Checks that the specified com port exists * + * * + * * + * * + * INPUT: ptr to SerialSettingsType * + * * + * OUTPUT: true if port is valid * + * * + * WARNINGS: Win32 version always returns true as win95 shouldnt allow us to open the * + * port if it doesnt exist or is in use by the mouse * + * * + * HISTORY: * + * 8/2/96 11:47AM ST : Documented / Win32 support * + *=============================================================================================*/ +DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + }else{ + SerialPort->Serial_Port_Close(); + } + + /* + ** Shift up the baud rate to sensible values + */ + int baud = settings->Baud; +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + + /* + ** Translate the port address into a usable device name + */ + char *device; + + switch ( settings->Port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = settings->ModemName; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (device, ModemRegistry->Get_Modem_Name() )){ + /* + ** Got a match. Break out leaving the registry info intact. + */ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + return (PORT_INVALID); + } + + /* + ** Open the com port + */ + HANDLE porthandle = SerialPort->Serial_Port_Open (device, baud, 0, 8, 1, settings->HardwareFlowControl); + + if (porthandle == INVALID_HANDLE_VALUE){ + return (PORT_INVALID); + } + + SerialPort->Serial_Port_Close(); + return (PORT_VALID); + + + +#else //WIN32 + + union REGS regs ; + int com; + int irqnum; + int address; + int status; + + + // shutdown previous port + + Shutdown(); + + if (settings->IRQ > 0xf) { + return( PORT_INVALID ); + } + + // + // check if the IRQ is the same as the mouse + // call mouse func to get mouse IRQ number + // + + regs.x.eax = 0x0024; + int386( 0x33, ®s, ®s ); + + // + // check for error + // + if (regs.w.ax != 0xffff) { + // + // is the mouse IRQ the same as that they selected + // + if (regs.h.cl == 0) { // PS/2 IRQ 0xc + if (settings->IRQ == 0xc) { + return( PORT_IRQ_INUSE ); + } + } else if (regs.h.cl == (unsigned char)(settings->IRQ)) { + return( PORT_IRQ_INUSE ); + } + } + + if (settings->IRQ < 2 // 0 timer, 1 keyboard + || settings->IRQ == 6 // floppy disk + || settings->IRQ == 8 // CMOS real-time clock + || settings->IRQ == 0xd // math coprocessor error + || settings->IRQ == 0xe) { // hard disk + return( PORT_IRQ_INUSE ); + } + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch ( settings->Port ) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (settings->Port != address || settings->IRQ != irqnum) { + status = FastSetPortHardware( com, settings->IRQ, settings->Port ); + } + } + + if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, settings->Baud, 'N', 8, 1); + + status = Port->status; + + PortClose(Port); + Port = NULL; + + if (status == ASIRQINUSE) { + return( PORT_IRQ_INUSE ); + } else if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + return( PORT_VALID ); +#endif //WIN32 +} + + +/*********************************************************************************************** + * NullModemClass::ShutDown -- Closes serial port and removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:43AM ST : Documented / Win32 support * + *=============================================================================================*/ +void NullModemClass::Shutdown ( void ) +{ + +#ifdef WIN32 + + if (PortHandle && SerialPort) { + SerialPort->Serial_Port_Close(); + delete SerialPort; + SerialPort = NULL; + PortHandle = NULL; + Delete_Connection(); + } + +#else //WIN32 + if (Port) { + PortClose(Port); + Port = NULL; + Delete_Connection(); + } + +#endif //WIN32 + +} /* end of Shutdown */ + + +/*************************************************************************** + * NullModemClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/07/1995 DRD : Created. * + *=========================================================================*/ +void NullModemClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + +} /* end of Set_Timing */ + + +/*************************************************************************** + * NullModemClass::Send_Message -- sends a message * + * * + * For clarity's sake, here's what happens to the buffer passed in: * + * - It gets passed to the Connection's Send_Packet() routine * + * - The CommHeaderType header gets tacked onto it * + * - The resulting buffer gets added to the Connection's Send Queue * + * - When Service() determines that it needs to send the data, it * + * copies the entire packet (CommHeaderType and all) into its local * + * SendBuf, adds the packet start ID, length, and CRC, then sends it out.* + * * + * The ack_req argument will almost always be '1' (the default). The only * + * reason to use 0 is if you don't know whether the other system is * + * ready or not, so you have to periodically send out a query packet, * + * and wait for a response. (Using the connection's built-in retry * + * system would just blast out useless data if the other system isn't * + * even there.) * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required; 0 = not * + * * + * OUTPUT: * + * 1 = OK; 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Send_Message (void *buf, int buflen, int ack_req) +{ + int rc; + + if (NumConnections == 0) { + return( false ); + } + + rc = Connection->Send_Packet(buf,buflen,ack_req); + if (!rc) + SendOverflows++; + + return(rc); + +} /* end of Send_Message */ + + +/*************************************************************************** + * NullModemClass::Get_Message -- polls the Queue for a message * + * * + * INPUT: * + * buf buffer to store message in * + * buflen ptr filled in with length of message * + * * + * OUTPUT: * + * 1 = message was received; 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Get_Message (void *buf, int *buflen) +{ + if (NumConnections == 0) { + return( false ); + } + return( Connection->Get_Packet( buf, buflen ) ); +} + + +/*************************************************************************** + * NullModemClass::Service -- main polling routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = connection has gone bad * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +int NullModemClass::Service (void) +{ + int pos; // current position in RXBuf + int i; // loop counter + unsigned short length; + SerialHeaderType *header; // decoded packet start, length + SerialCRCType *crc; // decoded packet CRC + char moredata = 0; + + if (NumConnections == 0) { + return( false ); + } + +#ifdef WIN32 + + RXCount += SerialPort->Read_From_Serial_Port((unsigned char*)(RXBuf + RXCount), int(RXSize - RXCount) ); + +#else //WIN32 + + int status; + static unsigned long last_time = 0; + + /*------------------------------------------------------------------------ + First, copy all the bytes we can from the Greenleaf RX buffer to our + own buffer. + (For Win95, only call GetLineStatus() 60 times a second at most. + Otherwise, we don't receive any packets!) + ------------------------------------------------------------------------*/ + if ( (TickCount - last_time) > 0) { + if ( GetLineStatus( Port ) ) { + Mono_Set_Cursor(0,0); + ClearLineStatus( Port ); + } + last_time = TickCount; + } + + status = SpaceUsedInRXBuffer( Port ); + + if ( status < ASSUCCESS) { +// Smart_Printf( "SpaceUsedInRXBuffer status = %d, port status = %d \n", status, Port->status ); + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + } else if (status > 0) { + status = ReadBuffer( Port, RXBuf + RXCount, RXSize - RXCount ); + +#if (CONN_DEBUG) + printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); +#endif +// Smart_Printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); + if (status < ASSUCCESS && status != ASBUFREMPTY) { +// Smart_Printf( "ReadBuffer ERRRRRRORRRRRR! \n" ); + } else { + moredata = 1; + } + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + RXCount += Port->count; + } + + +#endif //WIN32 + + // minimum packet size + + if ( RXCount < (PACKET_SERIAL_OVERHEAD_SIZE + 1) ) { + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Now scan the buffer for the start of a packet. + ------------------------------------------------------------------------*/ + pos = -1; + for (i = 0; i <= RXCount - sizeof( short ); i++) { + if ( *((unsigned short *)(RXBuf + i)) == PACKET_SERIAL_START ) { + pos = i; + break; + } + } + + /*------------------------------------------------------------------------ + No start code was found; throw away all bytes except the last few, and + return. + ------------------------------------------------------------------------*/ + if (pos==-1) { +// Smart_Printf( "No magic number found \n" ); + /*..................................................................... + move the remaining, un-checked bytes to the start of the buffer + .....................................................................*/ + memmove (RXBuf, RXBuf + i, sizeof( short ) - 1); + RXCount = sizeof( short ) - 1; + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Check to see if there are enough bytes for the header to be decoded + ------------------------------------------------------------------------*/ + if ( (RXCount - pos) < sizeof( SerialHeaderType ) ) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + A start code was found; check the packet's length & CRC + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *)(RXBuf + pos); + + /*------------------------------------------------------------------------ + If we lost a byte in the length, we may end up waiting a very long time + for the buffer to get to the right length; check the verify value to + make sure this didn't happen. + ------------------------------------------------------------------------*/ + if ( header->MagicNumber2 != PACKET_SERIAL_VERIFY ) { +// Smart_Printf( "Verify failed\n"); +// Hex_Dump_Data( (RXBuf + pos), PACKET_SERIAL_OVERHEAD_SIZE ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + length = header->Length; + + /*------------------------------------------------------------------------ + Special case: if the length comes out too long for us to process: + - Assume the packet is bad + - Throw away the bogus packet-start code + - Return; we'll search for another packet-start code next time. + ------------------------------------------------------------------------*/ + if (length > MaxLen) { +#if (CONN_DEBUG) + printf( "length too lonnng\n" ); +#endif +// Smart_Printf( "length too lonnng %d, max %d \n", length, MaxLen ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + If the entire packet isn't stored in our buffer, copy the remaining bytes + to the front of the buffer & return. + ------------------------------------------------------------------------*/ + if ( (pos + length + PACKET_SERIAL_OVERHEAD_SIZE) > RXCount) { + + if ( moredata ) { +// Smart_Printf( "waiting for more data %d, pos = %d \n", ((length + PACKET_SERIAL_OVERHEAD_SIZE) - (RXCount - pos)), pos ); + } + + if (pos) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + } + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + Now grab the CRC value in the packet, & compare it to the CRC value + computed from the actual data. If they don't match, throw away the bogus + start-code, move the rest to the front of the buffer, & return. + We'll continue parsing this data when we're called next time. + ------------------------------------------------------------------------*/ + crc = (SerialCRCType *)(RXBuf + pos + sizeof( SerialHeaderType ) + length); + if (NullModemConnClass::Compute_CRC(RXBuf + pos + + sizeof( SerialHeaderType ), length) != crc->SerialCRC) { + + CRCErrors++; + +#if (CONN_DEBUG) + printf( "CRC check failed\n" ); +#endif +// Smart_Printf( "CRC check failed for packet of length %d \n", length ); + +// if (length < 100) { +// Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +// } + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + +#if(0) +// Mono_Printf( "received %d bytes \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received tick=%d, Protocol=%d \n", TickCount, Session.CommProtocol ); + Hex_Dump_Data( (RXBuf + pos), (sizeof( SerialHeaderType) + length) ); + Debug_Modem_Dump = false; +#endif + + /*------------------------------------------------------------------------ + Give the new packet to the Connection to process. + ------------------------------------------------------------------------*/ + if (!Connection->Receive_Packet(RXBuf + pos + sizeof( SerialHeaderType ), length)) { + ReceiveOverflows++; +// Smart_Printf( "Received overflows %d \n", ReceiveOverflows ); + } + +#if(0) + else { +// Mono_Printf( "added packet \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received Packet \n" ); + Debug_Modem_Dump = false; + } +#endif + +#if (0) + Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +#endif + + /*------------------------------------------------------------------------ + Move all data past this packet to the front of the buffer. + ------------------------------------------------------------------------*/ + pos += (PACKET_SERIAL_OVERHEAD_SIZE + length); + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + + /*------------------------------------------------------------------------ + Now, service the connection's Queue's; this will handle ACK & Retries. + ------------------------------------------------------------------------*/ + return(Connection->Service()); + +} /* end of Service */ + + +/*************************************************************************** + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Send(void) +{ + if (Connection) + return( Connection->Queue->Num_Send() ); + else + return (0); + +} /* end of Num_Send */ + + +/*************************************************************************** + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Receive(void) +{ + if (Connection) + return( Connection->Queue->Num_Receive() ); + else + return (0); + +} /* end of Num_Receive */ + + +/*************************************************************************** + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +unsigned long NullModemClass::Response_Time(void) +{ + if (Connection) + return( Connection->Queue->Avg_Response_Time() ); + else + return (0); + +} /* end of Response_Time */ + + +/*************************************************************************** + * NullModemClass::Reset_Response_Time -- Resets response time computation * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Reset_Response_Time(void) +{ + if (Connection) + Connection->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * Oldest_Send -- Returns ptr to oldest unACK'd send buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void * NullModemClass::Oldest_Send(void) +{ + int i; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < Connection->Queue->Num_Send(); i++) { + send_entry = Connection->Queue->Get_Send(i); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + buf = send_entry->Buffer; + break; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * NullModemClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index (not used) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical start of the 1st name value * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Configure_Debug(int, int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + if (Connection) + Connection->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); +} + +#ifdef CHEAT_KEYS +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * INPUT: * + * index (not used) * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Mono_Debug_Print(int,int refresh) +{ + if (!Connection) + return; + + Connection->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (31,1); + Mono_Printf ("Serial Port Queues"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (20,3); + Mono_Printf ("CRC Errors:"); + + Mono_Set_Cursor (43,2); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,3); + Mono_Printf ("Receive Overflows:"); + } + + Mono_Set_Cursor (32,2); + Mono_Printf ("%d ", Connection->Queue->Avg_Response_Time()); + + Mono_Set_Cursor (32,3); + Mono_Printf ("%d ", CRCErrors); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,3); + Mono_Printf ("%d ", ReceiveOverflows); + + Mono_Set_Cursor (2,5); + Mono_Printf ("%d ", Num_Send()); + + Mono_Set_Cursor (41,5); + Mono_Printf ("%d ", Num_Receive()); + +} /* end of Mono_Debug_Print */ +#endif + + +/*************************************************************************** + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * -1 init string invalid * + * 0 no modem found * + * 1 modem found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +int NullModemClass::Detect_Modem( SerialSettingsType *settings, bool reconnect ) +{ + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + int status; + int error_count = 0; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Get resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + strcpy( buffer, Text_String( TXT_INITIALIZING_MODEM ) ); + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int lines = Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 40 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + if (lines == 1) { + Fancy_Text_Print(buffer, x + width/2, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_CENTER); + } else { + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + } + + Show_Mouse(); + + + HMWaitForOK( 0, NULL ); + + + /* + ** OK, lets not mess about any more. Just turn on echo, verbose, and result codes + ** before we even begin. At least this way when we get an error later on we have already + ** removed all the steps we use to try and recover. + ** The timeouts need to be quite small in case the modem is turned off. + */ + + /* + ** Turn on result codes. + */ + Send_Modem_Command ( "ATQ0", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Make result codes verbose. + */ + Send_Modem_Command ( "ATV1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Turn on echo. + */ + Send_Modem_Command ( "ATE1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + ModemVerboseOn = true; + ModemEchoOn = true; + + + + + + /* + ** Try sending a plain old AT command to the modem. Now that we have theoretically + ** turned on verbose result codes we should get an 'OK' back. + ** + */ + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 2 ); + + if (status < ASSUCCESS) { +#ifdef FIXIT_MULTI_SAVE + return(false); +#else + if (WWMessageBox().Process(TXT_ERROR_NO_RESP, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; +#endif + } + + /* + ** Send the user supplied modem init string + */ + if (settings->InitStringIndex == -1) { + status = Send_Modem_Command( "", '\r', buffer, 81, 300, 1 ); + } else { + /* + ** Split up the init string into seperate strings if it contains one or more '|' characters. + ** This character acts as a carriage return/pause. + */ + char *istr = new char [2 + strlen( Session.InitStrings [settings->InitStringIndex] )]; + char *tokenptr; + strcpy (istr, Session.InitStrings [settings->InitStringIndex] ); + + /* + ** Tokenise the string and send it in chunks + */ + tokenptr = strtok ( istr, "|" ); + while ( tokenptr ) { + + status = Send_Modem_Command( tokenptr, '\r', buffer, 81, 3000, 1 ); + /* + ** Handle error case. + */ + if (status < ASSUCCESS) { + if (WWMessageBox().Process(TXT_ERROR_NO_INIT, TXT_IGNORE, TXT_CANCEL)) { + delete istr; + return( false ); + } +#ifdef WIN32 + error_count++; +#endif //WIN32 + break; + } + + tokenptr = strtok ( NULL, "|"); + + } + } + +#ifdef WIN32 + + if (settings->Port == 1 && ModemRegistry) { + /* + ** Send the init strings from the registry if available + */ + char send_string[256] = {"AT"}; + + /* + ** Send the init string for hardware flow control + */ + if (settings->HardwareFlowControl){ + if (ModemRegistry->Get_Modem_Hardware_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Hardware_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, 300, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + /* + ** Send the init string for no flow control + */ + if (ModemRegistry->Get_Modem_No_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_No_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for data compresseion + */ + if (settings->Compression){ + + if (ModemRegistry->Get_Modem_Compression_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + + if (ModemRegistry->Get_Modem_Compression_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for error correction + */ + if (settings->ErrorCorrection){ + + if (ModemRegistry->Get_Modem_Error_Correction_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + if (ModemRegistry->Get_Modem_Error_Correction_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + } + +#endif //WIN32 + + + /* + ** We require that auto-answer be disabled so turn it off now. + */ + status = Send_Modem_Command( "ATS0=0", '\r', buffer, 81, DEFAULT_TIMEOUT, INIT_COMMAND_RETRIES ); + if (status != MODEM_CMD_OK) { + if (WWMessageBox().Process(TXT_ERROR_NO_DISABLE, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; + } + + /* + ** If we had an unreasonable number of ignored errors then return failure + */ + if (error_count >= 3) { + WWMessageBox().Process(TXT_ERROR_TOO_MANY, TXT_OK); + return (false); + } + + return( true ); +} + + +/*************************************************************************** + * NullModemClass::Dial_Modem -- dials a number passed * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Dial_Modem( char *string, DialMethodType method, bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( buffer, Text_String( TXT_MODEM_CONNERR_REDIALING ) ); + } else { + strcpy( buffer, Text_String( TXT_DIALING ) ); + } + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + int text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 *RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + Show_Mouse(); + + /* + ** Start waiting for connection response + */ +#ifdef WIN32 + SerialPort->Set_Modem_Dial_Type((WinCommDialMethodType) method); + /* + ** Clear out any old modem results that might be hanging around + */ + SerialPort->Get_Modem_Result(60, buffer, 81); + /* + ** Dial that sucker + */ + SerialPort->Dial_Modem(string); + +#else //WIN32 + HMSetDialingMethod( Port, (int)method ); + status = HMDial( Port, string ); + +#endif //WIN32 + + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = ModemWaitCarrier; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + Commands->Draw_All(); + } + + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + /*..................................................................... + Process input + .....................................................................*/ + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } else if ( strncmp( buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + process = false; + } + } + + if (delay <= 0) { + process = false; + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Dial_Modem */ + + +/*************************************************************************** + * NullModemClass::Answer_Modem -- waits for call and answers * + * * + * INPUT: * + * reconnect whether this is to reconnect * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Answer_Modem( bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Redraw values: in order from "top" to "bottom" layer of the dialog + ------------------------------------------------------------------------*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + bool ring = false; + + int x,y,width,height; // dialog dimensions + int text_width; + char text_buffer[80*3]; + char comm_buffer[80*3]; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( text_buffer, Text_String( TXT_MODEM_CONNERR_WAITING ) ); + } else { + strcpy( text_buffer, Text_String( TXT_WAITING_FOR_CALL ) ); + } + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + + Input = KN_NONE; + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack() and Input(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = 60000; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } +#endif //WIN32 + + /*..................................................................... + Refresh display if needed + .....................................................................*/ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /*............................................................... + Refresh the backdrop + ...............................................................*/ + if ( !reconnect ) { + Load_Title_Page(true); + } + + /*............................................................... + Draw the background + ...............................................................*/ + Dialog_Box(x, y, width, height); + + /*............................................................... + Draw the labels + ...............................................................*/ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 * RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, comm_buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, comm_buffer, 81 ); +#endif //WIN32 + + + /*..................................................................... + Process input + .....................................................................*/ + if (!Input) Input = Commands->Input(); + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( comm_buffer, "RING", 4 ) == 0 ) { + + strcpy( text_buffer, Text_String( TXT_ANSWERING ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATA\r", strlen("ATA\r")); +#else //WIN32 + PortKillTime( Port, 100 ); + HMWaitForOK( 0, NULL ); + status = HMAnswer( Port ); +#endif //WIN32 + + ring = true; + delay = ModemWaitCarrier; + display = REDRAW_ALL; + } else if ( strncmp( comm_buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, comm_buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( comm_buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( comm_buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( comm_buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_ERROR, TXT_OK); + process = false; + } + } + +#ifdef WIN32 + + if (delay <= 0) { + if (ring) { + if (SerialPort->Get_Modem_Status() & CD_SET) { + sprintf(ModemRXString, "%s", "Connected"); + dialstatus = DIAL_CONNECTED; + } else { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_TIMEOUT, TXT_OK); + } + process = false; + } else { + delay = 60000; + } + } +#else //WIN32 + + if (delay <= 0) { + if (ring) { + process = false; + } else { + delay = 60000; + } + } +#endif //WIN32 + + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Answer_Modem */ + + +/*************************************************************************** + * NullModemClass::Hangup_Modem -- hangs up the modem * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * status successful or not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 : Added Win32 support * + *=========================================================================*/ +bool NullModemClass::Hangup_Modem( void ) +{ + int status; + int delay; + char buffer[81]; + char escape[4]; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + /* + ** Toggle DTR low then high + */ +#ifdef WIN32 + SerialPort->Set_Serial_DTR(FALSE); + Delay(3200/60); + SerialPort->Set_Serial_DTR(TRUE); +#else //WIN32 + + SetDtr( Port, 0 ); + PortKillTime( Port, 3200 ); + SetDtr( Port, 1 ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + } + + /* + ** Send modem break commmand + */ + escape[0] = ModemEscapeCode; + escape[1] = ModemEscapeCode; + escape[2] = ModemEscapeCode; + escape[3] = 0; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char*)escape, 3); +#else //WIN32 + status = HMSendStringNoWait( Port, escape, -1 ); +#endif //WIN32 + + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + if ( strncmp( buffer, "OK", 2 ) == 0 ) { + break; + } + } + + /* + ** Send the hangup command + */ + status = Send_Modem_Command( "ATH", '\r', buffer, 81, ModemHangupDelay, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + Session.ModemService = true; + return( false ); + } + + /* + ** Send spurious ATZ command for no apparent reason + */ + status = Send_Modem_Command( "ATZ", '\r', buffer, 81, 5000, 1 ); + + if (status != MODEM_CMD_OK) { + Session.ModemService = true; + return( false ); + } + + Session.ModemService = true; + return( true ); + +} /* end of Hangup_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Modem_Echo -- Sets the echo callback function pointer * + * * + * * + * * + * INPUT: Ptr to callback function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:48PM ST : Documented and added WIn32 support * + *=============================================================================================*/ +void NullModemClass::Setup_Modem_Echo( void ( *func )( char c) ) +{ +#ifdef WIN32 + SerialPort->Set_Echo_Function(func); +#else //WIN32 + HMSetUpEchoRoutine( func ); +#endif //WIN32 + +} /* end of Setup_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Remove_Modem_Echo -- Set the echo function callback pointer to null * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:50PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Modem_Echo( void ) +{ +// Smart_Printf( "Remove Echo modem code\n" ); + HMSetUpEchoRoutine( NULL ); + +} /* end of Remove_Modem_Echo */ + + + +/*********************************************************************************************** + * NMC::Print_EchoBuf -- Print out the contents of the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Print_EchoBuf( void ) +{ + for (int i = 0; i < strlen(NullModem.EchoBuf); i++) { + if (NullModem.EchoBuf[i] == '\r') { + NullModem.EchoBuf[i] = 1; + } else { + if (NullModem.EchoBuf[i] == '\n') { + NullModem.EchoBuf[i] = 2; + } + } + } +// Smart_Printf( "Echo buffer length %d (%s)\n", NullModem.EchoCount, NullModem.EchoBuf ); +} + + + +/*********************************************************************************************** + * NMC::Reset_EchoBuf -- Empties the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Reset_EchoBuf( void ) +{ + *EchoBuf = 0; + EchoCount = 0; +} + + +/*********************************************************************************************** + * NMC::Abort_Modem -- Checks for user input so that modem operations can be aborted * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ASUSERABORT if abort key pressed. ASSUCESS otherwise. * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:52PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Abort_Modem( PORT * ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + Input = Commands->Input(); + + switch ( Input ) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + return( ASUSERABORT ); + } + + return( ASSUCCESS ); + +} /* end of Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Abort_Modem -- sets the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 2:59PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Setup_Abort_Modem( void ) +{ + +#ifdef WIN32 + + SerialPort->Set_Abort_Function((int(*)(void))Abort_Modem); + +#else //WIN32 + /* + ** save off original abort modem function to later restore + */ + OrigAbortModemFunc = _AbortModemFunctionPtr; + HMSetUpAbortKey( ESC ); + SetAbortModemFunctionPtr( Abort_Modem ); +#endif //WIN32 + +} /* end of Setup_Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Remove_Abort_Modem -- Removes the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:01PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Abort_Modem( void ) +{ + +#ifdef WIN32 + SerialPort->Set_Abort_Function(NULL); + +#else //WIN32 + + HMSetUpAbortKey( NOKEY ); + + /* + ** Restore the original abort modem function + */ + _AbortModemFunctionPtr = OrigAbortModemFunc; +#endif //WIN32 + +} /* end of Remove_Abort_Modem */ + + + +/*********************************************************************************************** + * NMC::Change_IRQ_Priority -- Increases the priority of the serial interrupt * + * * + * * + * * + * INPUT: Interrupt request number * + * * + * OUTPUT: ASSUCCESS if changed * + * * + * WARNINGS: The Win32 version of this function does nothing. * + * Priorities are controlled by windoze * + * * + * HISTORY: * + * 8/2/96 3:03PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Change_IRQ_Priority( int irq ) +{ +#ifdef WIN32 + + irq = irq; + return ( ASSUCCESS ); + +#else //WIN32 + + if (irq != OldIRQPri) { + OldIRQPri = irq; + return( Change8259Priority( irq ) ); + } else { + return( ASSUCCESS ); + } + +#endif //WIN32 + +} /* end of Change_IRQ_Priority */ + + + +/*********************************************************************************************** + * NMC::Get_Modem_Status -- returns status of modem control bits * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:06PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Get_Modem_Status( void ) +{ + int modemstatus; + int status; + char buffer[81]; + +#ifdef WIN32 + modemstatus = SerialPort->Get_Modem_Status(); +#else //WIN32 + modemstatus = GetModemStatus( Port ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + modemstatus &= (~CD_SET); + } + + return( modemstatus ); + +} /* end of Get_Modem_Status */ + + +/*********************************************************************************************** + * NMC::Send_Modem_Command -- Sends an 'AT' command to the modem and gets the response * + * * + * * + * * + * INPUT: command to send to modem. e.g. 'ATZ' * + * terminator byte for command string * + * buffer to put modem response into * + * length of above buffer * + * delay to wait for response * + * number of times to retry when modem doesnt respond * + * * + * OUTPUT: input delay less the time it took the modem to respond * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:09PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ) +{ + +#ifdef WIN32 + return (SerialPort->Send_Command_To_Modem(command, terminator, buffer, buflen, delay, retries)); +#else //WIN32 + + int status, tmpdelay, retry; + char tempbuf[81]; + + + *buffer = 0; + + for ( retry = 0; retry < retries; retry++ ) { + + PortKillTime( Port, 100 ); + + status = HMSendStringNoWait( Port, command, terminator ); + + + if (status < ASSUCCESS) { + return( status ); + } + + tmpdelay = delay; + while ( tmpdelay > 0 ) { + + tmpdelay = HMInputLine( Port, tmpdelay, tempbuf, 81 ); + + if ( strcmp( tempbuf, "OK" ) == 0 ) { + return( MODEM_CMD_OK ); + } else if ( strcmp( tempbuf, "0" ) == 0 ) { + return( MODEM_CMD_0 ); + } else if ( strcmp( tempbuf, "ERROR" ) == 0 ) { + return( MODEM_CMD_ERROR ); + } else if ( tempbuf[0] != 0 ) { + strncpy( buffer, tempbuf, buflen ); + } + } + + PortKillTime( Port, 100 ); + + /* + ** Send and extra return to help clear out any problems with the modem (if any). + */ + HMWaitForOK( 300, NULL ); + status = HMSendString( Port, "" ); + HMWaitForOK( 0, NULL ); + } + + return( tmpdelay ); + +#endif //WIN32 +} + + +/*********************************************************************************************** + * NMC::Verify_And_Convert_To_Int -- converts a text string of numbers to an int * + * * + * * + * * + * INPUT: ptr to buffer * + * * + * OUTPUT: value of text number in buffer * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:13PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Verify_And_Convert_To_Int( char *buffer ) +{ + int value = 0; + int len = strlen(buffer); + + + for (int i = 0; i < len; i++) { + if ( !isdigit( *(buffer + i) ) ) { + value = -1; + break; + } + } + + if (value == 0) { + value = atoi( buffer ); + } + + return( value ); + +} /* end of Verify_And_Convert_To_Int */ + +/*************************** end of nullmgr.cpp ****************************/ + diff --git a/CODE/NULLMGR.H b/CODE/NULLMGR.H new file mode 100644 index 0000000..d034a3a --- /dev/null +++ b/CODE/NULLMGR.H @@ -0,0 +1,226 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/NULLMGR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for a NULL-Modem connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLMODEM_H +#define NULLMODEM_H + + +/* +********************************* Includes ********************************** +*/ +#include "nullconn.h" +#include "connmgr.h" +#include "commlib.h" + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + char *BuildBuf; + int MaxLen; + + char *EchoBuf; + int EchoSize; + int EchoCount; + + int OldIRQPri; + + int ModemVerboseOn; + int ModemEchoOn; + int ModemWaitCarrier; + int ModemCarrierDetect; + int ModemCarrierLoss; + int ModemHangupDelay; + int ModemGuardTime; + char ModemEscapeCode; + + static void (*OrigAbortModemFunc)(int); + static KeyNumType Input; + static GadgetClass *Commands; // button list + + /* + ** Constructor/destructor. + */ + NullModemClass (int numsend, int numreceive, int maxlen, unsigned short magicnum); + virtual ~NullModemClass (); + + /* + ** This is the main initialization routine. + */ + int Init( int port, int irq, char *dev_name, int baud, char parity, int wordlength, int stopbits, int flowcontrol ); + int Delete_Connection( void ); + virtual int Num_Connections(void); + virtual int Connection_ID(int ) {return (0);} + virtual int Connection_Index(int ) {return (0);} + int Init_Send_Queue( void ); + void Shutdown( void ); + + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + /* + ** This is how the application sends & receives messages. + */ + int Send_Message (void *buf, int buflen, int ack_req = 1); + int Get_Message (void *buf, int *buflen); + + /* + ** These are for compatibility + */ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int = CONNECTION_NONE) + {return (Send_Message(buf,buflen,ack_req));} + virtual int Get_Private_Message (void *buf, int *buflen, int *) + {return (Get_Message(buf,buflen));} + + /* + ** The main polling routine; should be called as often as possible. + */ + virtual int Service (void); + + /* + ** Queue utility routines. The application can determine how many + ** messages are in the send/receive queues, and the queue's average + ** response time (in clock ticks). + */ + int Num_Send(void); + int Num_Receive(void); + virtual unsigned long Response_Time(void); + virtual void Reset_Response_Time(void); + void * Oldest_Send(void); + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh = 0); +#endif + + /* + ** These are for compatibility + */ + virtual int Global_Num_Send(void) {return (Num_Send());} + virtual int Global_Num_Receive(void) {return (Num_Receive());} + virtual int Private_Num_Send(int = CONNECTION_NONE) + {return (Num_Send());} + virtual int Private_Num_Receive(int = CONNECTION_NONE) + {return (Num_Receive());} + + DetectPortType Detect_Port( SerialSettingsType *settings ); + int Detect_Modem( SerialSettingsType *settings, int reconnect = 0 ); + DialStatusType Dial_Modem(char *string, DialMethodType method, int reconnect = 0); + DialStatusType Answer_Modem(int reconnect = 0); + int Hangup_Modem(void); + void Setup_Modem_Echo(void (*func)(char c)); + void Remove_Modem_Echo(void); + void Print_EchoBuf(void); + void Reset_EchoBuf(void); + static int Abort_Modem(PORT *); + void Setup_Abort_Modem(void); + void Remove_Abort_Modem(void); + + int Change_IRQ_Priority(int irq); + int Get_Modem_Status(void); + int Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ); + int Verify_And_Convert_To_Int( char *buffer ); + + /* + ** Private Interface. + */ + private: + + /* + ** This is a pointer to the NULL-Modem Connection object. + */ + NullModemConnClass *Connection; + int NumConnections; // # connection objects in use + +#ifdef WIN32 + /* + ** This is the Win95 port handle + */ + HANDLE PortHandle; +#else //WIN32 + /* + ** This is the Greenleaf port handle. + */ + PORT *Port; +#endif //WIN32 + + int NumSend; + int NumReceive; + unsigned short MagicNum; + + /* + ** This is the staging buffer for parsing incoming packets. + ** RXSize is the allocated size of the RX buffer. + ** RXCount is the # of characters we currently have in our buffer. + */ + char *RXBuf; + int RXSize; + int RXCount; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /* + ** Various Statistics + */ + int SendOverflows; + int ReceiveOverflows; + int CRCErrors; +}; + +#endif + +/*************************** end of nullmgr.h ******************************/ diff --git a/CODE/NUMBER.CPP b/CODE/NUMBER.CPP new file mode 100644 index 0000000..86be486 --- /dev/null +++ b/CODE/NUMBER.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + + + + + + +/* +** Method to raise a number by an arbitrary whole exponent and then +** modulo the result by another number. +*/ +unsigned long Power_Mod(unsigned long root, unsigned long exponent, unsigned long mod) +{ + unsigned long s = 1; + unsigned long t = root; + unsigned long u = exponent; + + while (u) { + if (u & 1) { + s = (s*t) % mod; + } + u >>= 1; + t = (t*t) % mod; + } + return(s); +} + + +/* +** This routine finds the greatest common divisor common +** to two specified numbers. +*/ +int Greatest_Common_Divisor(int x, int y) +{ + + if (x < 0) { + x = -x; + } + if (y < 0) { + y = -y; + } + if (x + y == 0) { + return(0); // This is an error condition. + } + + int greatest = y; + while (x > 0) { + greatest = x; + x = y % x; + y = greatest; + } + return(g); +} + + +/* +** Computes the greatest common divisor for a vector +** of integers. +*/ +int Grestest_Common_Divisor(int count, int * data) +{ + if (count < 1) { + return(0); + } + + greatest = data[0]; + for (int i = 1; i < count; i++) { + greatest = Greatest_Common_Divisor(greatest, data[i]); + if (greatest == 1) { + return(1); + } + } + return(greatest); +} \ No newline at end of file diff --git a/CODE/OBJECT.CPP b/CODE/OBJECT.CPP new file mode 100644 index 0000000..21945b4 --- /dev/null +++ b/CODE/OBJECT.CPP @@ -0,0 +1,2239 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OBJECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OBJECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ObjectClass::AI -- Handles generic object AI processing. * + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * ObjectClass::Detach -- Detach the specified target from this object. * + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * ObjectClass::Init -- Initializes the basic object system. * + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * ObjectClass::Mark -- Handles basic marking logic. * + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * ObjectClass::Name -- Fetches the identification name of this object. * + * ObjectClass::ObjectClass -- Default constructor for objects. * + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * ObjectClass::Passive_Click_With -- Right mouse button click process. * + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * ObjectClass::Render -- Displays the object onto the map. * + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * ObjectClass::Repair -- Handles object repair control. * + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * ObjectClass::Scatter -- Tries to scatter this object. * + * ObjectClass::Select -- Try to make this object the "selected" object. * + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * ObjectClass::Take_Damage -- Applies damage to the object. * + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * ObjectClass::Value -- Fetches the target value of this object. * + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object* + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** Selected objects have a special marking box around them. This is the shapes that are +** used for this purpose. +*/ +void const * ObjectTypeClass::SelectShapes = 0; + +void const * ObjectTypeClass::PipShapes = 0; + + +/*********************************************************************************************** + * ObjectClass::ObjectClass -- Default constructor for objects. * + * * + * This is the default constructor for objects. It is called as an inherent part of the * + * construction process for all the normal game objects instantiated. It serves merely to * + * initialize the object values to a common (default) state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Objects always start in a state of limbo. They must be Unlimbo()ed before they * + * can be used. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass::ObjectClass(RTTIType rtti, int id) : + AbstractClass(rtti, id), + IsDown(false), + IsToDamage(false), + IsToDisplay(false), + IsInLimbo(true), + IsSelected(false), + IsAnimAttached(false), + IsFalling(false), + Riser(0), + Next(0), + Trigger(NULL), + Strength(255) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * * + * This routine will return with a pointer to the image data that should be used when * + * this object is drawn. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * ObjectClass::Get_Image_Data(void) const +{ + return(Class_Of().Get_Image_Data()); +} + + +/*********************************************************************************************** + * ObjectClass::Name -- Fetches the identification name of this object. * + * * + * This routine will return a pointer to the identifier name for this object. This name * + * is usually short and is used in the INI files to identify this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text identifier name of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +char const * ObjectClass::Name(void) const +{ + return(Class_Of().Name()); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * * + * Ths exit coordinate is the location that a piggy back or newly produced object will * + * appear at when it exits this object. Transports and factory buildings will utilize this * + * routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that an object will appear at when exiting this * + * object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Exit_Coord(void) const +{ + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::AI -- Handles generic object AI processing. * + * * + * This routine is used to handle the AI processing that occurs for all object types. * + * Typically, this isn't much, but there is the concept of falling that all objects can * + * be subjected to (e.g., grenades). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::AI(void) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Falling logic is handled here. + */ + if (IsFalling) { + LayerType layer = In_Which_Layer(); + + Height += Riser; + if (Height <= 0) { + Height = 0; + IsFalling = false; + Per_Cell_Process(PCP_END); + + Shorten_Attached_Anims(this); + } + if (IsAnimAttached) { + Riser -= 1; + Riser = max(Riser, -3); + } else { + Riser -= Rule.Gravity; +// Riser -= GRAVITY; + Riser = max(Riser, -100); + } + + if (layer != In_Which_Layer()) { + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + if (Class_Of().IsFootprint) { + if (In_Which_Layer() == LAYER_GROUND) { + Map.Place_Down(Coord_Cell(Center_Coord()), this); + } else { + Map.Pick_Up(Coord_Cell(Center_Coord()), this); + } + } + } + } +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * * + * This routine will return that action that this object could perform if the mouse were * + * clicked over the object specified. * + * * + * INPUT: object -- Pointer to the object to check this object against when determining * + * the action to perform. * + * * + * OUTPUT: It returns that action that will be performed if the mouse were clicked over the * + * object. Since non-derived objects cannot do anything, and cannot even be * + * instantiated, this routine will always return ACTION_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(ObjectClass const *) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * * + * This routine is called when information on a potential action if the mouse were clicked * + * on the cell specified. This routine merely serves as a virtual placeholder so that * + * object types that can actually perform some action will override this routine to provide * + * true functionality. * + * * + * INPUT: cell -- The cell that the mouse is over and might be clicked on. * + * * + * OUTPUT: Returns with the action that this object would try to perform if the mouse were * + * clicked. Since objects at this level have no ability to do anything, this routine * + * will always returns ACTION_NONE unless it is overridden. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(CELL) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * * + * The default layer for object location is the LAYER_GROUND. Aircraft will override this * + * routine and make adjustments as necessary according to the aircraft's altitude. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this object is located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +LayerType ObjectClass::In_Which_Layer(void) const +{ + assert(this != 0); + assert(IsActive); + + if (Height < (FLIGHT_LEVEL - (FLIGHT_LEVEL/3))) { + return(LAYER_GROUND); + } + return(LAYER_TOP); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * * + * This routine will return the ownable bits for this object. Objects at this level can't * + * really be owned by anyone, but return the full spectrum of legality just to be safe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable flags (as a combined bitfield) for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Get_Ownable(void) const +{ + assert(this != 0); + assert(IsActive); + + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * * + * Most objects cannot be repaired. This routine defaults to returning "false", but is * + * overridden by derived functions defined by object types that can support repair. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be repaired? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Repair(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * * + * This routine is used to determine if this object can be sold. Most objects cannot be * + * but for those objects that can, this routine will be overridden as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be sold back? Typically, the answer is no. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Demolish(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * * + * This routine is used to determine if attacking is an option under player control with * + * respect to this unit. This routine will be overridden as necessary for those objects * + * that have the ability to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given an attack order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Fire(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * * + * This routine is used to determine if the player has the ability to command this object * + * with a movement mission. This routine will be overridden as necessary to support this * + * ability. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given a movement mission by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Move(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * * + * When the coordinate to use when firing at this object is needed, this routine will * + * provide it. Normal objects just use the center of the object for this, but there are * + * some more sophisticated objects that are not fired upon the center. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire at if this object is a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Target_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Center_Coord())); +// return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * * + * This routine will return the center coordinate for the object. The center coordinate is * + * typically the coordinate recorded in the object structure. Exceptions to this include * + * the trees and other terrain elements. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate that is considered the center point of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Center_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * * + * This routine will return the coordinate to base the drawing of this object's graphic * + * at. This is adjusted according to the nature of the graphic associated with this * + * object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate to draw the graphic of this object at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Render_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * * + * This routine returns the coordinate that a potential docking object should home in on. * + * Typically, this the center of the object, but in certain cases it is adjusted off center * + * according to the object type. An example of this would be the airfield. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that a docking object should head for. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Docking_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * * + * This routine will return the value to be used for object sorting. The sorting ensures * + * that the object are rendered from a top to bottom order. Certain object use a sorting * + * value different from their center coordinate. This is true if the object "touches the * + * ground" at a point that is different from the object's center point. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value to use as the Y sorting value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * * + * For those objects that fire, the coordinate that the projectile it launches will appear * + * at the location specified by the return value from this function. * + * * + * INPUT: which -- Which weapon to consider when determining fire coordinate? * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the coordinate that a launched projectile will appear at if this * + * object were to fire the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Fire_Coord(int ) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * * + * This routine is called when this object is killed. If the source of the death is known, * + * then a pointer to the responsible object is provided as a parameter. * + * * + * INPUT: source -- Pointer to the cause of this unit's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Record_The_Kill(TechnoClass * ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * * + * When an object is cloaked, there are several conditions that would cause it to shimmer * + * and thus reveal itself. When such a condition arrises, this function is called. If the * + * object is cloaked, then it will shimmer. At this derivation level, cloaking is * + * undefined. Objects that can cloak will override this function as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Do_Shimmer(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * * + * This routine is called, typically, by a transport building type that requires an object * + * to leave it. This routine will place the object at a suitable location or return * + * a value indicating why not. * + * * + * INPUT: object -- Pointer to the object that wishes to leave this object. * + * * + * OUTPUT: Returns the success value of the attempt: * + * 0: Object could not be placed -- ever * + * 1: Object placement is temporarily delayed -- try again later. * + * 2: Object placement proceeded normally * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Exit_Object(TechnoClass *) +{ + assert(this != 0); + assert(IsActive); + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * * + * This routine is called when the object becomes hidden from the player. It can result in * + * lost targeting and tracking abilities with respect to the hidden object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Hidden(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * * + * This routine is called when the object needs to look around the terrain. For player * + * owned objects, the terrain is revealed. For non-player objects, not effect occurs. * + * * + * INPUT: incremental -- If true, then the looking algorithm will only examine the edges * + * of the sight range. This is more efficient and work well if the * + * object has only moved one cell since the last time it has performed * + * the look operation. * + * * + * OUTPUT: none * + * * + * WARNINGS: This can be a time consuming operation. Call only when necessary. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Look(bool ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * * + * This routine is called when this object is selected and the mouse was clicked on the * + * tactical map. An action is required from the object. The object that the mouse was * + * over and the tentative action to perform are provided as parameters. * + * * + * INPUT: action -- The requested action to perform with the object specified. * + * * + * object -- The object that the action should be performed on. This object is * + * what the mouse was over when the click occurred. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , ObjectClass *) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * * + * This routine will dispatch the action requested upon the cell specified. It is called * + * when the mouse is clicked over a cell while this object is selected. * + * * + * INPUT: action -- The action to perform. * + * * + * cell -- The location (cell) to perform this action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , CELL ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * * + * This routine is called when this object is the target of some player click action. * + * For more sophisticated object, this will trigger the object to begin flashing a few * + * times. At this level, no action is performed. * + * * + * INPUT: flashes -- The requested number of times to flash this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Clicked_As_Target(int) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * * + * This routine will determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to check to see if it is within weapon range. * + * * + * which -- The weapon to check against. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: bool; Is the specified coordinate within weapon range for the weapon type * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::In_Range(COORDINATE , int) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * * + * This routine will return the weapon range according to the type of weapon specified. * + * * + * INPUT: which -- The weapon to fetch the range from. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the range (in leptons) of the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Weapon_Range(int) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Scatter -- Tries to scatter this object. * + * * + * This routine is used when the object should scatter from its current location. It * + * applies to units that have the ability to move. * + * * + * INPUT: coord -- The source of the threat that is causing the scatter. * + * * + * forced-- Whether this scatter attempt is serious and scattering should occur * + * regardless of what is doing now. * + * * + * OUTPUT: none * + * * + * WARNINGS: This may or may not cause the object to scatter. It is merely a request to the * + * object that it would be good if it were to scatter. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Scatter(COORDINATE , bool, bool) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * * + * This routine is called when an animation is attached to this object. It might be a * + * fire animation (hence the name), but it might also be smoke or any other animation * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object caught on fire by this routine? Actually, this is really * + * the answer to this question; "Is this animation attaching to this object * + * that doesn't already have an animation attached?" * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Catch_Fire(void) +{ + assert(this != 0); + assert(IsActive); + + return false; +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * * + * This routine is called if there is an attached animation on this object and that * + * animation has finished. Typically, this is necessary for when trees are on fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Fire_Out(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Value -- Fetches the target value of this object. * + * * + * This routine will return the target value of this object. The higher the number, the * + * better the object will be as a target. This routine is called when searching for * + * targets. Generic objects have no target potential, and this routine returns zero to * + * reflect that. Other object types will override this routine to return the appropriate * + * target value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value of this object as a target. Higher values mean better * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Value(void) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * * + * Generic objects don't have a mission, so this routine will just return MISSION_NONE. * + * However, techno objects do have a mission and this routine is overloaded to handle * + * those objects in order to return the correct mission value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current mission being followed by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +MissionType ObjectClass::Get_Mission(void) const +{ + assert(this != 0); + assert(IsActive); + + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::Repair -- Handles object repair control. * + * * + * This routine will control object repair mode. At the object level, no repair is * + * possible, so it is expected that any object that can repair will override this function * + * as necessary. * + * * + * INPUT: control -- The repair control parameter. * + * 0 = turn repair off * + * 1 = turn repair on * + * -1 = toggle repair state * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Repair(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * * + * This routine is called to sell back the object. Override this routine for the more * + * sophisticated objects that can actually be sold back. Normal objects can't be sold and * + * this routine does nothing as a consequence. * + * * + * INPUT: control -- How to control the sell state of this object. * + * 0 = stop selling. * + * 1 = start selling. * + * -1 = toggle selling state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Sell_Back(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * * + * This routine will instantly move the object one cell in the specified direction. It * + * moves the object by force. This is typically ONLY used by the scenario editor * + * process. * + * * + * INPUT: facing -- The direction to move the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: Naturally, this can cause illegal placement situations -- use with caution. * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Move(FacingType facing) +{ + assert(this != 0); + assert(IsActive); + + COORDINATE coord; + + Mark(MARK_UP); + coord = Adjacent_Cell(Coord, facing); + if (Can_Enter_Cell(Coord_Cell(coord)) == MOVE_OK) { + Coord = coord; + } + Mark(MARK_DOWN); +} + + +/*********************************************************************************************** + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * * + * This routine brings a currently selected object into an unselected state. This is * + * needed when another object becomes selected as well as if the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect(void) +{ + assert(this != 0); + assert(IsActive); + + if (IsSelected) { + CurrentObject.Delete(this); + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + IsSelected = false; + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + } +} + + +/*********************************************************************************************** + * ObjectClass::Select -- Try to make this object the "selected" object. * + * * + * This routine is used to make this object into the one that is "selected". A selected * + * object usually displays a floating bar graph and is available to be given orders from * + * the player's I/O. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + * 06/12/1995 JLB : Cannot select a loaner object. * + * 07/23/1995 JLB : Adds to head or tail depending on leader type flag. * + *=============================================================================================*/ +bool ObjectClass::Select(void) +{ + assert(this != 0); + assert(IsActive); + + if (!Debug_Map && (IsSelected || !Class_Of().IsSelectable)) { + return(false); + } + + if (!Debug_Map && Can_Player_Move() && Is_Techno() && ((TechnoClass *)this)->IsALoaner) { + return(false); + } + + /* + ** Don't allow selection if the object is still in the air. + */ + if (Height > 0 && (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL || What_Am_I() == RTTI_INFANTRY)) { + return(false); + } + + /* + ** Don't allow selection of object when in building placement mode. + */ + if (Map.PendingObject) { + return(false); + } + + /* + ** If selecting an object of a different house than the player's, make sure that + ** the entire selection list is cleared. + */ + if (CurrentObject.Count() > 0) { + HouseClass * tryhptr = HouseClass::As_Pointer(Owner()); + HouseClass * oldhptr = HouseClass::As_Pointer(CurrentObject[0]->Owner()); +// if (Owner() != CurrentObject[0]->Owner() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + if (oldhptr->IsPlayerControl != tryhptr->IsPlayerControl || !oldhptr->IsPlayerControl) { + Unselect_All(); + } + } + if (((TechnoTypeClass const &)Class_Of()).IsLeader) { + CurrentObject.Add_Head(this); + } else { + CurrentObject.Add(this); + } + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + IsSelected = true; + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + return(true); +} + + +/*********************************************************************************************** + * ObjectClass::Render -- Displays the object onto the map. * + * * + * This routine will determine the location of the object and if it is roughly on the * + * visible screen, it will display it. Not displaying objects that are not on the screen * + * will save valuable time. * + * * + * INPUT: bool; Should the render be forced regardless of whether the object is flagged to * + * be redrawn? * + * * + * OUTPUT: bool; Was the draw code called for this object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Render(bool forced) const +{ + assert(this != 0); + assert(IsActive); + + int x, y; + COORDINATE coord = Render_Coord(); + CELL cell = Coord_Cell(coord); + + if (Debug_Map || Debug_Unshroud || ((forced || IsToDisplay) && IsDown && !IsInLimbo)) { + IsToDisplay = false; + + if (Map.Coord_To_Pixel(coord, x, y)) { + + /* + ** Draw the object itself + */ + Draw_It(x, y, WINDOW_TACTICAL); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the trigger attached to the object. Draw_It is window- + ** relative, so add the window's x-coord to 'x'. + */ + if (Debug_Map && Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, + x + (WinX), y, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_6POINT); + } +#endif + + return(true); + } + } + return(false); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * * + * This routine is used to display the current status of the object class to the mono * + * monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 1);mono->Printf("%-18.18s", Text_String(Full_Name())); + if (Next != NULL) { + mono->Set_Cursor(20, 5);mono->Printf("%08X", Next->As_Target()); + } + if (Trigger.Is_Valid()) { + mono->Text_Print(Trigger->Class->IniName, 11, 3); + } + mono->Set_Cursor(34, 1);mono->Printf("%3d", Strength); + + mono->Fill_Attrib(1, 13, 12, 1, IsDown ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 14, 12, 1, IsToDamage ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 15, 12, 1, IsToDisplay ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 16, 12, 1, IsInLimbo ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 17, 12, 1, IsSelected ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 13, 12, 1, IsAnimAttached ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(23, 14);mono->Printf("%d", Riser); + mono->Fill_Attrib(14, 12, 14, 1, IsFalling ? MonoClass::INVERSE : MonoClass::NORMAL); + + AbstractClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * * + * This routine will mark the object and inform the display system * + * that appropriate rendering is needed. Whenever it is determined * + * that an object needs to be redrawn, call this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a subordinate function to the function Mark(). If an object needs to * + * be redrawn it is probably better to call the function Mark(MARK_CHANGE) rather * + * than this function. This function does not inform the map system that * + * overlapping objects are to be redrawn and thus unless you are really sure that * + * this routine should be called, don't. * + * * + * HISTORY: * + * 05/08/1994 JLB : Created. * + * 12/23/1994 JLB : Flags map and flags unit only. * + *=============================================================================================*/ +void ObjectClass::Mark_For_Redraw(void) +{ + assert(this != 0); + assert(IsActive); + + if (!IsToDisplay) { + IsToDisplay = true; + + /* + ** This tells the map rendering logic to "go through the motions" and call the + ** rendering function. In the rendering function, it will sort out what gets + ** rendered and what doesn't. + */ + Map.Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * * + * An object brought into a state of limbo by this routine can be safely deleted. This * + * routine will remove the object from all game lists and tracking systems. It is called * + * prior to deleting the object or placing the object "on ice". * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully placed in limbo? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Limbo(void) +{ + assert(this != 0); + assert(IsActive); + + if (GameActive && !IsInLimbo) { + + Unselect(); + Detach_All(); + Mark(MARK_UP); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Remove the object from the logic processing list. + */ + if (Class_Of().IsSentient) { + Logic.Delete(this); + } + + Hidden(); + IsInLimbo = true; + IsToDisplay = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * * + * This routine will place the object into the game tracking and display systems. It is * + * called as a consequence of creating the object. Every game object must be unlimboed at * + * some point. * + * * + * INPUT: coord -- The coordinate to place the object into the game system. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the game object successfully unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Sets object strength. * + *=============================================================================================*/ +bool ObjectClass::Unlimbo(COORDINATE coord, DirType ) +{ + assert(this != 0); + assert(IsActive); + if (GameActive && IsInLimbo && !IsDown) { + if (ScenarioInit || Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + IsInLimbo = false; + IsToDisplay = false; + Coord = Class_Of().Coord_Fixup(coord); + + if (Mark(MARK_DOWN)) { + if (IsActive) { + + /* + ** Add the object to the appropriate map layer. This layer is used + ** for rendering purposes. + */ + if (In_Which_Layer() != LAYER_NONE) { + Map.Submit(this, In_Which_Layer()); + } + + if (Class_Of().IsSentient) { + Logic.Submit(this); + } + } + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Detach -- Detach the specified target from this object. * + * * + * This routine is called when the object (as specified) is to be removed from the game * + * engine and thus, all references to it must be severed. Typically, the only thing * + * checked for at this level is the attached trigger. * + * * + * INPUT: target -- The target that will be removed from the game system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach(TARGET target, bool ) +{ + if (Trigger.Is_Valid() && Is_Target_Trigger(target) && Trigger->As_Target() == target) { + Attach_Trigger(NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * * + * This routine will take the object and see that it is removed from all miscellaneous * + * tracking systems in the game. This operation is vital when deleting an object. It is * + * necessary so that when the object is removed from the game, existing game objects won't * + * be referencing a now invalid game object. This typically affects the targeting * + * and navigation computers of other game objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_All(bool all) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Unselect this object if it was selected. + */ + if (all || Owner() != PlayerPtr->Class->House) { + Unselect(); + } + + Map.Detach(this); + + /* + ** Remove from targeting computers. + */ + Detach_This_From_All(As_Target(), all); +} + + +/*********************************************************************************************** + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * * + * Any radio message received that applies to objects in general are handled by this * + * routine. Typically, this is the "redraw" message, which occurs when another object is * + * loading or unloading and thus overlapping. * + * * + * INPUT: message -- The message received. * + * * + * OUTPUT: Returns with the appropriate radio response. If the message was recognized, then * + * RADIO_ROGER is returned, otherwise, just RADIO_STATIC is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType ObjectClass::Receive_Message(RadioClass *, RadioMessageType message, long & ) +{ + assert(this != 0); + assert(IsActive); + + switch (message) { + + /* + ** This message serves as a rendering convenience. It lets the system + ** know that there might be a visual conflict and the unit in radio + ** contact should be redrawn. This typically occurs when a vehicle + ** is being unloaded from a hover lander. + */ + case RADIO_REDRAW: + Mark(MARK_CHANGE); + return(RADIO_ROGER); + + default: + break; + } + return(RADIO_STATIC); +} + + +/*********************************************************************************************** + * ObjectClass::Take_Damage -- Applies damage to the object. * + * * + * This routine applies damage to the object according to the damage parameters. It handles * + * reducing the strength of the object and also returns the result of that damage. The * + * result value can be examined to determine if the object was destroyed, greatly damaged, * + * or other results. * + * * + * INPUT: damage -- Reference to the damage number to apply. This number will be adjusted * + * according to defensive armor and distance. Examine this value after * + * the call to determine the actual amount of damage applied. * + * * + * distance -- The distance (in leptons) from the center of the damage causing * + * explosion to the object itself. * + * * + * warhead -- The warhead type that is causing the damage. * + * * + * source -- The perpetrator of this damage. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: Returns the ResultType that indicates what the affect of the damage was. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 12/27/1994 JLB : Trigger event processing for attacked or destroyed. * + * 01/01/1995 JLB : Reduces damage greatly depending on range. * + *=============================================================================================*/ +ResultType ObjectClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(this != 0); + assert(IsActive); + + ResultType result = RESULT_NONE; + int oldstrength = Strength; + + if (oldstrength && damage != 0 && (forced || !Class_Of().IsImmune)) { + int maxstrength = Class_Of().MaxStrength; + + /* + ** Modify damage based on the warhead type and the armor of the object. This results + ** in a reduced damage value, but never below 1 damage point. Unless + ** it's forced damage, in which case we want full damage. + */ + if (!forced /*&& damage > 0*/) { + damage = Modify_Damage(damage, warhead, Class_Of().Armor, distance); + + /* + ** Special hack to ensure that dogs only do damage to intended victim and no + ** damage to others. + */ + if (source && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + } + if (damage == 0) return(RESULT_NONE); + + /* + ** Are we healing/repairing? If so, add strength, but in + ** any case, return that no damage was done. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_AIRCRAFT) { +#else + if (What_Am_I() == RTTI_INFANTRY) { +#endif + Clicked_As_Target(7); + Strength -= damage; + if (Strength > maxstrength) { + Strength = maxstrength; + } + } + return(RESULT_NONE); + } + + /* + ** At this point, we KNOW that at least light damage has occurred. + */ + result = RESULT_LIGHT; + + /* + ** A non-fatal blow has occurred. Check to see if the object transitioned to below + ** half strength or if it is now down to one hit point. + */ + if (oldstrength > damage) { + + if (oldstrength >= (maxstrength >> 1) && (oldstrength-damage) < (maxstrength >> 1)) { + result = RESULT_HALF; + } + } else { + + /* + ** When an object is damaged to destruction, it will instead stop at one + ** damage point. This will prolong the damage state as well as + ** give greater satisfaction when it is finally destroyed. + */ + damage = oldstrength; + } + + /* + ** Apply the damage to the object. + */ + Strength = oldstrength - damage; + + /* + ** Check to see if the object is majorly damaged or destroyed. + */ + switch (Strength) { + case 0: + Record_The_Kill(source); + result = RESULT_DESTROYED; + if (this->Is_Techno()) { + if (this == ::As_Object(((TechnoClass *)this)->House->UnitToTeleport)) ((TechnoClass *)this)->House->UnitToTeleport = 0; + } + Detach_All(); + break; + + case 1: + result = RESULT_MAJOR; + break; + + default: + break; + } + + /* + ** Handle any trigger event associated with this object. + */ + if (source && Trigger.Is_Valid() && result != RESULT_DESTROYED) { + Trigger->Spring(TEVENT_ATTACKED, this); + } + + /* + ** If any damage was assessed and this object is selected, then flag + ** the object to be redrawn so that the health bar will be updated. + */ + if (result != RESULT_NONE && IsSelected) { + Mark(MARK_CHANGE); + } + } + + /* + ** Return with the result of the damage taken. + */ + return(result); +} + + +/*********************************************************************************************** + * ObjectClass::Mark -- Handles basic marking logic. * + * * + * This routine handles the base logic for marking an object up or down on the map. It * + * manages the IsDown flag as well as flagging the object to be redrawn if necessary. * + * Whenever an object is to be marked, it should call this base class function first. If * + * this function returns true, then the higher level function should proceed with its own * + * logic. * + * * + * INPUT: mark -- The marking method to use for this object. It can be either MARK_DOWN, * + * MARK_UP, or MARK_CHANGE. * + * * + * OUTPUT: bool; Was the object marked successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (!IsInLimbo && IsActive) { + + /* + ** A mark for change is always successful UNLESS the object + ** is not placed down or has already been flagged as changed + ** this game frame. + */ + if (mark == MARK_CHANGE || mark == MARK_CHANGE_REDRAW) { + if (IsToDisplay && mark != MARK_CHANGE_REDRAW) return(false); + if (IsDown) { + Mark_For_Redraw(); + return(true); + } + return(false); + } + + /* + ** Handle adding or removing the object in the cells' overlap lists + */ + if (mark == MARK_OVERLAP_UP) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Up(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + if (mark == MARK_OVERLAP_DOWN) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Down(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + + /* + ** It is important to know whether the object is a techno class + ** or not to see if we have to adjust the regional threat ratings + */ + int threat = 0; + HousesType house = HOUSE_NONE; + CELL cell = 0; + TechnoClass * tech; + if (Is_Techno()) { + tech = (TechnoClass *)this; + threat = tech->Risk(); + house = tech->Owner(); + cell = Coord_Cell(Coord); + } else { + tech = NULL; + } + + /* + ** Marking down is only successful if the object isn't already + ** placed down. + */ + if (mark == MARK_DOWN && !IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, threat); + } + IsDown = true; + Mark_For_Redraw(); + return(true); + } + + /* + ** Lifting up is only successful if the object isn't already + ** lifted up from the map. + */ + if (mark == MARK_UP && IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, -threat); + } + IsDown = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Init -- Initializes the basic object system. * + * * + * This routine should be called when the basic object system needs to be initialized. This * + * occurs when the scenario is about to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Init(void) +{ + CurrentObject.Clear(); +} + + +/*********************************************************************************************** + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * * + * This routine is called when this object gets revealed to the house specified. * + * * + * INPUT: house -- Pointer to the house that this object is being revealed to. * + * * + * OUTPUT: Was this object revealed for the first time to this house? Generic objects always * + * return true unless an invalid house pointer was specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Revealed(HouseClass * house) +{ + assert(this != 0); + assert(IsActive); + + return(house != NULL); +} + + +/*********************************************************************************************** + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * * + * Call this routine as a replacement for Unlimbo() if the object is to be paradropped onto * + * the playing field. * + * * + * INPUT: coord -- The desired landing coordinate to give the dropping unit. * + * * + * OUTPUT: bool; Was the object successfully unlimboed and has begun paradropping? * + * * + * WARNINGS: The unit may not be successful in paradropping if the desired destination * + * location cannot be occupied by the object. * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Paradrop(COORDINATE coord) +{ + assert(this != 0); + assert(IsActive); + + Height = FLIGHT_LEVEL; + IsFalling = true; + if (Unlimbo(coord, DIR_S)) { + AnimClass * anim = NULL; + + if (What_Am_I() == RTTI_BULLET) { + anim = new AnimClass(ANIM_PARA_BOMB, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } else { + anim = new AnimClass(ANIM_PARACHUTE, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } + + /* + ** If the animation was created, then attach it to this object. + */ + if (anim != NULL) { + anim->Attach_To(this); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * * + * This routine is used to attach the specified trigger to the object. * + * * + * INPUT: trigger -- Pointer to the trigger to attach. If any existing trigger is desired * + * to be detached, then pass NULL to this routine. * + * * + * OUTPUT: bool; Was the trigger attached? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Attach_Trigger(TriggerClass * trigger) +{ + if (Trigger.Is_Valid()) { + TriggerClass * tptr = Trigger; + tptr->AttachCount--; + Trigger = NULL; + } + + if (trigger) { + Trigger = trigger; + trigger->AttachCount++; + return(true); + } + return(false); +} + + +// These can't be made inline (for various reasons). +short const * ObjectClass::Occupy_List(bool placement) const {return(Class_Of().Occupy_List(placement));}; +short const * ObjectClass::Overlap_List(bool ) const {return(Class_Of().Overlap_List());}; +BuildingClass * ObjectClass::Who_Can_Build_Me(bool intheory, bool legal) const {return(Class_Of().Who_Can_Build_Me(intheory, legal, Owner()));}; +fixed ObjectClass::Health_Ratio(void) const {return(fixed(Strength, Class_Of().MaxStrength));}; +int ObjectClass::Full_Name(void) const {return Class_Of().Full_Name();}; + + +//********************************************************************************************** +// MODULE SEPARATION -- ObjectTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * * + * This is the base constructor that is used when constructing the object type classes. * + * Every tangible game piece type calls this constructor for the ObjectTypeClass. This * + * class holds static information that is common to objects in general. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/23/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass::ObjectTypeClass( + RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int name, + char const * ini) : + AbstractTypeClass(rtti, id, name, ini), + IsCrushable(false), + IsStealthy(is_stealthy), + IsSelectable(is_selectable), + IsLegalTarget(is_legal_target), + IsInsignificant(is_insignificant), + IsImmune(is_immune), + IsSentient(is_sentient), + IsFootprint(is_footprint), + Armor(ARMOR_NONE), + MaxStrength(0), + ImageData(0), + RadarIcon(0) +{ +} + + +/*********************************************************************************************** + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * * + * This routine will return the maximum number of pips that can be displayed for this * + * object. When dealing with generic objects, this value is always zero. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pip boxes (empty or otherwise) to display. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Max_Pips(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * * + * This routine will fetch the dimensions of this object expressed as pixels width and * + * pixels height. This information can be used to intelligently update the clipping * + * rectangles. * + * * + * INPUT: width -- Reference to the width variable that will be filled in. * + * * + * height -- Reference to the height variable that will be filled in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::Dimensions(int &width, int &height) const +{ + width = 10; + height = 10; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * * + * This routine will return the cost to purchase this unit. This routine is expected to be * + * overridden by the objects that can actually be purchased. All other object types can * + * simply return zero since this value won't be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the cost of the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Cost_Of(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * * + * This routine will fetch the time in takes to construct this object. Objects that can * + * be constructed will override this routine in order to return a useful value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time units (arbitrary) that it takes to construct this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Time_To_Build(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * * + * This routine will return with the cameo data pointer for this object type. It is * + * expected that objects that can appear on the sidebar will override this routine in order * + * to provide proper cameo data pointer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo shape data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void const * ObjectTypeClass::Get_Cameo_Data(void) const +{ + return(NULL); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * * + * This routine returns a pointer to a simple occupation list for this object. Since at * + * this tier of the object class chain, the exact shape of the object is indeterminate, * + * this function merely returns a single cell occupation list. This actually works for * + * most vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to a simple occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * * + * This function returns a pointer to an overlap list for the object. An overlap list is * + * the offsets from the object's cell to get the cells the imagery overlaps, but is object * + * is not considered to occupy. Since at this stage, the overlap information is not * + * available, this function merely returns a pointer to an empty list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the generic overlap list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Overlap_List(void) const +{ + static short const _list[] = {REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * * + * This routine is used to handle the once per game processing required for object types. * + * This consists of loading any data and initializing any data tables the game requires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk. * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::One_Time(void) +{ + SelectShapes = MFCD::Retrieve("SELECT.SHP"); + + #ifndef NDEBUG + RawFileClass file("PIPS.SHP"); + if (file.Is_Available()) { + PipShapes = Load_Alloc_Data(file); + } else { + PipShapes = MFCD::Retrieve("PIPS.SHP"); + } + #else + PipShapes = MFCD::Retrieve("PIPS.SHP"); + #endif +} + + +/*********************************************************************************************** + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * * + * This routine will scan through all available factory buildings and determine which * + * is capable of building this object type. The scan can be controlled to scan for only * + * factory buildings that are free to produce now or those that could produce this * + * object type if conditions permit. * + * * + * INPUT: intheory -- Should the general (when conditions permit) case be examined to see * + * if a building could build this object type "in theory" even though it * + * might currently be otherwise occupied? * + * * + * legal -- Check for building prerequisite and technology level rules? Usually * + * this would be 'true' for human controlled requests and 'false' for * + * the computer. This is because the computer is usually not under * + * the normal restrictions that the player is under. * + * * + * OUTPUT: Returns with a pointer to the building that can produce the object of this * + * type. If no suitable factory building could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * ObjectTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI && + building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Get_Ownable()) && + (!legal || building->House->Can_Build(this, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + // BG: Hack so only kennels can build dogs, and no other, and barracks can + // only build humans and no other. + if (What_Am_I() == RTTI_INFANTRYTYPE) { + InfantryTypeClass * me = (InfantryTypeClass *)this; + if (me->IsDog) { + if (*building == STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } else { + if (*building != STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } else { + + /* + ** HACK ALERT: Helipads can build aircraft and airstrips can build + ** fixed wing craft only. + */ + if (What_Am_I() == RTTI_AIRCRAFTTYPE) { + AircraftTypeClass * air = (AircraftTypeClass *)this; + if ((*building == STRUCT_HELIPAD && !air->IsFixedWing) || (*building == STRUCT_AIRSTRIP && air->IsFixedWing)) { + if (building->IsLeader) return(building); + anybuilding = building; + } + + } else { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } + } + return(anybuilding); +} diff --git a/CODE/OBJECT.H b/CODE/OBJECT.H new file mode 100644 index 0000000..24e62bd --- /dev/null +++ b/CODE/OBJECT.H @@ -0,0 +1,255 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OBJECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OBJECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OBJECT_H +#define OBJECT_H + +#include "abstract.h" + +class ObjectClass; +class TechnoClass; +class ObjectTypeClass; +class HouseClass; +class BuildingClass; +class RadioClass; +class TriggerClass; + + +/********************************************************************** +** Every game object (that can exist on the map) is ultimately derived from this object +** class. It holds the common information between all objects. This is primarily the +** object unique ID number and its location in the world. All common operations between +** game objects are represented by virtual functions in this class. +*/ +class ObjectClass : public AbstractClass +{ + public: + /* + ** The object can be in one of two states -- placed down on the map, or not. If the + ** object is placed down on the map, then this flag will be true. + */ + unsigned IsDown:1; + + /* + ** This is a support flag that is only used while building a list of objects to + ** be damaged by a proximity affect (explosion). When this flag is set, this object + ** will not be added to the list of units to damage. When damage is applied to the + ** object, this flag is cleared again. This process ensures that an object is never + ** subject to "double jeopardy". + */ + unsigned IsToDamage:1; + + /* + ** Is this object flagged to be displayed during the next rendering process? This + ** flag could be set by many different circumstances. It is automatically cleared + ** when the object is rerendered. + */ + unsigned IsToDisplay:1; + + /* + ** An object in the game may be valid yet held in a state of "limbo". Units are in such + ** a state if they are being transported or are otherwise "inside" another unit. They can + ** also be in limbo if they have been created but are being held until the proper time + ** for delivery. + */ + unsigned IsInLimbo:1; + + /* + ** When an object is "selected" it is given a floating bar graph or other graphic imagery + ** to display this fact. When the player performs I/O, the actions may have a direct + ** bearing on the actions of the currently selected object. For quick checking purposes, + ** if this object is the one that is "selected", this flag will be true. + */ + unsigned IsSelected:1; + + /* + ** If an animation is attached to this object, then this flag will be true. + */ + unsigned IsAnimAttached:1; + + /* + ** If this object should process falling logic, then this flag will be true. Such + ** objects might be ballistic projectiles, grenades, or parachuters. + */ + unsigned IsFalling:1; + int Riser; + + /* + ** Several objects could exist in the same cell list. This is a pointer to the + ** next object in the cell list. The objects in this list are not in any + ** significant order. + */ + SmartPtr Next; + + /* + ** Every object can be assigned a trigger; the same trigger can be assigned + ** to multiple objects. + */ + CCPtr Trigger; + + /* + ** This is the current strength of this object. + */ + short Strength; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + ObjectClass(RTTIType rtti, int id); + ObjectClass(NoInitClass const & x) : AbstractClass(x), Next(x), Trigger(x) {}; + virtual ~ObjectClass(void) {Next = 0;}; + int operator < (ObjectClass const & object) const {return Sort_Y() < object.Sort_Y();}; + int operator > (ObjectClass const & object) const {return Sort_Y() > object.Sort_Y();}; + + /* + ** Object selection control. + */ + static void Init(void); + + /* + ** Query functions. + */ + virtual bool Is_Players_Army(void) const {return(false);} + virtual void const * Get_Image_Data(void) const; + virtual ActionType What_Action(ObjectClass const *) const; + virtual ActionType What_Action(CELL) const; + virtual LayerType In_Which_Layer(void) const; + bool Is_Infantry(void) const {return(RTTI == RTTI_INFANTRY);}; + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRY || RTTI == RTTI_UNIT || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + bool Is_Techno(void) const {return(RTTI == RTTI_BUILDING || RTTI == RTTI_UNIT || RTTI == RTTI_INFANTRY || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + virtual int Get_Ownable(void) const; + virtual ObjectTypeClass const & Class_Of(void) const = 0; + virtual char const * Name(void) const; + virtual int Full_Name(void) const; + virtual bool Can_Repair(void) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual void Detach(TARGET target, bool all=true); + virtual void Detach_All(bool all=true); + virtual void Record_The_Kill(TechnoClass * ); + virtual bool Paradrop(COORDINATE coord); + bool Attach_Trigger(TriggerClass * trigger); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Do_Shimmer(void); + virtual int Exit_Object(TechnoClass *); + virtual bool Render(bool forced) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual fixed Health_Ratio(void) const; + virtual void Draw_It(int x, int y, WindowNumberType ) const = 0; + virtual void Hidden(void); + virtual void Look(bool incremental=false); + virtual bool Mark(MarkType=MARK_CHANGE); + + private: + virtual void Mark_For_Redraw(void); + + public: + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType , ObjectClass *); + virtual void Active_Click_With(ActionType , CELL ); + virtual void Clicked_As_Target(int = 7); + virtual bool Select(void); + virtual void Unselect(void); + + /* + ** Combat related. + */ + virtual bool In_Range(COORDINATE , int=0) const; + virtual int Weapon_Range(int =0) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Scatter(COORDINATE , bool forced=false, bool nokidding=false); + virtual bool Catch_Fire(void); + virtual void Fire_Out(void); + virtual int Value(void) const; + virtual MissionType Get_Mission(void) const; + + /* + ** AI. + */ + virtual void Per_Cell_Process(PCPType) {} + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int ); + virtual void Sell_Back(int ); + virtual void AI(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void Move(FacingType); + + enum {FLIGHT_LEVEL=256}; +}; + +#endif diff --git a/CODE/ODATA.CPP b/CODE/ODATA.CPP new file mode 100644 index 0000000..092f080 --- /dev/null +++ b/CODE/ODATA.CPP @@ -0,0 +1,954 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ODATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ODATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : August 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static OverlayTypeClass const Sandbag( + OVERLAY_SANDBAG_WALL, // Overlay type number. + "SBAG", // INI name of overlay. + TXT_SANDBAG_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 20, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Cyclone( + OVERLAY_CYCLONE_WALL, // Overlay type number. + "CYCL", // INI name of overlay. + TXT_CYCLONE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Brick( + OVERLAY_BRICK_WALL, // Overlay type number. + "BRIK", // INI name of overlay. + TXT_BRICK_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 3, // If this is a wall, how many damage levels? + 70, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + true, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Barbwire( + OVERLAY_BARBWIRE_WALL, // Overlay type number. + "BARB", // INI name of overlay. + TXT_BARBWIRE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Wood( + OVERLAY_WOOD_WALL, // Overlay type number. + "WOOD", // INI name of overlay. + TXT_WOOD_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + true, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Fence( + OVERLAY_FENCE, // Overlay type number. + "FENC", // INI name of overlay. + TXT_FENCE, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gold1( + OVERLAY_GOLD1, // Overlay type number. + "GOLD01", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold2( + OVERLAY_GOLD2, // Overlay type number. + "GOLD02", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold3( + OVERLAY_GOLD3, // Overlay type number. + "GOLD03", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold4( + OVERLAY_GOLD4, // Overlay type number. + "GOLD04", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gems1( + OVERLAY_GEMS1, // Overlay type number. + "GEM01", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems2( + OVERLAY_GEMS2, // Overlay type number. + "GEM02", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems3( + OVERLAY_GEMS3, // Overlay type number. + "GEM03", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems4( + OVERLAY_GEMS4, // Overlay type number. + "GEM04", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V12( + OVERLAY_V12, // Overlay type number. + "V12", // INI name of overlay. + TXT_CIV12, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V13( + OVERLAY_V13, // Overlay type number. + "V13", // INI name of overlay. + TXT_CIV13, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V14( + OVERLAY_V14, // Overlay type number. + "V14", // INI name of overlay. + TXT_CIV14, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V15( + OVERLAY_V15, // Overlay type number. + "V15", // INI name of overlay. + TXT_CIV15, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V16( + OVERLAY_V16, // Overlay type number. + "V16", // INI name of overlay. + TXT_CIV16, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V17( + OVERLAY_V17, // Overlay type number. + "V17", // INI name of overlay. + TXT_CIV17, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V18( + OVERLAY_V18, // Overlay type number. + "V18", // INI name of overlay. + TXT_CIV18, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const FlagSpot( + OVERLAY_FLAG_SPOT, // Overlay type number. + "FPLS", // INI name of overlay. + TXT_FLAG_SPOT, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const WoodCrate( + OVERLAY_WOOD_CRATE, // Overlay type number. + "WCRATE", // INI name of overlay. + TXT_WOOD_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const WaterCrate( + OVERLAY_WATER_CRATE, // Overlay type number. + "WWCRATE", // INI name of overlay. + TXT_WATER_CRATE, // Full name of overlay. + LAND_WATER, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const SteelCrate( + OVERLAY_STEEL_CRATE, // Overlay type number. + "SCRATE", // INI name of overlay. + TXT_STEEL_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); + + +/*********************************************************************************************** + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * * + * This is the constructor for the overlay types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass::OverlayTypeClass( + OverlayType iconset, + char const * ininame, + int fullname, + LandType ground, + int damagelevels, + int damagepoints, + bool isradarvisible, + bool iswooden, + bool istarget, + bool iscrushable, + bool istiberium, + bool high, + bool theater, + bool walltype, + bool iscrate) : + ObjectTypeClass(RTTI_OVERLAYTYPE, + int(iconset), + false, + true, + false, + istarget, + true, + false, + false, + fullname, + ininame), + Type(iconset), + Land(ground), + DamageLevels(damagelevels), + DamagePoints(damagepoints), + IsTheater(theater), + IsWall(walltype), + IsHigh(high), + IsTiberium(istiberium), + IsWooden(iswooden), + IsCrate(iscrate), + IsRadarVisible(isradarvisible) +{ + IsCrushable = iscrushable; +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * * + * This will allocate an overlay type class object from the special memory pool that is * + * for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlay type class object allocated. If there is * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * OverlayTypeClass::operator new(size_t) +{ + return(OverlayTypes.Alloc()); +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * * + * This will return a previously allcoated overaly type object to the special memory * + * pool that it was allocated from. * + * * + * INPUT: pointer -- Pointer to the overlay type class object to return the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::operator delete(void * pointer) +{ + OverlayTypes.Free((OverlayTypeClass *)pointer); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * * + * This will initialize the overlay type heap by pre-allocated all overlay types known * + * to exist. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: It should be called once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init_Heap(void) +{ + /* + ** These overlay type class objects must be allocated in the exact order that they + ** are specified in the OverlayType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new OverlayTypeClass(Sandbag); // OVERLAY_SANDBAG_WALL + new OverlayTypeClass(Cyclone); // OVERLAY_CYCLONE_WALL + new OverlayTypeClass(Brick); // OVERLAY_BRICK_WALL + new OverlayTypeClass(Barbwire); // OVERLAY_BARBWIRE_WALL + new OverlayTypeClass(Wood); // OVERLAY_WOOD_WALL + new OverlayTypeClass(Gold1); // OVERLAY_GOLD1 + new OverlayTypeClass(Gold2); // OVERLAY_GOLD2 + new OverlayTypeClass(Gold3); // OVERLAY_GOLD3 + new OverlayTypeClass(Gold4); // OVERLAY_GOLD4 + new OverlayTypeClass(Gems1); // OVERLAY_GEMS1 + new OverlayTypeClass(Gems2); // OVERLAY_GEMS2 + new OverlayTypeClass(Gems3); // OVERLAY_GEMS3 + new OverlayTypeClass(Gems4); // OVERLAY_GEMS4 + new OverlayTypeClass(V12); // OVERLAY_V12 + new OverlayTypeClass(V13); // OVERLAY_V13 + new OverlayTypeClass(V14); // OVERLAY_V14 + new OverlayTypeClass(V15); // OVERLAY_V15 + new OverlayTypeClass(V16); // OVERLAY_V16 + new OverlayTypeClass(V17); // OVERLAY_V17 + new OverlayTypeClass(V18); // OVERLAY_V18 + new OverlayTypeClass(FlagSpot); // OVERLAY_FLAG_SPOT + new OverlayTypeClass(WoodCrate); // OVERLAY_WOOD_CRATE + new OverlayTypeClass(SteelCrate); // OVERLAY_STEEL_CRATE + new OverlayTypeClass(Fence); // OVERLAY_FENCE + new OverlayTypeClass(WaterCrate); // OVERLAY_WATER_CRATE +} + + +/*********************************************************************************************** + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * * + * This routine should be called once when the game first starts. It will establish * + * pointers to the graphic data of the overlay objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * * + * This routine is used to determine the overlay number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the overlay. * + * * + * OUTPUT: Returns with the overlay number. If the name had no match, * + * then returns with OVERLAY_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +OverlayType OverlayTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(OVERLAY_NONE); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the overlay map and build an * + * occupation list. This list is used to render a overlay cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the overlay occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * OverlayTypeClass::Occupy_List(bool) const +{ + static short _simple[] = {0, REFRESH_EOL}; + + return(_simple); +} + + +/*************************************************************************** + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +unsigned char * OverlayTypeClass::Radar_Icon(int data) const +{ + unsigned char * icon = (unsigned char *)Get_Radar_Data(); // Get pointer to radar icons + if (icon != NULL) icon += (data * 9) + 2; // move icon ptr to correct icon + return(icon); // Return the correct icon +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * * + * This routine is used to display a generic view of the overlay * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + if (Get_Image_Data() != NULL) { + int frame = 0; + + if (IsTiberium) { + frame = 7; + } + if (Type == OVERLAY_GEMS1 || Type == OVERLAY_GEMS2 || Type == OVERLAY_GEMS3 || Type == OVERLAY_GEMS4) { + frame = 2; + } + + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), frame, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * * + * This routine prepares a list of overlay objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a overlay object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created * + *=============================================================================================*/ +void OverlayTypeClass::Prep_For_Add(void) +{ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + if (overlay.Get_Image_Data() != NULL && + !overlay.IsWall && + (!overlay.IsTiberium || index == OVERLAY_GOLD1 || index == OVERLAY_GEMS1)) { + + Map.Add_To_List(&overlay); + } + } +} +#endif + + +/*********************************************************************************************** + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * * + * This support routine is used by the scenario editor to add a overlay object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the overlay object. * + * * + * OUTPUT: bool; Was the overlay object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new OverlayClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * * + * This routine will create an object of this type. For certain overlay objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a overlay at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this overlay type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * OverlayTypeClass::Create_One_Of(HouseClass *) const +{ + return(new OverlayClass(Type, -1)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * * + * This routine will draw the overlay shape at the coordinates specified. It is presumed * + * that all the underlying layers have already been rendered by the time this routine is * + * called. * + * * + * INPUT: x, y -- Coordinate (upper left) of cell where overlay image is to be drawn. * + * * + * data -- Cell specific data that controls the imagery of the overlay. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Draw_It(int x, int y, int data) const +{ + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), data, Map.TacPixelX+x+(CELL_PIXEL_W>>1), Map.TacPixelY+y+(CELL_PIXEL_H>>1), WINDOW_MAIN, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * * + * This routine will update the overlay graphic data according to the theater specified. * + * It is typically called when the scenario is first loaded (theater change). * + * * + * INPUT: theater -- The theater to load specific data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass & overlay = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + + if (overlay.IsTheater) { + _makepath(fullname, NULL, NULL, overlay.IniName, Theaters[theater].Suffix); + } else { + _makepath(fullname, NULL, NULL, overlay.IniName, ".SHP"); + } + overlay.ImageData = MFCD::Retrieve(fullname); + + IsTheaterShape = overlay.IsTheater; //Tell Build_Frame if this is a theater specific shape + if (overlay.RadarIcon != NULL) delete[] (char *)overlay.RadarIcon; + overlay.RadarIcon = Get_Radar_Icon(overlay.Get_Image_Data(), 0, -1, 3); + IsTheaterShape = false; + } + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * * + * Use this routine to get a reference that corresponds to the overlay type. * + * * + * INPUT: type -- The overlay type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the overlay type specified. * + * * + * WARNINGS: Be sure that the overlay type specified is legal. Illegal type value will * + * result in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass & OverlayTypeClass::As_Reference(OverlayType type) +{ + return(*OverlayTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * * + * This will adjust the coordinate specified so that it will be of legal format to * + * assign as the coordinate of an overlay. Overlays are always relative to the upper left * + * corner of the cell, so this routine drops the fractional cell components. * + * * + * INPUT: coord -- The coordinate to fixup to be legal for assignment. * + * * + * OUTPUT: Returns with a properly fixed up coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE OverlayTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/CODE/OPTIONS.CPP b/CODE/OPTIONS.CPP new file mode 100644 index 0000000..6d29ad1 --- /dev/null +++ b/CODE/OPTIONS.CPP @@ -0,0 +1,923 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OPTIONS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * OptionsClass::Get_Game_Speed -- Fetches the current game speed setting. * + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * OptionsClass::Get_Scroll_Rate -- Fetches the current scroll rate setting. * + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * OptionsClass::One_Time -- This performs any one time initialization for the options class.* + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * OptionsClass::Process -- Handles all the options graphic interface. * + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * OptionsClass::Set -- Sets options based on current settings * + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * OptionsClass::Set_Game_Speed -- Sets the game speed as specified. * + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * OptionsClass::Set_Scroll_Rate -- Sets the scroll rate as specified. * + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * OptionsClass::Set_Tint -- Sets the tint setting. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "options.h" + + +#ifdef WIN32 +char const * const OptionsClass::HotkeyName = "WinHotkeys"; +#else +char const * const OptionsClass::HotkeyName = "DOSHotkeys"; +#endif + + +/*********************************************************************************************** + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * * + * This is the constructor for the options class. It handles setting up all the globals * + * necessary for the options. This includes setting them to their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +OptionsClass::OptionsClass(void) : + GameSpeed(3), + ScrollRate(3), +#ifdef WIN32 + Volume(".40"), // was .295 + ScoreVolume(".25"), +#ifdef FIXIT_VERSION_3 + MultiScoreVolume("0"), +#endif +#else + Volume(".8"), + ScoreVolume(".6"), +#endif + Brightness(fixed::_1_2), + Tint(fixed::_1_2), + Saturation(fixed::_1_2), + Contrast(fixed::_1_2), + AutoScroll(true), + IsScoreRepeat(false), + IsScoreShuffle(false), + IsPaletteScroll(true), + + KeyForceMove1(KN_LALT), + KeyForceMove2(KN_RALT), + KeyForceAttack1(KN_LCTRL), + KeyForceAttack2(KN_RCTRL), + KeySelect1(KN_LSHIFT), + KeySelect2(KN_RSHIFT), + KeyScatter(KN_X), + KeyStop(KN_S), + KeyGuard(KN_G), + KeyNext(KN_N), + KeyPrevious(KN_B), + KeyFormation(KN_F), + KeyHome1(KN_HOME), + KeyHome2(KN_E_HOME), + KeyBase(KN_H), + KeyResign(KN_R), + KeyAlliance(KN_A), + KeyBookmark1(KN_F9), + KeyBookmark2(KN_F10), + KeyBookmark3(KN_F11), + KeyBookmark4(KN_F12), + KeySelectView(KN_E), + KeyRepair(KN_T), + KeyRepairOn(KN_NONE), + KeyRepairOff(KN_NONE), + KeySell(KN_Y), + KeySellOn(KN_NONE), + KeySellOff(KN_NONE), + KeyMap(KN_U), + KeySidebarUp(KN_UP), + KeySidebarDown(KN_DOWN), + KeyOption1(KN_ESC), + KeyOption2(KN_SPACE), + KeyScrollLeft(KN_NONE), + KeyScrollRight(KN_NONE), + KeyScrollUp(KN_NONE), + KeyScrollDown(KN_NONE), + KeyQueueMove1(KN_Q), + KeyQueueMove2(KN_Q), + KeyTeam1(KN_1), + KeyTeam2(KN_2), + KeyTeam3(KN_3), + KeyTeam4(KN_4), + KeyTeam5(KN_5), + KeyTeam6(KN_6), + KeyTeam7(KN_7), + KeyTeam8(KN_8), + KeyTeam9(KN_9), + KeyTeam10(KN_0) +{ +} + + +/*********************************************************************************************** + * OptionsClass::One_Time -- This performs any one time initialization for the options class. * + * * + * This routine should be called only once and it will perform any initializations for the * + * options class that is needed. This may include things like file loading and memory * + * allocation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::One_Time(void) +{ + Set_Score_Vol(ScoreVolume * 256); +} + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Process(void) +{ +} + + +/*********************************************************************************************** + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * * + * This routine will control the score shuffle flag. The setting to use is provided as * + * a parameter. When shuffling is on, the score play order is scrambled. * + * * + * INPUT: on -- Should the shuffle option be activated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Shuffle(int on) +{ + IsScoreShuffle = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * * + * This routine is used to control whether scores repeat or not. The setting to use for * + * the repeat flag is provided as a parameter. * + * * + * INPUT: on -- Should the scores repeat? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Repeat(int on) +{ + IsScoreRepeat = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * * + * This routine will set the global score volume to the value specified. The value ranges * + * from zero to 255. * + * * + * INPUT: volume -- The new volume setting to use for scores. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Score_Volume(fixed volume, bool feedback) +{ + ScoreVolume = Sub_Saturate(volume, 1); + Set_Score_Vol(ScoreVolume * 256); + if (feedback && !Theme.Still_Playing()) { + Sound_Effect(VOC_BEEP, ScoreVolume); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * * + * This routine will set the sound effect volume level as indicated. It can generate a * + * sound effect for feedback purposes if desired. The volume setting can range from zero * + * to 255. The value of 255 is the loudest. * + * * + * INPUT: volume -- The volume setting to use for the new value. 0 to 255. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Sound_Volume(fixed volume, bool feedback) +{ + Volume = Sub_Saturate(volume, 1); + if (feedback) { + Sound_Effect(VOC_BEEP); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * * + * This routine will set the current brightness level to the value specified. This value * + * can range from zero to 255, with 128 being the normal (default) brightness level. * + * * + * INPUT: brightness -- The brightness level to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Brightness(fixed brightness) +{ + Brightness = fixed::_1_4 + (fixed::_1_2 * brightness); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * * + * This routine will fetch the current setting for the brightness level. The value ranges * + * from zero to 255, with 128 being the normal (default) value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current brightness setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Brightness(void) const +{ + return((Brightness - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * * + * This routine will set the color value to that specified. The value specified can range * + * from zero to 255. The value of 128 is the normal default color setting. * + * * + * INPUT: color -- The new color value to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Saturation(fixed color) +{ + Saturation = color; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * * + * This routine will fetch the current color setting. This value ranges from zero to * + * 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current color setting. The value of 128 is the normal (default) * + * color setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Saturation(void) const +{ + return(Saturation); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * * + * This routine will set the contrast to the setting specified. This setting ranges from * + * zero to 255. The value o 128 is the normal default value. * + * * + * INPUT: contrast -- The contrast setting to make as the current setting. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Contrast(fixed contrast) +{ + Contrast = fixed::_1_4 + (fixed::_1_2 * contrast); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * * + * This routine will get the current contrast setting. The value returned is in the range * + * of zero to 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current contrast setting. A setting of 128 is the normal default value.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Contrast(void) const +{ + return((Contrast - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Tint -- Sets the tint setting. * + * * + * This routine will change the current tint setting according to the value specified. * + * * + * INPUT: tint -- The desired tint setting. This value ranges from zero to 255. * + * * + * OUTPUT: none * + * * + * WARNINGS: The value of 128 is the default (normal) tint setting. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Tint(fixed tint) +{ + Tint = tint; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * * + * This fetches the current tint setting. The value is returned as a number between * + * zero and 255. This has been adjusted for the valid range allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current tint setting. Normal tint setting is 128. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Tint(void) const +{ + return(Tint); +} + + +/*********************************************************************************************** + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * * + * This routine is used to adjust the palette according to the settings provided. It is * + * used by the options class to monkey with the palette. * + * * + * INPUT: oldpal -- Pointer to the original (unmodified) palette. * + * * + * newpal -- The new palette to create according to the settings provided. * + * * + * brightness -- The brightness level (0..255). * + * * + * color -- The color level (0..255). * + * * + * tint -- The tint (hue) level (0..255). * + * * + * contrast -- The contrast level (0..255). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + * 12/02/1995 JLB : Uses palette class objects. * + *=============================================================================================*/ +void OptionsClass::Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const +{ + if (!oldpal || !newpal) return; + + /* + ** Adjust for palette. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + if (index == CC_MOUSE_COLOR) { + newpal[index] = oldpal[index]; + } else { + /* + ** Convert the working palette entry into an HSV format for + ** manipulation. + */ + HSVClass hsv = oldpal[index]; + + /* + ** Adjust contrast by moving the value toward the center according to the + ** percentage indicated. + */ + int temp; + temp = (hsv.Value_Component() * (brightness * 256)) / 0x80; // Brightness + temp = Bound(temp, 0, 0xFF); + int v = temp; + temp = (((((int)v) - 0x80) * (contrast * 256)) / 0x80) + 0x80; // Contrast + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (hsv.Saturation_Component() * (color * 256)) / 0x80; // Color + temp = Bound(temp, 0, 0xFF); + int s = temp; + temp = (hsv.Hue_Component() * (tint * 256)) / 0x80; // Tint + temp = Bound(temp, 0, 0xFF); + int h = temp; + + /* + ** Replace the working palette entry according to the newly calculated + ** hue, saturation, and value. + */ + newpal[index] = HSVClass(h, s, v); + } + } +} + + +/*********************************************************************************************** + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Reworked to use new INI handler. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Load_Settings(void) +{ + /* + ** Create filename and read the file. + */ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + ini.Load(file); + + /* + ** Read in the Options values + */ + static char const * const OPTIONS = "Options"; + GameSpeed = ini.Get_Int(OPTIONS, "GameSpeed", GameSpeed); + ScrollRate = ini.Get_Int(OPTIONS, "ScrollRate", ScrollRate); + Set_Brightness(ini.Get_Fixed(OPTIONS, "Brightness", Brightness)); + Set_Sound_Volume(ini.Get_Fixed(OPTIONS, "Volume", Volume), false); + Set_Score_Volume(ini.Get_Fixed(OPTIONS, "ScoreVolume", ScoreVolume), false); +#ifdef FIXIT_VERSION_3 + MultiScoreVolume = ini.Get_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + Set_Contrast(ini.Get_Fixed(OPTIONS, "Contrast", Contrast)); + Set_Saturation(ini.Get_Fixed(OPTIONS, "Color", Saturation)); + Set_Tint(ini.Get_Fixed(OPTIONS, "Tint", Tint)); + AutoScroll = ini.Get_Bool(OPTIONS, "AutoScroll", AutoScroll); + Set_Repeat(ini.Get_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat)); + Set_Shuffle(ini.Get_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle)); + SlowPalette = ini.Get_Bool(OPTIONS, "SlowPalette", SlowPalette); + IsPaletteScroll = ini.Get_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + KeyForceMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + KeyForceMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + KeyForceAttack1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + KeyForceAttack2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + KeySelect1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect1", KeySelect1); + KeySelect2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect2", KeySelect2); + KeyScatter = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScatter", KeyScatter); + KeyStop = (KeyNumType)ini.Get_Int(HotkeyName, "KeyStop", KeyStop); + KeyGuard = (KeyNumType)ini.Get_Int(HotkeyName, "KeyGuard", KeyGuard); + KeyNext = (KeyNumType)ini.Get_Int(HotkeyName, "KeyNext", KeyNext); + KeyPrevious = (KeyNumType)ini.Get_Int(HotkeyName, "KeyPrevious", KeyPrevious); + KeyFormation = (KeyNumType)ini.Get_Int(HotkeyName, "KeyFormation", KeyFormation); + KeyHome1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome1", KeyHome1); + KeyHome2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome2", KeyHome2); + KeyBase = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBase", KeyBase); + KeyResign = (KeyNumType)ini.Get_Int(HotkeyName, "KeyResign", KeyResign); + KeyAlliance = (KeyNumType)ini.Get_Int(HotkeyName, "KeyAlliance", KeyAlliance); + KeyBookmark1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + KeyBookmark2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + KeyBookmark3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + KeyBookmark4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + KeySelectView = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelectView", KeySelectView); + KeyRepair = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + KeyRepairOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + KeyRepairOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + KeySell = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellToggle", KeySell); + KeySellOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOn", KeySellOn); + KeySellOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOff", KeySellOff); + KeyMap = (KeyNumType)ini.Get_Int(HotkeyName, "KeyMapToggle", KeyMap); + KeySidebarUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + KeySidebarDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + KeyOption1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption1", KeyOption1); + KeyOption2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption2", KeyOption2); + KeyScrollLeft = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + KeyScrollRight = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + KeyScrollUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + KeyScrollDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + KeyQueueMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + KeyQueueMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + KeyTeam1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam1", KeyTeam1); + KeyTeam2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam2", KeyTeam2); + KeyTeam3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam3", KeyTeam3); + KeyTeam4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam4", KeyTeam4); + KeyTeam5 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam5", KeyTeam5); + KeyTeam6 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam6", KeyTeam6); + KeyTeam7 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam7", KeyTeam7); + KeyTeam8 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam8", KeyTeam8); + KeyTeam9 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam9", KeyTeam9); + KeyTeam10 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + +#ifdef WIN32 + KeyForceMove1 = (KeyNumType)(KeyForceMove1 & ~WWKEY_VK_BIT); + KeyForceMove2 = (KeyNumType)(KeyForceMove2 & ~WWKEY_VK_BIT); + KeyForceAttack1 = (KeyNumType)(KeyForceAttack1 & ~WWKEY_VK_BIT); + KeyForceAttack2 = (KeyNumType)(KeyForceAttack2 & ~WWKEY_VK_BIT); + KeySelect1 = (KeyNumType)(KeySelect1 & ~WWKEY_VK_BIT); + KeySelect2 = (KeyNumType)(KeySelect2 & ~WWKEY_VK_BIT); + KeyScatter = (KeyNumType)(KeyScatter & ~WWKEY_VK_BIT); + KeyStop = (KeyNumType)(KeyStop & ~WWKEY_VK_BIT); + KeyGuard = (KeyNumType)(KeyGuard & ~WWKEY_VK_BIT); + KeyNext = (KeyNumType)(KeyNext & ~WWKEY_VK_BIT); + KeyPrevious = (KeyNumType)(KeyPrevious & ~WWKEY_VK_BIT); + KeyFormation = (KeyNumType)(KeyFormation & ~WWKEY_VK_BIT); + KeyHome1 = (KeyNumType)(KeyHome1 & ~WWKEY_VK_BIT); + KeyHome2 = (KeyNumType)(KeyHome2 & ~WWKEY_VK_BIT); + KeyBase = (KeyNumType)(KeyBase & ~WWKEY_VK_BIT); + KeyResign = (KeyNumType)(KeyResign & ~WWKEY_VK_BIT); + KeyAlliance = (KeyNumType)(KeyAlliance & ~WWKEY_VK_BIT); + KeyBookmark1 = (KeyNumType)(KeyBookmark1 & ~WWKEY_VK_BIT); + KeyBookmark2 = (KeyNumType)(KeyBookmark2 & ~WWKEY_VK_BIT); + KeyBookmark3 = (KeyNumType)(KeyBookmark3 & ~WWKEY_VK_BIT); + KeyBookmark4 = (KeyNumType)(KeyBookmark4 & ~WWKEY_VK_BIT); + KeySelectView = (KeyNumType)(KeySelectView & ~WWKEY_VK_BIT); + KeyRepair = (KeyNumType)(KeyRepair & ~WWKEY_VK_BIT); + KeyRepairOn = (KeyNumType)(KeyRepairOn & ~WWKEY_VK_BIT); + KeyRepairOff = (KeyNumType)(KeyRepairOff & ~WWKEY_VK_BIT); + KeySell = (KeyNumType)(KeySell & ~WWKEY_VK_BIT); + KeySellOn = (KeyNumType)(KeySellOn & ~WWKEY_VK_BIT); + KeySellOff = (KeyNumType)(KeySellOff & ~WWKEY_VK_BIT); + KeyMap = (KeyNumType)(KeyMap & ~WWKEY_VK_BIT); + KeySidebarUp = (KeyNumType)(KeySidebarUp & ~WWKEY_VK_BIT); + KeySidebarDown = (KeyNumType)(KeySidebarDown & ~WWKEY_VK_BIT); + KeyOption1 = (KeyNumType)(KeyOption1 & ~WWKEY_VK_BIT); + KeyOption2 = (KeyNumType)(KeyOption2 & ~WWKEY_VK_BIT); + KeyScrollLeft = (KeyNumType)(KeyScrollLeft & ~WWKEY_VK_BIT); + KeyScrollRight = (KeyNumType)(KeyScrollRight & ~WWKEY_VK_BIT); + KeyScrollUp = (KeyNumType)(KeyScrollUp & ~WWKEY_VK_BIT); + KeyScrollDown = (KeyNumType)(KeyScrollDown & ~WWKEY_VK_BIT); + KeyQueueMove1 = (KeyNumType)(KeyQueueMove1 & ~WWKEY_VK_BIT); + KeyQueueMove2 = (KeyNumType)(KeyQueueMove2 & ~WWKEY_VK_BIT); + KeyTeam1 = (KeyNumType)(KeyTeam1 & ~WWKEY_VK_BIT); + KeyTeam2 = (KeyNumType)(KeyTeam2 & ~WWKEY_VK_BIT); + KeyTeam3 = (KeyNumType)(KeyTeam3 & ~WWKEY_VK_BIT); + KeyTeam4 = (KeyNumType)(KeyTeam4 & ~WWKEY_VK_BIT); + KeyTeam5 = (KeyNumType)(KeyTeam5 & ~WWKEY_VK_BIT); + KeyTeam6 = (KeyNumType)(KeyTeam6 & ~WWKEY_VK_BIT); + KeyTeam7 = (KeyNumType)(KeyTeam7 & ~WWKEY_VK_BIT); + KeyTeam8 = (KeyNumType)(KeyTeam8 & ~WWKEY_VK_BIT); + KeyTeam9 = (KeyNumType)(KeyTeam9 & ~WWKEY_VK_BIT); + KeyTeam10 = (KeyNumType)(KeyTeam10 & ~WWKEY_VK_BIT); +#endif +} + + +/*********************************************************************************************** + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Revamped and tightened up. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Save_Settings (void) +{ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + + /* + ** Load any existing options file because it may contain entries that won't be + ** explicitly written out by this routine. By preloading the database, these entries + ** will be carried over. + */ + if (file.Is_Available()) { + ini.Load(file); + } + + /* + ** Save Options settings + */ + static char const * const OPTIONS = "Options"; + ini.Put_Int(OPTIONS, "GameSpeed", GameSpeed); + ini.Put_Int(OPTIONS, "ScrollRate", ScrollRate); + ini.Put_Fixed(OPTIONS, "Brightness", Brightness); + ini.Put_Fixed(OPTIONS, "Volume", Volume); +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_NORMAL ) // Save only when non-multiplayer. + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#else + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#endif +#ifdef FIXIT_VERSION_3 + ini.Put_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + ini.Put_Fixed(OPTIONS, "Contrast", Contrast); + ini.Put_Fixed(OPTIONS, "Color", Saturation); + ini.Put_Fixed(OPTIONS, "Tint", Tint); + ini.Put_Bool(OPTIONS, "AutoScroll", AutoScroll); + ini.Put_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat); + ini.Put_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle); + ini.Put_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + ini.Put_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + ini.Put_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + ini.Put_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + ini.Put_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + ini.Put_Int(HotkeyName, "KeySelect1", KeySelect1); + ini.Put_Int(HotkeyName, "KeySelect2", KeySelect2); + ini.Put_Int(HotkeyName, "KeyScatter", KeyScatter); + ini.Put_Int(HotkeyName, "KeyStop", KeyStop); + ini.Put_Int(HotkeyName, "KeyGuard", KeyGuard); + ini.Put_Int(HotkeyName, "KeyNext", KeyNext); + ini.Put_Int(HotkeyName, "KeyPrevious", KeyPrevious); + ini.Put_Int(HotkeyName, "KeyFormation", KeyFormation); + ini.Put_Int(HotkeyName, "KeyHome1", KeyHome1); + ini.Put_Int(HotkeyName, "KeyHome2", KeyHome2); + ini.Put_Int(HotkeyName, "KeyBase", KeyBase); + ini.Put_Int(HotkeyName, "KeyResign", KeyResign); + ini.Put_Int(HotkeyName, "KeyAlliance", KeyAlliance); + ini.Put_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + ini.Put_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + ini.Put_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + ini.Put_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + ini.Put_Int(HotkeyName, "KeySelectView", KeySelectView); + ini.Put_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + ini.Put_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + ini.Put_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + ini.Put_Int(HotkeyName, "KeySellToggle", KeySell); + ini.Put_Int(HotkeyName, "KeySellOn", KeySellOn); + ini.Put_Int(HotkeyName, "KeySellOff", KeySellOff); + ini.Put_Int(HotkeyName, "KeyMapToggle", KeyMap); + ini.Put_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + ini.Put_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + ini.Put_Int(HotkeyName, "KeyOption1", KeyOption1); + ini.Put_Int(HotkeyName, "KeyOption2", KeyOption2); + ini.Put_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + ini.Put_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + ini.Put_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + ini.Put_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + ini.Put_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + ini.Put_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + ini.Put_Int(HotkeyName, "KeyTeam1", KeyTeam1); + ini.Put_Int(HotkeyName, "KeyTeam2", KeyTeam2); + ini.Put_Int(HotkeyName, "KeyTeam3", KeyTeam3); + ini.Put_Int(HotkeyName, "KeyTeam4", KeyTeam4); + ini.Put_Int(HotkeyName, "KeyTeam5", KeyTeam5); + ini.Put_Int(HotkeyName, "KeyTeam6", KeyTeam6); + ini.Put_Int(HotkeyName, "KeyTeam7", KeyTeam7); + ini.Put_Int(HotkeyName, "KeyTeam8", KeyTeam8); + ini.Put_Int(HotkeyName, "KeyTeam9", KeyTeam9); + ini.Put_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + /* + ** Write the INI data out to a file. + */ + ini.Save(file); +} + + +/*********************************************************************************************** + * OptionsClass::Set -- Sets options based on current settings * + * * + * Use this routine to adjust the palette or sound settings after a fresh scenario load. * + * It assumes the values needed are already loaded into OptionsClass. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void OptionsClass::Set(void) +{ + Set_Brightness(Brightness); + Set_Contrast(Contrast); + Set_Saturation(Saturation); + Set_Tint(Tint); + Set_Sound_Volume(Volume, false); + Set_Score_Volume(ScoreVolume, false); + Set_Repeat(IsScoreRepeat); + Set_Shuffle(IsScoreShuffle); +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * * + * This routine is used to adjust delay factors that MUST be synchronized on all machines * + * but should maintain a speed as close to constant as possible. Building animations are * + * a good example of this. * + * * + * INPUT: delay -- The normal delay factor. * + * * + * OUTPUT: Returns with the delay to use that has been modified so that a reasonably constant * + * rate will result. * + * * + * WARNINGS: This calculation is crude due to the coarse resolution that a 1/15 second timer * + * allows. * + * * + * Use of this routine ASSUMES that the GameSpeed is synchronized on all machines. * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + * 06/30/1995 JLB : Handles low values in a more consistent manner. * + *=============================================================================================*/ +int OptionsClass::Normalize_Delay(int delay) const +{ + static int _adjust[][8] = { + {2,2,1,1,1,1,1,1}, + {3,3,3,2,2,2,1,1}, + {5,4,4,3,3,2,2,1}, + {7,6,5,4,4,4,3,2} + }; + if (delay) { + if (delay < 5) { + delay = _adjust[delay-1][GameSpeed]; + } else { + delay = ((delay * 8) / (GameSpeed+1)); + } + } + return(delay); +} + + +/*********************************************************************************************** + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * * + * This routine is used to adjust the real palette to match the values for the palette * + * control. The adjusted palette is placed into the palette buffer specified. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The palette is not actually set by this routine. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Fixup_Palette(void) const +{ + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + CCPalette = InGamePalette; +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * * + * This routine will take a relative volume value and convert it to the real volume value * + * to use. This allows all the game volumes to be corrected to the correct global volume. * + * * + * INPUT: volume -- Requested volume level. * + * * + * OUTPUT: Returns with the actual volume level to use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Normalize_Volume(int volume) const +{ + return(volume * Volume); +} diff --git a/CODE/OPTIONS.H b/CODE/OPTIONS.H new file mode 100644 index 0000000..13b9c28 --- /dev/null +++ b/CODE/OPTIONS.H @@ -0,0 +1,160 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OPTIONS.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +class OptionsClass { + public: + enum { + MAX_SCROLL_SETTING=7, + MAX_SPEED_SETTING=7 + }; + + OptionsClass(void); + + void One_Time(void); + void Process(void); + + void Fixup_Palette(void) const; + void Set_Shuffle(int on); + void Set_Repeat(int on); + void Set_Score_Volume(fixed volume, bool feedback); + void Set_Sound_Volume(fixed volume, bool feedback); + void Set_Brightness(fixed brightness); + fixed Get_Brightness(void) const; + void Set_Saturation(fixed color); + fixed Get_Saturation(void) const; + void Set_Contrast(fixed contrast); + fixed Get_Contrast(void) const; + void Set_Tint(fixed tint); + fixed Get_Tint(void) const; + int Normalize_Delay(int delay) const; + int Normalize_Volume(int volume) const; + + /* + ** File I/O routines + */ + void Load_Settings(void); + void Save_Settings(void); + + void Set(void); + + /* + ** This is actually the delay between game frames expressed as 1/60 of + ** a second. The default value is 4 (1/15 second). + */ + unsigned int GameSpeed; + + int ScrollRate; // Distance to scroll. + fixed Volume; // Volume for sound effects. + fixed ScoreVolume; // Volume for scores. +#ifdef FIXIT_VERSION_3 + fixed MultiScoreVolume; // Volume for scores during multiplayer games. +#endif + fixed Brightness; // Brightness. + fixed Tint; // Hue + fixed Saturation; // Saturation + fixed Contrast; // Value + unsigned AutoScroll:1; // Does map autoscroll? + unsigned IsScoreRepeat:1; // Score should repeat? + unsigned IsScoreShuffle:1; // Score list should shuffle? + unsigned IsPaletteScroll:1;// Allow palette scrolling? + + /* + ** These are the hotkeys used for keyboard control. + */ + KeyNumType KeyForceMove1; + KeyNumType KeyForceMove2; + KeyNumType KeyForceAttack1; + KeyNumType KeyForceAttack2; + KeyNumType KeySelect1; + KeyNumType KeySelect2; + KeyNumType KeyScatter; + KeyNumType KeyStop; + KeyNumType KeyGuard; + KeyNumType KeyNext; + KeyNumType KeyPrevious; + KeyNumType KeyFormation; + KeyNumType KeyHome1; + KeyNumType KeyHome2; + KeyNumType KeyBase; + KeyNumType KeyResign; + KeyNumType KeyAlliance; + KeyNumType KeyBookmark1; + KeyNumType KeyBookmark2; + KeyNumType KeyBookmark3; + KeyNumType KeyBookmark4; + KeyNumType KeySelectView; + KeyNumType KeyRepair; + KeyNumType KeyRepairOn; + KeyNumType KeyRepairOff; + KeyNumType KeySell; + KeyNumType KeySellOn; + KeyNumType KeySellOff; + KeyNumType KeyMap; + KeyNumType KeySidebarUp; + KeyNumType KeySidebarDown; + KeyNumType KeyOption1; + KeyNumType KeyOption2; + KeyNumType KeyScrollLeft; + KeyNumType KeyScrollRight; + KeyNumType KeyScrollUp; + KeyNumType KeyScrollDown; + KeyNumType KeyQueueMove1; + KeyNumType KeyQueueMove2; + KeyNumType KeyTeam1; + KeyNumType KeyTeam2; + KeyNumType KeyTeam3; + KeyNumType KeyTeam4; + KeyNumType KeyTeam5; + KeyNumType KeyTeam6; + KeyNumType KeyTeam7; + KeyNumType KeyTeam8; + KeyNumType KeyTeam9; + KeyNumType KeyTeam10; + + void Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const; + protected: + + private: + + static char const * const HotkeyName; +}; + + +#endif diff --git a/CODE/OPTIONS.LNT b/CODE/OPTIONS.LNT new file mode 100644 index 0000000..46e6057 --- /dev/null +++ b/CODE/OPTIONS.LNT @@ -0,0 +1,26 @@ +// Please note -- this is a representative set of error suppression +// options. Please adjust to suit your own policies +// See PC-lint for C/C++ manual (chapter LIVING WITH LINT) +// for further details. + +-e502 -e713 -e737 -eau // don't report on signed/unsigned mismatches +-e734 // allow sub-integer loss of information +-e701 -e703 // shifting int left is OK +-e537 // don't care about repeated include file +-e641 // converting enum to int is ok +-e1042 // operator ++/-- don't need class parameters +-e963 -e763 // redundant declarations are ok +-e1712 // no default constructor defined is ok +-e1704 // private constructors are ok +-e534 // ignoring return value is ok +-e732 // going from signed to unsigned parameter is ok +-e1411 // functions hiding base functions is ok +-e788 // switch with default doesn't need all values specified +-e655 -e656 // compatable enum bit and arithmetic operations are ok +-e1542 // members possibly not initialized isn't a valid warning +-e522 // calling 'new' without assignment isn't always an error + + +-e1401 // uninitialized by constructor warning disabled. + + diff --git a/CODE/OVERLAY.CPP b/CODE/OVERLAY.CPP new file mode 100644 index 0000000..9d087d9 --- /dev/null +++ b/CODE/OVERLAY.CPP @@ -0,0 +1,384 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OVERLAY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OVERLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * OverlayClass::Init -- Resets the overlay object system. * + * OverlayClass::Mark -- Marks the overlay down on the map. * + * OverlayClass::OverlayClass -- Overlay object constructor. * + * OverlayClass::delete -- Returns a overlay object to the pool. * + * OverlayClass::new -- Allocates a overlay object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "overlay.h" + + +HousesType OverlayClass::ToOwn = HOUSE_NONE; + + + +/*********************************************************************************************** + * OverlayClass::Init -- Resets the overlay object system. * + * * + * This routine resets the overlay object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Init(void) +{ + Overlays.Free_All(); +} + + +/*********************************************************************************************** + * OverlayClass::new -- Allocates a overlay object from pool * + * * + * This routine is used to allocate a overlay object from the * + * overlay object pool. * + * * + * INPUT: size -- The size of a overlay object (not used). * + * * + * OUTPUT: Returns with a pointer to an available overlay object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * OverlayClass::operator new(size_t ) +{ + void * ptr = Overlays.Allocate(); + if (ptr) { + ((OverlayClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * OverlayClass::delete -- Returns a overlay object to the pool. * + * * + * This routine will return a overlay object to the overlay object * + * pool. A overlay so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::operator delete(void * ptr) +{ + if (ptr) { + ((OverlayClass *)ptr)->IsActive = false; + } + Overlays.Free((OverlayClass *)ptr); +} + + +/*********************************************************************************************** + * OverlayClass::OverlayClass -- Overlay object constructor. * + * * + * This is the constructor for a overlay object. * + * * + * INPUT: type -- The overlay object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +OverlayClass::OverlayClass(OverlayType type, CELL pos, HousesType house) : + ObjectClass(RTTI_OVERLAY, Overlays.ID(this)), + Class(OverlayTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + Unlimbo(Cell_Coord(pos)); + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * OverlayClass::Mark -- Marks the overlay down on the map. * + * * + * This routine will place the overlay onto the map. The overlay object is deleted by this * + * operation. The map is updated to reflect the presence of the overlay. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the overlay successfully marked? Failure occurs if it is not being * + * marked down. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool OverlayClass::Mark(MarkType mark) +{ + assert(Overlays.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL cell = Coord_Cell(Coord); + CellClass * cellptr = &Map[cell]; + + /* + ** Walls have special logic when they are marked down. + */ + if (Class->IsWall) { + if (cellptr->Is_Clear_To_Build()) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + cellptr->Wall_Update(); + Map.Zone_Reset(Class->IsCrushable ? MZONE_NORMAL : MZONE_NORMAL|MZONE_CRUSHER); + + /* + ** Flag ownership of the cell if the 'global' ownership flag indicates that this + ** is necessary for the overlay. + */ + if (ToOwn != HOUSE_NONE) { + cellptr->Owner = ToOwn; + } + + } else { + delete this; + return(false); + } + } else { + + bool clear = false; + if (!ScenarioInit) { + if (Class->Type == OVERLAY_WATER_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_FLOAT, false, false); + } else { + if (Class->Type == OVERLAY_STEEL_CRATE || Class->Type == OVERLAY_WOOD_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false); + } else { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true); + } + } + } else { + clear = true; + } + + if ((ScenarioInit || cellptr->Overlay == OVERLAY_NONE) && clear) { + + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + + cellptr->Redraw_Objects(); + if (Class->Land == LAND_TIBERIUM) { + cellptr->OverlayData = 1; + cellptr->Tiberium_Adjust(); + } + } + } + + /* + ** ***** Is this really needed? + */ + cellptr->Recalc_Attributes(); + + /* + ** Remove the overlay and make sure the system thinks it was never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * * + * This routine is used to load a scenario's overlay data. The overlay objects are read * + * from the INI file and then created on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: Requires that all the buildings be placed first, so the scan for assigning wall * + * ownership to the nearest building will work. * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Specifically forbid manual crates in multiplayer scenarios. * + *=============================================================================================*/ +void OverlayClass::Read_INI(CCINIClass & ini) +{ + if (NewINIFormat > 1) { + int len = ini.Get_UUBlock("OverlayPack", _staging_buffer, sizeof(_staging_buffer)); + + if (len > 0) { + BufferStraw bpipe(_staging_buffer, len); + LCWStraw uncomp(LCWStraw::DECOMPRESS); + uncomp.Get_From(&bpipe); + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + OverlayType classid; + + uncomp.Get(&classid, sizeof(classid)); + + if (classid != OVERLAY_NONE) { + + if (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } + } + } + + if (NewINIFormat < 2 || ini.Is_Present("Overlay")) { + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + CELL cell = atoi(entry); + OverlayType classid = ini.Get_OverlayType(INI_Name(), entry, OVERLAY_NONE); + + /* + ** Don't allow placement of crates in the multiplayer scenarios. + */ + if (classid != OVERLAY_NONE && (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate)) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } +} + + +void OverlayClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + ini.Clear("OverlayPack"); + + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + LCWPipe comppipe(LCWPipe::COMPRESS); + + comppipe.Put_To(&bpipe); + + int total = 0; + CellClass * cellptr = &Map[(CELL)0]; + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + total += comppipe.Put(&cellptr->Overlay, sizeof(cellptr->Overlay)); + cellptr++; + } + if (total) { + ini.Put_UUBlock("OverlayPack", _staging_buffer, total); + } + +// for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { +// CellClass * cellptr = &Map[index]; +// if (cellptr->Overlay != OVERLAY_NONE) { +// char uname[10]; +// sprintf(uname, "%d", index); +// ini.Put_Overlay(INI_Name(), uname, cellptr->Overlay); +// } +// } +} diff --git a/CODE/OVERLAY.H b/CODE/OVERLAY.H new file mode 100644 index 0000000..d775bcf --- /dev/null +++ b/CODE/OVERLAY.H @@ -0,0 +1,95 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/OVERLAY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OVERLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the overlay object. Overlay objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class OverlayClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the overlay object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + OverlayClass(OverlayType type, CELL pos=-1, HousesType = HOUSE_NONE); + OverlayClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~OverlayClass(void) {if (GameActive) OverlayClass::Limbo();Class=0;}; + operator OverlayType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "OVERLAY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Virtual support functionality. + */ + virtual bool Mark(MarkType); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + private: + /* + ** This is used to control the marking process of the overlay. If this is + ** set to a valid house number, then the cell that the overlay is marked down + ** upon will be flagged as being owned by the specified house. + */ + static HousesType ToOwn; +}; + +#endif diff --git a/CODE/PACKET.CPP b/CODE/PACKET.CPP new file mode 100644 index 0000000..e72df70 --- /dev/null +++ b/CODE/PACKET.CPP @@ -0,0 +1,463 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include +#include + +enum {false=0,true=1}; +typedef int bool; + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass(void) +{ + FieldClass *current; + FieldClass *next; + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete current; + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *(unsigned short *)curbuf; + curbuf += sizeof (unsigned short); + Size = ntohs(Size); + ID = *(short *)curbuf; + curbuf += sizeof (short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *(unsigned short *)curbuf = (unsigned short)htons((unsigned short)size); + curbuf += sizeof (unsigned short); + *(short *)curbuf = htons(ID); + curbuf += sizeof (short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + + // + // If there is any pad left over, make sure you memset it + // to zeros, so it looks like a pad. + // + if (pad) { + memset(curbuf, 0, pad); + curbuf += pad; + } + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, min(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} diff --git a/CODE/PACKET.H b/CODE/PACKET.H new file mode 100644 index 0000000..78835bc --- /dev/null +++ b/CODE/PACKET.H @@ -0,0 +1,101 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __PACKET_H +#define __PACKET_H + + +#include "field.h" + +class PacketClass { + public: + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(void); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + bool Get_Field(char *id, char &data); + bool Get_Field(char *id, unsigned char &data); + bool Get_Field(char *id, short &data); + bool Get_Field(char *id, unsigned short &data); + bool Get_Field(char *id, long &data); + bool Get_Field(char *id, unsigned long &data); + bool Get_Field(char *id, char *data); + bool Get_Field(char *id, void *data, int &length); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + + +#endif diff --git a/CODE/PIPE.CPP b/CODE/PIPE.CPP new file mode 100644 index 0000000..8a84d8a --- /dev/null +++ b/CODE/PIPE.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * Pipe::Flush -- Flush all pending data out the pipe. * + * Pipe::Put -- Feed some data through the pipe. * + * Pipe::~Pipe -- Destructor for pipe class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pipe.h" +#include +#include + + +/*********************************************************************************************** + * Pipe::~Pipe -- Destructor for pipe class object. * + * * + * This destructor will unlink itself from any other pipes that it may be chained to. In * + * the process, it will flush any output it may have pending. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Pipe::~Pipe(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Put_To(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * * + * This routine will link two pipes together. The specified pipe will be fed data from * + * this pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that data will flow to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Pipe::Put_To(Pipe * pipe) +{ + if (ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + ChainTo->Flush(); + } + + ChainTo = pipe; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Pipe::Put -- Feed some data through the pipe. * + * * + * Use this to force feed data through the pipe. It is guaranteed to accept data as fast * + * as you can supply it. * + * * + * INPUT: source -- Pointer to the data to feed to this routine. * + * * + * length -- The number of bytes of data to submit. * + * * + * OUTPUT: Returns with the number of bytes actually output at the other far distant final * + * end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Put(void const * source, int length) +{ + if (ChainTo != NULL) { + return(ChainTo->Put(source, length)); + } + return(length); +} + + +/*********************************************************************************************** + * Pipe::Flush -- Flush all pending data out the pipe. * + * * + * Then the pipe needs to be flushed, this routine will be called. Since pipe segments * + * might have internal staging buffer for the data, this routine is necessary to force * + * all staging buffers to be clear. This routine is called when the pipe is being * + * destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Flush(void) +{ + if (ChainTo != NULL) { + return(ChainTo->Flush()); + } + return(0); +} + + diff --git a/CODE/PIPE.H b/CODE/PIPE.H new file mode 100644 index 0000000..33fa81b --- /dev/null +++ b/CODE/PIPE.H @@ -0,0 +1,92 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PIPE_H +#define PIPE_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** A "push through" pipe interface abstract class used for such purposes as compression +** and translation of data. In STL terms, this is functionally similar to an output +** iterator but with a few enhancements. A pipe class object that is not derived into +** another useful class serves only as a pseudo null-pipe. It will accept data but +** just throw it away but pretend that it sent it somewhere. +*/ +class Pipe +{ + public: + Pipe(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Pipe(void); + + virtual int Flush(void); + virtual int End(void) {return(Flush());} + virtual void Put_To(Pipe * pipe); + void Put_To(Pipe & pipe) {Put_To(&pipe);} + virtual int Put(void const * source, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Pipe * ChainTo; + Pipe * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Pipe(Pipe & rvalue); + Pipe & operator = (Pipe const & pipe); +}; + +#endif diff --git a/CODE/PK.CPP b/CODE/PK.CPP new file mode 100644 index 0000000..da8143f --- /dev/null +++ b/CODE/PK.CPP @@ -0,0 +1,358 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * PKey::Generate -- Generate a public and private key. * + * PKey::PKey -- Construct a key using encoded strings. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "pk.h" +#include "rndstraw.h" + + +/*********************************************************************************************** + * PKey::PKey -- Construct a key using encoded strings. * + * * + * This constructor will construct a key based on the encoded strings supplied. * + * * + * INPUT: exponent -- The encoded string for the exponent portion of the key. * + * * + * modulus -- The encoded string for the modulus portion of the key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey::PKey(void const * exponent, void const * modulus) +{ + Modulus.DERDecode((unsigned char *)modulus); + Exponent.DERDecode((unsigned char *)exponent); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * * + * This will store the modulus portion of the key into a buffer. The number of bytes * + * stored into the buffer depends on the value of the key. * + * * + * INPUT: buffer -- Pointer to the buffer that will hold the encoded modulus value. * + * * + * OUTPUT: Returns with the number of bytes stored to the buffer. * + * * + * WARNINGS: Be sure that the buffer can hold the encoded bytes. This is normally around the * + * same size as the Crypt_Block_Size() (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Modulus(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Modulus.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * * + * This routine will encode the exponent portion of the key. This is only necessary for the * + * slow key since the fast key always has an exponent of 65537. * + * * + * INPUT: buffer -- Pointer to the buffer that will be filled with the encoded exponent. * + * * + * OUTPUT: Returns with the nuber of bytes stored into the buffer. * + * * + * WARNINGS: Be sure the buffer is big enough to hold the encoded exponent. Usually this is * + * about the same size as the Crypt_Block_Size (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Exponent(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Exponent.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * * + * This is the counterpart to the Encode_Modulus() function. It will initialize the * + * modulus portion of the key with the encoded data supplied. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the previously encoded modulus value. * + * * + * OUTPUT: void * + * * + * WARNINGS: void * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Modulus(void * buffer) +{ + Modulus.DERDecode((unsigned char *)buffer); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * * + * This is the counterpart to the Encode_Exponent function. It will decode a previously * + * encoded exponent portion back into the key. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the encoded exponent value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Exponent(void * buffer) +{ + Exponent.DERDecode((unsigned char *)buffer); +} + + +/*********************************************************************************************** + * PKey::Generate -- Generate a public and private key. * + * * + * Public key cryptography relies on having two paired keys. The key used to encrypt * + * data must be decrypted by using the other key. Which key designated as the public or * + * private key is arbitrary. However, one is faster than the other. Use the faster key for * + * the more common operation. * + * * + * INPUT: random -- Reference to a source of random data. * + * * + * bits -- The number of bits to use for key generation. Use a number greater * + * than 16 but less than 2048. The ideal bit size is one that is evenly * + * divisible by 8 and then add one. Practical numbers range from 65 to * + * 1025 bits. * + * * + * fastkey -- Reference to the key that has fast encryption/decryption properties. * + * * + * slowkey -- Reference to the mate key of the other. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can take a very long time. It can take MINUTES to generate a * + * 1024 bit key (even on a Pentium Pro 200Mghz machine). * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + * 07/10/1996 JLB : Must supply source of random data. * + *=============================================================================================*/ +void PKey::Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey) +{ + /* + ** Key generation consists of create a key pair and then testing the key + ** pair. If the test fails, then repeat the process. The test and repeat + ** method is required since the prime number generating process can't + ** guarantee the generation of a prime number -- it can only generate a + ** highly likely prime number. + */ + for (;;) { + /* + ** Generate the two random prime numbers. This is the longest + ** step. + */ + BigInt p = Generate_Prime(random, bits, &p); + BigInt q = Generate_Prime(random, bits, &q); + + /* + ** The exponent factors are easy to calculate from the prime numbers. + */ + BigInt e = Fast_Exponent(); + BigInt n = p * q; + BigInt pqmin = (p-(unsigned short)1)*(q-(unsigned short)1); + BigInt d = e.Inverse(pqmin); + + /* + ** Store the data into the key objects. Notice that the modulus is the + ** same for both the fast and slow keys. Also notice that the exponent for + ** the fast key is ALWAYS 65537. Given this, it is possible to economize the + ** fast key into being just the modulus and the slow key to being just the + ** exponent (presuming the slow key also has access to the fast key so that + ** it can get the modulus). + */ + fastkey.Exponent = e; + fastkey.Modulus = n; + fastkey.BitPrecision = n.BitCount()-1; + + slowkey.Exponent = d; + slowkey.Modulus = n; + slowkey.BitPrecision = fastkey.BitPrecision; + + /* + ** Test the keys by encrypting a block of random bytes. If it decrypts + ** correctly, then a valid key pair has been generated -- bail. + */ + char before[256]; + char after[256]; + + for (int index = 0; index < fastkey.Plain_Block_Size(); index++) { + before[index] = (char)rand(); + } + fastkey.Encrypt(before, fastkey.Plain_Block_Size(), after); + slowkey.Decrypt(after, slowkey.Crypt_Block_Size(), after); + + /* + ** Compare the pre and post processing buffer. A match indicates + ** a valid key pair. + */ + if (memcmp(before, after, fastkey.Plain_Block_Size()) == 0) break; + } +} + + +/*********************************************************************************************** + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * * + * This routine will encrypt the supplied plaintext into cyphertext by processing the input * + * in block. The source is processed in whole blocks. Partial blocks are not supported by * + * public key cryptography. * + * * + * INPUT: source -- Pointer to the source plaintext that will be encrypted. * + * * + * length -- The length of the plaintext to encrypt. * + * * + * dest -- Pointer to the buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the number of cypher text bytes placed into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the output. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + + /* + ** Encrypt the source data in full blocks. Partial blocks are not processed and are not + ** copied to the destination buffer. + */ + while (slen >= Plain_Block_Size()) { + + /* + ** Perform the encryption of the block. + */ + BigInt temp = 0; + memmove(&temp, source, Plain_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Crypt_Block_Size()); + slen -= Plain_Block_Size(); + source = (char *)source + Plain_Block_Size(); + dest = (char *)dest + Crypt_Block_Size(); + total += Crypt_Block_Size(); + } + + return(total); +} + + +/*********************************************************************************************** + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * * + * This routine will process the supplied cyphertext by breaking it up into blocks and * + * then decrypting each block in turn. The block size is dependant upon the key. By NOT * + * embedding this information into the cypher data, it makes the encryption more secure. * + * * + * INPUT: source -- Pointer to the cypher text to be decrypted. * + * * + * length -- The number of cypher text bytes supplied to this routine. * + * * + * dest -- Pointer to the buffer to hold the plaintext. * + * * + * OUTPUT: Returns with the number of plaintext bytes output to the destination buffer. * + * * + * WARNINGS: Only whole blocks are processed. If the source has any partial block sized * + * data, then it will be left unprocessed. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Decrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + BigInt temp; + + /* + ** Decrypt the source data in full blocks. Partial blocks are not processed in any way. + */ + while (slen >= Crypt_Block_Size()) { + + /* + ** Perform the encryption. + */ + temp = 0; + memmove(&temp, source, Crypt_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Plain_Block_Size()); + slen -= Crypt_Block_Size(); + source = (char *)source + Crypt_Block_Size(); + dest = (char *)dest + Plain_Block_Size(); + total += Plain_Block_Size(); + } + + return(total); +} diff --git a/CODE/PK.H b/CODE/PK.H new file mode 100644 index 0000000..53e598f --- /dev/null +++ b/CODE/PK.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PK_H +#define PK_H + +#include "int.h" + +/* +** This class holds a public or private key used in Public Key Cryptography. It also serves +** as the conduit for encrypting/decrypting data using that key. Cryptography, using this +** method, has a couple of characteristics that affect how it is used. One, the process of +** encrypting/decrypting is very slow. This limits the effective quantity of data that can +** be processed. Two, the ciphertext is larger than the plaintext. This property generally +** limits its use to streaming data as opposed to random access data. The data is processed +** in blocks. The size of the ciphertext and plaintext blocks can be determined only from +** the key itself. +** +** A reasonable use of this technology would be to encrypt only critical data such as the +** password for a fast general purpose cryptographic algorithm. +*/ +class PKey +{ + public: + PKey(void) : Modulus(0), Exponent(0), BitPrecision(0) {} + PKey(void const * exponent, void const * modulus); // DER initialization. + + int Encrypt(void const * source, int slen, void * dest) const; + int Decrypt(void const * source, int slen, void * dest) const; + + static void Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey); + + int Plain_Block_Size(void) const {return((BitPrecision-1)/8);} + int Crypt_Block_Size(void) const {return(Plain_Block_Size()+1);} + int Block_Count(int plaintext_length) const {return((((plaintext_length-1)/Plain_Block_Size())+1));} + + int Encode_Modulus(void * buffer) const; + int Encode_Exponent(void * buffer) const; + + void Decode_Modulus(void * buffer); + void Decode_Exponent(void * buffer); + + static long Fast_Exponent(void) {return(65537L);} + + private: + + // p*q + BigInt Modulus; + + // 65537 or + // inverse of (p-1)(q-1). + BigInt Exponent; + + // Maximum bits allowed for block. + int BitPrecision; +}; + + +#endif diff --git a/CODE/PKPIPE.CPP b/CODE/PKPIPE.CPP new file mode 100644 index 0000000..d91682d --- /dev/null +++ b/CODE/PKPIPE.CPP @@ -0,0 +1,287 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PKPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * * + * File Name : PKPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/07/96 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * PKPipe::Put -- Submit data to the pipe for processing. * + * PKPipe::Put_To -- Chains one pipe to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkpipe.h" + + +/*********************************************************************************************** + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * * + * This will construct the public key pipe object. * + * * + * INPUT: control -- The method used to process the data flow (encrypt or decrypt). * + * * + * rnd -- Reference to a random number generate used to create the internal * + * blowfish key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKPipe::PKPipe(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowPipe::ENCRYPT : BlowPipe::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ +} + + +/*********************************************************************************************** + * PKPipe::Put_To -- Chains one pipe to another. * + * * + * This handles linking of one pipe to this pipe. Data will flow from this PKPipe to the * + * pipe segment specified. Special handling is done so that piping actually flows to the * + * embedded blowfish pipe and then flows to the designated pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that this pipe segment is to send data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Put_To(Pipe * pipe) +{ + if (BF.ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + BF.ChainTo = pipe; + if (pipe != NULL) { + pipe->ChainFrom = &BF; + } + BF.ChainFrom = this; + ChainTo = &BF; + } +} + + +/*********************************************************************************************** + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * * + * This routine must be called with a valid key pointer in order for encryption/description * + * to be performed on the data stream. Prior to calling this routine or after calling this * + * routine with a NULL pointer, the data stream will pass through this pipe without * + * modification. * + * * + * INPUT: key -- Pointer to the key to use for processing. Pass NULL if process is to be * + * terminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Key(PKey const * key) +{ + if (key == NULL) { + Flush(); + IsGettingKey = false; + } + CipherKey = key; + + if (CipherKey != NULL) { + IsGettingKey = true; + if (Control == DECRYPT) { + Counter = BytesLeft = Encrypted_Key_Length(); + } + } +} + + +/*********************************************************************************************** + * PKPipe::Put -- Submit data to the pipe for processing. * + * * + * This routine (if processing as been enabled by a previous key submission) will * + * encrypt or decrypt the data stream that passes through it. When encrypting, the data * + * stream will increase in size by about 10% (bit it varies according to the key used). * + * * + * INPUT: source -- Pointer to the data to be submitted to the pipe stream. * + * * + * length -- The number of bytes submitted. * + * * + * OUTPUT: Returns with the actual number of byte output at the final end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Put(void const * source, int length) +{ + /* + ** If the parameter seem illegal, then pass the pipe request to the + ** next pipe in the chain and let them deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Pipe::Put(source, length)); + } + + int total = 0; + + /* + ** Perform a special process if the this is the first part of the data flow. The special + ** key must be processed first. After this initial key processing, the rest of the data flow + ** is processed by the blowfish pipe and ignored by the PKPipe. + */ + if (IsGettingKey) { + + /* + ** When encrypting, first make the key block and then pass the data through the + ** normal blowfish processor. + */ + if (Control == ENCRYPT) { + + /* + ** Generate the largest blowfish key possible. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + int didput = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + total += Pipe::Put(Buffer, didput); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + + } else { + + /* + ** First try to accumulate a full key. + */ + int toget = (BytesLeft < length) ? BytesLeft : length; + memmove(&Buffer[Counter-BytesLeft], source, toget); + length -= toget; + BytesLeft -= toget; + source = (char *)source + toget; + + /* + ** If a full key has been accumulated, then decrypt it and feed the + ** key to the blowfish engine. + */ + if (BytesLeft == 0) { + char buffer[MAX_KEY_BLOCK_SIZE]; + CipherKey->Decrypt(Buffer, Counter, buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + } + } + } + + /* + ** If there are any remaining bytes to pipe through, then + ** pipe them through now -- they will be processed by the + ** blowfish engine. + */ + total += Pipe::Put(source, length); + return(total); +} + + +/*********************************************************************************************** + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/CODE/PKPIPE.H b/CODE/PKPIPE.H new file mode 100644 index 0000000..3033a3c --- /dev/null +++ b/CODE/PKPIPE.H @@ -0,0 +1,136 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PKPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PKPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/06/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PKPIPE_H +#define PKPIPE_H + +#include "pipe.h" +#include "pk.h" +#include "rndstraw.h" +#include "blowpipe.h" + + +/* +** This pipe will encrypt/decrypt the data stream. The data is encrypted by generating a +** symetric key that is then encrypted using the public key system. This symetric key is then +** used to encrypt the remaining data. +*/ +class PKPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKPipe(CryptControl control, RandomStraw & rnd); + + virtual void Put_To(Pipe * pipe); + virtual void Put_To(Pipe & pipe) {Put_To(&pipe);} + + // Feed data through for processing. + virtual int Put(void const * source, int length); + + // Submit key for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowPipe BF; + + /* + ** Controls the method of processing the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption/decryption. The actual process + ** performed is controlled by the Control member. A key can be used for + ** either encryption or decryption -- it makes no difference. However, whichever + ** process is performed, the opposite process must be performed using the + ** other key. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key (whichever is greater). + */ + char Buffer[MAX_KEY_BLOCK_SIZE]; + + /* + ** The working counter that holds the number of bytes in the staging buffer. + */ + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKPipe(PKPipe & rvalue); + PKPipe & operator = (PKPipe const & pipe); +}; + + +#endif diff --git a/CODE/PKSTRAW.CPP b/CODE/PKSTRAW.CPP new file mode 100644 index 0000000..6777ea5 --- /dev/null +++ b/CODE/PKSTRAW.CPP @@ -0,0 +1,304 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PKSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PKSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKStraw::Get -- Fetch data and process it accordingly. * + * PKStraw::Get_From -- Chains one straw to another. * + * PKStraw::Key -- Assign a key to the cipher process straw. * + * PKStraw::PKStraw -- Initialize the public key straw object. * + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + + +/*********************************************************************************************** + * PKStraw::PKStraw -- Initialize the public key straw object. * + * * + * This constructs the public key straw object. The operation to perform (encrypt or * + * decrypt) as well as a random number generator must be provided. * + * * + * INPUT: control -- What operation to perform on the data. Pass in either ENCRYPT or * + * DECRYPT. * + * * + * rnd -- Reference to a random number straw that is used internally to * + * generate the sub-key. The final strength of the cipher depends on * + * quality of this random number generator. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKStraw::PKStraw(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowStraw::ENCRYPT : BlowStraw::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ + Straw::Get_From(BF); +} + + +/*********************************************************************************************** + * PKStraw::Get_From -- Chains one straw to another. * + * * + * This routine handles the special case of this straw object in that there is an * + * embedded blowfish straw segment. It must be chained on correctly. * + * * + * INPUT: straw -- Pointer to the straw segment that this segment is to receive data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Get_From(Straw * straw) +{ + if (BF.ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + + BF.ChainTo = straw; + BF.ChainFrom = this; + ChainTo = &BF; + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = this; + } + } +} + + + +/*********************************************************************************************** + * PKStraw::Key -- Assign a key to the cipher process straw. * + * * + * This routine will assign the key (or NULL if the current key is to be removed) to the * + * cipher stream process. When a key has been assigned, encryption or decryption will * + * take place. In the absence (NULL key pointer) of a key, the data passes through * + * unchanged. * + * * + * INPUT: key -- Pointer to the key to assign to the stream. If the key pointer is NULL, * + * then this causes the cipher stream to stop processing the data and will * + * pass the data through unchanged. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the key passed to this routine is the opposite key to that used * + * to process the stream originally (when decrypting). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Key(PKey const * key) +{ + CipherKey = key; + if (key != NULL) { + IsGettingKey = true; + } + Counter = 0; + BytesLeft = 0; +} + + +/*********************************************************************************************** + * PKStraw::Get -- Fetch data and process it accordingly. * + * * + * This routine will fetch the number of bytes requested. If a valid key has been assigned * + * to this stream, then the data will be processed as it passes through. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of data bytes stored to the destination buffer. If * + * this number is less than that requested, then it indicates that the data source * + * has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Get(void * source, int length) +{ + /* + ** If the parameters seem invalid, then pass the request on so that someone + ** else can deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Straw::Get(source, length)); + } + + int total = 0; + + /* + ** The first part of the data flow must process the special key. After the special + ** key has been processed, the data flows through this straw without direct + ** modification (the blowfish straw will process the data). + */ + if (IsGettingKey) { + + if (Control == DECRYPT) { + + /* + ** Retrieve the pk encrypted blowfish key block. + */ + char cbuffer[MAX_KEY_BLOCK_SIZE]; + int got = Straw::Get(cbuffer, Encrypted_Key_Length()); + + /* + ** If the entire key block could not be retrieved, then this indicates + ** a major data flow error -- just return with no action performed. + */ + if (got != Encrypted_Key_Length()) return(0); + + /* + ** Decrypt the blowfish key and then activate the blowfish straw + ** with that key. + */ + CipherKey->Decrypt(cbuffer, got, Buffer); + BF.Key(Buffer, BLOWFISH_KEY_SIZE); + + } else { + + /* + ** Generate the blowfish key by using random numbers. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + Counter = BytesLeft = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + } + + /* + ** The first phase of getting the special key has been accomplished. Now, all + ** subsequent data is passed (unmodified) though this straw segment. The blowfish + ** straw takes over the compression/decompression from this point forward. + */ + IsGettingKey = false; + } + + /* + ** If there are any pending bytes in the buffer, then pass + ** these on first. The only time this should be is when the blowfish + ** key has first been generated. + */ + if (BytesLeft > 0) { + int tocopy = (length < BytesLeft) ? length : BytesLeft; + memmove(source, &Buffer[Counter-BytesLeft], tocopy); + source = (char *)source + tocopy; + BytesLeft -= tocopy; + length -= tocopy; + total += tocopy; + } + + /* + ** Any requested bytes that haven't been satisfied are copied over now by + ** drawing the data through the blowfish engine. The blowfish engine happens + ** to be linked to the chain so a normal Get() operation is sufficient. + */ + total += Straw::Get(source, length); + + return(total); +} + + +/*********************************************************************************************** + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/CODE/PKSTRAW.H b/CODE/PKSTRAW.H new file mode 100644 index 0000000..609057d --- /dev/null +++ b/CODE/PKSTRAW.H @@ -0,0 +1,125 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PKSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PKSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PKSTRAW_H +#define PKSTRAW_H + +#include "pk.h" +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + +class PKStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKStraw(CryptControl control, RandomStraw & rnd); + + virtual void Get_From(Straw * straw); + virtual void Get_From(Straw & straw) {Get_From(&straw);} + + virtual int Get(void * source, int slen); + + // Submit key to be used for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowStraw BF; + + /* + ** This control member tells what method (encryption or decryption) that should + ** be performed on the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption or decryption. If this pointer is NULL, then + ** the data passing through this segment will not be modified. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key size (whichever is + ** greater). + */ + char Buffer[256]; + + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKStraw(PKStraw & rvalue); + PKStraw & operator = (PKStraw const & straw); +}; + +#endif diff --git a/CODE/POWER.CPP b/CODE/POWER.CPP new file mode 100644 index 0000000..5ba557c --- /dev/null +++ b/CODE/POWER.CPP @@ -0,0 +1,478 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/POWER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : POWER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PowerClass::AI -- Process the power bar logic. * + * PowerClass::Draw_It -- Renders the power bar graphic. * + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * PowerClass::One_Time -- One time processing for the power bar. * + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Points to the shape to use for the "desired" power level indicator. +*/ +void const * PowerClass::PowerShape; +void const * PowerClass::PowerBarShape; + +PowerClass::PowerButtonClass PowerClass::PowerButton; + + +/*********************************************************************************************** + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * * + * This is the default constructor for the power bar class. It doesn't really do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +PowerClass::PowerClass(void) : + IsToRedraw(false), + IsActive(false), + FlashTimer(0), + RecordedDrain(-1), + RecordedPower(-1), + DesiredDrainHeight(0), + DesiredPowerHeight(0), + DrainHeight(0), + PowerHeight(0), + DrainBounce(0), + PowerBounce(0), + PowerDir(0), + DrainDir(0) +{ +} + + +/*********************************************************************************************** + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * * + * This routine is called in preparation for the start of a scenario. The power bar is * + * initialized into the null state by this routine. As soon as the scenario starts, the * + * power bar will rise to reflect the actual power output and drain. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Init_Clear(void) +{ + RadarClass::Init_Clear(); + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; + FlashTimer = 0; +} + + +/*********************************************************************************************** + * PowerClass::One_Time -- One time processing for the power bar. * + * * + * This routine is for code that truly only needs to be done once per game run. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void PowerClass::One_Time(void) +{ + RadarClass::One_Time(); + PowerButton.X = POWER_X * RESFACTOR; + PowerButton.Y = POWER_Y * RESFACTOR; + PowerButton.Width = (POWER_WIDTH * RESFACTOR)-1; + PowerButton.Height = POWER_HEIGHT * RESFACTOR; + PowerShape = MFCD::Retrieve("POWER.SHP"); + PowerBarShape = MFCD::Retrieve("POWERBAR.SHP"); +} + + +/*********************************************************************************************** + * PowerClass::Draw_It -- Renders the power bar graphic. * + * * + * This routine will draw the power bar graphic to the LogicPage. * + * * + * INPUT: complete -- Should the power bar be redrawn even if it isn't specifically flagged * + * to do so? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/27/1994 JLB : Changes power bar color depending on amount of power. * + *=============================================================================================*/ +void PowerClass::Draw_It(bool complete) +{ + static int _modtable[]={ + 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 + }; + + if (complete || IsToRedraw) { + BStart(BENCH_POWER); + + if (LogicPage->Lock()) { + if (Map.IsSidebarActive) { + IsToRedraw = false; + ShapeFlags_Type flags = SHAPE_NORMAL; + void const * remap = NULL; + + if (FlashTimer > 1 && ((FlashTimer % 3) & 0x01) != 0) { + flags = flags | SHAPE_FADING; + remap = Map.FadingRed; + } + +// LogicPage->Fill_Rect(POWER_X, POWER_Y, POWER_X+POWER_WIDTH-1, POWER_Y+POWER_HEIGHT-1, LTGREY); + CC_Draw_Shape(PowerBarShape, 0, 240 * RESFACTOR, 88 * RESFACTOR, WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); + +#ifdef WIN32 + /* + ** Hires power strip is too big to fit into a shape so it is in two parts + */ + CC_Draw_Shape(PowerBarShape, 1, 240 * RESFACTOR, (88 * RESFACTOR) + (56*RESFACTOR), WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); +#endif + /* + ** Determine how much the power production exceeds or falls short + ** of power demands. + */ + int bottom = (POWER_Y + POWER_HEIGHT - 1) * RESFACTOR; + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + power_height = Bound(power_height, 0, POWER_HEIGHT - 2); + drain_height = Bound(drain_height, 0, POWER_HEIGHT - 2); + + /* + ** Draw the power output graphic on top of the power bar framework. + */ + if (power_height) { + int color1 = 3; + int color2 = 4; + + if (PlayerPtr->Drain > PlayerPtr->Power) { + color1 = 214; + color2 = 211; + } + if (PlayerPtr->Drain > (PlayerPtr->Power * 2)) { + color1 = 235; + color2 = 230; + } + + /* + ** New power bar is in slightly different place + ** + ** Old power bar was 107 pixels high. New bar is 153 pixels high. + ** + ** ST - 5/2/96 11:23AM + */ +#ifdef WIN32 + power_height = (power_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); + drain_height = (drain_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); +#endif + bottom = (175*RESFACTOR)+1; + + LogicPage->Fill_Rect(245*RESFACTOR, bottom-power_height, 245*RESFACTOR+1, bottom, color2); + LogicPage->Fill_Rect(246*RESFACTOR, bottom-power_height, 246*RESFACTOR+1, bottom, color1); + } + + /* + ** Draw the power drain threshold marker. + */ + CC_Draw_Shape(PowerShape, 0, (POWER_X * RESFACTOR)+RESFACTOR, bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + } + LogicPage->Unlock(); + } + BEnd(BENCH_POWER); + } + RadarClass::Draw_It(complete); +} + + +/*********************************************************************************************** + * PowerClass::AI -- Process the power bar logic. * + * * + * Use this routine to process the power bar logic. This consists of animation effects. * + * * + * INPUT: input -- The player input value to be consumed or ignored as appropriate. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void PowerClass::AI(KeyNumType &input, int x, int y) +{ + if (Map.IsSidebarActive /*IsActive*/) { + int olddrain = DrainHeight; + int oldpower = PowerHeight; + + + /* + ** If the recorded power value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Power != RecordedPower) { + DesiredPowerHeight = Power_Height(PlayerPtr->Power); + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; + if (PowerHeight > DesiredPowerHeight) { + PowerDir = -1; + } else if (PowerHeight < DesiredPowerHeight) { + PowerDir = 1; + } else { + PowerBounce = 0; + } + } + + /* + ** If the recorded drain value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Drain != RecordedDrain) { + DesiredDrainHeight = Power_Height(PlayerPtr->Drain); + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; + if (DrainHeight > DesiredDrainHeight) { + DrainDir = -1; + } else if (DrainHeight < DesiredDrainHeight) { + DrainDir = 1; + } else { + DrainBounce = 0; + } + } + + if (DrainBounce && DrainHeight == DesiredDrainHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + DrainBounce--; + } else { + /* + ** If we need to move the drain height then do so. + */ + if (DrainHeight != DesiredDrainHeight) { + DrainHeight += DrainDir; + } + } + + if (PowerBounce && PowerHeight == DesiredPowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + PowerBounce--; + } else { + /* + ** If we need to move the power height then do so. + */ + if (PowerHeight != DesiredPowerHeight) { + PowerHeight += PowerDir; + } + } + + if (olddrain != DrainHeight || oldpower != PowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Flag to redraw if the power bar flash effect has expired. + */ +// if (FlashTimer == 1) { + if (FlashTimer > 0) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + RadarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * * + * This routine will examine a refresh list request and determine if the sidebar would be * + * affect. If so, it will flag the sidebar to be redrawn. * + * * + * INPUT: cell -- The cell that the offset list is base on. * + * * + * list -- The list of cell offset used to flag for redraw. If the special sidebar * + * affecting cell magic offset number is detected, the sidebar is flagged * + * for redraw and the magic offset is removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + RadarClass::Refresh_Cells(cell, list); +} + + +/*************************************************************************** + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * * + * INPUT: int value - the value we are testing * + * * + * OUTPUT: int the height of the point that this value is on graph * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 PWG : Created. * + *=========================================================================*/ +int PowerClass::Power_Height(int value) +{ + int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power + + /* + ** Loop through the different hundreds figuring out the fractional piece + ** of each. + */ + for (int lp = 0; lp < num; lp ++) { + retval = retval + (((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; + } + + /* + ** Adjust the retval to factor in the remainder + */ + if (value) { + retval = retval + (((((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); + } + + retval = Bound(retval, 0, POWER_HEIGHT-2); + return(retval); +} + + +/*********************************************************************************************** + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * * + * This routine handles input on the power bar area. Since no input is used for the power * + * bar, this routine just pops up appropriate help text for the power bar. * + * * + * INPUT: flags -- The event flags that triggered this action call. * + * * + * key -- The key code (if any) associated with the trigger event. * + * * + * OUTPUT: Should further button processing be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +{ + if (!Map.IsSidebarActive) { + return(false); + } + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + Map.Override_Mouse_Shape(MOUSE_NORMAL); + if (PlayerPtr->Power_Fraction() < 1 && PlayerPtr->Power > 0) { + Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } else { + Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } + GadgetClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * * + * This will cause the power bar to display with a flash so as to draw attention to * + * itself. Typical use of this effect is when power is low. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/14/1996 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Flash_Power(void) +{ + FlashTimer = TICKS_PER_SECOND; + IsToRedraw = true; + Flag_To_Redraw(false); +} diff --git a/CODE/POWER.H b/CODE/POWER.H new file mode 100644 index 0000000..3d37b7f --- /dev/null +++ b/CODE/POWER.H @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/POWER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : POWER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef POWER_H +#define POWER_H + +#include "radar.h" + +class PowerClass : public RadarClass +{ + public: + PowerClass(void); + PowerClass(NoInitClass const & x) : RadarClass(x), FlashTimer(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + + virtual void Init_Clear(void); // Clears all to known state + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Refresh_Cells(CELL cell, short const *list); + void Flash_Power(void); + + unsigned IsToRedraw:1; + + protected: + /* + ** This gadget is used to capture mouse input on the power bar. + */ + class PowerButtonClass : public GadgetClass { + public: + PowerButtonClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class PowerClass; + }; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static PowerButtonClass PowerButton; + + enum PowerEnums { + POWER_X=10*ICON_PIXEL_W, +#ifdef WIN32 + POWER_Y= (7+70+13), + POWER_HEIGHT=(200-(7+70+13)), +#else + POWER_Y= (88+9), + POWER_HEIGHT=80, +#endif + POWER_WIDTH=8, + POWER_LINE_SPACE=5, + POWER_LINE_WIDTH=3, + POWER_STEP_LEVEL=100, + POWER_STEP_FACTOR=5 + }; + + private: + int Power_Height(int value); + + unsigned IsActive:1; + + /* + ** If the power bar should be rendered with some flash effect then + ** this specifies the duration that the flash will occur. + */ + CDTimerClass FlashTimer; + + int RecordedDrain; + int RecordedPower; + int DesiredDrainHeight; + int DesiredPowerHeight; + int DrainHeight; + int PowerHeight; + int DrainBounce; + int PowerBounce; + short PowerDir; + short DrainDir; + + /* + ** Points to the shape to use for the "desired" power level indicator. + */ + static void const * PowerShape; + static void const * PowerBarShape; +}; + +#endif diff --git a/CODE/PROFILE.CPP b/CODE/PROFILE.CPP new file mode 100644 index 0000000..3759046 --- /dev/null +++ b/CODE/PROFILE.CPP @@ -0,0 +1,870 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/PROFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWGetPrivateProfileInt -- Fetches integer value from INI. * + * WWGetPrivateProfileString -- Fetch string from INI. * + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static char * WriteBinBuffer = NULL; +static int WriteBinBufferLen = 0; +static int WriteBinBufferPos = 0; +static int WriteBinBufferMax = 0; +static char * ReadBinBuffer = NULL; +static int ReadBinBufferLen = 0; +static int ReadBinBufferPos = 0; +static int ReadBinBufferMax = 0; + + +/*************************************************************************** + * Read_Private_Config_Struct -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config) +{ + INIClass ini; + ini.Load(file); + + config->DigitCard = ini.Get_Hex("Sound", "Card", 0); + config->IRQ = ini.Get_Int("Sound", "IRQ", 0); + config->DMA = ini.Get_Int("Sound", "DMA", 0); + config->Port = ini.Get_Hex("Sound", "Port", 0); + config->BitsPerSample= ini.Get_Int("Sound", "BitsPerSample", 0); + config->Channels = ini.Get_Int("Sound", "Channels", 0); + config->Reverse = ini.Get_Int("Sound", "Reverse", 0); + config->Speed = ini.Get_Int("Sound", "Speed", 0); + ini.Get_String("Language", "Language", NULL, config->Language, sizeof(config->Language)); + + +// config->DigitCard = WWGetPrivateProfileHex("Sound", "Card", profile); +// config->IRQ = WWGetPrivateProfileInt("Sound", "IRQ", 0,profile); +// config->DMA = WWGetPrivateProfileInt("Sound", "DMA", 0,profile); +// config->Port = WWGetPrivateProfileHex("Sound", "Port", profile); +// config->BitsPerSample= WWGetPrivateProfileInt("Sound", "BitsPerSample",0,profile); +// config->Channels = WWGetPrivateProfileInt("Sound", "Channels", 0,profile); +// config->Reverse = WWGetPrivateProfileInt("Sound", "Reverse", 0,profile); +// config->Speed = WWGetPrivateProfileInt("Sound", "Speed", 0,profile); +// WWGetPrivateProfileString("Language", "Language", NULL, config->Language, 3, profile); + + return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); +} + + +/*************************************************************************** + * Get_Private_Profile_Hex -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 MML : Created. * + *=========================================================================*/ +unsigned WWGetPrivateProfileHex (char const * section, char const * entry, char * profile) +{ + char buffer[16]; // Integer staging buffer. + unsigned card; + + memset (buffer, '0', sizeof(buffer)); // MAX_ENTRY_SIZE = 15 + buffer[sizeof(buffer)-1] = '\0'; + + WWGetPrivateProfileString(section, entry, "0", buffer, sizeof(buffer), profile); + + if (strlen (buffer) > 0) { + sscanf (buffer, "%x", &card); + } else { + card = 0; + } + + return(card); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileInt -- Fetches integer value. * + * * + * INPUT: * + * section section to read from * + * * + * entry name of entry to read * + * * + * def default value, if entry isn't found * + * * + * profile buffer containing INI data * + * * + * OUTPUT: * + * integer requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +int WWGetPrivateProfileInt(char const * section, char const * entry, int def, char * profile) +{ + char buffer[16]; // Integer staging buffer. + + /* + ** Store the default in the buffer. + */ + sprintf(buffer, "%d", def); + + /* + ** Get the buffer; use itself as the default. + */ + WWGetPrivateProfileString(section, entry, buffer, buffer, sizeof(buffer)-1, profile); + + /* + ** Convert to int & return. + */ + return(atoi(buffer)); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * * + * entry name of entry to write; if NULL, the entire section is deleted * + * * + * value value to write * + * * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileInt(char const * section, char const * entry, int value, char * profile) +{ + char buffer[250]; // Working section buffer. + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Generate string to save. + */ + sprintf(buffer, "%d", value); + + /* + ** Save the string. + */ + return(WWWritePrivateProfileString(section, entry, buffer, profile)); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileString -- Fetch game override string. * + * * + * INPUT: * + * section section name to read from * + * * + * entry name of entry to read; if NULL, all entry names are returned * + * * + * def default string to use if not found; can be NULL * + * * + * retbuffer buffer to store result in * + * * + * retlen max length of return buffer * + * * + * profile INI buffer * + * * + * OUTPUT: * + * ptr to entry found in INI buf; NULL if not found * + * * + * WARNINGS: * + * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * + * to disk. This routine must take this into consideration, by searching * + * for \n when scanning backward, and for \r when scanning forward. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +char * WWGetPrivateProfileString(char const * section, char const * entry, char const * def, char * retbuffer, int retlen, char const * profile) +{ + char const * workptr; // Working pointer into profile block. + char * altworkptr; // Alternate work pointer. + char sec[50]; // Working section buffer. + char const * retval; // Start of section or entry pointer. + char * next; // Pointer to start of next section (or EOF). + char c,c2; // Working character values. + int len; // Working substring length value. + int entrylen; // Byte length of specified entry. + char * orig_retbuf; // original retbuffer ptr + +// if (!retlen) return(NULL); + + /* + ** Fill in the default value just in case the entry could not be found. + */ + if (retbuffer) { + retbuffer[0] = '\0'; + if (retlen > 1 || retlen == 0) retbuffer[1] = '\0'; + if (def) { + strncpy(retbuffer, def, retlen); + } + retbuffer[retlen-1] = '\0'; + orig_retbuf = retbuffer; + } + + /* + ** Make sure a profile string was passed in + */ + if (!profile || !section) { + return(retbuffer); + } + + /* + ** Build section string to match file image. + */ + sprintf(sec, "[%s]", section); // sec = section name including []'s + strupr(sec); + len = strlen(sec); // len = section name length, incl []'s + + /* + ** Scan for a matching section + */ + retval = profile; + workptr = profile; + for (;;) { + + /* + ** 'workptr' = start of next section + */ + workptr = strchr(workptr, '['); + + /* + ** If the end has been reached without finding the desired section + ** then abort with a failure flag. + */ + if (!workptr) { + return(NULL); + } + + /* + ** 'c' = character just before the '[' + */ + if (workptr==profile) { + c = '\n'; + } else { + c = *(workptr-1); + } + + /* + ** If this is the section name & the character before is a newline, + ** process this section + */ + if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { + + /* + ** Skip work pointer to start of first valid entry. + */ + workptr += len; + while (isspace(*workptr)) { + workptr++; + } + + /* + ** If the section name is empty, we will have stepped onto the start + ** of the next section name; inserting new entries here will leave + ** a blank line between this section's name & 1st entry. So, check + ** for 2 newlines in a row & step backward. + */ + if (workptr - profile > 4) { + if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') + workptr -= 2; + } + + /* + ** 'next = end of section or end of file. + */ + next = strchr(workptr, '['); + for (;;) { + if (next) { + + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if (*(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = (char*)workptr + strlen(workptr)-1; + break; + } + } + + /* + ** If a specific entry was specified then return with the associated + ** string. + */ + if (entry) { + retval = workptr; + entrylen = strlen(entry); + + for (;;) { + /* + ** Search for the 1st character of the entry + */ + workptr = strchr(workptr, *entry); + + /* + ** If the end of the file has been reached or we have spilled + ** into the next section, then abort + */ + if (!workptr || workptr >= next) { + return(NULL); + } + + /* + ** 'c' = character before possible entry; must be a newline + ** 'c2' = character after possible entry; must be '=' or space + */ + c = *(workptr-1); + c2 = *(workptr+entrylen); + + /* + ** Entry found; extract it + */ + if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && + (c2 == '=' || isspace(c2))) { + retval = workptr; + workptr += entrylen; // skip entry name + workptr = strchr(workptr, '='); // find '=' + + /* + ** 'altworkptr' = next newline; \r is used here since we're + ** scanning forward! + */ + if (workptr) { + altworkptr = strchr(workptr, '\r'); // find next newline + } + + /* + ** Return if there was no '=', or if the newline is before + ** the next '=' + */ + if (workptr == NULL || altworkptr < workptr) { + return((char *)retval); + } + + /* + ** Skip any white space after the '=' and before the first + ** valid character of the parameter. + */ + workptr++; // Skip the '='. + while (isspace(*workptr)) { + + /* + ** Just return if there's no entry past the '='. + */ + if (workptr >= altworkptr) + return((char*)retval); + + workptr++; // Skip the whitespace + } + + /* + ** Copy the entry into the return buffer. + */ + len = (int)(altworkptr - workptr); + if (len > retlen-1) { + len = retlen-1; + } + + if (retbuffer) { + memcpy(retbuffer, workptr, len); + *(retbuffer + len) = '\0'; // Insert trailing null. + strtrim(retbuffer); + } + return((char*)retval); + } + + /* + ** Entry was not found; go to the next one + */ + workptr++; + } + } else { + + /* + ** No entry was specified, so build a list of all entries. + ** 'workptr' is at 1st entry after section name + ** 'next' is next bracket, or end of file + */ + retval = workptr; + + if (retbuffer) { + + /* + ** Keep accumulating the identifier strings in the retbuffer. + */ + while (workptr && workptr < next) { + altworkptr = strchr(workptr, '='); // find '=' + + if (altworkptr && altworkptr < next) { + int length; // Length of ID string. + + length = (int)(altworkptr - workptr); + + /* + ** Make sure we don't write past the end of the retbuffer; + ** add '3' for the 3 NULL's at the end + */ + if (retbuffer - orig_retbuf + length + 3 < retlen) { + memcpy(retbuffer, workptr, length); // copy entry name + *(retbuffer+length) = '\0'; // NULL-terminate it + strtrim(retbuffer); // trim spaces + retbuffer += strlen(retbuffer)+1; // next pos in dest buf + } else { + break; + } + + /* + ** Advance the work pointer to the start of the next line + ** by skipping the end of line character. + */ + workptr = strchr(altworkptr, '\n'); + if (!workptr) { + break; + } + workptr++; + } else { + + /* + ** If no '=', break out of loop + */ + break; + } + } + + /* + ** Final trailing terminator. Make double sure the double + ** trailing null is added. + */ + *retbuffer++ = '\0'; + *retbuffer++ = '\0'; + } + break; + } + } else { + + /* + ** Section name not found; go to the next bracket & try again + ** Advance past '[' and keep scanning. + */ + workptr++; + } + } + + return((char*)retval); +} + + +/*********************************************************************************************** + * WritePrivateProfileString -- Write a string to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * entry name of entry to write; if NULL, the section is deleted * + * string string to write; if NULL, the entry is deleted * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * This function has to translate newlines into \r\n sequences. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileString(char const * section, char const * entry, char const * string, char * profile) +{ + char buffer[250]; // Working section buffer + char const * offset; + char const * next; // ptr to next section + char c; // Working character value + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Try to find the section. WWGetPrivateProfileString with NULL entry name + ** will return all entry names in the given buffer, truncated to the given + ** buffer length. 'offset' will point to 1st entry in the section, NULL if + ** section not found. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + + /* + ** If the section could not be found, then add it to the end. Don't add + ** anything if a removal of an entry is requested (it is obviously already + ** non-existent). Make sure two newlines precede the section name. + */ + if (!offset && entry) { + sprintf(buffer, "\r\n[%s]\r\n", section); + strcat(profile, buffer); + } + + /* + ** If the section is there and 'entry' is NULL, remove the entire section + */ + if (offset && !entry) { + + /* + ** 'next = end of section or end of file. + */ + next = strchr(offset, '['); + for (;;) { + if (next) { + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if ( *(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = offset + strlen(offset); + break; + } + } + + /* + ** Remove the section + */ + strcpy((char*)offset, (char*)next); + + return(true); + } + + /* + ** Find the matching entry within the desired section. A NULL return buffer + ** with 0 length will just return the offset of the found entry, NULL if + ** entry not found. + */ + offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); + + /* + ** Remove any existing entry + */ + if (offset) { + int eol; // Working EOL offset. + + /* + ** Get # characters up to newline; \n is used since we're after the end + ** of this line + */ + eol = strcspn(offset, "\n"); + + /* + ** Erase the entry by strcpy'ing the entire INI file over this entry + */ + if (eol) { + strcpy((char*)offset, offset + eol + 1); + } + } else { + + /* + ** Entry doesn't exist, so point 'offset' to the 1st entry position in + ** the section. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + } + + /* + ** Add the desired entry. + */ + if (entry && string) { + + /* + ** Generate entry string. + */ + sprintf(buffer, "%s=%s\r\n", entry, string); + + /* + ** Make room for new entry. + */ + memmove((char*)offset+strlen(buffer), offset, strlen(offset)+1); + + /* + ** Copy the entry into the INI buffer. + */ + memcpy((char*)offset, buffer, strlen(buffer)); + } + + return(true); +} + + +char * Read_Bin_Buffer( void ) +{ + return( ReadBinBuffer ); +} + + +bool Read_Bin_Init( char * buffer, int length ) +{ + ReadBinBuffer = buffer; + ReadBinBufferLen = length; + ReadBinBufferPos = 0; + ReadBinBufferMax = 0; + return( true ); +} + + +int Read_Bin_Length( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferMax ); + } +} + + +bool Read_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != ReadBinBuffer || length <= 0 || length > 4 || + (ReadBinBufferPos + length) >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + memcpy( num, ptr, length ); + ReadBinBufferPos += length; + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } +} + + +int Read_Bin_Pos( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferPos ); + } +} + + +int Read_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + ReadBinBufferPos = pos; + return( ReadBinBufferPos ); + } +} + + +bool Read_Bin_String( char * string, char * buffer ) +{ + char * ptr; + unsigned char length; + + if (buffer != ReadBinBuffer || + ReadBinBufferPos >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + length = (unsigned char)*ptr++; + if ( (ReadBinBufferPos + length + 2) <= ReadBinBufferLen) { + memcpy( string, ptr, (unsigned int)(length + 1) ); + ReadBinBufferPos += (length + 2); + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } else { + return( false ); + } + } +} + + +char * Write_Bin_Buffer( void ) +{ + return( WriteBinBuffer ); +} + + +bool Write_Bin_Init( char * buffer, int length ) +{ + WriteBinBuffer = buffer; + WriteBinBufferLen = length; + WriteBinBufferPos = 0; + WriteBinBufferMax = 0; + return( true ); +} + + +int Write_Bin_Length( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferMax ); + } +} + + +bool Write_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length <= 0 || length > 4 || + (WriteBinBufferPos + length) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + memcpy( ptr, num, length ); + WriteBinBufferPos += length; + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + + +int Write_Bin_Pos( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferPos ); + } +} + + +int Write_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + WriteBinBufferPos = pos; + return( WriteBinBufferPos ); + } +} + + +bool Write_Bin_String( char * string, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length < 0 || length > 255 || + (WriteBinBufferPos + length + 2) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + *ptr++ = (char)length; + memcpy( ptr, string, (length + 1) ); + WriteBinBufferPos += (length + 2); + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + diff --git a/CODE/PROFILE.DEF b/CODE/PROFILE.DEF new file mode 100644 index 0000000..405636f --- /dev/null +++ b/CODE/PROFILE.DEF @@ -0,0 +1,402 @@ +# PRFILE CONQUER, COORD, TARGET, DISPLAY +# PRFILE OBJ +# PRCLASS CODE +# CODECLASS CODE + PRSYMBOL $$VMGETPAGE # Watches VM overhead. + NOPRFILE soundio.obj + NOPRFILE wwlib.lib, nomidi.lib, emu.lib, mathl.lib, cl.lib + PRSHOWVECTOR + # Symbols that shouldn't be profiled. + NOPRSYMBOL _DigiCallback + NOPRSYMBOL __MMODEL + NOPRSYMBOL __terminate + NOPRSYMBOL _abort + NOPRSYMBOL __restorezero + NOPRSYMBOL __cleanup + NOPRSYMBOL DGROUP@ + NOPRSYMBOL __checknull + +# Make sure that all symbols that start with "@$xt" +# or contain "$XL$" are not profiled. These appear to be +# mysterious internal routines and they break when profile vectored. + + NOPRSYMBOL "@$xt$n11BulletClass" + NOPRSYMBOL "@$xt$9FootClass" + NOPRSYMBOL "@$xt$32%ArrayOf$t13AircraftClass$ii$10%" + NOPRSYMBOL "@$xt$a10$13AircraftClass" + NOPRSYMBOL "@$xt$11BulletClass" + NOPRSYMBOL "@$xt$9FuseClass" + NOPRSYMBOL "@$xt$n13AircraftClass" + NOPRSYMBOL "@$xt$nc" + NOPRSYMBOL "@$xt$c" + NOPRSYMBOL "@$xt$13AircraftClass" + NOPRSYMBOL "@$xt$12RawFileClass" + NOPRSYMBOL "@$xt$9FileClass" + NOPRSYMBOL "@$xt$11CDFileClass" + NOPRSYMBOL "@$xt$12ExtFileClass" + NOPRSYMBOL "@$xt$15ObjectTypeClass" + NOPRSYMBOL "@$xt$11CCFileClass" + NOPRSYMBOL "@$xt$15TechnoTypeClass" + NOPRSYMBOL "@$xt$17AircraftTypeClass" + NOPRSYMBOL "@$xt$10RadioClass" + NOPRSYMBOL "@$xt$8FlyClass" + NOPRSYMBOL "@$xt$11TechnoClass" + NOPRSYMBOL "@$xt$11ObjectClass" + NOPRSYMBOL "@$xt$12FlasherClass" + NOPRSYMBOL "@$xt$10StageClass" + NOPRSYMBOL "@$xt$10CargoClass" + NOPRSYMBOL "@$xt$12MissionClass" + NOPRSYMBOL "@$xt$13AnimTypeClass" + NOPRSYMBOL "@$xt$n9AnimClass" + NOPRSYMBOL "@$xt$n11SmudgeClass" + NOPRSYMBOL "@$xt$27%ArrayOf$t9AnimClass$ii$25%" + NOPRSYMBOL "@$xt$a25$9AnimClass" + NOPRSYMBOL "@$xt$11SmudgeClass" + NOPRSYMBOL "@$xt$9AnimClass" + NOPRSYMBOL "@$xt$15BulletTypeClass" + NOPRSYMBOL "@$xt$n13BuildingClass" + NOPRSYMBOL "@$xt$10CacheClass" + NOPRSYMBOL "@$xt$13BuildingClass" + NOPRSYMBOL "@$xt$17BuildingTypeClass" + NOPRSYMBOL "@$xt$n18ReinforcementClass" + NOPRSYMBOL "@$xt$13InfantryClass" + NOPRSYMBOL "@$xt$9UnitClass" + NOPRSYMBOL "@$xt$32%ArrayOf$t13BuildingClass$ii$50%" + NOPRSYMBOL "@$xt$a50$13BuildingClass" + NOPRSYMBOL "@$xt$11TarComClass" + NOPRSYMBOL "@$xt$18ReinforcementClass" + NOPRSYMBOL "@$xt$8typeinfo" + NOPRSYMBOL "@$xt$11TurretClass" + NOPRSYMBOL "@$xt$10DriveClass" + NOPRSYMBOL "@$xt$30%ArrayOf$t11BulletClass$ii$20%" + NOPRSYMBOL "@$xt$a20$11BulletClass" + NOPRSYMBOL "@$xt$n13TemplateClass" + NOPRSYMBOL "@$xt$13TemplateClass" + NOPRSYMBOL "@$xt$17TemplateTypeClass" + NOPRSYMBOL "@$xt$12TerrainClass" + NOPRSYMBOL "@$xt$x15ObjectTypeClass" + NOPRSYMBOL "@$xt$16OverlayTypeClass" + NOPRSYMBOL "@$xt$15SmudgeTypeClass" + NOPRSYMBOL "@$xt$n12OverlayClass" + NOPRSYMBOL "@$xt$12OverlayClass" + NOPRSYMBOL "@$xt$n12MixFileClass" + NOPRSYMBOL "@$xt$n11CCFileClass" + NOPRSYMBOL "@$xt$16TerrainTypeClass" + NOPRSYMBOL "@$xt$13UnitTypeClass" + NOPRSYMBOL "@$xt$17InfantryTypeClass" + NOPRSYMBOL "@$xt$12MixFileClass" + NOPRSYMBOL "@$xt$n9UnitClass" + NOPRSYMBOL "@$xt$a16$9MonoClass" + NOPRSYMBOL "@$xt$10LogicClass" + NOPRSYMBOL "@$xt$12DisplayClass" + NOPRSYMBOL "@$xt$28%EMSListOf$tp12TriggerClass%" + NOPRSYMBOL "@$xt$8MapClass" + NOPRSYMBOL "@$xt$12SidebarClass" + NOPRSYMBOL "@$xt$9HelpClass" + NOPRSYMBOL "@$xt$a3$10LayerClass" + NOPRSYMBOL "@$xt$10LayerClass" + NOPRSYMBOL "@$xt$9MonoClass" + NOPRSYMBOL "@$xt$11MemoryClass" + NOPRSYMBOL "@$xt$16DebugMemoryClass" + NOPRSYMBOL "@$xt$14EMSMemoryClass" + NOPRSYMBOL "@$xt$27%EMSListOf$tp11ObjectClass%" + NOPRSYMBOL "@$xt$n10HouseClass" + NOPRSYMBOL "@$xt$a3$10HouseClass" + NOPRSYMBOL "@$xt$10HouseClass" + NOPRSYMBOL "@$xt$n13InfantryClass" + NOPRSYMBOL "@$xt$32%ArrayOf$t13InfantryClass$ii$50%" + NOPRSYMBOL "@$xt$a50$13InfantryClass" + NOPRSYMBOL "@$xt$n9MonoClass" + NOPRSYMBOL "@$xt$15DOSSegmentClass" + NOPRSYMBOL "@$xt$30%ArrayOf$t12OverlayClass$ii$3%" + NOPRSYMBOL "@$xt$a3$12OverlayClass" + NOPRSYMBOL "@$xt$x15TechnoTypeClass" + NOPRSYMBOL "@$xt$37%ArrayOf$t18ReinforcementClass$ii$15%" + NOPRSYMBOL "@$xt$a15$18ReinforcementClass" + NOPRSYMBOL "@$xt$29%ArrayOf$t11SmudgeClass$ii$1%" + NOPRSYMBOL "@$xt$a1$11SmudgeClass" + NOPRSYMBOL "@$xt$n12TerrainClass" + NOPRSYMBOL "@$xt$31%ArrayOf$t13TemplateClass$ii$1%" + NOPRSYMBOL "@$xt$a1$13TemplateClass" + NOPRSYMBOL "@$xt$32%ArrayOf$t12TerrainClass$ii$120%" + NOPRSYMBOL "@$xt$a120$12TerrainClass" + NOPRSYMBOL "@$xt$n12TriggerClass" + NOPRSYMBOL "@$xt$31%ArrayOf$t12TriggerClass$ii$26%" + NOPRSYMBOL "@$xt$a26$12TriggerClass" + NOPRSYMBOL "@$xt$12TriggerClass" + NOPRSYMBOL "@$xt$28%ArrayOf$t9UnitClass$ii$120%" + NOPRSYMBOL "@$xt$a120$9UnitClass" + + NOPRSYMBOL "cell.cpp_NONPUB_$XL$0" + NOPRSYMBOL "cell.cpp_NONPUB_$XL$1" + NOPRSYMBOL "combat.cpp_NONPUB_$XL$0" + NOPRSYMBOL "combat.cpp_NONPUB_$XL$1" + NOPRSYMBOL "display.cpp_NONPUB_$XL$0" + NOPRSYMBOL "display.cpp_NONPUB_$XL$1" + NOPRSYMBOL "display.cpp_NONPUB_$XL$2" + NOPRSYMBOL "display.cpp_NONPUB_$XL$3" + NOPRSYMBOL "display.cpp_NONPUB_$XL$4" + NOPRSYMBOL "display.cpp_NONPUB_$XL$5" + NOPRSYMBOL "foot.cpp_NONPUB_$XL$0" + NOPRSYMBOL "foot.cpp_NONPUB_$XL$1" + NOPRSYMBOL "globals.cpp_NONPUB_$XL$0" + NOPRSYMBOL "globals.cpp_NONPUB_$XL$1" + NOPRSYMBOL "house.cpp_NONPUB_$XL$0" + NOPRSYMBOL "house.cpp_NONPUB_$XL$1" + NOPRSYMBOL "house.cpp_NONPUB_$XL$2" + NOPRSYMBOL "house.cpp_NONPUB_$XL$3" + NOPRSYMBOL "idata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "idata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "idata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "idata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$0" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$1" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$2" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$3" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$4" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$5" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$6" + NOPRSYMBOL "infantry.cpp_NONPUB_$XL$7" + NOPRSYMBOL "init.cpp_NONPUB_$XL$0" + NOPRSYMBOL "init.cpp_NONPUB_$XL$1" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$0" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$1" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$2" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$3" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$4" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$5" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$6" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$7" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$8" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$9" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$10" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$11" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$12" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$13" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$14" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$15" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$16" + NOPRSYMBOL "mono.cpp_NONPUB_$XL$17" + NOPRSYMBOL "odata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "odata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "odata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "odata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "overlay.cpp_NONPUB_$XL$0" + NOPRSYMBOL "overlay.cpp_NONPUB_$XL$1" + NOPRSYMBOL "overlay.cpp_NONPUB_$XL$2" + NOPRSYMBOL "overlay.cpp_NONPUB_$XL$3" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$0" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$1" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$2" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$3" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$4" + NOPRSYMBOL "reinf.cpp_NONPUB_$XL$5" + NOPRSYMBOL "sdata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "sdata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "sdata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "sdata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "smudge.cpp_NONPUB_$XL$0" + NOPRSYMBOL "smudge.cpp_NONPUB_$XL$1" + NOPRSYMBOL "smudge.cpp_NONPUB_$XL$2" + NOPRSYMBOL "smudge.cpp_NONPUB_$XL$3" + NOPRSYMBOL "tdata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "tdata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "tdata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "tdata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "template.cpp_NONPUB_$XL$0" + NOPRSYMBOL "template.cpp_NONPUB_$XL$1" + NOPRSYMBOL "template.cpp_NONPUB_$XL$2" + NOPRSYMBOL "template.cpp_NONPUB_$XL$3" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$0" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$1" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$2" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$3" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$4" + NOPRSYMBOL "terrain.cpp_NONPUB_$XL$5" + NOPRSYMBOL "trigger.cpp_NONPUB_$XL$0" + NOPRSYMBOL "trigger.cpp_NONPUB_$XL$1" + NOPRSYMBOL "trigger.cpp_NONPUB_$XL$2" + NOPRSYMBOL "trigger.cpp_NONPUB_$XL$3" + NOPRSYMBOL "turret.cpp_NONPUB_$XL$0" + NOPRSYMBOL "turret.cpp_NONPUB_$XL$1" + NOPRSYMBOL "udata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "udata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "udata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "udata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$0" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$1" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$2" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$3" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$4" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$5" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$6" + NOPRSYMBOL "unit.cpp_NONPUB_$XL$7" + NOPRSYMBOL "building.cpp_NONPUB_$XL$0" + NOPRSYMBOL "building.cpp_NONPUB_$XL$1" + NOPRSYMBOL "building.cpp_NONPUB_$XL$2" + NOPRSYMBOL "building.cpp_NONPUB_$XL$3" + NOPRSYMBOL "building.cpp_NONPUB_$XL$4" + NOPRSYMBOL "building.cpp_NONPUB_$XL$5" + NOPRSYMBOL "building.cpp_NONPUB_$XL$6" + NOPRSYMBOL "building.cpp_NONPUB_$XL$7" + NOPRSYMBOL "building.cpp_NONPUB_$XL$8" + NOPRSYMBOL "building.cpp_NONPUB_$XL$9" + NOPRSYMBOL "building.cpp_NONPUB_$XL$10" + NOPRSYMBOL "building.cpp_NONPUB_$XL$11" + NOPRSYMBOL "aadata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "aadata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$0" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$1" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$2" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$3" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$4" + NOPRSYMBOL "aircraft.cpp_NONPUB_$XL$5" + NOPRSYMBOL "anim.cpp_NONPUB_$XL$0" + NOPRSYMBOL "anim.cpp_NONPUB_$XL$1" + NOPRSYMBOL "anim.cpp_NONPUB_$XL$2" + NOPRSYMBOL "anim.cpp_NONPUB_$XL$3" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$3" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$4" + NOPRSYMBOL "bdata.cpp_NONPUB_$XL$5" + NOPRSYMBOL "bullet.cpp_NONPUB_$XL$0" + NOPRSYMBOL "bullet.cpp_NONPUB_$XL$1" + NOPRSYMBOL "bullet.cpp_NONPUB_$XL$2" + NOPRSYMBOL "bullet.cpp_NONPUB_$XL$3" + NOPRSYMBOL "cdata.cpp_NONPUB_$XL$0" + NOPRSYMBOL "cdata.cpp_NONPUB_$XL$1" + NOPRSYMBOL "cdata.cpp_NONPUB_$XL$2" + NOPRSYMBOL "cdata.cpp_NONPUB_$XL$3" + + PRFILE AIRCRAFT.OBJ + PRFILE ANIM.OBJ + PRFILE BBDATA.OBJ + PRFILE BULLET.OBJ + PRFILE AADATA.OBJ + PRFILE ADATA.OBJ + PRFILE ALLOC.OBJ + PRFILE AUDIO.OBJ + PRFILE BDATA.OBJ + PRFILE BUTTON.OBJ + PRFILE BUILDING.OBJ + PRFILE CACHE.OBJ + PRFILE CARGO.OBJ + PRFILE CCFILE.OBJ + PRFILE CDATA.OBJ + PRFILE CDFILE.OBJ + PRFILE CELL.OBJ + PRFILE COMBAT.OBJ + PRFILE CONQUER.OBJ + PRFILE CONST.OBJ + PRFILE COORD.OBJ + PRFILE CREDITS.OBJ + PRFILE CREW.OBJ + PRFILE CURSOR.OBJ + PRFILE DEBUG.OBJ + PRFILE DIALOG.OBJ + PRFILE DISPLAY.OBJ + PRFILE DPMI.OBJ + PRFILE DRIVE.OBJ + PRFILE EXTFILE.OBJ + PRFILE FINDPATH.OBJ + PRFILE FLASHER.OBJ + PRFILE FLY.OBJ + PRFILE FOOT.OBJ + PRFILE FUSE.OBJ + PRFILE GLOBALS.OBJ + PRFILE HDATA.OBJ + PRFILE HOUSE.OBJ + PRFILE IDATA.OBJ + PRFILE INFANTRY.OBJ + PRFILE INI.OBJ + PRFILE INIT.OBJ + PRFILE JSHELL.OBJ + PRFILE LAYER.OBJ + PRFILE LOGIC.OBJ + PRFILE MAP.OBJ + PRFILE MAPEDDLG.OBJ + PRFILE MAPEDIT.OBJ + PRFILE MAPEDPLC.OBJ + PRFILE MAPEDSEL.OBJ + PRFILE MEMORY.OBJ + PRFILE MENUS.OBJ + PRFILE MISSION.OBJ + PRFILE MIXFILE.OBJ + PRFILE MONO.OBJ + PRFILE OBJECT.OBJ + PRFILE HELP.OBJ + PRFILE ODATA.OBJ + PRFILE OPTIONS.OBJ + PRFILE OVERLAY.OBJ + PRFILE PROFILE.OBJ + PRFILE RADIO.OBJ + PRFILE RAWFILE.OBJ + PRFILE REINF.OBJ + PRFILE SAVELOAD.OBJ + PRFILE SCENARIO.OBJ + PRFILE SCORE.OBJ + PRFILE SDATA.OBJ + PRFILE SIDEBAR.OBJ + PRFILE SMUDGE.OBJ + PRFILE STARTUP.OBJ + PRFILE SUPPORT.OBJ + PRFILE TARCOM.OBJ + PRFILE TARGET.OBJ + PRFILE TDATA.OBJ + PRFILE TECHNO.OBJ + PRFILE TEMPLATE.OBJ + PRFILE TERRAIN.OBJ + PRFILE TEXT.OBJ + PRFILE THEME.OBJ + PRFILE TRIGGER.OBJ + PRFILE TURRET.OBJ + PRFILE UDATA.OBJ + PRFILE UNIT.OBJ + + PRMODULE addlong.asm + PRMODULE iconset.c + PRMODULE addlong.asm + PRMODULE bitblit.asm + PRMODULE buff.asm + PRMODULE buffer.asm + PRMODULE buffpage.asm + PRMODULE button.c + PRMODULE clear.asm + PRMODULE dipthong.c + PRMODULE drawchar.asm + PRMODULE drawline.asm + PRMODULE drawrect.c + PRMODULE drawshp.asm + PRMODULE ems.asm + PRMODULE emsmem.c + PRMODULE fadeblit.c + PRMODULE fillrect.asm + PRMODULE font.c + PRMODULE iconset.c + PRMODULE iff.c + PRMODULE input.c + PRMODULE irandom.c + PRMODULE lib.c + PRMODULE load.c + PRMODULE locking.c + PRMODULE lpage.c + PRMODULE mem_copy.asm + PRMODULE palette.c + PRMODULE putpixel.asm + PRMODULE remap.asm + PRMODULE select.asm + PRMODULE shakescr.asm + PRMODULE shape.c + PRMODULE shapeinf.asm + PRMODULE sizeof.asm + PRMODULE stamp.asm + PRMODULE textprnt.asm + PRMODULE uncomp.asm + PRMODULE windows.c + PRSYMBOL _ldiv + PRSYMBOL __ldiv +# PRMODULE shape.c +# PRMODULE uncomp.asm + diff --git a/CODE/QUEUE.CPP b/CODE/QUEUE.CPP new file mode 100644 index 0000000..e1ade0b --- /dev/null +++ b/CODE/QUEUE.CPP @@ -0,0 +1,4555 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/QUEUE.CPP 6 3/14/97 5:12p Steve_tall $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : QUEUE.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/28/95 * + * * + * Last Update : October 14, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions for Queueing Events: * + * Queue_Mission -- Queue a mega mission event. * + * Queue_Options -- Queue the options event. * + * Queue_Exit -- Add the exit game event to the queue. * + * * + * Functions for processing Queued Events: * + * Queue_AI -- Process all queued events. * + * Queue_AI_Normal -- Process all queued events. * + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * Main Multiplayer Queue Logic: * + * Wait_For_Players -- Waits for other systems to come on-line * + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * Process_Send_Period -- timing for sending packets every 'n' frames * + * Send_Packets -- sends out events from the OutList * + * Send_FrameSync -- Sends a FRAMESYNC packet * + * Process_Receive_Packet -- processes an incoming packet * + * Process_Serial_Packet -- Handles an incoming serial packet * + * Can_Advance -- determines if it's OK to advance to the next frame * + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * Handle_Timeout -- attempts to reconnect; if fails, bails. * + * Stop_Game -- stops the game * + * * + * Packet Compression / Decompression: * + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * Add_Compressed_Events -- adds compressed events to a packet * + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * Extract_Uncompressed_Events -- extracts events from a packet * + * Extract_Compressed_Events -- extracts events from a packet * + * * + * DoList Management: * + * Execute_DoList -- Executes commands from the DoList * + * Clean_DoList -- Cleans out old events from the DoList * + * Queue_Record -- Records the DoList to disk * + * Queue_Playback -- plays back queue entries from a record file * + * * + * Debugging: * + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * Add_CRC -- Adds a value to a CRC * + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * Init_Queue_Mono -- inits mono display * + * Update_Queue_Mono -- updates mono display * + * Print_Framesync_Values -- displays frame-sync variables * + * Check_Mirror -- Checks mirror memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +bool bReconnectDialogCancelled; +#endif + + +/********************************** Defines *********************************/ +#define SHOW_MONO 0 + + +/********************************** Globals *********************************/ +//--------------------------------------------------------------------------- +// GameCRC is the current computed CRC value for this frame. +// CRC[] is a record of our last 32 game CRC's. +// ColorNames is for debug output in Print_CRCs +//--------------------------------------------------------------------------- +static unsigned long GameCRC; +static unsigned long CRC[32] = + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0}; +static char *ColorNames[8] = { + "Yellow", + "LtBlue", + "Red", + "Green", + "Orange", + "Grey", + "Blue", + "Brown" +}; + +//........................................................................... +// Mono debugging variables: +// NetMonoMode: 0 = show connection output, 1 = flowcount output +// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen +// IsMono: used for taking control of Mono screen away from the engine +//........................................................................... +int NetMonoMode = 1; +int NewMonoMode = 1; +static int IsMono = 0; + +//--------------------------------------------------------------------------- +// Several routines return various codes; here's an enum for all of them. +//--------------------------------------------------------------------------- +typedef enum RetcodeEnum { + RC_NORMAL, // no news is good news + RC_PLAYER_READY, // a new player has been heard from + RC_SCENARIO_MISMATCH, // scenario mismatch + RC_DOLIST_FULL, // DoList is full + RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed + RC_PLAYER_LEFT, // modem: other player left the game + RC_HUNG_UP, // modem has hung up + RC_NOT_RESPONDING, // other player not responding (timeout/hung up) + RC_CANCEL, // user cancelled +} RetcodeType; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern void Enable_Secret_Units(void); +#endif + +/********************************* Prototypes *******************************/ +//........................................................................... +// Main multiplayer queue logic +//........................................................................... +static void Queue_AI_Normal(void); +static void Queue_AI_Multiplayer(void); +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv); +static void Generate_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Process_Time_Event(ConnManClass *net); +static int Process_Send_Period(ConnManClass *net); //, int init); +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent); +static void Send_FrameSync(ConnManClass *net, int cmd_count); +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time); +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh); +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static void Stop_Game(void); + +//........................................................................... +// Packet compression/decompression: +//........................................................................... +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap); +int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +static int Breakup_Receive_Packet(void *buf, int bufsize ); +int Extract_Uncompressed_Events(void *buf, int bufsize); +int Extract_Compressed_Events(void *buf, int bufsize); + +//........................................................................... +// DoList management: +//........................................................................... +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, +// ConnManClass *net, TCountDownTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +static void Clean_DoList(ConnManClass *net); +static void Queue_Record(void); +static void Queue_Playback(void); + +//........................................................................... +// Debugging: +//........................................................................... +static void Compute_Game_CRC(void); +void Add_CRC(unsigned long *crc, unsigned long val); +static void Print_CRCs(EventClass *ev); +static void Init_Queue_Mono(ConnManClass *net); +static void Update_Queue_Mono(ConnManClass *net, int flow_index); +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent); + +extern void Keyboard_Process(KeyNumType &input); +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +void Check_Mirror(void); + + +/*************************************************************************** + * Queue_Mission -- Queue a mega mission event. * + * * + * This routine is called when the player causes a change to a game unit. * + * The event that initiates the change is queued to as a result of a call * + * to this routine. * + * * + * INPUT: * + * whom Whom this mission request applies to (a friendly unit). * + * mission The mission to assign to this object. * + * target The target of this mission (if any). * + * dest The movement destination for this mission (if any). * + * * + * OUTPUT: * + * Was the mission request queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) { + return(false); + } else { + return(true); + } +} + + +/*********************************************************************************************** + * Queue_Mission -- Queue a mega mission event, formation override for common speed. * + * * + * This routine is called when the player causes a change to a game unit. The event that * + * initiates the change is queued to as a result of a call to this routine. * + * * + * INPUT: whom -- Whom this mission request applies to (a friendly unit). * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target of this mission (if any). * + * * + * dest -- The movement destination for this mission (if any). * + * * + * speed -- The override speed for this unit. * + * * + * maxspeed -- The override maximum speed for this unit. * + * * + * OUTPUT: Was the mission request queued successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) { + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * Queue_Options -- Queue the options event. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the options screen event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Options(void) +{ + if (! OutList.Add(EventClass(EventClass::OPTIONS))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Options */ + + +/*************************************************************************** + * Queue_Exit -- Add the exit game event to the queue. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the exit event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Exit(void) +{ + if (! OutList.Add(EventClass(EventClass::EXIT))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Exit */ + + +/*************************************************************************** + * Queue_AI -- Process all queued events. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +void Queue_AI(void) +{ + if (Session.Play) { + Queue_Playback(); + } + + else { + + switch (Session.Type) { + + case GAME_SKIRMISH: + case GAME_NORMAL: + Queue_AI_Normal(); + break; + + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: + case GAME_INTERNET: + case GAME_TEN: + case GAME_MPATH: + Queue_AI_Multiplayer(); + break; + } + } + +} /* end of Queue_AI */ + + +/*************************************************************************** + * Queue_AI_Normal -- Process all queued events. * + * * + * This is the "normal" version of the queue management routine. It does * + * the following: * + * - Transfers items in the OutList to the DoList * + * - Executes any commands in the DoList that are supposed to be done on * + * this frame # * + * - Cleans out the DoList * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +static void Queue_AI_Normal(void) +{ + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + OutList.Next(); + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(1, PlayerPtr->Class->House, NULL, NULL, NULL, + NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_AI_Normal */ + + +/*************************************************************************** + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * This is the network version of the queue management routine. It does * + * the following: * + * - If this is the 1st frame, waits for other systems to signal ready * + * - Generates a timing event, to allow the connection time to be dynamic * + * - Handles timing related to sending packets every 'n' frames * + * - Sends outgoing events * + * - Frame-syncs to the other systems (see below) * + * - Executes & cleans out the DoList * + * * + * The Frame-Sync'ing logic is the heart & soul of network play. It works * + * by ensuring that any system won't out-run the other system by more than * + * 'Session.MaxAhead' frames; this in turn ensures that a packet's * + * execution frame # won't have been passed by the time that packet is * + * received by all systems. * + * * + * To achieve this, the system must keep track of all other system's * + * current frame #'s; these are stored in an array called 'their_frame[]'. * + * However, because current frame #'s are sent in FRAMEINFO packets, which * + * don't require an ACK, and command packets are sent in packets requiring * + * an ACK, it's possible for a command packet to get lost, and the next * + * frame's FRAMEINFO packet to not get lost; the other system may then * + * advance past the frame # the command is to execute on! So, to prevent * + * this, all FRAMEINFO packets include a CommandCount field. This value * + * tells the other system how many events it should have received by this * + * time. This system can therefore keep track of how many commands it's * + * actually received, and compare it to the CommandCount field, to see if * + * it's missed an event packet. The # of events we've received from each * + * system is stored in 'their_recv[]', and the # events they say they've * + * sent is stored in 'their_sent[]'. * + * * + * Thus, two conditions must be met in order to advance to the next frame: * + * - Our current frame # must be < their_frame + Session.MaxAhead * + * - their_recv[i] must be >= their_sent[i] * + * * + * 'their_frame[] is updated by Process_Receive_Packet() * + * 'their_recv[] is updated by Process_Receive_Packet() * + * 'their_sent[] is updated by Process_Receive_Packet() * + * 'my_sent' is updated by this routine. * + * * + * The order of the arrays their_frame[] etc is the same order the * + * connections are created in. The Sender's ID is passed to * + * Connection_Index() to obtain the array index. * + * * + * The only routines allowed to pop up dialogs are: * + * Wait_For_Players() (only pops up the reconnect dialog) * + * Execute_DoList() (tells if out of sync, or packet recv'd too late) * + * * + * Sign-off's are detected by: * + * - Timing out while waiting for a packet * + * - Detecting that the other player is now at the score screen or * + * connection dialog (serial) * + * - If we see an EventClass::EXIT event on the private channel * + * * + * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has * + * the following properties: * + * - It compresses packets, so that the minimum number of bytes are * + * transmitted. Packets are compressed by extracting all info common to * + * the events into the packet header, and then sending only the bytes * + * relevant to each type of event. For instance, if 100 infantry guys * + * are told to move to the same location, the command itself & the * + * location will be included in the 1st movement command only; after * + * that, there will be a rep count then 99 infantry TARGET numbers, * + * identifying all the infantry told to move. * + * - The protocol also only sends packets out every 'n' frames. This cuts * + * the data rate dramatically. It means that 'Session.MaxAhead' must be * + * divisible by 'n'; also, the minimum value for 'Session.MaxAhead' is * + * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO * + * packet gets missed. * + * * + * Note: For synchronization-waiting loops (like waiting to hear from all * + * other players, waiting to advance to the next frame, etc), use * + * Net.Num_Connections() rather than Session.NumPlayers; this reflects the * + * actual # of connections, and can be "faked" into playing even when * + * there aren't any other players actually there. A typical example of * + * this is playing back a recorded game. For command-execution loops, use * + * Session.NumPlayers. This ensures all commands get executed, even if * + * there isn't a human generating those commands. * + * * + * The modem works a little differently from the network in this respect: * + * - The connection has to stay "alive" even if the other player exits to * + * the join dialog. This prevents each system from timing out & hanging * + * the modem up. Thus, packets are sent back & forth & just thrown away,* + * but each system knows the other is still there. Messages may be sent * + * between systems, though. * + * - Destroy_Null_Connection doesn't hang up the modem, so * + * Num_Connections() still reports a value of 1 even though the other * + * player has left. * + * - Any waits on Num_Connections() must also check for * + * Session.NumPlayers > 1, to keep from waiting forever if the other * + * guy has left * + * - Packets sent to a player who's left require no ACK * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_AI_Multiplayer(void) +{ + if(Session.Type == GAME_SKIRMISH) return; + + //........................................................................ + // Enums: + //........................................................................ + enum { + MIXFILE_RESEND_DELTA = 120, // ticks b/w resends + MIXFILE_TIMEOUT = 3600*2, // timeout waiting for mixfiles. + FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog + FRAMESYNC_TIMEOUT = (15*60), // timeout waiting for frame sync packet + }; + + int timeout_factor = (Session.Type == GAME_INTERNET) ? 6 : 1; + + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + ConnManClass *net; // ptr to access all multiplayer functions + EventClass packet; // for sending single frame-sync's + char *multi_packet_buf; // buffer for sending/receiving + int multi_packet_max; // max length of multi_packet_buf + + //........................................................................ + // Frame-sync'ing variables + //........................................................................ + static long + their_frame[MAX_PLAYERS - 1]; // other players' frame #'s + static unsigned short + their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent + static unsigned short + their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others + static unsigned short + my_sent; // # cmds I've sent out + + //........................................................................ + // Timing variables + //........................................................................ + static CDTimerClass skip_crc; // to delay the CRC check +// static TCountDownTimerClass skip_crc; // to delay the CRC check + + //........................................................................ + // Other misc variables + //........................................................................ + int i; + RetcodeType rc; + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //------------------------------------------------------------------------ + // Initialize the packet buffer pointer & its max size + //------------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + multi_packet_buf = NullModem.BuildBuf; + multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType); + net = &NullModem; + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + multi_packet_buf = Session.MetaPacket; + multi_packet_max = Session.MetaSize; + net = &Ipx; + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + multi_packet_buf = Session.TenPacket; + multi_packet_max = Session.TenSize; + net = Ten; + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + multi_packet_buf = Session.MPathPacket; + multi_packet_max = Session.MPathSize; + net = MPath; + } +#endif + + //------------------------------------------------------------------------ + // Debug stuff + //------------------------------------------------------------------------ + Init_Queue_Mono(net); + Update_Queue_Mono (net, 0); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've just started a game, or loaded a multiplayer game, we must + // wait for all other systems to signal ready. + //------------------------------------------------------------------------ + if (Frame==0 || Session.LoadGame) { + //..................................................................... + // Initialize static locals + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) { + their_frame[i] = -1; + their_sent[i] = 0; + their_recv[i] = 0; + } + my_sent = 0; +#ifdef FIXIT_MULTI_SAVE + skip_crc = 32; +#else + skip_crc = Frame + 32; +#endif // FIXIT_MULTI_SAVE + for (i = 0; i < 32; i++) + CRC[i] = 0; + + //..................................................................... + // If we've loaded a saved game: + // - If this game was saved as the result of a lost connection, clear + // the CRC value so it will always match the other system's + // - Otherwise, use the GameCRC value, so we'll compare save-game files + // rather than scenario INI files + //..................................................................... + if (Session.LoadGame) { + if (Session.EmergencySave) + ScenarioCRC = 0; + else + ScenarioCRC = GameCRC; + } + + //..................................................................... + // Send our initial FRAMESYNC packet + //..................................................................... + Send_FrameSync(net, my_sent); + + //..................................................................... + // Wait for the other guys + //..................................................................... + rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor, + MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //..................................................................... + // Re-initialize frame numbers (in case somebody signed off while I was + // waiting for MIX files to load; we would have fallen through, but + // their frame # would still be -1). + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) + their_frame[i] = 0; + + //..................................................................... + // Reset the network response time computation, now that we're both + // sending data again (loading MIX files will have introduced + // deceptively large values). + //..................................................................... + net->Reset_Response_Time(); + + //..................................................................... + // Initialize the frame timers + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Process_Send_Period(net);//, 1); + } + + //..................................................................... + // Turn off our special load-game flags + //..................................................................... + if (Session.LoadGame) { + Session.EmergencySave = false; + Session.LoadGame = false; + } + + } // end of Frame 0 wait + + //------------------------------------------------------------------------ + // Adjust connection timing parameters every 128 frames. + //------------------------------------------------------------------------ + + else if ( (Frame & 0x007f) == 0) { + // + // If we're using the new spiffy protocol, do proper timing handling. + // If we're the net "master", compute our desired frame rate & new + // 'MaxAhead' value. + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // All systems will transmit their required process time. + // + Generate_Process_Time_Event(net); + + // + // The game "host" will transmit timing adjustment events. + // + if (Session.Am_I_Master()) { + Generate_Real_Timing_Event(net, my_sent); + } + } else { + // + // For the older protocols, do the old broken timing handling. + // + Generate_Timing_Event(net, my_sent); + } + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (!Process_Send_Period(net)) { //, 0)) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + } + + //------------------------------------------------------------------------ + // Send our data packet(s); update my command-sent counter + //------------------------------------------------------------------------ + my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max, + Session.MaxAhead, my_sent); + + //------------------------------------------------------------------------ + // If this is our first time through, we're done. + //------------------------------------------------------------------------ + if (Frame==0) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + + //------------------------------------------------------------------------ + // Frame-sync'ing: wait until it's OK to advance to the next frame. + //------------------------------------------------------------------------ +#ifdef FIXIT_VERSION_3 + int iFramesyncTimeout; + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 ) + // Shortened resync timeout for non-2 player games. + iFramesyncTimeout = 5 * 60; // One minute. + else + iFramesyncTimeout = FRAMESYNC_TIMEOUT; + + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + iFramesyncTimeout * (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#else + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + FRAMESYNC_TIMEOUT* (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#endif + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); +#ifdef WOLAPI_INTEGRATION + // New rule - if you cancel a waiting to reconnect dialog, you lose. + bReconnectDialogCancelled = ( rc == RC_CANCEL ); +#endif + } +#endif //WIN32 + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc, + their_frame, their_sent, their_recv)) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(net); + + if (IsMono) { + MonoClass::Disable(); + } + +} // end of Queue_AI_Multiplayer + + +/*************************************************************************** + * Wait_For_Players -- Waits for other systems to come on-line * + * * + * This routine performs the most critical logic in multiplayer; that of * + * synchronizing my frame number with those of the other systems. * + * * + * INPUT: * + * first_time 1 = 1st time this routine is called * + * net ptr to connection manager * + * resend_delta time (ticks) between FRAMESYNC resends * + * dialog_time time (ticks) until pop up a reconnect dialog * + * timeout time (ticks) until we give up the ghost * + * multi_packet_buf buffer to store packets in * + * my_sent # commands I've sent so far * + * their_frame array of their frame #'s * + * their_sent array of their CommandCount values * + * their_recv array of # cmds I've received from them * + * * + * OUTPUT: * + * RC_NORMAL OK to advance to the next frame * + * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg * + * RC_NOT_RESPONDING other player(s) not responding * + * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) * + * RC_DOLIST_FULL DoList was full * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv) +{ + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + EventClass *event; // event ptr for parsing incoming packets + int packetlen; // size of meta-packet sent, & received + int id; // id of other player + int messages_this_loop; // to limit # messages processed each loop + int message_limit; // max # messages we'll read each frame + + //........................................................................ + // Variables used only if 'first_time': + //........................................................................ + int num_ready; // # players signalling ready + + //........................................................................ + // Timing variables + //........................................................................ + CDTimerClass retry_timer; // time between FRAMESYNC packet resends + CDTimerClass dialog_timer; // time to pop up a dialog + CDTimerClass timeout_timer; // general-purpose timeout + + //........................................................................ + // Dialog variables + //........................................................................ + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //........................................................................ + // Other misc variables + //........................................................................ + KeyNumType input; // for user input + int x,y; // for map input + RetcodeType rc; + + //------------------------------------------------------------------------ + // Wait to hear from all other players + //------------------------------------------------------------------------ + num_ready = 0; + retry_timer = resend_delta; // time to retry + dialog_timer = dialog_time; // time to show dlg + timeout_timer = timeout; // time to bail out + + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 2); + + //--------------------------------------------------------------------- + // Resend a frame-sync packet if longer than one propagation delay goes + // by; this prevents a "deadlock". If he's waiting for me to advance, + // but has missed my last few FRAMEINFO packets, I may be waiting for + // him to advance. Resending a FRAMESYNC ensures he knows what frame + // number I'm on. + //--------------------------------------------------------------------- + if (!retry_timer) { + retry_timer = resend_delta; // time to retry + Update_Queue_Mono (net, 3); + Send_FrameSync(net, my_sent); + } + + //--------------------------------------------------------------------- + // Service the connections + //--------------------------------------------------------------------- + net->Service(); + + //--------------------------------------------------------------------- + // Pop up a reconnect dialog if enough time goes by + //--------------------------------------------------------------------- + if (!dialog_timer && SpecialDialog==SDLG_NONE) { + if (reconnect_dlg == 0 && first_time == 0) { + FILE *fp; + int i; + HouseClass *housep; + + fp = fopen("recon.txt","wt"); + if (fp) { + fprintf(fp,"# Connections: %d\n",net->Num_Connections()); + fprintf(fp," My Frame #: %d\n",Frame); + for (i = 0; i < net->Num_Connections(); i++) { + housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i))); + fprintf(fp,"%15s: Their Sent:%d Their Recv:%d Their Frame:%d\n", + housep->IniName, their_sent[i], their_recv[i], their_frame[i]); + } + fclose(fp); + } + +#ifdef WOLAPI_INTEGRATION + // "Reconnecting" dialog is about to be shown. + // At this point, begin wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + pWolapi->Init_DisconnectPinging(); +#endif + } + + if (Process_Reconnect_Dialog(&timeout_timer, their_frame, // (Returns immediately.) + net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) { + return (RC_CANCEL); + } + reconnect_dlg = 1; + +#ifdef WOLAPI_INTEGRATION + // Continue wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging ) + pWolapi->Pump_DisconnectPinging(); +#endif + } + + //--------------------------------------------------------------------- + // Exit if too much time goes by (the other system has crashed or + // bailed) + //--------------------------------------------------------------------- + if (!timeout_timer) { + //.................................................................. + // For the first-time run, just give up; something's wrong. + //.................................................................. + if (first_time) { + return (RC_NOT_RESPONDING); + } + //.................................................................. + // Otherwise, we're in the middle of a game; so, the modem & + // network must deal with a timeout differently. + //.................................................................. + else { + Update_Queue_Mono (net, 4); + + if (Handle_Timeout(net, their_frame, their_sent, their_recv)) { + Map.Flag_To_Redraw(true); // erase modem reconnect dialog + Map.Render(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + } +#ifdef FIXIT_MULTI_SAVE +#ifdef FIXIT_VERSION_3 + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) { +#else + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) && + PlayingAgainstVersion != VERSION_RED_ALERT_104) { +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; +//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); +//Print_CRCs(NULL); +//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); +//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); + } +#endif // FIXIT_MULTI_SAVE + else { + return (RC_NOT_RESPONDING); + } + } + } + + //--------------------------------------------------------------------- + // Check for an incoming message. We must still process commands + // even if 'first_time' is set, in case the other system got my 1st + // FRAMESYNC, but I didn't get his; he'll be at the next frame, and + // may be sending commands. + // We have to limit the number of incoming messages we handle; it's + // possible to go into an infinite loop processing modem messages. + // (This feature is disabled for Ten; we need to keep the TCP buffers + // clear, so we read all the packets we can every time.) + //--------------------------------------------------------------------- + messages_this_loop = 0; + message_limit = 5; + + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + message_limit = 9999; + } + + while ( (messages_this_loop++ < message_limit) && + net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) { + + Keyboard->Check(); + + Update_Queue_Mono (net, 5); + + /*.................................................................. + Get an event ptr to the incoming message + ..................................................................*/ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------ + // Special processing for a modem game: process SERIAL packets + //------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + rc = Process_Serial_Packet(multi_packet_buf, first_time); + //............................................................... + // SERIAL packet received & processed + //............................................................... + if (rc == RC_SERIAL_PROCESSED) { + net->Service(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + continue; + } + //............................................................... + // other player has left the game + //............................................................... + else if (rc == RC_PLAYER_LEFT) { + if (first_time) { + num_ready++; + } + break; + } + //............................................................... + // Connection was lost + //............................................................... + else if (rc == RC_HUNG_UP) { +#ifdef FIXIT_MULTI_SAVE +#ifndef FIXIT_VERSION_3 + if (PlayingAgainstVersion != VERSION_RED_ALERT_104){ +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; + //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); + //Print_CRCs(NULL); + //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); +#ifndef FIXIT_VERSION_3 + }else{ + return (RC_NOT_RESPONDING); + } +#endif + +#else + return (RC_NOT_RESPONDING); +#endif // FIXIT_MULTI_SAVE + } + //............................................................... + // If it was any other type of serial packet, break + //............................................................... + else if (rc != RC_NORMAL) { + break; + } + } + + //------------------------------------------------------------------ + // Process the incoming packet + //------------------------------------------------------------------ + rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen, + their_frame, their_sent, their_recv); + //.................................................................. + // New player heard from + //.................................................................. + if (rc == RC_PLAYER_READY) { + num_ready++; + } + //.................................................................. + // Scenario's don't match + //.................................................................. + else if (rc == RC_SCENARIO_MISMATCH) { + return (RC_SCENARIO_MISMATCH); + } + //.................................................................. + // DoList was full + //.................................................................. + else if (rc == RC_DOLIST_FULL) { + return (RC_DOLIST_FULL); + } + + //.................................................................. + // Service the connection, to clean out the receive queues + //.................................................................. + net->Service(); + } + + //--------------------------------------------------------------------- + // Debug output + //--------------------------------------------------------------------- + Print_Framesync_Values(Frame, Session.MaxAhead, net->Num_Connections(), + their_recv, their_sent, my_sent); + + //--------------------------------------------------------------------- + // Attempt to advance to the next frame. + //--------------------------------------------------------------------- + //..................................................................... + // For the first-time run, just check to see if we've heard from + // everyone. + //..................................................................... + if (first_time) { + if (num_ready >= net->Num_Connections()) { + break; + } + } + //..................................................................... + // For in-game processing, we have to check their_sent, their_recv, + // their_frame, etc. + //..................................................................... + else { + if (Can_Advance(net, Session.MaxAhead, their_frame, their_sent, + their_recv)) { + break; + } + } + + //--------------------------------------------------------------------- + // Service game stuff. Servicing the map's input, and rendering the + // map, allows the map to scroll even though we're hung up waiting for + // packets. Don't do this if 'first_time' is set, since users could be + // waiting a very long time for all systems to load the scenario, and + // it gets frustrating being able to scroll around without doing + // anything. + //--------------------------------------------------------------------- + Call_Back(); + if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + Map.Input(input, x, y); + if (input) + Keyboard_Process(input); + Map.Render(); + } + + } /* end of while */ + + //------------------------------------------------------------------------ + // If the reconnect dialog was shown, force the map to redraw. + //------------------------------------------------------------------------ + if (reconnect_dlg) { + Map.Flag_To_Redraw(true); + Map.Render(); + } + + return (RC_NORMAL); + +} // end of Wait_For_Players + + +/*************************************************************************** + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * * + * This routine adjusts the connection timing on the local system; it also * + * optionally generates a RESPONSE_TIME event, to tell all systems to * + * dynamically adjust the current MaxAhead value. This allows both the * + * MaxAhead & the connection retry logic to have dynamic timing, to adjust * + * to varying line conditions. * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Generate_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + + // + // For now, TEN & MPATH don't measure the net's response time, so there's + // no point in adjusting our timing. Do nothing. + // + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + return; + } + + //------------------------------------------------------------------------ + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, divide again by 4, assuming a game rate of 15 fps. + //------------------------------------------------------------------------ + resp_time = net->Response_Time(); + + //------------------------------------------------------------------------ + // Adjust my connection retry timing; only do this if I've sent out more + // than 5 commands, so I know I have a measure of the response time. + //------------------------------------------------------------------------ + if (my_sent > 5) { + + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + //..................................................................... + // If I'm the network "master", I'm also responsible for updating the + // MaxAhead value on all systems, so do that here too. + //..................................................................... + if (Session.Am_I_Master()) { + ev.Type = EventClass::RESPONSE_TIME; + //.................................................................. + // For multi-frame compressed events, the MaxAhead must be an even + // multiple of the FrameSendRate. + //.................................................................. + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + //.................................................................. + // For sending packets every frame, just use the 1-way connection + // response time. + //.................................................................. + else { + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + NETWORK_MIN_MAX_AHEAD ); + } + else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + OutList.Add(ev); + } + } + +} // end of Generate_Timing_Event + + +/*************************************************************************** + * Generate_Real_Timing_Event -- Generates a TIMING event * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + int highest_ticks; + int i; + int specified_frame_rate; + int maxahead; + + // + // If we haven't sent out at least 5 guaranteed-delivery packets, don't + // bother trying to measure our connection response time; just return. + // + if (my_sent < 5) { + return; + } + + // + // Find the highest processing time we have stored + // + highest_ticks = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + // + // If we haven't heard from all systems yet, bail out. + // + if (Session.Players[i]->Player.ProcessTime == -1) { + return; + } + if (Session.Players[i]->Player.ProcessTime > highest_ticks) { + highest_ticks = Session.Players[i]->Player.ProcessTime; + } + } + + // + // Compute our "desired" frame rate as the lower of: + // - What the user has dialed into the options screen + // - What we're really able to run at + // + if (highest_ticks == 0) { + Session.DesiredFrameRate = 60; + } else { + Session.DesiredFrameRate = 60 / highest_ticks; + } + + if (Options.GameSpeed == 0) { + specified_frame_rate = 60; + } else { + specified_frame_rate = 60 / Options.GameSpeed; + } + + Session.DesiredFrameRate = MIN (Session.DesiredFrameRate, specified_frame_rate); + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Compute our new 'MaxAhead' value, based upon the response time of our + // connection and our desired frame rate. + // 'MaxAhead' in frames is: + // + // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec) + // + // resp_time is divided by 2 because, as reported, it represents a round- + // trip, and we only want to use a one-way trip. + // + maxahead = (resp_time * Session.DesiredFrameRate) / (2 * 60); + + // + // Now, we have to round 'maxahead' so it's an even multiple of our + // send rate. It also must be at least thrice the FrameSendRate. + // (Isn't "thrice" a cool word?) + // + maxahead = ((maxahead + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate; + maxahead = MAX (maxahead, Session.FrameSendRate * 3); + + ev.Type = EventClass::TIMING; + ev.Data.Timing.DesiredFrameRate = Session.DesiredFrameRate; + ev.Data.Timing.MaxAhead = maxahead; + + OutList.Add(ev); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + +} + + +/*************************************************************************** + * Generate_Process_Time_Event -- Generates a PROCESS_TIME event * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Process_Time_Event(ConnManClass *net) +{ + EventClass ev; + int avgticks; + unsigned long resp_time; // connection response time, in ticks + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + + + if (IsMono) { + MonoClass::Enable(); + Mono_Set_Cursor(0,23); + Mono_Printf("Processing Ticks:%03d Frames:%03d\n", Session.ProcessTicks,Session.ProcessFrames); + MonoClass::Disable(); + } + + avgticks = Session.ProcessTicks / Session.ProcessFrames; + + ev.Type = EventClass::PROCESS_TIME; + ev.Data.ProcessTime.AverageTicks = avgticks; + OutList.Add(ev); + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; +} + + +/*************************************************************************** + * Process_Send_Period -- timing for sending packets every 'n' frames * + * * + * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. * + * It determines if it's time to send a packet or not. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * 1 = it's time to send a packet; 0 = don't send a packet this frame. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Send_Period(ConnManClass *net) //, int init) +{ + //------------------------------------------------------------------------ + // If the current frame # is not an even multiple of 'FrameSendRate', then + // it's not time to send a packet; just return. + //------------------------------------------------------------------------ + if (Frame != (((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate) ) { + + net->Service(); + + if (IsMono) { + MonoClass::Disable(); + } + + return (0); + } + + return (1); + + +} // end of Process_Send_Period + + +/*************************************************************************** + * Send_Packets -- sends out events from the OutList * + * * + * This routine computes how many events can be sent this frame, and then * + * builds the "meta-packet" & sends it. * + * * + * The 'cap' value is the max # of events we can send. Ideally, it should * + * be based upon the bandwidth of our connection. Currently, it's just * + * hardcoded to prevent the modem from having to resend "too much" data, * + * which is about 200 bytes per frame. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer to store packets in * + * multi_packet_max max size of multi_packet_buf * + * max_ahead current game MaxAhead value * + * my_sent # commands I've sent this game * + * * + * OUTPUT: * + * # events sent, NOT including the FRAMEINFO event * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent) +{ + int cap; // max # events to send, NOT including FRAMEINFO event + int do_once; // true: only go through packet loop once + int ack_req; // 0 = no ack required on outgoing packet + int packetlen; // size of meta-packet sent + + //------------------------------------------------------------------------ + // Determine how many events it's OK to send this frame. + //------------------------------------------------------------------------ + //........................................................................ + // If we have 4 or more packets queued for sending, don't add any more + // this frame. + //........................................................................ + if (net->Private_Num_Send() >= 4) { + cap = 0; + do_once = 1; + } + //........................................................................ + // If there are 2 or more packets queued, the entire packet we send must + // fit within a single ComQueue buffer, so limit # events to 5. + // (The Modem connection manager has a max buffer size of 200 bytes, which + // is large enough for 6 uncompressed events, which leaves room for 5 + // events plus a FRAMEINFO.) + //........................................................................ + else if (net->Private_Num_Send() >= 2) { + cap = 5; + do_once = 1; + + } + //........................................................................ + // Otherwise, just send all events in the OutList + //........................................................................ + else { + cap = OutList.Count; + do_once = 0; + } + + //........................................................................ + // Make sure we aren't sending more events than are in the OutList + //........................................................................ + if (cap > OutList.Count) { + cap = OutList.Count; + } + + //........................................................................ + // Make sure we don't send so many events that our DoList fills up + //........................................................................ + if (cap > (MAX_EVENTS * 64) - DoList.Count) { + cap = (MAX_EVENTS * 64) - DoList.Count; + } + + // + // 10/21/96 5:12PM - ST + // + if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){ + cap = OutList.Count; + do_once = 0; + } + + //------------------------------------------------------------------------ + // Build our meta-packet & transmit it. + //------------------------------------------------------------------------ + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 1); + + //..................................................................... + // If there are no commands this frame, we'll just be sending a FRAMEINFO + // packet; no ack is required. For the modem's sake, check + // Session.NumPlayers; no ACK is needed if we're just sending to someone + // who's left the game. + //..................................................................... + if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 1) { + ack_req = 0; + } + else { + ack_req = 1; + } + + //..................................................................... + // Build & send out our message + //..................................................................... + packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max, + max_ahead, my_sent, cap); + net->Send_Private_Message (multi_packet_buf, packetlen, ack_req); + + //..................................................................... + // Call Service() to actually send the packet + //..................................................................... + net->Service(); + + //..................................................................... + // Stop if there's no more data to send, or if our send queue is + // filling up. + //..................................................................... + if (OutList.Count == 0 || do_once) { + break; + } + } + + return (cap); + +} // end of Send_Packets + + +/*************************************************************************** + * Send_FrameSync -- Sends a FRAMESYNC packet * + * * + * This routine is used to periodically remind the other systems that * + * we're still here, and to tell them what frame # we're on, in case * + * they've missed my FRAMEINFO packets. * + * * + * INPUT: * + * net ptr to connection manager * + * cmd_count # commands I've sent so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Send_FrameSync(ConnManClass *net, int cmd_count) +{ + EventClass packet; + + //------------------------------------------------------------------------ + // Build a frame-sync event to send. FRAMESYNC packets contain a + // scenario-based CRC rather than a game-state-based CRC, to let the + // games compare scenario CRC's on startup. + //------------------------------------------------------------------------ + memset (&packet, 0, sizeof(EventClass)); + packet.Type = EventClass::FRAMESYNC; + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + packet.Frame = Frame + Session.MaxAhead; + } + packet.ID = PlayerPtr->ID; + packet.Data.FrameInfo.CRC = ScenarioCRC; + packet.Data.FrameInfo.CommandCount = cmd_count; + packet.Data.FrameInfo.Delay = Session.MaxAhead; + + //------------------------------------------------------------------------ + // Send the event. For modem, this just sends to the other player; + // for network, it sends to everyone we're connected to. + //------------------------------------------------------------------------ + + net->Send_Private_Message (&packet, (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)), 0 ); + + return; + +} // end of Send_FrameSync + + +/*************************************************************************** + * Process_Receive_Packet -- processes an incoming packet * + * * + * This routine receives a packet from another system, adds it to our * + * execution queue (the DoList), and updates my arrays of their frame #, * + * their commands-sent, and their commands-received. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer containing packet(s) to parse * + * id id of sender * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * RC_NORMAL: nothing unusual happened, although * + * their_sent or their_recv may have been * + * altered * + * RC_PLAYER_READY: player has been heard from for the 1st time; * + * this presumes that his original * + * 'their_frame[]' value was -1 when this * + * routine was called * + * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; * + * normally only applies after loading a new * + * scenario or save-game * + * RC_DOLIST_FULL: fatal error; unable to add events to DoList * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + EventClass *event; + int index; + RetcodeType retcode = RC_NORMAL; + int i; + + //------------------------------------------------------------------------ + // Get an event ptr to the incoming message + //------------------------------------------------------------------------ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------------ + // Get the index of the sender + //------------------------------------------------------------------------ + index = net->Connection_Index(id); + + //------------------------------------------------------------------------ + // Compute the other player's frame # (at the time this packet was sent) + //------------------------------------------------------------------------ + if (their_frame[index] < + (int)(event->Frame - event->Data.FrameInfo.Delay)) { + + //..................................................................... + // If the original frame # for this player is -1, it means we've heard + // from this player for the 1st time; return the appropriate value. + //..................................................................... + if (their_frame[index]==-1) { + retcode = RC_PLAYER_READY; + } + + their_frame[index] = event->Frame - event->Data.FrameInfo.Delay; + } + + //------------------------------------------------------------------------ + // Extract the other player's CommandCount. This count will include + // the commands in this packet, if there are any. + //------------------------------------------------------------------------ + if (event->Data.FrameInfo.CommandCount > their_sent[index]) { + + if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) { + FILE *fp; + fp = fopen("badcount.txt","wt"); + if (fp) { + fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame:%d ID:%d IsExec:%d\n", + event->Frame, + event->ID, + event->IsExecuted); + if (event->Type != EventClass::FRAMEINFO) { + fprintf(fp,"Wrong Event Type!\n"); + } else { + fprintf(fp,"CRC:%x CommandCount:%d Delay:%d\n", + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + } + } + + their_sent[index] = event->Data.FrameInfo.CommandCount; + } + + if (Debug_Print_Events) { + if (event->Type == EventClass::FRAMESYNC) { + printf("(%d) Received FRAMESYNC: ", Frame); + } else { + printf("(%d) Received FRAMEINFO: ", Frame); + } + printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n", + event->Frame, + event->ID, + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + + //------------------------------------------------------------------------ + // If this packet was not a FRAMESYNC packet: + // - Add the events in it to our DoList + // - Increment our commands-received counter by the number of non- + // FRAMEINFO packets received + //------------------------------------------------------------------------ + if (event->Type != EventClass::FRAMESYNC) { + //..................................................................... + // Break up the packet into its component events. A returned packet + // count of -1 indicates a fatal queue-full error. + //..................................................................... + i = Breakup_Receive_Packet( multi_packet_buf, packetlen); + if (i==-1) { + return (RC_DOLIST_FULL); + } + //..................................................................... + // Compute the actual # commands in the packet by subtracting off the + // FRAMEINFO event + //..................................................................... + if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) { + i--; + } + + their_recv[index] += i; + } + + //------------------------------------------------------------------------ + // If the event was a FRAMESYNC packet, there will be no commands to add, + // but we must check the ScenarioCRC value. + //------------------------------------------------------------------------ + else if (event->Data.FrameInfo.CRC != ScenarioCRC) { + return (RC_SCENARIO_MISMATCH); + } + + return (retcode); + +} // end of Process_Receive_Packet + + +/*************************************************************************** + * Process_Serial_Packet -- Handles an incoming serial packet * + * * + * This routine is needed because the modem classes don't support a * + * "global channel" like the network classes do, but that functionality is * + * still needed for modem communications. Specifically, the modem dialogs * + * transmit their own special packets back & forth, and messages are sent * + * using a special packet type. Thus, we have to call this routine when * + * we receive a modem packet, to allow it to process messages & dialog * + * packets. * + * * + * INPUT: * + * multi_packet_buf packet buffer to process * + * first_time 1 = this is the 1st game frame * + * * + * OUTPUT: * + * RC_NORMAL: this wasn't a SERIAL-type packet * + * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was * + * processed; the other player is still connected, * + * even if he's not in the game. * + * RC_PLAYER_LEFT: other player has left the game * + * RC_HUNG_UP: we're getting our own packets back; thus, the * + * modem is mirroring our packets, which means the * + * modem hung up! * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time) +{ + SerialPacketType *serial_packet; // for parsing serial packets + int player_gone; + EventClass *event; + + //------------------------------------------------------------------------ + // Determine if this packet means that the other player has left the game + //------------------------------------------------------------------------ + serial_packet = (SerialPacketType *)multi_packet_buf; + player_gone = 0; + //........................................................................ + // On Frame 0, only a SIGN_OFF means the other player left; the other + // packet types may be left over from a previous session. + //........................................................................ + if (first_time) { + if (serial_packet->Command == SERIAL_SIGN_OFF) { + player_gone = 1; + } + } + //........................................................................ + // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means + // the other player is gone. + //........................................................................ + else { + if (serial_packet->Command == SERIAL_SIGN_OFF || + serial_packet->Command == SERIAL_TIMING || + serial_packet->Command == SERIAL_SCORE_SCREEN ) { + player_gone = 1; + } + } + if (player_gone) { + Destroy_Null_Connection(serial_packet->ScenarioInfo.Color, 0); + return (RC_PLAYER_LEFT); + } + + //------------------------------------------------------------------------ + // Process an incoming message + //------------------------------------------------------------------------ + if (serial_packet->Command == SERIAL_MESSAGE) { + if (!Session.Messages.Concat_Message(serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Appears to do nothing + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, + (PlayerColorType)serial_packet->ID, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + //..................................................................... + // Save this message in our last-message buffer + //..................................................................... + if (strlen (serial_packet->Message.Message)) { + strcpy (Session.LastMessage, serial_packet->Message.Message); + } + + //..................................................................... + // Tell the map to do a partial update (just to force the + // messages to redraw). + //..................................................................... + //Map.Flag_To_Redraw(false); + Map.Flag_To_Redraw(true); + return (RC_SERIAL_PROCESSED); + } + + //------------------------------------------------------------------------ + // Any other SERIAL-type packet means the other player is still there; + // throw them away, but let the caller know the connection is OK. + //------------------------------------------------------------------------ + if ( (serial_packet->Command >= SERIAL_CONNECT && + serial_packet->Command < SERIAL_LAST_COMMAND) || + (serial_packet->Command >= SERIAL_REQ_SCENARIO && + serial_packet->Command <= SERIAL_NO_SCENARIO) || + Session.NumPlayers == 1) { + return (RC_SERIAL_PROCESSED); + } + + //........................................................................ + // are we getting our own packets back?? + //........................................................................ + event = (EventClass *)multi_packet_buf; + + if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED); + + if (event->ID == PlayerPtr->ID) { + return (RC_HUNG_UP); + } + + return (RC_NORMAL); + +} // end of Process_Serial_Packet + + +/*************************************************************************** + * Can_Advance -- determines if it's OK to advance to the next frame * + * * + * This routine uses the current values stored in their_frame[], * + * their_send[], and their_recv[] to see if it's OK to advance to the next * + * game frame. We must not advance if: * + * - If our frame # would be too far ahead of the slowest player (the * + * lowest their_frame[] value). "Too far" means * + * (Frame >= their_frame + MaxAhead). * + * - our current command count doesn't match the sent command count of one * + * other player (meaning that we've missed a command packet from that * + * player, and thus the frame # we're receiving from him may be due to a * + * FRAMEINFO packet sent later than the command, so we shouldn't use * + * this frame # to see if we should advance; we should wait until we * + * have all the commands before we advance. * + * * + * Of course, this routine assumes the values in their_frame[] etc are * + * kept current by the caller. * + * * + * INPUT: * + * net ptr to connection manager * + * max_ahead max frames ahead * + * their_frame array of their frame #'s * + * their_sent array of their sent command count * + * their_recv array of their # received commands * + * * + * OUTPUT: * + * 1 = OK to advance; 0 = not OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + long their_oldest_frame; // other players' oldest frame # + int count_ok; // true = my cmd count matches theirs + int i; + + //------------------------------------------------------------------------ + // Special case for modem: if the other player has left, go ahead and + // advance to the next frame; don't wait on him. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + return (1); + } + + //------------------------------------------------------------------------ + // Find the oldest frame # in 'their_frame' + //------------------------------------------------------------------------ + their_oldest_frame = Frame + 1000; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < their_oldest_frame) + their_oldest_frame = their_frame[i]; + } + + //------------------------------------------------------------------------ + // I can advance to the next frame IF: + // 1) I'm less than a one-way propagation delay ahead of the other + // players' frame numbers, AND + // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands + // the other players have sent so far). + //------------------------------------------------------------------------ + count_ok = 1; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_recv[i] < their_sent[i]) { + count_ok = 0; + break; + } + } + if (count_ok && (Frame < (their_oldest_frame + max_ahead))) { + return (1); + } + + return (0); + +} // end of Can_Advance + + +/*************************************************************************** + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * * + * This routine [re]draws the reconnection dialog; if 'reconn' is set, * + * it tells the user who we're trying to reconnect to; otherwise, is just * + * says something generic like "Waiting for connections". * + * * + * INPUT: * + * timeout_timer ptr to count down timer, showing time remaining * + * their_frame array of other players' frame #'s * + * num_conn # connections in 'their_frame' * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * * + * OUTPUT: * + * 1 = user wants to cancel, 0 = not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh) +{ + static int displayed_time = 0; // time value currently displayed + int new_time; + int oldest_index; // index of person requiring a reconnect + int i,j; + + //------------------------------------------------------------------------ + // Convert the timer to seconds + //------------------------------------------------------------------------ + new_time = *timeout_timer / 60; + + //------------------------------------------------------------------------ + // If the timer has changed, or 'fresh' is set, redraw the dialog + //------------------------------------------------------------------------ + if (fresh || (new_time != displayed_time)) { + //..................................................................... + // Find the index of the person we're trying to reconnect to + //..................................................................... + if (reconn) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < num_conn; i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + } + Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time); + } + displayed_time = new_time; + + //........................................................................ + // If user hits ESC, bail out + //........................................................................ + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + return (1); + } + } + + return (0); + +} // end of Process_Reconnect_Dialog + + +/*************************************************************************** + * Handle_Timeout -- handles a timeout in the wait-for-players loop * + * * + * This routine "gracefully" handles a timeout in the frame-sync loop. * + * The timeout must be handled differently by a modem game or network * + * game. * + * * + * The modem game must detect if the other player is still connected * + * physically, even if he's not playing the game any more; if so, this * + * routine returns an OK status. If the other player isn't even * + * physically connected, an error is returned. * + * * + * The network game must find the connection that's causing the timeout, * + * and destroy it. The game continues, even if there are no more human * + * players left. * + * * + * INPUT: * + * net ptr to connection manager * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * 1 = it's OK; reset timeout timers & keep processing * + * 0 = game over, man * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + int oldest_index; // index of person requiring a reconnect + int i,j; + int id; + + //------------------------------------------------------------------------ + // For modem, attempt to reconnect; if that fails, save the game & bail. + //------------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + if ( net->Num_Connections() ) { + if (!Reconnect_Modem()) { +#ifndef FIXIT_MULTI_SAVE + //............................................................... + // Set 'Session.EmergencySave', so when this game is loaded, we + // won't check the CRC of the game state (this system & the + // other may be on different frames, in which case the CRC + // won't match). + //............................................................... + Session.EmergencySave = 1; + //Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + Session.EmergencySave = 0; +#endif // FIXIT_MULTI_SAVE + return (0); + } else { + return (1); + } + } + } + + //------------------------------------------------------------------------ + // For network, destroy the oldest connection + //------------------------------------------------------------------------ + else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + + id = net->Connection_ID(oldest_index); +#ifdef WIN32 + /* + ** Send the game statistics packet now if the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + ConnectionLost = true; + Send_Statistics_Packet(); // Disconnect, and I'll be the only one left. + } +#endif //WIN32 + + if (id != ConnManClass::CONNECTION_NONE) { + for (i = oldest_index; i < net->Num_Connections() - 1; i++) { + their_frame[i] = their_frame[i+1]; + their_sent[i] = their_sent[i+1]; + their_recv[i] = their_recv[i+1]; + } + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + Destroy_Connection(id,1); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(id,1); + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(id,1); + } +#endif + } + } + + return (1); + +} // end of Handle_Timeout + + +/*************************************************************************** + * Stop_Game -- stops the game * + * * + * This routine clears any global flags that need it, in preparation for * + * halting the game prematurely. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1995 BRR : Created. * + *=========================================================================*/ +static void Stop_Game(void) +{ + Session.LoadGame = false; + Session.EmergencySave = false; + GameActive = 0; + if (IsMono) { + MonoClass::Disable(); + } +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + ConnectionLost = true; + Send_Statistics_Packet(); // Stop_Game() + } +#endif //WIN32 + + return; + +} // end of Stop_Game + + +/*************************************************************************** + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * * + * This routine takes events from the OutList, and puts them into a * + * "meta-packet", which is transmitted to all systems we're connected to. * + * Also, these events are added to our own DoList. * + * * + * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this * + * tells the other systems what frame we're on, as well as serving as a * + * standard packet header. * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * num_cmds value to use for the CommandCount field * + * cap max # events to send * + * * + * OUTPUT: * + * new size of packet * + * * + * WARNINGS: * + * 'num_cmds' should be the total of of commands, including all those sent * + * this frame! * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap) +{ + int size = 0; + EventClass *finfo; + + //------------------------------------------------------------------------ + // All events start with a FRAMEINFO event; fill this part in. + //------------------------------------------------------------------------ + //........................................................................ + // Set the event type + //........................................................................ + finfo = (EventClass *)buf; + finfo->Type = EventClass::FRAMEINFO; + //........................................................................ + // Set the frame to execute this event on; this is protocol-specific + //........................................................................ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + finfo->Frame = Frame + frame_delay; + } + //........................................................................ + // Fill in the rest of the event + //........................................................................ + finfo->ID = PlayerPtr->ID; + finfo->Data.FrameInfo.CRC = GameCRC; + finfo->Data.FrameInfo.CommandCount = num_cmds; + finfo->Data.FrameInfo.Delay = frame_delay; + + //------------------------------------------------------------------------ + // Initialize the # of bytes processed; this is protocol-specific + //------------------------------------------------------------------------ + if (Session.CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) { + size += sizeof(EventClass); + } + else { + size += (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)); + } + + //------------------------------------------------------------------------ + // Transfer all events from the OutList into the DoList, building our + // packet while we go. + //------------------------------------------------------------------------ + switch (Session.CommProtocol) { + //..................................................................... + // COMM_PROTOCOL_SINGLE_NO_COMP: + // We'll send at least a FRAMEINFO every single frame, no compression + //..................................................................... + case (COMM_PROTOCOL_SINGLE_NO_COMP): + size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // COMM_PROTOCOL_SINGLE_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every frame. + // COMM_PROTOCOL_MULTI_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every 'n' frames. + //..................................................................... + case (COMM_PROTOCOL_SINGLE_E_COMP): + case (COMM_PROTOCOL_MULTI_E_COMP): + size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // Default: We have no idea what to do, so do nothing. + //..................................................................... + default: + size = 0; + break; + } + + return( size ); + +} /* end of Build_Send_Packet */ + + +/*************************************************************************** + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + int ev_size; // size of event we're adding + + //------------------------------------------------------------------------ + // Loop until there are no more events, or we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + if (OutList.First().Type==EventClass::ADDPLAYER) { + ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size; + } + else { + ev_size = sizeof(EventClass); + } + //..................................................................... + // Will the next event exceed the size of the buffer? If so, break. + //..................................................................... + if ( (size + ev_size) > bufsize ) { + return (size); + } + + //..................................................................... + // Set the event's frame delay + //..................................................................... + OutList.First().Frame = Frame + frame_delay; + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList + // event. If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if (!DoList.Add(OutList.First())) { + return (size); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //..................................................................... + // Add event to the send packet + //..................................................................... + if (OutList.First().Type==EventClass::ADDPLAYER) { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + } + else { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + } + + //..................................................................... + // Increment our event counter; delete the last event from the queue + //..................................................................... + num++; + OutList.Next(); + } + + return (size); + +} // end of Add_Uncompressed_Events + + +/*************************************************************************** + * Add_Compressed_Events -- adds an compressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size reference to current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + EventClass::EventType eventtype; // type of event being compressed + EventClass prevevent; // last event processed + int datasize; // size of element plucked from event union + int storedsize; // actual # bytes stored from event + unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count + unsigned char numunits = 0; // megamission rep count value + bool missiondup = false; // flag: is this event a megamission repeat? + + //------------------------------------------------------------------------ + // clear previous event + //------------------------------------------------------------------------ + memset (&prevevent, 0, sizeof(EventClass)); + + if (Debug_Print_Events) { + printf("\n(%d) Building Send Packet\n", Frame); + } + + //------------------------------------------------------------------------ + // Loop until there are no more events, we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + eventtype = OutList.First().Type; + datasize = EventClass::EventLength[ eventtype ]; + //..................................................................... + // For a variable-sized event, pull the size from the event; otherwise, + // the size will be the data element size plus the event type value. + // (The other data elements in the event, Frame, ID, etc, are stored + // in the packet header.) + //..................................................................... + if (eventtype==EventClass::ADDPLAYER) { + storedsize = datasize + sizeof (EventClass::EventType) + + OutList.First().Data.Variable.Size; + } + else { + storedsize = datasize + sizeof (EventClass::EventType); + } + + //..................................................................... + // MegaMission compression: MegaMissions are stored as: + // EventType + // Rep Count + // MegaMission structure (event # 1 only) + // Whom #2 + // Whom #3 + // Whom #4 + // ... + // Whom #n + //..................................................................... + if (prevevent.Type == EventClass::MEGAMISSION) { + //.................................................................. + // If previous & current events are both MegaMissions: + //.................................................................. + if (eventtype == EventClass::MEGAMISSION) { + //............................................................... + // If the Mission, Target, & Destination are the same, compress + // the events into one: + // - Change datasize to the size of the 'Whom' field only + // - set total # bytes to store to the size of the 'Whom' only + // - increment the MegaMission rep count + // - set the MegaMission rep flag + //............................................................... + if (OutList.First().Data.MegaMission.Mission == + prevevent.Data.MegaMission.Mission && + OutList.First().Data.MegaMission.Target == + prevevent.Data.MegaMission.Target && + OutList.First().Data.MegaMission.Destination == + prevevent.Data.MegaMission.Destination) { + + if (Debug_Print_Events) { + printf(" adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n", + OutList.First().Data.MegaMission.Whom.As_TARGET(), + OutList.First().Data.MegaMission.Whom, + MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission), + OutList.First().Data.MegaMission.Target.As_TARGET(), + OutList.First().Data.MegaMission.Target, + OutList.First().Data.MegaMission.Destination.As_TARGET(), + OutList.First().Data.MegaMission.Destination); + } + + datasize = sizeof(prevevent.Data.MegaMission.Whom); + storedsize = datasize; + numunits++; + missiondup = true; + } + //............................................................... + // Data doesn't match; start a new run of MegaMissions: + // - Store previous MegaMission rep count + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //............................................................... + else { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + *unitsptr = numunits; + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + } + //.................................................................. + // Previous event was a MegaMission, but this one isn't: end the + // run of MegaMissions: + // - Store previous MegaMission rep count + // - Clear variables + //.................................................................. + else { + *unitsptr = numunits; // save # events in our run + unitsptr = NULL; // init other values + numunits = 0; + missiondup = false; + } + } + + //..................................................................... + // The previous event is not a MEGAMISSION but the current event is: + // Set up a new run of MegaMissions: + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //..................................................................... + else if (eventtype == EventClass::MEGAMISSION) { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + + //..................................................................... + // Will the next event exceed the size of the buffer? If so, + // stop compressing. + //..................................................................... + if ( (size + storedsize) > bufsize ) + break; + + //..................................................................... + // Set the event's frame delay (this is protocol-dependent) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + OutList.First().Frame = ((Frame + frame_delay + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate; + } + else { + OutList.First().Frame = Frame + frame_delay; + } + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList event. + // If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if ( !DoList.Add( OutList.First() ) ) { + break; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //--------------------------------------------------------------------- + // Compress the event into the send packet buffer + //--------------------------------------------------------------------- + switch ( eventtype ) { + //.................................................................. + // RESPONSE_TIME: just use the Delay field of the FrameInfo union + //.................................................................. + case (EventClass::RESPONSE_TIME): + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.FrameInfo.Delay, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + break; + + //.................................................................. + // MEGAMISSION: + //.................................................................. + case (EventClass::MEGAMISSION): + //............................................................... + // Repeated mission in a run: + // - Update the rep count (in case we break out) + // - Copy the Whom field only + //............................................................... + if (missiondup) { + *unitsptr = numunits; + + memcpy ( ((char *)buf) + size, + &OutList.First().Data.MegaMission.Whom, datasize ); + + size += datasize; + } + //............................................................... + // 1st mission in a run: + // - Init the rep count (in case we break out) + // - Set the EventType + // - Copy the MegaMission structure, leaving room for 'numunits' + //............................................................... + else { + *unitsptr = numunits; + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + + sizeof(EventClass::EventType) + sizeof(numunits), + &OutList.First().Data.MegaMission, datasize ); + + size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits)); + } + break; + + //.................................................................. + // Variable-sized packets: Copy the packet Size & the buffer + //.................................................................. + case (EventClass::ADDPLAYER): + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.Variable.Size, datasize ); + size += (datasize + sizeof(EventClass::EventType)); + + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + + break; + + //.................................................................. + // Default case: Just copy over the data field from the union + //.................................................................. + default: + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + + break; + } + + //--------------------------------------------------------------------- + // update # events processed + //--------------------------------------------------------------------- + num++; + + //--------------------------------------------------------------------- + // Update 'prevevent' + //--------------------------------------------------------------------- + memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) ); + + //--------------------------------------------------------------------- + // Go to the next event to process + //--------------------------------------------------------------------- + OutList.Next(); + } + + if (Debug_Print_Events) { + printf("\n"); + } + + return (size); + +} // end of Add_Compressed_Events + + +/*************************************************************************** + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * * + * INPUT: * + * buf buffer to break up * + * bufsize length of buffer * + * * + * OUTPUT: * + * # events added to queue, -1 if fatal error (queue is full) * + * (return value includes any FRAMEINFO packets encountered; * + * FRAMESYNC's are ignored) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Breakup_Receive_Packet(void *buf, int bufsize ) +{ + int count = 0; + + /* + ** is there enough leftover for another record + */ + switch (Session.CommProtocol) { + case (COMM_PROTOCOL_SINGLE_NO_COMP): + count = Extract_Uncompressed_Events(buf, bufsize); + break; + + default: + count = Extract_Compressed_Events(buf, bufsize); + break; + } + + return (count); + +} /* end of Breakup_Receive_Packet */ + + +/*************************************************************************** + * Extract_Uncompressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Uncompressed_Events(void *buf, int bufsize) +{ + int count = 0; + int pos = 0; + int leftover = bufsize; + EventClass *event; + + //------------------------------------------------------------------------ + // Loop until there are no more events in the packet + //------------------------------------------------------------------------ + while (leftover >= sizeof(EventClass) ) { + + Keyboard->Check(); + + event = (EventClass *)(((char *)buf) + pos); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + event->IsExecuted = 0; + + //.................................................................. + // Special processing for variable-sized events + //.................................................................. + if (event->Type == EventClass::ADDPLAYER) { + event->Data.Variable.Pointer = new char[event->Data.Variable.Size]; + memcpy (event->Data.Variable.Pointer, + ((char *)buf) + sizeof(EventClass), + event->Data.Variable.Size); + + pos += event->Data.Variable.Size; + leftover -= event->Data.Variable.Size; + } + + if (!DoList.Add( *event )) { + if (event->Type == EventClass::ADDPLAYER) { + delete [] event->Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(*event); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + } + + //..................................................................... + // Point to the next position in the buffer; decrement our 'leftover' + //..................................................................... + pos += sizeof(EventClass); + leftover -= sizeof(EventClass); + } + + return (count); + +} // end of Extract_Uncompressed_Events + + +/*************************************************************************** + * Extract_Compressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Compressed_Events(void *buf, int bufsize) +{ + int pos = 0; // current buffer parsing position + int leftover = bufsize; // # bytes left to process + EventClass *event; // event ptr for parsing buffer + int count = 0; // # events processed + int datasize = 0; // size of data to copy + EventClass eventdata; // stores Frame, ID, etc + unsigned char numunits = 0; // # units stored in compressed MegaMissions + + //------------------------------------------------------------------------ + // Clear work event structure + //------------------------------------------------------------------------ + memset (&eventdata, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Assume the first event is a FRAMEINFO event + // Init 'datasize' to the amount of data to copy, minus the EventType value + // For the 1st packet only, this will include all info before the Data + // union, plus the size of the FrameInfo structure, minus the EventType size. + //------------------------------------------------------------------------ + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType); + event = (EventClass *)(((char *)buf) + pos); + + while (leftover >= (datasize + sizeof(EventClass::EventType)) ) { + + Keyboard->Check(); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + //.................................................................. + // initialize the common data from the FRAMEINFO event + // keeping IsExecuted 0 + //.................................................................. + if (event->Type == EventClass::FRAMEINFO) { + eventdata.Frame = event->Frame; + eventdata.ID = event->ID; + + //............................................................... + // Adjust position past the common data + //............................................................... + pos += (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + leftover -= (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + } + //.................................................................. + // if MEGAMISSION event get the number of units (events to generate) + //.................................................................. + else if (event->Type == EventClass::MEGAMISSION) { + numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type)); + pos += sizeof(numunits); + leftover -= sizeof(numunits); + } + + //.................................................................. + // clear the union data portion of the event + //.................................................................. + memset (&eventdata.Data, 0, sizeof(eventdata.Data)); + eventdata.Type = event->Type; + datasize = EventClass::EventLength[ eventdata.Type ]; + + switch (eventdata.Type) { + case (EventClass::RESPONSE_TIME): + memcpy ( &eventdata.Data.FrameInfo.Delay, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + + case (EventClass::ADDPLAYER): + + memcpy ( &eventdata.Data.Variable.Size, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + eventdata.Data.Variable.Pointer = + new char[eventdata.Data.Variable.Size]; + memcpy (eventdata.Data.Variable.Pointer, + ((char *)buf) + pos + sizeof(EventClass::EventType) + datasize, + eventdata.Data.Variable.Size); + + pos += eventdata.Data.Variable.Size; + leftover -= eventdata.Data.Variable.Size; + + break; + + case (EventClass::MEGAMISSION): + memcpy ( &eventdata.Data.MegaMission, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + if (numunits > 1) { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + datasize = sizeof(eventdata.Data.MegaMission.Whom); + + while (numunits) { + + Keyboard->Check(); + + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //...................................................... + // Keep count of how many events we add to the queue + //...................................................... + count++; + numunits--; + memcpy ( &eventdata.Data.MegaMission.Whom, + ((char *)buf) + pos, datasize ); + + //...................................................... + // if one unit left fall thru to normal code + //...................................................... + if (numunits == 1) { + datasize -= sizeof(EventClass::EventType); + break; + } + else { + pos += datasize; + leftover -= datasize; + } + } + } + break; + + default: + memcpy ( &eventdata.Data, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + } + + if ( !DoList.Add( eventdata ) ) { + if (eventdata.Type == EventClass::ADDPLAYER) { + delete [] eventdata.Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + + if (leftover) { + event = (EventClass *)(((char *)buf) + pos); + datasize = EventClass::EventLength[ event->Type ]; + if (event->Type == EventClass::MEGAMISSION) { + datasize += sizeof(numunits); + } + } + } + //..................................................................... + // FRAMESYNC event: This >should< be the only event in the buffer, + // and it will be uncompressed. + //..................................................................... + else { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + event = (EventClass *)(((char *)buf) + pos); + + //.................................................................. + // size of FRAMESYNC event - EventType size + //.................................................................. + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - + sizeof(EventClass::EventType); + } + } + + return (count); + +} // end of Extract_Compressed_Events + + +/*************************************************************************** + * Execute_DoList -- Executes commands from the DoList * + * * + * This routine executes any events in the DoList that need to be executed * + * on the current game frame. The events must be executed in a special * + * order, so that all systems execute all events in exactly the same * + * order. * + * * + * This routine also handles checking the Game CRC sent by other systems * + * against my own, to be sure we're still in sync. * + * * + * INPUT: * + * max_houses # houses to execute commands for * + * base_house HousesType to start with * + * net ptr to connection manager; NULL if none * + * skip_crc a frame-based countdown timer; if it's non-zero, the * + * CRC check will be skipped. Ignored if NULL. * + * their_frame array of their frame #'s * + * their_sent array of # commands they've sent * + * their_recv array of # commands I've received from them * + * * + * (their_xxx are ignored if 'net' is NULL.) * + * * + * OUTPUT: * + * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + HousesType house; + HouseClass *hptr; + int i,j,k; + int index; + int check_crc; + + Check_Mirror(); + +#if(TIMING_FIX) + // + // If MPlayerMaxAhead is recomputed such that it increases, the systems + // may try to free-run to the new MaxAhead value. If so, they may miss + // an event that was generated after the TIMING event was created, but + // before it executed; this event will be scheduled with the older, + // shorter MaxAhead value. If a system doesn't receive this event, it + // may execute past the frame it's scheduled to execute on, creating + // a Packet-Recieved-Too-Late error. To prevent this, find any events + // that are scheduled to execute during this "period of vulnerability", + // and re-schedule for the end of that period. + // + for (j = 0; j < DoList.Count; j++) { + if (DoList[j].Type != EventClass::FRAMEINFO && + DoList[j].Frame > NewMaxAheadFrame1 && + DoList[j].Frame < NewMaxAheadFrame2) { + DoList[j].Frame = NewMaxAheadFrame2; + #ifdef MIRROR_QUEUE + MirrorList[j].Frame = NewMaxAheadFrame2; + #endif + } + } +#endif + + //------------------------------------------------------------------------ + // Execute the DoList. Events must be executed in the same order on all + // systems; so, execute them in the order of the HouseClass array. This + // array is stored in the same order on all systems. + //------------------------------------------------------------------------ + for (i = 0; i < max_houses; i++) { + //..................................................................... + // Convert our index into a HousesType value + //..................................................................... + house = (HousesType)(i + base_house); + hptr = HouseClass::As_Pointer(house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!hptr) { + continue; + } + if (!hptr->IsHuman) { + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (j = 0; j < DoList.Count; j++) { + + if (net) + Update_Queue_Mono (net, 6); + + //.................................................................. + // If this event was from the currently-executing player ID, and it's + // time to execute it, execute it. + //.................................................................. + if (DoList[j].ID == hptr->ID && Frame >= DoList[j].Frame && + !DoList[j].IsExecuted) { + + //............................................................... + // Error if it's too late to execute this packet! + // (Hack: disable this check for solo or skirmish mode.) + //............................................................... + if (Frame > DoList[j].Frame && DoList[j].Type != + EventClass::FRAMEINFO && Session.Type != GAME_NORMAL && + Session.Type != GAME_SKIRMISH) { + +#if(TEN) + Send_TEN_Packet_Too_Late(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Packet_Too_Late(); +#endif // MPATH + + Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame, + their_sent, their_recv); + WWMessageBox().Process (TXT_PACKET_TOO_LATE); + return (0); + } + + //............................................................... + // Only execute EXIT & OPTIONS commands if they're from myself. + //............................................................... + if (DoList[j].Type==EventClass::EXIT || + DoList[j].Type==EventClass::OPTIONS) { + +#ifdef WIN32 + if (DoList[j].Type==EventClass::EXIT) { + /* + ** Flag that this house lost because it quit. + */ + HousesType quithouse; + HouseClass *quithptr; + + for (int player = 0; player < max_houses ; player++) { + quithouse = (HousesType)(player + base_house); + quithptr = HouseClass::As_Pointer(quithouse); + if (!quithptr) { + continue; + } + if (quithptr->ID == DoList[j].ID) { + quithptr->IsGiverUpper = true; + break; + } + } + + /* + ** Send the game statistics packet now since the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Event - player aborted, and there were only 2 left. + } + } +#endif //WIN32 + + if (Debug_Print_Events) { + if (DoList[j].Type==EventClass::EXIT) { + printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n", + Frame, + DoList[j].ID, + (HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName, + DoList[j].Frame); + } + } + + if (DoList[j].ID == PlayerPtr->ID) { + DoList[j].Execute(); + } else if (DoList[j].Type==EventClass::EXIT) { + //............................................................ + // If this EXIT event isn't from myself, destroy the connection + // for that player. The HousesType for this event is the + // connection ID. + //............................................................ + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + Destroy_Null_Connection( house, 0 ); + } else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || + Session.Type == GAME_MPATH) && net) { + index = net->Connection_Index (house); + if (index != -1) { + for (k = index; k < net->Num_Connections() - 1; k++) { + their_frame[k] = their_frame[k+1]; + their_sent[k] = their_sent[k+1]; + their_recv[k] = their_recv[k+1]; + } + if (Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) { + Destroy_Connection(house,0); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(house,0); + } +#endif // TEN +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(house,0); + } +#endif // MPATH + } + } + // + // Special case for recording playback: turn the house over + // to the computer. + // + if (Session.Play && DoList[j].Type==EventClass::EXIT) { + hptr->IsHuman = false; + hptr->IQ = Rule.MaxIQ; + hptr->Computer_Paranoid(); + strcpy (hptr->IniName,Text_String(TXT_COMPUTER)); + Session.NumPlayers--; + } + } + } + + //............................................................... + // For a FRAMEINFO event, check the CRC value. + //............................................................... + else if (DoList[j].Type == EventClass::FRAMEINFO) { + //............................................................ + // Skip the CRC check if we're less than 32 frames into the game; + // this will prevent a newly-loaded modem game from instantly + // going out of sync, if the games were saved at different + // frame numbers. + //............................................................ + if (!skip_crc || *skip_crc == 0) { + check_crc = 1; + } + else { + check_crc = 0; + } + if (check_crc + && DoList[j].Frame == Frame + && DoList[j].Data.FrameInfo.Delay < 32) { + index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) & + 0x001f); + if (CRC[index] != DoList[j].Data.FrameInfo.CRC) { + Print_CRCs(&DoList[j]); + +#if(TEN) + Send_TEN_Out_Of_Sync(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Out_Of_Sync(); +#endif // MPATH + + if (WWMessageBox().Process (TXT_OUT_OF_SYNC, + TXT_CONTINUE, TXT_STOP) == 0) { + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + Destroy_Null_Connection( house, -1 ); + Shutdown_Modem(); + Session.Type = GAME_NORMAL; + } + else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) && net) { + while (net->Num_Connections()) { + Keyboard->Check(); + Destroy_Connection (net->Connection_ID(0), -1); + } + } +#if(TEN) + else if (Session.Type == GAME_TEN && net) { + while (net->Num_Connections()) { + Destroy_TEN_Connection (net->Connection_ID(0), -1); + } + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH && net) { + while (net->Num_Connections()) { + Destroy_MPATH_Connection (net->Connection_ID(0), -1); + } + } +#endif + Map.Flag_To_Redraw(true); + } + else { + return (0); + } + return (1); + } + } + } + //............................................................... + // Execute other commands + //............................................................... + else { + DoList[j].Execute(); + } + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + #ifdef MIRROR_QUEUE + MirrorList[j].IsExecuted = 1; + #endif + } + } + } + + return (1); + +} // end of Execute_DoList + + +/*************************************************************************** + * Clean_DoList -- Cleans out old events from the DoList * + * * + * Currently, an event can only be removed from the DoList if it's at the * + * head of the list; and event can't be removed from the middle. So, * + * this routine loops as long as the next event in the DoList has been * + * executed, it's removed. * + * * + * INPUT: * + * net ptr to connection manager; ignored if NULL * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Clean_DoList(ConnManClass *net) +{ + while (DoList.Count) { + + Keyboard->Check(); + + if (net) + Update_Queue_Mono (net, 7); + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || (Frame > DoList.First().Frame) ) { + DoList.Next(); + #ifdef MIRROR_QUEUE + MirrorList.Next(); + #endif + } + else { + break; + } + } + +} // end of Clean_DoList + + +/*************************************************************************** + * Queue_Record -- Records the DoList to disk * + * * + * This routine just saves any events in the DoList to disk; we can later * + * "play back" the recording just be pulling events from disk rather than * + * from the network! * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/14/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Record(void) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # of events to save this frame + //------------------------------------------------------------------------ + j = 0; + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + j++; + } + } + + //------------------------------------------------------------------------ + // Save the # of events, then all events. + //------------------------------------------------------------------------ + Session.RecordFile.Write (&j,sizeof(j)); + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + Session.RecordFile.Write (&DoList[i],sizeof (EventClass)); + j--; + } + } + +} /* end of Queue_Record */ + + +/*************************************************************************** + * Queue_Playback -- plays back queue entries from a record file * + * * + * This routine reads events from disk, putting them into the DoList; * + * it then executes the DoList just like the network version does. The * + * result is that the game "plays back" like a recording. * + * * + * This routine detects mouse motion and stops playback, so it can work * + * like an "attract" mode, showing a demo of the game itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Playback(void) +{ + int numevents; + EventClass event; + int i; + int ok; + static int mx,my; + int max_houses; + HousesType base_house; + int key; + int testframe; + + //------------------------------------------------------------------------ + // If the user hits ESC, stop the playback + //------------------------------------------------------------------------ + if (Keyboard->Check()) { + key = Keyboard->Get(); + if (key == KA_ESC || Session.Attract) { + GameActive = 0; + return; + } + } + + //------------------------------------------------------------------------ + // If we're in "Attract" mode, and the user moves the mouse, stop the + // playback. + //------------------------------------------------------------------------ + if (Session.Attract && Frame > 0 && + (mx != Get_Mouse_X() || my != Get_Mouse_Y())) { + GameActive = 0; + return; + } + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've reached the CRC print frame, do so & exit + //------------------------------------------------------------------------ + if (Frame >= Session.TrapPrintCRC) { + Print_CRCs(NULL); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Don't read anything the first time through (since the Queue_AI_Network + // routine didn't write anything the first time through); do this after the + // CRC is computed, since we'll still need a CRC for Frame 0. + //------------------------------------------------------------------------ + if (Frame==0 && Session.Type!=GAME_NORMAL) { + return; + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + testframe = ((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (Frame != testframe) { + return; + } + } + + //------------------------------------------------------------------------ + // Read the DoList from disk + //------------------------------------------------------------------------ + ok = 1; + if (Session.RecordFile.Read (&numevents, sizeof(numevents)) == + sizeof(numevents)) { + for (i = 0; i < numevents; i++) { + if (Session.RecordFile.Read (&event, sizeof(EventClass)) == + sizeof(EventClass)) { + event.IsExecuted = 0; + DoList.Add (event); + #ifdef MIRROR_QUEUE + MirrorList.Add(event); + #endif + } + else { + ok = 0; + break; + } + } + } + else { + ok = 0; + } + + if (!ok) { + GameActive = 0; + return; + } + + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (Session.Type == GAME_NORMAL) { + max_houses = 1; + base_house = PlayerPtr->Class->House; + } + else { + max_houses = Session.MaxPlayers; + base_house = HOUSE_MULTI1; + } + if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_Playback */ + + +/*************************************************************************** + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Compute_Game_CRC(void) +{ + int i,j; + VesselClass *vessp; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + ObjectClass *objp; + HouseClass *housep; + + GameCRC = 0; + + //------------------------------------------------------------------------ + // Infantry + //------------------------------------------------------------------------ + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + } + + //------------------------------------------------------------------------ + // Units + //------------------------------------------------------------------------ + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + } + + //------------------------------------------------------------------------ + // Shippies + //------------------------------------------------------------------------ + for (i = 0; i < Vessels.Count(); i++) { + vessp = (VesselClass *)Vessels.Active_Ptr(i); + Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom); + Add_CRC (&GameCRC, (int)vessp->Strength); + Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom); + } + + //------------------------------------------------------------------------ + // Buildings + //------------------------------------------------------------------------ + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + } + + //------------------------------------------------------------------------ + // Houses + //------------------------------------------------------------------------ + for (i = 0; i < Houses.Count(); i++) { + housep = (HouseClass *)Houses.Active_Ptr(i); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + for (i = 0; i < LAYER_COUNT; i++) { + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + } + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + + //------------------------------------------------------------------------ + // A random # + //------------------------------------------------------------------------ +// Add_CRC(&GameCRC, Scen.RandomNumber.Seed); + Add_CRC(&GameCRC, Scen.RandomNumber); + +} /* end of Compute_Game_CRC */ + + +/*************************************************************************** + * Add_CRC -- Adds a value to a CRC * + * * + * INPUT: * + * crc ptr to crc * + * val value to add * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Add_CRC(unsigned long *crc, unsigned long val) +{ + int hibit; + + if ( (*crc) & 0x80000000) { + hibit = 1; + } + else { + hibit = 0; + } + + (*crc) <<= 1; + (*crc) += val; + (*crc) += hibit; + +} /* end of Add_CRC */ + +/*************************************************************************** + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * * + * INPUT: * + * ev -- event to display * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Print_CRCs(EventClass *ev) +{ + int i,j; + InfantryClass *infp; + UnitClass *unitp; + VesselClass *vesselp; + BuildingClass *bldgp; + ObjectClass *objp; + FILE *fp; + HouseClass *housep; + HousesType house; + int color; + + Mono_Clear_Screen(); + Mono_Set_Cursor (0,0); + fp = fopen("OUT.TXT","wt"); + if (fp==NULL) { + return; + } + + for (i = 0; i < 32; i++) { + fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]); + } + + // + // Houses + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + GameCRC = 0; + housep = HouseClass::As_Pointer (house); + if (housep) { + HousesType actlike = housep->ActLike; + color = housep->RemapColor; + fprintf(fp,"%s: IsHuman:%d Color:%s ID:%d ActLike:%s\n", + housep->IniName, + housep->IsHuman, + ColorNames[color], + housep->ID, + HouseClass::As_Pointer(actlike)->Class->Name()); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Infantry + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Infantry -------------------\n", + housep->Class->Name()); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==house) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x Speed:%d NavCom:%x\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom); + } + } + Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Units + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Units -------------------\n", + housep->Class->Name()); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==house) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d Tgt:%x\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type, unitp->As_Target()); + } + } + Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Vessels + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Vessels -------------------\n", + housep->Class->Name()); + for (i = 0; i < Vessels.Count(); i++) { + vesselp = (VesselClass *)Vessels.Active_Ptr(i); + if (vesselp->Owner()==house) { + Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom); + Add_CRC (&GameCRC, (int)vesselp->Strength); + Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom); + fprintf(fp, + "COORD:%x Facing:%d Mission:%d Strength:%d Type:%d Tgt:%x\n", + vesselp->Coord,(int)vesselp->PrimaryFacing, + vesselp->Get_Mission(), vesselp->Strength, + vesselp->Class->Type, vesselp->As_Target()); + } + } + Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Buildings + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Buildings -------------------\n", + housep->Class->Name()); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==house) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type, bldgp->As_Target()); + } + } + Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Animations + // + AnimClass *animp; + fprintf(fp,"-------------------- Animations -------------------\n"); + for (i = 0; i < Anims.Count(); i++) { + animp = (AnimClass *)Anims.Active_Ptr(i); + fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n", + animp->xObject, + animp->OwnerHouse, + animp->Loops); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + GameCRC = 0; + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,">>>> MAP LAYER %d <<<<\n",i); + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",j,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + else if (objp->What_Am_I() == RTTI_VESSEL) + fprintf(fp,"Vessel (Type:%d) ", + (VesselType)(*((VesselClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + } + Mono_Printf("Map Layers:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + GameCRC = 0; + fprintf(fp,">>>> LOGIC LAYER <<<<\n"); + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",i,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + Mono_Printf("Logic:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Random # generator, frame # + //------------------------------------------------------------------------ + Mono_Printf("Random Number:%x \n",Scen.RandomNumber.Seed); +#ifdef RANDOM_COUNT + fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n", + Scen.RandomNumber.Seed, + Scen.RandomNumber.Count1, + Scen.RandomNumber.Count2); +#else + fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed); +#endif + + Mono_Printf("My Frame:%d \n",Frame); + fprintf(fp,"My Frame:%d\n",Frame); + + if (ev) { + fprintf(fp,"\n"); + fprintf(fp,"Offending event:\n"); + fprintf(fp," Type: %d\n",ev->Type); + fprintf(fp," Frame: %d\n",ev->Frame); + fprintf(fp," ID: %x\n",ev->ID); + fprintf(fp," CRC: %x\n",ev->Data.FrameInfo.CRC); + fprintf(fp," CommandCount: %d\n",ev->Data.FrameInfo.CommandCount); + fprintf(fp," Delay: %d\n",ev->Data.FrameInfo.Delay); + } + + fclose(fp); + +} /* end of Print_CRCs */ + + +/*************************************************************************** + * Init_Queue_Mono -- inits mono display * + * * + * This routine steals control of the mono screen away from the rest of * + * the engine, by setting the global IsMono; if IsMono is set, the other * + * routines in this module turn off the Mono display when they're done * + * with it, so the rest of the engine won't over-write what we're writing. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Init_Queue_Mono(ConnManClass *net) +{ +#if(SHOW_MONO) + //------------------------------------------------------------------------ + // Set 'IsMono' so we can steal the mono screen from the engine + //------------------------------------------------------------------------ + if ((Frame==0 || Session.LoadGame) && MonoClass::Is_Enabled()) { + IsMono = true; + } + + //------------------------------------------------------------------------ + // Enable mono output for our stuff; we must Disable it before we return + // control to the engine. + //------------------------------------------------------------------------ + if (IsMono) + MonoClass::Enable(); + + if (net->Num_Connections() > 0) { + //..................................................................... + // Network mono debugging screen + //..................................................................... + if (NetMonoMode==0) { + if (Frame==0 || Session.LoadGame || NewMonoMode) { + net->Configure_Debug (0, sizeof (CommHeaderType), + sizeof(EventClass::EventType), EventClass::EventNames, 0, 27); + net->Mono_Debug_Print (0,1); + NewMonoMode = 0; + } + else { + net->Mono_Debug_Print (0,0); + } + } + //..................................................................... + // Flow control debugging output + //..................................................................... + else { + if (NewMonoMode) { + Mono_Clear_Screen(); + Mono_Printf(" Queue AI:\n"); // flowcount[0] + Mono_Printf(" Build Packet Loop:\n"); // flowcount[1] + Mono_Printf(" Frame Sync:\n"); // flowcount[2] + Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3] + Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4] + Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5] + Mono_Printf(" DoList Execution:\n"); // flowcount[6] + Mono_Printf(" DoList Cleaning:\n"); // flowcount[7] + Mono_Printf("\n"); + Mono_Printf(" Frame:\n"); + Mono_Printf(" Session.MaxAhead:\n"); + Mono_Printf(" their_recv:\n"); + Mono_Printf(" their_sent:\n"); + Mono_Printf(" my_sent:\n"); + NewMonoMode = 0; + } + } + } +#else + net = net; +#endif +} // end of Init_Queue_Mono + + +/*************************************************************************** + * Update_Queue_Mono -- updates mono display * + * * + * INPUT: * + * net ptr to connection manager * + * flow_index index # for flow-count updates * + * -1: display * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Update_Queue_Mono(ConnManClass *net, int flow_index) +{ +#if(SHOW_MONO) + static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + //------------------------------------------------------------------------ + // If 'NetMonoMode' is 1, display flowcount info + //------------------------------------------------------------------------ + if (NetMonoMode==1) { + if (flow_index >= 0 && flow_index < 20) { + Mono_Set_Cursor(35,flow_index); + flowcount[flow_index]++; + Mono_Printf("%d",flowcount[flow_index]); + } + } + //------------------------------------------------------------------------ + // Otherwise, display the connection debug screen + //------------------------------------------------------------------------ + else { + net->Mono_Debug_Print (0,0); + } + +#else + flow_index = flow_index; + net = net; +#endif + +} // end of Update_Queue_Mono + + +/*************************************************************************** + * Print_Framesync_Values -- displays frame-sync variables * + * * + * INPUT: * + * curframe current game Frame # * + * max_ahead max-ahead value * + * num_connections # connections * + * their_recv # commands I've received from my connections * + * their_sent # commands each connection claims to have sent * + * my_sent # commands I've sent * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent) +{ +#if(SHOW_MONO) + int i; + + if (NetMonoMode==1) { + Mono_Set_Cursor(35,9); + Mono_Printf("%d",curframe); + + Mono_Set_Cursor(35,10); + Mono_Printf("%d",max_ahead); + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,11); + Mono_Printf("%4d",(int)their_recv[i]); + } + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,12); + Mono_Printf("%4d",(int)their_sent[i]); + } + + Mono_Set_Cursor(35,13); + Mono_Printf("%4d",(int)my_sent); + } +#else + curframe = curframe; + max_ahead = max_ahead; + num_connections = num_connections; + their_recv = their_recv; + their_sent = their_sent; + my_sent = my_sent; +#endif +} // end of Print_Framesync_Values + + +/*************************************************************************** + * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk * + * * + * INPUT: * + * event ptr to event to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/28/1996 BRR : Created. * + *=========================================================================*/ +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + FILE *fp; + int i; + HousesType house; + + fp = fopen("toolate.txt", "wt"); + if (!fp) { + return; + } + fprintf(fp,"----------------- Event data: ----------------------\n"); + fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame: %d\n",event->Frame); + fprintf(fp,"ID: %d\n",event->ID); + + for (i = 0; i < Session.Players.Count(); i++) { + if (event->ID == Session.Players[i]->Player.ID) { + fprintf(fp,"Player's Name: %s",Session.Players[i]->Name); + } + } + fprintf(fp,"\n"); + + fprintf(fp,"--------------------- My data: ---------------------\n"); + fprintf(fp,"My Frame:%d\n",Frame); + fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead); + + if (net) { + fprintf(fp,"-------------------- Frame Stats: ------------------\n"); + fprintf(fp,"Name ID TheirFrame TheirSent TheirRecv\n"); + for (i = 0; i < net->Num_Connections(); i++) { + house = (HousesType)(net->Connection_ID(i)); + fprintf(fp,"%12s %2d %6d %6d %6d\n", + (HouseClass::As_Pointer(house))->IniName, + net->Connection_ID(i), + their_frame[i], + their_sent[i], + their_recv[i]); + } + } + + fclose(fp); +} + +/*************************************************************************** + * Check_Mirror -- Checks mirror memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/14/1996 BRR : Created. * + *=========================================================================*/ +void Check_Mirror(void) +{ +#ifdef MIRROR_QUEUE + int i; + char txt[80]; + unsigned long *ptr; + int found_5s = 0; + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + for (i = 0; i < DoList.Count; i++) { + if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) { + sprintf(txt,"Queue Memory Trashed! Head:%d Tail:%d, Addr:%p or %p", + DoList.Get_Head(), + DoList.Get_Tail(), + DoList.Get_Array() + i, + MirrorList.Get_Array() + i); + WWMessageBox().Process (txt); + //Prog_End(); + Emergency_Exit(0); + } + } + + if (found_5s) { + //Prog_End(); + Emergency_Exit(0); + } + +#endif +} // end of Check_Mirror + + +/*************************** end of queue.cpp ******************************/ diff --git a/CODE/QUEUE.H b/CODE/QUEUE.H new file mode 100644 index 0000000..bf5182a --- /dev/null +++ b/CODE/QUEUE.H @@ -0,0 +1,295 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/QUEUE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : QUEUE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/08/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * QueueClass::Add -- Add object to queue. * + * QueueClass::First -- Fetches reference to first object in list. * + * QueueClass::Init -- Initializes queue to empty state. * + * QueueClass::Next -- Throws out the head of the line. * + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include "mission.h" +#include "target.h" +#include "defines.h" + +#pragma warn -inl + +/* +** This class implements a classic FIFO queue (also known as - standing in line). Objects +** are added to the end (tail) of the line. Objects are removed from the start (first) of +** the line. A keyboard buffer is a good example of a common computer use of a queue. There +** is no provision for "taking cuts" or leaving the line once an object has been added. +** +** The implementation uses a circular list of objects. This allows adding and deleting of +** elements without any maintenance moves of remaining objects that would otherwise be +** necessary in a simple array storage method. A side effect of this means that accessing the +** internal array directly is not allowed. Supporting functions are provided for this purpose. +** +** WARNING WARNING WARNING WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +** The size parameter MUST be an exact power of two (2, 4, 8, 16, etc.) otherwise the internal +** indexing algorithm will fail. +*/ +template +class QueueClass +{ + public: + /* + ** This is the count of the number of objects in the queue. If this count is zero, + ** then the operator[], First(), and Next() functions are undefined. Check this + ** value BEFORE calling these functions. + */ + const int Count; + + //-------------- Functions -------------------- + QueueClass(void); // Default constructor. + + /* + ** The bracket subscript operator functions similarly to the way a normal subscript + ** operator works except that entry [0] matches the first-in-line and entry + ** [Count-1] matches the last-in-line. This is ensured regardless of the actual position + ** of the object in the circular internal list. + */ + T & operator[](int); + + /* + ** This function will return a reference to the "head of the line" object. + */ + T & First(void); + + /* + ** This function clears the list of objects. + */ + void Init(void); + + /* + ** This function discards the head-of-the-line object and advances all the remaining + ** objects up by one. Mnemonic: Imagine a broadway audition and the director yells + ** "NEXT!" + */ + int Next(void); + + /* + ** This will add an object to the tail of the line. If there is no more room to add + ** the object, then false will be returned. + */ + int Add(T const &); + + int Get_Head(void); + int Get_Tail(void); + T * Get_Array(void); + + private: + int Head; // Index of element in list the longest. + int Tail; // Index where next new addition will go. + + T Array[size]; // Raw array of objects. +}; + + +/*********************************************************************************************** + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * * + * This default constructor for QueueClass objects initializes the queue to an empty * + * state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline QueueClass::QueueClass(void) : Count(0) +{ + Init(); +} + + +/*********************************************************************************************** + * QueueClass::Init -- Initializes queue to empty state. * + * * + * This function resets the queue to an empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline void QueueClass::Init(void) +{ + ((int &)Count) = 0; + Head = 0; + Tail = 0; +} + + +/*********************************************************************************************** + * QueueClass::Add -- Add object to queue. * + * * + * This function is used to add an object to the tail of the line. If the queue cannot * + * accept any more entries, then the object won't be added and false will be returned. * + * * + * INPUT: object -- The object that is to be added to the queue. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: If the queue is full, then the object won't be added. Be sure to check for this.* + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Add(T const &q) +{ + if (Count < size) { + Array[Tail] = q; + Tail = (Tail + 1) & (size-1); + ((int &)Count) = Count + 1; + return(true); + } + return (false); +} + + +/*********************************************************************************************** + * QueueClass::Next -- Throws out the head of the line. * + * * + * This routine is used to discard the object at the head of the line. All remaining * + * objects "move up" one. No actual movement occurs, merely the index is adjusted, but * + * the affect is that the next oldest object in the queue will now be returned with the * + * next call to the First() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of object remaining in the queue. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Next(void) +{ + if (Count) { + Head = (Head + 1) & (size-1); + ((int &)Count) = Count - 1; + } + return (Count); +} + + +/*********************************************************************************************** + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * * + * Use this routine to examine individual objects within the queue. The oldest object in * + * the queue is referenced by an index value of zero. The newest object in the queue is * + * referenced by a value of Count-1. If there are no objects in the queue, then this * + * operator is undefined. Although this operator allows examination of the queue, there is * + * no corresponding ability to insert or delete objects from the middle of the queue. * + * * + * INPUT: index -- The index into the queue of objects. Valid values range from zero to * + * Count-1. All other values return an undefined reference! * + * * + * OUTPUT: Returns with a reference to the object indicated by the index. * + * * + * WARNINGS: Check to make sure that Count is not zero before using this operator. Failure * + * to do so will return a reference to an undefined object. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::operator[](int index) +{ + return Array[(Head + index) & (size-1)]; +} + + +/*********************************************************************************************** + * QueueClass::First -- Fetches reference to first object in list. * + * * + * This routine is used to fetch the first object in the list (head of the line). This * + * object is the oldest in the list. Typical use of this function is to get and process * + * the first object so that it may be discarded with the Next() function in order to bring * + * subsequent objects to the first position. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the oldest object in the queue. * + * * + * WARNINGS: If there are no objects in the queue, then this function returns an undefined * + * reference. Be sure to check Count against zero before calling this function. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::First(void) +{ + return Array[Head]; +} + +template +inline int QueueClass::Get_Head(void) +{ + return Head; +} + +template +inline int QueueClass::Get_Tail(void) +{ + return Tail; +} + +template +inline T * QueueClass::Get_Array(void) +{ + return Array; +} + +#endif diff --git a/CODE/RA-HDOS.PJT b/CODE/RA-HDOS.PJT new file mode 100644 index 0000000..0c27d1f --- /dev/null +++ b/CODE/RA-HDOS.PJT @@ -0,0 +1,478 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[Files] +e:\c&czero\code\2KEYFRAM.CPP +e:\c&czero\code\AADATA.CPP +e:\c&czero\code\ABSTRACT.CPP +e:\c&czero\code\ABSTRACT.H +e:\c&czero\code\ADATA.CPP +e:\c&czero\code\AIRCRAFT.CPP +e:\c&czero\code\AIRCRAFT.H +e:\c&czero\code\ANIM.CPP +e:\c&czero\code\ANIM.H +e:\c&czero\code\AUDIO.CPP +e:\c&czero\code\B64PIPE.CPP +e:\c&czero\code\B64PIPE.H +e:\c&czero\code\B64STRAW.CPP +e:\c&czero\code\B64STRAW.H +e:\c&czero\code\BAR.CPP +e:\c&czero\code\BAR.H +e:\c&czero\code\BASE.CPP +e:\c&czero\code\BASE.H +e:\c&czero\code\BASE64.CPP +e:\c&czero\code\BASE64.H +e:\c&czero\code\BBDATA.CPP +e:\c&czero\code\BDATA.CPP +e:\c&czero\code\BENCH.CPP +e:\c&czero\code\BENCH.H +e:\c&czero\code\BFIOFILE.CPP +e:\c&czero\code\BFIOFILE.H +e:\c&czero\code\BLOWFISH.CPP +e:\c&czero\code\BLOWFISH.H +e:\c&czero\code\BLOWPIPE.CPP +e:\c&czero\code\BLOWPIPE.H +e:\c&czero\code\BLWSTRAW.CPP +e:\c&czero\code\BLWSTRAW.H +e:\c&czero\code\BUFF.CPP +e:\c&czero\code\BUFF.H +e:\c&czero\code\BUFFERX.H +e:\c&czero\code\BUILDING.CPP +e:\c&czero\code\BUILDING.H +e:\c&czero\code\BULLET.CPP +e:\c&czero\code\BULLET.H +e:\c&czero\code\CARGO.CPP +e:\c&czero\code\CARGO.H +e:\c&czero\code\CARRY.CPP +e:\c&czero\code\CARRY.H +e:\c&czero\code\CCDDE.CPP +e:\c&czero\code\CCDDE.H +e:\c&czero\code\CCFILE.CPP +e:\c&czero\code\CCFILE.H +e:\c&czero\code\CCINI.CPP +e:\c&czero\code\CCINI.H +e:\c&czero\code\CCPTR.CPP +e:\c&czero\code\CCPTR.H +e:\c&czero\code\CDATA.CPP +e:\c&czero\code\CDFILE.CPP +e:\c&czero\code\CDFILE.H +e:\c&czero\code\CELL.CPP +e:\c&czero\code\CELL.H +e:\c&czero\code\CHECKBOX.CPP +e:\c&czero\code\CHECKBOX.H +e:\c&czero\code\CHEKLIST.CPP +e:\c&czero\code\CHEKLIST.H +e:\c&czero\code\COLRLIST.CPP +e:\c&czero\code\COLRLIST.H +e:\c&czero\code\COMBAT.CPP +e:\c&czero\code\COMBUF.CPP +e:\c&czero\code\COMBUF.H +e:\c&czero\code\COMPAT.H +e:\c&czero\code\CONNECT.CPP +e:\c&czero\code\CONNECT.H +e:\c&czero\code\CONNMGR.H +e:\c&czero\code\CONQUER.CPP +e:\c&czero\code\CONQUER.H +e:\c&czero\code\CONST.CPP +e:\c&czero\code\CONTROL.CPP +e:\c&czero\code\CONTROL.H +e:\c&czero\code\COORD.CPP +e:\c&czero\code\CRATE.CPP +e:\c&czero\code\CRATE.H +e:\c&czero\code\CRC.CPP +e:\c&czero\code\CRC.H +e:\c&czero\code\CRCPIPE.CPP +e:\c&czero\code\CRCPIPE.H +e:\c&czero\code\CRCSTRAW.CPP +e:\c&czero\code\CRCSTRAW.H +e:\c&czero\code\CREDITS.CPP +e:\c&czero\code\CREDITS.H +e:\c&czero\code\CREW.CPP +e:\c&czero\code\CREW.H +e:\c&czero\code\DDE.CPP +e:\c&czero\code\DDE.H +e:\c&czero\code\DEBUG.CPP +e:\c&czero\code\DEBUG.H +e:\c&czero\code\DEFINES.H +e:\c&czero\code\DESCDLG.CPP +e:\c&czero\code\DESCDLG.H +e:\c&czero\code\DIAL8.CPP +e:\c&czero\code\DIAL8.H +e:\c&czero\code\DIALOG.CPP +e:\c&czero\code\DISPLAY.CPP +e:\c&czero\code\DISPLAY.H +e:\c&czero\code\DOOR.CPP +e:\c&czero\code\DOOR.H +e:\c&czero\code\DRIVE.CPP +e:\c&czero\code\DRIVE.H +e:\c&czero\code\DROP.CPP +e:\c&czero\code\DROP.H +e:\c&czero\code\EDIT.CPP +e:\c&czero\code\EDIT.H +e:\c&czero\code\EGOS.CPP +e:\c&czero\code\EGOS.H +e:\c&czero\code\ENDING.CPP +e:\c&czero\code\ENDING.H +e:\c&czero\code\EVENT.CPP +e:\c&czero\code\EVENT.H +e:\c&czero\code\EXPAND.CPP +e:\c&czero\code\EXTERNS.H +e:\c&czero\code\FACE.CPP +e:\c&czero\code\FACE.H +e:\c&czero\code\FACING.CPP +e:\c&czero\code\FACING.H +e:\c&czero\code\FACTORY.CPP +e:\c&czero\code\FACTORY.H +e:\c&czero\code\FAKESOCK.H +e:\c&czero\code\FIELD.CPP +e:\c&czero\code\FIELD.H +e:\c&czero\code\FILEPCX.H +e:\c&czero\code\FINDPATH.CPP +e:\c&czero\code\FIXED.CPP +e:\c&czero\code\FIXED.H +e:\c&czero\code\FLASHER.CPP +e:\c&czero\code\FLASHER.H +e:\c&czero\code\FLY.CPP +e:\c&czero\code\FLY.H +e:\c&czero\code\FOOT.CPP +e:\c&czero\code\FOOT.H +e:\c&czero\code\FTIMER.H +e:\c&czero\code\FUNCTION.H +e:\c&czero\code\FUSE.CPP +e:\c&czero\code\FUSE.H +e:\c&czero\code\GADGET.CPP +e:\c&czero\code\GADGET.H +e:\c&czero\code\GAMEDLG.CPP +e:\c&czero\code\GAMEDLG.H +e:\c&czero\code\GAUGE.CPP +e:\c&czero\code\GAUGE.H +e:\c&czero\code\GETCPU.CPP +e:\c&czero\code\GLOBALS.CPP +e:\c&czero\code\GOPTIONS.CPP +e:\c&czero\code\GOPTIONS.H +e:\c&czero\code\GSCREEN.CPP +e:\c&czero\code\GSCREEN.H +e:\c&czero\code\HDATA.CPP +e:\c&czero\code\HEAP.CPP +e:\c&czero\code\HEAP.H +e:\c&czero\code\HELP.CPP +e:\c&czero\code\HELP.H +e:\c&czero\code\HOUSE.CPP +e:\c&czero\code\HOUSE.H +e:\c&czero\code\HSV.CPP +e:\c&czero\code\HSV.H +e:\c&czero\code\IDATA.CPP +e:\c&czero\code\INFANTRY.CPP +e:\c&czero\code\INFANTRY.H +e:\c&czero\code\INI.CPP +e:\c&czero\code\INI.H +e:\c&czero\code\INIT.CPP +e:\c&czero\code\INLINE.H +e:\c&czero\code\INT.CPP +e:\c&czero\code\INT.H +e:\c&czero\code\INTERNET.CPP +e:\c&czero\code\INTERPAL.CPP +e:\c&czero\code\INTRO.CPP +e:\c&czero\code\INTRO.H +e:\c&czero\code\IOMAP.CPP +e:\c&czero\code\IOOBJ.CPP +e:\c&czero\code\IPX.CPP +e:\c&czero\code\IPX.H +e:\c&czero\code\IPX95.CPP +e:\c&czero\code\IPX95.H +e:\c&czero\code\IPXADDR.CPP +e:\c&czero\code\IPXADDR.H +e:\c&czero\code\IPXCONN.CPP +e:\c&czero\code\IPXCONN.H +e:\c&czero\code\IPXGCONN.CPP +e:\c&czero\code\IPXGCONN.H +e:\c&czero\code\IPXMGR.CPP +e:\c&czero\code\IPXMGR.H +e:\c&czero\code\JSHELL.CPP +e:\c&czero\code\JSHELL.H +e:\c&czero\code\KEYFRAME.CPP +e:\c&czero\code\LANGUAGE.H +e:\c&czero\code\LAYER.CPP +e:\c&czero\code\LAYER.H +e:\c&czero\code\LCW.CPP +e:\c&czero\code\LCW.H +e:\c&czero\code\LCWPIPE.CPP +e:\c&czero\code\LCWPIPE.H +e:\c&czero\code\LCWSTRAW.CPP +e:\c&czero\code\LCWSTRAW.H +e:\c&czero\code\LCWUNCMP.CPP +e:\c&czero\code\LINK.CPP +e:\c&czero\code\LINK.H +e:\c&czero\code\LINT.H +e:\c&czero\code\LIST.CPP +e:\c&czero\code\LIST.H +e:\c&czero\code\LISTNODE.H +e:\c&czero\code\LOADDLG.CPP +e:\c&czero\code\LOADDLG.H +e:\c&czero\code\LOGIC.CPP +e:\c&czero\code\LOGIC.H +e:\c&czero\code\LZO.H +e:\c&czero\code\LZO_CONF.H +e:\c&czero\code\LZO1X.H +e:\c&czero\code\LZO1X_C.CPP +e:\c&czero\code\LZO1X_D.CPP +e:\c&czero\code\LZOCONF.H +e:\c&czero\code\LZOPIPE.CPP +e:\c&czero\code\LZOPIPE.H +e:\c&czero\code\LZOSTRAW.CPP +e:\c&czero\code\LZOSTRAW.H +e:\c&czero\code\LZW.CPP +e:\c&czero\code\LZW.H +e:\c&czero\code\LZWOTRAW.CPP +e:\c&czero\code\LZWPIPE.CPP +e:\c&czero\code\LZWPIPE.H +e:\c&czero\code\LZWSTRAW.CPP +e:\c&czero\code\LZWSTRAW.H +e:\c&czero\code\MAP.CPP +e:\c&czero\code\MAP.H +e:\c&czero\code\MAPEDDLG.CPP +e:\c&czero\code\MAPEDIT.CPP +e:\c&czero\code\MAPEDIT.H +e:\c&czero\code\MAPEDPLC.CPP +e:\c&czero\code\MAPEDSEL.CPP +e:\c&czero\code\MAPEDTM.CPP +e:\c&czero\code\MAPSEL.CPP +e:\c&czero\code\MENUS.CPP +e:\c&czero\code\MESSAGE.H +e:\c&czero\code\MISSION.CPP +e:\c&czero\code\MISSION.H +e:\c&czero\code\MIXFILE.CPP +e:\c&czero\code\MIXFILE.H +e:\c&czero\code\MONOC.CPP +e:\c&czero\code\MONOC.H +e:\c&czero\code\MOUSE.CPP +e:\c&czero\code\MOUSE.H +e:\c&czero\code\MP.CPP +e:\c&czero\code\MP.H +e:\c&czero\code\MPLAYER.CPP +e:\c&czero\code\MPU.CPP +e:\c&czero\code\MPU.H +e:\c&czero\code\MSGBOX.CPP +e:\c&czero\code\MSGBOX.H +e:\c&czero\code\MSGLIST.CPP +e:\c&czero\code\MSGLIST.H +e:\c&czero\code\NETDLG.CPP +e:\c&czero\code\NULLCONN.CPP +e:\c&czero\code\NULLCONN.H +e:\c&czero\code\NULLDLG.CPP +e:\c&czero\code\NULLMGR.CPP +e:\c&czero\code\NULLMGR.H +e:\c&czero\code\OBJECT.CPP +e:\c&czero\code\OBJECT.H +e:\c&czero\code\ODATA.CPP +e:\c&czero\code\OPTIONS.CPP +e:\c&czero\code\OPTIONS.H +e:\c&czero\code\OVERLAY.CPP +e:\c&czero\code\OVERLAY.H +e:\c&czero\code\PACKET.CPP +e:\c&czero\code\PACKET.H +e:\c&czero\code\PALETTE.CPP +e:\c&czero\code\PALETTE.H +e:\c&czero\code\PCX.H +e:\c&czero\code\PHONE.H +e:\c&czero\code\PIPE.CPP +e:\c&czero\code\PIPE.H +e:\c&czero\code\PK.CPP +e:\c&czero\code\PK.H +e:\c&czero\code\PKPIPE.CPP +e:\c&czero\code\PKPIPE.H +e:\c&czero\code\PKSTRAW.CPP +e:\c&czero\code\PKSTRAW.H +e:\c&czero\code\POWER.CPP +e:\c&czero\code\POWER.H +e:\c&czero\code\PROFILE.CPP +e:\c&czero\code\QUEUE.CPP +e:\c&czero\code\QUEUE.H +e:\c&czero\code\RADAR.CPP +e:\c&czero\code\RADAR.H +e:\c&czero\code\RADIO.CPP +e:\c&czero\code\RADIO.H +e:\c&czero\code\RAMFILE.CPP +e:\c&czero\code\RAMFILE.H +e:\c&czero\code\RANDOM.CPP +e:\c&czero\code\RANDOM.H +e:\c&czero\code\RAWFILE.CPP +e:\c&czero\code\RAWFILE.H +e:\c&czero\code\READLINE.CPP +e:\c&czero\code\READLINE.H +e:\c&czero\code\RECT.CPP +e:\c&czero\code\RECT.H +e:\c&czero\code\REGION.H +e:\c&czero\code\REINF.CPP +e:\c&czero\code\RGB.CPP +e:\c&czero\code\RGB.H +e:\c&czero\code\RNDSTRAW.CPP +e:\c&czero\code\RNDSTRAW.H +e:\c&czero\code\RNG.H +e:\c&czero\code\ROTBMP.CPP +e:\c&czero\code\ROTBMP.H +e:\c&czero\code\RULES.CPP +e:\c&czero\code\RULES.H +e:\c&czero\code\SAVEDLG.H +e:\c&czero\code\SAVELOAD.CPP +e:\c&czero\code\SCENARIO.CPP +e:\c&czero\code\SCENARIO.H +e:\c&czero\code\SCORE.CPP +e:\c&czero\code\SCORE.H +e:\c&czero\code\SCREEN.H +e:\c&czero\code\SCROLL.CPP +e:\c&czero\code\SCROLL.H +e:\c&czero\code\SDATA.CPP +e:\c&czero\code\SENDFILE.CPP +e:\c&czero\code\SESSION.CPP +e:\c&czero\code\SESSION.H +e:\c&czero\code\SHA.CPP +e:\c&czero\code\SHA.H +e:\c&czero\code\SHAPEBTN.CPP +e:\c&czero\code\SHAPEBTN.H +e:\c&czero\code\SHAPIPE.CPP +e:\c&czero\code\SHAPIPE.H +e:\c&czero\code\SHASTRAW.CPP +e:\c&czero\code\SHASTRAW.H +e:\c&czero\code\SIDEBAR.CPP +e:\c&czero\code\SIDEBAR.H +e:\c&czero\code\SLIDER.CPP +e:\c&czero\code\SLIDER.H +e:\c&czero\code\SMUDGE.CPP +e:\c&czero\code\SMUDGE.H +e:\c&czero\code\SOUNDDLG.CPP +e:\c&czero\code\SOUNDDLG.H +e:\c&czero\code\SPECIAL.CPP +e:\c&czero\code\SPECIAL.H +e:\c&czero\code\SPRITE.CPP +e:\c&czero\code\STAGE.H +e:\c&czero\code\STARTUP.CPP +e:\c&czero\code\STATBTN.CPP +e:\c&czero\code\STATBTN.H +e:\c&czero\code\STATS.CPP +e:\c&czero\code\STRAW.CPP +e:\c&czero\code\STRAW.H +e:\c&czero\code\STYLE.H +e:\c&czero\code\SUPER.CPP +e:\c&czero\code\SUPER.H +e:\c&czero\code\TAB.CPP +e:\c&czero\code\TAB.H +e:\c&czero\code\TACTION.CPP +e:\c&czero\code\TACTION.H +e:\c&czero\code\TARCOM.CPP +e:\c&czero\code\TARGET.CPP +e:\c&czero\code\TARGET.H +e:\c&czero\code\TCPIP.CPP +e:\c&czero\code\TCPIP.H +e:\c&czero\code\TDATA.CPP +e:\c&czero\code\TEAM.CPP +e:\c&czero\code\TEAM.H +e:\c&czero\code\TEAMTYPE.CPP +e:\c&czero\code\TEAMTYPE.H +e:\c&czero\code\TECHNO.CPP +e:\c&czero\code\TECHNO.H +e:\c&czero\code\TEMPLATE.CPP +e:\c&czero\code\TEMPLATE.H +e:\c&czero\code\TERRAIN.CPP +e:\c&czero\code\TERRAIN.H +e:\c&czero\code\TEVENT.CPP +e:\c&czero\code\TEVENT.H +e:\c&czero\code\TEXTBTN.CPP +e:\c&czero\code\TEXTBTN.H +e:\c&czero\code\THEME.CPP +e:\c&czero\code\THEME.H +e:\c&czero\code\TOGGLE.CPP +e:\c&czero\code\TOGGLE.H +e:\c&czero\code\TRACKER.CPP +e:\c&czero\code\TRIGGER.CPP +e:\c&czero\code\TRIGGER.H +e:\c&czero\code\TRIGTYPE.CPP +e:\c&czero\code\TRIGTYPE.H +e:\c&czero\code\TXTLABEL.CPP +e:\c&czero\code\TXTLABEL.H +e:\c&czero\code\TYPE.H +e:\c&czero\code\UDATA.CPP +e:\c&czero\code\UNIT.CPP +e:\c&czero\code\UNIT.H +e:\c&czero\code\UTRACKER.CPP +e:\c&czero\code\UTRACKER.H +e:\c&czero\code\VDATA.CPP +e:\c&czero\code\VECTOR.CPP +e:\c&czero\code\VECTOR.H +e:\c&czero\code\VERSION.CPP +e:\c&czero\code\VERSION.H +e:\c&czero\code\VESSEL.CPP +e:\c&czero\code\VESSEL.H +e:\c&czero\code\VISUDLG.CPP +e:\c&czero\code\VISUDLG.H +e:\c&czero\code\VORTEX.CPP +e:\c&czero\code\VORTEX.H +e:\c&czero\code\WARHEAD.CPP +e:\c&czero\code\WARHEAD.H +e:\c&czero\code\WATCOM.H +e:\c&czero\code\WEAPON.CPP +e:\c&czero\code\WEAPON.H +e:\c&czero\code\WINSTUB.CPP +e:\c&czero\code\WRITEPCX.CPP +e:\c&czero\code\WWFILE.H +e:\c&czero\code\XPIPE.CPP +e:\c&czero\code\XPIPE.H +e:\c&czero\code\XSTRAW.CPP +e:\c&czero\code\XSTRAW.H + + +[State] +SysSetCwd='E:\C&CZERO\CODE' +SrchSetFlags=0x000320aa +FileSortMode=0x0 +StateWindowFrame=37,16,923,511,0x62c9f5fa +_StateWindow=44,44,792,452,0x00100018,'E:\C&CZERO\CODE\STATBTN.CPP',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,1,10,'',12,255,48,0,7,243,252,253,247,249,247,107,1,400,0,246,252,248,244,247,15,15,15,15,0,0 +_StateBuffer='E:\C&CZERO\CODE\STATBTN.CPP',0x0400048e,107,1,25,'4 7','',0x0,'' +_StateHistory=FILELIST,'E:\C&CZERO\CODE\STATBTN.CPP','E:\C&CZERO\CODE\MAKEFILE' +_StateHistory=EDITFILE,'E:\C&CZERO\CODE\statbtn.cpp','makefile' + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +VCSProject='' +VCSProjectPath='' +VCSProjectLocalPath='' +_RestoreSysFlags=0x62c9f5fa, 0xfffffffc + +[Compiler] +TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060 +BrowseSetFile='E:\C&CZERO\CODE\Ra-hdos.ptg' +TagSetFile='E:\C&CZERO\CODE\Ra-hdos.tag' +CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft Assembler', +CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p' +CompilerAddResponse='Borland C++', +CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p' +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'watcom\binw\wmake %r.obj',449,'wmake WIN32=1',449,'ftee joemake',209,'g.bat cheater -xqm',224,'_MSLinkErrorInfo','_MicrosoftErrorInfo','_NMakeErrorInfo','proj.err','e:\c&czero\code' +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p' +CompilerAddResponse='Default Project', +CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft C', +CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','','' +CompilerAddResponse='Script', +CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p' +CompilerAddResponse='Zortech C++', +CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','','' +CompilerAddResponse='Microsoft C(NT-i386)', +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Microsoft C(NT-i386)','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Microsoft C(NT-i386)','.hpp' diff --git a/CODE/RA-HOME.PJT b/CODE/RA-HOME.PJT new file mode 100644 index 0000000..efc6b2a --- /dev/null +++ b/CODE/RA-HOME.PJT @@ -0,0 +1,507 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[Files] +e:\c&czero\code\2KEYFRAM.CPP +e:\c&czero\code\AADATA.CPP +e:\c&czero\code\ABSTRACT.CPP +e:\c&czero\code\ABSTRACT.H +e:\c&czero\code\ADATA.CPP +e:\c&czero\code\AIRCRAFT.CPP +e:\c&czero\code\AIRCRAFT.H +e:\c&czero\code\ANIM.CPP +e:\c&czero\code\ANIM.H +e:\c&czero\code\AUDIO.CPP +e:\c&czero\code\B64PIPE.CPP +e:\c&czero\code\B64PIPE.H +e:\c&czero\code\B64STRAW.CPP +e:\c&czero\code\B64STRAW.H +e:\c&czero\code\BAR.CPP +e:\c&czero\code\BAR.H +e:\c&czero\code\BASE.CPP +e:\c&czero\code\BASE.H +e:\c&czero\code\BASE64.CPP +e:\c&czero\code\BASE64.H +e:\c&czero\code\BBDATA.CPP +e:\c&czero\code\BDATA.CPP +e:\c&czero\code\BENCH.CPP +e:\c&czero\code\BENCH.H +e:\c&czero\code\BFIOFILE.CPP +e:\c&czero\code\BFIOFILE.H +e:\c&czero\code\BLOWFISH.CPP +e:\c&czero\code\BLOWFISH.H +e:\c&czero\code\BLOWPIPE.CPP +e:\c&czero\code\BLOWPIPE.H +e:\c&czero\code\BLWSTRAW.CPP +e:\c&czero\code\BLWSTRAW.H +e:\c&czero\code\BUFF.CPP +e:\c&czero\code\BUFF.H +e:\c&czero\code\BUFFERX.H +e:\c&czero\code\BUILDING.CPP +e:\c&czero\code\BUILDING.H +e:\c&czero\code\BULLET.CPP +e:\c&czero\code\BULLET.H +e:\c&czero\code\CARGO.CPP +e:\c&czero\code\CARGO.H +e:\c&czero\code\CARRY.CPP +e:\c&czero\code\CARRY.H +e:\c&czero\code\CCDDE.CPP +e:\c&czero\code\CCDDE.H +e:\c&czero\code\CCFILE.CPP +e:\c&czero\code\CCFILE.H +e:\c&czero\code\CCINI.CPP +e:\c&czero\code\CCINI.H +e:\c&czero\code\CCPTR.CPP +e:\c&czero\code\CCPTR.H +e:\c&czero\code\CDATA.CPP +e:\c&czero\code\CDFILE.CPP +e:\c&czero\code\CDFILE.H +e:\c&czero\code\CELL.CPP +e:\c&czero\code\CELL.H +e:\c&czero\code\CHECKBOX.CPP +e:\c&czero\code\CHECKBOX.H +e:\c&czero\code\CHEKLIST.CPP +e:\c&czero\code\CHEKLIST.H +e:\c&czero\code\COLRLIST.CPP +e:\c&czero\code\COLRLIST.H +e:\c&czero\code\COMBAT.CPP +e:\c&czero\code\COMBUF.CPP +e:\c&czero\code\COMBUF.H +e:\c&czero\code\COMPAT.H +e:\c&czero\code\CONNECT.CPP +e:\c&czero\code\CONNECT.H +e:\c&czero\code\CONNMGR.H +e:\c&czero\code\CONQUER.CPP +e:\c&czero\code\CONQUER.H +e:\c&czero\code\CONST.CPP +e:\c&czero\code\CONTROL.CPP +e:\c&czero\code\CONTROL.H +e:\c&czero\code\COORD.CPP +e:\c&czero\code\CRATE.CPP +e:\c&czero\code\CRATE.H +e:\c&czero\code\CRC.CPP +e:\c&czero\code\CRC.H +e:\c&czero\code\CRCPIPE.CPP +e:\c&czero\code\CRCPIPE.H +e:\c&czero\code\CRCSTRAW.CPP +e:\c&czero\code\CRCSTRAW.H +e:\c&czero\code\CREDITS.CPP +e:\c&czero\code\CREDITS.H +e:\c&czero\code\CREW.CPP +e:\c&czero\code\CREW.H +e:\c&czero\code\DDE.CPP +e:\c&czero\code\DDE.H +e:\c&czero\code\DEBUG.CPP +e:\c&czero\code\DEBUG.H +e:\c&czero\code\DEFINES.H +e:\c&czero\code\DESCDLG.CPP +e:\c&czero\code\DESCDLG.H +e:\c&czero\code\DIAL8.CPP +e:\c&czero\code\DIAL8.H +e:\c&czero\code\DIALOG.CPP +e:\c&czero\code\DISPLAY.CPP +e:\c&czero\code\DISPLAY.H +e:\c&czero\code\DOOR.CPP +e:\c&czero\code\DOOR.H +e:\c&czero\code\DRIVE.CPP +e:\c&czero\code\DRIVE.H +e:\c&czero\code\DROP.CPP +e:\c&czero\code\DROP.H +e:\c&czero\code\EDIT.CPP +e:\c&czero\code\EDIT.H +e:\c&czero\code\EGOS.CPP +e:\c&czero\code\EGOS.H +e:\c&czero\code\ENDING.CPP +e:\c&czero\code\ENDING.H +e:\c&czero\code\EVENT.CPP +e:\c&czero\code\EVENT.H +e:\c&czero\code\EXPAND.CPP +e:\c&czero\code\EXTERNS.H +e:\c&czero\code\FACE.CPP +e:\c&czero\code\FACE.H +e:\c&czero\code\FACING.CPP +e:\c&czero\code\FACING.H +e:\c&czero\code\FACTORY.CPP +e:\c&czero\code\FACTORY.H +e:\c&czero\code\FAKESOCK.H +e:\c&czero\code\FIELD.CPP +e:\c&czero\code\FIELD.H +e:\c&czero\code\FILEPCX.H +e:\c&czero\code\FINDPATH.CPP +e:\c&czero\code\FIXED.CPP +e:\c&czero\code\FIXED.H +e:\c&czero\code\FLASHER.CPP +e:\c&czero\code\FLASHER.H +e:\c&czero\code\FLY.CPP +e:\c&czero\code\FLY.H +e:\c&czero\code\FOOT.CPP +e:\c&czero\code\FOOT.H +e:\c&czero\code\FTIMER.H +e:\c&czero\code\FUNCTION.H +e:\c&czero\code\FUSE.CPP +e:\c&czero\code\FUSE.H +e:\c&czero\code\GADGET.CPP +e:\c&czero\code\GADGET.H +e:\c&czero\code\GAMEDLG.CPP +e:\c&czero\code\GAMEDLG.H +e:\c&czero\code\GAUGE.CPP +e:\c&czero\code\GAUGE.H +e:\c&czero\code\GETCPU.CPP +e:\c&czero\code\GLOBALS.CPP +e:\c&czero\code\GOPTIONS.CPP +e:\c&czero\code\GOPTIONS.H +e:\c&czero\code\GSCREEN.CPP +e:\c&czero\code\GSCREEN.H +e:\c&czero\code\HDATA.CPP +e:\c&czero\code\HEAP.CPP +e:\c&czero\code\HEAP.H +e:\c&czero\code\HELP.CPP +e:\c&czero\code\HELP.H +e:\c&czero\code\HOUSE.CPP +e:\c&czero\code\HOUSE.H +e:\c&czero\code\HSV.CPP +e:\c&czero\code\HSV.H +e:\c&czero\code\IDATA.CPP +e:\c&czero\code\INFANTRY.CPP +e:\c&czero\code\INFANTRY.H +e:\c&czero\code\INI.CPP +e:\c&czero\code\INI.H +e:\c&czero\code\INIT.CPP +e:\c&czero\code\INLINE.H +e:\c&czero\code\INT.CPP +e:\c&czero\code\INT.H +e:\c&czero\code\INTERNET.CPP +e:\c&czero\code\INTERPAL.CPP +e:\c&czero\code\INTRO.CPP +e:\c&czero\code\INTRO.H +e:\c&czero\code\IOMAP.CPP +e:\c&czero\code\IOOBJ.CPP +e:\c&czero\code\IPX.CPP +e:\c&czero\code\IPX.H +e:\c&czero\code\IPX95.CPP +e:\c&czero\code\IPX95.H +e:\c&czero\code\IPXADDR.CPP +e:\c&czero\code\IPXADDR.H +e:\c&czero\code\IPXCONN.CPP +e:\c&czero\code\IPXCONN.H +e:\c&czero\code\IPXGCONN.CPP +e:\c&czero\code\IPXGCONN.H +e:\c&czero\code\IPXMGR.CPP +e:\c&czero\code\IPXMGR.H +e:\c&czero\code\JSHELL.CPP +e:\c&czero\code\JSHELL.H +e:\c&czero\code\KEYFRAME.CPP +e:\c&czero\code\LANGUAGE.H +e:\c&czero\code\LAYER.CPP +e:\c&czero\code\LAYER.H +e:\c&czero\code\LCW.CPP +e:\c&czero\code\LCW.H +e:\c&czero\code\LCWPIPE.CPP +e:\c&czero\code\LCWPIPE.H +e:\c&czero\code\LCWSTRAW.CPP +e:\c&czero\code\LCWSTRAW.H +e:\c&czero\code\LCWUNCMP.CPP +e:\c&czero\code\LINK.CPP +e:\c&czero\code\LINK.H +e:\c&czero\code\LINT.H +e:\c&czero\code\LIST.CPP +e:\c&czero\code\LIST.H +e:\c&czero\code\LISTNODE.H +e:\c&czero\code\LOADDLG.CPP +e:\c&czero\code\LOADDLG.H +e:\c&czero\code\LOGIC.CPP +e:\c&czero\code\LOGIC.H +e:\c&czero\code\LZO.H +e:\c&czero\code\LZO_CONF.H +e:\c&czero\code\LZO1X.H +e:\c&czero\code\LZO1X_C.CPP +e:\c&czero\code\LZO1X_D.CPP +e:\c&czero\code\LZOCONF.H +e:\c&czero\code\LZOPIPE.CPP +e:\c&czero\code\LZOPIPE.H +e:\c&czero\code\LZOSTRAW.CPP +e:\c&czero\code\LZOSTRAW.H +e:\c&czero\code\LZW.CPP +e:\c&czero\code\LZW.H +e:\c&czero\code\LZWOTRAW.CPP +e:\c&czero\code\LZWPIPE.CPP +e:\c&czero\code\LZWPIPE.H +e:\c&czero\code\LZWSTRAW.CPP +e:\c&czero\code\LZWSTRAW.H +e:\c&czero\code\MAP.CPP +e:\c&czero\code\MAP.H +e:\c&czero\code\MAPEDDLG.CPP +e:\c&czero\code\MAPEDIT.CPP +e:\c&czero\code\MAPEDIT.H +e:\c&czero\code\MAPEDPLC.CPP +e:\c&czero\code\MAPEDSEL.CPP +e:\c&czero\code\MAPEDTM.CPP +e:\c&czero\code\MAPSEL.CPP +e:\c&czero\code\MENUS.CPP +e:\c&czero\code\MESSAGE.H +e:\c&czero\code\MISSION.CPP +e:\c&czero\code\MISSION.H +e:\c&czero\code\MIXFILE.CPP +e:\c&czero\code\MIXFILE.H +e:\c&czero\code\MONOC.CPP +e:\c&czero\code\MONOC.H +e:\c&czero\code\MOUSE.CPP +e:\c&czero\code\MOUSE.H +e:\c&czero\code\MP.CPP +e:\c&czero\code\MP.H +e:\c&czero\code\MPLAYER.CPP +e:\c&czero\code\MPU.CPP +e:\c&czero\code\MPU.H +e:\c&czero\code\MSGBOX.CPP +e:\c&czero\code\MSGBOX.H +e:\c&czero\code\MSGLIST.CPP +e:\c&czero\code\MSGLIST.H +e:\c&czero\code\NETDLG.CPP +e:\c&czero\code\NULLCONN.CPP +e:\c&czero\code\NULLCONN.H +e:\c&czero\code\NULLDLG.CPP +e:\c&czero\code\NULLMGR.CPP +e:\c&czero\code\NULLMGR.H +e:\c&czero\code\OBJECT.CPP +e:\c&czero\code\OBJECT.H +e:\c&czero\code\ODATA.CPP +e:\c&czero\code\OPTIONS.CPP +e:\c&czero\code\OPTIONS.H +e:\c&czero\code\OVERLAY.CPP +e:\c&czero\code\OVERLAY.H +e:\c&czero\code\PACKET.CPP +e:\c&czero\code\PACKET.H +e:\c&czero\code\PALETTE.CPP +e:\c&czero\code\PALETTE.H +e:\c&czero\code\PCX.H +e:\c&czero\code\PHONE.H +e:\c&czero\code\PIPE.CPP +e:\c&czero\code\PIPE.H +e:\c&czero\code\PK.CPP +e:\c&czero\code\PK.H +e:\c&czero\code\PKPIPE.CPP +e:\c&czero\code\PKPIPE.H +e:\c&czero\code\PKSTRAW.CPP +e:\c&czero\code\PKSTRAW.H +e:\c&czero\code\POWER.CPP +e:\c&czero\code\POWER.H +e:\c&czero\code\PROFILE.CPP +e:\c&czero\code\QUEUE.CPP +e:\c&czero\code\QUEUE.H +e:\c&czero\code\RADAR.CPP +e:\c&czero\code\RADAR.H +e:\c&czero\code\RADIO.CPP +e:\c&czero\code\RADIO.H +e:\c&czero\code\RAMFILE.CPP +e:\c&czero\code\RAMFILE.H +e:\c&czero\code\RANDOM.CPP +e:\c&czero\code\RANDOM.H +e:\c&czero\code\RAWFILE.CPP +e:\c&czero\code\RAWFILE.H +e:\c&czero\code\READLINE.CPP +e:\c&czero\code\READLINE.H +e:\c&czero\code\RECT.CPP +e:\c&czero\code\RECT.H +e:\c&czero\code\REGION.H +e:\c&czero\code\REINF.CPP +e:\c&czero\code\RGB.CPP +e:\c&czero\code\RGB.H +e:\c&czero\code\RNDSTRAW.CPP +e:\c&czero\code\RNDSTRAW.H +e:\c&czero\code\RNG.H +e:\c&czero\code\ROTBMP.CPP +e:\c&czero\code\ROTBMP.H +e:\c&czero\code\RULES.CPP +e:\c&czero\code\RULES.H +e:\c&czero\code\SAVEDLG.H +e:\c&czero\code\SAVELOAD.CPP +e:\c&czero\code\SCENARIO.CPP +e:\c&czero\code\SCENARIO.H +e:\c&czero\code\SCORE.CPP +e:\c&czero\code\SCORE.H +e:\c&czero\code\SCREEN.H +e:\c&czero\code\SCROLL.CPP +e:\c&czero\code\SCROLL.H +e:\c&czero\code\SDATA.CPP +e:\c&czero\code\SENDFILE.CPP +e:\c&czero\code\SESSION.CPP +e:\c&czero\code\SESSION.H +e:\c&czero\code\SHA.CPP +e:\c&czero\code\SHA.H +e:\c&czero\code\SHAPEBTN.CPP +e:\c&czero\code\SHAPEBTN.H +e:\c&czero\code\SHAPIPE.CPP +e:\c&czero\code\SHAPIPE.H +e:\c&czero\code\SHASTRAW.CPP +e:\c&czero\code\SHASTRAW.H +e:\c&czero\code\SIDEBAR.CPP +e:\c&czero\code\SIDEBAR.H +e:\c&czero\code\SLIDER.CPP +e:\c&czero\code\SLIDER.H +e:\c&czero\code\SMUDGE.CPP +e:\c&czero\code\SMUDGE.H +e:\c&czero\code\SOUNDDLG.CPP +e:\c&czero\code\SOUNDDLG.H +e:\c&czero\code\SPECIAL.CPP +e:\c&czero\code\SPECIAL.H +e:\c&czero\code\SPRITE.CPP +e:\c&czero\code\STAGE.H +e:\c&czero\code\STARTUP.CPP +e:\c&czero\code\STATBTN.CPP +e:\c&czero\code\STATBTN.H +e:\c&czero\code\STATS.CPP +e:\c&czero\code\STRAW.CPP +e:\c&czero\code\STRAW.H +e:\c&czero\code\STYLE.H +e:\c&czero\code\SUPER.CPP +e:\c&czero\code\SUPER.H +e:\c&czero\code\SURFACE.CPP +e:\c&czero\code\SURFACE.H +e:\c&czero\code\TAB.CPP +e:\c&czero\code\TAB.H +e:\c&czero\code\TACTION.CPP +e:\c&czero\code\TACTION.H +e:\c&czero\code\TARCOM.CPP +e:\c&czero\code\TARGET.CPP +e:\c&czero\code\TARGET.H +e:\c&czero\code\TCPIP.CPP +e:\c&czero\code\TCPIP.H +e:\c&czero\code\TDATA.CPP +e:\c&czero\code\TEAM.CPP +e:\c&czero\code\TEAM.H +e:\c&czero\code\TEAMTYPE.CPP +e:\c&czero\code\TEAMTYPE.H +e:\c&czero\code\TECHNO.CPP +e:\c&czero\code\TECHNO.H +e:\c&czero\code\TEMPLATE.CPP +e:\c&czero\code\TEMPLATE.H +e:\c&czero\code\TERRAIN.CPP +e:\c&czero\code\TERRAIN.H +e:\c&czero\code\TEVENT.CPP +e:\c&czero\code\TEVENT.H +e:\c&czero\code\TEXTBTN.CPP +e:\c&czero\code\TEXTBTN.H +e:\c&czero\code\THEME.CPP +e:\c&czero\code\THEME.H +e:\c&czero\code\TOGGLE.CPP +e:\c&czero\code\TOGGLE.H +e:\c&czero\code\TRACKER.CPP +e:\c&czero\code\TRIGGER.CPP +e:\c&czero\code\TRIGGER.H +e:\c&czero\code\TRIGTYPE.CPP +e:\c&czero\code\TRIGTYPE.H +e:\c&czero\code\TXTLABEL.CPP +e:\c&czero\code\TXTLABEL.H +e:\c&czero\code\TYPE.H +e:\c&czero\code\UDATA.CPP +e:\c&czero\code\UNIT.CPP +e:\c&czero\code\UNIT.H +e:\c&czero\code\UTRACKER.CPP +e:\c&czero\code\UTRACKER.H +e:\c&czero\code\VDATA.CPP +e:\c&czero\code\VECTOR.CPP +e:\c&czero\code\VECTOR.H +e:\c&czero\code\VERSION.CPP +e:\c&czero\code\VERSION.H +e:\c&czero\code\VESSEL.CPP +e:\c&czero\code\VESSEL.H +e:\c&czero\code\VISUDLG.CPP +e:\c&czero\code\VISUDLG.H +e:\c&czero\code\VORTEX.CPP +e:\c&czero\code\VORTEX.H +e:\c&czero\code\WARHEAD.CPP +e:\c&czero\code\WARHEAD.H +e:\c&czero\code\WATCOM.H +e:\c&czero\code\WEAPON.CPP +e:\c&czero\code\WEAPON.H +e:\c&czero\code\WINSTUB.CPP +e:\c&czero\code\WRITEPCX.CPP +e:\c&czero\code\WWFILE.H +e:\c&czero\code\XPIPE.CPP +e:\c&czero\code\XPIPE.H +e:\c&czero\code\XSTRAW.CPP +e:\c&czero\code\XSTRAW.H + +[State] +SysSetCwd='E:\c&czero\code' +SrchSetFlags=0x000320aa +FileSortMode=0x0 +StateWindowFrame=37,16,923,511,0x62c9f4fa +_StateWindow=0,0,991,648,0x00100008,'E:\c&czero\code\TECHNO.CPP',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,1,10,'',12,255,48,0,7,243,252,253,247,249,247,1567,23,400,0,246,252,248,244,247,15,15,15,15,0,0 +_StateBuffer='E:\c&czero\code\TECHNO.CPP',0x0400048e,1567,23,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\DEFINES.H',0x0400048e,736,4,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\FOOT.H',0x0400048e,105,16,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\OBJECT.H',0x0400048e,134,12,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\TARGET.H',0x0400048e,34,13,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\AIRCRAFT.CPP',0x0400048e,2975,1,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\HOUSE.H',0x0400048e,608,16,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\HOUSE.CPP',0x0400048e,4722,7,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\TODO.TXT',0x0400048e,3,1,25,'5 9','',0x0,'' +_StateBuffer='E:\c&czero\code\UNIT.CPP',0x0c00048e,4248,19,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\DRIVE.CPP',0x0400048e,31,18,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\KEY.CPP',0x0c00048e,740,1,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\KEY.H',0x0400048e,84,7,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\DISPLAY.CPP',0x0c00048e,3579,34,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\MAP.CPP',0x0400048e,1262,76,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\INI.CPP',0x0400048e,371,1,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\INI.H',0x0400048e,121,1,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\SENDFILE.CPP',0x0400048e,1,1,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\search.h',0x0400048e,611,16,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\run\RULES.INI',0x0400048e,250,1,25,'5 9','',0x0,'' +_StateBuffer='E:\c&czero\code\RULES.CPP',0x0400048e,669,92,25,'4 7','',0x0,'' +_StateBuffer='E:\c&czero\code\RULES.H',0x0400048e,68,1,25,'4 7','',0x0,'' +_StateHistory=SEARCH,'THREAT_','_TIBERIUM','In_Which','Zone','Which_Zone','::Mission_Attack','::Good_Fire_Loca','Is_Tar','Is_','IsDr' +_StateHistory=REPLACE,'ph','tx','ty','tw','th','Structure','trycell','Is_Clear_To_Build','Is_Clear_To_Move','Archive' +_StateHistory=XMACRO,'sort' +_StateHistory=FILELIST,'E:\c&czero\code\RULES.CPP','E:\c&czero\run\RULES.INI','E:\c&czero\code\DEFINES.H','E:\c&czero\code\AIRCRAFT.CPP','E:\c&czero\code\defines.g','E:\c&czero\code\TECHNO.CPP','E:\c&czero\code\HOUSE.H','E:\c&czero\code\TARGET.H','E:\c&czero\code\OBJECT.H','E:\c&czero\code\FOOT.H' +_StateHistory=EDITFILE,'techno.cpp','house.cpp','house.h','aircraft.cpp','target.h','object.h','foot.h','todo.txt','techno.cpp','house.h' +_StateHistory=DIRECTORY,'E:\C&CZero\code' +_StateHistory=GOTOMARK,'1' +_StateHistory=GOTOLINE,'122','371','366','372','575','655','656','664','611','1567' +_StateMark=MARK_GLOBAL,1,'E:\c&czero\code\HOUSE.CPP',4732,1,0 + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +VCSProject='' +VCSProjectPath='' +VCSProjectLocalPath='' +_RestoreSysFlags=0x62c9f5fa, 0xfffffffc + +[Compiler] +TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060 +BrowseSetFile='e:\C&Czero\code\RA-Home.ptg' +TagSetFile='e:\C&Czero\code\RA-Home.tag' +CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft Assembler', +CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p' +CompilerAddResponse='Borland C++', +CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p' +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'watcom\binw\wmake WIN32=1 %r.obj',209,'wmake WIN32=1',209,'ftee joemake',209,'r.bat -hansolo cheater -xqm',224,'_MSLinkErrorInfo','_MicrosoftErrorInfo','_NMakeErrorInfo','proj.err','e:\c&czero\code' +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft C', +CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p' +CompilerAddResponse='Default Project', +CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p' +CompilerAddResponse='Zortech C++', +CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','','' +CompilerAddResponse='Script', +CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','','' +CompilerAddResponse='Microsoft C(NT-i386)', +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Microsoft C(NT-i386)','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Microsoft C(NT-i386)','.hpp' diff --git a/CODE/RA95.PJT b/CODE/RA95.PJT new file mode 100644 index 0000000..c4f0cc7 --- /dev/null +++ b/CODE/RA95.PJT @@ -0,0 +1,549 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010940 +ProjCheckPath='C:\PROJECTS\RedAlert\code\\' + +[Files] +C:\projects\redalert\code\2KEYFRAM.CPP +C:\projects\redalert\code\AADATA.CPP +C:\projects\redalert\code\ABSTRACT.CPP +C:\projects\redalert\code\ABSTRACT.H +C:\projects\redalert\code\ADATA.CPP +C:\projects\redalert\code\AIRCRAFT.CPP +C:\projects\redalert\code\AIRCRAFT.H +C:\projects\redalert\code\ANIM.CPP +C:\projects\redalert\code\ANIM.H +C:\projects\redalert\code\AUDIO.CPP +C:\projects\redalert\code\B64PIPE.CPP +C:\projects\redalert\code\B64PIPE.H +C:\projects\redalert\code\B64STRAW.CPP +C:\projects\redalert\code\B64STRAW.H +C:\projects\redalert\code\BAR.CPP +C:\projects\redalert\code\BAR.H +C:\projects\redalert\code\BASE.CPP +C:\projects\redalert\code\BASE.H +C:\projects\redalert\code\BASE64.CPP +C:\projects\redalert\code\BASE64.H +C:\projects\redalert\code\BBDATA.CPP +C:\projects\redalert\code\BDATA.CPP +C:\projects\redalert\code\BENCH.CPP +C:\projects\redalert\code\BENCH.H +C:\projects\redalert\code\BFIOFILE.CPP +C:\projects\redalert\code\BFIOFILE.H +C:\projects\redalert\code\BLOWFISH.CPP +C:\projects\redalert\code\BLOWFISH.H +C:\projects\redalert\code\BLOWPIPE.CPP +C:\projects\redalert\code\BLOWPIPE.H +C:\projects\redalert\code\BLWSTRAW.CPP +C:\projects\redalert\code\BLWSTRAW.H +C:\projects\redalert\code\BUFF.CPP +C:\projects\redalert\code\BUFF.H +C:\projects\redalert\code\BUFFERX.H +C:\projects\redalert\code\BUILDING.CPP +C:\projects\redalert\code\BUILDING.H +C:\projects\redalert\code\BULLET.CPP +C:\projects\redalert\code\BULLET.H +C:\projects\redalert\code\CARGO.CPP +C:\projects\redalert\code\CARGO.H +C:\projects\redalert\code\CARRY.CPP +C:\projects\redalert\code\CARRY.H +C:\projects\redalert\code\CCDDE.CPP +C:\projects\redalert\code\CCDDE.H +C:\projects\redalert\code\CCFILE.CPP +C:\projects\redalert\code\CCFILE.H +C:\projects\redalert\code\CCINI.CPP +C:\projects\redalert\code\CCINI.H +C:\projects\redalert\code\CCMPATH.CPP +C:\projects\redalert\code\CCPTR.CPP +C:\projects\redalert\code\CCPTR.H +C:\projects\redalert\code\CCTEN.CPP +C:\projects\redalert\code\CDATA.CPP +C:\projects\redalert\code\CDFILE.CPP +C:\projects\redalert\code\CDFILE.H +C:\projects\redalert\code\CELL.CPP +C:\projects\redalert\code\CELL.H +C:\projects\redalert\code\CHECKBOX.CPP +C:\projects\redalert\code\CHECKBOX.H +C:\projects\redalert\code\CHEKLIST.CPP +C:\projects\redalert\code\CHEKLIST.H +C:\projects\redalert\code\CLASS.CPP +C:\projects\redalert\code\COLRLIST.CPP +C:\projects\redalert\code\COLRLIST.H +C:\projects\redalert\code\COMBAT.CPP +C:\projects\redalert\code\COMBUF.CPP +C:\projects\redalert\code\COMBUF.H +C:\projects\redalert\code\COMPAT.H +C:\projects\redalert\code\CONNECT.CPP +C:\projects\redalert\code\CONNECT.H +C:\projects\redalert\code\CONNMGR.H +C:\projects\redalert\code\CONQUER.CPP +C:\projects\redalert\code\CONQUER.H +C:\projects\redalert\code\CONST.CPP +C:\projects\redalert\code\CONTROL.CPP +C:\projects\redalert\code\CONTROL.H +C:\projects\redalert\code\COORD.CPP +C:\projects\redalert\code\CRATE.CPP +C:\projects\redalert\code\CRATE.H +C:\projects\redalert\code\CRC.CPP +C:\projects\redalert\code\CRC.H +C:\projects\redalert\code\CRCPIPE.CPP +C:\projects\redalert\code\CRCPIPE.H +C:\projects\redalert\code\CRCSTRAW.CPP +C:\projects\redalert\code\CRCSTRAW.H +C:\projects\redalert\code\CREDITS.CPP +C:\projects\redalert\code\CREDITS.H +C:\projects\redalert\code\CREW.CPP +C:\projects\redalert\code\CREW.H +C:\projects\redalert\code\CSTRAW.CPP +C:\projects\redalert\code\CSTRAW.H +C:\projects\redalert\code\DDE.CPP +C:\projects\redalert\code\DDE.H +C:\projects\redalert\code\DEBUG.CPP +C:\projects\redalert\code\DEBUG.H +C:\projects\redalert\code\DEFINES.H +C:\projects\redalert\code\DESCDLG.CPP +C:\projects\redalert\code\DESCDLG.H +C:\projects\redalert\code\DIAL8.CPP +C:\projects\redalert\code\DIAL8.H +C:\projects\redalert\code\DIALOG.CPP +C:\projects\redalert\code\DISPLAY.CPP +C:\projects\redalert\code\DISPLAY.H +C:\projects\redalert\code\DOOR.CPP +C:\projects\redalert\code\DOOR.H +C:\projects\redalert\code\DRIVE.CPP +C:\projects\redalert\code\DRIVE.H +C:\projects\redalert\code\DROP.CPP +C:\projects\redalert\code\DROP.H +C:\projects\redalert\code\DYNAVEC.CPP +C:\projects\redalert\code\EDIT.CPP +C:\projects\redalert\code\EDIT.H +C:\projects\redalert\code\EGOS.CPP +C:\projects\redalert\code\EGOS.H +C:\projects\redalert\code\ENDING.CPP +C:\projects\redalert\code\ENDING.H +C:\projects\redalert\code\EVENT.CPP +C:\projects\redalert\code\EVENT.H +C:\projects\redalert\code\EXPAND.CPP +C:\projects\redalert\code\EXTERNS.H +C:\projects\redalert\code\FACE.CPP +C:\projects\redalert\code\FACE.H +C:\projects\redalert\code\FACING.CPP +C:\projects\redalert\code\FACING.H +C:\projects\redalert\code\FACTORY.CPP +C:\projects\redalert\code\FACTORY.H +C:\projects\redalert\code\FAKESOCK.H +C:\projects\redalert\code\FIELD.CPP +C:\projects\redalert\code\FIELD.H +C:\projects\redalert\code\FILEPCX.H +C:\projects\redalert\code\FINDPATH.CPP +C:\projects\redalert\code\FIXED.CPP +C:\projects\redalert\code\FIXED.H +C:\projects\redalert\code\FLASHER.CPP +C:\projects\redalert\code\FLASHER.H +C:\projects\redalert\code\FLY.CPP +C:\projects\redalert\code\FLY.H +C:\projects\redalert\code\FOOT.CPP +C:\projects\redalert\code\FOOT.H +C:\projects\redalert\code\FTIMER.H +C:\projects\redalert\code\FUNCTION.H +C:\projects\redalert\code\FUSE.CPP +C:\projects\redalert\code\FUSE.H +C:\projects\redalert\code\GADGET.CPP +C:\projects\redalert\code\GADGET.H +C:\projects\redalert\code\GAMEDLG.CPP +C:\projects\redalert\code\GAMEDLG.H +C:\projects\redalert\code\GAUGE.CPP +C:\projects\redalert\code\GAUGE.H +C:\projects\redalert\code\GETCPU.CPP +C:\projects\redalert\code\GLOBALS.CPP +C:\projects\redalert\code\GOPTIONS.CPP +C:\projects\redalert\code\GOPTIONS.H +C:\projects\redalert\code\GSCREEN.CPP +C:\projects\redalert\code\GSCREEN.H +C:\projects\redalert\code\HDATA.CPP +C:\projects\redalert\code\HEAP.CPP +C:\projects\redalert\code\HEAP.H +C:\projects\redalert\code\HELP.CPP +C:\projects\redalert\code\HELP.H +C:\projects\redalert\code\HOUSE.CPP +C:\projects\redalert\code\HOUSE.H +C:\projects\redalert\code\HSV.CPP +C:\projects\redalert\code\HSV.H +C:\projects\redalert\code\IDATA.CPP +C:\projects\redalert\code\INFANTRY.CPP +C:\projects\redalert\code\INFANTRY.H +C:\projects\redalert\code\INI.CPP +C:\projects\redalert\code\INI.H +C:\projects\redalert\code\INIT.CPP +C:\projects\redalert\code\INLINE.H +C:\projects\redalert\code\INT.CPP +C:\projects\redalert\code\INT.H +C:\projects\redalert\code\INTERNET.CPP +C:\projects\redalert\code\INTERPAL.CPP +C:\projects\redalert\code\INTRO.CPP +C:\projects\redalert\code\INTRO.H +C:\projects\redalert\code\IOMAP.CPP +C:\projects\redalert\code\IOOBJ.CPP +C:\projects\redalert\code\IPX.CPP +C:\projects\redalert\code\IPX.H +C:\projects\redalert\code\IPX95.CPP +C:\projects\redalert\code\IPX95.H +C:\projects\redalert\code\IPXADDR.CPP +C:\projects\redalert\code\IPXADDR.H +C:\projects\redalert\code\IPXCONN.CPP +C:\projects\redalert\code\IPXCONN.H +C:\projects\redalert\code\IPXGCONN.CPP +C:\projects\redalert\code\IPXGCONN.H +C:\projects\redalert\code\IPXMGR.CPP +C:\projects\redalert\code\IPXMGR.H +C:\projects\redalert\code\JSHELL.CPP +C:\projects\redalert\code\JSHELL.H +C:\projects\redalert\code\KEY.CPP +C:\projects\redalert\code\KEY.H +C:\projects\redalert\code\KEYFRAME.CPP +C:\projects\redalert\code\LANGUAGE.H +C:\projects\redalert\code\LAYER.CPP +C:\projects\redalert\code\LAYER.H +C:\projects\redalert\code\LCW.CPP +C:\projects\redalert\code\LCW.H +C:\projects\redalert\code\LCWPIPE.CPP +C:\projects\redalert\code\LCWPIPE.H +C:\projects\redalert\code\LCWSTRAW.CPP +C:\projects\redalert\code\LCWSTRAW.H +C:\projects\redalert\code\LCWUNCMP.CPP +C:\projects\redalert\code\LINK.CPP +C:\projects\redalert\code\LINK.H +C:\projects\redalert\code\LINT.H +C:\projects\redalert\code\LIST.CPP +C:\projects\redalert\code\LIST.H +C:\projects\redalert\code\LISTNODE.H +C:\projects\redalert\code\LOADDLG.CPP +C:\projects\redalert\code\LOADDLG.H +C:\projects\redalert\code\LOGIC.CPP +C:\projects\redalert\code\LOGIC.H +C:\projects\redalert\code\LZO.H +C:\projects\redalert\code\LZO_CONF.H +C:\projects\redalert\code\LZO1X.H +C:\projects\redalert\code\LZO1X_C.CPP +C:\projects\redalert\code\LZO1X_D.CPP +C:\projects\redalert\code\LZOCONF.H +C:\projects\redalert\code\LZOPIPE.CPP +C:\projects\redalert\code\LZOPIPE.H +C:\projects\redalert\code\LZOSTRAW.CPP +C:\projects\redalert\code\LZoSTRAW.H +C:\projects\redalert\code\LZW.CPP +C:\projects\redalert\code\LZW.H +C:\projects\redalert\code\LZWOTRAW.CPP +C:\projects\redalert\code\LZWPIPE.CPP +C:\projects\redalert\code\LZWPIPE.H +C:\projects\redalert\code\LZWSTRAW.CPP +C:\projects\redalert\code\LZWSTRAW.H +C:\projects\redalert\code\MAP.CPP +C:\projects\redalert\code\MAP.H +C:\projects\redalert\code\MAPEDDLG.CPP +C:\projects\redalert\code\MAPEDIT.CPP +C:\projects\redalert\code\MAPEDIT.H +C:\projects\redalert\code\MAPEDPLC.CPP +C:\projects\redalert\code\MAPEDSEL.CPP +C:\projects\redalert\code\MAPEDTM.CPP +C:\projects\redalert\code\MAPSEL.CPP +C:\projects\redalert\code\MENUS.CPP +C:\projects\redalert\code\MESSAGE.H +C:\projects\redalert\code\MISSION.CPP +C:\projects\redalert\code\MISSION.H +C:\projects\redalert\code\MIXFILE.CPP +C:\projects\redalert\code\MIXFILE.H +C:\projects\redalert\code\MONOC.CPP +C:\projects\redalert\code\MONOC.H +C:\projects\redalert\code\MOUSE.CPP +C:\projects\redalert\code\MOUSE.H +C:\projects\redalert\code\MP.CPP +C:\projects\redalert\code\MP.H +C:\projects\redalert\code\MPLAYER.CPP +C:\projects\redalert\code\MPLIB.CPP +C:\projects\redalert\code\MPLPC.CPP +C:\projects\redalert\code\MPMGRD.CPP +C:\projects\redalert\code\MPMGRD.H +C:\projects\redalert\code\MPMGRW.CPP +C:\projects\redalert\code\MPMGRW.H +C:\projects\redalert\code\MPU.CPP +C:\projects\redalert\code\MPU.H +C:\projects\redalert\code\MSGBOX.CPP +C:\projects\redalert\code\MSGBOX.H +C:\projects\redalert\code\MSGLIST.CPP +C:\projects\redalert\code\MSGLIST.H +C:\projects\redalert\code\NETDLG.CPP +C:\projects\redalert\code\NULLCONN.CPP +C:\projects\redalert\code\NULLCONN.H +C:\projects\redalert\code\NULLDLG.CPP +C:\projects\redalert\code\NULLMGR.CPP +C:\projects\redalert\code\NULLMGR.H +C:\projects\redalert\code\OBJECT.CPP +C:\projects\redalert\code\OBJECT.H +C:\projects\redalert\code\ODATA.CPP +C:\projects\redalert\code\OPTIONS.CPP +C:\projects\redalert\code\OPTIONS.H +C:\projects\redalert\code\OVERLAY.CPP +C:\projects\redalert\code\OVERLAY.H +C:\projects\redalert\code\PACKET.CPP +C:\projects\redalert\code\PACKET.H +C:\projects\redalert\code\palette.cpp +C:\projects\redalert\code\PALETTE.H +C:\projects\redalert\code\PCX.H +C:\projects\redalert\code\PHONE.H +C:\projects\redalert\code\PIPE.CPP +C:\projects\redalert\code\PIPE.H +C:\projects\redalert\code\PK.CPP +C:\projects\redalert\code\PK.H +C:\projects\redalert\code\PKPIPE.CPP +C:\projects\redalert\code\PKPIPE.H +C:\projects\redalert\code\PKSTRAW.CPP +C:\projects\redalert\code\PKSTRAW.H +C:\projects\redalert\code\POWER.CPP +C:\projects\redalert\code\POWER.H +C:\projects\redalert\code\PROFILE.CPP +C:\projects\redalert\code\QUEUE.CPP +C:\projects\redalert\code\QUEUE.H +C:\projects\redalert\code\RADAR.CPP +C:\projects\redalert\code\RADAR.H +C:\projects\redalert\code\RADIO.CPP +C:\projects\redalert\code\RADIO.H +C:\projects\redalert\code\RAMFILE.CPP +C:\projects\redalert\code\RAMFILE.H +C:\projects\redalert\code\RANDOM.CPP +C:\projects\redalert\code\RANDOM.H +C:\projects\redalert\code\RAWFILE.CPP +C:\projects\redalert\code\RAWFILE.H +C:\projects\redalert\code\READLINE.CPP +C:\projects\redalert\code\READLINE.H +C:\projects\redalert\code\RECT.CPP +C:\projects\redalert\code\RECT.H +C:\projects\redalert\code\REGION.H +C:\projects\redalert\code\REINF.CPP +C:\projects\redalert\code\RGB.CPP +C:\projects\redalert\code\RGB.H +C:\projects\redalert\code\RNDSTRAW.CPP +C:\projects\redalert\code\RNDSTRAW.H +C:\projects\redalert\code\RNG.H +C:\projects\redalert\code\ROTBMP.CPP +C:\projects\redalert\code\ROTBMP.H +C:\projects\redalert\code\RULES.CPP +C:\projects\redalert\code\RULES.H +C:\projects\redalert\code\SAVEDLG.H +C:\projects\redalert\code\SAVELOAD.CPP +C:\projects\redalert\code\SCENARIO.CPP +C:\projects\redalert\code\SCENARIO.H +C:\projects\redalert\code\SCORE.CPP +C:\projects\redalert\code\SCORE.H +C:\projects\redalert\code\SCREEN.H +C:\projects\redalert\code\SCROLL.CPP +C:\projects\redalert\code\SCROLL.H +C:\projects\redalert\code\SDATA.CPP +C:\projects\redalert\code\SEARCH.H +C:\projects\redalert\code\SENDFILE.CPP +C:\projects\redalert\code\SESSION.CPP +C:\projects\redalert\code\SESSION.H +C:\projects\redalert\code\SHA.CPP +C:\projects\redalert\code\SHA.H +C:\projects\redalert\code\SHAPEBTN.CPP +C:\projects\redalert\code\SHAPEBTN.H +C:\projects\redalert\code\SHAPIPE.CPP +C:\projects\redalert\code\SHAPIPE.H +C:\projects\redalert\code\SHASTRAW.CPP +C:\projects\redalert\code\SHASTRAW.H +C:\projects\redalert\code\SIDEBAR.CPP +C:\projects\redalert\code\SIDEBAR.H +C:\projects\redalert\code\SLIDER.CPP +C:\projects\redalert\code\SLIDER.H +C:\projects\redalert\code\SMUDGE.CPP +C:\projects\redalert\code\SMUDGE.H +C:\projects\redalert\code\SOUNDDLG.CPP +C:\projects\redalert\code\SOUNDDLG.H +C:\projects\redalert\code\SPECIAL.CPP +C:\projects\redalert\code\SPECIAL.H +C:\projects\redalert\code\SPRITE.CPP +C:\projects\redalert\code\STAGE.H +C:\projects\redalert\code\STARTUP.CPP +C:\projects\redalert\code\STATBTN.CPP +C:\projects\redalert\code\STATBTN.H +C:\projects\redalert\code\STATS.CPP +C:\projects\redalert\code\STRAW.CPP +C:\projects\redalert\code\STRAW.H +C:\projects\redalert\code\STYLE.H +C:\projects\redalert\code\SUPER.CPP +C:\projects\redalert\code\SUPER.H +C:\projects\redalert\code\SURFACE.CPP +C:\projects\redalert\code\SURFACE.H +C:\projects\redalert\code\TAB.CPP +C:\projects\redalert\code\TAB.H +C:\projects\redalert\code\TACTION.CPP +C:\projects\redalert\code\TACTION.H +C:\projects\redalert\code\TARCOM.CPP +C:\projects\redalert\code\TARGET.CPP +C:\projects\redalert\code\TARGET.H +C:\projects\redalert\code\TCPIP.CPP +C:\projects\redalert\code\TCPIP.H +C:\projects\redalert\code\TDATA.CPP +C:\projects\redalert\code\TEAM.CPP +C:\projects\redalert\code\TEAM.H +C:\projects\redalert\code\TEAMTYPE.CPP +C:\projects\redalert\code\TEAMTYPE.H +C:\projects\redalert\code\TECHNO.CPP +C:\projects\redalert\code\TECHNO.H +C:\projects\redalert\code\TEMP.CPP +C:\projects\redalert\code\TEMPLATE.CPP +C:\projects\redalert\code\TEMPLATE.H +C:\projects\redalert\code\TENMGR.CPP +C:\projects\redalert\code\TENMGR.H +C:\projects\redalert\code\TERRAIN.CPP +C:\projects\redalert\code\TERRAIN.H +C:\projects\redalert\code\TEVENT.CPP +C:\projects\redalert\code\TEVENT.H +C:\projects\redalert\code\TEXTBTN.CPP +C:\projects\redalert\code\TEXTBTN.H +C:\projects\redalert\code\THEME.CPP +C:\projects\redalert\code\THEME.H +C:\projects\redalert\code\TOGGLE.CPP +C:\projects\redalert\code\TOGGLE.H +C:\projects\redalert\code\TRACKER.CPP +C:\projects\redalert\code\TRIGGER.CPP +C:\projects\redalert\code\TRIGGER.H +C:\projects\redalert\code\TRIGTYPE.CPP +C:\projects\redalert\code\TRIGTYPE.H +C:\projects\redalert\code\TXTLABEL.CPP +C:\projects\redalert\code\TXTLABEL.H +C:\projects\redalert\code\TYPE.H +C:\projects\redalert\code\UDATA.CPP +C:\projects\redalert\code\UNIT.CPP +C:\projects\redalert\code\UNIT.H +C:\projects\redalert\code\UTRACKER.CPP +C:\projects\redalert\code\UTRACKER.H +C:\projects\redalert\code\VDATA.CPP +C:\projects\redalert\code\VECTOR.CPP +C:\projects\redalert\code\VECTOR.H +C:\projects\redalert\code\VERSION.CPP +C:\projects\redalert\code\VERSION.H +C:\projects\redalert\code\VESSEL.CPP +C:\projects\redalert\code\VESSEL.H +C:\projects\redalert\code\VISUDLG.CPP +C:\projects\redalert\code\VISUDLG.H +C:\projects\redalert\code\VORTEX.CPP +C:\projects\redalert\code\VORTEX.H +C:\projects\redalert\code\WARHEAD.CPP +C:\projects\redalert\code\WARHEAD.H +C:\projects\redalert\code\WATCOM.H +C:\projects\redalert\code\WEAPON.CPP +C:\projects\redalert\code\WEAPON.H +C:\projects\redalert\code\WINSTUB.CPP +C:\projects\redalert\code\WRITEPCX.CPP +C:\projects\redalert\code\WWFILE.H +C:\projects\redalert\code\XPIPE.CPP +C:\projects\redalert\code\XPIPE.H +C:\projects\redalert\code\XSTRAW.CPP +C:\projects\redalert\code\XSTRAW.H +C:\projects\redalert\code\2KEYFBUF.ASM +C:\projects\redalert\code\2support.asm +C:\projects\redalert\code\2TXTPRNT.ASM +C:\projects\redalert\code\coorda.asm +C:\projects\redalert\code\CPUID.ASM +C:\projects\redalert\code\ipxprot.asm +C:\projects\redalert\code\ipxreal.asm +C:\projects\redalert\code\KEYFBUFF.ASM +C:\projects\redalert\code\LCWCOMP.ASM +C:\projects\redalert\code\pagfault.asm +C:\projects\redalert\code\SUPPORT.ASM +C:\projects\redalert\code\txtprnt.asm +C:\projects\redalert\code\winasm.asm + +[State] +SysSetCwd='C:\projects\redalert\code' +SrchSetFlags=0x000320aa +FileSortMode=0x3 +StateWindowFrame=0,0,923,616,0x6181f5fa +_StateWindow=0,0,904,618,0x00000118,'C:\projects\redalert\code\bfile.mak',240,7,244,28,20,0,249,26,32,176,8,4294967295,4294967295,1,10,'',12,255,48,0,4,243,0,0,241,252,249,1731,4,400,0,246,252,245,242,240,240,240,240,240,0,0,253,224,252,1720,1,208 +_StateBuffer='C:\projects\redalert\code\bfile.mak',0x0400048e,1731,4,25,'4 7','',0x0,'',1,72 +_StateBuffer='C:\projects\redalert\code\CONQUER.CPP',0x0c00058e,113,11,25,'4 7','',0x0,'',1,72 +_StateBuffer='C:\projects\redalert\code\INIT.CPP',0x0400058e,547,1,25,'4 7','',0x0,'',1,72 +_StateBuffer='C:\projects\redalert\code\SCENARIO.CPP',0x0400058e,1027,4,25,'4 7','',0x0,'',1,72 +_StateBuffer='C:\projects\redalert\code\IDATA.CPP',0x0400058e,20,6,25,'4 7','',0x0,'',1,72 +_StateHistory=FILELIST,'C:\projects\redalert\code\CONQUER.H','C:\projects\redalert\code\DISPLAY.CPP','C:\projects\redalert\code\IDATA.CPP','C:\projects\redalert\code\VERSION.CPP','C:\projects\redalert\code\SCENARIO.CPP','C:\projects\redalert\code\STARTUP.CPP','C:\projects\redalert\code\CONQUER.CPP','C:\projects\redalert\code\INIT.CPP','C:\projects\redalert\code\bfile.mak' +_StateHistory=SEARCH,'Goodie_Check','selection','GNRL','ENEMY_S','TXT_ENEMY_SOLDIER','Force_CD_A','Read_Scenario','Start_Scenario','Force_CD','TUTORI' +_StateHistory=REPLACE,'index','coord','Ukraine','r0','obj','MAP_CELL_W','} else {',') {','PCOLOR_DIALOG_BLUE','difficulty' +_StateHistory=XMACRO,'ShowVisibles','Visibles 1','Visibles on','Visibles 0','Visibles true','Visibles','LoadLibrary westwood.dll','load westwood.dll','loadlib westwood.dll','sort' +_StateHistory=XSYMBOL,'Stop','Emergency_Exit','new','Scan','CCINIClass','Process','bool','if','Force_CD_Available','Set_Search_Drives' +_StateHistory=EDITFILE,'map.cpp','i:\projects\redalert\code\map.cpp','cell.cpp','conquer.h','idata.cpp','version.cpp','scenario.cpp','init.cpp','conquer.cpp','bfile.mak' +_StateHistory=DIRECTORY,'C:\PROJECTS\C&Czero\code','c:\projects\redalert\code' +_StateHistory=GOTOMARK,'1','3' +_StateHistory=OUTNAME,'lzwstraw.h','lzwstraw.cpp','lzwpipe.h','lzwpipe.cpp','c:\test.ini' +_StateHistory=GOTOLINE,'1365','1460','264','5724','5742','152','3820','1904','169','2633' +_StateHistory=DIFFFILE,'C:\projects\redalert\code\y\SCENARIO.CPP','C:\projects\redalert\code\y\TECHNO.CPP','C:\projects\redalert\code\v\language.h','C:\projects\redalert\code\LANGUAGE.H','C:\projects\redalert\code\v\techno.cpp','C:\projects\redalert\code\TECHNO.CPP','C:\projects\redalert\code\CRATE.CPP','I:\PROJECTS\RedAlert\code\Crate.cpp','C:\projects\redalert\code\CELL.CPP','i:\projects\redalert\code\CELL.CPP' +_StateMark=MARK_GLOBAL,1,'C:\projects\redalert\code\INIT.CPP',547,1,0,'',0 + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +VCSProject='' +VCSProjectPath='' +VCSProjectLocalPath='' +_RestoreSysFlags=0x62c9f5fa, 0xfffffffc + +[Compiler] +TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060 +BrowseSetFile='C:\PROJECTS\redalert\code\ra95.ptg' +TagSetFile='C:\PROJECTS\redalert\code\ra95.tag' +CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft Assembler', +CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p' +CompilerAddResponse='Borland C++', +CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p' +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'ftee wmake WIN32=1 %b.obj',497,'ftee wmake',497,'ftee joemake',209,'r.bat cheater -xm',224,'_NMakeErrorInfo','_MicrosoftErrorInfo','_ErrorInfoParser Microsoft','proj.err','%x' +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft C', +CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p' +CompilerAddResponse='Default Project', +CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p' +CompilerAddResponse='Zortech C++', +CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','','' +CompilerAddResponse='Script', +CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','','' +CompilerAddResponse='Microsoft C(NT-i386)', +CompilerNewExt=.htm +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerNewExt=.pas +CompilerAssign='Microsoft C','.h' +CompilerNewExt=.ini +CompilerNewExt=.html +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Microsoft C(NT-i386)','.cxx' +CompilerNewExt=.txt +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Microsoft C(NT-i386)','.hpp' + +[Project] +ConfigSetLinkDBFilename='D:\CW32\cwright.ldb' +ConfigSetSymbolDBFilename='D:\CW32\cwright.sbl' +ConfigSetMarkDBFilename='D:\CW32\cwright.mrk' +EditSetPath='' + +[VersionControl:PVCS] +VCSSetCmd=VCS_CHECKIN_ID,'put -n -T@%Q -M@%Q %b%e',0x8000040 +VCSSetCmd=VCS_CHECKINLOCK_ID,'put -n -T@%Q -M@%Q -l %b%e',0x8000040 +VCSSetCmd=VCS_CHECKOUT_ID,'i:\vss\ss.exe Get %b%e',0x8000040 +VCSSetCmd=VCS_CHECKOUTLOCK_ID,'i:\vss\ss.exe Checkout %b%e',0x8000040 +VCSSetCmd=VCS_CHECKLOCK_ID,'vcs -l %b%e',0x8000040 +VCSSetCmd=VCS_CHECKUNLOCK_ID,'vcs -u %b%e',0x8000040 +VCSSetCmd=VCS_CHECKLABEL_ID,'vcs -v${VCSLABEL} %b%e',0x8000040 +VCSSetCmd=VCS_CHECKLOG_ID,'vlog %b%e',0x8000040 +VCSSetCmd=VCS_CHECKPROP_ID,'vlog -b %b%e',0x8000040 +VCSSetCmd=VCS_CHECKDIFF_ID,'vdiff -r %b%e',0x8000040 +VCSSetCmd=VCS_CHECKMENU_ID,'pvcswinu',0x8000000 diff --git a/CODE/RADAR.CPP b/CODE/RADAR.CPP new file mode 100644 index 0000000..833ca5e --- /dev/null +++ b/CODE/RADAR.CPP @@ -0,0 +1,2607 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RADAR.CPP 3 3/12/97 2:35p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RADAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Multi_Color -- Get the multi color offset number * + * RadarClass::AI -- Processes radar input (non-tactical). * + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * RadarClass::Click_In_Radar -- Check to see if a click is in radar map * + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * RadarClass::Draw_Names -- draws players' names on the radar map * + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * RadarClass::Init_Clear -- Sets the radar map to a known state * + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * RadarClass::Radar_Activate -- Controls radar activation. * + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell.* + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to* + * RadarClass::Zoom_Mode(void) -- Handles toggling zoom on the map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +//void const * RadarClass::CoverShape; +RadarClass::RTacticalClass RadarClass::RadarButton; + +void const * RadarClass::RadarAnim = NULL; +void const * RadarClass::RadarPulse = NULL; +void const * RadarClass::RadarFrame = NULL; + +static bool FullRedraw = false; + +static GraphicBufferClass _IconStage(3,3); +static GraphicBufferClass _TileStage(24,24); + + +/*********************************************************************************************** + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * * + * This default constructor merely sets the radar specific values to default settings. The * + * radar must be deliberately activated in order for it to be displayed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/16/1994 JLB : Created. * + *=============================================================================================*/ +RadarClass::RadarClass(void) : + IsToRedraw(false), + RadarCursorRedraw(false), + IsPulseActive(false), + RadarPulseFrame(0), + DoesRadarExist(false), + IsRadarActive(false), + IsRadarActivating(false), + IsRadarDeactivating(false), + IsRadarJammed(false), + SpecialRadarFrame(0), + RadarAnimFrame(0), + RadarX(0), + RadarY(0), + RadarCellWidth(0), + RadarCellHeight(0), + RadarCell(0), + BaseX(0), + BaseY(0), + RadarWidth(0), + RadarHeight(0), + IsZoomed(true), + ZoomFactor(0), + IsPlayerNames(false), + IsHouseSpy(false), + SpyingOn(HOUSE_SPAIN), + PixelPtr(0) +{ +} + + +/*********************************************************************************************** + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * * + * This routine handles any one time processing required in order for the radar map to * + * function. This actually only requires an allocation of the radar staging buffer. This * + * buffer is needed for those cases where the radar area of the page is being destroyed * + * and it needs to be destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine only ONCE. * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::One_Time(void) +{ + RadWidth = 80 * RESFACTOR; + RadHeight = 70 * RESFACTOR; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = 7 * RESFACTOR; + RadPWidth = 64 * RESFACTOR; + RadPHeight = 64 * RESFACTOR; + #ifdef WIN32 + RadOffX = 6; + RadOffY = 7; + RadIWidth = 128+18;//************ + RadIHeight = 128+2;//************ + #else + RadOffX = 4; + RadOffY = 1; + RadIWidth = 72; + RadIHeight = 69; + #endif + + DisplayClass::One_Time(); +#ifdef OBSOLETE + RadarButton.X = RadX+RadOffX; + RadarButton.Y = RadY+RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +#else + RadarButton.X = RadX; + RadarButton.Y = RadY; + RadarButton.Width = RadWidth; + RadarButton.Height = RadHeight; +#endif +} + + +/*********************************************************************************************** + * RadarClass::Init_Clear -- Sets the radar map to a known state. * + * * + * This routine is used to initialize the radar map at the start of the scenario. It * + * sets the radar map position and starts it in the disabled state. * + * * + * INPUT: theater -- The theater that the scenario is starting (unused by this routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Init_Clear(void) +{ + DisplayClass::Init_Clear(); + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; + + /* + ** If we have a valid map lets make sure that we set it correctly + */ + if (MapCellWidth || MapCellHeight) { +#ifdef WIN32 + IsZoomed = false; +#else + IsZoomed = true; +#endif + Zoom_Mode(Coord_Cell(Map.TacticalCoord)); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Activate -- Controls radar activation. * + * * + * Use this routine to turn the radar map on or off. * + * * + * INPUT: control -- What to do with the radar map: * + * 0 = Turn radar off. * + * 1 = Turn radar on. * + * 2 = Remove Radar Gadgets * + * 3 = Add Radar Gadgets * + * 4 = Remove radar. * + * -1= Toggle radar on or off. * + * * + * OUTPUT: bool; Was the radar map already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Radar_Activate(int control) +{ + bool old = IsRadarActive; + + switch (control) { + + /* + ** Toggle the state of the radar map on or off. + */ + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; + } + } + } else { + Radar_Activate(2); + } + return(old); + + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } else { + RadarAnimFrame = 0; + } + } + } + } else { + Radar_Activate(3); + } + return(old); + + case 2: + if (Session.Type==GAME_NORMAL) { + SidebarClass::Zoom.Disable(); + } else { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 3: + if (Session.Type == GAME_NORMAL && Is_Zoomable()) { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; + + default: + break; + } + + if (IsRadarActive != old) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + FullRedraw = IsRadarActive; + return(old); +} + + +/*********************************************************************************************** + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * * + * This is used to display the radar map that appears in the lower * + * right corner. The main changes to this map are the vehicles and * + * structure pixels. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1991 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Draw_It(bool forced) +{ + DisplayClass::Draw_It(forced); + + static char * _hiresradarnames[]={ + "natoradr.shp", //HOUSE_SPAIN, + "natoradr.shp", //HOUSE_GREECE, + "ussrradr.shp", //HOUSE_USSR, + "natoradr.shp", //HOUSE_ENGLAND, + "ussrradr.shp", //HOUSE_UKRAINE, + "natoradr.shp", //HOUSE_GERMANY, + "natoradr.shp", //HOUSE_FRANCE, + "natoradr.shp", //HOUSE_TURKEY, + "natoradr.shp", //HOUSE_GOOD + "ussrradr.shp", //HOUSE_BAD + }; + static char * _frames[]={ + "nradrfrm.shp", //HOUSE_SPAIN, + "nradrfrm.shp", //HOUSE_GREECE, + "uradrfrm.shp", //HOUSE_USSR, + "nradrfrm.shp", //HOUSE_ENGLAND, + "uradrfrm.shp", //HOUSE_UKRAINE, + "nradrfrm.shp", //HOUSE_GERMANY, + "nradrfrm.shp", //HOUSE_FRANCE, + "nradrfrm.shp", //HOUSE_TURKEY, + "nradrfrm.shp", //HOUSE_GOOD + "uradrfrm.shp", //HOUSE_BAD + }; + + int radarforced = 0; + + /* + ** Don't perform any rendering if none is requested. + */ + if (!forced && !IsToRedraw && !FullRedraw) return; + + BStart(BENCH_RADAR); + + static HousesType _house = HOUSE_NONE; + + if (PlayerPtr->ActLike != _house) { + char name[_MAX_FNAME + _MAX_EXT]; + +// strcpy(name, "NATORADR.SHP" ); +// if (Session.Type == GAME_NORMAL) { + strcpy(name, _hiresradarnames[PlayerPtr->ActLike]); +// } + #ifndef NDEBUG + RawFileClass file(name); + if (file.Is_Available()) { + RadarAnim = Load_Alloc_Data(file); + } else { + RadarAnim = MFCD::Retrieve(name); + } + strcpy(name, "PULSE.SHP"); + RawFileClass file2(name); + if (file2.Is_Available()) { + RadarPulse = Load_Alloc_Data(file2); + } else { + RadarPulse = MFCD::Retrieve(name); + } + strcpy(name, _frames[PlayerPtr->ActLike]); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarFrame = Load_Alloc_Data(file3); + } else { + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + } + #else + RadarAnim = MFCD::Retrieve(name); + strcpy(name, "PULSE.SHP"); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarPulse = Load_Alloc_Data(file3); + } else { + RadarPulse = MFCD::Retrieve(name); + } + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + #endif + _house = PlayerPtr->ActLike; + } + + /* + ** If in player name mode, just draw player names + */ + if (IsPlayerNames) { + Draw_Names(); + IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + /* + ** If in spy-on-radar facility mode, draw the appropriate info. + */ + if (IsHouseSpy) { + IsToRedraw = false; + if (Draw_House_Info()) { + BEnd(BENCH_RADAR); + return; + } + } + + if (IsRadarActivating || IsRadarDeactivating || IsRadarJammed) { + Radar_Anim(); + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + if (Map.IsSidebarActive) { + if (IsRadarActive) { + + /* + ** If only a few of the radar pixels need to be redrawn, then find and redraw + ** only these. + */ + if (!forced && IsToRedraw && !FullRedraw && !IsPulseActive) { + IsToRedraw = false; + + if (PixelPtr) { + + /* + ** Render all pixels in the "to redraw" stack. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < PixelPtr; index++) { + CELL cell = PixelStack[index]; + if (Cell_On_Radar(cell)) { + (*this)[cell].IsPlot = false; + Plot_Radar_Pixel(cell); + RadarCursorRedraw |= (*this)[cell].IsRadarCursor; + } + } + LogicPage->Unlock(); + } + + /* + ** Refill the stack if there is pending pixels yet to be plotted. + ** This should only process in sections for speed reasons + */ + if (PixelPtr == PIXELSTACK) { + PixelPtr = 0; + + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = XY_Cell(MapCellX + x, MapCellY + y); + if (Cell_On_Radar(cell)) { + + if ((*this)[cell].IsPlot) { + PixelStack[PixelPtr++] = cell; + IsToRedraw = true; + if (PixelPtr == PIXELSTACK) break; + } + } + } + if (PixelPtr == PIXELSTACK) break; + } + } else { + PixelPtr = 0; + } + } + + Radar_Cursor(RadarCursorRedraw); + + } else { + +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); +#endif + + CC_Draw_Shape(RadarFrame, 1, RadX, RadY+(1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + if (BaseX || BaseY) { + + if (!IsZoomed && BaseX && BaseY && RadarWidth< (RadIWidth-1) && RadarHeight < (RadIHeight-1)) { +#ifdef WIN32 + LogicPage->Draw_Rect(RadX + RadOffX + BaseX -1, + RadY + RadOffY + BaseY -1, + RadX + RadOffX + BaseX + RadarWidth, +// RadX + RadOffX + BaseX + RadarWidth +1, + RadY + RadOffY + BaseY + RadarHeight, +// RadY + RadOffY + BaseY + RadarHeight +1, + WHITE); +#endif + } + } else { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + } + + /* + ** Draw the entire radar map. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (In_Radar(index) && Cell_On_Radar(index)) { + Plot_Radar_Pixel(index); + } + } + if (IsPulseActive) { + CC_Draw_Shape(RadarPulse, RadarPulseFrame++, RadX + RadOffX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + } + LogicPage->Unlock(); + } + + Radar_Cursor(true); + FullRedraw = false; + IsToRedraw = false; + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + if (oldpage == &SeenBuff) { + Hide_Mouse(); + LogicPage->Blit(SeenBuff, RadX, RadY, RadX, RadY, RadWidth, RadHeight); + Show_Mouse(); + } + + Set_Logic_Page(oldpage); + } + + } else { + + /* + ** If the radar is not active, then only draw the cover plate if forced to do so. + */ + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + CC_Draw_Shape(RadarAnim, val, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + FullRedraw = false; + IsToRedraw = false; + + /* + ** Display the country name on the cover plate when in multi play only. + */ + if (Session.Type != GAME_NORMAL) { + Fancy_Text_Print(Text_String(HouseTypeClass::As_Reference(PlayerPtr->ActLike).Full_Name()), RadX+RadWidth/2, RadY+RadHeight-10*RESFACTOR, &ColorRemaps[PlayerPtr->RemapColor], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + } + + } + BEnd(BENCH_RADAR); +} + + +/*************************************************************************** + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) +{ + TerrainClass * list[4] = {0,0,0,0}; + int listidx = 0; + int lp,lp2; + + + ObjectClass * obj = Map[cell].Cell_Occupier(); + + /* + ** If the cell is occupied by a terrain type, add it to the sortable + ** list. + */ + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + + /* + ** Now loop through all the occupiers and add them to the list if they + ** are terrain type. + */ + for (lp = 0; lp < ARRAY_SIZE(Map[cell].Overlapper); lp ++) { + obj = Map[cell].Overlapper[lp]; + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + } + + /* + ** If there are no entries in our list then just get out. + */ + if (!listidx) return; + + /* + ** If there is terrain in this cell then draw a dark pixel to + ** represent it. + */ + if (size == 1) { + LogicPage->Put_Pixel(x, y, 21); +// LogicPage->Put_Pixel(x, y, 60); + return; + } + + /* + ** Sort the list by its sort Y value so that we can render in the proper + ** order. + */ + for (lp = 0; lp < listidx - 1; lp ++) { + for (lp2 = lp + 1; lp2 < listidx; lp2++) { + if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { + TerrainClass * terrain = list[lp]; + list[lp] = list[lp2]; + list[lp2] = terrain; + } + } + } + + /* + ** loop through the list and take care of rendering the correct icon. + */ + for (lp = 0; lp < listidx; lp ++) { + unsigned char * icon = list[lp]->Radar_Icon(cell); + if (!icon) continue; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char *)&FadingBrighten[0]); +#else + for (int lpy = 0; lpy < 3; lpy++) { + for (int lpx = 0; lpx < 3; lpx++) { + if (*icon) { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + icon++; + } + } +#endif //WIN32 + } +} + + +/*********************************************************************************************** + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * * + * This routine will display an object imagery at the location specified according to the * + * condition of the specified cell. * + * * + * INPUT: cell -- The cell to use as reference when drawing the radar pixel. * + * * + * x,y -- The pixel coordinate to render the radar "pixel" at. * + * * + * size -- The size of the "pixel". When zoomed in, this value will be "3". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) +{ + ObjectClass * obj; + + obj = (ObjectClass *)Map[cell].Cell_Occupier(); + while (obj) { + if (obj->Is_Techno() && ((TechnoClass *)obj)->Is_Visible_On_Radar()) { + int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].Bar; +// int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].BrightColor; + int xoff; + int yoff; + int subsize = max(1, size/3); + + switch (obj->What_Am_I()) { + case RTTI_INFANTRY: + xoff = (Coord_XLepton(obj->Coord) / (CELL_LEPTON_W/(size+1))) - subsize/2; + xoff = max(xoff, 0); + xoff = min(xoff, size-subsize); + yoff = (Coord_YLepton(obj->Coord) / (CELL_LEPTON_H/(size+1))) - subsize/2; + yoff = max(yoff, 0); + yoff = min(yoff, size-subsize); + + /* + ** Draw the infantryman's pixel. If he's a spy, draw in my house color + */ + if (*(InfantryClass *)obj == INFANTRY_SPY) { + color = ColorRemaps[PlayerPtr->RemapColor].Bar; +// color = ColorRemaps[PlayerPtr->RemapColor].BrightColor; + } + LogicPage->Fill_Rect(x+xoff, y+yoff, x+xoff+(subsize-1), y+yoff+(subsize-1), color); + break; + + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + LogicPage->Fill_Rect(x, y, x+size-1, y+size-1, color); + break; + + default: + break; + } + } + obj = obj->Next; + } + +} + + +/*************************************************************************** + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) +{ + //int lpx,lpy; + + OverlayType overlay = (*this)[cell].Overlay; + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const * otype = &OverlayTypeClass::As_Reference(overlay); + + if (otype->IsRadarVisible) { + unsigned char * icon = otype->Radar_Icon((*this)[cell].OverlayData); + if (!icon) return; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x, y, DKGREY); + +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingShade[0]); + } else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingYellow[0]); + } +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); +// } else { +// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + } + +#else + for (int lpy = 0; lpy < size; lpy++) { + for (int lpx = 0; lpx < size; lpx++) { + if (size == 1) icon+=4; + if (*icon) { + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x + lpx, y + lpy, DKGREY); +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingShade[*icon]); + } else { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingYellow[*icon]); + } +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingGreen[*icon]); +// } else { +// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + } + if (size == 1) { + icon+=5; + } else { + icon++; + } + icon++; + } + } +#endif //WIN32 + } + } +} + + +/*************************************************************************** + * RadarClass::Zoom_Mode -- Handles toggling zoom on the map * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/29/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Zoom_Mode(CELL cell) +{ + int map_c_width; + int map_c_height; + + /* + ** Set all of the initial zoom mode variables to the correct + ** setting. + */ +#ifdef WIN32 + if (Is_Zoomable()) { + IsZoomed = !IsZoomed; + } else { + IsZoomed = true; + } +#else + IsZoomed = false; +#endif + BaseX = 0; + BaseY = 0; + + /* + ** Figure out exactly what size we need to zoom the map to. + */ + if (!IsZoomed) { + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = max(min(xfactor, yfactor) , 1); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } else { + ZoomFactor = 3; +// ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; + } + + /* + ** Make sure we do not show more cells than are on the map. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + map_c_width = min(map_c_width, RadIWidth); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, RadIHeight); + map_c_height = min(map_c_height, MapCellHeight); +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif + + /* + ** Find the amount of remainder because this will let us calculate + ** how to center the thing. + */ + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); + + /* + ** Finally mark the map so it shows just as much as it is supposed + ** to. + */ + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIHeight - rem_y; + + /* + ** Set the radar position to the current cell. + */ + Set_Radar_Position(cell); + + /* + ** When zoom mode changes then we need to redraw the radar + ** area. + */ + IsToRedraw = true; + + /* + ** Notify the map that we need to redraw a portion + */ + Flag_To_Redraw(false); + + /* + ** Since we have made a vast change we must redraw everything + */ + FullRedraw = true; +} + + +/*********************************************************************************************** + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * * + * This will check to see if the zoomed mode of the map would be just the same size as * + * the non-zoomed mode. If this is true, then zooming would have no effect, so return * + * false indicating that zooming is not allowed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is zooming allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Zoomable(void) const +{ + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + int factor = max(min(xfactor, yfactor) , 1); + if (factor == 3) { + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * * + * This will update the radar map with a pixel. It is used to display * + * vehicle positions on the radar map. * + * * + * INPUT: unit -- Pointer to unit to render at the given position. If * + * NULL is passed in, then the underlying terrain is * + * displayed instead. * + * * + * pos -- Position on the map to update. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine does NOT hide the mouse. It is up to you to * + * do so. * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 06/21/1991 JLB : Large blips for units & buildings. * + * 02/14/1994 JLB : Revamped. * + * 04/17/1995 PWG : Created. * + * 04/18/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Plot_Radar_Pixel(CELL cell) +{ + if (cell == -1) cell = 1; + + int x,y; // Coordinate of cell location. + + /* + ** Perform any clipping on the cell coordinate. + */ + if (!IsRadarActive || (unsigned)cell > MAP_CELL_TOTAL) { + return; + } + + if (!In_Radar(cell) || !Cell_On_Radar(cell)) { + return; + } + + /* + ** If we are zoomed in then calculate the pixel based off of the portion + ** of the map the radar is viewing. + */ + x = Cell_X(cell) - RadarX; + y = Cell_Y(cell) - RadarY; + if ((unsigned)x >= RadarCellWidth || (unsigned)y >= RadarCellHeight) { + return; + } + + bool usjamming = false; + if (LogicPage->Lock()) { + CellClass * cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); + + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color=TBLACK; // Color of the pixel to plot. + int housebit = (1 << PlayerPtr->Class->House); + int celljammed = (*this)[cell].Jammed; + int jammed = celljammed & (0xFFFF - housebit); + if (!jammed && ((*this)[cell].IsMapped || Debug_Unshroud)) { +// if (!jammed && ((*this)[cell].IsVisible || Debug_Unshroud)) { + color = cellptr->Cell_Color(true); + if ( (celljammed & housebit) && (color == TBLACK) ) { + color = BLACK;//FadingWayDark[color]; + usjamming = true; + } + } else { + color = BLACK; + } + + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ + if (color == TBLACK) { + if (ZoomFactor > 1) { + void const * ptr = NULL; + int icon; + + /* + ** Fetch the template pointer and template icon number for the + ** specified cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != 255) { + ptr = TemplateTypeClass::As_Reference(cellptr->TType).Get_Image_Data(); + icon = cellptr->TIcon; + } + + /* + ** If the template pointer is still NULL, then this means either a clear + ** template or an illegal one. Setup for a clear template. + */ + if (ptr == NULL) { + ptr = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Get_Image_Data(); + icon = cellptr->Clear_Icon(); + } + + IconsetClass const * iconset = (IconsetClass const *)ptr; + unsigned char const * icondata = iconset->Icon_Data(); + + + /* + ** Convert the logical icon number into the actual icon number. + */ + icon &= 0x00FF; + icon = *(iconset->Map_Data() + icon); + + unsigned char * data = (unsigned char *)icondata + icon*(24*24); + Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + _TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); + } else { +// LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, cellptr->Cell_Color(false)); +/*BG*/ LogicPage->Put_Pixel(x, y, cellptr->Cell_Color(false)); + } + } else { + LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, color); +///*BG*/ LogicPage->Put_Pixel(x, y, color); + } + if (color != BLACK) { + Render_Overlay(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); + Render_Infantry(cell, x, y, ZoomFactor); + } else { + if(usjamming) { + Render_Infantry(cell, x, y, ZoomFactor); + } + } + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * * + * This routine is used to inform the system that a pixel needs to be * + * rerendered on the radar map. The pixel(s) will be rendered the * + * next time the map is refreshed. * + * * + * INPUT: cell -- The map cell to be rerendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1992 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Radar_Pixel(CELL cell) +{ + if (IsRadarActive && Map.IsSidebarActive && Cell_On_Radar(cell)) { + IsToRedraw = true; + (*this)[cell].IsPlot = true; + if (PixelPtr < PIXELSTACK) { + PixelStack[PixelPtr++] = cell; + } + } +} + + +/*********************************************************************************************** + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * * + * This routine will examine the X and Y coordinate and convert them into the X and Y * + * cell coordinate value that corresponds to the location. * + * * + * INPUT: x,y -- The X and Y mouse coordinate already normalized to the radar upper left * + * corner. * + * * + * OUTPUT: Returns with success rating in addition, the X and Y values will now hold the * + * cell coordinates of the cell the pixel offsets indicated. * + * Result 1 = click was in radar region * + * Result 0 = click was outside radar region completely * + * Result-1 = click in radar area but not on clickable region of radar. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1995 PWG : Created. * + * 07/16/1995 JLB : Recognizes when sidebar is closed now. * + *=============================================================================================*/ +int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) const +{ + int x = ptr_x; + int y = ptr_y; + + /* + ** If radar is not active the click could have been on a radar point + */ + if (!IsRadarActive || !Map.IsSidebarActive) return(0); + + x -= (RadX + RadOffX); + y -= (RadY + RadOffY); + if ((unsigned)x < RadIWidth && (unsigned)y < RadIHeight) { + x -= BaseX; + y -= BaseY; + + if ((unsigned)x < RadarWidth + (ZoomFactor-1) && (unsigned)y < RadarHeight + (ZoomFactor-1)) { +// if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + x = RadarX + (x / ZoomFactor); + y = RadarY + (y / ZoomFactor); + if (change) { + ptr_x = x; + ptr_y = y; + } + return(1); + } + return(-1); + } + return(0); +} + + +/*********************************************************************************************** + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * * + * This routine will examine the pixel coordinate provided and determine what cell it * + * represents. If the radar map is not active or the coordinates are not positioned over * + * the radar map, then it will fall into the base class corresponding routine. * + * * + * INPUT: x,y -- The pixel coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that the coordinate is over or -1 if not over any * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Click_Cell_Calc(int x, int y) const +{ + int result = Click_In_Radar(x, y, true); + switch (result) { + case 1: + return(XY_Cell(x, y)); + + case -1: + return(-1); + + default: + break; + } + return(DisplayClass::Click_Cell_Calc(x, y)); +} + + +/*********************************************************************************************** + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * * + * This routine will update the radar map if a cell becomes mapped. * + * * + * INPUT: cell -- The cell that is being mapped. * + * * + * house -- The house that is doing the mapping. * + * * + * OUTPUT: bool; Was the cell mapped (for the first time) by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Map_Cell(CELL cell, HouseClass * house) +{ + if (DisplayClass::Map_Cell(cell, house)) { + Radar_Pixel(cell); + return(true); + } + return(false); +} + + + +void RadarClass::Cursor_Cell(CELL cell, int value) +{ + /* + ** If this cell is not on the radar don't bother doing anything. + */ + if (Cell_On_Radar(cell)) { + + int temp = (*this)[cell].IsRadarCursor; + + if (temp != value) { + + /* + ** Record the new state of this cell. + */ + (*this)[cell].IsRadarCursor = value; + + /* + ** If we are erasing then erase the cell. + */ + if (value == FALSE) { + Plot_Radar_Pixel(cell); + } + } + } +} + + +void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen) +{ + int x, y; + /* + ** First step is to convert pixel coordinates back to a CellX and CellY. + */ + x1 = RadarX + (x1 / ZoomFactor); + y1 = RadarY + (y1 / ZoomFactor); + x2 = RadarX + (x2 / ZoomFactor); + y2 = RadarY + (y2 / ZoomFactor); + + /* + ** Now we need to convert the Pixel length to a cell length. + */ + barlen = (barlen / ZoomFactor) + 1; + + /* + ** Now lets loop through and mark the map with the proper value. + */ + for (int lp = 0; lp <= barlen; lp++) { + /* + ** Do Horizontal action to upper and lower left corners. + */ + x = x1 + lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Horizontal Action to upper and lower right corners + */ + x = x2 - lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Vertical Action to left and right upper corners + */ + y = y1 + lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + + /* + ** Do Vertical action to left and right lower corners. + */ + y = y2 - lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + } +} + + + +/*********************************************************************************************** + * RadarClass::Cell_XY_To_Radar_Pixel-- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) +{ + x = (cellx - RadarX) * ZoomFactor; + y = (celly - RadarY) * ZoomFactor; +} + + +/*********************************************************************************************** + * RadarClass::Jam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Jam_Cell(CELL cell, HouseClass * house/*KO, bool shadeit*/) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Jammed |= jam; + if (house != PlayerPtr) Shroud_Cell(cell/*KO, shadeit*/); + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::UnJam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::UnJam_Cell(CELL cell, HouseClass * house) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Redraw_Objects(); + (*this)[cell].Jammed &= (0xFFFF - jam); + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +#pragma argsused +void RadarClass::Radar_Cursor(int forced) +{ + static _last_pos = -1; + static _last_frame = -1; +#ifdef WIN32 + GraphicViewPortClass * oldpage; +#else + GraphicBufferClass * oldpage; +#endif + int x1, y1, x2, y2; + + /* + ** figure out these function calls as we will need to call them multiple times. + */ + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; + + /* + ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then + ** just skip the redraw process. + */ + if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; + + if ( _last_pos != -1 ) { + /* + ** The first thing we need to do is take care of erasing the last radar cell position. We do this + ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and + ** finally taking care of calling the erase procedure which will convert the pixel coordinates back + ** to the cells that need to be redraw. + **/ + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); + + Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the current coordinates based on the last animation frame. + */ + x1-= _last_frame; + y1-= _last_frame; + x2+= _last_frame; + y2+= _last_frame; + + /* + ** Finally mark the map (actually remove the marks that indicate the radar cursor was there + */ + Mark_Radar(x1, y1, x2, y2, FALSE, barlen); + } + + + /* + ** Find the upper left and lower right corners of the radar cursor. + ** Remember to adjust x2 and y2 back by one pixel as they will not be + ** pointing to the right value otherwise. They point one cell ahead + ** of where they should. + */ + Cell_XY_To_Radar_Pixel(tac_cell_x, tac_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(tac_cell_x + Lepton_To_Cell(TacLeptonWidth), tac_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the coordinates based on the current frame of radar animation. + */ + x1-= SpecialRadarFrame; + y1-= SpecialRadarFrame; + x2+= SpecialRadarFrame; + y2+= SpecialRadarFrame; + + Mark_Radar(x1, y1, x2, y2, TRUE, barlen); + + /* + ** setup a graphic view port class so we can write all the pixels relative + ** to 0,0 rather than relative to full screen coordinates. + */ + oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); + + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + + // Draw upper right hand corner + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + + // Draw lower left hand corner + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + + // Draw lower right hand corner + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); + + Set_Logic_Page(oldpage); + _last_pos = tac_cell; + _last_frame = SpecialRadarFrame; + RadarCursorRedraw = FALSE; +} + + +/*************************************************************************** + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Radar_Anim(void) +{ + /* + ** Do nothing if we're in player-name mode + */ + if (IsPlayerNames) + return; + + if (!Map.IsSidebarActive) return; + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage, + RadX + RadOffX, + RadY + RadOffY, + RadIWidth, + RadIHeight-2); +#endif +//Mono_Set_Cursor(0,0); +#ifdef WIN32 + Draw_Box(RadX+RadOffX-1, RadY+RadOffY-1, RadIWidth+2, RadIHeight+2, BOXSTYLE_RAISED, true); +#endif + draw_window.Clear(); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + Flag_To_Redraw(false); + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * RadarClass::AI -- Processes radar input (non-tactical). * + * * + * This routine intercepts any player input that concerns the radar map, but not those * + * areas that represent the tactical map. These are handled by the tactical map AI * + * processor. Primarily, this routine handles the little buttons that border the radar * + * map. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 12/26/1994 JLB : Moves tactical map with click or drag. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void RadarClass::AI(KeyNumType & input, int x, int y) +{ + /* + ** Check to see if we need to animate the radar cursor + */ + if (IsRadarActive && Map.IsSidebarActive && SpecialRadarFrame) { + SpecialRadarFrame--; + RadarCursorRedraw = TRUE; + IsToRedraw = TRUE; + Flag_To_Redraw(FALSE); + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarActivating) { + if (!DoesRadarExist) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + DoesRadarExist = true; + Radar_Activate(3); + } + } else { + RadarAnimFrame--; + if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + Radar_Activate(3); + } + } + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame == MAX_RADAR_FRAMES) { + IsRadarDeactivating = false; + } else { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + + /* + ** Check here to see if radar is being jammed, so we can update the + ** animation with snow. + */ + if (!IsRadarActivating && !IsRadarDeactivating && IsRadarJammed) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + if (RadarAnimFrame > (3 + RADAR_ACTIVATED_FRAME)) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Check here to see if the sonar pulse is active, and if it is, flag the + ** radar to redraw so the pulse ping will display. + */ + if (IsPulseActive) { + Flag_To_Redraw(true); + IsToRedraw = true; + if (RadarPulseFrame >= 8) { + RadarPulseFrame = 0; + IsPulseActive = false; + } + } + + DisplayClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * * + * This is the main action function for handling player I/O on the radar map. It processes * + * mouse clicks as well as mouse moves. * + * * + * INPUT: flags -- The event flags that trigger this function call. * + * * + * key -- Reference the keyboard event that applies to the trigger event. * + * * + * OUTPUT: Should further processing of the input list be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) +{ + CELL cell; // cell num click happened over + int x,y; // Sub cell pixel coordinates. + int cellx,celly; // Sub cell pixel coordinates. + bool shadow; // is the cell in shadow or not + ObjectClass * object = 0; // what object is in the cell + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + if (Map.IsSidebarActive) { + Map.Help_Text(TXT_NONE); + } + + if (!Map.IsRadarActive) { + if (Map.IsSidebarActive) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); +// Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + return(false); + } + + /* + ** Disable processing if the player names are up + */ + if (Map.Is_Player_Names()) { + GadgetClass::Action(0, key); + return(true); + } + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + + /* + ** See if the mouse is over the radar general area, but not yet + ** over the active region of the radar map. In such a case, the + ** mouse is overridden to be the normal cursor and no other + ** action is performed. + */ + if (x < Map.RadX+Map.RadOffX || x >= Map.RadX+Map.RadIWidth || y < Map.RadY+Map.RadOffY || y >= Map.RadY+Map.RadIHeight) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); + return(false); + } + + int result = Map.RadarClass::Click_In_Radar(x, y, false); + + if (result == 1) { + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1 && Map.In_Radar(cell)) { + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); +// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + cellx = 12; + celly = 12; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Cell_Object(cell, cellx, celly); + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + + /* + ** If this is not a valid radar map action then we are not going to do + ** anything. + */ + switch (action) { + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; + + default: + action = ACTION_NONE; + object = NULL; + break; + } + + /* + ** On the radar map the only reason we would want the normal cursor to + ** appear is if we were over one of our own selected units. Otherwise + ** we can't move there. + **/ + if (action == ACTION_NONE) { + if (object && object->IsSelected) { + object = NULL; + } else { + action = ACTION_NOMOVE; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (flags & LEFTUP) { + Map.Mouse_Left_Up(-1, shadow, object, action, true); + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTPRESS) { + Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); + } + + } else { + + Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); + + if (flags & LEFTPRESS) { + + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + int cellx = Cell_X(cell); + int celly = Cell_Y(cell); + cellx -= Lepton_To_Cell(Map.TacLeptonWidth) / 2; + cellx = max(cellx, Map.MapCellX); + celly -= Lepton_To_Cell(Map.TacLeptonHeight) / 2; + celly = max(celly, Map.MapCellY); + cell = XY_Cell(cellx, celly); + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); +// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + Map.Set_Tactical_Position(Cell_Coord(cell)); + cell = Coord_Cell(Map.DesiredTacticalCoord); + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(true); + Map.SpecialRadarFrame = 4; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Zoom_Mode(cell); + } + } + } + } + if (result == -1) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + GadgetClass::Action(0, key); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * * + * This routine intercepts the refresh cells request and if it detects that the sidebar * + * should be rerendered, it flags the radar map to redraw during the next draw operation. * + * * + * INPUT: cell -- The origin cell that the refresh cell offset list is based upon. * + * * + * list -- Pointer to the list of offsets from the origin cell that specifies the * + * cells to be flagged for redraw. If the list starts with the special * + * code to refresh the sidebar, then this routine recognizes it and flags * + * the radar map to be redrawn accordingly. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + DisplayClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell. * + * * + * This routine will try to center the radar map around the cell position specified. * + * * + * INPUT: cell -- The cell to try and position the radar map around. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Radar_Position(CELL cell) +{ +#ifdef WIN32 + int oldx, oldy; + int newx, newy; + int newcell; + + if (ZoomFactor != 1) { + oldx = (Cell_X(cell) - MapCellX); + oldy = (Cell_Y(cell) - MapCellY); + } else { + oldx = 0; + oldy = 0; + } + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); + + if (RadarCell != newcell) { + int forced = FALSE; + int xmod = newx; + int ymod = newy; + + int radx = (Cell_X(RadarCell)) - xmod; + int rady = (Cell_Y(RadarCell)) - ymod; + + RadarX = newx; + RadarY = newy; + RadarCell = newcell; + + if (Map.IsSidebarActive && Map.IsRadarActive) { + int radw = RadarCellWidth-ABS(radx); // Replicable width. + int radh = RadarCellHeight-ABS(rady); // Replicable height. + + if (radw < 1) forced = true; + if (radh < 1) forced = true; + + if (!forced && (radw != RadarWidth || radh != RadarHeight)) { + /* + ** Blit the section that is actually overlapping. + ** + ** If the video card isnt able to blit overlapped regions then we have + ** to do the blit in two stages via an intermediate buffer. The test to allow + ** overlapped blits is done in the library at the time of setting the video mode. + */ + if (OverlappedVideoBlits || !HidPage.Get_IsDirectDraw()) { + + /* + ** Overlapped blits are OK or we dont have a video memory hid page so blits are + ** always done in software by the library anyway. + */ + HidPage.Blit(HidPage, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + + } else { + /* + ** Create a temporary intermediate surface + */ + GraphicBufferClass temp_surface; + temp_surface.Init((RadarWidth + 16) & 0xfffffff0, + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum) GBC_VIDEOMEM); + + /* + ** Do the blit in 2 stages. + */ + HidPage.Blit(temp_surface, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); + + temp_surface.Blit(HidPage, + 0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + } + + /* + ** Now we need to flag the section of the map that is going to redraw. + */ + if ( radx != 0 ) { + int min; + int max; + if ( radx < 0 ) { // this mean regen the right edge + min = radw; + max = radw+ABS(radx); + } else { // this mean regen the left edge + min = 0; + max = radx; + } + for (int x = min; x < max; x++ ) { + for (int y = 0; y < RadarCellHeight; y++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + if ( newy != 0 ) { + int min; + int max; + if ( rady < 0 ) { // this mean regen the bottom edge + min = radh; + max = radh+ABS(rady); + } else { // this mean regen the top edge + min = 0; + max = rady; + } + for (int y = min; y < max; y++ ) { + for ( int x = 0; x < RadarCellWidth; x++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + } + } + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + if (ZoomFactor > 4) { + FullRedraw = forced; + } + } else { + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + } +#else + + if (cell != RadarCell) { + int oldx = RadarX; + int oldy = RadarY; + CELL oldcell = RadarCell; + int x = Cell_X(cell); + int y = Cell_Y(cell); + + /* + ** If the new radar position is not too close to the edge of the + ** current radar display, then don't bother to change the radar position. + */ + if ((unsigned)(x - (RadarX+10)) > RadarCellWidth-20) { + oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; + } else { + oldx = Cell_X(RadarCell)-MapCellX; + } + if ((unsigned)(y - (RadarY+10)) > RadarCellHeight-20) { + oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; + } else { + oldy = Cell_Y(RadarCell)-MapCellY; + } + +#ifdef NEVER + if ((unsigned)(x - (RadarX+10)) > RadarWidth-20 || (unsigned)(y - (RadarY+10)) > RadarHeight-20) { + oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; + oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; + } else { + oldx = Cell_X(RadarCell)-MapCellX; + oldy = Cell_Y(RadarCell)-MapCellY; + } +#endif + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + RadarX = oldx + MapCellX; + RadarY = oldy + MapCellY; + RadarCell = XY_Cell(RadarX, RadarY); + IsToRedraw = true; + Flag_To_Redraw(false); + if (oldcell != RadarCell) { + FullRedraw = IsRadarActive; + } + } +#endif +} + + +/*********************************************************************************************** + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * * + * This returns the cell number of the upper left corner of the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the radar map upper left corner cell position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Radar_Position(void) +{ + return(RadarCell); +} + + +/*********************************************************************************************** + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * * + * This routine is called when the tactical map changes its dimensions. This occurs when * + * the tactical map moves and when the sidebar pops on or off. * + * * + * INPUT: x,y -- The cell coordinate of the upper left corner of the tactical map. * + * * + * w,y -- The cell width and height of the tactical map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + Set_Radar_Position(XY_Cell(x, y)); + DisplayClass::Set_Map_Dimensions(x, y, w, h); +} + + +/*********************************************************************************************** + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to * + * * + * This routine is called when the tactical map is to change position. The radar map might * + * be adjusted as well by this routine. * + * * + * INPUT: coord -- The new coordinate to use for the upper left corner of the tactical * + * map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Tactical_Position(COORDINATE coord) +{ + DisplayClass::Set_Tactical_Position(coord); + Set_Radar_Position(Coord_Cell(TacticalCoord)); +} + + +/*********************************************************************************************** + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * * + * This routine will examine the specified cell number and return whether it is visible * + * on the radar map. This depends on the radar map position. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: Is the specified cell visible on the radar map currently? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Cell_On_Radar(CELL cell) +{ + if ((unsigned)cell > MAP_CELL_TOTAL) + return(false); + + if (!IsZoomed) { + return(true); + } + return(!(((Cell_X(cell) - RadarX) > RadarCellWidth) || ((Cell_Y(cell) - RadarY) > RadarCellHeight))); +} + + +/*********************************************************************************************** + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * * + * INPUT: * + * on true = turn on; false = turn off * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Player_Names(bool on) +{ + IsPlayerNames = on; + IsToRedraw = true; + if (on) { + Flag_To_Redraw(true); +// Flag_To_Redraw(false); + } else { + Flag_To_Redraw(true); // force drawing of the plate + } +} + + +/*********************************************************************************************** + * RadarClass::Spy_Next_House -- advances to the next house we're spying on, or returns NULL * + * * + * INPUT: * + * * + * OUTPUT: * + * 0 = no house to spy on, 1 = found house to spy on * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Spy_Next_House(void) +{ + bool tospy = false; + int spiedby = (1<<(PlayerPtr->Class->House)); + + IsPlayerNames = false; + IsToRedraw = true; + + HousesType maxhouse; + HousesType firsthouse; + HousesType house; + + if (Session.Type == GAME_NORMAL) { + firsthouse = HOUSE_SPAIN; + maxhouse = HOUSE_GOOD; + } else { + firsthouse = HOUSE_MULTI1; + maxhouse = HOUSE_COUNT; + } + + if (IsHouseSpy) { + house = (HousesType)(SpyingOn+1); + } else { + house = firsthouse; + } + + if (house < firsthouse) house = firsthouse; + + while (house < maxhouse && !tospy) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive && hptr != PlayerPtr) { + if (hptr->RadarSpied & spiedby) { + tospy = true; + SpyingOn = house; + break; + } + } + house++; + } + + IsHouseSpy = tospy; + + Flag_To_Redraw(true); // force drawing of the plate + return(tospy); +} + + +/*********************************************************************************************** + * Draw_House_Info -- Print house statistics on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Draw_House_Info(void) +{ + int y; + char txt[40]; + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return false; + } + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + y = RadY + RadOffY + (2*RESFACTOR); + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + Fancy_Text_Print (TXT_SPY_INFO, RadX + RadOffX + (6 * RESFACTOR), y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += 7*RESFACTOR; + + HouseClass * ptr = HouseClass::As_Pointer(SpyingOn); + if (ptr && (ptr->RadarSpied & (1<<(PlayerPtr->Class->House))) ) { + PlayerColorType c_idx; + RemapControlType * color; + TextPrintType style; + + c_idx = ptr->RemapColor; + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + + /* + ** Print house's name below 'spy report' + */ + txt[0] = 0; + sprintf(txt, "%s", ptr->IniName);//Text_String(ptr->Class->FullName)); +// sprintf(txt, "%s", ptr->Name());//Text_String(ptr->Class->FullName)); + if (strlen(txt)) { + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } + Fancy_Text_Print (txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + } else { + strcpy(txt,"________"); + } + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_BUILDNGS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + +// count & print buildings + itoa(ptr->CurBuildings, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_UNITS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print units + itoa(ptr->CurUnits, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + 6 * RESFACTOR, y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_INFANTRY, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print infantry + itoa(ptr->CurInfantry, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); +#if(0) + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_AIRCRAFT, RADAR_X + RADAR_OFF_X + 6, y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; +// count & print aircraft + for (i = AIRCRAFT_NONE+1, count = 0; i < AIRCRAFT_COUNT; i++) { + count += ptr->AQuantity[i]; + } + itoa(count, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX, y, + color, BLACK, style); +#endif + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * Draw_Names -- draws players' names on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Draw_Names(void) +{ + PlayerColorType c_idx; + HousesType house; + HouseClass * ptr; + int y; + char txt[40]; + HousesType h; + int kills; + RemapControlType * color; + TextPrintType style; + + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return; + } + +// CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RADAR_X, RADAR_Y+1, +// WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + + y = RadY + RadOffY+(2*RESFACTOR); + + Fancy_Text_Print (TXT_NAME_COLON, RadX + RadOffX, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Fancy_Text_Print (TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW); + y += 6*RESFACTOR+1; + + LogicPage->Draw_Line(RadX + RadOffX, y, RadX + RadOffX + RadIWidth - 1, y, LTGREY); + y += 2*RESFACTOR; + + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr) continue; + + /* + ** Decode this house's color + */ + c_idx = ptr->RemapColor; + + if (ptr->IsDefeated) { + color = &GreyScheme; + style = TPF_6PT_GRAD | TPF_NOSHADOW; + } else { + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + } + + /* + ** Initialize our message + */ + txt[0] = 0; +// sprintf(txt, "%s", ptr->Name()); + sprintf(txt, "%s", ptr->IsHuman ? ptr->IniName : Text_String(TXT_COMPUTER)); + + if (strlen(txt) == 0) { + strcpy(txt,"________"); + } + + /* + ** Print the player name, and the # of kills + */ +#ifdef WIN32 + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } +#else + if (strlen(txt) > 8) { + txt[8] = '.'; + txt[9] = '\0'; + } +#endif + Fancy_Text_Print (txt, RadX + RadOffX, y, color, TBLACK, style); + + kills = 0; + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + kills += ptr->UnitsKilled[h]; + kills += ptr->BuildingsKilled[h]; + } + sprintf(txt, "%2d", kills); + Fancy_Text_Print (txt, RadX + RadOffX + RadIWidth - 2, y, color, TBLACK, style | TPF_RIGHT); + + y += 6*RESFACTOR+1; + + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); +} + + +void RadarClass::Activate_Pulse(void) +{ + if (IsRadarActive || PlayerPtr->IsGPSActive) { + IsPulseActive = true; + RadarPulseFrame = 0; + } +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * * + * Determines if the radar map is currently being displayed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map currently being displayed as active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Active(void) +{ + return(IsRadarActive || PlayerPtr->IsGPSActive); +// return IsRadarActive || PlayerPtr->IsGPSActive; +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * * + * This will determine if the radar map is available. If available, the radar will show * + * representations of terrain, units, and buildings. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map available to be displayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Existing(void) +{ + return(DoesRadarExist || PlayerPtr->IsGPSActive); +} + + +/*********************************************************************************************** + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * * + * This will fetch the current state of the radar jamming for the player. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar currently jammed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Get_Jammed(void) +{ + if(PlayerPtr->IsGPSActive) return(false); + return(IsRadarJammed); +} + + +void RadarClass::Flag_Cell(CELL cell) +{ +// Radar_Pixel(cell); + DisplayClass::Flag_Cell(cell); +} diff --git a/CODE/RADAR.H b/CODE/RADAR.H new file mode 100644 index 0000000..8b0c2fd --- /dev/null +++ b/CODE/RADAR.H @@ -0,0 +1,246 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RADAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RADAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADAR_H +#define RADAR_H + +#include "display.h" + +class RadarClass: public DisplayClass +{ + public: + RadarClass(void); + RadarClass(NoInitClass const & x) : DisplayClass(x) {}; + + /* + ** The dimensions and coordinates of the radar map. + */ + int RadX; + int RadOffX; + int RadY; + int RadOffY; + int RadWidth; + int RadHeight; + int RadIWidth; + int RadIHeight; + int RadPWidth; + int RadPHeight; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void Flag_Cell(CELL cell); + virtual bool Map_Cell(CELL cell, HouseClass * house); + virtual bool Jam_Cell(CELL cell, HouseClass * house); + virtual bool UnJam_Cell(CELL cell, HouseClass * house); + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + virtual void Set_Tactical_Position(COORDINATE coord); + void Zoom_Mode(CELL cell); + int Click_In_Radar(int &x, int &y, bool change=false) const; + void Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y); + + bool Is_Zoomable(void) const; + void Set_Radar_Position(CELL cell); + CELL Radar_Position(void); + bool Radar_Activate(int control); + void Plot_Radar_Pixel(CELL cell); + void Radar_Pixel(CELL cell); + void Coord_To_Radar_Pixel(COORDINATE coord, int &x, int &y); + void Cursor_Cell(CELL cell, int value); + void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen); + void Radar_Cursor(int forced = false); + void Render_Terrain(CELL cell, int x, int y, int size); + bool Cell_On_Radar(CELL cell); + void Render_Infantry(CELL cell, int x, int y, int size); + void Render_Overlay(CELL cell, int x, int y, int size); + void Radar_Anim(void); + bool Is_Radar_Active(void); + bool Is_Radar_Existing(void); + + /* + ** Toggles player names on & off + */ + void Player_Names(bool on); + int Is_Player_Names(void) {return IsPlayerNames;} + bool Spying_On_House(void) {return IsHouseSpy;} + void Draw_Names(void); + bool Draw_House_Info(void); + int Is_Zoomed(void) {return IsZoomed;} + bool Get_Jammed(void); + void Set_Jammed(bool jam) {IsRadarJammed = jam;} + bool Spy_Next_House(void); + void Activate_Pulse(void); + + protected: + + /* + ** Radar map constant values. + */ + enum RadarClassEnums { + RADAR_ACTIVATED_FRAME=22, + MAX_RADAR_FRAMES = 41 + }; + + /* + ** If the radar map must be completely redrawn, then this flag will be true. + ** Typical causes of this would be when the radar first appears, or when the + ** screen has been damaged. + */ + unsigned IsToRedraw:1; + unsigned RadarCursorRedraw:1; + + /* + ** If the radar map is visible then this flag is true. + */ + unsigned DoesRadarExist:1; + unsigned IsRadarActive:1; + unsigned IsRadarActivating:1; + unsigned IsRadarDeactivating:1; + unsigned IsRadarJammed:1; + + /* + ** Flag to tell whether sonar pulse should be displayed on radar map + */ + unsigned IsPulseActive:1; + int RadarPulseFrame; + + /* + ** Special radar frame is set when a new location is selected on the + ** radar map. It counts down through the special radar cursors until + ** either the radar cursor becomes normal or the radar cursor is moved + ** again. + */ + int SpecialRadarFrame; + int RadarAnimFrame; + + static void const * RadarAnim; + static void const * RadarPulse; + static void const * RadarFrame; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class RTacticalClass : public GadgetClass { + public: + RTacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class RadarClass; + }; + friend class RTacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static RTacticalClass RadarButton; + + private: + + /* + ** The current radar position as the upper left corner cell for the + ** radar map display. The width and height is controlled by the + ** actual dimensions of the radar map display box (in pixels). + */ + unsigned RadarX; + unsigned RadarY; + unsigned RadarCellWidth; + unsigned RadarCellHeight; + unsigned RadarCell; + + /* + ** This is the origin (pixel offsets) for the upper left corner + ** of the radar map within the full radar map area of the screen. + ** This is biased so that the radar map, when smaller than full + ** size will appear centered. + */ + unsigned BaseX; + unsigned BaseY; + + unsigned RadarWidth; + unsigned RadarHeight; + + /* + ** If the radar map is in zoom mode, then this value will be true. + */ + unsigned IsZoomed:1; + + /* + ** This flag is true if the radar map is in its special show-the-player + ** names mode. + */ + unsigned IsPlayerNames:1; + + /* + ** This flag is true if the radar map is in its special show-the-units + ** of-another-house mode. + */ + unsigned IsHouseSpy:1; + + /* + ** This is the zoom factor to use. This value is the number of pixels wide + ** each cell will occupy on the radar map. Completely zoomed out would be a + ** value of 1. + */ + int ZoomFactor; + + /* + ** If we're spying on a house's radar facility, this field shows the + ** name of the house we're spying on. + */ + HousesType SpyingOn; + + /* + ** This is the list of radar pixels that need to be updated. Only a partial + ** list is maintained for maximum speed. + */ + unsigned PixelPtr; + enum PixelStackEnums {PIXELSTACK=400}; + CELL PixelStack[PIXELSTACK]; +}; + + +#endif diff --git a/CODE/RADIO.CPP b/CODE/RADIO.CPP new file mode 100644 index 0000000..a003245 --- /dev/null +++ b/CODE/RADIO.CPP @@ -0,0 +1,293 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RADIO.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RADIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** These are the text representations of the radio messages that can be transmitted. +*/ +char const * RadioClass::Messages[RADIO_COUNT] = { + "static (no message)", + "Roger.", + "Come in.", + "Over and out.", + "Requesting transport.", + "Attach to transport.", + "I've got a delivery for you.", + "I'm performing load/unload maneuver. Be careful.", + "I'm clear.", + "You are clear to unload. Driving away now.", + "Am unable to comply.", + "I'm starting construction now... act busy.", + "I've finished construction. You are free.", + "We bumped, redraw yourself please.", + "I'm trying to load up now.", + "May I become a passenger?", + "Are you ready to receive shipment?", + "Are you trying to become a passenger?", + "Move to location X.", + "Do you need to move?", + "All right already. Now what?", + "I'm a passenger now.", + "Backup into refinery now.", + "Run away!", + "Tether established.", + "Tether broken.", + "Repair one step.", + "Are you prepared to fight?", + "Attack this target please.", + "Reload one step.", + "Circumstances prevent success.", + "All done with the request.", + "Do you need service depot work?", + "Are you sitting on service depot?" +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * * + * This displays the radio connection value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void RadioClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(29, 7);mono->Printf("0-%-47s", Messages[Old[0]]); + mono->Set_Cursor(29, 8);mono->Printf("1-%-47s", Messages[Old[1]]); + mono->Set_Cursor(29, 9);mono->Printf("2-%-47s", Messages[Old[2]]); + if (Radio) { + mono->Set_Cursor(20, 7);mono->Printf("%08X", Radio->As_Target()); + } + MissionClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * * + * This is the base version of what should happen when a radio message is received. It * + * turns the radio off when the "OVER_OUT" message is received. All other messages are * + * merely acknowledged with a "ROGER". * + * * + * INPUT: from -- The object that is initiating this radio message (always valid). * + * * + * message -- The radio message received. * + * * + * param -- Reference to optional value that might be used to return more * + * information than can be conveyed in the simple radio response * + * messages. * + * * + * OUTPUT: Returns with the response radio message. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 09/24/1994 JLB : Streamlined to be only a communications carrier. * + * 05/22/1995 JLB : Recognized who is sending the message * + * 06/05/1996 JLB : Radio message history tracking. * + *=============================================================================================*/ +RadioMessageType RadioClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + /* + ** Keep a record of the last message received by this radio. + */ + if (message != Old[0]) { + Old[2] = Old[1]; + Old[1] = Old[0]; + Old[0] = message; + } + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + ** This only applies if the message is coming from the object that + ** has an established conversation with this object. + */ + if (from == Radio && message == RADIO_OVER_OUT) { + MissionClass::Receive_Message(from, message, param); + Radio_Off(); + return(RADIO_ROGER); + } + + /* + ** The "hello" message is an attempt to establish contact. If this radio + ** is already in an established conversation with another object, then + ** return with "negative". If all is well, return with "roger". + */ + if (message == RADIO_HELLO && Strength) { + if (Radio == from || Radio == NULL) { + Radio = from; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(MissionClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * * + * This routine is used to transmit a radio message from this object to another. Most * + * inter object coordination is handled through this mechanism. * + * * + * INPUT: to -- Pointer to the object that will receive the radio message. * + * * + * message -- The message itself (see RadioType). * + * * + * param -- Optional reference to parameter that might be used to pass or * + * receive additional information. * + * * + * OUTPUT: Returns with the response radio message from the receiving object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, long & param, RadioClass * to) +{ + assert(IsActive); + + if (to == NULL) { + to = Contact_With_Whom(); + } + + /* + ** If there is no target for the radio message, then always return static. + */ + if (to == NULL) return(RADIO_STATIC); + + /* + ** Handle some special case processing that occurs when certain messages + ** are transmitted. + */ + if (to == Radio && message == RADIO_OVER_OUT) { + Radio = 0; + } + + /* + ** If this object is not in radio contact but the message + ** indicates that radio contact should be established, then + ** try to do so. If the other party agrees then contact + ** is established. + */ + if (message == RADIO_HELLO) { + Transmit_Message(RADIO_OVER_OUT); + if (to->Receive_Message(this, message, param) == RADIO_ROGER) { + Radio = to; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(to->Receive_Message(this, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * * + * This routine will break radio contact as the object is entering limbo state. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool RadioClass::Limbo(void) +{ + assert(IsActive); + + if (!IsInLimbo) { + Transmit_Message(RADIO_OVER_OUT); + } + return(MissionClass::Limbo()); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * * + * This routine will transmit the specified message to the object. This routine differs * + * from the normal Transmit_Message in that the LParam value is "faked" into the * + * parameter list. It is presumed that the message sent with this function does not * + * require the LParam. * + * * + * INPUT: message -- The message to transmit. * + * * + * to -- The requested receiver of this message. * + * * + * OUTPUT: Returns with the radio response from the receiver. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, RadioClass * to) +{ + assert(IsActive); + + return(Transmit_Message(message, LParam, to)); +} diff --git a/CODE/RADIO.H b/CODE/RADIO.H new file mode 100644 index 0000000..07d284a --- /dev/null +++ b/CODE/RADIO.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RADIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RADIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADIO_H +#define RADIO_H + +#include "mission.h" + +class ObjectClass; +class TechnoClass; + + +/**************************************************************************** +** Radio contact is controlled by this class. It handles the mundane chore +** of keeping the radio contact alive as well as broadcasting messages +** to the receiving radio. Radio contact is primarily used when one object +** is in "command" of another. +*/ +class RadioClass : public MissionClass { + private: + + /* + ** This is a record of the last message received by this receiver. + */ + RadioMessageType Old[3]; + + /* + ** This is the object that radio communication has been established + ** with. Although is is only a one-way reference, it is required that + ** the receiving radio is also tuned to the object that contains this + ** radio set. + */ + RadioClass * Radio; + + /* + ** This is a text representation of all the possible radio messages. This + ** text is used for monochrome debug printing. + */ + static char const * Messages[RADIO_COUNT]; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + RadioClass(RTTIType rtti, int id) : MissionClass(rtti, id), Radio(0) {}; + RadioClass(NoInitClass const & x) : MissionClass(x) {}; + virtual ~RadioClass(void) {Radio=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool In_Radio_Contact(void) const {return (Radio != 0);}; + void Radio_Off(void) {Radio = 0;}; + TechnoClass * Contact_With_Whom(void) const {return (TechnoClass *)Radio;}; + + // Inherited from base class(es). + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual RadioMessageType Transmit_Message(RadioMessageType message, long & param=LParam, RadioClass * to=NULL); + virtual RadioMessageType Transmit_Message(RadioMessageType message, RadioClass * to); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Limbo(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/CODE/RADOS.PJT b/CODE/RADOS.PJT new file mode 100644 index 0000000..7d5864d --- /dev/null +++ b/CODE/RADOS.PJT @@ -0,0 +1,389 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[Files] +C:\PROJECTS\C&Czero\code\2KEYFRAM.CPP +C:\PROJECTS\C&Czero\code\aadata.cpp +C:\PROJECTS\C&Czero\code\abstract.cpp +C:\PROJECTS\C&Czero\code\abstract.h +C:\PROJECTS\C&Czero\code\adata.cpp +C:\PROJECTS\C&Czero\code\adpcm.cpp +C:\PROJECTS\C&Czero\code\aircraft.cpp +C:\PROJECTS\C&Czero\code\aircraft.h +C:\PROJECTS\C&Czero\code\anim.cpp +C:\PROJECTS\C&Czero\code\anim.h +C:\PROJECTS\C&Czero\code\audio.cpp +C:\PROJECTS\C&Czero\code\audio.h +C:\PROJECTS\C&Czero\code\base.cpp +C:\PROJECTS\C&Czero\code\base.h +C:\PROJECTS\C&Czero\code\bbdata.cpp +C:\PROJECTS\C&Czero\code\bdata.cpp +C:\PROJECTS\C&Czero\code\bfiofile.cpp +C:\PROJECTS\C&Czero\code\BFIOFILE.H +C:\PROJECTS\C&Czero\code\BLOWFISH.CPP +C:\PROJECTS\C&Czero\code\BLOWFISH.H +C:\PROJECTS\C&Czero\code\BUFFERX.H +C:\PROJECTS\C&Czero\code\building.cpp +C:\PROJECTS\C&Czero\code\building.h +C:\PROJECTS\C&Czero\code\bullet.cpp +C:\PROJECTS\C&Czero\code\bullet.h +C:\PROJECTS\C&Czero\code\cargo.cpp +C:\PROJECTS\C&Czero\code\cargo.h +C:\PROJECTS\C&Czero\code\carry.cpp +C:\PROJECTS\C&Czero\code\carry.h +C:\PROJECTS\C&Czero\code\ccfile.cpp +C:\PROJECTS\C&Czero\code\ccfile.h +C:\PROJECTS\C&Czero\code\ccini.cpp +C:\PROJECTS\C&Czero\code\ccini.h +C:\PROJECTS\C&Czero\code\cdata.cpp +C:\PROJECTS\C&Czero\code\cdfile.cpp +C:\PROJECTS\C&Czero\code\cdfile.h +C:\PROJECTS\C&Czero\code\cell.cpp +C:\PROJECTS\C&Czero\code\cell.h +C:\PROJECTS\C&Czero\code\checkbox.cpp +C:\PROJECTS\C&Czero\code\checkbox.h +C:\PROJECTS\C&Czero\code\cheklist.cpp +C:\PROJECTS\C&Czero\code\cheklist.h +C:\PROJECTS\C&Czero\code\colrlist.cpp +C:\PROJECTS\C&Czero\code\colrlist.h +C:\PROJECTS\C&Czero\code\combat.cpp +C:\PROJECTS\C&Czero\code\combuf.cpp +C:\PROJECTS\C&Czero\code\combuf.h +C:\PROJECTS\C&Czero\code\compat.h +C:\PROJECTS\C&Czero\code\confdlg.cpp +C:\PROJECTS\C&Czero\code\confdlg.h +C:\PROJECTS\C&Czero\code\connect.cpp +C:\PROJECTS\C&Czero\code\connect.h +C:\PROJECTS\C&Czero\code\connmgr.h +C:\PROJECTS\C&Czero\code\conquer.cpp +C:\PROJECTS\C&Czero\code\conquer.h +C:\PROJECTS\C&Czero\code\const.cpp +C:\PROJECTS\C&Czero\code\control.cpp +C:\PROJECTS\C&Czero\code\control.h +C:\PROJECTS\C&Czero\code\coord.cpp +C:\PROJECTS\C&Czero\code\crc.cpp +C:\PROJECTS\C&Czero\code\crc.h +C:\PROJECTS\C&Czero\code\credits.cpp +C:\PROJECTS\C&Czero\code\credits.h +C:\PROJECTS\C&Czero\code\crew.cpp +C:\PROJECTS\C&Czero\code\crew.h +C:\PROJECTS\C&Czero\code\debug.cpp +C:\PROJECTS\C&Czero\code\debug.h +C:\PROJECTS\C&Czero\code\defines.h +C:\PROJECTS\C&Czero\code\descdlg.cpp +C:\PROJECTS\C&Czero\code\descdlg.h +C:\PROJECTS\C&Czero\code\dial8.cpp +C:\PROJECTS\C&Czero\code\dial8.h +C:\PROJECTS\C&Czero\code\dialog.cpp +C:\PROJECTS\C&Czero\code\display.cpp +C:\PROJECTS\C&Czero\code\display.h +C:\PROJECTS\C&Czero\code\door.cpp +C:\PROJECTS\C&Czero\code\door.h +C:\PROJECTS\C&Czero\code\dpmi.cpp +C:\PROJECTS\C&Czero\code\dpmi.h +C:\PROJECTS\C&Czero\code\drive.cpp +C:\PROJECTS\C&Czero\code\drive.h +C:\PROJECTS\C&Czero\code\drop.cpp +C:\PROJECTS\C&Czero\code\drop.h +C:\PROJECTS\C&Czero\code\dtable.cpp +C:\PROJECTS\C&Czero\code\edit.cpp +C:\PROJECTS\C&Czero\code\edit.h +C:\PROJECTS\C&Czero\code\ending.cpp +C:\PROJECTS\C&Czero\code\ending.h +C:\PROJECTS\C&Czero\code\event.cpp +C:\PROJECTS\C&Czero\code\event.h +C:\PROJECTS\C&Czero\code\expand.cpp +C:\PROJECTS\C&Czero\code\externs.h +C:\PROJECTS\C&Czero\code\face.cpp +C:\PROJECTS\C&Czero\code\face.h +C:\PROJECTS\C&Czero\code\facing.cpp +C:\PROJECTS\C&Czero\code\facing.h +C:\PROJECTS\C&Czero\code\factory.cpp +C:\PROJECTS\C&Czero\code\factory.h +C:\PROJECTS\C&Czero\code\FILEPCX.H +C:\PROJECTS\C&Czero\code\findpath.cpp +C:\PROJECTS\C&Czero\code\flasher.cpp +C:\PROJECTS\C&Czero\code\flasher.h +C:\PROJECTS\C&Czero\code\fly.cpp +C:\PROJECTS\C&Czero\code\fly.h +C:\PROJECTS\C&Czero\code\foot.cpp +C:\PROJECTS\C&Czero\code\foot.h +C:\PROJECTS\C&Czero\code\ftimer.h +C:\PROJECTS\C&Czero\code\function.h +C:\PROJECTS\C&Czero\code\fuse.cpp +C:\PROJECTS\C&Czero\code\fuse.h +C:\PROJECTS\C&Czero\code\gadget.cpp +C:\PROJECTS\C&Czero\code\gadget.h +C:\PROJECTS\C&Czero\code\gamedlg.cpp +C:\PROJECTS\C&Czero\code\gamedlg.h +C:\PROJECTS\C&Czero\code\gauge.cpp +C:\PROJECTS\C&Czero\code\gauge.h +C:\PROJECTS\C&Czero\code\globals.cpp +C:\PROJECTS\C&Czero\code\goptions.cpp +C:\PROJECTS\C&Czero\code\goptions.h +C:\PROJECTS\C&Czero\code\gscreen.cpp +C:\PROJECTS\C&Czero\code\gscreen.h +C:\PROJECTS\C&Czero\code\hdata.cpp +C:\PROJECTS\C&Czero\code\heap.cpp +C:\PROJECTS\C&Czero\code\heap.h +C:\PROJECTS\C&Czero\code\help.cpp +C:\PROJECTS\C&Czero\code\help.h +C:\PROJECTS\C&Czero\code\house.cpp +C:\PROJECTS\C&Czero\code\house.h +C:\PROJECTS\C&Czero\code\hsv.cpp +C:\PROJECTS\C&Czero\code\HSV.H +C:\PROJECTS\C&Czero\code\idata.cpp +C:\PROJECTS\C&Czero\code\infantry.cpp +C:\PROJECTS\C&Czero\code\infantry.h +C:\PROJECTS\C&Czero\code\ini.cpp +C:\PROJECTS\C&Czero\code\ini.h +C:\PROJECTS\C&Czero\code\inibin.cpp +C:\PROJECTS\C&Czero\code\inicode.cpp +C:\PROJECTS\C&Czero\code\init.cpp +C:\PROJECTS\C&Czero\code\INT.CPP +C:\PROJECTS\C&Czero\code\INT.H +C:\PROJECTS\C&Czero\code\interpal.cpp +C:\PROJECTS\C&Czero\code\intro.cpp +C:\PROJECTS\C&Czero\code\intro.h +C:\PROJECTS\C&Czero\code\iomap.cpp +C:\PROJECTS\C&Czero\code\ioobj.cpp +C:\PROJECTS\C&Czero\code\ipx.cpp +C:\PROJECTS\C&Czero\code\ipx.h +C:\PROJECTS\C&Czero\code\ipxaddr.cpp +C:\PROJECTS\C&Czero\code\ipxaddr.h +C:\PROJECTS\C&Czero\code\ipxconn.cpp +C:\PROJECTS\C&Czero\code\ipxconn.h +C:\PROJECTS\C&Czero\code\ipxgconn.cpp +C:\PROJECTS\C&Czero\code\ipxgconn.h +C:\PROJECTS\C&Czero\code\ipxmgr.cpp +C:\PROJECTS\C&Czero\code\ipxmgr.h +C:\PROJECTS\C&Czero\code\itable.cpp +C:\PROJECTS\C&Czero\code\jshell.cpp +C:\PROJECTS\C&Czero\code\jshell.h +C:\PROJECTS\C&Czero\code\keyframe.cpp +C:\PROJECTS\C&Czero\code\language.h +C:\PROJECTS\C&Czero\code\layer.cpp +C:\PROJECTS\C&Czero\code\layer.h +C:\PROJECTS\C&Czero\code\lcwuncmp.cpp +C:\PROJECTS\C&Czero\code\led.h +C:\PROJECTS\C&Czero\code\link.cpp +C:\PROJECTS\C&Czero\code\link.h +C:\PROJECTS\C&Czero\code\lint.h +C:\PROJECTS\C&Czero\code\list.cpp +C:\PROJECTS\C&Czero\code\list.h +C:\PROJECTS\C&Czero\code\listnode.h +C:\PROJECTS\C&Czero\code\loaddlg.cpp +C:\PROJECTS\C&Czero\code\loaddlg.h +C:\PROJECTS\C&Czero\code\logic.cpp +C:\PROJECTS\C&Czero\code\logic.h +C:\PROJECTS\C&Czero\code\map.cpp +C:\PROJECTS\C&Czero\code\map.h +C:\PROJECTS\C&Czero\code\mapeddlg.cpp +C:\PROJECTS\C&Czero\code\mapedit.cpp +C:\PROJECTS\C&Czero\code\mapedit.h +C:\PROJECTS\C&Czero\code\mapedplc.cpp +C:\PROJECTS\C&Czero\code\mapedsel.cpp +C:\PROJECTS\C&Czero\code\mapedtm.cpp +C:\PROJECTS\C&Czero\code\mapsel.cpp +C:\PROJECTS\C&Czero\code\menus.cpp +C:\PROJECTS\C&Czero\code\message.h +C:\PROJECTS\C&Czero\code\mission.cpp +C:\PROJECTS\C&Czero\code\mission.h +C:\PROJECTS\C&Czero\code\mixfile.cpp +C:\PROJECTS\C&Czero\code\mixfile.h +C:\PROJECTS\C&Czero\code\monoc.cpp +C:\PROJECTS\C&Czero\code\monoc.h +C:\PROJECTS\C&Czero\code\mouse.cpp +C:\PROJECTS\C&Czero\code\mouse.h +C:\PROJECTS\C&Czero\code\MP.CPP +C:\PROJECTS\C&Czero\code\MP.H +C:\PROJECTS\C&Czero\code\mplayer.cpp +C:\PROJECTS\C&Czero\code\msgbox.cpp +C:\PROJECTS\C&Czero\code\msgbox.h +C:\PROJECTS\C&Czero\code\msglist.cpp +C:\PROJECTS\C&Czero\code\msglist.h +C:\PROJECTS\C&Czero\code\netdlg.cpp +C:\PROJECTS\C&Czero\code\nullconn.cpp +C:\PROJECTS\C&Czero\code\nullconn.h +C:\PROJECTS\C&Czero\code\nulldlg.cpp +C:\PROJECTS\C&Czero\code\nullmgr.cpp +C:\PROJECTS\C&Czero\code\nullmgr.h +C:\PROJECTS\C&Czero\code\object.cpp +C:\PROJECTS\C&Czero\code\object.h +C:\PROJECTS\C&Czero\code\odata.cpp +C:\PROJECTS\C&Czero\code\options.cpp +C:\PROJECTS\C&Czero\code\options.h +C:\PROJECTS\C&Czero\code\overlay.cpp +C:\PROJECTS\C&Czero\code\overlay.h +C:\PROJECTS\C&Czero\code\palette.cpp +C:\PROJECTS\C&Czero\code\PALETTE.H +C:\PROJECTS\C&Czero\code\phone.h +C:\PROJECTS\C&Czero\code\power.cpp +C:\PROJECTS\C&Czero\code\power.h +C:\PROJECTS\C&Czero\code\profile.cpp +C:\PROJECTS\C&Czero\code\queue.cpp +C:\PROJECTS\C&Czero\code\queue.h +C:\PROJECTS\C&Czero\code\radar.cpp +C:\PROJECTS\C&Czero\code\radar.h +C:\PROJECTS\C&Czero\code\radio.cpp +C:\PROJECTS\C&Czero\code\radio.h +C:\PROJECTS\C&Czero\code\rand.cpp +C:\PROJECTS\C&Czero\code\random.cpp +C:\PROJECTS\C&Czero\code\RANDOM.H +C:\PROJECTS\C&Czero\code\rawfile.cpp +C:\PROJECTS\C&Czero\code\rawfile.h +C:\PROJECTS\C&Czero\code\region.h +C:\PROJECTS\C&Czero\code\reinf.cpp +C:\PROJECTS\C&Czero\code\rgb.cpp +C:\PROJECTS\C&Czero\code\RGB.H +C:\PROJECTS\C&Czero\code\RNG.H +C:\PROJECTS\C&Czero\code\rotbmp.cpp +C:\PROJECTS\C&Czero\code\ROTBMP.H +C:\PROJECTS\C&Czero\code\rules.cpp +C:\PROJECTS\C&Czero\code\rules.h +C:\PROJECTS\C&Czero\code\savedlg.h +C:\PROJECTS\C&Czero\code\saveload.cpp +C:\PROJECTS\C&Czero\code\scenario.cpp +C:\PROJECTS\C&Czero\code\SCENARIO.H +C:\PROJECTS\C&Czero\code\score.cpp +C:\PROJECTS\C&Czero\code\score.h +C:\PROJECTS\C&Czero\code\screen.h +C:\PROJECTS\C&Czero\code\scroll.cpp +C:\PROJECTS\C&Czero\code\scroll.h +C:\PROJECTS\C&Czero\code\sdata.cpp +C:\PROJECTS\C&Czero\code\session.cpp +C:\PROJECTS\C&Czero\code\SESSION.H +C:\PROJECTS\C&Czero\code\SHA.CPP +C:\PROJECTS\C&Czero\code\SHA.H +C:\PROJECTS\C&Czero\code\shapebtn.cpp +C:\PROJECTS\C&Czero\code\shapebtn.h +C:\PROJECTS\C&Czero\code\sidebar.cpp +C:\PROJECTS\C&Czero\code\sidebar.h +C:\PROJECTS\C&Czero\code\slider.cpp +C:\PROJECTS\C&Czero\code\slider.h +C:\PROJECTS\C&Czero\code\smudge.cpp +C:\PROJECTS\C&Czero\code\smudge.h +C:\PROJECTS\C&Czero\code\sounddlg.cpp +C:\PROJECTS\C&Czero\code\sounddlg.h +C:\PROJECTS\C&Czero\code\special.cpp +C:\PROJECTS\C&Czero\code\special.h +C:\PROJECTS\C&Czero\code\sprite.cpp +C:\PROJECTS\C&Czero\code\stage.h +C:\PROJECTS\C&Czero\code\startup.cpp +C:\PROJECTS\C&Czero\code\super.cpp +C:\PROJECTS\C&Czero\code\super.h +C:\PROJECTS\C&Czero\code\tab.cpp +C:\PROJECTS\C&Czero\code\tab.h +C:\PROJECTS\C&Czero\code\taction.cpp +C:\PROJECTS\C&Czero\code\TACTION.H +C:\PROJECTS\C&Czero\code\tarcom.cpp +C:\PROJECTS\C&Czero\code\target.cpp +C:\PROJECTS\C&Czero\code\target.h +C:\PROJECTS\C&Czero\code\tcpip.h +C:\PROJECTS\C&Czero\code\tdata.cpp +C:\PROJECTS\C&Czero\code\team.cpp +C:\PROJECTS\C&Czero\code\team.h +C:\PROJECTS\C&Czero\code\teamtype.cpp +C:\PROJECTS\C&Czero\code\teamtype.h +C:\PROJECTS\C&Czero\code\techno.cpp +C:\PROJECTS\C&Czero\code\techno.h +C:\PROJECTS\C&Czero\code\template.cpp +C:\PROJECTS\C&Czero\code\template.h +C:\PROJECTS\C&Czero\code\terrain.cpp +C:\PROJECTS\C&Czero\code\terrain.h +C:\PROJECTS\C&Czero\code\tevent.cpp +C:\PROJECTS\C&Czero\code\TEVENT.H +C:\PROJECTS\C&Czero\code\textbtn.cpp +C:\PROJECTS\C&Czero\code\textbtn.h +C:\PROJECTS\C&Czero\code\theme.cpp +C:\PROJECTS\C&Czero\code\theme.h +C:\PROJECTS\C&Czero\code\toggle.cpp +C:\PROJECTS\C&Czero\code\toggle.h +C:\PROJECTS\C&Czero\code\trigger.cpp +C:\PROJECTS\C&Czero\code\trigger.h +C:\PROJECTS\C&Czero\code\txtlabel.cpp +C:\PROJECTS\C&Czero\code\txtlabel.h +C:\PROJECTS\C&Czero\code\type.h +C:\PROJECTS\C&Czero\code\udata.cpp +C:\PROJECTS\C&Czero\code\unit.cpp +C:\PROJECTS\C&Czero\code\unit.h +C:\PROJECTS\C&Czero\code\vdata.cpp +C:\PROJECTS\C&Czero\code\vector.cpp +C:\PROJECTS\C&Czero\code\vector.h +C:\PROJECTS\C&Czero\code\version.cpp +C:\PROJECTS\C&Czero\code\VERSION.H +C:\PROJECTS\C&Czero\code\vessel.cpp +C:\PROJECTS\C&Czero\code\vessel.h +C:\PROJECTS\C&Czero\code\visudlg.cpp +C:\PROJECTS\C&Czero\code\visudlg.h +C:\PROJECTS\C&Czero\code\warhead.cpp +C:\PROJECTS\C&Czero\code\warhead.h +C:\PROJECTS\C&Czero\code\watcom.h +C:\PROJECTS\C&Czero\code\weapon.cpp +C:\PROJECTS\C&Czero\code\weapon.h +C:\PROJECTS\C&Czero\code\winstub.cpp +C:\PROJECTS\C&Czero\code\wwfile.h + + +[State] +SysSetCwd='C:\projects\c&czero\code' +SrchSetFlags=0x000320aa +FileSortMode=0x0 +StateWindowFrame=62,134,923,511,0x62c9f5fa +_StateWindow=0,0,990,647,0x00100018,'C:\projects\c&czero\code\RECT.CPP',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,1,10,'',12,255,48,0,7,243,252,253,247,249,247,24,1,400,0,246,252,248,244,247,15,15,15,15,0,0 +_StateWindow=0,0,990,647,0x00100008,'C:\projects\c&czero\code\eng\conquer.txt',240,15,244,32,32,0,32,32,32,32,8,4294967295,4294967295,0,10,'',12,255,48,0,7,243,252,253,247,249,247,1,1,400,0,246,252,248,244,247,15,15,15,15,0,0 +_StateBuffer='C:\projects\c&czero\code\RECT.CPP',0x0400048e,24,1,25,'4 7','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\eng\conquer.txt',0x0000048e,1,1,25,'5 9','',0x0,'' +_StateBuffer='C:\projects\c&czero\code\2KEYFRAM.CPP',0x0c00048e,198,40,25,'4 7','',0x0,'' +_StateHistory=SEARCH,'MCV','Official','Create_Units','Create_U','Is_Forced_','Create_U','FIX','Take_Dam','threat','MAP_ERRO' +_StateHistory=REPLACE,'buffer','pointer','unit','} else {','_cd_name','_CD_Volume_Label','DEBUG_LEVEL_2' +_StateHistory=XMACRO,'sort','upper','sort' +_StateHistory=FILELIST,'C:\projects\c&czero\code\INFANTRY.CPP','C:\projects\c&czero\code\VERSION.CPP','C:\projects\c&czero\code\FINDPATH.CPP','C:\projects\c&czero\code\todo.txt','D:\CW32\NOSPLASH','C:\projects\c&czero\code\eng\conquer.txt','C:\projects\c&czero\code\LANGUAGE.H','C:\projects\c&czero\code\CONQUER.CPP','C:\projects\c&czero\code\2KEYFRAM.CPP','C:\projects\c&czero\code\RECT.CPP' +_StateHistory=EDITFILE,'bullet.cpp','scenario.cpp','infantry.cpp','version.cpp','findpath.cpp','todo.txt','eng\conquer.txt','language.h','conquer.cpp','2keyfram.cpp' +_StateHistory=GOTOLINE,'754','344','3649','634','210' + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +VCSProject='' +VCSProjectPath='' +VCSProjectLocalPath='' +_RestoreSysFlags=0x62c9f5fa, 0xfffffffc + +[Compiler] +TagSetCmd='${HOME}${WTAGS} -oc -d -t${TAGFILE}.tag -p${TAGFILE}.ptg',0x8000060 +BrowseSetFile='C:\PROJECTS\C&Czero\code\rados.ptg' +TagSetFile='C:\PROJECTS\C&Czero\code\rados.tag' +CompilerAddCmd='Microsoft Assembler','ftee masm -w2 -zi %r%e;',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft Assembler', +CompilerAddCmd='Borland C++','ftee bcc -S %r.c',1073741824,'',0,'ftee make %r.obj',16,'ftee make %r.obj',16,'',0,'',0,'_BorlandCppErrorInfo','','','','%v%p' +CompilerAddResponse='Borland C++', +CompilerAddCmd='Borland Turbo Assembler','ftee make %r.obj',1073741824,'',0,'ftee make %r.obj',16,'',16,'',0,'',0,'_TasmErrorInfo','','','','%v%p' +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddCmd='$_cw_proj_hash_$','',1073741825,'',1,'ftee watcom\binw\wmake %r.obj',209,'ftee m.bat',209,'ftee joemake',209,'g.bat',224,'_MSLinkErrorInfo','_MicrosoftErrorInfo','_NMakeErrorInfo','proj.err','c:\projects\c&czero\code' +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddCmd='Microsoft C','ftee cl -c -AL -Gsw -Ow -Zpe %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_MicrosoftErrorInfo','','','','%v%p' +CompilerAddResponse='Microsoft C', +CompilerAddCmd='Default Project','',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_ErrorInfoDefault','','','proj.err','%v%p' +CompilerAddResponse='Default Project', +CompilerAddCmd='Zortech C++','ftee ztc -a -b -c -g -ml -W %r%e',1073741824,'',0,'',16,'',16,'',0,'',0,'_ZortechCppErrorInfo','','','','%v%p' +CompilerAddResponse='Zortech C++', +CompilerAddCmd='Script','ftee make %r.inf',1073741824,'',0,'ftee make',16,'ftee make',16,'',0,'',0,'_BorlandCppErrorInfo','','','','' +CompilerAddResponse='Script', +CompilerAddCmd='Microsoft C(NT-i386)','${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',1073741936,'${FTEE} cl -DSTRICT -c -W3 -G3 -D_X86_=1 -DWIN32 %r%e',112,'',144,'',144,'',0,'',0,'_MicrosoftErrorInfo','','','','' +CompilerAddResponse='Microsoft C(NT-i386)', +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Microsoft C(NT-i386)','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Microsoft C(NT-i386)','.hpp' diff --git a/CODE/RAMFILE.CPP b/CODE/RAMFILE.CPP new file mode 100644 index 0000000..4b7e7d6 --- /dev/null +++ b/CODE/RAMFILE.CPP @@ -0,0 +1,475 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RAMFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RAMFileClass::Close -- This will 'close' the ram file. * + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * RAMFileClass::Is_Open -- Is the file open? * + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * RAMFileClass::Open -- Opens the RAM based file. * + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * RAMFileClass::Read -- Read data from the file. * + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * RAMFileClass::Size -- Returns with the size of the ram file. * + * RAMFileClass::Write -- Copies data to the ram file. * + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "ramfile.h" +#include + + +/*********************************************************************************************** + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * * + * This routine will construct a "file" object that actually is just a front end processor * + * for a buffer. Access to the buffer will appear as if it was accessing a file. This * + * is different from the caching ability of the buffered file class in that this file * + * class has no real file counterpart. Typical use of this is for algorithms that were * + * originally designed for file processing, but are now desired to work with a buffer. * + * * + * INPUT: buffer -- Pointer to the buffer to use for this file. The buffer will already * + * contain data if the file is opened for READ. It will be considered * + * a scratch buffer if opened for WRITE. If the buffer pointer is NULL * + * but the length parameter is not, then a buffer will be allocated * + * of the specified length. This case is only useful for opening the * + * file for WRITE. * + * * + * length -- The length of the buffer submitted to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::RAMFileClass(void * buffer, int len) : + Buffer((char *)buffer), + MaxLength(len), + Length(len), + Offset(0), + Access(READ), + IsOpen(false), + IsAllocated(false) +{ + if (buffer == NULL && len > 0) { + Buffer = new char[len]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * * + * The destructor will deallocate any buffer that it allocated. Otherwise it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::~RAMFileClass(void) +{ + Close(); + if (IsAllocated) { + delete [] Buffer; + Buffer = NULL; + IsAllocated = false; + } +} + + +/*********************************************************************************************** + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Create(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Delete(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * * + * RAM files are always available. * + * * + * INPUT: none * + * * + * OUTPUT: TRUE * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Available(int ) +{ + return(true); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Open -- Is the file open? * + * * + * This answers the question whether the file is open or not. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Open(void) const +{ + return(IsOpen); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * * + * This routine will open the ram file. The name is meaningless so that parameter is * + * ignored. If the access mode is for write, then the pseudo-file can be written until the * + * buffer is full. If the file is opened for read, then the buffer is presumed to be full * + * of the data to be read. * + * * + * INPUT: name -- ignored. * + * * + * access-- The access method to use for the data buffer -- either READ or WRITE. * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(char const * , int access) +{ + return(Open(access)); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens the RAM based file. * + * * + * This will open the ram based file for read or write. If the file is opened for write, * + * the the 'file' can be written up to the limit of the buffer's size. If the file is * + * opened for read, then the buffer is presumed to hold the data to be read. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(int access) +{ + if (Buffer == NULL || Is_Open()) { + return(false); + } + + Offset = 0; + Access = access; + IsOpen = true; + + switch (access) { + default: + case READ: + break; + + case WRITE: + Length = 0; + break; + + case READ|WRITE: + break; + } + + return(Is_Open()); +} + + +/*********************************************************************************************** + * RAMFileClass::Read -- Read data from the file. * + * * + * Use this routine just like a normal file read. It will copy the bytes from the ram * + * buffer to the destination specified. When the ram buffer is exhausted, less bytes than * + * requested will be read. * + * * + * INPUT: buffer -- Pointer to the buffer to store the data to. * + * * + * size -- The number of bytes to 'read' into the specified buffer. * + * * + * OUTPUT: Returns with the number of bytes copied to the destination buffer. If the number * + * of bytes returned is less than requested, then this indicates that the source * + * buffer is exhausted. * + * * + * WARNINGS: The read function only applies to ram 'files' opened for read access. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Read(void * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(READ); + hasopened = true; + } else { + if ((Access & READ) == 0) { + return(0); + } + } + + int tocopy = (size < (Length-Offset)) ? size : (Length-Offset); + memmove(buffer, &Buffer[Offset], tocopy); + Offset += tocopy; + + if (hasopened) { + Close(); + } + + return(tocopy); +} + + +/*********************************************************************************************** + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * * + * This routine will move the read/write position of the ram file to the location specified * + * by the offset and direction parameters. It functions similarly to the regular file * + * seek method. * + * * + * INPUT: pos -- The signed offset from the home position specified by the "dir" * + * parameter. * + * * + * dir -- The home position to base the position offset on. This will either be * + * the start of the file, the end of the file, or the current read/write * + * position. * + * * + * OUTPUT: Returns with the new file position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Seek(long pos, int dir) +{ + if (Buffer == NULL || !Is_Open()) { + return(Offset); + } + + int maxoffset = Length; + if ((Access & WRITE) != 0) { + maxoffset = MaxLength; + } + + switch (dir) { + case SEEK_CUR: + Offset += pos; + break; + + case SEEK_SET: + Offset = 0 + pos; + break; + + case SEEK_END: + Offset = maxoffset + pos; + break; + } + + if (Offset < 0) Offset = 0; + if (Offset > maxoffset) Offset = maxoffset; + + if (Offset > Length) { + Length = Offset; + } + + return(Offset); +} + + +/*********************************************************************************************** + * RAMFileClass::Size -- Returns with the size of the ram file. * + * * + * This will return the size of the 'real' data in the ram file. The real data is either * + * the entire buffer, if opened for READ, or just the written data if opened for WRITE. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the ram file system considers to be valid * + * data of the 'file'. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Size(void) +{ + return(Length); +} + + +/*********************************************************************************************** + * RAMFileClass::Write -- Copies data to the ram file. * + * * + * This function similarly to the regular write operation supported for files. It copies * + * the data specified to the current write position in the ram file. * + * * + * INPUT: buffer -- Pointer to the data to be written. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the actual number of bytes written. This will be less than requested * + * if the buffer is exhausted of space prematurely. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Write(void const * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(WRITE); + hasopened = true; + } else { + if ((Access & WRITE) == 0) { + return(0); + } + } + + int maxwrite = MaxLength - Offset; + int towrite = (size < maxwrite) ? size : maxwrite; + memmove(&Buffer[Offset], buffer, towrite); + Offset += towrite; + + if (Offset > Length) { + Length = Offset; + } + + if (hasopened) { + Close(); + } + + return(towrite); +} + + +/*********************************************************************************************** + * RAMFileClass::Close -- This will 'close' the ram file. * + * * + * Closing a ram file actually does nothing but record that it is now closed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void RAMFileClass::Close(void) +{ + IsOpen = false; +} diff --git a/CODE/RAMFILE.H b/CODE/RAMFILE.H new file mode 100644 index 0000000..d98f754 --- /dev/null +++ b/CODE/RAMFILE.H @@ -0,0 +1,112 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RAMFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RAMFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RAMFILE_H +#define RAMFILE_H + +#include "wwfile.h" + + +class RAMFileClass : public FileClass +{ + public: + RAMFileClass(void * buffer, int len); + virtual ~RAMFileClass(void); + + virtual char const * File_Name(void) const {return("UNKNOWN");} + virtual char const * Set_Name(char const * ) {return(File_Name());} + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int access=READ); + virtual int Open(int access=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(true);} + virtual void Error(int , int = false, char const * =NULL) {} + + operator char const * () {return File_Name();} + + private: + + /* + ** Pointer to the buffer that the "file" will reside in. + */ + char * Buffer; + + /* + ** The maximum size of the buffer. The file occupying the buffer + ** may be smaller than this size. + */ + int MaxLength; + + /* + ** The number of bytes in the sub-file occupying the buffer. + */ + int Length; + + /* + ** The current file position offset within the buffer. + */ + int Offset; + + /* + ** The file was opened with this access mode. + */ + int Access; + + /* + ** Is the file currently open? + */ + bool IsOpen; + + /* + ** Was the file buffer allocated during construction of this object? + */ + bool IsAllocated; +}; + + + + +#endif diff --git a/CODE/RAND.CPP b/CODE/RAND.CPP new file mode 100644 index 0000000..19111a1 --- /dev/null +++ b/CODE/RAND.CPP @@ -0,0 +1,173 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\rand.cpv 4.35 04 Jul 1996 16:14:56 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/25/95 * + * * + * Last Update : June 17, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Sim_IRandom -- Returns minval to maxval, inclusive * + * Sim_Random -- Returns 0 - 255 * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +int SimRandIndex = 0; + +/*************************************************************************** + * Sim_Random -- Returns 0 - 255 * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 - 255, at random * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Takes advantage of character math wrap. * + *=========================================================================*/ +int Sim_Random(void) +{ + static unsigned char _randvals[] = { + 0x47, 0xce, 0xc6, 0x6e, 0xd7, 0x9f, 0x98, 0x29, 0x92, 0x0c, 0x74, 0xa2, + 0x65, 0x20, 0x4b, 0x4f, 0x1e, 0xed, 0x3a, 0xdf, 0xa5, 0x7d, 0xb5, 0xc8, + 0x86, 0x01, 0x81, 0xca, 0xf1, 0x17, 0xd6, 0x23, 0xe1, 0xbd, 0x0e, 0xe4, + 0x62, 0xfa, 0xd9, 0x5c, 0x68, 0xf5, 0x7f, 0xdc, 0xe7, 0xb9, 0xc4, 0xb3, + 0x7a, 0xd8, 0x06, 0x3e, 0xeb, 0x09, 0x1a, 0x31, 0x3f, 0x46, 0x28, 0x12, + 0xf0, 0x10, 0x84, 0x76, 0x3b, 0xc5, 0x53, 0x18, 0x14, 0x73, 0x7e, 0x59, + 0x48, 0x93, 0xaa, 0x1d, 0x5d, 0x79, 0x24, 0x61, 0x1b, 0xfd, 0x2b, 0xa8, + 0xc2, 0xdb, 0xe8, 0x2a, 0xb0, 0x25, 0x95, 0xab, 0x96, 0x83, 0xfc, 0x5f, + 0x9c, 0x32, 0x78, 0x9a, 0x9e, 0xe2, 0x8e, 0x35, 0x4c, 0x41, 0xa1, 0x69, + 0x5a, 0xfe, 0xa7, 0xa4, 0xf6, 0x6d, 0xc1, 0x58, 0x0a, 0xcf, 0xea, 0xc3, + 0xba, 0x85, 0x99, 0x8d, 0x36, 0xb6, 0xdd, 0xd3, 0x04, 0xe6, 0x45, 0x0d, + 0x60, 0xae, 0xa3, 0x22, 0x4d, 0xe9, 0xc9, 0x9b, 0xb7, 0x0f, 0x02, 0x42, + 0xf9, 0x0b, 0x8f, 0x43, 0x44, 0x87, 0x70, 0xbe, 0xe3, 0xf8, 0xee, 0xa9, + 0xbc, 0xc0, 0x67, 0x33, 0x16, 0x37, 0x57, 0xad, 0x5e, 0x9d, 0x64, 0x40, + 0x54, 0x05, 0x2c, 0xe0, 0xb2, 0x97, 0x08, 0xaf, 0x75, 0x8a, 0x5b, 0xfb, + 0x4e, 0xbf, 0x91, 0xf3, 0xcb, 0x7c, 0x63, 0xef, 0x89, 0x52, 0x6c, 0x2f, + 0x21, 0x4a, 0xf7, 0xcd, 0x2e, 0xf4, 0xc7, 0x6f, 0x19, 0xb1, 0x66, 0xcc, + 0x90, 0x8c, 0x50, 0x51, 0x26, 0x7b, 0xda, 0x49, 0x80, 0x30, 0x55, 0x1f, + 0xd2, 0xb4, 0xd1, 0xd5, 0x6b, 0xf2, 0x72, 0xbb, 0x13, 0x3d, 0xff, 0x15, + 0x38, 0xe5, 0xd4, 0xde, 0x2d, 0x27, 0x94, 0xa0, 0xd0, 0x39, 0x82, 0x8b, + 0x03, 0xac, 0x3c, 0x34, 0x77, 0xb8, 0xec, 0x00, 0x07, 0x1c, 0x88, 0xa6, + 0x56, 0x11, 0x71, 0x6a, + }; + + ((unsigned char&)SimRandIndex)++; +// SimRandIndex &= 0xff; + return(_randvals[SimRandIndex]); +} + + +/*************************************************************************** + * Sim_IRandom -- returns minval to maxval, inclusive * + * * + * INPUT: * + * minval,maxval range limits * + * * + * OUTPUT: * + * random value * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Uses fixed point math helper routine. * + *=========================================================================*/ +int Sim_IRandom(int minval, int maxval) +{ + return(Fixed_To_Cardinal((maxval-minval), Sim_Random()) + minval); +} + + +/* +** This routine can be used to create the _RandVals[] table. It will be +** different every time it's run. +*/ +#if 0 +#include +#include +#include +#include + +void main(void); + +void main(void) +{ + int i; + FILE *fp; + int r; + char is_used[256]; + char rand_val[256]; + + srand ( time ( NULL ) ) ; + + for (i = 0; i < 256; i++) { + is_used[i] = 0; + } + + /* + ** Store the digits 0 to 255 in a random order within the 'rand_val' array + ** This ensures our random number sequence represents every value. + */ + for (i = 0; i < 256; i++) { + if (kbhit()!=0) { + printf("ABORTED!\n"); + break; + } + r = (rand() * 256) / RAND_MAX; // r is the index into array + if (is_used[r]==0) { + is_used[r] = 1; + rand_val[r] = i; + } else { + i--; + } + } + + fp = fopen("rand.cpp","wt"); + if (fp==NULL) { + printf("File error\n"); + exit(0); + } + + fprintf(fp, "unsigned char _RandVals[] = {\n"); + for (i = 0; i < 256; i++) { + fprintf(fp,"0x%02x,\n",rand_val[i]); + } + fprintf(fp,"};\n"); + fclose(fp); + +} + +#endif + diff --git a/CODE/RANDOM.CPP b/CODE/RANDOM.CPP new file mode 100644 index 0000000..7020a62 --- /dev/null +++ b/CODE/RANDOM.CPP @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RANDOM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/28/96 * + * * + * Last Update : February 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomClass::RandomClass -- Constructor for the random number class. * + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * RandomClass::operator() -- Ranged random number generator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "random.h" +#ifdef RANDOM_COUNT +#include +extern long Frame; +#endif + +/*********************************************************************************************** + * RandomClass::RandomClass -- Constructor for the random number class. * + * * + * This constructor can take an integer as a parameter. This allows the class to be * + * constructed by assigning an integer to an existing object. The compiler creates a * + * temporary with the constructor and then performs a copy constructor operation. * + * * + * INPUT: seed -- The optional starting seed value to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +RandomClass::RandomClass(unsigned seed) : + Seed(seed) +{ +#ifdef RANDOM_COUNT + Count1 = 0; + Count2 = 0; +#endif +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * * + * This routine will fetch the next random number in the sequence. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next random number. * + * * + * WARNINGS: This routine modifies the seed value so that subsequent calls will not return * + * the same value. Take note that this routine only returns 15 bits of * + * random number. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator ()(void) +{ +#ifdef RANDOM_COUNT + Count1++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Transform the seed value into the next number in the sequence. + */ + Seed = (Seed * MULT_CONSTANT) + ADD_CONSTANT; + + /* + ** Extract the 'random' bits from the seed and return that value as the + ** random number result. + */ + return((Seed >> THROW_AWAY_BITS) & (~((~0) << SIGNIFICANT_BITS))); +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Ranged random number generator. * + * * + * This function will return with a random number within the range specified. This replaces * + * the functionality of IRandom() in the old library. * + * * + * INPUT: minval -- The minimum value to return from the function. * + * * + * maxval -- The maximum value to return from the function. * + * * + * OUTPUT: Returns with a random number that falls between the minval and maxval (inclusive). * + * * + * WARNINGS: The range specified must fall within the maximum bit significance of the * + * random number algorithm (15 bits), otherwise the value returned will be * + * decidedly non-random. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator() (int minval, int maxval) +{ +#ifdef RANDOM_COUNT + Count2++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Test for shortcut case where the range is null and thus + ** the number to return is actually implicit from the + ** parameters. + */ + if (minval == maxval) return(minval); + + /* + ** Ensure that the min and max range values are in proper order. + */ + if (minval > maxval) { + int temp = minval; + minval = maxval; + maxval = temp; + } + + /* + ** Find the highest bit that fits within the magnitude of the + ** range of random numbers desired. Notice that the scan is + ** limited to the range of significant bits returned by the + ** random number algorithm. + */ + int magnitude = maxval - minval; + int highbit = SIGNIFICANT_BITS-1; + while ((magnitude & (1 << highbit)) == 0 && highbit > 0) { + highbit--; + } + + /* + ** Create a full bit mask pattern that has all bits set that just + ** barely covers the magnitude of the number range desired. + */ + int mask = ~( (~0L) << (highbit+1) ); + + /* + ** Keep picking random numbers until it fits within the magnitude desired. + */ + int pick = magnitude+1; + while (pick > magnitude) { + pick = operator()() & mask; + } + + /* + ** Finally, bias the random number pick to the start of the range + ** requested. + */ + return(pick + minval); +} + diff --git a/CODE/RANDOM.H b/CODE/RANDOM.H new file mode 100644 index 0000000..a495dbb --- /dev/null +++ b/CODE/RANDOM.H @@ -0,0 +1,86 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RANDOM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RANDOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/96 * + * * + * Last Update : February 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RANDOM_H +#define RANDOM_H + +/********************************************************************** +** Enable this define to turn on the random # counters. This must not +** be defined for the release version, or saved games won't work! +*/ +//#define RANDOM_COUNT + + +/* +** This class functions like a 'magic' int value that returns a random number +** every time it is read. To set the "random seed" for this, just assign a number +** to the object (just as you would if it were a true 'int' value). Take note that although +** this class will return an 'int', the actual significance of the random number is +** limited to 15 bits (0..32767). +*/ +class RandomClass { + public: + RandomClass(unsigned seed=0); + + operator int(void) {return(operator()());}; + int operator() (void); + int operator() (int minval, int maxval); + + enum { + SIGNIFICANT_BITS=15 // Random number bit significance. + }; + + unsigned long Seed; + +#ifdef RANDOM_COUNT + unsigned long Count1; + unsigned long Count2; +#endif + + protected: + /* + ** Internal working constants that are used to generate the next + ** random number. + */ + enum { + MULT_CONSTANT=0x41C64E6D, // K multiplier value. + ADD_CONSTANT=0x00003039, // K additive value. + THROW_AWAY_BITS=10 // Low bits to throw away. + }; +}; + +#endif diff --git a/CODE/RAWFILE.CPP b/CODE/RAWFILE.CPP new file mode 100644 index 0000000..ae5d51a --- /dev/null +++ b/CODE/RAWFILE.CPP @@ -0,0 +1,1348 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RAWFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include + +#include "rawfile.h" + +#ifndef WIN32 +#include +#include +#include +extern short Hard_Error_Occured; +#endif + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Error(int , int , char const * ) +{ +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const * filename) : + Rights(0), + BiasStart(0), + BiasLength(-1), + Handle(NULL_HANDLE), + Filename(filename), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const * filename) +{ + if (Filename != NULL && Allocated) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (filename == NULL) return(NULL); + + Bias(0); + + ((char *&)Filename) = strdup(filename); + if (Filename == NULL) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (Filename == NULL) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetitively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ + #ifndef WIN32 + Hard_Error_Occured = 0; + #endif + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + #endif + break; + + case WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_creat(Filename, 0, &Handle); + #endif + break; + + case READ|WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); + #endif + break; + } + + /* + ** Biased files must be positioned past the bias start position. + */ + if (BiasStart != 0 || BiasLength != -1) { + Seek(0, SEEK_SET); + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == NULL_HANDLE) { + +#ifdef WIN32 +// return(false); + Error(GetLastError(), false, Filename); +// continue; +#else + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } + continue; +#endif + } + break; + } + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ +#ifndef WIN32 + bool open_failed; +#endif + + if (Filename == NULL) return(false); + + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +#ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (Handle == NULL_HANDLE) { + return(false); + } + break; +#else + + Hard_Error_Occured = 0; + open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } + } + if (!open_failed) break; +#endif + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ +#ifdef WIN32 + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + if (_dos_close(Handle)) { + Error(errno, false, Filename); + } +#endif + Handle = NULL_HANDLE; + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + +#ifdef WIN32 + /* + ** Try to close the file. If there was an error (who knows what that could be), then + ** call the error routine. + */ + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ + Hard_Error_Occured = 0; + if (_dos_close(Handle)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + break; + } + +#endif + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = NULL_HANDLE; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void * buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** A biased file has the requested read length limited to the bias length of + ** the file. + */ + if (BiasLength != -1) { + int remainder = BiasLength - Seek(0); + size = size < remainder ? size : remainder; + } + +#ifdef WIN32 + long total = 0; + while (size > 0) { + bytesread = 0; + + SetErrorMode(SEM_FAILCRITICALERRORS); + if (!ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + size -= bytesread; + total += bytesread; + Error(GetLastError(), true, Filename); + SetErrorMode(0); + continue; + } + SetErrorMode(0); + size -= bytesread; + total += bytesread; + if (bytesread == 0) break; + } + bytesread = total; + +#else + + int readresult; + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size < 32000L ? size : 32000L; + + Hard_Error_Occured = 0; + readresult = _dos_read(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + } + } + } +#endif //WIN32 + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const * buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + +#ifdef WIN32 + if (!WriteFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + Error(GetLastError(), false, Filename); + } + +#else + + int writeresult; + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + + Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = _dos_write(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } + } + } +#endif //WIN32 + + /* + ** Fixup the bias length if necessary. + */ + if (BiasLength != -1) { + if (Raw_Seek(0) > BiasStart+BiasLength) { + BiasLength = Raw_Seek(0) - BiasStart; + } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + + /* + ** A file that is biased will have a seek operation modified so that the file appears to + ** exist only within the bias range. All bytes outside of this range appear to be + ** non-existant. + */ + if (BiasLength != -1) { + switch (dir) { + case SEEK_SET: + if (pos > BiasLength) { + pos = BiasLength; + } + pos += BiasStart; + break; + + case SEEK_CUR: + break; + + case SEEK_END: + dir = SEEK_SET; + pos += BiasStart + BiasLength; +// pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; +// pos = (pos >= BiasStart) ? pos : BiasStart; + break; + } + + /* + ** Perform the modified raw seek into the file. + */ + long newpos = Raw_Seek(pos, dir) - BiasStart; + + /* + ** Perform a final double check to make sure the file position fits with the bias range. + */ + if (newpos < 0) { + newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; + } + if (newpos > BiasLength) { + newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; + } + return(newpos); + } + + /* + ** If the file is not biased in any fashion, then the normal seek logic will + ** work just fine. + */ + return(Raw_Seek(pos, dir)); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + + /* + ** A biased file already has its length determined. + */ + if (BiasLength != -1) { + return(BiasLength); + } + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + +#ifdef WIN32 + size = GetFileSize(Handle, NULL); + + /* + ** If there was in internal error, then call the error function. + */ + if (size == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { + Hard_Error_Occured = 0; + size = filelength(Handle); + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (size == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + + BiasLength = size-BiasStart; + return(BiasLength); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + + /* + ** A biased file must be at least as long as the bias offset. Seeking to the + ** appropriate start offset has the effect of lengthening the file to the + ** correct length. + */ + if (BiasLength != -1) { + Seek(0, SEEK_SET); + } + + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +#ifdef WIN32 + if (!DeleteFile(Filename)) { + Error(GetLastError(), false, Filename); + return(false); + } +#else + Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } +#endif + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win32 method. * + *=============================================================================================*/ +unsigned long RawFileClass::Get_Date_Time(void) +{ +#ifdef WIN32 + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + WORD dosdate; + WORD dostime; + FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); + return((dosdate << 16) | dostime); + } + return(0); +#else + unsigned short time; + unsigned short date; + unsigned long datetime = 0; + + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + if ( _dos_getftime( Handle, &date, &time ) ) { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + if ( _dos_getftime( Handle, &date, &time ) ) { + RawFileClass::Close(); + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + + RawFileClass::Close(); + } else { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } + + // + // combine the date and time as a long + // + datetime = (date << 16) + time; + + return( datetime ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win 32 method * + *=============================================================================================*/ +bool RawFileClass::Set_Date_Time(unsigned long datetime) +{ +#ifdef WIN32 + if (RawFileClass::Is_Open()) { + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + FILETIME filetime; + if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { + return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime)); + } + } + } + return(false); +#else + unsigned short time; + unsigned short date; + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + // + // only set the date and time if open for READ only + // + if ( Rights == READ ) { + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + // + // return true indicating success + // + return( true ); + } + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + RawFileClass::Close(); + // + // return true indicating success + // + return( true ); + } + + RawFileClass::Close(); + } + } + + // + // return false indicating error + // + return( false ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * * + * This will bias a file by giving it an artificial starting position and length. By * + * using this routine, it is possible to 'fool' the file into ignoring a header and * + * trailing extra data. An example of this would be a file inside of a mixfile. * + * * + * INPUT: start -- The starting offset that will now be considered the start of the * + * file. * + * * + * length -- The forced length of the file. For files that are opened for write, * + * this serves as the artificial constraint on the file's length. For * + * files opened for read, this limits the usable file size. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1996 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Bias(int start, int length) +{ + if (start == 0) { + BiasStart = 0; + BiasLength = -1; + return; + } + + BiasLength = RawFileClass::Size(); + BiasStart += start; + if (length != -1) { + BiasLength = BiasLength < length ? BiasLength : length; + } + BiasLength = BiasLength > 0 ? BiasLength : 0; + + /* + ** Move the current file offset to a legal position if necessary and the + ** file was open. + */ + if (Is_Open()) { + RawFileClass::Seek(0, SEEK_SET); + } +} + + +/*********************************************************************************************** + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * * + * This will perform a seek on the file as if it were unbiased. This is in spite of any * + * bias setting the file may have. The ability to perform a raw seek in this fasion is * + * necessary to maintain the bias ability. * + * * + * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * + * * + * dir -- The origin of the seek operation. * + * * + * OUTPUT: Returns with the new position of the seek operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Raw_Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + +#ifdef WIN32 + switch (dir) { + case SEEK_SET: + dir = FILE_BEGIN; + break; + + case SEEK_CUR: + dir = FILE_CURRENT; + break; + + case SEEK_END: + dir = FILE_END; + break; + } + + pos = SetFilePointer(Handle, pos, NULL, dir); + + /* + ** If there was an error in the seek, then bail with an error condition. + */ + if (pos == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ + Hard_Error_Occured = 0; + pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} diff --git a/CODE/RAWFILE.H b/CODE/RAWFILE.H new file mode 100644 index 0000000..8c5bbc7 --- /dev/null +++ b/CODE/RAWFILE.H @@ -0,0 +1,335 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RAWFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_Hx +#define RAWFILE_Hx + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +#define NULL_HANDLE INVALID_HANDLE_VALUE +#define HANDLE_TYPE HANDLE +#else +#define NULL_HANDLE -1 +#define HANDLE_TYPE int +#endif + +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +#ifndef WWERROR +#define WWERROR -1 +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void); + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + void Bias(int start, int length=-1); + + HANDLE_TYPE Get_File_Handle(void) { return (Handle); }; + + /* + ** These bias values enable a sub-portion of a file to appear as if it + ** were the whole file. This comes in very handy for multi-part files such as + ** mixfiles. + */ + int BiasStart; + int BiasLength; + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + long Raw_Seek(long pos, int dir=SEEK_CUR); + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + HANDLE_TYPE Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + // + // file date and time are in the following formats: + // + // date bits 0-4 day (0-31) + // bits 5-8 month (1-12) + // bits 9-15 year (0-119 representing 1980-2099) + // + // time bits 0-4 second/2 (0-29) + // bits 5-10 minutes (0-59) + // bits 11-15 hours (0-23) + // + unsigned short Date; + unsigned short Time; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : + Rights(READ), + BiasStart(0), + BiasLength(-1), + #ifdef WIN32 + Handle(INVALID_HANDLE_VALUE), + #else + Handle(-1), + #endif + Filename(0), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::~RawFileClass(void) +{ + Close(); + if (Allocated && Filename) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } +} + + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ +#ifdef WIN32 + return(Handle != INVALID_HANDLE_VALUE); +#else + return (Handle >= 0); +#endif +} + +#endif diff --git a/CODE/RAWOLAPI.CPP b/CODE/RAWOLAPI.CPP new file mode 100644 index 0000000..87f2db9 --- /dev/null +++ b/CODE/RAWOLAPI.CPP @@ -0,0 +1,2102 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// rawolapi.cpp - Core WOLAPI interface functions stuff. +// Definitions for RAChatEventSink, RADownloadEventSink, RANetUtilEventSink. +// ajw 07/10/98 + +#include "RAWolapi.h" +#define IID_DEFINED +#include "wolapi\wolapi_i.c" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "Wol_gsup.h" +#include "wolapi\netutildefs.h" + +#include "WolDebug.h" + +bool operator<( const User& u1, const User& u2 ); + +const char* Game_Registry_Key(); + +// The definitions of QueryInterface, AddRef, and Release are needed because we are not including +// files that ordinarily (under MSVC) would define these for us, as part of CComObjectRoot, I believe. +// This Watcom has no equivalent we can use, so we do it manually... + +//*********************************************************************************************** +RAChatEventSink::RAChatEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + bRequestServerListWait( false ), + pOwner( pOwnerIn ), + pServer( NULL ), + bConnected( false ), + hresRequestConnectionError( 0 ), + pChannelList( NULL ), + pUserList( NULL ), + pUserTail( NULL ), + szMotd( NULL ), + bJoined( false ), + bGotKickedTrigger( false ), + bIgnoreChannelLists( false ), + bRequestChannelListForLobbiesWait( false ), + pGameUserList( NULL ), + bRequestGameStartWait( false ), + pUserIPList( NULL ), + pUserIPListTail( NULL ), + iGameID( 0 ) +{ +} + +//*********************************************************************************************** +RAChatEventSink::~RAChatEventSink() +{ +// debugprint( "RAChatEventSink destructor\n" ); + delete pServer; + delete [] szMotd; + DeleteChannelList(); + DeleteUserList(); + DeleteUserIPList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RAChatEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RAChatEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_IChatEvent)) + { + *ppv = (IChatEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RAChatEventSink::AddRef() +{ +// debugprint( "RAChatEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RAChatEventSink::Release() +{ +// debugprint( "RAChatEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerList( HRESULT hRes, Server* pServerHead ) +{ + //strcpy( szLadderServerHost, "games.westwood.com" ); + //iLadderServerPort = 3840; + //strcpy( szGameResServerHost, "games.westwood.com" ); + +// debugprint( ">>> OnServerList got: %i ", hRes ); + DebugChatDef( hRes ); + + if( pServer ) + { + delete pServer; + pServer = NULL; + } + + if( SUCCEEDED( hRes ) ) + { + while( pServerHead ) + { + // Copy the first IRC Server to use in the RequestConnection() call. + if( !pServer && ( strcmp( (char*)pServerHead->connlabel, "IRC" ) == 0 ) ) + { + pServer = new Server; + *pServer = *pServerHead; + } + else if( !*pOwner->szLadderServerHost && ( strcmp( (char*)pServerHead->connlabel, "LAD" ) == 0 ) ) + { +// debugprint( "Scanning '%s'\n", (char*)pServerHead->conndata ); + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szLadderServerHost, token ); + token = strtok( NULL, ";" ); + pOwner->iLadderServerPort = atoi( token ); +// debugprint( "Ladder is at: %s, port %i\n", pOwner->szLadderServerHost, pOwner->iLadderServerPort ); + } + else if( !*pOwner->szGameResServerHost1 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Red Alert game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost1, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort1 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + else if( !*pOwner->szGameResServerHost2 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Aftermath game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost2, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort2 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + pServerHead = pServerHead->next; + } + } + + bRequestServerListWait = false; + return(S_OK); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPageSend( HRESULT hRes ) +{ +// debugprint( ">>> OnPageSend got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_PAGE_NOTHERE && hRes != CHAT_S_PAGE_OFF && hRes != S_OK ) + hRes = E_FAIL; + + hresRequestPageResult = hRes; + + bRequestPageWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPaged( HRESULT, User* pUser, LPCSTR szMessage ) +{ +// debugprint( ">>> OnPaged got: %s ", szMessage ); + + char* szPrint = new char[ strlen( (char*)pUser->name ) + strlen( szMessage ) + strlen( TXT_WOL_ONPAGE ) ]; + sprintf( szPrint, TXT_WOL_ONPAGE, (char*)pUser->name, szMessage ); + if( !pOwner->bInGame ) + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PAGE ); + else + { + Session.Messages.Add_Message( NULL, 0, szPrint, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + if( !pOwner->bFreezeExternalPager ) + strcpy( pOwner->szExternalPager, (char*)pUser->name ); + Map.Flag_To_Redraw(true); + } + + delete [] szPrint; + + Sound_Effect( WOLSOUND_ONPAGE ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnFind( HRESULT hRes, Channel* pChannel ) +{ +// debugprint( ">>> OnFind got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_FIND_NOTHERE && hRes != CHAT_S_FIND_NOCHAN && hRes != CHAT_S_FIND_OFF && hRes != S_OK ) + hRes = E_FAIL; + + if( hRes == S_OK ) + OnFindChannel = *pChannel; + + hresRequestFindResult = hRes; + + bRequestFindWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnLogout( HRESULT hRes, User* pUser ) +{ +// debugprint( ">>> OnLogout got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone has been logged out by the chat server due to inactivity. + // Fake a call to OnChannelLeave(), as the processing is identical. +// debugprint( "OnLogout calling OnChannelLeave for %s, owner=%i\n", (char*)pUser->name, ( pUser->flags & CHAT_USER_CHANNELOWNER ) ); + OnChannelLeave( S_OK, NULL, pUser ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnBusy(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnIdle(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnConnection( HRESULT hRes, LPCSTR motd ) +{ +// debugprint( ">>> OnConnection got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + + // Prepare a new string for a modified version of motd. + szMotd = new char[ strlen( motd ) + 1 ]; + // Replace single line breaks with a space. + // Replace double line breaks with double carriage returns. + + bool bJustDidBreak = false; + const char* szIn = motd; + char* szOut = szMotd; + + while( *szIn ) + { + if( *szIn == '\r' && *( szIn + 1 ) == '\n' ) + { + if( !bJustDidBreak ) + { + *szOut++ = ' '; + bJustDidBreak = true; + } + else + { + szOut--; + *szOut++ = '\r'; + *szOut++ = '\r'; + bJustDidBreak = false; +// debugprint( "^" ); + } + szIn += 2; + } + else + { + *szOut++ = *szIn++; + bJustDidBreak = false; + } +// debugprint( "%c", *( szOut - 1 ) ); + } + *szOut = 0; // Null-terminate. +// debugprint( "\n" ); + +// pOwner->PrintMessage( szMotd ); + + bConnected = true; + } + else + { + hresRequestConnectionError = hRes; + + char szError[150]; + ChatDefAsText( szError, hRes ); + strcat( szError, " (Connect Error)" ); +// debugprint( szError ); + } + + bRequestConnectionWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelCreate( HRESULT hRes, Channel* ) +{ +// debugprint( ">>> OnChannelCreate got: %i ", hRes ); + DebugChatDef( hRes ); + +// if( bJoined ) +// { +// WWMessageBox().Process( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// Fatal( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// } + + if( SUCCEEDED( hRes ) ) + { + bJoined = true; + } + bRequestChannelCreateWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelModify(HRESULT, Channel *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelJoin( HRESULT hRes, Channel* /*pChannel*/, User* pUser ) +{ +// if( SUCCEEDED( hRes ) ) +// debugprint( ">>> OnChannelJoin got: channel '%s', user '%s', %i ", (char*)pChannel->name, (char*)pUser->name, hRes ); +// else +// debugprint( ">>> OnChannelJoin got: %i ", hRes ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelJoin when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring join.\n" ); +// return S_OK; +// } + + hresRequestJoinResult = hRes; + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = true; + bRequestChannelJoinWait = false; + if( pUserList ) + { + // Should never happen. +// debugprint( "pUserList should be NULL (as I am the joiner)!!! Deleting user list...\n" ); + DeleteUserList(); + } + } + else + { + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We can ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + // Add user to our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelJoin - ignoring %s join... \n", (char*)pUser->name ); + return S_OK; + } +// if( !pUserList ) +// { +// // There has to be at least one user there - you. +// debugprint( "pUserList is null in OnChannelJoin!!! users: %s\n", (char*)pUser->name ); +// Fatal( "pUserList is null in OnChannelJoin!!!\n" ); +// } + + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + + // Insert user into list alphabetically. + InsertUserSorted( pUserNew ); + + // Update the shown list. + pOwner->ListChannelUsers(); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + _ASSERTE( pOwner->pGSupDlg ); + pOwner->pGSupDlg->OnGuestJoin( pUser ); + + // Ask for this player's IP address. + pOwner->RequestIPs( (char*)pUser->name ); + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL || pOwner->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Request ladder results for new user. + pOwner->RequestLadders( (char*)pUser->name ); + } + } + } + else + { + bRequestChannelJoinWait = false; + } + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::InsertUserSorted( User* pUserNew ) +{ + if( !pUserList ) + { + pUserList = pUserNew; + pUserTail = pUserNew; + } + else + { + if( *pUserNew < *pUserList ) + { + // Insert user at beginning. + pUserNew->next = pUserList; + pUserList = pUserNew; + } + else + { + User* pUserCheck = pUserList; + User* pUserInsertAfter = NULL; + while( pUserCheck->next ) + { + if( *pUserNew < *pUserCheck->next ) + { + pUserInsertAfter = pUserCheck; + break; + } + pUserCheck = pUserCheck->next; + } + if( pUserInsertAfter ) + { + pUserNew->next = pUserInsertAfter->next; + pUserInsertAfter->next = pUserNew; + } + else + { + // Add user to end. + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + } + } +} + +//*********************************************************************************************** +bool operator<( const User& u1, const User& u2 ) +{ + if( u1.flags & CHAT_USER_CHANNELOWNER && !( u2.flags & CHAT_USER_CHANNELOWNER ) ) + return true; + if( !( u1.flags & CHAT_USER_CHANNELOWNER ) && u2.flags & CHAT_USER_CHANNELOWNER ) + return false; + if( u1.flags & CHAT_USER_VOICE && !( u2.flags & CHAT_USER_VOICE ) ) + return true; + if( !( u1.flags & CHAT_USER_VOICE ) && u2.flags & CHAT_USER_VOICE ) + return false; + return ( _stricmp( (char*)u1.name, (char*)u2.name ) < 0 ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelLeave( HRESULT hRes, Channel*, User* pUser ) +{ + // Note: This is also called directly from OnUserKick(), below, when someone is kicked from a channel. + // Also now from OnLogout(). + +// debugprint( ">>> OnChannelLeave got %s: ", (char*)pUser->name ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelLeave when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring leave.\n" ); +// return S_OK; +// } + + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = false; + bRequestChannelLeaveWait = false; + } + else + { + // Remove user from our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelLeave - ignoring %s leave... \n", (char*)pUser->name ); + return S_OK; + } + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + User* pUserSearch = pUserList; + User* pUserPrevious = NULL; + bool bFound = false; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Remove from list. + if( !pUserPrevious ) + { + // Head of list is being removed. + pUserList = pUserSearch->next; + if( !pUserList ) + { + // This means all entries were removed. Can't happen, as you are still there. +// debugprint( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + Fatal( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + } + } + else + { + pUserPrevious->next = pUserSearch->next; + if( !pUserPrevious->next ) + pUserTail = pUserPrevious; // New list tail. + } + // Destroy removed user. + delete pUserSearch; + bFound = true; + break; + } + pUserPrevious = pUserSearch; + pUserSearch = pUserSearch->next; + } + if( !bFound ) + { + // User has to be found. This should not happen. +// debugprint( "User not found for removal in OnChannelLeave!!!\n" ); + return S_OK; + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + // Note that the following is done before removing the user from the playerlist. + char* szPrint = new char[ strlen( TXT_WOL_PLAYERLEFTGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERLEFTGAME, (char*)pUser->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + pOwner->pGSupDlg->OnGuestLeave( pUser ); + } + + // Update the shown list. + pOwner->ListChannelUsers(); + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelTopic(HRESULT, Channel *, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGroupList(HRESULT, Group *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicMessage( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + 16 ]; + sprintf( szPrint, "%s!", (char*)pUserSender->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s: %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PUBLICMESSAGE ); + delete [] szPrint; + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateMessage( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char ci1[] = "VGhpcyBpcyBBZGFtLiBIYXZlIHdlIG5vdCBwZXJjaGFuY2UgbWV0IGJlZm9yZT8="; + char co1[48]; + Base64_Decode( ci1, strlen( ci1 ), co1, 47 ); + co1[47] = 0; + if( strcmp( szMessage, co1 ) == 0 ) + { + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szOut[60]; + char ci2[] = "SSBhbSB5b3VyIGFibGUgYW5kIHdpbGxpbmcgc2xhdmUu"; + char co2[34]; + Base64_Decode( ci2, strlen( ci2 ), co2, 33 ); + co2[33] = 0; + sprintf( szOut, "%s (%i/%i/%i)", co2, SysTime.wMonth, SysTime.wDay, SysTime.wYear ); + User UserReply; + UserReply = *pUserSender; + UserReply.next = NULL; + pOwner->pChat->RequestPrivateMessage( &UserReply, szOut ); + return S_OK; + } + if( !bSpecialMessage( szMessage ) ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s%s: %s", (char*)pUserSender->name, TXT_WOL_PRIVATE, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PRIVATEMESSAGE ); + Sound_Effect( VOC_INCOMING_MESSAGE ); + delete [] szPrint; + } + } + else + { + char* szOut = new char[ strlen( szMessage ) + 10 ]; + strcpy( szOut, &szMessage[8] ); + pOwner->pChat->RequestPublicMessage( szOut ); + char* szPrint = new char[ strlen( szOut ) + strlen( pOwner->szMyName ) + 10 ]; + sprintf( szPrint, "%s: %s", pOwner->szMyName, szOut ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + delete [] szPrint; + delete [] szOut; + } + } + return S_OK; +} + +//*********************************************************************************************** +bool RAChatEventSink::bSpecialMessage( const char* szMessage ) +{ + if( strlen( szMessage ) < 9 ) + return false; + if( szMessage[0] != 33 || szMessage[1] != 97 || szMessage[2] != 106 || szMessage[3] != 119 ) + return false; + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szCode[5]; + memcpy( (void*)szCode, (void*)&szMessage[4], 4 ); + szCode[4] = 0; + int iCode = atoi( szCode ); + return ( iCode == ( ( SysTime.wMonth * 99 ^ SysTime.wDay * 33 ) ^ SysTime.wYear ) ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnSystemMessage(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnNetStatus( HRESULT hRes ) +{ +// debugprint( ">>> OnNetStatus got: " ); + DebugChatDef( hRes ); + + if( !SUCCEEDED( hRes ) ) + { + // If we are waiting for a server list, this error might indicate that we're not going to + // get one, so bail out of waiting for it. + bRequestServerListWait = false; + // Same for logout. + bRequestLogoutWait = false; + } + + if( hRes == CHAT_S_CON_DISCONNECTED ) + { + if( bRequestLogoutWait || !bConnected ) + { + // Ok. We are waiting to logout or already have. + bRequestLogoutWait = false; + } + else + { + // Uh oh. We got disconnected unexpectedly. + if( pOwner->bInGame ) + // Set flag for wolapi destruction if connection is lost during game. + pOwner->bConnectionDown = true; + else + { + if( !pOwner->bSelfDestruct ) + { + // Set flag for wolapi destruction. + WWMessageBox().Process( TXT_WOL_WOLAPIGONE ); + pOwner->bSelfDestruct = true; + } + } + } + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelList( HRESULT, Channel* pChannelListIn ) +{ + if( bIgnoreChannelLists ) // Response to channel lists has been temporarily turned off. + { +// debugprint( ">>> IGNORED OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + return S_OK; + } + + // Special case for modal GetLobbyChannels(). Because we want to be sure this OnChannelList is one that was caused + // by a Request for gametype 0, and not one arriving from an earlier Request for games. + // This OnChannelList might not actually match the Request in GetLobbyChannels(), but as long as it's type 0 it'll do. + if( bRequestChannelListForLobbiesWait ) + { + if( pChannelListIn && pChannelListIn->type != 0 ) + { +// debugprint( ">>> IGNORED OnChannelList, bRequestChannelListForLobbiesWait if\n" ); + return S_OK; + } + // Note: if no channels in list, can't tell what kind of Request call gave us this list. + // (In our case assume it was the one asking for lobbies and allow to fail later naturally due to no lobbies available.) + } + + DeleteChannelList(); +// debugprint( ">>> OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + + int iLobbyCur = iChannelLobbyNumber( (unsigned char*)pOwner->szChannelNameCurrent ); + + Channel* pChannelListTail = NULL; + + // Copy channel list to our own list. + while( pChannelListIn ) + { +// debugprint( "OnChannelList got %s\n", pChannelListIn->name ); + switch( ChannelFilter ) + { + case CHANNELFILTER_OFFICIAL: + if( pChannelListIn->official != 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_UNOFFICIAL: + if( pChannelListIn->official == 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_LOBBIES: + { + int iLobby = iChannelLobbyNumber( pChannelListIn->name ); + if( iLobby == -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + } + case CHANNELFILTER_LOCALLOBBYGAMES: + // We are listing games of our type, and may have to filter out non-local-lobby games. + if( !pOwner->bAllGamesShown ) + { + int iGameSourceLobby = pChannelListIn->reserved & 0x00FFFFFF; + if( iLobbyCur == -1 || iGameSourceLobby != iLobbyCur ) + { + pChannelListIn = pChannelListIn->next; + continue; + } + } + break; + } + Channel* pChannelNew = new Channel; + *pChannelNew = *pChannelListIn; + pChannelNew->next = NULL; // (We don't want the value that was just copied!) + if( !pChannelListTail ) + { + // First channel in list. + pChannelList = pChannelNew; // This is the head of our channel list. + pChannelListTail = pChannelNew; + } + else + { + pChannelListTail->next = pChannelNew; + pChannelListTail = pChannelNew; + } + pChannelListIn = pChannelListIn->next; + } + +// bRequestChannelListWait = false; + + if( bRequestChannelListForLobbiesWait ) + bRequestChannelListForLobbiesWait = false; + else + pOwner->OnChannelList(); + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteChannelList() +{ + // Delete all channels allocated on the heap. + // pChannelList points to the head element of a linked list of channels, copied during OnChannelList(). +// debugprint( "DeleteChannelList\n" ); + while( pChannelList ) + { + Channel* pChannelHead = pChannelList; + pChannelList = pChannelHead->next; + delete pChannelHead; + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserList(HRESULT, Channel*, User* pUserListIn ) +{ + // Maintenance of users list is like that for channels list. +// debugprint( ">>> OnUserList\n" ); + DeleteUserList(); + + // Copy channel list to our own list. + while( pUserListIn ) + { +// debugprint( "OnUserList got %s\n", pUserListIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserListIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserList = pUserNew; // This is the head of our User list. + pUserTail = pUserNew; + } + else + { + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + pUserListIn = pUserListIn->next; + } + +// bRequestUserListWait = false; + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserList() +{ + // Delete all Users allocated on the heap. + // pUserList points to the head element of a linked list of Users, copied during OnUserList(). +// debugprint( "DeleteUserList\n" ); + while( pUserList ) + { + User* pUserHead = pUserList; + pUserList = pUserHead->next; + delete pUserHead; + } + pUserTail = NULL; +} + + +//*********************************************************************************************** +// We got a list of updates to apply +// +STDMETHODIMP RAChatEventSink::OnUpdateList( HRESULT hRes, Update* pUpdateList ) +{ +// debugprint( ">>> OnUpdateList got: " ); + DebugChatDef( hRes ); + + if( !pUpdateList ) // Shouldn't happen. + return S_OK; + + // Count the updates. + int iUpdates = 0; + Update* pUpdate = pUpdateList; + + while( pUpdate != NULL ) + { + pUpdate = pUpdate->next; + ++iUpdates; + } +// debugprint( "%i updates\n", iUpdates ); + + if( WWMessageBox().Process( TXT_WOL_PATCHQUESTION, TXT_YES, TXT_NO ) == 0 ) + { + // Get the updates. (I ignore the concept of "optional" downloads here.) + if( DownloadUpdates( pUpdateList, iUpdates ) ) + pOwner->hresPatchResults = PATCHDOWNLOADED; + else + pOwner->hresPatchResults = PATCHAVOIDED; + } + else + // User says don't do the download. + // Set flag to tell WolapiObject what has happened. + pOwner->hresPatchResults = PATCHAVOIDED; + + return S_OK; +} + +extern bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ); +//*********************************************************************************************** +bool RAChatEventSink::DownloadUpdates( Update* pUpdateList, int iUpdates ) +{ + // First we create a Download and Download Sink interface object, like Chat and ChatSink. + bool bReturn = true; + // This is all like WolapiObject::bSetupCOMStuff(). +//debugprint( "Do all the COM crap.\n" ); + IDownload* pDownload; + CoCreateInstance( CLSID_Download, NULL, CLSCTX_INPROC_SERVER, IID_IDownload, (void**)&pDownload ); + _ASSERTE( pDownload ); + RADownloadEventSink* pDownloadSink = new RADownloadEventSink(); + pDownloadSink->AddRef(); + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + HRESULT hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + DWORD dwDownloadAdvise; + hRes = pConnectionPoint->Advise( (IDownloadEvent*)pDownloadSink, &dwDownloadAdvise ); + _ASSERTE(SUCCEEDED(hRes)); + // Presumably the above calls will succeed, because they did so when we did bSetupComStuff(). + + pContainer->Release(); + pConnectionPoint->Release(); + + Update* pUpdate = pUpdateList; + int iUpdateCurrent = 0; + // Save current directory. + char szCurDirSave[_MAX_PATH]; + ::GetCurrentDirectory( _MAX_PATH, szCurDirSave ); + while( pUpdate ) + { + ++iUpdateCurrent; + char szTitle[ 120 ]; + sprintf( szTitle, TXT_WOL_DOWNLOADING, iUpdateCurrent, iUpdates ); + char fullpath[ _MAX_PATH ]; + sprintf( fullpath, "%s\\%s", pUpdate->patchpath, pUpdate->patchfile ); + // Downloading in WOLAPI is in a state of disarray somewhat. + // Make sure the destination directory exists, and make it the current directory during the download. +//debugprint( "Switching to %s dir.\n", (char*)pUpdate->localpath ); + if( !::SetCurrentDirectory( (char*)pUpdate->localpath ) ) + { + // Create the destination directory. +// debugprint( "Creating dir.\n" ); + ::CreateDirectory( (char*)pUpdate->localpath, NULL ); + ::SetCurrentDirectory( (char*)pUpdate->localpath ); + } + // Note: Unknown what the reg key value is actually used for... +//debugprint( "Asking to download %s to %s. Server '%s', login '%s', password '%s'\n", fullpath, (char*)pUpdate->patchfile, +// (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password ); + pDownload->DownloadFile( (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password, fullpath, + (char*)pUpdate->patchfile, Game_Registry_Key() ); +// debugprint( "Call WOL_Download_Dialog()\n" ); + if( !WOL_Download_Dialog( pDownload, pDownloadSink, szTitle ) ) + { + bReturn = false; + break; + } + pUpdate = pUpdate->next; + } + ::SetCurrentDirectory( szCurDirSave ); + + // Undo all the COM crap. +//debugprint( "Undo all the COM crap.\n" ); + pConnectionPoint = NULL; + pContainer = NULL; + hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + pConnectionPoint->Unadvise( dwDownloadAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pDownload->Release(); + pDownloadSink->Release(); // This results in pDownloadSink deleting itself for us. + + return bReturn; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError( HRESULT hRes ) +{ +// debugprint( ">>> OnServerError got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnMessageOfTheDay(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::ActionEggSound( const char* szMessage ) +{ + // Easter egg related. + if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) ) + { + int i = rand() % 30; + if( i == 0 ) + Sound_Effect( VOC_DOG_HURT ); + else if( i == 1 ) + Sound_Effect( VOC_ANTDIE ); + else + Sound_Effect( (VocType)( VOC_SCREAM1 + rand() % 9 ) ); + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) + || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_WHINE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 6 ) + { + case 0: Sound_Effect( VOC_CANNON1 ); break; + case 1: Sound_Effect( VOC_CANNON2 ); break; + case 2: Sound_Effect( VOC_GUN_RIFLE ); break; + case 3: Sound_Effect( VOC_SILENCER ); break; + case 4: Sound_Effect( VOC_CANNON6 ); break; + case 5: Sound_Effect( VOC_CANNON8 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_KABOOM1 ); break; + case 1: Sound_Effect( VOC_KABOOM12 ); break; + case 2: Sound_Effect( VOC_KABOOM15 ); break; + case 3: Sound_Effect( VOC_KABOOM30 ); break; + case 4: Sound_Effect( VOC_KABOOM25 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 8 ) + { + case 0: Sound_Effect( VOC_E_AH ); break; + case 1: Sound_Effect( VOC_E_YES ); break; + case 2: Sound_Effect( VOC_THIEF_YEA ); break; + case 3: Sound_Effect( VOC_SPY_YESSIR ); break; + case 4: Sound_Effect( VOC_SPY_INDEED ); break; + case 5: Sound_Effect( VOC_ENG_YES ); break; + case 6: Sound_Effect( VOC_MED_YESSIR ); break; + case 7: Sound_Effect( VOC_MED_AFFIRM ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_E_OK ); + else if( strstr( szMessage, "<>" )|| strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_SPY_ONWAY ); break; + case 1: Sound_Effect( VOC_ENG_MOVEOUT ); break; + case 2: Sound_Effect( VOC_SPY_KING ); break; + case 3: Sound_Effect( VOC_MED_MOVEOUT ); break; + case 4: Sound_Effect( VOC_THIEF_MOVEOUT ); break; + } + } + else if( strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_SPLASH ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_POWER_UP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_ZAP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TORPEDO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_SUBSHOW ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_BARK ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_GROWL2 ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CHRONO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CRUMBLE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CASHTURN ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_HEAL ); + else if( strstr( szMessage, "<>" ) ) + { + switch( rand() % 3 ) + { + case 0: Sound_Effect( VOC_MISSILE_1 ); break; + case 1: Sound_Effect( VOC_MISSILE_2 ); break; + case 2: Sound_Effect( VOC_MISSILE_3 ); break; + } + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateAction( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATE, (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicAction( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateGameOptions( HRESULT, User* pUser, LPCSTR szRequest ) +{ +// debugprint( ">>> OnPrivateGameOptions\n" ); +// DebugChatDef( hRes ); + + char szRequestCopy[ 600 ]; + strcpy( szRequestCopy, szRequest ); + + if( pOwner->pGSupDlg ) + { + if( pOwner->pGSupDlg->bHost ) + pOwner->pGSupDlg->ProcessGuestRequest( pUser, szRequestCopy ); + else + pOwner->pGSupDlg->ProcessInform( szRequestCopy ); // Must be private message to guest from game host. + } +// else +// debugprint( "OnPrivateGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicGameOptions( HRESULT, Channel*, User*, LPCSTR szInform ) +{ +// debugprint( ">>> OnPublicGameOptions: %s\n", szInform ); + + char szInformCopy[ 600 ]; + strcpy( szInformCopy, szInform ); + + if( pOwner->pGSupDlg ) + { + pOwner->pGSupDlg->ProcessInform( szInformCopy ); + } +// else +// debugprint( "OnPublicGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGameStart( HRESULT hRes, Channel*, User* pUserIn, int iGameID ) +{ + // Note: All players receive this, not just the host that requested it. + +// debugprint( ">>> OnGameStart got: " ); + DebugChatDef( hRes ); + +// if( bRequestGameStartWait ) // Implies user is the host that did RequestGameStart(). +// { + + // Create the list of users that are actually involved in a game. + // Most likely will always match pUserList, but there is a chance of someone leaving or joining + // at the wrong moment, so from this point on, the pGameUserList is used. + + // Note: pUserIPList was added later, for pre-start pinging. It duplicates pGameUserList ip information, + // strictly speaking. + + // Delete any existing list. + while( pGameUserList ) + { + User* pGameUserHead = pGameUserList; + pGameUserList = pGameUserList->next; + delete pGameUserHead; + } + // Copy incoming user list. + User* pGameUserListTail = NULL; + while( pUserIn ) + { +// debugprint( "OnGameStart got %s\n", (char*)pUserIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pGameUserListTail ) + { + // First User in list. + pGameUserList = pUserNew; // This is the head of our User list. + pGameUserListTail = pUserNew; + } + else + { + pGameUserListTail->next = pUserNew; + pGameUserListTail = pUserNew; + } + pUserIn = pUserIn->next; + } + + bRequestGameStartWait = false; +// } + +// debugprint( "iGameID is %i\n", iGameID ); + this->iGameID = iGameID; + + return S_OK; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetPlayerGameIP( const char* szPlayerName ) const +{ + // Returns ipaddr value of player if found in pGameUserList, else 0. + User* pUser = pGameUserList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szPlayerName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserKick( HRESULT hRes, Channel*, User* pUserKicked, User* pUserKicker ) +{ +// debugprint( ">>> OnUserKick got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone was kicked. + // Fake a call to OnChannelLeave(), as the processing is identical. + OnChannelLeave( S_OK, NULL, pUserKicked ); + if( pUserKicked->flags & CHAT_USER_MYSELF ) + { + // Trigger a channel exit later on, when we have left this callback. + bGotKickedTrigger = true; + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( TXT_WOL_USERKICKEDYOU ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDYOU, (char*)pUserKicker->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + // Ensure that the bGotKickedTrigger is acted upon immediately... + pOwner->dwTimeNextWolapiPump = ::timeGetTime(); + } + else + { + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( (char*)pUserKicked->name ) + + strlen( TXT_WOL_USERKICKEDUSER ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDUSER, (char*)pUserKicker->name, (char*)pUserKicked->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + switch( rand() % 4 ) + { + case 0: + Sound_Effect( VOC_TANYA_CHEW ); + break; + case 1: + Sound_Effect( VOC_TANYA_LAUGH ); + break; + case 2: + Sound_Effect( VOC_TANYA_CHING ); + break; + case 3: + Sound_Effect( VOC_TANYA_KISS ); + break; + } + } + else + { + // You tried to kick someone, but the user wasn't found. + // Ignore. +// debugprint( "OnUserKick non S_OK value\n" ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserIP( HRESULT hRes, User* pUser ) +{ + // A list of users is kept, separate from other user lists, to preserve the ipaddr's we've found through this + // callback. OnUserList (for some dumb reason) doesn't hold valid ipaddr's, so we have to go through + // all this rigamarole... + // (List is cleared when entering game channel. Users are added initially and on joins, not removed on leaves.) +// debugprint( ">>> OnUserIP got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { + // Look for user in our current users list. + User* pUserSearch = pUserIPList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Found matching user. Replace it's ipaddr value, in case it changed.(?) + pUserSearch->ipaddr = pUser->ipaddr; + return S_OK; + } + pUserSearch = pUserSearch->next; + } + // User not found in current list. Add. + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserIPListTail ) + { + // First user in list. + pUserIPList = pUserNew; // This is the head of our list. + pUserIPListTail = pUserNew; + } + else + { + pUserIPListTail->next = pUserNew; + pUserIPListTail = pUserNew; + } + } + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserIPList() +{ + // Same as DeleteUserList but for pUserIPList. +// debugprint( "DeleteUserIPList\n" ); + while( pUserIPList ) + { + User* pUserHead = pUserIPList; + pUserIPList = pUserHead->next; + delete pUserHead; + } + pUserIPListTail = NULL; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetUserIP( const char* szName ) const +{ + // Looks in pUserIPList for the ipaddr of user with name szName. + // This is used only while in game channels. + // This is for step 2 in acquiring fellow player ping times. To get the IP addresses into pUserIPList + // we had to go through request/callbacks. Now pings are requested on these addresses, and the results + // tallied in NetUtilSink for our retrieval later. + // Returns 0 if not found. + + // Find szName in list. + User* pUser = pUserIPList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError(HRESULT , LPCSTR ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerBannedYou(HRESULT , time_t ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserFlags( HRESULT hRes, LPCSTR name, unsigned int flags, unsigned int ) +{ +// debugprint( ">>> OnUserFlags got: " ); + DebugChatDef( hRes ); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring OnUserFlags because game is starting.\n" ); // (Shouldn't ever happen.) + return S_OK; + } + } + + // Find user in our current users list. + User* pUserPrior = NULL; + User* pUserSearch = pUserList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, name ) == 0 ) + { + // Set user's flags to new value. + pUserSearch->flags = flags; + + // Remove user from userlist and reinsert appropriately sorted. + if( !pUserPrior ) + { + // User was head of list. + pUserList = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was also tail of list. + pUserTail = NULL; + else + pUserSearch->next = NULL; + } + else + { + // User was not head of list. + pUserPrior->next = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was tail of list. + pUserTail = pUserPrior; + else + pUserSearch->next = NULL; + } + InsertUserSorted( pUserSearch ); + + // Update shown list. + pOwner->ListChannelUsers(); + break; + } + pUserPrior = pUserSearch; + pUserSearch = pUserSearch->next; + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelBan( HRESULT , LPCSTR name, int banned ) +{ + if( banned && strcmp( name, "*" ) != 0 ) + { + char* szPrint = new char[ strlen( name ) + strlen( TXT_WOL_USERWASBANNED ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERWASBANNED, name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + + return S_OK; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RADownloadEventSink::RADownloadEventSink() : + bFlagEnd( false ), + bFlagError( false ), + bFlagProgressUpdate( false ), + bFlagStatusUpdate( false ), + bFlagQueryResume( false ) +{ + m_cRef=0; // Ref counter +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RADownloadEventSink::QueryInterface(const IID& iid, void** ppv) +{ + if ((iid == IID_IUnknown) ||(iid == IID_IDownloadEvent)) + { + *ppv = (IDownloadEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RADownloadEventSink::AddRef() +{ + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RADownloadEventSink::Release() +{ + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnEnd(void) +{ +// debugprint( ">>> OnEnd\n" ); + bFlagEnd = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnError( int /*iCode*/ ) +{ +// debugprint( ">>> OnError got: %i\n", iCode ); +//#define DOWNLOADEVENT_NOSUCHSERVER 1 +//#define DOWNLOADEVENT_COULDNOTCONNECT 2 +//#define DOWNLOADEVENT_LOGINFAILED 3 +//#define DOWNLOADEVENT_NOSUCHFILE 4 +//#define DOWNLOADEVENT_LOCALFILEOPENFAILED 5 +//#define DOWNLOADEVENT_TCPERROR 6 +//#define DOWNLOADEVENT_DISCONNECTERROR 7 + + bFlagError = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnProgressUpdate( int bytesread, int totalsize, int timetaken, int timeleft ) +{ +// debugprint( ">>> OnProgressUpdate\n" ); + bFlagProgressUpdate = true; + + iBytesRead = bytesread; + iTotalSize = totalsize; + iTimeTaken = timetaken; + iTimeLeft = timeleft; + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnStatusUpdate( int status ) +{ +// debugprint( ">>> OnStatusUpdate, status = %i\n", status ); + bFlagStatusUpdate = true; + + iStatus = status; + + return S_OK; +} + +//*********************************************************************************************** +// Just tell the FTP module to go ahead and resume +// +STDMETHODIMP RADownloadEventSink::OnQueryResume() +{ +// debugprint( ">>> OnQueryResume\n" ); + bFlagQueryResume = true; + + bResumed = true; + + return DOWNLOADEVENT_RESUME; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RANetUtilEventSink::RANetUtilEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + pOwner( pOwnerIn ), + pLadderList( NULL ), + pLadderTail( NULL ), + pLadderListAM( NULL ), + pLadderTailAM( NULL ) +{ +// debugprint( "RANetUtilEventSink constructor\n" ); +} + +//*********************************************************************************************** +RANetUtilEventSink::~RANetUtilEventSink() +{ +// debugprint( "RANetUtilEventSink destructor\n" ); + DeleteLadderList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RANetUtilEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RANetUtilEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_INetUtilEvent)) + { + *ppv = (INetUtilEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RANetUtilEventSink::AddRef() +{ +// debugprint( "RANetUtilEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RANetUtilEventSink::Release() +{ +// debugprint( "RANetUtilEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnGameresSent( HRESULT hRes ) +{ +// debugprint( ">>> OnGameresSent got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnLadderList( HRESULT hRes, Ladder* pLadderListIn, int /*totalCount*/, long /*timeStamp*/, int /*keyRung*/ ) +{ + // Maintenance of ladders list is like that for channels list above. + // DeleteLadderList(); -> This is done once, before a set of RequestLadderList() calls are made. +// debugprint( ">>> OnLadderList got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { +// debugprint( "(SUCCEEDED)\n" ); + // Copy ladder list to our own list. + while( pLadderListIn ) + { +// debugprint( "OnLadderList got %s, rung %u\n", pLadderListIn->login_name, pLadderListIn->rung ); + if( *pLadderListIn->login_name != 0 && pLadderListIn->rung != -1 ) + { + Ladder* pLadderNew = new Ladder; + *pLadderNew = *pLadderListIn; + pLadderNew->next = NULL; // (We don't want the value that was just copied!) + if( pLadderNew->sku == LADDER_CODE_RA ) + { + if( !pLadderTail ) + { + // First Ladder in list. + pLadderList = pLadderNew; // This is the head of our Ladder list. + pLadderTail = pLadderNew; + } + else + { + pLadderTail->next = pLadderNew; + pLadderTail = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string. + sprintf( pOwner->szMyRecord, TXT_WOL_PERSONALWINLOSSRECORD, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + else // sku must be LADDER_CODE_AM + { + if( !pLadderTailAM ) + { + // First Ladder in list. + pLadderListAM = pLadderNew; // This is the head of our Ladder list. + pLadderTailAM = pLadderNew; + } + else + { + pLadderTailAM->next = pLadderNew; + pLadderTailAM = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string for Aftermath. + sprintf( pOwner->szMyRecordAM, TXT_WOL_PERSONALWINLOSSRECORDAM, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + } + pLadderListIn = pLadderListIn->next; + } + // Update shown list. + pOwner->ListChannelUsers(); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnPing( HRESULT hRes, int time, unsigned long ip, int /*handle*/ ) +{ + if( pOwner->bDoingDisconnectPinging ) + { +// debugprint( ">>> OnPing got : ip %i, time %i, ", ip, time ); + DebugChatDef( hRes ); + + if( ip == pOwner->TournamentOpponentIP ) + { + // This is the result of the opponent ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Opponent\n", pOwner->iDisconnectPingCurrent ); + } + else + { + // This is the result of the game server ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Server\n", pOwner->iDisconnectPingCurrent ); + } + } + return S_OK; +} + +//*********************************************************************************************** +void RANetUtilEventSink::DeleteLadderList() +{ +// debugprint( "DeleteLadderList()\n" ); + // Delete all Ladders allocated on the heap. + while( pLadderList ) + { + Ladder* pLadderHead = pLadderList; + pLadderList = pLadderHead->next; + delete pLadderHead; + } + pLadderTail = NULL; +} + +//*********************************************************************************************** +unsigned int RANetUtilEventSink::GetUserRank( const char* szName, bool bRankRA ) +{ + // Searches for szName in ladder list, returns player rank if found, else 0. + // Slow linear search. + // If bRankRA, returns RA rank, else returns AM rank. +// debugprint( "GetUserRank: Asked for %s, ", szName ); + Ladder* pLad; + if( bRankRA ) + pLad = pLadderList; + else + pLad = pLadderListAM; + + while( pLad ) + { +// debugprint( " comparing %s\n", (char*)pLad->login_name ); + if( _stricmp( (char*)pLad->login_name, szName ) == 0 ) + { +// debugprint( "found rung value %u\n", pLad->rung ); + return pLad->rung; + } + pLad = pLad->next; + } +// debugprint( "couldn't find in my ladder list.\n", szName ); + + return 0; +} + +//*********************************************************************************************** +//*********************************************************************************************** +void ChatDefAsText( char* szDesc, HRESULT hRes ) +{ + // Sets szDesc to the text meaning of hRes. + // Make sure szDesc is as long as the longest of the below. + switch( hRes ) + { + case CHAT_E_NICKINUSE : + sprintf( szDesc, "Your nick is still logged into chat" ); + break; + case CHAT_E_BADPASS : + sprintf( szDesc, "Your password is incorrect during login" ); + break; + case CHAT_E_NONESUCH : + sprintf( szDesc, "Reference made to non-existant user or channel" ); + break; + case CHAT_E_CON_NETDOWN : + sprintf( szDesc, "The network layer is down or cannot be initialized for some reason" ); + break; + case CHAT_E_CON_LOOKUP_FAILED : + sprintf( szDesc, "Name lookup (e.g DNS) failed for some reason" ); + break; + case CHAT_E_CON_ERROR : + sprintf( szDesc, "Some fatal error occured with the net connection" ); + break; + case CHAT_E_TIMEOUT : + sprintf( szDesc, "General request timeout for a request" ); + break; + case CHAT_E_MUSTPATCH : + sprintf( szDesc, "Must patch before continuing" ); + break; + case CHAT_E_STATUSERROR : + sprintf( szDesc, "Miscellaneous internal status error" ); + break; + case CHAT_E_UNKNOWNRESPONSE : + sprintf( szDesc, "Server has returned something we don't recognise" ); + break; + case CHAT_E_CHANNELFULL : + sprintf( szDesc, "Tried to join a channel that has enough players already" ); + break; + case CHAT_E_CHANNELEXISTS : + sprintf( szDesc, "Tried to create a channel that already exists" ); + break; + case CHAT_E_CHANNELDOESNOTEXIST : + sprintf( szDesc, "Tried to join a channel that does not exist" ); + break; + case CHAT_E_BADCHANNELPASSWORD : + sprintf( szDesc, "Tried to join a channel with the wrong password" ); + break; + case CHAT_E_BANNED : + sprintf( szDesc, "You've been banned from the server / channel" ); + break; + case CHAT_E_NOT_OPER : + sprintf( szDesc, "You tried to do something that required operator status" ); + break; + + case CHAT_S_CON_CONNECTING : + sprintf( szDesc, "A network connection is underway" ); + break; + case CHAT_S_CON_CONNECTED : + sprintf( szDesc, "A network connection is complete" ); + break; + case CHAT_S_CON_DISCONNECTING : + sprintf( szDesc, "A network connection is going down" ); + break; + case CHAT_S_CON_DISCONNECTED : + sprintf( szDesc, "A network connection is closed" ); + break; + case CHAT_S_FIND_NOTHERE : + sprintf( szDesc, "Find - Nick not in system" ); + break; + case CHAT_S_FIND_NOCHAN : + sprintf( szDesc, "Find - Not in any channels" ); + break; + case CHAT_S_FIND_OFF : + sprintf( szDesc, "Find - user has find turned off" ); + break; + case CHAT_S_PAGE_NOTHERE : + sprintf( szDesc, "Page - Nick not in system" ); + break; + case CHAT_S_PAGE_OFF : + sprintf( szDesc, "Page - user has page turned off" ); + break; + case CHAT_E_NOTCONNECTED : + sprintf( szDesc, "You are not connected to the chat server" ); + break; + case CHAT_E_NOCHANNEL : + sprintf( szDesc, "You are not in a channel" ); + break; + case CHAT_E_NOTIMPLEMENTED : + sprintf( szDesc, "Feature is not implemented" ); + break; + case CHAT_E_PENDINGREQUEST : + sprintf( szDesc, "The request was made while while a conflicting request was still pending" ); + break; + case CHAT_E_PARAMERROR : + sprintf( szDesc, "Invalid parameter passed - usually a NULL pointer" ); + break; + case CHAT_E_LEAVECHANNEL : + sprintf( szDesc, "Tried to create or join a channel before leaving the previous one" ); + break; + case CHAT_E_JOINCHANNEL : + sprintf( szDesc, "Tried to send something to a channel when not a member of any channel" ); + break; + case CHAT_E_UNKNOWNCHANNEL : + sprintf( szDesc, "Tried to join a non-existant channel" ); + break; + case S_OK: + sprintf( szDesc, "S_OK" ); + break; + case E_FAIL: + sprintf( szDesc, "E_FAIL" ); + break; + default: + sprintf( szDesc, "ERROR - Value not recognized!" ); + break; + } + // Append NetUtil errors. + switch( hRes ) + { + case NETUTIL_E_ERROR: + strcat( szDesc, " NetUtil: NETUTIL_E_ERROR" ); + break; + case NETUTIL_E_BUSY: + strcat( szDesc, " NetUtil: NETUTIL_E_BUSY" ); + break; + case NETUTIL_S_FINISHED: + strcat( szDesc, " NetUtil: NETUTIL_S_FINISHED" ); + break; + } +} + +//*********************************************************************************************** +void DebugChatDef( HRESULT hRes ) +{ + char szText[200]; + ChatDefAsText( szText, hRes ); +// debugprint( "%s\n", szText ); +} + +//*********************************************************************************************** +int iChannelLobbyNumber( const unsigned char* szChannelName ) +{ + // Returns lobby number of channel, or -1 for "channel is not a lobby". + if( strncmp( (char*)szChannelName, LOB_PREFIX, strlen( LOB_PREFIX ) ) == 0 ) + { + char szNum[10]; + strcpy( szNum, (char*)szChannelName + strlen( LOB_PREFIX ) ); +// debugprint( " ^ iChannelLobbyNumber returning atoi of %s\n", szNum ); + return atoi( szNum ); + } + else + return -1; +} + +//*********************************************************************************************** +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ) +{ + // Hard-coded translation of lobby number to apparent lobby name. + switch( iLobby ) + { + case 0: + strcpy( szLobbyNameToSet, "Combat Alley" ); + break; + case 1: + strcpy( szLobbyNameToSet, "No Man's Land" ); + break; + case 2: + strcpy( szLobbyNameToSet, "Hell's Pass" ); + break; + case 3: + strcpy( szLobbyNameToSet, "Lost Vegas" ); + break; + case 4: + strcpy( szLobbyNameToSet, "Death Valley" ); + break; + case 5: + strcpy( szLobbyNameToSet, "The Wastelands" ); + break; + case 6: + strcpy( szLobbyNameToSet, "Isle of Fury" ); + break; + case 7: + strcpy( szLobbyNameToSet, "Armourgarden" ); + break; + case 8: + strcpy( szLobbyNameToSet, "The Hive" ); + break; + case 9: + strcpy( szLobbyNameToSet, "North by Northwest" ); + break; + case 10: + strcpy( szLobbyNameToSet, "Decatur High" ); + break; + case 11: + strcpy( szLobbyNameToSet, "Damnation Alley" ); + break; + default: + sprintf( szLobbyNameToSet, "%ith Division", iLobby ); + break; + } +} + +#endif diff --git a/CODE/RAWOLAPI.H b/CODE/RAWOLAPI.H new file mode 100644 index 0000000..ac3f174 --- /dev/null +++ b/CODE/RAWOLAPI.H @@ -0,0 +1,340 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +#ifndef WIN32 +#error WOLAPI_INTEGRATION can't be specified for non WIN32 version! +#endif + +// rawolapi.h - WOLAPI sinks declarations. +// ajw 07/10/98 + +// Based somewhat on Neal's Borlandized version, "chatapi.h". + +#ifndef RAWOLAPI_H +#define RAWOLAPI_H + +#include "function.h" + +//#include "cominit.h" +#include + +// From OBJBASE.H +#define interface struct + +// From RPCNDR.H +#define DECLSPEC_UUID(x) + +#include + +//namespace WOL // namespace is workaround due to the use of "Server" as a global in Red Alert. +// ajw - Can't use namespaces in Watcom 10.5 it seems... +//{ + #include "wolapi\wolapi.h" + #define IID_DEFINED + //#include "wlib/wdebug.h" + #include "wolapi\chatdefs.h" + #include "wolapi\downloaddefs.h" + #include "wolapi\ftpdefs.h" +//}; +//using namespace WOL; +#include +//#include +#include + +//*********************************************************************************************** +// For debugging chat defined hresults... +void ChatDefAsText( char* szDesc, HRESULT hRes ); +void DebugChatDef( HRESULT hRes ); + +int iChannelLobbyNumber( const unsigned char* szChannelName ); +#define REASONABLELOBBYINTERPRETEDNAMELEN 50 +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ); + +class WolapiObject; + +#define MAXCHATSENDLENGTH 71 // Mainly aesthetic, and because of the length of edit line. + +enum CHANNELFILTER +{ + CHANNELFILTER_NO, + CHANNELFILTER_OFFICIAL, + CHANNELFILTER_UNOFFICIAL, + CHANNELFILTER_LOBBIES, + CHANNELFILTER_LOCALLOBBYGAMES, +}; + +#define WOLCOLORREMAP_ACTION PCOLOR_GREY +#define WOLCOLORREMAP_SELFSPEAKING PCOLOR_RED +#define WOLCOLORREMAP_LOCALMACHINEMESS PCOLOR_REALLY_BLUE // Color of system messages that originate locally. +#define WOLCOLORREMAP_PAGE PCOLOR_GOLD +#define WOLCOLORREMAP_KICKORBAN PCOLOR_GREEN //LTBLUE +#define WOLCOLORREMAP_PUBLICMESSAGE PCOLOR_NONE +#define WOLCOLORREMAP_PRIVATEMESSAGE PCOLOR_ORANGE + +#define WOLSOUND_ERROR VOC_SYS_ERROR +#define WOLSOUND_LOGIN VOC_RADAR_ON +#define WOLSOUND_LOGOUT VOC_RADAR_OFF +#define WOLSOUND_ENTERCHAN VOC_PLAYER_JOINED +#define WOLSOUND_EXITCHAN VOC_PLAYER_LEFT +#define WOLSOUND_ONPAGE VOC_INCOMING_MESSAGE +#define WOLSOUND_KICKORBAN VOC_TANYA_KISS +#define WOLSOUND_ENTERGAME VOC_INVULNERABLE +#define WOLSOUND_EXITGAME VOC_DOOR + +enum DISCONNECT_PING_STATUS +{ + PING_UNSTARTED, + PING_WAITING, + PING_GOOD, + PING_BAD, +}; +#define DISCONNECT_PING_COUNT 5 + +//*********************************************************************************************** +class RAChatEventSink : /////public CComObjectRoot, /////public IConnectionPoint, + public IChatEvent +{ + public: + RAChatEventSink( WolapiObject* pOwner ); + virtual ~RAChatEventSink(); + +// BEGIN_COM_MAP(RAChatEventSink) +// COM_INTERFACE_ENTRY(IChatEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IChatEvent + STDMETHOD(OnServerList)(HRESULT res, Server* servers); + STDMETHOD(OnLogout)(HRESULT r, User *user); + STDMETHOD(OnBusy)(HRESULT r); + STDMETHOD(OnIdle)(HRESULT r); + STDMETHOD(OnPageSend)(HRESULT r); + STDMETHOD(OnPaged)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnFind)(HRESULT r, Channel *); + STDMETHOD(OnConnection)(HRESULT r,LPCSTR motd); + STDMETHOD(OnChannelCreate)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelModify)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelJoin)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelLeave)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelTopic)(HRESULT r, Channel *channel, LPCSTR topic); + STDMETHOD(OnGroupList)(HRESULT r, Group *); + STDMETHOD(OnPublicMessage)(HRESULT r, Channel *channel, User *user, LPCSTR text); + STDMETHOD(OnPrivateMessage)(HRESULT r, User *user,LPCSTR text); + STDMETHOD(OnSystemMessage)(HRESULT r, LPCSTR); + STDMETHOD(OnNetStatus)(HRESULT r); + STDMETHOD(OnChannelList)(HRESULT r, Channel* channels); + STDMETHOD(OnUserList)(HRESULT r, Channel* channel, User* users); + STDMETHOD(OnUpdateList)(HRESULT res, Update *); + STDMETHOD(OnServerError)(HRESULT res); + STDMETHOD(OnMessageOfTheDay)(HRESULT res, LPCSTR); + STDMETHOD(OnPrivateAction)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicAction)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnPrivateGameOptions)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicGameOptions)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnGameStart)(HRESULT r, Channel *, User *, int); + STDMETHOD(OnUserKick)(HRESULT r, Channel *, User *, User *); + STDMETHOD(OnUserIP)(HRESULT r, User *); + STDMETHOD(OnServerError)(HRESULT res, LPCSTR ircmsg); + STDMETHOD(OnServerBannedYou)(HRESULT r, time_t bannedTill); + STDMETHOD(OnUserFlags)(HRESULT r, LPCSTR name, unsigned int flags, unsigned int mask); + STDMETHOD(OnChannelBan)(HRESULT r, LPCSTR name, int banned); + + unsigned long GetPlayerGameIP( const char* szPlayerName ) const; + void DeleteUserList(); // Deletes from heap all users pointed to through pUserList. + void DeleteUserIPList(); + unsigned long GetUserIP( const char* szName ) const; + + void ActionEggSound( const char* szMessage ); + +public: + // These vars are rather hackish. Basically, they are set before a callback is expected to be fired, and + // then checked immediately afterwards. The rest of the time, their values are meaningless. + // The idea is to force wolapi act in a modal way. In many places I "block" until a callback response to a + // wolapi request has been received. + bool bRequestServerListWait; + bool bRequestConnectionWait; + bool bRequestLogoutWait; +// bool bRequestChannelListWait; + bool bRequestChannelJoinWait; + bool bRequestChannelLeaveWait; + bool bRequestUserListWait; + bool bRequestChannelCreateWait; + bool bRequestFindWait; + bool bRequestPageWait; + + bool bRequestChannelListForLobbiesWait; + + bool bIgnoreChannelLists; // Used to temporarily turn off response to channel lists, when we are in the midst + // of some processing that depends on pChannelList remaining constant. + + bool bRequestGameStartWait; + + Server* pServer; // Server to connect to, acquired from OnServerList. + bool bConnected; // True when user is logged in to chat server. + bool bJoined; // True when user has joined a channel. + + Channel* pChannelList; // First element of channel list, or null. + CHANNELFILTER ChannelFilter; // Affects what channels are included in channel list when built. + + User* pUserList; // First element of user list, or null. + User* pUserTail; // Last element of user list, or null. + + char* szMotd; // Message of the day. + HRESULT hresRequestConnectionError; // Used to pass error hresult. + + HRESULT hresRequestFindResult; // Used to pass hresult. + Channel OnFindChannel; + + HRESULT hresRequestPageResult; // Used to pass hresult. + + HRESULT hresRequestJoinResult; // Used to pass hresult. + + bool bGotKickedTrigger; // Special flag meaning do some more processing after callback has exited. + + User* pGameUserList; // First element of start game user list, or null. + int iGameID; // WW Online game id received from OnGameStart. + // Is also a flag indicating "OnGameStart() called, TriggerGameStart() not yet called". + + User* pUserIPList; // List that holds user IP's, used for pinging in game channel. + User* pUserIPListTail; + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + + void DeleteChannelList(); // Deletes from heap all channels pointed to through pChannelList. + bool DownloadUpdates( Update* pUpdateList, int iUpdates ); + bool bSpecialMessage( const char* szMessage ); + void InsertUserSorted( User* pUserNew ); + +private: + long m_cRef; // Reference Count + +}; + +//*********************************************************************************************** +class RADownloadEventSink : +/////////// public CComObjectRoot, + public IDownloadEvent +{ +public: + RADownloadEventSink(); + virtual ~RADownloadEventSink() {}; + +// BEGIN_COM_MAP(RADownloadEventSink) +// COM_INTERFACE_ENTRY(IDownloadEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IDownloadEvent + STDMETHOD(OnEnd)(void); + STDMETHOD(OnError)(int error); + STDMETHOD(OnProgressUpdate)(int bytesread, int totalsize, int timetaken, int timeleft); + STDMETHOD(OnStatusUpdate)(int status); + STDMETHOD(OnQueryResume)(void); + +public: + bool bFlagEnd; + bool bFlagError; + bool bFlagProgressUpdate; + bool bFlagStatusUpdate; + bool bFlagQueryResume; + int iBytesRead; + int iTotalSize; + int iTimeTaken; + int iTimeLeft; + int iStatus; + bool bResumed; + +private: + long m_cRef; // Ref count +}; + +//*********************************************************************************************** +class RANetUtilEventSink : +// public CComObjectRoot, + public INetUtilEvent +{ +public: + RANetUtilEventSink( WolapiObject* pOwner ); + virtual ~RANetUtilEventSink(); + +//BEGIN_COM_MAP(CNetUtilEventSink) +// COM_INTERFACE_ENTRY(INetUtilEvent) +//END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // INetUtilEvent + + STDMETHOD(OnGameresSent)(HRESULT res); + STDMETHOD(OnLadderList)(HRESULT res, Ladder *list, int totalCount, long timeStamp, int keyRung); + STDMETHOD(OnPing)(HRESULT res, int time, unsigned long ip, int handle); + + + void DeleteLadderList(); // Deletes from heap all users pointed to through pUserList. + unsigned int GetUserRank( const char* szName, bool bRankRA ); + + Ladder* pLadderList; // First element of Ladder list, or null. + Ladder* pLadderTail; // Last element of Ladder list, or null. + Ladder* pLadderListAM; // First element of Aftermath Ladder list, or null. + Ladder* pLadderTailAM; // Last element of Aftermath Ladder list, or null. + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + +private: + long m_cRef; // Reference Count +}; + +//*********************************************************************************************** + +// SKU, reported to WOLAPI for the purpose of finding patches. +#ifdef ENGLISH +#define GAME_SKU 0x1500 +#else +#ifdef GERMAN +#define GAME_SKU 0x1502 +#else +#define GAME_SKU 0x1503 +#endif +#endif + +#define GAME_VERSION 0x00030003 +#define GAME_TYPE 21 +#define LOB_PREFIX "Lob_21_" + +// Sent to gameres server in order to receive Red Alert or Aftermath ladder rankings. (Sent in RequestLadderList.) +#define LADDER_CODE_RA 1005 +#define LADDER_CODE_AM 500 + +#endif + +#endif diff --git a/CODE/READLINE.CPP b/CODE/READLINE.CPP new file mode 100644 index 0000000..d566f4f --- /dev/null +++ b/CODE/READLINE.CPP @@ -0,0 +1,89 @@ +/* +** 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 . +*/ + + +#include +#include +#include "wwfile.h" +#include "xstraw.h" +#include "readline.h" + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + + +void strtrim(char * buffer) +{ + if (buffer) { + + /* + ** Strip leading white space from the string. + */ + char * source = buffer; + while (isspace(*source)) { + source++; + } + if (source != buffer) { + strcpy(buffer, source); + } + + /* + ** Clip trailing white space from the string. + */ + for (int index = strlen(buffer)-1; index >= 0; index--) { + if (isspace(buffer[index])) { + buffer[index] = '\0'; + } else { + break; + } + } + } +} + + + +int Read_Line(FileClass & file, char * buffer, int len, bool & eof) +{ + return(Read_Line(FileStraw(file), buffer, len, eof)); +} + + +int Read_Line(Straw & file, char * buffer, int len, bool & eof) +{ + if (len == 0 || buffer == NULL) return(0); + + int count = 0; + for (;;) { + char c; + if (file.Get(&c, sizeof(c)) != sizeof(c)) { + eof = true; + buffer[0] = '\0'; + break; + } + + if (c == '\x0A') break; + if (c != '\x0D' && count+1 < len) { + buffer[count++] = c; + } + } + buffer[count] = '\0'; + + strtrim(buffer); + return(strlen(buffer)); +} diff --git a/CODE/READLINE.H b/CODE/READLINE.H new file mode 100644 index 0000000..64fae6b --- /dev/null +++ b/CODE/READLINE.H @@ -0,0 +1,26 @@ +/* +** 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 . +*/ + + +#include "wwfile.h" +#include "straw.h" + +void strtrim(char * buffer); +int Read_Line(FileClass & file, char * buffer, int len, bool & eof); +int Read_Line(Straw & file, char * buffer, int len, bool & eof); + diff --git a/CODE/RECT.CPP b/CODE/RECT.CPP new file mode 100644 index 0000000..96a3fdd --- /dev/null +++ b/CODE/RECT.CPP @@ -0,0 +1,200 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/22/96 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Rect::Rect -- Constructs a rectangle object. * + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * Rect::Intersect -- Find the intersection between two rectangles. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "rect.h" + + +/*********************************************************************************************** + * Rect::Rect -- Constructs a rectangle object. * + * * + * This will construct a rectangle object according to the parameters specified. * + * * + * INPUT: x,y -- The X and Y values of the upper left corner of the rectangle. * + * * + * w,h -- The width and height values of the rectangle. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect::Rect(int x, int y, int w, int h) : + X(x), + Y(y), + Width(w), + Height(h) +{ +} + + +/*********************************************************************************************** + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * * + * An invalid rectangle has values that do not make any sense. This is a useful state since * + * this can be used to determine if a rectangle has been initialized correctly or for * + * detecting an error return condition for rectangle manipulation routines. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this rectangle appear valid? * + * * + * WARNINGS: An invalid rectangle is one that has a width or height of less than one. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +bool Rect::Is_Valid(void) const +{ + return(Width > 0 && Height > 0); +} + + +/*********************************************************************************************** + * Rect::Intersect -- Find the intersection between two rectangles. * + * * + * This routine will take the specified rectangle and use it like a "cookie cutter" on this * + * rectangle. The intersection of these two rectangles is returned. An optional X and * + * Y parameter is supplied so that an absolute coordinate can be maintained to the new * + * rectangle. * + * * + * INPUT: rectangle -- Reference to the rectangle to use as a cookie cutter. * + * * + * x,y -- Optional pointer to a coordinate that will be adjusted to stay * + * in an absolute position in coordinates even though it is a * + * relative offset. * + * * + * OUTPUT: Returns with the rectangle that is the intersection of the one specified and * + * this rectangle. * + * * + * WARNINGS: The rectangle returned may be invalid. This can occur if there is no legal * + * intersection between the rectangles. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Rect::Intersect(Rect const & rectangle, int * x, int * y) const +{ + Rect rect(0, 0, 0, 0); // Dummy (illegal) rectangle. + Rect r = rectangle; // Working rectangle. + + /* + ** Both rectangles must be valid or else no intersection can occur. In such + ** a case, return an illegal rectangle. + */ + if (!Is_Valid() || !rectangle.Is_Valid()) return(rect); + + /* + ** The rectangle spills past the left edge. + */ + if (r.X < X) { + r.Width -= X - r.X; + r.X = X; + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past top edge. + */ + if (r.Y < Y) { + r.Height -= Y - r.Y; + r.Y = Y; + } + if (r.Height < 1) return(rect); + + /* + ** The rectangle spills past the right edge. + */ + if (r.X + r.Width > X + Width) { + r.Width -= (r.X + r.Width) - (X + Width); + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past the bottom edge. + */ + if (r.Y + r.Height > Y + Height) { + r.Height -= (r.Y + r.Height) - (Y + Height); + } + if (r.Height < 1) return(rect); + + /* + ** Adjust Height relative draw position according to Height new rectangle + ** union. + */ + if (x != NULL) { + *x -= (r.X-X); + } + if (y != NULL) { + *y -= (r.Y-Y); + } + + return(r); +} + + +Rect const Union(Rect const & rect1, Rect const & rect2) +{ + if (rect1.Is_Valid()) { + if (rect2.Is_Valid()) { + Rect result = rect1; + + if (result.X > rect2.X) { + result.Width += result.X-rect2.X; + result.X = rect2.X; + } + if (result.Y > rect2.Y) { + result.Height += result.Y-rect2.Y; + result.Y = rect2.Y; + } + if (result.X+result.Width < rect2.X+rect2.Width) { + result.Width = ((rect2.X+rect2.Width)-result.X)+1; + } + if (result.Y+result.Height < rect2.Y+rect2.Height) { + result.Height = ((rect2.Y+rect2.Height)-result.Y)+1; + } + return(result); + } + return(rect1); + } + return(rect2); +} diff --git a/CODE/RECT.H b/CODE/RECT.H new file mode 100644 index 0000000..5200b72 --- /dev/null +++ b/CODE/RECT.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/21/96 * + * * + * Last Update : July 21, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RECT_H +#define RECT_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include + +class Rect +{ + public: + Rect(int x=0, int y=0, int w=0, int h=0); + + Rect const Intersect(Rect const & rectangle, int * x=NULL, int * y=NULL) const; + friend Rect const Union(Rect const & rect1, Rect const & rect2); + + bool Is_Valid(void) const; + int Size(void) const {return(Width*Height);} + +// private: + int X; + int Y; + int Width; + int Height; +}; + + +#endif + diff --git a/CODE/REDALERT.ICO b/CODE/REDALERT.ICO new file mode 100644 index 0000000..5950f05 Binary files /dev/null and b/CODE/REDALERT.ICO differ diff --git a/CODE/REDALERT.IDE b/CODE/REDALERT.IDE new file mode 100644 index 0000000..a6ba4f1 Binary files /dev/null and b/CODE/REDALERT.IDE differ diff --git a/CODE/REDALERT_NO256.ICO b/CODE/REDALERT_NO256.ICO new file mode 100644 index 0000000..f516d7d Binary files /dev/null and b/CODE/REDALERT_NO256.ICO differ diff --git a/CODE/REGION.H b/CODE/REGION.H new file mode 100644 index 0000000..0eaf613 --- /dev/null +++ b/CODE/REGION.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/REGION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : REGION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/09/95 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef REGION_H +#define REGION_H + + +class RegionClass { + public: + RegionClass(void) {Threat = 0;}; + ~RegionClass(void) {}; + int operator != (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass));}; + int operator == (RegionClass const & region) {return !memcmp(this, ®ion, sizeof(RegionClass));}; + int operator > (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) > 0;}; + int operator < (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) < 0;}; + + void Reset_Threat(void) {Threat = 0;}; + void Adjust_Threat(int threat, int neg) {if (neg) Threat -= threat; else Threat+= threat;}; + int Threat_Value(void) const {return Threat;}; + + protected: + long Threat; +}; + +#endif diff --git a/CODE/REINF.CPP b/CODE/REINF.CPP new file mode 100644 index 0000000..b5ffa8f --- /dev/null +++ b/CODE/REINF.CPP @@ -0,0 +1,749 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/REINF.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : REINF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 24, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * Do_Reinforcements -- Create and place a reinforcement team. * + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * _Create_Group -- Create a group given team specification. * + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * * + * This routine will cause the group to pop out of the object specified. * + * * + * INPUT: group -- Pointer to the first object in the group to be popped out. * + * * + * object -- Pointer to the object that the group is to pop out of. * + * * + * OUTPUT: bool; Was the group popped out of the specified object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Pop_Group_Out_Of_Object(FootClass * group, TechnoClass * object) +{ + assert(group != NULL && object != NULL); + int quantity = 0; + + /* + ** Take every infantry member of this group and detach it from the group list + ** and then make it pop out of the candidate source. + */ + while (group != NULL) { + TechnoClass * todo = group; + group = (FootClass *)(ObjectClass *)group->Next; + todo->Next = NULL; + + switch (object->What_Am_I()) { + + /* + ** The infantry just walks out of a building. + */ + case RTTI_BUILDING: + if (object->Exit_Object(todo) != 2) { + delete todo; + } else { + ++quantity; + } + break; + + /* + ** Infantry get attached to transport vehicles and then unload. + */ + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + object->Attach((FootClass *)todo); + object->Assign_Mission(MISSION_UNLOAD); + ++quantity; + break; + + default: + delete todo; + break; + } + } + + return (quantity != 0); +} + + +/*********************************************************************************************** + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * * + * The unit is examined and if the owning house needs to confiscate it, then this routine * + * will return TRUE. In other cases, the unit should be left to its own devices. * + * * + * INPUT: unit -- Pointer to the object to examine. * + * * + * OUTPUT: bool; Should the object be confiscated by the player so that it becomes one of * + * his normal game objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool _Need_To_Take(AircraftClass const * air) +{ + if (*air == AIRCRAFT_YAK || *air == AIRCRAFT_MIG) { + int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP); +// int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP) - (air->House->Get_Quantity(AIRCRAFT_YAK)+air->House->Get_Quantity(AIRCRAFT_MIG)); + + /* + ** Loop through all aircraft and subtract all the ones that are NOT loaners. + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * airptr = Aircraft.Ptr(index); + if ((*airptr == AIRCRAFT_YAK || *airptr == AIRCRAFT_MIG) && airptr->IsOwnedByPlayer && !airptr->IsALoaner && airptr != air) { + deficit -= 1; + if (deficit == 0) break; + } + } + + if (deficit > 0) return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Create_Group -- Create a group given team specification. * + * * + * This routine will create all members of the group as specified by the team type. * + * * + * INPUT: teamtype -- Pointer to the team type that specifies what objects should be * + * created in this group. * + * * + * OUTPUT: Returns with a pointer to the first member of the group created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static FootClass * _Create_Group(TeamTypeClass const * teamtype) +{ + assert(teamtype != NULL); + + TeamClass * team = new TeamClass(teamtype); + if (team != NULL) { + team->Force_Active(); + } + + bool hasunload = false; + for (int tm = 0; tm < teamtype->MissionCount; tm++) { + if (teamtype->MissionList[tm].Mission == TMISSION_UNLOAD) { + hasunload = true; + break; + } + } + + /* + ** Now that the official source for the reinforcement has been determined, the + ** objects themselves must be created. + */ + FootClass * transport = NULL; + FootClass * object = NULL; + for (int index = 0; index < teamtype->ClassCount; index++) { + TechnoTypeClass const * tclass = teamtype->Members[index].Class; + + for (int sub = 0; sub < teamtype->Members[index].Quantity; sub++) { + ScenarioInit++; + FootClass * temp = (FootClass *)tclass->Create_One_Of(HouseClass::As_Pointer(teamtype->House)); + ScenarioInit--; + + if (temp != NULL) { + + /* + ** Add the member to the team. + */ + if (team != NULL) { + ScenarioInit++; + bool ok = team->Add(temp); +//Mono_Printf("Added to team = %d.\n", ok);Keyboard->Get(); + + ScenarioInit--; + temp->IsInitiated = true; + } + + if (temp->What_Am_I() == RTTI_AIRCRAFT && !_Need_To_Take((AircraftClass const *)temp)) { + temp->IsALoaner = true; + } + + /* + ** Build the list of transporters and passengers. + */ + if (tclass->Max_Passengers() > 0) { + + /* + ** Link to the list of transports. + */ + temp->Next = transport; + transport = temp; + + } else { + + /* + ** Link to the list of normal objects. + */ + temp->Next = object; + object = temp; + } + } + } + } + + /* + ** If the group consists of transports and normal objects, then assign the normal + ** objects to be passengers on the transport. + */ + if (transport != NULL && object != NULL) { + transport->Attach(object); + + /* + ** HACK ALERT! If the this team has an unload mission, then flag the transport + ** as a loaner so that it will exit from the map when the unload process is + ** complete, but only if the transport is an aircraft type. + */ + if (hasunload && (transport->What_Am_I() == RTTI_AIRCRAFT || transport->What_Am_I() == RTTI_VESSEL)) { + transport->IsALoaner = true; + } + } + + /* + ** For JUST transport helicopters, consider the loaner a gift if there are + ** no passengers. + */ + if (transport != NULL && object == NULL && transport->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)transport) == AIRCRAFT_TRANSPORT) { + transport->IsALoaner = false; + } + + if (transport == 0 && object == 0) { + if (team != NULL) delete team; + return(NULL); + } + + /* + ** If this group consists only of non-transport object, then just return with a pointer + ** to the first member of the group. + */ + if (transport == NULL) { + return(object); + } + + return(transport); +} + + +/*********************************************************************************************** + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * * + * Use this to determine if the specified group only contains infantry. Such a reinforcement* + * group is a candidate for popping out of a building or transport vehicle rather than * + * driving/walking/sailing/flying onto the map under its own power. * + * * + * INPUT: first -- Pointer to the first object in the group to examine. * + * * + * OUTPUT: bool; Is the entire group composed of infantry type units? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Consists_Only_Of_Infantry(FootClass const * first) +{ + while (first != NULL) { + if (first->What_Am_I() != RTTI_INFANTRY) { + return(false); + } + first = (FootClass const *)((ObjectClass *)first->Next); + } + return(true); +} + + +/*********************************************************************************************** + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * * + * This routine is used to scan nearby locations to determine if there is a suitable host * + * for these reinforcements to "pop out of" (apologies to Aliens). Typical hosts include * + * buildings and transport vehicles (of any kind). * + * * + * INPUT: origin -- The cell that should be scanned from. Only this location and immediate * + * adjacent locations will be scanned. * + * * + * OUTPUT: Returns with a pointer to a suitable host. If none could be found then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static TechnoClass * _Who_Can_Pop_Out_Of(CELL origin) +{ + CellClass * cellptr = &Map[origin]; + TechnoClass * candidate = NULL; + + for (int f = -1; f < 8; f++) { + CellClass * ptr = cellptr; + if (f != -1) { + ptr = &ptr->Adjacent_Cell(FacingType(f)); + } + + BuildingClass * building = ptr->Cell_Building(); + if (building && building->Strength > 0) { + candidate = building; + } + + UnitClass * unit = ptr->Cell_Unit(); + if (unit && unit->Strength && unit->Class->Max_Passengers() > 0) { + return(unit); + } + } + return(candidate); +} + + +/*********************************************************************************************** + * Do_Reinforcements -- Create and place a reinforcement team. * + * * + * This routine is called when a reinforcement team must be created and placed on the map. * + * It will create all members of the team and place them at the location determined from * + * the team composition. The reinforcement team should follow team orders until overridden * + * by AI or player intervention. * + * * + * INPUT: teamtype -- Pointer to the team type to create as a reinforcement. * + * * + * OUTPUT: Was the reinforcement successfully created and placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/18/1995 JLB : Returns success or failure condition. * + * 06/19/1995 JLB : Announces reinforcements. * + * 02/15/1996 JLB : Recognizes team reinforcement location. * + *=============================================================================================*/ +bool Do_Reinforcements(TeamTypeClass const * teamtype) +{ + assert(teamtype != 0); + + /* + ** perform some preliminary checks for validity. + */ + if (!teamtype || !teamtype->ClassCount) return(false); + + /* + ** HACK ALERT! + ** Give this team an attack waypoint mission that will attack the waypoint location of this + ** team if there are no team missions previously assigned. + */ + if (teamtype->MissionCount == 0) { + TeamTypeClass * tt = (TeamTypeClass *)teamtype; + tt->MissionCount = 1; + tt->MissionList[0].Mission = TMISSION_ATT_WAYPT; + tt->MissionList[0].Data.Value = teamtype->Origin; + } + + FootClass * object = _Create_Group(teamtype); + + +//Mono_Printf("%d-%s (object=%p, team=%d).\n", __LINE__, __FILE__, object, object->Team.Is_Valid());Keyboard->Get(); + + + /* + ** Bail on this reinforcement if no reinforcements could be created. + ** This is probably because the object maximum was reached. + */ + if (!object) { + return(false); + } + + /* + ** Special case code to handle infantry types that run from a building. This presumes + ** that infantry are never a transport (which is safe to do). + */ + if (object != NULL && teamtype->Origin != -1 && _Consists_Only_Of_Infantry(object)) { + + /* + ** Search for an object that these infantry can pop out of. + */ + TechnoClass * candidate = _Who_Can_Pop_Out_Of(Scen.Waypoint[teamtype->Origin]); + + if (candidate != NULL) { + return(_Pop_Group_Out_Of_Object(object, candidate)); + } + } + + /* + ** The reinforcements must be delivered the old fashioned way -- by moving onto the + ** map using their own power. First order of business is to determine where they + ** should arrive from. + */ + SourceType source = HouseClass::As_Pointer(teamtype->House)->Control.Edge; + if (source == SOURCE_NONE) { + source = SOURCE_NORTH; + } + + /* + ** Pick the location where the reinforcements appear and then place + ** them there. + */ + bool placed = false; + + FacingType eface = (FacingType)(source << 1); // Facing to enter map. + + CELL cell = Map.Calculated_Cell(source, teamtype->Origin, -1, object->Techno_Type_Class()->Speed); +#ifdef FIXIT_ANTS + /* + ** For the ants, they will pop out of the ant hill directly. + */ + if (teamtype->Origin != -1 && object->What_Am_I() == RTTI_UNIT && + (*((UnitClass*)object) == UNIT_ANT1 || + *((UnitClass*)object) == UNIT_ANT2 || + *((UnitClass*)object) == UNIT_ANT3)) { + CELL newcell = Scen.Waypoint[teamtype->Origin]; + if (newcell != -1) { + if (Map[newcell].TType == TEMPLATE_HILL01) { + cell = newcell; + } + } + } +#endif + + CELL newcell = cell; + + FootClass * o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + bool okvoice = false; + while (newcell > 0 && object != NULL) { + DirType desiredfacing = Facing_Dir(eface); + if (object->What_Am_I() == RTTI_AIRCRAFT) { + desiredfacing = Random_Pick(DIR_N, DIR_MAX); + } + + ScenarioInit++; + if (object->Unlimbo(Cell_Coord(newcell), desiredfacing)) { + okvoice = true; + + /* + ** If this object is part of a team, then the mission for this + ** object will be guard. The team handler will assign the proper + ** mission that it should follow. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT) { + object->Assign_Mission(MISSION_GUARD); + object->Commence(); + } + + } else { + + /* + ** Could not unlimbo at location specified so find an adjacent location that it can + ** be unlimboed at. If this fails, then abort the whole placement process. + */ + for (FacingType adj = FACING_N; adj < FACING_COUNT; adj++) { + CELL trycell = Adjacent_Cell(newcell, adj); + if (!Map.In_Radar(trycell) && object->Can_Enter_Cell(trycell, adj) == MOVE_OK) { + newcell = trycell; + break; + } + } + if (adj < FACING_COUNT) continue; + newcell = -1; + } + ScenarioInit--; + + object = o; + if (object != NULL) { + o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + } + } + + /* + ** If there are still objects that could not be placed, then delete them. + */ + if (o != NULL) { + while (o != NULL) { + FootClass * old = o; + o = (FootClass *)(ObjectClass *)o->Next; + old->Next = 0; + + delete old; + } + } + + /* + ** Announce when the reinforcements have arrived. + */ + if (okvoice && teamtype->House == PlayerPtr->Class->House) { + Speak(VOX_REINFORCEMENTS); + } + + return(true); +} + + +/*********************************************************************************************** + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * * + * Use this routine to bring on a reinforcement that hasn't been anticipated by the trigger * + * system. An example of this would be replacement harvesters or airfield ordered units. * + * The appropriate transport is created (if necessary) and a mission is assigned such that * + * the object will legally bring itself onto the playing field. * + * * + * INPUT: house -- The owner of this reinforcement. * + * * + * type -- The object to bring on. * + * * + * another -- This is reserved for the transport class in those cases where the * + * transport MUST be forced to a specific type. * + * * + * mission -- The mission to assign this reinforcement team. * + * * + * argument -- Optional team mission argument (usually a waypoint). * + * * + * OUTPUT: Was the special reinforcement created without error? * + * * + * WARNINGS: This routine will fail if a team type cannot be created. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission, int argument) +{ + assert(house != 0); + assert(type != 0); + + if (house && type) { + TeamTypeClass * team = new TeamTypeClass(); + + if (team) { + + /* + ** If there is no overridden mission assign to this special reinforcement, then + ** we must assign something. If not, the reinforcement will just sit at the edge + ** of the map. + */ + if (!another && mission == TMISSION_NONE) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(house->Control.Edge); + } + + /* + ** Fill in the team characteristics. + */ + strcpy((char *)&team->IniName[0], "TEMP"); + team->IsReinforcable = false; + team->IsTransient = true; + team->ClassCount = 1; + team->Members[0].Class = type; + team->Members[0].Quantity = 1; + team->MissionCount = 1; + if (mission == TMISSION_NONE) { + team->MissionList[0].Mission = TMISSION_UNLOAD; + team->MissionList[0].Data.Value = WAYPT_REINF; + } else { + team->MissionList[0].Mission = mission; + team->MissionList[0].Data.Value = argument; + } + team->House = house->Class->House; + if (another) { + team->ClassCount++; + team->Members[1].Class = another; + team->Members[1].Quantity = 1; + } + + bool ok = Do_Reinforcements(team); + if (!ok) delete team; + return(ok); + } + } + return(false); +} + + +/*********************************************************************************************** + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * * + * This routine is used to launch an airstrike. It will create the necessary aircraft and * + * assign them to attack the target specified. This routine bypasses the normal * + * reinforcement logic since it doesn't need the sophistication of unloading and following * + * team mission lists. * + * * + * INPUT: house -- The perpetrator of this air strike. * + * * + * air -- The type of aircraft to make up this airstrike. * + * * + * number -- The number of aircraft in this airstrike. * + * * + * mission -- The mission to assign the aircraft. * + * * + * tarcom -- The target to assign these aircraft. * + * * + * navcom -- The navigation target to assign (if necessary). * + * * + * OUTPUT: Returns the number of aircraft created for this airstrike. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int Create_Air_Reinforcement(HouseClass * house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger) +{ + assert(house != 0); + assert((unsigned)air < AIRCRAFT_COUNT); + assert(number != 0); + assert((unsigned)mission < MISSION_COUNT); + /* + ** Get a pointer to the class of the object that we are going to create. + */ + TechnoTypeClass const * type = (TechnoTypeClass *)&AircraftTypeClass::As_Reference(air); + + /* + ** Abort the airstrike if Tanya is the passenger and she's dead. + */ + if (passenger == INFANTRY_TANYA && IsTanyaDead) { + number = 0; + } + + /* + ** Loop through the number of objects we are supposed to create and + ** create and place them on the map. + */ + for (int sub = 0; sub < number; sub++) { + + /* + ** Create one of the required objects. If this fails we could have + ** a real problem. + */ + ScenarioInit++; + TechnoClass * obj = (TechnoClass *)type->Create_One_Of(house); + ScenarioInit--; + if (!obj) return(sub); + + /* + ** Flying objects always have the IsALoaner bit set. + */ + obj->IsALoaner = true; + + /* + ** Find a cell for the object to come in on. This is stolen from the + ** the code that handles a SOURCE_AIR in the normal logic. + */ + SourceType source = house->Control.Edge; + switch (source) { + case SOURCE_NORTH: + case SOURCE_EAST: + case SOURCE_SOUTH: + case SOURCE_WEST: + break; + + default: + source = SOURCE_NORTH; + break; + } + CELL newcell = Map.Calculated_Cell(source, -1, -1, SPEED_WINGED); + + /* + ** Try and place the object onto the map. + */ + ScenarioInit++; + int placed = obj->Unlimbo(Cell_Coord(newcell), DIR_N); + ScenarioInit--; + if (placed) { + + /* + ** If we succeeded in placing the obj onto the map then + ** now we need to give it a mission and destination. + */ + obj->Assign_Mission(mission); + + /* + ** If a navcom was specified then set it. + */ + if (navcom != TARGET_NONE) { + obj->Assign_Destination(navcom); + } + + /* + ** If a tarcom was specified then set it. + */ + if (tarcom != TARGET_NONE) { + obj->Assign_Target(tarcom); + } + + /* + ** Assign generic passenger value here. This value is used to determine + ** if this aircraft should drop parachute reinforcements. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass * aircraft = (AircraftClass *)obj; + if (passenger != INFANTRY_NONE) { + aircraft->Passenger = passenger; + } +// if (Passenger == INFANTRY_TANYA) { +// aircraft->Ammo = 1; + //aircraft->AttacksRemaining = 1; +// } + } + + /* + ** Start the object into action. + */ + obj->Commence(); + } else { + delete obj; + sub--; + return(sub); + } + } + return(sub); +} diff --git a/CODE/RGB.CPP b/CODE/RGB.CPP new file mode 100644 index 0000000..e9df386 --- /dev/null +++ b/CODE/RGB.CPP @@ -0,0 +1,249 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RGB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RGB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * RGBClass::Difference -- Determines the "distance" between two colors. * + * RGBClass::Set -- Set the color index to the RGB object specified. * + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "rgb.h" +#include "hsv.h" +#include "palette.h" + +RGBClass const BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * * + * This routine is used to modify an RGB value to proportionately match another RGB value * + * according to the ratio parameter specified. Typical use of this routine is in palette * + * fading from one palette to another or to black. * + * * + * INPUT: ratio -- The ration of transformation. This value is in the form of 0 to 255, * + * with 0 being no change, and 255 being 100% transformed into the * + * destination color. * + * * + * rgb -- Reference to the destination RGB color to transform this color into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Adjust(int ratio, RGBClass const & rgb) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = (int)rgb.Red - (int)Red; + Red = (int)Red + (value * ratio) / 256; + + value = (int)rgb.Green - (int)Green; + Green = (int)Green + (value * ratio) / 256; + + value = (int)rgb.Blue - (int)Blue; + Blue = (int)Blue + (value * ratio) / 256; +} + + +/*********************************************************************************************** + * RGBClass::Difference -- Determines the "distance" between two colors. * + * * + * This routine is used to calculate a relative distance between two colors. The value is * + * relative only to itself and thus is useful only for determining the magnitude of * + * color difference rather than the nature of the color difference. Palette remapping * + * code uses this routine to find closest matches for colors. * + * * + * INPUT: rgb -- Reference to the color to be compared to this color. * + * * + * OUTPUT: Returns the difference between the two colors. The value returned is zero if the * + * colors exactly match. The greater the positive value the greater the difference * + * between the colors. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int RGBClass::Difference(RGBClass const & rgb) const +{ + int r = (int)Red - (int)rgb.Red; + if (r < 0) r = -r; + + int g = (int)Green - (int)rgb.Green; + if (g < 0) g = -g; + + int b = (int)Blue - (int)rgb.Blue; + if (b < 0) b = -b; + + return(r*r + g*g + b*b); +} + + +/*********************************************************************************************** + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * * + * This conversion operator will convert an RGBClass object into an HSVClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implicit) to the HSVClass object that most closely * + * represents the RGBClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +RGBClass::operator HSVClass (void) const +{ + int hue; + int saturation; + int value; + + /* + ** Fetch working component values for the color guns. + */ + int red = Red_Component(); + int green = Green_Component(); + int blue = Blue_Component(); + + /* + ** The hue defaults to none. Only if there is a saturation value will the + ** hue be calculated. + */ + hue = 0; + + /* + ** Set the value (brightness) to match the brightest color gun. + */ + value = (red > green) ? red : green; + if (blue > value) value = blue; + + /* + ** Determine the amount of true white present in the color. This is the + ** minimum color gun value. The white component is used to determine + ** color saturation. + */ + int white = (red < green) ? red : green; + if (blue < white) white = blue; + + /* + ** Determine the saturation (intensity) of the color by comparing the + ** ratio of true white as a component of the overall color. The more + ** white component, the less saturation. + */ + saturation = 0; + if (value) { + saturation = ((value - white) * 255) / value; + } + + /* + ** If there is any saturation at all, then the hue must be calculated. The + ** hue is based on a six sided color wheel. + */ + if (saturation != 0) { + unsigned int tmp = value - white; + unsigned int r1 = ((value - red) * 255) / tmp; + unsigned int g1 = ((value - green) * 255) / tmp; + unsigned int b1 = ((value - blue) * 255) / tmp; + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if (value == red) { + if (white == green) { + tmp = 5 * 256 + b1; + } else { + tmp = 1 * 256 - g1; + } + } else { + if (value == green) { + if (white == blue) { + tmp = 1 * 256 + r1; + } else { + tmp = 3 * 256 - b1; + } + } else { + if (white == red) { + tmp = 3 * 256 + g1; + } else { + tmp = 5 * 256 - r1; + } + } + } + + // Divide by six and round. + hue = tmp / 6; + } + + return(HSVClass(hue, saturation, value)); +} + + +/*********************************************************************************************** + * RGBClass::Set -- Set the color index to the RGB object specified. * + * * + * This routine will set the specified color index with this RGBClass object. Use this * + * routine to set one color. * + * * + * INPUT: color -- The color index to set with this RGBClass object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Set(int color) const +{ + Raw_Color_Prep(color); + Raw_Set(); + + ((RGBClass &)PaletteClass::CurrentPalette[color]) = *this; +} + diff --git a/CODE/RGB.H b/CODE/RGB.H new file mode 100644 index 0000000..d44047d --- /dev/null +++ b/CODE/RGB.H @@ -0,0 +1,138 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RGB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RGB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RGB_H +#define RGB_H + +class PaletteClass; +class HSVClass; + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + +extern void outrgb(unsigned char red, unsigned char green, unsigned char blue); +#pragma aux outrgb parm [al] [bl] [cl] \ + modify [dx al] = \ + "mov dx,03C9h" \ + "out dx,al" \ + "jmp e1" \ + "e1:" \ + "mov al,bl" \ + "out dx,al" \ + "jmp e2" \ + "e2:" \ + "mov al,cl" \ + "out dx,al" \ + "jmp e3" \ + "e3:" + +/* +** Each color entry is represented by this class. It holds the values for the color +** guns. The gun values are recorded in device dependant format, but the interface +** uses gun values from 0 to 255. +*/ +class RGBClass +{ +// static RGBClass const BlackColor; + + public: + RGBClass(void) : Red(0), Green(0), Blue(0) {}; + RGBClass(unsigned char red, unsigned char green, unsigned char blue) : + Red((unsigned char)(red>>2)), + Green((unsigned char)(green>>2)), + Blue((unsigned char)(blue>>2)) + {}; + operator HSVClass (void) const; + RGBClass & operator = (RGBClass const & rgb) { + if (this == &rgb) return(*this); + + Red = rgb.Red; + Green = rgb.Green; + Blue = rgb.Blue; + return(*this); + }; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, RGBClass const & rgb); +// void Adjust(int ratio, RGBClass const & rgb = BlackColor); + int Difference(RGBClass const & rgb) const; + int Red_Component(void) const {return ((Red << 2) | (Red >> 6));}; + int Green_Component(void) const {return((Green << 2) | (Green >> 6));}; + int Blue_Component(void) const {return((Blue << 2) | (Blue >> 6));}; + void Set(int color) const; + + private: + + friend class PaletteClass; + + void Raw_Set(void) const { + outrgb(Red, Green, Blue); +// outportb(0x03C9, Red); +// outportb(0x03C9, Green); +// outportb(0x03C9, Blue); + }; + + static void Raw_Color_Prep(unsigned char color) { + outportb(0x03C8, color); + }; + + /* + ** These hold the actual color gun values in machine dependant scale. This + ** means the values range from 0 to 63. + */ + unsigned char Red; + unsigned char Green; + unsigned char Blue; +}; + +extern RGBClass const BlackColor; + +#endif diff --git a/CODE/RNDSTRAW.CPP b/CODE/RNDSTRAW.CPP new file mode 100644 index 0000000..0b757e9 --- /dev/null +++ b/CODE/RNDSTRAW.CPP @@ -0,0 +1,320 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RNDSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RNDSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomStraw::Get -- Fetch random data. * + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * RandomStraw::Reset -- Reset the data to known initial state. * + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "rndstraw.h" +#include "sha.h" + + +/*********************************************************************************************** + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * * + * This will initialize the random straw into a known state. The initial state is useless * + * for cryptographic purposes. It must be seeded before it should be used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::RandomStraw(void) : + SeedBits(0), + Current(0) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * * + * This destructor will clear out the seed data so that there won't be any sensitive * + * information left over in the memory this object occupied. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::~RandomStraw(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::Reset -- Reset the data to known initial state. * + * * + * This routine is functionally equivalent to performing an placement new on the object. It * + * clears out all the seed data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must reseed it before fetching random numbers. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Reset(void) +{ + SeedBits = 0; + Current = 0; + memset(Random, '\0', sizeof(Random)); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * * + * This routine is used when seeding the random straw. Keep feeding random bits to this * + * class until the Seed_Bits_Needed value returns zero. Random bits should be gathered from * + * sources external to the immediate program. Examples would be the system clock (good for * + * a few bits), the delay between player keystrokes (good for a few bits per keystroke), * + * etc. When gathering random bits from integers, use the low order bits (since they * + * characteristically are less predictable and more 'random' than the others). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bits required in order to fully seed this random * + * number generator. * + * * + * WARNINGS: Even if this routine returns zero, you are still allowed and encouraged to feed * + * more random bits when the opportunity arrises. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +int RandomStraw::Seed_Bits_Needed(void) const +{ + const int total = sizeof(Random) * CHAR_BIT; + if (SeedBits < total) { + return(total - SeedBits); + } + return(0); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * * + * This routine should be called to feed a single random bit to the random straw object. * + * You must feed as many bits as requested by the Seed_Bits_Needed() function. Submitting * + * additional bits is not only allowed, but encouraged. * + * * + * INPUT: seed -- The bit (lowest order bit) to feed to the random number seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Bit(int seed) +{ + char * ptr = ((char *)&Random[0]) + ((SeedBits / CHAR_BIT) % sizeof(Random)); + char frac = (char)(1 << (SeedBits & (CHAR_BIT-1))); + + if (seed & 0x01) { + *ptr ^= frac; + } + SeedBits++; + + if (SeedBits == (sizeof(Random) * CHAR_BIT)) { + Scramble_Seed(); + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * * + * This will submit 8 bits (as specified) to the random number seed. * + * * + * INPUT: seed -- The seed bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Byte(char seed) +{ + for (int index = 0; index < CHAR_BIT; index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * * + * This will submit 16 bits to the accumulated seed. * + * * + * INPUT: seed -- The 16 bits to submit to the seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Short(short seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * * + * This will submit a full 32 bits to the accumulated seed value. * + * * + * INPUT: seed -- The 32 bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Long(long seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * * + * This routine is called when a full set of seed bits has been accumulated. It will take * + * the existing seed and scramble the bits. An effective way of doing this is to use the * + * Secure Hash Algorithm. It is necessary to have this routine because there might be * + * some coorelation in the seed bits. Even more important is that this routine will result * + * in every bit of the seed having a scrambling effect on every other bit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Scramble_Seed(void) +{ + SHAEngine sha; + + for (int index = 0; index < sizeof(Random); index++) { + char digest[20]; + + sha.Hash(&Random[0], sizeof(Random)); + sha.Result(digest); + + int tocopy = sizeof(digest) < (sizeof(Random)-index) ? sizeof(digest) : (sizeof(Random)-index); + memmove(((char *)&Random[0]) + index, digest, tocopy); + } +} + + +/*********************************************************************************************** + * RandomStraw::Get -- Fetch random data. * + * * + * This routine will fetch random data and store it into the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to fill with random data. * + * * + * length -- The number of bytes to store into the buffer. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. This will always * + * be the number of bytes requested since random numbers are unexhaustible. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + * 07/10/1996 JLB : Revamped to make cryptographically secure. * + *=============================================================================================*/ +int RandomStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Straw::Get(source, slen)); + } + + int total = 0; + while (slen > 0) { + *(char *)source = (char)Random[Current++]; + Current = Current % (sizeof(Random) / sizeof(Random[0])); + source = (char*)source + sizeof(char); + slen--; + total++; + } + return(total); +} diff --git a/CODE/RNDSTRAW.H b/CODE/RNDSTRAW.H new file mode 100644 index 0000000..8c6c63c --- /dev/null +++ b/CODE/RNDSTRAW.H @@ -0,0 +1,92 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RNDSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RNDSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RNDSTRAW_H +#define RNDSTRAW_H + + +#include "straw.h" +#include "random.h" + +/* +** This is a straw terminator class. It will generate random numbers to fill the data request. +** Unlike regular straw terminators, this class will never run out of "data". +*/ +class RandomStraw : public Straw +{ + public: + RandomStraw(void); + virtual ~RandomStraw(void); + + virtual int Get(void * source, int slen); + + void Reset(void); + void Seed_Bit(int seed); + void Seed_Byte(char seed); + void Seed_Short(short seed); + void Seed_Long(long seed); + + int Seed_Bits_Needed(void) const; + + private: + /* + ** Counter of the number of seed bits stored to this random number + ** generator. + */ + int SeedBits; + + /* + ** The current random generator to use when fetching the next random + ** byte of data. + */ + int Current; + + /* + ** Array of generators. There must be at least 448 bits of random number seed + ** in order to be reasonably secure, however, using 1024 bits would be best. + */ + RandomClass Random[32]; + + void Scramble_Seed(void); + + RandomStraw(RandomStraw & rvalue); + RandomStraw & operator = (RandomStraw const & pipe); +}; + + +#endif diff --git a/CODE/RNG.H b/CODE/RNG.H new file mode 100644 index 0000000..db114c0 --- /dev/null +++ b/CODE/RNG.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RNG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RNG.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RNG_H +#define RNG_H + +/* +** This is an abstract interface class for a random number generator. It serves only to +** provide random numbers. +*/ +class RandomNumberGenerator { + public: + virtual ~RandomNumberGenerator() {} + + virtual void Get_Block(void * output, unsigned int size) = 0; +}; + + +#endif + diff --git a/CODE/ROTBMP.CPP b/CODE/ROTBMP.CPP new file mode 100644 index 0000000..3f0b99a --- /dev/null +++ b/CODE/ROTBMP.CPP @@ -0,0 +1,415 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/ROTBMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ROTBMP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +#include "watcom.h" +#include "rotbmp.h" +#define FILE_H +#define WWMEM_H +#include + + +int Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int angle); + +struct WPPOINT { + int x; + int y; +}; + +/*************************************************************************** + * Rotate_bitmap -- rotate a bitmap from srcvp to destvp * + * * + * INPUT: note that angles are 0 - 255 (best if used in increments of 4!) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * destvp should be a square. width and height should be equal to : * + * MAX(srcvp->width,srcvp->height) * 1.7 (square root of 2!!) * + * * + * * + * HISTORY: * + * 01/02/1996 BP : Created. * + *=========================================================================*/ +Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int a) +{ + int shift = 7; + int fixpoint1 = 1 << shift; // this is a fixed point 1 + int Deltax; + int Deltay; + int sx,sy,dx,dy; + int Error = 0; + int Decimal = 0; + // this is used if I walk in Y + int buffwidth = srcvp->Get_Width() + srcvp->Get_XAdd(); + int buffwid2 = destvp->Get_Width() + destvp->Get_XAdd(); + char * dest; + char * src; + int sa,ca; + int t; + int x,y; + int rx,ry; + int w,h; + int dw = destvp->Get_Width(); + int dh = destvp->Get_Height(); + WPPOINT sp[4]; + WPPOINT dp[4]; + sa = Sin256[a]; + ca = Cos256[a]; + // get rectangle size + x = 0; + y = 0; + w = srcvp->Get_Width(); + h = srcvp->Get_Height(); + + int halfws = w >> 1; + int halfhs = h >> 1; + int halfwd = dw >> 1; + int halfhd = dh >> 1; + + + // make the src rectangle + sp[0].x = x; + sp[0].y = y; + + sp[1].x = x + w; + sp[1].y = y; + + // now calculate the rotated rectangle + dp[0].x = ( ((sp[0].x - halfws) * ca) - ((sp[0].y - halfhs) * sa) ) >> shift; + dp[0].x += halfwd; + + dp[0].y = ( ((sp[0].x - halfws) * sa) + ((sp[0].y - halfhs) * ca) ) >> shift; + dp[0].y += halfhd; + + dp[1].x = ( ((sp[1].x - halfws) * ca) - ((sp[1].y - halfhs) * sa) ) >> shift; + dp[1].x += halfwd; + + dp[1].y = ( ((sp[1].x - halfws) * sa) + ((sp[1].y - halfhs) * ca) ) >> shift; + dp[1].y += halfhd; + + rx = dp[0].x; + ry = dp[0].y; + // now calculate slope + + // diff from new to old x + + Deltax = (dp[1].x - dp[0].x); + + // diff from new to old y + + Deltay = (dp[1].y - dp[0].y); + + // handle the easy cases + + // no change in x + int r; + if (!Deltax) { // must be 90 or 270 degree transpose! + if (Deltay < 0) { + // walk across source in the x + dir + // walk across dest in the y - dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + dx, ry - dy); + for (r = 0; r< w; r++) { + // transparency + if (* src) + *dest = *src; + src++; + dest -= buffwid2; + } + y++; + dx++; + } + } else { + // walk across source in the x + dir + // walk across dest in the y + dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - dx, ry + dy); + for (r = 0; r< w; r++) { + // transparency + if (*src) + *dest = *src; + src++; + dest += buffwid2; + } + y++; + dx++; + } + } + return 0; + } + // no change in y + + if (!Deltay) { // must be 0 or 180 degree transpose ! + if (Deltax < 0) { + y = 0; + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - x , ry - y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest--; + src++; + } + } + } else { + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + x, ry + y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest++; + src++; + } + } + } + return 0; + } + + + // ok now the hard part + + // make them 16.16 + if ( ABS(Deltax) < ABS(Deltay)) { // ok this means we want to walk in y + + // walk in + x in the src and + // walk in + y in the dest + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + int yinc1 = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + } + Decimal = ( Deltax << shift) / Deltay ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratio between the source height and the dest height + // as the rectangle rotates the height and width change + int DeltaH = (w << shift) / Deltay; + int Error2 = 0; + sy = 0; + for (int r = 0; r< h ;r++) { + // now we walk across the top calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start in dest + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + + // this is the end point of the line + + int y2 = ( ( ((w) - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + y2 += halfhd; + + // length of line + + int diff = ABS(y2 - ry); + + // if walking backwards reveres diff to reflect sign + + src = MAKE_PTR(srcvp, x, y + sy); + + dest = MAKE_PTR(destvp, rx, ry); + + Error = 0; + Error2 = 0; + char * baseptr = src; + // while walking line + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaH; + dest += buffwid2; + Error += Decimal; + src = baseptr + (Error2 >> shift) ; + // this is time to inc x in src y in dest + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += xinc; + } + } + sy += yinc; + } + return 0; + } else { // else we walk in X + int lasterror = 0; + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + sy = sy + h - 1; + yinc = -1; + } + + + Decimal = ( Deltay << shift) / Deltax ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratios between the source width and the dest width + // as the rectangle rotates the actual size changes! + + int DeltaW = (w << shift) / Deltax; + int Error2 = 0; + for (int r = 0; r< h ;r++) { + sx = 0; + + // now we walk across the side calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start + + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + // this is the other side of the box + + int x2 = ( ( ((sx + w) - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + + x2 += halfwd; + + int diff = x2 - rx; + + + dx = 0; + dy = 0; + + if (xinc == -1) { + diff = -diff; + } + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + Error = 0; + Error2 = 0; + char * baseptr = src; + + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaW; + rx++; + dest += xinc; + Error += Decimal; + src = baseptr + (Error2 >> shift); + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += buffwid2; + } + } + sy += yinc; + } + } + return 0; +} diff --git a/CODE/ROTBMP.H b/CODE/ROTBMP.H new file mode 100644 index 0000000..96aed06 --- /dev/null +++ b/CODE/ROTBMP.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +// list of 256 sines + +#define MAKE_PTR(vp,x,y) (char *) (vp->Get_Offset() + ((y) * (vp->Get_Width() + vp->Get_XAdd())) + (x)) +#define MAKE_PTR2(vp,x,y) (char *) (vp->Get_Offset() + (((y)<<1) * (vp->Get_Width() + vp->Get_XAdd())) + ((x)<<1)) + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +long Cos256[] = { + 128, 127, 127, 127, 127, 127, 126, 126, + 125, 124, 124, 123, 122, 121, 120, 119, + 118, 116, 115, 114, 112, 111, 109, 107, + 106, 104, 102, 100, 98, 96, 94, 92, + 90, 87, 85, 83, 80, 78, 75, 73, + 70, 68, 65, 62, 59, 57, 54, 51, + 48, 45, 42, 39, 36, 33, 30, 27, + 24, 21, 18, 14, 11, 8, 5, 2, + 0, -3, -7, -10, -13, -16, -19, -22, + -25, -28, -31, -35, -38, -41, -44, -46, + -49, -52, -55, -58, -61, -63, -66, -69, + -72, -74, -77, -79, -82, -84, -86, -89, + -91, -93, -95, -97, -99, -101, -103, -105, + -107, -108, -110, -112, -113, -114, -116, -117, + -118, -119, -120, -121, -122, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -116, -114, -113, -112, -110, -108, -107, + -105, -103, -101, -99, -97, -95, -93, -91, + -89, -86, -84, -82, -79, -77, -74, -72, + -69, -66, -64, -61, -58, -55, -52, -49, + -46, -44, -41, -38, -35, -31, -28, -25, + -22, -19, -16, -13, -10, -7, -3, 0, + 2, 5, 8, 11, 14, 18, 21, 24, + 27, 30, 33, 36, 39, 42, 45, 48, + 51, 54, 57, 59, 62, 65, 68, 70, + 73, 75, 78, 80, 83, 85, 87, 90, + 92, 94, 96, 98, 100, 102, 104, 106, + 107, 109, 111, 112, 114, 115, 116, 118, + 119, 120, 121, 122, 123, 124, 124, 125, + 126, 126, 127, 127, 127, 127, 127, 127, +}; +long Sin256[] = { + 0, 3, 6, 9, 12, 15, 18, 21, + 25, 28, 31, 34, 37, 40, 43, 46, + 49, 52, 54, 57, 60, 63, 66, 68, + 71, 73, 76, 79, 81, 83, 86, 88, + 90, 92, 95, 97, 99, 101, 103, 104, + 106, 108, 110, 111, 113, 114, 115, 117, + 118, 119, 120, 121, 122, 123, 124, 125, + 125, 126, 126, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 126, 126, 125, + 125, 124, 123, 123, 122, 121, 120, 119, + 117, 116, 115, 113, 112, 110, 109, 107, + 105, 104, 102, 100, 98, 96, 94, 91, + 89, 87, 85, 82, 80, 77, 75, 72, + 70, 67, 64, 61, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, + 23, 20, 17, 14, 11, 7, 4, 1, + -1, -4, -7, -11, -14, -17, -20, -23, + -26, -29, -32, -35, -38, -41, -44, -47, + -50, -53, -56, -59, -61, -64, -67, -70, + -72, -75, -77, -80, -82, -85, -87, -89, + -91, -94, -96, -98, -100, -102, -104, -105, + -107, -109, -110, -112, -113, -115, -116, -117, + -119, -120, -121, -122, -123, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -115, -114, -113, -111, -110, -108, -106, + -104, -103, -101, -99, -97, -95, -92, -90, + -88, -86, -83, -81, -79, -76, -73, -71, + -68, -66, -63, -60, -57, -54, -52, -49, + -46, -43, -40, -37, -34, -31, -28, -25, + -21, -18, -15, -12, -9, -6, -3, 0, +}; diff --git a/CODE/RULES.CPP b/CODE/RULES.CPP new file mode 100644 index 0000000..a97ca82 --- /dev/null +++ b/CODE/RULES.CPP @@ -0,0 +1,1049 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RULES.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RULES.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Difficulty_Get -- Fetch the difficulty bias values. * + * RulesClass::AI -- Processes the AI control constants from the database. * + * RulesClass::General -- Process the general main game rules. * + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * RulesClass::Land_Types -- Inits the land type values. * + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * RulesClass::Powerups -- Process the powerup values from the database. * + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * RulesClass::RulesClass -- Default constructor for rules class object. * + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * Techno_Get -- Get rule data common for all techno type objects. * + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * RulesClass::Objects -- Fetch all the object characteristic values. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "vortex.h" + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = fixed(100, 256) * val; + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * RulesClass::RulesClass -- Default constructor for rules class object. * + * * + * This is the default constructor for the rules class object. Although it initializes the * + * rule data with default values, it is expected that they will all be overridden by the * + * rules control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +RulesClass::RulesClass(void) : + TurboBoost("1.5"), + AttackInterval(3), + AttackDelay(5), + PowerEmergencyFraction(fixed::_3_4), + BadgerBombCount(1), + AirstripRatio(".12"), + AirstripLimit(5), + HelipadRatio(".12"), + HelipadLimit(5), + TeslaRatio(".16"), + TeslaLimit(10), + AARatio(".14"), + AALimit(10), + DefenseRatio(".5"), + DefenseLimit(40), + WarRatio(".1"), + WarLimit(2), + BarracksRatio(".16"), + BarracksLimit(2), + RefineryLimit(4), + RefineryRatio(".16"), + BaseSizeAdd(3), + PowerSurplus(50), + InfantryReserve(2000), + InfantryBaseMult(2), + ChronoDuration(3), + WaterCrateChance(".2"), + SoloCrateMoney(2000), + GPSTechLevel(0), + UnitCrateType(UNIT_NONE), + PatrolTime(".016"), + TeamDelay(".6"), + CloakDelay(0), + GameSpeedBias(1), + NervousBias(1), + VortexRange(10*CELL_LEPTON_W), + VortexSpeed((MPHType)10), + VortexDamage(200), + VortexChance(".2"), + ExplosionSpread(fixed::_1_2), + SupressRadius(CELL_LEPTON_W), + ParaInfantryTechLevel(10), + SpyPlaneTechLevel(10), + ParaBombTechLevel(10), + MaxIQ(5), + IQSuperWeapons(4), + IQProduction(5), + IQGuardArea(4), + IQRepairSell(3), + IQCrush(2), + IQScatter(3), + IQContentScan(4), + IQAircraft(4), + IQHarvester(3), + IQSellBack(2), + SilverCrate(CRATE_HEAL_BASE), + WoodCrate(CRATE_MONEY), + WaterCrate(CRATE_MONEY), + CrateMinimum(1), + CrateMaximum(255), + LZScanRadius(16*CELL_LEPTON_W), + MPDefaultMoney(3000), + MPMaxMoney(10000), + IsMPShadowGrow(true), + IsMPBasesOn(true), + IsMPTiberiumGrow(true), + IsMPCrates(true), + IsMPAIPlayers(false), + IsMPCaptureTheFlag(false), + DropZoneRadius(4*CELL_LEPTON_W), + MessageDelay(".6"), + SavourDelay(".03"), + AVMineDamage(1200), + APMineDamage(1000), + MaxPlayers(8), + BaseDefenseDelay(fixed::_1_4), + SuspendPriority(20), + SuspendDelay(2), + SurvivorFraction(fixed::_1_2), + ReloadRate(".05"), + AutocreateTime(5), + BuildupTime(".05"), + OreDumpRate(2), + AtomDamage(1000), + IsComputerParanoid(true), + IsCurleyShuffle(false), + IsFlashLowPower(true), + IsCompEasyBonus(true), + IsFineDifficulty(false), + IsExplosiveHarvester(false), + IsHealthBar(true), + IsMCVDeploy(false), + IsAllyReveal(true), + IsSeparate(false), + IsTreeTarget(false), + IsMineAware(true), + IsTGrowth(true), + IsTSpread(true), + IsNamed(false), + IsAutoCrush(false), + IsSmartDefense(false), + IsScatter(false), + IsChronoKill(true), + ProneDamageBias(fixed::_1_2), + QuakeDamagePercent(".33"), + QuakeChance(".2"), + GrowthRate(2), + ShroudRate(4), + CrateTime(10), + TimerWarning(2), + ChronoTechLevel(1), + SonarTime(14), + ChronoTime(3), + ParaBombTime(14), + ParaInfantryTime(2), + ParaSaboteurTime(14), + SpyTime(2), + IronCurtainTime(14), + GPSTime(1), + NukeTime(14), + SpeakDelay(2), + DamageDelay(1), + Gravity(3), + GapShroudRadius(10), + GapRegenInterval(".1"), + RadarJamRadius(10*CELL_LEPTON_W), + Incoming(MPH_IMMOBILE), + MinDamage(1), + MaxDamage(1000), + RepairStep(5), + RepairPercent(fixed::_1_4), + URepairStep(5), + URepairPercent(fixed::_1_4), + RepairRate(".016"), + ConditionGreen(1), + ConditionYellow(fixed::_1_2), + ConditionRed(fixed::_1_4), + RandomAnimateTime(".083"), + BailCount(28), + GoldValue(35), + GemValue(110), + AircraftMax(100), + AnimMax(100), + BuildingMax(500), + BulletMax(40), + FactoryMax(20), + InfantryMax(500), + OverlayMax(1), + SmudgeMax(1), + TeamMax(60), + TeamTypeMax(60), + TemplateMax(1), + TerrainMax(500), + TriggerMax(60), + UnitMax(500), + VesselMax(100), + ProjectileMax(20), + WeaponMax(20), + WarheadMax(20), + TrigTypeMax(80), + CloseEnoughDistance(0x0280), + StrayDistance(0x0200), + CrushDistance(0x0180), + CrateRadius(0x0280), + HomingScatter(0x0200), + BallisticScatter(0x0100), + RefundPercent(fixed::_1_2), + IronCurtainDuration(fixed::_1_2), + BridgeStrength(1000), + BuildSpeedBias(1), + C4Delay(".03"), + RepairThreshhold(1000), + PathDelay(".016"), + MovieTime(fixed::_1_4), + TiberiumShortScan(0x0600), + TiberiumLongScan(0x2000) +{ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; + MTankDistance = 30; + QuakeUnitDamage = 0x080; + QuakeBuildingDamage = 0x040; + QuakeInfantryDamage = 0; + QuakeDelay = 120; + ChronoTankDuration = 0x300; +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + EngineerDamage=(fixed)1 / (fixed)3; // Amount of damage an engineer does + EngineerCaptureLevel=ConditionRed; // Building damage level before engineer can capture +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = 60; +#endif +#endif +} + + +/*********************************************************************************************** + * Difficulty_Get -- Fetch the difficulty bias values. * + * * + * This will fetch the difficulty bias values for the section specified. * + * * + * INPUT: ini -- Reference the INI database to fetch the values from. * + * * + * diff -- Reference to the difficulty class object to fill in with the values. * + * * + * section -- The section identifier to lift the values from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +static void Difficulty_Get(CCINIClass & ini, DifficultyClass & diff, char const * section) +{ + if (ini.Is_Present(section)) { + diff.FirepowerBias = ini.Get_Fixed(section, "FirePower", 1); + diff.GroundspeedBias = ini.Get_Fixed(section, "Groundspeed", 1); + diff.AirspeedBias = ini.Get_Fixed(section, "Airspeed", 1); + diff.ArmorBias = ini.Get_Fixed(section, "Armor", 1); + diff.ROFBias = ini.Get_Fixed(section, "ROF", 1); + diff.CostBias = ini.Get_Fixed(section, "Cost", 1); + diff.RepairDelay = ini.Get_Fixed(section, "RepairDelay", ".02"); + diff.BuildDelay = ini.Get_Fixed(section, "BuildDelay", ".03"); + diff.IsBuildSlowdown = ini.Get_Bool(section, "BuildSlowdown", false); + diff.BuildSpeedBias = ini.Get_Fixed(section, "BuildTime", 1); + diff.IsWallDestroyer = ini.Get_Bool(section, "DestroyWalls", true); + diff.IsContentScan = ini.Get_Bool(section, "ContentScan", false); + } +} + + +/*********************************************************************************************** + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * * + * This routine will fetch the rule data from the control file. * + * * + * INPUT: file -- Reference to the rule file to process. * + * * + * OUTPUT: bool; Was the rule file processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Process(CCINIClass & ini) +{ + BStart(BENCH_RULES); + + General(ini); + MPlayer(ini); + Recharge(ini); + Heap_Maximums(ini); + AI(ini); + Powerups(ini); + Land_Types(ini); + Themes(ini); + IQ(ini); + Objects(ini); + Difficulty(ini); + + BEnd(BENCH_RULES); + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::General -- Process the general main game rules. * + * * + * This fetches the control constants uses for regular game processing. Any game behavior * + * controlling values that don't properly fit in any of the other catagories will be * + * stored here. * + * * + * INPUT: ini -- Reference to the database to fetch the values from. * + * * + * OUTPUT: bool; Was the general section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::General(CCINIClass & ini) +{ + static char const * const GENERAL = "General"; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static char const * const AFTERMATH = "Aftermath"; + + if(ini.Is_Present(AFTERMATH)) { +//debugprint( "NewUnitsEnabled previously %i\n", NewUnitsEnabled ); + NewUnitsEnabled = ini.Get_Int(AFTERMATH, "NewUnitsEnabled", 0); +//debugprint( "NewUnitsEnabled set to %i by Rules\n", NewUnitsEnabled ); + MTankDistance = ini.Get_Int(AFTERMATH,"MTankDistance",MTankDistance); + QuakeUnitDamage = ini.Get_Fixed(AFTERMATH, "QuakeUnitDamage", QuakeUnitDamage); + QuakeBuildingDamage = ini.Get_Fixed(AFTERMATH, "QuakeBuildingDamage", QuakeBuildingDamage); + QuakeInfantryDamage = ini.Get_Int(AFTERMATH,"QuakeInfantryDamage",QuakeInfantryDamage); + QuakeDelay = ini.Get_Int(AFTERMATH,"QuakeDelay",QuakeDelay); + ChronoTankDuration = ini.Get_Fixed(AFTERMATH, "ChronoTankDuration", ChronoTankDuration); +//Mono_Set_Cursor(0,0);Mono_Printf("Chrono duration: %08x \n",ChronoTankDuration);Keyboard->Get();Keyboard->Get(); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = ini.Get_Int(AFTERMATH,"CarrierLaunchDelay",120); +#endif +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + // Engineer changing fields were specifically left out of Aftrmath.ini, thus these values are not found to set. ajw + // Implies interesting security hole if user creates a separate Aftrmath.ini file! + EngineerDamage = ini.Get_Fixed(AFTERMATH, "EngineerDamage", EngineerDamage); // Amount of damage an engineer does + EngineerCaptureLevel = ini.Get_Fixed(AFTERMATH, "EngineerCaptureLevel", EngineerCaptureLevel); // Building damage level before engineer can capture +#endif + } + +#endif + + if (ini.Is_Present(GENERAL)) { + TurboBoost = ini.Get_Fixed(GENERAL, "TurboBoost", TurboBoost); + BadgerBombCount = ini.Get_Int(GENERAL, "BadgerBombCount", BadgerBombCount); + IsCurleyShuffle = ini.Get_Bool(GENERAL, "CurleyShuffle", IsCurleyShuffle); + IsFlashLowPower = ini.Get_Bool(GENERAL, "FlashLowPower", IsFlashLowPower); + IsChronoKill = ini.Get_Bool(GENERAL, "ChronoKillCargo", IsChronoKill); + ChronoDuration = ini.Get_Fixed(GENERAL, "ChronoDuration", ChronoDuration); + IsFineDifficulty = ini.Get_Bool(GENERAL, "FineDiffControl", IsFineDifficulty); + WaterCrateChance = ini.Get_Fixed(GENERAL, "WaterCrateChance", WaterCrateChance); + SoloCrateMoney = ini.Get_Int(GENERAL, "SoloCrateMoney", SoloCrateMoney); + ParaBombTechLevel = ini.Get_Int(GENERAL, "ParabombTech", ParaBombTechLevel); + GPSTechLevel = ini.Get_Int(GENERAL, "GPSTechLevel", GPSTechLevel); + UnitCrateType = ini.Get_UnitType(GENERAL, "UnitCrateType", UnitCrateType); + IsExplosiveHarvester = ini.Get_Fixed(GENERAL, "OreExplosive", IsExplosiveHarvester); + GapRegenInterval = ini.Get_Fixed(GENERAL, "GapRegenInterval", GapRegenInterval); + TeamDelay = ini.Get_Fixed(GENERAL, "TeamDelay", TeamDelay); + CloakDelay = ini.Get_Fixed(GENERAL, "SubmergeDelay", CloakDelay); + GameSpeedBias = ini.Get_Fixed(GENERAL, "GameSpeedBias", GameSpeedBias); + NervousBias = ini.Get_Fixed(GENERAL, "BaseBias", NervousBias); + ExplosionSpread = ini.Get_Fixed(GENERAL, "ExpSpread", ExplosionSpread); + SupressRadius = ini.Get_Lepton(GENERAL, "FireSupress", SupressRadius); + ParaInfantryTechLevel = ini.Get_Int(GENERAL, "ParaTech", ParaInfantryTechLevel); + SpyPlaneTechLevel = ini.Get_Int(GENERAL, "SpyPlaneTech", SpyPlaneTechLevel); + SilverCrate = ini.Get_CrateType(GENERAL, "SilverCrate", SilverCrate); + WoodCrate = ini.Get_CrateType(GENERAL, "WoodCrate", WoodCrate); + WaterCrate = ini.Get_CrateType(GENERAL, "WaterCrate", WaterCrate); + CrateMinimum = ini.Get_Int(GENERAL, "CrateMinimum", CrateMinimum); + CrateMaximum = ini.Get_Int(GENERAL, "CrateMaximum", CrateMaximum); + IsScatter = ini.Get_Bool(GENERAL, "PlayerScatter", IsScatter); + IsSmartDefense = ini.Get_Bool(GENERAL, "PlayerReturnFire", IsSmartDefense); + IsAutoCrush = ini.Get_Bool(GENERAL, "PlayerAutoCrush", IsAutoCrush); + IsNamed = ini.Get_Bool(GENERAL, "NamedCivilians", IsNamed); + IsTGrowth = ini.Get_Bool(GENERAL, "OreGrows", IsTGrowth); + IsTSpread = ini.Get_Bool(GENERAL, "OreSpreads", IsTSpread); + IsMineAware = ini.Get_Bool(GENERAL, "MineAware", IsMineAware); + IsTreeTarget = ini.Get_Bool(GENERAL, "TreeTargeting", IsTreeTarget); + IsSeparate = ini.Get_Bool(GENERAL, "SeparateAircraft", IsSeparate); + DropZoneRadius = ini.Get_Lepton(GENERAL, "DropZoneRadius", DropZoneRadius); + MessageDelay = ini.Get_Fixed(GENERAL, "MessageDelay", MessageDelay); + SavourDelay = ini.Get_Fixed(GENERAL, "SavourDelay", SavourDelay); + AVMineDamage = ini.Get_Int(GENERAL, "AVMineDamage", AVMineDamage); + APMineDamage = ini.Get_Int(GENERAL, "APMineDamage", APMineDamage); + BaseDefenseDelay = ini.Get_Fixed(GENERAL, "BaseDefenseDelay", BaseDefenseDelay); + SuspendPriority = ini.Get_Int(GENERAL, "SuspendPriority", SuspendPriority); + SuspendDelay = ini.Get_Fixed(GENERAL, "SuspendDelay", SuspendDelay); + SurvivorFraction = ini.Get_Fixed(GENERAL, "SurvivorRate", SurvivorFraction); + RadarJamRadius = ini.Get_Lepton(GENERAL, "RadarJamRadius", RadarJamRadius); + ReloadRate = ini.Get_Fixed(GENERAL, "ReloadRate", ReloadRate); + RandomAnimateTime = ini.Get_Fixed(GENERAL, "IdleActionFrequency", RandomAnimateTime); + BuildupTime = ini.Get_Fixed(GENERAL, "BuildupTime", BuildupTime); + OreDumpRate = ini.Get_Int(GENERAL, "OreTruckRate", OreDumpRate); + AtomDamage = ini.Get_Int(GENERAL, "AtomDamage", AtomDamage); + BailCount = ini.Get_Int(GENERAL, "BailCount", BailCount); + BallisticScatter = ini.Get_Lepton(GENERAL, "BallisticScatter", BallisticScatter); + BridgeStrength = ini.Get_Int(GENERAL, "BridgeStrength", BridgeStrength); + BuildSpeedBias = ini.Get_Fixed(GENERAL, "BuildSpeed", BuildSpeedBias); + ConditionGreen = 1; + ConditionRed = ini.Get_Fixed(GENERAL, "ConditionRed", ConditionRed); + ConditionYellow = ini.Get_Fixed(GENERAL, "ConditionYellow", ConditionYellow); + CrateRadius = ini.Get_Lepton(GENERAL, "CrateRadius", CrateRadius); + CrushDistance = ini.Get_Lepton(GENERAL, "Crush", CrushDistance); + DamageDelay = ini.Get_Fixed(GENERAL, "DamageDelay", DamageDelay); + GapShroudRadius = ini.Get_Int(GENERAL, "GapRadius", GapShroudRadius); + GemValue = ini.Get_Int(GENERAL, "GemValue", GemValue); + GoldValue = ini.Get_Int(GENERAL, "GoldValue", GoldValue); + Gravity = ini.Get_Int(GENERAL, "Gravity", Gravity); + GrowthRate = ini.Get_Fixed(GENERAL, "GrowthRate", GrowthRate); + HomingScatter = ini.Get_Lepton(GENERAL, "HomingScatter", HomingScatter); + Incoming = ini.Get_MPHType(GENERAL, "Incoming", MPH_IMMOBILE); + IronCurtainDuration = ini.Get_Fixed(GENERAL, "IronCurtain", IronCurtainDuration); + IsAllyReveal = ini.Get_Bool(GENERAL, "AllyReveal", IsAllyReveal); + IsHealthBar = ini.Get_Bool(GENERAL, "EnemyHealth", IsHealthBar); + IsMCVDeploy = ini.Get_Bool(GENERAL, "MCVUndeploy", IsMCVDeploy); + MaxDamage = ini.Get_Int(GENERAL, "MaxDamage", MaxDamage); + MinDamage = ini.Get_Int(GENERAL, "MinDamage", MinDamage); + ProneDamageBias = ini.Get_Fixed(GENERAL, "ProneDamage", ProneDamageBias); + QuakeDamagePercent = ini.Get_Fixed(GENERAL, "QuakeDamage", QuakeDamagePercent); + QuakeChance = ini.Get_Fixed(GENERAL, "QuakeChance", QuakeChance); + RefundPercent = ini.Get_Fixed(GENERAL, "RefundPercent", RefundPercent); + RepairPercent = ini.Get_Fixed(GENERAL, "RepairPercent", RepairPercent); + RepairStep = ini.Get_Int(GENERAL, "RepairStep", RepairStep); + URepairPercent = ini.Get_Fixed(GENERAL, "URepairPercent", URepairPercent); + URepairStep = ini.Get_Int(GENERAL, "URepairStep", URepairStep); + RepairRate = ini.Get_Fixed(GENERAL, "RepairRate", RepairRate); + ShroudRate = ini.Get_Fixed(GENERAL, "ShroudRate", ShroudRate); + SpeakDelay = ini.Get_Fixed(GENERAL, "SpeakDelay", SpeakDelay); + StrayDistance = ini.Get_Lepton(GENERAL, "Stray", StrayDistance); + CloseEnoughDistance = ini.Get_Lepton(GENERAL, "CloseEnough", CloseEnoughDistance); + TimerWarning = ini.Get_Fixed(GENERAL, "TimerWarning", TimerWarning); + MovieTime = ini.Get_Fixed(GENERAL, "MovieTime", MovieTime); + C4Delay = ini.Get_Fixed(GENERAL, "C4Delay", C4Delay); + ChronoTechLevel = ini.Get_Int(GENERAL, "ChronoTechLevel", ChronoTechLevel); + CrateTime = ini.Get_Fixed(GENERAL, "CrateRegen", CrateTime); + VortexRange = ini.Get_Lepton(GENERAL, "VortexRange", VortexRange); + VortexSpeed = MPHType(_Scale_To_256(ini.Get_Int(GENERAL, "VortexSpeed", VortexSpeed))); + VortexDamage = ini.Get_Int(GENERAL, "VortexDamage", VortexDamage); + VortexChance = ini.Get_Fixed(GENERAL, "VortexChance", VortexChance); + + ChronalVortex.Set_Range(VortexRange / CELL_LEPTON_W); + ChronalVortex.Set_Speed(VortexSpeed); + ChronalVortex.Set_Damage(VortexDamage); + + //ChronalVortex.Set_Range ( ini.Get_Int (GENERAL, "VortexRange", ChronalVortex.Get_Range() ) ); + //ChronalVortex.Set_Speed ( ini.Get_Int (GENERAL, "VortexSpeed", ChronalVortex.Get_Speed() ) ); + //ChronalVortex.Set_Damage ( ini.Get_Int (GENERAL, "VortexDamage", ChronalVortex.Get_Damage() ) ); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * * + * This is used to set the default settings for the multiplayer system. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the multiplayer default override section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::MPlayer(CCINIClass & ini) +{ + static char const * const MPLAYER = "MultiplayerDefaults"; + if (ini.Is_Present(MPLAYER)) { + MPDefaultMoney = ini.Get_Int(MPLAYER, "Money", MPDefaultMoney); + MPMaxMoney = ini.Get_Int(MPLAYER, "MaxMoney", MPMaxMoney); + IsMPShadowGrow = ini.Get_Bool(MPLAYER, "ShadowGrow", IsMPShadowGrow); + IsMPBasesOn = ini.Get_Bool(MPLAYER, "Bases", IsMPBasesOn); + IsMPTiberiumGrow = ini.Get_Bool(MPLAYER, "OreGrows", IsMPTiberiumGrow); + IsMPCrates = ini.Get_Bool(MPLAYER, "Crates", IsMPCrates); + IsMPCaptureTheFlag = ini.Get_Bool(MPLAYER, "CaptureTheFlag", IsMPCaptureTheFlag); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * * + * Use this to set the recharge times for the various super weapons available. * + * * + * INPUT: ini -- Reference to the database. * + * * + * OUTPUT: bool; Was the recharge section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Recharge(CCINIClass & ini) +{ + static char const * const RECHARGE = "Recharge"; + if (ini.Is_Present(RECHARGE)) { + SonarTime = ini.Get_Fixed(RECHARGE, "Sonar", SonarTime); + ChronoTime = ini.Get_Fixed(RECHARGE, "Chrono", ChronoTime); + ParaBombTime = ini.Get_Fixed(RECHARGE, "ParaBomb", ParaBombTime); + ParaInfantryTime = ini.Get_Fixed(RECHARGE, "Paratrooper", ParaInfantryTime); + SpyTime = ini.Get_Fixed(RECHARGE, "SpyPlane", SpyTime); + IronCurtainTime = ini.Get_Fixed(RECHARGE, "IronCurtain", IronCurtainTime); + GPSTime = ini.Get_Fixed(RECHARGE, "GPS", GPSTime); + NukeTime = ini.Get_Fixed(RECHARGE, "Nuke", NukeTime); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * * + * This fetches the maximum heap sizes from the database specified. The heaps will be * + * initialized by this routine as indicated. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the maximum section found and processed? * + * * + * WARNINGS: This process is catastrophic to any data currently existing in the heaps * + * modified. This should only be processed during the game initialization stage. * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Heap_Maximums(CCINIClass & ini) +{ + /* + ** Heap maximum values. + */ + static char const * const MAXIMUMS = "Maximums"; + if (ini.Is_Present(MAXIMUMS)) { + MaxPlayers = ini.Get_Int(MAXIMUMS, "Players", MaxPlayers); + AircraftMax = ini.Get_Int(MAXIMUMS, "Aircraft", AircraftMax); + AnimMax = ini.Get_Int(MAXIMUMS, "Anim", AnimMax); + BuildingMax = ini.Get_Int(MAXIMUMS, "Building", BuildingMax); + BulletMax = ini.Get_Int(MAXIMUMS, "Bullet", BulletMax); + FactoryMax = ini.Get_Int(MAXIMUMS, "Factory", FactoryMax); + InfantryMax = ini.Get_Int(MAXIMUMS, "Infantry", InfantryMax); + OverlayMax = ini.Get_Int(MAXIMUMS, "Overlay", OverlayMax); + SmudgeMax = ini.Get_Int(MAXIMUMS, "Smudge", SmudgeMax); + TeamMax = ini.Get_Int(MAXIMUMS, "Team", TeamMax); + TeamTypeMax = ini.Get_Int(MAXIMUMS, "TeamType", TeamTypeMax); + TemplateMax = ini.Get_Int(MAXIMUMS, "Template", TemplateMax); + TerrainMax = ini.Get_Int(MAXIMUMS, "Terrain", TerrainMax); + TriggerMax = ini.Get_Int(MAXIMUMS, "Trigger", TriggerMax); + UnitMax = ini.Get_Int(MAXIMUMS, "Unit", UnitMax); + VesselMax = ini.Get_Int(MAXIMUMS, "Vessel", VesselMax); + ProjectileMax = ini.Get_Int(MAXIMUMS, "Projectile", ProjectileMax); + WeaponMax = ini.Get_Int(MAXIMUMS, "Weapon", WeaponMax); + WarheadMax = ini.Get_Int(MAXIMUMS, "Warhead", WarheadMax); + TrigTypeMax = ini.Get_Int(MAXIMUMS, "TrigType", TrigTypeMax); + } + + /* + ** Any heaps that use the maximums that were just loaded, must + ** be initialized as necessary. + */ + Warheads.Set_Heap(WarheadMax); + new WarheadTypeClass("SA"); + new WarheadTypeClass("HE"); + new WarheadTypeClass("AP"); + new WarheadTypeClass("Fire"); + new WarheadTypeClass("HollowPoint"); + new WarheadTypeClass("Super"); + new WarheadTypeClass("Organic"); + new WarheadTypeClass("Nuke"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WarheadTypeClass("Mechanical"); +#endif + + Weapons.Set_Heap(WeaponMax); + new WeaponTypeClass("Colt45"); + new WeaponTypeClass("ZSU-23"); + new WeaponTypeClass("Vulcan"); + new WeaponTypeClass("Maverick"); + new WeaponTypeClass("Camera"); + new WeaponTypeClass("FireballLauncher"); + new WeaponTypeClass("Sniper"); + new WeaponTypeClass("ChainGun"); + new WeaponTypeClass("Pistol"); + new WeaponTypeClass("M1Carbine"); + new WeaponTypeClass("Dragon"); + new WeaponTypeClass("Hellfire"); + new WeaponTypeClass("Grenade"); + new WeaponTypeClass("75mm"); + new WeaponTypeClass("90mm"); + new WeaponTypeClass("105mm"); + new WeaponTypeClass("120mm"); + new WeaponTypeClass("TurretGun"); + new WeaponTypeClass("MammothTusk"); + new WeaponTypeClass("155mm"); + new WeaponTypeClass("M60mg"); + new WeaponTypeClass("Napalm"); + new WeaponTypeClass("TeslaZap"); + new WeaponTypeClass("Nike"); + new WeaponTypeClass("8Inch"); + new WeaponTypeClass("Stinger"); + new WeaponTypeClass("TorpTube"); + new WeaponTypeClass("2Inch"); + new WeaponTypeClass("DepthCharge"); + new WeaponTypeClass("ParaBomb"); + new WeaponTypeClass("DogJaw"); + new WeaponTypeClass("Heal"); + new WeaponTypeClass("SCUD"); + new WeaponTypeClass("Flamer"); + new WeaponTypeClass("RedEye"); + +#ifdef FIXIT_ANTS + new WeaponTypeClass("Mandible"); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WeaponTypeClass("PortaTesla"); + new WeaponTypeClass("GoodWrench"); + new WeaponTypeClass("SubSCUD"); + new WeaponTypeClass("TTankZap"); + new WeaponTypeClass("APTusk"); + new WeaponTypeClass("Democharge"); +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new WeaponTypeClass("AirAssault"); +#endif + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::AI -- Processes the AI control constants from the database. * + * * + * This will examine the database specified and set the AI override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database that holds the AI overrides. * + * * + * OUTPUT: bool; Was the AI section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::AI(CCINIClass & ini) +{ + static char const * const AI = "AI"; + if (ini.Is_Present(AI)) { + AttackInterval = ini.Get_Fixed(AI, "AttackInterval", AttackInterval); + AttackDelay = ini.Get_Fixed(AI, "AttackDelay", AttackDelay); + PatrolTime = ini.Get_Fixed(AI, "PatrolScan", PatrolTime); + RepairThreshhold = ini.Get_Int(AI, "CreditReserve", RepairThreshhold); + PathDelay = ini.Get_Fixed(AI, "PathDelay", PathDelay); + TiberiumShortScan = ini.Get_Lepton(AI, "OreNearScan", TiberiumShortScan); + TiberiumLongScan = ini.Get_Lepton(AI, "OreFarScan", TiberiumLongScan); + AutocreateTime = ini.Get_Fixed(AI, "AutocreateTime", AutocreateTime); + InfantryReserve = ini.Get_Int(AI, "InfantryReserve", InfantryReserve); + InfantryBaseMult = ini.Get_Int(AI, "InfantryBaseMult", InfantryBaseMult); + PowerSurplus = ini.Get_Int(AI, "PowerSurplus", PowerSurplus); + BaseSizeAdd = ini.Get_Int(AI, "BaseSizeAdd", BaseSizeAdd); + RefineryRatio = ini.Get_Fixed(AI, "RefineryRatio", RefineryRatio); + RefineryLimit = ini.Get_Int(AI, "RefineryLimit", RefineryLimit); + BarracksRatio = ini.Get_Fixed(AI, "BarracksRatio", BarracksRatio); + BarracksLimit = ini.Get_Int(AI, "BarracksLimit", BarracksLimit); + WarRatio = ini.Get_Fixed(AI, "WarRatio", WarRatio); + WarLimit = ini.Get_Int(AI, "WarLimit", WarLimit); + DefenseRatio = ini.Get_Fixed(AI, "DefenseRatio", DefenseRatio); + DefenseLimit = ini.Get_Int(AI, "DefenseLimit", DefenseLimit); + AARatio = ini.Get_Fixed(AI, "AARatio", AARatio); + AALimit = ini.Get_Int(AI, "AALimit", AALimit); + TeslaRatio = ini.Get_Fixed(AI, "TeslaRatio", TeslaRatio); + TeslaLimit = ini.Get_Int(AI, "TeslaLimit", TeslaLimit); + HelipadRatio = ini.Get_Fixed(AI, "HelipadRatio", HelipadRatio); + HelipadLimit = ini.Get_Int(AI, "HelipadLimit", HelipadLimit); + AirstripRatio = ini.Get_Fixed(AI, "AirstripRatio", AirstripRatio); + AirstripLimit = ini.Get_Int(AI, "AirstripLimit", AirstripLimit); + IsCompEasyBonus = ini.Get_Bool(AI, "CompEasyBonus", IsCompEasyBonus); + IsComputerParanoid = ini.Get_Bool(AI, "Paranoid", IsComputerParanoid); + PowerEmergencyFraction = ini.Get_Fixed(AI, "PowerEmergency", PowerEmergencyFraction); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Powerups -- Process the powerup values from the database. * + * * + * This will examine the database and initialize the powerup override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database the the powerup values are to be * + * initialized from. * + * * + * OUTPUT: bool; Was the powerup section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Powerups(CCINIClass & ini) +{ + static char const * const POWERUPS = "Powerups"; + if (ini.Is_Present(POWERUPS)) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + char buffer[128]; + if (ini.Get_String(POWERUPS, CrateNames[crate], "0,NONE", buffer, sizeof(buffer))) { + + /* + ** Share odds. + */ + char * token = strtok(buffer, ","); + if (token) { + strtrim(token); + CrateShares[crate] = atoi(token); + } + + /* + ** Animation to use. + */ + token = strtok(NULL, ","); + if (token) { + strtrim(token); + CrateAnims[crate] = Anim_From_Name(token); + } + + /* + ** Optional data number. + */ + token = strtok(NULL, ","); + if (token != NULL) { + if (strpbrk(token, ".%") != NULL) { + CrateData[crate] = fixed(token) * 256; + } else { + strtrim(token); + CrateData[crate] = atoi(token); + } + } + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Land_Types -- Inits the land type values. * + * * + * This will set the land movement attributes from the database specified. * + * * + * INPUT: ini -- Reference to the database that has the land value overrides. * + * * + * OUTPUT: bool; Was the land type sections found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Land_Types(CCINIClass & ini) +{ + /* + ** Fetch the movement characteristic data for terrain types. + */ + for (LandType land = LAND_FIRST; land < LAND_COUNT; land++) { + static char const * _lands[LAND_COUNT] = { + "Clear", + "Road", + "Water", + "Rock", + "Wall", + "Ore", + "Beach", + "Rough", + "River" + }; + + GroundType * gptr = &Ground[land]; + + if (ini.Is_Present(_lands[land])) { + gptr->Cost[SPEED_FOOT] = Sub_Saturate(ini.Get_Fixed(_lands[land], "Foot", 1), 1); + gptr->Cost[SPEED_TRACK] = Sub_Saturate(ini.Get_Fixed(_lands[land], "Track", 1), 1); + gptr->Cost[SPEED_WHEEL] = Sub_Saturate(ini.Get_Fixed(_lands[land], "Wheel", 1), 1); + gptr->Cost[SPEED_WINGED] = Sub_Saturate(1, 1); + gptr->Cost[SPEED_FLOAT] = Sub_Saturate(ini.Get_Fixed(_lands[land], "Float", 1), 1); + gptr->Build = ini.Get_Bool(_lands[land], "Buildable", false); + } + } + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * * + * The musical theme availability is controlled by the scenario and the player's house * + * choice. These controls can be specified in the theme control section of the INI * + * database. * + * * + * INPUT: ini -- Reference to the INI database to process. * + * * + * OUTPUT: bool; Was the theme section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Themes(CCINIClass & ini) +{ + static char const * const THEMECONTROL = "ThemeControl"; + + if (ini.Is_Present(THEMECONTROL)) { + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (ini.Is_Present(THEMECONTROL, Theme.Base_Name(theme))) { + + char buffer[128]; + int scen = 1; + int owners = HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS; + + ini.Get_String(THEMECONTROL, Theme.Base_Name(theme), "", buffer, sizeof(buffer)); + char const * token = strtok(buffer, ","); + if (token != NULL) { + scen = atoi(token); + } + + token = strtok(NULL, ","); + if (token != NULL) { + owners = Owner_From_Name(token); + } + + Theme.Set_Theme_Data(theme, scen, owners); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * * + * This will scan the database specified and retrieve the IQ control values from it. These * + * IQ control values are what gives the IQ rating meaning. It fundimentally controls how * + * the computer behaves. * + * * + * INPUT: ini -- Reference to the INI database to read the IQ controls from. * + * * + * OUTPUT: bool; Was the IQ section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::IQ(CCINIClass & ini) +{ + static char const * const IQCONTROL = "IQ"; + if (ini.Is_Present(IQCONTROL)) { + MaxIQ = ini.Get_Int(IQCONTROL, "MaxIQLevels", MaxIQ); + IQSuperWeapons = ini.Get_Int(IQCONTROL, "SuperWeapons", IQSuperWeapons); + IQProduction = ini.Get_Int(IQCONTROL, "Production", IQProduction); + IQGuardArea = ini.Get_Int(IQCONTROL, "GuardArea", IQGuardArea); + IQRepairSell = ini.Get_Int(IQCONTROL, "RepairSell", IQRepairSell); + IQCrush = ini.Get_Int(IQCONTROL, "AutoCrush", IQCrush); + IQScatter = ini.Get_Int(IQCONTROL, "Scatter", IQScatter); + IQContentScan = ini.Get_Int(IQCONTROL, "ContentScan", IQContentScan); + IQAircraft = ini.Get_Int(IQCONTROL, "Aircraft", IQAircraft); + IQHarvester = ini.Get_Int(IQCONTROL, "Harvester", IQHarvester); + IQSellBack = ini.Get_Int(IQCONTROL, "SellBack", IQSellBack); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Objects -- Fetch all the object characteristic values. * + * * + * This will parse the specified INI database and fetch all the object characteristic * + * values specified therein. * + * * + * INPUT: ini -- Reference to the ini database to scan. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Objects(CCINIClass & ini) +{ + /* + ** Fetch the game object values from the rules file. + */ + for (int index = 0; index < Warheads.Count(); index++) { + Warheads.Ptr(index)->Read_INI(ini); + } + + for (int proj = 0; proj < BulletTypes.Count(); proj++) { + BulletTypes.Ptr(proj)->Read_INI(ini); + } + + for (int windex = 0; windex < Weapons.Count(); windex++) { + Weapons.Ptr(windex)->Read_INI(ini); + } + + for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { + UnitTypes.Ptr(uindex)->Read_INI(ini); + } + + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypes.Ptr(iindex)->Read_INI(ini); + } + + for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { + VesselTypes.Ptr(vindex)->Read_INI(ini); + } + + for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { + AircraftTypes.Ptr(aindex)->Read_INI(ini); + } + + for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { + BuildingTypes.Ptr(bindex)->Read_INI(ini); + } + + /* + ** Fetch the house attribute override values. + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTypeClass::As_Reference(house).Read_INI(ini); + } + + /* + ** Fetch the mission control values. + */ + for (MissionType mission = MISSION_FIRST; mission < MISSION_COUNT; mission++) { + MissionControlClass * miss = &MissionControl[mission]; + miss->Mission = mission; + miss->Read_INI(ini); + } + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * * + * This routine is used to fetch the various group settings for the difficulty levels. * + * * + * INPUT: ini -- Reference to the INI database that has the difficulty setting values. * + * * + * OUTPUT: bool; Was the difficulty section found and processed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Difficulty(CCINIClass & ini) +{ + Difficulty_Get(ini, Diff[DIFF_EASY], "Easy"); + Difficulty_Get(ini, Diff[DIFF_NORMAL], "Normal"); + Difficulty_Get(ini, Diff[DIFF_HARD], "Difficult"); + return(true); +} diff --git a/CODE/RULES.H b/CODE/RULES.H new file mode 100644 index 0000000..0a6a0ed --- /dev/null +++ b/CODE/RULES.H @@ -0,0 +1,885 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/RULES.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : RULES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RULES_H +#define RULES_H + +#include "ccini.h" + +class DifficultyClass +{ + public: + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + fixed RepairDelay; + fixed BuildDelay; + + unsigned IsBuildSlowdown:1; + unsigned IsWallDestroyer:1; + unsigned IsContentScan:1; +}; + +class RulesClass { + public: + + RulesClass(void); + + bool Process(CCINIClass & file); + bool General(CCINIClass & ini); + bool MPlayer(CCINIClass & ini); + bool Recharge(CCINIClass & ini); + bool Heap_Maximums(CCINIClass & ini); + bool AI(CCINIClass & ini); + bool Powerups(CCINIClass & ini); + bool Land_Types(CCINIClass & ini); + bool Themes(CCINIClass & ini); + bool IQ(CCINIClass & ini); + bool Objects(CCINIClass & ini); + bool Difficulty(CCINIClass & ini); + + /* + ** This specifies the turbo boost speed for missiles when they are fired upon + ** aircraft and the weapon is specified as having a turbo boost bonus. + */ + fixed TurboBoost; + + /* + ** This specifies the average number of minutes between each computer attack. + */ + fixed AttackInterval; + + /* + ** This specifies the average minutes delay before the computer will begin + ** its first attack upon the player. The duration is also modified by the + ** difficulty level. + */ + fixed AttackDelay; + + /* + ** If the power ratio falls below this percentage, then a power emergency is + ** in effect. At such times, the computer might decide to sell off some + ** power hungry buildings in order to alleviate the situation. + */ + fixed PowerEmergencyFraction; + + /* + ** The number of badgers that arrive when the parabomb option is used. + */ + int BadgerBombCount; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of airstrips. + */ + fixed AirstripRatio; + + /* + ** Limit the number of airstrips to this amount. + */ + int AirstripLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of helipads. + */ + fixed HelipadRatio; + + /* + ** Limit the number of helipads to this amount. + */ + int HelipadLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of Tesla Coils. + */ + fixed TeslaRatio; + + /* + ** Limit tesla coil production to this maximum. + */ + int TeslaLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of anti-aircraft defense. + */ + fixed AARatio; + + /* + ** Limit anti-aircraft building quantity to this amount. + */ + int AALimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of defensive structures. + */ + fixed DefenseRatio; + + /* + ** This is the limit to the number of defensive building that can be built. + */ + int DefenseLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of war factories. + */ + fixed WarRatio; + + /* + ** War factories are limited to this quantity for the computer controlled player. + */ + int WarLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of infantry producing structures. + */ + fixed BarracksRatio; + + /* + ** No more than this many barracks can be built. + */ + int BarracksLimit; + + /* + ** Refinery building is limited to this many refineries. + */ + int RefineryLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of refineries. + */ + fixed RefineryRatio; + + /* + ** The computer is limited in the size of the base it can build. It is limited to the + ** size of the largest human opponent base plus this surplus count. + */ + int BaseSizeAdd; + + /* + ** If the power surplus is less than this amount, then the computer will + ** build power plants. + */ + int PowerSurplus; + + /* + ** The computer will build infantry if their cash reserve is greater than this amount. + */ + int InfantryReserve; + + /* + ** This factor is multiplied by the number of buildings in the computer's base and infantry + ** are always built until it matches that number. + */ + int InfantryBaseMult; + + /* + ** This specifies the duration that a unit will remain chronoshifted before it + ** will be returned to its starting location. + */ + fixed ChronoDuration; + + /* + ** Percent chance that a water crate will be generated instead of a land + ** crate when crates are on and in a multiplay game. + */ + fixed WaterCrateChance; + + /* + ** Solo play has money crate amount fixed according to this rule value. + */ + int SoloCrateMoney; + + /* + ** GPS tech level control. + */ + int GPSTechLevel; + + /* + ** If a unit type is specified here, then the unit crate will generate + ** a unit of this type (always). + */ + UnitType UnitCrateType; + + /* + ** This is the time to delay between patrol-to-waypoint target scanning. + */ + fixed PatrolTime; + + /* + ** This is the time interval that checking to create teams will span. The + ** smaller this number, the more often checking for team creation will occur. + */ + fixed TeamDelay; + + /* + ** This is the arbitrary delay to make all cloaking objects remain uncloaked + ** before having it recloak. + */ + fixed CloakDelay; + + /* + ** This is an overall game apparent speed bias to use for object + ** movement purposes. + */ + fixed GameSpeedBias; + + /* + ** If a potential target is close to the base then increase + ** the likelyhood of attacking it by this bias factor. + */ + fixed NervousBias; + + /* + ** Controls the Chronal vortex characteristics. + */ + LEPTON VortexRange; + MPHType VortexSpeed; + int VortexDamage; + fixed VortexChance; + + /* + ** When an explosive object explodes, the damage will spread out + ** by this factor. The value represents the number of cells radius + ** that the damage will spread for every 100 points of raw damage at + ** the explosion center point. + */ + fixed ExplosionSpread; + + /* + ** For weapons specially marked to check for nearby friendly buildings + ** when scanning for good targets, this indicates the scan radius. Such + ** weapons will supress firing on enemies if they are in close proximity + ** to allied buildings. + */ + LEPTON SupressRadius; + + /* + ** This is the tech level that para infantry are granted free to the owner + ** of an airstrip. + */ + int ParaInfantryTechLevel; + + /* + ** This is the tech level that spy planes are granted free to the owner of + ** an airstrip. + */ + int SpyPlaneTechLevel; + + /* + ** This is the tech level that the parabombs are granted free to the owner + ** of an airstrip. + */ + int ParaBombTechLevel; + + /* + ** This is the maximum number of IQ settings available. The human player is + ** presumed to be at IQ level zero. + */ + int MaxIQ; + + /* + ** The IQ level at which super weapons will be automatically fired by the computer. + */ + int IQSuperWeapons; + + /* + ** The IQ level at which production is automatically controlled by the computer. + */ + int IQProduction; + + /* + ** The IQ level at which newly produced units start out in guard area mode instead + ** of normal guard mode. + */ + int IQGuardArea; + + /* + ** The IQ level at which the computer will be able to decide what gets repaired + ** or sold. + */ + int IQRepairSell; + + /* + ** At this IQ level or higher, a unit is allowed to automatically try to crush + ** an atagonist if possible. + */ + int IQCrush; + + /* + ** The unit/infantry will try to scatter if an incoming threat + ** is detected. + */ + int IQScatter; + + /* + ** Tech level at which the computer will scan the contents of a transport + ** in order to pick the best target to fire upon. + */ + int IQContentScan; + + /* + ** Aircraft replacement production occurs at this IQ level or higher. + */ + int IQAircraft; + + /* + ** Checks for and replaces lost harvesters. + */ + int IQHarvester; + + /* + ** Is allowed to sell a structure being damaged. + */ + int IQSellBack; + + /* + ** The silver and wood crates in solo play will have these powerups. + */ + CrateType SilverCrate; + CrateType WoodCrate; + CrateType WaterCrate; + + /* + ** This specifies the minimum number of crates to place on the map in spite + ** of the number of actual human players. + */ + int CrateMinimum; + + /* + ** This specifies the crate maximum quantity to use. + */ + int CrateMaximum; + + /* + ** Landing zone maximum alternate zone scan radius. + */ + LEPTON LZScanRadius; + + /* + ** Multiplayer default settings. + */ + int MPDefaultMoney; + int MPMaxMoney; + unsigned IsMPShadowGrow:1; + unsigned IsMPBasesOn:1; + unsigned IsMPTiberiumGrow:1; + unsigned IsMPCrates:1; + unsigned IsMPAIPlayers:1; + unsigned IsMPCaptureTheFlag:1; + + /* + ** Drop zone reveal radius. + */ + LEPTON DropZoneRadius; + + /* + ** This is the delay that multiplayer messages will remain on the screen. + */ + fixed MessageDelay; + + /* + ** Savour delay between when scenario detects end and the actual + ** end of the play. + */ + fixed SavourDelay; + + /* + ** This specifies the damage to inflict for two differnt styles of + ** land mine. + */ + int AVMineDamage; + int APMineDamage; + + /* + ** This is the maximum number of multiplayers allowed. + */ + int MaxPlayers; + + /* + ** This is the delay between 'panic attacks' when the computer's base is under + ** attack. This delay gives the previously assigned units a chance to affect the + ** attacker before the computer sends more. + */ + fixed BaseDefenseDelay; + + /* + ** These values control the team suspension logic for dealing with immedate base threats. + ** When the base is attacked, all teams with less than the specified priority will be + ** temporarily put on hold for the number of minutes specified. + */ + int SuspendPriority; + fixed SuspendDelay; + + /* + ** This serves as the fraction of a building's original cost that is converted + ** into survivors (of some fashion). There are rounding and other marginal + ** fudge effects, but this value is the greatest control over the survivor rate. + */ + fixed SurvivorFraction; + + /* + ** This is the aircraft reload rate expressed in minutes per ammo load. + */ + fixed ReloadRate; + + /* + ** The average time (in minutes) between the computer autocreating a team + ** from the team's autocreate list. + */ + fixed AutocreateTime; + + /* + ** Build up time for buildings (minutes). + */ + fixed BuildupTime; + + /* + ** Ore truck speed for dumping. + */ + int OreDumpRate; + + /* + ** This is the amount of damage done by the atom bomb in solo missions. The + ** damage done during multiplay will be 1/5th this value. + */ + int AtomDamage; + + /* + ** This array controls the difficulty affects on the game. There is one + ** difficulty class object for each difficulty level. + */ + DifficultyClass Diff[DIFF_COUNT]; + + /* + ** Is the computer paranoid? If so, then it will band together with other computer + ** paranoid players when the situation looks rough. + */ + bool IsComputerParanoid:1; + + /* + ** Should helicopters shuffle their position between firing on their + ** target? + */ + bool IsCurleyShuffle:1; + + /* + ** Flash the power bar when the power goes below 100%. + */ + bool IsFlashLowPower:1; + + /* + ** If the computer players will go to easy mode if there is more + ** than one human player, this flag will be true. + */ + bool IsCompEasyBonus:1; + + /* + ** If fine control of difficulty settings is desired, then set this value to true. + ** Fine control allows 5 settings. The coarse control only allows three settings. + */ + bool IsFineDifficulty:1; + + /* + ** If the harvester is to explode more violently than normal + ** if it is carrying cargo, then this flag will be true. + */ + unsigned IsExplosiveHarvester:1; + + /* + ** Show the health bar on the enemy units? + */ + unsigned IsHealthBar:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + */ + unsigned IsMCVDeploy:1; + + /* + ** If the base is to be revealed to a new ally, then this + ** flag will be true. + */ + unsigned IsAllyReveal:1; + + /* + ** Can the helipad (and airfield) be purchased separately from the associated + ** aircraft. + */ + unsigned IsSeparate:1; + + /* + ** Give target cursor for trees? Doing this will make targetting of trees easier. + */ + unsigned IsTreeTarget:1; + + /* + ** Are friendly units automatically aware of mines so that they can avoid them? + */ + unsigned IsMineAware:1; + + /* + ** If Tiberium is allowed to grow, then this flag will be true. + */ + unsigned IsTGrowth:1; + + /* + ** If Tiberium is allowed to spread, then this flag will be true. + */ + unsigned IsTSpread:1; + + /* + ** Should civilan buildings and civilians display their true name rather than + ** the generic "Civilian Building" and "Civilain"? + */ + unsigned IsNamed:1; + + /* + ** Should player controlled vehicles automatically try to crush nearby infantry + ** instead of required the player to manually direct them to crush. + */ + unsigned IsAutoCrush:1; + + /* + ** Should the player controlled buildings and units automatically return fire when + ** fired upon? + */ + unsigned IsSmartDefense:1; + + /* + ** Should player controlled units try to scatter more easily in order to + ** avoid damage or threats? + */ + unsigned IsScatter:1; + + /* + ** If the chronoshift effect should kill all cargo, then this flag will + ** be set to true. + */ + unsigned IsChronoKill:1; + + /* + ** When infantry are prone or when civilians are running around like crazy, + ** they are less prone to damage. This specifies the multiplier to the damage + ** (as a fixed point number). + */ + fixed ProneDamageBias; + + /* + ** The time quake will do this percentage of damage to all units and buildings + ** in the game. The number is expressed as a fixed point percentage. + */ + fixed QuakeDamagePercent; + + /* + ** Percentage chance that a time quake will occur with each chronoshift use. + */ + fixed QuakeChance; + + /* + ** Ore (Tiberium) growth rate. The value is the number of minutes between + ** growth steps. + */ + fixed GrowthRate; + + /* + ** This specifies the number of minutes between each shroud regrowth process. + */ + fixed ShroudRate; + + /* + ** This is the average minutes between each generation of a random crate + ** to be placed on the map if generating of random crates is indicated. + */ + fixed CrateTime; + + /* + ** This specifies the number of minutes remaining before that if the mission timer + ** gets to this level or below, it will be displayed in red. + */ + fixed TimerWarning; + + /* + ** This specifies the minutes of delay between recharges for these + ** special weapon types. + */ + fixed SonarTime; + fixed ChronoTime; + fixed ParaBombTime; + fixed ParaInfantryTime; + fixed ParaSaboteurTime; + fixed SpyTime; + fixed IronCurtainTime; + fixed GPSTime; + fixed NukeTime; + + /* + ** Other miscellaneous delay times. + */ + fixed SpeakDelay; + fixed DamageDelay; + + /* + ** This is the gravity constant used to control the arcing and descent of ballistic + ** object such as grenades and artillery. + */ + int Gravity; + + /* + ** Gap generators have a shroud radius of this many cells. + */ + int GapShroudRadius; + + /* + ** This is the minute interval between the gap generators refreshing + ** their zones of gapping. + */ + fixed GapRegenInterval; + + /* + ** Mobile radar jammer radius of effect. + */ + LEPTON RadarJamRadius; + + /* + ** The speed at which a projectile that travels at or slower will cause + ** objects in the target location to scatter. This simulates the ability + ** of targets to run for cover if the projectile gives them enough time + ** to react. + */ + MPHType Incoming; + + /* + ** Minimum and maximum damage allowed per shot. + */ + int MinDamage; + int MaxDamage; + + /* + ** This is the rate of repair for units and buildings. The rate is the + ** number of strength points repaired per repair clock tick. The cost of + ** repair is the (fixed point) fractional cost to repair the object based + ** on the full price of the object. Example; a value of 50% means that to + ** repair the object from 1 damage point to full strength would cost 50% of + ** the cost to build it from scratch. + */ + int RepairStep; + fixed RepairPercent; + int URepairStep; + fixed URepairPercent; + + /* + ** This is the rate that objects with self healing will heal. They will repair a bit + ** every 'this' number of minutes. + */ + fixed RepairRate; + + /* + ** These fixed point values are used to determine the status (health bar + ** color) of the game objects. Objects in the 'yellow' are in a cautionary + ** state. Object in the 'red' are in a danger state. + */ + fixed ConditionGreen; + fixed ConditionYellow; + fixed ConditionRed; + + /* + ** Average number of minutes between infantry random idle animations. + */ + fixed RandomAnimateTime; + + /* + ** These control the capacity and value of the ore types that a harvester + ** may carry. The harvester carries a maximum discrete number of 'bails'. + ** The value of each bail depends on the ore it is composed of. + */ + int BailCount; // was STEP_COUNT + int GoldValue; // was GOLD_WORTH + int GemValue; // was GEM_WORTH + + /* + ** This specifies the heap maximum for the various game objects. + */ + int AircraftMax; + int AnimMax; + int BuildingMax; + int BulletMax; + int FactoryMax; + int InfantryMax; + int OverlayMax; + int SmudgeMax; + int TeamMax; + int TeamTypeMax; + int TemplateMax; + int TerrainMax; + int TriggerMax; + int UnitMax; + int VesselMax; + int ProjectileMax; + int WeaponMax; + int WarheadMax; + int TrigTypeMax; + + /* + ** Close enough distance that is used to determine if the object should + ** stop movement when blocked. If the distance to the desired destination + ** is equal to this distance or less, but the path is blocked, then consider + ** the object to have gotten "close enough" to the destination to stop. + */ + LEPTON CloseEnoughDistance; + + /* + ** Stray distance to group team members within. The larger the distance, + ** the looser the teams will move. + */ + LEPTON StrayDistance; + + /* + ** If a vehicle is closer than this range to a target that it can crush + ** by driving over it, then it will try to drive over it instead of firing + ** upon it. The larger the value, the greater the 'bigfoot crush syndrome' is + ** has. + */ + LEPTON CrushDistance; + + /* + ** For area effect crate bonus items will affect all objects within this radius. + */ + LEPTON CrateRadius; + + /* + ** Maximum scatter distances for homing and non-homing projectiles. + */ + LEPTON HomingScatter; + LEPTON BallisticScatter; + + /* + ** This is the refund percentage when selling off buildings and units + ** on the repair pad (service depot). + */ + fixed RefundPercent; + + /* + ** The Iron Curtain invulnerability effect lasts for this many minutes. + */ + fixed IronCurtainDuration; + + /* + ** The strength of bridges is held here. By corollary, the strength of the + ** demolition charge carried by Tanya is equal to this value as well. + */ + int BridgeStrength; + + /* + ** This is the overall build speed bias. Multiply this value by the normal build + ** delay to get the effective build delay. + */ + fixed BuildSpeedBias; + + /* + ** Weapon type array pointer should go here. Dynamic type. + */ + + + /* + ** Warhead type class array pointer should go here. Dynamic type. + */ + + + /* + ** Ground type and speed affect data should go here. + */ + + + /* + ** This is the delay between the time a C4 bomb is planted and the time it will + ** explode. The longer the delay, the greater safety margin for a demolitioner + ** type. The short the delay, the less time the victim has to sell the building + ** off. + */ + fixed C4Delay; + + /* + ** The computer will only repair a structure if it has spare money greater than this + ** amount. The thinking is that this will prevent the computer from frittering away + ** all it's cash on repairing and thus leaving nothing for production of defenses. + */ + int RepairThreshhold; + + /* + ** This is the delay (in minutes) between retries of a failed path. The longer the + ** delay the faster the system, but the longer the units take to react to a blocked + ** terrain event. + */ + fixed PathDelay; + + /* + ** This is the special (debug version only) movie recorder timeout value. Each second + ** results in about 2-3 megabytes. + */ + fixed MovieTime; + + /* + ** This is the level at or above which the chronosphere facility can + ** actually produce the chronosphere effect. Below this tech level, + ** the facility is merely a showpiece and has no effect. + */ + int ChronoTechLevel; + + /* + ** These are the Tiberium scan distances. The short range scan is used to determine if the + ** current field has been exhausted. The long range scan is used when finding a Tiberium + ** field to harvest. Keep these ranges as small as possible. + */ + LEPTON TiberiumShortScan; + LEPTON TiberiumLongScan; +}; + + +#endif diff --git a/CODE/RULES.MAK b/CODE/RULES.MAK new file mode 100644 index 0000000..031ee00 --- /dev/null +++ b/CODE/RULES.MAK @@ -0,0 +1,386 @@ +# +# 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 . +# + +# Size of the program shape buffer (passed to MAKESHPS.EXE) +SHAPEBUFFSIZE = 20000 + +# Default paths to use +ROOTCD = v:\projects\c&c0\cstrike2\cd\\ +ROOTDIR = ..\\ +MAPDIR = ..\\ +ROOTCODEDIR = .\\ +ABSROOT = ..\\ +WAVDIR = f:\projects\c&c0\audio\amingame\\ +SCOREDIR = f:\projects\c&c0\audio\amingame\scores\\ +VQADIR = f:\projects\c&c0\art\movies\\ +LANGDIR = +INGAMEDIR = ingame +TEMPDIR = s: + + +# Override paths +!if $d(GERMAN) +ROOTCD = v:\projects\c&c0\cstrike2\cdger\\ +ROOTDIR = ..\\ +MAPDIR = ..\ger +ROOTCODEDIR = .\\ +ABSROOT = ..\\ +WAVDIR = f:\projects\c&c0\audio\amingame\\ +LANGDIR = \german +INGAMEDIR = ingamegr +#VQADIR = s:t\\ +VQADIR = t:movies\\ +TEMPDIR = s: +!endif + +!if $d(FRENCH) +ROOTCD = v:\projects\c&c0\cstrike2\cdfre\\ +ROOTDIR = ..\\ +MAPDIR = ..\fre +ROOTCODEDIR = .\\ +ABSROOT = ..\\ +WAVDIR = f:\projects\c&c0\audio\amingame\ +LANGDIR = \french +INGAMEDIR=ingamefr +VQADIR = t:movies\\ +TEMPDIR = s: +!endif + + +# Directory where temporary mixfiles can be stored. +!if $d(TEMP_MIX_DIR) +TEMP_MIX_DIR = $(TEMP_MIX_DIR) +!else +TEMP_MIX_DIR = $(TEMPDIR)t\m +!endif + +!if $d(CPS) +TEMP_CPS_DIR = $(CPS) +!else +TEMP_CPS_DIR = $(TEMPDIR)t +!endif + + +#.path.a6a = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a6j = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a8 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a80 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a81 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a82 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a83 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a84 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a8a = $(TEMP_CPS_DIR)\\ # Game formatted audio data +#.path.a8j = $(TEMP_CPS_DIR)\\ # Game formatted audio data +.path.a6 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +.path.anm = $(ROOTDIR)art\$(INGAMEDIR)\\ # Raw animation file +.path.ash = .\\ # Autogenerated assembly header file +.path.asm = .\\ # Raw assembly file +.path.aud = $(TEMP_CPS_DIR)\\ # Game formatted audio data +.path.c = .\\ # Raw C file +.path.cd1 = $(ROOTCD)CD4\\ # CD #1 (Allies) +.path.cd2 = $(ROOTCD)CD2\\ # CD #1 (Soviet) +.path.cpp = .\\ # Raw C++ file +.path.cps = $(TEMP_CPS_DIR)\\ # Compressed art +.path.dat = $(TEMP_CPS_DIR)\\ # Generic data +.path.des = $(TEMP_CPS_DIR)\\ # Desert icons +.path.dip = $(TEMP_CPS_DIR)\\ # Generic compressed text (obsolete) +.path.eng = $(TEMP_CPS_DIR)\\ # English compressed text +.path.exe = $(ROOTDIR)run\\ # Executable (relative path from OBJ directory) +.path.fin = $(ROOTDIR)art\ingame\fin\\ # Finale source animation +.path.fnt = $(TEMP_CPS_DIR)\\ # Font (compressed) +.path.foc = $(ROOTDIR)audio\french\\ # French digitized voices +.path.fre = $(TEMP_CPS_DIR)\\ # French compressed text +.path.gdi = $(TEMP_CPS_DIR)\\ # Good guy compressed files +.path.ger = $(TEMP_CPS_DIR)\\ # German compressed text +.path.goc = $(ROOTDIR)audio\german\\ # German digitized voices +.path.h = .\\ # C header file +.path.hi = $(TEMP_CPS_DIR)\\ # Shape file (processed) +.path.hnt = $(TEMP_CPS_DIR)\\ # Font (compressed) +.path.i = .\\ # Assembly header +.path.icn = $(TEMP_CPS_DIR)\\ # Icon file (compressed) +.path.ini = $(MAPDIR)maps\\ # INI control files +.path.int = $(TEMP_CPS_DIR)\\ # Interior icons +.path.j6 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +.path.jp = $(TEMP_CPS_DIR)\\ # Dinosaur compressed files +.path.jun = $(TEMP_CPS_DIR)\\ # Jungle icons +.path.juv = $(TEMP_CPS_DIR)\\ # Juvenile sound effects. +.path.lbm = $(ROOTDIR)art\ingame\\ # Source art (uncompressed) +.path.lnt = $(TEMP_CPS_DIR)\\ # Font (compressed) +.path.low = $(TEMP_CPS_DIR)\\ # Shape file (processed) +.path.lut = $(TEMP_CPS_DIR)\\ # Look up tables +.path.mak = .\\ # Shape making control file +.path.map = $(TEMP_CPS_DIR)\\ # Icon map data file +.path.mid = $(ROOTDIR)audio\mid\\ # Midi file +.path.mix = $(TEMP_MIX_DIR)\\ # Mix-file location. +.path.mpr = $(MAPDIR)maps\\ # Multiplayer scenario files +.path.mrf = $(TEMP_CPS_DIR)\\ # Palette morph data files +.path.nod = $(TEMP_CPS_DIR)\\ # Bad guy compressed files +.path.o = obj\\ # Overlay object file +.path.obj = obj\\ # Object file +.path.out = .\\ # Temporary parser file. +.path.pak = $(ROOTDIR)run\\ # Packed file +.path.pal = $(TEMP_CPS_DIR)\\ # Palette file (processed) +.path.pcx = $(TEMP_CPS_DIR)\\ #.pcx files +.path.pkt = $(MAPDIR)maps\\ # Scenario grouping control files +.path.r00 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.r01 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.r02 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.r03 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.shp = $(TEMP_CPS_DIR)\\ # Shape file (processed) +.path.sno = $(TEMP_CPS_DIR)\\ # Snow icons +.path.sym = .\\ # Precompiled header symbol file. +.path.tbl = $(TEMP_CPS_DIR)\\ # Remap table file. +.path.tem = $(TEMP_CPS_DIR)\\ # Temperate icons +.path.txt = $(ROOTDIR)code\eng\\ # Default location for text files. +.path.v00 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.v01 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.v02 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.v03 = $(TEMP_CPS_DIR)\\ # Infantry response. +.path.v16 = $(TEMP_CPS_DIR)\\ # Game formatted audio data +.path.vqa = $(VQADIR)vq$(LANGDIR)\\ # VQA movie files location. +.path.vqp = $(VQADIR)vq$(LANGDIR)\\ # VQA Movie Interpolated palette files +!if $d(FRENCH) +.path.vqa = $(VQADIR)vq\french\\ # VQA movie files location. +.path.vqp = $(VQADIR)vq\french\\ # VQA Movie Interpolated palette files +!endif +.path.wav = $(WAVDIR) # Digitized sample file (generic) +.path.win = $(TEMP_CPS_DIR)\\ # Winter icons +.path.wsa = $(TEMP_CPS_DIR)\\ # Processed animation file +.path.wv = $(ROOTDIR)audio\wv\\ # Digitized sample file (generic) +.path.xmi = $(ROOTDIR)audio\xmi\\ # MT-32 midi scores + + +########################################################################## +# Rules + +# +# Rule for converting anim files. +# +.anm.wsa: + utils\animate $*.anm $(.path.anm)$&.lbm $(.path.wsa)$&.wsa -l -f -p + +# +# Rule for converting art files. +# +.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + +# +# Rule for converting outtake first frame art files. +# +{$(.path.lbm)outtake}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex + +{$(.path.anm)outtake}.anm.wsa: + utils\animate $*.anm $(.path.anm)outtake\$&.lbm $(.path.wsa)$&.wsa -l -f -p + +{$(.path.anm)score}.anm.wsa: + utils\animate $*.anm $(.path.wsa)$&.wsa -p + +######################################################### +# Rule for creating palette files. +# +{$(.path.lbm)palettes}.lbm.pal: + utils\wwcomp $*.lbm -ppal + copy $&.pal $(.path.pal)$&.pal + del $&.pal + +{$(.path.lbm)score}.lbm.pal: + utils\wwcomp $*.lbm -ppal + copy $&.pal $(.path.pal)$&.pal + del $&.pal + +######################################################### +# Rule for creating vehicles. +{$(.path.anm)units}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for creating buildings. +{$(.path.anm)building}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for creating misc shape animations. +{$(.path.anm)anim}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for converting font files. +{$(.path.lbm)fonts}.lbm.fnt: + echo Creating font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.fnt)$&.fnt + +{$(.path.lbm)hires}.lbm.hnt: + echo Creating hi-res font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.hnt)$&.hnt + +{$(.path.lbm)hires}.lbm.fnt: + echo Creating lo-res font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.hnt)$&.fnt + +{$(.path.lbm)lores}.lbm.lnt: + echo Creating lo-res font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.lnt)$&.lnt + +{$(.path.lbm)lores}.lbm.fnt: + echo Creating lo-res font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.lnt)$&.fnt + +######################################################### +# Rules for creating overlay files. +{$(.path.lbm)overlay}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps + +{$(.path.anm)overlay}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rules for creating temperate files. +{$(.path.lbm)temperat}.lbm.tem: + utils\iconmap -r -w3 -o$(.path.tem)$&.tem $*.lbm + +{$(.path.anm)temperat}.anm.tem: + utils\keyframe $*.anm $(.path.tem)$&.tem -l + +######################################################### +# Rules for creating interior files. +{$(.path.lbm)interior}.lbm.int: + utils\iconmap -r -w3 -o$(.path.int)$&.int $*.lbm + +{$(.path.anm)interior}.anm.int: + utils\keyframe $*.anm $(.path.int)$&.int -l + +######################################################### +# Rules for creating winter files. +{$(.path.lbm)snow}.lbm.sno: + utils\iconmap -r -w3 -o$(.path.sno)$&.sno $*.lbm + +{$(.path.anm)snow}.anm.sno: + utils\keyframe $*.anm $(.path.sno)$&.sno -l + +######################################################### +# Rules for creating desert files. +{$(.path.lbm)desert}.lbm.des: + utils\iconmap -r -w3 -o$(.path.des)$&.des $*.lbm + +{$(.path.anm)desert}.anm.des: + utils\keyframe $*.anm $(.path.des)$&.des -l + +######################################################### +# Generic icon file creation. +.lbm.icn: + utils\iconmap -r -w3 -o$(.path.icn)$&.icn $*.lbm + + +######################################################### +# Text files. +{eng}.txt.eng: + utils\textmake $*.txt $(.path.eng)$&.eng $&.h + +{ger}.txt.ger: + utils\textmake $*.txt $(.path.ger)$&.ger $&.h + +{fre}.txt.fre: + utils\textmake $*.txt $(.path.fre)$&.fre $&.h + +######################################################### +# Score/mapsel files. +{$(.path.anm)score}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +{$(.path.lbm)score}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + + +######################################################### +# Generic shape files. +#.lbm.shp: +# utils\makeshps -q $*.lbm $&.mak $(.path.cps)$&.shp $(SHAPEBUFFSIZE) + +{$(.path.anm)generic}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +.c.lob: + utils\LINT -ml -si2 -sl4 +v $(WWLIB)WWLIB.LNT $*.c >$&.err + +# Generic shape file creation. +{$(.path.anm)}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +{$(.path.lbm)units}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + + +########################################################## +# Hi-res data files +# +{$(.path.anm)hires}.anm.hi: + utils\keyframe $*.anm $(.path.hi)$&.hi -l + +# Hi-res font files +{$(.path.lbm)hires}.lbm.fnt: + echo Creating font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.fnt)$&.fnt + +######################################################### +# Rule for creating low res data files. +{$(.path.anm)lores}.anm.low: + utils\keyframe $*.anm $(.path.low)$&.low -l + +{$(.path.lbm)lores}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + +####################################### +# Rules to convert 16 bit audio files. +{$(SCOREDIR)}.wav.aud: + utils\audiomak $(SCOREDIR)$&.wav $(.path.aud)$&.aud + +{$(.path.wav)speech$(LANGDIR)}.wav.aud: + utils\audiomak $(.path.wav)speech$(LANGDIR)\$&.wav $(.path.aud)$&.aud + +{$(.path.wav)sfx\tanya$(LANGDIR)}.wav.aud: + utils\audiomak $(.path.wav)sfx\tanya$(LANGDIR)\$&.wav $(.path.aud)$&.aud + +{$(.path.wav)sfx$(LANGDIR)}.wav.aud: + utils\audiomak $(.path.wav)sfx$(LANGDIR)\$&.wav $(.path.aud)$&.aud + +{$(.path.wav)sfx\v00$(LANGDIR)}.wav.v00: + utils\audiomak $(.path.wav)sfx\v00$(LANGDIR)\$&.wav $(.path.aud)$&.v00 + +{$(.path.wav)sfx\v01$(LANGDIR)}.wav.v01: + utils\audiomak $(.path.wav)sfx\v01$(LANGDIR)\$&.wav $(.path.aud)$&.v01 + +{$(.path.wav)sfx\v02$(LANGDIR)}.wav.v02: + utils\audiomak $(.path.wav)sfx\v02$(LANGDIR)\$&.wav $(.path.aud)$&.v02 + +{$(.path.wav)sfx\v03$(LANGDIR)}.wav.v03: + utils\audiomak $(.path.wav)sfx\v03$(LANGDIR)\$&.wav $(.path.aud)$&.v03 + +{$(.path.wav)sfx\r00$(LANGDIR)}.wav.r00: + utils\audiomak $(.path.wav)sfx\r00$(LANGDIR)\$&.wav $(.path.aud)$&.r00 + +{$(.path.wav)sfx\r01$(LANGDIR)}.wav.r01: + utils\audiomak $(.path.wav)sfx\r01$(LANGDIR)\$&.wav $(.path.aud)$&.r01 + +{$(.path.wav)sfx\r02$(LANGDIR)}.wav.r02: + utils\audiomak $(.path.wav)sfx\r02$(LANGDIR)\$&.wav $(.path.aud)$&.r02 + +{$(.path.wav)sfx\r03$(LANGDIR)}.wav.r03: + utils\audiomak $(.path.wav)sfx\r03$(LANGDIR)\$&.wav $(.path.aud)$&.r03 + diff --git a/CODE/SAVEDLG.H b/CODE/SAVEDLG.H new file mode 100644 index 0000000..37df7e2 --- /dev/null +++ b/CODE/SAVEDLG.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SAVEDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SAVEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SAVEDLG_H +#define SAVEDLG_H + +#include "gadget.h" + +class SaveOptionsClass +{ + private: + + enum SaveOptionsClassEnums { + BUTTON_CANCEL=200, + BUTTON_SAVE, + OPTION_WIDTH=216, + OPTION_HEIGHT=122, + OPTION_X=((320 - OPTION_WIDTH) / 2) & ~7, + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + NUMBER_OF_BUTTONS=2, + CAPTION_Y_POS=5, + BORDER1_LEN=49, + BUTTON_CANCEL_X=90, + BUTTON_CANCEL_Y=103, + LISTBOX_X=40, + LISTBOX_Y=24, + LISTBOX_W=136, + LISTBOX_H=72 + }; + + public: + SaveOptionsClass (void) { }; + void Process (void); +}; + + +#endif diff --git a/CODE/SAVELOAD.CPP b/CODE/SAVELOAD.CPP new file mode 100644 index 0000000..061a11d --- /dev/null +++ b/CODE/SAVELOAD.CPP @@ -0,0 +1,1563 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/SAVELOAD.CPP 9 3/17/97 1:04a Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SAVELOAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 23, 1994 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Code_All_Pointers -- Code all pointers. * + * Decode_All_Pointers -- Decodes all pointers. * + * Get_Savefile_Info -- gets description, scenario #, house * + * Load_Game -- loads a saved game * + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * Load_Misc_Values -- loads miscellaneous variables * + * MPlayer_Save_Message -- pops up a "saving..." message * + * Put_All -- Store all save game data to the pipe. * + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * Save_Game -- saves a game to disk * + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * Save_Misc_Values -- saves miscellaneous variables * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +//#include "WolDebug.h" + +extern bool SpawnedFromWChat; +#endif + +//#define SAVE_BLOCK_SIZE 512 +#define SAVE_BLOCK_SIZE 4096 +//#define SAVE_BLOCK_SIZE 1024 + +/* +********************************** Defines ********************************** +*/ +#define SAVEGAME_VERSION (DESCRIP_MAX + \ + 0x01000006 + ( \ + sizeof(AircraftClass) + \ + sizeof(AircraftTypeClass) + \ + sizeof(AnimClass) + \ + sizeof(AnimTypeClass) + \ + sizeof(BaseClass) + \ + sizeof(BuildingClass) + \ + sizeof(BuildingTypeClass) + \ + sizeof(BulletClass) + \ + sizeof(BulletTypeClass) + \ + sizeof(CellClass) + \ + sizeof(FactoryClass) + \ + sizeof(HouseClass) + \ + sizeof(HouseTypeClass) + \ + sizeof(InfantryClass) + \ + sizeof(InfantryTypeClass) + \ + sizeof(LayerClass) + \ + sizeof(MouseClass) + \ + sizeof(OverlayClass) + \ + sizeof(OverlayTypeClass) + \ + sizeof(SmudgeClass) + \ + sizeof(SmudgeTypeClass) + \ + sizeof(TeamClass) + \ + sizeof(TeamTypeClass) + \ + sizeof(TemplateClass) + \ + sizeof(TemplateTypeClass) + \ + sizeof(TerrainClass) + \ + sizeof(TerrainTypeClass) + \ + sizeof(TriggerClass) + \ + sizeof(TriggerTypeClass) + \ + sizeof(UnitClass) + \ + sizeof(UnitTypeClass) + \ + sizeof(VesselClass) + \ + sizeof(ScenarioClass) + \ + sizeof(ChronalVortexClass))) +// sizeof(Waypoint))) + + +static int Reconcile_Players(void); +extern bool Is_Mission_Counterstrike (char *file_name); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Put_All -- Store all save game data to the pipe. * + * * + * This is the bulk processor of the game related save game data. All the game object * + * and state data is stored to the pipe specified. * + * * + * INPUT: pipe -- Reference to the pipe that will receive the save game data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Put_All(Pipe & pipe, int save_net) +{ + /* + ** Save the scenario global information. + */ + pipe.Put(&Scen, sizeof(Scen)); + + /* + ** Save the map. The map must be saved first, since it saves the Theater. + */ + if (!save_net) Call_Back(); + Map.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save all game objects. This code saves every object that's stored in a + ** TFixedIHeap class. + */ + Houses.Save(pipe); + if (!save_net) Call_Back(); + TeamTypes.Save(pipe); + if (!save_net) Call_Back(); + Teams.Save(pipe); + if (!save_net) Call_Back(); + TriggerTypes.Save(pipe); + if (!save_net) Call_Back(); + Triggers.Save(pipe); + if (!save_net) Call_Back(); + Aircraft.Save(pipe); + if (!save_net) Call_Back(); + Anims.Save(pipe); + + if (!save_net) Call_Back(); + + Buildings.Save(pipe); + if (!save_net) Call_Back(); + Bullets.Save(pipe); + if (!save_net) Call_Back(); + Infantry.Save(pipe); + if (!save_net) Call_Back(); + Overlays.Save(pipe); + if (!save_net) Call_Back(); + Smudges.Save(pipe); + if (!save_net) Call_Back(); + Templates.Save(pipe); + if (!save_net) Call_Back(); + Terrains.Save(pipe); + if (!save_net) Call_Back(); + Units.Save(pipe); + if (!save_net) Call_Back(); + Factories.Save(pipe); + if (!save_net) Call_Back(); + Vessels.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save the Logic & Map layers + */ + Logic.Save(pipe); + + int count = MapTriggers.Count(); + pipe.Put(&count, sizeof(count)); + for (int index = 0; index < MapTriggers.Count(); index++) { + TARGET target = MapTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + count = LogicTriggers.Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < LogicTriggers.Count(); index++) { + TARGET target = LogicTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + count = HouseTriggers[h].Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < HouseTriggers[h].Count(); index++) { + TARGET target = HouseTriggers[h][index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + } + if (!save_net) Call_Back(); + + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Save(pipe); + } + + if (!save_net) Call_Back(); + + /* + ** Save the Score + */ + pipe.Put(&Score, sizeof(Score)); + if (!save_net) Call_Back(); + + /* + ** Save the AI Base + */ + Base.Save(pipe); + if (!save_net) Call_Back(); + + /* + ** Save out the carry over list (if present). First see how + ** many carry over objects are in the list. + */ + int carry_count = 0; + CarryoverClass const * cptr = Carryover; + while (cptr != NULL) { + carry_count++; + cptr = (CarryoverClass const *)cptr->Get_Next(); + } + + if (!save_net) Call_Back(); + + /* + ** Save out the number of objects in the list. + */ + pipe.Put(&carry_count, sizeof(carry_count)); + if (!save_net) Call_Back(); + + /* + ** Now write out the objects themselves. + */ + CarryoverClass const * object_to_write = Carryover; + while (object_to_write != NULL) { + pipe.Put(object_to_write, sizeof(*object_to_write)); + object_to_write = (CarryoverClass const *)object_to_write->Get_Next(); + } + if (!save_net) Call_Back(); + + /* + ** Save miscellaneous variables. + */ + Save_Misc_Values(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save multiplayer values + */ + if (save_net) { + Save_MPlayer_Values(pipe); + } + + pipe.Flush(); +} + + +/*************************************************************************** + * Save_Game -- saves a game to disk * + * * + * Saving the Map: * + * DisplayClass::Save() invokes CellClass's Write() for every cell * + * that needs to be saved. A cell needs to be saved if it contains * + * any special data at all, such as a TIcon, or an Occupier. * + * The cell saves its own CellTrigger pointer, converted to a TARGET. * + * * + * Saving game objects: * + * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* + * Save() routine invokes each object's Write() routine, if that * + * object's IsActive is set. * + * * + * Saving the layers: * + * The Map's Layers (Ground, Air, etc) of things that are on the map, * + * and the Logic's Layer of things to process both need to be saved. * + * LayerClass::Save() writes the entire layer array to disk * + * * + * Saving the houses: * + * Each house needs to be saved, to record its Credits, Power, etc. * + * * + * Saving miscellaneous data: * + * There are a lot of miscellaneous variables to save, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 02/27/1996 JLB : Uses simpler game control value save operation. * + *=========================================================================*/ +bool Save_Game(int id, char const * descr, bool ) +{ + char name[_MAX_FNAME+_MAX_EXT]; + unsigned scenario; + HousesType house; + int save_net = 0; // 1 = save network/modem game + + scenario = Scen.Scenario; // get current scenario # + house = PlayerPtr->Class->House; // get current house + + /* + ** Generate the filename to save. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id==-1) { + strcpy(name, NET_SAVE_FILE_NAME); + save_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + + /* + ** Code everybody's pointers + */ + Code_All_Pointers(); + + /* + ** Open the file + */ + BufferIOFileClass file(name); + + FilePipe fpipe(&file); + + /* + ** Save the description, scenario #, and house + ** (scenario # & house are saved separately from the actual Scenario & + ** PlayerPtr globals for convenience; we can quickly find out which + ** house & scenario this save-game file is for by reading these values. + ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), + ** which may or may not be a HousesType number; so, saving 'house' + ** here ensures we can always pull out the house for this file.) + */ + char descr_buf[DESCRIP_MAX]; + memset(descr_buf, '\0', sizeof(descr_buf)); + sprintf(descr_buf, "%s\r\n", descr); // put CR-LF after text + descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL + fpipe.Put(descr_buf, DESCRIP_MAX); + + fpipe.Put(&scenario, sizeof(scenario)); + + fpipe.Put(&house, sizeof(house)); + + /* + ** Save the save-game version, for loading verification + */ + unsigned long version = SAVEGAME_VERSION; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + version++; +#endif + fpipe.Put(&version, sizeof(version)); + + int pos = file.Seek(0, SEEK_CUR); + + /* + ** Store a dummy message digest. + */ + char digest[20]; + fpipe.Put(digest, sizeof(digest)); + + + /* + ** Dump the save game data to the file. The data is compressed + ** and then encrypted. The message digest is calculated in the + ** process by using the data just as it is written to disk. + */ + SHAPipe sha; + BlowPipe bpipe(BlowPipe::ENCRYPT); + LZOPipe pipe(LZOPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LZWPipe pipe(LZWPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LCWPipe pipe(LCWPipe::COMPRESS, SAVE_BLOCK_SIZE); + bpipe.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + + sha.Put_To(fpipe); + bpipe.Put_To(sha); + pipe.Put_To(bpipe); + Put_All(pipe, save_net); + + /* + ** Output the real final message digest. This is the one that is of + ** the data image as it exists on the disk. + */ + pipe.Flush(); + file.Seek(pos, SEEK_SET); + sha.Result(digest); + fpipe.Put(digest, sizeof(digest)); + + pipe.End(); + + Decode_All_Pointers(); + + return(true); +} + + +/*************************************************************************** + * Load_Game -- loads a saved game * + * * + * This routine loads the data in the same way it was saved out. * + * * + * Loading the Map: * + * - DisplayClass::Load() invokes CellClass's Load() for every cell * + * that was saved. * + * - The cell loads its own CellTrigger pointer. * + * * + * Loading game objects: * + * - IHeap's Load() routine loads the # of objects stored, and loads * + * each object. * + * - Triggers: Add themselves to the HouseTriggers if they're associated * + * with a house * + * * + * Loading the layers: * + * LayerClass::Load() reads the entire layer array to disk * + * * + * Loading the houses: * + * Each house is loaded in its entirety. * + * * + * Loading miscellaneous data: * + * There are a lot of miscellaneous variables to load, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * If this routine returns false, the entire game will be in an * + * unknown state, so the scenario will have to be re-initialized. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 1/20/97 V.Grippi Added expansion CD check * + *=========================================================================*/ +bool Load_Game(int id) +{ + char name[_MAX_FNAME+_MAX_EXT]; + int i; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + int load_net = 0; // 1 = save network/modem game + + + /* + ** Generate the filename to load. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id == -1) { + strcpy(name, NET_SAVE_FILE_NAME); + load_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + + /* + ** Open the file + */ + RawFileClass file(name); + if (!file.Is_Available()) { + return(false); + } + + FileStraw fstraw(file); + + Call_Back(); + + /* + ** Read & discard the save-game's header info + */ + if (fstraw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + if (fstraw.Get(&scenario, sizeof(scenario)) != sizeof(scenario)) { + return(false); + } + + if (fstraw.Get(&house, sizeof(house)) != sizeof(house)) { + return(false); + } + + /* + ** Read in & verify the save-game ID code + */ + unsigned long version; + if (fstraw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } + GameVersion = version; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1) != SAVEGAME_VERSION) ) { + return(false); + } +#else + if (version != SAVEGAME_VERSION /*&& version != 0x0100616D*/){ + return(false); + } +#endif + /* + ** Get the message digest that is embedded in the file. + */ + char digest[20]; + fstraw.Get(digest, sizeof(digest)); + + /* + ** Remember the file position since we must seek back here to + ** perform the real saved game read. + */ + long pos = file.Seek(0, SEEK_CUR); + + /* + ** Pass the rest of the file through the hash straw so that + ** the digest can be compaired to the one in the file. + */ + SHAStraw sha; + sha.Get_From(fstraw); + for (;;) { + if (sha.Get(_staging_buffer, sizeof(_staging_buffer)) != sizeof(_staging_buffer)) break; + } + char actual[20]; + sha.Result(actual); + sha.Get_From(NULL); + + Call_Back(); + + /* + ** Compare the two digests. If they differ then return a failure condition + ** before any damage could be done. + */ + if (memcmp(actual, digest, sizeof(digest)) != 0) { + return(false); + } + + /* + ** Set up the pipe so that the scenario data can be read. + */ + file.Seek(pos, SEEK_SET); + BlowStraw bstraw(BlowStraw::DECRYPT); + LZOStraw straw(LZOStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LZWStraw straw(LZWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LCWStraw straw(LCWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); + + bstraw.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + bstraw.Get_From(fstraw); + straw.Get_From(bstraw); + + /* + ** Clear the scenario so we start fresh; this calls the Init_Clear() routine + ** for the Map, and all object arrays. It has the following important + ** effects: + ** - Every cell is cleared to 0's, via MapClass::Init_Clear() + ** - All heap elements' are cleared + ** - The Houses are Initialized, which also clears their HouseTriggers + ** array + ** - The map's Layers & Logic Layer are cleared to empty + ** - The list of currently-selected objects is cleared + */ + Clear_Scenario(); + + /* + ** Load the scenario global information. + */ + straw.Get(&Scen, sizeof(Scen)); + + /* + ** Fixup the Sessionclass scenario info so we can work out which + ** CD to request later + */ + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + if ( !scenario_file.Is_Available() ){ + + int cd = -1; + if (Is_Mission_Counterstrike (Scen.ScenarioName)) { + cd = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Expansion_AM_Present()) { + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + if (index == 3) cd = 3; + } +#endif + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Mission_Aftermath (Scen.ScenarioName)) { + cd = 3; +#ifdef BOGUSCD + cd = -1; +#endif + } +#endif + RequiredCD = cd; + if (!Force_CD_Available (RequiredCD)) { + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + } else { + /* + ** The scenario is available so set RequiredCD to whatever is currently + ** in the drive. + */ + int current_drive = CCFileClass::Get_CD_Drive(); + RequiredCD = Get_CD_Index(current_drive, 1*60); + } + } + + /* + ** Load the map. The map comes first, since it loads the Theater & init's + ** mixfiles. The map calls all the type-class's Init routines, telling them + ** what the Theater is; this must be done before any objects are created, so + ** they'll be properly created. + */ + Map.Load(straw); + + Call_Back(); + + /* + ** Load the object data. + */ + Houses.Load(straw); + TeamTypes.Load(straw); + Teams.Load(straw); + TriggerTypes.Load(straw); + Triggers.Load(straw); + Aircraft.Load(straw); + Anims.Load(straw); + Buildings.Load(straw); + Bullets.Load(straw); + + Call_Back(); + + Infantry.Load(straw); + Overlays.Load(straw); + Smudges.Load(straw); + Templates.Load(straw); + Terrains.Load(straw); + Units.Load(straw); + Factories.Load(straw); + Vessels.Load(straw); + + /* + ** Load the Logic & Map Layers + */ + Logic.Load(straw); + + int count; + straw.Get(&count, sizeof(count)); + MapTriggers.Clear(); + for (int index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + MapTriggers.Add(As_Trigger(target)); + } + + straw.Get(&count, sizeof(count)); + LogicTriggers.Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + LogicTriggers.Add(As_Trigger(target)); + } + + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + straw.Get(&count, sizeof(count)); + HouseTriggers[h].Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + HouseTriggers[h].Add(As_Trigger(target)); + } + } + + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Load(straw); + } + + Call_Back(); + + /* + ** Load the Score + */ + straw.Get(&Score, sizeof(Score)); + new(&Score) ScoreClass(NoInitClass()); + + /* + ** Load the AI Base + */ + Base.Load(straw); + + /* + ** Delete any carryover pseudo-saved game list. + */ + while (Carryover != NULL) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Load any carryover pseudo-saved game list. + */ + int carry_count = 0; + straw.Get(&carry_count, sizeof(carry_count)); + while (carry_count) { + CarryoverClass * cptr = new CarryoverClass; + assert(cptr != NULL); + + straw.Get(cptr, sizeof(CarryoverClass)); + new (cptr) CarryoverClass(NoInitClass()); + cptr->Zap(); + + if (!Carryover) { + Carryover = cptr; + } else { + cptr->Add_Tail(*Carryover); + } + carry_count--; + } + + Call_Back(); + + /* + ** Load miscellaneous variables, including the map size & the Theater + */ + Load_Misc_Values(straw); + + /* + ** Load multiplayer values + */ + if (load_net) { + Load_MPlayer_Values(straw); + } + + file.Close(); + Decode_All_Pointers(); + Map.Init_IO(); + Map.Flag_To_Redraw(true); + + /* + ** Fixup any expediency data that can be inferred from the physical + ** data loaded. + */ + Post_Load_Game(load_net); + + Call_Back(); + + /* + ** Set the required CD to be in the drive according to the scenario + ** loaded. + */ + if (RequiredCD != -2 && !load_net) { + +#ifdef FIXIT_ANTS + /* + ** Determines if this an ant mission. Since the ant mission looks no different from + ** a regular mission, examining of the scenario name is the only way to tell. + */ + if (toupper(Scen.ScenarioName[0]) == 'S' && + toupper(Scen.ScenarioName[1]) == 'C' && + toupper(Scen.ScenarioName[2]) == 'A' && + toupper(Scen.ScenarioName[3]) == '0' && + toupper(Scen.ScenarioName[5]) == 'E' && + toupper(Scen.ScenarioName[6]) == 'A') { + + AntsEnabled = true; + } else{ + AntsEnabled = false; + } +#endif + + if (Scen.Scenario == 1) { + RequiredCD = -1; + } else { +#ifdef FIXIT_ANTS + if (Scen.Scenario > 19 || AntsEnabled) { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(Scen.Scenario >= 36) { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { +#endif //FIXIT_ANTS + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } +#ifdef FIXIT_ANTS + } +#endif //FIXIT_ANTS + } + + }else{ + + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + + /* + ** Fix up the session class variables + */ + for ( int s=0 ; sDescription() == Scen.Description){ + + memcpy (Session.Options.ScenarioDescription, Scen.Description, + sizeof (Session.Options.ScenarioDescription)); + memcpy (Session.ScenarioFileName, Scen.ScenarioName, + sizeof (Session.ScenarioFileName)); + Session.ScenarioFileLength = scenario_file.Size(); + memcpy (Session.ScenarioDigest, Session.Scenarios[s]->Get_Digest(), + sizeof (Session.ScenarioDigest)); + Session.ScenarioIsOfficial = Session.Scenarios[s]->Get_Official(); + Scen.Scenario = s; + Session.Options.ScenarioIndex = s; + break; + } + } + } + } + + if (!Force_CD_Available(RequiredCD)) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } + + ScenarioInit = 0; + + if (load_net) { + + if (!Reconcile_Players()) { // (must do after Decode pointers) + return(false); + } + //!!!!!!!!!! put Fixup_Player_Units() here + Session.LoadGame = true; + } + + Map.Reload_Sidebar(); // re-load sidebar art. + + /* + ** Rescan the scenario file for any rules updates. + */ + CCINIClass ini; + int result = ini.Load(CCFileClass(Scen.ScenarioName), true); + + /* + ** Reset the rules values to their initial settings. + */ + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - But does this incorporate *changes*? - NO. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); \ + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode. + if (load_net) { + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: + readini = Is_Aftermath_Installed(); + break; + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + if (!Force_CD_Available(3)) { // force Aftermath CD in drive. + //Prog_End(); +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 +if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); +} +#endif +#endif + Emergency_Exit(EXIT_FAILURE); + } + } + CCINIClass mpini; + if (mpini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(mpini); + Rule.Recharge(mpini); + Rule.AI(mpini); + Rule.Powerups(mpini); + Rule.Land_Types(mpini); + Rule.Themes(mpini); + Rule.IQ(mpini); + Rule.Objects(mpini); + Rule.Difficulty(mpini); + } + } + + } +#endif + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } else { + Theme.Queue_Song(Scen.TransitTheme); + } + return(true); +} + + +/*************************************************************************** + * Save_Misc_Values -- saves miscellaneous variables * + * * + * INPUT: * + * file file to use for writing * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/29/1994 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=========================================================================*/ +bool Save_Misc_Values(Pipe & file) +{ + int i; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for saving 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x = PlayerPtr->Class->House; + file.Put(&x, sizeof(x)); + + /* + ** Save frame #. + */ + file.Put(&Frame, sizeof(Frame)); + + /* + ** Save currently-selected objects list. + ** Save the # of ptrs in the list. + */ + count = CurrentObject.Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save the pointers. + */ + for (i = 0; i < count; i++) { + ptr = CurrentObject[i]; + file.Put(&ptr, sizeof(ptr)); + } + + /* + ** Save the chronal vortex + */ + ChronalVortex.Save(file); + + /* + ** Save Tanya flags. + */ + file.Put(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Put(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*********************************************************************************************** + * Load_Misc_Values -- Loads miscellaneous variables. * + * * + * INPUT: file -- The file to load the misc values from. * + * * + * OUTPUT: Was the misc load process successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool Load_Misc_Values(Straw & file) +{ + ObjectClass * ptr; // for loading 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x; + file.Get(&x, sizeof(x)); +// file.Get(&PlayerPtr, sizeof(PlayerPtr)); + PlayerPtr = HouseClass::As_Pointer((HousesType)x); + + /* + ** Load frame #. + */ + file.Get(&Frame, sizeof(Frame)); + + /* + ** Load currently-selected objects list. + ** Load the # of ptrs in the list. + */ + int count; // # ptrs in 'CurrentObject' + file.Get(&count, sizeof(count)); + + /* + ** Load the pointers. + */ + for (int i = 0; i < count; i++) { + file.Get(&ptr, sizeof(ptr)); + CurrentObject.Add(ptr); // add to the list + } + + /* + ** Load the chronal vortex + */ + ChronalVortex.Load(file); + + /* + ** Save Tanya flags. + */ + file.Get(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Get(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*************************************************************************** + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_MPlayer_Values(Pipe & file) +{ + Session.Save(file); + file.Put(&BuildLevel, sizeof(BuildLevel)); + file.Put(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Put(&Seed, sizeof(Seed)); + file.Put(&Whom, sizeof(Whom)); + file.Put(&Special, sizeof(SpecialClass)); + file.Put(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_MPlayer_Values(Straw & file) +{ + Session.Load(file); + file.Get(&BuildLevel, sizeof(BuildLevel)); + file.Get(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Get(&Seed, sizeof(Seed)); + file.Get(&Whom, sizeof(Whom)); + file.Get(&Special, sizeof(SpecialClass)); + file.Get(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*********************************************************************************************** + * Code_All_Pointers -- Code all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Code_All_Pointers(void) +{ + int i; + + /* + ** The Map. + */ + Map.Code_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Code_Pointers(); + Teams.Code_Pointers(); + Triggers.Code_Pointers(); + Aircraft.Code_Pointers(); + Anims.Code_Pointers(); + Buildings.Code_Pointers(); + Bullets.Code_Pointers(); + Infantry.Code_Pointers(); + Overlays.Code_Pointers(); + Smudges.Code_Pointers(); + Templates.Code_Pointers(); + Terrains.Code_Pointers(); + Units.Code_Pointers(); + Factories.Code_Pointers(); + Vessels.Code_Pointers(); + + /* + ** The Layers. + */ + Logic.Code_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Code_Pointers(); + } + + /* + ** The Score. + */ + Score.Code_Pointers(); + + /* + ** The Base. + */ + Base.Code_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < CurrentObject.Count(); i++) { + CurrentObject[i] = (ObjectClass *)CurrentObject[i]->As_Target(); + } + + /* + ** Houses must be coded last, because the Class->House member of the HouseClass + ** is used to code HouseClass pointers for all other objects, and if Class is + ** coded, it will point to a meaningless value. + */ + Houses.Code_Pointers(); +} + + +/*********************************************************************************************** + * Decode_All_Pointers -- Decodes all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Decode_All_Pointers(void) +{ + /* + ** The Map. + */ + Map.Decode_Pointers(); + + /* + ** Decode houses first, so we can properly decode all other objects' + ** House pointers + */ + Houses.Decode_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Decode_Pointers(); + Teams.Decode_Pointers(); + Triggers.Decode_Pointers(); + Aircraft.Decode_Pointers(); + Anims.Decode_Pointers(); + Buildings.Decode_Pointers(); + Bullets.Decode_Pointers(); + Infantry.Decode_Pointers(); + Overlays.Decode_Pointers(); + Smudges.Decode_Pointers(); + Templates.Decode_Pointers(); + Terrains.Decode_Pointers(); + Units.Decode_Pointers(); + Factories.Decode_Pointers(); + Vessels.Decode_Pointers(); + + /* + ** The Layers. + */ + Logic.Decode_Pointers(); + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Decode_Pointers(); + } + + /* + ** The Score. + */ + Score.Decode_Pointers(); + + /* + ** The Base. + */ + Base.Decode_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = HouseClass::As_Pointer((HousesType)PlayerPtr); + Whom = PlayerPtr->Class->House; + assert(PlayerPtr != NULL); + + /* + ** Currently-selected objects. + */ + for (int index = 0; index < CurrentObject.Count(); index++) { + CurrentObject[index] = As_Object((TARGET)CurrentObject[index]); + assert(CurrentObject[index] != NULL); + } + + /* + ** Last-Minute Fixups; to resolve these pointers properly requires all other + ** pointers to be loaded & decoded. + */ + if (Map.PendingObjectPtr) { + Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); + assert(Map.PendingObject != NULL); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); +#ifdef BG + Map.Set_Placement_List(Map.PendingObject->Placement_List(true)); +#endif + } else { + Map.PendingObject = 0; + Map.Set_Cursor_Shape(0); + } +} + + +/*************************************************************************** + * Get_Savefile_Info -- gets description, scenario #, house * + * * + * INPUT: * + * id numerical ID, for the file extension * + * buf buffer to store description in * + * scenp ptr to variable to hold scenario * + * housep ptr to variable to hold house * + * * + * OUTPUT: * + * true = OK, false = error (save-game file invalid) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep) +{ + char name[_MAX_FNAME+_MAX_EXT]; + unsigned long version; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + BufferIOFileClass file(name); + + FileStraw straw(file); + + /* + ** Read in the description, scenario #, and the house + */ + if (straw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF + strcpy(buf, descr_buf); + + if (straw.Get(scenp, sizeof(unsigned)) != sizeof(unsigned)) { + return(false); + } + + if (straw.Get(housep, sizeof(HousesType)) != sizeof(HousesType)) { + return(false); + } + + /* + ** Read & verify the save-game version # + */ + if (straw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1 != SAVEGAME_VERSION)) ) { +#else + if (version != SAVEGAME_VERSION) { +#endif + return(false); + } + return(true); +} + + +/*************************************************************************** + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * * + * This function is for supporting loading a saved multiplayer game. * + * When the game is loaded, we have to figure out which house goes with * + * which entry in the Players vector. We also have to figure out if * + * everyone who was originally in the game is still with us, and if not, * + * turn their stuff over to the computer. * + * * + * So, this function does the following: * + * - For every name in 'Players', makes sure that name is in the House * + * array; if not, it's a fatal error. * + * - For every human-controlled house, makes sure there's a player * + * with that name; if not, it turns that house over to the computer. * + * - Fills in the Player's house ID * + * * + * This assumes that each player MUST keep their name the same as it was * + * when the game was saved! It's also assumed that the network * + * connections have not been formed yet, since Player[i]->Player.ID will * + * be invalid until this routine has been called. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconcile_Players(void) +{ + int i; + int found; + HousesType house; + HouseClass * housep; + + /* + ** If there are no players, there's nothing to do. + */ + if (Session.Players.Count()==0) + return (true); + + /* + ** Make sure every name we're connected to can be found in a House + */ + for (i = 0; i < Session.Players.Count(); i++) { + found = 0; + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + break; + } + } + if (!found) + return (false); + } + + // + // Loop through all Houses; if we find a human-owned house that we're + // not connected to, turn it over to the computer. + // + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + // + // Skip this house if it wasn't human to start with. + // + if (!housep->IsHuman) { + continue; + } + + // + // Try to find this name in the Players vector; if it's found, set + // its ID to this house. + // + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + Session.Players[i]->Player.ID = house; + break; + } + } + + /* + ** If this name wasn't found, remove it + */ + if (!found) { + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; + housep->IsStarted = true; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName, Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + } + } + + // + // If all went well, our Session.NumPlayers value should now equal the value + // from the saved game, minus any players we removed. + // + if (Session.NumPlayers == Session.Players.Count()) { + return (true); + } else { + return (false); + } +} + + +/*************************************************************************** + * MPlayer_Save_Message -- pops up a "saving..." message * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +void MPlayer_Save_Message(void) +{ + //char *txt = Text_String( +} + diff --git a/CODE/SCENARIO.CPP b/CODE/SCENARIO.CPP new file mode 100644 index 0000000..3272625 --- /dev/null +++ b/CODE/SCENARIO.CPP @@ -0,0 +1,3488 @@ +/* +** 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 . +*/ + +//Mono_Printf("%d %s\n",__LINE__,__FILE__); +/* $Header: /CounterStrike/SCENARIO.CPP 15 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCENARIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 21, 1996 [JLB] * + * * + * This module handles the scenario reading and writing. Scenario related * + * code that is executed between scenario play can also be here. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Houses -- Assigns multiplayer houses to various players * + * Clear_Flag_Spots -- Clears flag overlays off the map * + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * Clip_Move -- moves in given direction from given cell; clips to map * + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * Do_Lose -- Display losing comments. * + * Do_Restart -- Handle the restart mission process. * + * Do_Win -- Display winning congratulations. * + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * Post_Load_Game -- Fill in an inferred data from the game state. * + * Read_Scenario -- Reads a scenario from disk. * + * Read_Scenario_INI -- Read specified scenario INI file. * + * Remove_AI_Players -- Removes the computer AI houses & their units * + * Restate_Mission -- Handles restating the mission objective. * + * Scan_Place_Object -- places an object >near< the given cell * + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * Set_Scenario_Name -- Creates the INI scenario name string. * + * Start_Scenario -- Starts the scenario. * + * Write_Scenario_INI -- Write the scenario INI file. * + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +extern bool SpawnedFromWChat; +#endif +extern int PreserveVQAScreen; + +//#include "WolDebug.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +static void Remove_AI_Players(void); +static void Create_Units(bool official); +static CELL Clip_Scatter(CELL cell, int maxdist); +static CELL Clip_Move(CELL cell, FacingType facing, int dist); + + +static int _build_tech[11] = { + 2,2, // Tech level 0 and 1 are the same (tech 0 is never used). + 4, + 5, + 7, + 8, + 9, + 10, + 11, + 12, + 13 +}; + + +#ifdef FRENCH +#define TXT_HACKHACK "Accomplie" +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_ACCOMPLISHED) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Counterstrike (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * * + * This constructs the default scenario control object. Normally, all the default values * + * are meaningless since the act of starting a scenario will fill in all of the values with * + * settings retrieved from the scenario control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ScenarioClass::ScenarioClass(void) : + Difficulty(DIFF_NORMAL), + CDifficulty(DIFF_NORMAL), + Timer(0), + MissionTimer(0), + ShroudTimer(TICKS_PER_MINUTE * Rule.ShroudRate), + Scenario(1), + Theater(THEATER_TEMPERATE), + IntroMovie(VQ_NONE), + BriefMovie(VQ_NONE), + WinMovie(VQ_NONE), + LoseMovie(VQ_NONE), + ActionMovie(VQ_NONE), + TransitTheme(THEME_NONE), + PlayerHouse(HOUSE_GREECE), + CarryOverPercent(0), + CarryOverMoney(0), + CarryOverCap(0), + Percent(0), + BridgeCount(0), + CarryOverTimer(0), + IsBridgeChanged(false), + IsGlobalChanged(false), + IsToCarryOver(false), + IsToInherit(false), + IsTanyaEvac(false), + IsFadingBW(false), + IsFadingColor(false), + IsEndOfGame(false), + IsInheritTimer(false), + IsNoSpyPlane(false), + IsSkipScore(false), + IsOneTimeOnly(false), + IsNoMapSel(false), + IsTruckCrate(false), + IsMoneyTiberium(false), +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40 + AutoSonarTimer( AUTOSONAR_PERIOD ), +#endif + FadeTimer(0) +{ + for (int index = 0; index < ARRAY_SIZE(Waypoint); index++) { + Waypoint[index] = -1; + } + strcpy(Description, ""); + strcpy(ScenarioName, ""); + strcpy(BriefingText, ""); + memset(GlobalFlags, '\0', sizeof(GlobalFlags)); + memset(Views, '\0', sizeof(Views)); +} + + +/*********************************************************************************************** + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * * + * This routine will start the palette to fade to B/W for a brief moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_BW_Fade(void) +{ + IsFadingBW = true; + IsFadingColor = false; + FadeTimer = GRAYFADETIME; +} + + +/*********************************************************************************************** + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * * + * This routine will handle the maintenance of the palette fading effect. It should be * + * called once per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_Fade_AI(void) +{ + if (IsFadingColor) { + if (FadeTimer == 0) { + IsFadingColor = false; + } + fixed newsat = Options.Get_Saturation() * fixed(GRAYFADETIME-FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + } + if (IsFadingBW) { + if (FadeTimer == 0) { + IsFadingBW = false; + } + fixed newsat = Options.Get_Saturation() * fixed(FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + if (!IsFadingBW) { + IsFadingColor = true; + FadeTimer = GRAYFADETIME; + } + } +} + + +/*********************************************************************************************** + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * * + * This routine will set the global flag to the falue (true/false) specified. It will * + * also scan for and spring any triggers that are dependant upon that global. * + * * + * INPUT: global -- The global flag to change. * + * * + * value -- The value to change the global flag to. * + * * + * OUTPUT: Returns with the previous value of the flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool ScenarioClass::Set_Global_To(int global, bool value) +{ + if ((unsigned)global < ARRAY_SIZE(Scen.GlobalFlags)) { + + bool previous = GlobalFlags[global]; + if (previous != value) { + GlobalFlags[global] = value; + IsGlobalChanged = true; + + /* + ** Special case to scan through all triggers and if any are found that depend on this + ** global being set/cleared, then if there is an elapsed time event associated, it + ** will be reset at this time. + */ + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + if ((tp->Class->Event1.Event == TEVENT_GLOBAL_SET || tp->Class->Event1.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event1.Data.Value == global) { + tp->Class->Event2.Reset(tp->Event1); + } + if ((tp->Class->Event2.Event == TEVENT_GLOBAL_SET || tp->Class->Event2.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event2.Data.Value == global) { + tp->Class->Event1.Reset(tp->Event1); + } + } + } + return(previous); + } + return(false); +} + + +/*********************************************************************************************** + * Start_Scenario -- Starts the scenario. * + * * + * This routine will start the scenario. In addition to loading the scenario data, it will * + * play the briefing and action movies. * + * * + * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * + * * + * briefing -- Should the briefing be played? Normally this is true except when the * + * scenario is restarting. * + * * + * OUTPUT: Was the scenario started without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Start_Scenario(char * name, bool briefing) +{ +//BG Theme.Queue_Song(THEME_QUIET); +Theme.Stop(); + IsTanyaDead = SaveTanya; + if (!Read_Scenario(name)) { + return(false); + } + + /* + ** Play the winning movie and then start the next scenario. + */ + RequiredCD = -1; +// if (RequiredCD != -2 && Session.Type == GAME_NORMAL) { +// if (Scen.Scenario == 1) +// RequiredCD = -1; +// else { +// if((Scen.Scenario >= 20 && Scen.ScenarioName[2] == 'G' || Scen.ScenarioName[2] == 'U') || Scen.ScenarioName[2] == 'A' +// || (Scen.ScenarioName[2] == 'M' && Scen.Scenario >= 25)) +// RequiredCD = 2; +// else if(Scen.ScenarioName[2] == 'U') +// RequiredCD = 1; +// else if(Scen.ScenarioName[2] == 'G') +// RequiredCD = 0; +// } +// +//#ifdef FIXIT_FORCE_CD +// Forces the CD to be inserted according to the scenario being loaded. +//Hide_Mouse(); +//VisiblePage.Clear(); +//Show_Mouse(); +//GamePalette.Set(); +//if (!Force_CD_Available(RequiredCD)) { +// Prog_End(); +// exit(EXIT_FAILURE); +//} +//#endif + + + +// } + Theme.Stop(); + + if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.IntroMovie); + Play_Movie(Scen.BriefMovie); + } + + /* + ** If there's no briefing movie, restate the mission at the beginning. + */ + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + if (Session.Type == GAME_NORMAL && (Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available())) { + /* + ** Make sure the mouse is visible before showing the restatement. + */ + while(Get_Mouse_State()) { + Show_Mouse(); + } + Restate_Mission(Scen.ScenarioName, TXT_OK, TXT_NONE); + } + + if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.ActionMovie, Scen.TransitTheme); + } + + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } + + /* + ** Set the options values, since the palette has been initialized by Read_Scenario + */ + Options.Set(); + + return(true); +} + + +/*********************************************************************************************** + * Read_Scenario -- Reads a scenario from disk. * + * * + * This will read a scenario from disk. Use this to begin a scenario. * + * It doesn't perform any rendering, it merely sets up the system * + * with the proper data. Setting of the right game state will start * + * the scenario running. * + * * + * INPUT: root -- Scenario root filename * + * * + * OUTPUT: none * + * * + * WARNINGS: You must clear out the system variables before calling * + * this function. Use the Clear_Scenario() function. * + * It is assumed that Scenario is set to the current scenario number. * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 02/03/1992 JLB : Uses house identification. * + *=============================================================================================*/ +bool Read_Scenario(char * name) +{ + BStart(BENCH_SCENARIO); + Clear_Scenario(); + ScenarioInit++; + if (Read_Scenario_INI(name)) { +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode case. + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#endif + break; + case GAME_INTERNET: +#ifndef FIXIT_VERSION_3 // Loading of Aftermath rules depends on bAftermathMultiplayer now. + if (Is_Mission_Counterstrike(name)) { + readini = false; // Don't allow AM units on a CS map in WChat + break; + } +#endif // ( Note lack of break; ) + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != 3 ) { +#else + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { +#endif + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + RequiredCD = 3; + if (!Force_CD_Available(RequiredCD)) { // force Aftermath CD in drive. +#ifndef WOLAPI_INTEGRATION + #ifdef WIN32 + if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + } + #endif +#endif + Emergency_Exit(EXIT_FAILURE); + } + } + CCINIClass ini; + if (ini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + } + } +#endif + Fill_In_Data(); + } else { + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); + WWMessageBox().Process(TXT_UNABLE_READ_SCENARIO); + Hide_Mouse(); + BEnd(BENCH_SCENARIO); + return(false); + } + ScenarioInit--; + BEnd(BENCH_SCENARIO); + return(true); +} + + +/*********************************************************************************************** + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * * + * This routine is called after the INI file for the scenario has been processed. It will * + * infer the game state from the scenario INI data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Fill_In_Data(void) +{ + /* + ** The basic scenario data load does not contain the full set of + ** game data. We now must fill in the missing pieces. + */ + ScenarioInit++; + + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + + Map.Flag_To_Redraw(true); + + /* + ** Reset the movement zones according to the terrain passability. + */ + Map.Zone_Reset(MZONEF_ALL); + + +#ifdef WIN32 + /* + ** Since the sidebar starts up activated, adjust the home start position so that + ** the right edge of the map will still be visible. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); +// if (Session.Type == GAME_NORMAL) { + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Map.Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); +// } + } +#endif + + /* + ** Handle any data resetting that can be safely inferred from the actual + ** data that has been loaded. + */ + /* + ** Distribute the trigger pointers to the appropriate working lists. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + + assert(tp != NULL); + + if (tp->Attaches_To() & ATTACH_MAP) { + MapTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_GENERAL) { + LogicTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_HOUSE) { + HouseTriggers[tp->House].Add(Find_Or_Make(tp)); + } + } + + ScenarioInit--; + + /* + ** Now go through and set all the cells ringing the map to be visible, so + ** we won't get the wall of shadow at the edge of the map. + */ + int x,y; + for (x = Map.MapCellX-1; x < ((unsigned)(Map.MapCellX + Map.MapCellWidth + 1)); x++) { + Map[XY_Cell(x, Map.MapCellY-1)].IsVisible = + Map[XY_Cell(x, Map.MapCellY-1)].IsMapped = true; + + Map[XY_Cell(x, Map.MapCellY+(unsigned)Map.MapCellHeight)].IsVisible = + Map[XY_Cell(x, Map.MapCellY+(unsigned)Map.MapCellHeight)].IsMapped = true; + } + for (y = Map.MapCellY; y < (Map.MapCellY + Map.MapCellHeight); y++) { + Map[XY_Cell(Map.MapCellX-1, y)].IsVisible = + Map[XY_Cell(Map.MapCellX-1, y)].IsMapped = true; + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsVisible = + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsMapped = true; + } + + /* + ** If inheriting from a previous scenario was indicated, then create the carry over + ** objects at this time. + */ + if (Scen.IsToInherit) { + CarryoverClass * cptr = Carryover; + while (cptr != NULL) { + cptr->Create(); + cptr = (CarryoverClass *)cptr->Get_Next(); + } + } + + /* + ** The "allow win" action is a special case that is handled here. The total number + ** of triggers that have this action must be recorded. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + if (tp->Action1.Action == TACTION_ALLOWWIN || + (tp->ActionControl != MULTI_ONLY && tp->Action2.Action == TACTION_ALLOWWIN)) { + + HouseClass::As_Pointer(tp->House)->Blockage++; + } + } + + /* + ** Move available money to silos, if the scenario flag so indicates. + */ + if (Scen.IsMoneyTiberium) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL) { + int tomove = hptr->Capacity - hptr->Tiberium; + hptr->Credits -= tomove; + hptr->Tiberium += tomove; + } + } + } + + /* + ** Count all non-destroyed bridges on the map. + */ + Scen.BridgeCount = Map.Intact_Bridge_Count(); + + Map.All_To_Look(true); +} + + +/*********************************************************************************************** + * Post_Load_Game -- Fill in an inferred data from the game state. * + * * + * This routine is typically called after a game has been loaded. Some working data lists * + * can be rebuild from the game state. This working data is rebuilt rather than being * + * stored with the game data file. * + * * + * INPUT: load_multi -- true if we're loading a multiplayer game * + * * + * OUTPUT: none * + * * + * WARNINGS: Although it is safe to call this routine whenever, it is only needed after a * + * game load. * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +void Post_Load_Game(int load_multi) +{ + // + // Do NOT call Overpass if we're loading a multiplayer game; it calls the + // random # generator, which throws the games out of sync if they were + // saved on different frame #'s. + // + if (!load_multi) { + Map.Overpass(); + } + Scen.BridgeCount = Map.Intact_Bridge_Count(); + Map.Zone_Reset(MZONEF_ALL); +} + + +/*********************************************************************************************** + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * * + * This routine will clear out all data specific to a scenario in * + * preparation for a subsequent scenario data load. This will free * + * all units, animations, and icon maps. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * + * 07/13/1995 JLB : End count down moved here. * + *=============================================================================================*/ +void Clear_Scenario(void) +{ +// TCTCTC -- possibly just use in-place new of scenario object? + + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.Timer = 0; + Scen.ShroudTimer = 0; + Scen.IntroMovie = VQ_NONE; + Scen.BriefMovie = VQ_NONE; + Scen.WinMovie = VQ_NONE; + Scen.LoseMovie = VQ_NONE; + Scen.ActionMovie = VQ_NONE; + Scen.IsNoSpyPlane = false; + Scen.IsTanyaEvac = false; + Scen.IsEndOfGame = false; + Scen.IsInheritTimer = false; + Scen.IsToCarryOver = false; + Scen.IsSkipScore = false; + Scen.IsOneTimeOnly = false; + Scen.IsTruckCrate = false; + Scen.IsMoneyTiberium = false; + Scen.IsNoMapSel = false; + Scen.CarryOverCap = 0; + Scen.CarryOverPercent = 0; + Scen.TransitTheme = THEME_NONE; + Scen.Percent = 0; + + memset(Scen.GlobalFlags, 0, sizeof(Scen.GlobalFlags)); + + MapTriggers.Clear(); + LogicTriggers.Clear(); + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTriggers[house].Clear(); + } + + /* + ** Call everyone's Init routine, except the Map's; for the Map, only call + ** MapClass::Init, which clears the Cell array. The Display::Init requires + ** a Theater argument, and the theater is not known at this point; also, it + ** would reload MixFiles, which isn't desired. Display::Read_INI calls its + ** own Init, which will Init the entire Map hierarchy. + */ + Map.Init_Clear(); + Score.Init(); + Logic.Init(); + + HouseClass::Init(); + ObjectClass::Init(); + TeamTypeClass::Init(); + TeamClass::Init(); + TriggerClass::Init(); + TriggerTypeClass::Init(); + AircraftClass::Init(); + AnimClass::Init(); + BuildingClass::Init(); + BulletClass::Init(); + InfantryClass::Init(); + OverlayClass::Init(); + SmudgeClass::Init(); + TemplateClass::Init(); + TerrainClass::Init(); + UnitClass::Init(); + VesselClass::Init(); + + FactoryClass::Init(); + + Base.Init(); + + CurrentObject.Clear(); + + for (int index = 0; index < WAYPT_COUNT; index++) { + Scen.Waypoint[index] = -1; + } + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + bAutoSonarPulse = false; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + Scen.bLocalProposesDraw = false; + Scen.bOtherProposesDraw = false; +#endif + +} + + +/*********************************************************************************************** + * Do_Win -- Display winning congratulations. * + * * + * Perform the win the mission process. This will display any winning movies and the score * + * screen. Followed by the map selection screen and then the load of the new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 01/01/1995 JLB : Carries money forward into next scenario. * + *=============================================================================================*/ +void Do_Win(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ +#ifdef FIXIT_ANTS + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore || AntsEnabled) { +#else + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore ) { +#endif //FIXIT_ANTS + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw (true); + Map.Render(); +#ifdef WIN32 + Fancy_Text_Print(TXT_SCENARIO_WON, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#else + Fancy_Text_Print(TXT_MISSION, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_HACKHACK, x, 110*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#endif + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_ACCOMPLISHED); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.WinMovie); + + Keyboard->Clear(); + + SaveTanya = IsTanyaDead; + Scen.CarryOverTimer = Scen.MissionTimer; +// int timer = Scen.MissionTimer; + + /* + ** Do the ending screens only if not playing back a recorded game. + */ + if (!Session.Play) { + /* + ** If the score presentation should be performed, then do + ** so now. + */ + Keyboard->Clear(); + if (!Scen.IsSkipScore) { + Score.Presentation(); + } + + + if (Scen.IsOneTimeOnly) { + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +// Mono_Printf("Scenario.cpp one time only antsenabled is false\n"); +#endif + return; + } + + /* + ** If this scenario is flagged as ending the game then print the credits and exit. + */ + if (Scen.IsEndOfGame) { + if (PlayerPtr->ActLike == HOUSE_USSR) { + Play_Movie(VQ_SOVFINAL); + } else { + Play_Movie(VQ_ALLYEND); + } + Show_Who_Was_Responsible(); + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + return; + } + + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ + if (Scen.IsNoMapSel) { + // force it to play the second half of scenario 10 +#ifdef FIXIT_ANTS + if (AntsEnabled) { + char scenarioname[24]; + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + Scen.Scenario++; + sprintf(buf, "%02d", Scen.Scenario); + memcpy(&scenarioname[3], buf, 2); + Scen.Set_Scenario_Name(scenarioname); + } else { + Scen.ScenarioName[6] = 'B'; + } + +#else + Scen.ScenarioName[6] = 'B'; +#endif + + } else { + Scen.Set_Scenario_Name(Map_Selection()); + } + + Keyboard->Clear(); + } + + Scen.CarryOverMoney = PlayerPtr->Credits; + + /* + ** If requested, record the scenario's objects in the carry over list + ** for possible use in a future scenario. + */ + if (Scen.IsToCarryOver) { + + /* + ** First delete any existing carry over list. Any old list will be + ** blasted over by the new list -- there is only one logic carryover + ** list to be maintained. + */ + while (Carryover) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Record all objects, that are to be part of the carry over set, into + ** the carry over list. + */ + for (int building_index = 0; building_index < Buildings.Count(); building_index++) { + BuildingClass * building = Buildings.Ptr(building_index); + + if (building && !building->IsInLimbo && building->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(building); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + UnitClass * unit = Units.Ptr(unit_index); + + if (unit && !unit->IsInLimbo && unit->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(unit); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + InfantryClass * infantry = Infantry.Ptr(infantry_index); + + if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(infantry); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + VesselClass * vessel = Vessels.Ptr(vessel_index); + + if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(vessel); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + } + + /* + ** Generate a new scenario filename + */ +// Scen.Set_Scenario_Name(Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, Scen.ScenVar); + Start_Scenario(Scen.ScenarioName); + + /* + ** If the mission timer is to be inheriteded from the previous scenario then do it now. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + +// PlayerPtr->NukePieces = nukes; + + Map.Render(); + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Lose -- Display losing comments. * + * * + * Performs the lose mission processing. This will generally display a "would you like * + * to replay" dialog and then either reload the scenario or set flags such that the main * + * menu will appear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +void Do_Lose(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_SCENARIO_LOST, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_FAIL); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef CHEAT_KEYS +// Mono_Printf("Trying to play lose movie\n"); +#endif //CHEAT_KEYS + Play_Movie(Scen.LoseMovie); + + /* + ** Start same scenario again + */ + GamePalette.Set(); + Show_Mouse(); + if (!Session.Play && !WWMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { + Hide_Mouse(); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + Map.Render(); + } else { + Hide_Mouse(); + GameActive = 0; + } + + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + +#ifdef FIXIT_VERSION_3 // Stalemate games. +/*********************************************************************************************** + * Do_Draw -- Parallels Do_Win and Do_Lose, for multiplayer games that end in a draw. + *=============================================================================================*/ +void Do_Draw(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_WOL_DRAW, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_CONTROL_EXIT); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); +} +#endif + +/*********************************************************************************************** + * Do_Restart -- Handle the restart mission process. * + * * + * This routine is called in the main game loop when the mission must be restarted. This * + * routine will throw away the current game and reload the appropriate mission. The * + * game will "resume" at the start of the mission. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +void Do_Restart(void) +{ + /* + ** Start a timer going, before we restart the scenario + */ + CDTimerClass timer; + timer = TICKS_PER_SECOND * 4; + Theme.Queue_Song(THEME_QUIET); + + WWMessageBox().Process(TXT_RESTARTING, TXT_NONE); + + Map.Set_Default_Mouse(MOUSE_NORMAL); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + /* + ** Make sure the message stays displayed for at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + Map.Render(); +} + + +/*********************************************************************************************** + * Restate_Mission -- Handles restating the mission objective. * + * * + * This routine will display the mission objective (as text). It will also give the * + * option to redisplay the mission briefing video. * + * * + * INPUT: name -- The scenario name. This is the unique identifier for the scenario * + * briefing text as it appears in the "MISSION.INI" file. * + * * + * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * + * requested, or 0 if the return to game options button was selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + * 08/06/1995 JLB : Uses preloaded briefing text. * + *=============================================================================================*/ +bool Restate_Mission(char const * name, int button1, int button2) +{ + if (name) { + + bool brief = true; + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + + if (Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { + button2 = TXT_OK; + button1 = TXT_NONE; + brief = false; + } + + /* + ** If mission object text was found, then display it. + */ + if (strlen(Scen.BriefingText)) { + strcpy(_ShapeBuffer, Scen.BriefingText); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + if (BGMessageBox(_ShapeBuffer, button2, button1)) { + return(true); + } + if (!brief) return(true); + return(false); + } + } + return(false); +} + + +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +bool BGMessageBox(char const * msg, int btn1, int btn2) +{ +#define BUFFSIZE 511 + char buffer[BUFFSIZE]; + bool retval; + bool process; // loop while true + KeyNumType input; // user input + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + BOOL display; // display level + int realval[5]; + int morebutton = 3; // which button says "more": 2 or 3? + + const char * b1txt = Text_String(btn1); + const char * b2txt = Text_String(btn2); +#ifdef FRENCH + const char * b3txt = "SUITE"; +#else +#ifdef GERMAN + const char * b3txt = "MEHR"; +#else + const char * b3txt = "MORE"; +#endif +#endif + + const void *briefsnd = MFCD::Retrieve("BRIEFING.AUD"); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_TYPE]); + + /* + ** If the message won't be needing the 'more' button, get rid of it. + */ + if (strlen(msg) <= BUFFSIZE-1) { + b3txt = ""; + } + +#ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); +#endif + + /* + ** If there's no text for button one, zero it out. + */ + if (*b1txt == '\0') { + b1txt = b2txt; + b2txt = ""; + if(*b1txt == '\0') { + b1txt=0; + } + } + + /* + ** If there's no text for button two, zero it out. However, if there + ** is text for button three, move its text (always "MORE") to button two, + ** and set the morebutton flag to point to button two. Then, clear out + ** button 3. + */ + if (*b2txt == '\0') { + b2txt = 0; + if (*b3txt != '\0') { + b2txt = b3txt; + b3txt = ""; + morebutton = 1; + } + } + + /* + ** If there's no text for button three, zero it out. + */ + if (*b3txt == '\0') b3txt = 0; + + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + char b1char, b2char, b3char; // 1st char of each string + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt) { + b1char = toupper(b1txt[0]); + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max((String_Pixel_Width(b1txt) + 8), 80); + if (b2txt) { + numbuttons = 2; + b2char = toupper(b2txt[0]); + bwidth = max((String_Pixel_Width( b2txt ) + 8), bwidth); +// b1x = x + 10; // left side + + if (b3txt) { + numbuttons = 3; + b3char = toupper(b3txt[0]); + bwidth = max((String_Pixel_Width( b3txt ) + 8), bwidth); + } + + } else { + numbuttons = 1; +// b1x = x + ((width - bwidth) >> 1); // centered + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + int buffend = BUFFSIZE-1; + strncpy(buffer, msg, BUFFSIZE-1); + /* + ** Scan through the string to see if it got clipped, and if so, we'll + ** trim it back to the last space so it'll clip on a word. + */ + if (strlen(buffer) != strlen(msg)) { + while (buffer[buffend] != ' ') buffend--; + buffer[buffend]=0; + } + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + int width; + int height; + Format_Window_String(buffer, 300, width, height); + height += (numbuttons == 0) ? 30 : 60; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, + 0, y + height - (bheight + 5)); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + if (numbuttons) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + + PaletteClass temp; +#ifdef WIN32 + char *filename = "SOVPAPER.PCX"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.PCX"; + } + Load_Title_Screen(filename, &HidPage, temp); +#else + char *filename = "SOVPAPER.CPS"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.CPS"; + } + Load_Uncompress(CCFileClass(filename), HidPage, HidPage, temp); +#endif + HidPage.Blit(SeenPage); + + #ifdef WIN32 + VisiblePage.Blit(seen_buff_save); + #endif + + static char _scorepal[]={0,1,12,13,4,5,6,7,8,9,10,255,252,253,14,248}; + Set_Font_Palette(_scorepal); + temp.Set(FADE_PALETTE_MEDIUM, Call_Back); + + /* + ** Main Processing Loop. + */ + + int bufindex = 0; + + Keyboard->Clear(); + + Set_Font_Palette(_scorepal); + int xprint = x + 20; + int yprint = y + 25; + do { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + char bufprint[2]; + bufprint[1]=0; + bufprint[0] = buffer[bufindex]; + if (bufprint[0] == '\r' || bufprint[0] == '@') { + xprint = x + 20; + yprint += FontHeight + FontYSpacing; + + } else { + if (bufprint[0] != 20) { + SeenPage.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#ifdef WIN32 + seen_buff_save.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#endif + xprint += Char_Pixel_Width(bufprint[0]); + } + } + if (bufprint[0] == '\r' || bufprint[0] == '@') { +#ifdef WIN32 + Play_Sample(briefsnd, 255, Options.Normalize_Volume(135)); +#else + Play_Sample(briefsnd, 255, Options.Normalize_Volume(45)); +#endif + CDTimerClass cd; + cd = 5; + do { + Call_Back(); + } while(!Keyboard->Check() && cd); + } + } while (buffer[++bufindex]); + + Show_Mouse(); + Keyboard->Clear(); + + if (buttonlist) { + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + switch (input) { + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (KN_ESC): + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } + break; + + case (BUTTON_2|BUTTON_FLAG): + selection = BUTTON_2; + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_1; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + if (b1char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_1; + pressed = true; + } else if (b2txt!=NULL && + b2char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_2; + pressed = true; + } else if (b3txt!=NULL && + b3char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_3; + pressed = true; + } + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_1): + retval = 1; + process = false; + break; + + case (BUTTON_2): + retval = 0; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + if (retval == (morebutton-1) && strlen(msg) > BUFFSIZE-1) { + retval = BGMessageBox(msg + buffend + 1, btn1, btn2); + } + /* + ** Restore the screen. + */ + Hide_Mouse(); + /* + ** Now set the palette, depending on if we're going to show the video or + ** go back to the main menu. + */ + switch (retval) { + case 0: +// BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// SeenPage.Clear(); +//// CCPalette.Set(); +// break; + case 1: + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + SeenPage.Clear(); + break; + default: + break; + } + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + return(retval); +} + + +/*********************************************************************************************** + * Set_Scenario_Name -- Creates the INI scenario name string. * + * * + * This routine is used by the scenario loading and saving code. It generates the scenario * + * INI root file name for the specified scenario parameters. * + * * + * INPUT: * + * buf buffer to store filename in; must be long enough for root.ext * + * scenario scenario number * + * player player type for this game (GDI, NOD, multi-player, ...) * + * dir directional parameter for this game (East/West) * + * var variation of this game (Lose, A/B/C/D, etc) * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * + *=============================================================================================*/ +void ScenarioClass::Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) +{ + Scenario = scenario; +// ScenPlayer = player; +// ScenDir = dir; +// ScenVar = var; + + char c_player; // character representing player type + char c_dir; // character representing direction type + char c_var; // character representing variation type + ScenarioVarType i; + char fname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Set the player-type value. + */ + switch (player) { + case SCEN_PLAYER_SPAIN: + c_player = HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix; + break; + + case SCEN_PLAYER_GREECE: + c_player = HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix; + break; + + case SCEN_PLAYER_USSR: + c_player = HouseTypeClass::As_Reference(HOUSE_USSR).Prefix; + break; + + case SCEN_PLAYER_JP: + c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; + break; + + /* + ** Multi player scenario. + */ + default: + c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; + break; + } + + /* + ** Set the directional character value. + ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' + */ + switch (dir) { + case SCEN_DIR_EAST: + c_dir = 'E'; + break; + + case SCEN_DIR_WEST: + c_dir = 'W'; + break; + + default: + case SCEN_DIR_NONE: + c_dir = Percent_Chance(50) ? 'W' : 'E'; + break; + } + + /* + ** Set the variation value. + */ + if (var == SCEN_VAR_NONE) { + + /* + ** Find which variations are available for this scenario + */ + for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { + sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); + if (!CCFileClass(fname).Is_Available()) { + break; + } + } + + if (i==SCEN_VAR_FIRST) { + c_var = 'X'; // indicates an error + } else { + c_var = 'A' + Random_Pick(0, i-1); +// ScenVar = (ScenarioVarType)i; + } + } else { + switch (var) { + case SCEN_VAR_A: + c_var = 'A'; + break; + + case SCEN_VAR_B: + c_var = 'B'; + break; + + case SCEN_VAR_C: + c_var = 'C'; + break; + + case SCEN_VAR_D: + c_var = 'D'; + break; + + default: + c_var = 'L'; + break; + + } + } + + /* + ** generate the filename + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +//Mono_Printf("In set_scenario_name, scenario # = %d\n",scenario);Keyboard->Get();Keyboard->Get(); + if (scenario < 100) { + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); + } else { + char first = (scenario / 36) + 'A'; + char second = scenario % 36; + + if (second < 10) { + second += '0'; + } else { + second = (second - 10) + 'A'; + } + + sprintf(ScenarioName, "SC%c%c%c%c%c.INI", c_player, first, second, c_dir, c_var); + } +#else + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); +#endif +} + + +void ScenarioClass::Set_Scenario_Name(char const * name) +{ + if (name != NULL) { + strncpy(ScenarioName, name, sizeof(ScenarioName)); + ScenarioName[ARRAY_SIZE(ScenarioName)-1] = '\0'; + + char buf[3]; + memcpy(buf, &ScenarioName[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (buf[0] > '9' || buf[1] > '9') { + char first = buf[0]; + char second = buf[1]; + if (first <= '9') { + first -= '0'; + } else { + first -= 'A'; + } + + if (second <= '9') { + second -= '0'; + } else { + second = (second - 'A') + 10; + } + + Scenario = (36 * first) + second; + } else { + Scenario = atoi(buf); + } +#else + Scenario = atoi(buf); +#endif + } +} + + + +/*********************************************************************************************** + * Read_Scenario_INI -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. V.Grippi added CS check 2/5/97 * + *=============================================================================================*/ +bool Read_Scenario_INI(char * fname, bool ) +{ +// char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + + + ScenarioInit++; + + Clear_Scenario(); +#ifdef OBSOLETE + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + RequiredCD = -1; + } +#endif + + /* + ** Only force a CD check if this is a single player game or if its + ** a multiplayer game on an official scenario. If its non-official + ** (a user scenario) then we dont care which CD is in because the + ** scenario is stored locally on the hard drive. In this case, we + ** have already verified its existance. ST 3/1/97 4:52PM. + */ +#ifdef FIXIT_VERSION_3 // Avoid CD check if official scenario was downloaded. + if( ( Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial ) && _stricmp( Scen.ScenarioName, "download.tmp" ) ){ +#else + if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial){ +#endif + + /* + ** If this is scenario 1 then it should be on all CDs unless its an ant scenario + */ + if (Scen.Scenario == 1 && Scen.ScenarioName[2] != 'A') { + RequiredCD = -1; + } else { +// Mono_Printf("Read_SCen_INI scenario is: %s\n", Scen.ScenarioName); + /* + ** If this is a multiplayer scenario we need to find out if its a counterstrike + ** scenario. If so then we need CD 2. The original multiplayer scenarios are on + ** all CDs. + */ + if (Session.Type != GAME_NORMAL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + RequiredCD = -1; // default that any CD will do. +// If it's a counterstrike mission, require the counterstrike CD, unless the +// Aftermath CD is already in the drive, in which case, leave it there. +// Note, this works because this section only tests for multiplayer scenarios. + if (Is_Mission_Counterstrike(Scen.ScenarioName)) { + RequiredCD = 2; + if( Is_Aftermath_Installed() || Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) == 3 ) + { + RequiredCD = 3; + } + } + if(Is_Mission_Aftermath(Scen.ScenarioName)) { + RequiredCD = 3; + } +#else + if (Scen.Scenario > 24) { + RequiredCD = 2; + } else { + RequiredCD = -1; + } +#endif + } else { + + /* + ** This is a solo game. If the scenario number is >= 20 or its an ant mission + ** then we need the counterstrike CD (2) + */ + if (Scen.Scenario >= 20 || Scen.ScenarioName[2] == 'A') { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Scen.Scenario >= 36 && Scen.ScenarioName[2] != 'A') { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { + + /* + ** This is a solo mission from the original Red Alert. Choose the Soviet or + ** allied CD depending on the scenario name. + */ + if (Scen.ScenarioName[2] == 'U') { + RequiredCD = 1; + } else { + if (Scen.ScenarioName[2] == 'G') { +// Mono_Printf("We are setting REquiredCD to 0"); + RequiredCD = 0; + + } + } + } + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're asking for a CD swap, check to see if we need to set the palette +// to avoid a black screen. If this is a normal RA game, and the CD being +// requested is an RA CD, then don't set the palette, leave the map screen up. + +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != RequiredCD ) { +#else + if (Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != RequiredCD) { +#endif + if ((RequiredCD == 0 || RequiredCD == 1) && Session.Type == GAME_NORMAL) { + SeenPage.Clear(); + } + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + } +#endif + if (!Force_CD_Available(RequiredCD)) { + //Prog_End(); + Emergency_Exit(EXIT_FAILURE); + } + } else { + /* + ** This is a user scenario so any old CD will do. + */ + RequiredCD = -1; + } + + + /* + ** Create scenario filename and read the file. + */ +// sprintf(fname, "%s.INI", root); + CCINIClass ini; + CCFileClass file(fname); +// file.Cache(); + + int result = ini.Load(file, true); + if (result == 0) { +// Mono_Printf("ini.Load failed"); + return(false); + } + + /* + ** If the scenario digest is wrong then the return code will be a 2. + */ + if (result == 2) { +// if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { + /* + ** Make a special exception so that multiplayer maps from 1 through + ** 24 will not care if the message digest is in error. All other + ** maps will abort the scenario load. + */ + if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) { + GamePalette.Set(); + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); +#ifdef RELEASE_VERSION + return(false); +#endif + } +// } + } + + /* + ** Reset the rules values to their initial settings. + */ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameOverride[index] != NULL) free((void*)NameOverride[index]); + NameOverride[index] = NULL; + NameIDOverride[index] = 0; + } + if (Session.Type == GAME_NORMAL) { + Special.IsShadowGrow = false; + } +#endif + +#ifdef FIXIT_ANTS + Session.Messages.Reset(); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); + WeaponTypeClass::As_Pointer(WEAPON_FLAMER)->Sound = VOC_NONE; + InfantryTypeClass::As_Reference(INFANTRY_THIEF).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_E4).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_SPY).PrimaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_SPY).SecondaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_GENERAL).IsBomber = false; + UnitTypeClass::As_Reference(UNIT_HARVESTER).IsExploding = false; + UnitTypeClass::As_Reference(UNIT_ANT1).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT2).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT3).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_QUEEN).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA1).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA2).Level = -1; +#endif + + + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Except does this _change_ any rules, or just add to them? - Just adds. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; +#ifdef TOFIX + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } +#endif + + /* + ** Fetch the appropriate movie names from the INI file. + */ + const char * const BASIC = "Basic"; + ini.Get_String(BASIC, "Name", "", Scen.Description, sizeof(Scen.Description)); + Scen.IntroMovie = ini.Get_VQType(BASIC, "Intro", Scen.IntroMovie); + Scen.BriefMovie = ini.Get_VQType(BASIC, "Brief", Scen.BriefMovie); + Scen.WinMovie = ini.Get_VQType(BASIC, "Win", Scen.WinMovie); + Scen.LoseMovie = ini.Get_VQType(BASIC, "Lose", Scen.LoseMovie); + Scen.ActionMovie = ini.Get_VQType(BASIC, "Action", Scen.ActionMovie); + Scen.IsToCarryOver = ini.Get_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + Scen.IsToInherit = ini.Get_Bool(BASIC, "ToInherit", Scen.IsToInherit); + Scen.IsInheritTimer = ini.Get_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + Scen.IsEndOfGame = ini.Get_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + Scen.IsTanyaEvac = ini.Get_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + Scen.TransitTheme = ini.Get_ThemeType(BASIC, "Theme", THEME_NONE); + NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); + Scen.CarryOverPercent = ini.Get_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + Scen.CarryOverPercent = Saturate(Scen.CarryOverPercent, 1); + Scen.CarryOverCap = ini.Get_Int(BASIC, "CarryOverCap", Scen.CarryOverCap); + Scen.IsNoSpyPlane = ini.Get_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + Scen.IsSkipScore = ini.Get_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + Scen.IsOneTimeOnly = ini.Get_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + Scen.IsNoMapSel = ini.Get_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + Scen.IsTruckCrate = ini.Get_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + Scen.IsMoneyTiberium = ini.Get_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + Scen.Percent = ini.Get_Int(BASIC, "Percent", Scen.Percent); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(ini); + Call_Back(); + + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + PlayerPtr = HouseClass::As_Pointer(ini.Get_HousesType(BASIC, "Player", HOUSE_GREECE)); + PlayerPtr->Assign_Handicap(Scen.Difficulty); + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = min(Scen.CarryOverMoney * Scen.CarryOverPercent, Scen.CarryOverCap); + } else { + carryover = Scen.CarryOverMoney * Scen.CarryOverPercent; + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { + Assign_Houses(); + } + PlayerPtr->IsHuman = true; + PlayerPtr->IsPlayerControl = true; + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerTypeClass::Read_INI(ini); + Call_Back(); + + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(ini); + Call_Back(); + + + +// if (NewINIFormat < 2 || !ini.Is_Present("MapPack")) { +// Map.Read_Binary(root, &ScenarioCRC); +// } + + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(ini); + Call_Back(); + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(ini); + Call_Back(); + + VesselClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(ini); + Call_Back(); + + + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(ini); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(ini); + Call_Back(); + + /* Moved above ini.Get_TextBlock(...) so Xlat mission.ini could be loaded + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. VG 10/17/96 + */ + INIClass mini; + mini.Load(CCFileClass("MISSION.INI")); + mini.Get_TextBlock(fname, Scen.BriefingText, sizeof(Scen.BriefingText)); + + /* + ** Read in any briefing text. + */ + if (Scen.BriefingText[0] == '\0') { + ini.Get_TextBlock("Briefing", Scen.BriefingText, sizeof(Scen.BriefingText)); + } + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (Session.Type != GAME_NORMAL /*|| Scen.ScenPlayer == SCEN_PLAYER_2PLAYER || Scen.ScenPlayer == SCEN_PLAYER_MPLAYER*/) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if ( (Session.Options.AIPlayers + Session.Players.Count() < Rule.MaxPlayers) && !Debug_Map) { + Remove_AI_Players(); + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. Session.Options.UnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(ini.Get_Bool("Basic", "Official", false)); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if random crates are enabled for + ** this scenario. + */ + if (Session.Options.Goodies) { + int count = max(Rule.CrateMinimum, Session.NumPlayers); + count = min(count, Rule.CrateMaximum); + for (int index = 0; index < count; index++) { + Map.Place_Random_Crate(); + } + } + + /* + ** Compute my starting location as the average Coord of all my stuff. + */ + Map.Compute_Start_Pos(); + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Added runtime check. + if( Is_Aftermath_Installed() ) + { + if (Session.Type == GAME_SKIRMISH) { + bAftermathMultiplayer = NewUnitsEnabled = true; + } + } +#endif + ScenarioInit--; + return(true); +} + + +/*********************************************************************************************** + * Write_Scenario_INI -- Write the scenario INI file. * + * * + * INPUT: * + * root root filename for the scenario * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/11/1995 JLB : Updates movie data. * + *=============================================================================================*/ +void Write_Scenario_INI(char * fname) +{ +#ifndef CHEAT_KEYS + fname = fname; +#else +// CCFileClass file(fname); + + CCINIClass ini; + + /* + ** Preload the old scenario if it is present because there may + ** be some fields in the INI that are processed but not written + ** out. Preloading the scenario will preserve these manually + ** maintained entries. + */ + if (CCFileClass(fname).Is_Available()) { + ini.Load(CCFileClass(fname), true); + } + + static char const * const BASIC = "Basic"; + ini.Clear(BASIC); + ini.Put_String(BASIC, "Name", Scen.Description); + ini.Put_VQType(BASIC, "Intro", Scen.IntroMovie); + ini.Put_VQType(BASIC, "Brief", Scen.BriefMovie); + ini.Put_VQType(BASIC, "Win", Scen.WinMovie); + ini.Put_VQType(BASIC, "Lose", Scen.LoseMovie); + ini.Put_VQType(BASIC, "Action", Scen.ActionMovie); + ini.Put_HousesType(BASIC, "Player", PlayerPtr->Class->House); + ini.Put_ThemeType(BASIC, "Theme", Scen.TransitTheme); + ini.Put_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + ini.Put_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + ini.Put_Bool(BASIC, "ToInherit", Scen.IsToInherit); + ini.Put_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + ini.Put_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + ini.Put_Int(BASIC, "NewINIFormat", 3); + ini.Put_Int(BASIC, "CarryOverCap", Scen.CarryOverCap/100); + ini.Put_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + ini.Put_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + ini.Put_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + ini.Put_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + ini.Put_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + ini.Put_Bool(BASIC, "Official", true); + ini.Put_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + ini.Put_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + ini.Put_Int(BASIC, "Percent", Scen.Percent); + + HouseClass::Write_INI(ini); + TeamTypeClass::Write_INI(ini); + TriggerTypeClass::Write_INI(ini); + Map.Write_INI(ini); + TerrainClass::Write_INI(ini); + UnitClass::Write_INI(ini); + VesselClass::Write_INI(ini); + InfantryClass::Write_INI(ini); + BuildingClass::Write_INI(ini); + Base.Write_INI(ini); + OverlayClass::Write_INI(ini); + SmudgeClass::Write_INI(ini); + + if (strlen(Scen.BriefingText)) { + ini.Put_TextBlock("Briefing", Scen.BriefingText); + } +// sprintf(fname, "%s.INI", root); + RawFileClass rawfile(fname); + ini.Save(rawfile, true); +#endif +} + + +/*********************************************************************************************** + * Assign_Houses -- Assigns multiplayer houses to various players * + * * + * This routine assigns all players to a multiplayer house slot; it forms network connections * + * to each player. The Connection ID used is the value for that player's HousesType. * + * * + * PlayerPtr is also set here. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine assumes the 'Players' vector has been properly filled in with players' * + * names, addresses, color, etc. * + * Also, it's assumed that the HouseClass's have all been created & initialized. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + * 07/14/1995 JLB : Records name of player in house structure. * + *=============================================================================================*/ +void Assign_Houses(void) +{ + int assigned[MAX_PLAYERS]; + int color_used[8]; + int i,j; + HousesType house; + HouseClass * housep; + int lowest_color; + int index; + HousesType pref_house; + int color; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + for (i = 0; i < MAX_PLAYERS; i++) { + assigned[i] = 0; + color_used[i] = 0; + } + +// debugprint( "Assign_Houses()\n" ); + //------------------------------------------------------------------------ + // Assign each player in 'Players' to a multiplayer house. Players will + // be sorted by their chosen color value (this value must be unique among + // all the players). + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + + //..................................................................... + // Find the player with the lowest color index + //..................................................................... + index = 0; + lowest_color = 255; + for (j = 0; j < Session.Players.Count(); j++) { + //.................................................................. + // If we've already assigned this house, skip it. + //.................................................................. + if (assigned[j]) { + continue; + } + if (Session.Players[j]->Player.Color < lowest_color) { + lowest_color = Session.Players[j]->Player.Color; + index = j; + } + } + + //..................................................................... + // Mark this player as having been assigned. + //..................................................................... + assigned[index] = 1; + color_used[Session.Players[index]->Player.Color] = 1; + + //..................................................................... + // Assign the lowest-color'd player to the next available slot in the + // HouseClass array. + //..................................................................... + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#ifdef WOLAPI_INTEGRATION + // Make another copy of name, permanent throughout entire game. + strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#endif + housep->IsHuman = true; + housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), + Session.Players[index]->Player.House, Session.Options.Credits); + if (index == 0) { + PlayerPtr = housep; + } + /* + ** Convert the build level into an actual tech level to assign to the house. + ** There isn't a one-to-one correspondence. + */ + housep->Control.TechLevel = _build_tech[BuildLevel]; + + housep->Assign_Handicap(Scen.Difficulty); + + //..................................................................... + // Record where we placed this player + //..................................................................... + Session.Players[index]->Player.ID = house; + +// debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); + } + + //------------------------------------------------------------------------ + // Now assign computer players to the remaining houses. + //------------------------------------------------------------------------ + for (i = Session.Players.Count(); i < Session.Players.Count() + Session.Options.AIPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (Percent_Chance(50)) { + pref_house = HOUSE_GREECE; + } else { + pref_house = HOUSE_USSR; + } + + //..................................................................... + // Pick a color for this house; keep looping until we find one. + //..................................................................... + while (1) { + color = Random_Pick(0, 7); + if (color_used[color] == false) { + break; + } + } + color_used[color] = true; + + //..................................................................... + // Set up the house + //..................................................................... +// housep->Control.MaxUnit = 80; +// housep->Control.MaxInfantry = 60; +// housep->Control.MaxBuilding = 60; +// housep->Control.MaxVessel = 60; + housep->IsHuman = false; + housep->IsStarted = true; + + strcpy(housep->IniName, Text_String(TXT_COMPUTER)); + + if (Session.Type != GAME_NORMAL) { + housep->IQ = Rule.MaxIQ; + } + + housep->Init_Data((PlayerColorType)color, pref_house, Session.Options.Credits); + housep->Control.TechLevel = _build_tech[BuildLevel]; +// housep->Control.TechLevel = BuildLevel; + + DiffType difficulty = Scen.CDifficulty; + + if (Session.Players.Count() > 1 && Rule.IsCompEasyBonus && difficulty > DIFF_EASY) { + difficulty = (DiffType)(difficulty - 1); + } + housep->Assign_Handicap(difficulty); + } + + for (i = Session.Players.Count()+Session.Options.AIPlayers; i < Rule.MaxPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (housep != NULL) { + housep->IsDefeated = true; + } + } +} + + +/*********************************************************************************************** + * Remove_AI_Players -- Removes the computer AI houses & their units * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Remove_AI_Players(void) +{ + int i; + int aicount = 0; + HousesType house; + HouseClass * housep; + + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + aicount++; + if(aicount > Session.Options.AIPlayers) { + housep->Clobber_All(); + } + } + } +} + + +/*********************************************************************************************** + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * * + * This routine uses data tables to determine which units to create for either * + * a GDI or NOD house, and how many of each. * + * * + * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * + * as that house's "home" cell. * + * * + * INPUT: official -- Directs the placement logic to use the full set of waypoints rather * + * than biasing toward the first four. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Create_Units(bool official) +{ + static struct { + int MinLevel; + UnitType AllyType[2]; + UnitType SovietType[2]; + } utable[] = { + {4, {UNIT_MTANK2, UNIT_LTANK}, {UNIT_MTANK, UNIT_NONE}}, + {5, {UNIT_APC, UNIT_NONE}, {UNIT_V2_LAUNCHER, UNIT_NONE}}, + {8, {UNIT_ARTY, UNIT_JEEP}, {UNIT_MTANK, UNIT_NONE}}, + {10, {UNIT_MTANK2, UNIT_MTANK2}, {UNIT_HTANK, UNIT_NONE}} + }; + static int num_units[ARRAY_SIZE(utable)]; // # of each type of unit to create + int tot_units; // total # units to create + + static struct { + int MinLevel; + int AllyCount; + InfantryType AllyType; + int SovietCount; + InfantryType SovietType; + } itable[] = { + {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, + {2, 1,INFANTRY_E3, 1,INFANTRY_E2}, + {4, 1,INFANTRY_E3, 1,INFANTRY_E4}, + +// removed because of bug B478 (inappropriate infantry given in a bases off scenario). +// {5, 1,INFANTRY_RENOVATOR, 1,INFANTRY_RENOVATOR}, +// {6, 1,INFANTRY_SPY, 1,INFANTRY_DOG}, +// {10, 1,INFANTRY_THIEF, 1,INFANTRY_DOG}, +// {12, 1,INFANTRY_MEDIC, 2,INFANTRY_DOG} + }; + static int num_infantry[ARRAY_SIZE(itable)];// # of each type of infantry to create + int tot_infantry; // total # infantry to create + + + CELL centroid; // centroid of this house's stuff + CELL centerpt; // centroid for a category of objects, as a CELL + + int u_limit=0; // last allowable index of units for this BuildLevel + int i_limit=0; // last allowable index of infantry for this BuildLevel + TechnoClass * obj; // newly-created object + int i,j,k; // loop counters + int scaleval; // value to scale # units or infantry + + /* + ** For the current BuildLevel, find the max allowable index into the tables + */ + for (i = 0; i < ARRAY_SIZE(utable); i++) { + if (PlayerPtr->Control.TechLevel >= utable[i].MinLevel) { + u_limit = i+1; + } + } + for (i = 0; i < ARRAY_SIZE(itable); i++) { + if (PlayerPtr->Control.TechLevel >= itable[i].MinLevel) { + i_limit = i+1; + } + } + + /* + ** Compute how many of each buildable category to create + */ + /* + ** Compute allowed # units + */ + tot_units = (Session.Options.UnitCount * 2) / 3; + if (u_limit == 0) tot_units = 0; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < u_limit; i++) { + num_units[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all units + */ + j = 0; + for (i = 0; i < tot_units; i++) { + num_units[j]++; + j++; + if (j >= u_limit) { + j = 0; + } + } + + /* + ** Compute allowed # infantry + */ + tot_infantry = Session.Options.UnitCount - tot_units; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < i_limit; i++) { + num_infantry[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all infantry + */ + j = 0; + for (i = 0; i < tot_infantry; i++) { + num_infantry[j]++; + j++; + if (j >= i_limit) { + j = 0; + } + } + + /* + ** Build a list of the valid waypoints. This normally shouldn't be + ** necessary because the scenario level designer should have assigned + ** valid locations to the first N waypoints, but just in case, this + ** loop verifies that. + */ + bool taken[26]; + CELL waypts[26]; + assert(Rule.MaxPlayers < ARRAY_SIZE(waypts)); + int num_waypts = 0; + + /* + ** Calculate the number of waypoints (as a minimum) that will be lifted from the + ** mission file. Bias this number so that only the first 4 waypoints are used + ** if there are 4 or fewer players. Unofficial maps will pick from all the + ** available waypoints. + */ + int look_for = max(4, Session.Players.Count()+Session.Options.AIPlayers); + if (!official) { + look_for = 8; + } + + for (int waycount = 0; waycount < look_for; waycount++) { +// for (int waycount = 0; waycount < max(4, Session.Players.Count()+Session.Options.AIPlayers); waycount++) { + if (Scen.Waypoint[waycount] != -1) { + waypts[num_waypts] = Scen.Waypoint[waycount]; + taken[num_waypts] = false; + num_waypts++; + } + } + + /* + ** If there are insufficient waypoints to account for all players, then randomly assign + ** starting points until there is enough. + */ + int deficiency = look_for - num_waypts; +// int deficiency = (Session.Players.Count() + Session.Options.AIPlayers) - num_waypts; + if (deficiency > 0) { + for (int index = 0; index < deficiency; index++) { + CELL trycell = XY_Cell(Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1), Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1)); + + trycell = Map.Nearby_Location(trycell, SPEED_TRACK); + waypts[num_waypts] = trycell; + taken[num_waypts] = false; + num_waypts++; + } + } + + /* + ** Loop through all houses. Computer-controlled houses, with Session.Options.Bases + ** ON, are treated as though bases are OFF (since we have no base-building + ** AI logic.) + */ + int numtaken = 0; + for (HousesType house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + + /* + ** Get a pointer to this house; if there is none, go to the next house + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr == NULL) { + continue; + } + + /* + ** Pick the starting location for this house. The first house just picks + ** one of the valid locations at random. The other houses pick the furthest + ** wapoint from the existing houses. + */ + if (numtaken == 0) { + int pick = Random_Pick(0, num_waypts-1); + centroid = waypts[pick]; + taken[pick] = true; + numtaken++; + } else { + + /* + ** Set all waypoints to have a score of zero in preparation for giving + ** a distance score to all waypoints. + */ + int score[26]; + memset(score, '\0', sizeof(score)); + + /* + ** Scan through all waypoints and give a score as a value of the sum + ** of the distances from this waypoint to all taken waypoints. + */ + for (int index = 0; index < num_waypts; index++) { + + /* + ** If this waypoint has not already been taken, then accumulate the + ** sum of the distance between this waypoint and all other taken + ** waypoints. + */ + if (!taken[index]) { + for (int trypoint = 0; trypoint < num_waypts; trypoint++) { + + if (taken[trypoint]) { + score[index] += Distance(Cell_Coord(waypts[index]), Cell_Coord(waypts[trypoint])); + } + } + } + } + + /* + ** Now find the waypoint with the largest score. This waypoint is the one + ** that is furthest from all other taken waypoints. + */ + int best = 0; + int bestvalue = 0; + for (int searchindex = 0; searchindex < num_waypts; searchindex++) { + if (score[searchindex] > bestvalue || bestvalue == 0) { + bestvalue = score[searchindex]; + best = searchindex; + } + } + + /* + ** Assign this best position to the house. + */ + centroid = waypts[best]; + taken[best] = true; + numtaken++; + } + + /* + ** Assign the center of this house to the waypoint location. + */ + hptr->Center = Cell_Coord(centroid); + + /* + ** If Bases are ON, human & computer houses are treated differently + */ + if (Session.Options.Bases) { + + /* + ** - For a human-controlled house: + ** - Set 'scaleval' to 1 + ** - Create an MCV + ** - Attach a flag to it for capture-the-flag mode + */ + scaleval = 1; + obj = new UnitClass (UNIT_MCV, house); + if (!obj->Unlimbo(Cell_Coord(centroid), DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj != NULL) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj, true); + } + } + } else { + + /* + ** If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for + ** capture-the-flag mode. + */ + scaleval = 1; +#ifdef TOFIX + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_TRUCK, house); + obj->Unlimbo(Cell_Coord(centroid), DIR_N); + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } +#endif + } + + /* + ** Create units for this house + */ + for (i = 0; i < u_limit; i++) { + + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_units[i] * scaleval; j++) { + + /* + ** Create an Ally unit + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < 2; k++) if(utable[i].AllyType[k] != UNIT_NONE) { + obj = new UnitClass (utable[i].AllyType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create a Soviet unit + */ + for (k = 0; k < 2; k++) if(utable[i].SovietType[k] != UNIT_NONE) { + obj = new UnitClass (utable[i].SovietType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + + /* + ** Create infantry + */ + for (i = 0; i < i_limit; i++) { + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_infantry[i] * scaleval; j++) { + + /* + ** Create Ally infantry (Note: Unlimbo calls Enter_Idle_Mode(), which + ** assigns the infantry to HUNT; we must use Set_Mission() to override + ** this state.) + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < itable[i].AllyCount; k++) { + obj = new InfantryClass (itable[i].AllyType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create Soviet infantry + */ + for (k = 0; k < itable[i].SovietCount; k++) { + obj = new InfantryClass (itable[i].SovietType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Scan_Place_Object -- places an object >near< the given cell * + * * + * INPUT: * + * obj ptr to object to Unlimbo * + * cell center of search area * + * * + * OUTPUT: * + * true = object was placed; false = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +int Scan_Place_Object(ObjectClass * obj, CELL cell) +{ + int dist; // for object placement + FacingType rot; // for object placement + FacingType fcounter; // for object placement + int tryval; + CELL newcell; + TechnoClass * techno; + int skipit; + + /* + ** First try to unlimbo the object in the given cell. + */ + if (Map.In_Radar(cell)) { + techno = Map[cell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(cell), DIR_N)) { + return(true); + } + } + } + + /* + ** Loop through distances from the given center cell; skip the center cell. + ** For each distance, try placing the object along each rotational direction; + ** if none are available, try each direction with a random scatter value. + ** If that fails, go to the next distance. + ** This ensures that the closest coordinates are filled first. + */ + for (dist = 1; dist < 32; dist++) { + + /* + ** Pick a random starting direction + */ + rot = Random_Pick(FACING_N, FACING_NW); + + /* + ** Try all directions twice + */ + for (tryval = 0 ; tryval < 2; tryval++) { + + /* + ** Loop through all directions, at this distance. + */ + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + + skipit = false; + + /* + ** Pick a coordinate along this directional axis + */ + newcell = Clip_Move(cell, rot, dist); + + /* + ** If this is our second try at this distance, add a random scatter + ** to the desired cell, so our units aren't all aligned along spokes. + */ + if (tryval > 0) { + newcell = Clip_Scatter (newcell, 1); + } + + /* + ** If, by randomly scattering, we've chosen the exact center, skip + ** it & try another direction. + */ + if (newcell==cell) { + skipit = true; + } + + if (!skipit) { + /* + ** Only attempt to Unlimbo the object if: + ** - there is no techno in the cell + ** - the techno in the cell & the object are both infantry + */ + techno = Map[newcell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(newcell), DIR_N)) { + return(true); + } + } + } + + rot++; + if (rot > FACING_NW) { + rot = FACING_N; + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * * + * INPUT: * + * cell cell to scatter from * + * maxdist max distance to scatter * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Scatter(CELL cell, int maxdist) +{ + int x,y; + int xdist; + int ydist; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + xdist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + x += xdist; + if (x > xmax) { + x = xmax; + } + } else { + x -= xdist; + if (x < xmin) { + x = xmin; + } + } + + /* + ** Adjust the y-coordinate + */ + ydist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + y += ydist; + if (y > ymax) { + y = ymax; + } + } else { + y -= ydist; + if (y < ymin) { + y = ymin; + } + } + + return (XY_Cell(x, y)); +} + + +/*********************************************************************************************** + * Clip_Move -- moves in given direction from given cell; clips to map * + * * + * INPUT: * + * cell cell to start from * + * facing direction to move * + * dist distance to move * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Move(CELL cell, FacingType facing, int dist) +{ + int x,y; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + switch (facing) { + case FACING_N: + y -= dist; + break; + + case FACING_NE: + x += dist; + y -= dist; + break; + + case FACING_E: + x += dist; + break; + + case FACING_SE: + x += dist; + y += dist; + break; + + case FACING_S: + y += dist; + break; + + case FACING_SW: + x -= dist; + y += dist; + break; + + case FACING_W: + x -= dist; + break; + + case FACING_NW: + x -= dist; + y -= dist; + break; + } + + /* + ** Clip to the map + */ + if (x > xmax) + x = xmax; + if (x < xmin) + x = xmin; + + if (y > ymax) + y = ymax; + if (y < ymin) + y = ymin; + + return (XY_Cell(x, y)); +} + + +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var) +{ + if (name == NULL) return; + + /* + ** Fetch the scenario number. + */ + char buf[3]; + memcpy(buf, &name[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char first = buf[0]; + char second = buf[1]; + if (first <= '9' && second <= '9') { + scenario = atoi(buf); + } else { + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scenario = (36 * first) + second; + } +#else + scenario = atoi(buf); +#endif + + /* + ** Fetch the scenario player (side). + */ + player = SCEN_PLAYER_GREECE; + if (name[2] == HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix) { + player = SCEN_PLAYER_SPAIN; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix) { + player = SCEN_PLAYER_GREECE; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_USSR).Prefix) { + player = SCEN_PLAYER_USSR; + } + + /* + ** Fetch the direction. + */ + dir = SCEN_DIR_EAST; + if (name[5] == 'E') { + dir = SCEN_DIR_EAST; + } else { + dir = SCEN_DIR_WEST; + } + + /* + ** Fetch the variation. + */ + var = SCEN_VAR_A; + var = ScenarioVarType((name[6] - 'A') + SCEN_VAR_A); +} diff --git a/CODE/SCENARIO.H b/CODE/SCENARIO.H new file mode 100644 index 0000000..46b26f1 --- /dev/null +++ b/CODE/SCENARIO.H @@ -0,0 +1,324 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SCENARIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCENARIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SCENARIO_H +#define SCENARIO_H + + +/* +** This class holds the information about the current game being played. This information is +** global to the scenario and is generally of a similar nature to the information that was held +** in the controlling scenario INI file. It is safe to write this structure out as a whole since +** it doesn't contain any embedded pointers. +*/ +class ScenarioClass { + public: + + // Constructor. + ScenarioClass(void); + void Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); + void Set_Scenario_Name(char const * name); + + bool Set_Global_To(int global, bool value); + void Do_BW_Fade(void); + void Do_Fade_AI(void); + + /* + ** This is the source of the random numbers used in the game. This controls + ** the game logic and thus must be in sync with any networked machines. + */ + RandomClass RandomNumber; + + /* + ** This is the difficulty setting of the game. + */ + DiffType Difficulty; // For human player. + DiffType CDifficulty; // For computer players. + + /* + ** This is the main mission timer. This is the timer that is reset at the + ** start of the mission. It, effectively, holds the elapsed time of the + ** mission. + */ + TTimerClass Timer; + + /* + ** This is an array of waypoints; each waypoint corresponds to a letter of + ** the alphabet, and points to a cell number. -1 means unassigned. + ** The CellClass has a bit that tells if that cell has a waypoint attached to + ** it; the only way to find which waypoint it is, is to scan this array. This + ** shouldn't be needed often; usually, you know the waypoint & you want the CELL. + */ + CELL Waypoint[WAYPT_COUNT]; + + /* + ** This holds the system wide mission countdown timer. Time based missions + ** are governed by this timer. Various trigger events can modify and examine + ** this timer. The current value of this timer will display on the game + ** screen. + */ + CDTimerClass MissionTimer; + + /* + ** The shroud regrowth (if enabled) is regulated by this timer. When the + ** timer expires, the shroud will regrow one step. + */ + CDTimerClass ShroudTimer; + + /* + ** The scenario number. + */ + int Scenario; + + /* + ** The theater of the current scenario. + */ + TheaterType Theater; + + /* + ** The full name of the scenario (as it exists on disk). + */ + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + /* + ** Description of the scenario. + */ + char Description[DESCRIP_MAX]; + + /* + ** The filename of the introduction movie. + */ + VQType IntroMovie; + + /* + ** The filename of the briefing movie. + */ + VQType BriefMovie; + + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie; + + /* + ** The filename of the movie to play if the scenario is lost. + */ + VQType LoseMovie; + + /* + ** The filename of the movie to play right after the briefing and + ** just before the game. + */ + VQType ActionMovie; + + /* + ** This is the full text of the briefing. This text will be + ** displayed when the player commands the "restate mission + ** objectives" operation. + */ + char BriefingText[1024]; + + /* + ** This is the theme to start playing at the beginning of the action + ** movie. A score started in this fashion will continue to play as + ** the game progresses. + */ + ThemeType TransitTheme; + + /* + ** The house that the player is to be (obsolete). + */ + HousesType PlayerHouse; + + /* + ** The percentage of money that is allowed to be carried over into the + ** following scenario. + */ + fixed CarryOverPercent; + + /* + ** This is the amount of money that was left over in the previous + ** scenario. + */ + int CarryOverMoney; + + /* + ** This specifies the maximum amount of money that is allowed to be + ** carried over from the previous scenario. This limits the amount + ** regardless of what the carry over percentage is set to. + */ + int CarryOverCap; + + /* + ** This is the percent that the computer controlled base is to be + ** built up to at the scenario start. + */ + int Percent; + + /* + ** Global flags that are used in the trigger system and are persistent + ** over the course of the game. + */ + bool GlobalFlags[30]; + + /* + ** This records the bookmark view locations the player has recorded. + */ + CELL Views[4]; + + /* + ** This is the number of active passable bridges in the current game. + */ + int BridgeCount; + + /* + ** This records the carry over timer value that is used when the mission + ** starts (presuming the appropriate flag is set) and also used when the + ** scenario restarts. + */ + int CarryOverTimer; + + /* + ** If a bridge has been destroyed, then this flag will be set to true. + ** If there is a trigger that depends on this, it might be triggered. + */ + unsigned IsBridgeChanged:1; + + /* + ** If a global has changed and global change trigger events must be + ** processed, then this flag will be set to true. + */ + unsigned IsGlobalChanged:1; + + /* + ** Are the buildings and units in this scenario to carry over into + ** some (unspecified) later scenario and thus have to be recorded + ** at the end? + */ + unsigned IsToCarryOver:1; + + /* + ** Is this scenario to begin by taking the previously recorded + ** carryover objects and creating them onto the map? + */ + unsigned IsToInherit:1; + + /* + ** If Tanya or a civilian is to be automatically evacuated when they enter + ** a transport vehicle, then this flag will be true. + */ + unsigned IsTanyaEvac:1; + + /* + ** These variables are assigned to the chronosphere effect, and control + ** whether the palette should be fading towards b&w or towards color. + */ + unsigned IsFadingBW:1; + unsigned IsFadingColor:1; + + /* + ** If this scenario is to be the last mission of the game (for this side), then + ** this flag will be true. + */ + unsigned IsEndOfGame:1; + + /* + ** If the mission countdown timer is to be inherited from the previous + ** scenario, then this flag will be set to true. + */ + unsigned IsInheritTimer:1; + + /* + ** If the spy plane is to be disabled in this scenario even though circumstances + ** might otherwise indicate that it should appear, then this flag will be true. + */ + unsigned IsNoSpyPlane:1; + + /* + ** If the score screen (and "mission accomplished" voice) is to be skipped when + ** this scenario is finished, then this flag will be true. + */ + unsigned IsSkipScore:1; + + /* + ** If this is to be a one time only mission such that when it is completed, the game + ** will return to the main menu, then this flag will be set to true. + */ + unsigned IsOneTimeOnly:1; + + /* + ** If the map selection is to be skipped then this flag will be true. If this + ** ins't a one time only scenario, then the next scenario will have the same + ** name as the current one but will be for variation "B". + */ + unsigned IsNoMapSel:1; + + /* + ** If trucks are supposed to drop wood crates when they explode, then this flag + ** will be set to true. + */ + unsigned IsTruckCrate:1; + + /* + ** If the initial money is to be assigned as ore in available silos, then + ** this flag will be set to true. + */ + unsigned IsMoneyTiberium:1; + + /* + ** This is the fading countdown timer. As this timer counts down, the + ** fading to b&w or color will progress. This timer represents a + ** percentage of the Options.Get_Saturation() to fade towards. + */ + CDTimerClass FadeTimer; + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + // Timer to set the period for checking if an auto-sonar pulse should be performed. + // This will take place if a player has nothing but subs left in the game. + CDTimerClass AutoSonarTimer; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + bool bLocalProposesDraw; // True if the local player in a 2-player game has a draw offer extended. + bool bOtherProposesDraw; // True if the other player in a 2-player game has a draw offer extended. +#endif + +}; + + +#endif diff --git a/CODE/SCORE.CPP b/CODE/SCORE.CPP new file mode 100644 index 0000000..892cfad --- /dev/null +++ b/CODE/SCORE.CPP @@ -0,0 +1,1935 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/SCORE.CPP 3 3/14/97 12:02a Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCORE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : May 3, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * Draw_Infantrymen -- Draw all the guys on the score screen * + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen * + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly * + * ScoreClass::DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen * + * ScoreClass::Delay -- Pauses waiting for keypress. * + * ScoreClass::Presentation -- Main routine to display score screen. * + * ScoreClass::Print_Graph_Title -- Prints title on score screen. * + * ScoreClass::Print_Minutes -- Print out hours/minutes up to max * + * ScoreClass::Pulse_Bar_Graph -- Pulses the bargraph color. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WIN32 +extern short StreamLowImpact; +#endif + +#include "function.h" + +#define SCORETEXT_X 184 +#define SCORETEXT_Y 8 +#define CASUALTY_Y 88 +#define BUILDING_X 256 +#define BUILDING_Y 128 +#define BARGRAPH_X 266 +#define MAX_BAR_X 318 // max possible is 319 because of bar's right shadow +#define SIZEGBAR 118 +#define HALLFAME_X 11 +#define HALLFAME_Y 120 + +#define MULTISCOREX 30 + +#define TEDIT_FAME 1 +#define NUMINFANTRYMEN 10 +#define NUMFAMENAMES 7 +#define MAX_FAMENAME_LENGTH 11 + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +GraphicBufferClass *PseudoSeenBuff; + +struct InfantryAnim { + int xpos; + int ypos; + void const *shapefile; + void const *remap; + int anim; + int stage; + char delay; + InfantryTypeClass const *Class; +} InfantryMan[NUMINFANTRYMEN]; +void Draw_InfantryMen(void); +void Draw_InfantryMan(int index); +void New_Infantry_Anim(int index, int anim); +void Draw_Bar_Graphs(int i, int gkilled, int nkilled); +void Animate_Cursor(int pos, int ypos); +void Animate_Score_Objs(void); +void Cycle_Wait_Click(bool cycle=true); + +#ifdef FIXIT_SCORE_CRASH +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); +#endif //FIXIT + +void const * Beepy6; +int ControlQ; // cheat key to skip past score/mapsel screens +bool StillUpdating; + +#ifdef WIN32 +char *ScreenNames[2]={"ALIBACKH.PCX", "SOVBACKH.PCX"}; +#else +char *ScreenNames[2]={"ALI-TRAN.WSA", "SOV-TRAN.WSA"}; +#endif + +//#ifdef WIN32 +//TextBlitClass BlitList; +//#endif + + +struct Fame { + char name[MAX_FAMENAME_LENGTH]; + int score; + int level; + int side; +}; + +ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + + +ScoreAnimClass::ScoreAnimClass(int x, int y, void const * data) +{ + XPos = x * RESFACTOR; + YPos = y * RESFACTOR; + Timer = 0; + DataPtr = data; +} + + +ScoreTimeClass::ScoreTimeClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; +} + +void ScoreTimeClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + +ScoreCredsClass::ScoreCredsClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; + Clock1 = MFCD::Retrieve("CLOCK1.AUD"); + CashTurn = MFCD::Retrieve("CASHTURN.AUD"); +} + + +void ScoreCredsClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(Clock1, 255, Options.Normalize_Volume(130)); +#else + Play_Sample(Clock1, 255, Options.Normalize_Volume(50)); +#endif + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + + +ScorePrintClass::ScorePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +ScorePrintClass::ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void ScorePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + delete this; + return; + } + +#ifdef WIN32 + StillUpdating = true; +#endif + if (!Timer) { + Timer = 1; + + int pos = XPos+(Stage*(6*RESFACTOR)); +// print the letter properly + if (Stage) { + Set_Font_Palette(PrimaryPalette); + localstr[0]=((char *)DataPtr)[Stage-1]; + HidPage.Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); + PseudoSeenBuff->Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); +#endif + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + SeenPage.Print(localstr, pos, YPos-1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos, YPos+1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(localstr, pos, YPos-1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos, YPos+1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#endif + } + Stage++; + } +} + + +ScoreScaleClass::ScoreScaleClass(void const * string, int xpos, int ypos, char const palette[]) : + ScoreAnimClass(xpos, ypos, string) +{ + Palette = &palette[0]; +#ifdef WIN32 + Stage = 0; +#else + Stage = 5; +#endif +} + + +void ScoreScaleClass::Update(void) +{ + static int _destx[]={0,80,107,134,180,228}; + static int _destw[]={6,20, 30, 40, 60, 80}; + + /* + ** Restore the background for the scaled-up letter + */ + if (!Timer) { + Timer = 1; +#ifndef WIN32 + if (Stage != 5) { + int destx = _destx[Stage+1]*RESFACTOR; + int destw = _destw[Stage+1]*RESFACTOR; + HidPage.Blit(SeenPage, destx, YPos, destx, YPos, (destx + destw) <= 320 * RESFACTOR ? destw : (320 * RESFACTOR) - destx, (YPos + destw) <= 200 * RESFACTOR ? destw : (200 * RESFACTOR) - YPos); + } +#endif + if (Stage) { + Set_Font_Palette(Palette); + HidPage.Fill_Rect(0, 0, 7*RESFACTOR, 7*RESFACTOR, TBLACK); + HidPage.Print((char *)DataPtr, 0, 0, TBLACK, TBLACK); + HidPage.Scale(SeenPage, 0, 0, _destx[Stage]*RESFACTOR, YPos, 5*RESFACTOR, 6*RESFACTOR, _destw[Stage]*RESFACTOR, _destw[Stage]*RESFACTOR, true); + Stage--; + } else { + Set_Font_Palette(Palette); + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]==this) ScoreObjs[i] = 0; + } + HidPage.Print((char *)DataPtr, XPos, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#endif + delete this; + return; + } + } +} + +int Alloc_Object(ScoreAnimClass *obj) +{ + int i,ret; + + for (i = ret = 0; i < MAXSCOREOBJS; i++) { + if (!ScoreObjs[i]) { + ScoreObjs[i] = obj; + ret = i; + break; + } + } + return(ret); +} + + + +/*********************************************************************************************** + * ScoreClass::Presentation -- Main routine to display score screen. * + * * + * This is the main routine that displays the score screen graphics. * + * It gets called at the end of each scenario and is used to present * + * the results and a rating of the player's battle. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 : Created. * + *=============================================================================================*/ +static char const _bluepal[]={0xC0,0xC1,0xC1,0xC3,0xC2,0xC5,0xC3,0xC7,0xC4,0xC9,0xCA,0xCB,0xCC,0xCD,0xC0,0xCF}; +static char const _greenpal[]={0x70,0x71,0x7C,0x73,0x7D,0x75,0x7E,0x77,0x7F,0x79,0x7A,0x7B,0x7C,0x7D,0x7C,0x7F}; +static char const _redpal[]={0xD0,0xD1,0xD7,0xD3,0xD9,0xD5,0xDA,0xD7,0xDB,0xD9,0xDA,0xDB,0xDC,0xDD,0xD6,0xDF}; +static char const _yellowpal[]={0x0,0x0,0xEC,0x0,0xEB,0x0,0xEA,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; +void ScoreClass::Presentation(void) +{ +#ifdef WIN32 +// if (Keyboard != NULL) return; +#endif + static int const _casuax[2]={144,150}; + static int const _casuay[2]={ 78, 78}; + static int const _gditxy[2]={ 90, 90}; + +#if defined(FRENCH) || defined(GERMAN) + static int const _gditxx[2]={130,150}; + static int const _nodtxx[2]={130,150}; +#else + static int const _gditxx[2]={135,150}; + static int const _nodtxx[2]={135,150}; +#endif + static int const _nodtxy[2]={102,102}; + static int const _bldggy[2]={138,138}; + static int const _bldgny[2]={150,150}; + +#ifdef WIN32 +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Disable_Uncompressed_Shapes(); +#endif //FIXIT + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + int i; + void const * yellowptr; + void const * redptr; + CCFileClass file(FAME_FILE_NAME); + struct Fame hallfame[NUMFAMENAMES]; + void *oldfont; + int oldfontxspacing = FontXSpacing; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 +#ifdef WIN32 + char inter_pal[15]; + sprintf(inter_pal, "SCORPAL1.PAL"); +#endif + + ControlQ = 0; + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_SCORE); + +#ifdef WIN32 + VisiblePage.Clear(); + SysMemPage.Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + Set_Logic_Page(SysMemPage); +#else + SeenPage.Clear(); + HidPage.Clear(); + Set_Logic_Page(HidPage); +#endif + BlackPalette.Set(); + + + void const * country4 = MFCD::Retrieve("COUNTRY4.AUD"); + void const * sfx4 = MFCD::Retrieve("SFX4.AUD"); + Beepy6 = MFCD::Retrieve("BEEPY6.AUD"); + + /* + ** Load the background for the score screen + */ +#ifndef WIN32 + void *anim = Open_Animation(ScreenNames[house], NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); +#endif + + unsigned minutes = (unsigned)((ElapsedTime / (long)TIMER_MINUTE))+1; + +// Load up the shapes for the Nod score screen +#ifdef WIN32 + yellowptr = MFCD::Retrieve("BAR3BHR.SHP"); + redptr = MFCD::Retrieve("BAR3RHR.SHP"); +#else + if (!house) { + yellowptr = MFCD::Retrieve("BAR3BLU.SHP"); + redptr = MFCD::Retrieve("BAR3RED.SHP"); + } +#endif + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + +/* --- Now display the background animation --- */ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen(ScreenNames[house], &HidPage, ScorePalette); + Increase_Palette_Luminance (ScorePalette , 30, 30, 30, 63); + HidPage.Blit(SeenPage); + HidPage.Blit(*PseudoSeenBuff); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(60)); +#endif + +#ifndef WIN32 + int frame = 1; + StreamLowImpact = true; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, SeenPage, frame++); + Call_Back_Delay(2); + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); +#endif + + /* + ** Background's up, so now load various shapes and animations + */ +#ifdef WIN32 + void const * timeshape = MFCD::Retrieve("TIMEHR.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISC1-HR.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISC2-HR.SHP"); +#else + void const * timeshape = MFCD::Retrieve("TIME.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISCORE1.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISCORE2.SHP"); +#endif + ScoreObjs[0] = new ScoreTimeClass(238, 2, timeshape, 30, 4); + ScoreObjs[1] = new ScoreTimeClass(4, 89, hiscore1shape, 10, 4); + ScoreObjs[2] = new ScoreTimeClass(4, 180, hiscore2shape, 10, 4); + + /* Now display the stuff */ +#ifdef WIN32 +#else + SeenPage.Blit(HidPage); +#endif + Set_Logic_Page(SeenBuff); + + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 198, 9, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 204, 9, _greenpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_LEAD, 164, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_EFFI, 164, 38, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOTA, 164, 50, _greenpal)); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(13); + + int scorecounter = 0; + + Keyboard->Clear(); + + /* + ** Determine leadership rating. + */ + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + HousesType owner = object->Owner(); + if ( (house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE) ) { + leadership++; + } else { + if ( (!house) && (object->Owner() == HOUSE_GREECE) ) { + leadership++; + } + } + } + int uspoints = 0; + + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass *hows = HouseClass::As_Pointer(hous); + if (hous == HOUSE_USSR || hous == HOUSE_BAD || hous == HOUSE_UKRAINE) { + NKilled += hows->UnitsLost; + NBKilled += hows->BuildingsLost; + } else { + GKilled += hows->UnitsLost; + GBKilled += hows->BuildingsLost; + } + if (PlayerPtr->Is_Ally(hous) ) { + uspoints += hows->PointTotal; + } + } +// if(uspoints < 0) uspoints = 0; +// uspoints += 1000; //BG 1000 bonus points for winning mission + + /* + ** Bias the base score upward according to the difficulty level. + */ + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + + + if (!leadership) leadership++; + leadership = 100*fixed(leadership, (house ? NKilled+NBKilled+leadership : GKilled+GBKilled+leadership)); + leadership = min(150,leadership); + + /* + ** Determine economy rating. + */ + int init = PlayerPtr->Control.InitialCredits; + int cred = PlayerPtr->Available_Money(); + + int economy = 100*fixed((unsigned)PlayerPtr->Available_Money()+1+PlayerPtr->StolenBuildingsCredits, PlayerPtr->HarvestedCredits + (unsigned)PlayerPtr->Control.InitialCredits+1); + economy=min(economy,150); + + int total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (total < -9999) total = -9999; + total = min(total, 99999); + +Keyboard->Clear(); + for (i = 0; i <= 130; i++) { + Set_Font_Palette(_greenpal); + int lead = (leadership * i) / 100; + Count_Up_Print("%3d%%", lead, leadership, 244, 26); + if (i>=30) { + int econo = (economy * (i-30)) / 100; + Count_Up_Print("%3d%%", econo, economy, 244, 38); + } + Print_Minutes(minutes); + Call_Back_Delay(1); +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(100)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(40)); +#endif + if ( (i >= 30) && (i >= leadership) && ((i-30) >= economy) ) break; +//BG if (Keyboard->Check()) break; + } + Count_Up_Print("%3d%%", leadership, leadership, 244, 26); + Count_Up_Print("%3d%%", economy, economy, 244, 38); + + char buffer[16]; + sprintf(buffer, "x %5d",uspoints); + Alloc_Object(new ScorePrintClass(buffer, 274, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(buffer, 274, 38, _greenpal)); + Call_Back_Delay(8); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, WHITE); + Call_Back_Delay(1); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, GREEN); + + sprintf(buffer,"%5d", total); + Alloc_Object(new ScorePrintClass(buffer, 286, 50, _greenpal)); + +//BG if (!Keyboard->Check()) { + Call_Back_Delay(60); +//BG } + + if (house) Show_Credits(house, _greenpal); + +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(60); + + /* + ** Show stats on # of units killed + */ + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + int indx = house; +#ifdef WIN32 + indx = 0; +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_CASU, _casuax[indx], _casuay[indx], _greenpal)); + Call_Back_Delay(9); + if (house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _gditxy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _nodtxy[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _gditxy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _nodtxy[indx], _redpal)); + } + Call_Back_Delay(6); + + Set_Font_Palette(_redpal); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); +#else + if (house) { + Do_Nod_Casualties_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); + } +#endif + + Set_Logic_Page(SeenBuff); + + /* + ** Print out stats on buildings destroyed + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif +#ifdef WIN32 + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); +#else + if (!house) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); + } else { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL1, 150, 118, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL2, 150, 126, _greenpal)); + Call_Back_Delay(13); + } +#endif + if(house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldggy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldgny[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldggy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldgny[indx], _redpal)); + } + Call_Back_Delay(7); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); +#else + if (house) { + Call_Back_Delay(6); + Set_Font_Palette(_greenpal); + Do_Nod_Buildings_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); + } +#endif + +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + + Keyboard->Clear(); + + if (!house) Show_Credits(house, _greenpal); + /* + ** Hall of fame display and processing + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 28, 110, _greenpal)); + Call_Back_Delay(9); + + /* + ** First check for the existence of the file, and if there isn't one, + ** make a new one filled with blanks. + */ + if (!file.Is_Available()) { + + // hall of fame doesn't exist, so blank it out & write it + file.Open(WRITE); + + for (i = 0; i < NUMFAMENAMES; i++) { + hallfame[i].name[0] = + hallfame[i].score = + hallfame[i].level = 0; + hallfame[i].side = 0; + file.Write(&hallfame[i], sizeof(struct Fame)); + } + + file.Close(); + } + + file.Open(READ); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Read(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + + /* + ** If the player's score is good enough to bump someone off the list, + ** remove their data, move everyone down a notch, and set index = where + ** their info goes + */ + if (hallfame[NUMFAMENAMES-1].score >= total) + hallfame[NUMFAMENAMES-1].score = 0; + for (index = 0; index < NUMFAMENAMES; index++) { + if (total > hallfame[index].score) { + if (index < (NUMFAMENAMES-1)) for (i = (NUMFAMENAMES-1); i > index; i--) hallfame[i] = hallfame[i-1]; + hallfame[index].score = total; + hallfame[index].level = Scen.Scenario; + hallfame[index].name[0] = 0; // blank out the name + hallfame[index].side = house; + break; + } + } + + /* + ** Now display the hall of fame + */ + Set_Logic_Page(SeenBuff); + +#ifdef WIN32 + char maststr[NUMFAMENAMES*32]; +#endif + char const *pal; + for (i = 0; i < NUMFAMENAMES; i++) { + pal = hallfame[i].side ? _redpal : _bluepal; + Alloc_Object(new ScorePrintClass(hallfame[i].name, HALLFAME_X, HALLFAME_Y + (i*8), pal)); + if (hallfame[i].score) { +#ifdef WIN32 + char *str = maststr + i*32; +#else + char *str = (char *)(HidPage.Get_Buffer()) + i*32; +#endif + sprintf(str, "%d", hallfame[i].score); + Alloc_Object(new ScorePrintClass(str, HALLFAME_X+(6*14), HALLFAME_Y + (i*8), pal, BLACK)); + if (hallfame[i].level < 20) { + sprintf(str+16, "%d", hallfame[i].level); + } else { + strcpy(str+16, "**"); + } + Alloc_Object(new ScorePrintClass(str+16, HALLFAME_X+(6*11), HALLFAME_Y + (i*8), pal, BLACK)); + Call_Back_Delay(13); + } + } +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + /* + ** If the player's on the hall of fame, have him enter his name now + */ + Keyboard->Clear(); + + if (index < NUMFAMENAMES) { + pal = hallfame[index].side ? _redpal : _bluepal; + Input_Name(hallfame[index].name, HALLFAME_X, HALLFAME_Y + (index*8), pal); + + file.Open(WRITE); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Write(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + } else { + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 149, 190, _yellowpal)); + ControlQ = false; + Cycle_Wait_Click(); + } + + Keyboard->Clear(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + Show_Mouse(); +// Map_Selection(); +// Scen.ScenVar = SCEN_VAR_A; +// Scen.ScenDir = SCEN_DIR_EAST; + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + GamePalette.Set(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + +#ifdef WIN32 + delete PseudoSeenBuff; +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Enable_Uncompressed_Shapes(); +#endif //FIXIT + +#endif +} + + +void Cycle_Wait_Click(bool cycle) +{ + int counter = 0; + int minclicks = 20; + unsigned long timingtime = TickCount; + SerialPacketType sendpacket; + SerialPacketType receivepacket; + int packetlen; + + + Keyboard->Clear(); + while (minclicks || (!Keyboard->Check() && !ControlQ) ) { + + if (Session.Type == GAME_NULL_MODEM || + Session.Type == GAME_MODEM) { + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&sendpacket, 0, sizeof(SerialPacketType)); + sendpacket.Command = SERIAL_SCORE_SCREEN; + sendpacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + sendpacket.ID = Session.ModemType; + + NullModem.Send_Message (&sendpacket, sizeof(sendpacket), 0); + timingtime = TickCount; + } + + if (NullModem.Get_Message (&receivepacket, &packetlen) > 0) { + // throw packet away + packetlen = packetlen; + } + + NullModem.Service(); + } + + Call_Back_Delay(1); + if (minclicks) { + minclicks--; + Keyboard->Clear(); + } + + if(cycle) { + counter = ((++counter) & 7); + if (counter == 0 && Options.IsPaletteScroll) { + RGBClass rgb = ScorePalette[233]; + for (int i = 233; i < 237; i++) { + ScorePalette[i] = ScorePalette[i+1]; + } + ScorePalette[237] = rgb; + ScorePalette.Set(); + } + } + } + Keyboard->Clear(); +} + +void ScoreClass::Do_Nod_Buildings_Graph(void) +{ + int shapenum; + InfantryTypeClass const *ramboclass; + + void const * factptr = MFCD::Retrieve("POWR.SHP"); + void const * rmboptr = MFCD::Retrieve("E7.SHP"); + void const * fball1ptr = MFCD::Retrieve("FBALL1.SHP"); + ramboclass = &InfantryTypeClass::As_Reference(INFANTRY_TANYA); + + /* + ** Print the # of buildings on the hidpage so we only need to do it once + */ + SeenPage.Blit(HidPage); + Set_Logic_Page(HidPage); + Call_Back_Delay(30); + Set_Font_Palette(_redpal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 10, TBLACK, TBLACK); + Set_Font_Palette(_bluepal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 22, TBLACK, TBLACK); + + /* + ** Here's the animation/draw loop for blowing up the factory + */ + for (int i=0; i<98; i++) { + HidPage.Blit(HidPage, BUILDING_X, BUILDING_Y, 0, 0, 320-BUILDING_X, 48); + shapenum = 0; // no damage + if (i >= 60) { + shapenum = Extract_Shape_Count(factptr) - 2; // some damage + if (i == 60) { + Shake_The_Screen(6); + Sound_Effect(VOC_CRUMBLE); + } + if (i > 65) { + shapenum = Extract_Shape_Count(factptr) - 1; // mega damage + } + } + + /* + ** Draw the building before Rambo + */ + if (i < 68) { + CC_Draw_Shape(factptr, shapenum, 0, 0, WINDOW_MAIN, + SHAPE_GHOST|SHAPE_FADING|SHAPE_WIN_REL, ColorRemaps[PCOLOR_GOLD].RemapTable, DisplayClass::UnitShadow); + + } + + /* + ** Now draw some fires, if appropriate + */ + if (i >= 61) { + int firecount = Extract_Shape_Count(fball1ptr); + int shapeindex = (i-61) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 10, 10, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + if (i > 64) { + shapeindex = (i-64) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 50, 30, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + } + } + /* + ** Draw the Tanya character running away from the building + */ + CC_Draw_Shape(rmboptr, (ramboclass->DoControls[DO_WALK].Frame + ramboclass->DoControls[DO_WALK].Jump*6) + ((unsigned(i)>>1)%ramboclass->DoControls[DO_WALK].Count), + i+32, 40, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + ColorRemaps[PCOLOR_RED].RemapTable, DisplayClass::UnitShadow); + HidPage.Blit(SeenPage, 0, 0, BUILDING_X, BUILDING_Y, 320-BUILDING_X, 48); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(1); + } + + i = max(GBKilled, NBKilled); + for (int q = 0; q <= i; q++) { + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", q, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", q, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(1); +//BG } + } + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", NBKilled, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", GBKilled, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +} + + +/*************************************************************************** + * DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen* + * * + * * + * * + * INPUT: yellowptr, redptr = pointers to shape file for graphs * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 BWG : Created. * + *=========================================================================*/ + +void ScoreClass::Do_GDI_Graph(void const * yellowptr, void const * redptr, int gkilled, int nkilled, int ypos) +{ + int i, maxval; +#ifdef WIN32 + int xpos = 174; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 + if(house) { + int temp = gkilled; + gkilled = nkilled; + nkilled = temp; + void const *tempptr = yellowptr; + yellowptr = redptr; + redptr = tempptr; + } +#else + int xpos = 173; +#endif + int gdikilled = gkilled, nodkilled=nkilled; + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + gdikilled = (gdikilled * SIZEGBAR) / maxval; + nodkilled = (nodkilled * SIZEGBAR) / maxval; + if (maxval < 20) { + gdikilled = gkilled * 5; + nodkilled = nkilled * 5; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + // Draw the white-flash shape on the hidpage + Set_Logic_Page(HidPage); + HidPage.Fill_Rect(0, 0, 124*RESFACTOR, 9*RESFACTOR, TBLACK); + CC_Draw_Shape(redptr, 119, 0, 0, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Set_Font_Palette(house ? _redpal : _bluepal); +#else + Set_Font_Palette(_bluepal); +#endif + + for (i = 1; i <= gdikilled; i++) { + if (i != gdikilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*gkilled) / maxval, gkilled, 297, ypos+2); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + Count_Up_Print("%d", gkilled, gkilled, 297, ypos+ 2); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); + +#ifdef WIN32 + Set_Font_Palette(house ? _bluepal : _redpal); +#else + Set_Font_Palette(_redpal); +#endif + for (i = 1; i <= nodkilled; i++) { + if (i != nodkilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*nkilled) / maxval, nkilled, 297, ypos+14); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + +// if (Keyboard::Check()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", nkilled, nkilled, 297, ypos+14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); +} + + +void ScoreClass::Do_Nod_Casualties_Graph(void) +{ + int i, gdikilled, nodkilled, maxval; + + void const * e1ptr = MFCD::Retrieve("E1.SHP"); + + gdikilled = GKilled; + nodkilled = NKilled; + maxval = max(gdikilled, nodkilled); + + if (!maxval) maxval=1; + if ((gdikilled > (MAX_BAR_X - BARGRAPH_X)) || (nodkilled > (MAX_BAR_X - BARGRAPH_X)) ) { + gdikilled = (gdikilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + nodkilled = (nodkilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + /* + ** Initialize a bunch of objects for the infantrymen who pose for the bar + ** graphs of casualties. + */ + int r = NUMINFANTRYMEN/2; + for (i = 0; i < NUMINFANTRYMEN/2; i++) { + InfantryMan[i+0].xpos = + InfantryMan[i+r].xpos = (i*10) + 7; + InfantryMan[i+0].ypos = 11; + InfantryMan[i+r].ypos = 21; + InfantryMan[i+0].shapefile = + InfantryMan[i+r].shapefile = e1ptr; + InfantryMan[i+0].remap = ColorRemaps[PCOLOR_RED].RemapTable; + InfantryMan[i+r].remap = ColorRemaps[PCOLOR_BLUE].RemapTable; + InfantryMan[i+0].anim = + InfantryMan[i+r].anim = 0; + InfantryMan[i+0].stage = + InfantryMan[i+r].stage = 0; + InfantryMan[i+0].delay = + InfantryMan[i+r].delay = NonCriticalRandomNumber & 0x1F; + InfantryMan[i+0].Class = + InfantryMan[i+r].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + } + + /* + ** Draw the infantrymen and pause briefly before running the graph + */ + Draw_InfantryMen(); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(40); + + for (i = 1; i <= maxval; i++) { + // Draw & update infantrymen 3 times for every tick on the graph (i) + for (int index = 0; index < 3; index++) { + Draw_InfantryMen(); + Draw_Bar_Graphs(i, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + + Set_Font_Palette(_redpal); + Count_Up_Print("%d", (i*NKilled) / maxval, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", (i*GKilled) / maxval, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(3); + } +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + } +//BG if (Keyboard->Check()) Keyboard->Clear(); + + /* + ** Make sure accurate count is printed at end + */ + Set_Font_Palette(_redpal); + Count_Up_Print("%d", NKilled, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", GKilled, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + + /* + ** Finish up death animations, if there are any active + */ + int k = 1; + while (k) { + for (i=k=0; i= DO_GUN_DEATH) { + k=1; + } + } + if (k) { + Draw_InfantryMen(); + } + Draw_Bar_Graphs(maxval, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(1); + } +} + + +void ScoreClass::Show_Credits(int house, char const pal[]) +{ + static int _credsx[2]={276,276}; + static int _credsy[2]={173,58}; + static int _credpx[2]={228,236}; +#ifdef GERMAN + static int _credpy[2]={181, 74}; + static int _credtx[2]={162,162}; + static int _credty[2]={173, 62}; +#else + static int _credpy[2]={189-12, 74}; + static int _credtx[2]={182,182}; + static int _credty[2]={179-12, 62}; +#endif + + int credobj,i; + int minval,add; + +#ifdef WIN32 + void const * credshape = MFCD::Retrieve(house ? "CREDSUHR.SHP" : "CREDSAHR.SHP"); +#else + void const * credshape = MFCD::Retrieve(house ? "CREDSU.SHP" : "CREDSA.SHP"); +#endif + + Alloc_Object(new ScorePrintClass(TXT_SCORE_ENDCRED, _credtx[house], _credty[house], pal)); + Call_Back_Delay(15); + + credobj = Alloc_Object(new ScoreCredsClass(_credsx[house], _credsy[house], credshape, 32, 2)); + minval = PlayerPtr->Available_Money() / 100; + + /* + ** Print out total credits left at end of scenario + */ + i = -50; + + do { + add = 5; + if ((PlayerPtr->Available_Money() - i) > 100 ) add += 15; + if ((PlayerPtr->Available_Money() - i) > 500 ) add += 30; + if ((PlayerPtr->Available_Money() - i) > 1000) add += PlayerPtr->Available_Money() / 40; + if (add < minval) add = minval; + i += add; + + if (i < 0) i=0; + + Set_Font_Palette(pal); + Count_Up_Print("%d", i, PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Call_Back_Delay(2); +/*BG if (Keyboard->Check()) { + Count_Up_Print("%d", PlayerPtr->Available_Money(), PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Keyboard->Clear(); + break; + }*/ + } while (i < PlayerPtr->Available_Money()) ; + + delete ScoreObjs[credobj]; + ScoreObjs[credobj] = 0; +} + + +/*************************************************************************** + * SCORECLASS::PRINT_MINUTES -- Print out hours/minutes up to max * + * * + * Same as count-up-print, but for the time * + * * + * INPUT: current minute count and maximum * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void ScoreClass::Print_Minutes(int minutes) +{ + char str[20]; + if (minutes >= 60) { + if ((minutes/60) > 9) minutes = (9*60 + 59); + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT1), (minutes / 60), (minutes % 60)); + } else { + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT2), minutes); + } + SeenPage.Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly. * + * * + * This routine prints out a number (like 70) or its maximum number, into a string, onto * + * the screen, on a clean section of the screen, and blits it forward to the seenpage so you* + * can print without flashing and can print over something (to count up %'s). * + * * + * INPUT: str = string to print into * + * percent = # to print * + * max = # to print if percent > max * + * xpos = x pixel coord * + * ypos = y pixel coord * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/07/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Count_Up_Print(char *str, int percent, int maxval, int xpos, int ypos) +{ + char destbuf[64]; + + sprintf(destbuf, str, percent <= maxval ? percent : maxval); + SeenPage.Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#ifdef WIN32 + PseudoSeenBuff->Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Input_Name -- Gets the name from the keyboard * + * * + * This routine handles keyboard input, and does a nifty zooming letter effect too. * + * * + * INPUT: str = string to put user's typing into * + * xpos = x pixel coord * + * ypos = y pixel coord * + * pal = text remapping palette to print using * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Input_Name(char str[], int xpos, int ypos, char const pal[]) +{ + int key = 0; + int ascii, index=0; + + void const * keystrok = MFCD::Retrieve("KEYSTROK.AUD"); + + /* + ** Ready the hidpage so it can restore background under zoomed letters + */ + SeenPage.Blit(HidPage); + + /* + ** Put a copy of the high score area on a spare area of the hidpage, so + ** we can use it to restore the letter's background instead of filling + ** with black. + */ + HidPage.Blit(HidPage, 0, 100*RESFACTOR, 0, 0, 100*RESFACTOR, 100*RESFACTOR); + + do { + Call_Back(); + Animate_Score_Objs(); + Animate_Cursor(index, ypos); + if (Keyboard->Check()) { + key = Keyboard->To_ASCII(Keyboard->Get()) & 0xFF; + Call_Back(); + + if (index == MAX_FAMENAME_LENGTH-2) { + while (Keyboard->Check()) { + Keyboard->Get(); + } + } + + /* + ** If they hit 'backspace' when they're on the last letter, + ** turn it into a space instead. + */ + if ((key == KA_BACKSPACE) && (index == MAX_FAMENAME_LENGTH-2) ) { + if (str[index] && str[index]!=32) key = 32; + } + if (key == KA_BACKSPACE) { //if (key == KN_BACKSPACE) { + if (index) { + str[--index] = 0; + + int xposindex6 = (xpos+(index*6))*RESFACTOR; + HidPage.Blit(SeenPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #endif + HidPage.Blit(HidPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + } + + } else if (key != KA_RETURN) { //else if (key != KN_RETURN && key!=KN_KEYPAD_RETURN) { + ascii = key; //ascii = KN_To_KA(key); + if (ascii >= 'a' && ascii <= 'z') ascii -= ('a' - 'A'); + if ( (ascii >= '!' && ascii <= KA_TILDA) || ascii == ' ') { + HidPage.Blit(SeenPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + #endif + HidPage.Blit(HidPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); + str[index] = ascii; + str[index+1] = 0; + + int objindex; +#ifdef WIN32 + Play_Sample(keystrok, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(keystrok, 255, Options.Normalize_Volume(105)); +#endif + objindex = Alloc_Object(new ScoreScaleClass(str+index, xpos+(index*6), ypos, pal)); + while (ScoreObjs[objindex]) Call_Back_Delay(1); + + if (index < (MAX_FAMENAME_LENGTH-2) ) index++; + } + } + } + } while (key != KA_RETURN); // } while(key != KN_RETURN && key!=KN_KEYPAD_RETURN); +} + + +void Animate_Cursor(int pos, int ypos) +{ + static int _lastpos = 0, _state; + static CDTimerClass _timer; + + ypos += 6; // move cursor to bottom of letter + + ypos *= RESFACTOR; + + // If they moved the cursor, erase old one and force state=0, to make green draw right away + if (pos != _lastpos) { + HidPage.Blit(SeenPage, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#endif + _lastpos = pos; + _state = 0; + } + SeenBuff.Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#endif + /* + ** Toggle the color of the cursor, green or black, if it's time to do so. + */ + if (!_timer) { + _state ^= 1; + _timer = 5; + } +} + + +/*************************************************************************** + * Draw_InfantryMen -- Draw all the guys on the score screen * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMen() +{ + int k; + +// Only draw the infantrymen if we're playing USSR... Allies wouldn't execute +// people like that. + + /* + ** First restore the background + */ + HidPage.Blit(HidPage, BARGRAPH_X, CASUALTY_Y, 0, 0, 320-BARGRAPH_X, 34); + Set_Logic_Page(HidPage); + + /* + ** Then draw all the infantrymen on the clean hidpage + */ + for (k = 0; k < NUMINFANTRYMEN; k++) Draw_InfantryMan(k); + /* + ** They'll all be blitted over to the seenpage after the graphs are drawn + */ +} + +/*************************************************************************** + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * * + * This routine draws one of the infantrymen in the "Casualties" area * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMan(int index) +{ + int stage; + + /* If the infantryman's dead, just abort this function */ + if (InfantryMan[index].anim == -1) return; + + stage = InfantryMan[index].stage + InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Frame; + + CC_Draw_Shape(InfantryMan[index].shapefile, + stage, + InfantryMan[index].xpos, + InfantryMan[index].ypos, + WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + InfantryMan[index].remap, + DisplayClass::UnitShadow); + /* + ** see if it's time to run a new anim + */ + if (--InfantryMan[index].delay <= 0) { + InfantryMan[index].delay = 3; + if (++InfantryMan[index].stage >= InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Count) { + + /* + ** was he playing a death anim? If so, and it's done, erase him + */ + if (InfantryMan[index].anim >= DO_GUN_DEATH) { + InfantryMan[index].anim = -1; + } else { + New_Infantry_Anim(index, DO_STAND_READY); + } + } + } +} + + +/*************************************************************************** + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen* + * * + * * + * * + * INPUT: index: which of the 30 infantrymen to affect * + * anim: which animation sequence to start him into * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void New_Infantry_Anim(int index, int anim) +{ + InfantryMan[index].anim = anim; + InfantryMan[index].stage = 0; + if (anim >= DO_GUN_DEATH) { + InfantryMan[index].delay = 1; // start right away + } else { + InfantryMan[index].delay = NonCriticalRandomNumber & 15; + } +} + + +/*************************************************************************** + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * * + * * + * * + * INPUT: i = current count of how far to draw graph * + * gkilled = # of GDI forces killed (adjusted to fit in space) * + * nkilled = # of Nod forces killed (adjusted to fit in space) * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + * 07/02/1996 BWG : Removed references to civilians. * + *=========================================================================*/ +void Draw_Bar_Graphs(int i, int gkilled, int nkilled) +{ + + if (gkilled) { + LogicPage->Fill_Rect(0, 0+4*RESFACTOR, 0+min(i, gkilled)*RESFACTOR, 0+5*RESFACTOR, RED); + LogicPage->Draw_Line(0+1*RESFACTOR, 0+6*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+6*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, TBLACK); + if (i <= gkilled) { + int anim = InfantryMan[i/11].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(i/11, DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim(i/11, DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + if (nkilled) { + LogicPage->Fill_Rect( 0, 0+16*RESFACTOR, 0+min(i, nkilled)*RESFACTOR, 0+17*RESFACTOR, LTCYAN); + LogicPage->Draw_Line( 0+1*RESFACTOR, 0+18*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+18*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, TBLACK); + if (i <= nkilled) { + int anim = InfantryMan[(NUMINFANTRYMEN/2)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } +} + + +/*************************************************************************** + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * * + * This is just to cut down on code size and typing a little. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Call_Back_Delay(int time) +{ + if (time < 0 ) time = 0; + if (time > 60) time = 60; + CDTimerClass cd; + CDTimerClass callbackcd = 0; + + if (!ControlQ) { + if (Keyboard->Down(KN_LCTRL) && Keyboard->Down(KN_Q)) { + ControlQ = 1; + Keyboard->Clear(); + } + } + if (ControlQ) time=0; + + cd = time; + StreamLowImpact = true; + do { + if (callbackcd == 0) { + Call_Back(); + callbackcd = TIMER_SECOND/4; + } + Animate_Score_Objs(); + } while (cd); + StreamLowImpact = false; +} + + +void Animate_Score_Objs() +{ +#ifdef WIN32 + StillUpdating = false; + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + PseudoSeenBuff->Blit(SeenPage); + } +#endif + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]) { + ScoreObjs[i]->Update(); + } + } +} + +char *Int_Print(int a) +{ + static char str[10]; + + sprintf(str, "%d", a); + return str; +} + + +/*********************************************************************************************** + * Multi_Score_Presentation -- Multiplayer routine to display score screen. * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 BWG: Created. * + *=============================================================================================*/ +extern int CopyType; + +void Multi_Score_Presentation(void) +{ + char remap[16]; +#ifdef WIN32 + GraphicBufferClass *pseudoseenbuff = new GraphicBufferClass(320, 200, (void*)NULL); + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + + int i,k; + void *oldfont; + int oldfontxspacing = FontXSpacing; + + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); +// Theme.Queue_Song(THEME_WIN); + + BlackPalette.Set(); + SeenPage.Clear(); + HidPage.Clear(); + Hide_Mouse(); + void *anim = Open_Animation("MLTIPLYR.WSA", NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); + /* + ** Display the background animation + */ +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); +for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); +CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); + + int frame = 1; + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + Animate_Frame(anim, *pseudoseenbuff, frame++); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + } + Close_Animation(anim); + +#ifdef WIN32 + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , PseudoSeenBuff , NULL); + CopyType = 0; +#endif + + /* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + + Set_Logic_Page(SeenBuff); + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 113, 13, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 130, 13, _greenpal)); +#endif + Call_Back_Delay(5); + Alloc_Object(new ScorePrintClass(TXT_COMMANDER, 27, 31, _greenpal)); + Call_Back_Delay(10); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 113, 31, _greenpal)); +#endif +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 118, 31, _greenpal)); +#endif +#ifdef ENGLISH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 126, 31, _greenpal)); +#endif + Call_Back_Delay(13); + Alloc_Object(new ScorePrintClass(TXT_KILLS_COLON, 249, 31, _greenpal)); + Call_Back_Delay(6); + + /* + ** Move all the scores over a notch if there's more games than can be + ** shown (which is known by Session.CurGame == MAX_MULTI_GAMES-1); + */ + if (Session.CurGame == MAX_MULTI_GAMES-1) { + for (i = 0; i < MAX_MULTI_NAMES; i++) { + for (k = 0; k < MAX_MULTI_GAMES-1; k++) { + Session.Score[i].Kills[k] = Session.Score[i].Kills[k+1]; + } + } + } + + int y = 41; + for (i = 0; i < MAX_MULTI_NAMES; i++) { + if (strlen(Session.Score[i].Name)) { + int color = Session.Score[i].Color; + remap[ 8] = ColorRemaps[color].FontRemap[11]; + remap[ 6] = ColorRemaps[color].FontRemap[12]; + remap[ 4] = ColorRemaps[color].FontRemap[13]; + remap[ 2] = ColorRemaps[color].FontRemap[14]; + remap[14] = ColorRemaps[color].FontRemap[15]; + + Alloc_Object(new ScorePrintClass(Session.Score[i].Name, 15, y, remap)); + Call_Back_Delay(20); + + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Wins), 118, y, remap)); + Call_Back_Delay(6); + + for (k = 0; k <= min(Session.CurGame, MAX_MULTI_GAMES-2); k++) { + if (Session.Score[i].Kills[k] >= 0) { + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Kills[k]), 225+(24*k), y, remap)); + Call_Back_Delay(6); + } + } + y += 12; + } + } + +#if defined(GERMAN) || defined(FRENCH) + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 95 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 109 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#endif + Cycle_Wait_Click(false); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); + SeenPage.Clear(); + GamePalette.Set(); +#ifdef WIN32 + delete PseudoSeenBuff; +#endif + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + Show_Mouse(); +} + +void ScoreClass::Init(void) +{ + Score = 0; + NKilled = 0; + GKilled = 0; + CKilled = 0; + NBKilled = 0; + GBKilled = 0; + CBKilled = 0; + NHarvested = 0; + GHarvested = 0; + CHarvested = 0; + ElapsedTime = 0; + RealTime = 0; + ChangingGun = 0; +} diff --git a/CODE/SCORE.H b/CODE/SCORE.H new file mode 100644 index 0000000..f1d9e61 --- /dev/null +++ b/CODE/SCORE.H @@ -0,0 +1,148 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SCORE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCORE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCORE_H +#define SCORE_H + +#include "unit.h" +#include "building.h" + +class ScoreClass { + public: + ScoreClass(void) {}; + ScoreClass(NoInitClass const &) {}; + + int Score; + int NKilled; + int GKilled; + int CKilled; + int NBKilled; + int GBKilled; + int CBKilled; + int NHarvested; + int GHarvested; + int CHarvested; + unsigned long ElapsedTime; + TTimerClass RealTime; + + void Init(void); + void Presentation(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + unsigned char *ChangingGun; + + void ScoreDelay(int ticks); + void Pulse_Bar_Graph(void); + void Print_Graph_Title(int,int); + void Print_Minutes(int minutes); + void Count_Up_Print(char *str, int percent, int max, int xpos, int ypos); + void Show_Credits(int house, char const pal[]); + void Do_GDI_Graph(void const * yellowptr, void const * redptr, int gdikilled, int nodkilled, int ypos); + void Do_Nod_Casualties_Graph(void); + void Do_Nod_Buildings_Graph(void); + void Input_Name(char str[], int xpos, int ypos, char const pal[]); +}; + +class ScoreAnimClass { + public: + ScoreAnimClass(int x, int y, void const * data); + int XPos; + int YPos; + CDTimerClass Timer; + void const * DataPtr; + virtual void Update(void) {} ; + virtual ~ScoreAnimClass(void) {DataPtr=0;} ; +}; + +class ScoreCredsClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + void const * CashTurn; + void const * Clock1; + + virtual void Update(void); + ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreCredsClass(void) {CashTurn=0;Clock1=0;}; +}; + +class ScoreTimeClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + virtual void Update(void); + ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreTimeClass(void) {}; +}; + +class ScorePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + ScorePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~ScorePrintClass(void) {PrimaryPalette=0;}; +}; + +class ScoreScaleClass : public ScoreAnimClass { + public: + int Stage; + char const * Palette; + virtual void Update(void); + ScoreScaleClass(void const * data, int xpos, int ypos, char const pal[]); + virtual ~ScoreScaleClass(void) {Palette=0;}; + +}; + +#define MAXSCOREOBJS 8 +extern ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + +void Multi_Score_Presentation(void); + +#endif diff --git a/CODE/SCREEN.H b/CODE/SCREEN.H new file mode 100644 index 0000000..e3dbf3c --- /dev/null +++ b/CODE/SCREEN.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SCREEN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 2, 1994 * + * * + * Last Update : June 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCREEN_H +#define SCREEN_H + + +class ScreenClass +{ + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseShapeType CurrentMouseShape; + MouseShapeType NormalMouseShape; + + public: + + ScreenClass(void) { + CurrentMouseShape = SHP_NONE; + NormalMouseShape = SHP_MOUSE; + }; + + + Init(void); + Set_Default_Mouse(MouseShapeType mouse); + Force_Mouse_Shape(MouseShapeType mouse); + + unsigned char *GamePalette; + unsigned char *BlackPalette; +}; + +#endif diff --git a/CODE/SCROLL.CPP b/CODE/SCROLL.CPP new file mode 100644 index 0000000..b3c1547 --- /dev/null +++ b/CODE/SCROLL.CPP @@ -0,0 +1,269 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SCROLL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCROLL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/08/95 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ScrollClass::AI -- Handles scroll AI processing. * + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define SCROLL_DELAY 1 +#else +#define SCROLL_DELAY 2 +#endif + +CDTimerClass ScrollClass::Counter; + + +/*********************************************************************************************** + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * * + * This is the constructor for the scroll class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +ScrollClass::ScrollClass(void) : + IsAutoScroll(true) +{ + Counter = SCROLL_DELAY; + Inertia = 0; +} + + +/*********************************************************************************************** + * ScrollClass::AI -- Handles scroll AI processing. * + * * + * This routine is called every game frame for purposes of input processing. * + * * + * INPUT: input -- Reference to the keyboard/mouse event that just occurred. * + * * + * x,y -- The mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + * 08/10/1995 JLB : Revamped for free smooth scrolling. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +void ScrollClass::AI(KeyNumType &input, int x, int y) +{ + bool player_scrolled=false; + static DirType direction; + int rate; + + + /* + ** If rubber band mode is in progress, then don't allow scrolling of the tactical map. + */ + if (!IsRubberBand /*&& !IsTentative*/) { + + /* + ** Special check to not scroll within the special no-scroll regions. + */ + bool noscroll = false; + + if (!noscroll) { + bool at_screen_edge = (y == 0 || x == 0 || x >= SeenBuff.Get_Width()-1 || y >= SeenBuff.Get_Height()-1); + + /* + ** Verify that the mouse is over a scroll region. + */ + if (Inertia || at_screen_edge) { + if (at_screen_edge) { + + player_scrolled=true; + + /* + ** Adjust the mouse coordinates to emphasize the + ** cardinal directions over the diagonals. + */ + int altx = x; + if (altx < 50 * RESFACTOR) altx -= ((50 * RESFACTOR)-altx); + altx = max(altx, 0); + if (altx > ((320-50) * RESFACTOR)) altx += altx-((320-50) * RESFACTOR); + altx = min(altx, (320 * RESFACTOR)); + if (altx > (50 * RESFACTOR) && altx < ((320-50) * RESFACTOR)) { + altx += (((320/2) * RESFACTOR)-altx)/2; + } + + int alty = y; + if (alty < (50 * RESFACTOR)) alty -= (50 * RESFACTOR)-alty; + alty = max(alty, 0); + if (alty > (150 * RESFACTOR)) alty += alty-(150 * RESFACTOR); + alty = min(alty, 200 * RESFACTOR); + + direction = (DirType)Desired_Facing256((320/2) * RESFACTOR, (200/2) * RESFACTOR, altx, alty); + } + + int control = Dir_Facing(direction); + + /* + ** The mouse is over a scroll region so set the mouse shape accordingly if the map + ** can be scrolled in the direction indicated. + */ + static int _rate[9] = { + 0x00E0*RESFACTOR, + 0x00C0*RESFACTOR, + 0x00A0*RESFACTOR, + 0x0080*RESFACTOR, + 0x0060*RESFACTOR, + 0x0040*RESFACTOR, + 0x0020*RESFACTOR, + 0x0010*RESFACTOR, + 0x0008*RESFACTOR + }; + if (Debug_Map) { + rate = Options.ScrollRate+1; + } else { + rate = 8-Inertia; + } + + if (rate < Options.ScrollRate+1) { + rate = Options.ScrollRate+1; + Inertia = 8-rate; + } + + /* + ** Increase the scroll rate if the mouse button is held down. + */ + // if (Keyboard->Down(KN_LMOUSE)) { + // rate = Bound(rate-3, 0, 4); + // } + if (Keyboard->Down(KN_RMOUSE)) { + rate = Bound(rate+1, 4, (int)(sizeof(_rate)/sizeof(_rate[0]))-1); + } + + /* + ** If options indicate that scrolling should be forced to + ** one of the 8 facings, then adjust the direction value + ** accordingly. + */ + direction = Facing_Dir(Dir_Facing(direction)); + + int distance = _rate[rate]/2; + + if (!Scroll_Map(direction, distance, false)) { + Override_Mouse_Shape((MouseType)(MOUSE_NO_N+control), false); + } else { + Override_Mouse_Shape((MouseType)(MOUSE_N+control), false); + + /* + ** If the mouse button is pressed or auto scrolling is active, then scroll + ** the map if the delay counter indicates. + */ + if (Keyboard->Down(KN_LMOUSE) || IsAutoScroll) { + distance = _rate[rate]; + + if (Debug_Map) { + Scroll_Map(direction, distance, true); + Counter = SCROLL_DELAY; + } else { + distance = _rate[rate]; + Scroll_Map(direction, distance, true); + + if (Counter == 0 && player_scrolled) { + Counter = SCROLL_DELAY; + Inertia++; + } + } + } + } + + } + + if (!Debug_Map && !player_scrolled) { + if (!Counter) { + Inertia--; + if (Inertia<0) Inertia++; + Counter = SCROLL_DELAY; + } + } + + } + } + + HelpClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * * + * This routine controls the autoscrolling setting. Autoscroll, when active, will cause the * + * map to scroll if the mouse is held over the scroll region. This is regardless of whether * + * any mouse button is held down or not. * + * * + * INPUT: control -- Should the autoscroll be turned on? * + * 0 = turn off * + * 1 = turn on * + * -1 = toggle current setting * + * * + * OUTPUT: Returns with the old setting of the autoscroll flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +bool ScrollClass::Set_Autoscroll(int control) +{ + bool old = IsAutoScroll; + + switch (control) { + case -1: + IsAutoScroll = !IsAutoScroll; + break; + + default: + IsAutoScroll = control; + break; + } + return(old); +} + + diff --git a/CODE/SCROLL.H b/CODE/SCROLL.H new file mode 100644 index 0000000..1b05677 --- /dev/null +++ b/CODE/SCROLL.H @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SCROLL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SCROLL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCROLL_H +#define SCROLL_H + +#include "help.h" + + +class ScrollClass: public HelpClass +{ + /* + ** If map scrolling is automatic, then this flag is true. Automatic scrolling will + ** cause the map to scroll if the mouse is in the scroll region, regardless of + ** whether or not the mouse button is held down. + */ + unsigned IsAutoScroll:1; + + /* + ** Scroll speed is regulated by this count down timer. When this value reaches zero, + ** scroll the map in the direction required and reset this timer. + */ + static CDTimerClass Counter; + + /* + ** Inertia control for scrolling + */ + int Inertia; + + public: + ScrollClass(void); + ScrollClass(NoInitClass const & x) : HelpClass(x) {}; + + bool Set_Autoscroll(int control); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Init_IO(void) {Counter = 0;HelpClass::Init_IO();}; +}; + +#endif diff --git a/CODE/SDATA.CPP b/CODE/SDATA.CPP new file mode 100644 index 0000000..575dfdf --- /dev/null +++ b/CODE/SDATA.CPP @@ -0,0 +1,571 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SDATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static SmudgeTypeClass const Crater1 ( + SMUDGE_CRATER1, + "CR1", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater2 ( + SMUDGE_CRATER2, + "CR2", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater3 ( + SMUDGE_CRATER3, + "CR3", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater4 ( + SMUDGE_CRATER4, + "CR4", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater5 ( + SMUDGE_CRATER5, + "CR5", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater6 ( + SMUDGE_CRATER6, + "CR6", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch1 ( + SMUDGE_SCORCH1, + "SC1", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch2 ( + SMUDGE_SCORCH2, + "SC2", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch3 ( + SMUDGE_SCORCH3, + "SC3", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch4 ( + SMUDGE_SCORCH4, + "SC4", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch5 ( + SMUDGE_SCORCH5, + "SC5", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch6 ( + SMUDGE_SCORCH6, + "SC6", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); + +static SmudgeTypeClass const Bibx1 ( + SMUDGE_BIB1, + "BIB1", + TXT_BIB, + 4,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx2 ( + SMUDGE_BIB2, + "BIB2", + TXT_BIB, + 3,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx3 ( + SMUDGE_BIB3, + "BIB3", + TXT_BIB, + 2,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); + + +/*********************************************************************************************** + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * * + * This constructor is used to create the smudge type objects. These type objects contain * + * static information about the various smudge types supported in the game. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass::SmudgeTypeClass( + SmudgeType smudge, + char const * ininame, + int fullname, + int width, + int height, + bool isbib, + bool iscrater) : + ObjectTypeClass( + RTTI_SMUDGETYPE, + int(smudge), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(smudge), + Width(width), + Height(height), + IsCrater(iscrater), + IsBib(isbib) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * * + * This will allocate a smudge type class object from the special memory pool for that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the newly allocated smudge type class object. If there is insufficient* + * memory in the pool to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * SmudgeTypeClass::operator new(size_t) +{ + return(SmudgeTypes.Alloc()); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * * + * This will return the smudge type class object back to the memory pool from whence it * + * was originally allocated. * + * * + * INPUT: pointer -- Pointer to the smudge type class object to return the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::operator delete(void * pointer) +{ + SmudgeTypes.Free((SmudgeTypeClass *)pointer); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * * + * This will initialize the special heap for smudge type class objects, by pre-allocated * + * all known smudge types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init_Heap(void) +{ + /* + ** These smudge type class objects must be allocated in the exact order that they + ** are specified in the SmudgeType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new SmudgeTypeClass(Crater1); // SMUDGE_CRATER1 + new SmudgeTypeClass(Crater2); // SMUDGE_CRATER2 + new SmudgeTypeClass(Crater3); // SMUDGE_CRATER3 + new SmudgeTypeClass(Crater4); // SMUDGE_CRATER4 + new SmudgeTypeClass(Crater5); // SMUDGE_CRATER5 + new SmudgeTypeClass(Crater6); // SMUDGE_CRATER6 + new SmudgeTypeClass(Scorch1); // SMUDGE_SCORCH1 + new SmudgeTypeClass(Scorch2); // SMUDGE_SCORCH2 + new SmudgeTypeClass(Scorch3); // SMUDGE_SCORCH3 + new SmudgeTypeClass(Scorch4); // SMUDGE_SCORCH4 + new SmudgeTypeClass(Scorch5); // SMUDGE_SCORCH5 + new SmudgeTypeClass(Scorch6); // SMUDGE_SCORCH6 + new SmudgeTypeClass(Bibx1); // SMUDGE_BIB1 + new SmudgeTypeClass(Bibx2); // SMUDGE_BIB2 + new SmudgeTypeClass(Bibx3); // SMUDGE_BIB3 +} + + +/*********************************************************************************************** + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * * + * This converts an ASCII name into a smudge type number. This is typically necessary * + * when processing scenario INI files and not used otherwise. * + * * + * INPUT: name -- Pointer to the name to convert. * + * * + * OUTPUT: Returns with the SmudgeType number that matches the name supplied. If no match * + * was found, then SMUDGE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeType SmudgeTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(SMUDGE_NONE); +} + + +/*********************************************************************************************** + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * * + * Smudges are always only one icon in dimension, so this routine always returns a cell * + * occupation offset list of the center cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns occupation list specifying all the cells that the overlay occupies. This * + * is just the center cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +short const * SmudgeTypeClass::Occupy_List(bool) const +{ + static short _occupy[4*4]; + short * ptr = &_occupy[0]; + + for (int x = 0; x < Width; x++) { + for (int y = 0; y < Height; y++) { + *ptr++ = x + (y*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + return(_occupy); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * * + * Smudge object imagery varies between theaters. This routine will load the appropriate * + * imagery for the theater specified. * + * * + * INPUT: theater -- The theater to prepare for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + SmudgeTypeClass const & smudge = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed smudge data set name. + + _makepath(fullname, NULL, NULL, smudge.IniName, Theaters[theater].Suffix); + ((void const *&)smudge.ImageData) = MFCD::Retrieve(fullname); + } + } +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * * + * The scenario object editor will call this routine to display a typical imagery of this * + * smudge object for graphical identification purposes. * + * * + * INPUT: x,y -- Coordinate to render the smudge at. * + * * + * window-- The window to base the coordinate rendering upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const * ptr = Get_Image_Data(); + + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IsTheaterShape = true; // Smudges are theater specific + if (ptr != NULL) { + for (int w = 0; w < Width; w++) { + for (int h = 0; h < Height; h++) { + CC_Draw_Shape(ptr, w + (h*Width), x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, WINDOW_TACTICAL, SHAPE_WIN_REL); + } + } + } + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * * + * This routine adds smudge objects to the list of objects that the scenario editor can * + * place upon the ground. It is only called from the scenario editor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Prep_For_Add(void) +{ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * * + * This routine will, in one motion, create a smudge object and place it upon the map. * + * Since placing a smudge on the map will destroy the object, this routine will leave the * + * smudge object count unchanged. Typically, this routine is used by the scenario editor * + * for creating smudges and placing them on the map. * + * * + * INPUT: cell -- The cell to place the smudge object. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new SmudgeClass(Type, Cell_Coord(cell))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * * + * This routine will create a smudge object of the appropriate type. Smudge objects are * + * transitory in nature. They exist only from the point of creation until they are given * + * a spot on the map to reside. At that time the map data is updated and the smudge * + * object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a created smudge object. If none could be created, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * SmudgeTypeClass::Create_One_Of(HouseClass *) const +{ + return(new SmudgeClass(Type, -1)); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * * + * This routine will draw the smudge overlay image at the coordinate (upper left) * + * specified. The underlying terrain icon is presumed to have already been rendered. * + * * + * INPUT: x,y -- Coordinate of the upper left corner of icon to render the smudge object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Draw_It(int x, int y, int data) const +{ + void const * ptr = Get_Image_Data(); + if (ptr != NULL) { + IsTheaterShape = true; // Smudges are theater specific + CC_Draw_Shape(ptr, data, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * * + * Use this routine to get a reference to the smudge type class object when given just * + * the smudge type identifier. * + * * + * INPUT: type -- The smudge type identifier to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the smudge type class object. * + * * + * WARNINGS: Be sure that the smudge type specified is legal. An illegal type value will * + * produce undefined results. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass & SmudgeTypeClass::As_Reference(SmudgeType type) +{ + return(*SmudgeTypes.Ptr(type)); +} diff --git a/CODE/SEARCH.H b/CODE/SEARCH.H new file mode 100644 index 0000000..b81913f --- /dev/null +++ b/CODE/SEARCH.H @@ -0,0 +1,689 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SEARCH.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SEARCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/02/96 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * IndexClass::Add_Index -- Add element to index tracking system. * + * IndexClass::Clear -- Clear index handler to empty state. * + * IndexClass::Count -- Fetch the number of index entries recorded. * + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * IndexClass::IndexClass -- Constructor for index handler. * + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * IndexClass::Is_Present -- Checks for presense of index entry. * + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * IndexClass::Sort_Nodes -- Sorts nodes in preparation for a binary search. * + * IndexClass::~IndexClass -- Destructor for index handler object. * + * compfunc -- Support function for bsearch and bsort. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SEARCH_H +#define SEARCH_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#ifndef __BORLANDC__ +#define _USERENTRY +#endif + +/* +** This class is used to create and maintain an index. It does this by assigning a unique +** identifier number to the type objects that it is indexing. The regular binary sort and search +** function are used for speedy index retreival. Typical use of this would be to index pointers to +** normal data objects, but it can be used to hold the data objects themselves. Keep in mind that +** the data object "T" is copied around by this routine in the internal tables so not only must +** it have a valid copy constructor, it must also be efficient. The internal algorithm will +** create an arbitrary number of default constructed objects of type "T". Make sure it has a +** default constructor that is effecient. The constructor need not perform any actual +** initialization since this class has prior knowledge about the legality of these temporary +** objects and doesn't use them until after the copy constructor is used to initialize them. +*/ + +template +class IndexClass +{ + public: + IndexClass(void); + ~IndexClass(void); + + /* + ** Add element to index table. + */ + bool Add_Index(int id, T data); + + /* + ** Removes an index entry from the index table. + */ + bool Remove_Index(int id); + + /* + ** Check to see if index is present. + */ + bool Is_Present(int id) const; + + /* + ** Fetch number of indexes in the table. + */ + int Count(void) const; + + /* + ** Actually a fetch an index data element from the table. + */ + T Fetch_Index(int id) const; + + /* + ** Clear out the index table to null (empty) state. + */ + void Clear(void); + + private: + /* + ** This node object is used to keep track of the connection between the data + ** object and the index identifier number. + */ + struct NodeElement { + int ID; // ID number (must be first element in this structure). + T Data; // Data element assigned to this ID number. + }; + + /* + ** This is the pointer to the allocated index table. It contains all valid nodes in + ** a sorted order. + */ + NodeElement * IndexTable; + + /* + ** This records the number of valid nodes within the index table. + */ + int IndexCount; + + /* + ** The total size (in nodes) of the index table is recorded here. If adding a node + ** would cause the index count to exceed this value, the index table must be resized + ** to make room. + */ + int IndexSize; + + /* + ** If the index table is sorted and ready for searching, this flag will be true. Sorting + ** of the table only occurs when absolutely necessary. + */ + bool IsSorted; + + /* + ** This records a pointer to the last element found by the Is_Present() function. Using + ** this last recorded value can allow quick fetches of data whenever possible. + */ + NodeElement const * Archive; + + //------------------------------------------------------------------------------------- + IndexClass(IndexClass const & rvalue); + IndexClass * operator = (IndexClass const & rvalue); + + /* + ** Increase size of internal index table by amount specified. + */ + bool Increase_Table_Size(int amount); + + /* + ** Check if archive pointer is the same as that requested. + */ + bool Is_Archive_Same(int id) const; + + /* + ** Invalidate the archive pointer. + */ + void Invalidate_Archive(void); + + /* + ** Set archive to specified value. + */ + void Set_Archive(NodeElement const * node); + + /* + ** Search for the node in the index table. + */ + NodeElement const * Search_For_Node(int id) const; + + static int _USERENTRY search_compfunc(void const * ptr, void const * ptr2); +}; + + +/*********************************************************************************************** + * IndexClass::IndexClass -- Constructor for index handler. * + * * + * This constructs an empty index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::IndexClass(void) : + IndexTable(0), + IndexCount(0), + IndexSize(0), + IsSorted(false), + Archive(0) +{ + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::~IndexClass -- Destructor for index handler object. * + * * + * This will destruct and free any resources managed by this index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::~IndexClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * IndexClass::Clear -- Clear index handler to empty state. * + * * + * This routine will free all internal resources and bring the index handler into a * + * known empty state. After this routine, the index handler is free to be reused. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Clear(void) +{ + delete [] IndexTable; + IndexTable = 0; + IndexCount = 0; + IndexSize = 0; + IsSorted = false; + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * * + * This helper function will increase the capacity of the internal index table without * + * performing any alterations to the index data. Use this routine prior to adding a new * + * element if the existing capacity is insufficient. * + * * + * INPUT: amount -- The number of index element spaces to add to its capacity. * + * * + * OUTPUT: bool; Was the internal capacity increased without error? * + * * + * WARNINGS: If there is insufficient RAM, then this routine will fail. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Increase_Table_Size(int amount) +{ + /* + ** Check size increase parameter for legality. + */ + if (amount < 0) return(false); + + NodeElement * table = new NodeElement[IndexSize + amount]; + if (table != NULL) { + + /* + ** Copy all valid nodes into the new table. + */ + for (int index = 0; index < IndexCount; index++) { + table[index] = IndexTable[index]; + } + + /* + ** Make the new table the current one (and delete the old one). + */ + delete [] IndexTable; + IndexTable = table; + IndexSize += amount; + Invalidate_Archive(); + + /* + ** Return with success flag. + */ + return(true); + } + + /* + ** Failure to allocate the memory results in a failure to increase + ** the size of the index table. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Count -- Fetch the number of index entries recorded. * + * * + * This will return the quantity of index entries that have been recored by this index * + * handler. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with number of recored indecies present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +int IndexClass::Count(void) const +{ + return(IndexCount); +} + + +/*********************************************************************************************** + * IndexClass::Is_Present -- Checks for presense of index entry. * + * * + * This routine will scan for the specified index entry. If it was found, then 'true' is * + * returned. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: bool; Was the index entry found in this index handler object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Present(int id) const +{ + /* + ** If there are no data elements in the index table, then it can + ** never find the specified index. Check for and return failure + ** in this case. + */ + if (IndexCount == 0) { + return(false); + } + + /* + ** Check to see if this same index element was previously searched for. If + ** so and it was previously found, then there is no need to search for it + ** again -- just return true. + */ + if (Is_Archive_Same(id)) { + return(true); + } + + /* + ** Perform a binary search on the index nodes in order to look for a + ** matching index value. + */ + NodeElement const * nodeptr = Search_For_Node(id); + + /* + ** If a matching index was found, then record it for future reference and return success. + */ + if (nodeptr != 0) { + ((IndexClass *)this)->Set_Archive(nodeptr); + return(true); + } + + /* + ** Could not find element so return failure condition. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * * + * This routine will find the specified index and return the data value associated with it. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with the data value associated with the index value. * + * * + * WARNINGS: This routine presumes that the index exists. If it doesn't exist, then the * + * default constructed object "T" is returned instead. To avoid this problem, * + * always verfiy the existance of the index by calling Is_Present() first. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +T IndexClass::Fetch_Index(int id) const +{ + if (Is_Present(id)) { + + /* + ** Count on the fact that the archive pointer is always valid after a call to Is_Present + ** that returns "true". + */ + return(Archive->Data); + } + return(T()); +} + + +/*********************************************************************************************** + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * * + * This routine compares the specified index ID with the previously recorded index archive * + * pointer in order to determine if they match. * + * * + * INPUT: id -- The index ID to compare to the archive index object pointer. * + * * + * OUTPUT: bool; Does the specified index match the archive pointer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Archive_Same(int id) const +{ + if (Archive != 0 && Archive->ID == id) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * * + * This routine will set the archive pointer to an invalid state. This must be performed * + * if ever the archive pointer would become illegal (such as when the element it refers to * + * is deleted). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Invalidate_Archive(void) +{ + Archive = 0; +} + + +/*********************************************************************************************** + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * * + * This routine records the specified node pointer. Use this routine when there is a * + * good chance that the specified node will be requested in the immediate future. * + * * + * INPUT: node -- Pointer to the node to assign to the archive. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Set_Archive(NodeElement const * node) +{ + Archive = node; +} + + +/*********************************************************************************************** + * IndexClass::Add_Index -- Add element to index tracking system. * + * * + * This routine will record the index information into this index manager object. It * + * associates the index number with the data specified. The data is copied to an internal * + * storage location. * + * * + * INPUT: id -- The ID number to associate with the data. * + * * + * data -- The data to store. * + * * + * OUTPUT: bool; Was the element added without error? Failure indicates that RAM has been * + * exhausted. * + * * + * WARNINGS: The data is COPIED to internal storage. This means that the data object must * + * have a functional and efficient copy constructor and assignment operator. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Add_Index(int id, T data) +{ + /* + ** Ensure that there is enough room to add this index. If not, then increase the + ** capacity of the internal index table. + */ + if (IndexCount + 1 > IndexSize) { + if (!Increase_Table_Size(IndexSize == 0 ? 10 : IndexSize)) { + + /* + ** Failure to increase the size of the index table means failure to add + ** the index element. + */ + return(false); + } + } + + /* + ** Add the data to the end of the index data and then sort the index table. + */ + IndexTable[IndexCount].ID = id; + IndexTable[IndexCount].Data = data; + IndexCount++; + IsSorted = false; + + return(true); +} + + +/*********************************************************************************************** + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * * + * This will scan through the previously added index elements and if a match was found, it * + * will be removed. * + * * + * INPUT: id -- The index ID to search for and remove. * + * * + * OUTPUT: bool; Was the index element found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Remove_Index(int id) +{ + /* + ** Find the array index into the table that matches the specified id value. + */ + int found_index = -1; + for (int index = 0; index < IndexCount; index++) { + if (IndexTable[index].ID == id) { + found_index = index; + break; + } + } + + /* + ** If the array index was found, then copy all higher index entries + ** downward to fill the vacated location. We cannot use memcpy here because the type + ** object may not support raw copies. C++ defines the assignment operator to deal + ** with this, so that is what we use. + */ + if (found_index != -1) { + + for (int index = found_index+1; index < IndexCount; index++) { + IndexTable[index-1] = IndexTable[index]; + } + IndexCount--; + + NodeElement fake; + fake.ID = 0; + fake.Data = T(); + IndexTable[IndexCount] = fake; // zap last (now unused) element + + Invalidate_Archive(); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * compfunc -- Support function for bsearch and bsort. * + * * + * This compare function presumes that its parameters are pointing to NodeElements and that * + * the first "int" in the node is the index ID number to be used for comparison. * + * * + * INPUT: ptr1 -- Pointer to first node. * + * * + * ptr2 -- Pointer to second node. * + * * + * OUTPUT: Returns with the comparision value between the two nodes. * + * * + * WARNINGS: This is highly dependant upon the layout of the NodeElement structure. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +static int _USERENTRY IndexClass::search_compfunc(void const * ptr1, void const * ptr2) +{ + if (*(int const *)ptr1 == *(int const *)ptr2) { + return(0); + } + if (*(int const *)ptr1 < *(int const *)ptr2) { + return(-1); + } + return(1); +} + + +/*********************************************************************************************** + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * * + * This routine will perform a binary search on the previously recorded index values and * + * if a match was found, it will return a pointer to the NodeElement. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with a pointer to the NodeElement that matches the index ID specified. If * + * no matching index could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::NodeElement const * IndexClass::Search_For_Node(int id) const +{ + /* + ** If there are no elements in the list, then it certainly can't find any matches. + */ + if (IndexCount == 0) { + return(0); + } + + /* + ** If the list has not yet been sorted, then do so now. Binary searching requires + ** the list to be sorted. + */ + if (!IsSorted) { + qsort(&IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc); + ((IndexClass *)this)->Invalidate_Archive(); + ((IndexClass *)this)->IsSorted = true; + } + + /* + ** This list is sorted and ready to perform a binary search upon it. + */ + NodeElement node; + node.ID = id; + return((NodeElement const *)bsearch(&node, &IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc)); +} + + +#endif + + diff --git a/CODE/SEDITDLG.CPP b/CODE/SEDITDLG.CPP new file mode 100644 index 0000000..da5144f --- /dev/null +++ b/CODE/SEDITDLG.CPP @@ -0,0 +1,386 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.cpp - "SimpleEditDlgClass": An ok/cancel type dialog with 1 or 2 edit boxes. +// Mostly a hack for what I need right now - not necessarily very flexible. +// Still - I can't believe there isn't a set of dialog classes in here already. +// ajw 07/21/98 + +#include "SEditDlg.h" +#include "WOLEdit.h" + +extern bool cancel_current_msgbox; +bool disable_current_msgbox = false; + +//*********************************************************************************************** +SimpleEditDlgClass::SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 /* = NULL */, int iEditCharsAccept2 /* = 0 */ ) + : iDialogWidth( iDialogWidth ), iEditCharsAccept( iEditCharsAccept ), iEditCharsAccept2( iEditCharsAccept2 ) +{ + // Copy strings. + if( szTitle ) + { + this->szTitle = new char[ strlen( szTitle ) + 1 ]; + strcpy( this->szTitle, szTitle ); + } + else + this->szTitle = NULL; + + if( szPrompt ) + { + this->szPrompt = new char[ strlen( szPrompt ) + 1 ]; + strcpy( this->szPrompt, szPrompt ); + } + else + this->szPrompt = NULL; // I wouldn't try this ... not totally implemented. + + if( szPrompt2 ) + { + this->szPrompt2 = new char[ strlen( szPrompt2 ) + 1 ]; + strcpy( this->szPrompt2, szPrompt2 ); + } + else + this->szPrompt2 = NULL; // This is the flag for whether or not there is a second edit box. + + *szEdit = 0; + *szEdit2 = 0; + + szOkButton = Text_String( TXT_OK ); + szCancelButton = Text_String( TXT_CANCEL ); + szMiddleButton = NULL; +} + +//*********************************************************************************************** +SimpleEditDlgClass::~SimpleEditDlgClass() +{ + delete [] szTitle; + delete [] szPrompt; + delete [] szPrompt2; +} + +//*********************************************************************************************** +void SimpleEditDlgClass::SetButtons( const char* szOk, const char* szCancel, const char* szMiddle /*= NULL*/ ) +{ + szOkButton = szOk; + szCancelButton = szCancel; + szMiddleButton = szMiddle; +} + +//*********************************************************************************************** +const char* SimpleEditDlgClass::Show() +{ + // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int x_margin = 18 * RESFACTOR; // margin width/height + int y_margin = 10 * RESFACTOR; // margin width/height + int d_gap_y = 5 * RESFACTOR; + + int d_dialog_w = iDialogWidth; + int d_dialog_h = szPrompt2 ? ( 29 * RESFACTOR ) + 2 * d_gap_y + 2 * y_margin : ( 19 * RESFACTOR ) + d_gap_y + 2 * y_margin; + if( szTitle ) + d_dialog_h += 10 * RESFACTOR + 2 * d_gap_y; + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + +/* + if( szTitle ) + { + d_title_w = String_Pixel_Width( szTitle ); + d_title_h = 10 * RESFACTOR; + d_title_x = d_dialog_cx - d_title_w / 2; + d_title_y = d_dialog_y + d_gap_y; + } +*/ + + int d_prompt_w = String_Pixel_Width( szPrompt ); + int d_prompt_h = 10 * RESFACTOR; + int d_prompt_x = d_dialog_x + x_margin; + int d_prompt_y = szTitle ? ( d_dialog_y + 3 * d_gap_y + 10 * RESFACTOR ) : ( d_dialog_y + d_gap_y ); + + int d_edit_w = d_dialog_w - d_prompt_w - 2 * x_margin; + int d_edit_h = 10 * RESFACTOR; + int d_edit_x = d_dialog_x + d_prompt_w + x_margin; + int d_edit_y = d_prompt_y; + + int d_prompt2_w = szPrompt2 ? String_Pixel_Width( szPrompt2 ) : 0; + int d_prompt2_h = 10 * RESFACTOR; + int d_prompt2_x = d_dialog_x + x_margin; + int d_prompt2_y = d_prompt_y + d_prompt2_h + d_gap_y; + + int d_edit2_w = d_dialog_w - d_prompt2_w - 2 * x_margin; + int d_edit2_h = 10 * RESFACTOR; + int d_edit2_x = d_dialog_x + d_prompt2_w + x_margin; + int d_edit2_y = d_prompt2_y; + + int d_ok_w, d_ok_h, d_ok_x, d_ok_y, d_cancel_w, d_cancel_h, d_cancel_x, d_cancel_y, d_mid_x, d_mid_y, d_mid_w, d_mid_h; + + if( !szMiddleButton ) + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 10 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 10 * RESFACTOR; + d_cancel_y = d_ok_y; + } + else + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 30 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_mid_w = 40 * RESFACTOR; + d_mid_h = 9 * RESFACTOR; + d_mid_x = d_dialog_cx - ( d_mid_w / 2 ); + d_mid_y = d_ok_y; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 30 * RESFACTOR; + d_cancel_y = d_ok_y; + } + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_MIDDLE, + BUTTON_EDIT, + BUTTON_EDIT2 + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + const char* szReturn = NULL; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, szOkButton, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, szCancelButton, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + TextButtonClass MiddleBtn( BUTTON_MIDDLE, szMiddleButton, TPF_BUTTON, d_mid_x, d_mid_y, d_mid_w ); + + WOLEditClass EditBox( BUTTON_EDIT, szEdit, min( sizeof(szEdit), iEditCharsAccept ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC ); + WOLEditClass EditBox2( BUTTON_EDIT2, szEdit2, min( sizeof(szEdit2), iEditCharsAccept2 ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit2_x, d_edit2_y, d_edit2_w, -1, EditClass::ALPHANUMERIC ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + if( szMiddleButton ) + MiddleBtn.Add_Tail(*commands); + EditBox.Add_Tail(*commands); + if( szPrompt2 ) + EditBox2.Add_Tail(*commands); + EditBox.Set_Focus(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + if( szTitle ) + Draw_Caption( szTitle, d_dialog_x, d_dialog_y, d_dialog_w ); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( szPrompt, d_prompt_x, d_prompt_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + if( szPrompt2 ) + Fancy_Text_Print( szPrompt2, d_prompt2_x, d_prompt2_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + EditBox.Set_Focus(); + EditBox.Flag_To_Redraw(); + } + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + } + + if( disable_current_msgbox ) + { + disable_current_msgbox = false; + EditBox.Disable(); + // These do not actually draw. I am actually clearing the "draw" flag! + // Problem is Disable sets them to redraw, and I don't want to, and there is no Flag_To_Redraw( false ). + EditBox.GadgetClass::Draw_Me( true ); + if( szPrompt2 ) + { + EditBox2.Disable(); + EditBox2.GadgetClass::Draw_Me( true ); + } + OkBtn.Disable(); + OkBtn.GadgetClass::Draw_Me( true ); + CancelBtn.Disable(); + CancelBtn.GadgetClass::Draw_Me( true ); + if( szMiddleButton ) + { + MiddleBtn.Disable(); + MiddleBtn.GadgetClass::Draw_Me( true ); + } + } + + /* + ** Process input. + */ + switch( input ) + { +// case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + szReturn = szCancelButton; + process = false; + break; + +// case KN_RETURN: + case ( BUTTON_EDIT | KN_BUTTON ): // (Return pressed while on edit.) + case ( BUTTON_OK | KN_BUTTON ): + szReturn = szOkButton; + process = false; + break; + + case ( BUTTON_MIDDLE | KN_BUTTON ): + szReturn = szMiddleButton; + process = false; + break; + + default: + break; + } + } + + return szReturn; +} + +#endif diff --git a/CODE/SEDITDLG.H b/CODE/SEDITDLG.H new file mode 100644 index 0000000..70a639f --- /dev/null +++ b/CODE/SEDITDLG.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.h - "SimpleEditDlgClass": An ok/cancel type dialog with a single edit box. +// ajw 07/21/98 + +#include "function.h" + +class SimpleEditDlgClass +{ +public: + SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 = NULL, int iEditCharsAccept2 = 0 ); + virtual ~SimpleEditDlgClass(); + + const char* Show(); // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + void SetButtons( const char* szOk, const char* szCancel, const char* szMiddle = NULL ); + + char szEdit[ 300 ]; // iEditCharsAccept upper limit. + char szEdit2[ 300 ]; + +protected: + int iDialogWidth; // X pixels width of entire dialog. + char* szTitle; // Title of dialog, or NULL for no title. + + char* szPrompt; // Text appearing to the left of edit box. + int iEditCharsAccept; // Max length of string allowed in edit, includes null-terminator. + + char* szPrompt2; + int iEditCharsAccept2; + + const char* szOkButton; // Text of button that acts like an Ok button. Appears on left. + const char* szCancelButton; // Text of button that acts like an Cancel button. Appears on right. + const char* szMiddleButton; // Optional middle button text. Null = no middle button. +}; + +#endif diff --git a/CODE/SENDFILE.CPP b/CODE/SENDFILE.CPP new file mode 100644 index 0000000..02f42e2 --- /dev/null +++ b/CODE/SENDFILE.CPP @@ -0,0 +1,879 @@ +/* +** 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 . +*/ + + +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : SENDFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : Audust 20th, 1996 * + * * + * Last Update : August 20th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for scenario file transfer between machines * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +//#include "WolDebug.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else + +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +#define RESPONSE_TIMEOUT 60*60 + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +/*********************************************************************************************** + * Get_Scenario_File_From_Host -- Initiates download of scenario file from game host * + * * + * * + * * + * INPUT: ptr to buffer to copy file name into * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file sucessfully downloaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:06PM ST : Created * + *=============================================================================================*/ +bool Get_Scenario_File_From_Host(char *return_name, int gametype) +{ + //WWDebugString ("RA95 - In Get_Scenario_From_Host\n"); + + unsigned int file_length; + + SerialPacketType send_packet; + SerialPacketType receive_packet; + GlobalPacketType net_send_packet; + GlobalPacketType net_receive_packet; + unsigned int packet_len; + unsigned short product_id; + + IPXAddressClass sender_address; + + CDTimerClass response_timer; // timeout timer for waiting for responses + + /* + ** Send the scenario request using guaranteed delivery. + */ + if (!gametype) { + memset ((void*)&send_packet, 0, sizeof (send_packet)); + send_packet.Command = SERIAL_REQ_SCENARIO; + NullModem.Send_Message (&send_packet, sizeof(send_packet), 1); + } else { + memset ((void*)&net_send_packet, 0, sizeof (net_send_packet)); + net_send_packet.Command = NET_REQ_SCENARIO; + Ipx.Send_Global_Message (&net_send_packet, sizeof (net_send_packet), + 1, &(Session.HostAddress) ); + } + + + + //WWDebugString ("RA95 - Waiting for response from host\n"); + + /* + ** Wait for host to respond with a file info packet + */ + response_timer = RESPONSE_TIMEOUT; + if (!gametype){ + do { + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == SERIAL_FILE_INFO){ + strcpy (return_name, receive_packet.ScenarioInfo.ShortFileName); + file_length = receive_packet.ScenarioInfo.FileLength; + break; + } + } + } while ( response_timer ); + }else{ + do { + Ipx.Service(); + int receive_packet_length = sizeof (net_receive_packet); + if (Ipx.Get_Global_Message (&net_receive_packet, &receive_packet_length, + &sender_address, &product_id)){ + +//WWDebugString ("RA95 - Got packet from host\n"); +#ifdef WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && sender_address == Session.HostAddress) { +#else //WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + strcpy (return_name, net_receive_packet.ScenarioInfo.ShortFileName); + file_length = net_receive_packet.ScenarioInfo.FileLength; +//WWDebugString ("RA95 - Got file info packet from host\n"); + break; + } + } + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + } while ( response_timer ); + } + +//char rt[80]; +//sprintf (rt, "RA95 - response_timer = %d\n", response_timer ); +//WWDebugString (rt); + + /* + ** If we timed out then something horrible has happened to the other player so just + ** return failure. + */ + if (!response_timer) return (false); + +// debugprint( "about to download '%s'\n", return_name ); + + /* + ** Receive the file from the host + */ + return (Receive_Remote_File ( return_name, file_length, gametype)); +} + + + + + + + +/*********************************************************************************************** + * Receive_Remote_File -- Handles incoming file download packets from the game host * + * * + * * + * * + * INPUT: file name to save as * + * length of file to expect * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file downloaded was completed * + * * + * WARNINGS: This fuction can modify the file name passed in * + * * + * HISTORY: * + * 8/22/96 3:07PM ST : Created * + *=============================================================================================*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype) +{ + + //WWDebugString ("RA95 - In Receive_Remote_File\n"); + unsigned short product_id; + IPXAddressClass sender_address; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200 *RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_RECEIVING_SCENARIO); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + + RemoteFileTransferType receive_packet; + + int last_received_block = -1; //No blocks received yet + unsigned int total_length = 0; + unsigned int packet_len; + + /* + ** If the file name is already in use, use the temp file name + */ + CCFileClass test_file (file_name); + if (test_file.Is_Available()){ + strcpy (file_name, "DOWNLOAD.TMP"); + } + + RawFileClass save_file (file_name); + + /* + ** If the file already exists then delete it and re-create it. + */ + if (save_file.Is_Available()) save_file.Delete(); + + /* + ** Open the file for write + */ + save_file.Open ( WRITE ); + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + /* + ** Wait for all the blocks to arrive + */ + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == NET_FILE_CHUNK){ + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + }else{ + Ipx.Service(); + + int receive_packet_len = sizeof (receive_packet); + if (Ipx.Get_Global_Message (&receive_packet, &receive_packet_len, + &sender_address, &product_id)){ + +#ifdef WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && sender_address == Session.HostAddress){ +#else //WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + + } while ( process ); + + save_file.Close(); + + /* + ** Update the internal list of scenarios to include the downloaded one so we know about it + ** for the next game. + */ + Session.Read_Scenario_Descriptions(); + + return (return_code); +} + + + + + + +/*********************************************************************************************** + * Send_Remote_File -- Sends a file to game clients * + * * + * * + * * + * INPUT: File name * + * * + * OUTPUT: true if file transfer was successfully completed * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:09PM ST : Created * + *=============================================================================================*/ +bool Send_Remote_File ( char *file_name, int gametype ) +{ + //WWDebugString ("RA95 - In Send_Remote_File\n"); + + /* + ** Dialog & button dimensions + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 240 *factor; // dialog width + int d_dialog_h = 90*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int d_progress_w = 100*factor; + int d_progress_h = 10*factor; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*factor; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_SENDING_SCENARIO); + + CDTimerClass response_timer; // timeout timer for waiting for responses + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + int file_length; + int block_number; + int max_chunk_size; + int total_blocks; + int bytes_left; + + void *read_ptr; + + RemoteFileTransferType send_packet; + SerialPacketType file_info; + GlobalPacketType net_file_info; + + + CCFileClass send_file (file_name); + + if ( !send_file.Is_Available() ){ + //WWDebugString ("RA95 - Error - could not find file to send to client\n"); +// debugprint("RA95 - Error - could not find file to send to client\n"); + return (false); + } + file_length = send_file.Size(); + + response_timer = RESPONSE_TIMEOUT; + + /* + ** Send the file info to the remote machine(s) + */ + if (!gametype){ + file_info.Command = SERIAL_FILE_INFO; + strcpy (&file_info.ScenarioInfo.ShortFileName[0], file_name); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#endif +#endif + file_info.ScenarioInfo.FileLength = file_length; + NullModem.Send_Message (&file_info, sizeof (file_info), 1); + while (NullModem.Num_Send() > 0 && response_timer){ + NullModem.Service(); + } + } else { + net_file_info.Command = NET_FILE_INFO; + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], file_name); +// debugprint( "Uploading '%s'\n", file_name ); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + // There was a bug here: s/b net_file_info. This means that players that don't have Aftermath could have been + // accumulating Aftermath maps all this time!!! (File wasn't getting renamed to "DOWNLOAD.TMP".) + } +#endif +#endif +// debugprint( "ShortFileName is '%s'\n", net_file_info.ScenarioInfo.ShortFileName ); + net_file_info.ScenarioInfo.FileLength = file_length; + + for (int i=0 ; iAddress) ); + } + + while (Ipx.Global_Num_Send() > 0 && response_timer) { + Ipx.Service(); + } + } + + + max_chunk_size = MAX_SEND_FILE_PACKET_SIZE; + total_blocks = (file_length + max_chunk_size-1) / max_chunk_size; + bytes_left = file_length; + + send_file.Open ( READ ); + + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + + block_number = 0; + + while ( process ){ + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + + if (block_number < total_blocks){ + + if ( NullModem.Num_Send() <2 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + NullModem.Send_Message ((void*)&send_packet, sizeof(send_packet), 1); + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (NullModem.Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + + } + } + + + }else{ + Ipx.Service(); + + if (block_number < total_blocks){ + + if ( Ipx.Global_Num_Send() == 0 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + for (int i=0 ; iAddress) ); + } + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (Ipx.Global_Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + + + + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + } + + return (return_code); +} diff --git a/CODE/SEQCONN.CPP b/CODE/SEQCONN.CPP new file mode 100644 index 0000000..b5d6cc3 --- /dev/null +++ b/CODE/SEQCONN.CPP @@ -0,0 +1,562 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.10 01 Mar 1996 18:29:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * SequencedConnClass::SequencedConnClass -- class constructor * + * SequencedConnClass::~SequencedConnClass -- class destructor * + * SequencedConnClass::Init -- Initializes connection queue to empty * + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * SequencedConnClass::Receive_Packet -- adds packet to receive queue * + * SequencedConnClass::Get_Packet -- gets a packet from receive queue * + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * SequencedConnClass::Service_Receive_Queue -- services recieve queue * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "WolDebug.h" + +/*************************************************************************** + * SequencedConnClass::SequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::SequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); + +} /* end of SequencedConnClass */ + + +/*************************************************************************** + * SequencedConnClass::~SequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::~SequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * SequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void SequencedConnClass::Init (void) +{ + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { + NumSendAck++; + } else { + NumSendNoAck++; + } + return(true); + + } else { + return(false); + } +} + + +/*************************************************************************** + * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *entry_data; // ptr to queue entry data + int save_packet = 1; // 0 = this is a resend, or + // out-of-order packet + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber!=MagicNum) + return(false); + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Code) { + /*..................................................................... + DATA: If this is a No-Ack packet, always save it. Otherwise, only + save it if it's received in the proper sequence. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + if (packet->Code == PACKET_DATA_NOACK) { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); +#endif + save_packet = 1; + } else { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); +#endif + if ((packet->PacketID == NumRecAck)) { + save_packet = 1; + } else { + save_packet = 0; + /*............................................................... + If this is a resend of our next-available received message, it + means the other app didn't get our ACK, so mark it as + non-acknowledged to tell Service_Receive_Queue to send an ACK. + ...............................................................*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + if (entry_data->PacketID==packet->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + rec_entry->IsACK = 0; +#ifdef DEBUG_SEQ +printf("(Resend)\n"); +#endif + } + } + } + } + + /* + ...................... queue this packet ........................ + */ + if (save_packet) { + if (!Queue->Queue_Receive(buf, buflen)) { + return(false); + } + if (packet->Code == PACKET_DATA_ACK) { + NumRecAck++; + } else { + NumRecNoAck++; + } + } + break; + + /*..................................................................... + ACK: If this ACK is for the latest-sent packet, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: +#ifdef DEBUG_SEQ +printf("ACK received (%d)\n",packet->PacketID); +#endif + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Next_Send(); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry!=NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Next_Receive(); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + + return(false); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Send_Queue (void) +{ + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + + /*------------------------------------------------------------------------ + - If the next packet is ACK'd remove it from the queue + - If the next packet isn't ACK'd, [re-]send it + ------------------------------------------------------------------------*/ + /* + ......................... Get ptr to data to send ........................ + */ + send_entry = Queue->Next_Send(); + if (send_entry==NULL) + return(true); + + /* + ------------------ If ACK has been received, unqueue it ------------------ + */ + if (send_entry->IsACK) { + + /* + .................. Update this queue's response time .................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /* + ......................... unqueue the packet .......................... + */ +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Send(NULL,NULL); + } else { + + /* + ----------------- ACK not received yet, [re-]send packet ----------------- + */ + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ + Send (send_entry->Buffer, send_entry->BufLen); + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) + send_entry->IsACK = 1; + } + +#ifdef DEBUG_SEQ +packet_hdr = (CommHeaderType *)send_entry->Buffer; +if (packet_hdr->Code == PACKET_DATA_NOACK) { + printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); +} else { + printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); +} +#endif + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) + return(false); + if (Timeout != -1 && send_entry->LastTime - + send_entry->FirstTime > Timeout) + return(false); + + } + } + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Receive_Queue (void) +{ + CommHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry==NULL) + return(true); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { +#ifdef DEBUG_SEQ +printf("Sending ACK (%d)\n",packet_hdr->PacketID); +#endif + ackpacket.MagicNumber = MagicNum; + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet_hdr->PacketID; + + Send((char *)&ackpacket, sizeof(CommHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) { +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Receive(NULL,NULL); + } + + return(true); +} + diff --git a/CODE/SEQCONN.H b/CODE/SEQCONN.H new file mode 100644 index 0000000..57e5de4 --- /dev/null +++ b/CODE/SEQCONN.H @@ -0,0 +1,111 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\seqconn.h_v 1.13 01 Mar 1996 17:45:24 JOE_BOSTIC $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : SEQCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Sequenced" ACK/Retry approach to packet * + * transmission. It waits until the last packet has been ACK'd before * + * sending another packet. Thus, it guarantees order of delivery of * + * packets, but its performance will be slower than the Non-Sequenced * + * approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SEQCONN_H +#define SEQCONN_H + +#include "connect.h" +#include "comqueue.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class SequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + SequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~SequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet queue. + .....................................................................*/ + CommQueueClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + +}; + +#endif +/*************************** end of seqconn.h ******************************/ diff --git a/CODE/SESSION.CPP b/CODE/SESSION.CPP new file mode 100644 index 0000000..118f894 --- /dev/null +++ b/CODE/SESSION.CPP @@ -0,0 +1,1870 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/SESSION.CPP 3 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SESSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SessionClass::SessionClass -- Constructor * + * SessionClass::~SessionClass -- Destructor * + * SessionClass::One_Time -- one-time initializations * + * SessionClass::Init -- Initializes all values * + * SessionClass::Create_Connections -- forms connections to other players * + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * SessionClass::Save -- Saves this class to a file * + * SessionClass::Load -- Loads this class from a file * + * SessionClass::Read_MultiPlayer_Settings -- reads settings from INI * + * SessionClass::Write_MultiPlayer_Settings -- writes settings to INI * + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * SessionClass::Trap_Object -- searches for an object, for debugging * + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for station ID computation +#include // for station ID computation + +//#include "WolDebug.h" + +/***************************** Globals *************************************/ +//--------------------------------------------------------------------------- +// This is the array of remap colors. Each player in a network game is +// assigned one of these colors. The 'G' is for graphics drawing; the 'T' +// is for text printing (indicates a remap table for the font to use). +//--------------------------------------------------------------------------- +//int SessionClass::GColors[MAX_MPLAYER_COLORS] = { + //5, // Yellow + //127, // Red + //135, // BlueGreen + //26, // Orange + //4, // Green + //202 // Blue-Grey +//}; + +//int SessionClass::TColors[MAX_MPLAYER_COLORS] = { + //CC_GDI_COLOR, // Yellow + //CC_NOD_COLOR, // Red + //CC_BLUE_GREEN, // BlueGreen + //CC_ORANGE, // Orange + //CC_GREEN, // Green + //CC_BLUE_GREY, // Blue +//}; + +/*--------------------------------------------------------------------------- +Min & Max unit count values; index0 = bases OFF, index1 = bases ON +---------------------------------------------------------------------------*/ +int SessionClass::CountMin[2] = {1,0}; +int SessionClass::CountMax[2] = {50,12}; + +//--------------------------------------------------------------------------- +// This is a list of all the names of the multiplayer scenarios +//--------------------------------------------------------------------------- +char SessionClass::Descriptions[100][40]; + +//--------------------------------------------------------------------------- +// These values are used purely for the Mono debug display. They show the +// names of the Global Channel packet types, and the event types. +//--------------------------------------------------------------------------- +char * SessionClass::GlobalPacketNames[] = { + "Game?", + "Game!", + "Player?", + "Player!", + "Join?", + "Join!", + "Reject", + "GameOptions", + "Sign Off", + "GO!", + "Message", + "Ping", + "Load" +}; + +char * SessionClass::SerialPacketNames[] = { + "CONNECT", + "GAME_OPTIONS", + "SIGN_OFF", + "GO", + "MESSAGE", + "TIMING", + "SCORE_SCREEN", + "LOADGAME", + "LAST_COMMAND", +}; + + +char * SessionClass::DialMethodCheck[ DIAL_METHODS ] = { + "T", + "P" +}; + +char *SessionClass::CallWaitStrings[ CALL_WAIT_STRINGS_NUM ] = { + "*70,", + "70#,", + "1170,", + "CUSTOM - " +}; + +/*************************************************************************** + * SessionClass::SessionClass -- Constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::SessionClass(void) +{ + Type = GAME_NORMAL; + CommProtocol = DEFAULT_COMM_PROTOCOL; + + Options.ScenarioIndex = 0; + Options.Bases = 0; + Options.Credits = 0; + Options.Tiberium = 0; + Options.Goodies = 0; + Options.Ghosts = 0; + Options.UnitCount = 0; + + UniqueID = 0; + + Handle[0] = 0; + PrefColor = PCOLOR_FIRST; + ColorIdx = PCOLOR_FIRST; + House = HOUSE_GOOD; + ObiWan = 0; + Solo = 0; + + MaxPlayers = 8; + NumPlayers = 0; + + MaxAhead = 5; + FrameSendRate = DEFAULT_FRAME_SEND_RATE; + + LoadGame = 0; + EmergencySave = 0; + + LastMessage[0] = 0; + WWChat = 0; + + RecordFile.Set_Name("RECORD.BIN"); // always uses this name + Record= 0; // set via command line + Play = 0; // set via command line + Attract = 0; // set via command line + + IsBridge = 0; + NetStealth = 0; + NetProtect = 1; + NetOpen = 0; + GameName[0] = 0; + GProductID = 0; + + MetaSize = MAX_IPX_PACKET_SIZE; + + ModemService = true; + CurPhoneIdx = 0; // set from INI file + SerialDefaults.Port = 0x2f8; // set from INI file + SerialDefaults.IRQ = 3; // set from INI file + SerialDefaults.Baud = 9600; // set from INI file + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; // set from INI file + SerialDefaults.InitStringIndex = 0; // set from INI file + SerialDefaults.CallWaitStringIndex = 0; // set from INI file + strcpy(SerialDefaults.CallWaitString,""); + ModemType = MODEM_NULL_HOST; // set from INI file + + TrapFrame = 0x7fffffff; // frame to start trapping object values at + TrapObjType = RTTI_NONE; // type of object to trap + TrapObject.Ptr.All = NULL; // ptr to object being trapped + TrapCoord = 0; // COORDINATE of object to trap + TrapTarget = TARGET_NONE; // TARGET value of object to trap + TrapCell = NULL; // for trapping a cell + TrapCheckHeap = 0; // start checking the Heap + TrapPrintCRC = 0; // output CRC file + +#if(TEN) + TenPacket = NULL; + TenSize = 200; + TenPlayerID = -1; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // TEN + +#if(MPATH) + MPathPacket = NULL; + MPathSize = 200; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // MPATH + +} // end of SessionClass + + +/*************************************************************************** + * SessionClass::~SessionClass -- Destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::~SessionClass(void) +{ +} // end of ~SessionClass + + +/*************************************************************************** + * SessionClass::One_Time -- one-time initializations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::One_Time(void) +{ + Read_MultiPlayer_Settings(); + Read_Scenario_Descriptions(); + + UniqueID = Compute_Unique_ID(); + +} // end of One_Time + + +/*************************************************************************** + * SessionClass::Init -- Initializes all values * + * * + * This function should be called for every new game played; it only sets * + * those variables that should be set for a new game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Init(void) +{ + +} // end of Init + + +/*************************************************************************** + * SessionClass::Create_Connections -- forms connections to other players * + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_Connections(void) +{ + int i; + + if (Session.Type != GAME_IPX && Session.Type != GAME_INTERNET) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ipx.Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + &(Players[i]->Address) ); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_Connections + + +#if(TEN) +/*************************************************************************** + * SessionClass::Create_TEN_Connections -- forms connections to TEN players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_TEN_Connections(void) +{ + int i; + + if (Session.Type != GAME_TEN) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ten->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->TenAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_TEN_Connections + +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * SessionClass::Create_MPATH_Connections -- forms connections to MPATH players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_MPATH_Connections(void) +{ + int i; + + if (Session.Type != GAME_MPATH) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + MPath->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->MPathAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_MPATH_Connections + +#endif // MPATH + + +/*************************************************************************** + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +bool SessionClass::Am_I_Master(void) +{ + int i; + HousesType house; + HouseClass *hptr; + + //------------------------------------------------------------------------ + // Check every house; if PlayerPtr points to the first human house, we're + // the master. + //------------------------------------------------------------------------ + for (i = 0; i < Session.MaxPlayers; i++) { + house = (HousesType)((int)HOUSE_MULTI1 + i); + hptr = HouseClass::As_Pointer(house); + if (hptr->IsHuman) { + if (PlayerPtr == hptr) { + return (true); + } + else { + return (false); + } + } + } + + return (false); + +} // end of Am_I_Master + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::Save(Pipe & file) const +{ +#ifdef FIXIT_MULTI_SAVE + file.Put(&CommProtocol, sizeof(CommProtocol)); + file.Put(&MaxAhead, sizeof(MaxAhead)); + file.Put(&FrameSendRate, sizeof(FrameSendRate)); + file.Put(&DesiredFrameRate, sizeof(DesiredFrameRate)); +#endif // FIXIT_MULTI_SAVE + file.Put(&PrefColor, sizeof(PrefColor)); + file.Put(&ColorIdx, sizeof(ColorIdx)); + file.Put(&House, sizeof(House)); + file.Put(&NumPlayers, sizeof(NumPlayers)); + file.Put(&Options.Bases, sizeof(Options.Bases)); + file.Put(&Options.Credits, sizeof(Options.Credits)); + file.Put(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Put(&Options.Goodies, sizeof(Options.Goodies)); + file.Put(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Put(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Put(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Put(&ObiWan, sizeof(ObiWan)); + file.Put(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::Load(Straw & file) +{ +#ifdef FIXIT_MULTI_SAVE +// if(GameVersion != 0x0100616D){ + file.Get(&CommProtocol, sizeof(CommProtocol)); + file.Get(&MaxAhead, sizeof(MaxAhead)); + file.Get(&FrameSendRate, sizeof(FrameSendRate)); + file.Get(&DesiredFrameRate, sizeof(DesiredFrameRate)); +// } +#endif // FIXIT_MULTI_SAVE + file.Get(&PrefColor, sizeof(PrefColor)); + file.Get(&ColorIdx, sizeof(ColorIdx)); + file.Get(&House, sizeof(House)); + file.Get(&NumPlayers, sizeof(NumPlayers)); + file.Get(&Options.Bases, sizeof(Options.Bases)); + file.Get(&Options.Credits, sizeof(Options.Credits)); + file.Get(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Get(&Options.Goodies, sizeof(Options.Goodies)); + file.Get(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Get(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Get(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Get(&ObiWan, sizeof(ObiWan)); + file.Get(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::Save(CCFileClass & file) +{ + int i; + + file.Write(&Type, sizeof(Type)); + file.Write(&CommProtocol, sizeof(CommProtocol)); + file.Write(&FrameSendRate, sizeof(FrameSendRate)); + file.Write(&PrefColor, sizeof(PrefColor)); + file.Write(&ColorIdx, sizeof(ColorIdx)); + file.Write(&House, sizeof(House)); + file.Write(&NumPlayers, sizeof(NumPlayers)); + file.Write(&Options.Bases, sizeof(Options.Bases)); + file.Write(&Options.Credits, sizeof(Options.Credits)); + file.Write(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Write(&Options.Goodies, sizeof(Options.Goodies)); + file.Write(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Write(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Write(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Write(&ObiWan, sizeof(ObiWan)); + file.Write(&EmergencySave, sizeof(EmergencySave)); + + i = Players.Count(); + file.Write(&i, sizeof(i)); + for (i = 0; i < Players.Count(); i++) { + file.Write(Players[i], sizeof(NodeNameType)); + } + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::Load(CCFileClass & file) +{ + int count; + int i; + NodeNameType *node; + + file.Read(&Type, sizeof(Type)); + file.Read(&CommProtocol, sizeof(CommProtocol)); + file.Read(&FrameSendRate, sizeof(FrameSendRate)); + file.Read(&PrefColor, sizeof(PrefColor)); + file.Read(&ColorIdx, sizeof(ColorIdx)); + file.Read(&House, sizeof(House)); + file.Read(&NumPlayers, sizeof(NumPlayers)); + file.Read(&Options.Bases, sizeof(Options.Bases)); + file.Read(&Options.Credits, sizeof(Options.Credits)); + file.Read(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Read(&Options.Goodies, sizeof(Options.Goodies)); + file.Read(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Read(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Read(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Read(&ObiWan, sizeof(ObiWan)); + file.Read(&EmergencySave, sizeof(EmergencySave)); + + file.Read(&count, sizeof(count)); + for (i = 0; i < count; i++) { + node = new NodeNameType; + file.Read(node, sizeof(NodeNameType)); + Players.Add(node); + } + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Read_MultiPlayer_Settings -- reads settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Read_MultiPlayer_Settings (void) +{ + char *tokenptr; // ptr to token + PhoneEntryClass *phone; // a phone book entry + char *entry; // a phone book entry + char buf[128]; // buffer for parsing INI entry + int i; + CELL cell; + +// CCFileClass file (CONFIG_FILE_NAME); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete[] InitStrings[i]; + } + InitStrings.Clear(); + + // Clear the dialing entries + for (i = 0; i < PhoneBook.Count(); i++) { + delete[] PhoneBook[i]; + } + PhoneBook.Clear(); + + // Create filename and read the file. + INIClass ini; + if (ini.Load(RawFileClass(CONFIG_FILE_NAME))) { + + // Get the player's last-used Handle + ini.Get_String("MultiPlayer", "Handle", "Noname", Handle, sizeof(Handle)); + + // Get the player's last-used Color + PrefColor = (PlayerColorType)ini.Get_Int("MultiPlayer", "Color", 0); +#ifdef FIXIT_VERSION_3 + int iSide = ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); + iSide = max( 2, min( 6, iSide ) ); + House = (HousesType)iSide; +#else + House = (HousesType)ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); +#endif + CurPhoneIdx = ini.Get_Int("MultiPlayer", "PhoneIndex", -1); + TrapCheckHeap = ini.Get_Int("MultiPlayer", "CheckHeap", 0); + + // Read in default serial settings + ini.Get_String("SerialDefaults", "ModemName", "NoName", SerialDefaults.ModemName, MODEM_NAME_MAX); + if (!strcmp ( SerialDefaults.ModemName, "NoName")) { + SerialDefaults.ModemName[0] = 0; + } + SerialDefaults.Port = ini.Get_Int("SerialDefaults", "Port", 0); + SerialDefaults.IRQ = ini.Get_Int("SerialDefaults", "IRQ", -1); + SerialDefaults.Baud = ini.Get_Int("SerialDefaults", "Baud", -1); + SerialDefaults.Compression = ini.Get_Int ("SerialDefaults", "Compression", 0); + SerialDefaults.ErrorCorrection = ini.Get_Int ("SerialDefaults", "ErrorCorrection", 0); + SerialDefaults.HardwareFlowControl = ini.Get_Int ("SerialDefaults", "HardwareFlowControl", 1); + + ini.Get_String("SerialDefaults", "DialMethod", "T", buf, 2); + +#ifndef WIN32 + /* + ** Ignore any modem name in DOS. This should only be nessasary if the user + ** previously set up the modem in the windows version. + */ + if (SerialDefaults.ModemName[0] && SerialDefaults.Port == 1) { + SerialDefaults.Port = 0x3F8; + SerialDefaults.ModemName[0] = 0; + } +#endif //WIN32 + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + SerialDefaults.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; + } + + SerialDefaults.InitStringIndex = ini.Get_Int("SerialDefaults", "InitStringIndex", 0); + + SerialDefaults.CallWaitStringIndex = ini.Get_Int("SerialDefaults", "CallWaitStringIndex", CALL_WAIT_CUSTOM); + + ini.Get_String("SerialDefaults", "CallWaitString", "", SerialDefaults.CallWaitString, CWAITSTRBUF_MAX); + + if (SerialDefaults.IRQ == 0 || SerialDefaults.Baud == 0) { + SerialDefaults.Port = 0; + SerialDefaults.IRQ = -1; + SerialDefaults.Baud = -1; + } + + int initcount = ini.Entry_Count("InitStrings"); + for (int index = 0; index < initcount; index++) { + entry = new char[ INITSTRBUF_MAX ]; + entry[0] = 0; + ini.Get_String("InitStrings", ini.Get_Entry("InitStrings", index), NULL, entry, INITSTRBUF_MAX); + strupr( entry ); + InitStrings.Add( entry ); + } + + // if no entries then have at least one + if (initcount == 0) { + entry = new char[ INITSTRBUF_MAX ]; + strcpy( entry, "ATZ" ); + InitStrings.Add( entry ); + SerialDefaults.InitStringIndex = 0; + } + + // Read the entry names in + int phonecount = ini.Entry_Count("PhoneBook"); + for (index = 0; index < phonecount; index++) { + // Create a new phone book entry + phone = new PhoneEntryClass(); + + // Read the entire entry in + ini.Get_String("PhoneBook", ini.Get_Entry("PhoneBook", index), NULL, buf, sizeof(buf)); + + // Extract name, phone # & serial port settings + tokenptr = strtok( buf, "|" ); + if (tokenptr) { + strcpy( phone->Name, tokenptr ); + strupr( phone->Name ); + } else { + phone->Name[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( phone->Number, tokenptr ); + strupr( phone->Number ); + } else { + phone->Number[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + sscanf( tokenptr, "%x", &phone->Settings.Port ); + } else { + phone->Settings.Port = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.IRQ = atoi( tokenptr ); + } else { + phone->Settings.IRQ = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Baud = atoi( tokenptr ); + } else { + phone->Settings.Baud = -1; + } + + phone->Settings.Compression = 0; + phone->Settings.ErrorCorrection = 0; + phone->Settings.HardwareFlowControl = 1; + + /* + ** Find out if this phonebook entry has the new settings included. If not + ** then we need to skip this section. + */ + tokenptr = strtok( NULL, "|" ); + if (tokenptr){ + strcpy( buf, tokenptr ); + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + /* + ** This must be an old phonebook entry + */ + break; + } + } + + /* + ** Method wasnt found - assume its a new phonebook entry so get the extra settings + */ + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + + phone->Settings.Compression = atoi( tokenptr ); + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.ErrorCorrection = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.HardwareFlowControl = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + } + } + + + if (tokenptr) { + strcpy( buf, tokenptr ); + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + phone->Settings.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + } else { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.InitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.InitStringIndex = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.CallWaitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy (phone->Settings.CallWaitString, tokenptr); + } else { + phone->Settings.CallWaitString[0] = 0; + } + + // Add it to our list + PhoneBook.Add(phone); + } + + // Read special recording playback values, to help find sync bugs + TrapFrame = ini.Get_Int("SyncBug", "Frame", 0x7fffffff); + + ini.Get_String("SyncBug", "Type", "NONE", buf, 80); + + if (!stricmp(buf,"AIRCRAFT")) + TrapObjType = RTTI_AIRCRAFT; + else if (!stricmp(buf,"ANIM")) + TrapObjType = RTTI_ANIM; + else if (!stricmp(buf,"BUILDING")) + TrapObjType = RTTI_BUILDING; + else if (!stricmp(buf,"BULLET")) + TrapObjType = RTTI_BULLET; + else if (!stricmp(buf,"INFANTRY")) + TrapObjType = RTTI_INFANTRY; + else if (!stricmp(buf,"UNIT")) + TrapObjType = RTTI_UNIT; + else { + TrapObjType = RTTI_NONE; + } + + ini.Get_String("SyncBug", "Coord", "0", buf, 80); + sscanf(buf,"%x",&TrapCoord); + + ini.Get_String("SyncBug", "Target", "0", buf, 80); + sscanf(buf,"%x",&TrapTarget); + + ini.Get_String("SyncBug", "Cell", "0", buf, 80); + cell = atoi(buf); + if (cell) { + TrapCell = &(Map[cell]); + } + + TrapPrintCRC = ini.Get_Int("SyncBug", "PrintCRC", 0x7fffffff); + } +} + + +/*************************************************************************** + * SessionClass::Write_MultiPlayer_Settings -- writes settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Write_MultiPlayer_Settings (void) +{ +#ifdef NEVER + char * buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char entrytext[4]; + char buf[128]; // buffer for parsing INI entry + + //------------------------------------------------------------------------ + // Get a working pointer to the INI staging buffer. Make sure that the + // buffer starts cleared out of any data. + //------------------------------------------------------------------------ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name(CONFIG_FILE_NAME); + if (file.Is_Available()) { + file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + } + + //------------------------------------------------------------------------ + // Save the player's last-used Handle & Color + //------------------------------------------------------------------------ + WWWritePrivateProfileInt("MultiPlayer", "PhoneIndex", CurPhoneIdx, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Color", (int)PrefColor, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Side", House, buffer); + WWWritePrivateProfileString("MultiPlayer", "Handle", Handle, buffer); + + //------------------------------------------------------------------------ + // Clear all existing Settings.SerialDefault entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("SerialDefaults", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save default serial settings in opposite order you want to see them + //------------------------------------------------------------------------ + WWWritePrivateProfileString("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex, buffer); + WWWritePrivateProfileString("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ], buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Baud", SerialDefaults.Baud, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "IRQ", SerialDefaults.IRQ, buffer); + sprintf(buf, "%x", SerialDefaults.Port); + WWWritePrivateProfileString("SerialDefaults", "Port", buf, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Compression", SerialDefaults.Compression , buffer); + WWWritePrivateProfileInt ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl, buffer); + + //------------------------------------------------------------------------ + // Clear all existing InitString entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("InitStrings", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all InitString entries. In descending order so they come out in + // ascending order. + //------------------------------------------------------------------------ + for (i = (InitStrings.Count() - 1); i >= 0; i--) { + sprintf( buf, "%03d", i); + WWWritePrivateProfileString ("InitStrings", buf, InitStrings[i], buffer); + } + + //------------------------------------------------------------------------ + // Clear all existing Phone Book entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("PhoneBook", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + //------------------------------------------------------------------------ + for (i = (PhoneBook.Count() - 1); i >= 0; i--) { + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + WWWritePrivateProfileString ("PhoneBook", entrytext, buf, buffer); + } + + //------------------------------------------------------------------------ + // Write the INI data out to a file. + //------------------------------------------------------------------------ + file.Open(WRITE); + file.Write(buffer,strlen(buffer)); + file.Close(); +#endif + + INIClass ini; + RawFileClass file(CONFIG_FILE_NAME); + if (ini.Load(file)) { + + // Save the player's last-used Handle & Color + ini.Put_Int("MultiPlayer", "PhoneIndex", CurPhoneIdx); + ini.Put_Int("MultiPlayer", "Color", (int)PrefColor); + ini.Put_Int("MultiPlayer", "Side", House); + ini.Put_String("MultiPlayer", "Handle", Handle); + + // Clear all existing Settings.SerialDefault entries. + ini.Clear("SerialDefaults"); + + // Save default serial settings in opposite order you want to see them + ini.Put_String("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString); + ini.Put_Int("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex); + ini.Put_Int("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex); + ini.Put_String("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ]); + ini.Put_Int("SerialDefaults", "Baud", SerialDefaults.Baud); + ini.Put_Int("SerialDefaults", "IRQ", SerialDefaults.IRQ); + ini.Put_Int("SerialDefaults", "Port", SerialDefaults.Port, 1); + ini.Put_String("SerialDefaults", "ModemName", SerialDefaults.ModemName); + ini.Put_Int ("SerialDefaults", "Compression", SerialDefaults.Compression ); + ini.Put_Int ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection ); + ini.Put_Int ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl ); + + // Clear all existing InitString entries. + ini.Clear("InitStrings"); + + // Save all InitString entries. + for (int index = 0; index < InitStrings.Count(); index++) { + char buf[10]; + sprintf( buf, "%03d", index); + ini.Put_String("InitStrings", buf, InitStrings[index]); + } + + // Clear all existing Phone Book entries. + ini.Clear("PhoneBook"); + + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + for (int i = (PhoneBook.Count() - 1); i >= 0; i--) { + char buf[128]; + char entrytext[10]; + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + ini.Put_String("PhoneBook", entrytext, buf); + } + + // Write the INI data out to a file. + ini.Save(file); + } +} + + +// Determine if a mission is from counterstrike or aftermath, or either. +// Multiplayer maps >24, with a numerical name, are Counterstrike. +// Multiplayer maps with an alphabetical name, like SCMJGEA.INI, are Aftermath. + +bool Is_Mission_Counterstrike (char *file_name) +{ + int scenario_number = 0; + + if ( isdigit ( file_name[5] )){ + sscanf (file_name, "SCM%03d", &scenario_number); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!isdigit(file_name[3]) || !isdigit(file_name[4])) { + return(false); + } +#endif + sscanf (file_name, "SCM%02d", &scenario_number); + } + return ( scenario_number > 24 ); +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Aftermath (char *file_name) +{ + // ajw added + // Must start with "scm". + char szCopy[ _MAX_PATH + 1 ]; + strcpy( szCopy, file_name ); + _strlwr( szCopy ); + if( strstr( szCopy, "scm" ) != szCopy ) + return false; + + if (isdigit(file_name[5])) { + return(false); + } + + if ( !isdigit(file_name[3]) || !isdigit(file_name[4]) ) { + return (true); + } + return (false); +} + +/* +** Certain missions are 126x126 size, and those can't be downloaded to a +** non-Aftermath player, so this function checks to see if the map in +** question is one of those. We'll know that by the file name: if it's +** K0 -> M9, it's 126x126. +*/ +bool Is_Mission_126x126 (char *file_name) // This is no longer used. ajw +{ + if (isdigit(file_name[5])) { + return(false); + } + + if ( (file_name[3] >= 'k' && file_name[3] <= 'm') || + (file_name[3] >= 'K' && file_name[3] <= 'M') ) { + return (true); + } + return (false); +} + +#endif + + + + + +/*************************************************************************** + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 09/10/1996 JLB : Searches using different method. * + *=========================================================================*/ +void SessionClass::Read_Scenario_Descriptions (void) +{ + + // Clear the scenario description lists + Scenarios.Clear(); + + /* + ** Fetch the main multiplayer scenario packet data. + */ + CCFileClass file("MISSIONS.PKT"); + if (file.Is_Available()) { + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in Missions.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "msns_pkt.txt" ); + file.Seek( 0, SEEK_SET ); + long lSize = file.Size(); + char* pData = new char[ lSize + 1 ]; + file.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } + + /* + ** Fetch any scenario packet lists and apply them first. + */ +#ifdef WIN32 + WIN32_FIND_DATA block; + HANDLE handle = FindFirstFile("*.PKT", &block); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + char const * name = &block.cAlternateFileName[0]; + if (*name == '\0') name = &block.cFileName[0]; +//Mono_Printf("Found file '%s'.\n", block.cAlternateFileName); +//Mono_Printf("Found file '%s'.\n", block.cFileName); +//debugprint("Found file '%s'.\n", block.cAlternateFileName); +//debugprint("Found file '%s'.\n", block.cFileName); +//debugprint( "Found alternate PKT file.\n" ); + CCFileClass file(name); + INIClass ini; + ini.Load(file); + + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } + } + + if (FindNextFile(handle, &block) == 0) break; + } + + + #ifdef FIXIT_CSII // checked - ajw + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +#ifdef FIXIT_VERSION_3 + if( Is_Counterstrike_Installed() ) + { +#endif + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in cstrike.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "cs_pkt.txt" ); + file2.Seek( 0, SEEK_SET ); + long lSize = file2.Size(); + char* pData = new char[ lSize + 1 ]; + file2.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } +#ifdef FIXIT_VERSION_3 + } +#endif + #endif + +#ifdef FIXIT_VERSION_3 // Aftermath scenarios are now in their own pkt file. + if( Is_Aftermath_Installed() ) + { + CCFileClass file2("AFTMATH.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in aftmath.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); + } + } + } +#endif + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + char const * file_name; + char name_buffer[128]; + char digest_buffer[32]; + + handle = FindFirstFile ( "*.MPR" , &block ); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + file_name = &block.cAlternateFileName[0]; + if (*file_name == '\0') file_name = &block.cFileName[0]; +//debugprint( "Found MPR '%s'\n", file_name ); + CCFileClass file(file_name); + INIClass ini; + ini.Load(file); + + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + Scenarios.Add (new MultiMission (file_name, name_buffer, digest_buffer,ini.Get_Bool("Basic", "Official", false), false )); + } + + if (FindNextFile(handle, &block) == 0) break; + } + +#else //WIN32 + +#error What? You think you can still build the DOS version after all this time? + + char name_buffer[128]; + char digest_buffer[32]; + + struct find_t block; + if (_dos_findfirst("*.PKT", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_CSII + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } + + } while(_dos_findnext(&block) == 0); + } + + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + if (_dos_findfirst("*.MPR", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + bool official = ini.Get_Bool("Basic", "Official", false); + Scenarios.Add (new MultiMission (block.name, name_buffer, digest_buffer, official, false )); + } while(_dos_findnext(&block) == 0); + } + + #ifdef FIXIT_CSII + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +// if (Is_Counterstrike_Installed()) { + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); + } + } +// } + #endif + +#endif //WIN32 +} + + +/*************************************************************************** + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Free_Scenario_Descriptions(void) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the scenario descriptions & filenames + //------------------------------------------------------------------------ + for (int index = 0; index < Scenarios.Count(); index++) { + delete Scenarios[index]; + } + Scenarios.Clear(); +// Filenum.Clear(); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete InitStrings[i]; + } + InitStrings.Clear(); + + //------------------------------------------------------------------------ + // Clear the dialing entries + //------------------------------------------------------------------------ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); + +} /* end of Free_Scenario_Descriptions */ + + +/*************************************************************************** + * SessionClass::Trap_Object -- searches for an object, for debugging * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Trap_Object(void) +{ + int i; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + TrapObject.Ptr.All = NULL; + + //------------------------------------------------------------------------ + // Search for the object based upon its type, then its coordinate or + // 'this' pointer value. + //------------------------------------------------------------------------ + switch (TrapObjType) { + case RTTI_AIRCRAFT: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Ptr(i)->Coord == TrapCoord || + Aircraft.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); + break; + } + } + break; + + case RTTI_ANIM: + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Ptr(i)->Coord == TrapCoord || + Anims.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Ptr(i); + break; + } + } + break; + + case RTTI_BUILDING: + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->Coord == TrapCoord || + Buildings.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Ptr(i); + break; + } + } + break; + + case RTTI_BULLET: + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Ptr(i)->Coord == TrapCoord || + Bullets.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Ptr(i); + break; + } + } + break; + + case RTTI_INFANTRY: + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->Coord == TrapCoord || + Infantry.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Ptr(i); + break; + } + } + break; + + case RTTI_UNIT: + for (i = 0; i < Units.Count(); i++) { + if (Units.Ptr(i)->Coord == TrapCoord || + Units.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Ptr(i); + break; + } + } + break; + + //..................................................................... + // Last-ditch find-the-object-right-now-darnit loop + //..................................................................... + case RTTI_NONE: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || + Aircraft.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); + TrapObjType = RTTI_AIRCRAFT; + return; + } + } + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Raw_Ptr(i)->Coord == TrapCoord || + Anims.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); + TrapObjType = RTTI_ANIM; + return; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || + Buildings.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); + TrapObjType = RTTI_BUILDING; + return; + } + } + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || + Bullets.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); + TrapObjType = RTTI_BULLET; + return; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || + Infantry.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); + TrapObjType = RTTI_INFANTRY; + return; + } + } + for (i = 0; i < Units.Count(); i++) { + if (Units.Raw_Ptr(i)->Coord == TrapCoord || + Units.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Raw_Ptr(i); + TrapObjType = RTTI_UNIT; + return; + } + } + + default: + break; + } +} + + +/*************************************************************************** + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1995 BRR : Created. * + *=========================================================================*/ +unsigned long SessionClass::Compute_Unique_ID(void) +{ + time_t tm; + unsigned long id; + struct diskfree_t dtable; + char *path; + int i; + + //------------------------------------------------------------------------ + // Start with the seconds since Jan 1, 1970 (system local time) + //------------------------------------------------------------------------ + time(&tm); + id = (unsigned long)tm; + + //------------------------------------------------------------------------ + // Now add in the free space on the hard drive + //------------------------------------------------------------------------ + if (_dos_getdiskfree(3, &dtable) == 0) { + Add_CRC(&id, (unsigned long)dtable.avail_clusters); + Add_CRC(&id, (unsigned long)dtable.total_clusters); + Add_CRC(&id, (unsigned long)dtable.bytes_per_sector); + Add_CRC(&id, (unsigned long)dtable.sectors_per_cluster); + } + + //------------------------------------------------------------------------ + // Add in every byte in the user's path environment variable + //------------------------------------------------------------------------ + path = getenv("PATH"); + if (path) { + for (i = 0; i < strlen(path); i++) { + Add_CRC(&id, (unsigned long)path[i]); + } + } + + return (id); + +} // end of Compute_Unique_ID + + + + +MultiMission::MultiMission(char const * filename, char const * description, char const * digest, bool official, bool expansion) +{ + Set_Filename(filename); + Set_Description(description); + Set_Digest(digest); + Set_Official(official); + Set_Expansion(expansion); +} + + +void MultiMission::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(ScenarioDescription, x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(ScenarioDescription, x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +void MultiMission::Set_Description(char const * description) +{ + if (description != NULL) { + strncpy(ScenarioDescription, description, ARRAY_SIZE(ScenarioDescription)); + ScenarioDescription[ARRAY_SIZE(ScenarioDescription)-1] = '\0'; + } +} + + +void MultiMission::Set_Filename(char const * filename) +{ + if (filename != NULL) { + strncpy(Filename, filename, ARRAY_SIZE(Filename)); + Filename[ARRAY_SIZE(Filename)-1] = '\0'; + } +} + +void MultiMission::Set_Digest(char const * digest) +{ + if (digest != NULL) { + strncpy(Digest, digest, ARRAY_SIZE(Digest)); + Digest[ARRAY_SIZE(Digest)-1] = '\0'; + } + else + { + strcpy( Digest, "NODIGEST" ); + } +} + +void MultiMission::Set_Official (bool official) +{ + IsOfficial = official; +} + +void MultiMission::Set_Expansion (bool expansion) +{ + IsExpansion = expansion; +} + + +/************************** end of session.cpp *****************************/ diff --git a/CODE/SESSION.H b/CODE/SESSION.H new file mode 100644 index 0000000..9bb6909 --- /dev/null +++ b/CODE/SESSION.H @@ -0,0 +1,705 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/SESSION.H 4 3/10/97 6:23p Steve_tall $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : SESSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + * The purpose of this class is to contain those variables & routines * + * specifically related to a multiplayer game. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SESSION_H +#define SESSION_H + +#include "ipxaddr.h" +#include "msglist.h" +#include "connect.h" + +//--------------------------------------------------------------------------- +// Forward declarations +//--------------------------------------------------------------------------- +class AircraftClass; +class AnimClass; +class BuildingClass; +class BulletClass; +class InfantryClass; +class UnitClass; +class PhoneEntryClass; +class CellClass; + +//--------------------------------------------------------------------------- +// Defines +//--------------------------------------------------------------------------- +//........................................................................... +// Various limiting values +//........................................................................... +#define MPLAYER_BUILD_LEVEL_MAX 10 // max build level in multiplay +#define MAX_MPLAYER_COLORS 8 // max # of colors + +//........................................................................... +// Max sizes of packets we want to send +// The IPX packet's size is IPX's max size (546), rounded down to accommodate +// the max number of events possible. +//........................................................................... +#define MAX_IPX_PACKET_SIZE (((546 - sizeof(CommHeaderType)) / \ + sizeof(EventClass) ) * sizeof(EventClass)) +#define MAX_SERIAL_PACKET_SIZE 256 + +//........................................................................... +// Max length of player names fields; attempt to use the constant for the +// HouseClass, if it's been defined; otherwise, define it myself. +//........................................................................... +#ifdef HOUSE_NAME_MAX +#define MPLAYER_NAME_MAX HOUSE_NAME_MAX +#else +#define MPLAYER_NAME_MAX 12 // max length of a player's name +#endif + +//........................................................................... +// Values to control the multiplayer score screen +//........................................................................... +#define MAX_MULTI_NAMES 8 // max # names (rows) on the score screen +#define MAX_MULTI_GAMES 4 // max # games (columns) on the score screen + +//........................................................................... +// Min value for MaxAhead, for both net & modem; only applies for +// COMM_PROTOCOL_MULTI_E_COMP. +//........................................................................... +#define MODEM_MIN_MAX_AHEAD 5 +#define NETWORK_MIN_MAX_AHEAD 2 + +//........................................................................... +// Send period (in frames) for COMM_PROTOCOL_MULTI_E_COMP and above +//........................................................................... +#define DEFAULT_FRAME_SEND_RATE 3 + +//........................................................................... +// Modem-specific constants +//........................................................................... +#define PORTBUF_MAX 64 // dialog field sizes +#define IRQBUF_MAX 3 +#define BAUDBUF_MAX 7 +#define INITSTRBUF_MAX 41 +#define CWAITSTRBUF_MAX 16 +#define CREDITSBUF_MAX 5 +#define PACKET_TIMING_TIMEOUT 40 // ticks b/w sending a timing packet +#define MODEM_NAME_MAX PORTBUF_MAX - 1 // Max length of modem name in list box + +//--------------------------------------------------------------------------- +// Enums +//--------------------------------------------------------------------------- +//........................................................................... +// Types of games; used to tell which protocol we're using +//........................................................................... +typedef enum GameEnum { + GAME_NORMAL, // not multiplayer + GAME_MODEM, // modem game + GAME_NULL_MODEM, // NULL-modem + GAME_IPX, // IPX Network game + GAME_INTERNET, // Internet H2H + GAME_SKIRMISH, // 1 plr vs. AI's + GAME_TEN, // TEN Network game + GAME_MPATH // MPath Network game +} GameType; + +//........................................................................... +// Various Modem-specific enums +//........................................................................... +typedef enum DetectPortType { + PORT_VALID = 0, + PORT_INVALID, + PORT_IRQ_INUSE +} DetectPortType; + +typedef enum DialStatusType { + DIAL_CONNECTED = 0, + DIAL_NO_CARRIER, + DIAL_BUSY, + DIAL_ERROR, + DIAL_NO_DIAL_TONE, + DIAL_CANCELED +} DialStatusType; + +typedef enum DialMethodType { + DIAL_TOUCH_TONE = 0, + DIAL_PULSE, + DIAL_METHODS +} DialMethodType; + +typedef enum CallWaitStringType { + CALL_WAIT_TONE_1 = 0, + CALL_WAIT_TONE_2, + CALL_WAIT_PULSE, + CALL_WAIT_CUSTOM, + CALL_WAIT_STRINGS_NUM +} CallWaitStringType; + +typedef enum ModemGameType { + MODEM_NULL_HOST = 0, + MODEM_NULL_JOIN, + MODEM_DIALER, + MODEM_ANSWERER +} ModemGameType; + +//........................................................................... +// Commands sent over the serial Global Channel +//........................................................................... +typedef enum SerialCommandType { + SERIAL_CONNECT = 100, // Are you there? Hello? McFly? + SERIAL_GAME_OPTIONS = 101, // Hey, dudes, here's some new game options + SERIAL_SIGN_OFF = 102, // Bogus, dudes, my boss is coming; I'm outta here! + SERIAL_GO = 103, // OK, dudes, jump into the game loop! + SERIAL_MESSAGE = 104, // Here's a message + SERIAL_TIMING = 105, // timimg packet + SERIAL_SCORE_SCREEN = 106, // player at score screen + SERIAL_LOADGAME = 107, // Start the game, loading a saved game first + SERIAL_LAST_COMMAND, // last command + SERIAL_REQ_SCENARIO = 1000, // Reqest that host sends the scenario file to the other players. + SERIAL_FILE_INFO = 1001, // Info about the file that is going to be transferred + SERIAL_FILE_CHUNK = 1002, // A chunk of scenario + SERIAL_READY_TO_GO = 1003, // Sent in response to a 'GO' command + SERIAL_NO_SCENARIO = 1004 // Scenario isnt available on remote machine so we cant play +} SerialCommandType; + +//........................................................................... +// Commands sent over the network Global Channel +//........................................................................... +typedef enum NetCommandType { + NET_QUERY_GAME, // Hey, what games are out there? + NET_ANSWER_GAME, // Yo, Here's my game's name! + NET_QUERY_PLAYER, // Hey, what players are in this game? + NET_ANSWER_PLAYER, // Yo, I'm in that game! + NET_CHAT_ANNOUNCE, // I'm at the chat screen + NET_CHAT_REQUEST, // Respond with a CHAT_ANNOUNCE, please. + NET_QUERY_JOIN, // Hey guys, can I play too? + NET_CONFIRM_JOIN, // Well, OK, if you really want to. + NET_REJECT_JOIN, // No, you can't join; sorry, dude. + NET_GAME_OPTIONS, // Hey, dudes, here's some new game options + NET_SIGN_OFF, // Bogus, dudes, my boss is coming; I'm outta here! + NET_GO, // OK, jump into the game loop! + NET_MESSAGE, // Here's a message + NET_PING, // I'm pinging you to take a time measurement + NET_LOADGAME, // start a game by loading a saved game + NET_REQ_SCENARIO =1000,// Reqest that host sends the scenario file to the other players. + NET_FILE_INFO =1001,// Info about the file that is going to be transferred + NET_FILE_CHUNK =1002,// A chunk of scenario + NET_READY_TO_GO =1003,// Sent in response to a 'GO' command + NET_NO_SCENARIO =1004 // Scenario isnt available on remote machine so we cant play +} NetCommandType; + +//--------------------------------------------------------------------------- +// Structures +//--------------------------------------------------------------------------- +//........................................................................... +// An entry on the score screen is defined by this structure +//........................................................................... +typedef struct { + char Name[MPLAYER_NAME_MAX]; + int Wins; + int Kills[MAX_MULTI_GAMES]; + PlayerColorType Color; +} MPlayerScoreType; + +//........................................................................... +// Settings for the serial port +//........................................................................... +typedef struct { + int Port; + int IRQ; + int Baud; + DialMethodType DialMethod; + int InitStringIndex; + int CallWaitStringIndex; + char CallWaitString[ CWAITSTRBUF_MAX ]; + bool Compression; + bool ErrorCorrection; + bool HardwareFlowControl; + char ModemName [ MODEM_NAME_MAX ]; +} SerialSettingsType; + +//........................................................................... +// This is a "node", used for the lists of available games & players. The +// 'Game' structure is used for games; the 'Player' structure for players. +//........................................................................... +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; // player or game name + IPXAddressClass Address; +#if(TEN) + int TenAddress; +#endif +#if(MPATH) + int MPathAddress; +#endif + union { + struct { + unsigned char IsOpen; // is the game open? + unsigned long LastTime; // last time we heard from this guy + } Game; + struct { + HousesType House; // "ActLike" House of this player + PlayerColorType Color; // Color of this player + HousesType ID; // Actual House of this player + int ProcessTime; // Length of time to process players main loop + } Player; + struct { + unsigned long LastTime; // last time we heard from this guy + unsigned char LastChance; // we're about to remove him from the list + PlayerColorType Color; // chat player's color + } Chat; + }; +} NodeNameType; + + +//........................................................................... +// Packet sent over the serial Global Channel +//........................................................................... +typedef struct { + SerialCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + unsigned char ID; // unique ID of sender of message + union { + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color or SIGNOFF ID + unsigned long MinVersion; // min version this game supports + unsigned long MaxVersion; // max version this game supports + char Scenario[DESCRIP_MAX]; // Scenario name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? + int CheatCheck; // Unique ID of "rules.ini" file. + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long ResponseTime; // packet response time + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Message[MAX_MESSAGE_LENGTH]; // inter-player message + PlayerColorType Color; // player's color or SIGNOFF ID + } Message; + struct { + PlayerColorType Color; // player's color or SIGNOFF ID + } Chat; + }; +} SerialPacketType; + +//........................................................................... +// Other packet sent over the serial global channel (for file transfers) +//........................................................................... +#define MAX_SEND_FILE_PACKET_SIZE MAX_SERIAL_PACKET_SIZE - 64 +typedef struct { + SerialCommandType Command; // Enum defined above. Should be a file transfer enum. + unsigned short BlockNumber; // Index position of this file chunk in the file + unsigned short BlockLength; // Length of data in the RawData buffer + unsigned char RawData [MAX_SEND_FILE_PACKET_SIZE]; +} RemoteFileTransferType; + + +//........................................................................... +// Packet sent over the network Global Channel +//........................................................................... +typedef struct GlobalPacketType { + NetCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + union { + struct { + unsigned int IsOpen : 1; // 1 = game is open for joining + } GameInfo; + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color + unsigned long NameCRC; // CRC of player's game's name + unsigned long MinVersion; // game's min supported version + unsigned long MaxVersion; // game's max supported version + int CheatCheck; // Unique ID of "rules.ini" file. + } PlayerInfo; + struct { + char Scenario[DESCRIP_MAX]; // Scenario Name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario :1;// Is this scenario an official Westwood one? + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long Version; // version # common to all players + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Buf[MAX_MESSAGE_LENGTH]; // inter-user message + PlayerColorType Color; // color of sender of message + unsigned long NameCRC; // CRC of sender's Game Name + } Message; + struct { + int OneWay; // one-way response time + } ResponseTime; + struct { + int Why; // why were we rejected from the game? + } Reject; + struct { + unsigned long ID; // unique ID for this chat node + PlayerColorType Color; // my color + } Chat; + }; +} GlobalPacketType; + + +//........................................................................... +// For finding sync bugs; filled in by the engine when certain conditions +// are met; the pointers allow examination of objects in the debugger. +//........................................................................... +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +/* +** This is the identifier for a multiplayer mission. This can be used to +** identify the filename of the mission as well as display the mission in a +** mission selection list. +*/ +class MultiMission +{ + public: + MultiMission(char const * filename = NULL, char const * description = NULL, char const *digest = NULL, bool official = true, bool expansion = false); + + void Set_Description(char const * description); + void Set_Filename(char const * filename); + void Set_Digest(char const * digest); + void Set_Official(bool official); + void Set_Expansion(bool expansion); + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Description(void) const {return(ScenarioDescription);} + char const * Get_Filename(void) const {return(Filename);} + char const * Get_Digest(void) const {return(Digest);} + bool Get_Official(void) { return (IsOfficial); } + bool Get_Expansion(void) { return (IsExpansion); } // Implied "IsCounterstrike". No longer used. -ajw + + private: + char ScenarioDescription[DESCRIP_MAX]; + char Filename[_MAX_FNAME+_MAX_EXT]; + char Digest[32]; + bool IsOfficial; + bool IsExpansion; +}; + + +typedef struct { + int ScenarioIndex; //Used on host machine only as index into scenario list + int Bases; + int Credits; + int Tiberium; + int Goodies; + int Ghosts; + int UnitCount; + int AIPlayers; // # of AI players allowed to be built + char ScenarioDescription [DESCRIP_MAX]; //Used on client machines only +} GameOptionsType; + +//--------------------------------------------------------------------------- +// Class Definition +//--------------------------------------------------------------------------- +class SessionClass +{ + //------------------------------------------------------------------------ + // Public interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + SessionClass(void); + ~SessionClass(void); + + //..................................................................... + // Initialization + //..................................................................... + void One_Time(void); + void Init(void); + + //..................................................................... + // Reads/writes to the INI file + //..................................................................... + void Read_MultiPlayer_Settings (void); + void Write_MultiPlayer_Settings (void); + void Read_Scenario_Descriptions (void); + void Free_Scenario_Descriptions(void); + + //..................................................................... + // Utility functions + //..................................................................... + int Create_Connections(void); + bool Am_I_Master(void); + unsigned long Compute_Unique_ID(void); + +#if(TEN) + int Create_TEN_Connections(void); +#endif // TEN + +#if(MPATH) + int Create_MPATH_Connections(void); +#endif // MPATH + + //..................................................................... + // File I/O + //..................................................................... + int Save(Pipe & file) const; + int Load(Straw & file); + int Save(CCFileClass & file); + int Load(CCFileClass & file); + + //..................................................................... + // Debugging / Sync Bugs + //..................................................................... + void Trap_Object(void); + + //--------------------------------------------------------------------- + // Public Data + //--------------------------------------------------------------------- + //..................................................................... + // The type of session being played + //..................................................................... + GameType Type; + + //..................................................................... + // The current communications protocol + //..................................................................... + CommProtocolType CommProtocol; + + //..................................................................... + // Game options + //..................................................................... + GameOptionsType Options; + + //..................................................................... + // Unique workstation ID, for detecting my own packets + //..................................................................... + unsigned long UniqueID; + + //..................................................................... + // Player's local options + //..................................................................... + char Handle[MPLAYER_NAME_MAX]; // player name + PlayerColorType PrefColor; // preferred color index + PlayerColorType ColorIdx; // actual color index + HousesType House; // GDI / NOD + int ObiWan; // 1 = player can see all + int Solo; // 1 = player can play alone + + //..................................................................... + // Max allowable # of players & actual # of (human) players + //..................................................................... + int MaxPlayers; + int NumPlayers; + + //..................................................................... + // Frame-sync'ing timing variables + // 'MaxAhead' is the number of frames ahead of this one to execute + // a given packet. It's set by the RESPONSE_TIME event. + // 'FrameSendRate' is the # frames between data packets + //..................................................................... + unsigned long MaxAhead; + unsigned long FrameSendRate; + + int DesiredFrameRate; + + int ProcessTimer; + int ProcessTicks; + int ProcessFrames; + + //..................................................................... + // This flag is set when we've loaded a multiplayer game. + //..................................................................... + int LoadGame; + + //..................................................................... + // This flag is set when the modem game saves the game due to a lost + // connection. + //..................................................................... + int EmergencySave; + + //..................................................................... + // List of scenarios & their file numbers + //..................................................................... + DynamicVectorClass Scenarios; +// DynamicVectorClass Scenarios; +// DynamicVectorClass Filenum; + + char ScenarioFileName[_MAX_FNAME+_MAX_EXT+1]; //File name of scenario to load + + char ScenarioDigest [32]; //Digest of scenario to load + unsigned int ScenarioFileLength; + bool ScenarioIsOfficial; + + char ScenarioRequests[20]; //Which players requested scenario files + int RequestCount; + IPXAddressClass HostAddress; + + //..................................................................... + // This is the multiplayer messaging system + //..................................................................... + MessageListClass Messages; + IPXAddressClass MessageAddress; + char LastMessage[MAX_MESSAGE_LENGTH]; + unsigned WWChat : 1; // 1 = go into special WW Chat mode + + //..................................................................... + // This is the multiplayer scorekeeping system + //..................................................................... + MPlayerScoreType Score[MAX_MULTI_NAMES]; + int GamesPlayed; // # games played this run + int NumScores; // # active entries in MPlayerScore + int Winner; // index of winner of last game + int CurGame; // index of current game being played + + //..................................................................... + // Static arrays + //..................................................................... + static char Descriptions[100][40]; + static int CountMin[2]; + static int CountMax[2]; + static char * GlobalPacketNames[]; + static char * SerialPacketNames[]; + + //..................................................................... + // For Recording & Playing back a file + //..................................................................... + CCFileClass RecordFile; + unsigned Record : 1; + unsigned Play : 1; + unsigned Attract : 1; + + //..................................................................... + // IPX-specific variables + //..................................................................... + int IsBridge; // 1 = we're crossing a bridge + IPXAddressClass BridgeNet; // address of bridge + bool NetStealth; // makes us invisible + bool NetProtect; // keeps others from messaging us + bool NetOpen; // 1 = game is open for joining + char GameName[MPLAYER_NAME_MAX]; // game's name + GlobalPacketType GPacket; // global packet + int GPacketlen; // global packet length + IPXAddressClass GAddress; // address of sender + unsigned short GProductID; // product ID of sender + char MetaPacket[MAX_IPX_PACKET_SIZE]; // packet building buffer + int MetaSize; // size of MetaPacket + DynamicVectorClass Games; // list of games + DynamicVectorClass Players; // list of players + DynamicVectorClass Chat; // list of chat nodes + + //..................................................................... + // Modem-specific variables + //..................................................................... + unsigned ModemService : 1; // 1 = service modem in Call_Back + int CurPhoneIdx; // phone listing index + SerialSettingsType SerialDefaults; // default serial settings + ModemGameType ModemType; // caller or answerer? + + DynamicVectorClass PhoneBook; + DynamicVectorClass InitStrings; + static char * DialMethodCheck[ DIAL_METHODS ]; + static char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + + //..................................................................... + // For finding Sync Bugs + //..................................................................... + long TrapFrame; // frame # to start trapping 'TrapObject' + RTTIType TrapObjType; // type of object to trap + TrapObjectType TrapObject; // ptr to object to trap (watch) + COORDINATE TrapCoord; // coord of object, 0 = ignore + TARGET TrapTarget; // Target # of object, 0 = ignore + CellClass * TrapCell; // Ptr to cell to trap (watch) + int TrapCheckHeap; // true = check the heap as of TrapFrame + long TrapPrintCRC; // Frame # to print CRC state file + +#if(TEN) + // + // TEN-specific variables + // + char *TenPacket; + int TenSize; + int TenMessageAddress; + int TenAddress; + int TenPlayerID; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // TEN + +#if(MPATH) + // + // MPATH-specific variables + // + char *MPathPacket; + int MPathSize; + int MPathMessageAddress; + int MPathAddress; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // MPATH +}; + +#endif // SESSION_H + +/*************************** end of session.h ******************************/ diff --git a/CODE/SHA.CPP b/CODE/SHA.CPP new file mode 100644 index 0000000..4b6e4fa --- /dev/null +++ b/CODE/SHA.CPP @@ -0,0 +1,317 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAEngine::Result -- Fetch the current digest. * + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data blo* + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "sha.h" + + + +#if !defined(__BORLANDC__) && !defined(min) +#define min(a, b) ((a)<(b))?(a):(b) +#endif + + +/*********************************************************************************************** + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data bloc * + * * + * This routine will see if there is a partial block already accumulated in the holding * + * buffer. If so, then the data is fetched from the source such that a full buffer is * + * accumulated and then processed. If there is insufficient data to fill the buffer, then * + * it accumulates what data it can and then returns so that this routine can be called * + * again later. * + * * + * INPUT: data -- Reference to a pointer to the data. This pointer will be modified if * + * this routine consumes any of the data in the buffer. * + * * + * length-- Reference to the length of the data available. If this routine consumes * + * any of the data, then this length value will be modified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Partial(void const * & data, long & length) +{ + if (length == 0 || data == NULL) return; + + /* + ** If there is no partial buffer and the source is greater than + ** a source block size, then partial processing is unnecessary. + ** Bail out in this case. + */ + if (PartialCount == 0 && length >= SRC_BLOCK_SIZE) return; + + /* + ** Attach as many bytes as possible from the source data into + ** the staging buffer. + */ + int add_count = min((int)length, SRC_BLOCK_SIZE - PartialCount); + memcpy(&Partial[PartialCount], data, add_count); + data = ((char const *&)data) + add_count; + PartialCount += add_count; + length -= add_count; + + /* + ** If a full staging buffer has been accumulated, then process + ** the staging buffer and then bail. + */ + if (PartialCount == SRC_BLOCK_SIZE) { + Process_Block(&Partial[0], Acc); + Length += (long)SRC_BLOCK_SIZE; + PartialCount = 0; + } +} + + +/*********************************************************************************************** + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * * + * This is the main access routine to the SHA engine. It will take the arbitrarily long * + * data block and process it. The hash value is accumulated with any previous calls to * + * this routine. * + * * + * INPUT: data -- Pointer to the data block to process. * + * * + * length -- The number of bytes to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Hash(void const * data, long length) +{ + IsCached = false; + + /* + ** Check for and handle any smaller-than-512bit blocks. This can + ** result in all of the source data submitted to this routine to be + ** consumed at this point. + */ + Process_Partial(data, length); + + /* + ** If there is no more source data to process, then bail. Speed reasons. + */ + if (length == 0) return; + + /* + ** First process all the whole blocks available in the source data. + */ + long blocks = (length / SRC_BLOCK_SIZE); + long const * source = (long const *)data; + for (int bcount = 0; bcount < blocks; bcount++) { + Process_Block(source, Acc); + Length += (long)SRC_BLOCK_SIZE; + source += SRC_BLOCK_SIZE/sizeof(long); + length -= (long)SRC_BLOCK_SIZE; + } + + /* + ** Process any remainder bytes. This data is stored in the source + ** accumulator buffer for future processing. + */ + data = source; + Process_Partial(data, length); +} + + +#define Reverse_LONG(a) ((a>>24)&0x000000FFL) | ((a>>8)&0x0000FF00L) | ((a<<8)&0x00FF0000L) | ((a<<24)&0xFF000000L) + + +/*********************************************************************************************** + * SHAEngine::Result -- Fetch the current digest. * + * * + * This routine will return the digest as it currently stands. * + * * + * INPUT: pointer -- Pointer to the buffer that will hold the digest -- 20 bytes. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will always be * + * 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAEngine::Result(void * result) const +{ + /* + ** If the final hash result has already been calculated for the + ** current data state, then immediately return with the precalculated + ** value. + */ + if (IsCached) { + memcpy(result, &FinalResult, sizeof(FinalResult)); + } + + long length = Length + PartialCount; + int partialcount = PartialCount; + char partial[SRC_BLOCK_SIZE]; + memcpy(partial, Partial, sizeof(Partial)); + + /* + ** Cap the end of the source data stream with a 1 bit. + */ + partial[partialcount] = 0x80; + + /* + ** Determine if there is insufficient room to append the + ** data length number to the hash source. If not, then + ** fill out the rest of the accumulator and flush it to + ** the hash so that there will be room for the final + ** count value. + */ + SHADigest acc = Acc; + if ((SRC_BLOCK_SIZE - partialcount) < 9) { + if (partialcount+1 < SRC_BLOCK_SIZE) { + memset(&partial[partialcount+1], '\0', SRC_BLOCK_SIZE - (partialcount+1)); + } + Process_Block(&partial[0], acc); + partialcount = 0; + } else { + partialcount++; + } + + /* + ** Put the length of the source data as a 64 bit integer in the + ** last 8 bytes of the pseudo-source data. + */ + memset(&partial[partialcount], '\0', SRC_BLOCK_SIZE - partialcount); + *(long *)(&partial[SRC_BLOCK_SIZE-4]) = Reverse_LONG((length*8)); + Process_Block(&partial[0], acc); + + memcpy((char *)&FinalResult, &acc, sizeof(acc)); + for (int index = 0; index < sizeof(FinalResult)/sizeof(long); index++) { +// for (int index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + (long &)FinalResult.Long[index] = Reverse_LONG(FinalResult.Long[index]); + } + (bool&)IsCached = true; + memcpy(result, &FinalResult, sizeof(FinalResult)); + return(sizeof(FinalResult)); +} + +/* +** This pragma to turn off the warning "Conversion may lose significant digits" is to +** work around a bug within the Borland compiler. It will give this warning when the +** _rotl() function is called but will NOT give the warning when the _lrotl() function +** is called even though they both have the same parameters and declaration attributes. +*/ +#pragma warn -sig +template +T _rotl(T X, int n) +{ + return(T)( ( X << n ) | ( (unsigned)X >> ((sizeof(T)*8) - n) ) ); +} +//unsigned long _RTLENTRY _rotl(unsigned long X, int n) +//{ +// return(unsigned long)( (unsigned long)( (unsigned long)( (unsigned long)X ) << (int)n ) | (unsigned long)( ((unsigned long) X ) >> ( (int)((int)(sizeof(long)*(long)8) - (long)n) ) ) ); +//} +void memrev(char * buffer, size_t length); + + +/*********************************************************************************************** + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * * + * This helper routine is called when a full block of data is available for processing * + * into the hash. * + * * + * INPUT: source -- Pointer to the block of data to process. * + * * + * acc -- Reference to the hash accumulator that this hash step will be * + * accumulated into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Block(void const * source, SHADigest & acc) const +{ + /* + ** The hash is generated by performing operations on a + ** block of generated/seeded data. + */ + long block[PROC_BLOCK_SIZE/sizeof(long)]; + + /* + ** Expand the source data into a large 80 * 32bit buffer. This is the working + ** data that will be transformed by the secure hash algorithm. + */ + long const * data = (long const *)source; + int index; + for (index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + block[index] = Reverse_LONG(data[index]); + } + + for (index = SRC_BLOCK_SIZE/sizeof(long); index < PROC_BLOCK_SIZE/sizeof(long); index++) { +// block[index] = _rotl(block[(index-3)&15] ^ block[(index-8)&15] ^ block[(index-14)&15] ^ block[(index-16)&15], 1); + block[index] = _rotl(block[index-3] ^ block[index-8] ^ block[index-14] ^ block[index-16], 1); + } + + /* + ** This is the core algorithm of the Secure Hash Algorithm. It is a block + ** transformation of 512 bit source data with a 2560 bit intermediate buffer. + */ + SHADigest alt = acc; + for (index = 0; index < PROC_BLOCK_SIZE/sizeof(long); index++) { + long temp = _rotl(alt.Long[0], 5) + Do_Function(index, alt.Long[1], alt.Long[2], alt.Long[3]) + alt.Long[4] + block[index] + Get_Constant(index); + alt.Long[4] = alt.Long[3]; + alt.Long[3] = alt.Long[2]; + alt.Long[2] = _rotl(alt.Long[1], 30); + alt.Long[1] = alt.Long[0]; + alt.Long[0] = temp; + } + acc.Long[0] += alt.Long[0]; + acc.Long[1] += alt.Long[1]; + acc.Long[2] += alt.Long[2]; + acc.Long[3] += alt.Long[3]; + acc.Long[4] += alt.Long[4]; +} + diff --git a/CODE/SHA.H b/CODE/SHA.H new file mode 100644 index 0000000..7c2218e --- /dev/null +++ b/CODE/SHA.H @@ -0,0 +1,204 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHA.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHA.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHA_H +#define SHA_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include +#include +#include +#include + + +/* +** This implements the Secure Hash Algorithm. It is a cryptographically +** secure hash with no known weaknesses. It generates a 160 bit hash +** result given an arbitrary length data source. +*/ +class SHAEngine +{ + public: + SHAEngine(void) : IsCached(false), Length(0), PartialCount(0) { + Acc.Long[0] = SA; + Acc.Long[1] = SB; + Acc.Long[2] = SC; + Acc.Long[3] = SD; + Acc.Long[4] = SE; + }; + + void Init(void) { + new ((void*)this) SHAEngine; + }; + + // Fetch result as if source data were to stop now. + int Result(void * result) const; + + void Hash(void const * data, long length); + + static int Digest_Size(void) {return(sizeof(SHADigest));} + + private: + + typedef union { + unsigned long Long[5]; + unsigned char Char[20]; + } SHADigest; + + /* + ** This holds the calculated final result. It is cached + ** here to avoid the overhead of recalculating it over + ** multiple sequential requests. + */ + bool IsCached; + SHADigest FinalResult; + + enum { + // These are the initial seeds to the block accumulators. + SA=0x67452301L, + SB=0xefcdab89L, + SC=0x98badcfeL, + SD=0x10325476L, + SE=0xc3d2e1f0L, + + // These are the constants used in the block transformation. + K1=0x5a827999L, // t=0..19 2^(1/2)/4 + K2=0x6ed9eba1L, // t=20..39 3^(1/2)/4 + K3=0x8f1bbcdcL, // t=40..59 5^(1/2)/4 + K4=0xca62c1d6L, // t=60..79 10^(1/2)/4 + + // Source data is grouped into blocks of this size. + SRC_BLOCK_SIZE=16*sizeof(long), + + // Internal processing data is grouped into blocks this size. + PROC_BLOCK_SIZE=80*sizeof(long) + }; + + long Get_Constant(int index) const { + if (index < 20) return K1; + if (index < 40) return K2; + if (index < 60) return K3; + return K4; + }; + + // Used for 0..19 + long Function1(long X, long Y, long Z) const { + return(Z ^ ( X & ( Y ^ Z ) ) ); + }; + + // Used for 20..39 + long Function2(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + // Used for 40..59 + long Function3(long X, long Y, long Z) const { + return( (X & Y) | (Z & (X | Y) ) ); + }; + + // Used for 60..79 + long Function4(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + long Do_Function(int index, long X, long Y, long Z) const { + if (index < 20) return Function1(X, Y, Z); + if (index < 40) return Function2(X, Y, Z); + if (index < 60) return Function3(X, Y, Z); + return Function4(X, Y, Z); + }; + + // Process a full source data block. + void Process_Block(void const * source, SHADigest & acc) const; + + // Processes a partially filled source accumulator buffer. + void Process_Partial(void const * & data, long & length); + + /* + ** This is the running accumulator values. These values + ** are updated by a block processing step that occurs + ** every 512 bits of source data. + */ + SHADigest Acc; + + /* + ** This is the running length of the source data + ** processed so far. This total is used to modify the + ** resulting hash value as if it were appended to the end + ** of the source data. + */ + long Length; + + /* + ** This holds any partial source block. Partial source blocks are + ** a consequence of submitting less than block sized data chunks + ** to the SHA Engine. + */ + int PartialCount; + char Partial[SRC_BLOCK_SIZE]; +}; + + +#define SHA_SOURCE1 "abc" +#define SHA_DIGEST1a "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78\x50\xC2\x6C\x9C\xD0\xD8\x9D" +#define SHA_DIGEST1b "\x01\x64\xB8\xA9\x14\xCD\x2A\x5E\x74\xC4\xF7\xFF\x08\x2C\x4D\x97\xF1\xED\xF8\x80" + + +#define SHA_SOURCE2 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +#define SHA_DIGEST2a "\x84\x98\x3E\x44\x1C\x3B\xD2\x6E\xBA\xAE\x4A\xA1\xF9\x51\x29\xE5\xE5\x46\x70\xF1" +#define SHA_DIGEST2b "\xD2\x51\x6E\xE1\xAC\xFA\x5B\xAF\x33\xDF\xC1\xC4\x71\xE4\x38\x44\x9E\xF1\x34\xC8" + +#define SHA_SOURCE3 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define SHA_DIGEST3a "\x34\xAA\x97\x3C\xD4\xC4\xDA\xA4\xF6\x1E\xEB\x2B\xDB\xAD\x27\x31\x65\x34\x01\x6F" +#define SHA_DIGEST3b "\x32\x32\xAF\xFA\x48\x62\x8A\x26\x65\x3B\x5A\xAA\x44\x54\x1F\xD9\x0D\x69\x06\x03" + +#endif diff --git a/CODE/SHAPEBTN.CPP b/CODE/SHAPEBTN.CPP new file mode 100644 index 0000000..0250448 --- /dev/null +++ b/CODE/SHAPEBTN.CPP @@ -0,0 +1,182 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHAPEBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHAPEBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "shapebtn.h" + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Default Constructor for a shape type button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must call Set_Shape() before using a button constructed with this function, * + * and you must set X & Y, and ID. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0), + ReflectButtonState(false) +{ +} + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * * + * This is the normal constructor for a shape type button. Shape buttons are ones that * + * have their imagery controlled by a shape file. The various states of the button are * + * given a visual form as one of these shapes. Button dimensions are controlled by the * + * first shape. * + * * + * INPUT: id -- The button ID. * + * * + * shape -- Pointer to the shape file that controls the button's display. * + * * + * x,y -- The pixel coordinate of the upper left corner of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The width and height of the shape is controlled by the first shape in the * + * shape file provided. This means that all the shapes in the shape file must be * + * the same size. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(unsigned id, void const * shape, int x, int y) : + ToggleClass(id, x, y, 0, 0), + ReflectButtonState(false) +{ +// Width = 0; +// Height = 0; + Set_Shape(shape); +} + + +/*********************************************************************************************** + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * * + * This routine will assign the specified shape to this shape object. * + * * + * INPUT: data -- Pointer to the shape to assign. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void ShapeButtonClass::Set_Shape(void const * data) +{ + ShapeData = data; + if (ShapeData) { + Width = Get_Build_Frame_Width(ShapeData); + Height = Get_Build_Frame_Height(ShapeData); + } +} + + +/*********************************************************************************************** + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * * + * This function is called when the button detects that it must be redrawn. The actual * + * shape to use is controled by the button's state and the shape file provided when then * + * button was constructed. * + * * + * INPUT: forced -- Should the button be redrawn regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the shape redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ShapeButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced) && ShapeData) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the body & set text color. + */ + int shapenum = 0; + if (IsDisabled) { + shapenum = DISABLED_SHAPE; + } else { + + if (!ReflectButtonState) { + + if (IsPressed) { + shapenum = DOWN_SHAPE; + } else { + shapenum = UP_SHAPE; + } + } else { + shapenum = IsOn; + } + + } + CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + + diff --git a/CODE/SHAPEBTN.H b/CODE/SHAPEBTN.H new file mode 100644 index 0000000..45f1cd1 --- /dev/null +++ b/CODE/SHAPEBTN.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHAPEBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHAPEBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SHAPEBTN_H +#define SHAPEBTN_H + +#include "toggle.h" + +class ShapeButtonClass : public ToggleClass +{ + public: + ShapeButtonClass(void); + ShapeButtonClass(unsigned id, void const * shapes, int x, int y); + virtual int Draw_Me(int forced=false); + virtual void Set_Shape(void const * data); + void const * Get_Shape_Data(void) {return(ShapeData);}; + + enum ShapeButtonClassEnums { + UP_SHAPE, // Shape to use when button is "up". + DOWN_SHAPE, // Shape to use when button is "down". + DISABLED_SHAPE // Shape to use when button is disabled. + }; + + unsigned ReflectButtonState:1; + + protected: + + /* + ** This points to the shape data file. This file contains the appropriate shapes + ** for this button in the offsets specified above. + */ + void const * ShapeData; +}; +#endif diff --git a/CODE/SHAPIPE.CPP b/CODE/SHAPIPE.CPP new file mode 100644 index 0000000..3efeb5e --- /dev/null +++ b/CODE/SHAPIPE.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHAPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHAPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAPipe::Result -- Fetches the current SHA value. * + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shapipe.h" + + +/*********************************************************************************************** + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * * + * This pipe segment will not modify the data, but it will examine the data and use it when * + * building a SHA digest. * + * * + * INPUT: source -- Pointer to the data to flow through the pipe. * + * * + * length -- The number of bytes to submit. * + * * + * OUTPUT: Returns with the actual number of bytes output at the distant final end of the * + * pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Put(void const * source, int slen) +{ + SHA.Hash(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * SHAPipe::Result -- Fetches the current SHA value. * + * * + * This routine will return the SHA digest for the data that has passed through this * + * link in the pipe chain. It is a non-destructive read. * + * * + * INPUT: result -- Pointer to the buffer to hold the SHA digest. This buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Result(void * result) const +{ + return(SHA.Result(result)); +} + + diff --git a/CODE/SHAPIPE.H b/CODE/SHAPIPE.H new file mode 100644 index 0000000..d43f9fe --- /dev/null +++ b/CODE/SHAPIPE.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHAPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHAPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHAPIPE_H +#define SHAPIPE_H + +#include "pipe.h" +#include "sha.h" + +/* +** This class serves as a pipe that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAPipe : public Pipe +{ + public: + SHAPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAPipe(SHAPipe & rvalue); + SHAPipe & operator = (SHAPipe const & pipe); +}; + +#endif diff --git a/CODE/SHASTRAW.CPP b/CODE/SHASTRAW.CPP new file mode 100644 index 0000000..b973521 --- /dev/null +++ b/CODE/SHASTRAW.CPP @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHASTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHASTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * SHAStraw::Result -- Fetches the current SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shastraw.h" + + +/*********************************************************************************************** + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * * + * This routine will fetch the requested data and as it passes through this straw it will * + * submit it to the SHA processor. The data that passes through is unmodified by this * + * straw segment. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The length of the data requested. * + * * + * OUTPUT: Returns with the number of bytes stored in the buffer. If this number is less * + * than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + SHA.Hash(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * SHAStraw::Result -- Fetches the current SHA digest. * + * * + * Use this routine to fetch the current SHA digest from the straw. It will return the * + * digest of the data that has passed through this straw segment. * + * * + * INPUT: result -- Pointer to the buffer to hold the message digest. The buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes stored into the digest buffer. This will always * + * be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Result(void * result) const +{ + return(SHA.Result(result)); +} diff --git a/CODE/SHASTRAW.H b/CODE/SHASTRAW.H new file mode 100644 index 0000000..03e68c3 --- /dev/null +++ b/CODE/SHASTRAW.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SHASTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SHASTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHASTRAW_H +#define SHASTRAW_H + + +#include "straw.h" +#include "sha.h" + +/* +** This class serves as a straw that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAStraw : public Straw +{ + public: + SHAStraw(void) {} + virtual int Get(void * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAStraw(SHAStraw & rvalue); + SHAStraw & operator = (SHAStraw const & straw); +}; + + +#endif diff --git a/CODE/SIDEBAR.CPP b/CODE/SIDEBAR.CPP new file mode 100644 index 0000000..89c97ed --- /dev/null +++ b/CODE/SIDEBAR.CPP @@ -0,0 +1,2432 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/SIDEBAR.CPP 2 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SIDEBAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * SidebarClass::Activate -- Controls the sidebar activation. * + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * SidebarClass::Draw_It -- Renders the sidebar display. * + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void * SidebarClass::SidebarShape = NULL; +void * SidebarClass::SidebarMiddleShape = NULL; +void * SidebarClass::SidebarBottomShape = NULL; + + +/*************************************************************************** +** This holds the translucent table for use with the construction clock +** animation. +*/ +char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; + + +/*************************************************************************** +** This points to the main sidebar shapes. These include the upgrade and +** repair buttons. +*/ +//TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; + +typedef enum ButtonNumberType { + BUTTON_RADAR = 100, + BUTTON_REPAIR, + BUTTON_DEMOLISH, + BUTTON_UPGRADE, + BUTTON_SELECT, + BUTTON_ZOOM +} ButtonNumberType; + +/* +** Sidebar buttons +*/ +SidebarClass::SBGadgetClass SidebarClass::Background; +ShapeButtonClass SidebarClass::Repair; +ShapeButtonClass SidebarClass::Upgrade; +ShapeButtonClass SidebarClass::Zoom; +ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; +ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; +SidebarClass::StripClass::SelectClass +SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; + +/* +** Shape data pointers +*/ +void * SidebarClass::StripClass::LogoShapes = NULL; +void const * SidebarClass::StripClass::ClockShapes; +void const * SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(void) : + IsSidebarActive(false), + IsToRedraw(true), + IsRepairActive(false), + IsUpgradeActive(false), + IsDemolishActive(false) +{ + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = (SIDE_X+8); + WindowList[WINDOW_SIDEBAR][WINDOWY] = SIDE_Y + 1 + TOP_HEIGHT; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = SIDE_WIDTH; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT; +// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ + new (&Column[0]) StripClass(InitClass()); + new (&Column[1]) StripClass(InitClass()); + + Column[0].X = COLUMN_ONE_X * RESFACTOR; + Column[0].Y = COLUMN_ONE_Y * RESFACTOR; + Column[1].X = COLUMN_TWO_X * RESFACTOR; + Column[1].Y = COLUMN_TWO_Y * RESFACTOR; +} + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * * + * Unlike the normal constructor, this one doesn't do any initialization. There is one * + * exception to this. The stip classes can't call an explicit NoInitClass constructor * + * since they are an array. Since the default constructor is called for these strips, we * + * must reset the X and Y location to what we know they should be. * + * * + * INPUT: flag to indicate that this is a no initialization constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(NoInitClass const & x) : PowerClass(x) +{ + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; +} + + +/*********************************************************************************************** + * SidebarClass::One_Time -- Handles the one time game initializations. * + * * + * This routine is used to load the graphic data that is needed by the sidebar display. It * + * should only be called ONCE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once when the game first starts. * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::One_Time(void) +{ + PowerClass::One_Time(); + + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X+8)) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWY] = (SIDE_Y + 1 + TOP_HEIGHT) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT) * RESFACTOR; +// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; + + /* + ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM + */ + WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1*RESFACTOR; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; + Column[0].One_Time(0); + Column[1].One_Time(1); + + /* + ** Load the sidebar shape in at this time. (Hi-Res sidebar is theater dependant) + */ + if (SidebarShape == NULL) { + SidebarShape = (void*)MFCD::Retrieve("SIDEBAR.SHP"); + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Clear(void) +{ + + PowerClass::Init_Clear(); + + IsToRedraw = true; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + Activate(false); +} + + +/*********************************************************************************************** + * SidebarClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_IO(void) +{ + PowerClass::Init_IO(); + + /* + ** Add the sidebar's buttons only if we're not in editor mode. + */ + if (!Debug_Map) { + + Repair.IsSticky = true; + Repair.ID = BUTTON_REPAIR; + Repair.X = (0x1f2/2)*RESFACTOR; + Repair.Y = (0x96/2)*RESFACTOR; + Repair.IsPressed = false; + Repair.IsToggleType = true; + Repair.ReflectButtonState = true; + Repair.Set_Shape(MFCD::Retrieve("REPAIR.SHP")); + + Upgrade.IsSticky = true; + Upgrade.ID = BUTTON_UPGRADE; +#ifdef WIN32 + Upgrade.X = 0x21f; +#else + Upgrade.X = ((0x21f/2)+1)*RESFACTOR; +#endif + Upgrade.Y = (0x96/2)*RESFACTOR; + Upgrade.IsPressed = false; + Upgrade.IsToggleType = true; + Upgrade.ReflectButtonState = true; + Upgrade.Set_Shape(MFCD::Retrieve("SELL.SHP")); + + Zoom.IsSticky = true; + Zoom.ID = BUTTON_ZOOM; + Zoom.X = (0x24c/2)*RESFACTOR; + Zoom.Y = (0x96/2)*RESFACTOR; + Zoom.IsPressed = false; + Zoom.Set_Shape(MFCD::Retrieve("MAP.SHP")); + + if ((IsRadarActive && Is_Zoomable()) || Session.Type != GAME_NORMAL) { + Zoom.Enable(); + } else { + Zoom.Disable(); + } + Column[0].Init_IO(0); + Column[1].Init_IO(1); + + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + if (IsSidebarActive) { + IsSidebarActive = false; + Activate(1); +// Background.Zap(); +// Add_A_Button(Background); + } + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * + * the data that is loaded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Theater(TheaterType theater) +{ + Reload_Sidebar(); + + PowerClass::Init_Theater(theater); + + Column[0].Init_Theater(theater); + Column[1].Init_Theater(theater); +} + +/*********************************************************************************************** + * SidebarClass::Reload_Sidebar -- Loads appropriate sidebar shapes depending on house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/18/1996 BWG : Created. * + *=============================================================================================*/ +void SidebarClass::Reload_Sidebar(void) +{ + static char * sidebarnames[]={ + "SIDE?NA.SHP", //NATO + "SIDE?NA.SHP", + "SIDE?US.SHP", //USSR + "SIDE?NA.SHP", + "SIDE?US.SHP", //UKRAINE + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", //HOUSE_GOOD + "SIDE?US.SHP" //HOUSE_BAD + }; + int houseloaded = 0; + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } + + char * sidename = sidebarnames[houseloaded]; + *(sidename+4) = '1'; + SidebarShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '2'; + SidebarMiddleShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '3'; + SidebarBottomShape = (void*)MFCD::Retrieve(sidename); + + Column[0].Reload_LogoShapes(); + Column[1].Reload_LogoShapes(); +} + +/*********************************************************************************************** + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) +{ + assert((unsigned)type < RTTI_COUNT); + assert(id >= 0); + + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + +/*********************************************************************************************** + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * * + * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * + * to be refreshed as well. If the special code to refresh the sidebar was found, it * + * flags the sidebar to be redrawn and then removes the code from the list. * + * * + * INPUT: cell -- The cell to base the refresh list on. * + * * + * list -- Pointer to the cell offset list that elaborates all the cells that * + * need to be flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Column[0].IsToRedraw = true; + Column[1].IsToRedraw = true; + Flag_To_Redraw(false); + } + PowerClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * * + * Use this routine to turn the repair sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure is friendly and damaged. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Repair(int control) +{ + bool old = IsRepairActive; + + if (control == -1) { + control = IsRepairActive ? 0 : 1; + } + switch (control) { + case 1: + IsRepairActive = true; + break; + + default: + case 0: + IsRepairActive = false; + break; + } + if (old != IsRepairActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + + if (!IsRepairActive) { + Help_Text(TXT_NONE); + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * * + * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure can be upgraded and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Upgrade(int control) +{ + bool old = IsUpgradeActive; + if (control == -1) { + control = IsUpgradeActive ? 0 : 1; + } + switch (control) { + case 1: + IsUpgradeActive = true; + break; + + default: + case 0: + IsUpgradeActive = false; + break; + } + if (old != IsUpgradeActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsUpgradeActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * * + * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * + * the button is enabled when a friendly building is selected and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Demolish(int control) +{ + bool old = IsDemolishActive; + + if (control == -1) { + control = IsDemolishActive ? 0 : 1; + } + switch (control) { + case 1: + IsDemolishActive = true; + break; + + default: + case 0: + IsDemolishActive = false; + break; + } + if (old != IsDemolishActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsDemolishActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Add(RTTIType type, int id) +{ + assert((unsigned)type < RTTI_COUNT); + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + int column = Which_Column(type); + + if (Column[column].Add(type, id)) { + Activate(1); + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * * + * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * + * a building is selected that can produce units. If the number of units to produce is * + * greater than what the sidebar can hold, this routine is used to scroll the other object * + * into view so they can be selected. * + * * + * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * + * later in the list of objects. * + * * + * OUTPUT: bool; Did scrolling occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Scroll(bool up, int column) +{ + if (column == -1) { + bool scr = false; + scr |= Column[0].Scroll(up); + scr |= Column[1].Scroll(up); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + if (scr) { + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + if (Column[column].Scroll(up)) { + // No need to redraw the whole sidebar juts because we scrolled a strip is there? ST - 10/15/96 7:29PM + //IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Draw_It -- Renders the sidebar display. * + * * + * This routine performs the actual drawing of the sidebar display. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the sidebar imagery changed at all? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * + *=============================================================================================*/ +void SidebarClass::Draw_It(bool complete) +{ + PowerClass::Draw_It(complete); + + BStart(BENCH_SIDEBAR); + + if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { + IsToRedraw = false; + + if (LogicPage->Lock()) { + /* + ** Draw the outline box around the sidebar buttons. + */ + int shape = complete ? 0 : 1; + + /* + ** The sidebar shape is too big in 640x400 so it needs to be drawn in three chunks. + */ + CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, (8+80+50)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + LogicPage->Unlock(); + } + } + + /* + ** Draw the side strip elements by calling their respective draw functions. + */ + if (IsSidebarActive) { + Column[0].Draw_It(complete); + Column[1].Draw_It(complete); + + if (complete || IsToRedraw) { + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + } + } + IsToRedraw = false; + + BEnd(BENCH_SIDEBAR); +} + + +/*********************************************************************************************** + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarClass::AI(KeyNumType & input, int x, int y) +{ + bool redraw = false; + + /* + ** Toggle the sidebar in and out with the key. + */ +#ifndef WIN32 + if (input == KN_TAB) { + Activate(-1); + } +#else + if (!Debug_Map) { + Activate(1); // Force the sidebar always on in Win95 mode + } +#endif //WIN32 + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } + +#ifdef NEVER + if (IsSidebarActive && !Debug_Map) { + + if (input == KN_DOWN) { + int scr = 0; + scr |= Column[0].Scroll(false); + scr |= Column[1].Scroll(false); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + if (input == KN_UP) { + int scr = 0; + scr |= Column[0].Scroll(true); + scr |= Column[1].Scroll(true); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + } +#endif + + + if (IsSidebarActive) { + + /* + ** If there are any buildings in the payer's inventory, then allow the repair + ** option. + */ + if (PlayerPtr->BScan) { + Activate_Repair(true); + } else { + Activate_Repair(false); + } + + if (input == (BUTTON_REPAIR|KN_BUTTON)) { + Repair_Mode_Control(-1); + } + + if (input == (BUTTON_ZOOM|KN_BUTTON)) { + Zoom_Mode_Control(); + } + + if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + Sell_Mode_Control(-1); + } + + if (redraw) { + //IsToRedraw = true; + Column[0].Flag_To_Redraw(); + Column[1].Flag_To_Redraw(); + + Flag_To_Redraw(false); + } + } + + if ((!IsRepairMode) && Repair.IsOn) { + Repair.Turn_Off(); + } + + if ((!IsSellMode) && Upgrade.IsOn) { + Upgrade.Turn_Off(); + } + + PowerClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Recalc(void) +{ + bool redraw = false; + + redraw |= Column[0].Recalc(); + redraw |= Column[1].Recalc(); + + if (redraw) { + IsToRedraw = true; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * SidebarClass::Activate -- Controls the sidebar activation. * + * * + * Use this routine to turn the sidebar on or off. This routine handles updating the * + * necessary flags. * + * * + * INPUT: control -- Tells what to do with the sidebar according to the following: * + * 0 = Turn sidebar off. * + * 1 = Turn sidebar on. * + * -1= Toggle sidebar on or off. * + * * + * OUTPUT: bool; Was the sidebar already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate(int control) +{ + bool old = IsSidebarActive; + + if (Session.Play) + return (old); + + /* + ** Determine the new state of the sidebar. + */ + switch (control) { + case -1: + IsSidebarActive = IsSidebarActive == false; + break; + + case 1: + IsSidebarActive = true; + break; + + default: + case 0: + IsSidebarActive = false; + break; + } + + /* + ** Only if there is a change in the state of the sidebar will anything + ** be done to change it. + */ + if (IsSidebarActive != old) { + + /* + ** If the sidebar is activated but was on the right side of the screen, then + ** activate it on the left side of the screen. + */ + if (IsSidebarActive /*&& X*/) { + Set_View_Dimensions(0, 8 * RESFACTOR, ((320-SIDE_WIDTH)/ICON_PIXEL_W) * RESFACTOR); + IsToRedraw = true; + Help_Text(TXT_NONE); + Repair.Zap(); + Add_A_Button(Repair); + Upgrade.Zap(); + Add_A_Button(Upgrade); + Zoom.Zap(); + Add_A_Button(Zoom); + Column[0].Activate(); + Column[1].Activate(); + Background.Zap(); + Add_A_Button(Background); + Map.RadarButton.Zap(); + Add_A_Button(Map.RadarButton); + Map.PowerButton.Zap(); + Add_A_Button(Map.PowerButton); + } else { + Help_Text(TXT_NONE); + Set_View_Dimensions(0, 8 * RESFACTOR); + Remove_A_Button(Repair); + Remove_A_Button(Upgrade); + Remove_A_Button(Zoom); + Remove_A_Button(Background); + Column[0].Deactivate(); + Column[1].Deactivate(); + Remove_A_Button(Map.RadarButton); + Remove_A_Button(Map.PowerButton); + } + + /* + ** Since the sidebar status has changed, update the map so that the graphics + ** will be rendered correctly. + */ + Flag_To_Redraw(true); + } + + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::StripClass(InitClass const & ) : + X(0), + Y(0), + ID(0), + IsToRedraw(true), + IsBuilding(false), + IsScrollingDown(false), + IsScrolling(false), + Flasher(-1), + TopIndex(0), + Scroller(0), + Slid(0), + BuildableCount(0) +{ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * + * * + * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * + * the shape files it needs for rendering. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::One_Time(int ) +{ + /* + ** Sidebar is player team specific in Hires + */ + ClockShapes = MFCD::Retrieve("CLOCK.SHP"); + + for (SpecialWeaponType lp = SPC_FIRST; lp < SPC_COUNT; lp++) { + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", SpecialWeaponFile[lp]); + + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + SpecialShapes[lp] = MFCD::Retrieve(fullname); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * * + * This routine will return with a pointer to the cameo data for the special objects that * + * can appear on the sidebar (e.g., nuclear bomb). * + * * + * INPUT: type -- The special type to fetch the cameo imagery for. * + * * + * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : commented * + *=============================================================================================*/ +void const * SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) +{ + if ((unsigned)type < SPC_COUNT) { + return(SpecialShapes[type]); + } + return(0); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Clear(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; + Flasher = -1; + TopIndex = 0; + Slid = 0; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * + * * + * This routine doesn't actually add any buttons to the list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_IO(int id) +{ + ID = id; + + UpButton[ID].IsSticky = true; + UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].X = X+(UP_X_OFFSET * RESFACTOR); + UpButton[ID].Y = Y+(UP_Y_OFFSET * RESFACTOR); + +#if (FRENCH) +#ifdef WIN32 + UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); +#else + UpButton[ID].Set_Shape(MFCD::Retrieve("STUP_FIX.SHP")); +#endif +#else //FRENCH + UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); +#endif //FRENCH + + DownButton[ID].IsSticky = true; + DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].X = X+(DOWN_X_OFFSET * RESFACTOR); + DownButton[ID].Y = Y+(DOWN_Y_OFFSET * RESFACTOR); + + /* + ** Buttons are in a slightly different position in the new sidebar + */ + UpButton[ID].Y--; + DownButton[ID].Y--; + + DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectClass & g = SelectButton[ID][index]; + g.ID = BUTTON_SELECT; + g.X = X; + g.Y = Y + ((OBJECT_HEIGHT*index) * RESFACTOR); + g.Width = OBJECT_WIDTH * RESFACTOR; + g.Height = OBJECT_HEIGHT * RESFACTOR; + g.Set_Owner(*this, index); + } + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Theater(TheaterType theater) +{ + + Reload_LogoShapes(); + + if ( (theater != THEATER_NONE) && (theater != ::LastTheater)) { + + static TLucentType const ClockCols[1] = { + {GREEN, BLACK, 100, 0} +// {GREEN, LTGREY, 180, 0} + }; + + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + PaletteClass pal = OriginalPalette; + memset(&pal[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + Build_Translucent_Table(pal, &ClockCols[0], 1, (void*)ClockTranslucentTable); + +// Mem_Copy(GamePalette, OriginalPalette, 768); +// memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + + /* + ** Create the translucent table used for the sidebar. + */ +// Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); +// GamePalette = OriginalPalette; + + Conquer_Build_Fading_Table(GamePalette, &ClockTranslucentTable[256], BLACK, 100); + } +} + +void SidebarClass::StripClass::Reload_LogoShapes(void) +{ + /* + ** Load hi-res strip art here since it is player side specific + */ + static char * stripnames[]={ + "stripna.shp", //Nato + "stripna.shp", + "stripus.shp", //USSR + "stripna.shp", + "stripus.shp", //UKRAINE + "stripna.shp", + "stripna.shp", + "stripna.shp", + "stripna.shp", //HOUSE_GOOD + "stripus.shp", //HOUSE_BAD + }; + int houseloaded = 0; + + /* + ** Sidebar art is dependent on the side of the player + */ + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } + LogoShapes = (void*)MFCD::Retrieve(stripnames[houseloaded]); +} + +/*********************************************************************************************** + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * * + * This routine will add the side strip buttons to the map's input system. This routine * + * should be called once when the sidebar activates. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine a second time without first calling Deactivate(). * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Activate(void) +{ + UpButton[ID].Zap(); + Map.Add_A_Button(UpButton[ID]); + + DownButton[ID].Zap(); + Map.Add_A_Button(DownButton[ID]); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectButton[ID][index].Zap(); + Map.Add_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * + * * + * Call this routine to remove all the buttons on the side strip from the map's input * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine unless the Activate() function was previously called. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Deactivate(void) +{ + Map.Remove_A_Button(UpButton[ID]); + Map.Remove_A_Button(DownButton[ID]); + for (int index = 0; index < MAX_VISIBLE; index++) { + Map.Remove_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Add(RTTIType type, int id) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + BuildableCount++; + IsToRedraw = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * * + * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * + * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * + * the AI function and the Draw_It function are required to properly give the appearance * + * of scrolling. * + * * + * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * + * * + * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 07/29/1995 JLB : Simplified scrolling logic. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Scroll(bool up) +{ + if (up) { + if (!TopIndex) return(false); + Scroller--; + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) return(false); + Scroller++; + } + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * * + * This utility routine is called when something changes on the sidebar and it must be * + * reflected the next time drawing is performed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Flag_To_Redraw(void) +{ + IsToRedraw = true; + //Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +{ + bool redraw = false; + + /* + ** If this is scroll button for this side strip, then scroll the strip as + ** indicated. + */ + if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + UpButton[ID].IsPressed = false; + if (!Scroll(true)) { + Sound_Effect(VOC_SCOLD); + } + } + if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + DownButton[ID].IsPressed = false; + if (!Scroll(false)) { + Sound_Effect(VOC_SCOLD); + } + } + + /* + ** Reflect the scroll desired direction/value into the scroll + ** logic handler. This might result in up or down scrolling. + */ + if (!IsScrolling && Scroller) { + if (BuildableCount <= MAX_VISIBLE) { + Scroller = 0; + } else { + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (Scroller < 0) { + if (!TopIndex) { + Scroller = 0; + } else { + Scroller++; + IsScrollingDown = false; + IsScrolling = true; + TopIndex--; + Slid = 0; + } + + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) { + Scroller = 0; + } else { + Scroller--; + Slid = OBJECT_HEIGHT; + IsScrollingDown = true; + IsScrolling = true; + } + } + } + } + + /* + ** Scroll logic is handled here. + */ + if (IsScrolling) { + if (IsScrollingDown) { + Slid -= SCROLL_RATE; + if (Slid <= 0) { + IsScrolling = false; + Slid = 0; + TopIndex++; + } + } else { + Slid += SCROLL_RATE; + if (Slid >= OBJECT_HEIGHT) { + IsScrolling = false; + Slid = 0; + } + } + redraw = true; + } + + /* + ** Handle any flashing logic. Flashing occurs when the player selects an object + ** and provides the visual feedback of a recognized and legal selection. + */ + if (Flasher != -1) { + if (Graphic_Logic()) { + redraw = true; + if (Fetch_Stage() >= 7) { + Set_Rate(0); + Set_Stage(0); + Flasher = -1; + } + } + } + + /* + ** Handle any building clock animation logic. + */ + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && factory->Has_Changed()) { + redraw = true; + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + switch (pending->What_Am_I()) { + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + Speak(VOX_UNIT_READY); + break; + + case RTTI_BUILDING: + Speak(VOX_CONSTRUCTION); + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + Speak(VOX_UNIT_READY); + break; + } + } + } + } + } + } + } + + /* + ** If any of the logic determined that this side strip needs to be redrawn, then + ** set the redraw flag for this side strip. + */ + if (redraw) { + Flag_To_Redraw(); + } + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * * + * Use this routine to render the sidebar display. It checks to see if it needs to be * + * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * + * will force redraw the entire strip. * + * * + * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * + * flag. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 08/06/1995 JLB : Handles multi factory tracking in same strip. * + *=============================================================================================*/ +void SidebarClass::StripClass::Draw_It(bool complete) +{ + if (IsToRedraw || complete) { + IsToRedraw = false; + + SidebarRedraws++; + + /* + ** Fills the background to the side strip. We shouldnt need to do this if the strip + ** has a full complement of icons. + */ + /* + ** New sidebar needs to be drawn not filled + */ + if (BuildableCount < MAX_VISIBLE) { + CC_Draw_Shape(LogoShapes, ID, X+(2*RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + } + + /* + ** Redraw the scroll buttons. + */ + UpButton[ID].Draw_Me(true); + DownButton[ID].Draw_Me(true); + + /* + ** Loop through all the buildable objects that are visible in the strip and render + ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. + */ + for (int i = 0; i < MAX_VISIBLE + (IsScrolling ? 1 : 0); i++) { + bool production; + bool completed; + int stage; + bool darken = false; + void const * shapefile = 0; + int shapenum = 0; + void const * remapper = 0; + FactoryClass * factory = 0; + int index = i+TopIndex; + int x = X; + int y = Y + (i*OBJECT_HEIGHT * RESFACTOR); + + /* + ** If the strip is scrolling, then the offset is adjusted accordingly. + */ + if (IsScrolling) { + y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; +// y -= OBJECT_HEIGHT - Slid; + } + + /* + ** Fetch the shape number for the object type located at this current working + ** slot. This shape pointer is used to draw the underlying graphic there. + */ + if ((unsigned)index < BuildableCount) { + ObjectTypeClass const * obj = NULL; + SpecialWeaponType spc = SPC_NONE; + + if (Buildables[index].BuildableType != RTTI_SPECIAL) { + + obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (obj != NULL) { + + /* + ** Fetch the remap table that is appropriate for this object + ** type. + */ + remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const *)obj)->Remap); + + /* + ** If there is already a factory producing this kind of object, then all + ** objects of this type are displays in a disabled state. + */ + bool isbusy = (PlayerPtr->Fetch_Factory(Buildables[index].BuildableType) != NULL); + if (!isbusy && PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID)) { + isbusy = true; + } + + /* + ** Infantry don't get remapped in the sidebar (special case). + */ + if (Buildables[index].BuildableType == RTTI_INFANTRYTYPE) { + remapper = 0; + } + + shapefile = obj->Get_Cameo_Data(); + shapenum = 0; + if (Buildables[index].Factory != -1) { + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } else { + production = false; +// darken = IsBuilding; + + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ + darken = isbusy; + } + } else { + darken = PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID); + } + + } else { + + spc = SpecialWeaponType(Buildables[index].BuildableID); + shapefile = Get_Special_Cameo(spc); + shapenum = 0; + + production = true; + completed = PlayerPtr->SuperWeapon[spc].Is_Ready(); + stage = PlayerPtr->SuperWeapon[spc].Anim_Stage(); + darken = false; + } + + if (obj != NULL || spc != SPC_NONE) { + /* + ** If this item is flashing then take care of it. + ** + */ + if (Flasher == index && (Fetch_Stage() & 0x01)) { + remapper = Map.FadingLight; + } + + } else { + shapefile = LogoShapes; + if (!darken) { + shapenum = SB_BLANK; + } + } + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; + } + + remapper = 0; + /* + ** Now that the shape of the object at the current working slot has been found, + ** draw it and any graphic overlays as necessary. + ** + ** Don't draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM + */ + if (shapenum != SB_BLANK || shapefile != LogoShapes) { + CC_Draw_Shape(shapefile, shapenum, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + remapper); + + /* + ** Darken this object because it cannot be produced or is otherwise + ** unavailable. + */ + if (darken) { + CC_Draw_Shape(ClockShapes, 0, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + } + } + + /* + ** Draw the overlapping clock shape if this is object is being constructed. + ** If the object is completed, then display "Ready" with no clock shape. + */ + if (production) { + if (completed) { + + /* + ** Display text showing that the object is ready to place. + */ + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]))+(LEFT_EDGE_OFFSET+15) * RESFACTOR, + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } else { + + CC_Draw_Shape(ClockShapes, stage+1, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + + /* + ** Display text showing that the construction is temporarily on hold. + */ + if (factory && !factory->Is_Building()) { + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET+15) * RESFACTOR), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } + } + } + + } + + LastSlid = Slid; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + bool redraw = false; + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech != NULL) { + ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; + } else { + if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { + ok = PlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); + } else { + ok = false; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + TopIndex = 0; + IsToRedraw = true; + redraw = true; + BuildableCount--; + index--; + } + } + +#ifdef NEVER + /* + ** If there are no more buildable objects to display, make the sidebar go away. + */ + if (!BuildableCount) { + Map.SidebarClass::Activate(0); + } +#endif + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * * + * This is the default constructor for the button that controls the buildable cameos on * + * the sidebar strip. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * + * correct values before this button will function. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::SelectClass::SelectClass(void) : + ControlClass(0, 0, 0, (OBJECT_WIDTH-1) * RESFACTOR, OBJECT_HEIGHT * RESFACTOR, LEFTPRESS|RIGHTPRESS|LEFTUP), + Strip(0), + Index(0) +{ +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * + * * + * Use this routine to set custom buildable vars for this particular select button. It * + * uses this information to properly know what buildable object to start or stop production * + * on. * + * * + * INPUT: strip -- Reference to the strip that owns this buildable button. * + * * + * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * + * the owning strip know what index this button refers to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +{ + Strip = &strip; + Index = index; + X = strip.X; + Y = strip.Y + ((index * OBJECT_HEIGHT) * RESFACTOR); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * + * * + * This function is called when the buildable icon (cameo) is clicked on. It handles * + * starting and stopping production as indicated. * + * * + * INPUT: flags -- The input event that triggered the call. * + * * + * key -- The keyboard value at the time of the input. * + * * + * OUTPUT: Returns with whether the input list should be scanned further. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 10/09/1996 JLB : Sonar pulse converted to regular event type. * + *=============================================================================================*/ +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +{ + int index = Strip->TopIndex + Index; + RTTIType otype = Strip->Buildables[index].BuildableType; + int oid = Strip->Buildables[index].BuildableID; + int fnumber = Strip->Buildables[index].Factory; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + ObjectTypeClass const * choice = NULL; + SpecialWeaponType spc = SPC_NONE; + + /* + ** Determine the factory number that would apply to objects of the type + ** the mouse is currently addressing. This doesn't mean that the factory number + ** fetched is actually producing the indicated object, merely that that particular + ** kind of factory is specified by the "genfactory" value. This can be used to see + ** if the factory type is currently busy or not. + */ + FactoryClass * factory = PlayerPtr->Fetch_Factory(otype); + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + + if (index < Strip->BuildableCount) { + if (otype != RTTI_SPECIAL) { + choice = Fetch_Techno_Type(otype, oid); + } else { + spc = SpecialWeaponType(oid); + } + + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + } else { + Map.Help_Text(TXT_NONE); + } + + if (spc != SPC_NONE) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(SpecialWeaponHelp[spc], X, Y, scheme->Color, true); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". If we are in targeting + ** mode then we don't want to be any more. + */ + if (flags & RIGHTPRESS) { + Map.IsTargettingMode = SPC_NONE; + } + /* + ** A left mouse press signal "activate". If our weapon type is + ** available then we should activate it. + */ + if (flags & LEFTPRESS) { + + if ((unsigned)spc < SPC_COUNT) { + if (PlayerPtr->SuperWeapon[spc].Is_Ready()) { + if (spc != SPC_SONAR_PULSE) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SONAR_PULSE, 0)); + } + } else { + PlayerPtr->SuperWeapon[spc].Impatient_Click(); + } + } + } + + } else { + + if (choice != NULL) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(choice->Full_Name(), X, Y, scheme->Color, true); + Map.Set_Cost(choice->Cost_Of() * PlayerPtr->CostBias); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". + */ + if (flags & RIGHTPRESS) { + + /* + ** If production is in progress, put it on hold. If production is already + ** on hold, then abandon it. Money will be refunded, the factory + ** manager deleted, and the object under construction is returned to + ** the free pool. + */ + if (factory != NULL) { + + /* + ** Cancels placement mode if the sidebar factory is abandoned or + ** suspended. + */ + if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + + if (!factory->Is_Building()) { + Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + } else { + Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); + Map.Column[0].IsToRedraw = true; + Map.Column[1].IsToRedraw = true; + } + } + } + + if (flags & LEFTPRESS) { + + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && factory != NULL) { + Speak(VOX_NO_FACTORY); + ControlClass::Action(flags, key); + return(true); + } + + if (factory != NULL) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + Map.IsTargettingMode = SPC_ANY; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + } else { + flags = 0; + } + } + + ControlClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * + * * + * This routine is called whenever the mouse is over the sidebar. It makes sure that the * + * mouse is always the normal shape while over the sidebar. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- Reference the keyboard code that may be present. * + * * + * OUTPUT: Returns that no further keyboard processing is necessary. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/28/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +{ + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + /* + ** Flag that all the icons on this strip need to be redrawn + */ + Flag_To_Redraw(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there was a change to the strip, then flag the strip to be redrawn. + */ + if (abandon) { + Flag_To_Redraw(); + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + + +/*********************************************************************************************** + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * * + * This is the function that is called when the map button is pressed. It will toggle * + * between the different modes that the radar map can assume. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Zoom_Mode_Control(void) +{ +#ifdef WIN32 + /* + ** If radar is active, cycle as follows: + ** Zoomed => not zoomed + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => zoomed + */ + if (IsRadarActive) { + if (Is_Zoomed() || Session.Type==GAME_NORMAL) { + if (Is_Zoomed() || !Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#else + /* + ** If radar is active, cycle as follows: + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => not zoomed + */ + if (IsRadarActive) { + if (Session.Type==GAME_NORMAL) { + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#endif +} diff --git a/CODE/SIDEBAR.H b/CODE/SIDEBAR.H new file mode 100644 index 0000000..55bd3fa --- /dev/null +++ b/CODE/SIDEBAR.H @@ -0,0 +1,399 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SIDEBAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "function.h" +#include "power.h" +#include "factory.h" + +class InitClass {}; + +class SidebarClass: public PowerClass +{ + public: + /* + ** These constants are used to control the sidebar rendering. They are instantiated + ** as enumerations since C++ cannot use "const" in this context. + */ + enum SideBarClassEnums { + BUTTON_ACTIVATOR=100, // Button ID for the activator. + SIDE_X=320-80, // The X position of sidebar upper left corner. + SIDE_Y=7+70, // The Y position of sidebar upper left corner. + SIDE_WIDTH=SIDEBAR_WID, // Width of the entire sidebar (in pixels). + SIDE_HEIGHT=200-(7+70), // Height of the entire sidebar (in pixels). + TOP_HEIGHT=13, // Height of top section (with repair/sell buttons). + COLUMN_ONE_X=(320-80)+8, // Sidestrip upper left coordinates... + COLUMN_ONE_Y=int(SIDE_Y)+int(TOP_HEIGHT), + COLUMN_TWO_X=(320-80)+8+((80-16)/2)+3, + COLUMN_TWO_Y=7+70+13, + +//BGA: changes to all buttons +#ifdef GERMAN + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef FRENCH + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef ENGLISH + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=(int)SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=(int)SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=(int)SIDE_X+36, // Right button X coordinate. + BUTTON_TWO_Y=(int)SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=(int)SIDE_X+58, // Right button X coordinate. + BUTTON_THREE_Y=(int)SIDE_Y+2, // Right button Y coordinate. +#endif + + COLUMNS=2 // Number of side strips on sidebar. + }; + + static void * SidebarShape; + static void * SidebarMiddleShape; //Only used in Win95 version + static void * SidebarBottomShape; //Only used in Win95 version + + SidebarClass(void); + SidebarClass(NoInitClass const & x); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + void Reload_Sidebar(void); // Loads house-specific sidebar art + + virtual void AI(KeyNumType & input, int x, int y); + virtual void Draw_It(bool complete); + virtual void Refresh_Cells(CELL cell, short const *list); + + void Zoom_Mode_Control(void); + bool Abandon_Production(RTTIType type, int factory); + bool Activate(int control); + bool Add(RTTIType type, int ID); + bool Sidebar_Click(KeyNumType & input, int x, int y); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + SelectClass(NoInitClass const & x) : ControlClass(x) {}; + + void Set_Owner(StripClass & strip, int index); + + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + StripClass(void) {} + StripClass(InitClass const &); + StripClass(NoInitClass const & ) {}; + + bool Add(RTTIType type, int ID); + bool Abandon_Production(int factory); + bool Scroll(bool up); + bool AI(KeyNumType & input, int x, int y); + void Draw_It(bool complete); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + void Reload_LogoShapes(void); + bool Recalc(void); + void Activate(void); + void Deactivate(void); + void Flag_To_Redraw(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(SpecialWeaponType type); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + BUTTON_UP=200, + BUTTON_DOWN=210, + BUTTON_SELECT=220, + MAX_BUILDABLES=75, // Maximum number of object types in sidebar. + OBJECT_HEIGHT=24, // Pixel height of each buildable object. + OBJECT_WIDTH=32, // Pixel width of each buildable object. + STRIP_WIDTH=35, // Width of strip (not counting border lines). + MAX_VISIBLE=4, // Number of object slots visible at any one time. +#ifdef WIN32 + SCROLL_RATE=12, // The pixel jump while scrolling (larger is faster). +#else + SCROLL_RATE=8, // The pixel jump while scrolling (larger is faster). +#endif + UP_X_OFFSET=2, // Scroll up arrow coordinates. +#ifdef WIN32 + UP_Y_OFFSET=int(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, +#else + UP_Y_OFFSET=int(MAX_VISIBLE)*int(OBJECT_HEIGHT)+2, +#endif + DOWN_X_OFFSET=18, // Scroll down arrow coordinates. + DOWN_Y_OFFSET=UP_Y_OFFSET,//BGint(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, + SBUTTON_WIDTH=16, // Width of the mini-scroll button. + SBUTTON_HEIGHT=12, // Height of the mini-scroll button. + LEFT_EDGE_OFFSET=2, // Offset from left edge for building shapes. + TEXT_X_OFFSET=18, // X offset to print "ready" text. + TEXT_Y_OFFSET=15, // Y offset to print "ready" text. + TEXT_COLOR=255 // Color to use for the "Ready" text. + }; + + /* + ** This is the coordinate of the upper left corner that this side strip + ** uses for rendering. + */ + int X,Y; + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** Shape numbers for the shapes in the STRIP.SHP file. + */ + enum SideBarStipShapeEnums { + SB_BLANK, // The blank rectangle to use if there are no objects present. + SB_FRAME + }; + + /* + ** If this particular side strip needs to be redrawn, then this flag + ** will be true. + */ + unsigned IsToRedraw:1; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This controls the sidebar slide direction. If this is true, then the sidebar + ** will scroll downward -- revealing previous objects. + */ + unsigned IsScrollingDown:1; + + /* + ** If the sidebar is scrolling, then this flag is true. Otherwise it is false. + */ + unsigned IsScrolling:1; + + /* + ** This is the object (sidebar slot) that is flashing. Only one slot can be flashing + ** at any one instant. This is usually the result of a click on the slot and construction + ** has commenced. + */ + int Flasher; + + /* + ** As the sidebar scrolls up and down, this variable holds the index for the topmost + ** visible sidebar slot. + */ + int TopIndex; + + /* + ** This is the queued scroll direction and amount. The sidebar + ** will scroll the number of slots indicated by this value. This + ** value is set according to the scroll buttons. + */ + int Scroller; + + /* + ** The sidebar has smooth scrolling. This is the number of pixels the sidebar + ** has slide down. Thus, if this value were 5, then there would be 5 pixels of + ** the TopIndex-1 sidebar object visible. When the Slid value reaches 24, then + ** the value resets to zero and the TopIndex is decremented. For sliding in the + ** opposite direction, change the IsScrollingDown flag. + */ + int Slid; + + /* + ** The value of Slid the last time we rendered the sidebar. + */ + int LastSlid; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + /* + ** Pointer to the shape data for small versions of the logos. These are used as + ** placeholder pieces on the side bar. + */ + static void * LogoShapes; + + /* + ** This points to the animation sequence of frames used to mark the passage of time + ** as an object is undergoing construction. + */ + static void const * ClockShapes; + + /* + ** This points to the animation sequence which deals with special + ** shapes which handle non-production based icons. + */ + static void const * SpecialShapes[SPC_COUNT]; + + /* + ** This is the last theater that the special palette remap table was loaded + ** for. If the current theater differs from this recorded value, then the + ** remap tables are reloaded. + */ +// static TheaterType LastTheater; + + static ShapeButtonClass UpButton[COLUMNS]; + static ShapeButtonClass DownButton[COLUMNS]; + static SelectClass SelectButton[COLUMNS][MAX_VISIBLE]; + + /* + ** This points to the shapes that are used for the clock overlay. This displays + ** progress of construction. + */ + static char ClockTranslucentTable[(1+1)*256]; + + } Column[COLUMNS]; + + + /* + ** If the sidebar is active then this flag is true. + */ + unsigned IsSidebarActive:1; + + /* + ** This flag tells the rendering system that the sidebar needs to be redrawn. + */ + unsigned IsToRedraw:1; + + class SBGadgetClass: public GadgetClass { + public: +//#ifdef WIN32 + SBGadgetClass(void) : GadgetClass((int)((int)SIDE_X+8)*RESFACTOR, (int)SIDE_Y*RESFACTOR, (int)((int)SIDE_WIDTH-1)*RESFACTOR-1, (int)((int)SIDE_HEIGHT-1)*RESFACTOR, LEFTUP) {}; +//#else +// SBGadgetClass(void) : GadgetClass((int)SIDE_X+8, (int)SIDE_Y, (int)SIDE_WIDTH-1, (int)SIDE_HEIGHT-1, LEFTUP) {}; +//#endif + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + /* + ** This is the button that is used to collapse and expand the sidebar. + ** These buttons must be available to derived classes, for Save/Load. + */ + static ShapeButtonClass Repair; + static ShapeButtonClass Upgrade; + static ShapeButtonClass Zoom; + static SBGadgetClass Background; + + bool Scroll(bool up, int column); + + private: + bool Activate_Repair(int control); + bool Activate_Upgrade(int control); + bool Activate_Demolish(int control); + int Which_Column(RTTIType type); + + unsigned IsRepairActive:1; + unsigned IsUpgradeActive:1; + unsigned IsDemolishActive:1; +}; + +#endif diff --git a/CODE/SLIDER.CPP b/CODE/SLIDER.CPP new file mode 100644 index 0000000..b0f5032 --- /dev/null +++ b/CODE/SLIDER.CPP @@ -0,0 +1,416 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SLIDER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SLIDER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SliderClass::Action -- Handles input processing for the slider. * + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * SliderClass::Step -- Steps the slider one value up or down. * + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * SliderClass::~SliderClass -- Destructor for slider object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "slider.h" + + +/*********************************************************************************************** + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * * + * This is the normal constructor for the slider gadget. * + * * + * INPUT: id -- The ID number to assign to this gadget. * + * x,y -- The pixel coordinate of the upper left corner for this gadget. * + * w,h -- The width and height of the slider gadget. The slider automatically * + * adapts for horizontal or vertical operation depending on which of these * + * dimensions is greater. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list) + : GaugeClass(id, x, y, w, h) +{ + BelongToList = belong_to_list ? true : false; + + PlusGadget = 0; + MinusGadget = 0; + if (!BelongToList) { + PlusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-PLUS.SHP"), X+Width+2, Y); + MinusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-MINS.SHP"), X-6, Y); + + if (PlusGadget) { + PlusGadget->Make_Peer(*this); + PlusGadget->Add(*this); + PlusGadget->Flag_To_Redraw(); + } + if (MinusGadget) { + MinusGadget->Make_Peer(*this); + MinusGadget->Add(*this); + MinusGadget->Flag_To_Redraw(); + } + } + Set_Thumb_Size(1); + Recalc_Thumb(); + + /* + ** Gauges have at least 2 colors, but sliders should only have one. + */ + IsColorized = 0; +} + + +/*********************************************************************************************** + * SliderClass::~SliderClass -- Destructor for slider object. * + * * + * This cleans up the slider object in preparation for deletion. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +virtual SliderClass::~SliderClass(void) +{ + if (PlusGadget) { + delete PlusGadget; + PlusGadget = 0; + } + if (MinusGadget) { + delete MinusGadget; + MinusGadget = 0; + } +} + + +/*********************************************************************************************** + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * * + * This sets the maximum value that the slider can be set at. The maximum value controls * + * the size of the thumb and the resolution of the thumb's movement. * + * * + * INPUT: value -- The value to set for the slider's maximum. * + * OUTPUT: bool; Was the maximum value changed? A false indicates a set to the value it * + * is currently set to already. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Maximum(int value) +{ + if (GaugeClass::Set_Maximum(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * * + * This routine will set the size of the thumb as it relates to the maximum value the * + * slider can achieve. This serves to display a proportionally sized thumb as well as * + * control how the slider "bumps" up or down. * + * * + * INPUT: value -- The new value of the thumb. It should never be larger than the slider * + * maximum. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Set_Thumb_Size(int value) +{ + Thumb = min(value, MaxValue); + Thumb = max(Thumb, 1); + Flag_To_Redraw(); + Recalc_Thumb(); +} + + +/*********************************************************************************************** + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * * + * This routine will set the thumb position for the slider. * + * * + * INPUT: value -- The position to set the slider. This position is relative to the maximum * + * value for the slider. * + * * + * OUTPUT: bool; Was the slider thumb position changed at all? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Value(int value) +{ + value = min(value, MaxValue-Thumb); + + if (GaugeClass::Set_Value(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * * + * This takes the current thumb logical size and starting value and calculates the pixel * + * size and starting offset accordingly. This function should be called whenever one of * + * these elements has changed. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Recalc_Thumb(void) +{ + int length = IsHorizontal ? Width : Height; + int size = length * fixed(Thumb, MaxValue); +// int size = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, Thumb)); + ThumbSize = max(size, 4); + int start = length * fixed(CurValue, MaxValue); +// int start = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, CurValue)); + ThumbStart = min(start, length-ThumbSize); +} + + +/*********************************************************************************************** + * SliderClass::Action -- Handles input processing for the slider. * + * * + * This routine is called when a qualifying input event has occurred. This routine will * + * process that event and make any adjustments to the slider as necessary. * + * * + * INPUT: flags -- Flag bits that tell the input event that caused this function to * + * be called. * + * key -- Reference to the key that caused the input event. * + * OUTPUT: bool; Was the event consumed and further processing of the gadget list should be * + * aborted? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** Handle the mouse click in a special way. If the click was not on the thumb, then + ** jump the thumb position one "step" in the appropriate direction. Otherwise, let normal + ** processing take place -- the slider then "sticks" and the thumb moves according to + ** mouse position. + */ + if (flags & LEFTPRESS) { + int mouse; // Mouse pixel position. + int edge; // Edge of slider. + + if (IsHorizontal) { + mouse = Get_Mouse_X(); + edge = X; + } else { + mouse = Get_Mouse_Y(); + edge = Y; + } + edge += 1; + + /* + ** Clicking outside the thumb: invoke parent's Action to process flags etc, + ** but turn off the event & return true so processing stops at this button. + */ + if (mouse < edge+ThumbStart) { + Bump(true); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + if (mouse > edge+ThumbStart+ThumbSize) { + Bump(false); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + GaugeClass::Action(flags, key); + key = KN_NONE; + return(true); + } + } + } + + /* + ** CHANGE GAUGECLASS::ACTION -- REMOVE (LEFTRELEASE) FROM IF STMT + */ + return(GaugeClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * * + * This support function will bump the slider one "step" or the size of the thumb up or * + * down as specified. It is typically called when the slider is clicked outside of the * + * thumb region but still inside of the slider. * + * * + * INPUT: up -- Should the bump be to increase the current position? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Bump(int up) +{ + if (up) { + return(Set_Value(CurValue - Thumb)); + } + return(Set_Value(CurValue + Thumb)); +} + + +/*********************************************************************************************** + * SliderClass::Step -- Steps the slider one value up or down. * + * * + * This routine will move the slider thumb one step in the direction specified. * + * * + * INPUT: up -- Should the step be up (i.e., forward)? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Step(int up) +{ + if (up) { + return(Set_Value(CurValue - 1)); + } + return(Set_Value(CurValue + 1)); +} + + +/*********************************************************************************************** + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * * + * This will draw the thumb graphic for this slider. Sometimes the thumb requires special * + * drawing, thus the need for this function separate from the normal Draw_Me function. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: The mouse is guaranteed to be hidden when this routine is called. * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Draw_Thumb(void) +{ + if (IsHorizontal) { + Draw_Box(X+ThumbStart, Y, ThumbSize, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, Y+ThumbStart, Width, ThumbSize, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * SliderClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * OUTPUT: bool; Was the gauge redrawn? * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Draw_Me(int forced) +{ + if (BelongToList) { + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + Draw_Thumb(); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + } + + /* + ** If it does not belong to a listbox... + */ + return(GaugeClass::Draw_Me(forced)); +} + + +/*********************************************************************************************** + * SliderClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * key -- The key value at the time of the event. * + * whom -- Which gadget is being touched. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Peer_To_Peer(unsigned flags, KeyNumType & , ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == PlusGadget) { + Step(false); + } + if (&whom == MinusGadget) { + Step(true); + } + } +} + + diff --git a/CODE/SLIDER.H b/CODE/SLIDER.H new file mode 100644 index 0000000..754a855 --- /dev/null +++ b/CODE/SLIDER.H @@ -0,0 +1,110 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SLIDER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SLIDER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "gauge.h" +#include "shapebtn.h" + + +/*************************************************************************** + * SliderClass -- Like a Windows ListBox structure * + * * + * INPUT: int id-- id of gadget * + * int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * int belong_to_list -- does this slider go with a listclass? * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class SliderClass : public GaugeClass +{ + public: + SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + virtual ~SliderClass(void); + + virtual void Set_Thumb_Size(int value); + virtual int Set_Maximum(int value); + virtual int Set_Value(int); + virtual int Bump(int up); + virtual int Step(int up); + virtual int Draw_Me(int forced); + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + + virtual int Thumb_Pixels(void) { return (ThumbSize);} + + protected: + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + ShapeButtonClass * PlusGadget; + ShapeButtonClass * MinusGadget; + + /* + ** If I belong to a listbox, I have to draw myself differently... + **/ + unsigned BelongToList:1; + + /* + ** This is the logical size of the thumb. This value is used when drawing + ** the thumb imagery. It is also the amount that is bumped when the + ** Bump() function is called. (This value is in application units.) + */ + int Thumb; + + /* + ** This is the current thumb pixel size and starting offset from beginning + ** of slider region. (These values are in pixels.) + */ + int ThumbSize; + int ThumbStart; // x or y position for the thumb + + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Thumb(void); + + private: + void Recalc_Thumb(void); +}; + +#endif diff --git a/CODE/SMUDGE.CPP b/CODE/SMUDGE.CPP new file mode 100644 index 0000000..cdbac7d --- /dev/null +++ b/CODE/SMUDGE.CPP @@ -0,0 +1,364 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SMUDGE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SMUDGE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * SmudgeClass::operator new -- Creator of smudge objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "smudge.h" + + + +HousesType SmudgeClass::ToOwn = HOUSE_NONE; + + +/*********************************************************************************************** + * SmudgeClass::operator new -- Creator of smudge objects. * + * * + * This routine will allocate a smudge object from the smudge tracking pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a newly allocated smudge object. If one couldn't be * + * found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * SmudgeClass::operator new(size_t ) +{ + void * ptr = Smudges.Allocate(); + if (ptr != NULL) { + ((SmudgeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * * + * This routine is used to remove the smudge from the tracking system. * + * * + * INPUT: ptr -- Pointer to the smudge to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((SmudgeClass *)ptr)->IsActive = false; + } + Smudges.Free((SmudgeClass *)ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * * + * This is the typical constructor for smudge objects. If the position to place the * + * smudge is not given, then the smudge will be initialized in a limbo state. If the * + * smudge is placed on the map, then this operation causes the smudge object itself to be * + * deleted and special map values updated to reflect the presence of a smudge. * + * * + * INPUT: type -- The type of smudge to construct. * + * * + * pos -- The position to place the smudge. If -1, then the smudge is initialized * + * into a limbo state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeClass::SmudgeClass(SmudgeType type, COORDINATE pos, HousesType house) : + ObjectClass(RTTI_SMUDGE, Smudges.ID(this)), + Class(SmudgeTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + if (!Unlimbo(pos)) { + delete this; + } else { + ToOwn = HOUSE_NONE; + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * * + * This routine is used during the scenario clearing process to initialize the smudge * + * object tracking system to a null state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Init(void) +{ + Smudges.Free_All(); +} + + +/*********************************************************************************************** + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * * + * This routine will place the smudge on the map. If the map cell allows. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the smudge marked successfully? Failure occurs if the smudge isn't * + * marked DOWN. * + * * + * WARNINGS: The smudge object is DELETED by this routine. * + * * + * HISTORY: * + * 09/22/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool SmudgeClass::Mark(MarkType mark) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL origin = Coord_Cell(Coord); + + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CELL newcell = origin + w + (h*MAP_CELL_W); + if (Map.In_Radar(newcell)) { + CellClass * cell = &Map[newcell]; + + if (Class->IsBib) { + cell->Smudge = Class->Type; + cell->SmudgeData = w + (h*Class->Width); + cell->Owner = ToOwn; + } else { + if (cell->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + if (Class->IsCrater && cell->Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(cell->Smudge).IsCrater) { + cell->SmudgeData++; + cell->SmudgeData = (int)min((int)cell->SmudgeData, (int)4); + } + + if (cell->Smudge == SMUDGE_NONE) { + + /* + ** Special selection of a crater that starts as close to the + ** specified coordinate as possible. + */ + if (Class->IsCrater) { + cell->Smudge = (SmudgeType)(SMUDGE_CRATER1 + CellClass::Spot_Index(Coord)); + } else { + cell->Smudge = Class->Type; + } + cell->SmudgeData = 0; + } + } + } + + /* + ** Flag everything that might be overlapping this cell to redraw itself. + */ + cell->Redraw_Objects(); + } + } + } + + /* + ** Whether it was successful in placing, or not, delete the smudge object. It isn't + ** needed once the map has been updated with the proper smudge data. Fake this object + ** as if it were never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * * + * This routine is used when a building is removed from the game. If there was any bib * + * attached, this routine will be called to disown the cells and remove the bib artwork. * + * * + * INPUT: cell -- The origin cell for this bib removal. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is actually working on a temporary bib object. It is created for the sole * + * purpose of calling this routine. It will be deleted immediately afterward. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Disown(CELL cell) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (Class->IsBib) { + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CellClass & cellptr = Map[(CELL)(cell + w + (h*MAP_CELL_W))]; + + if (cellptr.Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr.Overlay).IsWall) { + cellptr.Smudge = SMUDGE_NONE; + cellptr.SmudgeData = 0; + if (!cellptr.IsFlagged) { + cellptr.Owner = HOUSE_NONE; + } + cellptr.Redraw_Objects(); + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * * + * This routine is used by the scenario loader to read the smudge data in an INI file and * + * create the appropriate smudge objects on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Sets the smudge data value as well. * + *=============================================================================================*/ +void SmudgeClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; // Working string staging buffer. + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + SmudgeType smudge; // Smudge type. + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + smudge = SmudgeTypeClass::From_Name(strtok(buf, ",")); + if (smudge != SMUDGE_NONE) { + char * ptr = strtok(NULL, ","); + if (ptr != NULL) { + int data = 0; + CELL cell = atoi(ptr); + ptr = strtok(NULL, ","); + if (ptr != NULL) data = atoi(ptr); + new SmudgeClass(smudge, Cell_Coord(cell)); + if (Map[cell].Smudge == smudge && data != 0) { + Map[cell].SmudgeData = data; + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * * + * This routine will output all the smudge data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing template data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Find all templates and write them to the file. + */ + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass const * stype = &SmudgeTypeClass::As_Reference(ptr->Smudge); + if (!stype->IsBib) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%d,%d", stype->IniName, index, ptr->SmudgeData); + ini.Put_String(INI_Name(), uname, buf); + } + } + } +} diff --git a/CODE/SMUDGE.H b/CODE/SMUDGE.H new file mode 100644 index 0000000..22fb1d0 --- /dev/null +++ b/CODE/SMUDGE.H @@ -0,0 +1,92 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SMUDGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SMUDGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SMUDGE_H +#define SMUDGE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This is the transitory form for smudges. They exist as independent objects +** only in the transition stage from creation to placement upon the map. Once +** they are placed on the map, they merely become 'smudges' in the cell data. This +** object is then destroyed. +*/ +class SmudgeClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + SmudgeClass(SmudgeType type, COORDINATE pos=0xFFFFFFFFUL, HousesType house = HOUSE_NONE); + SmudgeClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + operator SmudgeType(void) const {return Class->Type;}; + virtual ~SmudgeClass(void) {if (GameActive) SmudgeClass::Limbo();Class=0;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "SMUDGE";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual bool Mark(MarkType); + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + void Disown(CELL cell); + + private: + + static HousesType ToOwn; +}; + +#endif diff --git a/CODE/SOUNDDLG.CPP b/CODE/SOUNDDLG.CPP new file mode 100644 index 0000000..eb61be7 --- /dev/null +++ b/CODE/SOUNDDLG.CPP @@ -0,0 +1,458 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SOUNDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SOUNDDLG.CPP * + * * + * Programmer : Maria del Mar McCready-Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "sounddlg.h" + +class MusicListClass : public ListClass +{ + public: + MusicListClass(int id, int x, int y, int w, int h) : + ListClass(id, x, y, w, h, TPF_6PT_GRAD|TPF_NOSHADOW, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")) + {}; + virtual ~MusicListClass(void) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; + + +/*********************************************************************************************** + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void SoundControlsClass::Process(void) +{ + + /* + ** Adjust dialog controls for resolution + */ + int option_width= OPTION_WIDTH * RESFACTOR; + int option_height= OPTION_HEIGHT * RESFACTOR; + + int option_x= OPTION_X * RESFACTOR; + int option_y= OPTION_Y * RESFACTOR; + + int listbox_x= LISTBOX_X * RESFACTOR; + int listbox_y= LISTBOX_Y * RESFACTOR; + int listbox_w= LISTBOX_W * RESFACTOR; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + int listbox_h= (LISTBOX_H * RESFACTOR)+2; +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif + + int button_width= BUTTON_WIDTH * RESFACTOR; + int button_x= BUTTON_X * RESFACTOR; + int button_y= BUTTON_Y * RESFACTOR; + + int stop_x= STOP_X * RESFACTOR; + int stop_y= STOP_Y * RESFACTOR; + + int play_x= PLAY_X * RESFACTOR; + int play_y= PLAY_Y * RESFACTOR; + + int onoff_width= ONOFF_WIDTH * RESFACTOR; + int shuffle_x= SHUFFLE_X * RESFACTOR; + int shuffle_y= SHUFFLE_Y * RESFACTOR; + int repeat_x= REPEAT_X * RESFACTOR; + int repeat_y= REPEAT_Y * RESFACTOR; + + int mslider_x= MSLIDER_X * RESFACTOR; + int mslider_y= MSLIDER_Y * RESFACTOR; + int mslider_w= MSLIDER_W * RESFACTOR; + int mslider_height= MSLIDER_HEIGHT * RESFACTOR; + + int fxslider_x= FXSLIDER_X * RESFACTOR; + int fxslider_y= FXSLIDER_Y * RESFACTOR; + int fxslider_w= FXSLIDER_W * RESFACTOR; + int fxslider_height= FXSLIDER_HEIGHT * RESFACTOR; + + int button_stop= BUTTON_STOP; + int button_play= BUTTON_PLAY; + int button_shuffle= BUTTON_SHUFFLE; + int button_repeat= BUTTON_REPEAT; + int button_options= BUTTON_OPTIONS; + int slider_music= SLIDER_MUSIC; + int slider_sound= SLIDER_SOUND; + int button_listbox= BUTTON_LISTBOX; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); +// ThemeType theme; + + /* + ** List box that holds the score text strings. + */ + MusicListClass listbox(0, option_x+listbox_x, option_y+listbox_y, listbox_w, listbox_h); + + /* + ** Return to options menu button. + */ + TextButtonClass returnto(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, option_x+button_x, option_y+button_y, button_width); +// TextButtonClass returnto(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, + + /* + ** Stop playing button. + */ + ShapeButtonClass stopbtn(BUTTON_STOP, MFCD::Retrieve("BTN-ST.SHP"), option_x+stop_x, option_y+stop_y); + + /* + ** Start playing button. + */ + ShapeButtonClass playbtn(BUTTON_PLAY, MFCD::Retrieve("BTN-PL.SHP"), option_x+play_x, option_y+play_y); + + /* + ** Shuffle control. + */ + TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, onoff_width); +// TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, ONOFF_WIDTH); + + /* + ** Repeat control. + */ + TextButtonClass repeatbtn(BUTTON_REPEAT, TXT_OFF, TPF_BUTTON, option_x+repeat_x, option_y+repeat_y, onoff_width); + + /* + ** Music volume slider. + */ + SliderClass music(SLIDER_MUSIC, option_x+mslider_x, option_y+mslider_y, mslider_w, mslider_height, true); + + /* + ** Sound volume slider. + */ + SliderClass sound(SLIDER_SOUND, option_x+fxslider_x, option_y+fxslider_y, fxslider_w, fxslider_height, true); + + /* + ** Causes left mouse clicks inside the dialog area, but not on any + ** particular button, to be ignored. + */ + GadgetClass area(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + + /* + ** Causes right clicks anywhere or left clicks outside of the dialog + ** box area to be the same a clicking the return to game options button. + */ + ControlClass ctrl(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::RIGHTPRESS|GadgetClass::LEFTPRESS); + + /* + ** The repeat and shuffle buttons are of the toggle type. They toggle + ** between saying "on" and "off". + */ + shufflebtn.IsToggleType = true; + if (Options.IsScoreShuffle) { + shufflebtn.Turn_On(); + } else { + shufflebtn.Turn_Off(); + } + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + + repeatbtn.IsToggleType = true; + if (Options.IsScoreRepeat) { + repeatbtn.Turn_On(); + } else { + repeatbtn.Turn_Off(); + } + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + + /* + ** Set the initial values of the sliders. + */ + music.Set_Maximum(255); + music.Set_Thumb_Size(16); + music.Set_Value(Options.ScoreVolume * 256); + sound.Set_Maximum(255); + sound.Set_Thumb_Size(16); + sound.Set_Value(Options.Volume * 256); + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. + */ + GadgetClass * optionsbtn = &returnto; + listbox.Add_Tail(*optionsbtn); + stopbtn.Add_Tail(*optionsbtn); + playbtn.Add_Tail(*optionsbtn); + shufflebtn.Add_Tail(*optionsbtn); + repeatbtn.Add_Tail(*optionsbtn); + music.Add_Tail(*optionsbtn); + sound.Add_Tail(*optionsbtn); + area.Add_Tail(*optionsbtn); + ctrl.Add_Tail(*optionsbtn); + + /* + ** Add all the themes to the list box. The list box entries are constructed + ** and then stored into allocated EMS memory blocks. + */ + for (ThemeType index = THEME_FIRST; index < Theme.Max_Themes(); index++) { + if (Theme.Is_Allowed(index)) { + char buffer[100]; + int length = Theme.Track_Length(index); + char const * fullname = Theme.Full_Name(index); + + void * ptr = new char [sizeof(buffer)]; + if (ptr) { + sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s", index, listbox.Count()+1, length / 60, length % 60, fullname); + listbox.Add_Item((char const *)ptr); + } + + if (Theme.What_Is_Playing() == index) { + listbox.Set_Selected_Index(listbox.Count()-1); + } + } + } + static int _tabs[] = { + 55 * RESFACTOR, 72 * RESFACTOR, 90 * RESFACTOR + }; + listbox.Set_Tabs(_tabs); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(option_x, option_y, option_width, option_height); + + Draw_Caption(TXT_SOUND_CONTROLS, option_x, option_y, option_width); + + /* + ** Draw the Music, Speech & Sound titles. + */ + Fancy_Text_Print(TXT_MUSIC_VOLUME, option_x+mslider_x-(5 * RESFACTOR), option_y+mslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + Fancy_Text_Print(TXT_SOUND_VOLUME, option_x+fxslider_x-(5 * RESFACTOR), option_y+fxslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + +#if defined(GERMAN) || defined(FRENCH) + Fancy_Text_Print(TXT_SHUFFLE, option_x+4+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#else + Fancy_Text_Print(TXT_SHUFFLE, option_x+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#endif + Fancy_Text_Print(TXT_REPEAT, option_x+repeat_x-(5 * RESFACTOR), option_y+repeat_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + optionsbtn->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = optionsbtn->Input(); + + /* + ** Process Input. + */ + switch (input) { + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + process = false; + break; + + /* + ** Control music volume. + */ + case SLIDER_MUSIC|KN_BUTTON: + Options.Set_Score_Volume(fixed(music.Get_Value(), 256), true); +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + Options.MultiScoreVolume = Options.ScoreVolume; +#endif + break; + + /* + ** Control sound volume. + */ + case SLIDER_SOUND|KN_BUTTON: + Options.Set_Sound_Volume(fixed(sound.Get_Value(), 256), true); + break; + + case BUTTON_LISTBOX|KN_BUTTON: + break; + + /* + ** Stop all themes from playing. + */ + case BUTTON_STOP|KN_BUTTON: + Theme.Stop(); + Theme.Queue_Song(THEME_QUIET); +// Theme.Queue_Song(THEME_NONE); + break; + + /* + ** Start the currently selected theme to play. + */ + case KN_SPACE: + case BUTTON_PLAY|KN_BUTTON: + Theme.Queue_Song( (ThemeType)*((unsigned char *)listbox.Current_Item()) ); + break; + + /* + ** Toggle the shuffle button. + */ + case BUTTON_SHUFFLE|KN_BUTTON: + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Shuffle(shufflebtn.IsOn); + break; + + /* + ** Toggle the repeat button. + */ + case BUTTON_REPEAT|KN_BUTTON: + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Repeat(repeatbtn.IsOn); + break; + } + } + + /* + ** If the score volume was turned all the way down, then actually + ** stop the scores from being played. + */ + if (Options.ScoreVolume == 0) { + Theme.Stop(); + } + + /* + ** Free the items from the list box. + */ + while (listbox.Count()) { + char const * ptr = listbox.Get_Item(0); + listbox.Remove_Item(ptr); + delete [] (void*)ptr; + } +} + + +/*********************************************************************************************** + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * * + * This routine will display the score line in a list box. It overrides the list box * + * handler for line drawing. * + * * + * INPUT: index -- The index within the list box that is being drawn. * + * * + * x,y -- The pixel coordinates of the upper left position of the line. * + * * + * width -- The width of the line that drawing is allowed to use. * + * * + * selected-- Is the current line selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void MusicListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, + GadgetClass::Get_Color_Scheme()->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, scheme, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, TextFlags, width, Tabs); + } +} diff --git a/CODE/SOUNDDLG.H b/CODE/SOUNDDLG.H new file mode 100644 index 0000000..b3497f2 --- /dev/null +++ b/CODE/SOUNDDLG.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SOUNDDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUNDDLG_H +#define SOUNDDLG_H + +#include "gadget.h" + +class SoundControlsClass +{ + enum SoundControlsClassEnums { +#ifdef FRENCH + OPTION_WIDTH=308, +#else + OPTION_WIDTH=292, +#endif + OPTION_HEIGHT=146, + + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + + LISTBOX_X=17, + LISTBOX_Y=54, + LISTBOX_W=OPTION_WIDTH-(LISTBOX_X*2), + LISTBOX_H=72, + + BUTTON_WIDTH=70, + BUTTON_X=OPTION_WIDTH-(BUTTON_WIDTH+17), // Options button x pos + BUTTON_Y=128, // Options button y pos + + STOP_X=17, // Stop button X. + STOP_Y=128, // Stop button Y. + + PLAY_X=35, + PLAY_Y=128, + + ONOFF_WIDTH=25, +#ifdef GERMAN + SHUFFLE_X=79,//BGA:91, +#else +#ifdef FRENCH + SHUFFLE_X=99, +#else + SHUFFLE_X=97, +#endif +#endif + SHUFFLE_Y=128, + +#ifdef FRENCH + REPEAT_X=169, +#else + REPEAT_X=164, +#endif + REPEAT_Y=128, + + MSLIDER_X=147, + MSLIDER_Y=28, + MSLIDER_W=108, + MSLIDER_HEIGHT=5, + + FXSLIDER_X=147, + FXSLIDER_Y=40, + FXSLIDER_W=108, + FXSLIDER_HEIGHT=5, + + BUTTON_STOP = 605, + BUTTON_PLAY, + BUTTON_SHUFFLE, + BUTTON_REPEAT, + BUTTON_OPTIONS, + SLIDER_MUSIC, + SLIDER_SOUND, + BUTTON_LISTBOX, + }; + + public: + SoundControlsClass(void) {} + void Process(void); +}; + +#endif diff --git a/CODE/SPECIAL.CPP b/CODE/SPECIAL.CPP new file mode 100644 index 0000000..8ec11e8 --- /dev/null +++ b/CODE/SPECIAL.CPP @@ -0,0 +1,592 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SPECIAL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SPECIAL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/27/95 * + * * + * Last Update : August 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * Fetch_Password -- Prompts for a password entry from client. * + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * Special_Dialog -- Handles the special options dialog. * + * SpecialClass::Init -- Initialize the special class of options. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define OPTION_WIDTH 236*2 +#define OPTION_HEIGHT 162*2 +#define OPTION_X ((640 - OPTION_WIDTH) / 2) +#define OPTION_Y (400 - OPTION_HEIGHT) / 2 +#else +#define OPTION_WIDTH 236 +#define OPTION_HEIGHT 162 +#define OPTION_X ((320 - OPTION_WIDTH) / 2) +#define OPTION_Y (200 - OPTION_HEIGHT) / 2 +#endif + + +/*********************************************************************************************** + * SpecialClass::Init -- Initialize the special class of options. * + * * + * This initialization function is required (as opposed to using a constructor) because * + * the SpecialClass is declared as part of a union. A union cannot have a member with a * + * constructor. Other than this anomoly, the function serves the same purpose as a * + * normal constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/20/1996 JLB : Created. * + *=============================================================================================*/ +void SpecialClass::Init(void) +{ + IsShadowGrow = false; + IsSpeedBuild = false; + IsFromInstall = false; + IsCaptureTheFlag = false; + IsInert = false; + IsThreePoint = false; + IsTGrowth = true; + IsTSpread = true; +} + + +/*********************************************************************************************** + * Special_Dialog -- Handles the special options dialog. * + * * + * This dialog is used when setting the special game options. It does not appear in the * + * final version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void Special_Dialog(bool simple) +{ + SpecialClass oldspecial = Special; + GadgetClass * buttons = NULL; + static struct { + int Description; + int Setting; + CheckBoxClass * Button; + } _options[] = { + {TXT_THREE_POINT, 0, 0}, + {TXT_SPEED_BUILD, 0, 0}, + }; + + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+15*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-60*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + buttons = &ok; + cancel.Add(*buttons); + + for (int index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + _options[index].Button = new CheckBoxClass(100+index, OPTION_X+17*RESFACTOR, OPTION_Y+20*RESFACTOR+(index*10*RESFACTOR)); + if (_options[index].Button) { + _options[index].Button->Add(*buttons); + + bool value = false; + switch (_options[index].Description) { + case TXT_THREE_POINT: + value = Special.IsThreePoint; + break; + + case TXT_SPEED_BUILD: + value = Special.IsSpeedBuild; + break; + } + + _options[index].Setting = value; + if (value) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + while (process) { + + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Draw_Caption(TXT_SPECIAL_OPTIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + Fancy_Text_Print(_options[index].Description, _options[index].Button->X+10*RESFACTOR, _options[index].Button->Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_ESC: + case 200|KN_BUTTON: + process = false; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + bool setting = _options[index].Setting; + switch (_options[index].Description) { + case TXT_THREE_POINT: + oldspecial.IsThreePoint = setting; + break; + + case TXT_SPEED_BUILD: + oldspecial.IsSpeedBuild = setting; + break; + } + } + if (!simple) { + OutList.Add(EventClass(oldspecial)); + } else { + Special = oldspecial; + } + break; + + case 201|KN_BUTTON: + process = false; + break; + + case KN_NONE: + break; + + default: + index = (input & ~KN_BUTTON) - 100; + if ((unsigned)index < sizeof(_options)/sizeof(_options[0])) { + _options[index].Setting = _options[index].Button->IsOn; + } + break; + } + } + + if (!simple) { + Map.Revert_Mouse_Shape(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + + +/* +** Derived from the edit class, this class allows entry of passwords style text +** as an edit box. This style is characterized by "*" being displayed for every +** real character entered. +*/ +class PWEditClass : public EditClass +{ + public: + PWEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1) : + EditClass(id, text, max_len, flags, x, y, w, h, ALPHANUMERIC) {}; + + protected: + virtual void Draw_Text(char const * text); +}; + + +/*********************************************************************************************** + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * * + * This routine is used by the password style edit box in order to display the entered * + * text. The text will be displayed as asterisks instead of the actual characters the * + * edit box may contain. This is necessary to obscure the password entry from glancing * + * eyes. * + * * + * INPUT: text -- Pointer to the text that is to be rendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +void PWEditClass::Draw_Text(char const * text) +{ + char buffer[80]; + + memset(buffer, '\0', sizeof(buffer)); + memset(buffer, '*', strlen(text)); + + if (FontPtr == GradFont6Ptr) { + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(buffer), Y+1, Color, TBLACK, TextFlags | flags); + } + } else { + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Has_Focus() ? + &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY], + TBLACK, TextFlags, Width-2); + + if (Has_Focus() && strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print("_", X+1+String_Pixel_Width(buffer), Y+1, + &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TextFlags); + } + } +} + + +/*********************************************************************************************** + * Fetch_Password -- Prompts for a password entry from client. * + * * + * This routine will prompt for and return a password entry from the player. * + * * + * INPUT: caption -- The caption to use for the top of the prompt dialog. * + * * + * message -- The body of the message to display in the dialog. * + * * + * btext -- The button text to use to finish the dialog box entry. * + * * + * OUTPUT: Returns with a pointer to the password text entered. This pointer is valid * + * only until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +#define BUFFSIZE (511) +char const * Fetch_Password(int caption, int message, int btext) +{ + char buffer[BUFFSIZE]; + bool process; // loop while true + KeyNumType input; // user input + bool pressed; + int curbutton; + TextButtonClass ok; + + if (btext == TXT_NONE) btext = TXT_OK; + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2*RESFACTOR; + bwidth = max((String_Pixel_Width(Text_String(btext)) + 8*RESFACTOR), 30*RESFACTOR); + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, Text_String(message), BUFFSIZE-1); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, 255, width, height); + + width = max(width, 50*RESFACTOR); + width += 40*RESFACTOR; + height += (60+25)*RESFACTOR; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Create the "ok" and password edit buttons. + */ + TextButtonClass button1(1, btext, TPF_BUTTON, + x + ((width - bwidth) >> 1), y + height - (bheight + 5*RESFACTOR), bwidth); + + static char pbuffer[45]; + memset(pbuffer, '\0', sizeof(pbuffer)); + int editx = x+26*RESFACTOR; + int editwidth = (SeenBuff.Get_Width()/2 - editx) * 2; + PWEditClass button2(2, &pbuffer[0], sizeof(pbuffer), TPF_6PT_GRAD|TPF_NOSHADOW, editx, (y+height)-35*RESFACTOR, editwidth, 10*RESFACTOR); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + buttonlist = &button1; + button2.Add(*buttonlist); + + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, width, height); + Draw_Caption(caption, x, y, width); + + /* + ** Draw the body of the message box. + */ + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + process = true; + pressed = false; + bool first = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + process = false; + break; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + if (first) { + button2.Set_Focus(); + button2.Flag_To_Redraw(); + first = false; + } + switch (input) { + case (1|BUTTON_FLAG): + process = false; + break; + + case (KN_ESC): + case (2|BUTTON_FLAG): + process = false; + break; + + case (KN_RETURN): + process = false; + break; + + default: + break; + } + } + + return(pbuffer); +} + + +/*********************************************************************************************** + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * * + * This will display a dialog box that requests the player to specify a difficulty * + * setting. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a difficulty setting of 0 for easiest and 4 for hardest. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath) +#else +int Fetch_Difficulty(void) +#endif +{ + int const w = 250 * RESFACTOR; + int const h = 80 * RESFACTOR; + int const x = ((320 * RESFACTOR)/2) - w/2; + int const y = ((200 * RESFACTOR)/2) - h/2; + int const bwidth = 30 * RESFACTOR; + + /* + ** Fill the description buffer with the description text. Break + ** the text into appropriate spacing. + */ + char buffer[512]; + strncpy(buffer, Text_String(TXT_DIFFICULTY), sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If it's an aftermath mission, trim the sentence to get rid of the campaign stuff. + if (amath) { + int index=0; + while (buffer[index] && buffer[index]!='.') index++; + if (buffer[index]=='.') { + buffer[index+1]=0; + } + } +#endif + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, w-60*RESFACTOR, width, height); + + /* + ** Create the OK button. + */ + TextButtonClass okbutton(1, TXT_OK, TPF_BUTTON, (x+w) - (bwidth+20*RESFACTOR) , (y+h) - (18*RESFACTOR), bwidth); + GadgetClass * buttonlist = &okbutton; + + /* + ** Create the slider button. + */ + SliderClass slider(2, x+20*RESFACTOR, y+h - 29*RESFACTOR, w - 40*RESFACTOR, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + slider.Set_Maximum(5); + slider.Set_Value(2); + } else { + slider.Set_Maximum(3); + slider.Set_Value(1); + } + slider.Add(*buttonlist); + + /* + ** Main Processing Loop. + */ + Set_Logic_Page(SeenBuff); + bool redraw = true; + bool process = true; + while (process) { + + if (redraw) { + redraw = false; + + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Dialog_Box(x, y, w, h); + + /* + ** Draw the body of the message. + */ +// Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Display the descripton of the slider range. + */ + Fancy_Text_Print(TXT_HARD, slider.X+slider.Width, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_RIGHT|TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_EASY, slider.X, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_NORMAL, slider.X + (slider.Width/2), slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + redraw = true; + continue; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); + + switch (input) { + case KN_RETURN: + case (1|BUTTON_FLAG): + process = false; + break; + + default: + break; + } + } + + return(slider.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2)); +} diff --git a/CODE/SPECIAL.H b/CODE/SPECIAL.H new file mode 100644 index 0000000..f074a6c --- /dev/null +++ b/CODE/SPECIAL.H @@ -0,0 +1,101 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SPECIAL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SPECIAL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/95 * + * * + * Last Update : February 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +class SpecialClass +{ + public: + + /* + ** This initializes all members just like a constructor. A constructor + ** cannot be used for this class because it is part of a union. + */ + void Init(void); + + /* + ** If the shroud should regenerated, then this flag will be true. + */ + unsigned IsShadowGrow:1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild:1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall:1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag:1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert:1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint:1; + + /* + ** If Tiberium is allowed to spread and grow, then these flags will be true. + ** These are duplicated from the rules.ini file and also controlled by the + ** multiplayer dialog box. + */ + unsigned IsTGrowth:1; + unsigned IsTSpread:1; + + /* + ** Flag that we were spawned from WChat. + */ + unsigned IsFromWChat:1; +}; + + +#endif diff --git a/CODE/SPRITE.CPP b/CODE/SPRITE.CPP new file mode 100644 index 0000000..4e7a02e --- /dev/null +++ b/CODE/SPRITE.CPP @@ -0,0 +1,329 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SPRITE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/********************************************************************** + + Sprite.cpp + + Dec 28,1995 + + GraphicBufferClass member functions for blitting, scaling, + and rotating bitmaps + +**********************************************************************/ + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +//#include "function.h" +#define FILE_H +#define RAWFILE_H +#define WWMEM_H +#define WWFILE_Hx +#include +#include +//#include "gbuffer.h" +//#include "math.h" + + +long _SineTab[256] = { +0 ,6 ,12 , 18 , 25 , 31 , 37 , 43 , 49 , 56 , 62 , 68 , 74 , 80 , 86 , +92 , 97 , 103 , 109 , 115 , 120 , 126 , 131 , 136 , 142 , 147 , 152 , +157 , 162 , 167 , 171 , 176 , 180 , 185 , 189 , 193 , 197 , 201 , 205 , +209 , 212 , 216 , 219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , +243 , 244 , 246 , 248 , 249 , 251 , 252 , 253 , 254 , 254 , 255 , 255 , +255 , 255 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , +246 , 245 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , +216 , 213 , 209 , 205 , 201 , 198 , 194 , 189 , 185 , 181 , 176 , 172 , +167 , 162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , +104 , 98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 44 , 37 , 31 , 25 , +19 , 12 , 6 , 0 , -5 , -12 , -18 , -24 , -30 , -37 , -43 , -49 , -55 , +-61 , -67 , -73 , -79 , -85 , -91 , -97 , -103 , -109 , -114 , -120 , +-125 , -131 , -136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , +-176 , -180 , -185 , -189 , -193 , -197 , -201 , -205 , -208 , -212 , +-215 , -219 , -222 , -225 , -228 , -231 , -233 , -236 , -238 , -240 , +-242 , -244 , -246 , -248 , -249 , -250 , -252 , -253 , -254 , -254 , +-255 , -255 , -255 , -255 , -255 , -255 , -255 , -254 , -254 , -253 , +-252 , -251 , -249 , -248 , -246 , -245 , -243 , -241 , -239 , -236 , +-234 , -231 , -228 , -226 , -223 , -219 , -216 , -213 , -209 , -206 , +-202 , -198 , -194 , -190 , -185 , -181 , -177 , -172 , -167 , -162 , +-158 , -153 , -148 , -142 , -137 , -132 , -126 , -121 , -115 , -110 , +-104 , -98 , -92 , -86 , -81 , -75 , -69 , -62 , -56 , -50 , -44 , +-38 , -32 , -25 , -19 , -13 , -7 , +}; + +long _CosineTab[256] = { +256 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , 246 , +244 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , 216 , +212 , 209 , 205 , 201 , 197 , 193 , 189 , 185 , 181 , 176 , 171 , 167 , +162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , 103 , +98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 43 , 37 , 31 , 25 , 19 , +12 , 6 , 0 , -6 , -12 , -18 , -24 , -31 , -37 , -43 , -49 , -55 , -61 , +-68 , -74 , -80 , -86 , -91 , -97 , -103 , -109 , -114 , -120 , -125 , -131 , +-136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , -176 , -180 , +-185 , -189 , -193 , -197 , -201 , -205 , -209 , -212 , -216 , -219 , -222 , +-225 , -228 , -231 , -233 , -236 , -238 , -240 , -242 , -244 , -246 , +-248 , -249 , -251 , -252 , -253 , -254 , -254 , -255 , -255 , -255 , +-255 , -255 , -255 , -255 , -254 , -254 , -253 , -252 , -251 , -249 , -248 , +-246 , -245 , -243 , -241 , -239 , -236 , -234 , -231 , -228 , -225 , +-222 , -219 , -216 , -213 , -209 , -205 , -202 , -198 , -194 , -190 , -185 , +-181 , -176 , -172 , -167 , -162 , -157 , -152 , -147 , -142 , -137 , -132 , +-126 , -121 , -115 , -109 , -104 , -98 , -92 , -86 , -80 , -74 , -68 , +-62 , -56 , -50 , -44 , -38 , -31 , -25 , -19 , -13 , -6 , 0 , 5 , 11 , +18 , 24 , 30 , 36 , 43 , 49 , 55 , 61 , 67 , 73 , 79 , 85 , 91 , 97 , 103 , +108 , 114 , 120 , 125 , 131 , 136 , 141 , 146 , 151 , 156 , 161 , 166 , +171 , 176 , 180 , 184 , 189 , 193 , 197 , 201 , 205 , 208 , 212 , 215 , +219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , 242 , 244 , 246 , +248 , 249 , 250 , 252 , 253 , 253 , 254 , 255 , 255 , 255 , +}; + + + +/*************************************************************** +* +* Scale_Rotate +* +* FUNCTION: +* +* Using Bi-Linear Interpolation, draws a scaled and rotated +* bitmap onto the buffer. No clipping is performed so beware. +* +* INPUTS +* +* bmp - bitmap to draw +* pt - desired position of the center +* scale - 24.8 fixed point scale factor +* angle - 8bit angle (0=0deg, 255=360deg) +* +***************************************************************/ + +void GraphicBufferClass::Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle) +{ + unsigned int scrpos; + unsigned int temp; + + int i,j; // counter vars + int pxerror =0; // these three vars will be used in an + int pyerror =0; // integer difference alg to keep track + int pixpos =0; // of what pixel to draw. + unsigned char pixel; + + TPoint2D p0; // "upper left" corner of the rectangle + TPoint2D p1; // "upper right" corner of the rectangle + TPoint2D p2; // "lower left" corner of the rectangle + + /*------------------------------------------------- + Compute three corner points of the rectangle + -------------------------------------------------*/ + { + angle &= 0x0FF; + long c = _CosineTab[angle]; + long s = _SineTab[angle]; + long W = (scale*bmp.Width)>>1; + long L = (scale*bmp.Height)>>1; + + p0.x = (pt.x + ((( (L*c) >> 8)-((W*s) >> 8)) >> 8)); + p0.y = (pt.y + (((-(L*s) >> 8)-((W*c) >> 8)) >> 8)); + p1.x = (pt.x + ((( (L*c) >> 8)+((W*s) >> 8)) >> 8)); + p1.y = (pt.y + (((-(L*s) >> 8)+((W*c) >> 8)) >> 8)); + p2.x = (pt.x + (((-(L*c) >> 8)-((W*s) >> 8)) >> 8)); + p2.y = (pt.y + ((( (L*s) >> 8)-((W*c) >> 8)) >> 8)); + } + + /*----------------------------------- + Initialize Breshnam constants + -----------------------------------*/ + + // This breshnam line goes across the FRONT of the rectangle + // In the bitmap, this will step from left to right + + int f_deltax =(p1.x-p0.x); + int f_deltay =(p1.y-p0.y); + int f_error =0; + int f_xstep =1; + int f_ystep =Width; + + // This breshnam line goes down the SIDE of the rectangle + // In the bitmap, this line will step from top to bottom + + int s_deltax =(p2.x-p0.x); + int s_deltay =(p2.y-p0.y); + int s_error =0; + int s_xstep =1; + int s_ystep =Width; + + /*-------------------------------- + fixup deltas and step values + --------------------------------*/ + + if (f_deltay<0) { + f_deltay=-f_deltay; + f_ystep=-Width; + }; + + if (f_deltax<0) { + f_deltax=-f_deltax; + f_xstep=-1; + }; + + if (s_deltay<0) { + s_deltay=-s_deltay; + s_ystep=-Width; + }; + + if (s_deltax<0) { + s_deltax=-s_deltax; + s_xstep=-1; + }; + + scrpos=p0.x+Width*p0.y; //address of initial screen pos. + temp=scrpos; + + /*--------------------------------------------------------------------- + Now all of the differences, errors, and steps are set up so we can + begin drawing the bitmap... + + There are two cases here, + 1 - the "Front" line has a slope of < 1.0 (45 degrees) + 2 - the "Front" line has a slope of >= 1.0 + + For case 1, we step along the X direction, for case 2, step in y + ---------------------------------------------------------------------*/ + + if (f_deltax>f_deltay) { //CASE 1, step front in X, side in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltax) + { + pixpos++; + pxerror-=f_deltax; + }; + scrpos+=f_xstep; //step to next screen pos + f_error+=f_deltay; + if (f_error>f_deltax) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltax; + scrpos+=f_ystep; + }; + }; + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + + while (pyerror>s_deltay) + { + pixpos+=bmp.Width; + pyerror-=s_deltay; + }; + + f_error=0; + scrpos=temp; + scrpos+=s_ystep; + s_error+=s_deltax; + + if (s_error>s_deltay) + { + s_error-=s_deltay; + scrpos+=s_xstep; + }; + } + + } else { // CASE 2, Step front line in X, side line in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltay) + { + pixpos++; + pxerror-=f_deltay; + }; + + scrpos+=f_ystep; //step to next screen pos + f_error+=f_deltax; + if (f_error>f_deltay) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltay; + scrpos+=f_xstep; + }; + }; + + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + while (pyerror>s_deltax) + { + pixpos+=bmp.Width; + pyerror-=s_deltax; + }; + + scrpos=temp; + scrpos+=s_xstep; + s_error+=s_deltay; + f_error=0; + if (s_error>s_deltax) + { + s_error-=s_deltax; + scrpos+=s_ystep; + }; + } + } +} \ No newline at end of file diff --git a/CODE/STAGE.H b/CODE/STAGE.H new file mode 100644 index 0000000..8036b1f --- /dev/null +++ b/CODE/STAGE.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/STAGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : STAGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 17, 1994 * + * * + * Last Update : June 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STAGE_H +#define STAGE_H + +class StageClass { + + /* + ** This handles the animation stage of the object. This includes smoke, walking, + ** flapping, and rocket flames. + */ + unsigned Stage; + + /* + ** This is the countdown timer for stage animation. When this counts down + ** to zero, then the stage increments by one and the time cycle starts + ** over again. + */ + CDTimerClass Timer; + + /* + ** This is the value to assign the StageTimer whenever it needs to be reset. Thus, + ** this value is the control of how fast the stage value increments. + */ + int Rate; + + public: + StageClass(void) : Stage(0), Timer(0), Rate(0) {}; + StageClass(NoInitClass const & x) : Timer(x) {}; + + int Fetch_Stage(void) const {return(Stage);}; + int Fetch_Rate(void) const {return(Rate);}; + void Set_Stage(int stage) {Stage = stage;}; + void Set_Rate(int rate) {Timer = rate; Rate = rate;}; + void AI(void) {}; + bool About_To_Change(void) const {return(Timer == 0 && Rate != 0);} + bool Graphic_Logic(void) { + if (About_To_Change()) { + Stage++; + Timer = Rate; + return(true); + } + return(false); + }; +}; + + +#endif diff --git a/CODE/STARTUP.CPP b/CODE/STARTUP.CPP new file mode 100644 index 0000000..80a80a2 --- /dev/null +++ b/CODE/STARTUP.CPP @@ -0,0 +1,1069 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/STARTUP.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 3, 1994 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Prog_End -- Cleans up library systems in prep for game exit. * + * main -- Initial startup routine (preps library systems). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +#ifdef WIN32 +#include "ccdde.h" +#include "ipx95.h" +#endif //WIN32 + +#ifdef MCIMPEG // Denzil 6/15/98 +#include "mcimovie.h" +#endif + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#endif + +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config); +void Print_Error_End_Exit(char * string); +void Print_Error_Exit(char * string); + +#ifdef WIN32 +//WinTimerClass * WinTimer; +extern void Create_Main_Window ( HANDLE instance , int command_show , int width , int height); +extern bool RA95AlreadyRunning; +HINSTANCE ProgramInstance; +void Check_Use_Compressed_Shapes (void); +void Read_Setup_Options(RawFileClass *config_file); +bool VideoBackBufferAllowed = true; +#else +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); +#endif //WIN32 + +const char* Game_Registry_Key(); + +#if (ENGLISH) +#define WINDOW_NAME "Red Alert" +#endif + +#if (FRENCH) +#define WINDOW_NAME "Alerte Rouge" +#endif + +#if (GERMAN) +#define WINDOW_NAME "Alarmstufe Rot" +#endif + + + +/*********************************************************************************************** + * main -- Initial startup routine (preps library systems). * + * * + * This is the routine that is first called when the program starts up. It basically * + * handles the command line parsing and setting up library systems. * + * * + * INPUT: argc -- Number of command line arguments. * + * * + * argv -- Pointer to array of command line argument strings. * + * * + * OUTPUT: Returns with execution failure code (if any). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +//int PASCAL WinMain(HINSTANCE, HINSTANCE, char *, int ) +int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char * command_line , int command_show ) +#else //WIN32 +int main(int argc, char * argv[]) +#endif //WIN32 + +{ +#ifdef WIN32 + + #ifndef MPEGMOVIE // Denzil 6/10/98 + DDSCAPS surface_capabilities; + #endif + + /* + ** First thing to do is check if there is another instance of Red Alert already running + ** and if so, switch to the existing instance and terminate ourselves. + */ + SpawnedFromWChat = false; + + if (RA95AlreadyRunning) { //Set in the DDEServer constructor + //MessageBox (NULL, "Error - attempt to restart Red Alert 95 when already running.", "Red Alert", MB_ICONEXCLAMATION|MB_OK); + + HWND ccwindow; + ccwindow = FindWindow (WINDOW_NAME, WINDOW_NAME); + if (ccwindow) { + SetForegroundWindow ( ccwindow ); + ShowWindow ( ccwindow, SW_RESTORE ); + } + + return (EXIT_SUCCESS); + } + + +#endif +//printf("in program.\n");getch(); +//printf("ram free = %ld\n",Ram_Free(MEM_NORMAL));getch(); +#ifdef WIN32 + if (Ram_Free(MEM_NORMAL) < 7000000) { +#else + + void *temp_mem = malloc (13*1024*1024); + if (temp_mem) { + free (temp_mem); + }else{ + + //if (Ram_Free(MEM_NORMAL) < 13000000) { +// if (Ram_Free(MEM_NORMAL) < 3500000) { +#endif + printf (TEXT_NO_RAM); + +#ifndef WIN32 + printf (TEXT_USE_START_MENU); + getch(); +#endif + +#if (0) + + /* + ** Take a stab at finding out how much memory there is available. + */ + + for ( int mem = 13*1024*1024 ; mem >0 ; mem -=1024 ) { + temp_mem = malloc (mem); + if (temp_mem){ + free (temp_mem); + printf ("Memory available: %d", mem); + break; + } + } + + getch(); +#endif //(0) + return(EXIT_FAILURE); + } + +#ifdef WIN32 + + if (strstr(command_line, "f:\\projects\\c&c0") != NULL || + strstr(command_line, "F:\\PROJECTS\\C&C0") != NULL) { + MessageBox(0, "Playing off of the network is not allowed.", "Red Alert", MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } + + int argc; //Command line argument count + unsigned command_scan; + char command_char; + char * argv[20]; //Pointers to command line arguments + char path_to_exe[132]; + + ProgramInstance = instance; + + /* + ** Get the full path to the .EXE + */ + GetModuleFileName (instance, &path_to_exe[0], 132); + + /* + ** First argument is supposed to be a pointer to the .EXE that is running + ** + */ + argc=1; //Set argument count to 1 + argv[0]=&path_to_exe[0]; //Set 1st command line argument to point to full path + + /* + ** Get pointers to command line arguments just like if we were in DOS + ** + ** The command line we get is cr/zero? terminated. + ** + */ + + command_scan=0; + + do { + /* + ** Scan for non-space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ) { + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char!=' ' && command_char != 0 && command_char!=13 ); + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + +#endif //WIN32 + + /* + ** Remember the current working directory and drive. + */ + unsigned olddrive; + char oldpath[PATH_MAX]; + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + /* + ** Change directory to the where the executable is located. Handle the + ** case where there is no path attached to argv[0]. + */ + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + _splitpath(argv[0], drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = ('A' + olddrive)-1; + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); + +#ifdef WOLAPI_INTEGRATION + // Look for special wolapi install program, used after the patch to version 3, to install "Shared Internet Components". + WIN32_FIND_DATA wfd; + HANDLE hWOLSetupFile = FindFirstFile( "wolsetup.exe", &wfd ); + bool bWOLSetupFile = ( hWOLSetupFile != INVALID_HANDLE_VALUE ); +// if( bWOLSetupFile ) +// debugprint( "Found wolsetup.exe\n" ); + FindClose( hWOLSetupFile ); + // Look for special registry entry that tells us when the setup exe has done its thing. + HKEY hKey; + RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ); + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WolapiInstallComplete", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) == ERROR_SUCCESS ) + { +// debugprint( "Found WolapiInstallComplete in registry\n" ); + // Setup has finished. Delete the setup exe and remove reg key. + if( bWOLSetupFile ) + { + if( DeleteFile( "wolsetup.exe" ) ) + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + else + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + RegCloseKey( hKey ); + + // I've been having problems getting the patch to delete "conquer.eng", which is present in the game + // directory for 1.08, but which must NOT be present for this version (Aftermath mix files provide the + // string overrides that the 1.08 separate conquer.eng did before Aftermath). + // Delete conquer.eng if it's found. + if( FindFirstFile( "conquer.eng", &wfd ) != INVALID_HANDLE_VALUE ) + DeleteFile( "conquer.eng" ); + +#endif + + if (Parse_Command_Line(argc, argv)) { + +#if(TEN) + // + // Only allow the TEN version of the game to run if the TEN + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_TEN) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for TEN\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for TEN\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // TEN + +#if(MPATH) + // + // Only allow the MPATH version of the game to run if the MPATH + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_MPATH) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for MPATH\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for MPATH\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // MPATH + +#ifdef WIN32 + WindowsTimer = new WinTimerClass(60, FALSE); + + int time_test = WindowsTimer->Get_System_Tick_Count(); + Sleep (1000); + if (WindowsTimer->Get_System_Tick_Count() == time_test){ + MessageBox(0, TEXT_ERROR_TIMER, TEXT_SHORT_TITLE, MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } +#else //WIN32 + Init_Timer_System(60, true); +#endif //WIN32 + RawFileClass cfile(CONFIG_FILE_NAME); + +#ifndef WIN32 + Install_Keyboard_Interrupt(Get_RM_Keyboard_Address(), Get_RM_Keyboard_Size()); +#endif //WIN32 + +#ifdef NEVER + Keyboard->IsLibrary = true; + if (Debug_Flag) { + Keyboard_Attributes_On(DEBUGINT); + } +#endif + + Keyboard = new KeyboardClass(); + +#ifdef WIN32 + /* + ** If there is loads of memory then use uncompressed shapes + */ + Check_Use_Compressed_Shapes(); +#endif + + /* + ** If there is not enough disk space free, don't allow the product to run. + */ + if (Disk_Space_Available() < INIT_FREE_DISK_SPACE) { + +#ifdef WIN32 + char disk_space_message [512]; + sprintf (disk_space_message, TEXT_CRITICALLY_LOW, (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + int reply = MessageBox(NULL, disk_space_message, TEXT_SHORT_TITLE, MB_ICONQUESTION|MB_YESNO); + if (reply == IDNO) { + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); + } +#else + printf(TEXT_INSUFFICIENT); + printf(TEXT_MUST_HAVE, INIT_FREE_DISK_SPACE / (1024 * 1024)); + printf("\n"); + if (Keyboard) Keyboard->Get(); +// Keyboard::IsLibrary = false; + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); + return(EXIT_FAILURE); +#endif + } + + if (cfile.Is_Available()) { + + Read_Private_Config_Struct(cfile, &NewConfig); + +#ifdef WIN32 + + /* + ** Set the options as requested by the ccsetup program + */ + Read_Setup_Options( &cfile ); + + Create_Main_Window( instance , command_show , ScreenWidth , ScreenHeight ); + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); +#else //WIN32 + if (!Debug_Quiet) { + Audio_Init(NewConfig.DigitCard, + NewConfig.Port, + NewConfig.IRQ, + NewConfig.DMA, + PLAYBACK_RATE_NORMAL, +// (NewConfig.Speed) ? PLAYBACK_RATE_SLOW : PLAYBACK_RATE_NORMAL, + NewConfig.BitsPerSample, +// 4, + (Get_CPU() < 5) ? 3 : 5, +// (NewConfig.Speed) ? 3 : 5, + NewConfig.Reverse); + SoundOn = true; + } else { + Audio_Init(0, -1, -1, -1, PLAYBACK_RATE_NORMAL, 8, 5, false); + } +#endif //WIN32 + + +#ifdef WIN32 + #ifdef MPEGMOVIE // Denzil 6/10/98 + if (!InitDDraw()) + return (EXIT_FAILURE); + #else + BOOL video_success = FALSE; + /* + ** Set 640x400 video mode. If its not available then try for 640x480 + */ + if (ScreenHeight == 400) { + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)) { + video_success = TRUE; + } else { + if (Set_Video_Mode (MainWindow, ScreenWidth, 480, 8)) { + video_success = TRUE; + ScreenHeight = 480; + } + } + } else { + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)) { + video_success = TRUE; + } + } + + if (!video_success) { + MessageBox(MainWindow, TEXT_VIDEO_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + //if (Palette) delete Palette; + return (EXIT_FAILURE); + } + + if (ScreenWidth==320) { + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + ModeXBuff.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + } else { + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + + /* + ** Check that we really got a video memory page. Failure is fatal. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) { + /* + ** Aaaarrgghh! + */ + WWDebugString(TEXT_DDRAW_ERROR);WWDebugString("\n"); + MessageBox(MainWindow, TEXT_DDRAW_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + return (EXIT_FAILURE); + } + + /* + ** If we have enough left then put the hidpage in video memory unless... + ** + ** If there is no blitter then we will get better performance with a system + ** memory hidpage + ** + ** Use a system memory page if the user has specified it via the ccsetup program. + */ + unsigned video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + if (video_memory < ScreenWidth*ScreenHeight || + (! (video_capabilities & VIDEO_BLITTER)) || + (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) || + !VideoBackBufferAllowed) { + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + //HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)GBC_VIDEOMEM); + + /* + ** Make sure we really got a video memory hid page. If we didnt then things + ** will run very slowly. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) { + + /* + ** Oh dear, big trub. This must be an IBM crAptiva or something similarly cruddy. + ** We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } + } + + ScreenHeight = 400; + + if (VisiblePage.Get_Height() == 480) { + SeenBuff.Attach(&VisiblePage, 0, 40, 640, 400); + HidPage.Attach(&HiddenPage, 0, 40, 640, 400); + } else { + SeenBuff.Attach(&VisiblePage, 0, 0, 640, 400); + HidPage.Attach(&HiddenPage, 0, 0, 640, 400); + } + #endif // MPEGMOVIE - Denzil 6/10/98 + + Options.Adjust_Variables_For_Resolution(); + + /* + ** Install the memory error handler + */ + Memory_Error = &Memory_Error_Handler; + + WindowList[0][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[0][WINDOWHEIGHT] = SeenBuff.Get_Height(); + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[WINDOW_EDITOR][WINDOWHEIGHT]= SeenBuff.Get_Height(); + + WWMouse = new WWMouseClass(&SeenBuff, 48, 48); + MouseInstalled = TRUE; + + CDFileClass::Set_CD_Drive (CDList.Get_First_CD_Drive()); + +#else //WIN32 + + Options.Adjust_Variables_For_Resolution(); + if (!Special.IsFromInstall) { + BlackPalette.Set(); +// Set_Palette(Palette); + Set_Video_Mode(MCGA_MODE); + } + MouseInstalled = Install_Mouse(32, 24, 320, 200); +#endif //WIN32 + + /* + ** See if we should run the intro + */ + INIClass ini; + ini.Load(cfile); + + /* + ** Check for forced intro movie run disabling. If the conquer + ** configuration file says "no", then don't run the intro. + ** Don't do this for TEN & MPath. + */ + if (!Special.IsFromInstall && Session.Type != GAME_TEN && + Session.Type != GAME_MPATH) { + Special.IsFromInstall = ini.Get_Bool("Intro", "PlayIntro", true); + } + SlowPalette = ini.Get_Bool("Options", "SlowPalette", false); + + /* + ** Regardless of whether we should run it or not, here we're + ** gonna change it to say "no" in the future. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + ini.Put_Bool("Intro", "PlayIntro", false); + ini.Save(cfile); + } + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** If WChat has been trying to send us a game start packet then receive it and + ** flag that we were spawned from WCHat so we dont play the into movie. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + }else{ + //Check_From_WChat("C&CSPAWN.INI"); + //if (Special.IsFromWChat){ + // DDEServer.Disable(); + //} + } +#endif //WIN32 +#endif + /* + ** If the intro is being run for the first time, then don't + ** allow breaking out of it with the key. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + } + + Memory_Error_Exit = Print_Error_End_Exit; + + Main_Game(argc, argv); + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if (MpgSettings != NULL) + delete MpgSettings; + + #ifdef MCIMPEG + if (MciMovie != NULL) + delete MciMovie; + #endif + #endif + +#ifdef WIN32 + VisiblePage.Clear(); + HiddenPage.Clear(); +#else //WIN32 + SeenPage.Clear(); + Set_Video_Mode(RESET_MODE); +#endif //WIN32 + Memory_Error_Exit = Print_Error_Exit; + +#ifdef WIN32 + /* + ** Flag that this is a clean shutdown (not killed with Ctrl-Alt-Del) + */ + ReadyToQuit = 1; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + }while (ReadyToQuit == 1); + + return (EXIT_SUCCESS); + +#else //WIN32 + Remove_Mouse(); + Sound_End(); +#endif //WIN32 + } else { + puts(TEXT_SETUP_FIRST); + Keyboard->Get(); + } + +#ifdef WIN32 + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + +#else //WIN32 + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); +#endif //WIN32 + } + /* + ** Restore the current drive and directory. + */ +#ifndef WIN32 + _dos_setdrive(olddrive, &drivecount); + chdir(oldpath); +#endif //WIN32 + return(EXIT_SUCCESS); +} + + +/* Initialize DirectDraw and surfaces */ +bool InitDDraw(void) + { + DDSCAPS surface_capabilities; + BOOL video_success = FALSE; + + /* Set 640x400 video mode. If its not available then try for 640x480 */ + if (ScreenHeight == 400) + { + if (Set_Video_Mode(MainWindow, ScreenWidth, ScreenHeight, 8)) + { + video_success = TRUE; + } + else + { + if (Set_Video_Mode(MainWindow, ScreenWidth, 480, 8)) + { + video_success = TRUE; + ScreenHeight = 480; + } + } + } + else + { + if (Set_Video_Mode(MainWindow, ScreenWidth, ScreenHeight, 8)) + { + video_success = TRUE; + } + } + + if (!video_success) + { + MessageBox(MainWindow, TEXT_VIDEO_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + + if (WindowsTimer) + delete WindowsTimer; + + return false; + } + + if (ScreenWidth == 320) + { + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + ModeXBuff.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + } + else + { + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + + /* Check that we really got a video memory page. Failure is fatal. */ + memset(&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + /* Aaaarrgghh! */ + WWDebugString(TEXT_DDRAW_ERROR);WWDebugString("\n"); + MessageBox(MainWindow, TEXT_DDRAW_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + + if (WindowsTimer) + delete WindowsTimer; + + return false; + } + + /* If we have enough left then put the hidpage in video memory unless... + * + * If there is no blitter then we will get better performance with a system + * memory hidpage + * + * Use a system memory page if the user has specified it via the ccsetup program. + */ + unsigned video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + + if (video_memory < ScreenWidth * ScreenHeight + || (!(video_capabilities & VIDEO_BLITTER)) + || (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) + || !VideoBackBufferAllowed) + { + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + } + else + { + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + /* Make sure we really got a video memory hid page. If we didnt then things + * will run very slowly. + */ + memset(&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + /* Oh dear, big trub. This must be an IBM crAptiva or something similarly cruddy. + * We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)0); + } + else + { + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } + } + + ScreenHeight = 400; + + if (VisiblePage.Get_Height() == 480) + { + SeenBuff.Attach(&VisiblePage, 0, 40, 640, 400); + HidPage.Attach(&HiddenPage, 0, 40, 640, 400); + } + else + { + SeenBuff.Attach(&VisiblePage, 0, 0, 640, 400); + HidPage.Attach(&HiddenPage, 0, 0, 640, 400); + } + + return true; + } + + +/*********************************************************************************************** + * Prog_End -- Cleans up library systems in prep for game exit. * + * * + * This routine should be called before the game terminates. It handles cleaning up * + * library systems so that a graceful return to the host operating system is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +void __cdecl Prog_End(void) +{ + Sound_End(); + if (WWMouse) { + delete WWMouse; + WWMouse = NULL; + } + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } +} +#else //WIN32 + +void Prog_End(void) +{ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + NullModem.Change_IRQ_Priority(0); + } + + Set_Video_Mode(RESET_MODE); + Remove_Keyboard_Interrupt(); + Remove_Mouse(); + Sound_End(); + Remove_Timer_System(); +} +#endif //WIN32 + + +void Print_Error_End_Exit(char * string) +{ + Prog_End(); + printf( "%s\n", string ); + exit(1); +} + + +void Print_Error_Exit(char * string) +{ + printf( "%s\n", string ); + exit(1); +} + + + + + + + +/*********************************************************************************************** + * Emergency_Exit -- Function to call when we want to exit unexpectedly. * + * Use this function instead of exit(n) so everything is properly cleaned up.* + * * + * * + * INPUT: Code to return to the OS * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/13/97 1:32AM ST : Created * + *=============================================================================================*/ +void Emergency_Exit ( int code ) +{ + +#ifdef WIN32 + + /* + ** Clear out the video buffers so we dont glitch when we lose focus + */ + VisiblePage.Clear(); + HiddenPage.Clear(); + BlackPalette.Set(); + Memory_Error_Exit = Print_Error_Exit; + + /* + ** Flag that this is an emergency shut down - not a clean shutdown but + ** not killed with Ctrl-Alt-Del either. + */ + ReadyToQuit = 3; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + }while (ReadyToQuit == 3); + + +#else //WIN32 + /* + ** Do the DOS end + */ + Prog_End(); + +#endif //WIN32 + + exit ( code ); +} + + + + + + + + + + + +#ifdef WIN32 + +/*********************************************************************************************** + * Read_Setup_Options -- Read stuff in from the INI file that we need to know sooner * + * * + * * + * * + * INPUT: Ptr to config file class * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 4:09PM ST : Created * + * 09/30/1996 JLB : Uses INI class. * + *=============================================================================================*/ +void Read_Setup_Options( RawFileClass *config_file ) +{ + if (config_file->Is_Available()) { + + INIClass ini; + + ini.Load(*config_file); + + /* + ** Read in the boolean options + */ + VideoBackBufferAllowed = ini.Get_Bool("Options", "VideoBackBuffer", true); + AllowHardwareBlitFills = ini.Get_Bool("Options", "HardwareFills", true); + ScreenHeight = ini.Get_Bool("Options", "Resolution", false) ? 480 : 400; + + /* + ** See if an alternative socket number has been specified + */ + int socket = ini.Get_Int("Options", "Socket", 0); + if (socket >0 ) { + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + } + + /* + ** See if a destination network has been specified + */ + char netbuf [512]; + memset(netbuf, 0, sizeof(netbuf)); + char * netptr = netbuf; + bool found = ini.Get_String("Options", "DestNet", NULL, netbuf, sizeof(netbuf)); + + if (found && netptr != NULL && strlen(netbuf)) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the string, pulling off each address piece + */ + int i = 0; + char * p = strtok(netbuf,"."); + int x; + while (p != NULL) { + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + } + } +} + +void Get_OS_Version (void) +{ + + WindowsNT = ((GetVersion() & 0x80000000) == 0) ? true : false; + +#if (0) + OSVERSIONINFO osversion; + if ( GetVersionEx (&osversion) ){ + WindowsNT = (osversion.dwPlatformId == VER_PLATFORM_WIN32_NT) ? true : false; + OutputDebugString ("RA95 - Got OS version\n"); + }else{ + OutputDebugString ("RA95 - Failed to get OS version\n"); + char fuck [128]; + sprintf (fuck,"RA95 - Error code is %d\n", GetLastError()); + OutputDebugString (fuck); + + } +#endif //(0) + +} + + +#endif //WIN32 + + diff --git a/CODE/STATBTN.CPP b/CODE/STATBTN.CPP new file mode 100644 index 0000000..e349468 --- /dev/null +++ b/CODE/STATBTN.CPP @@ -0,0 +1,271 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/STATBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "statbtn.h" + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(unsigned , char const * text, TextPrintType style, int x, int y, int w, int h) : + GadgetClass(x, y, w, h, FlagEnum(0)), + String(NULL), + PrintFlags(style) +{ + /* + ** Make a duplicate of the string to display. + */ + Set_Text(text, false); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String); + } + if (h == -1) { + Height = FontHeight; + } + } +} + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(void) : + GadgetClass(0, 0, 0, 0, FlagEnum(0)), + String(NULL), + PrintFlags(TPF_8POINT) +{ +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int StaticButtonClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Set_Text(char const * text, bool resize) +{ + if (String != NULL) { + delete [] String; + String = NULL; + } + + if (text != NULL) { + String = new char[strlen(text)+1]; + if (String != NULL) { + strcpy(String, text); + } + } + + Flag_To_Redraw(); + if (resize && String != NULL) { + Draw_Background(); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String); + Height = FontHeight + FontYSpacing; + Background = Buffer(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Background(void) +{ + /* + ** If the background hasn't been recorded from the buffer, then + ** allocate and record the background image now. + */ + if (Background.Get_Buffer() == NULL && Width > 0 && Height > 0) { + new(&Background) Buffer(Width*Height); + if (Background.Get_Buffer() != NULL) { + LogicPage->To_Buffer(X, Y, Width, Height, Background, Background.Get_Size()); + } + } + + /* + ** If there is a background image present, then restore it to the buffer now. + */ + if (Background.Get_Buffer() != NULL && LogicPage->Lock()) { + Buffer_To_Page(X, Y, Width, Height, Background, *LogicPage); + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Text(char const * text) +{ + /* + ** Display the text. + */ + if (String != NULL) { + int x = X; + + if (PrintFlags & TPF_CENTER) { + x += Width/2; + } + if (PrintFlags & TPF_RIGHT) { + x += Width-1; + } + + Fancy_Text_Print(text, x, Y, GadgetClass::Get_Color_Scheme(), TBLACK, PrintFlags); + } +} diff --git a/CODE/STATBTN.H b/CODE/STATBTN.H new file mode 100644 index 0000000..8c76fae --- /dev/null +++ b/CODE/STATBTN.H @@ -0,0 +1,75 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/STATBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : STATBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STATBTN_H +#define STATBTN_H + +#include "gadget.h" +#include "buff.h" + +class StaticButtonClass : public GadgetClass +{ + public: + StaticButtonClass(void); + StaticButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const * text, bool resize = false); + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + /* + ** If a background is to be preserved for this button, then this will point to + ** a buffer that holds a pristine background image. + */ + Buffer Background; + + /* + ** This points to a copy of the string that is used for the button's text. + */ + char * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/CODE/STATS.CPP b/CODE/STATS.CPP new file mode 100644 index 0000000..0fd0a38 --- /dev/null +++ b/CODE/STATS.CPP @@ -0,0 +1,912 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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 * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 05/29/1996 * + * * + * Last Update : May 29th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Internet game statistics to collect and upload to the server * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "packet.h" +#include "ccdde.h" + +#define FIELD_PACKET_TYPE "TYPE" +#define FIELD_GAME_ID "IDNO" +#define FIELD_START_CREDITS "CRED" +#define FIELD_BASES "BASE" +#define FIELD_TIBERIUM "TIBR" +#define FIELD_CRATES "CRAT" +#define FIELD_AI_PLAYERS "AIPL" +#define FIELD_CAPTURE_THE_FLAG "FLAG" +#define FIELD_START_UNIT_COUNT "UNIT" +#define FIELD_TECH_LEVEL "TECH" +#define FIELD_SCENARIO "SCEN" +#define FIELD_COMPLETION "CMPL" +#define FIELD_START_TIME "TIME" +#define FIELD_GAME_DURATION "DURA" +#define FIELD_FRAME_RATE "AFPS" +#define FIELD_SPEED_SETTING "SPED" +#define FIELD_GAME_VERSION "VERS" +#define FIELD_GAME_BUILD_DATE "DATE" +#define FIELD_COVERT_PRESENT "COVT" +#define FIELD_CPU_TYPE "PROC" +#define FIELD_MEMORY "MEMO" +#define FIELD_VIDEO_MEMORY "VIDM" +#define FIELD_SHADOW_REGROWS "SHAD" + +#ifdef WOLAPI_INTEGRATION +#define FIELD_HOSTORNOT "SDFX" +#define FIELD_TOURNAMENT "TRNY" +#define FIELD_NUM_INITIAL_PLAYERS "NUMP" +#define FIELD_NUM_REMAINING_PLAYERS "REMN" +#define FIELD_DISCONNECT_PINGS "PING" +#define FIELD_COMPUTERTOOKOVER "QUIT" +//#define FIELD_HARDWARE_GUID "GUID" +#define FIELD_PLAYER1_IP "ADR1" +#define FIELD_PLAYER2_IP "ADR2" +#endif + +// ajw The following were never used (thank god). +#define FIELD_PLAYER1_HANDLE "NAM1" +#define FIELD_PLAYER2_HANDLE "NAM2" +#define FIELD_PLAYER1_TEAM "SID1" +#define FIELD_PLAYER2_TEAM "SID2" +#define FIELD_PLAYER1_COLOR "COL1" +#define FIELD_PLAYER2_COLOR "COL2" +#define FIELD_PLAYER1_CREDITS "CRD1" +#define FIELD_PLAYER2_CREDITS "CRD2" + +#define FIELD_PLAYER1_UNITS_LEFT "UNL1" +#define FIELD_PLAYER2_UNITS_LEFT "UNL2" +#define FIELD_PLAYER1_INFANTRY_LEFT "INL1" +#define FIELD_PLAYER2_INFANTRY_LEFT "INL2" +#define FIELD_PLAYER1_PLANES_LEFT "PLL1" +#define FIELD_PLAYER2_PLANES_LEFT "PLL2" +#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1" +#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2" +#define FIELD_PLAYER1_VESSELS_LEFT "VSL1" +#define FIELD_PLAYER2_VESSELS_LEFT "VSL2" + +#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1" +#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2" +#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1" +#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2" +#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1" +#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2" +#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1" +#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2" +#define FIELD_PLAYER1_VESSELS_BOUGHT "VSB1" +#define FIELD_PLAYER2_VESSELS_BOUGHT "VSB2" + +#define FIELD_PLAYER1_UNITS_KILLED "UNK1" +#define FIELD_PLAYER2_UNITS_KILLED "UNK2" +#define FIELD_PLAYER1_INFANTRY_KILLED "INK1" +#define FIELD_PLAYER2_INFANTRY_KILLED "INK2" +#define FIELD_PLAYER1_PLANES_KILLED "PLK1" +#define FIELD_PLAYER2_PLANES_KILLED "PLK2" +#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1" +#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2" +#define FIELD_PLAYER1_VESSELS_KILLED "VSK1" +#define FIELD_PLAYER2_VESSELS_KILLED "VSK2" + +#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1" +#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2" + +#define FIELD_PLAYER1_CRATES_FOUND "CRA1" +#define FIELD_PLAYER2_CRATES_FOUND "CRA2" +#define FIELD_PLAYER1_HARVESTED "HRV1" +#define FIELD_PLAYER2_HARVESTED "HRV2" + + +#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50 +#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51 + +// Note: These enums match those in the game results server code. +enum { + COMPLETION_CONNECTION_LOST, + COMPLETION_PLAYER_1_WON, + COMPLETION_PLAYER_1_WON_BY_RESIGNATION, + COMPLETION_PLAYER_1_WON_BY_DISCONNECTION, + COMPLETION_PLAYER_2_WON, + COMPLETION_PLAYER_2_WON_BY_RESIGNATION, + COMPLETION_PLAYER_2_WON_BY_DISCONNECTION, +#ifdef FIXIT_VERSION_3 // Stalemate games. + COMPLETION_WASH = 64, +#endif +}; + + +extern unsigned long PlanetWestwoodGameID; +extern HINSTANCE ProgramInstance; +extern unsigned long PlanetWestwoodStartTime; + +extern "C" char CPUType; + + +bool GameTimerInUse = false; +TimerClass GameTimer; +long GameEndTime; +void *PacketLater = NULL; + +#include "WolDebug.h" + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +extern bool bReconnectDialogCancelled; +#endif + +/*********************************************************************************************** + * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/29/96 12:38PM ST : Created * + *=============================================================================================*/ + +void Send_Statistics_Packet(void) +{ +// debugprint( "Stats: Send_Statistics_Packet() called.\n" ); +#ifndef INTERNET_OFF // Denzil 5/4/98 + +#ifdef WOLAPI_INTEGRATION + if( !pWolapi ) // Should no longer ever happen. + return; +#endif + + PacketClass stats; + HouseClass *player; + static int packet_size; + int index; + bool packet_later = false; // Should the packet be sent later + void *packet; + + static char field_player_handle[5] = { "NAM?" }; + static char field_player_team[5] = { "SID?" }; + static char field_player_color[5] = { "COL?" }; + static char field_player_credits[5] = { "CRD?" }; + static char field_player_units_left[5] = { "UNL?" }; + static char field_player_infantry_left[5] = { "INL?" }; + static char field_player_planes_left[5] = { "PLL?" }; + static char field_player_buildings_left[5] = { "BLL?" }; + static char field_player_vessels_left[5] = { "VSL?" }; + static char field_player_units_bought[5] = { "UNB?" }; + static char field_player_infantry_bought[5] = { "INB?" }; + static char field_player_planes_bought[5] = { "PLB?" }; + static char field_player_buildings_bought[5] = { "BLB?" }; + static char field_player_vessels_bought[5] = { "VSB?" }; + static char field_player_units_killed[5] = { "UNK?" }; + static char field_player_infantry_killed[5] = { "INK?" }; + static char field_player_planes_killed[5] = { "PLK?" }; + static char field_player_buildings_killed[5] = { "BLK?" }; + static char field_player_vessels_killed[5] = { "VSK?" }; + static char field_player_buildings_captured[5] = { "BLC?" }; + static char field_player_crates_found[5] = { "CRA?" }; + static char field_player_harvested[5] = { "HRV?" }; + + static char *houses[] = { + "SPA", + "GRE", + "USS", + "ENG", + "ITA", + "GER", + "FRA", + "TKY", + "GUD", + "BAD", + "CIV", + "JP ", + "M01", + "M02", + "M03", + "M04", + "M05", + "M06", + "M07", + "M08" + }; + + if (!PacketLater){ + + /* + ** Field to identify this as C&C 95 internet game statistics packet + */ +#ifdef WOLAPI_INTEGRATION + if( pWolapi->bGameServer ) + { + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)0 ); // Reversed meaning of this for Neal. + }else{ + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)1 ); + } +#else + if (Server){ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO); + }else{ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO); + } +#endif + + /* + ** Game ID. A unique game identifier assigned by WChat. + */ + stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID); + +#ifdef WOLAPI_INTEGRATION + + // Number of players initially in game. + stats.Add_Field( FIELD_NUM_INITIAL_PLAYERS, (unsigned long)pWolapi->GameInfoCurrent.iPlayerCount ); +//debugprint( "Stats: number of initial players is %i\n", pWolapi->GameInfoCurrent.iPlayerCount ); + + // Number of players remaining in game. Not sure of what use this will be statistically... + stats.Add_Field( FIELD_NUM_REMAINING_PLAYERS, (unsigned long)Session.Players.Count() ); +//debugprint( "Stats: number of remaining players is %i\n", Session.Players.Count() ); + + // Whether or not this was a tournament game. + stats.Add_Field( FIELD_TOURNAMENT, (unsigned char)( pWolapi->GameInfoCurrent.bTournament ? 1 : 0 ) ); + +// ajw This is now in WOLAPI... +// // A unique value that identifies the machine that the game was played on. +// HW_PROFILE_INFO hwinfo; +// ::GetCurrentHwProfile( &hwinfo ); +// stats.Add_Field( FIELD_HARDWARE_GUID, hwinfo.szHwProfileGuid ); +#endif + + /* + ** Start credits. + */ + stats.Add_Field(FIELD_START_CREDITS, (unsigned long)Session.Options.Credits); + + /* + ** Bases (On/Off) + */ + stats.Add_Field(FIELD_BASES, Session.Options.Bases ? "ON" : "OFF"); + + /* + ** Tiberium (On/Off) + */ + stats.Add_Field(FIELD_TIBERIUM, Session.Options.Tiberium ? "ON" : "OFF"); + + /* + ** Crates (On/Off) + */ + stats.Add_Field(FIELD_CRATES, Session.Options.Goodies ? "ON" : "OFF"); + + /* + ** AI Players (On/Off) + */ + stats.Add_Field(FIELD_AI_PLAYERS, (unsigned long) Session.Options.AIPlayers); + + /* + ** Shadow regrowth enabled + */ + stats.Add_Field(FIELD_SHADOW_REGROWS, Special.IsShadowGrow ? "ON" : "OFF" ); + + /* + ** Capture the flag mode (On/Off) + */ + stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF"); + + /* + ** Start unit count + */ + stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)Session.Options.UnitCount); + + /* + ** Tech level. + */ + stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel); + + /* + ** Scenario + */ +#if (1) + + stats.Add_Field(FIELD_SCENARIO, Session.Options.ScenarioDescription); + +#else //(1) + char fname[128]; + char namebuffer[40]; + char *abuffer = (char *)_ShapeBuffer; + memset(abuffer, '\0', _ShapeBufferSize); + sprintf(fname,"%s.INI",Scen.ScenarioName); + CCFileClass fileo; + fileo.Set_Name (fname); + fileo.Read(abuffer, _ShapeBufferSize-1); + fileo.Close(); + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer); + stats.Add_Field(FIELD_SCENARIO, namebuffer); + //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]); +#endif //(1) + +#ifdef WOLAPI_INTEGRATION + // Completion status is set for Tournament games only - ajw. + if( pWolapi->GameInfoCurrent.bTournament ) + { +#endif + + /* + ** Game completion status. + ** + ** Connection lost. + ** Player 1 won + ** Player 2 won + ** Player 1 won by resignation + ** Player 2 won by resignation + ** Player 1 aborted + ** Player 2 aborted + ** + ** Game was a draw + */ + HouseClass *player1 = NULL; + HouseClass *player2 = NULL; + for (int h=0 ; hIsHuman){ + if (player1){ + player2 = ptr; + break; + }else{ + player1 = ptr; + } + } + } + + int completion = -1; + + if (player1 && player2){ // Can this ever fail? ajw +#ifdef FIXIT_VERSION_3 + // Send IP addresses of both players. + NetNumType net; + NetNodeType node; + char szIPAddress[ 30 ]; + Session.Players[ 0 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + if( strcmp( szIPAddress, "255.255.255.255" ) == 0 ) + { + + // Ok. It's not set. Let's try to get it ourselves... + char szHostName[ 512 ]; + int iRes = gethostname( szHostName, 512 ); + if( iRes != SOCKET_ERROR ) // else forget about trying + { +// debugprint( "gethostname got me %s\n", szHostName ); + struct hostent* pHostent = gethostbyname( szHostName ); + if( pHostent ) // else forget about trying + { + int i = 0; + int* piAddress = (int*)pHostent->h_addr_list[ i ]; + while( piAddress ) + { + // There is a non-null value for this h_addr_list entry. + char szAsciiIP[ 30 ]; + strcpy( szAsciiIP, inet_ntoa( *( (struct in_addr*)piAddress ) ) ); + // We have an address in the right form. + // Now, is it an address in a private network? If so we should ignore it. + unsigned char q1 = ( (char*)piAddress )[ 0 ]; // First digit. + unsigned char q2 = ( (char*)piAddress )[ 1 ]; // Second digit. +// debugprint( "ip: %s\n", szAsciiIP ); + if( q1 == 10 || ( q1 == 172 && ( q2 >= 16 && q2 <= 31 ) ) || ( q1 == 192 && q2 == 168 ) ) + { + // This is a private network address - ignore it and go on to next. + } + else + { + strcpy( szIPAddress, szAsciiIP ); + break; + } + piAddress = (int*)pHostent->h_addr_list[ ++i ]; + } + } +// else +// debugprint( "gethostbyname failed. Error %i\n", WSAGetLastError() ); + } +// else +// debugprint( "gethostname failed with %i, error %i\n", iRes, WSAGetLastError() ); + } + stats.Add_Field( FIELD_PLAYER1_IP, (char*)szIPAddress ); + Session.Players[ 1 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + stats.Add_Field( FIELD_PLAYER2_IP, (char*)szIPAddress ); +#endif +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + completion = COMPLETION_WASH; + } + else + { +#endif + if (ConnectionLost){ +#ifdef WOLAPI_INTEGRATION + if( bReconnectDialogCancelled ) + { + if( Session.Players[ 0 ]->Player.ID == HOUSE_MULTI1 ) + // I am player1. + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + else + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + else + { + completion = COMPLETION_CONNECTION_LOST; + if( pWolapi->bDisconnectPingingCompleted ) + { + char szPingResult[ 8 ]; // Format is "x/y a/b", e.g., "3/5 4/5" + pWolapi->DisconnectPingResultsString( szPingResult ); + stats.Add_Field( FIELD_DISCONNECT_PINGS, (char*)szPingResult ); + } +// else +// debugprint( "Stats: bDisconnectPingingCompleted is false! Should be finished!!!!!!!!!!!!!!!\n" ); + } +#else + completion = COMPLETION_CONNECTION_LOST; +#endif + }else{ + + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + + + if (player2->IsDefeated){ + /* + ** Player 1 won. Find out how. + */ + completion = COMPLETION_PLAYER_1_WON; + if (player2->IsResigner){ + completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION; + }else{ + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + } + + + }else{ + + if (player1->IsDefeated){ + /* + ** Player 2 won. Find out how. + */ + completion = COMPLETION_PLAYER_2_WON; + if (player1->IsResigner){ + completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION; + }else{ + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + } + } + } + } +#ifdef FIXIT_VERSION_3 // Stalemate games. + } +#endif + } + + stats.Add_Field (FIELD_COMPLETION, (char) completion); +//debugprint( "Stats: Tournament game completion value: %i\n", completion ); + +#ifdef WOLAPI_INTEGRATION + } +#endif + + + /* + ** Game start time (GMT or Pacific?) + ** + ** Passed from WChat + */ + stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime); + + /* + ** Game duration (seconds). + */ + stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60); + + /* + ** Avg. frame rate. + */ +#ifdef FIXIT_IP_CRASH + long divisor = GameEndTime / 60; + if (divisor != 0) { + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); + } else { + stats.Add_Field (FIELD_FRAME_RATE, 0l); + } +#else + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); +#endif + + + /* + ** CPU type + */ + stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType); + + /* + ** Memory + */ + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys); + + /* + ** Video memory + */ + DDCAPS video_capabilities; + long video_memory; + + if (DirectDrawObject){ + video_capabilities.dwSize = sizeof (video_capabilities); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + video_memory = video_capabilities.dwVidMemTotal; + video_memory += 1024*1024 -1; + video_memory &= 0xfff00000; + stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory); + } + } + + /* + ** Game speed setting. + */ + stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed); + + /* + ** Red Alert version/build date + */ + char version[128]; + sprintf (version, "V%s", VerNum.Version_Name() ); + stats.Add_Field (FIELD_GAME_VERSION, (char*)version); + + char path_to_exe[280]; + FILETIME write_time; //File time is 64 bits + + GetModuleFileName (ProgramInstance, path_to_exe, 280); + RawFileClass file; + file.Set_Name(path_to_exe); + file.Open(); + HANDLE handle = file.Get_File_Handle(); + + if (handle != INVALID_HANDLE_VALUE){ + if (GetFileTime (handle, NULL, NULL, &write_time)){ + write_time.dwLowDateTime = htonl (write_time.dwLowDateTime); + write_time.dwHighDateTime = htonl (write_time.dwHighDateTime); + stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time)); + } + } + + /* + ** Covert installed? (Yes/No) + */ + //stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present()); + + /* + ** Build the player specific statistics + ** + */ +#ifdef WOLAPI_INTEGRATION + for (int house = 0 ; house < 8 ; house++){ +#else + for (int house = 0 ; house < 2 ; house++){ +#endif + player = HouseClass::As_Pointer((HousesType) (house + HOUSE_MULTI1)); + +#ifdef WOLAPI_INTEGRATION + if( !player ) + continue; +#endif + + /* + ** Player handle. + */ + field_player_handle[3] = '1' + (char)house; +#ifdef WOLAPI_INTEGRATION + stats.Add_Field (field_player_handle, (char*) player->InitialName); +//debugprint( "Stats: Player %i name %s\n", house, (char*) player->InitialName ); +//debugprint( "Stats: Player %i ending name %s\n", house, (char*) player->IniName ); +#else + stats.Add_Field (field_player_handle, (char*) player->IniName); +#endif + +#ifdef WOLAPI_INTEGRATION + // Whether or not this player was taken over by the computer, due to his quitting the game. + if( strcmp( player->IniName, player->InitialName ) ) + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)1 ); + else + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)0 ); +#endif + /* + ** Player team. (NOD or GDI) + */ + field_player_team[3] = '1' + (char)house; + stats.Add_Field (field_player_team, houses[player->ActLike]); + + /* + ** Player color + */ + field_player_color[3] = '1' + (char)house; + stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1)); + + /* + ** Player end credits. + */ + field_player_credits[3] = '1' + (char)house; + stats.Add_Field (field_player_credits, player->Credits + player->Tiberium); + + /* + ** Number of each unit/building type built + */ + field_player_infantry_bought[3] = '1' + (char)house; + field_player_units_bought[3] = '1' + (char)house; + field_player_planes_bought[3] = '1' + (char)house; + field_player_buildings_bought[3] = '1' + (char)house; + field_player_vessels_bought[3] = '1' + (char)house; + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_bought, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + player->InfantryTotals->To_PC_Format(); + player->UnitTotals->To_PC_Format(); + player->AircraftTotals->To_PC_Format(); + player->BuildingTotals->To_PC_Format(); + + /* + ** Clear out the counts and use the space to count up the current number of units/buildings + */ + player->InfantryTotals->Clear_Unit_Total(); + player->AircraftTotals->Clear_Unit_Total(); + player->UnitTotals->Clear_Unit_Total(); + player->BuildingTotals->Clear_Unit_Total(); + player->VesselTotals->Clear_Unit_Total(); + + /* + ** Number of units remaining to player + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (player == unit->House){ + player->UnitTotals->Increment_Unit_Total (unit->Class->Type); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (player == infantry->House && !infantry->Class->IsCivilian){ + player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (player == aircraft->House){ // && aircraft->Class->Type != AIRCRAFT_CARGO){ + player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type); + } + } + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (player == building->House){ + player->BuildingTotals->Increment_Unit_Total (building->Class->Type); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + if (player == vessel->House){ + player->VesselTotals->Increment_Unit_Total (vessel->Class->Type); + } + } + + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + field_player_infantry_left[3] = '1' + (char)house; + field_player_units_left[3] = '1' + (char)house; + field_player_planes_left[3] = '1' + (char)house; + field_player_buildings_left[3] = '1' + (char)house; +#ifdef FIXIT_IP_STATS + field_player_vessels_left[3] = '1' + (char)house; +#endif + stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_left, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + + /* + ** Number of enemy units/buildings of each type destroyed. + */ + + player->DestroyedInfantry->To_Network_Format(); + player->DestroyedUnits->To_Network_Format(); + player->DestroyedAircraft->To_Network_Format(); + player->DestroyedBuildings->To_Network_Format(); + player->DestroyedVessels->To_Network_Format(); + + field_player_infantry_killed[3] = '1' + (char)house; + field_player_units_killed[3] = '1' + (char)house; + field_player_planes_killed[3] = '1' + (char)house; + field_player_buildings_killed[3] = '1' + (char)house; + field_player_vessels_killed[3] = '1' + (char)house; + + stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#ifdef FIXIT_VERSION_3 + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedVessels->Get_Unit_Count()*4); +#else + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#endif + + + /* + ** Number and type of enemy buildings captured + */ + field_player_buildings_captured[3] = '1' + (char)house; + player->CapturedBuildings->To_Network_Format(); + stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4); + + /* + ** Number of crates discovered and their contents + */ + field_player_crates_found[3] = '1' + (char)house; + player->TotalCrates->To_Network_Format(); + stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4); + + /* + ** Amount of tiberium turned into credits + */ + field_player_harvested[3] = '1' + (char)house; + stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits); + + } + + /* + ** Create the comms packet to be sent + */ + packet = stats.Create_Comms_Packet(packet_size); + +#ifndef WOLAPI_INTEGRATION // ajw - 'PacketLater' is no longer ever used. + /* + ** If a player disconnected then dont send the packet at this time - save it for later + */ + if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION + || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){ + PacketLater = packet; + return; + } +#endif + + }else{ //else for if (!PacketLater) + + /* + ** Send the packet we calculated earlier when the disconnect occurred + */ + packet = PacketLater; + PacketLater = NULL; + } + + /* + ** Send it..... + */ + const char* szGameResServer; + int iPort; + if( pWolapi->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + szGameResServer = pWolapi->szGameResServerHost2; + iPort = pWolapi->iGameResServerPort2; + } + else + { + szGameResServer = pWolapi->szGameResServerHost1; + iPort = pWolapi->iGameResServerPort1; + } + + if( *szGameResServer ) + { + if( pWolapi->pNetUtil->RequestGameresSend( szGameResServer, iPort, (unsigned char*)packet, packet_size ) != S_OK ) + //debugprint( "RequestGameresSend( %s, %i ) failed!!!\n", szGameResServer, iPort ); + ; + } + + /* + ** Save it to disk as well so I can see it + */ +// RawFileClass anotherfile ("packet.net"); +// anotherfile.Write(packet, packet_size); +//debugprint( "Wrote out packet.net\n" ); + + /* + ** Tidy up + */ + delete [] packet; + + GameStatisticsPacketSent = true; +#endif // INTERNET_OFF +} + + + +void Register_Game_Start_Time(void) +{ + + GameTimer.Set (0, true); + GameTimerInUse = true; +} + +extern void Register_Game_End_Time(void) +{ + GameEndTime = GameTimer.Time(); + GameTimerInUse = false; +} + + +#endif //WIN32 diff --git a/CODE/STD.LNT b/CODE/STD.LNT new file mode 100644 index 0000000..1807aae --- /dev/null +++ b/CODE/STD.LNT @@ -0,0 +1,102 @@ +// Watcom C, C++ (32 bit), -si4 -sp4, +// Standard lint options + +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + +//------------------------------------------------------------------ + +// Please note -- this is a representative set of error suppression +// options. Please adjust to suit your own policies +// See PC-lint for C/C++ manual (chapter LIVING WITH LINT) +// for further details. + +-e502 -e713 -e737 -eau // don't report on signed/unsigned mismatches +-e734 // allow sub-integer loss of information +-e701 -e703 // shifting int left is OK +-e537 // don't care about repeated include file +-e641 // converting enum to int is ok +-e1042 // operator ++/-- don't need class parameters +-e963 -e763 // redundant declarations are ok +-e1712 // no default constructor defined is ok +-e1704 // private constructors are ok +-e534 // ignoring return value is ok +-e732 // going from signed to unsigned parameter is ok +-e1411 // functions hiding base functions is ok +-e788 // switch with default doesn't need all values specified +-e655 -e656 // compatable enum bit and arithmetic operations are ok +-e1542 // members possibly not initialized isn't a valid warning +-e522 // calling 'new' without assignment isn't always an error + +-e1401 // uninitialized by constructor warning disabled. + + +// 32 bit integer and pointer size is four bytes. +-si4 -sp4 + +// Include directories +-ic:\projects\c&czero\code\watcom\h;..\vq\include;..\gcl510\h diff --git a/CODE/STRAW.CPP b/CODE/STRAW.CPP new file mode 100644 index 0000000..74377c1 --- /dev/null +++ b/CODE/STRAW.CPP @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/STRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Straw::Get_From -- Connect one straw segment to another. * + * Straw::Get -- Fetch some data from the straw chain. * + * Straw::~Straw -- Destructor for a straw segment. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "straw.h" +#include +#include + + +/*********************************************************************************************** + * Straw::~Straw -- Destructor for a straw segment. * + * * + * This destructor will remove this segment from the straw chain. If there were any * + * connected segments to either side, they will be joined so that the straw chain will * + * still remain linked. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Straw::~Straw(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Get_From(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Straw::Get_From -- Connect one straw segment to another. * + * * + * Use this routine to connect straw segments together. The straw segment specified as the * + * parameter will be linked to the chain such that when data is requested, it will be * + * fetched from the specified link before being processed by this link. * + * * + * INPUT: straw -- Pointer to the straw segment that data will be fetched from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Straw::Get_From(Straw * straw) +{ + if (ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + } + + ChainTo = straw; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Straw::Get -- Fetch some data from the straw chain. * + * * + * Use this routine to fetch some data from the straw. It is presumed that this routine * + * will be overridden to provide some useful functionality. At this level, the straw chain * + * merely passes the request on to the next straw in the chain. * + * * + * INPUT: source -- Pointer to the buffer to hold the requested data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number returned * + * is less than the number requested, then this indicates that the straw data has * + * been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Straw::Get(void * source, int slen) +{ + if (ChainTo != NULL) { + return(ChainTo->Get(source, slen)); + } + return(0); +} + + diff --git a/CODE/STRAW.H b/CODE/STRAW.H new file mode 100644 index 0000000..399c904 --- /dev/null +++ b/CODE/STRAW.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/STRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STRAW_H +#define STRAW_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This is a demand driven data carrier. It will retrieve the byte request by passing +** the request down the chain (possibly processing on the way) in order to fulfill the +** data request. Without being derived, this class merely passes the data through. Derived +** versions are presumed to modify the data in some useful way or monitor the data +** flow. +*/ +class Straw +{ + public: + Straw(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Straw(void); + + virtual void Get_From(Straw * pipe); + void Get_From(Straw & pipe) {Get_From(&pipe);} + virtual int Get(void * buffer, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Straw * ChainTo; + Straw * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Straw(Straw & rvalue); + Straw & operator = (Straw const & pipe); +}; + + +#endif diff --git a/CODE/STUB.CPP b/CODE/STUB.CPP new file mode 100644 index 0000000..5b02110 --- /dev/null +++ b/CODE/STUB.CPP @@ -0,0 +1,25 @@ +/* +** 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 . +*/ + +#include +#include + +void main(void) +{ + printf("Run C&C.COM.\n"); +} diff --git a/CODE/STUFF.TXT b/CODE/STUFF.TXT new file mode 100644 index 0000000..ce1d513 --- /dev/null +++ b/CODE/STUFF.TXT @@ -0,0 +1,108 @@ + + +52 Mission Editor (52)* +36 Variable difficulty settings (36)* +35 Improve manual (more detailed information) (35)* +26 SVGA graphics (or at least the option) (26) +22 Improve AI (smarter/harder/no traffic jams) (22)* +21 Random map/mission generator (21)* +21 Ability to review missions and cut scenes independent of the game(21)* +18 Terrain Editor (18)* +12 Larger map (12)* +9 Fog of war (9) +8 More than 2 sides/combatants (8) +6 C&C-type games based on other genres (such as Tolwkien, Lands of Lore, Civil War, American old west, etc.) (6) +6 Captions for hearing impaired customers (6)* +6 New Mission Add-ins (6) +4 Remove linear missions (winning and losing paths) (4) +4 (units and money kept in next mission) (4) +3 Variable weather which affects play (3)* +3 No more maze-like levels (3) +3 Ability to play multi-player maps in single-player mode (3) +2 Windows 95 version (2) +2 Optional auto-save feature (2) +2 More varried terrain types (2) +2 Allow player option to remain in mission after goal is met (2) +1 Terrain affects movement/combat ability (1) +1 Option screen to chose which messages are actually heard (1) +1 Multiple resources (1) +1 More continuity between scenarios +1 Civilian riots (1) +1 Add an option key to move to any occuring battle (1) + +MULTI-PLAYER: + +42 Save option for modem/net play (42)* +27 AI players should build bases and be more aggressive (27)* +17 User variable setting for Tiberium regeneration (15) Increase chat message space (17)* +6 Ability to specifically select which units and structures can be used in a game (not according to tech level) (6) +4 Higher setting for credits in modem/net play (4)* +4 Ability to connect to more than 4 players (4)* +3 Ability to pause in modem/network game (3) +2 Handicapping option (2) +1 Ability to connect through a BBS or online service (1) +1 Allow players to build bases before the game starts (1) +1 Allow allys to transfer units (1) + +UNITS: +31 More land units (31)* +22 More air units (22)* +18 More water units (18)* +16 Air units reveal terrain as they fly over, not just when they land (16) +16 Increase intelligence of Harvester (so it can recognize and avoid enemy threats) (16)* +12 Group loading of APCs (12)* +7 Put the XO armor and Stealth fighters in the game (7) +6 Bridge-laying/amphibious units (6)* +6 Sabotage and espionage teams/infantry (6) +6 Paratroopers that can jump from planes or transport copters (6) +6 Officer infantry that make your troops more effective (hit better, stay in formation) (6) +4 Ability to control hovercraft, gunships, and A-10s (4)* +4 Ability to assign a unit to more than one Hot Key grouping (4)* +4 Engineers should have multiple purposes (mine laying/clearing, bridge building/blowing) (4)* +4 Mobile SAM (4)* +4 Carry-alls (like in dune 2) for Harvesters and damaged units (4) +4 MANY different harvester types, including flying and armed (4) +4 Units should have capability to move and fire at the same time (4) +3 The capability of squad/formation movement (3) +3 Multiple weapons on certain units (machine guns on tanks) (3) +3 Allow infantry to scale cliffs and ford rivers (3) +3 The ability to set down patrol points (2 or more) for a unit or group of units (3) +2 Scout (in conjunction with Fog of War) (2) +2 Make the commando a buildable unit in single-play (2) +2 Ability to put weapons on harvesters (2) +2 Ability to assign multiple targets (2) +1 Mammoth Tanks can crush light vehicles (1) +1 Allow Construction Yard to revert to MCV (1) +1 Sniper Units (1) +1 Medical Vehicals (Ambulances) to heal infantry (1) +1 Mobile Chemical mortar tank (1) +1 Cargo ships and vehicals able to carry more than 5 troopers and vehicles (1) +1 Mobile Anti-aircraft artillery (1) +1 Mobile Laser artillery (1) +1 Increase range of artillery (1) +1 Liquid Nitrogen troopers (like chem troops) (1) +1 Some type of air-to-air fighter craft (1) +1 Automatic scatter (X-key) of infantry units (and no scattering into threats) (1) +1 When a harvester is destroyed, the tiberium should stay on the ground (1) +1 Sound effects for each unit (such as engine whine for Chinooks) (1) +1 Suicide Units (Humm Vees filled with C4) (1) + + +STRUCTURES: +22 Automatic gates for the walls/fences (22)* +21 Ability to lay down mines (21)* +16 More balance to Ion Cannon / Nukes (Ion Cannon should be stronger or faster) (16) +11 Ability to build structures anywhere rather than next to existing structures (11)* +6 Ability to dig trenches (6)* +6 Bridges that can be destroyed (6)* +4 Make SAM sites respond faster (4) +3 Fix the sandbag bug (3) +3 Add a hospital to heal infantry (3) +2 Make capturing buildings more difficult (2) +1 Radar Jamming Center (1) +1 Non-mobile artillery emplacements (1) +1 Generators as backup power to specific buildings (1) +1 Listening Posts (in conjunction with Fog of War) (1) +1 Force fields as a barrier/wall type (1) +1 A defense shield generator to protect buildings from Nuke and Ion cannon (1) +1 Better walls than concrete (Steel?) (1) diff --git a/CODE/STYLE.H b/CODE/STYLE.H new file mode 100644 index 0000000..73c3484 --- /dev/null +++ b/CODE/STYLE.H @@ -0,0 +1,84 @@ +/* +** 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 . +*/ + +/* + STYLE GUIDE FOR COMMAND & CONQUER : TIBERIAN SUN + +In addition to the standard Westwood programmer's style guide, the following +guidelines apply. + +> Use every feature the compiler has that will help catch bugs and encourage +solid portable coding practices. i.e., turn on all warnings and treat all warnings +as errors. Use helper programs such as Lint, BoundsChecker, or CodeGuard whenever +possible. + +> Keep related items together. Examples: Declare variables right before they are +used. Keep functions that work on the same subsystem, within the same module. + +> Use consistent commenting and spacing style. Examples: see existing Red Alert +code. Creative freedom does not extend to formatting. + +> Use descriptive variable names. Examples: Use "index" rather than "i". Use +"unit_index" rather than "index". + +> Use a consistent variable and function naming convention. Examples; boolean +variables should begin with "Is" (e.g., "IsActive", "IsFiring"). Functions that +exist solely to return a similar boolean query should begin with "Is_". + +> If you have a variable that only serves a boolean function, then declare it +as "bool" rather than as "int" and assign it "true" or "false" rather than "1" +or "0". + +> Choose function and variable names that are precise, descriptive, and concise (in that +order). + +> Hide data where possible: Examples: If a global is only used in one module, make +it static to that module. If a helper function is only used in one module, make it +static as well. Hide class members in the private section if they aren't needed +public. Most variables aren't needed as public. + +> Keep function bodies short. It is often times, more easy to understand and maintain +if a very long function is broken up into helper functions with descriptive names. +A clue if a function is too long is if you can page down 5 times and still be somewhere +in the middle of the function. If you can page down 10 times and still be in the +function, you've got a serious function size problem. + +> Compare pointers to NULL and integer types to zero (NULL) instead of using the +"if (x)" or "if (!x)" format. This short format should only be used for boolean +values and expressions. It is just as efficient and it makes it more clear to the +reader that the variable is a pointer and not a simple boolean value. + +> Use "const" when declaring any member functions that do not modify data. Corollary -- +make sure any function that appears like it will not modify data will, in fact, NOT +modify any data. Example; A function called Is_Able_To_Move() shouldn't modify the +object nor should it actually cause something to start moving! The use of "const" is a +variation on style guide rule #1 (use the compiler to help ensure proper code). + +> Shun using assembly language except in extreme cases. Such would be if a +function can only be performed by using assembly (requires special opcodes), or +if performance would be PROVEABLY dramatically improved. Don't code in assembly for +performance reasons if there isn't benchmarking code in place to analyze the results. +As a corollary, design a better algorithm as the first recourse. + +> Keep class interfaces small and simple. + +> Organize algorithms to use as few special case "if" statements as possible. Data +tables or data files are preferred. This follows the generally good guideline of offloading +as much processing as possible to compile-time rather than run-time. + +*/ diff --git a/CODE/SUPER.CPP b/CODE/SUPER.CPP new file mode 100644 index 0000000..a1acc92 --- /dev/null +++ b/CODE/SUPER.CPP @@ -0,0 +1,391 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SUPER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SUPER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : October 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SuperClass::AI -- Process the super weapon AI. * + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * SuperClass::Discharged -- Handles discharged action for special super weapon. * + * SuperClass::Enable -- Enable this super special weapon. * + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * SuperClass::Remove -- Removes super weapon availability. * + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * * + * This is the constructor for the super weapons. * + * * + * INPUT: recharge -- The recharge delay time (in game frames). * + * * + * charging -- Voice to announce that the weapon is charging. * + * * + * ready -- Voice to announce that the weapon is fully charged. * + * * + * impatient -- Voice to announce current charging state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +SuperClass::SuperClass(int recharge, bool powered, VoxType charging, VoxType ready, VoxType impatient, VoxType suspend) : + IsPowered(powered), + IsPresent(false), + IsOneTime(false), + IsReady(false), + Control(0), + OldStage(-1), + VoxRecharge(ready), + VoxCharging(charging), + VoxImpatient(impatient), + VoxSuspend(suspend), + RechargeTime(recharge) +{ +} + + +/*********************************************************************************************** + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * * + * This will temporarily put on hold the charging of the special weapon. This might be the * + * result of insufficient power. * + * * + * INPUT: on -- Should the weapon charging be suspended? Else, it will unsuspend. * + * * + * OUTPUT: Was the weapon suspend state changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Suspend(bool on) +{ + if (IsPresent && !IsReady && !IsOneTime && on == Control.Is_Active()) { + if (!on) { + Control.Start(); + } else { + Control.Stop(); + } +// if (on != IsSuspended) { +// if (on) { +// SuspendTime = Control; +// } else { +// Control = SuspendTime; +// } +// IsSuspended = on; + return(true); +// } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Enable -- Enable this super special weapon. * + * * + * This routine is called when the special weapon needs to be activated. This is used for * + * both the normal super weapons and the special one-time super weapons (from crates). * + * * + * INPUT: onetime -- Is this a special one time super weapon? * + * * + * player -- Is this weapon for the player? If true, then there might be a voice * + * announcement of this weapon's availability. * + * * + * quiet -- Request that the weapon start in suspended state (quiet mode). * + * * + * OUTPUT: Was the special super weapon enabled? Failure might indicate that the weapon was * + * already available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Enable(bool onetime, bool player, bool quiet) +{ + if (!IsPresent) { + IsPresent = true; + IsOneTime = onetime; + bool retval = Recharge(player && !quiet); + if (quiet) Suspend(true); + return(retval); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Remove -- Removes super weapon availability. * + * * + * Call this routine when the super weapon should be removed because of some outside * + * force. For one time special super weapons, they can never be removed except as the * + * result of discharging them. * + * * + * INPUT: none * + * * + * OUTPUT: Was the special weapon removed and disabled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Remove(void) +{ + if (IsPresent && !IsOneTime) { + IsReady = false; + IsPresent = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * * + * This routine is called when the special weapon is allowed to recharge. Suspension will * + * be disabled and the animation process will begin. * + * * + * INPUT: player -- Is this for a player owned super weapon? If so, then a voice * + * announcement might be in order. * + * * + * OUTPUT: Was the super weapon begun charging up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Recharge(bool player) +{ + if (IsPresent && !IsReady) { +// IsSuspended = false; + OldStage = -1; + Control.Start(); + Control = RechargeTime; + +#ifdef CHEAT_KEYS + if (Special.IsSpeedBuild) { + Control = 1; + } +#endif + + if (player) { + Speak(VoxCharging); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Superclass::Discharged -- Handles discharged action for special super weapon. * + * * + * This routine should be called when the special super weapon has been discharged. The * + * weapon will either begin charging anew or will be removed entirely -- depends on the * + * one time flag for the weapon. * + * * + * INPUT: player -- Is this special weapon for the player? If so, then there might be a * + * voice announcement. * + * * + * OUTPUT: Should the sidebar be reprocessed because the special weapon has been eliminated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Discharged(bool player) +{ + if (Control.Is_Active() && IsPresent && IsReady) { + IsReady = false; + if (IsOneTime) { + IsOneTime = false; + return(Remove()); + } else { + Recharge(player); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::AI -- Process the super weapon AI. * + * * + * This routine will process the charge up AI for this super weapon object. If the weapon * + * has advanced far enough to change any sidebar graphic that might represent it, then * + * "true" will be returned. Use this return value to intelligently update the sidebar. * + * * + * INPUT: player -- Is this for the player? If it is and the weapon is now fully charged, * + * then this fully charged state will be announced to the player. * + * * + * OUTPUT: Was the weapon's state changed such that a sidebar graphic update will be * + * necessary? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::AI(bool player) +{ + if (IsPresent && !IsReady) { + if (!Control.Is_Active()) { + if (OldStage != -1) { + OldStage = -1; + return(true); + } + } else { + if (Control == 0) { + IsReady = true; + if (player) { + Speak(VoxRecharge); + } + return(true); + } else { + if (Anim_Stage() != OldStage) { + OldStage = Anim_Stage(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * * + * This will return the current animation stage for this super weapon. The value will be * + * between zero (uncharged) to ANIMATION_STAGES (fully charged). Use this value to render * + * the appropriate graphic on the sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current animation stage for this special super weapon powerup. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + * 10/11/1996 JLB : Doesn't show complete until really complete. * + *=============================================================================================*/ +int SuperClass::Anim_Stage(void) const +{ + if (IsPresent) { + if (IsReady) { + return(ANIMATION_STAGES); + } +// int time = Control; +// if (IsSuspended) { +// time = SuspendTime; +// } + + int stage = ANIMATION_STAGES * fixed(RechargeTime-Control.Value(), RechargeTime); + stage = min(stage, ANIMATION_STAGES-1); + return(stage); + } + return(0); +} + + +/*********************************************************************************************** + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * * + * This routine is called when the player clicks on the super weapon icon on the sidebar * + * when the super weapon is not ready yet. This results in a voice message feedback to the * + * player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Impatient_Click(void) const +{ + if (!Control.Is_Active()) { + Speak(VoxSuspend); + } else { + Speak(VoxImpatient); + } +} + + +/*********************************************************************************************** + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * * + * This routine will force the special weapon to full charge state. Call it when the weapon * + * needs to be instantly charged. The airstrike (when it first becomes available) is a * + * good example. * + * * + * INPUT: player -- Is this for the player? If true, then the full charge state will be * + * announced. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Forced_Charge(bool player) +{ + if (IsPresent) { + IsReady = true; + Control.Start(); + Control = 0; +// IsSuspended = false; + if (player) { + Speak(VoxRecharge); + } + } +} diff --git a/CODE/SUPER.H b/CODE/SUPER.H new file mode 100644 index 0000000..0101a50 --- /dev/null +++ b/CODE/SUPER.H @@ -0,0 +1,86 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SUPER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SUPER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SUPER_H +#define SUPER_H + +#include "ftimer.h" + +class SuperClass { + public: + SuperClass(NoInitClass const & x) : Control(x) {}; + SuperClass(void) : Control(NoInitClass()) {}; + SuperClass(int recharge, bool powered, VoxType charging=VOX_NONE, VoxType ready=VOX_NONE, VoxType impatient=VOX_NONE, VoxType suspend=VOX_NONE); + + bool Suspend(bool on); + bool Enable(bool onetime = false, bool player=false, bool quiet=false); + void Forced_Charge(bool player=false); + bool AI(bool player=false); + bool Remove(void); + void Impatient_Click(void) const; + int Anim_Stage(void) const; + bool Discharged(bool player); + bool Is_Ready(void) const {return(IsReady);} + bool Is_Present(void) const {return(IsPresent);} + bool Is_One_Time(void) const {return(IsOneTime && IsPresent);} + bool Is_Powered(void) const {return(IsPowered);} + + private: + bool Recharge(bool player=false); + + unsigned IsPowered:1; + unsigned IsPresent:1; + unsigned IsOneTime:1; + unsigned IsReady:1; + + CDTimerClass Control; + int OldStage; + + VoxType VoxRecharge; + VoxType VoxCharging; + VoxType VoxImpatient; + VoxType VoxSuspend; + int RechargeTime; + + enum { + ANIMATION_STAGES=54 + }; +}; + + + +#endif diff --git a/CODE/SUPPORT.ASM b/CODE/SUPPORT.ASM new file mode 100644 index 0000000..0d86dd1 --- /dev/null +++ b/CODE/SUPPORT.ASM @@ -0,0 +1,559 @@ +; +; 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 . +; + +; $Header: F:\projects\c&c0\vcs\code\support.asv 5.0 11 Nov 1996 09:40:30 JOE_BOSTIC $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + GLOBAL C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mul [(GraphicViewPort ebx).GVPWidth] + add edi,eax + + ; Verify the the X pixel offset is legal. + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +if 0 + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + GLOBAL C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + GLOBAL C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + GLOBAL C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + GLOBAL C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX +endif + + + DATASEG + +TabA DD 6949350 + DD 4913933 + DD 3474675 + DD 2456966 + DD 1737338 + DD 1228483 + DD 868669 + DD 614242 + DD 434334 + DD 307121 + DD 217167 + DD 153560 + DD 108584 + DD 76780 + DD 54292 + DD 38390 + DD 27146 + DD 19195 + DD 13573 + DD 9598 + DD 6786 + DD 4799 + DD 3393 + DD 2399 + DD 1697 + DD 1200 + DD 848 + DD 600 + DD 424 + DD 300 + DD 212 + DD 150 + DD 106 + +TabB DD 154 + DD 218 + DD 309 + DD 437 + DD 618 + DD 874 + DD 1236 + DD 1748 + DD 2472 + DD 3496 + DD 4944 + DD 6992 + DD 9888 + DD 13983 + DD 19775 + DD 27967 + DD 39551 + DD 55933 + DD 79101 + DD 111866 + DD 158203 + DD 223732 + DD 316405 + DD 447465 + DD 632811 + DD 894929 + DD 1265621 + DD 1789859 + DD 2531243 + DD 3579718 + DD 5062486 + DD 7159436 + DD 10124971 + + CODESEG + +;*********************************************************************************************** +;* Square_Root -- Finds the square root of the fixed pointer parameter. * +;* * +;* INPUT: val -- The fixed point (16:16) value to find the square root of. * +;* * +;* OUTPUT: Returns with the square root of the fixed pointer parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/04/1995 JLB : Adapted. * +;*=============================================================================================*/ +;unsigned Square_Root(unsigned val); + GLOBAL C Square_Root :NEAR + PROC Square_Root C near + USES ebx,edx + + bsr ebx,eax + jz ??zero + + mul [DWORD 4*ebx + OFFSET TabA] + shrd eax,edx,10h + add eax, [4*ebx + OFFSET TabB] +??zero: + ret + + ENDP Square_Root + +;---------------------------------------------------------------------------- + + END + diff --git a/CODE/SURFACE.CPP b/CODE/SURFACE.CPP new file mode 100644 index 0000000..2d2e226 --- /dev/null +++ b/CODE/SURFACE.CPP @@ -0,0 +1,162 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SURFACE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SURFACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "surface.h" + + +Surface::Surface(int w, int h, Buffer const * buffer, int pitch) : + Width(w), + Height(h), + Pitch(pitch) +{ + /* + ** If a buffer was specified, then this means that the surface will use the buffer memory + ** and thus not allocate and manage its own memory. + */ + if (buffer != NULL) { + SurfaceData = *buffer; + + /* + ** Reduce the dimensions if the buffer is not big enough. This size bounding is only + ** possible if the buffer size is known. Otherwise, presume that it is big enough. + */ + if (buffer->Get_Size() > 0 && Get_Size() > buffer->Get_Size()) { + + Height = buffer->Get_Size() / (Width+Pitch); + if (Height == 0) { + Height = 1; + Width = buffer->Get_Size(); + } + } + } else { + /* + ** A new buffer without existing memory specified, will allocate and manage its + ** own memory for the surface. + */ + new(&SurfaceData) Buffer(Logical_Size()); + } +} + + +Surface::Surface(Surface const & surface, int x, int y, int w, int h) : + Width(w), + Height(h), + Pitch(surface.Bytes_Per_Line() % w) +{ + new(&SurfaceData) Buffer((char*)surface.Get_Buffer() + y*surface.Bytes_Per_Line() + x); +} + + +void Surface::Copy_To(Buffer & buffer, int x, int y, int w, int h) const +{ + assert(buffer.Is_Valid()); + + /* + ** Determine the width of the region to copy from this surface. + */ + int width = w; + if (width == -1) { + width = Width; + } + + /* + ** Determine the height of the region to copy from this surface. + */ + int height = h; + if (height == -1) { + height = Height; + } + + Copy_To(Rect(x, y, width, height), buffer); +} + + +void Surface::Copy_To(Rect const & fromrect, Buffer & tobuffer) const +{ + assert(fromrect.Is_Valid()); + assert(tobuffer.Is_Valid()); + + /* + ** Determine the copy-from rectangle. The size is bounded to the source + ** size of the surface, regardless of what was specified as the source + ** rectangle. + */ + Rect rect = fromrect.Intersect(Rect(0, 0, Width, Height)); + + /* + ** Determine the number of bytes to copy. If this number would be + ** larger than the size of the destination buffer (presuming the size + ** of the destination buffer is known), then limit the copy size + ** to the buffer size. + */ + int tocopy = rect.Size(); + if (tobuffer.Get_Size() > 0 && tobuffer.Get_Size() > tocopy) { + tocopy = tobuffer.Get_Size(); + } + + /* + ** Determine the source starting byte pointer. + */ + char * source = Get_Buffer() + rect.Y*Bytes_Per_Line() + rect.X; + + /* + ** Determine the destination buffer pointer. This will always be the + ** start of the destination buffer object specified. + */ + char * dest = tobuffer; + + /* + ** Determine the working pitch value to use. For full width surface + ** copies on surfaces that have no inherent pitch, then a very fast + ** copy-in-one-fast-step operation can be performed. + */ + int pitch = Bytes_Per_Line() % rect.Width; + if (pitch == 0) { + memmove(dest, source, tocopy); + } else { + + /* + ** Copy the source to the destination in line segments. + */ + for (int y = 0; y < rect.Height; y++) { + memmove(dest, source, width); + dest += rect.Width; + source += rect.Width + pitch; + } + } +} diff --git a/CODE/SURFACE.H b/CODE/SURFACE.H new file mode 100644 index 0000000..affcaac --- /dev/null +++ b/CODE/SURFACE.H @@ -0,0 +1,116 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/SURFACE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : SURFACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SURFACE_H +#define SURFACE_H + +#include "buff.h" +#include "rect.h" + +/* +** This class is used to represent an arbitrary rectangle in a graphic memory space. Typically, +** this represents what could be called a graphic buffer or even a portion of a graphic buffer. +** It can also represent a piece of artwork (such as a shape image) to be used to construct a +** display image. This wide range of use is possible, because this class is a very minimal +** representation of the graphic data. It holds only the information is absolutely needs and +** has the minimum member functions possible. +*/ +class Surface +{ + public: + Surface(void) : Width(0), Height(0), Pitch(0) {} + Surface(int w, int h, Buffer const * buffer=NULL, int pitch=0); + Surface(Surface const & surface, int x, int y, int w, int h); + virtual ~Surface(void) {} + + /* + ** Basic manipulation routines for copying entire surfaces or + ** a sub-region. + */ + void Copy_To(Rect const & fromrect, Surface & tosurface, Rect const & torect) const; + void Copy_To(Rect const & fromrect, Buffer & tobuffer) const; + + /* + ** Convenience routines that make presumptions on what is desired so that a + ** minimum of parameters can be specified. These routine ultimately map to the + ** basic copy routines. + */ + void Copy_To(Buffer & buffer, int x=0, int y=0, int w=-1, int h=-1) const; + void Copy_From(Buffer const & buffer, int x=0, int y=0, int w=-1, int h=-1); + + /* + ** Basic query functions. Support routines that will manipulate the underlying + ** image data will require access to this information. + */ + void * Get_Buffer(void) const {return(SurfaceData.Get_Buffer());} + int Get_Size(void) const {return(Bytes_Per_Line() * Height);} + int Get_Width(void) const {return(Width);} + int Get_Height(void) const {return(Height);} + int Get_Pitch(void) const {return(Pitch);} + + protected: + int Bytes_Per_Line(void) const {return(Width+Pitch);} + + /* + ** This is the pointer to the surface memory. Sometimes this could be allocated memory + ** managed by this class but more likely, it is memory that is merely referred to by + ** this class. + */ + Buffer SurfaceData; + + /* + ** This is the width (columns) of the surface (in pixels). + */ + int Width; + + /* + ** This ithe height (rows) of the surface (in pixels). + */ + int Height; + + /* + ** This is the modulus of the surface underlying memory 'display surface' width + ** divided by this surface's width. For a surface that is full width, this value + ** will be 0. This value will be non-zero if this surface is referring to a + ** sub-region within another surface. + */ + int Pitch; +}; + + +#endif diff --git a/CODE/TAB.CPP b/CODE/TAB.CPP new file mode 100644 index 0000000..6cf6fe8 --- /dev/null +++ b/CODE/TAB.CPP @@ -0,0 +1,318 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TAB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TAB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TabClass::AI -- Handles player I/O with the tab buttons. * + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * TabClass::TabClass -- Default construct for the tab button class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * TabClass::TabShape = NULL; + + +/*********************************************************************************************** + * TabClass::TabClass -- Default construct for the tab button class. * + * * + * The default constructor merely sets the tab buttons to default non-selected state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +TabClass::TabClass(void) : + FlasherTimer(0), + IsToRedraw(false), + MoneyFlashTimer(0) +{ +} + + +/*********************************************************************************************** + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * * + * This routine is called whenever the display is being redrawn (in some fashion). The * + * parameter can be used to force the tab buttons to redraw completely. The default action * + * is to only redraw if the tab buttons have been explicitly flagged to be redraw. The * + * result of this is the elimination of unnecessary redraws. * + * * + * INPUT: complete -- bool; Force redraw of the entire tab button graphics? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 05/19/1995 JLB : New EVA style. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +#define TAB_HEIGHT 8 +void TabClass::Draw_It(bool complete) +{ + SidebarClass::Draw_It(complete); + + if (Debug_Map) { + return; + } + + /* + ** Redraw the top bar imagery if flagged to do so or if the entire display needs + ** to be redrawn. + */ + if ((complete || IsToRedraw) && LogicPage->Lock()) { + + int width = SeenBuff.Get_Width(); + int rightx = width - 1; + int tab_height = TAB_HEIGHT * RESFACTOR; + + LogicPage->Fill_Rect(0, 0, rightx, tab_height-1, BLACK); +// LogicPage->Fill_Rect(0, 0, rightx, tab_height-(2 * RESFACTOR), BLACK); + +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 0, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, 2, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + Draw_Credits_Tab(); + LogicPage->Draw_Line(0, tab_height-(1* RESFACTOR), rightx, tab_height-(1 * RESFACTOR), BLACK); +#ifdef WIN32 + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); +#else +// Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +#endif //WIN32 + if (IsSidebarActive) { +#ifndef WIN32 + TabClass::Hilite_Tab(1); +#endif //WIN32 + } else { + CC_Draw_Shape(TabShape, 0, width-(EVA_WIDTH * RESFACTOR), 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_SIDEBAR, width-((EVA_WIDTH/2) * RESFACTOR), 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); + } + + LogicPage->Unlock(); + } + Credits.Graphic_Logic(complete || IsToRedraw); + IsToRedraw = false; +} + + +void TabClass::Draw_Credits_Tab(void) +{ +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, Map.MoneyFlashTimer > 1 ? 8 : 6, (320-EVA_WIDTH) * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, 4, (320-(EVA_WIDTH*2)) * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + + if (Scen.MissionTimer.Is_Active()) { + bool light = (Scen.MissionTimer < TICKS_PER_MINUTE * Rule.TimerWarning) || Map.FlasherTimer > 0; +#ifdef WIN32 + CC_Draw_Shape(TabShape, light ? 4 : 2, 320, 0, WINDOW_MAIN, SHAPE_NORMAL); +#else + CC_Draw_Shape(TabShape, light ? 6 : 5, EVA_WIDTH * RESFACTOR, 0, WINDOW_MAIN, SHAPE_NORMAL); +#endif + } +} + + +void TabClass::Hilite_Tab(int tab) +{ + int xpos = 0; + int text = TXT_TAB_BUTTON_CONTROLS; + int textx = (EVA_WIDTH/2) * RESFACTOR; + + if (tab) { + xpos = (320-EVA_WIDTH) * RESFACTOR; + text = TXT_TAB_SIDEBAR; + textx = (320-(EVA_WIDTH/2)) * RESFACTOR; + } + +#ifdef WIN32 + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 1, xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + MetalScheme.Color = 128+6; + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); + MetalScheme.Color = 128; +#else + CC_Draw_Shape(TabShape, 1 + (tab ? 0 : 2), xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(text, textx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +// Fancy_Text_Print(text, textx, 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); +#endif +} + + +/*********************************************************************************************** + * TabClass::AI -- Handles player I/O with the tab buttons. * + * * + * This routine is called every game tick and passed whatever key the player has supplied. * + * If the input selects a tab button, then the graphic gets updated accordingly. * + * * + * INPUT: input -- The player's input character (might be mouse click). * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 05/31/1995 JLB : Fixed to handle mouse shape properly. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +void TabClass::AI(KeyNumType &input, int x, int y) +{ + if (y >= 0 && y < (TAB_HEIGHT * RESFACTOR) && x < (SeenBuff.Get_Width() - 1) && x > 0) { + + bool ok = false; + int width = SeenBuff.Get_Width(); + + /* + ** If the mouse is at the top of the screen, then the tab bars only work + ** in certain areas. If the special scroll modification is not active, then + ** the tabs never work when the mouse is at the top of the screen. + */ + if (y > 0) { + ok = true; + } + + if (ok) { + if (input == KN_LMOUSE) { + int sel = -1; + if (x < EVA_WIDTH * RESFACTOR) sel = 0; +#ifndef WIN32 // No Sidebar tab in hires - sidebar is always active. + if (x > (320-80) * RESFACTOR) sel = 1; +#endif //WIN32 + if (sel >= 0) { + Set_Active(sel); + input = KN_NONE; + } + } + + Override_Mouse_Shape(MOUSE_NORMAL, false); + } + } + + if (MoneyFlashTimer == 1) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + Credits.AI(); + + SidebarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * * + * This function is used to activate one of the file folder tab buttons that appear at the * + * top edge of the screen. * + * * + * INPUT: select -- The button to activate. 0 = left button, 1=next button, etc. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void TabClass::Set_Active(int select) +{ + switch (select) { + case 0: + Queue_Options(); + break; + + case 1: + Map.SidebarClass::Activate(-1); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * * + * This routine will perform any one time initializations of the tab handler class. This * + * typically includes the loading of the shapes that appear on it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void TabClass::One_Time(void) +{ + SidebarClass::One_Time(); + RawFileClass file("tabs.shp"); + TabShape = MFCD::Retrieve("TABS.SHP"); +} + + +void TabClass::Flash_Money(void) +{ + IsToRedraw = true; + Flag_To_Redraw(false); + MoneyFlashTimer = 7; +} diff --git a/CODE/TAB.H b/CODE/TAB.H new file mode 100644 index 0000000..873371b --- /dev/null +++ b/CODE/TAB.H @@ -0,0 +1,79 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TAB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TAB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TAB_H +#define TAB_H + +#include "sidebar.h" +#include "credits.h" + +class TabClass: public SidebarClass +{ + public: + TabClass(void); + TabClass(NoInitClass const & x) : SidebarClass(x), Credits(x), FlasherTimer(x), MoneyFlashTimer(x) {}; + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + static void Draw_Credits_Tab(void); + static void Hilite_Tab(int tab); + void Flash_Money(void); + + virtual void One_Time(void); // One-time inits + void Redraw_Tab(void) {IsToRedraw = true;Flag_To_Redraw(false);}; + + CreditClass Credits; + + CDTimerClass FlasherTimer; + + protected: + + /* + ** If the tab graphic is to be redrawn, then this flag is true. + */ + unsigned IsToRedraw:1; + + private: + void Set_Active(int select); + + CDTimerClass MoneyFlashTimer; + + static void const * TabShape; +}; + + +#endif diff --git a/CODE/TACTION.CPP b/CODE/TACTION.CPP new file mode 100644 index 0000000..9807970 --- /dev/null +++ b/CODE/TACTION.CPP @@ -0,0 +1,892 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TACTION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ACTION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Action_From_Name -- retrieves ActionType for given name * + * Action_Needs -- Figures out what data an action object needs. * + * Name_From_Action -- retrieves name for ActionType * + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * TActionClass::Detach -- Removes any attachment from associated action. * + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * TActionClass::operator -- Performs the action that this object does. * + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** These are the text names for the various actions. If the action name ends with "..." then +** this means that additional data is probably required. +*/ +static const char * ActionText[TACTION_COUNT] = { + "-No Action-", + "Winner is...", + "Loser is...", + "Production Begins", + "Create Team...", + "Destroy All Teams", + "All to Hunt...", + "Reinforcement (team)...", + "Drop Zone Flare (waypoint)...", + "Fire Sale...", + "Play Movie...", + "Text Trigger (ID num)...", + "Destroy Trigger...", + "Autocreate Begins...", + "~don't use~", + "Allow Win", + "Reveal all map", + "Reveal around waypoint...", + "Reveal zone of waypoint...", + "Play sound effect...", + "Play music theme...", + "Play speech...", + "Force Trigger...", + "Timer Start", + "Timer Stop", + "Timer Extend (1/10th min)...", + "Timer Shorten (1/10th min)...", + "Timer Set (1/10th min)...", + "Global Set...", + "Global Clear...", + "Auto Base Building...", + "Grow shroud one 'step'", + "Destroy attached building", + "Add 1-time special weapon...", + "Add repeating special weapon...", + "Preferred target...", + "Launch Nukes" +}; + + +ActionChoiceClass ActionChoices[TACTION_COUNT] = { + {TACTION_NONE}, + {TACTION_WIN}, + {TACTION_LOSE}, + {TACTION_BEGIN_PRODUCTION}, + {TACTION_CREATE_TEAM}, + {TACTION_DESTROY_TEAM}, + {TACTION_ALL_HUNT}, + {TACTION_REINFORCEMENTS}, + {TACTION_DZ}, + {TACTION_FIRE_SALE}, + {TACTION_PLAY_MOVIE}, + {TACTION_TEXT_TRIGGER}, + {TACTION_DESTROY_TRIGGER}, + {TACTION_AUTOCREATE}, + {TACTION_WINLOSE}, + {TACTION_ALLOWWIN}, + {TACTION_REVEAL_ALL}, + {TACTION_REVEAL_SOME}, + {TACTION_REVEAL_ZONE}, + {TACTION_PLAY_SOUND}, + {TACTION_PLAY_MUSIC}, + {TACTION_PLAY_SPEECH}, + {TACTION_FORCE_TRIGGER}, + {TACTION_START_TIMER}, + {TACTION_STOP_TIMER}, + {TACTION_ADD_TIMER}, + {TACTION_SUB_TIMER}, + {TACTION_SET_TIMER}, + {TACTION_SET_GLOBAL}, + {TACTION_CLEAR_GLOBAL}, + {TACTION_BASE_BUILDING}, + {TACTION_CREEP_SHADOW}, + {TACTION_DESTROY_OBJECT}, + {TACTION_1_SPECIAL}, + {TACTION_FULL_SPECIAL}, + {TACTION_PREFERRED_TARGET}, + {TACTION_LAUNCH_NUKES} +}; + + +/*********************************************************************************************** + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * * + * This is a support routine only used to display this object when it is part of a list * + * box. * + * * + * INPUT: x,y -- The coordinate (upper left) to display the description. * + * * + * width,height -- Dimensions of the area to display the description. * + * * + * selected -- Is this item highlighted? * + * * + * flags -- The text print flags to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void ActionChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TActionClass::Detach -- Removes any attachment from associated action. * + * * + * This routine will remove any action reference to the team type specified. This routine * + * is called when the team type is being destroyed. All references to that team type must * + * also be severed. This routine does that with respect to trigger actions. * + * * + * INPUT: target-- The target object or type to remove from this taction object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Detach(TARGET target) +{ + if (Is_Target_TeamType(target) && Team == As_TeamType(target)) { + Team = NULL; + } + if (Is_Target_TriggerType(target) && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*********************************************************************************************** + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * * + * This routine will build the text (INI entry) format for the data of this trigger * + * action object. Typical use of this is when the INI file is being written. * + * * + * INPUT: ptr -- Pointer to the location to build the INI text to. The buffer is presumed * + * to be big enough. * + * * + * OUTPUT: none * + * * + * WARNINGS: The buffer passed to this routine must be big enough to hold the largest * + * text that will be created into it. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d,%d", Action, TeamTypes.Logical_ID(Team), TriggerTypes.Logical_ID(Trigger), Data.Value); +} + + +/*********************************************************************************************** + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * * + * This routine will convert INI data into the right values within this trigger action * + * object. Typical use of this routine is when the INI file is being read. It is the * + * counterpart to the Build_INI_Entry function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Read_INI(void) +{ + switch (NewINIFormat) { + default: + Action = TActionType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + case 0: + Action = TActionType(atoi(strtok(NULL, ","))); + + char const * ptr = strtok(NULL, ","); + Team = TeamTypeClass::From_Name(ptr); + assert(Action_Needs(Action) != NEED_TEAM || Team.Is_Valid()); + + /* + ** Since triggers refer to other triggers, only record a copy of the trigger text + ** name. This will be fixed up later. + */ + Trigger.Set_Raw((long)strdup(strtok(NULL, ","))); + + Data.Value = atoi(strtok(NULL, ",")); + break; + } +} + + +/*********************************************************************************************** + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * * + * This routine is called prior to saving the game. It will convert any pointers into a * + * format that is safe for persistent storage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine makes the object unfit for use. The Decode_Pointers() routine * + * must be called prior to using this object. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * * + * This routine is called after a game has been loaded. The encoded pointers will be * + * converted back into usable format by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will NOT work unless the pointer are, in fact, coded. There is * + * no prevention check to protect against calling this routine twice. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::operator -- Performs the action that this object does. * + * * + * This routine is called when the action associated with this action object must be * + * performed. Typically, this occurs when a trigger has "sprung" and now it must take * + * effect. The action object is what carries out this effect. * + * * + * INPUT: house -- The owner of this action. This information is necessary since some * + * actions depend on who the trigger was owned by. * + * * + * object-- Pointer to the object that the springing trigger was attached to. If * + * this parameter is null, then the trigger wasn't attached to any object. * + * * + * id -- Trigger ID (only if forced) otherwise -1. * + * * + * cell -- The cell this trigger is attached to (if any). * + * * + * OUTPUT: bool; Was this action able to perform what it needed to do? Failure could be * + * because a reinforcement couldn't be generated, for example. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + * 04/10/1996 JLB : Added the ID parameter. * + *=============================================================================================*/ +bool TActionClass::operator() (HousesType house, ObjectClass * object, int id, CELL cell) +{ + /* + ** Otherwise, take an appropriate action. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + TriggerClass * trig = NULL; + if (id != -1) { + trig = Triggers.Raw_Ptr(id); + } + bool success = true; +// TeamTypeClass * ttype = Team; + + /* + ** Ensure that the specified object is not actually dead. A dead object could + ** be passed to this routine in the case of a multiple event trigger that + ** had the first event kill the object. + */ + if (object && !object->IsActive) { + object = 0; + } + + switch (Action) { + /* + ** Display a text message overlayed onto the tactical map. + */ + case TACTION_TEXT_TRIGGER: + Session.Messages.Add_Message(NULL, 0, (char *)TutorialText[Data.Value], PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + break; + + /* + ** Launch nuclear missiles (duds) from all mslo's + */ + case TACTION_LAUNCH_NUKES: + { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_MSLO) { + bldg->Assign_Mission(MISSION_MISSILE); + } + } + break; + } + + /* + ** Set the preferred target for the house. + */ + case TACTION_PREFERRED_TARGET: + if (hptr) { + hptr->PreferredTarget = Data.Quarry; + } + break; + + /* + ** Initiate (or disable) the computer AI. When active, the computer will + ** build bases and units. + */ + case TACTION_BASE_BUILDING: + if (Data.Bool) { + hptr->IsBaseBuilding = true; + } else { + hptr->IsBaseBuilding = false; + } + break; + + /* + ** Cause the shadow to creep back one step. + */ + case TACTION_CREEP_SHADOW: + Map.Encroach_Shadow(); + break; + + /* + ** Set a scenario global. + */ + case TACTION_SET_GLOBAL: + Scen.Set_Global_To(Data.Value, true); + break; + + /* + ** Clear a scenario global. + */ + case TACTION_CLEAR_GLOBAL: + Scen.Set_Global_To(Data.Value, false); + break; + + /* + ** Reveal the map around the area specified. + */ + case TACTION_REVEAL_SOME: + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[Data.Value], Rule.GapShroudRadius, PlayerPtr, false); + } + break; + + /* + ** Reveal all cells of the zone that the specified waypoint is located + ** in. This can be used to reveal whole islands or bodies of water + */ + case TACTION_REVEAL_ZONE: + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[Data.Value]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + } + break; + + /* + ** Reveal the entire map. + */ + case TACTION_REVEAL_ALL: + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + break; + + /* + ** Star the mission timer. + */ + case TACTION_START_TIMER: + if (!Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + } + break; + + /* + ** Stop the mission timer. This will really just + ** suspend the timer. + */ + case TACTION_STOP_TIMER: + if (Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Stop(); + Map.Redraw_Tab(); + } + break; + + /* + ** Add time to the mission timer. + */ + case TACTION_ADD_TIMER: + Scen.MissionTimer = Scen.MissionTimer + (Data.Value * (TICKS_PER_MINUTE/10)); + Map.Redraw_Tab(); + break; + + /* + ** Remove time from the mission timer. + */ + case TACTION_SUB_TIMER: + if (Scen.MissionTimer <= Data.Value * (TICKS_PER_MINUTE/10)) { + Scen.MissionTimer = 0; + } else { + Scen.MissionTimer = Scen.MissionTimer - (Data.Value * (TICKS_PER_MINUTE/10)); + } + Map.Redraw_Tab(); + break; + + /* + ** Set the mission timer to the value specified. + */ + case TACTION_SET_TIMER: + Scen.MissionTimer = Data.Value * (TICKS_PER_MINUTE/10); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + break; + + /* + ** Play a movie immediately. The game is temporarily + ** suspended while the movie plays. + */ + case TACTION_PLAY_MOVIE: + Hide_Mouse(); + SeenPage.Clear(); + Play_Movie(Data.Movie, THEME_NONE, true); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + Show_Mouse(); + break; + + /* + ** Play a sound effect. + */ + case TACTION_PLAY_SOUND: + Sound_Effect(Data.Sound); + break; + + /* + ** Play a musical theme. + */ + case TACTION_PLAY_MUSIC: + Theme.Queue_Song(Data.Theme); + break; + + /* + ** Play the speech data specified. + */ + case TACTION_PLAY_SPEECH: + Speak(Data.Speech); + break; + + /* + ** Give the special weapon to the house. + */ + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + hptr->SuperWeapon[Data.Special].Enable(Action==TACTION_1_SPECIAL, false); +// hptr->SuperWeapon[Data.Special].Forced_Charge(PlayerPtr == hptr); + + if (PlayerPtr == hptr) { + Map.Add(RTTI_SPECIAL, Data.Special); + Map.Column[1].Flag_To_Redraw(); + } + break; + + /* + ** Destroying a trigger means that all triggers of that type will be destroyed. + */ + case TACTION_DESTROY_TRIGGER: + if (Trigger.Is_Valid()) { + for (int index = 0; index < Triggers.Count(); index++) { + if (Triggers.Ptr(index)->Class == Trigger) { + Detach_This_From_All(Triggers.Ptr(index)->As_Target()); + delete Triggers.Ptr(index); + index--; + } + } + } + break; + + /* + ** A forced trigger will force an existing trigger of that type or + ** will create a trigger of that type and then force it to be sprung. + */ + case TACTION_FORCE_TRIGGER: + if (Trigger.Is_Valid()) { + Find_Or_Make(Trigger)->Spring(TEVENT_ANY, 0, 0, true); + } + break; + + /* + ** Place a smoke marker at the waypoint specified. + */ + case TACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Scen.Waypoint[Data.Value])); + break; + + /* + ** Flag the house specified as the winner. Really the house value + ** is only used to determine if it is the player or the computer. + */ + case TACTION_WIN: + if (Data.House == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** Flag the house specified as the loser. The house parameter is only + ** used to determine if it refers to the player or the computer. + */ + case TACTION_LOSE: + if (Data.House != PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** This will enable production to begin for the house specified. + */ + case TACTION_BEGIN_PRODUCTION: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->Begin_Production(); + } + break; + + /* + ** Cause all buildings to be sold and all units to go into + ** hunt mode. + */ + case TACTION_FIRE_SALE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->State = STATE_ENDGAME; + } + break; + + /* + ** Begin the team autocreate logic for the house specified. + */ + case TACTION_AUTOCREATE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->IsAlerted = true; + } + break; + + /* + ** Manually create the team specified. + */ + case TACTION_CREATE_TEAM: + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + break; + + /* + ** Destroy all teams of the type specified. + */ + case TACTION_DESTROY_TEAM: + Team->Destroy_All_Of(); + break; + + /* + ** Create a reinforcement of the team specified. + */ + case TACTION_REINFORCEMENTS: + success = Do_Reinforcements(&*Team); + break; + + /* + ** Force all units of the house specified to go into + ** hunt mode. + */ + case TACTION_ALL_HUNT: + HouseClass::As_Pointer(Data.House)->Do_All_To_Hunt(); + break; + + /* + ** This will destroy all objects that this trigger is + ** attached to. + */ + case TACTION_DESTROY_OBJECT: + if (object) { + int damage = object->Strength; + object->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } else { + success = false; + } + + /* + ** If the trigger is attached to a bridge, then the bridge + ** gets destroyed regardless of whether the trigger was a + ** forced or natural spring event. + */ + if (cell != 0) { + Map.Destroy_Bridge_At(cell); + } + + /* + ** Loop through and destroy all objects that have this trigger + ** attached to them. + */ + if (trig) { + for (int u_index = 0; u_index < Units.Count(); u_index++) { + UnitClass * unit = Units.Ptr(u_index); + + if (unit && unit->Trigger == trig) { + unit->Trigger = NULL; + int damage = unit->Strength; + unit->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int i_index = 0; i_index < Infantry.Count(); i_index++) { + InfantryClass * infantry = Infantry.Ptr(i_index); + + if (infantry && infantry->Trigger == trig) { + infantry->Trigger = NULL; + int damage = infantry->Strength; + infantry->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { + AircraftClass * aircraft = Aircraft.Ptr(a_index); + + if (aircraft && aircraft->Trigger == trig) { + aircraft->Trigger = NULL; + int damage = aircraft->Strength; + aircraft->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass * building = Buildings.Ptr(b_index); + + if (building && building->Trigger == trig) { + building->Trigger = NULL; + int damage = building->Strength; + building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + } + break; + + /* + ** Do no action at all. + */ + case TACTION_NONE: + default: + break; + } + return(success); +} + + +/*********************************************************************************************** + * Action_From_Name -- retrieves ActionType for given name * + * * + * INPUT: * + * name name to get ActionType for * + * * + * OUTPUT: * + * ActionType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TActionType Action_From_Name (char const * name) +{ + if (name == NULL) { + return(TACTION_NONE); + } + + for (TActionType i = TACTION_NONE; i < TACTION_COUNT; i++) { + if (!stricmp(name, ActionText[i])) { + return(i); + } + } + + return(TACTION_NONE); +} + + +/*********************************************************************************************** + * Name_From_Action -- retrieves name for ActionType * + * * + * INPUT: * + * action ActionType to get name for * + * * + * OUTPUT: * + * name of ActionType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Action(TActionType action) +{ + return(ActionText[action]); +} + + +/*********************************************************************************************** + * Action_Needs -- Figures out what data an action object needs. * + * * + * Use this routine to determine what extra data is needed for the specified action. This * + * data will be prompted for in the scenario editor. * + * * + * INPUT: action -- The action that is to be queried. * + * * + * OUTPUT: Returns with the data type (enumeration) needed for this action type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +NeedType Action_Needs(TActionType action) +{ + switch (action) { + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + return(NEED_SPECIAL); + + case TACTION_FIRE_SALE: + case TACTION_WIN: + case TACTION_LOSE: + case TACTION_ALL_HUNT: + case TACTION_BEGIN_PRODUCTION: + case TACTION_AUTOCREATE: + return(NEED_HOUSE); + + case TACTION_BASE_BUILDING: + return(NEED_BOOL); + + case TACTION_CREATE_TEAM: + case TACTION_DESTROY_TEAM: + case TACTION_REINFORCEMENTS: + return(NEED_TEAM); + + case TACTION_FORCE_TRIGGER: + case TACTION_DESTROY_TRIGGER: + return(NEED_TRIGGER); + + case TACTION_DZ: + return(NEED_WAYPOINT); + + case TACTION_REVEAL_SOME: + case TACTION_REVEAL_ZONE: + return(NEED_WAYPOINT); + + case TACTION_PLAY_MUSIC: + return(NEED_THEME); + + case TACTION_PLAY_MOVIE: + return(NEED_MOVIE); + + case TACTION_PLAY_SOUND: + return(NEED_SOUND); + + case TACTION_PLAY_SPEECH: + return(NEED_SPEECH); + + case TACTION_TEXT_TRIGGER: + case TACTION_ADD_TIMER: + case TACTION_SUB_TIMER: + case TACTION_SET_TIMER: + case TACTION_SET_GLOBAL: + case TACTION_CLEAR_GLOBAL: + return(NEED_NUMBER); + + case TACTION_PREFERRED_TARGET: + return(NEED_QUARRY); + + default: + break; + } + return(NEED_NONE); +} + + diff --git a/CODE/TACTION.H b/CODE/TACTION.H new file mode 100644 index 0000000..725f931 --- /dev/null +++ b/CODE/TACTION.H @@ -0,0 +1,157 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TACTION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : ACTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef ACTION_H +#define ACTION_H + +typedef enum TActionType { + TACTION_NONE, + + TACTION_WIN, // player wins! + TACTION_LOSE, // player loses. + TACTION_BEGIN_PRODUCTION, // computer begins factory production. + TACTION_CREATE_TEAM, // computer creates a certain type of team + TACTION_DESTROY_TEAM, + TACTION_ALL_HUNT, // all enemy units go into hunt mode (teams destroyed). + TACTION_REINFORCEMENTS, // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + TACTION_DZ, // Deploy drop zone smoke. + TACTION_FIRE_SALE, // Sell all buildings and go on rampage. + TACTION_PLAY_MOVIE, // Play movie (temporarily suspend game). + TACTION_TEXT_TRIGGER, // Triggers a text message display. + TACTION_DESTROY_TRIGGER, // Destroy specified trigger. + TACTION_AUTOCREATE, // Computer to autocreate teams. + TACTION_WINLOSE, // Win if captured, lose if destroyed. + TACTION_ALLOWWIN, // Allows winning if triggered. + + TACTION_REVEAL_ALL, // Reveal the entire map. + TACTION_REVEAL_SOME, // Reveal map around cell #. + TACTION_REVEAL_ZONE, // Reveal all of specified zone. + TACTION_PLAY_SOUND, // Play sound effect. + TACTION_PLAY_MUSIC, // Play musical score. + TACTION_PLAY_SPEECH, // Play EVA speech. + TACTION_FORCE_TRIGGER, // Force trigger to activate. + TACTION_START_TIMER, // Start mission timer. + TACTION_STOP_TIMER, // Stop mission timer. + TACTION_ADD_TIMER, // Increase mission timer time. + TACTION_SUB_TIMER, // Decrease mission timer time. + TACTION_SET_TIMER, // Set and start the mission timer. + TACTION_SET_GLOBAL, // Set global variable. + TACTION_CLEAR_GLOBAL, // Clear global variable. + TACTION_BASE_BUILDING, // Automated base building. + TACTION_CREEP_SHADOW, // Shadow grows back one 'step'. + + TACTION_DESTROY_OBJECT, // Destroys the building this trigger is attached to. + TACTION_1_SPECIAL, // Add a one-time special weapon ability to house. + TACTION_FULL_SPECIAL, // Add a repeating special weapon ability to house. + + TACTION_PREFERRED_TARGET, // Designates preferred target for house. + TACTION_LAUNCH_NUKES, // Launch fake nuclear missiles from all silos + + TACTION_COUNT, + TACTION_FIRST=0 +} TActionType; + +TActionType Action_From_Name(char const * name); +char const * Name_From_Action(TActionType action); +NeedType Action_Needs(TActionType action); + +class TriggerTypeClass; +class TeamTypeClass; + +/* +** This elaborates the information necessary to carry out +** a trigger's action. +*/ +struct TActionClass { + TActionType Action; // Action to perform. + + CCPtr Team; // Team type pointer for this action (if needed). + + CCPtr Trigger; // Trigger type pointer for this action (if needed). + + union { + ThemeType Theme; // Musical theme. + VocType Sound; // Sound effect. + VoxType Speech; // Speech identifier. + HousesType House; // House to be affected. + SpecialWeaponType Special; // Special weapon ability. + QuarryType Quarry; // Preferred target for attack. + VQType Movie; // The movie to play. + bool Bool; // Boolean value. + long Value; + } Data; + + TActionClass(void) : Action(TACTION_NONE) { + Data.Theme = THEME_NONE; + Data.Value = -1; + }; + TActionClass(NoInitClass const & x) : Team(x), Trigger(x) {}; + + void Detach(TARGET target); + void Code_Pointers(void); + void Decode_Pointers(void); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; + + bool operator() (HousesType house, ObjectClass * object, int id, CELL cell); +}; + + +class ActionChoiceClass { + public: + ActionChoiceClass(TActionType event=TACTION_NONE) : Action(event) {} + + operator TActionType (void) const {return(Action);} + bool operator == (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action);} + bool operator != (ActionChoiceClass const & rvalue) const {return(Action != rvalue.Action);} + bool operator > (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Action(Action));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TActionType Action; +}; + + +extern ActionChoiceClass ActionChoices[TACTION_COUNT]; + +#endif diff --git a/CODE/TARCOM.CPP b/CODE/TARCOM.CPP new file mode 100644 index 0000000..6a09153 --- /dev/null +++ b/CODE/TARCOM.CPP @@ -0,0 +1,199 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TARCOM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TARCOM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 16, 1994 * + * * + * Last Update : July 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * TarComClass::~TarComClass -- Destructor for turret object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TarComClass::~TarComClass -- Destructor for turret object. * + * * + * This is the destructor for turret objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +TarComClass::~TarComClass(void) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * * + * This routine is used to display the tarcom class status to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::Debug_Dump(MonoClass *mono) const +{ + TurretClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * * + * This handles the AI logic for the targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::AI(void) +{ + assert(IsActive); + + TurretClass::AI(); + + if (!IsActive) return; + + if (Class->Primary != WEAPON_NONE) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + WeaponTypeClass const * weapon = &Weapons[Class->Primary]; + int primary = 0; + FireErrorType ok = Can_Fire(TarCom, 0); + if (ok != FIRE_OK) { + if (Can_Fire(TarCom, 1) == FIRE_OK) { + ok = FIRE_OK; + primary = 1; + weapon = &Weapons[Class->Secondary]; + } + } + + switch (ok) { + case FIRE_OK: +// if (What_Am_I() != RTTI_UNIT) { +// IsFiring = false; +// } else { + if (!((UnitClass *)this)->Class->IsFireAnim) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } +// } + + if (TurretClass::Fire_At(TarCom, primary)) { +// Sound_Effect(weapon->Sound, Coord); + } + break; + + case FIRE_FACING: + if (Class->IsLockTurret) { + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { +#ifdef OLD + if (*this == UNIT_FTANK) { + SecondaryFacing.Set_Desired(Facing_Dir(Dir_Facing(Direction(TarCom)))); + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } +#else + SecondaryFacing.Set_Desired(Direction(TarCom)); +#endif +// SecondaryFacing.Set_Desired(Direction256(Center_Coord(), As_Coord(TarCom))); + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } + + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { +#ifdef OLD + if (*this == UNIT_FTANK) { + PrimaryFacing.Set_Desired(Facing_Dir(Dir_Facing(dir))); + } else { + PrimaryFacing.Set_Desired(dir); + } +#else + PrimaryFacing.Set_Desired(dir); +#endif + } + } + } +} + + diff --git a/CODE/TARCOM.H b/CODE/TARCOM.H new file mode 100644 index 0000000..5092a0d --- /dev/null +++ b/CODE/TARCOM.H @@ -0,0 +1,78 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tarcom.h_v 2.17 01 Mar 1996 17:27:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TARCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARCOM_H +#define TARCOM_H + +#include "turret.h" +#include "bullet.h" + +/**************************************************************************** +** Units that can perform combat are handled by this class. It performs +** such operations as determining threat value down to actually launching the +** projectile. +*/ +class TarComClass : public TurretClass +{ + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TarComClass(void) {}; + TarComClass(UnitType classid, HousesType house) : TurretClass(classid, house) {}; + virtual ~TarComClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void AI(void); +// virtual bool Target_Something_Nearby(ThreatType rangmatters=THREAT_NORMAL); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + +}; + +#endif + + diff --git a/CODE/TARGET.CPP b/CODE/TARGET.CPP new file mode 100644 index 0000000..b02fe73 --- /dev/null +++ b/CODE/TARGET.CPP @@ -0,0 +1,872 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TARGET.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TARGET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * As_Animation -- Converts target value into animation pointer. * + * As_Building -- Converts a target value into a building object pointer. * + * As_Bullet -- Converts the target into a bullet pointer. * + * As_Cell -- Converts a target value into a cell number. * + * As_Coord -- Converts a target value into a coordinate value. * + * As_Infantry -- If the target is infantry, return a pointer to it. * + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * As_Object -- Converts a target value into an object pointer. * + * As_Target -- Converts a cell into a target value. * + * As_Target -- Converts a coordinate into a target value. * + * As_Team -- Converts a target number into a team pointer. * + * As_TeamType -- Converts a target into a team type pointer. * + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * As_Trigger -- Converts specified target into a trigger pointer. * + * As_TriggerType -- Convert the specified target into a trigger type. * + * As_Unit -- Converts a target value into a unit pointer. * + * As_Vessel -- Converts a target number into a vessel pointer. * + * TClass::TClass -- Constructor for target from object pointer. * + * TargetClass::As_Object -- Converts a target into an object pointer. * + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * Target_Legal -- Determines if the specified target is legal. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "target.h" + + +TargetClass::TargetClass(TARGET target) +{ + Target.Target = target; +} + + +TargetClass::TargetClass(AbstractClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(AbstractTypeClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(CellClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +CellClass * xTargetClass::As_Cell(void) const +{ + if (Target.Sub.Exponent == RTTI_CELL) { + return(&Map[(CELL)Target.Sub.Mantissa]); + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Trigger -- Converts specified target into a trigger pointer. * + * * + * This routine will convert the specified target number into a trigger pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the trigger pointer that the specified target number represents. If * + * it doesn't represent a legal trigger object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass * As_Trigger(TARGET target) +{ + return(Is_Target_Trigger(target) ? Triggers.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Team -- Converts a target number into a team pointer. * + * * + * This routine will convert the specified target number into a team pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the team object that the specified target number represents. If it * + * doesn't represent a legal team then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * As_Team(TARGET target) +{ + return(Is_Target_Team(target) ? Teams.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_TeamType -- Converts a target into a team type pointer. * + * * + * This routine will convert the specified target number into a team type pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the team type represented by the target number. If the * + * target number doesn't represent a legal team type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * As_TeamType(TARGET target) +{ + return(Is_Target_TeamType(target) ? TeamTypes.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Animation -- Converts target value into animation pointer. * + * * + * This routine will convert the specified target number into an animation pointer. * + * * + * INPUT: target -- The target number to convert into an animation pointer. * + * * + * OUTPUT: Returns with a pointer to the legal animation that this target represents. If it * + * doesn't represent a legal animation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +AnimClass * As_Animation(TARGET target) +{ + return(Is_Target_Animation(target) ? Anims.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Bullet -- Converts the target into a bullet pointer. * + * * + * This routine will convert the specified target number into a bullet pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the bullet it specifies. If the target doesn't refer to * + * a legal bullet, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * As_Bullet(TARGET target) +{ + return(Is_Target_Bullet(target) ? Bullets.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * * + * This routine will convert the specified target value into an aircraft object pointer. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with a pointer to the aircraft that this target value represents. If the * + * specified target value doesn't represent an aircraft, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass * As_Aircraft(TARGET target) +{ + return(Is_Target_Aircraft(target) ? Aircraft.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * * + * This routine will take the target value specified and convert it into a TechnoClass * + * pointer if the target represents an object that has a TechnoClass. * + * * + * INPUT: target -- The target value to convert into a TechnoClass pointer. * + * * + * OUTPUT: Returns with a pointer to the associated object's TechnoClass. If the target * + * cannot be converted into a TechnoClass pointer, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * As_Techno(TARGET target) +{ + ObjectClass * obj = As_Object(target); + + if (obj && obj->Is_Techno()) { + return(TechnoClass *)obj; + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Object -- Converts a target value into an object pointer. * + * * + * This routine is used to convert the target value specified into an object pointer. If * + * the target doesn't represent an object or the target value is illegal, then NULL is * + * returned. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the object it represent, or NULL if not an object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * As_Object(TARGET target) +{ + int val = Target_Value(target); + ObjectClass * object = NULL; + switch (Target_Kind(target)) { + case RTTI_INFANTRY: + object = Infantry.Raw_Ptr(val); + break; + + case RTTI_UNIT: + object = Units.Raw_Ptr(val); + break; + + case RTTI_VESSEL: + object = Vessels.Raw_Ptr(val); + break; + + case RTTI_BUILDING: + object = Buildings.Raw_Ptr(val); + break; + + case RTTI_AIRCRAFT: + object = Aircraft.Raw_Ptr(val); + break; + + case RTTI_TERRAIN: + object = Terrains.Raw_Ptr(val); + break; + + case RTTI_BULLET: + object = Bullets.Raw_Ptr(val); + break; + + case RTTI_ANIM: + object = Anims.Raw_Ptr(val); + break; + + default: + break; + } + + /* + ** Special check to ensure that a target value that references an + ** invalid object will not be converted back into an object pointer. + ** This condition is rare, but could occur in a network game if the + ** object it refers to is destroyed between the time an event message + ** is sent and when it is received. + */ + if (object != NULL && !object->IsActive) { + object = NULL; + } + + return(object); +} + + +/*********************************************************************************************** + * As_Unit -- Converts a target value into a unit pointer. * + * * + * This routine is used to convert the target value specified into a pointer to a unit * + * object. * + * * + * INPUT: target -- The target value to convert into a unit pointer. * + * * + * OUTPUT: Returns with a pointer to the unit the target value represents or NULL if not * + * a unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * As_Unit(TARGET target) +{ + return(Is_Target_Unit(target) ? Units.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Vessel -- Converts a target number into a vessel pointer. * + * * + * Use this routine to conver the specified target number into a pointer to a vessel object * + * that it represents. * + * * + * INPUT: target -- The target number to convert to a vessel pointer. * + * * + * OUTPUT: Returns with a pointer to the vessel object that this target value represents. If * + * the target number does not represent a vessel, then null is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * As_Vessel(TARGET target) +{ + return(Is_Target_Vessel(target) ? Vessels.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Infantry -- If the target is infantry, return a pointer to it. * + * * + * This routine will translate the specified target value into an infantry pointer if the * + * target actually represents an infantry object. * + * * + * INPUT: target -- The target to convert to a pointer. * + * * + * OUTPUT: Returns a pointer to the infantry object that this target value represents. If * + * the target doesn't represent an infantry object, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * As_Infantry(TARGET target) +{ + return(Is_Target_Infantry(target) ? Infantry.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Building -- Converts a target value into a building object pointer. * + * * + * This routine is used to convert the target value specified into a building pointer. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the building object that the target value represents. * + * If it doesn't represent a building, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * As_Building(TARGET target) +{ + return(Is_Target_Building(target) ? Buildings.Raw_Ptr(Target_Value(target)) : NULL); +} + + +#ifdef NEVER +/*********************************************************************************************** + * Target_Legal -- Determines if the specified target is legal. * + * * + * This routine is used to check for the legality of the target value specified. It is * + * necessary to call this routine if there is doubt about the the legality of the target. * + * It is possible for the unit that a target value represents to be eliminated and thus * + * rendering the target value invalid. * + * * + * INPUT: target -- The target value to check. * + * * + * OUTPUT: bool; Is the target value legal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +bool Target_Legal(TARGET target) +{ + if (!Target_Legal(target)) return(false); + + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Get_Strength() > 0 && obj->IsActive && !obj->IsInLimbo && obj->Class_Of().IsLegalTarget); + } + return(true); +} +#endif + + +/*********************************************************************************************** + * As_Cell -- Converts a target value into a cell number. * + * * + * This routine is used to convert the target value specified, into a cell value. This is * + * necessary for find path and other procedures that need a cell value. * + * * + * INPUT: target -- The target value to convert to a cell value. * + * * + * OUTPUT: Returns with the target value expressed as a cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL As_Cell(TARGET target) +{ + return(Coord_Cell(As_Coord(target))); +} + + +/*********************************************************************************************** + * As_Coord -- Converts a target value into a coordinate value. * + * * + * This routine is used to convert the target value specified into a coordinate value. It * + * is necessary for those procedures that require a coordinate value. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with the target expressed as a COORDINATE value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + * 11/16/1994 JLB : Simplified. * + *=============================================================================================*/ +COORDINATE As_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + int v = Target_Value(target); + + int x = ((v & 0x0FFF) << 4) + 0x0008; + int y = (((v>>12) & 0x0FFF) << 4) + 0x0008; + return(XY_Coord(x, y)); + +// return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + assert(obj->IsActive); + return(obj->Target_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * * + * This routine will convert the specified target into a coordinate location. This location * + * is used when moving to the target specified. For cells, this is the center of the cell. * + * For special buildings that allow docking, it is the center location of the docking * + * bay. * + * * + * INPUT: target -- The target to convert into a coordinate value. * + * * + * OUTPUT: Returns with the docking coordinate of the target value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE As_Movement_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Docking_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * TargetClass::As_Object -- Converts a target into an object pointer. * + * * + * If the target represents an object of some type, then this routine will return a * + * pointer to the object. Otherwise it will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object that this target represents or NULL if it * + * doesn't represent a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +AbstractClass * xTargetClass::As_Abstract(void) const +{ + switch ((RTTIType)*this) { + case RTTI_TEAM: + return(Teams.Raw_Ptr(Value())); + + case RTTI_BULLET: + return(Bullets.Raw_Ptr(Value())); + + case RTTI_OVERLAY: + return(Overlays.Raw_Ptr(Value())); + + case RTTI_SMUDGE: + return(Smudges.Raw_Ptr(Value())); + + case RTTI_UNIT: + return(Units.Raw_Ptr(Value())); + + case RTTI_VESSEL: + return(Vessels.Raw_Ptr(Value())); + + case RTTI_BUILDING: + return(Buildings.Raw_Ptr(Value())); + + case RTTI_INFANTRY: + return(Infantry.Raw_Ptr(Value())); + + case RTTI_AIRCRAFT: + return(Aircraft.Raw_Ptr(Value())); + + case RTTI_TERRAIN: + return(Terrains.Raw_Ptr(Value())); + + case RTTI_ANIM: + return(Anims.Raw_Ptr(Value())); + + default: + break; + } + return(0); +} + + +AbstractTypeClass * xTargetClass::As_TypeClass(void) const +{ + switch ((RTTIType)*this) { + case RTTI_TEAMTYPE: + return(TeamTypes.Raw_Ptr(Value())); + + case RTTI_TRIGGERTYPE: + return(TriggerTypes.Raw_Ptr(Value())); + + case RTTI_BULLETTYPE: + return((BulletTypeClass *)&BulletTypeClass::As_Reference(BulletType(Value()))); + + case RTTI_OVERLAY: + return((OverlayTypeClass *)&OverlayTypeClass::As_Reference(OverlayType(Value()))); + + case RTTI_SMUDGE: + return((SmudgeTypeClass *)&SmudgeTypeClass::As_Reference(SmudgeType(Value()))); + + case RTTI_UNIT: + return((UnitTypeClass *)&UnitTypeClass::As_Reference(UnitType(Value()))); + + case RTTI_VESSEL: + return((VesselTypeClass *)&VesselTypeClass::As_Reference(VesselType(Value()))); + + case RTTI_BUILDING: + return((BuildingTypeClass *)&BuildingTypeClass::As_Reference(StructType(Value()))); + + case RTTI_INFANTRY: + return((InfantryTypeClass *)&InfantryTypeClass::As_Reference(InfantryType(Value()))); + + case RTTI_AIRCRAFT: + return((AircraftTypeClass *)&AircraftTypeClass::As_Reference(AircraftType(Value()))); + + case RTTI_TERRAIN: + return((TerrainTypeClass *)&TerrainTypeClass::As_Reference(TerrainType(Value()))); + + case RTTI_ANIM: + return((AnimTypeClass *)&AnimTypeClass::As_Reference(AnimType(Value()))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * * + * This routine is used to convert the target object into a pointer to a techno class * + * object. If the target doesn't specify a techno class object, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the techno class object that this target represents or * + * else it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TechnoClass * xTargetClass::As_Techno(void) const +{ + switch ((RTTIType)*this) { + case RTTI_UNIT: + return(Units.Raw_Ptr(Value())); + + case RTTI_VESSEL: + return(Vessels.Raw_Ptr(Value())); + + case RTTI_BUILDING: + return(Buildings.Raw_Ptr(Value())); + + case RTTI_INFANTRY: + return(Infantry.Raw_Ptr(Value())); + + case RTTI_AIRCRAFT: + return(Aircraft.Raw_Ptr(Value())); + + default: + break; + } + return(0); +} + + +ObjectClass * xTargetClass::As_Object(void) const +{ + switch ((RTTIType)*this) { + case RTTI_TERRAIN: + return(Terrains.Raw_Ptr(Value())); + + case RTTI_SMUDGE: + return(Smudges.Raw_Ptr(Value())); + + case RTTI_OVERLAY: + return(Overlays.Raw_Ptr(Value())); + + case RTTI_BULLET: + return(Bullets.Raw_Ptr(Value())); + + case RTTI_ANIM: + return(Anims.Raw_Ptr(Value())); + + case RTTI_UNIT: + return(Units.Raw_Ptr(Value())); + + case RTTI_VESSEL: + return(Vessels.Raw_Ptr(Value())); + + case RTTI_BUILDING: + return(Buildings.Raw_Ptr(Value())); + + case RTTI_INFANTRY: + return(Infantry.Raw_Ptr(Value())); + + case RTTI_AIRCRAFT: + return(Aircraft.Raw_Ptr(Value())); + + default: + break; + } + return(0); +} + + + + +/*********************************************************************************************** + * As_Target -- Converts a cell into a target value. * + * * + * This routine will convert a cell into a target value. * + * * + * INPUT: cell -- The cell number that will be coerced into a target value. * + * * + * OUTPUT: Returns with the target value that this cell represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(CELL cell) +{ + int x = Cell_X(cell); + int y = Cell_Y(cell); + + x <<= 4; + y <<= 4; + + x += 0x0008; + y += 0x0008; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_Target -- Converts a coordinate into a target value. * + * * + * This routine is used to convert the specified coordinate into a target value. * + * * + * INPUT: coord -- The coordinate that is to be converted into a target value. * + * * + * OUTPUT: Returns with the target value that represents the coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(COORDINATE coord) +{ + int x = Coord_X(coord); + int y = Coord_Y(coord); + + x >>= 4; + y >>= 4; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * * + * This routine will conver the specified target number into a pointer to the techno * + * type class that it represents. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the TechnoTypeClass object that the target number * + * represents. If it doesn't represent that kind of object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * As_TechnoType(TARGET target) +{ + int val = Target_Value(target); + switch (Target_Kind(target)) { + case RTTI_INFANTRYTYPE: + return(&InfantryTypeClass::As_Reference(InfantryType(val))); + + case RTTI_UNITTYPE: + return(&UnitTypeClass::As_Reference(UnitType(val))); + + case RTTI_VESSELTYPE: + return(&VesselTypeClass::As_Reference(VesselType(val))); + + case RTTI_AIRCRAFTTYPE: + return(&AircraftTypeClass::As_Reference(AircraftType(val))); + + case RTTI_BUILDINGTYPE: + return(&BuildingTypeClass::As_Reference(StructType(val))); + + } + return(NULL); +} + + +/*********************************************************************************************** + * As_TriggerType -- Convert the specified target into a trigger type. * + * * + * This routine will conver the target number into a pointer to the trigger type it * + * represents. * + * * + * INPUT: target -- The target value to convert into a trigger type pointer. * + * * + * OUTPUT: Returns with a pointer to the trigger type object that the specified target value * + * represents. If it doesn't represent a trigger type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * As_TriggerType(TARGET target) +{ + if (Target_Kind(target) == RTTI_TRIGGERTYPE) { + return(TriggerTypes.Raw_Ptr(Target_Value(target))); + } + return(NULL); +} \ No newline at end of file diff --git a/CODE/TARGET.H b/CODE/TARGET.H new file mode 100644 index 0000000..d3c5dbb --- /dev/null +++ b/CODE/TARGET.H @@ -0,0 +1,176 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TARGET.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TARGET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARGET_H +#define TARGET_H + + +inline RTTIType Target_Kind(TARGET a) +{ + return(RTTIType(((TARGET_COMPOSITE &)a).Sub.Exponent)); +} + +inline unsigned Target_Value(TARGET a) +{ + return(((TARGET_COMPOSITE &)a).Sub.Mantissa); +} + +inline bool Is_Target_Team(TARGET a) {return (Target_Kind(a) == RTTI_TEAM);} +inline bool Is_Target_TeamType(TARGET a) {return (Target_Kind(a) == RTTI_TEAMTYPE);} +inline bool Is_Target_Trigger(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGER);} +inline bool Is_Target_TriggerType(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGERTYPE);} +inline bool Is_Target_Infantry(TARGET a) {return (Target_Kind(a) == RTTI_INFANTRY);} +inline bool Is_Target_Bullet(TARGET a) {return (Target_Kind(a) == RTTI_BULLET);} +inline bool Is_Target_Terrain(TARGET a) {return (Target_Kind(a) == RTTI_TERRAIN);} +inline bool Is_Target_Cell(TARGET a) {return (Target_Kind(a) == RTTI_CELL);} +inline bool Is_Target_Unit(TARGET a) {return (Target_Kind(a) == RTTI_UNIT);} +inline bool Is_Target_Vessel(TARGET a) {return (Target_Kind(a) == RTTI_VESSEL);} +inline bool Is_Target_Building(TARGET a) {return (Target_Kind(a) == RTTI_BUILDING);} +inline bool Is_Target_Template(TARGET a) {return (Target_Kind(a) == RTTI_TEMPLATE);} +inline bool Is_Target_Aircraft(TARGET a) {return (Target_Kind(a) == RTTI_AIRCRAFT);} +inline bool Is_Target_Animation(TARGET a) {return (Target_Kind(a) == RTTI_ANIM);} +inline bool Is_Target_Object(TARGET a) +{ + return (Target_Kind(a) == RTTI_TERRAIN || + Target_Kind(a) == RTTI_UNIT || + Target_Kind(a) == RTTI_VESSEL || + Target_Kind(a) == RTTI_INFANTRY || + Target_Kind(a) == RTTI_BUILDING || + Target_Kind(a) == RTTI_AIRCRAFT); +} + + +TARGET As_Target(CELL cell); +TARGET As_Target(COORDINATE coord); +//inline TARGET As_Target(CELL cell) {return (TARGET)(((unsigned)RTTI_CELL << TARGET_MANTISSA) | cell);} + +class UnitClass; +class BuildingClass; +class TechnoClass; +class TerrainClass; +class ObjectClass; +class InfantryClass; +class BulletClass; +class TriggerClass; +class TeamClass; +class TeamTypeClass; +class AnimClass; +class AircraftClass; +class VesselClass; +class CellClass; +class TriggerTypeClass; + +/* +** Must not have a constructor since Watcom cannot handle a class that has a constructor if +** that class object is in a union. Don't use this class for normal purposes. Use the TargetClass +** instead. The xTargetClass is only used in one module for a special reason -- keep it that way. +*/ +class xTargetClass +{ + protected: + + TARGET_COMPOSITE Target; + + public: + + // conversion operator to RTTIType + operator RTTIType (void) const {return(RTTIType(Target.Sub.Exponent));} + + // comparison operator + int operator == (xTargetClass & tgt) {return (tgt.Target.Target==Target.Target ? 1 : 0);} + + // conversion operator to regular TARGET type + TARGET As_TARGET(void) const {return(Target.Target);} + + unsigned Value(void) const {return(Target.Sub.Mantissa);}; + + void Invalidate(void) {Target.Sub.Exponent = RTTI_NONE;Target.Sub.Mantissa = -1;} + bool Is_Valid(void) const {return (Target.Sub.Exponent != RTTI_NONE);} + + TARGET As_Target(void) const {return(Target.Target);} + AbstractTypeClass * As_TypeClass(void) const; + AbstractClass * As_Abstract(void) const; + TechnoClass * As_Techno(void) const; + ObjectClass * As_Object(void) const; + CellClass * As_Cell(void) const; + + /* + ** Helper routines to combine testing for, and fetching a pointer to, the + ** type of object indicated. + */ + TriggerTypeClass * As_TriggerType(void) const {if (*this == RTTI_TRIGGERTYPE) return((TriggerTypeClass *)As_TypeClass());return(0);} + TeamTypeClass * As_TeamType(void) const {if (*this == RTTI_TEAMTYPE) return((TeamTypeClass *)As_TypeClass());return(0);} + TerrainClass * As_Terrain(void) const {if (*this == RTTI_TERRAIN) return((TerrainClass *)As_Abstract());return(0);} + BulletClass * As_Bullet(void) const {if (*this == RTTI_BULLET) return((BulletClass *)As_Abstract());return(0);} + AnimClass * As_Anim(void) const {if (*this == RTTI_ANIM) return((AnimClass *)As_Abstract());return(0);} + TeamClass * As_Team(void) const {if (*this == RTTI_TEAM) return((TeamClass *)As_Abstract());return(0);} + InfantryClass * As_Infantry(void) const {if (*this == RTTI_INFANTRY) return((InfantryClass *)As_Techno());return(0);} + UnitClass * As_Unit(void) const {if (*this == RTTI_UNIT) return((UnitClass *)As_Techno());return(0);} + BuildingClass * As_Building(void) const {if (*this == RTTI_BUILDING) return((BuildingClass *)As_Techno());return(0);} + AircraftClass * As_Aircraft(void) const {if (*this == RTTI_AIRCRAFT) return((AircraftClass *)As_Techno());return(0);} + VesselClass * As_Vessel(void) const {if (*this == RTTI_VESSEL) return((VesselClass *)As_Techno());return(0);} +}; + +/* +** This class only serves as a wrapper to the xTargetClass. This class must not define any members except +** for the constructors. This is because the xTargetClass is used in a union and this target object is +** used as its initializer. If this class had any extra members they would not be properly copied and +** communicated to the other machines in a network/modem game. Combining this class with xTargetClass would +** be more efficient, but Watcom doesn't allow class objects that have a constructor to be part of a union [even +** if the class object has a default constructor!]. +*/ +class TargetClass : public xTargetClass +{ + public: + + TargetClass(void) {Invalidate();} + TargetClass(NoInitClass const &) {} + TargetClass(RTTIType rtti, int id) { + Target.Sub.Exponent = rtti; + Target.Sub.Mantissa = id; + } + TargetClass(CELL cell) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = cell; + } + TargetClass(TARGET target); + TargetClass(AbstractClass const * ptr); + TargetClass(AbstractTypeClass const * ptr); + TargetClass(CellClass const * ptr); +}; + +#endif diff --git a/CODE/TASM.CFG b/CODE/TASM.CFG new file mode 100644 index 0000000..e69de29 diff --git a/CODE/TCPIP.CPP b/CODE/TCPIP.CPP new file mode 100644 index 0000000..a139ac2 --- /dev/null +++ b/CODE/TCPIP.CPP @@ -0,0 +1,909 @@ +/* +** 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 . +*/ + + +/*************************************************************************** + ** 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 - Red Alert * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 20th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Member functions of the TcpipManagerClass which provides the Winsock * + * interface for C&C * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * TMC::Close -- restores any currently in use Winsock resources * + * TMC::Init -- Initialised Winsock for use. * + * TMC::Start_Server -- Initialise connection and start listening. * + * TMC::Read -- read any pending input from the stream socket * + * TMC::Write -- Send data via the Winsock streaming socket * + * TMC::Add_Client -- A client has requested to connect. * + * TMC::Message_Handler -- Message handler for Winsock. * + * TMC::Set_Host_Address -- Set the address of the host * + * TMC::Start_Client -- Start trying to connect to a game host * + * TMC::Close_Socket -- Close an opened Winsock socket. * + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" + + +/* +** Nasty globals +*/ +#ifndef WOLAPI_INTEGRATION +BOOL Server; //Is this player acting as client or server +#endif +TcpipManagerClass Winsock; //The object for interfacing with Winsock + + + +/*********************************************************************************************** + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +TcpipManagerClass::TcpipManagerClass(void) +{ + WinsockInitialised = FALSE; + Connected = FALSE; + UseUDP = TRUE; + SocketReceiveBuffer = 4096; + SocketSendBuffer = 4096; +} + + +/*********************************************************************************************** + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +TcpipManagerClass::~TcpipManagerClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * TMC::Close -- restores any currently in use Winsock resources * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + if (Async){ + WSACancelAsyncRequest(Async); + } + + /* + ** Close any open sockets + */ + if (ConnectSocket != INVALID_SOCKET){ + Close_Socket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + if (ListenSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + ListenSocket = INVALID_SOCKET; + } + + if (UDPSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + UDPSocket = INVALID_SOCKET; + } + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = FALSE; + Connected = FALSE; +} + + + +/*********************************************************************************************** + * TMC::Init -- Initialised Winsock for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (TRUE); + + /* + ** Initialise sockets to null + */ + ListenSocket = INVALID_SOCKET; + ConnectSocket =INVALID_SOCKET; + UDPSocket = INVALID_SOCKET; + + /* + ** Start WinSock, and fill in our WinSockData + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, &WinsockInfo); + if (rc != 0) { + return (FALSE); + } + + /* + ** Check the Winsock version number + */ + if ((WinsockInfo.wVersion & 0x00ff) != (version & 0x00ff) || + (WinsockInfo.wVersion >> 8) != (version >> 8)) { + return (FALSE); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = TRUE; + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Start_Server -- initialise out connection as the server. Start listening for clients. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:56PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Server(void) +{ + int i; + //struct sockaddr_in addr; + + Start_Client(); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; iCheck(); + + /* + ** Copy any outstanding incoming data to the buffer provided + */ + if (ReceiveBuffers[RXBufferTail].InUse){ + memcpy (buffer, ReceiveBuffers[RXBufferTail].Buffer, + MIN(ReceiveBuffers[RXBufferTail].DataLength, buffer_len)); + ReceiveBuffers[RXBufferTail].InUse = false; + + bytes_copied = MIN(ReceiveBuffers[RXBufferTail++].DataLength, buffer_len); + + RXBufferTail &= WS_NUM_RX_BUFFERS-1; + } + + return (bytes_copied); +} + + + +/*********************************************************************************************** + * TMC::Write -- Send data via the Winsock UDP socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Write(void *buffer, int buffer_len) +{ + char *source_buf = (char*) buffer; + + /* + ** Copy the data to one of the classes internal buffers + */ + if (!TransmitBuffers[TXBufferHead].InUse){ + memcpy (TransmitBuffers[TXBufferHead].Buffer, + buffer, + MIN (buffer_len, WS_INTERNET_BUFFER_LEN)); + TransmitBuffers[TXBufferHead].InUse = true; + TransmitBuffers[TXBufferHead++].DataLength = MIN(buffer_len, WS_INTERNET_BUFFER_LEN); + TXBufferHead &= WS_NUM_TX_BUFFERS-1; + } + + /* + ** Send a message to ourselves to start off the event + */ + if (UseUDP){ + SendMessage(MainWindow, WM_UDPASYNCEVENT, 0, (LONG)FD_WRITE); + }else{ + SendMessage(MainWindow, WM_ASYNCEVENT, 0, (LONG)FD_WRITE); + } + /* + ** Make sure the message loop gets called because all the Winsock notifications + ** are done via messages. + */ + Keyboard->Check(); +} + + + + + + +/*********************************************************************************************** + * TMC::Add_Client -- a client has requested to connect. Make the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if client was successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:02PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Add_Client(void) +{ + struct sockaddr_in addr; + int addrsize; + bool delay = TRUE; + + /* + ** Accept the connection. If there is an error then dont do anything else + */ + addrsize = sizeof(addr); + ConnectSocket = accept (ListenSocket, (LPSOCKADDR)&addr, &addrsize); + if (ConnectSocket == INVALID_SOCKET) { + //Show_Error("accept", WSAGetLastError()); + return(FALSE); + } + + /* + ** Set options for this socket + */ + setsockopt (ConnectSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&delay, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Save the clients address + */ + memcpy(&ClientIPAddress, &addr.sin_addr.s_addr,4); + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr,4); + + /* + ** Initiate an asynchronous host lookup by address. Our window will receive notification + ** when this is complete or when it times out. + */ + Async = WSAAsyncGetHostByAddr (MainWindow, WM_HOSTBYADDRESS, + (char const *)&addr.sin_addr, 4, PF_INET, &HostBuff[0], + MAXGETHOSTSTRUCT); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, + FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (ConnectSocket); + return(FALSE); + } + + /* + ** Create our UDP socket + */ + UDPSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (UDPSocket == INVALID_SOCKET) { + return (FALSE); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(UDPSocket, (LPSOCKADDR)&addr, sizeof(addr)) == + SOCKET_ERROR) { + Close_Socket(UDPSocket); + ConnectStatus = NOT_CONNECTING; + return(FALSE); + } + + /* + ** Set options for the UDP socket + */ + setsockopt (UDPSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (UDPSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (UDPSocket, MainWindow, WM_UDPASYNCEVENT, + FD_READ | FD_WRITE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (UDPSocket); + Close_Socket (ConnectSocket); + return(FALSE); + } + + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Message_Handler(HWND, UINT message, UINT , LONG lParam) +{ + struct hostent *hentry; + struct sockaddr_in addr; + int event; + int rc; + int addr_len; + + switch (message){ + + /* + ** Handle the GetHostByAddress result + */ + case WM_HOSTBYADDRESS: + + if (IsServer){ + /* + ** We are the server + */ + ConnectStatus = CONNECTING; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (&ClientName[0], hentry->h_name); + } + Async = 0; + return; + + }else{ + /* + ** We are the client + */ + ConnectStatus = CONTACTING_SERVER; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (Server.Name, hentry->h_name); + } + else { + Server.Name[0] = 0; + } + Async = 0; + return; + } + + + /* + ** Retrieve host by name: Start connecting now that we have the + ** address. + */ + case WM_HOSTBYNAME: + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + memcpy (&(Server.Addr.s_addr), hentry->h_addr, 4); + memcpy(&UDPIPAddress, hentry->h_addr, 4); + strcpy (Server.DotAddr, inet_ntoa(Server.Addr)); + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + Server.Name[0] = 0; + strcpy (Server.DotAddr, "????"); + ConnectStatus = SERVER_ADDRESS_LOOKUP_FAILED; + } + Async = 0; + return; + + + /* + ** Connection is ready - accept the client + */ + case WM_ACCEPT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + return; + } + if (Add_Client()) { + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + } + return; + + + + /* + ** Handle UDP packet events + */ + case WM_UDPASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + + case FD_READ: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + addr_len = sizeof(addr); + rc = recvfrom(UDPSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0, + (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(UDPSocket); + return; + } + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr, 4); + Copy_To_In_Buffer(rc); + return; + + + case FD_WRITE: + if (UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, &UDPIPAddress, 4); + + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (TransmitBuffers[TXBufferTail].InUse){ + rc = sendto(UDPSocket, + TransmitBuffers[TXBufferTail].Buffer, + TransmitBuffers[TXBufferTail].DataLength, + 0, + (LPSOCKADDR)&addr, + sizeof (addr)); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(UDPSocket); + } + break; + } + TransmitBuffers[TXBufferTail++].InUse = false; + TXBufferTail &= WS_NUM_TX_BUFFERS-1; + } + return; + } + } + + + + /* + ** Handle the asynchronous event callbacks + */ + case WM_ASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + /* + ** FD_CLOSE: the client has gone away. Remove the client from our system. + */ + case FD_CLOSE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0 && rc != WSAECONNRESET) { + ConnectStatus = CONNECTION_LOST; + return; + } + if (Async != 0) { + WSACancelAsyncRequest(Async); + } + WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, 0); + Close_Socket (ConnectSocket); + ConnectSocket = INVALID_SOCKET; + //Connected = FALSE; + ConnectStatus = CONNECTION_LOST; + break; + + /* + ** FD_CONNECT: A connection was made, or an error occurred. + */ + case FD_CONNECT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_CONNECT; + return; + } + + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + return; + + } + } +} + + + +/*********************************************************************************************** + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer to our internal buffer * + * * + * * + * * + * INPUT: bytes to copy * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:17PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Copy_To_In_Buffer(int bytes) +{ + if (!ReceiveBuffers[RXBufferHead].InUse){ + memcpy (ReceiveBuffers[RXBufferHead].Buffer, ReceiveBuffer, MIN(bytes, WS_INTERNET_BUFFER_LEN)); + ReceiveBuffers[RXBufferHead].InUse = true; + ReceiveBuffers[RXBufferHead++].DataLength = MIN(bytes, WS_INTERNET_BUFFER_LEN); + RXBufferHead &= WS_NUM_RX_BUFFERS-1; + } +} + + + +/*********************************************************************************************** + * TMC::Set_Host_Address -- Set the address of the host game we want to connect to * + * * + * * + * * + * INPUT: ptr to address string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Set_Host_Address(char *address) +{ + strcpy(HostAddress, address); +} + + + +/*********************************************************************************************** + * TMC::Start_Client -- Start trying to connect to a game host * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Client(void) +{ + struct sockaddr_in addr; + bool delay = true; + int i; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; i. +*/ + +/* $Header: /CounterStrike/TCPIP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 11th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#ifndef WOLAPI_INTEGRATION +extern bool Server; +#endif + +#define FORCE_WINSOCK 1 + +#define WINSOCK_MINOR_VER 1 +#define WINSOCK_MAJOR_VER 1 +#define PORTNUM 0x1000 +#define UDP_PORT 0x1001 +#define WS_INTERNET_BUFFER_LEN 1024 +#define WS_NUM_TX_BUFFERS 16 //Must be a power of 2 +#define WS_NUM_RX_BUFFERS 16 //MUst be a power of 2 +#define WS_RECEIVE_BUFFER_LEN 1024 +//#define WS_IN_BUFFER_LEN 8192 +//#define WS_OUT_BUFFER_LEN 8192 + +#define PLANET_WESTWOOD_HANDLE_MAX 20 +#define PLANET_WESTWOOD_PASSWORD_MAX 20 +#define IP_ADDRESS_MAX 40 +#define PORT_NUMBER_MAX 6 + +//........................................................................... +// Custom messages: WM_USER + 1 to WM_USER + 100 +// These will be sent to the dialog procedure, for display only. +//........................................................................... +#define WM_UPDATE_STATUS (WM_USER + 1) // update status text +#define WM_UPDATE_CLIENTS (WM_USER + 2) // update client list box +#define WM_UPDATE_MESSAGE (WM_USER + 3) // update received message list + +//........................................................................... +// Messages for Async processing. +//........................................................................... +#define WM_ACCEPT (WM_USER + 101) // client wants to connect +#define WM_HOSTBYADDRESS (WM_USER + 102) // async get host by address +#define WM_HOSTBYNAME (WM_USER + 103) // async get host by name +#define WM_ASYNCEVENT (WM_USER + 104) // other Async event +#define WM_UDPASYNCEVENT (WM_USER + 105) // UDP socket Async event + + +class TcpipManagerClass { + + public: + + TcpipManagerClass(void); + ~TcpipManagerClass(void); + + BOOL Init(void); + void Start_Server(void); + void Start_Client(void); + void Close_Socket(SOCKET s); + void Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + void Copy_To_In_Buffer(int bytes); + int Read(void *buffer, int buffer_len); + void Write(void *buffer, int buffer_len); + BOOL Add_Client(void); + void Close(void); + void Set_Host_Address(char *address); + void Set_Protocol_UDP(BOOL state); + void Clear_Socket_Error(SOCKET socket); + + inline BOOL Get_Connected(void) {return (Connected);} + + typedef enum ConnectStatusEnum { + CONNECTED_OK = 0, + NOT_CONNECTING, + CONNECTING, + UNABLE_TO_CONNECT_TO_SERVER, + CONTACTING_SERVER, + SERVER_ADDRESS_LOOKUP_FAILED, + RESOLVING_HOST_ADDRESS, + UNABLE_TO_ACCEPT_CLIENT, + UNABLE_TO_CONNECT, + CONNECTION_LOST + } ConnectStatusEnum; + + inline ConnectStatusEnum Get_Connection_Status(void) {return (ConnectStatus);} + + private: + + //........................................................................... + // This structure defines all the info we need about a host + //........................................................................... + typedef struct { + struct in_addr Addr; // address + char DotAddr[16]; // decimal-dot address string + char Name[255]; // character-string name + } HostType; + + typedef struct { + char Buffer[WS_INTERNET_BUFFER_LEN]; + int DataLength; + bool InUse:1; + } InternetBufferType; + + + BOOL WinsockInitialised; + WSADATA WinsockInfo; + SOCKET ListenSocket; + SOCKET ConnectSocket; + SOCKET UDPSocket; + IN_ADDR ClientIPAddress; + HANDLE Async; + char HostBuff[MAXGETHOSTSTRUCT]; + char ClientName[128]; + char ReceiveBuffer[WS_RECEIVE_BUFFER_LEN]; + //char InBuffer[WS_IN_BUFFER_LEN]; + //int InBufferHead; + //int InBufferTail; + //char OutBuffer[WS_OUT_BUFFER_LEN]; + //int OutBufferHead; + //int OutBufferTail; + BOOL IsServer; + BOOL Connected; + HostType Server; + char HostAddress[IP_ADDRESS_MAX]; + ConnectStatusEnum ConnectStatus; + BOOL UseUDP; + IN_ADDR UDPIPAddress; + int SocketReceiveBuffer; + int SocketSendBuffer; + InternetBufferType ReceiveBuffers[WS_NUM_TX_BUFFERS]; + InternetBufferType TransmitBuffers[WS_NUM_RX_BUFFERS]; + int TXBufferHead; + int TXBufferTail; + int RXBufferHead; + int RXBufferTail; + +}; + + +extern TcpipManagerClass Winsock; + +extern char PlanetWestwoodIPAddress[IP_ADDRESS_MAX]; +extern long PlanetWestwoodPortNumber; +extern bool PlanetWestwoodIsHost; +extern int Read_Game_Options(char *); + + +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + + + +#endif //WIN32 diff --git a/CODE/TDATA.CPP b/CODE/TDATA.CPP new file mode 100644 index 0000000..aded174 --- /dev/null +++ b/CODE/TDATA.CPP @@ -0,0 +1,919 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TDATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainTypeClass::As_Reference -- Fetches a reference to the terrain type object specified* + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * TerrainTypeClass::Display -- Display a generic terrain object. * + * TerrainTypeClass::From_Name -- Convert name to terrain type. * + * TerrainTypeClass::Init -- Loads terrain object shape files. * + * TerrainTypeClass::Init_Heap -- Initialize the terrain object heap. * + * TerrainTypeClass::Occupy_List -- Returns with the occupy list for the terrain object type.* + * TerrainTypeClass::One_Time -- Performs any special one time processing for terrain types. * + * TerrainTypeClass::Overlap_List -- Fetches the overlap list for the terrain type. * + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * TerrainTypeClass::TerrainTypeClass -- The general constructor for the terrain type objects* + * TerrainTypeClass::operator delete -- Returns a terrain type object back to the mem pool. * + * TerrainTypeClass::operator new -- Allocates a terrain type object from special pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" +#include "type.h" + + +static short const _List000011101000[] = {MAP_CELL_W, MAP_CELL_W+1, MAP_CELL_W+2, MAP_CELL_W*2, REFRESH_EOL}; +static short const _List000110[] = {MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List001011100110[] = {2, MAP_CELL_W, MAP_CELL_W+1, MAP_CELL_W+2, MAP_CELL_W*2+1, MAP_CELL_W*2+2, REFRESH_EOL}; +static short const _List0010[] = {MAP_CELL_W, REFRESH_EOL}; +static short const _List0011[] = {MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List001[] = {2, REFRESH_EOL}; +static short const _List010110[] = {1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List01[] = {1, REFRESH_EOL}; +static short const _List11[] = {0, 1, REFRESH_EOL}; +static short const _List1001[] = {0, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List1010[] = {0, MAP_CELL_W, REFRESH_EOL}; +static short const _List101001[] = {0, 2, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List10[] = {0, REFRESH_EOL}; +static short const _List110000011001[] = {0, 1, MAP_CELL_W+3, MAP_CELL_W*2, MAP_CELL_W*2+3, REFRESH_EOL}; +static short const _List110001[] = {0, 1, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List1100[] = {0, 1, REFRESH_EOL}; +static short const _List110110[] = {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List1101[] = {0, 1, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List1111[] = {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List111000010110[] = {0, 1, 2, MAP_CELL_W+3, MAP_CELL_W*2+1, MAP_CELL_W*2+2, REFRESH_EOL}; + + +static TerrainTypeClass const Mine( + TERRAIN_MINE, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "MINE", + TXT_ORE_MINE, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes01( + TERRAIN_BOXES01, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES01", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes02( + TERRAIN_BOXES02, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES02", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes03( + TERRAIN_BOXES03, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES03", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes04( + TERRAIN_BOXES04, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES04", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes05( + TERRAIN_BOXES05, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES05", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes06( + TERRAIN_BOXES06, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES06", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes07( + TERRAIN_BOXES07, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES07", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes08( + TERRAIN_BOXES08, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES08", + TXT_CRATES, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Boxes09( + TERRAIN_BOXES09, + THEATERF_INTERIOR, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "BOXES09", + TXT_CRATES, + (short const *)_List10, + NULL +); + +static TerrainTypeClass const Ice01( + TERRAIN_ICE01, + THEATERF_SNOW, + XYP_COORD(24,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + true, // Is based on the water? + "ICE01", + TXT_ICE, + (short const *)_List1111, + NULL +); +static TerrainTypeClass const Ice02( + TERRAIN_ICE02, + THEATERF_SNOW, + XYP_COORD(12,24), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + true, // Is based on the water? + "ICE02", + TXT_ICE, + (short const *)_List1010, + NULL +); +static TerrainTypeClass const Ice03( + TERRAIN_ICE03, + THEATERF_SNOW, + XYP_COORD(24,12), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + true, // Is based on the water? + "ICE03", + TXT_ICE, + (short const *)_List11, + NULL +); +static TerrainTypeClass const Ice04( + TERRAIN_ICE04, + THEATERF_SNOW, + XYP_COORD(12,12), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + true, // Is based on the water? + "ICE04", + TXT_ICE, + (short const *)_List10, + NULL +); +static TerrainTypeClass const Ice05( + TERRAIN_ICE05, + THEATERF_SNOW, + XYP_COORD(12,12), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + true, // Is based on the water? + "ICE05", + TXT_ICE, + (short const *)_List10, + NULL +); + +static TerrainTypeClass const Tree1Class( + TERRAIN_TREE1, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(11,41), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T01", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree2Class( + TERRAIN_TREE2, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(11,44), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T02", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree3Class( + TERRAIN_TREE3, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(12,45), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T03", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree5Class( + TERRAIN_TREE5, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(15,41), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T05", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree6Class( + TERRAIN_TREE6, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(16,37), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T06", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree7Class( + TERRAIN_TREE7, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(15,41), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T07", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree8Class( + TERRAIN_TREE8, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(14,22), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T08", + TXT_TREE, + (short const *)_List10, + (short const *)_List01 +); + +static TerrainTypeClass const Tree10Class( + TERRAIN_TREE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(25,43), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T10", + TXT_TREE, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree11Class( + TERRAIN_TREE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(23,44), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T11", + TXT_TREE, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree12Class( + TERRAIN_TREE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(14,36), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T12", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree13Class( + TERRAIN_TREE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T13", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1101 +); + +static TerrainTypeClass const Tree14Class( + TERRAIN_TREE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T14", + TXT_TREE, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree15Class( + TERRAIN_TREE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T15", + TXT_TREE, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree16Class( + TERRAIN_TREE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(13,36), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T16", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree17Class( + TERRAIN_TREE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(18,44), // Center base coordinate offset. + false, // Is it immune to normal combat damage? + false, // Is based on the water? + "T17", + TXT_TREE, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Clump1Class( + TERRAIN_CLUMP1, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(28,41), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "TC01", + TXT_TREE, + (short const *)_List000110, + (short const *)_List110001 +); + +static TerrainTypeClass const Clump2Class( + TERRAIN_CLUMP2, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(38,41), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "TC02", + TXT_TREE, + (short const *)_List010110, + (short const *)_List101001 +); + +static TerrainTypeClass const Clump3Class( + TERRAIN_CLUMP3, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(33,35), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "TC03", + TXT_TREE, + (short const *)_List110110, + (short const *)_List001 +); + +static TerrainTypeClass const Clump4Class( + TERRAIN_CLUMP4, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(44,49), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "TC04", + TXT_TREE, + (short const *)_List000011101000, + (short const *)_List111000010110 +); + +static TerrainTypeClass const Clump5Class( + TERRAIN_CLUMP5, + THEATERF_TEMPERATE|THEATERF_SNOW, + XYP_COORD(49,58), // Center base coordinate offset. + true, // Is it immune to normal combat damage? + false, // Is based on the water? + "TC05", + TXT_TREE, + (short const *)_List001011100110, + (short const *)_List110000011001 +); + + +/*********************************************************************************************** + * TerrainTypeClass::TerrainTypeClass -- The general constructor for the terrain type objects. * + * * + * This is the constructor for terrain type objects. It is only used to construct the * + * static (constant) terrain type objects. * + * * + * INPUT: see below.. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +TerrainTypeClass::TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_immune, + bool is_water, + char const * ininame, + int fullname, + short const * occupy, + short const * overlap) : + ObjectTypeClass(RTTI_TERRAINTYPE, + int(terrain), + true, + true, + false, + false, + true, + is_immune, + true, + fullname, + ininame), + Type(terrain), + CenterBase(centerbase), + Theater(theater), + IsWaterBased(is_water), + Occupy(occupy), + Overlap(overlap) +{ + MaxStrength = 800; + Armor = ARMOR_WOOD; +} + + +/*********************************************************************************************** + * TerrainTypeClass::operator new -- Allocates a terrain type object from special pool. * + * * + * This routine will allocated a terrain type class object from the memory pool set up * + * for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the freshly allocated terrain type object block. If * + * there was insufficient memory, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void * TerrainTypeClass::operator new(size_t) +{ + return(TerrainTypes.Alloc()); +} + + +/*********************************************************************************************** + * TerrainTypeClass::operator delete -- Returns a terrain type object back to the mem pool. * + * * + * This routine will return the supplied terrain type object back to the special memory * + * pool for whence it was originally allocated. * + * * + * INPUT: pointer -- Pointer to the terrain type object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::operator delete(void * pointer) +{ + TerrainTypes.Free((TerrainTypeClass *)pointer); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Init_Heap -- Initialize the terrain object heap. * + * * + * This routine preallocates the terrain type objects in the memory pool. It must be called * + * before the terrain type object data can be filled in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This must be called only once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Init_Heap(void) +{ + /* + ** These terrain type class objects must be allocated in the exact order that they + ** are specified in the TerrainType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new TerrainTypeClass(Tree1Class); // TERRAIN_TREE1 + new TerrainTypeClass(Tree2Class); // TERRAIN_TREE2 + new TerrainTypeClass(Tree3Class); // TERRAIN_TREE3 + new TerrainTypeClass(Tree5Class); // TERRAIN_TREE5 + new TerrainTypeClass(Tree6Class); // TERRAIN_TREE6 + new TerrainTypeClass(Tree7Class); // TERRAIN_TREE7 + new TerrainTypeClass(Tree8Class); // TERRAIN_TREE8 + new TerrainTypeClass(Tree10Class); // TERRAIN_TREE10 + new TerrainTypeClass(Tree11Class); // TERRAIN_TREE11 + new TerrainTypeClass(Tree12Class); // TERRAIN_TREE12 + new TerrainTypeClass(Tree13Class); // TERRAIN_TREE13 + new TerrainTypeClass(Tree14Class); // TERRAIN_TREE14 + new TerrainTypeClass(Tree15Class); // TERRAIN_TREE15 + new TerrainTypeClass(Tree16Class); // TERRAIN_TREE16 + new TerrainTypeClass(Tree17Class); // TERRAIN_TREE17 + new TerrainTypeClass(Clump1Class); // TERRAIN_CLUMP1 + new TerrainTypeClass(Clump2Class); // TERRAIN_CLUMP2 + new TerrainTypeClass(Clump3Class); // TERRAIN_CLUMP3 + new TerrainTypeClass(Clump4Class); // TERRAIN_CLUMP4 + new TerrainTypeClass(Clump5Class); // TERRAIN_CLUMP5 + new TerrainTypeClass(Ice01); // TERRAIN_ICE01 + new TerrainTypeClass(Ice02); // TERRAIN_ICE02 + new TerrainTypeClass(Ice03); // TERRAIN_ICE03 + new TerrainTypeClass(Ice04); // TERRAIN_ICE04 + new TerrainTypeClass(Ice05); // TERRAIN_ICE05 + new TerrainTypeClass(Boxes01); // TERRAIN_BOXES01 + new TerrainTypeClass(Boxes02); // TERRAIN_BOXES02 + new TerrainTypeClass(Boxes03); // TERRAIN_BOXES03 + new TerrainTypeClass(Boxes04); // TERRAIN_BOXES04 + new TerrainTypeClass(Boxes05); // TERRAIN_BOXES05 + new TerrainTypeClass(Boxes06); // TERRAIN_BOXES06 + new TerrainTypeClass(Boxes07); // TERRAIN_BOXES07 + new TerrainTypeClass(Boxes08); // TERRAIN_BOXES08 + new TerrainTypeClass(Boxes09); // TERRAIN_BOXES09 + new TerrainTypeClass(Mine); // TERRAIN_MINE +} + + +/*********************************************************************************************** + * TerrainTypeClass::One_Time -- Performs any special one time processing for terrain types. * + * * + * This routine will perform any special one time processing needed for the terrain * + * object types. Typically, this would load up artwork for terrain objects that have * + * artwork independant of the theater they appear in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::One_Time(void) +{ +} + +/*********************************************************************************************** + * TerrainTypeClass::Init -- Loads terrain object shape files. * + * * + * This routine is used to load up the terrain object shape files. * + * The shape files loaded depends on theater. * + * * + * INPUT: theater -- The theater to load the terrain shape data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + TerrainTypeClass const & terrain = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Clear any existing shape pointer. All terrain is theater specific, thus if + ** it isn't loaded in this routine, it shouldn't exist at all. + */ + ((void const *&)terrain.ImageData) = NULL; + + if (terrain.Theater & (1 << theater)) { + + /* + ** Load in the appropriate object shape data. + */ + _makepath(fullname, NULL, NULL, terrain.IniName, Theaters[theater].Suffix); + ((void const *&)terrain.ImageData) = MFCD::Retrieve(fullname); + + IsTheaterShape = true; //Let Build_Frame know that this is a theater specific shape + if (terrain.RadarIcon != NULL) delete[] (char *)terrain.RadarIcon; + ((void const *&)terrain.RadarIcon) = Get_Radar_Icon(terrain.Get_Image_Data(), 0, 1, 3); + IsTheaterShape = false; + + } + } + } +} + + +/*********************************************************************************************** + * TerrainTypeClass::From_Name -- Convert name to terrain type. * + * * + * This routine is used to convert a text name into the matching * + * terrain type number. This is used during scenario initialization. * + * * + * INPUT: name -- The name to convert. * + * * + * OUTPUT: Returns the TerrainType that matches the name specified. If * + * no match was found, then TERRAIN_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +TerrainType TerrainTypeClass::From_Name(char const * name) +{ + TerrainType index; + + if (name != NULL) { + for (index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (stricmp(name, As_Reference(index).IniName) == 0) { + return(index); + } + } + } + return(TERRAIN_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TerrainTypeClass::Display -- Display a generic terrain object. * + * * + * This routine is used to display a generic terrain object. Typical * + * use is during scenario editing. * + * * + * INPUT: x,y -- Pixel coordinates to display object at (centered). * + * * + * window-- The window to display the object within. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Display(int x, int y, WindowNumberType window, HousesType) const +{ + IsTheaterShape = true; + CC_Draw_Shape(Get_Image_Data(), 0, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * * + * Submits all of the valid terrain objects to the scenario editor for possible selection * + * and subsequent placement on the map. All terrain objects, that have a valid shape * + * file available, are added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Prep_For_Add(void) +{ + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * * + * This support routine is used by the scenario editor to add a terrain object to the map. * + * * + * INPUT: cell -- The cell to place the terrain object in. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TerrainClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * * + * This is used to create a terrain object by using the terrain type as a guide. This * + * routine is typically used by the scenario editor in order to place a terrain object * + * onto the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the created terrain object or NULL if one couldn't be * + * created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TerrainTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TerrainClass(Type, -1)); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Occupy_List -- Returns with the occupy list for the terrain object type. * + * * + * This routine will return with the occupy list for the terrain object type. If there is * + * no occupy list for this terrain object type, then a special zero length occupy list * + * pointer is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object's occupy list. A zero length list is * + * returned in the case of no occupy list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +short const * TerrainTypeClass::Occupy_List(bool ) const +{ + if (Occupy != NULL) return(Occupy); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Overlap_List -- Fetches the overlap list for the terrain type. * + * * + * This routine will return with the overlap list for the terrain object type. If there * + * is no overlap list for the terrain object, then a null length list is returned in order * + * to ensure that a non-null pointer is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list for this object type. A special zero * + * length list pointer is returned if there is no overlap list associated with the * + * object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +short const * TerrainTypeClass::Overlap_List(void) const +{ + if (Overlap != NULL) return(Overlap); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +/*********************************************************************************************** + * TerrainTypeClass::As_Reference -- Fetches a reference to the terrain type object specified. * + * * + * Use this routine to convert the terrain type number into a reference to a terrain * + * type class object. * + * * + * INPUT: type -- The terrain type number to convert. * + * * + * OUTPUT: Returns with a reference to the terrain type class object that is referred to by * + * the terrain type number. * + * * + * WARNINGS: Be sure that the terrain type number is valid. Using an invalid value causes * + * undefined behavior. * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +TerrainTypeClass & TerrainTypeClass::As_Reference(TerrainType type) +{ + return(*TerrainTypes.Ptr(type)); +} + + +COORDINATE TerrainTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/CODE/TEAM.CPP b/CODE/TEAM.CPP new file mode 100644 index 0000000..b98a592 --- /dev/null +++ b/CODE/TEAM.CPP @@ -0,0 +1,3076 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEAM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEAM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : August 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamClass::AI -- Process team logic. * + * TeamClass::Add -- Adds specified object to team. * + * TeamClass::Assign_Mission_Target -- Sets teams mission target and clears old target * + * TeamClass::Calc_Center -- Determines average location of team members. * + * TeamClass::Can_Add -- Determines if the specified object can be added to team. * + * TeamClass::Control -- Updates control on a member unit. * + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * TeamClass::Debug_Dump -- Displays debug information about the team. * + * TeamClass::Detach -- Removes specified target from team tracking. * + * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * + * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * + * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * TeamClass::Remove -- Removes the specified object from the team. * + * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * + * TeamClass::TMission_Attack -- Perform the team attack mission command. * + * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * + * TeamClass::TMission_Formation -- Process team formation change command. * + * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of tim* + * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * + * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * + * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * + * TeamClass::TMission_Spy -- Perform the team spy mission. * + * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * + * TeamClass::TeamClass -- Constructor for the team object type. * + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * TeamClass::operator delete -- Deallocates a team object. * + * TeamClass::operator new -- Allocates a team object. * + * TeamClass::~TeamClass -- Team object destructor. * + * _Is_It_Breathing -- Checks to see if unit is an active team member. * + * _Is_It_Playing -- Determines if unit is active and an initiated team member. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "mission.h" + + +/*********************************************************************************************** + * _Is_It_Breathing -- Checks to see if unit is an active team member. * + * * + * A unit could be a team member, but not be active. Such a case would occur when a * + * reinforcement team is inside a transport. It could also occur if a unit is in the * + * process of dying. Call this routine to ensure that the specified unit is a will and * + * able participant in the team. * + * * + * INPUT: object -- Pointer to the unit/infantry/aircraft that is to be checked. * + * * + * OUTPUT: bool; Is the specified unit active and able to be given commands by the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +static inline bool _Is_It_Breathing(FootClass const * object) +{ + /* + ** If the object is not present or appears to be dead, then it + ** certainly isn't an active member of the team. + */ + if (object == NULL || !object->IsActive || object->Strength == 0) return(false); + + /* + ** If the object is in limbo, then it isn't an active team member either. However, if the + ** scenario init flag is on, then it is probably a reinforcement issue or scenario + ** creation situation. In such a case, the members are considered active because they need to + ** be given special orders and treatment. + */ + if (!ScenarioInit && object->IsInLimbo) return(false); + + /* + ** Nothing eliminated this object from being considered an active member of the team (i.e., + ** "breathing"), then return that it is ok. + */ + return(true); +} + + +/*********************************************************************************************** + * _Is_It_Playing -- Determines if unit is active and an initiated team member. * + * * + * Use this routine to determine if the specified unit is an active participant of the * + * team. When a unit is first recruited to the team, it must travel to the team's location * + * before it can become an active player. Call this routine to determine if the specified * + * unit can be considered an active player. * + * * + * INPUT: object -- Pointer to the object that is to be checked to see if it is an * + * active player. * + * * + * OUTPUT: bool; Is the specified unit an active, living, initiated member of the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +static inline bool _Is_It_Playing(FootClass const * object) +{ + /* + ** If the object is not active, then it certainly can be a participating member of the + ** team. + */ + if (!_Is_It_Breathing(object)) return(false); + + /* + ** Only members that have been "Initiated" are considered "playing" participants of the + ** team. This results in the team members that are racing to regroup with the team (i.e., + ** not initiated), will continue to catch up to the team even while the initiated team members + ** carry out their team specific orders. + */ + if (!object->IsInitiated && object->What_Am_I() != RTTI_AIRCRAFT) return(false); + + /* + ** If it reaches this point, then nothing appears to disqualify the specified object from + ** being considered an active playing member of the team. In this case, return that + ** information. + */ + return(true); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TeamClass::Debug_Dump -- Displays debug information about the team. * + * * + * This routine will display information about the team. This is useful for debugging * + * purposes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the debugging information will * + * be displayed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 20);mono->Printf("%8.8s", Class->IniName); + mono->Set_Cursor(10, 20);mono->Printf("%3d", Total); + mono->Set_Cursor(17, 20);mono->Printf("%3d", Quantity[Class->ID]); + if (CurrentMission != -1) { + mono->Set_Cursor(1, 22); + mono->Printf("%-29s", Class->MissionList[CurrentMission].Description(CurrentMission)); + } + mono->Set_Cursor(40, 20);mono->Printf("%-10s", FormationName[Formation]); + mono->Set_Cursor(22, 20);mono->Printf("%08X", Zone); + mono->Set_Cursor(31, 20);mono->Printf("%08X", Target); + + mono->Fill_Attrib(53, 20, 12, 1, IsUnderStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 21, 12, 1, IsFullStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 22, 12, 1, IsHasBeen ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(66, 20, 12, 1, IsMoving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 21, 12, 1, IsForcedActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 22, 12, 1, IsReforming ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * * + * This routine clears out the team object array in preparation for starting a new * + * scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Init(void) +{ + Teams.Free_All(); +} + + +/*********************************************************************************************** + * TeamClass::operator new -- Allocates a team object. * + * * + * This routine will allocate a team object from the team object pool. * + * * + * INPUT: size -- The size of the requested allocation. * + * * + * OUTPUT: Returns with a pointer to the freshly allocated team object. If an allocation * + * could not be made, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * TeamClass::operator new(size_t) +{ + void * ptr = Teams.Allocate(); + if (ptr != NULL) { + ((TeamClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TeamClass::operator delete -- Deallocates a team object. * + * * + * This routine will return a team object to the team object pool. * + * * + * INPUT: ptr -- Pointer to the team object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((TeamClass *)ptr)->IsActive = false; + } + Teams.Free((TeamClass *)ptr); +} + + +/*********************************************************************************************** + * TeamClass::~TeamClass -- Team object destructor. * + * * + * This routine is called when a team object is destroyed. It handles updating the total * + * number count for this team object type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 07/04/1996 JLB : Keeps trigger if trigger still attached to objects. * + *=============================================================================================*/ +TeamClass::~TeamClass(void) +{ + if (GameActive && Class.Is_Valid()) { + while (Member != NULL) { + Remove(Member); + } + Class->Number--; + + /* + ** When the team dies, any trigger associated with it, dies as well. This will only occur + ** if there are no other objects linked to this trigger. Only player reinforcement + ** members that broke off of the team earlier will have this occur. + */ + if (Trigger.Is_Valid()) { + if (Trigger->AttachCount == 0) { + delete (TriggerClass *)Trigger; + } + Trigger = NULL; + } + } +} + + +/*********************************************************************************************** + * TeamClass::TeamClass -- Constructor for the team object type. * + * * + * This routine is called when the team object is created. * + * * + * INPUT: type -- Pointer to the team type to make this team object from. * + * * + * owner -- The owner of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : + AbstractClass(RTTI_TEAM, Teams.ID(this)), + Class((TeamTypeClass *)type), + House(owner), + IsForcedActive(false), + IsHasBeen(false), + IsFullStrength(false), + IsUnderStrength(true), + IsReforming(false), + IsLagging(false), + IsAltered(true), + JustAltered(false), + IsMoving(false), + IsNextMission(true), + IsLeaveMap(false), + Suspended(false), + Trigger(NULL), + Zone(TARGET_NONE), + ClosestMember(TARGET_NONE), + MissionTarget(TARGET_NONE), + Target(TARGET_NONE), + Total(0), + Risk(0), + Formation(FORMATION_NONE), + SuspendTimer(0), + CurrentMission(-1), + TimeOut(0), + Member(0) +{ + assert(Class); + assert(Class->IsActive); + assert(Class->ClassCount > 0); + + if (owner == NULL) { + House = HouseClass::As_Pointer(Class->House); + } + + memset(Quantity, 0, sizeof(Quantity)); + if (Class->Origin != -1) { + Zone = ::As_Target(Scen.Waypoint[Class->Origin]); + } + Class->Number++; + + /* + ** If there is a trigger tightly associated with this team, then + ** create an instance of that trigger and attach it to the team. + */ + if (Class->Trigger.Is_Valid()) { + Trigger = new TriggerClass(Class->Trigger); + } +} + + +/*************************************************************************** + * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Assign_Mission_Target(TARGET new_target) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** First go through and find anyone who is currently targeting + ** the old mission target and clear their Tarcom. + */ + FootClass * unit = Member; + if (MissionTarget != TARGET_NONE) { + while (unit != NULL) { + bool tar = (unit->TarCom == MissionTarget); + bool nav = (unit->NavCom == MissionTarget); + if (tar || nav) { + + /* + ** If the unit was doing something related to the team mission + ** then we kick him into guard mode so that he is easy to change + ** missions for. + */ + unit->Assign_Mission(MISSION_GUARD); + + /* + ** If the unit's tarcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (nav) { + unit->Assign_Destination(TARGET_NONE); + } + + /* + ** If the unit's navcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (tar) { + unit->Assign_Target(TARGET_NONE); + } + } + unit = unit->Member; + } + } + + /* + ** If there is not currently an override on the current mission target + ** then assign both MissionTarget and Target to the new target. If + ** there is an override, allow the team to keep fighting the override but + ** make sure they pick up on the new mission when they are ready. + */ + if (Target == MissionTarget || !Target_Legal(Target)) { + MissionTarget = Target = new_target; + } else { + MissionTarget = new_target; + } +} + + +/*********************************************************************************************** + * TeamClass::AI -- Process team logic. * + * * + * General purpose team logic is handled by this routine. It should be called once per * + * active team per game tick. This routine handles recruitment and assigning orders to * + * member units. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/06/1995 JLB : Choreographed gesture. * + *=============================================================================================*/ +void TeamClass::AI(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + int desired = 0; + int old_under = IsUnderStrength; + int old_full = IsFullStrength; + + /* + ** If the team has been suspended then we need to check if it's time for + ** us to reactivate the team. If not, no team logic will be processed + ** for this team. + */ + if (Suspended) { + if (SuspendTimer != 0) { + return; + } + Suspended = false; + } + + /* + ** If this team senses that its composition has been altered, then it should + ** recalculate the under strength and full strength flags. + */ + if (IsAltered) { + + /* + ** Figure out the total number of objects that this team type requires. + */ + for (int index = 0; index < Class->ClassCount; index++) { + desired += Class->Members[index].Quantity; + } + assert(desired != 0); + + if (Total) { + IsFullStrength = (Total == desired); + if (IsFullStrength) { + IsHasBeen = true; + } + + /* + ** Reinforceable teams will revert (or snap out of) the under strength + ** mode when the members transition the magic 1/3 strength threshold. + */ + if (Class->IsReinforcable) { + if (desired > 2) { + IsUnderStrength = (Total <= desired / 3); + } else { + IsUnderStrength = (Total < desired); + } + } else { + + /* + ** Teams that are not flagged as reinforceable are never considered under + ** strength if the team has already started its main mission. This + ** ensures that once the team has started, it won't dally to pick up + ** new members. + */ + IsUnderStrength = !IsHasBeen; + } + + IsAltered = JustAltered = false; + } else { + IsUnderStrength = true; + IsFullStrength = false; + Zone = TARGET_NONE; + + /* + ** A team that exists on the player's side is automatically destroyed + ** when there are no team members left. This team was created as a + ** result of reinforcement logic and no longer needs to exist when there + ** are no more team members. + */ + if (IsHasBeen || Session.Type != GAME_NORMAL) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + } + + /* + ** If the team has gone from under strength to no longer under + ** strength than the team needs to reform. + */ + if (old_under != IsUnderStrength) { + IsReforming = true; + } + } + + /* + ** If the team is under strength, then flag it to regroup. + */ + if (IsMoving && IsUnderStrength) { + IsMoving = false; + CurrentMission = -1; + if (Total) { + Calc_Center(Zone, ClosestMember); + + /* + ** When a team is badly damaged and needs to regroup it should + ** pick a friendly building to go and regroup at. Its first preference + ** should be somewhere near repair factory. If it cannot find a repair + ** factory then it should pick another structure that is friendly to + ** its side. + */ + CELL dest = As_Cell(Zone); + int max = 0x7FFFFFFF; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == House && b->Class->PrimaryWeapon == NULL) { + CELL cell = Coord_Cell(b->Center_Coord()); + int dist = ::Distance(b->Center_Coord(), As_Coord(Zone)) * (Map.Cell_Threat(cell, House->Class->House) + 1); + + if (*b == STRUCT_REPAIR) { + dist /= 2; + } + if (dist < max) { + cell = Fetch_A_Leader()->Safety_Point(As_Cell(Zone), cell, 2, 4); +// cell = Member->Safety_Point(As_Cell(Zone), cell, 2, 4); + if (cell != -1) { + max = dist; + dest = cell; + } + } + } + } + + // Should calculate a regroup location. + Target = ::As_Target(dest); + Coordinate_Move(); + return; + } else { + Zone = TARGET_NONE; + } + } + + /* + ** Flag this team into action when it gets to full strength. Human owned teams are only + ** used for reinforcement purposes -- always consider them at full strength. + */ + if (!IsMoving && (IsFullStrength || IsForcedActive)) { + IsMoving = true; + IsHasBeen = true; + IsUnderStrength = false; + + /* + ** Infantry can do a gesture when they start their mission. Pick + ** a gesture at random. + */ + FootClass * techno = Member; + DoType doaction = Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2; + while (techno) { + if (_Is_It_Breathing(techno) && techno->What_Am_I() == RTTI_INFANTRY) { + ((InfantryClass *)techno)->Do_Action(doaction); + } + + if (IsReforming || IsForcedActive) { + techno->IsInitiated = true; + } + + techno = techno->Member; + } + CurrentMission = -1; + IsNextMission = true; +// IsForcedActive = false; + } + + /* + ** If the team is moving or if there is no center position for + ** the team, then the center position must be recalculated. + */ + if (IsReforming || IsMoving || Zone == TARGET_NONE || ClosestMember == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Try to recruit members if there is room to do so for this team. + ** Only try to recruit members for a non player controlled team. + */ + if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((!House->IsHuman || !IsHasBeen) && Session.Type == GAME_NORMAL)) { +// if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((/*!House->IsHuman ||*/ !IsHasBeen) && Session.Type == GAME_NORMAL)) { + for (int index = 0; index < Class->ClassCount; index++) { + if (Quantity[index] < Class->Members[index].Quantity) { + Recruit(index); + } + } + } + + /* + ** If there are no members of the team and the team has reached + ** full strength at one time, then delete the team. + */ + if (Member == NULL && IsHasBeen) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + + /* + ** If the mission should be advanced to the next entry, then do so at + ** this time. Various events may cause the mission to advance, but it + ** all boils down to the following change-mission code. + */ + if (IsMoving && !IsReforming && IsNextMission) { + IsNextMission = false; + CurrentMission++; + if (CurrentMission < Class->MissionCount) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + TimeOut = mission->Data.Value * (TICKS_PER_MINUTE/10); + Target = TARGET_NONE; + switch (mission->Mission) { + + case TMISSION_MOVECELL: + Assign_Mission_Target(::As_Target((CELL)(mission->Data.Value))); + break; + + case TMISSION_MOVE: + if ((unsigned)mission->Data.Value < WAYPT_COUNT && Member != NULL) { + FootClass * leader = Fetch_A_Leader(); + CELL movecell = Scen.Waypoint[mission->Data.Value]; + if (!Is_Leaving_Map()) { + if (leader->Can_Enter_Cell(movecell) != MOVE_OK) { + movecell = Map.Nearby_Location(movecell, leader->Techno_Type_Class()->Speed); + } + } + Assign_Mission_Target(::As_Target(movecell)); + Target = ::As_Target(movecell); + } + break; + + case TMISSION_ATT_WAYPT: + case TMISSION_PATROL: + case TMISSION_SPY: + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + break; + + case TMISSION_ATTACKTARCOM: + Assign_Mission_Target(mission->Data.Value); + break; + + case TMISSION_UNLOAD: + default: + Assign_Mission_Target(TARGET_NONE); + break; + } + } else { + delete this; + return; + } + } + + /* + ** Perform mission of the team. This depends on the mission list. + */ + if (Member != NULL && IsMoving && !IsReforming && !IsUnderStrength) { + + /* + ** If the current Target has been dealt with but the mission target + ** has not, then the current target needs to be reset to the mission + ** target. + */ + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** If the current mission is one that times out, then check for + ** this case. If it has timed out then advance to the next + ** mission in the list or disband the team. + */ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; +// FootClass * member = Member; + + switch (mission->Mission) { + case TMISSION_PATROL: + TMission_Patrol(); + break; + + case TMISSION_FORMATION: + TMission_Formation(); + break; + + case TMISSION_ATTACKTARCOM: + case TMISSION_ATTACK: + TMission_Attack(); + break; + + case TMISSION_LOAD: + TMission_Load(); + break; + + case TMISSION_DEPLOY: + TMission_Deploy(); + break; + + case TMISSION_UNLOAD: + TMission_Unload(); + break; + + case TMISSION_MOVE: + case TMISSION_MOVECELL: + Coordinate_Move(); + break; + + /* + ** All members of this team become invulnerable as if by magic. + */ + case TMISSION_INVULNERABLE: + TMission_Invulnerable(); + break; + + case TMISSION_GUARD: + Coordinate_Regroup(); + break; + + case TMISSION_DO: + Coordinate_Do(); + break; + + case TMISSION_SET_GLOBAL: + TMission_Set_Global(); + break; + + case TMISSION_ATT_WAYPT: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } else { + Coordinate_Attack(); + } + break; + + case TMISSION_SPY: + TMission_Spy(); + break; + + case TMISSION_HOUND_DOG: + TMission_Follow(); + break; + + case TMISSION_LOOP: + TMission_Loop(); + break; + } + + /* + ** Check for mission time out condition. If the mission does in fact time out, then + ** flag it so that the team mission list will advance. + */ + switch (mission->Mission) { +// case TMISSION_UNLOAD: + case TMISSION_GUARD: + if (TimeOut == 0) { + IsNextMission = true; + } + break; + } + + } else { + + if (IsMoving) { + IsReforming = !Coordinate_Regroup(); + } else { + Coordinate_Move(); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Add -- Adds specified object to team. * + * * + * Use this routine to add the specified object to the team. The object is checked to make * + * sure that it can be assigned to the team. If it can't, then the object will be left * + * alone and false will be returned. * + * * + * INPUT: obj -- Pointer to the object that is to be assigned to this team. * + * * + * OUTPUT: bool; Was the unit added to the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation flag setup. * + * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * + *=============================================================================================*/ +bool TeamClass::Add(FootClass * obj) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + if (!obj) return(false); + + int typeindex; + if (!Can_Add(obj, typeindex)) return(false); + + /* + ** All is ok to add the object to the team, but if the object is already part of + ** another team, then it must be removed from that team first. + */ + if (obj->Team.Is_Valid()) { + obj->Team->Remove(obj); + } + + /* + ** Actually add the object to the team. + */ + Quantity[typeindex]++; + obj->IsInitiated = (Member == NULL); + obj->Member = Member; + Member = obj; + obj->Team = this; + + /* + ** If a common trigger is designated for this team type, then attach the + ** trigger to this team member. + */ + if (Trigger.Is_Valid()) { + obj->Attach_Trigger(Trigger); + } + + Total++; + Risk += obj->Risk(); + if (Zone == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Return with success, since the object was added to the team. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Can_Add -- Determines if the specified object can be added to team. * + * * + * This routine will examine the team and determine if the specified object can be * + * properly added to this team. This is a security check to filter out those objects that * + * should not be added because of conflicting priorities or other restrictions. * + * * + * INPUT: obj -- Pointer to the candidate object that is being checked for legal * + * adding to this team. * + * * + * typeindex-- The class index number (according to the team type's class array) that * + * the candidate object is classified as. The routine processes much * + * faster if you can provide this information, but if you don't, the * + * routine will figure it out. * + * * + * OUTPUT: bool; Can the specified object be added to this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Can_Add(FootClass * obj, int & typeindex) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Trying to add the team member to itself is an error condition. + */ + if (obj->Team == this) { + return(false); + } + + /* + ** The object must be active, a member of this house. A special dispensation is given to + ** units that are in radio contact. It is presumed that they are very busy and should + ** not be disturbed. + */ + if (!_Is_It_Breathing(obj) || obj->In_Radio_Contact() || obj->House != House) { + return(false); + } + + /* + ** If the object is doing some mission that precludes it from joining + ** a team then don't add it. + */ + if (obj->Mission != MISSION_NONE && !MissionClass::Is_Recruitable_Mission(obj->Mission)) { + return(false); + } + + /* + ** If this object is part of another team, then check to make sure that it + ** is permitted to leave the other team in order to join this one. If not, + ** then no further processing is allowed -- bail. + */ + if (obj->Team.Is_Valid() && (obj->Team->Class->RecruitPriority >= Class->RecruitPriority)) { + return(false); + } + + /* + ** Aircraft that have no ammo for their weapons cannot be recruited into a team. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT && obj->Techno_Type_Class()->PrimaryWeapon != NULL && !obj->Ammo) { + return(false); + } + + /* + ** Search for the exact member index that the candidate object matches. + ** If no match could be found, then adding the object to the team cannot + ** occur. + */ + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + if (typeindex == Class->ClassCount) { + return(false); + } + + /* + ** If the team is already full of this type, then adding the object is not allowed. + ** Return with a failure flag in this case. + */ + if (Quantity[typeindex] >= Class->Members[typeindex].Quantity) { + return(false); + } + + return(true); +} + + + +/*********************************************************************************************** + * TeamClass::Remove -- Removes the specified object from the team. * + * * + * Use this routine to remove an object from a team. Objects removed from the team are * + * then available to be recruited by other teams, or even by the same team at a later time. * + * * + * INPUT: obj -- Pointer to the object that is to be removed from this team. * + * * + * typeindex-- Optional index of where this object type is specified in the type * + * type class. This parameter can be omitted. It only serves to make * + * the removal process faster. * + * * + * OUTPUT: bool; Was the object removed from this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation tracking and team captain selection. * + *=============================================================================================*/ +bool TeamClass::Remove(FootClass * obj, int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Make sure that the object is in fact a member of this team. If not, then it can't + ** be removed. Return success because the end result is the same. + */ + if (this != obj->Team) { + return(true); + } + + /* + ** Detach the common trigger for this team type. Only current and active members of the + ** team have that trigger attached. The exception is for player team members that + ** get removed from a reinforcement team. + */ + if (obj->Trigger == Trigger && !obj->House->IsPlayerControl) { + obj->Attach_Trigger(NULL); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. The + ** team type class will not be set if the appropriate type could not be found + ** for this object. This indicates that the object was illegally added. Continue to + ** process however, since removing this object from the team is a good idea. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + } + + /* + ** Decrement the counter for the team class. There is now one less of this object type. + */ + if ((unsigned)typeindex < Class->ClassCount) { + Quantity[typeindex]--; + } + + /* + ** Actually remove the object from the team. Scan through the team members + ** looking for the one that matches the one specified. If it is found, it + ** is unlinked from the member chain. During this scan, a check is made to + ** ensure that at least one remaining member is still initiated. If not, then + ** a new team captain must be chosen. + */ + bool initiated = false; + FootClass * prev = 0; + FootClass * curr = Member; + bool found = false; + while (curr != NULL && (!found || !initiated)) { + if (curr == obj) { + if (prev != NULL) { + prev->Member = curr->Member; + } else { + Member = curr->Member; + } + FootClass * temp = curr->Member; + curr->Member = 0; + curr->Team = 0; + curr->SuspendedMission = MISSION_NONE; + curr->SuspendedNavCom = TARGET_NONE; + curr->SuspendedTarCom = TARGET_NONE; + curr = temp; + Total--; + found = true; + Risk -= obj->Risk(); + continue; + } + + /* + ** If this (remaining) member is initiated, then keep a record of this. + */ + initiated |= curr->IsInitiated; + + prev = curr; + curr = curr->Member; + } + + /* + ** A unit that breaks off of a team will enter idle mode. + */ + obj->Enter_Idle_Mode(); + + /* + ** If, after removing the team member, there are no initiated members left + ** in the team, then just make the first remaining member of the team the + ** team captain. Mark the center location of the team as invalid so that + ** it will be centered around the captain. + */ + if (!initiated && Member != NULL) { + Member->IsInitiated = true; + Zone = TARGET_NONE; + } + + /* + ** Must record that the team composition has changed. At the next opportunity, + ** the team members will be counted and appropriate AI adjustments made. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * * + * This routine will take the given index ID and scan for available objects of that type * + * to recruit to the team. Recruiting will continue until that object type has either * + * been exhausted or if the team's requirement for that type has been filled. * + * * + * INPUT: typeindex -- The index for the object type to recruit. The index is used to * + * look into the type type's array of object types that make up this * + * team. * + * * + * OUTPUT: Returns with the number of objects added to this team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 04/10/1995 JLB : Scans for units too. * + *=============================================================================================*/ +int TeamClass::Recruit(int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + COORDINATE center = As_Coord(Zone); + + if (Class->Origin != -1) { + center = Cell_Coord(Scen.Waypoint[Class->Origin]); + } + + int added = 0; // Total number added to team. + + /* + ** Quick check to see if recruiting is really allowed for this index or not. + */ + if (Class->Members[typeindex].Quantity > Quantity[typeindex]) { + switch (Class->Members[typeindex].Class->What_Am_I()) { + + /* + ** For infantry objects, sweep through the infantry in the game looking for + ** ones owned by the house that owns the team. When found, try to add. + */ + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + { + InfantryClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + int d = infantry->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(infantry, typeindex)) { + best = infantry; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + { + AircraftClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + int d = aircraft->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(aircraft, typeindex)) { + best = aircraft; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_UNITTYPE: + case RTTI_UNIT: + { + UnitClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + int d = unit->Distance(center); + + if (unit->House == House && unit->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(unit, typeindex)) { + best = unit; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + { + VesselClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + int d = vessel->Distance(center); + + if (vessel->House == House && vessel->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(vessel, typeindex)) { + best = vessel; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + } + } + return(added); +} + + +/*********************************************************************************************** + * TeamClass::Detach -- Removes specified target from team tracking. * + * * + * When a target object is about to be removed from the game (e.g., it was killed), then * + * any team that is looking at that target must abort from that target. * + * * + * INPUT: target -- The target object that is going to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Detach(TARGET target, bool ) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** If the target to detach matches the target of this team, then remove + ** the target from this team's Tar/Nav com and let the chips fall + ** where they may. + */ + if (Target == target) { + Target = TARGET_NONE; + } + if (MissionTarget == target) { + MissionTarget = TARGET_NONE; + } + if (Trigger.Is_Valid() && Trigger->As_Target() == target) { + Trigger = 0; + } +} + + +/*********************************************************************************************** + * TeamClass::Calc_Center -- Determines average location of team members. * + * * + * Use this routine to calculate the "center" location of the team. This is the average * + * position of all members of the team. Using this center value it is possible to tell * + * if a team member is too far away and where to head to in order to group up. * + * * + * INPUT: center -- Average center target location of the team. Only initiated members * + * will be considered. * + * * + * close_member--Location (as target) of the unit that is closest to the team's * + * target. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Calc_Center(TARGET & center, TARGET & close_member) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Presume there is no center. This will be confirmed in the following scanning + ** operation. + */ + close_member = TARGET_NONE; + center = TARGET_NONE; + + FootClass const * team_member = Member; // Working team member pointer. + + /* + ** If there are no members of the team, then there can be no center point of the team. + */ + if (team_member == NULL) return; + + /* + ** If the team is supposed to follow a nearby friendly unit, then the + ** team's "center" will actually be that unit. Otherwise, calculated the + ** average center location for the team. + */ + if (Class->MissionList[CurrentMission].Mission == TMISSION_HOUND_DOG) { + + /* + ** First pick a member of the team. The closest friendly object to that member + ** will be picked. + */ + if (!team_member) return; + + FootClass const * closest = NULL; // Current closest friendly object. + int distance = -1; // Record of last closest distance calc. + + /* + ** Scan through all vehicles. + */ + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + FootClass const * trial_unit = Units.Ptr(unit_index); + + if (_Is_It_Breathing(trial_unit) && trial_unit->House->Is_Ally(House) && trial_unit->Team != this) { + int trial_distance = team_member->Distance(trial_unit); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_unit; + } + } + } + + /* + ** Scan through all infantry. + */ + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + FootClass const * trial_infantry = Infantry.Ptr(infantry_index); + + if (_Is_It_Breathing(trial_infantry) && trial_infantry->House->Is_Ally(House) && trial_infantry->Team != this) { + int trial_distance = team_member->Distance(trial_infantry); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_infantry; + } + } + } + + /* + ** Scan through all vessels. + */ + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + FootClass const * trial_vessel = Vessels.Ptr(vessel_index); + + if (_Is_It_Breathing(trial_vessel) && trial_vessel->House->Is_Ally(House) && trial_vessel->Team != this) { + int trial_distance = team_member->Distance(trial_vessel); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_vessel; + } + } + } + + /* + ** Set the center location as actually the friendly object that is closest. If there + ** is no friendly object, then don't set any center location at all. + */ + if (closest) { + center = closest->As_Target(); + close_member = Member->As_Target(); + } + + } else { + + long x = 0; // Accumulated X coordinate. + long y = 0; // Accumulated Y coordinate. + int dist = 0; // Closest recorded distance to team target. + int quantity = 0; // Number of team members counted. + FootClass const * closest = 0; // Closest member to target. + + /* + ** Scan through all team members and accumulate the X and Y component of their + ** location. Only team members that are active will be considered. Also keep + ** track of the team member that is closest to the team's target. + */ + while (team_member != NULL) { + if (_Is_It_Playing(team_member)) { + + /* + ** Accumulate X and Y components of qualified team members. + */ + x += Coord_X(team_member->Coord); + y += Coord_Y(team_member->Coord); + quantity++; + + /* + ** Keep a record of the team member that is nearest to the team's + ** target. + */ + int try_dist = team_member->Distance(Target); + if (!dist || try_dist < dist) { + dist = try_dist; + closest = team_member; + } + } + + team_member = team_member->Member; + } + + /* + ** If there were any qualifying members, then the team's center point can be + ** determined. + */ + if (quantity) { + x /= quantity; + y /= quantity; + COORDINATE coord = XY_Coord((int)x, (int)y); + center = ::As_Target(coord); + + + /* + ** If the center location is impassable, then just pick the location of + ** one of the team members. + */ + if (!closest->Can_Enter_Cell(As_Cell(center))) { +// if (Class->Origin != -1) { +// center = ::As_Target(Scen.Waypoint[Class->Origin]); +// } else { + center = ::As_Target(Coord_Cell(closest->Center_Coord())); +// } + } + } + + /* + ** Record the position of the closest member to the team's target and + ** that will be used as the regroup point. + */ + if (closest != NULL) { + close_member = ::As_Target(Coord_Cell(closest->Center_Coord())); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * * + * This routine is used when a team member takes damage. Usually the team will react in * + * some fashion to the attack. This reaction can range from running away to assigning this * + * new target as the team's target. * + * * + * INPUT: obj -- The team member that was damaged. * + * * + * result -- The severity of the damage taken. * + * * + * source -- The perpetrator of the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if ((result != RESULT_NONE) && (!Class->IsSuicide)) { + if (!IsMoving) { + + // TCTCTC + // Should run to a better hiding place or disband into a group of hunting units. + + } else { + /* + ** Respond to the attack, but not if we're an aircraft or a LST. + */ + if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT && (Member->What_Am_I() != RTTI_VESSEL || *(VesselClass *)((FootClass *)Member) != VESSEL_TRANSPORT)) { + if (Target != source->As_Target()) { + + /* + ** Don't change target if the team's target is one that can fire as well. There is + ** no point in endlessly shuffling between targets that have firepower. + */ + if (Target_Legal(Target)) { + TechnoClass * techno = As_Techno(Target); + + if (techno && ((TechnoTypeClass const &)techno->Class_Of()).PrimaryWeapon != NULL) { + if (techno->In_Range(As_Coord(Zone), 0)) { + return; + } + } + } + + /* + ** Don't change target to aggressor if the aggressor cannot normally be attacked. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT || (source->What_Am_I() == RTTI_VESSEL && (Member->What_Am_I() == RTTI_UNIT || Member->What_Am_I() == RTTI_INFANTRY))) { + return; + } + + Target = source->As_Target(); + } + } + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * * + * This function is called when the team knows what it should attack. This routine will * + * give the necessary orders to the members of the team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Attack(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** Check if they're attacking a cell. If the contents of the cell are + ** a bridge or a building/unit/techno, then it's a valid target. Otherwise, + ** the target is invalid. This only applies to non-aircraft teams. An aircraft team + ** can "attack" an empty cell and this is perfectly ok (paratrooper drop and parabombs + ** are prime examples). + */ + if (Is_Target_Cell(Target) && Member != NULL && Fetch_A_Leader()->What_Am_I() != RTTI_AIRCRAFT) { + CellClass *cellptr = &Map[As_Cell(Target)]; + TemplateType tt = cellptr->TType; + if (cellptr->Cell_Object()) { + Target = cellptr->Cell_Object()->As_Target(); + } else { + if (tt != TEMPLATE_BRIDGE1 && tt != TEMPLATE_BRIDGE2 && + tt != TEMPLATE_BRIDGE1H && tt != TEMPLATE_BRIDGE2H && + tt != TEMPLATE_BRIDGE_1A && tt != TEMPLATE_BRIDGE_1B && + tt != TEMPLATE_BRIDGE_2A && tt != TEMPLATE_BRIDGE_2B && + tt != TEMPLATE_BRIDGE_3A && tt != TEMPLATE_BRIDGE_3B ) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + FootClass *unit = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if(unit->What_Am_I() != RTTI_UNIT || + *(UnitClass *)unit != UNIT_CHRONOTANK || + mission->Mission != TMISSION_SPY) +#endif + Target = 0; // invalidize the target so it'll go to next mission. + } + } + } + + if (!Target_Legal(Target)) { + IsNextMission = true; + + } else { + + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + FootClass * unit = Member; + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)unit == INFANTRY_SPY) { + unit->Assign_Mission(MISSION_CAPTURE); + unit->Assign_Target(Target); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_CHRONOTANK) { + UnitClass *tank = (UnitClass *)unit; + tank->Teleport_To(::As_Cell(Target)); + tank->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONOTANK1, unit->Coord); + tank->Assign_Target(TARGET_NONE); + tank->Assign_Mission(MISSION_GUARD); + } else { +#endif + if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { + unit->Transmit_Message(RADIO_OVER_OUT); + unit->Assign_Mission(MISSION_ATTACK); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + if (unit->TarCom != Target) { + unit->Assign_Target(Target); + } + } + + unit = unit->Member; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * * + * This routine is called when the team must delay at its current location. Team members * + * are grouped together by this function. It is called when the team needs to sit and * + * wait. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the team completely regrouped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Regroup(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool retval = true; + + /* + ** Regroup default logic. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->Distance(Zone) > Rule.StrayDistance && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { + if (!Target_Legal(unit->NavCom)) { +// TCTCTC +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, Zone) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + + retval = false; + if (!unit->IsFormationMove) { + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + } else { + retval = true; // formations are always considered regrouped. + } + } + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (unit->Mission != MISSION_GUARD_AREA) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } + return(retval); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * + * * + * This will assign the specified mission to the team. If there are team members that are * + * too far away from the center of the team, then they will be told to move to the team's * + * location. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This only works if the special mission the team members are to perform does not * + * require extra parameters. The ATTACK and MOVE missions are particularly bad. * + * * + * HISTORY: * + * 05/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Do(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + MissionType do_mission = Class->MissionList[CurrentMission].Data.Mission; + + /* + ** For each unit either head it back to the team center or give it the main + ** team mission order as appropriate. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Distance(Zone) > Rule.StrayDistance * 2) { + + /* + ** Only if the unit isn't already heading to regroup with the team, will it + ** be given orders to do so. + */ + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Mission != do_mission) { + unit->ArchiveTarget = TARGET_NONE; + unit->Assign_Mission(do_mission); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * * + * This routine is called when the team must move to a new location. Movement and grouping * + * commands associated with this task are initiated here. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Move(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + bool found = false; + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (Target_Legal(Target)) { + + if (!Lagging_Units()) { + + while (unit != NULL) { + + /* + ** Tell the unit, if necessary, that it should regroup + ** with the main team location. If the unit is regrouping, then + ** the team should continue NOT qualify as fully reaching the desired + ** location. + */ + if (Coordinate_Conscript(unit)) { + finished = false; + } + + if (unit->Mission == MISSION_UNLOAD || unit->MissionQueue == MISSION_UNLOAD) { + finished = false; + } + + if (_Is_It_Playing(unit) && unit->Mission != MISSION_UNLOAD && unit->MissionQueue != MISSION_UNLOAD) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + if (unit->What_Am_I() == RTTI_INFANTRY && ((InfantryClass const *)unit)->Class->IsDog) { + if (Target_Legal(unit->TarCom)) stray = unit->Techno_Type_Class()->ThreatRange; + if (Target_Legal(unit->TarCom) && unit->Distance(unit->TarCom) > stray) { + unit->Assign_Target(TARGET_NONE); + } + } + found = true; + + int dist = unit->Distance(Target); + if (unit->IsFormationMove) { + if (::As_Target(Coord_Cell(unit->Coord)) != unit->NavCom) { + dist = Rule.StrayDistance + 1; // formation moves must be exact. + } + } + + if (dist > stray || + (unit->What_Am_I() == RTTI_AIRCRAFT && +// (unit->In_Which_Layer() == LAYER_TOP && + ((AircraftClass *)unit)->Height > 0 && + Coord_Cell(unit->Center_Coord()) != As_Cell(Target) && + !((AircraftClass *)unit)->Class->IsFixedWing && + Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { + + bool wasform = false; + + + if (unit->Mission != MISSION_MOVE) { + unit->Assign_Mission(MISSION_MOVE); + } + + if (unit->NavCom != Target) { + + /* + ** Check if this destination should be adjusted for + ** a formation move + */ + if (Is_Target_Cell(Target) && unit->IsFormationMove) { + CELL newcell = unit->Adjust_Dest(As_Cell(Target)); + if (Coord_Cell(unit->Coord) != newcell) { + unit->Assign_Destination(::As_Target(newcell)); + } else { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + wasform = true; + } + } else { + unit->Assign_Destination(Target); + } + } + + if (!wasform) { + finished = false; + } + + } else { + if (unit->Mission == MISSION_MOVE && (!Target_Legal(unit->NavCom) || Distance(unit->NavCom) < CELL_LEPTON_W)) { + unit->Assign_Destination(TARGET_NONE); + unit->Enter_Idle_Mode(); + } + } + + /* + ** If any member still has a valid NavCom then consider this + ** movement mission to still be in progress. This will ensure + ** that the members come to a complete stop before the next + ** mission commences. Without this, the team will prematurely + ** start on the next mission even when all members aren't yet + ** in their proper spot. + */ + if (Target_Legal(unit->NavCom)) { + finished = false; + } + } + + unit = unit->Member; + } + } else { + finished = false; + } + } + + /* + ** If there are no initiated members to this team, then it certainly + ** could not have managed to move to the target destination. + */ + if (!found) { + finished = false; + } + + /* + ** If all the team members are close enough to the desired destination, then + ** move to the next mission. + */ + if (finished && IsMoving) { + IsNextMission = true; + } +} + + +/*********************************************************************************************** + * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * + * * + * This routine will examine the team and find any lagging units. The units are then * + * ordered to catch up to the team member that is closest to the team's destination. This * + * routine will not do anything unless lagging members are suspected. This fact is * + * indicated by setting the IsLagging flag. The flag is set by some outside agent. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to set IsLagging for the team if a lagging member is suspected. * + * * + * HISTORY: * + * 08/01/1995 PWG : Created. * + * 04/11/1996 JLB : Modified. * + *=============================================================================================*/ +bool TeamClass::Lagging_Units(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool lag = false; + + //BG: HACK - if it's in a formation move, then disable the check for + // VG added NULL check laggers, 'cause they're all moving simultaneously. + if (unit != NULL && unit->IsFormationMove) IsLagging = false; + + /* + ** If the IsLagging bit is not set, then obviously there are no lagging + ** units. + */ + if (!IsLagging) return(false); + + /* + ** Scan through all of the units, searching for units who are having + ** trouble keeping up with the pack. + */ + while (unit != NULL) { + + if (_Is_It_Playing(unit)) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + + /* + ** If we find a unit who has fallen too far away from the center of + ** the pack, then we need to order that unit to catch up with the + ** first unit. + */ + if (unit->Distance(ClosestMember) > stray) { +// TCTCTC + if (!Target_Legal(unit->NavCom)) { +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, ClosestMember) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(ClosestMember); + } + lag = true; + + } else { + /* + ** We need to order all of the other units to hold their + ** position until all lagging units catch up. + */ + if (unit->Mission != MISSION_GUARD) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + } + unit = unit->Member; + } + + /* + ** Once we have handled the loop we know whether there are any lagging + ** units or not. + */ + IsLagging = lag; + return(lag); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * + * * + * This routine tells all transport vehicles to unload passengers now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Unload(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + /* + ** Only assign the mission if the unit is carrying a passenger, OR + ** if the unit is a minelayer, with mines in it, and the cell it's + ** on doesn't have a building (read: mine) in it already. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* Also, allow unload if it's a MAD Tank. */ + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MAD )) { +#else + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) ) { +#endif + if (unit->Is_Something_Attached()) { + /* + ** Passenger-carrying vehicles will always return false until + ** they've unloaded all passengers. + */ + finished = false; + } + + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (Map[unit->Center_Coord()].Cell_Building() == NULL && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + + } else { + + /* + ** A loaner transport should vacate the map when all transported objects + ** have been offloaded. + */ + if (unit->IsALoaner) { + Remove(unit); + unit->Assign_Mission(MISSION_RETREAT); + unit->Commence(); + } + } + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * + * * + * This routine tells all non-transport units in the team to climb onto the transport in the* + * team. Note the transport must be a member of this team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1996 BWG : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Load(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + FootClass * trans = 0; + + /* + ** First locate the transport in the team, if there is one. There should + ** only be one transport in the team. + */ + while(unit != NULL && trans == NULL) { + if (unit->Techno_Type_Class()->Max_Passengers() > 0) { + trans = unit; + break; + } + unit = unit->Member; + } + + /* + ** In the case of no transport available, then consider the mission complete + ** since it can never complete otherwise. + */ + if (trans == NULL) { + IsNextMission = true; + return(1); + } + + /* + ** If the transport is already in radio contact, then this means that + ** it is in the process of loading. During this time, don't bother to assign + ** the enter mission to the other team members. + */ + if (trans->In_Radio_Contact()) { + return(1); + } + + /* + ** Find a member to assign the entry logic for. + */ + bool finished = true; + unit = Member; // re-point at the first member of the team again. + while (unit != NULL && Total > 1) { + Coordinate_Conscript(unit); + + /* + ** Only assign the mission if the unit is not the transport. + */ + if (_Is_It_Playing(unit) && unit != trans) { + if (unit->Mission != MISSION_ENTER) { + unit->Assign_Mission(MISSION_ENTER); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(trans->As_Target()); + finished = false; + break; + } + finished = false; + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * * + * This routine will give the movement orders to the conscript so that it will group * + * with the other members of the team. * + * * + * INPUT: unit -- Pointer to the conscript unit. * + * * + * OUTPUT: bool; Is the unit still scurrying to reach the team's current location? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Conscript(FootClass * unit) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (_Is_It_Breathing(unit) && !unit->IsInitiated) { + if (unit->Distance(Zone) > Rule.StrayDistance) { + if (!Target_Legal(unit->NavCom)) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Target(TARGET_NONE); + unit->IsFormationMove = false; + unit->Assign_Destination(Zone); + } + return(true); + + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered initiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + } + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Is_A_Member(void const * who) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + while (unit != NULL) { + if (unit == who) { + return(true); + } + unit = unit->Member; + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * * + * INPUT: int priority - determines what is considered low priority. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/19/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Suspend_Teams(int priority, HouseClass const * house) +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + /* + ** If a team is below the "survival priority level", then it gets + ** destroyed. The team members are then free to be reassigned. + */ + if (team != NULL && team->House == house && team->Class->RecruitPriority < priority) { + FootClass * unit = team->Member; + while (team->Member) { + team->Remove(team->Member); + } + team->IsAltered = team->JustAltered = true; + team->SuspendTimer = Rule.SuspendDelay * TICKS_PER_MINUTE; + team->Suspended = true; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * + * * + * This routine is used to see if the team is leaving the map. A team that is leaving the * + * map gives implicit permission for its members to leave the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this team trying to leave the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Is_Leaving_Map(void) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (IsMoving && CurrentMission >= 0) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + if (mission->Mission == TMISSION_MOVE && !Map.In_Radar(Scen.Waypoint[mission->Data.Value])) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * + * * + * This will examine all team members and only if all of them have entered the map, will * + * it return true. This routine is used to recognize the case of a team that has been * + * generated off map and one that has already entered game play. This knowledge can lead * + * to more intelligent behavior regarding team and member disposition. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Have all members of this team entered the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Has_Entered_Map(void) const +{ + bool ok = true; + FootClass * foot = Member; + while (foot != NULL) { + if (!foot->IsLocked) { + ok = false; + break; + } + foot = (FootClass *)(ObjectClass *)(foot->Next); + } + return(ok); +} + + +/*********************************************************************************************** + * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * + * * + * This routine is used when one of the team members cannot get within range of the team's * + * target. In such a case, the team must be assigned a new target and all members of that * + * team must recognize that a restricted target scan is required. This is done by clearing * + * out the team's target so that it will be forced to search for a new one. Also, since the * + * members are flagged for short scanning, whichever team member is picked to scan for a * + * target will scan for one that is within range. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The team will reassign its target as a result of this routine. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Scan_Limit(void) +{ + Assign_Mission_Target(TARGET_NONE); + FootClass * foot = Member; + while (foot != NULL) { + foot->Assign_Target(TARGET_NONE); + foot->IsScanLimited = true; + foot = foot->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::TMission_Formation -- Process team formation change command. * + * * + * This routine will change the team's formation to that specified in the team command * + * parameter. It is presumed that the team will have further movement orders so that the * + * formation can serve some purpose. Merely changing the formation doesn't alter the * + * member's location. The team must be given a movement order before team member * + * repositioning will occur. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to delay before further team actions should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Formation(void) +{ + FootClass * member = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Formation = mission->Data.Formation; + int group = ID + 10; + int xdir = 0; + int ydir = 0; + bool evenodd = 1; + + /* + ** Assign appropriate formation offsets for each of the members + ** of this team. + */ + switch (Formation) { + case FORMATION_NONE: + while (member != NULL) { + member->Group = 0xFF; + member->XFormOffset = 0x80000000; + member->YFormOffset = 0x80000000; + member->IsFormationMove = false; + member = member->Member; + } + break; + case FORMATION_TIGHT: + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + } + break; + case FORMATION_LOOSE: + break; + case FORMATION_WEDGE_N: + ydir = -(Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir += 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_E: + xdir = (Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_S: + ydir = (Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_W: + xdir = -(Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir += 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_LINE_NS: + ydir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = ydir; + member->IsFormationMove = true; + member = member->Member; + ydir += 2; + } + break; + case FORMATION_LINE_EW: + xdir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + xdir += 2; + } + break; + } + + /* + ** Now calculate the group's movement type and speed + */ + if (Formation != FORMATION_NONE) { + TeamSpeed[group] = SPEED_WHEEL; + TeamMaxSpeed[group] = MPH_LIGHT_SPEED; + member = Member; + while (member != NULL) { + RTTIType mytype = member->What_Am_I(); + SpeedType memspeed; + MPHType memmax; + bool speedcheck = false; + + if (mytype == RTTI_INFANTRY) { + memspeed = SPEED_FOOT; + memmax = ((InfantryClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + if (mytype == RTTI_UNIT) { + memspeed = ((UnitClass *)member)->Class->Speed; + memmax = ((UnitClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (mytype == RTTI_VESSEL) { + memspeed = ((VesselClass *)member)->Class->Speed; + memmax = ((VesselClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (speedcheck) { + if (memmax < TeamMaxSpeed[group]) { + TeamMaxSpeed[group] = memmax; + TeamSpeed[group] = memspeed; + } + } + member = member->Member; + } + + /* + ** Now that it's all calculated, assign the movement type and + ** speed to every member of the team. + */ + member = Member; + while (member != NULL) { + member->FormationSpeed = TeamSpeed[group]; + member->FormationMaxSpeed = TeamMaxSpeed[group]; + if (member->What_Am_I() == RTTI_INFANTRY) { + member->FormationSpeed = SPEED_FOOT; + member->FormationMaxSpeed = MPH_SLOW_ISH; + } + member = member->Member; + } + } + + // Advance past the formation-setting command. + IsNextMission = true; + + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Attack -- Perform the team attack mission command. * + * * + * This will tell the team to attack the quarry specified in the team command. If the team * + * already has a target, this it is presumed that this target take precidence and it won't * + * be changed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Attack(void) +{ + if (!Target_Legal(MissionTarget) && Member != NULL) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + /* + ** Pick a team leader that has a weapon. Only in the case of no + ** team members having any weapons, will a member without a weapon + ** be chosen. + */ + FootClass const * candidate = Fetch_A_Leader(); + + /* + ** Have the team leader pick what the next team target will be. + */ + switch (mission->Data.Quarry) { + case QUARRY_ANYTHING: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_BUILDINGS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BUILDINGS)); + break; + + case QUARRY_HARVESTERS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_TIBERIUM)); + break; + + case QUARRY_INFANTRY: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_INFANTRY)); + break; + + case QUARRY_VEHICLES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_VEHICLES)); + break; + + case QUARRY_FACTORIES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FACTORIES)); + break; + + case QUARRY_DEFENSE: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BASE_DEFENSE)); + break; + + case QUARRY_THREAT: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_POWER: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_POWER)); + break; + + case QUARRY_FAKES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FAKES)); + break; + + default: + break; + } + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Spy -- Perform the team spy mission. * + * * + * This will give the team a spy mission to the location specified. It is presumed that * + * the location of the team mission actually resides under the building to be spied. If * + * no building exists at the location, then the spy operation is presumed to be a mere * + * move operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Spy(void) +{ + if (Is_Target_Cell(MissionTarget)) + { + CELL cell = ::As_Cell(MissionTarget); + CellClass * cellptr = &Map[cell]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + ObjectClass * bldg = cellptr->Cell_Building(); +#else + ObjectClass * bldg = cellptr->Cell_Object(); +#endif + if (bldg != NULL) + { + Assign_Mission_Target(bldg->As_Target()); + Coordinate_Attack(); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else + { + FootClass *member = Member; + if(member->What_Am_I() == RTTI_UNIT && *(UnitClass *)member == UNIT_CHRONOTANK) + { + bool finished = true; + while (member) + { + if ( !((UnitClass *)member)->MoebiusCountDown) finished = false; + member = member->Member; + } + + if (!finished) + { + Coordinate_Attack(); + } + else + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + } + } +#endif + } + else + { + if (!Target_Legal(MissionTarget)) + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + else + { + Coordinate_Attack(); + } + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * + * * + * This will cause the team members to search out and follow the nearest friendly object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Follow(void) +{ + Calc_Center(Zone, ClosestMember); + Target = Zone; + Coordinate_Move(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * + * * + * This is equivalent to a jump or goto command. It will alter the team command processing * + * such that it will continue processing at the command number specified. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Loop(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + CurrentMission = mission->Data.Value-1; + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of time * + * * + * This is a team mission that simulates the Iron Curtain device. It will make all team * + * members invlunerable for a temporary period of time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Invulnerable(void) +{ + FootClass * foot = Member; + while (foot != NULL) { + foot->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; + foot->Mark(MARK_CHANGE); + foot = foot->Member; + } + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * + * * + * This routine is used by the team to set a global variable but otherwise perform no * + * visible effect on the team. By using this routine, sophisticated trigger dependencies * + * can be implemented. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Set_Global(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Scen.Set_Global_To(mission->Data.Value, true); + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * + * * + * A patrolling team will move to the designated waypoint, but along the way it will * + * periodically scan for nearby enemies. If an enemy is found, the patrol mission turns * + * into an attack mission until the target is destroyed -- after which it resumes its * + * patrol duties. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next call to this routine is needed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Patrol(void) +{ + /* + ** Reassign the movement destination if the target has been prematurely + ** cleared (probably because the object has been destroyed). + */ + if (!Target_Legal(Target)) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + } + + /* + ** Every so often, scan for a nearby enemy. + */ + if (Frame % (Rule.PatrolTime * TICKS_PER_MINUTE) == 0) { + FootClass * leader = Fetch_A_Leader(); + if (leader != NULL) { + TARGET target = leader->Greatest_Threat(THREAT_NORMAL|THREAT_RANGE); + + if (Target_Legal(target)) { + Assign_Mission_Target(target); + } else { + Assign_Mission_Target(TARGET_NONE); + } + } + } + + /* + ** If the mission target looks like it should be attacked, then do so, otherwise + ** treat it as a movement destination. + */ + if (Is_Target_Object(Target)) { + Coordinate_Attack(); + } else { + Coordinate_Move(); + } + return(1); +} + + +int TeamClass::TMission_Deploy(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MCV) { + if (unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo != 0) { + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (!Map[unit->Center_Coord()].Cell_Building() && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * + * * + * This will scan through the team members looking for one that is suitable as a leader * + * type. A team can sometimes contain limboed or unarmed members. These members are not * + * suitable for leadership roles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suitable leader type unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1996 JLB : Created. * + *=============================================================================================*/ +FootClass * TeamClass::Fetch_A_Leader(void) const +{ + FootClass * leader = Member; + + /* + ** Scan through the team members trying to find one that is an active member and + ** is equipped with a weapon. + */ + while (leader != NULL) { + if (_Is_It_Playing(leader) && leader->Is_Weapon_Equipped()) break; + leader = leader->Member; + } + + /* + ** If no suitable leader was found, then just return with the first conveniently + ** accessable team member. This presumes that some member is better than no member + ** at all. + */ + if (leader == NULL) { + leader = Member; + } + + return(leader); +} diff --git a/CODE/TEAM.H b/CODE/TEAM.H new file mode 100644 index 0000000..b881038 --- /dev/null +++ b/CODE/TEAM.H @@ -0,0 +1,269 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEAM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEAM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : December 11, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAM_H +#define TEAM_H + + +#include "wwfile.h" +#include "teamtype.h" +#include "abstract.h" + +/* +** Units are only allowed to stray a certain distance away from their +** team. When they exceed this distance, some sort of fixup must be +** done. +*/ +#define STRAY_DISTANCE 2 + +class TeamClass : public AbstractClass +{ + public: + /* + ** This specifies the type of team this is. + */ + CCPtr Class; + + /* + ** This specifies the owner of this team. + */ + CCPtr House; + + /* + ** This flag forces the team into active state regardless of whether it + ** is understrength or not. + */ + unsigned IsForcedActive:1; + + /* + ** This flag is set to true when the team initiates into active mode. The + ** flag is never cleared. By examining this flag, it is possible to determine + ** if the team has ever launched into active mode. + */ + unsigned IsHasBeen:1; + + /* + ** If the team is full strength, then this flag is true. A full strength + ** team will not try to recruit members. + */ + unsigned IsFullStrength:1; + + /* + ** A team that is below half strength has this flag true. It means that the + ** the team should hide back at the owner's base and try to recruit + ** members. + */ + unsigned IsUnderStrength:1; + + /* + ** If a team is not understrength but is not yet full strength, then + ** the team is regrouping. If this flag is set and the team becomes + ** full strength, the all members of the team will become initiated + ** and this flag will be reset. + */ + unsigned IsReforming:1; + + /* + ** This bit should be set if a team is determined to have lagging + ** units in its formation. + */ + unsigned IsLagging:1; + + /* + ** If a team member was removed or added, then this flag will be set to true. The + ** team system uses this flag to tell whether it should recalculate the team + ** under strength or full strength flags. This process does not need to occur + ** EVERY time a unit added or deleted from a team, just every so often if the + ** team has been changed. + */ + unsigned IsAltered:1; + unsigned JustAltered:1; + + /* + ** If the team is working on it's primary mission (it is past the build up stage) + ** then this flag will be true. The transition between "moving" and "stationary" + ** stages usually requires some action on the team's part. + */ + unsigned IsMoving:1; + + /* + ** When the team determines that the next mission should be advanced to, it will + ** set this flag to true. Mission advance will either change the behavior of the + ** team or cause it to disband. + */ + unsigned IsNextMission:1; + + /* + ** If at least one member of this team successfully left the map, then this + ** flag will be set. At the time the team is terminated, if this flag is true, then + ** if there are any triggers that depend upon this team leaving, they will be + ** sprung. + */ + unsigned IsLeaveMap:1; + + /* + ** Records whether the team is suspended from production. + */ + unsigned Suspended:1; + + /* + ** A team will have a center point. This is the point used to determine if + ** any member of the team is "too far" from the team and must return. This + ** center point is usually calculated as the average position of all the + ** team members. + */ + TARGET Zone; + + /* + ** This is the target value of the team member that is closest to the + ** destination of the team. The implied location serves as the + ** regroup point for the unit as it is moving. + */ + TARGET ClosestMember; + + /* + ** This is the target of the team. Typically, it is a unit or structure, but + ** for the case of teams with a movement mission, it might represent a + ** destination cell. + */ + TARGET MissionTarget; + TARGET Target; + + /* + ** This is the total number of members in this team. + */ + int Total; + + /* + ** This is the teams combined risk value + */ + int Risk; + + /* + ** If this team is assigned a formation, then the formation type + ** will be stored here. If the formation type is FORMATION_NONE, then + ** the team is a loose grouping -- just like C&C. + */ + FormationType Formation; + + /* + ** This is the amount of time the team is suspended for. + */ + CDTimerClass SuspendTimer; + + /* + ** If there is a trigger that should be attached to every member of this team, then + ** it is pointed to by this. + */ + CCPtr Trigger; + + //------------------------------------------------------------ + TeamClass(TeamTypeClass const * team=0, HouseClass * owner=0); + TeamClass(NoInitClass const & x) : AbstractClass(x), Class(x), House(x), SuspendTimer(x), Trigger(x), TimeOut(x), Member(x) {}; + virtual ~TeamClass(void); + static void operator delete(void *ptr); + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void Init(void); + static void Suspend_Teams(int priority, HouseClass const * house); + void Debug_Dump(MonoClass * mono) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Is_Empty(void) const {return(Member == (void*)NULL);} + bool Has_Entered_Map(void) const; + void Force_Active(void) {IsForcedActive = true;IsUnderStrength=false;}; + bool Remove(FootClass *, int typeindex=-1); + void Detach(TARGET target, bool all); + void AI(void); + void Took_Damage(FootClass * obj, ResultType result, TechnoClass * source); + bool Add(FootClass *); + bool Can_Add(FootClass * obj, int & typeindex) const; + void Assign_Mission_Target(TARGET new_target); + bool Is_Leaving_Map(void) const; + void Scan_Limit(void); + + private: + + /* + ** The current mission index into the mission list is recorded here. + */ + int CurrentMission; + + /* + ** Some missions will time out. This is the timer that keeps track of the + ** time to transition between missions. + */ + CDTimerClass TimeOut; + + int TMission_Formation(void); + int TMission_Attack(void); + int TMission_Spy(void); + int TMission_Follow(void); + int TMission_Loop(void); + int TMission_Load(void); + int TMission_Unload(void); + int TMission_Invulnerable(void); + int TMission_Set_Global(void); + int TMission_Patrol(void); + int TMission_Deploy(void); + bool Coordinate_Regroup(void); + void Coordinate_Attack(void); + void Coordinate_Move(void); + bool Coordinate_Conscript(FootClass * unit); + void Coordinate_Do(void); + void Calc_Center(TARGET ¢er, TARGET &obj_center) const; + int Recruit(int typeindex); + bool Is_A_Member(void const * who) const; + bool Lagging_Units(void); + FootClass * Fetch_A_Leader(void) const; + + /* + ** Points to the first member in the list of members for this team. + */ + SmartPtr Member; + + unsigned char Quantity[TeamTypeClass::MAX_TEAM_CLASSCOUNT]; +}; + +#endif diff --git a/CODE/TEAMTYPE.CPP b/CODE/TEAMTYPE.CPP new file mode 100644 index 0000000..92b5d80 --- /dev/null +++ b/CODE/TEAMTYPE.CPP @@ -0,0 +1,1879 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEAMTYPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEAMTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/28/96 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamMissionClass::Description -- Compose a text description of team mi * + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * TeamMission_Needs -- Determines what extra data is needed by team miss * + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * TeamTypeClass::Description -- Builds a description of the team. * + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * TeamTypeClass::Edit -- Edit the team type. * + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * TeamTypeClass::Init -- pre-scenario initialization * + * TeamTypeClass::Member_Description -- Builds a member description string * + * TeamTypeClass::Mission_From_Name -- returns mission for given name * + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * TeamTypeClass::Read_INI -- reads INI data * + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * TeamTypeClass::TeamTypeClass -- class constructor * + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * TeamTypeClass::operator delete -- 'delete' operator * + * TeamTypeClass::operator new -- 'new' operator * + * TeamTypeClass::~TeamTypeClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +TeamMissionClass TeamMissions[TMISSION_COUNT] = { + {TMISSION_ATTACK}, + {TMISSION_ATT_WAYPT}, + {TMISSION_FORMATION}, + {TMISSION_MOVE}, + {TMISSION_MOVECELL}, + {TMISSION_GUARD}, + {TMISSION_LOOP}, + {TMISSION_ATTACKTARCOM}, + {TMISSION_UNLOAD}, + {TMISSION_DEPLOY}, + {TMISSION_HOUND_DOG}, + {TMISSION_DO}, + {TMISSION_SET_GLOBAL}, + {TMISSION_LOAD}, + {TMISSION_SPY}, + {TMISSION_PATROL}, +}; + + + +int atoh(char * str); + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * * + * This is a helper routine that is used when this team type is stored into a list box * + * and must be displayed. It will display the team type as a single line of text. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/* +********************************** Globals ********************************** +*/ +char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { + "Attack...", + "Attack Waypoint...", + "Change Formation to...", + "Move to waypoint...", + "Move to Cell...", + "Guard area (1/10th min)...", + "Jump to line #...", + "Attack Tarcom", + "Unload", + "Deploy", + "Follow friendlies", + "Do this...", + "Set global...", + "Invulnerable", + "Load onto Transport", + "Spy on bldg @ waypt...", + "Patrol to waypoint..." +}; + + +/*************************************************************************** + * TeamTypeClass::TeamTypeClass -- class constructor * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 11/22/1995 JLB : Uses initializer constructor method. * + *=========================================================================*/ +TeamTypeClass::TeamTypeClass(void) : + AbstractTypeClass(RTTI_TEAMTYPE, TeamTypes.ID(this), TXT_NONE, ""), + IsRoundAbout(false), + IsSuicide(false), + IsAutocreate(false), + IsPrebuilt(true), + IsReinforcable(true), + IsTransient(false), + RecruitPriority(7), + InitNum(0), + MaxAllowed(0), + Fear(0), + House(HOUSE_NONE), + Trigger(0), + Origin(-1), + Number(0), + MissionCount(0), + ClassCount(0) +{ + for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { + Members[i].Class = NULL; + Members[i].Quantity = 0; + } +} + + +/*************************************************************************** + * TeamTypeClass::Init -- pre-scenario initialization * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Init(void) +{ + TeamTypes.Free_All(); +} + + +/*************************************************************************** + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * * + * INPUT: * + * name name of teamtype * + * * + * OUTPUT: * + * ptr to TeamType with that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass * TeamTypeClass::As_Pointer(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (!stricmp(name, TeamTypes.Ptr(index)->IniName)) { + return(TeamTypes.Ptr(index)); + } + } + } + return(NULL); +} + + +/*************************************************************************** + * TeamTypeClass::Mission_From_Name -- returns team mission for given name * + * * + * INPUT: * + * name name to compare * + * * + * OUTPUT: * + * mission for that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +TeamMissionType TeamTypeClass::Mission_From_Name(char const * name) +{ + if (name) { + for (TeamMissionType order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { + if (stricmp(TMissions[order], name) == 0) { + return(order); + } + } + } + + return(TMISSION_NONE); +} + + +/*************************************************************************** + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * * + * INPUT: * + * order mission to get name for * + * * + * OUTPUT: * + * name of mission * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) +{ + assert((unsigned)order < TMISSION_COUNT); + + return(TMissions[order]); +} + + +/*************************************************************************** + * TeamTypeClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new TeamType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void * TeamTypeClass::operator new(size_t ) +{ + void * ptr = TeamTypes.Allocate(); + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*************************************************************************** + * TeamTypeClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = false; + } + TeamTypes.Free((TeamTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * * + * Use this routine to create a team object from this team type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly created team object. If one could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * TeamTypeClass::Create_One_Of(void) const +{ + if (ScenarioInit || Number < MaxAllowed) { +// if (ScenarioInit || TeamClass::Number[ID] < MaxAllowed) { + return(new TeamClass(this, HouseClass::As_Pointer(House))); + } + return(NULL); +} + + +/*********************************************************************************************** + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * * + * This routine will destroy all teams of this type. Typical use of this is from a trigger * + * event. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Destroy_All_Of(void) const +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + if (team->Class == this) { + delete team; + index--; + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * * + * This routine will scan through the team types available and create teams of the * + * type that can best utilize the existing unit mix. * + * * + * INPUT: house -- Pointer to the house that this team is to be created for. * + * * + * atypes -- A bit mask of the aircraft types available for this house. * + * * + * utypes -- A bit mask of the unit types available for this house. * + * * + * itypes -- A bit mask of the infantry types available for this house. * + * * + * vtypes -- A bit mask of the vessel types available for this house. * + * * + * alerted -- Is this house alerted? If true, then the Autocreate teams will be * + * considered in the selection process. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1995 JLB : Created. * + * 07/21/1995 JLB : Will autocreate team even if no members in field. * + *=============================================================================================*/ +TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long , long , long , long , bool alerted) +//TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted) +{ +// TeamTypeClass const * best = NULL; +// int bestvalue = 0; + + TeamTypeClass const * choices[20]; + int choicecount = 0; + + for (int index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * ttype = TeamTypes.Ptr(index); + + assert(ttype != NULL); + + int maxnum = ttype->MaxAllowed; + if ((alerted && !ttype->IsAutocreate) || (!alerted && ttype->IsAutocreate)) { + maxnum = 0; + } + + if (choicecount >= ARRAY_SIZE(choices)) break; + + if (ttype != NULL && ttype->House == house->Class->House && ttype->Number < maxnum) { + + choices[choicecount++] = ttype; + +#ifdef OBSOLETE + /* + ** Determine what kind of units this team requires. + */ + long uneeded = 0; + long ineeded = 0; + long vneeded = 0; + long aneeded = 0; + for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { + switch (ttype->Members[ctype].Class->What_Am_I()) { + case RTTI_INFANTRYTYPE: + ineeded |= (1 << ((InfantryTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_UNITTYPE: + uneeded |= (1 << ((UnitTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_VESSELTYPE: + vneeded |= (1 << ((VesselTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_AIRCRAFTTYPE: + aneeded |= (1 << ((AircraftTypeClass *)ttype->Members[ctype].Class)->Type); + break; + } + } + + /* + ** If this team can use the types required, then consider it a possible + ** team type to create. + */ + int value = 0; + if ((aneeded & atypes) != 0 || (ineeded & itypes) != 0 || (uneeded & utypes) != 0 || (vneeded & vtypes) != 0) { + value = ttype->RecruitPriority; + } else { + value = ttype->RecruitPriority/2; + } + + if (best == NULL || bestvalue < value) { + bestvalue = value; + best = ttype; + } +#endif + } + } + + if (choicecount > 0) { + return(choices[Random_Pick(0, choicecount-1)]); + } + return(NULL); + +// return(best); +} + + +/*********************************************************************************************** + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * * + * This routine is used to convert an ASCII name of a team type into the corresponding * + * team type pointer. * + * * + * INPUT: name -- Pointer to the ASCII name of the team type. * + * * + * OUTPUT: Returns with a pointer to the team type that this ASCII name represents. If there * + * is no match, the NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * TeamTypeClass::From_Name(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (stricmp(name, TeamTypes.Ptr(index)->IniName) == 0) { + return(TeamTypes.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * TeamMission_Needs -- Determines what extra data is needed by team mission. * + * * + * This routine will return the required extra data that the specified team mission will * + * need. * + * * + * INPUT: tmtype -- The team mission type to check. * + * * + * OUTPUT: Returns with the data type needed for this team mission. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +NeedType TeamMission_Needs(TeamMissionType tmtype) +{ + switch (tmtype) { + /* + ** Requires a formation type. + */ + case TMISSION_FORMATION: + return(NEED_FORMATION); + + /* + ** Team mission requires a target quarry value. + */ + case TMISSION_ATTACK: + return(NEED_QUARRY); + + /* + ** Team mission requires a data value. + */ + case TMISSION_MOVECELL: + return(NEED_HEX_NUMBER); + + case TMISSION_SET_GLOBAL: + case TMISSION_GUARD: + case TMISSION_LOOP: + return(NEED_NUMBER); + + /* + ** Team mission requires a waypoint. + */ + case TMISSION_PATROL: + case TMISSION_MOVE: + case TMISSION_ATT_WAYPT: + case TMISSION_SPY: + return(NEED_WAYPOINT); + + /* + ** Team mission requires a general mission type. + */ + case TMISSION_DO: + return(NEED_MISSION); + + default: + break; + } + return(NEED_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * * + * This routine will display a team mission list box entry. It converts the index number * + * into the appropriate text string and then displays the text at the coordinates * + * specified. * + * * + * INPUT: index -- The index number to use when rendering the team mission. * + * * + * x,y -- The X,Y coordinate to use when drawing the team mission entry. * + * * + * width, height -- The dimensions of the area that the description can be * + * rendered into. * + * * + * selected -- Is this entry currently selected? If so, then it should be rendered * + * differently. * + * * + * flags -- Text print control flags used for when the text is printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +void TeamMissionClass::Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, + scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(index), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(index), x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Edit -- Edit the team type. * + * * + * This routine handles the editing dialog box for the team type. * + * * + * INPUT: none * + * * + * OUTPUT: Was the team type edited? A FALSE indicates that the dialog was canceled. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Edit(void) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_NAME_X=D_DIALOG_X+35, + D_NAME_Y=D_DIALOG_Y+27, + ED_WIDTH=40, + + D_CHECK_X=D_DIALOG_X+35, // Start of check box attribute list. + D_CHECK_Y=D_NAME_Y+25, + + CB_SPACING_Y=9, // Vertical spacing between check box lines. + CB_SPACING_X=8, // Horizontal spacing for check box description text. + D_SPACING_X=9, // Horizontal spacing between data entry fields. + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+35), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - (D_CANCEL_H+20), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + D_DIALOG_W - (D_OK_W+18)*2, + D_OK_Y = D_CANCEL_Y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_NAME=100, + BUTTON_RECRUIT, + BUTTON_MAXNUM, + BUTTON_INITNUM, + BUTTON_HOUSE, + BUTTON_ROUNDABOUT, + BUTTON_LEARNING, + BUTTON_SUICIDE, + BUTTON_AUTO, + BUTTON_PREBUILT, + BUTTON_REINFORCE, + BUTTON_MISSION1, + BUTTON_MISSION2, + BUTTON_ADD, + BUTTON_INSERT, + BUTTON_DELETE, + BUTTON_REPLACE, + BUTTON_ARG, + BUTTON_FORMATION, + BUTTON_MEMBERS, + BUTTON_MISSION, + BUTTON_TRIGGER, + BUTTON_ORIGIN, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_QUARRY, + }; + + /* + ** Dialog variables: + */ + ControlClass * commands = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team name edit field. + */ + char name_buf[10]; + EditClass name_edt(BUTTON_NAME, name_buf, sizeof(name_buf), + TPF_EFNT | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, ED_WIDTH, 9, EditClass::ALPHANUMERIC); + strcpy(name_buf, IniName); + commands = &name_edt; + + /* + ** House ownership of this team. + */ + char housetext[25] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X+name_edt.Width+D_SPACING_X, name_edt.Y, 55, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + housebtn.Add(*commands); + + /* + ** Recruit priority for this team. + */ + char recr_buf[4]; + EditClass recr_edt(BUTTON_RECRUIT, recr_buf, sizeof(recr_buf), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X+housebtn.Width+5+D_SPACING_X, housebtn.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(recr_buf, "%d", RecruitPriority); + recr_edt.Add(*commands); + + /* + ** Maximum allowed for this team type. + */ + char maxnum_buf[4]; + EditClass maxnum_edt(BUTTON_MAXNUM, maxnum_buf, sizeof(maxnum_buf), + TPF_EFNT | TPF_NOSHADOW, + recr_edt.X+recr_edt.Width+D_SPACING_X, recr_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(maxnum_buf, "%d", MaxAllowed); + maxnum_edt.Add(*commands); + + /* + ** Initial number for this team type. + */ + char initnum_buf[4]; + EditClass initnum_edt(BUTTON_INITNUM, initnum_buf, sizeof(initnum_buf), + TPF_EFNT | TPF_NOSHADOW, + maxnum_edt.X+maxnum_edt.Width+D_SPACING_X, maxnum_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(initnum_buf, "%d", InitNum); + initnum_edt.Add(*commands); + + /* + ** Waypoint preference to create/reinforce this team. + */ + char origin[4]; + EditClass originbtn(BUTTON_ORIGIN, origin, sizeof(origin), + TPF_EFNT | TPF_NOSHADOW, + initnum_edt.X+initnum_edt.Width+D_SPACING_X, initnum_edt.Y, 20, 9, EditClass::ALPHA); + *originbtn.Get_Text() = '\0'; + if (Origin != -1) { + if (Origin < 26) { + sprintf(originbtn.Get_Text(), "%c", Origin + 'A'); + } else { + sprintf(originbtn.Get_Text(), "%c%c", (Origin/26) + 'A'-1, Origin % 26 + 'A'); + } + } + originbtn.Add(*commands); + + /* + ** Members of this team control button. + */ + TextButtonClass membersbtn(BUTTON_MEMBERS, "Members", TPF_EBUTTON, name_edt.X, name_edt.Y+12, 50); + membersbtn.Add(*commands); + + /* + ** Trigger to assign to each member of this team (when object joins team). + */ + char trigtext[25] = ""; + DropListClass triggerbtn(BUTTON_TRIGGER, trigtext, sizeof(trigtext), + TPF_EFNT | TPF_NOSHADOW, + D_DIALOG_X+D_DIALOG_W-95, membersbtn.Y, 60, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + triggerbtn.Add_Item(""); + for (int index = 0; index < TriggerTypes.Count(); index++) { + triggerbtn.Add_Item(TriggerTypes.Ptr(index)->IniName); + } + if (Trigger.Is_Valid()) { + triggerbtn.Set_Selected_Index(Trigger->Name()); + } else { + triggerbtn.Set_Selected_Index(0); + } + triggerbtn.Add(*commands); + + /* + ** Roundabout travel logic attribute for this team. + */ + CheckBoxClass roundbtn(BUTTON_ROUNDABOUT, D_CHECK_X, D_CHECK_Y); + if (IsRoundAbout) { + roundbtn.Turn_On(); + } else { + roundbtn.Turn_Off(); + } + roundbtn.Add(*commands); + + /* + ** Suicide travel to target attribute. + */ + CheckBoxClass suicidebtn(BUTTON_SUICIDE, D_CHECK_X, roundbtn.Y+CB_SPACING_Y); + if (IsSuicide) { + suicidebtn.Turn_On(); + } else { + suicidebtn.Turn_Off(); + } + suicidebtn.Add(*commands); + + /* + ** Autocreate attribute for this team. + */ + CheckBoxClass autocreatebtn(BUTTON_AUTO, D_CHECK_X, suicidebtn.Y+CB_SPACING_Y); + if (IsAutocreate) { + autocreatebtn.Turn_On(); + } else { + autocreatebtn.Turn_Off(); + } + autocreatebtn.Add(*commands); + + /* + ** Prebuild team members attribute for this team. + */ + CheckBoxClass prebuildbtn(BUTTON_PREBUILT, D_CHECK_X, autocreatebtn.Y+CB_SPACING_Y); + if (IsPrebuilt) { + prebuildbtn.Turn_On(); + } else { + prebuildbtn.Turn_Off(); + } + prebuildbtn.Add(*commands); + + /* + ** Reinforce this team in progress attribute. + */ + CheckBoxClass reinforcebtn(BUTTON_REINFORCE, D_CHECK_X, prebuildbtn.Y+CB_SPACING_Y); + if (IsReinforcable) { + reinforcebtn.Turn_On(); + } else { + reinforcebtn.Turn_Off(); + } + reinforcebtn.Add(*commands); + + /* + ** Create drop box of possible team missions. This is used when building/editing the + ** team mission list. + */ + char droptext[45]; + DropListClass missionlist1(BUTTON_MISSION1, droptext, sizeof(droptext), + TPF_EFNT | TPF_NOSHADOW, + reinforcebtn.X, reinforcebtn.Y+15, 170, 8*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TeamMissionType tm = TMISSION_FIRST; tm < TMISSION_COUNT; tm++) { + missionlist1.Add_Item(TeamTypeClass::Name_From_Mission(tm)); + } + missionlist1.Set_Selected_Index(0); + missionlist1.Add_Tail(*commands); + + /* + ** Optional mission argument entry field. + */ + char arg_buf[6] = {0}; + EditClass arg_edt(BUTTON_ARG, arg_buf, sizeof(arg_buf), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 60, -1, EditClass::ALPHANUMERIC); +// arg_edt.Add(*commands); + + char qtext[55]; + DropListClass qlist(BUTTON_QUARRY, qtext, sizeof(qtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist.Add_Item(QuarryName[q]); + } + qlist.Set_Selected_Index(0); + qlist.Add_Tail(*commands); + + char ftext[55]; + DropListClass flist(BUTTON_FORMATION, ftext, sizeof(ftext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (FormationType f = FORMATION_FIRST; f < FORMATION_COUNT; f++) { + flist.Add_Item(FormationName[f]); + } + flist.Set_Selected_Index(0); + flist.Add_Tail(*commands); + + char mtext[55]; + DropListClass mlist(BUTTON_MISSION, mtext, sizeof(mtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (MissionType m = MISSION_FIRST; m < MISSION_COUNT; m++) { + mlist.Add_Item(MissionClass::Mission_Name(m)); + } + mlist.Set_Selected_Index(0); + mlist.Add_Tail(*commands); + + TListClass missionlist2(BUTTON_MISSION2, missionlist1.X+60, missionlist1.Y+22, 240, 8*7, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (index = 0; index < MissionCount; index++) { + missionlist2.Add_Item(new TeamMissionClass(MissionList[index])); +// missionlist2.Add_Item(&TeamMissions[MissionList[index].Mission]); + } + static int tabs[] = {13, 40}; // list box tab stops + missionlist2.Set_Tabs(tabs); + missionlist2.Add_Tail(*commands); + + /* + ** Mission editing command buttons. + */ + TextButtonClass addbtn(BUTTON_ADD, "Append", TPF_EBUTTON, D_NAME_X, missionlist1.Y+missionlist1.Height+1, 50); + addbtn.Add(*commands); + + TextButtonClass insertbtn(BUTTON_INSERT, "Insert", TPF_EBUTTON, addbtn.X, addbtn.Y+10, 50); + insertbtn.Add(*commands); + + TextButtonClass delbtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, insertbtn.X, insertbtn.Y+10, 50); + delbtn.Add(*commands); + + TextButtonClass repbtn(BUTTON_REPLACE, "Replace", TPF_EBUTTON, delbtn.X, delbtn.Y+10, 50); + repbtn.Add(*commands); + + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the list + */ + okbtn.Add(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + int lastindex = -1; + int lastcount = -1; + int lastbutton = -1; + bool cancel = false; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + /* + ** Add the optional data entry field as necessary. + */ + arg_edt.Remove(); + qlist.Remove(); + flist.Remove(); + mlist.Remove(); + switch (TeamMission_Needs(TeamMissionType(missionlist1.Current_Index()))) { + default: + break; + + case NEED_MISSION: + mlist.Add(*commands); + break; + + case NEED_FORMATION: + flist.Add(*commands); + break; + + case NEED_WAYPOINT: + case NEED_NUMBER: + case NEED_HEX_NUMBER: + arg_edt.Add(*commands); + break; + + case NEED_QUARRY: + qlist.Add(*commands); + break; + } + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Pri:", recr_edt.X, recr_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Max:", maxnum_edt.X, maxnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Num:", initnum_edt.X, initnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Loc:", originbtn.X, originbtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("# Team Mission", missionlist2.X, missionlist2.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Trigger:", triggerbtn.X-4, triggerbtn.Y+1, scheme, TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print(Member_Description(), membersbtn.X + membersbtn.Width + 3, membersbtn.Y+1, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print("Use safest, possibly longer, route to target?", roundbtn.X+CB_SPACING_X, roundbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Charge toward target ignoring distractions?", suicidebtn.X+CB_SPACING_X, suicidebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Only 'Autocreate A.I.' uses this team type?", autocreatebtn.X+CB_SPACING_X, autocreatebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Prebuild team members before team is created?", prebuildbtn.X+CB_SPACING_X, prebuildbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Automatically reinforce team whenever possible?", reinforcebtn.X+CB_SPACING_X, reinforcebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw the buttons + */ + commands->Draw_All(); + Show_Mouse(); + + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + + /* + ** Select a Mission on the right-hand mission list; update the Argument + ** field to reflect the current value. This only serves as an aide to editing + ** the team mission list. + */ + case BUTTON_MISSION2 | KN_BUTTON: + if (missionlist2.Count() && lastcount == missionlist2.Count() && lastbutton == BUTTON_MISSION2 && lastindex == missionlist2.Current_Index()) { + missionlist1.Set_Selected_Index(missionlist2.Current_Item()->Mission); + + switch (TeamMission_Needs(missionlist2.Current_Item()->Mission)) { + case NEED_MISSION: + mlist.Set_Selected_Index(missionlist2.Current_Item()->Data.Mission); + break; + + case NEED_FORMATION: + flist.Set_Selected_Index(missionlist2.Current_Item()->Data.Formation); + break; + + case NEED_NUMBER: + sprintf(arg_edt.Get_Text(), "%d", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(arg_edt.Get_Text(), "%x", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_QUARRY: + strcpy(qlist.Get_Text(), QuarryName[missionlist2.Current_Item()->Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (missionlist2.Current_Item()->Data.Value < 26) { + sprintf(arg_edt.Get_Text(), "%c", missionlist2.Current_Item()->Data.Value + 'A'); + } else { + sprintf(arg_edt.Get_Text(), "%c%c", (missionlist2.Current_Item()->Data.Value)/26 + 'A'-1, (missionlist2.Current_Item()->Data.Value % 26) + 'A'); + } + break; + } + } + lastindex = missionlist2.Current_Index(); + lastcount = missionlist2.Count(); + display = true; + break; + + /* + ** Add current mission data to current position of team mission list. Any + ** subsequent missions get moved downward. + */ + case BUTTON_INSERT | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Insert_Item(tm); + } + display = true; + break; + + /* + ** Add mission data to the end of the mission list. + */ + case BUTTON_ADD | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A';; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Add_Item(tm); + display = true; + } + break; + + /* + ** Replace the currently selected mission with the work mission data. + */ + case BUTTON_REPLACE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = missionlist2.Current_Item(); + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A'; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2[missionlist2.Current_Index()] = tm; + } + display = true; + break; + + /* + ** Delete the currently selected mission. + */ + case BUTTON_DELETE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = missionlist2.Current_Item(); + missionlist2.Remove_Index(missionlist2.Current_Index()); + delete tm; + } + display = true; + break; + + /* + ** Invoke the members dialog + */ + case BUTTON_MEMBERS | KN_BUTTON: + + /* + ** Take editor focus away + */ + membersbtn.Turn_Off(); + + /* + ** Invoke the dialog + */ + Map.Team_Members(HousesType(housebtn.Current_Index())); + + /* + ** Redraw + */ + display = true; + break; + + /* + ** When the OK button is selected, lift the values from the dialog box + ** and place them into the team type object. + */ + case BUTTON_OK | KN_BUTTON: + strtrim(name_edt.Get_Text()); + if (strlen(name_edt.Get_Text()) != 0) { + strcpy(IniName, name_edt.Get_Text()); + } else { + strcpy(IniName, "----"); + } + + IsRoundAbout = roundbtn.IsOn; + IsSuicide = suicidebtn.IsOn; + IsAutocreate = autocreatebtn.IsOn; + IsPrebuilt = prebuildbtn.IsOn; + IsReinforcable = reinforcebtn.IsOn; + + RecruitPriority = atoi(recr_edt.Get_Text()); + InitNum = atoi(initnum_edt.Get_Text()); + MaxAllowed = atoi(maxnum_edt.Get_Text()); + House = HousesType(housebtn.Current_Index()); + Trigger = NULL; + if (triggerbtn.Current_Index() > 0) { + Trigger = TriggerTypes.Ptr(triggerbtn.Current_Index()-1); + } + + MissionCount = missionlist2.Count(); + for (index = 0; index < MissionCount; index++) { + MissionList[index].Data.Value = 0; // Clears extra bits. + MissionList[index] = *missionlist2[index]; + } + +#ifdef TOFIX +// the mission class objects pointed to in the list should be deleted? +#endif + + if (strlen(originbtn.Get_Text())) { + if (strlen(originbtn.Get_Text()) == 1) { + Origin = toupper(*originbtn.Get_Text()) - 'A'; + } else { + Origin = (toupper(*originbtn.Get_Text())+1 - 'A' ) * 26; + Origin += toupper(*(originbtn.Get_Text()+1)) - 'A'; + } + } else { + Origin = -1; + } + cancel = false; + process = false; + break; + + /* + ** Cancel: return + */ + case BUTTON_CANCEL | KN_BUTTON: + cancel = true; + process = false; + break; + + /* + ** Unrecognized events are ignored. If any button related event is + ** detected, then collapse any drop down list boxes. This keeps the + ** dialog box clean. + */ + default: + if (input & KN_BUTTON) { + housebtn.Collapse(); + missionlist1.Collapse(); + triggerbtn.Collapse(); + display = true; + } + break; + } + + /* + ** Record the last dialog control touched so that a double click + ** can be detected. + */ + if (input & KN_BUTTON) { + lastbutton = (input & ~KN_BUTTON); + } + } + + return(!cancel); +} + + +int atoh(char * str) +{ + int retval = 0; + while (*str) { + retval *= 16; + if (*str >= '0' && *str <= '9') { + retval += *str-'0'; + } else { + if (*str >= 'a' && *str <= 'f') { + retval += 10 + (*str - 'a'); + } else { + if (*str >= 'A' && *str <= 'F') { + retval += 10 + (*str - 'A'); + } + } + } + str++; + } + return(retval); +} + +#endif + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Member_Description -- Builds a member description string. * + * * + * This routine will build a team member description string. The string will be composed * + * of the team member type and quantity. As many team member types will be listed that * + * can fit within a reasonable size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text string that contains a description of the team * + * type members. * + * * + * WARNINGS: The return string may be truncated if necessary to fit within reasonable size * + * limits. * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Member_Description(void) const +{ + static char buffer[128]; + + buffer[0] = '\0'; + + /* + ** Fill in class & count for all classes + */ + for (int index = 0; index < ClassCount; index++) { + char txt[10]; + + strcat(buffer, Members[index].Class->IniName); + strcat(buffer, ":"); + + sprintf(txt, "%d", Members[index].Quantity); + strcat(buffer, txt); + + if (index < ClassCount-1) { + strcat(buffer, ","); + } + } + + if (strlen(buffer) > 25) { + strcpy(&buffer[25-3], "..."); + } + + return(buffer); +} + + +/*********************************************************************************************** + * TeamTypeClass::Description -- Builds a description of the team. * + * * + * This routine will build a brief description of the team type. This description is used * + * in the team type list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the composed text string that represents the team type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Description(void) const +{ + static char _buffer[128]; + char extra = ' '; + char loc[3]; + + loc[0] = loc[1] = loc[2] = 0; + if (IsAutocreate) extra = '*'; + if (Origin > -1) { +// if (Origin != -1) { + if (Origin < 26) { + loc[0] = 'A' + Origin; + } else { + loc[0] = Origin / 26 + 'A'-1; + loc[1] = Origin % 26 + 'A'; + } + } + + sprintf(_buffer, "%s\t%s\t%c%s\t%d\t%s", IniName, HouseTypeClass::As_Reference(House).Suffix, extra, loc, MissionCount, Member_Description()); + return(_buffer); +} + + +/*********************************************************************************************** + * TeamMissionClass::Description -- Compose a text description of team mission. * + * * + * This routine will create a text representation of a team mission. This description will * + * be used in the list of team missions for display purposes. * + * * + * INPUT: index -- The index to assign to this team. The index is used since some team * + * missions refer to the another team mission by index number. * + * * + * OUTPUT: Returns with a pointer to the team mission text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamMissionClass::Description(int index) const +{ + static char buffer[64]; + + sprintf(buffer, "%d\t%s", index, TeamTypeClass::Name_From_Mission(Mission)); + + switch (TeamMission_Needs(Mission)) { + case NEED_MISSION: + strcat(buffer, MissionClass::Mission_Name(Data.Mission)); + break; + + case NEED_FORMATION: + strcat(buffer, FormationName[Data.Quarry]); + break; + + case NEED_NUMBER: + sprintf(&buffer[strlen(buffer)], "%d", Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(&buffer[strlen(buffer)], "%x", Data.Value); + break; + + case NEED_QUARRY: + strcat(buffer, QuarryName[Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (Data.Value < 26) { + sprintf(&buffer[strlen(buffer)], "%c", Data.Value + 'A'); + } else { + sprintf(&buffer[strlen(buffer)], "%c%c", (Data.Value/26) + 'A'-1, (Data.Value % 26) + 'A'); + } + break; + + } + + return(buffer); +} +#endif + + +/*********************************************************************************************** + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * * + * This routine is called when some object is about to be removed from the game system and * + * all references to it must be severed. This will check to see if the specified object * + * is a trigger that this team refers to. If so, then the reference will be cleared. * + * * + * INPUT: target -- The target object to remove references to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Detach(TARGET target, bool) +{ + if (Is_Target_TriggerType(target) && Trigger.Is_Valid() && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*************************************************************************** + * TeamTypeClass::Read_INI -- reads INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_INI(CCINIClass & ini) +{ + TeamTypeClass * team; // Working team pointer. + char buf[500]; // INI entry buffer + + int len = ini.Entry_Count(INI_Name()); + + /* + ** Loop for all team entries, create and fill in. + */ + for (int index = 0; index < len; index++) { + team = new TeamTypeClass(); + if (team != NULL) { + char const * entry = ini.Get_Entry(INI_Name(), index); + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + team->Fill_In((char *)entry, buf); + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given teamtype with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + * 11/29/1995 JLB : Revamped to use new team class. * + *=============================================================================================*/ +void TeamTypeClass::Fill_In(char * name, char * entry) +{ + assert(TeamTypes.ID(this) == ID); + + /* + ** Set its name + */ + Set_Name(name); + + House = HousesType(atoi(strtok(entry, ","))); + + int code; + switch (NewINIFormat) { + default: + code = atoi(strtok(NULL, ",")); + IsRoundAbout = ((code & 0x0001) != 0); + IsSuicide = ((code & 0x0002) != 0); + IsAutocreate = ((code & 0x0004) != 0); + IsPrebuilt = ((code & 0x0008) != 0); + IsReinforcable = ((code & 0x0010) != 0); + break; + + case 0: + case 1: + IsRoundAbout = atoi(strtok(NULL, ",")); + IsSuicide = atoi(strtok(NULL, ",")); + IsAutocreate = atoi(strtok(NULL, ",")); + IsPrebuilt = atoi(strtok(NULL, ",")); + IsReinforcable = atoi(strtok(NULL, ",")); + break; + } + + RecruitPriority = atoi(strtok(NULL, ",")); + InitNum = atoi(strtok(NULL, ",")); + MaxAllowed = atoi(strtok(NULL, ",")); + Origin = atoi(strtok(NULL, ",")); + + switch (NewINIFormat) { + default: + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + break; + + case 0: + case 1: + // Throw this token away -- it isn't used. + strtok(NULL, ","); + break; + } + + /* + ** Fetch the team member types and quantity values. + */ + ClassCount = atoi(strtok(NULL, ",")); + for (int index = 0; index < ClassCount; index++) { + char * p1 = strtok(NULL, ",:"); + char * p2 = strtok(NULL, ",:"); + TechnoTypeClass const * otype = NULL; + + /* + ** See if this is an infantry name + */ + InfantryType i_id = InfantryTypeClass::From_Name(p1); + if (i_id != INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } else { + + /* + ** See if this is a unit name + */ + UnitType u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } else { + + /* + ** See if this is an aircraft name + */ + AircraftType a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } else { + + /* + ** See if this is a vessel name. + */ + VesselType v_id = VesselTypeClass::From_Name(p1); + if (v_id != VESSEL_NONE) { + otype = &VesselTypeClass::As_Reference(v_id); + } + } + } + } + + /* + ** If the name was resolved, add this class + */ + if (otype) { + Members[index].Class = otype; + Members[index].Quantity = atoi(p2); + } else { + ClassCount--; + if (index == 0) break; + index--; + } + } + + /* + ** Fetch the missions assigned to this team type. + */ + MissionCount = atoi(strtok(NULL, ",")); + for (index = 0; index < MissionCount; index++) { + MissionList[index].Mission = TeamMissionType(atoi(strtok(NULL, ",:"))); + MissionList[index].Data.Value = atoi(strtok(NULL, ",:")); + } + + if (NewINIFormat < 2) { + /* + ** Fetch the trigger ID. + */ + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * * + * This routine will take all team types and write them out to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that will hold al the teams. * + * * + * OUTPUT: none * + * * + * WARNINGS: All preexisting team data in the database will be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("TeamTypes"); + ini.Clear(INI_Name()); + + /* + ** Now write all the team data out + */ + for (int index = 0; index < TeamTypes.Count(); index++) { +// for (int index = TeamTypes.Count()-1; index >= 0; index--) { + TeamTypeClass * team = TeamTypes.Ptr(index); + char buf[256]; + + buf[0] = 0; + team->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), team->IniName, buf); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * * + * This routine is used to build the text string that will go into the INI database for * + * a team of this type. This text string will be parsed back into a team object when the * + * scenario INI is read in. * + * * + * INPUT: buf -- Pointer to a buffer that will hold the team text entry. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the buffer can hold the string build. A size of 80 or so is * + * usually sufficient. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Build_INI_Entry(char * buf) +{ + int code = 0; + code |= IsRoundAbout ? 0x0001 : 0; + code |= IsSuicide ? 0x0002 : 0; + code |= IsAutocreate ? 0x0004 : 0; + code |= IsPrebuilt ? 0x0008 : 0; + code |= IsReinforcable ? 0x0010 : 0; + + /* + ** Output the general data for this team type. + */ + sprintf(buf, "%d,%d,%d,%d,%d,%d,%d", + House, + code, + RecruitPriority, + InitNum, + MaxAllowed, + Origin, + TriggerTypes.Logical_ID(Trigger) + ); + buf += strlen(buf); + + /* + ** For every class in the team, record the class's name & desired count + */ + sprintf (buf, ",%d", ClassCount); + buf += strlen(buf); + for (int i = 0; i < ClassCount; i++) { + sprintf (buf, ",%s:%d", Members[i].Class->IniName, Members[i].Quantity); + buf += strlen(buf); + } + + /* + ** Record the # of missions, and each mission name & argument value. + */ + sprintf(buf, ",%d", MissionCount); + buf += strlen(buf); + for (i = 0; i < MissionCount; i++) { + sprintf (buf, ",%d:%d", MissionList[i].Mission, MissionList[i].Data.Value); + buf += strlen(buf); + } +} + + diff --git a/CODE/TEAMTYPE.H b/CODE/TEAMTYPE.H new file mode 100644 index 0000000..628d6ef --- /dev/null +++ b/CODE/TEAMTYPE.H @@ -0,0 +1,280 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEAMTYPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEAMTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAMTYPE_H +#define TEAMTYPE_H + +/* +** TeamMissionType: the various missions that a team can have. +*/ +typedef enum TeamMissionType { + TMISSION_NONE=-1, + TMISSION_ATTACK, // Attack specified quarry type. + TMISSION_ATT_WAYPT, // Attack specified waypoint + TMISSION_FORMATION, // Change formation of team. + TMISSION_MOVE, // moves to waypoint specified. + TMISSION_MOVECELL, // moves to cell # specified. + TMISSION_GUARD, // works like an infantry's guard mission + TMISSION_LOOP, // loop back to start of mission list + TMISSION_ATTACKTARCOM, // attack tarcom + TMISSION_UNLOAD, // Unload at current location. + TMISSION_DEPLOY, // Deploy mobile building type. + TMISSION_HOUND_DOG, // Follow nearest friendly unit. + TMISSION_DO, // Do guard, sticky, area guard (mission sticks on this). + TMISSION_SET_GLOBAL, // Set global variable. + TMISSION_INVULNERABLE, // Magical invulnerability. + TMISSION_LOAD, // Load onto transport member of team. + TMISSION_SPY, // Spy enter the building at specified waypoint + TMISSION_PATROL, // Move but look for enemies as well. + + TMISSION_COUNT, + TMISSION_FIRST=0 +} TeamMissionType; + + +/* +** Forward declarations. +*/ +class TechnoTypeClass; + + +/* +** This structure contains one team mission value & its argument. +*/ +class TeamMissionClass +{ + public: +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(int index) const; + operator const char * () const {return(Description(0));}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags); + + TeamMissionType Mission; // Mission type. + union { + FormationType Formation; // Formation to use. + QuarryType Quarry; // Combat quarry type. + MissionType Mission; // General mission orders. + int Value; // Usually a waypoint number. + } Data; +}; + + +/* +** This class specifies the quantity and type of members desired for the +** team. +*/ +class TeamMemberClass +{ + public: + int Quantity; // Number of objects desired for this type. + TechnoTypeClass const * Class; // The type of object desired. +}; + + +/* +** TeamTypeClass declaration +*/ +class TeamTypeClass : public AbstractTypeClass +{ + public: + enum TeamTypeClassEnums { + MAX_TEAM_CLASSCOUNT=5, + MAX_TEAM_MISSIONS=20 + }; + + /* + ** Constructor/Destructor + */ + TeamTypeClass(void); + TeamTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Trigger(x) {}; + virtual ~TeamTypeClass(void) {}; + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all team types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + void Build_INI_Entry(char * buffer); + static void Read_INI(CCINIClass & ini); + void Fill_In(char *name, char *entry); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "TeamTypes";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give its name + */ + static TeamTypeClass *As_Pointer(char const * name); + + /* + ** Processing routines + */ + TeamClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + void Detach(TARGET target, bool all=true); + + /* + ** Utility routines + */ + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + static char const * Name_From_Mission(TeamMissionType order); + static TeamMissionType Mission_From_Name(char const *name); + static TeamTypeClass const * Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted); + static TeamTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Member_Description(void) const; + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + + /* + ** If this teamtype object is active, then this flag will be true. + ** TeamType objects that are not active are either not yet created or have + ** been deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** If RoundAbout, the team avoids high-threat areas + */ + unsigned IsRoundAbout:1; + + /* + ** If Suicide, the team won't stop until it achieves its mission or it's + ** dead + */ + unsigned IsSuicide:1; + + /* + ** Is this team type allowed to be created automatically by the computer + ** when the appropriate trigger indicates? + */ + unsigned IsAutocreate:1; + + /* + ** This flag tells the computer that it should build members to fill + ** a team of this type regardless of whether there actually is a team + ** of this type active. + */ + unsigned IsPrebuilt:1; + + /* + ** If this team should allow recruitment of new members, then this flag + ** will be true. A false value results in a team that fights until it + ** is dead. This is similar to IsSuicide, but they will defend themselves. + */ + unsigned IsReinforcable:1; + + /* + ** A transient team type was created exclusively to bring on reinforcements + ** as a result of some special event. As soon as there are no teams + ** existing of this type, then this team type should be deleted. + */ + unsigned IsTransient:1; + + /* + ** Priority given the team for recruiting purposes; higher priority means + ** it can steal members from other teams (scale: 0 - 15) + */ + int RecruitPriority; + + /* + ** Initial # of this type of team + */ + unsigned char InitNum; + + /* + ** Max # of this type of team allowed at one time + */ + unsigned char MaxAllowed; + + /* + ** Fear level of this team + */ + unsigned char Fear; + + /* + ** House the team belongs to + */ + HousesType House; + + /* + ** Trigger to assign to each object as it joins this team. + */ + CCPtr Trigger; + + /* + ** This is the waypoint origin to use when creating this team or + ** when bringing the team on as a reinforcement. + */ + WAYPOINT Origin; + + /* + ** This records the number of teams of this type that are currently + ** active. + */ + int Number; + + /* + ** Number and list of missions that this team will follow. + */ + int MissionCount; + TeamMissionClass MissionList[MAX_TEAM_MISSIONS]; + + /* + ** Number and type of members desired for this team. + */ + int ClassCount; + TeamMemberClass Members[MAX_TEAM_CLASSCOUNT]; + + static char const * TMissions[TMISSION_COUNT]; +}; + + +#endif diff --git a/CODE/TECHNO.CPP b/CODE/TECHNO.CPP new file mode 100644 index 0000000..8269652 --- /dev/null +++ b/CODE/TECHNO.CPP @@ -0,0 +1,6349 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TECHNO.CPP 5 3/17/97 1:28a Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : BASE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 8, 1994 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TechnoClass::AI -- Handles AI processing for techno object. * + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * TechnoClass::Captured -- Handles capturing this object. * + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon.* + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate.* + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * TechnoClass::Mark -- Handles marking of techno objects. * + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * TechnoClass::Owner -- Who is the owner of this object? * + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * TechnoClass::Renovate -- Heal a building to maximum * + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * TechnoClass::Set_Mission -- Forced mission set (used by editor). * + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * TechnoClass::Value -- Fetches the target value for this object. * + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Cloaking control values. +*/ +#define MAX_UNCLOAK_STAGE 38 + + +/*************************************************************************** +** These are the pointers to the special shape data that the units may need. +*/ +void const * TechnoTypeClass::WakeShapes = 0; +void const * TechnoTypeClass::TurretShapes = 0; +void const * TechnoTypeClass::SamShapes = 0; +void const * TechnoTypeClass::MGunShapes = 0; + +//Xlat Tables for French and German +// For name overriding +//#define NEWNAMES 22 +#ifdef GERMAN +const char* NewName[] = { + "Scout Ant", "Kundschafter-Ameise", + "Warrior Ant", "Krieger-Ameise", + "Fire Ant", "Feuer-Ameise", + "Queen Ant", "Ameisenk”nigin", + "ATS", "Angriffs-U-Boot", + "Tesla Tank", "Telsapanzer", + "Volkov", "Modell Volkov", + "Chitzkoi", "Roboterhund", + "Stavros", "Stavros", + "F-A Longbow", "Jagdhubschrauber Longbow", + "Civilian Specialist", "Wissenschaftler", + "Alloy Facility", "Legierungswerk", + NULL, + }; + +#endif +#ifdef FRENCH +const char* NewName[] = { + "Scout Ant", "Fourmi de Reconnaissance", + "Warrior Ant", "Fourmi GuerriŠre", + "Fire Ant", "Fourmi Lance-Flammes", + "Queen Ant", "Reine des Fourmis", + "ATS", "SMTA", + "Tesla Tank", "Tank Tesla", + "Volkov", "Volkov", + "Chitzkoi", "Cyber-Chien", + "Stavros", "Stavros", + "F-A Longbow", "HAA (H‚licoptŠre d'Assaut Avanc‚)", + "Civilian Specialist", "Sp‚cialiste Civil", + "Alloy Facility", "Usine M‚tallurgique", + NULL, + }; + +#endif + + +/*************************************************************************** +** Which shape to use depending on which facing is controlled by these arrays. +*/ +int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + + +/*********************************************************************************************** + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * * + * The player's army is considered to be all those mobile units that can be selected * + * and controlled by the player (they may or may not have weapons in the traditional * + * sense). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object part of the player's selectable controllable army? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Players_Army(void) const +{ + /* + ** An object that is dead (or about to be) is not considered part of + ** the player's army. + */ + if (Strength <= 0) { + return(false); + } + + /* + ** If the object is not yet on the map or is otherwise indisposed, then + ** don't consider it. + */ + if (IsInLimbo || !IsLocked) { + return(false); + } + + /* + ** Buildings, although sometimes serving a combat purpose, are not part + ** of the player's army. + */ + if (What_Am_I() == RTTI_BUILDING) { + return(false); + } + + /* + ** If not discoverd by the player, then don't consider it part of the + ** player's army (yet). + */ + if (!IsDiscoveredByPlayer) { + return(false); + } + + /* + ** If not selectable, then not really part of the player's active army. + */ + if (!Techno_Type_Class()->IsSelectable) { + return(false); + } + + /* + ** If not under player control, then it isn't part of the player's army. + */ + if (!House->IsPlayerControl) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * * + * Use this routine to examine the cell specified and if the cell could be a valid * + * destination for a teleport, then return true. * + * * + * INPUT: cell -- The cell to examine as a possible legal teleport destination. * + * * + * OUTPUT: bool; Is the specified cell a legal teleport destination? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Teleport_Here(CELL cell) const +{ + /* + ** Infantry are never allowed to teleport. + */ + if (What_Am_I() == RTTI_INFANTRY) { + return(false); + } + + /* + ** The destination cell must be on the playfield. + */ + if (!Map.In_Radar(cell)) { + return(false); + } + + /* + ** Teleporting directly onto the flag spot is not allowed. This is due to the + ** requirement that entering that location must proceed under normal channels. + */ + if (Map[cell].Overlay == OVERLAY_FLAG_SPOT) { + return(false); + } + + /* + ** Determine if the object could enter the cell by normal means. If the + ** cell is impassable, then it can't be teleported there. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (!Map[cell].Is_Clear_To_Move(ttype->Speed, true, true, -1, ttype->Speed == SPEED_FLOAT ? MZONE_WATER : MZONE_NORMAL)) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * * + * This routine will compare the weapons this object is equipped with verses the * + * candidate target object. The best weapon to use against the target will be returned. * + * Special emphasis is given to weapons that can fire on the target without requiring * + * this object to move within range. * + * * + * INPUT: target -- The candidate target to determine which weapon is best against. * + * * + * OUTPUT: Returns with an identifier the specifies what weapon to use against the target. * + * The return value will be "0" for the primary weapon and "1" for the secondary. * + * * + * WARNINGS: This routine is called very frequently. It should be as efficient as possible. * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::What_Weapon_Should_I_Use(TARGET target) const +{ + if (!Target_Legal(target)) return(0); + + /* + ** Fetch the armor of the candidate target object. Presume that if the target + ** is not an object, then its armor is equivalent to wood. Who knows why? + */ + ArmorType armor = ARMOR_WOOD; + if (Is_Target_Object(target)) { + armor = As_Object(target)->Class_Of().Armor; + } + + TechnoTypeClass const * ttype = Techno_Type_Class(); + + /* + ** Get the value of the primary weapon verses the candidate target. Increase the + ** value of the weapon if it happens to be in range. + */ + int w1 = 0; + WeaponTypeClass const * wptr = ttype->PrimaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w1 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 0)) w1 *= 2; + FireErrorType ok = Can_Fire(target, 0); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w1 = 0; + + /* + ** Calculate a similar value for the secondary weapon. + */ + int w2 = 0; + wptr = ttype->SecondaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w2 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 1)) w2 *= 2; + ok = Can_Fire(target, 1); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w2 = 0; + + /* + ** Return with the weapon identifier that should be used to fire upon the + ** candidate target. + */ + if (w2 > w1) return(1); + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * * + * This routine will determine the number of survivors that should run from this object * + * when it is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of survivors that should run from this object * + * when the object gets destroyed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::How_Many_Survivors(void) const +{ + if (Techno_Type_Class()->IsCrew) { + return(1); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon. * + * * + * This routine will examine the specified weapon of this object and determine how much * + * damage it could inflict -- best case. * + * * + * INPUT: which -- Which weapon to consider. If -1 is specified, then the average of both * + * weapon types (if present) is returned. * + * * + * OUTPUT: Returns with the combat damage that could be inflicted by the specified weapon. * + * * + * WARNINGS: This routine could return a negative number if a medic is scanned. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Combat_Damage(int which) const +{ + TechnoTypeClass const * ttype = Techno_Type_Class(); + + int divisor = 0; + int value = 0; + + if (which == 0 || which == -1) { + if (ttype->PrimaryWeapon != NULL) { + value += ttype->PrimaryWeapon->Attack; + divisor += 1; + } + } + + if (which == 1 || which == -1) { + if (ttype->SecondaryWeapon != NULL) { + value += ttype->SecondaryWeapon->Attack; + divisor += 1; + } + } + + if (divisor > 1) { + return(value / divisor); + } + return(value); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * * + * Determine is this object can recloak now and returns that info. Usually the answer is * + * yes, but it can be overridden be derived classes. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object recloak now? (presumes it has the ability to cloak) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Recloak(void) const +{ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * * + * This routine will determine the coordinate to use when this object fires. The coordinate * + * is the location where bullets appear (or fire effects appear) when the object fires * + * its weapon. * + * * + * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * + * weapon, 1 means secondary weapon. * + * * + * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * + * should appear. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TechnoClass::Fire_Coord(int which) const +{ + assert(IsActive); + + DirType dir = Turret_Facing(); + TechnoTypeClass const * tclass = Techno_Type_Class(); + + int dist = 0; + int lateral = 0; + if (which == 0) { + dist = tclass->PrimaryOffset; + lateral = tclass->PrimaryLateral; + } else { + dist = tclass->SecondaryOffset; + lateral = tclass->SecondaryLateral; + } + + COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height); + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); + } + coord = Coord_Move(coord, dir, dist); + + return(coord); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * * + * This routine is used to dump the status of the object class to the monochrome screen. * + * This display can be used to track down or prevent bugs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + mono->Set_Cursor(10, 7);mono->Printf("%2d", Fetch_Rate()); + mono->Set_Cursor(14, 7);mono->Printf("%2d", Fetch_Stage()); + mono->Set_Cursor(49, 1);mono->Printf("%3d", Ammo); + mono->Set_Cursor(71, 1);mono->Printf("$%4d", PurchasePrice); + mono->Set_Cursor(54, 1);mono->Printf("%3d", (long)Arm); + if (Is_Something_Attached()) { + mono->Set_Cursor(1, 5);mono->Printf("%08X", Attached_Object()); + } + if (Target_Legal(TarCom)) { + mono->Set_Cursor(29, 3);mono->Printf("%08X", TarCom); + } + if (Target_Legal(SuspendedTarCom)) { + mono->Set_Cursor(38, 3);mono->Printf("%08X", SuspendedTarCom); + } + if (Target_Legal(ArchiveTarget)) { + mono->Set_Cursor(69, 5);mono->Printf("%08X", ArchiveTarget); + } + mono->Set_Cursor(47, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); + mono->Set_Cursor(64, 1);mono->Printf("%d(%d)", Cloak, CloakingDevice); + + mono->Fill_Attrib(14, 15, 12, 1, IsUseless ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 16, 12, 1, IsTickedOff ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 17, 12, 1, IsCloakable ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 13, 12, 1, IsLeader ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 14, 12, 1, IsALoaner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 15, 12, 1, IsLocked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 16, 12, 1, IsInRecoilState ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 17, 12, 1, IsTethered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 13, 12, 1, IsOwnedByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 14, 12, 1, IsDiscoveredByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 15, 12, 1, IsDiscoveredByComputer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 16, 12, 1, IsALemon ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(47, 17);mono->Printf("%3d", (long)IronCurtainCountDown); + mono->Fill_Attrib(40, 17, 12, 1, IronCurtainCountDown > 0 ? MonoClass::INVERSE : MonoClass::NORMAL); + + RadioClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * * + * This default constructor for techno objects is used to reset all internal variables to * + * their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) : + RadioClass(rtti, id), + IsUseless(false), + IsTickedOff(false), + IsCloakable(false), + IsLeader(false), + IsALoaner(false), + IsLocked(false), + IsInRecoilState(false), + IsTethered(false), + IsOwnedByPlayer(false), + IsDiscoveredByPlayer(false), + IsDiscoveredByComputer(false), + IsALemon(false), + IsSecondShot(true), + ArmorBias(1), + FirepowerBias(1), + IdleTimer(0), + IronCurtainCountDown(0), + SpiedBy(0), + ArchiveTarget(TARGET_NONE), + House(HouseClass::As_Pointer(house)), + Cloak(UNCLOAKED), + TarCom(TARGET_NONE), + SuspendedTarCom(TARGET_NONE), + PrimaryFacing(DIR_N), + Arm(0), + Ammo(-1), + PurchasePrice(0) +{ + IsOwnedByPlayer = (PlayerPtr == House); +} + + +/*********************************************************************************************** + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * * + * Use this routine to determine the amount of time it would take to build an object of * + * this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time it should take (unmodified by outside factors) to build * + * this object. The time is expressed in game frames. * + * * + * WARNINGS: The time will usually be modified by power status and handicap rating for the * + * owning house. The value returned is merely the raw unmodified time to build. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 09/27/1996 JLB : Takes into account the power availability. * + *=============================================================================================*/ +//#define UNIT_BUILD_BIAS fixed(5,4) +//#define UNIT_BUILD_BIAS fixed(6,4) +#define UNIT_BUILD_BIAS fixed(1,1) +//#define UNIT_BUILD_BIAS fixed(5,1) + +extern int UnitBuildPenalty; + +int TechnoClass::Time_To_Build(void) const +{ + int val = Class_Of().Time_To_Build(); + +#ifdef FIXIT_VERSION_3 + if (Session.Type == GAME_NORMAL ) { +#else + if (Session.Type == GAME_NORMAL || + PlayingAgainstVersion == VERSION_RED_ALERT_104 || + PlayingAgainstVersion == VERSION_RED_ALERT_107){ +#endif + val *= House->BuildSpeedBias; + }else{ + if (What_Am_I() == RTTI_BUILDING || What_Am_I() == RTTI_INFANTRY) { + val *= House->BuildSpeedBias; + } else { + val *= House->BuildSpeedBias * fixed (UnitBuildPenalty, 100); //UNIT_BUILD_BIAS; + } + } + + + /* + ** Adjust the time to build based on the power output of the owning house. + */ + fixed power = House->Power_Fraction(); + if (power > 1) power = 1; + if (power < 1 && power > fixed::_3_4) power = fixed::_3_4; + if (power < fixed::_1_2) power = fixed::_1_2; + power.Inverse(); + val *= power; + + int divisor = House->Factory_Count(What_Am_I()); + if (divisor != 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack: allow the multiple-factory bonus, but only up to two factories if +// this is an AM<->AM game. + if(NewUnitsEnabled) { + val /= min(divisor,2); + } else { + val /= divisor; + } +#else + val /= divisor; +#endif + } + return(val); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * * + * Use this routine to determine if this object should be visible on the player's radar * + * screen. This routine doesn't take into consideration mapped terrain or whether the * + * radar is active. It merely checks to see that if all else is functioning correctly, * + * is this unit invisible or visible on the radar map. Typically, cloaked vehicles will * + * not be visible. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object nominally visible on the player's radar screen? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Visible_On_Radar(void) const +{ + /* + ** Hack: MRJ is invisible to radar, unless it's allied with the player. + */ + if (What_Am_I() == RTTI_UNIT) { + if(*((UnitClass *)this) == UNIT_MRJ) { + if(!House->Is_Ally(PlayerPtr)) { + return(false); + } + } + } + if (!Techno_Type_Class()->IsInvisible && (Cloak != CLOAKED || House->Is_Ally(PlayerPtr))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + assert(IsActive); + + if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + if (house != PlayerPtr) { + if (IsDiscoveredByComputer) return(false); + IsDiscoveredByComputer = true; + } + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (house == PlayerPtr) { + IsDiscoveredByPlayer = true; + + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (!ScenarioInit && Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + } else { + + /* + ** A newly revealed object will always perform a look operation. + */ + Look(); + } + } else { + IsDiscoveredByComputer = true; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * * + * This routine is called for every object that returns to the darkness shroud or when * + * it gets destroyed. This also occurs when an object enters another (such as infantry * + * entering a transport helicopter). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Hidden(void) +{ + assert(IsActive); + + if (!IsDiscoveredByPlayer) return; + if (!House->IsHuman) { + IsDiscoveredByPlayer = false; + } +} + + +/*********************************************************************************************** + * TechnoClass::Mark -- Handles marking of techno objects. * + * * + * On the Techno-level, marking handles transmission of the redraw command to any object * + * that it is 'connected' with. This only occurs during loading and unloading. * + * * + * INPUT: mark -- The marking method. This routine just passes this on to base classes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Mark(MarkType mark) +{ + assert(IsActive); + + if (RadioClass::Mark(mark)) { + + /* + ** When redrawing an object, if there is another object tethered to this one, + ** redraw it as well. + */ + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * * + * This routine is used to handle inbound radio messages. It only handles those messages * + * that deal with techno objects. Typically, these include loading and unloading. * + * * + * INPUT: from -- Pointer to the originator of the radio message. * + * * + * message -- The inbound radio message. * + * * + * param -- Reference to optional parameter that might be used to transfer * + * more information than is possible with the simple radio message * + * type. * + * * + * OUTPUT: Returns with the radio response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 06/17/1995 JLB : Handles tether contact messages. * + *=============================================================================================*/ +RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Just received instructions to attack the specified target. + */ + case RADIO_ATTACK_THIS: + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + Assign_Target(param); + Assign_Mission(MISSION_ATTACK); + return(RADIO_ROGER); + } + break; + + /* + ** Establish a tethered connection to the object in radio contact. + */ + case RADIO_TETHER: + if (!IsTethered) { + IsTethered = true; + Transmit_Message(RADIO_TETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** Break the tethered connection to the object in radio contact. + */ + case RADIO_UNTETHER: + if (IsTethered) { + IsTethered = false; + Transmit_Message(RADIO_UNTETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** A tethered unit has reached it's destination. All is + ** clear, so radio contact can be broken. + */ + case RADIO_UNLOADED: + Transmit_Message(RADIO_UNTETHER, from); + return(Transmit_Message(RADIO_OVER_OUT, from)); + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + Transmit_Message(RADIO_UNTETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Request to be informed when unloaded. This message is transmitted + ** by the transport unit to the transported unit as it is about to be + ** unloaded. It is saying, "All is clear. Drive off now." + */ + case RADIO_UNLOAD: + case RADIO_BACKUP_NOW: + case RADIO_HOLD_STILL: + Transmit_Message(RADIO_TETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Handle reloading one ammo point for this unit. + */ + case RADIO_RELOAD: + if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); +/*BG*/ Mark(MARK_CHANGE_REDRAW); + Ammo++; + return(RADIO_ROGER); + + /* + ** Handle repair of this unit. + */ + case RADIO_REPAIR: + /* + ** If it's a mine layer, re-arm him if he's empty. This always takes precedence + ** over repair, since this operation is free. + */ + if (What_Am_I() == RTTI_UNIT && *((UnitClass *)this) == UNIT_MINELAYER && ((UnitClass *)this)->Ammo < ((UnitClass *)this)->Class->MaxAmmo) { + ((UnitClass *)this)->Ammo = ((UnitClass *)this)->Class->MaxAmmo; + return(RADIO_NEGATIVE); + } + + /* + ** Determine if this unit can be repaired becaause it is under strength. If so, then + ** proceed with the repair process. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + int cost = Techno_Type_Class()->Repair_Cost(); + cost = max(cost, 1); + int step = Techno_Type_Class()->Repair_Step(); + step = max(step, 1); + + /* + ** If there is sufficient money to repair the unit one step, then do so. + ** Otherwise return with a "can't complete" radio response. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + /* + ** Return with either an all ok or mission accomplished radio message. This + ** lets the repairing object know if it should abort the repair control process + ** or continue it. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + return(RADIO_ROGER); + } else { + Strength = Techno_Type_Class()->MaxStrength; + return(RADIO_ALL_DONE); + } + } else { + return(RADIO_CANT); + } + } + return(RADIO_NEGATIVE); + + default: + break; + } + return(RadioClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * + * * + * This routine handles marking a game object as not a loaner. It is set only if the unit * + * is not player owned and is on the regular map. This is necessary so that enemy objects * + * can exist off-map but as soon as they move onto the map, are flagged so that can never * + * leave it again. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Handles scanner units. * + * 12/27/1994 JLB : Checks for an processes any trigger in cell. * + *=============================================================================================*/ +void TechnoClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Center_Coord()); + + /* + ** When enemy units enter the proper map area from off map, they are + ** flagged so that they won't travel back off the map again. + */ + if (!IsLocked && Map.In_Radar(cell)) { + IsLocked = true; + } + + /* + ** If this object somehow moves into mapped terrain, but is not yet + ** discovered, then flag it to be discovered. + */ + if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { + Revealed(PlayerPtr); + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * * + * This routine will draw the common elements for techno type objects. This element is * + * the health bar. The main game object has already been rendered by the time this * + * routine is called. * + * * + * INPUT: x,y -- The coordinate of the center of the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Knows about radar scanned cells. * + * 12/13/1994 JLB : Clips health bar against map edge. * + * 01/23/1995 JLB : Dynamic selected object rectangle. * + *=============================================================================================*/ +void TechnoClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Tells the door logic that it has been drawn. + */ + ((TechnoClass *)this)->Clear_Redraw_Flag(); + + if (IsSelected) { + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + + + /* + ** The infantry select box should be a bit higher than normal. + */ + if (What_Am_I() == RTTI_INFANTRY) { + y -= 6; + } + + if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { + y -= 5; + } + + /* + ** Fetch the dimensions of the object. These dimensions will be used to draw + ** the selection box and the health bar. + */ + int width,height; + Class_Of().Dimensions(width, height); + + if (Strength && (House->Is_Ally(PlayerPtr) || Rule.IsHealthBar)) { + fixed ratio = Health_Ratio(); + int pwidth; // Pixel width of bar interior. + int color; // The color to give the interior of the bargraph. + + int xx = x-width/2; + int yy = y-(height/2); + + /* + ** Draw the outline of the bargraph. + */ + draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); + draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); + + /* + ** Determine the width of the interior strength + ** graph. + */ + pwidth = (width-2) * ratio; + + pwidth = Bound(pwidth, 1, width-2); + + color = LTGREEN; + if (ratio <= Rule.ConditionYellow) { + color = YELLOW; + } + if (ratio <= Rule.ConditionRed) { + color = RED; + } + draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); + } + + /* + ** Draw the selected object graphic. + */ + if (IsSelected) { + int lx = width/2; + int ly = height/2; + int dx = width/5; + int dy = height/5; + int fudge = (House->Is_Ally(PlayerPtr) || Rule.IsHealthBar) ? 4 : 0; + if (What_Am_I() == RTTI_VESSEL) { + lx = width / 2; + } + + // Upper left corner. + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx+dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx, fudge+y-ly+dy, WHITE); + + // Upper right corner. + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx-dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx, fudge+y-ly+dy, WHITE); + + // Lower right corner. + draw_window.Draw_Line(x+lx, y+ly, x+lx-dx, y+ly, WHITE); + draw_window.Draw_Line(x+lx, y+ly, x+lx, y+ly-dy, WHITE); + + // Lower left corner. + draw_window.Draw_Line(x-lx, y+ly, x-lx+dx, y+ly, WHITE); + draw_window.Draw_Line(x-lx, y+ly, x-lx, y+ly-dy, WHITE); + if (House->Is_Ally(PlayerPtr) || (SpiedBy & (1<<(PlayerPtr->Class->House)))) { + Draw_Pips((x-lx)+5, y+ly-3, window); + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * * + * This routine handles the common operation between techno objects when they are * + * unlimboed. This includes revealing the map. * + * * + * INPUT: coord -- The coordinate to unlimbo object at. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + if (RadioClass::Unlimbo(coord, dir)) { + PrimaryFacing = dir; + Enter_Idle_Mode(true); + Commence(); + + IsLocked = Map.In_Radar(Coord_Cell(coord)); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine is used to compare the distance to the specified target with the range * + * of the weapon. If the target is outside of weapon range, then false is returned. * + * * + * INPUT: target -- The target to check if it is within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the specified target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(TARGET target, int which) const +{ + assert(IsActive); + + if (IsLocked && Target_Legal(target)) { + int range = Weapon_Range(which); + BuildingClass const * building = As_Building(target); + if (building != NULL) { + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine will determine if the pointer to the target object passed into this * + * routine is within weapon range. * + * * + * INPUT: target -- Pointer to the target object to check if within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(ObjectClass const * target, int which) const +{ + assert(IsActive); + + if (IsLocked && target) { + int range = Weapon_Range(which); + if (target->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * building = (BuildingClass const *)target; + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * * + * Use this routine to determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to examine against the object to determine range. * + * * + * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the weapon within range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/16/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(COORDINATE coord, int which) const +{ + assert(IsActive); + + return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); +} + + +/*********************************************************************************************** + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * * + * This routine scans around the cell specified and if there are any friendly buildings * + * nearby, the multiplier return value will be reduced. If there are no friendly buildings * + * nearby, then the return value will be 1. It checks to see if the primary weapon is * + * supposed to perform this scan and if so, the scan will be performed. Otherwise the * + * default value is quickly returned. * + * * + * INPUT: cell -- The cell where the potential target lies. An area around this cell will * + * be scanned for friendly buildings. * + * * + * OUTPUT: Returns with the multiplier to be multiplied by the potential target score value. * + * For less opportune targets, the multiplier fraction will be less than one. For * + * all other cases, it will return the default value of 1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Area_Modify(CELL cell) const +{ +// assert(Techno_Type_Class()->PrimaryWeapon != NULL); + if (Techno_Type_Class()->PrimaryWeapon == NULL || !Techno_Type_Class()->PrimaryWeapon->IsSupressed) return(1); + + int crange = Lepton_To_Cell(Rule.SupressRadius); + fixed odds = 1; + + for (int radius = 1; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + } + return(odds); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * * + * This routine is used to determine the score value (value as a potential target) of the * + * object specified. This routine will check the specified object for all the various * + * legality checks that threat scanning requires. This is the main workhorse routine for * + * target searching. * + * * + * INPUT: method -- The threat method requested. This is a combined bitflag value that * + * not only specifies the kind of targets to consider, but how far away * + * they are allowed to be. * + * * + * mask -- This is an RTTI mask to use for quickly eliminating object types. * + * The mask is created outside of this routine because this routine is * + * usually called from within a loop and this value is constant in that * + * context. * + * * + * range -- The range at which potential target objects are rejected. * + * 0 = must be within weapon range. * + * >0 = must be within this lepton distance. * + * <0 = range doesn't matter. * + * * + * object -- Pointer to the object itself. * + * * + * value -- Reference to the value variable that this routine will fill in. The * + * higher the value the more likely this object will be selected as best. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * + * value parameter will be filled in correctly. * + * * + * WARNINGS: This routine is time consuming. Don't call unless necessary. * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * + * 09/22/1995 JLB : Zone checking enabled. * + * 10/05/1995 JLB : Gives greater weight to designated enemy house targets. * + * 02/16/1996 JLB : Added additional threat checks. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone) const +{ + assert(IsActive); + assert(object != NULL); + + BStart(BENCH_EVAL_OBJECT); + + /* + ** An object in limbo can never be a valid target. + */ + if (object == NULL || object->IsInLimbo) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is cloaked, then it isn't a legal target. + */ + if (object->Cloak == CLOAKED) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is in a "harmless" state, then don't bother to consider it + ** a threat. + */ + if (MissionControl[object->Mission].IsNoThreat) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is not within the desired zone, then ignore it, but only if + ** zone checking is desired. + */ + COORDINATE objectcoord = object->Center_Coord(); + if (zone != -1 && Map[objectcoord].Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Friendly units are never considered a good target. Bail if this + ** object is a friend. Unless we're a medic, of course. But then, + ** only consider it a target if it's injured. + */ + if (House->Is_Ally(object)) { + if (Combat_Damage() < 0) { + if (object->Health_Ratio() == Rule.ConditionGreen) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is further away than allowed, bail. + */ + int dist = Distance(object); + if (range > 0 && dist > range) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + if (range == 0) { + int primary = What_Weapon_Should_I_Use(object->As_Target()); + if (!In_Range(object, primary)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is not visible, then bail. Human controlled units + ** are always considered to be visible. + */ + if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && Session.Type == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Quickly eliminate all unit types that are not allowed according to the mask + ** value. + */ + RTTIType otype = object->What_Am_I(); + if (!((1 << otype) & mask)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Mask failure. + } + + /* + ** Determine if the target is theoretically allowed to be a target. If + ** not, then bail. + */ + TechnoTypeClass const * tclass = object->Techno_Type_Class(); + if (!tclass->IsLegalTarget) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Legality failure. + } + + /* + ** Never consider a spy to be a valid target, unless you're a dog + */ + if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_SPY) { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsDog) { + // continue executing... + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** Special case so that SAM site doesn't fire on aircraft that are landed. + */ + if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { + if (((AircraftClass *)object)->Height == 0) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If only allowed to attack civilians, then eliminate all other types. + */ + if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the scan is limited to capturable buildings only, then bail if the examined + ** object isn't a capturable building. + */ + if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If we're a sub and the subject is a structure, bail if the structure + ** is other than a sub pen or shipyard. + */ + if (otype == RTTI_BUILDING && What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_SS) { + StructType ostruc = *(BuildingClass *)object; + if (ostruc != STRUCT_SUB_PEN && ostruc != STRUCT_SHIP_YARD) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** SPECIAL CASE: Friendly units won't automatically fire on buildings + ** if the building is not aggressive. That is, unless it is part of a team. A team + ** is allowed to pick any target it so chooses. + */ + if ((!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && + (House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && + otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#ifdef OBSOLETE + if ((!Is_Foot() || ((FootClass *)this)->Team.Is_Valid()) && House->IsHuman && otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#endif + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the search is restricted to Tiberium processing objects, then + ** perform the special qualification check now. + */ + if (method & THREAT_TIBERIUM) { + switch (otype) { + case RTTI_UNIT: + if (!((UnitTypeClass const *)tclass)->IsToHarvest) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + case RTTI_BUILDING: + if (!((BuildingTypeClass const *)tclass)->Capacity && Session.Type != GAME_NORMAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + default: + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If this target value is better than the previously recorded best + ** target value then record this target for possible return as the + ** best. + */ + int rawval = object->Value(); + value = rawval + object->Crew.Kills; + + /* + ** If the candidate object is owned by the designated enemy of this house, then + ** give it a higher value. This will tend to gravitate attacks toward the main + ** antagonist of this house. + */ + if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) { + value += 500; + value *= 3; + } + + /* + ** If the object is outside of the protective umbrella of the enemy base, then give it + ** a target boost value. + */ + if (object->House->Which_Zone(object) == ZONE_NONE) { + value *= 2; + } + + /* + ** If fake buildings are considered to be a greater target option, then boost + ** the fake building's value. + */ + if ((method & THREAT_FAKES) && otype == RTTI_BUILDING) { + switch (!((BuildingTypeClass const *)tclass)->Type) { + case STRUCT_FAKECONST: + case STRUCT_FAKEWEAP: + case STRUCT_FAKE_YARD: + case STRUCT_FAKE_PEN: + case STRUCT_FAKE_RADAR: + break; + + /* + ** Ignore all non-fake buildings. + */ + default: + value = 0; + break; + } + } + + /* + ** If power plants are to be considered a greater threat, then increase + ** their value here. Buildings that produce no power are not considered + ** a threat. + */ + if ((method & THREAT_POWER) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->Power > 0) { + value += ((BuildingTypeClass const *)tclass)->Power * 1000; + } else { + value = 0; + } + } + + /* + ** If factories are to be considered a greater threat, then don't + ** consider any non-factory building. + */ + if ((method & THREAT_FACTORIES) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->ToBuild == RTTI_NONE) { + value = 0; + } + } + + /* + ** If base defensive structures are to be considered a greater threat, then + ** don't consider an unarmed building to be a threat. + */ + if ((method & THREAT_BASE_DEFENSE) /*&& otype == RTTI_BUILDING*/) { + if (tclass->PrimaryWeapon == NULL) { + value = 0; + } + } + + /* + ** Possibly cause a reduction of the target's value if it is nearby friendly + ** structures and the primary weapon of this object is flagged for + ** friendly fire supression special check logic. + */ + fixed areamod = Area_Modify(Coord_Cell(object->Center_Coord())); + if (areamod != 1) { + value = areamod * value; + } + + /* + ** Adjust the target value upward if it is in the 'nervous zone' of the + ** owning base. This will tend to protect the base more thoroughly than + ** an unmodified scan would. + */ + if (House->Which_Zone(object) != ZONE_NONE) { + value *= Rule.NervousBias; + } + + /* + ** Lessen threat as a factor of distance. + */ + if (value) { +// if (rawval) { + + value = (value * 32000) / ((dist/ICON_LEPTON_W)+1); +// value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); + +// if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; + value = max(value, 1); + BEnd(BENCH_EVAL_OBJECT); + return(true); + } + value = 0; + BEnd(BENCH_EVAL_OBJECT); + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * * + * This routine will examine the specified cell and return with the potential target * + * object it contains and the value of it. Use this routine when searching for threats. * + * * + * INPUT: method -- The scan method to use for target searching. * + * * + * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * + * * + * range -- Scan range limit to use for elimination purposes. This ensures that * + * objects in the "corner" of a square scan get properly discarded. * + * * + * object -- Pointer to object pointer to be filled in with the object at this * + * cell as a valid target. * + * * + * value -- Reference to the value of the object in this cell. It will be set * + * according to the object's value. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Was a valid potential target found in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Zone checking enabled. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value, int zone) const +{ + assert(IsActive); + + BStart(BENCH_EVAL_CELL); + + *object = NULL; + value = 0; + + /* + ** If the cell is not on the legal map, then always ignore it. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + if (!Map.In_Radar(cell)) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + /* + ** Fetch the techno object from the cell. If there is no + ** techno object there, then bail. + */ + CellClass * cellptr = &Map[cell]; + + /* + ** Don't consider for evaluation a cell that is not within the same zone. Only + ** perform this check if zone checking is required. + */ + if (zone != -1 && cellptr->Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); + while (tentative != NULL) { + if (tentative != this) { + if (tentative->Is_Techno()) { + if (Combat_Damage() < 0) { + if (tentative->Health_Ratio() < Rule.ConditionGreen && House->Is_Ally(tentative)) break; + } else { + if (!House->Is_Ally(tentative)) break; + } + } + } + tentative = (TechnoClass const *)(ObjectClass *)tentative->Next; + } + + if (tentative == NULL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + *object = tentative; + + bool result = Evaluate_Object(method, mask, range, tentative, value); + + BEnd(BENCH_EVAL_CELL); + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * * + * This will examine the cell (as if it contained no sentient objects) and determine a * + * target value to assign to it. Typically, this is only useful for wall destroyable * + * weapons when dealing with enemy walls. * + * * + * INPUT: cell -- The cell to examine and evaluate. * + * * + * OUTPUT: Returns with the target value to assign to this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Evaluate_Just_Cell(CELL cell) const +{ + BStart(BENCH_EVAL_WALL); + + /* + ** Ships don't scan for walls. + */ + if (What_Am_I() == RTTI_VESSEL) { + return(0); + } + + /* + ** First, only computer objects are allowed to automatically scan for walls. + */ + if (House->IsHuman) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Even then, if the difficulty indicates that it shouldn't search for wall + ** targets, then don't allow it to do so. + */ + if (!Rule.Diff[House->Difficulty].IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Determine if, in fact, a wall is located at this cell location. + */ + CellClass const * cellptr = &Map[cell]; + if (cellptr->Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr->Overlay).IsWall) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** As a convenience to the target scanning logic, don't consider any wall to be + ** a target if it isn't in range of the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (!In_Range(Cell_Coord(cell), primary)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** See if the object has a weapon that can damage walls. + */ + TechnoTypeClass const * ttype = (TechnoTypeClass const *)Techno_Type_Class(); + if (ttype->PrimaryWeapon == NULL || ttype->PrimaryWeapon->WarheadPtr == NULL) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the weapon cannot deal with ground based targets, then don't consider + ** this a valid cell target. + */ + if (ttype->PrimaryWeapon->Bullet != NULL && !ttype->PrimaryWeapon->Bullet->IsAntiGround) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the primary weapon cannot destroy a wall, then don't give the cell any + ** value as a target. + */ + if (!ttype->PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If this is a friendly wall, then don't attack it. + */ + if (House->Is_Ally(cellptr->Owner)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Since a wall was found, then return a value adjusted according to the range the wall + ** is from the object. The greater the range, the lesser the value returned. + */ + BEnd(BENCH_EVAL_WALL); + return(Weapon_Range(0) - Distance(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * * + * This routine will scan game objects looking for the best target. It is used by the * + * general target searching processes. The type of target scan to perform is controlled * + * by the method control parameter. * + * * + * INPUT: method -- The method control parameter is used to control the type of target * + * scan performed. It consists of a series of bit flags (see ThreatType) * + * that are combined to form the target scan desired. * + * * + * OUTPUT: Returns the target value of a suitable target. If no target was found then the * + * value TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + * 06/20/1995 JLB : Greatly optimized scan method. * + * 09/22/1995 JLB : Takes into account the zone (if necessary). * + * 05/30/1996 JLB : Tighter elimination mask checking. * + *=============================================================================================*/ +TARGET TechnoClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + BStart(BENCH_GREATEST_THREAT); + + ObjectClass const * bestobject = NULL; + int bestval = -1; + int zone = -1; + + TargetScan++; + + /* + ** Determine the zone that the target must be in. For aircraft and gunboats, they + ** ignore zones since they either can fly over any zone or are designed to fire into + ** other zones. If scanning for targets that are within range, then zone checking need + ** not be performed -- range checking is much more thorough and effective. + */ + if (!(method & THREAT_RANGE) && + What_Am_I() != RTTI_VESSEL && + What_Am_I() != RTTI_BUILDING && + What_Am_I() != RTTI_AIRCRAFT) { + + zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + } + + /* + ** Hack for dogs, 'cause they can only consider infantrymen to be a + ** threat. Medics also. + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (((InfantryClass *)this)->Class->IsDog || Combat_Damage() < 0) { + method = THREAT_INFANTRY | (method & (THREAT_RANGE | THREAT_AREA)); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*(InfantryClass *)this == INFANTRY_MECHANIC) { + method = (THREAT_VEHICLES | THREAT_AIR) | (method & (THREAT_RANGE | THREAT_AREA)); + } +#endif + } + } + + /* + ** Build a quick elimination mask. If the RTTI of the object doesn't + ** qualify with this mask, then we KNOW that it shouldn't be considered. + */ + int mask = 0; + if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); + if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); + if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_BUILDINGS|THREAT_FACTORIES|THREAT_POWER|THREAT_FAKES|THREAT_BASE_DEFENSE|THREAT_TIBERIUM)) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_INFANTRY|THREAT_BASE_DEFENSE)) mask |= (1 << RTTI_INFANTRY); + if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); + if (method & THREAT_BASE_DEFENSE) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_BOATS) mask |= (1 << RTTI_VESSEL); + + /* + ** Limit area target scans use a method where the actual map cells are + ** examined for occupants. The occupant is then examined in turn. The + ** best target within the area is returned as a target. + */ + if (method & (THREAT_AREA|THREAT_RANGE)) { + int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); + + int crange = range / ICON_LEPTON_W; + if (range == 0) { + crange = max(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; + crange++; + } + CELL cell = Coord_Cell(Fire_Coord(0)); + + /* + ** BG: Miserable hack to get the stupid doctor to actually do area + ** guarding. + */ + if (Combat_Damage() < 0) { + /*if (method & THREAT_AREA)*/ crange++; + } + + /* + ** If aircraft are a legal target, then scan through all of them at this time. + ** Scanning by cell is not possible for aircraft since they are not recorded + ** at the cell level. + */ + if (method & THREAT_AIR) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (object->In_Which_Layer() != LAYER_GROUND && Evaluate_Object(method, mask, range, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + CELL bestcell = -1; + int bestcellvalue = 0; + TechnoClass const * object; + int value; +// int rad = 1; + + // BG: Medics need to be able to look in their own cell too. +// if (Combat_Damage() < 0 || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass*)this)->Class->IsDog)) { +// rad = 0; +// } + + for (int radius = 0; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Bail early if a target has already been found and the range is at + ** one of the breaking points (i.e., normal range or range * 2). + */ + if (bestobject != NULL) { + if (radius == crange/4) { + return(bestobject->As_Target()); + } + if (radius == crange/2) { + return(bestobject->As_Target()); + } + } + if (bestcell != -1) { + return(::As_Target(bestcell)); + } + } + + } else { + /* + ** A full map scan was requested. First scan through aircraft. The top map layer + ** is NOT scanned since that layer will probably contain more bullets and animations + ** than aircraft. + */ + if (mask & (1L << RTTI_AIRCRAFT)) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, -1, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Now scan through the entire ground layer. This is painful, but what other + ** choice is there? + */ + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; + + int value = 0; + if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value, zone)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + BEnd(BENCH_GREATEST_THREAT); + + /* + ** If a good target object was found, then return with the target value + ** of it. + */ + if (bestobject != NULL) { + return(bestobject->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Owner -- Who is the owner of this object? * + * * + * Use this routine to examine this object and return who the owner is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the house number of the owner of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +HousesType TechnoClass::Owner(void) const +{ + assert(IsActive); + + return(House->Class->House); +} + + +/*********************************************************************************************** + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * * + * Use this routine to set the flash count for the object. This flash count is the number * + * of times the object will "flash". Typically it is called as a result of the player * + * clicking on this object in order to make it the target of a move or attack. * + * * + * INPUT: count -- The number of times the object should flash. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Clicked_As_Target(int count) +{ + assert(IsActive); + + FlashCount = count; +} + + +/*********************************************************************************************** + * TechnoClass::AI -- Handles AI processing for techno object. * + * * + * This routine handles AI processing for techno objects. Typically, this merely dispatches * + * to the appropriate AI routines for the base classes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure that this routine is only called ONCE per game tick. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::AI(void) +{ + assert(IsActive); + + /* + ** Handle recoil recovery here. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE_REDRAW); + } + + CargoClass::AI(); + RadioClass::AI(); + + if (!IsActive || (Height > 0 && What_Am_I() != RTTI_AIRCRAFT)) return; + + DoorClass::AI(); + + /* + ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform + ** the heal logic here. + */ + if (Techno_Type_Class()->IsSelfHealing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0 && Health_Ratio() <= Rule.ConditionYellow) { + Strength++; + Mark(MARK_CHANGE); + } + + /* + ** Cloaking device processing. + */ + Cloaking_AI(); + + /* + ** If for some strange reason, the computer is firing upon itself, then + ** tell it not to. + */ + if (!House->IsHuman && As_Techno(TarCom) && As_Techno(TarCom)->House->Is_Ally(this)) { +//#ifdef FIXIT_CSII // checked - ajw 9/28/98 (commented out) +//if(What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this==INFANTRY_GENERAL && Session.Type==GAME_NORMAL && House->Class->House==HOUSE_UKRAINE) { +//} else +//#endif + Assign_Target(TARGET_NONE); + } + + /* + ** Perform a maintenance check to see that if somehow this object is trying to fire + ** upon an object it can never hit (because it can't reach it), then abort the tarcom + */ + if (What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && (!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && (!Is_Foot() || !Is_In_Same_Zone(As_Cell(TarCom)))) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + + /* + ** Update the animation timer system. If the animation stage + ** changes, then flag the object to be redrawn as well as determine + ** if the current animation process needs to change. + */ + if (What_Am_I() != RTTI_BUILDING) { + if (StageClass::About_To_Change()) { + Mark(MARK_CHANGE_REDRAW); + } + if (StageClass::Graphic_Logic() || Time_To_Redraw()) { + Mark(MARK_CHANGE_REDRAW); + } + } + + /* + ** If the object is flashing and a change of flash state has occurred, then mark the + ** object to be redrawn. + */ + if (FlasherClass::Process()) { + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * * + * This routine handles the cloaking device logic for this object. It will handle the * + * transition effects as the object cloaks or decloaks. It will also try to start an * + * object to cloak if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Cloaking_AI(void) +{ + /* + ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. + */ + if (IsCloakable) { + + /* + ** If this object is uncloaked, but it can be cloaked and it thinks that it + ** is a good time do so, then begin cloaking. + */ + if (Cloak == UNCLOAKED) { +#ifdef PREDATOR + if (IsOwnedByPlayer) Mark(MARK_CHANGE); +#endif + CloakingDevice.Graphic_Logic(); + if (Is_Ready_To_Cloak()) { + if (Health_Ratio() > Rule.ConditionRed) { + Do_Cloak(); + } else { + if (Percent_Chance(4)) { + Do_Cloak(); + } + } + } + } else { + + CloakingDevice.Graphic_Logic(); + switch (Cloak) { + + /* + ** Handle the uncloaking process. Always mark to redraw + ** the object and when cloaking is complete, stabilize into + ** the normal uncloaked state. + */ + case UNCLOAKING: + Mark(MARK_CHANGE); + if (Visual_Character(true) == VISUAL_NORMAL) { + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); // re-start the stage counter + Cloak = UNCLOAKED; + CloakDelay = Rule.CloakDelay * TICKS_PER_MINUTE; + Mark(MARK_CHANGE); + } + break; + + /* + ** Handle the cloaking process. Always mark to redraw the object + ** and when the cloaking process is complete, stabilize into the + ** normal cloaked state. + */ + case CLOAKING: + Mark(MARK_CHANGE); + if(!CloakingDevice.Fetch_Rate()) { + CloakingDevice.Set_Rate(1); + } + switch (Visual_Character(true)) { + + /* + ** If badly damaged, then it can never fully cloak. + */ + case VISUAL_DARKEN: + if (Health_Ratio() <= Rule.ConditionRed && Percent_Chance(25)) { + Cloak = UNCLOAKING; + } + break; + + case VISUAL_HIDDEN: + Cloak = CLOAKED; + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); + Mark(MARK_CHANGE); + + Map[Center_Coord()].Redraw_Objects(true); + Map.RadarClass::Flag_To_Redraw(true); + + /* + ** Special check to ensure that if the unit is carrying a captured + ** flag, it will never fully cloak. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + Do_Shimmer(); + } else { + Detach_All(false); + } + + /* + ** A computer controlled unit will try to scatter if possible so + ** that it will be much harder to locate. + */ + if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { + Scatter(0, true); + } + break; + } + break; + + /* + ** A cloaked object will always be redrawn if it is owned by the + ** player. This ensures that the shimmering effect will animate. + */ + case CLOAKED: +#ifdef PREDATOR + if (IsOwnedByPlayer) { + Mark(MARK_CHANGE); + } +#endif + break; + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * * + * This routine will examine this object and determine if it can and is ready and able * + * to begin cloaking. It will also check to make sure it appears to be a good time to cloak * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this unit ready and able to start cloaking? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Cloak(void) const +{ + /* + ** If it is already cloaked or in the process of cloaking, then it can't start cloaking. + */ + if (Cloak == CLOAKED || (Cloak == CLOAKING && CloakingDevice.Fetch_Rate())) { + return(false); + } + + /* + ** If the object cannot recloak, then it certainly is not allowed to start. + */ + if (!IsCloakable || !Is_Allowed_To_Recloak()) { + return(false); + } + + /* + ** If the object is currently rearming, then don't begin to recloak. + */ + if (Arm != 0) { + return(false); + } + + /* + ** If it seems like this object is about to fire on a target, then don't begin + ** cloaking either. + */ + if (Target_Legal(TarCom) && In_Range(TarCom)) { + return(false); + } + + /* + ** Recloaking can only begin if the cloaking device is not already operating. + */ + if (CloakingDevice.Fetch_Stage() != 0) { + return(false); + } + + /* + ** If the arbitrary cloak delay value is still counting down, then don't + ** allow recloaking just yet. + */ + if (CloakDelay != 0) { + return(false); + } + + /* + ** All tests passed, so this object is allowed to begin cloaking. + */ + return(true); +} + + + +/*********************************************************************************************** + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * * + * This function checks to see if this techno object can be selected. If it can, then it * + * is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Select(void) +{ + assert(IsActive); + + if (!IsDiscoveredByPlayer && !House->IsPlayerControl && !Debug_Unshroud) { + return(false); + } + + if (RadioClass::Select()) { + + /* + ** Speak a confirmation of selection. + */ + if (House->IsPlayerControl && AllowVoice) { + Response_Select(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * * + * This performs a simple check to make sure that this techno object can fire. At this * + * level, the only thing checked for is the rearming delay. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the fire legality control code. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const +{ + assert(IsActive); + + /* + ** Don't allow firing if the target is illegal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + ObjectClass * object = As_Object(target); + + /* + ** If the object is completely cloaked, then you can't fire on it. + */ + if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) { + return(FIRE_CANT); + } + + /* + ** A falling object is too busy falling to fire. + */ + if (IsFalling) { + return(FIRE_CANT); + } + + /* + ** If there is no weapon, then firing is not allowed. + */ + WeaponTypeClass const * weapon = ((which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon); + if (weapon == NULL) { + return(FIRE_CANT); + } + + /* + ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is + ** sitting on the ground. + */ + if (object != NULL && object->What_Am_I() == RTTI_AIRCRAFT && + !weapon->Bullet->IsAntiAircraft && + ((AircraftClass *)object)->Height > 0) { + + return(FIRE_CANT); + } + + /* + ** If the object is on the ground, then don't allow firing if it can't fire upon ground objects. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || (*((VesselClass*)object) != VESSEL_SS && *((VesselClass*)object) != VESSEL_MISSILESUB )) && +#else + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || *((VesselClass*)object) != VESSEL_SS) && +#endif + !weapon->Bullet->IsAntiGround) { + + return(FIRE_CANT); + } + if (Is_Target_Cell(target) && !weapon->Bullet->IsAntiGround) { + return(FIRE_CANT); + } + + /* + ** Don't allow firing if still rearming. + */ + if (Arm != 0) return(FIRE_REARM); + + /* + ** The target must be within range in order to allow firing. + */ + if (!In_Range(target, which)) { + return(FIRE_RANGE); + } + + /* + ** If there is no ammo left, then it can't fire. + */ + if (!Ammo) { + return(FIRE_AMMO); + } + + /* + ** If cloaked, then firing is disabled. + */ + if (Cloak != UNCLOAKED) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Special hack for John Archer's Hunt-The-Wumpus multiplayer mission... if +// the object firing is a cloaked civilian, don't require uncloaking before +// allowing firing. + if (What_Am_I()==RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCivilian ) { + return(FIRE_OK); + } +#endif + return(FIRE_CLOAKED); + } + + return(FIRE_OK); +} + + +/*********************************************************************************************** + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * * + * This routine handles cleaning up this techno object from the game system so that when * + * it is subsequently removed, it doesn't leave any loose ends. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Stun(void) +{ + assert(IsActive); + + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + Transmit_Message(RADIO_OVER_OUT); + Detach_All(); + Unselect(); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * * + * Use this routine to set the targeting computer for this object. It checks to make sure * + * that targeting of itself is prohibited. * + * * + * INPUT: target -- The target for this object to attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Target(TARGET target) +{ + assert(IsActive); + + if (target == TarCom) return; + + if (!Target_Legal(target)) { + target = TARGET_NONE; + } else { + + /* + ** Prevent targeting of self. + */ + if (target == As_Target()) { + target = ::As_Target(Coord_Cell(Coord)); + } else { + + /* + ** Make sure that the target is not already dead. + */ + ObjectClass * object = As_Object(target); + if (object != NULL && (object->IsActive == false || object->Strength == 0)) { + target = TARGET_NONE; + } + } + } + + /* + ** Set the unit's targeting computer. + */ + TarCom = target; +} + + +/*********************************************************************************************** + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * * + * This function calculates the delay between shots. It determines this from the standard * + * rate of fire (ROF) of the base class and modifies it according to game speed and * + * whether this is the first or second shot. All single shot attackers consider their * + * shots to be "second" since the second shot is the one handled normally. The first shot * + * usually gets assigned a much shorter delay time before the next shot can fire. * + * * + * INPUT: second -- bool; Is this the second of a two shot salvo? * + * * + * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Rearm_Delay(bool second, int which) const +{ + assert(IsActive); + + if (What_Am_I() == RTTI_BUILDING && Ammo > 1) { + return(1); + } + + WeaponTypeClass const * weapon = (which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; + if (second && weapon != NULL) { + return(weapon->ROF * House->ROFBias); + } + return(3); +} + + +/*********************************************************************************************** + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * * + * This routine is used to fire an electric zap at the target specified. * + * * + * INPUT: target -- The target to fire the zap at. * + * * + * which -- Which weapon is this zap associated with (0=primary, 1=secondary). * + * * + * source_coord -- The coordinate that the zap is to originate from. This is an * + * override value and if not specifide, the normal fire coordinate * + * is used. * + * * + * remap -- Pointer to the zap animation remap override table. If not specified * + * then the zap remains the normal blue white color. * + * * + * OUTPUT: bool; Does this object need to redraw? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 BWG : Created. * + * 09/30/1996 JLB : Uses standard facing conversion and distance routines. * + *=============================================================================================*/ +bool TechnoClass::Electric_Zap(TARGET target, int which, COORDINATE source_coord, unsigned char * remap) +{ + int x,y,x1,y1; + COORDINATE source; + + if (source_coord != 0) { + source = source_coord; + } else { + source = Fire_Coord(which); + } + COORDINATE dest = As_Coord(target); + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharging = false; + } + bool gonnadraw = false; + + if (Map.Push_Onto_TacMap(source, dest) && SpecialDialog == SDLG_NONE) { + Map.Coord_To_Pixel(source, x, y); + Map.Coord_To_Pixel(dest, x1, y1); + x += Map.TacPixelX; + x1 += Map.TacPixelX; + y += Map.TacPixelY; + y1 += Map.TacPixelY; + Set_Logic_Page(SeenBuff); + gonnadraw = true; + } + + static int _shape[]={ 2, 3, 1, 0, 2, 3, 1, 0}; + static int _xadd[8][8]={ + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8} + }; + static int _yadd[8][8]={ + {-8,-8,-8, 0, 0, 0,-8,-8}, + {-8,-8,-8, 0, 0, 0,-8,-8}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + {-8,-8,-8, 0, 0, 0,-8,-8} + }; + + int savex = x, savey = y; + if (gonnadraw) { + for (int shots = 0; shots < 3; shots++) { + x = savex; + y = savey; + int lastfacing = 0; + while (::Distance(x, y, x1, y1) > 8) { + + /* + ** Determine true (0..7) facing from current position to + ** destination (actually the source coordinate of the zap). + */ + int facing = Dir_Facing(Desired_Facing8(x, y, x1, y1)); + + /* + ** If there's quite a bit of distance to go, + ** we may vary the desired facing to give the + ** bolt some randomness. + */ + if (::Distance(x, y, x1, y1) > 40) { + switch (Sim_Random_Pick(1, 3 + ((shots==0) ? 3 : 0))) { + case 1: + facing++; + break; + + case 2: + facing--; + break; + + default: + break; + } + facing &= 7; + } + + /* + ** Now that we have the direction of the bolt, + ** draw it and move the x & y coords in the right + ** direction for the next piece. + */ + x += _xadd[facing][lastfacing]; + y += _yadd[facing][lastfacing]; + if (remap != NULL) { + CC_Draw_Shape(LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, WINDOW_TACTICAL, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap); + } else { + CC_Draw_Shape(LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL); + } + lastfacing = facing; + } + } + } + + return (gonnadraw); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * * + * This is the main projectile firing code. Buildings, units, and infantry route fire * + * requests through this function. * + * * + * INPUT: target -- The target that the projectile is to be fired at. * + * * + * which -- Which weapon to fire. * + * * + * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * + * could be created or there was some other illegality detected, the return value * + * will be NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * + * 02/22/1996 JLB : Handles camera "weapon" case. * + *=============================================================================================*/ +BulletClass * TechnoClass::Fire_At(TARGET target, int which) +{ + assert(IsActive); + + BulletClass * bullet; // Projectile. + DirType dir; // The facing to impart upon the projectile. + COORDINATE target_coord; // Coordinate of the target. + COORDINATE fire_coord; // Coordinate of firing position. + TechnoTypeClass const & tclass = *Techno_Type_Class(); + ObjectClass * object; + WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; + + /* + ** If this object doesn't have a weapon, then it is obvious that firing + ** cannot ever succeed. Return with failure flag. + */ + if (weapon == NULL) return(NULL); + + BulletTypeClass const & btype = *weapon->Bullet; + + /* + ** Perform a quick legality check to see if firing can occur. + */ + if (Debug_Map || !Target_Legal(target)) { + return(NULL); + } + + /* + ** Fetch the target coordinate for the target specified. + */ + object = As_Object(target); + if (object != NULL) { + target_coord = object->Target_Coord(); + } else { + target_coord = As_Coord(target); + } + + /* + ** Get the location where the projectile should appear. + */ + fire_coord = Fire_Coord(which); + + /* + ** If the projectile is a homing type (such as a missile), then it will + ** launch in the direction the turret is facing, NOT necessarily the same + ** direction as the target. + */ + if (btype.ROT != 0 || btype.IsDropping) { + dir = Fire_Direction(); + if (btype.IsDropping) { + fire_coord = Center_Coord(); + } + } else { + dir = ::Direction(fire_coord, target_coord); + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + int firepower = weapon->Attack; + if (firepower > 0) { + firepower = weapon->Attack * FirepowerBias * House->FirepowerBias; + } + + + /* + ** Give the bullet a boost of speed if the weapon indicates that this is required. Only + ** need to perform this check if the target is an aircraft. + */ + int firespeed = weapon->MaxSpeed; + if (weapon->IsTurboBoosted && Is_Target_Aircraft(target)) { + firespeed *= Rule.TurboBoost; + } + + bullet = new BulletClass(weapon->Bullet->Type, target, this, firepower, WarheadType(weapon->WarheadPtr->ID), firespeed); + + if (bullet != NULL) { + + /* + ** If this is firing from a moving platform, then the projectile is inaccurate. + */ + if (Is_Foot() && ((FootClass const *)this)->IsDriving) { + bullet->IsInaccurate = true; + } + + if (bullet->Unlimbo(fire_coord, dir)) { + } else { + delete bullet; + } + if (tclass.IsTurretEquipped) { + IsInRecoilState = true; + Mark(MARK_CHANGE_REDRAW); + } + + Arm = Rearm_Delay(IsSecondShot, which); + if (tclass.Is_Two_Shooter()) { + IsSecondShot = (IsSecondShot == false); + } + + /* + ** Perform any animation effect for this weapon. + */ + AnimType a = weapon->Anim; + switch (a) { + case ANIM_GUN_N: + a = AnimType(a + Dir_Facing(Fire_Direction())); + break; + + case ANIM_SAM_N: + a = AnimType(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())); + break; + } + + /* + ** Play any sound effect tied to this weapon type. + */ + Sound_Effect(weapon->Sound, Fire_Coord(which)); + + /* + ** If there is a special firing animation, then create and attach it + ** now. + */ + if (a != ANIM_NONE) { + AnimClass * anim = new AnimClass(a, Fire_Coord(which)); + if (anim != NULL) { + anim->Attach_To(this); + } + } + + /* + ** Electric zap animation. + */ + if (weapon->IsElectric) { + bool gonnadraw = Electric_Zap(target, which); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() != RTTI_INFANTRY) { + Set_Stage(0); + Set_Rate(0); + } +#else + Set_Stage(0); + Set_Rate(0); +#endif + if (Ammo <= 1 && What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharged = false; + } + + TechnoClass * tech = As_Techno(target); + if (tech != NULL) { + tech->Clicked_As_Target(4); + } + + Delay(1); // Make sure line is visible briefly + if (gonnadraw) { + Map.Flag_To_Redraw(true); + } + } + + /* + ** Reduce ammunition for this object. + */ + if (Ammo > 0) { + Ammo--; + } + + /* + ** Firing will in all likelihood, require the unit to be redrawn. Flag it to be + ** redrawn here. + */ + Mark(MARK_CHANGE); + + /* + ** If a projectile was fired from a unit that is hidden in the darkness, + ** reveal that unit and a little area around it. + ** For multiplayer games, only reveal the unit if the target is the + ** local player. + */ + if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || (!Map[Center_Coord()].IsMapped && (What_Am_I()!=RTTI_AIRCRAFT || !IsOwnedByPlayer)) ) { + if (Session.Type == GAME_NORMAL) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } else { + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + HousesType tgt_owner = obj->Owner(); + + if (PlayerPtr->Class->House == tgt_owner) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } + } + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * * + * This routine is called when the mission for an object needs to change as a result of * + * player input. The basic operation would be to queue the event and let the action * + * occur at the frame dictated by the queuing system. However, if a voice response is * + * indicated, then perform it at this time. This will give a greater illusion of * + * immediate response. * + * * + * INPUT: mission -- The mission order to assign to this object. * + * * + * target -- The target of this object. This will be used for combat and attack. * + * * + * destination -- The movement destination for this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + + if (FormMove) { + Queue_Mission(TargetClass(this), mission, target, destination, FormSpeed, FormMaxSpeed); + } else { + + /* + ** Cooerce the movement mission into a queued movement mission if the ALT key was + ** held down. + */ + if (mission == MISSION_MOVE && (Keyboard->Down(Options.KeyQueueMove1) || Keyboard->Down(Options.KeyQueueMove2))) { + mission = MISSION_QMOVE; + } + + Queue_Mission(TargetClass(this), mission, target, destination); + } +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * * + * This routine will examine the object specified and return with the action that will * + * be performed if the mouse button were clicked over the object. * + * * + * INPUT: object -- The object that the mouse button might be clicked on. * + * * + * OUTPUT: Returns with the action that will be performed if the object was clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 03/21/1995 JLB : Special target control for trees. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(ObjectClass const * object) const +{ + assert(IsActive); + + if (object != NULL) { + + /* + ** Return the ACTION_SELF flag if clicking on itself. However, if this + ** object cannot do anything special with itself, then just return with + ** the no action flag. + */ + if (object == this && CurrentObject.Count() == 1 && House->IsPlayerControl) { + return(ACTION_SELF); + } + + bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); + bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() /*KO && Can_Player_Fire()*/) { +// if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (altdown) { + if (House->IsPlayerControl && Can_Player_Move()) { + return(ACTION_MOVE); + } + } + + /* + ** Override so that toggled select state can be performed while the key + ** is held down. + */ + if (shiftdown) { + if (House->IsPlayerControl && !IsALoaner) { + return(ACTION_TOGGLE_SELECT); + } + } + + /* + ** If the weapon is blatantly disallowed from firing on the object specified, then + ** don't allow the attach check logic to proceed. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (Is_Weapon_Equipped() && + ttype->PrimaryWeapon->Bullet != NULL && + ttype->PrimaryWeapon->Bullet->IsSubSurface && + Map[object->Target_Coord()].Land_Type() != LAND_WATER) { + + // Do nothing. + + } else { + + /* + ** If firing is possible and legal, then return this action potential. + */ + if (House->IsPlayerControl && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Rule.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + + if (Is_Weapon_Equipped() || + (What_Am_I() == RTTI_INFANTRY && + (((InfantryTypeClass const *)ttype)->IsBomber || + ((InfantryTypeClass const *)ttype)->IsCapture) + )) { + + int primary = What_Weapon_Should_I_Use(object->As_Target()); + if (Can_Player_Move() || In_Range(object, primary)) { + if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable)) { + return(ACTION_ATTACK); + } else { + if (!Can_Player_Move()) { + return(ACTION_NONE); + } else { + return(ACTION_ATTACK); + } + } + } + } + } + } + + /* + ** Possibly try to select the specified object, if that is warranted. + */ + if (!Is_Weapon_Equipped() || !House->IsPlayerControl || object->Owner() == Owner()) { + if ((!IsALoaner || !IsOwnedByPlayer) && object->Class_Of().IsSelectable && !object->IsSelected) { + return(ACTION_SELECT); + } + return(ACTION_NONE); + } + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * * + * Use this routine to determine what action will be performed if the specified cell * + * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * + * nomove is used to perform special case checking for nearby cells if in fact the mouse * + * is clicked over the cell. * + * * + * INPUT: cell -- The cell to check for being clicked over. * + * * + * OUTPUT: Returns with the action that will occur if the cell is clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 07/10/1995 JLB : Force fire for buildings is explicitly disabled. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(CELL cell) const +{ + assert(IsActive); + + CellClass const * cellptr = &Map[cell]; + OverlayTypeClass const * optr = NULL; + + bool ctrldown = Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2); + bool shiftdown = Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2); + bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + + /* + ** Disable recognizing the key forced fire option when dealing with buildings. + */ + if (What_Am_I() == RTTI_BUILDING) ctrldown = false; + + /* + ** Disable recognizing the key forced fire option when dealing with submarines. + */ + if(What_Am_I() == RTTI_VESSEL) { + WeaponTypeClass const * weapon = ((VesselClass *)this)->Class->PrimaryWeapon; + if (weapon && weapon->Bullet->IsSubSurface) ctrldown = false; + } + + + if (cellptr->Overlay != OVERLAY_NONE) { + optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + } + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + if (House->IsPlayerControl && Techno_Type_Class()->PrimaryWeapon != NULL && (ctrldown || (optr && optr->IsLegalTarget))) { + WarheadTypeClass const * whead = Techno_Type_Class()->PrimaryWeapon->WarheadPtr; + +// To be fixed for firing on ore by accounting for ore and ignoring the overlay in that case. + + if (optr == NULL || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (Can_Player_Move() || In_Range(::As_Target(cell), primary)) { + return(ACTION_ATTACK); + } + } + } + + if (House->IsPlayerControl && Can_Player_Move()) { + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (shiftdown) { + return(ACTION_MOVE); + } + + /* + ** If the object can enter the cell specified, then allow + ** movement to it. + */ + if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { + return(ACTION_MOVE); + } + return(ACTION_NOMOVE); + } + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * * + * Use this routine to determine whether a movement order can be given to this object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given a movement order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Move(void) const +{ + assert(IsActive); + + return(House->IsPlayerControl); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * + * * + * Call this routine to determine if this object can be given a fire order by the player. * + * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * + * cursor to appear. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given firing orders by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Fire(void) const +{ + assert(IsActive); + + if (House->IsPlayerControl && Is_Techno() && Techno_Type_Class()->PrimaryWeapon != NULL) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * * + * Use this routine to determine if this object is equipped with a combat weapon. Such * + * determination is used by the AI system to gauge the threat potential of the object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object equipped with a combat weapon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Weapon_Equipped(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->PrimaryWeapon != NULL); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * * + * Use this routine to determine if the specified object is a candidate for repair. In * + * order to qualify, the object must be allowed to be repaired (in theory) and it must * + * be below full strength. If these conditions are met, then it can be repaired. * + * * + * INPUT: none * + * * + * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * + * is not allowed to be repaired, or it might be full strength already. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Repair(void) const +{ + assert(IsActive); + + /* + ** Temporary hack to disable repair cursor over non-buildings. + */ + if (What_Am_I() != RTTI_BUILDING) { + return(false); + } + return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); +} + + +/*********************************************************************************************** + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * * + * Use this routine to determine the maximum range for the weapon indicated. * + * * + * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the range of the weapon (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Weapon_Range(int which) const +{ + assert(IsActive); + assert((unsigned)which < 2); + + WeaponTypeClass const * weapon = NULL; + TechnoTypeClass const & ttype = *Techno_Type_Class(); + + switch (which) { + case 0: + weapon = ttype.PrimaryWeapon; + break; + + case 1: + weapon = ttype.SecondaryWeapon; + break; + } + if (weapon != NULL) { + return(weapon->Range); + } + return(0); +} + + +/*************************************************************************** + * TechnoClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override* + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedTarCom = TarCom; + RadioClass::Override_Mission(mission, tarcom, navcom); + Assign_Target(tarcom); +} + + +/*************************************************************************** + * TechnoClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool TechnoClass::Restore_Mission(void) +{ + assert(IsActive); + + if (RadioClass::Restore_Mission()) { + Assign_Target(SuspendedTarCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Renovate -- Heal a building to maximum * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1996 BWG : Created. * + *=============================================================================================*/ +void TechnoClass::Renovate(void) +{ + assert(IsActive); + + Mark(MARK_CHANGE); + Strength = Techno_Type_Class()->MaxStrength; + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->Repair(0); + } +} + + +/*********************************************************************************************** + * TechnoClass::Captured -- Handles capturing this object. * + * * + * This routine is called when this object gets captured by the house specified. It handles * + * removing this object from any targeting computers and then changes the ownership of * + * the object to the new house. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the object captured? Failure would mean that it is already under control of * + * the house specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 09/29/1995 JLB : Keeps track of quantity records. * + *=============================================================================================*/ +bool TechnoClass::Captured(HouseClass * newowner) +{ + assert(IsActive); + + if (newowner != House) { + + /* + ** Capture attempt springs any "entered" trigger. The entered trigger + ** occurs first since there may be a special trigger attached to this + ** object that flags a capture as a win and a destroy as a loss. This + ** order is necessary because the object is recorded as a kill as well. + */ + if (Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + /* + ** Record this as a kill. + */ + Record_The_Kill(NULL); + + /* + ** Special kill record logic for capture process. + */ + House->Tracking_Remove(this); + newowner->Tracking_Add(this); + switch (What_Am_I()) { + case RTTI_BUILDING: + newowner->BuildingsKilled[Owner()]++; + break; + + case RTTI_AIRCRAFT: + case RTTI_INFANTRY: + case RTTI_UNIT: + case RTTI_VESSEL: + newowner->UnitsKilled[Owner()]++; + break; + + default: + break; + } + House->WhoLastHurtMe = newowner->Class->House; + + /* + ** Remove from targeting computers. + */ + Detach_All(false); + + /* + ** Change ownership now. + */ + House = newowner; + IsOwnedByPlayer = (House == PlayerPtr); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * * + * This routine is called when this object has taken damage. It handles recording whether * + * this object has been destroyed. If it has, then mark the appropriate kill records as * + * necessary. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(IsActive); + + ResultType result = RESULT_NONE; + + /* + ** If not a forced damage condition, adjust damage according to house override armor + ** value. + */ + if (!forced && damage > 0) { + damage = damage * ArmorBias * House->ArmorBias; + } + + if (IronCurtainCountDown == 0) { + result = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + } + + switch (result) { + case RESULT_DESTROYED: + Transmit_Message(RADIO_OVER_OUT); + Stun(); + + /* + ** If this object explodes with violent damage, then perform the explosion + ** now and use the warhead type and full strength as the explosion values. + */ + if (Techno_Type_Class()->IsExploding) { + + /* + ** The warhead to use is based on the weapon this object is equipped with. + */ + WarheadType wh = WARHEAD_HE; + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + wh = WarheadType(Techno_Type_Class()->PrimaryWeapon->WarheadPtr->ID); + } + + int damage = Techno_Type_Class()->MaxStrength; + new AnimClass(Combat_Anim(damage, wh, Map[Center_Coord()].Land_Type()), Center_Coord()); + int radius = damage * Rule.ExplosionSpread; +// int radius = damage/2; + Wide_Area_Damage(Center_Coord(), radius, damage, source, wh); + } + + if (this == (TechnoClass *)::As_Object(House->UnitToTeleport)) { + House->UnitToTeleport = 0; + if (!Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + if (Map.IsTargettingMode == SPC_CHRONO2) { + KeyNumType input = KN_RMOUSE; + Map.AI(input, 0, 0); + } + } + break; + + /* + ** If some damage was received and this object is cloaked, shimmer + ** the cloak a bit. + */ + default: + if (source != NULL && !House->Is_Ally(source)) { + IsTickedOff = true; + } + Do_Shimmer(); + break; + + case RESULT_NONE: + break; + } + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * * + * This routine is used to record the death of this object. It will handle updating the * + * owner house with the kill record as well as springing any trigger events associated with * + * this object's death. * + * * + * INPUT: source -- Pointer to the source of this object's death (if there is a source). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 08/23/1995 JLB : Building loss is only counted if it received damage. * + *=============================================================================================*/ +void TechnoClass::Record_The_Kill(TechnoClass * source) +{ + assert(IsActive); + + int total_recorded = 0; + + int points = Techno_Type_Class()->Points; + + /* + ** Handle any trigger event associated with this object. + */ + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_ATTACKED, this); + + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_DISCOVERED, this); + + if (Trigger.Is_Valid()) Trigger->Spring(TEVENT_DESTROYED, this); + + if (source != NULL) { + Crew.Made_A_Kill(); + + House->WhoLastHurtMe = source->Owner(); + + /* + ** Add up the score for killing this unit + */ + source->House->PointTotal += points; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack check: if they were trying to teleport this unit when it died, take +// the map mode out of teleportation mode. + if(IsOwnedByPlayer && Map.IsTargettingMode == SPC_CHRONO2 && House->UnitToTeleport == As_Target()) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + switch (What_Am_I()) { + case RTTI_BUILDING: + { + StructType bldg = *(BuildingClass *)this; + if (bldg != STRUCT_BARREL && bldg != STRUCT_BARREL3 && + bldg != STRUCT_APMINE && bldg != STRUCT_AVMINE) { + if (((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { + House->BuildingsLost++; + } + + if (source != NULL) { + if (Session.Type == GAME_INTERNET) { + source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); + } + source->House->BuildingsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + } + } + break; + + case RTTI_AIRCRAFT: + if (source != NULL && Session.Type == GAME_INTERNET) { + source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_INFANTRY: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_UNIT: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_VESSEL: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((VesselClass*)this)->Class->Type ); + } + + House->UnitsLost++; + if (source != NULL) source->House->UnitsKilled[Owner()]++; + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + default: + break; + } + + /* + ** Since we lost an object, we lose the associated points as well. + */ + House->PointTotal -= points; +} + + +/*********************************************************************************************** + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * * + * This routine is used to find a nearby location from center of this object. It can lean * + * toward finding a location closest to an optional object. * + * * + * INPUT: object -- Optional object that the finding algorithm will try to find a close * + * spot to. * + * * + * OUTPUT: Returns with the cell that is closest to this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1995 JLB : Created. * + * 09/28/1995 JLB : Uses map scan function. * + *=============================================================================================*/ +CELL TechnoClass::Nearby_Location(TechnoClass const * techno) const +{ + assert(IsActive); + + SpeedType speed = Techno_Type_Class()->Speed; + if (speed == SPEED_WINGED) { + speed = SPEED_TRACK; + } + + CELL cell = 0; + if (techno != NULL) { + cell = Coord_Cell(techno->Center_Coord()); + } else { + cell = Coord_Cell(Center_Coord()); + } + + return(Map.Nearby_Location(cell, speed, Map[cell].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +} + + +/*********************************************************************************************** + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * * + * This routine will start the stealth tank to uncloak. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Uncloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { + if (Cloak == CLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + Cloak = UNCLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * * + * This routine will start the object into its cloaking state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Cloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { + Detach_All(false); + + if (Cloak == UNCLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + + Cloak = CLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * * + * This routine is called when this object should shimmer. If the object is cloaked, then * + * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * + * effect occurs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Shimmer(void) +{ + assert(IsActive); +#if(0) + if (IsCloakable && Cloak == CLOAKED) { + Cloak = CLOAKING; + CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); + CloakingDevice.Set_Rate(1); + } +#else + Do_Uncloak(); +#endif +} + + +/*********************************************************************************************** + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * * + * This routine will determine how this object should be drawn. Typically, this is the * + * unmodified visible state, but cloaked objects have a different character. * + * * + * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * + * object? If false, then an object owned by the player will never become * + * completely invisible. * + * * + * OUTPUT: Returns with the visual character to use when displaying this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1995 JLB : Created. * + * 05/27/1996 JLB : Knows about invisible objects. * + *=============================================================================================*/ +VisualType TechnoClass::Visual_Character(bool raw) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsInvisible && IsOwnedByPlayer) return(VISUAL_NORMAL); + if (Techno_Type_Class()->IsInvisible && !IsOwnedByPlayer && !Debug_Map) return(VISUAL_HIDDEN); + + /* + ** When uncloaked or in map editor mode, always draw the object normally. + */ + if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); + + /* + ** A cloaked unit will not be visible at all unless it is owned + ** by the player. + */ + if (Cloak == CLOAKED) { + if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + return(VISUAL_HIDDEN); + } + + int stage = CloakingDevice.Fetch_Stage(); + if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; + if (stage <= 0) { + return(VISUAL_NORMAL); + } + + stage = fixed(stage, MAX_UNCLOAK_STAGE) * 256; + + if (stage < 0x0040) return(VISUAL_INDISTINCT); + if (stage < 0x0080) return(VISUAL_DARKEN); + if (stage < 0x00C0) return(VISUAL_SHADOWY); + if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + if (stage < 0x00FF) return(VISUAL_RIPPLE); + return(VISUAL_HIDDEN); +} + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * * + * This routine is used to draw the object. It will handle any remapping or cloaking * + * effects required. This logic is isolated here since all techno object share the same * + * render logic when it comes to remapping and cloaking. * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * rotation -- The rotation of the object. * + * * + * scale -- The scaling factor to use (24.8 fixed point). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 01/11/1996 JLB : Added rotation and scaling. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const +{ + assert(IsActive); + + if (shapefile != NULL) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + void const * shadow = Map.UnitShadow; + + /* + ** Create a minimum shape rectangle if one hasn't already been + ** calculated and the shape file matches the one that the + ** class thinks it should be using. This check is necessary because + ** the dimension rectangle pointer is referenced from the type class + ** object on the presumption that the shapefile pointer passed to this + ** routine matches. If it doesn't match, then the wrong rectangle information + ** will be stored into the type class object. + */ + TechnoTypeClass * ttype = Techno_Type_Class(); + if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { + if (ttype->DimensionData == NULL) { + ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; + } + if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { + ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); + } + } + + if (Height > 0) { + shadow = Map.UnitShadowAir; + } + + y -= Lepton_To_Pixel(Height); + + /* + ** If they're viewing a spy, and the spy belongs to some other house, + ** make it look like an infantryman from our house + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (!IsOwnedByPlayer) { + if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); + } + if (((InfantryClass *)this)->Class->IsRemapOverride) { + remap = ((InfantryClass *)this)->Class->OverrideRemap; + } + } + + /* + ** Check for the special visual effect for the iron curtain + */ + if (IronCurtainCountDown > 0) { +// remap = RemapEmber; + remap = DisplayClass::FadingRed; + } + +#ifdef PREDATOR + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); + } +#else + switch (visual) { + case VISUAL_NORMAL: + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + break; + + case VISUAL_INDISTINCT: + case VISUAL_DARKEN: + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); + break; + + case VISUAL_SHADOWY: + case VISUAL_RIPPLE: + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + break; + + case VISUAL_HIDDEN: + break; + } +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * * + * This routine is used to fetch the appropriate remap table to use for this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoClass::Remap_Table(void) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsRemappable) { + return(House->Remap_Table(IsBlushing, Techno_Type_Class()->Remap)); + } + return(ColorRemaps[PCOLOR_GOLD].RemapTable); +} + + +/*********************************************************************************************** + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * * + * This routine is called when the specified object is about to be removed from the game * + * system. The target object is removed from any tracking computers that this object may * + * have. * + * * + * INPUT: target -- The target object (as a target value) that is being removed from the * + * game. * + * * + * all -- Is the target about to die? A false value might indicate that the * + * object is merely cloaking. In such a case, radio contact will not * + * be affected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + RadioClass::Detach(target, all); + + if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { + SuspendedMission = MISSION_NONE; + SuspendedTarCom = TARGET_NONE; + } + + /* + ** If the targeting computer is assigned to the target, then the targeting + ** computer must be cleared. + */ + if (TarCom == target) { + Assign_Target(TARGET_NONE); + Restore_Mission(); + } + + /* + ** If it is in radio contact with another object, then that radio contact + ** must be broken. + */ + if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { + Transmit_Message(RADIO_OVER_OUT); + } +} + + +/*********************************************************************************************** + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * * + * This routine handles the destruction of any cargo this object may contain. Typical of * + * this would be when a transport helicopter gets destroyed. * + * * + * INPUT: source -- The source of the destruction of the cargo. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Kill_Cargo(TechnoClass * source) +{ + assert(IsActive); + + while (Is_Something_Attached()) { + FootClass * foot = Detach_Object(); + if (foot != NULL) { + foot->Record_The_Kill(source); + delete foot; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * * + * This routine is called when generating survivors to this object. This routine returns * + * the type of survivor to generate. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type of a survivor. * + * * + * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * + * generate. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType TechnoClass::Crew_Type(void) const +{ + assert(IsActive); + + /* + ** If this object contains no crew, then there can be no + ** crew inside, duh... return this news. + */ + if (!Techno_Type_Class()->IsCrew) { + return(INFANTRY_NONE); + } + + /* + ** The normal infantry survivor is the standard issue + ** minigunner. Certain buildings, especially neutral ones, tend to have + ** civilians exit them instead. + */ + InfantryType infantry = INFANTRY_E1; + if (House->ActLike == HOUSE_NEUTRAL) { + infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); + } else { + if (Techno_Type_Class()->PrimaryWeapon == NULL && Percent_Chance(15)) { + if (Percent_Chance(50)) { + infantry = INFANTRY_C1; + } else { + infantry = INFANTRY_C7; + } + } + } + return(infantry); +} + + +/*********************************************************************************************** + * TechnoClass::Value -- Fetches the target value for this object. * + * * + * This routine is used to fetch the target value for this object. The greater the value * + * returned, the better this object is as a target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + * 08/16/1995 JLB : Adjusted for early mission lame-out. * + *=============================================================================================*/ +int TechnoClass::Value(void) const +{ + assert(IsActive); + + int value = 0; + + /* + ** In early missions, contents of transports are not figured + ** into the total value. + */ + if (Rule.Diff[House->Difficulty].IsContentScan || House->IQ >= Rule.IQContentScan) { + if (Is_Something_Attached()) { + FootClass * object = Attached_Object(); + + while (object != NULL) { + value += object->Value(); + object = (FootClass *)(ObjectClass *)object->Next; + } + } + } + +#ifdef TOFIX + /* + ** Increase the value of power producing object when there is power critical + ** defensive structures. + */ + if (What_Am_I() == RTTI_BUILDING && ((BuildingClass *)this)->Class->Power) { + if (House->BScan & (STRUCTF_ATOWER|STRUCTF_OBELISK)) { + value += Techno_Type_Class()->Reward; + } + } +#endif + + return Risk() + Techno_Type_Class()->Reward + value; +} + + +/*********************************************************************************************** + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * * + * This routine will return the range to scan based on the control value specified. The * + * value returned by this routine is typically used when scanning for enemies. * + * * + * INPUT: control -- The range control parameter. * + * 0 = Use weapon range (zero is returned in this special case). * + * -1 = Scan without range restrictions (-1 is returned in this case). * + * 1 = Scan up to twice weapon range. * + * * + * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * + * process. If zero is returned, then always check threat against In_Range(). If * + * -1 is returned, then no range limitation restriction exists. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Threat_Range(int control) const +{ + assert(IsActive); + + /* + ** Threat range means nothing if scanning the whole map. In such a case, just + ** return with the same control flag specified. + */ + if (control == -1) return(-1); + + /* + ** If simple guard range is requested, then return "0" since + ** this is a special control value that is calculated as the object's + ** weapon range. + */ + if (control == 0) { + /* + ** For normal guard mode or for area guard mode, use the override + ** threat range value as specified by the object's type class. + */ + if (Techno_Type_Class()->ThreatRange != 0) { + return(Techno_Type_Class()->ThreatRange); + } + return(0); + } + + /* + ** Area guard range is specified, so figure twice the weapon range of the + ** longest range weapon this object is equipped with. + */ + int range = Techno_Type_Class()->ThreatRange; + if (range == 0) { + range = max(Weapon_Range(0), Weapon_Range(1)); + } + + range *= 2; + range = Bound(range, 0x0000, 0x0A00); + + return(range); +} + + +/*********************************************************************************************** + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * * + * This will examine the specified cell to determine if it is in the same zone as this * + * object's location. * + * * + * INPUT: cell -- The cell that is to be checked against this object's current location. * + * * + * OUTPUT: bool; Is the specified cell in the same zone as this object is? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/06/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_In_Same_Zone(CELL cell) const +{ + MZoneType zone = Techno_Type_Class()->MZone; + return(Map[cell].Zones[zone] == Map[Center_Coord()].Zones[zone]); +} + + +/*********************************************************************************************** + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * * + * This routine is called when the base is being attacked. It will pull units off of the * + * field and send them back to defend the base. This routine will make taking an enemy * + * base much more difficult. * + * * + * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can drastically affect the game play. The computer will probably * + * call off its attacks as a result. * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + * 10/15/1996 JLB : Alternates between guard area and attack. * + * 11/01/1996 JLB : Allow recruit of guard area units in multiplay. * + *=============================================================================================*/ +void TechnoClass::Base_Is_Attacked(TechnoClass const * enemy) +{ + assert(IsActive); + + FootClass * defender[6]; + memset(defender, '\0', sizeof(defender)); + + int value[ARRAY_SIZE(defender)]; + memset(value, '\0', sizeof(value)); + + int count = 0; + int weakest = 0; + int desired = enemy->Risk() * House->Control.TechLevel; + int risktotal = 0; + int zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + + /* + ** Humans have to deal with their own base is attacked problems. + */ + if (enemy == NULL || House->Is_Ally(enemy) || House->IsHuman) { + return; + } + + /* + ** Don't overreact if this building can defend itself. + */ + if (Session.Type == GAME_NORMAL && Techno_Type_Class()->PrimaryWeapon != NULL) return; + + /* + ** If the enemy is not an infantry or a unit there is not much we can + ** do about it. + */ + if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT) { + return; + } + + /* + ** If we are a certain type of building, such as a barrel or land mine, + ** ignore the attack. + */ + if (Techno_Type_Class()->IsInsignificant) { + return; + } + + /* + ** If the threat has already been dealt with then we don't need to do + ** any work. Check for that here. + */ + if (enemy->Is_Foot() && ((FootClass *)enemy)->BaseAttackTimer != 0) { + return; + } + + /* + ** We will need units to defend our base. We need to suspend teams until + ** the situation has been dealt with. + */ + TeamClass::Suspend_Teams(Rule.SuspendPriority, House); + + /* + ** Loop through the infantry looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Infantry.Count() && desired > 0; index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (infantry != NULL && infantry->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!infantry->Is_Weapon_Equipped() || + (!MissionControl[infantry->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; +// (Mission != MISSION_GUARD_AREA || Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (infantry->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[infantry->Center_Coord()].Zones[infantry->Class->MZone] != Map[Center_Coord()].Zones[infantry->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = infantry->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (ArchiveTarget == As_Target()) { + threat *= 100; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = infantry; + value[count] = threat; + count++; + continue; + } + + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) infantry; + continue; + } + if (value[lp] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + /* + ** Loop through the units looking for those who are capable of going + ** on a rescue mission. + */ + for (index = 0; index < Units.Count() && desired > 0; index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && unit->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!unit->Is_Weapon_Equipped() || + (!MissionControl[unit->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (unit->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[unit->Center_Coord()].Zones[unit->Class->MZone] != Map[Center_Coord()].Zones[unit->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = unit->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (threat > 0 && ArchiveTarget == As_Target()) { + threat *= 10; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = unit; + value[count] = threat; + count++; + continue; + } + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) unit; + continue; + } + if (value[lp] < newweakest) { +// if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + if (desired > 0) { + + /* + ** Sort the defenders by value, this doesn't take very long and will + ** help the closest defenders to respond. + */ + for (int lp = 0; lp < count - 1; lp ++) { + for (int lp2 = lp + 1; lp2 < count; lp2++) { + if (value[lp] < value[lp2]) { + + value[lp] ^= value[lp2]; + value[lp2] ^= value[lp]; + value[lp] ^= value[lp2]; + + FootClass *temp; + temp = defender[lp]; + defender[lp] = defender[lp2]; + defender[lp2] = temp; + } + } + } + + for (lp = 0; lp < count; lp ++) { + if (Percent_Chance(50)) { + defender[lp]->Assign_Mission(MISSION_RESCUE); + } else { + defender[lp]->Assign_Mission(MISSION_GUARD_AREA); + defender[lp]->ArchiveTarget = As_Target(); + } + defender[lp]->Assign_Target(enemy->As_Target()); + risktotal += defender[lp]->Risk(); + if (risktotal > desired) { + break; + } + } + } + + if (risktotal > desired && enemy->Is_Foot()) { + ((FootClass *)enemy)->BaseAttackTimer = TICKS_PER_MINUTE * Rule.BaseDefenseDelay; + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * * + * This routine is called when this object has suffered some damage and it needs to know * + * if it should fight back. The object that caused the damage is specifed as a parameter. * + * * + * INPUT: source -- The points to the object that was the source of the damage applied * + * to this object. * + * * + * OUTPUT: bool; Should retaliation occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Retaliate(TechnoClass const * source) const +{ + /* + ** If there is no source of the damage, then retaliation cannot occur. + */ + if (source == NULL) return(false); + + /* + ** If the mission precludes retaliation, then don't retaliate. + */ + if (!MissionControl[Mission].IsRetaliate) return(false); + + /* + ** Fixed wing aircraft are not responsive enough to retaliate to damage recieved. + */ + if (What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)this)->Class->IsFixedWing) { + return(false); + } + + /* + ** If the source of the damage is an ally, then retaliation shouldn't + ** occur either. + */ + if (House->Is_Ally(source)) return(false); + + /* + ** Only objects that have a damaging weapon are allowed to retaliate. + */ + if (Combat_Damage() <= 0 || !Is_Weapon_Equipped()) return(false); + + /* + ** If this is not equipped with a weapon that can attack the molester, then + ** don't allow retaliation. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (ttype->PrimaryWeapon->WarheadPtr != NULL && + ttype->PrimaryWeapon->WarheadPtr->Modifier[source->Techno_Type_Class()->Armor] == 0) { + return(false); + } + + /* + ** One can never retaliate against a dog because of their peculiar nature of attacking. + ** Dogs must be attacked using normal target processing. + */ + if (source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) return(false); + + /* + ** Don't allow retaliation if it isn't equipped with a weapon that can deal with the threat. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT && !ttype->PrimaryWeapon->Bullet->IsAntiAircraft) return(false); + + /* + ** Tanya is not allowed to retaliate against buildings in the normal sense while in guard mode. That + ** is, unless it is owned by the computer. Normally, Tanya can't do anything substantial to a building + ** except to blow it up. + */ + if ((House->IsHuman || (Session.Type == GAME_NORMAL && House->IsPlayerControl)) + && source->What_Am_I() == RTTI_BUILDING && What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const *)ttype)->IsBomber) { + return(false); + } + + /* + ** If a human house is not allowed to retaliate automatically, then don't + */ + if (House->IsHuman && !Rule.IsSmartDefense && (What_Am_I() != RTTI_INFANTRY || *((InfantryClass*)this) != INFANTRY_TANYA || source->What_Am_I() != RTTI_INFANTRY)) return(false); + + /* + ** If this object is part of a team that prevents retaliation then don't allow retaliation. + */ + if (Is_Foot() && ((FootClass *)this)->Team.Is_Valid() && ((FootClass *)this)->Team->Class->IsSuicide) { + return(false); + } + + /* + ** Compare potential threat of the current target and the potential new target. Don't retaliate + ** if it is currently attacking the greater threat. + */ + if (!House->IsHuman && Percent_Chance(50)) { + fixed source_val = 0; + int primary = What_Weapon_Should_I_Use(source->As_Target()); + WeaponTypeClass const * weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(source, primary)) { + source_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + + fixed current_val = 0; + TechnoClass const * tech = As_Techno(TarCom); + if (tech != NULL) { + primary = What_Weapon_Should_I_Use(tech->As_Target()); + weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(tech, primary)) { + current_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + } + if (source_val <= current_val) return(false); + } + + /* + ** If it is already busy attacking another target, then don't retaliate. + */ +// if (In_Range(TarCom)) return(false); + + /* + ** All checks passed, so return that retaliation is allowed. + */ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * * + * This routine will return the ownable bits for this object. The ownable bits represent * + * the houses that are allowed to own this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the ownable bits for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Get_Ownable(void) const +{ + assert(IsActive); + + return ((TechnoTypeClass const &)Class_Of()).Get_Ownable(); +// return ((TechnoTypeClass const &)Class_Of()).Ownable; +} + + +/*********************************************************************************************** + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * * + * This routine is called when the risk value for this object needs to be determined. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the risk value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Risk(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->Risk); +} + + +/*********************************************************************************************** + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * * + * This routine will return the current Tiberium load (expressed as a fixed point fraction) * + * that this object currently contains. Typical implementor of this function would be * + * the harvester. Any object that can return a non-zero value should derive from this * + * function in order to return the appropriate value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * + * 0x0000 = empty * + * 0x0080 = half full * + * 0x0100 = full * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Tiberium_Load(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * * + * This routine is called when an object desires to load up on this object. The object * + * desiring to load is specified. The cell that the loading object should move to is * + * determined. The direction that this object should face is also calculated. This routine * + * will be overridden by those objects that can actually load up passengers. * + * * + * INPUT: object -- The object that is desiring to load up. * + * * + * moveto -- Reference to the cell that the loading object should move to before * + * the final load process occurs (this value will be filled in). * + * * + * OUTPUT: Returns with the direction that the transport object should face. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const +{ + assert(IsActive); + + moveto = 0; + return(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * * + * This routine will return the number of pips to display on this object when the object * + * is selected. The default condition is to return no pips at all. This routine is * + * derived for those objects that can have pips. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this object when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Pip_Count(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * * + * This routine will fetch the direction that a fired projectile will take. This is * + * usually the facing of the object's weapon. This routine will be derived for the objects * + * that have their weapon barrel facing a different direction than the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction a fired projectile will take. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Fire_Direction(void) const +{ + assert(IsActive); + + return(Turret_Facing()); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * * + * This routine is called when a voice response to a select action is desired. This routine * + * should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Select(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * * + * This routine is called when a voice response to a movement order is desired. This * + * routine should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Move(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * * + * This routine is called when a voice response to an attack order is desired. This routine * + * should be overridden for any object that actually have a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Attack(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * * + * This routine will search for a nearby target and assign it to this object's TarCom. * + * The method to use when scanning for a target is controlled by the parameter passed. * + * * + * INPUT: threat -- The threat control parameter used to control the range searched. The * + * only values recognized are THREAT_RANGE and THREAT_AREA. * + * * + * OUTPUT: Was a suitable target acquired and assigned to the TarCom? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Target_Something_Nearby(ThreatType threat) +{ + assert(IsActive); + threat = threat & (THREAT_RANGE|THREAT_AREA); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + if ((threat & THREAT_RANGE)) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + } + + /* + ** If there is no target, then try to find one and assign it as + ** the target for this unit. + */ + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** Return with answer to question: Does this unit now have a target? + */ + return(Target_Legal(TarCom)); +} + + +/*********************************************************************************************** + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * * + * This routine is called when there is an attached object that should detach and leave * + * this object. Typical of this would be the refinery and APC. * + * * + * INPUT: object -- The object that is trying to leave this object. * + * * + * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * + * there is insufficient room to detach the specified object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Exit_Object(TechnoClass *) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate. * + * * + * This will examine this object to determine if it is time and ready to perform some * + * kind of random animation. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is it time to perform an random animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Random_Animate(void) const +{ + assert(IsActive); + return(IdleTimer == 0); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. This routine must be overridden since at this level, movement is not allowed. * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Destination(TARGET ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * * + * This routine is called when the object should intelligently revert to an idle state. * + * Typically this routine is called after some mission has completed. This routine must * + * be overridden by the various object types. It is located at this level merely to provide * + * a virtual function entry point. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Enter_Idle_Mode(bool ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * * + * This routine is used to render the small transportation pip (occupant feedback graphic) * + * used for transporter object. It will also display if the techno object is "primary" * + * if necessary. * + * * + * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsequent pips * + * are drawn rightward. * + * * + * window-- The window that pip clipping is relative to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1995 JLB : Created. * + * 10/06/1995 JLB : Displays the team group number. * + * 09/10/1996 JLB : Medic hack for red pip. * + *=============================================================================================*/ +void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Transporter type objects have a different graphic representation for the pips. The + ** pip color represents the type of occupant. + */ + if (Techno_Type_Class()->Max_Passengers() > 0) { + ObjectClass const * object = Attached_Object(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + PipEnum pip = PIP_EMPTY; + + if (object != NULL) { + pip = PIP_FULL; + if (object->What_Am_I() == RTTI_INFANTRY) { + pip = ((InfantryClass *)object)->Class->Pip; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_CARRIER) { + if (object->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass *heli = (AircraftClass *)object; + if (heli->Ammo != heli->Techno_Type_Class()->MaxAmmo) { + pip = PIP_ENGINEER; + if (!heli->Ammo) { + pip = PIP_COMMANDO; + } + } + } + } +#endif + object = object->Next; + } + CC_Draw_Shape(Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + } else { + + /* + ** Display number of how many attached objects there are. This is also used + ** to display the fullness rating for a harvester. + */ + int pips = Pip_Count(); + + /* + ** Check if it's a harvester, to show the right type of pips for the + ** various minerals it could have harvested. + */ + if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_HARVESTER) { + UnitClass * harv = (UnitClass *)this; + + int iron = harv->Gems; + int nickel = harv->Gold; + int graypips = pips * fixed(iron, Rule.BailCount); + int greenpips = pips * fixed(nickel, Rule.BailCount); + + while (greenpips + graypips < pips) { + int ironnickelmax = max(iron, nickel); + if (iron > nickel) { + graypips++; + } else { + greenpips++; + } + } + + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + int shape = PIP_EMPTY; + if (index < pips) { + if (greenpips) { + shape = PIP_FULL; + greenpips--; + } else { + shape = PIP_COMMANDO; + graypips--; + } + } + CC_Draw_Shape(Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** Check if it's a Chrono tank, to show the recharge gauge. + */ + else if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_CHRONOTANK) { + for (int index = 0; index < 5; index++) { + int shape = PIP_EMPTY; + if (index < pips) { + switch(index) { + case 0: + case 1: + shape = PIP_COMMANDO; + break; + + case 2: + case 3: + shape = PIP_ENGINEER; + break; + + case 4: + shape = PIP_FULL; + default: + break; + } + } + CC_Draw_Shape(Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } +#endif + } else { + bool building = false; + int pip = PIP_FULL; // green + if(!IsOwnedByPlayer && What_Am_I() == RTTI_BUILDING) { + if(*(BuildingClass *)this==STRUCT_POWER || *(BuildingClass *)this==STRUCT_ADVANCED_POWER) { + building = true; + if (House->Power_Fraction() < 1) { + pip = PIP_ENGINEER; // gold + if (House->Drain > (House->Power * 2) ) { + pip = PIP_COMMANDO; + } + } + } + } + + for (int index = 0; index < (building ? 5 : Class_Of().Max_Pips()); index++) { + if (building) { + CC_Draw_Shape(Class_Of().PipShapes, pip, x, y-index*3, window, SHAPE_CENTER|SHAPE_WIN_REL); + } else { + CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + +//BG for (int index = 0; index < Class_Of().Max_Pips(); index++) { +//BG CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +//BG } + } + } + + /* + ** Special hack to display a red pip on the medic. + */ + if (What_Am_I() == RTTI_INFANTRY && Combat_Damage() < 0) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_MEDIC, x+8, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + /* + ** Display whether this unit is a leader unit or not. + */ + if (IsLeader) { + PipEnum prishape = PIP_PRIMARY; + + if(What_Am_I() == RTTI_BUILDING) { + if(*((BuildingClass *)this) == STRUCT_KENNEL) { + prishape = PIP_PRI; + } + } + CC_Draw_Shape(Class_Of().PipShapes, prishape, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); + } + + /* + ** Display what group this unit belongs to. This corresponds to the team + ** number assigned with the key. + */ + if (Is_Foot() && ((FootClass *)this)->Group != 0xFF && ((FootClass *)this)->Group < 10) { + int yval = -1; + int group = ((FootClass *)this)->Group+1; + + if (Class_Of().Max_Pips()) yval -= 4; + if (group == 10) group = 0; + + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS+group, x+2, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + + /* + ** If this unit is part of a formation, draw an 'F' after the group + ** number. + */ + if ( ((FootClass *)this)->XFormOffset != 0x80000000UL) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_LETTERF, x+8, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + /* + ** If this building is being spied on by the player, draw the money or + ** factory-producing item or whatever. + */ + if (What_Am_I() == RTTI_BUILDING) { + int spiedby = SpiedBy & (1<<(PlayerPtr->Class->House)); + + /* + ** If it's an ore refinery or other such storage-capable building, + ** loop thru all of their buildings to see if ANY of them are spied + ** upon, 'cause once you spy any money, you've spied all of it. + */ + if (((BuildingClass *)this)->Class->Capacity) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building->House == House && building->Class->Capacity) { + spiedby |= (building->SpiedBy) & (1<<(PlayerPtr->Class->House)); + } + } + } + + /* + ** Print word "Decoy" above buildings that are spied upon or fake + */ + if (((BuildingClass *)this)->Class->IsFake) { + if (spiedby || IsOwnedByPlayer) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_DECOY, x, y-16, window, SHAPE_WIN_REL); + } + } + /* + ** See if we should print the credits for a spied refinery + */ + if (spiedby) { + // If it's a refinery/silo, print the enemy's money + if (((BuildingClass *)this)->Class->Capacity) { + long money = House->Available_Money(); + + /* + ** Determine how many digits will be printed. + */ + int digits; + int factor = 10; + for (digits = 1; digits < 9; digits++) { + if (money < factor) break; + factor *= 10; + } + + int startx = x + 6 * digits - 3;// + 6 * 8; + while (money) { + int xdigit = money % 10; + money /= 10; + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS + xdigit, startx, y-6, window, SHAPE_CENTER|SHAPE_WIN_REL); + startx -= 6; + } + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * + *=============================================================================================*/ +BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + assert(IsActive); + + BuildingClass * best = 0; + + /* + ** First check to see if there are ANY buildings of the specified + ** type in this house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->Get_Quantity(b) != 0) { + int bestval = -1; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (building != NULL && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + !building->IsInLimbo && + *building == b && + (What_Am_I() == RTTI_AIRCRAFT || Map[building->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) && + ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { + best = building; + bestval = Distance(building); + } + } + } + } + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * * + * This routine is called when an object would like to exit from this (presumed) transport. * + * A suitable cell should be returned by this routine. The specified object will probably * + * be unloaded at that cell. * + * * + * INPUT: techno -- Pointer to the object that would like to unload. This is used to * + * determine suitability for placement. * + * * + * OUTPUT: Returns with the cell that is recommended for object exit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const +{ + assert(IsActive); + + return(Coord_Cell(Docking_Coord())); +} + + +/*********************************************************************************************** + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * * + * This routine is used by the selling back mechanism in order to credit the owning house * + * with some refund credits. The value returned is the credits to refund to the owner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credits to refund to the owner. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Refund_Amount(void) const +{ + assert(IsActive); + + int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias; + +#ifdef TOFIX + /* + ** If the object is carrying Tiberium directly (i.e., the harvester), then + ** account for the credits of the load. + */ +// cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; +#endif + + if (House->IsHuman) { + cost = cost * Rule.RefundPercent; +// cost /= 2; + } + return(cost); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * * + * This routine will calculate and return the anti-aircraft strength of this object. * + * Typical users of this strength value is the base defense expert system AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned * + * is an abstract number to be used for relative comparisons only. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Air(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + + if (bullet->IsAntiAircraft) { + int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF; + + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + return(value/50); + } + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * * + * This routine is used to examine and calculate the anti-armor strength of this object. * + * Typical user user of this would be the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the relative anti-armor combat value for this object. The value * + * is abstract and is only to be used in relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Armor(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * * + * This routine is used to determine the anti-infantry strength of this object. The * + * typical user of this routine is the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is * + * abstract and should only be used for relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Infantry(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * * + * This routine will reveal the map around this object. * + * * + * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * + * If it is absolutely known that the object has only moved one * + * cell from its previous location that it performed a Look() at, * + * then set this parameter to TRUE. It will only perform the look * + * check on the perimeter cells. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is slow, try to call it only when necessary. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + + if (sight_range) { + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + } +} + + +//********************************************************************************************** +// MODULE SEPARATION -- TechnoTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * * + * This is the normal constructor for techno type objects. It is called in the process of * + * constructing all the object type (constant) data for the various techno type objects. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 05/11/1996 JLB : Moderated risk calc so range doesn't dominate. * + *=============================================================================================*/ +TechnoTypeClass::TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed) : + ObjectTypeClass( rtti, + id, + true, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_footprint, + name, + ininame), + Remap(remap), + IsDoubleOwned(false), + IsInvisible(false), + IsLeader(false), + IsScanner(false), + IsNominal(is_nominal), + IsTheater(is_theater), + IsTurretEquipped(is_turret_equipped), + IsCrew(false), + IsRepairable(true), + IsRemappable(is_remappable), + IsCloakable(false), + IsSelfHealing(false), + IsExploding(false), + MZone(MZONE_NORMAL), + ThreatRange(0), + MaxPassengers(0), + SightRange(0), + Cost(0), + Level(-1), + Prerequisite(STRUCTF_NONE), + Risk(0),Reward(0), + MaxSpeed(MPH_IMMOBILE), + Speed(speed), + MaxAmmo(-1), + Ownable(0), + CameoData(NULL), + Rotation(rotation), + ROT(0), + PrimaryWeapon(NULL), + SecondaryWeapon(NULL), + VerticalOffset(verticaloffset), + PrimaryOffset(primaryoffset), + PrimaryLateral(primarylateral), + SecondaryOffset(secondaryoffset), + SecondaryLateral(secondarylateral), + Points(0) +{ +} + + +/*********************************************************************************************** + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * * + * This routine is used to find the underlying cost for this object. The underlying cost * + * does not include any free items that normally come with the object when purchased * + * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * + * harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost of the base object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Raw_Cost(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * * + * This routine will return the ownable bits for this object type. The ownable bits are * + * a bitflag composite of the houses that can own (build) this object type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable bits for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Get_Ownable(void) const +{ + if (IsDoubleOwned && Session.Type != GAME_NORMAL) { + return(Ownable | HOUSEF_SOVIET | HOUSEF_ALLIES); + } + return(Ownable); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * * + * This routine will return the time it takes to construct this object. Usually the time * + * to produce is directly related to cost. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * + * form of game ticks. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Time_To_Build(void) const +{ + return(Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000)); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * * + * This routine will return the cost to produce an object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce one object of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Cost_Of(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * * + * This routine will fetch the cameo (sidebar small image) shape of this object type. * + * If there is no cameo data available (typical for non-produceable units), then NULL will * + * be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoTypeClass::Get_Cameo_Data(void) const +{ + return(CameoData); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * * + * This routine will return the cost to repair one step. At the TechnoTypeClass level, * + * this merely serves as a placeholder function. The derived classes will provide a * + * functional version of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to repair one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Cost(void) const +{ + if (Is_Foot()) { + return((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); + } + return((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * * + * This routine merely serves as placeholder virtual function. The various type classes * + * will override this routine to return the number of health points to repair in one * + * "step". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to repair in one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Step(void) const +{ + if (Is_Foot()) { + return(Rule.URepairStep); + } + return(Rule.RepairStep); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * * + * Some objects fire two shots in quick succession. If this is true for this object, then * + * a 'true' value will be returned from this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object a two shooter? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Is_Two_Shooter(void) const +{ + if (PrimaryWeapon != NULL && (PrimaryWeapon == SecondaryWeapon || PrimaryWeapon->Burst > 1)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * * + * Use this routine to fill in the data for this techno type class object from the * + * database specified. Typical use of this is for the rules parsing. * + * * + * INPUT: ini -- Reference to the INI database that the information will be lifted from. * + * * + * OUTPUT: bool; Was the database used to extract information? A failure (false) response * + * would mean that the database didn't contain a section that applies to this * + * techno class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { +#ifdef FIXIT_NAME_OVERRIDE + char buffer[256]; + int id = ((RTTI+1) * 100) + ID; + + ini.Get_String(Name(), "Name", "", buffer, sizeof(buffer)); + if (strlen(buffer) > 0) { + +#if defined (GERMAN)||(FRENCH) + + for(int xx=0; NewName[xx] != NULL; xx++){ + if(!strcmp(NewName[xx], buffer)){ + memcpy(buffer, "", sizeof(buffer)); + memcpy(buffer, NewName[xx+1], sizeof(buffer)); + break; + } + } +#endif + + /* + ** Insert the new name text into the buffer list. + */ + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == 0) { + NameOverride[index] = strdup(buffer); + NameIDOverride[index] = id; +// FullName = -(index+1); + break; + } + } + } +#endif + + IsDoubleOwned = ini.Get_Bool(Name(), "DoubleOwned", IsDoubleOwned); + ThreatRange = ini.Get_Lepton(Name(), "GuardRange", ThreatRange); + IsExploding = ini.Get_Bool(Name(), "Explodes", IsExploding); + PrimaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Primary", PrimaryWeapon != NULL ? (WeaponType)(PrimaryWeapon->ID) : WEAPON_NONE)); + SecondaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Secondary", SecondaryWeapon != NULL ? (WeaponType)(SecondaryWeapon->ID) : WEAPON_NONE)); + IsCloakable = ini.Get_Bool(Name(), "Cloakable", IsCloakable); + IsCrushable = ini.Get_Bool(Name(), "Crushable", IsCrushable); + IsScanner = ini.Get_Bool(Name(), "Sensors", IsScanner); + Armor = ini.Get_ArmorType(Name(), "Armor", Armor); + Prerequisite = ini.Get_Buildings(Name(), "Prerequisite", Prerequisite); + MaxStrength = ini.Get_Int(Name(), "Strength", MaxStrength); + SightRange = ini.Get_Int(Name(), "Sight", SightRange); + Level = ini.Get_Int(Name(), "TechLevel", Level); + MaxSpeed = MPHType(_Scale_To_256(ini.Get_Int(Name(), "Speed", fixed(MaxSpeed, 256) * 100))); + Cost = ini.Get_Int(Name(), "Cost", Cost); + MaxAmmo = ini.Get_Int(Name(), "Ammo", MaxAmmo); + Risk = Reward = Points = ini.Get_Int(Name(), "Points", Points); + Ownable = ini.Get_Owners(Name(), "Owner", Ownable); + IsCrew = ini.Get_Bool(Name(), "Crewed", IsCrew); + IsRepairable = ini.Get_Bool(Name(), "Repairable", IsRepairable); + IsInvisible = ini.Get_Bool(Name(), "Invisible", IsInvisible); + IsSelfHealing = ini.Get_Bool(Name(), "SelfHealing", IsSelfHealing); + ROT = ini.Get_Int(Name(), "ROT", ROT); + MaxPassengers = ini.Get_Int(Name(), "Passengers", MaxPassengers); +//Mono_Printf("before image=: %s\n",GraphicName); + ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); +//Mono_Printf("after image=: %s\n",GraphicName);if(Random_Pick(0,4)) Keyboard->Get(); + + IsLeader = false; + if (PrimaryWeapon != NULL && PrimaryWeapon->Attack > 0) { + IsLeader = true; + } + + /* + ** Check to see what zone this object should recognize. + */ + if (PrimaryWeapon != NULL && PrimaryWeapon->WarheadPtr != NULL && PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + MZone = MZONE_DESTROYER; + } + if (Speed == SPEED_FLOAT) { + MZone = MZONE_WATER; + } + + return(true); + } + return(false); +} + + +int TechnoTypeClass::Legal_Placement(CELL pos) const +{ + if (pos == -1) return(0); + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + short const * offset = Occupy_List(true); + bool build = (What_Am_I() == RTTI_BUILDINGTYPE); + + while (offset != NULL && *offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + if (build) { + if (!Map[cell].Is_Clear_To_Build(Speed)) { + return(0); + } + } else { + if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) { + return(0); + } + } + } + return(1); +} diff --git a/CODE/TECHNO.H b/CODE/TECHNO.H new file mode 100644 index 0000000..7c9bbe2 --- /dev/null +++ b/CODE/TECHNO.H @@ -0,0 +1,389 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TECHNO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TECHNO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TECHNO_H +#define TECHNO_H + +#include "radio.h" +#include "stage.h" +#include "cargo.h" +#include "flasher.h" +#include "house.h" +#include "target.h" +#include "bullet.h" +#include "door.h" +#include "crew.h" + +/**************************************************************************** +** This is the common data between building and units. +*/ +class TechnoClass : public RadioClass, + public FlasherClass, + public StageClass, + public CargoClass, + public DoorClass +{ + public: + CrewClass Crew; + + /* + ** If this techno object has detected that it has outlived its + ** purpose, then this flag will be true. Such object will either + ** be sold or sacrificed at the first opportunity. + */ + unsigned IsUseless:1; + + /* + ** This flag will be true if the object has been damaged with malice. + ** Damage received due to friendly fire or wear and tear does not count. + ** The computer is not allowed to sell a building unless it has been + ** damaged with malice. + */ + unsigned IsTickedOff:1; + + /* + ** If this object has inherited the ability to cloak, then this bit will + ** be set to true. + */ + unsigned IsCloakable:1; + + /* + ** If this object is designated as special then this flag will be true. For + ** buildings, this means that it is the primary factory. For units, it means + ** that the unit is the team leader. + */ + unsigned IsLeader:1; + + /* + ** Certain units are flagged as "loaners". These units are typically transports that + ** are created solely for the purpose of delivering reinforcements. Such "loaner" + ** units are not owned by the player and thus cannot be directly controlled. These + ** units will leave the game as soon as they have fulfilled their purpose. + */ + unsigned IsALoaner:1; + + /* + ** Once a unit enters the map, then this flag is set. This flag is used to make + ** sure that a unit doesn't leave the map once it enters the map. + */ + unsigned IsLocked:1; + + /* + ** Buildings and units with turrets usually have a recoil animation when they + ** fire. If this flag is true, then the next rendering of the object will be + ** in the "recoil state". The flag will then be cleared pending the next + ** firing event. + */ + unsigned IsInRecoilState:1; + + /* + ** If this unit is "loosely attached" to another unit it is given special + ** processing. A unit is in such a condition when it is in the process of + ** unloading from a transport type object. During the unloading process + ** the transport object must stay still until the unit is free and clear. + ** At that time it radios the transport object and the "tether" is broken - + ** freeing both the unit and the transport object. + */ + unsigned IsTethered:1; + + /* + ** Is this object owned by the player? If not, then it is owned by the computer + ** or remote opponent. This flag facilitates the many logic differences when dealing + ** with player's or computer's units or buildings. + */ + unsigned IsOwnedByPlayer:1; + + /* + ** The more sophisticated game objects must keep track of whether they are discovered + ** or not. This is because the state of discovery can often control how the object + ** behaves. In addition, this fact is used in radar and user I/O processing. + */ + unsigned IsDiscoveredByPlayer:1; + + /* + ** This is used to control the computer recognizing this object. + */ + unsigned IsDiscoveredByComputer:1; + + /* + ** Some game objects can be of the "lemon" variety. This means that they take damage + ** even when everything is ok. This adds a little variety to the game. + */ + unsigned IsALemon:1; + + /* + ** This flag is used to control second shot processing for those units or buildings + ** that fire two shots in quick succession. When this flag is true, it indicates that + ** the second shot is ready to fire. After this shot is fired, regular rearm timing + ** is used rather than the short rearm time. + */ + unsigned IsSecondShot:1; + + /* + ** This is the firepower and armor modifiers for this techno object. Normally, + ** these values are fixed at 0x0100, but they can be modified by certain + ** crate powerups. + */ + fixed ArmorBias; + fixed FirepowerBias; + + /* + ** Idle animations (if any are supported by the object type) are regulated by + ** this timer. When the timer expires an idle animation occurs. Then the + ** timer is reinitialized to some random (bounded) setting. + */ + CDTimerClass IdleTimer; + + /* + ** This timer keeps track of how long the unit is under the influence + ** of the iron curtain. + */ + CDTimerClass IronCurtainCountDown; + + + /* + ** This is a list of bits of which houses are spying on this building, + ** if in fact this is a building. + */ + unsigned SpiedBy; + + /* + ** For units in area guard mode, this is the recorded home position. The guarding + ** unit will try to stay near this location in the course of it's maneuvers. This is + ** also used to record a pending transport for those passengers that are waiting for + ** the transport to become available. It is also used by harvesters so that they know + ** where to head back to after unloading. + */ + TARGET ArchiveTarget; + + /* + ** This is the house that the unit belongs to. + */ + CCPtr House; + + /* + ** This records the current cloak state for this vehicle. + */ + CloakType Cloak; + StageClass CloakingDevice; + CDTimerClass CloakDelay; + + /* (Targeting Computer) + ** This is the target value for the item that this vehicle should ATTACK. If this + ** is a vehicle with a turret, then it may differ from its movement destination. + */ + TARGET TarCom; + TARGET SuspendedTarCom; + + /* + ** This is the visible facing for the unit or building. + */ + FacingClass PrimaryFacing; + + /* + ** This is the arming countdown. It represents the time necessary + ** to reload the weapon. + */ + CDTimerClass Arm; + + /* + ** The number of shot this object can fire before running out of ammo. If this + ** value is zero, then firing is not allowed. If -1, then there is no ammunition + ** limit. + */ + int Ammo; + + /* + ** This is the amount of money spent to produce this object. This value really + ** only comes into play for the case of buildings that have special "free" + ** objects available when purchased at the more expensive rate. + */ + int PurchasePrice; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TechnoClass(RTTIType rtti, int id, HousesType house=HOUSE_NONE); +#ifdef FIXIT_MULTI_SAVE + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IdleTimer(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#else + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#endif + virtual ~TechnoClass(void) {House=0;}; + + /* + ** Query functions. + */ + bool Is_Allowed_To_Retaliate(TechnoClass const * source) const; + bool Can_Teleport_Here(CELL cell) const; + bool Is_In_Same_Zone(CELL cell) const; + virtual bool Is_Players_Army(void) const; + int Combat_Damage(int which=-1) const; + bool Is_Ready_To_Cloak(void) const; + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const {return(PrimaryFacing.Current());} + CELL Nearby_Location(TechnoClass const * from=NULL) const; + TechnoTypeClass * Techno_Type_Class(void) const {return((TechnoTypeClass *)&Class_Of());}; + bool Is_Visible_On_Radar(void) const; + int Anti_Air(void) const; + int Anti_Armor(void) const; + int Anti_Infantry(void) const; + int Time_To_Build(void) const; + int What_Weapon_Should_I_Use(TARGET target) const; + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual BuildingClass * Find_Docking_Bay(StructType b, bool friendly) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual DirType Desired_Load_Dir(ObjectClass * , CELL & moveto) const; + virtual DirType Fire_Direction(void) const; + virtual HousesType Owner(void) const; + virtual InfantryType Crew_Type(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + virtual bool Is_Allowed_To_Recloak(void) const; + virtual bool Can_Repair(void) const; + virtual bool Is_Weapon_Equipped(void) const; + virtual fixed Tiberium_Load(void) const; + virtual int Pip_Count(void) const; + virtual int Rearm_Delay(bool second=true, int which=0) const; + virtual int Refund_Amount(void) const; + virtual int Risk(void) const; + virtual int Threat_Range(int control) const; + virtual int Value(void) const; + virtual int Get_Ownable(void) const; + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(int count=7); + virtual bool Select(void); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Player_Assign_Mission(MissionType order, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + + /* + ** Combat related. + */ + fixed Area_Modify(CELL cell) const; + virtual int Made_A_Kill(void) {return(Crew.Made_A_Kill());} + void Base_Is_Attacked(TechnoClass const *enemy); + void Kill_Cargo(TechnoClass * source); + virtual void Record_The_Kill(TechnoClass * source); + virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual void Stun(void); + virtual bool In_Range(COORDINATE coord, int which=0) const; + virtual bool In_Range(TARGET target, int which=0) const; + virtual bool In_Range(ObjectClass const * target, int which=0) const; + virtual void Death_Announcement(TechnoClass const * source=0) const = 0; + virtual FireErrorType Can_Fire(TARGET target, int which=0) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual void Assign_Target(TARGET target); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + virtual BulletClass * Fire_At(TARGET target, int which=0); + virtual int Weapon_Range(int which) const; + virtual bool Captured(HouseClass * newowner); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value, int zone=0) const; + bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone=-1) const; + int Evaluate_Just_Cell(CELL cell) const; + virtual bool Electric_Zap (TARGET target, int which, COORDINATE target_coord=0L, unsigned char * remap=NULL); + + /* + ** AI. + */ + virtual void Renovate(void); + virtual void AI(void); + virtual bool Revealed(HouseClass * house); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + void Cloaking_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void) const; + VisualType Visual_Character(bool raw = false) const; + void Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation=DIR_N, int scale=0x0100) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Draw_Pips(int x, int y, WindowNumberType window) const; + virtual void Hidden(void); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual int Exit_Object(TechnoClass *); + virtual void Do_Uncloak(void); + virtual void Do_Cloak(void); + virtual void Do_Shimmer(void); + + /* + ** Movement and animation. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + virtual bool Random_Animate(void) {return(false);} + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Look(bool incremental=false); + + /* + ** Map entry and exit logic. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual void Detach(TARGET target, bool all); + + /* + ** Facing translation tables that fix the flaw with 3D studio when + ** it renders 45 degree angles. + */ + static int const BodyShape[32]; +}; + +#endif diff --git a/CODE/TEMP.CPP b/CODE/TEMP.CPP new file mode 100644 index 0000000..5497ee0 --- /dev/null +++ b/CODE/TEMP.CPP @@ -0,0 +1,114 @@ +/* +** 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 . +*/ + +THIS IS FROM SCORE.CPP + +line 79 +#ifdef FIXIT_SCORE_CRASH +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); +#endif //FIXIT + + +line 361 +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Disable_Uncompressed_Shapes(); +#endif //FIXIT + + + +LINE 844 +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Enable_Uncompressed_Shapes(); +#endif //FIXIT + + +end of score + +********************** + + +keyframe.cpp + +line 116 +#ifdef FIXIT_SCORE_CRASH + char * ptr; + unsigned long offcurr, offdiff; +#else + char * ptr, * lockptr; + unsigned long offcurr, off16, offdiff; +#endif + + +line 184 +#ifndef FIXIT_SCORE_CRASH + off16 = (unsigned long)lockptr & 0x00003FFFL; +#endif + + +line 194 +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + +line 228 +#ifndef FIXIT_SCORE_CRASH + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } +#endif + + +end keyframe +********************** + + +2keyframe.cpp + +line 53 +#ifdef FIXIT_SCORE_CRASH +/* +** Global required to fix the score screen crash bug by allowing disabling of uncompressed shapes. +*/ +bool OriginalUseBigShapeBuffer = false; +#endif //FIXIT + +end 2keyframe.cpp + + + + diff --git a/CODE/TEMPLATE.CPP b/CODE/TEMPLATE.CPP new file mode 100644 index 0000000..a89e698 --- /dev/null +++ b/CODE/TEMPLATE.CPP @@ -0,0 +1,241 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEMPLATE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEMPLATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateClass::Init -- Resets the template object system. * + * TemplateClass::Mark -- Lifts or drops a template object. * + * TemplateClass::Select -- Select the template object. * + * TemplateClass::TemplateClass -- Template object constructor. * + * TemplateClass::Unlimbo -- Places a template object into the game/map system. * + * TemplateClass::delete -- Returns a template object to the pool. * + * TemplateClass::new -- Allocates a template object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "template.h" + + +/*********************************************************************************************** + * TemplateClass::Init -- Resets the template object system. * + * * + * This routine resets the template object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Init(void) +{ + Templates.Free_All(); +} + + +/*********************************************************************************************** + * TemplateClass::Mark -- Lifts or drops a template object. * + * * + * This routine handles placing or removing a template object. This * + * entails marking the map as appropriate and redisplaying affected * + * cells. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the template successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + * 12/23/1994 JLB : Examines low level legality before processing. * + *=============================================================================================*/ +bool TemplateClass::Mark(MarkType mark) +{ + assert(Templates.ID(this) == ID); + assert(IsActive); + + static bool noup = false; + void const * iset = Get_Image_Data(); + if (iset && ObjectClass::Mark(mark)) { + + void * map = Get_Icon_Set_Map(iset); + + for (int y = 0; y < Class->Height; y++) { + for (int x = 0; x < Class->Width; x++) { + CELL cell = Coord_Cell(Coord) + y*MAP_CELL_W + x; + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + int number = y*Class->Width + x; + + /* + ** Determine if this logical icon actually maps to a real icon. If no real + ** icon is associated with this logical position, then don't do any action + ** since none is required. + */ + char * mapptr = (char*)map; + bool real = (mapptr[number] != -1); + + if (real) { + /* + ** Lift the terrain object from the map. + */ + if (mark == MARK_UP && !noup) { + if (cellptr->TType == Class->Type && cellptr->TIcon == number) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } + } + + /* + ** Place the terrain object down. + */ + if (mark == MARK_DOWN) { + if (*this == TEMPLATE_CLEAR1) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } else { + cellptr->TType = Class->Type; + cellptr->TIcon = number; + } + + /* + ** Make sure that no overlays or smudges exist after + ** placing the template down. + */ + cellptr->Smudge = SMUDGE_NONE; + cellptr->SmudgeData = 0; + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + } + + cellptr->Redraw_Objects(); + cellptr->Recalc_Attributes(); + } + } + } + } + + /* + ** When marking this template down onto the map, the map template numbers are update + ** but the template is removed from existence. Make sure that the deletion of the + ** template object doesn't also lift the template numbers up from the map. + */ + if (mark == MARK_DOWN) { + noup = true; + delete this; + noup = false; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateClass::new -- Allocates a template object from pool * + * * + * This routine is used to allocate a template object from the * + * template object pool. * + * * + * INPUT: size -- The size of a template object (not used). * + * * + * OUTPUT: Returns with a pointer to an available template object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * TemplateClass::operator new(size_t ) +{ + void * ptr = Templates.Allocate(); + if (ptr) { + ((TemplateClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TemplateClass::delete -- Returns a template object to the pool. * + * * + * This routine will return a template object to the template object * + * pool. A template so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::operator delete(void * ptr) +{ + if (ptr) { + ((TemplateClass *)ptr)->IsActive = false; + } + Templates.Free((TemplateClass *)ptr); +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Template object constructor. * + * * + * This is the constructor for a template object. * + * * + * INPUT: type -- The template object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(TemplateType type, CELL pos) : + ObjectClass(RTTI_TEMPLATE, Templates.ID(this)), + Class(TemplateTypes.Ptr((int)type)) +{ + if (pos != -1) { + Unlimbo(Cell_Coord(pos)); + } +} diff --git a/CODE/TEMPLATE.H b/CODE/TEMPLATE.H new file mode 100644 index 0000000..12e459f --- /dev/null +++ b/CODE/TEMPLATE.H @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEMPLATE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEMPLATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This class controls the template object. Template objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class TemplateClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TemplateClass(TemplateType type, CELL pos=-1); + TemplateClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~TemplateClass(void) {if (GameActive) TemplateClass::Limbo();Class=0;}; + operator TemplateType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + int Icon_Number(CELL cell); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int , int , WindowNumberType ) const {}; + virtual bool Mark(MarkType mark); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; +}; + +#endif diff --git a/CODE/TENMGR.CPP b/CODE/TENMGR.CPP new file mode 100644 index 0000000..e9db9b8 --- /dev/null +++ b/CODE/TENMGR.CPP @@ -0,0 +1,1121 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : July 22, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TenConnManClass::TenConnManClass -- Class constructor * + * TenConnManClass::~TenConnManClass -- Class destructor * + * TenConnManClass::Init -- Inits TEN * + * TenConnManClass::Service -- Service routine * + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * TenConnManClass::Get_Global_Message -- Gets next global message * + * TenConnManClass::Num_Connections -- Reports # connections * + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * TenConnManClass::Connection_Index -- Gets a connection's index * + * TenConnManClass::Create_Connection -- Creates a new connection * + * TenConnManClass::Delete_Connection -- Deletes a connection * + * TenConnManClass::Connection_Name -- Reports a connection's name * + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Flush_All -- Flushes all packets * + * TenConnManClass::Reset_Response_Time -- Does nothing * + * TenConnManClass::Response_Time -- Reports response time * + * TenConnManClass::Set_Timing -- Does nothing * + * TenConnManClass::Configure_Debug -- Does nothing * + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * terminateApp -- Callback: app terminates on error * + * debugMessage -- outputs debug message * + * doAlert -- Outputs debug message * + * doPregameHook -- Callback: game is starting * + * doIncomingPacket -- Callback: packet has arrived * + * doPlayerJoins -- Callback: player joins * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WIN32 +#define WINDOWS +#endif + +#if(TEN) +#include "ten.h" + +//************************************************************************** +// Constants +// +const unsigned char kGlobalChannelFlag = 0x80; +const int kMaxPlayers = 8; + +//************************************************************************** +// Functions +// +static void terminateApp(void); +static void debugMessage(int msgLevel, char *msg); +static void doAlert(int, int, char *); +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *); +void doIncomingPacket(int addr, void *buf, size_t size); +void doPlayerEntered(int pid, int isYou, char *, char *, char *, long , char *); + + +//************************************************************************** +// Globals +// +static int IgnoreIncoming = 0; + +/*************************************************************************** + * TenConnManClass::TenConnManClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::TenConnManClass(void) +{ + int i; + + IsHost = 0; + + GlobalQueue = new CommBufferClass(1, 50, sizeof(GlobalPacketType), 4); + PrivateQueue = new CommBufferClass(1, 50, Session.TenSize, 4); + + NumConnections = 0; + for (i = 0; i < MAX_PLAYERS; i++) { + Connections[i] = 0; + ID[i] = 0; + Names[i][0] = 0; + } + +} // end of TenConnManClass + + +/*************************************************************************** + * TenConnManClass::~TenConnManClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::~TenConnManClass() +{ + tenArExitArena(); + + delete GlobalQueue; + delete PrivateQueue; + +} // end of ~TenConnManClass + + +/*************************************************************************** + * TenConnManClass::Init -- Inits TEN * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Init(void) +{ + // + // set the debugging functions + // + setExitRoutine(terminateApp); + setDebugMsgRoutine(debugMessage); + + // + // callback function addresses + // + tenArSetAlertMessageRoutine(doAlert); + + tenArSetPregameHookRoutine(doPregameHook); + + tenArSetIncomingPacketRoutine(doIncomingPacket); + + tenArSetPlayerEnteredRoutine(doPlayerEntered); + + verifyNoErr(tenArInitArena("redalt")); + + return (1); + +} // end of Init + + +/*************************************************************************** + * TenConnManClass::Service -- Service routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Service(void) +{ + tenArIdleArena(); + + return (1); + +} // Service + + +/*************************************************************************** + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * * + * If Connection ID is -1, the packet is multicast; otherwise, it's sent * + * to only the specified connection. * + * * + * Private & Global messages are sent via the same mechanism. The only * + * way to tell the difference between them is that the Global Channel * + * packets have the 'kGlobalChannelFlag' bit set in the 1st byte (the * + * "Type" field for EventClass's, and the NetCommand field for Global * + * packets). * + * * + * INPUT: * + * buf packet to send * + * buflen size of packet * + * reliable 1 = must be delivered reliably * + * conn_id connection ID to send to, -1 = all * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Private_Message(void *buf, int buflen, + int reliable, int conn_id) +{ + int doBroadcast = conn_id == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = Connection_Address(conn_id); + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + return (1); + +} // end of Send_Private_Message + + +/*************************************************************************** + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * * + * Retrieves the next-available "private" message, if there is one. * + * * + * INPUT: * + * buf packet retrieved * + * buflen length of packet * + * conn_id ptr to store sender's connection ID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + int addr; + int addrlen; + int i; + + if (PrivateQueue->Num_Receive() > 0) { + PrivateQueue->UnQueue_Receive(buf, buflen, 0, &addr, &addrlen); + (void)verify(addrlen == 4); + (*conn_id) = CONNECTION_NONE; + for (i = 0; i < NumConnections; i++) { + if (addr == Connections[i]) { + (*conn_id) = ID[i]; + return (1); + } + } + } + + return (0); + +} // end of Get_Private_Message + + +/*************************************************************************** + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * * + * INPUT: * + * buf packet to send * + * buflen length of packet * + * reliable 1 = send reliably * + * address address to send to; -1 = broadcast it (for TEN, this * + * means send to all connected players). * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Global_Message(void *buf, int buflen, + int reliable, int address) +{ + int doBroadcast = address == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + // + // Set the global channel flag for this packet + // + ucbuf[0] |= kGlobalChannelFlag; + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = address; + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + + // + // The caller may re-use this buffer, so clear the global channel flag. + // + ucbuf[0] &= ~kGlobalChannelFlag; + + return (1); + +} // end of Send_Global_Message + + +/*************************************************************************** + * TenConnManClass::Get_Global_Message -- Gets next global message * + * * + * INPUT: * + * buf buffer to store packet in * + * buflen ptr filled in with packet length * + * address ptr filled in with address (Player ID) of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Global_Message(void *buf, int *buflen, + int *address) +{ + int addrlen; + + if (GlobalQueue->Num_Receive() > 0) { + GlobalQueue->UnQueue_Receive(buf, buflen, 0, address, &addrlen); + (void)verify(addrlen == 4); + return (1); + } + + return (0); + +} // end of Get_Global_Message + + +/*************************************************************************** + * TenConnManClass::Num_Connections -- Reports # connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # connections * + * * + * WARNINGS: * + * * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Num_Connections(void) +{ + return (NumConnections); + +} // end of Num_Connections + + +/*************************************************************************** + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * * + * INPUT: * + * index index of connection to report * + * * + * OUTPUT: * + * connection ID for this connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(ID[index]); + } else { + return(CONNECTION_NONE); + } + +} // end of Connection_ID + + +/*************************************************************************** + * TenConnManClass::Connection_Index -- Gets a connection's index * + * * + * INPUT: * + * id Connection ID to find index for * + * * + * OUTPUT: * + * index for that connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i] == id) { + return (i); + } + } + + return(CONNECTION_NONE); + +} // end of Connection_Index + + +/*************************************************************************** + * TenConnManClass::Create_Connection -- Creates a new connection * + * * + * INPUT: * + * id ID of connection * + * name name of connection * + * address TEN address (player ID) to give this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Create_Connection(int id, char *name, int address) +{ + Connections[NumConnections] = address; + ID[NumConnections] = id; + strcpy(Names[NumConnections], name); + NumConnections++; + + return (1); + +} // end of Create_Connection + + +/*************************************************************************** + * TenConnManClass::Delete_Connection -- Deletes a connection * + * * + * INPUT: * + * id ID for connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == CONNECTION_NONE) { + return 0; + } + + for (i = idx; i < NumConnections - 1; i++) { + Connections[i] = Connections[i+1]; + ID[i] = ID[i + 1]; + strcpy (Names[i], Names[i + 1]); + } + + NumConnections--; + + return (1); + +} // end of Delete_Connection + + +/*************************************************************************** + * TenConnManClass::Connection_Name -- Reports a connection's name * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +char * TenConnManClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Names[i]); + } + } + + return(NULL); + +} // end of Connection_Name + + +/*************************************************************************** + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection "address" (TEN player ID) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Connections[i]); + } + } + + return(NULL); + +} // end of Connection_Address + + +/*************************************************************************** + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Send(void) +{ + return(0); + +} // end of Global_Num_Send + +/*************************************************************************** + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Receive(void) +{ + return (GlobalQueue->Num_Receive()); + +} // end of Global_Num_Receive + +/*************************************************************************** + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Send(int /*id*/) +{ + return(0); + +} // end of Private_Num_Send + +/*************************************************************************** + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Receive(int /*id*/) +{ + return (PrivateQueue->Num_Receive()); + +} // end of Private_Num_Receive + + +/*************************************************************************** + * TenConnManClass::Flush_All -- Flushes all packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Flush_All(void) +{ + int i; + int maxqueuesize; + int rc; + + // + // Set the max # of packets that Ten will send me during any given + // call to tenArIdleArena() to slightly smaller than my max queue + // size. + // + maxqueuesize = 45; + rc = tenArSetOption(kTenArOptReadQueueSize, &maxqueuesize, + sizeof(maxqueuesize)); + + verifyNoErr(rc); + + + // + // Set the flag to tell the doIncomingPacket routine to ignore packets. + // (doIncomingPacket() is called by tenArIdleArena().) + // + IgnoreIncoming = 1; + + while (i++ < 1000) { + tenArIdleArena(); + if (GlobalQueue->Num_Receive() == 0 && + PrivateQueue->Num_Receive() == 0) { + break; + } + GlobalQueue->Init(); + PrivateQueue->Init(); + } + + IgnoreIncoming = 0; + +} // end of Flush_All + + +/*************************************************************************** + * TenConnManClass::Reset_Response_Time -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Reset_Response_Time(void) +{ + // + // (This function intentionally left blank.) + // + +} // end of Reset_Response_Time + + +/*************************************************************************** + * TenConnManClass::Response_Time -- Reports response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * worst-case connection response time (round-trip) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +unsigned long TenConnManClass::Response_Time(void) +{ + return((Session.NetResponseTime * 60) / 1000); // 300 milliseconds one way + +} // end of Response_Time + + +/*************************************************************************** + * TenConnManClass::Set_Timing -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, unsigned long /*timeout*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Set_Timing + + +/*************************************************************************** + * TenConnManClass::Configure_Debug -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char **/*names*/, int /*namestart*/, int /*namecount*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Configure_Debug + +/*************************************************************************** + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Mono_Debug_Print + +/*************************************************************************** + * terminateApp -- Callback: app terminates on error * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void terminateApp(void) +{ + Prog_End(); + dprintf("Exiting due to a fatal error.\n"); + exit(0); + +} // end of terminateApp + + +/*************************************************************************** + * debugMessage -- outputs debug message * + * * + * INPUT: * + * msgLevel not used * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void debugMessage(int /*msgLevel*/, char *msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tendebug.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of debugMessage + + +/*************************************************************************** + * doAlert -- Outputs debug message * + * * + * INPUT: * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doAlert(int, int, char * msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tenalert.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of doAlert + + +/*************************************************************************** + * doPregameHook -- Callback: game is starting * + * * + * INPUT: * + * joinType if "create", we're the game host * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *) +{ + char typeToken[16]; + + sscanf(joinType, "%s", typeToken); + if (!strcmp(typeToken, "create")) { + Ten->IsHost = 1; + } + + if (Ten->IsHost) { + verifyNoErr(tenArReturnGameOptions("")); + } + + verifyNoErr(tenArReturnPlayerOptions("")); + +} // end of doPregameHook + + +/*************************************************************************** + * doIncomingPacket -- Callback: packet has arrived * + * * + * INPUT: * + * addr player ID of sender * + * buf buffer containing incoming packet * + * size size of incoming packet * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doIncomingPacket(int addr, void *buf, size_t size) +{ + int rc; + unsigned char *byte; + + // + // Check to see if this packet belongs to the Global Channel or not + // + byte = (unsigned char *)buf; + + // + // If the global channel flag is set in this packet, queue it onto the + // Global Channel queue. Ignore any errors if "IgnoreIncoming" is set, + // which means we're in the process of flushing the queues. + // + if (byte[0] & kGlobalChannelFlag) { + byte[0] &= (~kGlobalChannelFlag); + rc = Ten->GlobalQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } else { + rc = Ten->PrivateQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } + +} // end of doIncomingPacket + + +/*************************************************************************** + * doPlayerJoins -- Callback: player joins * + * * + * INPUT: * + * pid player ID of the player joining * + * isYou true = this is you * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doPlayerEntered(int pid, int isYou, char * /*options*/, + char */*termOptions*/, char */*address*/, long /*uniqueId*/, + char */*joinType*/) +{ + if (isYou) { + Session.TenPlayerID = pid; + } + +} // end of doPlayerJoins + + +#endif //TEN +/************************** end of tenmgr.cpp ******************************/ diff --git a/CODE/TENMGR.H b/CODE/TENMGR.H new file mode 100644 index 0000000..39552e8 --- /dev/null +++ b/CODE/TENMGR.H @@ -0,0 +1,118 @@ +/* +** 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 . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : June 26, 1996 [BRR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class TenConnManClass : public ConnManClass +{ + public: + // + // Constructor / Destructor + // + TenConnManClass(void); + virtual ~TenConnManClass(); + + // + // Initialization + // + int Init(void); + + // + // Service routine + // + virtual int Service(void); + + // + // Sending & receiving data + // + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id); + + int Send_Global_Message(void *buf, int buflen, + int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, + int *address = 0); + + // + // Connection management + // + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + virtual int Connection_Index(int id); + + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // + // Queue utility routines + // + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + void Flush_All(void); + + // + // Timing management + // + virtual void Reset_Response_Time(void); + virtual unsigned long Response_Time(void); + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + // + // Debugging + // + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh); + + CommBufferClass *GlobalQueue; + CommBufferClass *PrivateQueue; + + // + // This flag will be set if I'm the game host. + // + int IsHost; + + private: + int Connections[MAX_PLAYERS]; + int ID[MAX_PLAYERS]; + char Names[MAX_PLAYERS][128]; + int NumConnections; + +}; + +/***************************** end of tenmgr.h *****************************/ diff --git a/CODE/TERRAIN.CPP b/CODE/TERRAIN.CPP new file mode 100644 index 0000000..cf3c127 --- /dev/null +++ b/CODE/TERRAIN.CPP @@ -0,0 +1,792 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TERRAIN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TERRAIN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainClass::AI -- Process the terrain object AI. * + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * TerrainClass::Heath_Ratio -- Determines the health ratio for the terrain object. * + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * TerrainClass::Mark -- Marks the terrain object on the map. * + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * TerrainClass::TerrainClass -- This is the constructor for a terrain object. * + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * TerrainClass::delete -- Deletes a terrain object. * + * TerrainClass::new -- Creates a new terrain object. * + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" + + +/*********************************************************************************************** + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * * + * This is the default destructor for terrain objects. It will remove the object from the * + * map and tracking systems, but only if the game is running. Otherwise, it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::~TerrainClass(void) +{ + if (GameActive && Class) { + TerrainClass::Limbo(); + } +} + + +/*********************************************************************************************** + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * * + * This routine is called when damage is to be inflicted upon the terrain object. It is * + * through this routine that terrain objects are attacked and thereby destroyed. Not all * + * terrain objects can be damaged by this routine however. * + * * + * INPUT: damage -- The damage points to inflict (raw). * + * * + * warhead -- The warhead type the indicates the kind of damage. This is used to * + * determine if the terrain object is damaged and if so, by how much. * + * * + * OUTPUT: bool; Was the terrain object destroyed by this damage? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 12/11/1994 JLB : Shortens attached burning animations. * + *=============================================================================================*/ +ResultType TerrainClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Small arms can never destroy a terrain element. + */ + if ((!IsOnFire || warhead == WARHEAD_FIRE) && warhead != WARHEAD_SA && !Class->IsImmune) { + + res = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + + if (damage && warhead == WARHEAD_FIRE) { + Catch_Fire(); + } + + /* + ** If the terrain object is destroyed by this damage, then only remove it if it + ** currently isn't on fire and isn't in the process of crumbling. + */ + if (res == RESULT_DESTROYED) { + + /* + ** Remove this terrain object from the targeting computers of all other + ** game objects. No use beating a dead horse. + */ + Detach_All(); + + if (IsOnFire) { + + /* + ** Attached flame animation should be shortened as much as possible so that + ** crumbling can begin soon. + */ + Shorten_Attached_Anims(this); + } else { + Start_To_Crumble(); + } + } + } + return(res); +} + + +/*********************************************************************************************** + * TerrainClass::new -- Creates a new terrain object. * + * * + * This routine creates a new terrain object by grabbing a free slot * + * out of the terrain object pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void * TerrainClass::operator new(size_t) +{ + void * ptr = Terrains.Allocate(); + if (ptr) { + ((TerrainClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TerrainClass::delete -- Deletes a terrain object. * + * * + * This routine deletes a terrain object by returning it to the * + * terrain object pool. * + * * + * INPUT: ptr -- Pointer to the terrain object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::operator delete(void * ptr) +{ + if (ptr) { + ((TerrainClass *)ptr)->IsActive = false; + } + Terrains.Free((TerrainClass *)ptr); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- This is the constructor for a terrain object * + * * + * This constructor for a terrain object will initialize the terrain * + * object with it's proper type and insert it into the access * + * tracking system. * + * * + * INPUT: type -- The terrain object type. * + * * + * cell -- The location of the terrain object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(TerrainType type, CELL cell) : + ObjectClass(RTTI_TERRAIN, Terrains.ID(this)), + Class(TerrainTypes.Ptr((int)type)), + IsOnFire(false), + IsCrumbling(false) +{ + Strength = Class->MaxStrength; + if (cell != -1) { + if (!Unlimbo(Cell_Coord(cell))) { + delete this; + } + } + Set_Rate(0); // turn off animation +} + + +/*********************************************************************************************** + * TerrainClass::Mark -- Marks the terrain object on the map. * + * * + * This routine will mark or remove the terrain object from the map * + * tracking system. This is typically called when the terrain object * + * is first created, when it is destroyed, and whenever it needs to be * + * redrawn. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the terrain object successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 12/23/1994 JLB : Performs low level legality check before proceeding. * + *=============================================================================================*/ +bool TerrainClass::Mark(MarkType mark) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + CELL cell = Coord_Cell(Coord); + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * * + * This routine is used to render the terrain object at the location specified and * + * clipped to the window specified. This is the gruntwork drawing routine for the * + * terrain objects as they are displayed on the map. * + * * + * INPUT: x,y -- The coordinate to draw the terrain object at (centered). * + * * + * window -- The clipping window to draw to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + * 11/09/1994 JLB : Changed selected terrain highlight method. * + *=============================================================================================*/ +void TerrainClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + void const * shapedata; + + shapedata = Get_Image_Data(); + if (shapedata) { + int shapenum = 0; + + /* + ** Determine the animation stage to render the terrain object. If it is crumbling, then + ** it will display the crumbling animation. + */ + if (IsCrumbling) { + shapenum = Fetch_Stage()+IsCrumbling; + } else { + if (Strength < 2) { + shapenum++; + } + } + + ShapeFlags_Type flags = SHAPE_NORMAL; + if (IsSelected && Debug_Map) flags = flags | SHAPE_FADING; + + /* + **Terrain is always theater specific so flag it as such for Build_Frame + */ + IsTheaterShape = true; + CC_Draw_Shape(shapedata, shapenum, x, y, window, flags|SHAPE_WIN_REL|SHAPE_GHOST, Map.FadingLight, Map.UnitShadow); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * * + * This routine will clear out the terrain object system so that no terrain objects will * + * exists. It is called prior to loading or starting a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Init(void) +{ + Terrains.Free_All(); +} + + +/*********************************************************************************************** + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * * + * This routine will examine the cell specified and determine if the the terrain object * + * can legally exist there. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: If the terrain object can be placed in the cell specified, then a value less than * + * 256 will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 01/01/1995 JLB : Actually works now. * + *=============================================================================================*/ +MoveType TerrainClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + short const * offset; // Pointer to cell offset list. + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (Class->IsWaterBased) { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build(SPEED_FLOAT)) { + return(MOVE_NO); + } + } else { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build()) { + return(MOVE_NO); + } + } + } + return(MOVE_OK); +} + + +/*********************************************************************************************** + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * * + * This routine is called if the terrain object is supposed to catch on fire. The routine * + * performs checking to make sure that only flammable terrain objects that aren't already * + * on fire get caught on fire. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object caught on fire by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 12/11/1994 JLB : Don't catch fire if already on fire or crumbling. * + *=============================================================================================*/ +bool TerrainClass::Catch_Fire(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling && !IsOnFire && Class->Armor == ARMOR_WOOD) { + AnimClass * anim = new AnimClass(ANIM_BURN_BIG, Coord_Add(Sort_Y(), 0xFFB00000L)); + if (anim) { + anim->Attach_To(this); + } + anim = new AnimClass(ANIM_BURN_MED, Coord_Add(Sort_Y(), 0xFF200000L), 15); + if (anim) { + anim->Attach_To(this); + } + IsOnFire = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * * + * When the fire has gone out on a burning terrain object, this routine is called. The * + * animation has already been terminated prior to calling this routine. All this routine * + * needs to perform is any necessary local flag updating. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Fire_Out(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (IsOnFire) { + IsOnFire = false; + if (!IsCrumbling && !Strength) { + Detach_All(); + Mark(); + Start_To_Crumble(); + new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, Class->CenterBase)); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::AI -- Process the terrain object AI. * + * * + * This is used to handle any AI processing necessary for terrain objects. This might * + * include animation effects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 09/28/1994 JLB : Crumbling animation. * + * 08/12/1996 JLB : Reset map zone when terrain object destroyed. * + * 10/04/1996 JLB : Growth speed regulated by rules. * + *=============================================================================================*/ +void TerrainClass::AI(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::AI(); + + if ((*this == TERRAIN_MINE) && (Frame % (Rule.GrowthRate * TICKS_PER_MINUTE)) == 0) { + Map[::As_Cell(As_Target())].Spread_Tiberium(true); + } + if (StageClass::Graphic_Logic()) { + Mark(); + + /* + ** If the terrain object is in the process of crumbling, then when at the + ** last stage of the crumbling animation, delete the terrain object. + */ + if (IsCrumbling && Fetch_Stage() == Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + delete this; + + Map.Zone_Reset(MZONEF_NORMAL|MZONEF_CRUSHER|MZONEF_DESTROYER); + } + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * * + * This debugging support routine is used to display the status of the terrain object to * + * the debug screen. * + * * + * INPUT: mono -- The mono screen to display the status to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Debug_Dump(MonoClass * mono) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * * + * This routine is used to unlimbo the terrain object onto a location on the map. Normal * + * unlimbo procedures are sufficient except that the coordinate location of a terrain * + * object is based on the upper left corner of a cell rather than the center. Mask the * + * coordinate value so that it snaps to the upper left corner and then proceed with a * + * normal unlimbo process. * + * * + * INPUT: coord -- The coordinate to mark as the terrain's location. * + * * + * dir -- unused * + * * + * OUTPUT: bool; Was the terrain object successful in the unlimbo process? Failure could be * + * the result of illegal positioning. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + * 11/16/1994 JLB : Checks for theater legality. * + *=============================================================================================*/ +bool TerrainClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (Class->Theater & (1 << Scen.Theater)) { + return(ObjectClass::Unlimbo(coord, dir)); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * * + * This routine is used to start the crumbling process for terrain object. This only * + * applies to trees. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Start_To_Crumble(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling) { + IsCrumbling = true; + Set_Rate(2); + Set_Stage(0); + } +} + + +/*********************************************************************************************** + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * * + * This routine (called as a part of the limbo process) will remove the terrain occupation * + * flag in the cell it occupies. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Limbo(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + CELL cell = Coord_Cell(Coord); + Map[cell].Flag.Occupy.Monolith = false; + } + return(ObjectClass::Limbo()); +} + + +/*********************************************************************************************** + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * * + * Use this routine to fetch the center point terrain * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Center_Coord(void) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, Class->CenterBase)); +} + + +/*********************************************************************************************** + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * * + * This routine will return with a pointer to the radar icon to use for the cell number * + * specified. * + * * + * INPUT: cell -- The cell number to use when determine what icon pointer to return. * + * * + * OUTPUT: Returns with a pointer to the 9 pixel values that make up the icon of this * + * terrain object located at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char * TerrainClass::Radar_Icon(CELL cell) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + unsigned char * icon = (unsigned char *)Class->Get_Radar_Data(); // get a pointer to radar icons + int width = *icon++; // extract the width from data + int height = *icon++; // extract the width from data + + /* + ** Icon number that we need can be found by converting the cell and base + ** cell to and x and y offset from the upper left of the cell, and then + ** multiplying it by the width of the terrain in icons, which we + ** conveniently stored out as the first byte of every icon we made. + */ + int basecell = Coord_Cell(Coord); // find the base cell of terrain + int ydiff = Cell_Y(cell) - Cell_Y(basecell); + int xdiff = Cell_X(cell) - Cell_X(basecell); + if (xdiff < width && ydiff < height) { + int iconnum = (ydiff * width) + xdiff; + return(icon + (iconnum * 9)); + } + return(NULL); +} + + +/*********************************************************************************************** + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * * + * This routine will return with the coordinate to use if this terrain object were to be * + * fired upon and the coordinate where the bullets should hit is needed. Usually, this * + * location is the base of the object (e.g., the trunk of a tree). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing at this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Target_Coord(void) const +{ + return(Coord_Add(XY_Coord(0, -Height), Sort_Y())); +} + + +/*********************************************************************************************** + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * * + * This routine reads a scenario control INI file and creates all * + * terrain objects specified therein. Objects so created are placed * + * upon the map. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Read_INI(CCINIClass & ini) +{ + TerrainClass * tptr; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + TerrainType terrain = ini.Get_TerrainType(INI_Name(), entry, TERRAIN_NONE); + CELL cell = atoi(entry); + + if (terrain != TERRAIN_NONE) { + tptr = new TerrainClass(terrain, cell); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * * + * This routine will clear out any old terrain data from the INI database and then * + * fill it in with all the data from the terrain objects that currently exists. * + * * + * INPUT: ini -- Reference to the INI database to store the terrain objects in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing terrain data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the terrain data out. + */ + for (int index = 0; index < Terrains.Count(); index++) { + TerrainClass * terrain; + + terrain = Terrains.Ptr(index); + if (terrain != NULL && !terrain->IsInLimbo && terrain->IsActive) { + char uname[10]; + sprintf(uname, "%d", Coord_Cell(terrain->Coord)); + ini.Put_TerrainType(INI_Name(), uname, *terrain); + } + } +} + diff --git a/CODE/TERRAIN.H b/CODE/TERRAIN.H new file mode 100644 index 0000000..376e9a9 --- /dev/null +++ b/CODE/TERRAIN.H @@ -0,0 +1,152 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TERRAIN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TERRAIN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "object.h" +#include "type.h" + + +/**************************************************************************** +** Each type of terrain has certain pieces of static information associated +** with it. This class elaborates this data. +*/ +class TerrainClass : public ObjectClass, public StageClass +{ + public: + /* + ** This points to the constant terrain data (for this type) that gives this + ** terrain object its character. + */ + CCPtr Class; + + /* + ** Constructor for terrain object class. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TerrainClass(TerrainType id, CELL cell); + TerrainClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~TerrainClass(void); + operator TerrainType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Terrain specific support functions. + */ + void Start_To_Crumble(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const {return Coord;}; + virtual COORDINATE Sort_Y(void) const {return Coord_Add(Coord, Class->CenterBase);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType dir=DIR_N); + virtual bool Limbo(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing = FACING_NONE) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + unsigned char *Radar_Icon(CELL cell); + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(int) {}; + + /* + ** Combat related. + */ + virtual void Fire_Out(void); + virtual bool Catch_Fire(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + + /* + ** AI. + */ + virtual void AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "TERRAIN";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + private: + + /* + ** If this terrain object is on fire, then this flag will be true. + */ + unsigned IsOnFire:1; + + /* + ** Is this a terrain object that undergoes crumbling animation and it is + ** in fact crumbling at this time? + */ + unsigned IsCrumbling:1; +}; + +#endif diff --git a/CODE/TEVENT.CPP b/CODE/TEVENT.CPP new file mode 100644 index 0000000..d985024 --- /dev/null +++ b/CODE/TEVENT.CPP @@ -0,0 +1,766 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEVENT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Attaches_To -- Determines what event can be attached to. * + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * Event_From_Name -- retrieves EventType for given name * + * Event_Needs -- Returns with what this event type needs for data. * + * Name_From_Event -- retrieves name for EventType * + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +/* +** This is the text name for all of the trigger events. These are used by the scenario editor +*/ +const char * EventText[TEVENT_COUNT] = { + "-No Event-", + "Entered by...", + "Spied by...", + "Thieved by...", + "Discovered by player", + "House Discovered...", + "Attacked by anybody", + "Destroyed by anybody", + "Any Event", + "Destroyed, Units, All...", + "Destroyed, Buildings, All...", + "Destroyed, All...", + "Credits exceed (x100)...", + "Elapsed Time (1/10th min)...", + "Mission Timer Expired", + "Destroyed, Buildings, #...", + "Destroyed, Units, #...", + "No Factories left", + "Civilians Evacuated", + "Build Building Type...", + "Build Unit Type...", + "Build Infantry Type...", + "Build Aircraft Type...", + "Leaves map (team)...", + "Zone Entry by...", + "Crosses Horizontal Line...", + "Crosses Vertical Line...", + "Global is set...", + "Global is clear...", + "Destroyed, Fakes, All...", + "Low Power...", + "All bridges destroyed", + "Building exists..." +}; + + +/* +** This is an ordinal list of trigger events. This list +** is used when generating the trigger dialog box. +*/ +EventChoiceClass EventChoices[TEVENT_COUNT] = { + {TEVENT_NONE}, + {TEVENT_PLAYER_ENTERED}, + {TEVENT_SPIED}, + {TEVENT_THIEVED}, + {TEVENT_DISCOVERED}, + {TEVENT_HOUSE_DISCOVERED}, + {TEVENT_ATTACKED}, + {TEVENT_DESTROYED}, + {TEVENT_ANY}, + {TEVENT_UNITS_DESTROYED}, + {TEVENT_BUILDINGS_DESTROYED}, + {TEVENT_ALL_DESTROYED}, + {TEVENT_CREDITS}, + {TEVENT_TIME}, + {TEVENT_MISSION_TIMER_EXPIRED}, + {TEVENT_NBUILDINGS_DESTROYED}, + {TEVENT_NUNITS_DESTROYED}, + {TEVENT_NOFACTORIES}, + {TEVENT_EVAC_CIVILIAN}, + {TEVENT_BUILD}, + {TEVENT_BUILD_UNIT}, + {TEVENT_BUILD_INFANTRY}, + {TEVENT_BUILD_AIRCRAFT}, + {TEVENT_LEAVES_MAP}, + {TEVENT_ENTERS_ZONE}, + {TEVENT_CROSS_HORIZONTAL}, + {TEVENT_CROSS_VERTICAL}, + {TEVENT_GLOBAL_SET}, + {TEVENT_GLOBAL_CLEAR}, + {TEVENT_FAKES_DESTROYED}, + {TEVENT_LOW_POWER}, + {TEVENT_ALL_BRIDGES_DESTROYED}, + {TEVENT_BUILDING_EXISTS}, +}; + + +/*********************************************************************************************** + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * * + * This utility routine is used by the list box display code to print the text line that * + * describes the event choice action. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void EventChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * * + * This will reset the trigger event so that it may be "sprung" again. Typically, this * + * occurs for persistent triggers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Reset(TDEventClass & td) const +{ + td.IsTripped = false; + if (Event == TEVENT_TIME) { + td.Timer = Data.Value * (TICKS_PER_MINUTE/10); + } +} + + +/*********************************************************************************************** + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * * + * This routine is called to see if the event has been satisfied. Typically, when the * + * necessary trigger events have been satisfied, then the trigger is sprung. For some * + * events, the act of calling this routine is tacit proof enough that the event has * + * occurred. For most other events, the success condition must be explicitly checked. * + * * + * INPUT: event -- The event that has occurred according to the context from which this * + * routine was called. In the case of no specific event having occurred, * + * then TEVENT_ANY will be passed in. * + * * + * house -- The house that this event is tied to. * + * * + * object-- The object that this event might apply to. For object triggering * + * events, this will point to the perpetrator object. * + * * + * forced-- If this event is forced by some outside action, this flag will be true. * + * Forcing only occurs as an explicit action from another trigger. * + * * + * OUTPUT: Was this event satisfied? A satisfied event will probably spring the trigger * + * it is attached to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +bool TEventClass::operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced) +{ + /* + ** If this trigger event has been forced, then no further checks are required. + ** Force the trigger to be tripped. + */ + if (forced) { + td.IsTripped = true; + } + + if (td.IsTripped) return(true); + + /* + ** Triggers based on the game's global environment such as time or + ** global flags are triggered only when the appropriate condition + ** is true. + */ + switch (Event) { + case TEVENT_GLOBAL_SET: + if (!Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_GLOBAL_CLEAR: + if (Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_MISSION_TIMER_EXPIRED: +// if (MissionSuspend == -1 || MissionTimer != 0) return(false); + if (!Scen.MissionTimer.Is_Active() || Scen.MissionTimer != 0) return(false); + return(true); + + case TEVENT_TIME: + if (td.Timer != 0) return(false); + return(true); + } + + /* + ** Don't trigger this event if the parameters mean nothing. Typical of + ** this would be for events related to time or other outside influences. + */ + if (Event == TEVENT_NONE) { + return(false); + } + + /* + ** If this is not the event for this trigger, just return. This is only + ** necessary to check for those trigger events that are presumed to be + ** true just by the fact that this routine is called with the appropriate + ** event identifier. + */ + if (Event == TEVENT_ATTACKED || + Event == TEVENT_DESTROYED || + Event == TEVENT_DISCOVERED || + Event == TEVENT_SPIED || + Event == TEVENT_NONE || + Event == TEVENT_CROSS_HORIZONTAL || + Event == TEVENT_CROSS_VERTICAL || + Event == TEVENT_ENTERS_ZONE || + Event == TEVENT_PLAYER_ENTERED) { + + if (event != Event && event != TEVENT_ANY) { + return(false); + } + } + + /* + ** The cell entry trigger event is only tripped when an object of the + ** matching ownership has entered the cell in question. All other + ** conditions will not trigger the event. + */ + if (Event == TEVENT_PLAYER_ENTERED || Event == TEVENT_CROSS_HORIZONTAL || Event == TEVENT_CROSS_VERTICAL || Event == TEVENT_ENTERS_ZONE) { + if (!object || object->Owner() != Data.House) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** Check for all bridges destroyed condition. + */ + if (Event == TEVENT_ALL_BRIDGES_DESTROYED) { + if (Scen.BridgeCount) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** The following trigger events are not considered to have sprung + ** merely by fact that this routine has been called. These trigger + ** events must be verified manually by examining the house that + ** they are assigned to. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + int index; + if (hptr != NULL) { + switch (Event) { + /* + ** Check to see if a team of the appropriate type has left the map. + */ + case TEVENT_LEAVES_MAP: + for (index = 0; index < Teams.Count(); index++) { + TeamClass * ptr = Teams.Ptr(index); + if (ptr->Class == Team && ptr->Is_Empty() && ptr->IsLeaveMap) { +// if (ptr->Class == Team && ptr->House == hptr && ptr->Is_Empty() && ptr->IsLeaveMap) { + td.IsTripped = true; + break; + } + } + if (index == Teams.Count()) return(false); + break; + + /* + ** Credits must be equal or greater to the value specified. + */ + case TEVENT_CREDITS: + if (hptr->Available_Money() < Data.Value) return(false); + break; + + /* + ** Ensure that there are no more factories left. + */ + case TEVENT_NOFACTORIES: + if (hptr->BScan & (STRUCTF_AIRSTRIP|STRUCTF_TENT|STRUCTF_WEAP|STRUCTF_BARRACKS|STRUCTF_CONST)) return(false); + break; + + /* + ** Ensure that there are no fake structures left. + */ + case TEVENT_FAKES_DESTROYED: + if (hptr->BScan & (STRUCTF_FAKECONST|STRUCTF_FAKEWEAP)) return(false); + break; + + /* + ** A civilian must have been evacuated. + */ + case TEVENT_EVAC_CIVILIAN: + if (!hptr->IsCivEvacuated) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILDING_EXISTS: + if ((hptr->ActiveBScan & (1 << Data.Structure)) == 0) return(false); +// if (hptr->Get_Quantity(Data.Structure) == 0) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILD: + if (hptr->JustBuiltStructure != Data.Structure) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the unit has been built. + */ + case TEVENT_BUILD_UNIT: + if (hptr->JustBuiltUnit != Data.Unit) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the infantry has been built. + */ + case TEVENT_BUILD_INFANTRY: + if (hptr->JustBuiltInfantry != Data.Infantry) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the aircraft has been built. + */ + case TEVENT_BUILD_AIRCRAFT: + if (hptr->JustBuiltAircraft != Data.Aircraft) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the specified number of buildings have been destroyed. + */ + case TEVENT_NBUILDINGS_DESTROYED: + if (hptr->BuildingsLost < Data.Value) return(false); + break; + + /* + ** Verify that the specified number of units have been destroyed. + */ + case TEVENT_NUNITS_DESTROYED: + if (hptr->UnitsLost < Data.Value) return(false); + break; + + default: + break; + } + } + + hptr = HouseClass::As_Pointer(Data.House); + if (hptr) { + switch (Event) { + case TEVENT_LOW_POWER: + if (hptr->Power_Fraction() >= 1) return(false); + break; + +// case TEVENT_SPIED: +// if (!hptr->IsSpied) return(false); +// break; + + case TEVENT_THIEVED: + if (!hptr->IsThieved) return(false); + break; + + /* + ** Verify that the house has been discovered. + */ + case TEVENT_HOUSE_DISCOVERED: + if (!hptr->IsDiscovered) return(false); + break; + + /* + ** Verify that all buildings have been destroyed. + */ + case TEVENT_BUILDINGS_DESTROYED: + if (hptr->ActiveBScan) return(false); + break; + + /* + ** Verify that all units have been destroyed -- with some + ** exceptions. + */ + case TEVENT_UNITS_DESTROYED: + if (hptr->ActiveUScan | hptr->ActiveIScan) return(false); + break; + + /* + ** Verify that all buildings and units have been destroyed. + */ + case TEVENT_ALL_DESTROYED: + if (hptr->ActiveBScan | hptr->ActiveUScan | hptr->ActiveIScan | hptr->ActiveVScan) return(false); + break; + + default: + break; + } + } + + return(true); +} + + +/*********************************************************************************************** + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * * + * This routine will build the ini text for this trigger event. The ini text is appended * + * to the string buffer specified. This routine is used to build the complete trigger * + * ini text for writing out to the INI scenario file. * + * * + * INPUT: ptr -- Pointer to the string buffer that will hold the event INI data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d", Event, TeamTypes.Logical_ID(Team), Data.Value); +} + + +/*********************************************************************************************** + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * * + * This routine is used to parse the INI data line to fetch the event's data from it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Read_INI(void) +{ + char const * token; + switch (NewINIFormat) { + default: + Event = TEventType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + token = strtok(NULL, ","); + Event = TEVENT_NONE; + if (token) Event = TEventType(atoi(token)); + + token = strtok(NULL, ","); + Team = NULL; + Data.Value = -1; + if (token) { + if (Event_Needs(Event) == NEED_TEAM) { + Team = TeamTypes.Raw_Ptr(atoi(token)); + } else { + Data.Value = atoi(token); + } + } + break; + + case 0: + Event = TEventType(atoi(strtok(NULL, ","))); + + strtok(NULL, ","); + strtok(NULL, ","); + + Team = TeamTypeClass::From_Name(strtok(NULL, ",")); + Data.Value = atoi(strtok(NULL, ",")); + strtok(NULL, ","); + break; + } +} + + +/*********************************************************************************************** + * Event_Needs -- Returns with what this event type needs for data. * + * * + * This routine will examine the specified event type and return a code that indicates * + * the type of data that must be supplied to this event. Some events require no data and * + * these will return NEED_NONE. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the type of additional data that is needed by this event. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +NeedType Event_Needs(TEventType event) +{ + switch (event) { + case TEVENT_THIEVED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_LOW_POWER: + return(NEED_HOUSE); + + case TEVENT_NUNITS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_CREDITS: + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + return(NEED_NUMBER); + + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + return(NEED_STRUCTURE); + + case TEVENT_BUILD_UNIT: + return(NEED_UNIT); + + case TEVENT_BUILD_INFANTRY: + return(NEED_INFANTRY); + + case TEVENT_BUILD_AIRCRAFT: + return(NEED_AIRCRAFT); + + case TEVENT_LEAVES_MAP: + return(NEED_TEAM); + + default: + break; + } + return(NEED_NONE); +} + + +/*********************************************************************************************** + * Event_From_Name -- retrieves EventType for given name * + * * + * INPUT: * + * name name to get event for * + * * + * OUTPUT: * + * EventType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TEventType Event_From_Name (char const * name) +{ + if (name) { + for (TEventType i = TEVENT_NONE; i < TEVENT_COUNT; i++) { + if (!stricmp(name, EventText[i])) { + return(i); + } + } + } + + return(TEVENT_NONE); +} + + +/*********************************************************************************************** + * Name_From_Event -- retrieves name for EventType * + * * + * INPUT: * + * event EventType to get name for * + * * + * OUTPUT: * + * name for EventType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Event(TEventType event) +{ + return(EventText[event]); +} + + +/*********************************************************************************************** + * Attaches_To -- Determines what event can be attached to. * + * * + * This routine is used to determine what this event type can be attached to in the game. * + * Some events are specifically tied to cells or game object. This routine will indicate * + * this requirement. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the attachable characteristics for this event type. These * + * characteristics are represented by a composite bit field. Some events can be * + * attached to multiple objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +AttachType Attaches_To(TEventType event) +{ + AttachType attach = ATTACH_NONE; + + switch (event) { + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_PLAYER_ENTERED: + case TEVENT_ANY: + case TEVENT_DISCOVERED: + case TEVENT_NONE: + attach = attach | ATTACH_CELL; + break; + + default: + break; + } + + switch (event) { + case TEVENT_SPIED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_DISCOVERED: + case TEVENT_DESTROYED: + case TEVENT_ATTACKED: + case TEVENT_ANY: + case TEVENT_NONE: + attach = attach | ATTACH_OBJECT; + break; + + default: + break; + } + + switch (event) { +// case TEVENT_CROSS_HORIZONTAL: +// case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_ANY: + attach = attach | ATTACH_MAP; + break; + + default: + break; + } + + switch (event) { + case TEVENT_LOW_POWER: + case TEVENT_EVAC_CIVILIAN: + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + case TEVENT_BUILD_UNIT: + case TEVENT_BUILD_INFANTRY: + case TEVENT_BUILD_AIRCRAFT: + case TEVENT_NOFACTORIES: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_NUNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_CREDITS: +// case TEVENT_ATTACKED: + case TEVENT_THIEVED: + case TEVENT_ANY: + case TEVENT_FAKES_DESTROYED: + attach = attach | ATTACH_HOUSE; + break; + + default: + break; + } + + switch (event) { + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + case TEVENT_MISSION_TIMER_EXPIRED: + case TEVENT_ANY: + case TEVENT_ALL_BRIDGES_DESTROYED: + case TEVENT_LEAVES_MAP: + attach = attach | ATTACH_GENERAL; + break; + + default: + break; + } + + return(attach); +} diff --git a/CODE/TEVENT.H b/CODE/TEVENT.H new file mode 100644 index 0000000..63dcf9e --- /dev/null +++ b/CODE/TEVENT.H @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEVENT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef TEVENT_H +#define TEVENT_H + +/* +** These are the trigger events that are checked for and if qualified, they will signal +** a successful trigger event. This might result in the trigger action being performed. +*/ +typedef enum TEventType { + TEVENT_NONE, + TEVENT_PLAYER_ENTERED, // player enters this square + TEVENT_SPIED, // Spied by. + TEVENT_THIEVED, // Thieved by (raided or stolen vehicle). + TEVENT_DISCOVERED, // player discovers this object + TEVENT_HOUSE_DISCOVERED, // House has been discovered. + TEVENT_ATTACKED, // player attacks this object + TEVENT_DESTROYED, // player destroys this object + TEVENT_ANY, // Any object event will cause the trigger. + TEVENT_UNITS_DESTROYED, // all house's units destroyed + TEVENT_BUILDINGS_DESTROYED, // all house's buildings destroyed + TEVENT_ALL_DESTROYED, // all house's units & buildings destroyed + TEVENT_CREDITS, // house reaches this many credits + TEVENT_TIME, // Scenario elapsed time from start. + TEVENT_MISSION_TIMER_EXPIRED, // Pre expired mission timer. + TEVENT_NBUILDINGS_DESTROYED, // Number of buildings destroyed. + TEVENT_NUNITS_DESTROYED, // Number of units destroyed. + TEVENT_NOFACTORIES, // No factories left. + TEVENT_EVAC_CIVILIAN, // Civilian has been evacuated. + TEVENT_BUILD, // Specified building has been built. + TEVENT_BUILD_UNIT, // Specified unit has been built. + TEVENT_BUILD_INFANTRY, // Specified infantry has been built. + TEVENT_BUILD_AIRCRAFT, // Specified aircraft has been built. + TEVENT_LEAVES_MAP, // Specified team member leaves map. + TEVENT_ENTERS_ZONE, // Enters same zone as waypoint 'x'. + TEVENT_CROSS_HORIZONTAL, // Crosses horizontal trigger line. + TEVENT_CROSS_VERTICAL, // Crosses vertical trigger line. + TEVENT_GLOBAL_SET, // If specified global has been set. + TEVENT_GLOBAL_CLEAR, // If specified global has been cleared. + TEVENT_FAKES_DESTROYED, // If all fake structures are gone. + TEVENT_LOW_POWER, // When power drops below 100%. + TEVENT_ALL_BRIDGES_DESTROYED, // All bridges destroyed. + TEVENT_BUILDING_EXISTS, // Check for building existing. + + TEVENT_COUNT, + TEVENT_FIRST=0 +} TEventType; + +TEventType Event_From_Name(char const * name); +NeedType Event_Needs(TEventType event); +char const * Name_From_Event(TEventType event); + +/* +** This holds the changable data that is associated with an event as +** it relates to a trigger. +*/ +struct TDEventClass { + /* + ** If this event has been triggered by something that is temporal, then + ** this flag will be set to true so that subsequent trigger examination + ** will return a successful event trigger flag. Typical use of this is + ** for when objects of a specific type are built. + */ + unsigned IsTripped:1; + + /* + ** Timer based events require a special timer control handler. + */ + CDTimerClass Timer; + + TDEventClass(void) : IsTripped(false), Timer(0) {}; + TDEventClass(NoInitClass const & x) : Timer(x) {}; +}; + + +/* +** This elaborates the information necessary to trigger +** an event. +*/ +class TeamTypeClass; +struct TEventClass { + + /* + ** This is the event that will controls how this event gets triggered. + */ + TEventType Event; + + /* + ** If this event needs to reference a team type, then this is the pointer + ** to the team type object. This must be separated from the following + ** union because Watcom compiler won't allow a class that has a + ** constructor to be declared in a union. + */ + CCPtr Team; + + union { + StructType Structure; // Used for structure type checking. + UnitType Unit; // Used for unit type checking. + InfantryType Infantry; // Used for infantry type checking. + AircraftType Aircraft; // Used for aircraft type checking. + HousesType House; // Used for house specific events. + long Value; // Used for other events that need data. + } Data; + + TEventClass(void) : Event(TEVENT_NONE) {Data.Value = 0;}; + TEventClass(TEventType event) : Event(event) {Data.Value = 0;}; + TEventClass(NoInitClass const & x) : Team(x) {}; + + void Code_Pointers(void); + void Decode_Pointers(void); + void Reset(TDEventClass & td) const; + bool operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; +}; + + +typedef enum AttachType { + ATTACH_NONE=0x00, // Trigger doesn't attach to anything (orphan trigger types). + ATTACH_CELL=0x01, // Trigger can only attach to a cell. + ATTACH_OBJECT=0x02, // Trigger can attach only to object (usually building or vehicle). + ATTACH_MAP=0x04, // Trigger applies to the general map (usually zone or parallel triggers). + ATTACH_HOUSE=0x08, // Trigger applies only to a house. + ATTACH_GENERAL=0x10, // General purpose trigger attached to game state. + ATTACH_TEAM=0x20 // Trigger applies to team object. +} AttachType; + +AttachType Attaches_To(TEventType event); + + + +class EventChoiceClass { + public: + EventChoiceClass(TEventType event=TEVENT_NONE) : Event(event) {} + + operator TEventType (void) const {return(Event);} + bool operator == (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event);} + bool operator != (EventChoiceClass const & rvalue) const {return(Event != rvalue.Event);} + bool operator > (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Event(Event));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TEventType Event; +}; + +extern EventChoiceClass EventChoices[TEVENT_COUNT]; + + +#endif diff --git a/CODE/TEXTBTN.CPP b/CODE/TEXTBTN.CPP new file mode 100644 index 0000000..2d5e50b --- /dev/null +++ b/CODE/TEXTBTN.CPP @@ -0,0 +1,348 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEXTBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textbtn.h" + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass(id, x, y, w, h), + String(text) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + IsBlackBorder = false; + String = NULL; + PrintFlags = TPF_8POINT; +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- The text number to use for displaying on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass (unsigned id, int text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass (id, x, y, w, h), + String(0) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + Set_Text(text); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int TextButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(char const * text, bool resize) +{ + String = text; + Flag_To_Redraw(); + if (resize && String) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String)+8; + Height = FontHeight + FontYSpacing + 2; + } +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * * + * This will set the text for this button. The text is provided as a text number. This * + * number is automatically converted to the appropriate text string before being stored * + * in the button's structure. * + * * + * INPUT: text -- The text number to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text number information is lost when it is assigned to the button. Once * + * the assignment takes place, the text number is NOT remembered by the button. * + * Only the associated text string is. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(int text, bool resize) +{ + if (text != TXT_NONE) { + Set_Text(Text_String(text), resize); + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Background(void) +{ + /* + ** Draw a border if selected style. + */ + if (IsBlackBorder) { + LogicPage->Draw_Rect (X-1, Y-1, X+Width+2, Y+Height+2, BLACK); + } + + /* + ** Draw the body & set text color. + */ + BoxStyleEnum style; + + if (IsDisabled) { +#ifdef WOLAPI_INTEGRATION + style = BOXSTYLE_BOX; +#else + style = BOXSTYLE_DIS_RAISED; +#endif + } else { + if (IsPressed) { + style = BOXSTYLE_DOWN; + } else { + style = BOXSTYLE_RAISED; + } + } + + Draw_Box(X, Y, Width, Height, style, true); +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Text(char const * text) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Display the text. + */ + if (String) { + TextPrintType flags; + + if (IsDisabled) { + flags = (TextPrintType)0; + } else { + if (IsPressed || IsOn) { + flags = TPF_USE_GRAD_PAL|TPF_BRIGHT_COLOR; + } else { + flags = TPF_USE_GRAD_PAL|TPF_MEDIUM_COLOR; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, scheme, TBLACK, PrintFlags|flags|TPF_CENTER); + } +} diff --git a/CODE/TEXTBTN.H b/CODE/TEXTBTN.H new file mode 100644 index 0000000..738a017 --- /dev/null +++ b/CODE/TEXTBTN.H @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TEXTBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TEXTBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEXTBTN_H +#define TEXTBTN_H + +#include "toggle.h" + + +class TextButtonClass : public ToggleClass +{ + public: + TextButtonClass(void); + TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + TextButtonClass(unsigned id, int text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const *text, bool resize = false); + virtual void Set_Text(int text, bool resize = false); + virtual void Set_Style (TextPrintType style) {PrintFlags = style;} + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + unsigned IsBlackBorder:1; + + /* + ** This points to a constant string that is used for the button's text. + */ + char const * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/CODE/THEME.CPP b/CODE/THEME.CPP new file mode 100644 index 0000000..9b0b700 --- /dev/null +++ b/CODE/THEME.CPP @@ -0,0 +1,617 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/THEME.CPP 3 3/11/97 4:03p Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : THEME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ThemeClass::AI -- Process the theme engine and restart songs. * + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * ThemeClass::From_Name -- Determines theme number from specified name. * + * ThemeClass::Full_Name -- Retrieves the full score name. * + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * ThemeClass::Scan -- Scans all scores for availability. * + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * ThemeClass::Stop -- Stops the current theme from playing. * + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "theme.h" + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/* +** These are the actual filename list for the theme sample files. +*/ +ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { + {"BIGF226M", TXT_THEME_BIGF, 0, 307, true, false, true, HOUSEF_ALLIES}, + {"CRUS226M", TXT_THEME_CRUS, 0, 222, true, false, true, HOUSEF_SOVIET}, + {"FAC1226M", TXT_THEME_FAC1, 0, 271, true, false, true, HOUSEF_ALLIES}, + {"FAC2226M", TXT_THEME_FAC2, 0, 328, true, false, true, HOUSEF_SOVIET}, + {"HELL226M", TXT_THEME_HELL, 0, 375, true, false, true, HOUSEF_ALLIES}, + {"RUN1226M", TXT_THEME_RUN1, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"SMSH226M", TXT_THEME_SMSH, 0, 272, true, false, true, HOUSEF_ALLIES}, + {"TREN226M", TXT_THEME_TREN, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"WORK226M", TXT_THEME_WORK, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"AWAIT", TXT_THEME_AWAIT, 0, 259, true, false, true, HOUSEF_ALLIES}, + {"DENSE_R", TXT_THEME_DENSE_R, 0, 294, true, false, true, HOUSEF_ALLIES}, + {"FOGGER1A", TXT_THEME_FOGGER1A, 0, 297, true, false, true, HOUSEF_ALLIES}, + {"MUD1A", TXT_THEME_MUD1A, 0, 280, true, false, true, HOUSEF_ALLIES}, + {"RADIO2", TXT_THEME_RADIO2, 0, 237, true, false, true, HOUSEF_ALLIES}, + {"ROLLOUT", TXT_THEME_ROLLOUT, 0, 227, true, false, true, HOUSEF_ALLIES}, + {"SNAKE", TXT_THEME_SNAKE, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"TERMINAT", TXT_THEME_TERMINAT, 0, 310, true, false, true, HOUSEF_ALLIES}, + {"TWIN", TXT_THEME_TWIN, 0, 229, true, false, true, HOUSEF_ALLIES}, + {"VECTOR1A", TXT_THEME_VECTOR1A, 0, 252, true, false, true, HOUSEF_ALLIES}, + {"MAP", TXT_THEME_MAP, 0, 63, false, true, true, HOUSEF_NONE}, + {"SCORE", TXT_THEME_SCORE, 0, 106, false, true, true, HOUSEF_NONE}, + {"INTRO", TXT_THEME_INTRO, 0, 205, false, true, true, HOUSEF_NONE}, + {"CREDITS", TXT_THEME_CREDITS, 0, 163, false, true, true, HOUSEF_NONE}, + + {"2ND_HAND", TXT_THEME_2ND_HAND, 0, 268, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"ARAZOID", TXT_THEME_ARAZOID, 0, 257, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"BACKSTAB", TXT_THEME_BACKSTAB, 0, 278, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"CHAOS2", TXT_THEME_CHAOS2, 0, 250, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"SHUT_IT", TXT_THEME_SHUT_IT, 0, 261, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"TWINMIX1", TXT_THEME_TWINMIX1, 0, 222, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"UNDER3", TXT_THEME_UNDER3, 0, 246, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"VR2", TXT_THEME_VR2, 0, 255, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"BOG", TXT_THEME_BOG, 0, 212, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"FLOAT_V2", TXT_THEME_FLOAT_V2, 0, 274, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"GLOOM", TXT_THEME_GLOOM, 0, 236, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"GRNDWIRE", TXT_THEME_GRNDWIRE, 0, 228, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"RPT", TXT_THEME_RPT, 0, 275, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"SEARCH", TXT_THEME_SEARCH, 0, 276, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"TRACTION", TXT_THEME_TRACTION, 0, 237, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"WASTELND", TXT_THEME_WASTELND, 0, 242, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#endif +}; + + +/*********************************************************************************************** + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * * + * This routine is used to retrieve a pointer to the base filename for the theme * + * specified. * + * * + * INPUT: theme -- The theme number to convert into a base filename. * + * * + * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * + * theme number is invalid, then a pointer to "No Theme" is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Base_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(_themes[theme].Name); + } + return("No theme"); +} + + +/*********************************************************************************************** + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * * + * This is the default constructor for the theme class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ThemeClass::ThemeClass(void) : + Current(-1), + Score(THEME_NONE), + Pending(THEME_NONE) +{ +} + + +/*********************************************************************************************** + * ThemeClass::Full_Name -- Retrieves the full score name. * + * * + * This routine will fetch and return with a pointer to the full name of the theme * + * specified. * + * * + * INPUT: theme -- The theme to fetch the full name for. * + * * + * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * + * EMS memory. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Full_Name(ThemeType theme) const +{ + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + return(Text_String(_themes[theme].Fullname)); + } + return(NULL); +} + + +/*********************************************************************************************** + * ThemeClass::AI -- Process the theme engine and restart songs. * + * * + * This is a maintenance function that will restart an appropriate theme if the current one * + * has finished. This routine should be called frequently. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 01/23/1995 JLB : Picks new song just as it is about to play it. * + *=============================================================================================*/ +void ThemeClass::AI(void) +{ + if (SampleType && !Debug_Quiet) { + if (ScoresPresent && Options.ScoreVolume != 0 && !Still_Playing() && Pending != THEME_NONE) { + + /* + ** If the pending song needs to be picked, then pick it now. + */ + if (Pending == THEME_PICK_ANOTHER) { + Pending = Next_Song(Score); + } + + /* + ** Start the song playing and then flag it so that a new song will + ** be picked when this one ends. + */ + Play_Song(Pending); + Pending = THEME_PICK_ANOTHER; + } + Sound_Callback(); + } +} + + +/*********************************************************************************************** + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * * + * use this routine to figure out what song number to play. It examines the option settings * + * for repeat and shuffle so that it can return the correct value. * + * * + * INPUT: theme -- The origin (last) index. The new value is related to this for all but * + * the shuffling method of play. * + * * + * OUTPUT: Returns with the song number for the next song to play. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * + *=============================================================================================*/ +ThemeType ThemeClass::Next_Song(ThemeType theme) const +{ + if (theme == THEME_NONE || theme == THEME_PICK_ANOTHER || (theme != THEME_QUIET && !_themes[theme].Repeat && !Options.IsScoreRepeat)) { + if (Options.IsScoreShuffle) { + + /* + ** Shuffle the theme, but never pick the same theme that was just + ** playing. + */ + ThemeType newtheme; + do { + newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); + } while (newtheme == theme || !Is_Allowed(newtheme)); + theme = newtheme; + + } else { + + /* + ** Sequential score playing. + */ + do { + theme++; + if (theme > THEME_LAST) { + theme = THEME_FIRST; + } + } while (!Is_Allowed(theme)); + } + } + return(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * * + * This routine will cause the current song to fade and the specified song to start. This * + * is the normal and friendly method of changing the current song. * + * * + * INPUT: theme -- The song to start playing. If -1 is passed in, then just the current song.* + * is faded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Queue_Song(ThemeType theme) +{ + /* + ** If there is no score file present, then abort. + */ + if (!ScoresPresent) return; + + /* + ** If there is no sound driver or sounds have been specifically + ** turned off, then abort. + */ + if (SampleType == 0 || Debug_Quiet) return; + + /* + ** If the current score volumne is set to silent, then there is no need to play the + ** specified theme. + */ + if (Options.ScoreVolume == 0) return; + + /* + ** If the pending theme is available to be set and the specified theme is valid, then + ** set the queued theme accordingly. + */ + if (Pending == THEME_NONE || Pending == THEME_PICK_ANOTHER || theme == THEME_NONE || theme == THEME_QUIET) { + Pending = theme; + if (Still_Playing()) { + Fade_Sample(Current, THEME_DELAY); + } + } +} + + +/*********************************************************************************************** + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * * + * This routine is used to start the specified theme playing right now. If there is already * + * a theme playing, it is cut short so that this one may start. * + * * + * INPUT: theme -- The theme number to start playing. * + * * + * OUTPUT: Returns with the sample play handle. * + * * + * WARNINGS: This cuts off any current song in a abrupt manner. Only use this routine when * + * necessary. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Play_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume != 0) { + Stop(); + Score = theme; + if (theme != THEME_NONE && theme != THEME_QUIET) { + StreamLowImpact = true; + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); + StreamLowImpact = false; + } + } + return(Current); +} + + +/*********************************************************************************************** + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * * + * This routine will construct (into a static buffer) a filename that matches the theme * + * number specified. This constructed filename is returned as a pointer. The filename will * + * remain valid until the next call to this routine. * + * * + * INPUT: theme -- The theme number to convert to a filename. * + * * + * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 05/09/1995 JLB : Theme variation support. * + *=============================================================================================*/ +char const * ThemeClass::Theme_File_Name(ThemeType theme) +{ + static char name[_MAX_FNAME+_MAX_EXT]; + + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); + return((char const *)(&name[0])); + } + + return(""); +} + + +/*********************************************************************************************** + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * * + * Use this routine to calculate the length of the song. The length is determined by * + * reading the header of the song and dividing the sample rate into the sample length. * + * * + * INPUT: theme -- The song number to examine to find its length. * + * * + * OUTPUT: Returns with the length of the specified theme. This length is in the form of * + * seconds. * + * * + * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Track_Length(ThemeType theme) const +{ + if ((unsigned)theme < THEME_COUNT) { + return(_themes[theme].Duration); + } + return(0); +} + + +/*********************************************************************************************** + * ThemeClass::Stop -- Stops the current theme from playing. * + * * + * Use this routine to stop the current theme. After this routine is called, no more music * + * will play until the Start() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Stop(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; + } +} + + +void ThemeClass::Suspend(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Pending = Score; + Score = THEME_NONE; + } +} + + +/*********************************************************************************************** + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * * + * Use this routine to determine if music is still playing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the music still audible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Still_Playing(void) const +{ + if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { + return(Sample_Status(Current)); + } + return(false); +} + + +/*********************************************************************************************** + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * * + * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * + * if the scenario is too early for that score, or the score only is allowed in special * + * cases. * + * * + * INPUT: index -- The score the check to see if it is allowed to play. * + * * + * OUTPUT: Is the specified score allowed to play in the normal score playlist? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 07/04/1996 JLB : Handles alternate playlist checking. * + *=============================================================================================*/ +bool ThemeClass::Is_Allowed(ThemeType index) const +{ + if ((unsigned)index >= THEME_COUNT) return(true); + + /* + ** If the theme is not present, then it certainly isn't allowed. + */ + if (!_themes[index].Available) return(false); + + /* + ** Only normal themes (playable during battle) are considered allowed. + */ + if (!_themes[index].Normal) return(false); + + /* + ** If the theme is not allowed to be played by the player's house, then don't allow + ** it. If the player's house hasn't yet been determined, then presume this test + ** passes. + */ + if (PlayerPtr != NULL && ((1 << PlayerPtr->ActLike) & _themes[index].Owner) == 0) return(false); + + /* + ** If the scenario doesn't allow this theme yet, then return the failure flag. The + ** scenario check only makes sense for solo play. + */ + if (Session.Type == GAME_NORMAL && Scen.Scenario < _themes[index].Scenario) return(false); + + /* + ** Since all tests passed, return with the "is allowed" flag. + */ + return(true); +} + + +/*********************************************************************************************** + * ThemeClass::From_Name -- Determines theme number from specified name. * + * * + * Use this routine to convert a name (either the base filename of the theme, or a partial * + * substring of the full name) into the matching ThemeType value. Typical use of this is * + * when parsing the INI file for theme control values. * + * * + * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * + * theme name. * + * * + * OUTPUT: Returns with the matching theme number. If no match could be found, then * + * THEME_NONE is returned. * + * * + * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * + * the full theme name, the comparison is case sensitive. * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +ThemeType ThemeClass::From_Name(char const * name) const +{ + if (name && strlen(name) > 0) { + /* + ** First search for an exact name match with the filename + ** of the theme. This is guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (stricmp(_themes[theme].Name, name) == 0) { + return(theme); + } + } + + /* + ** If the filename scan failed to find a match, then scan for + ** a substring within the full name of the score. This might + ** yield a match, but is not guaranteed to be unique. + */ + for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { + return(theme); + } + } + } + + return(THEME_NONE); +} + + +/*********************************************************************************************** + * ThemeClass::Scan -- Scans all scores for availability. * + * * + * This routine should be called whenever a score mixfile is registered. It will scan * + * to see if any score is unavailable. If this is the case, then the score will be so * + * flagged in order not to appear on the play list. This condition is likely to occur * + * when expansion mission disks contain a different score mix than the release version. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Scan(void) +{ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); + } +} + + + +/*********************************************************************************************** + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * * + * This is an override function used to set a particular theme's initial scenario and * + * owner values. Typically, the rules control file will be the source of calling this * + * routine. * + * * + * INPUT: theme -- The theme to set these override values for. * + * * + * scenario -- The first scenario when this theme becomes available on the play list. * + * * + * owners -- A bitfield representing the owners allowed to play this song. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Set_Theme_Data(ThemeType theme, int scenario, int owners) +{ + if (theme != THEME_NONE) { + _themes[theme].Normal = true; + _themes[theme].Scenario = scenario; + _themes[theme].Owner = owners; + } +} + diff --git a/CODE/THEME.H b/CODE/THEME.H new file mode 100644 index 0000000..6020578 --- /dev/null +++ b/CODE/THEME.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/THEME.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : THEME.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef THEME_H +#define THEME_H + +class ThemeClass +{ + private: + static char const * Theme_File_Name(ThemeType theme); + + int Current; // Handle to current score. + ThemeType Score; // Score number currently being played. + ThemeType Pending; // Score to play next. + + typedef struct { + char const * Name; // Filename of score. + int Fullname; // Text number for full score name. + int Scenario; // Scenario when it first becomes available. + int Duration; // Duration of theme in seconds. + bool Normal; // Allowed in normal game play? + bool Repeat; // Always repeat this score? + bool Available; // Is the score available? + int Owner; // What houses are allowed to play this theme (bit field)? + } ThemeControl; + + static ThemeControl _themes[THEME_COUNT]; + + enum { + THEME_DELAY=TIMER_SECOND + }; + + public: + ThemeClass(void); + + ThemeType From_Name(char const * name) const; + ThemeType Next_Song(ThemeType index) const; + ThemeType What_Is_Playing(void) const {return Score;} + bool Is_Allowed(ThemeType index) const; + bool Is_Regular(ThemeType theme) const {return(theme != THEME_NONE && _themes[theme].Normal);} + char const * Base_Name(ThemeType index) const; + char const * Full_Name(ThemeType index) const; + int Max_Themes(void) const {return THEME_COUNT;} + int Play_Song(ThemeType index); + int Still_Playing(void) const; + int Track_Length(ThemeType index) const; + static void Scan(void); + void AI(void); + void Fade_Out(void) {Queue_Song(THEME_QUIET);} + void Queue_Song(ThemeType index); + void Set_Theme_Data(ThemeType theme, int scenario, int owners); + void Stop(void); + void Suspend(void); +}; + +#endif diff --git a/CODE/TLINK.CFG b/CODE/TLINK.CFG new file mode 100644 index 0000000..a0b168b --- /dev/null +++ b/CODE/TLINK.CFG @@ -0,0 +1,4 @@ + /Lbc402\lib;d:\library;d:\bc4\lib;d:\rtlink + /c + /E + /Tde diff --git a/CODE/TODO.TXT b/CODE/TODO.TXT new file mode 100644 index 0000000..f48a18c --- /dev/null +++ b/CODE/TODO.TXT @@ -0,0 +1,463 @@ +MASTER 11/7/96 + +*Skirmish and cap flag carry over bug. + +*Speed boots to anti-aircraft flag. + +*allies reveal fakes + +Badguy -vs- soviet repair glitch + +Building one too many harvester. + +force music on the title page and credit scroll + +Add mouse dead zone around key buttons + +*Harvester getting stuck against a unit that is perpetually trying to scatter (stutter rotating). + +AI base is unit heavy. Should concentrate more on air defenses and selling off old buildings. + +Attack buildings to discourage from attacking base defenses. + +*Longbow not going onto pad even if empty. + +Tanya should laugh when exiting barracks. + +*If dog drops onto impassable terrain, then fake it into a passable cell instead. + +*Keep record of previously existing building to use for computer prerequisite checks. + +*Bias the base center away from the edge of the map. + +*Add "delay before beginning the first attack" to the rules for ai players. + +Add difficulty slider to the skirmish mode. + +Driving over weapons factory door. + +Give rules control to some of the house check and ai function pairs. + +Control engineer repair of bridge by rules file. + +Combine chrono effects into a single event. + +If near capacity, then build a silo. + +HouseClass::Attacked is not multiplay friendly + +Use the return value from the house AI function to regulate the interval between their processing. + +Build MCV (and go into hunt) when no construction yard left. + +Credit scroll scraggs title page. + +Retreat mode with LST + +*AI units sometimes drive to upper left corner + +Sneak peek after intro replay. + +Don't allow more than two allies together (if humans only). + +Fix the Where_To_Go() function so that it prefers under protected zones and zones that are +closest to the enemy. + +Computer should cause blocking units to move before giving up placing a building. + +Computer should avoid building against the map edge. + +Special build emphasis on the zone that gets attacked. + + +Tournament rules: +> Countdown timer -- game ends with winner who has higher score. If not +counting down, it should count upward. +> Display score instead of kills on map display. +> Handicap assigned to each player? +> Invulnerable harvester option. Will not be automatically fired +upon until all refineries destroyed. Also, it cannot overrun +infantry until then either. +> Allow disabling of ally option. +> Ally should be limited to only one other player. Say "Changes allegience to ???" or +"Turncoats from ??? to ???" + +Clicks on unit production (especially infantry) should be +queued up (with a number). + +C&C2: Ideas +> Some advantage to the civilian or mutant villages so that they +won't likely be destroyed in a multiplayer game. Maybe they plant +Tiberium. +> Tiberium should spill from a destroyed harvester. +> Multiplayer maps should have more than one entrance into the +likely starting areas for the players. This is needed to make +complete blocking of access to the base very difficult. + +Audio control slider should have a step rate + +Can harvester detect a stealth tank on the radar map as long as the +stealth tank is not over tiberium? + +Civilians should run from civilian buildings. + +When ordering to move to a tree, the units don't move at all. + +Transport should not scatter. + +Harvesters should return to refinery instead of scattering. If they are already +unloading or heading back to refinery, then they shouldn't scatter at all. + +Official maps should be brighter color. + +Detect counterfiet maps. + +Add KN bits for Num, Scroll, and Caps lock toggle keys. This will give more +information to the ToASCII function for proper keyboard conversion. + +Fire coord for AA guns are off. + +Draw parachute before drawing attached object. + +Patrol to waypoint should clear out any prexisting target so the team members wont +start to run back to their old target. + +force bases on if cap flag checked and visa versa. + +Boats are not giving out-of-range cursor when selecting a target? Make this controllable +through the rules.ini file. + +Buildings are all scanned during every foot::percellprocess as well as any +time a building is drawn that is spied. + +Force attack for mine layer could be "lay mine". + +Need scenario.Start(), Restart(), Reset() functions to control the campaign start, +next mission start, and restart current mission over functions. +Break scenario data into two groups. The group that needs to be preserved between scenarios +and that which is set by the scenario INI file. Both groups should be saved in the saved game. +The IsTanyaDead and SaveTanya should be in there. There should be two copies of the section +that is preserved between scenarios. This will allow restarting the scenario. + +Encrypt the message digest for scenario files. Detect whether scenario is custom or +original version when loading? + +The overlap list could be implemented as a sparse array. Each cell would have a pointer +to the overlap list handler. when no overlaps exist, the handler gets deleted. + +Optimize INI scanning code. Store only CRC if never going to be written back out? Store +entry/section CRC values as a sorted array to allow binary search? Use strcmp instead of +stricmp? + +Thief in hunt mode should go after structures. + +Choose computer to attack based on force comparison? If stronger by certain +amount, then attack. If weaker than certain amount, then build. + +Progress bargraph on game save/load. + +Longbow shows up for airstrip production? + +Suggest that these get added to the westwood library: _rotl(), ARRAY_SIZE, min(), max(), abs(), Bound() + +Put saving/load of Special and Options into save_misc_values and remove from +save/load mplayer_values function. + +Why does SessionClass::Save seem to save the same values that save_mplayer_values +and save_misc_values does. + +Flying debris from an exploding building or vehicle. + +The UnitToTeleport and TimeQuake variables might need to be save/loaded with the game. + +Each class should have an equivalent Build_INI_Entry routine. A generic (save all objects) routine +could then be written. +...or... Need a INI-Put_String with variable parameters. + +Add random animation logic for buildings, vessels, and vehicles. + +If directed to move to the immediately adjacent behind cell, then just back up into it. Anything +more distant, rotate to normal orientation. + +The MPHType seems obsolete. Just a simple "leptons per game frame" speed value seems +more appropriate. + +Crosses line triggers may need special handling in some kind of list. They need to have a cell +attached so that when a unit moves, it can quickly determine if it causes one of these +trigger types to spring. This is the only trigger type that requires a cell data. + +Need a more efficient way of detecting when a unit crosses line or enters zone trigger event. + +Crack the radio messages into separate handler functions e.g., Radio_Run_Away(), Radio_Over_Out() + +Take close look at findpath logic. Incorporate; +> Settle for simple path calc when first starting and distant from destination. +> Escilate path searching when blockage occurs. +> Reset search aggressiveness when new navcom is assigned. +> Use common routine when path is block and "What now?" needs to be answered. + +Computer ships SHOULD fire on non-combat buildings when in area guard mode. + +Team AI should have a delay between calls. + +Hires_Load may be obsolete? + +Crosses line trigger is causing problems. + +Check for out of disk space during save. Delete partial save file. + +V04, V05, V07 have more anim frames. + +Put INI reading code within the warhead class object. The warhead object will be +created by the weapon INI info. It will be named but otherwise undefined. The warhead +reading code will look up the warhead info based on the name and then fill in the +values. + +Add override flag so unit name can always be printed over unit? Possibly have +full name override for pop up help text as well. + + +Adjust_Threat should only make an adjustment if the unit moves to a new +cell, is unlimboed, or limboed. + +New to Borland 5.0! + namespaces + mutable + bool + standard template library + + + +kevin_aguilar + +TOFIX -- fix for Red Alert +OLD -- obsolete for Red Alert, but applicable for Tiberiun Sun + + +While moving, scan for targets within range (turret vehicles only) so that +firing may occur while moving. + +Keep track of the amount of damage (rate) received by a building. This +is used to determine if the building should be sold if it is taking +too much damage. + +Send attack helicopter to harrass the enemy. + +Don't consider SAM sites to be weapon equipped when determining retaliation +logic. + +Abort existing production in emergency situations. e.g., when power is low, base +is under attack, or no refineries left. + +Break down AI into request and priority. Then after building list, try to perform +all actions that are highest priority without being mutually exclusive. + +Record the current state of the house. Possible states could be "buildup", "under attack", +"idle". The AI will adjust itself depending on the state of the base. + + +When damaged and previous target is outside of range but antagonist is much closer, +then attack antagonist. Recuit assistance from any nearby friendly units that +are also far from their designated target or are otherwise unoccupied. + +Hall of fame data to record more information. Histogram of progress? Records +the number of wins/losses/reloads/saves, etc. Time they play. + +Colors on team selection brackets. + +Build base defense toward last enemy, but as a ring from the center of the base. + +Build infrastructure away from enemy threat. + +If low on money also sell of some base defense if there is just too much of it. + + +Setup program crashes in windows 95. Error is "stack overflow on interrupt". + +MCV not deploying if it starts in correct rotated position. + +Score timer needs fixing. + +33. SCG03EA: Won by capturing his Hand of Nod, and then +building engineers from it to capture the rest of the base. +When something like that happens, the AI should recall most or all of its forces for defense. + +Distribute damage applied to refinery to any attached harvester as well. + +If no more tiberium or no more refinery, then rampage harvester. + +Fix tree crumbling. + +Add a hover command to the mission list so that the helicopter can hover (forever). + +Prevent using the placement cursor to find stealth tanks. + +Airstrip should animation flashing lights when an aircraft is approaching only. + +Apply low power damage only when the "need more power" message is played. + +Click and drag on the radar map? + +If engineer has its target snatched out from under him, then assign a hunt +mission so that it will pick another target (applied to computer non-team only). + +If hit, then respond even if on hunt. + +Optimize the update threat function to occur ever cell rather than every time the object +is marked up or down. + +If a building is captured, cause a defend base action. + +When full of storage, stop the harvester. Don't enter refinery if there is +no storage available. If less than 350 credit storage available, pause. + +Darken "repair" and "sell" if there are no buildings available. + +If a computer controlled unit enters a transport square, then load it up. + +Take advantage of archive target for aircraft. Returns to location after +attacking. + +Aircraft dying on ground should have crew survivor. + +Helicopters puff smoke before crashing. Handles the transport helicopter. + +After evac, send the helicopter back to the game? Keep track of number +of civilians evacutated? Tie a trigger event to this number? + +Don't reload the SCUD while it is moving. Suspend the reload delay +by incrementing the countdown timer. + +Need accurate mouse tracking so that it is possible to move infantry into the same square +as other infantry. + +Tighter individual infantry AI. Self defence priority. + +Bringing a wall to its last damage level should be much easier +than completely destroying it. This will allow infantry to +enter through the holes in a wall more easily than the vehicles +would. + +Need a function that will return the cell number that either the unit is +currently at (if it isn't moving) or the one that it will be at the +moment is needs to pull more values off of the path list. This function +will be used in the findpath draw routine. This is necessary because +infantry and vehicles use a different method of movement and path list +processing. + +Add flag so that bullet (or warhead) knows whether to do explosion area +effect damage or whether to just damage the target. SAM missiles would +just damage the target (maybe APDS as well). + +Add some function to the facing class that automatically convert the +return value to 1/32 or 1/8 values. + +Misc. strange Watcom errors/quirks +> Internal compiler error 57 +> Abnormal program termination: Array bounds exception CS:EIP = 2BCDh:000009A6h +> Created a 100 meg precompiled header file. +> read error on work file: error code = 1 +> "const" bitfields show up as 32bit numbers in debugger. +> couldn't add a specifier (foobar::) to a friend class declaration +> Inline assembly can generate wrong code for parameter setup. +> casting doesn't follow precidence rules. +> CTRL-C to break out of compile sometimes crashes. Here is an error. + Invalid date "" + run-time error R6000 + - stack overflow +> Unable to find carve memory block. +> Can't overload inline assembly routines?!?! #pragma doesn't specify parameter + type information so that it can resolve properly? +> Access Violation abort and register dump when compiling display.cpp + WPP386 caused an invalid page fault in + module at 0000:4f435c4f. + Registers: + EAX=8161e314 CS=014f EIP=4f435c4f EFLGS=00010246 + EBX=0059f798 SS=0157 ESP=005900f0 EBP=00590110 + ECX=00590198 DS=0157 ESI=005901dc FS=0d97 + EDX=bff76648 ES=0157 EDI=005901c0 GS=0000 + Bytes at CS:EIP: + + +Pass range to bullet constructor. It will use this value to limit the +fuel and scatter calculations. + +Library allocation and input system should route through a global +base class pointer. This allows changing some aspect of these system +by using simple derived classes. Example: The memory system could keep +track of what and why allocations take place. The input system could be +intercepted to playback a recorded demo. Etc. + +Create a standard format for dissemenating technology from a project. This form +would have a concise technology synopsis at the begining. This would be +followed by greater detail explanations. + +Create inline versions of Swap_LONG, Reverse_WORD, and Reverse_LONG. + +Straighten out the ScrollGadget / SliderGadgets. It shouldn't "new" +the arrow gadgets, but rather either have them as integral objects +or separate them completely and create a special combination class +in a similar fashion to the list class. + +Create a number<->string handler for the library. String data files (blocks) are registered +with the handler. All library routines that can take const strings could then also take +string numbers. They would use the handler to extract the correct string pointer. + +Create a utility that loads the palette source pictures and then generates all the morph +palette tables. This is a C&C custom utility. + +Need a smarter team movement logic for the computer so that infantry do not clump into +one cell unless absolutely necessary. + +Create a function that submits a "pending object" to the map I/O handler. This handles +setting up the Zone cell, pseudocursor sizes, as well as all the pending pointers. + +If fire shard lands in the exact sub-cell position of an infantry unit, then it will automatically +catch fire. + +If cell targets were simple object types, then they can exists in a sort of virtual cell existence +when specific targeting of a cell is desired. This will allow all target values to be converted +into short object pointers. There would be a limit to the number of cells that could be targeted +simultaneously. Multiple units targeting the same cell would be allowed. Each pseudo-cell would +keep a record of the number of simultaneous targets upon itself. + +Add a "new" and "delete" function for cells. It would merely return pointer to appropriate spot in +the cell array, but does allow the constructor/destructor logic to function as well as allow the +possibility of arbitrary sized maps. These cell objects would not be the same as the virtual cell +objects used for targeting purposes, although each targeting cell object has a real cell object it +corresponds to. + +Need a utility that will calculate fading and translucent tables from a specified palette. +It will create a data file of the created table(s). This utility should be passed a +source LBM (for palette file) and a destination filename and the operation to perform. +It could also be passed a text file that elaborated the process to perform. + +Example: +[All] ; Options that perform to all colors unless overridden. +Percent=70 ; Percent to fade from original color to destination color. +[1] ; Process for color table entry #1 in original palette. +FadeTo=13 ; Fades to color #13. +Percent=70 ; Fades 70% of the way from color 1 to color 13. +Uses=240-255 ; Only allowed to pick from these colors as final color. + +Objects should move into and out of the logic processing list according to what state they are in. +A unit that is just sitting and waiting for its timer to count down shouldn't be processed in the +regular list like the other units would be. Example: A unit is sitting there with no order so it +is out of the normal logic loop. If it gets selected as a target then it must flash. The +"isflashing" flag gets set and the object gets placed into the logic list. When the flashing +expires, a simple (union?) check of the logic activation bits is performed. If any of the bits are +still on, then the object remains in the logic list, otherwise it is removed. A simple countdown +timer processor is needed for those cases where the only reason the unit is out of the logic loop +is if it is waiting for the next AI command to occur. + +search for ",[^ \t$]" and " (" and "}\n[ \t]else {" and "for(" and "switch(" and "\*." and "\t\*-" and "return(" +){ and }else{ + +Separate arming value for each weapon. This will allow weapons to fire at will according to +their fire rates. This will also require a What_Weapon_Should_I_Fire_Now() function to +help resolve the ambiguous situations. + +Create a project format style guide for C&C Tiberian Sun. Put summary into some header file. + +Need very specific question functions for the objects. Wrapper functions (radio messages) coordinate +these sub functions to perform tasks. \ No newline at end of file diff --git a/CODE/TOGGLE.CPP b/CODE/TOGGLE.CPP new file mode 100644 index 0000000..1892111 --- /dev/null +++ b/CODE/TOGGLE.CPP @@ -0,0 +1,201 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TOGGLE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TOGGLE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : February 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "toggle.h" + + +/*********************************************************************************************** + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * * + * This is the normal constructor for toggle buttons. A toggle button is one that most * + * closely resembles the Windows style. It has an up and down state as well as an on * + * and off state. * + * * + * INPUT: id -- ID number for this button. * + * * + * x,y -- Pixel coordinate of upper left corner of this button. * + * * + * w,h -- Width and height of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ToggleClass::ToggleClass(unsigned id, int x, int y, int w, int h) : + ControlClass(id, x, y, w, h, LEFTPRESS|LEFTRELEASE, true), + IsPressed(false), + IsOn(false), + IsToggleType(false) +{ +} + + +/*********************************************************************************************** + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * * + * This routine will turn the button on. The button will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_On(void) +{ + IsOn = true; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * * + * This routine will turn the toggle button "off". It will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_Off(void) +{ + IsOn = false; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Action -- Handles mouse clicks on a text button. * + * * + * This routine will process any mouse or keyboard event that is associated with this * + * button object. It detects and flags the text button so that it will properly be drawn * + * in a pressed or raised state. It also handles any toggle state for the button. * + * * + * INPUT: flags -- The event flags that triggered this button. * + * * + * key -- The keyboard code associated with this event. Usually this is KN_LMOUSE * + * or similar, but it could be a regular key if this text button is given * + * a hotkey. * + * * + * OUTPUT: Returns whatever the lower level processing for buttons decides. This is usually * + * true. * + * * + * WARNINGS: The button is flagged to be redrawn by this routine. * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + * 02/02/1995 JLB : Left press doesn't get passed to other buttons now * + *=============================================================================================*/ +int ToggleClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there are no action flag bits set, then this must be a forced call. A forced call + ** must never actually function like a real call, but rather only performs any necessary + ** graphic updating. + */ + bool overbutton = ((unsigned)(Get_Mouse_X() - X) < Width && (unsigned)(Get_Mouse_Y() - Y) < Height ); + if (!flags) { + if (overbutton) { + if (!IsPressed) { + IsPressed = true; + Flag_To_Redraw(); + } + } else { + if (IsPressed) { + IsPressed = false; + Flag_To_Redraw(); + } + } + } + + /* + ** Handle the sticky state for this gadget. It must be processed here + ** because the event flags might be cleared before the action function + ** is called. + */ + Sticky_Process(flags); + + /* + ** Flag the button to show the pressed down imagery if this mouse button + ** was pressed over this gadget. + */ + if (flags & LEFTPRESS) { + IsPressed = true; + Flag_To_Redraw(); + flags &= ~LEFTPRESS; + ControlClass::Action(flags, key); + key = KN_NONE; // erase the event + return(true); // stop processing other buttons now + } + + if (flags & LEFTRELEASE) { + if (IsPressed) { + if (IsToggleType && overbutton) { + IsOn = (IsOn == false); + } + IsPressed = false; + Flag_To_Redraw(); + } else { + flags &= ~LEFTRELEASE; + } + } + + /* + ** Do normal button processing. This ends up causing the button's ID number to + ** be returned from the controlling Input() function. + */ + return(ControlClass::Action(flags, key)); +} + + diff --git a/CODE/TOGGLE.H b/CODE/TOGGLE.H new file mode 100644 index 0000000..d7fbc13 --- /dev/null +++ b/CODE/TOGGLE.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TOGGLE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TOGGLE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TOGGLE_H +#define TOGGLE_H + +#include "control.h" + +/* +** This class handles gadgets that behave like the Windows buttons. That is, once the mouse +** button is clicked over them, they capture the focus until the mouse button is released. +** They have a different imagery for the pressed and released states. They only recognize +** a valid selection when the mouse button is release while over the button. +*/ +class ToggleClass : public ControlClass +{ + public: + ToggleClass(unsigned id, int x, int y, int w, int h); + void Turn_On(void); + void Turn_Off(void); + + /* + ** Is this button in a pressed down state? This occurs when the mouse is clicked on the + ** button and the mouse is still being held down. + */ + unsigned IsPressed:1; + + /* + ** This is the button on/off state. Sometimes a button that is "on" has a different + ** imagery than one that is "off". If the on/off state is not necessary, then just + ** ignore this flag. + */ + unsigned IsOn:1; + + /* + ** If this button can be turned "on" or "off", then this flag should be set to + ** true. Sometimes a button needs to display its on/off state. In the render routine, + ** examine the IsOn flag and display accordingly. If this flag is false, then the + ** IsOn flag will not be changed, regardless of button clicking. + */ + unsigned IsToggleType:1; + + protected: + + virtual int Action(unsigned flags, KeyNumType &key); +}; + +#endif diff --git a/CODE/TOOLTIP.CPP b/CODE/TOOLTIP.CPP new file mode 100644 index 0000000..b629d3c --- /dev/null +++ b/CODE/TOOLTIP.CPP @@ -0,0 +1,296 @@ +/* +** 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 . +*/ + +// ToolTip.cpp + +#include "function.h" +#include "ToolTip.h" +#include "IconList.h" + +//#include "WolDebug.h" + +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ); +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ); + +//*********************************************************************************************** +ToolTipClass::ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, + bool bRightAlign /* = false */, bool bIconList /*= false */ ) + : pGadget( pGadget ), xShow( xShow ), yShow( yShow ), next( NULL ), bShowing( false ), bIconList( bIconList ), + bRightAlign( bRightAlign ) + +{ + if( szText ) + { + if( strlen( szText ) > TOOLTIPTEXT_MAX_LEN ) + strcpy( szTip, "Tooltip too long!" ); + else + strcpy( szTip, szText ); + } + else + *szTip = 0; + + Set_Font( TypeFontPtr ); + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShow = String_Pixel_Width( szTip ) + 2; + hShow = 11; + + if( !bIconList ) + { + pSaveRect = new char[ wShow * hShow ]; // Else it is reallocated on every draw. + if( bRightAlign ) + this->xShow -= wShow; + } + else + pSaveRect = NULL; + + // bIconList is true if tooltips appear for individual line items in an iconlist. + // szText in this case is ignored. + // yShow is the y position of the top row's tooltip - other rows will be offset from here. +} + +//*********************************************************************************************** +ToolTipClass* ToolTipClass::GetToolTipHit() +{ + // Returns 'this' if the mouse is over gadget bound to tooltip. + // Otherwise calls the same function in the next tooltip in the list of which *this is a part. + if( bGadgetHit() ) + return this; + else if( next ) + return next->GetToolTipHit(); + else + return NULL; +} + +//*********************************************************************************************** +bool ToolTipClass::bGadgetHit() const +{ + // Returns true if the mouse is currently over the gadget to which *this is bound. + int x = Get_Mouse_X(); + int y = Get_Mouse_Y(); + return ( x > pGadget->X && x < pGadget->X + pGadget->Width && y > pGadget->Y && y < pGadget->Y + pGadget->Height ); +} + +//*********************************************************************************************** +void ToolTipClass::Move( int xShow, int yShow ) +{ + bool bRestoreShow = false; + if( bShowing ) + { + bRestoreShow = true; + Unshow(); + } + this->xShow = xShow; + if( !bIconList ) + { + if( bRightAlign ) + this->xShow -= wShow; + } + this->yShow = yShow; + if( bRestoreShow ) + Show(); +} + +//*********************************************************************************************** +void ToolTipClass::Show() +{ + if( !bShowing ) + { + Set_Font( TypeFontPtr ); + int xShowUse = xShow, yShowUse, wShowUse; + const char* szTipUse; + if( !bIconList ) + { + yShowUse = yShow; + wShowUse = wShow; + szTipUse = szTip; + } + else + { + IconListClass* pIconList = (IconListClass*)pGadget; + iLastIconListIndex = pIconList->IndexUnderMouse(); + if( iLastIconListIndex < 0 ) + { + // Nothing to show. + bShowing = true; + return; + } + yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); + szTipUse = pIconList->Get_Item_Help( iLastIconListIndex ); + if( !szTipUse || *szTipUse == 0 ) + { + // Nothing to show. + bShowing = true; + bLastShowNoText = true; + return; + } + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShowUse = String_Pixel_Width( szTipUse ) + 2; + if( bRightAlign ) + xShowUse -= wShowUse; + delete [] pSaveRect; + pSaveRect = new char[ wShowUse * hShow ]; + bLastShowNoText = false; + xLastShow = xShowUse; + yLastShow = yShowUse; + wLastShow = wShowUse; + } + + // Save rect about to be corrupted. + Hide_Mouse(); + SaveSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + // Draw text. + //Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), ColorRemaps[ PCOLOR_BROWN ].Color, TPF_TYPE ); //TPF_DROPSHADOW ); + Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), BLACK, TPF_TYPE ); //TPF_DROPSHADOW ); + // Draw bounding rect. +// LogicPage->Draw_Rect( xShowUse, yShowUse, xShowUse + wShowUse - 1, yShowUse + hShow - 1, ColorRemaps[ PCOLOR_GOLD ].Color ); + Draw_Box( xShowUse, yShowUse, wShowUse, hShow, BOXSTYLE_BOX, false ); + Show_Mouse(); + bShowing = true; + } +} + +//*********************************************************************************************** +void ToolTipClass::Unshow() +{ + if( bShowing ) + { + int xShowUse, yShowUse, wShowUse; + if( !bIconList ) + { + xShowUse = xShow; + wShowUse = wShow; + yShowUse = yShow; + } + else + { + if( iLastIconListIndex == -1 || bLastShowNoText ) + { + // Nothing to restore. + bShowing = false; + return; + } + // (Can't rely on iconlist being the same as when Show() occurred.) +// IconListClass* pIconList = (IconListClass*)pGadget; +// yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); +// const char* szTipUsed = pIconList->Get_Item_Help( iLastIconListIndex ); +// if( !szTipUsed || *szTipUsed == 0 ) +// { +// // Nothing to restore. +// bShowing = false; +// return; +// } +// Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. +// wShowUse = String_Pixel_Width( szTipUsed ) + 2; +// if( bRightAlign ) +// xShowUse -= wShowUse; + xShowUse = xLastShow; + yShowUse = yLastShow; + wShowUse = wLastShow; + } + Hide_Mouse(); + RestoreSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + Show_Mouse(); + bShowing = false; + } +} + +//*********************************************************************************************** +bool ToolTipClass::bOverDifferentLine() const +{ + // bIconList must be true if this is being used. + // Returns true if the iconlist line that the mouse is over is different than the last time Show() was called. + return ( ((IconListClass*)pGadget)->IndexUnderMouse() != iLastIconListIndex ); +} + +//*********************************************************************************************** +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ) +{ + // Saves a rect of the LogicPage DirectDraw surface to pBits. +// if( wRect * hRect > iBufferSize ) +// { +// debugprint( "SaveSurfaceRect failed.\n" ); +// return false; +// } + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + const char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + const char* pSurf = pLineSurf; + char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSave++ = *pSurf++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "SaveSurfaceRect Could not lock surface.\n" ); + return false; + } +} + +//*********************************************************************************************** +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ) +{ + // Copies a saved rect of bits back to the LogicPage DD surface. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + const char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + char* pSurf = pLineSurf; + const char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSurf++ = *pSave++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "RestoreSurfaceRect Could not lock surface.\n" ); + return false; + } +} diff --git a/CODE/TOOLTIP.H b/CODE/TOOLTIP.H new file mode 100644 index 0000000..bd538f1 --- /dev/null +++ b/CODE/TOOLTIP.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +// ToolTip.h + +#ifdef WOLAPI_INTEGRATION + +#ifndef TOOLTIP_H +#define TOOLTIP_H + +#include "Gadget.h" + +#define TOOLTIPTEXT_MAX_LEN 100 + +#define TOOLTIPDELAY 400 // Milliseconds + +class ToolTipClass +{ +public: + ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, bool bRightAlign = false, bool bIconList = false ); + ~ToolTipClass() + { + delete [] pSaveRect; + } + + ToolTipClass* GetToolTipHit(); + void Show(); + void Unshow(); + void Move( int xShow, int yShow ); + bool bOverDifferentLine() const; + + ToolTipClass* next; // Next tooltip in list of which *this is a part. + + GadgetClass* pGadget; // Gadget to which this tooltip is bound. + + bool bRightAlign; + + bool bShowing; + bool bIconList; // True if gadget is iconlist and line-specific tooltips are to be used. + +protected: + bool bGadgetHit() const; + + int xShow; + int yShow; + int wShow; + int hShow; + char szTip[ TOOLTIPTEXT_MAX_LEN + 1 ]; // Text to show as tip. + + char* pSaveRect; + + // Used only if bIconList. + int iLastIconListIndex; + bool bLastShowNoText; + int xLastShow; + int yLastShow; + int wLastShow; +}; + +#endif + +#endif diff --git a/CODE/TRACKER.CPP b/CODE/TRACKER.CPP new file mode 100644 index 0000000..1e32540 --- /dev/null +++ b/CODE/TRACKER.CPP @@ -0,0 +1,134 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TRACKER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TRACKER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/14/96 * + * * + * Last Update : June 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Detach_This_From_All -- Detaches this object from all others. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * Detach_This_From_All -- Detaches this object from all others. * + * * + * This routine sweeps through all game objects and makes sure that it is no longer * + * referenced by them. Typically, this is called in preparation for the object's death * + * or limbo state. * + * * + * INPUT: target -- This object expressed as a target number. * + * * + * all -- Is this object really in truly being removed from the game? The * + * answer would be false if the target was actually a stealth * + * tank that is cloaking. In such a case, the object should be removed * + * from all non-friendly tracking systems, but otherwise left alone. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Detach_This_From_All(TARGET target, bool all) +{ + int index; + if (Target_Legal(target)) { + + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypes.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Units.Count(); index++) { + Units.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Vessels.Count(); index++) { + Vessels.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Aircraft.Count(); index++) { + Aircraft.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Bullets.Count(); index++) { + Bullets.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Infantry.Count(); index++) { + Infantry.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Anims.Count(); index++) { + Anims.Ptr(index)->Detach(target, all); + } + + Map.Detach(target, all); + + Logic.Detach(target, all); + + ChronalVortex.Detach(target); + + /* + ** Removing a trigger type must also remove all triggers that are dependant + ** upon that type. + */ + if (As_TriggerType(target) != NULL) { + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + + if (tp->Class->As_Target() == target) { + Detach_This_From_All(tp->As_Target()); + delete tp; + index--; + } + } + } + + for (index = 0; index < Triggers.Count(); index++) { + Triggers.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypes.Ptr(index)->Detach(target, all); + } + } +} + + + + + diff --git a/CODE/TRIGGER.CPP b/CODE/TRIGGER.CPP new file mode 100644 index 0000000..07cc21e --- /dev/null +++ b/CODE/TRIGGER.CPP @@ -0,0 +1,497 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TRIGGER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TRIGGER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : August 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Or_Make -- Find or create a trigger of the type specified. * + * TriggerClass::As_Target -- Converts trigger to a target value * + * TriggerClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * TriggerClass::Detach -- Detach specified target from this trigger. * + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * TriggerClass::Init -- clears triggers for new scenario * + * TriggerClass::Spring -- Spring the trigger (possibly). * + * TriggerClass::TriggerClass -- constructor * + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * TriggerClass::operator new -- 'new' operator * + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * * + * In the rare (possibly never?) case of requiring a description for this trigger, then * + * just have the model class generate a description and return that. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with an ASCII description of this trigger. * + * * + * WARNINGS: see TriggerTypeClass::Description() * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerClass::Description(void) const +{ + return(Class->Description()); +} + + +/*********************************************************************************************** + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * * + * This routine is called when the trigger has been assigned to a list box. It will * + * display a description of the trigger. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerClass::TriggerClass -- constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::TriggerClass(TriggerTypeClass * trigtype) : + RTTI(RTTI_TRIGGER), + ID(Triggers.ID(this)), + Class(trigtype), + AttachCount(0), + Cell(0) +{ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); +} + + +/*********************************************************************************************** + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * * + * This destructor will update the house blockage value if necessary. No other action need * + * be performed on trigger destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass::~TriggerClass(void) +{ + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_GENERAL) != 0) { + if (LogicTriggerID >= LogicTriggers.ID(this)) { + LogicTriggerID--; + if (LogicTriggerID < 0 && LogicTriggers.Count() == 0) { + LogicTriggerID = 0; + } + } + } + + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_MAP) != 0) { + if (MapTriggerID >= MapTriggers.ID(this)) { + MapTriggerID--; + if (MapTriggerID < 0 && MapTriggers.Count() == 0) { + MapTriggerID = 0; + } + } + } + + if (GameActive && Class->House != HOUSE_NONE && Class->Action1.Action == TACTION_ALLOWWIN) { + if (Houses.Ptr(Class->House)->Blockage) Houses.Ptr(Class->House)->Blockage--; + Houses.Ptr(Class->House)->BorrowedTime = TICKS_PER_SECOND*4; + } + ID = -1; +} + + +/*********************************************************************************************** + * TriggerClass::Init -- clears triggers for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Init(void) +{ + Triggers.Free_All(); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Spring the trigger (possibly). * + * * + * This routine is called when a potential trigger even has occurred. The event is matched * + * with the trigger event needed by this trigger. If the condition warrants, the trigger * + * action is performed. * + * * + * INPUT: event -- The event that is occurring. * + * * + * obj -- If the trigger is attached to an object, this points to the object. * + * * + * cell -- If the trigger is attached to a cell, this is the cell number. * + * * + * forced -- Should the trigger be forced to execute regardless of the event? * + * * + * OUTPUT: bool; Was the trigger sprung? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 JLB : Created. * + * 08/13/1996 JLB : Linked triggers supported. * + *=============================================================================================*/ +bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool forced) +{ + assert(Triggers.ID(this) == ID); + + bool e1 = Class->Event1(Event1, event, Class->House, obj, forced); + bool e2 = false; + bool execute = false; + + /* + ** Forced triggers must presume that the cell parameter is invalid. It then + ** uses the embedded cell value in the trigger as the official location where + ** the trigger will detonate at. + */ + if (forced) { + cell = Cell; + } else { + + /* + ** Determine if the trigger event is considered to have been sprung according to the + ** event control value. This might require that both events be triggered, one event + ** triggered, or either event triggered to activate the trigger action. + */ + switch (Class->EventControl) { + case MULTI_ONLY: + execute = e1; + break; + + case MULTI_AND: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 && e2); + break; + + case MULTI_LINKED: + case MULTI_OR: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 || e2); + break; + } + } + + /* + ** See if the trigger is sprung with a qualifying event. + */ + if (execute || forced) { + + /* + ** Verify that the trigger event should really be sprung. Exceptions + ** would include semi-persistent triggers that don't actually + ** spring until all triggers have sprung. + */ + if (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT) { + + /* + ** Detach ourselves from the object and record that there + ** is one less attachment to keep track of. + */ + if (obj) { + obj->Trigger = NULL; + } + if (cell) { + Map[cell].Trigger = NULL; + } + + /* + ** If we're attached to more objects, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + AttachCount--; + if (AttachCount > 0) { + return(false); + } + } + + /* + ** For linked trigger events, perform the action associated with the matching + ** trigger event. Otherwise perform the action for both events. + */ + bool ok = false; + HousesType hh = Class->House; + if (Class->EventControl == MULTI_LINKED) { + if (e1 || forced) ok |= Class->Action1(hh, obj, ID, cell); + if (e2 && !forced) ok |= Class->Action2(hh, obj, ID, cell); + } else { + + switch (Class->ActionControl) { + case MULTI_ONLY: + ok |= Class->Action1(hh, obj, ID, cell); + break; + + default: + case MULTI_AND: + ok |= Class->Action1(hh, obj, ID, cell); + ok |= Class->Action2(hh, obj, ID, cell); + break; + } + } + if (!IsActive) return(true); + + /* + ** If at least one action was performed, then consider this + ** trigger to have completed and thus will be deleted if + ** necessary. + */ + if (ok) { + #ifdef CHEAT_KEYS + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 17, 11); + MonoArray[DMONO_STRESS].Scroll(); + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 18, 11); + MonoArray[DMONO_STRESS].Set_Cursor(0, 10); + MonoArray[DMONO_STRESS].Printf("%02d:%02d:%02d-%s", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND, Class->IniName); + MonoArray[DMONO_STRESS].Sub_Window(); + #endif + + if (Class->IsPersistant == TriggerTypeClass::VOLATILE || (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT && AttachCount <= 1)) { + Detach_This_From_All(As_Target(), true); + delete this; + return(true); + } else { + + /* + ** Reset event data so that the event will + ** repeat as necessary. + */ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new trigger * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void * TriggerClass::operator new(size_t ) +{ + void * ptr = Triggers.Allocate(); + if (ptr) { + ((TriggerClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * * + * This routine will return a previously allocated trigger object back to the memory * + * pool from which it came. * + * * + * INPUT: pointer -- Pointer to the trigger to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::operator delete(void * pointer) +{ + if (pointer) { + ((TriggerClass *)pointer)->IsActive = false; + } + Triggers.Free((TriggerClass *)pointer); +} + + +/*********************************************************************************************** + * TriggerClass::As_Target -- Converts trigger to a target value * + * * + * Converts the trigger object into a target identifier. * + * * + * INPUT: none * + * * + * OUTPUT: TARGET value * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerClass::As_Target(void) const +{ + assert(Triggers.ID(this) == ID); + + return(Build_Target(RTTI_TRIGGER, ID)); +} + + +/*********************************************************************************************** + * Find_Or_Make -- Find or create a trigger of the type specified. * + * * + * This routine is used when, given a trigger type, an actual trigger object is needed. * + * In this case, an existing trigger of the correct type must be located, or a trigger * + * object must be created. In either case, this routine will return a trigger object that * + * corresponds to the trigger type class specified. * + * * + * INPUT: trigtype -- Pointer to the trigger type to find (or create) a matching trigger * + * object. * + * * + * OUTPUT: Returns a pointer to a matching trigger object. If no more triggers could be * + * allocated and no matching trigger could be found, then this routine will return * + * NULL (a very rare case). * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype) +{ + if (!trigtype) return(NULL); + + for (int index = 0; index < Triggers.Count(); index++) { + if (trigtype == Triggers.Ptr(index)->Class) { + return(Triggers.Ptr(index)); + } + } + + /* + ** No trigger was found, so make one. + */ + TriggerClass * trig = new TriggerClass(trigtype); + return(trig); +} + + +/*********************************************************************************************** + * TriggerClass::Detach -- Detach specified target from this trigger. * + * * + * This routine is called when the specified trigger MUST be detached from all references * + * to it. The only reference maintained by a trigger is the reference to the trigger * + * type class it is modeled after. * + * * + * INPUT: target -- The target identifier to remove all attachments to. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must never detach the trigger type class from a trigger. Such a process * + * will leave the trigger orphan and in a 'crash the game immediately if used' * + * state. As such, this routine will throw an assertion if this is tried. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Detach(TARGET target, bool ) +{ + if (Is_Target_TriggerType(target)) { + assert((TriggerTypeClass*)Class != As_TriggerType(target)); +// if (Class == As_TriggerType(target)) { +// Class = NULL; +// } + } +} diff --git a/CODE/TRIGGER.H b/CODE/TRIGGER.H new file mode 100644 index 0000000..f43cb26 --- /dev/null +++ b/CODE/TRIGGER.H @@ -0,0 +1,121 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TRIGGER.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TRIGGER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : November 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TRIGGER_H +#define TRIGGER_H + +class TriggerClass { + public: + RTTIType RTTI; + int ID; + + CCPtr Class; + + /* + ** Record of the "already sprung" flag for the events. + */ + TDEventClass Event1; + TDEventClass Event2; + + /* + ** Constructor/Destructor + */ + TriggerClass(TriggerTypeClass * trigtype=NULL); + TriggerClass(NoInitClass const & x) : Class(x), Event1(x), Event2(x) {}; + ~TriggerClass(void); + + /* + ** Initialization: clears all triggers in preparation for new scenario + */ + static void Init(void); + + /* + ** Processing routines + */ + bool Spring(TEventType event=TEVENT_ANY, ObjectClass * object=0, CELL cell=0, bool forced=false); + void Detach(TARGET target, bool all=true); + + /* + ** File I/O routines + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + + /* + ** Utility routines + */ + TARGET As_Target(void) const; + char const * Description(void) const; + void Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Name(void) const {return(Class->Name());} + + /* + ** Overloaded operators + */ + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + /* + ** If this trigger object is active, then this flag will be true. Trigger + ** objects that are not active are either not yet created or have been + ** deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** This value tells how many objects or cells this trigger is attached + ** to. The Read_INI routine for all classes that point to a trigger must + ** increment this value! + */ + int AttachCount; + + /* + ** This value is used for triggers that can only exist in one cell. It is + ** needed for such triggers that the exact location of the trigger is needed + ** during processing but its location cannot be inferred from other data. + ** For all other triggers, this value is ignored. + */ + CELL Cell; +}; + + + + +#endif diff --git a/CODE/TRIGTYPE.CPP b/CODE/TRIGTYPE.CPP new file mode 100644 index 0000000..f336f04 --- /dev/null +++ b/CODE/TRIGTYPE.CPP @@ -0,0 +1,2129 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TRIGTYPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TRIGTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "trigtype.h" + + +/*********************************************************************************************** + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * * + * This is the normal constructor for a trigger object. The trigger starts with no team * + * members, no mission, and default values for all settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/10/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::TriggerTypeClass(void) : + AbstractTypeClass(RTTI_TRIGGERTYPE, TriggerTypes.ID(this), TXT_NONE, "x"), + IsPersistant(VOLATILE), + EventControl(MULTI_ONLY), + ActionControl(MULTI_ONLY), + House(HOUSE_SPAIN) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers. * + * * + * When a trigger type is deleted, then all triggers that refer to that type must also * + * be deleted as well. There can be no 'orphan' triggers in existence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::~TriggerTypeClass(void) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * * + * This routine will allocate a block of memory from the special trigger type object * + * pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated trigger type memory block. If there is * + * no more block available in the pool, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * TriggerTypeClass::operator new(size_t ) +{ + void * ptr = TriggerTypes.Allocate(); + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * * + * This routine will return a previously allocated trigger type object to the private * + * memory pool from which it was allocated. * + * * + * INPUT: ptr -- Pointer to the trigger type class to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = false; + } + TriggerTypes.Free((TriggerTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * * + * Use this routine to take this trigger type class object and convert it into a * + * target number. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target number that represents this trigger type class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerTypeClass::As_Target(void) const +{ + return(Build_Target(RTTI_TRIGGERTYPE, ID)); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * * + * When an object disappears from the game, it must be detached from all other objects that * + * may be referring to it. This routine will detach the specified target object from any * + * references to it in this trigger type class. * + * * + * INPUT: target -- The target object to be detached from this trigger type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Detach(TARGET target, bool) +{ + Action1.Detach(target); + Action2.Detach(target); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * * + * This is the scenario editor interface to a trigger type class object. It brings up a * + * fancy schmancy dialog to allow full edit control of the trigger type. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the "OK" button pressed? A false return value indicates that the edits * + * to this trigger type class object should be rejected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TriggerTypeClass::Edit(void) +{ + enum { + /* + ** Dialog position and dimensions. + */ + D_DIALOG_W = 320 + 100, + D_DIALOG_H = 200 + 20, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + /* + ** Event entry list box coordinates and dimensions. + */ + E1_X=D_DIALOG_X+45, + E1_Y=D_DIALOG_Y+65, + E2_X=E1_X, + E2_Y=E1_Y+22, + E_WIDTH=160, + E_HEIGHT=8*5, + + /* + ** Event optional data entry coordinates and dimensions. + */ + ED1_X=E1_X+E_WIDTH+20, + ED1_Y=E1_Y, + ED2_X=ED1_X, + ED2_Y=E2_Y, + + ED_WIDTH=95, + ED_HEIGHT=8*5, + + /* + ** Action entry list box coordinates. + */ + A1_X=E1_X, + A1_Y=D_DIALOG_Y+120, + A2_X=E1_X, + A2_Y=A1_Y+22, + + /* + ** Action optional data entry coordinates. + */ + AD1_X=A1_X+E_WIDTH+20, + AD1_Y=A1_Y, + AD2_X=AD1_X, + AD2_Y=A2_Y, + + /* + ** Misc control values. + */ + GENERAL_SIZE=10, // Text length for general data entry fields. + ENTRY_SIZE=35, // Maximum size of event or action description text. + WAYPOINT_SIZE=3, // Text length maximum for waypoint entry. + TEAM_SIZE=10, // Team name text entry field length. + DESC_SIZE=35 // Maximum length of object full name description. + }; + + /* + ** Button enumerations: + */ + enum { + EVENT_LIST=100, // Primary event list. + EVENT_LIST2, // Secondary event list. + ACTION_LIST, // Primary action list. + ACTION_LIST2, // Secondary action list. + NAME_EDIT, // Trigger name edit field. + DATA_SPEECH1, // Primary action speech. + DATA_SPEECH2, // Secondary action speech. + DATA_THEME1, // Primary action theme. + DATA_THEME2, // Secondary action theme. + DATA_MOVIE1, // Primary action movie. + DATA_MOVIE2, // Secondary action movie. + DATA_SOUND1, // Primary action sound effect. + DATA_SOUND2, // Secondary action sound effect. + DATA_SPECIAL1, // Primary action special weapon. + DATA_SPECIAL2, // Secondary action special weapon. + DATA_EDIT, // Primary event waypoint data field. + DATA_EDIT2, // Secondary event waypoint data field. + DATA_EDIT3, // Primary action waypoint data field. + DATA_EDIT4, // Secondary action waypoint data field. + DATA_HTYPE1, // Primary event house choice list. + DATA_HTYPE2, // Secondary event house choice list. + DATA_HTYPE3, // Primary action house choice list. + DATA_HTYPE4, // Secondary action house choice list. + DATA_BOOLTYPE1, // Primary action boolean data list. + DATA_BOOLTYPE2, // Secondary action boolean data list. + DATA_GENERAL1, // Primary event general data field. + DATA_GENERAL2, // Secondary event general data field. + DATA_GENERAL3, // Primary action general data field. + DATA_GENERAL4, // Secondary action general data field. + DATA_BTYPE1, // Primary event building type list. + DATA_BTYPE2, // Secondary event building type list. + DATA_ITYPE1, // Primary event infantry type list. + DATA_ITYPE2, // Secondary event infantry type list. + DATA_ATYPE1, // Primary event aircraft type list. + DATA_ATYPE2, // Secondary event aircraft type list. + DATA_UTYPE1, // Primary event unit type list. + DATA_UTYPE2, // Secondary event unit type list. + DATA_TTYPE1, // Primary event team type entry list. + DATA_TTYPE2, // Secondary event team type entry list. + DATA_TTYPE3, // Primary action team type entry list. + DATA_TTYPE4, // Secondary action team type entry list. + DATA_TRTYPE1, // Primary action trigger list. + DATA_TRTYPE2, // Secondary action trigger list. + BUTTON_HOUSE, // House ownership for this trigger. + BUTTON_PERSISTANCE, // Persistence of this trigger. + BUTTON_OK, // Ok button - save and exit. + BUTTON_CANCEL, // Cancel button - just exit. + BUTTON_ACTION, // Multiple action control button. + BUTTON_EVENT, // Multiple event control button. + }; + + /* + ** Dialog variables: + */ + bool cancel = false; // true = user cancels + int i; // loop counter + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + /* + ** List of events allowed. + */ + char eventtext[ENTRY_SIZE] = ""; + TDropListClass event1list(EVENT_LIST, eventtext, sizeof(eventtext), + TPF_EFNT | TPF_NOSHADOW, + E1_X, E1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char event2text[ENTRY_SIZE] = ""; + TDropListClass event2list(EVENT_LIST2, event2text, sizeof(event2text), + TPF_EFNT | TPF_NOSHADOW, + E2_X, E2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TEventType event = TEVENT_FIRST; event < TEVENT_COUNT; event++) { + event1list.Add_Item(&EventChoices[event]); + event2list.Add_Item(&EventChoices[event]); + } + + PBubble_Sort(&event1list[0], event1list.Count()); + PBubble_Sort(&event2list[0], event2list.Count()); + + if (Event1.Event == TEVENT_NONE) Event1.Event = TEVENT_FIRST; + event1list.Set_Selected_Index(&EventChoices[Event1.Event]); + if (Event2.Event == TEVENT_NONE) Event2.Event = TEVENT_FIRST; + event2list.Set_Selected_Index(&EventChoices[Event2.Event]); + + /* + ** List of actions allowed. + */ + char actiontext[ENTRY_SIZE] = ""; + TDropListClass action1list(ACTION_LIST, actiontext, sizeof(actiontext), + TPF_EFNT | TPF_NOSHADOW, + A1_X, A1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char action2text[ENTRY_SIZE] = ""; + TDropListClass action2list(ACTION_LIST2, action2text, sizeof(action2text), + TPF_EFNT | TPF_NOSHADOW, + A2_X, A2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TActionType action = TACTION_FIRST; action < TACTION_COUNT; action++) { + action1list.Add_Item(&ActionChoices[action]); + action2list.Add_Item(&ActionChoices[action]); + } + + PBubble_Sort(&action1list[0], action1list.Count()); + PBubble_Sort(&action2list[0], action2list.Count()); + + if (Action1.Action == ACTION_NONE) Action1.Action = TACTION_FIRST; + action1list.Set_Selected_Index(&ActionChoices[Action1.Action]); + if (Action2.Action == ACTION_NONE) Action2.Action = TACTION_FIRST; + action2list.Set_Selected_Index(&ActionChoices[Action2.Action]); + + /* + ** Optional waypoint entry field. + */ + char way1[WAYPOINT_SIZE] = "A"; + EditClass way1data(DATA_EDIT, way1, sizeof(way1), TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event1.Event) == NEED_WAYPOINT) { + if (Event1.Data.Value < 26) { + sprintf(way1data.Get_Text(), "%c", Event1.Data.Value + 'A'); + } else { + sprintf(way1data.Get_Text(), "%c%c", (Event1.Data.Value / 26) + 'A'-1, (Event1.Data.Value % 26) + 'A'); + } + } + + char way2[WAYPOINT_SIZE] = "A"; + EditClass way2data(DATA_EDIT2, way2, sizeof(way2), TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event2.Event) == NEED_WAYPOINT) { + if (Event2.Data.Value < 26) { + sprintf(way2data.Get_Text(), "%c", Event2.Data.Value + 'A'); + } else { + sprintf(way2data.Get_Text(), "%c%c", (Event2.Data.Value / 26) + 'A'-1, (Event2.Data.Value % 26) + 'A'); + } + } + + char way3[WAYPOINT_SIZE] = "A"; + EditClass way3data(DATA_EDIT3, way3, sizeof(way3), TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action1.Action) == NEED_WAYPOINT) { + if (Action1.Data.Value < 26) { + sprintf(way3data.Get_Text(), "%c", Action1.Data.Value + 'A'); + } else { + sprintf(way3data.Get_Text(), "%c%c", (Action1.Data.Value / 26) + 'A'-1, (Action1.Data.Value % 26) + 'A'); + } + } + + char way4[WAYPOINT_SIZE] = "A"; + EditClass way4data(DATA_EDIT4, way4, sizeof(way4), TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action2.Action) == NEED_WAYPOINT) { + if (Action2.Data.Value < 26) { + sprintf(way4data.Get_Text(), "%c", Action2.Data.Value + 'A'); + } else { + sprintf(way4data.Get_Text(), "%c%c", (Action2.Data.Value / 26) + 'A'-1, (Action2.Data.Value % 26) + 'A'); + } + } + + /* + ** Optional event data entry field. + */ + char databuf1[GENERAL_SIZE] = ""; + EditClass event1data(DATA_GENERAL1, databuf1, sizeof(databuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event1.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event1data.Get_Text(), "%d", Event1.Data.Value); + break; + } + + char databuf2[GENERAL_SIZE] = ""; + EditClass event2data(DATA_GENERAL2, databuf2, sizeof(databuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event2.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event2data.Get_Text(), "%d", Event2.Data.Value); + break; + } + + char actionbuf1[GENERAL_SIZE] = ""; + EditClass action1data(DATA_GENERAL3, actionbuf1, sizeof(actionbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action1.Action)) { + case NEED_NUMBER: + sprintf(action1data.Get_Text(), "%d", Action1.Data.Value); + break; + } + + char actionbuf2[GENERAL_SIZE] = ""; + EditClass action2data(DATA_GENERAL4, actionbuf2, sizeof(actionbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action2.Action)) { + case NEED_NUMBER: + sprintf(action2data.Get_Text(), "%d", Action2.Data.Value); + break; + } + + /* + ** Optional team entry list. + */ + char tbuf1[TEAM_SIZE] = ""; + DropListClass ttype1list(DATA_TTYPE1, tbuf1, sizeof(tbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf2[TEAM_SIZE] = ""; + DropListClass ttype2list(DATA_TTYPE2, tbuf2, sizeof(tbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf3[TEAM_SIZE] = ""; + DropListClass ttype3list(DATA_TTYPE3, tbuf3, sizeof(tbuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf4[TEAM_SIZE] = ""; + DropListClass ttype4list(DATA_TTYPE4, tbuf4, sizeof(tbuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int index = 0; index < TeamTypes.Count(); index++) { + ttype1list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype2list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype3list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype4list.Add_Item(TeamTypes.Ptr(index)->IniName); + } + + if (Event1.Team.Is_Valid()) { + ttype1list.Set_Selected_Index(Event1.Team->IniName); + } else { + ttype1list.Set_Selected_Index(0); + } + if (Event2.Team.Is_Valid()) { + ttype2list.Set_Selected_Index(Event2.Team->IniName); + } else { + ttype2list.Set_Selected_Index(0); + } + if (Action1.Team.Is_Valid()) { + ttype3list.Set_Selected_Index(Action1.Team->IniName); + } else { + ttype3list.Set_Selected_Index(0); + } + if (Action2.Team.Is_Valid()) { + ttype4list.Set_Selected_Index(Action2.Team->IniName); + } else { + ttype4list.Set_Selected_Index(0); + } + + /* + ** Optional trigger entry list. + */ + char trbuf1[TEAM_SIZE] = ""; + DropListClass trtype1list(DATA_TRTYPE1, trbuf1, sizeof(trbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char trbuf2[TEAM_SIZE] = ""; + DropListClass trtype2list(DATA_TRTYPE2, trbuf2, sizeof(trbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (index = 0; index < TriggerTypes.Count(); index++) { + trtype1list.Add_Item(TriggerTypes.Ptr(index)->IniName); + trtype2list.Add_Item(TriggerTypes.Ptr(index)->IniName); + } + + if (Action1.Trigger.Is_Valid()) { + trtype1list.Set_Selected_Index(Action1.Trigger->IniName); + } else { + trtype1list.Set_Selected_Index(0); + } + if (Action2.Trigger.Is_Valid()) { + trtype2list.Set_Selected_Index(Action2.Trigger->IniName); + } else { + trtype2list.Set_Selected_Index(0); + } + + /* + ** Optional boolean value list. + */ + char boolbuf1[TEAM_SIZE] = ""; + DropListClass booltype1list(DATA_BOOLTYPE1, boolbuf1, sizeof(boolbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char boolbuf2[TEAM_SIZE] = ""; + DropListClass booltype2list(DATA_BOOLTYPE2, boolbuf2, sizeof(boolbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + booltype1list.Add_Item("OFF"); + booltype1list.Add_Item("ON"); + booltype2list.Add_Item("OFF"); + booltype2list.Add_Item("ON"); + + booltype1list.Set_Selected_Index(Action1.Data.Bool); + booltype2list.Set_Selected_Index(Action2.Data.Bool); + + /* + ** Optional musical theme choice list. + */ + char themebuf1[DESC_SIZE] = ""; + DropListClass themetype1list(DATA_THEME1, themebuf1, sizeof(themebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char themebuf2[DESC_SIZE] = ""; + DropListClass themetype2list(DATA_THEME2, themebuf2, sizeof(themebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + themetype1list.Add_Item(Theme.Full_Name(theme)); + themetype2list.Add_Item(Theme.Full_Name(theme)); + } + + if (Action_Needs(Action1.Action) == NEED_THEME) { + themetype1list.Set_Selected_Index(Action1.Data.Theme); + } else { + themetype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_THEME) { + themetype2list.Set_Selected_Index(Action2.Data.Theme); + } else { + themetype2list.Set_Selected_Index(0); + } + + /* + ** Optional movie list. + */ + char moviebuf1[DESC_SIZE] = ""; + DropListClass movietype1list(DATA_MOVIE1, moviebuf1, sizeof(moviebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char moviebuf2[DESC_SIZE] = ""; + DropListClass movietype2list(DATA_MOVIE2, moviebuf2, sizeof(moviebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VQType movie = VQ_FIRST; movie < VQ_COUNT; movie++) { + movietype1list.Add_Item(VQName[movie]); + movietype2list.Add_Item(VQName[movie]); + } + + if (Action_Needs(Action1.Action) == NEED_MOVIE) { + movietype1list.Set_Selected_Index(Action1.Data.Movie); + } else { + movietype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_MOVIE) { + movietype2list.Set_Selected_Index(Action2.Data.Movie); + } else { + movietype2list.Set_Selected_Index(0); + } + + /* + ** Optional sound effect list. + */ + char soundbuf1[DESC_SIZE] = ""; + DropListClass soundtype1list(DATA_SOUND1, soundbuf1, sizeof(soundbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char soundbuf2[DESC_SIZE] = ""; + DropListClass soundtype2list(DATA_SOUND2, soundbuf2, sizeof(soundbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VocType sound = VOC_FIRST; sound < VOC_COUNT; sound++) { + soundtype1list.Add_Item(Voc_Name(sound)); + soundtype2list.Add_Item(Voc_Name(sound)); + } + + if (Action_Needs(Action1.Action) == NEED_SOUND) { + soundtype1list.Set_Selected_Index(Action1.Data.Sound); + } else { + soundtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SOUND) { + soundtype2list.Set_Selected_Index(Action2.Data.Sound); + } else { + soundtype2list.Set_Selected_Index(0); + } + + /* + ** Optional speech effect list. + */ + char speechbuf1[DESC_SIZE] = ""; + DropListClass speechtype1list(DATA_SPEECH1, speechbuf1, sizeof(speechbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char speechbuf2[DESC_SIZE] = ""; + DropListClass speechtype2list(DATA_SPEECH2, speechbuf2, sizeof(speechbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VoxType speech = VOX_FIRST; speech < VOX_COUNT; speech++) { + speechtype1list.Add_Item(Speech_Name(speech)); + speechtype2list.Add_Item(Speech_Name(speech)); + } + + if (Action_Needs(Action1.Action) == NEED_SPEECH) { + speechtype1list.Set_Selected_Index(Action1.Data.Speech); + } else { + speechtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SPEECH) { + speechtype2list.Set_Selected_Index(Action2.Data.Speech); + } else { + speechtype2list.Set_Selected_Index(0); + } + + /* + ** Optional building type entry list. + */ + char bbuf1[DESC_SIZE] = ""; + DropListClass btype1list(DATA_BTYPE1, bbuf1, sizeof(bbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char bbuf2[DESC_SIZE] = ""; + DropListClass btype2list(DATA_BTYPE2, bbuf2, sizeof(bbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (StructType ss = STRUCT_FIRST; ss < STRUCT_COUNT; ss++) { + btype1list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + btype2list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_STRUCTURE) { + btype1list.Set_Selected_Index(Event1.Data.Structure); + } else { + btype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_STRUCTURE) { + btype2list.Set_Selected_Index(Event2.Data.Structure); + } else { + btype2list.Set_Selected_Index(0); + } + + /* + ** Optional infantry type entry list. + */ + char ibuf1[DESC_SIZE] = ""; + DropListClass itype1list(DATA_ITYPE1, ibuf1, sizeof(ibuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ibuf2[DESC_SIZE] = ""; + DropListClass itype2list(DATA_ITYPE2, ibuf2, sizeof(ibuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (InfantryType ii = INFANTRY_FIRST; ii < INFANTRY_COUNT; ii++) { + itype1list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + itype2list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_INFANTRY) { + itype1list.Set_Selected_Index(Event1.Data.Infantry); + } else { + itype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_INFANTRY) { + itype2list.Set_Selected_Index(Event2.Data.Infantry); + } else { + itype2list.Set_Selected_Index(0); + } + + /* + ** Optional aircraft type entry list. + */ + char abuf1[DESC_SIZE] = ""; + DropListClass atype1list(DATA_ATYPE1, abuf1, sizeof(abuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char abuf2[DESC_SIZE] = ""; + DropListClass atype2list(DATA_ATYPE2, abuf2, sizeof(abuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (AircraftType aa = AIRCRAFT_FIRST; aa < AIRCRAFT_COUNT; aa++) { + atype1list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + atype2list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_AIRCRAFT) { + atype1list.Set_Selected_Index(Event1.Data.Aircraft); + } else { + atype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_AIRCRAFT) { + atype2list.Set_Selected_Index(Event2.Data.Aircraft); + } else { + atype2list.Set_Selected_Index(0); + } + + /* + ** Optional unit type entry list. + */ + char ubuf1[DESC_SIZE] = ""; + DropListClass utype1list(DATA_UTYPE1, ubuf1, sizeof(ubuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ubuf2[DESC_SIZE] = ""; + DropListClass utype2list(DATA_UTYPE2, ubuf2, sizeof(ubuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (UnitType uu = UNIT_FIRST; uu < UNIT_COUNT; uu++) { + utype1list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + utype2list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_UNIT) { + utype1list.Set_Selected_Index(Event1.Data.Unit); + } else { + utype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_UNIT) { + utype2list.Set_Selected_Index(Event2.Data.Unit); + } else { + utype2list.Set_Selected_Index(0); + } + + /* + ** Optional house type entry list. + */ + char housebuf1[DESC_SIZE] = ""; + DropListClass htype1list(DATA_HTYPE1, housebuf1, sizeof(housebuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf2[DESC_SIZE] = ""; + DropListClass htype2list(DATA_HTYPE2, housebuf2, sizeof(housebuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf3[DESC_SIZE] = ""; + DropListClass htype3list(DATA_HTYPE3, housebuf3, sizeof(housebuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf4[DESC_SIZE] = ""; + DropListClass htype4list(DATA_HTYPE4, housebuf4, sizeof(housebuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (HousesType hh = HOUSE_FIRST; hh < HOUSE_COUNT; hh++) { + htype1list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype2list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype3list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype4list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + } + + if (Event_Needs(Event1.Event) == NEED_HOUSE) { + htype1list.Set_Selected_Index(Event1.Data.House); + } else { + htype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_HOUSE) { + htype2list.Set_Selected_Index(Event2.Data.House); + } else { + htype2list.Set_Selected_Index(0); + } + if (Action_Needs(Action1.Action) == NEED_HOUSE) { + htype3list.Set_Selected_Index(Action1.Data.House); + } else { + htype3list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_HOUSE) { + htype4list.Set_Selected_Index(Action2.Data.House); + } else { + htype4list.Set_Selected_Index(0); + } + + /* + ** Optional special weapon list. + */ + char special1[DESC_SIZE] = ""; + DropListClass spc1(DATA_SPECIAL1, special1, sizeof(special1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char special2[DESC_SIZE] = ""; + DropListClass spc2(DATA_SPECIAL2, special2, sizeof(special2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SpecialWeaponType spec = SPC_FIRST; spec < SPC_COUNT; spec++) { + spc1.Add_Item(SpecialWeaponName[spec]); + spc2.Add_Item(SpecialWeaponName[spec]); + } + if ((unsigned)Action1.Data.Special < SPC_COUNT) { + spc1.Set_Selected_Index(Action1.Data.Special); + } else { + spc1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Special < SPC_COUNT) { + spc2.Set_Selected_Index(Action2.Data.Special); + } else { + spc2.Set_Selected_Index(0); + } + + /* + ** Optional quarry type. + */ + char quarry1[DESC_SIZE] = ""; + DropListClass qlist1(DATA_SPECIAL1, quarry1, sizeof(quarry1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char quarry2[DESC_SIZE] = ""; + DropListClass qlist2(DATA_SPECIAL2, quarry2, sizeof(quarry2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist1.Add_Item(QuarryName[q]); + qlist2.Add_Item(QuarryName[q]); + } + if ((unsigned)Action1.Data.Quarry < QUARRY_COUNT) { + qlist1.Set_Selected_Index(Action1.Data.Quarry); + } else { + qlist1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Quarry < QUARRY_COUNT) { + qlist2.Set_Selected_Index(Action2.Data.Quarry); + } else { + qlist2.Set_Selected_Index(0); + } + + /* + ** Name of this trigger text edit field. + */ + char namebuf[5] = ""; + EditClass name_edt(NAME_EDIT, namebuf, sizeof(namebuf), TPF_EFNT | TPF_NOSHADOW, D_DIALOG_X+40, D_DIALOG_Y+30, 40, 9, EditClass::ALPHANUMERIC); + strcpy(namebuf, IniName); // Name + + /* + ** Create the list of house's allowed for trigger. + */ + char housetext[DESC_SIZE] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X+name_edt.Width+20, name_edt.Y, 95, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + + /* + ** Must match order and number of PersistantType specified in + ** TriggerTypeClass definition. + */ + char perstext[DESC_SIZE] = ""; + static char * _perstext[3] = { + "Volatile", + "Semi-persistent", + "Persistent" + }; + DropListClass persbtn(BUTTON_PERSISTANCE, perstext, sizeof(perstext), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X+housebtn.Width+20, housebtn.Y, 105, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (i = 0; i < sizeof(_perstext)/sizeof(_perstext[0]); i++) { + persbtn.Add_Item(_perstext[i]); + } + persbtn.Set_Selected_Index(IsPersistant); + + /* + ** This button controls the existence and relationship of a second trigger + ** event. + */ + int eventflag = EventControl; + TextButtonClass eventbtn(BUTTON_EVENT, TXT_TRIGGER_JUST_EVENT, TPF_EBUTTON, event1list.X, event1list.Y+11, 100, 9); + + /* + ** This button controls the existence of a secondary action. + */ + bool actionflag = ActionControl; + TextButtonClass actionbtn(BUTTON_ACTION, TXT_TRIGGER_JUST_ACTION, TPF_EBUTTON, action1list.X, action1list.Y+11, 100, 9); + + /* + ** Create the ubiquitous OK and Cancel buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_DIALOG_X+35, D_DIALOG_Y+D_DIALOG_H-30, 45, 9); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_DIALOG_X+D_DIALOG_W-80, D_DIALOG_Y+D_DIALOG_H-30, 45, 9); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + event1list.Add_Tail(*commands); + action1list.Add_Tail(*commands); + eventbtn.Add_Tail(*commands); + actionbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + persbtn.Add_Tail(*commands); + housebtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Trigger Event:", event1list.X, event1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Action to Perform:", action1list.X, action1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Persistence:", persbtn.X, persbtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + if (eventflag == 3) { + LogicPage->Draw_Line(event1list.X-1, event1list.Y+3, event1list.X-2*RESFACTOR, event1list.Y+3, WHITE); + LogicPage->Draw_Line(event1list.X-2*RESFACTOR, event1list.Y+3, action1list.X-2*RESFACTOR, action1list.Y+3, WHITE); + LogicPage->Draw_Line(action1list.X-1, action1list.Y+3, action1list.X-2*RESFACTOR, action1list.Y+3, WHITE); + + LogicPage->Draw_Line(event2list.X-1, event2list.Y+3, event2list.X-5*RESFACTOR, event2list.Y+3, WHITE); + LogicPage->Draw_Line(event2list.X-5*RESFACTOR, event2list.Y+3, action2list.X-5*RESFACTOR, action2list.Y+3, WHITE); + LogicPage->Draw_Line(action2list.X-1, action2list.Y+3, action2list.X-5*RESFACTOR, action2list.Y+3, WHITE); + } + + /* + ** Adjust the button list to match current control settings. + */ + event2list.Remove(); + switch (eventflag) { + case 0: + eventbtn.Set_Text(TXT_TRIGGER_JUST_EVENT); + break; + + case 1: + eventbtn.Set_Text(TXT_TRIGGER_AND); + event2list.Add(*commands); + break; + + case 2: + eventbtn.Set_Text(TXT_TRIGGER_OR); + event2list.Add(*commands); + break; + + case 3: + eventbtn.Set_Text(TXT_TRIGGER_LINKED); + event2list.Add(*commands); + break; + } + + /* + ** Prepare the primary event data field. + */ + htype1list.Remove(); + way1data.Remove(); + event1data.Remove(); + btype1list.Remove(); + itype1list.Remove(); + atype1list.Remove(); + utype1list.Remove(); + ttype1list.Remove(); + switch (Event_Needs(*event1list.Current_Item())) { + case NEED_HOUSE: + htype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype1list.Add(*commands); + break; + + case NEED_WAYPOINT: + way1data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event1data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype1list.Add(*commands); + break; + + case NEED_INFANTRY: + itype1list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype1list.Add(*commands); + break; + + case NEED_UNIT: + utype1list.Add(*commands); + break; + + default: + break; + } + + /* + ** Prepare the secondary event data field. + */ + htype2list.Remove(); + way2data.Remove(); + event2data.Remove(); + btype2list.Remove(); + itype2list.Remove(); + atype2list.Remove(); + utype2list.Remove(); + ttype2list.Remove(); + if (commands->Extract_Gadget(EVENT_LIST2)) { + switch (Event_Needs(*event2list.Current_Item())) { + case NEED_HOUSE: + htype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype2list.Add(*commands); + break; + + case NEED_WAYPOINT: + way2data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event2data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype2list.Add(*commands); + break; + + case NEED_INFANTRY: + itype2list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype2list.Add(*commands); + break; + + case NEED_UNIT: + utype2list.Add(*commands); + break; + + default: + break; + } + } + + /* + ** Setup the action buttons and associated data entry fields. + */ + actionbtn.Remove(); + action2list.Remove(); + if (eventflag == 3) { + action2list.Add(*commands); + } else { + actionbtn.Add(*commands); + if (actionflag) { + actionbtn.Set_Text(TXT_TRIGGER_AND); + action2list.Add(*commands); + } else { + actionbtn.Set_Text(TXT_TRIGGER_JUST_ACTION); + } + } + + qlist1.Remove(); + spc1.Remove(); + htype3list.Remove(); + booltype1list.Remove(); + trtype1list.Remove(); + way3data.Remove(); + action1data.Remove(); + ttype3list.Remove(); + themetype1list.Remove(); + soundtype1list.Remove(); + movietype1list.Remove(); + speechtype1list.Remove(); + switch (Action_Needs(*action1list.Current_Item())) { + case NEED_MOVIE: + movietype1list.Add(*commands); + break; + + case NEED_SPECIAL: + spc1.Add(*commands); + break; + + case NEED_HOUSE: + htype3list.Add(*commands); + break; + + case NEED_BOOL: + booltype1list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype3list.Add(*commands); + break; + + case NEED_NUMBER: + action1data.Add(*commands); + break; + + case NEED_WAYPOINT: + way3data.Add(*commands); + break; + + case NEED_THEME: + themetype1list.Add(*commands); + break; + + case NEED_SOUND: + soundtype1list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype1list.Add(*commands); + break; + + case NEED_QUARRY: + qlist1.Add(*commands); + break; + } + + qlist2.Remove(); + spc2.Remove(); + htype4list.Remove(); + booltype2list.Remove(); + trtype2list.Remove(); + way4data.Remove(); + action2data.Remove(); + ttype4list.Remove(); + themetype2list.Remove(); + soundtype2list.Remove(); + movietype2list.Remove(); + speechtype2list.Remove(); + if (commands->Extract_Gadget(ACTION_LIST2)) { + switch (Action_Needs(*action2list.Current_Item())) { + case NEED_MOVIE: + movietype2list.Add(*commands); + break; + + case NEED_SPECIAL: + spc2.Add(*commands); + break; + + case NEED_HOUSE: + htype4list.Add(*commands); + break; + + case NEED_BOOL: + booltype2list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype4list.Add(*commands); + break; + + case NEED_NUMBER: + action2data.Add(*commands); + break; + + case NEED_WAYPOINT: + way4data.Add(*commands); + break; + + case NEED_THEME: + themetype2list.Add(*commands); + break; + + case NEED_SOUND: + soundtype2list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype2list.Add(*commands); + break; + + case NEED_QUARRY: + qlist2.Add(*commands); + break; + } + } + + /* + ** Collapse any dropped down list boxes. + */ + spc1.Collapse(); + spc2.Collapse(); + qlist1.Collapse(); + qlist2.Collapse(); + htype1list.Collapse(); + htype2list.Collapse(); + htype3list.Collapse(); + htype4list.Collapse(); + ttype1list.Collapse(); + ttype2list.Collapse(); + ttype3list.Collapse(); + ttype4list.Collapse(); + btype1list.Collapse(); + btype2list.Collapse(); + utype1list.Collapse(); + utype2list.Collapse(); + itype1list.Collapse(); + itype2list.Collapse(); + atype1list.Collapse(); + atype2list.Collapse(); + trtype1list.Collapse(); + trtype2list.Collapse(); + action1list.Collapse(); + action2list.Collapse(); + event1list.Collapse(); + event2list.Collapse(); + housebtn.Collapse(); + persbtn.Collapse(); + booltype1list.Collapse(); + booltype2list.Collapse(); + themetype1list.Collapse(); + themetype2list.Collapse(); + soundtype1list.Collapse(); + soundtype2list.Collapse(); + movietype1list.Collapse(); + movietype2list.Collapse(); + speechtype1list.Collapse(); + speechtype2list.Collapse(); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case BUTTON_EVENT | KN_BUTTON: + eventflag = (eventflag+1) % 4; + display = true; + break; + + case BUTTON_ACTION | KN_BUTTON: + actionflag = (actionflag == false); + display = true; + break; + + case DATA_SPEECH1 | KN_BUTTON: + Speak(VoxType(speechtype1list.Current_Index())); + display = true; + break; + + case DATA_SPEECH2 | KN_BUTTON: + Speak(VoxType(speechtype2list.Current_Index())); + display = true; + break; + + case DATA_SOUND1 | KN_BUTTON: + Sound_Effect(VocType(soundtype1list.Current_Index())); + display = true; + break; + + case DATA_SOUND2 | KN_BUTTON: + Sound_Effect(VocType(soundtype2list.Current_Index())); + display = true; + break; + + /* + ** Transfer all the necessary values from the edit fields into their + ** respective positions within the trigger object. + */ + case KN_RETURN: + case BUTTON_OK | KN_BUTTON: + House = HousesType(housebtn.Current_Index()); + IsPersistant = PersistantType(persbtn.Current_Index()); + if (strlen(namebuf)==0) { + Set_Name("____"); + } else { + Set_Name(namebuf); + } + + /* + ** Primary event specific data retrieval. + */ + EventControl = MultiStyleType(eventflag); + Event1.Event = *event1list.Current_Item(); + switch (Event_Needs(Event1.Event)) { + case NEED_HOUSE: + Event1.Data.House = HousesType(htype1list.Current_Index()); + break; + + case NEED_TIME: + Event1.Data.Value = atoi(event1data.Get_Text()); + break; + + case NEED_NUMBER: + Event1.Data.Value = atoi(event1data.Get_Text()); + break; + + case NEED_STRUCTURE: + Event1.Data.Structure = StructType(btype1list.Current_Index()); + break; + + case NEED_UNIT: + Event1.Data.Unit = UnitType(utype1list.Current_Index()); + break; + + case NEED_INFANTRY: + Event1.Data.Infantry = InfantryType(itype1list.Current_Index()); + break; + + case NEED_AIRCRAFT: + Event1.Data.Aircraft = AircraftType(atype1list.Current_Index()); + break; + + case NEED_WAYPOINT: + Event1.Data.Value = toupper(way1[0]) - 'A'; + if (way1[1] != '\0') { + Event1.Data.Value = (Event1.Data.Value+1)*26; + Event1.Data.Value += toupper(way1[1]) - 'A'; + } + break; + + case NEED_TEAM: + Event1.Team = TeamTypeClass::From_Name(ttype1list.Current_Item()); + break; + } + + /* + ** Secondary event specific data retrieval. + */ + Event2.Event = *event2list.Current_Item(); + switch (Event_Needs(Event2.Event)) { + case NEED_HOUSE: + Event2.Data.House = HousesType(htype2list.Current_Index()); + break; + + case NEED_TIME: + Event2.Data.Value = atoi(event2data.Get_Text()); + break; + + case NEED_NUMBER: + Event2.Data.Value = atoi(event2data.Get_Text()); + break; + + case NEED_STRUCTURE: + Event2.Data.Structure = StructType(btype2list.Current_Index()); + break; + + case NEED_UNIT: + Event2.Data.Unit = UnitType(utype2list.Current_Index()); + break; + + case NEED_INFANTRY: + Event2.Data.Infantry = InfantryType(itype2list.Current_Index()); + break; + + case NEED_AIRCRAFT: + Event2.Data.Aircraft = AircraftType(atype2list.Current_Index()); + break; + + case NEED_WAYPOINT: + Event2.Data.Value = toupper(way2[0]) - 'A'; + if (way2[1] != '\0') { + Event2.Data.Value = (Event2.Data.Value+1)*26; + Event2.Data.Value += toupper(way2[1]) - 'A'; + } + break; + + case NEED_TEAM: + Event2.Team = TeamTypeClass::As_Pointer(ttype2list.Current_Item()); + break; + } + + /* + ** Primary action data retrieval. + */ + ActionControl = MultiStyleType(actionflag); + Action1.Action = *action1list.Current_Item(); + switch (Action_Needs(Action1.Action)) { + case NEED_SPECIAL: + Action1.Data.Special = SpecialWeaponType(spc1.Current_Index()); + break; + + case NEED_HOUSE: + Action1.Data.House = HousesType(htype3list.Current_Index()); + break; + + case NEED_TRIGGER: + Action1.Trigger = TriggerTypeClass::From_Name(trtype1list.Current_Item()); + break; + + case NEED_TEAM: + Action1.Team = TeamTypeClass::From_Name(ttype3list.Current_Item()); + break; + + case NEED_NUMBER: + Action1.Data.Value = atoi(action1data.Get_Text()); + break; + + case NEED_WAYPOINT: + Action1.Data.Value = toupper(way3[0]) - 'A'; + if (way3[1] != '\0') { + Action1.Data.Value = (Action1.Data.Value+1)*26; + Action1.Data.Value += toupper(way3[1]) - 'A'; + } + break; + + case NEED_BOOL: + Action1.Data.Bool = booltype1list.Current_Index(); + break; + + case NEED_THEME: + Action1.Data.Theme = ThemeType(themetype1list.Current_Index()); + break; + + case NEED_SOUND: + Action1.Data.Sound = VocType(soundtype1list.Current_Index()); + break; + + case NEED_MOVIE: + Action1.Data.Movie = VQType(movietype1list.Current_Index()); + break; + + case NEED_SPEECH: + Action1.Data.Speech = VoxType(speechtype1list.Current_Index()); + break; + + case NEED_QUARRY: + Action1.Data.Quarry = QuarryType(qlist1.Current_Index()); + break; + } + + /* + ** Secondary action data retrieval. + */ + Action2.Action = *action2list.Current_Item(); + switch (Action_Needs(Action2.Action)) { + case NEED_SPECIAL: + Action2.Data.Special = SpecialWeaponType(spc2.Current_Index()); + break; + + case NEED_HOUSE: + Action2.Data.House = HousesType(htype4list.Current_Index()); + break; + + case NEED_TRIGGER: + Action2.Trigger = TriggerTypeClass::From_Name(trtype2list.Current_Item()); + break; + + case NEED_TEAM: + Action2.Team = TeamTypeClass::From_Name(ttype4list.Current_Item()); + break; + + case NEED_NUMBER: + Action2.Data.Value = atoi(action2data.Get_Text()); + break; + + case NEED_WAYPOINT: + Action2.Data.Value = toupper(way4[0]) - 'A'; + if (way4[1] != '\0') { + Action2.Data.Value = (Action2.Data.Value+1)*26; + Action2.Data.Value += toupper(way4[1]) - 'A'; + } + break; + + case NEED_BOOL: + Action2.Data.Bool = booltype2list.Current_Index(); + break; + + case NEED_THEME: + Action2.Data.Theme = ThemeType(themetype2list.Current_Index()); + break; + + case NEED_MOVIE: + Action2.Data.Movie = VQType(movietype2list.Current_Index()); + break; + + case NEED_SOUND: + Action2.Data.Sound = VocType(soundtype2list.Current_Index()); + break; + + case NEED_SPEECH: + Action2.Data.Speech = VoxType(speechtype2list.Current_Index()); + break; + + case NEED_QUARRY: + Action2.Data.Quarry = QuarryType(qlist1.Current_Index()); + break; + } + return(true); + + case KN_ESC: + case BUTTON_CANCEL | KN_BUTTON: + process = false; + + /* + ** Always signal a redraw if any of the buttons were touched. This + ** can be determined by examining the button bit flag in the input + ** return value. + */ + default: + if (input & KN_BUTTON) { + display = true; + } + break; + } + } + return(false); +} +#endif + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * * + * This will build a (static) text description of the trigger type. Use this description * + * when displaying this trigger in a list box. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a one line text description of this trigger. * + * * + * WARNINGS: The pointer returned actually points to a static buffer. The pointer is only * + * valid until this routine is called again. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerTypeClass::Description(void) const +{ + static char _buffer[128]; + + char special; + switch (EventControl) { + case MULTI_AND: + special = '&'; + break; + + case MULTI_OR: + special = '|'; + break; + + case MULTI_LINKED: + special = '='; + break; + + default: + special = '.'; + break; + } + + char special2 = '.'; + if (ActionControl == MULTI_AND) { + special2 = '&'; + } + + char tbuf[32]; + char const * added = ""; + switch (Event_Needs(Event1.Event)) { + case NEED_NUMBER: + sprintf(tbuf, "%d", Event1.Data.Value); + added = tbuf; + break; + + case NEED_UNIT: + added = Text_String(UnitTypeClass::As_Reference(Event1.Data.Unit).Full_Name());; + break; + + case NEED_AIRCRAFT: + added = Text_String(AircraftTypeClass::As_Reference(Event1.Data.Aircraft).Full_Name());; + break; + + case NEED_STRUCTURE: + added = Text_String(BuildingTypeClass::As_Reference(Event1.Data.Structure).Full_Name()); + break; + + case NEED_INFANTRY: + added = Text_String(InfantryTypeClass::As_Reference(Event1.Data.Infantry).Full_Name()); + break; + + case NEED_WAYPOINT: + if (Event1.Data.Value < 26) { + sprintf(tbuf, "'%c'", Event1.Data.Value + 'A'); + } else { + sprintf(tbuf, "'%c%c'", (Event1.Data.Value / 26) + 'A'-1, (Event1.Data.Value % 26) + 'A'); + } + added = tbuf; + break; + + default: + break; + } + + /* + ** Persistence indicator value. + */ + char pers = 'V'; + if (IsPersistant == SEMIPERSISTANT) pers = 'S'; + if (IsPersistant == PERSISTANT) pers = 'P'; + + sprintf(_buffer, "%4.4s\t %s %c%c%c %s%s", + IniName, + HouseTypeClass::As_Reference(House).Suffix, + pers, + special, + special2, + Name_From_Event(Event1.Event), + added); + return(_buffer); +} + +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * * + * This routine will examine the trigger events and return with a composit bitfield that * + * indicates what this trigger can be attached to. This is used for trigger placement * + * and logic processing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with AttachType bitfield representing what this trigger can be attached * + * to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +AttachType TriggerTypeClass::Attaches_To(void) const +{ + AttachType attach = ::Attaches_To(Event1.Event); + + if (EventControl != MULTI_ONLY) { + attach = attach | ::Attaches_To(Event2.Event); + } + return(attach); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * This routine reads in the triggers & creates them. Then, other classes can * + * get pointers to the triggers they're linked to. * + * * + * The routine relies on the TeamTypeClasses already being loaded so it can resolve * + * references to teams in this function. * + * * + * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * + * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * + * * + * Object's pointers are set in: * + * InfantryClass::Read_INI() * + * BuildingClass::Read_INI() * + * UnitClass::Read_INI() * + * TerrainClass::Read_INI() * + * The object trigger pointers are cleared in the ObjectClass constructor. * + * * + * The House's EMSListOf triggers is set in this routine, and cleared in the * + * HouseClass::Init() routine. * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This function must be called before any other class's Read_INI. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Read_INI(CCINIClass & ini) +{ + TriggerTypeClass *trigger; // Working trigger pointer. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Create a new trigger. + */ + trigger = new TriggerTypeClass(); + + /* + ** Get the trigger entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** Fill in the trigger. + */ + trigger->Fill_In((char *)entry, buf); + } + + if (NewINIFormat < 2) { + /* + ** Fix up the self-referential trigger pointers. + */ + for (int trig_index = 0; trig_index < TriggerTypes.Count(); trig_index++) { + TriggerTypeClass * trigger = TriggerTypes.Ptr(trig_index); + + char * ptr = (char *)trigger->Action1.Trigger.Raw(); + if (ptr /*&& trigger->Action1.Trigger.Raw() != -1*/) { + trigger->Action1.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + + ptr = (char *)trigger->Action2.Trigger.Raw(); + if (ptr /*&& trigger->Action2.Trigger.Raw() != -1*/) { + trigger->Action2.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + } + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given trigger with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Fill_In(char * name, char * entry) +{ + assert(TriggerTypes.ID(this) == ID); + + /* + ** Set its name. + */ + Set_Name(name); + + IsPersistant = PersistantType(atoi(strtok(entry, ","))); + House = HousesType(atoi(strtok(NULL, ","))); + EventControl = MultiStyleType(atoi(strtok(NULL, ","))); + ActionControl = MultiStyleType(atoi(strtok(NULL, ","))); + + Event1.Read_INI(); + Event2.Read_INI(); + Action1.Read_INI(); + Action2.Read_INI(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * * + * This routine will write out all trigger type objects to the INI database. Any existing * + * trigger types in the database will be cleared out. * + * * + * INPUT: ini -- Reference to the INI database to have the trigger types added. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("Triggers"); + ini.Clear(INI_Name()); + + /* + ** Now write all the trigger data out + */ + for (int index = 0; index < TriggerTypes.Count(); index++) { +// for (int index = TriggerTypes.Count()-1; index >= 0; index--) { + char buf[256]; + TriggerTypeClass * trigger = TriggerTypes.Ptr(index); + + trigger->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), trigger->IniName, buf); + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * * + * This low level routine will take the information in this trigger type and store it * + * into a buffer such that the resultant string can be stored into an INI database for * + * later retrieval. * + * * + * INPUT: buffer -- Pointer to the buffer to store the INI entry string. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the buffer is big enough. Usually 128 bytes is more than sufficient. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Build_INI_Entry(char * buffer) const +{ + /* + ** Build the root portion of the trigger event. + */ + sprintf(buffer, "%d,%d,%d,%d,", IsPersistant, House, EventControl, ActionControl); + + /* + ** Append the event and action values. + */ + buffer += strlen(buffer); + Event1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Event2.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action2.Build_INI_Entry(buffer); +} + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * * + * This routine is called when triggers are assigned to a list box and then must be drawn. * + * It will display an identifying text string with as much information as is useful. * + * * + * INPUT: index -- The index number of this line in the list box. * + * * + * x,y -- The pixel coordinate of the upper left corner of the text box. * + * * + * width,height -- The dimensions of the text box to display the description in. * + * * + * selected -- Is this a selected line? If so, then it should be highlighted. * + * * + * flags -- The text print flags to use to display this text string. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * * + * This routine should be called to initialize the trigger type object system. It should * + * be called when clearing out a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All trigger types will be destroyed by this routine. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Init(void) +{ + TriggerTypes.Free_All(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * * + * Given just an ASCII representation of the trigger type, this routine will return with * + * a pointer to the trigger type it refers to. Typical use of this is when parsing * + * scenario INI files. * + * * + * INPUT: name -- Pointer to the name to use to identify the trigger type class object to * + * be looked up. * + * * + * OUTPUT: Returns with a pointer to the trigger type class object that matches the name * + * specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * TriggerTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int index = 0; index < TriggerTypes.Count(); index++) { + if (stricmp(TriggerTypes.Ptr(index)->Name(), name) == 0) { + return(TriggerTypes.Ptr(index)); + } + } + } + return(NULL); +} diff --git a/CODE/TRIGTYPE.H b/CODE/TRIGTYPE.H new file mode 100644 index 0000000..07cb4e2 --- /dev/null +++ b/CODE/TRIGTYPE.H @@ -0,0 +1,152 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TRIGTYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TRIGTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef TRIGTYPE_H +#define TRIGTYPE_H + +#include "tevent.h" +#include "taction.h" + +class TriggerClass; + +/* +** There can be multiple trigger events and trigger actions. This enumeration is used to +** indicate if there are multiple events/actions and what their relationship is. +*/ +typedef enum MultiStyleType { + MULTI_ONLY, // "Only" main trigger action/event? + MULTI_AND, // "And" secondary trigger action/event? + MULTI_OR, // "Or" secondary event? + MULTI_LINKED // Cause and effect pairs are linked? +} MultiStyleType; + + +class TriggerTypeClass : public AbstractTypeClass +{ + public: + unsigned IsActive:1; + + typedef enum PersistantType { + VOLATILE = 0, + SEMIPERSISTANT = 1, + PERSISTANT = 2 + } PersistantType; + + /* + ** This flag controls whether the trigger destroys itself after it goes + ** off. + ** 0 = trigger destroys itself immediately after going off, and removes + ** itself from all objects it's attached to + ** 1 = trigger is "Semi-Persistent"; it maintains a count of all objects + ** it's attached to, and only actually "springs" after its been + ** triggered from all the objects; then, it removes itself. + ** 2 = trigger is Fully Persistent; it just won't go away. + */ + PersistantType IsPersistant; + + /* + ** For house-specific events, this is the house for that event. + */ + HousesType House; + + /* + ** Each trigger must have an event which activates it. This is the event that is + ** used to activate this trigger. + */ + TEventClass Event1; + TEventClass Event2; + MultiStyleType EventControl; + + /* + ** This is the action to perform when the trigger event occurs. + */ + TActionClass Action1; + TActionClass Action2; + MultiStyleType ActionControl; + + + TriggerTypeClass(void); + TriggerTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Event1(x), Event2(x), Action1(x), Action2(x) {}; + virtual ~TriggerTypeClass(void); + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all trigger types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + void Fill_In(char * name, char * entry); + void Build_INI_Entry(char * buf) const; + + static char * INI_Name(void) {return "Trigs";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Processing routines + */ + TriggerClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + + /* + ** Utility routines + */ + void Detach(TARGET target, bool all=true); + AttachType Attaches_To(void) const; + TARGET As_Target(void) const; + static TriggerTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; +}; + + +#endif diff --git a/CODE/TURBOC.CFG b/CODE/TURBOC.CFG new file mode 100644 index 0000000..a4ef446 --- /dev/null +++ b/CODE/TURBOC.CFG @@ -0,0 +1,37 @@ + -Vt + -Vb- + -a- + -b- + -3 + -ml + -nobj\. + -C- + -c + -Jg + -DIBM=1 + -H=conquer.sym + -Ibc402\include;d:\WATCOM\H;d:\WATCOM\H\win;player + -Id:\library;d:\library\source\hmi\midi\inc;d:\library\source\hmi\digi\inc + -P- + -w + -w! + -X- + -RT + -x + -k- + -vi + -Z + -Oi + -Op + -Og + -Oc + -Ol + -d + -r + -V + -dc + -K2- + -R- + -Oe + -Ob + -Om diff --git a/CODE/TURRET.CPP b/CODE/TURRET.CPP new file mode 100644 index 0000000..985ff5c --- /dev/null +++ b/CODE/TURRET.CPP @@ -0,0 +1,114 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\turret.cpv 3.1 13 Mar 1996 09:49:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TURRET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * TurretClass::Unlimbo -- Unlimboes turret object. * + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "turret.h" + + +/*********************************************************************************************** + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * * + * This is the default destructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::~TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * * + * This is the default constructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * * + * This is the normal constructor for the turret class. It merely sets the turret up to * + * face north. * + * * + * INPUT: classid -- The type id for this particular unit. * + * * + * house -- The house that this unit will belong to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(UnitType classid, HousesType house) : + DriveClass(classid, house) +{ +} + + + diff --git a/CODE/TURRET.H b/CODE/TURRET.H new file mode 100644 index 0000000..36d1dc7 --- /dev/null +++ b/CODE/TURRET.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\turret.h_v 3.1 13 Mar 1996 09:43:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TURRET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TURRET_H +#define TURRET_H + +#include "drive.h" + +class TurretClass : public DriveClass +{ + public: + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + TurretClass(UnitType classid, HousesType house); + TurretClass(void); + virtual ~TurretClass(void); +}; + + +#endif diff --git a/CODE/TXTLABEL.CPP b/CODE/TXTLABEL.CPP new file mode 100644 index 0000000..ae975d2 --- /dev/null +++ b/CODE/TXTLABEL.CPP @@ -0,0 +1,101 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TXTLABEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextLableClass::Draw_Me -- Graphical update routine * + * TextLableClass::TextLabelClass -- Constructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TextLableClass::TextLabelClass -- Constructor * + * * + * INPUT: * + * txt pointer to text buffer to print from * + * x x-coord for text printing * + * y y-coord for text printing * + * color color to print in * + * style style to print (determines the meaning of x & y) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +TextLabelClass::TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style) : GadgetClass(x, y, 1, 1, 0, 0) +{ + Text = txt; + Color = color; + Style = style; + UserData1 = 0; + UserData2 = 0; + PixWidth = -1; +} + + +/*********************************************************************************************** + * TextLableClass::Draw_Me -- Graphical update routine * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state * + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +virtual int TextLabelClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + if (PixWidth == -1) { + Simple_Text_Print(Text, X, Y, Color, TBLACK, Style); +// Fancy_Text_Print(Text, X, Y, Color, TBLACK, Style); + } else { + Conquer_Clip_Text_Print(Text, X, Y, Color, TBLACK, Style, PixWidth); + } + return(true); + } + return(false); +} diff --git a/CODE/TXTLABEL.H b/CODE/TXTLABEL.H new file mode 100644 index 0000000..980a813 --- /dev/null +++ b/CODE/TXTLABEL.H @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TXTLABEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TXTLABEL_H +#define TXTLABEL_H + +class TextLabelClass : public GadgetClass +{ + public: + /* + ** Constructor/Destructor + */ + TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + /* + ** Sets the displayed text of the label + */ + virtual void Set_Text(char *txt) {Text = txt;}; + + /* + ** General-purpose data fields + */ + unsigned long UserData1; + unsigned long UserData2; + TextPrintType Style; + char *Text; + RemapControlType * Color; + int PixWidth; +}; + +#endif + diff --git a/CODE/TXTPRNT.ASM b/CODE/TXTPRNT.ASM new file mode 100644 index 0000000..98edd85 --- /dev/null +++ b/CODE/TXTPRNT.ASM @@ -0,0 +1,506 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* MCGA_Print -- Assembly MCGA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +GLOBAL MCGA_Print : NEAR + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL FontPtr:DWORD +GLOBAL FontXSpacing:DWORD +GLOBAL FontYSpacing:DWORD +GLOBAL ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* MCGA_PRINT -- Assembly MCGA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC MCGA_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP MCGA_Print +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL Get_Font_Palette_Ptr:NEAR + PROC Get_Font_Palette_Ptr C near + mov eax, OFFSET ColorXlat + ret + ENDP Get_Font_Palette_Ptr + + +END diff --git a/CODE/TYPE.H b/CODE/TYPE.H new file mode 100644 index 0000000..b0dd3b6 --- /dev/null +++ b/CODE/TYPE.H @@ -0,0 +1,2024 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/TYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : TYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TYPE_H +#define TYPE_H + +#include "mission.h" +#include "target.h" + +class MapEditClass; +class HouseClass; +class WeaponTypeClass; + + +/*************************************************************************** +** This is the abstract type class. It holds information common to all +** objects that might exist. This contains the name of the object type. +*/ +class AbstractTypeClass +{ + public: + + /* + ** This serves to identify the object class. The ID corresponds to the + ** variation number (e.g., UNIT_TANK1, UNIT_TANK2, etc.). + */ + RTTIType RTTI; + int ID; + + /* + ** This is the internal control name of the object. This name does + ** not change regardless of language specified. This is the name + ** used in scenario control files and for other text based unique + ** identification purposes. + */ + char IniName[24]; + + /* + ** The translated (language specific) text name number of this object. + ** This number is used to fetch the object's name from the language + ** text file. Whenever the name of the object needs to be displayed, + ** this is used to determine the text string. + */ + int FullName; + + AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini); + AbstractTypeClass(NoInitClass const & ) {}; + ~AbstractTypeClass(void) {}; + + RTTIType What_Am_I(void) const {return(RTTI);}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Full_Name(void) const; + char const * Name(void) const {return(IniName);} + void Set_Name(char const * buf) const { + strncpy((char *)IniName, buf, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; + }; + virtual int Get_Ownable(void) const; + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} +}; + + +/********************************************************************** +** Each house has certain unalienable characteristics. This structure +** elaborates these. +*/ +class HouseTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the house number (enum). This is a unique identification + ** number for the house. + */ + HousesType House; + + /* + ** This is the filename suffix to use when creating a house specific + ** file name. It is three characters long. + */ + char Suffix[_MAX_EXT]; + + /* + ** This is the "lemon percentage" to use when determining if a particular + ** object owned by this house is to be flagged as a "lemon". Objects so + ** flagged have a greater break-down chance. The percentage is expressed + ** as a fixed point number with 0x000 meaning 0% and 0x100 meaning 100%. + */ + unsigned Lemon; + + /* + ** This points to the default remap table for this house. + */ + PlayerColorType RemapColor; + + /* + ** This is a unique ASCII character used when constructing filenames. It + ** serves a similar purpose as the "Suffix" element, but is only one + ** character long. + */ + char Prefix; + + /* + ** This controls the various general adjustments to the house owned + ** unit and building ratings. The default value for these ratings is + ** a fixed point number of 1.0. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + //------------------------------------------------------------------------ + HouseTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + HouseTypeClass(HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix); + + unsigned char const * Remap_Table(void) const; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static HousesType From_Name(char const * name); + static HouseTypeClass & As_Reference(HousesType house); + static void One_Time(void); + static void Init_Heap(void); + + virtual bool Read_INI(CCINIClass & ini); +}; + + +/*************************************************************************** +** This the the common base class of game objects. Since these values +** represent the unchanging object TYPES, this data is initialized at game +** start and not changed during play. It is "const" data. +*/ +class ObjectTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the base name of the graphic data associated with this object + ** type. If the graphic name is a null string, then there is no graphic + ** associated with this object type. + */ + char GraphicName[_MAX_FNAME]; + + /* + ** Is this object squashable by heavy vehicles? If it is, then the vehicle + ** can travel over this object and destroy it in the process. + */ + unsigned IsCrushable:1; + + /* + ** Does this object type NOT show up on radar scans? If true, then in any + ** radar display, only the underlying ground will be show, not this object. + ** Most terrain falls into this category, but only a few special real units/buildings + ** do. + */ + unsigned IsStealthy:1; + + /* + ** It is legal to "select" some objects in the game. If it is legal to select this + ** object type then this flag will be true. Selected game objects typically display + ** a floating health bar and allows special user I/O control. + */ + unsigned IsSelectable:1; + + /* + ** Can this object be the target of an attack or move command? Typically, only objects + ** that take damage or can be destroyed are allowed to be a target. + */ + unsigned IsLegalTarget:1; + + /* + ** "Insignificant" objects will not be announced when they are destroyed or when they + ** appear. Terrain elements and some lesser vehicles have this characteristic. + */ + unsigned IsInsignificant:1; + + /* + ** Is this object immune to normal combat damage? Rocks and other inert type terrain + ** object are typically of this type. + */ + unsigned IsImmune:1; + + /* + ** "Sentient" objects are ones that have logic AI processing performed on them. All + ** vehicles, buildings, infantry, and aircraft are so flagged. Terrain elements also + ** fall under this category, but only because certain animation effects require this. + */ + unsigned IsSentient:1; + + /* + ** If this object type affects the occupation and collision logic associated with + ** cells, then this flag will be true. Typically, this characteristic is limited + ** to buildings, units, terrain objects, and landed aircraft. + */ + unsigned IsFootprint:1; + + /* + ** The defense of this object is greatly affected by the type of armor + ** it possesses. This value specifies the type of armor. + */ + ArmorType Armor; + + /* + ** This is the maximum strength of this object type. + */ + unsigned short MaxStrength; + + /* + ** These point to the shape imagery for this object type. Since the shape imagery + ** exists in a separate file, the data is filled in after this object is constructed. + ** The "mutable" keyword allows easy modification to this otherwise const object. + */ + void const * ImageData; + + /* + ** Points to the dimension data for each shape in the image list. By using this + ** data, the minimum number of cells will be redrawn when the object changes shape. + */ + Rect * DimensionData; + + /* + ** This points to the radar imagery for this object. + */ + void const * RadarIcon; + + //-------------------------------------------------------------------- + ObjectTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + ObjectTypeClass( RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int fullname, + char const * name + ); + + static void One_Time(void); + + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRYTYPE || RTTI == RTTI_UNITTYPE || RTTI == RTTI_VESSELTYPE || RTTI == RTTI_AIRCRAFTTYPE);}; + char const * Graphic_Name(void) const {if (GraphicName[0] != '\0') return(GraphicName); return(Name());} + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const = 0; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual void const * Get_Cameo_Data(void) const; + void const * Get_Image_Data(void) const {return ImageData;}; + void const * Get_Radar_Data(void) const {return RadarIcon;}; + + #ifdef SCENARIO_EDITOR + virtual void Display(int, int, WindowNumberType, HousesType) const {}; + #endif + + static void const * SelectShapes; + static void const * PipShapes; +}; + + +/*************************************************************************** +** This class is the common data for all objects that can be owned, produced, +** or delivered as reinforcements. These are objects that typically contain +** crews and weapons -- the fighting objects of the game. +*/ +class TechnoTypeClass : public ObjectTypeClass +{ + public: + + /* + ** This controls how this object type is remapped when it is displayed + ** in the sidebar. + */ + RemapType Remap; + + /* + ** Is this object ownable by all sides in a multiplayer game? There are some + ** special case objects that need this override ability. + */ + unsigned IsDoubleOwned:1; + + /* + ** If this object should be completely and always invisible to the enemy, then + ** this flag will be true. + */ + unsigned IsInvisible:1; + + /* + ** If this object can serve as a good leader for a group selected + ** series of objects, then this flag will be true. Unarmed or + ** ability challenged units do not make good leaders. This flag is + ** also used to indicate the primary factory when dealing with + ** buildings. + */ + unsigned IsLeader:1; + + /* + ** Does this object have the ability to detect the presence of a nearby + ** cloaked object? + */ + unsigned IsScanner:1; + + /* + ** If this object is always given its proper name rather than a generic + ** name, then this flag will be true. Typically, civilians and Dr. Moebius + ** fall under this category. + */ + unsigned IsNominal:1; + + /* + ** If the artwork for this object (only for generics) is theater specific, then + ** this flag will be true. Civilian buildings are a good example of this. + */ + unsigned IsTheater:1; + + /* + ** Does this object type contain a rotating turret? Gun emplacements, SAM launchers, + ** and many vehicles contain a turret. If a turret is present, special rendering and + ** combat logic must be performed. + */ + unsigned IsTurretEquipped:1; + + /* + ** Certain objects can be repaired. For buildings, they repair "in place". For units, + ** they must travel to a repair center to be repaired. If this flag is true, then + ** allow the player or computer AI to repair the object. + */ + unsigned IsRepairable:1; + + /* + ** Does this object contain a crew? If it does, then when the object is destroyed, there + ** is a distinct possibility that infantry will "pop out". Only units with crews can + ** become "heros". + */ + unsigned IsCrew:1; + + /* + ** This tells whether this unit should EVER be remapped when it is displayed + ** on the tactical map. Normally, the unit is remapped, but for certain civilian + ** object, remapping is not to be performed, regardless of owner. + */ + unsigned IsRemappable:1; + + /* + ** Is the unit capable of cloaking? Only Stealth Tank can do so now. + */ + unsigned IsCloakable:1; + + /* + ** Can this object self heal up to half strength? Mammoth tanks from C&C had this + ** feature. + */ + unsigned IsSelfHealing:1; + + /* + ** If this object explodes violently when destroyed, then this flag will be true. + ** The type of explosion is based on the warhead type and the damage generated + ** corresponds to the full strength of the object. + */ + unsigned IsExploding:1; + + /* + ** This specifies the zone that an object of this type should recognize. Zones + ** of this type or lower will be considered "possible to travel to". + */ + MZoneType MZone; + + /* + ** When determining threat, the range can be overridden to be the value + ** specified here. Otherwise, the range for enemy scan is equal to the + ** longest weapon range the object has. If the value is zero, then the + ** weapon range is used. + */ + LEPTON ThreatRange; + + /* + ** If this is a transporter object (e.g., hovercraft, chinook, APC), then this + ** value specifies the maximum number of passengers it may carry. + */ + int MaxPassengers; + + /* + ** Most objects have the ability to reveal the terrain around themselves. + ** This sight range (expressed in cell distance) is specified here. If + ** this value is 0, then this unit never reveals terrain. Bullets are + ** typically of this nature. + */ + int SightRange; + + /* + ** This is the credit cost to produce this object (presuming production is + ** allowed). + */ + int Cost; + + /* + ** The tech level that this object can be produced at. + */ + unsigned Level; + + /* + ** This specifies the building prerequisites required before an object + ** of this type can be produced. + */ + long Prerequisite; + + /* + ** The risk and reward values are used to determine targets and paths + ** toward targets. When heading toward a target, a path of least + ** risk will be followed. When picking a target, the object of + ** greatest reward will be selected. The values assigned are + ** arbitrary. + */ + int Risk,Reward; + + /* + ** This value indicates the maximum speed that this object can achieve. + */ + MPHType MaxSpeed; + + /* + ** This indicates the speed (locomotion) type for this unit. Through this + ** value the movement capabilities are deduced. + */ + SpeedType Speed; + + /* + ** This is the maximum number of ammo shots this object can hold. If + ** this number is -1, then this indicates unlimited ammo. + */ + int MaxAmmo; + + /* + ** This is a bit field representing the houses that are allowed to + ** own (by normal means) this particular object type. This value is + ** typically used in production contexts. It is possible for a side + ** to take possession of an object type otherwise not normally allowed. + ** This event usually occurs as a result of capture. + */ + long Ownable; + + /* + ** This is the small icon image that is used to display the object in + ** the sidebar for construction selection purposes. + */ + void const * CameoData; + + /* + ** The number of animation frames allotted to rotation is specified here. + ** For an object that has no rotation, this value will be 1. For normal + ** vehicles this value will be 32. There are some special case units that + ** have intermediate rotation frames. + */ + int Rotation; + + /* + ** This is the rotational speed of the object. This value represents the + ** turret or body rotation speed expresses as 360/256ths rotation steps per + ** game tick. + */ + int ROT; + + /* + ** These are the weapons that this techno object is armed with. + */ + WeaponTypeClass const * PrimaryWeapon; + WeaponTypeClass const * SecondaryWeapon; + + /* + ** These specify the lepton offsets to locate the exact coordinate of the + ** 'tip of the barrel' for the weapon. This is used for generating the bullet + ** at the proper location. + */ + int VerticalOffset; // Distance to move north (compensates for perspective). + int PrimaryOffset; // Offset along turret centerline and facing. + int PrimaryLateral; // Sideways offset from turret centerline and facing. + int SecondaryOffset; + int SecondaryLateral; + + /* + ** Points you're awarded for destroying an object of this type, and + ** points you lose if you lose an object of this type. + */ + int Points; + + //-------------------------------------------------------------------- + TechnoTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed + ); + + bool Is_Two_Shooter(void) const; + int Legal_Placement(CELL pos) const; + virtual int Raw_Cost(void) const; + virtual int Max_Passengers(void) const {return(MaxPassengers);} + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + virtual void const * Get_Cameo_Data(void) const; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual int Get_Ownable(void) const; + virtual bool Read_INI(CCINIClass & ini); + + /* + ** This is a pointer to the wake shape (as needed by the gunboat). + */ + static void const * WakeShapes; + static void const * TurretShapes; + static void const * SamShapes; + static void const * MGunShapes; +}; + + +/*************************************************************************** +** Building types need some special information custom to buildings. This +** is a derived class that elaborates these additional data elements. +*/ +class BuildingTypeClass : public TechnoTypeClass { + public: + /* + ** Is this building allowed to be considered for building adjacency + ** checking? If false, then building off of (or adjacent to) this building + ** is not considered. + */ + unsigned IsBase:1; + + /* + ** If this building is a fake, this flag will be set. + */ + unsigned IsFake:1; + + /* + ** This flag controls whether the building is equiped with a dirt + ** bib or not. A building with a bib has a dirt patch automatically + ** attached to the structure when it is placed. + */ + unsigned IsBibbed:1; + + /* + ** If this building is a special wall type, such that it exists as a building + ** for purposes of construction but transforms into an overlay wall object when + ** it is placed on the map, then this flag will be true. + */ + unsigned IsWall:1; + + /* + ** Buildings can have either simple or complex damage stages. If simple, + ** then the second to the last frame is the half damage stage, and the last + ** frame is the complete damage stage. For non-simple damage, buildings + ** have a complete animation set for damaged as well as undamaged condition. + ** Turrets, oil pumps, and repair facilities are a few examples. + */ + unsigned IsSimpleDamage:1; + + /* + ** Certain building types can be captures by enemy infantry. For those + ** building types, this flag will be true. Typically, military or hardened + ** structures such as turrets cannot be captured. + */ + unsigned IsCaptureable:1; + + /* + ** If this building really only has cosmetic idle animation, then this flag will be + ** true if this animation should run at a relatively constant rate regardless of game + ** speed setting. + */ + unsigned IsRegulated:1; + + /* + ** Does this building require power to function? Usually, this isn't the case. The building + ** normally either has no effect by power level or is gradually reduced in effectiveness. This + ** flag is for those buildings that completely cease to function when the power drops below + ** full. + */ + unsigned IsPowered:1; + + /* + ** If this flag is true, then the building cannot be sold even if it could have been built. This + ** is especially useful for mines which can be built but cannot be sold. + */ + unsigned IsUnsellable:1; + + /* + ** This is the direction (from the center cell) of the building in order to find a + ** legitimate foundation square. This location will be used for targeting and capture + ** move destination purposes. + */ + FacingType FoundationFace; + + /* + ** Adjacent distance for building next to. + */ + int Adjacent; + + /* + ** This flag specifies the type of object this factory building can "produce". For non + ** factory buildings, this value will be RTTI_NONE. + */ + RTTIType ToBuild; + + /* + ** For building that produce ground units (infantry and vehicles), there is a default + ** exit point defined. This point is where the object is first placed on the map. + ** Typically, this is located next to a door. The unit will then travel on to a clear + ** terrain area and enter normal game processing. + */ + COORDINATE ExitCoordinate; + + /* + ** When determine which cell to head toward when exiting a building, use the + ** list elaborated by this variable. There are directions of exit that are + ** more suitable than others. This list is here to inform the system which + ** directions those are. + */ + short const * ExitList; + + /* + ** This is the structure type identifier. It can serve as a unique + ** identification number for building types. + */ + StructType Type; + + /* + ** This is the starting facing to give this building when it first + ** gets constructed. The facing matches the final stage of the + ** construction animation. + */ + DirType StartFace; + + /* + ** This is the Tiberium storage capacity of the building. The sum of all + ** building's storage capacity is used to determine how much Tiberium can + ** be accumulated. + */ + int Capacity; + + /* + ** Each building type produces and consumes power. These values tell how + ** much. + */ + int Power; + int Drain; + + /* + ** This is the size of the building. This size value is a rough indication + ** of the building's "footprint". + */ + BSizeType Size; + + /********************************************************************** + ** For each stage that a building may be in, its animation is controlled + ** by this structure. It dictates the starting and length of the animation + ** frames needed for the specified state. In addition it specifies how long + ** to delay between changes in animation. With this data it is possible to + ** control the appearance of all normal buildings. Turrets and SAM sites are + ** an exception since their animation is not merely cosmetic. + */ + typedef struct { + int Start; // Starting frame of animation. + int Count; // Number of frames in this animation. + int Rate; // Number of ticks to delay between each frame. + } AnimControlType; + AnimControlType Anims[BSTATE_COUNT]; + + /*--------------------------------------------------------------------------- + ** This is the building type explicit constructor. + */ + BuildingTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + BuildingTypeClass ( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap + ); + operator StructType(void) const {return(Type);}; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BuildingTypeClass & As_Reference(StructType type); + static StructType From_Name(char const * name); + static void Init(TheaterType theater); + static void One_Time(void); + static void Prep_For_Add(void); + + int Width(void) const; + int Height(bool bib=false) const; + + virtual int Full_Name(void) const; + virtual bool Read_INI(CCINIClass & ini); + bool Flush_For_Placement(CELL cell, HouseClass * house) const; + virtual int Cost_Of(void) const; + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual void const * Get_Buildup_Data(void) const {return(BuildupData);}; + + bool Is_Factory(void) const {return(ToBuild != RTTI_NONE);} + virtual int Raw_Cost(void) const; + bool Bib_And_Offset(SmudgeType & bib, CELL & cell) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** Special overlay for the weapons factory. + */ + static void const * WarFactoryOverlay; + + private: + + /* + ** This is a pointer to a list of offsets (from the upper left corner) that + ** are used to indicate the building's "footprint". This footprint is used + ** to determine building placement legality and terrain passibility. + */ + short const * OccupyList; + + /* + ** Buildings can often times overlap a cell but not actually "occupy" it for + ** purposes of movement. This points to a list of offsets that indicate which + ** cells the building has visual overlap but does not occupy. + */ + short const * OverlapList; + + /* + ** The construction animation graphic data pointer is + ** pointed to by this element. + */ + void const * BuildupData; + + void Init_Anim(BStateType state, int start, int count, int rate) const; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class UnitTypeClass : public TechnoTypeClass +{ + public: + /* + ** If this unit can appear out of a crate, then this flag will be true. + */ + unsigned IsCrateGoodie:1; + + /* + ** Can this unit squash infantry? If it can then if the player selects + ** an (enemy) infantry unit as the movement target, it will ride over and + ** squish the infantry unit. + */ + unsigned IsCrusher:1; + + /* + ** Does this unit go into harvesting mode when it stops on a tiberium + ** field? Typically, only one unit does this and that is the harvester. + */ + unsigned IsToHarvest:1; + + /* + ** Some units are equipped with a rotating radar dish. These units have special + ** animation processing. The rotating radar dish is similar to a turret, but + ** always rotates and does not affect combat. + */ + unsigned IsRadarEquipped:1; + + /* + ** If this unit has a firing animation, this flag is true. Infantry and some special + ** vehicles are the ones with firing animations. + */ + unsigned IsFireAnim:1; + + /* + ** Many vehicles have a turret with restricted motion. These vehicles must move the + ** turret into a locked down position while travelling. Rocket launchers and artillery + ** are good examples of this kind of unit. + */ + unsigned IsLockTurret:1; + + /* + ** Is this unit of the humongous size? Harvesters and mobile construction vehicles are + ** of this size. If the vehicle is greater than 24 x 24 but less than 48 x 48, it is + ** considered "Gigundo". + */ + unsigned IsGigundo:1; + + /* + ** Does this unit have a constant animation (like Visceroid?) + */ + unsigned IsAnimating:1; + + /* + ** Does this unit have the ability to jam radar facilities? + */ + unsigned IsJammer:1; + + /* + ** Is this unit a mobile gap generator? + */ + unsigned IsGapper:1; + + /* + ** If this unit cannot fire while moving, then this flag will be + ** true. Such a unit must stop and stabilize for a bit before it + ** can fire. + */ + unsigned IsNoFireWhileMoving:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + UnitType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + UnitTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + UnitTypeClass ( + UnitType type, + int name, + char const * ininame, + AnimType exp, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_goodie, + bool is_nominal, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_insignificant, + bool is_turret_equipped, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_gigundo, + bool is_animating, + bool is_jammer, + bool is_gapper, + int rotation, + int toffset, + MissionType order + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static UnitType From_Name(char const * name); + static UnitTypeClass & As_Reference(UnitType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** The animation stage list for harvester dumping into the refinery. + */ + static const int Harvester_Dump_List[22]; + + /* + ** The animatino stage list for harvester loading up on ore. + */ + static const int Harvester_Load_List[9]; + + /* + ** The number of animation stages when the harvester is loading + ** up on ore in the field. + */ + static const int Harvester_Load_Count; +}; + + +/*************************************************************************** +** This specifies the constant attribute data associated with naval +** vessels. +*/ +class VesselTypeClass : public TechnoTypeClass +{ + public: + /* + ** Does this unit have only 8 facings? Special test units have limited + ** facings. + */ + unsigned IsPieceOfEight:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + VesselType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + VesselTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + VesselTypeClass ( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static VesselType From_Name(char const * name); + static VesselTypeClass & As_Reference(VesselType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + virtual short const * Overlap_List(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class InfantryTypeClass : public TechnoTypeClass +{ + public: + + /* + ** If this civilian infantry type is female, then this flag + ** will be true. This information is used to get the correct + ** voice response. + */ + unsigned IsFemale:1; + + /* + ** Does this infantry unit have crawling animation? If not, then this + ** means that the "crawling" frames are actually running animation frames. + */ + unsigned IsCrawling:1; + + /* + ** For those infantry types that can capture buildings, this flag + ** will be set to true. Typically, this is the engineer. + */ + unsigned IsCapture:1; + + /* + ** For infantry types that will run away from any damage causing + ** events, this flag will be true. Typically, this is so for all + ** civilians as well as the flame thrower guys. + */ + unsigned IsFraidyCat:1; + + /* + ** This flags whether this infantry is actually a civilian. A + ** civilian uses different voice responses, has less ammunition, + ** and runs from danger more often. + */ + unsigned IsCivilian:1; + + /* + ** If the infantry unit is equipped with C4 explosives, then this + ** flag will be true. Such infantry can enter and destroy enemy + ** buildings. + */ + unsigned IsBomber:1; + + /* + ** This flags whether this infantry is actually a dog. A dog + ** uses different voice responses, has no ammo, and runs instead + ** of walks to attack. + */ + unsigned IsDog:1; + + /* + ** This flag specifies whether this infantry type should use the + ** override remap table, instead of the house remap table. This is + ** used to turn the two civilian animations into a veritable smorgasbord + ** of civilian types, for example. + */ + unsigned IsRemapOverride:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + InfantryType Type; + + /* + ** When this infantry unit is loaded onto a transport, then this + ** is the pip shape to use. Primarily, this is a color control. + */ + PipEnum Pip; + + /* + ** This is an array of the various animation frame data for the actions that + ** the infantry may perform. + */ + DoInfoStruct const * DoControls; + + /* + ** There are certain units with special animation sequences built into the + ** shape file. These values tell how many frames are used for the firing animation. + */ + char FireLaunch; + char ProneLaunch; + + /* + ** This is a pointer to the special override remap table, which is + ** used only in conjunction with the IsRemapOverride flag, and is + ** primarily used for the civilians. + */ + unsigned char const * OverrideRemap; + + /* + ** This is the explicit unit class constructor. + */ + InfantryTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * controls, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static InfantryType From_Name(char const * name); + static InfantryTypeClass & As_Reference(InfantryType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int & width, int & height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual int Full_Name(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/**************************************************************************** +** The various aircraft types are controlled by object types of +** this class. +*/ +class AircraftTypeClass : public TechnoTypeClass +{ + public: + + /* + ** Fixed wing aircraft (ones that cannot hover) have this flag set to true. + ** Such aircraft will not vary speed while it is flying. + */ + unsigned IsFixedWing:1; + + /* + ** Can this aircraft land? If it can land it is presumed to be controllable by the player. + */ + unsigned IsLandable:1; + + /* + ** Does this aircraft have a rotor blade (helicopter) type propulsion? + */ + unsigned IsRotorEquipped:1; // Is a rotor attached? + + /* + ** Is there a custom rotor animation stage set for each facing of the aircraft? + */ + unsigned IsRotorCustom:1; // Custom rotor sets for each facing? + + /* + ** This is the kind of aircraft identifier number. + */ + AircraftType Type; + + /* + ** This specifies the default mission order for this aircraft. Some aircraft default + ** to guard mode (e.g., helicopters) while some default to attack mode (e.g., bombers). + */ + MissionType Mission; + + /* + ** This is the preferred landing building. The aircraft will try to land at the + ** building of this type. + */ + StructType Building; + + /* + ** This is the final approach speed of this aircraft type for landing + ** at an airfield. Most aircraft hit it at full speed, but the MIG is + ** an example of a plane that needs a slower approach speed to hit the + ** airfield. + */ + int LandingSpeed; + + AircraftTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static AircraftType From_Name(char const * name); + static AircraftTypeClass & As_Reference(AircraftType a); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL, HousesType) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual int Max_Pips(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + static void const * LRotorData; + static void const * RRotorData; +}; + + +/*************************************************************************** +** Bullets and other projectiles need some specific information according +** to their type. +*/ +class BulletTypeClass : public ObjectTypeClass +{ + public: + + /* + ** Does this bullet type fly over walls? + */ + unsigned IsHigh:1; + + /* + ** Does this bullet need a shadow drawn under it? Shadowed bullets + ** use the Height value to offset their Y position. + */ + unsigned IsShadow:1; + + /* + ** If this projectile is one that ballistically arcs from ground level, up into the air and + ** then back to the ground, where it explodes. Typical uses of this are for grenades and + ** artillery shells. + */ + unsigned IsArcing:1; + + /* + ** Certain projectiles do not travel horizontally, but rather, vertically -- they drop + ** from a height. Bombs fall into this category and will have this value set to + ** true. Dropping projectiles do not calculate collision with terrain (such as walls). + */ + unsigned IsDropping:1; + + /* + ** Is this projectile invisible? Some bullets and weapon effects are not directly + ** rendered. Small caliber bullets and flame thrower flames are treated like + ** normal projectiles for damage purposes, but are displayed using custom + ** rules. + */ + unsigned IsInvisible:1; + + /* + ** Does this bullet explode when near the target? Some bullets only explode if + ** it actually hits the target. Some explode even if nearby. + */ + unsigned IsProximityArmed:1; + + /* + ** Does this projectile spew puffs of smoke out its tail while it + ** travels? Missiles are prime examples of this projectile type. + */ + unsigned IsFlameEquipped:1; + + /* + ** Should fuel consumption be tracked for this projectile? Rockets are the primary + ** projectile with this characteristic, but even for bullets it should be checked so that + ** bullets don't travel too far. + */ + unsigned IsFueled:1; + + /* + ** Is this projectile without different facing visuals? Most plain bullets do not change + ** visual imagery if their facing changes. Rockets, on the other hand, are equipped with + ** the full 32 facing imagery. + */ + unsigned IsFaceless:1; + + /* + ** If this is a typically inaccurate projectile, then this flag will be true. Artillery + ** is a prime example of this type. + */ + unsigned IsInaccurate:1; + + /* + ** If the bullet contains translucent pixels, then this flag will be true. These + ** translucent pixels really are "shadow" pixels in the same style as the shadow + ** cast by regular ground units. + */ + unsigned IsTranslucent:1; + + /* + ** If this bullet can be fired on aircraft, then this flag will be true. + */ + unsigned IsAntiAircraft:1; + + /* + ** If this bullet can fire upon ground targets, then this flag will be true. + */ + unsigned IsAntiGround:1; + + /* + ** If this bullet can be fired upon submarines (that are submerged), then + ** this flag will be true. + */ + unsigned IsAntiSub:1; + + /* + ** If this bullet should lose strength as it travels toward the target, then + ** this flag will be true. + */ + unsigned IsDegenerate:1; + + /* + ** Does this projectile travel under the water? If so, then its imagery will be modified + ** to look like it is doing so. + */ + unsigned IsSubSurface:1; + + /* + ** If this projectile is equipped with a parachute, then this flag will be set. Parachute + ** bombs are usually the only one with this flag set. + */ + unsigned IsParachuted:1; + + /* + ** Is this unit of the humongous size? Certain very large projectiles have + ** this flag set. Typically, they require a special offset list so that the cells + ** they overlap will be properly redrawn. + */ + unsigned IsGigundo:1; + + /* + ** This element is a unique identification number for the bullet + ** type. + */ + BulletType Type; + + /* + ** This is the rotation speed of the bullet. It only has practical value + ** for those projectiles that performing homing action during flight -- such + ** as with rockets. If the ROT is zero, then no homing is performed. Otherwise + ** the projectile is considered to be a homing type. + */ + unsigned char ROT; + + /* + ** Some projectiles have a built in arming distance that must elapse before the + ** projectile may explode. If this value is non-zero, then this override is + ** applied. + */ + int Arming; + + /* + ** If this bullet is of the tumbling type, then this is the modulo to factor + ** into the game frame when determining what shape number to use for the + ** imagery. + */ + int Tumble; + + //--------------------------------------------------------------------- + BulletTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + BulletTypeClass(char const * name); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BulletTypeClass & As_Reference(BulletType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const {return false;}; + virtual ObjectClass * Create_One_Of(HouseClass *) const {return 0;}; +}; + + +/**************************************************************************** +** These are the different TYPES of terrain objects. Every terrain object must +** be one of these types. +*/ +class TerrainTypeClass : public ObjectTypeClass +{ + public: + /* + ** Which terrain object does this class type represent. + */ + TerrainType Type; + + /* + ** This is the coordinate offset (from upper left) of where the center base + ** position of the terrain object lies. For trees, this would be the base of + ** the trunk. This is used for sorting purposes. + */ + COORDINATE CenterBase; + + /* + ** This is the bitfield control that tells which theater this terrain object is + ** valid for. If the bit (1 << TheaterType) is true, then this terrain object + ** is allowed. + */ + int Theater; + + /* + ** Does this terrain object get placed on the water instead of the ground? + */ + unsigned IsWaterBased:1; + + //---------------------------------------------------------------- + TerrainTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_immune, + bool is_water, + char const * ininame, + int fullname, + short const * occupy, + short const * overlap); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static TerrainType From_Name(char const * name); + static TerrainTypeClass & As_Reference(TerrainType type); + static void Init(TheaterType theater = THEATER_TEMPERATE); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house=HOUSE_NONE) const; + #endif + + private: + short const * Occupy; + short const * Overlap; +}; + + +/**************************************************************************** +** The tile type objects are controlled by this class. It specifies the form +** of the tile set for the specified object as well as other related datum. +** It is derived from the ObjectTypeClass solely for the purpose of scenario +** editing and creation. +*/ +class TemplateTypeClass: public ObjectTypeClass +{ + public: + /* + ** What template is this. + */ + TemplateType Type; + + /* + ** A bitfield container that indicates which theaters this template is allowed + ** in. A bit set in the (1<. +*/ + +/* $Header: /CounterStrike/UDATA.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : UDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * UnitTypeClass::Display -- Displays a generic unit shape. * + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * UnitTypeClass::Init_Heap -- Initialize the unit type class heap. * + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * UnitTypeClass::Read_INI -- Fetch the unit type data from the INI database. * + * UnitTypeClass::Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * UnitTypeClass::operator delete -- Return a unit type class object back to the pool. * + * UnitTypeClass::operator new -- Allocates an object from the unit type class heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the list of animation stages to use when the harvester +** is to dump its load into the refinery. The offsets are based from the +** start of the dump animation. +*/ +const int UnitTypeClass::Harvester_Dump_List[22] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,6,5,4,3,2,1,0}; +const int UnitTypeClass::Harvester_Load_List[9] = {0, 1, 2, 3, 4, 5, 6, 7, 0}; +const int UnitTypeClass::Harvester_Load_Count = 8; + + +// V2 rocket launcher +static UnitTypeClass const UnitV2Launcher( + UNIT_V2_LAUNCHER, + TXT_V2_LAUNCHER, // NAME: Text name of this unit type. + "V2RL", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Light tank +static UnitTypeClass const UnitLTank( + UNIT_LTANK, + TXT_LTANK, // NAME: Text name of this unit type. + "1TNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0020, // Vertical offset. + 0x00C0, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Heavy tank +static UnitTypeClass const UnitMTank( + UNIT_MTANK, + TXT_MTANK, // NAME: Text name of this unit type. + "3TNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0040, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0018, // Primary weapon lateral offset along turret centerline. + 0x0080, // Secondary weapon offset along turret centerline. + 0x0018, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Medium tank +static UnitTypeClass const UnitMTank2( + UNIT_MTANK2, + TXT_MTANK2, // NAME: Text name of this unit type. + "2TNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x00C0, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x00C0, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mastadon tank +static UnitTypeClass const UnitHTank( + UNIT_HTANK, + TXT_HTANK, // NAME: Text name of this unit type. + "4TNK", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0020, // Vertical offset. + 0x00C0, // Primary weapon offset along turret centerline. + 0x0028, // Primary weapon lateral offset along turret centerline. + 0x0008, // Secondary weapon offset along turret centerline. + 0x0040, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile Radar Jammer +static UnitTypeClass const UnitMRJammer( + UNIT_MRJ, + TXT_MRJ, // NAME: Text name of this unit type. + "MRJ", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + true, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + true, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile Gap Generator +static UnitTypeClass const UnitMGG( + UNIT_MGG, + TXT_MGG, // NAME: Text name of this unit type. + "MGG", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + true, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + true, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Artillery +static UnitTypeClass const UnitArty( + UNIT_ARTY, + TXT_ARTY, // NAME: Text name of this unit type. + "ARTY", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0040, // Vertical offset. + 0x0060, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Harvester +static UnitTypeClass const UnitHarvester( + UNIT_HARVESTER, + TXT_HARVESTER, // NAME: Text name of this unit type. + "HARV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + true, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + true, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HARVEST // ORDERS: Default order to give new unit. +); + +// Mobile construction vehicle +static UnitTypeClass const UnitMCV( + UNIT_MCV, + TXT_MCV, // NAME: Text name of this unit type. + "MCV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Jeep (hummer) +static UnitTypeClass const UnitJeep( + UNIT_JEEP, + TXT_JEEP, // NAME: Text name of this unit type. + "JEEP", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0030, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0030, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Armored personnel carrier +static UnitTypeClass const UnitAPC( + UNIT_APC, + TXT_APC, // NAME: Text name of this unit type. + "APC", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0030, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0030, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mine laying truck +static UnitTypeClass const UnitMineLayer( + UNIT_MINELAYER, + TXT_MINE_LAYER, // NAME: Text name of this unit type. + "MNLY", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + true, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Convoy Truck +static UnitTypeClass const UnitConvoyTruck( + UNIT_TRUCK, + TXT_TRUCK, // NAME: Text name of this unit type. + "TRUK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +#ifdef FIXIT_ANTS +/* +[ANT] +Name=Warrior Ant +Primary=Mandible +Strength=150 +Armor=light +TechLevel=-1 +Sight=2 +Speed=5 +Cost=700 +Points=40 +ROT=5 +Tracked=yes +Crewed=no +NoMovingFire=yes + +; Ant mandible +[Mandible] +Damage=50 +ROF=5 +Range=1.5 +Projectile=Invisible +Speed=100 +Warhead=HollowPoint +Report=none + + +*/ + +// Warrior ant +static UnitTypeClass const UnitAnt1( + UNIT_ANT1, + TXT_NONE, // NAME: Text name of this unit type. + "ANT1", // NAME: Text name of this unit type. + ANIM_ANT_DEATH, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + true, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 8, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); +static UnitTypeClass const UnitAnt2( + UNIT_ANT2, + TXT_NONE, // NAME: Text name of this unit type. + "ANT2", // NAME: Text name of this unit type. + ANIM_ANT_DEATH, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + true, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 8, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); +static UnitTypeClass const UnitAnt3( + UNIT_ANT3, + TXT_NONE, // NAME: Text name of this unit type. + "ANT3", // NAME: Text name of this unit type. + ANIM_ANT_DEATH, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + true, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 8, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Chrono Tank +static UnitTypeClass const UnitChrono( + UNIT_CHRONOTANK, + TXT_CHRONOTANK, // NAME: Text name of this unit type. + "CTNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Tesla Tank +static UnitTypeClass const UnitTesla( + UNIT_TESLATANK, + TXT_TESLATANK, // NAME: Text name of this unit type. + "TTNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + true, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + true, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// M.A.D. Tank +static UnitTypeClass const UnitMAD( + UNIT_MAD, + TXT_MAD, // NAME: Text name of this unit type. + "QTNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + + +// Demolition Truck +static UnitTypeClass const UnitDemoTruck( + UNIT_DEMOTRUCK, + TXT_DEMOTRUCK, // NAME: Text name of this unit type. + "DTRK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + false, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 +static UnitTypeClass const UnitPhase( + UNIT_PHASE, + TXT_PHASETRANSPORT, // NAME: Text name of this unit type. + "STNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + REMAP_NORMAL, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0030, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0030, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Can this be a goodie surprise from a crate? + false, // Always use the given name for the vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + false, // Is it insignificant (won't be announced)? + true, // Is it equipped with a combat turret? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? +true,// false, // Is this a gigundo-rotund-enormous unit? + false, // Does the unit have a constant animation? + false, // Is the unit capable of jamming radar? + false, // Is the unit a mobile gap generator? + 32, // Rotation stages. + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +#endif +#endif + +/*********************************************************************************************** + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the unit types. It is used to initialize the unit type class * + * structure. The unit type class is used to control the behavior of the various types * + * of units in the game. This constructor is called for every unique unit type as it * + * exists in the array of unit types. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass::UnitTypeClass( + UnitType type, + int name, + char const * ininame, + AnimType exp, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_goodie, + bool is_nominal, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_insignificant, + bool is_turret_equipped, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_gigundo, + bool is_animating, + bool is_jammer, + bool is_gapper, + int rotation, + int toffset, + MissionType order) : + TechnoTypeClass(RTTI_UNITTYPE, + int(type), + name, + ininame, + remap, + verticaloffset, + primaryoffset, + primarylateral, + secondaryoffset, + secondarylateral, + is_nominal, + is_stealthy, + true, + true, + is_insignificant, + false, + false, + is_turret_equipped, + true, + true, + rotation, + SPEED_TRACK), + IsCrateGoodie(is_goodie), + IsCrusher(is_crusher), + IsToHarvest(is_harvest), + IsRadarEquipped(is_radar_equipped), + IsFireAnim(is_fire_anim), + IsLockTurret(is_lock_turret), + IsGigundo(is_gigundo), + IsAnimating(is_animating), + IsJammer(is_jammer), + IsGapper(is_gapper), + IsNoFireWhileMoving(false), + Type(type), + TurretOffset(toffset), + Mission(order), + Explosion(exp), + MaxSize(0) +{ + /* + ** Forced unit overrides form the default. + */ + Speed = SPEED_WHEEL; +} + + +/*********************************************************************************************** + * UnitTypeClass::operator new -- Allocates an object from the unit type class heap. * + * * + * Use this routine to allocate a unit type class object from the special heap that is * + * maintained for this purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated unit type class object. If there is * + * no more room to allocate another unit type class object, then NULL will be * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * UnitTypeClass::operator new(size_t) +{ + return(UnitTypes.Alloc()); +} + + +/*********************************************************************************************** + * UnitTypeClass::operator delete -- Return a unit type class object back to the pool. * + * * + * This will return a previously allocated unit to the memory pool from whence it came. * + * * + * INPUT: pointer -- A Pointer to the unit type class object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::operator delete(void * pointer) +{ + UnitTypes.Free((UnitTypeClass *)pointer); +} + + +/*********************************************************************************************** + * UnitTypeClass::Init_Heap -- Initialize the unit type class heap. * + * * + * This initializes the unit type class heap by pre-allocated all the known unit types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once and call it before processing the rules.ini file. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Init_Heap(void) +{ + /* + ** These unit type class objects must be allocated in the exact order that they + ** are specified in the UnitType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new UnitTypeClass(UnitHTank); // UNIT_HTANK + new UnitTypeClass(UnitMTank); // UNIT_MTANK + new UnitTypeClass(UnitMTank2); // UNIT_MTANK2 + new UnitTypeClass(UnitLTank); // UNIT_LTANK + new UnitTypeClass(UnitAPC); // UNIT_APC + new UnitTypeClass(UnitMineLayer); // UNIT_MINELAYER + new UnitTypeClass(UnitJeep); // UNIT_JEEP + new UnitTypeClass(UnitHarvester); // UNIT_HARVESTER + new UnitTypeClass(UnitArty); // UNIT_ARTY + new UnitTypeClass(UnitMRJammer); // UNIT_MRJ + new UnitTypeClass(UnitMGG); // UNIT_MGG + new UnitTypeClass(UnitMCV); // UNIT_MCV + new UnitTypeClass(UnitV2Launcher); // UNIT_V2_LAUNCHER + new UnitTypeClass(UnitConvoyTruck); // UNIT_TRUCK +#ifdef FIXIT_ANTS + new UnitTypeClass(UnitAnt1); // UNIT_ANT1 + new UnitTypeClass(UnitAnt2); // UNIT_ANT2 + new UnitTypeClass(UnitAnt3); // UNIT_ANT3 +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new UnitTypeClass(UnitChrono); // UNIT_CHRONOTANK + new UnitTypeClass(UnitTesla); // UNIT_TESLATANK + new UnitTypeClass(UnitMAD); // UNIT_MAD + new UnitTypeClass(UnitDemoTruck); // UNIT_DEMOTRUCK +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + new UnitTypeClass(UnitPhase); // UNIT_PHASETRANSPORT +#endif +#endif + +} + + +/*********************************************************************************************** + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * * + * This routine converts an ASCII representation of a unit class and * + * converts it into a real unit class number. * + * * + * INPUT: name -- ASCII name representing a unit class. * + * * + * OUTPUT: Returns with the actual unit class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +UnitType UnitTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (UnitType classid = UNIT_FIRST; classid < UNIT_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(UNIT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * UnitTypeClass::Display -- Displays a generic unit shape. * + * * + * This routine displays a generic representation of a unit of this * + * type. Typically, it is used when adding objects to the game map. * + * * + * INPUT: x,y -- Coordinate to render the unit shape. * + * * + * window-- Window to render within. * + * * + * house -- House to render the unit colors. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 11/08/1994 JLB : Handles chunky type vehicles now. * + *=============================================================================================*/ +void UnitTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = Rotation/6; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * * + * This routine is used to prepare the generic object adder dialog * + * box so that it will be able to add a unit object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void UnitTypeClass::Prep_For_Add(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * * + * This routine is used to perform the action necessary only once for the unit type class. * + * It loads unit shapes and brain files. This routine should only be called once. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::One_Time(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + UnitTypeClass const & uclass = As_Reference(index); + CCFileClass file; + + void const * ptr; // Shape pointer and set pointer. + + int largest = 0; +// if (uclass.Level != -1) { +// if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + #ifndef NDEBUG + RawFileClass datafile(fullname); + if (datafile.Is_Available()) { + ((void const *&)uclass.CameoData) = Load_Alloc_Data(datafile); + } else { + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + #endif +// } + + /* + ** Fetch a pointer to the unit's shape data. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + #ifndef NDEBUG + RawFileClass shpfile(fullname); + if (shpfile.Is_Available()) { + ptr = Load_Alloc_Data(shpfile); + } else { + ptr = MFCD::Retrieve(fullname); + } + #else + ptr = MFCD::Retrieve(fullname); + #endif + + ((void const *&)uclass.ImageData) = ptr; + if (ptr != NULL) { + + largest = max(largest, (int)Get_Build_Frame_Width(ptr)); + largest = max(largest, (int)Get_Build_Frame_Height(ptr)); + } + + ((int &)uclass.MaxSize) = max(largest, 8); + } + + /* + ** Load any custom shapes at this time. + */ + if (WakeShapes == NULL) { + WakeShapes = MFCD::Retrieve("WAKE.SHP"); + } + if (TurretShapes == NULL) { + TurretShapes = MFCD::Retrieve("TURR.SHP"); + } + if (SamShapes == NULL) { + SamShapes = MFCD::Retrieve("SSAM.SHP"); + } + if (MGunShapes == NULL) { + MGunShapes = MFCD::Retrieve("MGUN.SHP"); + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * * + * This routine is used by the scenario editor to create and place a unit object of this * + * type onto the map. * + * * + * INPUT: cell -- The cell that the unit is to be placed into. * + * * + * house -- The house that the unit belongs to. * + * * + * OUTPUT: bool; Was the unit created and placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + UnitClass * unit = new UnitClass(Type, house); + if (unit != NULL) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + return(false); +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * * + * This function creates a unit of this type and keeps it in limbo. A pointer to the * + * created unit object is returned. It is presumed that this object will later be * + * unlimboed at the correct time and place. * + * * + * INPUT: house -- Pointer to the house that is to own the unit. * + * * + * OUTPUT: Returns with a pointer to the created unit object. If the unit object * + * could not be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * UnitTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new UnitClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * * + * Use this routine to return a reference to the UnitTypeClass object as indicated by * + * the unit type number specified. * + * * + * INPUT: type -- The unit type number to convert into a UnitTypeClass object reference. * + * * + * OUTPUT: Returns with a reference to the unit type class object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass & UnitTypeClass::As_Reference(UnitType type) +{ + return(*UnitTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * * + * This routine will fill in the width and height for this unit type. This width and * + * height are used to render the selection rectangle and the positioning of the health * + * bargraph. * + * * + * INPUT: width -- Reference to the width of the unit (to be filled in). * + * * + * height -- Reference to the height of the unit (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Dimensions(int &width, int &height) const +{ + width = MaxSize-(MaxSize/4); + width = min(width, 48); + height = MaxSize-(MaxSize/4); + height = min(height, 48); +} + + +/*********************************************************************************************** + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed, but for harvesters, it is the * + * number of credits it holds divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this unit type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int UnitTypeClass::Max_Pips(void) const +{ + if (Type == UNIT_HARVESTER) { + return(7); + } + + if (Type == UNIT_MINELAYER) { + return(MaxAmmo); + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * UnitTypeClass::Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * * + * This routine adjusts the pixel coordinates specified to account for the displacement of * + * the turret on the MLRS and MSAM vehicles. * + * * + * INPUT: dir -- The direction of the body of the vehicle. * + * * + * x,y -- References to the turret center pixel position. These will be modified as * + * necessary. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Turret_Adjust(DirType dir, int &x, int &y) const +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {1,2}, // N + {-1,1}, + {-2,0}, + {-3,0}, + {-3,1}, // NW + {-4,-1}, + {-4,-1}, + {-5,-2}, + {-5,-3}, // W + {-5,-3}, + {-3,-3}, + {-3,-4}, + {-3,-4}, // SW + {-3,-5}, + {-2,-5}, + {-1,-5}, + {0,-5}, // S + {1,-6}, + {2,-5}, + {3,-5}, + {4,-5}, // SE + {6,-4}, + {6,-3}, + {6,-3}, + {6,-3}, // E + {5,-1}, + {5,-1}, + {4,0}, + {3,0}, // NE + {2,0}, + {2,1}, + {1,2} + }; + + int index = 0; + switch (Type) { + case UNIT_JEEP: + y -= 4; + break; + + case UNIT_MGG: + index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Read_INI -- Fetch the unit type data from the INI database. * + * * + * This routine will find the section in the INI database for this unit type object and * + * then fill in the override values specified. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the section for this unit found in the database and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsNoFireWhileMoving = ini.Get_Bool(IniName, "NoMovingFire", IsNoFireWhileMoving); + Speed = ini.Get_Bool(IniName, "Tracked", (Speed == SPEED_TRACK)) ? SPEED_TRACK : SPEED_WHEEL; + + /* + ** If this unit can drive over walls, then mark it as recognizing the crusher zone. + */ + if (MZone < MZONE_CRUSHER && IsCrusher) { + MZone = MZONE_CRUSHER; + } + return(true); + } + return(false); +} diff --git a/CODE/UDPADDR.CPP b/CODE/UDPADDR.CPP new file mode 100644 index 0000000..4a5f1d1 --- /dev/null +++ b/CODE/UDPADDR.CPP @@ -0,0 +1,232 @@ +/* +** 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 . +*/ + + + +#include "function.h" +//#include "_WSProto.h" +#include "WSPUDP.h" + + +bool Get_Broadcast_Addresses (void) +{ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 160 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_ip_address_list_w = 300 *RESFACTOR; + int d_ip_address_list_h = ((20 * 6) + 3) *RESFACTOR; // 6 rows high + int d_ip_address_list_x = d_dialog_cx - d_ip_address_list_w / 2; + int d_ip_address_list_y = d_margin2 + d_dialog_y; + + + int d_ok_w = 40*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_cx + d_dialog_w / 4; + int d_ok_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_dialog_w / 4; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_IPLIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int width; + int height; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String("IP Addresses", SeenBuff.Get_Height(), width, height); + + + GadgetClass *commands; // button list + ColorListClass ip_address_list(BUTTON_IPLIST, d_ip_address_list_x, d_ip_address_list_y, d_ip_address_list_w, d_ip_address_list_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ip_address_list.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + + + /* + ** Add all the ip addresses from the ini file to the list box. + */ + CCINIClass ip_ini; + int res=0; + + if (ip_ini.Load(CCFileClass("IP.INI"), false)) { + int entry=0; + char entry_name[16]; + do { + entry++; + char *temp = new char [128]; + sprintf (entry_name, "%d", entry); + res = ip_ini.Get_String("IP_ADDRESSES", entry_name, "", temp, 128); + if ( res ) { + ip_address_list.Add_Item (temp); +char debug[128]; +sprintf (debug, "RA95 - Adding address %s\n", temp); +WWDebugString (debug); + } + }while (res); + } + ip_address_list.Flag_To_Redraw(); + + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print("IP Addresses", d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + okbtn.Zap(); + cancelbtn.Zap(); + ip_address_list.Zap(); + + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + ip_address_list.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return (false); + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Show_Mouse(); + + for ( int i=0 ; iSet_Broadcast_Address ((char*)temp); + } + + return (true); +} \ No newline at end of file diff --git a/CODE/UNIT.CPP b/CODE/UNIT.CPP new file mode 100644 index 0000000..7afc24b --- /dev/null +++ b/CODE/UNIT.CPP @@ -0,0 +1,5064 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/UNIT.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : UNIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * UnitClass::AI -- AI processing for the unit. * + * UnitClass::APC_Close_Door -- Closes an APC door. * + * UnitClass::APC_Open_Door -- Opens an APC door. * + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * UnitClass::Click_With -- Handles player map clicking while this unit is selected. * + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * UnitClass::Draw_It -- Draws a unit object. * + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * UnitClass::Init -- Clears all units for scenario preparation. * + * UnitClass::Limbo -- Limbo this unit. * + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * UnitClass::Overlap_List -- Determines overlap list for units. * + * UnitClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * UnitClass::Random_Animate -- Handles random idle animation for the unit. * + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * UnitClass::Set_Speed -- Initiate unit movement physics. * + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * UnitClass::UnitClass -- Constructor for units. * + * UnitClass::Unlimbo -- Removes unit from stasis. * + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * UnitClass::Write_INI -- Store the units to the INI database. * + * UnitClass::delete -- Deletion operator for units. * + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * UnitClass::~UnitClass -- Destructor for unit objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * * + * This is a helper routine that modifies the pixel coordinates provided according to the * + * direction specified. The effect is the simulate recoil effects by moving an object 'back'* + * one pixel. Since the pixels moved depend on facing, this routine handles the pixel * + * adjustment quickly. * + * * + * INPUT: dir -- The direction to base the recoil on. * + * * + * x,y -- References to the pixel coordinates that will be adjusted. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Recoil_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {0, 1}, // N + {0, 1}, + {0, 1}, + {-1, 1}, + {-1, 1}, // NE + {-1, 1}, + {-1, 0}, + {-1, 0}, + {-1, 0}, // E + {-1, 0}, + {-1, -1}, + {-1, -1}, + {-1, -1}, // SE + {-1, -1}, + {-1, -1}, + {0, -1}, + {0, -1}, // S + {0, -1}, + {0, -1}, + {1, -1}, + {1, -1}, // SW + {1, -1}, + {1, 0}, + {1, 0}, + {1, 0}, // W + {1, 0}, + {1, 1}, + {1, 1}, + {1, 1}, // NW + {1, 1}, + {0, 1}, + {0, 1} + }; + + int index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * * + * This routine will allocate a unit from the available unit pool and * + * fixup all the access lists to match. It will allocate a unit slot * + * from within the range allowed for the specified unit type. If no * + * slot was found, then it will fail. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + *=============================================================================================*/ +void * UnitClass::operator new(size_t) +{ + void * ptr = Units.Alloc(); + if (ptr != NULL) { + ((UnitClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * UnitClass::delete -- Deletion operator for units. * + * * + * This removes the unit from the local allocation system. Since this * + * is a fixed block of memory, not much has to be done to delete the * + * unit. Merely marking it as inactive is enough. * + * * + * INPUT: ptr -- Pointer to the unit to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((UnitClass *)ptr)->IsActive = false; + } + Units.Free((UnitClass *)ptr); +} + + +/*********************************************************************************************** + * UnitClass::~UnitClass -- Destructor for unit objects. * + * * + * This destructor will lower the unit count for the owning house as well as inform any * + * other units in communication, that this unit is about to leave reality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::~UnitClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * UnitClass::UnitClass -- Constructor for units. * + * * + * This constructor for units will initialize the unit into the game * + * system. It will be placed in all necessary tracking lists. The initial condition will * + * be in a state of limbo. * + * * + * INPUT: classid -- The type of unit to create. * + * * + * house -- The house owner of this unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::UnitClass(UnitType classid, HousesType house) : + DriveClass(RTTI_UNIT, Units.ID(this), house), + Class(UnitTypes.Ptr((int)classid)), + Flagged(HOUSE_NONE), + IsDumping(false), + Gems(0), + Gold(0), + Tiberium(0), + IsToScatter(false), + ShroudBits(0xFFFFFFFFUL), + ShroudCenter(0), + Reload(0), + SecondaryFacing(PrimaryFacing) +{ + Reload = 0; + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + IsCloakable = Class->IsCloakable; + if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3)); + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * * + * This displays the current status of the unit class to the mono monitor. By this display * + * bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Debug_Dump(MonoClass * mono) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_VEHICLE)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Set_Cursor(1, 11);mono->Printf("%03", Gems); + mono->Set_Cursor(7, 11);mono->Printf("%03", Gold); + + mono->Fill_Attrib(66, 13, 12, 1, IsDumping ? MonoClass::INVERSE : MonoClass::NORMAL); + + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * * + * This routine is used by the rendering system in order to sort the * + * game objects in a back to front order. This is now the correct * + * overlap effect is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value that can be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Sort_Y(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * UnitClass::AI -- AI processing for the unit. * + * * + * This routine will perform the AI processing necessary for the unit. These are non- * + * graphic related operations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::AI(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (Height == 0 && !IsDumping && !IsDriving && Is_Door_Closed() /*&& Mission != MISSION_UNLOAD*/) { +// if (MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + DriveClass::AI(); + if (!IsActive || Height > 0) { + return; + } + + /* + ** Hack check to ensure that a harvester won't harvest if it is not harvesting. + */ + if (Mission != MISSION_HARVEST) { + IsHarvesting = false; + } + + /* + ** Handle combat logic for this unit. It will determine if it has a target and + ** if so, if conditions are favorable for firing. When conditions permit, the + ** unit will fire upon its target. + */ + Firing_AI(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!IsActive) { + return; + } +#endif + /* + ** Turret rotation processing. Handles rotating radar dish + ** as well as conventional turrets if present. If no turret present, but + ** it decides that the body should face its target, then body rotation + ** would occur by this process as well. + */ + Rotation_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Units will reload every so often if they are under the burden of + ** being required to reload between shots. + */ + Reload_AI(); + + /* + ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS + ** mission that they can follow. Passenger loading is merely a part of their normal operation. + */ + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) { + APC_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDumping && !IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** A cloaked object that is carrying the flag will always shimmer. + */ + if (Cloak == CLOAKED && Flagged != HOUSE_NONE) { + Do_Shimmer(); + } + + /* + ** Mobile gap generators regenerate their gap every so often (just in case). + */ + if (Class->IsGapper && !IsDriving && (Frame % TICKS_PER_SECOND) == 0) { + Shroud_Regen(); + } +} + + +/*********************************************************************************************** + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * * + * This routine will handle the rotation logic for the unit's turret (if it has one) as * + * well as its normal body shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { + PrimaryFacing.Set_Desired(dir); + } + } + } + + if (Class->IsRadarEquipped) { + Mark(MARK_CHANGE_REDRAW); + SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8)); + Mark(MARK_CHANGE_REDRAW); + } else { + + IsRotating = false; + if (Class->IsTurretEquipped) { + if (IsTurretLockedDown) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + if (!Class->IsRadarEquipped) { + IsRotating = SecondaryFacing.Is_Rotating(); + } + } else { + if (!IsTurretLockedDown && !Target_Legal(TarCom)) { + if (!Target_Legal(NavCom)) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * * + * When a unit leaves the map it will be eliminated. This routine checks for this case * + * and eliminates the unit accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the unit eliminated by this routine? * + * * + * WARNINGS: Be sure to check for the return value and if 'true' abort any further processing* + * of the unit since it is dead. Only call this routine once per unit per * + * game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Edge_Of_World_AI(void) +{ + if (Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * * + * Some units require special reload logic. The V2 rocket launcher in particular. Perform * + * this reload logic with this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Reload_AI(void) +{ + if (*this == UNIT_V2_LAUNCHER && Ammo < Class->MaxAmmo) { + if (IsDriving) { + Reload = Reload + 1; + } else { + if (Reload == 0) { + Ammo++; + if (Ammo < Class->MaxAmmo) { + Reload = TICKS_PER_SECOND*30; + } + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * * + * This routine wil check for and perform any firing logic required of this unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This should be called only once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Firing_AI(void) +{ + if (Target_Legal(TarCom) && Class->PrimaryWeapon != NULL) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + switch (ok) { + case FIRE_OK: + if (!((UnitClass *)this)->Class->IsFireAnim) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Class->IsLockTurret || Class->Type == UNIT_DEMOTRUCK) { +#else + if (Class->IsLockTurret) { +#endif + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a unit receives a radio * + * message. Typical use of this is when a unit unloads from a hover * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + switch (message) { + /* + ** Checks to see if this object is in need of service depot processing. + */ + case RADIO_NEED_REPAIR: + if (!IsDriving && !Target_Legal(NavCom) && (Health_Ratio() >= 1 && (*this != UNIT_MINELAYER || Ammo >= Class->MaxAmmo))) return(RADIO_NEGATIVE); + break; +// return(RADIO_ROGER); + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** The refinery has told this harvester that it should begin the backup procedure + ** so that proper unloading may take place. + */ + case RADIO_BACKUP_NOW: + DriveClass::Receive_Message(from, message, param); + if (!IsRotating && PrimaryFacing != DIR_W) { + Do_Turn(DIR_W); + } else { + if (!IsDriving) { + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (Transmit_Message(RADIO_IM_IN, whom) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOADED, whom); + } + } + } + } + } + return(RADIO_ROGER); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + APC_Close_Door(); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + Do_Turn(dir); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is or needs + ** to rotate. + */ +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_APC || *this == UNIT_PHASE) { +#else + if (*this == UNIT_APC) { +#endif + if (IsRotating) { + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + } else { + if (!Is_Door_Open()) { + APC_Open_Door(); + } + } + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if ( (*this != UNIT_APC && *this != UNIT_PHASE) || Is_Door_Open()) { +#else + if (*this != UNIT_APC || Is_Door_Open()) { +#endif + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * UnitClass::Unlimbo -- Removes unit from stasis. * + * * + * This routine will place a unit into the game and out of its limbo * + * state. This occurs whenever a unit is unloaded from a transport. * + * * + * INPUT: coord -- The coordinate to make the unit appear. * + * * + * dir -- The initial facing to impart upon the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? If the desired * + * coordinate is illegal, then this might very well return * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** All units must start out facing one of the 8 major directions. + */ + dir = Facing_Dir(Dir_Facing(dir)); + if (DriveClass::Unlimbo(coord, dir)) { + + SecondaryFacing = dir; + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->UScan |= (1L << Class->Type); + House->ActiveUScan |= (1L << Class->Type); + + /* + ** If it starts off the edge of the map, then it already starts cloaked. + */ + if (IsCloakable && !IsLocked) Cloak = CLOAKED; + + /* + ** Units default to no special animation. + */ + Set_Rate(0); + Set_Stage(0); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * * + * This routine will inflict the specified number of damage points on * + * the given unit. If the unit is destroyed, then this routine will * + * remove the unit cleanly from the game. The return value indicates * + * whether the unit was destroyed. This will allow appropriate death * + * animation or whatever. * + * * + * INPUT: damage-- The number of damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead--The type of damage to inflict. * + * * + * source -- Who is responsible for this damage? * + * * + * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to * + * RESULT_DESTROYED. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1991 JLB : Created. * + * 07/12/1991 JLB : Script initiated by unit destruction. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Warhead modifier. * + * 06/03/1994 JLB : Added the source of the damage target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 06/30/1995 JLB : Lasers do maximum damage against gunboat. * + * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. * + *=============================================================================================*/ +ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Remember if this object was selected. If it was and it gets destroyed and it has + ** passengers that pop out, then the passengers will inherit the select state. + */ + bool select = (IsSelected && House->IsPlayerControl); + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = DriveClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + Shroud_Regen(); // remove the shroud if it's a gap generator + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + /* + ** SSM launchers will really explode big if they are carrying + ** missiles at the time of the explosion. + */ + if (*this == UNIT_V2_LAUNCHER && Ammo) { + anim = ANIM_NAPALM3; + } + + new AnimClass(anim, Coord); + + /* + ** Harvesters explode with a force equal to the amount of + ** Tiberium they are carrying. + */ + if (Tiberium > 0 && Rule.IsExplosiveHarvester) { + Wide_Area_Damage(Coord, CELL_LEPTON_W + CELL_LEPTON_W/2, Credit_Load()+Class->MaxStrength, this, WARHEAD_HE); + } + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_The_Screen(3); + } + } + + /* + ** Possibly have the crew member run away. + */ + CELL cell = Coord_Cell(Center_Coord()); + Mark(MARK_UP); + + if (Class->IsCrew && Class->Max_Passengers() == 0) { + if (Percent_Chance(50)) { + InfantryClass * i = 0; + + if (Class->PrimaryWeapon == NULL) { + i = new InfantryClass(INFANTRY_C1, House->Class->House); + if (i != NULL) i->IsTechnician = true; + } else { + i = new InfantryClass(INFANTRY_E1, House->Class->House); + } + if (i != NULL) { + if (i->Unlimbo(Coord, DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2); + i->Scatter(0, true); + if (!House->IsHuman) { + i->Assign_Mission(MISSION_HUNT); + } else { + i->Assign_Mission(MISSION_GUARD); + } + if (select) i->Select(); + } else { + delete i; + } + } + } + } else { + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + if (object == NULL) break; // How can this happen? + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) { + object->Scatter(0, true); + if (select) object->Select(); + } else { + object->Record_The_Kill(source); + delete object; + } + } + } + + /* + ** If this is a truck, there is a possibility that a crate will drop out + ** if the scenario so indicates and there is room. + */ + if (Scen.IsTruckCrate && *this == UNIT_TRUCK) { + cell = Nearby_Location(); + if (cell != 0) { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking. + */ + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached) { +#ifdef FIXIT_ANTS + if (*this != UNIT_ANT1 && *this != UNIT_ANT2 && *this != UNIT_ANT3) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim) anim->Attach_To(this); +#ifdef FIXIT_ANTS + } +#endif + } + + /* + ** Try to crush anyone that fires on this unit if possible. The harvester + ** typically is the only one that will qualify here. + */ + if (!Team.Is_Valid() && source != NULL && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Rule.IsAutoCrush)) { + + /* + ** Try to crush the attacker if it can be crushed by this unit and this unit is + ** not equipped with a flame type weapon. If this unit has a weapon and the target + ** is not very close, then fire on it instead. In easy mode, they never run over the + ** player. In hard mode, they always do. In normal mode, they only overrun past + ** mission #8. + */ + if (Should_Crush_It(source)) { + Assign_Destination(source->As_Target()); + Assign_Mission(MISSION_MOVE); + } else { + + /* + ** Try to return to base if possible. + */ + if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() <= Rule.ConditionYellow) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + } + } + } + } + + /* + ** Computer controlled harvester will radio for help if they are attacked. + */ + if (*this == UNIT_HARVESTER && !House->IsHuman && source) { + Base_Is_Attacked(source); + } + } + return(res); +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + break; + } + } + + /* + ** Short circuit out if trying to tell a unit to "nomove" to itself. This bypass of the + ** normal active click with logic prevents any disturbance to the vehicle's state. Without + ** this bypass, a unit on a repair bay would stop repairing because it would break radio + ** contact. + */ + if (object == this && action == ACTION_NOMOVE) { + return; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, object); + } +#else + DriveClass::Active_Click_With(action, object); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, cell); + } +#else + DriveClass::Active_Click_With(action, cell); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * * + * This routine is called when the unit completes one mission but does not have a clear * + * follow up mission to perform. In such a case, the unit should enter a default idle * + * state. This idle state varies depending on what the current internal computer * + * settings of the unit is as well as what kind of unit it is. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/03/1994 JLB : Fixed to handle non-combat vehicles. * + * 06/18/1995 JLB : Allows a harvester to stop harvesting. * + *=============================================================================================*/ +void UnitClass::Enter_Idle_Mode(bool initial) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (IsToScatter) { + IsToScatter = false; + Scatter(0, true); + } + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (!Is_Weapon_Equipped()) { + if (Class->IsToHarvest) { + if (!In_Radio_Contact() && Mission != MISSION_HARVEST && MissionQueue != MISSION_HARVEST) { + if (initial || !House->IsHuman || Map[Coord].Land_Type() == LAND_TIBERIUM) { + order = MISSION_HARVEST; + } else { + order = MISSION_GUARD; + } + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } else { + return; + } + } else { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team.Is_Valid()) { + order = MISSION_UNLOAD; + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*this == UNIT_MAD && Mission == MISSION_UNLOAD) { + order = MISSION_UNLOAD; + } else { +#endif + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IQ < Rule.IQGuardArea || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * * + * This routine is used by the MCV to find a clear spot to deploy. If a clear spot * + * is found, then the MCV will assign that location to its navigation computer. This only * + * occurs if the MCV isn't already heading toward a spot. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the located at a spot where it can deploy? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Clear_Spot(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Mark(MARK_UP); + if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Adjacent_Cell(Coord_Cell(Center_Coord()), FACING_NW))) { + Mark(MARK_DOWN); + return(true); + } + + if (!Target_Legal(NavCom)) { + /* + ** This scan table is skewed to north scanning only. This should + ** probably be converted to a more flexible method. + */ + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, +//BG: Added south scanning + MAP_CELL_W*1, + MAP_CELL_W*2, + (MAP_CELL_W*2)+1, + (MAP_CELL_W*2)-1, + MAP_CELL_W*3, + (MAP_CELL_W*3)+1, + (MAP_CELL_W*3)-1, + (MAP_CELL_W*3)+2, + (MAP_CELL_W*3)-2, + MAP_CELL_W*4, + (MAP_CELL_W*4)+1, + (MAP_CELL_W*4)-1, + (MAP_CELL_W*4)+2, + (MAP_CELL_W*4)-2, + +//BG: Added some token east/west scanning + -1,-2,-3,-4, + + 1, 2, 3, 4, + 0 + }; + int * ptr; + + ptr = &_offsets[0]; + while (*ptr) { + CELL cell = Coord_Cell(Coord)+*ptr++; + CELL check_cell = Adjacent_Cell(cell, FACING_NW); + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(check_cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + } + Mark(MARK_DOWN); + + /* + ** If we couldn't find a destination to go to, let's try random movement + ** to see if that brings us to a better spot. + */ + if(!Target_Legal(NavCom) && !House->IsHuman) { + Scatter(0); + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * * + * Certain units have the ability to deploy into a building. When this routine is called * + * for one of those units, it will attempt to deploy at its current location. If the unit * + * is in motion to a destination or it isn't one of the special units that can deploy or * + * it isn't allowed to deploy at this location for some reason it won't deploy. In all * + * other cases, it will begin to deploy and once it begins only a player abort action will * + * stop it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was deployment begun? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Try_To_Deploy(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsRotating) { + if (*this == UNIT_MCV) { + + /* + ** Determine if it is legal to deploy at this location. If not, tell the + ** player. + */ + Mark(MARK_UP); + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + if (PlayerPtr == House) { + Speak(VOX_DEPLOY); + } + if (!House->IsHuman) { + BuildingTypeClass::As_Reference(STRUCT_CONST).Flush_For_Placement(cell, House); + } + Mark(MARK_DOWN); + IsDeploying = false; + return(false); + } + Mark(MARK_DOWN); + + /* + ** If the unit is not facing the correct direction, then start it rotating + ** toward the right facing, but still flag it as if it had deployed. This is + ** because it will deploy as soon as it reaches the correct facing. + */ + if (PrimaryFacing.Current() != DIR_SW) { + Do_Turn(DIR_SW); +// PrimaryFacing.Set_Desired(DIR_SW); + IsDeploying = true; + return(true); + } + + /* + ** Since the unit is already facing the correct direction, actually do the + ** deploy logic. If for some reason this cannot occur, then don't delete the + ** unit, just mark it as not deploying. + */ + Mark(MARK_UP); + BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House); + if (building != NULL) { + if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) { + + /* + ** Play the buildup sound for the player if this is the players + ** MCV. + */ + if (building->House == PlayerPtr) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN, Center_Coord()); + } else { + building->IsToRebuild = true; + building->IsToRepair = true; + } + + /* + ** Always reveal the construction yard to the player that owned the + ** mobile construction vehicle. + */ + building->Revealed(House); + + /* + ** When the MCV deploys, always consider production to have started + ** for the owning house. This ensures that in multiplay, computer + ** opponents will begin construction as soon as they start their + ** base. + */ + House->IsStarted = true; + + /* + ** Force the newly placed construction yard to be in the same strength + ** ratio as the MCV that deployed into it. + */ + building->Strength = Health_Ratio() * building->Class->MaxStrength; + + /* + ** Force the MCV to drop any flag it was carrying. This will also set + ** the owner house's flag home cell (since the house's FlagHome is + ** presumably 0 at this point). + */ + Stun(); + + /* + ** If this MCV was teleported here, clear the gray flag so + ** the screen will go back to color. + */ + if (IsMoebius && !Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + delete this; + return(true); + } else { + + /* + ** Could not deploy the construction yard at this location! Just revert + ** back to normal "just sitting there" mode and await further instructions. + */ + delete building; + } + } + Mark(MARK_DOWN); + IsDeploying = false; + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * * + * This routine will perform the operations necessary that occur when a unit is at the * + * center of a cell. These operations could entail deploying into a construction yard, * + * radioing a transport unit, and looking around for the enemy. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + * 06/17/1995 JLB : Handles case when building says "NO!" * + * 06/30/1995 JLB : Gunboats head back and forth now. * + *=============================================================================================*/ +void UnitClass::Per_Cell_Process(PCPType why) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + HousesType house; + + if (why == PCP_END || why == PCP_ROTATION) { + /* + ** Check to see if this is merely the end of a rotation for the MCV as it is + ** preparing to deploy. In this case, it should begin its deploy process. + */ + if (IsDeploying) { + Try_To_Deploy(); + if (!IsActive) return; // Unit no longer exists -- bail. + } + } + + BStart(BENCH_PCP); + if (why == PCP_END) { + /* + ** If this is a unit that is driving onto a building then the unit must enter + ** the building as the final step. + */ + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (whom == Map[CELL(cell-MAP_CELL_W)].Cell_Building()) { + switch (Transmit_Message(RADIO_IM_IN, whom)) { + case RADIO_ROGER: + break; + + case RADIO_ATTACH: + break; + + default: + Scatter(0, true); + break; + } + } + } + } + + /* + ** Unit entering a transport vehicle will break radio contact + ** and attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** When breaking away from a transport object or building, possibly + ** scatter or otherwise begin normal unit operations. + */ + if (IsTethered && (Mission != MISSION_ENTER || + (As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom)) + ) && + Mission != MISSION_UNLOAD) { + + /* + ** Special hack check to make sure that even though it has moved one + ** cell, if it is still on the building (e.g., service depot), have + ** it scatter again. + */ + if (Map[Coord].Cell_Building() != NULL && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } else { + if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) { + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } else { + + /* + ** Special case hack to allow automatic transition to loading + ** onto a transport (or other situation) if the destination + ** so indicates. + */ + TechnoClass * techno = As_Techno(NavCom); + if (techno != NULL) { + Transmit_Message(RADIO_DOCKING, techno); + } + } + } else { + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } + } + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that + ** it is probably carrying an incoming reinforcement and it should not be eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + if (IsPlanningToLook) { + IsPlanningToLook = false; + Look(false); + } else { + Look(true); + } + + /* + ** If this is a mobile gap generator, restore the shroud where appropriate + ** and re-shroud around us. + */ + if (Class->IsGapper && !House->IsPlayerControl) { + Shroud_Regen(); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDumping) { + Commence(); + } + + /* + ** Certain units require some setup time after they come to a halt. + */ + if (!Target_Legal(NavCom) && Path[0] == FACING_NONE) { + if (Class->IsNoFireWhileMoving) { + Arm = Rearm_Delay(true)/4; + } + } + + /* + ** If there is a house flag here, then this unit just might pick it up. + */ + if (Flagged == HOUSE_NONE) { + + if (Map[cell].IsFlagged && Map[cell].Owner != House->Class->House) { + HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this); + } + } + + /* + ** If this is the unit's own flag-home-cell and the unit is carrying + ** a flag, destroy the house of the flag the unit is carrying. + */ + if (Flagged != HOUSE_NONE) { + + /* + ** If this vehicle is carrying your flag, then it will reveal the + ** map for you as well as itself. This gives you and opportunity to + ** attack the unit. + */ + if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) { + Map.Sight_From(Coord_Cell(Coord), Class->SightRange, House, true); + } + + /* + ** If the flag reaches the home cell for the player, then the flag's + ** owner will be destroyed. + */ + if (cell == HouseClass::As_Pointer(Owner())->FlagHome) { + house = Flagged; // Flag_Remove will clear 'Flagged', so save it + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(), true); + HouseClass::As_Pointer(house)->Flag_To_Die(); + } + } + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = Map[cell].Cell_Building(); + if (bldng != NULL && (*bldng == STRUCT_AVMINE || *bldng == STRUCT_APMINE) && !bldng->House->Is_Ally(this)) { + + /* + ** Special case: if it's a land mine deployer, and it ran over the + ** type of mine it deploys (only possible if it just dropped it + ** down) then ignore the mine. + */ + if (*this != UNIT_MINELAYER || bldng->House != House) { + + COORDINATE blcoord = bldng->Center_Coord(); + + new AnimClass(ANIM_MINE_EXP1, blcoord); +// new AnimClass(Combat_Anim(Rule.AVMineDamage, WARHEAD_HE, Map[cell].Land_Type()), blcoord); + + /* + ** Vehicles blow up both mines, but they only take significant damage from AV mines. + */ + if (*bldng == STRUCT_AVMINE) { + int damage = Rule.AVMineDamage; + Take_Damage(damage, 0, WARHEAD_HE); + } else { + int damage = 10; + Take_Damage(damage, 0, WARHEAD_HE); + } + delete bldng; + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + } + + /* + ** If after all is said and done, the unit finishes its move on an impassable cell, then + ** it must presume that it is in the case of a unit driving onto a bridge that blows up + ** before the unit completes it's move. In such a case the unit should have been destroyed + ** anyway, so blow it up now. + */ + LandType land = Map[Coord].Land_Type(); + if (!IsDriving && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + new AnimClass(Combat_Anim(Strength, WARHEAD_AP, land), Coord); + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; + } + } + + /* + ** Destroy any crushable wall that is driven over by a tracked vehicle. + */ + CellClass * cellptr = &Map[cell]; + if (Class->IsCrusher && cellptr->Overlay != OVERLAY_NONE) { +// if (Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrushable) { + if (optr->Type == OVERLAY_SANDBAG_WALL) { + Sound_Effect(VOC_SANDBAG, Center_Coord()); + } else { + Sound_Effect(VOC_WALLKILL2, Center_Coord()); + } + cellptr->Reduce_Wall(-1); + } + } + + /* + ** Check to see if crushing of any unfortunate infantry is warranted. + */ + Overrun_Square(Coord_Cell(Coord), false); + + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + DriveClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * * + * This routine will calculate the shape number for this unit. The shape number is used * + * for the body of the unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to be used for this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Shape_Number(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + +#ifdef FIXIT_ANTS + /* + ** This handles the ant case. + */ + if (Class->Rotation == 8) { + + /* + ** The starting frame is based on the facing of the unit. + */ + shapenum = ((UnitClass::BodyShape[facing]+2)/4) & 0x07; + + /* + ** If the unit is driving, then it has an animation adjustment to the frame number. + */ + if (IsDriving) { + shapenum = 8 + (shapenum * 8) + ((::Frame+ID)/2) % 8; + } else { + + /* + ** If in combat, then do combat anims. + */ + if (Arm > 0) { + shapenum = 8 + 64 + (shapenum * 4) + ((::Frame+ID)/2) % 4; + } + } + } else { +#endif + + /* + ** Fetch the harvesting animation stage as appropriate. + */ + if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) { +// static char _hstage[] = {0, 1, 2, 3, 4, 5, 6, 7, 0}; + unsigned stage = Fetch_Stage(); + if (stage >= ARRAY_SIZE(Class->Harvester_Load_List)) stage = ARRAY_SIZE(Class->Harvester_Load_List)-1; + shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*Class->Harvester_Load_Count)+Class->Harvester_Load_List[stage]; + } else { + /* + ** If the harvester's dumping a load of ore, show that animation + */ + if (IsDumping) { + unsigned stage = Fetch_Stage(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD) { + if (stage >= 8) { + stage = 7; + } + shapenum = 32 + stage + (UnitClass::BodyShape[facing]/4)*8; + } else { + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; + } +#else + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; +#endif + } else { + shapenum = UnitClass::BodyShape[facing]; + + if (Class->IsAnimating) { + shapenum = Fetch_Stage(); + } + + /* + ** Door opening and closing animation must be handled carefully. There are only + ** certain directions where this door animation will work. + */ + if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) { + if (PrimaryFacing == DIR_NE) { + shapenum = 32; + } else { + if (PrimaryFacing == DIR_NW) { + shapenum = 35; + } + } + shapenum += Door_Stage(); + } + } + } + +#ifdef FIXIT_ANTS + } +#endif + + /* + ** The body of the V2 launcher indicates whether it is loaded with a missile + ** or not. + */ + if (*this == UNIT_V2_LAUNCHER) { + if (Ammo == 0) shapenum += 32; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * UnitClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Removed infantry support. * + * 01/07/1995 JLB : Harvester animation support. * + * 07/08/1995 JLB : Uses general purpose draw routine. * + *=============================================================================================*/ +void UnitClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + void const * shapefile; // Working shape file pointer. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + + /* + ** Verify the legality of the unit class. + */ + shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + if (Visual_Character() != VISUAL_HIDDEN) { + shapenum = Shape_Number(); + + /* + ** The artillery unit should have its entire body recoil when it fires. + */ + if (*this == UNIT_ARTY && IsInRecoilState) { + Recoil_Adjust(PrimaryFacing.Current(), x, y); + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); + + /* + ** If there is a rotating radar dish, draw it now. + */ + if (Class->IsRadarEquipped) { + if (*this == UNIT_MGG) { + int x2 = x, y2 = y; + shapenum = 32 + (Frame & 7); + Class->Turret_Adjust(PrimaryFacing, x2, y2); + Techno_Draw_Object(shapefile, shapenum, x2, y2, window); + } else { +//#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 +// if (*this == UNIT_PHASE) { +// shapenum = 38 + (Frame & 7); +// } else { +// shapenum = 32 + (Frame % 32); +// } +//#else + shapenum = 32 + (Frame % 32); +//#endif +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_TESLATANK) { + Techno_Draw_Object(shapefile, shapenum, x, y, window); + } else { + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); + } +#else + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); +#endif + } + } + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (/*!Class->IsChunkyShape &&*/ Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + shapenum = TechnoClass::BodyShape[tfacing]+32; +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_PHASE) { + shapenum += 6; + } +#endif + /* + ** A recoiling turret moves "backward" one pixel. + */ + if (IsInRecoilState) { + Recoil_Adjust(SecondaryFacing, xx, yy); + } + + Class->Turret_Adjust(PrimaryFacing, xx, yy); + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + + /* + ** If this unit is carrying the flag, then draw that on top of everything else. + */ + if (Flagged != HOUSE_NONE) { + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow); + } + + DriveClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * * + * This routine is used to move a harvester to a place where it can load up with * + * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets * + * the navigation computer toward the nearest Tiberium and lets the unit head there * + * automatically. * + * * + * INPUT: center -- Reference to the center of the radius scan. * + * * + * x,y -- Relative offset from the center cell to perform the check upon. * + * * + * OUTPUT: bool; Is it located directly over a Tiberium patch? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Tiberium_Check(CELL & center, int x, int y) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** If the specified offset from the origin will cause it + ** to spill past the map edge, then abort this cell check. + */ + if (Cell_X(center)+x < Map.MapCellX) return(false); + if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false); + if (Cell_Y(center)+y < Map.MapCellY) return(false); + if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false); + + center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y); + + if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsMapped))) { + if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(false); + if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Tiberium -- Searches for and heads toward tiberium. * + * * + * This routine will cause the unit to search for and head toward nearby Tiberium. When * + * the Tiberium is reached, then this routine should not be called again until such time * + * as additional harvesting is required. When this routine returns false, then it should * + * be called again until such time as it returns true. * + * * + * INPUT: rad = size of ring to search * + * * + * OUTPUT: Has the unit reached Tiberium and harvesting should begin? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Tiberium(int rad) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom)) { + CELL center = Coord_Cell(Center_Coord()); + if (Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } else { + + /* + ** Perform a ring search outward from the center. + */ + for (int radius = 1; radius < rad; radius++) { + for (int x = -radius; x <= radius; x++) { + CELL cell = center; + if (Tiberium_Check(cell, x, -radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, x, +radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, -radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, +radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * * + * This routine is used to by the harvester to harvest Tiberium at the current location. * + * When harvesting is complete, this routine will return true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is harvesting complete? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Harvesting(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + CellClass * ptr = &Map[cell]; + + /* + ** Keep waiting if still heading toward a spot to harvest. + */ + if (Target_Legal(NavCom)) return(true); + + if (Tiberium_Load() < 1 && ptr->Land_Type() == LAND_TIBERIUM) { + + /* + ** Lift some Tiberium from the ground. Try to lift a complete + ** "level" of Tiberium. A level happens to be 6 steps. If there + ** is a partial level, then lift that instead. Never lift more + ** than the harvester can carry. + */ +// int reducer = (ptr->OverlayData % 6) + 1; + int reducer = 1; + OverlayType overlay = ptr->Overlay; + reducer = ptr->Reduce_Tiberium(min(reducer, Rule.BailCount-Tiberium)); + Tiberium += reducer; + switch (overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + Gold += reducer; + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + Gems += reducer; + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + break; + + default: + break; + } + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + + } else { + + /* + ** If the harvester is stopped on a non Tiberium field and the harvester + ** isn't loaded with Tiberium, then no further action can be performed + ** by this logic routine. Bail with a failure and thus cause a branch to + ** a better suited logic processor. + */ + Set_Stage(0); + Set_Rate(0); + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo and * + * then exit the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Unload(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case UNIT_HARVESTER: + if (PrimaryFacing != DIR_W) { + if (!IsRotating) { + Do_Turn(DIR_W); + } + return(5); + } + + if (!IsDumping) { + IsDumping = true; + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + break; + } + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Dump_List)-1) break; + + IsDumping = false; + if (Tiberium) { + Tiberium = 0; + int credits = Credit_Load(); + House->Harvested(credits); + Tiberium = Gold = Gems = 0; + } + Transmit_Message(RADIO_OVER_OUT); + + Assign_Mission(MISSION_HARVEST); + break; + + case UNIT_TRUCK: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + Status = UNLOADING; + return(1); + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + Assign_Mission(MISSION_GUARD); + break; + } + break; + + case UNIT_APC: +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + case UNIT_PHASE: +#endif + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + + case UNIT_MCV: + switch (Status) { + case 0: + Path[0] = FACING_NONE; + Status = 1; + break; + + case 1: + if (!IsDriving) { + Try_To_Deploy(); + if (IsActive) { + if (IsDeploying) { + Status = 2; + } else { + if (!House->IsHuman && Session.Type != GAME_NORMAL) { + Assign_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + } + } + break; + + case 2: + if (!IsDeploying) { + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); + + case UNIT_MINELAYER: + switch (Status) { + case INITIAL_CHECK: + dir = DIR_NE; + if (Ammo > 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (Ammo > 0) { + if (!Map[Center_Coord()].Cell_Building()) { + Mark(MARK_UP); + BuildingClass * building = new BuildingClass((House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE || House->ActLike == HOUSE_BAD) ? STRUCT_APMINE : STRUCT_AVMINE, House->Class->House); + if (building != NULL) { + ScenarioInit = true; + if (building->Unlimbo(Coord)) { + Sound_Effect(VOC_MINELAY1, Coord); + ScenarioInit = false; + building->Revealed(House); + Ammo--; + } + ScenarioInit = false; + } + Status = CLOSING_DOOR; + Mark(MARK_DOWN); + } else { + Status = CLOSING_DOOR; + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case UNIT_MAD: + if (!Gems && !IsDumping) { + Gems = 1; + Gold = 0; + Arm = QuakeDelay * House->ROFBias; +#ifdef ENGLISH + Speak(VOX_MADTANK_DEPLOYED); // this voice only exists in English +#else + Sound_Effect(VOC_BUZZY1,Center_Coord()); +#endif + Set_Stage(0); + Set_Rate(Rule.OreDumpRate*2); + IsDumping = true; + +#if 1 + InfantryClass *crew = new InfantryClass(INFANTRY_C1, House->Class->House); + if (crew != NULL) crew->IsTechnician = true; + + if (crew != NULL) { + DirType toface = DIR_S + PrimaryFacing; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + if (crew->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + crew->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + crew->Assign_Mission(MISSION_MOVE); + crew->Assign_Destination(::As_Target(newcell)); + break; + } + } + } +#endif + } + + if ( (Arm && !Gold) || IronCurtainCountDown) { + Set_Stage(Fetch_Stage() & 1); + return(1); + } + + if (!Gold) { + Sound_Effect(VOC_MAD_CHARGE, Center_Coord()); + Set_Stage(0); + Gold = 1; + return(1); + } + + if (Fetch_Stage() < 7) { + return(1); + } + + IsDumping = false; + + Sound_Effect(VOC_MAD_EXPLODE, Center_Coord()); + + Strength = 1; // assure destruction + PendingTimeQuake = true; // trigger a time quake + TimeQuakeCenter = ::As_Target(Center_Coord()); + break; + + case UNIT_CHRONOTANK: + if (IsOwnedByPlayer) { + Map.IsTargettingMode = SPC_CHRONO2; + Unselect_All(); + } + House->UnitToTeleport = As_Target(); + + Assign_Mission(MISSION_GUARD); + break; +#endif + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * * + * This is the AI process used by harvesters when they are doing their harvesting action. * + * This entails searching for nearby Tiberium, heading there, harvesting, and then * + * returning to a refinery for unloading. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 06/21/1995 JLB : Force guard mode if no Tiberium found. * + * 09/28/1995 JLB : Aborts harvesting if there are no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Harvest(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + LOOKING, + HARVESTING, + FINDHOME, + HEADINGHOME, + GOINGTOIDLE, + }; + + /* + ** A non-harvesting type unit will just sit still if it is given the harvest mission. This + ** allows combat units to act "brain dead". + */ + if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30); + + /* + ** If there are no more refineries, then drop into guard mode. + */ + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + switch (Status) { + + /* + ** Go and find a Tiberium field to harvest. + */ + case LOOKING: + /* + ** When full of tiberium, just skip to finding a free refinery + ** to unload at. + */ + if (Tiberium_Load() == 1) { + Status = FINDHOME; + return(1); + } + + /* + ** Look for ore where we last found some - mine the same patch + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + ArchiveTarget = 0; + } + IsHarvesting = false; + if (Goto_Tiberium(Rule.TiberiumLongScan / CELL_LEPTON_W)) { + IsHarvesting = true; + Set_Rate(2); + Set_Stage(0); + Status = HARVESTING; + return(1); + } else { + + /* + ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then + ** force it to go into guard mode. This will prevent the harvester from repeatedly + ** searching for Tiberium. + */ + if (!Target_Legal(NavCom)) { + + /* + ** If the archive target is legal, then head there since it is presumed + ** that the archive target points to the last place it harvested at. This might + ** solve the case where the harvester gets stuck and can't find Tiberium just because + ** it is greater than 32 squares away. + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + } else { + Status = GOINGTOIDLE; + IsUseless = true; + House->IsTiberiumShort = true; + return(TICKS_PER_SECOND*7); + } + } else { + IsUseless = false; + } + } + break; + + /* + ** Harvest at current location until full or Tiberium exhausted. + */ + case HARVESTING: +// if (Fetch_Stage() > ARRAY_SIZE(Class->Harvester_Load_List)) { +// Set_Stage(0); +// } + if (Fetch_Rate() == 0) { + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + } + + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Load_List)) return(1); + if (!Harvesting()) { + IsHarvesting = false; + if (Tiberium_Load() == 1) { + Status = FINDHOME; + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } else { + if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom)) { + ArchiveTarget = TARGET_NONE; + Status = FINDHOME; + } else { + Status = HARVESTING; + IsHarvesting = true; + } + } + return(1); + } + return(1); +// return(TICKS_PER_SECOND*Rule.OreDumpRate); + + /* + ** Find and head to refinery. + */ + case FINDHOME: + if (!Target_Legal(NavCom)) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + } + } else { + ScenarioInit++; + nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + ScenarioInit--; + if (nearest != NULL) { + Assign_Destination(::As_Target(Nearby_Location(nearest))); + } + } + } + break; + + /* + ** In communication with refinery so that it will successfully dock and + ** unload. If, for some reason, radio contact was lost, then hunt for + ** another refinery to unload at. + */ + case HEADINGHOME: + Assign_Mission(MISSION_ENTER); + return(1); + + /* + ** The harvester has nothing to do. There is no Tiberium nearby and + ** no where to go. + */ + case GOINGTOIDLE: + if (IsUseless) { + if (House->ActiveBScan & STRUCTF_REPAIR) { + Assign_Mission(MISSION_REPAIR); + } else { + Assign_Mission(MISSION_HUNT); + } + } + Assign_Mission(MISSION_GUARD); + break; + + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * * + * Computer controlled units must be intelligent enough to find enemies as well as to * + * attack them. This AI process will handle both the simple attack process as well as the * + * scanning for enemy units to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Hunt(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == UNIT_MCV) { + enum { + FIND_SPOT, + WAITING + }; + + switch (Status) { + + /* + ** This stage handles locating a convenient spot, rotating to face the correct + ** direction and then commencing the deployment operation. + */ + case FIND_SPOT: + if (Goto_Clear_Spot()) { + if (Try_To_Deploy()) { + Status = WAITING; + } + } + break; + + /* + ** This stage watchdogs the deployment operation and if for some reason, the deployment + ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for + ** a convenient spot to deploy. + */ + case WAITING: + if (!IsDeploying) { + Status = FIND_SPOT; + } + break; + } + } else { + + return(DriveClass::Mission_Hunt()); + } + return(MissionControl[Mission].Normal_Delay()+Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Overlap_List -- Determines overlap list for units. * + * * + * The unit overlap list is used to keep track of which cells are to * + * be marked for redraw. This is critical in order to keep the units * + * displayed correctly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the overlap list pointer for the unit at its * + * present position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1994 JLB : Created. * + * 06/19/1994 JLB : Uses Coord_Spillable_List function. * + *=============================================================================================*/ +short const * UnitClass::Overlap_List(bool redraw) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (IsSelected) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + if (Class->IsTurretEquipped || Class->IsRadarEquipped) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + int size = ICON_PIXEL_W; + + if (redraw && (IsSelected || IsFiring)) { + size += 24; + } + if (Class->IsGigundo || IsAnimAttached) { + size = ICON_PIXEL_W*2; + } + + return(Coord_Spillage_List(Coord, size)+1); +} + + +/*********************************************************************************************** + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * * + * Use this routine to determine if the unit can enter the cell * + * specified and given the direction of entry specified. Typically, * + * this is used when determining unit travel path. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The facing that the unit would enter the specified * + * cell. If this value is -1, then don't consider * + * facing when performing the check. * + * * + * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is * + * allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/16/1994 JLB : Converted to member function. * + * 07/04/1995 JLB : Allowed to drive on building trying to enter it. * + *=============================================================================================*/ +MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + bool cancrush = false; + + CellClass const * cellptr = &Map[cell]; + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map() && IsLocked) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** Certain vehicles can drive over walls. Check for this case and + ** and return the appropriate flag. Other units treat walls as impassable. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (optr->IsWall) { + + /* + ** If the blocking wall is crushable (and not owned by this player or one of this players + ** allies, then record that it is crushable and let the normal logic take over. The end + ** result should cause this unit to consider the cell passable. + */ + if (optr->IsCrushable && Class->IsCrusher) { + cancrush = !House->Is_Ally(cellptr->Owner); + } + + if (!cancrush && Is_Weapon_Equipped()) { + WarheadTypeClass const * whead = Class->PrimaryWeapon->WarheadPtr; + + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { +// if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; +// } else { +// return(MOVE_NO); +// } + } else { + return(MOVE_NO); + } + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + bool crushable = false; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** If object is a land mine, allow movement if possible. + */ + if (obj->What_Am_I() == RTTI_BUILDING && (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House))) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) return(MOVE_OK); + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) return(MOVE_OK); + } + + /* + ** Always allow entry if trying to move on a cell with + ** authorization from the occupier. + */ + if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) { + return(MOVE_OK); + } + + /* + ** Special check to allow entry into the sea transport this vehicle + ** is trying to enter. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target()) { + return(MOVE_NO); + } + + bool is_moving = obj->Is_Foot() && + (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->IsRotating || ((FootClass *)obj)->IsDriving); +// (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->IsDriving); + + if (House->Is_Ally(obj)) { + if (is_moving) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4; + if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) { + return(MOVE_NO); + } + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO); + + /* + ** If the blocking object is not in the same zone, then it certainly + ** isn't a temporary block, it is a permanent one. + */ + if (Map[Coord].Zones[Class->MZone] != cellptr->Zones[Class->MZone]) return(MOVE_NO); + + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** If this unit can crush infantry, and there is an enemy infantry in the + ** cell, don't consider the cell impassible. This is true even if the unit + ** doesn't contain a legitimate weapon. + */ + bool crusher = Class->IsCrusher; + if (!crusher || !obj->Class_Of().IsCrushable) { + + /* + ** Any non-allied blockage is considered impassable if the unit + ** is not equipped with a weapon. + */ + if (Class->PrimaryWeapon == NULL) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the unit is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + +#ifdef TOFIX + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + crushable = true; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (!cancrush && retval != MOVE_DESTROYABLE && Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (retval == MOVE_OK && !crushable && (cellptr->Flag.Composite & 0x3F) != 0) { + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } else { + if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) { + retval = MOVE_MOVING_BLOCK; + } else { + + /* + ** Enemy infantry have reserved the cell. If this unit can crush + ** infantry, consider the cell passable. If not, then consider the + ** cell destroyable if it has a weapon. If neither case applies, then + ** this vehicle should avoid the cell altogether. + */ + if (!Class->IsCrusher) { + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsAntiGround) { + retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + } + } + + /* + ** If its ok to move into the cell because we can crush whats in the cell, then + ** make sure no one else is already moving into the cell to crush something. + */ + if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) { + + /* + ** However, if the cell is occupied by a crushable vehicle, then we can + ** never be sure if some other friendly vehicle is also trying to crush + ** the cell at the same time. In the case of a crushable vehicle in the + ** cell, then allow entry. + */ + if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) { + return(MOVE_MOVING_BLOCK); + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * UnitClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the unit list and unit objects. This routine is typically * + * used in preparation for a new scenario load. All units are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Init(void) +{ + Units.Free_All(); +} + + +/*********************************************************************************************** + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * * + * This routine will start the vehicle moving by marking the destination cell as * + * reserved. Cells must be reserved in this fashion or else they might become occupied as * + * the vehicle is proceeding toward it. * + * * + * INPUT: headto -- The location where the vehicle will be heading. * + * * + * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell * + * being occupied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Start_Driver(COORDINATE & headto) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) {//BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/11/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(ObjectClass const * object) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + /* + ** Allow units to move onto land mines. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** If the unit doesn't have a weapon, but can crush the object, then consider + ** the object as a movable location. + */ + if (action == ACTION_ATTACK && !Can_Player_Fire()) { + if (Class->IsCrusher && object->Class_Of().IsCrushable) { + action = ACTION_MOVE; + } else { + action = ACTION_SELECT; + } + } + + /* + ** Don't allow special deploy action unless there is something to deploy. + */ + if (action == ACTION_SELF) { + if (*this == UNIT_MCV) { + + /* + ** The MCV will get the no-deploy cursor if it couldn't + ** deploy at its current location. + */ + ((ObjectClass &)(*this)).Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + action = ACTION_NO_DEPLOY; + } + ((ObjectClass &)(*this)).Mark(MARK_DOWN); + + } else { + + /* + ** The mine layer can "deploy" its mines if it currently isn't + ** sitting on top of a mine and it still has mines available. + */ + if (*this == UNIT_MINELAYER) { + if (!Ammo || Map[Center_Coord()].Cell_Building() || (Map[Center_Coord()].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Map[Center_Coord()].Smudge).IsBib)) { + action = ACTION_NO_DEPLOY; + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK || *this == UNIT_MAD) { + if (*this == UNIT_CHRONOTANK) { +// If the chrono tank's counter is still charging up, don't allow deploy. Or, +// if it's a player-controlled chrono tank, and the player's currently trying +// to teleport a different unit, don't allow teleporting this unit. + if(MoebiusCountDown || (IsOwnedByPlayer && House->UnitToTeleport && Map.IsTargettingMode == SPC_CHRONO2)) { + action = ACTION_NO_DEPLOY; + } + } + } else { +#endif + /* + ** All other units can "deploy" their passengers if they in-fact have + ** passengers and are a transport vehicle. Otherwise, they cannot + ** perform any self action. + */ + if (Class->Max_Passengers() > 0) { + if (How_Many() == 0) { + action = ACTION_NO_DEPLOY; + } + } else { + action = ACTION_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + + /* + ** Special return to friendly refinery action. + */ + if (House->IsPlayerControl && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) { + if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { + action = ACTION_ENTER; + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_MOVE; + } + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno() && object->What_Am_I() == RTTI_VESSEL) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if( *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + action = ACTION_NONE; + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NONE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. At the unit level, only the harvester is checked for. The lower * + * classes determine the regular action response. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(CELL cell) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) { + return(ACTION_HARVEST); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NOMOVE; + } +#endif + return(action); +} + + +/*********************************************************************************************** + * UnitClass::Exit_Repair -- Drive the unit off the repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +#define XYCELL(x, y) (y*MAP_CELL_W+x) +void UnitClass::Exit_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int i; + CELL cell; + bool found = false; + static short const ExitRepair[] = { + XYCELL(0, -2), + XYCELL(1, -1), + XYCELL(2, 0), + XYCELL(1, 1), + XYCELL(0, 2), + XYCELL(-1, 1), + XYCELL(-2, 0), + XYCELL(-1, -1) + }; + + cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())]; + if (Can_Enter_Cell(cell) == MOVE_OK) found = true; + + if (!found) for (i=0; i<8; i++) { + cell = Coord_Cell(Coord) + ExitRepair[i]; + if (Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } + if (found) { +// DirType dir = Direction(cell); + + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * * + * Handles the guard mission for the unit. If the IQ is high enough and the unit is * + * a harvester, it will begin to harvest automatically. An MCV might autodeploy. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the time delay before this command is executed again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/08/1995 JLB : Fixes gunboat problems. * + *=============================================================================================*/ +int UnitClass::Mission_Guard(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + if (/*House->IsBaseBuilding &&*/ !House->IsHuman && Class->IsToHarvest && House->Get_Quantity(STRUCT_REFINERY) > 0 && !House->IsTiberiumShort) { + Assign_Mission(MISSION_HARVEST); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + if (*this == UNIT_MCV && House->IsBaseBuilding) { + Assign_Mission(MISSION_UNLOAD); + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + return(DriveClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * * + * This routine intercepts the normal move mission and if a gunboat is being processed, * + * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission * + * regardless of what the player did. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 09/28/1995 JLB : Harvester stick in guard mode if no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Move(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + IsHarvesting = false; + + /* + ** Always make sure that that transport door is closed if the vehicle is moving. + */ + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + + return(DriveClass::Mission_Move()); +} + + +/*********************************************************************************************** + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; + + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_S); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * * + * This routine will attach a house flag to this unit. * + * * + * INPUT: house -- The house that is having its flag attached to it. * + * * + * OUTPUT: Was the house flag successfully attached to this unit? * + * * + * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure * + * of this routine. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Attach(HousesType house) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (house != HOUSE_NONE && Flagged == HOUSE_NONE) { + Flagged = house; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * * + * This routine will remove the house flag that is attached to this unit. * + * * + * INPUT: none * + * * + * OUTPUT: Was the flag successfully removed? * + * * + * WARNINGS: This routine doesn't put the flag into a new location. That operation must * + * be performed or else the house flag will cease to exist. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Remove(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Flagged != HOUSE_NONE) { + Flagged = HOUSE_NONE; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the unit. * + * This will either be the number of passengers or the percentage full (in 1/5ths) of * + * a harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Pip_Count(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->Max_Passengers() > 0) { + return(How_Many()); + } + + if (*this == UNIT_MINELAYER) { + int retval = 0; + if (Ammo > 0) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + return(retval); + } + + if (*this == UNIT_HARVESTER) { + return((Gold + Gems) / 4); + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK) { + int fulldur = ChronoTankDuration * TICKS_PER_MINUTE; + return( (fulldur - MoebiusCountDown) / (fulldur / 5)); + } +#endif + return(0); +} + + +/*********************************************************************************************** + * UnitClass::APC_Close_Door -- Closes an APC door. * + * * + * This routine will initiate closing of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Close_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Close_Door(10, 2); +} + + +/*********************************************************************************************** + * UnitClass::APC_Open_Door -- Opens an APC door. * + * * + * This routine will initiate opening of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Open_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) { + Open_Door(10, 2); + } else { + Open_Door(1, 2); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * * + * When a unit is destroyed, a crew member might be generated. This routine will return * + * with the infantry type to produce for this unit. This routine will be called for every * + * survivor that is generated. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType UnitClass::Crew_Type(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon == NULL) { + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + } + return(DriveClass::Crew_Type()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * * + * This mission handler will look for a repair facility. If one is found then contact * + * is established and then the normal Mission_Enter logic is performed. The repair facility * + * will take over the actual repair coordination process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, true); + + IsHarvesting = false; + + /* + ** If there is no available repair facility, then check to see if there + ** are any repair facilities at all. If not, then enter this unit + ** into idle state. + */ + if (nearest == NULL) { + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Enter_Idle_Mode(); + } + } else { + + /* + ** Try to establish radio contact with the repair facility. If contact + ** was established, then proceed with normal enter mission, which handles + ** the repair process. + */ + if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + return(1); + } + } + + /* + ** If no action could be performed at this time, then wait + ** around for a bit before trying again. + */ + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * * + * This routine will return with the facing that a projectile will travel if it was * + * fired at this instant. The facing should match the turret facing for those units * + * equipped with a turret. If the unit doesn't have a turret, then it will be the facing * + * of the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for a projectile. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Fire_Direction(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + if (*this == UNIT_V2_LAUNCHER) { + int diff1 = SecondaryFacing.Difference(DIR_E); + int diff2 = SecondaryFacing.Difference(DIR_W); + diff1 = ABS(diff1); + diff2 = ABS(diff2); + int diff = min(diff1, diff2); + int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff); + if (SecondaryFacing.Difference(DIR_N) < 0) { + return(DirType)(SecondaryFacing - (DirType)adj); + } else { + return(DirType)(SecondaryFacing + (DirType)adj); + } + } + return(SecondaryFacing.Current()); + } + + return(DriveClass::Fire_Direction()); +} + + +/*********************************************************************************************** + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * * + * This virtual routine is used to determine if the vehicle is allowed * + * to start moving. It is typically called when the vehicle desires * + * to move but needs confirmation from the turret logic before * + * proceeding. This happens when dealing with a vehicle that must have * + * its turret face the same direction as the body before the vehicle * + * may begin movement. * + * * + * INPUT: dir -- The facing the unit wants to travel in. * + * * + * OUTPUT: bool; Can the unit begin forward movement now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Ok_To_Move(DirType dir) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsLockTurret) { + if (IsRotating) { + return(false); + } else { + if (SecondaryFacing.Difference(dir)) { + ((UnitClass *)this)->SecondaryFacing.Set_Desired(dir); + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * * + * This routine determines if the turret can fire upon the target * + * specified. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use to determine legality to fire. 0=primary, * + * 1=secondary. * + * * + * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + * 06/01/1994 JLB : Returns reason why it can't fire. * + *=============================================================================================*/ +FireErrorType UnitClass::Can_Fire(TARGET target, int which) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + FireErrorType fire = DriveClass::Can_Fire(target, which); + + if (fire == FIRE_OK) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((Class->IsNoFireWhileMoving /*!Class->IsTurretEquipped || Class->IsLockTurret*/) && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + dir = Direction(target); + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff < 8) { + return(DriveClass::Can_Fire(target, which)); + } + return(FIRE_FACING); + } + return(fire); +} + + +/*********************************************************************************************** + * UnitClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the turret. It will check * + * to see if firing is technically legal given the specified target. * + * If it is legal to fire, it does so. It is safe to call this routine * + * every game tick. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * UnitClass::Fire_At(TARGET target, int which) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BulletClass * bullet = NULL; + WeaponTypeClass const * weap = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + if (weap == NULL) return(NULL); + + if (Can_Fire(target, which) == FIRE_OK) { + bullet = DriveClass::Fire_At(target, which); + + if (bullet != NULL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +if(Class->Type == UNIT_DEMOTRUCK && IsActive) delete this; +#endif + /* + ** Possible reload timer set. + */ + if ((*this == UNIT_V2_LAUNCHER) && Reload == 0) { + Reload = TICKS_PER_SECOND * 30; + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * UnitClass::Class_Of -- Fetches a reference to the class type for this object. * + * * + * This routine will fetch a reference to the TypeClass of this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with reference to the type class of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & UnitClass::Class_Of(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * * + * Use this routine to determine what the Tiberium load is (as a fixed point percentage). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current "fullness" rating for the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +fixed UnitClass::Tiberium_Load(void) const +{ + assert(IsActive); + + if (*this == UNIT_HARVESTER) { + return(fixed(Tiberium, Rule.BailCount)); + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * * + * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple * + * calls to this routine are needed in order to fully offload all Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,* + * then this indicates that all Tiberium has been offloaded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + if (Tiberium) { + Tiberium--; +#ifdef TOFIX + if (House->IsHuman) { + return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); + } + return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); +#endif + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * * + * This routine will check to see if the target is infantry and it can be overrun. It will * + * try to overrun the infantry rather than attack it. This only applies to computer * + * controlled vehicles. If it isn't the infantry overrun case, then it falls into the * + * base class for normal (complex) approach algorithm. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. * + *=============================================================================================*/ +void UnitClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Only if there is a legal target should the approach check occur. + */ + if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) { + + /* + ** Special case: + ** If this is for a unit that can crush infantry, and the target is + ** infantry, AND the infantry is pretty darn close, then just try + ** to drive over the infantry instead of firing on it. + */ + TechnoClass * target = As_Techno(TarCom); + if (Class->IsCrusher && Distance(TarCom) < Rule.CrushDistance && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) { + Assign_Destination(TarCom); + return; + } + } + + /* + ** In the other cases, uses the more complex "get to just within weapon range" + ** algorithm. + */ + DriveClass::Approach_Target(); +} + + +/*********************************************************************************************** + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * * + * This routine is called when a vehicle enters a square or when it is about to enter a * + * square (controlled by parameter). When a vehicle that can crush infantry enters a * + * cell that contains infantry, then the infantry will be destroyed (regardless of * + * affiliation). When a vehicle threatens to overrun a square, all occupying infantry * + * will attempt to get out of the way. * + * * + * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. * + * * + * threaten -- Don't kill, but just threaten to enter the cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Overrun_Square(CELL cell, bool threaten) +{ + assert(IsActive); + + CellClass * cellptr = &Map[cell]; + + if (Class->IsCrusher) { + if (threaten) { + + /* + ** If the cell contains infantry, then they will panic when a vehicle tries + ** drive over them. Have the infantry run away instead. + */ + if (cellptr->Flag.Composite & 0x1F) { + + /* + ** Scattering is controlled by the game difficulty level. + */ + cellptr->Incoming(0, true); + } + } else { + ObjectClass * object = cellptr->Cell_Occupier(); + int crushed = false; + while (object != NULL) { + if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < CELL_LEPTON_W/2) { + +#ifdef OBSOLETE + /* + ** If we're running over infantry, let's see if the infantry we're + ** squashing is a thief trying to capture us. If so, let him succeed. + */ + if (object->What_Am_I() == RTTI_INFANTRY && *((InfantryClass *)object) == INFANTRY_THIEF && ((InfantryClass *)object)->NavCom == As_Target()) { + ObjectClass * next = object->Next; + IsOwnedByPlayer = ((InfantryClass *)object)->IsOwnedByPlayer; + House = ((InfantryClass *)object)->House; + delete object; + object = next; + } else { +#endif + ObjectClass * next = object->Next; + crushed = true; + + /* + ** Record credit for the kill(s) + */ + Sound_Effect(VOC_SQUISH, Coord); + if (object->Height == 0) { + new AnimClass(ANIM_CORPSE1, object->Center_Coord()); + } + object->Record_The_Kill(this); + object->Mark(MARK_UP); + object->Limbo(); + delete object; + //new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord)); + + object = next; +#ifdef OBSOLETE + } +#endif + } else { + object = object->Next; + } + } + if (crushed) Do_Uncloak(); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * * + * This will assign the specified destination to the unit. It is presumed that doing is * + * is all that is needed in order to cause the unit to move to the specified destination. * + * * + * INPUT: target -- The target (location) to move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && Contact_With_Whom()->Is_Infantry()) { + Transmit_Message(RADIO_OVER_OUT); + } + + BuildingClass * b = As_Building(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + if (b != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (b->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + ArchiveTarget = target; + +/* +** HACK ALERT: The repair bay is counting on the assignment of the NavCom by this routine. +** The refinery must NOT have the navcom assigned by this routine. +*/ +if (*b != STRUCT_REPAIR) { + target = TARGET_NONE; +} + } else { + if (Transmit_Message(RADIO_DOCKING, b) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + if (*b == STRUCT_REPAIR) { + ArchiveTarget = target; + } + } +if (*b != STRUCT_REPAIR) { + ArchiveTarget = target; +// target = TARGET_NONE; +} + } + } else { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { + // TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + + /* + ** If the player clicked on a friendly repair facility and the repair + ** facility is currently not involved with some other unit (radio or unloading). + */ + if (b != NULL && *b == STRUCT_REPAIR) { + if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this) ) { +// if (target != NULL) { + ArchiveTarget = target; +// } +// target = TARGET_NONE; + } else { + + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + + /* + ** Last check to make sure that the loading square is free from permanent + ** occupation (such as a building). + */ + CELL cell = (CELL)(Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1)); + if (Ground[Map[cell].Land_Type()].Cost[Techno_Type_Class()->Speed] > 0) { + if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) { + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; + return; + } + + /* + ** Failure to establish a docking relationship with the refinery. + ** Bail & await further instructions. + */ + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * * + * This routine will search the map looking for a good target to attack. It takes into * + * consideration the type of weapon it is equipped with. * + * * + * INPUT: threat -- The threat type to search for. * + * * + * OUTPUT: Returns with a target value of the target that this unit should pursue. If there * + * is no suitable target, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET UnitClass::Greatest_Threat(ThreatType threat) const +{ + assert(IsActive); + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +#ifdef OBSOLETE + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } +#endif + + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Read_INI(CCINIClass & ini) +{ + UnitClass * unit; // Working unit pointer. + HousesType inhouse; // Unit house. + UnitType classid; // Unit class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = UnitTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != UNIT_NONE) { + + unit = new UnitClass(classid, inhouse); + if (unit != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + unit->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL,",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + unit->Trigger = tt; + } + } + + if (unit->Unlimbo(coord, dir)) { + unit->Strength = unit->Class->MaxStrength * fixed(strength, 256); + if (unit->Strength > unit->Class->MaxStrength-3) unit->Strength = unit->Class->MaxStrength; + if (Session.Type == GAME_NORMAL || unit->House->IsHuman) { + unit->Assign_Mission(mission); + unit->Commence(); + } else { + unit->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the unit could not be unlimboed, then this is a catastrophic error + ** condition. Delete the unit. + */ + delete unit; + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Write_INI -- Store the units to the INI database. * + * * + * This routine will store all the unit data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object to store to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the unit data out. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && !unit->IsInLimbo && unit->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio()*256, + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission), + unit->Trigger.Is_Valid() ? unit->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * * + * This will determine the value of the cargo carried (limited to considering only gold * + * and gems) and return that value. Use this to determine how 'valuable' a harvester is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit value of the cargo load of this unit (harvester). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Credit_Load(void) const +{ + return((Gold * Rule.GoldValue) + (Gems * Rule.GemValue)); +} + + +/*********************************************************************************************** + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * * + * Call this routine to determine if this unit should crush the object specified. The * + * test for crushable action depends on proximity and ability of the unit. If a unit * + * should crush the object, then it should be given a movement order to enter the cell * + * where the object is located. * + * * + * INPUT: it -- The object to see if it should be crushed. * + * * + * OUTPUT: bool; Should "it" be crushed by this unit? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Should_Crush_It(TechnoClass const * it) const +{ + assert(IsActive); + + /* + ** If this unit cannot crush anything or the candidate object cannot be crushed, + ** then it obviously should not try to crush it -- return negative answer. + */ + if (!Class->IsCrusher || it == NULL || !it->Techno_Type_Class()->IsCrushable) return(false); + + /* + ** Objects that are far away should really be fired upon rather than crushed. + */ + if (Distance(it) > Rule.CrushDistance) return(false); + + /* + ** Human controlled units don't automatically crush. Neither do computer controlled ones + ** if they are at difficult setting. + */ + if (House->IsHuman || House->Difficulty == DIFF_HARD) return(false); + + /* + ** If the weapon this unit is equipped with is very good against crushable objects then + ** fire the weapon instead. It is presumed that a wood destroying weapon is good against + ** most crushable object types (infantry). + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) return(false); + + /* + ** If the house IQ indicates that crushing should not be allowed, then don't + ** suggest that crushing be done. + */ + if (House->IQ < Rule.IQCrush) return(false); + + /* + ** Don't allow crushing of spies by computer-controlled vehicles. + */ + if (it->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)it == INFANTRY_SPY) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * * + * This scatter logic will actually look for a nearby location rather than an adjacent * + * free location. This is necessary because sometimes a unit is required to scatter more * + * than one cell. A vehicle on a service depot is a prime example. * + * * + * INPUT: threat -- The coordinate that a potential threat resides. If this is a non * + * threat related scatter, then this parameter will be zero. * + * * + * forced -- Should the scatter be performed even if it would be otherwise * + * inconvenient? * + * * + * nokidding-- Should the scatter be performed even if it would otherwise be * + * illegal? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + if (Mission == MISSION_SLEEP || Mission == MISSION_STICKY || Mission == MISSION_UNLOAD) return; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + if (PrimaryFacing.Is_Rotating()) return; +// if (IsRotating) return; + + if (Target_Legal(NavCom) && !nokidding) return; + + if (threat == 0) { + Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord), Class->Speed))); + } else { + DriveClass::Scatter(threat, forced, nokidding); + } +} + + +/*********************************************************************************************** + * UnitClass::Limbo -- Limbo this unit. * + * * + * This will cause the unit to go into a limbo state. If it was carrying a flag, then * + * the flag will be dropped where the unit is at. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was this unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Limbo(void) +{ + if (DriveClass::Limbo()) { + if (Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord)); + Flagged = HOUSE_NONE; + } + return(true); + } + return(false); +} + + +void UnitClass::Shroud_Regen(void) +{ + if (Class->IsGapper/*KO && !House->IsPlayerControl*/) { + static int _xtab[]={ + -1, 0, 1, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -1, 0, 1 + }; + static int _ytab[]={ + -3,-3,-3, + -2,-2,-2,-2,-2, + -1,-1,-1,-1,-1, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, + 3, 3, 3 + }; + int index; + int centerx, centery; + CELL trycell; + + // Only restore under the shroud if it's a valid field. + if (ShroudBits != (unsigned)-1L) { + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 30; index >= 0 && ShroudBits; index--) { + if (ShroudBits & 1) { + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); +#if(0) + Map.Map_Cell(trycell, PlayerPtr); +#else + Map.UnJam_Cell(trycell, House); + Map.Map_Cell(trycell, PlayerPtr); +#endif + } + ShroudBits >>= 1; + } + } + + if(IsActive && Strength) { + // Now shroud around the new center + ShroudBits = 0L; + ShroudCenter = Coord_Cell(Center_Coord()); + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 0; index < 31; index++) { + ShroudBits <<= 1; + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); + if (Map[trycell].IsMapped) { +#if(0) + Map.Shroud_Cell(trycell); +#else + Map.Jam_Cell(trycell, House); +#endif + ShroudBits |= 1; + } + } + } + + if (!House->IsPlayerControl) { + Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W); +// PlayerPtr->IsToLook = true; + } + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * * + * This logic is similar to normal guard area except that APCs owned by the computer will * + * try to load up with nearby infantry. This will give the computer some fake intelligence * + * when playing in skirmish mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay to use before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + /* + ** Check to see if this is an APC that is largely empty and not otherwise doing anything. + ** Such an APC should load up with infantry. + */ + if (Session.Type != GAME_NORMAL && +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + (*this == UNIT_APC || *this == UNIT_PHASE ) && +#else + *this == UNIT_APC && +#endif + !Target_Legal(TarCom) && + !In_Radio_Contact() && + House->Which_Zone(this) != ZONE_NONE && + !House->IsHuman) { + + + int needed = Class->Max_Passengers() - How_Many(); + for (int index = 0; index < Infantry.Count(); index++) { + if (needed == 0) break; + + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry != NULL && + infantry->IsActive && + !infantry->IsInLimbo && + infantry->Strength > 0 && + infantry->House == House && + !Target_Legal(infantry->TarCom) && + !Target_Legal(infantry->NavCom) && + Distance(infantry) < 7 * CELL_LEPTON_W && + (infantry->Mission == MISSION_GUARD || infantry->Mission == MISSION_GUARD_AREA)) { + + infantry->Assign_Mission(MISSION_ENTER); + infantry->ArchiveTarget = As_Target(); + needed--; + } + } + } + return(DriveClass::Mission_Guard_Area()); +} diff --git a/CODE/UNIT.H b/CODE/UNIT.H new file mode 100644 index 0000000..e075b84 --- /dev/null +++ b/CODE/UNIT.H @@ -0,0 +1,248 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/UNIT.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : UNIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef UNIT_H +#define UNIT_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + + +/**************************************************************************** +** For each instance of a unit (vehicle) in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular unit. +*/ +class UnitClass : public DriveClass +{ + public: + + /* + ** This points to the static control data that gives 'this' unit its characteristics. + */ + CCPtr Class; + + /* + ** This records the house flag that this object is currently carrying. + */ + HousesType Flagged; + + /* + ** This flag is used for when the harvester dumps ore, to track its + ** special animation. + */ + unsigned IsDumping:1; + + /* + ** This is a count of the # of loads of the various minerals that the + ** unit has harvested. + */ + unsigned Gold:5; + unsigned Gems:5; + + /* + ** This flag tells a unit that, if after reaching its destination, it + ** should scatter away. It's meant to help a LST unload its units by + ** having its previous passengers get out of the way. + */ + unsigned IsToScatter:1; + + /* + ** This records the number of "loads" of Tiberium the unit is carrying. Only + ** harvesters use this field. + */ + int Tiberium; + + /* + ** This is the area where a mobile gap generator stores the previously-held + ** shroud values for the cells surrounding itself. + */ + unsigned long ShroudBits; + + /* + ** This is the center coordinate for the mobile gap generator, as to + ** what cells should be revealed (according to ShroudBits) + */ + CELL ShroudCenter; + + /* + ** This is the timer that controls the reload rate. The MSAM rocket + ** launcher is the primary user of this. + */ + CDTimerClass Reload; + + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + UnitClass(UnitType classid, HousesType house); + UnitClass(NoInitClass const & x) : DriveClass(x), Class(x), Reload(x), SecondaryFacing(x) {}; + operator UnitType(void) const {return Class->Type;}; + virtual ~UnitClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual ObjectTypeClass const & Class_Of(void) const; + static void Init(void); + + bool Goto_Clear_Spot(void); + bool Try_To_Deploy(void); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + bool Tiberium_Check(CELL ¢er, int x, int y); + bool Flag_Attach(HousesType house); + bool Flag_Remove(void); + bool Goto_Tiberium(int radius); + bool Harvesting(void); + void APC_Close_Door(void); + void APC_Open_Door(void); + + /* + ** Query functions. + */ + bool Should_Crush_It(TechnoClass const * it) const; + int Credit_Load(void) const; + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + int Shape_Number(void) const; + virtual int Pip_Count(void) const; + virtual InfantryType Crew_Type(void) const; + virtual DirType Fire_Direction(void) const; + virtual bool Ok_To_Move(DirType facing) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual fixed Tiberium_Load(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * object) const; + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which=0); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Unload(void); + virtual int Mission_Guard(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int Mission_Repair(void); + virtual int Mission_Move(void); + void Rotation_AI(void); + void Firing_AI(void); + void Reload_AI(void); + bool Edge_Of_World_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Assign_Destination(TARGET target); + virtual void Overrun_Square(CELL cell, bool threaten=true); + virtual void Approach_Target(void); + virtual int Offload_Tiberium_Bail(void); + virtual void Enter_Idle_Mode(bool initial=false); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + void Exit_Repair(void); + void Shroud_Regen(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "UNITS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; +}; + +#endif diff --git a/CODE/UTRACKER.CPP b/CODE/UTRACKER.CPP new file mode 100644 index 0000000..0cbff8b --- /dev/null +++ b/CODE/UTRACKER.CPP @@ -0,0 +1,236 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : UTRACKER.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + * * + * Functions: * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" + +/* +** Define host to network to host functions for DOS +*/ +#ifndef WIN32 + +#define htonl(val) 0 +#define ntohl(val) 0 + +#endif //WIN32 + + +/*********************************************************************************************** + * UTC::UnitTrackerClass -- Class constructor * + * * + * * + * * + * INPUT: Number of unit types to reserve space for * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::UnitTrackerClass (int unit_count) +{ + UnitTotals = new long [unit_count]; // Allocate memory for the unit totals + UnitCount = unit_count; // Keep a record of how many unit entries there are + InNetworkFormat = 0; // The unit entries are in host format + Clear_Unit_Total(); // Clear each entry +} + + +/*********************************************************************************************** + * UTC::~UnitTrackerClass -- Class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::~UnitTrackerClass (void) +{ + delete UnitTotals; +} + + + +/*********************************************************************************************** + * UTC::Increment_Unit_Total -- Increment the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:12AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Increment_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]++; +} + + +/*********************************************************************************************** + * UTC::Decrement_Unit_Total -- Decrement the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Decrement_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]--; +} + + +/*********************************************************************************************** + * UTC::Get_All_Totals -- Returns a pointer to the start of the unit totals list * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Ptr to unit totals list * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +long *UnitTrackerClass::Get_All_Totals (void) +{ + return (UnitTotals); +} + + +/*********************************************************************************************** + * UTC::Clear_Unit_Total -- Clear out all the unit totals * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:14AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Clear_Unit_Total (void) +{ + memset (UnitTotals, 0, UnitCount * sizeof(long) ); +} + + + +/*********************************************************************************************** + * UTC::To_Network_Format -- Changes all unit totals to network format for the internet * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:15AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::To_Network_Format (void) +{ + if (!InNetworkFormat){ + for (int i=0 ; i. +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : UTRACKER.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** UnitTracker Class +*/ + +class UnitTrackerClass { + + public: + + UnitTrackerClass(int unit_count); + ~UnitTrackerClass(void); + + void Increment_Unit_Total (int unit_type); + void Decrement_Unit_Total (int unit_type); + void Clear_Unit_Total(void); + + int Get_Unit_Total (int unit_type); + long *Get_All_Totals (void); + int Get_Unit_Count (void){return (UnitCount);}; + + void To_Network_Format(void); + void To_PC_Format(void); + + private: + + long *UnitTotals; + int UnitCount; + int InNetworkFormat; + +}; + + + + + + + + + + + + + + + + diff --git a/CODE/VDATA.CPP b/CODE/VDATA.CPP new file mode 100644 index 0000000..4d24e9f --- /dev/null +++ b/CODE/VDATA.CPP @@ -0,0 +1,712 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VDATA.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * VesselTypeClass::VesselTypeClass -- Constructor for naval vessel types. * + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel.* + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory poo* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +// Submarine +static VesselTypeClass const VesselSubmarine( + VESSEL_SS, + TXT_SS, // NAME: Text name of this unit type. + "SS", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Destroyer +static VesselTypeClass const VesselDestroyer( + VESSEL_DD, + TXT_DD, // NAME: Text name of this unit type. + "DD", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Cruiser +static VesselTypeClass const VesselCruiser( + VESSEL_CA, + TXT_CA, // NAME: Text name of this unit type. + "CA", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Transport +static VesselTypeClass const VesselTransport( + VESSEL_TRANSPORT, + TXT_TRANSPORT, // NAME: Text name of this unit type. + "LST", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); + +// Gun Boat +static VesselTypeClass const VesselPTBoat( + VESSEL_PT, + TXT_PT, // NAME: Text name of this unit type. + "PT", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Missile Submarine +static VesselTypeClass const VesselMissileSubmarine( + VESSEL_MISSILESUB, + TXT_MISSILESUB, // NAME: Text name of this unit type. + "MSUB", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Transport +static VesselTypeClass const VesselCarrier( + VESSEL_CARRIER, + TXT_CARRIER, // NAME: Text name of this unit type. + "CARR", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); +#endif + +/*********************************************************************************************** + * VesselTypeClass::VesselTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the vessel static data. Each vessels is assign a specific * + * variation. This class elaborates what the variation actually is. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created * + *=============================================================================================*/ +VesselTypeClass::VesselTypeClass( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ) : + TechnoTypeClass( + RTTI_VESSELTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + secondaryoffset, + secondarylateral, + is_nominal, + false, + true, + true, + false, + false, + false, + is_turret_equipped, + true, + true, + rotation, + SPEED_FLOAT + ), + IsPieceOfEight(is_eight), + Type(type), + TurretOffset(toffset), + Mission(MISSION_GUARD), + Explosion(exp), + MaxSize(0) +{ + /* + ** Forced vessel overrides from the default. + */ + IsCrew = false; + Speed = SPEED_FLOAT; + IsScanner = true; +} + + +/*********************************************************************************************** + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory pool * + * * + * This will allocate a vessel type class object from the memory pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated vessel type class object. If memory in the * + * special heap has been exhaused, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselTypeClass::operator new(size_t) +{ + return(VesselTypes.Alloc()); +} + + +/*********************************************************************************************** + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * * + * This will return a previously allocated vessel object back to the special pool from * + * whence it was originally allocated. * + * * + * INPUT: pointer -- Pointer to the vessel type object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::operator delete(void * pointer) +{ + VesselTypes.Free((VesselTypeClass *)pointer); +} + + +/*********************************************************************************************** + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * * + * This will pre-allocate all the vessel types required. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once and do so before processing the rules.ini file. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Init_Heap(void) +{ + /* + ** These vessel type class objects must be allocated in the exact order that they + ** are specified in the VesselType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new VesselTypeClass(VesselSubmarine); // VESSEL_SS + new VesselTypeClass(VesselDestroyer); // VESSEL_DD + new VesselTypeClass(VesselCruiser); // VESSEL_CA + new VesselTypeClass(VesselTransport); // VESSEL_TRANSPORT + new VesselTypeClass(VesselPTBoat); // VESSEL_PT +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new VesselTypeClass(VesselMissileSubmarine); // VESSEL_MISSILESUB +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new VesselTypeClass(VesselCarrier); // VESSEL_CARRIER +#endif +} + + +/*********************************************************************************************** + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * * + * This routine will fetch a reference to the vessel type that corresponds to the vessel * + * type specified. * + * * + * INPUT: type -- The vessel type number to convert. * + * * + * OUTPUT: Returns with a reference to the vessel type class that corresponds to the vessel * + * type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselTypeClass & VesselTypeClass::As_Reference(VesselType type) +{ + return(*VesselTypes.Ptr(type)); +} + + +#ifdef NEVER +/*********************************************************************************************** + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel. * + * * + * Use this routine to fetch a pointer to the vessel factory that can build this vessel * + * type. * + * * + * INPUT: intheory -- If true, then this indicates that if the factory is currently * + * busy doing other things, this won't make in ineligible for searching. * + * Typical use of this is by the sidebar logic which needs only to know * + * if theoretical production is allowed. * + * * + * legal -- If true, then the buildings are checked for specific legality when * + * being scanned. For building placement, this is usually false, for * + * sidebar button adding, this is usually true. * + * * + * house -- The owner of the unit to be produced. This has an effect of legality. * + * * + * OUTPUT: Returns with a pointer to the factory (building) that can produce this vessel type.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * VesselTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_VESSELTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} +#endif + + +/*********************************************************************************************** + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * * + * This routine is used by the scenario editor to display a representation of this * + * vessel type in the object placement dialog. * + * * + * INPUT: x,y -- Pixel coordinate to render the center of this vessel type to. * + * * + * window -- The window to clip the shape to. The pixel coordinates are relative * + * to this window. * + * * + * house -- The owner of the vessel. This is used to give the vessel its color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef SCENARIO_EDITOR +void VesselTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = Rotation/6; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * * + * This routine is called when the scenario editor needs to obtain a list of the * + * vessel object that can be placed down. It will submit all the vessel types that can * + * be placed down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Prep_For_Add(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif //SCENARIO_EDITOR + + +/*********************************************************************************************** + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * * + * This routine is called when the type of vessel is known (by way of a VesselTypeClass) * + * and a corresponding vessel object needs to be created. * + * * + * INPUT: house -- Pointer to the owner that this vessel will be assigned to. * + * * + * OUTPUT: Returns with a pointer to the vessel object created. If no vessel could be * + * created, then NULL is returned. * + * * + * WARNINGS: The vessel is created in a limbo state. It must first be placed down upon * + * the map before it starts to function. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +ObjectClass * VesselTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new VesselClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * * + * This routine is used to create a vessel and then place it down upon the * + * map. * + * * + * INPUT: cell -- The location to place this vessel down upon. * + * * + * house -- The house to assign this vessel's ownership to. * + * * + * OUTPUT: bool; Was the vessel successfully created and placed down upon the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + VesselClass * unit = new VesselClass(Type, house); + if (unit != NULL) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + delete unit; + return(false); +} + + +/*********************************************************************************************** + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * * + * This routine is used to fetch the width and height of this vessel type. These dimensions * + * are not specific to any particular facing. Rather, they are only for the generic vessel * + * size. * + * * + * INPUT: width, height -- Reference to the integers that are to be initialized with the * + * pixel width and height of this vessel type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Dimensions(int & width, int & height) const +{ + width = 48; + height = 48; +} + + +/*********************************************************************************************** + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * * + * This routine will load in the vessel shape data. It should be called only once at the * + * beginning of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::One_Time(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + VesselTypeClass const & uclass = As_Reference(index); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (uclass.Level != -1 || index==VESSEL_CARRIER) { +#else + if (uclass.Level != -1) { +#endif +// if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch a pointer to the unit's shape data. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + + ((int &)uclass.MaxSize) = 26; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * * + * This routine will determine the pixel adjustment necessary for a turret. The direction * + * specified is what the vessel body is facing. * + * * + * INPUT: dir -- The presumed direction of the body facing for the vessel. * + * * + * x,y -- The center pixel position for the vessel. These values should be * + * adjusted (they are references) to match the adjusted offset for the * + * turret. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Turret_Adjust(DirType dir, int & x, int & y) const +{ + short xx = x; + short yy = y; + + switch (Type) { + case VESSEL_CA: + Normal_Move_Point(xx, yy, dir, 22); + x = xx; + y = yy-4; + break; + + case VESSEL_PT: + Normal_Move_Point(xx, yy, dir, 14); + x = xx; + y = yy+1; + break; + + case VESSEL_DD: + Normal_Move_Point(xx, yy, dir+DIR_S, 8); + x = xx; + y = yy-4; + break; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * * + * This routine will return the overlap list for a vessel that is sitting still in the * + * center of a cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list that this vessel would use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselTypeClass::Overlap_List(void) const +{ + static short const _ship[] = {-3, -2, -1, 1, 2, 3, + -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), -(MAP_CELL_W+2), -(MAP_CELL_W-2), + +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +(MAP_CELL_W+2), +(MAP_CELL_W-2), + REFRESH_EOL}; +// static short const _ship[] = {-1, 1, +// -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), +// +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +// REFRESH_EOL}; + + return(&_ship[0]); +} + + +/*********************************************************************************************** + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * * + * Use this routine to convert an ASCII version of a vessel type into the corresponding * + * VesselType id value. Typical use of this would be to parse the INI file. * + * * + * INPUT: name -- Pointer to the ASCII name to be converted into a vessel type. * + * * + * OUTPUT: Returns with the vessel type number that matches the string specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselType VesselTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (VesselType classid = VESSEL_FIRST; classid < VESSEL_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(VESSEL_NONE); +} + + +/*********************************************************************************************** + * VesselTypeClass::Max_Pips -- Fetches the maximum pips allowed for this vessel. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this vessel type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselTypeClass::Max_Pips(void) const +{ + return(Max_Passengers()); +} diff --git a/CODE/VECTOR.CPP b/CODE/VECTOR.CPP new file mode 100644 index 0000000..f6e8c7ae --- /dev/null +++ b/CODE/VECTOR.CPP @@ -0,0 +1,675 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VECTOR.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VECTOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BooleanVectorClass::BooleanVectorClass -- Copy constructor for boolean array. * + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * BooleanVectorClass::operator = -- Assignment operator. * + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::ID -- Finds object ID based on value. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::Resize -- Changes the size of the vector. * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +#include "vector.h" +#include +#include + +/* +** The following template function can be located here ONLY if all the instantiations are +** declared in a header file this module includes. By placing the template functions here, +** it speeds up compiler operation and reduces object module size. +*/ + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) : + Vector(0), + VectorMax(size), + IsAllocated(false) +{ + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) : + Vector(0), + VectorMax(0), + IsAllocated(false) +{ + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + if (this != &vector) { + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasible) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assignment operator will + ** only work for the simplest of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} + +//---------------------------------------------------------------------------------------------- + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * * + * This is the constructor for a boolean array. This constructor takes the memory pointer * + * provided as assigns that as the array data pointer. * + * * + * INPUT: size -- The size of the array (in bits). * + * * + * array -- Pointer to the memory that the array is to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must make sure that the memory specified is large enough to contain the * + * bits specified. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(unsigned size, unsigned char * array) +{ + BitArray.Resize(((size + (8-1)) / 8), array); + LastIndex = -1; + BitCount = size; +} + + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Copy constructor of boolean array. * + * * + * This is the copy constructor for a boolean array. It is used to make a duplicate of the * + * boolean array. * + * * + * INPUT: vector -- Reference to the vector to be duplicated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(BooleanVectorClass const & vector) +{ + LastIndex = -1; + *this = vector; +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator = -- Assignment operator. * + * * + * This routine will make a copy of the specified boolean vector array. The vector is * + * copied into an already constructed existing vector. The values from the existing vector * + * are destroyed by this copy. * + * * + * INPUT: vector -- Reference to the vector to make a copy of. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass & BooleanVectorClass::operator =(BooleanVectorClass const & vector) +{ + Fixup(); + Copy = vector.Copy; + LastIndex = vector.LastIndex; + BitArray = vector.BitArray; + BitCount = vector.BitCount; + return(*this); +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * * + * This is the comparison operator for a boolean vector class. Boolean vectors are equal * + * if the bit count and bit values are identical. * + * * + * INPUT: vector -- Reference to the vector to compare to. * + * * + * OUTPUT: Are the boolean vectors identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::operator == (const BooleanVectorClass & vector) +{ + Fixup(LastIndex); + return(BitCount == vector.BitCount && BitArray == vector.BitArray); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * * + * This routine will resize the boolean vector object. An index value used with a boolean * + * vector must be less than the value specified in as the new size. * + * * + * INPUT: size -- The new maximum size of this boolean vector. * + * * + * OUTPUT: Was the boolean vector sized successfully? * + * * + * WARNINGS: The boolean array might be reallocated or even deleted by this routine. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::Resize(unsigned size) +{ + Fixup(); + + if (size) { + + /* + ** Record the previous bit count of the boolean vector. This is used + ** to determine if the array has grown in size and thus clearing is + ** necessary. + */ + int oldsize = BitCount; + + /* + ** Actually resize the bit array. Since this is a bit packed array, + ** there are 8 elements per byte (rounded up). + */ + int success = BitArray.Resize(((size + (8-1)) / 8)); + + /* + ** Since there is no default constructor for bit packed integers, a manual + ** clearing of the bits is required. + */ + BitCount = size; + if (success && oldsize < size) { + for (int index = oldsize; index < size; index++) { + (*this)[index] = 0; + } + } + + return(success); + } + + /* + ** Resizing to zero is the same as clearing and deallocating the array. + ** This is always successful. + */ + Clear(); + return(true); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * * + * This routine will clear out the boolean array. This will free any allocated memory and * + * result in the boolean vector being unusable until the Resize function is subsequently * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The boolean vector cannot be used until it is resized to a non null condition. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Clear(void) +{ + Fixup(); + BitCount = 0; + BitArray.Clear(); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * * + * This is the preferred (and quick) method to clear the boolean array to a false condition.* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Reset(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\0', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * * + * This is the preferred (and fast) way to set all boolean elements to true. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Set(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\xFF', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * * + * Use this routine to set the boolean value copy to match the appropriate bit in the * + * boolean array. The boolean array will be updated with any changes from the last time * + * a value was fetched from the boolean vector. By using this update method, the boolean * + * array can be treated as a normal array even though the elements are composed of * + * otherwise inaccessible bits. * + * * + * INPUT: index -- The index to set the new copy value to. If the index is -1, then the * + * previous value will be updated into the vector array, but no new value * + * will be fetched from it. * + * * + * OUTPUT: none * + * * + * WARNINGS: Always call this routine with "-1" if any direct manipulation of the bit * + * array is to occur. This ensures that the bit array is accurate. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Fixup(int index) const +{ + /* + ** If the requested index value is illegal, then force the index + ** to be -1. This is the default non-index value. + */ + if ((unsigned)index >= BitCount) { + index = -1; + } + + /* + ** If the new index is different than the previous index, there might + ** be some fixing up required. + */ + if (index != LastIndex) { + + /* + ** If the previously fetched boolean value was changed, then update + ** the boolean array accordingly. + */ + if (LastIndex != -1) { + Set_Bit((void*)&BitArray[0], LastIndex, Copy); + } + + /* + ** If this new current index is valid, then fill in the reference boolean + ** value with the appropriate data from the bit array. + */ + if (index != -1) { + ((unsigned char&)Copy) = Get_Bit(&BitArray[0], index); + } + + ((int &)LastIndex) = index; + } +} + + diff --git a/CODE/VECTOR.H b/CODE/VECTOR.H new file mode 100644 index 0000000..a36c6e1 --- /dev/null +++ b/CODE/VECTOR.H @@ -0,0 +1,298 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VECTOR.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VECTOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::Resize -- Changes the size of the vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::ID -- Finds object ID based on value. * + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VECTOR_H +#define VECTOR_H + +#ifdef NEVER +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif +#endif + +#include +#include + +inline void * operator new(size_t , void * pointer) {return(pointer);} +inline void * operator new[](size_t , void * pointer) {return(pointer);} + + +/************************************************************************** +** This is a general purpose vector class. A vector is defined by this +** class, as an array of arbitrary objects where the array can be dynamically +** sized. Because is deals with arbitrary object types, it can handle everything. +** As a result of this, it is not terribly efficient for integral objects (such +** as char or int). It will function correctly, but the copy constructor and +** equality operator could be highly optimized if the integral type were known. +** This efficiency can be implemented by deriving an integral vector template +** from this one in order to supply more efficient routines. +*/ +template +class VectorClass +{ + public: + VectorClass(NoInitClass const & ) {}; + VectorClass(unsigned size=0, T const * array=0); + VectorClass(VectorClass const &); // Copy constructor. + virtual ~VectorClass(void); + + T & operator[](unsigned index) {return(Vector[index]);}; + T const & operator[](unsigned index) const {return(Vector[index]);}; + virtual VectorClass & operator =(VectorClass const &); // Assignment operator. + virtual int operator == (VectorClass const &) const; // Equality operator. + virtual int Resize(unsigned newsize, T const * array=0); + virtual void Clear(void); + unsigned Length(void) const {return VectorMax;}; + virtual int ID(T const * ptr); // Pointer based identification. + virtual int ID(T const & ptr); // Value based identification. + + protected: + + /* + ** This is a pointer to the allocated vector array of elements. + */ + T * Vector; + + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; + + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated:1; +}; + + +/************************************************************************** +** This derivative vector class adds the concept of adding and deleting +** objects. The objects are packed to the beginning of the vector array. +** If this is instantiated for a class object, then the assignment operator +** and the equality operator must be supported. If the vector allocates its +** own memory, then the vector can grow if it runs out of room adding items. +** The growth rate is controlled by setting the growth step rate. A growth +** step rate of zero disallows growing. +*/ +template +class DynamicVectorClass : public VectorClass +{ + public: + DynamicVectorClass(unsigned size=0, T const * array=0); + + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const * array=0); + + // Resets and frees the vector array. + virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + + // Fetch number of "allocated" vector objects. + int Count(void) const {return(ActiveCount);}; + + // Add object to vector (growing as necessary). + int Add(T const & object); + int Add_Head(T const & object); + + // Delete object just like this from vector. + int Delete(T const & object); + + // Delete object at this vector index. + int Delete(int index); + + // Deletes all objects in the vector. + void Delete_All(void) {ActiveCount = 0;}; + + // Set amount that vector grows by. + int Set_Growth_Step(int step) {return(GrowthStep = step);}; + + // Fetch current growth step rate. + int Growth_Step(void) {return GrowthStep;}; + + virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; + virtual int ID(T const & ptr); + + protected: + + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; + + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; +}; + + +/************************************************************************** +** This is a derivative of a vector class that supports boolean flags. Since +** a boolean flag can be represented by a single bit, this class packs the +** array of boolean flags into an array of bytes containing 8 boolean values +** each. For large boolean arrays, this results in an 87.5% savings. Although +** the indexing "[]" operator is supported, DO NOT pass pointers to sub elements +** of this bit vector class. A pointer derived from the indexing operator is +** only valid until the next call. Because of this, only simple +** direct use of the "[]" operator is allowed. +*/ +class BooleanVectorClass +{ + public: + BooleanVectorClass(unsigned size=0, unsigned char * array=0); + BooleanVectorClass(BooleanVectorClass const & vector); + + // Assignment operator. + BooleanVectorClass & operator =(BooleanVectorClass const & vector); + + // Equivalency operator. + int operator == (BooleanVectorClass const & vector); + + // Fetch number of boolean objects in vector. + int Length(void) {return BitCount;}; + + // Set all boolean values to false; + void Reset(void); + + // Set all boolean values to true. + void Set(void); + + // Resets vector to zero length (frees memory). + void Clear(void); + + // Change size of this boolean vector. + int Resize(unsigned size); + + // Fetch reference to specified index. + bool const & operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool & operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index)); + }; + + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_False_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + private: + void Fixup(int index=-1) const; + + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; +}; + +#endif diff --git a/CODE/VERSION.CPP b/CODE/VERSION.CPP new file mode 100644 index 0000000..0b80ad8 --- /dev/null +++ b/CODE/VERSION.CPP @@ -0,0 +1,852 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VERSION.CPP 14 3/16/97 10:16p Joe_b $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : VERSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : September 17, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VersionClass::VersionClass -- Class constructor * + * VersionClass::Version_Number -- Returns program version number * + * VersionClass::Major_Version -- returns major version # * + * VersionClass::Minor_Version -- returns minor version (revision) number* + * VersionClass::Version_Name -- returns version # as char string * + * VersionClass::Read_Text_String -- reads version text string from disk * + * VersionClass::Version_Protocol -- returns default protocol for version* + * VersionClass::Init_Clipping -- Initializes version clipping * + * VersionClass::Clip_Version -- "clips" the given version range * + * VersionClass::Min_Version -- returns lowest version # to connect to * + * VersionClass::Max_Version -- returns highest version # to connect to * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "rawolapi.h" // For version number. +#endif + +/****************************** Globals ************************************/ +//--------------------------------------------------------------------------- +// This is a table of version numbers # the communications protocol used for +// that version number. It's used by the game owner to determine the +// protocol to be used for a given session. +// +// This table needs to be updated every time a new communications protocol +// is implemented, not every time a new version is created. +// +// A given protocol is used from its corresponding version #, up to (but not +// including) the next version number in the table. The last protocol in +// the table is the default protocol for this version. +//--------------------------------------------------------------------------- +static VersionProtocolType VersionProtocol[] = { + {0x00001000,COMM_PROTOCOL_SINGLE_NO_COMP}, // (obsolete) + {0x00002000,COMM_PROTOCOL_SINGLE_E_COMP}, // (obsolete) + {0x00010000,COMM_PROTOCOL_MULTI_E_COMP}, +}; + + +/*************************************************************************** + * VersionClass::VersionClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + * 09/17/1996 JLB : Converted to used initializer list. * + *=========================================================================*/ +VersionClass::VersionClass(void) : + Version(0), + MajorVer(0), + MinorVer(0), + MinClipVer(0), + MaxClipVer(0), + VersionInit(false), + MajorInit(false), + MinorInit(false), + TextInit(false) +{ + VersionText[0] = '\0'; + VersionName[0] = '\0'; +} + + +/*************************************************************************** + * VersionClass::Version_Number -- Returns program version number * + * * + * Version Number Format: * + * Non-CHEAT format: * + * Byte 3,2: major version (printed to the left of a decimal) * + * Byte 1,0: minor version (printed to the right of a decimal) * + * Thus, version 1.07 would appear as 0x0001 0700 * + * * + * This format guarantees that a greater-than or less-than comparison * + * will work on version numbers. * + * * + * CHEAT format: * + * Byte 3: Month # * + * Byte 2: Day # * + * Byte 1: Hour # * + * Byte 0: Minute # * + * * + * This format guarantees a unique version number for each compile (as * + * long as they're a minute or more apart), with increasing version #'s * + * for later times. * + * * + * Either format should be printed in hex. * + * * + * This routine also fills in a text string (retrieved with Version_Text), * + * which may contain a custom string (such as "Beta"); this string is * + * read from the file VERSION.TXT. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ + +//ajw Note: This function is no longer called. MIN_VERSION is now incorrect, but I don't have time +// for a full rebuild (3 hrs!), and as MIN_VERSION is no longer referred to, I'm going to leave it. +// Really, it should be deleted or commented out. +// Version number used is now GAME_VERSION. +// Note also that VERSION_RA_300 is wrong, but not used. + +unsigned long VersionClass::Version_Number(void) +{ + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the version has already been set, just return it. + //------------------------------------------------------------------------ + if (VersionInit) { + return (Version); + } + + //------------------------------------------------------------------------ + // Generate the version # + //------------------------------------------------------------------------ + Version = ((Major_Version() << 16) | Minor_Version()); + VersionInit = 1; + + return (Version); + +} /* end of Version_Number */ + + +/*************************************************************************** + * VersionClass::Major_Version -- returns major version # * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Major Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Major_Version(void) +{ +#ifdef DEV_VERSION + static char * date = __DATE__; // format: Mmm dd yyyy + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char buf[10]; + char * ptr; + char * tok; + int monthnum; + int daynum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the major version # is already set, just return it. + //------------------------------------------------------------------------ + if (MajorInit) { + return (MajorVer); + } + + //------------------------------------------------------------------------ + // For a development version, use the date (month & day) as the major + // version number. + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the month and place in the high byte. + //........................................................................ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + monthnum = (((ptr - months) / 3) + 1); + } else { + monthnum = 0; + } + + //........................................................................ + // Convert the month number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",monthnum); + sscanf(buf,"%x",&monthnum); + + //........................................................................ + // Fetch the date and place that in the low byte. + //........................................................................ + tok = strtok(NULL, " "); + if (tok) { + daynum = atoi(tok); + } else { + daynum = 0; + } + + //........................................................................ + // Convert the day number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",daynum); + sscanf(buf,"%x",&daynum); + + MajorVer = ((monthnum << 8) | daynum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor version number. + //------------------------------------------------------------------------ +#else + MajorVer = MAJOR_VERSION; +#endif + + MajorInit = 1; + + return (MajorVer); + +} /* end of Major_Version */ + + +/*************************************************************************** + * VersionClass::Minor_Version -- returns minor version (revision) number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Minor Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Minor_Version(void) +{ +#ifdef DEV_VERSION + static char * time = __TIME__; // format: hh:mm:ss + char * tok; + char buf[10]; + int hournum; + int minnum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the minor version # is already set, just return it. + //------------------------------------------------------------------------ + if (MinorInit) { + return (MinorVer); + } + + //------------------------------------------------------------------------ + // For in-development versions, use the time (hour & min) as the minor + // version + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the hour and place that in the last two digit positions. + //........................................................................ + tok = strtok(time, ": "); + if (tok) { + hournum = atoi(tok); + } else { + hournum = 0; + } + + //........................................................................ + // Convert the hour number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",hournum); + sscanf(buf,"%x",&hournum); + + //........................................................................ + // Fetch the minute and place that in the last two digit positions. + //........................................................................ + tok = strtok(NULL, ": "); + if (tok) { + minnum = atoi(tok); + } else { + minnum = 0; + } + + //........................................................................ + // Convert the minute number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",minnum); + sscanf(buf,"%x",&minnum); + + MinorVer = ((hournum << 8) | minnum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor revision number. + //------------------------------------------------------------------------ +#else + +#ifdef FIXIT_VERSION_3 // Insanity. CS installation should not have affected version number. ajw + + MinorVer = MINOR_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_CSII + MinorVer = MINOR_VERSION; + if (Is_Counterstrike_Installed()) { + MinorVer = MINOR_VERSION - 1; + } +#else + #ifdef FIXIT_VERSION + /* If counterstrike is not installed then we report version 1.06 + * otherwise we report ourselves as 1.08 + */ + if (Is_Counterstrike_Installed() == false) { + MinorVer = (MINOR_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + MinorVer = MINOR_VERSION; + } + #else + MinorVer = MINOR_VERSION; + #endif +#endif + +#endif // FIXIT_VERSION_3 + +#endif + + MinorInit = 1; + + return (MinorVer); + +} /* end of Minor_Version */ + + +/*************************************************************************** + * VersionClass::Version_Name -- returns version # as char string * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +char * VersionClass::Version_Name(void) +{ + //------------------------------------------------------------------------ + // For developmental versions, just use the major & minor version #'s + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), VerNum.Minor_Version()); + + //------------------------------------------------------------------------ + // For final versions, trim 0's off the minor version + //------------------------------------------------------------------------ +#else + unsigned short adjusted_minor; + int i; + + adjusted_minor = Minor_Version(); + for (i = 0; i < 4; i++) { + if ( (adjusted_minor & 0x000f) != 0) { + break; + } + adjusted_minor >>= 4; + } + + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), adjusted_minor); +#endif + + return (VersionName); + +} /* end of Version_Name */ + + +/*************************************************************************** + * VersionClass::Read_Text_String -- reads version # text string from disk * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Read_Text_String(void) +{ + RawFileClass file("VERSION.TXT"); + + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[strlen(VersionText)-1] == '\r') { + VersionText[strlen(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + +} /* end of Read_Text_String */ + + +/*************************************************************************** + * VersionClass::Version_Protocol -- returns default protocol for version * + * * + * INPUT: * + * version version # to look up * + * * + * OUTPUT: * + * protocol value to use for that version # * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +CommProtocolType VersionClass::Version_Protocol(unsigned long version) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # entries in the VersionProtocol table + //------------------------------------------------------------------------ + j = sizeof (VersionProtocol) / sizeof(VersionProtocolType); + + //------------------------------------------------------------------------ + // Search backwards through the table, finding the first entry for which + // the given version # is >= the table's; this is the range containing + // the given version number. + //------------------------------------------------------------------------ + for (i = j - 1; i >= 0; i--) { + if (version >= VersionProtocol[i].Version) { + return (VersionProtocol[i].Protocol); + } + } + + //------------------------------------------------------------------------ + // If no range was found for the given version, return the highest + // possible protocol. (If version clipping is being done properly, this + // case should never happen, but never say never.) + //------------------------------------------------------------------------ + return (VersionProtocol[j-1].Protocol); + +} /* end of Version_Protocol */ + + +/*************************************************************************** + * VersionClass::Init_Clipping -- Initializes version clipping * + * * + * Initializes the Min & Max clip version #'s to the min & max values * + * defined for this program. This sets the initial range for use by * + * the Clip_Version routine. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Init_Clipping(void) +{ + MinClipVer = Min_Version(); + MaxClipVer = Max_Version(); + +} /* end of Init_Clipping */ + + +/*************************************************************************** + * VersionClass::Clip_Version -- "clips" the given version range * + * * + * This routine compares another program's supported min/max version * + * range with the range currently defined by 'MinClipVer' and 'MaxClipVer'.* + * If there is overlap in the two ranges, Min & MaxClipVer are adjusted * + * to the bounds of the overlap. The routine returns the largest version * + * number shared by the ranges (MaxClipVer). * + * * + * Thus, by calling Init_Clipping(), then a series of Clip_Version() calls,* + * a mutually-acceptable range of version #'s may be negotiated between * + * different versions of this program. The max shared version may then * + * be used to decide upon a communications protocol that all programs * + * support. * + * * + * INPUT: * + * minver min version to clip to * + * maxver max version to clip to * + * * + * OUTPUT: * + * highest clipped version # * + * 0 = given range is below our current range * + * 0xFFFFFFFF = given range is above our current range * + * * + * WARNINGS: * + * Be sure Init_Clipping() was called before performing a clipping * + * session. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Clip_Version(unsigned long minver, + unsigned long maxver) +{ + //------------------------------------------------------------------------ + // If the given range is outside & above our own, return an error. + //------------------------------------------------------------------------ + if (minver > MaxClipVer) + return (0xffffffff); + + //------------------------------------------------------------------------ + // If the given range is outside & below our own, return an error. + //------------------------------------------------------------------------ + if (maxver < MinClipVer) + return (0); + + //------------------------------------------------------------------------ + // Clip the lower range value + //------------------------------------------------------------------------ + if (minver > MinClipVer) + MinClipVer = minver; + + //------------------------------------------------------------------------ + // Clip the upper range value + //------------------------------------------------------------------------ + if (maxver < MaxClipVer) + MaxClipVer = maxver; + + //------------------------------------------------------------------------ + // Return the highest version supported by the newly-adjusted range. + //------------------------------------------------------------------------ + return (MaxClipVer); + +} /* end of Clip_Version */ + + +/*************************************************************************** + * VersionClass::Min_Version -- returns lowest version # to connect to * + * * + * Returns the minimum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * min version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Min_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_VERSION + if ( Is_Counterstrike_Installed() ) { + return (MIN_VERSION - 1); + } + return (MIN_VERSION); +#else + if ( Is_Counterstrike_Installed() ){ + return (MIN_VERSION - CS_MINOR_VERSION_MODIFIER); + }else{ + return (MIN_VERSION); + } +#endif + +#endif // FIXIT_VERSION_3 + +#endif + +} /* end of Min_Version */ + + +/*************************************************************************** + * VersionClass::Max_Version -- returns highest version # to connect to * + * * + * Returns the maximum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * max version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Max_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else + +#ifdef FIXIT_CSII // checked - ajw + return (MAX_VERSION); +#else + #ifdef FIXIT_VERSION + if (Is_Counterstrike_Installed() == false) { + return (MAX_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + return (MAX_VERSION); + } + #else + if ( Is_Counterstrike_Installed() ){ + return (MAX_VERSION + CS_MINOR_VERSION_MODIFIER); + }else{ + return (MAX_VERSION); + } + #endif +#endif +#endif + +#endif // FIXIT_VERSION_3 + +} /* end of Max_Version */ + + +char const * Version_Name(void) +{ +#ifdef NEVER + static char buffer[32]; + + /* + ** Fetch the day and month components from the current + ** build date. + */ + static char * date = __DATE__; // format: Mmm dd yyyy + strupr(date); + char const * tok = strtok(date, " "); + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char const * ptr = strstr(months, tok); + int monthnum = 0; + if (ptr != NULL) { + monthnum = (((ptr - months) / 3) + 1); + } + + tok = strtok(NULL, " "); + int daynum = 0; + if (tok != NULL) { + daynum = atoi(tok); + } + + /* + ** Fetch the time components from the current build time. + */ + static char * time = __TIME__; // format: hh:mm:ss + tok = strtok(time, ": "); + int hournum = 0; + if (tok != NULL) { + hournum = atoi(tok); + } + + tok = strtok(NULL, ": "); + int minnum = 0; + if (tok != NULL) { + minnum = atoi(tok); + } + + sprintf(buffer, "%02d%02d%02d", monthnum, daynum, (hournum*4) + (minnum / 15)); + return(buffer); +#else + + static char buffer[128]; + + memset(buffer, '\0', sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + strcpy( buffer, "3.03" ); + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_PATCH_108 + //strcpy(buffer, "1.08PE"); + strcpy(buffer, "1.08P"); + +#ifdef FIXIT_CSII + strcpy(buffer,"2.00"); +#ifdef DEV_VERSION + strcpy(buffer,VerNum.Version_Name()); +#endif +#ifdef DEV_VER_NAME + strcpy(buffer,__DATE__); // format: Mmm dd yyyy +#endif +#endif + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else + strcpy(buffer, "1.07E"); +#endif + +#endif // FIXIT_VERSION_3 + + if (Is_Counterstrike_Installed ()){ + strcat (buffer, "CS"); + } + if (Is_Aftermath_Installed()) { + strcat (buffer, "AM"); + } + +#if(TEN) + strcat(buffer, "Ten"); // Ten version +#endif + +#if(MPATH) + strcat(buffer, "MPath"); // MPath version +#endif + + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + strcat(buffer, "\r"); + file.Read(&buffer[strlen(buffer)], 25); + } + return(buffer); +#endif +} diff --git a/CODE/VERSION.H b/CODE/VERSION.H new file mode 100644 index 0000000..d7799c0 --- /dev/null +++ b/CODE/VERSION.H @@ -0,0 +1,202 @@ +/* +** 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 . +*/ + +/* $Header: /counterstrike/VERSION.H 2 3/10/97 6:22p Steve_tall $ */ +/*************************************************************************** + *** 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 * + * * + * File Name : VERSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : October 26, 1995 [BRR] * + * * + * This class maintains version information, and communications protocol * + * information. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_RED_ALERT_104 0x00010000 //Shipped US version number +#define VERSION_RED_ALERT_107 0x00011000 //Shipped Counterstrike number +#define VERSION_RED_ALERT_106 0x00010500 //Patch without CS installed +#define VERSION_RED_ALERT_108 0x00011500 //Patch with CS installed +#define VERSION_AFTERMATH_CS 0x00011FFF //Aftermath with CS installed +#define VERSION_AFTERMATH 0x00012000 //Aftermath + +// Aftermath has, in a sense, used version 2.00. (Because of the text on title screen.) Call ourselves version 3. +#define VERSION_RA_300 0x00030000 // RA, CS, AM executables unified into one. All are now the same version. -ajw +// It seems that extra information, that didn't belong there, was being stuffed into version number. Namely, whether or not +// Counterstrike is installed. I'm going to change things back to the way they should be, as I see it. Version will describe +// the version of the executable only. When it comes to communicating whether or not a player has expansions present, separate +// data will be transmitted. + +// However, having said that, a caveat. I'm going to have to use the same communication method that was used previously, because +// I need to have prior versions of the game recognize that they can't play against this version. What I'll do is encode +// "does player have aftermath" (which is actually the only fact that matters, in multiplayer) in the communicated version +// number, as a high bit set/unset. This version of the game will receive this communicated value and pull out the Aftermath +// bit. Older version will reject us as a possible opponent, because, whether or not AM is installed, our version number will +// be too high for them. + +// These horrible things are no longer used. ajw +#define CS_MAJOR_VERSION_MODIFIER 0x0000 +#define CS_MINOR_VERSION_MODIFIER 0x1000 + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +typedef enum CommProtocolEnum { + COMM_PROTOCOL_SINGLE_NO_COMP = 0, // single frame with no compression + COMM_PROTOCOL_SINGLE_E_COMP, // single frame with event compression + COMM_PROTOCOL_MULTI_E_COMP, // multiple frame with event compression + COMM_PROTOCOL_COUNT, + DEFAULT_COMM_PROTOCOL = COMM_PROTOCOL_MULTI_E_COMP +} CommProtocolType; + +typedef struct { + unsigned long Version; + CommProtocolType Protocol; +} VersionProtocolType; + +class VersionClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + VersionClass(void); + virtual ~VersionClass() {}; + + //..................................................................... + // These routines return the current version number. The long version + // number contains the major version in the high word, and the minor + // version in the low word. They should be interpreted in hex. + //..................................................................... + unsigned long Version_Number(void); + unsigned short Major_Version(void); + unsigned short Minor_Version(void); + + //..................................................................... + // Retrieves a pointer to the version # as a text string (#.#), with + // the trailing 0's trimmed off. + //..................................................................... + char *Version_Name(void); + + //..................................................................... + // Retrieves a pointer to the current version text. + //..................................................................... + char *Version_Text() {return (VersionText);} + + //..................................................................... + // Returns the default comm protocol for a given version number. + //..................................................................... + CommProtocolType Version_Protocol(unsigned long version); + + //..................................................................... + // These routines support "version clipping". + //..................................................................... + void Init_Clipping(void); + unsigned long Clip_Version(unsigned long minver, unsigned long maxver); + unsigned long Get_Clipped_Version(void) {return (MaxClipVer);} + + //..................................................................... + // These routines return the theoretical lowest & highest version #'s + // that this program will connect to; this does not take any previous + // version clipping into account. + //..................................................................... + unsigned long Min_Version(void); + unsigned long Max_Version(void); + + private: + //..................................................................... + // Fills in a 'VersionText' with a descriptive version name. + //..................................................................... + void Read_Text_String(void); + + //..................................................................... + // These values define the major & minor version #'s for the current + // version. Change these values to change the game's version #! + //..................................................................... + enum VersionEnum { +#ifdef FIXIT_VERSION_3 + MAJOR_VERSION = 0x0003, + MINOR_VERSION = 0x0000 +#else + MAJOR_VERSION = 0x0001, + MINOR_VERSION = 0x2000 +#endif + }; + + //..................................................................... + // These values control which other versions this program will connect + // to. Keep them current! + // If CHEAT is defined, the program will only connect to itself; these + // values aren't used. + //..................................................................... + enum VersionRangeEnum { +#ifdef FIXIT_VERSION_3 + // ajw - We can only play against same version. + MIN_VERSION = VERSION_RA_300, + MAX_VERSION = VERSION_RA_300 +#else + MIN_VERSION = VERSION_RED_ALERT_104, //0x00010000, // Version: 1.0 + MAX_VERSION = VERSION_AFTERMATH //0x00012000 // Version: 1.2 +#endif + }; + + //..................................................................... + // This is the program's version number, stored internally. + //..................................................................... + unsigned long Version; + unsigned short MajorVer; + unsigned short MinorVer; + + //..................................................................... + // This array is used for formatting the version # as a string + //..................................................................... + char VersionName[30]; + + //..................................................................... + // This array contains special version labels (such as "Beta"), stored + // in the file VERSION.TXT. If the file isn't present, no label is + // shown. + //..................................................................... + char VersionText[16]; + + //..................................................................... + // Values used for "Version Clipping" + //..................................................................... + unsigned long MinClipVer; + unsigned long MaxClipVer; + + //..................................................................... + // Bitfield Flags + // IsInitialized: is set if the VERSION.TXT file has been read + //..................................................................... + unsigned VersionInit : 1; + unsigned MajorInit : 1; + unsigned MinorInit : 1; + unsigned TextInit : 1; +}; +#endif +/************************** end of version.h *******************************/ + diff --git a/CODE/VER_FUNC.TXT b/CODE/VER_FUNC.TXT new file mode 100644 index 0000000..8b87660 --- /dev/null +++ b/CODE/VER_FUNC.TXT @@ -0,0 +1,98 @@ +/*********************************************************************************************** + * Version_Number -- Determines the version number. * + * * + * This routine will determine the version number by analyzing the date and teim that the * + * program was compiled and then generating a unique version number based on it. The * + * version numbers are guaranteed to be larger for later dates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the version number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/24/1995 JLB : Created. * + *=============================================================================================*/ +int Version_Number(void) +{ +#ifdef OBSOLETE + static bool initialized = false; + static int version; + static char * date = __DATE__; + static char * time = __TIME__; + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + + if (!initialized) { + char * ptr; + char * tok; + + /* + ** Fetch the month and place in the first two digit positions. + */ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + version = (((ptr - months) / 3)+1) * 10000; + } + + /* + ** Fetch the date and place that in the next two digit positions. + */ + tok = strtok(NULL, " "); + if (tok) { + version += atoi(tok) * 100; + } + + /* + ** Fetch the time and place that in the last two digit positions. + */ + tok = strtok(time, ": "); + if (tok) { + version += atoi(tok); + } + + + /* + ** Fetch the virgin text file (if present). + */ + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[sizeof(VersionText)-1] == '\r') { + VersionText[sizeof(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + + initialized = true; + } + return(version); +#endif + +#ifdef PATCH + + #ifdef DEMO + sprintf(VersionText, " 1.1a"); // Demo version. + #else + strcpy(VersionText, ".19a "); +// strcpy(VersionText, ".34 "); + #endif + return(1); + +#else + + #ifdef DEMO + sprintf(VersionText, " 1.0a"); // Demo version. + #else + // sprintf(VersionText, ".%02dp", 13); // Patch version. + sprintf(VersionText, ".%02d", 14); // Master version. + #endif + return(1); +#endif +} + + diff --git a/CODE/VESSEL.CPP b/CODE/VESSEL.CPP new file mode 100644 index 0000000..914d132 --- /dev/null +++ b/CODE/VESSEL.CPP @@ -0,0 +1,2356 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VESSEL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * VesselClass::Draw_It -- Draws the vessel. * + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse* + * VesselClass::Init -- Initialize the vessel heap system. * + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * operator delete -- Deletes a vessel's memory block. * + * operator new -- Allocates a vessel object memory block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * * + * This is the normal constructor for vessel class objects. It will set up a vessel that * + * is valid excepting that it won't be placed on the map. * + * * + * INPUT: classid -- The type of vessel this will be. * + * * + * house -- The owner of this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::VesselClass(VesselType classid, HousesType house) : + DriveClass(RTTI_VESSEL, Vessels.ID(this), house), + Class(VesselTypes.Ptr((int)classid)), + IsToSelfRepair(false), + IsSelfRepairing(false), + DoorShutCountDown(0), + PulseCountDown(0), + SecondaryFacing(PrimaryFacing) +{ + House->Tracking_Add(this); + + /* + ** The ammo member is actually part of the techno class, but must be initialized + ** manually here because this is where we first have access to the class pointer. + */ + Ammo = Class->MaxAmmo; + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** The techno class cloakabilty flag is set according to the type + ** class cloakability flag. + */ + IsCloakable = Class->IsCloakable; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +/*********************************************************************************************** + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * * + * The destructor will destroy the vessel and ensure that it is properly removed from the * + * game engine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::~VesselClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * operator new -- Allocates a vessel object memory block. * + * * + * This routine is used to allocate a block of memory from the vessel heap. If there is * + * no more space in the heap, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the pointer to the allocated block of memory. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselClass::operator new(size_t) +{ + void * ptr = Vessels.Alloc(); + if (ptr != NULL) { + ((VesselClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * operator delete -- Deletes a vessel's memory block. * + * * + * This overloaded delete operator will return the vessel's memory back to the pool of * + * memory used for vessel allocation. * + * * + * INPUT: ptr -- Pointer to the vessel's memory block. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + assert(((VesselClass *)ptr)->IsActive); + ((VesselClass *)ptr)->IsActive = false; + } + Vessels.Free((VesselClass *)ptr); +} + + +/*********************************************************************************************** + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * * + * This routine will return with a reference to the static class data for this vessel. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the class data structure associated with this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & VesselClass::Class_Of(void) const +{ + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * * + * This routine is used by find path and other movement logic to determine if this * + * vessel can enter the cell specified. * + * * + * INPUT: cell -- The cell to check this vessel against. * + * * + * OUTPUT: Returns with the movement restriction associated with movement into this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + CellClass const * cellptr = &Map[cell]; + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** If there is blocking terrain (such as ice), then the vessel + ** can't move there. + */ + if (cellptr->Cell_Terrain() != NULL) { + return(MOVE_NO); + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (cellptr->Flag.Composite) { + + if (cellptr->Flag.Occupy.Building) { + return(MOVE_NO); + } + + TechnoClass * techno = cellptr->Cell_Techno(); + if (techno != NULL && techno->Cloak == CLOAKED && !House->Is_Ally(techno)) { + return(MOVE_CLOAK); + } + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * * + * This routine will return with the shape number to use for the ship's body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the ship's body when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Shape_Number(void) const +{ + /* + ** For eight facing units, adjust the facing number accordingly. + */ + FacingType facing = Dir_Facing(PrimaryFacing.Current()); + + int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1; + + /* + ** Special case code for transport. The north/south facing is in frame + ** 0. The east/west facing is in frame 3. + */ + if (*this == VESSEL_TRANSPORT) { + shapenum = 0; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + shapenum = 0; + } +#endif + /* + ** Door opening and closing animation stage check. + */ + if (!Is_Door_Closed()) { + shapenum = Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * VesselClass::Draw_It -- Draws the vessel. * + * * + * Draws the vessel on the tactical display. This routine is called by the map rendering * + * process to display the vessel. * + * * + * INPUT: x,y -- The pixel coordinate to draw this vessel at. * + * * + * window-- The window to base clipping and coordinates upon when drawing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + if (Visual_Character() != VISUAL_HIDDEN) { + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale); + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + int shapenum = TechnoClass::BodyShape[tfacing]+32; + DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16); + + switch (Class->Type) { + case VESSEL_CA: + shapefile = Class->TurretShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + xx = x; + yy = y; + turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16); + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_DD: + shapefile = Class->SamShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_PT: + shapefile = Class->MGunShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + default: + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + + DriveClass::Draw_It(x, y, window); + + /* + ** Patch so the transport will draw its passengers on top of itself. + */ + if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { + assert(Contact_With_Whom()->IsActive); + Contact_With_Whom()->Render(true); + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * * + * This routine will display the vessel's status information. The information is dumped to * + * the monochrome monitor. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed * + * to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Debug_Dump(MonoClass * mono) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_SHIP)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL); + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * * + * This routine will fetch the overlap list for this vessel type. It takes into * + * consideration any movement the vessel may be doing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list for this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselClass::Overlap_List(bool redraw) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (IsSelected) { + rect = Union(rect, Rect(-32, 32, 64, 64)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + return(Coord_Spillage_List(Coord, 56)+1); +} + + +/*********************************************************************************************** + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * * + * This routine is called once for each vessel object during each main game loop. All * + * normal AI processing is handled here. This includes dispatching and maintaining any * + * processing that is specific to vessel objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. * + *=============================================================================================*/ +void VesselClass::AI(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + + /* + ** HACK ALERT: + ** If the ship finds itself in a hunt order but it has no weapons, then tell it + ** to sail off the map instead. + */ + if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) { + Assign_Mission(MISSION_RETREAT); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { + Commence(); + } + +#ifndef CLIPDRAW + if (Map.In_View(Coord_Cell(Center_Coord())) && Visual_Character() != VISUAL_HIDDEN && Visual_Character() != VISUAL_NORMAL) { + Mark(MARK_CHANGE); + } +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Re-stock the ammo of any on-board helicopters on an aircraft carrier. + if (*this == VESSEL_CARRIER && How_Many()) { + if (!MoebiusCountDown) { + MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE; + ObjectClass *obj = Attached_Object(); + while (obj) { + long bogus; + ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus); + obj = (obj->Next); + } + } + } +#endif + /* + ** Process base class AI routine. If as a result of this, the vessel gets + ** destroyed, then detect this fact and bail early. + */ + DriveClass::AI(); + if (!IsActive) { + return; + } + + /* + ** Handle body and turret rotation. + */ + Rotation_AI(); + + /* + ** Handle any combat processing required. + */ + Combat_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) { + LST_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** Do a step of repair here, if appropriate. + */ + Repair_AI(); +} + + +/*********************************************************************************************** + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * * + * This routine is called when the vessel travels one cell. It handles any processes that * + * must occur on a per-cell basis. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Per_Cell_Process(PCPType why) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + Look(!IsPlanningToLook); + IsPlanningToLook = false; + + if (IsToSelfRepair) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face)); + SmartPtr whom; + whom = Map[cell].Cell_Building(); + if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) { + if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + IsSelfRepairing = true; + IsToSelfRepair = false; + break; + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + } + + if (IsActive) { + DriveClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(ObjectClass const * object) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + if (action == ACTION_SELF) { + if (Class->Max_Passengers() == 0 || !How_Many() ) { + action = ACTION_NONE; + } else { +// check to see if the transporter can unload. + bool found = 0; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this != VESSEL_CARRIER) +#endif + for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) { + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + CellClass * cell = &Map[cellnum]; + if (Map.In_Radar(cellnum) && Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + continue; + } else { + found = true; + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + + if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) { + action = ACTION_ENTER; + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL && + (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) { + action = ACTION_NOMOVE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +// if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_ENTER: + action = ACTION_MOVE; + // BRR 10/18/96 IsToSelfRepair = true; + break; + + default: +// action = ACTION_NONE; + break; + } +// } +// if (action == ACTION_ENTER) { + // BRR 10/18/96 IsToSelfRepair = true; +// action = ACTION_MOVE; +// } else { +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false; +// } +// } + + DriveClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + // BRR 10/18/96 IsToSelfRepair = false; +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = false; +// } + DriveClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * * + * This routine is called to apply damage to this vessel. The amount and type of damage * + * to apply is passed as parameters. This routine could end up destroying the vessel. * + * * + * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage * + * value will be adjusted so that the actual damage applied will be * + * stored into this variable for possible subsequent examination. * + * * + * distance -- The distance from the center of the damage to the vessel itself. * + * * + * warhead -- The warhead type of damage to apply. * + * * + * source -- The perpetrator of this damage. Knowing who was responsible allows * + * retaliation logic. * + * * + * forced -- Is this damage forced upon the vessel by some supernatural means? * + * * + * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the * + * general effect of the damage. Examine this return value to see if the vessel * + * has been destroyed. * + * * + * WARNINGS: The vessel could be destroyed by the call to this routine! * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, int forced) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + new AnimClass(anim, Coord); + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_The_Screen(Class->MaxStrength / 150); + } + } + + /* + ** Possibly have the crew member run away. + */ + Mark(MARK_UP); + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + object->Record_The_Kill(source); + delete object; + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking (and it's not a submarine). + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) { +#else + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim != NULL) anim->Attach_To(this); + } + } + return(res); +} + + +/*********************************************************************************************** + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * * + * This routine is used to determine if this vessel can fire its weapon at the target * + * specified. * + * * + * INPUT: target -- The target candidate to determine if firing upon is valid. * + * * + * which -- Which weapon to use when considering the candidate as a potential * + * target. * + * * + * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If * + * it can't fire, then the enum indicates why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +FireErrorType VesselClass::Can_Fire(TARGET target, int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + if(!How_Many() || Arm) { + return(FIRE_REARM); + } else { + return(FIRE_OK); + } + } +#endif + FireErrorType fire = DriveClass::Can_Fire(target, which); +if(*this==VESSEL_DD) { +Mono_Set_Cursor(0,0); +} + if (fire == FIRE_OK || fire == FIRE_CLOAKED) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** Ensure that a torpedo will never be fired upon a non naval target. + ** Unless that non-naval target is a naval building (sub pen/ship yard) + */ + bool isseatarget = Is_Target_Vessel(target); + bool isbridgetarget = false; + if (weapon->Bullet->IsSubSurface) { + isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges + isseatarget |= isbridgetarget; + } + BuildingClass * bldg = ::As_Building(target); + if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) { + isseatarget = true; + } + + dir = Direction(target); + + if (weapon->Bullet->IsSubSurface) { + if (!isseatarget && Is_Target_Object(target)) { + return(FIRE_CANT); + } + + /* + ** If it's a torpedo, let's check line-of-sight to make sure that + ** there's only water squares between us and the target. + */ + ObjectClass * obj = As_Object(target); + COORDINATE coord = Center_Coord(); + if (obj != NULL) { + int totaldist = ::Distance(coord, obj->Center_Coord()); + while (totaldist > CELL_LEPTON_W) { + coord = Coord_Move(coord, dir, CELL_LEPTON_W); + if (Map[coord].Land_Type() != LAND_WATER) { + if (!isbridgetarget) { + return(FIRE_RANGE); + } + } + + /* + ** Check for friendly boats in the way. + */ + TechnoClass * tech = Map[coord].Cell_Techno(); + if (tech != NULL && tech != this && House->Is_Ally(tech)) { + return(FIRE_RANGE); + } + totaldist -= CELL_LEPTON_W; + } + } + } + + /* + ** Depth charges are only good against submarines. + */ + if (weapon->Bullet->IsAntiSub) { + if (!isseatarget) { + return(FIRE_CANT); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) { +#endif + if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) { + return(FIRE_CANT); + } + } + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) { +#endif + return(FIRE_CANT); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (!Class->IsTurretEquipped && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff > 8) { + return(FIRE_FACING); + } + } + return(fire); +} + + +/*********************************************************************************************** + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * * + * This routine is called to determine the coordinate that a fired projectile will * + * originate from. * + * * + * INPUT: which -- Which weapon is this query directed at? * + * * + * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE VesselClass::Fire_Coord(int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + COORDINATE coord = Center_Coord(); + + if (*this == VESSEL_CA) { + if (IsSecondShot) { + coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100); + } else { + coord = Coord_Move(coord, PrimaryFacing, 0x0100); + } + coord = Coord_Move(coord, DIR_N, 0x0030); + coord = Coord_Move(coord, Turret_Facing(), 0x0040); + return(coord); + } + + if (*this == VESSEL_PT) { + coord = Coord_Move(coord, PrimaryFacing, 0x0080); + coord = Coord_Move(coord, DIR_N, 0x0020); + coord = Coord_Move(coord, Turret_Facing(), 0x0010); + return(coord); + } + + return(DriveClass::Fire_Coord(which)); +} + + +/*********************************************************************************************** + * VesselClass::Init -- Initialize the vessel heap system. * + * * + * This routine is used to clear out the vessel heap. It is called whenever a scenario is * + * being initialized prior to scenario or saved game loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All vessel objects are invalid after this routine is called. * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Init(void) +{ + Vessels.Free_All(); +} + + +/*********************************************************************************************** + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel * + * * + * This routine is used by ships to determine what target they should go after. * + * * + * INPUT: threat -- The threat type that this ship should go after (as determined by the * + * team mission or general self defense principles). * + * * + * OUTPUT: Returns with the target that this ship should attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +TARGET VesselClass::Greatest_Threat(ThreatType threat) const +{ + if (*this == VESSEL_SS) { + threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA); + threat = threat | THREAT_BOATS; + + //BG: get subs to attack buildings also. + threat = threat | THREAT_BUILDINGS; + threat = threat | THREAT_FACTORIES; + } else { + if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) { + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +// threat = threat | THREAT_GROUND | THREAT_BOATS; + } + + // Cruisers can never hit infantry anyway, so take 'em out of the list + // of possible targets. + if (*this == VESSEL_CA) { + threat = (ThreatType) (threat & (~THREAT_INFANTRY)); + } + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + return(TARGET_NONE); + } +#endif + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * * + * This routine is called when the vessel is finished with what it is doing, but the next * + * action is not known. This routine will determine what is the appropriate course of * + * action for this vessel and then start it doing that. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Enter_Idle_Mode(bool ) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (Class->PrimaryWeapon == NULL) { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) { + order = MISSION_UNLOAD; + } else { + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * VesselClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a vessel receives a radio * + * message. Typical use of this is when a unit unloads from a lst * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 BWG : Created. * + *=============================================================================================*/ +RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) { + return(RADIO_ROGER); + } +#endif + /* + ** Before saying "Sure, come on board", make sure we're adjacent to + ** the shore. + */ + CELL cell; + Desired_Load_Dir(from, cell); + if(cell) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this != VESSEL_CARRIER) { +#endif + if (How_Many() == Class->Max_Passengers()) { + LST_Close_Door(); + } else { + DoorShutCountDown = TICKS_PER_SECOND; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + param = TARGET_NONE; + Transmit_Message(RADIO_HELLO, from); + Transmit_Message(RADIO_MOVE_HERE, param); + return(RADIO_ROGER); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + /* + ** + */ + if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating) { +// if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is full or needs + ** to rotate. + */ + if (!Is_Door_Open()) { + LST_Open_Door(); + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + if (Is_Door_Open()) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) { + TechnoClass::Receive_Message(from, message, param); + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + param = As_Target(); + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + Transmit_Message(RADIO_TETHER); + } + } + return(RADIO_ROGER); + } +#endif + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + +#if(0) + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; +#endif + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; +// } else { +// ObjectClass * obj = Map[cellnum].Cell_Occupier(); +// if (obj) obj->Scatter(Coord, true); + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * VesselClass::LST_Open_Door -- Opens a LST door. * + * * + * This routine will initiate opening of the doors on the LST. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Open_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + Open_Door(5, 6); + } +} + + +/*********************************************************************************************** + * VesselClass::LST_Close_Door -- Closes a LST door. * + * * + * This routine will initiate closing of the LST door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Close_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + Close_Door(5, 6); +} + + +/*********************************************************************************************** + * VesselClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Unload(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case VESSEL_TRANSPORT: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() > 0 && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + if (!How_Many()) { // don't break out if still carrying passengers + Assign_Mission(MISSION_GUARD); + } + } + break; + + case MANEUVERING: + if (!IsRotating) { + LST_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + + /* + ** Don't do anything if still in radio contact. + */ + if (In_Radio_Contact()) return(TICKS_PER_SECOND); + + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + passenger->Commence(); + Transmit_Message(RADIO_HELLO, passenger); + Transmit_Message(RADIO_TETHER, passenger); + if (passenger->What_Am_I() == RTTI_UNIT) { + ((UnitClass *)passenger)->IsToScatter = true; + } + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + + /* + ** Tell everyone around the transport to scatter. + */ + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CellClass * cellptr = &Map[Coord].Adjacent_Cell(face); + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + cellptr->Incoming(0, true); + } + } + +// Status = CLOSING_DOOR; + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close LST door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + LST_Close_Door(); + } + if (Is_Door_Closed()) { + if (IsALoaner) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + break; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * * + * This routine is called when a destination is to be assigned to this vessel. * + * * + * INPUT: target -- The destination to assign to this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) { + long param = TARGET_NONE; + Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport. + Transmit_Message(RADIO_OVER_OUT); + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + } + + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * * + * This will cause the vessel to run away from the battlefield. It searches for an escape * + * map edge according to the reinforcement edge specified in the house. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine is called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Retreat(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + PICK_RETREAT_POINT, + TRAVEL + }; + switch (Status) { + case PICK_RETREAT_POINT: + IsALoaner = true; + if (!Target_Legal(NavCom)) { +// CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed); + CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed); + if (Team.Is_Valid()) { + Team->Remove(this); + } + Assign_Destination(::As_Target(cell)); + } + Status = TRAVEL; + return(1); + + case TRAVEL: + if (!Target_Legal(NavCom)) { + Status = PICK_RETREAT_POINT; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * * + * Asking this question is part of the recloak process. If the answer is no, then * + * recloaking is postponed. This facilitates keeping submarines visible for longer than * + * they otherwise would be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this vessel recloak now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 BWG : Created. * + *=============================================================================================*/ +bool VesselClass::Is_Allowed_To_Recloak(void) const +{ + return(PulseCountDown == 0); +} + + +/*********************************************************************************************** + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * * + * This will read and create all vessels specified in the INI database. This routine is * + * called when the scenario starts. * + * * + * INPUT: ini -- Reference to the INI database to read the vessel data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: Vessels will be created and placed on the map by this function. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Read_INI(CCINIClass & ini) +{ + VesselClass * vessel; // Working vessel pointer. + HousesType inhouse; // Vessel house. + VesselType classid; // Vessel class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = VesselTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != VESSEL_NONE) { + + vessel = new VesselClass(classid, inhouse); + if (vessel != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + vessel->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + vessel->Trigger = tt; + } + } + + if (vessel->Unlimbo(coord, dir)) { + vessel->Strength = vessel->Class->MaxStrength * fixed(strength, 256); + if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength; +// vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength); + if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) { + vessel->Assign_Mission(mission); + vessel->Commence(); + } else { + vessel->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the vessel could not be unlimboed, then this is a catastrophic error + ** condition. Delete the vessel. + */ + delete vessel; + } + } + } + } + } +} + + +/*********************************************************************************************** + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * * + * This routine is used to add the vessel data (needed for scenario start) to the INI * + * database specified. If there was any preexisting vessel data in the database, it will * + * be cleared * + * * + * INPUT: ini -- Reference to the ini database to store the vessel data into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing vessel data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the vessel data out. + */ + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + vessel->House->Class->IniName, + vessel->Class->IniName, + vessel->Health_Ratio()*256, + Coord_Cell(vessel->Coord), + vessel->PrimaryFacing.Current(), + MissionClass::Mission_Name(vessel->Mission), + vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * * + * This routine is called when the vessel starts moving. It will reserve the destination * + * cell so that it won't be occupied by another vessel as this one is travelling. * + * * + * INPUT: headto -- The coordinate that will be headed to. * + * * + * OUTPUT: bool; Was the destination location successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Start_Driver(COORDINATE & headto) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(CELL cell) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) { + return(ACTION_MOVE); + } + + if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) { + return(ACTION_ATTACK); + } + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * * + * Any turret or body rotation for this vessel will be handled by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } + } + + IsRotating = false; + if (Class->IsTurretEquipped) { + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + IsRotating = SecondaryFacing.Is_Rotating(); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * * + * This routine will process firing logic for the vessel. It includes searching for targets * + * and performing any adjustments necessary to bring the target to bear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Combat_AI(void) +{ + if (Target_Legal(TarCom) && Is_Weapon_Equipped()) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + + switch (ok) { + case FIRE_OK: + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * * + * In addition to detecting the edge of world case, this routine will delete the vessel * + * if it occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vessel deleted by this routine? * + * * + * WARNINGS: Be sure to examine the return value and if true, abort any further processing * + * for this vessel since it has been deleted. This routine should be called once * + * per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Edge_Of_World_AI(void) +{ + if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * * + * When a vessel repairs, it does so 'by itself' and not under direct control of another * + * object. This self repair logic is processed here. Upon repair completion of money * + * exhuastion, the repair process will terminate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Repair_AI(void) +{ + if (IsSelfRepairing) { + if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) { + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsSelfRepairing = IsToSelfRepair = false; + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); + } + } + } + } +} + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +/*********************************************************************************************** + * VesselClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the ship. It will check * + * to see if we're an aircraft carrier, and if so, launch one of our * + * aircraft. If we're not an aircraft carrier, it lets the higher-level * + * Fire_At logic take over. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * VesselClass::Fire_At(TARGET target, int which) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == VESSEL_CARRIER) { + Arm = CarrierLaunchDelay; + FootClass * passenger = Detach_Object(); + if (passenger != NULL) { + ScenarioInit++; + passenger->Unlimbo(Center_Coord()); + ScenarioInit--; + passenger->Assign_Mission(MISSION_ATTACK); + passenger->Assign_Target(TarCom); + passenger->Commence(); +// If we've launched our last aircraft, discontinue attacking. + if (!How_Many()) Assign_Target(TARGET_NONE); + } + } else { + return DriveClass::Fire_At(target, which); + } + return (NULL); +} + +#endif diff --git a/CODE/VESSEL.H b/CODE/VESSEL.H new file mode 100644 index 0000000..5e3f1e0 --- /dev/null +++ b/CODE/VESSEL.H @@ -0,0 +1,150 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VESSEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VESSEL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : March 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VESSEL_H +#define VESSEL_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + +class VesselClass : public DriveClass +{ + public: + /* + ** This points to the controling static characteristic data associated with + ** this vessel. + */ + CCPtr Class; + + /* + ** Has this sea vessel been told to move to a shipyard? If so, then + ** when we get there, start the repair process. + */ + unsigned IsToSelfRepair:1; + + /* + ** Is this sea vessel parked next to a shipyard/subpen, and therefore + ** in the special self-repair mode? + */ + unsigned IsSelfRepairing:1; + + /* + ** If this is an LST, is it time to shut the door? + */ + CDTimerClass DoorShutCountDown; + + /* + ** If this is a sub, has the sonar pulse worn off, such that we can + ** re-submerge? + */ + CDTimerClass PulseCountDown; + + VesselClass(VesselType classid, HousesType house); + VesselClass(NoInitClass const & x) : DriveClass(x), Class(x), SecondaryFacing(x) {}; + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + operator VesselType(void) const {return Class->Type;}; + + static void Init(void); + + virtual ~VesselClass(void); + virtual ObjectTypeClass const & Class_Of(void) const; + + virtual MZoneType Zone_Check_Type(void) const {return(MZONE_WATER);} + int Shape_Number(void) const; + void Rotation_AI(void); + void Combat_AI(void); + bool Edge_Of_World_AI(void); + void Repair_AI(void); + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + virtual bool Start_Driver(COORDINATE & headto); + virtual int Mission_Retreat(void); + virtual int Mission_Unload(void); + void LST_Open_Door(void); + void LST_Close_Door(void); + virtual COORDINATE Fire_Coord(int which) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Assign_Destination(TARGET target); + + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, int forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + virtual void Enter_Idle_Mode(bool initial=false); + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual bool Is_Allowed_To_Recloak(void) const; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + virtual BulletClass * Fire_At(TARGET target, int which=0); +#endif + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "SHIPS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + protected: + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; +}; + +#endif diff --git a/CODE/VISUDLG.CPP b/CODE/VISUDLG.CPP new file mode 100644 index 0000000..6a5dff1 --- /dev/null +++ b/CODE/VISUDLG.CPP @@ -0,0 +1,416 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VISUDLG.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VISUDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : June 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VisualControlsClass::Process -- Process the visual control dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "visudlg.h" + + +/*********************************************************************************************** + * VisualControlsClass::Process -- Process the visual control dialog box. * + * * + * This routine displays and processes the visual controls dialog box. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +void VisualControlsClass::Process(void) +{ + static int _titles[4] = { + TXT_BRIGHTNESS, + TXT_COLOR, + TXT_CONTRAST, + TXT_TINT + }; + + enum { + NUM_OF_BUTTONS = 6, + }; + + /* + ** Make em resolution independent + */ + int option_width = OPTION_WIDTH * RESFACTOR; // Width of dialog box. + int option_height = OPTION_HEIGHT * RESFACTOR; // Height of dialog box. + int option_x = OPTION_X * RESFACTOR; + int option_y = OPTION_Y * RESFACTOR; + int text_x = TEXT_X * RESFACTOR; + int text_y = TEXT_Y * RESFACTOR; + int slider_x = SLIDER_X * RESFACTOR; + int slider_y = SLIDER_Y * RESFACTOR; + int slider_width = SLIDER_WIDTH * RESFACTOR; // Width of each control slider. + int slider_height = SLIDER_HEIGHT * RESFACTOR; // Height of each control slider. + int slider_y_spacing = SLIDER_Y_SPACING * RESFACTOR; // Vertical spacing between sliders. + int button_x = BUTTON_X * RESFACTOR; // Options button x pos + int button_y = BUTTON_Y * RESFACTOR; // Options button y pos + + + /* + ** Variables. + */ + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + SliderClass * buttonsliders[NUM_OF_BUTTONS]; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, button_y, 60*RESFACTOR); +// TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, 0, button_y); + TextButtonClass resetbtn(BUTTON_RESET, TXT_RESET_MENU, TPF_BUTTON, 0, button_y, 80*RESFACTOR); + + /* + ** Centers options button. + */ + optionsbtn.X = option_x + (option_width - optionsbtn.Width - (17 * RESFACTOR)); + resetbtn.X = option_x + (17 * RESFACTOR); + + resetbtn.Add_Tail(optionsbtn); + + /* + ** Brightness (value) control. + */ + SliderClass brightness(BUTTON_BRIGHTNESS, slider_x, slider_y + (slider_y_spacing*0), slider_width, slider_height, true); + brightness.Set_Thumb_Size(20 * RESFACTOR); + brightness.Set_Value(Options.Get_Brightness() * 256); + brightness.Add_Tail(optionsbtn); + + /* + ** Color (saturation) control. + */ + SliderClass color(BUTTON_COLOR, slider_x, slider_y + (slider_y_spacing*1), slider_width, slider_height, true); + color.Set_Thumb_Size(20 * RESFACTOR); + color.Set_Value(Options.Get_Saturation() * 256); + color.Add_Tail(optionsbtn); + + /* + ** Contrast control. + */ + SliderClass contrast(BUTTON_CONTRAST, slider_x, slider_y + (slider_y_spacing*2), slider_width, slider_height, true); + contrast.Set_Thumb_Size(20 * RESFACTOR); + contrast.Set_Value(Options.Get_Contrast() * 256); + contrast.Add_Tail(optionsbtn); + + /* + ** Tint (hue) control. + */ + SliderClass tint(BUTTON_TINT, slider_x, slider_y + (slider_y_spacing*3), slider_width, slider_height, true); + tint.Set_Thumb_Size(20 * RESFACTOR); + tint.Set_Value(Options.Get_Tint() * 256); + tint.Add_Tail(optionsbtn); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(optionsbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), + GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(optionsbtn); + + curbutton = 0; + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = NULL; + buttons[3] = NULL; + buttons[4] = &resetbtn; + buttons[5] = &optionsbtn; + + buttonsliders[0] = &brightness; + buttonsliders[1] = &color; + buttonsliders[2] = &contrast; + buttonsliders[3] = ∭ + buttonsliders[4] = NULL; + buttonsliders[5] = NULL; + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + bool partial = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_VISUAL_CONTROLS, option_x, option_y, option_width); + Show_Mouse(); + display = false; + partial = true; + } + + /* + ** If just the buttons and captions need to be redrawn, then do so now. + */ + if (partial) { + Hide_Mouse(); + + /* + ** Draw the titles. + */ + for (int i = 0; i < (sizeof(_titles)/sizeof(_titles[0])); i++) { + Fancy_Text_Print(_titles[i], slider_x - (8 * RESFACTOR), text_y + (i*slider_y_spacing), + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_RIGHT| ((curbutton == i) ? TPF_BRIGHT_COLOR : TPF_TEXT)); + } + optionsbtn.Draw_All(); + Show_Mouse(); + partial = false; + } + + /* + ** Get and process player input. + */ + KeyNumType input = optionsbtn.Input(); + switch (input) { + case (BUTTON_BRIGHTNESS | KN_BUTTON): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR | KN_BUTTON): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST | KN_BUTTON): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT | KN_BUTTON): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + + case (BUTTON_RESET | KN_BUTTON): + selection = BUTTON_RESET; + pressed = true; + break; + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + selection = BUTTON_OPTIONS; + pressed = true; + break; + + case (KN_LEFT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(1); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_OPTIONS - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(0); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (BUTTON_OPTIONS - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_UP): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton == (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton--; + } + + if (curbutton < 0) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_DOWN): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = 0; + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_BRIGHTNESS; + pressed = true; + break; + + default: + break; + } + + + if (pressed) { + switch (selection) { + case (BUTTON_RESET): + brightness.Set_Value(128); + contrast.Set_Value(128); + color.Set_Value(128); + tint.Set_Value(128); + + Options.Set_Brightness(fixed::_1_2); + Options.Set_Contrast(fixed::_1_2); + Options.Set_Saturation(fixed::_1_2); + Options.Set_Tint(fixed::_1_2); + break; + + case (BUTTON_OPTIONS): + process = false; + break; + } + + pressed = false; + } + } +} + diff --git a/CODE/VISUDLG.H b/CODE/VISUDLG.H new file mode 100644 index 0000000..f646130 --- /dev/null +++ b/CODE/VISUDLG.H @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/VISUDLG.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : VISUDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + *---------------------------------------------------------------------------------------------*/ + +#ifndef VISUDLG_H +#define VISUDLG_H + +#include "gadget.h" + +class VisualControlsClass +{ + private: + + enum VisualControlEnums { + BUTTON_BRIGHTNESS=1, + BUTTON_COLOR, + BUTTON_CONTRAST, + BUTTON_TINT, + BUTTON_RESET, + BUTTON_OPTIONS, // Button number for "Options menu" + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2)), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+28, // Title's x pos + TEXT_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_X=OPTION_X+105, // Slider's x pos + SLIDER_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_WIDTH=70, // Width of each control slider. + SLIDER_HEIGHT=5, // Height of each control slider. + SLIDER_Y_SPACING=11, // Vertical spacing between sliders. + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + }; + + public: + + VisualControlsClass(void) {}; + void Process(void); +}; + +#endif diff --git a/CODE/VORTEX.CPP b/CODE/VORTEX.CPP new file mode 100644 index 0000000..8840e2e --- /dev/null +++ b/CODE/VORTEX.CPP @@ -0,0 +1,1258 @@ +/* +** 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 . +*/ + +/************************************************************************************* + ** 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 - Red Alert * + * * + * File Name : VORTEX.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 12th, 1996 * + * * + * Last Update : September 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Circley vortexy swirly type thing. (Really just a pixel & color remap). * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * CVC::ChronalVortexClass -- vortex class constructor * + * CVC::~ChronalVortexClass -- vortex class destructor * + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * CVC::Disappear -- Makes the chronal vortex go away. * + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * CVC::Show -- Makes a hidden vortex visible again. * + * CVC::Stop -- Stops the vortex without going through the hide animation * + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * CVC::Save -- Saves the vortex class data to a savegame file * + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * CVC::Movement -- Movement AI for the vortex. * + * CVC::Set_Target -- Make the vortex zap a particular object. * + * CVC::Attack -- look for objects to attack * + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * CVC::Coordinate_Remap -- Draws the vortex * + * CVC::Render -- Renders the vortex at its current position. * + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * CVC::Build_Fading_Table -- Builds a fading color lookup table. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + + +/* +** Instance of chronal vortex class. This must be the only instance. +*/ +ChronalVortexClass ChronalVortex; + + + +/*********************************************************************************************** + * CVC::ChronalVortexClass -- vortex class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::ChronalVortexClass (void) +{ + Active = 0; + Theater = THEATER_NONE; + Speed = 10; + Range = 10; + Damage = 200; + RenderBuffer = NULL; //We havn't allocated it yet. It will be allocated as needed. +} + + + +/*********************************************************************************************** + * CVC::~ChronalVortexClass -- vortex class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::~ChronalVortexClass (void) +{ + if (RenderBuffer) delete RenderBuffer; + Active = 0; +} + + + +/*********************************************************************************************** + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * * + * * + * * + * INPUT: Coordinate that vortex should appear at. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This member does nothing if the vortex is already active * + * * + * HISTORY: * + * 8/29/96 4:27PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Appear (COORDINATE coordinate) +{ + if (Active) return; + + /* + ** Adjust the given coordinate so the vortex appears in a central position + */ + int x = Lepton_To_Pixel(Coord_X(coordinate)); + int y = Lepton_To_Pixel(Coord_Y(coordinate)); + + x -= 32; + y -= 32; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + Position = XY_Coord (lx, ly); + + /* + ** Initialise the vortex variables. + */ + AnimateDir = 1; + AnimateFrame = 0; + State = STATE_GROW; + Active = true; + Animate = 0; + StartShutdown = false; + LastAttackFrame= Frame; + TargetObject = TARGET_NONE; + ZapFrame = 0; + Hidden = false; + StartHiding = false; + XDir = 0; + YDir = 0; + + /* + ** Vortex starts off in a random direction. + */ + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + +} + + +/*********************************************************************************************** + * CVC::Disappear -- Makes the chronal vortex go away. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Disappear (void) +{ + if (Hidden) { + Active = false; + } else { + StartShutdown = true; + } +} + + + +/*********************************************************************************************** + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This doesnt deactivate the vortex. Use Disappear to get rid of it permanently. * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Hide (void) +{ + if (!StartShutdown) { + StartHiding = true; + } +} + + +/*********************************************************************************************** + * CVC::Show -- Makes a hidden vortex visible again. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:31PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Show (void) +{ + /* + ** Dont do anything if vortx is dying. + */ + if (!StartShutdown) { + + /* + ** If the vortex is hidden then show it again. + */ + if (Hidden) { + Hidden = false; + StartHiding = false; + AnimateFrame = 0; + State = STATE_GROW; + XDir = 0; + YDir = 0; + } else { + /* + ** If the vortex is in the process of hiding then reverse it. + */ + StartHiding = false; + if (State == STATE_SHRINK) { + State = STATE_GROW; + AnimateFrame = VORTEX_FRAMES - AnimateFrame; + } + } + } +} + + + +/*********************************************************************************************** + * CVC::Stop -- Stops the vortex without going through the hide animation * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Stop(void) +{ + if (Active) Active = false; +} + + + + +/*********************************************************************************************** + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * * + * * + * * + * INPUT: ptr to file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Load(Straw &file) +{ + /* + ** Delete the render buffer as we are going to lose the pointer anyway. + ** It will be re-allocated when needed. + */ + if (RenderBuffer) delete RenderBuffer; + + file.Get (this, sizeof (ChronalVortexClass)); +} + + + +/*********************************************************************************************** + * CVC::Save -- Saves the vortex class data to a savegame file * + * * + * * + * * + * INPUT: file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:33PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Save(Pipe &file) +{ + GraphicBufferClass *save_ptr = NULL; + + if (RenderBuffer){ + /* + ** Save the ptr to the render buffer so we can null it for the save + */ + save_ptr = RenderBuffer; + RenderBuffer = NULL; + } + + file.Put (this, sizeof (ChronalVortexClass)); + + /* + ** Restore the render buffer ptr + */ + if (save_ptr){ + RenderBuffer = save_ptr; + } +} + + + +/*********************************************************************************************** + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:34PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::AI(void) +{ + + int chance; + + /* + ** No AI if vortex isnt active + */ + if (Active) { + + /* + ** Do the movement AI + */ + Movement(); + + /* + ** Do the attack AI + */ + Zap_Target(); + + + if (Hidden && (Frame - HiddenFrame > 50) ) { + /* + ** Vortex is hidden. Chance of it showing itself increases the longer its stays hidden. + */ + chance = Random_Pick(0,2000); + if (chance <= Frame - HiddenFrame) { + Show(); + } + } else { + + if (Animate == 0) { + + /* + ** Its time to animate the vortex. + */ + AnimateFrame += AnimateDir; + + if (AnimateFrame > VORTEX_FRAMES) { + /* + ** State changes can only occur on final animation frames. + */ + AnimateFrame = 1; + + if (StartShutdown) { + + /* + ** Vortex is in the process of dying. + */ + if (State == STATE_SHRINK) { + Set_Redraw(); + Active = false; + AnimateFrame = 0; + } else { + Attack(); + State = STATE_SHRINK; + } + } else { + + if (StartHiding) { + /* + ** Vortex wants to hide. + */ + if (State == STATE_SHRINK) { + /* + ** Hide the vortex now. + */ + Set_Redraw(); + StartHiding = false; + Hidden = true; + HiddenFrame = Frame; + if (Random_Pick(0,4) == 4) { + Disappear(); + } + } else { + /* + ** Start hiding the vortex. + */ + Attack(); + State = STATE_SHRINK; + } + } else { + + Attack(); + if (State == STATE_GROW) { + State = STATE_ROTATE; + } else { + //Attack(); + } + } + } + } else { + if (AnimateFrame == VORTEX_FRAMES / 2) Attack(); + } + } + Animate++; + Animate &= 1; + } + } +} + + + + + +/*********************************************************************************************** + * CVC::Movement -- Movement AI for the vortex. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:39PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Movement (void) +{ + bool newpick = true; + + /* + ** Update the vortex position by applying the x and y direction variables + */ + LEPTON x = Coord_X(Position); + LEPTON y = Coord_Y(Position); + + x += XDir; + y += YDir; + + Position = XY_Coord (x,y); + + /* + ** Reverse the direction of the vortex if its drifting off the map. + */ + if (x > CELL_LEPTON_W *(Map.MapCellX + Map.MapCellWidth -4)) { + newpick = false; + if (DesiredXDir >0 ) DesiredXDir = -DesiredXDir; + } + + if (y > CELL_LEPTON_H *(Map.MapCellY + Map.MapCellHeight -4)) { + newpick = false; + if (DesiredYDir >0 ) DesiredYDir = -DesiredYDir; + } + + if (x < CELL_LEPTON_W *Map.MapCellX + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredXDir <0 ) DesiredXDir = -DesiredXDir; + } + + if (y < CELL_LEPTON_H *Map.MapCellY + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredYDir <0 ) DesiredYDir = -DesiredYDir; + } + + /* + ** Vortex direction tends towards the desired direction unless the vortex is shutting down or + ** appearing in which case the direction tends towards 0. + */ + if (State == STATE_ROTATE || Hidden) { + if (XDir < DesiredXDir) XDir ++; + if (XDir > DesiredXDir) XDir --; + if (YDir < DesiredYDir) YDir ++; + if (YDir > DesiredYDir) YDir --; + } else { + if (XDir > 0) XDir -= Speed/8; + if (XDir < 0) XDir += Speed/8; + if (YDir > 0) YDir -= Speed/8; + if (YDir < 0) YDir += Speed/8; + } + + /* + ** Occasionally change the direction of the vortex. + */ + if (newpick && Random_Pick (0, 100) == 100) { + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + } +} + + + +/*********************************************************************************************** + * CVC::Set_Target -- Make the vortex zap a particular object. * + * * + * * + * * + * INPUT: ptr to object to zap * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Target (ObjectClass *target) +{ + if (Active){ + ZapFrame = 0; + TargetObject = TARGET_NONE; + if (target != NULL) TargetObject = target->As_Target(); + LastAttackFrame = Frame; + TargetDistance = (target != NULL) ? Distance (target->Center_Coord(), Position) : 0; + } +} + + +/*********************************************************************************************** + * CVC::Attack -- look for objects to attack * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Attack(void) +{ + int distance; +// if(TargetObject) return; +// if(!TargetObject) return; + /* + ** Calculate the position of the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Scan through the ground layer objects and see who we should attack + */ + + /* + ** First scan - find any object directly above the vortex. + */ + for (unsigned i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj->Is_Techno() && obj->Strength > 0 ) { + + distance = Distance (obj->Center_Coord(), here); + + if (distance <= CELL_LEPTON_W*2) { + Set_Target (obj); + break; + } + } + } + + /* + ** If we found something to attack then just return + */ + if (!Target_Legal(TargetObject)) return; + + + /* + ** Scan through all ground level objects. + ** + ** Objects within range have a chance of being selected based on their distance from the vortex. + */ + + int chance = Random_Pick (0, 1000); + if (chance > Frame - LastAttackFrame) return; + + for (i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj && obj->Is_Techno() ) { + + distance = Distance (obj->Center_Coord(), Position); + + if (distance < CELL_LEPTON_W * Range) { + chance = Random_Pick (0, distance); + if (chance < CELL_LEPTON_W) { + Set_Target (obj); + break; + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:45PM ST : Created * + *=============================================================================================*/ +#define ZAP_COUNT 1 +void ChronalVortexClass::Zap_Target (void) +{ + if (!Hidden && Target_Legal(TargetObject) && ZapFrame < ZAP_COUNT) { + + /* + ** Get the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Create a temporary techno object se we can access the lightning ability of the tesla. + */ + TechnoClass *temptech = new BuildingClass (STRUCT_TESLA, HOUSE_GOOD); + if (temptech != NULL) { + temptech->Coord = here; + ObjectClass * obj = As_Object(TargetObject); + TARGET target = As_Target (obj->Center_Coord()); + Sound_Effect(VOC_TESLA_ZAP, obj->Center_Coord()); + temptech->Electric_Zap (target, 0, here, LightningRemap); + delete temptech; + + /* + ** Flag the whole map to redraw to cover the lightning. + */ + Map.Flag_To_Redraw(true); + + /* + ** Zap the target 3 times but only do damage on the last frame. + */ + ZapFrame++; + + if (ZapFrame == ZAP_COUNT) { + ZapFrame = 0; + int damage = Damage; + obj->Take_Damage(damage, TargetDistance, WARHEAD_TESLA, NULL, 1); + TargetObject = TARGET_NONE; + } + } + + /* + ** Vortex might pretend to go away after zapping the target. + */ + if (Random_Pick (0,2) == 2) Hide(); + } +} + + + + + +/*********************************************************************************************** + * CVC::Coordinate_Remap -- Draws the vortex * + * * + * * + * * + * INPUT: ptr to view port to draw the vortex into * + * x offset * + * y offset * + * width of vortex * + * height of vortex * + * ptr to shading remap tables * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:48PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table) +{ + unsigned char getx,gety, remap_color, pixel_color; + + + BufferClass destbuf (width * height); + + unsigned char *destptr = (unsigned char*) destbuf.Get_Buffer(); + + int destx = x; + int desty = y; + + int dest_width = width; + int dest_height = height; + + if (inbuffer->Lock()) { + + /* + ** Get a pointer to the section of buffer we are going to work on. + */ + unsigned char *bufptr = (unsigned char *) inbuffer->Get_Offset() + + destx +#ifdef WIN32 + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd() + inbuffer->Get_Pitch()); +#else + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd()); +#endif + + +#ifdef WIN32 + int modulo = inbuffer->Get_Pitch() + inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#else + int modulo = inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#endif + + + for (int yy = desty ; yy < desty+dest_height ; yy++) { + for (int xx = destx ; xx < destx+dest_width ; xx++) { + /* + ** Get the coordinates of the pixel to draw + */ + getx = *(remap_table++); + gety = *(remap_table++); + remap_color = *(remap_table++); + + pixel_color = * (bufptr + getx + (gety * modulo) ); + + *(destptr++) = VortexRemapTables [remap_color] [pixel_color]; + } + + remap_table += 3*(width - dest_width); + destptr += width - dest_width; + + } + + destbuf.To_Page(destx, desty, dest_width, dest_height, *inbuffer); + + + inbuffer->Unlock(); + } +} + + + +/*********************************************************************************************** + * CVC::Render -- Renders the vortex at its current position. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:49PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Render (void) +{ + if (Active && !Hidden) { + char fname [80]; + + int frame; + + /* + ** Calculate which coordinate lookup table we should be using for this frame. + */ + switch (State) { + case STATE_GROW: + frame = 0; + break; + + case STATE_ROTATE: + frame = VORTEX_FRAMES; + break; + + case STATE_SHRINK: + frame = VORTEX_FRAMES*2; + break; + } + + frame += AnimateFrame; + + sprintf (fname, "HOLE%04d.lut", frame); + + void const *lut_ptr = MFCD::Retrieve(fname); + if (lut_ptr) { + + /* + ** Build a representation of the area of the screen where the vortex will be + ** in an off-screen buffer. + ** This is necessary for clipping as we cant remap pixels from off screen if we build + ** the image from the hidpage. + */ + if (!RenderBuffer) { + RenderBuffer = new GraphicBufferClass(CELL_PIXEL_W * 4, CELL_PIXEL_H * 4, (void*)NULL); + } + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + CellClass *cellptr; + CELL cell; + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + + +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(RenderBuffer); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(RenderBuffer); +#endif + + /* + ** Temporarily modify the tactical window so it works with our offscreen buffer + */ + int wx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int wy = WindowList[WINDOW_TACTICAL][WINDOWY]; + int ww = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int wh = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + WindowList[WINDOW_TACTICAL][WINDOWX] = 0; + WindowList[WINDOW_TACTICAL][WINDOWY] = 0; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = RenderBuffer->Get_Width(); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = RenderBuffer->Get_Height(); + + + /* + ** Loop through all the cells that the vortex overlaps and render the template, smudge + ** and overlay for each cell. + */ + for (int y = 0 ; y<4 ; y++) { + for (int x = 0 ; x<4 ; x++) { + + cell = XY_Cell (xc+x,yc+y); + if (cell != -1) { + + //cellptr = &Map[ Coord_Whole (Cell_Coord(cell)) ]; + cellptr = &Map [cell]; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != TEMPLATE_CLEAR1 && cellptr->TType != 255) { + ttype = &TemplateTypeClass::As_Reference(cellptr->TType); + icon = cellptr->TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = cellptr->Clear_Icon(); + } + + /* + ** Draw the template + */ + if (ttype->Get_Image_Data()) { + RenderBuffer->Draw_Stamp(ttype->Get_Image_Data(), icon, x*CELL_PIXEL_W, y*CELL_PIXEL_H, NULL, WINDOW_MAIN); + } + + /* + ** Draw any smudge. + */ + if (cellptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(cellptr->Smudge).Draw_It(x*CELL_PIXEL_W, y*CELL_PIXEL_H, cellptr->SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + CC_Draw_Shape(otype.Get_Image_Data(), + cellptr->OverlayData, + x*CELL_PIXEL_W + (CELL_PIXEL_W >> 1), + y*CELL_PIXEL_H + (CELL_PIXEL_H >> 1), + WINDOW_TACTICAL, + SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, + DisplayClass::UnitShadow); + + IsTheaterShape = false; + } + } + } + } + + + Set_Logic_Page(oldpage); + + /* + ** Restore the tactical window to its correct value + */ + WindowList[WINDOW_TACTICAL][WINDOWX] = wx; + WindowList[WINDOW_TACTICAL][WINDOWY] = wy; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = ww; + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = wh; + + /* + ** Render the vortex over the cells we just rendered to our buffer + */ + Coordinate_Remap (RenderBuffer, Lepton_To_Pixel(Coord_X(Coord_Fraction(Position))), + Lepton_To_Pixel(Coord_Y(Coord_Fraction(Position))), + 64, + 64, + (unsigned char*) lut_ptr); + + + /* + ** Calculate the pixel position of our fresh block of cells on the tactical map so + ** we can blit it to the hid page. + */ + COORDINATE render_pos = XY_Coord(xc * CELL_LEPTON_W, yc * CELL_LEPTON_H); //Coord_Whole(Position); + + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(Map.TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(render_pos))); + xoff -= xtac; + + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(Map.TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(render_pos))); + yoff -= ytac; + + + /* + ** Create a view port to blit to + */ + GraphicViewPortClass target (LogicPage->Get_Graphic_Buffer(), + 0, + 8*RESFACTOR + LogicPage->Get_YPos(), + Lepton_To_Pixel (Map.TacLeptonWidth), + Lepton_To_Pixel (Map.TacLeptonHeight)); + + + /* + ** Do some clipping since the library clipping gets it wrong. + */ + int diff; + + int source_x = 0; + int source_y = 0; + int source_width = CELL_PIXEL_W*4; + int source_height = CELL_PIXEL_H*4; + + int dest_x = Lepton_To_Pixel (xoff); + int dest_y = Lepton_To_Pixel (yoff); + int dest_width = source_width; + int dest_height = source_height; + + if (dest_x < 0) { + source_width += dest_x; + dest_width += dest_x; + source_x -= dest_x; + dest_x = 0; + } + + if (dest_y <0) { + source_height += dest_y; + dest_height += dest_y; + source_y -= dest_y; + dest_y = 0; + } + + if (dest_x + dest_width > target.Get_Width() ) { + diff = dest_x + dest_width - target.Get_Width(); + dest_width -= diff; + source_width -= diff; + } + + if (dest_y + dest_height > target.Get_Height() ) { + diff = dest_y + dest_height - target.Get_Height(); + dest_height -= diff; + source_height -= diff; + } + + + /* + ** Blit our freshly draw cells and vortex into their correct position on the hidpage + */ + if (dest_width > 0 && dest_height > 0) { + RenderBuffer->Blit (target, source_x, source_y, dest_x, dest_y, dest_width, dest_height, false); + } + + } + } +} + + + +/*********************************************************************************************** + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:50PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Redraw(void) +{ + + if (Active) { + + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + + CELL cell; + + for (int y = MAX(0,yc - 1) ; y< yc+4 ; y++) { + for (int x = MAX(0, xc-1) ; x< xc + 4 ; x++) { + cell = XY_Cell (x,y); + if (cell != -1) { + Map[cell].Redraw_Objects(); + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * * + * * + * * + * INPUT: Theater * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:51PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Setup_Remap_Tables (TheaterType theater) +{ + /* + ** The names of the remap files for each theater + */ + static char _remaps[3][13] ={ + "TEMP_VTX.PAL", + "SNOW_VTX.PAL", + "INTR_VTX.PAL" + }; + + int i; + + /* + ** If the theater has changed then load the remap tables from disk if they exist or create + ** them if they dont. + */ + if (theater != Theater) { + + Theater = theater; + + CCFileClass file (_remaps[(int)Theater]); + + if (file.Is_Available()) { + file.Read (VortexRemapTables, MAX_REMAP_SHADES*256); + } else { + + for (i=0 ; i= CYCLE_COLOR_START && index < (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) || + index == CC_PULSE_COLOR || + index == CC_EMBER_COLOR) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. + */ + int best = -1; + int bvalue = 0; + for (int id = 0; id < PaletteClass::COLOR_COUNT; id++) { + + if (id != 0 && + (id < CYCLE_COLOR_START || id >= (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) && + id != CC_PULSE_COLOR && + id != CC_EMBER_COLOR) { + + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + } + *ptr++ = best; + } + } + } +} + + +void ChronalVortexClass::Detach(TARGET target) +{ + if (Target_Legal(target) && target == TargetObject) { + Set_Target(NULL); + } +} diff --git a/CODE/VORTEX.H b/CODE/VORTEX.H new file mode 100644 index 0000000..fcd95b6 --- /dev/null +++ b/CODE/VORTEX.H @@ -0,0 +1,272 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 - Red Alert * + * * + * File Name : VORTEX.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 8/12/96 * + * * + * Last Update : August 29th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Definition of ChronalVortexClass. The Chronal vortex sometimes appears when the * + * chronosphere is used. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VORTEX_H +#define VORTEX_H + + +#define MAX_REMAP_SHADES 16 //Number of lookup tables required for vortex shading. +#define VORTEX_FRAMES 16 //Number of frames in one complete rotation of the vortex. + + + +class ChronalVortexClass { + + public: + + /* + ** Constructor and destructor. + */ + ChronalVortexClass(void); + ~ChronalVortexClass(void); + + void Detach(TARGET target); + + /* + ** Makes the vortex appear at the specified coordinate. + */ + void Appear (COORDINATE coordinate); + + /* + ** Makes the vortex go away. + */ + void Disappear (void); + + /* + ** Call this every frame. + */ + void AI (void); + + /* + ** Render the vortex + */ + void Render (void); + + /* + ** Flags cells under the vortex to be redrawn + */ + void Set_Redraw (void); + + /* + ** Call whenever the theater changes to recalculate the shading lookup tables + */ + void Setup_Remap_Tables (TheaterType theater); + + /* + ** Functions to load and save the vortex. + */ + void Load(Straw &file); + void Save(Pipe &file); + + /* + ** Returns true of vortex is currently active. + */ + bool Is_Active(void) {return (Active);}; + + /* + ** Makes the vortex attack the specified target. Target must be in range of the vortex. + */ + void Set_Target (ObjectClass *target); + + /* + ** Disables the vortex. + */ + void Stop(void); + + /* + ** Members to allow read access to private data + */ + int Get_Range (void) {return (Range);}; + int Get_Speed (void) {return (Speed);}; + int Get_Damage (void) {return (Damage);}; + + /* + ** Members to allow write access to private data. + */ + void Set_Range (int range) {Range = range;}; + void Set_Speed (int speed) {Speed = speed;}; + void Set_Damage (int damage) {Damage = damage;}; + + /* + ** Possible states the vortex can be in. + */ + typedef enum AnimStateType{ + STATE_GROW, //Vortex has just appeared and is growing larger + STATE_ROTATE, //Vortex is rotating + STATE_SHRINK //Vortex is shrinking and about to disappear + }AnimStateType; + + private: + + /* + ** Members for setting up the lookup tables. + */ + void Build_Fading_Table (PaletteClass const & palette, void * dest, int color, int frac); + void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); + + /* + ** Misc internal functions + */ + void Attack(void); + void Zap_Target(void); + void Movement(void); + void Hide(void); + void Show(void); + + /* + ** Position of the top left of the vortex + */ + COORDINATE Position; + + /* + ** Direction of rotation + */ + int AnimateDir; + + /* + ** Current frame of animation + */ + int AnimateFrame; + + /* + ** Animation flag. When 0 vortex will animate 1 frame. + */ + int Animate; + + /* + ** State of vortex. See ENUM for info. + */ + AnimStateType State; + + /* + ** Color lookup tables for shading on vortex. + */ + unsigned char VortexRemapTables [MAX_REMAP_SHADES][256]; + + /* + ** Color lookup table to make the blue lightning orange. + */ + unsigned char LightningRemap[256]; + + /* + ** Is vortex currently active? + */ + int Active : 1; + + /* + ** Is the vortex winding down? + */ + int StartShutdown : 1; + + /* + ** Is the vortex about to hide from view? + */ + int StartHiding : 1; + + /* + ** Is the vortex active but hidden? + */ + int Hidden : 1; + + /* + ** Theater that lookup table is good for. + */ + TheaterType Theater; + + /* + ** Last frame that vortex attacked on + */ + int LastAttackFrame; + + /* + ** How many times lightning has zapped on this attack + */ + int ZapFrame; + + /* + ** Ptr to object that the vortex is zapping + */ + TARGET TargetObject; +// ObjectClass *TargetObject; + + /* + ** Distance to the target object + */ + int TargetDistance; + + /* + ** Game frame that vortex hid on. + */ + int HiddenFrame; + + /* + ** Direction vortex is going in. + */ + int XDir; + int YDir; + + /* + ** Direction vortex should be going in + */ + int DesiredXDir; + int DesiredYDir; + + /* + ** Range in cells of the vortex lightning + */ + int Range; + + /* + ** Max speed in leptons per frame of the vortex. + */ + int Speed; + + /* + ** Damge of vortex lightning zap. + */ + int Damage; + + /* + ** Offscreen buffer to render vortex into. This is needed so we can handle clipping. + */ + GraphicBufferClass *RenderBuffer; +}; + + +#endif diff --git a/CODE/W95TRACE.CPP b/CODE/W95TRACE.CPP new file mode 100644 index 0000000..3560300 --- /dev/null +++ b/CODE/W95TRACE.CPP @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + + +/* + Implementation of Win95 tracing facility to mimic that of NT +*/ + +#include +#include +#include +#include +#include "w95trace.h" + + +void OutputDebugStringW95( LPCTSTR /*lpOutputString*/, ...) +{ +#if 0 + + HANDLE heventDBWIN; /* DBWIN32 synchronization object */ + HANDLE heventData; /* data passing synch object */ + HANDLE hSharedFile; /* memory mapped file shared data */ + LPSTR lpszSharedMem; + char achBuffer[500]; + + /* create the output buffer */ + va_list args; + va_start(args, lpOutputString); + vsprintf(achBuffer, lpOutputString, args); + va_end(args); + + achBuffer[499] = 0; // Null-terminate here, just in case vsprintf didn't do it because lpOutputString was too long. + + /* + Do a regular OutputDebugString so that the output is + still seen in the debugger window if it exists. + + This ifdef is necessary to avoid infinite recursion + from the inclusion of W95TRACE.H + */ +#ifdef _UNICODE + ::OutputDebugStringW(achBuffer); +#else + ::OutputDebugStringA(achBuffer); +#endif + + // added by ajw + //FILE* pFile = fopen( "debugout.wri", "a" ); + FILE* pFile = fopen( "wolapi.out", "a" ); + fprintf( pFile, achBuffer ); + fclose( pFile ); + +// /* bail if it's not Win95 */ +// { +// OSVERSIONINFO VerInfo; +// VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); +// GetVersionEx(&VerInfo); +// if ( VerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS ) +// return; +// } + + /* make sure DBWIN is open and waiting */ + heventDBWIN = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_BUFFER_READY"); + if ( !heventDBWIN ) + { + //MessageBox(NULL, "DBWIN_BUFFER_READY nonexistent", NULL, MB_OK); + return; + } + + /* get a handle to the data synch object */ + heventData = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_DATA_READY"); + if ( !heventData ) + { + // MessageBox(NULL, "DBWIN_DATA_READY nonexistent", NULL, MB_OK); + CloseHandle(heventDBWIN); + return; + } + + hSharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, "DBWIN_BUFFER"); + if (!hSharedFile) + { + //MessageBox(NULL, "DebugTrace: Unable to create file mapping object DBWIN_BUFFER", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + lpszSharedMem = (LPSTR)MapViewOfFile(hSharedFile, FILE_MAP_WRITE, 0, 0, 512); + if (!lpszSharedMem) + { + //MessageBox(NULL, "DebugTrace: Unable to map shared memory", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + /* wait for buffer event */ + WaitForSingleObject(heventDBWIN, INFINITE); + + /* write it to the shared memory */ + *((LPDWORD)lpszSharedMem) = getpid(); + wsprintf(lpszSharedMem + sizeof(DWORD), "%s", achBuffer); + + /* signal data ready event */ + SetEvent(heventData); + + /* clean up handles */ + CloseHandle(hSharedFile); + CloseHandle(heventData); + CloseHandle(heventDBWIN); + + return; +#endif +} diff --git a/CODE/W95TRACE.H b/CODE/W95TRACE.H new file mode 100644 index 0000000..ffa1250 --- /dev/null +++ b/CODE/W95TRACE.H @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +/* + declarations for Win95 tracing facility +*/ + +#ifdef _DEBUG + +#ifndef __TRACEW95__ +#define __TRACEW95__ + +#include + +// redefine all the MFC macros to point to us + +#undef TRACE +#define TRACE OutputDebugStringW95 + +#undef TRACE0 +#define TRACE0 OutputDebugStringW95 + +#undef TRACE1 +#define TRACE1 OutputDebugStringW95 + +#undef TRACE2 +#define TRACE2 OutputDebugStringW95 + +#undef TRACE3 +#define TRACE3 OutputDebugStringW95 + +// redefine OutputDebugString so that it works with +// API calls +#undef OutputDebugString +#define OutputDebugString OutputDebugStringW95 + + +// function declarations +void OutputDebugStringW95( LPCTSTR lpOutputString, ... ); + +#endif //__TRACEW95__ + +#endif // _DEBUG diff --git a/CODE/WARHEAD.CPP b/CODE/WARHEAD.CPP new file mode 100644 index 0000000..3b830cf --- /dev/null +++ b/CODE/WARHEAD.CPP @@ -0,0 +1,192 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WARHEAD.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WARHEAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*************************************************************************** +** This is the warhead data object array. +*/ +TFixedIHeapClass Warheads; + + +/*********************************************************************************************** + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * * + * This default constructor for a warhead object will fill in all the default values * + * for a warhead. It is presumed that these values will be normal unless specifically * + * overridden by the INI database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass::WarheadTypeClass(char const * name) : + ID(Warheads.ID(this)), + IniName(name), + SpreadFactor(1), + IsWallDestroyer(false), + IsWoodDestroyer(false), + IsTiberiumDestroyer(false), + IsOrganic(false), + ExplosionSet(0), + InfantryDeath(0) +{ + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = 1; + } +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * * + * This will allocate a warhead object from the special heap that is maintained for * + * this purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated warhead type object. If there is * + * insufficient memory for the allocation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void * WarheadTypeClass::operator new(size_t) +{ + return(Warheads.Alloc()); +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * * + * This routine will return the warhead object to the memory pool from whence it came. * + * * + * INPUT: pointer -- Pointer to the warhead object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void WarheadTypeClass::operator delete(void * pointer) +{ + Warheads.Free((WarheadTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * * + * This will locate the warhead type specified and return a pointer to it. * + * * + * INPUT: warhead -- The warhead to convert into a pointer. * + * * + * OUTPUT: Returns with a pointer to the warhead type object that is represented by the * + * warhead type number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass * WarheadTypeClass::As_Pointer(WarheadType warhead) +{ + if (warhead != WARHEAD_NONE) { + return(Warheads.Ptr(warhead)); + } + return(NULL); +} + + +/*********************************************************************************************** + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * * + * Use this routine to retrieve the data specific to this warhead type class object from * + * the INI database specified. Typical use of this is when processing the rules.ini * + * file. * + * * + * INPUT: ini -- Reference to the INI database to fetch the values from. * + * * + * OUTPUT: bool; Was the warhead entry found and the data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WarheadTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + SpreadFactor = ini.Get_Int(Name(), "Spread", SpreadFactor); + IsWallDestroyer = ini.Get_Bool(Name(), "Wall", IsWallDestroyer); + IsWoodDestroyer = ini.Get_Bool(Name(), "Wood", IsWoodDestroyer); + IsTiberiumDestroyer = ini.Get_Bool(Name(), "Ore", IsTiberiumDestroyer); + ExplosionSet = ini.Get_Int(Name(), "Explosion", ExplosionSet); + InfantryDeath = ini.Get_Int(Name(), "InfDeath", InfantryDeath); + + char buffer[128]; + if (ini.Get_String(Name(), "Verses", "100%%,100%%,100%%,100%%,100%%", buffer, sizeof(buffer))) { + char * aval = strtok(buffer, ","); + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = fixed(aval); + aval = strtok(NULL, ","); + } + } + + IsOrganic = (Modifier[ARMOR_STEEL] == 0); + return(true); + } + return(false); +} + diff --git a/CODE/WARHEAD.H b/CODE/WARHEAD.H new file mode 100644 index 0000000..8652749 --- /dev/null +++ b/CODE/WARHEAD.H @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WARHEAD.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WARHEAD.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WARHEAD_H +#define WARHEAD_H + + +/********************************************************************** +** Each of the warhead types has specific characteristics. This structure +** holds these characteristics. +*/ +class WarheadTypeClass +{ + public: + WarheadTypeClass(char const * name); + WarheadTypeClass(NoInitClass const &) {} + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WarheadTypeClass * As_Pointer(WarheadType weapon); + + /* + ** This is the ID number of the weapon type. It is the weapon + ** type ID enum as well as the index number into the weapon + ** heap. + */ + int ID; + + /* + ** This is the identifying name of this warhead type. + */ + char const * IniName; + + /* + ** This value control how damage from this warhead type will reduce + ** over distance. The larger the number, the less the damage is reduced + ** the further the distance from the source of the damage. + */ + int SpreadFactor; + + /* + ** If this warhead type can destroy walls, then this flag will be true. + */ + bool IsWallDestroyer:1; + + /* + ** If this warhead can destroy wooden walls, then this flag will be true. + */ + bool IsWoodDestroyer:1; + + /* + ** Does this warhead damage tiberium? + */ + bool IsTiberiumDestroyer:1; + + /* + ** Only effective against infantry? + */ + bool IsOrganic:1; + + /* + ** The warhead damage is reduced depending on the the type of armor the + ** defender has. This table is what gives weapons their "character". + */ + fixed Modifier[ARMOR_COUNT]; + + /* + ** Which explosion set to use for warhead impact. + */ + int ExplosionSet; + + /* + ** This specifies the infantry death animation to use if the infantry dies as + ** a result of a warhead of this type. + */ + int InfantryDeath; +}; + + +#endif diff --git a/CODE/WATCOM.H b/CODE/WATCOM.H new file mode 100644 index 0000000..081a59c --- /dev/null +++ b/CODE/WATCOM.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WATCOM.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WATCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/12/95 * + * * + * Last Update : March 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WATCOM_H +#define WATCOM_H + +// Turn all warnings into errors. +#pragma warning * 0 + +// Disables warning when "sizeof" is used on an object with virtual functions. +#pragma warning 549 9 + +// Disable the "Integral value may be truncated during assignment or initialization". +#pragma warning 389 9 + +// Allow constructing a temporary to be used as a parameter. +#pragma warning 604 9 + +// Disable the construct resolved as an expression warning. +#pragma warning 595 9 + +// Disable the strange "construct resolved as a declaration/type" warning. +#pragma warning 594 9 + +// Disable the "pre-compiled header file cannot be used" warning. +#pragma warning 698 9 + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + +// Disable the "pointer or reference truncated by cast. Cast is supposed to REMOVE warnings, not create them. +#pragma warning 579 9 + +// Disable the warning that suggests a null destructor be placed in class definition. +#pragma warning 656 9 + +// Disable the warning about moving empty constructors/destructors to the class declaration. +#pragma warning 657 9 + +// No virtual destructor is not an error in C&C. +#pragma warning 004 9 + +// Turns off unreferenced function parameter warning. +//#pragma off(unreferenced) + + +// Fix deficiency in Watcom so that true/false will be defined. +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#endif diff --git a/CODE/WEAPON.CPP b/CODE/WEAPON.CPP new file mode 100644 index 0000000..1530b4b --- /dev/null +++ b/CODE/WEAPON.CPP @@ -0,0 +1,336 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WEAPON.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WEAPON.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : September 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Armor_From_Name -- Convert ASCII name into armor type number. * + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object.* + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the various weapons and their characteristics. +*/ +TFixedIHeapClass Weapons; + + +/*********************************************************************************************** + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * * + * This default constructor will initialize all the values of the weapon to the default * + * state. Thus, if any of these settings are not specifically overridden by the rules.ini * + * file, they will remain this value in the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::WeaponTypeClass(char const * name) : + ID(Weapons.ID(this)), + IniName(name), + IsSupressed(false), + IsCamera(false), + IsElectric(false), + Burst(1), + Bullet(NULL), + Attack(0), + MaxSpeed(MPH_IMMOBILE), + WarheadPtr(NULL), + ROF(0), + Range(0), + Sound(VOC_NONE), + Anim(ANIM_NONE) +{ +} + + +/*********************************************************************************************** + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * * + * This destructor really doesn't do anything but set the pointers to NULL. This is a * + * general purposes safety tactic but is otherwise useless. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::~WeaponTypeClass(void) +{ + IniName = NULL; + Bullet = NULL; + WarheadPtr = NULL; +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * * + * This will allocate a weapon type object from a special heap that has been set up for * + * that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the weapon type object allocated. If there was * + * insufficient memory available, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void * WeaponTypeClass::operator new(size_t) +{ + return(Weapons.Alloc()); +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * * + * This routine will return the weapon type object back to the heap so that the memory * + * can be reused for subsiquent allocations of weapon type objects. * + * * + * INPUT: pointer -- Pointer to the weapon type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void WeaponTypeClass::operator delete(void * pointer) +{ + Weapons.Free((WeaponTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object. * + * * + * This routine will conver the weapon type ID specified into a pointer to the weapon type * + * object it represents. * + * * + * INPUT: weapon -- The weapon type object ID to convert to a pointer. * + * * + * OUTPUT: Returns with a pointer to the weapon type class object that is represented by the * + * weapon ID number specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass * WeaponTypeClass::As_Pointer(WeaponType weapon) +{ + if (weapon != WEAPON_NONE) { + return(Weapons.Ptr(weapon)); +// for (int index = 0; index < Weapons.Count(); index++) { +// WeaponTypeClass * ptr = Weapons.Ptr(index); +// if (ptr->ID == weapon) { +// return(ptr); +// } +// } + } + return(NULL); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * * + * This routine will fetch the weapon data for this weapon type object from the INI * + * database specified. * + * * + * INPUT: ini -- Reference to the INI database that the weapon data will be fetched * + * from. * + * * + * OUTPUT: bool; Was this weapon type described in the database and the values retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WeaponTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsSupressed = ini.Get_Bool(Name(), "Supress", IsSupressed); + Burst = ini.Get_Int(Name(), "Burst", Burst); + Attack = ini.Get_Int(Name(), "Damage", Attack); + MaxSpeed = ini.Get_MPHType(Name(), "Speed", MaxSpeed); + ROF = ini.Get_Int(Name(), "ROF", ROF); + Range = ini.Get_Lepton(Name(), "Range", Range); + Sound = ini.Get_VocType(Name(), "Report", Sound); + Anim = ini.Get_AnimType(Name(), "Anim", Anim); + IsCamera = ini.Get_Bool(Name(), "Camera", IsCamera); + IsElectric = ini.Get_Bool(Name(), "Charges", IsElectric); + IsTurboBoosted = ini.Get_Bool(Name(), "TurboBoost", IsTurboBoosted); + + WarheadType wtype = (WarheadPtr != NULL) ? WarheadType(WarheadPtr->ID) : WARHEAD_NONE; + wtype = ini.Get_WarheadType(Name(), "Warhead", wtype); + if (wtype != WARHEAD_NONE) { + WarheadPtr = WarheadTypeClass::As_Pointer(wtype); +// WarheadPtr = &Warheads[wtype]; + } else { + WarheadPtr = NULL; + } + + BulletType btype = (Bullet != NULL) ? BulletType(Bullet->ID) : BULLET_NONE; + btype = ini.Get_BulletType(Name(), "Projectile", btype); + if (btype != BULLET_NONE) { + Bullet = &BulletTypeClass::As_Reference(btype); + } else { + Bullet = NULL; + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * * + * This will find the weapon whos name matches that specified and then it will return the * + * weapon ID number associated with it. * + * * + * INPUT: name -- Pointer to the ASCII name of the weapon type. * + * * + * OUTPUT: Returns with the weapon type ID number that matches the name specified. If no * + * match could be found, then WEAPON_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType Weapon_From_Name(char const * name) +{ + if (!name) return(WEAPON_NONE); + + for (int index = 0; index < Weapons.Count(); index++) { + if (stricmp(Weapons.Ptr(index)->Name(), name) == 0) { + return(WeaponType(Weapons.Ptr(index)->ID)); + } + } + + return(WEAPON_NONE); +} + + +/*********************************************************************************************** + * Armor_From_Name -- Convert ASCII name into armor type number. * + * * + * This will find the armor type that matches the ASCII name specified and then will return * + * the armor ID number of it. * + * * + * INPUT: name -- Pointer to the ASCII name of the armor to find. * + * * + * OUTPUT: Returns with the armor ID number of the armor that matches the name specified. If * + * no match could be found, then ARMOR_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType Armor_From_Name(char const * name) +{ + if (!name) return(ARMOR_NONE); + + for (ArmorType index = ARMOR_FIRST; index < ARMOR_COUNT; index++) { + if (stricmp(ArmorName[index], name) == 0) { + return(index); + } + } + + return(ARMOR_NONE); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * * + * This routine will examine the capabilities of this weapon and return with the threat * + * types that it can address. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the threat types that this weapon can address. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +ThreatType WeaponTypeClass::Allowed_Threats(void) const +{ + ThreatType threat = THREAT_NORMAL; + if (Bullet->IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + if (Bullet->IsAntiGround) { + threat = threat | THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BOATS|THREAT_BUILDINGS; + } + return(threat); +} + + +bool WeaponTypeClass::Is_Wall_Destroyer(void) const +{ + if (WarheadPtr != NULL && WarheadPtr->IsWallDestroyer) { + return(true); + } + return(false); +} diff --git a/CODE/WEAPON.H b/CODE/WEAPON.H new file mode 100644 index 0000000..d0598f1 --- /dev/null +++ b/CODE/WEAPON.H @@ -0,0 +1,165 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WEAPON.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WEAPON.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WEAPON_H +#define WEAPON_H + + +/********************************************************************** +** This is the constant data associated with a weapon. Some objects +** can have multiple weapons and this class is used to isolate and +** specify this data in a convenient and selfcontained way. +*/ +class WeaponTypeClass +{ + public: + WeaponTypeClass(char const * name); + WeaponTypeClass(NoInitClass const &) {} + ~WeaponTypeClass(void); + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WeaponTypeClass * As_Pointer(WeaponType weapon); + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + ThreatType Allowed_Threats(void) const; + bool Is_Wall_Destroyer(void) const; + + /* + ** This is both the weapon type number and the index number into + ** the weapon array. + */ + int ID; + + /* + ** This is the identifying name of this weapon. + */ + char const * IniName; + + /* + ** Increase the weapon speed if the target is flying. + */ + unsigned IsTurboBoosted:1; + + /* + ** If potential targets of this weapon should be scanned for + ** nearby friendly structures and if found, firing upon the target + ** would be discouraged, then this flag will be true. + */ + unsigned IsSupressed:1; + + /* + ** If this weapon is equipped with a camera that reveals the + ** area around the firer, then this flag will be true. + */ + unsigned IsCamera:1; + + /* + ** If this weapon requires charging before it can fire, then this + ** flag is true. In actuality, this only applies to the Tesla coil + ** which has specific charging animation. The normal rate of fire + ** value suffices for all other cases. + */ + unsigned IsElectric:1; + + /* + ** This is the number of shots this weapon first (in rapid succession). + ** The normal value is 1, but for the case of two shooter weapons such as + ** the double barreled gun turrets of the Mammoth tank, this value will be + ** set to 2. + */ + int Burst; + + /* + ** This is the unit class of the projectile fired. A subset of the unit types + ** represent projectiles. It is one of these classes that is specified here. + ** If this object does not fire anything, then this value will be BULLET_NONE. + */ + BulletTypeClass const * Bullet; + + /* + ** This is the damage (explosive load) to be assigned to the projectile that + ** this object fires. For the rare healing weapon, this value is negative. + */ + int Attack; + + /* + ** Speed of the projectile launched. + */ + MPHType MaxSpeed; + + /* + ** Warhead to attach to the projectile. + */ + WarheadTypeClass const * WarheadPtr; + + /* + ** Objects that fire (which can be buildings as well) will fire at a + ** frequency controlled by this value. This value serves as a count + ** down timer between shots. The smaller the value, the faster the + ** rate of fire. + */ + int ROF; + + /* + ** When this object fires, the range at which it's projectiles travel is + ** controlled by this value. The value represents the number of cells the + ** projectile will travel. Objects outside of this range will not be fired + ** upon (in normal circumstances). + */ + LEPTON Range; + + /* + ** This is the typical sound generated when firing. + */ + VocType Sound; + + /* + ** This is the animation to display at the firing coordinate. + */ + AnimType Anim; +}; + + +#endif + + diff --git a/CODE/WIDEFUNC.MAC b/CODE/WIDEFUNC.MAC new file mode 100644 index 0000000..2f32a9e --- /dev/null +++ b/CODE/WIDEFUNC.MAC @@ -0,0 +1 @@ +y#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#41381#4138#41431#4138#4143#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#41571#4143#4190#4143#4138#4190#4188#4143#4188#4138 \ No newline at end of file diff --git a/CODE/WIDEHDR.MAC b/CODE/WIDEHDR.MAC new file mode 100644 index 0000000..09b003a --- /dev/null +++ b/CODE/WIDEHDR.MAC @@ -0,0 +1 @@ +#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138y#4138#41431#4138#4143#4141#4141#4141#4141#4141#4141#4141#4141#4141#41411#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#41411 \ No newline at end of file diff --git a/CODE/WINASM.ASM b/CODE/WINASM.ASM new file mode 100644 index 0000000..c6b0d95 --- /dev/null +++ b/CODE/WINASM.ASM @@ -0,0 +1,890 @@ +; +; 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 . +; + + +;*************************************************************************** +;** 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 I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : WINSAM.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : October 26th, 1995 * +;* * +;* Last Update : October 26th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C _AbortModemFunctionPtr:dword +global C Memory_Error_Exit :dword +global C MouseQX :dword +global C MouseQY :dword + +global FastGetPortHardware_ :near +global FastSetPortHardware_ :near +global PortOpenGreenleafFast_ :near +global HMWaitForOK_ :near +global HMSetDialingMethod_ :near +global HMDial_ :near +global HMInputLine_ :near +global HMAnswer_ :near +global PortKillTime_ :near +global HMSendStringNoWait_ :near +global HMSetUpEchoRoutine_ :near +global HMSetUpAbortKey_ :near +global SetAbortModemFunctionPtr_:near +global Change8259Priority_ :near +global HMSendString_ :near +global C Stop_Execution :near + + +global _IPX_Initialise:near +global _ASM_IPX_Initialise:near + + + codeseg + +;proc _ASM_IPX_Initialise near +; +; int 3 +; jmp _IPX_Initialise +; +;endp + + + + +global _Int3:near +proc _Int3 near + ;int 3 + ret +endp + + +proc Stop_Execution C near + + nop + ret + +endp + + + +; +; Stuff needed from the shape library +; +; +; + + +INCLUDE "shape.inc" + + + + +;*************************************************************************** +;* ModeX_Blit -- Copy a 320x200 graphic view port to a modex screen * +;* * +;* * +;* INPUT: eax - graphic view port * +;* * +;* OUTPUT: none * +;* * +;* PROTO: extern "C" void ModeX_Blit (GraphicBufferClass *source); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* + +SEQUENCER =3c4h ; sequencer port +MAP_MASK =2 ; map mask register + + INCLUDE "gbuffer.inc" + global ModeX_Blit_:near + + +proc ModeX_Blit_ NEAR + + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + + mov ebp,200 + +??each_line_lp: mov ah,1 ;1st plane + push ebx + push esi + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + + push esi + push edi + push eax + + rept 10 + mov al,[esi] + mov bl,[esi+8] + mov cl,[esi+16] + mov dl,[esi+24] + mov ah,[esi+4] + mov bh,[esi+12] + mov ch,[esi+20] + mov dh,[esi+28] + shl ebx,16 + shl edx,16 + or ebx,eax + or edx,ecx + mov [edi],ebx + mov [edi+4],edx + add esi,32 + add edi,8 + endm + + pop eax + pop edi + pop esi + inc esi + shl ah,1 + cmp ah,16 + jl ??each_plane_lp + + + pop esi + pop ebx + lea esi,[esi+ebx+320] + add edi,80 + dec ebp + jnz ??each_line_lp + + popad + + ret + +endp ModeX_Blit_ + + + + + + +ifdef cuts + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + mov ah,1 ;1st plane + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + mov ebp,200 ;do 200 lines + push esi + push edi + +??each_line_lp: mov ecx,320/4 + +??each_pixel_lp:mov dl,[esi] + mov [edi],dl + add esi,4 + inc edi + dec ecx + jnz ??each_pixel_lp + + add esi,ebx + dec ebp + jnz ??each_line_lp + + pop edi + pop esi + inc esi + shl ah,1 + + cmp ah,16 + jl ??each_plane_lp +endif + + + + + + + + + + +proc FastGetPortHardware_ NEAR +endp + +proc FastSetPortHardware_ NEAR +endp + +proc PortOpenGreenleafFast_ NEAR +endp + +proc HMWaitForOK_ NEAR +endp + +proc HMSetDialingMethod_ NEAR +endp + +proc HMDial_ NEAR +endp + +proc HMInputLine_ NEAR +endp + +proc HMAnswer_ NEAR +endp + +proc PortKillTime_ NEAR +endp + +proc HMSendStringNoWait_ NEAR +endp + +proc HMSetUpEchoRoutine_ NEAR +endp + +proc HMSetUpAbortKey_ NEAR +endp + +proc SetAbortModemFunctionPtr_ NEAR +endp + +proc Change8259Priority_ NEAR +endp + +proc HMSendString_ NEAR +endp + + ret + + + + + + + + masm +; +; Change a DAC colour register directly +; +; register number in al +; +; bh=red bl=green cl=blue +; + +set_dac_col proc near + pushad + cli + push eax + mov dx,03dah + in al,dx + jmp @@1 +@@1: mov dx,03c8h + pop eax + out dx,al + jmp @@2 +@@2: inc dl + mov al,bh + out dx,al + jmp @@3 +@@3: mov al,bl + out dx,al + jmp @@4 +@@4: mov al,cl + out dx,al + jmp @@5 +@@5: sti + popad + ret +set_dac_col endp + + ideal + + +global Set_Palette_Register_:near + + +proc Set_Palette_Register_ near + + pushad + and cl,63 + mov bh,dl + and bh,63 + and bl,63 + call set_dac_col + popad + ret + +endp Set_Palette_Register_ + + + + + locals ?? + + + + ends + + dataseg + +LineBuffer dd 640 dup (?) + ends + + + + segment mycode page public use32 'code' ; Need stricter segment alignment + +global C Asm_Interpolate:near +global C Asm_Interpolate_Line_Double:near +global C Asm_Interpolate_Line_Interpolate:near +global C PaletteInterpolationTable:byte + + +;********************************************************************************************* +;* Asm_Interpolate -- interpolate a 320x200 buffer to a 640x400 screen * +;* * +;* INPUT: ptr to source buffer (320x200 image) * +;* ptr to dest buffer (640x400) * +;* height of source buffer * +;* width of source buffer * +;* width of dest buffer * +;* * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 12/15/95 ST : Created. * +;*===========================================================================================* + +PROC Asm_Interpolate C near + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + +??each_line_loop: + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov edi,[old_dest] + jmp ??interpolate_loop + + align 32 +; +; convert 2 pixels of source into 4 pixels of destination +; so we can write to video memory with dwords +; +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + +;1st 3 pixels now in ebx + + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank on the end of a row + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + mov edi,[dest_width] + add [old_dest],edi + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate + + + + + + + + + + + + + + +PROC Asm_Interpolate_Line_Double C near + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL width_counter:dword + LOCAL pixel_count:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + mov edi,[dest_ptr] + +??each_line_loop: + mov [width_counter],0 + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov [pixel_count],ecx + mov ecx,offset LineBuffer + mov edi,[old_dest] + jmp ??interpolate_loop + align 16 + +; convert 2 pixels of source into 4 pixels of destination +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + mov [ecx],ebx + add edi,4 + add ecx,4 + + dec [pixel_count] + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + mov [edi],ah + mov [ecx],ah + inc edi + inc ecx + mov [byte edi],0 + mov [byte ecx],0 + + mov edi,[dest_width] + shr edi,1 + add [old_dest],edi + push esi + push edi + mov esi,offset LineBuffer + mov edi,[old_dest] + mov ecx,[source_width] + shr ecx,1 + rep movsd + pop edi + pop esi + add [old_dest],edi + mov edi,[old_dest] + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate_Line_Double + + + + + + + + + + ends + + dataseg + +TopLine dd 640 dup (?) +BottomLine dd 640 dup (?) + + + segment mycode page public use32 'code' ; Need stricter segment alignment + + +proc Interpolate_Single_Line C near + + ARG source_ptr:dword + ARG dest_ptr:dword + ARG source_width:dword + + pushad + + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + + mov esi,[source_ptr] + mov edi,[dest_ptr] + +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + popad + ret + + +endp Interpolate_Single_Line + + +proc Interpolate_Between_Lines C near + + ARG source1:dword + ARG source2:dword + ARG destination:dword + ARG source_width:dword + + pushad + mov esi,[source1] + mov edi,[destination] + mov ebx,[source2] + xor eax,eax + mov ecx,[source_width] + add ecx,ecx + +??interpolate_each_pixel_loop: + mov al,[esi] + mov ah,[ebx] + inc esi + inc ebx + mov dl,[eax+PaletteInterpolationTable] + mov [edi],dl + inc edi + dec ecx + jnz ??interpolate_each_pixel_loop + + popad + ret + +endp Interpolate_Between_Lines + + + + +macro Lineswp + push [next_line] + push [last_line] + pop [next_line] + pop [last_line] +endm + + +PROC Asm_Interpolate_Line_Interpolate C near + + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_lines:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL pixel_count:dword + LOCAL next_line:dword + LOCAL last_line:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov [next_line],offset TopLine + mov [last_line],offset BottomLine + mov ecx,[source_width] + shr ecx,1 + mov [pixel_count],ecx + shr [dest_width],1 + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[source_width] + Lineswp + add [src_ptr],esi + dec [source_lines] + + +??each_line_pair_loop: + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + call Interpolate_Between_Lines C,[last_line],[next_line],offset LineBuffer,[source_width] + + mov esi,[last_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + mov edi,[old_dest] + mov esi,offset LineBuffer + add edi,[dest_width] + mov ecx,[pixel_count] + mov [old_dest],edi + rep movsd + + mov edi,[old_dest] + mov esi,[source_width] + add edi,[dest_width] + add [src_ptr],esi + mov [old_dest],edi + + Lineswp + dec [source_lines] + jnz ??each_line_pair_loop + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[next_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + popad + ret + + +endp Asm_Interpolate_Line_Interpolate + + ends mycode + + + + +global C Asm_Create_Palette_Interpolation_Table:near +global C InterpolationPalette:dword + + codeseg + + +proc Asm_Create_Palette_Interpolation_Table C near + + LOCAL palette_counter:dword + LOCAL first_palette:dword + LOCAL second_palette:dword + LOCAL dest_ptr:dword + LOCAL count:dword + LOCAL closest_colour:dword + LOCAL distance_of_closest:dword + + pushad + + mov [dest_ptr],0 + mov [palette_counter],256 + mov esi,[InterpolationPalette] + +??palette_outer_loop: + mov edi,[InterpolationPalette] + mov ecx,256 + +??palette_inner_loop: + mov bl,[esi] + add bl,[edi] + shr bl,1 + + mov bh,[esi+1] + add bh,[edi+1] + shr bh,1 + + mov dl,[esi+2] + add dl,[edi+2] + shr dl,1 + + mov [closest_colour],0 + mov [distance_of_closest],-1 + + push edi + push ecx + mov edi,[InterpolationPalette] + mov [count],0 + +??cmp_pal_lp: xor eax,eax + xor ecx,ecx + mov al,[edi] + sub al,bl + imul al + mov ecx,eax + mov al,[edi+1] + sub al,bh + imul al + add ecx,eax + mov al,[edi+2] + sub al,dl + imul al + add ecx,eax + + cmp ecx,[distance_of_closest] + ja ??end_cmp_lp + mov [distance_of_closest],ecx + mov eax,[count] + mov [closest_colour],eax + test ecx,ecx + jz ??got_perfect + +??end_cmp_lp: lea edi,[edi+3] + inc [count] + cmp [count],256 + jb ??cmp_pal_lp + + +??got_perfect: mov edi,[dest_ptr] + mov eax,[closest_colour] + mov [edi+PaletteInterpolationTable],al + inc [dest_ptr] + + pop ecx + pop edi + lea edi,[edi+3] + dec ecx + jnz ??palette_inner_loop + + lea esi,[esi+3] + dec [palette_counter] + jnz ??palette_outer_loop + + popad + ret + +endp Asm_Create_Palette_Interpolation_Table + + + + + DATASEG + +_AbortModemFunctionPtr dd 0 +Memory_Error_Exit dd 0 +MouseQX dd 0 +MouseQY dd 0 + +end diff --git a/CODE/WINSTUB.CPP b/CODE/WINSTUB.CPP new file mode 100644 index 0000000..dd35586 --- /dev/null +++ b/CODE/WINSTUB.CPP @@ -0,0 +1,962 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WINSTUB.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : October 4th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This file contains stubs for undefined externals when linked under Watcom for Win 95 * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Assert_Failure -- display the line and source file where a failed assert occurred * + * Check_For_Focus_Loss -- check for the end of the focus loss * + * Create_Main_Window -- opens the MainWindow for C&C * + * Focus_Loss -- this function is called when a library function detects focus loss * + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#include "ipx95.h" +#endif //WINSOCK_IPX + +void output(short,short) +{} + + +unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus +extern void VQA_PauseAudio(void); +extern void VQA_ResumeAudio(void); + +//#include "WolDebug.h" + + +/*********************************************************************************************** + * Focus_Loss -- this function is called when a library function detects focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 2:10PM ST : Created * + *=============================================================================================*/ + +void Focus_Loss(void) +{ + Theme.Suspend(); + Stop_Primary_Sound_Buffer(); + if (WWMouse) WWMouse->Clear_Cursor_Clip(); +} + +void Focus_Restore(void) +{ + Restore_Cached_Icons(); + Map.Flag_To_Redraw(true); + Start_Primary_Sound_Buffer(TRUE); + if (WWMouse) WWMouse->Set_Cursor_Clip(); + VisiblePage.Clear(); + HiddenPage.Clear(); +} + + + +/*********************************************************************************************** + * Check_For_Focus_Loss -- check for the end of the focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/2/96 10:49AM ST : Created * + *=============================================================================================*/ + +void Check_For_Focus_Loss(void) +{ + static BOOL focus_last_time = 1; + MSG msg; + + + if ( !GameInFocus ) { + Focus_Loss(); + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (!focus_last_time && GameInFocus) { + + VQA_PauseAudio(); + CountDownTimerClass cd; + cd.Set(60*1); + + do { + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } while (cd.Time()); + VQA_ResumeAudio(); + PostMessage (MainWindow, CCFocusMessage, 0, 0); +// AllSurfaces.Restore_Surfaces(); +// VisiblePage.Clear(); +// HiddenPage.Clear(); +// Map.Flag_To_Redraw(true); + } + + focus_last_time = GameInFocus; + +} + + + +extern BOOL InMovie; + +long FAR PASCAL _export Windows_Procedure(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + + int low_param = LOWORD(wParam); + + if (message == CCFocusMessage) { + Start_Primary_Sound_Buffer(TRUE); + if (!InMovie) { + Theme.Stop(); + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + return(0); + } + +#ifdef WINSOCK_IPX + /* + ** Pass on any messages intended for the winsock message handler. + */ + if ( PacketTransport ) { + if ( message == (UINT) PacketTransport->Protocol_Event_Message() ) { + if ( PacketTransport->Message_Handler (hwnd, message, wParam, lParam) ){ + return ( DefWindowProc (hwnd, message, wParam, lParam) ); + }else{ + return (0); + } + } + } +#endif //WINSOCK_IPX + + + /* + ** Pass this message through to the keyboard handler. If the message + ** was processed and requires no further action, then return with + ** this information. + */ + if (Keyboard->Message_Handler(hwnd, message, wParam, lParam)) { + return(1); + } + + switch ( message ) { +// case WM_SYSKEYDOWN: +// Mono_Printf("wparam=%08X lparam=%08X\n", (long)wParam, (long)lParam); + // fall through + +// case WM_MOUSEMOVE: +// case WM_KEYDOWN: +// case WM_SYSKEYUP: +// case WM_KEYUP: +// case WM_LBUTTONDOWN: +// case WM_LBUTTONUP: +// case WM_LBUTTONDBLCLK: +// case WM_MBUTTONDOWN: +// case WM_MBUTTONUP: +// case WM_MBUTTONDBLCLK: +// case WM_RBUTTONDOWN: +// case WM_RBUTTONUP: +// case WM_RBUTTONDBLCLK: +// Keyboard->Message_Handler(hwnd, message, wParam, lParam); +// return(0); + + /* + ** Windoze message says we have to shut down. Try and do it cleanly. + */ + case WM_DESTROY: + Prog_End(); + Invalidate_Cached_Icons(); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger) Reset_Video_Mode(); + Stop_Profiler(); + PostQuitMessage( 0 ); + + /* + ** If we are shutting down gracefully than flag that the message loop has finished. + ** If this is a forced shutdown (ReadyToQuit == 0) then try and close down everything + ** before we exit. + */ + switch (ReadyToQuit) { + case 1: + ReadyToQuit = 2; + break; + + case 0: + Shutdown_Network(); +#ifndef WINSOCK_IPX + if (Winsock.Get_Connected()) Winsock.Close(); + /* + ** Free the THIPX32 dll + */ + Unload_IPX_Dll(); +#endif //WINSOCK_IPX + ExitProcess(0); + break; + case 3: + Shutdown_Network(); +#ifndef WINSOCK_IPX + /* + ** Call the function to disable the IPX callback as horrible things can + ** happen if we get a callback after the process has exited! + */ + if (Session.Type == GAME_IPX){ + IPX_Shut_Down95(); + } + /* + ** Free the THIPX32 dll + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#else + Unload_IPX_Dll(); +#endif + + if (Winsock.Get_Connected()) Winsock.Close(); +#endif //WINSOCK_IPX + ReadyToQuit = 2; + break; + + } + return(0); + + case WM_ACTIVATEAPP: + GameInFocus=(BOOL)wParam; + if (!GameInFocus) Focus_Loss(); + AllSurfaces.Set_Surface_Focus (GameInFocus); + AllSurfaces.Restore_Surfaces(); +// if (GameInFocus) { +// Restore_Cached_Icons(); +// Map.Flag_To_Redraw(true); +// Start_Primary_Sound_Buffer(TRUE); +// if (WWMouse) WWMouse->Set_Cursor_Clip(); +// } + return(0); +#ifdef NEVER + case WM_ACTIVATE: + if (low_param == WA_INACTIVE) { + GameInFocus = FALSE; + Focus_Loss(); + } + return(0); +#endif //NEVER + + + case WM_SYSCOMMAND: + switch ( wParam ) { + + case SC_CLOSE: + /* + ** Windows sent us a close message. Probably in response to Alt-F4. Ignore it by + ** pretending to handle the message and returning true; + */ + return (0); + + case SC_SCREENSAVE: + /* + ** Windoze is about to start the screen saver. If we just return without passing + ** this message to DefWindowProc then the screen saver will not be allowed to start. + */ + return (0); + } + break; + + +#ifndef WINSOCK_IPX + case WM_ACCEPT: + case WM_HOSTBYADDRESS: + case WM_HOSTBYNAME: + case WM_ASYNCEVENT: + case WM_UDPASYNCEVENT: + Winsock.Message_Handler(hwnd, message, wParam, lParam); + return (0); +#endif //WINSOCK_IPX + } + + + + return DefWindowProc (hwnd, message, wParam, lParam); +} + + + + + + +HANDLE DebugFile = INVALID_HANDLE_VALUE; + +/*********************************************************************************************** + * WWDebugString -- sends a string to the debugger and echos it to disk * + * * + * * + * * + * INPUT: string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/28/96 12:48PM ST : Created * + *=============================================================================================*/ +void WWDebugString (char *string) +{ +#if (0) + char outstr[256]; + + sprintf (outstr, "%s", string); + + DWORD actual; + if (DebugFile == INVALID_HANDLE_VALUE){ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + }else{ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (DebugFile != INVALID_HANDLE_VALUE){ + SetFilePointer (DebugFile, 0, NULL, FILE_END); + WriteFile(DebugFile, outstr, strlen(outstr)+1, &actual, NULL); + CloseHandle (DebugFile); + } + + OutputDebugString (string); +#else //(0) + + string = string; +// debugprint( string ); + +#endif //(0) + +} + + + + + + + + + +/*********************************************************************************************** + * Create_Main_Window -- opens the MainWindow for C&C * + * * + * * + * * + * INPUT: instance -- handle to program instance * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/10/95 4:08PM ST : Created * + *=============================================================================================*/ + +#define CC_ICON 1 + +#if (ENGLISH) +#define WINDOW_NAME "Red Alert" +#endif + +#if (FRENCH) +#define WINDOW_NAME "Alerte Rouge" +#endif + +#if (GERMAN) +#define WINDOW_NAME "Alarmstufe Rot" +#endif + + +void Create_Main_Window ( HANDLE instance , int command_show , int width , int height ) + +{ + HWND hwnd ; + WNDCLASS wndclass ; + // + // Register the window class + // + + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = Windows_Procedure ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = instance ; + wndclass.hIcon = LoadIcon (instance, MAKEINTRESOURCE(CC_ICON)) ; + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = WINDOW_NAME; //NULL + wndclass.lpszClassName = WINDOW_NAME; + + RegisterClass (&wndclass) ; + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + WINDOW_NAME, + WINDOW_NAME, + WS_POPUP, // Denzil | WS_MAXIMIZE, + 0, + 0, + // Denzil 5/18/98 - Making window fullscreen prevents other apps + // from getting WM_PAINT messages + GetSystemMetrics(SM_CXSCREEN), //width, + GetSystemMetrics(SM_CYSCREEN), //height, + // End Denzil + NULL, + NULL, + instance, + NULL ); +// Denzil +width = width; height = height; +// End + + ShowWindow (hwnd, command_show ); + ShowCommand = command_show; + UpdateWindow (hwnd); + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + hInstance = instance; + + CCFocusMessage = RegisterWindowMessage ("CC_GOT_FOCUS"); + + Audio_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Restore_Function = &Focus_Restore; + Gbuffer_Focus_Loss_Function = &Focus_Loss; +} + + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc) +{ + MSG msg; + /* + ** Get rid of the Westwood mouse cursor because we are showing a + ** dialog box and we want to have the right windows cursor showing + ** for it. + */ + Hide_Mouse(); + ShowCursor(TRUE); + + /* + ** Pop up the dialog box and then run a standard message handler + ** until the dialog box is closed. + */ + + DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc); + while (GetMessage(&msg, NULL, 0, 0) && !AllDone) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* + ** Restore the westwood mouse cursor and get rid of the windows one + ** because it is now time to restore back to the westwood way of + ** doing things. + */ + ShowCursor(FALSE); + Show_Mouse(); +} + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + +int DebugColour=1; + + +extern "C" void Set_Palette_Register(int number, int red, int green, int blue); +#pragma off (unreferenced) +void Colour_Debug (int call_number) +{ + //#if 0 + //if (DebugColour==call_number || !call_number) { + + //if (call_number) { + // Wait_Vert_Blank(); + //} + + Set_Palette_Register (0,ColourLookup[call_number].Red , + ColourLookup[call_number].Green, + ColourLookup[call_number].Blue); + //} + //#endif +} + +#pragma on (unreferenced) + + + + + +BOOL Any_Locked (void) +{ + if (SeenBuff.Get_LockCount() || + HidPage.Get_LockCount()) { + return (TRUE); + } else { + return(FALSE); + } +} + + + + + + + + +// +// Miscellaneous stubs. Mainly for multi player stuff +// +// +// + +//IPXAddressClass::IPXAddressClass(void) { +// int i; +// i++; +//} +//int IPXManagerClass::Num_Connections(void) { return (0); } +//int IPXManagerClass::Connection_ID( int ) { return (0); } +//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); } +//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); } +//int IPXAddressClass::Is_Broadcast() { return (0); } +//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); } +//int IPXManagerClass::Service() { return (0); } +//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); } +//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); } +//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {} +//IPXManagerClass::~IPXManagerClass() { +// int i; +// i++; +// } +//int IPXManagerClass::Delete_Connection( int ) { return (0); } +//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Socket( short unsigned ) {} +//int IPXManagerClass::Is_IPX() { return (0); } +//int IPXManagerClass::Init() { return (0); } +//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Bridge( char unsigned * ) {} +//int IPXManagerClass::Global_Num_Send() { return (0); } +//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ) {} +//unsigned long IPXManagerClass::Global_Response_Time() { return (0); } +//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); } +//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); } +//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); } +//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); } +//int IPXManagerClass::Connection_Index( int ) { return (0); } +//void IPXManagerClass::Reset_Response_Time() {} +//long unsigned IPXManagerClass::Response_Time() { return (0); } +//int IPXManagerClass::Private_Num_Send( int ) { return (0); } + +//_VQAHandle * VQA_Alloc(void) { return ((_VQAHandle *)0); } +//void VQA_Init( _VQAHandle *, long ( *)()) {} +//long VQA_Open( _VQAHandle *, char const *, _VQAConfig * ) { return (0); } +//void VQA_Free( _VQAHandle * ) {} +//void VQA_Close( _VQAHandle * ) {} +//long VQA_Play( _VQAHandle *, long ) { return (0); } + +//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)) {} + +//long VQA_Open(VQAHandle *, char const *, VQAConfig *) +//{ +// return (0); +//} + +//void VQA_Close(VQAHandle *) {} + +//long VQA_Play(VQAHandle *, long) +//{ +// return (0); +//} + + +unsigned char *VQPalette; +long VQNumBytes; +unsigned long VQSlowpal; +bool VQPaletteChange = false; + + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette, long numbytes, unsigned long slowpal); +} + + + +void Flag_To_Set_Palette(unsigned char *palette, long numbytes, unsigned long slowpal) +{ + VQPalette = palette; + VQNumBytes = numbytes; + VQSlowpal = slowpal; + VQPaletteChange = true; +} + + + +void Check_VQ_Palette_Set(void) +{ + if (VQPaletteChange) { + SetPalette(VQPalette, VQNumBytes, VQSlowpal); + VQPaletteChange = false; + } +} + + + + + +void __cdecl SetPalette(unsigned char *palette, long, unsigned long) +{ + for (int i=0 ; i<256*3 ; i++) { + *(palette+i)&=63; + } + Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63); + + if (PalettesRead) { + memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536); + } + Set_Palette(palette); +} + + +#ifndef NDEBUG +/*********************************************************************************************** + * Assert_Failure -- display the line and source file where a failed assert occurred * + * * + * * + * INPUT: line number in source file * + * name of source file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/17/96 9:58AM ST : Created * + *=============================================================================================*/ + +void Assert_Failure (char *expression, int line, char *file) +{ + char assertbuf[256]; + char timebuff[512]; + SYSTEMTIME time; + + sprintf (assertbuf, "assert '%s' failed at line %d in module %s.\n", expression, line, file); + + if (!MonoClass::Is_Enabled()) MonoClass::Enable(); + + Mono_Clear_Screen(); + Mono_Printf("%s", assertbuf); + + WWDebugString(assertbuf); + + GetLocalTime(&time); + + sprintf (timebuff, "%02d/%02d/%04d %02d:%02d:%02d - %s", time.wMonth, time.wDay, time.wYear, + time.wHour, time.wMinute, time.wSecond, + assertbuf); + + + HMMIO handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + if (!handle) { + handle = mmioOpen("ASSERT.TXT", NULL, MMIO_CREATE | MMIO_WRITE); + //mmioClose(handle, 0); + //handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + } + + if (handle) { + + mmioWrite(handle, timebuff, strlen(timebuff)); + mmioClose(handle, 0); + } + + WWMessageBox().Process(assertbuf); +// WWMessageBox().Process("Red Alert demo timed out - Aborting"); + //Get_Key(); + + Prog_End(); + Invalidate_Cached_Icons(); + PostQuitMessage( 0 ); + ExitProcess(0); +} +#endif + + + + + + + +/*********************************************************************************************** + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 3:57PM ST : Created * + *=============================================================================================*/ +void Memory_Error_Handler(void) +{ + VisiblePage.Clear(); + CCPalette.Set(); + while (Get_Mouse_State()) {Show_Mouse();}; + WWMessageBox().Process(TEXT_MEMORY_ERROR, TEXT_ABORT, false); + + ReadyToQuit = 1; + + PostMessage(MainWindow, WM_DESTROY, 0, 0); + do + { + Keyboard->Check(); + }while (ReadyToQuit == 1); + + ExitProcess(0); +} + + + + + + + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size); +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette) +{ + + GraphicBufferClass *load_buffer; + + + load_buffer = Read_PCX_File (name, (char*)palette, NULL, 0); + + if (load_buffer) { + load_buffer->Blit(*video_page); + delete load_buffer; + } +} + + + +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the format [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optional, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointed by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; * * + * OUTPUT: on success a pointer to a GraphicBufferClass containing the * + * pcx file, NULL otherwise. * + * * + * WARNINGS: * + * Appears to be a comment-free zone * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + * 04/30/1996 ST : Tidied up and modified to use CCFileClass * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + file_handle.Read (pool , POOL_SIZE ); \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size) +{ + unsigned i, j; + unsigned rle; + unsigned color; + unsigned scan_pos; + char *file_ptr; + int width; + int height; + char *buffer; + PCX_HEADER header; + RGB *pal; + char pool [POOL_SIZE]; + GraphicBufferClass *pic; + + CCFileClass file_handle(name); + + if (!file_handle.Is_Available()) return (NULL); + + file_handle.Open(READ); + + file_handle.Read (&header, sizeof (PCX_HEADER)); + + if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1; + height = header.height - header.y + 1; + + if (Buff) { + buffer = (char *)Buff; + i = Size / width; + height = MIN (i - 1, height); + pic = new GraphicBufferClass(width, height, buffer, Size); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } else { + pic = new GraphicBufferClass(width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } + + buffer = (char *) pic->Get_Buffer(); + file_ptr = pool ; + file_handle.Read (pool , POOL_SIZE); + + if ( header.byte_per_line != width ) { + + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ); + i += rle; + } else { + *(buffer+scan_pos + i++ ) = (char)rle; + } + } + } + + if ( i == width ) rle = READ_CHAR (); + if ( rle > 192 ) rle = READ_CHAR (); + + } else { + + for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ); + i += rle ; + } else { + *(buffer + i++) = (char)rle; + } + } + } + + if ( palette ) { + file_handle.Seek (- (256 * sizeof ( RGB )) , SEEK_END ); + file_handle.Read (palette , 256L * sizeof ( RGB )); + + pal = ( RGB * ) palette; + for (i = 0 ; i < 256 ; i ++) { + pal ->red >>= 2; + pal ->green >>= 2; + pal ->blue >>= 2; + pal ++ ; + } + } + + file_handle.Close(); + return pic; +} diff --git a/CODE/WOLAPI/CHATDEFS.H b/CODE/WOLAPI/CHATDEFS.H new file mode 100644 index 0000000..1619137 --- /dev/null +++ b/CODE/WOLAPI/CHATDEFS.H @@ -0,0 +1,152 @@ +/* +** 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 . +*/ + +#ifndef CHATDEFS_HEADER +#define CHATDEFS_HEADER + +// +// Response errors (Sent as arguments to the OnFoo calls) +// + +// Your nick is still logged into chat +#define CHAT_E_NICKINUSE MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 100) +// Your password is incorrect during login +#define CHAT_E_BADPASS MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 101) +// Reference made to non-existant user or channel +#define CHAT_E_NONESUCH MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 102) +// The network layer is down or cannot be initialized for some reason +#define CHAT_E_CON_NETDOWN MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 103) +// Name lookup (e.g DNS) failed for some reason +#define CHAT_E_CON_LOOKUP_FAILED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 104) +// Some fatal error occured with the net connection +#define CHAT_E_CON_ERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 105) +// General request timeout for a request +#define CHAT_E_TIMEOUT MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 106) +// Must patch before continuing +#define CHAT_E_MUSTPATCH MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 107) +// Miscellaneous internal status error +#define CHAT_E_STATUSERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 108) +// Server has returned something we don't recognise +#define CHAT_E_UNKNOWNRESPONSE MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 109) +// Tried to join a channel that has enough players already +#define CHAT_E_CHANNELFULL MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 110) +// Tried to create a channel that already exists +#define CHAT_E_CHANNELEXISTS MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 111) +// Tried to join a channel that does not exist +#define CHAT_E_CHANNELDOESNOTEXIST MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 112) +// Tried to join a channel with the wrong password +#define CHAT_E_BADCHANNELPASSWORD MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 113) +// You've been banned from the server / channel +#define CHAT_E_BANNED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 114) +// You tried to do something that required operator status +#define CHAT_E_NOT_OPER MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 115) + + +// +// Response success codes (non-error arguments passed to the OnFoo callbacks) +// Note: S_OK is the usual success code + +// A network connection is underway +#define CHAT_S_CON_CONNECTING MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 300) +// A network connection is complete +#define CHAT_S_CON_CONNECTED MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 301) +// A network connection is going down +#define CHAT_S_CON_DISCONNECTING MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 302) +// A network connection is closed +#define CHAT_S_CON_DISCONNECTED MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 303) + + +// Find - Nick not in system +#define CHAT_S_FIND_NOTHERE MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 304) +// Find - Not in any channels +#define CHAT_S_FIND_NOCHAN MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 305) +// Find - user has find turned off +#define CHAT_S_FIND_OFF MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 306) + + +// Page - Nick not in system +#define CHAT_S_PAGE_NOTHERE MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 307) +// Page - user has page turned off +#define CHAT_S_PAGE_OFF MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 308) + +// This channel list is only a ping update +#define CHAT_S_PINGLIST MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 315) + + + + + + + +// +// Request errors (returned from the RequestFoo calls) +// + +// You are not connected to the chat server +#define CHAT_E_NOTCONNECTED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 500) +// You are not in a channel +#define CHAT_E_NOCHANNEL MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 501) +// Feature is not implemented +#define CHAT_E_NOTIMPLEMENTED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 502) +// The request was made while while a conflicting request was still pending +#define CHAT_E_PENDINGREQUEST MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 503) +// Invalid parameter passed - usually a NULL pointer +#define CHAT_E_PARAMERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 504) +// Tried to create or join a channel before leaving the previous one +#define CHAT_E_LEAVECHANNEL MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 508) +// Tried to send something to a channel when not a member of any channel +#define CHAT_E_JOINCHANNEL MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 509) +// Tried to join a non-existant channel +#define CHAT_E_UNKNOWNCHANNEL MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 510) + + +// +// Request success codes +// Notes: S_OK is the usual success code + +// +// Channel list filter values +// + +#define CHAT_CHANNEL_LIST_ALL -99999 + +// +// User flags (bit field) +// +// The owner of the current channel +#define CHAT_USER_CHANNELOWNER 0x0001 +// The person with voice (conch shell...) +#define CHAT_USER_VOICE 0x0002 +// The local user +#define CHAT_USER_MYSELF 0x8000 + +// +// Channel flags (bit field) +// +#define CHAN_MODE_PRIVATE 0x0004 +#define CHAN_MODE_SECRET 0x0008 +#define CHAN_MODE_MODERATED 0x0010 +#define CHAN_MODE_TOPICLIMIT 0x0020 +#define CHAN_MODE_INVITEONLY 0x0040 +#define CHAN_MODE_NOPRIVMSGS 0x0080 +#define CHAN_MODE_KEY 0x0100 +#define CHAN_MODE_BAN 0x0200 +#define CHAN_MODE_LIMIT 0x0400 + + +#endif diff --git a/CODE/WOLAPI/DOWNLOADDEFS.H b/CODE/WOLAPI/DOWNLOADDEFS.H new file mode 100644 index 0000000..8fcc7d4 --- /dev/null +++ b/CODE/WOLAPI/DOWNLOADDEFS.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +#ifndef _DOWNLOADDEFS_H +#define _DOWNLOADDEFS_H + +// CDownload statuses + +#define DOWNLOADSTATUS_NONE 0 +#define DOWNLOADSTATUS_GO 1 +#define DOWNLOADSTATUS_CONNECTING 2 +#define DOWNLOADSTATUS_LOGGINGIN 3 +#define DOWNLOADSTATUS_FINDINGFILE 4 +#define DOWNLOADSTATUS_QUERYINGRESUME 5 +#define DOWNLOADSTATUS_DOWNLOADING 6 +#define DOWNLOADSTATUS_DISCONNECTING 7 +#define DOWNLOADSTATUS_DONE 0 + +// CDownload return codes + +#define DOWNLOAD_SUCCEEDED S_OK +#define DOWNLOAD_PARAMERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 1 ) +#define DOWNLOAD_STATUSERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 2 ) +#define DOWNLOAD_NETWORKERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 3 ) +#define DOWNLOAD_FILEERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 4 ) +#define DOWNLOAD_REENTERERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 4 ) + +// CDownloadEvent return codes. + +#define DOWNLOADEVENT_SUCCEEDED S_OK +#define DOWNLOADEVENT_FAILED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 5 ) +#define DOWNLOADEVENT_RESUME S_OK +#define DOWNLOADEVENT_DONOTRESUME MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 6 ) + +// CDownloadEvent error codes for OnError() + +#define DOWNLOADEVENT_NOSUCHSERVER 1 +#define DOWNLOADEVENT_COULDNOTCONNECT 2 +#define DOWNLOADEVENT_LOGINFAILED 3 +#define DOWNLOADEVENT_NOSUCHFILE 4 +#define DOWNLOADEVENT_LOCALFILEOPENFAILED 5 +#define DOWNLOADEVENT_TCPERROR 6 +#define DOWNLOADEVENT_DISCONNECTERROR 7 + +#endif diff --git a/CODE/WOLAPI/FTPDEFS.H b/CODE/WOLAPI/FTPDEFS.H new file mode 100644 index 0000000..7674f39 --- /dev/null +++ b/CODE/WOLAPI/FTPDEFS.H @@ -0,0 +1,30 @@ +/* +** 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 . +*/ + +#ifndef __FTPDEFS_H_INCLUDED__ +#define __FTPDEFS_H_INCLUDED__ + + +// CFtp return codes. + +#define FTP_SUCCEEDED S_OK +#define FTP_FAILED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 1 ) +#define FTP_TRYING MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 2 ) + + +#endif \ No newline at end of file diff --git a/CODE/WOLAPI/NETUTILDEFS.H b/CODE/WOLAPI/NETUTILDEFS.H new file mode 100644 index 0000000..ae22dd4 --- /dev/null +++ b/CODE/WOLAPI/NETUTILDEFS.H @@ -0,0 +1,28 @@ +/* +** 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 . +*/ + +#ifndef NETUTILDEFS_HEADER +#define NETUTILDEFS_HEADER + + +#define NETUTIL_E_ERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 100) +#define NETUTIL_E_BUSY MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 101) + +#define NETUTIL_S_FINISHED MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_ITF, 500) + +#endif \ No newline at end of file diff --git a/CODE/WOLAPI/WOLAPI.H b/CODE/WOLAPI/WOLAPI.H new file mode 100644 index 0000000..50471ec --- /dev/null +++ b/CODE/WOLAPI/WOLAPI.H @@ -0,0 +1,3859 @@ +/* +** 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 . +*/ + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + +/* File created by MIDL compiler version 3.01.75 */ +/* at Wed Jul 29 16:25:34 1998 + */ +/* Compiler settings for WOLAPI.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: none +*/ +//@@MIDL_FILE_HEADING( ) +#include "rpc.h" +#include "rpcndr.h" +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __WOLAPI_h__ +#define __WOLAPI_h__ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* Forward Declarations */ + +#ifndef __IRTPatcher_FWD_DEFINED__ +#define __IRTPatcher_FWD_DEFINED__ +typedef interface IRTPatcher IRTPatcher; +#endif /* __IRTPatcher_FWD_DEFINED__ */ + + +#ifndef __IRTPatcherEvent_FWD_DEFINED__ +#define __IRTPatcherEvent_FWD_DEFINED__ +typedef interface IRTPatcherEvent IRTPatcherEvent; +#endif /* __IRTPatcherEvent_FWD_DEFINED__ */ + + +#ifndef __IChat_FWD_DEFINED__ +#define __IChat_FWD_DEFINED__ +typedef interface IChat IChat; +#endif /* __IChat_FWD_DEFINED__ */ + + +#ifndef __IChatEvent_FWD_DEFINED__ +#define __IChatEvent_FWD_DEFINED__ +typedef interface IChatEvent IChatEvent; +#endif /* __IChatEvent_FWD_DEFINED__ */ + + +#ifndef __IDownload_FWD_DEFINED__ +#define __IDownload_FWD_DEFINED__ +typedef interface IDownload IDownload; +#endif /* __IDownload_FWD_DEFINED__ */ + + +#ifndef __IDownloadEvent_FWD_DEFINED__ +#define __IDownloadEvent_FWD_DEFINED__ +typedef interface IDownloadEvent IDownloadEvent; +#endif /* __IDownloadEvent_FWD_DEFINED__ */ + + +#ifndef __INetUtil_FWD_DEFINED__ +#define __INetUtil_FWD_DEFINED__ +typedef interface INetUtil INetUtil; +#endif /* __INetUtil_FWD_DEFINED__ */ + + +#ifndef __INetUtilEvent_FWD_DEFINED__ +#define __INetUtilEvent_FWD_DEFINED__ +typedef interface INetUtilEvent INetUtilEvent; +#endif /* __INetUtilEvent_FWD_DEFINED__ */ + + +#ifndef __IChat2_FWD_DEFINED__ +#define __IChat2_FWD_DEFINED__ +typedef interface IChat2 IChat2; +#endif /* __IChat2_FWD_DEFINED__ */ + + +#ifndef __IChat2Event_FWD_DEFINED__ +#define __IChat2Event_FWD_DEFINED__ +typedef interface IChat2Event IChat2Event; +#endif /* __IChat2Event_FWD_DEFINED__ */ + + +#ifndef __RTPatcher_FWD_DEFINED__ +#define __RTPatcher_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class RTPatcher RTPatcher; +#else +typedef struct RTPatcher RTPatcher; +#endif /* __cplusplus */ + +#endif /* __RTPatcher_FWD_DEFINED__ */ + + +#ifndef __Chat_FWD_DEFINED__ +#define __Chat_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class Chat Chat; +#else +typedef struct Chat Chat; +#endif /* __cplusplus */ + +#endif /* __Chat_FWD_DEFINED__ */ + + +#ifndef __Download_FWD_DEFINED__ +#define __Download_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class Download Download; +#else +typedef struct Download Download; +#endif /* __cplusplus */ + +#endif /* __Download_FWD_DEFINED__ */ + + +#ifndef __NetUtil_FWD_DEFINED__ +#define __NetUtil_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class NetUtil NetUtil; +#else +typedef struct NetUtil NetUtil; +#endif /* __cplusplus */ + +#endif /* __NetUtil_FWD_DEFINED__ */ + + +#ifndef __Chat2_FWD_DEFINED__ +#define __Chat2_FWD_DEFINED__ + +#ifdef __cplusplus +typedef class Chat2 Chat2; +#else +typedef struct Chat2 Chat2; +#endif /* __cplusplus */ + +#endif /* __Chat2_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" +//#include "ocidl.h" + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +#ifndef __IRTPatcher_INTERFACE_DEFINED__ +#define __IRTPatcher_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IRTPatcher + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_IRTPatcher; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("925CDEDE-71B9-11D1-B1C5-006097176556") + IRTPatcher : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE ApplyPatch( + /* [string][in] */ LPCSTR destpath, + /* [string][in] */ LPCSTR filename) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE PumpMessages( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IRTPatcherVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IRTPatcher __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IRTPatcher __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IRTPatcher __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ApplyPatch )( + IRTPatcher __RPC_FAR * This, + /* [string][in] */ LPCSTR destpath, + /* [string][in] */ LPCSTR filename); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PumpMessages )( + IRTPatcher __RPC_FAR * This); + + END_INTERFACE + } IRTPatcherVtbl; + + interface IRTPatcher + { + CONST_VTBL struct IRTPatcherVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IRTPatcher_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IRTPatcher_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IRTPatcher_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IRTPatcher_ApplyPatch(This,destpath,filename) \ + (This)->lpVtbl -> ApplyPatch(This,destpath,filename) + +#define IRTPatcher_PumpMessages(This) \ + (This)->lpVtbl -> PumpMessages(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IRTPatcher_ApplyPatch_Proxy( + IRTPatcher __RPC_FAR * This, + /* [string][in] */ LPCSTR destpath, + /* [string][in] */ LPCSTR filename); + + +void __RPC_STUB IRTPatcher_ApplyPatch_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IRTPatcher_PumpMessages_Proxy( + IRTPatcher __RPC_FAR * This); + + +void __RPC_STUB IRTPatcher_PumpMessages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IRTPatcher_INTERFACE_DEFINED__ */ + + +#ifndef __IRTPatcherEvent_INTERFACE_DEFINED__ +#define __IRTPatcherEvent_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IRTPatcherEvent + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_IRTPatcherEvent; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("925CDEE3-71B9-11D1-B1C5-006097176556") + IRTPatcherEvent : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnProgress( + /* [in] */ LPCSTR filename, + /* [in] */ int progress) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnTermination( + /* [in] */ BOOL success) = 0; + + }; + +#else /* C style interface */ + + typedef struct IRTPatcherEventVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IRTPatcherEvent __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IRTPatcherEvent __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IRTPatcherEvent __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnProgress )( + IRTPatcherEvent __RPC_FAR * This, + /* [in] */ LPCSTR filename, + /* [in] */ int progress); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnTermination )( + IRTPatcherEvent __RPC_FAR * This, + /* [in] */ BOOL success); + + END_INTERFACE + } IRTPatcherEventVtbl; + + interface IRTPatcherEvent + { + CONST_VTBL struct IRTPatcherEventVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IRTPatcherEvent_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IRTPatcherEvent_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IRTPatcherEvent_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IRTPatcherEvent_OnProgress(This,filename,progress) \ + (This)->lpVtbl -> OnProgress(This,filename,progress) + +#define IRTPatcherEvent_OnTermination(This,success) \ + (This)->lpVtbl -> OnTermination(This,success) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IRTPatcherEvent_OnProgress_Proxy( + IRTPatcherEvent __RPC_FAR * This, + /* [in] */ LPCSTR filename, + /* [in] */ int progress); + + +void __RPC_STUB IRTPatcherEvent_OnProgress_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IRTPatcherEvent_OnTermination_Proxy( + IRTPatcherEvent __RPC_FAR * This, + /* [in] */ BOOL success); + + +void __RPC_STUB IRTPatcherEvent_OnTermination_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IRTPatcherEvent_INTERFACE_DEFINED__ */ + + +#ifndef __IChat_INTERFACE_DEFINED__ +#define __IChat_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IChat + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + +//typedef long time_t; +#include + +struct Ladder + { + unsigned int sku; + unsigned int team_no; + unsigned int wins; + unsigned int losses; + unsigned int points; + unsigned int kills; + unsigned int rank; + unsigned int rung; + unsigned int disconnects; + unsigned int team_rung; + unsigned int provisional; + unsigned int last_game_date; + unsigned int win_streak; + unsigned int reserved1; + unsigned int reserved2; + struct Ladder __RPC_FAR *next; + unsigned char login_name[ 40 ]; + }; +typedef int GroupID; + +struct Server + { + int gametype; + int chattype; + int timezone; + float longitude; + float lattitude; + struct Server __RPC_FAR *next; + unsigned char name[ 71 ]; + unsigned char connlabel[ 5 ]; + unsigned char conndata[ 128 ]; + unsigned char login[ 10 ]; + unsigned char password[ 10 ]; + }; +struct Channel + { + int type; + unsigned int minUsers; + unsigned int maxUsers; + unsigned int currentUsers; + unsigned int official; + unsigned int tournament; + unsigned int ingame; + unsigned int flags; + unsigned long reserved; + unsigned long ipaddr; + int latency; + int hidden; + struct Channel __RPC_FAR *next; + unsigned char name[ 17 ]; + unsigned char topic[ 81 ]; + unsigned char location[ 65 ]; + unsigned char key[ 9 ]; + unsigned char exInfo[ 41 ]; + }; +struct User + { + unsigned int flags; + GroupID group; + unsigned long reserved; + unsigned long reserved2; + unsigned long reserved3; + unsigned long reserved4; + unsigned long ipaddr; + unsigned long squad_icon; + struct User __RPC_FAR *next; + unsigned char name[ 10 ]; + unsigned char squadname[ 41 ]; + }; +struct Group + { + GroupID ident; + int type; + unsigned int members; + struct Group __RPC_FAR *next; + unsigned char name[ 65 ]; + }; +struct Update + { + unsigned long SKU; + unsigned long version; + int required; + struct Update __RPC_FAR *next; + unsigned char server[ 65 ]; + unsigned char patchpath[ 256 ]; + unsigned char patchfile[ 33 ]; + unsigned char login[ 33 ]; + unsigned char password[ 65 ]; + unsigned char localpath[ 256 ]; + }; +typedef struct Server Server; + +typedef struct Channel Channel; + +typedef struct User User; + +typedef struct Group Group; + +typedef struct Update Update; + +typedef struct Ladder Ladder; + + +EXTERN_C const IID IID_IChat; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("4DD3BAF4-7579-11D1-B1C6-006097176556") + IChat : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE PumpMessages( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestServerList( + /* [in] */ unsigned long SKU, + /* [in] */ unsigned long current_version, + /* [in] */ LPCSTR loginname, + /* [in] */ LPCSTR password, + /* [in] */ int timeout) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestConnection( + /* [in] */ Server __RPC_FAR *server, + /* [in] */ int timeout, + int domangle) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelList( + /* [in] */ int channelType, + /* [in] */ int autoping) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelCreate( + /* [in] */ Channel __RPC_FAR *channel) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelJoin( + /* [in] */ Channel __RPC_FAR *channel) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelLeave( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestUserList( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPublicMessage( + /* [in] */ LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPrivateMessage( + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestLogout( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPrivateGameOptions( + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR options) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPublicGameOptions( + /* [in] */ LPCSTR options) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPublicAction( + /* [in] */ LPCSTR action) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPrivateAction( + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR action) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestGameStart( + /* [in] */ User __RPC_FAR *users) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelTopic( + /* [in] */ LPCSTR topic) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetVersion( + /* [in] */ unsigned long __RPC_FAR *version) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestUserKick( + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestUserIP( + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetGametypeInfo( + unsigned int gtype, + int icon_size, + unsigned char __RPC_FAR *__RPC_FAR *bitmap, + int __RPC_FAR *bmp_bytes, + LPCSTR __RPC_FAR *name, + LPCSTR __RPC_FAR *URL) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestFind( + User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestPage( + User __RPC_FAR *user, + LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetFindPage( + int findOn, + int pageOn) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetSquelch( + User __RPC_FAR *user, + int squelch) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetSquelch( + User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetChannelFilter( + int channelType) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestGameEnd( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetLangFilter( + int onoff) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelBan( + LPCSTR name, + int ban) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetGametypeList( + LPCSTR __RPC_FAR *list) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetHelpURL( + LPCSTR __RPC_FAR *url) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetProductSKU( + unsigned long SKU) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetNick( + int num, + LPCSTR __RPC_FAR *nick, + LPCSTR __RPC_FAR *pass) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetNick( + int num, + LPCSTR nick, + LPCSTR pass, + int domangle) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetLobbyCount( + int __RPC_FAR *count) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestRawMessage( + LPCSTR ircmsg) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetAttributeValue( + LPCSTR attrib, + LPCSTR __RPC_FAR *value) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetAttributeValue( + LPCSTR attrib, + LPCSTR value) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetChannelExInfo( + LPCSTR info) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE StopAutoping( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IChatVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IChat __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IChat __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PumpMessages )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestServerList )( + IChat __RPC_FAR * This, + /* [in] */ unsigned long SKU, + /* [in] */ unsigned long current_version, + /* [in] */ LPCSTR loginname, + /* [in] */ LPCSTR password, + /* [in] */ int timeout); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestConnection )( + IChat __RPC_FAR * This, + /* [in] */ Server __RPC_FAR *server, + /* [in] */ int timeout, + int domangle); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelList )( + IChat __RPC_FAR * This, + /* [in] */ int channelType, + /* [in] */ int autoping); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelCreate )( + IChat __RPC_FAR * This, + /* [in] */ Channel __RPC_FAR *channel); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelJoin )( + IChat __RPC_FAR * This, + /* [in] */ Channel __RPC_FAR *channel); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelLeave )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUserList )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPublicMessage )( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPrivateMessage )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLogout )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPrivateGameOptions )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR options); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPublicGameOptions )( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR options); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPublicAction )( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR action); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPrivateAction )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR action); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestGameStart )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelTopic )( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR topic); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetVersion )( + IChat __RPC_FAR * This, + /* [in] */ unsigned long __RPC_FAR *version); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUserKick )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUserIP )( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetGametypeInfo )( + IChat __RPC_FAR * This, + unsigned int gtype, + int icon_size, + unsigned char __RPC_FAR *__RPC_FAR *bitmap, + int __RPC_FAR *bmp_bytes, + LPCSTR __RPC_FAR *name, + LPCSTR __RPC_FAR *URL); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestFind )( + IChat __RPC_FAR * This, + User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPage )( + IChat __RPC_FAR * This, + User __RPC_FAR *user, + LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetFindPage )( + IChat __RPC_FAR * This, + int findOn, + int pageOn); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetSquelch )( + IChat __RPC_FAR * This, + User __RPC_FAR *user, + int squelch); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSquelch )( + IChat __RPC_FAR * This, + User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetChannelFilter )( + IChat __RPC_FAR * This, + int channelType); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestGameEnd )( + IChat __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetLangFilter )( + IChat __RPC_FAR * This, + int onoff); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelBan )( + IChat __RPC_FAR * This, + LPCSTR name, + int ban); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetGametypeList )( + IChat __RPC_FAR * This, + LPCSTR __RPC_FAR *list); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetHelpURL )( + IChat __RPC_FAR * This, + LPCSTR __RPC_FAR *url); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetProductSKU )( + IChat __RPC_FAR * This, + unsigned long SKU); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNick )( + IChat __RPC_FAR * This, + int num, + LPCSTR __RPC_FAR *nick, + LPCSTR __RPC_FAR *pass); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetNick )( + IChat __RPC_FAR * This, + int num, + LPCSTR nick, + LPCSTR pass, + int domangle); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLobbyCount )( + IChat __RPC_FAR * This, + int __RPC_FAR *count); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestRawMessage )( + IChat __RPC_FAR * This, + LPCSTR ircmsg); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeValue )( + IChat __RPC_FAR * This, + LPCSTR attrib, + LPCSTR __RPC_FAR *value); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetAttributeValue )( + IChat __RPC_FAR * This, + LPCSTR attrib, + LPCSTR value); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetChannelExInfo )( + IChat __RPC_FAR * This, + LPCSTR info); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *StopAutoping )( + IChat __RPC_FAR * This); + + END_INTERFACE + } IChatVtbl; + + interface IChat + { + CONST_VTBL struct IChatVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IChat_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IChat_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IChat_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IChat_PumpMessages(This) \ + (This)->lpVtbl -> PumpMessages(This) + +#define IChat_RequestServerList(This,SKU,current_version,loginname,password,timeout) \ + (This)->lpVtbl -> RequestServerList(This,SKU,current_version,loginname,password,timeout) + +#define IChat_RequestConnection(This,server,timeout,domangle) \ + (This)->lpVtbl -> RequestConnection(This,server,timeout,domangle) + +#define IChat_RequestChannelList(This,channelType,autoping) \ + (This)->lpVtbl -> RequestChannelList(This,channelType,autoping) + +#define IChat_RequestChannelCreate(This,channel) \ + (This)->lpVtbl -> RequestChannelCreate(This,channel) + +#define IChat_RequestChannelJoin(This,channel) \ + (This)->lpVtbl -> RequestChannelJoin(This,channel) + +#define IChat_RequestChannelLeave(This) \ + (This)->lpVtbl -> RequestChannelLeave(This) + +#define IChat_RequestUserList(This) \ + (This)->lpVtbl -> RequestUserList(This) + +#define IChat_RequestPublicMessage(This,message) \ + (This)->lpVtbl -> RequestPublicMessage(This,message) + +#define IChat_RequestPrivateMessage(This,users,message) \ + (This)->lpVtbl -> RequestPrivateMessage(This,users,message) + +#define IChat_RequestLogout(This) \ + (This)->lpVtbl -> RequestLogout(This) + +#define IChat_RequestPrivateGameOptions(This,users,options) \ + (This)->lpVtbl -> RequestPrivateGameOptions(This,users,options) + +#define IChat_RequestPublicGameOptions(This,options) \ + (This)->lpVtbl -> RequestPublicGameOptions(This,options) + +#define IChat_RequestPublicAction(This,action) \ + (This)->lpVtbl -> RequestPublicAction(This,action) + +#define IChat_RequestPrivateAction(This,users,action) \ + (This)->lpVtbl -> RequestPrivateAction(This,users,action) + +#define IChat_RequestGameStart(This,users) \ + (This)->lpVtbl -> RequestGameStart(This,users) + +#define IChat_RequestChannelTopic(This,topic) \ + (This)->lpVtbl -> RequestChannelTopic(This,topic) + +#define IChat_GetVersion(This,version) \ + (This)->lpVtbl -> GetVersion(This,version) + +#define IChat_RequestUserKick(This,user) \ + (This)->lpVtbl -> RequestUserKick(This,user) + +#define IChat_RequestUserIP(This,user) \ + (This)->lpVtbl -> RequestUserIP(This,user) + +#define IChat_GetGametypeInfo(This,gtype,icon_size,bitmap,bmp_bytes,name,URL) \ + (This)->lpVtbl -> GetGametypeInfo(This,gtype,icon_size,bitmap,bmp_bytes,name,URL) + +#define IChat_RequestFind(This,user) \ + (This)->lpVtbl -> RequestFind(This,user) + +#define IChat_RequestPage(This,user,message) \ + (This)->lpVtbl -> RequestPage(This,user,message) + +#define IChat_SetFindPage(This,findOn,pageOn) \ + (This)->lpVtbl -> SetFindPage(This,findOn,pageOn) + +#define IChat_SetSquelch(This,user,squelch) \ + (This)->lpVtbl -> SetSquelch(This,user,squelch) + +#define IChat_GetSquelch(This,user) \ + (This)->lpVtbl -> GetSquelch(This,user) + +#define IChat_SetChannelFilter(This,channelType) \ + (This)->lpVtbl -> SetChannelFilter(This,channelType) + +#define IChat_RequestGameEnd(This) \ + (This)->lpVtbl -> RequestGameEnd(This) + +#define IChat_SetLangFilter(This,onoff) \ + (This)->lpVtbl -> SetLangFilter(This,onoff) + +#define IChat_RequestChannelBan(This,name,ban) \ + (This)->lpVtbl -> RequestChannelBan(This,name,ban) + +#define IChat_GetGametypeList(This,list) \ + (This)->lpVtbl -> GetGametypeList(This,list) + +#define IChat_GetHelpURL(This,url) \ + (This)->lpVtbl -> GetHelpURL(This,url) + +#define IChat_SetProductSKU(This,SKU) \ + (This)->lpVtbl -> SetProductSKU(This,SKU) + +#define IChat_GetNick(This,num,nick,pass) \ + (This)->lpVtbl -> GetNick(This,num,nick,pass) + +#define IChat_SetNick(This,num,nick,pass,domangle) \ + (This)->lpVtbl -> SetNick(This,num,nick,pass,domangle) + +#define IChat_GetLobbyCount(This,count) \ + (This)->lpVtbl -> GetLobbyCount(This,count) + +#define IChat_RequestRawMessage(This,ircmsg) \ + (This)->lpVtbl -> RequestRawMessage(This,ircmsg) + +#define IChat_GetAttributeValue(This,attrib,value) \ + (This)->lpVtbl -> GetAttributeValue(This,attrib,value) + +#define IChat_SetAttributeValue(This,attrib,value) \ + (This)->lpVtbl -> SetAttributeValue(This,attrib,value) + +#define IChat_SetChannelExInfo(This,info) \ + (This)->lpVtbl -> SetChannelExInfo(This,info) + +#define IChat_StopAutoping(This) \ + (This)->lpVtbl -> StopAutoping(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_PumpMessages_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_PumpMessages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestServerList_Proxy( + IChat __RPC_FAR * This, + /* [in] */ unsigned long SKU, + /* [in] */ unsigned long current_version, + /* [in] */ LPCSTR loginname, + /* [in] */ LPCSTR password, + /* [in] */ int timeout); + + +void __RPC_STUB IChat_RequestServerList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestConnection_Proxy( + IChat __RPC_FAR * This, + /* [in] */ Server __RPC_FAR *server, + /* [in] */ int timeout, + int domangle); + + +void __RPC_STUB IChat_RequestConnection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelList_Proxy( + IChat __RPC_FAR * This, + /* [in] */ int channelType, + /* [in] */ int autoping); + + +void __RPC_STUB IChat_RequestChannelList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelCreate_Proxy( + IChat __RPC_FAR * This, + /* [in] */ Channel __RPC_FAR *channel); + + +void __RPC_STUB IChat_RequestChannelCreate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelJoin_Proxy( + IChat __RPC_FAR * This, + /* [in] */ Channel __RPC_FAR *channel); + + +void __RPC_STUB IChat_RequestChannelJoin_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelLeave_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_RequestChannelLeave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestUserList_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_RequestUserList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPublicMessage_Proxy( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR message); + + +void __RPC_STUB IChat_RequestPublicMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPrivateMessage_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR message); + + +void __RPC_STUB IChat_RequestPrivateMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestLogout_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_RequestLogout_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPrivateGameOptions_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR options); + + +void __RPC_STUB IChat_RequestPrivateGameOptions_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPublicGameOptions_Proxy( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR options); + + +void __RPC_STUB IChat_RequestPublicGameOptions_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPublicAction_Proxy( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR action); + + +void __RPC_STUB IChat_RequestPublicAction_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPrivateAction_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users, + /* [in] */ LPCSTR action); + + +void __RPC_STUB IChat_RequestPrivateAction_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestGameStart_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *users); + + +void __RPC_STUB IChat_RequestGameStart_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelTopic_Proxy( + IChat __RPC_FAR * This, + /* [in] */ LPCSTR topic); + + +void __RPC_STUB IChat_RequestChannelTopic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetVersion_Proxy( + IChat __RPC_FAR * This, + /* [in] */ unsigned long __RPC_FAR *version); + + +void __RPC_STUB IChat_GetVersion_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestUserKick_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChat_RequestUserKick_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestUserIP_Proxy( + IChat __RPC_FAR * This, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChat_RequestUserIP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetGametypeInfo_Proxy( + IChat __RPC_FAR * This, + unsigned int gtype, + int icon_size, + unsigned char __RPC_FAR *__RPC_FAR *bitmap, + int __RPC_FAR *bmp_bytes, + LPCSTR __RPC_FAR *name, + LPCSTR __RPC_FAR *URL); + + +void __RPC_STUB IChat_GetGametypeInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestFind_Proxy( + IChat __RPC_FAR * This, + User __RPC_FAR *user); + + +void __RPC_STUB IChat_RequestFind_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestPage_Proxy( + IChat __RPC_FAR * This, + User __RPC_FAR *user, + LPCSTR message); + + +void __RPC_STUB IChat_RequestPage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetFindPage_Proxy( + IChat __RPC_FAR * This, + int findOn, + int pageOn); + + +void __RPC_STUB IChat_SetFindPage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetSquelch_Proxy( + IChat __RPC_FAR * This, + User __RPC_FAR *user, + int squelch); + + +void __RPC_STUB IChat_SetSquelch_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetSquelch_Proxy( + IChat __RPC_FAR * This, + User __RPC_FAR *user); + + +void __RPC_STUB IChat_GetSquelch_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetChannelFilter_Proxy( + IChat __RPC_FAR * This, + int channelType); + + +void __RPC_STUB IChat_SetChannelFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestGameEnd_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_RequestGameEnd_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetLangFilter_Proxy( + IChat __RPC_FAR * This, + int onoff); + + +void __RPC_STUB IChat_SetLangFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestChannelBan_Proxy( + IChat __RPC_FAR * This, + LPCSTR name, + int ban); + + +void __RPC_STUB IChat_RequestChannelBan_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetGametypeList_Proxy( + IChat __RPC_FAR * This, + LPCSTR __RPC_FAR *list); + + +void __RPC_STUB IChat_GetGametypeList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetHelpURL_Proxy( + IChat __RPC_FAR * This, + LPCSTR __RPC_FAR *url); + + +void __RPC_STUB IChat_GetHelpURL_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetProductSKU_Proxy( + IChat __RPC_FAR * This, + unsigned long SKU); + + +void __RPC_STUB IChat_SetProductSKU_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetNick_Proxy( + IChat __RPC_FAR * This, + int num, + LPCSTR __RPC_FAR *nick, + LPCSTR __RPC_FAR *pass); + + +void __RPC_STUB IChat_GetNick_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetNick_Proxy( + IChat __RPC_FAR * This, + int num, + LPCSTR nick, + LPCSTR pass, + int domangle); + + +void __RPC_STUB IChat_SetNick_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetLobbyCount_Proxy( + IChat __RPC_FAR * This, + int __RPC_FAR *count); + + +void __RPC_STUB IChat_GetLobbyCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_RequestRawMessage_Proxy( + IChat __RPC_FAR * This, + LPCSTR ircmsg); + + +void __RPC_STUB IChat_RequestRawMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_GetAttributeValue_Proxy( + IChat __RPC_FAR * This, + LPCSTR attrib, + LPCSTR __RPC_FAR *value); + + +void __RPC_STUB IChat_GetAttributeValue_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetAttributeValue_Proxy( + IChat __RPC_FAR * This, + LPCSTR attrib, + LPCSTR value); + + +void __RPC_STUB IChat_SetAttributeValue_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_SetChannelExInfo_Proxy( + IChat __RPC_FAR * This, + LPCSTR info); + + +void __RPC_STUB IChat_SetChannelExInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat_StopAutoping_Proxy( + IChat __RPC_FAR * This); + + +void __RPC_STUB IChat_StopAutoping_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IChat_INTERFACE_DEFINED__ */ + + +#ifndef __IChatEvent_INTERFACE_DEFINED__ +#define __IChatEvent_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IChatEvent + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_IChatEvent; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("4DD3BAF6-7579-11D1-B1C6-006097176556") + IChatEvent : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnServerList( + /* [in] */ HRESULT res, + /* [in] */ Server __RPC_FAR *servers) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUpdateList( + /* [in] */ HRESULT res, + /* [in] */ Update __RPC_FAR *updates) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnServerError( + /* [in] */ HRESULT res, + /* [in] */ LPCSTR ircmsg) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnConnection( + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnMessageOfTheDay( + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelList( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channels) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelCreate( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelJoin( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelLeave( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelTopic( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ LPCSTR topic) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPrivateAction( + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR action) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPublicAction( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + User __RPC_FAR *user, + /* [in] */ LPCSTR action) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUserList( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPublicMessage( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPrivateMessage( + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnSystemMessage( + /* [in] */ HRESULT res, + /* [in] */ LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnNetStatus( + /* [in] */ HRESULT res) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnLogout( + /* [in] */ HRESULT status, + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPrivateGameOptions( + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPublicGameOptions( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnGameStart( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users, + /* [in] */ int gameid) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUserKick( + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *kicked, + /* [in] */ User __RPC_FAR *kicker) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUserIP( + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnFind( + HRESULT res, + Channel __RPC_FAR *chan) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPageSend( + HRESULT res) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnPaged( + HRESULT res, + User __RPC_FAR *user, + LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnServerBannedYou( + HRESULT res, + time_t bannedTill) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUserFlags( + HRESULT res, + LPCSTR name, + unsigned int flags, + unsigned int mask) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelBan( + HRESULT res, + LPCSTR name, + int banned) = 0; + + }; + +#else /* C style interface */ + + typedef struct IChatEventVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IChatEvent __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IChatEvent __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IChatEvent __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnServerList )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Server __RPC_FAR *servers); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUpdateList )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Update __RPC_FAR *updates); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnServerError )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR ircmsg); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnConnection )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnMessageOfTheDay )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelList )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channels); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelCreate )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelJoin )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelLeave )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelTopic )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ LPCSTR topic); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPrivateAction )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR action); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPublicAction )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + User __RPC_FAR *user, + /* [in] */ LPCSTR action); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUserList )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPublicMessage )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPrivateMessage )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnSystemMessage )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNetStatus )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnLogout )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT status, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPrivateGameOptions )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPublicGameOptions )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnGameStart )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users, + /* [in] */ int gameid); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUserKick )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *kicked, + /* [in] */ User __RPC_FAR *kicker); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUserIP )( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnFind )( + IChatEvent __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPageSend )( + IChatEvent __RPC_FAR * This, + HRESULT res); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPaged )( + IChatEvent __RPC_FAR * This, + HRESULT res, + User __RPC_FAR *user, + LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnServerBannedYou )( + IChatEvent __RPC_FAR * This, + HRESULT res, + time_t bannedTill); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUserFlags )( + IChatEvent __RPC_FAR * This, + HRESULT res, + LPCSTR name, + unsigned int flags, + unsigned int mask); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelBan )( + IChatEvent __RPC_FAR * This, + HRESULT res, + LPCSTR name, + int banned); + + END_INTERFACE + } IChatEventVtbl; + + interface IChatEvent + { + CONST_VTBL struct IChatEventVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IChatEvent_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IChatEvent_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IChatEvent_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IChatEvent_OnServerList(This,res,servers) \ + (This)->lpVtbl -> OnServerList(This,res,servers) + +#define IChatEvent_OnUpdateList(This,res,updates) \ + (This)->lpVtbl -> OnUpdateList(This,res,updates) + +#define IChatEvent_OnServerError(This,res,ircmsg) \ + (This)->lpVtbl -> OnServerError(This,res,ircmsg) + +#define IChatEvent_OnConnection(This,res,motd) \ + (This)->lpVtbl -> OnConnection(This,res,motd) + +#define IChatEvent_OnMessageOfTheDay(This,res,motd) \ + (This)->lpVtbl -> OnMessageOfTheDay(This,res,motd) + +#define IChatEvent_OnChannelList(This,res,channels) \ + (This)->lpVtbl -> OnChannelList(This,res,channels) + +#define IChatEvent_OnChannelCreate(This,res,channel) \ + (This)->lpVtbl -> OnChannelCreate(This,res,channel) + +#define IChatEvent_OnChannelJoin(This,res,channel,user) \ + (This)->lpVtbl -> OnChannelJoin(This,res,channel,user) + +#define IChatEvent_OnChannelLeave(This,res,channel,user) \ + (This)->lpVtbl -> OnChannelLeave(This,res,channel,user) + +#define IChatEvent_OnChannelTopic(This,res,channel,topic) \ + (This)->lpVtbl -> OnChannelTopic(This,res,channel,topic) + +#define IChatEvent_OnPrivateAction(This,res,user,action) \ + (This)->lpVtbl -> OnPrivateAction(This,res,user,action) + +#define IChatEvent_OnPublicAction(This,res,channel,user,action) \ + (This)->lpVtbl -> OnPublicAction(This,res,channel,user,action) + +#define IChatEvent_OnUserList(This,res,channel,users) \ + (This)->lpVtbl -> OnUserList(This,res,channel,users) + +#define IChatEvent_OnPublicMessage(This,res,channel,user,message) \ + (This)->lpVtbl -> OnPublicMessage(This,res,channel,user,message) + +#define IChatEvent_OnPrivateMessage(This,res,user,message) \ + (This)->lpVtbl -> OnPrivateMessage(This,res,user,message) + +#define IChatEvent_OnSystemMessage(This,res,message) \ + (This)->lpVtbl -> OnSystemMessage(This,res,message) + +#define IChatEvent_OnNetStatus(This,res) \ + (This)->lpVtbl -> OnNetStatus(This,res) + +#define IChatEvent_OnLogout(This,status,user) \ + (This)->lpVtbl -> OnLogout(This,status,user) + +#define IChatEvent_OnPrivateGameOptions(This,res,user,options) \ + (This)->lpVtbl -> OnPrivateGameOptions(This,res,user,options) + +#define IChatEvent_OnPublicGameOptions(This,res,channel,user,options) \ + (This)->lpVtbl -> OnPublicGameOptions(This,res,channel,user,options) + +#define IChatEvent_OnGameStart(This,res,channel,users,gameid) \ + (This)->lpVtbl -> OnGameStart(This,res,channel,users,gameid) + +#define IChatEvent_OnUserKick(This,res,channel,kicked,kicker) \ + (This)->lpVtbl -> OnUserKick(This,res,channel,kicked,kicker) + +#define IChatEvent_OnUserIP(This,res,user) \ + (This)->lpVtbl -> OnUserIP(This,res,user) + +#define IChatEvent_OnFind(This,res,chan) \ + (This)->lpVtbl -> OnFind(This,res,chan) + +#define IChatEvent_OnPageSend(This,res) \ + (This)->lpVtbl -> OnPageSend(This,res) + +#define IChatEvent_OnPaged(This,res,user,message) \ + (This)->lpVtbl -> OnPaged(This,res,user,message) + +#define IChatEvent_OnServerBannedYou(This,res,bannedTill) \ + (This)->lpVtbl -> OnServerBannedYou(This,res,bannedTill) + +#define IChatEvent_OnUserFlags(This,res,name,flags,mask) \ + (This)->lpVtbl -> OnUserFlags(This,res,name,flags,mask) + +#define IChatEvent_OnChannelBan(This,res,name,banned) \ + (This)->lpVtbl -> OnChannelBan(This,res,name,banned) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnServerList_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Server __RPC_FAR *servers); + + +void __RPC_STUB IChatEvent_OnServerList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnUpdateList_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Update __RPC_FAR *updates); + + +void __RPC_STUB IChatEvent_OnUpdateList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnServerError_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR ircmsg); + + +void __RPC_STUB IChatEvent_OnServerError_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnConnection_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd); + + +void __RPC_STUB IChatEvent_OnConnection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnMessageOfTheDay_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR motd); + + +void __RPC_STUB IChatEvent_OnMessageOfTheDay_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelList_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channels); + + +void __RPC_STUB IChatEvent_OnChannelList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelCreate_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel); + + +void __RPC_STUB IChatEvent_OnChannelCreate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelJoin_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChatEvent_OnChannelJoin_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelLeave_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChatEvent_OnChannelLeave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelTopic_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ LPCSTR topic); + + +void __RPC_STUB IChatEvent_OnChannelTopic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPrivateAction_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR action); + + +void __RPC_STUB IChatEvent_OnPrivateAction_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPublicAction_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + User __RPC_FAR *user, + /* [in] */ LPCSTR action); + + +void __RPC_STUB IChatEvent_OnPublicAction_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnUserList_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users); + + +void __RPC_STUB IChatEvent_OnUserList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPublicMessage_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message); + + +void __RPC_STUB IChatEvent_OnPublicMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPrivateMessage_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR message); + + +void __RPC_STUB IChatEvent_OnPrivateMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnSystemMessage_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ LPCSTR message); + + +void __RPC_STUB IChatEvent_OnSystemMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnNetStatus_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res); + + +void __RPC_STUB IChatEvent_OnNetStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnLogout_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT status, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChatEvent_OnLogout_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPrivateGameOptions_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options); + + +void __RPC_STUB IChatEvent_OnPrivateGameOptions_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPublicGameOptions_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *user, + /* [in] */ LPCSTR options); + + +void __RPC_STUB IChatEvent_OnPublicGameOptions_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnGameStart_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *users, + /* [in] */ int gameid); + + +void __RPC_STUB IChatEvent_OnGameStart_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnUserKick_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ Channel __RPC_FAR *channel, + /* [in] */ User __RPC_FAR *kicked, + /* [in] */ User __RPC_FAR *kicker); + + +void __RPC_STUB IChatEvent_OnUserKick_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnUserIP_Proxy( + IChatEvent __RPC_FAR * This, + /* [in] */ HRESULT res, + /* [in] */ User __RPC_FAR *user); + + +void __RPC_STUB IChatEvent_OnUserIP_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnFind_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan); + + +void __RPC_STUB IChatEvent_OnFind_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPageSend_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res); + + +void __RPC_STUB IChatEvent_OnPageSend_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnPaged_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res, + User __RPC_FAR *user, + LPCSTR message); + + +void __RPC_STUB IChatEvent_OnPaged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnServerBannedYou_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res, + time_t bannedTill); + + +void __RPC_STUB IChatEvent_OnServerBannedYou_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnUserFlags_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res, + LPCSTR name, + unsigned int flags, + unsigned int mask); + + +void __RPC_STUB IChatEvent_OnUserFlags_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChatEvent_OnChannelBan_Proxy( + IChatEvent __RPC_FAR * This, + HRESULT res, + LPCSTR name, + int banned); + + +void __RPC_STUB IChatEvent_OnChannelBan_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IChatEvent_INTERFACE_DEFINED__ */ + + +#ifndef __IDownload_INTERFACE_DEFINED__ +#define __IDownload_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IDownload + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [unique][helpstring][dual][uuid][object] */ + + + +EXTERN_C const IID IID_IDownload; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("0BF5FCEB-9F03-11D1-9DC7-006097C54321") + IDownload : public IUnknown + { + public: + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE DownloadFile( + LPCSTR server, + LPCSTR login, + LPCSTR password, + LPCSTR file, + LPCSTR localfile, + LPCSTR regkey) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE Abort( void) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE PumpMessages( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IDownloadVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IDownload __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IDownload __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IDownload __RPC_FAR * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DownloadFile )( + IDownload __RPC_FAR * This, + LPCSTR server, + LPCSTR login, + LPCSTR password, + LPCSTR file, + LPCSTR localfile, + LPCSTR regkey); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Abort )( + IDownload __RPC_FAR * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PumpMessages )( + IDownload __RPC_FAR * This); + + END_INTERFACE + } IDownloadVtbl; + + interface IDownload + { + CONST_VTBL struct IDownloadVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDownload_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IDownload_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IDownload_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IDownload_DownloadFile(This,server,login,password,file,localfile,regkey) \ + (This)->lpVtbl -> DownloadFile(This,server,login,password,file,localfile,regkey) + +#define IDownload_Abort(This) \ + (This)->lpVtbl -> Abort(This) + +#define IDownload_PumpMessages(This) \ + (This)->lpVtbl -> PumpMessages(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE IDownload_DownloadFile_Proxy( + IDownload __RPC_FAR * This, + LPCSTR server, + LPCSTR login, + LPCSTR password, + LPCSTR file, + LPCSTR localfile, + LPCSTR regkey); + + +void __RPC_STUB IDownload_DownloadFile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE IDownload_Abort_Proxy( + IDownload __RPC_FAR * This); + + +void __RPC_STUB IDownload_Abort_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE IDownload_PumpMessages_Proxy( + IDownload __RPC_FAR * This); + + +void __RPC_STUB IDownload_PumpMessages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IDownload_INTERFACE_DEFINED__ */ + + +#ifndef __IDownloadEvent_INTERFACE_DEFINED__ +#define __IDownloadEvent_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IDownloadEvent + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_IDownloadEvent; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("6869E99D-9FB4-11D1-9DC8-006097C54321") + IDownloadEvent : public IUnknown + { + public: + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE OnEnd( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnError( + int error) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnProgressUpdate( + int bytesread, + int totalsize, + int timetaken, + int timeleft) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnQueryResume( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnStatusUpdate( + int status) = 0; + + }; + +#else /* C style interface */ + + typedef struct IDownloadEventVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IDownloadEvent __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IDownloadEvent __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IDownloadEvent __RPC_FAR * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnEnd )( + IDownloadEvent __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnError )( + IDownloadEvent __RPC_FAR * This, + int error); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnProgressUpdate )( + IDownloadEvent __RPC_FAR * This, + int bytesread, + int totalsize, + int timetaken, + int timeleft); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnQueryResume )( + IDownloadEvent __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnStatusUpdate )( + IDownloadEvent __RPC_FAR * This, + int status); + + END_INTERFACE + } IDownloadEventVtbl; + + interface IDownloadEvent + { + CONST_VTBL struct IDownloadEventVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IDownloadEvent_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IDownloadEvent_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IDownloadEvent_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IDownloadEvent_OnEnd(This) \ + (This)->lpVtbl -> OnEnd(This) + +#define IDownloadEvent_OnError(This,error) \ + (This)->lpVtbl -> OnError(This,error) + +#define IDownloadEvent_OnProgressUpdate(This,bytesread,totalsize,timetaken,timeleft) \ + (This)->lpVtbl -> OnProgressUpdate(This,bytesread,totalsize,timetaken,timeleft) + +#define IDownloadEvent_OnQueryResume(This) \ + (This)->lpVtbl -> OnQueryResume(This) + +#define IDownloadEvent_OnStatusUpdate(This,status) \ + (This)->lpVtbl -> OnStatusUpdate(This,status) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE IDownloadEvent_OnEnd_Proxy( + IDownloadEvent __RPC_FAR * This); + + +void __RPC_STUB IDownloadEvent_OnEnd_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IDownloadEvent_OnError_Proxy( + IDownloadEvent __RPC_FAR * This, + int error); + + +void __RPC_STUB IDownloadEvent_OnError_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IDownloadEvent_OnProgressUpdate_Proxy( + IDownloadEvent __RPC_FAR * This, + int bytesread, + int totalsize, + int timetaken, + int timeleft); + + +void __RPC_STUB IDownloadEvent_OnProgressUpdate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IDownloadEvent_OnQueryResume_Proxy( + IDownloadEvent __RPC_FAR * This); + + +void __RPC_STUB IDownloadEvent_OnQueryResume_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IDownloadEvent_OnStatusUpdate_Proxy( + IDownloadEvent __RPC_FAR * This, + int status); + + +void __RPC_STUB IDownloadEvent_OnStatusUpdate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IDownloadEvent_INTERFACE_DEFINED__ */ + + +#ifndef __INetUtil_INTERFACE_DEFINED__ +#define __INetUtil_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: INetUtil + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_INetUtil; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("B832B0AA-A7D3-11D1-97C3-00609706FA0C") + INetUtil : public IUnknown + { + public: + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE RequestGameresSend( + LPCSTR host, + int port, + unsigned char __RPC_FAR *data, + int length) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE RequestLadderSearch( + LPCSTR host, + int port, + LPCSTR key, + unsigned long SKU, + int team, + int cond, + int sort, + int number, + int leading) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE RequestLadderList( + LPCSTR host, + int port, + LPCSTR keys, + unsigned long SKU, + int team, + int cond, + int sort) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE RequestPing( + LPCSTR host, + int timeout, + int __RPC_FAR *handle) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE PumpMessages( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetAvgPing( + unsigned long ip, + int __RPC_FAR *avg) = 0; + + }; + +#else /* C style interface */ + + typedef struct INetUtilVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + INetUtil __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + INetUtil __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + INetUtil __RPC_FAR * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestGameresSend )( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + unsigned char __RPC_FAR *data, + int length); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLadderSearch )( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + LPCSTR key, + unsigned long SKU, + int team, + int cond, + int sort, + int number, + int leading); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLadderList )( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + LPCSTR keys, + unsigned long SKU, + int team, + int cond, + int sort); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestPing )( + INetUtil __RPC_FAR * This, + LPCSTR host, + int timeout, + int __RPC_FAR *handle); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PumpMessages )( + INetUtil __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAvgPing )( + INetUtil __RPC_FAR * This, + unsigned long ip, + int __RPC_FAR *avg); + + END_INTERFACE + } INetUtilVtbl; + + interface INetUtil + { + CONST_VTBL struct INetUtilVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INetUtil_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INetUtil_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INetUtil_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INetUtil_RequestGameresSend(This,host,port,data,length) \ + (This)->lpVtbl -> RequestGameresSend(This,host,port,data,length) + +#define INetUtil_RequestLadderSearch(This,host,port,key,SKU,team,cond,sort,number,leading) \ + (This)->lpVtbl -> RequestLadderSearch(This,host,port,key,SKU,team,cond,sort,number,leading) + +#define INetUtil_RequestLadderList(This,host,port,keys,SKU,team,cond,sort) \ + (This)->lpVtbl -> RequestLadderList(This,host,port,keys,SKU,team,cond,sort) + +#define INetUtil_RequestPing(This,host,timeout,handle) \ + (This)->lpVtbl -> RequestPing(This,host,timeout,handle) + +#define INetUtil_PumpMessages(This) \ + (This)->lpVtbl -> PumpMessages(This) + +#define INetUtil_GetAvgPing(This,ip,avg) \ + (This)->lpVtbl -> GetAvgPing(This,ip,avg) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtil_RequestGameresSend_Proxy( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + unsigned char __RPC_FAR *data, + int length); + + +void __RPC_STUB INetUtil_RequestGameresSend_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtil_RequestLadderSearch_Proxy( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + LPCSTR key, + unsigned long SKU, + int team, + int cond, + int sort, + int number, + int leading); + + +void __RPC_STUB INetUtil_RequestLadderSearch_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtil_RequestLadderList_Proxy( + INetUtil __RPC_FAR * This, + LPCSTR host, + int port, + LPCSTR keys, + unsigned long SKU, + int team, + int cond, + int sort); + + +void __RPC_STUB INetUtil_RequestLadderList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtil_RequestPing_Proxy( + INetUtil __RPC_FAR * This, + LPCSTR host, + int timeout, + int __RPC_FAR *handle); + + +void __RPC_STUB INetUtil_RequestPing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtil_PumpMessages_Proxy( + INetUtil __RPC_FAR * This); + + +void __RPC_STUB INetUtil_PumpMessages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE INetUtil_GetAvgPing_Proxy( + INetUtil __RPC_FAR * This, + unsigned long ip, + int __RPC_FAR *avg); + + +void __RPC_STUB INetUtil_GetAvgPing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INetUtil_INTERFACE_DEFINED__ */ + + +#ifndef __INetUtilEvent_INTERFACE_DEFINED__ +#define __INetUtilEvent_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: INetUtilEvent + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_INetUtilEvent; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("B832B0AC-A7D3-11D1-97C3-00609706FA0C") + INetUtilEvent : public IUnknown + { + public: + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE OnPing( + HRESULT res, + int time, + unsigned long ip, + int handle) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE OnLadderList( + HRESULT res, + /* [in] */ Ladder __RPC_FAR *list, + int totalCount, + long timeStamp, + int keyRung) = 0; + + virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE OnGameresSent( + HRESULT res) = 0; + + }; + +#else /* C style interface */ + + typedef struct INetUtilEventVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + INetUtilEvent __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + INetUtilEvent __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + INetUtilEvent __RPC_FAR * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPing )( + INetUtilEvent __RPC_FAR * This, + HRESULT res, + int time, + unsigned long ip, + int handle); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnLadderList )( + INetUtilEvent __RPC_FAR * This, + HRESULT res, + /* [in] */ Ladder __RPC_FAR *list, + int totalCount, + long timeStamp, + int keyRung); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnGameresSent )( + INetUtilEvent __RPC_FAR * This, + HRESULT res); + + END_INTERFACE + } INetUtilEventVtbl; + + interface INetUtilEvent + { + CONST_VTBL struct INetUtilEventVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define INetUtilEvent_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define INetUtilEvent_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define INetUtilEvent_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define INetUtilEvent_OnPing(This,res,time,ip,handle) \ + (This)->lpVtbl -> OnPing(This,res,time,ip,handle) + +#define INetUtilEvent_OnLadderList(This,res,list,totalCount,timeStamp,keyRung) \ + (This)->lpVtbl -> OnLadderList(This,res,list,totalCount,timeStamp,keyRung) + +#define INetUtilEvent_OnGameresSent(This,res) \ + (This)->lpVtbl -> OnGameresSent(This,res) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtilEvent_OnPing_Proxy( + INetUtilEvent __RPC_FAR * This, + HRESULT res, + int time, + unsigned long ip, + int handle); + + +void __RPC_STUB INetUtilEvent_OnPing_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtilEvent_OnLadderList_Proxy( + INetUtilEvent __RPC_FAR * This, + HRESULT res, + /* [in] */ Ladder __RPC_FAR *list, + int totalCount, + long timeStamp, + int keyRung); + + +void __RPC_STUB INetUtilEvent_OnLadderList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring][id] */ HRESULT STDMETHODCALLTYPE INetUtilEvent_OnGameresSent_Proxy( + INetUtilEvent __RPC_FAR * This, + HRESULT res); + + +void __RPC_STUB INetUtilEvent_OnGameresSent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __INetUtilEvent_INTERFACE_DEFINED__ */ + + +#ifndef __IChat2_INTERFACE_DEFINED__ +#define __IChat2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IChat2 + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + +typedef unsigned long GID; + + +enum GTYPE_ + { SERVER = 0, + CHANNEL = 1, + CLIENT = 2 + }; +typedef enum GTYPE_ GTYPE; + + +enum CHAN_CTYPE_ + { ALLEXIT = 0, + CREATOREXIT = 1, + CLOSEC = 2 + }; +typedef enum CHAN_CTYPE_ CHAN_CTYPE; + + +EXTERN_C const IID IID_IChat2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("8B938190-EF3F-11D1-9808-00609706FA0C") + IChat2 : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE PumpMessages( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestConnection( + Server __RPC_FAR *server, + int timeout) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestMessage( + GID who, + LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTypeFromGID( + GID id, + GTYPE __RPC_FAR *type) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelList( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelJoin( + LPCSTR name) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelLeave( + Channel __RPC_FAR *chan) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestUserList( + Channel __RPC_FAR *chan) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestLogout( void) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestChannelCreate( + Channel __RPC_FAR *chan) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RequestRawCmd( + LPCSTR cmd) = 0; + + }; + +#else /* C style interface */ + + typedef struct IChat2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IChat2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IChat2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IChat2 __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PumpMessages )( + IChat2 __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestConnection )( + IChat2 __RPC_FAR * This, + Server __RPC_FAR *server, + int timeout); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestMessage )( + IChat2 __RPC_FAR * This, + GID who, + LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeFromGID )( + IChat2 __RPC_FAR * This, + GID id, + GTYPE __RPC_FAR *type); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelList )( + IChat2 __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelJoin )( + IChat2 __RPC_FAR * This, + LPCSTR name); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelLeave )( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUserList )( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLogout )( + IChat2 __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestChannelCreate )( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestRawCmd )( + IChat2 __RPC_FAR * This, + LPCSTR cmd); + + END_INTERFACE + } IChat2Vtbl; + + interface IChat2 + { + CONST_VTBL struct IChat2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IChat2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IChat2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IChat2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IChat2_PumpMessages(This) \ + (This)->lpVtbl -> PumpMessages(This) + +#define IChat2_RequestConnection(This,server,timeout) \ + (This)->lpVtbl -> RequestConnection(This,server,timeout) + +#define IChat2_RequestMessage(This,who,message) \ + (This)->lpVtbl -> RequestMessage(This,who,message) + +#define IChat2_GetTypeFromGID(This,id,type) \ + (This)->lpVtbl -> GetTypeFromGID(This,id,type) + +#define IChat2_RequestChannelList(This) \ + (This)->lpVtbl -> RequestChannelList(This) + +#define IChat2_RequestChannelJoin(This,name) \ + (This)->lpVtbl -> RequestChannelJoin(This,name) + +#define IChat2_RequestChannelLeave(This,chan) \ + (This)->lpVtbl -> RequestChannelLeave(This,chan) + +#define IChat2_RequestUserList(This,chan) \ + (This)->lpVtbl -> RequestUserList(This,chan) + +#define IChat2_RequestLogout(This) \ + (This)->lpVtbl -> RequestLogout(This) + +#define IChat2_RequestChannelCreate(This,chan) \ + (This)->lpVtbl -> RequestChannelCreate(This,chan) + +#define IChat2_RequestRawCmd(This,cmd) \ + (This)->lpVtbl -> RequestRawCmd(This,cmd) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_PumpMessages_Proxy( + IChat2 __RPC_FAR * This); + + +void __RPC_STUB IChat2_PumpMessages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestConnection_Proxy( + IChat2 __RPC_FAR * This, + Server __RPC_FAR *server, + int timeout); + + +void __RPC_STUB IChat2_RequestConnection_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestMessage_Proxy( + IChat2 __RPC_FAR * This, + GID who, + LPCSTR message); + + +void __RPC_STUB IChat2_RequestMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_GetTypeFromGID_Proxy( + IChat2 __RPC_FAR * This, + GID id, + GTYPE __RPC_FAR *type); + + +void __RPC_STUB IChat2_GetTypeFromGID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestChannelList_Proxy( + IChat2 __RPC_FAR * This); + + +void __RPC_STUB IChat2_RequestChannelList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestChannelJoin_Proxy( + IChat2 __RPC_FAR * This, + LPCSTR name); + + +void __RPC_STUB IChat2_RequestChannelJoin_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestChannelLeave_Proxy( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + +void __RPC_STUB IChat2_RequestChannelLeave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestUserList_Proxy( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + +void __RPC_STUB IChat2_RequestUserList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestLogout_Proxy( + IChat2 __RPC_FAR * This); + + +void __RPC_STUB IChat2_RequestLogout_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestChannelCreate_Proxy( + IChat2 __RPC_FAR * This, + Channel __RPC_FAR *chan); + + +void __RPC_STUB IChat2_RequestChannelCreate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2_RequestRawCmd_Proxy( + IChat2 __RPC_FAR * This, + LPCSTR cmd); + + +void __RPC_STUB IChat2_RequestRawCmd_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IChat2_INTERFACE_DEFINED__ */ + + +#ifndef __IChat2Event_INTERFACE_DEFINED__ +#define __IChat2Event_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IChat2Event + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [object][unique][helpstring][uuid] */ + + + +EXTERN_C const IID IID_IChat2Event; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("8B938192-EF3F-11D1-9808-00609706FA0C") + IChat2Event : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnNetStatus( + HRESULT res) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnMessage( + HRESULT res, + User __RPC_FAR *user, + LPCSTR message) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelList( + HRESULT res, + Channel __RPC_FAR *list) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelJoin( + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnLogin( + HRESULT res) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUserList( + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *users) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelLeave( + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnChannelCreate( + HRESULT res, + Channel __RPC_FAR *chan) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE OnUnknownLine( + HRESULT res, + LPCSTR line) = 0; + + }; + +#else /* C style interface */ + + typedef struct IChat2EventVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IChat2Event __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IChat2Event __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IChat2Event __RPC_FAR * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNetStatus )( + IChat2Event __RPC_FAR * This, + HRESULT res); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnMessage )( + IChat2Event __RPC_FAR * This, + HRESULT res, + User __RPC_FAR *user, + LPCSTR message); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelList )( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *list); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelJoin )( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnLogin )( + IChat2Event __RPC_FAR * This, + HRESULT res); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUserList )( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *users); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelLeave )( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChannelCreate )( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUnknownLine )( + IChat2Event __RPC_FAR * This, + HRESULT res, + LPCSTR line); + + END_INTERFACE + } IChat2EventVtbl; + + interface IChat2Event + { + CONST_VTBL struct IChat2EventVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IChat2Event_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IChat2Event_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IChat2Event_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IChat2Event_OnNetStatus(This,res) \ + (This)->lpVtbl -> OnNetStatus(This,res) + +#define IChat2Event_OnMessage(This,res,user,message) \ + (This)->lpVtbl -> OnMessage(This,res,user,message) + +#define IChat2Event_OnChannelList(This,res,list) \ + (This)->lpVtbl -> OnChannelList(This,res,list) + +#define IChat2Event_OnChannelJoin(This,res,chan,user) \ + (This)->lpVtbl -> OnChannelJoin(This,res,chan,user) + +#define IChat2Event_OnLogin(This,res) \ + (This)->lpVtbl -> OnLogin(This,res) + +#define IChat2Event_OnUserList(This,res,chan,users) \ + (This)->lpVtbl -> OnUserList(This,res,chan,users) + +#define IChat2Event_OnChannelLeave(This,res,chan,user) \ + (This)->lpVtbl -> OnChannelLeave(This,res,chan,user) + +#define IChat2Event_OnChannelCreate(This,res,chan) \ + (This)->lpVtbl -> OnChannelCreate(This,res,chan) + +#define IChat2Event_OnUnknownLine(This,res,line) \ + (This)->lpVtbl -> OnUnknownLine(This,res,line) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnNetStatus_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res); + + +void __RPC_STUB IChat2Event_OnNetStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnMessage_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + User __RPC_FAR *user, + LPCSTR message); + + +void __RPC_STUB IChat2Event_OnMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnChannelList_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *list); + + +void __RPC_STUB IChat2Event_OnChannelList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnChannelJoin_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user); + + +void __RPC_STUB IChat2Event_OnChannelJoin_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnLogin_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res); + + +void __RPC_STUB IChat2Event_OnLogin_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnUserList_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *users); + + +void __RPC_STUB IChat2Event_OnUserList_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnChannelLeave_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan, + User __RPC_FAR *user); + + +void __RPC_STUB IChat2Event_OnChannelLeave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnChannelCreate_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + Channel __RPC_FAR *chan); + + +void __RPC_STUB IChat2Event_OnChannelCreate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IChat2Event_OnUnknownLine_Proxy( + IChat2Event __RPC_FAR * This, + HRESULT res, + LPCSTR line); + + +void __RPC_STUB IChat2Event_OnUnknownLine_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IChat2Event_INTERFACE_DEFINED__ */ + + + +#ifndef __WOLAPILib_LIBRARY_DEFINED__ +#define __WOLAPILib_LIBRARY_DEFINED__ + +/**************************************** + * Generated header for library: WOLAPILib + * at Wed Jul 29 16:25:34 1998 + * using MIDL 3.01.75 + ****************************************/ +/* [helpstring][version][uuid] */ + + + +EXTERN_C const IID LIBID_WOLAPILib; + +#ifdef __cplusplus +EXTERN_C const CLSID CLSID_RTPatcher; + +class DECLSPEC_UUID("925CDEDF-71B9-11D1-B1C5-006097176556") +RTPatcher; +#endif + +#ifdef __cplusplus +EXTERN_C const CLSID CLSID_Chat; + +class DECLSPEC_UUID("4DD3BAF5-7579-11D1-B1C6-006097176556") +Chat; +#endif + +#ifdef __cplusplus +EXTERN_C const CLSID CLSID_Download; + +class DECLSPEC_UUID("BF6EA206-9E55-11D1-9DC6-006097C54321") +Download; +#endif + +#ifdef __cplusplus +EXTERN_C const CLSID CLSID_NetUtil; + +class DECLSPEC_UUID("B832B0AB-A7D3-11D1-97C3-00609706FA0C") +NetUtil; +#endif + +#ifdef __cplusplus +EXTERN_C const CLSID CLSID_Chat2; + +class DECLSPEC_UUID("8B938191-EF3F-11D1-9808-00609706FA0C") +Chat2; +#endif +#endif /* __WOLAPILib_LIBRARY_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/CODE/WOLAPI/WOLAPI_I.C b/CODE/WOLAPI/WOLAPI_I.C new file mode 100644 index 0000000..51428b9 --- /dev/null +++ b/CODE/WOLAPI/WOLAPI_I.C @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + +/* this file contains the actual definitions of */ +/* the IIDs and CLSIDs */ + +/* link this file in with the server and any clients */ + + +/* File created by MIDL compiler version 3.01.75 */ +/* at Wed Jul 29 16:25:34 1998 + */ +/* Compiler settings for WOLAPI.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: none +*/ +//@@MIDL_FILE_HEADING( ) +#ifdef __cplusplus +extern "C"{ +#endif + + +#ifndef __IID_DEFINED__ +#define __IID_DEFINED__ + +typedef struct _IID +{ + unsigned long x; + unsigned short s1; + unsigned short s2; + unsigned char c[8]; +} IID; + +#endif // __IID_DEFINED__ + +#ifndef CLSID_DEFINED +#define CLSID_DEFINED +typedef IID CLSID; +#endif // CLSID_DEFINED + +const IID IID_IRTPatcher = {0x925CDEDE,0x71B9,0x11D1,{0xB1,0xC5,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const IID IID_IRTPatcherEvent = {0x925CDEE3,0x71B9,0x11D1,{0xB1,0xC5,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const IID IID_IChat = {0x4DD3BAF4,0x7579,0x11D1,{0xB1,0xC6,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const IID IID_IChatEvent = {0x4DD3BAF6,0x7579,0x11D1,{0xB1,0xC6,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const IID IID_IDownload = {0x0BF5FCEB,0x9F03,0x11D1,{0x9D,0xC7,0x00,0x60,0x97,0xC5,0x43,0x21}}; + + +const IID IID_IDownloadEvent = {0x6869E99D,0x9FB4,0x11D1,{0x9D,0xC8,0x00,0x60,0x97,0xC5,0x43,0x21}}; + + +const IID IID_INetUtil = {0xB832B0AA,0xA7D3,0x11D1,{0x97,0xC3,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +const IID IID_INetUtilEvent = {0xB832B0AC,0xA7D3,0x11D1,{0x97,0xC3,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +const IID IID_IChat2 = {0x8B938190,0xEF3F,0x11D1,{0x98,0x08,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +const IID IID_IChat2Event = {0x8B938192,0xEF3F,0x11D1,{0x98,0x08,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +const IID LIBID_WOLAPILib = {0x925CDED1,0x71B9,0x11D1,{0xB1,0xC5,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const CLSID CLSID_RTPatcher = {0x925CDEDF,0x71B9,0x11D1,{0xB1,0xC5,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const CLSID CLSID_Chat = {0x4DD3BAF5,0x7579,0x11D1,{0xB1,0xC6,0x00,0x60,0x97,0x17,0x65,0x56}}; + + +const CLSID CLSID_Download = {0xBF6EA206,0x9E55,0x11D1,{0x9D,0xC6,0x00,0x60,0x97,0xC5,0x43,0x21}}; + + +const CLSID CLSID_NetUtil = {0xB832B0AB,0xA7D3,0x11D1,{0x97,0xC3,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +const CLSID CLSID_Chat2 = {0x8B938191,0xEF3F,0x11D1,{0x98,0x08,0x00,0x60,0x97,0x06,0xFA,0x0C}}; + + +#ifdef __cplusplus +} +#endif + diff --git a/CODE/WOLAPIOB.CPP b/CODE/WOLAPIOB.CPP new file mode 100644 index 0000000..48bc8cc --- /dev/null +++ b/CODE/WOLAPIOB.CPP @@ -0,0 +1,3435 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.cpp - Implementation of class WolapiObject. +// ajw 07/10/98 + +#include "WolapiOb.h" +#include "RAWolapi.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "ToolTip.h" +#include "Wol_GSup.h" + +#include "WolDebug.h" + +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ); + +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); + +extern bool cancel_current_msgbox; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +WolapiObject::WolapiObject() : pChat( NULL ), pDownload( NULL ), pChatSink( NULL ), pDownloadSink( NULL ), + dwChatAdvise( 0 ), dwDownloadAdvise( 0 ), pILChat( NULL ), pILUsers( NULL ), + CurrentLevel( WOL_LEVEL_TOP ), bChannelOwner( false ), GameTypeInfos( NULL ), + nGameTypeInfos( 0 ), bChatShownBefore( false ), + pILPlayers( NULL ), pChatSaveList( NULL ), iLobbyReturnAfterGame( -1 ), iLobbyLast( -1 ), + bFindEnabled( true ), + bPageEnabled( true ), + bLangFilter( true ), + bAllGamesShown( true ), + pGSupDlg( NULL ), + pShpDiscon( NULL ), + pShpLeave( NULL ), + pShpRefresh( NULL ), + pShpSquelch( NULL ), + pShpBan( NULL ), + pShpKick( NULL ), + pShpFindpage( NULL ), + pShpOptions( NULL ), + pShpLadder( NULL ), + pShpHelp( NULL ), + pShpBtnDiscon( NULL ), + pShpBtnLeave( NULL ), + pShpBtnRefresh( NULL ), + pShpBtnSquelch( NULL ), + pShpBtnBan( NULL ), + pShpBtnKick( NULL ), + pShpBtnFindpage( NULL ), + pShpBtnOptions( NULL ), + pShpBtnLadder( NULL ), + pShpBtnHelp( NULL ), + bReturningAfterGame( false ), + pTTipDiscon( NULL ), + pTTipLeave( NULL ), + pTTipRefresh( NULL ), + pTTipSquelch( NULL ), + pTTipBan( NULL ), + pTTipKick( NULL ), + pTTipFindpage( NULL ), + pTTipOptions( NULL ), + pTTipLadder( NULL ), + pTTipHelp( NULL ), + bMyRecordUpdated( false ), + bChannelListTitleUpdated( false ), + bInGame( false ), + pStaticUsers( NULL ), + bPump_In_Call_Back( false ), + bFreezeExternalPager( false ), + bDoingDisconnectPinging( false ), + bSelfDestruct( false ), + bEggSounds( true ), + bEgg8Player( false ), + bShowRankRA( true ), + bShowRankUpdated( false ) +{ + *szMyName = 0; + *szMyRecord = 0; + *szMyRecordAM = 0; + *szChannelListTitle = 0; + *szChannelNameCurrent = 0; + *szChannelReturnOnGameEnterFail = 0; + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + dwTimeNextChannelUpdate = 0; + *szLadderServerHost = 0; + *szGameResServerHost1 = 0; + *szGameResServerHost2 = 0; + + strcpy( DibIconInfos[ DIBICON_OWNER ].szFile, "dib_own.bmp" ); + strcpy( DibIconInfos[ DIBICON_SQUELCH ].szFile, "dib_sqel.bmp" ); + strcpy( DibIconInfos[ DIBICON_LATENCY ].szFile, "latency.bmp" ); + strcpy( DibIconInfos[ DIBICON_ACCEPT ].szFile, "dib_acpt.bmp" ); + strcpy( DibIconInfos[ DIBICON_NOTACCEPT ].szFile, "dib_acp2.bmp" ); + strcpy( DibIconInfos[ DIBICON_USER ].szFile, "dib_user.bmp" ); + strcpy( DibIconInfos[ DIBICON_PRIVATE ].szFile, "privgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_TOURNAMENT ].szFile, "tourgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_VOICE ].szFile, "voice.bmp" ); + for( int i = 0; i != NUMDIBICONS; i++ ) + { + DibIconInfos[ i ].hDIB = 0; + DibIconInfos[ i ].pDIB = NULL; + } + + // Determine name of executable of user's web browser. + // This seems to be the "correct" way to do this, but it's bloody stupid. + *szWebBrowser = 0; + char szFile[] = "\\name_unlikely_to_conflict_77.html"; //"\\it is really dumb for me to have to create this file.html"; + HANDLE hFile = ::CreateFile( szFile, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile != INVALID_HANDLE_VALUE ) + { + ::CloseHandle( hFile ); + HINSTANCE hExecutable = ::FindExecutable( szFile, "", szWebBrowser ); +// if( (int)hExecutable <= 32 ) +// { +// debugprint( "error %i getting browser\n", hExecutable ); +// } +// else +// debugprint( "szWebBrowser is %s\n", szWebBrowser ); + ::DeleteFile( szFile ); + } +} + +//*********************************************************************************************** +WolapiObject::~WolapiObject() +{ + DeleteSavedChat(); + + if( nGameTypeInfos ) + { + // Delete DIBs that were created, and the wol_gametypeinfos themselves + for( unsigned int n = 0; n != nGameTypeInfos; n++ ) + { + if( GameTypeInfos[ n ].hDIB ) + { + GlobalUnlock( GameTypeInfos[ n ].hDIB ); // Release pDIB. + DestroyDIB( GameTypeInfos[ n ].hDIB ); // Destroy mem alloc'ed for dib bits data. + } + } + delete [] GameTypeInfos; + GameTypeInfos = NULL; + } + + for( int i = 0; i != NUMDIBICONS; i++ ) + { + if( DibIconInfos[ i ].pDIB ) + { + GlobalUnlock( DibIconInfos[ i ].hDIB ); + DestroyDIB( DibIconInfos[ i ].hDIB ); + } + } + + if( pChatSink ) + UnsetupCOMStuff(); + + // Delete buttons, etc., shared by dialogs. + delete pShpBtnDiscon; + delete pShpBtnLeave; + delete pShpBtnRefresh; + delete pShpBtnSquelch; + delete pShpBtnBan; + delete pShpBtnKick; + delete pShpBtnFindpage; + delete pShpBtnOptions; + delete pShpBtnLadder; + delete pShpBtnHelp; + // Delete shared tooltips. + delete pTTipDiscon; + delete pTTipLeave; + delete pTTipRefresh; + delete pTTipSquelch; + delete pTTipBan; + delete pTTipKick; + delete pTTipFindpage; + delete pTTipOptions; + delete pTTipLadder; + delete pTTipHelp; +} + +//*********************************************************************************************** +bool WolapiObject::bSetupCOMStuff() +{ +// debugprint( "++++Begin WolapiObject::bSetupCOMStuff\n" ); + + HRESULT hRes; + + // Grab IChat, INetUtil, set up "sinks". +// debugprint( "CoCreateInstance\n" ); + CoCreateInstance( CLSID_Chat, NULL, CLSCTX_INPROC_SERVER, IID_IChat, (void**)&pChat ); + if( !pChat ) + return false; // Severe, essentially fatal. + CoCreateInstance( CLSID_NetUtil, NULL, CLSCTX_INPROC_SERVER, IID_INetUtil, (void**)&pNetUtil ); + if( !pNetUtil ) + return false; // Severe, essentially fatal. + + // Set up RAChatEventSink. + pChatSink = new RAChatEventSink( this ); + pChatSink->AddRef(); + // Set up RANetUtilEventSink. + pNetUtilSink = new RANetUtilEventSink( this ); + pNetUtilSink->AddRef(); + + // If we could use ATL stuff, this would be different. (We'd use AtlAdvise.) + + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + + // Get a connection point from the chat class for the chatsink. +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect chat to chatsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (IChatEvent*)pChatSink, &dwChatAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; + // Get a connection point from the netutil class for the netutilsink. +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect netutil to netutilsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (INetUtilEvent*)pNetUtilSink, &dwNetUtilAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "++++End WolapiObject::bSetupCOMStuff\n" ); + return true; +} + +//*********************************************************************************************** +void WolapiObject::UnsetupCOMStuff() +{ +// debugprint( "----Begin WolapiObject::UnsetupCOMStuff\n" ); + + HRESULT hRes; + + // If we could use ATL stuff, this would be different. (We'd use AtlUnadvise.) + + // Unsetup RAChatEventSink and RANetUtilEventSink, release IChat. + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwChatAdvise ); + pConnectionPoint->Unadvise( dwChatAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwNetUtilAdvise ); + pConnectionPoint->Unadvise( dwNetUtilAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "pChat->Release\n" ); + pChat->Release(); + +// debugprint( "pChatSink->Release\n" ); + pChatSink->Release(); // This results in pChatSink deleting itself for us. + pChatSink = NULL; + +// debugprint( "pNetUtil->Release\n" ); + pNetUtil->Release(); + +// debugprint( "pNetUtilSink->Release\n" ); + pNetUtilSink->Release(); // This results in pChatSink deleting itself for us. + pNetUtilSink = NULL; + +// debugprint( "----End WolapiObject::UnsetupCOMStuff\n" ); +} + +//*********************************************************************************************** +void WolapiObject::LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ) +{ + // Called to initialize this before the chat dialog is shown. + + // Set pointers to lists in dialog. + this->pILChat = pILChat; + this->pILChannels = pILChannels; + this->pILUsers = pILUsers; + + this->pStaticUsers = pStaticUsers; +} + +//*********************************************************************************************** +void WolapiObject::ClearListPtrs() +{ + // Called to clear list pointers when chat or gamesetup dialog goes away, for safety. + pILChat = NULL; + pILChannels = NULL; + pILUsers = NULL; + + pILPlayers = NULL; + + pStaticUsers = NULL; +} + +//*********************************************************************************************** +void WolapiObject::LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ) +{ + // Called to initialize this before the gamesetup dialog is shown. + + // Set pointers to lists in dialog. + pILChat = pILDisc; + this->pILPlayers = pILPlayers; +} + +//*********************************************************************************************** +void WolapiObject::PrepareButtonsAndIcons() +{ + // Load shapes for buttons. Store images in this order: up, down, disabled. + //pShpDiscon = LoadShpFile( "discon.shp" ); etc + pShpDiscon = (char*)MFCD::Retrieve( "discon.shp" ); + pShpLeave = (char*)MFCD::Retrieve( "leave.shp" ); + pShpRefresh = (char*)MFCD::Retrieve( "refresh.shp" ); + pShpSquelch = (char*)MFCD::Retrieve( "squelch.shp" ); + pShpBan = (char*)MFCD::Retrieve( "ban.shp" ); + pShpKick = (char*)MFCD::Retrieve( "kick.shp" ); + pShpFindpage = (char*)MFCD::Retrieve( "findpage.shp" ); + pShpOptions = (char*)MFCD::Retrieve( "ops.shp" ); + pShpLadder = (char*)MFCD::Retrieve( "ladder.shp" ); + pShpHelp = (char*)MFCD::Retrieve( "help.shp" ); + + // Set up standard wol buttons, used by both main dialogs. Note hardcoded ID values: must match values in dialog. + int iWolButtons_x = 34; + int iWolButtons_y = 20; + int iWolButtons_dx = 53; + int xWolButton = iWolButtons_x; + int xTTip = 10; // Offset for tooltip. + int yTTip = - 5; // Offset for tooltip. + pShpBtnDiscon = new ShapeButtonClass( 100, pShpDiscon, xWolButton, iWolButtons_y ); + pTTipDiscon = new ToolTipClass( pShpBtnDiscon, TXT_WOL_TTIP_DISCON, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnLeave = new ShapeButtonClass( 101, pShpLeave, xWolButton, iWolButtons_y ); + pTTipLeave = new ToolTipClass( pShpBtnLeave, TXT_WOL_TTIP_LEAVE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnRefresh = new ShapeButtonClass( 102, pShpRefresh, xWolButton, iWolButtons_y ); + pTTipRefresh = new ToolTipClass( pShpBtnRefresh, TXT_WOL_TTIP_REFRESH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnSquelch = new ShapeButtonClass( 103, pShpSquelch, xWolButton, iWolButtons_y ); + pTTipSquelch = new ToolTipClass( pShpBtnSquelch, TXT_WOL_TTIP_SQUELCH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnBan = new ShapeButtonClass( 104, pShpBan, xWolButton, iWolButtons_y ); + pTTipBan = new ToolTipClass( pShpBtnBan, TXT_WOL_TTIP_BAN, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnKick = new ShapeButtonClass( 105, pShpKick, xWolButton, iWolButtons_y ); + pTTipKick = new ToolTipClass( pShpBtnKick, TXT_WOL_TTIP_KICK, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnFindpage = new ShapeButtonClass( 106, pShpFindpage, xWolButton, iWolButtons_y ); + pTTipFindpage = new ToolTipClass( pShpBtnFindpage, TXT_WOL_TTIP_FINDPAGE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton = 452; + pShpBtnOptions = new ShapeButtonClass( 107, pShpOptions, xWolButton, iWolButtons_y ); + pTTipOptions = new ToolTipClass( pShpBtnOptions, TXT_WOL_TTIP_OPTIONS, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnLadder = new ShapeButtonClass( 108, pShpLadder, xWolButton, iWolButtons_y ); + pTTipLadder = new ToolTipClass( pShpBtnLadder, TXT_WOL_TTIP_LADDER, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnHelp = new ShapeButtonClass( 109, pShpHelp, xWolButton, iWolButtons_y ); + pTTipHelp = new ToolTipClass( pShpBtnHelp, TXT_WOL_TTIP_HELP, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + + // Load standard hard-coded icons. + HPALETTE hPal = GetCurrentScreenPalette(); + + int iFileLength; + const char* pFileData; + for( int iDibIcon = 0; iDibIcon != NUMDIBICONS; iDibIcon++ ) + { + //pFileData = LoadFileIntoMemory( DibIconInfos[ iDibIcon ].szFile, iFileLength ); + pFileData = (char*)MFCD::Retrieve( DibIconInfos[ iDibIcon ].szFile ); + if( pFileData ) + { + CCFileClass ccfileDib( DibIconInfos[ iDibIcon ].szFile ); + iFileLength = ccfileDib.Size(); + //debugprint( "Loaded %s, size is %i.\n", DibIconInfos[ iDibIcon ].szFile, iFileLength ); + DibIconInfos[ iDibIcon ].hDIB = LoadDIB_FromMemory( (unsigned char*)pFileData, iFileLength ); + if( DibIconInfos[ iDibIcon ].hDIB ) + { + DibIconInfos[ iDibIcon ].pDIB = (char*)GlobalLock( DibIconInfos[ iDibIcon ].hDIB ); + RemapDIBToPalette( hPal, DibIconInfos[ iDibIcon ].pDIB ); + } +// else +// debugprint( "LoadDIB_FromMemory failed!\n" ); + } +// else +// debugprint( "Couldn't find %s in mix.\n", DibIconInfos[ iDibIcon ].szFile ); + } + + if( DibIconInfos[ DIBICON_LATENCY ].pDIB ) + fLatencyToIconWidth = (float)DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) / 1000; + else + fLatencyToIconWidth = 0; + + // All of the following is for the list of game icons... + + // Load game icons from the wol api. + LPCSTR szSkus; + if( pChat->GetGametypeList( &szSkus ) == S_OK ) + { + // Make two copies of szSkus because strtok insists on messing with them. + char* szSkus1 = new char[ strlen( szSkus ) + 1 ]; + char* szSkus2 = new char[ strlen( szSkus ) + 1 ]; + strcpy( szSkus1, szSkus ); + strcpy( szSkus2, szSkus ); + // Count commas. + char seps[] = ","; + char* token; + nGameTypeInfos = 0; + token = strtok( szSkus1, seps ); + while( token != NULL ) + { + nGameTypeInfos++; + token = strtok( NULL, seps ); + } + // There are actually 2 additional game types available in wolapi - 0 (ws icon) and -1 (wwonline icon). + nGameTypeInfos += 2; + // Create structs to hold infos. +// debugprint( "Creating %i gametypeinfos\n", nGameTypeInfos ); + GameTypeInfos = new WOL_GAMETYPEINFO[ nGameTypeInfos ]; + int iMyIndex = 0; + token = strtok( szSkus2, seps ); + while( token != NULL ) + { + GetGameTypeInfo( atoi( token ), GameTypeInfos[ iMyIndex ], hPal ); + token = strtok( NULL, seps ); + iMyIndex++; + } + // Get the two extra game type infos... + GetGameTypeInfo( -1, GameTypeInfos[ iMyIndex++ ], hPal ); + GetGameTypeInfo( 0, GameTypeInfos[ iMyIndex++ ], hPal ); + } +// else +// debugprint( "GetGametypeList() failed.\n" ); + + // Load icons that we'll need to represent Red Alert GameKinds. + // These are available in wolapi at their old sku number locations. + GetGameTypeInfo( 2, OldRAGameTypeInfos[ 0 ], hPal ); // RA + GetGameTypeInfo( 3, OldRAGameTypeInfos[ 1 ], hPal ); // CS + GetGameTypeInfo( 4, OldRAGameTypeInfos[ 2 ], hPal ); // AM + + if( hPal ) + DeleteObject( hPal ); +} + +//*********************************************************************************************** +void WolapiObject::GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ) +{ + unsigned char* pVirtualFile; + int iFileLength; +// debugprint( "GetGametypeInfo, type %i\n", iGameType ); + LPCSTR szName; + LPCSTR szURL; + pChat->GetGametypeInfo( iGameType, 12, &pVirtualFile, &iFileLength, &szName, &szURL ); + GameTypeInfo.iGameType = iGameType; + if( szName ) + strcpy( GameTypeInfo.szName, szName ); + else + *GameTypeInfo.szName = 0; + if( szURL ) + strcpy( GameTypeInfo.szURL, szURL ); + else + *GameTypeInfo.szURL = 0; + +// debugprint( "LoadDIB_FromMemory( %i, %i )\n", pVirtualFile, iFileLength ); + // Create a DIB by "loading" (as if it was a file) the bitmap data. + GameTypeInfo.hDIB = LoadDIB_FromMemory( pVirtualFile, iFileLength ); +// debugprint( "hDIB is %i\n", GameTypeInfo.hDIB ); + if( !GameTypeInfo.hDIB ) + { + GameTypeInfo.pDIB = NULL; + return; // Load failed. Should not ever happen. + } + GameTypeInfo.pDIB = (const char*)GlobalLock( GameTypeInfo.hDIB ); + +// debugprint( "@@@@@ Created gametypeinfo #%i: name %s, pDIB = %i\n", iIndex, GameTypeInfo.szName, GameTypeInfo.pDIB ); + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GameTypeInfo.pDIB; + if( lpbi->biBitCount != 8 ) + { + // Do not use this loaded bmp, as it's not 256 color. + GlobalUnlock( GameTypeInfo.hDIB ); // Release pDIB. + GameTypeInfo.pDIB = NULL; + DestroyDIB( GameTypeInfo.hDIB ); // Destroy mem alloc'ed for dib bits data. + GameTypeInfo.hDIB = 0; + return; + } + + // Remap colors... + RemapDIBToPalette( hPal, GameTypeInfo.pDIB ); + +} + +//*********************************************************************************************** +void* WolapiObject::IconForGameType( int iGameType ) +{ + // Returns a GameTypeInfos entry by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return (void*)GameTypeInfos[i].pDIB; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::NameOfGameType( int iGameType ) const +{ + // Returns the name of a sku by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szName; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::URLForGameType( int iGameType ) const +{ + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szURL; + } + return NULL; +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, iColorRemap ); +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, RemapControlType* pColorRemap ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, pColorRemap ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::GetChatServer() +{ + // Calls RequestServerList() to get a chat server ready for us to login to. + // Returns S_OK and sets pChatSink->pServer if successful. + WWMessageBox().Process( TXT_WOL_CONNECTING, TXT_NONE ); + +//if( !( ::GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) // ajw - allow use of test servers +//{ + + // Request chat server address from server server. + pChatSink->bRequestServerListWait = true; +// debugprint( "Calling RequestServerList...\n" ); + if( !SUCCEEDED( pChat->RequestServerList( GAME_SKU, GAME_VERSION, "unused", "unused", 5 ) ) ) + { +// debugprint( "RequestServerList call failed\n" ); + return E_FAIL; + } + DWORD dwTimeLimit = timeGetTime(); // ajw My own extra timeout at one minute, in case wolapi chokes. +// debugprint( "Called RequestServerList...\n" ); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + hresPatchResults = 0; + while( pChatSink->bRequestServerListWait && timeGetTime() - dwTimeLimit < 60000 ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } +// debugprint( "PumpMessages after RequestServerList...\n" ); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + // If an "update list" of patches has been received, instead of a server list, this flag will have been set + // for us describing the results. We'll either cancel log in or trigger game exit. + if( hresPatchResults ) + { + pChatSink->bRequestServerListWait = false; + return hresPatchResults; + } + } +// debugprint( "RequestServerList wait finished\n" ); + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->pServer ) + return S_OK; + else + return E_FAIL; + +/* +} +else +{ + // Test using local server on LAN. + + // Bypass RequestServerList, as it is unnecessary and may not be possible if serverserver can't be reached. + // Set SKU manually because normally RequestServerList does this for you. + pChat->SetProductSKU( GAME_SKU ); + if( pChatSink->pServer ) + delete pChatSink->pServer; + pChatSink->pServer = new Server; + if( !( ::GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) + strcpy( (char*)pChatSink->pServer->conndata, "TCP;irc.westwood.com;9000" ); + else + // Control key down as well. + strcpy( (char*)pChatSink->pServer->conndata, "TCP;10.2.20.28;4000" ); + strcpy( (char*)pChatSink->pServer->connlabel, "IRC" ); + strcpy( (char*)pChatSink->pServer->name, "Chat"); + return S_OK; +} +*/ + +} + +//*********************************************************************************************** +HRESULT WolapiObject::AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ) +{ + // If RequestConnection() succeeds, sets pChatSink->bConnected true and returns S_OK. + // Else returns RequestConnection() error result. + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGIN, TXT_NONE ); + +// debugprint( "~1\n" ); + strcpy( (char*)pChatSink->pServer->login, szName ); + strcpy( (char*)pChatSink->pServer->password, szPass ); + +/* +// debugprint( "RequestConnection with:\n%s,%s,%s,%s,%s - %s\n", + pChatSink->pServer->name, + pChatSink->pServer->connlabel, + pChatSink->pServer->conndata, + pChatSink->pServer->login, + pChatSink->pServer->password, + bPassIsMangled ? "(mangled)" : "(unmangled)" ); +*/ + pChatSink->bRequestConnectionWait = true; + pChatSink->hresRequestConnectionError = 0; + + if( !SUCCEEDED( pChat->RequestConnection( pChatSink->pServer, 15, !bPassIsMangled ) ) ) + { +// debugprint( "RequestConnection call failed\n" ); + return CHAT_E_CON_ERROR; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + while( pChatSink->bRequestConnectionWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } + if( bCancel ) + break; + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + } + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->bRequestConnectionWait ) + return CHAT_E_CON_ERROR; + + if( pChatSink->bConnected ) + { + strcpy( szMyName, szName ); + strcpy( szMyRecord, szName ); + strcpy( szMyRecordAM, szName ); + return S_OK; + } + else + return pChatSink->hresRequestConnectionError; +} + +//*********************************************************************************************** +bool WolapiObject::bLoggedIn() +{ + return pChatSink->bConnected; +} + +//*********************************************************************************************** +void WolapiObject::Logout() +{ + // Requests logout from wolapi. Doesn't return any error values, as what we would do if it + // failed - force the user to stay connected? + + if( bSelfDestruct ) + WWMessageBox().Process( TXT_WOL_ERRORLOGOUT, TXT_NONE ); + else + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGOUT, TXT_NONE ); + +// debugprint( "RequestLogout()\n" ); + + pChatSink->bRequestLogoutWait = true; + + if( !SUCCEEDED( pChat->RequestLogout() ) ) + { +// debugprint( "RequestLogout() call failed\n" ); + } + + DWORD dwTimePatience = timeGetTime(); // After 5 seconds we run out of patience and bail. + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestLogoutWait && timeGetTime() - dwTimePatience < 5000 ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + pChatSink->bConnected = false; + *szMyName = 0; + + Sound_Effect( WOLSOUND_LOGOUT ); +} + +//*********************************************************************************************** +bool WolapiObject::UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ) +{ + // This is now non-modal. + // Sends off a request for a new channels list. + +// // Returns false upon total failure. +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + +// pChatSink->bRequestChannelListWait = true; + pChatSink->ChannelFilter = ChannelFilter; + +// debugprint( "RequestChannelList(), iChannelType = %i, filter = %i\n", iChannelType, ChannelFilter ); + if( !SUCCEEDED( pChat->RequestChannelList( iChannelType, bAutoping ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } +/* + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListWait ) + return false; +*/ + LastUpdateChannelCallLevel = CurrentLevel; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnChannelList() +{ + // The chatsink calls this when its OnChannelList() is called, and it has remade its internal channel list. + // The question here is: should we display the values now in the chatsink? + // As UpdateChannels() is now non-modal, there is the danger that we have moved away from the place in + // the channel heirarchy where we originally called RequestChannelList(). + // To help ensure we're getting this where we expect to get it, we check the value of CurrentLevel against + // what it was when we called UpdateChannels(). + if( CurrentLevel == LastUpdateChannelCallLevel ) + ListChannels(); +} + +//*********************************************************************************************** +void WolapiObject::ListChannels() +{ + // Show pChatSink's pChannelList in pILChannels. + // The extra data ptr hidden in each list item will hold a void pointer to the channel described. +// debugprint( "ListChannels(), pChannelList = %i\n", pChatSink->pChannelList ); + + static WOL_LEVEL LevelLastListed = WOL_LEVEL_INVALID; + + int iListViewIndex = 0; + + // If redrawing the same list as before, preserve the view position. + if( LevelLastListed == CurrentLevel ) + iListViewIndex = pILChannels->Get_View_Index(); + else + LevelLastListed = CurrentLevel; + + pILChannels->Clear(); + switch( CurrentLevel ) + { + case WOL_LEVEL_GAMESOFTYPE: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_LOBBIES: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_INLOBBY: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + break; + default: + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + break; + } + + Channel* pChannel = pChatSink->pChannelList; + while( pChannel ) + { + if( pChannel->type == 0 ) + { + // Show chat channel. + char* pShow; + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby == - 1 ) + { + // Regular chat channel. + pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + sprintf( pShow, "%s\t%-3u", (char*)pChannel->name, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_CHAT, (char*)pChannel->name, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, (void*)DibIconInfos[ DIBICON_USER ].pDIB, ICON_DIB, CHANNELTYPE_CHATCHANNEL, (void*)pChannel ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + pShow = new char[ REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( pShow, "%s\t%-3u", szLobbyName, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_LOBBY, szLobbyName, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, IconForGameType( -1 ), ICON_DIB, CHANNELTYPE_LOBBYCHANNEL, (void*)pChannel ); +// debugprint( ":::::added pChannel %i, name %s, as %s\n", pChannel, pChannel->name, pShow ); + } + delete [] pShow; + } + else + { + // Show game channel. + char* pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + char szHelp[ 200 ]; + void* pGameKindIcon; + if( pChannel->type == GAME_TYPE ) + { + // Get RedAlert GameKind. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 0 ].pDIB; // Red Alert icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_REDALERT, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::CSGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 1 ].pDIB; // CS icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_COUNTERSTRIKE, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::AMGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 2 ].pDIB; // AM icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_AFTERMATH, + pChannel->currentUsers, pChannel->maxUsers ); + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + pGameKindIcon = NULL; + break; + } + sprintf( pShow, "%s\t%u/%u", (char*)pChannel->name, pChannel->currentUsers, pChannel->maxUsers ); + } + else + { + pGameKindIcon = IconForGameType( pChannel->type ); + sprintf( pShow, "%s\t%-2u", (char*)pChannel->name, pChannel->currentUsers ); + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_GAME, NameOfGameType( pChannel->type ), + pChannel->currentUsers ); + } + void* pPrivateIcon = NULL; + if( pChannel->flags & CHAN_MODE_KEY ) + { + // Game is private. + pPrivateIcon = (void*)DibIconInfos[ DIBICON_PRIVATE ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_PRIVATEGAME ); + } + + void* pTournamentIcon = NULL; + if( pChannel->tournament ) + { + // Game is tournament. + pTournamentIcon = (void*)DibIconInfos[ DIBICON_TOURNAMENT ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_TOURNAMENTGAME ); + } + + int iLatencyUse = pChannel->latency; + if( iLatencyUse == -1 ) + iLatencyUse = 0; + + static int iLatencyBarX = 227 - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 19; + + pILChannels->Add_Item( pShow, szHelp, pGameKindIcon, ICON_DIB, + CHANNELTYPE_GAMECHANNEL, (void*)pChannel, NULL, + pPrivateIcon, ICON_DIB, pTournamentIcon, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, + iLatencyBarX, 0, iLatencyUse * fLatencyToIconWidth ); + delete [] pShow; + } + pChannel = pChannel->next; + } + if( iListViewIndex ) + pILChannels->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( const char* szChannelName, const char* szKey ) +{ + // Used for CHAT channels (or lobbies) only. Channel type is set to 0. + Channel ChannelTemp; + memset( &ChannelTemp, 0, sizeof( ChannelTemp ) ); + strcpy( (char*)ChannelTemp.name, szChannelName ); + strcpy( (char*)ChannelTemp.key, szKey ); + return ChannelJoin( &ChannelTemp ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( Channel* pChannelToJoin ) +{ + // Returns an HRESULT, the meaning of which is totally customized for my own uses. + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelJoinWait = true; + pChatSink->hresRequestJoinResult = 0; + +// debugprint( "RequestChannelJoin(), %s\n", pChannelToJoin->name ); + HRESULT hRes = pChat->RequestChannelJoin( pChannelToJoin ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelJoin() call failed, result %i ", hRes ); + DebugChatDef( hRes ); + return E_FAIL; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelJoinWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; // Turn on response to channel lists. + + if( pChatSink->bRequestChannelJoinWait ) + return CHAT_E_TIMEOUT; + + switch( pChatSink->hresRequestJoinResult ) + { + case CHAT_E_CHANNELDOESNOTEXIST: + case CHAT_E_BADCHANNELPASSWORD: + case CHAT_E_BANNED: + case CHAT_E_CHANNELFULL: + return pChatSink->hresRequestJoinResult; + } + + if( !pChatSink->bJoined ) + return E_FAIL; + + return S_OK; +} + +//*********************************************************************************************** +bool WolapiObject::ChannelLeave() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelLeaveWait = true; + pChatSink->DeleteUserList(); + +// debugprint( "RequestChannelLeave()\n" ); + if( !SUCCEEDED( pChat->RequestChannelLeave() ) ) + { +// debugprint( "RequestChannelLeave() call failed\n" ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelLeaveWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelLeaveWait || pChatSink->bJoined ) + return false; + + return true; +} + +/* +//*********************************************************************************************** +bool WolapiObject::UserList() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestUserListWait = true; + + // I no longer request a user list, as this was being done only when entering a channel, and it turns out wolapi gives + // us a user list automatically when joining. This function is used as a blocker that waits until the user list has + // definitely arrived. +// debugprint( "RequestUserList()\n" ); +// if( !SUCCEEDED( pChat->RequestUserList() ) ) +// { +// debugprint( "RequestUserList() call failed\n" ); +// return false; +// } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestUserListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestUserListWait ) + { + pChatSink->bRequestUserListWait = false; + return false; + } + + return true; +} +*/ + +//*********************************************************************************************** +//typedef char CHANNELUSERNAME[ WOL_NAME_LEN_MAX ]; +struct CHANNELUSERINFO +{ + char szName[ WOL_NAME_LEN_MAX ]; + bool bFlagged; + RemapControlType* pColorRemap; + HousesType House; // Only used if game channel. + bool bAccept; // Only used if game channel. + char szExtra[ 50 ]; // Only used if game channel. +}; + +//*********************************************************************************************** +bool WolapiObject::ListChannelUsers() +{ + // Show pChatSink's pUserList in pILUsers or pILPlayers (depending on CurrentLevel), after clearing it. + // The extra data ptr hidden in each list item will hold a void pointer to the user described. + // For simplicity, I destroy the old list and write a new one, even though possibly only one item + // may have changed. + // In order for the multiselect flags in the list to persist, I record the names that are flagged + // before clearing the list, then reset them (if found) in the new list. + // This is inefficient, but should be fine in this non-time-critical situation. + // (I also save item color here, and save all items. Now it's really inefficient.) + // (Oh, boy. Now I've added the persistence of house info when this is a game channel...) + // The idea was to avoid duplication of player data, and not have any dependence on the integrity of the chatsink's + // players list. Now it is a case of it working "well enough" to not require time to elegantize it. + + // Extra bonus, if useful: returns true if an operator (channel owner) is found in the channel, false otherwise. + + bool bChannelOwnerFound = false; + + bool bInLobby; + IconListClass* pListToUse; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + bInLobby = false; + pListToUse = pILPlayers; + } + else + { + bInLobby = ( iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ) != -1 ); + pListToUse = pILUsers; + } + + if( pListToUse && // Fails in rare cases when list draw is triggered before it is fully set up. + *szChannelNameCurrent ) // No users to list if not in a channel. + { + // If redrawing the same list as before, preserve the view position. + static char szChannelLastListed[ WOL_CHANNAME_LEN_MAX ] = { 0 }; +//debugprint( "szChannelLastListed '%s', szChannelNameCurrent '%s'\n", szChannelLastListed, szChannelNameCurrent ); + int iListViewIndex = 0; + if( strcmp( szChannelLastListed, szChannelNameCurrent ) == 0 ) + iListViewIndex = pListToUse->Get_View_Index(); + else + strcpy( szChannelLastListed, szChannelNameCurrent ); + +//debugprint( "ListChannelUsers(), pUserList = %i\n", pChatSink->pUserList ); + // Save users in current list. + int iCount = pListToUse->Count(); + CHANNELUSERINFO* pUsersSaved = NULL; + int iUsersSaved = 0; + if( iCount ) + { + pUsersSaved = new CHANNELUSERINFO[ iCount ]; + for( int i = 0; i != iCount; i++ ) + { + PullPlayerName_Into_From( pUsersSaved[ iUsersSaved ].szName, pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bFlagged = pListToUse->bItemIsMultiSelected( i ); + pUsersSaved[ iUsersSaved ].pColorRemap = pListToUse->Get_Item_Color( i ); +// debugprint( " Saving color of %s as %i.\n", pUsersSaved[ iUsersSaved ].szName, pUsersSaved[ iUsersSaved ].pColorRemap ); + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + pUsersSaved[ iUsersSaved ].House = PullPlayerHouse_From( pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bAccept = bItemMarkedAccepted( i ); + const char* szExtra = pListToUse->Get_Item_ExtraDataString( i ); + if( szExtra ) + strcpy( pUsersSaved[ iUsersSaved ].szExtra, szExtra ); + else + *pUsersSaved[ iUsersSaved ].szExtra = 0; + } + iUsersSaved++; + } + } + // Clear list and recreate with new users list. + pListToUse->Clear(); + User* pUser = pChatSink->pUserList; + int iUserCount = 0; + while( pUser ) + { + ++iUserCount; + void* pIcon1 = NULL; + void* pIcon2 = NULL; + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + pIcon1 = (void*)DibIconInfos[ DIBICON_OWNER ].pDIB; + bChannelOwnerFound = true; + } + else + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + pIcon1 = (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB; + else + { + if( pUser->flags & CHAT_USER_VOICE ) + pIcon1 = (void*)DibIconInfos[ DIBICON_VOICE ].pDIB; + else + pIcon1 = (void*)DibIconInfos[ DIBICON_USER ].pDIB; + } + } + if( pUser->flags & CHAT_USER_SQUELCHED ) + pIcon2 = (void*)DibIconInfos[ DIBICON_SQUELCH ].pDIB; + + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL || bInLobby ) + { + int iRank = pNetUtilSink->GetUserRank( (char*)pUser->name, bShowRankRA ); + char szNameToShow[ WOL_NAME_LEN_MAX + 40 ]; + if( iRank ) + { +// debugprint(" Found %s has rank %u\n", (char*)pUser->name, iRank ); + sprintf( szNameToShow, TXT_WOL_USERRANK, (char*)pUser->name, iRank ); + } + else + strcpy( szNameToShow, (char*)pUser->name ); + + static int iLatencyBarX = 124*RESFACTOR - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 5 - 16; + + // If we have had a chance to request pings to the player, there'll be some avg. results waiting for us. + int iLatencyBarWidth = 0; + int iLatency; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); +// debugprint( "player %s ip address %i\n", szNameToShow, UserIP ); + if( UserIP && pNetUtil->GetAvgPing( UserIP, &iLatency ) == S_OK ) + { +// debugprint( "player %s latency %i\n", szNameToShow, iLatency ); + if( iLatency == -1 ) + iLatency = 0; + iLatencyBarWidth = iLatency * fLatencyToIconWidth; + } + } + pListToUse->Add_Item( szNameToShow, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB, NULL, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, iLatencyBarX, 2, iLatencyBarWidth ); + } + else + pListToUse->Add_Item( (char*)pUser->name, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB ); + + pUser = pUser->next; + } + if( pStaticUsers ) + { + // Display number of users in channel. + char szCount[100]; + sprintf( szCount, TXT_WOL_USERLIST, iUserCount ); + pStaticUsers->Set_Text( szCount ); + } + // Reset multiselectedness, color, and item text for a user. Slow. + // (What a bloody, bloody hack.) + for( int iUser = 0; iUser != iUsersSaved; iUser++ ) + { + int iFind = pListToUse->Find( pUsersSaved[ iUser ].szName ); // Finds any item beginning with szName... + if( iFind != -1 ) + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pUsersSaved[ iUser ].House != HOUSE_NONE ) + { + // Append house text to item string, as we found a valid house name after the name, above. + char szItem[ 120 ]; + WritePlayerListItem( szItem, pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].House ); + pListToUse->Set_Item( iFind, szItem ); + } + if( pUsersSaved[ iUser ].bAccept ) + { + // Player was marked "accepted" before. If he has one now, it's because he is the host. + // Else it was an accepted icon before, so put one in again now. (a-hacking-we-will-go) + if( !bItemMarkedAccepted( iFind ) ) + MarkItemAccepted( iFind, true ); + } + if( *pUsersSaved[ iUser ].szExtra ) + pListToUse->Set_Item_ExtraDataString( iFind, pUsersSaved[ iUser ].szExtra ); + } + if( pUsersSaved[ iUser ].bFlagged ) + pListToUse->MultiSelect( iFind, true ); +// debugprint( " Restoring color of %s as %i.\n", pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].pColorRemap ); + pListToUse->Set_Item_Color( iFind, pUsersSaved[ iUser ].pColorRemap ); + } +// else +// debugprint( "ListChannelUsers() - Couldn't find %s!\n", pUsersSaved[ iUser ].szName ); + } + delete [] pUsersSaved; + if( iListViewIndex ) + pListToUse->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. + } + return bChannelOwnerFound; +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedAccepted( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers has an icon pointer in position 0 that + // is either the host icon or the accepted icon. + const IconList_ItemExtras* pItemExtras = pILPlayers->Get_ItemExtras( iIndex ); + return ( pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_OWNER ].pDIB || + pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB ); +} + +//*********************************************************************************************** +bool WolapiObject::MarkItemAccepted( int iIndex, bool bAccept ) +{ + pILPlayers->Flag_To_Redraw(); + if( bAccept ) + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB, ICON_DIB ); + else + //return pILPlayers->Set_Icon( iIndex, 0, NULL, ICON_DIB ); + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB, ICON_DIB ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedReadyToGo( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as "ready to go". + // This is true if the player is marked as "ready" or "need scenario". + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; +// debugprint( "szItem is %s\n", szItem ); + return ( strcmp( szItem, "ready" ) == 0 || strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::MarkItemReadyToGo( int iIndex, const char* szReadyState ) +{ + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + pILPlayers->Flag_To_Redraw(); + pILPlayers->Set_Item_ExtraDataString( iIndex, szReadyState ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedNeedScenario( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as ready to go, but needing scenario download. + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; + return ( strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::PullPlayerName_Into_From( char* szDest, const char* szSource ) +{ + // Sets szDest to the "player name" found in szSource. + // Called "player" name because this is mainly designed for use in game channels. + // Player name appears first in item, separated by a space from anything later. + + char* pSpace = strstr( szSource, " " ); + if( !pSpace ) + { + // No space character. Use entire item. + strcpy( szDest, szSource ); + } + else + { + int iSpacePosition = pSpace - szSource; + strncpy( szDest, szSource, iSpacePosition ); + szDest[ iSpacePosition ] = 0; // terminate + } +// debugprint( "PullPlayerName_Into_From: '%s' from '%s', ok?\n", szDest, szSource ); +} + +//*********************************************************************************************** +HousesType WolapiObject::PullPlayerHouse_From( const char* szSource ) +{ + // Pulls the house value out of a player list item in a game channel. + // House appears as the last word, and it's in <>. +// char* pChar = strrchr( szSource, ' ' ); // Last space character. was failing on roy. uni cause of space +// if( !pChar ) +// return HOUSE_NONE; +// ++pChar; +// if( *pChar++ != '<' ) // We know house has to be last, so if not the case, no house in item. +// return HOUSE_NONE; + char* pChar = strrchr( szSource, '<' ); // Last < character. + if( !pChar ) + return HOUSE_NONE; + ++pChar; + int iLen = strlen( pChar ); // Remaining: "housename>" + // Copy remaining string. + char szHouse[ 30 ]; + strcpy( szHouse, pChar ); + *( szHouse + iLen - 1 ) = 0; // Terminate to remove ">" +// debugprint( "PullPlayerHouse_From: '%s' from '%s', ok?\n", szHouse, szSource ); + // pChar is now a valid house name. + +// return HouseTypeClass::From_Name( szHouse ); +#ifdef ENGLISH + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( szHouse, "Russia" ) == 0 ) + return HOUSE_USSR; + else + return HouseTypeClass::From_Name( szHouse ); // Fails on "Russia". (Thinks "USSR".) +#else + for( HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++ ) + if( strcmp( Text_String(HouseTypeClass::As_Reference(house).Full_Name()), szHouse ) == 0 ) + return house; +// debugprint( "dohfus" ); // should never happen +// Fatal( "" ); + return HOUSE_USSR; +#endif +} + +//*********************************************************************************************** +void WolapiObject::WritePlayerListItem( char* szDest, const char* szName, HousesType House ) +{ + // Sets szDest to the way a player list item appears in a game channel. + char szHouse[ 50 ]; + strcpy( szHouse, Text_String( HouseTypeClass::As_Reference( House ).Full_Name() ) ); + + int iRank = pNetUtilSink->GetUserRank( szName, bShowRankRA ); // Horrendous inefficiency here, when called for relisting players... + if( iRank ) + sprintf( szDest, TXT_WOL_USERRANKHOUSE, szName, iRank, szHouse ); + else + sprintf( szDest, TXT_WOL_USERHOUSE, szName, szHouse ); +// debugprint( "WritePlayerListItem: '%s', ok?\n", szDest ); +} + +//*********************************************************************************************** +void WolapiObject::RequestPlayerPings() +{ + // Does a RequestPing for every other player listed in pILPlayers. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_MYSELF ) ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); + if( UserIP ) + { + int iUnused; + in_addr inaddrUser; + inaddrUser.s_addr = UserIP; + char* szIP = inet_ntoa( inaddrUser ); +// debugprint( "RequestPing of %s, ipaddr of %i, aka %s\n", (char*)pUser->name, UserIP, szIP ); + pNetUtil->RequestPing( szIP, 1000, &iUnused ); + } + } + } +} + +//*********************************************************************************************** +void WolapiObject::SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ) +{ + // Send regular chat message. + + if( *szMessage == 0 ) + return; + + if( strlen( szMessage ) > 4 && szMessage[0] == 63 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + return; + } + if( strlen( szMessage ) > 4 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + } + + // Iterate through ILUsers looking for selected entries. Build up a users list of selected + // items. If the list turns out to be blank, send message publicly. + User* pUserListSend = NULL; + User* pUserNew; + User* pUserTail = NULL; + int iCount = ILUsers.Count(); + int iPrivatePrintLen = 1; + for( int i = 0; i != iCount; i++ ) + { + if( ILUsers.bItemIsMultiSelected( i ) ) + { + pUserNew = new User; + *pUserNew = *( (User*)ILUsers.Get_Item_ExtraDataPtr( i ) ); +// debugprint( "Copied %s for sendmessage.\n", pUserNew->name ); + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserListSend = pUserNew; + } + else + { + pUserTail->next = pUserNew; + } + pUserTail = pUserNew; + iPrivatePrintLen += ( strlen( (char*)pUserNew->name ) + 2 ); // Extra space and comma. + } + } + if( pUserListSend ) + { + // Send private message. + if( !bAction ) + pChat->RequestPrivateMessage( pUserListSend, szMessage ); + else + pChat->RequestPrivateAction( pUserListSend, szMessage ); + char* szPrint = 0; + if( iPrivatePrintLen > 50 ) + { + // Too many users specified to print out. Just say "multiple users". + if( !bAction ) + { + szPrint = new char[ strlen( szMessage ) + 135 ]; + sprintf( szPrint, "%s %s", TXT_WOL_PRIVATETOMULTIPLE, szMessage ); + } + else + { + szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 138 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATETOMULTIPLE, szMyName, szMessage ); + } + } + else + { + if( !bAction ) + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 120 ]; + else + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 125 + strlen( szMyName ) ]; + //strcpy( szPrint, "name ); + if( pUserPrint->next ) + strcat( szPrint, ", " ); + else + strcat( szPrint, ">: " ); + pUserPrint = pUserPrint->next; + } + if( bAction ) + { + strcat( szPrint, szMyName ); + strcat( szPrint, " " ); + } + strcat( szPrint, szMessage ); + } + if( !bAction ) + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + else + { + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } + else + { + // Send public message. + if( !bAction ) + { + // Easter egg related. + if( _stricmp( szMessage, "/nousersounds" ) == 0 ) + { + bEggSounds = false; + return; + } + else if( _stricmp( szMessage, "/usersounds" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEggSounds = true; + return; + } + else if( _stricmp( szMessage, "/8playergames" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEgg8Player = true; + return; + } + HRESULT hRes = pChat->RequestPublicMessage( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicMessage() failed with: " ); +// DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pChat->RequestPublicAction( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicAction() failed with: " ); +// DebugChatDef( hRes ); + } + } + char* szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 10 ]; + if( !bAction ) + { + sprintf( szPrint, "%s: %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + } + else + { + sprintf( szPrint, "%s %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } +} + +//*********************************************************************************************** +bool WolapiObject::ChannelCreate( const char* szChannelName, const char* szKey, bool bGame /* = false */, + int iMaxPlayers /* = 0 */, bool bTournament /* = false */, int iLobby /* = 0 */, + CREATEGAMEINFO::GAMEKIND GameKind /* = red alert */ ) +{ + // Create a channel. + // szKey is NULL if a public channel is to be created, else channel password. + + // Returns true if everything goes okay. + + if( pChatSink->bJoined ) + { + // This never happens. Here just in case. +// debugprint( "WolapiObject::ChannelCreate called when bJoined is true!\n" ); + return false; +// Fatal( "WolapiObject::ChannelCreate called when bJoined is true!" ); + } + + Channel ChannelNew; + + // Prepare the struct. + memset( &ChannelNew, 0, sizeof( ChannelNew ) ); + + if( !bGame ) + { + // ChannelNew.type = 0; 0 for chat channel. + strcpy( (char*)ChannelNew.name, szChannelName ); + } + else + { + ChannelNew.type = GAME_TYPE; + ChannelNew.maxUsers = iMaxPlayers; + ChannelNew.tournament = bTournament; + // Channel 'reserved' stores GameKind in the highest byte, and + // lobby number to return to in the lower three bytes. + // Note: If lobby number is -1 (no lobby to return to), it's encoded as 0x00FFFFFF + ChannelNew.reserved = ( iLobby & 0x00FFFFFF ) | GameKind; + strcpy( (char*)ChannelNew.name, szChannelName ); + } + +// debugprint( "RequestChannelCreate(), channel name: '%s'\n", szChannelName ); + + if( szKey ) + strcpy( (char*)ChannelNew.key, szKey ); + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelCreateWait = true; + + HRESULT hRes = pChat->RequestChannelCreate( &ChannelNew ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelCreate() call failed:" ); + DebugChatDef( hRes ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelCreateWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelCreateWait || !pChatSink->bJoined ) + return false; // Timed out or callback got fail value. + + if( bGame ) + iLobbyReturnAfterGame = iLobby; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoFindPage() +{ + // User presses find/page button. + SimpleEditDlgClass* pFindPageDlg; + + // Ask user for user desired. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pFindPageDlg = new SimpleEditDlgClass( 400, TXT_WOL_PAGELOCATE, TXT_WOL_USERNAMEPROMPT, WOL_NAME_LEN_MAX ); + pFindPageDlg->SetButtons( TXT_WOL_LOCATE, Text_String( TXT_CANCEL ), TXT_WOL_PAGE ); + bPump_In_Call_Back = true; + const char* szNameDlgResult = pFindPageDlg->Show(); + bPump_In_Call_Back = false; + + if( strcmp( szNameDlgResult, Text_String( TXT_CANCEL ) ) == 0 || !*pFindPageDlg->szEdit ) + { + delete pFindPageDlg; + return; + } + + if( strcmp( szNameDlgResult, TXT_WOL_LOCATE ) == 0 ) + { + // Locate user. + HRESULT hRes = Locate( pFindPageDlg->szEdit ); + switch( hRes ) + { + case CHAT_S_FIND_NOTHERE: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOTHERE ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_NOCHAN: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOCHAN ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_OFF: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_OFF ); + bPump_In_Call_Back = false; + break; + case CHAT_E_TIMEOUT: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + bPump_In_Call_Back = false; + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + { + char* szChannel = (char*)pChatSink->OnFindChannel.name; + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannel ); + char* szFound; + if( iLobby != -1 ) + { + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szLobbyName ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szLobbyName ); + } + else + { + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szChannel ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szChannel ); + } + bPump_In_Call_Back = true; + WWMessageBox().Process( szFound ); + bPump_In_Call_Back = false; + delete [] szFound; + break; + } + } + } + else + { + // Page user. + // Ask user for text to send. + SimpleEditDlgClass* pMessDlg = new SimpleEditDlgClass( 600, TXT_WOL_PAGEMESSAGETITLE, + TXT_WOL_PAGEMESSAGEPROMPT, MAXCHATSENDLENGTH ); + bPump_In_Call_Back = true; + if( strcmp( pMessDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pMessDlg->szEdit ) + { + switch( Page( pFindPageDlg->szEdit, pMessDlg->szEdit, true ) ) + { + case CHAT_S_PAGE_NOTHERE: + WWMessageBox().Process( TXT_WOL_PAGE_NOTHERE ); + break; + case CHAT_S_PAGE_OFF: + WWMessageBox().Process( TXT_WOL_PAGE_OFF ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + char szMessage[ WOL_NAME_LEN_MAX + 30 ]; + sprintf( szMessage, TXT_WOL_WASPAGED, pFindPageDlg->szEdit ); + PrintMessage( szMessage, WOLCOLORREMAP_LOCALMACHINEMESS ); + break; + } + } + bPump_In_Call_Back = false; + } + + delete pFindPageDlg; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Locate( const char* szUser ) +{ + // Returns HRESULT with possibly customized meanings. + + char* szMessage = new char[ strlen( TXT_WOL_LOCATING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_LOCATING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + + pChatSink->bRequestFindWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestFind()\n" ); + if( !SUCCEEDED( pChat->RequestFind( &userFind ) ) ) + { +// debugprint( "RequestFind() call failed\n" ); + return 0; + } + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestFindWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Find pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestFindWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestFindResult; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Page( const char* szUser, const char* szSend, bool bWaitForResult ) +{ + // Returns HRESULT with possibly customized meanings. + + if( bWaitForResult ) + { + char* szMessage = new char[ strlen( TXT_WOL_PAGING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_PAGING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + } + + pChatSink->bRequestPageWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestPage()\n" ); + if( !SUCCEEDED( pChat->RequestPage( &userFind, szSend ) ) ) + { +// debugprint( "RequestPage() call failed\n" ); + return 0; + } + if( !bWaitForResult ) + return 0; + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestPageWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Page pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestPageWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestPageResult; +} + +//*********************************************************************************************** +void WolapiObject::DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ) +{ + // Kick selected users. + + if( CurrentLevel != WOL_LEVEL_INCHATCHANNEL && CurrentLevel != WOL_LEVEL_INLOBBY && CurrentLevel != WOL_LEVEL_INGAMECHANNEL ) + { + PrintMessage( TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else if( !bChannelOwner ) + { + PrintMessage( TXT_WOL_ONLYOWNERCANKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else + { + int iFound = 0; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't kick yourself. + { + Kick( pUser ); + if( bAndBan ) + Ban( pUser ); + iFound++; + if( iFound < 5 ) + Sound_Effect( (VocType)( VOC_SCREAM1 + ( rand() % 9 ) ) ); + } + } + } + if( !iFound ) + { + PrintMessage( TXT_WOL_NOONETOKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + } +} + +//*********************************************************************************************** +bool WolapiObject::Kick( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestUserKick()\n" ); + if( !SUCCEEDED( pChat->RequestUserKick( pUserToKick ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::Ban( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestChannelBan()\n" ); + if( !SUCCEEDED( pChat->RequestChannelBan( (char*)pUserToKick->name, true ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoSquelch( IconListClass* pILUsersOrPlayers ) +{ + // Squelch/unsquelch selected users. + bool bFound = false; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser ) + { + if( strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't squelch yourself. + { + Squelch( pUser ); +// char szMess[ 150 ]; +// if( Squelch( pUser ) ) +// sprintf( szMess, TXT_WOL_USERISSQUELCHED, (char*)pUser->name ); +// else +// sprintf( szMess, TXT_WOL_USERISNOTSQUELCHED, (char*)pUser->name ); +// WOL_PrintMessage( chatlist, szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + + bFound = true; + pILUsersOrPlayers->Flag_To_Redraw(); + } + else + PrintMessage( TXT_WOL_CANTSQUELCHSELF, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + } + if( bFound ) + { + Sound_Effect( VOC_SQUISH ); + ListChannelUsers(); // Refresh displayed user list. + } +} + +//*********************************************************************************************** +bool WolapiObject::Squelch( User* pUserToSquelch ) +{ + // Returns true if user is now squelched, false if not squelched. + // Sets User pointer flags value. +// debugprint( "Squelch:: pUser is %i, flags is %i\n", pUserToSquelch, pUserToSquelch->flags ); + + if( pUserToSquelch->flags & CHAT_USER_SQUELCHED ) + { + pChat->SetSquelch( pUserToSquelch, false ); + pUserToSquelch->flags &= ~CHAT_USER_SQUELCHED; + return false; + } + pChat->SetSquelch( pUserToSquelch, true ); + pUserToSquelch->flags |= CHAT_USER_SQUELCHED; + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoOptions() +{ + // Show options dialog. + bPump_In_Call_Back = true; + WOL_Options_Dialog( this, false ); + bPump_In_Call_Back = false; + // Set trigger for an immediate channel list update, in case local lobby games filter was changed. + dwTimeNextChannelUpdate = ::timeGetTime(); +} + +//*********************************************************************************************** +bool WolapiObject::DoLadder() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_LADDERSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; +#ifdef ENGLISH + //return SpawnBrowser( "http://www.westwood.com/ra_ladders.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +#ifdef GERMAN +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_german.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_french.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#endif +#endif + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoHelp() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_HELPSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + const char* szURL; + if( pChat->GetHelpURL( &szURL ) == S_OK ) + return SpawnBrowser( szURL ); + GenericErrorMessage(); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoWebRegistration() +{ + // Get the executable name from the registry. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Westwood\\Register", 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + char szPath[ _MAX_PATH + 1 ]; + DWORD dwBufSize = _MAX_PATH; + if( RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szPath, &dwBufSize ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + RegCloseKey( hKey ); +// debugprint( "Registration app is '%s'\n", szPath ); + + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_WEBREGISTRATIONSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + ::ShellExecute( NULL, "open", szPath, NULL, ".", SW_SHOW ); + return true; + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoGameAdvertising( const Channel* pChannel ) +{ + const char* szURL = URLForGameType( pChannel->type ); + if( !szURL ) + { + GenericErrorMessage(); + return false; + } + + char szQuestion[512]; + sprintf( szQuestion, TXT_WOL_GAMEADVERTSHELL, NameOfGameType( pChannel->type ) ); + bPump_In_Call_Back = true; + if( WWMessageBox().Process( szQuestion, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + return SpawnBrowser( szURL ); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::SpawnBrowser( const char* szURL ) +{ + // Attempts to launch user's web browser, and monitors it, waiting for user to close it, at which + // point we bring focus back to the game. + + // Loosely based on Dune2000 example. + + bool bSuccess = false; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + + if( *szWebBrowser ) + { + char szCommandLine[ _MAX_PATH + 300 ]; + sprintf( szCommandLine, "\"%s\" %s", szWebBrowser, szURL ); +// debugprint( "About to CreateProcess: '%s'\n", szCommandLine ); + Hide_Mouse(); + BlackPalette.Set( FADE_PALETTE_FAST, Call_Back ); +// ::ShowWindow( MainWindow, SW_SHOWMINIMIZED ); + SeenPage.Clear(); + if( ::CreateProcess( NULL, + szCommandLine, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent’s environment block. + NULL, // Use parent’s starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi ) ) // Pointer to PROCESS_INFORMATION structure. + { + if( pi.hProcess ) + { +// debugprint( "CreateProcess: '%s'\n", szCommandLine ); + bSuccess = true; + ::WaitForInputIdle( pi.hProcess, 5000 ); + bPump_In_Call_Back = true; + for( ; ; ) + { + DWORD dwActive; + Call_Back(); + Sleep( 200 ); + ::GetExitCodeProcess( pi.hProcess, &dwActive ); + if( dwActive != STILL_ACTIVE || cancel_current_msgbox ) + { + // Either user closed the browser app, or game is starting and we should return focus to game. + cancel_current_msgbox = false; + ::SetForegroundWindow( MainWindow ); + ::ShowWindow( MainWindow, SW_RESTORE ); + break; + } + if( ::GetTopWindow( NULL ) == MainWindow ) + { + ::ShowWindow( MainWindow, SW_RESTORE ); // In case it was topmost but minimized. + break; + } + } + bPump_In_Call_Back = false; + GamePalette.Set( FADE_PALETTE_FAST, Call_Back ); + Show_Mouse(); + } + } + } + + if( !bSuccess ) + { + // This was the old way - does not pop you back into game when finished... + if( (int)::ShellExecute( NULL, NULL, szURL, NULL, ".", SW_SHOW ) <= 32 ) + { +// debugprint( "ShellExecute\n" ); + // ShellExecute failed as well. Just print a message instead. + GamePalette.Set(); + ::ShowWindow( MainWindow, SW_RESTORE ); + char szError[ 300 ]; + sprintf( szError, TXT_WOL_CANTLAUNCHBROWSER, szURL ); + Show_Mouse(); + WWMessageBox().Process( szError ); + return false; + } + // (We return immediately after launching in this case.) + GamePalette.Set(); + Show_Mouse(); + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::ChannelListTitle( const char* szTitle ) +{ + strcpy( szChannelListTitle, szTitle ); + bChannelListTitleUpdated = true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Top() +{ + // + +// debugprint( "*** EnterLevel_Top\n" ); + // (Might as well hardcode the channels tree.) + + ChannelListTitle( TXT_WOL_TOPLEVELTITLE ); + pILChannels->Clear(); + //void* pTopIcon = (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB; + void* pTopIcon = IconForGameType( 0 ); + pILChannels->Add_Item( TXT_WOL_OFFICIALCHAT, CHANNELTYPE_OFFICIALCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_OFFICIALCHAT ); + pILChannels->Add_Item( TXT_WOL_USERCHAT, CHANNELTYPE_USERCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_USERCHAT ); + pILChannels->Add_Item( TXT_WOL_GAMECHANNELS, CHANNELTYPE_GAMES, pTopIcon, ICON_DIB, CHANNELTYPE_GAMES ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + CurrentLevel = WOL_LEVEL_TOP; + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_OfficialChat() +{ + // + +// debugprint( "*** EnterLevel_OfficialChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_OFFICIALCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_OFFICIALCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_UserChat() +{ + // + +// debugprint( "*** EnterLevel_UserChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_USERCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_USERCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Games() +{ + // + +// debugprint( "*** EnterLevel_Games\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_GAMES; + + ChannelListTitle( TXT_WOL_GAMECHANNELS ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + + // Create entry for our lobbies at the top. + bool bFound = false; + // (There are actually 2 additional game types at the end of GameTypeInfos - for ws icon and wwonline icon.) + for( int i = 0; i < nGameTypeInfos - 2; i++ ) + { + if( GameTypeInfos[ i ].iGameType == GAME_TYPE ) + { + //pILChannels->Add_Item( GameTypeInfos[ i ].szName, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + bFound = true; + break; + } + } + if( !bFound ) + { + // In the production version, this should never happen, as there should always be a gametypeinfo created that matches + // our game type. It depends on the recentness of the WOR file accompanying the wolapi.dll. + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)OldRAGameTypeInfos[ 0 ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + } + + // A pointer to the GameTypeInfos entry is stored in the item for convenience later. + for( i = 0; i < nGameTypeInfos - 2; i++ ) + { + int iType = GameTypeInfos[ i ].iGameType; + if( iType != GAME_TYPE ) // Else it is our game - skip it here since we put it at the top. + { + if( iType != 2 && iType != 3 && iType != 4 ) // Hack needed for the time being, to prevent the old ra games from being seen. + { + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE, GameTypeInfos[ i ].szName ); + pILChannels->Add_Item( GameTypeInfos[ i ].szName, szHelp, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_GAMESOFTYPE, (void*)&GameTypeInfos[ i ] ); + } + } + } + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ) +{ + // + +// debugprint( "*** EnterLevel_GamesOfType: pGameTypeInfo->szName %s, iGameType %i, URL %s\n", pGameTypeInfo->szName, pGameTypeInfo->iGameType, pGameTypeInfo->szURL ); + + CurrentLevel = WOL_LEVEL_GAMESOFTYPE; + if( !UpdateChannels( pGameTypeInfo->iGameType, CHANNELFILTER_NO, true ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( pGameTypeInfo->szName ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Lobbies() +{ + // + +// debugprint( "*** EnterLevel_Lobbies\n" ); + + CurrentLevel = WOL_LEVEL_LOBBIES; + if( !UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_REDALERTLOBBIES ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ) +{ + // Called when a chat channel (or lobby) has been successfully joined. +// debugprint( "*** OnEnteringChatChannel '%s'\n", szChannelName ); + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders if this is a lobby. + if( iLobby != -1 ) + RequestLadders( NULL ); + + // Set channels list. + pILChannels->Clear(); + // Add a "return" choice at the top of the channel list, based on where we want to go 'back' to... + if( iLobby == -1 ) + { + switch( CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_OFFICIALCHAT, NULL, ICON_SHAPE, CHANNELTYPE_OFFICIALCHAT ); + break; + case WOL_LEVEL_USERCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); + break; + default: + // If entering a channel from anywhere else, user must have created the channel. + // Make "back" take them to user channels list. +// if( bICreatedChannel ) // ajw just verifying + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); +/* else + { +// debugprint( "Case that should not occur in OnEnteringChatChannel. CurrentLevel %i\n", CurrentLevel ); + pILChannels->Add_Item( "ERROR in OnEnteringChatChannel", NULL, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + } +*/ + break; + } + } + else + { + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + } + + char* szMess; + if( iLobby == -1 ) + { + CurrentLevel = WOL_LEVEL_INCHATCHANNEL; + szMess = new char[ strlen( TXT_WOL_YOUJOINED ) + strlen( szChannelName ) + 5 ]; + sprintf( szMess, TXT_WOL_YOUJOINED, szChannelName ); + ChannelListTitle( szChannelName ); + } + else + { + CurrentLevel = WOL_LEVEL_INLOBBY; + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOUJOINEDLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOUJOINEDLOBBY, szLobbyName ); + ChannelListTitle( szLobbyName ); + iLobbyLast = iLobby; + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + } + + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; + + // Set users list. + ListChannelUsers(); + + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + Sound_Effect( WOLSOUND_ENTERCHAN ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + if( CurrentLevel == WOL_LEVEL_INLOBBY ) + pShpBtnRefresh->Enable(); + else + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnExitingChatChannel() +{ + // Called when we successfully ExitChannel, or we get kicked out. (Lobbies included.) + + // Clear users list. + pILUsers->Clear(); + if( pStaticUsers ) + pStaticUsers->Set_Text( TXT_WOL_NOUSERLIST ); + +// debugprint( "*** OnExitingChatChannel() - szChannelNameCurrent '%s', CurrentLevel %i\n", szChannelNameCurrent, CurrentLevel ); + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ); + char* szMess; + if( iLobby == -1 ) + { + szMess = new char[ strlen( TXT_WOL_YOULEFT ) + strlen( szChannelNameCurrent ) + 5 ]; + sprintf( szMess, TXT_WOL_YOULEFT, szChannelNameCurrent ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOULEFTLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOULEFTLOBBY, szLobbyName ); + } + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + *szChannelNameCurrent = 0; + CurrentLevel = WOL_LEVEL_INVALID; + + Sound_Effect( WOLSOUND_EXITCHAN ); +} + +//*********************************************************************************************** +bool WolapiObject::ExitChatChannelForGameChannel() +{ + // We are about to try and join/create a game channel, and are currently in a chat channel. + + // Save this channel name, so we can come back to it if game channel join/create fails. + strcpy( szChannelReturnOnGameEnterFail, szChannelNameCurrent ); + + if( !ChannelLeave() ) + { + GenericErrorMessage(); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, + const CREATEGAMEINFO& CreateGameInfo ) +{ + // Called when a game channel has been successfully joined, while still in chat dialog, + // before game dialog has been created. + // CreateGameInfo is copied to GameInfoCurrent, so that we know what kind of a game we're in during setup. + +// debugprint( "*** OnEnteringGameChannel() - %s\n", szChannelName ); + + CurrentLevel = WOL_LEVEL_INGAMECHANNEL; + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; +// GameKindCurrent = GameKind; + GameInfoCurrent = CreateGameInfo; + strcpy( GameInfoCurrent.szPassword, CreateGameInfo.szPassword ); + + // Remove shared buttons from wolchat's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + if( CreateGameInfo.GameKind == CREATEGAMEINFO::AMGAME ) + { + if( bShowRankRA ) + { + // Switch to "show AM rankings" mode. + bShowRankRA = false; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + else + { + if( !bShowRankRA ) + { + // Switch to "show RA rankings" mode. + bShowRankRA = true; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameSetup() +{ + // Called when entering the game setup screen. Controls are initialized. OnEnteringGameChannel + // has just been called earlier. + + // Returns false only if we find there is not host - he must have simultaneously left. + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders. + RequestLadders( NULL ); + + // Request IP addresses. + RequestIPs( NULL ); + + // Set users list. + if( !ListChannelUsers() ) + { + // No host was found currently in channel! + return false; + } + + if( !pGSupDlg->bHost ) + { + char* szMess = new char[ strlen( TXT_WOL_YOUJOINEDGAME ) + WOL_NAME_LEN_MAX + 5 ]; + char szHostName[ WOL_NAME_LEN_MAX ]; + HostNameFromGameChannelName( szHostName, szChannelNameCurrent ); + sprintf( szMess, TXT_WOL_YOUJOINEDGAME, szHostName ); + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + } + else + PrintMessage( TXT_WOL_YOUCREATEDGAME, WOLCOLORREMAP_LOCALMACHINEMESS ); + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnFailedToEnterGameChannel() +{ + if( *szChannelReturnOnGameEnterFail == 0 ) + return; + + // This is called when we fail to join/create a game channel. + *szChannelNameCurrent = 0; + + // Because we don't save the channel key as well, assume the usual lobby password. If we fail, we'll return to top level. + HRESULT hRes = ChannelJoin( szChannelReturnOnGameEnterFail, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + OnEnteringChatChannel( szChannelReturnOnGameEnterFail, false, iChannelLobbyNumber( (unsigned char*)szChannelReturnOnGameEnterFail ) ); + break; + default: + // ChannelJoin returned fail value. + // (Now only applies if you could ever enter a game channel from a non-lobby.) + // There is the possibility that the channel we were in disappeared in the instant between leaving it and + // failing to join the game channel. Or, the channel has a password, that we didn't record. In either + // case, go back to the top level. + GenericErrorMessage(); + EnterLevel_Top(); + } +} + +//*********************************************************************************************** +void WolapiObject::OnExitingGameChannel() +{ + // This is called after we leave a game channel, while still in the game setup dialog. + + // Remove shared buttons from wolgsup's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + CurrentLevel = WOL_LEVEL_INVALID; + *szChannelNameCurrent = 0; +} + +//*********************************************************************************************** +void WolapiObject::RejoinLobbyAfterGame() +{ + // Called to rejoin lobby after EITHER a game, or the game setup dialog. +//debugprint( "RejoinLobbyAfterGame, iLobbyReturnAfterGame is %i\n", iLobbyReturnAfterGame ); + + if( iLobbyReturnAfterGame == -1 ) + { + // Will never happen presumably, if games are always entered via a lobby chat channel. + // We will naturally reenter the top level. + } + else + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, iLobbyReturnAfterGame ); +//debugprint( "RejoinLobbyAfterGame, channel is %s\n", szChannelToJoin ); + + HRESULT hRes = ChannelJoin( szChannelToJoin, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + //OnEnteringChatChannel( szChannelToJoin, false ); Done automatically now in wol_chat. + break; + default: + // Something went wrong when trying to rejoin the lobby we were in. + // We'll go back to the top level instead, which happens automatically if we do this... + iLobbyReturnAfterGame = -1; + break; + } + } +} + +//*********************************************************************************************** +bool WolapiObject::RequestLadders( const char* szName ) +{ + // If szName is NULL, calls RequestLadderList() until all ladder structs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current ladder list. + // This function does not block. + + if( szName && *szName ) + { +// debugprint( "RequestLadderList( %s )\n", szName ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + return true; + } + + char szNames[ ( WOL_NAME_LEN_MAX + 1 ) * 30 ]; // Neal says max is actually 25 names requested at once. Do 24... + + pNetUtilSink->DeleteLadderList(); + + // Do not request more than this number of times, to prevent overloads to ladder server. + // If we have that many people in the channel, forget about doing ladders for all of them. + // Probably this will never come into play (except while testing), because lobbies will be limited in # of users. + int iCallLimit = 4; + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + // Reset names string. + *szNames = 0; + // Get 24 users from list and add names to string. + for( int i = 0; i != 24; ++i ) + { + strcat( szNames, (char*)pUser->name ); + strcat( szNames, ":" ); + pUser = pUser->next; + if( !pUser ) + break; + } + // Remove last colon. + szNames[ strlen( szNames ) - 1 ] = 0; + +// debugprint( "RequestLadderList( %s )\n", szNames ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( --iCallLimit == 0 ) + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestIPs( const char* szName ) +{ + // If szName is NULL, calls RequestUserIP() until IPs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current IP list. + // This function does not block. + + if( szName && *szName ) + { + User user; + strcpy( (char*)user.name, szName ); +// debugprint( "RequestUserIP( %s )\n", szName ); + if( !SUCCEEDED( pChat->RequestUserIP( &user ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + return true; + } + + // Do all users in current chatsink list. + + pChatSink->DeleteUserIPList(); // Clear old user IPs. (To keep searches fast, if we go in and out of game channels a lot.) + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + if( !( pUser->flags & CHAT_USER_MYSELF ) ) + { + if( !SUCCEEDED( pChat->RequestUserIP( pUser ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::SaveChat() +{ + // Basically, a big hack to avoiding restructuring things so that the dialogs are persistent + // objects. Save the contents of the chat list in the chat dialog so that we can refresh it + // after returning from the game setup dialog (if necessary). + // This turns out to be the easiest and most straightforward way to implement this. + pChatSaveLast = pChatSaveList = NULL; + CHATSAVE* pChatSaveNew; + + for( int i = 0; i != pILChat->Count(); i++ ) + { + pChatSaveNew = new CHATSAVE; + const char* szItem = pILChat->Get_Item( i ); + if( strlen( szItem ) < SAVECHATWIDTH ) + strcpy( pChatSaveNew->szText, szItem ); + const IconList_ItemExtras* pItemExtras = pILChat->Get_ItemExtras( i ); + pChatSaveNew->ItemExtras.pColorRemap = pItemExtras->pColorRemap; + pChatSaveNew->next = NULL; + + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; + } +} + +//*********************************************************************************************** +void WolapiObject::RestoreChat() +{ + // See SaveChat()... + CHATSAVE* pChatSave = pChatSaveList; + while( pChatSave ) + { + PrintMessage( pChatSave->szText, pChatSave->ItemExtras.pColorRemap ); + pChatSave = pChatSave->next; + } +} + +//*********************************************************************************************** +void WolapiObject::AddHostLeftMessageToSavedChat( const char* szName ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + sprintf( pChatSaveNew->szText, TXT_WOL_HOSTLEFTGAME, szName ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::AddMessageToSavedChat( const char* szMessage ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + strcpy( pChatSaveNew->szText, szMessage ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::DeleteSavedChat() +{ + // See SaveChat()... + CHATSAVE* pChatSaveNext; + while( pChatSaveList ) + { + pChatSaveNext = pChatSaveList->next; + delete pChatSaveList; + pChatSaveList = pChatSaveNext; + } +} + +//*********************************************************************************************** +void WolapiObject::GenericErrorMessage() +{ + // Displays generic "something bad happened" error message. + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_ERRORMESSAGE ); + bPump_In_Call_Back = false; +} + +//*********************************************************************************************** +bool WolapiObject::GetNameOfBeginningLobby( char* szNameToSet ) +{ + // Checks for game lobbies, sets szNameToSet to the channel name that the new user should enter and returns true if succeeds. + if( !GetLobbyChannels() ) + return false; + + // Chatsink should now have a list of lobbies. + int iCount = 0; + Channel* pChannel = pChatSink->pChannelList; + if( !pChannel ) + // List is empty. + return false; + + // Return the name of the first lobby with less than 50 users. + while( pChannel ) + { + if( pChannel->currentUsers < 50 ) + { + strcpy( szNameToSet, (char*)pChannel->name ); + return true; + } + ++iCount; + pChannel = pChannel->next; + } + + // All lobbies have 50 or more users. So just choose a random one. + int iChoice = ( rand() % iCount ); + pChannel = pChatSink->pChannelList; + for( int i = 0; i != iChoice; i++ ) + pChannel = pChannel->next; + + strcpy( szNameToSet, (char*)pChannel->name ); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::GetLobbyChannels() +{ + // Modal version of UpdateChannels, for fetching lobby names. + +// // Returns false upon total failure. ajxxx do same for other calls +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelListForLobbiesWait = true; + pChatSink->ChannelFilter = CHANNELFILTER_LOBBIES; + +// debugprint( "RequestChannelList() for lobbies\n" ); + if( !SUCCEEDED( pChat->RequestChannelList( 0, false ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListForLobbiesWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListForLobbiesWait ) + return false; + + return true; +} + +//*********************************************************************************************** +const char* WolapiObject::pGameHostName() +{ + // Returns a POINTER (careful - temporary!) to the name of the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return (char*)pUser->name; + } + } + return NULL; +} + +//*********************************************************************************************** +User* WolapiObject::pGameHost() +{ + // Returns a POINTER (careful - temporary!) to the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return pUser; + } + } + return NULL; +} + +//*********************************************************************************************** +bool WolapiObject::SendGameOpt( const char* szSend, User* pUserPriv ) +{ + // Used during game setup to send public or private game options string. + // If pUserPriv is NULL, message is public, else private to pUserPriv. + if( !pUserPriv ) + { +// debugprint( "Send public game opt: '%s'\n", szSend ); + if( !SUCCEEDED( pChat->RequestPublicGameOptions( szSend ) ) ) + { +// debugprint( "RequestPublicGameOptions() call failed\n" ); + return false; + } + } + else + { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUserPriv->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUserPriv, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestGameStart() +{ + // Host is starting a game. + +/* + // Block any users that join the channel in the next microsecond from becoming involved, and + // block any users that leave from being recognized as having left. + //what if someone leaves? + // This is done to preserve the integrity of the ChatSink's user list + pWO->pChatSink->bIgnoreJoin = true; +*/ + pChatSink->bRequestGameStartWait = true; + +// debugprint( "RequestGameStart()\n" ); + if( !SUCCEEDED( pChat->RequestGameStart( pChatSink->pUserList ) ) ) + { +// debugprint( "RequestGameStart() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestGameStartWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestGameStartWait ) + { +// debugprint( "WolapiObject::RequestGameStart returning false\n" ); + pChatSink->bRequestGameStartWait = false; + return false; + } + +// debugprint( "WolapiObject::RequestGameStart returning true\n" ); + return true; +} + +//*********************************************************************************************** +bool WolapiObject::SendGo( const char* szSend ) +{ + // Send a "GO" message to all players included in the list that came back from OnGameStart. + // (Don't just broadcast it. We don't want to include any users that may have joined the channel + // in the last microsecond.) +// debugprint( "SendGo()\n" ); + + User* pUser = pChatSink->pGameUserList; + + while( pUser ) + { +// if( !( pUser->flags & CHAT_USER_MYSELF ) ) Method changed. I now wait for go message to bounce back to me. +// { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUser->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUser, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } +// } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::Init_DisconnectPinging() +{ + // Sets us up to begin "disconnect pinging" - the pinging that occurs when connection is broken + // during a tournament game. The idea is to try and figure out who is responsible for the connection + // going down. We do this by repeatedly pinging the opponent and the game results server. The number + // of successful pings is sent in the game results package. + iDisconnectPingCurrent = 0; + for( int i = 0; i != DISCONNECT_PING_COUNT; ++i ) + { + DisconnectPingResult_Server[ i ] = PING_UNSTARTED; + DisconnectPingResult_Opponent[ i ] = PING_UNSTARTED; + } + bDisconnectPingingCompleted = false; + bDoingDisconnectPinging = true; +} + +//*********************************************************************************************** +bool WolapiObject::Pump_DisconnectPinging() +{ + // Called repeatedly and continuously when it seems a tournament game connection with the opponent + // has been broken. Does PumpMessages() and requests new pings when previous results have been received. + // Returns true when the required number of pings have been completed. + + if( ::timeGetTime() > dwTimeNextWolapiPump ) + { + pChat->PumpMessages(); + pNetUtil->PumpMessages(); + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + switch( DisconnectPingResult_Server[ iDisconnectPingCurrent ] ) + { + case PING_UNSTARTED: + // Pings have yet to be requested. + // Ping game results server. + int iUnused; + if( *szGameResServerHost1 ) + { +// debugprint( "RequestPing ( gameres server )\n" ); + if( pNetUtil->RequestPing( szGameResServerHost1, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( gameres server ) failed\n" ); + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + } + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_WAITING; + } + else + // We never got an address for the gameresults server. Fake fail result. + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + + // Ping opponent. + in_addr inaddr; + char* szIP; + inaddr.s_addr = TournamentOpponentIP; + szIP = inet_ntoa( inaddr ); +// debugprint( "RequestPing ( opponent )\n" ); + if( pNetUtil->RequestPing( szIP, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( opponent ) failed\n" ); + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_BAD; + } + else + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_WAITING; + break; + case PING_WAITING: + // Ping results still pending. (Callback will set vars when results arrive.) + break; + default: + // Ping result for server is in. + if( DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] == PING_WAITING ) + break; + // Both results are in. Begin new ping, or end disconnect pinging. + iDisconnectPingCurrent++; + if( iDisconnectPingCurrent == DISCONNECT_PING_COUNT ) + { + bDisconnectPingingCompleted = true; + bDoingDisconnectPinging = false; + return true; + } + break; + } + return false; +} + +//*********************************************************************************************** +void WolapiObject::DisconnectPingResultsString( char* szStringToSet ) +{ + int iGoodServerPings = 0; + int iGoodPlayerPings = 0; + for( int i = 0; i < DISCONNECT_PING_COUNT; ++i ) + { + if( DisconnectPingResult_Server[ i ] == PING_GOOD ) ++iGoodServerPings; + if( DisconnectPingResult_Opponent[ i ] == PING_GOOD ) ++iGoodPlayerPings; + } + + sprintf( szStringToSet, "%1u/%1u %1u/%1u", iGoodServerPings, DISCONNECT_PING_COUNT, iGoodPlayerPings, DISCONNECT_PING_COUNT ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptionDefaults() +{ + // Get stored defaults for options. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WOLAPI Find Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bFindEnabled = true; + else + bFindEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Page Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bPageEnabled = true; + else + bPageEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Lang Filter", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bLangFilter = true; + else + bLangFilter = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Show All Games", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bAllGamesShown = true; + else + bAllGamesShown = (bool)dwValue; + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ) +{ + // Set options and remember them in registry. + + bFindEnabled = bEnableFind; + bPageEnabled = bEnablePage; + bLangFilter = bLangFilterOn; + bAllGamesShown = bShowAllGames; + + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_WRITE, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue = bFindEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Find Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bPageEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Page Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bLangFilter ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Lang Filter", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bAllGamesShown ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Show All Games", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette() +{ + // Get the palette of the current screen. + // Returns 0 if can't get palette. + // Remember to DeleteObject the HPALETTE if non-zero. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[WINDOW_MAIN][WINDOWX] + LogicPage->Get_XPos(), + WindowList[WINDOW_MAIN][WINDOWY] + LogicPage->Get_YPos(), + WindowList[WINDOW_MAIN][WINDOWWIDTH], + WindowList[WINDOW_MAIN][WINDOWHEIGHT] ); + LPDIRECTDRAWSURFACE lpDDS = draw_window.Get_Graphic_Buffer()->Get_DD_Surface(); + LPDIRECTDRAWPALETTE lpDDP; + HPALETTE hPal = 0; + if( lpDDS->GetPalette( &lpDDP ) == DD_OK ) + { + PALETTEENTRY pe[256]; + if( lpDDP->GetEntries( 0, 0, 256, pe ) == DD_OK ) + { + LOGPALETTE* pLogPal = (LOGPALETTE*)new char[ sizeof( LOGPALETTE ) + sizeof( PALETTEENTRY ) * 255 ]; + pLogPal->palVersion = 0x300; + pLogPal->palNumEntries = 256; + for( int i = 0; i != 256; i++ ) + { + pLogPal->palPalEntry[i].peRed = pe[i].peRed; + pLogPal->palPalEntry[i].peGreen = pe[i].peGreen; + pLogPal->palPalEntry[i].peBlue = pe[i].peBlue; + pLogPal->palPalEntry[i].peFlags = 0; +// debugprint( "DD Palette %03u: %03u, %03u, %03u\n", i, pe[i].peRed, pe[i].peGreen, pe[i].peBlue ); + } + hPal = CreatePalette( pLogPal ); + delete [] pLogPal; + } +// else +// debugprint( "DD GetEntries failed.\n" ); + } +// else +// debugprint( "DD GetPalette failed.\n" ); + + return hPal; +} + +//*********************************************************************************************** +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ) // Note: pDIB is treated as non-const. +{ + // Converts pDIB's actual pixel data to proper values for a different palette (hPal). + // Obeys convention that index 0 in the DIB's palette should map to transparent. For our purposes, make it black. + + // Set the values of the qNewPalette array to hold the new destination palette index we want + // a bmp palette entry to map to. + unsigned char qNewPalette[ 256 ]; + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pDIB; + + RGBQUAD* pRGBEntry = (RGBQUAD*)( (char*)lpbi + lpbi->biSize ); // This now points to the first entry in the bmp's palette table. + +// debugprint( "Starting rgbquads at %i\n", pRGBEntry ); + + // Index zero is supposed to be transparent. In our case, that means make it black. + qNewPalette[0] = GetNearestPaletteIndex( hPal, RGB( 0, 0, 0 ) ); + pRGBEntry++; + + for( int i = 1; i != 256; i++ ) + { + qNewPalette[i] = GetNearestPaletteIndex( hPal, RGB( pRGBEntry->rgbRed, pRGBEntry->rgbGreen, pRGBEntry->rgbBlue ) ); +// if( iIndex == 1 ) +// debugprint( "Remapping bmp %03u to new %03u\n", i, qNewPalette[i] ); + pRGBEntry++; + } + + // Convert the data to values that match game palette. + int iWidth = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + int iSrcPitch = ( iWidth + 3 ) & ~3; + int iLength = iSrcPitch * iHeight; + unsigned char* pBits = (unsigned char*)FindDIBBits( pDIB ); +// debugprint( "First Byte value %03u will become %03u\n", *pBits, qNewPalette[ *pBits ] ); + for( i = 0; i != iLength; i++ ) + { + *pBits++ = qNewPalette[ *pBits ]; + } +} + +/* +//*********************************************************************************************** +char* LoadFileIntoMemory( const char* szFileName, int& iLength ) +{ + // Loads a file into a buffer. + // Delete[] the pointer when you're done with the buffer. + HANDLE hFile; + hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + iLength = GetFileSize( hFile, NULL ); + char* pData = new char[ iLength ]; + DWORD dwBytesRead; + ReadFile( hFile, pData, iLength, &dwBytesRead, NULL ); + if( dwBytesRead != iLength ) +// debugprint( "######LoadFileIntoMemory expected %i bytes and got %i\n", iLength, dwBytesRead ); + CloseHandle( hFile ); + return pData; +} +*/ + +//*********************************************************************************************** +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ) +{ + int iApostrophe = strcspn( szChannelName, "'" ); + memcpy( szNameToSet, szChannelName, iApostrophe ); + szNameToSet[ iApostrophe ] = 0; +} + +#endif diff --git a/CODE/WOLAPIOB.H b/CODE/WOLAPIOB.H new file mode 100644 index 0000000..cacd3f9 --- /dev/null +++ b/CODE/WOLAPIOB.H @@ -0,0 +1,419 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.h +// ajw 07/10/98 + +// Class WolapiObject is mainly a container so I can avoid globals and keep things clean. +// All WOLAPI interfacing will be done through this object. It's lifetime will begin when +// API functions are first needed and end when we are finished with the API - this will +// presumably parallel the duration of the user's connection to WOL. + +#ifndef WOLAPI_H +#define WOLAPI_H + +#include "RAWolapi.h" +#include "dibapi.h" +#include "IconList.h" + +//*********************************************************************************************** +class IconListClass; +class WOL_GameSetupDialog; +class ToolTipClass; + +#define PUMPSLEEPDURATION 300 // Milliseconds between PumpMessages() calls. +#define EMERGENCY_TIMEOUT 40000 // Longest we wait for a wolapi response before terminating everything. + +// Milliseconds between automatic behaviors. +#define WOLAPIPUMPWAIT 300 +#define CHANNELUPDATEWAIT 45000 + +#define WOL_NAME_LEN_MAX 10 // Includes null-terminator. +#define WOL_PASSWORD_LEN 9 // Includes null-terminator. +#define WOL_CHANNAME_LEN_MAX 17 // Includes null-terminator. +#define WOL_CHANKEY_LEN_MAX 9 // Includes null-terminator. + +#define CHAT_USER_SQUELCHED 0x0004 // Will theoretically be added to the api and implemented. + +#define LOBBYPASSWORD "not_a_valid_password" // Password removed per Security review - LFeenanEA, 26th February 2025 + +#define USERCANCELLED 1 +#define PATCHDOWNLOADED 2 +#define PATCHAVOIDED 3 + +// Special hidden descriptors added to channel list items. +// These serve double-duty as tooltip help text. +#define CHANNELTYPE_TOP TXT_WOL_CHANNELTYPE_TOP +#define CHANNELTYPE_OFFICIALCHAT TXT_WOL_CHANNELTYPE_OFFICIALCHAT +#define CHANNELTYPE_USERCHAT TXT_WOL_CHANNELTYPE_USERCHAT +#define CHANNELTYPE_GAMES TXT_WOL_CHANNELTYPE_GAMES +#define CHANNELTYPE_GAMESOFTYPE "GamesOfType" // Not seen. +#define CHANNELTYPE_CHATCHANNEL "ChatChannel" // Not seen. +#define CHANNELTYPE_GAMECHANNEL "GameChannel" // Not seen. +#define CHANNELTYPE_LOADING TXT_WOL_CHANNELTYPE_LOADING +#define CHANNELTYPE_LOBBIES TXT_WOL_CHANNELTYPE_LOBBIES +#define CHANNELTYPE_LOBBYCHANNEL "LobbyChannel" // Not seen. + +enum WOL_LEVEL +{ + WOL_LEVEL_TOP, // Viewing top level menu choices. + WOL_LEVEL_OFFICIALCHAT, // Viewing official chat channels. + WOL_LEVEL_USERCHAT, // Viewing user chat channels. + WOL_LEVEL_INCHATCHANNEL, // In a chat channel. + WOL_LEVEL_GAMES, // Viewing types (skus) of games. + WOL_LEVEL_GAMESOFTYPE, // Viewing game channels of a type. + WOL_LEVEL_INGAMECHANNEL, // In a game channel. + WOL_LEVEL_LOBBIES, // Viewing the game lobbies. + WOL_LEVEL_INLOBBY, // In a "lobby" chat channel. + WOL_LEVEL_INVALID +}; + +struct WOL_GAMETYPEINFO +{ + int iGameType; + char szName[128]; + char szURL[256]; + HDIB hDIB; // DIB handle. + const char* pDIB; // What you get when you GlobalLock hDIB. +}; + +// Header values for game options messages. Note that 0 is not used! +enum WOL_GAMEOPT +{ + WOL_GAMEOPT_REQCOLOR = 1, // REQuest = guest asks game host for a color + WOL_GAMEOPT_INFCOLOR, // INForm = game host tells guests color of a single player (not "accept-canceling") + WOL_GAMEOPT_INFPARAMS, // host tells guests all common game params + WOL_GAMEOPT_REQHOUSE, // guest tells host he's changed house (REQ because it's guest->host) + WOL_GAMEOPT_INFHOUSE, // host tells guests about new house of a single player + WOL_GAMEOPT_REQACCEPT, // guest tells host he accepts current params + WOL_GAMEOPT_INFACCEPT, // host tells guests that a player accepted + WOL_GAMEOPT_INFSTART, // host tell guests to go into wait for start mode + WOL_GAMEOPT_REQSTART, // guest acknowledges WOL_GAMEOPT_INFSTART + WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, // guests acks WOL_GAMEOPT_INFSTART and asks for scenario download + WOL_GAMEOPT_INFCANCELSTART, // host tells guests to cancel game start, as a change arrived or player joined/left + WOL_GAMEOPT_INFGO, // host tells everyone to start + WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, // host tells new guest a lot of stuff about everyone that's in the game +}; +enum DIBICON +{ + DIBICON_OWNER, + DIBICON_SQUELCH, + DIBICON_LATENCY, + DIBICON_ACCEPT, + DIBICON_NOTACCEPT, + DIBICON_USER, + DIBICON_PRIVATE, + DIBICON_TOURNAMENT, + DIBICON_VOICE, +}; +#define NUMDIBICONS 9 + +struct DIBICONINFO +{ + char szFile[50]; + HDIB hDIB; + const char* pDIB; +}; + +// See SaveChat()... +#define SAVECHATWIDTH 150 // Wider than text that will fit in the chat list window. +struct CHATSAVE // What we save about each individual list item. +{ + char szText[ SAVECHATWIDTH + 1 ]; + IconList_ItemExtras ItemExtras; // Only color is used. + CHATSAVE* next; +}; + +struct CREATEGAMEINFO +{ + enum GAMEKIND // Gets or'ed with lobby number in channel 'reserved' field. + { + RAGAME = 0x01000000, + CSGAME = 0x02000000, + AMGAME = 0x04000000, + }; + + bool bCreateGame; // True if user confirms game creation. + int iPlayerMax; // NOT number of players, but maximum number allowed into game channel. + int iPlayerCount; // Number of initial human players in game. Set at game launch, used for stats. + bool bTournament; + bool bPrivate; + GAMEKIND GameKind; + char szPassword[ WOL_CHANKEY_LEN_MAX ]; // If not blank, key for private game. +}; + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette(); +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ); // Note: pDIB is treated as non-const. +//char* LoadFileIntoMemory( const char* szFileName, int& iLength ); + +//*********************************************************************************************** +class WolapiObject +{ +public: + WolapiObject(); + virtual ~WolapiObject(); + + IChat* pChat; + IDownload* pDownload; + INetUtil* pNetUtil; + DWORD dwChatAdvise; // Value that identifies the "connection" from chat to chatsink. + DWORD dwDownloadAdvise; + DWORD dwNetUtilAdvise; + + RAChatEventSink* pChatSink; + RADownloadEventSink* pDownloadSink; + RANetUtilEventSink* pNetUtilSink; + + bool bChatShownBefore; + + char szLadderServerHost[ 150 ]; + int iLadderServerPort; + char szGameResServerHost1[ 150 ]; + int iGameResServerPort1; + char szGameResServerHost2[ 150 ]; + int iGameResServerPort2; + + bool bFindEnabled; // I have to maintain these, though wolapi should do it for me... + bool bPageEnabled; // Note they are initialized true, as is currently the case in wol. + bool bLangFilter; // + bool bAllGamesShown; + + bool bEggSounds; // Easter egg related. True = user actions trigger sounds. + bool bEgg8Player; // True = 8 player games can be created. This is hidden so that we don't really have to support the feature... + + WOL_LEVEL CurrentLevel; + WOL_LEVEL LastUpdateChannelCallLevel; + char szMyName[WOL_NAME_LEN_MAX]; // Local user's name, valid while connected. + char szMyRecord[ WOL_NAME_LEN_MAX + 80 ]; + char szMyRecordAM[ WOL_NAME_LEN_MAX + 80 ]; + bool bMyRecordUpdated; // True when szMyRecord has changed and not yet recognized by chat dialog. + char szChannelListTitle[ 100 ]; + bool bChannelListTitleUpdated; + char szChannelNameCurrent[WOL_CHANNAME_LEN_MAX]; + bool bChannelOwner; + char szChannelReturnOnGameEnterFail[WOL_CHANNAME_LEN_MAX]; + + int iLobbyReturnAfterGame; // When in game channel, part of the value of the channel's 'reserved' field. + + bool bReturningAfterGame; + + int iLobbyLast; // Number of last lobby we personally were in. + +// CREATEGAMEINFO::GAMEKIND GameKindCurrent; // Kind of game (Red Alert, CS, AM) we are in game setup for. + CREATEGAMEINFO GameInfoCurrent; // Kind of game (Red Alert, CS, AM, tournament, private) we are in game setup for. + bool bEnableNewAftermathUnits; // Used to pass game parameter back to init only. + + DWORD dwTimeNextWolapiPump; + DWORD dwTimeNextChannelUpdate; + + DIBICONINFO DibIconInfos[ NUMDIBICONS ]; + + HRESULT hresPatchResults; // Used when a patch has been downloaded or cancelled. + + WOL_GameSetupDialog* pGSupDlg; // When in a game channel, setting up a game; ptr to the dialog. + + bool bInGame; // True while playing a game. + bool bConnectionDown; // Flag used while in a game, set to true if connection goes down. + bool bGameServer; // Flag used while in a game, true if game server (host). + + unsigned long TournamentOpponentIP; // Valid while playing a tournament game. IP address of opponent. + + bool bPump_In_Call_Back; // Used to enable PumpMessages during Call_Back(), for when we're in a modal dialog. + + bool bSelfDestruct; // If set true, causes logout and deletion of wolapi object. + + char szWebBrowser[ _MAX_PATH + 1 ]; + + // For "disconnect pinging". + bool bDoingDisconnectPinging; + bool bDisconnectPingingCompleted; + int iDisconnectPingCurrent; + DISCONNECT_PING_STATUS DisconnectPingResult_Server[ DISCONNECT_PING_COUNT ]; + DISCONNECT_PING_STATUS DisconnectPingResult_Opponent[ DISCONNECT_PING_COUNT ]; + + // Used for in-game paging and responding. + char szExternalPager[ WOL_NAME_LEN_MAX ]; // Last person to page me from outside the game, or blank for none. + bool bFreezeExternalPager; + + bool bShowRankRA; // true = view RA rankings, false = view AM rankings + bool bShowRankUpdated; // set true when bShowRankRA value changes + + // Standard wol buttons. + char* pShpDiscon; + char* pShpLeave; + char* pShpRefresh; + char* pShpSquelch; + char* pShpBan; + char* pShpKick; + char* pShpFindpage; + char* pShpOptions; + char* pShpLadder; + char* pShpHelp; + ShapeButtonClass* pShpBtnDiscon; + ShapeButtonClass* pShpBtnLeave; + ShapeButtonClass* pShpBtnRefresh; + ShapeButtonClass* pShpBtnSquelch; + ShapeButtonClass* pShpBtnBan; + ShapeButtonClass* pShpBtnKick; + ShapeButtonClass* pShpBtnFindpage; + ShapeButtonClass* pShpBtnOptions; + ShapeButtonClass* pShpBtnLadder; + ShapeButtonClass* pShpBtnHelp; + ToolTipClass* pTTipDiscon; + ToolTipClass* pTTipLeave; + ToolTipClass* pTTipRefresh; + ToolTipClass* pTTipSquelch; + ToolTipClass* pTTipBan; + ToolTipClass* pTTipKick; + ToolTipClass* pTTipFindpage; + ToolTipClass* pTTipOptions; + ToolTipClass* pTTipLadder; + ToolTipClass* pTTipHelp; + + WOL_GAMETYPEINFO OldRAGameTypeInfos[ 3 ]; // Used for storing old red alert icons only. + +public: + bool bLoggedIn(); + + void LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ); + void ClearListPtrs(); + void LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ); + + void PrepareButtonsAndIcons(); + + bool bSetupCOMStuff(); + void UnsetupCOMStuff(); + + void PrintMessage( const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); + void PrintMessage( const char* szText, RemapControlType* pColorRemap ); + + HRESULT GetChatServer(); + HRESULT AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ); + void Logout(); + bool UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ); + void OnChannelList(); + void ListChannels(); + HRESULT ChannelJoin( const char* szChannelName, const char* szKey ); + HRESULT ChannelJoin( Channel* pChannelToJoin ); + bool ChannelLeave(); +// bool UserList(); + bool ListChannelUsers(); + + bool bItemMarkedAccepted( int iIndex ); + bool MarkItemAccepted( int iIndex, bool bAccept ); + bool bItemMarkedReadyToGo( int iIndex ); + void MarkItemReadyToGo( int iIndex, const char* szReadyState ); + bool bItemMarkedNeedScenario( int iIndex ); + void PullPlayerName_Into_From( char* szDest, const char* szSource ); + HousesType PullPlayerHouse_From( const char* szSource ); + void WritePlayerListItem( char* szDest, const char* szName, HousesType House ); + + void RequestPlayerPings(); + + void SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ); + bool ChannelCreate( const char* szChannelName, const char* szKey, bool bGame = false, int iMaxPlayers = 0, + bool bTournament = false, int iLobby = 0, CREATEGAMEINFO::GAMEKIND GameKind = CREATEGAMEINFO::RAGAME ); + void DoFindPage(); + HRESULT Locate( const char* szUser ); + HRESULT Page( const char* szUser, const char* szSend, bool bWaitForResult ); + void DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ); + bool Kick( User* pUserToKick ); + bool Ban( User* pUserToKick ); + void DoSquelch( IconListClass* pILUsersOrPlayers ); + bool Squelch( User* pUserToSquelch ); + void DoOptions(); + bool DoLadder(); + bool DoHelp(); + bool DoWebRegistration(); + bool DoGameAdvertising( const Channel* pChannel ); + bool SpawnBrowser( const char* szURL ); + + void ChannelListTitle( const char* szTitle ); + bool EnterLevel_Top(); + bool EnterLevel_OfficialChat(); + bool EnterLevel_UserChat(); + bool EnterLevel_Games(); + bool EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ); + bool EnterLevel_Lobbies(); + bool OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ); + void OnExitingChatChannel(); + bool ExitChatChannelForGameChannel(); + bool OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, const CREATEGAMEINFO& CreateGameInfo ); + bool OnEnteringGameSetup(); + void OnFailedToEnterGameChannel(); + void OnExitingGameChannel(); + void RejoinLobbyAfterGame(); + + bool RequestLadders( const char* szName ); + bool RequestIPs( const char* szName ); + + void SaveChat(); + void RestoreChat(); + void AddHostLeftMessageToSavedChat( const char* szName ); + void AddMessageToSavedChat( const char* szMessage ); + void DeleteSavedChat(); + void GenericErrorMessage(); + + bool GetNameOfBeginningLobby( char* szNameToSet ); + bool GetLobbyChannels(); + + const char* pGameHostName(); + User* pGameHost(); + bool SendGameOpt( const char* szSend, User* pUserPriv ); + bool RequestGameStart(); + bool SendGo( const char* szSend ); + + void Init_DisconnectPinging(); + bool Pump_DisconnectPinging(); + void DisconnectPingResultsString( char* szStringToSet ); + + void SetOptionDefaults(); + void SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ); + +protected: + void GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ); + void* IconForGameType( int iGameType ); + const char* NameOfGameType( int iGameType ) const; + const char* URLForGameType( int iGameType ) const; + +protected: + // Used by the general chat dialog. + IconListClass* pILChat; // Main messages list. + IconListClass* pILChannels; // Channels list. + IconListClass* pILUsers; // Users list. + +// IconListClass* pILDisc; // Main messages list. (pILChat is used.) + IconListClass* pILPlayers; // Players list. + + StaticButtonClass* pStaticUsers; // Title for a users list. Used by main chat dialog only, not by game setup. + + WOL_GAMETYPEINFO* GameTypeInfos; + unsigned int nGameTypeInfos; + + float fLatencyToIconWidth; + + CHATSAVE* pChatSaveList; + CHATSAVE* pChatSaveLast; +}; + +#endif + +#endif diff --git a/CODE/WOLDEBUG.H b/CODE/WOLDEBUG.H new file mode 100644 index 0000000..9d4df8d --- /dev/null +++ b/CODE/WOLDEBUG.H @@ -0,0 +1,35 @@ +/* +** 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 . +*/ + + +#if 0 + +#define debugprogress +#define _ASSERTE( x ) + +#else + +#define debugprint OutputDebugString + +#define _DEBUG +#include "w95trace.h" + +#define debugprogress debugprint( "...%s: %i\n", __FILE__, __LINE__ ) +#define _ASSERTE( x ) if( !(x) ) debugprint( "ASSERT FALSE!\n" ); + +#endif diff --git a/CODE/WOLEDIT.CPP b/CODE/WOLEDIT.CPP new file mode 100644 index 0000000..f26ba57 --- /dev/null +++ b/CODE/WOLEDIT.CPP @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * Note: An editbox of this class cannot be made read-only. See comment below. + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "WOLEdit.h" + +//#include "WolDebug.h" + +bool bTabKeyPressedHack = false; + +//*********************************************************************************************** +void WOLEditClass::Draw_Text( char const * text ) +{ + // Only difference between this and EditClass: cursor shows up when + // string is at MaxLength. + + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && // strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + +//*********************************************************************************************** +// Override of EditClass::Action, because the base class does not behave correctly in certain circumstances. +// (Escape key is being processed as enter key.) +// Again, I'm not about to change the base class directly, as I'm trying to have as minimal an affect as possible on +// the current game code. -ajw +int WOLEditClass::Action(unsigned flags, KeyNumType & key) +{ + // (Mostly duplicated from base class ::Action) +/* For some painful reason, IsReadOnly is private in the base class, so I can't do the following. + For this reason, don't make a WOLEditClass edit box read-only. + + // + // If this is a read-only edit box, it's a display-only device + // + if (IsReadOnly) { + return(false); + } +*/ + + //debugprint( "WOLEditClass::Action this=%i, flags=0x%x, key=0x%x\n", this, flags, key ); + // + // If the left mouse button is pressed over this gadget, then set the focus to + // this gadget. The event flag is cleared so that no button ID number is returned. + // + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + // + // Handle keyboard events here. Normally, the key is added to the string, but if the + // RETURN key is pressed, then the button ID number is returned from the Input() + // function. + // + if ((flags & KEYBOARD) && Has_Focus()) { + + // + // Process the keyboard character. If indicated, consume this keyboard event + // so that the edit gadget ID number is not returned. + // + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + + // + // Allow numeric keypad presses to map to ascii numbers + // + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + key = (KeyNumType)(key & ~WWKEY_VK_BIT); + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + // + // Filter out all special keys except return and backspace + // + if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) + || key == KN_RETURN || key == KN_BACKSPACE) { + + + + if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + if( key == KN_TAB ) + { + bTabKeyPressedHack = true; + } + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + else + { + // ajw added +// if( key == ( KN_ESC | WWKEY_RLS_BIT ) && ( key & WWKEY_ALT_BIT ) ) +// { + //Clear_Focus(); + flags = 0; + key = KN_NONE; +// } + } + + return(ControlClass::Action(flags, key)); +} + +#endif diff --git a/CODE/WOLEDIT.H b/CODE/WOLEDIT.H new file mode 100644 index 0000000..baf721d --- /dev/null +++ b/CODE/WOLEDIT.H @@ -0,0 +1,43 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "Function.h" + +class WOLEditClass : public EditClass +{ +public: + WOLEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + EditClass( id, text, max_len, flags, x, y, w, h, style ) {} + + virtual int Action (unsigned flags, KeyNumType &key); // Override of base + +protected: + virtual void Draw_Text( char const * text ); // Override of base + +}; + +#endif diff --git a/CODE/WOLSTRNG.CPP b/CODE/WOLSTRNG.CPP new file mode 100644 index 0000000..3400504 --- /dev/null +++ b/CODE/WOLSTRNG.CPP @@ -0,0 +1,807 @@ +/* +** 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 . +*/ + +// New character strings for wolapi integration. + +#ifdef WOLAPI_INTEGRATION +#include "function.h" +#include "WolStrng.h" + +//#undef ENGLISH +//#define GERMAN + +#ifdef ENGLISH +#pragma message( "...Building English version..." ) + +// Menu choice for Internet game. +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +// Generic error message, though implies that blame lies with Westwood Online. +const char TXT_WOL_ERRORMESSAGE[] = "Unexpected error occurred communicating with Westwood Online."; +// Connect button on login dialog. +const char TXT_WOL_CONNECT[] = "Connect"; +// Title for login dialog. +const char TXT_WOL_LOGINDIALOG[] = "Westwood Online Login"; +// Appears on login dialog - user login name field. +const char TXT_WOL_NAME[] = "Nickname"; +// Appears on login dialog - user password field. +const char TXT_WOL_PASSWORD[] = "Password"; +// Appears on login dialog - checkbox specifying whether nickname/password should be saved to disk. +const char TXT_WOL_SAVELOGIN[] = "Save"; +// User hit the Escape button to cancel the logging in process. +const char TXT_WOL_LOGINCANCEL[] = "Login cancelled."; + +const char TXT_WOL_MISSINGNAME[] = "Please enter your login nickname."; + +const char TXT_WOL_MISSINGPASSWORD[] = "Please enter your login password."; + +const char TXT_WOL_CANTSAVENICK[] = "Error saving nickname/password."; + +const char TXT_WOL_NICKINUSE[] = "That nickname is in use. Please select another."; + +const char TXT_WOL_BADPASS[] = "Invalid password for this nickname."; + +const char TXT_WOL_TIMEOUT[] = "Connection to Westwood Online timed out."; + +const char TXT_WOL_CONNECTING[] = "Connecting to Westwood Online..."; + +const char TXT_WOL_CANTCONNECT[] = "Could not establish connection to Westwood Online."; +// Appears while connecting and logging in to Westwood Online. +const char TXT_WOL_ATTEMPTLOGIN[] = "Logging in..."; +// Appears while logging out and disconnecting from Westwood Online. +const char TXT_WOL_ATTEMPTLOGOUT[] = "Logging out..."; +// Appears while logging out and disconnecting from Westwood Online after an error has occurred. +const char TXT_WOL_ERRORLOGOUT[] = "Terminating connection with Westwood Online..."; +// Common "please wait" message. +const char TXT_WOL_WAIT[] = "Please wait... communicating with Westwood Online..."; +// Title for the top WW Online level. +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +// Title for the WW Online level where "official" chat channels are listed. +const char TXT_WOL_OFFICIALCHAT[] = "Official Chat"; +// Title for the WW Online level where "user" (in other words, unofficial) chat channels are listed. +const char TXT_WOL_USERCHAT[] = "User Chat"; +// Title for the WW Online level where game channels are listed. +const char TXT_WOL_GAMECHANNELS[] = "Game Channels"; +// Title for the WW Online level where Red Alert game lobbies are listed. +const char TXT_WOL_REDALERTLOBBIES[] = "Red Alert Lobbies"; +// Appears briefly while a list of channels is being downloaded. +const char TXT_WOL_CHANNELLISTLOADING[] = "...downloading..."; + +const char TXT_WOL_YOURENOTINCHANNEL[] = "You are not currently in a chat channel."; +// "Action" button. Causes text entered by user to show up as if they were performing an action, as opposed to speaking. +const char TXT_WOL_ACTION[] = "Action"; +// "Join" button. Allows user to join a channel, game, or WW Online level. +const char TXT_WOL_JOIN[] = "Join"; + +const char TXT_WOL_CANTCREATEINCHANNEL[] = "You can't create a new channel until you exit this channel."; +// "New" button. Allows user to create a new chat channel or game. +const char TXT_WOL_NEWSOMETHING[] = "New"; +// Title for chat channel creation dialog. +const char TXT_WOL_CREATECHANNELTITLE[] = "Create Channel"; + +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel Name: "; +// Prompt for fields where the user must enter a password. +const char TXT_WOL_PASSPROMPT[] = "Password: "; +// Prompt for fields where the user may enter a password, but it is not required. +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Password (optional): "; +// Appears in channel list, as top choice, which the user can use to go back to the top WW Online level. +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +// Appears in channel list, as top choice, which the user can use to go back up one WW Online level. +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOUJOINED[] = "You have joined the %s channel."; +// %s is replaced by the name of a user. +const char TXT_WOL_YOUJOINEDGAME[] = "You have joined %s's game."; +// Message confirming that user created a new game. +const char TXT_WOL_YOUCREATEDGAME[] = "New game created."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOUJOINEDLOBBY[] = "You have entered the %s lobby."; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOULEFT[] = "You have left the %s channel."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOULEFTLOBBY[] = "You have left the %s lobby."; +// Title for dialog that prompts user for the password needed to enter a private channel. +const char TXT_WOL_JOINPRIVATETITLE[] = "Join Private Channel"; + +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Enter Channel Password: "; + +const char TXT_WOL_BADCHANKEY[] = "Incorrect channel password."; +// Title for the Page/Locate dialog. Page = send a user a message. Locate = find out where a user is. +const char TXT_WOL_PAGELOCATE[] = "Page/Locate"; +// Appears on Page/Locate dialog. +const char TXT_WOL_USERNAMEPROMPT[] = "User Name: "; +// Text for Page button on dialog. +const char TXT_WOL_PAGE[] = "Page"; +// Text for Locate button on dialog. +const char TXT_WOL_LOCATE[] = "Locate"; +// %s is replaced with name of user being located. +const char TXT_WOL_LOCATING[] = "Locating %s..."; + +const char TXT_WOL_FIND_NOTHERE[] = "The specified user name does not exist."; + +const char TXT_WOL_FIND_NOCHAN[] = "The specified user is currently not in a channel."; + +const char TXT_WOL_FIND_OFF[] = "The specified user has disabled find capability."; +// %s is replaced with name of user being located. +const char TXT_WOL_FOUNDIN[] = "User found in the %s channel."; +// Title for Page dialog. +const char TXT_WOL_PAGEMESSAGETITLE[] = "Page User"; +// Prompt for field in which user enters the message that is to be sent to user. +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message to Send: "; +// %s is replaced with name of user being paged. +const char TXT_WOL_PAGING[] = "Paging %s..."; + +const char TXT_WOL_PAGE_NOTHERE[] = "The specified user is not logged in."; + +const char TXT_WOL_PAGE_OFF[] = "The specified user has disabled page capability."; +// First %s is replaced with user name, second %s with a text message. +const char TXT_WOL_ONPAGE[] = "Page from %s: %s"; +// %s is replaced with name of user being paged. +const char TXT_WOL_WASPAGED[] = "%s was successfully paged."; +// %s is replaced with the name of a user that has just been squelched. (Currently unused.) +//const char TXT_WOL_USERISSQUELCHED[] = "%s has been squelched."; +// %s is replaced with the name of a user that has had squelch removed. (Currently unused.) +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s is no longer squelched."; + +const char TXT_WOL_ONLYOWNERCANKICK[] = "Only the channel owner can kick users out."; +// Both %s replaced with user names. +const char TXT_WOL_USERKICKEDUSER[] = "%s kicked %s out of the channel."; +// %s replaced with user name. +const char TXT_WOL_USERKICKEDYOU[] = "You were kicked out of the channel by %s."; + +const char TXT_WOL_NOONETOKICK[] = "Select the user(s) you wish to kick out."; +// %s replaced with user name. +const char TXT_WOL_USERWASBANNED[] = "%s has been banned from the channel."; +// Title for dialog in which user enters password for new game they are creating. +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Create Private Game"; + +const char TXT_WOL_YOUREBANNED[] = "You've been banned from entering this channel."; +// %s replaced with user name. +const char TXT_WOL_PLAYERLEFTGAME[] = "%s has left the game."; +// %s replaced with user name. +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s has joined the game."; + +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "You've been kicked out of the game."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Red Alert: Ranked %u. Won %u. Lost %u. Points %u."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. Appended Aftermath ranking. +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Aftermath: Ranked %u. Won %u. Lost %u. Points %u."; +// Used to show brief user ladder ranking in user lists. Example: FredX (Rank 134) +const char TXT_WOL_USERRANK[] = "%s (Rank %u)"; +// No need to translate. +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +// "Rank" translates the same here as above. +const char TXT_WOL_USERRANKHOUSE[] = "%s (Rank %u) <%s>"; +// Button host user presses to start a game they have created. +const char TXT_WOL_STARTBUTTON[] = "Start"; +// Button that guests joining a game press to indicate that they agree to the game rules set up by the host. +const char TXT_WOL_ACCEPTBUTTON[] = "Accept"; +// %s replaced with user name. +const char TXT_WOL_HOSTLEFTGAME[] = "%s has cancelled the game."; +// Appears when game is actually being started. +const char TXT_WOL_WAITINGTOSTART[] = "Launching game..."; +// Tooltip help for WW Online button: disconnect. +const char TXT_WOL_TTIP_DISCON[] = " Leave Westwood Online "; +// Tooltip help for WW Online button: leave current channel. +const char TXT_WOL_TTIP_LEAVE[] = " Leave the channel you are in "; +// Tooltip help for WW Online button: refresh current list. +const char TXT_WOL_TTIP_REFRESH[] = " Refresh current channel list "; +// Tooltip help for WW Online button: squelch user(s). +const char TXT_WOL_TTIP_SQUELCH[] = " Enable/disable incoming message from user(s) "; +// Tooltip help for WW Online button: ban (and kick) user(s). +const char TXT_WOL_TTIP_BAN[] = " Ban user(s) from channel "; +// Tooltip help for WW Online button: kick user(s). +const char TXT_WOL_TTIP_KICK[] = " Kick user(s) out of channel "; +// Tooltip help for WW Online button: find/page. +const char TXT_WOL_TTIP_FINDPAGE[] = " Find or page a user "; +// Tooltip help for WW Online button: show options dialog. +const char TXT_WOL_TTIP_OPTIONS[] = " Set Westwood Online options "; +// Tooltip help for WW Online button: browse game ladder. +const char TXT_WOL_TTIP_LADDER[] = " Browse Red Alert ladders "; +// Tooltip help for WW Online button: show help. +const char TXT_WOL_TTIP_HELP[] = " Show Westwood Online help "; +// Tooltip help. Appears for button host presses to start a game. +const char TXT_WOL_TTIP_START[] = " Start the game "; +// Tooltip help. Appears for button guests press in order to agree to (accept) game rules set up by the host. +const char TXT_WOL_TTIP_ACCEPT[] = " Accept the current game settings "; +// Tooltip help. Appears for the small buttons that allow users to enlarge or diminish the size of channel/user lists. +const char TXT_WOL_TTIP_EXPANDLIST[] = " Expand/contract list "; +// Tooltip for Cancel button during game setup. +const char TXT_WOL_TTIP_CANCELGAME[] = " Go back a level "; +// Tooltip for Join button during chat. +const char TXT_WOL_TTIP_JOIN[] = " Join a chat or game channel "; +// Tooltip for Back button during chat. +const char TXT_WOL_TTIP_BACK[] = " Go back a level "; +// Tooltip for New button during chat. +const char TXT_WOL_TTIP_CREATE[] = " Create a new chat/game channel "; +// Tooltip for Action button. +const char TXT_WOL_TTIP_ACTION[] = " Action message "; + +const char TXT_WOL_OPTFIND[] = "Let others FIND you."; + +const char TXT_WOL_OPTPAGE[] = "Let others PAGE you."; + +const char TXT_WOL_OPTLANGUAGE[] = "Filter out bad language."; +// "Display just the games that were created by someone in the lobby you are currently in." +const char TXT_WOL_OPTGAMESCOPE[] = "Show local lobby games only."; + +const char TXT_WOL_CHANNELGONE[] = "Channel no longer exists."; +// Title for create new game dialog. +const char TXT_WOL_CG_TITLE[] = "Create Game"; +// %i replaced by number of players allowed into game channel. +const char TXT_WOL_CG_PLAYERS[] = "Players: %i"; +// Marks field indicating whether or not this is a tournament game. +const char TXT_WOL_CG_TOURNAMENT[] = "Tournament"; +// Marks field indicating whether or not this is a private game. +const char TXT_WOL_CG_PRIVACY[] = "Private"; + +const char TXT_WOL_CG_RAGAME[] = "Red Alert game"; + +const char TXT_WOL_CG_CSGAME[] = "Counterstrike game"; + +const char TXT_WOL_CG_AMGAME[] = "Aftermath game"; + +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sorry, you must have Counterstrike installed to play this game."; + +const char TXT_WOL_NEEDAFTERMATH[] = "Sorry, you must have Aftermath installed to play this game."; +// %s = name of channel, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doubleclick to join the '%s' channel (%i current users). "; +// %s = name of lobby, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doubleclick to join the '%s' lobby (%i current users). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_REDALERT[] = "Red Alert"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Counterstrike"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_AFTERMATH[] = "Aftermath"; +// %s = name of user, first %i = number of players in channel, second %i = maximum number of players allowed. +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s game (%i players of a maximum %i). "; +// %s = name of user, %i = number of players in channel. +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s game (%i players). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Private) "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournament) "; +// %s is a kind of game, for example, "Dune 2000". +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doubleclick to view %s games. "; + +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Tournament games must be two player games."; +// Shows on game setup screen for private games. %s = password for game. +const char TXT_WOL_PRIVATEPASSWORD[] = "Password: %s"; +// User cannot join game because either he or the game host has hacked the game. +const char TXT_WOL_RULESMISMATCH[] = "Your game is incompatible with the host's!"; +// Message appears when game host presses start button but slow responses cause an automatic cancellation of game start. +const char TXT_WOL_STARTTIMEOUT[] = "Timed out waiting for guest responses! Game start cancelled."; +// Message appears for guests when automatic cancellation occurs. +const char TXT_WOL_STARTCANCELLED[] = "Game start cancelled."; +// Text of button on game setup screen that takes user out of the game channel. +const char TXT_WOL_CANCELGAME[] = "Back"; + +const char TXT_WOL_PATCHQUESTION[] = "An update patch is required for Internet play. Do you want to download it now?"; +// Title of patch download dialog. First %i = current file being downloaded, second %i = total # of files to download. +const char TXT_WOL_DOWNLOADING[] = "Download file %i of %i"; + +const char TXT_WOL_DOWNLOADERROR[] = "An error occurred during file download."; +// Appears on patch download dialog. First %i = current # of bytes downloaded, second %i = total # of bytes to download. +const char TXT_WOL_DOWNLOADBYTES[] = "Received %i bytes out of %i. (%i%%%%)"; +// Appears on patch download dialog. First %i = number of minutes left, second %i = number of seconds left. +const char TXT_WOL_DOWNLOADTIME[] = "Time Remaining: %i min. %i secs."; +// Appended to title of patch download dialog when resuming an interrupted download. %s is the regular title, as above. +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Resumed after interruption.)"; + +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Connecting..."; + +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Locating file..."; + +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Downloading..."; + +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Download complete! Red Alert will now restart in order to apply the update patch."; + +const char TXT_WOL_HELPSHELL[] = "Are you sure you want to launch the Internet browser for Westwood Online help?"; + +const char TXT_WOL_LADDERSHELL[] = "Are you sure you want to launch the Internet browser for the Red Alert ladders?"; + +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "No saved usernames found. Would you like to register a new username for Westwood Online?"; + +const char TXT_WOL_GAMEADVERTSHELL[] = "Are you sure you want to launch the Internet browser for information about %s?"; +// Appears above user list. %i = number of users in the current channel. +const char TXT_WOL_USERLIST[] = "Users %i"; +// Appears above user list to explain why no users are being listed: because the user is not currently in a chat channel. +const char TXT_WOL_NOUSERLIST[] = "(not in a channel)"; + +const char TXT_WOL_CANTCREATEHERE[] = "To start a game, you have to be in a Red Alert lobby."; +// Appears inside game, when connection to WW Online is lost. +const char TXT_WOL_WOLAPIGONE[] = "Connection to Westwood Online has been lost!"; +// Appears after game, when attempting to get back into WW Online. +const char TXT_WOL_WOLAPIREINIT[] = "Connection to Westwood Online was lost. Reinitializing..."; + +const char TXT_WOL_NOTPAGED[] = "Can't respond to page; no one has paged you."; +// Appears briefly in the space for scenario name, in game setup dialog. +const char TXT_WOL_SCENARIONAMEWAIT[] = "waiting for scenario..."; +// Text of button on chat screen that takes user out of a chat channel, or up one WW Online level. +const char TXT_WOL_BACK[] = "Back"; + +const char TXT_WOL_AMDISCNEEDED[] = "The Aftermath disk will be required for this game; please insert it now."; + +const char TXT_WOL_CONFIRMLOGOUT[] = "Are you sure you want to leave Westwood Online?"; +// "Propose a stalemate" button. +const char TXT_WOL_PROPOSE_DRAW[] = "Propose a Draw"; +// Withdraw proposed stalemate button. +const char TXT_WOL_RETRACT_DRAW[] = "Retract Draw Proposal"; +// Accept offered stalemate button. +const char TXT_WOL_ACCEPT_DRAW[] = "Accept Proposed Draw"; +// User proposes that the game be declared a stalemate. +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Are you sure you want to propose a draw?"; +// User accepts the other's offer that the game be a tie. +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Are you sure you want to accept a draw?"; + +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "You have proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s has proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "You have retracted your offer of a draw."; + +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s has retracted the offer of a draw."; +// Message that appears in place of "Mission Accomplished" or "Mission Failed", when game is a draw. +const char TXT_WOL_DRAW[] = "The Game is a Draw"; +// Error message that appears when user's web browser can't be automatically launched. %s is a web site URL. +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Can't launch web browser to open %s!"; + +const char TXT_WOL_CHANNELFULL[] = "That chat/game channel is full."; + +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doubleclick to go to the top level. "; + +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doubleclick to go to the official chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doubleclick to go to the user chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doubleclick to go to the game channels level. "; + +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Loading list from Westwood Online, please wait... "; + +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doubleclick to go to the lobbies level. "; + +const char TXT_WOL_FINDINGLOBBY[] = "Connected - finding available lobby to enter..."; + +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; + +const char TXT_WOL_PRIVATETO[] = "Private to"; + +const char TXT_WOL_CS_MISSIONS[] = "Counterstrike Missions"; + +const char TXT_WOL_AM_MISSIONS[] = "Aftermath Missions"; + +const char TXT_WOL_CANTSQUELCHSELF[] = "You cannot disable viewing of your own messages!"; +// Title of the WW Online options dialog. +const char TXT_WOL_OPTTITLE[] = "Westwood Online Options"; + +const char TXT_WOL_SLOWUNITBUILD[] = "Slow Unit Build"; + +const char TXT_WOL_THEGAMEHOST[] = "The game host"; + +const char TXT_WOL_TTIP_RANKRA[] = " Show Red Alert ladder rankings "; + +const char TXT_WOL_TTIP_RANKAM[] = " Show Aftermath ladder rankings "; + +const char TXT_WOL_OPTRANKAM[] = "Show Aftermath rankings (instead of Red Alert)"; + +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (AND FORFEIT THE GAME)"; + +const char TXT_WOL_DLLERROR_GETIE3[] = "Your version of Windows is out of date. Please upgrade to Windows SP1, or install Internet Explorer 3.0 or higher."; + +const char TXT_WOL_DLLERROR_CALLUS[] = "An unexpected error has occurred. Please contact Westwood technical support."; + +const char TXT_WOL_PRIVATE[] = ""; + +#else + +#ifdef GERMAN +#pragma message( "...Building German version..." ) + +// Replaced "á" with ascii 169 (in octal, 251) in cases where 8 point font is used (see 8point.lbm for why). +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Unerwarteter Fehler trat bei der Kommunikation mit Westwood Online auf."; +const char TXT_WOL_CONNECT[] = "Verbinden"; +const char TXT_WOL_LOGINDIALOG[] = "Westwood-Online-Login"; +const char TXT_WOL_NAME[] = "Spitzname"; +const char TXT_WOL_PASSWORD[] = "Pa\251wort"; +const char TXT_WOL_SAVELOGIN[] = "Speichern"; +const char TXT_WOL_LOGINCANCEL[] = "Login abgebrochen."; +const char TXT_WOL_MISSINGNAME[] = "Bitte geben Sie Ihren Login-Spitznamen ein."; +const char TXT_WOL_MISSINGPASSWORD[] = "Bitte geben Sie Ihr Login-Pa\251wort ein."; +const char TXT_WOL_CANTSAVENICK[] = "Fehler beim Speichern des Spitznamens/Pa\251worts"; +const char TXT_WOL_NICKINUSE[] = "Dieser Spitzname wird bereits verwendet. Bitte w„hlen Sie einen anderen."; +const char TXT_WOL_BADPASS[] = "Ungltiges Pa\251wort fr diesen Spitznamen"; +const char TXT_WOL_TIMEOUT[] = "Verbindung zu Westwood Online unterbrochen"; +const char TXT_WOL_CONNECTING[] = "Verbinde zu Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Verbindung zu Westwood Online konnte nicht hergestellt werden."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Einloggen ... "; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Ausloggen ..."; +const char TXT_WOL_ERRORLOGOUT[] = "Verbindung zu Westwood Online beenden ..."; +const char TXT_WOL_WAIT[] = "Bitte warten ... Verbindung zu Westwood Online wird hergestellt ..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Offizieller Chat"; +const char TXT_WOL_USERCHAT[] = "User-Chat"; +const char TXT_WOL_GAMECHANNELS[] = "Game-Channels"; +const char TXT_WOL_REDALERTLOBBIES[] = "Alarmstufe-Rot-Lobbies"; +const char TXT_WOL_CHANNELLISTLOADING[] = "... Daten werden heruntergeladen ..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Sie befinden sich zur Zeit nicht in einem Chat-Channel."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Teilnehmen"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Sie k”nnen keinen neuen Channel erstellen, bevor Sie diesen Channel verlassen."; +const char TXT_WOL_NEWSOMETHING[] = "Neu"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Channel erstellen"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel-Name: "; +const char TXT_WOL_PASSPROMPT[] = "Pa\251wort: "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Pa\251wort (optional): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Sie nehmen am %s-Channel teil."; +const char TXT_WOL_YOUJOINEDGAME[] = "Sie nehmen an %ss Spiel teil."; +const char TXT_WOL_YOUCREATEDGAME[] = "Neues Spiel erstellt."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Sie haben die %s-Lobby betreten."; +const char TXT_WOL_YOULEFT[] = "Sie haben den %s-Channel verlassen."; +const char TXT_WOL_YOULEFTLOBBY[] = "Sie haben die %s-Lobby verlassen."; +const char TXT_WOL_JOINPRIVATETITLE[] = "An privatem Channel teilnehmen"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Channel-Pa\251wort eingeben: "; +const char TXT_WOL_BADCHANKEY[] = "Falsches Channel-Pa\251wort."; +const char TXT_WOL_PAGELOCATE[] = "Senden/Suchen"; +const char TXT_WOL_USERNAMEPROMPT[] = "User-Name: "; +const char TXT_WOL_PAGE[] = "Senden"; +const char TXT_WOL_LOCATE[] = "Suchen"; +const char TXT_WOL_LOCATING[] = "Suche %s..."; +const char TXT_WOL_FIND_NOTHERE[] = "Der gesuchte User-Name existiert nicht."; +const char TXT_WOL_FIND_NOCHAN[] = "Der genannte User befindet sich zur Zeit nicht in einem Channel."; +const char TXT_WOL_FIND_OFF[] = "Der genannte User hat die Suchfunktion ausgeschaltet."; +const char TXT_WOL_FOUNDIN[] = "User wurde im %s-Channel gefunden."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Sender"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Zu sendende Nachricht: "; +const char TXT_WOL_PAGING[] = "Sende an %s ..."; +const char TXT_WOL_PAGE_NOTHERE[] = "Der genannte User ist nicht eingeloggt."; +const char TXT_WOL_PAGE_OFF[] = "Der genannte User hat die Empfangsfunktion ausgeschaltet."; +const char TXT_WOL_ONPAGE[] = "Sende von %s: %s"; +const char TXT_WOL_WASPAGED[] = "Die Nachricht wurde %s erfolgreich zugestellt."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s ist zur Zeit nicht erreichbar."; +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s ist jetzt wieder erreichbar."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Nur der Channel-Besitzer kann andere User hinauswerfen."; +const char TXT_WOL_USERKICKEDUSER[] = "%s hat %s aus dem Channel geworfen."; +const char TXT_WOL_USERKICKEDYOU[] = "Sie wurden von %s aus dem Channel geworfen."; +const char TXT_WOL_NOONETOKICK[] = "W„hlen Sie den/die User, die Sie hinauswerfen m”chten."; +const char TXT_WOL_USERWASBANNED[] = "%s hat keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Privates Spiel erstellen"; +const char TXT_WOL_YOUREBANNED[] = "Sie haben keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s hat das Spiel verlassen."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s hat an dem Spiel teilgenommen."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Sie wurden aus dem Spiel geworfen."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alarmstufe Rot: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Vergeltungsschlag: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_USERRANK[] = "%s (Platz %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Platz %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "Start"; +const char TXT_WOL_ACCEPTBUTTON[] = "Best„tigen"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s hat das Spiel abgebrochen."; +const char TXT_WOL_WAITINGTOSTART[] = "Spiel wird gestartet ..."; +const char TXT_WOL_TTIP_DISCON[] = " Westwood Online verlassen"; +const char TXT_WOL_TTIP_LEAVE[] = " Derzeitigen Channel verlassen "; +const char TXT_WOL_TTIP_REFRESH[] = " Channel-Liste aktualisieren "; +const char TXT_WOL_TTIP_SQUELCH[] = " Nachrichteneingang von User(n) ein/ausschalten"; +const char TXT_WOL_TTIP_BAN[] = " User(n) Zutritt zum Channel verwehren "; +const char TXT_WOL_TTIP_KICK[] = " User aus dem Channel werfen "; +const char TXT_WOL_TTIP_FINDPAGE[] = " User suchen oder an User senden "; +const char TXT_WOL_TTIP_OPTIONS[] = " Westwood-Online-Optionen einstellen "; +const char TXT_WOL_TTIP_LADDER[] = " Alarmstufe-Rot-Tabelle durchsuchen "; +const char TXT_WOL_TTIP_HELP[] = " Westwood-Online-Hilfe anzeigen "; +const char TXT_WOL_TTIP_START[] = " Spiel starten "; +const char TXT_WOL_TTIP_ACCEPT[] = " Aktuelle Spieleinstellungen best„tigen "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Listen erweitern/verkleinern "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_JOIN[] = " An Chat- oder Game-Channel teilnehmen "; +const char TXT_WOL_TTIP_BACK[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_CREATE[] = " Neuen Chat- oder Game-Level erstellen "; +const char TXT_WOL_TTIP_ACTION[] = " Action-Nachricht "; +const char TXT_WOL_OPTFIND[] = "Lassen Sie zu, da\251 andere Sie FINDEN."; +const char TXT_WOL_OPTPAGE[] = "Lassen Sie zu, da\251 andere Ihnen Nachrichten SENDEN."; +const char TXT_WOL_OPTLANGUAGE[] = "Unangemessene Sprache herausfiltern."; +const char TXT_WOL_OPTGAMESCOPE[] = "Nur lokale Spiel-Lobby anzeigen."; +//const char TXT_WOL_OPTTITLE[] = "Optionen"; +const char TXT_WOL_CHANNELGONE[] = "Channel existiert nicht mehr."; +const char TXT_WOL_CG_TITLE[] = "Spiel erstellen"; +const char TXT_WOL_CG_PLAYERS[] = "Spieler: %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Turnier"; +const char TXT_WOL_CG_PRIVACY[] = "Privat"; +const char TXT_WOL_CG_RAGAME[] = "Alarmstufe-Rot-Spiel"; +const char TXT_WOL_CG_CSGAME[] = "Gegenangriff-Spiel"; +const char TXT_WOL_CG_AMGAME[] = "Vergeltungsschlag-Spiel"; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sie mssen 'Gegenangriff' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_NEEDAFTERMATH[] = "Sie mssen 'Vergeltungsschlag' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doppelklick zur Teilnahme am '%s'-Channel (z.Z. %i User). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doppelklick zur Teilnahme an der '%s'-Lobby (z.Z. %i User). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alarmstufe Rot"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Gegenangriff"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Vergeltungsschlag"; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s-Spiel (%i Spieler von maximal %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s-Spiel (%i Spieler). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Privat) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Turnier) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doppelklicken Sie, um %s-Spiele anzusehen. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Turnierspiele mssen von zwei Spieler gespielt werden."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Pa\251wort: %s"; +const char TXT_WOL_RULESMISMATCH[] = "Ihr Spiel ist mit dem des Host nicht kompatibel!"; +const char TXT_WOL_STARTTIMEOUT[] = "Keine Antworten von G„sten! Spielstart abgebrochen."; +const char TXT_WOL_STARTCANCELLED[] = "Spielstart abgebrochen."; +const char TXT_WOL_CANCELGAME[] = "Zurck"; +const char TXT_WOL_PATCHQUESTION[] = "Ein Update-Patch wird fr Internet-Spiele ben”tigt. M”chten Sie es jetzt herunterladen?"; +const char TXT_WOL_DOWNLOADING[] = "Datei %i von %i herunterladen"; +const char TXT_WOL_DOWNLOADERROR[] = "Ein Fehler trat beim Herunterladen der Dateien auf."; +const char TXT_WOL_DOWNLOADBYTES[] = "%i Bytes von %i erhalten. (%i%%%%)"; +const char TXT_WOL_DOWNLOADTIME[] = "Verbleibende Zeit: %i Min. %i Sek."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Nach Unterbrechung wiederaufgenommen.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Verbinde ..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Suche Datei ..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Lade herunter ..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Herunterladen abgeschlossen! Alarmstufe Rot wird jetzt neugestartet, damit das Update-Patch angewendet werden kann."; +const char TXT_WOL_HELPSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Westwood-Online-Hilfe starten m”chten?"; +const char TXT_WOL_LADDERSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Alarmstufe-Rot-Tabellen starten m”chten?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Keine gespeicherten User-Namen gefunden. M”chten Sie einen neuen User-Namen fr Westwood Online eintragen?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr Informationen ber %s starten m”chten?"; +const char TXT_WOL_USERLIST[] = "User: %i"; +const char TXT_WOL_NOUSERLIST[] = "(nicht in einem Channel)"; +const char TXT_WOL_CANTCREATEHERE[] = "Um ein Spiel zu starten, mssen Sie in der Lobby Alarmstufe Rot sein."; +const char TXT_WOL_WOLAPIGONE[] = "Verbindung zu Westwood Online verloren!"; +const char TXT_WOL_WOLAPIREINIT[] = "Verbindung zu Westwood Online verloren. Verbinde erneut ..."; +const char TXT_WOL_NOTPAGED[] = "Kann keine Antwort senden, niemand hat Ihnen geschrieben."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "Warte auf Szenario ..."; +const char TXT_WOL_BACK[] = "Zurck"; +const char TXT_WOL_AMDISCNEEDED[] = "Die CD 'Vergeltungsschlag' wird fr dieses Spiel ben”tigt, bitte legen Sie sie jetzt ein."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Sind Sie sicher, da\251 Sie Westwood Online verlassen m”chten?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Unentschieden vorschlagen"; +const char TXT_WOL_RETRACT_DRAW[] = "Unentschieden-Vorschlag zurckziehen"; +const char TXT_WOL_ACCEPT_DRAW[] = "Unentschieden-Vorschlag akzeptieren"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden vorschlagen m”chten?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden akzeptieren m”chten?"; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Sie haben vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s hat vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Sie haben Ihr Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s hat das Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW[] = "Das Spiel ist unentschieden"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Web-Browser kann %s nicht ”ffnen!"; +const char TXT_WOL_CHANNELFULL[] = "Dieser Chat-/Game-Channel ist voll."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doppelklicken Sie, um zum obersten Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doppelklicken Sie, um zum offiziellen Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doppelklicken Sie, um zum User-Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doppelklicken Sie, um zum Game-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Liste von Westwood Online wird geladen, bitte warten... "; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doppelklicken Sie, um zum Lobby-Level zu gelangen. "; +const char TXT_WOL_FINDINGLOBBY[] = "Verbunden - suche verfgbare Lobby..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; +const char TXT_WOL_PRIVATETO[] = "Privat an"; +const char TXT_WOL_CS_MISSIONS[] = "Gegenangriff-Missionen"; +const char TXT_WOL_AM_MISSIONS[] = "Vergeltungsschlag-Missionen"; +const char TXT_WOL_CANTSQUELCHSELF[] = "Sie k”nnen die Option zum Lesen Ihrer eigenen Nachrichten nicht ausschalten!"; +const char TXT_WOL_OPTTITLE[] = "Westwood Online-Optionen"; +const char TXT_WOL_SLOWUNITBUILD[] = "Einheitenbau verlangsamen"; +const char TXT_WOL_THEGAMEHOST[] = "Der Spiel-Host"; +const char TXT_WOL_TTIP_RANKRA[] = " Alarmstufe-Rot-Platz anzeigen "; +const char TXT_WOL_TTIP_RANKAM[] = " Vergeltungsschlag-Platz anzeigen "; +const char TXT_WOL_OPTRANKAM[] = "Vergeltungsschlag-Platz anzeigen."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (UND BSSEN SIE DAS SPIEL EIN)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Ihre Version der Windows ist veraltet. Bauen Sie bitte zu den Windows SP1, aus oder installieren Sie Internet Explorer 3,0 oder h”heres."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Ein unerwarteter Fehler ist aufgetreten. Bitte wenden Sie sich an den Technischen Kundendienst."; +const char TXT_WOL_PRIVATE[] = ""; + +#else // FRENCH + +#pragma message( "...Building French version..." ) + +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Erreur inattendue lors de la connexion … Westwood Online."; +const char TXT_WOL_CONNECT[] = "Se connecter"; +const char TXT_WOL_LOGINDIALOG[] = "Identifiant … Westwood Online"; +const char TXT_WOL_NAME[] = "Pseudo"; +const char TXT_WOL_PASSWORD[] = "Mot de passe"; +const char TXT_WOL_SAVELOGIN[] = "Sauvegarder"; +const char TXT_WOL_LOGINCANCEL[] = "Ouverture de session annul‚e."; +const char TXT_WOL_MISSINGNAME[] = "Veuillez entrer l'identifiant pour votre pseudo."; +const char TXT_WOL_MISSINGPASSWORD[] = "Veuillez entrer l'identifiant pour votre mot de passe."; +const char TXT_WOL_CANTSAVENICK[] = "Erreur lors de la sauvegarde du pseudo/mot de passe."; +const char TXT_WOL_NICKINUSE[] = "Ce pseudo est d‚j… utilis‚. S‚lectionnez-en un autre."; +const char TXT_WOL_BADPASS[] = "Mot de passe invalide pour ce pseudo."; +const char TXT_WOL_TIMEOUT[] = "Expiration du temps de connexion … Westwood Online."; +const char TXT_WOL_CONNECTING[] = "Connexion … Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Impossible d'‚tablir la connexion … Westwood Online."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Ouverture de la session en cours..."; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Fermeture de la session en cours..."; +const char TXT_WOL_ERRORLOGOUT[] = "Fin de connexion avec Westwood Online..."; +const char TXT_WOL_WAIT[] = "Attendez svp, en communication … Westwood Online..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Conversation officielle"; +const char TXT_WOL_USERCHAT[] = "Conversation utilisateur"; +const char TXT_WOL_GAMECHANNELS[] = "Canaux de jeu"; +const char TXT_WOL_REDALERTLOBBIES[] = "Salons d'Alerte Rouge"; +const char TXT_WOL_CHANNELLISTLOADING[] = "...En cours de t‚l‚chargement..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Vous n'ˆtes pas dans un canal de conversation."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Rejoindre"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Cr‚ation d'un nouveau canal impossible tant que vous ne quittez pas ce canal."; +const char TXT_WOL_NEWSOMETHING[] = "Nouveau"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Cr‚er un canal"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Nom du canal : "; +const char TXT_WOL_PASSPROMPT[] = "Mot de passe : "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Mot de passe (en option): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Vous avez rejoint le canal %s."; +const char TXT_WOL_YOUJOINEDGAME[] = "Vous rejoignez la partie de %s."; +const char TXT_WOL_YOUCREATEDGAME[] = "Cr‚ation d'une nouvelle partie."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Vous ˆtes entr‚ dans le salon %s."; +const char TXT_WOL_YOULEFT[] = "Vous avez quitt‚ le canal %s."; +const char TXT_WOL_YOULEFTLOBBY[] = "Vous avez quitt‚ le salon %s."; +const char TXT_WOL_JOINPRIVATETITLE[] = "Rejoindre un canal priv‚"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Entrer le mot de passe du canal : "; +const char TXT_WOL_BADCHANKEY[] = "Mot de passe du canal incorrect."; +const char TXT_WOL_PAGELOCATE[] = "Envoyer/Rechercher"; +const char TXT_WOL_USERNAMEPROMPT[] = "Nom de l'utilisateur : "; +const char TXT_WOL_PAGE[] = "Envoyer"; +const char TXT_WOL_LOCATE[] = "Rechercher"; +const char TXT_WOL_LOCATING[] = "Recherche de %s en cours ..."; +const char TXT_WOL_FIND_NOTHERE[] = "Le nom de l'utilisateur sp‚cifi‚ n'existe pas."; +const char TXT_WOL_FIND_NOCHAN[] = "L'utilisateur sp‚cifi‚ n'est pas sur le canal pour le moment."; +const char TXT_WOL_FIND_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction de recherche."; +const char TXT_WOL_FOUNDIN[] = "Utilisateur trouv‚ dans le canal %s."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Envoyer … l'utilisateur"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message … envoyer : "; +const char TXT_WOL_PAGING[] = "Envoi … %s en cours..."; +const char TXT_WOL_PAGE_NOTHERE[] = "L'utilisateur sp‚cifi‚ n'a pas ouvert la session."; +const char TXT_WOL_PAGE_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction d'envoi de messages."; +const char TXT_WOL_ONPAGE[] = "Envoi de %s : %s"; +const char TXT_WOL_WASPAGED[] = "Envoi … %s r‚ussi."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s a ‚t‚ rejet‚."; // ajw rejete really means squelched? +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s n'est plus rejet‚."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Seul le responsable du canal peut expulser des utilisateurs."; +const char TXT_WOL_USERKICKEDUSER[] = "%s expulse %s du canal."; +const char TXT_WOL_USERKICKEDYOU[] = "Vous ˆtes expuls‚ du canal par %s."; +const char TXT_WOL_NOONETOKICK[] = "S‚lectionnez l'(les) utilisateur(s) que vous voulez expulser."; +const char TXT_WOL_USERWASBANNED[] = "%s est exclu du canal."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Cr‚er une partie priv‚e"; +const char TXT_WOL_YOUREBANNED[] = "Vous n'ˆtes pas autoris‚ … entrer dans ce canal."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s a quitt‚ la partie."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s a rejoint la partie."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Vous avez ‚t‚ expuls‚ de la partie."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alerte Rouge: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Missions M.A.D.: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_USERRANK[] = "%s (Position %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Position %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "D‚marrer"; +const char TXT_WOL_ACCEPTBUTTON[] = "Accepter"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s a annul‚ la partie."; +const char TXT_WOL_WAITINGTOSTART[] = "Lancement de la partie..."; +const char TXT_WOL_TTIP_DISCON[] = " Quitter Westwood Online "; +const char TXT_WOL_TTIP_LEAVE[] = " Quitter le canal o— vous vous trouvez "; +const char TXT_WOL_TTIP_REFRESH[] = " RafraŒchir la liste du canal "; +const char TXT_WOL_TTIP_SQUELCH[] = " Activer/d‚sactiver les messages en provenance de(s) l'utilisateur(s) "; +const char TXT_WOL_TTIP_BAN[] = " Exclure l'/les utilisateur(s)du canal "; +const char TXT_WOL_TTIP_KICK[] = " Expulser l'/les utilisateurs du canal "; +const char TXT_WOL_TTIP_FINDPAGE[] = " Rechercher ou envoyer un message … un utilisateur "; +const char TXT_WOL_TTIP_OPTIONS[] = " R‚gler les options de Westwood Online "; +const char TXT_WOL_TTIP_LADDER[] = " Parcourir les hi‚rarchies d'Alerte Rouge "; +const char TXT_WOL_TTIP_HELP[] = " Afficher l'aide de Westwood Online "; +const char TXT_WOL_TTIP_START[] = " D‚marrer le jeu "; +const char TXT_WOL_TTIP_ACCEPT[] = " Valider les paramŠtres actuels du jeu "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Compl‚ter/r‚duire la liste "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_JOIN[] = " Rejoindre un canal de conversation/jeu "; +const char TXT_WOL_TTIP_BACK[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_CREATE[] = " Cr‚er un nouveau canal de conversation/jeu "; +const char TXT_WOL_TTIP_ACTION[] = " Message d'action "; +const char TXT_WOL_OPTFIND[] = "Laisser les autres vous RECHERCHER."; +const char TXT_WOL_OPTPAGE[] = "Laisser les autres vous ENVOYER des messages."; +const char TXT_WOL_OPTLANGUAGE[] = "Filtrer les vulgarit‚s."; +const char TXT_WOL_OPTGAMESCOPE[] = "Afficher seulement les parties en salons locaux."; +//const char TXT_WOL_OPTTITLE[] = "Options"; +const char TXT_WOL_CHANNELGONE[] = "Ce canal n'existe plus."; +const char TXT_WOL_CG_TITLE[] = "Cr‚er une partie"; +const char TXT_WOL_CG_PLAYERS[] = "Joueurs : %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Tournoi"; +const char TXT_WOL_CG_PRIVACY[] = "Priv‚e"; +const char TXT_WOL_CG_RAGAME[] = "Partie Alerte Rouge"; +const char TXT_WOL_CG_CSGAME[] = "Partie Missions Ta‹ga"; +const char TXT_WOL_CG_AMGAME[] = "Partie Missions M.A.D."; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "D‚sol‚, vous devez installer Missions Ta‹ga pour jouer cette partie."; +const char TXT_WOL_NEEDAFTERMATH[] = "D‚sol‚, vous devez installer Missions M.A.D. pour jouer cette partie."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Double-clic pour rejoindre canal %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Double-clic pour rejoindre salon %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alerte Rouge"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Missions Ta‹ga"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Missions M.A.D."; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " Partie de %s (%i joueurs pour un max. de %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " Partie de %s (%i joueurs). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Priv‚e) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournoi) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Double-clic pour afficher les parties %s. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Les parties en tournoi doivent rassembler deux joueurs."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Mot de passe : %s"; +const char TXT_WOL_RULESMISMATCH[] = "Votre partie n'est pas compatible avec celle du serveur !"; +const char TXT_WOL_STARTTIMEOUT[] = "Expiration du temps de r‚ponse des clients ! D‚marrage du jeu annul‚."; +const char TXT_WOL_STARTCANCELLED[] = "D‚marrage du jeu annul‚."; +const char TXT_WOL_CANCELGAME[] = "Retour"; +const char TXT_WOL_PATCHQUESTION[] = "Un patch mis … jour est n‚cessaire pour le jeu sur Internet. Voulez-vous le t‚l‚charger maintenant ?"; +const char TXT_WOL_DOWNLOADING[] = "T‚l‚charger %i fichier(s) sur %i."; +const char TXT_WOL_DOWNLOADERROR[] = "Erreur lors du t‚l‚chargement du fichier."; +const char TXT_WOL_DOWNLOADBYTES[] = "R‚ception de %i octets sur %i. (%i%%%%)."; +const char TXT_WOL_DOWNLOADTIME[] = "Temps restant : %i min. %i secs."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (reprise aprŠs interruption.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Etat : en cours de connexion..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Etat : recherche du fichier..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Etat : en cours de t‚l‚chargement..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "T‚l‚chargement termin‚ ! Alerte Rouge est relanc‚ pour que le nouveau patch soit pris en compte."; +const char TXT_WOL_HELPSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir l'aide Westwood Online ?"; +const char TXT_WOL_LADDERSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour les hi‚rarchies d'Alerte Rouge ?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Aucun nom d'utilisateur sauvegard‚. Voulez-vous enregistrer un nouveau nom d'utilisateur pour Westwood Online ?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir des informations sur %s ?"; +const char TXT_WOL_USERLIST[] = "Utilisateurs %i"; +const char TXT_WOL_NOUSERLIST[] = "(absent du canal)"; +const char TXT_WOL_CANTCREATEHERE[] = "Pour commencer une partie, vous devez ˆtre dans un salon d'Alerte Rouge."; +const char TXT_WOL_WOLAPIGONE[] = "Perte de connexion avec Westwood Online !"; +const char TXT_WOL_WOLAPIREINIT[] = "Perte de connexion avec Westwood Online. R‚initialisation en cours..."; +const char TXT_WOL_NOTPAGED[] = "Impossible de r‚pondre au message ; personne ne vous en a envoy‚."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "En attente du sc‚nario..."; +const char TXT_WOL_BACK[] = "Retour"; +const char TXT_WOL_AMDISCNEEDED[] = "Le CD de Missions M.A.D. est n‚cessaire pour cette partie ; ins‚rez-le maintenant."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Voulez-vous vraiment quitter Westwood Online ?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Proposer une fin avec ‚galit‚"; +const char TXT_WOL_RETRACT_DRAW[] = "Annuler la proposition de fin avec ‚galit‚"; +const char TXT_WOL_ACCEPT_DRAW[] = "Accepter la proposition de fin avec ‚galit‚"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Voulez-vous vraiment proposer une fin avec ‚galit‚ ?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Voulez-vous vraiment accepter une fin avec ‚galit‚ ?" ; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Vous proposez de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s a propos‚ de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Vous avez annul‚ votre proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s a annul‚ sa proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW[] = "Match nul"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Impossible de lancer le navigateur web pour ouvrir %s !"; +const char TXT_WOL_CHANNELFULL[] = "Ce canal de jeu/conversation est satur‚."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Double-clic pour retourner au premier niveau. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Double-clic pour les canaux de conversation officiels. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Double-clic pour les canaux d' utilisateur. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Double-clic pour acc‚der au niveau des canaux de jeu. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Chargement de la liste depuis Westwood Online, veuillez patienter..."; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Double-clic pour acc‚der au niveau des salons. "; +const char TXT_WOL_FINDINGLOBBY[] = "Connection : recherche de salons disponibles..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = " :"; +const char TXT_WOL_PRIVATETO[] = "Message personnel …"; +const char TXT_WOL_CS_MISSIONS[] = "Missions extraites de Missions Ta‹ga"; +const char TXT_WOL_AM_MISSIONS[] = "Missions extraites de Missions M.A.D."; +const char TXT_WOL_CANTSQUELCHSELF[] = "Vous ne pouvez pas d‚sactiver vos propres messages!"; +const char TXT_WOL_OPTTITLE[] = "Options de Westwood Online"; +const char TXT_WOL_SLOWUNITBUILD[] = "Ralentir la Construction"; +const char TXT_WOL_THEGAMEHOST[] = "Le serveur"; +const char TXT_WOL_TTIP_RANKRA[] = " Afficher les positions d'Alerte Rouge "; +const char TXT_WOL_TTIP_RANKAM[] = " Afficher les positions de Missions M.A.D. "; +const char TXT_WOL_OPTRANKAM[] = "Afficher les positions de Missions M.A.D."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (ET RENONCER AU JEU)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Votre version des Windows est d‚mod‚e. Am‚liorez s'il vous plait aux Windows SP1, ou installez l'Internet Explorer 3,0 ou plus haut."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Une erreur inattendue s'est produite. Veuillez contacter l'assistance technique de Electronic Arts."; +const char TXT_WOL_PRIVATE[] = ""; + +#endif + +#endif + + +#endif diff --git a/CODE/WOLSTRNG.H b/CODE/WOLSTRNG.H new file mode 100644 index 0000000..4fd9e72 --- /dev/null +++ b/CODE/WOLSTRNG.H @@ -0,0 +1,212 @@ +/* +** 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 . +*/ + +// Wolstrng.h - this will presumably go away eventually. + +// New character strings. + +extern const char TXT_WOL_INTERNETBUTTON[]; +extern const char TXT_WOL_ERRORMESSAGE[]; +extern const char TXT_WOL_CONNECT[]; +extern const char TXT_WOL_LOGINDIALOG[]; +extern const char TXT_WOL_NAME[]; +extern const char TXT_WOL_PASSWORD[]; +extern const char TXT_WOL_SAVELOGIN[]; +extern const char TXT_WOL_LOGINCANCEL[]; +extern const char TXT_WOL_MISSINGNAME[]; +extern const char TXT_WOL_MISSINGPASSWORD[]; +extern const char TXT_WOL_CANTSAVENICK[]; +extern const char TXT_WOL_NICKINUSE[]; +extern const char TXT_WOL_BADPASS[]; +extern const char TXT_WOL_TIMEOUT[]; +extern const char TXT_WOL_CONNECTING[]; +extern const char TXT_WOL_CANTCONNECT[]; +extern const char TXT_WOL_ATTEMPTLOGIN[]; +extern const char TXT_WOL_ATTEMPTLOGOUT[]; +extern const char TXT_WOL_ERRORLOGOUT[]; +extern const char TXT_WOL_WAIT[]; +extern const char TXT_WOL_OFFICIALCHAT[]; +extern const char TXT_WOL_USERCHAT[]; +extern const char TXT_WOL_YOURENOTINCHANNEL[]; +extern const char TXT_WOL_ACTION[]; +extern const char TXT_WOL_JOIN[]; +extern const char TXT_WOL_CANTCREATEINCHANNEL[]; +extern const char TXT_WOL_NEWSOMETHING[]; +extern const char TXT_WOL_CREATECHANNELTITLE[]; +extern const char TXT_WOL_CREATECHANNELPROMPT[]; +extern const char TXT_WOL_PASSPROMPT[]; +extern const char TXT_WOL_OPTIONALPASSPROMPT[]; +extern const char TXT_WOL_CHANNEL_TOP[]; +extern const char TXT_WOL_CHANNEL_BACK[]; +extern const char TXT_WOL_YOUJOINED[]; +extern const char TXT_WOL_YOUJOINEDGAME[]; +extern const char TXT_WOL_YOUCREATEDGAME[]; +extern const char TXT_WOL_YOUJOINEDLOBBY[]; +extern const char TXT_WOL_YOULEFT[]; +extern const char TXT_WOL_YOULEFTLOBBY[]; +extern const char TXT_WOL_JOINPRIVATETITLE[]; +extern const char TXT_WOL_JOINPRIVATEPROMPT[]; +extern const char TXT_WOL_BADCHANKEY[]; +extern const char TXT_WOL_PAGELOCATE[]; +extern const char TXT_WOL_USERNAMEPROMPT[]; +extern const char TXT_WOL_PAGE[]; +extern const char TXT_WOL_LOCATE[]; +extern const char TXT_WOL_LOCATING[]; +extern const char TXT_WOL_FIND_NOTHERE[]; +extern const char TXT_WOL_FIND_NOCHAN[]; +extern const char TXT_WOL_FIND_OFF[]; +extern const char TXT_WOL_FOUNDIN[]; +extern const char TXT_WOL_PAGEMESSAGETITLE[]; +extern const char TXT_WOL_PAGEMESSAGEPROMPT[]; +extern const char TXT_WOL_PAGING[]; +extern const char TXT_WOL_PAGE_NOTHERE[]; +extern const char TXT_WOL_PAGE_OFF[]; +extern const char TXT_WOL_ONPAGE[]; +extern const char TXT_WOL_WASPAGED[]; +//extern const char TXT_WOL_USERISSQUELCHED[]; +//extern const char TXT_WOL_USERISNOTSQUELCHED[]; +extern const char TXT_WOL_ONLYOWNERCANKICK[]; +extern const char TXT_WOL_USERKICKEDUSER[]; +extern const char TXT_WOL_USERKICKEDYOU[]; +extern const char TXT_WOL_NOONETOKICK[]; +extern const char TXT_WOL_USERWASBANNED[]; +extern const char TXT_WOL_GAMECHANNELS[]; +extern const char TXT_WOL_CHANNELLISTLOADING[]; +extern const char TXT_WOL_CREATEPRIVGAMETITLE[]; +extern const char TXT_WOL_YOUREBANNED[]; +extern const char TXT_WOL_PLAYERLEFTGAME[]; +extern const char TXT_WOL_PLAYERJOINEDGAME[]; +extern const char TXT_WOL_YOUWEREKICKEDFROMGAME[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORD[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORDAM[]; +extern const char TXT_WOL_USERRANK[]; +extern const char TXT_WOL_USERHOUSE[]; +extern const char TXT_WOL_USERRANKHOUSE[]; +extern const char TXT_WOL_STARTBUTTON[]; +extern const char TXT_WOL_ACCEPTBUTTON[]; +extern const char TXT_WOL_HOSTLEFTGAME[]; +extern const char TXT_WOL_WAITINGTOSTART[]; +extern const char TXT_WOL_TTIP_DISCON[]; +extern const char TXT_WOL_TTIP_LEAVE[]; +extern const char TXT_WOL_TTIP_REFRESH[]; +extern const char TXT_WOL_TTIP_SQUELCH[]; +extern const char TXT_WOL_TTIP_BAN[]; +extern const char TXT_WOL_TTIP_KICK[]; +extern const char TXT_WOL_TTIP_FINDPAGE[]; +extern const char TXT_WOL_TTIP_OPTIONS[]; +extern const char TXT_WOL_TTIP_LADDER[]; +extern const char TXT_WOL_TTIP_HELP[]; +extern const char TXT_WOL_TTIP_START[]; +extern const char TXT_WOL_TTIP_ACCEPT[]; +extern const char TXT_WOL_TTIP_EXPANDLIST[]; +extern const char TXT_WOL_TTIP_CANCELGAME[]; +extern const char TXT_WOL_TTIP_JOIN[]; +extern const char TXT_WOL_TTIP_BACK[]; +extern const char TXT_WOL_TTIP_CREATE[]; +extern const char TXT_WOL_TTIP_ACTION[]; +extern const char TXT_WOL_OPTFIND[]; +extern const char TXT_WOL_OPTPAGE[]; +extern const char TXT_WOL_OPTLANGUAGE[]; +extern const char TXT_WOL_OPTGAMESCOPE[]; +extern const char TXT_WOL_OPTTITLE[]; +extern const char TXT_WOL_REDALERTLOBBIES[]; +extern const char TXT_WOL_TOPLEVELTITLE[]; +extern const char TXT_WOL_CHANNELGONE[]; +extern const char TXT_WOL_CG_TITLE[]; +extern const char TXT_WOL_CG_PLAYERS[]; +extern const char TXT_WOL_CG_TOURNAMENT[]; +extern const char TXT_WOL_CG_PRIVACY[]; +extern const char TXT_WOL_CG_RAGAME[]; +extern const char TXT_WOL_CG_CSGAME[]; +extern const char TXT_WOL_CG_AMGAME[]; +extern const char TXT_WOL_NEEDCOUNTERSTRIKE[]; +extern const char TXT_WOL_NEEDAFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_CHAT[]; +extern const char TXT_WOL_TTIP_CHANLIST_LOBBY[]; +extern const char TXT_WOL_TTIP_REDALERT[]; +extern const char TXT_WOL_TTIP_COUNTERSTRIKE[]; +extern const char TXT_WOL_TTIP_AFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_RAGAME[]; +extern const char TXT_WOL_TTIP_CHANLIST_GAME[]; +extern const char TXT_WOL_TTIP_PRIVATEGAME[]; +extern const char TXT_WOL_TTIP_TOURNAMENTGAME[]; +extern const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[]; +extern const char TXT_WOL_TOURNAMENTPLAYERLIMIT[]; +extern const char TXT_WOL_PRIVATEPASSWORD[]; +extern const char TXT_WOL_RULESMISMATCH[]; +extern const char TXT_WOL_STARTTIMEOUT[]; +extern const char TXT_WOL_CANCELGAME[]; +extern const char TXT_WOL_PATCHQUESTION[]; +extern const char TXT_WOL_DOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADERROR[]; +extern const char TXT_WOL_DOWNLOADBYTES[]; +extern const char TXT_WOL_DOWNLOADTIME[]; +extern const char TXT_WOL_DOWNLOADRESUMED[]; +extern const char TXT_WOL_DOWNLOADCONNECTING[]; +extern const char TXT_WOL_DOWNLOADLOCATING[]; +extern const char TXT_WOL_DOWNLOADDOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADEXITWARNING[]; +extern const char TXT_WOL_HELPSHELL[]; +extern const char TXT_WOL_LADDERSHELL[]; +extern const char TXT_WOL_WEBREGISTRATIONSHELL[]; +extern const char TXT_WOL_GAMEADVERTSHELL[]; +extern const char TXT_WOL_USERLIST[]; +extern const char TXT_WOL_NOUSERLIST[]; +extern const char TXT_WOL_STARTCANCELLED[]; +extern const char TXT_WOL_CANTCREATEHERE[]; +extern const char TXT_WOL_WOLAPIGONE[]; +extern const char TXT_WOL_WOLAPIREINIT[]; +extern const char TXT_WOL_NOTPAGED[]; +extern const char TXT_WOL_SCENARIONAMEWAIT[]; +extern const char TXT_WOL_BACK[]; +extern const char TXT_WOL_AMDISCNEEDED[]; +extern const char TXT_WOL_CONFIRMLOGOUT[]; +extern const char TXT_WOL_PROPOSE_DRAW[]; +extern const char TXT_WOL_RETRACT_DRAW[]; +extern const char TXT_WOL_ACCEPT_DRAW[]; +extern const char TXT_WOL_PROPOSE_DRAW_CONFIRM[]; +extern const char TXT_WOL_ACCEPT_DRAW_CONFIRM[]; +extern const char TXT_WOL_DRAW_PROPOSED_LOCAL[]; +extern const char TXT_WOL_DRAW_PROPOSED_OTHER[]; +extern const char TXT_WOL_DRAW_RETRACTED_LOCAL[]; +extern const char TXT_WOL_DRAW_RETRACTED_OTHER[]; +extern const char TXT_WOL_DRAW[]; +extern const char TXT_WOL_CANTLAUNCHBROWSER[]; +extern const char TXT_WOL_CHANNELFULL[]; +extern const char TXT_WOL_CHANNELTYPE_TOP[]; +extern const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_USERCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_GAMES[]; +extern const char TXT_WOL_CHANNELTYPE_LOADING[]; +extern const char TXT_WOL_CHANNELTYPE_LOBBIES[]; +extern const char TXT_WOL_FINDINGLOBBY[]; +extern const char TXT_WOL_PRIVATETOMULTIPLE[]; +extern const char TXT_WOL_PRIVATETO[]; +extern const char TXT_WOL_CS_MISSIONS[]; +extern const char TXT_WOL_AM_MISSIONS[]; +extern const char TXT_WOL_CANTSQUELCHSELF[]; +extern const char TXT_WOL_SLOWUNITBUILD[]; +extern const char TXT_WOL_THEGAMEHOST[]; +extern const char TXT_WOL_TTIP_RANKRA[]; +extern const char TXT_WOL_TTIP_RANKAM[]; +extern const char TXT_WOL_OPTRANKAM[]; +extern const char TXT_WOL_CANCELMEANSFORFEIT[]; +extern const char TXT_WOL_DLLERROR_GETIE3[]; +extern const char TXT_WOL_DLLERROR_CALLUS[]; +extern const char TXT_WOL_PRIVATE[]; + diff --git a/CODE/WOL_CGAM.CPP b/CODE/WOL_CGAM.CPP new file mode 100644 index 0000000..31adbe8 --- /dev/null +++ b/CODE/WOL_CGAM.CPP @@ -0,0 +1,389 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION // Implies FIXIT_CSII. + +// Wol_CGam.cpp - Create game dialog. +// ajw 09/9/98 + +#include "function.h" + +#ifndef FIXIT_CSII +#error FIXIT_CSII must be defined. +#endif + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "BigCheck.h" + +//extern char* LoadShpFile( const char* szShpFile ); +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ); + +//*********************************************************************************************** +CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ) +{ + CREATEGAMEINFO cgiReturn; + cgiReturn.bCreateGame = false; + cgiReturn.iPlayerMax = 2; + cgiReturn.bTournament = false; + cgiReturn.bPrivate = false; + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + *cgiReturn.szPassword = 0; + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 150 * RESFACTOR; // dialog width + int d_dialog_h = 135 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_gaugeplayers_w = 70 * RESFACTOR; + int d_gaugeplayers_h = 9 * RESFACTOR; + int d_gaugeplayers_x = d_dialog_cx - d_gaugeplayers_w / 2; + int d_gaugeplayers_y = d_dialog_y + d_margin + 42; + + int d_checktourn_w = 75 * RESFACTOR; + int d_checktourn_h = 9 * RESFACTOR; + int d_checktourn_x = d_dialog_cx - d_checktourn_w / 2; + int d_checktourn_y = d_gaugeplayers_y + d_gaugeplayers_h + 10; + + int d_checkpriv_w = d_checktourn_w; + int d_checkpriv_h = 9 * RESFACTOR; + int d_checkpriv_x = d_checktourn_x; + int d_checkpriv_y = d_checktourn_y + d_checktourn_h + 10; + + int d_checkra_w = d_checktourn_w; + int d_checkra_h = 9 * RESFACTOR; + int d_checkra_x = d_checktourn_x; + int d_checkra_y = d_checkpriv_y + d_checkpriv_h + 20; + + int d_checkcs_w = d_checktourn_w; + int d_checkcs_h = 9 * RESFACTOR; + int d_checkcs_x = d_checktourn_x; + int d_checkcs_y = d_checkra_y + d_checkra_h + 5; + + int d_checkam_w = d_checktourn_w; + int d_checkam_h = 9 * RESFACTOR; + int d_checkam_x = d_checktourn_x; + int d_checkam_y = d_checkcs_y + d_checkcs_h + 5; + +#if (GERMAN | FRENCH) + int d_ok_w = 30 * RESFACTOR; +#else + int d_ok_w = 30 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_x + ( d_dialog_w / 3 ) - ( d_ok_w / 2 ); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + int d_cancel_w = 40 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ( ( d_dialog_w * 2 ) / 3 ) - ( d_cancel_w / 2 ); + int d_cancel_y = d_ok_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + GAUGE_PLAYERCOUNT, + CHECK_TOURNAMENT, + CHECK_PRIVACY, + CHECK_RA, + CHECK_CS, + CHECK_AM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + StaticButtonClass PlayerCountStatic( 0, " ", TPF_TEXT, d_gaugeplayers_x, d_gaugeplayers_y - 16 ); + GaugeClass PlayerCountGauge( GAUGE_PLAYERCOUNT, d_gaugeplayers_x, d_gaugeplayers_y, d_gaugeplayers_w, d_gaugeplayers_h ); + + if( pWO->bEgg8Player ) + PlayerCountGauge.Set_Maximum( 6 ); + else + PlayerCountGauge.Set_Maximum( 2 ); + PlayerCountGauge.Set_Value( cgiReturn.iPlayerMax - 2 ); + + BigCheckBoxClass TournamentCheck( CHECK_TOURNAMENT, d_checktourn_x, d_checktourn_y, d_checktourn_w, d_checktourn_h, + TXT_WOL_CG_TOURNAMENT, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bTournament ); + + BigCheckBoxClass PrivacyCheck( CHECK_PRIVACY, d_checkpriv_x, d_checkpriv_y, d_checkpriv_w, d_checkpriv_h, + TXT_WOL_CG_PRIVACY, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bPrivate ); + + BigCheckBoxClass RA_Check( CHECK_RA, d_checkra_x, d_checkra_y, d_checkra_w, d_checkra_h, + TXT_WOL_CG_RAGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::RAGAME ); + BigCheckBoxClass CS_Check( CHECK_CS, d_checkcs_x, d_checkcs_y, d_checkcs_w, d_checkcs_h, + TXT_WOL_CG_CSGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::CSGAME ); + BigCheckBoxClass AM_Check( CHECK_AM, d_checkam_x, d_checkam_y, d_checkam_w, d_checkam_h, + TXT_WOL_CG_AMGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::AMGAME ); + + if( !Is_Counterstrike_Installed() ) + CS_Check.Disable(); + + if( !Is_Aftermath_Installed() ) + AM_Check.Disable(); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + PlayerCountStatic.Add_Tail(*commands); + PlayerCountGauge.Add_Tail(*commands); + TournamentCheck.Add_Tail(*commands); + PrivacyCheck.Add_Tail(*commands); + RA_Check.Add_Tail(*commands); + CS_Check.Add_Tail(*commands); + AM_Check.Add_Tail(*commands); + + char szPlayerCount[ 100 ]; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_CG_TITLE, d_dialog_x, d_dialog_y, d_dialog_w ); +// Fancy_Text_Print( TXT_WOL_CG_PLAYERS, d_gaugeplayers_x - 2*RESFACTOR, d_gaugeplayers_y, +// GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_RIGHT ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + /* + ** Process input. + */ + + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + cgiReturn.bCreateGame = true; + process = false; + break; + + case ( BUTTON_CANCEL | KN_BUTTON ): + process = false; + break; + + case ( GAUGE_PLAYERCOUNT | KN_BUTTON ): + if( PlayerCountGauge.Get_Value() != 0 && cgiReturn.bTournament ) + { + WWMessageBox().Process( TXT_WOL_TOURNAMENTPLAYERLIMIT ); + PlayerCountGauge.Set_Value( 0 ); + display = true; + } + cgiReturn.iPlayerMax = PlayerCountGauge.Get_Value() + 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + break; + + case ( CHECK_TOURNAMENT | KN_BUTTON ): + cgiReturn.bTournament = TournamentCheck.IsOn; + if( cgiReturn.bTournament ) + { + PlayerCountGauge.Set_Value( 0 ); +// PlayerCountGauge.Disable(); + cgiReturn.iPlayerMax = 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + } +// else +// PlayerCountGauge.Enable(); + break; + + case ( CHECK_PRIVACY | KN_BUTTON ): + cgiReturn.bPrivate = PrivacyCheck.IsOn; + break; + + case ( CHECK_RA | KN_BUTTON ): + if( RA_Check.IsOn ) + { + // Box was checked. + CS_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + } + else + // Box was unchecked. Has no effect. + RA_Check.Turn_On(); + break; + case ( CHECK_CS | KN_BUTTON ): + if( CS_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::CSGAME; + } + else + // Box was unchecked. Has no effect. + CS_Check.Turn_On(); + break; + case ( CHECK_AM | KN_BUTTON ): + if( AM_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + CS_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::AMGAME; + } + else + // Box was unchecked. Has no effect. + AM_Check.Turn_On(); + break; + + default: + break; + } + } + + if( cgiReturn.bCreateGame && cgiReturn.bPrivate ) + { + // Get a password for the channel. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATEPRIVGAMETITLE, + TXT_WOL_PASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + strcpy( cgiReturn.szPassword, pEditDlg->szEdit ); + else + cgiReturn.bCreateGame = false; // Cancel creation. + pWO->bPump_In_Call_Back = false; + } + + return cgiReturn; +} + +//*********************************************************************************************** +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ) +{ + // Checks appropriate list item based on iPlayerMax. + switch( iPlayerMax ) + { + case 2: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 3: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 4: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + break; + } +} + +#endif diff --git a/CODE/WOL_CHAT.CPP b/CODE/WOL_CHAT.CPP new file mode 100644 index 0000000..4870381 --- /dev/null +++ b/CODE/WOL_CHAT.CPP @@ -0,0 +1,1581 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// wol_chat.cpp +// ajw 7/8/98 + +#include "function.h" +#include "iconlist.h" +#include "WolapiOb.h" +#include "SEditDlg.h" +#include "WolStrng.h" +#include "ToolTip.h" + +//#include "WolDebug.h" + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ); +bool ExitChatChannel( WolapiObject* pWO ); +void CreateChatChannel( WolapiObject* pWO ); +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ); +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ); + +enum LIST_EXPAND_STATE +{ + LES_NORMAL, + LES_CHANNELS_EXPANDED, + LES_USERS_EXPANDED, +}; +static LIST_EXPAND_STATE lesCurrent = LES_NORMAL; + +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ); +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ); +void ResizeChannelList( IconListClass& chanlist, bool bExpand ); +void ResizeUserList( IconListClass& userlist, bool bExpand ); + +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ); + +extern CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ); + +static int d_chanlist_w; +static int d_chanlist_h; +static int d_chanlist_x; +static int d_chanlist_y; + +static int d_userlist_w; +static int d_userlist_h; +static int d_userlist_x; +static int d_userlist_y; + +#define DRAWTOGDOWN Turn_Off() +#define DRAWTOGUP Turn_On() + +//*********************************************************************************************** +int WOL_Chat_Dialog( WolapiObject* pWO ) +{ + int rc; + bool bFirsttime = true; + bool bHackFocus = true; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_text_h = 12; + int d_margin1 = 34; // large margin + int d_margin2 = 14; // small margin + + int d_chatlist_w = 340; + int d_chatlist_x = d_dialog_x + d_margin1; + int d_chatlist_y = d_dialog_y + d_margin2 + d_margin1 + 27; + int d_chatlist_h = 337 - d_chatlist_y; + + d_chanlist_w = 227; + d_chanlist_h = 50 * RESFACTOR; + d_chanlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_chanlist_w); + d_chanlist_y = d_chatlist_y; + + d_userlist_w = d_chanlist_w; +// int d_userlist_h = ((10 * 6) + 3) *RESFACTOR; + d_userlist_x = d_chanlist_x; + d_userlist_y = d_chanlist_y + d_chanlist_h + 14 + 5; + + d_userlist_h = d_chatlist_y + d_chatlist_h - d_userlist_y; + + int d_action_w = 100; + int d_action_h = 9 *RESFACTOR; + int d_action_x = d_dialog_x + 500; + int d_action_y = 365; + +// int d_chanpriv_w = 60; +// int d_chanpriv_h = 9 *RESFACTOR; +// int d_chanpriv_x = d_dialog_x + 150; +// int d_chanpriv_y = d_action_y; + +// int d_cgame_w = 60; +// int d_cgame_h = 9 *RESFACTOR; +// int d_cgame_x = d_dialog_x + 390; //d_dialog_cx - d_cgame_w / 2; +// int d_cgame_y = d_action_y; + + int d_back_w = 100; + int d_back_h = 9 *RESFACTOR; + int d_back_x = d_dialog_x + 100; + int d_back_y = d_action_y; + + int d_join_w = 100; + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + 210; + int d_join_y = d_action_y; + + int d_create_w = 100; + int d_create_h = 9 *RESFACTOR; + int d_create_x = d_dialog_x + 320; //((d_dialog_w * 5) / 6) - (d_create_w / 2); + int d_create_y = d_action_y; + + int d_send_w = d_chanlist_x + d_chanlist_w - d_chatlist_x; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_chatlist_x; + int d_send_y = d_chatlist_y + d_chatlist_h + 5; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_CHATLIST, + BUTTON_CHANLIST, + BUTTON_USERLIST, + BUTTON_SENDEDIT, + BUTTON_ACTION, +// BUTTON_CGAME, + BUTTON_CREATE, + BUTTON_JOIN, + BUTTON_BACK, + BUTTON_EXPANDCHANNELS, + BUTTON_EXPANDUSERS, + BUTTON_RANKRA, + BUTTON_RANKAM + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + TTimerClass lastclick_timer; + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + char* pShpExpand = (char*)MFCD::Retrieve( "exp.shp" ); + char* pShpUnexpand = (char*)MFCD::Retrieve( "unexp.shp" ); + + IconListClass chatlist( BUTTON_CHATLIST, d_chatlist_x, d_chatlist_y, d_chatlist_w, d_chatlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 500 ); + ShapeButtonClass ExpandChanBtn( BUTTON_EXPANDCHANNELS, pShpExpand, d_chanlist_x + d_chanlist_w - 17, d_chanlist_y - 14 ); + IconListClass chanlist( BUTTON_CHANLIST, d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + ShapeButtonClass ExpandUserBtn( BUTTON_EXPANDUSERS, pShpExpand, d_userlist_x + d_userlist_w - 17, d_userlist_y - 14 ); + IconListClass userlist( BUTTON_USERLIST, d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); + TextButtonClass ActionBtn( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + TextButtonClass CreateBtn( BUTTON_CREATE, TXT_WOL_NEWSOMETHING, TPF_BUTTON, d_create_x, d_create_y, d_create_w ); + TextButtonClass JoinBtn( BUTTON_JOIN, TXT_WOL_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w ); + TextButtonClass BackBtn( BUTTON_BACK, TXT_WOL_BACK, TPF_BUTTON, d_back_x, d_back_y, d_back_w ); + char* szRecordToStartWith; + if( pWO->bShowRankRA ) + szRecordToStartWith = pWO->szMyRecord; + else + szRecordToStartWith = pWO->szMyRecordAM; + StaticButtonClass chatlistTitle( 0, szRecordToStartWith, TPF_TYPE, d_chatlist_x + 2, d_chatlist_y - 13, d_chatlist_w - 4, 12 ); + StaticButtonClass chanlistTitle( 0, "", TPF_TYPE, d_chanlist_x + 2, d_chanlist_y - 16 + 4, d_chanlist_w - 4 - 16, 12 ); + StaticButtonClass userlistTitle( 0, TXT_WOL_NOUSERLIST, TPF_TYPE, d_userlist_x + 2, d_userlist_y - 16 + 4, d_userlist_w - 4 - 16 * 4, 12 ); + + char szSendBuffer[MAXCHATSENDLENGTH] = ""; + EditClass sendedit( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + + char* pShpRankRA = (char*)MFCD::Retrieve( "rank_ra.shp" ); + char* pShpRankAM = (char*)MFCD::Retrieve( "rank_am.shp" ); + ShapeButtonClass RankRABtn( BUTTON_RANKRA, pShpRankRA, d_userlist_x + d_userlist_w - ( 16 * 4 + 1 ), d_userlist_y - 14 ); + ShapeButtonClass RankAMBtn( BUTTON_RANKAM, pShpRankAM, d_userlist_x + d_userlist_w - ( 16 * 3 + 1 ), d_userlist_y - 14 ); + // Change draw behavior of toggle buttons. + RankRABtn.ReflectButtonState = true; + RankAMBtn.ReflectButtonState = true; + + // Build the button list. + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + chatlist.Add_Tail(*commands); + ExpandChanBtn.Add_Tail(*commands); + chanlist.Add_Tail(*commands); + ExpandUserBtn.Add_Tail(*commands); + userlist.Add_Tail(*commands); + ActionBtn.Add_Tail(*commands); + CreateBtn.Add_Tail(*commands); +// CreatePrivBtn.Add_Tail(*commands); + JoinBtn.Add_Tail(*commands); + BackBtn.Add_Tail(*commands); +// CGameBtn.Add_Tail(*commands); + chatlistTitle.Add_Tail(*commands); + chanlistTitle.Add_Tail(*commands); + userlistTitle.Add_Tail(*commands); + sendedit.Add_Tail(*commands); + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead = NULL; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast = NULL; // ToolTip the mouse was last over, or null. + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + pToolTip = pToolTip->next; + ToolTipClass TTipChanExpand( &ExpandChanBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandChanBtn.X + 8, ExpandChanBtn.Y - 9, true ); + pToolTip->next = &TTipChanExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipUserExpand( &ExpandUserBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandUserBtn.X + 8, ExpandUserBtn.Y - 9, true ); + pToolTip->next = &TTipUserExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipChanList( &chanlist, 0, chanlist.X + 1, chanlist.Y + 1, true, true ); + pToolTip->next = &TTipChanList; + pToolTip = pToolTip->next; + ToolTipClass TTipJoin( &JoinBtn, TXT_WOL_TTIP_JOIN, d_join_x + d_join_w/2, d_join_y - 6 ); + pToolTip->next = &TTipJoin; + pToolTip = pToolTip->next; + ToolTipClass TTipBack( &BackBtn, TXT_WOL_TTIP_BACK, d_back_x + d_back_w/2, d_back_y - 6 ); + pToolTip->next = &TTipBack; + pToolTip = pToolTip->next; + ToolTipClass TTipCreate( &CreateBtn, TXT_WOL_TTIP_CREATE, d_create_x + d_create_w/2, d_create_y - 6 ); + pToolTip->next = &TTipCreate; + pToolTip = pToolTip->next; + ToolTipClass TTipAction( &ActionBtn, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + pToolTip->next = &TTipAction; + pToolTip = pToolTip->next; + ToolTipClass TTipRankRA( &RankRABtn, TXT_WOL_TTIP_RANKRA, RankRABtn.X + 8, RankRABtn.Y - 9, true ); + pToolTip->next = &TTipRankRA; + pToolTip = pToolTip->next; + ToolTipClass TTipRankAM( &RankAMBtn, TXT_WOL_TTIP_RANKAM, RankAMBtn.X + 8, RankAMBtn.Y - 9, true ); + pToolTip->next = &TTipRankAM; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + //........................................................................ + // List boxes + //........................................................................ + int tabs[] = { 150 }; // tabs for channel list + chanlist.Set_Tabs( tabs ); + +// Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + lastclick_timer = 0; + + // Tell WolapiObject about lists to use for output. + // (Sure wish I'd gone against the grain and made this dialog a class...) + pWO->LinkToChatDlg( &chatlist, &chanlist, &userlist, &userlistTitle ); + + if( !pWO->bChatShownBefore ) + { + // Print message of the day. + chatlist.Add_Item( pWO->pChatSink->szMotd, NULL, NULL, ICON_SHAPE ); + } + else + { + // We have returned to the chat dialog after being in either game setup or an actual game. + pWO->RestoreChat(); + pWO->DeleteSavedChat(); + + if( pWO->bReturningAfterGame ) + pWO->RejoinLobbyAfterGame(); + else + { + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of a game setup. + WOL_PrintMessage( chatlist, TXT_WOL_YOUWEREKICKEDFROMGAME, WOLCOLORREMAP_KICKORBAN ); + pWO->pChatSink->bGotKickedTrigger = false; + } + } + + if( pWO->iLobbyReturnAfterGame != -1 ) + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, pWO->iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, pWO->iLobbyReturnAfterGame ); + pWO->OnEnteringChatChannel( szChannelToJoin, false, iChannelLobbyNumber( (unsigned char*)szChannelToJoin ) ); + } + else + // Will never happen presumably, if games are always entered via a lobby chat channel. + pWO->EnterLevel_Top(); + + pWO->iLobbyReturnAfterGame = -1; + + if( pWO->bReturningAfterGame ) + { + Sound_Effect( WOLSOUND_LOGIN ); + pWO->bReturningAfterGame = false; + } + else + Sound_Effect( WOLSOUND_EXITGAME ); + } + + // Cause a refresh of szMyRecord, the string showing my win/loss record. + pWO->RequestLadders( pWO->szMyName ); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) + { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { +/* + if( pToolTipHitLast && pToolTipHitLast->bShowing ) // Lame hack. Problem is draws that occur in callbacks. + { + pToolTipHitLast->Unshow(); + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + pToolTipHitLast->Show(); + } + else + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + } +*/ + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + + // Special post-callback processing... + if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + rc = -1; // As if the user logged himself out. + process = false; + break; + } + if( pWO->pChatSink->bGotKickedTrigger ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_UserChat(); + } + else if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_Lobbies(); + } + else // Must be WOL_LEVEL_INOFFICIALCHATCHANNEL. + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_OfficialChat(); + } + pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + if( pWO->bMyRecordUpdated ) + { + if( pWO->bShowRankRA ) + chatlistTitle.Set_Text( pWO->szMyRecord ); + else + chatlistTitle.Set_Text( pWO->szMyRecordAM ); + pWO->bMyRecordUpdated = false; + } + if( pWO->bChannelListTitleUpdated ) + { + chanlistTitle.Set_Text( pWO->szChannelListTitle ); + pWO->bChannelListTitleUpdated = false; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Synch rank toggle buttons state. + if( BackBtn.Get_Prev() == &RankAMBtn ) + { + if( pWO->CurrentLevel != WOL_LEVEL_INLOBBY ) + { + // Rank buttons are there and shouldn't be. + RankRABtn.Remove(); + RankAMBtn.Remove(); + display = REDRAW_ALL; + } + else + { + if( pWO->bShowRankUpdated ) + { + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + // Cause my own record to get refreshed. + pWO->bMyRecordUpdated = true; + // Refresh list to show different rankings type. + pWO->ListChannelUsers(); + } + } + } + else + { + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Rank buttons aren't there and should be. + RankRABtn.Add( JoinBtn ); + RankAMBtn.Add( RankRABtn ); + RankRABtn.Flag_To_Redraw(); + RankAMBtn.Flag_To_Redraw(); + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + } + } + + // Regularly update the channels list in certain cases. + if( ( pWO->CurrentLevel == WOL_LEVEL_OFFICIALCHAT || pWO->CurrentLevel == WOL_LEVEL_USERCHAT || + pWO->CurrentLevel == WOL_LEVEL_LOBBIES || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) && + ::timeGetTime() > pWO->dwTimeNextChannelUpdate ) + { + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ); + break; + case WOL_LEVEL_USERCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ); + break; + case WOL_LEVEL_LOBBIES: // Overkill in this case to update so often... + pWO->UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ); + break; + case WOL_LEVEL_INLOBBY: + pWO->UpdateChannels( GAME_TYPE, CHANNELFILTER_LOCALLOBBYGAMES, true ); + break; + } + pWO->dwTimeNextChannelUpdate = ::timeGetTime() + CHANNELUPDATEWAIT; + } + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if( bFirsttime && !pWO->bChatShownBefore ) + { + WWMessageBox().Process( TXT_WOL_FINDINGLOBBY, TXT_NONE ); + char szLobbyName[ WOL_CHANNAME_LEN_MAX ]; + if( pWO->GetNameOfBeginningLobby( szLobbyName ) ) + { +// debugprint( "Found lobby to go into: '%s'\n", szLobbyName ); + if( !EnterChannel( pWO, chatlist, NULL, szLobbyName, false ) ) + { + // Could not enter channel for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + } + else + { + // Could not find name of a lobby for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + pWO->bChatShownBefore = true; + display = REDRAW_ALL; + // Play login sound. + Sound_Effect( WOLSOUND_LOGIN ); + } + bFirsttime = false; + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) + { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + + commands->Draw_All(); + + // Draw title bar above channel list. + Draw_Box( d_chanlist_x, d_chanlist_y - 15, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + switch( lesCurrent ) + { + case LES_CHANNELS_EXPANDED: + // Draw users title bar at bottom. + Draw_Box( d_userlist_x, d_userlist_y + d_userlist_h - 16, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + case LES_USERS_EXPANDED: + // Draw users title bar at top. + Draw_Box( d_chanlist_x, d_chanlist_y, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + break; + default: + // Draw users title bar in middle. + Draw_Box( d_userlist_x, d_userlist_y - 15, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + // Mouse button is down. + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + } + + // If anything currently on the controls list is set to redraw, hide tooltip. + if( pToolTipHitLast && pToolTipHitLast->bShowing && commands->Is_List_To_Redraw() ) + { + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + // This hack, used elsewhere in this form, appears to be the standard dodge around GadgetClass::Input's + // tendency to remove any focus the first time it runs for a 'commands' list. + + // ajw - Perhaps I could try doing this every cycle regardless - would avoid stupid non-focused editbox key reactions bug. + if( bHackFocus ) + { + sendedit.Set_Focus(); + sendedit.Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && bLinkInList( commands, pToolTipHit->pGadget ) ) // (Gadget must be in controls list.) + { + if( !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && + !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + else if( pToolTipHit->bIconList && pToolTipHit->bOverDifferentLine() ) + { + pToolTipHit->Unshow(); + pToolTipHit->Show(); + } + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) + { + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by sendedit control. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, false ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case KN_LMOUSE: + break; + + case ( BUTTON_EXPANDCHANNELS | KN_BUTTON ): + if( OnExpandChannelList( chanlist, userlist ) ) + { + // Hide userlist. + if( ExpandUserBtn.Get_Next() == &userlist ) + userlist.Remove(); + // Ensure chanlist is visible. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, chanlist.Y + chanlist.Height ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, chanlist.Y + chanlist.Height + 2 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpUnexpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + else + { + // Show userlist. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_EXPANDUSERS | KN_BUTTON ): + if( OnExpandUserList( chanlist, userlist ) ) + { + // Hide chanlist controls. + if( ExpandChanBtn.Get_Next() == &chanlist ) + chanlist.Remove(); + // Ensure userlist is visible. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpUnexpand ); + } + else + { + // Show chanlist. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_CHANLIST | KN_BUTTON ): + // User clicks on the game list. + //............................................................... + // Handle a double-click + //............................................................... + if( lastclick_timer < 30 && chanlist.Current_Index() == lastclick_idx ) + { + // Doubleclick on channel list. + if( ProcessChannelListSelection( pWO, chatlist, chanlist, lastclick_idx ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + else + { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = chanlist.Current_Index(); + + } + break; + + case ( BUTTON_JOIN | KN_BUTTON ): + // Pressing the join button is exactly like doubleclicking on the selected index in chanlist, except: + // if the first item is selected, ignore, unless we are at the top level + if( pWO->CurrentLevel == WOL_LEVEL_TOP || chanlist.Current_Index() != 0 ) + { + if( ProcessChannelListSelection( pWO, chatlist, chanlist, chanlist.Current_Index() ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + break; + + case ( BUTTON_USERLIST | KN_BUTTON ): + // User clicks on user list. + break; + + case ( BUTTON_CREATE | KN_BUTTON ): + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_INCHATCHANNEL: +// debugprint( "%s\n", TXT_WOL_CANTCREATEINCHANNEL ); + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + case WOL_LEVEL_INLOBBY: + { + pWO->bPump_In_Call_Back = true; + CREATEGAMEINFO CreateGameInfo = WOL_CreateGame_Dialog( pWO ); + pWO->bPump_In_Call_Back = false; + if( CreateGameInfo.bCreateGame ) + { + if( CreateGameChannel( pWO, CreateGameInfo ) ) + { + rc = 1; + process = false; + } + } + break; + } + case WOL_LEVEL_GAMES: + case WOL_LEVEL_GAMESOFTYPE: + case WOL_LEVEL_LOBBIES: + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEHERE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + default: + CreateChatChannel( pWO ); + } + + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + // Because of the way things are set up, this is exactly like selecting the first item in chanlist. + // (Button is disabled when this is not appropriate.) + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): + pWO->dwTimeNextChannelUpdate = ::timeGetTime(); + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( &userlist ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( &userlist, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( &userlist, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( KN_ESC ): +// break; ajw Put back in? + + case ( BUTTON_BACK | KN_BUTTON ): + // Pressing the back button is exactly like doubleclicking on the top item in chanlist, except + // when we're at the top level. + if( pWO->CurrentLevel != WOL_LEVEL_TOP ) + { + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + } + // Note no break; here. Fall through if at top level. + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + ExitChatChannel( pWO ); + pWO->Logout(); + rc = -1; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, true ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + //chatlist.Flag_To_Redraw(); + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_RANKRA | KN_BUTTON ): + pWO->bShowRankRA = true; + pWO->bShowRankUpdated = true; + break; + + case ( BUTTON_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = false; + pWO->bShowRankUpdated = true; + break; + + default: + + break; + } + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + +/* + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); +*/ + + pWO->SaveChat(); + + pWO->ClearListPtrs(); + + return rc; +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + RemapControlType* pColorRemap = ( iColorRemap == PCOLOR_NONE ? NULL : &ColorRemaps[ iColorRemap ] ); + WOL_PrintMessage( ILTarget, szText, pColorRemap ); +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ) +{ + ILTarget.Add_Item( szText, NULL, NULL, ICON_SHAPE, NULL, NULL, pColorRemap ); + if( !ILTarget.bScrollBeingDragged() ) + ILTarget.Show_Last_Item(); +} + +//*********************************************************************************************** +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand channel list button was pressed. + // Returns true if userlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand user list button was pressed. + // Returns true if chanlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +void ResizeChannelList( IconListClass& chanlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_userlist_y + d_userlist_h - 15 - d_chanlist_y ); + else + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h ); +} + +//*********************************************************************************************** +void ResizeUserList( IconListClass& userlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + userlist.Resize( d_userlist_x, d_chanlist_y + 15, d_userlist_w, d_userlist_y + d_userlist_h - ( d_chanlist_y + 15 ) ); + else + userlist.Resize( d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ) +{ + // Enter the channel specified in chanlist at iIndex. + // Called to enter chat channels, "lobbies", and game channels. + + // We've stored the channel pointer in the hidden extra data field. + // ( Be careful about calling RAChatEventSink::DeleteChannelList()! ) + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + return EnterChannel( pWO, chatlist, pChannel, NULL, bGame ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ) +{ + // Called to cause the user to enter a channel (chat, lobby, or game). + // If pChannel is NULL, szChannelName will be used. + + Channel ChannelWhenNameOnly; + if( !pChannel ) + { + if( !szChannelName ) + { +// debugprint( "pChannel and szChannelName null in EnterChannel" ); + pWO->bSelfDestruct = true; + return false; + } + pChannel = &ChannelWhenNameOnly; + strcpy( (char*)pChannel->name, szChannelName ); + } + + if( bGame && pChannel->currentUsers >= pChannel->maxUsers ) // Pre-emptive fullness check. + { + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + + if( bGame ) + { + // It is possible to enter a game channel while currently in a chat channel. (A lobby, presumably.) + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel on join failed" ); + pWO->bSelfDestruct = true; + return false; + } + } + +/* The following doesn't work because the needpw field is not currently being set in wolapi for chat channels. + Instead, we wait for a fail on join, then present this dialog and try again. + if( pChannel->needpw ) + { + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); + if( !pEditDlg->Show() || !*pEditDlg->szEdit ) + return false; + + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + } +*/ + bool bKeepTrying = true; + + // Set password automatically for our lobbies, if trying to join one. + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby != -1 ) + strcpy( (char*)pChannel->key, LOBBYPASSWORD ); + + char szSuccessfulPassword[ WOL_PASSWORD_LEN + 5 ]; + *szSuccessfulPassword = 0; + + HRESULT hRes; + while( bKeepTrying ) + { + hRes = pWO->ChannelJoin( pChannel ); + switch( hRes ) + { + case CHAT_E_BADCHANNELPASSWORD: + { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. +#ifdef ENGLISH + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else +#ifdef GERMAN + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 400, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 500, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#endif +#endif + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) != 0 || !*pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + delete pEditDlg; + bKeepTrying = false; + break; + } + pWO->bPump_In_Call_Back = false; + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + strcpy( szSuccessfulPassword, pEditDlg->szEdit ); + delete pEditDlg; + break; + } + case CHAT_E_TIMEOUT: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_CHANNELDOESNOTEXIST: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_CHANNELGONE ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_BANNED: + WOL_PrintMessage( chatlist, TXT_WOL_YOUREBANNED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case CHAT_E_CHANNELFULL: + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case E_FAIL: + pWO->GenericErrorMessage(); + bKeepTrying = false; + break; + case S_OK: + bKeepTrying = false; + break; + } + } + + if( !bGame ) + { + if( hRes == S_OK ) + return pWO->OnEnteringChatChannel( (char*)pChannel->name, false, iLobby ); + else + return false; + } + else + { + if( hRes == S_OK ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; + // Return later to the lobby of the channel creator - which was saved in the channel itself. + pWO->iLobbyReturnAfterGame = pChannel->reserved & 0x00FFFFFF; + if( pWO->iLobbyReturnAfterGame == 0x00FFFFFF ) + pWO->iLobbyReturnAfterGame = -1; + CREATEGAMEINFO CreateGameInfo; + // Not all of these values are currently used during setup. + CreateGameInfo.bCreateGame = false; + CreateGameInfo.iPlayerMax = pChannel->maxUsers; + CreateGameInfo.bTournament = pChannel->tournament; + if( *szSuccessfulPassword ) + { + CreateGameInfo.bPrivate = true; + strcpy( CreateGameInfo.szPassword, szSuccessfulPassword ); + } + else + { + CreateGameInfo.bPrivate = false; + *CreateGameInfo.szPassword = 0; + } + CreateGameInfo.GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + return pWO->OnEnteringGameChannel( (char*)pChannel->name, false, CreateGameInfo ); + } + else + { + pWO->OnFailedToEnterGameChannel(); + *pWO->szChannelReturnOnGameEnterFail = 0; + return false; + } + } +} + +//*********************************************************************************************** +bool ExitChatChannel( WolapiObject* pWO ) +{ + // Called to cause user to leave current chat or lobby channel. + +// debugprint( "ExitChatChannel\n" ); + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingChatChannel(); + + return true; +} + +//*********************************************************************************************** +//void CreateChatChannel( WolapiObject* pWO, bool bPrivate ) +void CreateChatChannel( WolapiObject* pWO ) +{ + SimpleEditDlgClass* pEditDlg; +/* if( !bPrivate ) + { + pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX ); + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + else +*/ { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pEditDlg = new SimpleEditDlgClass( 350, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX, TXT_WOL_OPTIONALPASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + if( *pEditDlg->szEdit2 ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, pEditDlg->szEdit2 ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + else + { + // Create public channel. + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + pWO->bPump_In_Call_Back = false; + } + + delete pEditDlg; +} + +//*********************************************************************************************** +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ) +{ + char szNewChannelName[ WOL_CHANNAME_LEN_MAX ]; + sprintf( szNewChannelName, "%s's_game", pWO->szMyName ); + + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel in CreateGameChannel() error" ); + pWO->bSelfDestruct = true; + return false; + } + + const char* szKey; + if( *cgi.szPassword ) + szKey = cgi.szPassword; + else + szKey = NULL; + + if( pWO->ChannelCreate( szNewChannelName, szKey, true, cgi.iPlayerMax, cgi.bTournament, pWO->iLobbyLast, cgi.GameKind ) ) + pWO->OnEnteringGameChannel( szNewChannelName, true, cgi ); + else + { + pWO->OnFailedToEnterGameChannel(); +// debugprint( "CreateGameChannel fail" ); + return false; + } + *pWO->szChannelReturnOnGameEnterFail = 0; + + return true; +} + +//*********************************************************************************************** +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ) +{ + // Takes whatever action necessary due to user selecting iIndex from chanlist. + // Returns true if user selected to enter a game channel, else false. + if( iIndex < 0 ) + return false; + +// debugprint( "iIndex %i\n", iIndex ); + + const char* szChannelType = chanlist.Get_Item_ExtraDataString( iIndex ); + if( szChannelType ) + { +//debugprint( "szChannelType %s\n", szChannelType ); + if( strcmp( szChannelType, CHANNELTYPE_OFFICIALCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_OfficialChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_USERCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_UserChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_TOP ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Top(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Games(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMESOFTYPE ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + void* pExtraData = chanlist.Get_Item_ExtraDataPtr( iIndex ); + pWO->EnterLevel_GamesOfType( (WOL_GAMETYPEINFO*)pExtraData ); + } + else if( strcmp( szChannelType, CHANNELTYPE_CHATCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMECHANNEL ) == 0 ) + { + // User attempting to join game channel. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Check if local user is allowed to join GameKind. + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + if( pChannel->type == GAME_TYPE ) + { + // It is a game of our type, at least. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + break; + case CREATEGAMEINFO::CSGAME: + if( !Is_Counterstrike_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDCOUNTERSTRIKE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + case CREATEGAMEINFO::AMGAME: + if( !Is_Aftermath_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDAFTERMATH, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + // Join the game channel. + if( EnterChannel( pWO, chatlist, chanlist, iIndex, true ) ) + { + // Exit the chat dialog, go to game dialog. + return true; + } + } + else + { + // User doubleclicked on a game that is of a different type. + // Offer to take them to a web page regarding the game type. + pWO->DoGameAdvertising( pChannel ); + } + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBIES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Chat channel to lobbies level?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Lobbies(); + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBYCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || + pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Not currently possible. +// debugprint( "Chat or lobby channel to lobby channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the lobby chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_LOADING ) == 0 ) + { + // User clicked on the channel list loading notification - do nothing. + } + else + { +// debugprint( "Item selected in channel list unidentifiable from extradata field\n" ); + pWO->bSelfDestruct = true; + return false; + } + + } + return false; +} + +//*********************************************************************************************** +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ) +{ + const LinkClass* pLink = pListHead; + while( pLink ) + { + if( pLink == pLinkToFind ) + return true; + pLink = pLink->Get_Next(); + } + return false; +} + +#endif diff --git a/CODE/WOL_DNLD.CPP b/CODE/WOL_DNLD.CPP new file mode 100644 index 0000000..4c8a561 --- /dev/null +++ b/CODE/WOL_DNLD.CPP @@ -0,0 +1,263 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// Wol_Dnld.cpp - WW online patch download dialog. +// ajw 10/12/98 + +#include "function.h" +#include "WolapiOb.h" +#include "WolStrng.h" + +//*********************************************************************************************** +bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ) +{ + // This dialog is presented for each file that is to be downloaded during a WOLAPI patch. + + bool bReturn = true; + DWORD dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200*RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin = 34; + int d_txt6_h = 15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + +// int width; +// int height; +// char* info_string = (char*)szTitle; + + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW ); + +// Format_Window_String( info_string, SeenBuff.Get_Height(), width, height ); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + TextButtonClass cancelbtn( BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y ); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h ); +#endif + + GaugeClass progress_meter( BUTTON_PROGRESS, d_progress_x, d_progress_y, d_progress_w, d_progress_h ); + progress_meter.Use_Thumb( 0 ); + + StaticButtonClass StatTitle( 0, szTitle, TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 28, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatStatus( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 49, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatBytes( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 71, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatTime( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 117, d_dialog_w - 2 * d_margin, d_txt6_h ); + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass* commands; // button list + + commands = &cancelbtn; + progress_meter.Add_Tail(*commands); + StatTitle.Add_Tail(*commands); + StatBytes.Add_Tail(*commands); + StatTime.Add_Tail(*commands); + StatStatus.Add_Tail(*commands); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + +// Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, +// GadgetClass::Get_Color_Scheme(), TBLACK, +// TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + if (process){ + input = cancelbtn.Input(); + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + pDownload->Abort(); + process = false; + bReturn = false; + break; + } + } + + if( ::timeGetTime() > dwTimeNextPump ) + { + pDownload->PumpMessages(); + if( pDownloadSink->bFlagEnd ) + { + pDownloadSink->bFlagEnd = false; + process = false; + break; + } + if( pDownloadSink->bFlagError ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADERROR ); + pDownloadSink->bFlagError = false; + process = false; + bReturn = false; + break; + } + if( pDownloadSink->bFlagProgressUpdate ) + { + pDownloadSink->bFlagProgressUpdate = false; + progress_meter.Set_Value( ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + char szText[200]; + sprintf( szText, TXT_WOL_DOWNLOADBYTES, pDownloadSink->iBytesRead, pDownloadSink->iTotalSize, + ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + StatBytes.Set_Text( szText ); + sprintf( szText, TXT_WOL_DOWNLOADTIME, pDownloadSink->iTimeLeft / 60, pDownloadSink->iTimeLeft % 60 ); + StatTime.Set_Text( szText ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagStatusUpdate ) + { + pDownloadSink->bFlagStatusUpdate = false; + switch( pDownloadSink->iStatus ) + { + case DOWNLOADSTATUS_CONNECTING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADCONNECTING ); + break; + + case DOWNLOADSTATUS_FINDINGFILE: + StatStatus.Set_Text( TXT_WOL_DOWNLOADLOCATING ); + break; + + case DOWNLOADSTATUS_DOWNLOADING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADDOWNLOADING ); + break; + + default: +// debugprint( "Unknown status update!\n" ); + break; + } + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagQueryResume ) + { + if( pDownloadSink->bResumed ) + { + char szTitleNew[200]; + sprintf( szTitleNew, TXT_WOL_DOWNLOADRESUMED, szTitle ); + StatTitle.Set_Text( szTitleNew ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + } + + dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Invoke game callback + Call_Back(); + + } while ( process ); + + return bReturn; +} + +#endif diff --git a/CODE/WOL_GSUP.CPP b/CODE/WOL_GSUP.CPP new file mode 100644 index 0000000..d1c5b04 --- /dev/null +++ b/CODE/WOL_GSUP.CPP @@ -0,0 +1,3500 @@ +/* +** 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 . +*/ + + +#ifdef WOLAPI_INTEGRATION // Now implies also WINSOCK_IPX, WIN32, and FIXIT_CSII must be true + +#include "wol_gsup.h" +#include "function.h" +#include "IconList.h" +#include +#include "WolStrng.h" +#include "wsproto.h" +#include "BigCheck.h" +#include "ToolTip.h" + +extern char const* EngMisStr[]; + +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); + +bool Force_Scenario_Available( const char* szName ); + +int ScenarioIndex_From_Filename( const char* szScenarioFilename ); + +bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +#include "WolDebug.h" + +#define PARAMREFRESHWAIT 2000 + +#define PING_AND_DISPLAY_WAIT 5000 + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ); +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ); +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ); + +extern unsigned long PlanetWestwoodStartTime; //Time that game was started + +extern bool cancel_current_msgbox; +extern bool disable_current_msgbox; + +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ); + +//*********************************************************************************************** +/* Game startup logic: +When a guest joins the channel, the host sends all current setup info. +When a guest changes house, he tells the host, who informs everyone. A house change can't be denied. +When a guest changes color, he asks the host for the new color, and assumes the request will go through. If it doesn't, the +host sends the guest a messages setting him back to the old color, otherwise, everyone gets the new color for the guest. +When the host changes a game param, the change will be noticed within PARAMREFRESHWAIT milliseconds and then transmitted to +all guests. +All player specific information is stored within the players list (IconListClass), and nowhere else. Though this storage +method got a bit inelegant, it gets the job done. +All "informs" (messages sent from host to guest) except color changes (WOL_GAMEOPT_INFCOLOR) and player "accept" status +changes (WOL_GAMEOPT_INFACCEPT) are assigned a ID, which +increments on each send. Color informs are: a) not a condition for removing a guest's "accept" status, and b) sent out to +individual guests in certain cases, and would thus complicate the ID tracking scheme. +When a guest "accepts" the game setup (with a WOL_GAMEOPT_REQACCEPT), the latest received ID is included in the message. +If the host receives a WOL_GAMEOPT_REQACCEPT with an out-of-date ID, the accept is ignored. Though the guest sets himself to +"accepted" when he sends the WOL_GAMEOPT_REQACCEPT, if it gets ignored by the host it is because a new inform is already on its +way, which will reset the guest's own status to "not accepted" when it arrives. +Host clears all accepteds when params are changed, player joins or leaves, or house change message arrives. Though a +WOL_GAMEOPT_INFACCEPT is sent out when a guest accepts, there is no corresponding "unaccept all" message sent out at this point, +as all guests will naturally clear their recorded accept status for everyone when the change arrives there. +Guest clears own accepted when an inform from the server other than color change arrives, player joins or leaves, or own +house is directly changed. +When all players are "accepted", and the host says "start", a WOL_GAMEOPT_INFSTART is sent to all guests and the host goes into +"waiting to start" mode. If a house change or other arrives in this phase, host clears waiting mode and (naturally) marks guests +unaccepted and sends out the house change to the guests. (The same applies to player joins/leaves.) +While in this mode, the host user is locked out of making any changes to game params. +If a change does come in during this phase, a WOL_GAMEOPT_INFCANCELSTART is sent to the guests, and the waiting mode cancelled. +When a guest receives WOL_GAMEOPT_INFSTART: If he does not see himself as accepted, it is because a change has just been sent +to the host, or some other event occurred that the host will learn about, so the guest can simply ignore the WOL_GAMEOPT_INFSTART. +If the guest does see himself as accepted when WOL_GAMEOPT_INFSTART arrives, he responds with WOL_GAMEOPT_REQSTART and goes into +"waiting to start" mode. When in this mode, the user is locked out of making any changes. +Note that the host is not really asking for any sort of "confirmation that it's ok" to start from the guest here. (Hence no +"cancel" response is possible.) It's really just a way of telling the guests not to make any more changes to the setup; it's +like we're making sure we've "flushed the queue" of game changes. +If a guest receives a WOL_GAMEOPT_INFCANCELSTART, he cancels out of "waiting to start" mode. Note that while in the waiting +mode, normal processing of messages is allowed to take place - the user is simply blocked from doing anything. +Presumably (hopefully) this phase will last less than a second. +When the host receives a WOL_GAMEOPT_REQSTART from everyone, he tells everyone to start with a WOL_GAMEOPT_INFGO. +Because there is a chance of color changes being out of sync, all player colors are sent to all guests at this point. +There is no chance of other data being out of sync. +*/ + +//-------------------------------------------------------------------------- +// Pieced together from Net_New_Dialog() and Net_Join_Dialog(). +//-------------------------------------------------------------------------- +//*********************************************************************************************** +WOL_GameSetupDialog::WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ) : pWO( pWO ), bHost( bHost ), + HousePrevious( HOUSE_NONE ), + pILPlayers( NULL ), + pILScens( NULL ), + pILDisc( NULL ), + pEditSend( NULL ), + pGaugeCount( NULL ), + pGaugeLevel( NULL ), + pGaugeCredits( NULL ), + pGaugeAIPlayers( NULL ), + pCheckListOptions( NULL ), +// pTextBtnOk( NULL ), + pTextBtnCancel( NULL ), + pTextBtnAcceptStart( NULL ), + pTextBtnAction( NULL ), + pStaticDescrip( NULL ), + pStaticUnit( NULL ), + pStaticLevel( NULL ), + pStaticCredits( NULL ), + pStaticAIPlayers( NULL ), + pDropListHouse( NULL ), + pCheckAftermathUnits( NULL ), + nHostLastParamID( 0 ), + nGuestLastParamID( 0 ), + bWaitingToStart( false ), + bParamsReceived( false ), + bHostSayGo( false ), + bExitForGameTrigger( false ), + pToolTipHead( NULL ), + pToolTipHitLast( NULL ), + pTTipAcceptStart( NULL ), + pTTipCancel( NULL ), + pTTipAction( NULL ), + ScenKindCurrent( SCENARIO_UNINITIALIZED ), + pShpBtnScenarioRA( NULL ), + pShpBtnScenarioCS( NULL ), + pShpBtnScenarioAM( NULL ), + pShpBtnScenarioUser( NULL ), + bLeaveDueToRulesMismatchTrigger( false ), + bHostWaitingForGoTrigger( false ) +{ + *szSendBuffer = 0; + *szHouseBuffer = 0; + memset( &GParamsLastSent, 0, sizeof( GAMEPARAMS ) ); + *szNameOfHostWhoJustBailedOnUs = 0; + *szTriggerGameStartInfo = 0; +} + +//*********************************************************************************************** +WOL_GameSetupDialog::~WOL_GameSetupDialog() +{ + delete pILPlayers; + delete pILScens; + delete pILDisc; + delete pEditSend; + delete pGaugeCount; + delete pGaugeLevel; + delete pGaugeCredits; + delete pGaugeAIPlayers; + delete pCheckListOptions; +// delete pTextBtnOk; + delete pTextBtnCancel; + delete pTextBtnAcceptStart; + delete pTextBtnAction; +// delete pStaticDescrip; + delete pStaticUnit; + delete pStaticLevel; + delete pStaticCredits; + delete pStaticAIPlayers; + delete pDropListHouse; + delete pCheckAftermathUnits; + delete pTTipAcceptStart; + delete pTTipCancel; + delete pTTipAction; + delete pShpBtnScenarioRA; + delete pShpBtnScenarioCS; + delete pShpBtnScenarioAM; + delete pShpBtnScenarioUser; +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Run() +{ + Initialize(); + return Show(); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::Initialize() +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + d_dialog_w = 640; // dialog width + d_dialog_h = 400; // dialog height + d_dialog_x = 0; + d_dialog_y = 0; + d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_text_h = 12; + d_margin1 = 34; // large margin + + d_color_w = 10 *RESFACTOR; + d_color_h = 9 *RESFACTOR; + d_color_x = 294; //54; //d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + d_color_y = 89; //142; //d_house_y; + + d_house_w = 60 *RESFACTOR; + d_house_h = (8 * 5 *RESFACTOR); + d_house_x = 466; //65; //d_color_x; //d_dialog_cx - (d_house_w / 2); + d_house_y = d_color_y; // + 36; //d_dialog_y + ( 7 * RESFACTOR ) + d_txt6_h + (2*RESFACTOR); + + if( bHost ) + d_disc_w = 365; //d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + else + d_disc_w = d_dialog_w - ( d_margin1 * 2 ); + d_disc_x = d_dialog_x + d_margin1; + d_disc_y = 205; //d_dialog_y + d_dialog_h - ( 65 + d_disc_h ); + d_disc_h = 337 - d_disc_y; + + d_playerlist_w = 124*RESFACTOR; + d_playerlist_h = d_text_h * 4 + 4; + d_playerlist_x = d_dialog_x + d_margin1; + d_playerlist_y = 75; //d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR + 18 * RESFACTOR; + + int d_tab_h = 19; + + d_scenariolist_w = 200; +// d_scenariolist_h = (4 * d_txt6_h) + 3*RESFACTOR; // 4 rows high + d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + d_scenariolist_y = d_disc_y + d_tab_h; + d_scenariolist_h = d_disc_y + d_disc_h - d_scenariolist_y; + + d_gamekind_w = 182; + d_gamekind_h = 30; + d_gamekind_x = 52; // 370; + d_gamekind_y = 153; // d_playerlist_y + d_text_h + 3; // + d_playerlist_h - d_text_h; + + d_count_w = 25*RESFACTOR; + d_count_h = d_txt6_h; + d_count_x = 310; + d_count_y = 138; //d_playerlist_y + d_playerlist_h + d_margin2 + 9*RESFACTOR; + + d_level_w = 25*RESFACTOR; + d_level_h = d_txt6_h; + d_level_x = d_count_x; + d_level_y = d_count_y + d_count_h; + + d_credits_w = 25*RESFACTOR; + d_credits_h = d_txt6_h; + d_credits_x = d_count_x; + d_credits_y = d_level_y + d_level_h; + + d_aiplayers_w = 25*RESFACTOR; + d_aiplayers_h = d_txt6_h; + d_aiplayers_x = d_count_x; + d_aiplayers_y = d_credits_y + d_credits_h; + +#ifdef GERMAN + d_options_w = 186; +#else + d_options_w = 180; +#endif + d_options_h = ((6 * 6) + 4)*RESFACTOR + 1; + d_options_x = d_dialog_x + d_dialog_w - d_options_w - d_margin1; + d_options_y = 127 - d_txt6_h + 2; + + d_send_w = d_dialog_w - ( d_margin1 * 2 ); + d_send_h = 9*RESFACTOR; + d_send_x = d_dialog_x + d_margin1; + d_send_y = d_disc_y + d_disc_h + 5; + +// d_ok_w = 50*RESFACTOR; +// d_ok_h = 9*RESFACTOR; +// d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); +// d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + d_cancel_w = 100; + d_cancel_h = 9*RESFACTOR; + d_cancel_x = d_dialog_x + 100; //d_dialog_cx - (d_cancel_w / 2); + d_cancel_y = 365; //d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + d_accept_w = 100; + d_accept_h = 9 *RESFACTOR; + d_accept_x = d_dialog_x + 210; + d_accept_y = d_cancel_y; //d_dialog_y + d_dialog_h - 17*RESFACTOR; + + d_action_w = 100; + d_action_h = 9 *RESFACTOR; + d_action_x = d_dialog_x + 500; + d_action_y = d_cancel_y; + +/* int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; +*/ + d_amunits_w = 160; + d_amunits_h = 18; + d_amunits_x = d_dialog_x + (d_dialog_w / 6) - (d_amunits_w / 2); + d_amunits_y = 365; + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + + pILPlayers = new IconListClass( BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); +// ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + pILScens = new IconListClass( BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + pILDisc = new IconListClass( BUTTON_DISCLIST, d_disc_x, d_disc_y, d_disc_w, d_disc_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 300 ); + + pEditSend = new EditClass( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + +// TextButtonClass rejectbtn( BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y ); + pGaugeCount = new GaugeClass( BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h ); + pGaugeLevel = new GaugeClass( BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h ); + pGaugeCredits = new GaugeClass( BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h ); + pGaugeAIPlayers = new GaugeClass( BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h ); + pCheckListOptions = new CheckListClass( BUTTON_PARAMS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP") ); +// pTextBtnOk = new TextButtonClass( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR ); +// TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + pTextBtnCancel = new TextButtonClass( BUTTON_CANCEL, TXT_WOL_CANCELGAME, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + pTTipCancel = new ToolTipClass( pTextBtnCancel, TXT_WOL_TTIP_CANCELGAME, d_cancel_x + d_cancel_w/2, d_cancel_y - 6 ); + + if( bHost ) + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_STARTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_START, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + else + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_ACCEPTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_ACCEPT, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + + pTextBtnAction = new TextButtonClass( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + pTTipAction = new ToolTipClass( pTextBtnAction, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + + pToolTip = pToolTip->next; + pToolTip->next = pTTipCancel; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAcceptStart; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAction; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + if( bHost ) + pTextBtnAcceptStart->Disable(); + + // pStaticDescrip is no longer used - can't get the bloody thing to clip text. You'd think a StaticButton control would. +// pStaticDescrip = new StaticButtonClass( 0, "", TPF_TYPE, d_gamekind_x, d_gamekind_y, d_gamekind_w, d_gamekind_h ); + pStaticUnit = new StaticButtonClass( 0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y ); + pStaticLevel = new StaticButtonClass( 0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y ); + pStaticCredits = new StaticButtonClass( 0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y ); + pStaticAIPlayers = new StaticButtonClass( 0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y ); + + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + pDropListHouse = new DropListClass( BUTTON_HOUSE, szHouseBuffer, sizeof( szHouseBuffer ), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP") ); + + // ajw - This checkbox is not used. Could be turned on, though. + pCheckAftermathUnits = new BigCheckBoxClass( BUTTON_AFTERMATHUNITS, d_amunits_x, d_amunits_y, d_amunits_w, d_amunits_h, + "Aftermath units enabled", TPF_TEXT, false ); + + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + bAftermathUnits = true; + int current_drive = CCFileClass::Get_CD_Drive(); + int cd_index = Get_CD_Index(current_drive, 1*60); + if( cd_index != 3 && cd_index != 5 ) + { + WOL_PrintMessage( *pILDisc, TXT_WOL_AMDISCNEEDED, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + else + { + bAftermathUnits = false; + pCheckAftermathUnits->Disable(); + } + + bSlowUnitBuildRate = true; + +#define TABSPACING 38 + pShpBtnScenarioRA = new ShapeButtonClass( BUTTON_SCENARIO_RA, MFCD::Retrieve( "tabra.shp" ), d_scenariolist_x, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioCS = new ShapeButtonClass( BUTTON_SCENARIO_CS, MFCD::Retrieve( "tabcs.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioAM = new ShapeButtonClass( BUTTON_SCENARIO_AM, MFCD::Retrieve( "tabam.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + + int iScenarioUserTabPos; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME || pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + // Place user tab in the third tab position. (It may still not be present.) + iScenarioUserTabPos = d_scenariolist_x + TABSPACING * 2; + else + iScenarioUserTabPos = d_scenariolist_x + TABSPACING; + + pShpBtnScenarioUser = new ShapeButtonClass( BUTTON_SCENARIO_USER, MFCD::Retrieve( "tabus.shp" ), iScenarioUserTabPos, d_scenariolist_y - d_tab_h ); + + // Change draw behavior of tab buttons. + pShpBtnScenarioRA->ReflectButtonState = true; + pShpBtnScenarioCS->ReflectButtonState = true; + pShpBtnScenarioAM->ReflectButtonState = true; + pShpBtnScenarioUser->ReflectButtonState = true; + + if( !bHost ) + { + pILScens->Disable(); + pGaugeCount->Disable(); + pGaugeLevel->Disable(); + pGaugeCredits->Disable(); + pGaugeAIPlayers->Disable(); + pCheckListOptions->Disable(); + pCheckAftermathUnits->Disable(); + } + + pWO->LinkToGameDlg( pILDisc, pILPlayers ); + pWO->pGSupDlg = this; + + dwTimeNextParamRefresh = ::timeGetTime(); +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Show() +{ + // Returns: -1 == return to chat dialog. + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + bool bHackFocus = true; + + display = REDRAW_ALL; // redraw level + bProcess = true; // process while true + KeyNumType input; + + DWORD timeWaitingToStartTimeout; + + char szScenarioNameDisplay[ 300 ]; + + bool bInformParamChange; + + long ok_timer = 0; // for timing OK button + int i; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + + bool bRetractHouseDropDown = false; + + if( !pWO->OnEnteringGameSetup() ) // Gets a userlist setup, among other things. + strcpy( szNameOfHostWhoJustBailedOnUs, TXT_WOL_THEGAMEHOST ); // Will cause immediate exit. + + // If I'm not already listed with a color, give myself a color. + // (I may have already received my assigned color from the game host.) + int iItem = pILPlayers->Find( pWO->szMyName ); // (I must be in the list I just got.) + _ASSERTE( iItem != -1 ); + RemapControlType* pColorRemap = pILPlayers->Get_Item_Color( iItem ); + PlayerColorType Color = PlayerColorTypeOf( pColorRemap ); + +// debugprint( "Starting up, I see myself as color %i\n", Color ); + if( Color == PCOLOR_NONE ) + SetPlayerColor( pWO->szMyName, ColorNextAvailable() ); // Unless I'm host, this will be changed quite immediately, + // but nice to have it be a valid value until then. + + DWORD dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + DWORD dwTimeNextPingDisplay = dwTimeNextPlayerPing + 1500; // Stagger ping and ping display periods. + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + BindControls( true ); + + pILPlayers->Set_Tabs(tabs); + + // If we have not already received a param update from a host, set default param values. + if( !bParamsReceived ) + { + if( !bHost ) + // Accept button disabled until first params arrive. + pTextBtnAcceptStart->Disable(); + + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; // Ugh. Use of "Special" global. + if( bHost ) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + //first_time = 0; + } + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + if( bHost ) + { + //------------------------------------------------------------------------ + // Set up array of lists of available scenarios. + //------------------------------------------------------------------------ + for( i = 0; i < Session.Scenarios.Count(); i++ ) + { + // Reworking of the loop previously used for language translation. (What a hack I have inherited...) + MultiMission* pMMission = Session.Scenarios[ i ]; + const char* szScenarioNameShow = pMMission->Description(); + #if defined( GERMAN ) || defined( FRENCH ) + for( int j = 0; EngMisStr[j] != NULL; j++ ) + { + if( !strcmp( szScenarioNameShow, EngMisStr[j] ) ) + { + // Found description in translation array that matches mission. + szScenarioNameShow = EngMisStr[ j + 1 ]; + break; + } + } + // (If no match found, defaults to English description.) + #endif + // Place scenario name in a specific scenario list. + if( pMMission->Get_Official() ) + { + if( Is_Mission_Counterstrike( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as CS\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_CS ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_CS ].Add( (void*)i ); + } + else if( Is_Mission_Aftermath( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as AM\n", szScenarioNameShow ); + // If this is not an Aftermath game channel, we must filter out any AM maps that have + // special AM units on them. + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + !bSpecialAftermathScenario( pMMission->Description() ) ) + { + ar_szScenarios[ SCENARIO_AM ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_AM ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as RA\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_RA ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_RA ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as User\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_USER ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_USER ].Add( (void*)i ); + } + } + + // Set default scenario list viewing mode and prepare tab buttons. + /* switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + ScenarioDisplayMode( SCENARIO_RA ); + break; + case CREATEGAMEINFO::CSGAME: + ScenarioDisplayMode( SCENARIO_CS ); + break; + case CREATEGAMEINFO::AMGAME: + ScenarioDisplayMode( SCENARIO_AM ); + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + Fatal( "Illegal GameInfoCurrent value." ); + } + */ + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + ScenarioDisplayMode( SCENARIO_RA ); // Always start on RedAlert tab. Next line depends on selected item in + // list matching selected scenario. +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + } + + Seed = rand(); + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + pCheckListOptions->Set_Tabs(optiontabs); + pCheckListOptions->Set_Read_Only(0); + + pCheckListOptions->Add_Item(Text_String(TXT_BASES)); + pCheckListOptions->Add_Item(Text_String(TXT_ORE_SPREADS)); + pCheckListOptions->Add_Item(Text_String(TXT_CRATES)); + pCheckListOptions->Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + pCheckListOptions->Add_Item(Text_String(TXT_SHADOW_REGROWS)); + pCheckListOptions->Add_Item(TXT_WOL_SLOWUNITBUILD); + + SetSpecialControlStates(); + + //........................................................................ + // House buttons + //........................................................................ + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + pDropListHouse->Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + pDropListHouse->Set_Selected_Index(Session.House - HOUSE_USSR); + pDropListHouse->Set_Read_Only (true); + + + bInformParamChange = false; + +// PlayingAgainstVersion = VerNum.Version_Number(); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + +// Load_Title_Page(true); +// CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // +// pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); +// pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + Sound_Effect( WOLSOUND_ENTERGAME ); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (bProcess) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Check for change of house. Occurs on first loop and when user changes house. + if( HousePrevious != Session.House ) + { + if( bHost ) + { + // Host changed house. + // Do processing as if we'd received a message from a guest. + + // Set house in our own list. + SetPlayerHouse( pWO->szMyName, Session.House ); + // Tell guests. + nHostLastParamID++; + InformAboutPlayerHouse( pWO->szMyName, Session.House, NULL ); + HousePrevious = Session.House; + ClearAllAccepts(); + } + else + { + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else we have not received the user list yet and don't know who the host is. + // We'll keep trying this until we get a host - HousePrevious keeps us triggering until then. + { +// debugprint( "Session.House changed.\n" ); + // Tell host we changed our house. + char szSend[ 20 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQHOUSE, Session.House ); + pWO->SendGameOpt( szSend, pUserHost ); + // Set house in our own list. This is fine because we know that the change must be affirmed by the host. + SetPlayerHouse( pWO->szMyName, Session.House ); + HousePrevious = Session.House; + } + } + } + + // Regularly ping other players to assess latencies. + // Display of ping results is on a parallel timer, slightly behind the ping so that we'll be likely to have + // a fresh average to show. Not too big a deal if OnPings haven't arrived by then (though they should have). + if( ::timeGetTime() > dwTimeNextPlayerPing ) + { + pWO->RequestPlayerPings(); + dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + if( ::timeGetTime() > dwTimeNextPingDisplay ) + { + pWO->ListChannelUsers(); + dwTimeNextPingDisplay = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + + if( pWO->bShowRankUpdated ) + { + pWO->bShowRankUpdated = false; + pWO->ListChannelUsers(); + } + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + // Special post-callback processing... + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of the channel. + pWO->OnExitingGameChannel(); + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + // Leave the bGotKickedTrigger flag so we can react to it upon reentering the chat dialog. + //pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + if( bHostSayGo ) + { +// debugprint( "bHostSayGo trigger\n" ); + // We are the host, now ready to tell everyone to GO and start our game. + HostSaysGo(); +/* Part of old method of game start. + bProcess = false; + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; +// debugprint( "Host about to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; +*/ + } + else if( *szNameOfHostWhoJustBailedOnUs ) // Host left channel - cancel setup. + { +// debugprint( "Guest about to exit game setup dialog because host bailed on us.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_HOSTLEFT; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddHostLeftMessageToSavedChat( szNameOfHostWhoJustBailedOnUs ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( bLeaveDueToRulesMismatchTrigger ) + { +// debugprint( "Guest about to exit game setup dialog because of rules.ini mismatch.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_RULESMISMATCH; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddMessageToSavedChat( TXT_WOL_RULESMISMATCH ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( *szTriggerGameStartInfo ) + { +// debugprint( "About to trigger game start.\n" ); + TriggerGameStart( szTriggerGameStartInfo ); + } + else if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; // As if the user logged himself out. + } + + if( bExitForGameTrigger ) + { + // We are now exiting to go into a game. + bProcess = false; + if( bHost ) + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; + else + ResultReturn = RESULT_WOLGSUP_STARTGAME; +// debugprint( "About to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; + } + + if( bHost && bWaitingToStart && !bExitForGameTrigger && timeGetTime() > timeWaitingToStartTimeout ) + { + ClearAllAccepts(); // Results in all required steps to cancel game start. + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTTIMEOUT, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + + // Regularly send game param changes if I'm the host. + if( bHost && !bHostSayGo && !bExitForGameTrigger && ::timeGetTime() > dwTimeNextParamRefresh ) + { + if( bParamsUnfresh() ) + { + nHostLastParamID++; + SendParams(); + ClearAllAccepts(); + } + dwTimeNextParamRefresh = ::timeGetTime() + PARAMREFRESHWAIT; + } + + if( !bProcess ) // Avoid redrawing if we're about to bail. + break; + + // + // If we have just received input focus again after running in the background then + // we need to redraw. + // + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + + if( bRetractHouseDropDown ) //|| pCheckListOptions->Is_To_Redraw() ) + { + bRetractHouseDropDown = false; + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + } +// if( pILDisc->Is_To_Redraw() ) +// { +// pDropListHouse->Flag_To_Redraw(); +// } + + if( display ) + { + Hide_Mouse(); + + /* + ** Collapse the country list if we are going to redraw the game list + */ +// if (pILScens->Is_To_Redraw() && pDropListHouse->IsDropped) { +// pDropListHouse->Collapse(); +// if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; +// } + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( pDropListHouse->IsDropped ) + pDropListHouse->Collapse(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + if( bHost ) + Fancy_Text_Print( TXT_SCENARIOS, d_scenariolist_x + d_scenariolist_w, d_scenariolist_y - 12, scheme, TBLACK, TPF_TYPE | TPF_RIGHT ); +// else +// Fancy_Text_Print( TXT_SCENARIO_COLON, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_SIDE_COLON, +// d_house_x + (d_house_w / 2), + d_house_x + ( ( d_house_w + 16 ) / 2 ), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COLOR_COLON, + d_color_x + d_color_w * 4, + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + const char* szGameKind; + const char* pDIB; + switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + szGameKind = TXT_WOL_CG_RAGAME; + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + break; + case CREATEGAMEINFO::CSGAME: + szGameKind = TXT_WOL_CG_CSGAME; + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + break; + case CREATEGAMEINFO::AMGAME: + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + //Fatal( "Illegal GameInfoCurrent value." ); + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = NULL; + pWO->bSelfDestruct = true; + break; + } + int iGameInfoSpacingY = 14; + int iGameInfoSecondColumnX = 0; //170; + // Game kind. + Fancy_Text_Print( szGameKind, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + // Game kind icon. + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + // "Tournament." + if( pWO->GameInfoCurrent.bTournament ) + { + Fancy_Text_Print( TXT_WOL_CG_TOURNAMENT, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_TOURNAMENT ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + } + // "Password: ..." + if( pWO->GameInfoCurrent.bPrivate ) + { + char szPrivatePassword[ 100 ]; + sprintf( szPrivatePassword, TXT_WOL_PRIVATEPASSWORD, pWO->GameInfoCurrent.szPassword ); + Fancy_Text_Print( szPrivatePassword, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 2, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_PRIVATE ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 2 - 2, 100, WINDOW_MAIN ); + } + // "Scenario:" - scenario name is drawn separately. + //Fancy_Text_Print( TXT_SCENARIO_COLON, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY, scheme, TBLACK, TPF_TYPE ); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS ) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { +// Draw_Box(d_disc_x, d_disc_y, d_disc_w, d_disc_h, BOXSTYLE_BOX, true); +// Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); +// Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) + { + char txt[80]; + + char* szScenarioDesc; + bool bOfficial; + char* szScenarioFileName; + if( !bHost ) + { + szScenarioDesc = Session.Options.ScenarioDescription; + bOfficial = Session.ScenarioIsOfficial; + szScenarioFileName = Session.ScenarioFileName; + } + else + { + szScenarioDesc = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description(); + bOfficial = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + szScenarioFileName = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename(); + } + + if( *szScenarioDesc ) + { + // Language translation. + for (int ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp( szScenarioDesc, EngMisStr[ii]) ) { + #if defined(GERMAN) || defined(FRENCH) + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), EngMisStr[ii+1]); + sprintf(txt, "%s", EngMisStr[ii+1]); + #else + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Session.Options.ScenarioDescription); + sprintf(txt, "%s", szScenarioDesc); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s", szScenarioDesc); + } +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + + // Show icon for gamekind of scenario. + const char* pDIB; + + if( bOfficial ) + { + if( Is_Mission_Counterstrike( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + else if( Is_Mission_Aftermath( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + else + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + } + else + pDIB = pWO->DibIconInfos[ DIBICON_USER ].pDIB; + + DrawScenarioDescripIcon( pDIB ); + } + else + { + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Text_String(TXT_NOT_FOUND)); + sprintf(txt, "%s", TXT_WOL_SCENARIONAMEWAIT ); +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + } + + // Print scenario name. + Conquer_Clip_Text_Print( szScenarioNameDisplay, d_gamekind_x, d_gamekind_y, scheme, TBLACK, TPF_TYPE, d_gamekind_w ); +// pStaticDescrip->Draw_Me(); + + sprintf(txt,"%d",Session.Options.UnitCount); + pStaticUnit->Set_Text(txt); + pStaticUnit->Draw_Me(); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + pStaticLevel->Set_Text(txt); + pStaticLevel->Draw_Me(); + + sprintf(txt,"%d",Session.Options.Credits); + pStaticCredits->Set_Text(txt); + pStaticCredits->Draw_Me(); + + sprintf(txt,"%d",Session.Options.AIPlayers); + pStaticAIPlayers->Set_Text(txt); + pStaticAIPlayers->Draw_Me(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + if( bHackFocus ) + { + pEditSend->Set_Focus(); + pEditSend->Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + // Yummy special hack. Luv' that UI system. + if( input == 2049 ) // Left mouse released, not on control that captured it. + { + // Redraw the tabs in case they were what were pressed down on. + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); + } + + //..................................................................... + // Process input + //..................................................................... + switch( input ) + { + case KN_LMOUSE: + if( !bWaitingToStart ) + { + // Check for mouse down on a control when player is not host. + if( !bHost ) + { + if ( ( + Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h + ) || ( + Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h + ) || ( + Get_Mouse_X() >= d_scenariolist_x && + Get_Mouse_X() <= d_scenariolist_x + d_scenariolist_w && + Get_Mouse_Y() >= d_scenariolist_y && + Get_Mouse_Y() <= d_scenariolist_y + d_scenariolist_h + ) ) + { + //Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + WOL_PrintMessage( *pILDisc, Text_String( TXT_ONLY_HOST_CAN_MODIFY ), WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) + { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + + // Ensure that no one is using this color (to our knowledge). + if( pILPlayers->FindColor( &ColorRemaps[ Session.PrefColor == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Session.PrefColor ] ) == -1 ) + { + // Show me as the new color. +// debugprint( "Color box pressed - " ); + SetPlayerColor( pWO->szMyName, Session.PrefColor ); + if( bHost ) + { + // Tell all guests about the color change. + InformAboutPlayerColor( pWO->szMyName, Session.PrefColor, NULL ); + } + else + { + RequestPlayerColor( Session.PrefColor ); + } + } + + } + } + break; + + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { +// debugprint( "Logging out from gsup.\n" ); + ExitGameChannel(); + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): // Always disabled. + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( pILPlayers ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( pILPlayers, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( pILPlayers, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HOUSE | KN_BUTTON ): + Session.House = (HousesType)( pDropListHouse->Current_Index() + HOUSE_USSR ); +/* + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( pDropListHouse->Current_Item(), "Russia" ) == 0 ) + Session.House = HOUSE_USSR; + else + { + Session.House = HouseTypeClass::From_Name( pDropListHouse->Current_Item() ); // Fails on "Russia". (Thinks "USSR".) + if( Session.House == HOUSE_NONE ) + { +// debugprint( "Couldn't find house from selected '%s'.\n", pDropListHouse->Current_Item() ); + } + } +*/ + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + else + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; // Droplist already got contracted. + + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_AFTERMATHUNITS | KN_BUTTON ): + bAftermathUnits = pCheckAftermathUnits->IsOn; + break; + + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, false ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, true ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + //.................................................................. + // New Scenario selected. + //.................................................................. + case (BUTTON_SCENARIOLIST | KN_BUTTON): + { + if( pILScens->Count() ) + { + int iSelectedScenIndex = (int)pILScens->Get_Item_ExtraDataPtr( pILScens->Current_Index() ); + if( iSelectedScenIndex != Session.Options.ScenarioIndex ) + { + Session.Options.ScenarioIndex = iSelectedScenIndex; + bInformParamChange = true; + if( !pILScens->SetSelectType( 1 ) ) // Hack to deal with ListClass "highlighting nothing" problem. + // SelectType was 0 before call. + pILScens->Flag_To_Redraw(); +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + //if (display < REDRAW_PARMS) display = REDRAW_PARMS; + display = REDRAW_ALL; + Sound_Effect(VOC_OPTIONS_CHANGED); + } + } + break; + } + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = pGaugeCount->Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = pGaugeLevel->Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = pGaugeCredits->Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = pGaugeAIPlayers->Get_Value(); +// if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + if (Session.Options.AIPlayers + pWO->GameInfoCurrent.iPlayerMax > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax; + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + } + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case ( BUTTON_PARAMS | KN_BUTTON ): + bRetractHouseDropDown = true; + if (Special.IsCaptureTheFlag != pCheckListOptions->Is_Checked(3) && !Special.IsCaptureTheFlag) { + pCheckListOptions->Check_Item(0, true); + } + if (Session.Options.Bases != pCheckListOptions->Is_Checked(0)) { + Session.Options.Bases = pCheckListOptions->Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + pCheckListOptions->Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + pGaugeCount->Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = pCheckListOptions->Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = pCheckListOptions->Is_Checked(2); + Special.IsCaptureTheFlag = pCheckListOptions->Is_Checked(3); // Ugh. Use of "Special" global. + Special.IsShadowGrow = pCheckListOptions->Is_Checked(4); // Ugh. Use of "Special" global. + + bSlowUnitBuildRate = pCheckListOptions->Is_Checked(5); + + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_ACCEPTSTART | KN_BUTTON ): // 'Accept' or 'Start Game' button. + if( !bHost ) + { + // Guest wishes to accept game params. + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else it's too early to even be thinking about accepting, anyway. + { + // Set ourself as accepted. We want to do this immediately so the user has feedback for pressing button. + // Besides, the host is guaranteed to allow this and set us to "accepted", unless it is simultaneously + // sending us a new update we haven't received yet. If this is the case, we will get that update in a + // second and will set ourselves to unaccepted, which will match the unaccepted state on the server. + // Upshot - We can ignore messages from the server telling us that we, ourself, accepted. + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Disable(); + if( SetPlayerAccepted( pWO->szMyName, true ) ) // Else we didn't find ourself in list! + { +// debugprint( "Sending accept.\n" ); + // Tell host we accept. + char szSend[ 20 ]; + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQACCEPT, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + } + } + } + else + { + // Host says start the game. + // If we have changes just made and not yet sent, don't say start, because we're about to send changes + // that will unaccept everyone. + if( !bParamsUnfresh() ) + { + // Force user to put the correct disk in before proceeding. (Not crucial, but can lead to ugly + // timeouts if the scenario has to be downloaded before game start.) + if( !Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() || + Force_Scenario_Available( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ) ) + { + // Go into "waiting to start" mode, tell guests to, and wait for responses. + bWaitingToStart = true; + timeWaitingToStartTimeout = ::timeGetTime() + 30000; + nHostLastParamID++; + InformAboutStart(); + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + SetPlayerReadyToGo( pWO->szMyName, "ready" ); + Sound_Effect( VOC_GAME_CLOSED ); + } + } + } + break; + + case ( BUTTON_SCENARIO_RA | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_RA ); + break; + case ( BUTTON_SCENARIO_CS | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_CS ); + break; + case ( BUTTON_SCENARIO_AM | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_AM ); + break; + case ( BUTTON_SCENARIO_USER | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_USER ); + break; + +// case (BUTTON_LOAD | KN_BUTTON): +// case (BUTTON_OK | KN_BUTTON): +// break; + + case (KN_ESC): + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + case ( BUTTON_CANCEL | KN_BUTTON ): + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + + Call_Back(); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + pWO->ClearListPtrs(); + pWO->pGSupDlg = NULL; + + return ResultReturn; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetSpecialControlStates() +{ + // Set gauges and checklist. + + pCheckListOptions->Check_Item(0, Session.Options.Bases); + pCheckListOptions->Check_Item(1, Session.Options.Tiberium); + pCheckListOptions->Check_Item(2, Session.Options.Goodies); + pCheckListOptions->Check_Item(3, Special.IsCaptureTheFlag); // Ugh. Use of "Special" global. + pCheckListOptions->Check_Item(4, Special.IsShadowGrow); // Ugh. Use of "Special" global. + + pCheckListOptions->Check_Item(5, bSlowUnitBuildRate); // Ugh. Use of "Special" global. + + pGaugeCount->Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + pGaugeLevel->Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + pGaugeLevel->Set_Value(BuildLevel - 1); + + pGaugeCredits->Set_Maximum(Rule.MPMaxMoney); + pGaugeCredits->Set_Value(Session.Options.Credits); + + if( pWO->GameInfoCurrent.bTournament ) + { + pGaugeAIPlayers->Set_Maximum( 0 ); + } + else + { + // Note dependency of AIPlayers on number of human players. + // pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + pGaugeAIPlayers->Set_Maximum( Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax ); + } + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + if( bAftermathUnits ) + pCheckAftermathUnits->Turn_On(); + else + pCheckAftermathUnits->Turn_Off(); +} + +#define DRAWTABDOWN Turn_On() +#define DRAWTABUP Turn_Off() + +//*********************************************************************************************** +void WOL_GameSetupDialog::BindControls( bool bBind ) +{ + if( bBind ) + { + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + pILPlayers->Add_Tail(*commands); + if( bHost ) + { + // Draw order of tabs depends on which one is selected, as we want them to overlap appropriately. + // Also, the selected tab must appear over the scenario list, while the unselected tabs are below it. + // This assures that that bottom of the tab overlaps the scenario list border, making it look connected. + + // ajw I could make all maps always available now that they're downloadable. Playing CS map with AM rules + // would mean switching CDs, though. + // Would mean no difference between RA and CS games. + + switch( ScenKindCurrent ) + { + case SCENARIO_RA: + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABUP; + break; + case SCENARIO_CS: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::CSGAME. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABUP; + break; + case SCENARIO_AM: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::AMGAME, or RAGAME with AM installed. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABUP; + break; + case SCENARIO_USER: // pWO->GameInfoCurrent.bTournament must be false. + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABUP; + break; + } + } +// pStaticDescrip->Add_Tail(*commands); + pEditSend->Add_Tail(*commands); + pStaticUnit->Add_Tail(*commands); + pStaticLevel->Add_Tail(*commands); + pStaticCredits->Add_Tail(*commands); + pStaticAIPlayers->Add_Tail(*commands); + pGaugeCount->Add_Tail(*commands); + pGaugeCredits->Add_Tail(*commands); + pGaugeAIPlayers->Add_Tail(*commands); + pGaugeLevel->Add_Tail(*commands); + pCheckListOptions->Add_Tail(*commands); + pTextBtnCancel->Add_Tail(*commands); + pTextBtnAcceptStart->Add_Tail(*commands); + pTextBtnAction->Add_Tail(*commands); + pILDisc->Add_Tail(*commands); + pDropListHouse->Add_Tail(*commands); +// pCheckAftermathUnits->Add_Tail(*commands); + } + else + { + pWO->pShpBtnDiscon->Zap(); + pWO->pShpBtnLeave->Zap(); + pWO->pShpBtnRefresh->Zap(); + pWO->pShpBtnSquelch->Zap(); + pWO->pShpBtnBan->Zap(); + pWO->pShpBtnKick->Zap(); + pWO->pShpBtnFindpage->Zap(); + pWO->pShpBtnOptions->Zap(); + pWO->pShpBtnLadder->Zap(); + pWO->pShpBtnHelp->Zap(); + pILPlayers->Zap(); + if( bHost ) + { + pShpBtnScenarioRA->Zap(); + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Zap(); + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + pShpBtnScenarioCS->Zap(); + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + pShpBtnScenarioAM->Zap(); + pILScens->Zap(); + } +// pStaticDescrip->Zap(); + pEditSend->Zap(); + pStaticUnit->Zap(); + pStaticLevel->Zap(); + pStaticCredits->Zap(); + pStaticAIPlayers->Zap(); + pGaugeCount->Zap(); + pGaugeCredits->Zap(); + pGaugeAIPlayers->Zap(); + pGaugeLevel->Zap(); + pCheckListOptions->Zap(); + pTextBtnCancel->Zap(); + pTextBtnAcceptStart->Zap(); + pTextBtnAction->Zap(); + pILDisc->Zap(); + pDropListHouse->Zap(); +// pCheckAftermathUnits->Zap(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ) +{ +// debugprint( "ScenarioDisplayMode, from %i going into %i mode.\n", ScenKindCurrent, ScenKind ); + + // Puts us into mode where we are viewing a particular gamekind of scenario list. + if( ScenKindCurrent == ScenKind ) + return; + + // Reorder tab buttons. + BindControls( false ); + ScenKindCurrent = ScenKind; + BindControls( true ); + + // Reset list items. + pILScens->Clear(); +// debugprint( " * * * * * * ScenKind %i has %i items.\n", ScenKind, ar_szScenarios[ ScenKind ].Count() ); + // Check for the currently selected scenario. + bool bFoundCurrentSelection = false; + int iSelect; + for( int i = 0; i != ar_szScenarios[ ScenKind ].Count(); i++ ) + { + // Put ScenarioIndex in as extradata to list item. + int iScenIndex = (int)ar_szScenIndexes[ ScenKind ][ i ]; + int iIndexNew = pILScens->Add_Item( ar_szScenarios[ ScenKind ][ i ], NULL, NULL, ICON_DIB, NULL, (void*)iScenIndex ); + if( iScenIndex == Session.Options.ScenarioIndex && !bFoundCurrentSelection ) + { + // (Choose first line of what can be multiline description of currently selected scenario.) + bFoundCurrentSelection = true; + iSelect = i; + } + } + // If the current scenario selection is in this list, enable the list to show the selection, otherwise, + // make the listclass selection invisible, because it doesn't indicate the real scenario selection. + // Basically, problem is that I can't have no selection in ListClass, and I don't want to risk changing + // it as I'll affect the rest of the code. So I do this horrible hack. + if( bFoundCurrentSelection ) + { + pILScens->SetSelectType( 1 ); // Regular selection. + pILScens->Set_Selected_Index( iSelect ); + } + else + pILScens->SetSelectType( 0 ); // Invisible selection. + +// display = REDRAW_ALL; + pILScens->Flag_To_Redraw(); + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::ExitGameChannel() +{ +// debugprint( "ExitGameChannel \n" ); + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingGameChannel(); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::DrawScenarioDescripIcon( const char* pDIB ) const +{ + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - 2, 100, WINDOW_MAIN ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerColor( const char* szName, PlayerColorType Color ) +{ + // Sets player color - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerColor %s to %i\n", szName, Color ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player color Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - color will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerColor could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + if( strcmp( pWO->szMyName, szName ) == 0 ) + { + // I am the player involved. + Session.ColorIdx = Color; + if( display < REDRAW_COLORS ) display = REDRAW_COLORS; + } + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ Color == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Color ] ); + pILPlayers->Flag_To_Redraw(); + +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::GetPlayerColor( const char* szName ) +{ + // Returns player color, if player is found in list, else PCOLOR_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return PCOLOR_NONE; + } + RemapControlType* pRemap = pILPlayers->Get_Item_Color( iItem ); + return PlayerColorTypeOf( pRemap ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerHouse( const char* szName, HousesType House ) +{ + // Sets player house - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerHouse %s to %i\n", szName, House ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player house Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - house will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerHouse could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + // Reset item text. + char szItem[ 100 ]; + pWO->WritePlayerListItem( szItem, szName, House ); +// debugprint ( "%i, %s\n", iItem, szItem ); + pILPlayers->Set_Item( iItem, szItem ); + pILPlayers->Flag_To_Redraw(); +} + +//*********************************************************************************************** +HousesType WOL_GameSetupDialog::GetPlayerHouse( const char* szName ) +{ + // Returns player house for user if found in list, else HOUSE_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return HOUSE_NONE; + } + + return pWO->PullPlayerHouse_From( pILPlayers->Get_Item( iItem ) ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerAccepted( const char* szName, bool bAccepted ) +{ + // Sets player's 'accepted' state to true or false. + // Value is stored in the player list: if there is an accepted icon, player has accepted. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerAccepted() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerAccepted() - set '%s' to %s.\n", szName, bAccepted ? "accepted" : "NOT accepted" ); + return pWO->MarkItemAccepted( iItem, bAccepted ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::IveAccepted() +{ + // Returns true if I am marked as "accepted". + int iItem = pILPlayers->Find( pWO->szMyName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedAccepted( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerReadyToGo( const char* szName, const char* szReadyState ) +{ + // Sets player's 'ready to go' state. + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + // Value is stored in the player list in the hidden string field. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerReadyToGo() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerReadyToGo() - set '%s' to %s.\n", szName, szReadyState ); + pWO->MarkItemReadyToGo( iItem, szReadyState ); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ResetReadyToGo() +{ + // Resets all users to "not ready to go". + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + pWO->MarkItemReadyToGo( i, NULL ); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bPlayerReadyToGo( const char* szName ) +{ + // Returns true if player is marked as "ready to go". + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedReadyToGo( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllPlayersReadyToGo() +{ + // Returns true if all players are marked as "ready to go". +// debugprint( "Checking for all players ready - there are %i\n", pILPlayers->Count() ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedReadyToGo( i ) ) + { +// debugprint( "Item %i NOT ready\n", i ); + return false; + } +// debugprint( "Item %i is ready\n", i ); + } +// debugprint( "Return true\n" ); + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessGuestRequest( User* pUser, const char* szRequest ) +{ + // Game host processes a request that arrived as a privategameopt from one of the guests. + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 null-terminator +// debugprint( "ProcessGuestRequest. szRequest is '%s', len %i.\n", szRequest, strlen( szRequest ) ); + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szRequest ); + szRequest += 3; + + switch( opt ) + { + case WOL_GAMEOPT_REQCOLOR: + { + PlayerColorType ColorDesired = (PlayerColorType)( atoi( szRequest ) ); + if( pILPlayers->FindColor( &ColorRemaps[ ColorDesired == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : ColorDesired ] ) == -1 ) + { + // Color is available. + SetPlayerColor( (char*)pUser->name, ColorDesired ); + // Tell all guests about the color change. + InformAboutPlayerColor( (char*)pUser->name, ColorDesired, NULL ); + } + else + { + // Color is not available. +// debugprint( "Color %i denied to %s\n", ColorDesired, (char*)pUser->name ); + // Tell requestor that his color is still the same. + RemapControlType* pColorRemapCurrent = pILPlayers->Get_Item_Color( pILPlayers->Find( (char*)pUser->name ) ); + InformAboutPlayerColor( (char*)pUser->name, PlayerColorTypeOf( pColorRemapCurrent ), pUser ); + } + break; + } + case WOL_GAMEOPT_REQHOUSE: + { + HousesType HouseChoice = (HousesType)( atoi( szRequest ) ); +// debugprint( "Host received: '%s' changed house to %u.\n", (char*)pUser->name, HouseChoice ); + SetPlayerHouse( (char*)pUser->name, HouseChoice ); + nHostLastParamID++; + InformAboutPlayerHouse( (char*)pUser->name, HouseChoice, NULL ); + ClearAllAccepts(); + break; + } + case WOL_GAMEOPT_REQACCEPT: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) + { +// debugprint( "Host received valid accept from '%s'.\n", (char*)pUser->name ); + SetPlayerAccepted( (char*)pUser->name, true ); + InformAboutPlayerAccept( (char*)pUser->name, NULL ); + // We may be ready to start a game now. + if( bAllGuestsAccept() ) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Enable(); + } + } + else + { +// debugprint( "______________Host received invalid accept from '%s'. ID = %i when it should be %i.\n", (char*)pUser->name, +// atoi( szRequest ), nHostLastParamID ); + } + break; + case WOL_GAMEOPT_REQSTART: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART from '%s'.\n", (char*)pUser->name ); +// WOL_PrintMessage( *pILDisc, "WOL_GAMEOPT_REQSTART response", WOLCOLORREMAP_LOCALMACHINEMESS ); +// WOL_PrintMessage( *pILDisc, (char*)pUser->name, WOLCOLORREMAP_LOCALMACHINEMESS ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "ready" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO from '%s'.\n", (char*)pUser->name ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "need scenario" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_INFGO: + // I have told myself to start game right now. + // This is the new method. Avoids apparent "private messages/gameopts are getting delayed" problem in chatserver... + // (I don't want to end up leaving the channel before guests get my go message.) + strcpy( szTriggerGameStartInfo, szRequest ); + break; + default: +// debugprint( "Unhandled value of %i in ProcessGuestRequest!!!\n", opt ); + break; + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessInform( char* szInform ) +{ + // Process inform message arriving from game host. +// debugprint( "ProcessInform: '%s'\n", szInform ); + if( !bHost ) + { + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szInform ); + szInform += 3; + switch( opt ) + { + case WOL_GAMEOPT_INFCOLOR: + { + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + PlayerColorType Color = (PlayerColorType)( atoi( szInform ) ); + szInform += 3; + SetPlayerColor( szInform, Color ); // (szInform is now sitting at the start of the name string.) + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFHOUSE: // Note: In theory, I could ignore this if it refers to me. I've already set my own house. + { + nGuestLastParamID = atoi( szInform ); + szInform += 7; + HousesType House = (HousesType)( atoi( szInform ) ); + szInform += 3; + SetPlayerHouse( szInform, House ); // (szInform is now sitting at the start of the name string.) + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFACCEPT: // Note: In theory, I could ignore this if it refers to me. + // A guest has accepted. + SetPlayerAccepted( szInform, true ); // (szInform is now sitting at the start of the name string.) + break; + case WOL_GAMEOPT_INFPARAMS: + // Game params have changed. + bParamsReceived = true; + if( !AcceptParams( szInform ) ) + bLeaveDueToRulesMismatchTrigger = true; + SetSpecialControlStates(); + //pILScens->Set_Selected_Index( Session.Options.ScenarioIndex ); + display = REDRAW_ALL; + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFNEWGUESTPLAYERINFO: + // I have just joined and have received a message with info on all players in game. + AcceptNewGuestPlayerInfo( szInform ); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFSTART: + { + // Host tells us to wait for start of game. +// debugprint( "Guest received WOL_GAMEOPT_INFSTART.\n" ); + nGuestLastParamID = atoi( szInform ); +// The following check is not necessary. Rules.ini, if manually replaced by a cheater, is not reloaded. +// So prior checks (that occur on game params receives) are sufficient. +// szInform += 7; +// // Check rules.ini compatibility. +// int iRulesID = atoi( szInform ); +// if( RuleINI.Get_Unique_ID() != iRulesID ) +// { +// // Rules.ini incompatible. Don't respond to call for start. +// bLeaveDueToRulesMismatchTrigger = true; +// break; +// } + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // This better'd be true. + { + // Send ack back to host. + char szSend[ 20 ]; + // Make sure we have the scenario. + if( !bNeedScenarioDownload() ) + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART, nGuestLastParamID ); + else + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + // Enter waiting mode. + bWaitingToStart = true; + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. + // Set global that will force edit dialogs to stop accepting characters. + // This is to fix a minor glitch: guests can keep typing into a "page" dialog editbox after the + // "Launching game..." message has appeared on top of it. + if( pWO->bPump_In_Call_Back ) + disable_current_msgbox = true; + } + else + { +// debugprint( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + Fatal( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + } + Sound_Effect( VOC_GAME_CLOSED ); + break; + } + case WOL_GAMEOPT_INFCANCELSTART: + // Host tells us to cancel waiting for start of game. + bWaitingToStart = false; + ClearAllAccepts(); + BindControls( true ); + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTCANCELLED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( VOC_SYS_ERROR ); + display = REDRAW_ALL; + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + case WOL_GAMEOPT_INFGO: + // Host says start game right now. + strcpy( szTriggerGameStartInfo, szInform ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + default: +// debugprint( "Unhandled value of %i in ProcessInform!!!\n", opt ); + //WOL_PrintMessage( *pILDisc, "Error - Unhandled value in ProcessInform!!!", WOLCOLORREMAP_LOCALMACHINEMESS ); + Fatal( "Error - Unhandled value in ProcessInform!" ); + break; + } + } +// debugprint( "* END of ProcessInform: '%s'\n", szInform ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bParamsUnfresh() +{ + // Returns true if game setup parameters do not match what they were last time they were sent. + GAMEPARAMS GParamsNow; + SetGParamsToCurrent( GParamsNow ); + +// if( !( GParamsNow == GParamsLastSent ) ) +// { +// debugprint( "---- Params Unfresh: OLD...\n" ); +// Debug_GlobalPacketType( GParamsLastSent.GPacket ); +// debugprint( "------------------- NEW...\n" ); +// Debug_GlobalPacketType( GParamsNow.GPacket ); +// debugprint( "old bAftermathUnits = %i\n", GParamsLastSent.bAftermathUnits ); +// debugprint( "new bAftermathUnits = %i\n", GParamsNow.bAftermathUnits ); +// } + + return !( GParamsNow == GParamsLastSent ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SendParams() +{ + // Host only. + + SetGParamsToCurrent( GParamsLastSent ); + + char szSend[ 130 + DESCRIP_MAX + 12 + 32 + 100 ]; + sprintf( szSend, + "%01u " + "%06u " + "%03u " + "%s " + "%u " + "%s " + "%01u " + "%.32s " // No null-terminator on digest. There may be nothing at all inserted here. + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + , + WOL_GAMEOPT_INFPARAMS, + nHostLastParamID, + strlen( GParamsLastSent.GPacket.ScenarioInfo.Scenario ), + GParamsLastSent.GPacket.ScenarioInfo.Scenario, + GParamsLastSent.GPacket.ScenarioInfo.FileLength, + GParamsLastSent.GPacket.ScenarioInfo.ShortFileName, +// strlen( (char*)GParamsLastSent.GPacket.ScenarioInfo.FileDigest ), not null-terminated! + GParamsLastSent.GPacket.ScenarioInfo.FileDigest[0] ? 1 : 0, + GParamsLastSent.GPacket.ScenarioInfo.FileDigest, + GParamsLastSent.GPacket.ScenarioInfo.OfficialScenario, + GParamsLastSent.GPacket.ScenarioInfo.Credits, + GParamsLastSent.GPacket.ScenarioInfo.IsBases, + GParamsLastSent.GPacket.ScenarioInfo.IsTiberium, + GParamsLastSent.GPacket.ScenarioInfo.IsGoodies, + GParamsLastSent.GPacket.ScenarioInfo.BuildLevel, + GParamsLastSent.GPacket.ScenarioInfo.UnitCount, + GParamsLastSent.GPacket.ScenarioInfo.AIPlayers, + GParamsLastSent.GPacket.ScenarioInfo.Seed, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsShadowGrow, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsSpeedBuild, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsFromInstall, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsCaptureTheFlag, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsInert, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsThreePoint, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTGrowth, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTSpread, + GParamsLastSent.GPacket.ScenarioInfo.GameSpeed, + GParamsLastSent.GPacket.ScenarioInfo.Version, + GParamsLastSent.bAftermathUnits, // Not currently used. + GParamsLastSent.bSlowUnitBuildRate, + RuleINI.Get_Unique_ID() // Used to verify rules.ini files match. + ); + + pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::AcceptParams( char* szParams ) +{ + // Reverse of SendParams() process. szParams has already been stripped of 2 bytes header. Guest only. + + // Returns false if rules.ini doesn't match that of the host. + // (Or if an error occurs due to the packet being incorrect - which happened once in test...) + + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szParams, szDelimiter ); + nGuestLastParamID = atoi( szToken ); + + // Read in length of following string. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iLen = atoi( szToken ); + // Set string pointer to start of string (length is 3 digits). + szRemaining = szToken + 4; + // Read in string. + memcpy( Session.Options.ScenarioDescription, szRemaining, iLen ); + // Null-terminate. + Session.Options.ScenarioDescription[ iLen ] = 0; + // Advance string pointer to next param. + szRemaining += iLen + 1; + +//debugprint( "scenario description is '%s'\n", Session.Options.ScenarioDescription ); +//debugprint( "remaining: '%s'\n", szRemaining ); + + szToken = strtok( szRemaining, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioFileLength = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + strcpy( Session.ScenarioFileName, szToken ); + +// // Read in length of following string. +// szToken = strtok( NULL, szDelimiter ); +// iLen = atoi( szToken ); +// // Set string pointer to start of string (length is 3 digits). +// szRemaining = szToken + 4; + // Method changed. + // Check if there is a digest. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + iLen = atoi( szToken ); // 1 or 0, indicating if there is a digest following. + if( iLen ) + { +// // Set string pointer to start of string (previous field is 1 digit). +// szRemaining = szToken + 2; +// iLen = sizeof( Session.ScenarioDigest ); +// // Read in string. +// memcpy( Session.ScenarioDigest, szRemaining, iLen ); +// // // Null-terminate. +// // Session.ScenarioDigest[ iLen ] = 0; Digest has no null-terminator! +// // Advance string pointer to next param. +// szRemaining += iLen + 1; + // There is a digest. + szToken = strtok( NULL, szDelimiter ); // (Digests can't have spaces in the them.) +//debugprint( "digest: '%s'\n", szToken ); + if( !szToken ) return false; + strncpy( Session.ScenarioDigest, szToken, sizeof( Session.ScenarioDigest ) ); + } + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioIsOfficial = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Credits = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Bases = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Tiberium = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Goodies = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + BuildLevel = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.UnitCount = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.AIPlayers = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Seed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsShadowGrow = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsSpeedBuild = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsFromInstall = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsCaptureTheFlag = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsInert = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsThreePoint = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTGrowth = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTSpread = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Options.GameSpeed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; +// "Version" = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bAftermathUnits = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bSlowUnitBuildRate = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iRulesID = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + return ( RuleINI.Get_Unique_ID() == iRulesID ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetGParamsToCurrent( GAMEPARAMS& GParams ) +{ + // Sets values in a GAMEPARAMS to the current game settings. + + strcpy( GParams.GPacket.ScenarioInfo.Scenario, Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + CCFileClass file( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + GParams.GPacket.ScenarioInfo.FileLength = file.Size(); + strcpy( GParams.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + // Digest is not null-terminated. + strncpy( (char*)GParams.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Digest(), sizeof( GParams.GPacket.ScenarioInfo.FileDigest ) ); + GParams.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + GParams.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + GParams.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + GParams.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + GParams.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + GParams.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GParams.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + GParams.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + GParams.GPacket.ScenarioInfo.Seed = Seed; + GParams.GPacket.ScenarioInfo.Special = Special; + GParams.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + GParams.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + GParams.bAftermathUnits = bAftermathUnits; + GParams.bSlowUnitBuildRate = bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ) +{ + return gp1.GPacket == gp2.GPacket && + gp1.bAftermathUnits == gp2.bAftermathUnits && gp1.bSlowUnitBuildRate == gp2.bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ) +{ + if( strcmp( gp1.ScenarioInfo.Scenario, gp2.ScenarioInfo.Scenario ) != 0 ) return false; + if( strcmp( gp1.ScenarioInfo.ShortFileName, gp2.ScenarioInfo.ShortFileName ) != 0 ) return false; + // Digest is not null-terminated... + if( strncmp( (const char*)gp1.ScenarioInfo.FileDigest, (const char*)gp2.ScenarioInfo.FileDigest, sizeof( gp1.ScenarioInfo.FileDigest ) ) != 0 ) return false; + + if( gp1.ScenarioInfo.FileLength == gp2.ScenarioInfo.FileLength && + gp1.ScenarioInfo.OfficialScenario == gp2.ScenarioInfo.OfficialScenario && + gp1.ScenarioInfo.Credits == gp2.ScenarioInfo.Credits && + gp1.ScenarioInfo.IsBases == gp2.ScenarioInfo.IsBases && + gp1.ScenarioInfo.IsTiberium == gp2.ScenarioInfo.IsTiberium && + gp1.ScenarioInfo.IsGoodies == gp2.ScenarioInfo.IsGoodies && + gp1.ScenarioInfo.BuildLevel == gp2.ScenarioInfo.BuildLevel && + gp1.ScenarioInfo.UnitCount == gp2.ScenarioInfo.UnitCount && + gp1.ScenarioInfo.AIPlayers == gp2.ScenarioInfo.AIPlayers && + gp1.ScenarioInfo.Seed == gp2.ScenarioInfo.Seed && + gp1.ScenarioInfo.Special.IsShadowGrow == gp2.ScenarioInfo.Special.IsShadowGrow && + gp1.ScenarioInfo.Special.IsSpeedBuild == gp2.ScenarioInfo.Special.IsSpeedBuild && + gp1.ScenarioInfo.Special.IsFromInstall == gp2.ScenarioInfo.Special.IsFromInstall && + gp1.ScenarioInfo.Special.IsCaptureTheFlag == gp2.ScenarioInfo.Special.IsCaptureTheFlag && + gp1.ScenarioInfo.Special.IsInert == gp2.ScenarioInfo.Special.IsInert && + gp1.ScenarioInfo.Special.IsThreePoint == gp2.ScenarioInfo.Special.IsThreePoint && + gp1.ScenarioInfo.Special.IsTGrowth == gp2.ScenarioInfo.Special.IsTGrowth && + gp1.ScenarioInfo.Special.IsTSpread == gp2.ScenarioInfo.Special.IsTSpread && + gp1.ScenarioInfo.GameSpeed == gp2.ScenarioInfo.GameSpeed && + gp1.ScenarioInfo.Version == gp2.ScenarioInfo.Version ) + return true; + + return false; +} + +//*********************************************************************************************** +/* +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ) // ajw debugging +{ + if( *gp1.ScenarioInfo.Scenario ) +// debugprint( "Scenario = %s\n", (char*)gp1.ScenarioInfo.Scenario ); + else +// debugprint( "!Scenario string is null\n" ); + if( *gp1.ScenarioInfo.ShortFileName ) +// debugprint( "ShortFileName = %s\n", (char*)gp1.ScenarioInfo.ShortFileName ); + else +// debugprint( "!ShortFileName string is null\n" ); + // Remember ShortFileName is not null-terminated... + if( *gp1.ScenarioInfo.FileDigest ) +// debugprint( "FileDigest = %.32s\n", (char*)gp1.ScenarioInfo.FileDigest ); + else +// debugprint( "!FileDigest string is null\n" ); + +// debugprint( "FileLength = %i\n" + "OfficialScenario = %i\n" + "Credits = %i\n" + "IsBases = %i\n" + "IsTiberium = %i\n" + "IsGoodies = %i\n" + "BuildLevel = %i\n" + "UnitCount = %i\n" + "AIPlayers = %i\n" + "Seed = %i\n" + "Special.IsShadowGrow = %i\n" + "Special.IsSpeedBuild = %i\n" + "Special.IsFromInstall = %i\n" + "Special.IsCaptureTheFlag = %i\n" + "Special.IsInert = %i\n" + "Special.IsThreePoint = %i\n" + "Special.IsTGrowth = %i\n" + "Special.IsTSpread = %i\n" + "GameSpeed = %i\n" + "Version = %i\n", + gp1.ScenarioInfo.FileLength, + gp1.ScenarioInfo.OfficialScenario, + gp1.ScenarioInfo.Credits, + gp1.ScenarioInfo.IsBases, + gp1.ScenarioInfo.IsTiberium, + gp1.ScenarioInfo.IsGoodies, + gp1.ScenarioInfo.BuildLevel, + gp1.ScenarioInfo.UnitCount, + gp1.ScenarioInfo.AIPlayers, + gp1.ScenarioInfo.Seed, + gp1.ScenarioInfo.Special.IsShadowGrow, + gp1.ScenarioInfo.Special.IsSpeedBuild, + gp1.ScenarioInfo.Special.IsFromInstall, + gp1.ScenarioInfo.Special.IsCaptureTheFlag, + gp1.ScenarioInfo.Special.IsInert, + gp1.ScenarioInfo.Special.IsThreePoint, + gp1.ScenarioInfo.Special.IsTGrowth, + gp1.ScenarioInfo.Special.IsTSpread, + gp1.ScenarioInfo.GameSpeed, + gp1.ScenarioInfo.Version ); +} +*/ + +//*********************************************************************************************** +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ) +{ + for( PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++ ) + { + if( &ColorRemaps[ pcolor ] == pColorRemap ) + return pcolor; + } + return PCOLOR_NONE; +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::RequestPlayerColor( PlayerColorType Color ) +{ + // Local player sends a request to the game host asking for a particular color. + char szSend[ 20 ]; + + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQCOLOR, Color ); + + _ASSERTE( pWO->pGameHost() ); + + return pWO->SendGameOpt( szSend, pWO->pGameHost() ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ) +{ + // Game host tells guests about a player color assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 20 + WOL_NAME_LEN_MAX ]; + + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + + if( Color == PCOLOR_NONE ) + { +// debugprint( "Bad Color for %s in InformAboutPlayerColor.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %02u %s", WOL_GAMEOPT_INFCOLOR, Color, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ) +{ + // Game host tells guests about a player house assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 28 + WOL_NAME_LEN_MAX ]; + // WOL_GAMEOPT_INFHOUSE format: + // 2 WOL_GAMEOPT + // 1 space + // 6 param ID + // 1 space + // 2 house + // 1 space + // string name of player + + if( House == HOUSE_NONE ) + { +// debugprint( "Bad House for %s in InformAboutPlayerHouse.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %06u %02u %s", WOL_GAMEOPT_INFHOUSE, nHostLastParamID, (short)House, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerAccept( const char* szName, User* pUserPriv ) +{ + // Game host tells guests about player accepting game params. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 6 + WOL_NAME_LEN_MAX ]; + sprintf( szSend, "%02u %s", WOL_GAMEOPT_INFACCEPT, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. + char szSend[ 10 ]; + + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_INFSTART, nHostLastParamID ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutCancelStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. +// debugprint( "InformAboutCancelStart!\n" ); + + char szSend[ 10 ]; + + sprintf( szSend, "%02u", WOL_GAMEOPT_INFCANCELSTART ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestJoin( User* pUser ) +{ + // A guest (not myself) has entered the game channel. +// debugprint( "OnGuestJoin()\n" ); + char* szPrint = new char[ strlen( TXT_WOL_PLAYERJOINEDGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERJOINEDGAME, (char*)pUser->name ); + WOL_PrintMessage( *pILDisc, szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + + ClearAllAccepts(); + + if( bHost ) + { + // Send the new guest the current setup. Note that nHostLastParamID doesn't change here. + + // Assign color to new guest. + PlayerColorType Color = ColorNextAvailable(); + SetPlayerColor( (char*)pUser->name, Color ); + + // Previously, I was sending an individual color, house, and acceptedstate message for each other guest. + // Though convenient code-wise, this causes the initial info to arrive at the new guest in a very slow + // manner. This is because of the wonderful "anti-flood" feature of the chat server, which prevents a + // series of messages from a client from being passed on faster than a certain rate. + // For this reason, a new message that contains all of the info about all of the other guests has been + // created (WOL_GAMEOPT_INFNEWGUESTPLAYERINFO). + + // WOL_GAMEOPT_INFNEWGUESTPLAYERINFO format (items separated by spaces): + // WOL_GAMEOPT + // number of players + // for each player { + // length of player name string + // player name + // color + // bool - is there a house field following? + // (house) + // // (removed) acceptedness - true for set player accepted, false for do nothing + // } + + // Build up a big WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + char szSend[ 500 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, pILPlayers->Count() ); + // Send color and house of all players (including himself) to the new guest. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + char szSendPiece[ 100 ]; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( i ) ); +// InformAboutPlayerColor( szPlayerName, PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ), pUser ); + sprintf( szSendPiece, " %02u %s %02u", strlen( szPlayerName ), szPlayerName, + PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ) ); + + if( strcmp( szPlayerName, (char*)pUser->name ) != 0 ) + { + HousesType House = pWO->PullPlayerHouse_From( pILPlayers->Get_Item( i ) ); + if( House != HOUSE_NONE ) + { + // InformAboutPlayerHouse( szPlayerName, House, pUser ); + char szSendHouse[ 50 ]; + sprintf( szSendHouse, " 1 %02u", (short)House ); + strcat( szSendPiece, szSendHouse ); + } + else + { + // Player must not have told me what house he is yet. Don't send house value. + strcat( szSendPiece, " 0" ); + } + } + else + { + // Player is the new guest himself. Don't send house value. + strcat( szSendPiece, " 0" ); + } + +// Acceptedness must be false! No need to send. +// // Send "accepted" status of player. Ignore myself, as I'm the host. +// if( strcmp( szPlayerName, pWO->szMyName ) != 0 && pWO->bItemMarkedAccepted( i ) ) +// strcat( szSendPiece, " 1" ); +// else +// strcat( szSendPiece, " 0" ); + + strcat( szSend, szSendPiece ); + } + pWO->SendGameOpt( szSend, pUser ); + + // Send everyone the color of the new guest. + InformAboutPlayerColor( (char*)pUser->name, Color, NULL ); + + // Send game params. + // This is done last because it contains a param ID value, and we need to ensure that the new guest has + // received everything we are sending him here before he tries to send me an accept. + // If game params were sent first, he could theoretically receive them, then send an accept, even though he + // has not received the WOL_GAMEOPT_INFNEWGUESTPLAYERINFO. + // By doing this I avoid having to have a param ID in WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, which would be hard, + // since it is a private message. + // For simplicity, send public. + SendParams(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::AcceptNewGuestPlayerInfo( char* szMsg ) +{ + // Process a received WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + // szMsg has already been stripped of 2 bytes header. + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szMsg, szDelimiter ); + unsigned int nPlayers = atoi( szToken ); + + // We have to assist strtok a bit because of calls below that may also call strtok()... + szRemaining = szMsg + 3; + + for( unsigned int nPlayer = 0; nPlayer != nPlayers; ++nPlayer ) + { + // Read in length of following string. + szToken = strtok( szRemaining, szDelimiter ); + int iLen = atoi( szToken ); + + // Set string pointer to start of string (length is 2 digits). + szRemaining = szToken + 3; + // Read in string. + char szPlayerName[ 50 ]; + memcpy( szPlayerName, szRemaining, iLen ); + // Null-terminate. + szPlayerName[ iLen ] = 0; + + // Advance string pointer to next param. + szRemaining += iLen + 1; + + // Read color. + szToken = strtok( szRemaining, szDelimiter ); + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); + + // SetPlayerColor may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 3; + + // Read whether there is a house field. + szToken = strtok( szRemaining, szDelimiter ); + bool bHouseField = (bool)atoi( szToken ); + + if( bHouseField ) + { + // Read house. + szToken = strtok( NULL, szDelimiter ); + HousesType House = (HousesType)atoi( szToken ); + SetPlayerHouse( szPlayerName, House ); + // SetPlayerHouse may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 5; + } + else + szRemaining += 2; // Advance past "0 ". + +// Acceptedness must be false. No need for it in message. +// // Read acceptedness. +// szToken = strtok( NULL, szDelimiter ); +// bool bAccepted = (bool)atoi( szToken ); +// if( bAccepted ) +// SetPlayerAccepted( szPlayerName, true ); + } + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + ClearAllAccepts(); // Most likely a pointless precaution. +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestLeave( User* pUser ) +{ + // pUser is about to leave but is still in our player list. + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + // Host is leaving the channel. We must be a guest, and so must leave also. This will trigger exit. + strcpy( szNameOfHostWhoJustBailedOnUs, (char*)pUser->name ); + } + else + { + ClearAllAccepts(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ClearAllAccepts() +{ + // Clears all "player has accepted" marks. +//debugprint( "ClearAllAccepts()\n" ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_CHANNELOWNER ) ) // pUser null if this is an "early insertion" entry on startup + pWO->MarkItemAccepted( i, false ); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( bHost ) + { + pTextBtnAcceptStart->Disable(); + if( bWaitingToStart ) + { + // Something has happened that makes starting a game not possible now. + // Cancel out of waiting mode and tell guests to do the same. + bWaitingToStart = false; + InformAboutCancelStart(); + BindControls( true ); + ResetReadyToGo(); + display = REDRAW_ALL; + } + else + pTextBtnAcceptStart->Flag_To_Redraw(); + } + else + { + pTextBtnAcceptStart->Enable(); + pTextBtnAcceptStart->Flag_To_Redraw(); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllGuestsAccept() +{ + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedAccepted( i ) ) + return false; + } + return true; +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::ColorNextAvailable() +{ + // Returns the first free player color available. + + // (Totally unoptimized, but hardly ever called.) + + for( int i = 0; i < MAX_MPLAYER_COLORS; i++ ) + { + if( pILPlayers->FindColor( &ColorRemaps[ i ] ) == -1 ) + { + return (PlayerColorType)i; + } + } +// debugprint( "ColorNextAvailable is NONE!\n" ); + return PCOLOR_NONE; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::GuestIsReadyToPlay( const char* szName, const char* szReadyState ) +{ + // A guest has responded to a game start request. + SetPlayerReadyToGo( szName, szReadyState ); + + if( bAllPlayersReadyToGo() ) + { +// debugprint( "All players ready to go.\n" ); + // We can start the game. + bHostSayGo = true; // Set trigger to fire function after we're out of callback. + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bNeedScenarioDownload() +{ + // Returns true if we don't have the scenario and it is allowable as a download. + if( !bHost ) + { + if( Find_Local_Scenario( Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial ) ) + { +// debugprint( "bNeedScenarioDownload() returning false.\n" ); + bRequestedScenarioDownload = false; + return false; + } + else + { +/* if( !Session.ScenarioIsOfficial ) + { + bRequestedScenarioDownload = true; + return true; + } + else + { +// debugprint( "bNeedScenarioDownload fatal\n" ); + Fatal( "" ); + } +*/ +// debugprint( "Requesting download\n" ); // ajw Shouldn't be happening with am maps when i have am...? + bRequestedScenarioDownload = true; // All maps are downloadable. + return true; + } + } + + bRequestedScenarioDownload = false; + return false; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::HostSaysGo() +{ + bHostSayGo = false; + bHostWaitingForGoTrigger = true; + +// debugprint( "HostSaysGo()\n" ); + + if( !pWO->RequestGameStart() ) + { +// debugprint( "Call to RequestGameStart() failed.\n" ); + //Fatal( "Call to RequestGameStart() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + + // Tell guests to start game. +// debugprint( "Telling guests to start game.\n" ); + + // Create WOL_GAMEOPT_INFGO message. + // This contains the color for each player, which can change about haphazardly at the end of setup, + // without causing "unacceptedness". This means that the colors everyone thinks everyone else is might not + // be sync'ed. Host sets everyone straight here. + char szSend[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 50 ] = ""; + sprintf( szSend, "%02u", WOL_GAMEOPT_INFGO ); + + User* pUser = pWO->pChatSink->pGameUserList; + while( pUser ) + { + char szUser[ WOL_NAME_LEN_MAX + 10 ]; + PlayerColorType Color = GetPlayerColor( (char*)pUser->name ); + sprintf( szUser, " %s %02u", (char*)pUser->name, Color ); // What if player left just now, and got removed from list. Ok to continue and fail on game start? + strcat( szSend, szUser ); + pUser = pUser->next; + } + if( !pWO->SendGo( szSend ) ) + { +// debugprint( "Call to SendGo() failed.\n" ); + //Fatal( "Call to SendGo() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + +/* ...Method changed. It appears that my channelleave may appear to guests before the "go" privategameopt. + For this reason, I'll wait until I receive a copy of the "go" message sent to myself, before proceeding. + + pWO->pChat->PumpMessages(); // Flush the send out. + // (ajw - Note: An apparent bug in wolapi means that this pump does not necessarily flush the go gameopts. + // This is ok in practice, because the host hits pumps later that will flush them.) + + // Pretend we just received szSend, and processed it like a guest would. + char* szFakeGoMessage = szSend + 3; + + TriggerGameStart( szFakeGoMessage ); +*/ +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::TriggerGameStart( char* szGoMessage ) +{ + // Last function before dialog is exited for game start. (Which must occur now.) + // Host or guest is about to start a game using final data in szGoMessage. + +// debugprint( "TriggerGameStart( %s )\n", szGoMessage ); + + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + + bHostWaitingForGoTrigger = false; + bExitForGameTrigger = true; + + // The following is based on Read_Game_Options()... + +// WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, sizeof(Session.Handle), buffer); + + strcpy( Session.Handle, pWO->szMyName ); + + // GameName will be the host's name... + strcpy( Session.GameName, pWO->pGameHostName() ); +// debugprint( "Session.GameName is %s\n", Session.GameName ); + +// gotit Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); +// gotit Session.PrefColor = Session.ColorIdx; +// gotit int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); +// gotit Session.House = (HousesType) ((int)HOUSE_USSR + temp); + +// gotit Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); +// gotit Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); +// gotit Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); +// gotit Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); +// gotit Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); +// gotit BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); +// gotit Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); +// gotit Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); +// gotit Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + +// UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + if( bSlowUnitBuildRate ) + UnitBuildPenalty = 250; + else + UnitBuildPenalty = 100; + + //PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodGameID = pWO->pChatSink->iGameID; + + // Reset ChatSink's iGameID. + pWO->pChatSink->iGameID = 0; + + //PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + PlanetWestwoodStartTime = time( NULL ); + //WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + +// gotit Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + // The preceding was based on Read_Game_Options()... + + + // Now do whatever we've left out that the horrific Net_Fake_New_Dialog() and Net_Fake_Join_Dialog() used to do for us... + + // Set up the Session.Players list. + // I think there is dependence on the local player being first, so put him there. + // Else put them in order listed in the szGoMessage. + // I will set "ID" based on a player's color, though it seems unclear if this is even used in the game, or what it should be. + + Clear_Vector( &Session.Players ); + + // Make the pILPlayers a valid list of players in the game. + // Players might (incredibly rarely) have joined in the last split-second, and we only want the players listed in + // the szGoMessage. To test for whether they're in this list, first wipe the colors from all list items. + // Then we fill them in from info in szGoMessage. + // We can ignore any list items then that have no color assigned. + // Also, we'll know that the colors assigned to valid players indeed match up with what every other client has. + // Remember, all other data should already be sync'ed because it has been implemented in such a way that changes would + // cause "unacceptedness" of guests to occur. + + // Clear colors in list. + for( int iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ PCOLOR_NONE ] ); + + // Parse szGoMessage to iterate through players. + char szDelimiter[] = " "; + char* szToken; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + + szToken = strtok( szGoMessage, szDelimiter ); + + while( szToken ) + { + strcpy( szPlayerName, szToken ); + szToken = strtok( NULL, szDelimiter ); + + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); // ajw note: inserts if not found. + szToken = strtok( NULL, szDelimiter ); + } + + // Add myself to Session.Players list. + _ASSERTE( pILPlayers->Find( pWO->szMyName ) != -1 ); + + NodeNameType* pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, pWO->szMyName ); // "Name" is 12 chars max. + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( pWO->szMyName ); +//debugprint( "ME: pPlayerNew->Player.House = %i\n", pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( pWO->szMyName ); +// This gets done later. +// pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + char szHostName[ WOL_NAME_LEN_MAX ] = "Game host"; + + // Add all other players to Session.Players list (if they have a valid color - see just above). + // Also in this step - build the scenario download requests array (used by hosts only). + memset( Session.ScenarioRequests, 0, sizeof( Session.ScenarioRequests ) ); + Session.RequestCount = 0; + for( iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + { + // The following is not very efficient, but doesn't have to be. Better in this case to keep it clear and simple. + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( iItem ) ); + if( strcmp( szPlayerName, pWO->szMyName ) != 0 && GetPlayerColor( szPlayerName ) != PCOLOR_NONE ) + { +// debugprint( "Creating player node '%s'\n", szPlayerName ); + pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, szPlayerName ); + // Get player's IP address from pChatSink... + unsigned long lAddress = ( pWO->pChatSink->GetPlayerGameIP( szPlayerName ) ); //ntohl( +// debugprint( "IP address is %i, or 0x%x\n", lAddress, lAddress ); + if( pWO->GameInfoCurrent.bTournament ) + { + // This is a tournament game, and I therefore have only one opponent: this one. + // for convenience, save a copy of his IP address in case I need it later for disconnect pinging. + pWO->TournamentOpponentIP = lAddress; + pWO->bDisconnectPingingCompleted = false; + } + NetNumType net; + NetNodeType node; + memset( net, 0, 4 ); + memset( node, 0, 6 ); + memcpy( node, &lAddress, 4 ); + //memcpy( node + 2, &lAddress, 4 ); + pPlayerNew->Address.Set_Address( net, node ); + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( szPlayerName ); +//debugprint( "Player %i: pPlayerNew->Player.House = %i\n", iItem, pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( szPlayerName ); + // This gets done later. + //pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + // If player is the game host, set HostAddress. This global is used when downloading scenarios; who knows where else. + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( iItem ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + { + Session.HostAddress = pPlayerNew->Address; + strcpy( szHostName, (char*)pUser->name ); +/* + // debugging + NetNumType netxxx; + NetNodeType nodexxx; + Session.HostAddress.Get_Address( netxxx, nodexxx ); +// debugprint( "Host, ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); +*/ + } +/* + else + { + NetNumType netxxx; + NetNodeType nodexxx; + pPlayerNew->Address.Get_Address( netxxx, nodexxx ); +// debugprint( "Player ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); + } +*/ + + if( bHost && pWO->bItemMarkedNeedScenario( iItem ) ) + { +// debugprint( "%s has requested scenario download.\n", szPlayerName ); + Session.ScenarioRequests[ Session.RequestCount++ ] = Session.Players.Count() - 1; + } + } +// else +// debugprint( "%s excluded from Session.Players\n", szPlayerName ); + } + + + // From Init... +// debugprint( "About to call Open_Socket().\n"); + PacketTransport->Open_Socket( 0 ); + +// debugprint( "RA95 - About to call Start_Listening.\n" ); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + +// debugprint( "Session.ScenarioFileName is %s.\n", Session.ScenarioFileName ); + + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ +// gotit WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", +// Session.Options.ScenarioDescription, +// sizeof (Session.Options.ScenarioDescription), +// buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + if( !bHost ) // Else ScenarioIndex is already set. + { + if( bRequestedScenarioDownload ) + { + Session.Options.ScenarioIndex = 1; + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + { + // Shouldn't ever happen. We should never have the opportunity to ask for one of these maps to be downloaded. + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + // Wait for download from game host. +//debugprint( "Wait for download from game host.\n" ); + if( !Get_Scenario_File_From_Host( Session.ScenarioFileName, 1 ) ) + { +// debugprint( "Get_Scenario_File_From_Host failed!\n" ); + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.ScenarioFileName ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + else + { + // Match ScenarioDescription to a ScenarioIndex. +/* This is how the same code existed previously. Insufficient because Description may match on many scenarios. + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } +*/ + // (We have already done the lookup, in Find_Local_Scenario, above.) + Session.Options.ScenarioIndex = ScenarioIndex_From_Filename( Session.ScenarioFileName ); + _ASSERTE( Session.Options.ScenarioIndex != -1 ); + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + } + else // bHost + { + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + strcpy( Session.Options.ScenarioDescription, (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + } + + Options.GameSpeed = 0; + + //Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + //Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + Session.MaxAhead = 15; //9; + //Session.FrameSendRate = 5; //3; + Session.FrameSendRate = 3; //3; + + // This is from NETDLG processing... + Session.NumPlayers = Session.Players.Count(); + + pWO->GameInfoCurrent.iPlayerCount = Session.Players.Count(); + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + + if( bHost ) + { + if( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + { + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + pWO->bSelfDestruct = true; + return; + } + } + if( Session.RequestCount ) + { + // Send the scenario to any guests that requested a download. +//debugprint( "Send the scenario to any guests that requested a download.\n" ); + Send_Remote_File( Scen.ScenarioName, 1 ); + } + } + + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + Ipx.Set_Timing (30, (unsigned long) -1, 600); + + pWO->bEnableNewAftermathUnits = bAftermathUnits; + bAftermathMultiplayer = bAftermathUnits; + + *pWO->szExternalPager = 0; + + pWO->bDoingDisconnectPinging = false; // Pointlessly making sure. + + *szTriggerGameStartInfo = 0; // This was set in order to trigger my coming here. +} + +#endif + +//*********************************************************************************************** +bool bSpecialAftermathScenario( const char* szScenarioDescription ) +{ + // Returns true if szScenarioDescription matches one of the descriptions for Aftermath multiplayer + // scenarios that have special Aftermath-only units *embedded* within them. + if( strcmp( szScenarioDescription, "Booby Traps (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Central Conflict Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Circles of Death (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Holy Grounds (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Island Wars Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "King of the Hills Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "The Hills Have Eyes (Mega 8 players)" ) == 0 ) + return true; + return false; +} + +//*********************************************************************************************** +int ScenarioIndex_From_Filename( const char* szScenarioFilename ) +{ + // Returns the scenario index that matches the scenario filename, or -1 if no match found. + for( int index = 0; index < Session.Scenarios.Count(); index++ ) + { + if( _stricmp( szScenarioFilename, Session.Scenarios[index]->Get_Filename() ) == 0 ) + return index; + } + return -1; +} diff --git a/CODE/WOL_GSUP.H b/CODE/WOL_GSUP.H new file mode 100644 index 0000000..bf630db --- /dev/null +++ b/CODE/WOL_GSUP.H @@ -0,0 +1,364 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// wol_gsup.h "WOL Game Setup Dialog" +// ajw 08/06/98 + +// Class WOL_GameSetupDialog is a move away from doing dialogs purely in C, a format that I've maintained in the login and +// chat dialogs, mimicking how dialogs are done elsewhere in the code, but became frustrated with. +// Though I'll follow the same pattern as before, things will be centralized a little cleaner through the use of this object. +// Why a standard dialog class that would handle common input behavior and so forth (one that this class could derive from) +// wasn't written 5 years ago, I don't know... + +#include "WolapiOb.h" +//class WolapiObject; +class IconListClass; +class EditClass; +class GaugeClass; +class CheckListClass; +class TextButtonClass; +class StaticButtonClass; +class DropListClass; +class ShapeButtonClass; +class BigCheckBoxClass; +class ToolTipClass; + +//*********************************************************************************************** +enum RESULT_WOLGSUP +{ + RESULT_WOLGSUP_BACKTOCHAT, + RESULT_WOLGSUP_FATALERROR, + RESULT_WOLGSUP_HOSTLEFT, + RESULT_WOLGSUP_STARTGAMEHOST, + RESULT_WOLGSUP_STARTGAME, + RESULT_WOLGSUP_RULESMISMATCH, + RESULT_WOLGSUP_LOGOUT, +}; + +struct GAMEPARAMS +{ + GlobalPacketType GPacket; + // My additions to game params. - ajw + bool bAftermathUnits; + bool bSlowUnitBuildRate; +}; + +//*********************************************************************************************** +class WOL_GameSetupDialog +{ +public: + WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ); + ~WOL_GameSetupDialog(); + + RESULT_WOLGSUP Run(); + +public: + bool bHost; // True when I created the game channel and am the host. + + bool bHostSayGo; // Trigger host instructing all to start game immediately. + bool bHostWaitingForGoTrigger; // True while host is waiting for go message to bounce back to him and trigger start. + bool bExitForGameTrigger; // Trigger exiting dialog for game. + + + void ProcessGuestRequest( User* pUser, const char* szRequest ); + void ProcessInform( char* szRequest ); + void OnGuestJoin( User* pUser ); + void OnGuestLeave( User* pUser ); + +protected: + void Initialize(); + RESULT_WOLGSUP Show(); + void SetSpecialControlStates(); + void BindControls( bool bBind ); + bool ExitGameChannel(); + void DrawScenarioDescripIcon( const char* pDIB ) const; + void SetPlayerColor( const char* szName, PlayerColorType Color ); + PlayerColorType GetPlayerColor( const char* szName ); + void SetPlayerHouse( const char* szName, HousesType House ); + HousesType GetPlayerHouse( const char* szName ); + bool SetPlayerAccepted( const char* szName, bool bAccepted ); + bool IveAccepted(); + bool SetPlayerReadyToGo( const char* szName, const char* szReadyState ); + void ResetReadyToGo(); + bool bPlayerReadyToGo( const char* szName ); + bool bAllPlayersReadyToGo(); + + bool bParamsUnfresh(); + void SendParams(); + bool AcceptParams( char* szParams ); + void SetGParamsToCurrent( GAMEPARAMS& GParams ); + + void AcceptNewGuestPlayerInfo( char* szMsg ); + + bool RequestPlayerColor( PlayerColorType Color ); + bool InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ); + bool InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ); + bool InformAboutPlayerAccept( const char* szName, User* pUserPriv ); + bool InformAboutStart(); + bool InformAboutCancelStart(); + + void ClearAllAccepts(); + bool bAllGuestsAccept(); + + PlayerColorType ColorNextAvailable(); + + void GuestIsReadyToPlay( const char* szName, const char* szReadyState ); + bool bNeedScenarioDownload(); + void HostSaysGo(); + void TriggerGameStart( char* szGoMessage ); + + enum SCENARIO_GAMEKIND + { + SCENARIO_RA = 0, + SCENARIO_CS, + SCENARIO_AM, + SCENARIO_USER, + SCENARIO_UNINITIALIZED, + }; + void ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ); + +// bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +public: + int d_dialog_w; + int d_dialog_h; + int d_dialog_x; + int d_dialog_y; + int d_dialog_cx; + + int d_txt6_h; + int d_margin1; + + int d_house_w; + int d_house_h; + int d_house_x; + int d_house_y; + + int d_color_w; + int d_color_h; + int d_color_x; + int d_color_y; + + int d_playerlist_w; + int d_playerlist_h; + int d_playerlist_x; + int d_playerlist_y; + + int d_scenariolist_w; + int d_scenariolist_h; + int d_scenariolist_x; + int d_scenariolist_y; + + int d_gamekind_w; + int d_gamekind_h; + int d_gamekind_x; + int d_gamekind_y; + + int d_count_w; + int d_count_h; + int d_count_x; + int d_count_y; + + int d_level_w; + int d_level_h; + int d_level_x; + int d_level_y; + + int d_credits_w; + int d_credits_h; + int d_credits_x; + int d_credits_y; + + int d_aiplayers_w; + int d_aiplayers_h; + int d_aiplayers_x; + int d_aiplayers_y; + + int d_options_w; + int d_options_h; + int d_options_x; + int d_options_y; + + int d_disc_w; + int d_disc_h; + int d_disc_x; + int d_disc_y; + + int d_send_w; + int d_send_h; + int d_send_x; + int d_send_y; + + int d_ok_w; + int d_ok_h; + int d_ok_x; + int d_ok_y; + + int d_cancel_w; + int d_cancel_h; + int d_cancel_x; + int d_cancel_y; + + int d_accept_w; + int d_accept_h; + int d_accept_x; + int d_accept_y; + + int d_amunits_w; + int d_amunits_h; + int d_amunits_x; + int d_amunits_y; + + int d_action_w; + int d_action_h; + int d_action_x; + int d_action_y; + +protected: + GadgetClass* commands; // The controls list. + + IconListClass* pILPlayers; + IconListClass* pILScens; + IconListClass* pILDisc; + char szSendBuffer[ MAXCHATSENDLENGTH ]; + EditClass* pEditSend; + GaugeClass* pGaugeCount; + GaugeClass* pGaugeLevel; + GaugeClass* pGaugeCredits; + GaugeClass* pGaugeAIPlayers; + CheckListClass* pCheckListOptions; + TextButtonClass* pTextBtnOk; + TextButtonClass* pTextBtnCancel; + TextButtonClass* pTextBtnAcceptStart; + TextButtonClass* pTextBtnAction; + StaticButtonClass* pStaticDescrip; + StaticButtonClass* pStaticUnit; + StaticButtonClass* pStaticLevel; + StaticButtonClass* pStaticCredits; + StaticButtonClass* pStaticAIPlayers; + char szHouseBuffer[25]; // buffer for house droplist + DropListClass* pDropListHouse; + BigCheckBoxClass* pCheckAftermathUnits; + ShapeButtonClass* pShpBtnScenarioRA; + ShapeButtonClass* pShpBtnScenarioCS; + ShapeButtonClass* pShpBtnScenarioAM; + ShapeButtonClass* pShpBtnScenarioUser; + + ToolTipClass* pTTipAcceptStart; + ToolTipClass* pTTipCancel; + ToolTipClass* pTTipAction; + + WolapiObject* pWO; + + + GAMEPARAMS GParamsLastSent; // Used merely as a handy container for the vars I need to set. + + DWORD dwTimeNextParamRefresh; // Param changes are sent by host at certain interval. + + HousesType HousePrevious; + + unsigned int nHostLastParamID; // Host's send update tracking packet ID. + unsigned int nGuestLastParamID; // Guest's record of last ID received from host. + + bool bWaitingToStart; + + bool bProcess; // True means continue doing input loop. + RESULT_WOLGSUP ResultReturn; // Value that will be returned from Show(). + char szNameOfHostWhoJustBailedOnUs[ WOL_NAME_LEN_MAX ]; // If set, triggers setup cancellation. + + bool bParamsReceived; // True after any WOL_GAMEOPT_INFPARAMS messages have been received from a host. + + bool bLeaveDueToRulesMismatchTrigger; + + bool bRequestedScenarioDownload; + + char szTriggerGameStartInfo[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 60 ]; + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast; // ToolTip the mouse was last over, or null. + + // Extra game params... + bool bAftermathUnits; // True if aftermath units are to be used in the game. + bool bSlowUnitBuildRate; + + SCENARIO_GAMEKIND ScenKindCurrent; // Describes what gamekind of scenarios we are viewing, if host. + DynamicVectorClass< const char* > ar_szScenarios[ 4 ]; // Lists of scenarios, by SCENARIO_GAMEKIND. + // ar_szScenIndexes parallels ar_szScenarios, holds ScenarioIndex. It's actually an int, but I'm using void* to avoid + // template instantiation problems and the need to change defines.h. + DynamicVectorClass< void* > ar_szScenIndexes[ 4 ]; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_PLAYERLIST, + BUTTON_HOUSE, + BUTTON_SCENARIOLIST, + BUTTON_DISCLIST, + BUTTON_SENDEDIT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_PARAMS, +// BUTTON_OK, + BUTTON_CANCEL, + BUTTON_ACCEPTSTART, + BUTTON_ACTION, + BUTTON_AFTERMATHUNITS, + BUTTON_SCENARIO_RA, + BUTTON_SCENARIO_CS, + BUTTON_SCENARIO_AM, + BUTTON_SCENARIO_USER, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + RedrawType display; + +}; + +#endif + diff --git a/CODE/WOL_LOGN.CPP b/CODE/WOL_LOGN.CPP new file mode 100644 index 0000000..a0930ce --- /dev/null +++ b/CODE/WOL_LOGN.CPP @@ -0,0 +1,665 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// Wol_Logn.cpp - WW online name/password dialog. +// ajw 07/16/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "PassEdit.h" +#include "WolStrng.h" +#include "BigCheck.h" + +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ); +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ); +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ); +//char* LoadShpFile( const char* szShpFile ); + +void DebugChatDef( HRESULT hRes ); + +extern bool bTabKeyPressedHack; + +//#include "WolDebug.h" + +//*********************************************************************************************** +int WOL_Login_Dialog( WolapiObject* pWO ) +{ + // Return values: 0 = user cancels, 1 = success, -1 = force game exit + + if( pWO->bLoggedIn() ) + { + pWO->bReturningAfterGame = true; // Set trigger for chat dialog. + return 1; // We are already logged in, and have just come back from a game. + } + + /* + ** Dialog & button dimensions + */ +#ifdef FRENCH + int d_dialog_w = 160 * RESFACTOR; // dialog width +#else + int d_dialog_w = 150 * RESFACTOR; // dialog width +#endif + int d_dialog_h = 85 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((255 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_name_w = 66 * RESFACTOR; + int d_name_h = 10 * RESFACTOR; +#ifdef FRENCH + int d_name_x = d_dialog_x + 25 * RESFACTOR; +#else + int d_name_x = d_dialog_x + 20 * RESFACTOR; +#endif + int d_name_y = d_dialog_y + top_margin + 25 * RESFACTOR; + + int d_pass_w = 36 * RESFACTOR; + int d_pass_h = d_name_h; + int d_pass_x = d_name_x + d_name_w + 6 * RESFACTOR; + int d_pass_y = d_name_y; + + int d_list_w = d_name_w; + int d_list_h = 20 * RESFACTOR; + int d_list_x = d_name_x; + int d_list_y = d_dialog_y + top_margin + 40 * RESFACTOR; + +// int d_save_w = d_pass_w; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_pass_x + ( d_pass_w / 2 ) - ( d_pass_w / 2 ); + int d_save_y = d_list_y; // + ( d_list_h / 2 ) - ( d_save_h / 2 ); + + int d_delete_w = d_pass_w; + int d_delete_h = 10 * RESFACTOR; + int d_delete_x = d_save_x; + int d_delete_y = d_list_y + d_list_h - d_delete_h; + +#ifdef FRENCH + int d_connect_w = 45 * RESFACTOR; +#else + int d_connect_w = 40 * RESFACTOR; +#endif + int d_connect_h = 13 * RESFACTOR; + int d_connect_x = d_name_x + d_name_w/2 - d_connect_w/2; + int d_connect_y = d_dialog_y + top_margin + 65 * RESFACTOR; //d_dialog_y + d_dialog_h - d_connect_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 40 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_pass_x + d_pass_w/2 - d_cancel_w/2; //d_dialog_cx + d_margin; + int d_cancel_y = d_connect_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_CONNECT = 100, + BUTTON_CANCEL, + LISTBOX_NICKS, + EDITBOX_NAME, + EDITBOX_PASS, + BUTTON_SAVECHECK, + BUTTON_DELETE, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + int iReturn = 1; // 0 = user cancels, 1 = success, -1 = force game exit + + /* + ** Other Variables + */ + char szNameBuffer[ WOL_NAME_LEN_MAX ] = {0}; // User name. + char szPassBuffer[ WOL_PASSWORD_LEN ] = {0}; // User password. + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass ConnectBtn( BUTTON_CONNECT, TXT_WOL_CONNECT, TPF_BUTTON, d_connect_x, d_connect_y, d_connect_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + IconListClass NickList( LISTBOX_NICKS, d_list_x, d_list_y, d_list_w, d_list_h, TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1, 0 ); + + WOLEditClass NameEdit( EDITBOX_NAME, szNameBuffer, sizeof(szNameBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, -1, EditClass::ALPHANUMERIC ); + + PassEditClass PassEdit( EDITBOX_PASS, szPassBuffer, sizeof(szPassBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_pass_x, d_pass_y, d_pass_w, -1, EditClass::ALPHANUMERIC ); + + // Just making sure globals are set right before String_Pixel_Width() call... sigh + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW ); + int iSaveTextWidth = String_Pixel_Width( TXT_WOL_SAVELOGIN ) + BIGCHECK_OFFSETX; + BigCheckBoxClass SaveCheckBox( BUTTON_SAVECHECK, d_save_x, d_save_y, iSaveTextWidth, d_save_h, + TXT_WOL_SAVELOGIN, TPF_6PT_GRAD | TPF_NOSHADOW, true ); + + TextButtonClass DeleteBtn( BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + // Get saved nickname/passwords from the registry. + if( ReadSavedNicks( pWO, NickList, szNameBuffer, szPassBuffer ) ) + { + PassEdit.bClearOnNextSetFocus = true; + } + else + { + // Offer user the chance to go to web site now to get a nick. + if( pWO->DoWebRegistration() ) + { + // User chose to go to web page. Leave function so that we'll re-read nicks when they return. + return 0; + } + } + + /* + ** Create the button list. + */ + commands = &ConnectBtn; + CancelBtn.Add_Tail(*commands); + NickList.Add_Tail(*commands); + NameEdit.Add_Tail(*commands); + PassEdit.Add_Tail(*commands); + SaveCheckBox.Add_Tail(*commands); + DeleteBtn.Add_Tail(*commands); + NameEdit.Set_Focus(); + + if( NickList.Count() == 0 ) + DeleteBtn.Disable(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + //------------------------------------------------------------------------ + // Clear screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); +// Show_Mouse(); + + /* + ** Display the dialog box. + */ +// Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_WOL_LOGINDIALOG, d_dialog_x, d_dialog_y, d_dialog_w); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( TXT_WOL_NAME, d_name_x + ( d_name_w / 2 ), d_name_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + Fancy_Text_Print( TXT_WOL_PASSWORD, d_pass_x + ( d_pass_w / 2 ), d_pass_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + bTabKeyPressedHack = false; + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + } + +// /* +// ** If the key was pressed, then default to the appropriate +// ** action button according to the style of this dialog box. +// */ +/* if (input == KN_RETURN || input == (BUTTON_CONNECT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + input = (KeyNumType)(BUTTON_CONNECT|KN_BUTTON); + CancelBtn.Turn_Off(); + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_CONNECT); + if (toggle != NULL) { + toggle->Turn_On(); + toggle->IsPressed = true; + } + + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } +*/ + /* + ** Process input. + */ +// if( input ) +// debugprint( "input: %i\n", input ); + + if( bTabKeyPressedHack ) + { + if( NameEdit.Has_Focus() ) + PassEdit.Set_Focus(); + else + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + } + + switch( input ) + { + /* + ** ESC/Cancel: break + */ + case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + iReturn = 0; + process = false; + break; + + case KN_RETURN: + case ( EDITBOX_NAME | KN_BUTTON ): + case ( EDITBOX_PASS | KN_BUTTON ): + case ( BUTTON_CONNECT | KN_BUTTON ): + { + if( !strlen( szNameBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGNAME ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + if( !strlen( szPassBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGPASSWORD ); + firsttime = true; // Bloody hack. + PassEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + + // If we have not done RequestServerList() yet, do it now. + if( !pWO->pChatSink->pServer ) + { + bool bBreak = false; + HRESULT hRes = pWO->GetChatServer(); + switch( hRes ) + { + case E_FAIL: + bBreak = true; + WWMessageBox().Process( TXT_WOL_CANTCONNECT ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case USERCANCELLED: + bBreak = true; + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHAVOIDED: + bBreak = true; + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHDOWNLOADED: + bBreak = true; + process = false; + iReturn = -1; + break; + } + if( bBreak ) + break; + } + + // RequestConnection()... + HRESULT hRes = pWO->AttemptLogin( szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ); + if( hRes == S_OK ) + { + if( SaveCheckBox.IsOn && !bSaveNick( pWO, szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ) ) + { + // Nick/pass save failed. + WWMessageBox().Process( TXT_WOL_CANTSAVENICK ); + } + process = false; + } + else + { + switch( hRes ) + { + case USERCANCELLED: + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case CHAT_E_BADPASS: + WWMessageBox().Process( TXT_WOL_BADPASS ); + break; + case CHAT_E_NICKINUSE: + WWMessageBox().Process( TXT_WOL_NICKINUSE ); + break; + case CHAT_E_CON_ERROR: + // This error value I pass back myself, when the emergency timeout is hit. + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + } + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + } + break; + } + +/* + case( EDITBOX_PASS | KN_BUTTON ): + { + // Message with delay so that user has time to read it... + CDTimerClass timer; + timer = TICKS_PER_SECOND*4; + WWMessageBox().Process(TXT_WOL_DEBUG2, TXT_NONE); + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + display = true; + break; + } +*/ + case ( LISTBOX_NICKS | KN_BUTTON ): + strcpy( szNameBuffer, NickList.Get_Item( NickList.Current_Index() ) ); + strcpy( szPassBuffer, NickList.Get_Item_ExtraDataString( NickList.Current_Index() ) ); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + // Because the password is mangled, if the user begins to edit it now, we clear it. + // Otherwise we could get a half-mangled, half-unmangled password field. + // PassEdit.bClearOnNextSetFocus also acts as a flag telling us whether or not the + // password field is mangled or not. + PassEdit.bClearOnNextSetFocus = true; +// display = true; + break; + + case ( BUTTON_SAVECHECK | KN_BUTTON ): + break; + + case ( BUTTON_DELETE | KN_BUTTON ): + if( NickList.Count() > 0 ) + { + DeleteNick( pWO, NickList.Current_Index() + 1 ); + NickList.Remove_Item( NickList.Current_Index() ); + NickList.Flag_To_Redraw(); + if( NickList.Count() == 0 ) + { + DeleteBtn.Disable(); + DeleteBtn.Flag_To_Redraw(); + } + } + break; + + default: + break; + } + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ) +{ + // Read saved nickname/passwords from the registry. + // Set up the list of nick/passwords. + // Copy the first nick into the nick/password edits. + + // Returns true if edits are set with a default nick/pass because a nick was found. + + LPCSTR szNick; + LPCSTR szPass; + bool bReturn = false; + + for( int i = 1; i != 3; i++ ) + { + if( pWO->pChat->GetNick( i, &szNick, &szPass ) == S_OK ) + { + if( *szNick ) + { + NickList.Add_Item( szNick, NULL, NULL, ICON_SHAPE, szPass ); + if( i == 1 ) + { + strcpy( szNameBuffer, szNick ); + strcpy( szPassBuffer, szPass ); + bReturn = true; + } + } + } + } + return bReturn; +} + +//*********************************************************************************************** +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ) +{ + // Saves specified nick and password in the registry, using SetNick. + // Returns false if nick can't be saved. + + // If slot 1 empty, use slot 1. + // Else push nick 1 down to second slot and save new nick in slot 1, unless + // nick 1 name matches new entry. + + LPCSTR szNick; + LPCSTR szPass; + bool bPushSlot1 = true; + + switch( pWO->pChat->GetNick( 1, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bPushSlot1 = false; + break; + case S_OK: + if( *szNick == 0 ) + bPushSlot1 = false; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bPushSlot1 = false; // We can use this slot as the name is the same. + break; + } + + if( bPushSlot1 ) + { + // Move nick in slot 1 to slot 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + } + + // Save new nick in slot 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + +/* + int iSlot; + bool bStop = false; + for( iSlot = 1; iSlot != 3; iSlot++ ) + { + switch( pWO->pChat->GetNick( iSlot, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bStop = true; + break; + case S_OK: + if( *szNick == 0 ) + bStop = true; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bStop = true; // We can use this slot as the name is the same. + break; + } + if( bStop ) + break; + } + if( iSlot == 3 ) + { + // No open slots were found. + // Get nick 1. + pWO->pChat->GetNick( 1, &szNick, &szPass ); + // Save as nick 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + // Save new nick 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } + else + { + // iSlot points to an open slot. + return ( pWO->pChat->SetNick( iSlot, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } +*/ +} + +//*********************************************************************************************** +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ) +{ + // Delete a nick from the registry via wolapi SetNick. + // If nick to delete is in position one, and there is a second nick, move the second nick into position one. + if( iOneBasedEntryToDelete == 1 ) + { + // Check for nick 2. + LPCSTR szNick; + LPCSTR szPass; + if( pWO->pChat->GetNick( 2, &szNick, &szPass ) == S_OK && *szNick != 0 ) + { + // Copy nick in slot 2 to slot 1. + pWO->pChat->SetNick( 1, szNick, szPass, false ); // (Already mangled.) + // Delete slot 2. + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } + else + { + // No second nick. + HRESULT hRes = pWO->pChat->SetNick( 1, "", "", false ); + DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } +} + +/* +//*********************************************************************************************** +char* LoadShpFile( const char* szShpFile ) +{ + // Returns pointer to shp data that has been new'ed (and must be delete[]d), or NULL if failure. + // ajw: No longer needed - I used this before putting new resources into a mix file. + HANDLE hFile; + hFile = CreateFile( szShpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + DWORD dwFileSize = GetFileSize( hFile, NULL ); + char* pShp = new char[ dwFileSize ]; + DWORD dwBytesRead; + ReadFile( hFile, pShp, dwFileSize, &dwBytesRead, NULL ); +// debugprint( "~~ LoadShpFile() - Read %i bytes out of %i from shp file.\n", dwBytesRead, dwFileSize ); + CloseHandle( hFile ); + return pShp; +} +*/ + +#endif diff --git a/CODE/WOL_MAIN.CPP b/CODE/WOL_MAIN.CPP new file mode 100644 index 0000000..dd08d09 --- /dev/null +++ b/CODE/WOL_MAIN.CPP @@ -0,0 +1,287 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// Wol_Main.cpp - Bottom level wolapi-stuff function. +// ajw 07/16/98 + +#include "function.h" +#include "WolapiOb.h" +#include "wol_gsup.h" +#include "WolStrng.h" + +int WOL_Login_Dialog( WolapiObject* pWolapi ); +int WOL_Chat_Dialog( WolapiObject* pWolapi ); +const char* Game_Registry_Key(); + +bool ReregisterWolapiDLL(); +void HandleDLLFail(); + +WolapiObject* pWolapi = NULL; + +#include "WolDebug.h" + +//*********************************************************************************************** +// The first time through, pWolapi is NULL thus wolapi gets set up. WOL_Login_Dialog presents the user +// with the login dialog and attempts to log us on to the server. If the user continues on all the +// way to a game start, we will drop out of here with pWolapi still pointing to a valid WolapiObject, +// and with pWolapi's iLobbyReturnAfterGame set to the number of the lobby to return to automatically +// after the game ends. +// Init() automatically brings us here if pWolapi is non-null. +//*********************************************************************************************** +int WOL_Main() +{ + // Return values: + // 0 = cancel + // 1 = start game + // -1 = patch downloaded, shut down app + int iReturn = 0; + + if( pWolapi ) + { + // We have returned from a game started through ww online. + + // Start theme up again. + Theme.Play_Song( THEME_INTRO ); + + // Verify that we are still connected. If we aren't, kill WolapiObject and start over. + // (This will likely occur during the game, if connection is lost. Ensure that it is done here.) + pWolapi->pChat->PumpMessages(); // Causes OnNetStatus() call if no longer connected. + if( pWolapi->bConnectionDown ) + { +//debugprint( "Re-entering WOL_Main(), pWolapi->bConnectionDown is true. Deleting old WolapiObject...\n" ); + WWMessageBox().Process( TXT_WOL_WOLAPIREINIT ); + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + } + + if( !pWolapi ) + { + // Start up wolapi. + pWolapi = new WolapiObject; + if( !pWolapi->bSetupCOMStuff() ) + { + // Things are really bad if this happens. A COM call failed. + + // We first assume that their wolapi.dll failed to register during wolsetup.exe, part of the patch process. + // This happens if they have an outdated oleaut32.dll, such as the one that comes with original + // version of Windows 95. + +// debugprint( "bSetupCOMStuff failed. Attemping to reregister wolapi.dll...\n" ); + // Attempt to re-register wolapi.dll... + if( ReregisterWolapiDLL() ) + { + if( !pWolapi->bSetupCOMStuff() ) + { + // Still failed after reregistering seemed to work. + HandleDLLFail(); + return 0; + } + } + else + { + HandleDLLFail(); + return 0; + } + } + pWolapi->PrepareButtonsAndIcons(); + // Undocumented hack needed for patch downloading, per Neal. + pWolapi->pChat->SetAttributeValue( "RegPath", Game_Registry_Key() ); + // (Not that anything's really "documented".) + } + + pWolapi->bInGame = false; + + int iLoginResult = WOL_Login_Dialog( pWolapi ); + if( iLoginResult == 1 ) + { + pWolapi->SetOptionDefaults(); + bool bKeepGoing = true; + while( bKeepGoing ) + { + bool bCreator; + switch( WOL_Chat_Dialog( pWolapi ) ) + { + case -1: + bKeepGoing = false; + break; + case 1: + // User created game channel. + bCreator = true; + break; + case 2: + // User joined game channel. + bCreator = false; + break; + } + if( bKeepGoing ) + { + WOL_GameSetupDialog GSupDlg( pWolapi, bCreator ); + switch( GSupDlg.Run() ) + { + case RESULT_WOLGSUP_LOGOUT: + // User logged out. + bKeepGoing = false; + break; + case RESULT_WOLGSUP_BACKTOCHAT: + case RESULT_WOLGSUP_HOSTLEFT: + case RESULT_WOLGSUP_RULESMISMATCH: + // Return to chat. + break; + case RESULT_WOLGSUP_STARTGAMEHOST: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = true; + break; + case RESULT_WOLGSUP_STARTGAME: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = false; + break; + case RESULT_WOLGSUP_FATALERROR: +// debugprint( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); +// Fatal( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); + if( pWolapi->pChatSink->bConnected ) + pWolapi->Logout(); + bKeepGoing = false; + break; + } + } + } + } + + if( iReturn != 1 ) + { + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + else + { + pWolapi->bInGame = true; + pWolapi->bConnectionDown = false; + } + + if( iLoginResult == -1 ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADEXITWARNING ); + iReturn = -1; + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReregisterWolapiDLL() +{ + // Attempt to reregister wolapi.dll. + // Returns true if we think we succeeded. + HKEY hKey; + char szInstallPath[ _MAX_PATH ]; + if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Westwood\\WOLAPI", 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwBufSize = _MAX_PATH; + if( ::RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szInstallPath, &dwBufSize ) == ERROR_SUCCESS ) + { + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szInstallPath, &wfd ); + if( handle == INVALID_HANDLE_VALUE ) + { + // File is not there. + FindClose( handle ); + ::RegCloseKey( hKey ); + return false; + } +// debugprint( "Found dll -> %s\n", szInstallPath ); + // Get the DLL to register itself. + HINSTANCE hLib = LoadLibrary( szInstallPath ); + if( !hLib ) + { +// debugprint( "LoadLibrary failed, GetLastError is %i\n", GetLastError() ); + ::RegCloseKey( hKey ); + return false; + } + FARPROC lpDllRegisterFunction = GetProcAddress( hLib, "DllRegisterServer" ); + if( !lpDllRegisterFunction ) + { + ::RegCloseKey( hKey ); + return false; + } + if( lpDllRegisterFunction() != S_OK ) + { + ::RegCloseKey( hKey ); + return false; + } + // There is a bug in wolapi.dll that makes the following delay necessary. + // Something about Neal's extra threads only getting half-way set up before they get deleted. + // (The extra threads shouldn't really be created in this case, anyway...) + ::Sleep( 1000 ); + FreeLibrary( hLib ); + FindClose( handle ); + } + else + { + ::RegCloseKey( hKey ); + return false; + } + ::RegCloseKey( hKey ); + } + else + { + return false; + } + return true; +} + +//*********************************************************************************************** +void HandleDLLFail() +{ + // The DLL failed to load. Either we failed to reregister it, or we think we succeeded at this but it + // still is not working. Show an error message and delete pWolapi. + // We show either "call tech support" or "download IE3", depending on whether oleaut32.dll looks out of date. + + char szPath[ _MAX_PATH + 1 ]; + ::GetSystemDirectory( szPath, _MAX_PATH ); + if( *szPath && szPath[ strlen( szPath ) - 1 ] != '\\' ) + strcat( szPath, "\\" ); + + strcat( szPath, "oleaut32.dll" ); + + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szPath, &wfd ); + +// debugprint( "HandleDLLFail(): filesize of oleaut32 is %i\n", wfd.nFileSizeLow ); + if( handle != INVALID_HANDLE_VALUE && wfd.nFileSizeLow <= 232720 ) + WWMessageBox().Process( TXT_WOL_DLLERROR_GETIE3 ); + else + WWMessageBox().Process( TXT_WOL_DLLERROR_CALLUS ); + + FindClose( handle ); + + delete pWolapi; + pWolapi = NULL; +} + +#endif diff --git a/CODE/WOL_OPT.CPP b/CODE/WOL_OPT.CPP new file mode 100644 index 0000000..8538b57 --- /dev/null +++ b/CODE/WOL_OPT.CPP @@ -0,0 +1,257 @@ +/* +** 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 . +*/ + +#ifdef WOLAPI_INTEGRATION + +// Wol_Opt.cpp - WW online options dialog. +// ajw 09/1/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "BigCheck.h" +//#include "WolDebug.h" + +extern bool cancel_current_msgbox; + +//*********************************************************************************************** +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ) +{ + // Returns true only if called from inside game, and the game ended on us unexpectedly. + bool bReturn = false; + + bool bEscapeDown = false; + bool bReturnDown = false; + + bool bIgnoreReturnDown = false; + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + // The return key is already down, as we enter the dialog. + // Until it comes up again, ignore this fact, so that we don't act on a return press that's not valid. + bIgnoreReturnDown = true; + } + + /* + ** Dialog & button dimensions + */ +#ifdef GERMAN + int d_list_w = 180 * RESFACTOR; +#else +#ifdef FRENCH + int d_list_w = 165 * RESFACTOR; +#else + int d_list_w = 165 * RESFACTOR; +#endif +#endif + + int d_dialog_w = d_list_w + 40 * RESFACTOR; // dialog width + int d_dialog_h = 90 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + +// int d_list_w = 100 * RESFACTOR; + int d_list_h = 7 * RESFACTOR; + int d_list_x = d_dialog_cx - d_list_w / 2; + int d_list_y = d_dialog_y + d_margin + 24; + +#if (GERMAN | FRENCH) + int d_ok_w = 40 * RESFACTOR; +#else + int d_ok_w = 40 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_cx - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + CHECK_FIND, + CHECK_PAGE, + CHECK_LANGUAGE, + CHECK_ALLGAMES, + CHECK_RANKAM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + + BigCheckBoxClass FindCheck( CHECK_FIND, d_list_x, d_list_y, d_list_w, d_list_h, + TXT_WOL_OPTFIND, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bFindEnabled ); + BigCheckBoxClass PageCheck( CHECK_PAGE, d_list_x, d_list_y + d_list_h + 2, d_list_w, d_list_h, + TXT_WOL_OPTPAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bPageEnabled ); + BigCheckBoxClass LanguageCheck( CHECK_LANGUAGE, d_list_x, d_list_y + 2 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTLANGUAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bLangFilter ); + BigCheckBoxClass GamescopeCheck( CHECK_ALLGAMES, d_list_x, d_list_y + 3 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTGAMESCOPE, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bAllGamesShown ); + BigCheckBoxClass RankAMCheck( CHECK_RANKAM, d_list_x, d_list_y + 4 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTRANKAM, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bShowRankRA ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + FindCheck.Add_Tail(*commands); + PageCheck.Add_Tail(*commands); + LanguageCheck.Add_Tail(*commands); + GamescopeCheck.Add_Tail(*commands); + RankAMCheck.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if( !bCalledFromGame ) + Call_Back(); + else + { + if( Main_Loop() ) // Game ended on us in the background. + { + process = false; + bReturn = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_OPTTITLE, d_dialog_x, d_dialog_y, d_dialog_w ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + if( !bIgnoreReturnDown ) + bReturnDown = true; + } + else + { + bIgnoreReturnDown = false; + if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + } + + /* + ** Process input. + */ + + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + } + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + process = false; + break; + + case ( CHECK_FIND | KN_BUTTON ): + case ( CHECK_PAGE | KN_BUTTON ): + case ( CHECK_LANGUAGE | KN_BUTTON ): + case ( CHECK_ALLGAMES | KN_BUTTON ): + pWO->SetOptions( FindCheck.IsOn, PageCheck.IsOn, LanguageCheck.IsOn, !GamescopeCheck.IsOn ); + break; + + case ( CHECK_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = !RankAMCheck.IsOn; + pWO->bMyRecordUpdated = true; + pWO->bShowRankUpdated = true; + break; + + default: + break; + } + } + return bReturn; +} + +#endif diff --git a/CODE/WRITEPCX.CPP b/CODE/WRITEPCX.CPP new file mode 100644 index 0000000..55c3f43 --- /dev/null +++ b/CODE/WRITEPCX.CPP @@ -0,0 +1,228 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WRITEPCX.CPP 1 3/03/97 10:26a 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 : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include "function.h" +#include "pcx.h" + + +static void Write_Pcx_ScanLine(FileClass & file, int scansize, char * ptr); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the format [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memory block holding the color * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + * 06/03/1996 JLB : Converted to C++ and file class I/O. * + *=========================================================================*/ + +static const unsigned char rle_code = 0xC0; // Run code. +static const unsigned char rle_max_run = 0x2F; // Maximum run allowed. +static const unsigned char rle_full_run = (rle_max_run|rle_code); // Full character run. + + +/*********************************************************************************************** + * Write_PCX_File -- Write a PCX file from specified buffer. * + * * + * This routine will take the specified buffer and write out the data as a PCX file to the * + * the file object specified. * + * * + * INPUT: file -- Reference to the file object to write the buffer as a PCX file. * + * * + * pic -- Reference to a graphic buffer that contains the data to be written. * + * * + * palette -- Reference to the palette to be attached to the PCX file as well. * + * * + * OUTPUT: bool; Was there an error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +int Write_PCX_File(FileClass & file, GraphicBufferClass & pic, PaletteClass * palette) +{ + unsigned char palcopy[256 * sizeof(RGB)]; + int VP_Scan_Line; + char * ptr; + RGB * pal; + PCX_HEADER header = { + 10, + 5, + 1, + 8, + 0, + 0, + (short)(pic.Get_Width()-1), + (short)(pic.Get_Height()-1), + (short)(pic.Get_Width()), + (short)(pic.Get_Height()), + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + 0, + 1, + (short)pic.Get_Width(), + 1, + {0} + }; + + /* + ** Open the output file and write out the header information. If the file + ** is already open, then just presume that it is positioned correctly and is + ** open for write. + */ + bool open = false; + if (!file.Is_Open()) { + file.Open(WRITE); + open = true; + } + file.Write(&header, sizeof(header)); + + /* + ** Write out the picture, line by line. + */ + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + ptr = (char *)pic.Get_Buffer() ; + ptr += ((pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos()); + for (int line = 0; line < header.height + 1; line++) { + Write_Pcx_ScanLine(file, header.byte_per_line, ptr + line * VP_Scan_Line ) ; + } + + /* + ** Special marker for end of RLE data. + */ + unsigned char ender = 0x0C; + file.Write(&ender, sizeof(ender)); + + /* + ** Convert the palette from 6 bit to 8 bit format. + */ + memmove(palcopy, palette, sizeof(PaletteClass)); + pal = (RGB *)palcopy ; + for (int palindex = 0; palindex < 256; palindex++) { + pal->red = (unsigned char)((pal->red<<2)); // | (pal->red>>6)); + pal->green = (unsigned char)((pal->green<<2)); // | (pal->green>>6)); + pal->blue = (unsigned char)((pal->blue<<2)); // | (pal->blue>>6)); + pal++; + } + + /* + ** Write the palette out. + */ + file.Write(palcopy, sizeof(palcopy)); + + /* + ** Close the file (if necessary) and exit with no error flag. + */ + if (open) { + file.Close(); + } + return(false); +} + + +/*********************************************************************************************** + * Write_Pcx_ScanLine -- Writes a PCX scanline. * + * * + * Writes out a PCX scanline using RLE compression. * + * * + * INPUT: file -- Reference to the file to write the scan line to. * + * * + * scansize -- The number of bytes to compress (write). * + * * + * ptr -- Pointer to the data to compress (write). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 06/03/1996 JLB : Converted to C++ and file class I/O. * + *=============================================================================================*/ +static void Write_Pcx_ScanLine(FileClass & file, int scansize, char * ptr) +{ + unsigned char last = *ptr; + unsigned char rle=1; + unsigned char c; + for (int i = 1; i < scansize; i++) { + unsigned char color = (unsigned char)(0xff & * ++ptr); + if (color == last) { + rle++; + if (rle == rle_max_run) { + file.Write(&rle_full_run, sizeof(rle_full_run)); + file.Write(&color, sizeof(color)); + rle = 0 ; + } + } else { + if (rle) { + if (rle == 1 && (rle_code != (rle_code & last))) { + file.Write(&last, sizeof(last)); + } else { + c = (unsigned char)(rle | rle_code); + file.Write(&c, sizeof(c)); + file.Write(&last, sizeof(last)); + } + } + last = color ; + rle = 1 ; + } + } + if (rle) { + if (rle == 1 && ( rle_code != (rle_code & last))) { + file.Write(&last, sizeof(last)); + } else { + c = (unsigned char)(rle | rle_code); + file.Write(&c, sizeof(c)); + file.Write(&last, sizeof(last)); + } + } +} + diff --git a/CODE/WSPIPX.CPP b/CODE/WSPIPX.CPP new file mode 100644 index 0000000..b31a198 --- /dev/null +++ b/CODE/WSPIPX.CPP @@ -0,0 +1,449 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSPIPX.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 6 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * * + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wspipx.h" +#include "ipxaddr.h" + +#include +#include + + +/*********************************************************************************************** + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 11:41AM ST : Created * + *=============================================================================================*/ +IPXInterfaceClass::IPXInterfaceClass (void) : WinsockInterfaceClass() +{ + /* + ** Set the net and node addressed to their default values. + */ + memset ( BroadcastNet, 0xff, sizeof (BroadcastNet) ); + memset ( BroadcastNode, 0xff, sizeof (BroadcastNode) ); + memset ( MyNode, 0xff, sizeof (MyNode) ); +} + + +/*********************************************************************************************** + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * * + * * + * * + * INPUT: card number to retrieve ID for * + * ptr to addr to return ID in * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/1/97 3:04PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr) +{ + int cbOpt; + int cbAddr = sizeof( SOCKADDR_IPX ); + SOCKET s; + SOCKADDR_IPX Addr; + IPX_ADDRESS_DATA IpxData; + + /* + ** Create a temporary IPX socket. + */ + s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ); + if ( s == SOCKET_ERROR ) { + assert ( s != SOCKET_ERROR ); + return (false); + } + + /* + ** Socket must be bound prior to calling IPX_MAX_ADAPTER_NUM + */ + memset( &Addr, 0, sizeof( Addr )); + Addr.sa_family = AF_IPX; + int err = bind( s, (SOCKADDR*) &Addr, cbAddr); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + memset( &IpxData, 0, sizeof(IpxData)); + + /* + ** Specify which adapter to check. + */ + IpxData.adapternum = card_number; + cbOpt = sizeof( IpxData ); + + /* + ** Get information for the current adapter. + */ + err = getsockopt( s, NSPROTO_IPX, IPX_ADDRESS, (char*) &IpxData, &cbOpt ); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + /* + ** IpxData contains the address for the current adapter. + ** The network number will be needed later for broadcasts as the net number ff,ff,ff,ff + ** doesn't work under NT. + ** + ** Note: Due to a bug in Win95s implementation of Winsock, only the netnum & nodenum + ** values are correctly returned. NT returns all expected values. ST - 7/31/97 0:57AM + */ + memcpy (addr->sa_netnum, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (BroadcastNet, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (addr->sa_nodenum, IpxData.nodenum, sizeof (addr->sa_nodenum)); + + closesocket (s); + return (true); +} + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * * + * * + * * + * INPUT: SOCKET number to open. This is usually VIRGIN_SOCKET * + * * + * OUTPUT: true if socket was opened without error * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:54PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Open_Socket( SOCKET socketnum ) +{ + SOCKADDR_IPX addr; + bool delay = true; + int err; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + IPXSocketNumber = socketnum; + + /* + ** Set up the addr structure for the IPX socket + */ + addr.sa_family = AF_IPX; + memset (addr.sa_netnum, 0, sizeof (addr.sa_netnum)); + memset (addr.sa_nodenum, -1, sizeof (addr.sa_nodenum)); + addr.sa_socket = htons ( socketnum ); + + /* + ** Create the socket. + */ + Socket = socket (AF_NS, SOCK_DGRAM, NSPROTO_IPX); + if (Socket == INVALID_SOCKET) { + char out[128]; + sprintf (out, "TS: Failed to create IPX socket - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( Socket != INVALID_SOCKET ); + closesocket(Socket); + return ( false ); + } + + /* + ** Get the network card address. This is needed so we can bind the socket to the net card. + */ + if ( !Get_Network_Card_Address (0, &addr)){ + closesocket ( Socket ); + return ( false ); + } + + /* + ** Bind the IPX socket to the network card. + */ + if (bind ( Socket, (const struct sockaddr *) &addr, 16) == SOCKET_ERROR ){ + char out[128]; + sprintf (out, "TS: IPX socket bind failed with error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + closesocket(Socket); + return ( false );; + } + + + /* + ** Set the various options for this IPX socket + */ + unsigned long optval = true; + int packet_type = 4; + + /* + ** The SO_BROADCAST option allows broadcasting on this socket. This shouldn't be needed + ** except for the bug in the Win95 implementation of Winsock which causes broadcasts to + ** fail if it isn't set. + */ + if ( setsockopt ( Socket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval) ) == SOCKET_ERROR ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_BROADCAST - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + } + + /* + ** Set the value in the packet type field for outgoing packets. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_PTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_PTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Ignore all incoming packets not of this type. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_FILTERPTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_FILTERTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Set the the base class socket options for buffer sizes. + */ + WinsockInterfaceClass::Set_Socket_Options(); + + /* + ** Woohoo! + */ + return ( true ); +} + + + + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * * + * * + * * + * INPUT: Usual windoze message handler stuff * + * * + * OUTPUT: 0 if message was handled * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:55PM ST : Created * + *=============================================================================================*/ +long IPXInterfaceClass::Message_Handler(HWND , UINT message, UINT , LONG lParam) +{ + + int addr_len; // Length of address structure + int rc; // Result code + SOCKADDR_IPX addr; // Winsock IPX addressing structure + WinsockBufferType *packet; // Ptr to packet + NetNumType netnum; + NetNodeType nodenum; + + + /* + ** We only handle IPX events. + */ + if ( message != WM_IPXASYNCEVENT ) return ( 1 ); + + + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return(0); + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*) ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len ); + if (rc == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + } + return(0); + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make a copy of the address that this packet came from. + */ + memcpy ( netnum, addr.sa_netnum, sizeof (netnum) ); + memcpy ( nodenum, addr.sa_nodenum, sizeof (nodenum) ); + + /* + ** If this packet was from me then ignore it. + */ + if ( !memcmp (netnum, BroadcastNet, sizeof (BroadcastNet)) && !memcmp(nodenum, MyNode, sizeof (MyNode)) ) { + return (0); + } + + /* + ** Create a new buffer and store this packet in it. + */ + packet = new WinsockBufferType; + packet->BufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Set_Address ( netnum, nodenum ); + InBuffers.Add ( packet ); + } + return(0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error ( Socket ); + return(0); + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + while ( OutBuffers.Count() != 0 ) { + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sa_family = AF_IPX; + addr.sa_socket = htons ( IPXSocketNumber ); + + /* + ** Set up the address as either a broadcast address or the given address + */ + if ( packet->IsBroadcast ) { + memcpy ( addr.sa_netnum, BroadcastNet, sizeof (BroadcastNet) ); + memcpy ( addr.sa_nodenum, BroadcastNode, sizeof (BroadcastNode) ); + }else{ + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Get_Address ( netnum, nodenum ); + memcpy ( addr.sa_netnum, netnum, sizeof (netnum) ); + memcpy ( addr.sa_nodenum, nodenum, sizeof (nodenum) ); + } + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + break; + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + } + + return(0); + } + + return (0); +} + + diff --git a/CODE/WSPIPX.H b/CODE/WSPIPX.H new file mode 100644 index 0000000..027a4e3 --- /dev/null +++ b/CODE/WSPIPX.H @@ -0,0 +1,106 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSPIPX.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPIPX_H +#define WSPIPX_H + +#include "WSProto.h" + +/* +** Include Windows specific extensions for Winsock that allow IPX over winsock 1.1 +*/ +#include + +/* +** This file normally resides with the SDK. However, since it needs fixing up before watcom will +** compile it, it has been incorporated into the project. +*/ +#include "wsnwlink.h" + +/* +** IPX interface class. This handles access to the IPX specific portions of the +** Winsock interface. +** +*/ +class IPXInterfaceClass : public WinsockInterfaceClass { + + public: + + IPXInterfaceClass (void); + //virtual ~IPXInterfaceClass(void){Close();}; + bool Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr); + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_IPX); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_IPXASYNCEVENT); + }; + + + private: + /* + ** The address of the network we will send broadcasts to. Normally you would expect + ** this to be ff,ff,ff,ff but this fails under NT 4.0. Instead, we can use the network + ** number of the net that this PC is attached to. This limits broadcasts to the current + ** network. + */ + unsigned char BroadcastNet[4]; + + /* + ** The node to use as a broadcast address. Normally ff,ff,ff,ff,ff,ff. + */ + unsigned char BroadcastNode[6]; + + /* + ** The id of the network cars in this machine. + */ + unsigned char MyNode[6]; + + /* + ** The socket number to connect with. Normally this will be virgins reserved socket + ** number - VIRGIN_SOCKET (0x8813). + */ + SOCKET IPXSocketNumber; + +}; + + + +#endif \ No newline at end of file diff --git a/CODE/WSPROTO.CPP b/CODE/WSPROTO.CPP new file mode 100644 index 0000000..f2e696c --- /dev/null +++ b/CODE/WSPROTO.CPP @@ -0,0 +1,594 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 5 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * WIC::Close -- Releases any currently in use Winsock resources. * + * WIC::Close_Socket -- Close the communication socket if its open * + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * WIC::Stop_Listening -- Disable the winsock event callback * + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * WIC::Init -- Initialised Winsock and this class for use. * + * WIC::Read -- read any pending input from the communications socket * + * WIC::WriteTo -- Send data via the Winsock socket * + * WIC::Broadcast -- Send data via the Winsock socket * + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "WSProto.h" + +#include + + +/*********************************************************************************************** + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::WinsockInterfaceClass(void) +{ + WinsockInitialised = false; + ASync = INVALID_HANDLE_VALUE; + Socket = INVALID_SOCKET; +} + + +/*********************************************************************************************** + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::~WinsockInterfaceClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * WIC::Close -- Releases any currently in use Winsock resources. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + Stop_Listening(); + + /* + ** Close any open sockets + */ + Close_Socket(); + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = false; +} + + + +/*********************************************************************************************** + * WIC::Close_Socket -- Close the communication socket if its open * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:53AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close_Socket (void) +{ + if ( Socket != INVALID_SOCKET ) { + closesocket (Socket); + Socket = INVALID_SOCKET; + } +} + + + +/*********************************************************************************************** + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:54AM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Start_Listening (void) +{ + /* + ** Enable asynchronous events on the socket + */ + if ( WSAAsyncSelect ( Socket, MainWindow, Protocol_Event_Message(), FD_READ | FD_WRITE) == SOCKET_ERROR ){ + WWDebugString ( "TS: Async select failed.\n" ); + assert (false); + WSACancelAsyncRequest(ASync); + ASync = INVALID_HANDLE_VALUE; + return (false); + } + return (true); +} + + +/*********************************************************************************************** + * WIC::Stop_Listening -- Disable the winsock event callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:06PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Stop_Listening (void) +{ + if ( ASync != INVALID_HANDLE_VALUE ) { + WSACancelAsyncRequest ( ASync ); + ASync = INVALID_HANDLE_VALUE; + } +} + + + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_In_Buffers (void) +{ + WinsockBufferType *packet; + + while ( InBuffers.Count() ) { + packet = InBuffers [ 0 ]; + delete packet; + InBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_Out_Buffers (void) +{ + WinsockBufferType *packet; + + while ( OutBuffers.Count() ) { + packet = OutBuffers [ 0 ]; + delete packet; + OutBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Init -- Initialised Winsock and this class for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (true); + + /* + ** Create a buffer much larger than the sizeof (WSADATA) would indicate since Bounds Checker + ** says that a buffer of that size gets overrun. + */ + char *buffer = new char [sizeof (WSADATA) + 1024]; + WSADATA *winsock_info = (WSADATA*) (&buffer[0]); + + /* + ** Initialise socket and event handle to null + */ + Socket =INVALID_SOCKET; + ASync = INVALID_HANDLE_VALUE; + Discard_In_Buffers(); + Discard_Out_Buffers(); + + /* + ** Start WinSock, and fill in our Winsock info structure + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, winsock_info); + if (rc != 0) { + char out[128]; + sprintf (out, "TS: Winsock failed to initialise - error code %d.\n", GetLastError() ); + OutputDebugString (out); + delete [] buffer; + return (false); + } + + /* + ** Check the Winsock version number + */ + if ((winsock_info->wVersion & 0x00ff) != (version & 0x00ff) || + (winsock_info->wVersion >> 8) != (version >> 8)) { + OutputDebugString ("TS: Winsock version is less than 1.1\n" ); + delete [] buffer; + return (false); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = true; + + delete [] buffer; + return (true); + +} + + + + +/*********************************************************************************************** + * WIC::Read -- read any pending input from the communications socket * + * * + * * + * * + * INPUT: ptr to buffer to receive input * + * length of buffer * + * ptr to address to fill with address that packet was sent from * + * length of address buffer * + * * + * OUTPUT: number of bytes transfered to buffer * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * * + * HISTORY: * + * 3/20/96 2:58PM ST : Created * + *=============================================================================================*/ +int WinsockInterfaceClass::Read(void *buffer, int &buffer_len, void *address, int &address_len) +{ + address_len = address_len; + /* + ** Call the message loop in case there are any outstanding winsock READ messages. + */ + Keyboard->Check(); + + /* + ** If there are no available packets then return 0 + */ + if ( InBuffers.Count() == 0 ) return (0); + + /* + ** Get the oldest packet for reading + */ + int packetnum = 0; + WinsockBufferType *packet = InBuffers [packetnum]; + + assert ( buffer_len >= packet->BufferLen ); + assert ( address_len >= sizeof (packet->Address) ); + + /* + ** Copy the data and the address it came from into the supplied buffers. + */ + memcpy ( buffer, packet->Buffer, packet->BufferLen ); + memcpy ( address, packet->Address, sizeof (packet->Address) ); + + /* + ** Return the length of the packet in buffer_len. + */ + buffer_len = packet->BufferLen; + + /* + ** Delete the temporary storage for the packet now that it is being passed to the game. + */ + InBuffers.Delete ( packetnum ); + delete packet; + + return ( buffer_len ); +} + + + + +/*********************************************************************************************** + * WIC::WriteTo -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * address to send data to. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::WriteTo(void *buffer, int buffer_len, void *address) +{ + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + packet->IsBroadcast = false; +// memcpy ( packet->Address, address, sizeof (packet->Address) ); + memcpy ( packet->Address, address, sizeof( IPXAddressClass ) ); // Steve Tall has revised WriteTo due to this bug. + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * * + * * + * * + * INPUT: Socket * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:05PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Clear_Socket_Error(SOCKET socket) +{ + unsigned long error_code; + int length = 4; + + getsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length); + error_code = 0; + setsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, length); +} + + + + + + + +/*********************************************************************************************** + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:07PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Set_Socket_Options ( void ) +{ + static int socket_transmit_buffer_size = SOCKET_BUFFER_SIZE; + static int socket_receive_buffer_size = SOCKET_BUFFER_SIZE; + + /* + ** Specify the size of the receive buffer. + */ + int err = setsockopt ( Socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_receive_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_RCVBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Specify the size of the send buffer. + */ + err = setsockopt ( Socket, SOL_SOCKET, SO_SNDBUF, (char*)&socket_transmit_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_SNDBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + return ( true ); +} + + diff --git a/CODE/WSPROTO.H b/CODE/WSPROTO.H new file mode 100644 index 0000000..8178d90 --- /dev/null +++ b/CODE/WSPROTO.H @@ -0,0 +1,192 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 4 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPROTO_H +#define WSPROTO_H + +#include "_WSProto.h" + +/* +** Include standard Winsock 1.0 header file. +*/ +#include + +/* +** Misc defines +*/ +#define WINSOCK_MINOR_VER 1 // Version of Winsock +#define WINSOCK_MAJOR_VER 1 // that we require + +//#define WS_RECEIVE_BUFFER_LEN 32768 // Length of our temporary receive buffer. Needs to be more that the max packet size which is about 550 bytes. +//#define SOCKET_BUFFER_SIZE 32768 // Length of winsocks internal buffer. +#define WS_RECEIVE_BUFFER_LEN 1024 // Length of our temporary receive buffer. +#define SOCKET_BUFFER_SIZE 1024*128 // Length of winsocks internal buffer. + +#define PLANET_WESTWOOD_HANDLE_MAX 20 // Max length of a WChat handle + +/* +** Define events for Winsock callbacks +*/ +#define WM_IPXASYNCEVENT (WM_USER + 115) // IPX socket Async event +#define WM_UDPASYNCEVENT (WM_USER + 116) // UDP socket Async event + + +/* +** Enum to identify the protocols supported by the Winsock interface. +*/ +typedef enum tProtocolEnum { + PROTOCOL_NONE, + PROTOCOL_IPX, + PROTOCOL_UDP +} ProtocolEnum; + + + +/* +** +** Class to interface with Winsock. This interface only supports connectionless packet protocols +** like UDP & IPX. Connection orientated or streaming protocols like TCP are not supported by this +** class. +** +*/ +class WinsockInterfaceClass { + + public: + + WinsockInterfaceClass(void); + virtual ~WinsockInterfaceClass(void); + + bool Init(void); + void Close(void); + + + virtual void Close_Socket(void); + virtual int Read(void *buffer, int &buffer_len, void *address, int &address_len); + virtual void WriteTo (void *buffer, int buffer_len, void *address); + virtual void Broadcast (void *buffer, int buffer_len); + virtual void Discard_In_Buffers (void); + virtual void Discard_Out_Buffers (void); + virtual bool Start_Listening (void); + virtual void Stop_Listening (void); + virtual void Clear_Socket_Error(SOCKET socket); + virtual bool Set_Socket_Options ( void ); + virtual void Set_Broadcast_Address ( void * ) {}; + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_NONE); + }; + + virtual int Protocol_Event_Message (void) { + return (0); + }; + + virtual bool Open_Socket ( SOCKET ) { + return (false); + }; + + virtual long Message_Handler(HWND, UINT, UINT, LONG) { + return (1); + } + + + typedef enum ConnectStatusEnum { + CONNECTED_OK = 0, + NOT_CONNECTING, + CONNECTING, + UNABLE_TO_CONNECT_TO_SERVER, + CONTACTING_SERVER, + SERVER_ADDRESS_LOOKUP_FAILED, + RESOLVING_HOST_ADDRESS, + UNABLE_TO_ACCEPT_CLIENT, + UNABLE_TO_CONNECT, + CONNECTION_LOST + } ConnectStatusEnum; + + inline ConnectStatusEnum Get_Connection_Status(void) {return (ConnectStatus);} + + protected: + + /* + ** This struct contains the information needed for each incoming and outgoing packet. + ** It acts as a temporary control for these packets. + */ + typedef struct tWinsockBufferType { + unsigned char Address [64]; // Address. IN_ADDR, IPXAddressClass etc. + int BufferLen; // Length of data in buffer + bool IsBroadcast; // Flag to broadcast this packet + unsigned char Buffer[1024]; // Buffer to store packet in. + } WinsockBufferType; + + /* + ** Array of buffers to temporarily store incoming and outgoing packets. + */ + DynamicVectorClass InBuffers; + DynamicVectorClass OutBuffers; + + + /* + ** Is Winsock present and initialised? + */ + bool WinsockInitialised; + + /* + ** Socket that communications will take place over. + */ + SOCKET Socket; + + /* + ** Async object required for callbacks to our message handler. + */ + HANDLE ASync; + + /* + ** Temporary receive buffer to use when querying Winsock for incoming packets. + */ + unsigned char ReceiveBuffer[WS_RECEIVE_BUFFER_LEN]; + + /* + ** Current connection status. + */ + ConnectStatusEnum ConnectStatus; +}; + + + + + + + +#endif //WSPROTO_H diff --git a/CODE/WSPUDP.CPP b/CODE/WSPUDP.CPP new file mode 100644 index 0000000..e1a28b6 --- /dev/null +++ b/CODE/WSPUDP.CPP @@ -0,0 +1,431 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSPUDP.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "internet.h" +#include "WSPUDP.h" + +#include +#include +#include + + +/*********************************************************************************************** + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:11PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::UDPInterfaceClass (void) : WinsockInterfaceClass() +{} + + + +/*********************************************************************************************** + * UDPIC::~UDPInterfaceClass -- UDPInterface class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/9/97 12:17PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::~UDPInterfaceClass (void) +{ + while ( BroadcastAddresses.Count() ) { + delete BroadcastAddresses[0]; + BroadcastAddresses.Delete(0); + } + + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + Close(); +} + + +/*********************************************************************************************** + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * * + * * + * * + * INPUT: ptr to address in decimal dot format. i.e. xxx.xxx.xxx.xxx * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:12PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Set_Broadcast_Address (void *address) +{ + char* ip_addr = (char*) address; + assert ( strlen (ip_addr) <= strlen ( "xxx.xxx.xxx.xxx" ) ); + + unsigned char *baddr = new unsigned char[4]; + + sscanf ( ip_addr, "%d.%d.%d.%d", &baddr[0], &baddr[1], &baddr[2], &baddr[3] ); + BroadcastAddresses.Add (baddr); +} + + + +/*********************************************************************************************** + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * * + * * + * * + * INPUT: Socket number to use. Not required for this protocol. * + * * + * OUTPUT: True if socket was opened OK * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:13PM ST : Created * + *=============================================================================================*/ +bool UDPInterfaceClass::Open_Socket ( SOCKET ) +{ + LINGER ling; + struct sockaddr_in addr; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + /* + ** Create our UDP socket + */ + Socket = socket(AF_INET, SOCK_DGRAM, 0); + if (Socket == INVALID_SOCKET) { + return (false); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ( (unsigned short) PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl (INADDR_ANY); + + if ( bind (Socket, (LPSOCKADDR)&addr, sizeof(addr) ) == SOCKET_ERROR) { + Close_Socket (); + return (false); + } + + /* + ** Use gethostbyname to find the name of the local host. We will need this to look up + ** the local ip address. + */ + char hostname[128]; + gethostname(hostname, 128); + WWDebugString (hostname); + struct hostent *host_info = gethostbyname ( hostname ); + + /* + ** Clear out any old local addresses from the local address list. + */ + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + + /* + ** Add all local IP addresses to the list. This list will be used to discard any packets that + ** we send to ourselves. + */ + unsigned long **addresses = (unsigned long**) (host_info->h_addr_list); + + for ( ;; ) { + if ( !*addresses ) break; + + unsigned long address = **addresses++; + //address = ntohl (address); + + char temp[128]; + sprintf (temp, "RA95: Found local address: %d.%d.%d.%d\n", address & 0xff, (address & 0xff00) >> 8, (address & 0xff0000) >> 16, (address & 0xff000000) >> 24); + OutputDebugString (temp); + + unsigned char *a = new unsigned char [4]; + * ((unsigned long*) a) = address; + LocalAddresses.Add (a); + } + + /* + ** Set options for the UDP socket + */ + ling.l_onoff = 0; // linger off + ling.l_linger = 0; // timeout in seconds (ie close now) + setsockopt (Socket, SOL_SOCKET, SO_LINGER, (LPSTR)&ling, sizeof(ling)); + + WinsockInterfaceClass::Set_Socket_Options(); + + return (true); + + +} + + + + +/*********************************************************************************************** + * UDPIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + for ( int i=0 ; iBuffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Set up the send address for this packet. + */ + memset (packet->Address, 0, sizeof (packet->Address)); + memcpy (packet->Address+4, BroadcastAddresses[i], 4); + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); + } +} + + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ +long UDPInterfaceClass::Message_Handler(HWND, UINT message, UINT, LONG lParam) +{ + struct sockaddr_in addr; + int rc; + int addr_len; + WinsockBufferType *packet; + + /* + ** We only handle UDP events. + */ + if ( message != WM_UDPASYNCEVENT ) return (1); + + /* + ** Handle UDP packet events + */ + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*)ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make sure this packet didn't come from us. If it did then throw it away. + */ + for ( int i=0 ; iBufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + memset ( packet->Address, 0, sizeof (packet->Address) ); + memcpy ( packet->Address+4, &addr.sin_addr.s_addr, 4 ); + InBuffers.Add (packet); + } + return (0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + if ( OutBuffers.Count() == 0 ) return (0); + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ((unsigned short)PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, packet->Address+4, 4); + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + return (0); + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + return(0); + } + + return (0); +} diff --git a/CODE/WSPUDP.H b/CODE/WSPUDP.H new file mode 100644 index 0000000..80f48c7 --- /dev/null +++ b/CODE/WSPUDP.H @@ -0,0 +1,86 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/WSPUDP.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPUDP_H +#define WSPUDP_H + +#include "WSProto.h" +#include + + + + +/* +** Class to allow access to UDP specific portions of the Winsock interface. +** +*/ +class UDPInterfaceClass : public WinsockInterfaceClass { + + public: + + UDPInterfaceClass (void); + virtual ~UDPInterfaceClass(void); + + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + virtual void Set_Broadcast_Address ( void *address ); + virtual void Broadcast (void *buffer, int buffer_len); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_UDP); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_UDPASYNCEVENT); + }; + + + private: + + /* + ** Address to use when broadcasting a packet. + */ + DynamicVectorClass BroadcastAddresses; + + /* + ** List of local addresses. + */ + DynamicVectorClass LocalAddresses; +}; + + + +#endif \ No newline at end of file diff --git a/CODE/WWALLOC.H b/CODE/WWALLOC.H new file mode 100644 index 0000000..c47405c --- /dev/null +++ b/CODE/WWALLOC.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\wwalloc.h_v 4.9 07 May 1996 17:14:00 JOE_BOSTIC $ */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** This should be located in the wwlib32.h file, but is located here for +** test purposes. +*/ +#ifdef __FLAT__ +#define PRIVATE static +#endif + +typedef enum MemoryFlagType { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_PUBLIC = 0x0000, // Default memory (normal). + MEM_CHIP = 0x0000, // Graphic & sound buffer memory (Amiga). + MEM_UNUSED = 0x0001, // + MEM_SYSTEM = 0x0002, // Allocate out of system heap (XMS or EMS only). + MEM_RELAXED= 0x0004, // Don't worry about page conservation in EMS. + MEM_TEMP = 0x0008, // Temporary allocation (used by library only). + MEM_CLEAR = 0x0010, // Fill memory with '\0' characters. + MEM_PARA = 0x0020, // Paragraph aligned (IBM only). + MEM_XMS = 0x0040, // XMS memory. + MEM_EMS = 0x0080, // EMS memory (not implemented). + MEM_X = 0x8000 // Here to force this enum to be unsigned sized. +} MemoryFlagType; +MemoryFlagType operator |(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator &(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator ~(MemoryFlagType); + + +/* Prototypes for functions defined in this file */ +void *cdecl Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void cdecl Free(void const *pointer); +void *cdecl Resize_Alloc(void const *original_ptr, unsigned long new_size_in_bytes); +long cdecl Ram_Free(MemoryFlagType flag); +long cdecl Total_Ram_Free(MemoryFlagType flag); +long cdecl Heap_Size(MemoryFlagType flag); + +extern unsigned long cdecl MinRam; // Record of least memory at worst case. +extern unsigned long cdecl MaxRam; // Record of total allocated at worst case. + +#ifdef __cplusplus +} +#endif + diff --git a/CODE/WWFILE.H b/CODE/WWFILE.H new file mode 100644 index 0000000..831d02e --- /dev/null +++ b/CODE/WWFILE.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/WWFILE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_Hx +#define WWFILE_Hx + +#define YEAR(dt) (((dt & 0xFE000000) >> (9 + 16)) + 1980) +#define MONTH(dt) ((dt & 0x01E00000) >> (5 + 16)) +#define DAY(dt) ((dt & 0x001F0000) >> (0 + 16)) +#define HOUR(dt) ((dt & 0x0000F800) >> 11) +#define MINUTE(dt) ((dt & 0x000007E0) >> 5) +#define SECOND(dt) ((dt & 0x0000001F) << 1) + +#include +#include + +#ifndef READ +#define READ 1 +#endif +#ifndef WRITE +#define WRITE 2 +#endif + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(false);} + virtual void Error(int error, int canretry = false, char const * filename=NULL) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/CODE/XPIPE.CPP b/CODE/XPIPE.CPP new file mode 100644 index 0000000..2742ab0 --- /dev/null +++ b/CODE/XPIPE.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/XPIPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : XPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * FilePipe::Put -- Submit a block of data to the pipe. * + * FilePipe::End -- End the file pipe handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "xpipe.h" +#include +#include + + +//--------------------------------------------------------------------------------------------------------- +// BufferPipe +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * * + * The buffered pipe is a pipe terminator. That is, the data never flows onto subsequent * + * pipe chains. The data is stored into the buffer previously submitted to the pipe. * + * If the buffer is full, no more data is output to the buffer. * + * * + * INPUT: source -- Pointer to the data to submit. * + * * + * length -- The number of bytes to be submitted. * + * * + * OUTPUT: Returns with the number of bytes output to the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferPipe::Put(void const * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(((char *)BufferPtr.Get_Buffer()) + Index, source, len); + } + + Index += len; +// Length -= len; +// Buffer = ((char *)Buffer) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FilePipe +//--------------------------------------------------------------------------------------------------------- + +FilePipe::~FilePipe(void) +{ + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + File = NULL; + } +} + + +/*********************************************************************************************** + * FilePipe::End -- End the file pipe handler. * + * * + * This routine is called when there will be no more data sent through the pipe. It is * + * responsible for cleaning up anything it needs to. This is not handled by the * + * destructor, although it serves a similar purpose, because pipe are linked together and * + * the destructor order is not easily controlled. If the destructors for a pipe chain were * + * called out of order, the result might be less than pleasant. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes flushed out the final end of the pipe as a * + * consequence of this routine. * + * * + * WARNINGS: Don't send any more data through the pipe after this routine is called. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::End(void) +{ + int total = Pipe::End(); + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + } + return(total); +} + + +/*********************************************************************************************** + * FilePipe::Put -- Submit a block of data to the pipe. * + * * + * Takes the data block submitted and writes it to the file. If the file was not already * + * open, this routine will open it for write. * + * * + * INPUT: source -- Pointer to the data to submit to the file. * + * * + * length -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::Put(void const * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + File->Open(WRITE); + } + + return(File->Write(source, slen)); + } + return(0); +} diff --git a/CODE/XPIPE.H b/CODE/XPIPE.H new file mode 100644 index 0000000..96067e2 --- /dev/null +++ b/CODE/XPIPE.H @@ -0,0 +1,95 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/XPIPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : XPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef XPIPE_H +#define XPIPE_H + +#include "pipe.h" +#include "wwfile.h" +#include "buff.h" + +/* +** This is a simple store-into-buffer pipe terminator. Use it as the final link in a pipe process +** that needs to store the data into a memory buffer. This can only serve as the final +** link in the chain of pipe segments. +*/ +class BufferPipe : public Pipe +{ + public: + BufferPipe(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferPipe(void * buffer, int length) : BufferPtr(buffer, length), Index(0) {} + virtual int Put(void const * source, int slen); + + private: + Buffer BufferPtr; + int Index; + +// void * Buffer; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferPipe(BufferPipe & rvalue); + BufferPipe & operator = (BufferPipe const & pipe); +}; + + +/* +** This is a store-to-file pipe terminator. Use it as the final link in a pipe process that +** needs to store the data to a file. This can only serve as the last link in the chain +** of pipe segments. +*/ +class FilePipe : public Pipe +{ + public: + FilePipe(FileClass * file) : File(file), HasOpened(false) {} + FilePipe(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FilePipe(void); + + virtual int Put(void const * source, int slen); + virtual int End(void); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FilePipe(FilePipe & rvalue); + FilePipe & operator = (FilePipe const & pipe); + +}; + +#endif diff --git a/CODE/XSTRAW.CPP b/CODE/XSTRAW.CPP new file mode 100644 index 0000000..82c0373 --- /dev/null +++ b/CODE/XSTRAW.CPP @@ -0,0 +1,151 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/XSTRAW.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : XSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * FileStraw::Get -- Fetch data from the file. * + * FileStraw::~FileStraw -- The destructor for the file straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "xstraw.h" +#include +#include + +//--------------------------------------------------------------------------------------------------------- +// BufferStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * * + * This routine will copy the requested number of bytes from the buffer holding tank (as * + * set up by the straw's constructor) to the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to be filled with data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes copied to the buffer. If this is less than * + * requested, then it indicates that the data holding tank buffer is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(source, ((char*)BufferPtr.Get_Buffer()) + Index, len); + } + + Index += len; +// Length -= len; +// BufferPtr = ((char *)BufferPtr) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FileStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * FileStraw::Get -- Fetch data from the file. * + * * + * This routine will read data from the file (as specified in the straw's constructor) into * + * the buffer indicated. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If this number is less * + * than the number requested, then this indicates that the file is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FileStraw::Get(void * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + if (!File->Is_Available()) return(0); + if (!File->Open(READ)) return(0); + } + + return(File->Read(source, slen)); + } + return(0); +} + + +/*********************************************************************************************** + * FileStraw::~FileStraw -- The destructor for the file straw. * + * * + * This destructor only needs to close the file if it was the one to open it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +FileStraw::~FileStraw(void) +{ + if (Valid_File() && HasOpened) { + File->Close(); + HasOpened = false; + File = NULL; + } +} diff --git a/CODE/XSTRAW.H b/CODE/XSTRAW.H new file mode 100644 index 0000000..1612f2c --- /dev/null +++ b/CODE/XSTRAW.H @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/* $Header: /CounterStrike/XSTRAW.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : XSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef XSTRAW_H +#define XSTRAW_H + +#include "straw.h" +#include "buff.h" +#include "wwfile.h" +#include + +/* +** This class is used to manage a buffer as a data source. Data requests will draw from the +** buffer supplied until the buffer is exhausted. +*/ +class BufferStraw : public Straw +{ + public: + BufferStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferStraw(void const * buffer, int length) : BufferPtr((void*)buffer, length), Index(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; +// void const * BufferPtr; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferStraw(BufferStraw & rvalue); + BufferStraw & operator = (BufferStraw const & pipe); +}; + +/* +** This class is used to manage a file as a data source. Data requests will draw from the +** file until the file has been completely read. +*/ +class FileStraw : public Straw +{ + public: + FileStraw(FileClass * file) : File(file), HasOpened(false) {} + FileStraw(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FileStraw(void); + virtual int Get(void * source, int slen); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FileStraw(FileStraw & rvalue); + FileStraw & operator = (FileStraw const & pipe); +}; + + +#endif diff --git a/CODE/_WSPROTO.CPP b/CODE/_WSPROTO.CPP new file mode 100644 index 0000000..946caf6 --- /dev/null +++ b/CODE/_WSPROTO.CPP @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/_WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:47p $* + * * + * $Revision:: 2 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "_WSProto.h" + + +WinsockInterfaceClass *PacketTransport = 0; //The object for interfacing with Winsock diff --git a/CODE/_WSPROTO.H b/CODE/_WSPROTO.H new file mode 100644 index 0000000..eae6a15 --- /dev/null +++ b/CODE/_WSPROTO.H @@ -0,0 +1,45 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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:: /Sun/_WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:39p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef _WSPROTO_H +#define _WSPROTO_H + + +class WinsockInterfaceClass; +extern WinsockInterfaceClass *PacketTransport; //The object for interfacing with Winsock + +#endif //_WSPROTO_H diff --git a/IPX/FIXTHUNK.CPP b/IPX/FIXTHUNK.CPP new file mode 100644 index 0000000..683f849 --- /dev/null +++ b/IPX/FIXTHUNK.CPP @@ -0,0 +1,145 @@ +/* +** 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 . +*/ + + +#include +#include +#include +#include +#include +#include + + +unsigned char thunk_file[1000000]; +unsigned char thunk_file_out[100050]; + + +/*********************************************************************************************** + * Search_For_String -- search for a string of chars within a buffer * + * * + * * + * * + * INPUT: string * + * ptr to buffer to search in * + * length of buffer * + * * + * OUTPUT: ptr to string in buffer or NULL if not found * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:42PM ST : Created * + *=============================================================================================*/ + +char *Search_For_String (char *string , char *buffer_ptr , int buffer_length) +{ + int j; + int string_length=strlen(string); + + for (int i=0 ; i. +; + + + + IDEAL + P386N + LOCALS ?? + + + + segment realcode para public USE16 'code' + + +global PASCAL ASM_IPX_INITIALISE:far +global PASCAL ASM_IPX_UNINITIALISE:far + + +proc ASM_IPX_INITIALISE PASCAL FAR + + int 3 + mov ax,7a00h + int 2fh + and eax,0ffh + cmp al,-1 + setz al + ret + +endp ASM_IPX_INITIALISE + + + + +proc ASM_IPX_UNINITIALISE PASCAL FAR + + ret + +endp ASM_IPX_UNINITIALISE + + + + ends + + end + + + diff --git a/IPX/IPX16.BAK b/IPX/IPX16.BAK new file mode 100644 index 0000000..8ae28ec --- /dev/null +++ b/IPX/IPX16.BAK @@ -0,0 +1,13 @@ +system windows_dll +option redefsok +option quiet +option map +option eliminate +option caseexact +option one + +debug all +file ipx16a.obj +file ipx16c.obj + +name wwipx16.dll diff --git a/IPX/IPX16.H b/IPX/IPX16.H new file mode 100644 index 0000000..b6aa563 --- /dev/null +++ b/IPX/IPX16.H @@ -0,0 +1,25 @@ +/* +** 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 . +*/ + + + +extern "C"{ + BOOL FAR __cdecl IPX_Initialise(int); +} + + diff --git a/IPX/IPX16A.ASM b/IPX/IPX16A.ASM new file mode 100644 index 0000000..1676bef --- /dev/null +++ b/IPX/IPX16A.ASM @@ -0,0 +1,1472 @@ +; +; 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 . +; + + +;*************************************************************************** +;** 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 I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : IPX16a.asm * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : January 26th, 1996 * +;* * +;* Last Update : January 26th, 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Overview: * +;* 16-bit protected mode windows code. This code sits in a 16 bit .DLL * +;* and provides access to IPX functions via the standard DOS interface. * +;* * +;* Strangely enough, although you would expect to have to use segment * +;* addresses when passing data to and from the DOS IPX services, it only * +;* seems to work with selector addressing. This could be something to do * +;* with being in a windows .DLL. * +;* * +;* It seems that in Win95, the DOS services are stubbed to just pass up * +;* to NWLINK which is the win95 32bit netware services layer. If we ever * +;* have direct access to that then all this code can go away. * +;* * +;* * +;*-----------------------------------------------------------------------------------------------* +;* Functions: * +;* _IPX_Initialise -- confirms the presence of IPX and gets the entry point for subsequent calls * +;* _IPX_Open_Socket95 -- opens the given socket number * +;* IPX_Close_Socket95 -- closes the given socket number * +;* _IPX_Get_Connection_Number95 -- get the users local 'connection number' * +;* _IPX_Send_Packet95 -- Send a packet via IPX * +;* _IPX_Broadcast_Packet95 -- Broadcast a packet via IPX * +;* _IPX_Get_Local_Target95 -- Gets the node address of a destination or bridge * +;* _IPX_Get_Outstanding_Buffer95 -- return contents of the next unread buffer in the buffer list * +;* Receive_Callback -- IPX callback function. Called when a new packet arrives * +;* Init_Receive_ECB -- initialises an ECB for receiving a packet * +;* _IPX_Start_Listening95 -- initialise DOS memory for receive buffers and start listening for * +;* IPX packets * +;* _IPX_Shut_Down95 -- finish an IPX session. Releases any allocated resources except the socket * +;* Listen_For_Packet -- Start IPX listening for an incoming packet * +;* Call_IPX -- Make a call to the IPX entry point * +;* Call_DOS -- use DPMI to call a DOS interrupt service * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + .8086 ;cant specify .386 until we are into the code segment + + + .model large + option segment:USE16 ;16-bit segment + option readonly + option oldstructs + + + assume ds:@data ;ds is defauly segment register for our data segment + assume es:nothing + + +;############################################################################# +; +; Constants +; +RECEIVE_BUFFER_LENGTH =1024 ;length of each DOS memory receive buffer +MAX_RECEIVE_BUFFERS =32 ;number of receive buffers allocated + +OFFS =0 ;offset of OFFSET part of a far ptr +SEGM =2 ;offset of SEGMENT part of a far ptr + +SEND_ECB_OFFSET =0 ;offset of SEND ecb +SEND_HEADER_OFFSET =(sizeof ECB +3) AND 0fffch ;offset of our send header +SEND_BUFFER_OFFSET =(SEND_HEADER_OFFSET + sizeof IPXHeaderType +3) AND 0fffch ;offset of our send buffer + +;--------------------------------------------------------------------------- +;These defines are for the IPX functions. The function number is specified +;by placing it in BX when IPX is called. There are two ways to invoke IPX: +;use interrupt 0x7a, or use a function whose address is obtained by calling +;interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +;This is the preferred method, since other apps are known to use int 0x7a. +;--------------------------------------------------------------------------- +IPX_OPEN_SOCKET = 0000h +IPX_CLOSE_SOCKET = 0001h +IPX_GET_LOCAL_TARGET = 0002h +IPX_SEND_PACKET = 0003h +IPX_LISTEN_FOR_PACKET = 0004h +IPX_SCHEDULE_EVENT = 0005h +IPX_CANCEL_EVENT = 0006h +IPX_GET_INTERVAL_MARKER = 0008h +IPX_GET_INTERNETWORK_ADDRESS = 0009h +IPX_RELINQUISH_CONTROL = 000Ah +IPX_DISCONNECT_FROM_TARGET = 000Bh + +;/*--------------------------------------------------------------------------- +;These defines are for various IPX error codes: +;---------------------------------------------------------------------------*/ +IPXERR_CONNECTION_SEVERED = 00ech +IPXERR_CONNECTION_FAILED = 00edh +IPXERR_NO_CONNECTION = 00eeh +IPXERR_CONNECTION_TABLE_FULL = 00efh +IPXERR_NO_CANCEL_ECB = 00f9h +IPXERR_NO_PATH = 00fah +IPXERR_ECB_INACTIVE = 00fch +IPXERR_INVALID_PACKET_LENGTH = 00fdh +IPXERR_SOCKET_TABLE_FULL = 00feh +IPXERR_SOCKET_ERROR = 00ffh + +;/*--------------------------------------------------------------------------- +;These defines are for various interrupt vectors and DPMI functions: +;---------------------------------------------------------------------------*/ +IPX_INT = 007ah +DPMI_INT = 0031h +DPMI_ALLOC_DOS_MEM = 0100h +DPMI_FREE_DOS_MEM = 0101h +DPMI_CALL_REAL_INT = 0300h +DPMI_CALL_REAL_PROC = 0301h +DPMI_ALLOCATE_CALLBACK = 0303h +DPMI_RELEASE_CALLBACK = 0304h +DPMI_LOCK_MEM = 0600h +DPMI_UNLOCK_MEM = 0601h + + + +;############################################################################# +; +; Windows API functions +; +externdef GLOBALDOSALLOC:far +externdef GLOBALDOSFREE:far + + +;############################################################################# +; +; Global initialised data +; + .data + +IPXCallOffset dw 0 +IPXCallSegment dw 0 + +RegisterDump db 32h dup (0) +NoReenter db 0 + + +;############################################################################# +; +; Global uninitialised data +; + .data? + +MySocket dw ? +ReceiveBufferSegment dw ? +ReceiveBufferSelector dw ? +ReceiveECBOffset dw ? +CurrentReceiveBuffer dw ? +LastPassedReceiveBuffer dw ? + +ReceiveDescriptor db 8 dup (?) +OriginalReceiveDescriptor db 8 dup (?) + +Listening db ? +ListeningFailed db ? + + +;############################################################################# +; +; Structures +; + +;--------------------------------------------------------------------------- +;This is the IPX Packet structure. It's followed by the data itself, which +;can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +;field; annotation of 'APP' means the application must set the field. +;NOTE: All header fields are ordered high-byte,low-byte. +;---------------------------------------------------------------------------*/ +IPXHeaderType struct + + CheckSum dw ? ; IPX: Not used; always 0xffff + IPXLength dw ? ; IPX: Total size, incl header & data + TransportControl db ? ; IPX: # bridges message crossed + PacketType db ? ; APP: Set to 4 for IPX (5 for SPX) + DestNetworkNumber db ?,?,?,? ; APP: destination Network Number + DestNetworkNode db ?,?,?,?,?,? ; APP: destination Node Address + DestNetworkSocket dw ? ; APP: destination Socket Number + SourceNetworkNumber db ?,?,?,? ; IPX: source Network Number + SourceNetworkNode db ?,?,?,?,?,? ; IPX: source Node Address + SourceNetworkSocket dw ? ; IPX: source Socket Number + +IPXHeaderType ends + + +;/*--------------------------------------------------------------------------- +;This is the IPX Event Control Block. It serves as a communications area +;between IPX and the application for a single IPX operation. You should set +;up a separate ECB for each IPX operation you perform. +;---------------------------------------------------------------------------*/ + +ECB struct + + Link_Address dd ? + Event_Service_Routine dd ? ; APP: event handler (NULL=none) + InUse db ? ; IPX: 0 = event complete + CompletionCode db ? ; IPX: event's return code + SocketNumber dw ? ; APP: socket to send data through + ConnectionID dw ? ; returned by Listen (???) + RestOfWorkspace dw ? + DriverWorkspace db 12 dup (?) + ImmediateAddress db 6 dup (?) ; returned by Get_Local_Target + PacketCount dw ? + PacketAddress0 dd ? + PacketLength0 dw ? + PacketAddress1 dd ? + PacketLength1 dw ? + +ECB ends + + + +; +; Structures used for Get_Local_Target calls +; +request_local_target_buffer struct + lt_network_number db ?,?,?,? + lt_physical_node db ?,?,?,?,?,? + lt_socket dw ? +request_local_target_buffer ends + + +local_target_reply_buffer struct + lt_local_target db ?,?,?,?,?,? +local_target_reply_buffer ends + + + + + + +;############################################################################# +; +; Start of code segment +; +; + .code + + .386 ;now we can specify 386 instructions. Its still a 16 bit code segment though. + + include ;include 16 bit instruction macros + + + +;************************************************************************************************* +;* _IPX_Initialise -- confirms the presence of IPX and gets the entry point for subsequent calls * +;* * +;* * +;* INPUT: eax - nothing * +;* * +;* OUTPUT: true if IPX was initialised * +;* * +;* 32bit PROTO: extern BOOL __stdcall IPX_Initialise(void); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +_IPX_Initialise proc far pascal + + push ebx + push ecx + push edx + push esi + push edi + push ebp + push ds + push es + push fs + push gs + + ; Call the multiplex interrupt to confirm the presence of IPX + ; The real mode entry point is returned in es:di if present + ; + mov ax,7a00h + int 2fh + and eax,0ffh + cmp al,-1 + setz al + + test al,al + jz @f + + ; Save the real mode entry point + ; + mov bx,@data + mov ds,bx + mov [IPXCallSegment],es + mov [IPXCallOffset],di + mov [Listening],0 + mov [ListeningFailed],0 + +@@: pop gs + pop fs + pop es + pop ds + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + ret + +_IPX_Initialise endp + + + +;************************************************************************************************* +;* _IPX_Open_Socket95 -- opens the given socket number * +;* * +;* * +;* INPUT: socket - socket number to open * +;* * +;* OUTPUT: 0 if socket opened successfully * +;* * +;* 32bit PROTO: extern int __stdcall IPX_Open_Socket95(int socket); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Open_Socket95 proc far pascal uses bx cx dx si di ds es fs gs, + socket:word + + mov bx,@data + mov ds,bx + mov bx,IPX_OPEN_SOCKET ;open socket + mov_w dx,[socket] ;socket number + mov_w [MySocket],dx ;save it for later + mov ax,0ffh ;long lived + + call Call_IPX + + ret + +_IPX_Open_Socket95 endp + + + + +;************************************************************************************************* +;* IPX_Close_Socket95 -- closes the given socket number * +;* * +;* * +;* INPUT: socket - socket number to close * +;* * +;* OUTPUT: nothing * +;* * +;* 32bit PROTO: extern void __stdcall IPX_Close_Socket95(int socket); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Close_Socket95 proc far pascal uses ax bx cx dx si di ds es fs gs, + socket:word + + mov bx,1 + mov_w dx,[socket] + + call Call_IPX + + ret + +_IPX_Close_Socket95 endp + + + + + + + +;************************************************************************************************* +;* _IPX_Get_Connection_Number95 -- get the users local 'connection number' * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: connection number. 0 if user isnt logged into novell * +;* * +;* 32bit PROTO: extern int __stdcall IPX_Get_Connection_Number95(void); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Get_Connection_Number95 proc far pascal uses bx cx dx si di ds es fs gs + + mov ax,0dc00h + call Call_DOS + and eax,255 + ret + +_IPX_Get_Connection_Number95 endp + + + + + + + + + +;************************************************************************************************* +;* _IPX_Send_Packet95 -- Send a packet via IPX * +;* * +;* * +;* INPUT: send_address - ptr to address of destination for packet * +;* send_buffer - ptr to data to be sent * +;* send_buffer_len - length of data in send_buffer * +;* network_num - network number * +;* node_address - node address * +;* * +;* OUTPUT: true if successfully sent * +;* * +;* 32bit PROTO: extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, * +;* int, unsigned char*, unsigned char*); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Send_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_address :far ptr byte, + send_buffer :far ptr byte, + send_buffer_len :word, + network_num :far ptr byte, + node_address :far ptr byte + + local selector :word ;DOS memory selector + local segmento :word ;DOS memory segment + local dos_send_ecb :far ptr ECB ;ptr to send ECB + local dos_send_header :far ptr IPXHeaderType ;ptr to IPX header for sending + local dos_send_buffer :far ptr byte ;ptr to copy of buffer to be sent + local completion_code :word ;IPX send packet completion code + + ; Allocate required DOS memory + ; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory + ; GlobalDosAlloc returns the segment in dx and a selector in ax + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + push bp + push ax + push bx + call GLOBALDOSALLOC + pop bp + test ax,ax + jz @@error + mov [segmento],dx ;save the segment pointer + mov [selector],ax ;save the selector pointer + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send IPX header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the DOS memory send buffer + ; + mov cx,546 ;maximum size of IPX data packet + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the send ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[selector] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ; + ; Fill in the address field + ; + lds si,[send_address] + mov eax,[si] + mov_d es:[di.ImmediateAddress],eax + mov ax,[si+4] + mov_w es:[di.ImmediateAddress+4],ax + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the network number and node address + ; + lds si,[network_num] + mov_d eax,[si] + mov_d es:[di.DestNetworkNumber],eax + lds si,[node_address] + mov_d eax,[si] + mov_d es:[di.DestNetworkNode],eax + mov_w ax,[si+4] + mov_w es:[di.DestNetworkNode+4],ax + + ; + ; Send that sucker! + ; + mov es,[selector] + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + call Call_IPX + popa + + ; + ; Wait for the send to finish by monitoring the 'in use' flag of the ECB + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + ; + ; Let IPX 'breath' if send isnt complete + ; + mov bx,IPX_RELINQUISH_CONTROL + push bp + call Call_IPX + pop bp + jmp @@wait_send_loop + + + ; + ; Get the completion code and save it for posterity + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bp + push bx + call GLOBALDOSFREE + pop bp + + ; + ; Set up the return value + ; + cmp [completion_code],0 + jnz @@error + +@@success: mov ax,1 + ret + +@@error: xor ax,ax + ret + + +_IPX_Send_Packet95 endp + + + + + +;************************************************************************************************* +;* _IPX_Broadcast_Packet95 -- Broadcast a packet via IPX * +;* * +;* * +;* INPUT: send_buffer - ptr to data to be sent * +;* send_buffer_len - length of data in send_buffer * +;* * +;* OUTPUT: true if successfully sent * +;* * +;* 32bit PROTO: extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Broadcast_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_buffer :far ptr byte, + send_buffer_len :word + + local selector :word ;DOS memory selector + local segmento :word ;DOS memory segment + local dos_send_ecb :far ptr ECB ;ptr to send ECB + local dos_send_header :far ptr IPXHeaderType ;ptr to IPX header for sending + local dos_send_buffer :far ptr byte ;ptr to copy of buffer to be sent + local completion_code :word ;IPX send packet completion code + + ; Allocate required DOS memory + ; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory + ; GlobalDosAlloc returns the segment in dx and a selector in ax + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + push bp + push ax + push bx + call GLOBALDOSALLOC + pop bp + test ax,ax + jz @@error + mov [segmento],dx ;save the segment pointer + mov [selector],ax ;save the selector pointer + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the send buffer + ; + mov cx,546 ;maximum size of IPX data packet + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[selector] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ; + ; Fill in the address field + ; + mov_d es:[di.ImmediateAddress],0ffffffffh + mov_w es:[di.ImmediateAddress+4],0ffffh + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the network number and node address + ; + mov_d es:[di.DestNetworkNumber],0ffffffffh + mov_d es:[di.DestNetworkNode],0ffffffffh + mov_w es:[di.DestNetworkNode+4],0ffffh + + ; + ; Send that sucker! + ; + mov es,[selector] + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + push fs + call Call_IPX + pop fs + popa + + ; + ; Wait for the send to finish by monitoring the 'in use' flag of the ECB + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + ; + ; Let IPX 'breath' if send isnt complete + ; + mov bx,IPX_RELINQUISH_CONTROL + push bp + call Call_IPX + pop bp + jmp @@wait_send_loop + + + ; + ; Get the completion code and save it for posterity + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bp + push bx + call GLOBALDOSFREE + pop bp + + ; + ; Set up the return value + ; + cmp [completion_code],0 + jnz @@error + +@@success: mov ax,1 + ret + +@@error: xor ax,ax + ret + + +_IPX_Broadcast_Packet95 endp + + + + + + +;************************************************************************************************* +;* _IPX_Get_Local_Target95 -- Gets the node address of a destination or bridge * +;* * +;* * +;* INPUT: dest_network - ptr to network number of target * +;* dest_node - ptr to node number of target * +;* socket - socket number * +;* bridge_address - ptr to buffer to store immediate address in * +;* * +;* OUTPUT: IPX result code. -1 if error * +;* * +;* 32bit PROTO: extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, * +;* unsigned short, unsigned char*); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +_IPX_Get_Local_Target95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + dest_network :far ptr byte, + dest_node :far ptr byte, + socket :word, + bridge_address :far ptr byte + + local segmento :word ; DOS memory segment + local selector :word ; DOS memory selector + local result_code :word ; return code + + ; Allocate required DOS memory for data passed to IPX + ; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory + ; GlobalDosAlloc returns the segment in dx and a selector in ax + xor ax,ax + mov bx,(sizeof local_target_reply_buffer + \ + sizeof request_local_target_buffer + 15) + push bp + push ax + push bx + call GLOBALDOSALLOC + pop bp + test ax,ax + jz @@return_error + + mov [segmento],dx ; save the segment + mov [selector],ax ; save the selector + mov fs,ax + xor di,di + + ; + ; Init the request structure + ; + lds si,[dest_network] + mov_d eax,[si] + mov_d fs:[di.lt_network_number],eax + lds si,[dest_node] + mov eax,[si] + mov_d fs:[di.lt_physical_node],eax + mov_w ax,[si+4] + mov_w fs:[di.lt_physical_node+4],ax + mov ax,[socket] + mov fs:[di.lt_socket],ax + + ; + ; Set up the registers to call IPX + ; + mov bx,IPX_GET_LOCAL_TARGET + mov ax,[selector] + mov ds,ax + xor si,si + mov es,ax + mov di,sizeof request_local_target_buffer + + ; + ; Call IPX + ; + push bp + call Call_IPX + pop bp + + ; + ; Save the result code so we can return it + ; + mov [result_code],ax + + ; + ; Copy the bridge address into the return buffer + ; + mov ds,[selector] + mov si,sizeof request_local_target_buffer + lt_local_target + les di,[bridge_address] + movsd + movsw + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bp + push bx + call GLOBALDOSFREE + pop bp + + ; + ; Return the IPX result code + ; + mov ax,[result_code] + ret + +@@return_error: mov ax,-1 + ret + +_IPX_Get_Local_Target95 endp + + + +;************************************************************************************************* +;* _IPX_Get_Outstanding_Buffer95 -- return contents of the next unread buffer in the buffer list * +;* * +;* * +;* INPUT: copy_receive_buffer -- ptr to buffer that data will be copied to * +;* * +;* OUTPUT: number of buffers returned (0 or 1) * +;* * +;* 32bit PROTO: extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +_IPX_Get_Outstanding_Buffer95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs, + copy_receive_buffer:far ptr byte + + mov ax,@data + mov ds,ax + + ; + ; If the receive buffer pointer is the same as the pointer to the last buffer + ; returned then there is no outstanding data to return so just exit + ; + xor ax,ax + mov si,[LastPassedReceiveBuffer] + cmp si,[CurrentReceiveBuffer] + jz @@done + + ; + ; Copy the IPX receive data buffer into the buffer provided by the caller + ; + push ds + mov cx,RECEIVE_BUFFER_LENGTH/4 + mov ds,[ReceiveBufferSelector] + les di,[copy_receive_buffer] + rep movsd + pop ds + + ; + ; Update the pointer to the last buffer we copied + ; + cmp si,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor si,si +@@no_wrap: mov [LastPassedReceiveBuffer],si + + mov ax,1 + +@@done: ret + +_IPX_Get_Outstanding_Buffer95 endp + + + + +;************************************************************************************************* +;* Receive_Callback -- IPX callback function. Called when a new packet arrives * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +Receive_Callback proc far + + ; + ; Save everything + ; + pushf + cli + cld + pushad + push ds + push es + push fs + push gs + + ; + ; Dont reenter + ; + mov ax,@data + mov ds,ax + cmp [NoReenter],0 + jnz @@out + mov [NoReenter],1 + + ; + ; Update the receive buffer pointer to point to the next buffer + ; + mov ax,[CurrentReceiveBuffer] + add ax,RECEIVE_BUFFER_LENGTH + cmp ax,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor ax,ax +@@no_wrap: mov [CurrentReceiveBuffer],ax + + ; + ; Listen for another packet + ; + call Listen_For_Packet + mov ax,@data + mov ds,ax + mov [NoReenter],0 + + ; + ; Restore everything and exit + ; +@@out: pop gs + pop fs + pop es + pop ds + popad + popf + ret + +Receive_Callback endp + + + + +;************************************************************************************************* +;* Init_Receive_ECB -- initialises an ECB for receiving a packet * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +Init_Receive_ECB proc near uses eax ebx ecx edx esi edi ebp ds es + + mov ax,@data + mov ds,ax + mov es,[ReceiveBufferSelector] + + ; + ; Clear out the ECB + ; + mov di,[ReceiveECBOffset] + mov cx,sizeof ECB + xor al,al + rep stosb + + + ; + ; Set up the ECB + ; + + ; + ;General ECB data + mov di,[ReceiveECBOffset] + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + + ; + ; Packet address for IPX header + mov ax,[CurrentReceiveBuffer] + mov bx,[ReceiveBufferSelector] + mov_w es:[di.PacketAddress0+OFFS],ax + mov_w es:[di.PacketAddress0+SEGM],bx + mov es:[di.PacketLength0],sizeof IPXHeaderType + + ; + ; Packet address for receive buffer + mov ax,[CurrentReceiveBuffer] + add ax,sizeof IPXHeaderType + mov_w es:[di.PacketAddress1+OFFS],ax + mov_w es:[di.PacketAddress1+SEGM],bx + mov es:[di.PacketLength1],546 + + ; + ; Set up the callback address + mov_w es:[di.Event_Service_Routine+OFFS],offset Receive_Callback + mov_w es:[di.Event_Service_Routine+SEGM],cs + + ret + +Init_Receive_ECB endp + + + +;************************************************************************************************* +;* _IPX_Start_Listening95 -- initialise DOS memory for receive buffers and start listening for * +;* IPX packets * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: true if we started listening * +;* * +;* 32bit PROTO: extern BOOL __stdcall IPX_Start_Listening95(void); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +_IPX_Start_Listening95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs + + ; + ; Check that we arent already listening + ; + mov ax,@data + mov ds,ax + cmp [ListeningFailed],0 + jnz @@restart + cmp [Listening],0 + jnz @@out ;already listening + + ; + ; Allocate and lock DOS memory for listen ECB and receive buffers + ; GlobalDosAlloc is a win3.1 API function for allocating and locking DOS memory + ; GlobalDosAlloc returns the segment in dx and a selector in ax + mov bx,(RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + sizeof ECB + 15) + xor ax,ax + save bp,ds + push ax + push bx + call GLOBALDOSALLOC + restore bp,ds + test ax,ax + jz @@error + + mov [ReceiveBufferSegment],dx ;save the DOS memory segment in a global + mov [ReceiveBufferSelector],ax ;save the DOS memory selector in a global + + saveall +; int 3 + mov ax,@data + mov es,ax + mov ax,0bh + mov bx,es:[ReceiveBufferSelector] + mov di,offset OriginalReceiveDescriptor + int 31h + bcc @f +; int 3 +@@: restall + + + ; + ; Set up pointers to the DOS memory + ; + mov [ReceiveECBOffset],RECEIVE_BUFFER_LENGTH * MAX_RECEIVE_BUFFERS + mov [CurrentReceiveBuffer],0 ;1st receive buffer + mov [LastPassedReceiveBuffer],0 + + ; + ; Start listening + ; +@@restart: mov [ListeningFailed],0 + push ds + call Listen_For_Packet + pop ds + mov [Listening],1 + +@@out: mov ax,1 + ret + +@@error: xor ax,ax + ret + +_IPX_Start_Listening95 endp + + + +;************************************************************************************************* +;* _IPX_Shut_Down95 -- finish an IPX session. Releases any allocated resources except the socket * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* 32bit PROTO: extern void __stdcall IPX_Shut_Down95(void); * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +_IPX_Shut_Down95 proc far pascal uses eax ebx ecx edx esi edi ebp ds es fs gs + + ; + ; If we aren't listening then we cant stop + ; + mov ax,@data + mov ds,ax + cmp [Listening],0 + jz @@out + + ; + ; Stop listening for a packet + ; + mov es,[ReceiveBufferSelector] + mov si,[ReceiveECBOffset] + mov bx,IPX_CANCEL_EVENT + save bp,ds + call Call_IPX + restore bp,ds + + ; + ; Free the DOS memory + ; + mov ax,@data + mov ds,ax + mov es,ax + mov ax,[ReceiveBufferSelector] + save bp,ds + push ax + call GLOBALDOSFREE + restore bp,ds + + mov [Listening],0 + +@@out: ret + +_IPX_Shut_Down95 endp + + + + + +Verify_Selectors proc near uses eax ebx ecx edx esi edi ebp ds es fs gs + +; int 3 + mov ax,1686h + int 2fh + test ax,ax + jnz @@fail + + mov ax,@data + mov es,ax + mov ax,0bh + mov bx,es:[ReceiveBufferSelector] + mov di,offset ReceiveDescriptor + int 31h + bcs @@fail + + mov ax,@data + mov ds,ax + mov_d eax,[ReceiveDescriptor] + cmp_d eax,[OriginalReceiveDescriptor] + jnz @@fail + mov_d eax,[ReceiveDescriptor+4] + cmp_d eax,[OriginalReceiveDescriptor+4] + jnz @@fail + clc + ret + +@@fail: stc + ret + +Verify_Selectors endp + + + + +;************************************************************************************************* +;* Listen_For_Packet -- Start IPX listening for an incoming packet * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +Listen_For_Packet proc near uses eax ebx ecx edx esi edi ebp ds es fs gs + + call Verify_Selectors + bcs @@fail + + call Init_Receive_ECB + + mov ax,@data + mov ds,ax + mov es,[ReceiveBufferSelector] + mov si,[ReceiveECBOffset] + mov bx,IPX_LISTEN_FOR_PACKET + push ds + call Call_IPX + pop ds + ret + +@@fail: mov ax,@data + mov ds,ax + mov [ListeningFailed],1 + ret + + +Listen_For_Packet endp + + + + + +;************************************************************************************************* +;* Call_IPX -- Make a call to the IPX entry point * +;* * +;* * +;* INPUT: registers set up for the IPX call * +;* * +;* OUTPUT: registers returned by IPX * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* +Call_IPX proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + push bp + mov bp,offset IPXCallOffset + call dword ptr gs:[bp] + pop bp + + pop gs + ret + +Call_IPX endp + + + + + + + +;************************************************************************************************* +;* Call_DOS -- use DPMI to call a DOS interrupt service * +;* * +;* * +;* INPUT: registers set up for the DOS call * +;* * +;* OUTPUT: registers returned by DOS * +;* * +;* HISTORY: * +;* 01/29/96 ST: Created. * +;*===============================================================================================* + +Call_DOS proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + ; + ; Dump the registers first so we can use them + ; + mov_d gs:[RegisterDump],edi + mov_d gs:[RegisterDump+4],esi + mov_d gs:[RegisterDump+8],ebp + mov_d gs:[RegisterDump+10h],ebx + mov_d gs:[RegisterDump+14h],edx + mov_d gs:[RegisterDump+18h],ecx + mov_d gs:[RegisterDump+1ch],eax + + ; + ; Dump the flags + ; + pushf + pop gs:[word ptr RegisterDump+20h] + + ; + ; Save the segment registers + ; + mov_w gs:[RegisterDump+22h],es + mov_w gs:[RegisterDump+24h],ds + mov_w gs:[RegisterDump+26h],fs + mov_w gs:[RegisterDump+28h],gs + + ; + ; Set up stack addr to zero - DPMI will supply a real mode stack + ; + xor ax,ax + mov_w gs:[RegisterDump+2eh],ax ;sp + mov_w gs:[RegisterDump+30h],ax ;ss + + mov ax,DPMI_CALL_REAL_INT ; Simulate real mode interrupt + xor bh,bh ; flags - should be zero + mov bl,21h ; interrupt number + mov ecx,0 ; number of words to copy from the protected mode stack + mov di,offset RegisterDump + push gs + pop es + push gs + int 31h ;DPMI interrupt + pop gs + + ; + ; Get the return registers + ; + mov_d edi,gs:[RegisterDump] + mov_d esi,gs:[RegisterDump+4] + mov_d ebp,gs:[RegisterDump+8] + mov_d ebx,gs:[RegisterDump+10h] + mov_d edx,gs:[RegisterDump+14h] + mov_d ecx,gs:[RegisterDump+18h] + mov_d eax,gs:[RegisterDump+1ch] + mov_w es,gs:[RegisterDump+22h] + mov_w ds,gs:[RegisterDump+24h] + pop gs + + ret + + +Call_DOS endp + + + + + end + + + + + + diff --git a/IPX/IPX16C.CPP b/IPX/IPX16C.CPP new file mode 100644 index 0000000..f799eba --- /dev/null +++ b/IPX/IPX16C.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + + + + +#include + +extern "C"{ + extern BOOL FAR __pascal ASM_IPX_Initialise(void); + extern BOOL FAR __pascal ASM_IPX_Uninitialise(void); +} + + +extern "C"{ + BOOL FAR __pascal _export IPX_Initialise(int); + void FAR __pascal _export IPX_Uninitialise(void); +} + + + +int CALLBACK LibMain (HANDLE, WORD, WORD, LPSTR) +{ + return (1); +} + + + +BOOL FAR __pascal _export IPX_Initialise(int) +{ + + return (ASM_IPX_Initialise()); + +} + + + +void FAR __pascal _export IPX_Uninitialise(void) +{ + + ASM_IPX_Uninitialise(); + +} + + + + diff --git a/IPX/IPXTEST.CPP b/IPX/IPXTEST.CPP new file mode 100644 index 0000000..9a52b7a --- /dev/null +++ b/IPX/IPXTEST.CPP @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#include + + + +int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char *, int) +{ + + + + +} + + + diff --git a/IPX/MAKE.BAK b/IPX/MAKE.BAK new file mode 100644 index 0000000..7e3551b --- /dev/null +++ b/IPX/MAKE.BAK @@ -0,0 +1,8 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +wpp -ml -bd -zu -bt=windows -0 ipx16c.cpp +tasm /ml ipx16a.asm +wlink @ipx16.lnk +wlib -c -n wwipx16.lib +wwipx16.dll +call setinc2.bat diff --git a/IPX/MAKE.BAT b/IPX/MAKE.BAT new file mode 100644 index 0000000..0744f2e --- /dev/null +++ b/IPX/MAKE.BAT @@ -0,0 +1,8 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +wpp -s -ml -bd -zu -bt=windows -0 ipx16c.cpp +tasm /ml ipx16a.asm +wlink @ipx16.lnk +wlib -c -n wwipx16.lib +wwipx16.dll +call setinc2.bat diff --git a/IPX/MAKEFILE b/IPX/MAKEFILE new file mode 100644 index 0000000..6e86878 --- /dev/null +++ b/IPX/MAKEFILE @@ -0,0 +1,32 @@ +# +# 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 . +# + +.nocheck + +ALL: thipx32.dll & + thipx16.dll + +thipx32.dll: thipx.thk thipx32c.cpp thmap32.asm thipx32.lnk + maketh32 + +thipx16.dll: thipx.thk thipx16a.asm thipx16c.cpp thmap16.asm thipx16.lnk + maketh16 + + + + diff --git a/IPX/MAKETH16.BAK b/IPX/MAKETH16.BAK new file mode 100644 index 0000000..99e5610 --- /dev/null +++ b/IPX/MAKETH16.BAK @@ -0,0 +1,12 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +thunk Thipx.thk +wpp -ml -bd -zu -bt=windows -0 thipx16c.cpp +ml /c /W3 ipx16a.asm +ml /DIS_16 /c /W3 /Fothipx16a.obj thipx.asm +ml /c /W3 thmap16.asm +ml /c /W3 ipx16a.asm +wlink @thipx16.lnk +rc -40 thipx16.dll +call setinc2.bat diff --git a/IPX/MAKETH16.BAT b/IPX/MAKETH16.BAT new file mode 100644 index 0000000..3c2460a --- /dev/null +++ b/IPX/MAKETH16.BAT @@ -0,0 +1,11 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +thunk Thipx.thk +wpp -ml -bd -zu -bt=windows -0 thipx16c.cpp +ml /c /W3 ipx16a.asm +ml /DIS_16 /c /W3 /Fothipx16a.obj thipx.asm +ml /c /W3 thmap16.asm +wlink @thipx16.lnk +rc -40 thipx16.dll +call setinc2.bat diff --git a/IPX/MAKETH32.BAK b/IPX/MAKETH32.BAK new file mode 100644 index 0000000..5471020 --- /dev/null +++ b/IPX/MAKETH32.BAK @@ -0,0 +1,7 @@ +rem thunk Thipx.thk +rem fixthunk +wpp386 -s -mf -3s -bt=nt -bd -zu -4 thipx32c.cpp +ml /DIS_32 /Zf /Fl /c /W3 /Fothipx32a.obj thipx.asm +ml /c /W3 thmap32.asm +wlink @thipx32.lnk +wlib -c -n wwipx32.lib +thipx32.dll diff --git a/IPX/MAKETH32.BAT b/IPX/MAKETH32.BAT new file mode 100644 index 0000000..8827fab --- /dev/null +++ b/IPX/MAKETH32.BAT @@ -0,0 +1,7 @@ +thunk Thipx.thk +rem fixthunk +wpp386 -s -mf -3s -bt=nt -bd -zu -4 thipx32c.cpp +ml /DIS_32 /Zf /Fl /c /W3 /Fothipx32a.obj thipx.asm +ml /c /W3 thmap32.asm +wlink @thipx32.lnk +wlib -c -n wwipx32.lib +thipx32.dll diff --git a/IPX/OK/FIXTHUNK.CPP b/IPX/OK/FIXTHUNK.CPP new file mode 100644 index 0000000..683f849 --- /dev/null +++ b/IPX/OK/FIXTHUNK.CPP @@ -0,0 +1,145 @@ +/* +** 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 . +*/ + + +#include +#include +#include +#include +#include +#include + + +unsigned char thunk_file[1000000]; +unsigned char thunk_file_out[100050]; + + +/*********************************************************************************************** + * Search_For_String -- search for a string of chars within a buffer * + * * + * * + * * + * INPUT: string * + * ptr to buffer to search in * + * length of buffer * + * * + * OUTPUT: ptr to string in buffer or NULL if not found * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:42PM ST : Created * + *=============================================================================================*/ + +char *Search_For_String (char *string , char *buffer_ptr , int buffer_length) +{ + int j; + int string_length=strlen(string); + + for (int i=0 ; i. +; + + + + IDEAL + P386N + LOCALS ?? + + + + segment realcode para public USE16 'code' + + +global PASCAL ASM_IPX_INITIALISE:far +global PASCAL ASM_IPX_UNINITIALISE:far + + +proc ASM_IPX_INITIALISE PASCAL FAR + + int 3 + mov ax,7a00h + int 2fh + and eax,0ffh + cmp al,-1 + setz al + ret + +endp ASM_IPX_INITIALISE + + + + +proc ASM_IPX_UNINITIALISE PASCAL FAR + + ret + +endp ASM_IPX_UNINITIALISE + + + + ends + + end + + + diff --git a/IPX/OK/IPX16.BAK b/IPX/OK/IPX16.BAK new file mode 100644 index 0000000..8ae28ec --- /dev/null +++ b/IPX/OK/IPX16.BAK @@ -0,0 +1,13 @@ +system windows_dll +option redefsok +option quiet +option map +option eliminate +option caseexact +option one + +debug all +file ipx16a.obj +file ipx16c.obj + +name wwipx16.dll diff --git a/IPX/OK/IPX16.H b/IPX/OK/IPX16.H new file mode 100644 index 0000000..b6aa563 --- /dev/null +++ b/IPX/OK/IPX16.H @@ -0,0 +1,25 @@ +/* +** 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 . +*/ + + + +extern "C"{ + BOOL FAR __cdecl IPX_Initialise(int); +} + + diff --git a/IPX/OK/IPX16A.ASM b/IPX/OK/IPX16A.ASM new file mode 100644 index 0000000..1e2ea16 --- /dev/null +++ b/IPX/OK/IPX16A.ASM @@ -0,0 +1,1450 @@ +; +; 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 . +; + + + + .8086 + + + .model large + option segment:USE16 + option readonly + option oldstructs + + + assume ds:@data + assume es:nothing + + +RECEIVE_BUFFER_LENGTH =1024 +MAX_RECEIVE_BUFFERS =32 + + +externdef GLOBALDOSALLOC:far +externdef GLOBALDOSFREE:far + + + .data + +IPXCallOffset dw 0 +IPXCallSegment dw 0 +RealSegment dw 0 +PseudoES dw 0 + + + +RegisterDump db 32h dup (0) + +MyNetworkNumber db 4 dup (?) +MyNodeAddress db 6 dup (?) +MySocket dw ? + +ReceiveBufferSegment dw ? +ReceiveBufferSelector dw ? +ReceiveECBOffset dw ? +CurrentReceiveBuffer dw ? +LastPassedReceiveBuffer dw ? +RealModeCallbackSegment dw ? +RealModeCallbackOffset dw ? + +CallbackRegisterDump db 32h dup (0) + +Listening db ? +NoReenter db 0 +IPXHold db 0 + + + +OFFS =0 +SEGM =2 + + +;--------------------------------------------------------------------------- +;These defines are for the IPX functions. The function number is specified +;by placing it in BX when IPX is called. There are two ways to invoke IPX: +;use interrupt 0x7a, or use a function whose address is obtained by calling +;interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +;This is the preferred method, since other apps are known to use int 0x7a. +;--------------------------------------------------------------------------- +IPX_OPEN_SOCKET = 0000h +IPX_CLOSE_SOCKET = 0001h +IPX_GET_LOCAL_TARGET = 0002h +IPX_SEND_PACKET = 0003h +IPX_LISTEN_FOR_PACKET = 0004h +IPX_SCHEDULE_EVENT = 0005h +IPX_CANCEL_EVENT = 0006h +IPX_GET_INTERVAL_MARKER = 0008h +IPX_GET_INTERNETWORK_ADDRESS = 0009h +IPX_RELINQUISH_CONTROL = 000Ah +IPX_DISCONNECT_FROM_TARGET = 000Bh + +;/*--------------------------------------------------------------------------- +;These defines are for various IPX error codes: +;---------------------------------------------------------------------------*/ +IPXERR_CONNECTION_SEVERED = 00ech +IPXERR_CONNECTION_FAILED = 00edh +IPXERR_NO_CONNECTION = 00eeh +IPXERR_CONNECTION_TABLE_FULL = 00efh +IPXERR_NO_CANCEL_ECB = 00f9h +IPXERR_NO_PATH = 00fah +IPXERR_ECB_INACTIVE = 00fch +IPXERR_INVALID_PACKET_LENGTH = 00fdh +IPXERR_SOCKET_TABLE_FULL = 00feh +IPXERR_SOCKET_ERROR = 00ffh + +;/*--------------------------------------------------------------------------- +;These defines are for various interrupt vectors and DPMI functions: +;---------------------------------------------------------------------------*/ +IPX_INT = 007ah +DPMI_INT = 0031h +DPMI_ALLOC_DOS_MEM = 0100h +DPMI_FREE_DOS_MEM = 0101h +DPMI_CALL_REAL_INT = 0300h +DPMI_CALL_REAL_PROC = 0301h +DPMI_ALLOCATE_CALLBACK = 0303h +DPMI_RELEASE_CALLBACK = 0304h +DPMI_LOCK_MEM = 0600h +DPMI_UNLOCK_MEM = 0601h + + + + + +request_buffer struct + len word ? + buffer_type byte ? + connect_number byte ? +request_buffer ends + + + +request_local_target_buffer struct + lt_network_number db ?,?,?,? + lt_physical_node db ?,?,?,?,?,? + lt_socket dw ? +request_local_target_buffer ends + + +local_target_reply_buffer struct + lt_local_target db ?,?,?,?,?,? +local_target_reply_buffer ends + + + +reply_buffer struct + lem word ? + network_number byte ?,?,?,? + physical_node byte ?,?,?,?,?,? + server_socket word ? +reply_buffer ends + + + + + +userid_buffer struct + struct_len word ? + object_id byte ?,?,?,? + object_type byte ?,? + object_name byte 48 dup(?) + login_time byte 7 dup (?) + reserved word ? +userid_buffer ends + + .code + + .386 + + include + +externdef pascal _IPX_Initiialise:far16 +externdef pascal _IPX_Uninitialise:far16 + + +_IPX_Initialise proc far pascal + + push ebx + push ecx + push edx + push esi + push edi + push ebp + push ds + push es + push fs + push gs + + mov ax,7a00h + int 2fh + and eax,0ffh + cmp al,-1 + setz al + + test al,al + jz @f + + mov bx,@data + mov ds,bx + mov [IPXCallSegment],es + mov [IPXCallOffset],di + +@@: pop gs + pop fs + pop es + pop ds + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + ret + +_IPX_Initialise endp + + + +_IPX_Open_Socket95 proc far pascal uses bx cx dx si di ds es fs gs, + socket:word + + mov bx,@data + mov ds,bx + mov bx,IPX_OPEN_SOCKET ;open socket + mov_w dx,[socket] ;socket number + mov_w [MySocket],dx ;save it for later + mov ax,0ffh ;long lived + + call Call_IPX + + ret + +_IPX_Open_Socket95 endp + + + + +_IPX_Close_Socket95 proc far pascal uses ax bx cx dx si di ds es fs gs, + socket:word + + mov bx,1 + mov_w dx,[socket] + + call Call_IPX + + ret + +_IPX_Close_Socket95 endp + + + + + +_IPX_Uninitialise proc far pascal + + ;int 3 + ret + +_IPX_Uninitialise endp + + + + + +_IPX_Get_Connection_Number95 proc far pascal uses bx cx dx si di ds es fs gs + + mov ax,0dc00h + call Call_DOS + and eax,255 + ret + +_IPX_Get_Connection_Number95 endp + + + + + + +_IPX_Get_Internet_Address95 proc far pascal uses bx cx dx si di ds es fs gs, + connection_number :dword, + network_number_off :word, + network_number_seg :word, + physical_node_off :word, + physical_node_seg :word + + local selector:word + local segmento:word + + ret +ifdef cuts ;(unfinished) + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + int 3 + xor ax,ax + mov bx,(sizeof reply_buffer + sizeof request_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@dpmi_error + + mov [segmento],dx + mov [selector],ax + mov fs,dx + xor di,di + + mov_w fs:[di.len],2 + mov_b fs:[di.buffer_type],13h + mov_b al,[connection_number] + mov_b fs:[di.connect_number],al + + mov di,sizeof request_buffer + mov fs:[di.len],12 + + + mov ax,0e300h + mov ds,[segmento] + mov es,[segmento] + xor si,si + + pusha + call Call_DOS + popa + + les di,dword ptr [network_number_off] + mov ds,[selector] + mov si,sizeof request_buffer + network_number + movsd + + les di,dword ptr [physical_node_off] + mov si,sizeof request_buffer + physical_node + movsd + movsw + + ; + ; Save it here for posterity (or perhaps later use) + ; + mov di,offset MyNetworkNumber + mov ax,@data + mov es,ax + mov ds,[selector] + mov si,sizeof request_buffer + network_number + movsd + + mov di,offset MyNodeAddress + mov si,sizeof request_buffer + physical_node + movsd + movsw + + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bx + call GLOBALDOSFREE + + xor ax,ax + ret + +@@dpmi_error: mov ax,-1 + ret +endif + +_IPX_Get_Internet_Address95 endp + + + + +; Never called!! + +_IPX_Get_User_ID95 proc far pascal uses bx cx dx si di ds es fs gs, + connection_number:dword, + user_id_off:word, + user_id_seg:word + + local segmento:word + local selector:word + + cmp [connection_number],0 + jz @@return_error + + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + xor ax,ax + mov bx,(sizeof reply_buffer + sizeof request_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@return_error + + mov [segmento],dx + mov [selector],ax + mov fs,dx + xor di,di + mov ax,@data + mov ds,ax + + mov_w fs:[di.len],2 + mov_b fs:[di.buffer_type],16h + mov_b al,[connection_number] + mov_b fs:[di.connect_number],al + + mov di,sizeof request_buffer + mov fs:[di.struct_len],sizeof userid_buffer-2 + + mov ax,0e300h + mov ds,[segmento] + mov es,[segmento] + xor si,si + + pusha + call Call_DOS + popa + + les di,dword ptr [user_id_off] + mov ds,[selector] + mov si,sizeof request_buffer + object_name + mov cx,48/4 + rep movsd + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bx + call GLOBALDOSFREE + + xor ax,ax + ret + +@@return_error: mov ax,-1 + ret + +_IPX_Get_User_ID95 endp + + + + +;--------------------------------------------------------------------------- +;This is the IPX Packet structure. It's followed by the data itself, which +;can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +;field; annotation of 'APP' means the application must set the field. +;NOTE: All header fields are ordered high-byte,low-byte. +;---------------------------------------------------------------------------*/ +IPXHeaderType struct + + CheckSum dw ? ; IPX: Not used; always 0xffff + IPXLength dw ? ; IPX: Total size, incl header & data + TransportControl db ? ; IPX: # bridges message crossed + PacketType db ? ; APP: Set to 4 for IPX (5 for SPX) + DestNetworkNumber db ?,?,?,? ; APP: destination Network Number + DestNetworkNode db ?,?,?,?,?,? ; APP: destination Node Address + DestNetworkSocket dw ? ; APP: destination Socket Number + SourceNetworkNumber db ?,?,?,? ; IPX: source Network Number + SourceNetworkNode db ?,?,?,?,?,? ; IPX: source Node Address + SourceNetworkSocket dw ? ; IPX: source Socket Number + +IPXHeaderType ends + + + + +;/*--------------------------------------------------------------------------- +;This is the IPX Event Control Block. It serves as a communications area +;between IPX and the application for a single IPX operation. You should set +;up a separate ECB for each IPX operation you perform. +;---------------------------------------------------------------------------*/ + +ECB struct + + Link_Address dd ? + Event_Service_Routine dd ? ; APP: event handler (NULL=none) + InUse db ? ; IPX: 0 = event complete + CompletionCode db ? ; IPX: event's return code + SocketNumber dw ? ; APP: socket to send data through + ConnectionID dw ? ; returned by Listen (???) + RestOfWorkspace dw ? + DriverWorkspace db 12 dup (?) + ImmediateAddress db 6 dup (?) ; returned by Get_Local_Target + PacketCount dw ? + PacketAddress0 dd ? + PacketLength0 dw ? + PacketAddress1 dd ? + PacketLength1 dw ? + +ECB ends + + +SEND_ECB_OFFSET =0 +SEND_HEADER_OFFSET =(sizeof ECB +3) AND 0fffch +SEND_BUFFER_OFFSET =(SEND_HEADER_OFFSET + sizeof IPXHeaderType +3) AND 0fffch + + +_IPX_Send_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_address :far ptr byte, + send_buffer :far ptr byte, + send_buffer_len :word + + local selector :word + local segmento :word + local dos_send_ecb :far ptr ECB + local dos_send_header :far ptr IPXHeaderType + local dos_send_buffer :far ptr byte + local completion_code :word + + ; + ; Allocate required DOS memory + ; + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@error + mov [segmento],dx + mov [selector],ax + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the send buffer + ; + mov cx,546 + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: +; mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe) + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[selector] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ; + ; Fill in the address field + ; + lds si,[send_address] + mov eax,[si] + mov_d es:[di.ImmediateAddress],eax + mov ax,[si+4] + mov_w es:[di.ImmediateAddress+4],ax + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the nwtowrk number and node address + ; + mov_d eax,fs:[MyNetworkNumber] + mov_d es:[di.DestNetworkNumber],eax + mov_d eax,fs:[MyNodeAddress] + mov_d es:[di.DestNetworkNode],eax + mov_w ax,fs:[MyNodeAddress+4] + mov_w es:[di.DestNetworkNode+4],ax + + ; + ; Send that sucker! + ; + mov es,[selector] + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + push fs + call Call_IPX + pop fs + popa + mov fs:[PseudoES],0 + + + ; + ; Wait for the send to finish + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + mov bx,IPX_RELINQUISH_CONTROL + save bp + call Call_IPX + restore bp + jmp @@wait_send_loop + + + ; + ; Get the completion code + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + cmp [completion_code],0 + jnz @@error + +@@success: mov ax,1 + ret + + +@@error: xor ax,ax + ret + + +_IPX_Send_Packet95 endp + + + + + + +_IPX_Broadcast_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_buffer :far ptr byte, + send_buffer_len :word + + local selector :word + local segmento :word + local dos_send_ecb :far ptr ECB + local dos_send_header :far ptr IPXHeaderType + local dos_send_buffer :far ptr byte + local completion_code :word + + ; + ; Allocate required DOS memory + ; + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@error + mov [segmento],dx + mov [selector],ax + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the send buffer + ; + mov cx,546 + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: +; mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe) + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[selector] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ; + ; Fill in the address field + ; + mov_d es:[di.ImmediateAddress],0ffffffffh + mov_w es:[di.ImmediateAddress+4],0ffffh + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the nwtowrk number and node address + ; + mov_d es:[di.DestNetworkNumber],0ffffffffh + mov_d es:[di.DestNetworkNode],0ffffffffh + mov_w es:[di.DestNetworkNode+4],0ffffh + + ; + ; Send that sucker! + ; + mov es,[selector] + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + push fs + call Call_IPX + pop fs + popa + mov fs:[PseudoES],0 + + ; + ; Wait for the send to finish + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + mov bx,IPX_RELINQUISH_CONTROL + save bp + call Call_IPX + restore bp + jmp @@wait_send_loop + + + + ; + ; Get the completion code + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + cmp [completion_code],0 + jnz @@error + + +@@success: mov ax,1 + ret + + +@@error: xor ax,ax + ret + + +_IPX_Broadcast_Packet95 endp + + + + + + + +_IPX_Get_Local_Target95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + dest_network :far ptr byte, + dest_node :far ptr byte, + socket :word, + bridge_address :far ptr byte + + + local segmento :word + local selector :word + local result_code :word + + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + xor ax,ax + mov bx,(sizeof local_target_reply_buffer + \ + sizeof request_local_target_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@return_error + + mov [segmento],dx + mov [selector],ax + mov fs,ax + xor di,di + + ; + ; Init the request structure + ; + lds si,[dest_network] + mov_d eax,[si] + mov_d fs:[di.lt_network_number],eax + lds si,[dest_node] + mov eax,[si] + mov_d fs:[di.lt_physical_node],eax + mov_w ax,[si+4] + mov_w fs:[di.lt_physical_node+4],ax + mov ax,[socket] + mov fs:[di.lt_socket],ax + + mov bx,IPX_GET_LOCAL_TARGET + mov ax,[selector] + mov ds,ax + xor si,si + mov es,ax + mov di,sizeof request_local_target_buffer + + push bp + call Call_IPX + pop bp + + mov [result_code],ax + + mov ds,[selector] + mov si,sizeof request_local_target_buffer + lt_local_target + les di,[bridge_address] + movsd + movsw + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + ; + ; Return the IPX result code + ; + mov ax,[result_code] + ret + +@@return_error: mov ax,-1 + ret + +_IPX_Get_Local_Target95 endp + + + + +_IPX_Get_Outstanding_Buffer95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs, + copy_receive_buffer:far ptr byte + + mov ax,@data + mov ds,ax + + xor ax,ax + mov si,[LastPassedReceiveBuffer] + cmp si,[CurrentReceiveBuffer] + jz @@done + + push ds + mov cx,RECEIVE_BUFFER_LENGTH/4 + mov ds,[ReceiveBufferSelector] + les di,[copy_receive_buffer] + rep movsd + pop ds + + cmp si,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor si,si +@@no_wrap: mov [LastPassedReceiveBuffer],si + + mov ax,1 + +@@done: ret + +_IPX_Get_Outstanding_Buffer95 endp + + + + +Receive_Callback proc far + + pushf + cli + cld + pushad + push ds + push es + push fs + push gs + mov ax,@data + mov ds,ax + cmp [NoReenter],0 + jnz @@out + mov [NoReenter],1 + mov ax,[CurrentReceiveBuffer] + + add ax,RECEIVE_BUFFER_LENGTH + cmp ax,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor ax,ax +@@no_wrap: mov [CurrentReceiveBuffer],ax + + call Listen_For_Packet + mov ax,@data + mov ds,ax + mov [NoReenter],0 + +@@out: pop gs + pop fs + pop es + pop ds + popad + popf + ret + +Receive_Callback endp + + + + + +Init_Receive_ECB proc near uses eax ebx ecx edx esi edi ebp ds es + + mov ax,@data + mov ds,ax + mov es,[ReceiveBufferSelector] + + ; + ; Clear out the ECB + ; + mov di,[ReceiveECBOffset] + mov cx,sizeof ECB + xor al,al + rep stosb + + +; +; Set up the ECB +; + ; + ;General ECB data + mov di,[ReceiveECBOffset] + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + + ; + ; Packet address for IPX header + mov ax,[CurrentReceiveBuffer] + mov bx,[ReceiveBufferSelector] + mov_w es:[di.PacketAddress0+OFFS],ax + mov_w es:[di.PacketAddress0+SEGM],bx + mov es:[di.PacketLength0],sizeof IPXHeaderType + + ; + ; Packet address for receive buffer + mov ax,[CurrentReceiveBuffer] + add ax,sizeof IPXHeaderType + mov_w es:[di.PacketAddress1+OFFS],ax + mov_w es:[di.PacketAddress1+SEGM],bx + mov es:[di.PacketLength1],546 + + ; + ; Set up the callback address + mov ax,[RealModeCallbackOffset] + mov_w es:[di.Event_Service_Routine+OFFS],offset Receive_Callback ;ax + mov ax,[RealModeCallbackSegment] + mov_w es:[di.Event_Service_Routine+SEGM],cs ;ax + + ret + +Init_Receive_ECB endp + + +_IPX_Start_Listening95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs + + mov ax,@data + mov ds,ax + cmp [Listening],0 + jnz @@restart ;already listening + + ; + ; Allocate and lock DOS memory for listen + ; ECB and receive buffers + ; + mov bx,(RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + sizeof ECB + 15) + xor ax,ax + save bp,ds + push ax + push bx + call GLOBALDOSALLOC + restore bp,ds + test ax,ax + jz @@error + + mov [ReceiveBufferSegment],dx + mov [ReceiveBufferSelector],ax + + ; + ; Set up pointers to the DOS memory + ; + mov [ReceiveECBOffset],RECEIVE_BUFFER_LENGTH * MAX_RECEIVE_BUFFERS + mov [CurrentReceiveBuffer],0 ;1st receive buffer + mov [LastPassedReceiveBuffer],0 + + ; + ; Set up a real mode callback function + ; + ;mov ax,DPMI_ALLOCATE_CALLBACK + ;mov si,offset Receive_Callback + ;mov di,offset CallbackRegisterDump + ;push cs + ;pop ds + ;mov bx,@data + ;mov es,bx + ;save bp + ;int 31h + ;restore bp + ;bcs @@error + + mov ax,@data + mov ds,ax + mov [RealModeCallbackSegment],cx + mov [RealModeCallbackOffset],dx + +@@restart: save ds + call Listen_For_Packet + restore ds + mov [Listening],1 + +@@out: mov ax,1 + ret + +@@error: xor ax,ax + ret + +_IPX_Start_Listening95 endp + + + + +_IPX_Shut_Down95 proc far pascal uses eax ebx ecx edx esi edi ebp ds es fs gs + + mov ax,@data + mov ds,ax + cmp [Listening],0 + jz @@out + + ; + ; Stop listening for a packet + ; + mov es,[ReceiveBufferSelector] + mov si,[ReceiveECBOffset] + mov bx,IPX_CANCEL_EVENT + save bp,ds + call Call_IPX + restore bp,ds + + ; + ; Remove the real mode callback function + ; + ;mov cx,[RealModeCallbackSegment] + ;mov dx,[RealModeCallbackOffset] + ;mov ax,DPMI_RELEASE_CALLBACK + ;save bp,ds + ;int 31h + ;restore bp,ds + + ; + ; Free the DOS memory + ; + mov ax,@data + mov ds,ax + mov es,ax + mov ax,[ReceiveBufferSelector] + save bp,ds + push ax + call GLOBALDOSFREE + restore bp,ds + + mov [Listening],0 + +@@out: ret + +_IPX_Shut_Down95 endp + + + + +Listen_For_Packet proc near uses ebx ecx edx esi edi ebp ds es fs gs + + call Init_Receive_ECB + + mov ax,@data + mov ds,ax + mov es,[ReceiveBufferSelector] + mov si,[ReceiveECBOffset] + mov bx,IPX_LISTEN_FOR_PACKET + save ds + call Call_IPX + restore ds + mov [PseudoES],0 + + ret + +Listen_For_Packet endp + + + + + + +Call_IPX proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + push bp + mov bp,offset IPXCallOffset + call dword ptr gs:[bp] + pop bp + + pop gs + ret + + + + + + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + ; + ; Prevent reenterancy + ; +@@hold: ;cmp gs:[IPXHold],0 + ;jnz @@hold + + mov gs:[IPXHold],1 + + ; + ; Dump the registers first so we can use them + ; + mov_d gs:[RegisterDump+0ch],0 + mov_d gs:[RegisterDump],edi + mov_d gs:[RegisterDump+4],esi + mov_d gs:[RegisterDump+8],ebp + mov_d gs:[RegisterDump+10h],ebx + mov_d gs:[RegisterDump+14h],edx + mov_d gs:[RegisterDump+18h],ecx + mov_d gs:[RegisterDump+1ch],eax + + push gs + xor ax,ax + push ax + mov ax,4096 ;4k real mode stack + push ax + call GLOBALDOSALLOC + pop gs + mov bx,4094 ;stack ptr + test ax,ax + jnz @f + xor bx,bx + + ; + ; Set up stack addr to zero - DPMI will supply a real mode stack + ; +@@: mov_w gs:[RegisterDump+2eh],bx ;sp + mov_w gs:[RegisterDump+30h],dx ;ss + save ax ;save selector so we can free the memory later + + + ; + ; Dump the flags + ; + pushf + pop gs:[word ptr RegisterDump+20h] + + ; + ; Set up all segment register areas to point to our real mode segment + ; + mov_w gs:[RegisterDump+22h],es + mov_w gs:[RegisterDump+24h],ds + mov_w gs:[RegisterDump+26h],fs + mov_w gs:[RegisterDump+28h],gs + + cmp gs:[PseudoES],0 + jz @f + mov ax,gs:[PseudoES] + mov_w gs:[RegisterDump+22h],ax +@@: + + + ; + ; Set up the address to call + ; + mov ax,gs:[IPXCallSegment] + mov_w gs:[RegisterDump+2ch],ax + mov ax,gs:[IPXCallOffset] + mov_w gs:[RegisterDump+2ah],ax + + mov ax,DPMI_CALL_REAL_INT ; Call real mode procedure with far return frame + xor bh,bh ; flags - should be zero + mov bl,07ah ; IPX interrupt + mov ecx,0 ;512 ; number of words to copy from the protected mode stack + mov di,offset RegisterDump + push gs + pop es + save gs + ;push ss + ;push sp + int 31h ;DPMI interrupt + + ;pop ax + ;pop bx + ;pushf + ;pop cx + ;cli + ;mov sp,ax + ;mov ss,bx + ;add sp,2 + ;push cx + ;popf + + restore gs + + mov ax,@data + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + call GLOBALDOSFREE + + + mov_d eax,gs:[RegisterDump+1ch] + mov gs:[IPXHold],0 + pop gs + + ret + + +Call_IPX endp + + + + + + + + +Call_DOS proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + ; + ; Dump the registers first so we can use them + ; + mov_d gs:[RegisterDump],edi + mov_d gs:[RegisterDump+4],esi + mov_d gs:[RegisterDump+8],ebp + mov_d gs:[RegisterDump+10h],ebx + mov_d gs:[RegisterDump+14h],edx + mov_d gs:[RegisterDump+18h],ecx + mov_d gs:[RegisterDump+1ch],eax + + ; + ; Dump the flags + ; + pushf + pop gs:[word ptr RegisterDump+20h] + + ; + ; Set up all segment register areas to point to our real mode segment + ; + mov_w gs:[RegisterDump+22h],es + mov_w gs:[RegisterDump+24h],ds + mov_w gs:[RegisterDump+26h],fs + mov_w gs:[RegisterDump+28h],gs + + ; + ; Set up stack addr to zero - DPMI will supply a real mode stack + ; + xor ax,ax + mov_w gs:[RegisterDump+2eh],ax ;sp + mov_w gs:[RegisterDump+30h],ax ;ss + + mov ax,DPMI_CALL_REAL_INT; Simulate real mode interrupt + xor bh,bh ; flags - should be zero + mov bl,21h ; interrupt number + mov ecx,0 ;512 ; number of words to copy from the protected mode stack + mov di,offset RegisterDump + push gs + pop es + save gs + int 31h ;DPMI interrupt + restore gs + + mov_d edi,gs:[RegisterDump] + mov_d esi,gs:[RegisterDump+4] + mov_d ebp,gs:[RegisterDump+8] + mov_d ebx,gs:[RegisterDump+10h] + mov_d edx,gs:[RegisterDump+14h] + mov_d ecx,gs:[RegisterDump+18h] + mov_d eax,gs:[RegisterDump+1ch] + mov_w es,gs:[RegisterDump+22h] + mov_w ds,gs:[RegisterDump+24h] + pop gs + + ret + + +Call_DOS endp + + + + + end + + + + + + diff --git a/IPX/OK/IPX16C.CPP b/IPX/OK/IPX16C.CPP new file mode 100644 index 0000000..f799eba --- /dev/null +++ b/IPX/OK/IPX16C.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + + + + +#include + +extern "C"{ + extern BOOL FAR __pascal ASM_IPX_Initialise(void); + extern BOOL FAR __pascal ASM_IPX_Uninitialise(void); +} + + +extern "C"{ + BOOL FAR __pascal _export IPX_Initialise(int); + void FAR __pascal _export IPX_Uninitialise(void); +} + + + +int CALLBACK LibMain (HANDLE, WORD, WORD, LPSTR) +{ + return (1); +} + + + +BOOL FAR __pascal _export IPX_Initialise(int) +{ + + return (ASM_IPX_Initialise()); + +} + + + +void FAR __pascal _export IPX_Uninitialise(void) +{ + + ASM_IPX_Uninitialise(); + +} + + + + diff --git a/IPX/OK/IPXTEST.CPP b/IPX/OK/IPXTEST.CPP new file mode 100644 index 0000000..0879283 --- /dev/null +++ b/IPX/OK/IPXTEST.CPP @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include + + + +int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char *, int) +{ + + + + +} + + + diff --git a/IPX/OK/MAKE.BAK b/IPX/OK/MAKE.BAK new file mode 100644 index 0000000..7e3551b --- /dev/null +++ b/IPX/OK/MAKE.BAK @@ -0,0 +1,8 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +wpp -ml -bd -zu -bt=windows -0 ipx16c.cpp +tasm /ml ipx16a.asm +wlink @ipx16.lnk +wlib -c -n wwipx16.lib +wwipx16.dll +call setinc2.bat diff --git a/IPX/OK/MAKE.BAT b/IPX/OK/MAKE.BAT new file mode 100644 index 0000000..0744f2e --- /dev/null +++ b/IPX/OK/MAKE.BAT @@ -0,0 +1,8 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +wpp -s -ml -bd -zu -bt=windows -0 ipx16c.cpp +tasm /ml ipx16a.asm +wlink @ipx16.lnk +wlib -c -n wwipx16.lib +wwipx16.dll +call setinc2.bat diff --git a/IPX/OK/MAKETH16.BAK b/IPX/OK/MAKETH16.BAK new file mode 100644 index 0000000..99e5610 --- /dev/null +++ b/IPX/OK/MAKETH16.BAK @@ -0,0 +1,12 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +thunk Thipx.thk +wpp -ml -bd -zu -bt=windows -0 thipx16c.cpp +ml /c /W3 ipx16a.asm +ml /DIS_16 /c /W3 /Fothipx16a.obj thipx.asm +ml /c /W3 thmap16.asm +ml /c /W3 ipx16a.asm +wlink @thipx16.lnk +rc -40 thipx16.dll +call setinc2.bat diff --git a/IPX/OK/MAKETH16.BAT b/IPX/OK/MAKETH16.BAT new file mode 100644 index 0000000..3c2460a --- /dev/null +++ b/IPX/OK/MAKETH16.BAT @@ -0,0 +1,11 @@ +set include >temp.txt +set include=c:\wat\h;c:\wat\h\win +copy setinc.bat+temp.txt setinc2.bat +thunk Thipx.thk +wpp -ml -bd -zu -bt=windows -0 thipx16c.cpp +ml /c /W3 ipx16a.asm +ml /DIS_16 /c /W3 /Fothipx16a.obj thipx.asm +ml /c /W3 thmap16.asm +wlink @thipx16.lnk +rc -40 thipx16.dll +call setinc2.bat diff --git a/IPX/OK/MAKETH32.BAK b/IPX/OK/MAKETH32.BAK new file mode 100644 index 0000000..5471020 --- /dev/null +++ b/IPX/OK/MAKETH32.BAK @@ -0,0 +1,7 @@ +rem thunk Thipx.thk +rem fixthunk +wpp386 -s -mf -3s -bt=nt -bd -zu -4 thipx32c.cpp +ml /DIS_32 /Zf /Fl /c /W3 /Fothipx32a.obj thipx.asm +ml /c /W3 thmap32.asm +wlink @thipx32.lnk +wlib -c -n wwipx32.lib +thipx32.dll diff --git a/IPX/OK/MAKETH32.BAT b/IPX/OK/MAKETH32.BAT new file mode 100644 index 0000000..8827fab --- /dev/null +++ b/IPX/OK/MAKETH32.BAT @@ -0,0 +1,7 @@ +thunk Thipx.thk +rem fixthunk +wpp386 -s -mf -3s -bt=nt -bd -zu -4 thipx32c.cpp +ml /DIS_32 /Zf /Fl /c /W3 /Fothipx32a.obj thipx.asm +ml /c /W3 thmap32.asm +wlink @thipx32.lnk +wlib -c -n wwipx32.lib +thipx32.dll diff --git a/IPX/OK/OLTHIPX.ASM b/IPX/OK/OLTHIPX.ASM new file mode 100644 index 0000000..f1e7048 Binary files /dev/null and b/IPX/OK/OLTHIPX.ASM differ diff --git a/IPX/OK/PCMACRO.16 b/IPX/OK/PCMACRO.16 new file mode 100644 index 0000000..072ee55 --- /dev/null +++ b/IPX/OK/PCMACRO.16 @@ -0,0 +1,1091 @@ +; +; 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 . +; + +;$e:\ifil\pcmacro.inc +;- 16-5-1991 at 10:55:13 by mike +;- 16-5-1991 at 08:30:30 by mike +;- 3-5-1991 at 15:39:52 by mike + + + SUBTTL PCMACRO.INC + .xlist + + +fmulscan_size MACRO e0,e1 + IFDEF TANDY + .err + ENDIF + IFDEF CGA + .err + ENDIF + IFDEF EGA + m8 e0 ;; scan size = 40 for vga & ega + mov e1,e0 + m4 e0 + add e0,e1 + ENDIF + ENDM + +;.commo MACRO +; ENDM + +;.commf MACRO +; ENDM + +setpal MACRO e0 + push si + mov si,OFFSET e0 + call set_pallette + pop si + ENDM + + + + + + +waitvbl MACRO + call wait_vbl + ENDM + + +swapscr MACRO + call swap_scr + ENDM + +setscad MACRO + IFDEF TANDY + mov ax,0b800h ;;setup screen address + ENDIF + IFDEF CGA + mov ax,0b800h + ENDIF + IFDEF EGA + push ds + mov ax,_DATA + mov ds,ax + mov ax,screen_seg + pop ds + ENDIF + mov es,ax + ENDM + +reswordscr macro + stosw ;;movsw + endm + +;; x y +getscreen MACRO e0,e1 + IFIDN , + %out "CANT USED AX AS SECOND ARGUMENT" + .err + endif + + IFIDN , + %out "CANT USED DI AS SECOND ARGUMENT" + .err + endif + + IFDIF , + mov di,e1 + endif + m2 di + mov di,screenvtb[di] + + IFDIF , + mov ax,e0 + endif + + IFDEF EGA + di8 ax + ENDIF + IFDEF TANDY + shr ax,1 + ENDIF + IFDEF CGA + shr ax,1 + shr ax,1 + shr ax,1 + shr ax,1 + ENDIF + add di,ax + IFDEF TANDY + and di,1fffh + ENDIF + ENDM + +getscreen_no MACRO + mov ax,d1 + sal ax,1 + mov di,ax + mov ax,screenvtb[di] + mov di,ax + mov ax,scadd + add di,ax + mov ax,d0 + IFDEF EGA + di8 ax + ENDIF + IFDEF TANDY + shr ax,1 + ENDIF + IFDEF CGA + shr ax,1 + shr ax,1 + shr ax,1 + shr ax,1 + ENDIF + add di,ax + ENDM + + + + + + +mult macro e0,e1 + if ( e1 EQ 2 ) + m2 e0 + exitm + endif + + if ( e1 EQ 4 ) + m4 e0 + exitm + endif + + if ( e1 EQ 8 ) + m8 e0 + exitm + endif + + if ( e1 EQ 16 ) + m16 e0 + exitm + endif + + %out "CANT USE THIS MACRO WITH THIS VALUE" + .err + endm + + + +m2 macro e0 + shl e0,1 + endm + +m4 macro e0 + m2 e0 + m2 e0 + endm + +m8 macro e0 + m4 e0 + m2 e0 + endm + +m16 macro e0 + m8 e0 + m2 e0 + endm + +m32 macro e0 + m16 e0 + m2 e0 + endm + +m64 macro e0 + m32 e0 + m2 e0 + endm + +m128 macro e0 + m64 e0 + m2 e0 + endm + +di2 macro e0 + shr e0,1 + endm + +di4 macro e0 + di2 e0 + di2 e0 + endm + +di8 macro e0 + di4 e0 + di2 e0 + endm + +di16 macro e0 + di8 e0 + di2 e0 + endm + +di32 macro e0 + di16 e0 + di2 e0 + endm + +deca macro e0 + mov ax,e0 + dec ax + mov e0,ax + endm + +decb macro e0 + mov al,e0 + dec al + mov e0,al + endm + +inca macro e0 + mov ax,e0 + inc ax + mov e0,ax + endm + +incb macro e0 + mov al,e0 + inc al + mov e0,al + endm + +clrw macro e0 + mov WORD PTR e0,0 + endm + +clrb macro e0 + mov BYTE PTR e0,0 + endm + +clr macro e0 + mov e0,0 + endm + + + + +mova macro e1,e2 + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + mov ax,e2 + mov e1,ax + endm + +movb macro e1,e2 + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + .ERRIDN , + mov al,e2 + mov e1,al + endm + +btstb macro e1,e2 + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + IFIDN ,
+ test e1,1 SHL e2 + EXITM + ENDIF + IFIDN , + test e1,1 SHL e2 + EXITM + ENDIF + test BYTE PTR e1,1 SHL e2 + endm + + + +saveall macro + save ax,bx,cx,dx,bp,si,di,es,ds + endm + +restall macro + restore ax,bx,cx,dx,bp,si,di,es,ds + endm + +save macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + push r0 + save r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + ENDIF + endm + +restore macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + pop r14 + ENDIF + IFNB + pop r13 + ENDIF + IFNB + pop r12 + ENDIF + IFNB + pop r11 + ENDIF + IFNB + pop r10 + ENDIF + IFNB + pop r9 + ENDIF + IFNB + pop r8 + ENDIF + IFNB + pop r7 + ENDIF + IFNB + pop r6 + ENDIF + IFNB + pop r5 + ENDIF + IFNB + pop r4 + ENDIF + IFNB + pop r3 + ENDIF + IFNB + pop r2 + ENDIF + IFNB + pop r1 + ENDIF + IFNB + pop r0 + ENDIF + endm + +bhi macro lab + ja lab + endm + +bls macro lab + jbe lab + endm + +bcc macro lab + jnc lab + endm + +bcs macro lab + jc lab + endm + +bhs macro lab + jnc lab + endm + +blo macro lab + jc lab + endm + +bne macro lab + jne lab + endm + +beq macro lab + je lab + endm + +bpl macro lab + jns lab + endm + +bmi macro lab + js lab + endm + +bge macro lab + jge lab + endm + +blt macro lab + jl lab + endm + +bgt macro lab + jg lab + endm + +ble macro lab + jle lab + endm + +bra macro lab + jmp lab + endm + + +bhis macro lab + ja lab + endm + +blss macro lab + jbe lab + endm + +bccs macro lab + jnc lab + endm + +bcss macro lab + jc lab + endm + +bnes macro lab + jne lab + endm + +beqs macro lab + je lab + endm + +bpls macro lab + jns lab + endm + +bmis macro lab + js lab + endm + +bges macro lab + jge lab + endm + +blts macro lab + jl lab + endm + +bgts macro lab + jg lab + endm + +bles macro lab + jle lab + endm + +bras macro lab + jmp lab + endm + + +tstb macro e1 + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN ,
+ or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + cmp BYTE PTR e1,0 + endm + + +tstw macro e1 + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + cmp WORD PTR e1,0 + endm + + + +tst macro e1 + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN ,
+ or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + cmp e1,0 + endm + + +clear macro first + xor first,first + endm + +rts macro + ret + endm + + +bclrb macro e1,e2 + btstb e1,e2 + pushf + + bclrb_sub e1,e2 + popf + endm + +bclrb_sub macro e1,e2 + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN ,
+ and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + and BYTE PTR e1,NOT ( 1 SHL e2 ) + endm + + + +bsetb macro e1,e2 + btstb e1,e2 + pushf + or BYTE PTR e1,1 SHL e2 + popf + endm + + +bchgb macro e1,e2 + btstb e1,e2 + pushf + xor BYTE PTR e1,1 SHL e2 + popf + endm + + + +mov_b macro label,label2 + mov byte ptr label,byte ptr label2 + endm + + + +mov_w macro label,label2 + mov word ptr label,word ptr label2 + endm + + + +mov_d macro label,label2 + mov dword ptr label,dword ptr label2 + endm + + +cmp_b macro label,label2 + cmp byte ptr label,byte ptr label2 + endm + +cmp_w macro label,label2 + cmp word ptr label,word ptr label2 + endm + +cmp_d macro label,label2 + cmp dword ptr label,dword ptr label2 + endm + + + +add_b macro label,label2 + add byte ptr label,byte ptr label2 + endm + +add_w macro label,label2 + add word ptr label,word ptr label2 + endm + +add_d macro label,label2 + add dword ptr label,dword ptr label2 + endm + + + +sub_b macro label,label2 + sub byte ptr label,byte ptr label2 + endm + +sub_w macro label,label2 + sub word ptr label,word ptr label2 + endm + +sub_d macro label,label2 + sub dword ptr label,dword ptr label2 + endm + + + + + +or_b macro label,label2 + or byte ptr label,byte ptr label2 + endm + +or_w macro label,label2 + or word ptr label,word ptr label2 + endm + +or_d macro label,label2 + or dword ptr label,dword ptr label2 + endm + + + + + + + +xor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +xor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +xor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + + + + + +eor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +eor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +eor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + +and_b macro label,label2 + and byte ptr label,byte ptr label2 + endm + +and_w macro label,label2 + and word ptr label,word ptr label2 + endm + +and_d macro label,label2 + and dword ptr label,dword ptr label2 + endm + + + + + +test_b macro label,label2 + test byte ptr label,byte ptr label2 + endm + +test_w macro label,label2 + test word ptr label,word ptr label2 + endm + +test_d macro label,label2 + test dword ptr label,dword ptr label2 + endm + + + + + + + +shr_b macro label,label2 + shr byte ptr label,label2 + endm + +shr_w macro label,label2 + shr word ptr label,label2 + endm + +shr_d macro label,label2 + shr dword ptr label,label2 + endm + + + +shl_b macro label,label2 + shl byte ptr label,label2 + endm + +shl_w macro label,label2 + shl word ptr label,label2 + endm + +shl_d macro label,label2 + shl dword ptr label,label2 + endm + + + + +sar_b macro label,label2 + sar byte ptr label,label2 + endm + +sar_w macro label,label2 + sar word ptr label,label2 + endm + +sar_d macro label,label2 + sar dword ptr label,label2 + endm + + + + + +sal_b macro label,label2 + sal byte ptr label,label2 + endm + +sal_w macro label,label2 + sal word ptr label,label2 + endm + +sal_d macro label,label2 + sal dword ptr label,label2 + endm + + + +inc_b macro label + inc byte ptr label + endm + +inc_w macro label + inc word ptr label + endm + +inc_d macro label + inc dword ptr label + endm + + + + +dec_b macro label + dec byte ptr label + endm + +dec_w macro label + dec word ptr label + endm + +dec_d macro label + dec dword ptr label + endm + + + + + +movzx_b macro label,label2 + movzx label,byte ptr label2 + endm + + +movzx_w macro label,label2 + movzx label,word ptr label2 + endm + + +movsx_b macro label,label2 + movsx label,byte ptr label2 + endm + + +movsx_w macro label,label2 + movsx label,word ptr label2 + endm + + + + +mul_b macro label + mul byte ptr label + endm + + +mul_w macro label + mul word ptr label + endm + + +div_b macro label + div byte ptr label + endm + + +div_w macro label + div word ptr label + endm + + +idiv_b macro label + idiv byte ptr label + endm + + +idiv_w macro label + idiv word ptr label + endm + + + + +tst_b macro label + cmp byte ptr label,0 + endm + +tst_w macro label + cmp word ptr label,0 + endm + +tst_d macro label + cmp dword ptr label,0 + endm + + + + + + + +not_b macro label + not byte ptr label + endm + +not_w macro label + not word ptr label + endm + +not_d macro label + not dword ptr label + endm + + + + +neg_b macro label + neg byte ptr label + endm + +neg_w macro label + neg word ptr label + endm + +neg_d macro label + neg dword ptr label + endm + + + + + + .list + + + \ No newline at end of file diff --git a/IPX/OK/THIPX.ASM b/IPX/OK/THIPX.ASM new file mode 100644 index 0000000..67d95b3 --- /dev/null +++ b/IPX/OK/THIPX.ASM @@ -0,0 +1,503 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Fri Jan 26 16:12:04 1996 + +;Command Line: C:\BIN\THUNK.EXE Thipx.thk + + TITLE $Thipx.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public Thipx_ThunkData32 ;This symbol must be exported. +Thipx_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 0228d3h ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_Thipx - offset Thipx_ThunkData32 + dd offset FT_Prolog_Thipx - offset Thipx_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public Thipx_ThunkConnect32@16 +Thipx_ThunkConnect32@16: + pop edx + push offset Thipx_ThkData16 + push offset Thipx_ThunkData32 + push edx + jmp ThunkConnect32@24 +Thipx_ThkData16 label byte + db "Thipx_ThunkData16",0 + + + + + +pfnQT_Thunk_Thipx dd offset QT_Thunk_Thipx +pfnFT_Prolog_Thipx dd offset FT_Prolog_Thipx + .data +QT_Thunk_Thipx label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_Thipx label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public _IPX_Close_Socket95@4 +_IPX_Close_Socket95@4: + mov cl,9 + jmp II_IPX_Close_Socket95@4 +public _IPX_Initialise@4 +_IPX_Initialise@4: + mov cl,12 + jmp II_IPX_Close_Socket95@4 +public _IPX_Open_Socket95@4 +_IPX_Open_Socket95@4: + mov cl,10 +; _IPX_Close_Socket95(16) = _IPX_Close_Socket95(32) {} +; +; dword ptr [ebp+8]: param1 +; +public II_IPX_Close_Socket95@4 +II_IPX_Close_Socket95@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_Thipx] + cwde + leave + retn 4 + + + + + +; +public _IPX_Get_Internet_Address95@12 +_IPX_Get_Internet_Address95@12: + mov cl,7 +; _IPX_Get_Internet_Address95(16) = _IPX_Get_Internet_Address95(32) {} +; +; dword ptr [ebp+8]: param1 +; dword ptr [ebp+12]: netnum +; dword ptr [ebp+16]: node +; +public II_IPX_Get_Internet_Address95@12 +II_IPX_Get_Internet_Address95@12: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call SMapLS_IP_EBP_12 + push eax + call SMapLS_IP_EBP_16 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_12 + call SUnMapLS_IP_EBP_16 + leave + retn 12 + + + + + +; +public _IPX_Get_User_ID95@8 +_IPX_Get_User_ID95@8: + mov cl,6 +; _IPX_Get_User_ID95(16) = _IPX_Get_User_ID95(32) {} +; +; dword ptr [ebp+8]: param1 +; dword ptr [ebp+12]: user_id +; +public II_IPX_Get_User_ID95@8 +II_IPX_Get_User_ID95@8: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call SMapLS_IP_EBP_12 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_12 + leave + retn 8 + + + + + +; +public _IPX_Send_Packet95@12 +_IPX_Send_Packet95@12: + mov cl,5 +; _IPX_Send_Packet95(16) = _IPX_Send_Packet95(32) {} +; +; dword ptr [ebp+8]: address +; dword ptr [ebp+12]: buffer +; dword ptr [ebp+16]: param3 +; +public II_IPX_Send_Packet95@12 +II_IPX_Send_Packet95@12: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call SMapLS_IP_EBP_12 + push eax + push word ptr [ebp+16] ;param3: dword->word + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + call SUnMapLS_IP_EBP_12 + leave + retn 12 + + + + + +; +public _IPX_Broadcast_Packet95@8 +_IPX_Broadcast_Packet95@8: + mov cl,4 +; _IPX_Broadcast_Packet95(16) = _IPX_Broadcast_Packet95(32) {} +; +; dword ptr [ebp+8]: buffer +; dword ptr [ebp+12]: param2 +; +public II_IPX_Broadcast_Packet95@8 +II_IPX_Broadcast_Packet95@8: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + push word ptr [ebp+12] ;param2: dword->word + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + leave + retn 8 + + + + + +; +public _IPX_Get_Local_Target95@16 +_IPX_Get_Local_Target95@16: + mov cl,3 +; _IPX_Get_Local_Target95(16) = _IPX_Get_Local_Target95(32) {} +; +; dword ptr [ebp+8]: netnum +; dword ptr [ebp+12]: node +; dword ptr [ebp+16]: param3 +; dword ptr [ebp+20]: address +; +public II_IPX_Get_Local_Target95@16 +II_IPX_Get_Local_Target95@16: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call SMapLS_IP_EBP_12 + push eax + push word ptr [ebp+16] ;param3: dword->word + call SMapLS_IP_EBP_20 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + call SUnMapLS_IP_EBP_12 + call SUnMapLS_IP_EBP_20 + leave + retn 16 + + + + + +; +public _IPX_Shut_Down95@0 +_IPX_Shut_Down95@0: + mov cl,1 + jmp II_IPX_Shut_Down95@0 +public _IPX_Uninitialise@0 +_IPX_Uninitialise@0: + mov cl,11 + jmp II_IPX_Shut_Down95@0 +public _IPX_Get_Connection_Number95@0 +_IPX_Get_Connection_Number95@0: + mov cl,8 + jmp II_IPX_Shut_Down95@0 +public _IPX_Start_Listening95@0 +_IPX_Start_Listening95@0: + mov cl,2 +; _IPX_Shut_Down95(16) = _IPX_Shut_Down95(32) {} +; +; +public II_IPX_Shut_Down95@0 +II_IPX_Shut_Down95@0: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call dword ptr [pfnQT_Thunk_Thipx] + cwde + leave + retn + + + + + +; +public _IPX_Get_Outstanding_Buffer95@4 +_IPX_Get_Outstanding_Buffer95@4: + mov cl,0 +; _IPX_Get_Outstanding_Buffer95(16) = _IPX_Get_Outstanding_Buffer95(32) {} +; +; dword ptr [ebp+8]: buffer +; +public II_IPX_Get_Outstanding_Buffer95@4 +II_IPX_Get_Outstanding_Buffer95@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef _IPX_Get_Outstanding_Buffer95:far16 +externDef _IPX_Shut_Down95:far16 +externDef _IPX_Start_Listening95:far16 +externDef _IPX_Get_Local_Target95:far16 +externDef _IPX_Broadcast_Packet95:far16 +externDef _IPX_Send_Packet95:far16 +externDef _IPX_Get_User_ID95:far16 +externDef _IPX_Get_Internet_Address95:far16 +externDef _IPX_Get_Connection_Number95:far16 +externDef _IPX_Close_Socket95:far16 +externDef _IPX_Open_Socket95:far16 +externDef _IPX_Uninitialise:far16 +externDef _IPX_Initialise:far16 + + +FT_ThipxTargetTable label word + dw offset _IPX_Get_Outstanding_Buffer95 + dw seg _IPX_Get_Outstanding_Buffer95 + dw offset _IPX_Shut_Down95 + dw seg _IPX_Shut_Down95 + dw offset _IPX_Start_Listening95 + dw seg _IPX_Start_Listening95 + dw offset _IPX_Get_Local_Target95 + dw seg _IPX_Get_Local_Target95 + dw offset _IPX_Broadcast_Packet95 + dw seg _IPX_Broadcast_Packet95 + dw offset _IPX_Send_Packet95 + dw seg _IPX_Send_Packet95 + dw offset _IPX_Get_User_ID95 + dw seg _IPX_Get_User_ID95 + dw offset _IPX_Get_Internet_Address95 + dw seg _IPX_Get_Internet_Address95 + dw offset _IPX_Get_Connection_Number95 + dw seg _IPX_Get_Connection_Number95 + dw offset _IPX_Close_Socket95 + dw seg _IPX_Close_Socket95 + dw offset _IPX_Open_Socket95 + dw seg _IPX_Open_Socket95 + dw offset _IPX_Uninitialise + dw seg _IPX_Uninitialise + dw offset _IPX_Initialise + dw seg _IPX_Initialise + + + + + .data + +public Thipx_ThunkData16 ;This symbol must be exported. +Thipx_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 0228d3h ;Checksum + dw offset FT_ThipxTargetTable + dw seg FT_ThipxTargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public Thipx_ThunkConnect16 +Thipx_ThunkConnect16: + pop ax + pop dx + push seg Thipx_ThunkData16 + push offset Thipx_ThunkData16 + push seg Thipx_ThkData32 + push offset Thipx_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +Thipx_ThkData32 label byte + db "Thipx_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/OK/THIPX.BAK b/IPX/OK/THIPX.BAK new file mode 100644 index 0000000..f417a3a --- /dev/null +++ b/IPX/OK/THIPX.BAK @@ -0,0 +1,355 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Jan 23 15:12:09 1996 + +;Command Line: C:\BIN\THUNK.EXE thipx.thk + + TITLE $thipx.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public thipx_ThunkData32 ;This symbol must be exported. +thipx_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 0a179h ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_thipx - offset thipx_ThunkData32 + dd offset FT_Prolog_thipx - offset thipx_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public thipx_ThunkConnect32@16 +thipx_ThunkConnect32@16: + pop edx + push offset thipx_ThkData16 + push offset thipx_ThunkData32 + push edx + jmp ThunkConnect32@24 +thipx_ThkData16 label byte + db "thipx_ThunkData16",0 + + + + + +pfnQT_Thunk_thipx dd offset QT_Thunk_thipx +pfnFT_Prolog_thipx dd offset FT_Prolog_thipx + .data +QT_Thunk_thipx label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_thipx label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public _IPX_Close_Socket95@4 +_IPX_Close_Socket95@4: + mov cl,3 + jmp II_IPX_Close_Socket95@4 +public _IPX_Initialise@4 +_IPX_Initialise@4: + mov cl,6 + jmp II_IPX_Close_Socket95@4 +public _IPX_Open_Socket95@4 +_IPX_Open_Socket95@4: + mov cl,4 +; _IPX_Close_Socket95(16) = _IPX_Close_Socket95(32) {} +; +; dword ptr [ebp+8]: param1 +; +public II_IPX_Close_Socket95@4 +II_IPX_Close_Socket95@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_thipx] + cwde + leave + retn 4 + + + + + +; +public _IPX_Get_Connection_Number95@0 +_IPX_Get_Connection_Number95@0: + mov cl,2 + jmp II_IPX_Get_Connection_Number95@0 +public _IPX_Uninitialise@0 +_IPX_Uninitialise@0: + mov cl,5 +; _IPX_Get_Connection_Number95(16) = _IPX_Get_Connection_Number95(32) {} +; +; +public II_IPX_Get_Connection_Number95@0 +II_IPX_Get_Connection_Number95@0: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call dword ptr [pfnQT_Thunk_thipx] + cwde + leave + retn + + + + + +; +public _IPX_Get_Internet_Address95@12 +_IPX_Get_Internet_Address95@12: + mov cl,1 +; _IPX_Get_Internet_Address95(16) = _IPX_Get_Internet_Address95(32) {} +; +; dword ptr [ebp+8]: param1 +; dword ptr [ebp+12]: netnum +; dword ptr [ebp+16]: node +; +public II_IPX_Get_Internet_Address95@12 +II_IPX_Get_Internet_Address95@12: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call SMapLS_IP_EBP_12 + push eax + call SMapLS_IP_EBP_16 + push eax + call dword ptr [pfnQT_Thunk_thipx] + cwde + call SUnMapLS_IP_EBP_12 + call SUnMapLS_IP_EBP_16 + leave + retn 12 + + + + + +; +public __IPX_Get_User_ID95@8 +__IPX_Get_User_ID95@8: + mov cl,0 +; __IPX_Get_User_ID95(16) = __IPX_Get_User_ID95(32) {} +; +; dword ptr [ebp+8]: param1 +; dword ptr [ebp+12]: user_id +; +public II__IPX_Get_User_ID95@8 +II__IPX_Get_User_ID95@8: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call SMapLS_IP_EBP_12 + push eax + call dword ptr [pfnQT_Thunk_thipx] + cwde + call SUnMapLS_IP_EBP_12 + leave + retn 8 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef __IPX_Get_User_ID95:far16 +externDef _IPX_Get_Internet_Address95:far16 +externDef _IPX_Get_Connection_Number95:far16 +externDef _IPX_Close_Socket95:far16 +externDef _IPX_Open_Socket95:far16 +externDef _IPX_Uninitialise:far16 +externDef _IPX_Initialise:far16 + + +FT_thipxTargetTable label word + dw offset __IPX_Get_User_ID95 + dw seg __IPX_Get_User_ID95 + dw offset _IPX_Get_Internet_Address95 + dw seg _IPX_Get_Internet_Address95 + dw offset _IPX_Get_Connection_Number95 + dw seg _IPX_Get_Connection_Number95 + dw offset _IPX_Close_Socket95 + dw seg _IPX_Close_Socket95 + dw offset _IPX_Open_Socket95 + dw seg _IPX_Open_Socket95 + dw offset _IPX_Uninitialise + dw seg _IPX_Uninitialise + dw offset _IPX_Initialise + dw seg _IPX_Initialise + + + + + .data + +public thipx_ThunkData16 ;This symbol must be exported. +thipx_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 0a179h ;Checksum + dw offset FT_thipxTargetTable + dw seg FT_thipxTargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public thipx_ThunkConnect16 +thipx_ThunkConnect16: + pop ax + pop dx + push seg thipx_ThunkData16 + push offset thipx_ThunkData16 + push seg thipx_ThkData32 + push offset thipx_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +thipx_ThkData32 label byte + db "thipx_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/OK/THIPX.THK b/IPX/OK/THIPX.THK new file mode 100644 index 0000000..2963470 --- /dev/null +++ b/IPX/OK/THIPX.THK @@ -0,0 +1,98 @@ + +enablemapdirect3216 = true; + +typedef int BOOL; +typedef int INT; + + +typedef struct tag_network_number { + unsigned char bytes[4]; +}network_number; + +typedef struct tag_physical_node { + unsigned char bytes[6]; +}physical_node; + + + + +BOOL _IPX_Initialise(INT) +{ +} + +BOOL _IPX_Uninitialise(void) +{ +} + +INT _IPX_Open_Socket95(INT) +{ +} + +INT _IPX_Close_Socket95(INT) +{ +} + +INT _IPX_Get_Connection_Number95(void) +{ +} + +INT _IPX_Get_Internet_Address95(INT, network_number* netnum, physical_node* node) +{ + netnum = output; + node = output; +} + +INT _IPX_Get_User_ID95(INT, char *user_id) +{ + user_id = output; +} + + +typedef struct tag_send_address_struct{ + unsigned char address[6]; +}send_address_struct; + +typedef struct tag_send_buffer_struct{ + unsigned char buffer[512]; +}send_buffer_struct; + + +INT _IPX_Send_Packet95(send_address_struct *address, send_buffer_struct *buffer, INT) +{ + address = input; + buffer = input; +} + +INT _IPX_Broadcast_Packet95(send_buffer_struct *buffer, INT) +{ + buffer = input; +} + +INT _IPX_Get_Local_Target95(network_number *netnum, physical_node *node, short, send_address_struct *address) +{ + netnum = input; + node = input; + address = output; +} + + +INT _IPX_Start_Listening95(void) +{ +} + +INT _IPX_Shut_Down95(void) +{ +} + + +typedef struct tag_get_buffer_struct{ + unsigned char get_buffer[1024]; +}get_buffer_struct; + +INT _IPX_Get_Outstanding_Buffer95(get_buffer_struct *buffer) +{ + buffer = output; +} + + + diff --git a/IPX/OK/THIPX16C.CPP b/IPX/OK/THIPX16C.CPP new file mode 100644 index 0000000..5bdcd19 --- /dev/null +++ b/IPX/OK/THIPX16C.CPP @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + + + +#include + +extern "C" { + BOOL FAR PASCAL Thipx_ThunkConnect16_(LPSTR pszDll16, LPSTR pszDll32, WORD hInst, DWORD dwReason); +} + +int CALLBACK LibMain (HANDLE, WORD, WORD, LPSTR) +{ + return (1); +} + + +extern "C" { + BOOL FAR PASCAL DllEntryPoint_ (DWORD dwReason, WORD hInst, WORD, WORD, DWORD, WORD); +} + + +BOOL FAR PASCAL DllEntryPoint_ (DWORD dwReason, WORD hInst, WORD, WORD, DWORD, WORD) +{ + OutputDebugString("In 16bit DLL entry point. Calling Thipx_ThunkConnect16\r\n"); + + if (!Thipx_ThunkConnect16_("THIPX16.DLL", "THIPX32.DLL", hInst, dwReason)){ + OutputDebugString("In 16bit DllEntryPoint: thkThunkConnect16 ret FALSE\r\n"); + return FALSE; + } + + OutputDebugString("In 16bit DllEntryPoint: thkThunkConnect16 ret TRUE\r\n"); + return TRUE; +} + + diff --git a/IPX/OK/THIPX32C.CPP b/IPX/OK/THIPX32C.CPP new file mode 100644 index 0000000..84afcc6 --- /dev/null +++ b/IPX/OK/THIPX32C.CPP @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + + + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include + + + +extern "C" { +BOOL WINAPI Thipx_ThunkConnect32(LPSTR pszDll16, LPSTR pszDll32, DWORD hIinst, DWORD dwReason); +}; + + + + +BOOL APIENTRY DllMain(DWORD hInst, DWORD dwReason, DWORD dwReserved) + +{ + OutputDebugString("In THIPX32.DLL : DllMain\r\n"); + if (! Thipx_ThunkConnect32("THIPX16.DLL", "THIPX32.DLL", hInst, dwReason)) { + OutputDebugString("ERROR! 32Bit ThunkConnect32 failed!\r\n"); + return FALSE; + } + OutputDebugString("32Bit ThunkConnect32 succeeded.\r\n"); + return (TRUE); +} + + +BOOL APIENTRY LibMain(DWORD hInst, DWORD dwReason, DWORD dwReserved) +{ + return (DllMain(hInst, dwReason, dwReserved)); +} diff --git a/IPX/OK/THMAP16.ASM b/IPX/OK/THMAP16.ASM new file mode 100644 index 0000000..9cce8df --- /dev/null +++ b/IPX/OK/THMAP16.ASM @@ -0,0 +1,60 @@ +; +; 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 . +; + + + + + .8086 + + OPTION READONLY + OPTION OLDSTRUCTS + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + +externdef pascal Thipx_ThunkConnect16_:far16 +extern pascal Thipx_ThunkConnect16:near16 + +Thipx_ThunkConnect16_ proc far16 pascal + + jmp Thipx_ThunkConnect16 + +Thipx_ThunkConnect16_ endp + + + +externdef DllEntryPoint:far16 +extern pascal DllEntryPoint_:far16 + +DllEntryPoint proc far16 + + jmp DllEntryPoint_ + +DllEntryPoint endp + + + + + end + + + + diff --git a/IPX/OK/THMAP32.ASM b/IPX/OK/THMAP32.ASM new file mode 100644 index 0000000..20a6531 --- /dev/null +++ b/IPX/OK/THMAP32.ASM @@ -0,0 +1,188 @@ +; +; 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 . +; + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + + .model FLAT + + + + .code + + +;externdef _DllMain@12:near +;externdef DllMain@12:near +; +;DllMain proc near +; +; jmp _DllMain@12 +; +;DllMain endp + + + +externdef _IPX_Initialise:near +externdef __IPX_Initialise@4:near + +_IPX_Initialise proc near + + jmp __IPX_Initialise@4 + +_IPX_Initialise endp + + + + +externdef _IPX_Uninitialise:near +externdef __IPX_Uninitialise@0:near + +_IPX_Uninitialise proc near + + jmp __IPX_Uninitialise@0 + +_IPX_Uninitialise endp + + + + +externdef _IPX_Open_Socket95:near +externdef __IPX_Open_Socket95@4:near + +_IPX_Open_Socket95 proc near + + jmp __IPX_Open_Socket95@4 + +_IPX_Open_Socket95 endp + + + + + +externdef _IPX_Close_Socket95:near +externdef __IPX_Close_Socket95@4:near + +_IPX_Close_Socket95 proc near + + jmp __IPX_Close_Socket95@4 + +_IPX_Close_Socket95 endp + + + +externdef _IPX_Get_Connection_Number95:near +externdef __IPX_Get_Connection_Number95@0:near + +_IPX_Get_Connection_Number95 proc near + + jmp __IPX_Get_Connection_Number95@0 + +_IPX_Get_Connection_Number95 endp + + + +externdef _IPX_Get_Internet_Address95:near +externdef __IPX_Get_Internet_Address95@12:near + +_IPX_Get_Internet_Address95 proc near + + jmp __IPX_Get_Internet_Address95@12 + +_IPX_Get_Internet_Address95 endp + + + +externdef _IPX_Get_User_ID95:near +externdef __IPX_Get_User_ID95@8:near + +_IPX_Get_User_ID95 proc near + + jmp __IPX_Get_User_ID95@8 + +_IPX_Get_User_ID95 endp + + + + +externdef _IPX_Send_Packet95:near +externdef __IPX_Send_Packet95@12:near + +_IPX_Send_Packet95 proc near + + jmp __IPX_Send_Packet95@12 + +_IPX_Send_Packet95 endp + + + +externdef _IPX_Broadcast_Packet95:near +externdef __IPX_Broadcast_Packet95@8:near + +_IPX_Broadcast_Packet95 proc near + + jmp __IPX_Broadcast_Packet95@8 + +_IPX_Broadcast_Packet95 endp + + + +externdef _IPX_Get_Local_Target95:near +externdef __IPX_Get_Local_Target95@16:near + +_IPX_Get_Local_Target95 proc near + + jmp __IPX_Get_Local_Target95@16 + +_IPX_Get_Local_Target95 endp + + + +externdef _IPX_Start_Listening95:near +externdef __IPX_Start_Listening95@0:near + +_IPX_Start_Listening95 proc near + + jmp __IPX_Start_Listening95@0 + +_IPX_Start_Listening95 endp + + + +externdef _IPX_Shut_Down95:near +externdef __IPX_Shut_Down95@0:near + +_IPX_Shut_Down95 proc near + + jmp __IPX_Shut_Down95@0 + +_IPX_Shut_Down95 endp + + + +externdef _IPX_Get_Outstanding_Buffer95:near +externdef __IPX_Get_Outstanding_Buffer95@4:near + +_IPX_Get_Outstanding_Buffer95 proc near + + jmp __IPX_Get_Outstanding_Buffer95@4 + +_IPX_Get_Outstanding_Buffer95 endp + + + end diff --git a/IPX/OK/WWIPX.ASM b/IPX/OK/WWIPX.ASM new file mode 100644 index 0000000..453a4a0 --- /dev/null +++ b/IPX/OK/WWIPX.ASM @@ -0,0 +1,243 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Jan 16 13:53:22 1996 + +;Command Line: D:\MSTOOLS\BIN\THUNK.EXE wwipx.thk + + TITLE $wwipx.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public wwipx_ThunkData32 ;This symbol must be exported. +wwipx_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_wwipx - offset wwipx_ThunkData32 + dd offset FT_Prolog_wwipx - offset wwipx_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public wwipx_ThunkConnect32@16 +wwipx_ThunkConnect32@16: + pop edx + push offset wwipx_ThkData16 + push offset wwipx_ThunkData32 + push edx + jmp ThunkConnect32@24 +wwipx_ThkData16 label byte + db "wwipx_ThunkData16",0 + + + + + +pfnQT_Thunk_wwipx dd offset QT_Thunk_wwipx +pfnFT_Prolog_wwipx dd offset FT_Prolog_wwipx + .data +QT_Thunk_wwipx label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_wwipx label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public IPX_Initialise@4 +IPX_Initialise@4: + mov cl,0 +; IPX_Initialise(16) = IPX_Initialise(32) {} +; +; dword ptr [ebp+8]: param1 +; +public IIIPX_Initialise@4 +IIIPX_Initialise@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_wwipx] + cwde + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef IPX_Initialise:far16 + + +FT_wwipxTargetTable label word + dw offset IPX_Initialise + dw seg IPX_Initialise + + + + + .data + +public wwipx_ThunkData16 ;This symbol must be exported. +wwipx_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dw offset FT_wwipxTargetTable + dw seg FT_wwipxTargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public wwipx_ThunkConnect16 +wwipx_ThunkConnect16: + pop ax + pop dx + push seg wwipx_ThunkData16 + push offset wwipx_ThunkData16 + push seg wwipx_ThkData32 + push offset wwipx_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +wwipx_ThkData32 label byte + db "wwipx_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/OK/WWIPX16.ASM b/IPX/OK/WWIPX16.ASM new file mode 100644 index 0000000..c7d5a6f --- /dev/null +++ b/IPX/OK/WWIPX16.ASM @@ -0,0 +1,243 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Jan 16 13:14:21 1996 + +;Command Line: D:\MSTOOLS\BIN\THUNK.EXE wwipx16.thk + + TITLE $wwipx16.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public wwipx16_ThunkData32 ;This symbol must be exported. +wwipx16_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_wwipx16 - offset wwipx16_ThunkData32 + dd offset FT_Prolog_wwipx16 - offset wwipx16_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public wwipx16_ThunkConnect32@16 +wwipx16_ThunkConnect32@16: + pop edx + push offset wwipx16_ThkData16 + push offset wwipx16_ThunkData32 + push edx + jmp ThunkConnect32@24 +wwipx16_ThkData16 label byte + db "wwipx16_ThunkData16",0 + + + + + +pfnQT_Thunk_wwipx16 dd offset QT_Thunk_wwipx16 +pfnFT_Prolog_wwipx16 dd offset FT_Prolog_wwipx16 + .data +QT_Thunk_wwipx16 label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_wwipx16 label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public IPX_Initialise@4 +IPX_Initialise@4: + mov cl,0 +; IPX_Initialise(16) = IPX_Initialise(32) {} +; +; dword ptr [ebp+8]: param1 +; +public IIIPX_Initialise@4 +IIIPX_Initialise@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_wwipx16] + cwde + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef IPX_Initialise:far16 + + +FT_wwipx16TargetTable label word + dw offset IPX_Initialise + dw seg IPX_Initialise + + + + + .data + +public wwipx16_ThunkData16 ;This symbol must be exported. +wwipx16_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dw offset FT_wwipx16TargetTable + dw seg FT_wwipx16TargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public wwipx16_ThunkConnect16 +wwipx16_ThunkConnect16: + pop ax + pop dx + push seg wwipx16_ThunkData16 + push offset wwipx16_ThunkData16 + push seg wwipx16_ThkData32 + push offset wwipx16_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +wwipx16_ThkData32 label byte + db "wwipx16_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/OLD/IPX16A.ASM b/IPX/OLD/IPX16A.ASM new file mode 100644 index 0000000..1c902ae --- /dev/null +++ b/IPX/OLD/IPX16A.ASM @@ -0,0 +1,1472 @@ +; +; 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 . +; + + + + .8086 + + + .model large + option segment:USE16 + option readonly + option oldstructs + + + assume ds:@data + assume es:nothing + + +RECEIVE_BUFFER_LENGTH =1024 +MAX_RECEIVE_BUFFERS =32 + + +externdef GLOBALDOSALLOC:far +externdef GLOBALDOSFREE:far + + + .data + +IPXCallOffset dw 0 +IPXCallSegment dw 0 +RealSegment dw 0 +PseudoES dw 0 + + + +RegisterDump db 32h dup (0) + +MyNetworkNumber db 4 dup (?) +MyNodeAddress db 6 dup (?) +MySocket dw ? + +ReceiveBufferSegment dw ? +ReceiveBufferSelector dw ? +ReceiveECBOffset dw ? +CurrentReceiveBuffer dw ? +LastPassedReceiveBuffer dw ? +RealModeCallbackSegment dw ? +RealModeCallbackOffset dw ? + +CallbackRegisterDump db 32h dup (0) + +Listening db ? +NoReenter db 0 +IPXHold db 0 + + + +OFFS =0 +SEGM =2 + + +;--------------------------------------------------------------------------- +;These defines are for the IPX functions. The function number is specified +;by placing it in BX when IPX is called. There are two ways to invoke IPX: +;use interrupt 0x7a, or use a function whose address is obtained by calling +;interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +;This is the preferred method, since other apps are known to use int 0x7a. +;--------------------------------------------------------------------------- +IPX_OPEN_SOCKET = 0000h +IPX_CLOSE_SOCKET = 0001h +IPX_GET_LOCAL_TARGET = 0002h +IPX_SEND_PACKET = 0003h +IPX_LISTEN_FOR_PACKET = 0004h +IPX_SCHEDULE_EVENT = 0005h +IPX_CANCEL_EVENT = 0006h +IPX_GET_INTERVAL_MARKER = 0008h +IPX_GET_INTERNETWORK_ADDRESS = 0009h +IPX_RELINQUISH_CONTROL = 000Ah +IPX_DISCONNECT_FROM_TARGET = 000Bh + +;/*--------------------------------------------------------------------------- +;These defines are for various IPX error codes: +;---------------------------------------------------------------------------*/ +IPXERR_CONNECTION_SEVERED = 00ech +IPXERR_CONNECTION_FAILED = 00edh +IPXERR_NO_CONNECTION = 00eeh +IPXERR_CONNECTION_TABLE_FULL = 00efh +IPXERR_NO_CANCEL_ECB = 00f9h +IPXERR_NO_PATH = 00fah +IPXERR_ECB_INACTIVE = 00fch +IPXERR_INVALID_PACKET_LENGTH = 00fdh +IPXERR_SOCKET_TABLE_FULL = 00feh +IPXERR_SOCKET_ERROR = 00ffh + +;/*--------------------------------------------------------------------------- +;These defines are for various interrupt vectors and DPMI functions: +;---------------------------------------------------------------------------*/ +IPX_INT = 007ah +DPMI_INT = 0031h +DPMI_ALLOC_DOS_MEM = 0100h +DPMI_FREE_DOS_MEM = 0101h +DPMI_CALL_REAL_INT = 0300h +DPMI_CALL_REAL_PROC = 0301h +DPMI_ALLOCATE_CALLBACK = 0303h +DPMI_RELEASE_CALLBACK = 0304h +DPMI_LOCK_MEM = 0600h +DPMI_UNLOCK_MEM = 0601h + + + + + +request_buffer struct + len word ? + buffer_type byte ? + connect_number byte ? +request_buffer ends + + + +request_local_target_buffer struct + lt_network_number db ?,?,?,? + lt_physical_node db ?,?,?,?,?,? + lt_socket dw ? +request_local_target_buffer ends + + +local_target_reply_buffer struct + lt_local_target db ?,?,?,?,?,? +local_target_reply_buffer ends + + + +reply_buffer struct + lem word ? + network_number byte ?,?,?,? + physical_node byte ?,?,?,?,?,? + server_socket word ? +reply_buffer ends + + + + + +userid_buffer struct + struct_len word ? + object_id byte ?,?,?,? + object_type byte ?,? + object_name byte 48 dup(?) + login_time byte 7 dup (?) + reserved word ? +userid_buffer ends + + .code + + .386 + + include + +externdef pascal _IPX_Initiialise:far16 +externdef pascal _IPX_Uninitialise:far16 + + +_IPX_Initialise proc far pascal + + push ebx + push ecx + push edx + push esi + push edi + push ebp + push ds + push es + push fs + push gs + + mov ax,7a00h + int 2fh + and eax,0ffh + cmp al,-1 + setz al + + test al,al + jz @f + + mov bx,@data + mov ds,bx + mov [IPXCallSegment],es + mov [IPXCallOffset],di + +@@: pop gs + pop fs + pop es + pop ds + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + ret + +_IPX_Initialise endp + + + +_IPX_Open_Socket95 proc far pascal uses bx cx dx si di ds es fs gs, + socket:word + + mov bx,@data + mov ds,bx + mov bx,IPX_OPEN_SOCKET ;open socket + mov_w dx,[socket] ;socket number + mov_w [MySocket],dx ;save it for later + mov ax,0ffh ;long lived + + call Call_IPX + + ret + +_IPX_Open_Socket95 endp + + + + +_IPX_Close_Socket95 proc far pascal uses ax bx cx dx si di ds es fs gs, + socket:word + + mov bx,1 + mov_w dx,[socket] + + call Call_IPX + + ret + +_IPX_Close_Socket95 endp + + + + + +_IPX_Uninitialise proc far pascal + + int 3 + ret + +_IPX_Uninitialise endp + + + + + +_IPX_Get_Connection_Number95 proc far pascal uses bx cx dx si di ds es fs gs + + mov ax,0dc00h + call Call_DOS + and eax,255 + ret + +_IPX_Get_Connection_Number95 endp + + + + + + +_IPX_Get_Internet_Address95 proc far pascal uses bx cx dx si di ds es fs gs, + connection_number :dword, + network_number_off :word, + network_number_seg :word, + physical_node_off :word, + physical_node_seg :word + + local selector:word + local segmento:word + + ret +ifdef cuts ;(unfinished) + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + int 3 + xor ax,ax + mov bx,(sizeof reply_buffer + sizeof request_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@dpmi_error + + mov [segmento],dx + mov [selector],ax + mov fs,dx + xor di,di + + mov_w fs:[di.len],2 + mov_b fs:[di.buffer_type],13h + mov_b al,[connection_number] + mov_b fs:[di.connect_number],al + + mov di,sizeof request_buffer + mov fs:[di.len],12 + + + mov ax,0e300h + mov ds,[segmento] + mov es,[segmento] + xor si,si + + pusha + call Call_DOS + popa + + les di,dword ptr [network_number_off] + mov ds,[selector] + mov si,sizeof request_buffer + network_number + movsd + + les di,dword ptr [physical_node_off] + mov si,sizeof request_buffer + physical_node + movsd + movsw + + ; + ; Save it here for posterity (or perhaps later use) + ; + mov di,offset MyNetworkNumber + mov ax,@data + mov es,ax + mov ds,[selector] + mov si,sizeof request_buffer + network_number + movsd + + mov di,offset MyNodeAddress + mov si,sizeof request_buffer + physical_node + movsd + movsw + + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bx + call GLOBALDOSFREE + + xor ax,ax + ret + +@@dpmi_error: mov ax,-1 + ret +endif + +_IPX_Get_Internet_Address95 endp + + + + + + +_IPX_Get_User_ID95 proc far pascal uses bx cx dx si di ds es fs gs, + connection_number:dword, + user_id_off:word, + user_id_seg:word + + local segmento:word + local selector:word + + int 3 + + cmp [connection_number],0 + jz @@return_error + + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + xor ax,ax + mov bx,(sizeof reply_buffer + sizeof request_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@return_error + + mov [segmento],dx + mov [selector],ax + mov fs,dx + xor di,di + mov ax,@data + mov ds,ax + + mov_w fs:[di.len],2 + mov_b fs:[di.buffer_type],16h + mov_b al,[connection_number] + mov_b fs:[di.connect_number],al + + mov di,sizeof request_buffer + mov fs:[di.struct_len],sizeof userid_buffer-2 + + mov ax,0e300h + mov ds,[segmento] + mov es,[segmento] + xor si,si + + pusha + call Call_DOS + popa + + les di,dword ptr [user_id_off] + mov ds,[selector] + mov si,sizeof request_buffer + object_name + mov cx,48/4 + rep movsd + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + push bx + call GLOBALDOSFREE + + xor ax,ax + ret + +@@return_error: mov ax,-1 + ret + +_IPX_Get_User_ID95 endp + + + + +;--------------------------------------------------------------------------- +;This is the IPX Packet structure. It's followed by the data itself, which +;can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +;field; annotation of 'APP' means the application must set the field. +;NOTE: All header fields are ordered high-byte,low-byte. +;---------------------------------------------------------------------------*/ +IPXHeaderType struct + + CheckSum dw ? ; IPX: Not used; always 0xffff + IPXLength dw ? ; IPX: Total size, incl header & data + TransportControl db ? ; IPX: # bridges message crossed + PacketType db ? ; APP: Set to 4 for IPX (5 for SPX) + DestNetworkNumber db ?,?,?,? ; APP: destination Network Number + DestNetworkNode db ?,?,?,?,?,? ; APP: destination Node Address + DestNetworkSocket dw ? ; APP: destination Socket Number + SourceNetworkNumber db ?,?,?,? ; IPX: source Network Number + SourceNetworkNode db ?,?,?,?,?,? ; IPX: source Node Address + SourceNetworkSocket dw ? ; IPX: source Socket Number + +IPXHeaderType ends + + + + +;/*--------------------------------------------------------------------------- +;This is the IPX Event Control Block. It serves as a communications area +;between IPX and the application for a single IPX operation. You should set +;up a separate ECB for each IPX operation you perform. +;---------------------------------------------------------------------------*/ + +ECB struct + + Link_Address dd ? + Event_Service_Routine dd ? ; APP: event handler (NULL=none) + InUse db ? ; IPX: 0 = event complete + CompletionCode db ? ; IPX: event's return code + SocketNumber dw ? ; APP: socket to send data through + ConnectionID dw ? ; returned by Listen (???) + RestOfWorkspace dw ? + DriverWorkspace db 12 dup (?) + ImmediateAddress db 6 dup (?) ; returned by Get_Local_Target + PacketCount dw ? + PacketAddress0 dd ? + PacketLength0 dw ? + PacketAddress1 dd ? + PacketLength1 dw ? + +ECB ends + + +SEND_ECB_OFFSET =0 +SEND_HEADER_OFFSET =(sizeof ECB +3) AND 0fffch +SEND_BUFFER_OFFSET =(SEND_HEADER_OFFSET + sizeof IPXHeaderType +3) AND 0fffch + + +_IPX_Send_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_address :far ptr byte, + send_buffer :far ptr byte, + send_buffer_len :word + + local selector :word + local segmento :word + local dos_send_ecb :far ptr ECB + local dos_send_header :far ptr IPXHeaderType + local dos_send_buffer :far ptr byte + local completion_code :word + + int 3 + ; + ; Allocate required DOS memory + ; + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@error + mov [segmento],dx + mov [selector],ax + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the send buffer + ; + mov cx,546 + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: + mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe) + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + assume ds:@data + assume fs:@data + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[segmento] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ;assume ds:nothing + ; + ; Fill in the address field + ; + lds si,[send_address] + mov eax,[si] + mov_d es:[di.ImmediateAddress],eax + mov ax,[si+4] + mov_w es:[di.ImmediateAddress+4],ax + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the nwtowrk number and node address + ; + mov_d eax,fs:[MyNetworkNumber] + mov_d es:[di.DestNetworkNumber],eax + mov_d eax,fs:[MyNodeAddress] + mov_d es:[di.DestNetworkNode],eax + mov_w ax,fs:[MyNodeAddress+4] + mov_w es:[di.DestNetworkNode+4],ax + + ; + ; Send that sucker! + ; + mov si,[segmento] + mov fs:[PseudoES],si + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + push fs + call Call_IPX + pop fs + popa + mov fs:[PseudoES],0 + + + assume fs:nothing + ; + ; Wait for the send to finish + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + mov bx,IPX_RELINQUISH_CONTROL + save bp + call Call_IPX + restore bp + jmp @@wait_send_loop + + + ; + ; Get the completion code + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + cmp [completion_code],0 + jz @@error + +@@success: mov ax,1 + ret + + +@@error: xor ax,ax + ret + + +_IPX_Send_Packet95 endp + + + + + + +_IPX_Broadcast_Packet95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + send_buffer :far ptr byte, + send_buffer_len :word + + local selector :word + local segmento :word + local dos_send_ecb :far ptr ECB + local dos_send_header :far ptr IPXHeaderType + local dos_send_buffer :far ptr byte + local completion_code :word + + int 3 + ; + ; Allocate required DOS memory + ; + xor ax,ax + mov bx,(sizeof ECB + sizeof IPXHeaderType + 1024 +31) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@error + mov [segmento],dx + mov [selector],ax + + ; + ; Set up the pointers to the dos memory + ; + mov_w [dos_send_ecb+OFFS],SEND_ECB_OFFSET + mov_w [dos_send_ecb+SEGM],ax + mov_w [dos_send_header+OFFS],SEND_HEADER_OFFSET + mov_w [dos_send_header+SEGM],ax + mov_w [dos_send_buffer+OFFS],SEND_BUFFER_OFFSET + mov_w [dos_send_buffer+SEGM],ax + + ; + ; Clear out the send ECB + ; + xor al,al + mov cx,sizeof ECB + les di,[dos_send_ecb] + rep stosb + + ; + ; Clear out the send header + ; + mov cx,sizeof IPXHeaderType + les di,[dos_send_header] + rep stosb + + ; + ; Copy the data to be sent into the send buffer + ; + mov cx,546 + cmp_w cx,[send_buffer_len] + jle @@got_buffer_len + mov_w cx,[send_buffer_len] +@@got_buffer_len: + mov_w [send_buffer_len],546 ;always send same size packets (nice and big for Kali hehe) + les di,[dos_send_buffer] + lds si,[send_buffer] + rep movsb + + ; + ; Fill in the ECB + ; + mov ax,@data + mov ds,ax + mov fs,ax ;keep ptr to data seg in fs + les di,[dos_send_ecb] + + assume fs:@data + assume ds:@data + + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + mov ax,[segmento] + shl eax,16 + mov_w ax,[dos_send_header+OFFS] + mov_d es:[di.PacketAddress0],eax + mov_w es:[di.PacketLength0],sizeof IPXHeaderType + mov_w ax,[dos_send_buffer+OFFS] + mov_d es:[di.PacketAddress1],eax + mov_w ax,[send_buffer_len] + mov_w es:[di.PacketLength1],ax + + ;assume ds:nothing + ; + ; Fill in the address field + ; + mov_d es:[di.ImmediateAddress],0ffffffffh + mov_w es:[di.ImmediateAddress+4],0ffffh + + ; + ; Fill in the outgoing header + ; + les di,[dos_send_header] + mov es:[di.PacketType],4 + push fs:[MySocket] + pop es:[di.DestNetworkSocket] + + ; + ; Fill in the nwtowrk number and node address + ; + mov_d es:[di.DestNetworkNumber],0ffffffffh + mov_d es:[di.DestNetworkNode],0ffffffffh + mov_w es:[di.DestNetworkNode+4],0ffffh + + ; + ; Send that sucker! + ; + mov si,[segmento] + mov fs:[PseudoES],si + mov si,SEND_ECB_OFFSET + mov bx,IPX_SEND_PACKET + pusha + push fs + call Call_IPX + pop fs + popa + mov fs:[PseudoES],0 + + assume fs:nothing + ; + ; Wait for the send to finish + ; +@@wait_send_loop: + lds si,[dos_send_ecb] + cmp [si.InUse],0 + jz @@done + + mov bx,IPX_RELINQUISH_CONTROL + save bp + call Call_IPX + restore bp + jmp @@wait_send_loop + + + + ; + ; Get the completion code + ; +@@done: lds si,[dos_send_ecb] + mov al,[si.CompletionCode] + xor ah,ah + mov [completion_code],ax + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + cmp [completion_code],0 + jz @@error + + +@@success: mov ax,1 + ret + + +@@error: xor ax,ax + ret + + +_IPX_Broadcast_Packet95 endp + + + + + + + +_IPX_Get_Local_Target95 proc far pascal uses ebx ecx edx esi edi ds es fs gs, + dest_network :far ptr byte, + dest_node :far ptr byte, + socket :word, + bridge_address :far ptr byte + + + local segmento :word + local selector :word + local result_code :word + + int 3 + ; + ; Allocate DOS memory for buffers passed to interrupt + ; + xor ax,ax + mov bx,(sizeof local_target_reply_buffer + \ + sizeof request_local_target_buffer + 15) + save bp + push ax + push bx + call GLOBALDOSALLOC + restore bp + test ax,ax + jz @@return_error + + mov [segmento],dx + mov [selector],ax + mov fs,dx + xor di,di + + ; + ; Init the request structure + ; + mov_d eax,[dest_network] + mov_d fs:[di.lt_network_number],eax + mov_d eax,[dest_node] + mov_d fs:[di.lt_physical_node],eax + mov_w ax,[dest_node+4] + mov_w fs:[di.lt_physical_node+4],ax + mov ax,[socket] + mov fs:[di.lt_socket],ax + + mov bx,IPX_GET_LOCAL_TARGET + mov ax,[segmento] + mov ds,ax + xor si,si + mov es,ax + mov di,sizeof request_local_target_buffer + + push bp + call Call_IPX + pop bp + + mov [result_code],ax + + mov ds,[selector] + mov si,sizeof request_local_target_buffer + lt_local_target + les di,bridge_address + movsd + movsw + + ; + ; Free the DOS memory + ; + mov bx,@data + mov ds,bx + mov es,bx + mov fs,bx + mov gs,bx + mov bx,[selector] + save bp + push bx + call GLOBALDOSFREE + restore bp + + ; + ; Return the IPX result code + ; + mov ax,[result_code] + ret + +@@return_error: mov ax,-1 + ret + +_IPX_Get_Local_Target95 endp + + + + +_IPX_Get_Outstanding_Buffer95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs, + copy_receive_buffer:far ptr byte + + int 3 + mov ax,@data + mov ds,ax + + xor ax,ax + mov si,[LastPassedReceiveBuffer] + cmp si,[CurrentReceiveBuffer] + jz @@done + + push ds + mov cx,RECEIVE_BUFFER_LENGTH/4 + mov ds,[ReceiveBufferSelector] + les di,[copy_receive_buffer] + rep movsd + pop ds + + cmp si,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor si,si +@@no_wrap: mov [LastPassedReceiveBuffer],si + + mov ax,1 + +@@done: ret + +_IPX_Get_Outstanding_Buffer95 endp + + + + +Receive_Callback proc far + + pushf + cli + cld + pushad + push ds + push es + push fs + push gs + mov ax,@data + mov ds,ax + cmp [NoReenter],0 + jnz @@out + mov [NoReenter],1 + mov ax,[CurrentReceiveBuffer] + + add ax,RECEIVE_BUFFER_LENGTH + cmp ax,RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + jc @@no_wrap + xor ax,ax +@@no_wrap: mov [CurrentReceiveBuffer],ax + + call Listen_For_Packet + mov ax,@data + mov ds,ax + mov [NoReenter],0 + +@@out: pop gs + pop fs + pop es + pop ds + popad + popf + ret + +Receive_Callback endp + + + + + +Init_Receive_ECB proc near uses eax ebx ecx edx esi edi ebp ds es + + mov ax,@data + mov ds,ax + mov es,[ReceiveBufferSelector] + + ; + ; Clear out the ECB + ; + mov di,[ReceiveECBOffset] + mov cx,sizeof ECB + xor al,al + rep stosb + + +; +; Set up the ECB +; + ; + ;General ECB data + mov di,[ReceiveECBOffset] + mov ax,[MySocket] + mov es:[di.SocketNumber],ax + mov es:[di.PacketCount],2 + + ; + ; Packet address for IPX header + mov ax,[CurrentReceiveBuffer] + mov bx,[ReceiveBufferSegment] + mov_w es:[di.PacketAddress0+OFFS],ax + mov_w es:[di.PacketAddress0+SEGM],bx + mov es:[di.PacketLength0],sizeof IPXHeaderType + + ; + ; Packet address for receive buffer + mov ax,[CurrentReceiveBuffer] + add ax,sizeof IPXHeaderType + mov_w es:[di.PacketAddress1+OFFS],ax + mov_w es:[di.PacketAddress1+SEGM],bx + mov es:[di.PacketLength1],546 + + ; + ; Set up the callback address + mov ax,[RealModeCallbackOffset] + mov_w es:[di.Event_Service_Routine+OFFS],ax + mov ax,[RealModeCallbackSegment] + mov_w es:[di.Event_Service_Routine+SEGM],ax + + ret + +Init_Receive_ECB endp + + +_IPX_Start_Listening95 proc far pascal uses ebx ecx edx esi edi ebp ds es fs gs + + mov ax,@data + mov ds,ax + cmp [Listening],0 + jnz @@restart ;already listening + + ; + ; Allocate and lock DOS memory for listen + ; ECB and receive buffers + ; + mov bx,(RECEIVE_BUFFER_LENGTH*MAX_RECEIVE_BUFFERS + sizeof ECB + 15) + xor ax,ax + save bp,ds + push ax + push bx + call GLOBALDOSALLOC + restore bp,ds + test ax,ax + jz @@error + + mov [ReceiveBufferSegment],dx + mov [ReceiveBufferSelector],ax + + ; + ; Set up pointers to the DOS memory + ; + mov [ReceiveECBOffset],RECEIVE_BUFFER_LENGTH * MAX_RECEIVE_BUFFERS + mov [CurrentReceiveBuffer],0 ;1st receive buffer + mov [LastPassedReceiveBuffer],0 + + ; + ; Set up a real mode callback function + ; + mov ax,DPMI_ALLOCATE_CALLBACK + mov si,offset Receive_Callback + mov di,offset CallbackRegisterDump + push cs + pop ds + mov bx,@data + mov es,bx + save bp + int 31h + restore bp + bcs @@error + + mov ax,@data + mov ds,ax + mov [RealModeCallbackSegment],cx + mov [RealModeCallbackOffset],dx + +@@restart: save ds + call Listen_For_Packet + restore ds + mov [Listening],1 + +@@out: mov ax,1 + ret + +@@error: xor ax,ax + ret + +_IPX_Start_Listening95 endp + + + + +_IPX_Shut_Down95 proc far pascal uses eax ebx ecx edx esi edi ebp ds es fs gs + + int 3 + mov ax,@data + mov ds,ax + cmp [Listening],0 + jz @@out + + ; + ; Stop listening for a packet + ; + mov es,[ReceiveBufferSegment] + mov si,[ReceiveECBOffset] + mov bx,IPX_CANCEL_EVENT + save bp,ds + call Call_IPX + save bp,ds + + ; + ; Remove the real mode callback function + ; + mov cx,[RealModeCallbackSegment] + mov dx,[RealModeCallbackOffset] + mov ax,DPMI_RELEASE_CALLBACK + save bp,ds + int 31h + restore bp,ds + + ; + ; Free the DOS memory + ; + mov ax,@data + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + mov ax,[ReceiveBufferSelector] + save bp,ds + push ax + call GLOBALDOSFREE + restore bp,ds + + mov [Listening],0 + +@@out: ret + +_IPX_Shut_Down95 endp + + + + +Listen_For_Packet proc near uses ebx ecx edx esi edi ebp ds es fs gs + + call Init_Receive_ECB + + mov ax,@data + mov ds,ax + mov si,[ReceiveBufferSegment] + mov [PseudoES],si + mov si,[ReceiveECBOffset] + mov bx,IPX_LISTEN_FOR_PACKET + save ds + call Call_IPX + restore ds + mov [PseudoES],0 + + ret + +Listen_For_Packet endp + + + + + + +Call_IPX proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + push bp + mov bp,offset IPXCallOffset + call dword ptr gs:[bp] + pop bp + + pop gs + ret + + + + + + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + + ; + ; Prevent reenterancy + ; +@@hold: ;cmp gs:[IPXHold],0 + ;jnz @@hold + + mov gs:[IPXHold],1 + + ; + ; Dump the registers first so we can use them + ; + mov_d gs:[RegisterDump+0ch],0 + mov_d gs:[RegisterDump],edi + mov_d gs:[RegisterDump+4],esi + mov_d gs:[RegisterDump+8],ebp + mov_d gs:[RegisterDump+10h],ebx + mov_d gs:[RegisterDump+14h],edx + mov_d gs:[RegisterDump+18h],ecx + mov_d gs:[RegisterDump+1ch],eax + + int 3 + + push gs + xor ax,ax + push ax + mov ax,4096 ;4k real mode stack + push ax + call GLOBALDOSALLOC + pop gs + mov bx,4094 ;stack ptr + test ax,ax + jnz @f + xor bx,bx + + ; + ; Set up stack addr to zero - DPMI will supply a real mode stack + ; +@@: mov_w gs:[RegisterDump+2eh],bx ;sp + mov_w gs:[RegisterDump+30h],dx ;ss + save ax ;save selector so we can free the memory later + + + ; + ; Dump the flags + ; + pushf + pop gs:[word ptr RegisterDump+20h] + + ; + ; Set up all segment register areas to point to our real mode segment + ; + mov_w gs:[RegisterDump+22h],es + mov_w gs:[RegisterDump+24h],ds + mov_w gs:[RegisterDump+26h],fs + mov_w gs:[RegisterDump+28h],gs + + cmp gs:[PseudoES],0 + jz @f + mov ax,gs:[PseudoES] + mov_w gs:[RegisterDump+22h],ax +@@: + + + ; + ; Set up the address to call + ; + mov ax,gs:[IPXCallSegment] + mov_w gs:[RegisterDump+2ch],ax + mov ax,gs:[IPXCallOffset] + mov_w gs:[RegisterDump+2ah],ax + + mov ax,DPMI_CALL_REAL_INT ; Call real mode procedure with far return frame + xor bh,bh ; flags - should be zero + mov bl,07ah ; IPX interrupt + mov ecx,0 ;512 ; number of words to copy from the protected mode stack + mov di,offset RegisterDump + push gs + pop es + save gs + ;push ss + ;push sp + int 31h ;DPMI interrupt + + ;pop ax + ;pop bx + ;pushf + ;pop cx + ;cli + ;mov sp,ax + ;mov ss,bx + ;add sp,2 + ;push cx + ;popf + + restore gs + + mov ax,@data + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + call GLOBALDOSFREE + + + mov_d eax,gs:[RegisterDump+1ch] + mov gs:[IPXHold],0 + pop gs + + ret + + +Call_IPX endp + + + + + + + + +Call_DOS proc far + + push gs + push ax + mov ax,@data + mov gs,ax + pop ax + ; + ; Dump the registers first so we can use them + ; + mov_d gs:[RegisterDump],edi + mov_d gs:[RegisterDump+4],esi + mov_d gs:[RegisterDump+8],ebp + mov_d gs:[RegisterDump+10h],ebx + mov_d gs:[RegisterDump+14h],edx + mov_d gs:[RegisterDump+18h],ecx + mov_d gs:[RegisterDump+1ch],eax + + ; + ; Dump the flags + ; + pushf + pop gs:[word ptr RegisterDump+20h] + + ; + ; Set up all segment register areas to point to our real mode segment + ; + mov_w gs:[RegisterDump+22h],es + mov_w gs:[RegisterDump+24h],ds + mov_w gs:[RegisterDump+26h],fs + mov_w gs:[RegisterDump+28h],gs + + ; + ; Set up stack addr to zero - DPMI will supply a real mode stack + ; + xor ax,ax + mov_w gs:[RegisterDump+2eh],ax ;sp + mov_w gs:[RegisterDump+30h],ax ;ss + + mov ax,DPMI_CALL_REAL_INT; Simulate real mode interrupt + xor bh,bh ; flags - should be zero + mov bl,21h ; interrupt number + mov ecx,0 ;512 ; number of words to copy from the protected mode stack + mov di,offset RegisterDump + push gs + pop es + save gs + int 31h ;DPMI interrupt + restore gs + + mov_d edi,gs:[RegisterDump] + mov_d esi,gs:[RegisterDump+4] + mov_d ebp,gs:[RegisterDump+8] + mov_d ebx,gs:[RegisterDump+10h] + mov_d edx,gs:[RegisterDump+14h] + mov_d ecx,gs:[RegisterDump+18h] + mov_d eax,gs:[RegisterDump+1ch] + mov_w es,gs:[RegisterDump+22h] + mov_w ds,gs:[RegisterDump+24h] + pop gs + + ret + + +Call_DOS endp + + + + + end + + + + + + diff --git a/IPX/OLTHIPX.ASM b/IPX/OLTHIPX.ASM new file mode 100644 index 0000000..f1e7048 Binary files /dev/null and b/IPX/OLTHIPX.ASM differ diff --git a/IPX/PCMACRO.16 b/IPX/PCMACRO.16 new file mode 100644 index 0000000..d073274 --- /dev/null +++ b/IPX/PCMACRO.16 @@ -0,0 +1,564 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 16 bit ASM Macros * +;* * +;* File Name : PCMACRO.16 * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : November 17th, 1995 * +;* * +;* Last Update : November 20th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +saveall macro + save ax,bx,cx,dx,bp,si,di,es,ds + endm + +restall macro + restore ax,bx,cx,dx,bp,si,di,es,ds + endm + +save macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + push r0 + save r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + ENDIF + endm + +restore macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + pop r14 + ENDIF + IFNB + pop r13 + ENDIF + IFNB + pop r12 + ENDIF + IFNB + pop r11 + ENDIF + IFNB + pop r10 + ENDIF + IFNB + pop r9 + ENDIF + IFNB + pop r8 + ENDIF + IFNB + pop r7 + ENDIF + IFNB + pop r6 + ENDIF + IFNB + pop r5 + ENDIF + IFNB + pop r4 + ENDIF + IFNB + pop r3 + ENDIF + IFNB + pop r2 + ENDIF + IFNB + pop r1 + ENDIF + IFNB + pop r0 + ENDIF + endm + +bhi macro lab + ja lab + endm + +bls macro lab + jbe lab + endm + +bcc macro lab + jnc lab + endm + +bcs macro lab + jc lab + endm + +bhs macro lab + jnc lab + endm + +blo macro lab + jc lab + endm + +bne macro lab + jne lab + endm + +beq macro lab + je lab + endm + +bpl macro lab + jns lab + endm + +bmi macro lab + js lab + endm + +bge macro lab + jge lab + endm + +blt macro lab + jl lab + endm + +bgt macro lab + jg lab + endm + +ble macro lab + jle lab + endm + +bra macro lab + jmp lab + endm + + +bhis macro lab + ja lab + endm + +blss macro lab + jbe lab + endm + +bccs macro lab + jnc lab + endm + +bcss macro lab + jc lab + endm + +bnes macro lab + jne lab + endm + +beqs macro lab + je lab + endm + +bpls macro lab + jns lab + endm + +bmis macro lab + js lab + endm + +bges macro lab + jge lab + endm + +blts macro lab + jl lab + endm + +bgts macro lab + jg lab + endm + +bles macro lab + jle lab + endm + +bras macro lab + jmp lab + endm + + +clear macro first + xor first,first + endm + +rts macro + ret + endm + + + +mov_b macro label,label2 + mov byte ptr label,byte ptr label2 + endm + + + +mov_w macro label,label2 + mov word ptr label,word ptr label2 + endm + + + +mov_d macro label,label2 + mov dword ptr label,dword ptr label2 + endm + + +cmp_b macro label,label2 + cmp byte ptr label,byte ptr label2 + endm + +cmp_w macro label,label2 + cmp word ptr label,word ptr label2 + endm + +cmp_d macro label,label2 + cmp dword ptr label,dword ptr label2 + endm + + + +add_b macro label,label2 + add byte ptr label,byte ptr label2 + endm + +add_w macro label,label2 + add word ptr label,word ptr label2 + endm + +add_d macro label,label2 + add dword ptr label,dword ptr label2 + endm + + + +sub_b macro label,label2 + sub byte ptr label,byte ptr label2 + endm + +sub_w macro label,label2 + sub word ptr label,word ptr label2 + endm + +sub_d macro label,label2 + sub dword ptr label,dword ptr label2 + endm + + + + + +or_b macro label,label2 + or byte ptr label,byte ptr label2 + endm + +or_w macro label,label2 + or word ptr label,word ptr label2 + endm + +or_d macro label,label2 + or dword ptr label,dword ptr label2 + endm + + + + + + + +xor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +xor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +xor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + + + + + +eor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +eor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +eor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + +and_b macro label,label2 + and byte ptr label,byte ptr label2 + endm + +and_w macro label,label2 + and word ptr label,word ptr label2 + endm + +and_d macro label,label2 + and dword ptr label,dword ptr label2 + endm + + + + + +test_b macro label,label2 + test byte ptr label,byte ptr label2 + endm + +test_w macro label,label2 + test word ptr label,word ptr label2 + endm + +test_d macro label,label2 + test dword ptr label,dword ptr label2 + endm + + + + + + + +shr_b macro label,label2 + shr byte ptr label,label2 + endm + +shr_w macro label,label2 + shr word ptr label,label2 + endm + +shr_d macro label,label2 + shr dword ptr label,label2 + endm + + + +shl_b macro label,label2 + shl byte ptr label,label2 + endm + +shl_w macro label,label2 + shl word ptr label,label2 + endm + +shl_d macro label,label2 + shl dword ptr label,label2 + endm + + + + +sar_b macro label,label2 + sar byte ptr label,label2 + endm + +sar_w macro label,label2 + sar word ptr label,label2 + endm + +sar_d macro label,label2 + sar dword ptr label,label2 + endm + + + + + +sal_b macro label,label2 + sal byte ptr label,label2 + endm + +sal_w macro label,label2 + sal word ptr label,label2 + endm + +sal_d macro label,label2 + sal dword ptr label,label2 + endm + + + +inc_b macro label + inc byte ptr label + endm + +inc_w macro label + inc word ptr label + endm + +inc_d macro label + inc dword ptr label + endm + + + + +dec_b macro label + dec byte ptr label + endm + +dec_w macro label + dec word ptr label + endm + +dec_d macro label + dec dword ptr label + endm + + + + + +movzx_b macro label,label2 + movzx label,byte ptr label2 + endm + + +movzx_w macro label,label2 + movzx label,word ptr label2 + endm + + +movsx_b macro label,label2 + movsx label,byte ptr label2 + endm + + +movsx_w macro label,label2 + movsx label,word ptr label2 + endm + + + + +mul_b macro label + mul byte ptr label + endm + + +mul_w macro label + mul word ptr label + endm + + +div_b macro label + div byte ptr label + endm + + +div_w macro label + div word ptr label + endm + + +idiv_b macro label + idiv byte ptr label + endm + + +idiv_w macro label + idiv word ptr label + endm + + + + +tst_b macro label + cmp byte ptr label,0 + endm + +tst_w macro label + cmp word ptr label,0 + endm + +tst_d macro label + cmp dword ptr label,0 + endm + + + + + + + +not_b macro label + not byte ptr label + endm + +not_w macro label + not word ptr label + endm + +not_d macro label + not dword ptr label + endm + + + + +neg_b macro label + neg byte ptr label + endm + +neg_w macro label + neg word ptr label + endm + +neg_d macro label + neg dword ptr label + endm + + + + diff --git a/IPX/THIPX.ASM b/IPX/THIPX.ASM new file mode 100644 index 0000000..8eb1d88 --- /dev/null +++ b/IPX/THIPX.ASM @@ -0,0 +1,438 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Mar 12 13:23:57 1996 + +;Command Line: C:\BIN\THUNK.EXE Thipx.thk + + TITLE $Thipx.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public Thipx_ThunkData32 ;This symbol must be exported. +Thipx_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 014cdch ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_Thipx - offset Thipx_ThunkData32 + dd offset FT_Prolog_Thipx - offset Thipx_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public Thipx_ThunkConnect32@16 +Thipx_ThunkConnect32@16: + pop edx + push offset Thipx_ThkData16 + push offset Thipx_ThunkData32 + push edx + jmp ThunkConnect32@24 +Thipx_ThkData16 label byte + db "Thipx_ThunkData16",0 + + + + + +pfnQT_Thunk_Thipx dd offset QT_Thunk_Thipx +pfnFT_Prolog_Thipx dd offset FT_Prolog_Thipx + .data +QT_Thunk_Thipx label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_Thipx label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public _IPX_Close_Socket95@4 +_IPX_Close_Socket95@4: + mov cl,7 + jmp II_IPX_Close_Socket95@4 +public _IPX_Open_Socket95@4 +_IPX_Open_Socket95@4: + mov cl,8 +; _IPX_Close_Socket95(16) = _IPX_Close_Socket95(32) {} +; +; dword ptr [ebp+8]: param1 +; +public II_IPX_Close_Socket95@4 +II_IPX_Close_Socket95@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_Thipx] + cwde + leave + retn 4 + + + + + +; +public _IPX_Send_Packet95@20 +_IPX_Send_Packet95@20: + mov cl,5 +; _IPX_Send_Packet95(16) = _IPX_Send_Packet95(32) {} +; +; dword ptr [ebp+8]: address +; dword ptr [ebp+12]: buffer +; dword ptr [ebp+16]: param3 +; dword ptr [ebp+20]: net +; dword ptr [ebp+24]: node +; +public II_IPX_Send_Packet95@20 +II_IPX_Send_Packet95@20: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call SMapLS_IP_EBP_12 + push eax + push word ptr [ebp+16] ;param3: dword->word + call SMapLS_IP_EBP_20 + push eax + call SMapLS_IP_EBP_24 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + call SUnMapLS_IP_EBP_12 + call SUnMapLS_IP_EBP_20 + call SUnMapLS_IP_EBP_24 + leave + retn 20 + + + + + +; +public _IPX_Broadcast_Packet95@8 +_IPX_Broadcast_Packet95@8: + mov cl,4 +; _IPX_Broadcast_Packet95(16) = _IPX_Broadcast_Packet95(32) {} +; +; dword ptr [ebp+8]: buffer +; dword ptr [ebp+12]: param2 +; +public II_IPX_Broadcast_Packet95@8 +II_IPX_Broadcast_Packet95@8: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + push word ptr [ebp+12] ;param2: dword->word + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + leave + retn 8 + + + + + +; +public _IPX_Get_Local_Target95@16 +_IPX_Get_Local_Target95@16: + mov cl,3 +; _IPX_Get_Local_Target95(16) = _IPX_Get_Local_Target95(32) {} +; +; dword ptr [ebp+8]: netnum +; dword ptr [ebp+12]: node +; dword ptr [ebp+16]: param3 +; dword ptr [ebp+20]: address +; +public II_IPX_Get_Local_Target95@16 +II_IPX_Get_Local_Target95@16: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call SMapLS_IP_EBP_12 + push eax + push word ptr [ebp+16] ;param3: dword->word + call SMapLS_IP_EBP_20 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + call SUnMapLS_IP_EBP_12 + call SUnMapLS_IP_EBP_20 + leave + retn 16 + + + + + +; +public _IPX_Shut_Down95@0 +_IPX_Shut_Down95@0: + mov cl,1 + jmp II_IPX_Shut_Down95@0 +public _IPX_Initialise@0 +_IPX_Initialise@0: + mov cl,9 + jmp II_IPX_Shut_Down95@0 +public _IPX_Get_Connection_Number95@0 +_IPX_Get_Connection_Number95@0: + mov cl,6 + jmp II_IPX_Shut_Down95@0 +public _IPX_Start_Listening95@0 +_IPX_Start_Listening95@0: + mov cl,2 +; _IPX_Shut_Down95(16) = _IPX_Shut_Down95(32) {} +; +; +public II_IPX_Shut_Down95@0 +II_IPX_Shut_Down95@0: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call dword ptr [pfnQT_Thunk_Thipx] + cwde + leave + retn + + + + + +; +public _IPX_Get_Outstanding_Buffer95@4 +_IPX_Get_Outstanding_Buffer95@4: + mov cl,0 +; _IPX_Get_Outstanding_Buffer95(16) = _IPX_Get_Outstanding_Buffer95(32) {} +; +; dword ptr [ebp+8]: buffer +; +public II_IPX_Get_Outstanding_Buffer95@4 +II_IPX_Get_Outstanding_Buffer95@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + call SMapLS_IP_EBP_8 + push eax + call dword ptr [pfnQT_Thunk_Thipx] + cwde + call SUnMapLS_IP_EBP_8 + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef _IPX_Get_Outstanding_Buffer95:far16 +externDef _IPX_Shut_Down95:far16 +externDef _IPX_Start_Listening95:far16 +externDef _IPX_Get_Local_Target95:far16 +externDef _IPX_Broadcast_Packet95:far16 +externDef _IPX_Send_Packet95:far16 +externDef _IPX_Get_Connection_Number95:far16 +externDef _IPX_Close_Socket95:far16 +externDef _IPX_Open_Socket95:far16 +externDef _IPX_Initialise:far16 + + +FT_ThipxTargetTable label word + dw offset _IPX_Get_Outstanding_Buffer95 + dw seg _IPX_Get_Outstanding_Buffer95 + dw offset _IPX_Shut_Down95 + dw seg _IPX_Shut_Down95 + dw offset _IPX_Start_Listening95 + dw seg _IPX_Start_Listening95 + dw offset _IPX_Get_Local_Target95 + dw seg _IPX_Get_Local_Target95 + dw offset _IPX_Broadcast_Packet95 + dw seg _IPX_Broadcast_Packet95 + dw offset _IPX_Send_Packet95 + dw seg _IPX_Send_Packet95 + dw offset _IPX_Get_Connection_Number95 + dw seg _IPX_Get_Connection_Number95 + dw offset _IPX_Close_Socket95 + dw seg _IPX_Close_Socket95 + dw offset _IPX_Open_Socket95 + dw seg _IPX_Open_Socket95 + dw offset _IPX_Initialise + dw seg _IPX_Initialise + + + + + .data + +public Thipx_ThunkData16 ;This symbol must be exported. +Thipx_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 014cdch ;Checksum + dw offset FT_ThipxTargetTable + dw seg FT_ThipxTargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public Thipx_ThunkConnect16 +Thipx_ThunkConnect16: + pop ax + pop dx + push seg Thipx_ThunkData16 + push offset Thipx_ThunkData16 + push seg Thipx_ThkData32 + push offset Thipx_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +Thipx_ThkData32 label byte + db "Thipx_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/THIPX.BAK b/IPX/THIPX.BAK new file mode 100644 index 0000000..22f4d88 --- /dev/null +++ b/IPX/THIPX.BAK @@ -0,0 +1,104 @@ +/* +** 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 . +*/ + + +enablemapdirect3216 = true; + +typedef int BOOL; +typedef int INT; + + +typedef struct tag_network_number { + unsigned char bytes[4]; +}network_number; + +typedef struct tag_physical_node { + unsigned char bytes[6]; +}physical_node; + +typedef struct tag_send_address_struct{ + unsigned char address[6]; +}send_address_struct; + +typedef struct tag_send_buffer_struct{ + unsigned char buffer[512]; +}send_buffer_struct; + +typedef struct tag_get_buffer_struct{ + unsigned char get_buffer[1024]; +}get_buffer_struct; + + + + +BOOL _IPX_Initialise(INT) +{ +} + + +INT _IPX_Open_Socket95(INT) +{ +} + +INT _IPX_Close_Socket95(INT) +{ +} + +INT _IPX_Get_Connection_Number95(void) +{ +} + + +INT _IPX_Send_Packet95(send_address_struct *address, send_buffer_struct *buffer, INT, network_number *net, physical_node* node) +{ + address = input; + buffer = input; + net = input; + node = input; +} + +INT _IPX_Broadcast_Packet95(send_buffer_struct *buffer, INT) +{ + buffer = input; +} + +INT _IPX_Get_Local_Target95(network_number *netnum, physical_node *node, short, send_address_struct *address) +{ + netnum = input; + node = input; + address = output; +} + + +INT _IPX_Start_Listening95(void) +{ +} + +INT _IPX_Shut_Down95(void) +{ +} + + + +INT _IPX_Get_Outstanding_Buffer95(get_buffer_struct *buffer) +{ + buffer = output; +} + + + diff --git a/IPX/THIPX.THK b/IPX/THIPX.THK new file mode 100644 index 0000000..61a1ccf --- /dev/null +++ b/IPX/THIPX.THK @@ -0,0 +1,86 @@ + +enablemapdirect3216 = true; + +typedef int BOOL; +typedef int INT; + + +typedef struct tag_network_number { + unsigned char bytes[4]; +}network_number; + +typedef struct tag_physical_node { + unsigned char bytes[6]; +}physical_node; + +typedef struct tag_send_address_struct{ + unsigned char address[6]; +}send_address_struct; + +typedef struct tag_send_buffer_struct{ + unsigned char buffer[512]; +}send_buffer_struct; + +typedef struct tag_get_buffer_struct{ + unsigned char get_buffer[1024]; +}get_buffer_struct; + + + + +BOOL _IPX_Initialise(void) +{ +} + + +INT _IPX_Open_Socket95(INT) +{ +} + +INT _IPX_Close_Socket95(INT) +{ +} + +INT _IPX_Get_Connection_Number95(void) +{ +} + + +INT _IPX_Send_Packet95(send_address_struct *address, send_buffer_struct *buffer, INT, network_number *net, physical_node* node) +{ + address = input; + buffer = input; + net = input; + node = input; +} + +INT _IPX_Broadcast_Packet95(send_buffer_struct *buffer, INT) +{ + buffer = input; +} + +INT _IPX_Get_Local_Target95(network_number *netnum, physical_node *node, short, send_address_struct *address) +{ + netnum = input; + node = input; + address = output; +} + + +INT _IPX_Start_Listening95(void) +{ +} + +INT _IPX_Shut_Down95(void) +{ +} + + + +INT _IPX_Get_Outstanding_Buffer95(get_buffer_struct *buffer) +{ + buffer = output; +} + + + diff --git a/IPX/THIPX16C.CPP b/IPX/THIPX16C.CPP new file mode 100644 index 0000000..57015af --- /dev/null +++ b/IPX/THIPX16C.CPP @@ -0,0 +1,100 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 95 * + * * + * File Name : THIPX16C.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/15/96 * + * * + * Last Update : January 29thth 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * 'C' code for the 16 bit IPX library. This library is 'thunked' down to from a 32bit * + * .dll. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * LibMain -- library entry point * + * DllEntryPoint -- called each time a new app requests use of the .dll * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include + + +extern "C" { + BOOL FAR PASCAL Thipx_ThunkConnect16_(LPSTR pszDll16, LPSTR pszDll32, WORD hInst, DWORD dwReason); + BOOL FAR PASCAL DllEntryPoint_ (DWORD dwReason, WORD hInst, WORD, WORD, DWORD, WORD); +} + + +/*********************************************************************************************** + * LibMain -- library entry point * + * * + * * + * * + * INPUT: the usual windows rubbish * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/29/96 10:45AM ST : Created * + *=============================================================================================*/ +int CALLBACK LibMain (HANDLE, WORD, WORD, LPSTR) +{ + return (1); +} + + + +/*********************************************************************************************** + * DllEntryPoint -- called each time a new app requests use of the .dll * + * * + * * + * * + * INPUT: windows junk * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/29/96 10:45AM ST : Created * + *=============================================================================================*/ +BOOL FAR PASCAL DllEntryPoint_ (DWORD dwReason, WORD hInst, WORD, WORD, DWORD, WORD) +{ + if (!Thipx_ThunkConnect16_("THIPX16.DLL", "THIPX32.DLL", hInst, dwReason)){ + return FALSE; + } + return TRUE; +} + + diff --git a/IPX/THIPX32C.CPP b/IPX/THIPX32C.CPP new file mode 100644 index 0000000..0a00054 --- /dev/null +++ b/IPX/THIPX32C.CPP @@ -0,0 +1,100 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 95 * + * * + * File Name : THIPX16C.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/15/96 * + * * + * Last Update : January 29thth 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * 'C' code for the 32 bit IPX library. This library 'thunks' down to a 16bit .dll * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DllMain -- called each time a new app requests use of the .dll * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include + + +extern "C" { + BOOL WINAPI Thipx_ThunkConnect32(LPSTR pszDll16, LPSTR pszDll32, DWORD hIinst, DWORD dwReason); +}; + + + +/*********************************************************************************************** + * DllMain -- called every time a new app requests use of the .dll * + * * + * * + * * + * INPUT: usual windoze junk * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/29/96 10:55AM ST : Created * + *=============================================================================================*/ +BOOL APIENTRY DllMain(DWORD hInst, DWORD dwReason, DWORD dwReserved) + +{ + if (! Thipx_ThunkConnect32("THIPX16.DLL", "THIPX32.DLL", hInst, dwReason)) { + return FALSE; + } + return (TRUE); +} + + +/*********************************************************************************************** + * LibMain -- This just calls DllMain. Watcom erroneously links this in as the entry point! * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/29/96 10:56AM ST : Created * + *=============================================================================================*/ +BOOL APIENTRY LibMain(DWORD hInst, DWORD dwReason, DWORD dwReserved) +{ + return (DllMain(hInst, dwReason, dwReserved)); +} diff --git a/IPX/THMAP16.ASM b/IPX/THMAP16.ASM new file mode 100644 index 0000000..9cce8df --- /dev/null +++ b/IPX/THMAP16.ASM @@ -0,0 +1,60 @@ +; +; 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 . +; + + + + + .8086 + + OPTION READONLY + OPTION OLDSTRUCTS + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + +externdef pascal Thipx_ThunkConnect16_:far16 +extern pascal Thipx_ThunkConnect16:near16 + +Thipx_ThunkConnect16_ proc far16 pascal + + jmp Thipx_ThunkConnect16 + +Thipx_ThunkConnect16_ endp + + + +externdef DllEntryPoint:far16 +extern pascal DllEntryPoint_:far16 + +DllEntryPoint proc far16 + + jmp DllEntryPoint_ + +DllEntryPoint endp + + + + + end + + + + diff --git a/IPX/THMAP32.ASM b/IPX/THMAP32.ASM new file mode 100644 index 0000000..e8284a9 --- /dev/null +++ b/IPX/THMAP32.ASM @@ -0,0 +1,142 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : THMAP32.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : January 25th, 1996 * +;* * +;* Last Update : January 29th, 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Overview: * +;* These functions exist simply to map similar function names to each * +;* other. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + + .model FLAT + + + + .code + + + + + +externdef _IPX_Initialise:near +externdef __IPX_Initialise@0:near +_IPX_Initialise proc near + + jmp __IPX_Initialise@0 + +_IPX_Initialise endp + +externdef _IPX_Open_Socket95:near +externdef __IPX_Open_Socket95@4:near +_IPX_Open_Socket95 proc near + + jmp __IPX_Open_Socket95@4 + +_IPX_Open_Socket95 endp + +externdef _IPX_Close_Socket95:near +externdef __IPX_Close_Socket95@4:near +_IPX_Close_Socket95 proc near + + jmp __IPX_Close_Socket95@4 + +_IPX_Close_Socket95 endp + +externdef _IPX_Get_Connection_Number95:near +externdef __IPX_Get_Connection_Number95@0:near +_IPX_Get_Connection_Number95 proc near + + jmp __IPX_Get_Connection_Number95@0 + +_IPX_Get_Connection_Number95 endp + +externdef _IPX_Send_Packet95:near +externdef __IPX_Send_Packet95@20:near +_IPX_Send_Packet95 proc near + + jmp __IPX_Send_Packet95@20 + +_IPX_Send_Packet95 endp + +externdef _IPX_Broadcast_Packet95:near +externdef __IPX_Broadcast_Packet95@8:near +_IPX_Broadcast_Packet95 proc near + + jmp __IPX_Broadcast_Packet95@8 + +_IPX_Broadcast_Packet95 endp + +externdef _IPX_Get_Local_Target95:near +externdef __IPX_Get_Local_Target95@16:near +_IPX_Get_Local_Target95 proc near + + jmp __IPX_Get_Local_Target95@16 + +_IPX_Get_Local_Target95 endp + +externdef _IPX_Start_Listening95:near +externdef __IPX_Start_Listening95@0:near +_IPX_Start_Listening95 proc near + + jmp __IPX_Start_Listening95@0 + +_IPX_Start_Listening95 endp + +externdef _IPX_Shut_Down95:near +externdef __IPX_Shut_Down95@0:near +_IPX_Shut_Down95 proc near + + jmp __IPX_Shut_Down95@0 + +_IPX_Shut_Down95 endp + + + +externdef _IPX_Get_Outstanding_Buffer95:near +externdef __IPX_Get_Outstanding_Buffer95@4:near + +_IPX_Get_Outstanding_Buffer95 proc near + + jmp __IPX_Get_Outstanding_Buffer95@4 + +_IPX_Get_Outstanding_Buffer95 endp + + + end diff --git a/IPX/WWIPX.ASM b/IPX/WWIPX.ASM new file mode 100644 index 0000000..453a4a0 --- /dev/null +++ b/IPX/WWIPX.ASM @@ -0,0 +1,243 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Jan 16 13:53:22 1996 + +;Command Line: D:\MSTOOLS\BIN\THUNK.EXE wwipx.thk + + TITLE $wwipx.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public wwipx_ThunkData32 ;This symbol must be exported. +wwipx_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_wwipx - offset wwipx_ThunkData32 + dd offset FT_Prolog_wwipx - offset wwipx_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public wwipx_ThunkConnect32@16 +wwipx_ThunkConnect32@16: + pop edx + push offset wwipx_ThkData16 + push offset wwipx_ThunkData32 + push edx + jmp ThunkConnect32@24 +wwipx_ThkData16 label byte + db "wwipx_ThunkData16",0 + + + + + +pfnQT_Thunk_wwipx dd offset QT_Thunk_wwipx +pfnFT_Prolog_wwipx dd offset FT_Prolog_wwipx + .data +QT_Thunk_wwipx label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_wwipx label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public IPX_Initialise@4 +IPX_Initialise@4: + mov cl,0 +; IPX_Initialise(16) = IPX_Initialise(32) {} +; +; dword ptr [ebp+8]: param1 +; +public IIIPX_Initialise@4 +IIIPX_Initialise@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_wwipx] + cwde + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef IPX_Initialise:far16 + + +FT_wwipxTargetTable label word + dw offset IPX_Initialise + dw seg IPX_Initialise + + + + + .data + +public wwipx_ThunkData16 ;This symbol must be exported. +wwipx_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dw offset FT_wwipxTargetTable + dw seg FT_wwipxTargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public wwipx_ThunkConnect16 +wwipx_ThunkConnect16: + pop ax + pop dx + push seg wwipx_ThunkData16 + push offset wwipx_ThunkData16 + push seg wwipx_ThkData32 + push offset wwipx_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +wwipx_ThkData32 label byte + db "wwipx_ThunkData32",0 + + + + + +ENDIF +END diff --git a/IPX/WWIPX16.ASM b/IPX/WWIPX16.ASM new file mode 100644 index 0000000..c7d5a6f --- /dev/null +++ b/IPX/WWIPX16.ASM @@ -0,0 +1,243 @@ +; +; 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 . +; + + page ,132 + +;Thunk Compiler Version 1.8 May 11 1995 13:16:19 +;File Compiled Tue Jan 16 13:14:21 1996 + +;Command Line: D:\MSTOOLS\BIN\THUNK.EXE wwipx16.thk + + TITLE $wwipx16.asm + + .386 + OPTION READONLY + OPTION OLDSTRUCTS + +IFNDEF IS_16 +IFNDEF IS_32 +%out command line error: specify one of -DIS_16, -DIS_32 +.err +ENDIF ;IS_32 +ENDIF ;IS_16 + + +IFDEF IS_32 +IFDEF IS_16 +%out command line error: you can't specify both -DIS_16 and -DIS_32 +.err +ENDIF ;IS_16 +;************************* START OF 32-BIT CODE ************************* + + + .model FLAT,STDCALL + + +;-- Import common flat thunk routines (in k32) + +externDef MapHInstLS :near32 +externDef MapHInstLS_PN :near32 +externDef MapHInstSL :near32 +externDef MapHInstSL_PN :near32 +externDef FT_Prolog :near32 +externDef FT_Thunk :near32 +externDef QT_Thunk :near32 +externDef FT_Exit0 :near32 +externDef FT_Exit4 :near32 +externDef FT_Exit8 :near32 +externDef FT_Exit12 :near32 +externDef FT_Exit16 :near32 +externDef FT_Exit20 :near32 +externDef FT_Exit24 :near32 +externDef FT_Exit28 :near32 +externDef FT_Exit32 :near32 +externDef FT_Exit36 :near32 +externDef FT_Exit40 :near32 +externDef FT_Exit44 :near32 +externDef FT_Exit48 :near32 +externDef FT_Exit52 :near32 +externDef FT_Exit56 :near32 +externDef SMapLS :near32 +externDef SUnMapLS :near32 +externDef SMapLS_IP_EBP_8 :near32 +externDef SUnMapLS_IP_EBP_8 :near32 +externDef SMapLS_IP_EBP_12 :near32 +externDef SUnMapLS_IP_EBP_12 :near32 +externDef SMapLS_IP_EBP_16 :near32 +externDef SUnMapLS_IP_EBP_16 :near32 +externDef SMapLS_IP_EBP_20 :near32 +externDef SUnMapLS_IP_EBP_20 :near32 +externDef SMapLS_IP_EBP_24 :near32 +externDef SUnMapLS_IP_EBP_24 :near32 +externDef SMapLS_IP_EBP_28 :near32 +externDef SUnMapLS_IP_EBP_28 :near32 +externDef SMapLS_IP_EBP_32 :near32 +externDef SUnMapLS_IP_EBP_32 :near32 +externDef SMapLS_IP_EBP_36 :near32 +externDef SUnMapLS_IP_EBP_36 :near32 +externDef SMapLS_IP_EBP_40 :near32 +externDef SUnMapLS_IP_EBP_40 :near32 + +MapSL PROTO NEAR STDCALL p32:DWORD + + + + .code + +;************************* COMMON PER-MODULE ROUTINES ************************* + + .data + +public wwipx16_ThunkData32 ;This symbol must be exported. +wwipx16_ThunkData32 label dword + dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dd 0 ;Jump table address. + dd 3130424ch ;'LB01' + dd 0 ;Flags + dd 0 ;Reserved (MUST BE 0) + dd 0 ;Reserved (MUST BE 0) + dd offset QT_Thunk_wwipx16 - offset wwipx16_ThunkData32 + dd offset FT_Prolog_wwipx16 - offset wwipx16_ThunkData32 + + + + .code + + +externDef ThunkConnect32@24:near32 + +public wwipx16_ThunkConnect32@16 +wwipx16_ThunkConnect32@16: + pop edx + push offset wwipx16_ThkData16 + push offset wwipx16_ThunkData32 + push edx + jmp ThunkConnect32@24 +wwipx16_ThkData16 label byte + db "wwipx16_ThunkData16",0 + + + + + +pfnQT_Thunk_wwipx16 dd offset QT_Thunk_wwipx16 +pfnFT_Prolog_wwipx16 dd offset FT_Prolog_wwipx16 + .data +QT_Thunk_wwipx16 label byte + db 32 dup(0cch) ;Patch space. + +FT_Prolog_wwipx16 label byte + db 32 dup(0cch) ;Patch space. + + + .code + + + + + +;************************ START OF THUNK BODIES************************ + + + + +; +public IPX_Initialise@4 +IPX_Initialise@4: + mov cl,0 +; IPX_Initialise(16) = IPX_Initialise(32) {} +; +; dword ptr [ebp+8]: param1 +; +public IIIPX_Initialise@4 +IIIPX_Initialise@4: + push ebp + mov ebp,esp + push ecx + sub esp,60 + push word ptr [ebp+8] ;param1: dword->word + call dword ptr [pfnQT_Thunk_wwipx16] + cwde + leave + retn 4 + + + + +ELSE +;************************* START OF 16-BIT CODE ************************* + + + + + OPTION SEGMENT:USE16 + .model LARGE,PASCAL + + + .code + + + +externDef IPX_Initialise:far16 + + +FT_wwipx16TargetTable label word + dw offset IPX_Initialise + dw seg IPX_Initialise + + + + + .data + +public wwipx16_ThunkData16 ;This symbol must be exported. +wwipx16_ThunkData16 dd 3130534ch ;Protocol 'LS01' + dd 043bh ;Checksum + dw offset FT_wwipx16TargetTable + dw seg FT_wwipx16TargetTable + dd 0 ;First-time flag. + + + + .code + + +externDef ThunkConnect16:far16 + +public wwipx16_ThunkConnect16 +wwipx16_ThunkConnect16: + pop ax + pop dx + push seg wwipx16_ThunkData16 + push offset wwipx16_ThunkData16 + push seg wwipx16_ThkData32 + push offset wwipx16_ThkData32 + push cs + push dx + push ax + jmp ThunkConnect16 +wwipx16_ThkData32 label byte + db "wwipx16_ThunkData32",0 + + + + + +ENDIF +END diff --git a/LAUNCH/LAUNCH.ASM b/LAUNCH/LAUNCH.ASM new file mode 100644 index 0000000..dec3cc5 --- /dev/null +++ b/LAUNCH/LAUNCH.ASM @@ -0,0 +1,594 @@ +; +; 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 . +; + +;*********************************************************************************************** +;*** 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 -- launcher program * +;* * +;* File Name : launch.asm * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : August 19, 1995 * +;* * +;* Last Update : Modified for RA, October 14th, 1996 [ST] * +;* * +;* Build : Compile and link with MASM 6 * +;* Note that masm will complain there is no stack because * +;* we are not using a simplified segment model * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* Start -- Program entry point * +;* Mem_Error -- report an out of memory error (not a function) * +;* Disc_Error -- report an out of disk space error (not a function) * +;* Delete_Swaps -- Deletes old swap files from the game directory * +;* Setup_Environment -- Adds environment strings required for DOS4G professional * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + + + TITLE "Red Alert launcher" + + .386 + + +;---------------------------------------------------------------------------------------------- +; Defines for DOS system functions. +; +DOS_LOAD_AND_EXEC =4B00H +DOS_ALLOC =48H +DOS_FREE =49h +DOS_GET_FREE_DISC_SPACE =36H +DOS_SET_DIRECTORY =3BH +DOS =21H +DOS_SET_DTA =1AH +DOS_FIND_FIRST =4EH +DOS_FIND_NEXT =4FH +DOS_DELETE_FILE =41H +DOS_SET_ATTRIBUTES =43H +DOS_SET_DRIVE =0EH +DOS_RESIZE_ALLOCATION =4Ah +DOS_EXIT =4ch +DOS_PRINT_STRING =9 + + +;---------------------------------------------------------------------------------------------- +; Defines for keyboard BIOS functions +; +KEYBOARD_BIOS =16h +BIOS_CHECK_KEYBOARD =11h +BIOS_GET_KEYSTROKE =10h + + +;---------------------------------------------------------------------------------------------- +; Defines for video bios functions +; +VIDEO_BIOS =10h +BIOS_SET_VIDEO_MODE_3 =0003h + + +;---------------------------------------------------------------------------------------------- +; Misc defines +; +MEM_REQUIRED =1024 * 400 ;Check for 400k low memory +INIT_FREE_DISK_SPACE =15*1024*1024 ;C&C requirement for disc space + +MY_ALLOCATION =1536/16 ;only allow myself 1.5k to run in incl. psp and stack + ;you can find out how much space the program needs + ;by running WDISASM LAUNCH.OBJ + ;dont forget to add 256 bytes on for the psp + + + include + + +;---------------------------------------------------------------------------------------------- +; Simplified segment models are for wimmin +; +; We want 32 bit instructions in a 16 bit segment +; + +_code segment para public use16 'code' + + assume ds:_data + assume es:_data + assume ss:_data + assume cs:_code + + + +;************************************************************************************************* +;* Start -- Program entry point * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: DOS return code from spawning the game * +;* * +;* HISTORY: * +;* 08/19/95 ST: Created. * +;*===============================================================================================* + +Start: ; + ; Set up the segments and the stack pointer + ; + cli + mov ax,_data + mov ds,ax + mov [Psp],es + mov ss,ax + mov sp,offset MyStack + sti + + ; + ; Modify the starting memory allocation to free enough for the game proper + ; + save ax + mov bx,MY_ALLOCATION + mov ah,DOS_RESIZE_ALLOCATION + int DOS + restore ax + bcs Mem_Error + + ; + ; See if we have enough free memory to launch + ; + mov ah,DOS_ALLOC + mov bx,-1 ;I want it all! hahaha + int DOS + jnc Mem_Error ;shouldnt ever succeed but... + cmp ax,8 + jnz Mem_Error + cmp bx,(MEM_REQUIRED/16)-MY_ALLOCATION + bls Mem_Error ;oh dear + + ; + ; See if there is enough free disc space to launch + ; + xor dl,dl + mov ah,DOS_GET_FREE_DISC_SPACE + int DOS + + ; dos get free disc space returns with the following + ; ax - sectors per cluster + ; bx - number of available clusters + ; cx - bytes per sector + ; dx - cluster on the drive + + mul bx ;clusters avail*sectors per cluster + shl edx,16 + mov dx,ax + mov eax,edx + and ecx,65535 + mul ecx ;*bytes per sector + cmp eax,INIT_FREE_DISK_SPACE + blo Disc_Error + + ; + ; Get the directory we were run from and cd to it + ; + mov es,[Psp] + mov bx,2ch + mov es,es:[bx] ;es points to env + xor di,di + xor al,al + mov cx,-1 + + ; + ; Search for 0,0 as that aways terminates the environment block + ; +@@again: repnz scasb + cmp_b es:[di],al + jnz @@again + lea si,[di+3] ;skip length word and second zero + + ; + ; Copy the directory name to temporary workspace + ; + mov di,si + mov cx,-1 + repnz scasb + neg cx + mov bx,cx + mov cx,-1 + std + mov al,'\' + repnz scasb + neg cx + sub bx,cx + mov cx,bx + inc cx + cld + + mov di,offset Dta + push ds + mov ax,es + mov bx,ds + mov es,bx + mov ds,ax + rep movsb + xor al,al + stosb + pop ds + + ; + ; Actually set the drive and path + ; + mov dl,[Dta] + sub dl,'A' + mov ah,DOS_SET_DRIVE + int DOS + + mov dx,offset Dta + mov ah,DOS_SET_DIRECTORY + int DOS + + ; + ; Delete all the old swap files + ; + mov dx,offset Dta + mov ah,DOS_SET_DTA + int DOS + call Delete_Swaps + + ; + ; Add in the environment strings required for DOS4G professional + ; + call Setup_Environment + + ; + ; Set up the parameters to launch c&c + ; + mov es,[Psp] + mov dx,offset GameName + mov bx,offset GameParameterBlock + + mov ax,[EnvironmentSegment] ;Initialised by Setup_Environment + mov_w [bx],ax ;ptr to environment + + ; + ; just pass the command tail we got straight through + ; + mov ax,80h + mov [bx+2],ax ;offset of command tail + mov ax,[Psp] + mov [bx+4],ax ;segment of command tail + + mov es,ax + mov ax,es:[2ch] + mov [bx+8],ax ;segment of first fcb + mov [bx+12],ax ;segment of second fcb + + mov ax,_data + mov es,ax + + ; + ; Run the main game program + ; + mov ax,DOS_LOAD_AND_EXEC ;load and execute program + int DOS + + + ; + ; Restore teh environment memory + ; + mov ax,[EnvironmentSegment] + test ax,ax + jz @@no_free + mov es,ax + mov ah,DOS_FREE + int DOS + + +@@no_free: ; + ; Quit to DOS + ; + mov ah,DOS_EXIT + int DOS + + + + +;************************************************************************************************* +;* Mem_Error -- Print a memory error message and quit * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: undefined * +;* * +;* HISTORY: * +;* 08/19/95 ST: Created. * +;*===============================================================================================* +Mem_Error: mov ax,BIOS_SET_VIDEO_MODE_3 + int VIDEO_BIOS + mov dx,offset MemErrorTxt + mov ah,DOS_PRINT_STRING + int DOS + + mov dx,offset MoreInfoTxt +Error_In: mov ah,DOS_PRINT_STRING + int DOS + + ; + ; wait for keypress + ; + +nochar: mov ah,BIOS_CHECK_KEYBOARD + int KEYBOARD_BIOS + beq nochar + + ; + ; get the ket out of the buffer + ; + mov ah,BIOS_GET_KEYSTROKE + int KEYBOARD_BIOS + + ; + ; Quit to DOS + ; + mov ah,DOS_EXIT + int DOS + + + + + + +;************************************************************************************************* +;* Disc_Error -- Prints a disk error message and exits * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: DOS return code from spawning the game * +;* * +;* HISTORY: * +;* 08/19/95 ST: Created. * +;*===============================================================================================* +Disc_Error: mov ax,BIOS_SET_VIDEO_MODE_3 + int VIDEO_BIOS + mov dx,offset DiscErrorTxt + jmp Error_In + + + + + + + +;************************************************************************************************* +;* Setup_Environment -- Create a new environment block with the set DOS4GVM= stuff * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* Note: Modifies the global variable 'EnvironmentStrings' to point to the start of our new * +;* environment block. This memory will need to be freed later. * +;* * +;* HISTORY: * +;* 10/14/96 ST: Created. * +;*===============================================================================================* +Setup_Environment proc near + + ; + ; Point to default environment. + ; + mov [EnvironmentSegment],0 + + ; + ; Get the address of our environment block into es + ; + mov es,[Psp] + mov bx,2ch + mov es,es:[bx] ;es points to env + + ; + ; Search for the end of the block to get its length + ; + xor di,di + xor al,al + mov cx,-1 + +@@search: repnz scasb + cmp_b es:[di],al + jnz @@search + + ; + ; di is length of environment block + ; + lea bx,[di+256] ; a little extra to be sure. + + ; + ; Allocate memory for a copy of the environment. + ; + push di + mov ah,DOS_ALLOC + shr bx,4 ;needs to be number of paragraphs + int DOS + jnc @@ok + + ; + ; Oops. Not enough memory. We are screwed so just exit. + ; + pop di + jmp @@out + +@@ok: ; + ; Save the pointer to our new environment + ; We can also use this to free the memory later + ; + mov [EnvironmentSegment],ax + + ; + ; Copy the original environment to the newly alloced memory + ; + mov es,ax + pop cx ; size to copy + + push ds + mov ds,[Psp] + mov bx,2ch + mov ds,ds:[bx] ;ds points to original environment + + xor si,si + xor di,di + rep movsb + pop ds + + mov si,offset Dos4gEnvironment + +@@copy_loop: lodsb + stosb + test al,al + jnz @@copy_loop + + ; + ; Copy the final zero. The environment must be terminated by 2 zeros. + ; + movsb + +@@out: ret + +Setup_Environment endp + + + + + +;************************************************************************************************* +;* Delete_Swaps -- Delete any swap files left over from a previous game * +;* * +;* * +;* INPUT: nothing * +;* * +;* OUTPUT: nothing * +;* * +;* HISTORY: * +;* 08/19/95 ST: Created. * +;*===============================================================================================* +Delete_Swaps proc near + + ; + ; Use 'Find first/next' to find the swap files + ; + mov dx,offset SwapName + mov cx,7 ;attributes + mov ah,DOS_FIND_FIRST + int DOS + bcs @@out ;no matching files + + ; + ; Make sure it isn't read only + ; +@@loop: mov dx,offset Dta+1eh + mov ah,DOS_SET_ATTRIBUTES + mov al,1 + xor cx,cx ;attributes + int DOS + + ; + ; Delete the file + ; + mov dx,offset Dta+1eh + mov ah,DOS_DELETE_FILE + int DOS + + ; + ; Find the next one + ; + mov ah,DOS_FIND_NEXT + int DOS + bcc @@loop + +@@out: ret + +Delete_Swaps endp + + + + + +_code ends ;end of code segment + + + + + + + + + +;---------------------------------------------------------------------------------------------- +; +; Complex data segment +; +; + +_data segment dword public use16 'data' + + +EnvironmentSegment dw 0 ;ptr to environment to pass to child process +Psp dw 0 ;segment addr of program segment prefix +GameParameterBlock db 16h dup (0) ;required parameters for DOS launch + +GameName db "GAME.DAT",0 ;this is the name of the app to be spawned +SwapName db "*.SWP",0 ;swap files always have a .SWP extension + +; +; Environment to be set for DOS4G professional +; +Dos4gEnvironment db "DOS4GVM=SwapMin:12M,SwapInc:0",0,0 + +; +; Error and warning messages +; +MemErrorTxt db "Warning - There is very little free conventional DOS memory in the system.",13,10,"$" + +DiscErrorTxt db "Error - insufficient disk space to run Red Alert." + db 13,10,"Red Alert requires 15,728,640 bytes of free disk space.",13,10 ;this string will + ;run into the next because + ;there is no '$' +MoreInfoTxt db "Please read the Red Alert manual for more information.",13,10,13,10 + db "Press a key to continue.",13,10,"$" + + +Dta db 128 dup (0) ;enough for complete path + + +;************************************************************* +; +; The stack +; + +StackSpace db 256 dup (0) ;256 byte stack! +MyStack label byte ;The stack starts here and grows down. + +EndCode label byte ;The end of the data segment + + +_data ends + +end Start diff --git a/LAUNCH/PCMACRO.16 b/LAUNCH/PCMACRO.16 new file mode 100644 index 0000000..0b47031 --- /dev/null +++ b/LAUNCH/PCMACRO.16 @@ -0,0 +1,778 @@ +; +; 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 . +; + +;$e:\ifil\pcmacro.inc +;- 16-5-1991 at 10:55:13 by mike +;- 16-5-1991 at 08:30:30 by mike +;- 3-5-1991 at 15:39:52 by mike + + + SUBTTL PCMACRO.INC + .xlist + + + + + + +saveall macro + save ax,bx,cx,dx,bp,si,di,es,ds + endm + +restall macro + restore ax,bx,cx,dx,bp,si,di,es,ds + endm + +save macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + push r0 + save r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + ENDIF + endm + +restore macro r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14 + IFNB + pop r14 + ENDIF + IFNB + pop r13 + ENDIF + IFNB + pop r12 + ENDIF + IFNB + pop r11 + ENDIF + IFNB + pop r10 + ENDIF + IFNB + pop r9 + ENDIF + IFNB + pop r8 + ENDIF + IFNB + pop r7 + ENDIF + IFNB + pop r6 + ENDIF + IFNB + pop r5 + ENDIF + IFNB + pop r4 + ENDIF + IFNB + pop r3 + ENDIF + IFNB + pop r2 + ENDIF + IFNB + pop r1 + ENDIF + IFNB + pop r0 + ENDIF + endm + +bhi macro lab + ja lab + endm + +bls macro lab + jbe lab + endm + +bcc macro lab + jnc lab + endm + +bcs macro lab + jc lab + endm + +bhs macro lab + jnc lab + endm + +blo macro lab + jc lab + endm + +bne macro lab + jne lab + endm + +beq macro lab + je lab + endm + +bpl macro lab + jns lab + endm + +bmi macro lab + js lab + endm + +bge macro lab + jge lab + endm + +blt macro lab + jl lab + endm + +bgt macro lab + jg lab + endm + +ble macro lab + jle lab + endm + +bra macro lab + jmp lab + endm + + +bhis macro lab + ja lab + endm + +blss macro lab + jbe lab + endm + +bccs macro lab + jnc lab + endm + +bcss macro lab + jc lab + endm + +bnes macro lab + jne lab + endm + +beqs macro lab + je lab + endm + +bpls macro lab + jns lab + endm + +bmis macro lab + js lab + endm + +bges macro lab + jge lab + endm + +blts macro lab + jl lab + endm + +bgts macro lab + jg lab + endm + +bles macro lab + jle lab + endm + +bras macro lab + jmp lab + endm + + +tstb macro e1 + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN ,
+ or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + cmp BYTE PTR e1,0 + endm + + +tstw macro e1 + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + cmp WORD PTR e1,0 + endm + + + +tst macro e1 + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN ,
+ or e1,e1 + EXITM + ENDIF + + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + IFIDN , + or e1,e1 + EXITM + ENDIF + + cmp e1,0 + endm + + +clear macro first + xor first,first + endm + +rts macro + ret + endm + + +bclrb macro e1,e2 + btstb e1,e2 + pushf + + bclrb_sub e1,e2 + popf + endm + +bclrb_sub macro e1,e2 + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN ,
+ and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + IFIDN , + and e1,NOT ( 1 SHL e2 ) + EXITM + ENDIF + and BYTE PTR e1,NOT ( 1 SHL e2 ) + endm + + + +bsetb macro e1,e2 + btstb e1,e2 + pushf + or BYTE PTR e1,1 SHL e2 + popf + endm + + +bchgb macro e1,e2 + btstb e1,e2 + pushf + xor BYTE PTR e1,1 SHL e2 + popf + endm + + + +mov_b macro label,label2 + mov byte ptr label,byte ptr label2 + endm + + + +mov_w macro label,label2 + mov word ptr label,word ptr label2 + endm + + + +mov_d macro label,label2 + mov dword ptr label,dword ptr label2 + endm + + +cmp_b macro label,label2 + cmp byte ptr label,byte ptr label2 + endm + +cmp_w macro label,label2 + cmp word ptr label,word ptr label2 + endm + +cmp_d macro label,label2 + cmp dword ptr label,dword ptr label2 + endm + + + +add_b macro label,label2 + add byte ptr label,byte ptr label2 + endm + +add_w macro label,label2 + add word ptr label,word ptr label2 + endm + +add_d macro label,label2 + add dword ptr label,dword ptr label2 + endm + + + +sub_b macro label,label2 + sub byte ptr label,byte ptr label2 + endm + +sub_w macro label,label2 + sub word ptr label,word ptr label2 + endm + +sub_d macro label,label2 + sub dword ptr label,dword ptr label2 + endm + + + + + +or_b macro label,label2 + or byte ptr label,byte ptr label2 + endm + +or_w macro label,label2 + or word ptr label,word ptr label2 + endm + +or_d macro label,label2 + or dword ptr label,dword ptr label2 + endm + + + + + + + +xor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +xor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +xor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + + + + + +eor_b macro label,label2 + xor byte ptr label,byte ptr label2 + endm + +eor_w macro label,label2 + xor word ptr label,word ptr label2 + endm + +eor_d macro label,label2 + xor dword ptr label,dword ptr label2 + endm + + + + +and_b macro label,label2 + and byte ptr label,byte ptr label2 + endm + +and_w macro label,label2 + and word ptr label,word ptr label2 + endm + +and_d macro label,label2 + and dword ptr label,dword ptr label2 + endm + + + + + +test_b macro label,label2 + test byte ptr label,byte ptr label2 + endm + +test_w macro label,label2 + test word ptr label,word ptr label2 + endm + +test_d macro label,label2 + test dword ptr label,dword ptr label2 + endm + + + + + + + +shr_b macro label,label2 + shr byte ptr label,label2 + endm + +shr_w macro label,label2 + shr word ptr label,label2 + endm + +shr_d macro label,label2 + shr dword ptr label,label2 + endm + + + +shl_b macro label,label2 + shl byte ptr label,label2 + endm + +shl_w macro label,label2 + shl word ptr label,label2 + endm + +shl_d macro label,label2 + shl dword ptr label,label2 + endm + + + + +sar_b macro label,label2 + sar byte ptr label,label2 + endm + +sar_w macro label,label2 + sar word ptr label,label2 + endm + +sar_d macro label,label2 + sar dword ptr label,label2 + endm + + + + + +sal_b macro label,label2 + sal byte ptr label,label2 + endm + +sal_w macro label,label2 + sal word ptr label,label2 + endm + +sal_d macro label,label2 + sal dword ptr label,label2 + endm + + + +inc_b macro label + inc byte ptr label + endm + +inc_w macro label + inc word ptr label + endm + +inc_d macro label + inc dword ptr label + endm + + + + +dec_b macro label + dec byte ptr label + endm + +dec_w macro label + dec word ptr label + endm + +dec_d macro label + dec dword ptr label + endm + + + + + +movzx_b macro label,label2 + movzx label,byte ptr label2 + endm + + +movzx_w macro label,label2 + movzx label,word ptr label2 + endm + + +movsx_b macro label,label2 + movsx label,byte ptr label2 + endm + + +movsx_w macro label,label2 + movsx label,word ptr label2 + endm + + + + +mul_b macro label + mul byte ptr label + endm + + +mul_w macro label + mul word ptr label + endm + + +div_b macro label + div byte ptr label + endm + + +div_w macro label + div word ptr label + endm + + +idiv_b macro label + idiv byte ptr label + endm + + +idiv_w macro label + idiv word ptr label + endm + + + + +tst_b macro label + cmp byte ptr label,0 + endm + +tst_w macro label + cmp word ptr label,0 + endm + +tst_d macro label + cmp dword ptr label,0 + endm + + + + + + + +not_b macro label + not byte ptr label + endm + +not_w macro label + not word ptr label + endm + +not_d macro label + not dword ptr label + endm + + + + +neg_b macro label + neg byte ptr label + endm + +neg_w macro label + neg word ptr label + endm + +neg_d macro label + neg dword ptr label + endm + + + + + + .list + + diff --git a/LAUNCHER/256BMP.C b/LAUNCHER/256BMP.C new file mode 100644 index 0000000..61dd4cb --- /dev/null +++ b/LAUNCHER/256BMP.C @@ -0,0 +1,213 @@ +/* +** 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 . +*/ + +#include +#include +#include + +DWORD GetDibInfoHeaderSize (BYTE huge *); +WORD GetDibWidth (BYTE huge *); +WORD GetDibHeight (BYTE huge *); +BYTE huge * GetDibBitsAddr (BYTE huge *); +BYTE huge * ReadDib(char *); + +//-------------------------------------------------------------// + +DWORD GetDibInfoHeaderSize (BYTE huge * lpDib) +{ + return ((BITMAPINFOHEADER huge *) lpDib)->biSize ; +} + +WORD GetDibWidth (BYTE huge * lpDib) +{ + if (GetDibInfoHeaderSize (lpDib) == sizeof (BITMAPCOREHEADER)) + return (WORD) (((BITMAPCOREHEADER huge *) lpDib)->bcWidth) ; + else + return (WORD) (((BITMAPINFOHEADER huge *) lpDib)->biWidth) ; +} + +WORD GetDibHeight (BYTE huge * lpDib) +{ + if (GetDibInfoHeaderSize (lpDib) == sizeof (BITMAPCOREHEADER)) + return (WORD) (((BITMAPCOREHEADER huge *) lpDib)->bcHeight) ; + else + return (WORD) (((BITMAPINFOHEADER huge *) lpDib)->biHeight) ; +} + +BYTE huge * GetDibBitsAddr (BYTE huge * lpDib) +{ + DWORD dwNumColors, dwColorTableSize ; + WORD wBitCount ; + + if (GetDibInfoHeaderSize (lpDib) == sizeof (BITMAPCOREHEADER)) + { + wBitCount = ((BITMAPCOREHEADER huge *) lpDib)->bcBitCount ; + + if (wBitCount != 24) + dwNumColors = 1L << wBitCount ; + else + dwNumColors = 0 ; + + dwColorTableSize = dwNumColors * sizeof (RGBTRIPLE) ; + } + else + { + wBitCount = ((BITMAPINFOHEADER huge *) lpDib)->biBitCount ; + + if (GetDibInfoHeaderSize (lpDib) >= 36) + dwNumColors = ((BITMAPINFOHEADER huge *) lpDib)->biClrUsed ; + else + dwNumColors = 0 ; + + if (dwNumColors == 0) + { + if (wBitCount != 24) + dwNumColors = 1L << wBitCount ; + else + dwNumColors = 0 ; + } + + dwColorTableSize = dwNumColors * sizeof (RGBQUAD) ; + } + + return lpDib + GetDibInfoHeaderSize (lpDib) + dwColorTableSize ; +} + +// Read a DIB from a file into memory +BYTE huge * ReadDib (char * szFileName) +{ + BITMAPFILEHEADER bmfh ; + BYTE huge * lpDib ; + DWORD dwDibSize, dwOffset, dwHeaderSize ; + int hFile ; + WORD wDibRead ; + + if (-1 == (hFile = _lopen (szFileName, OF_READ | OF_SHARE_DENY_WRITE))) + return NULL ; + + if (_lread (hFile, (LPSTR) &bmfh, sizeof (BITMAPFILEHEADER)) != + sizeof (BITMAPFILEHEADER)) + { + _lclose (hFile) ; + return NULL ; + } + + if (bmfh.bfType != * (WORD *) "BM") + { + _lclose (hFile) ; + return NULL ; + } + + dwDibSize = bmfh.bfSize - sizeof (BITMAPFILEHEADER) ; + + lpDib = (BYTE huge * ) GlobalAllocPtr (GMEM_MOVEABLE, dwDibSize) ; + + if (lpDib == NULL) + { + _lclose (hFile) ; + return NULL ; + } + + dwOffset = 0 ; + + while (dwDibSize > 0) + { + wDibRead = (WORD) min (32768ul, dwDibSize) ; + + if (wDibRead != _lread (hFile, (LPSTR) (lpDib + dwOffset), wDibRead)) + { + _lclose (hFile) ; + return NULL ; + } + + dwDibSize -= wDibRead ; + dwOffset += wDibRead ; + } + + _lclose (hFile) ; + + dwHeaderSize = GetDibInfoHeaderSize (lpDib) ; + + if (dwHeaderSize < 12 || (dwHeaderSize > 12 && dwHeaderSize < 16)) + return NULL ; + return lpDib ; +} + +long FAR PASCAL _export MainWndProc(HWND hWnd,UINT message,UINT wParam,LONG lParam) +{ + PAINTSTRUCT ps; + HDC hdc; + RECT rect; + unsigned char r,g,b,x; + FILE *fi; + int i; + + static BYTE huge *lpDib; + static BYTE huge *lpDibBits; + static int cxDib, cyDib; + static LPLOGPALETTE LogPal; + static HPALETTE hLogPal; + + switch(message) + { + case WM_CREATE: + fi=fopen("somefile.bmp","r"); + if(fi==NULL) + return 0; + + LogPal=(LPLOGPALETTE)farmalloc(sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*256); + LogPal->palVersion=0x300; + LogPal->palNumEntries=256; + + fseek(fi,54L,0); + for(i=0;i<256;i++) + { + fscanf(fi,"%c%c%c%c",&b,&g,&r,&x); + LogPal->palPalEntry[i].peRed=r; + LogPal->palPalEntry[i].peGreen=g; + LogPal->palPalEntry[i].peBlue=b; + LogPal->palPalEntry[i].peFlags=x; + } + fclose(fi); + hLogPal=CreatePalette(LogPal); + return 0; + case WM_PAINT: + hdc=BeginPaint(hWnd,&ps); + GetClientRect(hWnd,&rect); + SelectPalette(hdc,hLogPal,0); + RealizePalette(hdc); + + SetStretchBltMode(hdc,COLORONCOLOR); + lpDib=ReadDib(bmp_fname); + lpDibBits=GetDibBitsAddr(lpDib); + cxDib=GetDibWidth(lpDib); + cyDib=GetDibHeight(lpDib); + StretchDIBits(hdc,0,0,rect.right,rect.bottom,0,0,cxDib,cyDib,(LPSTR)lpDibBits, + (LPBITMAPINFO)lpDib,DIB_RGB_COLORS,SRCCOPY); + EndPaint(hWnd,&ps); + break; + case WM_DESTROY: + farfree(LogPal); + DeleteObject(hLogPal); + PostQuitMessage(0); + break; + default: + return (DefWindowProc(hWnd,message,wParam,lParam)); + } + return (DefWindowProc(hWnd,message,wParam,lParam)); +} \ No newline at end of file diff --git a/LAUNCHER/BITMAP.CPP b/LAUNCHER/BITMAP.CPP new file mode 100644 index 0000000..fa6ab16 --- /dev/null +++ b/LAUNCHER/BITMAP.CPP @@ -0,0 +1,18 @@ +/* +** 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 . +*/ + diff --git a/LAUNCHER/CONFIGFILE.CPP b/LAUNCHER/CONFIGFILE.CPP new file mode 100644 index 0000000..0960470 --- /dev/null +++ b/LAUNCHER/CONFIGFILE.CPP @@ -0,0 +1,165 @@ +/* +** 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 . +*/ + +/****************************************************************************\ +* 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: Carpenter (The RedAlert ladder creator) +File Name : configfile.cpp +Author : Neal Kettler +Start Date : June 9, 1997 +Last Update : June 17, 1997 + + +This class will read in a config file and store the key value pairs for +later access. This is a fairly simple class, the config file is assumed +to be of the form: + +#comment +key = value + +The value can then be retrieved as a string or an integer. The key on +the left is used for retrieval and it must be specified in uppercase +for the 'get' functions. E.g. getString("KEY",valWstring); +\***************************************************************************/ + +#include +#include +#include + +#include "configfile.h" + +static uint32 Wstring_Hash(Wstring &string); +static char *Eat_Spaces(char *string); + +ConfigFile::ConfigFile() : dictionary(Wstring_Hash) +{ } + +ConfigFile::~ConfigFile() +{ } + +// Read and parse the config file. The key value pairs will be stored +// for later access by the getString/getInt functions. +bit8 ConfigFile::readFile(FILE *in) +{ + char string[256]; + Wstring key; + Wstring value; + char *cptr; + + memset(string,0,256); + while (fgets(string,256,in)) + { + cptr=Eat_Spaces(string); + if ((*cptr==0)||(*cptr=='#')) // '#' signals a comment + continue; + if (strchr(cptr,'=')==NULL) // All config entries must have a '=' + continue; + key=cptr; + key.truncate('='); + key.removeSpaces(); // No spaces allowed in the key + key.toUpper(); // make key all caps + cptr=Eat_Spaces(strchr(cptr,'=')+1); // Jump to after the '=' + value=cptr; + value.truncate('\r'); + value.truncate('\n'); + dictionary.add(key,value); + } + return(TRUE); +} + +// Get a config entry as a string +bit8 ConfigFile::getString(Wstring &key,Wstring &value) +{ + return(dictionary.getValue(key,value)); +} + +// Get a config entry as a string +bit8 ConfigFile::getString(char *key,Wstring &value) +{ + Wstring sKey; + sKey.set(key); + return(getString(sKey,value)); +} + +// Get a config entry as an integer +bit8 ConfigFile::getInt(Wstring &key,sint32 &value) +{ + Wstring svalue; + bit8 retval=dictionary.getValue(key,svalue); + if (retval==FALSE) + return(FALSE); + value=atol(svalue.get()); + return(TRUE); +} + +// Get a config entry as an integer +bit8 ConfigFile::getInt(char *key,sint32 &value) +{ + Wstring sKey; + sKey.set(key); + return(getInt(sKey,value)); +} + + + +// Get a config entry as an integer +bit8 ConfigFile::getInt(Wstring &key,sint16 &value) +{ + Wstring svalue; + bit8 retval=dictionary.getValue(key,svalue); + if (retval==FALSE) + return(FALSE); + value=atoi(svalue.get()); + return(TRUE); +} + +// Get a config entry as an integer +bit8 ConfigFile::getInt(char *key,sint16 &value) +{ + Wstring sKey; + sKey.set(key); + return(getInt(sKey,value)); +} + + + +/************* Static functions below **************/ + +// Given a Wstring, return a 32 bit integer that has a good numeric +// distributation for the purposes of indexing into a hash table. +static uint32 Wstring_Hash(Wstring &string) +{ + uint32 retval=0; + retval=string.length(); + for (uint32 i=0; i>24); // ROL 8 + } + return(retval); +} + +static char *Eat_Spaces(char *string) +{ + char *retval=string; + while (isspace(*retval)) + retval++; + return(retval); +} diff --git a/LAUNCHER/CONFIGFILE.H b/LAUNCHER/CONFIGFILE.H new file mode 100644 index 0000000..e5d22e3 --- /dev/null +++ b/LAUNCHER/CONFIGFILE.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +/****************************************************************************\ +* 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: Carpenter (The RedAlert ladder creator) +File Name : configfile.h +Author : Neal Kettler +Start Date : June 9, 1997 +Last Update : June 17, 1997 +\***************************************************************************/ + +#ifndef CONFIGFILE_HEADER +#define CONFIGFILE_HEADER + +#include "dictionary.h" +#include "wstring.h" + +class ConfigFile +{ + public: + ConfigFile(); + ~ConfigFile(); + bit8 readFile(IN FILE *config); + bit8 getString(IN Wstring &key,OUT Wstring &value); + bit8 getString(IN char *key,OUT Wstring &value); + + bit8 getInt(IN Wstring &key,OUT sint32 &value); + bit8 getInt(IN char *key,OUT sint32 &value); + + bit8 getInt(IN Wstring &key,OUT sint16 &value); + bit8 getInt(IN char *key,OUT sint16 &value); + + private: + Dictionary dictionary; // stores the mappings from keys + // to value strings +}; + +#endif diff --git a/LAUNCHER/DIALOG.CPP b/LAUNCHER/DIALOG.CPP new file mode 100644 index 0000000..3ad8eaf --- /dev/null +++ b/LAUNCHER/DIALOG.CPP @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +// +// Create the dialog used during the patching process. +// +#include"winblows.h" +#include"resource.h" +#include"loadbmp.h" +#include + +HWND PatchDialog; +BOOL CALLBACK Patch_Window_Proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); + +HWND Create_Patch_Dialog(void) +{ + PatchDialog=CreateDialog(Global_instance, MAKEINTRESOURCE(IDD_DIALOG1), + NULL, (DLGPROC)Patch_Window_Proc); + + ShowWindow(PatchDialog, SW_NORMAL); + SetForegroundWindow(PatchDialog); + return(PatchDialog); +} + +BOOL CALLBACK Patch_Window_Proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + + static LoadBmp bmpLoader; + + switch(iMsg) { + case WM_INITDIALOG: + // progress bar + SendMessage(GetDlgItem(hwnd,IDC_PROGRESS2),PBM_SETRANGE, + 0,MAKELPARAM(0,100)); + SendMessage(GetDlgItem(hwnd,IDC_PROGRESS2),PBM_SETPOS,0,0); + SendMessage(GetDlgItem(hwnd,IDC_PROGRESS2),PBM_SETSTEP,10,0); + + bmpLoader.init("launcher.bmp",GetDlgItem(hwnd,IDC_SPLASH)); + return(TRUE); // True means windows handles focus issues + break; + case WM_PAINT: + bmpLoader.drawBmp(); + break; + case WM_COMMAND: + /* May want to add cancel later + switch(wParam) { + case IDCANCEL: + { + // do some stuff + return(TRUE); + } + default: + break; + } + default: + *************/ + break; + case WM_CLOSE: + DestroyWindow(hwnd); + PostQuitMessage(0); + exit(0); + break; + } + return(FALSE); +} \ No newline at end of file diff --git a/LAUNCHER/DIALOG.H b/LAUNCHER/DIALOG.H new file mode 100644 index 0000000..b0c238a --- /dev/null +++ b/LAUNCHER/DIALOG.H @@ -0,0 +1,28 @@ +/* +** 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 . +*/ + +#ifndef DIALOG_HEADER +#define DIALOG_HEADER + +#include"winblows.h" +#include +HWND Create_Patch_Dialog(void); + +extern HWND PatchDialog; + +#endif diff --git a/LAUNCHER/DICTIONARY.H b/LAUNCHER/DICTIONARY.H new file mode 100644 index 0000000..897cda1 --- /dev/null +++ b/LAUNCHER/DICTIONARY.H @@ -0,0 +1,586 @@ +/* +** 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 . +*/ + +/****************************************************************************\ +* 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: Carpenter (The RedAlert ladder creator) +File Name : dictionary.h +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 + +This template file implements a hash dictionary. A hash dictionary is +used to quickly match a value with a key. This works well for very +large sets of data. A table is constructed that has some power of two +number of pointers in it. Any value to be put in the table has a hashing +function applied to the key. That key/value pair is then put in the +linked list at the slot the hashing function specifies. If everything +is working well, this is much faster than a linked list, but only if +your hashing function is good. +\****************************************************************************/ + +#ifndef DICTIONARY_HEADER +#define DICTIONARY_HEADER + +#include +#include +#include +#include + +#include "wstypes.h" +#include "wdebug.h" + +// Every entry in the hash dictionary must be an instance of the DNode +// template. 'K' and 'V' denote Key and Value. +template +class DNode +{ + public: + K key; + V value; + DNode *hashNext; +}; + +template +class Dictionary +{ + public: + Dictionary(uint32 (* hashFn)(K &key)); + ~Dictionary(); + + void clear(void); + bit8 add(IN K &key,IN V &value); + bit8 getValue(IN K &key, OUT V &value); + void print(IN FILE *out) const; + uint32 getSize(void) const; + uint32 getEntries(void) const; + bit8 contains(IN K &key); + bit8 updateValue(IN K &key,IN V &value); + bit8 remove(IN K &key,OUT V &value); + bit8 remove(IN K &key); + bit8 removeAny(OUT K &key,OUT V &value); + bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) const; + + private: + void shrink(void); // halve the number of slots + void expand(void); // double the number of slots + + + DNode **table; // This stores the lists at each slot + + uint32 entries; // number of entries + uint32 size; // size of table + uint32 tableBits; // table is 2^tableBits big + uint32 log2Size; // Junk variable + bit8 keepSize; // If true don't shrink or expand + + uint32 (* hashFunc)(K &key); // User provided hash function + uint32 keyHash(IN K &key); // This will reduce to correct range + + + // See initilizer list of constructor for values + const double SHRINK_THRESHOLD; // When table is this % full shrink it + const double EXPAND_THRESHOLD; // When table is this % full grow it + const int MIN_TABLE_SIZE; // must be a power of 2 +}; + + +//Create the empty hash dictionary +template +Dictionary::Dictionary(uint32 (* hashFn)(K &key)) : + SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it + EXPAND_THRESHOLD(0.80), // When table is 80% full grow it + MIN_TABLE_SIZE(32) // must be a power of 2 +{ + log2Size=MIN_TABLE_SIZE; + size=MIN_TABLE_SIZE; + assert(size>=4); + tableBits=0; + while(log2Size) { tableBits++; log2Size>>=1; } + tableBits--; + size=1< **)new DNode* [size]; + assert(table!=NULL); + + memset((void *)table,0,size*sizeof(void *)); + hashFunc=hashFn; +} + +//Free all the memory... +template +Dictionary::~Dictionary() +{ + clear(); // Remove the entries + delete[](table); // And the table as well +} + +// Remove all the entries and free the memory +template +void Dictionary::clear() +{ + DNode *temp,*del; + uint32 i; + //free all the data + for (i=0; ihashNext; + delete(del); + } + table[i]=NULL; + } + entries=0; + + while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE)) + shrink(); +} + +template +uint32 Dictionary::keyHash(IN K &key) +{ + uint32 retval=hashFunc(key); + retval &= ((1< +void Dictionary::print(IN FILE *out) const +{ + DNode *temp; + uint32 i; + + fprintf(out,"--------------------\n"); + for (i=0; ihashNext; + } + fprintf(out,"\n"); + } + fprintf(out,"--------------------\n"); +} + + +// +// Iterate through all the records. Index is for the table, offset specifies the +// element in the linked list. Set both to 0 and continue calling till false +// is returned. +template +bit8 Dictionary::iterate(INOUT int &index,INOUT int &offset, + OUT V &value) const +{ + DNode *temp; + + // index out of range + if ((index<0)||(index >= getSize())) + return(FALSE); + + temp=table[index]; + while ((temp==NULL)&&((++index) < getSize())) + { + temp=table[index]; + offset=0; + } + + if (temp==NULL) // no more slots with data + return(FALSE); + + uint32 i=0; + while ((temp!=NULL) && (i < offset)) + { + temp=temp->hashNext; + i++; + } + + if (temp==NULL) // should never happen + return(FALSE); + + value=temp->value; + if (temp->hashNext==NULL) + { + index++; + offset=0; + } + else + offset++; + + return(TRUE); +} + + + +// Return the current size of the hash table +template +uint32 Dictionary::getSize(void) const +{ return(size); } + + +// Return the current number of entries in the table +template +uint32 Dictionary::getEntries(void) const +{ return(entries); } + + +// Does the Dictionary contain the key? +template +bit8 Dictionary::contains(IN K &key) +{ + int offset; + DNode *node; + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) + { return(FALSE); } // can't find it + + while(node!=NULL) + { + if ((node->key)==key) + { return(TRUE); } + node=node->hashNext; + } + return(FALSE); +} + + +// Try and update the value of an already existing object +template +bit8 Dictionary::updateValue(IN K &key,IN V &value) +{ + sint32 retval; + + retval=remove(key); + if (retval==FALSE) + return(FALSE); + + add(key,value); + return(TRUE); +} + + +// Add to the dictionary (if key exists, value is updated with the new V) +template +bit8 Dictionary::add(IN K &key,IN V &value) +{ + int offset; + DNode *node,*item,*temp; + float percent; + + item=(DNode *)new DNode; + assert(item!=NULL); + + #ifdef KEY_MEM_OPS + memcpy(&(item->key),&key,sizeof(K)); + #else + item->key=key; + #endif + + #ifdef VALUE_MEM_OPS + memcpy(&(item->value),&value,sizeof(V)); + #else + item->value=value; + #endif + + item->hashNext=NULL; + + //If key already exists, it will be overwritten + remove(key); + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) + { table[offset]=item; } + else + { + temp=table[offset]; + table[offset]=item; + item->hashNext=temp; + } + + entries++; + percent=(float)entries; + percent/=(float)getSize(); + if (percent>= EXPAND_THRESHOLD ) expand(); + + return(TRUE); +} + +// Remove an item from the dictionary +template +bit8 Dictionary::remove(IN K &key,OUT V &value) +{ + int offset; + DNode *node,*last,*temp; + float percent; + + if (entries==0) + return(FALSE); + + percent=(float)(entries-1); + percent/=(float)getSize(); + + offset=keyHash(key); + node=table[offset]; + + last=node; + if (node==NULL) return(FALSE); + + //special case table points to thing to delete + + #ifdef KEY_MEM_OPS + if (0==memcmp(&(node->key),&key,sizeof(K))) + #else + if ((node->key)==key) + #endif + { + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + temp=table[offset]->hashNext; + delete(table[offset]); + table[offset]=temp; + entries--; + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(TRUE); + } + node=node->hashNext; + + //Now the case if the thing to delete is not the first + while (node!=NULL) + { + #ifdef KEY_MEM_OPS + if (0==memcmp(&(node->key),&key,sizeof(K))) + #else + if (node->key==key) + #endif + { + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + last->hashNext=node->hashNext; + entries--; + delete(node); + break; + } + last=node; + node=node->hashNext; + } + + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(TRUE); +} + + +template +bit8 Dictionary::remove(IN K &key) +{ + V temp; + return(remove(key,temp)); +} + + +// Remove some random K/V pair that's in the Dictionary +template +bit8 Dictionary::removeAny(OUT K &key,OUT V &value) +{ + int offset; + DNode *node,*last,*temp; + float percent; + + if (entries==0) + return(FALSE); + + percent=(entries-1); + percent/=(float)getSize(); + + int i; + offset=-1; + for (i=0; i<(int)getSize(); i++) + if (table[i]!=NULL) + { + offset=i; + break; + } + + if (offset==-1) // Nothing there + return(FALSE); + + node=table[offset]; + last=node; + + #ifdef KEY_MEM_OPS + memcpy(&key,&(node->key),sizeof(K)); + #else + key=node->key; + #endif + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + + temp=table[offset]->hashNext; + delete(table[offset]); + table[offset]=temp; + entries--; + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(TRUE); +} + +template +bit8 Dictionary::getValue(IN K &key,OUT V &value) +{ + int offset; + DNode *node; + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) return(FALSE); + + #ifdef KEY_MEM_OPS + while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K)))) + #else + while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't + #endif // have to do oper != + { node=node->hashNext; } + + if (node==NULL) + { return(FALSE); } + + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=(node->value); + #endif + return(TRUE); +} + + +//A note about Shrink and Expand: They are never necessary, they are +//only here to improve performance of the hash table by reducing +//the length of the linked list at each table entry. + +// Shrink the hash table by a factor of 2 (and relocate entries) +template +void Dictionary::shrink(void) +{ + int i; + int oldsize; + uint32 offset; + DNode **oldtable,*temp,*first,*next; + + if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE)) + return; + + //fprintf(stderr,"Shrinking....\n"); + + oldtable=table; + oldsize=size; + size/=2; + tableBits--; + + table=(DNode **)new DNode*[size]; + assert(table!=NULL); + memset((void *)table,0,size*sizeof(void *)); + + for (i=0; ikey); + first=table[offset]; + table[offset]=temp; + next=temp->hashNext; + temp->hashNext=first; + temp=next; + } + } + delete[](oldtable); +} + + +template +void Dictionary::expand(void) +{ + int i; + int oldsize; + uint32 offset; + DNode **oldtable,*temp,*first,*next; + + if (keepSize==TRUE) + return; + + //fprintf(stderr,"Expanding...\n"); + + oldtable=table; + oldsize=size; + size*=2; + tableBits++; + + table=(DNode **)new DNode* [size]; + assert(table!=NULL); + memset((void *)table,0,size*sizeof(void *)); + + for (i=0; ikey); + first=table[offset]; + table[offset]=temp; + next=temp->hashNext; + temp->hashNext=first; + temp=next; + } + } + delete[](oldtable); +} + +#endif diff --git a/LAUNCHER/FILED.H b/LAUNCHER/FILED.H new file mode 100644 index 0000000..cdfcce9 --- /dev/null +++ b/LAUNCHER/FILED.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef FILED_HEADER +#define FILED_HEADER + +#include "odevice.h" + +class FileD : public OutputDevice +{ + public: + FileD(char *filename) + { + out=fopen(filename,"w"); + if (out==NULL) + out=fopen("FileDev.out","w"); + } + + virtual ~FileD() + { fclose(out); } + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + fprintf(out,"%s",string); + delete[](string); + fflush(out); + return(len); + } + + FILE *out; +}; + +#endif diff --git a/LAUNCHER/FINDPATCH.CPP b/LAUNCHER/FINDPATCH.CPP new file mode 100644 index 0000000..7d45f95 --- /dev/null +++ b/LAUNCHER/FINDPATCH.CPP @@ -0,0 +1,203 @@ +/* +** 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 . +*/ + +#include "findpatch.h" + +// +// Locate a patch file +// If a patch can be found then TRUE is returned and the name is filled in, +// otherwise FALSE is returned. +// +// Patch Types: +// - *.rtp = RTPatch file that can be applied right now +// - *.exe = Executable that should be put in the RunOnce registry entry & reboot +// - *.exn = Executable that should be run right now +// - *.web = Link to a web page that will have the patch +// - else = File is ignored, possibly a resource file for one of the other types +// +int Find_Patch(OUT char *filename,int maxlen, ConfigFile &config) +{ + WIN32_FIND_DATA findData; + char string[128]; + HANDLE hFile; + char *extensions[]={"web","exe","exn","rtp",NULL}; + int i; + int skuIndex=0; + Wstring key; + Wstring path; + Wstring sku; + char gamePath[MAX_PATH]; + bit8 ok; + + + while(1) + { + // + // Loop through the apps we're responsible for + // + skuIndex++; + ok=Get_App_Dir(gamePath,MAX_PATH,config,skuIndex); + if (ok==FALSE) + break; + + i=0; + while(extensions[i++]) + { + _chdir(gamePath); // goto the directory with the game + + // should probably get the registry entry for the wchat install path + sprintf(string,"patches\\*.%s",extensions[i]); + hFile=FindFirstFile(string,&findData); + if (hFile!=INVALID_HANDLE_VALUE) + { + _getcwd(filename,MAX_PATH); + strcat(filename,"\\patches\\"); + strcat(filename,findData.cFileName); + FindClose(hFile); + return(skuIndex); + } + } + } + return(FALSE); +} + + +// +// Get the directory for the N'th application in the config file +// +// Returns FALSE if not in the config file or invalid for some reason. +// +bit8 Get_App_Dir(OUT char *filename,int maxlen, ConfigFile &config,int index) +{ + char string[128]; + Wstring key; + Wstring path; + Wstring sku; + int temp; + char gamePath[MAX_PATH]; + + + sprintf(string,"SKU%d",index); + + // Can't find this product + if (config.getString(string,key)==FALSE) + return(FALSE); + + + DBGMSG("KEY = "<. +*/ + +#ifndef FINDPATCH_HEADER +#define FINDPATCH_HEADER + +#include +#include +#include +#include +#include "wstypes.h" +#include "configfile.h" + +int Find_Patch(OUT char *file,int maxlen, ConfigFile &config); +bit8 Get_App_Dir(OUT char *file,int maxlen, ConfigFile &config, int index); +void Delete_Patches(ConfigFile &config); + +#endif \ No newline at end of file diff --git a/LAUNCHER/LAUNCHER.DSP b/LAUNCHER/LAUNCHER.DSP new file mode 100644 index 0000000..1bb5c9e --- /dev/null +++ b/LAUNCHER/LAUNCHER.DSP @@ -0,0 +1,219 @@ +# Microsoft Developer Studio Project File - Name="launcher" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=launcher - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "launcher.mak" CFG="launcher - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "launcher - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "launcher - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "launcher - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /map /machine:I386 + +!ELSEIF "$(CFG)" == "launcher - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "DEBUG" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib patchw32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept /libpath:"." + +!ENDIF + +# Begin Target + +# Name "launcher - Win32 Release" +# Name "launcher - Win32 Debug" +# Begin Group "dbglib" + +# PROP Default_Filter ".cpp,.h" +# Begin Source File + +SOURCE=.\configfile.cpp +# End Source File +# Begin Source File + +SOURCE=.\configfile.h +# End Source File +# Begin Source File + +SOURCE=.\dictionary.h +# End Source File +# Begin Source File + +SOURCE=.\filed.h +# End Source File +# Begin Source File + +SOURCE=.\monod.cpp +# End Source File +# Begin Source File + +SOURCE=.\monod.h +# End Source File +# Begin Source File + +SOURCE=.\odevice.h +# End Source File +# Begin Source File + +SOURCE=.\streamer.cpp +# End Source File +# Begin Source File + +SOURCE=.\streamer.h +# End Source File +# Begin Source File + +SOURCE=.\wdebug.cpp +# End Source File +# Begin Source File + +SOURCE=.\wdebug.h +# End Source File +# Begin Source File + +SOURCE=.\wstring.cpp +# End Source File +# Begin Source File + +SOURCE=.\wstring.h +# End Source File +# End Group +# Begin Group "util" + +# PROP Default_Filter "" +# End Group +# Begin Source File + +SOURCE=.\dialog.cpp +# End Source File +# Begin Source File + +SOURCE=.\dialog.h +# End Source File +# Begin Source File + +SOURCE=.\findpatch.cpp +# End Source File +# Begin Source File + +SOURCE=.\findpatch.h +# End Source File +# Begin Source File + +SOURCE=.\launcher1.rc +# End Source File +# Begin Source File + +SOURCE=.\loadbmp.cpp +# End Source File +# Begin Source File + +SOURCE=.\loadbmp.h +# End Source File +# Begin Source File + +SOURCE=.\main.cpp +# End Source File +# Begin Source File + +SOURCE=.\patch.cpp +# End Source File +# Begin Source File + +SOURCE=.\patch.h +# End Source File +# Begin Source File + +SOURCE=.\process.cpp +# End Source File +# Begin Source File + +SOURCE=.\process.h +# End Source File +# Begin Source File + +SOURCE=.\Redalert.ico +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# Begin Source File + +SOURCE=.\winblows.cpp +# End Source File +# Begin Source File + +SOURCE=.\winblows.h +# End Source File +# Begin Source File + +SOURCE=.\wstypes.h +# End Source File +# End Target +# End Project diff --git a/LAUNCHER/LAUNCHER.DSW b/LAUNCHER/LAUNCHER.DSW new file mode 100644 index 0000000..5a5f10a --- /dev/null +++ b/LAUNCHER/LAUNCHER.DSW @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "launcher"=.\launcher.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/LAUNCHER/LAUNCHER1.RC b/LAUNCHER/LAUNCHER1.RC new file mode 100644 index 0000000..4b92b64 --- /dev/null +++ b/LAUNCHER/LAUNCHER1.RC @@ -0,0 +1,150 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 243, 126 +STYLE DS_MODALFRAME | DS_SETFOREGROUND | WS_MINIMIZEBOX | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "Patching" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_SPLASH,"Static",SS_GRAYRECT | SS_SUNKEN,7,7,229, + 70 + GROUPBOX "File",IDC_CAPTION,7,78,229,22 + CONTROL "Progress1",IDC_PROGRESS2,"msctls_progress32",WS_BORDER, + 7,105,229,13 + CONTROL "",IDC_FILENAME,"Static",SS_LEFTNOWORDWRAP | + SS_CENTERIMAGE | WS_GROUP,13,85,217,13 +END + +IDD_CHANGELOG DIALOG DISCARDABLE 0, 0, 332, 204 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Update Information" +FONT 10, "Lucida Console" +BEGIN + EDITTEXT IDC_TEXT,5,5,322,178,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | WS_VSCROLL | WS_HSCROLL + PUSHBUTTON "OK",IDOK,128,185,76,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_DIALOG1, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 236 + TOPMARGIN, 7 + BOTTOMMARGIN, 119 + END + + IDD_CHANGELOG, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 327 + TOPMARGIN, 5 + BOTTOMMARGIN, 199 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON2 ICON DISCARDABLE "redalert.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_SYS_RESTART "Your computer needs to be restarted to continue the patch. Press OK to continue." + IDS_SYS_RESTART_TITLE "About to restart" + IDS_WEBPATCH "You need to download a patch from our website.\rWhen you press OK, your browser will automatically open to the download page." + IDS_WEBPATCH_TITLE "Web Patch" + IDS_FILE_X_OF_Y "File (%d of %d)" + IDS_MUST_RESTART "You must restart your computer now." + IDS_RUNONCE_ERR "Could not set the patch to run on system reboot!\rYou must exit the game and run %s to complete the patch." + IDS_ERROR "Error" + IDS_ERR_MISSING_FILE "Error: Couldn't find file: %s" + IDS_BAD_LIBRARY "A required DLL is corrupt." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/LAUNCHER/LOADBMP.CPP b/LAUNCHER/LOADBMP.CPP new file mode 100644 index 0000000..8c76bd1 --- /dev/null +++ b/LAUNCHER/LOADBMP.CPP @@ -0,0 +1,214 @@ +/* +** 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 . +*/ + +#include"loadbmp.h" + + +LoadBmp::LoadBmp() +{ + BitmapHandle_=NULL; + PalHandle_=NULL; +} + +LoadBmp::~LoadBmp() +{ + // free resources + DeleteObject(BitmapHandle_); + DeleteObject(PalHandle_); +} + +// +// Load a specified bitmap for later display on a window +// +bit8 LoadBmp::init(char *filename,HWND hwnd) +{ + int i; + char string[128]; + HANDLE hBitmapFile; + DWORD dwRead; + BITMAPFILEHEADER bitmapHeader; + BITMAPINFOHEADER bitmapInfoHeader; + LPLOGPALETTE lpLogPalette; + char *palData; + HGLOBAL hmem2; + LPVOID lpvBits; + PAINTSTRUCT ps; + HDC hdc; + HPALETTE select; + UINT realize; + RECT rect; + + + // Set the member for future reference + WindowHandle_=hwnd; + + // Retrieve a handle identifying the file. + hBitmapFile = CreateFile( + filename, + GENERIC_READ, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES) NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + (HANDLE) NULL); + + if (hBitmapFile==NULL) + return(FALSE); + + // Retrieve the BITMAPFILEHEADER structure. + ReadFile(hBitmapFile, &bitmapHeader, sizeof(BITMAPFILEHEADER), &dwRead, + (LPOVERLAPPED)NULL); + + + // Retrieve the BITMAPFILEHEADER structure. + ReadFile(hBitmapFile, &bitmapInfoHeader, sizeof(BITMAPINFOHEADER), + &dwRead, (LPOVERLAPPED)NULL); + + + // Allocate memory for the BITMAPINFO structure. + HGLOBAL infoHeaderMem = GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER) + + ((1<bmiHeader.biSize = bitmapInfoHeader.biSize; + lpHeaderMem->bmiHeader.biWidth = bitmapInfoHeader.biWidth; + lpHeaderMem->bmiHeader.biHeight = bitmapInfoHeader.biHeight; + lpHeaderMem->bmiHeader.biPlanes = bitmapInfoHeader.biPlanes; + lpHeaderMem->bmiHeader.biBitCount = bitmapInfoHeader.biBitCount; + lpHeaderMem->bmiHeader.biCompression = bitmapInfoHeader.biCompression; + lpHeaderMem->bmiHeader.biSizeImage = bitmapInfoHeader.biSizeImage; + lpHeaderMem->bmiHeader.biXPelsPerMeter = bitmapInfoHeader.biXPelsPerMeter; + lpHeaderMem->bmiHeader.biYPelsPerMeter = bitmapInfoHeader.biYPelsPerMeter; + lpHeaderMem->bmiHeader.biClrUsed = bitmapInfoHeader.biClrUsed; + lpHeaderMem->bmiHeader.biClrImportant = bitmapInfoHeader.biClrImportant; + + + // Retrieve the color table. + // 1 << bitmapInfoHeader.biBitCount == 2 ^ bitmapInfoHeader.biBitCount + ReadFile(hBitmapFile, lpHeaderMem->bmiColors, + ((1<palVersion=0x300; + lpLogPalette->palNumEntries=256; + + palData=(char *)lpHeaderMem->bmiColors; + + for (i=0; i<256; i++) + { + lpLogPalette->palPalEntry[i].peRed=*palData++; + lpLogPalette->palPalEntry[i].peGreen=*palData++; + lpLogPalette->palPalEntry[i].peBlue=*palData++; + lpLogPalette->palPalEntry[i].peFlags=*palData++; + } + PalHandle_=CreatePalette(lpLogPalette); + delete(lpLogPalette); + + + // Allocate memory for the required number of bytes. + hmem2 = GlobalAlloc(GHND, (bitmapHeader.bfSize - bitmapHeader.bfOffBits)); + + lpvBits = GlobalLock(hmem2); + + // Retrieve the bitmap data. + ReadFile(hBitmapFile, lpvBits, (bitmapHeader.bfSize - bitmapHeader.bfOffBits), + &dwRead, (LPOVERLAPPED) NULL); + + + // Create a bitmap from the data stored in the .BMP file. + hdc=GetDC(hwnd); + select=SelectPalette(hdc,PalHandle_,0); + if (select==NULL) + return(FALSE); + realize=RealizePalette(hdc); + if (realize==GDI_ERROR) + return(FALSE); + BitmapHandle_=CreateDIBitmap(hdc, &bitmapInfoHeader, CBM_INIT, lpvBits, lpHeaderMem, DIB_RGB_COLORS); + ReleaseDC(hwnd,hdc); + + + if (BitmapHandle_==NULL) + return(FALSE); + + // Unlock the global memory objects and close the .BMP file. + GlobalUnlock(infoHeaderMem); + GlobalUnlock(hmem2); + CloseHandle(hBitmapFile); + + if (BitmapHandle_==NULL) + return(FALSE); + + // Inform windows the window needs to be repainted + GetClientRect(hwnd, &rect); + InvalidateRect(hwnd, &rect, TRUE); + UpdateWindow(hwnd); + + return(TRUE); +} + + +bit8 LoadBmp::drawBmp(void) +{ + // Paint the window (and draw the bitmap). + + PAINTSTRUCT ps; + HDC hdc; + char string[128]; + + InvalidateRect(WindowHandle_,NULL,FALSE); // keep windows from screwing up the + // redrawing (as much). + hdc=BeginPaint(WindowHandle_,&ps); + + //Do palette stuff + HPALETTE select=SelectPalette(ps.hdc,PalHandle_,0); + if (select==NULL) + { + sprintf(string,"Select Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + UINT realize=RealizePalette(ps.hdc); + if (realize==GDI_ERROR) + { + sprintf(string,"Realize Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + + HDC hdcMem = CreateCompatibleDC(ps.hdc); + SelectObject(hdcMem, BitmapHandle_); + BITMAP bm; + GetObject(BitmapHandle_, sizeof(BITMAP), (LPSTR) &bm); + + /// for non-stretching version + ///////BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + RECT clientRect; + GetClientRect(WindowHandle_,&clientRect); + SetStretchBltMode(ps.hdc,COLORONCOLOR); + StretchBlt(ps.hdc,0,0,clientRect.right,clientRect.bottom,hdcMem,0,0,bm.bmWidth, + bm.bmHeight,SRCCOPY); + + + DeleteDC(hdcMem); + EndPaint(WindowHandle_,&ps); + return(TRUE); +} diff --git a/LAUNCHER/LOADBMP.H b/LAUNCHER/LOADBMP.H new file mode 100644 index 0000000..e80f2f6 --- /dev/null +++ b/LAUNCHER/LOADBMP.H @@ -0,0 +1,46 @@ +/* +** 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 . +*/ + +#ifndef LOADBMP_HEADER +#define LOADBMP_HEADER + +#include +#include +#include "wstypes.h" +#include "winblows.h" + + +// +// Functions and data assocated with a loaded bitmap on a single window. +// +class LoadBmp +{ + public: + LoadBmp(); + ~LoadBmp(); + bit8 init(char *filename,HWND hwnd); // must call before the drawBmp + bit8 drawBmp(void); // call this from your WM_PAINT message + + private: + HBITMAP BitmapHandle_; + HPALETTE PalHandle_; + HWND WindowHandle_; +}; + + +#endif \ No newline at end of file diff --git a/LAUNCHER/MAIN.CPP b/LAUNCHER/MAIN.CPP new file mode 100644 index 0000000..91f5f76 --- /dev/null +++ b/LAUNCHER/MAIN.CPP @@ -0,0 +1,270 @@ +/* +** 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 . +*/ + +/*****************************************************************************\ + 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 +******************************************************************************* + File: main.cpp + Programmer: Neal Kettler + + StartDate: Feb 6, 1998 + LastUpdate: Feb 10, 1998 +------------------------------------------------------------------------------- + + Launcher application for games/apps using the chat API. This should be + run by the user and it will start the actual game executable. If a patch + file has been downloaded the patch will be applied before starting the game. + + This does not download patches or do version checks, the game/app is responsible + for that. This just applies patches that are in the correct location for the + game. All patches should be in the "Patches" folder of the app. + + The launcher should have a config file (launcher.cfg) so it knows which apps + should be checked for patches. The file should look like this: + + # comment + # RUN = the game to launch + RUN = . notepad.exe # directory and app name + # + # Sku's to check for patches + # + SKU1 = 1100 SOFTWARE\Westwood\WOnline # skus and registry keys + SKU2 = 1234 SOFTWARE\Westwood\FakeGame + +\*****************************************************************************/ + + +#include "dialog.h" +#include "patch.h" +#include "findpatch.h" +#include "process.h" + +#include "wdebug.h" +#include "monod.h" +#include "filed.h" +#include "configfile.h" +#include + +#define UPDATE_RETVAL 123456789 // if a program returns this it means it wants to check for patches + +void CreatePrimaryWin(char *prefix); +void myChdir(char *path); + + +// +// Called by WinMain +// +int main(int argc, char *argv[]) +{ + char patchFile[MAX_PATH]; + bit8 ok; + int skuIndex=0; + char cwd[MAX_PATH]; // save current directory before game start + _getcwd(cwd,MAX_PATH); + + InitCommonControls(); + + #ifdef DEBUG + ///MonoD outputDevice; + FileD outputDevice("launcher.out"); + MsgManager::setAllStreams(&outputDevice); + DBGMSG("Launcher initialized"); + #endif + + + // Goto the folder where launcher is installed + myChdir(argv[0]); + + // extract the program name from argv[0]. Change the extension to + // .lcf (Launcher ConFig). This is the name of our config file. + char configName[MAX_PATH+3]; + strcpy(configName,argv[0]); + char *extension=configName; + char *tempptr; + while((tempptr=strchr(extension+1,'.'))) + extension=tempptr; + if (*extension=='.') + *extension=0; + strcat(configName,".lcf"); + + DBGMSG("Config Name: "<. +*/ + +#include "monod.h" + +MonoD::MonoD(void) +{ +#ifdef _WIN32 + unsigned long retval; + handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, + &retval,0); + } +#endif +} + +MonoD::~MonoD() +{ + #ifdef _WIN32 + CloseHandle(handle); + handle=NULL; + #endif +} + +int MonoD::print(const char *str, int len) +{ + #ifdef _WIN32 + unsigned long retval; + WriteFile(handle, str, len, &retval, NULL); + //DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0, + // &retval,0); + return(len); + #else + for (int i=0; i. +*/ + +#ifndef MONOD_HEADER +#define MONOD_HEADER + + +#include +#include +#include "odevice.h" + +///////////////////////// WIN32 ONLY /////////////////////////////////// + +#ifdef _WIN32 +#include +#include + + +/* +** This is the identifier for the Monochrome Display Driver +*/ +#define FILE_DEVICE_MONO 0x00008000 + +/* +** These are the IOCTL commands supported by the Monochrome Display Driver. +*/ +#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA) + +#endif // ifdef _WIN32 + +class MonoD : public OutputDevice +{ + public: + MonoD(); + ~MonoD(); + + virtual int print(const char *str,int len); + + private: + #ifdef _WIN32 + HANDLE handle; + #endif +}; + +#endif diff --git a/LAUNCHER/ODEVICE.H b/LAUNCHER/ODEVICE.H new file mode 100644 index 0000000..c3d6abe --- /dev/null +++ b/LAUNCHER/ODEVICE.H @@ -0,0 +1,32 @@ +/* +** 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 . +*/ + +#ifndef ODEVICE_HEADER +#define ODEVICE_HEADER + +// This virtual base class provides an interface for output devices +// that can be used for the debugging package. +class OutputDevice +{ + public: + OutputDevice() {} + virtual ~OutputDevice() {}; + virtual int print(const char *s,int len)=0; +}; + +#endif diff --git a/LAUNCHER/PATCH.CPP b/LAUNCHER/PATCH.CPP new file mode 100644 index 0000000..1ff5883 --- /dev/null +++ b/LAUNCHER/PATCH.CPP @@ -0,0 +1,548 @@ +/* +** 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 . +*/ + +/*****************************************************************************\ + 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 +******************************************************************************* + File: patch.cpp + Programmer: Neal Kettler + + StartDate: Feb 6, 1998 + LastUpdate: Feb 10, 1998 +------------------------------------------------------------------------------- + +This is where all the code is for applying various types of patches. + +\*****************************************************************************/ + + +#include "patch.h" +#include +#include + + +// +// For the text box showing patch info +// +BOOL CALLBACK Update_Info_Proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + + static int unselectText=0; + + switch(iMsg) + { + case WM_INITDIALOG: + { + FILE *in = fopen("launcher.txt","r"); + if (in==NULL) + { + EndDialog(hwnd,-1); + return(1); + } + + char line[270]; + int lastsel=0; + char *cptr=NULL; + while(fgets(line,255,in)) + { + //Get rid of any trailing junk + while(1) + { + if (strlen(line)<1) + break; + cptr=line+(strlen(line))-1; + if ((*cptr=='\r')||(*cptr=='\n')) + *cptr=0; + else + break; + } + // ...and add back the gunk that windows likes + strcat(line,"\r\r\n"); + + SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, (WPARAM)lastsel, (LPARAM)lastsel ); + SendDlgItemMessage(hwnd, IDC_TEXT, EM_REPLACESEL, 0, (LPARAM)(line) ); + SendDlgItemMessage(hwnd, IDC_TEXT, EM_GETSEL, (WPARAM)NULL, (LPARAM)&lastsel ); + } + unselectText=1; + fclose(in); + + return(1); // 1 means windows handles focus issues + } + break; + + case WM_PAINT: + if (unselectText) + SendDlgItemMessage(hwnd, IDC_TEXT, EM_SETSEL, -1, 0); + unselectText=0; + return(0); + break; + + + case WM_COMMAND: + switch(wParam) { + case IDOK: + { + EndDialog(hwnd,0); + return(1); + } + default: + break; + } + default: + break; + case WM_CLOSE: + EndDialog(hwnd,0); + return(1); + break; + } + return(FALSE); +} + + + + + + + +// Restart the computer for certain types of patches +void Shutdown_Computer_Now(void); + + +LPVOID CALLBACK __export PatchCallBack(UINT ID, LPVOID Param); + +typedef LPVOID (CALLBACK* PATCHCALLBACK)(UINT, LPVOID); +typedef UINT (CALLBACK *PATCHFUNC)( LPSTR, PATCHCALLBACK, BOOL); + +// +// Apply any type of patch. Filename in patchfile. Product base registry +// (eg: "SOFTWARE\Westwood\Red Alert") should be in the config file as +// SKUX SKU base reg dir where X = index +// +void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex) +{ + DBGMSG("PATCHFILE : "<SetRTPErrCode(Id); + + int percent; + + switch( Id ) + { + case 1: + case 2: + // Warning message header/text + DBGMSG("P_MSG: "<<((char *)Param)); + break; + + case 3: + // Error message header + DBGMSG("P_MSG: "<<((char *)Param)); + break; + + case 4: + // Error message header/text + ///////*g_LogFile << (char *)Parm << endl; + MessageBox(NULL,(char *)Param,"ERROR",MB_OK); + { + FILE *out=fopen("patch.err","a"); + time_t timet=time(NULL); + fprintf(out,"\n\nPatch Erorr: %s\n",ctime(&timet)); + fprintf(out,"%s\n",(char *)Param); + fclose(out); + } + break; + + case 9: + // progress message + break; + + case 0xa: + // help message + break; + + case 0xb: + // patch file comment + break; + + case 0xc: + // copyright message + break; // these just display text + + case 5: + // % completed + // so adjust the progress bar using the global Dialog pointer + /////////g_DlgPtr->SetProgressBar((int)((float)(*(UINT *)Parm)/(float)0x8000*(float)100)); + percent=((*(UINT *)Param)*100)/0x8000; + SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0); + break; + + case 6: + // Number of patch files + DBGMSG("6: "<<*((uint32 *)Param)); + fileCount=*((uint32 *)Param); + currFile=0; + break; + + case 7: + //// begin patch + //LoadString(g_AppInstance, IDS_PROCESSING, lpcBuf, 256); + //strcpy(buf,lpcBuf); + //strcat(buf,(char *)Parm); + //g_DlgPtr->SetProgressText(buf); + //*g_LogFile << buf << " : "; + //fileModified = true; + + DBGMSG("7: "<<(char *)Param); + SetWindowText(GetDlgItem(PatchDialog,IDC_FILENAME),(char *)Param); + percent=0; + SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0); + + currFile++; + char xofy[64]; + LoadString(NULL,IDS_FILE_X_OF_Y,xofy,64); + sprintf(string,xofy,currFile,fileCount); + SetWindowText(GetDlgItem(PatchDialog,IDC_CAPTION),string); + + break; + + case 8: + //// end patch + //LoadString(g_AppInstance, IDS_PROCCOMPLETE, lpcBuf, 256); + //g_DlgPtr->SetProgressText(lpcBuf); + //*g_LogFile << " complete" << endl; + percent=100; + SendMessage(GetDlgItem(PatchDialog,IDC_PROGRESS2),PBM_SETPOS,percent,0); + DBGMSG("P_DONE"); + break; + + case 0xd: + //// this one shouldn't happen (only occurs if the command line + //// doesn't have a patch file in it, and we insure that it does). + //Abort = TRUE; + //*g_LogFile << "Incorrect (or none) patch file specified in command line." << endl; + break; + + case 0xe: + //// this one shouldn't happen either (same reason) + //Abort = TRUE; + //*g_LogFile << "Incorrect (or none) path specified in command line." << endl; + break; + + case 0xf: + //// Password Dialog + break; + + case 0x10: + //// Invalid Password Alert + break; + + case 0x11: + //// Disk Change Dialog + break; + + case 0x12: + //// Disk Change Alert + break; + + case 0x13: + //// Confirmation Dialog + break; + + case 0x14: + //// Location Dialog + //Abort = TRUE; + //*g_LogFile << "Specified path is incorrect." << endl; + break; + + case 0x16: + //// Searching Call-back + break; + + case 0x15: + //// Idle... + break; + + default: + break; + } + + if(Abort) + return (NULL); + else + return (RetVal); +} diff --git a/LAUNCHER/PATCH.H b/LAUNCHER/PATCH.H new file mode 100644 index 0000000..c88a04c --- /dev/null +++ b/LAUNCHER/PATCH.H @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +#ifndef PATCH_HEADER +#define PATCH_HEADER + +#include "winblows.h" +#include "dialog.h" +#include "resource.h" +#include "wdebug.h" +#include "process.h" + +void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex); + +// Print an error message +#define ERRMSGX(X,F)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + strcpy(timebuf,ctime(&clock)); \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \ + F << " " << __LINE__ << "] " << X << endl; \ +} + +#endif diff --git a/LAUNCHER/PROCESS.CPP b/LAUNCHER/PROCESS.CPP new file mode 100644 index 0000000..c3ac312 --- /dev/null +++ b/LAUNCHER/PROCESS.CPP @@ -0,0 +1,138 @@ +/* +** 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 . +*/ + +#include "process.h" + +Process::Process() +{ + directory[0]=0; + command[0]=0; + args[0]=0; + hProcess=NULL; + hThread=NULL; +} + +// Create a process +bit8 Create_Process(Process &process) +{ + int retval; + STARTUPINFO si; + PROCESS_INFORMATION piProcess; + ZeroMemory(&si,sizeof(si)); + si.cb=sizeof(si); + + char cmdargs[513]; + memset(cmdargs,0,513); + strcpy(cmdargs,process.command); + strcat(cmdargs,process.args); + + retval=CreateProcess(NULL,cmdargs,NULL,NULL,FALSE, 0 ,NULL, NULL/*process.directory*/,&si,&piProcess); + + process.hProcess=piProcess.hProcess; + process.hThread=piProcess.hThread; + return(TRUE); +} + +bit8 Wait_Process(Process &process) +{ + DWORD retval; + retval=WaitForSingleObject(process.hProcess,INFINITE); + if (retval==WAIT_OBJECT_0) // process exited + return(TRUE); + else // can this happen? + return(FALSE); +} + + +// +// Get the process to run from the config object +// +bit8 Read_Process_Info(ConfigFile &config,OUT Process &info) +{ + + Wstring procinfo; + if (config.getString("RUN",procinfo)==FALSE) + { + DBGMSG("Couldn't read the RUN line"); + return(FALSE); + } + int offset=0; + Wstring dir; + Wstring executable; + Wstring args; + offset=procinfo.getToken(offset," ",dir); + offset=procinfo.getToken(offset," ",executable); + args=procinfo; + args.remove(0,offset); + + /// + /// + DBGMSG("RUN: EXE = "<. +*/ + +#ifndef PROCESS_HEADER +#define PROCESS_HEADER + +#include +#include "wstypes.h" +#include "wdebug.h" +#include "configfile.h" + +class Process +{ + public: + Process(); + + char directory[256]; + char command[256]; + char args[256]; + HANDLE hProcess; + HANDLE hThread; +}; + +bit8 Read_Process_Info(ConfigFile &config,OUT Process &info); +bit8 Create_Process(Process &process); +bit8 Wait_Process(Process &process); + + +#endif \ No newline at end of file diff --git a/LAUNCHER/REDALERT.ICO b/LAUNCHER/REDALERT.ICO new file mode 100644 index 0000000..ec84b96 Binary files /dev/null and b/LAUNCHER/REDALERT.ICO differ diff --git a/LAUNCHER/RESOURCE.H b/LAUNCHER/RESOURCE.H new file mode 100644 index 0000000..39cb248 --- /dev/null +++ b/LAUNCHER/RESOURCE.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by launcher1.rc +// +#define IDS_SYS_RESTART 1 +#define IDS_SYS_RESTART_TITLE 2 +#define IDS_WEBPATCH 3 +#define IDS_WEBPATCH_TITLE 4 +#define IDS_FILE_X_OF_Y 5 +#define IDS_MUST_RESTART 6 +#define IDS_RUNONCE_ERR 7 +#define IDS_ERROR 8 +#define IDS_ERR_MISSING_FILE 9 +#define IDS_BAD_LIBRARY 10 +#define IDD_DIALOG1 101 +#define IDI_ICON1 102 +#define IDD_CHANGELOG 103 +#define IDI_ICON 108 +#define IDI_ICON2 112 +#define IDI_ICON3 117 +#define IDC_PROGRESS2 1001 +#define IDC_SPLASH 1004 +#define IDC_FILENAME 1009 +#define IDC_CAPTION 1010 +#define IDC_TEXT 1011 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 118 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1013 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/LAUNCHER/STREAMER.CPP b/LAUNCHER/STREAMER.CPP new file mode 100644 index 0000000..1cd68fe --- /dev/null +++ b/LAUNCHER/STREAMER.CPP @@ -0,0 +1,142 @@ +/* +** 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 . +*/ + + +#include "streamer.h" +#ifdef _WIN32 + #include +#endif + + +Streamer::Streamer() : streambuf() +{ + int state=unbuffered(); + unbuffered(0); // 0 = buffered, 1 = unbuffered +} + +Streamer::~Streamer() +{ + sync(); + delete[](base()); +} + +int Streamer::setOutputDevice(OutputDevice *device) +{ + Output_Device=device; + return(0); +} + + +// put n chars from string into buffer +int Streamer::xsputn(const char* buf, int size) //implementation of sputn +{ + + if (size<=0) // Nothing to do + return(0); + + const unsigned char *ptr=(const unsigned char *)buf; + for (int i=0; i= epptr()) && (sync()==EOF)) + return(EOF); + else { + sputc(c); + if ((unbuffered() && c=='\n' || pptr() >= epptr()) + && sync()==EOF) { + return(EOF); + } + return(c); + } +} + +// This is a write only stream, this should never happen +int Streamer::underflow(void) +{ + return(EOF); +} + +int Streamer::doallocate() +{ + + if (base()==NULL) + { + char *buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor + memset(buf,0,2*STREAMER_BUFSIZ); + + // Buffer + setb( + buf, // base pointer + buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer); + 0); // 0 = manual deletion of buff + + // Get area + setg( + buf, // eback + buf, // gptr + buf); // egptr + + buf+=STREAMER_BUFSIZ; + // Put area + setp(buf,buf+STREAMER_BUFSIZ); + return(1); + } + else + return(0); +} + + +int Streamer::sync() +{ + if (pptr()<=pbase()) { + return(0); + } + + int wlen=pptr()-pbase(); + + if (Output_Device) + { + Output_Device->print(pbase(),wlen); + } + + if (unbuffered()) { + setp(pbase(),pbase()); + } + else { + setp(pbase(),pbase()+STREAMER_BUFSIZ); + } + return(0); +} diff --git a/LAUNCHER/STREAMER.H b/LAUNCHER/STREAMER.H new file mode 100644 index 0000000..0b956bf --- /dev/null +++ b/LAUNCHER/STREAMER.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +#ifndef STREAMER_HEADER +#define STREAMER_HEADER + +#include +#include +#include +#include +#include + +#include "odevice.h" + +#ifndef STREAMER_BUFSIZ +// This limits the number of characters that can be sent to a single 'print' +// call. If your debug message is bigger than this, it will get split over +// multiple 'print' calls. That's usually not a problem. + #define STREAMER_BUFSIZ 2048 +#endif + + +// Provide a streambuf interface for a class that can 'print' +class Streamer : public streambuf +{ + public: + Streamer(); + virtual ~Streamer(); + + int setOutputDevice(OutputDevice *output_device); + + protected: + // Virtual methods from streambuf + int xsputn(const char* s, int n); // buffer some characters + int overflow(int = EOF); // flush buffer and make more room + int underflow(void); // Does nothing + int sync(); + + int doallocate(); // allocate a buffer + + + OutputDevice *Output_Device; +}; + +#endif diff --git a/LAUNCHER/UTIL/FILED.H b/LAUNCHER/UTIL/FILED.H new file mode 100644 index 0000000..cdfcce9 --- /dev/null +++ b/LAUNCHER/UTIL/FILED.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef FILED_HEADER +#define FILED_HEADER + +#include "odevice.h" + +class FileD : public OutputDevice +{ + public: + FileD(char *filename) + { + out=fopen(filename,"w"); + if (out==NULL) + out=fopen("FileDev.out","w"); + } + + virtual ~FileD() + { fclose(out); } + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + fprintf(out,"%s",string); + delete[](string); + fflush(out); + return(len); + } + + FILE *out; +}; + +#endif diff --git a/LAUNCHER/UTIL/MBOXD.H b/LAUNCHER/UTIL/MBOXD.H new file mode 100644 index 0000000..1f6e914 --- /dev/null +++ b/LAUNCHER/UTIL/MBOXD.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +#ifndef MBOXD_HEADER +#define MBOXD_HEADER + +#include "odevice.h" + +class MboxD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + MessageBox(NULL,string,"Debug Message", MB_OK | MB_ICONINFORMATION); + delete[](string); + return(len); + } + +}; + +#endif diff --git a/LAUNCHER/UTIL/MONOD.CPP b/LAUNCHER/UTIL/MONOD.CPP new file mode 100644 index 0000000..07e508a --- /dev/null +++ b/LAUNCHER/UTIL/MONOD.CPP @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +#include "monod.h" + +MonoD::MonoD(void) +{ +#ifdef _WIN32 + unsigned long retval; + handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, + &retval,0); + } +#endif +} + +MonoD::~MonoD() +{ + #ifdef _WIN32 + CloseHandle(handle); + handle=NULL; + #endif +} + +int MonoD::print(const char *str, int len) +{ + #ifdef _WIN32 + unsigned long retval; + WriteFile(handle, str, len, &retval, NULL); + ////DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0, + //// &retval,0); + return(len); + #else + for (int i=0; i. +*/ + +#ifndef MONOD_HEADER +#define MONOD_HEADER + + +#include +#include +#include "odevice.h" + +///////////////////////// WIN32 ONLY /////////////////////////////////// + +#ifdef _WIN32 +#include +#include + + +/* +** This is the identifier for the Monochrome Display Driver +*/ +#define FILE_DEVICE_MONO 0x00008000 + +/* +** These are the IOCTL commands supported by the Monochrome Display Driver. +*/ +#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA) + +#endif // ifdef _WIN32 + +class MonoD : public OutputDevice +{ + public: + MonoD(); + ~MonoD(); + + virtual int print(const char *str,int len); + + private: + #ifdef _WIN32 + HANDLE handle; + #endif +}; + +#endif diff --git a/LAUNCHER/UTIL/ODEVICE.H b/LAUNCHER/UTIL/ODEVICE.H new file mode 100644 index 0000000..c3d6abe --- /dev/null +++ b/LAUNCHER/UTIL/ODEVICE.H @@ -0,0 +1,32 @@ +/* +** 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 . +*/ + +#ifndef ODEVICE_HEADER +#define ODEVICE_HEADER + +// This virtual base class provides an interface for output devices +// that can be used for the debugging package. +class OutputDevice +{ + public: + OutputDevice() {} + virtual ~OutputDevice() {}; + virtual int print(const char *s,int len)=0; +}; + +#endif diff --git a/LAUNCHER/UTIL/STDERRD.H b/LAUNCHER/UTIL/STDERRD.H new file mode 100644 index 0000000..43569dd --- /dev/null +++ b/LAUNCHER/UTIL/STDERRD.H @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +#ifndef STDERRD_HEADER +#define STDERRD_HEADER + +#include "odevice.h" + +class StderrD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + fprintf(stderr,"%s",string); + delete[](string); + return(len); + } +}; + +#endif diff --git a/LAUNCHER/UTIL/STDOUTD.H b/LAUNCHER/UTIL/STDOUTD.H new file mode 100644 index 0000000..835b379 --- /dev/null +++ b/LAUNCHER/UTIL/STDOUTD.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +#ifndef STDOUTD_HEADER +#define STDOUTD_HEADER + +#include "odevice.h" + +class StdoutD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memcpy(string,str,len); + string[len]=0; + fprintf(stdout,"%s",string); + fflush(stdout); + delete[](string); + return(len); + } +}; + +#endif diff --git a/LAUNCHER/UTIL/STREAMER.CPP b/LAUNCHER/UTIL/STREAMER.CPP new file mode 100644 index 0000000..62d1b32 --- /dev/null +++ b/LAUNCHER/UTIL/STREAMER.CPP @@ -0,0 +1,139 @@ +/* +** 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 . +*/ + + +#include "streamer.h" +#ifdef _WIN32 + #include +#endif + + +Streamer::Streamer() : streambuf() +{ + int state=unbuffered(); + unbuffered(0); // 0 = buffered, 1 = unbuffered +} + +Streamer::~Streamer() +{ + sync(); + delete[](base()); +} + +int Streamer::setOutputDevice(OutputDevice *device) +{ + Output_Device=device; + return(0); +} + + +// put n chars from string into buffer +int Streamer::xsputn(const char* buf, int size) //implementation of sputn +{ + if (size<=0) // Nothing to do + return(0); + + const unsigned char *ptr=(const unsigned char *)buf; + for (int i=0; i= epptr()) && (sync()==EOF)) + return(EOF); + else { + sputc(c); + if ((unbuffered() && c=='\n' || pptr() >= epptr()) + && sync()==EOF) { + return(EOF); + } + return(c); + } +} + +// This is a write only stream, this should never happen +int Streamer::underflow(void) +{ + return(EOF); +} + +int Streamer::doallocate() +{ + if (base()==NULL) + { + char *buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor + memset(buf,0,2*STREAMER_BUFSIZ); + + // Buffer + setb( + buf, // base pointer + buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer); + 0); // 0 = manual deletion of buff + + // Get area + setg( + buf, // eback + buf, // gptr + buf); // egptr + + buf+=STREAMER_BUFSIZ; + // Put area + setp(buf,buf+STREAMER_BUFSIZ); + return(1); + } + else + return(0); +} + + +int Streamer::sync() +{ + if (pptr()<=pbase()) { + return(0); + } + + int wlen=pptr()-pbase(); + + if (Output_Device) + { + Output_Device->print(pbase(),wlen); + } + + if (unbuffered()) { + setp(pbase(),pbase()); + } + else { + setp(pbase(),pbase()+STREAMER_BUFSIZ); + } + return(0); +} diff --git a/LAUNCHER/UTIL/STREAMER.H b/LAUNCHER/UTIL/STREAMER.H new file mode 100644 index 0000000..0b956bf --- /dev/null +++ b/LAUNCHER/UTIL/STREAMER.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +#ifndef STREAMER_HEADER +#define STREAMER_HEADER + +#include +#include +#include +#include +#include + +#include "odevice.h" + +#ifndef STREAMER_BUFSIZ +// This limits the number of characters that can be sent to a single 'print' +// call. If your debug message is bigger than this, it will get split over +// multiple 'print' calls. That's usually not a problem. + #define STREAMER_BUFSIZ 2048 +#endif + + +// Provide a streambuf interface for a class that can 'print' +class Streamer : public streambuf +{ + public: + Streamer(); + virtual ~Streamer(); + + int setOutputDevice(OutputDevice *output_device); + + protected: + // Virtual methods from streambuf + int xsputn(const char* s, int n); // buffer some characters + int overflow(int = EOF); // flush buffer and make more room + int underflow(void); // Does nothing + int sync(); + + int doallocate(); // allocate a buffer + + + OutputDevice *Output_Device; +}; + +#endif diff --git a/LAUNCHER/UTIL/SYSLOGD.CPP b/LAUNCHER/UTIL/SYSLOGD.CPP new file mode 100644 index 0000000..82415af --- /dev/null +++ b/LAUNCHER/UTIL/SYSLOGD.CPP @@ -0,0 +1,35 @@ +/* +** 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 . +*/ + +#include "syslogd.h" + +SyslogD::SyslogD(char *ident,int logopt,int facility,int _priority) +{ + openlog(ident,logopt,facility); + priority=_priority; +} + +int SyslogD::print(const char *str, int len) +{ + char *temp_str=new char[len+1]; + memset(temp_str,0,len+1); + strncpy(temp_str,str,len); + syslog(priority,temp_str); + delete[](temp_str); + return(len); +} diff --git a/LAUNCHER/UTIL/SYSLOGD.H b/LAUNCHER/UTIL/SYSLOGD.H new file mode 100644 index 0000000..c7e4a3a --- /dev/null +++ b/LAUNCHER/UTIL/SYSLOGD.H @@ -0,0 +1,38 @@ +/* +** 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 . +*/ + +#ifndef SYSLOGD_HEADER +#define SYSLOGD_HEADER + +#include +#include +#include +#include +#include "odevice.h" + +class SyslogD : public OutputDevice +{ + public: + SyslogD(char *ident,int logopt,int facility,int priority); + virtual int print(const char *str,int len); + + private: + int priority; +}; + +#endif diff --git a/LAUNCHER/UTIL/WDEBUG.CPP b/LAUNCHER/UTIL/WDEBUG.CPP new file mode 100644 index 0000000..d727d26 --- /dev/null +++ b/LAUNCHER/UTIL/WDEBUG.CPP @@ -0,0 +1,148 @@ +/* +** 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 . +*/ + +#include +#include "wdebug.h" +#include "streamer.h" +#include "odevice.h" + + +static MsgManager *msg_manager=NULL; + +static int debug_enabled=0; +static ostream *debug_ostream=NULL; +static Streamer debug_streamer; + +static int info_enabled=0; +static ostream *info_ostream=NULL; +static Streamer info_streamer; + +static int warn_enabled=0; +static ostream *warn_ostream=NULL; +static Streamer warn_streamer; + +static int error_enabled=0; +static ostream *error_ostream=NULL; +static Streamer error_streamer; + + +// Don't dare touch this semaphore in application code! +Sem4 DebugLibSemaphore; + + +int MsgManager::setAllStreams(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DebugLibSemaphore.Wait(); + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + + DebugLibSemaphore.Post(); + + return(0); +} + + +int MsgManager::setDebugStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DebugLibSemaphore.Wait(); + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + DebugLibSemaphore.Post(); + return(0); +} + +int MsgManager::setInfoStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DebugLibSemaphore.Wait(); + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + DebugLibSemaphore.Post(); + return(0); +} + +int MsgManager::setWarnStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DebugLibSemaphore.Wait(); + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + DebugLibSemaphore.Post(); + return(0); +} + +int MsgManager::setErrorStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DebugLibSemaphore.Wait(); + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + DebugLibSemaphore.Post(); + return(0); +} + + + +ostream *MsgManager::debugStream(void) +{ + return(debug_ostream); +} + +ostream *MsgManager::infoStream(void) +{ + return(info_ostream); +} + +ostream *MsgManager::warnStream(void) +{ + return(warn_ostream); +} + +ostream *MsgManager::errorStream(void) +{ + return(error_ostream); +} diff --git a/LAUNCHER/UTIL/WDEBUG.H b/LAUNCHER/UTIL/WDEBUG.H new file mode 100644 index 0000000..427304f --- /dev/null +++ b/LAUNCHER/UTIL/WDEBUG.H @@ -0,0 +1,216 @@ +/* +** 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 . +*/ + +/*****************************************************************************\ +wdebug Neal Kettler + +MT-LEVEL + MT-Safe + +The debugging module is pretty good for debugging and it has some message +printing stuff as well. The basic idea is that you write a class that +inherits from OutputDevice (severall are provided) and assign that output +device to a stream. There are seperate streams for debugging, information, +warning, and error messages. Each one can have a seperate output device, +or they can all have the same one. Debugging messages only get compiled +in if your module defines 'DEBUG'. If you don't define debug, then not even +the text of the debugging message gets into the binary. All the other +output streams get printed regardless of whether DEBUG is defined. + +Sample usage: +FileD debug_device("gameres.debug"); // create a file device +MsgManager::setDebugStream(&debug_device); +DBGMSG("This debug message #" << 1 << " you use C++ streams"); + +Note that since these are defines you really don't need to put a semicolon +at the end, and it can be bad in situations like this: + +if (x) + DBGMSG("Stuff is broken"); +else + DBGMSG("Stuff is NOT broken"); + +This won't compile, read the code until you figure it out. Only then +will you be ready to leave grasshopper. + +\*****************************************************************************/ + +#ifndef WDEBUG_HEADER +#define WDEBUG_HEADER + +#include +#include "sem4.h" +#include "odevice.h" +#include "streamer.h" + +// This is needed because the streams return a pointer. Every time you +// change the output device the old stream is deleted, and a new one +// is created. +extern Sem4 DebugLibSemaphore; + +// Print an information message +#define INFMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + cftime(timebuf,"%D %T",&clock); \ + DebugLibSemaphore.Wait(); \ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << "INF " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DebugLibSemaphore.Post(); \ +} + +// Print a warning message +#define WRNMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + cftime(timebuf,"%D %T",&clock); \ + DebugLibSemaphore.Wait(); \ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << "WRN " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DebugLibSemaphore.Post(); \ +} + +// Print an error message +#define ERRMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + cftime(timebuf,"%D %T",&clock); \ + DebugLibSemaphore.Wait(); \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DebugLibSemaphore.Post(); \ +} + + +// Just get a stream to the information device, no extra junk +#define INFSTREAM(X)\ +{\ + DebugLibSemaphore.Wait(); \ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << X;\ + DebugLibSemaphore.Post(); \ +} + +// Just get a stream to the warning device, no extra junk +#define WRNSTREAM(X)\ +{\ + DebugLibSemaphore.Wait(); \ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << X;\ + DebugLibSemaphore.Post(); \ +} + +// Just get a stream to the error device, no extra junk +#define ERRSTREAM(X)\ +{\ + DebugLibSemaphore.Wait(); \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << X;\ + DebugLibSemaphore.Post(); \ +} + +#ifndef DEBUG + +// No debugging, no debug messages. +// Note that anything enclosed in "DBG()" will NOT get executed +// unless DEBUG is defined. +// They are defined to {} for consistency when DEBUG is defined + +#define DBG(X) +#define DBGSTREAM(X) {} +#define PVAR(v) {} +#define DBGMSG(X) {} +#define VERBOSE(X) {} + +#else // DEBUG _is_ defined + +// Execute only if in debugging mode +#define DBG(X) X + +// Print a variable +#define PVAR(v) \ +{ \ + DebugLibSemaphore.Wait(); \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##V << " = " << V << endl; \ + DebugLibSemaphore.Post(); \ +} + + +#define DBGMSG(X)\ +{\ + DebugLibSemaphore.Wait(); \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ + " " << __LINE__ << "] " << X << endl;\ + DebugLibSemaphore.Post(); \ +} + +// Just get a stream to the debugging device, no extra junk +#define DBGSTREAM(X)\ +{\ + DebugLibSemaphore.Wait(); \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << X;\ + DebugLibSemaphore.Post(); \ +} + +// Verbosely execute a statement +#define VERBOSE(X)\ +{ \ + DebugLibSemaphore.Wait(); \ + if (MsgManager::debugStream()) \ + (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##X << endl; X \ + DebugLibSemaphore.Post(); \ +} + +#endif // DEBUG + + +class MsgManager +{ + protected: + MsgManager(); + + public: + static int setAllStreams(OutputDevice *device); + static int setDebugStream(OutputDevice *device); + static int setInfoStream(OutputDevice *device); + static int setWarnStream(OutputDevice *device); + static int setErrorStream(OutputDevice *device); + + static void enableDebug(int flag); + static void enableInfo(int flag); + static void enableWarn(int flag); + static void enableError(int flag); + + static ostream *debugStream(void); + static ostream *infoStream(void); + static ostream *warnStream(void); + static ostream *errorStream(void); +}; + +#endif diff --git a/LAUNCHER/WDEBUG.CPP b/LAUNCHER/WDEBUG.CPP new file mode 100644 index 0000000..cf3d369 --- /dev/null +++ b/LAUNCHER/WDEBUG.CPP @@ -0,0 +1,143 @@ +/* +** 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 . +*/ + +#include +#include "wdebug.h" +#include "streamer.h" +#include "odevice.h" + + +static MsgManager *msg_manager=NULL; + +static int debug_enabled=0; +static ostream *debug_ostream=NULL; +static Streamer debug_streamer; + +static int info_enabled=0; +static ostream *info_ostream=NULL; +static Streamer info_streamer; + +static int warn_enabled=0; +static ostream *warn_ostream=NULL; +static Streamer warn_streamer; + +static int error_enabled=0; +static ostream *error_ostream=NULL; +static Streamer error_streamer; + + + +int MsgManager::setAllStreams(OutputDevice *device) +{ + if (device==NULL) + return(1); + + + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + + return(0); +} + + +int MsgManager::setDebugStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + + return(0); +} + +int MsgManager::setInfoStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + + return(0); +} + +int MsgManager::setWarnStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + + return(0); +} + +int MsgManager::setErrorStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + + return(0); +} + + + +ostream *MsgManager::debugStream(void) +{ + return(debug_ostream); +} + +ostream *MsgManager::infoStream(void) +{ + return(info_ostream); +} + +ostream *MsgManager::warnStream(void) +{ + return(warn_ostream); +} + +ostream *MsgManager::errorStream(void) +{ + return(error_ostream); +} diff --git a/LAUNCHER/WDEBUG.H b/LAUNCHER/WDEBUG.H new file mode 100644 index 0000000..04307fa --- /dev/null +++ b/LAUNCHER/WDEBUG.H @@ -0,0 +1,193 @@ +/* +** 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 . +*/ + +/*****************************************************************************\ +wdebug Neal Kettler + +MT-LEVEL + MT-UnSafe! (UNIX version is safe though) + +The debugging module is pretty good for debugging and it has some message +printing stuff as well. The basic idea is that you write a class that +inherits from OutputDevice (severall are provided) and assign that output +device to a stream. There are seperate streams for debugging, information, +warning, and error messages. Each one can have a seperate output device, +or they can all have the same one. Debugging messages only get compiled +in if your module defines 'DEBUG'. If you don't define debug, then not even +the text of the debugging message gets into the binary. All the other +output streams get printed regardless of whether DEBUG is defined. + +Sample usage: +FileD debug_device("gameres.debug"); // create a file device +MsgManager::setDebugStream(&debug_device); +DBGMSG("This debug message #" << 1 << " you use C++ streams"); + +Note that since these are defines you really don't need to put a semicolon +at the end, and it can be bad in situations like this: + +if (x) + DBGMSG("Stuff is broken"); +else + DBGMSG("Stuff is NOT broken"); + +This won't compile, read the code until you figure it out. Only then +will you be ready to leave grasshopper. + +\*****************************************************************************/ + +#ifndef WDEBUG_HEADER +#define WDEBUG_HEADER + +#include +#include "odevice.h" +#include "streamer.h" +#include + + + +// Print an information message +#define INFMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + cftime(timebuf,"%D %T",&clock); \ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << "INF " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ +} + +// Print a warning message +#define WRNMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + cftime(timebuf,"%D %T",&clock); \ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << "WRN " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ +} + +// Print an error message +#define ERRMSG(X)\ +{\ + char timebuf[40]; \ + time_t clock=time(NULL); \ + strcpy(timebuf,ctime(&clock)); \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ +} + + +// Just get a stream to the information device, no extra junk +#define INFSTREAM(X)\ +{\ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << X;\ +} + +// Just get a stream to the warning device, no extra junk +#define WRNSTREAM(X)\ +{\ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << X;\ +} + +// Just get a stream to the error device, no extra junk +#define ERRSTREAM(X)\ +{\ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << X;\ +} + +#ifndef DEBUG + +// No debugging, no debug messages. +// Note that anything enclosed in "DBG()" will NOT get executed +// unless DEBUG is defined. +// They are defined to {} for consistency when DEBUG is defined + +#define DBG(X) +#define DBGSTREAM(X) {} +#define PVAR(v) {} +#define DBGMSG(X) {} +#define VERBOSE(X) {} + +#else // DEBUG _is_ defined + +// Execute only if in debugging mode +#define DBG(X) X + +// Print a variable +#define PVAR(v) \ +{ \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##V << " = " << V << endl; \ +} + + +#define DBGMSG(X)\ +{\ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ + " " << __LINE__ << "] " << X << endl;\ +} + +// Just get a stream to the debugging device, no extra junk +#define DBGSTREAM(X)\ +{\ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << X;\ +} + +// Verbosely execute a statement +#define VERBOSE(X)\ +{ \ + if (MsgManager::debugStream()) \ + (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##X << endl; X \ +} + +#endif // DEBUG + + +class MsgManager +{ + protected: + MsgManager(); + + public: + static int setAllStreams(OutputDevice *device); + static int setDebugStream(OutputDevice *device); + static int setInfoStream(OutputDevice *device); + static int setWarnStream(OutputDevice *device); + static int setErrorStream(OutputDevice *device); + + static void enableDebug(int flag); + static void enableInfo(int flag); + static void enableWarn(int flag); + static void enableError(int flag); + + static ostream *debugStream(void); + static ostream *infoStream(void); + static ostream *warnStream(void); + static ostream *errorStream(void); +}; + +#endif diff --git a/LAUNCHER/WINBLOWS.CPP b/LAUNCHER/WINBLOWS.CPP new file mode 100644 index 0000000..348d625 --- /dev/null +++ b/LAUNCHER/WINBLOWS.CPP @@ -0,0 +1,186 @@ +/* +** 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 . +*/ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include + +#include "winblows.h" + + +HINSTANCE Global_instance; +LPSTR Global_commandline; +int Global_commandshow; + + +/* + * WinMain - initialization, message loop + */ +int PASCAL WinMain( HINSTANCE instance, HINSTANCE, char *command_line, int command_show) +{ + //////MSG msg; + + Global_instance = instance; + Global_commandline = command_line; + Global_commandshow = command_show; + + int argc; + char *argv[64]; + + char path_to_exe[512]; + GetModuleFileName(instance,(char *)&path_to_exe,512); + argc=1; + argv[0]=path_to_exe; + + + int command_scan=0; + char command_char; + do + { + /* + ** Scan for non-space character on command line + */ + do + { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ) + { + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + do + { + command_char = *( command_line+command_scan++ ); + } while ( command_char!=' ' && command_char != 0 && command_char!=13); + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + + return(main(argc,argv)); + +} /* WinMain */ + + +int Print_WM(UINT message,char *out) +{ + switch(message) + { + case WM_NULL: + sprintf(out,"WM_NULL"); + break; + case WM_CREATE: + sprintf(out,"WM_CREATE"); + break; + case WM_DESTROY: + sprintf(out,"WM_DESTROY"); + break; + case WM_CANCELMODE: + sprintf(out,"WM_CANCELMODE"); + break; + case WM_ERASEBKGND: + sprintf(out,"WM_ERASEBKGND"); + break; + case WM_GETTEXT: + sprintf(out,"WM_GETTEXT"); + break; + case WM_QUERYOPEN: + sprintf(out,"WM_QUERYOPEN"); + break; + case WM_MOVE: + sprintf(out,"WM_MOVE"); + break; + case WM_SIZE: + sprintf(out,"WM_SIZE"); + break; + case WM_ACTIVATE: + sprintf(out,"WM_ACTIVATE"); + break; + case WM_SETFOCUS: + sprintf(out,"WM_SETFOCUS"); + break; + case WM_KILLFOCUS: + sprintf(out,"WM_KILLFOCUS"); + break; + case WM_ENABLE: + sprintf(out,"WM_ENABLE"); + break; + case WM_SETREDRAW: + sprintf(out,"WM_REDRAW"); + break; + case WM_PAINT: + sprintf(out,"WM_PAINT"); + break; + case WM_CLOSE: + sprintf(out,"WM_CLOSE"); + break; + case WM_QUIT: + sprintf(out,"WM_QUIT"); + break; + case WM_ACTIVATEAPP: + sprintf(out,"WM_ACTIVATEAPP"); + break; + case WM_SETCURSOR: + sprintf(out,"WM_SETCURSOR"); + break; + case WM_KEYDOWN: + sprintf(out,"WM_KEYDOWN"); + break; + case WM_MOUSEMOVE: + sprintf(out,"WM_MOUSEMOVE"); + break; + case WM_WINDOWPOSCHANGING: + sprintf(out,"WM_WINDOWPOSCHANGING"); + break; + case WM_WINDOWPOSCHANGED: + sprintf(out,"WM_WINDOWPOSCHANGED"); + break; + case WM_DISPLAYCHANGE: + sprintf(out,"WM_DISPLAYCHANGE"); + break; + case WM_NCPAINT: + sprintf(out,"WM_NCPAINT"); + break; + case WM_PALETTEISCHANGING: + sprintf(out,"WM_PALETTEISCHANGING"); + break; + case WM_PALETTECHANGED: + sprintf(out,"WM_PALETTECHANGED"); + break; + case WM_NCACTIVATE: + sprintf(out,"WM_NCACTIVATE"); + break; + case WM_NCCALCSIZE: + sprintf(out,"WM_NCCALCSIZE"); + break; + case WM_SYSCOMMAND: + sprintf(out,"WM_SYSCOMMAND"); + break; + default: + sprintf(out,"? UNKNOWN ?"); + return(-1); + } + return(0); +} diff --git a/LAUNCHER/WINBLOWS.H b/LAUNCHER/WINBLOWS.H new file mode 100644 index 0000000..61bbc21 --- /dev/null +++ b/LAUNCHER/WINBLOWS.H @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + +#ifndef WINBLOWS_HEADER +#define WINBLOWS_HEADER + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include"wstypes.h" + + extern HINSTANCE Global_instance; + extern LPSTR Global_commandline; + extern int Global_commandshow; + + extern int main(int argc, char *argv[]); + + int Print_WM(UINT wm,char *out); + +#endif diff --git a/LAUNCHER/WSTRING.CPP b/LAUNCHER/WSTRING.CPP new file mode 100644 index 0000000..483b88e --- /dev/null +++ b/LAUNCHER/WSTRING.CPP @@ -0,0 +1,611 @@ +/* +** 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 . +*/ + +/****************************************************************************\ +* 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: Carpenter (The RedAlert ladder creator) +File Name : string.cpp +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 + +A fairly typical string class. This string class always copies any input +string to it's own memory (for assignment or construction). +\***************************************************************************/ + +#include +#include +#include +#include + +#include "wstring.h" + +Wstring::Wstring():str(NULL) +{ } + +Wstring::Wstring(IN char *string):str(NULL) +{ set(string); } + +Wstring::Wstring(IN const Wstring &other):str(NULL) +{ + if (other.str!=NULL) + { + str=new char[strlen(other.str)+1]; + strcpy(str,other.str); + } +} + +Wstring::~Wstring() +{ clear(); } + +bit8 Wstring::operator==(IN char *other) +{ + if ((str==NULL)&&(other==NULL)) + return(TRUE); + if(strcmp(str, other) != 0) + return(FALSE); + else + return(TRUE); +} + +bit8 Wstring::operator==(IN Wstring &other) +{ + if((str == NULL) && (other.str == NULL)) + return(TRUE); + + if((str == NULL) || (other.str == NULL)) + return(FALSE); + + if(strcmp(str, other.str) != 0) + return(FALSE); + else + return(TRUE); +} + + +bit8 Wstring::operator!=(IN char *other) +{ + if(strcmp(str, other) != 0) + return(TRUE); + else + return(FALSE); +} + + +bit8 Wstring::operator!=(IN Wstring &other) +{ + if((str == NULL) && (other.str == NULL)) + return(FALSE); + + if((str == NULL) || (other.str == NULL)) + return(TRUE); + + if(strcmp(str, other.str) != 0) + return(TRUE); + else + return(FALSE); +} + + +Wstring &Wstring::operator=(char *other) +{ + set(other); + return(*this); +} + + +Wstring &Wstring::operator=(IN Wstring &other) +{ + if(*this == other) + return(*this); + + set(other.get()); + return(*this); +} + + +bit8 Wstring::cat(IN char *s) +{ + char *oldStr; + uint32 len; + + if (s==NULL) // it's OK to cat nothing + return(TRUE); + + // Save the contents of the string. + oldStr = str; + + // Determine the length of the resultant string. + len = strlen(s) + 1; + if(oldStr) + len += strlen(oldStr); + + // Allocate memory for the new string. + + if(!(str = new char[(len * sizeof(char))])) + { + str = oldStr; + return(FALSE); + } + + // Copy the contents of the old string and concatenate the passed + // string. + if(oldStr) strcpy(str, oldStr); + else str[0] = 0; + + strcat(str, s); + + // delete the old string. + if(oldStr) + delete[](oldStr); + + return(TRUE); +} + + +bit8 Wstring::cat(uint32 size, IN char *s) +{ + char *oldStr; + uint32 len; + + // Save the contents of the string. + oldStr = str; + + // Determine the length of the resultant string. + len = size + 1; + if(oldStr) + len += strlen(oldStr); + + // Allocate memory for the new string. + if(!(str = new char[(len * sizeof(char))])) + { + str = oldStr; + return(FALSE); + } + + // Copy the contents of the old string and concatenate the passed + // string. + if(oldStr) + strcpy(str, oldStr); + else + str[0] = 0; + + strncat(str, s, size); + + // delete the old string. + if(oldStr) + delete[](oldStr); + + return(TRUE); +} + +bit8 Wstring::cat(IN Wstring &other) +{ + return cat(other.get()); +} + +Wstring &Wstring::operator+=(IN char *string) +{ + cat(string); + return(*this); +} + +Wstring &Wstring::operator+=(IN Wstring &other) +{ + cat(other.get()); + return(*this); +} + +Wstring Wstring::operator+(IN char *string) +{ + Wstring temp = *this; + temp.cat(string); + return(temp); +} + +Wstring Wstring::operator+(IN Wstring &s) +{ + Wstring temp = *this; + temp.cat(s); + return(temp); +} + +// +// This function deletes 'count' characters indexed by `pos' from the Wstring. +// If `pos'+'count' is > the length of the array, the last 'count' characters +// of the string are removed. If an error occurs, FALSE is returned. +// Otherwise, TRUE is returned. Note: count has a default value of 1. +// +// +char Wstring::remove(sint32 pos,sint32 count) +{ + char *s; + sint32 len; + + if (!str) + return(FALSE); + + len = (sint32)strlen(str); + + if(pos+count > len) + pos = len - count; + if (pos < 0) + { + count+=pos; // If they remove before 0, ignore up till beginning + pos=0; + } + if (count<=0) + return(FALSE); + + if(!(s = new char[len-count+1])) + { + //ErrorMessage(SET_EM, "Insufficient memory to modify Wstring."); + return(FALSE); + } + + ///////DBGMSG("Wstring::remove POS: "<strlen(str)) + pos=strlen(str); + char *newstr=new char[strlen(str)+strlen(instring)+1]; + memset(newstr,0,strlen(str)+strlen(instring)+1); + strcpy(newstr,str); + // move the old data out of the way + int bytes=strlen(str)+1-pos; + memmove(newstr+pos+strlen(instring),newstr+pos,bytes); + // move the new data into place + memmove(newstr+pos,instring,strlen(instring)); + delete[](str); + str=newstr; + return(TRUE); +} + +// This function inserts the character specified by `k' into the string at the +// position indexed by `pos'. If `pos' is >= the length of the string, it is +// appended to the string. If an error occurs, FALSE is returned. Otherwise, +// TRUE is returned. +bit8 Wstring::insert(char k, uint32 pos) +{ + char *s; + uint32 len; + char c[2]; + + if(!str) + { + c[0] = k; + c[1] = 0; + return(set(c)); + } + + len = (uint32)strlen(str); + + if(pos > len) + pos = len; + + if(!(s = (char *)new char[(len + 2)])) + { + //ErrorMessage(SET_EM, "Insufficient memory to modify Wstring."); + return(FALSE); + } + + c[0] = str[pos]; + str[pos] = 0; + c[1] = 0; + strcpy(s, str); + str[pos] = c[0]; + c[0] = k; + strcat(s, c); + strcat(s, &(str[pos])); + delete[](str); + str = s; + + return(TRUE); +} + + +// This function replaces any occurences of the string pointed to by +// `replaceThis' with the string pointed to by `withThis'. If an error +// occurs, FALSE is returned. Otherwise, TRUE is returned. +bit8 Wstring::replace(char *replaceThis, char *withThis) +{ + Wstring dest; + char *foundStr, *src; + uint32 len; + + src=get(); + while(src && src[0]) + { + foundStr = strstr(src, replaceThis); + if(foundStr) + { + len = (uint32)foundStr - (uint32)src; + if(len) + { + if(!dest.cat(len, src)) + return(FALSE); + } + if(!dest.cat(withThis)) + return(FALSE); + src = foundStr + strlen(replaceThis); + } + else + { + if(!dest.cat(src)) + return(FALSE); + + src=NULL; + } + } + return(set(dest.get())); +} + + +bit8 Wstring::set(IN char *s) +{ + uint32 len; + + clear(); + + len = (uint32)strlen(s) + 1; + + if(!(str = new char[len])) + { + //ErrorMessage(SET_EM, "Insufficient memory to set Wstring."); + return(FALSE); + } + strcpy(str, s); + + return(TRUE); +} + + +bit8 Wstring::set(char c, uint32 index) +{ + if(index >= (uint32)strlen(str)) + return FALSE; + + str[index] = c; + + return TRUE; +} + + +char Wstring::set(uint32 size, IN char *string) +{ + uint32 len; + + clear(); + len = size + 1; + + if(!(str = new char[len])) + { + //ErrorMessage(SET_EM, "Insufficient memory to set Wstring."); + return(FALSE); + } + + // Copy the bytes in the string, and NULL-terminate it. + strncpy(str, string, size); + str[size] = 0; + + return(TRUE); +} + + +// This function converts all alphabetical characters in the string to lower +// case. +void Wstring::toLower(void) +{ + uint32 i; + + for(i = 0; i < length(); i++) + { + if((str[i] >= 'A') && (str[i] <= 'Z')) + str[i] = tolower(str[i]); + } +} + + +// This function converts all alphabetical characters in the string to upper +// case. +void Wstring::toUpper(void) +{ + uint32 i; + + for(i = 0; i < length(); i++) + { + if((str[i] >= 'a') && (str[i] <= 'z')) + str[i] = toupper(str[i]); + } +} + + +// This function truncates the string so its length will match the specified +// `len'. If an error occurs, FALSE is returned. Otherwise, TRUE is returned. +bit8 Wstring::truncate(uint32 len) +{ + Wstring tmp; + if(!tmp.set(len, get()) || !set(tmp.get())) + return(FALSE); + return(TRUE); +} + +// Truncate the string after the character 'c' (gets rid of 'c' as well) +// Do nothing if 'c' isn't in the string +bit8 Wstring::truncate(char c) +{ + sint32 len; + + if (str==NULL) + return(FALSE); + + char *cptr=strchr(str,c); + if (cptr==NULL) + return(FALSE); + len=(sint32)(cptr-str); + truncate((uint32)len); + return(TRUE); +} + +// Get a token from this string that's seperated by one or more +// chars from the 'delim' string , start at offset & return offset +sint32 Wstring::getToken(int offset,char *delim,Wstring &out) +{ + int i; + sint32 start; + sint32 stop; + for (i=offset; i=length()) + return(-1); + start=i; + + for (; i= length()) + return(-1); + + for (; i. +*/ + +/****************************************************************************\ +* 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: Carpenter (The RedAlert ladder creator) +File Name : main.cpp +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 +\****************************************************************************/ + +#ifndef WSTRING_HEADER +#define WSTRING_HEADER + +#include +#include +#include "wstypes.h" + +class Wstring +{ + public: + Wstring(); + Wstring(IN const Wstring &other); + Wstring(IN char *string); + ~Wstring(); + + void clear(void); + + bit8 cat(IN char *string); + bit8 cat(uint32 size,IN char *string); + bit8 cat(IN Wstring &string); + + void cellCopy(OUT char *dest, uint32 len); + char remove(sint32 pos, sint32 count); + bit8 removeChar(char c); + void removeSpaces(void); + char *get(void); + char get(uint32 index); + uint32 length(void); + bit8 insert(char c, uint32 pos); + bit8 insert(char *instring, uint32 pos); + bit8 replace(IN char *replaceThis,IN char *withThis); + char set(IN char *str); + char set(uint32 size,IN char *str); + bit8 set(char c, uint32 index); + void setSize(sint32 bytes); // create an empty string + void toLower(void); + void toUpper(void); + bit8 truncate(uint32 len); + bit8 truncate(char c); // trunc after char c + sint32 getToken(int offset,char *delim,Wstring &out); + sint32 getLine(int offset, Wstring &out); + + bit8 operator==(IN char *other); + bit8 operator==(IN Wstring &other); + bit8 operator!=(IN char *other); + bit8 operator!=(IN Wstring &other); + + Wstring &operator=(IN char *other); + Wstring &operator=(IN Wstring &other); + Wstring &operator+=(IN char *other); + Wstring &operator+=(IN Wstring &other); + Wstring operator+(IN char *other); + Wstring operator+(IN Wstring &other); + + private: + char *str; // Pointer to allocated string. +}; + +#endif diff --git a/LAUNCHER/WSTYPES.H b/LAUNCHER/WSTYPES.H new file mode 100644 index 0000000..cfa5b75 --- /dev/null +++ b/LAUNCHER/WSTYPES.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +/****************************************************************************\ +* 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: +File Name : wtypes.h +Author : Neal Kettler +Start Date : June 3, 1997 +Last Update : June 17, 1997 + +Standard type definitions for the sake of portability and readability. +\***************************************************************************/ + +#ifndef WTYPES_HEADER +#define WTYPES_HEADER + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<(y))?(x):(y)) +#endif + +#ifndef MAX +#define MAX(x,y) (((x)>(y))?(x):(y)) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +//These are used for readability purposes mostly, when a method takes a +// pointer or reference these help specify what will happen to the data +// that is sent in. +#define IN +#define OUT +#define INOUT + +typedef char bit8; +typedef char sint8; +typedef unsigned char uint8; +typedef signed short int sint16; +typedef unsigned short int uint16; +typedef signed int sint32; +typedef unsigned int uint32; + +#define MAX_BIT8 0x1 +#define MAX_UINT32 0xFFFFFFFF +#define MAX_UINT16 0xFFFF +#define MAX_UINT8 0xFF +#define MAX_SINT32 0x7FFFFFFF +#define MAX_SINT16 0x7FFF +#define MAX_SINT8 0x7F + +#ifdef _WIN32 +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#endif diff --git a/TOOLS/ANIMATE.EXE b/TOOLS/ANIMATE.EXE new file mode 100644 index 0000000..289351f Binary files /dev/null and b/TOOLS/ANIMATE.EXE differ diff --git a/TOOLS/ANIMATE.EXE.OLD b/TOOLS/ANIMATE.EXE.OLD new file mode 100644 index 0000000..9fac540 Binary files /dev/null and b/TOOLS/ANIMATE.EXE.OLD differ diff --git a/TOOLS/AUDIOMAK.EXE b/TOOLS/AUDIOMAK.EXE new file mode 100644 index 0000000..066be17 Binary files /dev/null and b/TOOLS/AUDIOMAK.EXE differ diff --git a/TOOLS/AUDIOMAK/AUDIOMAK.CPP b/TOOLS/AUDIOMAK/AUDIOMAK.CPP new file mode 100644 index 0000000..412b5b2 --- /dev/null +++ b/TOOLS/AUDIOMAK/AUDIOMAK.CPP @@ -0,0 +1,1285 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + ** 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 * + * * + * File Name : AUDIOMAK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include + +#define FALSE 0 +#define TRUE 1 + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) + +char * SourceFile = 0; +int SourceWild = FALSE; +char * DestFile = 0; +int DestDir = FALSE; +int DoSpecial = TRUE; // Compression starts "on". +long MagicNumber = 0xDEAF; +int DoVerbose = FALSE; // Verbose report starts "off". + +char Path[MAXPATH]; +char Drive[MAXDRIVE]; +char Dir[MAXDIR]; +char File[MAXFILE]; +char Ext[MAXEXT]; + +// The size of these buffers must be evenly divisble by 2048. +char far StageBuffer[1024*16]; +char far padding[1024]; +char far AltBuffer[1024*16]; +char far padding2[1024]; + +// General header to the RIFF file itself. +typedef struct { + char szRIFF[4]; // "RIFF" + long dwFormatLength; + char szWAVE[4]; // "WAVE" +} RIFFHeaderType; + +// Header to each sub-block of a RIFF file. +typedef struct { + char szDATA[4]; // ASCII identifier for block. + long dwDataLength; // Size of block (not counting this header). +} RIFFBlockType; + +// First part of the "fmt" data block. +typedef struct { + short wFormatTag; // 1=raw, 33=sonarc + short wChannels; // 1=mono, 2=stereo + long dwSamplesPerSec; // Playback "rate". + long dwAvgBytesPerSec; // =(samples/sec)*(bytes/sample)*wChannels + short wBlockAlign; + short wBitsPerSample; // 8 or 16 + + // Even more bytes can go here... examine dwDataLength to verify. + // Optional for Sonarc +// LONG cbSize; // Size of the extra portion of fmt (always 2) +// WORD wCompType; // Compression algorithm index +} RIFFfmtType; + +typedef struct { + char Description[0x14]; + short Offset; + short Version; + short IDCode; +} VocHeaderType; + +// Compression types. +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD, // Special sliding window delta compression. + SCOMP_SONARC=33 // Sonarc frame compression. +} SCompressType; + +// Header to output file. +typedef struct { + unsigned short Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + unsigned Stereo:1; // Is the sample stereo? + unsigned Bits:1; // Is the sample 16 bits? + unsigned pad:6; // Padding bits. + unsigned char Compression; // What kind of compression for this sample? +} RawHeaderType; + +typedef struct { + short frameSize; // Size of the frame in bytes (including header). + short nSamples; // # of samples in the frame. + short chkSum; // A checksum -- Words XORed with 0xACED. + char tableIndex; // Bit 7 set if frame was RLE'd + char order; // The order of the predictor. +} SonarcFrameType; + + +int main(int argc, char **argv); +void Process(char const *src, char const *name, char const *dest); +void Convert(char const *src, char const *dst); +int Is_WAV(FILE *src); +void Convert_WAV(FILE * src, FILE * dst); +int Is_VOC(FILE *src); +void Convert_VOC(FILE *src, FILE *dst); +long Compress_Frame(void * source, void * dest, long size); + +extern "C" { +short Decompress_Frame(void * source, void * dest, short size); +} + + +int main(int argc, char **argv) +{ + + /* + ** Process the command line parameters. + */ + try { + if (argc < 2) throw 1; + + for (int index = 1; index < argc; index++) { + char *string = argv[index]; + + switch (string[0]) { + case '-': + case '/': + switch (toupper(string[1])) { + case 'X': + if (string[2] == '-') { + DoSpecial = FALSE; + } + break; + + case 'V': + DoVerbose = TRUE; + break; + + default: + printf("\nERROR: Invalid command line argument.\n"); + return (EXIT_FAILURE); + } + break; + + default: + if (!SourceFile) { + SourceFile = string; + } else { + if (!DestFile) { + DestFile = string; + } else { + printf("\nERROR: Invalid command line argument.\n"); + return (EXIT_FAILURE); + } + } + } + } + + /* + ** Verify that the source filespec can locate at least one file. + */ + { + struct ffblk block; + + int result = fnsplit(SourceFile, Drive, Dir, File, Ext); + if (result & WILDCARDS) SourceWild = TRUE; + result = findfirst(SourceFile, &block, FA_ARCH|FA_RDONLY); + if (result) { + printf("\nERROR: Cannot find source file '%s'.\n", SourceFile); + exit(EXIT_FAILURE); + } + } + + /* + ** No output file is necessary if only a single file is specified for + ** the source. In such a case, use the source file as the output file + ** name but change the extension to .AUD. + */ + if (!DestFile) { + if (!SourceWild) { + fnsplit(SourceFile, Drive, Dir, File, Ext); + fnmerge(Path, Drive, Dir, File, ".AUD"); + DestFile = strdup(Path); + } else { + DestFile = strdup("."); + //printf("\nERROR: No destination directory specified.\n"); + //exit(EXIT_FAILURE); + } + } + + /* + ** The source and destination files cannot be the same. + */ + if (stricmp(SourceFile, DestFile) == 0) { + printf("\nERROR: Source and destination files must not be the same.\n"); + exit(EXIT_FAILURE); + } + + /* + ** Make sure that the destination filespec does not contain any + ** wildcard specifications. + */ + if (fnsplit(DestFile, Drive, Dir, File, Ext) & WILDCARDS) { + printf("\nERROR: Wildcard characters not allowed in destination file '%s'.\n", DestFile); + exit(EXIT_FAILURE); + } + + /* + ** Determine if the destination is a directory or not. If it is + ** a directory, then it must be mutated into proper directory format. + */ + { + struct ffblk block; + + strcpy(Path, DestFile); + if (Path[strlen(Path)-1] == '\\') { + Path[strlen(Path)-1] = '\0'; + } + + int result = findfirst(Path, &block, FA_DIREC); + if (!result && (block.ff_attrib & FA_DIREC)) { + DestDir = TRUE; + /* + ** Make sure that the output directory ends with a backslash or a colon. + */ + strcpy(Path, DestFile); + switch (Path[strlen(Path)-1]) { + default: + strcat(Path, "\\"); + DestFile = strdup(Path); + break; + + case '\\': + case ':': + break; + } + } + } + + /* + ** Verify that the destination is a directory if wildcards were specified + ** in the source filespec. + */ + if (SourceWild && !DestDir) { + printf("\nERROR: Destination '%s' was not a directory.\n", DestFile); + exit(EXIT_FAILURE); + } + + /* + ** Scan through all specified source files and process them into the + ** appropriate audio format. + */ + struct ffblk block; + + findfirst(SourceFile, &block, FA_ARCH|FA_RDONLY); + do { + Process(SourceFile, block.ff_name, DestFile); + } while (!findnext(&block)); + + } + catch(int) { + printf("\n" + "AUDIOMAK V2.3 (c) 1994, by Westwood Studios\n" + "USAGE: AUDIOMAK [option] \n" + "\n" + " Options:\n" + " -X- : Turn compression off.\n" + " -V : Verbose compression report.\n" + "\n" + " : File or filespec of VOC or WAV files to process\n" + " Can be SONARC compressed.\n" + " : Destination file or destination directory. If\n" + " wildcards were used in the source file then this\n" + " can only be a destination directory.\n" + "\n"); + return(EXIT_FAILURE); + } + + return(EXIT_SUCCESS); +} + +long rawframes; // Number of non-compressed frames. +long rate; // Sample rate. +long raw; // Number of raw bytes output. +long bit5; // Number of 5bit codes output. +long bit4; // Number of 4bit codes output (@2 samples). +long bit2; // Number of 2bit codes output (@4 samples). +long bit0; // Number of 0 delta runs. +long overhead; // Overhead (wrapper) bytes. +long original; // Original data size. +long compressed; // Compressed data output. + +inline void Log_RawFrame(long val) {rawframes += val;} +inline void Log_Raw(long val) {raw += val;} +inline void Log_5Bit(long val) {bit5 += val;} +inline void Log_4Bit(long val) {bit4 += val;} +inline void Log_2Bit(long val) {bit2 += val;} +inline void Log_0Bit(long val) {bit0 += val;} +inline void Log_Overhead(long val) {overhead += val;} +inline void Log_Compressed(long val) {compressed += val;} +inline void Log_Original(long val) {original += val;} +inline void Log_Rate(long val) {rate = val;} + +void Stat_Reset(void) +{ + if (DoVerbose) { + rawframes = 0; + rate = 0; + raw = 0; + bit5 = 0; + bit4 = 0; + bit2 = 0; + bit0 = 0; + overhead = 0; + original = 0; + compressed = 0; + } +} + +void Stat_Dump(void) +{ + if (DoVerbose) { + printf("Compression efficiency = %ld%% (%ld bytes)\n", ((original - compressed) * 100) / original, original - compressed); + printf(" Sample Rate = %ld\n", rate); + printf(" Uncompressed data..:%7ld %ld%%\n", raw, (raw * 100) / original); + printf(" Uncompressed frames:%7ld\n", rawframes); + printf(" 5 bit codes........:%7ld %ld%%\n", bit5, (bit5 * 100) / original); + printf(" 4 bit codes........:%7ld %ld%%\n", bit4, (bit4 * 100) / original); + printf(" 2 bit codes........:%7ld %ld%%\n", bit2, (bit2 * 100) / original); + printf(" 0 bit codes........:%7ld %ld%%\n", bit0, (bit0 * 100) / original); + printf(" overhead bytes.....:%7ld %ld%%\n", overhead, (overhead * 100) / original); + } else { + printf("%ld%%\n", ((original - compressed) * 100) / original); + } +} + + +void Process(char const *src, char const *name, char const *dest) +{ + char sourcename[MAXPATH]; + + /* + ** Open the input file. + */ + fnsplit(src, Drive, Dir, File, Ext); + fnmerge(sourcename, Drive, Dir, NULL, NULL); + strcat(sourcename, name); + + /* + ** Open the output filename based on the source filename and the destination + ** directory or filename. + */ + fnsplit(sourcename, NULL, NULL, File, Ext); + if (DestDir) { + fnsplit(dest, Drive, Dir, NULL, NULL); + fnmerge(Path, Drive, Dir, File, ".AUD"); + } else { + fnsplit(dest, Drive, Dir, NULL, Ext); + fnmerge(Path, Drive, Dir, File, Ext); + } + + strupr(sourcename); + strupr(Path); + printf("%s ", sourcename, Path); + Convert(sourcename, Path); +} + + +void Convert(char const *src, char const *dst) +{ + FILE *srcfile = 0; + FILE *dstfile = 0; + + srcfile = fopen(src, "rb"); + if (!srcfile) { + perror(src); + exit(EXIT_FAILURE); + } + + if (Is_WAV(srcfile)) { + dstfile = fopen(dst, "wb"); + if (!dstfile) { + perror(dst); + exit(EXIT_FAILURE); + } + Stat_Reset(); + Convert_WAV(srcfile, dstfile); + Stat_Dump(); + } else { + if (Is_VOC(srcfile)) { + dstfile = fopen(dst, "wb"); + if (!dstfile) { + perror(dst); + exit(EXIT_FAILURE); + } + Stat_Reset(); + Convert_VOC(srcfile, dstfile); + Stat_Dump(); + } else { + printf("ERROR: Unrecognized source file format for '%s'.\n", src); + exit(EXIT_FAILURE); + } + } + if (dstfile) fclose(dstfile); + fclose(srcfile); +} + + +int Is_WAV(FILE *src) +{ + long size; + RIFFHeaderType header; + + fseek(src, 0, SEEK_SET); + + /* + ** Determine if this is a WAV file. Examining the file extension is + ** not foolproof. The actual data must be looked at. + */ + size = fread(&header, 1, sizeof(header), src); + if (size == sizeof(header)) { + if (memcmp(header.szRIFF, "RIFF", 4) == 0) { + if (memcmp(header.szWAVE, "WAVE", 4) == 0) { + return(TRUE); + } + } + } + return(FALSE); +} + +void Convert_WAV(FILE * src, FILE * dst) +{ + RIFFBlockType block; + RIFFfmtType fmt; + RawHeaderType raw; + int nodo = FALSE; + long fcount = 0; + + raw.pad = 0; + raw.Size = 0; + fwrite(&raw, 1, sizeof(raw), dst); + Log_Overhead(sizeof(raw)); + + /* + ** At this point, we already know that it is a RIFF (wav) file, so + ** skip the identifier bytes at the beginning. + */ + fseek(src, sizeof(RIFFHeaderType), SEEK_SET); + + /* + ** Process all the blocks in the RIFF file. + */ + for (;;) { + long s; + + s = fread(&block, sizeof(block), 1, src); + if (!s) break; + + /* + ** If this is the format control block, the process it. + */ + if (memcmp(block.szDATA, "fmt ", sizeof("fmt ")-1) == 0) { + s = fread(&fmt, 1, sizeof(fmt), src); + if (s != sizeof(fmt)) break; + + raw.Rate = (short)fmt.dwSamplesPerSec; + Log_Rate(raw.Rate); + raw.Stereo = (fmt.wChannels == 2); + raw.Bits = (fmt.wBitsPerSample == 16); + switch (fmt.wFormatTag) { + default: + case 1: + raw.Compression = SCOMP_NONE; + if (DoSpecial) { + raw.Compression = SCOMP_WESTWOOD; + } + break; + + case 33: + raw.Compression = SCOMP_SONARC; + if (DoSpecial) { + printf("ERROR: Cannot compress an already compressed WAV file.\n"); + nodo = TRUE; + } + break; + } + + /* + ** Skip any extra format bytes. For a Sonarc file, there are + ** extra bytes to skip. + */ + fseek(src, block.dwDataLength - sizeof(fmt), SEEK_CUR); + + } else if (memcmp(block.szDATA, "data", sizeof("data")-1) == 0) { + + /* + ** Add in the size of this data block to the running total of data + ** count. For SONARC compressed blocks, there is an extra four bytes + ** at the start of the data that records the uncompressed size -- who + ** cares, so skip it. + */ + if (raw.Compression == SCOMP_SONARC) { + fseek(src, 4, SEEK_CUR); + block.dwDataLength -= sizeof(raw.Size); + } + + /* + ** Copy the bulk of the data to the output file. + */ + while (block.dwDataLength) { + long desired; + long actual; + + desired = sizeof(StageBuffer); + if (block.dwDataLength < desired) { + desired = block.dwDataLength; + } + actual = fread(StageBuffer, 1, (unsigned)desired, src); + Log_Original(actual); + block.dwDataLength -= actual; + + /* + ** Special process to convert audio data into deltas. + */ + if (DoSpecial && !nodo) { + void *sptr = &StageBuffer[0]; + long a = actual; + + /* + ** When compressing the data, it must be processed in small + ** chunks. Loop through the source and process the chunks + ** until there is no more source data. Careful choice of the + ** size of the buffers ensures that only full chunks are + ** output. + */ + while (a) { + long tocomp; + long comped; + + tocomp = MIN(a, 2048); + comped = Compress_Frame(sptr, AltBuffer, tocomp); + a -= tocomp; + Log_Compressed(comped); + + #if(FALSE) + if (comped < tocomp) { + Decompress_Frame(AltBuffer, sptr, (short)tocomp); + } + raw.Size += fwrite(sptr, 1, (size_t)tocomp, dst); + (char*)sptr += (unsigned)tocomp; + #else + + (char*)sptr += (unsigned)tocomp; + raw.Size += fwrite(&comped, 1, sizeof(short), dst); + raw.Size += fwrite(&tocomp, 1, sizeof(short), dst); + raw.Size += fwrite(&MagicNumber, 1, sizeof(MagicNumber), dst); + raw.Size += fwrite(&AltBuffer[0], 1, (size_t)comped, dst); + #endif + fcount++; + } + + } else { + raw.Size += actual; + fwrite(StageBuffer, 1, (unsigned)actual, dst); + Log_Compressed(actual); + fcount++; + } + if (!actual) break; + } + + } else { + + /* + ** Unrecognized blocks are skipped. + */ + fseek(src, block.dwDataLength, SEEK_CUR); + } + } + + /* + ** Output the finalized raw header structure. + */ + fseek(dst, 0, SEEK_SET); + fwrite(&raw, 1, sizeof(raw), dst); +} + + +int Is_VOC(FILE *src) +{ + long size; + VocHeaderType voc; + + fseek(src, 0, SEEK_SET); + size = fread(&voc, 1, sizeof(voc), src); + if (size != sizeof(voc)) { + return(FALSE); + } + + if (memicmp(voc.Description, "Creative Voice File", strlen("Creative Voice File")) != 0) { + return(FALSE); + } + + return (TRUE); +} + + +void Convert_VOC(FILE *src, FILE *dst) +{ +// long size = 0; + VocHeaderType voc; + RawHeaderType raw; + + raw.pad = 0; + raw.Size = 0; + raw.Compression = SCOMP_NONE; + if (DoSpecial) { + raw.Compression = SCOMP_WESTWOOD; + } + + fseek(src, 0, SEEK_SET); + fread(&voc, 1, sizeof(voc), src); + fseek(src, voc.Offset, SEEK_SET); + + /* + ** Output a dummy raw header (will be updated later). + */ + raw.Size = 0; + raw.Rate = 11025; + raw.Stereo = FALSE; + raw.Bits = FALSE; + fwrite(&raw, sizeof(raw), 1, dst); + + /* + ** Loop through all the VOC chunks in order to build the correct + ** rawheader structure. + */ + char code = -1; + while (code != 0) { + char c; + long blocksize=0; + unsigned rate=0; + unsigned char pack=0; + ldiv_t result; + + fread(&code, 1, sizeof(code), src); + switch (code) { + + /* + ** General data block. Packed data is not supported. + ** If an extended control block has been processed, then + ** ignore the control bytes in this file. + */ + case 1: + fread(&blocksize, 3, 1, src); + + /* + ** Determine the sample rate from the special code provided + ** for this block. The formula is very inaccurate, so check + ** for some special codes and fixe the playback rate accordingly. + ** When dealing with odd sample rates, then just figure out + ** the rate based on the formula. + */ + fread(&rate, 1, 1, src); + switch (256-rate) { + case 22: + case 23: + raw.Rate = 44100U; + break; + + case 45: + raw.Rate = 22050; + break; + + case 90: + case 91: + raw.Rate = 11025; + break; + + case 181: + raw.Rate = 5512; + break; + + default: + result = ldiv(1000000L, (long)256-(long)rate); + raw.Rate = (int)result.quot; + break; + } + Log_Rate(raw.Rate); + fread(&pack, 1, 1, src); + + /* + ** Copy over the raw data. If the data is packed, then + ** it is skipped since packed data is not supported by this + ** utility (yet). + */ + if (pack == 0) { + long desired; + long actual; + + blocksize -= 2; + //raw.Size += blocksize; + while (blocksize) { + desired = MIN(blocksize, sizeof(StageBuffer)); + actual = fread(StageBuffer, 1, (unsigned)desired, src); + Log_Original(actual); + + /* + ** Special process to convert audio data into deltas. + */ + if (DoSpecial) { + void *sptr = &StageBuffer[0]; + long a = actual; + + /* + ** When compressing the data, it must be processed in small + ** chunks. Loop through the source and process the chunks + ** until there is no more source data. Careful choice of the + ** size of the buffers ensures that only full chunks are + ** output. + */ + while (a) { + short tocomp; + short comped; + + tocomp = (short)MIN(a, 2048); + comped = (short)Compress_Frame(sptr, AltBuffer, tocomp); + a -= tocomp; + Log_Compressed(comped); + + (char*)sptr += (unsigned)tocomp; + raw.Size += fwrite(&comped, 1, sizeof(short), dst); + raw.Size += fwrite(&tocomp, 1, sizeof(short), dst); + raw.Size += fwrite(&MagicNumber, 1, sizeof(MagicNumber), dst); + raw.Size += fwrite(&AltBuffer[0], 1, (size_t)comped, dst); + } + + } else { + + raw.Size += actual; + fwrite(StageBuffer, 1, (unsigned)actual, dst); + Log_Compressed(actual); + } + + //fwrite(StageBuffer, 1, (unsigned)actual, dst); + blocksize -= actual; + if (actual != desired) break; + } + } else { + fseek(src, blocksize-2, SEEK_CUR); + } + break; + + /* + ** Continuation block. Sample rate and pack type is + ** inherited from the previous data block. + */ + case 2: + fread(&blocksize, 1, 3, src); + if (pack == 0) { + long desired; + long actual; + + blocksize -= 2; + raw.Size += blocksize; + while (blocksize) { + desired = MIN(blocksize, sizeof(StageBuffer)); + actual = fread(StageBuffer, 1, (unsigned)desired, src); + fwrite(StageBuffer, 1, (unsigned)actual, dst); + Log_Original(actual); + Log_Compressed(actual); + blocksize -= actual; + if (actual != desired) break; + } + } else { + fseek(src, blocksize-2, SEEK_CUR); + } + break; + + /* + ** Extended block is used for highspeed or + ** stereo samples. + */ + case 8: + fseek(src, 3, SEEK_CUR); + fread(&rate, 2, 1, src); + fread(&pack, 1, 1, src); + fread(&c, 1, 1, src); + raw.Stereo = (c == 1); + + if (raw.Stereo) { + result = ldiv(128000000L, 65536L-rate); + } else { + result = ldiv(256000000L, 65532L-rate); + } + raw.Rate = (int)result.quot; + Log_Rate(raw.Rate); + break; + + /* + ** Unprocessed blocks are skipped. + */ + case 3: // Silence block. + case 4: // Marker block. + case 5: // Embedded string. + case 6: // Loop start block. + case 7: // End of loop block. + fread(&blocksize, 1, 3, src); + fseek(src, blocksize, SEEK_CUR); + break; + + case 0: // End of file block. + break; + + default: + code = 0; + break; + } + } + + fseek(dst, 0, SEEK_SET); + fwrite(&raw, sizeof(raw), 1, dst); +} + + + +typedef enum { + CODE_2BIT, // Bit packed 2 bit delta. + CODE_4BIT, // Nibble packed 4 bit delta. + CODE_RAW, // Raw sample. + CODE_SILENCE // Run of silence. +} SCodeType; + + +signed char _2bitencode[5] = {0, 1, 2, 3}; +//signed char _2bitencode[5] = {0, 1, (2), 2, 3}; +signed int _2bitdecode[4] = {-2, -1, 0, 1}; +//signed int _2bitdecode[4] = {-2, -1, 1, 2}; + +#if(TRUE) +signed char _4bitencode[19] = { + 0, 1, 2, 2, 3, 4, 5, 6, 7, (8), 8, 9, 10, 11, 12, 13, 13, 14, 15 +}; +signed int _4bitdecode[16] = {-9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8}; +#else +signed char _4bitencode[33] = { + 0, 0, 0, 1, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 7, + 8, (8), 9, 10, 11, 12, 12, 13, + 13, 13, 14, 14, 14, 15, 15, 15, + 15 +}; +signed int _4bitdecode[16] = {-16, -13, -10, -8, -6, -4, -2, -1, 1, 2, 4, 6, 8, 10, 13, 16}; +#endif + +long Compress_Frame(void * source, void * dest, long size) +{ + unsigned char *s = (unsigned char *)source; + unsigned char *d = (unsigned char *)dest; + int delta; + unsigned int previous = 0x80; + long outcount = 0; + unsigned char *s4; // Scratch pointer into source data. + unsigned int p4; // Scratch "previous" sample value. + long max4; + unsigned char *lastraw = 0; // Pointer to last raw sequence code. + long osize = size; // Copy of original compression data length. + + /* + ** Reduce very small amplitudes to silence. Usually a rather large + ** portion of a sample is hovering around the silence value. This is + ** due, in part, to the artifacting of the sample process. These low + ** amplitudes are not audible. + */ + max4 = size; +#if(FALSE) + while (max4) { + int delta = (int)s[1] - (int)s[0]; + + /* + ** For steep sample transitions that just border on the range of 4 bit + ** deltas, adjust them to fit. This results in small savings but at + ** no loss to audio quality. + */ + if (delta == -18) s[1] += 2; + if (delta == -17) s[1]++; + if (delta == 18) s[1] -= 2; + if (delta == 17) s[1]--; + + if ((*s > 0x7F && *s < 0x81) && s[1] > 0x7F && s[1] < 0x81) *s = 0x80; + s++; + max4--; + } +#endif + s = (unsigned char *)source; + + while (size > 0 && outcount < osize) { + + /* + ** First check for runs of zero deltas. If a run of at least + ** any can be found, then output it. + */ + s4 = s; + max4 = MIN(63+1, size); + for (int i = 0; i < max4; i++) { + if (previous != *s4++) break; + } + if (i > 2) { + /* + ** When there is a code transition, terminate any run of raw + ** samples. + */ + lastraw = 0; + + *d++ = (i-1) | (CODE_SILENCE<<6); + outcount++; + s += i; + size -= i; + Log_0Bit(i); + Log_Overhead(1); + continue; + } + + /* + ** If there are fewer than 4 samples remaining, then using delta + ** compression is inefficient. Just drop into the raw routine + */ + if (size > 4) { + + s4 = s; + p4 = previous; + + /* + ** Find out the number of lossless 2 bit deltas available. These + ** deltas are always present in quads. The compressed code is + ** the delta quad count followed by the deltas in bit packed + ** bytes. + */ + max4 = MIN(64L*4L + 4L + 4L, size); + for (unsigned i = 0; i < max4; i++) { + delta = ((int)*s4++) - p4; + if (delta < -2 || delta > 1 /*|| !delta*/) break; + //if (delta < -2 || delta > 2 || !delta) break; + p4 += _2bitdecode[_2bitencode[delta+2]]; + + if (((signed)p4) < 0) p4 = 0; + if (((signed)p4) > 255) p4 = 255; + } + i >>= 2; // Delta 2 always occur in quads -- force this. + + /* + ** If there is the minimum benificial number of delta 2s available, + ** then compress them. + */ + if (i) { + Log_2Bit(i*4); + Log_Overhead(1); + + /* + ** When there is a code transition, terminate any run of raw + ** samples. + */ + lastraw = 0; + + /* + ** Output the delta 4 pair count. This is the number of pairs + ** minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, 63+1); + *d++ = (i-1) | (CODE_2BIT<<6); + outcount++; + + for (int dd = 0; dd < i; dd++) { + int delta1, delta2, delta3, delta4; + + delta1 = _2bitencode[(((int)*s++) - previous)+2]; + previous += _2bitdecode[delta1]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + delta2 = _2bitencode[(((int)*s++) - previous)+2]; + previous += _2bitdecode[delta2]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + delta3 = _2bitencode[(((int)*s++) - previous)+2]; + previous += _2bitdecode[delta3]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + delta4 = _2bitencode[(((int)*s++) - previous)+2]; + previous += _2bitdecode[delta4]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + *d++ = (delta4<<6) | (delta3<<4) | (delta2<<2) | delta1; + outcount++; + } + continue; + + } else { + + s4 = s; + p4 = previous; + + /* + ** Find out the number of lossless 4 bit deltas follow. These + ** deltas are always present in pairs. The compressed code is + ** the delta pair count followed by the deltas in nibble packed + ** bytes. + */ + max4 = MIN(64L*2L+4L+4L, size); + for (unsigned i = 0; i < max4; i++) { + delta = ((int)*s4++) - p4; + if (delta < -9 || delta >= 9 /*|| !delta*/) break; + //if (delta < -16 || delta > 16 || !delta) break; + p4 += _4bitdecode[_4bitencode[delta+9]]; + //p4 += _4bitdecode[_4bitencode[delta+16]]; + if (((signed)p4) < 0) p4 = 0; + if (((signed)p4) > 255) p4 = 255; + } + i >>= 1; // Delta 4 always occur in pairs -- force this. + + /* + ** If there is the minimum benificial number of delta 4s available, + ** then compress them. + */ + if (i) { + Log_4Bit(2*i); + Log_Overhead(1); + + /* + ** When there is a code transition, terminate any run of raw + ** samples. + */ + lastraw = 0; + + /* + ** Output the delta 4 pair count. This is the number of pairs + ** minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, 63+1); + *d++ = (i-1) | (CODE_4BIT<<6); + outcount++; + + for (int dd = 0; dd < i; dd++) { + int delta1, delta2; + + delta1 = _4bitencode[(((int)*s++) - previous) + 9]; + //delta1 = _4bitencode[(((int)*s++) - previous) + 16]; + previous += _4bitdecode[delta1]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + delta2 = _4bitencode[(((int)*s++) - previous) + 9]; + //delta2 = _4bitencode[(((int)*s++) - previous) + 16]; + previous += _4bitdecode[delta2]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + size--; + + *d++ = (delta2 << 4) | (delta1 & 0x0F); + outcount++; + } + continue; + } + } + } + + /* + ** Raw output since deltas were unsuccessful. If this is a run + ** of raw output, then merely tack it onto the run rather than + ** create a new code sequence. + */ + if (lastraw) { + *lastraw = (*lastraw)+1; + + /* + ** There is only so much a run code can accomodate. If the limit + ** has been reached, then terminate this code. A new one will be + ** created if necessary. + */ + if ((*lastraw & 0x1F) == 0x1F) { + lastraw = 0; + } + } else { + + /* + ** If there is no current raw dump of samples, then check to see if + ** this sample can fit into a 5 bit delta. If it can, then store + ** it as such as a parasite to the "raw" code. This will save a byte + ** for any stray 5 bit deltas that happen along. It is expected that + ** this is more frequent than 6 or more bit deltas that would necessitate + ** the use of the RAW code. + */ + delta = ((int)*s) - previous; + if (delta >= -16 && delta <= 15) { + lastraw = 0; + *d++ = (CODE_RAW<<6) | 0x20 | (delta & 0x1F); + outcount++; + previous = *s++; + size--; + Log_5Bit(1); + continue; + } else { + lastraw = d; + *d++ = (CODE_RAW<<6); + Log_Overhead(1); + outcount++; + } + } + Log_Raw(1); + *d++ = previous = *s++; + size--; + outcount++; + } + + /* + ** Check to see if the compression process actually resulted in smaller + ** data size. In some cases, the 'compressed' data is actually larger. In + ** this case, just output the raw frame. If the compressed and actual frame + ** size match, then it is presumed that no compression occurs. + */ + if (outcount >= osize) { + memcpy(dest, source, (size_t)osize); + outcount = osize; + Log_RawFrame(1); + } + + return(outcount); +} + +#if(FALSE) +long Decompress_Frame(void * source, void * dest, long size) +{ + unsigned int previous = 0x0080; + signed char *s = (signed char *)source; + unsigned char *d = (unsigned char *)dest; + long incount = 0; + + /* + ** Uncompress the source data until the buffer is filled. + */ + while (size > 0) { + signed char code; // Compression code. + int counter; + + code = *s++; + counter = (code & 0x3F) + 1; + incount++; + + switch ((code >> 6) & 0x03) { + case CODE_RAW: + + /* + ** The "raw" code could actually contain an embedded 5 bit delta. + ** If this is the case then this is a self contained code. Extract + ** and process the delta. + */ + if ((counter-1) & 0x20) { + counter = (counter-1) & 0x1F; + if (counter & 0x10) counter |= 0xFFE0; + previous = *d++ = previous + counter; + size--; + + } else { + + /* + ** Normal run of raw samples. + */ + memcpy(d, s, counter); + incount += counter; + size -= counter; + d += counter-1; + s += counter; + previous = *d++; + } + break; + + case CODE_4BIT: + while (counter) { + int delta; + + delta = *s++; + incount--; + + previous += (signed)_4bitdecode[delta & 0x0F]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + previous += (signed)_4bitdecode[(delta >> 4) & 0x0F]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + counter--; + } + break; + + case CODE_2BIT: + while (counter) { + int delta; + + delta = *s++; + incount--; + + previous += (signed)_2bitdecode[delta & 0x03]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + previous += (signed)_2bitdecode[(delta >> 2) & 0x03]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + previous += (signed)_2bitdecode[(delta >> 4) & 0x03]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + previous += (signed)_2bitdecode[(delta >> 6) & 0x03]; + if (((signed)previous) < 0) previous = 0; + if (((signed)previous) > 255) previous = 255; + *d++ = previous; + size--; + + counter--; + } + break; + + default: + case CODE_SILENCE: + memset(d, previous, counter); + d += counter; + size -= counter; + break; + } + } + return (incount); +} +#endif diff --git a/TOOLS/AUDIOMAK/AUDIOMAK.EXE b/TOOLS/AUDIOMAK/AUDIOMAK.EXE new file mode 100644 index 0000000..9a63bf1 Binary files /dev/null and b/TOOLS/AUDIOMAK/AUDIOMAK.EXE differ diff --git a/TOOLS/AUDIOMAK/AUDIOMAK.PJT b/TOOLS/AUDIOMAK/AUDIOMAK.PJT new file mode 100644 index 0000000..882877b --- /dev/null +++ b/TOOLS/AUDIOMAK/AUDIOMAK.PJT @@ -0,0 +1,104 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[State] +SysSetCwd='C:\PROJECTS\C&C\CODE\AUDIOMAK' +SrchSetFlags=0x20aa +FileSortMode=0x13 +StateWindowFrame=-1,0,1020,690,0x0981f5fa +_StateWindow=0,0,1024,657,0x00000108,'c:\projects\c&c\code\audiomak\audiomak.cpp',240,7,244,32,32,0,32,32,32,32,8,65535,65535,1,0,'8514oem',65518,255,49,0,4,243,14,15,241,252,249,560,28,400,0,246,252,245,242,0,0,0,0,0 +_StateBuffer='c:\projects\c&c\code\audiomak\audiomak.cpp',0x0400048e,560,28,25,'4 7','',0x0,'' +_StateBuffer='c:\projects\c&c\code\audiomak\scode.asm',0x0400048e,30,64,25,'9 17','',0x0,'' +WrapEnable=0 +_StateHistory=SEARCH,'UncompBuffer','saved','orig','FALSE','tasm','TASM','tasm','ASSEMBLER','Decom','.asm.' +_StateHistory=REPLACE,'audiomak','desired','long','unsigned','unsigned char','int','source' +_StateHistory=FILELIST,'c:\projects\c&c\code\audiomak\cointabl.voc','c:\projects\c&c\code\audiomak\cointabl.wav','c:\projects\c&c\code\audiomak\globals.cpp','c:\projects\c&c\code\audiomak\bfeared.aud','c:\projects\c&c\code\audiomak\audiomak.asm','d:\library\source\soundio.c','c:\projects\c&c\code\audiomak\scode.asm','d:\library\wwlib.i','c:\projects\c&c\code\audiomak\makefile','d:\library\source\makefile' +_StateHistory=GOTOMARK,'1','2','1' +_StateHistory=GOTOLINE,'364','398','424','431','465' + +[Editor] +FilterDeleteList +FilterAdd='Source Files','*.c;*.h;*.asm;*.inc',-1 +FilterAdd='All Files','*.*',-1 +FilterAdd='C++ Source Files','*.cpp;*.h;*.asm;*.inc',-1 +StateSetMarkLevel=7 +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +ExtColorsAssoc='.cpp',1 +ExtIndentEnableAssoc='.cpp',1 +ExtSetDelimiters=.cpp,a-zA-Z_0-9 +ExtColorsAssoc='.h',1 +ExtIndentEnableAssoc='.h',1 +ExtSetDelimiters=.h,a-zA-Z_0-9 +ExtSetDelimiters=.*,a-zA-Z_0-9 +ExtColorsAssoc='.int',1 +ExtIndentEnableAssoc='.int',1 +ExtSetDelimiters=.int,a-zA-Z_0-9 +ExtColorsAssoc='.lst',1 +ExtIndentEnableAssoc='.lst',1 +ExtColorsAssoc='.asm',1 +ExtSetDelimiters=.asm,a-zA-Z_0-9 +ExtColorsAssoc='.scr',1 +ExtIndentEnableAssoc='.scr',1 +ExtSetDelimiters=.scr,a-zA-Z_0-9 +ExtColorsAssoc='.c',1 +ExtIndentEnableAssoc='.c',1 +ExtSetDelimiters=.c,a-zA-Z_0-9 +ExtColorsAssoc='.ash',1 +ExtSetDelimiters=.ash,a-zA-Z_0-9 +ExtColorsAssoc='.i',1 +ExtIndentEnableAssoc='.i',1 +ExtSetDelimiters=.i,a-zA-Z_0-9 +Autosave=15,15 +AutosaveDir='' +ExtSetUpdateDelay=10 +ExtDelayedColoring=1 +ExtCommentSearchLimit=200 +KeyDelay=635 +KeyRepeat=190 +BufSetGlobalBackupSpec='%b.bak' +_FontDefault=0,'8514oem',65513,255,49,400,0 +SysSetDefault=DEFAULT_WINDOW_LEFT_MARGIN,4 +SysSetDefault=DEFAULT_WINDOW_VIS_MARGIN_COL,0 +SysSetDefault=DEFAULT_WINDOW_HINC,8 +SysSetDefault=DEFAULT_WINDOW_VIS_VIRTSPACES,32 +SysSetDefault=DEFAULT_WINDOW_VIS_VIRTLINES,32 +SysSetDefault=DEFAULT_WINDOW_VIS_TABS,32 +SysSetDefault=DEFAULT_WINDOW_VIS_SPACES,32 +SysSetDefault=DEFAULT_WINDOW_VIS_ELISION,0 +SysSetDefault=DEFAULT_WINDOW_VIS_EOL,32 +SysSetDefault=DEFAULT_WINDOW_VIS_EOF,32 +SysSetDefault=DEFAULT_WINDOW_SYSFLAGS,0x118 +SysSetDefault=DEFAULT_BUFFER_AUTOINDENT_MODE,0x0 +SysSetDefault=DEFAULT_BUFFER_BACKUP_SPEC,'' +SysSetDefault=DEFAULT_BUFFER_TABS,'5 9' +SysSetDefault=DEFAULT_BUFFER_MAX_TAB,200 +SysSetDefault=DEFAULT_BUFFER_MAX_VLINES,25 +SysSetDefault=DEFAULT_BUFFER_SYSFLAGS,0xc800048e +_RestoreSysFlags=0x0991f5fa, 0xfffffffc + +[Compiler] +BrowseSetFile='c:\projects\c&c\code\audiomak\audiomak.ptg' +TagSetFile='c:\projects\c&c\code\audiomak\audiomak.tag' +CompilerAddBuild='Microsoft Assembler',1073741880,'ftee masm -w2 -zi %r%e;','','','','','_MicrosoftErrorInfo','','%v%p','','','','','',0 +CompilerAddBuild='Borland C++',1073742089,'ftee make -DDEBUG=1 %r.obj','','','','','','','','ftee make -DDEBUG=1 %r.obj','','','','',49152 +CompilerAddBuild='Borland Turbo Assembler',1073741835,'ftee make -B %r.obj','','','','','','','','','','','','',16384 +CompilerAddBuild='$_cw_proj_hash_$',1073741875,'','ftee maker -B -DDEBUG=1 %r.obj','ftee maker -DDEBUG=1','','','_TasmErrorInfo','proj.err','c:\projects\c&c\code\audiomak','','_BorlandCppErrorInfo','','','',197376 +CompilerAddBuild='Default Project',1073741880,'','ftee make','ftee make','','','_ErrorInfoDefault','proj.err','%v%p','','','','','',0 +CompilerAddBuild='Microsoft C',1073741835,'ftee cl -c -AL -Gsw -Ow -Zpe %r%e','','','','','','','','','','','','',0 +CompilerAddBuild='Script',1073741880,'ftee make %r.inf','ftee make','ftee make','','','_BorlandCppErrorInfo','','','','','','','',0 +CompilerAddBuild='Zortech C++',1073741880,'ftee ztc -a -b -c -g -ml -W %r%e','','','','','_ZortechCppErrorInfo','','%v%p','','','','','',0 +CompilerAssign='Borland C++','.scr' +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Zortech C++','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Borland C++','.hpp' diff --git a/TOOLS/AUDIOMAK/MAKEFILE b/TOOLS/AUDIOMAK/MAKEFILE new file mode 100644 index 0000000..2db1d5d --- /dev/null +++ b/TOOLS/AUDIOMAK/MAKEFILE @@ -0,0 +1,116 @@ +# +# 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 . +# + +.AUTODEPEND + +OBJECTS = audiomak.obj scode.obj + +.cpp.obj: + bcc {$*.cpp } + +.asm.obj: + tasm $<,$&.obj + +audiomak.exe: turboc.cfg tasm.cfg tlink.cfg $(OBJECTS) + tlink @&&! + c0l.obj $(OBJECTS) + audiomak.exe + audiomak.map + CL.LIB MATHL.LIB EMU.LIB +! + + +#################################################################### +# Creates the TURBOC configuration file (also affects CPP.EXE) +turboc.cfg: makefile + copy &&! + -v # Debugging output. + -y + -Vt # Virtual table pointer comes at end of class members (NEVER CHANGE). + -Vb- # Always near pointer to virtual table. + -a- # Structures are packed (not byte aligned). + -b- # Enums as small as possible. + -3 # Enable 386 instructions. + -ml # Large model. + -C- # Don't allow nested comments (DON'T CHANGE THIS). + -c # Just compile -- don't link. + -Jg # Template instances are merged to avoid duplicates. + -Id:\bc4\include # Header search path. + -P- # If extension = .CPP then compile as C++, otherwise treat as C. + -w # Enable all warnings. +# -w! # Treat all warnings as errors. + -X- # Turn on autodependency checking. + -RT # Enable run time type itentification. + -x # Enable exception handling code. + -k- # Optimize: Use abbreviated stack frame. + -vi # Turns "inline" expansion on. + -Z # Optimize: Supress redundant register loads. + -Oi # Optimize: Helper functions expanded inline. + -O # Optimize: Jump optimization. + -Op # Optimize: Copy propagation to supress reloads. + -Ov # Optimize: Speeds array indexing inside of loops. + -Og # Optimize: Eliminates common subexpressions (globally). + -Oc # Optimize: Eliminates common subexpressions (locally). + -Ol # Optimize: Use REP instruction where possible. + -d # Optimize: Duplicate strings are merged. + -r # Try to put auto variables into registers. + -V # Use smart virtual tables (only usable with BCC and TLINK). + -dc- # Keep strings in the data segment (DON'T CHANGE THIS). + -Ff # Put very very large objects into the far segment. + -K2- # Treat "char" different from "signed char" or "unsigned char". + -Oa # Optimize: Assumes no pointer aliasing (could cause bugs)! + -R- # Don't include browser data (only used by IDE). +# -Oe # Optimize: Global register allocation tracking enabled. +# -Ob # Optimize: Dead code elimination. +# -Om # Optimize: Remove invariant code from loops. +# -p # Pascal calling convention default. +# -pr # Regiser calling convention default. +# -po # Pass "this" in registers when calling member functions. +# -f- # Floating point prohibited. +# -Yo # Compile as overlay module. +# -Ox # Optimize: Execution speed. +! turboc.cfg /Q + +#################################################################### +# Creates TASM config file. +tasm.cfg: makefile + copy &&! + /Zi # Include debug information into object file. + /w+ICG + /w-PDC + /t + /w2 # Maximum warning level. + /ml # Large model. + /w+ + /v + /JIDEAL # Ideal mode. + /JP386N # Enable 386 instructions. + /UT300 # Force compatibility with old TASM. +! tasm.cfg + +#################################################################### +# Creates the TLINK.CFG file which implicitly controls linking. +tlink.cfg: makefile + copy &&! + /Ld:\bc4\lib + /v + /c + /e + /Tde +! tlink.cfg /Q + diff --git a/TOOLS/AUDIOMAK/SCODE.ASM b/TOOLS/AUDIOMAK/SCODE.ASM new file mode 100644 index 0000000..5d6f241 --- /dev/null +++ b/TOOLS/AUDIOMAK/SCODE.ASM @@ -0,0 +1,322 @@ +; +; 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 . +; + + + IDEAL +IDEAL_MODE EQU 1 + VERSION T300 ; Code written for V3.0 TASM version. + IDEAL + P386N ; target machine. + SMART ; Enable optimizations. + WARN ; Full warnings. + LOCALS ?? + MODEL LARGE @filename +; INCLUDE "d:\library\wwlib.i" + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +; VERSION T300 ; Code written for V3.0 TASM version. +; P386N ; target machine. +; MODEL LARGE @filename +; WARN ; Full warnings. +; LOCALS ?? + + CODESEG + +_2bitdecode DB -2, -1, 0, 1 +;_2bitdecode DB -2, -1, 1, 2 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 +;_4bitdecode DB -16,-13,-10,-8,-6,-4,-2,-1,1,2,4,6,8,10,13,16 + + GLOBAL C Decompress_Frame:FAR + PROC C Decompress_Frame FAR USES bx cx edx ds si es di bp + ARG source:DWORD + ARG dest:DWORD + ARG count:WORD + + LOCAL previous:BYTE + LOCAL incount:WORD + + ; Initialize counter for number of bytes read from source. + mov [incount],0 + + ; Verify parameters for legality. + cmp [source],0 + je ??fini + cmp [dest],0 + je ??fini + cmp [count],0 + je ??fini + + ; Fetch parameter values into working registers. + lds si,[source] ; Pointer to source data. + les di,[dest] ; Pointer to destination data. + mov cx,[count] ; Number of bytes to fill dest buffer. + mov dl,080h ; Previous sample (starting value). + +??mainloop: + ; Check to see if the destination is full. Exit if so. + cmp cx,0 + jle ??fini + + ; Fetch code byte from input stream. + xor ah,ah + mov al,[ds:si] + inc [incount] + inc si + shl ax,2 ; AH contains code. + shr al,2 ; AL contains sub-code data. + + ; Check to see if a raw sequence follows. + cmp ah,CODE_RAW + jne short ??try4bit + + ; The code contains either a 5 bit delta or a count of raw samples + ; to dump out. + test al,00100000b + je short ??justraw + + ; The lower 5 bits are actually a signed delta. Sign extend the + ; delta and add it to the stream. + shl al,3 + sar al,3 + add dl,al + mov [es:di],dl + dec cx + inc di + jmp ??mainloop + + ; The lower 5 bits hold a count of the number of raw samples that + ; follow this code. Dump these samples to the output buffer. +??justraw: + mov bx,cx + xor ah,ah + inc al + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + mov cx,bx + add [incount],ax + sub cx,ax + dec di + mov dl,[es:di] ; Set "previous" value. + inc di + jmp ??mainloop + + ; Check to see if this is a 4 bit delta code sequence. +??try4bit: + inc al ; Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + + ; A sequence of 4bit deltas follow. AL equals the number of nibble + ; packed delta bytes to process. +??bit4loop: + ; Fetch nibble packed delta codes. + mov ah,[ds:si] + mov bl,ah + inc [incount] + inc si + + ; Add first delta to 'previous' sample already in DL. + and bx,00001111b + + add dl,[cs:_4bitdecode+bx] ; Add in delta + pushf + cmp [cs:_4bitdecode+bx],0 + jl short ??neg1 + popf + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 +??neg1: + popf + jc short ??ok1 + xor dl,dl +??ok1: + mov dh,dl ; DH now holds new 'previous' sample. + + mov bl,ah + shr bl,4 + add dh,[cs:_4bitdecode+bx] ; Add in delta + pushf + cmp [cs:_4bitdecode+bx],0 + jl short ??neg2 + popf + jnc short ??ok2 + mov dh,0FFh + jmp short ??ok2 +??neg2: + popf + jc short ??ok2 + xor dh,dh +??ok2: + + ; Output the two sample bytes. + mov [es:di],dx + sub cx,2 + add di,2 + + ; Put the correct 'previous' sample in DL where it belongs. + mov dl,dh + + ; If there are more deltas to process then loop back. + dec al + jnz short ??bit4loop + + jmp ??mainloop + + ; Check to see if 2 bit deltas need to be processed. +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + + ; A sequence of 2bit deltas follow. AL equals the number of + ; packed delta bytes to process. +??bit2loop: + ; Fetch packed delta codes. + mov ah,[ds:si] + inc [incount] + inc si + + ; Add first delta to 'previous' sample already in DL. + mov bl,ah + and bx,000011b + add dl,[cs:_2bitdecode+bx] ; Add in delta + + pushf + cmp [cs:_2bitdecode+bx],0 + jl short ??neg3 + popf + jnc short ??ok3 + mov dl,0FFh + jmp short ??ok3 +??neg3: + popf + jc short ??ok3 + xor dl,dl +??ok3: + + mov dh,dl + ror edx,8 + + mov bl,ah + shr bx,2 + and bl,00000011b + add dl,[cs:_2bitdecode+bx] ; Add in delta + + pushf + cmp [cs:_2bitdecode+bx],0 + jl short ??neg4 + popf + jnc short ??ok4 + mov dl,0FFh + jmp short ??ok4 +??neg4: + popf + jc short ??ok4 + xor dl,dl +??ok4: + + mov dh,dl + ror edx,8 + + mov bl,ah + shr bx,4 + and bl,00000011b + add dl,[cs:_2bitdecode+bx] ; Add in delta + + pushf + cmp [cs:_2bitdecode+bx],0 + jl short ??neg5 + popf + jnc short ??ok5 + mov dl,0FFh + jmp short ??ok5 +??neg5: + popf + jc short ??ok5 + xor dl,dl +??ok5: + + mov dh,dl + ror edx,8 + + mov bl,ah + shr bx,6 + and bl,00000011b + add dl,[cs:_2bitdecode+bx] ; Add in delta + + pushf + cmp [cs:_2bitdecode+bx],0 + jl short ??neg6 + popf + jnc short ??ok6 + mov dl,0FFh + jmp short ??ok6 +??neg6: + popf + jc short ??ok6 + xor dl,dl +??ok6: + + ;mov dh,dl + ror edx,8 + + ; Output the two sample bytes. + mov [es:di],edx + sub cx,4 + add di,4 + + ; Put the correct 'previous' sample in DL where it belongs. + rol edx,8 + + ; If there are more deltas to process then loop back. + dec al + jnz ??bit2loop + + jmp ??mainloop + + ; There is a run of zero deltas. Zero deltas merely duplicate + ; the 'previous' sample the requested number of times. +??zerodelta: + xor bh,bh + mov bl,al + mov al,dl + sub cx,bx + xchg cx,bx + rep stosb + mov cx,bx + jmp ??mainloop + + ; Final cleanup and exit. +??fini: + mov ax,[incount] + ret + + ENDP Decompress_Frame + + END + diff --git a/TOOLS/AUDIOMAK/TASM.CFG b/TOOLS/AUDIOMAK/TASM.CFG new file mode 100644 index 0000000..68cb1b5 --- /dev/null +++ b/TOOLS/AUDIOMAK/TASM.CFG @@ -0,0 +1,11 @@ + /Zi + /w+ICG + /w-PDC + /t + /w2 + /ml + /w+ + /v + /JIDEAL + /JP386N + /UT300 diff --git a/TOOLS/AUDIOMAK/TLINK.CFG b/TOOLS/AUDIOMAK/TLINK.CFG new file mode 100644 index 0000000..26d2986 --- /dev/null +++ b/TOOLS/AUDIOMAK/TLINK.CFG @@ -0,0 +1,5 @@ + /Ld:\bc4\lib + /v + /c + /e + /Tde diff --git a/TOOLS/AUDIOMAK/TURBOC.CFG b/TOOLS/AUDIOMAK/TURBOC.CFG new file mode 100644 index 0000000..4ededb5 --- /dev/null +++ b/TOOLS/AUDIOMAK/TURBOC.CFG @@ -0,0 +1,35 @@ + -v + -y + -Vt + -Vb- + -a- + -b- + -3 + -ml + -C- + -c + -Jg + -Id:\bc4\include + -P- + -w + -X- + -RT + -x + -k- + -vi + -Z + -Oi + -O + -Op + -Ov + -Og + -Oc + -Ol + -d + -r + -V + -dc- + -Ff + -K2- + -Oa + -R- diff --git a/TOOLS/FILEDATA.EXE b/TOOLS/FILEDATA.EXE new file mode 100644 index 0000000..8f27feb Binary files /dev/null and b/TOOLS/FILEDATA.EXE differ diff --git a/TOOLS/FONTMAKE.EXE b/TOOLS/FONTMAKE.EXE new file mode 100644 index 0000000..0c89a62 Binary files /dev/null and b/TOOLS/FONTMAKE.EXE differ diff --git a/TOOLS/GETREG.EXE b/TOOLS/GETREG.EXE new file mode 100644 index 0000000..6bbfb0c Binary files /dev/null and b/TOOLS/GETREG.EXE differ diff --git a/TOOLS/ICONCOMP.EXE b/TOOLS/ICONCOMP.EXE new file mode 100644 index 0000000..f392940 Binary files /dev/null and b/TOOLS/ICONCOMP.EXE differ diff --git a/TOOLS/ICONMAP.EXE b/TOOLS/ICONMAP.EXE new file mode 100644 index 0000000..d9f529a Binary files /dev/null and b/TOOLS/ICONMAP.EXE differ diff --git a/TOOLS/KEYFRAME.EXE b/TOOLS/KEYFRAME.EXE new file mode 100644 index 0000000..233e116 Binary files /dev/null and b/TOOLS/KEYFRAME.EXE differ diff --git a/TOOLS/MAP2MAP.EXE b/TOOLS/MAP2MAP.EXE new file mode 100644 index 0000000..7ade986 Binary files /dev/null and b/TOOLS/MAP2MAP.EXE differ diff --git a/TOOLS/MIX/MIXFILE.CPP b/TOOLS/MIX/MIXFILE.CPP new file mode 100644 index 0000000..cb55ba3 --- /dev/null +++ b/TOOLS/MIX/MIXFILE.CPP @@ -0,0 +1,675 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + ** 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 : Data file creator. * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 6, 1994 * + * * + * Last Update : 10/27/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Calc_CRC -- Calculate the CRC value of a block of data. * + * compfunc -- Comparison function used by qsort(). * + * DataClass::DataClass -- Constructor for a data file object node. * + * DataClass::Process_Input -- Process the input file list and builds linked records. * + * DataClass::Process_Output -- Creates the output data file from the component source files.* + * DataClass::~DataClass -- Destructor for the data file object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma inline + +#include +#include +#include +#include +#include + +#define FALSE 0 +#define TRUE 1 + +long Calc_CRC(void const *data, long size); + + +/******************************************************************** +** This is the data block controlling class. It is used for every data +** file as well as processing the entire data file list. +*/ +class DataClass { + public: + long CRC; + static short Count; + static long TotalSize; + static char *ExtFrom[10]; + static char *ExtTo[10]; + static int ExtCount; + static char *AltPath[10]; + static int AltPathCount; + + DataClass(char const *filename); + ~DataClass(void); + static void Process_Input(char const *infile, int quiet, int paths); + static void Process_Output(char const *outfile); + + char const * Output_Filename(void); + char const * Input_Filename(void); + + private: + DataClass *Next; // Pointer to next file in chain. + char *Filename; // Raw original filename. + long Size; // Size of data element. + long Offset; // Offset within mixfile for data start. + int Index; // Write order number. + + static DataClass *First; + static int Quiet; + static int Paths; +}; + +char *DataClass::AltPath[10]; +char *DataClass::ExtFrom[10]; +char *DataClass::ExtTo[10]; +int DataClass::ExtCount = 0; +int DataClass::AltPathCount = 0; +short DataClass::Count = 0; +DataClass * DataClass::First = 0; +int DataClass::Quiet = FALSE; +int DataClass::Paths = FALSE; +long DataClass::TotalSize = 0; + +int main(int argc, char ** argv) +{ + class UsageError{}; // Parameter error or usage display desired. + char *infile = 0; + char *outfile = 0; + int quiet = FALSE; + int paths = FALSE; + + /* + ** Banner message. + */ + printf("MIXFILE V1.4 (c)\n"); + + /* + ** Process the parameter list and dispatch the packing function. + */ + try { + + /* + ** If not enough parameters were specified, then immediately + ** display the usage instructions. + */ + if (argc < 2) throw UsageError(); + + try { + for (int index = 1; index < argc; index++) { + char *arg = argv[index]; + + switch (*arg) { + + /* + ** Process any command line switches. + */ + case '/': + case '-': + switch (toupper(arg[1])) { + case 'Q': + quiet = TRUE; + break; + + case 'S': + paths = TRUE; + break; + + case 'E': + if (DataClass::ExtCount >= sizeof(DataClass::ExtFrom)/sizeof(DataClass::ExtFrom[0])) { + throw "Too many extensions specified"; + } else { + char * ptr = strupr(strtok(&arg[2], "=")); + if (*ptr == '.') ptr++; + DataClass::ExtFrom[DataClass::ExtCount] = ptr; + + ptr = strupr(strtok(NULL, "=\r\n")); + if (*ptr == '.') ptr++; + DataClass::ExtTo[DataClass::ExtCount] = ptr; + + DataClass::ExtCount++; + } + break; + + case 'I': + if (DataClass::AltPathCount >= sizeof(DataClass::AltPath)/sizeof(DataClass::AltPath[0])) { + throw "Too many paths specified"; + } else { + char dir[MAXDIR]; + + strcpy(dir, &arg[2]); + if (dir[strlen(dir)-1] != '\\') { + strcat(dir, "\\"); + } + DataClass::AltPath[DataClass::AltPathCount++] = strupr(strdup(dir)); + } + break; + + default: + throw "Unrecognized option flag"; + } + break; + + /* + ** Process command line filenames for either input or output. + */ + default: + if (outfile) throw "Unrecognized parameter"; + if (!infile) { + FILE *file = fopen(arg, "r"); + if (!file) throw "Unable to open input file"; + fclose(file); + infile = arg; + continue; + } + if (!outfile) { + outfile = arg; + } + break; + } + } + + /* + ** Perform a last minute check to make sure both an input and + ** output filename was specified. If not, then throw an + ** error. + */ + if (!outfile) throw "No output file specified"; + if (!infile) throw "No input file specified"; + + /* + ** Process the data. + */ + try { + DataClass::Process_Input(infile, quiet, paths); + DataClass::Process_Output(outfile); + printf("Created mix file '%s'\nEmbedded objects = %d\nTotal file size = %ld\n", outfile, DataClass::Count, DataClass::TotalSize); + } + catch (char *message) { + printf("\nERROR: %s.\n", message); + exit(EXIT_FAILURE); + } + } + + /* + ** This exception is called for any of the various fatal errors that + ** can occur while parsing the parameters. + */ + catch(char *message) { + printf("\nERROR: %s.\n", message); + throw UsageError(); + } + } + + /* + ** This exception is thrown when the utility can't proceed and the + ** utility usage instructions are desired to be displayed. + */ + catch(UsageError) { + printf("\nUSAGE: DATFILE \n" + "\n" + " : list of filenames to pack\n" + " : output data filename\n" + " Options\n" + " -q : quiet operation\n" + " -i : alternate to find files\n" + " -e= : changes file extensions from to \n" + " -s : record paths in ID\n"); + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} + + +/*********************************************************************************************** + * DataClass::Process_Input -- Process the input file list and builds linked records. * + * * + * This routine will process the list of files in the input file. It builds a linked * + * list of DataClass objects. This routine is the initial process of creating a packed * + * data file. * * + * * + * INPUT: infile -- Pointer to input filename. * + * * + * quiet -- Process quietly? * + * * + * paths -- Should the path of the filename be considered as part of its signature?* + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + *=============================================================================================*/ +void DataClass::Process_Input(char const *infile, int quiet, int paths) +{ + FILE *file; // Input file. + static char buffer[MAXFILE]; + + Quiet = quiet; + Paths = paths; + + if (!infile) throw "No input file specified"; + + file = fopen(infile, "ra"); + if (!file) throw "Could not open input file"; + + if (!Quiet) { + printf("Processing '%s'.\n", infile); + } + + /* + ** Process the source file list. + */ + try { + for (;;) { + int result = fscanf(file, "%s", buffer); + if (result == EOF) break; + + new DataClass(buffer); + } + } + + /* + ** This error handler is used if there are any errors that occur while + ** parsing and verifying the data block filenames. Errors can include + ** source filename errors as well as missing source files themselves. + */ + catch (char *message) { + fclose(file); + throw message; + } + + fclose(file); +} + + +/*********************************************************************************************** + * DataClass::DataClass -- Constructor for a data file object node. * + * * + * This constructs a data file object node and links it into the list of object nodes. * + * It performs a preliminary check on the existance of the the source file. * + * * + * INPUT: filename -- The filename of the object to add. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + * 10/27/94 JLB : Handles multiple data paths. * + *=============================================================================================*/ +DataClass::DataClass(char const *filename) +{ + static char buffer[100]; + + if (!filename) throw "NULL filename"; + + Filename = strdup(filename); + strupr(Filename); + + /* + ** Try to find the data file. + */ + FILE *datafile = fopen(Filename, "rb"); + if (!datafile) { + + /* + ** If the file couldn't be found in the current directory, check + ** the alternate paths. If the alternate path is successful, then + ** alter the pathname to match and continue. + */ + for (int index = 0; index < AltPathCount; index++) { + strcpy(buffer, AltPath[index]); + strcat(buffer, Filename); + datafile = fopen(buffer, "rb"); + if (datafile) { + free(Filename); + Filename = strdup(buffer); + break; + } + } + + if (!datafile) { + sprintf(buffer, "Could not find source file '%s'", Filename); + throw buffer; + } + } + + /* + ** Determine the CRC identifier for the filename, This can be either + ** the base filename or the complete path (as indicated by the command + ** line parameter). + */ + char const * name = Output_Filename(); + CRC = Calc_CRC(name, strlen(name)); + + /* + ** Find out the size of the source data file. + */ + fseek(datafile, 0, SEEK_END); + long size = ftell(datafile); + fclose(datafile); + + if (size == -1) throw "Seek failure"; + Size = size; + + Index = Count; + Count++; + Next = First; + First = this; +} + + +/*********************************************************************************************** + * DataClass::~DataClass -- Destructor for the data file object. * + * * + * This is the destructor for the data file object. It deallocates any memory allocated * + * and de-links it from the list of file objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + *=============================================================================================*/ +DataClass::~DataClass(void) +{ + if (Filename) { + free(Filename); + Filename = 0; + } + + Count--; + DataClass *ptr = First; + DataClass *prev = 0; + while (ptr) { + if (ptr == this) { + if (prev) { + prev->Next = Next; + } + return; + } + prev = ptr; + ptr = ptr->Next; + } +} + + +/*********************************************************************************************** + * compfunc -- Comparison function used by qsort(). * + * * + * This is a support function that compares two file data objects against their CRC * + * values. * + * * + * INPUT: ptr1 -- Pointer to object number 1. * + * * + * ptr2 -- Pointer to object number 2. * + * * + * OUTPUT: Returns with a logical comparison of object 1 to object 2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + *=============================================================================================*/ +int compfunc(const void *ptr1, const void *ptr2) +{ + DataClass const *obj1 = *(DataClass const **)ptr1; + DataClass const *obj2 = *(DataClass const **)ptr2; + + if (obj1->CRC < obj2->CRC) { + return (-1); + } + if (obj1->CRC > obj2->CRC) { + return (1); + } + return(0); +} + + +/*********************************************************************************************** + * DataClass::Process_Output -- Creates the output data file from the component source files. * + * * + * This is the final step in creation of the data output file. It writes the header * + * block and then appends the appropriate files to the output file. * + * * + * INPUT: outname -- Pointer to the filename to use for output. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + *=============================================================================================*/ +void DataClass::Process_Output(char const *outname) +{ + FILE *outfile; + + if (Count) { + DataClass **array = new DataClass *[Count]; + + /* + ** Open the output file for creation. + */ + if (!outname) throw "Missing output filename"; + outfile = fopen(outname, "wb"); + if (!outfile) { + throw "Unable to open output file"; + } + + /* + ** Build a working array to the file objects. + */ + DataClass * ptr = First; + for (int index = 0; index < Count; index++) { + array[index] = ptr; + ptr->Index = index; + ptr = ptr->Next; + } + + /* + ** Precalculate the offset value for each data file will be. + ** This value will be inserted into the header block. This is + ** performed BEFORE the files are sorted because the data files + ** are written to disk in the order that they were specified. + */ + TotalSize = 0; + for (index = 0; index < Count; index++) { + array[index]->Offset = TotalSize; + TotalSize += array[index]->Size; + } + + /* + ** Next, sort the data objects so that a binary search on CRC values + ** can be used for file retrieval. + */ + qsort(array, Count, sizeof(array[0]), compfunc); + + /* + ** Output the header section of the data file. This contains + ** the count of objects contained, the CRC, and offset for each. + */ + fwrite(&Count, sizeof(Count), 1, outfile); + fwrite(&TotalSize, sizeof(TotalSize), 1, outfile); + for (index = 0; index < Count; index++) { + fwrite(&array[index]->CRC, sizeof(array[index]->CRC), 1, outfile); + fwrite(&array[index]->Offset, sizeof(array[index]->Offset), 1, outfile); + fwrite(&array[index]->Size, sizeof(array[index]->Size), 1, outfile); + } + + TotalSize += sizeof(Count) + sizeof(TotalSize) + (Count*12); + + if (!Quiet) { + printf("size CRC Filename\n"); + printf("------ -------- -------------------------\n"); + } + + /* + ** Now write the actual data -- one file at a time in the order that they were + ** originally specified. + */ + for (int order = 0; order < Count; order++) { + for (index = 0; index < Count; index++) { + DataClass * entry = array[index]; + + if (entry->Index == order) { + FILE *infile; + long size; + static char buffer[1024*30]; + + if (!Quiet) { + printf("%-8ld [%08lX] %s\n", entry->Size, entry->CRC, entry->Output_Filename()); + } + + size = entry->Size; + infile = fopen(entry->Input_Filename(), "rb"); + while (size > 0) { + int count; + + count = (size < sizeof(buffer)) ? (int)size : sizeof(buffer); + count = fread(buffer, sizeof(buffer[0]), count, infile); + fwrite(buffer, sizeof(buffer[0]), count, outfile); + size -= count; + if (!count) break; // Hmm.. + } + fclose(infile); + break; + } + } + } + + fclose(outfile); + } +} + + +/*********************************************************************************************** + * Calc_CRC -- Calculate the CRC value of a block of data. * + * * + * This routine is used to create a CRC value from a string of data bytes. * + * * + * INPUT: data -- Pointer to the data bytes to calculate the CRC value for. * + * * + * size -- The number of bytes in the data block. * + * * + * OUTPUT: Returns with the CRC value of the buffer specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created. * + *=============================================================================================*/ +long Calc_CRC(void const *data, long size) +{ + long crc = 0; // Accumulating CRC value. + long const *ptr = static_cast(data); + + /* + ** Process the bulk of the data (4 bytes at a time). + */ + for (; size > sizeof(long); size -= sizeof(long)) { + long temp = *ptr++; + asm { + mov eax,crc + rol eax,1 + add eax,temp + mov crc,eax + } + } + + /* + ** If there are any remainder bytes, then process them + ** as a group of four, but the trailing left over bytes + ** are forced to be NULL. + */ + if (size) { + long temp = 0; + + memcpy(&temp, ptr, (size_t)size); + asm { + mov eax,crc + rol eax,1 + add eax,temp + mov crc,eax + } + } + + return (crc); +} + + +char const * DataClass::Output_Filename(void) +{ + char file[MAXFILE]; + char ext[MAXEXT]; + char path[MAXPATH]; + char drive[MAXDRIVE]; + static char filename[255]; + + /* + ** Break the original filename into its component parts. + */ + fnsplit(Filename, drive, path, file, ext); + + /* + ** Substitute the extension if a substitution is called for. + */ + for (int index = 0; index < ExtCount; index++) { + if (stricmp(ExtFrom[index], &ext[1]) == 0) { + strcpy(&ext[1], ExtTo[index]); + break; + } + } + + /* + ** Strip the path from the filename if path stripping is enabled. + */ + if (!Paths) { + fnmerge(filename, NULL, NULL, file, ext); + } else { + fnmerge(filename, drive, path, file, ext); + } + return(&filename[0]); +} + + +char const * DataClass::Input_Filename(void) +{ + return(Filename); +} diff --git a/TOOLS/MIX/MIXFILE.EXE b/TOOLS/MIX/MIXFILE.EXE new file mode 100644 index 0000000..1cc677e Binary files /dev/null and b/TOOLS/MIX/MIXFILE.EXE differ diff --git a/TOOLS/MIX/MIXFILE.IDE b/TOOLS/MIX/MIXFILE.IDE new file mode 100644 index 0000000..c760cb7 Binary files /dev/null and b/TOOLS/MIX/MIXFILE.IDE differ diff --git a/TOOLS/MIX/MIXFILE.IDE.BAK b/TOOLS/MIX/MIXFILE.IDE.BAK new file mode 100644 index 0000000..28d214f Binary files /dev/null and b/TOOLS/MIX/MIXFILE.IDE.BAK differ diff --git a/TOOLS/MIXFILE.EXE b/TOOLS/MIXFILE.EXE new file mode 100644 index 0000000..fd9422b Binary files /dev/null and b/TOOLS/MIXFILE.EXE differ diff --git a/TOOLS/WWCOMP.EXE b/TOOLS/WWCOMP.EXE new file mode 100644 index 0000000..4f25f18 Binary files /dev/null and b/TOOLS/WWCOMP.EXE differ diff --git a/TOOLS/WWPACK.EXE b/TOOLS/WWPACK.EXE new file mode 100644 index 0000000..a3e9602 Binary files /dev/null and b/TOOLS/WWPACK.EXE differ diff --git a/VQ/INCLUDE/HMI32/SOS.H b/VQ/INCLUDE/HMI32/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/VQ/INCLUDE/HMI32/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/VQ/INCLUDE/HMI32/SOSDATA.H b/VQ/INCLUDE/HMI32/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/VQ/INCLUDE/HMI32/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/VQ/INCLUDE/HMI32/SOSDEFS.H b/VQ/INCLUDE/HMI32/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/VQ/INCLUDE/HMI32/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/VQ/INCLUDE/HMI32/SOSFNCT.H b/VQ/INCLUDE/HMI32/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/VQ/INCLUDE/HMI32/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/VQ/INCLUDE/HMI32/SOSRES.H b/VQ/INCLUDE/HMI32/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/VQ/INCLUDE/HMI32/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/VQ/INCLUDE/VOCFILE.H b/VQ/INCLUDE/VOCFILE.H new file mode 100644 index 0000000..bbdd54d --- /dev/null +++ b/VQ/INCLUDE/VOCFILE.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +#ifndef VOCFILE_H +#define VOCFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vocfile.h +* +* DESCRIPTION +* VOC audio file definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +****************************************************************************/ + +/* VOC file header. + * + * type - File type description + * offset - Offset of data block from the start of VOC file. + * ver - File format version number + * id - File identification code. + */ +typedef struct _VOCHeader { + char type[0x14]; + unsigned short offset; + unsigned short ver; + unsigned short id; +} VOCHeader; + +/* VOC sub-block block types */ +#define VBT_TERMINATE 0 /* Last block of file (no size field) */ +#define VBT_VOICEDATA 1 /* New set of voice data */ +#define VBT_VOICECONT 2 /* Continuation of voice data */ +#define VBT_SILENCE 3 /* Silence period */ +#define VBT_MARKER 4 /* Syncronization marker */ +#define VBT_ASCII 5 /* NULL terminated string */ +#define VBT_REPEAT 6 /* Mark beginning of repeat loop */ +#define VBT_ENDREPEAT 7 /* Mark end of repeat loop */ +#define VBT_EXTENDED 8 + +#define IS_VOC(a,b) (((~a)+0x1234)==b) +#define BLOCK_LEN(a) ((a&0x00FFFFFFL)) + +#endif /* VOCFILE_H */ + + diff --git a/VQ/INCLUDE/VQ.H b/VQ/INCLUDE/VQ.H new file mode 100644 index 0000000..f59d3f9 --- /dev/null +++ b/VQ/INCLUDE/VQ.H @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +#ifndef VQ_H +#define VQ_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* NAME +* vq.h +* +* DESCRIPTION +* Vq file definition +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +****************************************************************************/ + +/* VQHeader - VQ header structure. + * + * ImageSize - Compressed image size + * ImageWidth - Image x-dimension + * ImageHeight - Image y-dimension + * BlockWidth - Block x-dimension + * BlockHeight - Block y-dimension + * BlockType - Block type + * PaletteRange - Number of palette colors + * Num1Color - Number of 1-color blocks & 1-color colors + * CodebookSize - Number of actual codebook entries + * CodingFlag - Flag for 2-color blocks + * FrameDiffMethod - Frame differencing method + * ForcedPalette - Force a palette on image + * F555Palette - Flag for output 15-bit palette + * VQVersion - VQ Version # + * pad[5] - Pad out to 40 bytes total + */ +typedef struct _VQHeader { + unsigned long ImageSize; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned short BlockWidth; + unsigned short BlockHeight; + unsigned short BlockType; + unsigned short PaletteRange; + unsigned short Num1Color; + unsigned short CodebookSize; + unsigned short CodingFlag; + unsigned short FrameDiffMethod; + unsigned short ForcedPalette; + unsigned short F555Palette; + unsigned short VQVersion; + unsigned short pad[5]; +} VQHeader; + +#endif /* VQ_H */ diff --git a/VQ/INCLUDE/VQA32/VQAFILE.H b/VQ/INCLUDE/VQA32/VQAFILE.H new file mode 100644 index 0000000..24b45db --- /dev/null +++ b/VQ/INCLUDE/VQA32/VQAFILE.H @@ -0,0 +1,197 @@ +/* +** 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 . +*/ + +#ifndef VQAFILE_H +#define VQAFILE_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqafile.h +* +* DESCRIPTION +* VQA file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +#include + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED DEFINES. + *-------------------------------------------------------------------------*/ + +/* VQAHeader: VQA movie description header. (VQHD) + * + * Version - VQA version. + * Flags - Various flags. (See below) + * ImageWidth - Image width in pixels. + * ImageHeight - Image height in pixels. + * BlockWidth - Block width in pixels. + * BlockHeight - Block height in pixels. + * Frames - Total number of frames in the movie. + * FPS - Playback rate (Frame Per Second). + * Groupsize - Frame grouping size (frames per codebook). + * Num1Colors - Number of 1 color colors. + * CBentries - Number of codebook entries. + * Xpos - X position to draw frames. (-1 = Center) + * Ypos - Y position to draw frames. (-1 = Center) + * MaxFramesize - Size of largest frame. + * SampleRate - Sample rate of primary audio stream. + * Channels - Number of channels in primary audio stream. + * BitsPerSample - Sample bit size in primary audio stream. + * FutureUse - Reserved for future expansion. + */ +typedef struct _VQAHeader { + unsigned short Version; + unsigned short Flags; + unsigned short Frames; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char BlockWidth; + unsigned char BlockHeight; + unsigned char FPS; + unsigned char Groupsize; + unsigned short Num1Colors; + unsigned short CBentries; + unsigned short Xpos; + unsigned short Ypos; + unsigned short MaxFramesize; + unsigned short SampleRate; + unsigned char Channels; + unsigned char BitsPerSample; + unsigned short AltSampleRate; + unsigned char AltChannels; + unsigned char AltBitsPerSample; + unsigned short FutureUse[5]; +} VQAHeader; + +/* Version type. */ +#define VQAHD_VER1 1 +#define VQAHD_VER2 2 + +/* VQA header flag definitions */ +#define VQAHDB_AUDIO 0 /* Audio track present. */ +#define VQAHDB_ALTAUDIO 1 /* Alternate audio track present. */ +#define VQAHDF_AUDIO (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 1 /* 4x4 block decode enable/disable */ +#else +#define VQAVOC_ON 1 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 1 /* 4x4 block decode enable/disable */ +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_STOP 2 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +/* Error/condition values */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ + +/* Memory limits */ +#define VQA_NUM_MAXRATES 5 /* Number of max rates in the Config struct */ +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use 22050 scaled to the + * frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * MaxRate - Fixed rate playback table. + */ +typedef struct _VQAConfig { + void (*DrawerCallback)(unsigned char *screen, long framenum); + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQMALL_H +#define VQMALL_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* all.h +* +* DESCRIPTION +* All VQMisc32 library definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* VQMALL_H */ + diff --git a/VQ/INCLUDE/VQM32/CAPTOKEN.H b/VQ/INCLUDE/VQM32/CAPTOKEN.H new file mode 100644 index 0000000..17dc8e0 --- /dev/null +++ b/VQ/INCLUDE/VQM32/CAPTOKEN.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +#ifndef VQMCAPTOKEN_H +#define VQMCAPTOKEN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.h +* +* DESCRIPTION +* Text caption script definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +/* CaptionText: This structure describes a line of text to be displayed + * with the video/audio. + * + * Size - Size of caption in bytes. + * OnFrame - Frame number to display caption. + * OffFrame - Frame number to clear caption. + * Flags - Display modifiers. + * CPF - Characters to draw per frame. + * Xpos - X pixel position to display caption. + * Ypos - Y pixel position to display caption. + * BgPen - Background pen to use. + * FgPen - Foreground pen to use. + * Text - Text string to display. (WORD aligned) + */ +typedef struct _CaptionText { + unsigned short Size; + unsigned short OnFrame; + unsigned short OffFrame; + unsigned char Flags; + char CPF; + unsigned short Xpos; + unsigned short Ypos; + char BgPen; + char FgPen; + char Text[]; +} CaptionText; + +/* CaptionText flag definitions. */ +#define CTF_JUSTIFY (3<<0) /* Justification field. */ +#define CTF_ABS (0<<0) /* Use absolute X,Y positions. */ +#define CTF_CENTER (1<<0) /* Justify on Center */ +#define CTF_LEFT (2<<0) /* Justify on left */ +#define CTF_RIGHT (3<<0) /* Justify on right */ +#define CTF_FLASH (1<<4) /* Flash text. */ + +/* Function prototypes. */ +long BuildCaptions(char *name, char *buffer); + +#endif /* VQMCAPTOKEN_H */ + diff --git a/VQ/INCLUDE/VQM32/COMPRESS.H b/VQ/INCLUDE/VQM32/COMPRESS.H new file mode 100644 index 0000000..980d0ed --- /dev/null +++ b/VQ/INCLUDE/VQM32/COMPRESS.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMCOMP_H +#define VQMCOMP_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* compress.h +* +* DESCRIPTION +* Compression definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long cdecl LCW_Compress(char const *source, char *dest, + unsigned long length); +unsigned long cdecl LCW_Uncompress(char const *source, char *dest, + unsigned long length); +long AudioZap(void *source, void *dest, long size); +long cdecl AudioUnzap(void *source, void *dest, long); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCOMP_H */ + diff --git a/VQ/INCLUDE/VQM32/CRC.H b/VQ/INCLUDE/VQM32/CRC.H new file mode 100644 index 0000000..d70e018 --- /dev/null +++ b/VQ/INCLUDE/VQM32/CRC.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef VQMCRC_H +#define VQMCRC_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* CRC.h +* +* DESCRIPTION +* CRC calculation definitions. +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl Calculate_CRC(void const *buffer, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCRC_H */ diff --git a/VQ/INCLUDE/VQM32/FONT.H b/VQ/INCLUDE/VQM32/FONT.H new file mode 100644 index 0000000..f0c039a --- /dev/null +++ b/VQ/INCLUDE/VQM32/FONT.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMFONT_H +#define VQMFONT_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.h +* +* DESCRIPTION +* Font definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +****************************************************************************/ + +/* Font: A Westwood style font. + * + * Size - Size of font. + * CompMethod - Compression method of font. (0 = none) + * NumBlks - Number of data blocks. + * InfoBlk - Offset to font information block. + * OffsetBlk - Offset to character offset block. + * WidthBlk - Offset to character width block. + * DataBlk - Offset to character data block. + * HeightBlk - Offset to character height block. + */ +typedef struct _Font { + unsigned short Size; + unsigned char CompMethod; + unsigned char NumBlks; + unsigned short InfoBlk; + unsigned short OffsetBlk; + unsigned short WidthBlk; + unsigned short DataBlk; + unsigned short HeightBlk; +} Font; + +typedef struct _FontInfo { + long huh; + unsigned char MaxHeight; + unsigned char MaxWidth; +} FontInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void const *FontPtr; +extern int FontXSpacing; +extern int FontYSpacing; + +#ifdef __cplusplus +} +#endif + +extern char FontWidth; +extern char FontHeight; +extern char *FontWidthBlockPtr; + +/* Function prototypes. */ +void *cdecl Load_Font(char const *name); +void *cdecl Set_Font(void const *font); +unsigned short cdecl String_Pixel_Width(char const *string); + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __WATCOMC__ +long cdecl __saveregs Char_Pixel_Width(char chr); +#else +long cdecl Char_Pixel_Width(char chr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VQMFONT_H */ + diff --git a/VQ/INCLUDE/VQM32/GRAPHICS.H b/VQ/INCLUDE/VQM32/GRAPHICS.H new file mode 100644 index 0000000..98bca7d --- /dev/null +++ b/VQ/INCLUDE/VQM32/GRAPHICS.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +#ifndef VQMGRAPHICS_H +#define VQMGRAPHICS_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* graphics.h +* +* DESCRIPTION +* Graphic rendering and manipulation definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 27, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Eor_Region(long sx, long sy, long dx, long dy, long color); +void cdecl Fill_Rect(long x1, long y1, long x2, long y2, long color); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMGRAPHICS_H */ + diff --git a/VQ/INCLUDE/VQM32/HUFFMAN.H b/VQ/INCLUDE/VQM32/HUFFMAN.H new file mode 100644 index 0000000..ebb4b83 --- /dev/null +++ b/VQ/INCLUDE/VQM32/HUFFMAN.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +#ifndef HUFFMAN_H +#define HUFFMAN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffman.h +* +* DESCRIPTION +* Huffman order 0 compress/decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +****************************************************************************/ + + +/* TreeNode: Huffman decoding tree node. + * + * count - Weight of the node in the tree. + * child0 - Child node 0 + * child1 - Child node 1 + */ +typedef struct _TreeNode { + unsigned long count; + unsigned short child0; + unsigned short child1; +} TreeNode; + + +/* HuffCode: This structure is used for storing the code for each symbol + * during encoding. A table of codes for each symbol is built + * from the Huffman tree. + * + * code - Code used to represent a symbol. + * bits - Length of code in bits. + */ +typedef struct _HuffCode { + unsigned short code; + short bits; +} HuffCode; + + +#define HUFF_EOS 256 /* End of stream symbol */ + +/* Prototypes */ +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +long cdecl HuffDecompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +void cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero); + +void cdecl HuffScaleCounts(TreeNode *nodes); +long cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer); +long cdecl BuildHuffTree(TreeNode *nodes); + +void cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node); + +long cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* HUFFMAN_H */ + diff --git a/VQ/INCLUDE/VQM32/IFF.H b/VQ/INCLUDE/VQM32/IFF.H new file mode 100644 index 0000000..c81c7e1 --- /dev/null +++ b/VQ/INCLUDE/VQM32/IFF.H @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + +#ifndef VQMIFF_H +#define VQMIFF_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.h +* +* DESCRIPTION +* IFF (Interchange File Format) manager definitions. +* (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* FormHeader - Structure associated with IFF forms. + * + * id - IFF form id (IE: "FORM") + * size - Length of IFF in bytes + * type - Form type (IE: "ILBM") + */ +typedef struct _FormHeader { + long id; + long size; + long type; +} FormHeader; + +/* Context - Structure associated with chunks. + * + * id - Chunk identifier. + * size - Size of chunk in bytes. + * scan - Bytes read/written. + */ +typedef struct _Context { + long id; + long size; + long scan; +} Context; + +/* IFFHandle - Structure associated with an active IFF read\write session. + * + * fh - DOS filehandle + * flags - Internal flags used by IFF routines. + * form - IFF form information. + * scan - Bytes read/written + * cn - Context of current chunk. + */ +typedef struct _IFFHandle { + long fh; + long flags; + FormHeader form; + long scan; + Context cn; +} IFFHandle; + +/* bit masks for "flags" field. */ +#define IFFB_READ 0 +#define IFFB_WRITE 1 +#define IFFF_READ (1<>24) \ + &0x000000FFL)|(((unsigned long)(id)>>8) \ + &0x0000FF00L)|(((unsigned long)(id)<<8) \ + &0x00FF0000L)|(((unsigned long)(id)<<24)&0xFF000000L)) + +#define REVERSE_WORD(id) ((unsigned short)((((unsigned short)(id)<<8) \ + &0x00FF00)|(((unsigned short)(id)>>8)&0x0FF))) + +#define PADSIZE(size) (((size)+1)&(~1)) + +#ifndef MAKE_ID +#define MAKE_ID(a,b,c,d) ((long)((long)(d)<<24)|((long)(c)<<16)| \ + ((long)(b)<<8)|(long)(a)) +#endif + +/* Universal IFF identifiers */ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_LIST MAKE_ID('L','I','S','T') +#define ID_PROP MAKE_ID('P','R','O','P') +#define ID_NULL MAKE_ID(' ',' ',' ',' ') + +/* Prototypes */ +IFFHandle *OpenIFF(char *, long); +void CloseIFF(IFFHandle *); +long ReadForm(IFFHandle *, FormHeader *); +long WriteForm(IFFHandle *, FormHeader *); +long ReadChunkHeader(IFFHandle *); +long WriteChunkHeader(IFFHandle *, long, long); +long WriteChunk(IFFHandle *, long, char *, long); +long WriteChunkBytes(IFFHandle *, char *, long); +long ReadChunkBytes(IFFHandle *, char *, long); +long SkipChunkBytes(IFFHandle *, long); +long FindChunk(IFFHandle *, long); +char *IDtoStr(long, char *); +long CurrentFilePos(IFFHandle *); + +#endif /* VQMIFF_H */ + diff --git a/VQ/INCLUDE/VQM32/MEM.H b/VQ/INCLUDE/VQM32/MEM.H new file mode 100644 index 0000000..69b0c45 --- /dev/null +++ b/VQ/INCLUDE/VQM32/MEM.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQAMEM_H +#define VQAMEM_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.h +* +* DESCRIPTION +* Memory management definitions. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +/* Definitions */ +#define DPMI_INT 0x0031 +#define DPMI_LOCK 0x0600 +#define DPMI_UNLOCK 0x0601 + +/* Prototypes */ +#ifdef __WATCOMC__ +void DPMI_Lock(void const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +#else +#define DPMI_Lock(a,b) +#define DPMI_Unlock(a,b) +#endif + +#endif /* VQAMEM_H */ diff --git a/VQ/INCLUDE/VQM32/MIXFILE.H b/VQ/INCLUDE/VQM32/MIXFILE.H new file mode 100644 index 0000000..5d6b012 --- /dev/null +++ b/VQ/INCLUDE/VQM32/MIXFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMMIXFILE_H +#define VQMMIXFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.h +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These definitions are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Disable structure alignment.*/ +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* MIXHeader: Mix file data header. + * + * Count - Number of entries contained in this mix file. + * Size - Size of Mix file. + */ +typedef struct _MIXHeader { + short Count; + long Size; +} MIXHeader; + +/* MIXSubBlock: Mix file entry descriptor. + * + * CRC - Unique entry identifier. + * Offset - Offset from beginning of data segment to entry. + * Size - Size of entry. + */ +typedef struct _MIXSubBlock { + long CRC; + long Offset; + long Size; +} MIXSubBlock; + +/* MIXHandle: Mix file handle. + * + * Name - Pointer to the name of the mix file this handle is for. + * Size - Size of entire mix file. + * FH - DOS file handle of opened entry. + * Count - Number of files contained in this mix. + * Entries - Array of 'Count' MIXSubBlock structure entries. + */ +typedef struct _MIXHandle { + char *Name; + long Size; + long FH; + long Count; + MIXSubBlock Entries[]; +} MIXHandle; + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +MIXHandle *OpenMix(char *name); +void CloseMix(MIXHandle *mix); +long OpenMixEntry(MIXHandle *mix, char *name); + +/* Restore original alignment */ +#ifdef __WATCOMC__ +#pragma pack(); +#endif + +#endif /* VQMMIXFILE_H */ + diff --git a/VQ/INCLUDE/VQM32/MONO.H b/VQ/INCLUDE/VQM32/MONO.H new file mode 100644 index 0000000..880055f --- /dev/null +++ b/VQ/INCLUDE/VQM32/MONO.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +#ifndef VQMMONO_H +#define VQMMONO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mono.h +* +* DESCRIPTION +* Mono screen definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Mono_Enable(void); +void cdecl Mono_Disable(void); +void cdecl Mono_Set_Cursor(long x, long y); +void cdecl Mono_Clear_Screen(void); +void cdecl Mono_Scroll(long lines); +void cdecl Mono_Put_Char(long character, long attrib); +void cdecl Mono_Draw_Rect(long x, long y, long w, long h, long attrib, + long thick); + +void cdecl Mono_Text_Print(void const *text, long x, long y, long attrib); +void cdecl Mono_Print(void const *text); +short cdecl Mono_View_Page(long page); +short cdecl Mono_X(void); +short cdecl Mono_Y(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMMONO_H */ diff --git a/VQ/INCLUDE/VQM32/PALETTE.H b/VQ/INCLUDE/VQM32/PALETTE.H new file mode 100644 index 0000000..1bf3abb --- /dev/null +++ b/VQ/INCLUDE/VQM32/PALETTE.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMPALETTE_H +#define VQMPALETTE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Palette.h (32-Bit protected mode) +* +* DESCRIPTION +* Palette definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +void cdecl ReadPalette(void *palette); +void cdecl SetDAC(long color, long red, long green, long blue); +void cdecl TranslatePalette(void *pal24, void *pal15, long numbytes); + +#ifdef __cplusplus +} +#endif + +void SortPalette(unsigned char *pal, long numcolors); + +#endif /* VQMPALETTE_H */ + diff --git a/VQ/INCLUDE/VQM32/PORTIO.H b/VQ/INCLUDE/VQM32/PORTIO.H new file mode 100644 index 0000000..b104412 --- /dev/null +++ b/VQ/INCLUDE/VQM32/PORTIO.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +#ifndef VQMPORTIO_H +#define VQMPORTIO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Portio.h (32-Bit protected mode) +* +* DESCRIPTION +* Hardware port I/O +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifdef __BORLANDC__ + +#ifdef __cplusplus +extern "C" { +#endif + +short cdecl inp(unsigned short portid); +void cdecl outp(unsigned short portid, short value); + +#ifdef __cplusplus +} +#endif + +#endif /* __BORLANDC__ */ + +#endif /* VQMPORTIO_H */ + diff --git a/VQ/INCLUDE/VQM32/PROFILE.H b/VQ/INCLUDE/VQM32/PROFILE.H new file mode 100644 index 0000000..d4641bd --- /dev/null +++ b/VQ/INCLUDE/VQM32/PROFILE.H @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +#ifndef VQMPROFILE_H +#define VQMPROFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Profile.h (32-Bit protected mode) +* +* DESCRIPTION +* INI file profiling definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ +long GetINIInt(char const *section, char const *entry, + long deflt, char *fname); + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname); + +long Get_Frame_Pathname(char *inifile,long anim_frame,char *ext, + char *outbuf); + +#endif /* VQMPROFILE_H */ + diff --git a/VQ/INCLUDE/VQM32/REALMODE.H b/VQ/INCLUDE/VQM32/REALMODE.H new file mode 100644 index 0000000..593151b --- /dev/null +++ b/VQ/INCLUDE/VQM32/REALMODE.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +#ifndef VQMREALMODE_H +#define VQMREALMODE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* realmode.h +* +* DESCRIPTION +* Real-mode interfacing definitions and equates. Many of the definitions +* and descriptions in this file were taken from other sources and +* compiled here for use in MISC32 library. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 6, 1995 +* +****************************************************************************/ + +/* REALPTR: Real-mode pointer (segment:offset16). + * + * The REALPTR data type is used in protected mode to hold real-mode + * pointers. The type is an unsigned long value, were the upper 16 bits + * are the segment number and the lower 16 bit are an offset. This type + * and the associated macros are identical to that of the PHARLAP "pltypes.h" + * definitions for easy of conversion to WATCOM/4GW. + */ +typedef unsigned long REALPTR; + +#define RP_OFF(rp) ((unsigned short)(((unsigned long)(rp)) & 0xFFFF)) +#define RP_SEG(rp) ((unsigned short)(((unsigned long)(rp)) >> 16)) +#define RP_SET(rp, off, seg) (rp = ((unsigned long)(seg) << 16) + (off)) +#define RP_INCR(rp, incr) (rp += ((unsigned long)(incr)) & 0xFFFF) + +#define MK_PTR(off, seg) (void *)((((unsigned long)seg&0xFFFF)<<4)+off) + +/* RMInfo: Real-mode interrupt call structure. + * + * Information that needs to be passed down to the real-mode interrupt is + * transfered using this structure. The address to this protected-mode + * structure (allocated by user) is passed into DPMI function 0x300. DOS/4GW + * will then use this information to set up the real-mode registers, switch + * to real-mode and then execute the interrupt in real-mode. + */ +typedef struct _RMInfo { + long edi; + long esi; + long ebp; + long reservedbysystem; + long ebx; + long edx; + long ecx; + long eax; + short flags; + short es,ds,fs,gs,ip,cs,sp,ss; +} RMInfo; + +#endif /* VQMREALMODE_H */ + diff --git a/VQ/INCLUDE/VQM32/SOSCOMP.H b/VQ/INCLUDE/VQM32/SOSCOMP.H new file mode 100644 index 0000000..1c4e98c --- /dev/null +++ b/VQ/INCLUDE/VQM32/SOSCOMP.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + short wBitSize; + short wChannels; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); +unsigned long cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *,unsigned long); +unsigned long cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *,unsigned long); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/VQ/INCLUDE/VQM32/TARGA.H b/VQ/INCLUDE/VQM32/TARGA.H new file mode 100644 index 0000000..25bafc9 --- /dev/null +++ b/VQ/INCLUDE/VQM32/TARGA.H @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +#ifndef VQMTARGA_H +#define VQMTARGA_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Targa.h (32-Bit protected mode) +* +* DESCRIPTION +* Targa Image File definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * Targa Header definitions + *-------------------------------------------------------------------------*/ + +/* TGAHeader - Targa Image File header. + * + * IDLength - Size of Image ID field + * ColorMapType - Color map type. + * ImageType - Image type code. + * CMapStart - Color map origin. + * CMapLength - Color map length. + * CMapDepth - Depth of color map entries. + * XOffset - X origin of image. + * YOffset - Y origin of image. + * Width - Width of image. + * Height - Height of image. + * PixelDepth - Image pixel size + * ImageDescriptor - Image descriptor byte. + */ +typedef struct _TGAHeader { + char IDLength; + char ColorMapType; + char ImageType; + short CMapStart; + short CMapLength; + char CMapDepth; + short XOffset; + short YOffset; + short Width; + short Height; + char PixelDepth; + char ImageDescriptor; +} TGAHeader; + +/* ImageType definiton */ +#define TGA_NOIMAGE 0 /* No image data included in file */ +#define TGA_CMAPPED 1 /* Color-mapped image data */ +#define TGA_TRUECOLOR 2 /* Truecolor image data */ +#define TGA_MONO 3 /* Monochrome image data */ +#define TGA_CMAPPED_ENCODED 9 /* Color-mapped image data (Encoded) */ +#define TGA_TRUECOLOR_ENCODED 10 /* Truecolor image data (Encoded) */ +#define TGA_MONO_ENCODED 11 /* Monochrome image data (Encoded) */ + +/* ImageDescriptor definition */ +#define TGAF_ATTRIB_BITS (0x0F<<0) /* Number of attribute bits per pixel */ +#define TGAF_XORIGIN (1<<4) +#define TGAF_YORIGIN (1<<5) + +/*--------------------------------------------------------------------------- + * Targa Handle definitions + *-------------------------------------------------------------------------*/ + +/* TGAHandle - Targa Image File handle. + * + * fh - File handle returned by open(). + * mode - Access mode. + * header - TGAHeader structure. + */ +typedef struct _TGAHandle { + short fh; + unsigned short mode; + TGAHeader header; +} TGAHandle; + +/* Access modes. */ +#define TGA_READMODE 0 +#define TGA_WRITEMODE 1 +#define TGA_RDWRMODE 2 + +/* Error codes */ +#define TGAERR_OPEN -1 +#define TGAERR_READ -2 +#define TGAERR_WRITE -3 +#define TGAERR_SYNTAX -4 +#define TGAERR_NOMEM -5 +#define TGAERR_NOTSUPPORTED -6 + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +TGAHandle *OpenTarga(char *, unsigned short); +void CloseTarga(TGAHandle *); +long LoadTarga(char *, char *, char *); +long SaveTarga(char *, TGAHeader *, char *, char *); +void XFlipTarga(TGAHeader *, char *); +void YFlipTarga(TGAHeader *, char *); + +#endif /* VQMTARGA_H */ + diff --git a/VQ/INCLUDE/VQM32/TEXT.H b/VQ/INCLUDE/VQM32/TEXT.H new file mode 100644 index 0000000..2f02dd9 --- /dev/null +++ b/VQ/INCLUDE/VQM32/TEXT.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQMTEXT_H +#define VQMTEXT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* text.h +* +* DESCRIPTION +* Text printing definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 13, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl Text_Print(char *string, long x, long y, long fcol, long bcol); +void cdecl Draw_Char(long character, long x, long y); +void cdecl Set_Font_Palette_Range(void *palette, long start, long end); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMTEXT_H */ + diff --git a/VQ/INCLUDE/VQM32/VESABLIT.H b/VQ/INCLUDE/VQM32/VESABLIT.H new file mode 100644 index 0000000..245d966 --- /dev/null +++ b/VQ/INCLUDE/VQM32/VESABLIT.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +#ifndef VQMVESABLIT_H +#define VQMVESABLIT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESABlit.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA bitblit routines. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Blit_VESA640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +void cdecl Buf_320x200_To_VESA_320x200(unsigned char *buffer, long grain); +void cdecl Buf_320x200_To_VESA_640x400(unsigned char *buffer, long grain); +void cdecl Buf_320x200_To_VESA_32K(unsigned char *buffer, + unsigned char *palette, long grain); + +void cdecl Copy_Row(char *, char *, long); +void cdecl Copy_Word_Row(char *source, char *dest, char *palette, + long numbytes); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVESABLIT_H */ + diff --git a/VQ/INCLUDE/VQM32/VESAVID.H b/VQ/INCLUDE/VQM32/VESAVID.H new file mode 100644 index 0000000..01723dd --- /dev/null +++ b/VQ/INCLUDE/VQM32/VESAVID.H @@ -0,0 +1,182 @@ +/* +** 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 . +*/ + +#ifndef VQMVESAVID_H +#define VQMVESAVID_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESAVid.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifndef __WATCOMC__ +#include +#else +#include "realmode.h" +#endif + +/*--------------------------------------------------------------------------- + * VESA Video Modes + *-------------------------------------------------------------------------*/ + +#define VESA_640X400_256 0x100 +#define VESA_640X480_256 0x101 +#define VESA_800X600_16 0x102 +#define VESA_800X600_256 0x103 +#define VESA_1024X768_16 0x104 +#define VESA_1024X768_256 0x105 +#define VESA_1280X400_16 0x106 +#define VESA_1280X400_256 0x107 +#define VESA_TEXT_80X60 0x108 +#define VESA_TEXT_132X25 0x109 +#define VESA_TEXT_132X60 0x10C +#define VESA_320X200_32K_1 0x10D +#define VESA_320X200_32K_2 0x10E +#define VESA_640X480_32K 0x110 +#define VESA_640X480_65K 0x111 +#define VESA_640X480_16M 0x112 +#define VESA_800X600_32K 0x113 +#define VESA_800X600_65K 0x114 +#define VESA_1024X768_32K 0x116 +#define VESA_1024X768_65K 0x117 + +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_1024X768_65K + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* VESAInfo - General information about this VESA implementation. + * (Filled in by VESA BIOS Function 0) + * + * Signature - Will always be 'VESA' + * Version - Version # + * OEMString - OEM ID string + * Capabilities - Not defined by VESA yet + * AvailModes - List of available modes; terminated with -1 (0xffff) + * TotalMemory - ??? + * Reserved - Pads structure to 256 bytes total + */ +typedef struct _VESAInfo { + char Signature[4]; + unsigned short Version; + REALPTR OEMString; + unsigned long Capabilities; + REALPTR AvailModes; + unsigned short TotalMemory; + unsigned char Reserved[236]; +} VESAInfo; + +/* VESAModeInfo - Information about this VESA mode. + * (Filled in by VESA BIOS Function 1) + * + * Attributes - bit 0: 1 = mode is supported + * bit 1: 1 = optional info available + * bit 2: 1 = std BIOS output functions valid in this mode + * bit 3: 0 = monochrome, 1 = color + * bit 4: 0 = text mode, 1 = graphics + * WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinGranularity - smallest address boundary window can be placed upon; + * size is in KB (ie 64, 32, 4) + * WinSize - size of windows in KB (ie 64, 32) + * WinA_Segment - location of Window A in CPU space (usually 0xa000) + * WinB_Segment - location of Window B in CPU space (usually 0xb000) + * WinFunc - address of window-setting function (This is provided + * as an alternative to Int 10 for speed.) + * BytesPerScanline - # bytes per scan line + * + * Optional info (available if bit 1 of Attributes is set): + * + * XRes - X-resolution + * YRes - Y-resolution + * XCharSize - Horizontal size of char cell + * YCharSize - Vertical size of char cell + * NumPlanes - # of memory planes (???) + * BitsPerPixel - # bites per pixel + * NumBanks - # of banks (ie planes) + * MemoryModel - 00h = Text mode + * 01h = CGA mode + * 02h = Hercules + * 03h = 4 plane planar mode + * 04h = packed pixel mode (1 byte/pixel) + * 05h = non-chain 4, 256-color mode + * 06-0Fh = + * 10-FFh = OEM-specific + * BankSize - Bank size in KB + */ +typedef struct _VESAModeInfo { + unsigned short Attributes; + unsigned char WinA_Attributes; + unsigned char WinB_Attributes; + unsigned short WinGranularity; + unsigned short WinSize; + unsigned short WinA_Segment; + unsigned short WinB_Segment; + REALPTR WinFunc; + unsigned short BytesPerScanline; + unsigned short XRes; + unsigned short YRes; + unsigned char XCharSize; + unsigned char YCharSize; + unsigned char NumPlanes; + unsigned char BitsPerPixel; + unsigned char NumBanks; + unsigned char MemoryModel; + unsigned char BankSize; + unsigned char NumInputPages; + unsigned char Reserved; + unsigned char RedMaskSize; + unsigned char RedFieldPosition; + unsigned char GreenMaskSize; + unsigned char GreenFieldPosition; + unsigned char BlueMaskSize; + unsigned char BlueFieldPosition; + unsigned char RsvdMaskSize; + unsigned char RsvdFieldPosition; + unsigned char DirectColorModeInfo; + unsigned char pad[216]; +} VESAModeInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +long InitVESA(void); +void UninitVESA(void); +VESAModeInfo *SetVESAMode(long mode); +VESAModeInfo *ReadVESAModeInfo(long mode); +void SetVESAWindow(long grain_num); + +#endif /* VQMVESAVID_H */ + diff --git a/VQ/INCLUDE/VQM32/VESAVID.I b/VQ/INCLUDE/VQM32/VESAVID.I new file mode 100644 index 0000000..f305e09 --- /dev/null +++ b/VQ/INCLUDE/VQM32/VESAVID.I @@ -0,0 +1,203 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* vesavid.i +;* +;* DESCRIPTION +;* VESA video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VESA video modes +;---------------------------------------------------------------------------- + +VESA_640X400_256 EQU 0x100 +VESA_640X480_256 EQU 0x101 +VESA_800X600_16 EQU 0x102 +VESA_800X600_256 EQU 0x103 +VESA_1024X768_16 EQU 0x104 +VESA_1024X768_256 EQU 0x105 +VESA_1280X400_16 EQU 0x106 +VESA_1280X400_256 EQU 0x107 +VESA_TEXT_80X60 EQU 0x108 +VESA_TEXT_132X25 EQU 0x109 +VESA_TEXT_132X60 EQU 0x10C +VESA_320X200_32K_1 EQU 0x10D +VESA_320X200_32K_2 EQU 0x10E +VESA_640X480_32K EQU 0x110 +VESA_640X480_65K EQU 0x111 +VESA_640X480_16M EQU 0x112 +VESA_800X600_32K EQU 0x113 +VESA_800X600_65K EQU 0x114 +VESA_1024X768_32K EQU 0x116 +VESA_1024X768_65K EQU 0x117 + +VESA_MIN EQU VESA_640X400_256 +VESA_MAX EQU VESA_1024X768_65K + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; VESAInfo - General information about this VESA implementation. +; (Filled in by VESA BIOS Function 0) +; +; Signature - Will always be 'VESA' +; Ver - Version # +; OEMString - OEM ID string +; Capabilities - Not defined by VESA yet +; AvailModes - List of available modes; terminated with -1 (0xffff) +; TotalMemory - ??? +; Reserved - Pads structure to 256 bytes total + + STRUC VESAInfo +Signature DD ? +Ver DW ? +OEMString DD ? +Capabilities DD ? +AvailModes DD ? +TotalMemory DW ? +Reserved DB 236 DUP (?) + ENDS VESAInfo + +; VESAModeInfo - Information about this VESA mode. +; (Filled in by VESA BIOS Function 1) +; +; Attributes - bit 0: 1 = mode is supported +; bit 1: 1 = optional info available +; bit 2: 1 = std BIOS output functions valid in this mode +; bit 3: 0 = monochrome, 1 = color +; bit 4: 0 = text mode, 1 = graphics +; WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinGranularity - smallest address boundary window can be placed upon; +; size is in KB (ie 64, 32, 4) +; WinSize - size of windows in KB (ie 64, 32) +; WinA_Segment - location of Window A in CPU space (usually 0xa000) +; WinB_Segment - location of Window B in CPU space (usually 0xb000) +; WinFunc - address of window-setting function (This is provided +; as an alternative to Int 10 for speed.) +; BytesPerScanline - # bytes per scan line +; +; Optional info (available if bit 1 of Attributes is set): +; +; XRes - X-resolution +; YRes - Y-resolution +; XCharSize - Horizontal size of char cell +; YCharSize - Vertical size of char cell +; NumPlanes - # of memory planes (???) +; BitsPerPixel - # bites per pixel +; NumBanks - # of banks (ie planes) +; MemoryModel - 00h = Text mode +; 01h = CGA mode +; 02h = Hercules +; 03h = 4 plane planar mode +; 04h = packed pixel mode (1 byte/pixel) +; 05h = non-chain 4, 256-color mode +; 06-0Fh = +; 10-FFh = OEM-specific +; BankSize - Bank size in KB + + STRUC VESAModeInfo +Attributes DW ? +WinA_Attributes DB ? +WinB_Attributes DB ? +WinGranularity DW ? +WinSize DW ? +WinA_Segment DW ? +WinB_Segment DW ? +WinFunc DD ? +BytesPerScanline DW ? +XRes DW ? +YRes DW ? +XCharSize DB ? +YCharSize DB ? +NumPlanes DB ? +BitsPerPixel DB ? +NumBanks DB ? +MemoryModel DB ? +BankSize DB ? +NumInputPages DB ? +Reserved DB ? +RedMaskSize DB ? +RedFieldPosition DB ? +GreenMaskSize DB ? +GreenFieldPosition DB ? +BlueMaskSize DB ? +BlueFieldPosition DB ? +RsvdMaskSize DB ? +RsvdFieldPosition DB ? +DirectColorModeInfo DB ? +pad DB 216 DUP (?) + ENDS VESAModeInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C InitVESA:PROC + GLOBAL C UninitVESA:PROC + GLOBAL C SetVESAMode:PROC + GLOBAL C ReadVESAModeInfo:PROC + GLOBAL C SetVESAWindow:PROC + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WINDOW - Sets window A to the specified bank. +; +; SYNOPSIS +; SET_WINDOW GrainNum +; +; FUNCTION +; This routine uses the C Set_Window function rather than going through +; the BIOS, for two reasons: (1) Speed, and (2) On the Cirrus Logic 54xx +; VESA cards, BIOS calls make noise while playing digital audio. +; +; INPUTS +; GrainNum - Granularity number. +; +; RESULT +; NONE +; +;---------------------------------------------------------------------------- + + MACRO SET_WINDOW grain_num + push esi + push edi + push es + call SetVESAWindow C,grain_num + pop es + pop edi + pop esi + ENDM + diff --git a/VQ/INCLUDE/VQM32/VGA.I b/VQ/INCLUDE/VQM32/VGA.I new file mode 100644 index 0000000..f04d9a0 --- /dev/null +++ b/VQ/INCLUDE/VQM32/VGA.I @@ -0,0 +1,217 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vga.i +;* +;* DESCRIPTION +;* VGA hardware definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VGA Registers +;---------------------------------------------------------------------------- + +R_SEQUENCER EQU 03C4h ;Sequencer Controller Index reg +SEQ_RESET EQU 00h ;Reset +SEQ_MAP_MASK EQU 02h ;Index in Sequencer of Map Mask reg +SEQ_MEMORY_MODE EQU 04h ;Memory Mode + +R_GRAPHICS_CONTROLLER EQU 03CEh ;Graphics Controller Index reg +GC_READ_MAP EQU 04h ;Index in GController of Read Map reg +GC_MODE EQU 05h ;Read/Write Modes +GC_MISC EQU 06h ;Read/Write Modes +GC_BITMASK EQU 08h ;Index in GController of BitMask reg + +R_CRT_CONTROLLER EQU 03D4h ;CRT Controller Index reg +CRT_VERT_TOTAL EQU 06h ;Vertical total +CRT_OVERFLOW EQU 07h ;Overflow +CRT_MAX_SCANLINE EQU 09h ;Max scan line +CRT_STARTADDR_HIGH EQU 0Ch ;Bitmap start address high byte +CRT_STARTADDR_LOW EQU 0Dh ;Bitmap start address low byte +CRT_VERTRET_START EQU 010h ;Vertical retrace pulse start +CRT_VERTRET_END EQU 011h ;Vertical retrace pulse end +CRT_VERTDISP_END EQU 012h ;Vertical display end +CRT_UNDERLINE EQU 014h ;Underline location +CRT_START_VB EQU 015h ;Start vertical blank +CRT_END_VB EQU 016h ;End vertical blank +CRT_MODE_CONTROL EQU 017h ;Mode control +R_MISC_OUTPUT EQU 03C2h ;Miscellaneous Output reg + +;---------------------------------------------------------------------------- +; Palette Registers +;---------------------------------------------------------------------------- + +PEL_READ_ADDR EQU 03C7h +PEL_WRITE_ADDR EQU 03C8h +PEL_DATA EQU 03C9h + +;---------------------------------------------------------------------------- +; XMode planes, for the Map Mask register +;---------------------------------------------------------------------------- + +XPLANE_1 EQU 1 +XPLANE_2 EQU 2 +XPLANE_3 EQU 4 +XPLANE_4 EQU 8 + +;---------------------------------------------------------------------------- +; +; NAME +; SET_PLANE - Set an XMode plane. +; +; SYNOPSIS +; SET_PLANE plane +; +; INPUTS +; plane - Number of Xmode plane to set. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_PLANE plane + mov edx,R_SEQUENCER + mov eax,SEQ_MAP_MASK + out dx,al + inc edx + mov eax,plane + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_BITMASK - Set the BitMask register. +; +; SYNOPSIS +; SET_BITMASK mask +; +; INPUTS +; mask - Bitmask to use. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_BITMASK mask + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_BITMASK + out dx,al + inc edx + mov eax,mask + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WRITEMODE - Set the VGA writemode. +; +; SYNOPSIS +; SET_WRITEMODE mode +; +; INPUTS +; mode - Write mode. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_WRITEMODE mode + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_MODE + out dx,al + inc edx + in al,dx ;Read the register + and al,0FCh ;Turn off 2 lower bits + or al,mode ;Set write mode + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; OUTPORT - Output data to a VGA register. +; +; SYNOPSIS +; OUTPORT port,register,data +; +; INPUTS +; port - Port address. +; register - Register to write to. +; data - Data to write. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO OUTPORT port,register,data + mov edx,port + mov al,register + out dx,al + inc edx + mov al,data + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; INPORT - Input data from a VGA register. +; +; SYNOPSIS +; data = INPORT port,register +; +; INPUTS +; port - Port address. +; register - Register to read from. +; +; RESULT +; data - Value read from port in AL. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO INPORT port,register + mov edx,port + mov al,register + out dx,al + inc edx + in al,dx + ENDM + diff --git a/VQ/INCLUDE/VQM32/VIDEO.H b/VQ/INCLUDE/VQM32/VIDEO.H new file mode 100644 index 0000000..c54bb58 --- /dev/null +++ b/VQ/INCLUDE/VQM32/VIDEO.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +#ifndef VQMVIDEO_H +#define VQMVIDEO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Video.h (32-Bit protected mode) +* +* DESCRIPTION +* Video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * VGA video modes + *-------------------------------------------------------------------------*/ + +#define TEXT 0x02 +#define MCGA 0x13 +#define XMODE_320X200 0x50 +#define XMODE_320X240 0x51 +#define XMODE_320X400 0x52 +#define XMODE_320X480 0x53 +#define XMODE_360X400 0x54 +#define XMODE_360X480 0x55 + +#define XMODE_MIN 0x50 +#define XMODE_MAX 0x55 + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* DisplayInfo - Information about the current display. + * + * Mode - Mode identification. + * XRes - X resolution of mode. + * YRes - Y resolution of mode. + * VBIbit - Polarity of vertical blank bit. + * Extended - Pointer to mode specific data structure. + */ +typedef struct _DisplayInfo { + long Mode; + long XRes; + long YRes; + long VBIbit; + void *Extended; +} DisplayInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +DisplayInfo *SetVideoMode(long mode); +DisplayInfo *GetDisplayInfo(void); +long TestVBIBit(void); +long GetVBIBit(void); + +void SetupXPaging(void); +void FlipXPage(void); +unsigned char *GetXHidPage(void); +unsigned char *GetXSeenPage(void); +void DisplayXPage(long page); + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl WaitNoVB(short vbibit); +void cdecl WaitVB(short vbibit); +void cdecl ClearVRAM(void); +long cdecl SetXMode(long mode); +void cdecl ClearXMode(void); +void cdecl ShowXPage(unsigned long StartOffset); +void cdecl Xmode_BufferCopy_320x200(void *buff, void *screen); +void cdecl Xmode_Blit(void *buffer, void *screen, long imgwidth, long imgheight); +void cdecl MCGA_BufferCopy(unsigned char *buffer, unsigned char *dummy); +void cdecl MCGA_Blit(unsigned char *buffer, unsigned char *screen, + long imgwidth, long imgheight); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVIDEO_H */ + diff --git a/VQ/INCLUDE/VQM32/VIDEO.I b/VQ/INCLUDE/VQM32/VIDEO.I new file mode 100644 index 0000000..d2fc384 --- /dev/null +++ b/VQ/INCLUDE/VQM32/VIDEO.I @@ -0,0 +1,80 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* video.i +;* +;* DESCRIPTION +;* Video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + + INCLUDE "vesavid.i" + +;---------------------------------------------------------------------------- +; Video Modes +;---------------------------------------------------------------------------- + +TEXT EQU 002h +MCGA EQU 013h +XMODE_320X200 EQU 050h +XMODE_320X240 EQU 051h +XMODE_320X400 EQU 052h +XMODE_320X480 EQU 053h +XMODE_360X400 EQU 054h +XMODE_360X480 EQU 055h + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; DisplayInfo - Information about the current display. +; +; Mode - Mode identification +; XRes - X resolution +; YRes - Y resolution +; VBIbit - Polarity of vertical blank bit. +; Extended - Pointer to mode specified data structure. + + STRUC DisplayInfo +Mode DD ? +XRes DD ? +YRes DD ? +VBIbit DD ? +Extended DD ? + ENDS DisplayInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C GetDisplayInfo:NEAR + GLOBAL C GetVBIBit:NEAR + diff --git a/VQ/INCLUDE/WAVEFILE.H b/VQ/INCLUDE/WAVEFILE.H new file mode 100644 index 0000000..aad2eb9 --- /dev/null +++ b/VQ/INCLUDE/WAVEFILE.H @@ -0,0 +1,73 @@ +/* +** 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 . +*/ + +#ifndef WAVEFILE_H +#define WAVEFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* wavefile.c +* +* DESCRIPTION +* WAVE file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* February 21, 1995 +* +****************************************************************************/ + +/* WAVHeader: WAVE file header. + * + * RIFF - 4 byte identifier (always "RIFF"). + * Size - Size in bytes of entire file. + * WAVE - 4 byte identifier (always "WAVE"). + */ +typedef struct _WAVHeader { + long RIFF; + long Size; + long WAVE; +} WAVHeader; + +/* WAVFormat: This structure describes the format of the audio data contained + * in the WAV file. + * + * FormatTag - + * Channels - Number of channels (1 = mono, 2 = stereo). + * SamplesPerSec - Sampling rate. + * AvgBytesPerSec - Bytes in 1 second of audio (Rate * SampleSize * Channels) + * BlockAlign - padding. + * BitsPerSample - Number of bits per sample (8, 16). + */ +typedef struct _WAVFormat { + short FormatTag; + short Channels; + long SamplesPerSec; + long AvgBytesPerSec; + short BlockAlign; + short BitsPerSample; +} WAVFormat; + +#endif /* WAVEFILE_H */ + diff --git a/VQ/INCLUDE/WWLIB32/AUDIO.H b/VQ/INCLUDE/WWLIB32/AUDIO.H new file mode 100644 index 0000000..2a56858 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/AUDIO.H @@ -0,0 +1,143 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename); +int File_Stream_Sample_Vol(char const *filename, int volume); +void cdecl _saveregs Sound_Callback(void); +void cdecl far __saveregs __loadds maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Sound_Init(int sfx, int score, int sample, int reverse_channels = FALSE); +BOOL Audio_Init(int sample, int address, int inter, int dma, int reverse_channels = FALSE); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); + +extern unsigned int DigiHandle; +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; diff --git a/VQ/INCLUDE/WWLIB32/BUFFER.H b/VQ/INCLUDE/WWLIB32/BUFFER.H new file mode 100644 index 0000000..bc3c188 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/BUFFER.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class VideoViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + long To_Page(VideoViewPortClass &view); + long To_Page(int w, int h, VideoViewPortClass &view); + long To_Page(int x, int y, int w, int h, VideoViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif + diff --git a/VQ/INCLUDE/WWLIB32/DESCMGMT.H b/VQ/INCLUDE/WWLIB32/DESCMGMT.H new file mode 100644 index 0000000..83f34ca --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/DESCMGMT.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/VQ/INCLUDE/WWLIB32/DIPTHONG.H b/VQ/INCLUDE/WWLIB32/DIPTHONG.H new file mode 100644 index 0000000..2f5ed70 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/DIPTHONG.H @@ -0,0 +1,24 @@ +/* +** 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 . +*/ + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/VQ/INCLUDE/WWLIB32/FILE.H b/VQ/INCLUDE/WWLIB32/FILE.H new file mode 100644 index 0000000..2488a72 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/FILE.H @@ -0,0 +1,256 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern short Find_First(unsigned char *fname, unsigned short mode, struct find_t *ffblk); +extern short Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif // FILE_H + diff --git a/VQ/INCLUDE/WWLIB32/FILETEMP.H b/VQ/INCLUDE/WWLIB32/FILETEMP.H new file mode 100644 index 0000000..210f85a --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/FILETEMP.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD cdecl (*cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H diff --git a/VQ/INCLUDE/WWLIB32/FONT.H b/VQ/INCLUDE/WWLIB32/FONT.H new file mode 100644 index 0000000..d663a63 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/FONT.H @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef VBUFFER_H +#include +#endif + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Set_Font(VOID const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +WORD cdecl Char_Pixel_Width(BYTE chr); +UWORD cdecl String_Pixel_Width(BYTE const *string); +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y); +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Load_Font(BYTE const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID Set_Font_Palette_Range(VOID *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern BYTE FontWidth ; +extern BYTE FontHeight; +extern BYTE *FontWidthBlockPtr; + + +extern "C" VOID const *FontPtr; + + + + +#endif // FONT_H diff --git a/VQ/INCLUDE/WWLIB32/FUNCTION.H b/VQ/INCLUDE/WWLIB32/FUNCTION.H new file mode 100644 index 0000000..7edf5f2 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int cdecl File_Stream_Sample(char const *filename); +int cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void cdecl _saveregs _loadds Sound_Callback(void); +void cdecl far _saveregs _loadds maintenance_callback(void); +void *cdecl Load_Sample(char const *filename); +long cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long cdecl Sample_Read(int fh, void *buffer, long size); +void cdecl Free_Sample(void const *sample); +BOOL cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void cdecl Sound_End(void); +void cdecl Stop_Sample(int handle); +BOOL cdecl Sample_Status(int handle); +BOOL cdecl Is_Sample_Playing(void const * sample); +void cdecl Stop_Sample_Playing(void const * sample); +int cdecl Play_Sample(void const *sample); +int cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int cdecl Set_Sound_Vol(int volume); +int cdecl Set_Score_Vol(int volume); +void cdecl Fade_Sample(int handle, int ticks); diff --git a/VQ/INCLUDE/WWLIB32/GBUFFER.H b/VQ/INCLUDE/WWLIB32/GBUFFER.H new file mode 100644 index 0000000..f16e346 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/GBUFFER.H @@ -0,0 +1,866 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MCGAPRIM_H +#include "mcgaprim.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#include + + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr); +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + VOID Grey_Out_Region(int x, int y, int width, int height, int color); + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + GraphicBufferClass *GraphicBuff; // related graphic buff +}; + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + public: + GraphicBufferClass( long size = 64500, int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT, + VOID *buffer = 0); + GraphicBufferClass(int w, int h, void *buffer = 0); + ~GraphicBufferClass(); +}; + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return MCGA_Size_Of_Region(this, w, h); +} + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + MCGA_Put_Pixel(this, x, y, color); +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + return(MCGA_Get_Pixel(this, x, y)); +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + MCGA_Clear(this, color); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + return(MCGA_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(MCGA_Print(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + return(MCGA_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + MCGA_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); +} + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Draw_Line(this, sx, sy, dx, dy, color); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Fill_Rect(this, sx, sy, dx, dy, color); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + MCGA_Remap(this, sx, sy, width, height, remap); +} +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + MCGA_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + MCGA_Remap(this, 0, 0, Width, Height, remap); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + + +#endif diff --git a/VQ/INCLUDE/WWLIB32/GBUFFER.INC b/VQ/INCLUDE/WWLIB32/GBUFFER.INC new file mode 100644 index 0000000..457af5e --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/GBUFFER.INC @@ -0,0 +1,60 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + +STRUC VideoViewPort +VIVPOffset DD ? ; offset to virtual viewport +VIVPWidth DD ? ; width of virtual viewport +VIVPHeight DD ? ; height of virtual viewport +VIVPXAdd DD ? ; x mod to get to next line +VIVPXPos DD ? ; x pos relative to Graphic Buff +VIVPYPos DD ? ; y pos relative to Graphic Buff +VIVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS diff --git a/VQ/INCLUDE/WWLIB32/IFF.H b/VQ/INCLUDE/WWLIB32/IFF.H new file mode 100644 index 0000000..d80a8bb --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/IFF.H @@ -0,0 +1,168 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAG_H +#include // This is needed for MemoryFlagType. +#endif + +#ifndef GBUFFER_H +#include +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((LONG) ((LONG) d << 24) | ((LONG) c << 16) | ((LONG) b << 8) | (LONG)(a)) +#define IFFize_WORD(a) Reverse_WORD(a) +#define IFFize_LONG(a) Reverse_LONG(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + BYTE Method; // Compression method (CompressionType). + BYTE pad; // Reserved pad byte (always 0). + LONG Size; // Size of the uncompressed data. + WORD Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +WORD cdecl Open_Iff_File(BYTE const *filename); +VOID cdecl Close_Iff_File(WORD fh); +ULONG cdecl Get_Iff_Chunk_Size(WORD fh, LONG id); +ULONG cdecl Read_Iff_Chunk(WORD fh, LONG id, VOID *buffer, ULONG maxsize); +VOID cdecl Write_Iff_Chunk(WORD file, LONG id, VOID *buffer, LONG length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +ULONG cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size); +ULONG cdecl Write_Data(BYTE const *name, VOID *ptr, ULONG size); +VOID * cdecl Load_Alloc_Data(BYTE const *name, MemoryFlagType flags); +ULONG cdecl Load_Uncompress(BYTE const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data=NULL); +ULONG cdecl Uncompress_Data(VOID const *src, VOID *dst); +VOID cdecl Set_Uncomp_Buffer(WORD buffer_segment, UWORD size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern VOID Pack_2_Plane(VOID *buffer, VOID * pageptr, WORD planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Compress(VOID *source, VOID *dest, ULONG length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Uncompress(VOID *source, VOID *dest, ULONG length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H + diff --git a/VQ/INCLUDE/WWLIB32/KEYBOARD.H b/VQ/INCLUDE/WWLIB32/KEYBOARD.H new file mode 100644 index 0000000..a5ba15f --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/KEYBOARD.H @@ -0,0 +1,445 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : July 13, 1994 * + * * + * Last Update : July 13, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +extern "C" { + void Install_Keyboard_Interrupt(void *addr, long size); + void *Get_RM_Keyboard_Address(void); + long Get_RM_Keyboard_Size(void); + void Remove_Keyboard_Interrupt(void); + int Check_Key_Num(void); + int Check_Key_Bits(void); + int Check_Key(void); + short Get_Key_Num(void); + short Get_Key_Bits(void); + int Get_Key(void); + int KN_To_KA(int keynum); + int Keyboard_Attributes_On(int key_flags); + int Keyboard_Attributes_Off(int key_flags); + void Clear_KeyBuffer(void); + int Key_Down(int key); + int Key_Satisfied(int key); + void Stuff_Key_WORD(int code); + void Stuff_Key_Num(int key); + void Install_Mouse(int max_width, int max_height, int scr_width, int scr_height); + void Reset_Mouse (void) ; + void Remove_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + int Get_Mouse_Disabled(void); + void *Set_Mouse_Cursor(int xhotspot, int yhotspot, void *cursor); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int sx_pixel, int sy_pixel, int dx_pixel, int dy_pixel); + void Conditional_Show_Mouse(void); + + + void __interrupt far Keyboard_Interrupt(void); + extern int MouseQX; + extern int MouseQY; + +} + + +enum { + REPEATON = 0x0001, /* 1:all makes into buffer, 0:only 1st make */ + TRACKEXT = 0x0002, /* 1:Home != keypad Home, 0:Home=keypad Home */ + FILTERONLY = 0x0004, /* 1:Normal BIOS operation with filter */ + CTRLALTTURBO = 0x0010, /* 1:Allow turbo up and down in application */ + NONUMLOCK = 0x0200, /* 1:do NOT remap keypad to numbers */ + TASKSWITCHABLE = 0x400, /* 1:allows task switching keys thru ALT-TAB, */ + /* ALT-ESC,CTRL-ESC */ + PASSBREAKS = 0x0800, // Pass all breaks to the keyboard buffer. + + /* The following flags, if turned on, should only be used for + debugging purposes (remember to take out the calls when BETA */ + + CTRLSON = 0x0008, /* 1:pass scroll lock sequence into BIOS */ + CTRLCON = 0x0020, /* 1:pass stop code to BIOS */ + SCROLLLOCKON = 0x0040, /* 1:pass scroll lock key into BIOS */ + PAUSEON = 0x0080, /* 1:pass the pause key and seq to BIOS */ + /* make sure FILTERONLY is set */ + BREAKON = 0x0100, /* 1:pass the ctrl break seq to BIOS */ + KEYMOUSE = 0x1000, /* 1:keypad translates into mouse moves */ + SIMLBUTTON = 0x2000 /* 1:have space and enter keys simulate Left */ + /* mouse button when KEYMOUSE is set */ +}; + + +/* +** These are the codes for the various key codes that are returned from the +** keyboard input routines Get_Key() and Input_ASCII(). +*/ +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KeyASCIIType; + +/* +** These are the keyboard codes that are returned from the input routines +** Get_Key_Num and Input_Num. +*/ +typedef enum { + KN_NONE = 0, + KN_GRAVE = 1, /* ` */ + KN_1, KN_2, KN_3, KN_4, KN_5, KN_6, KN_7, KN_8, KN_9, KN_0, + KN_MINUS, /* - */ + KN_EQUAL, /* = */ + + KN_RESERVED1, + + KN_BACKSPACE, /* */ + + KN_TAB, /* */ + KN_Q, KN_W, KN_E, KN_R, KN_T, KN_Y, KN_U, KN_I, KN_O, KN_P, + KN_LBRACKET, /* [ */ + KN_RBRACKET, /* ] */ + KN_BACKSLASH, /* \ */ + + KN_CAPSLOCK, /* */ + KN_A, KN_S, KN_D, KN_F, KN_G, KN_H, KN_J, KN_K, KN_L, + KN_SEMICOLON, /* ; */ + KN_SQUOTE, /* ' */ + KN_BACKSLASH2, + KN_RETURN, /* or */ + + KN_LSHIFT, /* */ + + KN_MOUSE_MOVE, // Indicate a mouse move (for playback of + + KN_Z, KN_X, KN_C, KN_V, KN_B, KN_N, KN_M, + KN_COMMA, /* , */ + KN_PERIOD, /* . */ + KN_SLASH, /* / */ + + KN_RESERVED3, + + KN_RSHIFT, /* */ + + KN_LCTRL, /* */ + KN_LCOMM, /* for AMIGA */ + KN_LALT, /* */ + KN_SPACE, /* */ + KN_RALT, /* */ + KN_RCOMM, /* for AMIGA */ + KN_RCTRL, /* */ + /* the following are forced into key buffer */ + KN_LMOUSE, + KN_RMOUSE, + KN_JBUTTON1, + KN_JBUTTON2, + KN_J_UP, + KN_J_RIGHT, + KN_J_DOWN, + KN_J_LEFT, + + KN_SPECIAL9, + + KN_SPECIAL10, + + KN_E_INSERT, /* extended */ + KN_E_DELETE, /* extended */ + + KN_RESERVED4, + KN_RESERVED5, + + KN_E_LEFT, /* extended */ + KN_E_HOME, /* extended */ + KN_E_END, /* extended */ + + KN_RESERVED6, + + KN_E_UP, /* extended */ + KN_E_DOWN, /* extended */ + KN_E_PGUP, /* extended */ + KN_E_PGDN, /* extended */ + KN_K_LPAREN, /* for AMIGA */ + KN_K_RPAREN, /* for AMIGA */ + KN_E_RIGHT, /* extended */ + + KN_NUMLOCK, /* */ + KN_HOME, /* num key pad 7 */ + KN_UPLEFT = KN_HOME, + KN_LEFT, /* num key pad 4 */ + KN_END, /* num key pad 1 */ + KN_DOWNLEFT = KN_END, + + KN_RESERVED7, + + KN_KEYPAD_SLASH, /* num key pad / */ + KN_UP, /* num key pad 8 */ + KN_CENTER, /* num key pad 5 */ + KN_DOWN, /* num key pad 2 */ + KN_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK, /* num key pad * */ + KN_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KN_PGUP, + KN_RIGHT, /* num key pad 6 */ + KN_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KN_PGDN, + KN_DELETE, /* num key pad . */ + KN_KEYPAD_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS, /* num key pad + */ + + KN_RESERVED8, + + KN_KEYPAD_RETURN, /* num key pad */ + + KN_RESERVED9, + + KN_ESC, + KN_HELP, /* for AMIGA */ + KN_F1, KN_F2, KN_F3, KN_F4, KN_F5, KN_F6, KN_F7, KN_F8, KN_F9, KN_F10, + KN_F11, + KN_F12, + KN_PRNTSCRN, /* */ + KN_SCROLLLOCK, /* */ + KN_PAUSE, /* */ + + KN_SHIFT_BIT = 0x0100, + KN_CTRL_BIT = 0x0200, + KN_ALT_BIT = 0x0400, + KN_RLSE_BIT = 0x0800, + KN_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KN_RCOMM_BIT = 0x2000, /* Amiga Right Comm key */ + KN_BUTTON = 0x8000 /* Amiga Right Comm key */ +} KeyNumType; + +#endif diff --git a/VQ/INCLUDE/WWLIB32/KEYBOARD.INC b/VQ/INCLUDE/WWLIB32/KEYBOARD.INC new file mode 100644 index 0000000..960ab3a --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left + ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/VQ/INCLUDE/WWLIB32/KEYSTRUC.INC b/VQ/INCLUDE/WWLIB32/KEYSTRUC.INC new file mode 100644 index 0000000..159fe66 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/KEYSTRUC.INC @@ -0,0 +1,161 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. diff --git a/VQ/INCLUDE/WWLIB32/MCGAPRIM.H b/VQ/INCLUDE/WWLIB32/MCGAPRIM.H new file mode 100644 index 0000000..483eac4 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/MCGAPRIM.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + + + +#ifndef MCGAPRIM_H +#define MCGAPRIM_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + extern long MCGA_Size_Of_Region(void *thisptr, int w, int h); + + extern void MCGA_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int MCGA_Get_Pixel(void * thisptr, int x, int y); + extern void MCGA_Clear(void *thisptr, unsigned char color); + extern long MCGA_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long MCGA_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + extern BOOL Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + extern void Vesa_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int Vesa_Get_Pixel(void * thisptr, int x, int y); + extern void Vesa_Clear(void *thisptr, unsigned char color); + extern long Vesa_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long Vesa_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + + extern BOOL Linear_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern LONG MCGA_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + extern LONG Vesa_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + extern VOID MCGA_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + extern VOID MCGA_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + extern void MCGA_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + + extern void Shadow_Blit(long int xpix, long int ypix, long int width, long int height, GraphicViewPortClass &src, VideoBufferClass &dst, void *shadowbuff); + +// extern int Get_Standard_Selector(VOID); +// extern VOID Set_Selector(UWORD sel); +#ifdef __cplusplus +} +#endif + +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern void (*VVPC_Clear_Func)(void *, unsigned char); +extern long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +extern void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +extern int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +extern long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *buffer, void *view); +extern BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern LONG (*VVPC_Print_Func)(void *, const char *, int, int, int, int); +extern GraphicBufferClass *LogicPage; +#endif diff --git a/VQ/INCLUDE/WWLIB32/MCGAPRIM.INC b/VQ/INCLUDE/WWLIB32/MCGAPRIM.INC new file mode 100644 index 0000000..4bbd349 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/MCGAPRIM.INC @@ -0,0 +1,122 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + \ No newline at end of file diff --git a/VQ/INCLUDE/WWLIB32/MEMFLAG.H b/VQ/INCLUDE/WWLIB32/MEMFLAG.H new file mode 100644 index 0000000..f6670ec --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/MEMFLAG.H @@ -0,0 +1,102 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void *pointer); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/VQ/INCLUDE/WWLIB32/MISC.H b/VQ/INCLUDE/WWLIB32/MISC.H new file mode 100644 index 0000000..78a6b61 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/MISC.H @@ -0,0 +1,171 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +/*========================= C++ Routines ==================================*/ + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID cdecl Prog_End(VOID); +VOID cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +UBYTE Random(VOID); +int Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +LONG Reverse_LONG(LONG number); +WORD Reverse_WORD(WORD number); +LONG Swap_LONG(LONG number); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ + +void *Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); + +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD Processor(VOID); +extern WORD Operating_System(VOID); +extern ULONG random ( ULONG mod ) ; +extern void randomize ( void ) ; + +extern int Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + + diff --git a/VQ/INCLUDE/WWLIB32/MONO.H b/VQ/INCLUDE/WWLIB32/MONO.H new file mode 100644 index 0000000..b028af1 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/MONO.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Mono Sub-system * + * * + * File Name : MONO.H * + * * + * Programmer : Joe Bostic * + * 32Bit Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef MONO_H +#define MONO_H + +// C++ Routines +//================================================================== + +// Mono Screen routines +//================================================================== +int Initialize_Mono_Screen ( void ); + + +// C Routines +//================================================================== +#ifdef __cplusplus +extern "C" { +#endif + +// Mono Screen routines +//=================================================================== + extern unsigned MonoScreen; + extern unsigned MonoEnabled; + + extern void Mono_Set_Cursor(int x, int y); + extern void Mono_Clear_Screen(void); + extern void Mono_Scroll(int lines); + extern void Mono_Put_Char(char character, int attrib=2); + extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + extern void Mono_Text_Print(void const *text, int x, int y, int attrib=2); + extern void Mono_Print(void const *text); + extern void Mono_View_Page(int page); + extern int Mono_Printf(char const *string, ...); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/VQ/INCLUDE/WWLIB32/PALETTE.H b/VQ/INCLUDE/WWLIB32/PALETTE.H new file mode 100644 index 0000000..748c840 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/PALETTE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +VOID cdecl Set_Palette(VOID *palette); +VOID cdecl Set_Palette_Color(VOID *palette, WORD color, VOID *data); +VOID Fade_Palette_To(VOID *palette1, UWORD delay, VOID (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +VOID cdecl Load_Palette(BYTE *palette_file_name, VOID *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +VOID cdecl Morph_Palette (VOID *src_palette, VOID *dst_palette, UWORD delay, + VOID *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Set_Palette_Range(VOID *palette); +extern BOOL Bump_Color(VOID *palette, WORD changable, WORD target); + +#ifdef __cplusplus +} +#endif +extern "C" extern UBYTE CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ + diff --git a/VQ/INCLUDE/WWLIB32/PLAYCD.H b/VQ/INCLUDE/WWLIB32/PLAYCD.H new file mode 100644 index 0000000..97c6561 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/PLAYCD.H @@ -0,0 +1,249 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +class GetCDClass { + +protected: + + union REGS regs; + struct SREGS sregs; + + SEGSEL cdDrive_addrp; + UWORD largestp; + UBYTE cdDrive[26]; // CD Drive letters from MSCDEX + +public: + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + WORD GetCDDrive(VOID); + +}; + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + + +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/VQ/INCLUDE/WWLIB32/SHAPE.H b/VQ/INCLUDE/WWLIB32/SHAPE.H new file mode 100644 index 0000000..daadf3c --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SHAPE.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_FADING = 0x0100, // Fading effect (VOID * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (VOID * color_table) +} ShapeFlags_Type; + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + UWORD ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + UBYTE Height; // Height of the shape in scan lines + UWORD Width; // Width of the shape in bytes + UBYTE OriginalHeight; // Original height of shape in scan lines + UWORD ShapeSize; // Size of the shape, including header + UWORD DataLength; // Size of the uncompressed shape (just data) + UBYTE Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +typedef struct { + UWORD NumShapes; // number of shapes in the block + LONG Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern VOID *MaskPage; +extern VOID *BackGroundPage; +extern LONG _ShapeBufferSize; +extern BYTE *_ShapeBuffer; +} + + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +WORD Draw_Shape(GraphicViewPortClass *gvp, VOID const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short cdecl Get_Shape_Data(VOID const *shape, WORD data); +int cdecl Extract_Shape_Count(VOID const *buffer); +void * cdecl Extract_Shape(VOID const *buffer, int shape); +int cdecl Restore_Shape_Height(VOID *shape); +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight); + +extern "C" { +int Get_Shape_Width(VOID const *shape); +int Get_Shape_Height(VOID const *shape); +int Get_Shape_Original_Height(VOID const *shape); +int Get_Shape_Uncomp_Size(VOID const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +VOID Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +WORD cdecl Get_Shape_Flags(VOID const *shape); +int cdecl Get_Shape_Size(VOID const *shape); +int cdecl Get_Shape_Scaled_Width(VOID const *shape, WORD scale); +int cdecl Get_Shape_Scaled_Height(VOID const *shape, WORD scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + diff --git a/VQ/INCLUDE/WWLIB32/SHAPE.INC b/VQ/INCLUDE/WWLIB32/SHAPE.INC new file mode 100644 index 0000000..e6a4042 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SHAPE.INC @@ -0,0 +1,214 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD +GLOBAL _MaskPage:DWORD +GLOBAL _BackGroundPage:DWORD +GLOBAL PredCount:DWORD +GLOBAL PredTable:BYTE +GLOBAL PredValue:DWORD +GLOBAL PartialPred:DWORD +GLOBAL PartialCount:DWORD +GLOBAL Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/VQ/INCLUDE/WWLIB32/SOS.H b/VQ/INCLUDE/WWLIB32/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/VQ/INCLUDE/WWLIB32/SOSCOMP.H b/VQ/INCLUDE/WWLIB32/SOSCOMP.H new file mode 100644 index 0000000..7334764 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif + diff --git a/VQ/INCLUDE/WWLIB32/SOSDATA.H b/VQ/INCLUDE/WWLIB32/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/VQ/INCLUDE/WWLIB32/SOSDEFS.H b/VQ/INCLUDE/WWLIB32/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/VQ/INCLUDE/WWLIB32/SOSFNCT.H b/VQ/INCLUDE/WWLIB32/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/VQ/INCLUDE/WWLIB32/SOSRES.H b/VQ/INCLUDE/WWLIB32/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/VQ/INCLUDE/WWLIB32/SOUND.H b/VQ/INCLUDE/WWLIB32/SOUND.H new file mode 100644 index 0000000..81c6cee --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SOUND.H @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 4 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + + +extern WORD _SoundVolume; +extern WORD _ScoreVolume; + +#endif diff --git a/VQ/INCLUDE/WWLIB32/STAMP.INC b/VQ/INCLUDE/WWLIB32/STAMP.INC new file mode 100644 index 0000000..6679814 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/STAMP.INC @@ -0,0 +1,35 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +ICON256 EQU 1 + \ No newline at end of file diff --git a/VQ/INCLUDE/WWLIB32/SVGAPRIM.INC b/VQ/INCLUDE/WWLIB32/SVGAPRIM.INC new file mode 100644 index 0000000..2651d21 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/SVGAPRIM.INC @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/VQ/INCLUDE/WWLIB32/TILE.H b/VQ/INCLUDE/WWLIB32/TILE.H new file mode 100644 index 0000000..a5f72c0 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/TILE.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + +#endif //TILE_H + + \ No newline at end of file diff --git a/VQ/INCLUDE/WWLIB32/TIMER.H b/VQ/INCLUDE/WWLIB32/TIMER.H new file mode 100644 index 0000000..9aa2860 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/TIMER.H @@ -0,0 +1,151 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: + long (*Get_Ticks)(void); // System timer fetch. +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// + +extern TimerClass TickCount; +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); + void far Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); + + +#endif // TIMER_H + + diff --git a/VQ/INCLUDE/WWLIB32/VBUFFER.H b/VQ/INCLUDE/WWLIB32/VBUFFER.H new file mode 100644 index 0000000..e86db77 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/VBUFFER.H @@ -0,0 +1,1034 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : VBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 6, 1995 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + * This module contains the definition for the video buffer class. The * + * primary functionality of the video buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * This header files gives the definition for all VideoViewPort and * + * VideoBuffer functions. These functions mirror some of the basic * + * functionality of the GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Video ViewPort - The Video ViewPort holds all of the functions which * + * can be used on a Video Buffer. The video viewport but cannot be * + * attached to a Graphic Buffer. * + * * + * Video Buffer - A refers to a physical object which has been mapped to * + * ram, like a graphic card. The SeenPage is a good example of a Video * + * Buffer. * + + * Below is a tree which shows the relationship of the GraphicBuffer and * + * Buffer classes to the VideoBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Get_Selector -- Get selector for virtual viewport instance * + * VVPC::Get_Offset -- Get offset for virtual view port class instance * + * VVPC::Get_Height -- Gets the height of a virtual viewport instance * + * VVPC::Get_Width -- Get the width of a virtual viewport instance * + * VVPC::Get_XAdd -- Get the X add offset for virtual viewport instance * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * VVPC::Get_Grasphic_Buffer -- Get the graphic buffer of the VP. * + * VVPC::Put_Pixel -- stub to call curr graphic mode Put_Pixel * + * VVPC::Get_Pixel -- stub to call curr graphic mode Get_Pixel * + * VVPC::Clear -- stub to call curr graphic mode Clear * + * VVPC::To_Buffer -- stub func 1 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 2 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 3 to call curr graphic mode To_Buffer * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to VVPC * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable x, y, w, h * + * Buffer_To_Page -- Generic 'c' callable form of Buffer_To_Page * + * GVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * VVPC::Print -- Stub function to print string on a Video View Port * + * VVPC::Print -- Stub function to print an int on a Graphic Viewport Class* + * VVPC::Print -- Stub function to print long on a graphic viewport class* + * VVPC::Print -- Stub function to print a short on a Video Viewport * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VBUFFER_H +#define VBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +/*=========================================================================*/ +/* Define external assembly funcs which deals with buffers and viewports. */ +/*=========================================================================*/ +long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view); + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ +#ifdef __cplusplus +extern "C" { + extern UWORD Get_MCGA_Selector(VOID); +} +#endif + +/*=========================================================================*/ +/* Let the compiler know that a VideoBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class VideoBufferClass; + + +/*=========================================================================*/ +/* VideoViewPortClass - Holds viewport information which has been attached */ +/* to a VideoBuffer. A viewport is effectively a rectangular subset */ +/* of the full buffer which is used for clipping and the like. */ +/* */ +/* int Selector - is the selector to view port buffer */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoViewPortClass(VideoBufferClass* graphic_buff, int x, int y, int w, int h); + VideoViewPortClass(); + ~VideoViewPortClass(); + /*===================================================================*/ + /* Define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + VideoBufferClass *Get_Video_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of graphic functions that are members of a Video */ + /* ViewPort class. */ + /*===================================================================*/ + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x, int y, int fcolor, int bcolor); + unsigned long Print(int num, int x, int y, int fcol, int bcol); + unsigned long Print(short num, int x, int y, int fcol, int bcol); + unsigned long Print(long num, int x, int y, int fcol, int bcol); + + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + void Attach(VideoBufferClass *video_buff, int x, int y, int w, int h); + void Attach(VideoBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + VideoBufferClass *VideoBuff; // related graphic buff +}; + + +/*=========================================================================*/ +/* VideoBufferClass - A VideoBuffer refers to an actual instance of a */ +/* physcial device which has been mapped to a memory address like a */ +/* video card. The VideoBuffer can be drawn to directly because it */ +/* inherits a Video ViewPort which represent its full size. */ +/* size. */ +/* */ +/* int Selector - is now the selector to graphic buffer */ +/* char *Buffer - is now the offset to graphic buffer */ +/* int Width - is now the width of graphic buffer */ +/* int Height - is now the height of graphic buffer */ +/* int XAdd - is now the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class VideoBufferClass: public VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoBufferClass(unsigned short selector, long offset, int w, int h); + VideoBufferClass(int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT); + ~VideoBufferClass(); +}; + +/*************************************************************************** + * VVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * VVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * VVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * VVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * VVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * VVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * VVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline VideoBufferClass *VideoViewPortClass::Get_Video_Buffer(void) +{ + return (VideoBuff); +} + +/*************************************************************************** + * VVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + VVPC_Put_Pixel_Func(this, x, y, color); +} + +/*************************************************************************** + * VVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Pixel(int x, int y) +{ + return(VVPC_Get_Pixel_Func(this, x, y)); +} + +/*************************************************************************** + * VVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Clear(unsigned char color) +{ + VVPC_Clear_Func(this, color); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print string on a Video View Port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(VVPC_Print_Func(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print an int on a Video Viewport * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print a short on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print long on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + BYTE str[33]; + + return(VVPC_Print_Func(this, ltoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*=========================================================================*/ +/*= The following BufferClass functions are defined here because they act =*/ +/*= on video viewports. =*/ +/*=========================================================================*/ + + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*=========================================================================*/ +/* The following GraphicBufferClass functions are defined here because */ +/* they act on graphic viewports. */ +/*=========================================================================*/ +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +#endif // VBUFFER_H diff --git a/VQ/INCLUDE/WWLIB32/VIDEO.H b/VQ/INCLUDE/WWLIB32/VIDEO.H new file mode 100644 index 0000000..9dbf299 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/VIDEO.H @@ -0,0 +1,210 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern REALPTR VesaFunc; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +int Get_Video_Mode(void); +int Set_Video_Mode(int mode); +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H diff --git a/VQ/INCLUDE/WWLIB32/WINDOWS.H b/VQ/INCLUDE/WWLIB32/WINDOWS.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WINDOWS.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/VQ/INCLUDE/WWLIB32/WSA.H b/VQ/INCLUDE/WWLIB32/WSA.H new file mode 100644 index 0000000..22e4007 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WSA.H @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +#ifndef VBUFFER_H +#include "vbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +VOID * cdecl Open_Animation(BYTE const *file_name, BYTE *user_buffer, LONG user_buffer_size, WSAOpenType user_flags, UBYTE *palette=NULL); +VOID cdecl Close_Animation( VOID *handle ); +BOOL cdecl Animate_Frame(VOID *handle, GraphicViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +BOOL cdecl Animate_Frame(VOID *handle, VideoViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +WORD cdecl Get_Animation_X(VOID const *handle); +WORD cdecl Get_Animation_Y(VOID const *handle); +WORD cdecl Get_Animation_Width(VOID const *handle); +WORD cdecl Get_Animation_Height(VOID const *handle); +WORD cdecl Get_Animation_Palette(VOID const *handle); +ULONG cdecl Get_Animation_Size(VOID const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, BufferClass& buffer, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, (BYTE *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +UWORD Apply_XOR_Delta(BYTE *source_ptr, BYTE *delta_ptr); +VOID Apply_XOR_Delta_To_Page_Or_Viewport(VOID *target, VOID *delta, WORD width, WORD nextrow, WORD copy); +} + + + +#endif // WSA_H diff --git a/VQ/INCLUDE/WWLIB32/WWLIB32.H b/VQ/INCLUDE/WWLIB32/WWLIB32.H new file mode 100644 index 0000000..106364b --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WWLIB32.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +#endif // WWLIB32_H + diff --git a/VQ/INCLUDE/WWLIB32/WWMEM.H b/VQ/INCLUDE/WWLIB32/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/VQ/INCLUDE/WWLIB32/WWMEM.INC b/VQ/INCLUDE/WWLIB32/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/VQ/INCLUDE/WWLIB32/WWSTD.H b/VQ/INCLUDE/WWLIB32/WWSTD.H new file mode 100644 index 0000000..fd2f4f4 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/WWSTD.H @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define ERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +int ABS(int); +long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +short MIN(short, short); +int MIN(int, int); +long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +short MAX(short, short); +int MAX(int, int); +long MAX(long, long); + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. + +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif + + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif + + diff --git a/VQ/INCLUDE/WWLIB32/_FILE.H b/VQ/INCLUDE/WWLIB32/_FILE.H new file mode 100644 index 0000000..727c0d3 --- /dev/null +++ b/VQ/INCLUDE/WWLIB32/_FILE.H @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/VQ/INCLUDE/WWTYPES.H b/VQ/INCLUDE/WWTYPES.H new file mode 100644 index 0000000..ef92aa7 --- /dev/null +++ b/VQ/INCLUDE/WWTYPES.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +#ifndef WWTYPES_H +#define WWTYPES_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILENAME +* wwtypes.h +* +* DESCRIPTION +* Westwood standard types definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +****************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +#define VOID void +#define PRIVATE static +#define PUBLIC + +#define BOOL short +#define BYTE char +#define UBYTE unsigned char +#define WORD signed short +#define UWORD unsigned short +#define LONG signed long +#define ULONG unsigned long + +#endif /* WWTYPES_H */ + diff --git a/VQ/LIB/README.TXT b/VQ/LIB/README.TXT new file mode 100644 index 0000000..d739995 --- /dev/null +++ b/VQ/LIB/README.TXT @@ -0,0 +1,10 @@ +Library naming convention. + + nnnssctm.lib + + Where: + nnn = ID (such as 'VQA') 3 characters + ss = Size field (32bit / 16bit) 2 characters + c = Compiler name (W = Watcom, B = Borland) + t = Compiler type (P = "C++", C = Standard "C") + m = Memory model (F = flat, T = TNT) diff --git a/VQ/VQA32/AUDIO.BAK1 b/VQ/VQA32/AUDIO.BAK1 new file mode 100644 index 0000000..4a07743 --- /dev/null +++ b/VQ/VQA32/AUDIO.BAK1 @@ -0,0 +1,1220 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* audio.c +* +* DESCRIPTION +* Audio playback and timing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* VQA_StartTimerInt - Initialize system timer interrupt. +* VQA_StopTimerInt - Remove system timer interrupt. +* VQA_SetTimer - Resets current time to given tick value. +* VQA_GetTime - Return current time. +* VQA_TimerMethod - Get timer method being used. +* VQA_OpenAudio - Open sound system. +* VQA_CloseAudio - Close sound system +* VQA_StartAudio - Starts audio playback +* VQA_StopAudio - Stop audio playback. +* CopyAudio - Copy data from Audio Temp buf into Audio play buf. +* +* PRIVATE +* TimerCallback - Timer callback routine; called by HMI +* AutoDetect - Auto detect the sound card. +* AudioCallback - Sound system callback. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +#ifdef __WATCOMC__ +#pragma pack(4); +#endif + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + +/*--------------------------------------------------------------------------- + * AUDIO DEFINITIONS + *-------------------------------------------------------------------------*/ + +#define HMI_SAMPLE 0x1000 +#define HMI_DEFAULT_RATE 22050 + +#define HMI_UNINIT 0 /* Unitialize state. */ +#define HMI_VQAINIT 1 /* VQA initialized */ +#define HMI_APPINIT 2 /* Application initialized */ + +#if(VQAAUDIO_ON) +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +long AutoDetect(long bitsize, long channels); +void far TimerCallback(void); +void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID); + +/*--------------------------------------------------------------------------- + * GLOBAL DATA + *-------------------------------------------------------------------------*/ + +/* HMI Start Sample structure: + * + * lpSamplePtr - Pointer to audio buffer. + * dwSampleSize - Size of buffer. + * wLoopCount - Number of time to loop (-1 = infinate) + * wChannel - Channel to play sample on. + * wVolume - Volume level to play at. + * wSampleID - ID for sample + * lpCallback - Callback function pointer. + * wSamplePort - Port to use for non-DMA + * wSampleFlags - Flags + * dwSampleByteLength - Total length including loops + * dwSampleLoopPoint - + * dwSampleLoopLength - + * dwSamplePitchAdd - Pitch shifting component. + * wSamplePitchFraction - Pitch shifting component. + * wSamplePanLocation - For panning + * wSamplePanSpeed - For panning + * wSamplePanDirection - For panning + * wSamplePanStart - For panning. + * wSamplePanEnd - For panning. + * wSampleDelayBytes - Delay part. + * wSampleDelayRepeat - Delay part. + * dwSampleADPCMPredicted - For compression + * wSampleADPCMIndex - For compression + * wSampleRootNoteMIDI - Root note for pitch shift + * dwFiller[3] - Filler for future upgrades. + */ +_SOS_START_SAMPLE sSOSSampleData = { + _NULL, + 0L, + 0, + _CENTER_CHANNEL, + 0x7FFF, + HMI_SAMPLE, + AudioCallback, + 0, + 0, + 0L, + 0L,0L, + 0L,0, + 0,0,0,0,0, + 0,0, + 0L,0, + 0, + 0L,0L,0L +}; + +/* HMI Driver structure: Driver initialization structure. + * + * wBufferSize - Size of play buffer. + * lpBuffer - Pointer to buffer. + * wAllocateBuffer - Allocate buffer flag. + * wSampleRate - Playback rate. + * wParam - + * dwParam - + * lpFillHandler - + * lpDriverMemory - + * lpTimerMemory - + * wTimerID - + */ +_SOS_INIT_DRIVER sSOSInitDriver = { + 0, + _NULL, + _TRUE, + HMI_DEFAULT_RATE, + 19, + 0L, + _NULL, + _NULL, + _NULL, + _SOS_NORMAL_TIMER +}; + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD DigiHandle = -1; +static WORD DigiTimer = 0; +static WORD SampleHandle; +static long DigiInitFlag = HMI_UNINIT; +static long PlayingFlag = 0; + +static VQAHandleP *VQAP; +static unsigned long CurBlock = 0; +static unsigned long NextBlock; + +static WORD VQATimer = 0; +static long TimerMethod; +static long TimerInitFlag = HMI_UNINIT; +static long HMITimerFlag = HMI_UNINIT; +static long VQATickCount = 0; +#endif /* VQAAUDIO_ON */ + +static long TickOffset = 0; +char *HMIDevName = ""; + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(Init) +* +* long VQA_StartTimerInt(long); +* +* FUNCTION +* Initialize the HMI timer system if it hasn't been already then add +* our own timer event. +* +* INPUTS +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(long init) +{ + /* Does the caller want me to initialize the timer system? */ + if (init == 0) { + + /* If the timer system is uninitialized then it is the players + * responsibility to do it. + */ + if (TimerInitFlag == HMI_UNINIT) { + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + TimerInitFlag = HMI_VQAINIT; + } + } else { + TimerInitFlag = HMI_APPINIT; + } + + /* Register the VQA_TickCount timer event. */ + if (HMITimerFlag == HMI_UNINIT) { + if (sosTIMERRegisterEvent(VQA_TIMETICKS, TimerCallback, + &VQATimer) == 0) { + + HMITimerFlag = HMI_VQAINIT; + } else { + return (-1); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(void) +{ + /* Remove the timer event */ + if (HMITimerFlag == HMI_VQAINIT) { + sosTIMERRemoveEvent(VQATimer); + } + + HMITimerFlag = HMI_UNINIT; + + if (TimerInitFlag == HMI_VQAINIT) { + sosTIMERUnInitSystem(0); + } + + TimerInitFlag = HMI_UNINIT; +} + + +/**************************************************************************** +* +* NAME +* TimerCallback - VQA timer event. (Called by HMI) +* +* SYNOPSIS +* TimerCallback() +* +* void TimerCallback(void); +* +* FUNCTION +* Our custom timer event. This is the timer event that we register with +* HMI for system timing. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void far TimerCallback(void) +{ + VQATickCount++; +} +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_SetTimer - Resets current time to given tick value. +* +* SYNOPSIS +* VQA_SetTimer(Time, Method) +* +* void VQA_SetTimer(long, long); +* +* FUNCTION +* Sets 'TickOffset' to a value that will make the current time look like +* the time passed in. This function allows the player to be "paused", +* by recording the time of the pause, and then setting the timer to +* that time. The timer method used by the player is also set. The method +* selected is not neccesarily the method that will be used because some +* timer methods work with only certain playback conditions. (EX: The +* audio DMA timer method cannot be used if there is not any audio +* playing.) +* +* INPUTS +* Time - Value to set current time to. +* Method - Timer method to use. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_SetTimer(long time, long method) +{ + unsigned long curtime; + + #if(VQAAUDIO_ON) + /* If the client does not have a preferencee then pick a method + * based on the state of the player. + */ + if (method == VQA_TMETHOD_DEFAULT) { + + /* If we are playing audio, use the audio DMA position. */ + if (PlayingFlag) { + method = VQA_TMETHOD_AUDIO; + } + + /* Otherwise use the HMI timer if it is initialized. */ + else if (HMITimerFlag) { + method = VQA_TMETHOD_INT; + } + + /* If all else fails resort the the "jerky" DOS time. */ + else { + method = VQA_TMETHOD_DOS; + } + } else { + + /* We cannot use the DMA position if there isn't any audio playing. */ + if (!PlayingFlag && (method == VQA_TMETHOD_AUDIO)) { + method = VQA_TMETHOD_INT; + } + + /* We cannot use the timer if it has not been initialized. */ + if (!HMITimerFlag && (method == VQA_TMETHOD_INT)) { + method = VQA_TMETHOD_DOS; + } + } + + TimerMethod = method; + #endif + + TickOffset = 0L; + curtime = VQA_GetTime(); + TickOffset = (time - curtime); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetTime - Return current time. +* +* SYNOPSIS +* Time = VQA_GetTime() +* +* unsigned long VQA_GetTime(void); +* +* FUNCTION +* This routine returns timer ticks computed one of 3 ways: +* +* 1) If audio is playing, the timer is based on the DMA buffer position: +* Compute the number of audio samples that have actually been played. +* The following internal HMI variables are used: +* +* _lpSOSDMAFillCount[drv_handle]: current DMA buffer position +* _lpSOSSampleList[drv_handle][samp_handle]: +* sampleTotalBytes: total bytes sent by HMI to the DMA buffer +* sampleLastFill: HMI's last fill position in DMA buffer +* +* So, the number of samples actually played is: +* +* sampleTotalBytes - +* where is how far ahead sampleLastFill is in front of +* _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount) +* +* These values are indices into a circular DMA buffer, so: +* +* if (sampleLastFill >= _lpSOSDMAFillCount) +* = sampleLastFill - _lpSOSDMAFillCount +* else +* = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill +* +* Note that, if using the stereo driver with mono data, you must +* divide LastFill & FillCount by 2, but not TotalBytes. If using the +* stereo driver with stereo data, you must divide all 3 variables +* by 2. +* +* 2) If no audio is playing, but the timer interrupt is running, +* VQATickCount is used as the timer +* +* 3) If no audio is playing & no timer interrupt is going, the DOS 18.2 +* system timer is used. +* +* Regardless of the method, TickOffset is used as an offset from the +* computed time. +* +* INPUTS +* NONE +* +* RESULT +* Time - Time in VQA_TIMETICKS +* +****************************************************************************/ + +unsigned long VQA_GetTime(void) +{ + #if(VQAAUDIO_ON) + VQAAudio *audio; + unsigned long totalbytes; + unsigned long fillcount; + unsigned long lastfill; + unsigned long dma_diff; + unsigned long samples; + #endif + + long ticks; + + #if(VQAAUDIO_ON) + switch (TimerMethod) { + + /* If Audio is playing then timing is based on the audio DMA buffer + * position. + */ + case VQA_TMETHOD_AUDIO: + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + + /* Get private HMI values */ + fillcount = (*_lpSOSDMAFillCount[DigiHandle]); + lastfill = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleLastFill; + totalbytes = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleTotalBytes; + + /* At the very first, sometimes HMI reports a non-zero DMA buffer + * position even though totalbytes is 0. Obviously, if totalbytes + * is 0, the DMA buffer position must be 0 too. + */ + if (totalbytes == 0) { + dma_diff = 0; + } else { + + /* Compute the number of bytes played. */ + if (lastfill >= fillcount) { + dma_diff = (lastfill - fillcount); + } else { + dma_diff = (VQAP->Config.HMIBufSize - fillcount) + lastfill; + } + + /* Sometimes the dma difference ends up being larger than the number + * of bytes processed, so we must compensate for this in order to + * keep the player from skipping unecessarilly. + */ + if (dma_diff > totalbytes) { + dma_diff = 0L; + } + } + + /* The number of samples played is computed from the number of bytes + * played divided by the size of the sample and the number of channels. + */ + samples = totalbytes - dma_diff; + + #if(0) + /* Do not figure in skipped audio frames. */ + samples -= (audio->NumSkipped * VQAP->Config.HMIBufSize); + #endif + + samples /= (audio->Channels * (audio->BitsPerSample >> 3)); + + /* Convert to time ticks */ + ticks = (long)((samples * VQA_TIMETICKS) / sSOSInitDriver.wSampleRate); + ticks += TickOffset; + break; + + /* No audio playing, but timer interrupt is going; use VQATickCount */ + case VQA_TMETHOD_INT: + ticks = (VQATickCount + TickOffset); + break; + + /* No interrupts are going at all; use DOS's time */ + default: + case VQA_TMETHOD_DOS: + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + break; + } + #else + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + #endif + + return (ticks); +} + + +/**************************************************************************** +* +* NAME +* VQA_TimerMethod - Get timer method being used. +* +* SYNOPSIS +* Method = VQA_TimerMethod() +* +* long VQA_TimerMethod(void); +* +* FUNCTION +* Returns the ID of the current timer method being used. +* +* INPUTS +* NONE +* +* RESULT +* Method - Method used for the timer. +* +****************************************************************************/ + +long VQA_TimerMethod(void) +{ + #if(VQAAUDIO_ON) + return (TimerMethod); + #else + return (VQA_TMETHOD_DOS); + #endif +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* VQA_OpenAudio - Open sound system. +* +* SYNOPSIS +* Error = VQA_OpenAudio(VQAHandleP) +* +* long VQA_OpenAudio(VQAHandleP *); +* +* FUNCTION +* Initialize the sound system by setting the DigiHandle to and HMI +* driver handle. The timer must first be initialized before calling +* this function. +* +* INPUTS +* VQAHandleP - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_OpenAudio(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAAudio *audio; + VQAConfig *config; + VQAHeader *header; + unsigned char *driver_path; + WORD port; + long rc; + + /* Return if already initialized */ + if (DigiInitFlag != HMI_UNINIT) { + return (0); + } + + /* Dereference data memebers for quicker access. */ + config = &vqap->Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Fail if no audio buffer or DigiCard is 0 (no sound) */ + if ((audio->Buffer == NULL) || (config->DigiCard == 0)) { + return (-1); + } + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + sSOSInitDriver.wSampleRate = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + sSOSInitDriver.wSampleRate = ((audio->SampleRate * config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = sSOSInitDriver.wSampleRate; + } else { + sSOSInitDriver.wSampleRate = audio->SampleRate; + config->AudioRate = sSOSInitDriver.wSampleRate; + } + + /* If the application has already initialized HMI then set the + * necessary variables. Otherwise we must setup HMI ourself. + */ + if (config->OptionFlags & VQAOPTF_HMIINIT) { + DigiHandle = config->DigiHandle; + HMIDevName = "App-Specific"; + TimerInitFlag = HMI_APPINIT; + DigiInitFlag = HMI_APPINIT; + } else { + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /*----------------------------------------------------------------------- + * Initialize DigiHardware with port, IRQ, and DMA, and make sure + * Config.DigiCard contains the HMI ID we want to use: + * + * - If Config.DigiCard is -1, auto-detect the hardware; then, do a + * FindHardware so the GetSettings will work + * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use + * FindHardware & GetSettings to get the settings + * - If all are filled in, just use them as they are + *---------------------------------------------------------------------*/ + + /* Auto-Detect */ + if (config->DigiCard == -1) { + + /* Version 1 VQA's have only 8 bit mono audio streams. */ + if (header->Version == VQAHD_VER1) { + config->DigiCard = AutoDetect(8, 1); + } else { + config->DigiCard = AutoDetect(header->BitsPerSample, + header->Channels); + } + + if (config->DigiCard == -1) { + sosDIGIDetectUnInit(); + return (-1); + } + } + + /* Do a FindHardware & GetSettings */ + if (config->DigiPort == -1) { + if (sosDIGIDetectFindHardware(config->DigiCard, &DigiCaps, &port)) { + sosDIGIDetectUnInit(); + return (-1); + } + + if (sosDIGIDetectGetSettings(&DigiHardware)) { + sosDIGIDetectUnInit(); + return (-1); + } + + config->DigiPort = DigiHardware.wPort; + config->DigiIRQ = DigiHardware.wIRQ; + config->DigiDMA = DigiHardware.wDMA; + HMIDevName = (char *)DigiCaps.szDeviceName; + } else { + DigiHardware.wPort = config->DigiPort; + DigiHardware.wIRQ = config->DigiIRQ; + DigiHardware.wDMA = config->DigiDMA; + HMIDevName = "App-Specific"; + } + + sosDIGIDetectUnInit(); + + /* Initialize the DIGI system & driver */ + sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL); + sSOSInitDriver.wBufferSize = config->HMIBufSize; + sSOSInitDriver.lpBuffer = NULL; + sSOSInitDriver.lpFillHandler = NULL; + sSOSInitDriver.lpDriverMemory = NULL; + sSOSInitDriver.lpTimerMemory = NULL; + DigiHandle = -1; + + if ((rc = sosDIGIInitDriver(config->DigiCard, &DigiHardware, + &sSOSInitDriver, &DigiHandle)) != 0) { + + return (rc); + } + + /*----------------------------------------------------------------------- + * Register the timer event + *---------------------------------------------------------------------*/ + + /* If the timer hasn't been init'd, do it now */ + if (TimerInitFlag == HMI_UNINIT) { + sosTIMERInitSystem(_TIMER_DOS_RATE ,_SOS_DEBUG_NORMAL); + TimerInitFlag = HMI_VQAINIT; + } + + /* Register the event */ + rc = sosTIMERRegisterEvent(VQA_TIMETICKS, sSOSInitDriver.lpFillHandler, + &DigiTimer); + + if (rc) { + sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + return (rc); + } + + DigiInitFlag = HMI_VQAINIT; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* AutoDetect - Auto detect the sound card. +* +* SYNOPSIS +* CardID = AutoDetect(BitSize, Channels) +* +* long AutoDetect(long, long); +* +* FUNCTION +* Autodetects the type of sound card present in the system. +* +* INPUTS +* BitSize - Bits per sample size. +* Channels - Number of desired channels. +* +* RESULT +* CardID - HMI ID of the sound card found, -1 if none. +* +****************************************************************************/ + +long AutoDetect(long bitsize, long channels) +{ + long device_id = -1; + WORD port; + long i; + long rc; + + /* Search for an 8-bit mono device */ + if (sosDIGIDetectFindFirst(&DigiCaps, &port)) { + return (-1); + } + + channels--; + + i = 0; + while (i < 6) { + i++; + + if ((DigiCaps.wBitsPerSample == bitsize) + && (DigiCaps.wChannels == channels)) { + + break; + } + + if (sosDIGIDetectFindNext(&DigiCaps, &port)) { + return (-1); + } + } + + /* Exit if failed to find the required device */ + if ((DigiCaps.wBitsPerSample != bitsize) + || (DigiCaps.wChannels != channels)) { + + return (-1); + } + + /* Stash the ID */ + device_id = DigiCaps.wDeviceID; + + /* Now that we have handled the initial pass, verify that if we found an + * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise. + */ + if ((WORD)DigiCaps.wDeviceID == _ADLIB_GOLD_8_MONO) { + rc = sosDIGIDetectFindNext(&DigiCaps, &port); + + while ((i < 6) && (rc == 0)) { + i++; + + if ((DigiCaps.wBitsPerSample == 8) && (DigiCaps.wChannels == 0)) { + break; + } + + if ((rc = sosDIGIDetectFindNext(&DigiCaps, &port)) != 0) { + break; + } + } + + /* If we don't have an error use the secondary device ID, after all, + * anything's better than an Adlib Gold. If we do have an error or there + * is nothing we can use then the device ID is already set to the adlib + * gold so just let it rip. + */ + if ((rc == 0) && ((WORD)DigiCaps.wDeviceID == _SBPRO_8_MONO)) { + return (DigiCaps.wDeviceID); + } + } + + return (device_id); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(void) +{ + /* Remove the Digi event */ + if (DigiInitFlag == HMI_VQAINIT) { + sosTIMERRemoveEvent(DigiTimer); + } + + /* Un-init timer if necessary */ + if (TimerInitFlag == HMI_VQAINIT) { + sosTIMERUnInitSystem(0); + } + + TimerInitFlag = HMI_UNINIT; + + /* Remove the driver */ + if (DigiInitFlag == HMI_VQAINIT) { + sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + } + + DigiInitFlag = HMI_UNINIT; + + if (PlayingFlag) { + PlayingFlag = 0; + } +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (audio->Flags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* Set my driver handle */ + if (config->DigiHandle != -1) { + DigiHandle = config->DigiHandle; + } + + if (DigiHandle == (WORD)-1) { + return (-1); + } + + /* Re-init the sample structure */ + memset(&sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE)); + sSOSSampleData.lpSamplePtr = (unsigned char *)vqap->VQABuf->Audio.Buffer; + sSOSSampleData.dwSampleSize = config->HMIBufSize; + sSOSSampleData.wVolume = (config->Volume << 7); + sSOSSampleData.wSampleID = HMI_SAMPLE; + sSOSSampleData.lpCallback = AudioCallback; + sSOSSampleData.wSampleFlags = (_VOLUME|1); + + if (audio->Channels == 2) { + sSOSSampleData.wChannel = _INTERLEAVED; + } else { + sSOSSampleData.wChannel = _CENTER_CHANNEL; + } + + /* Start playback */ + #if(0) // Don't reset this to zero or pause will break. + CurBlock = 0; + #endif + + SampleHandle = sosDIGIStartSample(DigiHandle, &sSOSSampleData); + audio->Flags |= VQAAUDF_ISPLAYING; + PlayingFlag = 1; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + /* Just return if not playing */ + if (vqap->VQABuf->Audio.Flags & VQAAUDF_ISPLAYING) { + vqap->VQABuf->Audio.Flags &= ~VQAAUDF_ISPLAYING; + sosDIGIStopSample(DigiHandle, SampleHandle); + } +} + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + /* Suppress compiler warnings */ + wDriverHandle = wDriverHandle; + wSampleID = wSampleID; + + /* See if we're called because the buffer is empty */ + if (wAction == _SAMPLE_PROCESSED) { + + /* Compute the 'NextBlock' index */ + NextBlock = CurBlock + 1; + + if (NextBlock >= audio->NumAudBlocks) { + NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + CurBlock = 0; + } + } else { + audio->NumSkipped++; + } + + /* Start the new buffer playing */ + sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer)+audio->PlayPosition; + sosDIGIContinueSample(DigiHandle, SampleHandle, &sSOSSampleData); + audio->SamplesPlayed += config->HMIBufSize; + } +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif +#endif /* VQAAUDIO_ON */ + diff --git a/VQ/VQA32/AUDIO.BAK2 b/VQ/VQA32/AUDIO.BAK2 new file mode 100644 index 0000000..ca21204 --- /dev/null +++ b/VQ/VQA32/AUDIO.BAK2 @@ -0,0 +1,1246 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* audio.c +* +* DESCRIPTION +* Audio playback and timing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* June 22, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_StartTimerInt - Initialize system timer interrupt. +* VQA_StopTimerInt - Remove system timer interrupt. +* VQA_SetTimer - Resets current time to given tick value. +* VQA_GetTime - Return current time. +* VQA_TimerMethod - Get timer method being used. +* VQA_OpenAudio - Open sound system. +* VQA_CloseAudio - Close sound system +* VQA_StartAudio - Starts audio playback +* VQA_StopAudio - Stop audio playback. +* CopyAudio - Copy data from Audio Temp buf into Audio play buf. +* +* PRIVATE +* TimerCallback - Timer callback routine; called by HMI +* AutoDetect - Auto detect the sound card. +* AudioCallback - Sound system callback. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +#ifdef __WATCOMC__ +#pragma pack(4); +#endif + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + +/*--------------------------------------------------------------------------- + * AUDIO DEFINITIONS + *-------------------------------------------------------------------------*/ + +#define HMI_SAMPLE 0x1000 +#define HMI_DEFAULT_RATE 22050 + +#define HMI_UNINIT 0 /* Unitialize state. */ +#define HMI_VQAINIT 1 /* VQA initialized */ +#define HMI_APPINIT 2 /* Application initialized */ + +#if(VQAAUDIO_ON) +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +long AutoDetect(long bitsize, long channels); +void far TimerCallback(void); +void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID); + +/*--------------------------------------------------------------------------- + * GLOBAL DATA + *-------------------------------------------------------------------------*/ + +/* HMI Start Sample structure: + * + * lpSamplePtr - Pointer to audio buffer. + * dwSampleSize - Size of buffer. + * wLoopCount - Number of time to loop (-1 = infinate) + * wChannel - Channel to play sample on. + * wVolume - Volume level to play at. + * wSampleID - ID for sample + * lpCallback - Callback function pointer. + * wSamplePort - Port to use for non-DMA + * wSampleFlags - Flags + * dwSampleByteLength - Total length including loops + * dwSampleLoopPoint - + * dwSampleLoopLength - + * dwSamplePitchAdd - Pitch shifting component. + * wSamplePitchFraction - Pitch shifting component. + * wSamplePanLocation - For panning + * wSamplePanSpeed - For panning + * wSamplePanDirection - For panning + * wSamplePanStart - For panning. + * wSamplePanEnd - For panning. + * wSampleDelayBytes - Delay part. + * wSampleDelayRepeat - Delay part. + * dwSampleADPCMPredicted - For compression + * wSampleADPCMIndex - For compression + * wSampleRootNoteMIDI - Root note for pitch shift + * dwFiller[3] - Filler for future upgrades. + */ +_SOS_START_SAMPLE sSOSSampleData = { + _NULL, + 0L, + 0, + _CENTER_CHANNEL, + 0x7FFF, + HMI_SAMPLE, + AudioCallback, + 0, + 0, + 0L, + 0L,0L, + 0L,0, + 0,0,0,0,0, + 0,0, + 0L,0, + 0, + 0L,0L,0L +}; + +/* HMI Driver structure: Driver initialization structure. + * + * wBufferSize - Size of play buffer. + * lpBuffer - Pointer to buffer. + * wAllocateBuffer - Allocate buffer flag. + * wSampleRate - Playback rate. + * wParam - + * dwParam - + * lpFillHandler - + * lpDriverMemory - + * lpTimerMemory - + * wTimerID - + */ +_SOS_INIT_DRIVER sSOSInitDriver = { + 0, + _NULL, + _TRUE, + HMI_DEFAULT_RATE, + 19, + 0L, + _NULL, + _NULL, + _NULL, + _SOS_NORMAL_TIMER +}; + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD DigiHandle = -1; +static WORD DigiTimer = 0; +static WORD SampleHandle; +static long DigiInitFlag = HMI_UNINIT; +static long PlayingFlag = 0; + +static VQAHandleP *VQAP; +static unsigned long CurBlock = 0; +static unsigned long NextBlock; + +static WORD VQATimer = 0; +static long TimerMethod; +static long TimerInitFlag = HMI_UNINIT; +static long HMITimerFlag = HMI_UNINIT; +static long VQATickCount = 0; + +#endif /* VQAAUDIO_ON */ + +static long TickOffset = 0; +char *HMIDevName = ""; + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(Init) +* +* long VQA_StartTimerInt(long); +* +* FUNCTION +* Initialize the HMI timer system if it hasn't been already then add +* our own timer event. +* +* INPUTS +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(long init) +{ + /* Does the caller want me to initialize the timer system? */ + if (init == 0) { + + /* If the timer system is uninitialized then it is the players + * responsibility to do it. + */ + if (TimerInitFlag == HMI_UNINIT) { + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + TimerInitFlag = HMI_VQAINIT; + } + } else { + TimerInitFlag = HMI_APPINIT; + } + + /* Register the VQA_TickCount timer event. */ + if (HMITimerFlag == HMI_UNINIT) { + if (sosTIMERRegisterEvent(VQA_TIMETICKS,TimerCallback,&VQATimer) == 0) { + HMITimerFlag = HMI_VQAINIT; + } else { + return (-1); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(void) +{ + /* Remove the timer event */ + if (HMITimerFlag == HMI_VQAINIT) { + sosTIMERRemoveEvent(VQATimer); + } + + HMITimerFlag = HMI_UNINIT; + + if (TimerInitFlag == HMI_VQAINIT) { + sosTIMERUnInitSystem(0); + } + + TimerInitFlag = HMI_UNINIT; +} + + +/**************************************************************************** +* +* NAME +* TimerCallback - VQA timer event. (Called by HMI) +* +* SYNOPSIS +* TimerCallback() +* +* void TimerCallback(void); +* +* FUNCTION +* Our custom timer event. This is the timer event that we register with +* HMI for system timing. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void far TimerCallback(void) +{ + VQATickCount++; +} +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_SetTimer - Resets current time to given tick value. +* +* SYNOPSIS +* VQA_SetTimer(Time, Method) +* +* void VQA_SetTimer(long, long); +* +* FUNCTION +* Sets 'TickOffset' to a value that will make the current time look like +* the time passed in. This function allows the player to be "paused", +* by recording the time of the pause, and then setting the timer to +* that time. The timer method used by the player is also set. The method +* selected is not neccesarily the method that will be used because some +* timer methods work with only certain playback conditions. (EX: The +* audio DMA timer method cannot be used if there is not any audio +* playing.) +* +* INPUTS +* Time - Value to set current time to. +* Method - Timer method to use. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_SetTimer(long time, long method) +{ + unsigned long curtime; + + #if(VQAAUDIO_ON) + /* If the client does not have a preferencee then pick a method + * based on the state of the player. + */ + if (method == VQA_TMETHOD_DEFAULT) { + + /* If we are playing audio, use the audio DMA position. */ + if (PlayingFlag) { + method = VQA_TMETHOD_AUDIO; + } + + /* Otherwise use the HMI timer if it is initialized. */ + else if (HMITimerFlag) { + method = VQA_TMETHOD_INT; + } + + /* If all else fails resort the the "jerky" DOS time. */ + else { + method = VQA_TMETHOD_DOS; + } + } else { + + /* We cannot use the DMA position if there isn't any audio playing. */ + if (!PlayingFlag && (method == VQA_TMETHOD_AUDIO)) { + method = VQA_TMETHOD_INT; + } + + /* We cannot use the timer if it has not been initialized. */ + if (!HMITimerFlag && (method == VQA_TMETHOD_INT)) { + method = VQA_TMETHOD_DOS; + } + } + + TimerMethod = method; + #endif + + TickOffset = 0L; + curtime = VQA_GetTime(); + TickOffset = (time - curtime); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetTime - Return current time. +* +* SYNOPSIS +* Time = VQA_GetTime() +* +* unsigned long VQA_GetTime(void); +* +* FUNCTION +* This routine returns timer ticks computed one of 3 ways: +* +* 1) If audio is playing, the timer is based on the DMA buffer position: +* Compute the number of audio samples that have actually been played. +* The following internal HMI variables are used: +* +* _lpSOSDMAFillCount[drv_handle]: current DMA buffer position +* _lpSOSSampleList[drv_handle][samp_handle]: +* sampleTotalBytes: total bytes sent by HMI to the DMA buffer +* sampleLastFill: HMI's last fill position in DMA buffer +* +* So, the number of samples actually played is: +* +* sampleTotalBytes - +* where is how far ahead sampleLastFill is in front of +* _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount) +* +* These values are indices into a circular DMA buffer, so: +* +* if (sampleLastFill >= _lpSOSDMAFillCount) +* = sampleLastFill - _lpSOSDMAFillCount +* else +* = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill +* +* Note that, if using the stereo driver with mono data, you must +* divide LastFill & FillCount by 2, but not TotalBytes. If using the +* stereo driver with stereo data, you must divide all 3 variables +* by 2. +* +* 2) If no audio is playing, but the timer interrupt is running, +* VQATickCount is used as the timer +* +* 3) If no audio is playing & no timer interrupt is going, the DOS 18.2 +* system timer is used. +* +* Regardless of the method, TickOffset is used as an offset from the +* computed time. +* +* INPUTS +* NONE +* +* RESULT +* Time - Time in VQA_TIMETICKS +* +****************************************************************************/ + +unsigned long VQA_GetTime(void) +{ + #if(VQAAUDIO_ON) + VQAAudio *audio; + unsigned long bytesleft; + unsigned long samples; + #endif + + long ticks; + + #if(VQAAUDIO_ON) + switch (TimerMethod) { + + /* If Audio is playing then timing is based on the audio DMA buffer + * position. + */ + case VQA_TMETHOD_AUDIO: + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + + /* Compute our current position in the audio track by getting the + * bytes processed by HMI. Then adjust the bytes processed by the + * position of the DMA fill handler, this should tell us exactly + * where we are in the audio track. + */ + samples = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleTotalBytes; + bytesleft = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleBytesLeft; + + /* Adjust the bytes processed from the bytes left to DMA in the + * buffer. + */ + if (samples >= bytesleft) { + samples -= bytesleft; + } + + /* Calculate the number of samples by taking the total number of + * bytes processed and divide it by the number of channels and + * bits per sample. + */ + samples -= (audio->NumSkipped * VQAP->Config.HMIBufSize); + samples /= (audio->Channels * (audio->BitsPerSample >> 3)); + + /* The elapsed ticks is calculated by the number of samples + * processed times the tick resolution per second divided by the + * sample rate. + */ + ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate); + ticks += TickOffset; + break; + + /* No audio playing, but timer interrupt is going; use VQATickCount */ + case VQA_TMETHOD_INT: + ticks = (VQATickCount + TickOffset); + break; + + /* No interrupts are going at all; use DOS's time */ + default: + case VQA_TMETHOD_DOS: + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + break; + } + #else + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + #endif + + return (ticks); +} + + +/**************************************************************************** +* +* NAME +* VQA_TimerMethod - Get timer method being used. +* +* SYNOPSIS +* Method = VQA_TimerMethod() +* +* long VQA_TimerMethod(void); +* +* FUNCTION +* Returns the ID of the current timer method being used. +* +* INPUTS +* NONE +* +* RESULT +* Method - Method used for the timer. +* +****************************************************************************/ + +long VQA_TimerMethod(void) +{ + #if(VQAAUDIO_ON) + return (TimerMethod); + #else + return (VQA_TMETHOD_DOS); + #endif +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* VQA_OpenAudio - Open sound system. +* +* SYNOPSIS +* Error = VQA_OpenAudio(VQAHandleP) +* +* long VQA_OpenAudio(VQAHandleP *); +* +* FUNCTION +* Initialize the sound system by setting the DigiHandle to and HMI +* driver handle. The timer must first be initialized before calling +* this function. +* +* INPUTS +* VQAHandleP - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_OpenAudio(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAAudio *audio; + VQAConfig *config; + VQAHeader *header; + unsigned char *driver_path; + WORD port; + long rc; + + /* Return if already initialized */ + if (DigiInitFlag != HMI_UNINIT) { + return (0); + } + + /* Dereference data memebers for quicker access. */ + config = &vqap->Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Fail if no audio buffer or DigiCard is 0 (no sound) */ + if ((audio->Buffer == NULL) || (config->DigiCard == 0)) { + return (-1); + } + + /* Reset the buffer position to the beginning. */ + CurBlock = 0; + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + sSOSInitDriver.wSampleRate = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + sSOSInitDriver.wSampleRate = ((audio->SampleRate * config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = sSOSInitDriver.wSampleRate; + } else { + sSOSInitDriver.wSampleRate = audio->SampleRate; + config->AudioRate = audio->SampleRate; + } + + /* If the application has already initialized HMI then set the + * necessary variables. Otherwise we must setup HMI ourself. + */ + if (config->OptionFlags & VQAOPTF_HMIINIT) { + + /* The application MUST provide the card type! */ + if (config->DigiCard == -1) { + return (-1); + } + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /* Get the capabilities of the card being used. */ + rc = sosDIGIDetectGetCaps(config->DigiCard, &DigiCaps); + sosDIGIDetectUnInit(); + + if (rc != 0) { + return (rc); + } + + DigiHandle = config->DigiHandle; + HMIDevName = "App-Specific"; + TimerInitFlag = HMI_APPINIT; + DigiInitFlag = HMI_APPINIT; + } else { + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /*----------------------------------------------------------------------- + * Initialize DigiHardware with port, IRQ, and DMA, and make sure + * Config.DigiCard contains the HMI ID we want to use: + * + * - If Config.DigiCard is -1, auto-detect the hardware; then, do a + * FindHardware so the GetSettings will work + * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use + * FindHardware & GetSettings to get the settings + * - If all are filled in, just use them as they are + *---------------------------------------------------------------------*/ + + /* Auto-Detect */ + if (config->DigiCard == -1) { + + /* Version 1 VQA's have only 8 bit mono audio streams. */ + if (header->Version == VQAHD_VER1) { + config->DigiCard = AutoDetect(8, 1); + } else { + config->DigiCard = AutoDetect(audio->BitsPerSample, audio->Channels); + + /* Resort to 8bit mono */ + if (config->DigiCard == -1) { + config->DigiCard = AutoDetect(8, 1); + } + } + + if (config->DigiCard == -1) { + sosDIGIDetectUnInit(); + return (-1); + } + } + + /* Do a FindHardware & GetSettings */ + if (config->DigiPort == -1) { + if (sosDIGIDetectFindHardware(config->DigiCard, &DigiCaps, &port)) { + sosDIGIDetectUnInit(); + return (-1); + } + + if (sosDIGIDetectGetSettings(&DigiHardware)) { + sosDIGIDetectUnInit(); + return (-1); + } + + config->DigiPort = DigiHardware.wPort; + config->DigiIRQ = DigiHardware.wIRQ; + config->DigiDMA = DigiHardware.wDMA; + HMIDevName = (char *)DigiCaps.szDeviceName; + } else { + DigiHardware.wPort = config->DigiPort; + DigiHardware.wIRQ = config->DigiIRQ; + DigiHardware.wDMA = config->DigiDMA; + HMIDevName = "App-Specific"; + } + + sosDIGIDetectUnInit(); + + /* Initialize the DIGI system & driver */ + sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL); + sSOSInitDriver.wBufferSize = config->HMIBufSize; + sSOSInitDriver.lpBuffer = NULL; + sSOSInitDriver.lpFillHandler = NULL; + sSOSInitDriver.lpDriverMemory = NULL; + sSOSInitDriver.lpTimerMemory = NULL; + DigiHandle = -1; + + if ((rc = sosDIGIInitDriver(config->DigiCard, &DigiHardware, + &sSOSInitDriver, &DigiHandle)) != 0) { + + return (rc); + } + + /*----------------------------------------------------------------------- + * Register the timer event + *---------------------------------------------------------------------*/ + + /* If the timer hasn't been init'd, do it now */ + if (TimerInitFlag == HMI_UNINIT) { + sosTIMERInitSystem(_TIMER_DOS_RATE ,_SOS_DEBUG_NORMAL); + TimerInitFlag = HMI_VQAINIT; + } + + /* Register the event */ + rc = sosTIMERRegisterEvent(VQA_TIMETICKS, sSOSInitDriver.lpFillHandler, + &DigiTimer); + + if (rc) { + sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + return (rc); + } + + config->DigiHandle = DigiHandle; + DigiInitFlag = HMI_VQAINIT; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* AutoDetect - Auto detect the sound card. +* +* SYNOPSIS +* CardID = AutoDetect(BitSize, Channels) +* +* long AutoDetect(long, long); +* +* FUNCTION +* Autodetects the type of sound card present in the system. +* +* INPUTS +* BitSize - Bits per sample size. +* Channels - Number of desired channels. +* +* RESULT +* CardID - HMI ID of the sound card found, -1 if none. +* +****************************************************************************/ + +long AutoDetect(long bitsize, long channels) +{ + long device_id = -1; + WORD port; + long i; + long rc; + + /* Search for an 8-bit mono device */ + if (sosDIGIDetectFindFirst(&DigiCaps, &port)) { + return (-1); + } + + channels--; + + i = 0; + while (i < 6) { + i++; + + if ((DigiCaps.wBitsPerSample == bitsize) + && (DigiCaps.wChannels == channels)) { + + break; + } + + if (sosDIGIDetectFindNext(&DigiCaps, &port)) { + return (-1); + } + } + + /* Exit if failed to find the required device */ + if ((DigiCaps.wBitsPerSample != bitsize) + || (DigiCaps.wChannels != channels)) { + + return (-1); + } + + /* Stash the ID */ + device_id = DigiCaps.wDeviceID; + + /* Now that we have handled the initial pass, verify that if we found an + * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise. + */ + if ((WORD)DigiCaps.wDeviceID == _ADLIB_GOLD_8_MONO) { + rc = sosDIGIDetectFindNext(&DigiCaps, &port); + + while ((i < 6) && (rc == 0)) { + i++; + + if ((DigiCaps.wBitsPerSample == 8) && (DigiCaps.wChannels == 0)) { + break; + } + + if ((rc = sosDIGIDetectFindNext(&DigiCaps, &port)) != 0) { + break; + } + } + + /* If we don't have an error use the secondary device ID, after all, + * anything's better than an Adlib Gold. If we do have an error or there + * is nothing we can use then the device ID is already set to the adlib + * gold so just let it rip. + */ + if ((rc == 0) && ((WORD)DigiCaps.wDeviceID == _SBPRO_8_MONO)) { + return (DigiCaps.wDeviceID); + } + } + + return (device_id); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(void) +{ + /* Remove the Digi event */ + if (DigiInitFlag == HMI_VQAINIT) { + sosTIMERRemoveEvent(DigiTimer); + } + + /* Un-init timer if necessary */ + if (TimerInitFlag == HMI_VQAINIT) { + sosTIMERUnInitSystem(0); + } + + TimerInitFlag = HMI_UNINIT; + + /* Remove the driver */ + if (DigiInitFlag == HMI_VQAINIT) { + sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + } + + DigiInitFlag = HMI_UNINIT; + + if (PlayingFlag) { + PlayingFlag = 0; + } +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (audio->Flags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* Set my driver handle */ + if (config->DigiHandle != -1) { + DigiHandle = config->DigiHandle; + } + + if (DigiHandle == (WORD)-1) { + return (-1); + } + + /*------------------------------------------------------------------------- + * Initialize the sample structure. + *-----------------------------------------------------------------------*/ + memset(&sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE)); + sSOSSampleData.lpSamplePtr = (unsigned char *)vqap->VQABuf->Audio.Buffer; + sSOSSampleData.dwSampleSize = config->HMIBufSize; + sSOSSampleData.wVolume = (config->Volume << 7); + sSOSSampleData.wSampleID = HMI_SAMPLE; + sSOSSampleData.lpCallback = AudioCallback; + + /* Set the channel flags for the type of data we have. */ + if (audio->Channels == 2) { + sSOSSampleData.wChannel = _INTERLEAVED; + } else { + sSOSSampleData.wChannel = _CENTER_CHANNEL; + } + + /* If the card is unable to handle stereo data the we must notify the + * sound system to convert the stereo data to mono data during playback. + */ + if ((audio->Channels - 1) > DigiCaps.wChannels) { + sSOSSampleData.wSampleFlags |= _STEREOTOMONO; + } + + /* If the card is unable to handle the sample size of the audio data + * then we must notify the sound system to convert the audio data to + * the proper format. + */ + if (audio->BitsPerSample != DigiCaps.wBitsPerSample) { + if (audio->BitsPerSample > DigiCaps.wBitsPerSample) { + sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8; + } else { + sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* Start playback */ + SampleHandle = sosDIGIStartSample(DigiHandle, &sSOSSampleData); + audio->Flags |= VQAAUDF_ISPLAYING; + PlayingFlag = 1; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + /* Just return if not playing */ + if (vqap->VQABuf->Audio.Flags & VQAAUDF_ISPLAYING) { + vqap->VQABuf->Audio.Flags &= ~VQAAUDF_ISPLAYING; + sosDIGIStopSample(DigiHandle, SampleHandle); + } +} + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void far cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + /* Suppress compiler warnings */ + wDriverHandle = wDriverHandle; + wSampleID = wSampleID; + + /* See if we're called because the buffer is empty */ + if (wAction == _SAMPLE_PROCESSED) { + + /* Compute the 'NextBlock' index */ + NextBlock = CurBlock + 1; + + if (NextBlock >= audio->NumAudBlocks) { + NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + CurBlock = 0; + } + } else { + audio->NumSkipped++; + } + + /* Start the new buffer playing */ + sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer) + + audio->PlayPosition; + sosDIGIContinueSample(DigiHandle, SampleHandle, &sSOSSampleData); + audio->SamplesPlayed += config->HMIBufSize; + } +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif +#endif /* VQAAUDIO_ON */ + diff --git a/VQ/VQA32/AUDIO.CPP b/VQ/VQA32/AUDIO.CPP new file mode 100644 index 0000000..5d1e3e7 --- /dev/null +++ b/VQ/VQA32/AUDIO.CPP @@ -0,0 +1,1308 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* audio.c +* +* DESCRIPTION +* Audio playback and timing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 4, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_StartTimerInt - Initialize system timer interrupt. +* VQA_StopTimerInt - Remove system timer interrupt. +* VQA_SetTimer - Resets current time to given tick value. +* VQA_GetTime - Return current time. +* VQA_TimerMethod - Get timer method being used. +* VQA_OpenAudio - Open sound system. +* VQA_CloseAudio - Close sound system +* VQA_StartAudio - Starts audio playback +* VQA_StopAudio - Stop audio playback. +* CopyAudio - Copy data from Audio Temp buf into Audio play buf. +* +* PRIVATE +* TimerCallback - VQA timer event. (Called by HMI) +* AutoDetect - Auto detect the sound card. +* AudioCallback - Sound system callback. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +#ifdef __WATCOMC__ +#pragma pack(4); +#endif + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + + +/*--------------------------------------------------------------------------- + * AUDIO DEFINITIONS + *-------------------------------------------------------------------------*/ + +#define HMI_SAMPLE 0x1000 +#define MAKE_LONG(a,b) (((long)(a)<<16)|(long)((b)&0x0000FFFFL)) + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +#if(VQAAUDIO_ON) +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels); +void far TimerCallback(void); +void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID); + +/* Dummy functions used to mark the start/end address of the file. */ +static void StartAddr(void); +static void EndAddr(void); + + +/*--------------------------------------------------------------------------- + * GLOBAL DATA + *-------------------------------------------------------------------------*/ + +static VQAHandleP *VQAP = NULL; +static long AudioFlags = 0; +static long TimerSysCount = 0; +static long TimerIntCount = 0; +static WORD VQATimer = 0; +static long TimerMethod; +static long VQATickCount = 0; +#endif /* VQAAUDIO_ON */ + +static long TickOffset = 0; +char *HMIDevName = ""; + +#if(VQAAUDIO_ON) +/* This is a dummy function that is used to mark the start of the module. + * It is necessary for locking the memory the module occupies. This prevents + * the virtual memory manager from swapping out this memory. + */ +static void StartAddr(void) +{ +} + + +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(VQA, Init) +* +* long VQA_StartTimerInt(VQAHandeP *, long); +* +* FUNCTION +* Initialize the HMI timer system and add our own timer event. If the +* system has already been initialized then we are given access to the +* the timer system. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(VQAHandleP *vqap, long init) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Does the caller want me to initialize the timer system? */ + if (init == 0) { + + /* If the timer system is uninitialized then it is the players + * responsibility to do it. + */ + if ((AudioFlags & VQAAUDF_TIMERINIT)==(HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } else { + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + } + + /* Increment the timer system usage count. */ + TimerSysCount++; + + /* Register the VQA_TickCount timer event. */ + if ((AudioFlags & VQAAUDF_HMITIMER) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_HMITIMER); + + /* Increment the timer interrupt usage count. */ + TimerIntCount++; + + /* Lock the memory occupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock((void *)&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Decrement the timer interrupt usage count. */ + if (TimerIntCount) { + TimerIntCount--; + } + + /* Remove the timer interrrupt if it is initialized and the use count is + * zero. Otherwise, clear the callers timer interrupt availability flag. + */ + if (((AudioFlags & VQAAUDF_HMITIMER) == (HMI_VQAINIT<Flags &= ~VQAAUDF_HMITIMER; + } + + /* Derement the timer system usage count. */ + if (TimerSysCount) { + TimerSysCount--; + } + + /* Close the timer system if it has been opened and the use count is zero. + * Otherwise, clear the callers timer system availability flag. + */ + if (((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + } + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* TimerCallback - VQA timer event. (Called by HMI) +* +* SYNOPSIS +* TimerCallback() +* +* void TimerCallback(void); +* +* FUNCTION +* Our custom timer event. This is the timer event that we register with +* HMI for system timing. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void far TimerCallback(void) +{ + VQATickCount++; +} + + +/**************************************************************************** +* +* NAME +* VQA_OpenAudio - Open sound system. +* +* SYNOPSIS +* Error = VQA_OpenAudio(VQAHandleP) +* +* long VQA_OpenAudio(VQAHandleP *); +* +* FUNCTION +* Initialize the sound system by setting the DigiHandle to and HMI +* driver handle. The timer must first be initialized before calling +* this function. +* +* INPUTS +* VQAHandleP - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_OpenAudio(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAAudio *audio; + VQAConfig *config; + VQAHeader *header; + unsigned char *driver_path; + WORD port; + long rc; + + /* Dereference data memebers for quicker access. */ + config = &vqap->Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Fail if no audio buffer or DigiCard is 0 (no sound) */ + if ((audio->Buffer == NULL) || (config->DigiCard == 0)) { + return (-1); + } + + /* Reset the buffer position to the beginning. */ + audio->CurBlock = 0; + + /* Initialize the HMI driver structure. */ + audio->sSOSInitDriver.wAllocateBuffer = _TRUE; + audio->sSOSInitDriver.wParam = 19; + audio->sSOSInitDriver.wTimerID = _SOS_NORMAL_TIMER; + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + audio->sSOSInitDriver.wSampleRate = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + audio->sSOSInitDriver.wSampleRate = ((audio->SampleRate*config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = audio->sSOSInitDriver.wSampleRate; + } else { + audio->sSOSInitDriver.wSampleRate = audio->SampleRate; + config->AudioRate = audio->SampleRate; + } + + /* If the application has already initialized HMI then set the + * necessary variables. Otherwise we must setup HMI ourself. + */ + if ((config->OptionFlags & VQAOPTF_HMIINIT) + || ((AudioFlags & VQAAUDF_DIGIINIT) != 0)) { + + /* The application MUST provide the card type! */ + if (config->DigiCard == -1) { + return (-1); + } + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /* Get the capabilities of the card being used. */ + rc = sosDIGIDetectGetCaps(config->DigiCard, &audio->DigiCaps); + sosDIGIDetectUnInit(); + + if (rc != 0) { + return (rc); + } + + audio->DigiHandle = config->DigiHandle; + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + audio->Flags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + + if ((AudioFlags & (VQAAUDF_TIMERINIT|VQAAUDF_DIGIINIT)) == 0) { + HMIDevName = "App-Specific"; + AudioFlags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + } + } else { + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /*----------------------------------------------------------------------- + * Initialize DigiHardware with port, IRQ, and DMA, and make sure + * Config.DigiCard contains the HMI ID we want to use: + * + * - If Config.DigiCard is -1, auto-detect the hardware; then, do a + * FindHardware so the GetSettings will work + * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use + * FindHardware & GetSettings to get the settings + * - If all are filled in, just use them as they are + *---------------------------------------------------------------------*/ + + /* Auto-Detect */ + if (config->DigiCard == -1) { + + /* Version 1 VQA's have only 8 bit mono audio streams. */ + if (header->Version == VQAHD_VER1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } else { + config->DigiCard = AutoDetect(&audio->DigiCaps, audio->BitsPerSample, + audio->Channels); + + /* Resort to 8bit mono */ + if (config->DigiCard == -1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } + } + + if (config->DigiCard == -1) { + sosDIGIDetectUnInit(); + return (-1); + } + } + + /* Do a FindHardware & GetSettings */ + if (config->DigiPort == -1) { + if (sosDIGIDetectFindHardware(config->DigiCard, &audio->DigiCaps, + &port)) { + + sosDIGIDetectUnInit(); + return (-1); + } + + if (sosDIGIDetectGetSettings(&audio->DigiHardware)) { + sosDIGIDetectUnInit(); + return (-1); + } + + config->DigiPort = audio->DigiHardware.wPort; + config->DigiIRQ = audio->DigiHardware.wIRQ; + config->DigiDMA = audio->DigiHardware.wDMA; + HMIDevName = (char *)audio->DigiCaps.szDeviceName; + } else { + audio->DigiHardware.wPort = config->DigiPort; + audio->DigiHardware.wIRQ = config->DigiIRQ; + audio->DigiHardware.wDMA = config->DigiDMA; + HMIDevName = "App-Specific"; + } + + sosDIGIDetectUnInit(); + + /* Initialize the DIGI system & driver */ + sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL); + audio->sSOSInitDriver.wBufferSize = config->HMIBufSize; + audio->sSOSInitDriver.lpBuffer = NULL; + audio->sSOSInitDriver.lpFillHandler = NULL; + audio->sSOSInitDriver.lpDriverMemory = NULL; + audio->sSOSInitDriver.lpTimerMemory = NULL; + audio->DigiHandle = -1; + + if ((rc = sosDIGIInitDriver(config->DigiCard, &audio->DigiHardware, + &audio->sSOSInitDriver, &audio->DigiHandle)) != 0) { + + return (rc); + } + + /*----------------------------------------------------------------------- + * Register the timer event + *---------------------------------------------------------------------*/ + + /* If the timer hasn't been init'd, do it now */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } + + /* Register the event */ + rc = sosTIMERRegisterEvent(VQA_TIMETICKS, + audio->sSOSInitDriver.lpFillHandler, &audio->DigiTimer); + + if (rc) { + sosDIGIUnInitDriver(audio->DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + return (rc); + } + + config->DigiHandle = audio->DigiHandle; + audio->Flags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + } + + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Remove the Digi event */ + if ((AudioFlags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiTimer); + } + + /* Un-init timer if necessary */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + AudioFlags &= ~VQAAUDF_TIMERINIT; + + /* Remove the driver */ + if ((audio->Flags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + } + + audio->Flags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_ISPLAYING; + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* AutoDetect - Auto detect the sound card. +* +* SYNOPSIS +* CardID = AutoDetect(DigiCaps, BitSize, Channels) +* +* long AutoDetect(_SOS_CAPABILITIES *, long, long); +* +* FUNCTION +* Autodetects the type of sound card present in the system. +* +* INPUTS +* DigiCaps - Pointer to HMI digital card capabilities structure. +* BitSize - Bits per sample size. +* Channels - Number of desired channels. +* +* RESULT +* CardID - HMI ID of the sound card found, -1 if none. +* +****************************************************************************/ + +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels) +{ + long device_id = -1; + WORD port; + long i; + long rc; + + /* Search for an 8-bit mono device */ + if (sosDIGIDetectFindFirst(digicaps, &port)) { + return (-1); + } + + channels--; + + i = 0; + while (i < 6) { + i++; + + if ((digicaps->wBitsPerSample == bitsize) + && (digicaps->wChannels == channels)) { + + break; + } + + if (sosDIGIDetectFindNext(digicaps, &port)) { + return (-1); + } + } + + /* Exit if failed to find the required device */ + if ((digicaps->wBitsPerSample != bitsize) + || (digicaps->wChannels != channels)) { + + return (-1); + } + + /* Stash the ID */ + device_id = digicaps->wDeviceID; + + /* Now that we have handled the initial pass, verify that if we found an + * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise. + */ + if ((WORD)digicaps->wDeviceID == _ADLIB_GOLD_8_MONO) { + rc = sosDIGIDetectFindNext(digicaps, &port); + + while ((i < 6) && (rc == 0)) { + i++; + + if ((digicaps->wBitsPerSample == 8) && (digicaps->wChannels == 0)) { + break; + } + + if ((rc = sosDIGIDetectFindNext(digicaps, &port)) != 0) { + break; + } + } + + /* If we don't have an error use the secondary device ID, after all, + * anything's better than an Adlib Gold. If we do have an error or there + * is nothing we can use then the device ID is already set to the adlib + * gold so just let it rip. + */ + if ((rc == 0) && ((WORD)digicaps->wDeviceID == _SBPRO_8_MONO)) { + return (digicaps->wDeviceID); + } + } + + return (device_id); +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* Set my driver handle */ + if (config->DigiHandle != -1) { + audio->DigiHandle = config->DigiHandle; + } + + if (audio->DigiHandle == (WORD)-1) { + return (-1); + } + + /*------------------------------------------------------------------------- + * Initialize the sample structure. + *-----------------------------------------------------------------------*/ + memset(&audio->sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE)); + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)audio->Buffer; + audio->sSOSSampleData.dwSampleSize = config->HMIBufSize; + audio->sSOSSampleData.wVolume = (config->Volume << 7); + audio->sSOSSampleData.wSampleID = HMI_SAMPLE; + audio->sSOSSampleData.lpCallback = AudioCallback; + + /* Set the channel flags for the type of data we have. */ + if (audio->Channels == 2) { + audio->sSOSSampleData.wChannel = _INTERLEAVED; + } else { + audio->sSOSSampleData.wChannel = _CENTER_CHANNEL; + } + + /* If the card is unable to handle stereo data the we must notify the + * sound system to convert the stereo data to mono data during playback. + */ + if ((audio->Channels - 1) > audio->DigiCaps.wChannels) { + audio->sSOSSampleData.wSampleFlags |= _STEREOTOMONO; + } + + /* If the card is unable to handle the sample size of the audio data + * then we must notify the sound system to convert the audio data to + * the proper format. + */ + if (audio->BitsPerSample != audio->DigiCaps.wBitsPerSample) { + if (audio->BitsPerSample > audio->DigiCaps.wBitsPerSample) { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8; + } else { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* Adjust the pitch if the driver is setup to playback at a different + * rate than what the sample was recorded at. + */ + if (audio->sSOSInitDriver.wSampleRate != audio->SampleRate) { + ldiv_t result; + + result = ldiv(audio->SampleRate, audio->sSOSInitDriver.wSampleRate); + audio->sSOSSampleData.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, + (short)(((long)result.rem * 0x10000L) / audio->sSOSInitDriver.wSampleRate)); + audio->sSOSSampleData.wSampleFlags |= _PITCH_SHIFT; + } + + /* Start playback */ + audio->SampleHandle = sosDIGIStartSample(audio->DigiHandle, + &audio->sSOSSampleData); + + audio->Flags |= VQAAUDF_ISPLAYING; + AudioFlags |= VQAAUDF_ISPLAYING; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Just return if not playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + sosDIGIStopSample(audio->DigiHandle, audio->SampleHandle); + audio->Flags &= ~VQAAUDF_ISPLAYING; + AudioFlags &= ~VQAAUDF_ISPLAYING; + } + + VQAP = NULL; +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void far cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + /* Suppress compiler warnings */ + wDriverHandle = wDriverHandle; + wSampleID = wSampleID; + + /* See if we're called because the buffer is empty */ + if (wAction == _SAMPLE_PROCESSED) { + + /* Compute the 'NextBlock' index */ + audio->NextBlock = audio->CurBlock + 1; + + if (audio->NextBlock >= audio->NumAudBlocks) { + audio->NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[audio->NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[audio->CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + audio->CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + audio->CurBlock = 0; + } + } else { + audio->NumSkipped++; + } + + /* Start the new buffer playing */ + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer) + + audio->PlayPosition; + + sosDIGIContinueSample(audio->DigiHandle, audio->SampleHandle, + &audio->sSOSSampleData); + + audio->SamplesPlayed += config->HMIBufSize; + } +} + +/* Dummy function used to mark the beginning address of the file. */ +static void EndAddr(void) +{ +} +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_SetTimer - Resets current time to given tick value. +* +* SYNOPSIS +* VQA_SetTimer(Time, Method) +* +* void VQA_SetTimer(long, long); +* +* FUNCTION +* Sets 'TickOffset' to a value that will make the current time look like +* the time passed in. This function allows the player to be "paused", +* by recording the time of the pause, and then setting the timer to +* that time. The timer method used by the player is also set. The method +* selected is not neccesarily the method that will be used because some +* timer methods work with only certain playback conditions. (EX: The +* audio DMA timer method cannot be used if there is not any audio +* playing.) +* +* INPUTS +* Time - Value to set current time to. +* Method - Timer method to use. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_SetTimer(VQAHandleP *vqap, long time, long method) +{ + unsigned long curtime; + + #if(VQAAUDIO_ON) + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* If the client does not have a preferencee then pick a method + * based on the state of the player. + */ + if (method == VQA_TMETHOD_DEFAULT) { + + /* If we are playing audio, use the audio DMA position. */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + method = VQA_TMETHOD_AUDIO; + } + + /* Otherwise use the HMI timer if it is initialized. */ + else if (AudioFlags & VQAAUDF_HMITIMER) { + method = VQA_TMETHOD_INT; + } + + /* If all else fails resort the the "jerky" DOS time. */ + else { + method = VQA_TMETHOD_DOS; + } + } else { + + /* We cannot use the DMA position if there isn't any audio playing. */ + if (!(AudioFlags & VQAAUDF_ISPLAYING) && (method == VQA_TMETHOD_AUDIO)) { + method = VQA_TMETHOD_INT; + } + + /* We cannot use the timer if it has not been initialized. */ + if (!(AudioFlags & VQAAUDF_HMITIMER) && (method == VQA_TMETHOD_INT)) { + method = VQA_TMETHOD_DOS; + } + } + + TimerMethod = method; + #else + method = method; + #endif + + TickOffset = 0L; + curtime = VQA_GetTime(vqap); + TickOffset = (time - curtime); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetTime - Return current time. +* +* SYNOPSIS +* Time = VQA_GetTime() +* +* unsigned long VQA_GetTime(void); +* +* FUNCTION +* This routine returns timer ticks computed one of 3 ways: +* +* 1) If audio is playing, the timer is based on the DMA buffer position: +* Compute the number of audio samples that have actually been played. +* The following internal HMI variables are used: +* +* _lpSOSDMAFillCount[drv_handle]: current DMA buffer position +* _lpSOSSampleList[drv_handle][samp_handle]: +* sampleTotalBytes: total bytes sent by HMI to the DMA buffer +* sampleLastFill: HMI's last fill position in DMA buffer +* +* So, the number of samples actually played is: +* +* sampleTotalBytes - +* where is how far ahead sampleLastFill is in front of +* _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount) +* +* These values are indices into a circular DMA buffer, so: +* +* if (sampleLastFill >= _lpSOSDMAFillCount) +* = sampleLastFill - _lpSOSDMAFillCount +* else +* = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill +* +* Note that, if using the stereo driver with mono data, you must +* divide LastFill & FillCount by 2, but not TotalBytes. If using the +* stereo driver with stereo data, you must divide all 3 variables +* by 2. +* +* 2) If no audio is playing, but the timer interrupt is running, +* VQATickCount is used as the timer +* +* 3) If no audio is playing & no timer interrupt is going, the DOS 18.2 +* system timer is used. +* +* Regardless of the method, TickOffset is used as an offset from the +* computed time. +* +* INPUTS +* NONE +* +* RESULT +* Time - Time in VQA_TIMETICKS +* +****************************************************************************/ + +unsigned long VQA_GetTime(VQAHandleP *vqap) +{ + #if(VQAAUDIO_ON) + VQAAudio *audio; + VQAConfig *config; + unsigned long fillcount; + unsigned long lastfill; + unsigned long dma_diff; + unsigned long totalbytes; + unsigned long samples; + #endif + + long ticks; + + #if(VQAAUDIO_ON) + switch (TimerMethod) { + + /* If Audio is playing then timing is based on the audio DMA buffer + * position. + */ + case VQA_TMETHOD_AUDIO: + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* Compute our current position in the audio track by getting the + * bytes processed by HMI. Then adjust the bytes processed by the + * position of the DMA fill handler, this should tell us exactly + * where we are in the audio track. + */ + fillcount = (unsigned long)(*_lpSOSDMAFillCount[audio->DigiHandle]); + lastfill = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleLastFill; + totalbytes = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleTotalBytes; + + if (totalbytes == 0) { + dma_diff = 0; + } else { + if (lastfill > fillcount) { + dma_diff = lastfill - fillcount; + } else { + dma_diff = (config->HMIBufSize - fillcount) + lastfill; + } + + if (dma_diff > totalbytes) { + dma_diff = totalbytes; + } + } + + /* Calculate the number of samples by taking the total number of + * bytes processed and divide it by the number of channels and + * bits per sample. + */ + samples = totalbytes - dma_diff; + samples -= (audio->NumSkipped * config->HMIBufSize); + samples /= (audio->Channels * (audio->BitsPerSample >> 3)); + + /* The elapsed ticks is calculated by the number of samples + * processed times the tick resolution per second divided by the + * sample rate. + */ + ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate); + ticks += TickOffset; + break; + + /* No audio playing, but timer interrupt is going; use VQATickCount */ + case VQA_TMETHOD_INT: + ticks = (VQATickCount + TickOffset); + break; + + /* No interrupts are going at all; use DOS's time */ + default: + case VQA_TMETHOD_DOS: + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + break; + } + #else + { + struct timeb mytime; + + vqap = vqap; + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + #endif + + return (ticks); +} + + +/**************************************************************************** +* +* NAME +* VQA_TimerMethod - Get timer method being used. +* +* SYNOPSIS +* Method = VQA_TimerMethod() +* +* long VQA_TimerMethod(void); +* +* FUNCTION +* Returns the ID of the current timer method being used. +* +* INPUTS +* NONE +* +* RESULT +* Method - Method used for the timer. +* +****************************************************************************/ + +long VQA_TimerMethod(void) +{ + #if(VQAAUDIO_ON) + return (TimerMethod); + #else + return (VQA_TMETHOD_DOS); + #endif +} + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + diff --git a/VQ/VQA32/BCC32.CFG b/VQ/VQA32/BCC32.CFG new file mode 100644 index 0000000..7e1d1b9 --- /dev/null +++ b/VQ/VQA32/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vqa32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/VQ/VQA32/CAPTION.CPP b/VQ/VQA32/CAPTION.CPP new file mode 100644 index 0000000..04d5273 --- /dev/null +++ b/VQ/VQA32/CAPTION.CPP @@ -0,0 +1,400 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.c +* +* DESCRIPTION +* Text caption process/display manager. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "caption.h" + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define NUM_NODES 3 + +/* Function prototypes. */ +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext); +static void RemCaptionNode(CaptionList *list, CaptionNode *node); + + +/**************************************************************************** +* +* NAME +* OpenCaptions - Initialize the caption system. +* +* SYNOPSIS +* OpenCaptions(Captions, Font) +* +* CaptionInfo *OpenCaptions(void *, void *); +* +* FUNCTION +* Allocate and initialize resources used by the caption system. +* +* INPUTS +* Captions - Captions to process. +* Font - Font to use to display captions. +* +* RESULT +* CaptionInfo - Caption information structure. +* +* SEE ALSO +* CloseCaption +* +****************************************************************************/ + +CaptionInfo *OpenCaptions(void *captions, void *font) +{ + CaptionInfo *cap = NULL; + CaptionNode *node; + FontInfo *fi; + long i; + + /* Allocate memory for the captioning system. */ + cap = (CaptionInfo *)malloc(sizeof(CaptionInfo) + (sizeof(CaptionNode) + * NUM_NODES)); + + if (cap != NULL) { + memset(cap,0,(sizeof(CaptionInfo) + (sizeof(CaptionNode) * NUM_NODES))); + cap->Buffer = captions; + cap->Next = (CaptionText *)captions; + + /* Initialize font */ + fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk); + cap->Font = font; + cap->FontHeight = fi->MaxHeight; + cap->FontWidth = fi->MaxWidth; + + /* Initialize list header. */ + cap->List.Head = (CaptionNode *)&cap->List.Tail; + cap->List.Tail = NULL; + cap->List.TailPred = (CaptionNode *)&cap->List.Head; + + /* Link nodes. */ + node = (CaptionNode *)((char *)cap + sizeof(CaptionInfo)); + + for (i = 0; i < NUM_NODES; i++) { + node->Succ = cap->List.Head; + cap->List.Head = node; + node->Pred = (CaptionNode *)&cap->List.Head; + node->Succ->Pred = node; + + /* Next node. */ + node = (CaptionNode *)((char *)node + sizeof(CaptionNode)); + } + } + + return (cap); +} + + +/**************************************************************************** +* +* NAME +* CloseCaptions - Shutdown the caption system. +* +* SYNOPSIS +* CloseCaptions(CaptionInfo) +* +* void CloseCaptions(CaptionInfo *); +* +* FUNCTION +* Free the resources allocated by the caption system. +* +* INPUTS +* CaptionInfo - Caption information structure. +* +* RESULT +* NONE +* +* SEE ALSO +* OpenCaptions +* +****************************************************************************/ + +void CloseCaptions(CaptionInfo *cap) +{ + free(cap); +} + + +/**************************************************************************** +* +* NAME +* DoCaption - Process and display caption text. +* +* SYNOPSIS +* DoCaption(Captions, Frame) +* +* void DoCaption(CaptionInfo *, unsigned long); +* +* FUNCTION +* Process the caption events. +* +* INPUTS +* Captions - Pointer to CaptionInfo structure. +* Frame - Current frame number being processed. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DoCaptions(CaptionInfo *cap, unsigned long frame) +{ + CaptionText *captext; + CaptionNode *node; + void const *oldfont; + long width; + long i; + + /* Initialize variables. */ + oldfont = Set_Font((char *)cap->Font); + + /*------------------------------------------------------------------------- + * Process the captions that are on the active queue. + *-----------------------------------------------------------------------*/ + node = cap->List.Head; + + while ((node->Succ != NULL) && (node->Flags & CNF_USED)) { + captext = node->Captext; + + /* Clear the any previous captions that have expired. */ + if (captext->OffFrame <= frame) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + + /* Remove the caption from the active list. */ + node = node->Pred; + RemCaptionNode(&cap->List, node->Succ); + } else { + if (captext->CPF != 0) { + + /* If a NULL terminator is not found then display the next set of + * characters, otherwise remove the node. + */ + if (*node->Char != 0) { + Set_Font_Palette_Range(&captext->BgPen, 0, 1); + + for (i = 0; i < captext->CPF; i++) { + + /* Check for terminator. */ + if (*node->Char == 0) { + captext->CPF = 0; + break; + } + /* Check for newline. */ + else if (*node->Char == 0x0D) { + node->Char++; + node->CurX = captext->Xpos; + node->CurY += cap->FontHeight; + node->BoundH += cap->FontHeight; + } + + Draw_Char(*node->Char, node->CurX, node->CurY); + node->CurX += Char_Pixel_Width(*node->Char); + node->Char++; + } + } + } else if (captext->Flags & CTF_FLASH) { + if (frame & 4) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + } else { + Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + } + } + } + + /* Next node. */ + node = node->Succ; + } + + /*------------------------------------------------------------------------- + * Process any captions that are waiting to be activated. + *-----------------------------------------------------------------------*/ + captext = cap->Next; + + while (captext->OnFrame <= frame) { + + width = String_Pixel_Width(captext->Text); + + switch (captext->Flags & CTF_JUSTIFY) { + case CTF_RIGHT: + captext->Xpos = (319 - width); + break; + + case CTF_LEFT: + captext->Xpos = 0; + break; + + case CTF_CENTER: + captext->Xpos = (160 - (width / 2)); + break; + + default: + break; + } + + /* Display the text and record its bounding box. */ + if (captext->CPF == 0) { + i = Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = (cap->FontHeight * i); + } else { + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = cap->FontHeight; + } + + /* Next */ + cap->Next = (CaptionText *)(((char *)captext) + captext->Size); + captext = cap->Next; + } + + Set_Font(oldfont); +} + + +/**************************************************************************** +* +* NAME +* AddCaptionNode - Add a caption to the processing list. +* +* SYNOPSIS +* Node = AddCaptionNode(List, Caption) +* +* CaptionNode *AddCaptionNode(CaptionList *, CaptionText *); +* +* FUNCTION +* Add a caption the caption processing list. +* +* INPUTS +* List - Caption processing list. +* Caption - Caption to add to the list. +* +* RESULT +* Node - Pointer to node, otherwise NULL if error. +* +****************************************************************************/ + +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext) +{ + CaptionNode *node = NULL; + + /* If this list is not full. */ + node = list->TailPred; + + if (!(node->Flags & CNF_USED)) { + + /* Remove the node from the tail. */ + node->Pred->Succ = node->Succ; + list->TailPred = node->Pred; + + node->Flags |= CNF_USED; + node->Captext = captext; + node->Char = captext->Text; + node->CurX = captext->Xpos; + node->CurY = captext->Ypos; + + /* Add the node to the head. */ + node->Succ = list->Head; + list->Head = node; + node->Pred = (CaptionNode *)&list->Head; + node->Succ->Pred = node; + } + + return (node); +} + + +/**************************************************************************** +* +* NAME +* RemCaptionNode - Remove a caption from the processing list. +* +* SYNOPSIS +* RemCaptionNode(List, Node) +* +* void RemCaptionNode(CaptionList *, CaptionNode *); +* +* FUNCTION +* Remove the caption from the processing list. Mark the node as unused +* and put it at the tail of the list. +* +* INPUTS +* List - Caption processing list. +* Node - Caption node to remove. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void RemCaptionNode(CaptionList *list, CaptionNode *node) +{ + /* If the nodes successor is null then we are at the tail. */ + if (node->Succ != NULL) { + + /* Mark the node as unused. */ + node->Flags = 0; + + /* Relink the node to the tail. */ + node->Succ->Pred = node->Pred; + node->Pred->Succ = node->Succ; + node->Succ = (CaptionNode *)&list->Tail; + node->Pred = list->TailPred; + list->TailPred->Succ = node; + list->TailPred = node; + } +} + + + diff --git a/VQ/VQA32/CAPTION.H b/VQ/VQA32/CAPTION.H new file mode 100644 index 0000000..a6d35d7 --- /dev/null +++ b/VQ/VQA32/CAPTION.H @@ -0,0 +1,121 @@ +/* +** 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 . +*/ + +#ifndef VQACAPTION_H +#define VQACAPTION_H +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.h +* +* DESCRIPTION +* Text caption definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* CaptionNode: Node describing a caption to process. + * + * Succ - Pointer to the next node in the list (successor). + * Pred - Pointer to the previous node in the list (predecessor). + * Flags - Status flags. + * CapText - Pointer to the CaptionText being processed. + * Char - Pointer to current character in the string. + * CurX - Current X position. + * CurY - Current Y position. + * BoundW - Bounding width of text. + * BoundH - Bounding height of text. + */ +typedef struct _CaptionNode { + struct _CaptionNode *Succ; + struct _CaptionNode *Pred; + unsigned short Flags; + CaptionText *Captext; + char *Char; + unsigned short CurX; + unsigned short CurY; + unsigned short BoundW; + unsigned short BoundH; +} CaptionNode; + +/* CaptionNode flag definitions. */ +#define CNB_USED 0 /* This node is being used. */ +#define CNF_USED (1<. +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* config.c +* +* DESCRIPTION +* Player configuration routines. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +****************************************************************************/ + +#include +#include +#include +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Default configuration structure. */ +static VQAConfig _defaultconfig = { + + /* DrawerCallback: This is a function that is called for every frame + * in the movie. + */ + NULL, + + /* EventHandler: This is a function that is called for every event that + * the client requested to be notified about. + */ + NULL, + + /* NotifyFlags: Flags representing the events the client wishes to be + * notified about during playback. + */ + NULL, + + /* Vmode: Video mode to use. */ + MCGA, + + /* VBIBit: Vertical blank bit polarity. */ + -1, + + /* ImageBuf: Pointer to image buffer to draw into. */ + NULL, + + /* ImageWidth, ImageHeight: Width and height dimensions of image buffer. + * A width and height value of -1 tells the player to consider the image + * buffer as having the same dimensions as the frames in the movie. + */ + 320, 200, /* Image width and height */ + + /* X1, Y1: These are the coordinates to put the movies frame in the image + * buffer. Values of -1 tell the drawer to center the frames in the buffer. + */ + -1, -1, + + /* FrameRate: The rate to load the frames at. A value of -1 tells the + * player to use the framerate of the movie. + */ + -1, + + /* DrawRate: The rate to draw the frames at. A value of -1 tells the + * player to use the framerate of the movie. A value of 0 tells the player + * to use a fixed rate based on the frame size. + */ + -1, + + /* TimerMethod: Timer method to use for playback. */ + -1, + + /* DrawFlags: Various drawing related flags. */ + 0, + + /* OptionFlags: Various player options. */ + VQAOPTF_AUDIO, + + /* NumFrameBufs: The number of frame buffers to allocate/use. */ + 6, + + /* NumCBBufs: The number of codebook buffers to allocate/use. */ + 3, + + /* VocFile: Filename of audio track override. A value of 0 tells the + * player not to override the movies audio track. + */ + NULL, + + /* AudioBuf: Audio buffer to use. A value of 0 tells the player that + * it has to allocate a buffer itself. + */ + NULL, + + /* AudioBufSize: Size of audio buffer to use/allocate. A value of -1 + * tells the player to compute the buffer size from the audio + * information in the movie. + */ + -1, + + /* AudioRate: Audio playback rate in samples per second. A value of -1 + * tells the player to use the audio rate of the movie. + */ + -1, + + /* Volume: Volume level to playback audio track. */ + 0x00FF, + + /* HMIBufSize: Size of HMIs internal buffer. */ + 2048L, + + /* DigiHandle: Handle to an initialized HMI sound driver. A value of -1 + * tells the player it must initialize the HMI sound driver itself. + */ + -1, + + /* DigiCard: HMI ID of audio card to use. A value of 0 tells the player + * not to use any card. A value of -1 tells the player to autodetect the + * card in the system. + */ + -1, + + /* DigiPort: Port address of the sound card. A value of -1 tells the player + * to autodetect this address. + */ + -1, + + /* DigiIRQ: Interrupt number of sound card. A value of -1 tells the player + * to autodetect the interrupt used by the card. + */ + -1, + + /* DigiDMA: DMA channel of the sound card. A value of -1 tells the player + * to autodetect the channel used by the card. + */ + -1, + + /* Language: Prefered language. */ + 0, + + /* CaptionFont: Caption text font. */ + NULL, + + /* EVAFont: EVA text font. */ + NULL, +}; + +/* Supported video modes. */ +#if(VQAVIDEO_ON) +enum VMTAGS { + VMTAG_NONE = 0, + + #if(VQAMCGA_ON) + VMTAG_MCGA, + VMTAG_MCGA_BUF, + #endif + + #if(VQAXMODE_ON) + VMTAG_XMODE320X200, + VMTAG_XMODE320X200_BUF, + VMTAG_XMODE320X200_VRAM, + VMTAG_XMODE320X240, + VMTAG_XMODE320X240_BUF, + VMTAG_XMODE320X240_VRAM, + #endif + + #if(VQAVESA_ON) + VMTAG_VESA640X480_BUF, + VMTAG_VESA640X480_X2, + VMTAG_VESA320X200, + VMTAG_VESA320X200_BUF, + #endif +}; + +typedef struct _VideoModeTag { + char const *token; + long id; +} VideoModeTag; + +VideoModeTag VideoModeTags[] = { + {"NONE",VMTAG_NONE}, + + #if(VQAMCGA_ON) + {"MCGA", VMTAG_MCGA}, + {"MCGA_BUF",VMTAG_MCGA_BUF}, + #endif /* VQAMCGA_ON */ + + #if(VQAXMODE_ON) + {"XMODE_320X200", VMTAG_XMODE320X200}, + {"XMODE_320X200_BUF", VMTAG_XMODE320X200_BUF}, + {"XMODE_320X200_VRAM",VMTAG_XMODE320X200_VRAM}, + {"XMODE_320X240", VMTAG_XMODE320X240}, + {"XMODE_320X240_BUF", VMTAG_XMODE320X240_BUF}, + {"XMODE_320X240_VRAM",VMTAG_XMODE320X240_VRAM}, + #endif /* VQAXMODE_ON */ + + #if(VQAVESA_ON) + {"VESA_640X480_BUF",VMTAG_VESA640X480_BUF}, + {"VESA_640X480_X2", VMTAG_VESA640X480_X2}, + {"VESA_320X200", VMTAG_VESA320X200}, + {"VESA_320X200_BUF",VMTAG_VESA320X200_BUF}, + #endif /* VQAVESA_ON */ + + {NULL, NULL} +}; +#endif /* VQAVIDEO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* +* SYNOPSIS +* VQA_INIConfig(Config) +* +* void VQA_INIConfig(VQAConfig *); +* +* FUNCTION +* Initializes the configuration structure from the player INI file. +* +* INPUTS +* Config - Pointer to VQAConfig structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_INIConfig(VQAConfig *config) +{ + char *ininame; + char buf[80]; + long i; + + /* Set all Config entries to 0. */ + memset(config, 0, sizeof(VQAConfig)); + + /* Retrieve player INI filename from an enviroment variable if + * it is provided. + */ + if ((ininame = getenv("VQACFG")) == NULL) { + ininame = "PLAYER.INI"; + } + + /*------------------------------------------------------------------------- + * VIDEO MODE AND DRAW FLAGS + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + /* Get video mode from INI */ + GetINIString("Player", "PlayerMode", "MCGA", buf, 80, ininame); + + /* Search supported modes for a match. */ + i = 0; + + while (VideoModeTags[i].token != NULL) { + if (stricmp(buf, VideoModeTags[i].token) == 0) { + break; + } + i++; + } + + /* Setup for requested mode */ + switch (VideoModeTags[i].id) { + + /* MCGA direct */ + #if(VQAMONO_ON) + case VMTAG_MCGA: + config->Vmode = MCGA; + break; + + /* MCGA buffered */ + case VMTAG_MCGA_BUF: + config->Vmode = MCGA; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAMCGA_ON */ + + /* XMODE direct (320x200) */ + #if(VQAXMODE_ON) + case VMTAG_XMODE320X200: + config->Vmode = XMODE_320X200; + break; + + /* XMODE buffered (320x200) */ + case VMTAG_XMODE320X200_BUF: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x200) */ + case VMTAG_XMODE320X200_VRAM: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + + /* XMODE direct (320x240) */ + case VMTAG_XMODE320X240: + config->Vmode = XMODE_320X240; + break; + + /* XMODE buffered (320x240) */ + case VMTAG_XMODE320X240_BUF: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x240) */ + case VMTAG_XMODE320X240_VRAM: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + #endif /* VQAXMODE_ON */ + + /* VESA buffered (640x480_256) */ + #if(VQAVESA_ON) + case VMTAG_VESA640X480_BUF: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* VESA buffered scaled (640x480_256) */ + case VMTAG_VESA640X480_X2: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= (VQACFGF_BUFFER|VQACFGF_SCALEX2); + break; + + /* VESA direct (320x200_32k) */ + case VMTAG_VESA320X200: + config->Vmode = VESA_320X200_32K_1; + break; + + /* VESA buffered (320x200_32k) */ + case VMTAG_VESA320X200_BUF: + config->Vmode = VESA_320X200_32K_1; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAVESA_ON */ + + /* Default to MCGA direct */ + VMTAG_NONE: + default: + config->Vmode = MCGA; + break; + } + #endif /* VQAVIDEO_ON */ + + /* Get framerate and drawrate. */ + GetINIString("Player", "FrameRate", "-1", buf, 80, ininame); + config->FrameRate = atoi(buf); + + GetINIString("Player", "DrawRate", "Variable", buf, 80, ininame); + + if (!stricmp(buf, "Variable")) { + config->DrawRate = -1; + } else { + config->DrawRate = 0; + } + + /*------------------------------------------------------------------------- + * AUDIO SETTINGS + *-----------------------------------------------------------------------*/ + GetINIString("Player", "AudioRate", "-1", buf, 80, ininame); + config->AudioRate = atoi(buf); + + /* OptionFlags */ + GetINIString("Player", "SoundEnabled", "True", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_AUDIO; + } else { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + + /* Default audio settings. */ + config->AudioBufSize = 32768U; + config->HMIBufSize = 2048; + config->DigiHandle = -1; + config->Volume = 0x00FF; + config->DigiCard = 0xFFFF; + config->DigiPort = -1; + config->DigiIRQ = -1; + config->DigiDMA = -1; + + /* Configure sound hardware */ + GetINIString("Player", "Port", "-1", buf, 80, ininame); + + if (!stricmp(buf, "-1")) { + config->DigiPort = -1; + } else { + sscanf(buf, "%x", &config->DigiPort); + } + + GetINIString("Player", "IRQ", "-1", buf, 80, ininame); + config->DigiIRQ = atoi(buf); + GetINIString("Player", "DMA", "-1", buf, 80, ininame); + config->DigiDMA = atoi(buf); + + /*------------------------------------------------------------------------- + * GENERAL OPTIONS + *-----------------------------------------------------------------------*/ + + /* Enable/Disable single stepping */ + GetINIString("Player", "SingleStep", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_STEP; + config->DrawFlags |= VQACFGF_NOSKIP; + } else { + config->OptionFlags &= (~VQAOPTF_STEP); + } + + /* Enable/Disable Slowpalette */ + GetINIString("Player", "SlowPalette", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_SLOWPAL; + } else { + config->OptionFlags &= (~VQAOPTF_SLOWPAL); + } + + /* Enable/Disable monochrome display */ + GetINIString("Player", "MonoOutput", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_MONO; + } else { + config->OptionFlags &= (~VQAOPTF_MONO); + } + + /* Frame and codebook buffers */ + config->NumFrameBufs = 6; + config->NumCBBufs = 3; +} + + +/**************************************************************************** +* +* NAME +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +* SYNOPSIS +* VQA_DefaultConfig(Config); +* +* void VQA_DefaultConfig(VQAConfig *); +* +* FUNCTION +* Initialize configuration with default settings. +* +* INPUTS +* Config - Pointer to VQA configuration structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_DefaultConfig(VQAConfig *config) +{ + memcpy(config, &_defaultconfig, sizeof(VQAConfig)); +} + + + + diff --git a/VQ/VQA32/DRAWER.BAK b/VQ/VQA32/DRAWER.BAK new file mode 100644 index 0000000..ef710c4 --- /dev/null +++ b/VQ/VQA32/DRAWER.BAK @@ -0,0 +1,2225 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* drawer.c +* +* DESCRIPTION +* Frame drawing and page flip control. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* February 8, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Configure_Drawer - Configure the drawer routines. +* +* PRIVATE +* Select_Frame - Selects frame to draw and preforms frame +* skip. +* Prepare_Frame - Process/Decompress frame information. +* DrawFrame_Xmode - Draws a frame directly to Xmode screen. +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident +* Codebook. +* PageFlip_Xmode - Page flip Xmode display. +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* PageFlip_MCGA - Page flip MCGA display. +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* DrawFrame_VESA640 - Draws a frame in VESA640 format. +* DrawFrame_VESA320_32K - Draws a frame to VESA320_32K screen. +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* PageFlip_VESA - Page flip VESA display. +* DrawFrame_Buffer - Draw a frame to a buffer. +* PageFlip_Nop - Do nothing page flip. +* UnVQ_Nop - Do nothing UnVQ. +* Mask_Rect - Sets non-drawable rectangle in image. +* Mask_Pointers - Mask vector pointer that are in the mask +* rectangle. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "unvq.h" +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ +static long Select_Frame(VQAHandleP *vqap); +static void Prepare_Frame(VQAData *vqabuf); + +#if(VQAMCGA_ON) +static long DrawFrame_MCGABuf(VQAHandle *vqa); +static void PageFlip_MCGABuf(VQAHandle *vqa); +static long DrawFrame_MCGA(VQAHandle *vqa); +static void PageFlip_MCGA(VQAHandle *vqa); +#endif /* VQAMCGA_ON */ + +#if(VQAXMODE_ON) +static long DrawFrame_XmodeBuf(VQAHandle *vqa); +static long DrawFrame_Xmode(VQAHandle *vqa); +static long DrawFrame_XmodeVRAM(VQAHandle *vqa); +static void PageFlip_Xmode(VQAHandle *vqabuf); +#endif /* VQAXMODE_ON */ + +#if(VQAVESA_ON) +static long DrawFrame_VESA640(VQAHandle *vqa); +static long DrawFrame_VESA320_32K(VQAHandle *vqa); +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa); +static void PageFlip_VESA(VQAHandle *vqabuf); +#endif /* VQAVESA_ON */ + +static long DrawFrame_Buffer(VQAHandle *vqa); +static void PageFlip_Nop(VQAHandle *vqa); + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); +#endif + +#if(0) +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2); +static void Mask_Pointers(VQAData *vqabuf); +#endif + + +/**************************************************************************** +* +* NAME +* VQA_Configure_Drawer - Configure the drawer routines. +* +* SYNOPSIS +* VQA_Configure_Drawer(VQA) +* +* void VQA_Configure_Drawer(VQAHandleP *); +* +* FUNCTION +* Configure the drawing system for the current movie and configuration +* options. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Configure_Drawer(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAHeader *header; + VQADrawer *drawer; + long origin; + long blkdim; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + header = &vqap->Header; + config = &vqap->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((config->X1 == -1) && (config->Y1 == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((drawer->ImageWidth - header->ImageWidth) / 2); + drawer->Y1 = ((drawer->ImageHeight - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = config->X1; + drawer->Y1 = config->Y1; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = config->X1; + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (drawer->ImageWidth - config->X1); + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE UNVQ ROUTINE FOR THE SPECIFIED VIDEO MODE AND BLOCK SIZE. + *-----------------------------------------------------------------------*/ + + /* Pre-compute commonly used values for speed. */ + drawer->BlocksPerRow = header->ImageWidth / header->BlockWidth; + drawer->NumRows = header->ImageHeight / header->BlockHeight; + drawer->NumBlocks = drawer->BlocksPerRow * drawer->NumRows; + blkdim = BLOCK_DIM(header->BlockWidth, header->BlockHeight); + + /* Initialize draw routine vectors to a NOP routine in order to prevent + * a crash. + */ + vqabuf->UnVQ = UnVQ_Nop; + vqabuf->Page_Flip = PageFlip_Nop; + + /* If the client specifies buffering then go ahead an set the unvq + * vector. All of the buffered modes use the same unvq routines. + */ + if (config->DrawFlags & VQACFGF_BUFFER) { + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2; + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + + default: + break; + } + } + + /* Initialize the draw vectors for the specified video mode. */ + switch (config->Vmode) { + + /* MCGA */ + #if(VQAMCGA_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_MCGABuf; + vqabuf->Page_Flip = PageFlip_MCGABuf; + } else { + vqabuf->Draw_Frame = DrawFrame_MCGA; + vqabuf->Page_Flip = PageFlip_MCGA; + + /* MCGA uses the same unvq routines that are used for unvqing + * to a buffer because MCGA mode is just another buffer. However, + * instead of drawing to an allocated RAM buffer we are drawing + * directly to the VRAM video buffer. + */ + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + #if(0) + vqabuf->UnVQ = UnVQ_4x2; + #else + if (config->DrawFlags & VQACFGF_WOOFER) { + vqabuf->UnVQ = UnVQ_4x2_Woofer; + } else { + vqabuf->UnVQ = UnVQ_4x2; + } + #endif + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((320 * drawer->Y1) + drawer->X1); + break; + #endif /* VQAMCGA_ON */ + + /* XMODE */ + #if(VQAXMODE_ON) + case XMODE_320X200: + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_XmodeBuf; + vqabuf->Page_Flip = PageFlip_Xmode; + } else { + vqabuf->Page_Flip = PageFlip_Xmode; + + if (config->DrawFlags & VQACFGF_VRAMCB) { + vqabuf->Draw_Frame = DrawFrame_XmodeVRAM; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_XmodeCB; + break; + #endif /* VQABLOCK_4X2 */ + } + } else { + vqabuf->Draw_Frame = DrawFrame_Xmode; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_Xmode; + break; + #endif /* VQABLOCK_4X2 */ + } + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = (((320 / 4) * drawer->Y1) + (drawer->X1 / 4)); + break; + #endif /* VQAXMODE_ON */ + + /* VESA */ + #if(VQAVESA_ON) + + /* Currently this is a buffered mode, but should be optimized for + * for screen direct. + */ + case VESA_640X480_256: + vqabuf->Draw_Frame = DrawFrame_VESA640; + vqabuf->Page_Flip = PageFlip_VESA; + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_VESA320_32KBuf; + } else { + vqabuf->Draw_Frame = DrawFrame_VESA320_32K; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_VESA320_32K; + break; + #endif + } + } + break; + #endif /* VQAVESA_ON */ + + /* Purely buffered (Video refresh is up to the client. */ + default: + vqabuf->Draw_Frame = DrawFrame_Buffer; + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((drawer->ImageWidth * drawer->Y1)+drawer->X1); + break; + } +} + + +/**************************************************************************** +* +* NAME +* Select_Frame - Selects frame to draw and preforms frame skip. +* +* SYNOPSIS +* Error = Select_Frame(VQA) +* +* long Select_Frame(VQAHandleP *); +* +* FUNCTION +* Select a frame to draw. This is were the frame skipping/delay is +* performed. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long Select_Frame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAConfig *config; + VQAFrameNode *curframe; + long curtime; + long desiredframe; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Make sure the current frame is drawable. If the frame is not ready + * then we must wait for the loader to catch up. + */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + drawer->WaitsOnLoader++; + return (VQAERR_NOBUFFER); + } + + /* If single stepping then return with the next frame.*/ + if (config->OptionFlags & VQAOPTF_STEP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Find the frame # we should play (rounded to nearest frame): */ + curtime = VQA_GetTime(); + desiredframe = ((curtime * config->FrameRate) / VQA_TIMETICKS); + + #if(VQAMONO_ON) + drawer->DesiredFrame = desiredframe; + #endif + + /* Handle the cases where the player is going so fast that it's not time + * to draw this frame yet. + * + * - If the Drawer is using a slower frame rate than the Loader, use a + * delta-time-based wait; otherwise, use the frame number as the wait. + */ + if (config->DrawRate != config->FrameRate) { + if (curtime - drawer->LastTime < (VQA_TIMETICKS / config->DrawRate)) { + return (VQAERR_NOT_TIME); + } + } else { + if (curframe->FrameNum > desiredframe) { + return (VQAERR_NOT_TIME); + } + } + + /* Make sure we draw at least 5 frames per second */ + if ((curframe->FrameNum - drawer->LastFrame) >= (config->FrameRate / 5)) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* If frame skipping is disabled then draw every frame. */ + if (config->DrawFlags & VQACFGF_NOSKIP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Handle the case where the player is going too slow, so we have to skip + * some frames: + * + * - If this is a Key Frame, draw it + * - If this frame's # is less than what we're supposed to draw, skip it + * (Because the 1st 'desiredframe' will be 0, FrameNum MUST be typecast + * to signed WORD for the comparison; otherwise, the comparison uses + * UWORDs, and the first frame is always skipped.) + * - If this is a palette-set frame, set the palette before skipping it + * - Loop until we get the frame we need, or there's no frames available + */ + while (1) { + + /* No frame available; return */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + return (VQAERR_NOBUFFER); + } + + /* Force drawing of a Key Frame */ + if (curframe->Flags & VQAFRMF_KEY) { + break; + } + + /* Skip the frame */ + if (curframe->FrameNum < desiredframe) { + + /* Handle a palette in a skipped frame: + * + * - Stash the palette in Drawer.Palette_24 + * - Set the Drawer.Flags VQADRWF_SETPAL bit, to tell the page-flip + * routines that this palette must be set + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + + /* Un-LCW if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)curframe->Palette, + vqabuf->Max_Pal_Size); + + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Stash the palette */ + memcpy(drawer->Palette_24, curframe->Palette, curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + drawer->Flags |= VQADRWF_SETPAL; + } + + /* Invoke callback with NULL screen ptr */ + if (config->DrawerCallback != NULL) { + if (config->DrawerCallback(NULL, curframe->FrameNum)) { + return VQAERR_READ; + } + } + + /* Skip the frame */ + curframe->Flags = 0L; + curframe = curframe->Next; + drawer->CurFrame = curframe; + drawer->NumSkipped++; + } else { + break; + } + } + + drawer->LastFrame = curframe->FrameNum; + drawer->LastTime = curtime; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Prepare_Frame - Process/Decompress frame information. +* +* SYNOPSIS +* Prepare_Frame(VQAData) +* +* void Prepare_Frame(VQAData *); +* +* FUNCTION +* Decompress and preprocess the various frame elements (codebook, +* pointers, palette, etc...) +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Prepare_Frame(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + VQACBNode *codebook; + + /* Dereference commonly used data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + codebook = curframe->Codebook; + + /* Decompress the codebook, if needed */ + if (codebook->Flags & VQACBF_CBCOMP) { + + /* Decompress the codebook. */ + LCW_Uncompress((char *)codebook->Buffer + codebook->CBOffset, + (char *)codebook->Buffer, vqabuf->Max_CB_Size); + + /* Mark as uncompressed for the next time we use it */ + codebook->Flags &= (~VQACBF_CBCOMP); + } + + /* Decompress the palette, if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset,(char *)curframe->Palette,vqabuf->Max_Pal_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Decompress the pointer data, if needed */ + if (curframe->Flags & VQAFRMF_PTRCOMP) { + LCW_Uncompress((char *)curframe->Pointers + curframe->PtrOffset, + (char *)curframe->Pointers, vqabuf->Max_Ptr_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + } + + /* Mask the pointers */ + #if(0) + Mask_Pointers(vqabuf); + #endif +} + + +#if(VQAXMODE_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_Xmode - Draws a frame in Xmode format (Screen direct). +* +* SYNOPSIS +* Error = DrawFrame_Xmode(VQA) +* +* long DrawFrame_Xmode(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(scrn, curframe->FrameNum); + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_XmodeBuf(VQA) +* +* long DrawFrame_XmodeBuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into ImageBuf +* - Copy ImageBuf to video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf into the correct part of the +* Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAHeader *header; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + header = &((VQAHandleP *)vqa)->Header; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + header->ImageWidth); + + /* Copy the buffer */ + scrn = GetXHidPage(); + + if ((header->ImageWidth < 320) || (header->ImageHeight < 200)) { + Xmode_Blit(drawer->ImageBuf, scrn + drawer->ScreenOffset, + header->ImageWidth, header->ImageHeight); + } else { + Xmode_BufferCopy_320x200(drawer->ImageBuf,scrn + drawer->ScreenOffset); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(scrn, curframe->FrameNum); + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident Codebook. +* +* SYNOPSIS +* Error = DrawFrame_XmodeVRAM(VQA) +* +* long DrawFrame_XmodeVRAM(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Download Codebook if it isn't already (do this before skipping +* frames, so Select_Frame will smooth out the delay) +* - Skip frames +* - UnLCW frame +* - Convert Pointers to VRAM format +* - Un-VQ by copying codebook blocks within VRAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeVRAM(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Download codebook to VRAM */ + if ((curframe->Codebook->Flags & VQACBF_DOWNLOADED) == 0) { + Upload_4x2CB(curframe->Codebook->Buffer, + ((VQAHandleP *)vqa)->Header.CBentries + + ((VQAHandleP *)vqa)->Header.Num1Colors); + + curframe->Codebook->Flags |= VQACBF_DOWNLOADED; + } + + /* Convert pointers to VRAM format */ + XlatePointers(curframe->Pointers, drawer->NumBlocks); + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(scrn, curframe->FrameNum); + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Xmode - Page flip Xmode display. +* +* SYNOPSIS +* PageFlip_Xmode(VQA) +* +* void PageFlip_Xmode(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned char *pal; + VQAConfig *config; + long palsize; + long slowpal; + + /* Dereference commonly used data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for NoVB (active scan) + * - Flip the page (doesn't take effect until next active scan + * - Wait for VB + * - Set the palette + * - Otherwise, just flip the page + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } else { + FlipXPage(); + } +} +#endif /* VQAXMODE_ON */ + + +#if(VQAMCGA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* +* SYNOPSIS +* Error = DrawFrame_MCGA(VQA) +* +* long DrawFrame_MCGA(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set palette +* UnVQ to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + /* Dereference commonly used data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Select the frame to draw. */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGA - Page flip MCGA display. +* +* SYNOPSIS +* PageFlip_MCGA(VQA) +* +* void PageFlip_MCGA(VQAHandle *); +* +* FUNCTION +* Since the MCGA mode only has one buffer, the drawing is actually done +* at this point. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *ptrs; + unsigned char *cb; + long palsize; + long slowpal; + long blocksperrow; + long numrows; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /*------------------------------------------------------------------------- + * WAIT FOR THE VERTICAL BLANK TO SET THE PALETTE. + *-----------------------------------------------------------------------*/ + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Wait for the VBlank. */ + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + /* Set the palette. */ + if (curframe->Flags & VQAFRMF_PALETTE) { + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + } + + /*------------------------------------------------------------------------- + * UNVQ THE FRAME DIRECTLY TO THE MCGA SCREEN. + *-----------------------------------------------------------------------*/ + + /* Get screen address, blocks per row and number of rows. */ + #ifndef PHARLAP_TNT + scrn = (unsigned char *)(0xA0000 + (unsigned long)drawer->ScreenOffset); + #else + FP_SET(scrn, drawer->ScreenOffset, 0x1C); + #endif + + blocksperrow = drawer->BlocksPerRow; + numrows = drawer->NumRows; + ptrs = curframe->Pointers; + cb = curframe->Codebook->Buffer; + vqabuf->UnVQ(cb, ptrs, scrn, blocksperrow, numrows, 320); + + /* Process captions. */ + if ((((VQAHandleP *)vqa)->Caption != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + DoCaptions(((VQAHandleP *)vqa)->Caption, curframe->FrameNum); + } + + if ((((VQAHandleP *)vqa)->EVA != NULL) + && (config->OptionFlags & VQAOPTF_EVA)) { + DoCaptions(((VQAHandleP *)vqa)->EVA, curframe->FrameNum); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if (config->DrawerCallback(NULL, curframe->FrameNum)) { + vqabuf->Flags |= VQADATF_DDONE; + } + } +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_MCGABuf(VQA) +* +* long DrawFrame_MCGABuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled (can't use imgbuf til User_Update's done) +* - Un-VQ into ImageBuf +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set Palette from Flipper.CurFrame +* copy ImageBuf to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf onto the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf; + #else + FP_SET(scrn, drawer->ImageBuf, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* +* SYNOPSIS +* PageFlip_MCGABuf(VQA) +* +* void PageFlip_MCGABuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *buf; + unsigned char *scrn; + long palsize; + long slowpal; + long imgwidth; + long imgheight; + + /* Derefernce commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for the vertical blank + * - Set the palette + * - Copy ImageBuf to SEENPAGE: + * - use blit routine if image is smaller than full-screen, since the + * buffer copy assumes a full-screen image + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Draw image to the screen. */ + imgwidth = ((VQAHandleP *)vqa)->Header.ImageWidth; + imgheight = ((VQAHandleP *)vqa)->Header.ImageHeight; + buf = drawer->ImageBuf; + + #ifndef PHARLAP_TNT + scrn = (unsigned char *)0xA0000; + #endif + + if ((imgwidth < 320) || (imgheight < 200)) { + #ifndef PHARLAP_TNT + scrn += drawer->ScreenOffset; + #else + scrn = (unsigned char *)drawer->ScreenOffset; + #endif + + MCGA_Blit(buf, scrn, imgwidth, imgheight); + } else { + MCGA_BufferCopy(buf, NULL); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(NULL, curframe->FrameNum); + } +} +#endif /* VQAMCGA_ON */ + + +#if(VQAVESA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_VESA640 - Draws a frame in VESA 640 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA640(VQA) +* +* long DrawFrame_VESA640(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA640(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Drawer.WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf + drawer->ScreenOffset; + #else + FP_SET(scrn, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32K - Draws a frame in VESA 320 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32K(VQA) +* +* long DrawFrame_VESA320_32K(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32K(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Translate palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* UnVQ directly to screen */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->Palette_15, grain, 0, 0); + #else + { + FARPTR temp; + + FP_SET(temp, drawer->Palette_15, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, temp, + grain, 0, 0); + } + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(0, curframe->FrameNum); + } + } + + /* Wait for VQ_Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32KBuf(VQA) +* +* long DrawFrame_VESA320_32KBuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* UnVQ buffered mode */ + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + #else + { + FARPTR scrn; + + FP_SET(scrn, drawer->ImageBuf, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + } + #endif + + /* Translate the palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + Buf_320x200_To_VESA_32K(drawer->ImageBuf, drawer->Palette_15, grain); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(0, curframe->FrameNum); + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Flags &= (~VQADATF_DSLEEP); + drawer->WaitsOnFlipper++; + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_VESA - Page flip VESA display. +* +* SYNOPSIS +* PageFlip_VESA(VQA) +* +* void PageFlip_VESA(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_VESA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + long palsize; + long slowpal; + long grain; + + /* Dereference date members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Set the palette */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + switch (((VQAHandleP *)vqa)->Header.image_width) { + case 320: + if (config->DrawFlags & VQACFGF_SCALEX2) { + Buf_320x200_To_VESA_640x400(drawer->ImageBuf, grain); + } else { + Buf_320x200_To_VESA_320x200(drawer->ImageBuf, grain); + } + break; + + default: + VESA_Blit_640x480(drawer->Display, drawer->ImageBuf, drawer->X1, + drawer->Y1, ((VQAHandleP *)vqa)->Header.image_width, + ((VQAHandleP *)vqa)->Header.image_height); + break; + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum); + } +} +#endif /* VQAVESA_ON */ + + +/**************************************************************************** +* +* NAME +* DrawFrame_Buffer - Draw a frame to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_Buffer(VQA) +* +* long DrawFrame_Buffere(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* Error - 0 if successful, otherwise VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Buffer(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *buff; + #else + FARPTR buff; + #endif + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + buff = (unsigned char *)(drawer->ImageBuf + drawer->ScreenOffset); + #else + FP_SET(buff, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, buff, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum); + } + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* UnVQ_Nop - Do nothing UnVQ. +* +* SYNOPSIS +* UnVQ_Nop(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +* +* void UnVQ_Nop(unsigned char *, unsigned char *, unsigned char *, +* unsigned long, unsigned long, unsigned long); +* FUNCTION +* +* INPUTS +* Codebook - Not used. (Prototype placeholder) +* Pointers - Not used. (Prototype placeholder) +* Buffer - Not used. (Prototype placeholder) +* BPR - Not used. (Prototype placeholder) +* Rows - Not used. (Prototype placeholder) +* BufWidth - Not used. (Prototype placeholder) +* +* RESULT +* NONE +* +****************************************************************************/ + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth) +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth) +#endif +{ + /* Suppress compiler warnings */ + codebook = codebook; + pointers = pointers; + buffer = buffer; + blocksperrow = blocksperrow; + numrows = numrows; + bufwidth = bufwidth; +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Nop - Do nothing page flip. +* +* SYNOPSIS +* PageFlip_Nop(VQA) +* +* void PageFlip_Nop(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_Nop(VQAHandle *vqa) +{ + vqa = vqa; +} + + +#if(0) +/**************************************************************************** +* +* NAME +* Mask_Rect - Sets non-drawable rectangle in image. +* +* SYNOPSIS +* Mask_Rect(VQA, X1, Y1, X2, Y2) +* +* void Mask_Rect(VQAHandle *, unsigned long, unsigned long, +* unsigned long, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* X1 - X coordinate of upper-left corner +* Y1 - Y coordinate of upper-left corner +* X2 - X coordinate of lower-right corner +* Y2 - Y coordinate of lower-right corner +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAHeader *header; + long blocks_per_row; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + header = &((VQAHandleP *)vqa)->Header; + drawer = &vqabuf->Drawer; + + /* Clip coords to image size */ + if (x1 < vqabuf->Drawer.X1) { + x1 = vqabuf->Drawer.X1; + } + + if (y1 < vqabuf->Drawer.Y1) { + y1 = vqabuf->Drawer.Y1; + } + + if (x2 > vqabuf->Drawer.X2) { + x2 = vqabuf->Drawer.X2; + } + + if (y2 > vqabuf->Drawer.Y2) { + y2 = vqabuf->Drawer.Y2; + } + + /* Convert pixel coords to block coords */ + x1 /= header->block_width; + x2 /= header->block_width; + y1 /= header->block_height; + y2 /= header->block_height; + + /* Compute the mask values */ + blocks_per_row = (header->image_width / header->block_width); + vqabuf->Drawer.MaskStart = blocks_per_row * y1 + x1; + + if (x1 == x2) { + drawer->MaskWidth = 0; + } else { + drawer->MaskWidth = x2 - x1 + 1; + } + + if (y1 == y2) { + drawer->MaskHeight = 0; + } else { + drawer->MaskHeight = y2 - y1 + 1; + } +} + + +/**************************************************************************** +* +* NAME +* Mask_Pointers - Mask vector pointer that are in the mask rectangle. +* +* SYNOPSIS +* Mask_Pointers(VQAData) +* +* void Mask_Pointers(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Pointers(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned long *ptr; + unsigned long i,j; + unsigned long start; + + /* Dereference data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + start = vqabuf->Drawer.MaskStart; + + for (i = 0; i < drawer->MaskHeight; i++) { + ptr = (unsigned long *)(curframe->Pointers) + start; + + for (j = 0; j < drawer->MaskWidth; j++) { + ptr[j] = VQA_MASK_POINTER; + } + + start += drawer->BlocksPerRow; + } +} +#endif + diff --git a/VQ/VQA32/DRAWER.CPP b/VQ/VQA32/DRAWER.CPP new file mode 100644 index 0000000..498a3de --- /dev/null +++ b/VQ/VQA32/DRAWER.CPP @@ -0,0 +1,2247 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* drawer.c +* +* DESCRIPTION +* Frame drawing and page flip control. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* June 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Configure_Drawer - Configure the drawer routines. +* +* PRIVATE +* Select_Frame - Selects frame to draw and preforms frame +* skip. +* Prepare_Frame - Process/Decompress frame information. +* DrawFrame_Xmode - Draws a frame directly to Xmode screen. +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident +* Codebook. +* PageFlip_Xmode - Page flip Xmode display. +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* PageFlip_MCGA - Page flip MCGA display. +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* DrawFrame_VESA640 - Draws a frame in VESA640 format. +* DrawFrame_VESA320_32K - Draws a frame to VESA320_32K screen. +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* PageFlip_VESA - Page flip VESA display. +* DrawFrame_Buffer - Draw a frame to a buffer. +* PageFlip_Nop - Do nothing page flip. +* UnVQ_Nop - Do nothing UnVQ. +* Mask_Rect - Sets non-drawable rectangle in image. +* Mask_Pointers - Mask vector pointer that are in the mask +* rectangle. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "unvq.h" +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ +static long Select_Frame(VQAHandleP *vqap); +static void Prepare_Frame(VQAData *vqabuf); + +#if(VQAMCGA_ON) +static long DrawFrame_MCGABuf(VQAHandle *vqa); +static long PageFlip_MCGABuf(VQAHandle *vqa); +static long DrawFrame_MCGA(VQAHandle *vqa); +static long PageFlip_MCGA(VQAHandle *vqa); +#endif /* VQAMCGA_ON */ + +#if(VQAXMODE_ON) +static long DrawFrame_XmodeBuf(VQAHandle *vqa); +static long DrawFrame_Xmode(VQAHandle *vqa); +static long DrawFrame_XmodeVRAM(VQAHandle *vqa); +static void PageFlip_Xmode(VQAHandle *vqabuf); +#endif /* VQAXMODE_ON */ + +#if(VQAVESA_ON) +static long DrawFrame_VESA640(VQAHandle *vqa); +static long DrawFrame_VESA320_32K(VQAHandle *vqa); +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa); +static void PageFlip_VESA(VQAHandle *vqabuf); +#endif /* VQAVESA_ON */ + +static long DrawFrame_Buffer(VQAHandle *vqa); +static long PageFlip_Nop(VQAHandle *vqa); + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); +#endif + +#if(0) +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2); +static void Mask_Pointers(VQAData *vqabuf); +#endif + + +/**************************************************************************** +* +* NAME +* VQA_Configure_Drawer - Configure the drawer routines. +* +* SYNOPSIS +* VQA_Configure_Drawer(VQA) +* +* void VQA_Configure_Drawer(VQAHandleP *); +* +* FUNCTION +* Configure the drawing system for the current movie and configuration +* options. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Configure_Drawer(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAHeader *header; + VQADrawer *drawer; + long origin; + long blkdim; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + header = &vqap->Header; + config = &vqap->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((config->X1 == -1) && (config->Y1 == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((drawer->ImageWidth - header->ImageWidth) / 2); + drawer->Y1 = ((drawer->ImageHeight - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = config->X1; + drawer->Y1 = config->Y1; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = config->X1; + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (drawer->ImageWidth - config->X1); + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE UNVQ ROUTINE FOR THE SPECIFIED VIDEO MODE AND BLOCK SIZE. + *-----------------------------------------------------------------------*/ + + /* Pre-compute commonly used values for speed. */ + drawer->BlocksPerRow = header->ImageWidth / header->BlockWidth; + drawer->NumRows = header->ImageHeight / header->BlockHeight; + drawer->NumBlocks = drawer->BlocksPerRow * drawer->NumRows; + blkdim = BLOCK_DIM(header->BlockWidth, header->BlockHeight); + + /* Initialize draw routine vectors to a NOP routine in order to prevent + * a crash. + */ + vqabuf->UnVQ = UnVQ_Nop; + vqabuf->Page_Flip = PageFlip_Nop; + + /* If the client specifies buffering then go ahead an set the unvq + * vector. All of the buffered modes use the same unvq routines. + */ + if (config->DrawFlags & VQACFGF_BUFFER) { + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2; + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + + default: + break; + } + } + + /* Initialize the draw vectors for the specified video mode. */ + switch (config->Vmode) { + + /* MCGA */ + #if(VQAMCGA_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_MCGABuf; + vqabuf->Page_Flip = PageFlip_MCGABuf; + } else { + vqabuf->Draw_Frame = DrawFrame_MCGA; + vqabuf->Page_Flip = PageFlip_MCGA; + + /* MCGA uses the same unvq routines that are used for unvqing + * to a buffer because MCGA mode is just another buffer. However, + * instead of drawing to an allocated RAM buffer we are drawing + * directly to the VRAM video buffer. + */ + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + #if(!VQAWOOFER_ON) + vqabuf->UnVQ = UnVQ_4x2; + #else + if (config->DrawFlags & VQACFGF_WOOFER) { + vqabuf->UnVQ = UnVQ_4x2_Woofer; + } else { + vqabuf->UnVQ = UnVQ_4x2; + } + #endif + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((320 * drawer->Y1) + drawer->X1); + break; + #endif /* VQAMCGA_ON */ + + /* XMODE */ + #if(VQAXMODE_ON) + case XMODE_320X200: + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_XmodeBuf; + vqabuf->Page_Flip = PageFlip_Xmode; + } else { + vqabuf->Page_Flip = PageFlip_Xmode; + + if (config->DrawFlags & VQACFGF_VRAMCB) { + vqabuf->Draw_Frame = DrawFrame_XmodeVRAM; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_XmodeCB; + break; + #endif /* VQABLOCK_4X2 */ + } + } else { + vqabuf->Draw_Frame = DrawFrame_Xmode; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_Xmode; + break; + #endif /* VQABLOCK_4X2 */ + } + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = (((320 / 4) * drawer->Y1) + (drawer->X1 / 4)); + break; + #endif /* VQAXMODE_ON */ + + /* VESA */ + #if(VQAVESA_ON) + + /* Currently this is a buffered mode, but should be optimized for + * for screen direct. + */ + case VESA_640X480_256: + vqabuf->Draw_Frame = DrawFrame_VESA640; + vqabuf->Page_Flip = PageFlip_VESA; + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_VESA320_32KBuf; + } else { + vqabuf->Draw_Frame = DrawFrame_VESA320_32K; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_VESA320_32K; + break; + #endif + } + } + break; + #endif /* VQAVESA_ON */ + + /* Purely buffered (Video refresh is up to the client. */ + default: + vqabuf->Draw_Frame = DrawFrame_Buffer; + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((drawer->ImageWidth * drawer->Y1)+drawer->X1); + break; + } +} + + +/**************************************************************************** +* +* NAME +* Select_Frame - Selects frame to draw and preforms frame skip. +* +* SYNOPSIS +* Error = Select_Frame(VQA) +* +* long Select_Frame(VQAHandleP *); +* +* FUNCTION +* Select a frame to draw. This is were the frame skipping/delay is +* performed. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long Select_Frame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAConfig *config; + VQAFrameNode *curframe; + long curtime; + long desiredframe; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Make sure the current frame is drawable. If the frame is not ready + * then we must wait for the loader to catch up. + */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + drawer->WaitsOnLoader++; + return (VQAERR_NOBUFFER); + } + + /* If single stepping then return with the next frame.*/ + if (config->OptionFlags & VQAOPTF_STEP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Find the frame # we should play (rounded to nearest frame): */ + curtime = VQA_GetTime(vqap); + desiredframe = ((curtime * config->FrameRate) / VQA_TIMETICKS); + + #if(VQAMONO_ON) + drawer->DesiredFrame = desiredframe; + #endif + + /* Handle the cases where the player is going so fast that it's not time + * to draw this frame yet. + * + * - If the Drawer is using a slower frame rate than the Loader, use a + * delta-time-based wait; otherwise, use the frame number as the wait. + */ + if (config->DrawRate != config->FrameRate) { + if (curtime - drawer->LastTime < (VQA_TIMETICKS / config->DrawRate)) { + return (VQAERR_NOT_TIME); + } + } else { + if (curframe->FrameNum > desiredframe) { + return (VQAERR_NOT_TIME); + } + } + + /* Make sure we draw at least 5 frames per second */ + if ((curframe->FrameNum - drawer->LastFrame) >= (config->FrameRate / 5)) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* If frame skipping is disabled then draw every frame. */ + if (config->DrawFlags & VQACFGF_NOSKIP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Handle the case where the player is going too slow, so we have to skip + * some frames: + * + * - If this is a Key Frame, draw it + * - If this frame's # is less than what we're supposed to draw, skip it + * (Because the 1st 'desiredframe' will be 0, FrameNum MUST be typecast + * to signed WORD for the comparison; otherwise, the comparison uses + * UWORDs, and the first frame is always skipped.) + * - If this is a palette-set frame, set the palette before skipping it + * - Loop until we get the frame we need, or there's no frames available + */ + while (1) { + + /* No frame available; return */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + return (VQAERR_NOBUFFER); + } + + /* Force drawing of a Key Frame */ + if (curframe->Flags & VQAFRMF_KEY) { + break; + } + + /* Skip the frame */ + if (curframe->FrameNum < desiredframe) { + + /* Handle a palette in a skipped frame: + * + * - Stash the palette in Drawer.Palette_24 + * - Set the Drawer.Flags VQADRWF_SETPAL bit, to tell the page-flip + * routines that this palette must be set + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + + /* Un-LCW if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)curframe->Palette, + vqabuf->Max_Pal_Size); + + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Stash the palette */ + memcpy(drawer->Palette_24, curframe->Palette, curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + drawer->Flags |= VQADRWF_SETPAL; + } + + /* Invoke callback with NULL screen ptr */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Skip the frame */ + curframe->Flags = 0L; + curframe = curframe->Next; + drawer->CurFrame = curframe; + drawer->NumSkipped++; + } else { + break; + } + } + + drawer->LastFrame = curframe->FrameNum; + drawer->LastTime = curtime; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Prepare_Frame - Process/Decompress frame information. +* +* SYNOPSIS +* Prepare_Frame(VQAData) +* +* void Prepare_Frame(VQAData *); +* +* FUNCTION +* Decompress and preprocess the various frame elements (codebook, +* pointers, palette, etc...) +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Prepare_Frame(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + VQACBNode *codebook; + + /* Dereference commonly used data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + codebook = curframe->Codebook; + + /* Decompress the codebook, if needed */ + if (codebook->Flags & VQACBF_CBCOMP) { + + /* Decompress the codebook. */ + LCW_Uncompress((char *)codebook->Buffer + codebook->CBOffset, + (char *)codebook->Buffer, vqabuf->Max_CB_Size); + + /* Mark as uncompressed for the next time we use it */ + codebook->Flags &= (~VQACBF_CBCOMP); + } + + /* Decompress the palette, if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset,(char *)curframe->Palette,vqabuf->Max_Pal_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Decompress the pointer data, if needed */ + if (curframe->Flags & VQAFRMF_PTRCOMP) { + LCW_Uncompress((char *)curframe->Pointers + curframe->PtrOffset, + (char *)curframe->Pointers, vqabuf->Max_Ptr_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + } + + /* Mask the pointers */ + #if(0) + Mask_Pointers(vqabuf); + #endif +} + + +#if(VQAXMODE_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_Xmode - Draws a frame in Xmode format (Screen direct). +* +* SYNOPSIS +* Error = DrawFrame_Xmode(VQA) +* +* long DrawFrame_Xmode(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_XmodeBuf(VQA) +* +* long DrawFrame_XmodeBuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into ImageBuf +* - Copy ImageBuf to video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf into the correct part of the +* Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAHeader *header; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + header = &((VQAHandleP *)vqa)->Header; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + header->ImageWidth); + + /* Copy the buffer */ + scrn = GetXHidPage(); + + if ((header->ImageWidth < 320) || (header->ImageHeight < 200)) { + Xmode_Blit(drawer->ImageBuf, scrn + drawer->ScreenOffset, + header->ImageWidth, header->ImageHeight); + } else { + Xmode_BufferCopy_320x200(drawer->ImageBuf,scrn + drawer->ScreenOffset); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident Codebook. +* +* SYNOPSIS +* Error = DrawFrame_XmodeVRAM(VQA) +* +* long DrawFrame_XmodeVRAM(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Download Codebook if it isn't already (do this before skipping +* frames, so Select_Frame will smooth out the delay) +* - Skip frames +* - UnLCW frame +* - Convert Pointers to VRAM format +* - Un-VQ by copying codebook blocks within VRAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeVRAM(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Download codebook to VRAM */ + if ((curframe->Codebook->Flags & VQACBF_DOWNLOADED) == 0) { + Upload_4x2CB(curframe->Codebook->Buffer, + ((VQAHandleP *)vqa)->Header.CBentries + + ((VQAHandleP *)vqa)->Header.Num1Colors); + + curframe->Codebook->Flags |= VQACBF_DOWNLOADED; + } + + /* Convert pointers to VRAM format */ + XlatePointers(curframe->Pointers, drawer->NumBlocks); + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Xmode - Page flip Xmode display. +* +* SYNOPSIS +* PageFlip_Xmode(VQA) +* +* void PageFlip_Xmode(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned char *pal; + VQAConfig *config; + long palsize; + long slowpal; + + /* Dereference commonly used data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for NoVB (active scan) + * - Flip the page (doesn't take effect until next active scan + * - Wait for VB + * - Set the palette + * - Otherwise, just flip the page + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } else { + FlipXPage(); + } +} +#endif /* VQAXMODE_ON */ + + +#if(VQAMCGA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* +* SYNOPSIS +* Error = DrawFrame_MCGA(VQA) +* +* long DrawFrame_MCGA(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set palette +* UnVQ to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + /* Dereference commonly used data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Select the frame to draw. */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGA - Page flip MCGA display. +* +* SYNOPSIS +* PageFlip_MCGA(VQA) +* +* long PageFlip_MCGA(VQAHandle *); +* +* FUNCTION +* Since the MCGA mode only has one buffer, the drawing is actually done +* at this point. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *ptrs; + unsigned char *cb; + long palsize; + long slowpal; + long blocksperrow; + long numrows; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /*------------------------------------------------------------------------- + * WAIT FOR THE VERTICAL BLANK TO SET THE PALETTE. + *-----------------------------------------------------------------------*/ + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Wait for the VBlank. */ + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + /* Set the palette. */ + if (curframe->Flags & VQAFRMF_PALETTE) { + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + } + + /*------------------------------------------------------------------------- + * UNVQ THE FRAME DIRECTLY TO THE MCGA SCREEN. + *-----------------------------------------------------------------------*/ + + /* Get screen address, blocks per row and number of rows. */ + #ifndef PHARLAP_TNT + scrn = (unsigned char *)(0xA0000 + (unsigned long)drawer->ScreenOffset); + #else + FP_SET(scrn, drawer->ScreenOffset, 0x1C); + #endif + + blocksperrow = drawer->BlocksPerRow; + numrows = drawer->NumRows; + ptrs = curframe->Pointers; + cb = curframe->Codebook->Buffer; + vqabuf->UnVQ(cb, ptrs, scrn, blocksperrow, numrows, 320); + + /* Process captions. */ + if ((((VQAHandleP *)vqa)->Caption != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + DoCaptions(((VQAHandleP *)vqa)->Caption, curframe->FrameNum); + } + + if ((((VQAHandleP *)vqa)->EVA != NULL) + && (config->OptionFlags & VQAOPTF_EVA)) { + DoCaptions(((VQAHandleP *)vqa)->EVA, curframe->FrameNum); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_MCGABuf(VQA) +* +* long DrawFrame_MCGABuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled (can't use imgbuf til User_Update's done) +* - Un-VQ into ImageBuf +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set Palette from Flipper.CurFrame +* copy ImageBuf to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf onto the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf; + #else + FP_SET(scrn, drawer->ImageBuf, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* +* SYNOPSIS +* PageFlip_MCGABuf(VQA) +* +* void PageFlip_MCGABuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *buf; + unsigned char *scrn; + long palsize; + long slowpal; + long imgwidth; + long imgheight; + + /* Derefernce commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for the vertical blank + * - Set the palette + * - Copy ImageBuf to SEENPAGE: + * - use blit routine if image is smaller than full-screen, since the + * buffer copy assumes a full-screen image + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Draw image to the screen. */ + imgwidth = ((VQAHandleP *)vqa)->Header.ImageWidth; + imgheight = ((VQAHandleP *)vqa)->Header.ImageHeight; + buf = drawer->ImageBuf; + + #ifndef PHARLAP_TNT + scrn = (unsigned char *)0xA0000; + #endif + + if ((imgwidth < 320) || (imgheight < 200)) { + #ifndef PHARLAP_TNT + scrn += drawer->ScreenOffset; + #else + scrn = (unsigned char *)drawer->ScreenOffset; + #endif + + MCGA_Blit(buf, scrn, imgwidth, imgheight); + } else { + MCGA_BufferCopy(buf, NULL); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} +#endif /* VQAMCGA_ON */ + + +#if(VQAVESA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_VESA640 - Draws a frame in VESA 640 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA640(VQA) +* +* long DrawFrame_VESA640(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA640(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Drawer.WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf + drawer->ScreenOffset; + #else + FP_SET(scrn, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32K - Draws a frame in VESA 320 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32K(VQA) +* +* long DrawFrame_VESA320_32K(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32K(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Translate palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* UnVQ directly to screen */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->Palette_15, grain, 0, 0); + #else + { + FARPTR temp; + + FP_SET(temp, drawer->Palette_15, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, temp, + grain, 0, 0); + } + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for VQ_Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32KBuf(VQA) +* +* long DrawFrame_VESA320_32KBuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* UnVQ buffered mode */ + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + #else + { + FARPTR scrn; + + FP_SET(scrn, drawer->ImageBuf, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + } + #endif + + /* Translate the palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + Buf_320x200_To_VESA_32K(drawer->ImageBuf, drawer->Palette_15, grain); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Flags &= (~VQADATF_DSLEEP); + drawer->WaitsOnFlipper++; + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_VESA - Page flip VESA display. +* +* SYNOPSIS +* PageFlip_VESA(VQA) +* +* void PageFlip_VESA(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_VESA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + long palsize; + long slowpal; + long grain; + + /* Dereference date members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Set the palette */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + switch (((VQAHandleP *)vqa)->Header.image_width) { + case 320: + if (config->DrawFlags & VQACFGF_SCALEX2) { + Buf_320x200_To_VESA_640x400(drawer->ImageBuf, grain); + } else { + Buf_320x200_To_VESA_320x200(drawer->ImageBuf, grain); + } + break; + + default: + VESA_Blit_640x480(drawer->Display, drawer->ImageBuf, drawer->X1, + drawer->Y1, ((VQAHandleP *)vqa)->Header.image_width, + ((VQAHandleP *)vqa)->Header.image_height); + break; + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } +} +#endif /* VQAVESA_ON */ + + +/**************************************************************************** +* +* NAME +* DrawFrame_Buffer - Draw a frame to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_Buffer(VQA) +* +* long DrawFrame_Buffere(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* Error - 0 if successful, otherwise VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Buffer(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *buff; + #else + FARPTR buff; + #endif + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + buff = (unsigned char *)(drawer->ImageBuf + drawer->ScreenOffset); + #else + FP_SET(buff, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, buff, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* UnVQ_Nop - Do nothing UnVQ. +* +* SYNOPSIS +* UnVQ_Nop(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +* +* void UnVQ_Nop(unsigned char *, unsigned char *, unsigned char *, +* unsigned long, unsigned long, unsigned long); +* FUNCTION +* +* INPUTS +* Codebook - Not used. (Prototype placeholder) +* Pointers - Not used. (Prototype placeholder) +* Buffer - Not used. (Prototype placeholder) +* BPR - Not used. (Prototype placeholder) +* Rows - Not used. (Prototype placeholder) +* BufWidth - Not used. (Prototype placeholder) +* +* RESULT +* NONE +* +****************************************************************************/ + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth) +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth) +#endif +{ + /* Suppress compiler warnings */ + codebook = codebook; + pointers = pointers; + buffer = buffer; + blocksperrow = blocksperrow; + numrows = numrows; + bufwidth = bufwidth; +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Nop - Do nothing page flip. +* +* SYNOPSIS +* PageFlip_Nop(VQA) +* +* void PageFlip_Nop(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static long PageFlip_Nop(VQAHandle *vqa) +{ + vqa = vqa; + + return (0); +} + + +#if(0) +/**************************************************************************** +* +* NAME +* Mask_Rect - Sets non-drawable rectangle in image. +* +* SYNOPSIS +* Mask_Rect(VQA, X1, Y1, X2, Y2) +* +* void Mask_Rect(VQAHandle *, unsigned long, unsigned long, +* unsigned long, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* X1 - X coordinate of upper-left corner +* Y1 - Y coordinate of upper-left corner +* X2 - X coordinate of lower-right corner +* Y2 - Y coordinate of lower-right corner +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAHeader *header; + long blocks_per_row; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + header = &((VQAHandleP *)vqa)->Header; + drawer = &vqabuf->Drawer; + + /* Clip coords to image size */ + if (x1 < vqabuf->Drawer.X1) { + x1 = vqabuf->Drawer.X1; + } + + if (y1 < vqabuf->Drawer.Y1) { + y1 = vqabuf->Drawer.Y1; + } + + if (x2 > vqabuf->Drawer.X2) { + x2 = vqabuf->Drawer.X2; + } + + if (y2 > vqabuf->Drawer.Y2) { + y2 = vqabuf->Drawer.Y2; + } + + /* Convert pixel coords to block coords */ + x1 /= header->block_width; + x2 /= header->block_width; + y1 /= header->block_height; + y2 /= header->block_height; + + /* Compute the mask values */ + blocks_per_row = (header->image_width / header->block_width); + vqabuf->Drawer.MaskStart = blocks_per_row * y1 + x1; + + if (x1 == x2) { + drawer->MaskWidth = 0; + } else { + drawer->MaskWidth = x2 - x1 + 1; + } + + if (y1 == y2) { + drawer->MaskHeight = 0; + } else { + drawer->MaskHeight = y2 - y1 + 1; + } +} + + +/**************************************************************************** +* +* NAME +* Mask_Pointers - Mask vector pointer that are in the mask rectangle. +* +* SYNOPSIS +* Mask_Pointers(VQAData) +* +* void Mask_Pointers(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Pointers(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned long *ptr; + unsigned long i,j; + unsigned long start; + + /* Dereference data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + start = vqabuf->Drawer.MaskStart; + + for (i = 0; i < drawer->MaskHeight; i++) { + ptr = (unsigned long *)(curframe->Pointers) + start; + + for (j = 0; j < drawer->MaskWidth; j++) { + ptr[j] = VQA_MASK_POINTER; + } + + start += drawer->BlocksPerRow; + } +} +#endif + diff --git a/VQ/VQA32/DSTREAM.CPP b/VQ/VQA32/DSTREAM.CPP new file mode 100644 index 0000000..047e259 --- /dev/null +++ b/VQ/VQA32/DSTREAM.CPP @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. +* +* FILE +* dstream.c +* +* DESCRIPTION +* DOS IO handler. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* PRIVATE +* VQADOSHandler - Standard DOS IO handler. +* +****************************************************************************/ + +#include +#include +#include "vqaplayp.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes); + + +/**************************************************************************** +* +* NAME +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* SYNOPSIS +* VQA_InitAsDOS(VQA) +* +* VQA_InitAsDOS(VQAHandle *); +* +* FUNCTION +* Initialize the IO of the specified handle as a standard DOS access. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize as DOS. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_InitAsDOS(VQAHandle *vqa) +{ + ((VQAHandleP *)vqa)->IOHandler = VQADOSHandler; +} + + +/**************************************************************************** +* +* NAME +* VQADOSHandler - Standard DOS IO handler. +* +* SYNOPSIS +* Error = VQADOSHandler(VQA, Action, Buffer, NBytes) +* +* unsigned long VQADOSHandler(VQAHandle *, long, long, long); +* +* FUNCTION +* Perform the requested action on the standard DOS file system. +* +* INPUTS +* VQA - VQAHandle to operate on. +* Action - Action to perform. +* Buffer - Buffer to Read/Write to/from. +* NBytes - Number of bytes to operate on. +* +* RESULT +* Error - 0 if successful, otherwise error. +* +****************************************************************************/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes) +{ + long fh; + long error; + + fh = vqa->VQAio; + + /* Perform the action specified by the IO command */ + switch (action) { + + /* VQACMD_READ means read NBytes and place it in the memory + * pointed to by Buffer. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_READ. + */ + case VQACMD_READ: + error = (read(fh, buffer, nbytes) != nbytes); + break; + + /* VQACMD_WRITE is analogous to VQACMD_READ. + * + * Writing is not allowed to the VQA file, VQA library will remap the + * error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* VQACMD_SEEK asks that you perform a seek relative to the current + * position. NBytes is a signed number, indicating seek direction + * (positive for forward, negative for backward). Buffer has no meaning + * here. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (lseek(fh, nbytes, (long)buffer) == -1); + break; + + /* VQACMD_OPEN asks that you open the file for access. */ + case VQACMD_OPEN: + error = open((char *)buffer, (O_RDONLY|O_BINARY)); + + if (error != -1) { + vqa->VQAio = error; + error = 0; + } + break; + + case VQACMD_CLOSE: + close(fh); + error = 0; + break; + + /* VQACMD_INIT means to prepare your IO for reading. This is used for + * certain IOs that can't be read immediately upon opening, and need + * further preparation. This operation is allowed to fail; the error code + * will be returned directly to the client. + */ + case VQACMD_INIT: + + /* IFFCMD_CLEANUP means to terminate the transaction with the associated + * IO. This is used for IOs that can't simply be closed. This operation + * is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return (error); +} + diff --git a/VQ/VQA32/LOADER.BAK b/VQ/VQA32/LOADER.BAK new file mode 100644 index 0000000..f6b59da --- /dev/null +++ b/VQ/VQA32/LOADER.BAK @@ -0,0 +1,2353 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* loader.c +* +* DESCRIPTION +* Stream loading and pre-processing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Open - Open a VQA file to play. +* VQA_Close - Close an opened VQA file. +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* +* PRIVATE +* AllocBuffers - Allocates the numerous VQA play buffers +* FreeBuffers - Frees the VQA play buffers +* Load_FINF - Loads the Frame Info Table. +* Load_VQHD - Loads a VQA Header. +* Load_CBF0 - Loads a full, uncompressed codebook +* Load_CBFZ - Loads a full, compressed codebook +* Load_CBP0 - Loads a partial uncompressed codebook +* Load_CBPZ - Loads a partial compressed codebook +* Load_CPL0 - Loads an uncompressed palette +* Load_CPLZ - Loads a compressed palette +* Load_VPT0 - Loads uncompressed pointers +* Load_VPTZ - Loads compressed pointers +* Load_VQF - Loads a VQ Frame chunk +* Load_SND0 - Loads an uncompressed sound chunk +* Load_SND1 - Loads a compressed sound chunk +* Copy_SND - Copies data from Audio Temp buf into Audio play buf +* Load_VOC_Block - Loads blocks from separate VOC file, if needed. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +typedef struct _ChunkHeader { + unsigned long id; + unsigned long size; +} ChunkHeader; + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config); +static void FreeBuffers(VQAData *vqa, VQAConfig *config); +static long Load_VQF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAAUDIO_ON) +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize); +static long Copy_SND(VQAHandleP *vqap); + +#if(VQAVOC_ON && VQAAUDIO_ON) +static void Load_VOC_Block(VQAHandleP *vqap); +#endif /* VQAVOC_ON */ + +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_Open - Open a VQA file to play. +* +* SYNOPSIS +* Error = VQA_Open(VQAHandle, Name, Config) +* +* short VQA_Open(VQAHandle *, char *, VQAConfig *); +* +* FUNCTION +* - Open a VQA file for reading. +* - Validate that it is an IFF file, of the VQA type. +* - Read the VQA header. +* - Open a VOC file for playback, if requested. +* - Set the Loader's frame rate, if the caller's Config structure's +* FrameRate is set to -1 +* - Set the Drawer's frame rate, if the caller's Config structure's +* DrawRate is set to -1 +* +* INPUTS +* VQAHandle - Pointer to initialized handle. Obtained by VQA_Alloc(). +* Name - Pointer to name of VQA file to open. +* Config - Pointer to initialized VQA configuration structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_ error code. +* +****************************************************************************/ + +long VQA_Open(VQAHandle *vqa, char const *filename, VQAConfig *config) +{ + VQAHandleP *vqap; + VQAHeader *header; + ChunkHeader chunk; + long max_frm_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + header = &vqap->Header; + + /*------------------------------------------------------------------------- + * VERIFY VALIDITY OF VQA FILE. + *-----------------------------------------------------------------------*/ + + /* Open the file. */ + if (vqap->StreamHandler(vqa, VQACMD_OPEN, (void *)filename, 0)) { + return (VQAERR_OPEN); + } + + /* Read the file ID & Size */ + if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify an IFF FORM */ + if ((chunk.id != ID_FORM) || (chunk.size == 0)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read in WVQA ID */ + if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 4)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify VQA */ + if (chunk.id != ID_WVQA) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /*------------------------------------------------------------------------- + * READ IN THE VQA HEADER. + *-----------------------------------------------------------------------*/ + if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + chunk.size = REVERSE_LONG(chunk.size); + + /* Is this a valid VQA header? */ + if ((chunk.id != ID_VQHD) || (chunk.size != sizeof(VQAHeader))) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read the header data. */ + if (vqap->StreamHandler(vqa, VQACMD_READ, header, PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE PLAYERS CONFIGURATION + *-----------------------------------------------------------------------*/ + + /* Use the clients configuration if they provided one. */ + if (config != NULL) { + memcpy(&vqap->Config, config, sizeof(VQAConfig)); + } else { + VQA_DefaultConfig(&vqap->Config); + } + + /* Use the internal configuration structure from now on. */ + config = &vqap->Config; + + if (config->ImageWidth == -1) { + config->ImageWidth = header->ImageWidth; + } + + if (config->ImageHeight == -1) { + config->ImageHeight = header->ImageHeight; + } + + /*------------------------------------------------------------------------- + * ALLOCATE THE BUFFERS THAT WE NEED TO PLAY THE VQA. + *-----------------------------------------------------------------------*/ + if ((vqap->VQABuf = AllocBuffers(header, config)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /*------------------------------------------------------------------------- + * SET THE LOADER AND DRAWER PLAYBACK RATES. + *-----------------------------------------------------------------------*/ + + /* If Loaders frame rate is -1 then use the value from the header. */ + if (config->FrameRate == -1) { + config->FrameRate = header->FPS; + } + + /* If Drawers frame rate is -1 then use the value from the header, which + * will result in a "variable" frame rate. + */ + if (config->DrawRate == -1) { + config->DrawRate = header->FPS; + } + + /* Finally, if the DrawRate was set to -1 or 0 (ie MaxRate contained bogus + * values), set it to the header value. + */ + if ((config->DrawRate == -1) || (config->DrawRate == 0)) { + config->DrawRate = header->FPS; + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VIDEO SYSTEM IF WE ARE REQUIRED TO HANDLE THAT. + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + if ((vqap->VQABuf->Drawer.Display = SetVideoMode(config->Vmode)) == 0) { + VQA_Close(vqa); + return (VQAERR_VIDEO); + } + + /* Set the VBIBit polarity. */ + vqap->VQABuf->VBIBit = GetVBIBit(); + #else + if (config->VBIBit == -1) { + config->VBIBit = TestVBIBit(); + } + + vqap->VQABuf->VBIBit = config->VBIBit; + #endif /* VQAVIDEO_ON */ + + /*------------------------------------------------------------------------- + * AUDIO TRACK OVERRIDE (VOC FILE) + *-----------------------------------------------------------------------*/ + + /* Open VOC file if one is requested. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (config->VocFile != NULL) { + vqap->vocfh = open(config->VocFile, (O_RDONLY|O_BINARY)); + } else { + vqap->vocfh = -1; + } + + /* Make sure we won't try to play audio. */ + if ((vqap->vocfh == -1) && ((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #else /* VQAVOC_ON */ + + /* If the movie does not contain an audio track make sure we won't try + * to play one. + */ + if (((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #endif /* VQAVOC_ON */ + + /*------------------------------------------------------------------------- + * INITIALIZE THE AUDIO PLAYBACK/TIMING SYSTEM. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + + /* Open HMI audio resource for playback. */ + if (VQA_OpenAudio(vqap)) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + } + + if (!(config->OptionFlags & VQAOPTF_AUDIO) + || (config->TimerMethod == VQA_TMETHOD_INT)) { + + /* Start HMI timer system for timing. */ + if (VQA_StartTimerInt((config->OptionFlags & VQAOPTF_HMIINIT))) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + } + #endif /* VQAAUDIO_ON */ + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_Close - Close an opened VQA file. +* +* SYNOPSIS +* VQA_Close(VQAHandle) +* +* void VQA_Close(VQAHandle *); +* +* FUNCTION +* Close the file that was opened with VQA_Open(). +* +* INPUTS +* VQAHandle - Pointer VQAHandle to close. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Close(VQAHandle *vqa) +{ + /* Restore video mode to text. */ + #if(VQAVIDEO_ON) + SetVideoMode(TEXT); + #endif /* VQAVIDEO_ON */ + + /* Shutdown audio/timing system. */ + #if(VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->Config.OptionFlags & VQAOPTF_AUDIO) { + VQA_CloseAudio(); + } else { + VQA_StopTimerInt(); + } + #endif /* VQAAUDIO_ON */ + + /* Free memory */ + if (((VQAHandleP *)vqa)->VQABuf != NULL) { + FreeBuffers(((VQAHandleP *)vqa)->VQABuf, &((VQAHandleP *)vqa)->Config); + } + + /* Close the VOC override file if one was opened */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->vocfh != -1) { + close(((VQAHandleP *)vqa)->vocfh); + } + #endif /* VQAVOC_ON */ + + /* Close the VQA file */ + ((VQAHandleP *)vqa)->StreamHandler(vqa, VQACMD_CLOSE, NULL, 0); +} + + +/**************************************************************************** +* +* NAME +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* +* SYNOPSIS +* Error = VQA_LoadFrame(VQAHandle) +* +* short VQA_LoadFrame(VQAHandle *); +* +* FUNCTION +* The codebook is split up such that the last frame of every group gets +* a new, complete codebook, ready for the next group. The first codebook +* in the VQA is a full codebook, and goes with the first frame's data. +* Partial codebooks are stored per frame after that, and they add up to +* a full codebook just before the first frame for the next group is read. +* +* (Currently, this routine can read either the older non-frame-grouped +* VQA file format, or the new frame-chunk format. For the older format, +* it's assumed that the last chunk in a frame is the pointer data.) +* +* This routine also does a sort of "cooperative multitasking". If the +* Loader hits a "wait state" where it has to wait on the audio to finish +* playing before it can continue to load, it sets a "sleep" flag and +* just returns. The sleep flag is checked on entry to see if it needs +* to jump to the proper execution point. This may improve performance on +* some platforms, but it also allows the Loader to be called regardless +* of the size of the buffers; if the buffers fill up or the audio fails +* to play, the Loader won't just get stuck. +* +* INPUTS +* VQAHandle - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +long VQA_LoadFrame(VQAHandle *vqa) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQAFrameNode *curframe; + ChunkHeader chunk; + unsigned long iffsize; + long frame_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curframe = loader->CurFrame; + + /* If we're reading audio from a VOC file then service that requirement. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (vqap->vocfh != -1) { + Load_VOC_Block(vqap); + } + #endif /* VQAAUDIO_ON & VQAVOC_ON */ + + /* If no buffer is available for loading then return. This allows the + * drawer to service one of the buffers more readily. (We'll wait for one + * to free up). + */ + if (curframe->Flags & VQAFRMF_LOADED) { + loader->WaitsOnDrawer++; + return (VQAERR_NOBUFFER); + } + + /* If we're not sleeping, initialize */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + frame_loaded = 0; + loader->FrameSize = 0; + + /* Initialize the codebook ptr for the frame we're about to load: + * (This frame's codebook is the last full codebook; we have to init it + * now, because if we're on the last frame in a group, we'll get a new + * FullCB pointer.) + */ + curframe->Codebook = loader->FullCB; + } + + /*------------------------------------------------------------------------- + * THE MAIN LOADER LOOP + *-----------------------------------------------------------------------*/ + while (frame_loaded == 0) { + + /* Read new chunk, only if we're not sleeping */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + + /* Read chunk ID */ + if (vqap->StreamHandler(vqa, VQACMD_READ, &chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk.size); + loader->FrameSize += iffsize; + } + + /* Handle each chunk type */ + switch (chunk.id) { + + /* VQ Normal Frame */ + case ID_VQFR: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* VQ Key Frame */ + case ID_VQFK: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Frame Info Table */ + case ID_FINF: + if (Load_FINF(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* VQA Header */ + case ID_VQHD: + if (Load_VQHD(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Pointer data Key (Must draw) */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Uncompressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + #if(VQAAUDIO_ON) + case ID_SND0: + + /* Move the last audio frame to the play buffer. */ + if (Copy_SND(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + break; + + /* Compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND1: + + /* Move the last audio frame to the play buffer. */ + if (Copy_SND(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + break; + + /* HMI ADPCM compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND2: + + /* Move the last audio frame to the play buffer. */ + if (Copy_SND(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + break; + #endif + + /* Skip any unknown chunks. */ + default: + if (vqap->StreamHandler(vqa, VQACMD_SEEK, NULL, PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + break; + } + } + + /* Update maximum frame size stat. */ + if ((loader->CurFrameNum>0) && (loader->FrameSize>loader->MaxFrameSize)) { + loader->MaxFrameSize = loader->FrameSize; + } + + /*------------------------------------------------------------------------- + * SET UP THE FRAME FOR DRAWING. + *-----------------------------------------------------------------------*/ + + /* Set the frame # */ + curframe->FrameNum = loader->CurFrameNum; + loader->CurFrameNum++; + + /* Update data for mono output */ + loader->LastFrameNum = loader->CurFrameNum; + + /* Loader is finished with this frame; tell Drawer to draw it */ + curframe->Flags |= VQAFRMF_LOADED; + loader->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* AllocBuffers - Allocate VQA play buffers. +* +* SYNOPSIS +* VQAData = AllocBuffers(VQAFile) +* +* VQAData *AllocBuffers(VQAFile *); +* +* FUNCTION +* For those structures that contain buffer pointers (codebook nodes, +* frame buffer nodes), enough memory is allocated for both the structure +* and its associated buffers, then the buffer pointers are pointed to +* the appropriate offset from the structure pointer. This allows us +* to perform only one malloc & free for each node. +* +* Buffers allocated: +* - vqa +* - vqa->CBData (list) +* - vqa->FrameData (list) +* - vqa->Drawer.ImageBuf +* - vqa->Audio.Buffer +* - vqa->Audio.IsLoaded +* - vqa->Foff +* +* INPUTS +* VQAHeader - Pointer to VQAHeader structure. +* VQAConfig - Pointer to VQA configuration structure. +* +* RESULT +* VQAData - Pointer to initialized VQAData structure. +* +****************************************************************************/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config) +{ + VQAData *vqa; + VQACBNode *cbnode; + VQACBNode *this_cb; + VQAFrameNode *framenode; + VQAFrameNode *this_frame; + long i; + + /* Check the configuration for valid values. */ + if ((config->NumCBBufs == 0) || (config->NumFrameBufs == 0) + || (config->AudioBufSize < config->HMIBufSize)) { + + return (NULL); + } + + /* Allocate the master structure */ + if ((vqa = (VQAData *)malloc(sizeof(VQAData))) == NULL) { + return (NULL); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VQA DATA STRUCTURES. + * + * Pointers are set to NULL initially, and filled in as the buffers are + * allocated. The Max buffer sizes are computed with 1K of padding, + * and'd with 0xFFFC to make the size divisible by 4, to ensure DWORD + * alignment. + *-----------------------------------------------------------------------*/ + memset(vqa, 0, sizeof(VQAData)); + vqa->MemUsed = sizeof(VQAData); + vqa->Drawer.LastTime = (-VQA_TIMETICKS); + + /* Set maximum codebook size. */ + vqa->Max_CB_Size = ((header->CBentries) * header->BlockWidth + * header->BlockHeight + 250) & 0xFFFC; + + /* Set maximum palette size. */ + vqa->Max_Pal_Size = (768 + 1024) & 0xFFFC; + + /* Set maximum vector pointers size. */ + vqa->Max_Ptr_Size = ((header->ImageWidth / header->BlockWidth) + * (header->ImageHeight / header->BlockHeight) + * sizeof(short) + 1024) & 0xFFFC; + + /* Set the frame number of the frame containing the last codebook. */ + vqa->Loader.LastCBFrame = (((header->Frames - 1) / header->Groupsize) + * header->Groupsize); + + /*------------------------------------------------------------------------- + * ALLOCATE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumCBBufs; i++) { + + /* Allocate a codebook node. */ + cbnode = (VQACBNode *)malloc((sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* If failure then clean up and exit. */ + if (cbnode == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQACBNode) + vqa->Max_CB_Size); + + /* Initialize the node */ + memset(cbnode, 0, sizeof(VQACBNode)); + cbnode->Buffer = (unsigned char *)cbnode + sizeof(VQACBNode); + + /* Install the node */ + if (i == 0) { + vqa->CBData = cbnode; + this_cb = cbnode; + } else { + this_cb->Next = cbnode; + this_cb = cbnode; + } + } + + /* Make the list circular */ + cbnode->Next = vqa->CBData; + + /* Install the Codebook list */ + vqa->Loader.CurCB = vqa->CBData; + vqa->Loader.FullCB = vqa->CBData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumFrameBufs; i++) { + + /* Allocate a pointer node */ + framenode = (VQAFrameNode *)malloc((sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size + vqa->Max_Pal_Size)); + + /* If failure then clean up and exit. */ + if (framenode == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Initialize the node */ + memset(framenode, 0, sizeof(VQAFrameNode)); + framenode->Pointers = (unsigned char *)framenode + sizeof(VQAFrameNode); + framenode->Palette = (unsigned char *)framenode + sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size; + + framenode->Codebook = vqa->CBData; + + /* Install the node */ + if (i == 0) { + vqa->FrameData = framenode; + this_frame = framenode; + } else { + this_frame->Next = framenode; + this_frame = framenode; + } + } + + /* Make the list circular */ + framenode->Next = vqa->FrameData; + + /* Install the Frame Buffer list */ + vqa->Loader.CurFrame = vqa->FrameData; + vqa->Drawer.CurFrame = vqa->FrameData; + vqa->Flipper.CurFrame = vqa->FrameData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE IMAGE BUFFERS IF ONE IS NOT ALREADY PROVIDED. + *-----------------------------------------------------------------------*/ + if (config->ImageBuf == NULL) { + + /* Allocate our own buffer. */ + if (config->DrawFlags & VQACFGF_BUFFER) { + vqa->Drawer.ImageBuf = (unsigned char *)malloc((header->ImageWidth + * header->ImageHeight)); + + /* If the allocation failed we must free up and exit. */ + if (vqa->Drawer.ImageBuf == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Plugin image buffer information. */ + vqa->Drawer.ImageWidth = header->ImageWidth; + vqa->Drawer.ImageHeight = header->ImageHeight; + vqa->MemUsed += (long)(header->ImageWidth * header->ImageHeight); + } else { + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + } else { + + /* Use caller provided buffer */ + vqa->Drawer.ImageBuf = config->ImageBuf; + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + + /*------------------------------------------------------------------------- + * ALLOCATE AND INITIALIZE AUDIO BUFFERS AND STRUCTURES. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + /* Version 1 VQA's only supported 22050 8 bit mono audio. */ + if (header->Version < VQAHD_VER2) { + vqa->Audio.SampleRate = 22050U; + vqa->Audio.Channels = 1; + vqa->Audio.BitsPerSample = 8; + } else { + vqa->Audio.SampleRate = header->SampleRate; + vqa->Audio.Channels = header->Channels; + vqa->Audio.BitsPerSample = header->BitsPerSample; + } + + if ((config->AudioBufSize == 0) && (config->HMIBufSize == 0)) { + vqa->Audio.NumAudBlocks = 0; + } else { + vqa->Audio.NumAudBlocks = (config->AudioBufSize / config->HMIBufSize); + } + + if (config->AudioBufSize > 0) { + + /* Allocate an audio buffer if the user did not provide one. */ + if (config->AudioBuf == NULL) { + vqa->Audio.Buffer = (unsigned char *)malloc(config->AudioBufSize); + + /* If failure then clean up and exit. */ + if (vqa->Audio.Buffer == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Add audio buffer size to memory usage. */ + vqa->MemUsed += (long)config->AudioBufSize; + } else { + vqa->Audio.Buffer = config->AudioBuf; + } + + /* Allocate IsLoaded flags */ + vqa->Audio.IsLoaded = (short *)malloc(vqa->Audio.NumAudBlocks + * sizeof(short)); + + /* If failure then clean up and exit. */ + if (vqa->Audio.IsLoaded == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Keep a running total of memory usage. */ + vqa->MemUsed += (long)(vqa->Audio.NumAudBlocks * sizeof(short)); + + /* Initalize audio frames is loaded flags to false. */ + memset(vqa->Audio.IsLoaded,0,vqa->Audio.NumAudBlocks * sizeof(short)); + + /* Temp buffer */ + #if(0) // DENZIL + vqa->Audio.TempBuf = (unsigned char *)malloc(VQA_AUD_TEMPSIZE); + #else + + /* Adjust the temp buffer size for the size of audio data. */ + i = VQA_AUD_TEMPSIZE; + i *= vqa->Audio.Channels * (vqa->Audio.BitsPerSample >> 3); + vqa->Audio.TempBuf = (unsigned char *)malloc(i); + #endif + + if (vqa->Audio.TempBuf == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Keep a running total of memory usage. */ + vqa->MemUsed += i; + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME INFORMATION TABLE IF REQUESTED. + *-----------------------------------------------------------------------*/ + if (config->OptionFlags & VQAOPTF_FINF) { + vqa->Foff = (long *)malloc(header->Frames * sizeof(long)); + + if (vqa->Foff == NULL) { + FreeBuffers(vqa, config); + return (NULL); + } + + /* Keep a running total of memory usage. */ + vqa->MemUsed += (header->Frames * sizeof(long)); + } + + return (vqa); +} + + +/**************************************************************************** +* +* NAME +* FreeBuffers - Free VQA play buffers. +* +* SYNOPSIS +* FreeBuffers(VQAData) +* +* void FreeBuffers(VQAData *); +* +* FUNCTION +* Free the buffers allocated by AllocBuffers(). +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FreeBuffers(VQAData *vqa, VQAConfig *config) +{ + VQACBNode *cb_this, *cb_next; + VQAFrameNode *frame_this, *frame_next; + long i; + + /*------------------------------------------------------------------------- + * FREE THE FRAME INFORMATION TABLE. + *-----------------------------------------------------------------------*/ + if (vqa->Foff) { + free(vqa->Foff); + } + + /*------------------------------------------------------------------------- + * FREE THE AUDIO BUFFERS. + *-----------------------------------------------------------------------*/ + + #if(VQAAUDIO_ON) + if ((config->AudioBuf == NULL) && (vqa->Audio.Buffer)) { + free(vqa->Audio.Buffer); + } + + /* Free the audio segments loaded flag array. */ + if (vqa->Audio.IsLoaded) { + free(vqa->Audio.IsLoaded); + } + + /* Free the temporary audio buffer. */ + if (vqa->Audio.TempBuf) { + free(vqa->Audio.TempBuf); + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * FREE THE IMAGE BUFFER ONLY IF WE ALLOCATED IT. + *-----------------------------------------------------------------------*/ + if ((config->ImageBuf == NULL) && vqa->Drawer.ImageBuf) { + free(vqa->Drawer.ImageBuf); + } + + /*------------------------------------------------------------------------- + * FREE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + frame_this = vqa->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame_this) { + frame_next = frame_this->Next; + free(frame_this); + frame_this = frame_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + cb_this = vqa->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (cb_this) { + cb_next = cb_this->Next; + free(cb_this); + cb_this = cb_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE VQA DATA STRUCTURES. + *-----------------------------------------------------------------------*/ + free(vqa); +} + + +/**************************************************************************** +* +* NAME +* Load_VQF - Loads a VQ Frame chunk. +* +* SYNOPSIS +* Error = Load_VQF(VQAFile, VQAData, Iffsize) +* +* short Load_VQF(VQAFile *, VQAData *, unsigned long); +* +* FUNCTION +* The VQ Frame Chunk contains a set of other chunks (codebooks, +* palettes, pointers). This routine reads the frame's chunk size, +* then loops until it's read that many bytes. +* +* INPUTS +* VQAFile - Pointer to VQAFile structure. +* VQAData - Pointer to VQAData structure. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_VQF(VQAHandleP *vqap, unsigned long frame_iffsize) +{ + VQAData *vqabuf; + VQAFrameNode *curframe; + ChunkHeader chunk; + unsigned long iffsize; + unsigned long framesize; + unsigned long bytes_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + curframe = vqabuf->Loader.CurFrame; + framesize = PADSIZE(frame_iffsize); + + /*------------------------------------------------------------------------- + * FRAME LOADING LOOP. + *-----------------------------------------------------------------------*/ + while (bytes_loaded < framesize) { + + /* Read chunk ID */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk.size); + bytes_loaded += 8; + bytes_loaded += PADSIZE(iffsize); + + /* Handle each chunk type */ + switch (chunk.id) { + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + break; + + /* An unknown chunk in the video frame is an error. */ + default: + return (VQAERR_READ); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_FINF - Load Frame Info chunk. +* +* SYNOPSIS +* Error = Load_FINF(VQAFile, VQAData, Iffsize) +* +* short Load_FINF(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* Load FINF chunk if buffer available, otherwise skip it. +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + + /* Load the frame information table if we need to, otherwise we will + * skip it. + */ + if ((vqap->Config.OptionFlags & VQAOPTF_FINF) && (vqabuf->Foff != NULL)) { + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, vqabuf->Foff, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + } else { + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, + PADSIZE(iffsize))) { + + return (VQAERR_SEEK); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQHD - Load VQA header chunk. +* +* SYNOPSIS +* Error = Load_VQHD(VQAFile, VQAData, Iffsize) +* +* short Load_VQHD(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize) +{ + /* Read the header */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &vqap->Header, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reconfigure the Drawer for the new settings */ + VQA_Configure_Drawer(vqap); + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBF0 - Load full uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBF0(VQAFile, VQAData, Iffsize) +* +* short Load_CBF0(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + + /* Read into the start of the buffer */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curcb->Buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as uncompressed. */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Clock pointers to next CB Buffer. */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBFZ - Load full compressed codebook. +* +* SYNOPSIS +* Error = Load_CBFZ(VQAFile, VQAData, Iffsize) +* +* short Load_CBFZ(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Load the codebook into the end of the buffer. */ + lcwoffset = vqap->VQABuf->Max_CB_Size - (unsigned short)padsize; + buffer = curcb->Buffer + lcwoffset; + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as compressed */ + curcb->Flags |= VQACBF_CBCOMP; + curcb->CBOffset = lcwoffset; + + /* Clock pointers to next CB Buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBP0 - Load partial uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBP0(VQAFile, VQAData, Iffsize) +* +* short Load_CBP0(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + + /*------------------------------------------------------------------------- + * ASSEMBLY PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = curcb->Buffer + loader->PartialCBSize; + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Accumulate the partial codebook values. */ + loader->PartialCBSize += (unsigned short)iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as uncompressed */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBPZ - Load partial compressed codebook. +* +* SYNOPSIS +* Error = Load_CBPZ(VQAFile, VQAData, Iffsize) +* +* short Load_CBPZ(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + + /* Dereference commonly used data members for quicker access */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Attempt to compute the LCW offset into the codebook buffer by + * multiplying the size of this chunk by the # frames/group, and adding + * a small fudge factor on, then subtracting that from the CB buffer size. + */ + if (loader->PartialCBSize == 0) { + curcb->CBOffset = (vqabuf->Max_CB_Size - ((unsigned short)padsize + * vqap->Header.Groupsize + 100)); + } + + /*------------------------------------------------------------------------- + * ASSEMBLE PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = ((curcb->Buffer + curcb->CBOffset) + loader->PartialCBSize); + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Accumulate partial codebook values */ + loader->PartialCBSize += (unsigned short)iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as compressed. */ + curcb->Flags |= VQACBF_CBCOMP; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPL0 - Load an uncompressed palette. +* +* SYNOPSIS +* Error = Load_CPL0(VQAFile, VQAData, Iffsize) +* +* short Load_CPL0(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the palette into the palette buffer */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Palette, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag the palette as uncompressed. */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + curframe->PalOffset = 0; + curframe->PaletteSize = (unsigned short)iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPLZ - Load compressed palette. +* +* SYNOPSIS +* Error = Load_CPLZ(VQAFile, VQAData, Iffsize) +* +* short Load_CPLZ(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + + /* Read the palette into the end of the palette buffer. */ + lcwoffset = vqap->VQABuf->Max_Pal_Size - (unsigned short)padsize; + buffer = curframe->Palette + lcwoffset; + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this palette as compressed. */ + curframe->Flags |= VQAFRMF_PALCOMP; + curframe->PalOffset = lcwoffset; + curframe->PaletteSize = (unsigned short)iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPT0 - Load uncompressed pointers. +* +* SYNOPSIS +* Error = Load_VPT0(VQAFile, VQAData, Iffsize) +* +* short Load_VPT0(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the pointers into start of the pointer buffer. */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Pointers, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag this frame as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + curframe->PtrOffset = 0; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPTZ - Load compressed pointers. +* +* SYNOPSIS +* Error = Load_VPTZ(VQAFile, VQAData, Iffsize) +* +* short Load_VPTZ(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + lcwoffset = vqap->VQABuf->Max_Ptr_Size - (unsigned short)padsize; + + /* Read the pointers into end of the pointer buffer. */ + buffer = curframe->Pointers + lcwoffset; + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as compressed. */ + curframe->Flags |= VQAFRMF_PTRCOMP; + curframe->PtrOffset = lcwoffset; + + return (0); +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* Load_SND0 - Load uncompressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND0(VQAFile, VQAData, Iffsize) +* +* short Load_SND0(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned long padsize; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE((unsigned short)iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk. + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (audio->Buffer == NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read large startup chunk directly into AudioBuf */ + if ((padsize > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + + loader->AudBufPos += (unsigned short)iffsize; + + /* Flag the audio frame flags as loaded for the initial audio frame. */ + for (i = 0; i < (iffsize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Read data into TempBuf */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } + + /* Set the TempBufLen */ + audio->TempBufLen = (unsigned short)iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND1 - Load compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND1(VQAFile, VQAData, Iffsize) +* +* short Load_SND1(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + unsigned long uncomp_size; + unsigned long comp_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE((unsigned short)iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read the uncompressed data size */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &uncomp_size, 2)) { + return (VQAERR_READ); + } + + /* Read the compressed data size */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, &comp_size, 2)) { + return (VQAERR_READ); + } + + /* Adjust chunk size */ + padsize -= 4; + + /* Read large startup chunk directly into AudioBuf */ + if ((uncomp_size > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { + + /* Load uncompressed data */ + if (uncomp_size == comp_size) { + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, + padsize)) { + + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->Buffer, uncomp_size); + } + + /* Set buffer positions & flags */ + loader->AudBufPos += uncomp_size; + + for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + if (uncomp_size == comp_size) { + + /* If the frame is uncompressed the load it in directly. */ + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load the audio frame into the end of the buffer. */ + loadbuf = ((audio->TempBuf + VQA_AUD_TEMPSIZE) - padsize); + + if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->TempBuf, uncomp_size); + } + + /* Set the TempBufLen */ + audio->TempBufLen = uncomp_size; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND2 - Load compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND2(VQAFile, VQAData, Iffsize) +* +* short Load_SND2(VQAFile *, VQAData *, Iffsize); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + unsigned long uncomp_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->StreamHandler((VQAHandle *)vqap, VQACMD_SEEK, NULL, padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + uncomp_size = iffsize * (8 / 4); + + /* Read large startup chunk directly into AudioBuf */ + if ((uncomp_size > VQA_AUD_TEMPSIZE) && (loader->AudBufPos == 0)) { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->sSOSInfo.lpSource = (char *)loadbuf; + audio->sSOSInfo.lpDest = (char *)audio->Buffer; + sosCODECDecompressData(&audio->sSOSInfo, uncomp_size); + + /* Set buffer positions & flags */ + loader->AudBufPos += uncomp_size; + + for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + loadbuf = ((audio->TempBuf + VQA_AUD_TEMPSIZE) - padsize); + + if (vqap->StreamHandler((VQAHandle *)vqap,VQACMD_READ,loadbuf,padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->sSOSInfo.lpSource = (char *)loadbuf; + audio->sSOSInfo.lpDest = (char *)audio->TempBuf; + sosCODECDecompressData(&audio->sSOSInfo, uncomp_size); + + /* Set the TempBufLen */ + audio->TempBufLen = uncomp_size; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Copy_SND - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = Copy_SND(VQAFile, VQAData) +* +* short Load_SND0(VQAFile *, VQAData *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Copy_SND(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (loader->AudBufPos / config->HMIBufSize); + endblock = (loader->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + loader->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + loader->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - loader->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + loader->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + loader->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +#if(VQAVOC_ON && VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* Load_VOC_Block - Loads blocks from seperate VOC file. +* +* SYNOPSIS +* Load_VOC_Block(VQAFile, VQAData) +* +* void Load_VOC_Block(VQAFile *, VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAFile - Pointer to VQAFile structure +* VQAData - Pointer to VQAData structure +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Load_VOC_Block(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + static long lastplayblock = -1; + static long myblock = 0; + static long firsttime = 1; + long numblocks; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + + /* Do nothing if no buffer */ + if (audio->Buffer == NULL) { + return; + } + + /* If this is the first time we're called, pre-load the 1st 'n' audio + * blocks, where 'n' is half the total audio buffer size; this way, we'll + * always stay ahead of HMI. + */ + if (firsttime) { + numblocks = (audio->NumAudBlocks / 2); + read(vqap->vocfh, audio->Buffer, config->HMIBufSize * numblocks); + loader->AudBufPos += config->HMIBufSize * numblocks; + + if (loader->AudBufPos >= config->AudioBufSize) { + loader->AudBufPos = 0; + } + + for (i = 0; i < numblocks; i++) { + audio->IsLoaded[i] = 1; + } + + myblock += numblocks; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + + firsttime = 0; + } + + /* If HMI's block has changed, load the next block & mark it as loaded */ + if (audio->PlayPosition / config->HMIBufSize != lastplayblock) { + + /* update HMI's last known block position */ + lastplayblock = audio->PlayPosition / config->HMIBufSize; + + /* read the VOC data */ + read(vqap->vocfh, (audio->Buffer + myblock * config->HMIBufSize), + config->HMIBufSize); + + loader->AudBufPos += config->HMIBufSize; + + if (loader->AudBufPos >= config->AudioBufSize) { + loader->AudBufPos = 0; + } + + /* set the IsLoaded flags */ + audio->IsLoaded[myblock] = 1; + + /* increment my block counter */ + myblock++; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + } +} +#endif /* VQAVOC_ON */ +#endif /* VQAAUDIO_ON */ + diff --git a/VQ/VQA32/LOADER.CPP b/VQ/VQA32/LOADER.CPP new file mode 100644 index 0000000..6a29caa --- /dev/null +++ b/VQ/VQA32/LOADER.CPP @@ -0,0 +1,2832 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* loader.c +* +* DESCRIPTION +* Stream loading and pre-processing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 21, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Open - Open a VQA file to play. +* VQA_Close - Close an opened VQA file. +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* PRIVATE +* AllocBuffers - Allocates the numerous VQA play buffers +* FreeBuffers - Frees the VQA play buffers +* PrimeBuffers - Pre-Load the internal buffers. +* Load_FINF - Loads the Frame Info Table. +* Load_VQHD - Loads a VQA Header. +* Load_CBF0 - Loads a full, uncompressed codebook +* Load_CBFZ - Loads a full, compressed codebook +* Load_CBP0 - Loads a partial uncompressed codebook +* Load_CBPZ - Loads a partial compressed codebook +* Load_CPL0 - Loads an uncompressed palette +* Load_CPLZ - Loads a compressed palette +* Load_VPT0 - Loads uncompressed pointers +* Load_VPTZ - Loads compressed pointers +* Load_VQF - Loads a VQ Frame chunk +* Load_SND0 - Loads an uncompressed sound chunk +* Load_SND1 - Loads a compressed sound chunk +* Load_AudFrame - Loads blocks from separate audio file, if needed. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config); +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header); +static long PrimeBuffers(VQAHandle *vqa); +static long Load_VQF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAAUDIO_ON) +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAVOC_ON) +static void Load_AudFrame(VQAHandleP *vqap); +#endif /* VQAVOC_ON */ + +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_Open - Open a VQA file to play. +* +* SYNOPSIS +* Error = VQA_Open(VQA, Name, Config) +* +* long VQA_Open(VQAHandle *, char *, VQAConfig *); +* +* FUNCTION +* - Open a VQA file for reading. +* - Validate that it is an IFF file, of the VQA type. +* - Read the VQA header. +* - Open a VOC file for playback, if requested. +* - Set the Loader's frame rate, if the caller's Config structure's +* FrameRate is set to -1 +* - Set the Drawer's frame rate, if the caller's Config structure's +* DrawRate is set to -1 +* +* INPUTS +* VQA - Pointer to initialized handle. Obtained by VQA_Alloc(). +* Name - Pointer to name of VQA file to open. +* Config - Pointer to initialized VQA configuration structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_ error code. +* +****************************************************************************/ + +#define OPEN_VQHD (1<<0) +#define OPEN_FINF (1<<1) +#define OPEN_CAPTIONS (1<<2) +#define OPEN_EVA (1<<3) + +long VQA_Open(VQAHandle *vqa, char const *filename, VQAConfig *config) +{ + VQAHandleP *vqap; + VQAHeader *header; + ChunkHeader chunk; + long max_frm_size; + long i; + long done; + long found; + char *ptr; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + header = &vqap->Header; + + /*------------------------------------------------------------------------- + * VERIFY VALIDITY OF VQA FILE. + *-----------------------------------------------------------------------*/ + + /* Open the file. */ + if (vqap->IOHandler(vqa, VQACMD_OPEN, (void *)filename, 0)) { + return (VQAERR_OPEN); + } + + /* Read the file ID & Size */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify an IFF FORM */ + if ((chunk.id != ID_FORM) || (chunk.size == 0)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read in WVQA ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 4)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify VQA */ + if (chunk.id != ID_WVQA) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE PLAYERS CONFIGURATION + *-----------------------------------------------------------------------*/ + + /* Use the clients configuration if they provided one. */ + if (config != NULL) { + memcpy(&vqap->Config, config, sizeof(VQAConfig)); + } else { + VQA_DefaultConfig(&vqap->Config); + } + + /* Use the internal configuration structure from now on. */ + config = &vqap->Config; + + /*------------------------------------------------------------------------- + * PROCESS THE PRE-FRAME CHUNKS (VQHD, CAP, FINF, ETC...) + *-----------------------------------------------------------------------*/ + found = 0; + done = 0; + + while (!done) { + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + chunk.size = REVERSE_LONG(chunk.size); + + switch (chunk.id) { + + /*--------------------------------------------------------------------- + * READ IN THE VQA HEADER. + *-------------------------------------------------------------------*/ + case ID_VQHD: + if (chunk.size != sizeof(VQAHeader)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read the header data. */ + if (vqap->IOHandler(vqa, VQACMD_READ, header, PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /*------------------------------------------------------------------- + * SETUP THE CONFIGURATION FROM THE HEADER. + *-----------------------------------------------------------------*/ + if (config->ImageWidth == -1) { + config->ImageWidth = header->ImageWidth; + } + + if (config->ImageHeight == -1) { + config->ImageHeight = header->ImageHeight; + } + + /* If Loaders frame rate is -1 then use the value from the header. */ + if (config->FrameRate == -1) { + config->FrameRate = header->FPS; + } + + /* If Drawers frame rate is -1 then use the value from the header, + * which will result in a "variable" frame rate. + */ + if (config->DrawRate == -1) { + config->DrawRate = header->FPS; + } + + /* Finally, if the DrawRate was set to -1 or 0 (ie MaxRate contained + * bogus values), set it to the header value. + */ + if ((config->DrawRate == -1) || (config->DrawRate == 0)) { + config->DrawRate = header->FPS; + } + + #if(VQAAUDIO_ON) + /* If an alternate audio track is not available then turn it off. + * This enables the primary audio track to be played. + */ + if ((header->Version > VQAHD_VER1) + && !(header->Flags & VQAHDF_ALTAUDIO)) { + config->OptionFlags &= ~VQAOPTF_ALTAUDIO; + } + #endif + + /*------------------------------------------------------------------- + * ALLOCATE THE BUFFERS THAT WE NEED TO PLAY THE VQA. + *-----------------------------------------------------------------*/ + if ((vqap->VQABuf = AllocBuffers(header, config)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_VQHD; + break; + + /*--------------------------------------------------------------------- + * READ IN AND OPEN THE CAPTIONS STREAM. + *-------------------------------------------------------------------*/ + case ID_CAP0: + if ((config->CapFont != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + + short size = 0; + + /* Get uncompressed size of captions. */ + if (vqap->IOHandler(vqa, VQACMD_READ, &size, sizeof(short))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Allocate buffer for captions. */ + i = size + 50; + + if ((ptr = (char *)malloc(i)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /* Read in the captions chunk. */ + i -= PADSIZE(chunk.size); + + if (vqap->IOHandler(vqa, VQACMD_READ, (ptr + i), + PADSIZE(chunk.size - sizeof(short)))) { + + free(ptr); + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Decompress the captions. */ + LCW_Uncompress((ptr + i), ptr, size); + vqap->Caption = OpenCaptions(ptr, config->CapFont); + + if (vqap->Caption == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_CAPTIONS; + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + } + break; + + case ID_EVA0: + if ((config->EVAFont != NULL) + && (config->OptionFlags & VQAOPTF_EVA)) { + + short size = 0; + + /* Get uncompressed size of captions. */ + if (vqap->IOHandler(vqa, VQACMD_READ, &size, sizeof(short))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Allocate buffer for captions. */ + i = size + 50; + + if ((ptr = (char *)malloc(i)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /* Read in the captions chunk. */ + i -= PADSIZE(chunk.size); + + if (vqap->IOHandler(vqa, VQACMD_READ, (ptr + i), + PADSIZE(chunk.size - sizeof(short)))) { + free (ptr); + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Decompress the captions. */ + LCW_Uncompress((ptr + i), ptr, size); + vqap->EVA = OpenCaptions(ptr, config->EVAFont); + + if (vqap->EVA == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_EVA; + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + } + break; + + /*--------------------------------------------------------------------- + * READ FRAME INFORMATION + *-------------------------------------------------------------------*/ + case ID_FINF: + if (Load_FINF(vqap, chunk.size)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + done = 1; + break; + + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VIDEO SYSTEM IF WE ARE REQUIRED TO HANDLE THAT. + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + if ((vqap->VQABuf->Drawer.Display = SetVideoMode(config->Vmode)) == 0) { + VQA_Close(vqa); + return (VQAERR_VIDEO); + } + + /* Set the VBIBit polarity. */ + vqap->VQABuf->VBIBit = GetVBIBit(); + #else + if (config->VBIBit == -1) { + config->VBIBit = TestVBIBit(); + } + + vqap->VQABuf->VBIBit = config->VBIBit; + + if (found & (OPEN_CAPTIONS|OPEN_EVA)) { + SetDAC(251,255,255,255); /* White */ + SetDAC(252,255,000,000); /* Red */ + SetDAC(253,000,255,000); /* Green */ + SetDAC(254,255,255,255); + SetDAC(255,255,000,255); /* Cycle */ + } + #endif /* VQAVIDEO_ON */ + + /*------------------------------------------------------------------------- + * AUDIO TRACK OVERRIDE FROM EXTERNAL FILE (.VOC) + *-----------------------------------------------------------------------*/ + + /* Open VOC file if one is requested. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (config->VocFile != NULL) { + vqap->vocfh = open(config->VocFile, (O_RDONLY|O_BINARY)); + } else { + vqap->vocfh = -1; + } + + /* Make sure we won't try to play audio. */ + if ((vqap->vocfh == -1) && ((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #else /* VQAVOC_ON */ + + /* If the movie does not contain an audio track make sure we won't try + * to play one. + */ + if (((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #endif /* VQAVOC_ON */ + + /*------------------------------------------------------------------------- + * INITIALIZE THE AUDIO PLAYBACK/TIMING SYSTEM. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Open HMI audio resource for playback. */ + if (VQA_OpenAudio(vqap)) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + + /* Initialize ADPCM information structure for audio stream. */ + sosCODECInitStream(&audio->ADPCM_Info); + + if (header->Version == VQAHD_VER1) { + audio->ADPCM_Info.wBitSize = 8; + audio->ADPCM_Info.dwUnCompSize = (22050L/header->FPS) * header->Frames; + audio->ADPCM_Info.wChannels = 1; + } else { + audio->ADPCM_Info.wBitSize = audio->BitsPerSample; + audio->ADPCM_Info.dwUnCompSize = (((audio->SampleRate / header->FPS) + * (audio->BitsPerSample >> 3)) * audio->Channels) * header->Frames; + + audio->ADPCM_Info.wChannels = audio->Channels; + } + + audio->ADPCM_Info.dwCompSize = audio->ADPCM_Info.dwUnCompSize + / (audio->ADPCM_Info.wBitSize / 4); + } + + /* Turn off audio if the HMI DigiHandle is invalid. */ + if (config->DigiHandle == -1) { + config->OptionFlags &= ~VQAOPTF_AUDIO; + } + + /* Setup the timer interrupt if the client requests it for the timing + * source. + */ + if (!(config->OptionFlags & VQAOPTF_AUDIO) + || (config->TimerMethod == VQA_TMETHOD_INT)) { + + /* Start HMI timer system for timing. */ + if (VQA_StartTimerInt(vqap, (config->OptionFlags & VQAOPTF_HMIINIT))) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * PRIME THE BUFFERS BY PRE-LOADING THEM WITH FRAME DATA. + *-----------------------------------------------------------------------*/ + if (PrimeBuffers(vqa) != 0) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_Close - Close an opened VQA file. +* +* SYNOPSIS +* VQA_Close(VQA) +* +* void VQA_Close(VQAHandle *); +* +* FUNCTION +* Close the file that was opened with VQA_Open(). +* +* INPUTS +* VQA - Pointer VQAHandle to close. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Close(VQAHandle *vqa) +{ + long (*iohandler)(VQAHandle *, long, void *, long); + + /* Restore video mode to text. */ + #if(VQAVIDEO_ON) + SetVideoMode(TEXT); + #endif /* VQAVIDEO_ON */ + + /* Shutdown audio/timing system. */ + #if(VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->Config.OptionFlags & VQAOPTF_AUDIO) { + VQA_CloseAudio((VQAHandleP *)vqa); + } else { + VQA_StopTimerInt((VQAHandleP *)vqa); + } + #endif /* VQAAUDIO_ON */ + + /* Free captions. */ + if (((VQAHandleP *)vqa)->Caption != NULL) { + if (((VQAHandleP *)vqa)->Caption->Buffer != NULL) { + free(((VQAHandleP *)vqa)->Caption->Buffer); + } + + CloseCaptions(((VQAHandleP *)vqa)->Caption); + } + + /* Free EVA. */ + if (((VQAHandleP *)vqa)->EVA != NULL) { + if (((VQAHandleP *)vqa)->EVA->Buffer != NULL) { + free(((VQAHandleP *)vqa)->EVA->Buffer); + } + + CloseCaptions(((VQAHandleP *)vqa)->EVA); + } + + /* Free memory */ + if (((VQAHandleP *)vqa)->VQABuf != NULL) { + FreeBuffers(((VQAHandleP *)vqa)->VQABuf, &((VQAHandleP *)vqa)->Config, + &((VQAHandleP *)vqa)->Header); + } + + /* Close the VOC override file if one was opened */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->vocfh != -1) { + close(((VQAHandleP *)vqa)->vocfh); + } + #endif /* VQAVOC_ON */ + + /* Close the VQA file */ + ((VQAHandleP *)vqa)->IOHandler(vqa, VQACMD_CLOSE, NULL, 0); + + /* Reset the VQAHandle */ + iohandler = ((VQAHandleP *)vqa)->IOHandler; + memset(vqa, 0, sizeof(VQAHandleP)); + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* +* SYNOPSIS +* Error = VQA_LoadFrame(VQA) +* +* long VQA_LoadFrame(VQAHandle *); +* +* FUNCTION +* The codebook is split up such that the last frame of every group gets +* a new, complete codebook, ready for the next group. The first codebook +* in the VQA is a full codebook, and goes with the first frame's data. +* Partial codebooks are stored per frame after that, and they add up to +* a full codebook just before the first frame for the next group is read. +* +* (Currently, this routine can read either the older non-frame-grouped +* VQA file format, or the new frame-chunk format. For the older format, +* it's assumed that the last chunk in a frame is the pointer data.) +* +* This routine also does a sort of "cooperative multitasking". If the +* Loader hits a "wait state" where it has to wait on the audio to finish +* playing before it can continue to load, it sets a "sleep" flag and +* just returns. The sleep flag is checked on entry to see if it needs +* to jump to the proper execution point. This may improve performance on +* some platforms, but it also allows the Loader to be called regardless +* of the size of the buffers; if the buffers fill up or the audio fails +* to play, the Loader won't just get stuck. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long VQA_LoadFrame(VQAHandle *vqa) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + long frame_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curframe = loader->CurFrame; + chunk = &loader->CurChunkHdr; + + /* We have reached the end of the file if we loaded all the frames. */ + if (loader->CurFrameNum >= vqap->Header.Frames) { + return (VQAERR_EOF); + } + + /* If we're reading audio from a VOC file then service that requirement. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (vqap->vocfh != -1) { + Load_AudFrame(vqap); + } + #endif /* VQAAUDIO_ON & VQAVOC_ON */ + + /* If no buffer is available for loading then return. This allows the + * drawer to service one of the buffers more readily. (We'll wait for one + * to free up). + */ + if (curframe->Flags & VQAFRMF_LOADED) { + loader->WaitsOnDrawer++; + return (VQAERR_NOBUFFER); + } + + /* If we're not sleeping, initialize */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + frame_loaded = 0; + loader->FrameSize = 0; + + /* Initialize the codebook ptr for the frame we're about to load: + * (This frame's codebook is the last full codebook; we have to init it + * now, because if we're on the last frame in a group, we'll get a new + * FullCB pointer.) + */ + curframe->Codebook = loader->FullCB; + } + + /*------------------------------------------------------------------------- + * THE MAIN LOADER LOOP + *-----------------------------------------------------------------------*/ + while (frame_loaded == 0) { + + /* Read new chunk, only if we're not sleeping */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + + /* Read chunk ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + loader->FrameSize += iffsize; + } + + /* Handle each chunk type */ + switch (chunk->id) { + + /* VQ Normal Frame */ + case ID_VQFR: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* VQ Key Frame */ + case ID_VQFK: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Pointer data Key (Must draw) */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Uncompressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + #if(VQAAUDIO_ON) + case ID_SND0: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA0: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* Compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND1: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA1: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* HMI ADPCM compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND2: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA2: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + #endif + + /* Skip any unknown chunks. */ + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + break; + } + } + + /* Update maximum frame size stat. */ + if ((loader->CurFrameNum>0) && (loader->FrameSize>loader->MaxFrameSize)) { + loader->MaxFrameSize = loader->FrameSize; + } + + /*------------------------------------------------------------------------- + * SET UP THE FRAME FOR DRAWING. + *-----------------------------------------------------------------------*/ + + /* Set the frame # */ + curframe->FrameNum = loader->CurFrameNum; + loader->CurFrameNum++; + + /* Update data for mono output */ + loader->LastFrameNum = loader->CurFrameNum; + + /* Loader is finished with this frame; tell Drawer to draw it */ + curframe->Flags |= VQAFRMF_LOADED; + loader->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* SYNOPSIS +* Frame = VQA_SeekFrame(VQA, Frame, FromWhere) +* +* long VQA_SeekFrame(VQAHandle *, long, long); +* +* FUNCTION +* This function sets the movie stream to the new frame specified by +* the 'offset' parameter. 'FromWhere' is a symbolic constant that is used +* to specify from where in the stream offset should be applied. +* +* INPUTS +* VQA - Pointer to VQAHandle of movie to seek into. +* Frame - Frame to seek to. +* FromWhere - Relative position indicator. +* +* RESULT +* Frame - New frame position or -1 if error. +* +****************************************************************************/ + +long VQA_SeekFrame(VQAHandle *vqa, long framenum, long fromwhere) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQAHeader *header; + VQAFrameNode *frame; + long group; + long i; + long rc = VQAERR_SLEEPING; + + #if(VQAAUDIO_ON) + VQAConfig *config; + VQAAudio *audio; + long audio_on; + #endif + + /* Dereference commonly used data members for quick access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + header = &vqap->Header; + + #if(VQAAUDIO_ON) + config = &vqap->Config; + #endif + + fromwhere = fromwhere; + + #if(VQAAUDIO_ON) + audio = &vqabuf->Audio; + + /* Stop audio playback. */ + audio_on = (audio->Flags & VQAAUDF_ISPLAYING); + VQA_StopAudio(vqap); + #endif + + /* Make sure the requested frame is valid and the frame information + * array is allocated before continuing. + */ + if ((framenum < header->Frames) && (vqabuf->Foff != NULL)) { + + /* Get the current frame. */ + frame = loader->CurFrame; + + /* Find and load the most recent palette. */ + for (i = framenum; i >= 0; i--) { + if (vqabuf->Foff[i] & VQAFINF_PAL) { + + /* Seek to the palette frame. */ + rc = vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[i])); + + if (!rc) { + + /* Fool the loader into thinking this frame is empty. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = 0; + frame->Flags = 0; + + /* Load the frame with the palette. */ + if (VQA_LoadFrame(vqa) == 0) { + + /* Decompress the palette if neccessary.*/ + if (frame->Flags & VQAFRMF_PALCOMP) { + frame->PaletteSize = LCW_Uncompress((char *)frame->Palette + + frame->PalOffset, (char *)frame->Palette, + vqabuf->Max_Pal_Size); + } + + SetPalette(frame->Palette, frame->PaletteSize, 0); + } + } else { + rc = VQAERR_SEEK; + } + break; + } + } + + if (!rc) { + /* Compute the starting group frame of the requested frame. */ + group = (framenum / header->Groupsize); + group = (group * header->Groupsize); + + /* The codebook for the group we want to goto is found in the previous + * group, with the exception of the very first group. + */ + if (group >= header->Groupsize) { + group -= header->Groupsize; + } + + /* Seek to the start of the group containing the partial codebooks for + * the target frame. + */ + if (!vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[group]))) { + + /* Throw away any audio frames that were loaded. */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) + && (audio->Buffer != NULL)) { + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + memset(audio->Buffer, 0, config->AudioBufSize); + + /* Position the audio buffer to 1/2 second. */ + audio->AudBufPos = (long)(((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)) / 2); + + /* Mark 1/2 second of the audio buffer as loaded. */ + for (i = 0; i < (audio->AudBufPos / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + } + #endif + + /* Force the loader to the desired frame. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = group; + + /* Load frames up to the target frame collecting partial codebooks + * along the way. + */ + for (i = 0; i < (framenum - group); i++) { + + /* Fool the loader into thinking the frame has been drawn. */ + loader->CurFrame->Flags = 0; + + #if(VQAAUDIO_ON) + audio->TempBufLen = 0; + #endif + + /* Load the frame. */ + if ((rc = VQA_LoadFrame(vqa)) != 0) { + if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + break; + } else { + rc = 0; + } + } + } + + /* If everything is okay, then re-prime the buffers. */ + if (!rc) { + + /* Mark all the frames except the current one as empty. */ + loader->CurFrame->Flags = 0; + frame = loader->CurFrame->Next; + + while (frame != loader->CurFrame) { + frame->Flags = 0; + frame = frame->Next; + } + + /* Set the drawer to the current frame and the loader + * to the next. + */ + vqabuf->Drawer.CurFrame = loader->CurFrame; + + /* Prime the buffers for the new position. */ + rc = PrimeBuffers(vqa); + + /* An end of file is not considered and error. */ + if ((rc == 0) || (rc == VQAERR_EOF)) { + rc = framenum; + } + } + } else { + rc = VQAERR_SEEK; + } + } + } + + /* Restart audio playback. */ + #if(VQAAUDIO_ON) + if (audio_on) { + VQA_StartAudio(vqap); + } + #endif + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* AllocBuffers - Allocate VQA play buffers. +* +* SYNOPSIS +* VQAData = AllocBuffers(Header, Config) +* +* VQAData *AllocBuffers(VQAHeader *, VQAConfig *); +* +* FUNCTION +* For those structures that contain buffer pointers (codebook nodes, +* frame buffer nodes), enough memory is allocated for both the structure +* and its associated buffers, then the buffer pointers are pointed to +* the appropriate offset from the structure pointer. This allows us +* to perform only one malloc & free for each node. +* +* Buffers allocated: +* - vqa +* - vqa->CBData (list) +* - vqa->FrameData (list) +* - vqa->Drawer.ImageBuf +* - vqa->Audio.Buffer +* - vqa->Audio.IsLoaded +* - vqa->Foff +* +* INPUTS +* Header - Pointer to VQAHeader structure. +* Config - Pointer to VQA configuration structure. +* +* RESULT +* VQAData - Pointer to initialized VQAData structure. +* +****************************************************************************/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config) +{ + VQAData *vqa; + VQACBNode *cbnode; + VQACBNode *this_cb; + VQAFrameNode *framenode; + VQAFrameNode *this_frame; + long i; + + /* Check the configuration for valid values. */ + if ((config->NumCBBufs == 0) || (config->NumFrameBufs == 0)) { + return (NULL); + } + + /* Allocate the master structure */ + if ((vqa = (VQAData *)malloc(sizeof(VQAData))) == NULL) { + return (NULL); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VQA DATA STRUCTURES. + * + * Pointers are set to NULL initially, and filled in as the buffers are + * allocated. The Max buffer sizes are computed with 1K of padding, + * and'd with 0xFFFC to make the size divisible by 4, to ensure DWORD + * alignment. + *-----------------------------------------------------------------------*/ + DPMI_Lock(vqa, sizeof(VQAData)); + memset(vqa, 0, sizeof(VQAData)); + vqa->MemUsed = sizeof(VQAData); + vqa->Drawer.LastTime = (-VQA_TIMETICKS); + + /* Set maximum codebook size. */ + vqa->Max_CB_Size = ((header->CBentries) * header->BlockWidth + * header->BlockHeight + 250) & 0xFFFC; + + /* Set maximum palette size. */ + vqa->Max_Pal_Size = (768 + 1024) & 0xFFFC; + + /* Set maximum vector pointers size. */ + vqa->Max_Ptr_Size = ((header->ImageWidth / header->BlockWidth) + * (header->ImageHeight / header->BlockHeight) + * sizeof(short) + 1024) & 0xFFFC; + + /* Set the frame number of the frame containing the last codebook. */ + vqa->Loader.LastCBFrame = (((header->Frames - 1) / header->Groupsize) + * header->Groupsize); + + /*------------------------------------------------------------------------- + * ALLOCATE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumCBBufs; i++) { + + /* Allocate a codebook node. */ + cbnode = (VQACBNode *)malloc((sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* If failure then clean up and exit. */ + if (cbnode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ + DPMI_Lock(cbnode, (sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQACBNode) + vqa->Max_CB_Size); + + /* Initialize the node */ + memset(cbnode, 0, sizeof(VQACBNode)); + cbnode->Buffer = (unsigned char *)cbnode + sizeof(VQACBNode); + + /* Install the node */ + if (i == 0) { + vqa->CBData = cbnode; + this_cb = cbnode; + } else { + this_cb->Next = cbnode; + this_cb = cbnode; + } + } + + /* Make the list circular */ + cbnode->Next = vqa->CBData; + + /* Install the Codebook list */ + vqa->Loader.CurCB = vqa->CBData; + vqa->Loader.FullCB = vqa->CBData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumFrameBufs; i++) { + + /* Allocate a pointer node */ + framenode = (VQAFrameNode *)malloc((sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size + vqa->Max_Pal_Size)); + + /* If failure then clean up and exit. */ + if (framenode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ + DPMI_Lock(framenode, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Initialize the node */ + memset(framenode, 0, sizeof(VQAFrameNode)); + framenode->Pointers = (unsigned char *)framenode + sizeof(VQAFrameNode); + framenode->Palette = (unsigned char *)framenode + sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size; + + framenode->Codebook = vqa->CBData; + + /* Install the node */ + if (i == 0) { + vqa->FrameData = framenode; + this_frame = framenode; + } else { + this_frame->Next = framenode; + this_frame = framenode; + } + } + + /* Make the list circular */ + framenode->Next = vqa->FrameData; + + /* Install the Frame Buffer list */ + vqa->Loader.CurFrame = vqa->FrameData; + vqa->Drawer.CurFrame = vqa->FrameData; + vqa->Flipper.CurFrame = vqa->FrameData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE IMAGE BUFFERS IF ONE IS NOT ALREADY PROVIDED. + *-----------------------------------------------------------------------*/ + if (config->ImageBuf == NULL) { + + /* Allocate our own buffer. */ + if (config->DrawFlags & VQACFGF_BUFFER) { + vqa->Drawer.ImageBuf = (unsigned char *)malloc((header->ImageWidth + * header->ImageHeight)); + + /* If the allocation failed we must free up and exit. */ + if (vqa->Drawer.ImageBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); + + /* Plugin image buffer information. */ + vqa->Drawer.ImageWidth = header->ImageWidth; + vqa->Drawer.ImageHeight = header->ImageHeight; + vqa->MemUsed += (long)(header->ImageWidth * header->ImageHeight); + } else { + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + } else { + + /* Use caller provided buffer */ + vqa->Drawer.ImageBuf = config->ImageBuf; + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + + /*------------------------------------------------------------------------- + * ALLOCATE AND INITIALIZE AUDIO BUFFERS AND STRUCTURES. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if ((header->Flags & VQAHDF_AUDIO) + && (config->OptionFlags & VQAOPTF_AUDIO)) { + + /* Dereference audio structure for quick access. */ + VQAAudio *audio = &vqa->Audio; + + /* Version 1 VQA's only supported 22050 8 bit mono audio. */ + if (header->Version < VQAHD_VER2) { + audio->SampleRate = 22050U; + audio->Channels = 1; + audio->BitsPerSample = 8; + audio->BytesPerSec = 22050; + } else { + if ((config->OptionFlags & VQAOPTF_ALTAUDIO) + && (header->Flags & VQAHDF_ALTAUDIO)) { + audio->SampleRate = header->AltSampleRate; + audio->Channels = header->AltChannels; + audio->BitsPerSample = header->AltBitsPerSample; + } else { + audio->SampleRate = header->SampleRate; + audio->Channels = header->Channels; + audio->BitsPerSample = header->BitsPerSample; + } + + audio->BytesPerSec = ((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)); + } + + /* Adjust the HMI buffer to accomodate the amount of data. */ + #if(0) + config->HMIBufSize *= (audio->SampleRate / 22050); + config->HMIBufSize *= audio->Channels * (audio->BitsPerSample >> 3); + #endif + + /* The default audio buffer size should be large enough to hold + * 1.5 seconds of data. + */ + if (config->AudioBufSize == -1) { + + /* Compute the number of HMI buffers that will completly fit into + * 1.5 seconds of audio data. + */ + i = ((audio->BytesPerSec+(audio->BytesPerSec/2))/config->HMIBufSize); + config->AudioBufSize = (config->HMIBufSize * i); + } + + /* Do not allocate anything if the audio buffer is zero length. */ + if (config->AudioBufSize > 0) { + + /* Allocate an audio buffer if the user did not provide one. + * Otherwise, use the user supplied buffer. + */ + if (config->AudioBuf == NULL) { + audio->Buffer = (unsigned char *)malloc(config->AudioBufSize); + + /* If failure then clean up and exit. */ + if (audio->Buffer == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + DPMI_Lock(audio->Buffer, config->AudioBufSize); + + /* Add audio buffer size to memory usage. */ + vqa->MemUsed += config->AudioBufSize; + } else { + audio->Buffer = config->AudioBuf; + } + + /* Allocate IsLoaded flags */ + audio->NumAudBlocks = (config->AudioBufSize / config->HMIBufSize); + audio->IsLoaded = (short *)malloc(audio->NumAudBlocks * sizeof(short)); + + /* If failure then clean up and exit. */ + if (audio->IsLoaded == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(audio->IsLoaded, audio->NumAudBlocks * sizeof(short)); + + /* Add IsLoaded flags array to memory usage. */ + vqa->MemUsed += (audio->NumAudBlocks * sizeof(short)); + + /* Initialize audio IsLoaded flags to false. */ + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + + /* Allocate temporary staging buffer for the audio frames. */ + audio->TempBufSize = ((audio->BytesPerSec / header->FPS) * 2) + 100; + audio->TempBuf = (unsigned char *)malloc(audio->TempBufSize); + + if (audio->TempBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(audio->TempBuf, audio->TempBufSize); + + /* Add temporary buffer size to memory usage. */ + vqa->MemUsed += audio->TempBufSize; + } + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME INFORMATION TABLE IF REQUESTED. + *-----------------------------------------------------------------------*/ + vqa->Foff = (long *)malloc(header->Frames * sizeof(long)); + + if (vqa->Foff == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(vqa->Foff, header->Frames * sizeof(long)); + + /* Keep a running total of memory usage. */ + vqa->MemUsed += (header->Frames * sizeof(long)); + + return (vqa); +} + + +/**************************************************************************** +* +* NAME +* FreeBuffers - Free VQA play buffers. +* +* SYNOPSIS +* FreeBuffers(VQAData, Config, Header) +* +* void FreeBuffers(VQAData *, VQAConfig *, VQAHeader *); +* +* FUNCTION +* Free the buffers allocated by AllocBuffers(). +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* Config - Pointer to configuration structure. +* Header - Pointer to movie header structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header) +{ + VQACBNode *cb_this, + *cb_next; + VQAFrameNode *frame_this, + *frame_next; + long i; + + /*------------------------------------------------------------------------- + * FREE THE FRAME INFORMATION TABLE. + *-----------------------------------------------------------------------*/ + if (vqa->Foff) { + DPMI_Unlock(vqa->Foff, header->Frames * sizeof(long)); + free(vqa->Foff); + } + + /*------------------------------------------------------------------------- + * FREE THE AUDIO BUFFERS. + *-----------------------------------------------------------------------*/ + + #if(VQAAUDIO_ON) + if ((config->AudioBuf == NULL) && (vqa->Audio.Buffer)) { + DPMI_Unlock(vqa->Audio.Buffer, config->AudioBufSize); + free(vqa->Audio.Buffer); + } + + /* Free the audio segments loaded flag array. */ + if (vqa->Audio.IsLoaded) { + DPMI_Unlock(vqa->Audio.IsLoaded,vqa->Audio.NumAudBlocks * sizeof(short)); + free(vqa->Audio.IsLoaded); + } + + /* Free the temporary audio buffer. */ + if (vqa->Audio.TempBuf) { + DPMI_Unlock(vqa->Audio.TempBuf, vqa->Audio.TempBufSize); + free(vqa->Audio.TempBuf); + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * FREE THE IMAGE BUFFER ONLY IF WE ALLOCATED IT. + *-----------------------------------------------------------------------*/ + if ((config->ImageBuf == NULL) && vqa->Drawer.ImageBuf) { + DPMI_Unlock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); + free(vqa->Drawer.ImageBuf); + } + + /*------------------------------------------------------------------------- + * FREE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + frame_this = vqa->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame_this) { + frame_next = frame_this->Next; + DPMI_Unlock(frame_this, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + free(frame_this); + frame_this = frame_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + cb_this = vqa->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (cb_this) { + cb_next = cb_this->Next; + DPMI_Unlock(cb_this, sizeof(VQACBNode) + vqa->Max_CB_Size); + free(cb_this); + cb_this = cb_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE VQA DATA STRUCTURES. + *-----------------------------------------------------------------------*/ + DPMI_Unlock(vqa, sizeof(VQAData)); + free(vqa); +} + + +/**************************************************************************** +* +* NAME +* PrimeBuffers - Pre-Load the internal buffers. +* +* SYNOPSIS +* Error = PrimeBuffers(VQA) +* +* long = PrimeBuffers(VQAHandle *); +* +* FUNCTION +* Pre-load the internal buffers in order to give the player some slack +* in the playback of large frames. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +long PrimeBuffers(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQAConfig *config; + long rc; + long i; + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + + /* Pre-load the buffers */ + for (i = 0; i < config->NumFrameBufs; i++) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + return (rc); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQF - Loads a VQ Frame chunk. +* +* SYNOPSIS +* Error = Load_VQF(VQA, Iffsize) +* +* long Load_VQF(VQAHandleP *, unsigned long); +* +* FUNCTION +* The VQ Frame Chunk contains a set of other chunks (codebooks, +* palettes, pointers). This routine reads the frame's chunk size, +* then loops until it's read that many bytes. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQF(VQAHandleP *vqap, unsigned long frame_iffsize) +{ + VQAData *vqabuf; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + unsigned long framesize; + unsigned long bytes_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + curframe = vqabuf->Loader.CurFrame; + chunk = &vqabuf->Loader.CurChunkHdr; + framesize = PADSIZE(frame_iffsize); + + /*------------------------------------------------------------------------- + * FRAME LOADING LOOP. + *-----------------------------------------------------------------------*/ + while (bytes_loaded < framesize) { + + /* Read chunk ID */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + bytes_loaded += 8; + bytes_loaded += PADSIZE(iffsize); + + /* Handle each chunk type */ + switch (chunk->id) { + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + break; + + /* An unknown chunk in the video frame is an error. */ + default: + return (VQAERR_READ); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_FINF - Load Frame Info chunk. +* +* SYNOPSIS +* Error = Load_FINF(VQA, Iffsize) +* +* long Load_FINF(VQAHandleP *, unsigned long); +* +* FUNCTION +* Load FINF chunk if buffer available, otherwise skip it. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + + /* Load the frame information table if we need to, otherwise we will + * skip it. + */ + if (vqabuf->Foff != NULL) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, vqabuf->Foff, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQHD - Load VQA header chunk. +* +* SYNOPSIS +* Error = Load_VQHD(VQA, Iffsize) +* +* long Load_VQHD(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize) +{ + /* Read the header */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &vqap->Header, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reconfigure the Drawer for the new settings */ + VQA_Configure_Drawer(vqap); + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBF0 - Load full uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBF0(VQA, Iffsize) +* +* long Load_CBF0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + + /* Read into the start of the buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curcb->Buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as uncompressed. */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Clock pointers to next CB Buffer. */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBFZ - Load full compressed codebook. +* +* SYNOPSIS +* Error = Load_CBFZ(VQA, Iffsize) +* +* long Load_CBFZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Load the codebook into the end of the buffer. */ + lcwoffset = vqap->VQABuf->Max_CB_Size - padsize; + buffer = curcb->Buffer + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as compressed */ + curcb->Flags |= VQACBF_CBCOMP; + curcb->CBOffset = lcwoffset; + + /* Clock pointers to next CB Buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBP0 - Load partial uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBP0(VQA, Iffsize) +* +* long Load_CBP0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + + /*------------------------------------------------------------------------- + * ASSEMBLY PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = curcb->Buffer + loader->PartialCBSize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Accumulate the partial codebook values. */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as uncompressed */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBPZ - Load partial compressed codebook. +* +* SYNOPSIS +* Error = Load_CBPZ(VQA, Iffsize) +* +* long Load_CBPZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + + /* Dereference commonly used data members for quicker access */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Attempt to compute the LCW offset into the codebook buffer by + * multiplying the size of this chunk by the # frames/group, and adding + * a small fudge factor on, then subtracting that from the CB buffer size. + */ + if (loader->PartialCBSize == 0) { + curcb->CBOffset = (vqabuf->Max_CB_Size + - (padsize * vqap->Header.Groupsize + 100)); + } + + /*------------------------------------------------------------------------- + * ASSEMBLE PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = ((curcb->Buffer + curcb->CBOffset) + loader->PartialCBSize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Accumulate partial codebook values */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as compressed. */ + curcb->Flags |= VQACBF_CBCOMP; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPL0 - Load an uncompressed palette. +* +* SYNOPSIS +* Error = Load_CPL0(VQA, Iffsize) +* +* long Load_CPL0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the palette into the palette buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Palette, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag the palette as uncompressed. */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + curframe->PalOffset = 0; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPLZ - Load compressed palette. +* +* SYNOPSIS +* Error = Load_CPLZ(VQA, Iffsize) +* +* long Load_CPLZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + + /* Read the palette into the end of the palette buffer. */ + lcwoffset = vqap->VQABuf->Max_Pal_Size - padsize; + buffer = curframe->Palette + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this palette as compressed. */ + curframe->Flags |= VQAFRMF_PALCOMP; + curframe->PalOffset = lcwoffset; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPT0 - Load uncompressed pointers. +* +* SYNOPSIS +* Error = Load_VPT0(VQA, Iffsize) +* +* long Load_VPT0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the pointers into start of the pointer buffer. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Pointers, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag this frame as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + curframe->PtrOffset = 0; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPTZ - Load compressed pointers. +* +* SYNOPSIS +* Error = Load_VPTZ(VQA, Iffsize) +* +* long Load_VPTZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + lcwoffset = vqap->VQABuf->Max_Ptr_Size - padsize; + + /* Read the pointers into end of the pointer buffer. */ + buffer = curframe->Pointers + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as compressed. */ + curframe->Flags |= VQAFRMF_PTRCOMP; + curframe->PtrOffset = lcwoffset; + + return (0); +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* Load_SND0 - Load uncompressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND0(VQA, Iffsize) +* +* long Load_SND0(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned long padsize; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk. + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (audio->Buffer == NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read large startup chunk directly into AudioBuf */ + if ((padsize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + + audio->AudBufPos += iffsize; + + /* Flag the audio frame flags as loaded for the initial audio frame. */ + for (i = 0; i < (iffsize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Read data into TempBuf */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } + + /* Set the TempBufLen */ + audio->TempBufLen = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND1 - Load compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND1(VQA, Iffsize) +* +* long Load_SND1(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + ZAPHeader zap; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read the ZAP audio frame header. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &zap, + sizeof(ZAPHeader))) { + + return (VQAERR_READ); + } + + /* Adjust chunk size */ + padsize -= sizeof(ZAPHeader); + + /* Read large startup chunk directly into AudioBuf */ + if ((zap.UnCompSize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load RAW uncompressed data. */ + if (zap.UnCompSize == zap.CompSize) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, + padsize)) { + + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->Buffer, zap.UnCompSize); + } + + /* Set buffer positions & flags */ + audio->AudBufPos += zap.UnCompSize; + + for (i = 0; i < (zap.UnCompSize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + if (zap.UnCompSize == zap.CompSize) { + + /* If the frame is uncompressed the load it in directly. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load the audio frame into the end of the buffer. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->TempBuf, zap.UnCompSize); + } + + /* Set the TempBufLen */ + audio->TempBufLen = zap.UnCompSize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND2 - Load ADPCM compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND2(VQA, Iffsize) +* +* long Load_SND2(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + unsigned long uncomp_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + uncomp_size = iffsize * (audio->BitsPerSample / 4); + + /* Read large startup chunk directly into AudioBuf */ + if ((uncomp_size > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->Buffer; + sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set buffer positions & flags */ + audio->AudBufPos += uncomp_size; + + for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->TempBuf; + sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set the TempBufLen */ + audio->TempBufLen = uncomp_size; + + return (0); +} + + +#if(VQAVOC_ON) +/**************************************************************************** +* +* NAME +* Load_AudFrame - Loads blocks from seperate VOC file. +* +* SYNOPSIS +* Load_AudFrame(VQA) +* +* void Load_AudFrame(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Load_AudFrame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + static long lastplayblock = -1; + static long myblock = 0; + static long firsttime = 1; + long numblocks; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + + /* Do nothing if no buffer */ + if (audio->Buffer == NULL) { + return; + } + + /* If this is the first time we're called, pre-load the 1st 'n' audio + * blocks, where 'n' is half the total audio buffer size; this way, we'll + * always stay ahead of HMI. + */ + if (firsttime) { + numblocks = (audio->NumAudBlocks / 2); + read(vqap->vocfh, audio->Buffer, config->HMIBufSize * numblocks); + audio->AudBufPos += config->HMIBufSize * numblocks; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + for (i = 0; i < numblocks; i++) { + audio->IsLoaded[i] = 1; + } + + myblock += numblocks; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + + firsttime = 0; + } + + /* If HMI's block has changed, load the next block & mark it as loaded */ + if (audio->PlayPosition / config->HMIBufSize != lastplayblock) { + + /* update HMI's last known block position */ + lastplayblock = audio->PlayPosition / config->HMIBufSize; + + /* read the VOC data */ + read(vqap->vocfh, (audio->Buffer + myblock * config->HMIBufSize), + config->HMIBufSize); + + audio->AudBufPos += config->HMIBufSize; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + /* set the IsLoaded flags */ + audio->IsLoaded[myblock] = 1; + + /* increment my block counter */ + myblock++; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + } +} +#endif /* VQAVOC_ON */ +#endif /* VQAAUDIO_ON */ + diff --git a/VQ/VQA32/MAKEFILE b/VQ/VQA32/MAKEFILE new file mode 100644 index 0000000..cdd42a6 --- /dev/null +++ b/VQ/VQA32/MAKEFILE @@ -0,0 +1,163 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32wp +PRJDIR = $(%PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(%PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/VQ/VQA32/MAKEFILE.BAK b/VQ/VQA32/MAKEFILE.BAK new file mode 100644 index 0000000..2153d39 --- /dev/null +++ b/VQ/VQA32/MAKEFILE.BAK @@ -0,0 +1,167 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ +PRJ=.. +!endif + +!ifndef %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32wp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/VQ/VQA32/MAKEFILE.BOR b/VQ/VQA32/MAKEFILE.BOR new file mode 100644 index 0000000..df30026 --- /dev/null +++ b/VQ/VQA32/MAKEFILE.BOR @@ -0,0 +1,216 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay32 library. +# +# FILE +# makefile (Borland/TNT) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 7, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# BCDIR - Borland C installation path. +# TNTDIR - Pharlap/TNT installation path. +# +#**************************************************************************** + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32bp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = \ + config.obj \ + task.obj \ + loader.obj \ + drawer.obj \ + audio.obj \ + monodisp.obj \ + dstream.obj \ + unvqbuff.obj \ + unvqvesa.obj \ + vertag.obj \ + caption.obj \ +# unvqxmde.obj \ + +PRJLIBS = \ + vqm32bp.lib \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMAND AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).lib + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD LIBRARY +#---------------------------------------------------------------------------- + +$(PRJNAME).lib: $(OBJECTS) + - @del $(.path.lib)\$(PRJNAME).lib >&NUL + $(LIB_CMD) $(.path.lib)\$(PRJNAME).lib @$(LIB_CFG) @&&| +-+$(**: = -+) +| + - @copy vqaplay.h $(PRJ)\INCLUDE\VQA32 >&NUL + - @copy vqafile.h $(PRJ)\INCLUDE\VQA32 >&NUL + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + diff --git a/VQ/VQA32/MAKEFILE.WAT b/VQ/VQA32/MAKEFILE.WAT new file mode 100644 index 0000000..cdd42a6 --- /dev/null +++ b/VQ/VQA32/MAKEFILE.WAT @@ -0,0 +1,163 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32wp +PRJDIR = $(%PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(%PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/VQ/VQA32/MONODISP.BAK b/VQ/VQA32/MONODISP.BAK new file mode 100644 index 0000000..03f4a93 --- /dev/null +++ b/VQ/VQA32/MONODISP.BAK @@ -0,0 +1,508 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* monodisp.c +* +* DESCRIPTION +* Monochrome display (debug) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* Feburary 23, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitMono - Initialize the player's mono screen. +* VQA_UpdateMono - Update the player's mono output. +* +****************************************************************************/ + +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +#if(VQAMONO_ON) + +/* Main window */ +#define MAIN_WX1 0 +#define MAIN_WX2 79 +#define MAIN_WY1 0 +#define MAIN_WY2 9 +#define MAIN_TITLE "VQA Player" + +/* Loader data window */ +#define LOADER_WX1 0 +#define LOADER_WX2 39 +#define LOADER_WY1 10 +#define LOADER_WY2 20 +#define LOADER_TITLE " Frame Loader " + +/* Drawer data window */ +#define DRAWER_WX1 40 +#define DRAWER_WX2 79 +#define DRAWER_WY1 10 +#define DRAWER_WY2 20 +#define DRAWER_TITLE " Frame Drawer " + +/* Audio data window */ +#define AUDIO_WX1 0 +#define AUDIO_WX2 39 +#define AUDIO_WY1 21 +#define AUDIO_WY2 24 +#define AUDIO_TITLE " Audio Callback " + +/* Flipper data window */ +#define FLIPPER_WX1 40 +#define FLIPPER_WX2 79 +#define FLIPPER_WY1 21 +#define FLIPPER_WY2 24 +#define FLIPPER_TITLE " Frame Flipper " + +extern char *HMIDevName; + + +/**************************************************************************** +* +* NAME +* VQA_InitMono - Initialize the player's mono screen. +* +* SYNOPSIS +* VQA_InitMono(VQAData) +* +* void VQA_InitMono(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_InitMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + char txt[80]; + + /* Dereference commonly used data members of quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Enable and clear the mono screen */ + Mono_Enable(); + Mono_Clear_Screen(); + + /* Init main window */ + Mono_Draw_Rect(MAIN_WX1, MAIN_WY1, (MAIN_WX2 - MAIN_WX1 + 1), + (MAIN_WY2 - MAIN_WY1 + 1), 2, 1); + + Mono_Set_Cursor((MAIN_WX2 + MAIN_WX1 - strlen(MAIN_TITLE)) / 2, MAIN_WY1); + Mono_Print(MAIN_TITLE); + + /* Video mode */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 1); + Mono_Print("Video Mode: "); + + switch (config->Vmode) { + + #if(VQAMONO_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("MCGA Buffered"); + } else { + Mono_Print("MCGA Direct to screen"); + } + break; + #endif + + #if(VQAXMODE_ON) + case XMODE_320X200: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x200 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x200 VRAM Copy"); + } else { + Mono_Print("XMODE 320x200 Linear->Banked"); + } + } + break; + + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x240 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x240 VRAM Copy"); + } else { + Mono_Print("XMODE 320x240 Linear->Banked"); + } + } + break; + #endif + + #if(VQAVESA_ON) + case VESA_640X480_256: + Mono_Print("VESA 640x480"); + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("VESA 320x200 Buffered"); + } else { + Mono_Print("VESA 320x200 Direct to screen"); + } + break; + #endif + + default: + Mono_Print("UNKNOWN"); + break; + } + + /* Sound status */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 2); + Mono_Print(" Sound: "); + + if (config->OptionFlags & VQAOPTF_AUDIO) { + sprintf(txt,"%u Hz", config->AudioRate); + Mono_Print(txt); + } else { + Mono_Print("OFF"); + } + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 3); + Mono_Print(" Driver Name: "); + Mono_Print(HMIDevName); + + /* Frame rates */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 4); + sprintf(txt," Load Frame Rate: %d", config->FrameRate); + Mono_Print(txt); + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 5); + sprintf(txt," Draw Frame Rate: %d", config->DrawRate); + Mono_Print(txt); + + /* Slow palette */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 6); + Mono_Print(" Slow palette: "); + + if (config->OptionFlags & VQAOPTF_SLOWPAL) { + Mono_Print("ON"); + } else { + Mono_Print("OFF"); + } + + /* Memory Usage */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 7); + sprintf(txt," Memory Used: %ld", vqabuf->MemUsed); + Mono_Print(txt); + + /* Timer Method */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 8); + + if (VQA_TimerMethod() == VQA_TMETHOD_DOS) { + Mono_Print(" DOS Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_INT) { + Mono_Print(" Interrupt Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_AUDIO) { + Mono_Print(" Audio DMA Timer:"); + } else { + Mono_Print(" Defualt:"); + } + + /* Init loader data window */ + Mono_Draw_Rect(LOADER_WX1, LOADER_WY1, (LOADER_WX2 - LOADER_WX1 + 1), + (LOADER_WY2 - LOADER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((LOADER_WX2 + LOADER_WX1 - strlen(LOADER_TITLE)) / 2, + LOADER_WY1); + + Mono_Print(LOADER_TITLE); + + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 2); + Mono_Print("# Waits on Drawer:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 3); + Mono_Print(" # Waits on Audio:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 4); + Mono_Print(" Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 5); + Mono_Print(" Max Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY2 - 2); + Mono_Print("Audio:"); + + /* Init drawer data window */ + Mono_Draw_Rect(DRAWER_WX1, DRAWER_WY1, (DRAWER_WX2 - DRAWER_WX1 + 1), + (DRAWER_WY2 - DRAWER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((DRAWER_WX2 + DRAWER_WX1 - strlen(DRAWER_TITLE)) / 2, + DRAWER_WY1); + + Mono_Print(DRAWER_TITLE); + + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 2); + Mono_Print(" Desired Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 3); + Mono_Print("# Waits on Flipper:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 4); + Mono_Print(" # Waits on Loader:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 5); + Mono_Print(" # Frames Skipped:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 6); + Mono_Print(" VQ Block Size:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY2 - 2); + Mono_Print("Frames: Cbooks:"); + + /* Init audio data window */ + Mono_Draw_Rect(AUDIO_WX1, AUDIO_WY1, (AUDIO_WX2 - AUDIO_WX1 + 1), + (AUDIO_WY2 - AUDIO_WY1 + 1), 2, 1); + + Mono_Set_Cursor((AUDIO_WX2 + AUDIO_WX1 - strlen(AUDIO_TITLE)) / 2, + AUDIO_WY1); + + Mono_Print(AUDIO_TITLE); + + Mono_Set_Cursor(AUDIO_WX1 + 2, AUDIO_WY1 + 1); + Mono_Print("# Repeated Buffers:"); + + /* Init flipper data window */ + Mono_Draw_Rect(FLIPPER_WX1, FLIPPER_WY1, (FLIPPER_WX2 - FLIPPER_WX1 + 1), + (FLIPPER_WY2 - FLIPPER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((FLIPPER_WX2 + FLIPPER_WX1 - strlen(FLIPPER_TITLE)) / 2, + FLIPPER_WY1); + + Mono_Print(FLIPPER_TITLE); + + Mono_Set_Cursor(FLIPPER_WX1 + 2, FLIPPER_WY1 + 1); + Mono_Print("Current Frame #:"); +} + + +/**************************************************************************** +* +* NAME +* VQA_UpdateMono - Update the player's mono output. +* +* SYNOPSIS +* VQA_UpdateMono(VQAData) +* +* void VQA_UpdateMono(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_UpdateMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAFrameNode *frame; + VQACBNode *cbook; + long frameindex = -1; + long loadcb = -1; + long drawcb = -1; + long i; + char txt[80]; + unsigned long curtime; + + /* Dereference commonly used data members for quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Timer value */ + curtime = VQA_GetTime(); + Mono_Set_Cursor(MAIN_WX1 + 40, MAIN_WY1 + 8); + sprintf(txt,"%02ld:%02ld.%02ld",curtime / (VQA_TIMETICKS * VQA_TIMETICKS), + curtime / VQA_TIMETICKS,((curtime * 100L) / VQA_TIMETICKS) + -((curtime / VQA_TIMETICKS) * 100L)); + + Mono_Print(txt); + + /* Loader data */ + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 1); + sprintf(txt,"%4d",vqabuf->Loader.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 2); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnDrawer); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 3); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnAudio); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 4); + sprintf(txt,"%5u",vqabuf->Loader.FrameSize); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 5); + sprintf(txt,"%5u",vqabuf->Loader.MaxFrameSize); + Mono_Print(txt); + + /* Draw a picture of the audio buffers */ + for (i = 0; i < vqabuf->Audio.NumAudBlocks; i++) { + if (vqabuf->Audio.IsLoaded[i] == 0) { + txt[i] = '_'; + } else { + txt[i] = 'X'; + } + } + + txt[i] = 0; + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2-1); + Mono_Print(" "); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.PlayPosition + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("P"); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Loader.AudBufPos + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("L"); + + /* Drawer data */ + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Drawer.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 2); + sprintf(txt,"%4d", vqabuf->Drawer.DesiredFrame); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 3); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnFlipper); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 4); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnLoader); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 5); + sprintf(txt,"%4d", vqabuf->Drawer.NumSkipped); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 6); + sprintf(txt," %dx%d", vqap->Header.BlockWidth, vqap->Header.BlockHeight); + Mono_Print(txt); + + /* Draw a picture of the frame buffers */ + frame = vqabuf->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame->Flags & VQAFRMF_LOADED) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + + if (vqabuf->Flipper.CurFrame == frame) { + frameindex = i; + } + + frame = frame->Next; + } + + txt[i] = 0; + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 10 + frameindex,DRAWER_WY2 - 1); + Mono_Print("^"); + + /* Draw a picture of the codebook buffers */ + cbook = vqabuf->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (vqabuf->Loader.CurCB == cbook) { + loadcb = i; + } + + if (vqabuf->Flipper.CurFrame->Codebook == cbook) { + drawcb = i; + } + + cbook = cbook->Next; + } + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 2); + Mono_Print("___"); + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + loadcb,DRAWER_WY2 - 1); + Mono_Print("L"); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + drawcb,DRAWER_WY2 - 1); + Mono_Print("D"); + + /* Audio data */ + Mono_Set_Cursor(AUDIO_WX1 + 22, AUDIO_WY1 + 1); + sprintf(txt,"%4ld", vqabuf->Audio.NumSkipped); + Mono_Print(txt); + + /* Flipper data */ + Mono_Set_Cursor(FLIPPER_WX1 + 22,FLIPPER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Flipper.LastFrameNum); + Mono_Print(txt); + Mono_Set_Cursor(0,0); +} + +#endif /* VQAMONO_ON */ + diff --git a/VQ/VQA32/MONODISP.CPP b/VQ/VQA32/MONODISP.CPP new file mode 100644 index 0000000..a69c1e7 --- /dev/null +++ b/VQ/VQA32/MONODISP.CPP @@ -0,0 +1,512 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* monodisp.c +* +* DESCRIPTION +* Monochrome display (debug) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 6, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitMono - Initialize the player's mono screen. +* VQA_UpdateMono - Update the player's mono output. +* +****************************************************************************/ + +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +#if(VQAMONO_ON) + +/* Main window */ +#define MAIN_WX1 0 +#define MAIN_WX2 79 +#define MAIN_WY1 0 +#define MAIN_WY2 9 +#define MAIN_TITLE "VQA Player" + +/* Loader data window */ +#define LOADER_WX1 0 +#define LOADER_WX2 39 +#define LOADER_WY1 10 +#define LOADER_WY2 20 +#define LOADER_TITLE " Frame Loader " + +/* Drawer data window */ +#define DRAWER_WX1 40 +#define DRAWER_WX2 79 +#define DRAWER_WY1 10 +#define DRAWER_WY2 20 +#define DRAWER_TITLE " Frame Drawer " + +/* Audio data window */ +#define AUDIO_WX1 0 +#define AUDIO_WX2 39 +#define AUDIO_WY1 21 +#define AUDIO_WY2 24 +#define AUDIO_TITLE " Audio Callback " + +/* Flipper data window */ +#define FLIPPER_WX1 40 +#define FLIPPER_WX2 79 +#define FLIPPER_WY1 21 +#define FLIPPER_WY2 24 +#define FLIPPER_TITLE " Frame Flipper " + +extern char *HMIDevName; + + +/**************************************************************************** +* +* NAME +* VQA_InitMono - Initialize the player's mono screen. +* +* SYNOPSIS +* VQA_InitMono(VQA) +* +* void VQA_InitMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_InitMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + char txt[80]; + + /* Dereference commonly used data members of quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Enable and clear the mono screen */ + Mono_Enable(); + Mono_Clear_Screen(); + + /* Init main window */ + Mono_Draw_Rect(MAIN_WX1, MAIN_WY1, (MAIN_WX2 - MAIN_WX1 + 1), + (MAIN_WY2 - MAIN_WY1 + 1), 2, 1); + + Mono_Set_Cursor((MAIN_WX2 + MAIN_WX1 - strlen(MAIN_TITLE)) / 2, MAIN_WY1); + Mono_Print(MAIN_TITLE); + + /* Video mode */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 1); + Mono_Print("Video Mode: "); + + switch (config->Vmode) { + + #if(VQAMONO_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("MCGA Buffered"); + } else { + Mono_Print("MCGA Direct to screen"); + } + break; + #endif + + #if(VQAXMODE_ON) + case XMODE_320X200: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x200 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x200 VRAM Copy"); + } else { + Mono_Print("XMODE 320x200 Linear->Banked"); + } + } + break; + + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x240 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x240 VRAM Copy"); + } else { + Mono_Print("XMODE 320x240 Linear->Banked"); + } + } + break; + #endif + + #if(VQAVESA_ON) + case VESA_640X480_256: + Mono_Print("VESA 640x480"); + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("VESA 320x200 Buffered"); + } else { + Mono_Print("VESA 320x200 Direct to screen"); + } + break; + #endif + + default: + Mono_Print("UNKNOWN"); + break; + } + + /* Sound status */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 2); + Mono_Print(" Sound: "); + + if (config->OptionFlags & VQAOPTF_AUDIO) { + sprintf(txt,"%u Hz", config->AudioRate); + Mono_Print(txt); + } else { + Mono_Print("OFF"); + } + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 3); + Mono_Print(" Driver Name: "); + Mono_Print(HMIDevName); + + /* Frame rates */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 4); + sprintf(txt," Load Frame Rate: %d", config->FrameRate); + Mono_Print(txt); + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 5); + sprintf(txt," Draw Frame Rate: %d", config->DrawRate); + Mono_Print(txt); + + /* Slow palette */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 6); + Mono_Print(" Slow palette: "); + + if (config->OptionFlags & VQAOPTF_SLOWPAL) { + Mono_Print("ON"); + } else { + Mono_Print("OFF"); + } + + /* Memory Usage */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 7); + sprintf(txt," Memory Used: %ld", vqabuf->MemUsed); + Mono_Print(txt); + + /* Timer Method */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 8); + + if (VQA_TimerMethod() == VQA_TMETHOD_DOS) { + Mono_Print(" DOS Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_INT) { + Mono_Print(" Interrupt Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_AUDIO) { + Mono_Print(" Audio DMA Timer:"); + } else { + Mono_Print(" Defualt:"); + } + + /* Init loader data window */ + Mono_Draw_Rect(LOADER_WX1, LOADER_WY1, (LOADER_WX2 - LOADER_WX1 + 1), + (LOADER_WY2 - LOADER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((LOADER_WX2 + LOADER_WX1 - strlen(LOADER_TITLE)) / 2, + LOADER_WY1); + + Mono_Print(LOADER_TITLE); + + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 2); + Mono_Print("# Waits on Drawer:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 3); + Mono_Print(" # Waits on Audio:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 4); + Mono_Print(" Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 5); + Mono_Print(" Max Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY2 - 2); + Mono_Print("Audio:"); + + /* Init drawer data window */ + Mono_Draw_Rect(DRAWER_WX1, DRAWER_WY1, (DRAWER_WX2 - DRAWER_WX1 + 1), + (DRAWER_WY2 - DRAWER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((DRAWER_WX2 + DRAWER_WX1 - strlen(DRAWER_TITLE)) / 2, + DRAWER_WY1); + + Mono_Print(DRAWER_TITLE); + + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 2); + Mono_Print(" Desired Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 3); + Mono_Print("# Waits on Flipper:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 4); + Mono_Print(" # Waits on Loader:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 5); + Mono_Print(" # Frames Skipped:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 6); + Mono_Print(" VQ Block Size:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY2 - 2); + Mono_Print("Frames: Cbooks:"); + + /* Init audio data window */ + Mono_Draw_Rect(AUDIO_WX1, AUDIO_WY1, (AUDIO_WX2 - AUDIO_WX1 + 1), + (AUDIO_WY2 - AUDIO_WY1 + 1), 2, 1); + + Mono_Set_Cursor((AUDIO_WX2 + AUDIO_WX1 - strlen(AUDIO_TITLE)) / 2, + AUDIO_WY1); + + Mono_Print(AUDIO_TITLE); + + Mono_Set_Cursor(AUDIO_WX1 + 2, AUDIO_WY1 + 1); + Mono_Print("# Repeated Buffers:"); + + /* Init flipper data window */ + Mono_Draw_Rect(FLIPPER_WX1, FLIPPER_WY1, (FLIPPER_WX2 - FLIPPER_WX1 + 1), + (FLIPPER_WY2 - FLIPPER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((FLIPPER_WX2 + FLIPPER_WX1 - strlen(FLIPPER_TITLE)) / 2, + FLIPPER_WY1); + + Mono_Print(FLIPPER_TITLE); + + Mono_Set_Cursor(FLIPPER_WX1 + 2, FLIPPER_WY1 + 1); + Mono_Print("Current Frame #:"); +} + + +/**************************************************************************** +* +* NAME +* VQA_UpdateMono - Update the player's mono output. +* +* SYNOPSIS +* VQA_UpdateMono(VQA) +* +* void VQA_UpdateMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_UpdateMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAFrameNode *frame; + VQACBNode *cbook; + long frameindex = -1; + long loadcb = -1; + long drawcb = -1; + long i; + unsigned long curtime; + char txt[80]; + + /* Dereference commonly used data members for quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Timer value */ + curtime = VQA_GetTime(vqap); + Mono_Set_Cursor(MAIN_WX1 + 40, MAIN_WY1 + 8); + sprintf(txt,"%02ld:%02ld.%02ld",curtime / (VQA_TIMETICKS * VQA_TIMETICKS), + curtime / VQA_TIMETICKS,((curtime * 100L) / VQA_TIMETICKS) + -((curtime / VQA_TIMETICKS) * 100L)); + + Mono_Print(txt); + + /* Loader data */ + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 1); + sprintf(txt,"%4d",vqabuf->Loader.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 2); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnDrawer); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 3); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnAudio); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 4); + sprintf(txt,"%5u",vqabuf->Loader.FrameSize); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 5); + sprintf(txt,"%5u",vqabuf->Loader.MaxFrameSize); + Mono_Print(txt); + + #if(VQAAUDIO_ON) + /* Draw a picture of the audio buffers */ + for (i = 0; i < vqabuf->Audio.NumAudBlocks; i++) { + if (vqabuf->Audio.IsLoaded[i] == 0) { + txt[i] = '_'; + } else { + txt[i] = 'X'; + } + } + + txt[i] = 0; + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2-1); + Mono_Print(" "); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.PlayPosition + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("P"); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.AudBufPos + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("L"); + #endif /* VQAAUDIO_ON */ + + /* Drawer data */ + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Drawer.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 2); + sprintf(txt,"%4d", vqabuf->Drawer.DesiredFrame); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 3); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnFlipper); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 4); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnLoader); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 5); + sprintf(txt,"%4d", vqabuf->Drawer.NumSkipped); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 6); + sprintf(txt," %dx%d", vqap->Header.BlockWidth, vqap->Header.BlockHeight); + Mono_Print(txt); + + /* Draw a picture of the frame buffers */ + frame = vqabuf->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame->Flags & VQAFRMF_LOADED) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + + if (vqabuf->Flipper.CurFrame == frame) { + frameindex = i; + } + + frame = frame->Next; + } + + txt[i] = 0; + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 10 + frameindex,DRAWER_WY2 - 1); + Mono_Print("^"); + + /* Draw a picture of the codebook buffers */ + cbook = vqabuf->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (vqabuf->Loader.CurCB == cbook) { + loadcb = i; + } + + if (vqabuf->Flipper.CurFrame->Codebook == cbook) { + drawcb = i; + } + + cbook = cbook->Next; + } + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 2); + Mono_Print("___"); + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + loadcb,DRAWER_WY2 - 1); + Mono_Print("L"); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + drawcb,DRAWER_WY2 - 1); + Mono_Print("D"); + + /* Audio data */ + #if(VQAAUDIO_ON) + Mono_Set_Cursor(AUDIO_WX1 + 22, AUDIO_WY1 + 1); + sprintf(txt,"%4ld", vqabuf->Audio.NumSkipped); + Mono_Print(txt); + #endif + + /* Flipper data */ + Mono_Set_Cursor(FLIPPER_WX1 + 22,FLIPPER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Flipper.LastFrameNum); + Mono_Print(txt); + Mono_Set_Cursor(0,0); +} + +#endif /* VQAMONO_ON */ + diff --git a/VQ/VQA32/SOS.H b/VQ/VQA32/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/VQ/VQA32/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/VQ/VQA32/SOSDATA.H b/VQ/VQA32/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/VQ/VQA32/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/VQ/VQA32/SOSDEFS.H b/VQ/VQA32/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/VQ/VQA32/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/VQ/VQA32/SOSFNCT.H b/VQ/VQA32/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/VQ/VQA32/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/VQ/VQA32/SOSRES.H b/VQ/VQA32/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/VQ/VQA32/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/VQ/VQA32/TASK.CPP b/VQ/VQA32/TASK.CPP new file mode 100644 index 0000000..ba6ccbd --- /dev/null +++ b/VQ/VQA32/TASK.CPP @@ -0,0 +1,558 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* task.c +* +* DESCRIPTION +* Loading and drawing delegation +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* July 25, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Alloc - Allocate a VQAHandle to use. +* VQA_Free - Free a VQAHandle. +* VQA_Init - Initialize the VQAHandle IO. +* VQA_Play - Play the VQA movie. +* VQA_GetInfo - Get VQA movie information. +* VQA_GetStats - Get VQA movie statistics. +* VQA_Version - Get VQA library version number. +* VQA_IDString - Get the VQA player library's ID string. +* +* PRIVATE +* VQA_IO_Task - Loader task for multitasking. +* VQA_Rendering_Task - Drawer task for multitasking. +* User_Update - Page flip routine called by the task interrupt. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "vqaplayp.h" + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Externals */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int cdecl Check_Key(void); +extern int cdecl Get_Key(void); + +#ifdef __cplusplus +} +#endif + + +/**************************************************************************** +* +* NAME +* VQA_Alloc - Allocate a VQAHandle to use. +* +* SYNOPSIS +* VQAHandle = VQA_Alloc() +* +* VQAHandle *VQA_Alloc(void); +* +* FUNCTION +* Obtain a VQAHandle. This handle is used by most VQA library functions, +* and contains the current position in the file. This is the only legal +* way to obtain a VQAHandle. +* +* INPUTS +* NONE +* +* RESULT +* VQA - Handle of a VQA. +* +****************************************************************************/ + +VQAHandle *VQA_Alloc(void) +{ + VQAHandleP *vqa; + + if ((vqa = (VQAHandleP *)malloc(sizeof(VQAHandleP))) != NULL) { + memset(vqa, 0, sizeof(VQAHandleP)); + } + + return ((VQAHandle *)vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Free - Free a VQAHandle. +* +* SYNOPSIS +* VQA_Free(VQA) +* +* void VQA_Free(VQAHandle *); +* +* FUNCTION +* Dispose of a VQAHandle. This is the only legal way to dispose of a +* VQAHandle. +* +* INPUTS +* VQA - Pointer to VQAHandle to dispose of. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Free(VQAHandle *vqa) +{ + if (vqa) free(vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Init - Initialize the VQAHandle IO handler. +* +* SYNOPSIS +* VQA_Init(VQA, IOHandler) +* +* void VQA_Init(VQAHandle *, IOHandler *); +* +* FUNCTION +* Initialize the specified VQAHandle IO with the client provided custom +* IO handler. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize. +* IOHandler - Pointer to custom file I/O handler function. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Init(VQAHandle *vqa, long(*iohandler)(VQAHandle *vqa, long action, + void *buffer, long nbytes)) +{ + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_Play - Play the VQA movie. +* +* SYNOPSIS +* Error = VQA_Play(VQA, Mode) +* +* long VQA_Play(VQAHandle *, long); +* +* FUNCTION +* Playback the movie associated with the specified VQAHandle. +* +* INPUTS +* VQA - Pointer to handle of movie to play. +* Mode - Playback mode. +* VQAMODE_RUN - Run the movie until completion. +* VQAMODE_WALK - Walk the movie frame by frame. +* VQAMODE_PAUSE - Pause the movie. +* VQAMODE_STOP - Stop the movie (Shutdown). +* +* RESULT +* Error - 0 if successful, or error code. +* +****************************************************************************/ + +long VQA_Play(VQAHandle *vqa, long mode) +{ + VQAData *vqabuf; + VQAConfig *config; + VQADrawer *drawer; + long rc; + long i; + long key; + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* One time player priming. */ + if (!(vqabuf->Flags & VQADATF_PRIMED)) { + + /* Init the Drawer's configuration */ + VQA_Configure_Drawer((VQAHandleP *)vqa); + + /* If audio enabled & loaded, start playing */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) && vqabuf->Audio.IsLoaded[0]) { + VQA_StartAudio((VQAHandleP *)vqa); + } + #endif + + /* Initialize the timer */ + i = ((vqabuf->Drawer.CurFrame->FrameNum * VQA_TIMETICKS) + / config->DrawRate); + + VQA_SetTimer((VQAHandleP *)vqa, i, config->TimerMethod); + vqabuf->StartTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Set up the Mono screen */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_InitMono((VQAHandleP *)vqa); + } + #endif + + /* Priming is complete. */ + vqabuf->Flags |= VQADATF_PRIMED; + } + + /* Main Player Loop */ + switch (mode) { + case VQAMODE_PAUSE: + if ((vqabuf->Flags & VQADATF_PAUSED) == 0) { + vqabuf->Flags |= VQADATF_PAUSED; + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Stop the audio while the movie is paused. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + } + + rc = VQAERR_PAUSED; + break; + + case VQAMODE_RUN: + case VQAMODE_WALK: + default: + + /* Start up the movie if is it currently paused. */ + if (vqabuf->Flags & VQADATF_PAUSED) { + vqabuf->Flags &= ~VQADATF_PAUSED; + + /* Start the audio if it was previously on. */ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + VQA_StartAudio((VQAHandleP *)vqa); + } + #endif + + VQA_SetTimer((VQAHandleP *)vqa, vqabuf->EndTime, config->TimerMethod); + } + + /* Load, Draw, Load, Draw, Load, Draw ... */ + while ((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + != (VQADATF_DDONE|VQADATF_LDONE)) { + + /* Load a frame */ + if (!(vqabuf->Flags & VQADATF_LDONE)) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + vqabuf->Flags |= VQADATF_LDONE; + rc = 0; + } + } + + /* Draw a frame */ + if ((config->DrawFlags & VQACFGF_NODRAW) == 0) { + if ((rc = (*(vqabuf->Draw_Frame))(vqa)) == 0) { + vqabuf->DrawnFrames++; + + if (User_Update(vqa)) { + vqabuf->Flags |= (VQADATF_DDONE|VQADATF_LDONE); + } + } + else if ((vqabuf->Flags & VQADATF_LDONE) + && (rc == VQAERR_NOBUFFER)) { + vqabuf->Flags |= VQADATF_DDONE; + } + } else { + vqabuf->Flags |= VQADATF_DDONE; + drawer->CurFrame->Flags = 0L; + drawer->CurFrame = drawer->CurFrame->Next; + } + + /* Update Mono output */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_UpdateMono((VQAHandleP *)vqa); + } + #endif + + if (mode == VQAMODE_WALK) { + break; + } + #if(VQASTANDALONE) + else { + + /* Do single-stepping check. */ + if (config->OptionFlags & VQAOPTF_STEP) { + while ((key = Check_Key()) == 0); + Get_Key(); + + /* Escape key still quits. */ + if (key == 27) { + break; + } + } + + /* Check for ESC */ + if ((key = Check_Key()) != 0) { + mode = VQAMODE_STOP; + break; + } + } + #endif + } + break; + } + + /* If the movie is finished or we are requested to stop then shutdown. */ + if (((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + == (VQADATF_DDONE|VQADATF_LDONE)) || (mode == VQAMODE_STOP)) { + + /* Record the end time; must be done before stopping audio, since we're + * getting the elapsed time from the audio DMA position. + */ + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Stop audio, if it's playing. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + + /* Movie is finished. */ + rc = VQAERR_EOF; + } + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetInfo - Get VQA movie information. +* +* SYNOPSIS +* VQA_GetInfo(VQA, Info) +* +* void VQA_GetInfo(VQAHandle *, VQAInfo *); +* +* FUNCTION +* Retrieve information about the opened movie. +* +* INPUTS +* VQA - Pointer to VQAHandle of opened movie. +* Info - Pointer to VQAInfo structure to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetInfo(VQAHandle *vqa, VQAInfo *info) +{ + VQAHeader *header; + + /* Dereference header structure. */ + header = &((VQAHandleP *)vqa)->Header; + + info->NumFrames = header->Frames; + info->ImageHeight = header->ImageHeight; + info->ImageWidth = header->ImageWidth; + info->ImageBuf = ((VQAHandleP *)vqa)->VQABuf->Drawer.ImageBuf; +} + + +/**************************************************************************** +* +* NAME +* VQA_GetStats - Get VQA movie statistics. +* +* SYNOPSIS +* VQA_GetStats(VQA, Stats) +* +* void VQA_GetStats(VQAHandle *, VQAStatistics *); +* +* FUNCTION +* Retrieve the statistics for the VQA movie. +* +* INPUTS +* VQA - Handle of VQA movie to get statistics for. +* Stats - Pointer to VQAStatistics to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetStats(VQAHandle *vqa, VQAStatistics *stats) +{ + VQAData *vqabuf; + + /* Dereference VQAData structure from VQAHandle */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + stats->MemUsed = vqabuf->MemUsed; + stats->StartTime = vqabuf->StartTime; + stats->EndTime = vqabuf->EndTime; + stats->FramesLoaded = vqabuf->LoadedFrames; + stats->FramesDrawn = vqabuf->DrawnFrames; + stats->FramesSkipped = vqabuf->Drawer.NumSkipped; + stats->MaxFrameSize = vqabuf->Loader.MaxFrameSize; + + #if(VQAAUDIO_ON) + stats->SamplesPlayed = vqabuf->Audio.SamplesPlayed; + #else + stats->SamplesPlayed = 0; + #endif +} + + +/**************************************************************************** +* +* NAME +* VQA_Version - Get VQA library version number. +* +* SYNOPSIS +* Version = VQA_Version() +* +* char *VQA_Version(void); +* +* FUNCTION +* Return the version of the VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* Version - Pointer to version number string. +* +****************************************************************************/ + +char *VQA_Version(void) +{ + return(VQA_VERSION); +} + + +/**************************************************************************** +* +* NAME +* VQA_IDString - Get the VQA player library's ID string. +* +* SYNOPSIS +* IDString = VQA_IDString() +* +* char *VQA_IDString(void); +* +* FUNCTION +* Return the ID string of this VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* IDString - Pointer to ID string. +* +****************************************************************************/ + +char *VQA_IDString(void) +{ + return (VQA_IDSTRING); +} + + +/**************************************************************************** +* +* NAME +* User_Update - Page flip routine called by the task interrupt. +* +* SYNOPSIS +* User_Update(VQA) +* +* long User_Update(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Handle of VQA movie. +* +* RESULT +* NONE +* +****************************************************************************/ + +long User_Update(VQAHandle *vqa) +{ + VQAData *vqabuf; + long rc = 0; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + if (vqabuf->Flags & VQADATF_UPDATE) { + + /* Invoke the page flip routine */ + rc = (*(vqabuf->Page_Flip))(vqa); + + /* Update data for mono output */ + vqabuf->Flipper.LastFrameNum = vqabuf->Flipper.CurFrame->FrameNum; + + /* Mark the frame as loadable */ + vqabuf->Flipper.CurFrame->Flags = 0L; + vqabuf->Flags &= (~VQADATF_UPDATE); + } + + return (rc); +} + diff --git a/VQ/VQA32/TASM32.CFG b/VQ/VQA32/TASM32.CFG new file mode 100644 index 0000000..da37b1d --- /dev/null +++ b/VQ/VQA32/TASM32.CFG @@ -0,0 +1,10 @@ +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 diff --git a/VQ/VQA32/TLIB.CFG b/VQ/VQA32/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/VQ/VQA32/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/VQ/VQA32/TLINK32.CFG b/VQ/VQA32/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/VQ/VQA32/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/VQ/VQA32/UNVQ.H b/VQ/VQA32/UNVQ.H new file mode 100644 index 0000000..9f94fab --- /dev/null +++ b/VQ/VQA32/UNVQ.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQAUNVQ_H +#define VQAUNVQ_H +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* unvq.h +* +* DESCRIPTION +* VQ frame decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +#ifdef PHARLAP_TNT +#include +#endif + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* unvqbuff.asm */ +#ifndef PHARLAP_TNT +void cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x2_Woofer(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +/* unvqvesa.asm */ +void cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, unsigned char *palette, + unsigned long grains_per_win,unsigned long dummy1,unsigned long dummy2); + +#else /* PHARLAP_TNT */ + +void cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +/* unvqvesa.asm */ +void cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, FARPTR palette, unsigned long grains_per_win, + unsigned long dummy1, unsigned long dummy2); + +#endif /* PHARLAP_TNT */ + +/* unvqxmde.asm */ +void cdecl UnVQ_4x2_Xmode(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void cdecl UnVQ_4x2_XmodeCB(unsigned char *cbdummy, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void cdecl Upload_4x2CB(unsigned char *codebook, unsigned long numentries); +void cdecl XlatePointers(unsigned char *pointers, unsigned long numpointers); + +#ifdef __cplusplus +} +#endif + +#endif /* VQAUNVQ_H */ diff --git a/VQ/VQA32/UNVQBUFF.ASM b/VQ/VQA32/UNVQBUFF.ASM new file mode 100644 index 0000000..d908abd --- /dev/null +++ b/VQ/VQA32/UNVQBUFF.ASM @@ -0,0 +1,1152 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQBuff.asm +;* +;* DESCRIPTION +;* Buffered VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* Feburary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + + IF VQABLOCK_2X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x2(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x2:NEAR + PROC UnVQ_2x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x2 + ENDIF ;VQABLOCK_2X2 + + + IF VQABLOCK_2X3 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x3(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x3(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x3:NEAR + PROC UnVQ_2x3 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + add eax,[bufwidth] + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov dx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],dx + ELSE + mov [edi+ebx],dx + ENDIF + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2rd row to dest + ELSE + mov [edi+ebx],ax ;Write 2rd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x3 + ENDIF ;VQABLOCK_2X3 + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x2(unsigned char *, unsigned char *, unsigned char *, +;* long, long, long); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;;; GLOBAL C UnVQ_4x2:NEAR +;;; PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi +;;; +;;; ARG codebook:NEAR PTR +;;; ARG pointers:NEAR PTR +;;; +;;; IF PHARLAP_TNT +;;; ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; ARG buffer:NEAR PTR +;;; ENDIF +;;; +;;; ARG blocksperrow:DWORD +;;; ARG numrows:DWORD +;;; ARG bufwidth:DWORD +;;; +;;; LOCAL data_end:DWORD +;;; LOCAL cb_offset:DWORD +;;; LOCAL edi_startval:DWORD +;;; LOCAL rowoffset:DWORD +;;; +;;;;---------------------------------------------------------------------------- +;;;; Initialize +;;;;---------------------------------------------------------------------------- +;;; +;;; mov eax,[codebook] ;Adjust the codebook address so +;;; sub eax,4 ; that the pointer offsets will +;;; mov [cb_offset],eax ; point directly at the codeword. +;;; +;;; mov eax,[bufwidth] ;Compute the offset to the next +;;; shl eax,1 ; row of blocks. +;;; mov [rowoffset],eax +;;; +;;; mov esi,[pointers] +;;; mov eax,[numrows] ;Compute the end address of the +;;; mul [blocksperrow] ; pointer data. +;;; shl eax,1 +;;; add eax,esi +;;; mov [data_end],eax +;;; +;;; IF PHARLAP_TNT +;;; push es +;;; les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; mov edi,[buffer] +;;; ENDIF +;;; +;;; mov [edi_startval],edi +;;; +;;;;---------------------------------------------------------------------------- +;;;; Drawing loop +;;;;---------------------------------------------------------------------------- +;;; +;;;??Start_row: +;;; mov ecx,[blocksperrow] ;Number of blocks in a line +;;; +;;;??Not_finished_a_line: +;;; sub ebx,ebx +;;; mov bx,[WORD PTR esi] ;Get the codebook pointer value +;;; add esi,2 ; then advance to the next one. +;;; +;;; or bx,bx ;Is it a one color block? +;;; js short ??One_color +;;; +;;;; Draw multi-color block +;;; +;;; add ebx,[cb_offset] ;Codeword address +;;; mov eax,[ebx] ;Read 1st row of codeword +;;; mov edx,[ebx+4] ;Read 2nd row of codeword +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],edx ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],edx ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;; Draw 1-color block +;;; +;;;??One_color: +;;; cmp bx,SKIP_PTR ;Is this a skip block? +;;; jne ??Draw_One_Color +;;; +;;; add edi,4 ;Move to next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; jmp ??Next_row +;;; +;;;??Draw_One_Color: +;;; not bx ;NOT pointer value to get color +;;; mov bh,bl ;Duplicate color through the +;;; mov ax,bx ; entire dword register. +;;; rol eax,16 +;;; mov ax,bx +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],eax ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],eax ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block positionw +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;;??Next_row: +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;??End_of_data: +;;; IF PHARLAP_TNT +;;; pop es +;;; ENDIF +;;; +;;; ret +;;; +;;; ENDP UnVQ_4x2 +;;; ENDIF ;VQABLOCK_4X2 + + GLOBAL C UnVQ_4x2:NEAR + PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + ARG buffer:NEAR PTR + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so +; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax + add eax,esi + mov [data_end],eax + + mov edi,[buffer] + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx + mov bl,[esi] + mov bh,[esi + eax] ;Get the codebook pointer value + inc esi ; then advance to the next one. + + cmp bh,00Fh ;Is it a one color block? + je short ??One_color + +; Draw multi-color block + + shl ebx,3 + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],edx ;Write 2nd row to dest + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: +; cmp bx,SKIP_PTR ;Is this a skip block? +; jne ??Draw_One_Color +; +; add edi,4 ;Move to next dest block position +; dec ecx ;More blocks for this row? +; jnz short ??Not_finished_a_line +; jmp ??Next_row + +??Draw_One_Color: +; not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + ret + + ENDP UnVQ_4x2 + ENDIF ;VQABLOCK_4X2 + + + IF VQABLOCK_4X4 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x4(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x4(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x4:NEAR + PROC UnVQ_4x4 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,2 ; row of blocks + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + ENDIF + + mov eax,ebx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + mov ebx,eax + mov eax,[ebx+8] + mov edx,[ebx+12] + mov ebx,[bufwidth] + shl ebx,1 + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 3rd row to dest + ELSE + mov [edi+ebx],eax ;Write 3rd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 4th row to dest + ELSE + mov [edi+ebx],eax ;Write 4th row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x4 + ENDIF ;VQABLOCK_4X4 + + + IF VQABLOCK_WOOFER + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_Woofer - Draw 4x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x2_Woofer(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x2_Woofer(unsigned char *, unsigned char *, +;* unsigned char *, long, long, long); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_Woofer:NEAR + PROC UnVQ_4x2_Woofer C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so +; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax +; shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx +; mov bx,[WORD PTR esi] ;Get the codebook pointer value +; add esi,2 ; then advance to the next one. + mov bl,[esi] + mov bh,[esi + eax] + inc esi + +; or bx,bx ;Is it a one color block? +; js short ??One_color + + cmp bh,00Fh + je short ??One_color + +; Draw multi-color block + + shl ebx,3 + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al ;Write 1st row to dest + shr eax,16 + mov [es:edi+2],al + mov [es:edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [es:edi+ebx+3],dh + ELSE + mov [edi],al ;Write 1st row to dest + shr eax,16 + mov [edi+2],al + mov [edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [edi+ebx+3],dh + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: +; cmp bx,SKIP_PTR ;Is this a skip block? +; jne ??Draw_One_Color + +; add edi,4 ;Move to next dest block position +; dec ecx ;More blocks for this row? +; jnz short ??Not_finished_a_line +; jmp ??Next_row + +??Draw_One_Color: +; not bx ;NOT pointer value to get color + mov al,bl + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al + mov [es:edi+2],al + mov [es:edi+ebx+1],al ;Write 2nd row to dest + mov [es:edi+ebx+3],al ;Write 2nd row to dest + ELSE + mov [edi],al + mov [edi+2],al + mov [edi+ebx+1],al ;Write 2nd row to dest + mov [edi+ebx+3],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x2_Woofer + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQABLOCK_WOOFER + + END + diff --git a/VQ/VQA32/UNVQVESA.ASM b/VQ/VQA32/UNVQVESA.ASM new file mode 100644 index 0000000..c25488a --- /dev/null +++ b/VQ/VQA32/UNVQVESA.ASM @@ -0,0 +1,380 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay32 library. +;* +;* FILE +;* unvqvesa.asm +;* +;* DESCRIPTION +;* VESA VQ decompress/draw routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + IF VQAVESA_ON + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;* SYNOPSIS +;* UnVQ_4x2_VESA320_32K(Codebook, Pointers, Palette, GrainPerWin) +;* +;* void UnVQ_4x2_VESA320_32K(char *, char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Codebook - Pointer to codebook. +;* Pointers - Pointer to vector pointer data to unvq. +;* Palette - Pointer to 15-bit palette. +;* GrainPerWin - Granularity units for 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;--------------------------------------------------------------------------- +; SET_2_PIXELS: +; - Loads 2 bytes from codebook, expands them into words in eax +; - Sets them on the screen +; cb_index: offset into codebook of start of 2 bytes to load +; di_index: offset from current di to draw 2 pixels +; BX: offset into codebook of this block +; Uses: EAX, DX, SI! +;--------------------------------------------------------------------------- + + MACRO SET_2_PIXELS cb_index,edi_index + xor edx,edx + mov dl,[BYTE PTR ebx+1+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + shl eax,16 + xor edx,edx + mov dl,[BYTE PTR ebx+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + + IF PHARLAP_TNT + mov [DWORD PTR es:edi+edi_index],eax + ELSE + mov [DWORD PTR edi+edi_index],eax + ENDIF + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROWS macro: +; Draws 'numrows' rows of 'blocksperrow' blocks each +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_ROWS numrows,blocksperrow + LOCAL ??Start_row + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Next_row + LOCAL ??Done + + mov [rowcount],numrows ; initialize row counter + + ;---------------------------------------- Start a new row: +??Start_row: + mov ecx,blocksperrow ; # blocks to fill a line + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS 0,0 + SET_2_PIXELS 2,4 + SET_2_PIXELS 4,640 + SET_2_PIXELS 6,644 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz ??Not_finished_a_line + jmp ??Next_row +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + mov [DWORD PTR es:edi+640],eax ; set 2 pixels + mov [DWORD PTR es:edi+644],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + mov [DWORD PTR edi+640],eax ; set 2 pixels + mov [DWORD PTR edi+644],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: +??Next_row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row +??Done: + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_PART_ROW macro: +; Draws top or bottom half of blocks on a row +; 'numblocks' = # blocks to draw +; 'cb_add' = amt to add to codebook (0=top half, 4=bottom half) +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_PART_ROW numblocks,cb_add + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Done + + mov ecx,numblocks + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js short ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS cb_add,0 + SET_2_PIXELS cb_add+2,4 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz short ??Not_finished_a_line + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz short ??Not_finished_a_line + jmp ??Done +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line +??Done: + ENDM + +;=========================================================================== +; The actual procedure: +;=========================================================================== + + GLOBAL C UnVQ_4x2_VESA320_32K:NEAR + PROC UnVQ_4x2_VESA320_32K C NEAR + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG pal:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG palette:NEAR PTR + ENDIF + + ARG grains_per_win:DWORD + ARG dummy1:DWORD + ARG dummy2:DWORD + + LOCAL rowcount:DWORD ; # rows drawn + LOCAL esi_save:DWORD + LOCAL cb_offset:DWORD + + IF PHARLAP_TNT + LOCAL palette:NEAR PTR + ENDIF + + ;------------------------------------------------------------------- + ; Save registers + ;------------------------------------------------------------------- + pushad + + ;------------------------------------------------------------------- + ; Set GS:[cb_offset] to codebook + ;------------------------------------------------------------------- + mov eax,[codebook] + sub eax,4 + mov [cb_offset],eax + mov esi,[pointers] + + ;------------------------------------------------------------------- + ; Set ES:DI to screen + ;------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + les edi,[FWORD pal] + mov [palette],edi + mov eax,01Ch + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + ;------------------------------------------------------------------- + ; Do Bank 0: + ; - 102 full scanlines (51 rows of blocks) + ; - 128 pixels of the top half of blocks (32 top-half blocks) + ;------------------------------------------------------------------- + SET_WINDOW 0 + DRAW_BLOCK_ROWS 51,80 + DRAW_BLOCK_PART_ROW 32,0 ; do top half + + ;------------------------------------------------------------------- + ; Do Bank 1: + ; - 128 pixels of the bottom half of the previous 32 blocks + ; - 192 pixels of full blocks (1 row of 48 blocks) + ; - 96 full scanlines (48 rows of blocks) + ;------------------------------------------------------------------- + SET_WINDOW [grains_per_win] + sub esi,64 ; subtract word size of last 32 blks + mov edi,384 + DRAW_BLOCK_PART_ROW 32,4 ; do bottom half + mov edi,0 + DRAW_BLOCK_ROWS 1,48 ; draw one row of 48 full blocks + DRAW_BLOCK_ROWS 48,80 ; draw 48 full block rows + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + popad + ret + + ENDP UnVQ_4x2_VESA320_32K + + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQAVESA_ON + + END + diff --git a/VQ/VQA32/UNVQXMDE.ASM b/VQ/VQA32/UNVQXMDE.ASM new file mode 100644 index 0000000..3c641f2 --- /dev/null +++ b/VQ/VQA32/UNVQXMDE.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQxmde.asm +;* +;* DESCRIPTION +;* XMode VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* May 18, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + + IF VQAXMODE_ON + +SKIP_PTR EQU 8000h +CBOOK_SEG EQU (0B0000h - 270h) + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* +;* SYNOPSIS +;* UnVQ_4x2_Xmode(Codebook, Pointers, Buffer, BPR, Rows, Dummy) +;* +;* void UnVQ_4x2_Xmode(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image to the Xmode display from the pointers +;* and codebook provided. This routine has been optimized for a 320x200 +;* image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* Dummy - Not used (prototype placeholder) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_Xmode:NEAR + PROC UnVQ_4x2_Xmode C NEAR USES + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + + SET_PLANE XPLANE_1 + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line1: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color1 + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],dl ;Write 2nd row to dest + ENDIF + +; add edi,4 ;Next dest block position + inc edi ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color1: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + ENDP UnVQ_4x2_Xmode + + +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* +;* SYNOPSIS +;* UnVQ_4x2_XmodeCB(Dummy, Pointers, Buffer, BPR, Rows) +;* +;* void UnVQ_4x2_XmodeCB(unsigned char *, unsigned char *, +;* unsigned char *, unsigned short, +;* unsigned short); +;* +;* FUNCTION +;* This routine copies codebook entries from video RAM to video RAM. +;* The procedure for Write Mode 1 is: +;* +;* - Perform a CPU read at the address of the 4-byte codebook entry; +;* this will load each byte at that address from all 4 bitplanes +;* into the VGA's internal latches. +;* +;* - Perform a CPU write at the destination address, with the BitMask +;* register set to 0 (this tells the VGA hardware to only use the +;* data stored in the latches to write with), and the Map Mask +;* register set to 0Fh (all bitplanes enabled). +;* +;* Optimized for 320x200. +;* The codebook must have been downloaded to video RAM before this +;* routine is called. This routine assumes the multicolor block pointers +;* have been pre-divided by 4, and have a total of 512 added to them, so +;* the pointer is an exact offset into the codebook. +;* +;* INPUTS +;* Dummy - Not used (prototype placeholder) +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to Xmode buffer. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_XmodeCB:NEAR + PROC UnVQ_4x2_XmodeCB C NEAR USES + + ARG cbdummy:FAR PTR + ARG pointers:FAR PTR + ARG buffer:FAR PTR + ARG blocksperrow:WORD + ARG numrows:WORD + +; ;------------------------------------------------------------------- +; ; Local variables: +; ;------------------------------------------------------------------- +; LOCAL di_startval:WORD ; init value for DI, for new row +; +; ;=================================================================== +; ; Initialization +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Save our registers +; ;------------------------------------------------------------------- +; push ds +; push es +; push fs +; pushad +; +; ;------------------------------------------------------------------- +; ; Save codebook's segment in DS +; ;------------------------------------------------------------------- +; mov ax,CBOOK_SEG +; mov ds,ax +; +; ;------------------------------------------------------------------- +; ; Load pointers into FS:BX +; ;------------------------------------------------------------------- +; les di,[pointers] +; mov ax,es +; mov fs,ax +; mov bx,di +; +; ;------------------------------------------------------------------- +; ; Load screen address into ES:DI +; ;------------------------------------------------------------------- +; les di, [buffer] ; point ES:DI to dest +; mov [di_startval],di ; store it +; +; ;------------------------------------------------------------------- +; ; Initialize VGA registers: +; ; - Enable all bitplanes for writing +; ; - Set the BitMask register to 0, so only the data from the +; ; VGA latches is written into the bitplanes +; ;------------------------------------------------------------------- +; SET_PLANE 0fh ; enable all planes for write +; SET_WRITEMODE 1 +; +; ;=================================================================== +; ; The drawing loop: +; ; DS:SI = codebook +; ; ES:DI = drawing buffer +; ; FS:BX = pointers +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Start a new row of drawing +; ;------------------------------------------------------------------- +;??Start_row: +; mov cx,[blocksperrow] ; # blocks to fill a line +; +; ;------------------------------------------------------------------- +; ; Start a new block +; ;------------------------------------------------------------------- +;??Not_finished_a_line: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov si,[WORD PTR fs:bx] ; SI = ptr word (cbook offset) +; add bx,2 ; next ptr word +; ; +; ;................... skip ptr value SKIP_PTR ....................... +; ; +; cmp si,SKIP_PTR +; jne ??Draw_Block +; inc di +; dec cx +; jnz short ??Not_finished_a_line +; jmp ??Next_row +; +; ;------------------------------------------------------------------- +; ; Draw a block via the VGA internal latches: +; ; DS:SI = codebook address +; ; ES:DI = buffer position to draw at +; ; - Load the VGA latches from the 1st 4 codebook bytes +; ; - write 4 pixels with one CPU write +; ; - If this is a one-color block, skip the next codebook read +; ; (Video RAM reads are very slow); otherwise, latch the next 4 +; ; codebook bytes +; ; - write the next 4 pixels +; ;------------------------------------------------------------------- +; ; +; ;..................... draw 1st 4 pixels ........................... +; ; +;??Draw_Block: +; mov al,[ds:si] ; latch 1st 4 cbook bytes +; mov [es:di],al ; write 4 pixels +; ; +; ;.................. check for 1-color block ........................ +; ; +; cmp si,512 ; if 1color blk, don't read +; jb ??One_Color +; ; +; ;..................... draw next 4 pixels .......................... +; ; +; mov al,[ds:si+1] ; latch next 4 cbook bytes +;??One_Color: +; mov [es:di+80],al ; write next 4 pixels +; inc di ; next block position +; ; +; ;...................... check block count .......................... +; ; +; dec cx ; decrement block count +; jnz short ??Not_finished_a_line +; +; ;------------------------------------------------------------------- +; ; Go to the next block row: +; ; - Add (80*2/16) to ES, and set DI to its start-row value +; ; (Incrementing the segment allows us to unpack up to 1MB of data) +; ;------------------------------------------------------------------- +; ; +; ;...................... add (320*2/16) to ES ....................... +; ; +;??Next_row: +; mov ax,es +; add ax,10 ; add 80*2/16 +; mov es,ax +; ; +; ;.................. set DI to its start-row value .................. +; ; +; mov di,[di_startval] +; ; +; ;............ see if we're past the end of the ptr data ............ +; ; +; dec [numrows] +; jg ??Start_row +; +;??End_of_data: +; ;------------------------------------------------------------------- +; ; Restore VGA Write Mode to 0 +; ;------------------------------------------------------------------- +; SET_WRITEMODE 0 +; +; popad +; pop fs +; pop es +; pop ds + ret + + ENDP UnVQ_4x2_XmodeCB + + +;**************************************************************************** +;* +;* NAME +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* +;* SYNOPSIS +;* Upload_4x2CB(Codebook, Entries) +;* +;* void Upload_4x2CB(unsigned char *, unsigned short); +;* +;* FUNCTION +;* This routine copies the given codebook into Xmode VRAM, so that it +;* can be used later for direct video-to-video copies. The address used +;* is the end of video memory minus 02700h (10K). This should be plenty +;* for a 3000-entry codebook; each 4x2 codebook entry will take up 8 +;* 8 bytes, or 2 addresses in XMode (6000 addresses). +;* +;* The routine also creates a 1-color-block table in VRAM, so the 1-color +;* blocks can be generated the same way as the multicolor blocks. +;* +;* XMode 320x200 uses 320x200/4 addresses per page, for a total of 32000 +;* addresses. XMode 320x240 uses 320x240/4 addresses per page, for a +;* total of 38400 addresses. This leaves 27136 addresses unused. +;* +;* INPUTS +;* Codebook - Pointer to codebook to copy. +;* Entries - Number of codebook entries to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Upload_4x2CB:NEAR + PROC Upload_4x2CB C NEAR USES + + ARG codebook:NEAR PTR + ARG numentries:DWORD + +; ;=================================================================== +; ; Generate the 1-color block table by writing each color value from +; ; 0-255 into all 4 bitplanes, at 256 consecutive addresses: +; ;=================================================================== +; SET_PLANE 0fh ; enable all bitplanes for writing +; ;................................................................... +; ; Set ES:DI to destination address, set up CX for the loop +; ;................................................................... +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,0 +; mov cx,256 +; mov ax,0 +;??1_Color_Loop: +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; inc ax ; next color # +; dec cx ; decrement loop counter +; jnz ??1_Color_Loop +; +; ;=================================================================== +; ; Copy the codebook into video RAM, one plane at a time: +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Copy codebook byte 0 into Plane 1 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_1 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_1: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_1 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 1 Plane 2 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,1 ; use 2nd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_2 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_2: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_2 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 2 Plane 3 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,2 ; use 3rd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_3 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_3: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_3 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 3 Plane 4 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,3 ; use 4th byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_4 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_4: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_4 +; + ret + + ENDP Upload_4x2CB + + ENDIF ;VQABLOCK_4X2 + +;**************************************************************************** +;* +;* NAME +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;* SYNOPSIS +;* XlatePointers(Pointers, Entries) +;* +;* void XlatePointers(unsigned char *, unsigned short); +;* +;* FUNCTION +;* +;* INPUTS +;* Pointers - Pointer to vector pointers to translate. +;* Entries - Number of pointer entries. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C XlatePointers:NEAR + PROC XlatePointers C NEAR USES + + ARG pointers:NEAR PTR + ARG numpointers:DWORD + +; ;------------------------------------------------------------------- +; ; Load pointers into DS:SI +; ;------------------------------------------------------------------- +; lds si,[pointers] +; +; mov cx,[numpointers] ; init to # pointers on scrn +; +;??Process_pointer: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov ax,[WORD PTR ds:si] ; SI = ptr word (cbook offset) +; ; +; ;.................... check for a 1-color block .................... +; ; +; or ax,ax ; check to see if high bit set +; js short ??One_color +; ; +; ;....................... multi-color pointer ....................... +; ; +; sub ax,4 ; subtract 4 +; shr ax,2 ; divide by 4 +; add ax,512 ; add 512 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; jmp ??Done +; +;??One_color: +; ; +; ;......................... 1-color pointer ......................... +; ; +; not ax ; get actual color value +; shl ax,1 ; multiply by 2 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; +;??Done: + ret + + ENDP XlatePointers + + ENDIF ;VQAXMODE_ON + END + + + + + diff --git a/VQ/VQA32/UNVQXMDE.ASM.BAK b/VQ/VQA32/UNVQXMDE.ASM.BAK new file mode 100644 index 0000000..aa6fe9c --- /dev/null +++ b/VQ/VQA32/UNVQXMDE.ASM.BAK @@ -0,0 +1,1221 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQA player library. +;* +;* FILE +;* UnVQ4x2.asm +;* +;* DESCRIPTION +;* 4x2 VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;**************************************************************************** + + IDEAL + MODEL LARGE + P386N + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + + IF VQAXMODE_ON + +SKIP_PTR EQU 8000h +CBOOK_SEG EQU (0B000h - 270h) + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* +;* SYNOPSIS +;* UnVQ_4x2_Xmode(Codebook, Pointers, Buffer, BPR, Rows, Dummy) +;* +;* void UnVQ_4x2_Xmode(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image to the Xmode display from the pointers +;* and codebook provided. This routine has been optimized for a 320x200 +;* image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* Dummy - Not used (prototype placeholder) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + PUBLIC C UnVQ_4x2_Xmode + PROC C UnVQ_4x2_Xmode FAR USES + + ARG codebook:FAR PTR + ARG pointers:FAR PTR + ARG buffer:FAR PTR + ARG blocksperrow:WORD + ARG numrows:WORD + ARG dummy:WORD + + ;------------------------------------------------------------------- + ; Local variables: + ;------------------------------------------------------------------- + LOCAL data_end:WORD ; end of pointer data + LOCAL si_startval:WORD ; init value for SI, for new plane + LOCAL es_startval:WORD ; init value for ES, for new plane + LOCAL di_startval:WORD ; init value for DI, for new row + LOCAL cb_offset:WORD ; codebook offset + + ;=================================================================== + ; Initialization + ;=================================================================== + ;------------------------------------------------------------------- + ; Save our registers + ;------------------------------------------------------------------- + push ds + push es + push fs + pushad + + ;------------------------------------------------------------------- + ; Save codebook's address in FS:[cb_offset]: + ; - load it into DS:SI, and normalize it + ; - save it into FS:[cb_offset], and subtract 4 from it, so the + ; pointer offsets will point directly at the correct codeword + ;------------------------------------------------------------------- + lds si,[codebook] ; DS:SI = codebook + cmp si, 16 ; SI < 16 ? + jb short ??Decrement_codebook ; yes - don't normalize it + mov ax, si ; AX = SI + shr ax, 4 ; AX = SI / 16 + mov bx, ds ; BX = DS + add ax, bx ; AX = (SI / 16) + DS + mov ds, ax ; adjust DS upward + and si, 000Fh ; save only low nybble +??Decrement_codebook: + mov ax,ds ; AX = cb segment + sub ax,1 ; subtract 16 bytes + mov fs,ax ; save in FS + mov [cb_offset],si + add [cb_offset],12 + + ;------------------------------------------------------------------- + ; Load pointers into DS:SI, then normalize DS:SI to minimize offset + ;------------------------------------------------------------------- +??Normalize_pointers: + lds si, [pointers] ; DS:SI = pointers + cmp si, 16 ; SI < 16 ? + jb short ??Set_Data_End ; yes - don't normalize it + mov ax, si ; AX = SI + shr ax, 4 ; AX = SI / 16 + mov bx, ds ; BX = DS + add ax, bx ; AX = (SI / 16) + DS + mov ds, ax ; adjust DS upward + and si, 000Fh ; save only low nybble + + ;------------------------------------------------------------------- + ; Set [data_end] to [pointers] + size of ptr data + ;------------------------------------------------------------------- +??Set_Data_End: + mov ax,[numrows] + mul [blocksperrow] ; AX = total # blocks + shl ax,1 ; mult by 2 for word size + add ax,si ; compute end of data + mov [data_end],ax ; store it + mov [si_startval],si ; save SI + + ;------------------------------------------------------------------- + ; Load buffer into ES:DI, then normalize ES:DI to minimize offset + ;------------------------------------------------------------------- + les di, [buffer] ; point ES:DI to dest + mov [es_startval],es ; save ES's start value + mov [di_startval],di ; store it + cmp di, 16 ; DI < 16 ? + jb short ??Set_XPlane1 ; yes - don't normalize it + mov ax, di ; AX = DI + shr ax, 4 ; AX = DI / 16 + mov bx, es ; BX = ES + add ax, bx ; AX = (DI / 16) + ES + mov es, ax ; adjust ES upward + and di, 000Fh ; save only low nybble + mov [es_startval],es ; save ES's start value + mov [di_startval],di ; save DI's start value + + + ;=================================================================== + ; The drawing loop: + ; DS:SI = pointer data + ; ES:DI = drawing buffer + ; FS:[cb_offset] = codebook + ;=================================================================== + ;=================================================================== + ; Set X-Mode Plane 1 + ; Plane 1 is the left half of even-numbered blocks on a row. + ; (left pixel = low byte of codebook word) + ;=================================================================== +??Set_XPlane1: + SET_PLANE XPLANE_1 + + ;------------------------------------------------------------------- + ; Start a new row of drawing + ;------------------------------------------------------------------- +??Start_row_1: + mov cx,[blocksperrow] ; # blocks to fill a line + + ;------------------------------------------------------------------- + ; Start a new block + ;------------------------------------------------------------------- +??Not_finished_a_line_1: + ; + ;..................... get next pointer word ....................... + ; + mov bx,[WORD PTR DS:SI] ; AX = pointer word + add si,2 ; SI = next pointer + ; + ;.................... check for a 1-color block .................... + ; + or bx,bx ; check to see if high bit set + js short ??One_color_1 + + ;------------------------------------------------------------------- + ; Draw a multi-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- + add bx,[cb_offset] ; get codebook offset + ; + ;................ transfer bytes from FS:BX to ES:DI ............... + ; + mov ax,[fs:bx] ; get 1st codeword + mov dx,[fs:bx+4] ; get 2nd codeword + mov [es:di],al ; draw 1st + mov [es:di+80],dl ; draw 2nd + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_1 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_1 ; no - goto end + jmp ??Start_row_1 ; start new block row + + ;------------------------------------------------------------------- + ; Draw 1-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- +??One_color_1: + ; + ;................... skip ptr value SKIP_PTR ....................... + ; + cmp bx,SKIP_PTR + jne ??Draw_One_Color_1 + inc di + dec cx + jnz short ??Not_finished_a_line_1 + jmp ??Next_row_1 + ; + ;................. transfer bytes from AX to DS:SI ................. + ; +??Draw_One_Color_1: + not bx + mov bh,bl ; dup al in al&ah + mov [es:di],bl ; set pixel + mov [es:di+80],bl ; set pixel + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_1 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; +??Next_row_1: + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_1 ; no - goto end + jmp ??Start_row_1 ; start new block row + +??End_of_data_1: + ;------------------------------------------------------------------- + ; Restore DS:SI to start of pointer data, and ES:DI to Xmode page + ;------------------------------------------------------------------- + mov si,[si_startval] + mov es,[es_startval] + mov di,[di_startval] + + ;=================================================================== + ; Set X-Mode Plane 2 + ; Plane 2 is the right half of even-numbered blocks on a row. + ; (right pixel = high byte of codebook word) + ;=================================================================== + SET_PLANE XPLANE_2 + + ;------------------------------------------------------------------- + ; Start a new row of drawing + ;------------------------------------------------------------------- +??Start_row_2: + mov cx,[blocksperrow] ; # blocks to fill a line + + ;------------------------------------------------------------------- + ; Start a new block + ;------------------------------------------------------------------- +??Not_finished_a_line_2: + ; + ;..................... get next pointer word ....................... + ; + mov bx,[WORD PTR DS:SI] ; AX = pointer word + add si,2 ; SI = next pointer + ; + ;.................... check for a 1-color block .................... + ; + or bx,bx ; check to see if high bit set + js short ??One_color_2 + + ;------------------------------------------------------------------- + ; Draw a multi-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- + add bx,[cb_offset] ; get codebook offset + ; + ;................ transfer bytes from FS:BX to ES:DI ............... + ; + mov ax,[fs:bx] ; get 1st codeword + mov dx,[fs:bx+4] ; get 2nd codeword + mov [es:di],ah ; draw 1st + mov [es:di+80],dh ; draw 2nd + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_2 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_2 ; no - goto end + jmp ??Start_row_2 ; start new block row + + ;------------------------------------------------------------------- + ; Draw 1-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- +??One_color_2: + ; + ;................... skip ptr value SKIP_PTR ....................... + ; + cmp bx,SKIP_PTR + jne ??Draw_One_Color_2 + inc di + dec cx + jnz short ??Not_finished_a_line_2 + jmp ??Next_row_2 + ; + ;................. transfer bytes from AX to DS:SI ................. + ; +??Draw_One_Color_2: + not bx + mov bh,bl ; dup al in al&ah + mov [es:di],bh ; store 2 colors + mov [es:di+80],bh ; store 2 colors + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_2 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; +??Next_row_2: + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_2 ; no - goto end + jmp ??Start_row_2 ; start new block row + +??End_of_data_2: + ;------------------------------------------------------------------- + ; Restore DS:SI to start of pointer data, and ES:DI to Xmode page + ;------------------------------------------------------------------- + mov si,[si_startval] + mov es,[es_startval] + mov di,[di_startval] + + ;=================================================================== + ; Set X-Mode Plane 3 + ; Plane 3 is the left half of odd-numbered blocks on a row. + ; (left pixel = low byte of codebook word) + ;=================================================================== + SET_PLANE XPLANE_3 + + ;------------------------------------------------------------------- + ; Start a new row of drawing + ;------------------------------------------------------------------- +??Start_row_3: + mov cx,[blocksperrow] ; # blocks to fill a line + + ;------------------------------------------------------------------- + ; Start a new block + ;------------------------------------------------------------------- +??Not_finished_a_line_3: + ; + ;..................... get next pointer word ....................... + ; + mov bx,[WORD PTR DS:SI] ; AX = pointer word + add si,2 ; SI = next pointer + ; + ;.................... check for a 1-color block .................... + ; + or bx,bx ; check to see if high bit set + js short ??One_color_3 + + ;------------------------------------------------------------------- + ; Draw a multi-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- + add bx,[cb_offset] ; get codebook offset + ; + ;................ transfer bytes from FS:BX to ES:DI ............... + ; + mov ax,[fs:bx+2] ; get 1st codeword + mov dx,[fs:bx+6] ; get 2nd codeword + mov [es:di],al ; draw 1st + mov [es:di+80],dl ; draw 2nd + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_3 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_3 ; no - goto end + jmp ??Start_row_3 ; start new block row + + ;------------------------------------------------------------------- + ; Draw 1-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- +??One_color_3: + ; + ;................... skip ptr value SKIP_PTR ....................... + ; + cmp bx,SKIP_PTR + jne ??Draw_One_Color_3 + inc di + dec cx + jnz short ??Not_finished_a_line_3 + jmp ??Next_row_3 + ; + ;................. transfer bytes from AX to DS:SI ................. + ; +??Draw_One_Color_3: + not bx + mov bh,bl ; dup al in al&ah + mov [es:di],bl ; store 2 colors + mov [es:di+80],bl ; store 2 colors + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_3 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; +??Next_row_3: + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_3 ; no - goto end + jmp ??Start_row_3 ; start new block row + +??End_of_data_3: + ;------------------------------------------------------------------- + ; Restore DS:SI to start of pointer data, and ES:DI to Xmode page + ;------------------------------------------------------------------- + mov si,[si_startval] + mov es,[es_startval] + mov di,[di_startval] + + ;=================================================================== + ; Set X-Mode Plane 4 + ; Plane 4 is the right half of odd-numbered blocks on a row. + ; (right pixel = high byte of codebook word) + ;=================================================================== + SET_PLANE XPLANE_4 + + ;------------------------------------------------------------------- + ; Start a new row of drawing + ;------------------------------------------------------------------- +??Start_row_4: + mov cx,[blocksperrow] ; # blocks to fill a line + + ;------------------------------------------------------------------- + ; Start a new block + ;------------------------------------------------------------------- +??Not_finished_a_line_4: + ; + ;..................... get next pointer word ....................... + ; + mov bx,[WORD PTR DS:SI] ; AX = pointer word + add si,2 ; SI = next pointer + ; + ;.................... check for a 1-color block .................... + ; + or bx,bx ; check to see if high bit set + js short ??One_color_4 + + ;------------------------------------------------------------------- + ; Draw a multi-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- + add bx,[cb_offset] ; get codebook offset + ; + ;................ transfer bytes from FS:BX to ES:DI ............... + ; + mov ax,[fs:bx+2] ; get 1st codeword + mov dx,[fs:bx+6] ; get 2nd codeword + mov [es:di],ah ; draw 1st + mov [es:di+80],dh ; draw 2nd + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_4 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_4 ; no - goto end + jmp ??Start_row_4 ; start new block row + + ;------------------------------------------------------------------- + ; Draw 1-color block + ; (AX = pointer byte) + ; (ES:DI = buffer position to draw at) + ; (FS:[cb_offset] = codebook address) + ;------------------------------------------------------------------- +??One_color_4: + ; + ;................... skip ptr value SKIP_PTR ....................... + ; + cmp bx,SKIP_PTR + jne ??Draw_One_Color_4 + inc di + dec cx + jnz short ??Not_finished_a_line_4 + jmp ??Next_row_4 + ; + ;................. transfer bytes from AX to DS:SI ................. + ; +??Draw_One_Color_4: + not bx + mov bh,bl ; dup al in al&ah + mov [es:di],bh ; store 2 colors + mov [es:di+80],bh ; store 2 colors + inc di ; next pixel + ; + ;..................... check block count ........................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line_4 + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add new offset to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ; (Divide by 4 because there's 1/4 as many addresses per line + ; in XMODE) + ;------------------------------------------------------------------- + ; + ;....................... Add 80*2/16 to ES ......................... + ; +??Next_row_4: + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + cmp si,[data_end] + jnb short ??End_of_data_4 ; no - goto end + jmp ??Start_row_4 ; start new block row + +??End_of_data_4: + popad + pop fs + pop es + pop ds + ret + + ENDP UnVQ_4x2_Xmode + + +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* +;* SYNOPSIS +;* UnVQ_4x2_XmodeCB(Dummy, Pointers, Buffer, BPR, Rows) +;* +;* void UnVQ_4x2_XmodeCB(unsigned char *, unsigned char *, +;* unsigned char *, unsigned short, +;* unsigned short); +;* +;* FUNCTION +;* This routine copies codebook entries from video RAM to video RAM. +;* The procedure for Write Mode 1 is: +;* +;* - Perform a CPU read at the address of the 4-byte codebook entry; +;* this will load each byte at that address from all 4 bitplanes +;* into the VGA's internal latches. +;* +;* - Perform a CPU write at the destination address, with the BitMask +;* register set to 0 (this tells the VGA hardware to only use the +;* data stored in the latches to write with), and the Map Mask +;* register set to 0Fh (all bitplanes enabled). +;* +;* Optimized for 320x200. +;* The codebook must have been downloaded to video RAM before this +;* routine is called. This routine assumes the multicolor block pointers +;* have been pre-divided by 4, and have a total of 512 added to them, so +;* the pointer is an exact offset into the codebook. +;* +;* INPUTS +;* Dummy - Not used (prototype placeholder) +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to Xmode buffer. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + PUBLIC C UnVQ_4x2_XmodeCB + PROC C UnVQ_4x2_XmodeCB FAR USES + + ARG cbdummy:FAR PTR + ARG pointers:FAR PTR + ARG buffer:FAR PTR + ARG blocksperrow:WORD + ARG numrows:WORD + + ;------------------------------------------------------------------- + ; Local variables: + ;------------------------------------------------------------------- + LOCAL di_startval:WORD ; init value for DI, for new row + + ;=================================================================== + ; Initialization + ;=================================================================== + ;------------------------------------------------------------------- + ; Save our registers + ;------------------------------------------------------------------- + push ds + push es + push fs + pushad + + ;------------------------------------------------------------------- + ; Save codebook's segment in DS + ;------------------------------------------------------------------- + mov ax,CBOOK_SEG + mov ds,ax + + ;------------------------------------------------------------------- + ; Load pointers into FS:BX + ;------------------------------------------------------------------- + les di,[pointers] + mov ax,es + mov fs,ax + mov bx,di + + ;------------------------------------------------------------------- + ; Load screen address into ES:DI + ;------------------------------------------------------------------- + les di, [buffer] ; point ES:DI to dest + mov [di_startval],di ; store it + + ;------------------------------------------------------------------- + ; Initialize VGA registers: + ; - Enable all bitplanes for writing + ; - Set the BitMask register to 0, so only the data from the + ; VGA latches is written into the bitplanes + ;------------------------------------------------------------------- + SET_PLANE 0fh ; enable all planes for write + SET_WRITEMODE 1 + + ;=================================================================== + ; The drawing loop: + ; DS:SI = codebook + ; ES:DI = drawing buffer + ; FS:BX = pointers + ;=================================================================== + ;------------------------------------------------------------------- + ; Start a new row of drawing + ;------------------------------------------------------------------- +??Start_row: + mov cx,[blocksperrow] ; # blocks to fill a line + + ;------------------------------------------------------------------- + ; Start a new block + ;------------------------------------------------------------------- +??Not_finished_a_line: + ; + ;..................... get next pointer word ....................... + ; + mov si,[WORD PTR fs:bx] ; SI = ptr word (cbook offset) + add bx,2 ; next ptr word + ; + ;................... skip ptr value SKIP_PTR ....................... + ; + cmp si,SKIP_PTR + jne ??Draw_Block + inc di + dec cx + jnz short ??Not_finished_a_line + jmp ??Next_row + + ;------------------------------------------------------------------- + ; Draw a block via the VGA internal latches: + ; DS:SI = codebook address + ; ES:DI = buffer position to draw at + ; - Load the VGA latches from the 1st 4 codebook bytes + ; - write 4 pixels with one CPU write + ; - If this is a one-color block, skip the next codebook read + ; (Video RAM reads are very slow); otherwise, latch the next 4 + ; codebook bytes + ; - write the next 4 pixels + ;------------------------------------------------------------------- + ; + ;..................... draw 1st 4 pixels ........................... + ; +??Draw_Block: + mov al,[ds:si] ; latch 1st 4 cbook bytes + mov [es:di],al ; write 4 pixels + ; + ;.................. check for 1-color block ........................ + ; + cmp si,512 ; if 1color blk, don't read + jb ??One_Color + ; + ;..................... draw next 4 pixels .......................... + ; + mov al,[ds:si+1] ; latch next 4 cbook bytes +??One_Color: + mov [es:di+80],al ; write next 4 pixels + inc di ; next block position + ; + ;...................... check block count .......................... + ; + dec cx ; decrement block count + jnz short ??Not_finished_a_line + + ;------------------------------------------------------------------- + ; Go to the next block row: + ; - Add (80*2/16) to ES, and set DI to its start-row value + ; (Incrementing the segment allows us to unpack up to 1MB of data) + ;------------------------------------------------------------------- + ; + ;...................... add (320*2/16) to ES ....................... + ; +??Next_row: + mov ax,es + add ax,10 ; add 80*2/16 + mov es,ax + ; + ;.................. set DI to its start-row value .................. + ; + mov di,[di_startval] + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [numrows] + jg ??Start_row + +??End_of_data: + ;------------------------------------------------------------------- + ; Restore VGA Write Mode to 0 + ;------------------------------------------------------------------- + SET_WRITEMODE 0 + + popad + pop fs + pop es + pop ds + ret + + ENDP UnVQ_4x2_XmodeCB + + +;**************************************************************************** +;* +;* NAME +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* +;* SYNOPSIS +;* Upload_4x2CB(Codebook, Entries) +;* +;* void Upload_4x2CB(unsigned char *, unsigned short); +;* +;* FUNCTION +;* This routine copies the given codebook into Xmode VRAM, so that it +;* can be used later for direct video-to-video copies. The address used +;* is the end of video memory minus 02700h (10K). This should be plenty +;* for a 3000-entry codebook; each 4x2 codebook entry will take up 8 +;* 8 bytes, or 2 addresses in XMode (6000 addresses). +;* +;* The routine also creates a 1-color-block table in VRAM, so the 1-color +;* blocks can be generated the same way as the multicolor blocks. +;* +;* XMode 320x200 uses 320x200/4 addresses per page, for a total of 32000 +;* addresses. XMode 320x240 uses 320x240/4 addresses per page, for a +;* total of 38400 addresses. This leaves 27136 addresses unused. +;* +;* INPUTS +;* Codebook - Pointer to codebook to copy. +;* Entries - Number of codebook entries to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + PUBLIC C Upload_4x2CB + PROC C Upload_4x2CB FAR USES ds si es di cx dx + + ARG codebook:FAR PTR + ARG numentries:WORD + + ;=================================================================== + ; Generate the 1-color block table by writing each color value from + ; 0-255 into all 4 bitplanes, at 256 consecutive addresses: + ;=================================================================== + SET_PLANE 0fh ; enable all bitplanes for writing + ;................................................................... + ; Set ES:DI to destination address, set up CX for the loop + ;................................................................... + mov ax,CBOOK_SEG + mov es,ax + mov di,0 + mov cx,256 + mov ax,0 +??1_Color_Loop: + mov [es:di],al ; write 4 bytes + inc di ; next 4-byte position + mov [es:di],al ; write 4 bytes + inc di ; next 4-byte position + inc ax ; next color # + dec cx ; decrement loop counter + jnz ??1_Color_Loop + + ;=================================================================== + ; Copy the codebook into video RAM, one plane at a time: + ;=================================================================== + ;------------------------------------------------------------------- + ; Copy codebook byte 0 into Plane 1 + ;------------------------------------------------------------------- + ;................................................................... + ; Set DS:SI to codebook address, ES:DI to screen address + ; (Codebook is stored at offset 1, so the pointers will point at + ; exactly the right offset.) + ;................................................................... + lds si, [codebook] ; DS:SI = codebook + mov ax,CBOOK_SEG + mov es,ax + mov di,512 + + ;................................................................... + ; Set up the loop + ;................................................................... + SET_PLANE XPLANE_1 + mov cx,[numentries] ; set loop counter + shl cx,1 ; do 2 DWORDS per cbook entry + + ;................................................................... + ; Loop through codebook entries + ;................................................................... +??CB_Loop_1: + mov al,[ds:si] + mov [es:di],al + add si,4 + inc di + dec cx + jnz ??CB_Loop_1 + + ;------------------------------------------------------------------- + ; Copy codebook byte 1 Plane 2 + ;------------------------------------------------------------------- + ;................................................................... + ; Set DS:SI to codebook address, ES:DI to screen address + ; (Codebook is stored at offset 1, so the pointers will point at + ; exactly the right offset.) + ;................................................................... + lds si, [codebook] ; DS:SI = codebook + mov ax,CBOOK_SEG + mov es,ax + mov di,512 + add si,1 ; use 2nd byte value + + ;................................................................... + ; Set up the loop + ;................................................................... + SET_PLANE XPLANE_2 + mov cx,[numentries] ; set loop counter + shl cx,1 ; do 2 DWORDS per cbook entry + + ;................................................................... + ; Loop through codebook entries + ;................................................................... +??CB_Loop_2: + mov al,[ds:si] + mov [es:di],al + add si,4 + inc di + dec cx + jnz ??CB_Loop_2 + + ;------------------------------------------------------------------- + ; Copy codebook byte 2 Plane 3 + ;------------------------------------------------------------------- + ;................................................................... + ; Set DS:SI to codebook address, ES:DI to screen address + ; (Codebook is stored at offset 1, so the pointers will point at + ; exactly the right offset.) + ;................................................................... + lds si, [codebook] ; DS:SI = codebook + mov ax,CBOOK_SEG + mov es,ax + mov di,512 + add si,2 ; use 3rd byte value + + ;................................................................... + ; Set up the loop + ;................................................................... + SET_PLANE XPLANE_3 + mov cx,[numentries] ; set loop counter + shl cx,1 ; do 2 DWORDS per cbook entry + + ;................................................................... + ; Loop through codebook entries + ;................................................................... +??CB_Loop_3: + mov al,[ds:si] + mov [es:di],al + add si,4 + inc di + dec cx + jnz ??CB_Loop_3 + + ;------------------------------------------------------------------- + ; Copy codebook byte 3 Plane 4 + ;------------------------------------------------------------------- + ;................................................................... + ; Set DS:SI to codebook address, ES:DI to screen address + ; (Codebook is stored at offset 1, so the pointers will point at + ; exactly the right offset.) + ;................................................................... + lds si, [codebook] ; DS:SI = codebook + mov ax,CBOOK_SEG + mov es,ax + mov di,512 + add si,3 ; use 4th byte value + + ;................................................................... + ; Set up the loop + ;................................................................... + SET_PLANE XPLANE_4 + mov cx,[numentries] ; set loop counter + shl cx,1 ; do 2 DWORDS per cbook entry + + ;................................................................... + ; Loop through codebook entries + ;................................................................... +??CB_Loop_4: + mov al,[ds:si] + mov [es:di],al + add si,4 + inc di + dec cx + jnz ??CB_Loop_4 + + ret + + ENDP Upload_4x2CB + + ENDIF ;VQABLOCK_4X2 + +;**************************************************************************** +;* +;* NAME +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;* SYNOPSIS +;* XlatePointers(Pointers, Entries) +;* +;* void XlatePointers(unsigned char *, unsigned short); +;* +;* FUNCTION +;* +;* INPUTS +;* Pointers - Pointer to vector pointers to translate. +;* Entries - Number of pointer entries. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + PUBLIC C XlatePointers + PROC C XlatePointers FAR USES ds si cx + + ARG pointers:FAR PTR + ARG numpointers:WORD + + ;------------------------------------------------------------------- + ; Load pointers into DS:SI + ;------------------------------------------------------------------- + lds si,[pointers] + + mov cx,[numpointers] ; init to # pointers on scrn + +??Process_pointer: + ; + ;..................... get next pointer word ....................... + ; + mov ax,[WORD PTR ds:si] ; SI = ptr word (cbook offset) + ; + ;.................... check for a 1-color block .................... + ; + or ax,ax ; check to see if high bit set + js short ??One_color + ; + ;....................... multi-color pointer ....................... + ; + sub ax,4 ; subtract 4 + shr ax,2 ; divide by 4 + add ax,512 ; add 512 + mov [WORD PTR ds:si],ax ; save new value + add si,2 ; next ptr word + ; + ;....................... see if we're done ......................... + ; + dec cx + jnz ??Process_pointer + jmp ??Done + +??One_color: + ; + ;......................... 1-color pointer ......................... + ; + not ax ; get actual color value + shl ax,1 ; multiply by 2 + mov [WORD PTR ds:si],ax ; save new value + add si,2 ; next ptr word + ; + ;....................... see if we're done ......................... + ; + dec cx + jnz ??Process_pointer + +??Done: + ret + + ENDP XlatePointers + + ENDIF ;VQAXMODE_ON + END + diff --git a/VQ/VQA32/VERTAG.CPP b/VQ/VQA32/VERTAG.CPP new file mode 100644 index 0000000..85fbbe9 --- /dev/null +++ b/VQ/VQA32/VERTAG.CPP @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vertag.c +* +* DESCRIPTION +* Version Tag +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +****************************************************************************/ + +#include "vqaplayp.h" + +#ifdef __WATCOMC__ +#define DEVNAME "Watcom/4GW" +#else +#define DEVNAME "Borland/TNT" +#endif + +char VerTag[] = {"$VER$" VQA_IDSTRING" "DEVNAME" ("VQA_DATE")"}; +char ReqTag[] = {"$REQ$" VQA_REQUIRES}; + diff --git a/VQ/VQA32/VQAFILE.H b/VQ/VQA32/VQAFILE.H new file mode 100644 index 0000000..24b45db --- /dev/null +++ b/VQ/VQA32/VQAFILE.H @@ -0,0 +1,197 @@ +/* +** 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 . +*/ + +#ifndef VQAFILE_H +#define VQAFILE_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqafile.h +* +* DESCRIPTION +* VQA file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +#include + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED DEFINES. + *-------------------------------------------------------------------------*/ + +/* VQAHeader: VQA movie description header. (VQHD) + * + * Version - VQA version. + * Flags - Various flags. (See below) + * ImageWidth - Image width in pixels. + * ImageHeight - Image height in pixels. + * BlockWidth - Block width in pixels. + * BlockHeight - Block height in pixels. + * Frames - Total number of frames in the movie. + * FPS - Playback rate (Frame Per Second). + * Groupsize - Frame grouping size (frames per codebook). + * Num1Colors - Number of 1 color colors. + * CBentries - Number of codebook entries. + * Xpos - X position to draw frames. (-1 = Center) + * Ypos - Y position to draw frames. (-1 = Center) + * MaxFramesize - Size of largest frame. + * SampleRate - Sample rate of primary audio stream. + * Channels - Number of channels in primary audio stream. + * BitsPerSample - Sample bit size in primary audio stream. + * FutureUse - Reserved for future expansion. + */ +typedef struct _VQAHeader { + unsigned short Version; + unsigned short Flags; + unsigned short Frames; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char BlockWidth; + unsigned char BlockHeight; + unsigned char FPS; + unsigned char Groupsize; + unsigned short Num1Colors; + unsigned short CBentries; + unsigned short Xpos; + unsigned short Ypos; + unsigned short MaxFramesize; + unsigned short SampleRate; + unsigned char Channels; + unsigned char BitsPerSample; + unsigned short AltSampleRate; + unsigned char AltChannels; + unsigned char AltBitsPerSample; + unsigned short FutureUse[5]; +} VQAHeader; + +/* Version type. */ +#define VQAHD_VER1 1 +#define VQAHD_VER2 2 + +/* VQA header flag definitions */ +#define VQAHDB_AUDIO 0 /* Audio track present. */ +#define VQAHDB_ALTAUDIO 1 /* Alternate audio track present. */ +#define VQAHDF_AUDIO (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 1 /* 4x4 block decode enable/disable */ +#else +#define VQAVOC_ON 1 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 1 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 1 /* 4x4 block decode enable/disable */ +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_STOP 2 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +/* Error/condition values */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ + +/* Memory limits */ +#define VQA_NUM_MAXRATES 5 /* Number of max rates in the Config struct */ +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use 22050 scaled to the + * frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * MaxRate - Fixed rate playback table. + */ +typedef struct _VQAConfig { + void (*DrawerCallback)(unsigned char *screen, long framenum); + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay 32 library. +;* +;* FILE +;* vqaplay.i +;* +;* DESCRIPTION +;* VQA player definitions. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 30, 1995 +;* +;**************************************************************************** + +;*--------------------------------------------------------------------------- +;* CONDITIONAL COMPILATION FLAGS +;*--------------------------------------------------------------------------- + + IF PHARLAP_TNT + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 1 ;Mono display output enable/disable +VQAAUDIO_ON EQU 0 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 0 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ELSE + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 0 ;Mono display output enable/disable +VQAAUDIO_ON EQU 1 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 0 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ENDIF diff --git a/VQ/VQA32/VQAPLAYP.H b/VQ/VQA32/VQAPLAYP.H new file mode 100644 index 0000000..ed3a315 --- /dev/null +++ b/VQ/VQA32/VQAPLAYP.H @@ -0,0 +1,511 @@ +/* +** 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 . +*/ + +#ifndef VQAPLAYP_H +#define VQAPLAYP_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplayp.h +* +* DESCRIPTION +* VQAPlay private library definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* August 21, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include "vqafile.h" +#include "vqaplay.h" +#include "caption.h" + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Internal library version. */ +#define VQA_VERSION "2.41" +#define VQA_DATE __DATE__" "__TIME__ + +#define VQA_IDSTRING "VQA32 "VQA_VERSION" ("VQA_DATE")" +#define VQA_REQUIRES "VQM32 2.12 or better." + +/* Block dimensions macro and identifiers. */ +#define BLOCK_DIM(a,b) (((a&0xFF)<<8)|(b&0xFF)) +#define BLOCK_2X2 BLOCK_DIM(2,2) +#define BLOCK_2X3 BLOCK_DIM(2,3) +#define BLOCK_4X2 BLOCK_DIM(4,2) +#define BLOCK_4X4 BLOCK_DIM(4,4) + +/* Memory limits */ +#define VQA_MAX_CBBUFS 10 /* Maximum number of codebook buffers */ +#define VQA_MAX_FRAMEBUFS 30 /* Maximum number of frame buffers */ + +/* Special Constants */ +#define VQA_MASK_POINTER 0x8000 /* Pointer value to use for masking. */ + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* ChunkHeader: IFF chunk identifier header. + * + * id - 4 Byte chunk id. + * size - Size of chunk. + */ +typedef struct _ChunkHeader { + unsigned long id; + unsigned long size; +} ChunkHeader; + + +/* ZAPHeader: ZAP audio compression header. NOTE: If the uncompressed size + * and the compressed size are equal then the audio frame is RAW + * (NOT COMPRESSED). + * + * UnCompSize - Uncompressed size in bytes. + * CompSize - Compressed size in bytes. + */ +typedef struct _ZAPHeader { + unsigned short UnCompSize; + unsigned short CompSize; +} ZAPHeader; + + +/* VQACBNode: A circular list of codebook buffers, used by the load task. + * If the data is compressed, it is loaded into the end of the + * buffer and the compression flags is set. Otherwise the data + * is loaded into the start of the buffer. + * (Make sure this structure's size is always DWORD aligned.) + * + * Buffer - Pointer to Codebook data. + * Next - Pointer to next VQACBNode in the codebook list. + * Flags - Used by the drawer to tell if certain operations have been + * performed on this codebook, such as downloading to VRAM, + * or pre-scaling it. This field is cleared by the Loader when a + * new codebook is loaded. + * CBOffset - Offset into the buffer of the compressed data. + */ +typedef struct _VQACBNode { + unsigned char *Buffer; + struct _VQACBNode *Next; + unsigned long Flags; + unsigned long CBOffset; +} VQACBNode; + +/* VQACBNode flags */ +#define VQACBB_DOWNLOADED 0 /* Download codebook to VRAM (XMODE VRAM) */ +#define VQACBB_CBCOMP 1 /* Codebook is compressed */ +#define VQACBF_DOWNLOADED (1<. +*/ + +#ifndef VQMALL_H +#define VQMALL_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* all.h +* +* DESCRIPTION +* All VQMisc32 library definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* VQMALL_H */ + diff --git a/VQ/VQM32/AUDUNZAP.ASM b/VQ/VQM32/AUDUNZAP.ASM new file mode 100644 index 0000000..ea9a161 --- /dev/null +++ b/VQ/VQM32/AUDUNZAP.ASM @@ -0,0 +1,375 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* audunzap.asm +;* +;* DESCRIPTION +;* Audio uncompress (32-Bit protected mode) +;* +;* PROGRAMMER +;* Joe L. Bostic +;* Denzil E. Long, Jr. +;* +;* DATE +;* February 9, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* AudioUnzap - Uncompress zapped audio sample. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + +;**************************************************************************** +;* +;* NAME +;* AudioUnzap - Uncompress zapped audio sample. +;* +;* SYNOPSIS +;* Size = AudioUnzap(Source, Dest, Size) +;* +;* long AudioUnzap(void *, void *, long); +;* +;* FUNCTION +;* Decompress the zapped audio sample data into a buffer. +;* +;* INPUTS +;* Source - Pointer to encoded audio data. +;* Dest - Pointer to buffer to decompress into. +;* Size - Maximum size of dest buffer. +;* +;* RESULT +;* Size - Number of uncompressed bytes. +;* +;**************************************************************************** + + GLOBAL C AudioUnzap:NEAR + PROC AudioUnzap C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + mov [incount],0 ;Bytes read from source + +; Source, Dest and count must be valid. + +; cmp [source],0 +; je ??fini + +; cmp [dest],0 +; je ??fini + +; cmp [count],0 +; je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + cld + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popf + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popf + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popf + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popf + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popf + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popf + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popf + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popf + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popf + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popf + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popf + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popf + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + mov eax,[incount] + ret + + ENDP AudioUnzap + + END + diff --git a/VQ/VQM32/AUDZAP.CPP b/VQ/VQM32/AUDZAP.CPP new file mode 100644 index 0000000..2dc4a9a --- /dev/null +++ b/VQ/VQM32/AUDZAP.CPP @@ -0,0 +1,398 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Audzap.c +* +* DESCRIPTION +* Lossy audio compression. (32-Bit protected mode) +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* AudioZap - Compress audio sample data. +* +****************************************************************************/ + +#include +#include +#include "compress.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) + +typedef enum { + CODE_2BIT, /* Bit packed 2 bit delta. */ + CODE_4BIT, /* Nibble packed 4 bit delta. */ + CODE_RAW, /* Raw sample. */ + CODE_SILENCE /* Run of silence. */ +} SCodeType; + +char _2bitencode[5] = { + 0,1,2,3 +}; + +long _2bitdecode[4] = { + -2,-1,0,1 +}; + +char _4bitencode[19] = { + 0,1,2,2,3,4,5,6,7,(8), + 8,9,10,11,12,13,13,14,15 +}; + +long _4bitdecode[16] = { + -9,-8,-6,-5,-4,-3,-2, + -1,0,1,2,3,4,5,6,8 +}; + + +/**************************************************************************** +* +* NAME +* AudioZap - Compress audio sample data. +* +* SYNOPSIS +* Size = AudioZap(Source, Dest, Size) +* +* long AudioZap(void *, void *, long); +* +* FUNCTION +* NOTE - If the compressed size is equal to or greater than the original +* size then the data could not be compressed and the uncompressed data +* should be written. +* +* INPUTS +* Source - Pointer to buffer containing audio sample data. +* Dest - Pointer to buffer to put encoded data. +* Size - Number of bytes to compress. +* +* RESULT +* Size - Size in bytes of encoded data. +* +****************************************************************************/ + +long AudioZap(void *source, void *dest, long size) +{ + unsigned char *s = (unsigned char *)source; + unsigned char *d = (unsigned char *)dest; + long delta; + unsigned long previous = 0x80; + long outcount = 0; + unsigned char *s4; + unsigned long p4; + long max4; + unsigned char *lastraw = 0; + long osize = size; + unsigned long i; + unsigned long dd; + + /* Reduce very small amplitudes to silence. Usually a rather large + * portion of a sample is hovering around the silence value. This is + * due, in part, to the artifacting of the sample process. These low + * amplitudes are not audible. + */ + max4 = size; + s = (unsigned char *)source; + + while (size > 0 && outcount < osize) { + + /* First check for runs of zero deltas. If a run of at least + * any can be found, then output it. + */ + s4 = s; + max4 = MIN(63 + 1, size); + + for (i = 0; i < max4; i++) { + if (previous != *s4++) + break; + } + + /* When there is a code transition, terminate any run of raw + * samples. + */ + if (i > 2) { + lastraw = 0; + *d++ = ((i - 1)|(CODE_SILENCE << 6)); + outcount++; + s += i; + size -= i; + continue; + } + + /* If there are fewer than 4 samples remaining, then using delta + * compression is inefficient. Just drop into the raw routine + */ + if (size > 4) { + s4 = s; + p4 = previous; + + /* Find out the number of lossless 2 bit deltas available. These + * deltas are always present in quads. The compressed code is + * the delta quad count followed by the deltas in bit packed bytes. + */ + max4 = MIN(64L * 4L + 4L + 4L, size); + + for (i = 0; i < max4; i++) { + delta = (((int)*s4++) - p4); + + if ((delta < -2) || (delta > 1)) { + break; + } + + p4 += _2bitdecode[_2bitencode[delta + 2]]; + + if (((signed)p4) < 0) { + p4 = 0; + } + + if (((signed)p4) > 255) { + p4 = 255; + } + } + + i >>= 2; // Delta 2 always occur in quads -- force this. + + /* If there is the minimum benificial number of delta 2s available, + * then compress them. + */ + if (i) { + + /* When there is a code transition, terminate any run of raw + * samples. + */ + lastraw = 0; + + /* Output the delta 4 pair count. This is the number of pairs + * minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, (63 + 1)); + *d++ = ((i - 1)|(CODE_2BIT << 6)); + outcount++; + + for (dd = 0; dd < i; dd++) { + int delta1, delta2, delta3, delta4; + + delta1 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta1]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta2 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta2]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta3 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta3]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta4 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta4]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + *d++ = ((delta4 << 6)|(delta3 << 4)|(delta2 << 2)|delta1); + outcount++; + } + continue; + } else { + s4 = s; + p4 = previous; + + /* Find out the number of lossless 4 bit deltas follow. These + * deltas are always present in pairs. The compressed code is + * the delta pair count followed by the deltas in nibble packed + * bytes. + */ + max4 = MIN(64L * 2L + 4L + 4L, size); + + for (i = 0; i < max4; i++) { + delta = (((int)*s4++) - p4); + + if (delta < -9 || delta >= 9) { + break; + } + + p4 += _4bitdecode[_4bitencode[(delta + 9)]]; + + if (((signed)p4) < 0) { + p4 = 0; + } + + if (((signed)p4) > 255) { + p4 = 255; + } + } + + i >>= 1; // Delta 4 always occur in pairs -- force this. + + /* If there is the minimum benificial number of delta 4s available, + * then compress them. + */ + if (i) { + + /* When there is a code transition, terminate any run of raw + * samples. + */ + lastraw = 0; + + /* Output the delta 4 pair count. This is the number of pairs + * minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, (63 + 1)); + *d++ = ((i - 1)|(CODE_4BIT << 6)); + outcount++; + + for (dd = 0; dd < i; dd++) { + int delta1, delta2; + + delta1 = _4bitencode[((((int)*s++) - previous) + 9)]; + previous += _4bitdecode[delta1]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta2 = _4bitencode[((((int)*s++) - previous) + 9)]; + previous += _4bitdecode[delta2]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + *d++ = ((delta2 << 4)|(delta1 & 0x0F)); + outcount++; + } + continue; + } + } + } + + /* Raw output since deltas were unsuccessful. If this is a run + * of raw output, then merely tack it onto the run rather than + * create a new code sequence. + */ + if (lastraw) { + *lastraw = ((*lastraw) + 1); + + /* There is only so much a run code can accomodate. If the limit + * has been reached, then terminate this code. A new one will be + * created if necessary. + */ + if ((*lastraw & 0x1F) == 0x1F) { + lastraw = 0; + } + } else { + + /* If there is no current raw dump of samples, then check to see if + * this sample can fit into a 5 bit delta. If it can, then store + * it as such as a parasite to the "raw" code. This will save a byte + * for any stray 5 bit deltas that happen along. It is expected that + * this is more frequent than 6 or more bit deltas that would necessitate + * the use of the RAW code. + */ + delta = (((int)*s) - previous); + + if ((delta >= -16) && (delta <= 15)) { + lastraw = 0; + *d++ = ((CODE_RAW << 6)|0x20|(delta & 0x1F)); + outcount++; + previous = *s++; + size--; + continue; + } else { + lastraw = d; + *d++ = (CODE_RAW << 6); + outcount++; + } + } + + *d++ = previous = *s++; + size--; + outcount++; + } + + /* Check to see if the compression process actually resulted in smaller + * data size. In some cases, the 'compressed' data is actually larger. In + * this case, just output the raw frame. If the compressed and actual frame + * size match, then it is presumed that no compression occurs. + */ + if (outcount >= osize) { + memcpy(dest, source, (size_t)osize); + outcount = osize; + } + + return(outcount); +} + diff --git a/VQ/VQM32/BCC32.CFG b/VQ/VQM32/BCC32.CFG new file mode 100644 index 0000000..6bab081 --- /dev/null +++ b/VQ/VQM32/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vqm32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/VQ/VQM32/CAPTOKEN.CPP b/VQ/VQM32/CAPTOKEN.CPP new file mode 100644 index 0000000..c4dd511 --- /dev/null +++ b/VQ/VQM32/CAPTOKEN.CPP @@ -0,0 +1,458 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.c +* +* DESCRIPTION +* Tokenize a caption script for playback processing. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "captoken.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define STRING_LENGTH 256 +#define PADSIZE(size) (((size)+1)&(~1)) + +typedef enum { + TOKEN_NULL = 0, + TOKEN_BGCOL, + TOKEN_FGCOL, + TOKEN_XPOS, + TOKEN_YPOS, + TOKEN_ABS, + TOKEN_LEFT, + TOKEN_RIGHT, + TOKEN_CENTER, + TOKEN_FLASH, + TOKEN_CPF, + TOKEN_END +} TokenTag; + +typedef struct _Token { + char *name; + long tag; +} Token; + +/* Script tokens: + * BG/BGCOL - Background pen color (EX: BG=) + * FG/FGCOL - Foreground pen color (EX: FG=) + * X/XPOS - X pixel position of text (EX: X=) + * Y/YPOS - Y pixel position of text (EX: Y=) + * ABS/ABSOLUTE - Absolute justification + * LEFT - Left side justification + * RIGHT - Right side justification + * CENTER - Center justification + * FLASH - Enable flashing of text + * CPF - Print rate in characters per frame (EX: CPF=) + * END - Terminate compilation of caption script + */ +Token tokens[] = { + "BG", TOKEN_BGCOL, + "BGCOL", TOKEN_BGCOL, + "FG", TOKEN_FGCOL, + "FGCOL", TOKEN_FGCOL, + "X", TOKEN_XPOS, + "XPOS", TOKEN_XPOS, + "Y", TOKEN_YPOS, + "YPOS", TOKEN_YPOS, + "ABS", TOKEN_ABS, + "ABSOLUTE", TOKEN_ABS, + "LEFT", TOKEN_LEFT, + "RIGHT", TOKEN_RIGHT, + "CENTER", TOKEN_CENTER, + "FLASH", TOKEN_FLASH, + "CPF", TOKEN_CPF, + "END", TOKEN_END, + NULL, TOKEN_NULL, +}; + +Token colors[] = { + "BLACK", 0, + "WHITE", 251, + "RED", 252, + "GREEN", 253, + "SHADOW", 254, + "CYCLE", 255, + NULL, -1, +}; + +/* Prototypes. */ +static long GetColorNum(char *name); +static long IsNumeric(char *string); +static void FormatString(char *string); + + +/**************************************************************************** +* +* NAME +* BuildCaptions - Compile a caption script. +* +* SYNOPSIS +* Size = BuildCaptions(Name, Buffer) +* +* long BuildCaptions(char *, char *); +* +* FUNCTION +* Generate a compiled caption script for use in VQA playback. +* +* INPUTS +* Name - Name of caption script file to compile. +* Buffer - Buffer to put compiled captions into. +* +* RESULT +* Size - Size of compiled captions (in bytes). +* +****************************************************************************/ + +long BuildCaptions(char *name, char *buffer) +{ + FILE *fp; + char *ptr; + char *ptr1; + long size = 0; + long error; + long i; + long tag; + CaptionText caption; + char string[STRING_LENGTH]; + + /* Initialize the caption parameters. */ + memset(&caption, 0, sizeof(CaptionText)); + + /* Open the captions script file. */ + fp = fopen(name, "r"); + + if (fp != NULL) { + error = 0; + + while (!error) { + if (fgets(string, STRING_LENGTH, fp) == NULL) { + if (errno == 0) { + error = 1; + } + + break; + } + + /* Replace the newline with a NULL terminator. */ + string[strlen(string) - 1] = 0; + + /* Ignore comment lines. */ + if (string[0] == ';') continue; + + ptr = strtok(string, "="); + + if (ptr != NULL) { + + /* Check for a comma */ + ptr1 = strchr(ptr, ','); + + if (ptr1 != NULL) { + *ptr1++ = 0; + } + + /* Is this a frame number. */ + if (IsNumeric(ptr) && IsNumeric(ptr1)) { + i = atoi(ptr); + + /* Frames must be defined in ascending order. */ + if ((unsigned short)i >= caption.OnFrame) { + caption.OnFrame = i; + caption.OffFrame = atoi(ptr1); + caption.Size = sizeof(CaptionText); + + /* Get caption text. */ + ptr = strtok(NULL, ""); + + if (ptr != NULL) { + FormatString(ptr); + i = strlen(ptr) + 1; + caption.Size += PADSIZE(i); + size += caption.Size; + + /* Copy the caption structure. */ + memcpy(buffer, &caption, sizeof(CaptionText)); + buffer += sizeof(CaptionText); + + /* Copy the caption text. */ + memcpy(buffer, ptr, i); + buffer += i; + + /* WORD align */ + if (PADSIZE(i) > i) { + *buffer++ = 0; + } + } + } else { + error = 1; + break; + } + } else { + + /* Search for matching token. */ + tag = TOKEN_NULL; + i = 0; + + while (tokens[i].name != NULL) { + if (strcmpi(tokens[i].name, ptr) == 0) { + tag = tokens[i].tag; + break; + } + + i++; + } + + /* Get the data element. */ + ptr = strtok(NULL, ""); + + switch (tag) { + case TOKEN_BGCOL: + caption.BgPen = (char)GetColorNum(ptr); + break; + + case TOKEN_FGCOL: + caption.FgPen = (char)GetColorNum(ptr); + break; + + case TOKEN_XPOS: + caption.Xpos = (unsigned short)atoi(ptr); + break; + + case TOKEN_YPOS: + caption.Ypos = (unsigned short)atoi(ptr); + break; + + case TOKEN_ABS: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_ABS; + break; + + case TOKEN_LEFT: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_LEFT; + break; + + case TOKEN_RIGHT: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_RIGHT; + break; + + case TOKEN_CENTER: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_CENTER; + break; + + case TOKEN_FLASH: + if (strcmpi(ptr, "OFF") == 0) { + caption.Flags &= ~CTF_FLASH; + } else { + caption.Flags |= CTF_FLASH; + } + break; + + case TOKEN_CPF: + caption.CPF = (char)atoi(ptr); + break; + + /* Termination captions */ + case TOKEN_END: + caption.Size = sizeof(CaptionText); + caption.OnFrame = (unsigned short)-1; + memcpy(buffer, &caption, sizeof(CaptionText)); + buffer += sizeof(CaptionText); + break; + + default: + break; + } + } + } + } + + /* Close the script file. */ + fclose(fp); + } + + return (size); +} + + +/**************************************************************************** +* +* NAME +* GetColorNum - Get the color number from the color name. +* +* SYNOPSIS +* Color = GetColorNum(Name) +* +* long GetColorNum(char *); +* +* FUNCTION +* Look the color number that corresponds to the color name. +* +* INPUTS +* Name - Name of color. +* +* RESULT +* Color - Color number. +* +****************************************************************************/ + +static long GetColorNum(char *name) +{ + long color = -1; + long i; + + i = 0; + + /* Scan for a matching name and return the corresponding color number. */ + while (colors[i].name != NULL) { + if (strcmpi(colors[i].name, name) == 0) { + color = colors[i].tag; + } + + i++; + } + + return (color); +} + + +/**************************************************************************** +* +* NAME +* IsNumeric - Check if a string is numeric. +* +* SYNOPSIS +* Condition = IsNumeric(String) +* +* long IsNumeric(char *); +* +* FUNCTION +* Interogate the string to see if it represents a numeric value. Each +* byte of the string must be between 0x30 and 0x39 inclusively. +* +* INPUTS +* String - String to check. +* +* RESULT +* Condition - 1 if numeric, 0 if not. +* +****************************************************************************/ + +static long IsNumeric(char *string) +{ + long flag = 1; + + /* Ignore any proceeding sign designation. */ + if ((*string == '-') || (*string == '+')) { + string++; + } + + /* Check to see if every byte in the string is a digit. */ + while (flag && (*string != 0)) { + if (!isdigit(*string++)) { + flag = 0; + } + } + + return (flag); +} + + +/**************************************************************************** +* +* NAME +* FormatString - Parse any format codes in the string. +* +* SYNOPSIS +* FormatString(String) +* +* void FormatString(char *); +* +* FUNCTION +* Format a string with any embedded format commands contained in the +* input string. +* +* Supported format commands: +* /n - Insert carriage return. (0x0D) +* /r - Insert carriage return. (0x0D) +* // - Literal backslash. +* +* INPUTS +* String - Pointer to string to format. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FormatString(char *string) +{ + char *ptr; + + /* NULL strings are invalid. */ + if (string != NULL) { + ptr = string; + + /* Scan the string for embedded format commands. */ + while ((ptr = strchr(ptr, '/')) != NULL) { + switch (*(ptr + 1)) { + + /* Carriage return. */ + case 'n': + case 'r': + *ptr = 0x0D; + break; + + /* Literal backslash. */ + case '/': + break; + + default: + break; + } + + /* Remove the unwanted character. */ + strcpy((ptr + 1), (ptr + 2)); + } + } +} + diff --git a/VQ/VQM32/CAPTOKEN.H b/VQ/VQM32/CAPTOKEN.H new file mode 100644 index 0000000..17dc8e0 --- /dev/null +++ b/VQ/VQM32/CAPTOKEN.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +#ifndef VQMCAPTOKEN_H +#define VQMCAPTOKEN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.h +* +* DESCRIPTION +* Text caption script definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +/* CaptionText: This structure describes a line of text to be displayed + * with the video/audio. + * + * Size - Size of caption in bytes. + * OnFrame - Frame number to display caption. + * OffFrame - Frame number to clear caption. + * Flags - Display modifiers. + * CPF - Characters to draw per frame. + * Xpos - X pixel position to display caption. + * Ypos - Y pixel position to display caption. + * BgPen - Background pen to use. + * FgPen - Foreground pen to use. + * Text - Text string to display. (WORD aligned) + */ +typedef struct _CaptionText { + unsigned short Size; + unsigned short OnFrame; + unsigned short OffFrame; + unsigned char Flags; + char CPF; + unsigned short Xpos; + unsigned short Ypos; + char BgPen; + char FgPen; + char Text[]; +} CaptionText; + +/* CaptionText flag definitions. */ +#define CTF_JUSTIFY (3<<0) /* Justification field. */ +#define CTF_ABS (0<<0) /* Use absolute X,Y positions. */ +#define CTF_CENTER (1<<0) /* Justify on Center */ +#define CTF_LEFT (2<<0) /* Justify on left */ +#define CTF_RIGHT (3<<0) /* Justify on right */ +#define CTF_FLASH (1<<4) /* Flash text. */ + +/* Function prototypes. */ +long BuildCaptions(char *name, char *buffer); + +#endif /* VQMCAPTOKEN_H */ + diff --git a/VQ/VQM32/CHRWIDTH.CPP b/VQ/VQM32/CHRWIDTH.CPP new file mode 100644 index 0000000..daa41d8 --- /dev/null +++ b/VQ/VQM32/CHRWIDTH.CPP @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* chrwidth.c +* +* DESCRIPTION +* Character pixel width. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* June 9, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* Char_Pixel_Width - Get the pixel width of a character. +* +****************************************************************************/ + +#include "font.h" + + +/**************************************************************************** +* +* NAME +* Char_Pixel_Width - Get the pixel width of a character. +* +* SYNOPSIS +* Width = Char_Pixel_Width(Character) +* +* long Char_Pixel_Width(char); +* +* FUNCTION +* Gets the pixel width of the specified character. +* +* INPUTS +* Character - Character to get the width for. +* +* RESULT +* Width - Width in pixels. +* +****************************************************************************/ + +#ifdef __WATCOMC__ +long cdecl __saveregs Char_Pixel_Width(char chr) +#else +#pragma saveregs +long cdecl Char_Pixel_Width(char chr) +#endif +{ + return (*(FontWidthBlockPtr + chr) + FontXSpacing); +} diff --git a/VQ/VQM32/COMPRESS.H b/VQ/VQM32/COMPRESS.H new file mode 100644 index 0000000..980d0ed --- /dev/null +++ b/VQ/VQM32/COMPRESS.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMCOMP_H +#define VQMCOMP_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* compress.h +* +* DESCRIPTION +* Compression definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long cdecl LCW_Compress(char const *source, char *dest, + unsigned long length); +unsigned long cdecl LCW_Uncompress(char const *source, char *dest, + unsigned long length); +long AudioZap(void *source, void *dest, long size); +long cdecl AudioUnzap(void *source, void *dest, long); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCOMP_H */ + diff --git a/VQ/VQM32/CRC.ASM b/VQ/VQM32/CRC.ASM new file mode 100644 index 0000000..b7443af --- /dev/null +++ b/VQ/VQM32/CRC.ASM @@ -0,0 +1,133 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* crc.asm +;* +;* DESCRIPTION +;* CRC checksum calculation. +;* +;* PROGRAMMER +;* Joe L. Bostic +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Calculate_CRC - Calculate CRC checksum. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Calculate_CRC - Calculate CRC checksum. +;* +;* SYNOPSIS +;* CRC = Calculate_CRC(Buffer, Length) +;* +;* long Calculate_CRC(void *, long); +;* +;* FUNCTION +;* Compute a CRC checksum for a block of memory. +;* +;* INPUTS +;* Buffer - Pointer to buffer to calculate CRC for. +;* Length - Length of buffer. +;* +;* RESULT +;* CRC - CRC value. +;* +;**************************************************************************** + + GLOBAL C Calculate_CRC:NEAR + PROC Calculate_CRC C NEAR USES esi ebx ecx edx + ARG buffer:NEAR PTR + ARG length:DWORD + + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + mov ecx,[length] ;Get length of data block + or ecx,ecx + jz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + or ecx,ecx + jz short ??remainder + +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov eax,ebx + ret + + ENDP Calculate_CRC + + END diff --git a/VQ/VQM32/CRC.H b/VQ/VQM32/CRC.H new file mode 100644 index 0000000..d70e018 --- /dev/null +++ b/VQ/VQM32/CRC.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef VQMCRC_H +#define VQMCRC_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* CRC.h +* +* DESCRIPTION +* CRC calculation definitions. +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl Calculate_CRC(void const *buffer, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCRC_H */ diff --git a/VQ/VQM32/DRAWCHAR.ASM b/VQ/VQM32/DRAWCHAR.ASM new file mode 100644 index 0000000..3791de9 --- /dev/null +++ b/VQ/VQM32/DRAWCHAR.ASM @@ -0,0 +1,395 @@ +; +; 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 . +; + +; $Header: U:\vq\projects\vqm32\drawchar.asv 1.1 08 May 1995 10:48:32 DENZIL_LONG $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : DRAWCHAR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +XPIXEL_MAX EQU 320 +YPIXEL_MAX EQU 200 + +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + EXTRN FontPtr:NEAR PTR + +;*********************************************************** +; DRAW_CHAR +; +; VOID Draw_Char(BYTE fontchar, WORD x_pixel, WORD y_pixel); +; +; Draws a character to the screen only if given coordinates that will allow +; the entire character to be drawn on the screen else will exit. +; +; NOTE: This is a routine called by Text_Print. +; +;* + GLOBAL C Draw_Char:NEAR + PROC Draw_Char C NEAR USES eax ebx ecx edx esi edi + ARG fontchar:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + + LOCAL infoblock:DWORD + LOCAL offsetblock:DWORD + LOCAL widthblock:DWORD + LOCAL heightblock:DWORD + LOCAL fwidth:DWORD + LOCAL nextline:DWORD + LOCAL startv:BYTE + LOCAL dheight:BYTE + LOCAL wheight:BYTE + + mov esi,[FontPtr] + + ; Set up some working local variables. + + xor eax,eax + mov ax,[esi+FONTINFOBLOCK] ; get offset to info block + mov [infoblock],eax ; save offset to info block + mov ax,[esi+FONTOFFSETBLOCK] ; get offset to offset block + mov [offsetblock],eax ; save offset to offset block + mov ax,[esi+FONTWIDTHBLOCK] ; get offset to width block + mov [widthblock],eax ; save offset to width block + mov ax,[esi+FONTHEIGHTBLOCK] ; get offset to height block + mov [heightblock],eax ; save offset to height block + + ; Fetch character data offset -- if NULL then undefined character. + + mov ebx,[fontchar] + and ebx,0FFh + shl ebx,1 ; make word index + add ebx,[offsetblock] ; add offset to offset block + xor ecx,ecx + mov cx,[esi+ebx] ; load offset to font data + or ecx,ecx + jz ??exit ; is this character a null? if so exit + + ; If the character is off the left/right edge of the screen then abort. + + mov edx,[x_pixel] + cmp edx,XPIXEL_MAX + jae ??exit + + ; If the character is off the top/bottom edge of the screen then abort. + + mov ebx,[fontchar] ; get char + and ebx,0FFh + add ebx,[widthblock] ; add offset to width block + xor eax,eax + mov al,[esi+ebx] ; get width for character + mov [fwidth],eax ; save char width + add eax,edx ; ax = char len + x + cmp eax,XPIXEL_MAX + ja ??exit + + mov edi,edx ; save xpos in di + + mov edx,[y_pixel] + cmp edx,YPIXEL_MAX + jae ??exit + + mov ebx,[infoblock] ; get offset to offset block + xor eax,eax + ; get font max height from info block + mov al,[esi+ebx+FONTINFOMAXHEIGHT] + mov [wheight],al ; save max height of character + add eax,edx ; add height to y pos + cmp eax,YPIXEL_MAX ; will it go off the bottom + ja ??exit + +??vdraw: + mov ebx,[fontchar] ; get char + and ebx,0FFh + shl ebx,1 ; make 2 byte index + add ebx,[heightblock] ; add offset to height block + mov ah,[esi+ebx] ; get start vertical for character + mov [startv],ah ; save start vertical for later + mov al,[esi+ebx+1] ; get data height for character + mov [dheight],al ; save data height for later + add ah,al ; add background and data + sub [wheight],ah ; remaining background height + + add esi,ecx ; add font offset to font data + + push edx + mov eax,XPIXEL_MAX + mul edx + add edi,eax + pop edx + + mov eax,XPIXEL_MAX + sub eax,[fwidth] + mov [nextline],eax ; ?? to add to index for the nextline + + add edi,0A0000h + mov ebx,OFFSET ColorXlat ; setup up bx for xlat commands + xor ecx,ecx + mov cl,[startv] ; number of scan lines that are + ; background color + or ecx,ecx ; if starting vertical is zero + je short ??skiplead ; skip drawing top background lines + + mov al,0 + xlat [ebx] ; get background color + + or al,al ; check for none zero color + jne short ??lheight ; update background color + + push edx + mov eax,XPIXEL_MAX + mul ecx + add edi,eax + pop edx + + mov ebx,OFFSET ColorXlat ; restore bx for xlat commands + jmp SHORT ??skiplead + +??lheight: + mov edx,[fwidth] ; width of char + +??lwidth: + stosb ; write out line of pixels for width + dec edx + jne ??lwidth + +??lnext: + add edi,[nextline] ; goto next line at the start of char + loop ??lheight ; any more lines + +??skiplead: + mov cl,[dheight] ; number of scan lines that are data + or ecx,ecx ; is there any data to be drawn + je short ??exit + +??vheight: + mov edx,[fwidth] ; width of char + +??vwidth: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and al,00FH ; get lonibble + xlat [ebx] ; get new color + or al,al + je short ??chklowidth ; skip color zero + + mov [edi],al ; write out pixel of lonibble + +??chklowidth: + inc edi + dec edx + je short ??vnext ; check if done with width of char + + mov al,ah ; get byte value + and al,0F0H ; get hinibble + xlat [ebx] ; get new color + or al,al + je short ??chkhiwidth ; skip color zero + + mov [edi],al ; write out pixel of hinibble + +??chkhiwidth: + inc edi + dec edx + jne ??vwidth ; check if done with width of char + +??vnext: + add edi,[nextline] ; next line at start of char + loop ??vheight ; any more lines + + +??trail: + mov cl,[wheight] ; remaining height of background color + or ecx,ecx ; if trailing height is zero + jle short ??exit ; skip drawing bottom background lines + + mov al,0 + xlat [ebx] ; get background color + or al,al ; check for color zero + je short ??exit ; skip drawing + +??theight: + mov edx,[fwidth] ; width of char + +??twidth: + stosb ; write out line of pixels for width + dec edx + jne ??twidth + +??tnext: + add edi,[nextline] ; next line at start of char + loop ??theight ; any more lines + + +??exit: + ret + + ENDP Draw_Char + +;*********************************************************** + + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + GLOBAL C Set_Font_Palette_Range:NEAR + PROC Set_Font_Palette_Range C NEAR USES eax ebx ecx edi esi + ARG palette:NEAR PTR + ARG start:DWORD + ARG endval:DWORD + + cld + mov esi,[palette] + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + loop ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** +; DRAW_CHAR_SETUP +; +; VOID Draw_Char_Setup(VOID); +; +; This routine sets up code segment variables for Draw_Char. +; +; NOTE: This is a routine called by Set_Font. +; +;* + GLOBAL C Draw_Char_Setup:NEAR + PROC Draw_Char_Setup C NEAR + + ret + + ENDP Draw_Char_Setup + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + +;*********************************************************** + END + diff --git a/VQ/VQM32/FILLRECT.ASM b/VQ/VQM32/FILLRECT.ASM new file mode 100644 index 0000000..405d761 --- /dev/null +++ b/VQ/VQM32/FILLRECT.ASM @@ -0,0 +1,216 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* fillrect.asm +;* +;* DESCRIPTION +;* Rectangle rendering. +;* +;* PROGRAMMER +;* Joe L. Bostic +;* +;* DATE +;* Febuary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Fill_Rect - Draw a filled rectangle. +;* Eor_Region - Hilights or unhilights a region by EOR it. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Eor_Region - Hilights or unhilights a region by EOR it. +;* +;* SYNOPSIS +;* Eor_Region(X1, Y1, X2, Y2, Color) +;* +;* void Eor_Region(long, long, long, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* X1 - Starting X position. +;* Y1 - Starting Y position. +;* X2 - Ending X position. +;* Y2 - Ending Y position. +;* Color - Color to EOR. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Eor_Region:NEAR + PROC Eor_Region C NEAR USES eax ebx ecx edx edi + ARG x1_pixel:DWORD + ARG y1_pixel:DWORD + ARG x2_pixel:DWORD + ARG y2_pixel:DWORD + ARG color:DWORD + LOCAL eorit:DWORD + + mov [eorit],1 + jmp short Fill_Rect_Entry + + ENDP Eor_Region + + +;**************************************************************************** +;* +;* NAME +;* Fill_Rect - Draw a filled rectangle. +;* +;* SYNOPSIS +;* Fill_Rect(X1, Y1, X2, Y2, Color) +;* +;* void Fill_Rect(long, long, long, long, long); +;* +;* FUNCTION +;* Fill a rectangle area with a specified color. +;* +;* INPUTS +;* X1 - Starting X position. +;* Y1 - Starting Y position. +;* X2 - Ending X position. +;* Y2 - Ending Y position. +;* Color - Color to draw rectangle +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Fill_Rect:NEAR + PROC Fill_Rect C NEAR USES eax ebx ecx edx edi + ARG x1_pixel:DWORD + ARG y1_pixel:DWORD + ARG x2_pixel:DWORD + ARG y2_pixel:DWORD + ARG color:DWORD + LOCAL eorit:DWORD + + mov [eorit],0 + +Fill_Rect_Entry: + cld ; always go forward + mov edi,0A0000h + + ; Verify bounds of x1_pixel. + mov eax,[x1_pixel] + cmp eax,320 ;XPIXEL_MAX + jae short ??exit + + ; Verify bounds of x2_pixel. + mov ebx,[x2_pixel] + cmp ebx,320 ;XPIXEL_MAX + jae short ??exit + + ; Backward rectangles are legal -- adjust for it. + cmp eax,ebx + jbe short ??okx + xchg eax,ebx +??okx: + + ; Verify bounds of y1_pixel. + mov ecx,[y1_pixel] + cmp ecx,200 ;YPIXEL_MAX + jae short ??exit + + ; Verify bounds of y2_pixel. + mov edx,[y2_pixel] + cmp edx,200 ;YPIXEL_MAX + jae short ??exit + + ; Backward rectangles are legal -- adjust for it. + cmp ecx,edx + jbe short ??oky + xchg ecx,edx +??oky: + + ; Set DX for height and BX for width. + sub edx,ecx + inc edx + sub ebx,eax + inc ebx + + ; Adjust DI to match offset into page of upper left corner. + push edx + push eax + mov eax,320 ;XPIXEL_MAX + mul ecx + add edi,eax ; Add in Y offset. + pop edx + add edi,edx ; Add in X offset. + pop edx + + ; Fill the region with the specified color. + mov eax,320 ;XPIXEL_MAX + sub eax,ebx + xchg eax,[color] + mov ah,al + cmp [eorit],0 + je short ??loop + +;------ EOR rectangle filling. +??loop2: + mov ecx,ebx ; Length of row +??loop2in: + lodsb + xor al,ah + stosb + loop ??loop2in + add edi,[color] ; Modulo add for next line prep. + dec edx + jg short ??loop2 + jmp short ??exit + +;------ Conventional rectangle filling. +??loop: + mov ecx,ebx ; Length of row + shr ecx,1 + rep stosw + adc ecx,0 + rep stosb + add edi,[color] ; Modulo add for next line prep. + dec edx + jg short ??loop + +??exit: + ret + + ENDP Fill_Rect + + END + diff --git a/VQ/VQM32/FONT.CPP b/VQ/VQM32/FONT.CPP new file mode 100644 index 0000000..227cc43 --- /dev/null +++ b/VQ/VQM32/FONT.CPP @@ -0,0 +1,222 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.c +* +* DESCRIPTION +* Font manipulation. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* Load_Font - Open a font for use. +* Set_Font - Set the default system font. +* String_Pixel_Width - Get the pixel width of a string. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "font.h" + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* min and max macros */ +#ifdef __cplusplus +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#endif + +void const *FontPtr = NULL; +char FontHeight = 8; +char FontWidth = 8; +int FontXSpacing = 0; +int FontYSpacing = 0; +char *FontWidthBlockPtr = NULL; + + +/**************************************************************************** +* +* NAME +* Load_Font - Open a font for use. +* +* SYNOPSIS +* Font = Load_Font(Name) +* +* char *Load_Font(char *); +* +* FUNCTION +* Open a graphics font for use by Text_Print(). Use free() to dispose +* of the font. +* +* INPUTS +* Name - Name of font file to open. +* +* RESULT +* Font - Pointer to font, NULL if error. +* +****************************************************************************/ + +void *cdecl Load_Font(char const *name) +{ + Font *font = NULL; + long fh; + short size; + short valid; + + /* Open the font. */ + if ((fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + + /* Get the size of the font. */ + if (read(fh, &size, 2) == 2) { + + /* Allocate memory to contain the font. */ + if ((font = (Font *)malloc((unsigned long)size)) != NULL) { + valid = 0; + + /* Read in the body of the font. */ + if (read(fh, &font->CompMethod, (unsigned long)(size - 2)) + == (unsigned long)(size - 2)) { + + /* Verify the validity of the font. */ + if ((font->CompMethod == 0) && (font->NumBlks == 5)) { + font->Size = size; + valid = 1; + } + } + + /* Free the font if it is not valid. */ + if (valid == 0) { + free(font); + font = NULL; + } + } + } + + /* Close the font. */ + close(fh); + } + + return ((char *)font); +} + + +/**************************************************************************** +* +* NAME +* Set_Font - Set the default system font. +* +* SYNOPSIS +* OldFont = Set_Font(Font) +* +* char *Set_Font(char *); +* +* FUNCTION +* Sets up the specified font as the default font used by the system. +* +* INPUTS +* Font - Pointer to Font to set as default. (NULL returns current font) +* +* RESULT +* OldFont - Previous font. +* +****************************************************************************/ + +void *cdecl Set_Font(void const *font) +{ + void const *oldfont; + FontInfo *fi; + + oldfont = FontPtr; + + if (font != NULL) { + FontWidthBlockPtr = ((char *)font + ((Font *)font)->WidthBlk); + fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk); + FontHeight = fi->MaxHeight; + FontWidth = fi->MaxWidth; + FontPtr = font; + } + + return ((void *)oldfont); +} + + +/**************************************************************************** +* +* NAME +* String_Pixel_Width - Get the pixel width of a string. +* +* SYNOPSIS +* Width = String_Pixel_Width(String) +* +* long String_Pixel_Width(char *); +* +* FUNCTION +* Calculates the pixel width of a string of characters. +* +* INPUTS +* String - Pointer to string to calculate width for. +* +* RESULT +* Width - Width of string in pixels. +* +****************************************************************************/ + +unsigned short String_Pixel_Width(char const *string) +{ + long width = 0; + long largest = 0; + + while (*string != NULL) { + if (*string == '\r') { + string++; + largest = max(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); + } + } + + largest = max(largest, width); + + return (largest); +} + diff --git a/VQ/VQM32/FONT.H b/VQ/VQM32/FONT.H new file mode 100644 index 0000000..f0c039a --- /dev/null +++ b/VQ/VQM32/FONT.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMFONT_H +#define VQMFONT_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.h +* +* DESCRIPTION +* Font definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +****************************************************************************/ + +/* Font: A Westwood style font. + * + * Size - Size of font. + * CompMethod - Compression method of font. (0 = none) + * NumBlks - Number of data blocks. + * InfoBlk - Offset to font information block. + * OffsetBlk - Offset to character offset block. + * WidthBlk - Offset to character width block. + * DataBlk - Offset to character data block. + * HeightBlk - Offset to character height block. + */ +typedef struct _Font { + unsigned short Size; + unsigned char CompMethod; + unsigned char NumBlks; + unsigned short InfoBlk; + unsigned short OffsetBlk; + unsigned short WidthBlk; + unsigned short DataBlk; + unsigned short HeightBlk; +} Font; + +typedef struct _FontInfo { + long huh; + unsigned char MaxHeight; + unsigned char MaxWidth; +} FontInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void const *FontPtr; +extern int FontXSpacing; +extern int FontYSpacing; + +#ifdef __cplusplus +} +#endif + +extern char FontWidth; +extern char FontHeight; +extern char *FontWidthBlockPtr; + +/* Function prototypes. */ +void *cdecl Load_Font(char const *name); +void *cdecl Set_Font(void const *font); +unsigned short cdecl String_Pixel_Width(char const *string); + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __WATCOMC__ +long cdecl __saveregs Char_Pixel_Width(char chr); +#else +long cdecl Char_Pixel_Width(char chr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VQMFONT_H */ + diff --git a/VQ/VQM32/GRAPHICS.H b/VQ/VQM32/GRAPHICS.H new file mode 100644 index 0000000..98bca7d --- /dev/null +++ b/VQ/VQM32/GRAPHICS.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +#ifndef VQMGRAPHICS_H +#define VQMGRAPHICS_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* graphics.h +* +* DESCRIPTION +* Graphic rendering and manipulation definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 27, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Eor_Region(long sx, long sy, long dx, long dy, long color); +void cdecl Fill_Rect(long x1, long y1, long x2, long y2, long color); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMGRAPHICS_H */ + diff --git a/VQ/VQM32/HUFFCMP.CPP b/VQ/VQM32/HUFFCMP.CPP new file mode 100644 index 0000000..e3ab1d5 --- /dev/null +++ b/VQ/VQM32/HUFFCMP.CPP @@ -0,0 +1,708 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffcmp.h +* +* DESCRIPTION +* Huffman order 0 compressor. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* HuffCompress - Compress data using huffman order 0 compression. +* HuffCount - Count the frequency of occurence of every byte. +* HuffScaleCounts - Scale down the frequency counts. +* RLEHuffCounts - Run Length Encode the Huffman counts. +* ConvertToCodes - Convert the Huffman tree into a table of codes. +* HuffEncode - Huffman encode the data. +* +****************************************************************************/ + +#include +#include "huffman.h" + + +/**************************************************************************** +* +* NAME +* HuffCompress - Compress data using huffman order 0 compression. +* +* SYNOPSIS +* Size = HuffCompress(Data, Buffer, Size, Temp) +* +* long HuffCompress(unsigned char *, unsigned char *, long, char *); +* +* FUNCTION +* This function performs an order 0 Huffman encoding of the input data. +* The algorithm used is fairly straightforward. First a count is made of +* all the bytes in the input data, then the counts are scaled down to +* a single byte representation in the node array. After the counts are +* scaled, a Huffman decoding tree is built from the node array. Then +* a code array is built by traversing the tree for each symbol. Finally, +* the input data is compressed. +* +* INPUTS +* Data - Pointer to data to compress. +* Buffer - Pointer to compressed data. +* Size - Length of data to compress. +* Temp - Pointer to temporary working buffer. (Must be >= 5120 bytes!) +* +* RESULT +* Size - Compressed size. +* +****************************************************************************/ + +long cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *temp) +{ + #if(1) + TreeNode *nodes; + HuffCode *codes; + long size; + long root; + + /* Initialize variables */ + nodes = (TreeNode *)temp; + temp += (514 * sizeof(TreeNode)); + codes = (HuffCode *)temp; + + /* Analyze the frequency of the data. */ + HuffCount(data, nodes, length, 1); + HuffScaleCounts(nodes); + + /* Save the counts for the decompression. */ + size = RLEHuffCounts(nodes, buffer); + buffer += size; + + /* Build the Huffman decode tree and generate codes for encoding. */ + root = BuildHuffTree(nodes); + ConvertToCodes(nodes, codes, 0, 0, root); + + /* Encode the data. */ + size += HuffEncode(data, buffer, codes, length); + + return (size); + #else + TreeNode *nodes; + HuffCode *codes; + unsigned long *counts; + unsigned long max_count; + long i; + long size; + long first; + long last; + long next; + unsigned long symbol; + unsigned long mask; + + /* Initialize variables. */ + nodes = (TreeNode *)temp; + temp += (514 * sizeof(TreeNode)); + counts = (unsigned long *)temp; + codes = (HuffCode *)temp; + + /* Zero the initial counts. */ + memset(temp, 0, 256 * sizeof(unsigned long)); + size = 0; + + /*------------------------------------------------------------------------- + * Calculate the distribution of the data then scale down the counts so + * they fit in an 8 bit value. This is done in order to limit the size of + * the codes to 16 bits. + *-----------------------------------------------------------------------*/ + i = 0; + + while (i < length) { + counts[((unsigned char *)data)[i]]++; + i++; + } + + /* Scale down the counts. */ + max_count = 0; + + /* Find the maximum count. */ + for (i = 0; i < 256; i++) { + if (counts[i] > max_count) { + max_count = counts[i]; + } + } + + if (max_count == 0) { + counts[0] = 1; + max_count = 1; + } + + max_count /= 255; + max_count++; + + /* Scale down the counts. */ + for (i = 0; i < 256; i++) { + nodes[i].count = (unsigned long)(counts[i] / max_count); + + /* Make sure that a node with a non-zero count does not get scaled + * down to zero. + */ + if ((nodes[i].count == 0) && (counts[i] != 0)) { + nodes[i].count = 1; + } + } + + nodes[HUFF_EOS].count = 1; + + /*------------------------------------------------------------------------- + * OUTPUT THE COUNTS + * + * In order to save space, we store runs of counts in the following format: + * + * Start, Stop, Counts..., Start, Stop, Counts..., ..., 0 + * + * The list is terminated by storing a start value of zero (0). + * + * In order to efficiently use this format, we do not want to stop a run + * because of just one or two zero counts. So we include zero counts of + * less than three (3) in the run. + *-----------------------------------------------------------------------*/ + + /* Find the first occurance of a non-zero count. */ + first = 0; + + while ((first < 255) && (nodes[first].count == 0)) { + first++; + } + + /* Each time I hit the start of the loop, I assume that first is the + * number for a run of non-zero values. The rest of the loop is + * concerned with finding the value for last, which is the end of the + * run, and the value of next, which is the start of the next run. + * At the end of the loop, I assign next to first, so it starts in on + * the next run. + */ + for (; first < 256; first = next) { + last = first + 1; + + for (;;) { + + /* Find the end of the run of non-zeros. */ + for (; last < 256; last++) { + if (nodes[last].count == 0) { + break; + } + } + + last--; + + /* Check the beginning of the next run of non-zero counts. */ + for (next = last + 1; next < 256; next++) { + if (nodes[next].count != 0) { + break; + } + } + + /* Quit the run if we have reached the end. */ + if (next > 255) { + break; + } + + /* Quit the run if there is more than three non-zero counts. */ + if ((next - last) > 3) { + break; + } + + last = next; + }; + + /* Output Start and Stop. */ + *buffer++ = first; + *buffer++ = last; + + /* Output the run of counts. */ + for (i = first; i <= last; i++) { + *buffer++ = (char)nodes[i].count; + } + + size += (((last - first) + 1) + 2); + } + + /* Output terminator. */ + *buffer++ = 0; + size++; + + /*------------------------------------------------------------------------- + * Build the Huffman tree. All active nodes are scanned in order to locate + * the two nodes with the minimum weights. These two weights are added + * together and assigned to a new node. The new node makes the two minimum + * nodes into its 0 child and 1 child. The two minimum nodes are then + * marked as inactive. This process repeats until their is only one node + * left, which is the root node. + *-----------------------------------------------------------------------*/ + + /* Node 513 is used to arbitratilly provide a node with a guaranteed + * maximum value. + */ + nodes[513].count = 0xFFFF; + + for (next = (HUFF_EOS + 1); ; next++) { + first = 513; + last = 513; + + for (i = 0; i < next; i++) { + + /* We are only concerned with non-zero count nodes. */ + if (nodes[i].count != 0) { + if (nodes[i].count < nodes[first].count) { + last = first; + first = i; + } else if (nodes[i].count < nodes[last].count) { + last = i; + } + } + } + + if (last == 513) { + break; + } + + nodes[next].count = (nodes[first].count + nodes[last].count); + nodes[first].count = 0; + nodes[last].count = 0; + nodes[next].child0 = (first << 3); + nodes[next].child1 = (last << 3); + } + + next--; + + /*------------------------------------------------------------------------- + * Convert the Huffman tree into an encoding table then encode the data. + *-----------------------------------------------------------------------*/ + ConvertToCodes(nodes, codes, 0, 0, (next << 3)); + + /* Encode the data. */ + *buffer = 0; + next = 0x80; + i = 0; + + do { + if (i < length) { + symbol = ((unsigned char *)data)[i]; + } else { + symbol = HUFF_EOS; + } + + mask = 1L << (codes[symbol].bits - 1); + + while (mask != 0) { + + /* Set a bit in the output stream for each bit in the code. */ + if (mask & codes[symbol].code) { + *buffer |= next; + } + + /* Next bit position. */ + next >>= 1; + + /* Advance to the next byte in the output stream when the current + * byte is full. + */ + if (next == 0) { + buffer++; + *buffer = 0; + next = 0x80; + size++; + } + + /* Next bit in the code. */ + mask >>= 1; + } + + i++; + } while (symbol != HUFF_EOS); + + if (next != 0x80) { + size++; + } + + return (size); + #endif +} + + +/**************************************************************************** +* +* NAME +* HuffCount - Count the frequency of occurence of every byte. +* +* SYNOPSIS +* HuffCount(Data, Nodes, Length, Zero) +* +* void HuffCounts(unsigned char *, TreeNode *, long, long); +* +* FUNCTION +* This function counts the frequency of occurence of every byte in the +* input data. The nodes must be initialized to zero prior to calling this +* function for the first time, otherwise the counts will be flawed. +* +* INPUTS +* Data - Pointer to data to analyze. +* TreeNode - Pointer to array of nodes. +* Length - Length of data to analyze. +* Zero - Zero any previous counts flag. (TRUE = zero counts) +* +* RESULT +* Size - Amount of buffer used to hold counts. +* +****************************************************************************/ + +void cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero) +{ + long i; + + /* Zero any previous counts. */ + if (zero) { + for (i = 0; i < 256; i++) { + nodes[i].count = 0; + } + } + + /* Calculate the distribution of the data. */ + i = 0; + + while (i < length) { + nodes[((unsigned char *)data)[i]].count++; + i++; + } +} + + +/**************************************************************************** +* +* NAME +* HuffScaleCounts - Scale down the frequency counts. +* +* SYNOPSIS +* HuffScaleCounts(Nodes) +* +* void HuffScaleCounts(TreeNode *); +* +* FUNCTION +* In order to limit the size of the Huffman codes to 16 bits, we must +* scale down the counts so they can be represented by a BYTE size value. +* +* INPUTS +* Nodes - Pointer to nodes to scale counts for. +* +* RESULT +* NONE +* +****************************************************************************/ + +void cdecl HuffScaleCounts(TreeNode *nodes) +{ + unsigned long max_count; + unsigned long unscaled; + long i; + long first; + long last; + long next; + + /* Scale down the counts so they fit in an 8 bit value. This is done in + * order to limit the size of the codes to 16 bits. + */ + max_count = 0; + + /* Find the maximum count. */ + for (i = 0; i < 256; i++) { + if (nodes[i].count > max_count) { + max_count = nodes[i].count; + } + } + + if (max_count == 0) { + nodes[0].count = 1; + max_count = 1; + } + + max_count /= 255; + max_count++; + + /* Scale down the counts. */ + for (i = 0; i < 256; i++) { + unscaled = nodes[i].count; + nodes[i].count /= max_count; + + /* Make sure that a node with a non-zero count does not get scaled + * down to zero. + */ + if ((nodes[i].count == 0) && (unscaled != 0)) { + nodes[i].count = 1; + } + } + + nodes[HUFF_EOS].count = 1; +} + + +/**************************************************************************** +* +* NAME +* RLEHuffCounts - Run Length Encode the Huffman counts. +* +* SYNOPSIS +* Size = RLEHuffCounts(Nodes, Buffer) +* +* long RLEHuffCounts(TreeNode *, unsigned char *); +* +* FUNCTION +* In order for the decoder to build the same model, we have to transmit +* the symbol counts to it. To save space we do not save all 256 symbols +* unconditionally, instead we run length encode the counts. The format +* used to store the counts is as follows: +* +* Start, Stop, Counts[n], Start, Stop, Counts[n], .... 0 +* +* Note: The sequence is terminated by a start value of 0. Also at least +* 1 run of counts has to be stored, even if the first start value is 0. +* +* INPUTS +* Nodes - Pointer to initialized nodes. +* Buffer - Pointer to buffer to store RLE'd counts. +* +* RESULT +* Size - Size of the RLE'd counts. +* +****************************************************************************/ + +long cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer) +{ + long i; + long first; + long last; + long next; + long size = 0; + + /* Find the first occurance of a non-zero count. */ + first = 0; + + while ((first < 255) && (nodes[first].count == 0)) { + first++; + } + + /* Each time I hit the start of the loop, I assume that first is the + * number for a run of non-zero values. The rest of the loop is + * concerned with finding the value for last, which is the end of the + * run, and the value of next, which is the start of the next run. + * At the end of the loop, I assign next to first, so it starts in on + * the next run. + */ + for (; first < 256; first = next) { + last = first + 1; + + for (;;) { + + /* Find the end of the run of non-zeros. */ + for (; last < 256; last++) { + if (nodes[last].count == 0) { + break; + } + } + + last--; + + /* Check the beginning of the next run of non-zero counts. */ + for (next = last + 1; next < 256; next++) { + if (nodes[next].count != 0) { + break; + } + } + + /* Quit the run if we have reached the end. */ + if (next > 255) { + break; + } + + /* Quit the run if there is more than three non-zero counts. */ + if ((next - last) > 3) { + break; + } + + last = next; + }; + + /* Output Start and Stop. */ + *buffer++ = first; + *buffer++ = last; + + /* Output the run of counts. */ + for (i = first; i <= last; i++) { + *buffer++ = (unsigned char)nodes[i].count; + } + + size += (((last - first) + 1) + 2); + } + + /* Output terminator. */ + *buffer = 0; + size++; + + return (size); +} + + +/**************************************************************************** +* +* NAME +* ConvertToCodes - Convert the Huffman tree into a table of codes. +* +* SYNOPSIS +* ConvertToCodes(Nodes, Codes, Code, Bits, Node) +* +* void ConvertToCodes(TreeNode *, HuffCode *, unsigned short, short, +* short); +* +* FUNCTION +* Since the Huffman tree is built as a decoding tree, there is no simple +* way to get the encoding values for each symbol. This routine +* recursively walks through the tree, adding the child bits to each code +* until it gets to a leaf. When it gets to a leaf, it stores the code +* value. +* +* INPUTS +* Nodes - Pointer to the Huffman tree. +* Codes - Pointer to the table of codes to generate. +* Code - Code being built (initialize with 0). +* Bits - Number of bits the code is comprised of (initialize with 0). +* Node - Number of the current node. +* +* RESULT +* NONE +* +****************************************************************************/ + +void cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node) +{ + node >>= 3; + + if (node <= HUFF_EOS) { + codes[node].code = code; + codes[node].bits = bits; + return; + } + + code <<= 1; + bits++; + ConvertToCodes(nodes, codes, code, bits, nodes[node].child0); + ConvertToCodes(nodes, codes, code|1, bits, nodes[node].child1); +} + + +/**************************************************************************** +* +* NAME +* HuffEncode - Huffman encode the data. +* +* SYNOPSIS +* Size = HuffEncode(Data, Buffer, Codes, Length); +* +* long HuffEncode(unsigned char *, unsigned char *, HuffCodes *, long); +* +* FUNCTION +* Encoding of the data is simple. Each byte of data is taken as the index +* of the codes array, the corresponding code is then put in the output. +* +* INPUTS +* Data - Pointer to data to encode. +* Buffer - Pointer to buffer to hold encoded data. +* Codes - Pointer to array of Huffman codes. +* Length - Length of data buffer to encode. +* +* RESULT +* Size - Size of encoded data. +* +****************************************************************************/ + +long cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length) +{ + long i; + long size; + long next; + unsigned long mask; + unsigned long symbol; + + /* Initialize */ + *buffer = 0; + next = 0x80; + i = 0; + + do { + if (i < length) { + symbol = ((unsigned char *)data)[i]; + } else { + symbol = HUFF_EOS; + } + + mask = 1L << (codes[symbol].bits - 1); + + while (mask != 0) { + + /* Set a bit in the output stream for each bit in the code. */ + if (mask & codes[symbol].code) { + *buffer |= next; + } + + /* Next bit position. */ + next >>= 1; + + /* Advance to the next byte in the output stream when the current + * byte is full. + */ + if (next == 0) { + buffer++; + *buffer = 0; + next = 0x80; + size++; + } + + /* Next bit in the code. */ + mask >>= 1; + } + + i++; + } while (symbol != HUFF_EOS); + + if (next != 0x80) { + size++; + } + + return (size); +} + diff --git a/VQ/VQM32/HUFFDCMP.ASM b/VQ/VQM32/HUFFDCMP.ASM new file mode 100644 index 0000000..39a41a7 --- /dev/null +++ b/VQ/VQM32/HUFFDCMP.ASM @@ -0,0 +1,391 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* huffdcmp.asm +;* +;* DESCRIPTION +;* Huffman order 0 decompressor. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* May 22, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* HuffDecompress - Decompress Huffman order 0 encoded data. +;* BuildHuffTree - Build the Huffman decode tree. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + + STRUC TreeNode +count DD ? ;Weight of the node +child0 DW ? ;Child node 0 +child1 DW ? ;Child node 1 + ENDS TreeNode + +HUFF_EOS EQU 256 + + CODESEG + +;**************************************************************************** +;* +;* NAME +;* HuffDecompress - Decompress Huffman order 0 encoded data. +;* +;* SYNOPSIS +;* Size = HuffDecompress(Data, Buffer, Length, Temp) +;* +;* long = HuffDecompress(unsigned char *, unsigned char *, long, char *); +;* +;* FUNCTION +;* Expand data that has been compressed with order 0 Huffman coding. +;* The model (counts) are extracted from the data and a decode tree is +;* built. The data is expanded by reading a bit and traversing the tree +;* until a leaf node is encountered. +;* +;* INPUTS +;* Data - Pointer to Huffman encoded data. +;* Buffer - Pointer to decompress buffer. +;* Length - Maximum decompress length. +;* Temp - Pointer to temporary working buffer. (Must be >= 5120 bytes!) +;* +;* RESULT +;* Size - Size of decompressed data. +;* +;**************************************************************************** + + GLOBAL C HuffDecompress:NEAR + PROC HuffDecompress C NEAR USES esi edi ebx ecx edx + + ARG data:NEAR PTR + ARG buffer:NEAR PTR + ARG length:DWORD + ARG temp:NEAR PTR + + LOCAL next:DWORD + +;*--------------------------------------------------------------------------- +;* Read in the set of counts +;*--------------------------------------------------------------------------- + + mov esi,[data] ;Compressed data + mov ebx,[temp] ;Nodes array + + mov ax,[esi] ;Get first and last count + xor edx,edx ;i = 0 + xor ecx,ecx + add esi,2 + +??getcounts: + cmp al,dl ;Reached start of run? + jne ??zerocount + +;* Copy the run of counts to the nodes + + sub ah,al ;Run length = Stop - Start + xor ecx,ecx + mov cl,ah + xor eax,eax + inc ecx ;Run length + 1 + +??copycounts: + mov al,[esi] ;Get count + inc edx ;i++ + mov [ebx],eax ;Write count to node + inc esi + add ebx,8 ;Next node + dec ecx + jnz ??copycounts + + mov ax,[esi] ;Get next start + inc esi + cmp al,0 ;Terminator? + je short ??nextcount + + inc esi + jmp short ??nextcount + +;* Fill empty nodes with 0 + +??zerocount: + mov [DWORD PTR ebx],ecx + inc edx ;i++ + add ebx,8 ;Next node + +??nextcount: + cmp edx,256 + jl short ??getcounts + + mov [WORD PTR ebx],1 + mov [data],esi + +;*--------------------------------------------------------------------------- +;* Build the Huffman tree. All active nodes are scanned in order +;* to locate the two nodes with the minimum weights. These two +;* weights are added together and assigned a new node. The new +;* node makes the two minimum nodes into its 0 child and 1 child. +;* The two minimum nodes are then marked as inactive. This process +;* repeats until their is only one node left, which is the root. +;*--------------------------------------------------------------------------- + + mov eax,[temp] ;Nodes array + mov esi,eax + add eax,(513 * 8) ;Node[513] = guaranteed maximum + mov [DWORD PTR eax],-1 + + mov [next],((HUFF_EOS + 1) * 8) + +??sortnext: + mov edx,(513 * 8) ;first = 513 + mov edi,edx ;last = 513 + xor ecx,ecx ;i = 0 + mov ebx,esi ;nodes[i] + +??sortnodes: + cmp [WORD PTR ebx],0 ;Only check non-zero nodes + jz ??nextnode + +;* nodes[i].count < nodes[first].count + + mov eax,[DWORD PTR esi + edx] + cmp eax,[DWORD PTR ebx] + jbe ??checklast + + mov edi,edx ;last = first + mov edx,ecx ;first = i + jmp short ??nextnode + +;* nodes[i].count < nodes[last].count + +??checklast: + mov eax,[DWORD PTR esi + edi] + cmp eax,[DWORD PTR ebx] + jbe ??nextnode + + mov edi,ecx ;last = i + +??nextnode: + add ecx,8 ;i++ + add ebx,8 ;nodes[i] + cmp ecx,[next] + jne short ??sortnodes + +;* Tree done when last = 513 + + cmp edi,(513 * 8) + je short ??decode + + mov ebx,[next] + add ebx,esi + mov [WORD PTR ebx+4],dx ;nodes[next].child0 = first + mov [WORD PTR ebx+6],di ;nodes[next].child1 = last + + add edx,esi + mov eax,[DWORD PTR edx] ;nodes[first].count + add edi,esi + mov [DWORD PTR ebx],eax + + mov ecx,[DWORD PTR edi] ;nodes[last].count + xor eax,eax + add [DWORD PTR ebx],ecx + + mov [DWORD PTR edx],eax ;nodes[first].count = 0 + mov [DWORD PTR edi],eax ;nodes[lats].count = 0 + add [next],8 + jmp ??sortnext + +;*--------------------------------------------------------------------------- +;* Expand the compressed data. As each new symbol is decoded, the +;* tree is traversed, starting at the root node, reading a bit in, +;* and taking either the child0 or child1 path. Eventually, the +;* tree winds down to a leaf node, and the corresponding symbol is +;* output. If the symbol is the HUFF_EOS symbol the process +;* terminates. +;*--------------------------------------------------------------------------- + +??decode: + sub [next],8 ;rootnode - 1 + xor ecx,ecx + mov esi,[data] ;Input data buffer + mov al,080h ;mask = 0x80 + mov edi,[buffer] ;Output buffer + mov ah,[esi] ;Data byte + mov ebx,[temp] + inc esi + +??decodeloop: + mov edx,[next] ;node = root + +??walktree: + mov ecx,4 + add ecx,edx + test al,ah + jz short ??getnode + + add ecx,2 + +??getnode: + mov dx,[WORD PTR ebx + ecx] ;nodes[node].child + shr al,1 + jnz short ??checkleaf + + mov ah,[esi] ;Get next data byte + mov al,080h ;Reset mask + inc esi + +??checkleaf: + cmp edx,(HUFF_EOS * 8) + jg short ??walktree + je short ??done + + shr edx,3 + mov [edi],dl + inc edi + jmp short ??decodeloop + +??done: + mov eax,edi + sub eax,[buffer] + ret + + ENDP HuffDecompress + + +;**************************************************************************** +;* +;* NAME +;* BuildHuffTree - Build the Huffman decode tree. +;* +;* SYNOPSIS +;* Root = BuildHuffTree(Nodes) +;* +;* long BuildHuffTree(TreeNode *); +;* +;* FUNCTION +;* Build the Huffman tree. All active nodes are scanned in order to +;* locate the two nodes with the minimum weights. These two weights are +;* added together and assigned a new node. The new node makes the two +;* minimum nodes into its 0 child and 1 child. The two minimum nodes are +;* then marked as inactive. This process repeats until their is only one +;* node left, which is the root. +;* +;* INPUTS +;* Nodes - Pointer to array of nodes. +;* +;* RESULT +;* Root - Number of root node. +;* +;**************************************************************************** + + GLOBAL C BuildHuffTree:NEAR + PROC BuildHuffTree C NEAR USES esi edi ebx ecx edx + + ARG temp:NEAR PTR + + LOCAL next:DWORD + + mov eax,[temp] ;Nodes array + mov esi,eax + add eax,(513 * 8) ;Node[513] = guaranteed maximum + mov [DWORD PTR eax],-1 + + mov [next],((HUFF_EOS + 1) * 8) + +??sortnext: + mov edx,(513 * 8) ;first = 513 + mov edi,edx ;last = 513 + xor ecx,ecx ;i = 0 + mov ebx,esi ;nodes[i] + +??sortnodes: + cmp [WORD PTR ebx],0 ;Only check non-zero nodes + jz ??nextnode + +;* nodes[i].count < nodes[first].count + + mov eax,[DWORD PTR esi + edx] + cmp eax,[DWORD PTR ebx] + jbe ??checklast + + mov edi,edx ;last = first + mov edx,ecx ;first = i + jmp short ??nextnode + +;* nodes[i].count < nodes[last].count + +??checklast: + mov eax,[DWORD PTR esi + edi] + cmp eax,[DWORD PTR ebx] + jbe ??nextnode + + mov edi,ecx ;last = i + +??nextnode: + add ecx,8 ;i++ + add ebx,8 + cmp ecx,[next] + jne short ??sortnodes + +;* Tree done when last = 513 + + cmp edi,(513 * 8) + je short ??done + + mov ebx,[next] + add ebx,esi ;nodes[next] + mov [WORD PTR ebx+4],dx ;nodes[next].child0 = first + mov [WORD PTR ebx+6],di ;nodes[next].child1 = last + + add edx,esi + mov eax,[DWORD PTR edx] ;nodes[first].count + add edi,esi + mov [DWORD PTR ebx],eax + + mov ecx,[DWORD PTR edi] ;nodes[last].count + xor eax,eax + add [DWORD PTR ebx],ecx + + mov [DWORD PTR edx],eax ;nodes[first].count = 0 + mov [DWORD PTR edi],eax ;nodes[lats].count = 0 + add [next],8 + jmp ??sortnext + +??done: + mov eax,[next] + sub eax,8 + ret + + ENDP BuildHuffTree + + END + diff --git a/VQ/VQM32/HUFFMAN.H b/VQ/VQM32/HUFFMAN.H new file mode 100644 index 0000000..ebb4b83 --- /dev/null +++ b/VQ/VQM32/HUFFMAN.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +#ifndef HUFFMAN_H +#define HUFFMAN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffman.h +* +* DESCRIPTION +* Huffman order 0 compress/decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +****************************************************************************/ + + +/* TreeNode: Huffman decoding tree node. + * + * count - Weight of the node in the tree. + * child0 - Child node 0 + * child1 - Child node 1 + */ +typedef struct _TreeNode { + unsigned long count; + unsigned short child0; + unsigned short child1; +} TreeNode; + + +/* HuffCode: This structure is used for storing the code for each symbol + * during encoding. A table of codes for each symbol is built + * from the Huffman tree. + * + * code - Code used to represent a symbol. + * bits - Length of code in bits. + */ +typedef struct _HuffCode { + unsigned short code; + short bits; +} HuffCode; + + +#define HUFF_EOS 256 /* End of stream symbol */ + +/* Prototypes */ +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +long cdecl HuffDecompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +void cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero); + +void cdecl HuffScaleCounts(TreeNode *nodes); +long cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer); +long cdecl BuildHuffTree(TreeNode *nodes); + +void cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node); + +long cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* HUFFMAN_H */ + diff --git a/VQ/VQM32/IFF.CPP b/VQ/VQM32/IFF.CPP new file mode 100644 index 0000000..e40635e --- /dev/null +++ b/VQ/VQM32/IFF.CPP @@ -0,0 +1,699 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.c +* +* DESCRIPTION +* IFF manager. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenIFF - Open an IFF for reading or writting. +* CloseIFF - Close an IFF. +* ReadForm - Read the IFF FORM, size and type of the file. +* WriteForm - Write IFF form ID, size and type fields. +* ReadChunkHeader - Read the IFF chunk identification header. +* WriteChunkHeader - Write an IFF chunk identification header. +* WriteChunk - Write an IFF chunk with data from a buffer. +* WriteChunkBytes - Write data from a buffer to the IFF stream. +* SkipChunkBytes - Skip bytes in a chunk. +* FindChunk - Scan for a specific chunk name. +* IDtoStr - Convert a longword identifier to a NULL-terminated +* string. +* CurrentFilePos - Get the current file position. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "iff.h" + + +/**************************************************************************** +* +* NAME +* OpenIFF - Open an IFF for reading or writting. +* +* SYNOPSIS +* IFFHandle = OpenIFF(Name, Mode) +* +* IFFHandle *OpenIFF(char *, long); +* +* FUNCTION +* Opens an IFF for a new read or write. The direction of the I/O is +* given by the value of Mode, which can be either IFF_READ or IFF_WRITE. +* +* INPUTS +* Name - Pointer to name of file to open. +* Mode - IFF_READ or IFF_WRITE. +* +* RESULT +* IFFHandle - Pointer to IFFHandle structure or NULL if error. +* +****************************************************************************/ + +IFFHandle *OpenIFF(char *name, long mode) +{ + IFFHandle *iff; + + /* Allocate IFFHandle structure. */ + if ((iff = (IFFHandle *)malloc(sizeof(IFFHandle))) != NULL) { + + /* Initialize handle.*/ + memset(iff, 0, sizeof(IFFHandle)); + iff->flags = mode; + + switch (mode) { + case IFFF_READ: + iff->fh = open(name, O_RDONLY|O_BINARY); + break; + + case IFFF_WRITE: + iff->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY), + (S_IREAD|S_IWRITE)); + + printf("\r"); + break; + + case (IFFF_READ|IFFF_WRITE): + iff->fh = open(name, (O_RDWR|O_BINARY), (S_IREAD|S_IWRITE)); + break; + + default: + iff->fh = -1; + break; + } + + /* If something went wrong we must free up any resources + * that we have opened. + */ + if (iff->fh == -1) { + free(iff); + iff = NULL; + } + } + + return (iff); +} + + +/**************************************************************************** +* +* NAME +* CloseIFF - Close an IFF. +* +* SYNOPSIS +* CloseIFF(IFFHandle) +* +* void CloseIFF(IFFHandle *); +* +* FUNCTION +* Completes an IFF read or write operation. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseIFF(IFFHandle *iff) +{ + long length; + + /* Write the length of the FORM */ + if ((iff->flags & IFFF_WRITE) && ((iff->form.size == 0) + || (iff->scan > iff->form.size))) { + + lseek(iff->fh, 4, SEEK_SET); + length = REVERSE_LONG(iff->scan); + write(iff->fh, &length, 4); + } + + close(iff->fh); + free(iff); +} + + +/**************************************************************************** +* +* NAME +* ReadForm - Read the IFF FORM, size and type of the file. +* +* SYNOPSIS +* Error = ReadForm(IFFHandle, FormHeader) +* +* long ReadForm(IFFHandle *, FormHeader *); +* +* FUNCTION +* Read in the IFF form, size, type information. If the FormHeader +* structure pointer is NULL then the FORM will be read into the +* IFFHandles form structure. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* FormHeader - Pointer to FormHeader structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long ReadForm(IFFHandle *iff, FormHeader *form) +{ + FormHeader *ptr; + long error; + + /* Read the FORM into the IFFHandle or the provided FormHeader. */ + if (form == NULL) { + ptr = &iff->form; + } else { + ptr = form; + } + + /* Read in IFF FORM from the file stream.. */ + if ((error = read(iff->fh, ptr, 12)) == 12) { + ptr->size = REVERSE_LONG(ptr->size); + iff->scan = 4; + error = 0; + } else { + if (error == -1) + error = IFFERR_READ; + else if (error == 0) + error = IFFERR_EOF; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteForm - Write IFF form ID, size and type fields. +* +* SYNOPSIS +* Error = WriteForm(IFFHandle) +* +* long WriteForm(IFFHandle, FormHeader *); +* +* FUNCTION +* Write out the IFF form, size, type information. If the size field +* is zero then the IFF form size will be calculated and written by +* the CloseIFF() function. If the FormHeader structure pointer is NULL +* the the form from the IFFHandle will be written. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* FormHeader - Pointer to FormHeader structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long WriteForm(IFFHandle *iff, FormHeader *form) +{ + FormHeader *ptr; + long error = 0; + + /* Use the FORM from the IFFHandle or the provided FormHeader. */ + if (form == NULL) { + ptr = &iff->form; + } else { + ptr = form; + } + + /* Write the IFF form to the file stream. */ + if (iff->flags & IFFF_WRITE) { + ptr->size = REVERSE_LONG(ptr->size); + + if (write(iff->fh, ptr, 12) == 12) { + iff->scan = 4; + } else { + error = IFFERR_WRITE; + } + } else { + error = IFFERR_WRITE; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* ReadChunkHeader - Read the IFF chunk identification header. +* +* SYNOPSIS +* Error = ReadChunkHeader(IFFHandle) +* +* long ReadChunkHeader(IFFHandle *); +* +* FUNCTION +* Read the IFF identification header from the files data stream. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long ReadChunkHeader(IFFHandle *iff) +{ + long error = 0; + + /* Skip any part of the previous chunk that hasn't been processed. */ + if ((iff->cn.size != 0) && (iff->cn.scan < PADSIZE(iff->cn.size))) { + error = lseek(iff->fh, (PADSIZE(iff->cn.size) - iff->cn.scan), SEEK_CUR); + + if (error == -1) { + error = IFFERR_READ; + } else { + error = 0; + } + } + + /* Read in the next chunk header context. */ + if (!error) { + if ((error = read(iff->fh, &iff->cn, 8)) == 8) { + error = 0; + iff->scan += 8; + iff->cn.size = REVERSE_LONG(iff->cn.size); + iff->cn.scan = 0; + } else { + if (error == -1) { + error = IFFERR_READ; + } else if (error == 0) { + error = IFFERR_EOF; + } + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteChunkHeader - Write an IFF chunk identification header. +* +* SYNOPSIS +* Error = WriteChunkHeader(IFFHandle, ID, Size) +* +* long WriteChunkHeader(IFFHandle *, long, long); +* +* FUNCTION +* Write an IFF identification header to the files data stream. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* Size - Size of chunk in bytes (WORD aligned). +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunkHeader(IFFHandle *iff, long id, long size) +{ + long error = 0; + + /* Make sure it is okay to write. */ + if (iff->flags & IFFF_WRITE) { + iff->cn.id = id; + iff->cn.size = REVERSE_LONG(size); + iff->cn.scan = 0; + + if (write(iff->fh, &iff->cn, 8) == 8) { + iff->scan += 8; + } else { + error = IFFERR_WRITE; + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteChunk - Write an IFF chunk with data from a buffer. +* +* SYNOPSIS +* Actual = WriteChunk(IFFHandle, ID, Buffer, Size) +* +* long WriteChunk(IFFHandle *, long, char *, long); +* +* FUNCTION +* Write a IFF chunk at the current file position. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* Buffer - Pointer to buffer area with bytes to be written. +* Size - Number of bytes to write. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunk(IFFHandle *iff, long id, char *buffer, long size) +{ + Context cn; + long actual; + + /* Make sure we can write to this file. */ + if (iff->flags & IFFF_WRITE) { + cn.id = id; + cn.size = REVERSE_LONG(size); + + /* Write chunk header. */ + if (write(iff->fh, &cn, 8) == 8) { + iff->scan += 8; + iff->cn.scan += 8; + + /* Write chunk data. */ + actual = write(iff->fh, buffer, size); + + if (actual == size) { + iff->scan += actual; + iff->cn.scan += actual; + + /* Write chunk padding if necessary. */ + if (PADSIZE(size) > size) { + id = 0; + + if (write(iff->fh, &id, 1) == 1) { + iff->scan++; + iff->cn.scan++; + } else { + actual = IFFERR_WRITE; + } + } + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* WriteChunkBytes - Write data from a buffer to the IFF stream. +* +* SYNOPSIS +* Actual = WriteChunkBytes(IFFHandle, Buffer, Size) +* +* long WriteChunk(IFFHandle *, char *, long); +* +* FUNCTION +* Write a IFF chunk at the current file position. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Buffer - Pointer to buffer area with bytes to be written. +* Size - Number of bytes to write. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunkBytes(IFFHandle *iff, char *buffer, long size) +{ + long actual; + + /* Make sure we can write to this file. */ + if (iff->flags & IFFF_WRITE) { + + /* Write data. */ + if ((actual = (unsigned short)write(iff->fh, buffer, size)) == size) { + iff->scan += actual; + iff->cn.scan += actual; + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* ReadChunkBytes - Read data from a chunk into a buffer. +* +* SYNOPSIS +* Actual = ReadChunkBytes(IFFHandle, Buffer, Length) +* +* long ReadChunkBytes(IFFHandle *, char *, long); +* +* FUNCTION +* Read in 'Length' number of bytes from the current chunk context. +* If the specified length exceeds the number of bytes remaining in the +* chunk ReadChunkBytes() will read in only the number of remaining +* bytes. ReadChunkBytes() will never read beyond the scope of the +* current chunk. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Buffer - Pointer to buffer to read data into. +* Length - Number of bytes to read. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long ReadChunkBytes(IFFHandle *iff, char *buffer, long size) +{ + long actual; + + /* If the actual bytes remaining in the current chunk is less than + * the requested bytes to read then adjust the read request size + * to only read in the bytes that remain in the chunk. + */ + actual = (iff->cn.size - iff->cn.scan); + + if (size > actual) { + size = actual; + } + + /* Read in the requested number of bytes. */ + if ((actual = read(iff->fh, buffer, size)) != size) { + actual = IFFERR_READ; + } else { + iff->scan += actual; + iff->cn.scan += actual; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* SkipChunkBytes - Skip bytes in a chunk. +* +* SYNOPSIS +* Error = SkipChunkBytes(IFFHandle, Skip) +* +* long SkipChunkBytes(IFFHandle *, long); +* +* FUNCTION +* Skip the specified number of bytes of the chunk. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Skip - Number of bytes to skip. +* +* RESULT +* Error - 0 if successful or FAIL_??? error code. +* +****************************************************************************/ + +long SkipChunkBytes(IFFHandle *iff, long skip) +{ + long error = 0; + + if (lseek(iff->fh, skip, SEEK_CUR) == -1) { + error = IFFERR_READ; + } else { + iff->scan += skip; + iff->cn.scan += skip; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* FindChunk - Scan for a specific chunk name. +* +* SYNOPSIS +* Error = FindChunk(IFFHandle, ID) +* +* long FindChunk(IFFHandle *, long); +* +* FUNCTION +* Scan from the current file position for the next occurance of the +* specified chunk ID. When a match is found the function will return +* with the matching chunk as the current context. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* +* RESULT +* Error - 0 if successful or FAIL_??? error code. +* +****************************************************************************/ + +long FindChunk(IFFHandle *iff, long id) +{ + long found = 0; + long error = 0; + + /* Invalid handle check. */ + if (iff != NULL) { + + /* Scan until we have a match or an error. */ + while ((found == 0) && !(error = ReadChunkHeader(iff))) { + + /* If we found a match the terminate scan, otherwise skip this + * chunk and process the next. + */ + if (iff->cn.id == id) { + found = 1; + } else { + error = SkipChunkBytes(iff, PADSIZE(iff->cn.size)); + } + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* IDtoStr - Convert a longword identifier to a NULL-terminated string. +* +* SYNOPSIS +* String = IDtoStr(ID, Buffer) +* +* char *IDtoStr(long, char *); +* +* FUNCTION +* Writes the ASCII equivalent of the given longword ID into buffer as a +* NULL-terminated string. +* +* INPUTS +* ID - Longword ID. +* Buffer - Character buffer to accept string (at least 5 characters). +* +* RESULT +* String - The value of "Buffer". +* +****************************************************************************/ + +char *IDtoStr(long id, char *buf) +{ + memcpy(buf, &id, 4); + *(buf + 4) = 0; + + return (buf); +} + + +/**************************************************************************** +* +* NAME +* CurrentFilePos - Get the current file position. +* +* SYNOPSIS +* Position = CurrentFilePos(IFFHandle) +* +* long CurrentFilePos(IFFHandle *); +* +* FUNCTION +* This function returns the offset in bytes of the current file position +* from the beginning of the IFF. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* Position - Offset in bytes from the beginning of the file to the +* current position. +* +****************************************************************************/ + +long CurrentFilePos(IFFHandle *iff) +{ + long offset; + + if ((offset = lseek(iff->fh, 0, SEEK_CUR)) == -1) { + offset = IFFERR_READ; + } + + return (offset); +} + diff --git a/VQ/VQM32/IFF.H b/VQ/VQM32/IFF.H new file mode 100644 index 0000000..c81c7e1 --- /dev/null +++ b/VQ/VQM32/IFF.H @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + +#ifndef VQMIFF_H +#define VQMIFF_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.h +* +* DESCRIPTION +* IFF (Interchange File Format) manager definitions. +* (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* FormHeader - Structure associated with IFF forms. + * + * id - IFF form id (IE: "FORM") + * size - Length of IFF in bytes + * type - Form type (IE: "ILBM") + */ +typedef struct _FormHeader { + long id; + long size; + long type; +} FormHeader; + +/* Context - Structure associated with chunks. + * + * id - Chunk identifier. + * size - Size of chunk in bytes. + * scan - Bytes read/written. + */ +typedef struct _Context { + long id; + long size; + long scan; +} Context; + +/* IFFHandle - Structure associated with an active IFF read\write session. + * + * fh - DOS filehandle + * flags - Internal flags used by IFF routines. + * form - IFF form information. + * scan - Bytes read/written + * cn - Context of current chunk. + */ +typedef struct _IFFHandle { + long fh; + long flags; + FormHeader form; + long scan; + Context cn; +} IFFHandle; + +/* bit masks for "flags" field. */ +#define IFFB_READ 0 +#define IFFB_WRITE 1 +#define IFFF_READ (1<>24) \ + &0x000000FFL)|(((unsigned long)(id)>>8) \ + &0x0000FF00L)|(((unsigned long)(id)<<8) \ + &0x00FF0000L)|(((unsigned long)(id)<<24)&0xFF000000L)) + +#define REVERSE_WORD(id) ((unsigned short)((((unsigned short)(id)<<8) \ + &0x00FF00)|(((unsigned short)(id)>>8)&0x0FF))) + +#define PADSIZE(size) (((size)+1)&(~1)) + +#ifndef MAKE_ID +#define MAKE_ID(a,b,c,d) ((long)((long)(d)<<24)|((long)(c)<<16)| \ + ((long)(b)<<8)|(long)(a)) +#endif + +/* Universal IFF identifiers */ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_LIST MAKE_ID('L','I','S','T') +#define ID_PROP MAKE_ID('P','R','O','P') +#define ID_NULL MAKE_ID(' ',' ',' ',' ') + +/* Prototypes */ +IFFHandle *OpenIFF(char *, long); +void CloseIFF(IFFHandle *); +long ReadForm(IFFHandle *, FormHeader *); +long WriteForm(IFFHandle *, FormHeader *); +long ReadChunkHeader(IFFHandle *); +long WriteChunkHeader(IFFHandle *, long, long); +long WriteChunk(IFFHandle *, long, char *, long); +long WriteChunkBytes(IFFHandle *, char *, long); +long ReadChunkBytes(IFFHandle *, char *, long); +long SkipChunkBytes(IFFHandle *, long); +long FindChunk(IFFHandle *, long); +char *IDtoStr(long, char *); +long CurrentFilePos(IFFHandle *); + +#endif /* VQMIFF_H */ + diff --git a/VQ/VQM32/LCWCOMP.ASM b/VQ/VQM32/LCWCOMP.ASM new file mode 100644 index 0000000..f402077 --- /dev/null +++ b/VQ/VQM32/LCWCOMP.ASM @@ -0,0 +1,266 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* lcwcomp.asm +;* +;* DESCRIPTION +;* LCW compression code. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Louis Castle +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* LCW_Compress - LCW compress a buffer of memory. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* LCW_Compress - LCW compress a buffer of memory. +;* +;* SYNOPSIS +;* Size = LCW_Compress(Source, Dest, Length) +;* +;* long LCW_Compress(void *, void *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to compress. +;* Dest - Pointer to buffer to put compressed data. +;* Length - Length in bytes of data to compress. +;* +;* RESULT +;* Size - Size in bytes of compresed data. +;* +;**************************************************************************** + + GLOBAL C LCW_Compress:NEAR + PROC LCW_Compress C NEAR USES ebx ecx edx esi edi + + 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 + + mov esi,[source] + mov edi,[dest] + mov edx,[datasize] + + cld ;Forward direction + mov ebx,esi + add ebx,edx + mov [end_of_data],ebx ;Save end of source address + mov [inlen],1 ;Set the in-length flag + mov [a1stdest],edi ;Save original dest + mov [a1stsrc],esi ;Save original source + mov [lenoff],edi ;Save offset length + + mov al,081h ;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 address of first byte + 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 [inlen],0 ;Clear the in-length flag + mov esi,edi + mov edi,[ndest] + 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 ;Address of the last byte +1 + sub ecx,edi ;Total number of bytes left + 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 address for the next search + mov ebx,edi ;Save address 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 + + inc edi + +??notend: + mov esi,edx + 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 paragraph 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: + mov bl,cl ;Get the length (3-10) + sub bl,3 ;Sub 3 for a 3 bit number 0-7 + shl bl,4 + add ah,bl + xchg ah,al + 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 [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 [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 + add esi,[count] ;Add in the length of the run to the source + mov [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 eax,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_Compress + + END diff --git a/VQ/VQM32/LCWUNCMP.ASM b/VQ/VQM32/LCWUNCMP.ASM new file mode 100644 index 0000000..8af488b --- /dev/null +++ b/VQ/VQM32/LCWUNCMP.ASM @@ -0,0 +1,221 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* lcwuncmp.asm +;* +;* DESCRIPTION +;* LCW uncompress routine. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Chris Yates +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* LCW_Uncompress - Uncompress LCW encoded data. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* LCW_Uncompress - Uncompress LCW encoded data. +;* +;* SYNOPSIS +;* Size = LCW_Uncompress(Source, Dest, Length) +;* +;* LONG LCW_Uncompress(void *, void *, long); +;* +;* FUNCTION +;* Uncompress 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 copy copy w1 bytes from offset w2 +;* n=11111110,w1,b1 long run run byte b1 for w1 bytes +;* n=10000000 end end of data reached +;* +;* INPUTS +;* Source - Pointer to LCW encoded data. +;* Dest - Pointer to buffer to uncompress into. +;* Length - +;* +;* RESULT +;* Size - Size of uncompressed data in bytes. +;* +;**************************************************************************** + + GLOBAL C LCW_Uncompress:NEAR + PROC LCW_Uncompress C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG length:DWORD + + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + LOCAL lastcom:DWORD + LOCAL lastcom1:DWORD + + mov esi,[source] ;ESI - Source address + mov edi,[dest] ;EDI - Destination address + mov edx,[length] ;EDX - Maximum length + + mov [a1stdest],edi ;Save dest address + add edx,edi ;Last address (Dest + length) + mov [lastbyte],edx + + cld ;Forward direction + mov ebx,esi ;Save source address + +??loop: +; Exit if no bytes are remaining. + + mov eax,[lastbyte] + sub eax,edi + jz short ??out + + mov [maxlen],eax ;Save for string commands + mov esi,ebx ;Restore source address + lodsb + or al,al ;See if its a short run + js short ??notshort + + mov ah,al ;Put rel offset high nibble in ah + and ah,0Fh ; Only 4 bits count + sub ecx,ecx + mov ch,al ;Put count nibble in ch + shr ch,4 + mov cl,ch + xor ch,ch + add ecx,3 ;Get actual run length + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??rsok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??rsok: + lodsb ;Get rel offset low byte + mov ebx,esi ;Save the source address + mov esi,edi ;Get the current dest + sub esi,eax ;Get relative offset + rep movsb + jmp ??loop + +??notshort: + test al,40h ;Is it a length? + jne short ??notlength ;If not it could be med or long run + +; If end code then exit. + + cmp al,80h + je short ??out + + mov cl,al ;Put the byte in count register + and ecx,3Fh ;Mask off the extra bits + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??lenok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??lenok: + rep movsb + mov ebx,esi ;Save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp short ??exit + +??notlength: + mov cl,al ;Get the entire code + and ecx,3Fh ;Mask off all but the size -3 + add ecx,3 ;Add 3 for byte count + cmp al,0FEh + jne short ??notrunlength + + sub eax,eax + lodsw + mov ecx,eax + sub eax,eax + lodsb + mov ebx,esi ;Save the source offset + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??runlenok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??runlenok: + rep stosb + jmp ??loop + +??notrunlength: + cmp al,0FFh ;Is it a long run? + jne short ??notlong ;If not use the code as the size + + sub eax,eax + lodsw ;If so, get the size + mov ecx,eax ;Put int the count byte + +??notlong: + lodsw ;Get the rel index + mov ebx,esi ;Save the source offset + add eax,[a1stdest] ;Add in the first index + mov esi,eax ;Use this as a source + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??runok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??runok: + rep movsb + jmp ??loop + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx ;Calculate bytes uncompressed. + ret + + ENDP LCW_Uncompress + + END + diff --git a/VQ/VQM32/MAKEFILE b/VQ/VQM32/MAKEFILE new file mode 100644 index 0000000..5879824 --- /dev/null +++ b/VQ/VQM32/MAKEFILE @@ -0,0 +1,183 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc 32-bit library. (Watcom/4GW) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqm32wp +PRJDIR = $(%PRJ)\VQM32 + +OBJECTS = & + iff.obj & + profile.obj & + audzap.obj & + audunzap.obj & + targa.obj & + vertag.obj & + sortpal.obj & + palette.obj & + mono.obj & + lcwuncmp.obj & + lcwcomp.obj & + testvb.obj & + vb.obj & + mcgabuf.obj & + video.obj & + xmode.obj & + xmodepg.obj & + vesabuf.obj & + vesablit.obj & + vesavid.obj & + soscodec.obj & + drawchar.obj & + textprnt.obj & + font.obj & + chrwidth.obj & + mixfile.obj & + crc.obj & + fillrect.obj & + captoken.obj & + huffcmp.obj & + huffdcmp.obj & + mem.obj & + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQMisc32 header files! + @copy *.h $(%PRJ)\INCLUDE\VQM32 >NUL + @copy *.i $(%PRJ)\INCLUDE\VQM32 >NUL + diff --git a/VQ/VQM32/MAKEFILE.BAK b/VQ/VQM32/MAKEFILE.BAK new file mode 100644 index 0000000..adb83da --- /dev/null +++ b/VQ/VQM32/MAKEFILE.BAK @@ -0,0 +1,174 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc 32-bit library. (Watcom/4GW) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +PRJ=. + +!ifndef PRJ || %WATCOM +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = VQM32W +PRJDIR = $(PRJ)\VQM32 + +OBJECTS = & + iff.obj & + profile.obj & + audzap.obj & + audunzap.obj & + targa.obj & + vertag.obj & + sortpal.obj & + palette.obj & + lcwuncmp.obj & + lcwcomp.obj & + testvb.obj & + vb.obj & + mcgabuf.obj & + video.obj & + xmode.obj & + xmodepg.obj & + vesabuf.obj & + vesablit.obj & + vesavid.obj & + soscodec.obj + +# mono.obj & +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQMisc32 header files! + @copy *.h $(PRJ)\INCLUDE\VQM32 >NUL + @copy *.i $(PRJ)\INCLUDE\VQM32 >NUL + diff --git a/VQ/VQM32/MAKEFILE.BOR b/VQ/VQM32/MAKEFILE.BOR new file mode 100644 index 0000000..59ae8fd --- /dev/null +++ b/VQ/VQM32/MAKEFILE.BOR @@ -0,0 +1,234 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc32 library. (Borland/TNT) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Febuary 7, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# BCDIR - Borland C installation path. +# TNTDIR - Pharlap/TNT installation path. +# +#**************************************************************************** + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECT DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = VQM32BP +PRJDIR = $(PRJ)\VQM32 + +OBJECTS = \ + iff.obj \ + profile.obj \ + audzap.obj \ + audunzap.obj \ + targa.obj \ + vertag.obj \ + sortpal.obj \ + palette.obj \ + mono.obj \ + lcwuncmp.obj \ + lcwcomp.obj \ + testvb.obj \ + vb.obj \ + mcgabuf.obj \ + video.obj \ + xmode.obj \ + xmodepg.obj \ + vesabuf.obj \ + vesablit.obj \ + vesavid.obj \ + soscodec.obj \ + drawchar.obj \ + textprnt.obj \ + font.obj \ + chrwidth.obj \ + mixfile.obj \ + crc.obj \ + fillrect.obj \ + captoken.obj \ + huffcmp.obj \ + huffdcmp.obj \ + mem.obj \ + portio.obj \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).lib + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD THE LIBRARY +#---------------------------------------------------------------------------- + +$(PRJNAME).lib: $(OBJECTS) + - @del $(.path.lib)\$(PRJNAME).lib >&NUL + $(LIB_CMD) $(.path.lib)\$(PRJNAME).lib @$(LIB_CFG) @&&| +-+$(**: = -+) +| + - @copy *.h $(PRJ)\INCLUDE\VQM32 >&NUL + - @copy *.i $(PRJ)\INCLUDE\VQM32 >&NUL + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE +/zi +/dPHARLAP_TNT=1 +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + diff --git a/VQ/VQM32/MAKEFILE.WAT b/VQ/VQM32/MAKEFILE.WAT new file mode 100644 index 0000000..5879824 --- /dev/null +++ b/VQ/VQM32/MAKEFILE.WAT @@ -0,0 +1,183 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc 32-bit library. (Watcom/4GW) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqm32wp +PRJDIR = $(%PRJ)\VQM32 + +OBJECTS = & + iff.obj & + profile.obj & + audzap.obj & + audunzap.obj & + targa.obj & + vertag.obj & + sortpal.obj & + palette.obj & + mono.obj & + lcwuncmp.obj & + lcwcomp.obj & + testvb.obj & + vb.obj & + mcgabuf.obj & + video.obj & + xmode.obj & + xmodepg.obj & + vesabuf.obj & + vesablit.obj & + vesavid.obj & + soscodec.obj & + drawchar.obj & + textprnt.obj & + font.obj & + chrwidth.obj & + mixfile.obj & + crc.obj & + fillrect.obj & + captoken.obj & + huffcmp.obj & + huffdcmp.obj & + mem.obj & + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQMisc32 header files! + @copy *.h $(%PRJ)\INCLUDE\VQM32 >NUL + @copy *.i $(%PRJ)\INCLUDE\VQM32 >NUL + diff --git a/VQ/VQM32/MCGABUF.ASM b/VQ/VQM32/MCGABUF.ASM new file mode 100644 index 0000000..566cf10 --- /dev/null +++ b/VQ/VQM32/MCGABUF.ASM @@ -0,0 +1,196 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* mcgabuf.asm +;* +;* DESCRIPTION +;* MCGA display routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* ClearVRAM - Clear all VRAM. +;* MCGA_BufferCopy - Copy 320x200 buffer to MCGA VRAM +;* MCGA_Blit - Bit blit a block to the MCGA screen. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* ClearVRAM - Clear all VRAM. +;* +;* SYNOPSIS +;* ClearVRAM() +;* +;* void ClearVRAM(void); +;* +;* FUNCTION +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ClearVRAM:NEAR + PROC ClearVRAM C NEAR USES eax ecx edi es + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to video memory + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + SET_PLANE 0Fh ;Enable all planes for writing + cld + mov ecx,16000 ;Clear 320x200 + xor eax,eax + rep stosd + ret + + ENDP ClearVRAM + + +;**************************************************************************** +;* +;* NAME +;* MCGA_BufferCopy - Copy 320x200 buffer to MCGA VRAM +;* +;* SYNOPSIS +;* MCGA_BufferCopy(Buffer, Dummy) +;* +;* void MCGA_BufferCopy(char *, char *); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer. +;* Dummy - Prototype placeholder. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C MCGA_BufferCopy:NEAR + PROC MCGA_BufferCopy C NEAR USES ecx esi edi es + + ARG buffer:NEAR PTR + ARG dummy:NEAR PTR + + IF PHARLAP_TNT + mov eax,01Ch + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + mov esi, [buffer] + mov ecx,16000 + rep movsd ;Transfer the data + ret + + ENDP MCGA_BufferCopy + + +;**************************************************************************** +;* +;* NAME +;* MCGA_Blit - Bit blit a block to the MCGA screen. +;* +;* SYNOPSIS +;* MCGA_Blit(Buffer, Screen, Width, Height) +;* +;* void MCGA_Blit(char *, char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to copy. +;* Screen - Screen address to copy buffer to. +;* Width - Width of block. +;* Height - Height of block. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C MCGA_Blit:NEAR + PROC MCGA_Blit C NEAR USES ecx edx esi edi + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + ARG imgwidth:DWORD + ARG imgheight:DWORD + + IF PHARLAP_TNT + push es + mov eax,1Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov edi,[screen] + mov edx,320 + sub edx,[imgwidth] ;Compute modulo + +??Do_row: + mov ecx,[imgwidth] + rep movsb + add edi,edx + dec [imgheight] + jnz ??Do_row + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP MCGA_Blit + + END + diff --git a/VQ/VQM32/MEM.CPP b/VQ/VQM32/MEM.CPP new file mode 100644 index 0000000..a14f325 --- /dev/null +++ b/VQ/VQM32/MEM.CPP @@ -0,0 +1,134 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.c +* +* DESCRIPTION +* Memory management. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* DPMI_Lock - Lock a memory page. +* DPMI_Unlock - Unlock a locked memory page. +* +****************************************************************************/ + +#ifdef __WATCOMC__ +#include +#include +#include + + +/**************************************************************************** +* +* NAME +* DPMI_Lock - Lock a memory page. +* +* SYNOPSIS +* DPMI_Lock(Address, Size) +* +* void DPMI_Lock(void *, long); +* +* FUNCTION +* +* INPUTS +* Address - Starting linear address of memory to lock. +* Size - Size of region to lock in bytes. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DPMI_Lock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + memset(®s, 0, sizeof(REGS)); + segread(&sregs); + + /* Lock the memory page. + * + * AX = 0x600 + * BX:CX = Starting linear address of memory to lock. + * SI:DI = Size of region to lock in bytes. + */ + regs.x.eax = DPMI_LOCK; + regs.x.ebx = ((long)ptr & 0xFFFF0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000FFFF); + regs.x.esi = ((long)size & 0xFFFF0000) >> 16; + regs.x.edi = ((long)size & 0x0000FFFF); + int386x(DPMI_INT, ®s, ®s, &sregs); +} + + +/**************************************************************************** +* +* NAME +* DPMI_Unlock - Unlock a locked memory page. +* +* SYNOPSIS +* DPMI_Unlock(Address, Size) +* +* void DPMI_Unlock(void *, long); +* +* FUNCTION +* +* INPUTS +* Address - Starting linear address of memory to unlock. +* Size - Size of region to unlock in bytes. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* Unlock memory page. */ + memset(®s, 0 ,sizeof(REGS)); + segread(&sregs); + + regs.x.eax = DPMI_UNLOCK; + regs.x.ebx = ((long)ptr & 0xFFFF0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000FFFF); + regs.x.esi = ((long)size & 0xFFFF0000) >> 16; + regs.x.edi = ((long)size & 0x0000FFFF); + int386x(DPMI_INT, ®s, ®s, &sregs); +} + +#endif /* __WATCOMC__ */ diff --git a/VQ/VQM32/MEM.H b/VQ/VQM32/MEM.H new file mode 100644 index 0000000..69b0c45 --- /dev/null +++ b/VQ/VQM32/MEM.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQAMEM_H +#define VQAMEM_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.h +* +* DESCRIPTION +* Memory management definitions. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +/* Definitions */ +#define DPMI_INT 0x0031 +#define DPMI_LOCK 0x0600 +#define DPMI_UNLOCK 0x0601 + +/* Prototypes */ +#ifdef __WATCOMC__ +void DPMI_Lock(void const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +#else +#define DPMI_Lock(a,b) +#define DPMI_Unlock(a,b) +#endif + +#endif /* VQAMEM_H */ diff --git a/VQ/VQM32/MIXFILE.CPP b/VQ/VQM32/MIXFILE.CPP new file mode 100644 index 0000000..a0d4d86 --- /dev/null +++ b/VQ/VQM32/MIXFILE.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.c +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These routines are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* MixFile format: +* +* HEADER +* (2 bytes) Count - The number of entries in this file. +* (4 bytes) Size - Size of the mix file in bytes. +* +* SUBBLOCKS (There are "Count" number of these.) +* (4 bytes) CRC - Entry descriptor (CRC of filename). +* (4 bytes) Offset - Offset in bytes from beginning of the DATA chunk. +* (4 bytes) Size - Size of entry. +* +* DATA +* Entry data. +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenMix - Open mix file for access. +* CloseMix - Close a mix file. +* OpenMixEntry - Open a mix file entry. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "mixfile.h" +#include "crc.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +int compfunc(void const *ptr1, void const *ptr2); + + +/**************************************************************************** +* +* NAME +* OpenMix - Open mix file for access. +* +* SYNOPSIS +* MixHandle = OpenMix(Name) +* +* MixHandle *OpenMix(char *); +* +* FUNCTION +* Open a mix file for access. +* +* INPUTS +* Name - Pointer to name of mix file to open. +* +* RESULT +* MixHandle - Pointer to handle for mix file. +* +****************************************************************************/ + +MIXHandle *OpenMix(char *name) +{ + MIXHeader mfhdr; + MIXHandle *mix = NULL; + long fh; + long sbsize; + long size; + + /* Open mix file and read in header. */ + if ((fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + if (read(fh, &mfhdr, sizeof(MIXHeader)) == sizeof(MIXHeader)) { + + /* Allocate handle based on the number of SubBlocks. */ + sbsize = (mfhdr.Count * sizeof(MIXSubBlock)); + size = sbsize + sizeof(MIXHandle); + + if ((mix = (MIXHandle *)malloc(size)) != NULL) { + memset(mix, 0, size); + mix->Name = name; + mix->Size = mfhdr.Size; + mix->Count = mfhdr.Count; + + /* Read in the SubBlock entries. */ + if (read(fh, &mix->Entries[0], sbsize) != sbsize) { + free(mix); + mix = NULL; + } + } + } + + close(fh); + } + + return (mix); +} + + +/**************************************************************************** +* +* NAME +* CloseMix - Close a mix file. +* +* SYNOPSIS +* CloseMix(MixHandle) +* +* void CloseMix(MixHandle *); +* +* FUNCTION +* Close a mix file by freeing its handle. +* +* INPUTS +* MixHandle - Pointer to MixHandle returned by OpenMix(). +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseMix(MIXHandle *mix) +{ + free(mix); +} + + +/**************************************************************************** +* +* NAME +* OpenMixEntry - Open a mix file entry. +* +* SYNOPSIS +* FH = OpenMixEntry(MixHandle, EntryName) +* +* short OpenMixEntry(MIXHandle *, char *); +* +* FUNCTION +* Opens an entry from the specified mix file handle. Use close() to close +* the file when done. +* +* INPUTS +* MixHandle - Pointer to MIXHandle containing entry to open. +* EntryName - Pointer to name of mix file entry to open. +* +* RESULT +* FH - DOS filehandle, -1 if unable to open. +* +****************************************************************************/ + +long OpenMixEntry(MIXHandle *mix, char *name) +{ + MIXSubBlock key; + MIXSubBlock *block; + long fh; + + /* Search for the specified file in the mix file. */ + key.CRC = Calculate_CRC(name, strlen(name)); + block = (MIXSubBlock *)bsearch(&key, &mix->Entries[0], mix->Count, + sizeof(MIXSubBlock), compfunc); + + /* If the block exists for the requested filename. */ + if (block != NULL) { + + /* Initialize the key for file access. */ + key.Offset = block->Offset; + key.Offset += (mix->Count * sizeof(MIXSubBlock)) + sizeof(MIXHeader); + + /* Open the mix file. */ + if ((fh = open(mix->Name, (O_RDONLY|O_BINARY))) != -1) { + + /* Seek to the start of the file. */ + if (lseek(fh, key.Offset, SEEK_SET) == -1) { + close(fh); + fh = -1; + } + } + } + + return (fh); +} + + +/**************************************************************************** +* +* NAME +* compfunc - Compare function for bsearch(). +* +* SYNOPSIS +* Result = compfunc(Entry1, Entry2); +* +* int compfunc(void const *, void const *); +* +* FUNCTION +* +* INPUTS +* Entry1 - Pointer to first entry to compare. +* Entry2 - Pointer to second entry to compare. +* +* RESULT +* Result - +* +****************************************************************************/ + +int compfunc(void const *ptr1, void const *ptr2) +{ + if (((MIXSubBlock *)ptr1)->CRC < ((MIXSubBlock *)ptr2)->CRC) return -1; + if (((MIXSubBlock *)ptr1)->CRC > ((MIXSubBlock *)ptr2)->CRC) return 1; + return(0); +} + + diff --git a/VQ/VQM32/MIXFILE.H b/VQ/VQM32/MIXFILE.H new file mode 100644 index 0000000..5d6b012 --- /dev/null +++ b/VQ/VQM32/MIXFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMMIXFILE_H +#define VQMMIXFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.h +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These definitions are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Disable structure alignment.*/ +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* MIXHeader: Mix file data header. + * + * Count - Number of entries contained in this mix file. + * Size - Size of Mix file. + */ +typedef struct _MIXHeader { + short Count; + long Size; +} MIXHeader; + +/* MIXSubBlock: Mix file entry descriptor. + * + * CRC - Unique entry identifier. + * Offset - Offset from beginning of data segment to entry. + * Size - Size of entry. + */ +typedef struct _MIXSubBlock { + long CRC; + long Offset; + long Size; +} MIXSubBlock; + +/* MIXHandle: Mix file handle. + * + * Name - Pointer to the name of the mix file this handle is for. + * Size - Size of entire mix file. + * FH - DOS file handle of opened entry. + * Count - Number of files contained in this mix. + * Entries - Array of 'Count' MIXSubBlock structure entries. + */ +typedef struct _MIXHandle { + char *Name; + long Size; + long FH; + long Count; + MIXSubBlock Entries[]; +} MIXHandle; + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +MIXHandle *OpenMix(char *name); +void CloseMix(MIXHandle *mix); +long OpenMixEntry(MIXHandle *mix, char *name); + +/* Restore original alignment */ +#ifdef __WATCOMC__ +#pragma pack(); +#endif + +#endif /* VQMMIXFILE_H */ + diff --git a/VQ/VQM32/MONO.ASM b/VQ/VQM32/MONO.ASM new file mode 100644 index 0000000..2fea785 --- /dev/null +++ b/VQ/VQM32/MONO.ASM @@ -0,0 +1,871 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* mono.asm +;* +;* DESCRIPTION +;* Mono screen print and output routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Mono_Enable - Enable mono output. +;* Mono_Disable - Disable mono output. +;* Mono_X - Get mono cursors X position. +;* Mono_Y - Get mono cursors Y position. +;* Mono_Set_Cursor - Set the mono cursor to specified coordinates. +;* Mono_Clear_Screen - Clear the mono screen. +;* Mono_Scroll - Scroll the mono screen up. +;* Mono_Put_Char - Ouput a character to the mono screen. +;* Mono_Draw_Rect - Draw a box on the mono screen. +;* Mono_Text_Print - Print a string to the mono screen at a specified +;* position. +;* Mono_Print - Print a string to the mono screen. +;* Mono_View_Page - View a mono page. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + + DATASEG + +MonoEnabled DD 1 +MonoX DD 0 +MonoY DD 0 +MonoOff DD 0 + +CharData DB 0DAh,0C4h,0BFh,0B3h,0D9h,0C4h,0C0h,0B3h ; Single line + DB 0D5h,0CDh,0B8h,0B3h,0BEh,0CDh,0D4h,0B3h ; Double horz. + DB 0D6h,0C4h,0B7h,0BAh,0BDh,0C4h,0D3h,0BAh ; Double vert. + DB 0C9h,0CDh,0BBh,0BAh,0BCh,0CDh,0C8h,0BAh ; Double line. + + +; x,y,dist +BoxData DB 1,0,0 ; Upper left corner. + DB 1,0,1 ; Top edge. + DB 0,1,0 ; Upper right corner. + DB 0,1,2 ; Right edge. + DB -1,0,0 ; Bottom right corner. + DB -1,0,1 ; Bottom edge. + DB 0,-1,0 ; Bottom left corner. + DB 0,-1,2 ; Left edge. + DB 0,0,-1 ; End of list. + +PageMap DD 0,1,2,3,4,5,6,7 + + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Mono_Enable - Enable mono output. +;* +;* SYNOPSIS +;* Mono_Enable() +;* +;* void Mono_Enable(void); +;* +;* FUNCTION +;* Turn on the MonoEnabled flag. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Enable:NEAR + PROC Mono_Enable C NEAR + + mov [MonoEnabled],1 + ret + + ENDP Mono_Enable + + +;**************************************************************************** +;* +;* NAME +;* Mono_Disable - Disable mono output. +;* +;* SYNOPSIS +;* Mono_Disable() +;* +;* void Mono_Disable(void); +;* +;* FUNCTION +;* Turn off the MonoEnabled flag. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Disable:NEAR + PROC Mono_Disable C NEAR + + mov [MonoEnabled],0 + ret + + ENDP Mono_Disable + + +;**************************************************************************** +;* +;* NAME +;* Mono_X - Get mono cursors X position. +;* +;* SYNOPSIS +;* X = Mono_X() +;* +;* long Mono_X(void); +;* +;* FUNCTION +;* Return the X position of the mono screen cursor. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* X - X coordinate position. +;* +;**************************************************************************** + + GLOBAL C Mono_X:NEAR + PROC Mono_X C NEAR + + mov eax,[MonoX] + ret + + ENDP Mono_X + + +;**************************************************************************** +;* +;* NAME +;* Mono_Y - Get mono cursors Y position. +;* +;* SYNOPSIS +;* Y = Mono_Y() +;* +;* long Mono_Y(void); +;* +;* FUNCTION +;* Return the Y position of the mono screen cursor. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* Y - Y coordinate position. +;* +;**************************************************************************** + + GLOBAL C Mono_Y:NEAR + PROC Mono_Y C NEAR + + mov eax,[MonoY] + ret + + ENDP Mono_Y + + +;**************************************************************************** +;* +;* NAME +;* Mono_Set_Cursor - Set the mono cursor to specified coordinates. +;* +;* SYNOPSIS +;* Mono_Set_Cursor(X, Y) +;* +;* void Mono_Set_Cursor(long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* X - X coordinate position. +;* Y - Y coordinate position. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Set_Cursor:NEAR + PROC Mono_Set_Cursor C NEAR USES eax ebx edx + + ARG xpos:DWORD + ARG ypos:DWORD + + cmp [MonoEnabled],0 + je short ??fini + + mov eax,[ypos] + mov ah,80 + imul ah + add eax,[xpos] + mov ebx,eax + +; Update cursor position. + + mov dx,03B4h + mov al,0Eh ;High byte register set. + out dx,al + inc dx + mov al,bh + out dx,al ;Set high byte. + + dec dx + mov al,0Fh ;Low byte register set. + out dx,al + inc dx + mov al,bl + out dx,al ;Set low byte. + +; Update the globals. + + add ebx,ebx + mov [MonoOff],ebx + mov eax,[xpos] + mov [MonoX],eax + mov eax,[ypos] + mov [MonoY],eax + +??fini: + ret + + ENDP Mono_Set_Cursor + + +;**************************************************************************** +;* +;* NAME +;* Mono_Clear_Screen - Clear the mono screen. +;* +;* SYNOPSIS +;* Mono_Clear_Screen() +;* +;* void Mono_Clear_Screen(void); +;* +;* FUNCTION +;* Clear the mono screen and set the mono cursor to the upperleft corner +;* of the screen. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Clear_Screen:NEAR + PROC Mono_Clear_Screen C NEAR USES es eax ecx edi + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Clear the mono screen + + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + mov edi,0B0000h ;EDI = Mono screen address + xor eax,eax ;Set char & attributes to 0 + mov ecx,8000h/4 ;Number of longwords to clear + rep stosd ;Clear the mono screen. + call Mono_Set_Cursor C,eax,eax + +??fini: + ret + + ENDP Mono_Clear_Screen + + +;**************************************************************************** +;* +;* NAME +;* Mono_Scroll - Scroll the mono screen up. +;* +;* SYNOPSIS +;* Mono_Scroll(Lines) +;* +;* void Mono_Scroll(long); +;* +;* FUNCTION +;* Move the contents of the mono screen up the specified number of lines +;* while clearing out the bottom lines. +;* +;* INPUTS +;* Lines - Number of lines to scroll the screen up. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Scroll:NEAR + PROC Mono_Scroll C NEAR USES es eax ebx ecx esi edi + ARG lines:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Exit if lines to scroll is 0. + + mov eax,[lines] + or eax,eax + je short ??fini + +; Move the screen data up the specified lines + + mov ebx,eax + +??looper: + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + push ds ;Save DS selector + mov ds,ax ;Set DS selector to first MB + + mov ecx,((80*24)/2) ;Number of DWORDs to move + mov esi,0B00A0h + mov edi,0B0000h + rep movsd + + pop ds ;Restore DS selector + dec [MonoY] + sub [MonoOff],(80*2) + + xor eax,eax + mov ecx,(80/2) + rep stosd + + dec ebx + jne ??looper + +??fini: + ret + + ENDP Mono_Scroll + + +;**************************************************************************** +;* +;* NAME +;* Mono_Put_Char - Ouput a character to the mono screen. +;* +;* SYNOPSIS +;* Mono_Put_Char(Character, Attributes) +;* +;* void Mono_Put_Char(long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Character - ASCII character to output. +;* Attributes - Display attributes +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Put_Char:NEAR + PROC Mono_Put_Char C NEAR USES es eax edi + ARG character:DWORD + ARG attrib:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Output character to the mono screen + + cld + + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + mov edi,0B0000h ;EDI = mono screen + add edi,[MonoOff] ;Add cursor offset + mov eax,[character] + mov ah,[BYTE PTR attrib] + stosw + +; Update cursor position. + + inc [MonoX] ; X position moves. + call Mono_Set_Cursor C,[MonoX],[MonoY] + +??fini: + ret + + ENDP Mono_Put_Char + + +;**************************************************************************** +;* +;* NAME +;* Mono_Draw_Rect - Draw a box on the mono screen. +;* +;* SYNOPSIS +;* Mono_Draw_Rect(X, Y, Width, Height, Attributes, Thickness) +;* +;* void Mono_Draw_Rect(); +;* +;* FUNCTION +;* Draw a rectangle text box on the mono screen. +;* +;* INPUTS +;* X - X coordinate position of upperleft corner. +;* Y - Y coordinate position of upperleft corner. +;* Width - Desired width. +;* Height - Desired height. +;* Attributes - Display attributes. +;* Thickness - Line thickness. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Draw_Rect:NEAR + PROC Mono_Draw_Rect C NEAR + ARG xpos:DWORD + ARG ypos:DWORD + ARG width:DWORD + ARG height:DWORD + ARG attrib:DWORD + ARG thick:DWORD + +; Exit if mono disabled + + pushad + cmp [MonoEnabled],0 + je ??fini + +; Select the character table for the desired line thickness + + mov edi,OFFSET CharData + mov cl,3 + mov eax,[thick] + and eax,011b + shl eax,cl + add edi,eax + +; Prep width and height. + + cmp [width],2 + jb ??fini + + cmp [height],2 + jb ??fini + + sub [width],2 + sub [height],2 + +; Set cursor position to upperleft corner of box + + push [MonoY] + push [MonoX] ;Save current cursor position + call Mono_Set_Cursor C,[xpos],[ypos] + +; Draw the rectangle + + mov esi,OFFSET BoxData + +; Determine the number of characters to output + +??drawloop: + mov ecx,[width] + cmp [BYTE PTR esi+2],1 + je short ??gotlen + + mov ecx,[height] + cmp [BYTE PTR esi+2],2 + je short ??gotlen + + mov ecx,1 + +??gotlen: + jecxz ??donerun + +??runloop: + xor eax,eax + mov al,[BYTE PTR edi] + call Mono_Put_Char C,eax,[attrib] ;Output the character. + + mov al,[BYTE PTR esi+1] + cbw + cwde + add eax,[MonoY] + push eax + mov al,[BYTE PTR esi] + cbw + cwde + add eax,[MonoX] + dec eax ; Undo cursor advance. + push eax + call Mono_Set_Cursor ; Properly advance cursor. + add sp,8 + loop ??runloop + +; Advance to next control entry. + +??donerun: + add esi,3 + inc edi + cmp [BYTE PTR esi+2],-1 + jne ??drawloop + +; Restore cursor to original position. + + call Mono_Set_Cursor + add sp,8 + +??fini: + popad + ret + + ENDP Mono_Draw_Rect + + +;**************************************************************************** +;* +;* NAME +;* Mono_Text_Print - Print a string to the mono screen at a specified +;* position. +;* +;* SYNOPSIS +;* Mono_Text_Print(String, X, Y, Attributes, Update) +;* +;* void Mono_Text_Print(char *, long, long, long, long); +;* +;* FUNCTION +;* Print a NULL terminated string to the mono screen at the specified +;* cooridinates and attributes. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* X - X coordinate position. +;* Y - Y coordinate position. +;* Attributes - Display attributes +;* Update - Update cursor position flag. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C _Mono_Text_Print:NEAR + PROC _Mono_Text_Print C NEAR USES eax esi + ARG text:NEAR PTR + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + ARG update:DWORD + +; Save the current cursor position. + + push [MonoY] + push [MonoX] + cmp [text],0 ;Exit if the string is NULL + je short ??fini + + call Mono_Set_Cursor C,[xpos],[ypos] + +; Print string to the mono screen + + mov esi,[text] ;Text pointer + +??charloop: + mov eax,[esi] + inc esi + or al,al ;Stop on a NULL + je short ??fini + + cmp al,13 ;Special processing for '\r' + je short ??cr + +; Output character to mono screen + +??normal: + xor ah,ah + call Mono_Put_Char C,eax,[attrib] + +; Perform adjustments if wrapping past right margin. + + cmp [MonoX],80 + jb short ??nowrap + + inc [ypos] + call Mono_Set_Cursor C,0,[ypos] + jmp short ??nowrap + + ; Move to start of next line. + +??cr: + inc [ypos] + call Mono_Set_Cursor C,[xpos],[ypos] + +; Scroll the monochrome screen if necessary. + +??nowrap: + cmp [MonoY],25 + jb short ??noscroll + + call Mono_Scroll C,1 + dec [ypos] + +??noscroll: + jmp short ??charloop + +??fini: + cmp [update],0 + jne short ??noupdate + + call Mono_Set_Cursor + +??noupdate: + pop eax + pop eax + ret + + ENDP _Mono_Text_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_Text_Print - Print a string to the mono screen. (ASM call) +;* +;* SYNOPSIS +;* Mono_Text_Print(String, X, Y, Attributes) +;* +;* void Mono_Text_Print(char *, long, long, long); +;* +;* FUNCTION +;* Print a NULL terminated string to the mono screen at the specified +;* cooridinates and attributes. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* X - X coordinate position. +;* Y - Y coordinate position. +;* Attributes - Display attributes +;* +;* RESULT +;* NONE +;* +;* SEE ALSO +;* _Mono_Text_Print +;* +;**************************************************************************** + + GLOBAL C Mono_Text_Print:NEAR + PROC Mono_Text_Print C NEAR USES + ARG text:NEAR PTR + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + + call _Mono_Text_Print C,[text],[xpos],[ypos],[attrib],0 + +??fini: + ret + + ENDP Mono_Text_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_Print - Print a string to the mono screen. +;* +;* SYNOPSIS +;* Mono_Print(String) +;* +;* void Mono_Print(char *); +;* +;* FUNCTION +;* Print a string to the mono screen at the current cursor position and +;* update the cursor position. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Print:NEAR + PROC Mono_Print C NEAR + ARG text:NEAR PTR + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + + call _Mono_Text_Print C,[text],[MonoX],[MonoY],2,1 + +??fini: + ret + + ENDP Mono_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_View_Page - View a mono page. +;* +;* SYNOPSIS +;* Oldpage = Mono_View_Page(Page) +;* +;* long Mono_View_Page(long); +;* +;* FUNCTION +;* Displays the specified page in displayable mono memory. +;* +;* INPUTS +;* Page - Page to view. +;* +;* RESULT +;* Oldpage - Previous page. +;* +;**************************************************************************** + + GLOBAL C Mono_View_Page:NEAR + PROC Mono_View_Page C NEAR USES ds es eax ebx ecx edi esi + ARG page:DWORD + LOCAL oldpage:DWORD + +; Prepare the original page number for return to caller. + + cld + mov ebx,[PageMap] + mov [oldpage],ebx + +; Exit of mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; If the desired page is already displayed, then don't do anything. + + mov eax,[page] + cmp eax,ebx + je short ??fini + +; Verify that page specified is legal. + + cmp eax,7 + ja short ??fini + +; Find where the logical page to display is actually located. + + mov ecx,8 + push ds + pop es + lea edi,[PageMap] + repne scasw + neg ecx + add ecx,7 ; CX = where desired page is located. + +; Swap the page ID bytes in the PageMap array. + + sub edi,4 + mov ebx,[PageMap] + mov eax,[edi] + mov [edi],ebx + mov [PageMap],eax + + shl ecx,8 + add ecx,eax + mov esi,ecx + + IF PHARLAP_TNT + mov ax,034h + mov ds,ax + ENDIF + + mov edi,0B0000h + +; Exchange the two pages. + + mov ecx,1000H/4 + +??looper: + mov edx,[edi] + mov ebx,[esi] + mov [edi],ebx + mov [esi],edx + add esi,4 + add edi,4 + loop ??looper + +; Return with the original page number. + +??fini: + mov eax,[oldpage] + ret + + ENDP Mono_View_Page + + END + diff --git a/VQ/VQM32/MONO.H b/VQ/VQM32/MONO.H new file mode 100644 index 0000000..880055f --- /dev/null +++ b/VQ/VQM32/MONO.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +#ifndef VQMMONO_H +#define VQMMONO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mono.h +* +* DESCRIPTION +* Mono screen definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Mono_Enable(void); +void cdecl Mono_Disable(void); +void cdecl Mono_Set_Cursor(long x, long y); +void cdecl Mono_Clear_Screen(void); +void cdecl Mono_Scroll(long lines); +void cdecl Mono_Put_Char(long character, long attrib); +void cdecl Mono_Draw_Rect(long x, long y, long w, long h, long attrib, + long thick); + +void cdecl Mono_Text_Print(void const *text, long x, long y, long attrib); +void cdecl Mono_Print(void const *text); +short cdecl Mono_View_Page(long page); +short cdecl Mono_X(void); +short cdecl Mono_Y(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMMONO_H */ diff --git a/VQ/VQM32/PALETTE.ASM b/VQ/VQM32/PALETTE.ASM new file mode 100644 index 0000000..44e8833 --- /dev/null +++ b/VQ/VQM32/PALETTE.ASM @@ -0,0 +1,319 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* palette.asm +;* +;* DESCRIPTION +;* Hardware level palette routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* To write palette colors: +;* - Out color # to 3c8h +;* - Out RGB values to 3c9h (data must be written in three's; PEL address +;* register auto-increments after 3 reads or writes) +;* +;* A time interval of about 240 ns is required between successive reads/ +;* writes; on very fast machines, this means that the system may not be +;* able to handle a rapid-fire of RGB values. So, a "safe" routine is +;* provided that has wait states between each out. +;* +;* Reference: Progammers Guide to the EGA & VGA Cards, Ferraro, 2nd ed. +;* (Chapter 8.) +;* +;* Note that, if you set the palette in active scan, the screen will +;* flash; to prevent this, wait for vertical retrace (Vertical Blank +;* Interval), or turn the display off by using the Screen Off field in +;* the Clocking Mode register (Hmmmm....). +;* +;* To read palette colors: +;* - Out color # to 3c7h +;* - In RGB values from 3c9h (data must be read in three's; PEL address +;* register auto-increments after 3 reads or writes) +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* SetPalette - Set the palette without waiting to Vblank. +;* ReadPalette - Read the palette from the display adapter. +;* SetDAC - Set a single palette color in the DAC. +;* TranslatePalette - Translate 24-bit color to 15-bit color. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* SetPalette - Set the palette without waiting to Vblank. +;* +;* SYNOPSIS +;* SetPalette(Palette, Numbytes, SlowFlag) +;* +;* void SetPalette(char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Palette - Pointer to the palette to set. +;* NumBytes - Number of bytes of palette to transfer (multiple of 3). +;* SlowFlag - Slow palette set flag. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C SetPalette:NEAR + PROC SetPalette C NEAR USES eax ecx edx esi ds + + ARG palette:NEAR PTR + ARG numbytes:DWORD + ARG slowpal:DWORD + + pushf + cld + + cmp [slowpal],0 ;Do slow palette? + jne ??safe_palette_routine + +;---------------------------------------------------------------------------- +; Fast palette set +;---------------------------------------------------------------------------- + + mov esi,[palette] + mov edx,PEL_WRITE_ADDR + xor al,al + out dx,al ;Select color to write too. + inc al ;Step to the next color for next loop + inc edx ;DX = PEL_DATA + mov ecx,[numbytes] ;Max # colors to set + rep outsb ;Write 256 * RGB out to the palette + popf + ret + +;---------------------------------------------------------------------------- +; Safe palette set +;---------------------------------------------------------------------------- + +??safe_palette_routine: + mov esi,[palette] + mov ecx,[numbytes] + mov edx,PEL_WRITE_ADDR + sub eax,eax + out dx,al + mov edx,PEL_DATA + +??Write_loop: + lodsb + out dx,al ;Red + jmp $+02 ;Delay (flush instruction cache) + + lodsb + out dx,al ;Green + jmp $+02 ;Delay (flush instruction cache) + + lodsb + out dx,al ;Blue + jmp $+02 ;Delay (flush instruction cache) + + sub cx,3 + ja ??Write_loop + + popf + ret + + ENDP SetPalette + + +;**************************************************************************** +;* +;* NAME +;* ReadPalette - Read the palette from the display adapter. +;* +;* SYNOPSIS +;* ReadPalette(Palette) +;* +;* void SetPalette(char *); +;* +;* FUNCTION +;* +;* INPUTS +;* Palette - Pointer buffer to copy palette into. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ReadPalette:NEAR + PROC ReadPalette C NEAR USES ecx edx edi es + + ARG palette:NEAR PTR + + mov edi,[palette] + mov ecx,256 + mov edx,PEL_READ_ADDR + sub eax,eax + out dx,al + mov edx,PEL_DATA + +??Read_loop: + in al,dx ;Red + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + in al,dx ;Green + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + in al,dx ;Blue + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + dec ecx + jnz ??Read_loop + ret + + ENDP ReadPalette + + +;**************************************************************************** +;* +;* NAME +;* SetDAC - Set a single palette color in the DAC. +;* +;* SYNOPSIS +;* SetDAC(ColorNum, Red, Green, Blue) +;* +;* void SetPalette(long, char, char); +;* +;* FUNCTION +;* +;* INPUTS +;* ColorNum - Position number in palette of color to set. +;* Red - Red gun value. +;* Green - Green gun value. +;* Blue - Blue gun value. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C SetDAC:NEAR + PROC SetDAC C NEAR USES edx + + ARG color_num:DWORD + ARG red:BYTE + ARG green:BYTE + ARG blue:BYTE + + mov edx,PEL_WRITE_ADDR + mov eax,[color_num] + out dx,al ;Set color position + inc edx + jmp $+02 ;Delay (flush instruction cache) + + mov al,[red] + out dx,al ;Set red gun + jmp $+02 ;Delay (flush instruction cache) + + mov al,[green] + out dx,al ;Set green gun + jmp $+02 ;Delay (flush instruction cache) + + mov al,[blue] + out dx,al ;Set blue gun + ret + + ENDP SetDAC + + +;**************************************************************************** +;* +;* NAME +;* TranslatePalette - Translate 24-bit color to 15-bit color. +;* +;* SYNOPSIS +;* TranslatePalette(Pal24, Pal15, NumBytes) +;* +;* void TranslatePalette(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Pal24 - Pointer to 24-bit palette. (Input) +;* Pal15 - Pointer to 15-bit palette. (Output) +;* NumBytes - Number of bytes to translate. (divisible by 3) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C TranslatePalette:NEAR + PROC TranslatePalette C NEAR USES ecx edx edi esi + + ARG pal24:NEAR PTR + ARG pal15:NEAR PTR + ARG numbytes:DWORD + + mov esi,[pal24] + mov edi,[pal15] + mov ecx,[numbytes] + +??TranslatePalette: + mov ah,[BYTE PTR esi] ;AH = red + mov al,[BYTE PTR esi+1] ;AL = green + mov dl,[BYTE PTR esi+2] ;DL = blue + shr ah,1 ;Red = lower 5 bits of AH + shl al,2 ;Green = upper 6 bits of AL + shr dl,1 ;Blue = lower 5 bits of DL + shl eax,2 ;Make room for blue + and al,0E0h ;Trim off bottom bit of green + or al,dl ;Load in blue bits + mov [WORD PTR edi],ax ;Store the value + add esi,3 ;Increment to next RGB values + add edi,2 ;Increment to next palette word + sub ecx,3 + ja ??TranslatePalette + ret + + ENDP TranslatePalette + + END diff --git a/VQ/VQM32/PALETTE.H b/VQ/VQM32/PALETTE.H new file mode 100644 index 0000000..1bf3abb --- /dev/null +++ b/VQ/VQM32/PALETTE.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMPALETTE_H +#define VQMPALETTE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Palette.h (32-Bit protected mode) +* +* DESCRIPTION +* Palette definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +void cdecl ReadPalette(void *palette); +void cdecl SetDAC(long color, long red, long green, long blue); +void cdecl TranslatePalette(void *pal24, void *pal15, long numbytes); + +#ifdef __cplusplus +} +#endif + +void SortPalette(unsigned char *pal, long numcolors); + +#endif /* VQMPALETTE_H */ + diff --git a/VQ/VQM32/PORTIO.ASM b/VQ/VQM32/PORTIO.ASM new file mode 100644 index 0000000..0e855b7 --- /dev/null +++ b/VQ/VQM32/PORTIO.ASM @@ -0,0 +1,116 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* portio.asm +;* +;* DESCRIPTION +;* I/O Port access. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* inp - Read a byte from a hardware port. +;* outp - Write a byte to a hardware port. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* inp - Read a byte from a hardware port. +;* +;* SYNOPSIS +;* Data = inp(PortID) +;* +;* short inp(unsinged short); +;* +;* FUNCTION +;* +;* INPUTS +;* PortID - Address if hardware port. +;* +;* RESULT +;* Data - Data read from port. +;* +;**************************************************************************** + + GLOBAL C inp:NEAR + PROC inp C NEAR USES edx + ARG port:WORD + + mov dx,[port] + xor eax,eax + in al,dx + ret + + ENDP inp + + +;**************************************************************************** +;* +;* NAME +;* outp - Write a byte to a hardware port. +;* +;* SYNOPSIS +;* outp(PortID, Value) +;* +;* void outp(unsinged short, short); +;* +;* FUNCTION +;* +;* INPUTS +;* PortID - Address if hardware port. +;* Value - Value to write. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C outp:NEAR + PROC outp C NEAR USES edx + ARG port:WORD + ARG value:WORD + + mov dx,[port] + mov ax,[value] + out dx,al + ret + + ENDP outp + + END diff --git a/VQ/VQM32/PORTIO.H b/VQ/VQM32/PORTIO.H new file mode 100644 index 0000000..b104412 --- /dev/null +++ b/VQ/VQM32/PORTIO.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +#ifndef VQMPORTIO_H +#define VQMPORTIO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Portio.h (32-Bit protected mode) +* +* DESCRIPTION +* Hardware port I/O +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifdef __BORLANDC__ + +#ifdef __cplusplus +extern "C" { +#endif + +short cdecl inp(unsigned short portid); +void cdecl outp(unsigned short portid, short value); + +#ifdef __cplusplus +} +#endif + +#endif /* __BORLANDC__ */ + +#endif /* VQMPORTIO_H */ + diff --git a/VQ/VQM32/PROFILE.CPP b/VQ/VQM32/PROFILE.CPP new file mode 100644 index 0000000..a571b65 --- /dev/null +++ b/VQ/VQM32/PROFILE.CPP @@ -0,0 +1,431 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* profile.c +* +* DESCRIPTION +* INI file processing. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* Get_Frame_Pathname - Get pathname for a given frame and file type. +* GetINIInt - Get an integer value from an INI file. +* GetINIString - Get a string from the INI file. +* +****************************************************************************/ + +#include +#include +#include +#include "profile.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +#define isspace(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')?1:0) + +static char *strtrim(char *string); +static long FileGets(FILE *fp, char *buf, long buflen); + + +/**************************************************************************** +* +* NAME +* Get_Frame_Pathname - Get pathname for a given frame and file type. +* +* SYNOPSIS +* Error = Get_Frame_Pathname(IniFile, Frame, Extension, Buffer) +* +* long Get_Frame_Pathname(char *, long, char *, char *); +* +* FUNCTION +* +* INPUTS +* IniFile - Pointer to INI filename. +* Frame - Number of frame to get filename for. +* Extension - File extension type. +* Buffer - Pointer to buffer to put pathname into. +* +* RESULT +* Error - 0 if successful, or -1 if error. +* +***************************************************************************/ + +long Get_Frame_Pathname(char *inifile, long anim_frame, char *ext, + char *outbuf) +{ + char rootdir[_MAX_PATH]; // Root directory from INI file + char extdir[_MAX_PATH]; // this extension's directory + char entry_name[40]; // INI entry name + char inibuf[80]; // string returned from INI file + char *prefix; // 4-char prefix for this scene + char *startstr; // starting frame #, string + char *endstr; // ending frame #, string + char *palstr; // palette filename string + long startnum; // scene's starting frame # + long endnum; // scene's ending frame # + long total_frames; // accumulated frame total + long scene_frames; // # frames in a given scene + long scene_num; // scene # + long file_frame; // file's frame # + long rc; + + /* Get directory for this file type */ + GetINIString("Path", "Root", "", rootdir, 80, inifile); + + if (rootdir[strlen (rootdir) - 1] != '\\') { + strcat(rootdir,"\\"); + } + + GetINIString("Path", ext, "", extdir, 80, inifile); + + if (extdir[strlen (extdir) - 1] != '\\') { + strcat(extdir,"\\"); + } + + /* VQG is a special case: + * + * The VQG files are named based upon the 1st 4 characters of the 'Name' + * entry in the INI file, and their numbers match the actual animation + * frame numbers, not the scene frame numbers. + */ + if (!stricmp(ext, "VQG")) { + GetINIString("Common", "Name", "", inibuf, 80, inifile); + + if (strlen(inibuf) > 4) { + inibuf[4] = 0; + } + + sprintf(outbuf,"%s%s%s%04d.%s",rootdir,extdir,inibuf,anim_frame,ext); + return (0); + } + + /*------------------------------------------------------------------------- + * Loop through scenes until the desired frame # is found + *-----------------------------------------------------------------------*/ + total_frames = 0; + scene_num = 1; + + while (1) { + + /* Get this scene's entry */ + sprintf(entry_name, "Scene%d", scene_num); + rc = GetINIString("Scenes",entry_name,"",inibuf,80,inifile); + + if (rc == 0) { + return (-1); + } + + /* Parse the INI entry */ + prefix = strtok(inibuf, ","); + startstr = strtok(NULL, ","); + endstr = strtok(NULL, ","); + palstr = strtok(NULL, ","); + + if ((prefix == NULL) || (startstr == NULL) || (endstr == NULL)) { + return (-1); + } + + startnum = atoi(startstr); + endnum = atoi(endstr); + scene_frames = ((endnum - startnum) + 1); + + /* requested frame is found */ + if (anim_frame < (total_frames + scene_frames)) { + + /* Palette is a special case */ + if (!stricmp(ext, "PAL")) { + if (palstr == NULL) { + return (-1); + } else { + sprintf(outbuf, "%s%s%s.PAL", rootdir, extdir, palstr); + return (0); + } + } else { + file_frame = ((anim_frame - total_frames) + startnum); + sprintf(outbuf,"%s%s%s%04d.%s",rootdir,extdir,prefix,file_frame,ext); + return (0); + } + } + + /* Frame not found; go to next scene */ + total_frames += scene_frames; + scene_num++; + } +} + + +/**************************************************************************** +* +* NAME +* GetINIInt - Get an integer value from an INI file. +* +* SYNOPSIS +* Value = GetINIInt(Section, Entry, Default, ININame) +* +* long GetINIInt(char *, char *, long, char *); +* +* FUNCTION +* Retrieve an integer value from the INI file at the specified 'Section' +* and 'Entry' fields. If no value is defined then return the passed in +* 'Default' value. +* +* INPUTS +* Section - Pointer to section name. +* Entry - Pointer to entry name. +* Default - Default value. +* ININame - Pointer to INI filename. +* +* RESULT +* Value - Integer value from INI file or 'Default'. +* +****************************************************************************/ + +long GetINIInt(char const *section, char const *entry, long deflt, + char *fname) +{ + char buffer[20]; + + sprintf(buffer, "%d", deflt); + GetINIString(section, entry, buffer, buffer, sizeof(buffer), + fname); + + return (atoi(buffer)); +} + + +/**************************************************************************** +* +* NAME +* GetINIString - Get a string from the INI file. +* +* SYNOPSIS +* Length = GetINIString(Section, Entry, Default, Buffer, +* Length, ININame) +* +* long GetINIString(char *, char *, char *, char *, long, +* char *); +* +* FUNCTION +* +* INPUTS +* Section - Pointer to section name. +* Entry - Pointer to entry name. +* Default - Pointer to default string. +* Buffer - Pointer to buffer to copy string into. +* Length - Maximum length of string. +* ININame - Pointer to INI filename. +* +* RESULT +* Length - Length of string copied into the buffer. +* +****************************************************************************/ + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname) +{ + FILE *fp; + long retval; + char txt[80]; + char secname[40]; + long len; + char *workptr; + + /* Copy default value in case entry isn't found */ + strncpy(retbuffer, def, (retlen - 1)); + retbuffer[retlen - 1] = 0; + retval = min(strlen(def), (unsigned)retlen); + + /* Open the file */ + if ((fp = fopen(fname, "rt")) == NULL) { + return (retval); + } + + /* Generate section name for search */ + sprintf(secname, "[%s]", section); + len = strlen(secname); + + /* Scan file for section name */ + while (1) { + + /* Read line; return if end-of-file */ + if (FileGets(fp,txt,80)!=0) { + fclose(fp); + return (retval); + } + + /* Skip comments */ + if (txt[0] == ';') continue; + + /* Parse a section name */ + if (txt[0] == '[') { + if (!memicmp(secname, txt, len)) break; + } + } + + /* Scan file for desired entry */ + len = strlen(entry); + + while (1) { + + /* Read line; return if end-of-file */ + if (FileGets(fp, txt, 80) != 0) { + fclose(fp); + return (retval); + } + + /* Skip comments */ + if (txt[0] == ';') continue; + + /* Return if start of next section reached */ + if (txt[0] == '[') { + fclose(fp); + return (retval); + } + + /* Entry found; parse it */ + if (!memicmp(entry, txt, len) && (isspace(txt[len]) + || txt[len] == '=')) { + + fclose(fp); + + /* Find '=' character */ + workptr = strchr(txt, '='); + + /* Return if not found */ + if (workptr == NULL) return (retval); + + /* Skip past '=' */ + workptr++; + + /* Skip white space */ + while (isspace(*workptr) && strlen(workptr) > 0) { + workptr++; + } + + /* Return if no string left */ + if ((*workptr) == 0) return (retval); + + strtrim(workptr); + strcpy(retbuffer,workptr); + + return (strlen(workptr)); + } + } +} + + +/**************************************************************************** +* +* NAME +* strtrim - Trim off trailing spaces from a string. +* +* SYNOPSIS +* String = strtrim(String) +* +* char *strtrim(char *); +* +* FUNCTION +* +* INPUTS +* String - Pointer to string to trim. +* +* RESULT +* String - Pointer to trimmed string. +* +****************************************************************************/ + +static char *strtrim(char *string) +{ + long i; + + /* Return if NULL ptr or zero-length string */ + if ((string == NULL) || (strlen(string) == 0)) { + return (string); + } + + /* Find 1st non-white-space character from the right */ + i = (strlen(string) - 1); + + while ((i > 0) && isspace(string[i])) { + i--; + } + + /* Set end of string */ + i++; + string[i] = 0; + + return (string); +} + + +/**************************************************************************** +* +* NAME +* FileGets - A better fgets. +* +* SYNOPSIS +* Error = FileGets(FilePtr, Buffer, Length) +* +* long FileGets(FILE *, char *, long); +* +* FUNCTION +* +* INPUTS +* FilePtr - File pointer. +* Buffer - Pointer to buffer to fill. +* Length - Maximum length of buffer. +* +* RESULT +* Error = 0 if successfull, or -1 if error. +* +****************************************************************************/ + +static long FileGets(FILE *fp, char *buf, long buflen) +{ + if (fgets(buf, buflen, fp)) { + buf[(strlen(buf) - 1)] = 0; + return (0); + } else { + return (-1); + } +} + diff --git a/VQ/VQM32/PROFILE.H b/VQ/VQM32/PROFILE.H new file mode 100644 index 0000000..d4641bd --- /dev/null +++ b/VQ/VQM32/PROFILE.H @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +#ifndef VQMPROFILE_H +#define VQMPROFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Profile.h (32-Bit protected mode) +* +* DESCRIPTION +* INI file profiling definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ +long GetINIInt(char const *section, char const *entry, + long deflt, char *fname); + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname); + +long Get_Frame_Pathname(char *inifile,long anim_frame,char *ext, + char *outbuf); + +#endif /* VQMPROFILE_H */ + diff --git a/VQ/VQM32/REALMODE.H b/VQ/VQM32/REALMODE.H new file mode 100644 index 0000000..593151b --- /dev/null +++ b/VQ/VQM32/REALMODE.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +#ifndef VQMREALMODE_H +#define VQMREALMODE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* realmode.h +* +* DESCRIPTION +* Real-mode interfacing definitions and equates. Many of the definitions +* and descriptions in this file were taken from other sources and +* compiled here for use in MISC32 library. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 6, 1995 +* +****************************************************************************/ + +/* REALPTR: Real-mode pointer (segment:offset16). + * + * The REALPTR data type is used in protected mode to hold real-mode + * pointers. The type is an unsigned long value, were the upper 16 bits + * are the segment number and the lower 16 bit are an offset. This type + * and the associated macros are identical to that of the PHARLAP "pltypes.h" + * definitions for easy of conversion to WATCOM/4GW. + */ +typedef unsigned long REALPTR; + +#define RP_OFF(rp) ((unsigned short)(((unsigned long)(rp)) & 0xFFFF)) +#define RP_SEG(rp) ((unsigned short)(((unsigned long)(rp)) >> 16)) +#define RP_SET(rp, off, seg) (rp = ((unsigned long)(seg) << 16) + (off)) +#define RP_INCR(rp, incr) (rp += ((unsigned long)(incr)) & 0xFFFF) + +#define MK_PTR(off, seg) (void *)((((unsigned long)seg&0xFFFF)<<4)+off) + +/* RMInfo: Real-mode interrupt call structure. + * + * Information that needs to be passed down to the real-mode interrupt is + * transfered using this structure. The address to this protected-mode + * structure (allocated by user) is passed into DPMI function 0x300. DOS/4GW + * will then use this information to set up the real-mode registers, switch + * to real-mode and then execute the interrupt in real-mode. + */ +typedef struct _RMInfo { + long edi; + long esi; + long ebp; + long reservedbysystem; + long ebx; + long edx; + long ecx; + long eax; + short flags; + short es,ds,fs,gs,ip,cs,sp,ss; +} RMInfo; + +#endif /* VQMREALMODE_H */ + diff --git a/VQ/VQM32/SORTPAL.CPP b/VQ/VQM32/SORTPAL.CPP new file mode 100644 index 0000000..e56a6e5 --- /dev/null +++ b/VQ/VQM32/SORTPAL.CPP @@ -0,0 +1,315 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* sortpal.c +* +* DESCRIPTION +* Palette sorting routines. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SortPalette - Sort a palette. +* Comp_Luminance - Compare the luminace of two 24-bit palette entries. +* Comp_HSV - Compare the HSV of two 24-bit palette entries. +* RGB_To_HSV - Convert RGB color to HSV color. +* +****************************************************************************/ + +#include +#include +#include "palette.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* HSV color model */ +#define DIVIDE_WITH_ROUND(n,d) (unsigned short)(((n)/(d))+ \ + (unsigned short)(((n)%(d)) >= (((d)+1)>>1))) + +#define HSV_BASE 255 +#define HUE_WEIGHT 10L +#define SATURATION_WEIGHT 100L +#define VALUE_WEIGHT 1000L + +/* Prototypes */ +static int Comp_Luminance(const void *elem1, const void *elem2); +static int Comp_HSV(const void *elem1, const void *elem2); +static void RGB_To_HSV(unsigned short r, unsigned short g, unsigned short b, + unsigned short *h, unsigned short *s, unsigned short *v); + + +/**************************************************************************** +* +* NAME +* SortPalette - Sort a palette. +* +* SYNOPSIS +* SortPalette(Palette, NumColors) +* +* void SortPalette(unsigned char *, long); +* +* FUNCTION +* Sort the palette colors. +* +* INPUTS +* Palette - Pointer to palette to sort. +* NumColors - Number of colors in the palette. +* +* RESULT +* NONE +* +****************************************************************************/ + +void SortPalette(unsigned char *pal, long numcolors) +{ + qsort(pal, numcolors, 3, Comp_Luminance); + + pal[0] = 0; + pal[1] = 0; + pal[2] = 0; +} + + +/**************************************************************************** +* +* NAME +* Comp_Luminance - Compare the luminace of two 24-bit palette entries. +* +* SYNOPSIS +* Result = Comp_Luminance(Color1, Color2) +* +* long Comp_Luminance(void *, void *); +* +* FUNCTION +* Compare the luminace of the two colors and determine which color is +* brighter than the other. +* +* The computation used is: +* Luminance = (red * .299) + (green * .587) + (blue * .114) +* +* INPUTS +* Color1 - Pointer to palette entry. +* Color2 - Pointer to palette entry. +* +* RESULT +* Result - 0 = same, 1 = Color1 > Color2, -1 = Color1 < Color2 +* +****************************************************************************/ + +static int Comp_Luminance(const void *elem1, const void *elem2) +{ + unsigned char *pal; + long r,g,b; + long total1,total2; + + /* Compute luminance for color1 */ + pal = (unsigned char *)elem1; + r = ((long)pal[0]); + g = ((long)pal[1]); + b = ((long)pal[2]); + total1 = ((r * 19595L) + (g * 38470L) + (b * 7471L)); + + /* Compute luminance for color2 */ + pal = (unsigned char *)elem2; + r = ((long)pal[0]); + g = ((long)pal[1]); + b = ((long)pal[2]); + total2 = ((r * 19595L) + (g * 38470L) + (b * 7471L)); + + if (total1 < total2) { + return (-1); + } else if (total1 > total2) { + return (1); + } else { + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* Comp_HSV - Compare the HSV of two 24-bit palette entries. +* +* SYNOPSIS +* Result = Comp_HSV(Color1, Color2) +* +* long Comp_HSV(void *, void *); +* +* FUNCTION +* Compare the HSV color values of two colors and determine the +* relationship between the colors in the color space. +* +* INPUTS +* Color1 - Pointer to 1st palette entry. +* Color2 - Pointer to 2nd palette entry. +* +* RESULT +* Result - 0 = same, 1 = Color1 > Color2, -1 = Color1 < Color2 +* +****************************************************************************/ + +static int Comp_HSV(const void *elem1, const void *elem2) +{ + unsigned char *pal; + unsigned char r,g,b; + unsigned short h,s,v; + unsigned long key1,key2; + long retval; + + /* Convert 1st element to HSV */ + pal = (unsigned char *)elem1; + r = pal[0]; + g = pal[1]; + b = pal[2]; + + RGB_To_HSV((unsigned short)r,(unsigned short)g,(unsigned short)b,&h,&s,&v); + key1 = ((h * HUE_WEIGHT) + (s * SATURATION_WEIGHT) + (v * VALUE_WEIGHT)); + + /* Convert 2nd element to HSV */ + pal = (unsigned char *)elem2; + r = pal[0]; + g = pal[1]; + b = pal[2]; + + RGB_To_HSV((unsigned short)r,(unsigned short)g,(unsigned short)b,&h,&s,&v); + key2 = ((h * HUE_WEIGHT) + (s * SATURATION_WEIGHT) + (v * VALUE_WEIGHT)); + + if (key1 != key2) { + retval = ((key1 < key2) ? -1 : 1); + } else { + retval = 0; + } + + return (retval); +} + + +/*************************************************************************** +* +* NAME +* RGB_To_HSV - Convert RGB color to HSV color. +* +* SYNOPSIS +* RGB_To_HSV(R, G, B, H, S, V) +* +* void RGB_To_HSV(unsigned short, unsigned short, unsigned short, +* unsigned short *, unsigned short *, unsigned short *); +* +* FUNCTION +* Convert the RBG color to a HSV color. Assumes 8 bits per gun of R, G +* and B data. Also the HSV is based on a 255 degree scale rather than +* the more accurate 360 degree scale. +* +* INPUTS +* R - Red gun value. +* G - Green gun value. +* B - Blue gun value. +* H - Pointer to H value. (H will be set upon return of this function) +* S - Pointer to S value. (S will be set upon return of this function) +* V - Pointer to V value. (V will be set upon return of this function) +* +* RESULT +* NONE +* +***************************************************************************/ + +static void RGB_To_HSV(unsigned short r, unsigned short g, unsigned short b, + unsigned short *h, unsigned short *s, unsigned short *v) +{ + unsigned short m; + unsigned short r1; + unsigned short g1; + unsigned short b1; + unsigned short tmp; + + /* Set hue to default. */ + *h = 0; + + /* Set v = Max(r,g,b) to find dominant primary color. */ + *v = ((r > g) ? r : g); + + if (b > *v) { + *v = b; + } + + /* Set m = min(r,g,b) to find amount of white. */ + m = ((r < g) ? r : g); + + if (b < m) { + m = b; + } + + /* Determine the normalized saturation. */ + if (*v != 0) { + *s = DIVIDE_WITH_ROUND((*v - m) * HSV_BASE, *v); + } else { + *s = 0; + } + + if (*s != 0) { + tmp = *v - m; + r1 = DIVIDE_WITH_ROUND((*v - r) * HSV_BASE, tmp); + g1 = DIVIDE_WITH_ROUND((*v - g) * HSV_BASE, tmp); + b1 = DIVIDE_WITH_ROUND((*v - b) * HSV_BASE, tmp); + + /* Find effect of second most predominant color. + * In which section of the hexagon of colors does the color lie? + */ + if ((*v) == r) { + if (m == g) { + *h = 5 * HSV_BASE + b1; + } else { + *h = 1 * HSV_BASE - g1; + } + } else { + if ((*v) == g) { + if (m == b) { + *h = 1 * HSV_BASE + r1; + } else { + *h = 3 * HSV_BASE - b1; + } + } else { + if (m == r) { + *h = 3 * HSV_BASE + g1; + } else { + *h = 5 * HSV_BASE - r1; + } + } + } + + /* Divide by six and round. */ + *h = DIVIDE_WITH_ROUND(*h, 6); + } +} diff --git a/VQ/VQM32/SOSCODEC.ASM b/VQ/VQM32/SOSCODEC.ASM new file mode 100644 index 0000000..34b700c --- /dev/null +++ b/VQ/VQM32/SOSCODEC.ASM @@ -0,0 +1,1271 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 ?? + + STRUC sCompInfo +lpSource DD ? ;Compressed data pointer +lpDest DD ? ;Uncompressed data pointer +dwCompSize DD ? ;Compressed size +dwUnCompSize DD ? ;Uncompressed size +wBitSize DW ? ;Bit size for decompression +wChannels DW ? ;number of channels + +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 + ENDS sCompInfo + + DATASEG + +;* 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 +dwCODECBytesProcessed DD 0 ; this is how many so far compressed +dwCODECTempStep DD 0 ; tempory storage for step value +wCODECMask DW 0 ; Current mask + CODESEG + +;**************************************************************************** +;* +;* 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 +;* sosCODECCompressData - Compress audio data. +;* +;* SYNOPSIS +;* Size = sosCODECCompressData(CompInfo, NumBytes) +;* +;* long sosCODECCompressData(_SOS_COMPRESS_INFO *, long); +;* +;* FUNCTION +;* Compress an audio data stream into 4:1 ADPCM. 16 bit data is +;* compressed 4:1, 8 bit data is compressed 2:1. +;* +;* INPUTS +;* CompInfo - Pointer to initialized compress information structure. +;* NumBytes - Number of bytes to compress. +;* +;* RESULT +;* Size - Size of compressed data. +;* +;**************************************************************************** + + GLOBAL C sosCODECCompressData:NEAR + PROC sosCODECCompressData C NEAR + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 ;16 bit requested? + jne short ??skipByteDivide ;no so skip divide + shr eax,1 ;divide size by 2 + +??skipByteDivide: + mov [dwCODECByteIndex],eax + mov esi,[(sCompInfo ebx).lpSource] ;ESI = source + mov edi,[(sCompInfo ebx).lpDest] ;EDI = dest + + cmp [(sCompInfo ebx).wChannels],2 ;stereo check + je ??mainloopl + +;------------------------------------------------------------------------ +; Mono start +;------------------------------------------------------------------------ + +??mainloop: + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bit ;no. goto 8 bit input + + movsx eax,[word ptr esi] ;Get 16bit sample + add esi,2 + jmp short ??computeDiff ;skip 8 bit load + +??input8Bit: + mov ah,[esi] ;Get 8bit sample + inc esi + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax + +??computeDiff: + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted] + sub eax,ecx ;sample-predicted + xor ecx,ecx ;clear ecx + cmp eax,0 ;Diff > = 0 + jge ??positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positive: + mov [(sCompInfo ebx).wCode],cx ;Store code + movsx ecx,[(sCompInfo ebx).wStep] ;Get step value + mov [dwCODECTempStep],ecx + mov edx,4 ;mask value (i think) + mov ecx,3 ;loop count + +??quantizeLoop: + cmp eax,[dwCODECTempStep] ;Diff < step ? + jl short ??nextQLoop ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoop: + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoop ; back to quatize loop + +;----------------------------------------------------------------------------------------- +; now i'v got the new diff and code is masked right +;----------------------------------------------------------------------------------------- + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex],1 ; is it even? (starts at 0) + jne short ??storeToken ; if so goto store token + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf],ax ;wCodeBuf=ax + jmp short ??calcDifference ;goto calcDifference + +??storeToken: + ; fetch new token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=code + shl eax,4 ;shift low nibble to high + or ax,[(sCompInfo ebx).wCodeBuf] ;or in the stored nibble + mov [edi],al ;*dest=al + inc edi ;dest++ + +??calcDifference: + mov [(sCompInfo ebx).dwDifference],0 ;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4 + add [(sCompInfo ebx).dwDifference],ecx ;difference+=step + +??no4: + test eax,2 ;Check 0010 + je short ??no2 + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>1 + +??no2: + test eax,1 ;Check 0001 + je short ??no1 + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>2 + +??no1: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8 + neg [(sCompInfo ebx).dwDifference] ;Negate diff because sign bit was set + +??no8: + mov eax,[(sCompInfo ebx).dwPredicted] + add eax,[(sCompInfo ebx).dwDifference] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflow + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflow: + cmp eax,0FFFF8000h + jg short ??noUnderflow + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflow: + mov [(sCompInfo ebx).dwPredicted],eax ;store into predicted + xor ecx,ecx + mov cx,[(sCompInfo ebx).wCode] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex],ax ;wIndex+=ax + cmp [(sCompInfo ebx).wIndex],8000h ; check if wIndex < 0 + 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 ; }while !0 + + jmp ??exitout +;----------------------------------------------------------------------- +;Stereo Left Side +;----------------------------------------------------------------------- + +??mainloopl: +; determine bit size for input ;do{ + + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bitl ;no. goto 8 bit input ** + + movsx eax,[word ptr esi] ;load next word from source + add esi,4 ;inc source by 2 words ** + jmp short ??computeDiffl ;skip 8 bit load ** + +??input8Bitl: + mov ah,[esi] ;Get 8 bit sample + add esi,2 ;inc source by 2 bytes ** + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax ;sign extend into eax + +??computeDiffl: + ; compute difference + + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted] + ;load predicted (starts at 0) + sub eax,ecx ;difference=sample-preditcted + + ; check if dwDifference > 0. ECX is the + ; sign bit, it is initialized to positive. + + xor ecx,ecx ;clear ecx + cmp eax,0 ;if(difference>=0) + jge short ??positivel ;goto positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positivel: + mov [(sCompInfo ebx).wCode],cx ;Store code from cx into struct + + ; set up to quantize difference. initialize + ; wCODECTempStep = step value. + + movsx ecx,[(sCompInfo ebx).wStep] ;ecx=step value(starts at 7) + mov [dwCODECTempStep],ecx ;tempstep=step + mov edx,4 ;edx=4 mask value (i think) + mov ecx,3 ;ecx is loop number so loop 3 times + +??quantizeLoopl: + ; check to see if difference > tempstep value. + + cmp eax,[dwCODECTempStep] ;if(difference < tempstep) + jl short ??nextQLoopl ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoopl: + + ; shift down tempstep and mask + + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoopl ; back to quatize loop +;------------------------------------------------------------------------------------------ +; now i'v got the new diff and code is masked right + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex],1 ; is it even? (starts at 0) + jne short ??storeTokenl ; if so goto store token ** + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf],ax ;wCodeBuf=ax + jmp short ??calcDifferencel ;goto calcDifference ** + +??storeTokenl: + ; fetch new token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=code + shl eax,4 ;shift low nibble to hign nibble + or ax,[(sCompInfo ebx).wCodeBuf] ;or in the stored nibble + mov [edi],al ;*dest=al + add edi,2 ;dest+=2 ** + +??calcDifferencel: + mov [(sCompInfo ebx).dwDifference],0;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4l ; ** + add [(sCompInfo ebx).dwDifference],ecx ;difference+=step + +??no4l: + test eax,2 ;Check 0010 + je short ??no2l ; ** + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>1 + +??no2l: + test eax,1 ;Check 0001 + je short ??no1l ; ** + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>2 + +??no1l: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8l + + ;Negate diff because sign bit was set + + neg [(sCompInfo ebx).dwDifference] + +??no8l: + mov eax,[(sCompInfo ebx).dwPredicted] + add eax,[(sCompInfo ebx).dwDifference] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflowl + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflowl: + cmp eax,0FFFF8000h + jg short ??noUnderflowl + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflowl: + mov [(sCompInfo ebx).dwPredicted],eax ;store into predicted + xor ecx,ecx ;adjust index + mov cx,[(sCompInfo ebx).wCode] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex],ax ;wIndex+=ax + + + cmp [(sCompInfo ebx).wIndex],8000h ;check if wIndex < 0 + jb short ??checkOverflowl ; ** + mov [(sCompInfo ebx).wIndex],0 ; reset index to zero + jmp short ??adjustStepl ; ** + +??checkOverflowl: + ; check if wIndex > 88 + + cmp [(sCompInfo ebx).wIndex],88 + jbe short ??adjustStepl ; ** + + ; reset index to 88 + + mov [(sCompInfo ebx).wIndex],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 ; sub 2 for stereo ** + jne ??mainloopl ; }while !0 ** + +;------------------------------------------------------------------------- +;Right channel re-set up varibles +;------------------------------------------------------------------------- + + mov eax,[wBytes] + mov esi,[(sCompInfo ebx).lpSource] ; point to source buffer + mov edi,[(sCompInfo ebx).lpDest] ; point to destination buffer + inc esi ; skip first byte + inc edi ; ship first byte + +; Check for 16 bit compression + + cmp [(sCompInfo ebx).wBitSize],16 + je short ??do16bit + mov [dwCODECByteIndex],eax + jmp short ??mainloopr + +??do16bit: + shr eax,1 ;16 bit so half as many bytes + inc esi ;16 bit so 1 more byte to skip + mov [dwCODECByteIndex],eax + + +;----------------------------------------------------------------------- +;Start of Stereo Right Side +;----------------------------------------------------------------------- + +??mainloopr: +; determine bit size for input ;do{ + + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bitr ;no. goto 8 bit input ** + + movsx eax,[word ptr esi] ;load next word from source + add esi,4 ;inc source by 2 words ** + jmp short ??computeDiffr ;skip 8 bit load ** + +??input8Bitr: + mov ah,[esi] ;Get 8 bit sample + add esi,2 ;inc source by 2 bytes ** + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax ;sign extend into eax + +??computeDiffr: + ; compute difference + + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted2] + ;load predicted (starts at 0) + sub eax,ecx ;difference=sample-preditcted + + ; check if dwDifference > 0. ECX is the + ; sign bit, it is initialized to positive. + + xor ecx,ecx ;clear ecx + cmp eax,0 ;if(difference>=0) + jge short ??positiver ;goto positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positiver: + mov [(sCompInfo ebx).wCode2],cx ;Store code from cx into struct + + ; set up to quantize difference. initialize + ; wCODECTempStep = step value. + + movsx ecx,[(sCompInfo ebx).wStep2] ;ecx=step value(starts at 7) + mov [dwCODECTempStep],ecx ;tempstep=step + mov edx,4 ;edx=4 mask value (i think) + mov ecx,3 ;ecx is loop number so loop 3 times + +??quantizeLoopr: + ; check to see if difference > tempstep value. + + cmp eax,[dwCODECTempStep] ;if(difference < tempstep) + jl short ??nextQLoopr ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode2],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoopr: + + ; shift down tempstep and mask + + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoopr ; back to quatize loop +;------------------------------------------------------------------------------------------ +; now i'v got the new diff and code is masked right + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference2],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex2],1 ; is it even? (starts at 0) + jne short ??storeTokenr ; if so goto store token ** + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode2] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf2],ax ;wCodeBuf=ax + jmp short ??calcDifferencer ;goto calcDifference ** + +??storeTokenr: + xor eax,eax + mov ax,[(sCompInfo ebx).wCode2] ;ax=code + shl eax,4 ;shift low nibble to hign nibble + or ax,[(sCompInfo ebx).wCodeBuf2] ;or in the stored nibble + mov [edi],al ;*dest=al + add edi,2 ;dest+=2 ** + +??calcDifferencer: + mov [(sCompInfo ebx).dwDifference2],0 ;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep2] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode2] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4r + add [(sCompInfo ebx).dwDifference2],ecx ;difference+=step + +??no4r: + test eax,2 ;Check 0010 + je short ??no2r + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>1 + +??no2r: + test eax,1 ;Check 0001 + je short ??no1r + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>2 + +??no1r: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8r + neg [(sCompInfo ebx).dwDifference2] + +??no8r: + ; add difference to predicted value. + mov eax,[(sCompInfo ebx).dwPredicted2] + add eax,[(sCompInfo ebx).dwDifference2] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflowr + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflowr: + cmp eax,0FFFF8000h + jg short ??noUnderflowr + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflowr: + mov [(sCompInfo ebx).dwPredicted2],eax ;store into predicted + xor ecx,ecx ;adjust index + mov cx,[(sCompInfo ebx).wCode2] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex2],ax ;wIndex+=ax + cmp [(sCompInfo ebx).wIndex2],8000h ;check if wIndex < 0 + jb short ??checkOverflowr + mov [(sCompInfo ebx).wIndex2],0 ;reset index to zero + jmp short ??adjustStepr + +??checkOverflowr: + + cmp [(sCompInfo ebx).wIndex2],88 ;check if wIndex > 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 ; sub 2 for stereo + jne ??mainloopr ; }while !0 + + +;------------------------------------------------------------------------- +;Final clean up +;------------------------------------------------------------------------- + +??exitout: + ; save off ESI and EDI back into compress info structure. + +; mov [(sCompInfo ebx).lpSource],esi +; mov [(sCompInfo ebx).lpDest],edi + + ; set up return value for number of bytes processed. + + mov eax,[dwCODECBytesProcessed] + shr eax,1 + cmp [(sCompInfo ebx).wBitSize],16 + jne ??leave + shr eax,1 ;if not 16 bit then div/2 + +??leave: + pop edx + pop ecx + pop ebx + pop edi + pop esi + ret + + ENDP sosCODECCompressData + + +;**************************************************************************** +;* +;* 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 sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 + +??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 sosCODECDecompressData + END + + diff --git a/VQ/VQM32/SOSCOMP.H b/VQ/VQM32/SOSCOMP.H new file mode 100644 index 0000000..1c4e98c --- /dev/null +++ b/VQ/VQM32/SOSCOMP.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + short wBitSize; + short wChannels; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); +unsigned long cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *,unsigned long); +unsigned long cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *,unsigned long); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/VQ/VQM32/TARGA.CPP b/VQ/VQM32/TARGA.CPP new file mode 100644 index 0000000..363171f --- /dev/null +++ b/VQ/VQM32/TARGA.CPP @@ -0,0 +1,699 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* targa.c +* +* DESCRIPTION +* Targa Image File reader. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenTarga - Open Targa image file. +* CloseTarga - Close Targa image file. +* LoadTarga - Load Targa image file. +* XFlipTarga - X flip the image. +* YFlipTarga - Y flip the image. +* +* PRIVATE +* DecodeImageData - Decompress Targa image data. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "targa.h" + +/* Private data declerations. */ +static long DecodeImageData(TGAHandle *, char *); +static void InvertImageData(TGAHeader *, char *); + +/**************************************************************************** +* +* NAME +* OpenTarga - Open Targa image file. +* +* SYNOPSIS +* TGAHandle = OpenTarga(Name, Mode) +* +* TGAHandle *OpenTarga(char *, unsigned short); +* +* FUNCTION +* Open a Targa image file and read in its header. The file stream will +* positioned after the ID field (if there is one). +* +* INPUTS +* Name - Pointer to name of Targa file. +* Mode - Access mode. +* +* RESULT +* TGAHandle - Pointer to initialized TGAHandle or NULL if error. +* +****************************************************************************/ + +TGAHandle *OpenTarga(char *name, unsigned short mode) +{ + TGAHandle *tga; + long size; + long error = 0; + + /* Allocate TGAHandle */ + if ((tga = (TGAHandle *)malloc(sizeof(TGAHandle))) != NULL) { + + /* Initialize TGAHandle structure. */ + memset((void *)tga, 0, sizeof(TGAHandle)); + tga->mode = mode; + + switch (mode) { + + /* Open targa file for read. */ + case TGA_READMODE: + if ((tga->fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + + /* Read in header. */ + size = read(tga->fh, &tga->header, sizeof(TGAHeader)); + + if (size != sizeof(TGAHeader)) { + error = 1; + } + + /* Skip the ID field */ + if (!error && (tga->header.IDLength != 0)) { + if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) { + error = 1; + } + } + } else { + error = 1; + } + break; + + /* Open targa file for write. */ + case TGA_WRITEMODE: + if ((tga->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY), + (S_IREAD|S_IWRITE))) == -1) { + + error = 1; + } else { + printf("\r"); + } + break; + + /* Open targa file for read/write.*/ + case TGA_RDWRMODE: + if ((tga->fh = open(name, (O_RDWR|O_BINARY), + (S_IREAD|S_IWRITE))) != -1) { + + /* Read in header. */ + size = read(tga->fh, &tga->header, sizeof(TGAHeader)); + + if (size != sizeof(TGAHeader)) { + error = 1; + } + + /* Skip the ID field */ + if (!error && (tga->header.IDLength != 0)) { + if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) { + error = 1; + } + } + } else { + error = 1; + } + break; + } + + /* Close on any error! */ + if (error) { + CloseTarga(tga); + tga = NULL; + } + } + + return (tga); +} + + +/**************************************************************************** +* +* NAME +* CloseTarga - Close Targa image file. +* +* SYNOPSIS +* CloseTarga(TGAHandle) +* +* void CloseTarga(TGAHandle *); +* +* FUNCTION +* Close the Targa image file and free its handle. +* +* INPUTS +* TGAHandle - Pointer to TGAHandle returned by OpenTarga(). +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseTarga(TGAHandle *tga) +{ + /* Ensure valid handle. */ + if (tga) { + + /* Close the file if it is open. */ + if (tga->fh != -1) close(tga->fh); + + /* Free TGAHandle */ + free(tga); + } +} + + +/**************************************************************************** +* +* NAME +* LoadTarga - Load Targa Image File. +* +* SYNOPSIS +* Error = LoadTarga(Name, Palette, ImageBuffer) +* +* long LoadTarga(char *, char *, char *); +* +* FUNCTION +* Open and load the Targa into the specified buffers. If either buffer +* pointer is NULL then that field will not be processed. +* +* INPUTS +* Name - Name of Targa image file to load. +* Palette - Pointer to buffer to load the palette into. +* ImageBuffer - Pointer to buffer to load the image data into. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +long LoadTarga(char *name, char *palette, char *image) +{ + TGAHandle *tga; + long size; + long depth; + long i,n; + char c; + long error = 0; + + /* Open the Targa */ + if ((tga = OpenTarga(name, TGA_READMODE)) != NULL) { + + /* Process ColorMap (palette) */ + if (tga->header.ColorMapType == 1) { + depth = (tga->header.CMapDepth >> 3); + size = (tga->header.CMapLength * depth); + + /* Load the palette from the TGA if a palette buffer is provided + * otherwise we will skip it. + */ + if ((palette != NULL) && (tga->header.CMapLength > 0)) { + + /* Adjust palette to the starting color entry. */ + palette += (tga->header.CMapStart * depth); + + /* Read in the palette. */ + if (read(tga->fh, palette, size) == size) { + + /* Swap the byte ordering of the palette entries. */ + for (i = 0; i < tga->header.CMapLength; i++) { + #if(0) + for (n = 0; n < depth; n++) { + c = *(palette + n); + *(palette + n) = *(palette + ((depth - 1) - n)); + *(palette + ((depth - 1) - n)) = c; + } + #else + c = *palette; + *palette = *(palette + (depth - 1)); + *(palette + (depth - 1)) = c; + #endif + + /* Next entry */ + palette += depth; + } + } else { + error = TGAERR_READ; + } + } else { + if (lseek(tga->fh, size, SEEK_CUR) == -1) { + error = TGAERR_READ; + } + } + } + + /* Load the image data from the TGA if an image buffer is provided + * otherwise we are done. + */ + if (!error && (image != NULL)) { + depth = (tga->header.PixelDepth >> 3); + size = ((tga->header.Width * tga->header.Height) * depth); + + switch (tga->header.ImageType) { + case TGA_CMAPPED: + if (read(tga->fh, image, size) != size) { + error = TGAERR_READ; + } + break; + + case TGA_TRUECOLOR: + if (read(tga->fh, image, size) == size) { + InvertImageData(&tga->header, image); + } else { + error = TGAERR_READ; + } + break; + + case TGA_CMAPPED_ENCODED: + error = DecodeImageData(tga, image); + break; + + case TGA_TRUECOLOR_ENCODED: + if ((error = DecodeImageData(tga, image)) == NULL) { + InvertImageData(&tga->header, image); + } + break; + + default: + error = TGAERR_NOTSUPPORTED; + break; + } + + /* Arrange the image so that the origin position (coordinate 0,0) + * is the upperleft hand corner of the image. + */ + if (!error) { + if (tga->header.ImageDescriptor & TGAF_XORIGIN) { + XFlipTarga(&tga->header, image); + } + + if ((tga->header.ImageDescriptor & TGAF_YORIGIN) == 0) { + YFlipTarga(&tga->header, image); + } + } + } + + /* Close the Targa */ + CloseTarga(tga); + } else { + error = TGAERR_OPEN; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* SaveTarga - Save a Targa Image File. +* +* SYNOPSIS +* Error = SaveTarga(Name, TGAHeader, Palette, ImageBuffer) +* +* long SaveTarga(char *, TGAHeader *, char *, char *); +* +* FUNCTION +* +* INPUTS +* Name - Pointer to name of file to save. +* TGAHeader - Pointer to initialized targa header structure. +* Palette - Pointer to palette. +* ImageBuffer - Pointer to raw image data. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +long SaveTarga(char *name, TGAHeader *tgahd, char *palette, char *image) +{ + TGAHandle *tga; + long size; + long depth; + char *temppal; + char *ptr; + long i,n; + char c; + long error = 0; + + /* Open the Targa for write. */ + if ((tga = OpenTarga(name, TGA_WRITEMODE)) != NULL) { + + /* Write the header. */ + if (write(tga->fh, tgahd, sizeof(TGAHeader)) != sizeof(TGAHeader)) { + error = TGAERR_WRITE; + } + + /* Write the palette. */ + if (!error && (palette != NULL) && (tgahd->CMapLength > 0)) { + + /* Adjust palette to the starting color entry. */ + depth = (tgahd->CMapDepth >> 3); + palette += (tgahd->CMapStart * depth); + size = (tgahd->CMapLength * depth); + + /* Allocate temporary buffer for palette manipulation. */ + if ((temppal = (char *)malloc(size)) != NULL) { + memcpy(temppal, palette, size); + ptr = temppal; + + /* Swap the byte ordering of the palette entries. */ + for (i = 0; i < tga->header.CMapLength; i++) { + for (n = 0; n < (depth >> 1); n++) { + c = *(ptr + n); + *(ptr + n) = *(ptr + (depth - n)); + *(ptr + (depth - n)) = c; + } + + /* Next entry */ + palette += depth; + } + + /* Write the palette. */ + if (write(tga->fh, temppal, size) != size) { + error = TGAERR_WRITE; + } + + /* Free temporary palette buffer. */ + free(temppal); + } else { + error = TGAERR_NOMEM; + } + } + + /* Invert truecolor data. */ + if (tgahd->ImageType == TGA_TRUECOLOR) { + InvertImageData(tgahd, image); + } + + /* Write the image. */ + if (!error && (image != NULL)) { + depth = (tgahd->PixelDepth >> 3); + size = (((tgahd->Width * tgahd->Height)) * depth); + + if (write(tga->fh, image, size) != size) { + error = TGAERR_WRITE; + } + } + + /* Close targa file. */ + CloseTarga(tga); + } else { + error = TGAERR_OPEN; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* XFlipTarga - X flip the image. +* +* SYNOPSIS +* XFlipTarga(TGAHeader, Image) +* +* void XFlipTarga(TGAHeader *, char *); +* +* FUNCTION +* Flip the image in memory on its X axis. (left to right) +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* Image - Pointer to image buffer. +* +* RESULT +* NONE +* +****************************************************************************/ + +void XFlipTarga(TGAHeader *tga, char *image) +{ + char *ptr,*ptr1; + long x,y,d; + char v,v1; + char depth; + + /* Pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + for (y = 0; y < tga->Height; y++) { + ptr = (image + ((tga->Width * depth) * y)); + ptr1 = (ptr + ((tga->Width * depth) - depth)); + + for (x = 0; x < (tga->Width / 2); x++) { + for (d = 0; d < depth; d++) { + v = *(ptr + d); + v1 = *(ptr1 + d); + *(ptr + d) = v1; + *(ptr1 + d) = v; + } + + ptr += depth; + ptr1 -= depth; + } + } +} + + +/**************************************************************************** +* +* NAME +* YFlipTarga - Y flip the image. +* +* SYNOPSIS +* YFlipTarga(TGAHeader, Image) +* +* void YFlipTarga(TGAHeader *, char *); +* +* FUNCTION +* Flip the image in memory on its Y axis. (top to bottom) +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* Image - Pointer to image buffer. +* +* RESULT +* NONE +* +****************************************************************************/ + +void YFlipTarga(TGAHeader *tga, char *image) +{ + char *ptr,*ptr1; + long x,y; + char v,v1; + char depth; + + /* Pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + for (y = 0; y < (tga->Height >> 1); y++) { + + /* Compute address of lines to exchange. */ + ptr = (image + ((tga->Width * y) * depth)); + ptr1 = (image + ((tga->Width * (tga->Height - 1)) * depth)); + ptr1 -= ((tga->Width * y) * depth); + + /* Exchange all the pixels on this scan line. */ + for (x = 0; x < (tga->Width * depth); x++) { + v = *ptr; + v1 = *ptr1; + *ptr = v1; + *ptr1 = v; + ptr++; + ptr1++; + } + } +} + + +/**************************************************************************** +* +* NAME +* DecodeImageData - Decompress Targa image data. +* +* SYNOPSIS +* Error = DecodeImageData(TGAHandle, ImageBuffer) +* +* long DecodeImageData(TGAHandle *, char *); +* +* FUNCTION +* Decode the RLE compressed image data into the specified buffer from +* the file I/O stream. +* +* INPUTS +* TGAHandle - Pointer to TGAHandle returned by OpenTarga(). +* ImageBuffer - Pointer to buffer to decompress image into. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +static long DecodeImageData(TGAHandle *tga, char *image) +{ + char *packet; + unsigned char count; + unsigned char depth; + unsigned long pixel_count; + unsigned long size; + unsigned long c,i; + long error = 0; + + /* Compute pixel depth in bytes. */ + depth = (tga->header.PixelDepth >> 3); + + /* Total number of pixels compressed in this image. */ + pixel_count = (tga->header.Width * tga->header.Height); + + /* Allocate packet buffer to hold maximum encoded data run. */ + if ((packet = (char *)malloc(128 * depth)) != NULL) { + while ((pixel_count > 0) && !error) { + + /* Read count. */ + if (read(tga->fh, &count, 1) == 1) { + + /* If bit 8 of the count is set then we have a run of pixels, + * otherwise the data is raw pixels. + */ + if (count & 0x80) { + count &= 0x7F; + count++; + + /* Read in run pixel. */ + if (read(tga->fh, packet, depth) == depth) { + + /* Repeat the pixel for the run count in the image buffer. */ + for (c = 0; c < count; c++) { + for (i = 0; i < depth; i++) { + *image++ = *(packet + i); + } + } + } else { + error = TGAERR_READ; + } + } else { + count++; + size = (count * depth); + + /* Read in raw pixels. */ + if (read(tga->fh, packet, size) == size) { + + /* Copy the raw pixel data into the image buffer. */ + memcpy(image, packet, size); + image += size; + } else { + error = TGAERR_READ; + } + } + + /* Adjust the pixel count. */ + pixel_count -= count; + } else { + error = TGAERR_READ; + } + } + + /* Free packet buffer. */ + free(packet); + } else { + error = TGAERR_NOMEM; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* InvertImageData - Invert TrueColor image data. +* +* SYNOPSIS +* InvertImageData(TGAHeader, ImageData) +* +* void InvertImageData(TGAHeader *, char *); +* +* FUNCTION +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* ImageData - Pointer to TrueColor image data. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void InvertImageData(TGAHeader *tga, char *image) +{ + long depth; + long pixel_count; + long i; + char c; + + /* Compute the pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + /* Total number of pixels in this image. */ + pixel_count = (tga->Width * tga->Height); + + /* 16-bit pixel layout is different that 24-bit and 32-bit. */ + if (depth > 2) { + while (pixel_count > 0) { + for (i = 0; i < (depth / 2); i++) { + c = *(image + i); + *(image + i) = *(image + ((depth - 1) - i)); + *(image + ((depth - 1) - i)) = c; + } + + /* Next pixel */ + pixel_count--; + image += depth; + } + } else { + } +} + + diff --git a/VQ/VQM32/TARGA.H b/VQ/VQM32/TARGA.H new file mode 100644 index 0000000..25bafc9 --- /dev/null +++ b/VQ/VQM32/TARGA.H @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +#ifndef VQMTARGA_H +#define VQMTARGA_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Targa.h (32-Bit protected mode) +* +* DESCRIPTION +* Targa Image File definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * Targa Header definitions + *-------------------------------------------------------------------------*/ + +/* TGAHeader - Targa Image File header. + * + * IDLength - Size of Image ID field + * ColorMapType - Color map type. + * ImageType - Image type code. + * CMapStart - Color map origin. + * CMapLength - Color map length. + * CMapDepth - Depth of color map entries. + * XOffset - X origin of image. + * YOffset - Y origin of image. + * Width - Width of image. + * Height - Height of image. + * PixelDepth - Image pixel size + * ImageDescriptor - Image descriptor byte. + */ +typedef struct _TGAHeader { + char IDLength; + char ColorMapType; + char ImageType; + short CMapStart; + short CMapLength; + char CMapDepth; + short XOffset; + short YOffset; + short Width; + short Height; + char PixelDepth; + char ImageDescriptor; +} TGAHeader; + +/* ImageType definiton */ +#define TGA_NOIMAGE 0 /* No image data included in file */ +#define TGA_CMAPPED 1 /* Color-mapped image data */ +#define TGA_TRUECOLOR 2 /* Truecolor image data */ +#define TGA_MONO 3 /* Monochrome image data */ +#define TGA_CMAPPED_ENCODED 9 /* Color-mapped image data (Encoded) */ +#define TGA_TRUECOLOR_ENCODED 10 /* Truecolor image data (Encoded) */ +#define TGA_MONO_ENCODED 11 /* Monochrome image data (Encoded) */ + +/* ImageDescriptor definition */ +#define TGAF_ATTRIB_BITS (0x0F<<0) /* Number of attribute bits per pixel */ +#define TGAF_XORIGIN (1<<4) +#define TGAF_YORIGIN (1<<5) + +/*--------------------------------------------------------------------------- + * Targa Handle definitions + *-------------------------------------------------------------------------*/ + +/* TGAHandle - Targa Image File handle. + * + * fh - File handle returned by open(). + * mode - Access mode. + * header - TGAHeader structure. + */ +typedef struct _TGAHandle { + short fh; + unsigned short mode; + TGAHeader header; +} TGAHandle; + +/* Access modes. */ +#define TGA_READMODE 0 +#define TGA_WRITEMODE 1 +#define TGA_RDWRMODE 2 + +/* Error codes */ +#define TGAERR_OPEN -1 +#define TGAERR_READ -2 +#define TGAERR_WRITE -3 +#define TGAERR_SYNTAX -4 +#define TGAERR_NOMEM -5 +#define TGAERR_NOTSUPPORTED -6 + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +TGAHandle *OpenTarga(char *, unsigned short); +void CloseTarga(TGAHandle *); +long LoadTarga(char *, char *, char *); +long SaveTarga(char *, TGAHeader *, char *, char *); +void XFlipTarga(TGAHeader *, char *); +void YFlipTarga(TGAHeader *, char *); + +#endif /* VQMTARGA_H */ + diff --git a/VQ/VQM32/TASM32.CFG b/VQ/VQM32/TASM32.CFG new file mode 100644 index 0000000..f664aa0 --- /dev/null +++ b/VQ/VQM32/TASM32.CFG @@ -0,0 +1,10 @@ +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE +/zi +/dPHARLAP_TNT=1 diff --git a/VQ/VQM32/TESTVB.CPP b/VQ/VQM32/TESTVB.CPP new file mode 100644 index 0000000..c128aec --- /dev/null +++ b/VQ/VQM32/TESTVB.CPP @@ -0,0 +1,104 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* testvb.c +* +* DESCRIPTION +* Video mode setting. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* TestVBIBit - Test the polarity of the vertical blank bit. +* +****************************************************************************/ + +#include + +#ifdef __BORLANDC__ +#include "portio.h" +#else +#include +#endif + +/**************************************************************************** +* +* NAME +* TestVBIBit - Test the polarity of the vertical blank bit. +* +* SYNOPSIS +* Polarity = TestVBIBit() +* +* long TestVBIBit(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* Polarity - Polarity of the vertical blank bit. +* +****************************************************************************/ + +long TestVBIBit(void) +{ + static struct timeb mytime; + long curtime; + long endtime; + unsigned long high = 0; + unsigned long low = 0; + + /* Set the check time for .25 (1/4) of a second. */ + ftime(&mytime); + curtime = ((mytime.time * 1000) + mytime.millitm); + endtime = (curtime + (1000 / 4)); + + /* Sample the vertical blank bit for the specified period of time. + * The state in which it is in the least is the vertical blank state, + * the state in which it is in the most is the active scan state. + */ + while (endtime >= curtime) { + ftime(&mytime); + curtime = ((mytime.time * 1000) + mytime.millitm); + + if (inp(0x3DA) & 0x08) { + high++; + } else { + low++; + } + } + + return (high > low); +} + + + diff --git a/VQ/VQM32/TEXT.H b/VQ/VQM32/TEXT.H new file mode 100644 index 0000000..2f02dd9 --- /dev/null +++ b/VQ/VQM32/TEXT.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQMTEXT_H +#define VQMTEXT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* text.h +* +* DESCRIPTION +* Text printing definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 13, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +long cdecl Text_Print(char *string, long x, long y, long fcol, long bcol); +void cdecl Draw_Char(long character, long x, long y); +void cdecl Set_Font_Palette_Range(void *palette, long start, long end); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMTEXT_H */ + diff --git a/VQ/VQM32/TEXTPRNT.ASM b/VQ/VQM32/TEXTPRNT.ASM new file mode 100644 index 0000000..a62e2ca --- /dev/null +++ b/VQ/VQM32/TEXTPRNT.ASM @@ -0,0 +1,178 @@ +; +; 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 . +; + +; $Header: J:\vq\projects\vqm32\textprnt.asv 1.5 27 Jul 1995 13:57:04 DENZIL_LONG $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : January 28, 1992 * +;* * +;* Last Update : February 3, 1992 [DRD] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Text_Print(BYTE *string, WORD x_pixel, WORD y_pixel, * +; WORD fcolor, WORD bcolor); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +XPIXEL_MAX EQU 320 +YPIXEL_MAX EQU 200 + +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + EXTRN C Char_Pixel_Width:NEAR + EXTRN C Draw_Char:NEAR + EXTRN C Set_Font_Palette_Range:NEAR + EXTRN FontPtr:NEAR PTR + EXTRN FontYSpacing:DWORD + +;---------------------------------------------------------------------------- +; TEXT_PRINT +; +; VOID Text_Print(BYTE *string, WORD x_pixel, WORD y_pixel, +; WORD fcolor, WORD bcolor); +; +; Print the given string to the LogicPage. +; +; Bounds Checking: +; +; if x_pixel < 0, then x_pixel = 0 +; if x_pixel >= XPIXEL_MAX, then exit +; if y_pixel < 0, then y_pixel = 0 +; if y_pixel >= YPIXEL_MAX, then exit +;* + + GLOBAL C Text_Print:NEAR + PROC Text_Print C NEAR USES ebx ecx edx edi esi + ARG string:NEAR PTR + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcol:DWORD + ARG bcol:DWORD + + LOCAL fwidth:DWORD ;Pixel width of font. + LOCAL fgbg:DWORD ;Two bytes of background & foreground colors. + LOCAL lines:DWORD ;Number of lines + LOCAL fontheight:DWORD + + ; Make sure there is a font available. If not, then bail. + + xor eax,eax + mov [lines],eax + + mov eax,[FontPtr] + or eax,eax + je ??exit + + movzx ebx,[WORD PTR eax+FONTINFOBLOCK] + add ebx,eax + movzx eax,[BYTE PTR ebx+FONTINFOMAXHEIGHT] + mov [fontheight],eax + mov esi,[string] + + mov ebx,[x_pixel] ; x pixel + cmp ebx,XPIXEL_MAX ; check max x pos + jae short ??exit + + mov ecx,[y_pixel] ; y pixel + cmp ecx,YPIXEL_MAX ; check max y pos + jge short ??exit + + mov al,[BYTE PTR bcol] + mov ah,[BYTE PTR fcol] + mov [fgbg],eax + lea eax,[fgbg] + call Set_Font_Palette_Range C,eax,0,1 + +; start of loop to print string + + xor edx,edx + inc [lines] +??loop: + mov dl,[esi] + inc esi + + cmp edx,0 ; end of string + je short ??exit + + cmp edx,13 ; cmp to a '\r' + jne short ??chkxy + + ; Advance the screen to the left edge and down one line. Check + ; to see if the coordinate would still be visible. If not, then + ; bail. + +??onelinedown: + mov ebx,[x_pixel] ; get original x position + add ecx,[fontheight] + add ecx,[FontYSpacing] + cmp ecx,YPIXEL_MAX ; check y pos + jae short ??exit + inc [lines] + jmp ??loop + +??chkxy: + call Char_Pixel_Width C,edx + + ; Check to see if this character would spill past the right edge + ; of the screen. If it would then drop down a line. + + mov [fwidth],eax ; save width of char for later + add eax,ebx + + cmp eax,XPIXEL_MAX ; check x pos + ja short ??onelinedown + + call Draw_Char C,edx,ebx,ecx + + add ebx,[fwidth] ; add font width + jmp ??loop + + ; Exit routine and unlock string if it was in EMS. + +??exit: + mov eax,[lines] + ret + + ENDP Text_Print + +;---------------------------------------------------------------------------- + END + diff --git a/VQ/VQM32/TLIB.CFG b/VQ/VQM32/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/VQ/VQM32/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/VQ/VQM32/TLINK32.CFG b/VQ/VQM32/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/VQ/VQM32/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/VQ/VQM32/VB.ASM b/VQ/VQM32/VB.ASM new file mode 100644 index 0000000..41d6ece --- /dev/null +++ b/VQ/VQM32/VB.ASM @@ -0,0 +1,137 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vb.asm +;* +;* DESCRIPTION +;* Vertical blank routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* WaitNoVB - Wait for active scan. +;* WaitVB - Wait for vertical blank. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "video.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* WaitNoVB - Wait for active scan. +;* +;* SYNOPSIS +;* WaitNoVB() +;* +;* void WaitNoVB(void); +;* +;* FUNCTION +;* Sit and wait for the active scan of the display. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C WaitNoVB:NEAR + PROC WaitNoVB C NEAR USES edx + ARG vbibit:DWORD + + mov eax,[vbibit] + and al,1 + shl al,3 + mov ah,al + +; loop while VBL bit != VQ_VertBlank + +??no_scan_yet: + mov edx,03DAH + in al,dx + and al,8 + xor al,ah + jnz short ??no_scan_yet + ret + + ENDP WaitNoVB + + +;**************************************************************************** +;* +;* NAME +;* WaitVB - Wait for vertical blank. +;* +;* SYNOPSIS +;* WaitVB() +;* +;* void WaitVB(void); +;* +;* FUNCTION +;* Sit and wait for the vertical blank of the display. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C WaitVB:NEAR + PROC WaitVB C NEAR USES + ARG vbibit:DWORD + + mov eax,[vbibit] + and al,1 + shl al,3 + mov ah,al + +; Loop while VBL bit = VQ_VertBlank + +??no_vbl_yet: + mov edx,03DAH + in al,dx + and al,8 + xor al,ah + jz short ??no_vbl_yet + ret + + ENDP WaitVB + + END diff --git a/VQ/VQM32/VERTAG.CPP b/VQ/VQM32/VERTAG.CPP new file mode 100644 index 0000000..982c8fc --- /dev/null +++ b/VQ/VQM32/VERTAG.CPP @@ -0,0 +1,49 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQMisc32 library. +* +* FILE +* vertag.c (32-Bit protected mode) +* +* DESCRIPTION +* Embedded version string. This string is prefixed with a tag ("$VER$") +* which can be search for to find this string. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 8, 1995 +* +****************************************************************************/ + +#ifdef __WATCOMC__ +#define DEVNAME "Watcom/4GW" +#else +#define DEVNAME "Borland/TNT" +#endif + +char VerTag[] = {"$VER$VQM32 2.12 "DEVNAME" ("__DATE__" "__TIME__")"}; diff --git a/VQ/VQM32/VESABLIT.CPP b/VQ/VQM32/VESABLIT.CPP new file mode 100644 index 0000000..a5a0f3c --- /dev/null +++ b/VQ/VQM32/VESABLIT.CPP @@ -0,0 +1,135 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vesablit.c +* +* DESCRIPTION +* VESA bitblit routines. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VESA_Blit_640x480 - Blit to 640x480 256 color VESA mode. +* +****************************************************************************/ + +#include +#include +#include +#include "video.h" +#include "vesavid.h" +#include "vesablit.h" + + +/**************************************************************************** +* +* NAME +* VESA_Blit_640x480 - Blit to 640x480 256 color VESA mode. +* +* SYNOPSIS +* VESA_Blit_640x480(DisplayInfo, Buffer, X, Y, Width, Height) +* +* void VESA_Blit_640x480(DisplayInfo *, char *, long, long, long, long); +* +* FUNCTION +* +* INPUTS +* DisplayInfo - Pointer to display information structure. +* Buffer - Pointer to buffer to blit to VRAM. +* X - Destination X coordinate of blit (upper left). +* Y - Destination Y coordinate of blit (upper left). +* Width - Width of blit. +* Height - Height of blit. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1,long y1, + long width,long height) +{ + VESAModeInfo *vminfo; + long bank; + long last_bank; + long bank_offset; + long scrn_offset; + long grains_per_win; + long part1; + long part2; + long i; + + /* Initialize values */ + vminfo = (VESAModeInfo *)disp->Extended; + scrn_offset = ((disp->XRes * y1) + x1); + grains_per_win = ((long)vminfo->WinSize / (long)vminfo->WinGranularity); + bank_offset = scrn_offset % 65536L; + last_bank = -1; + + for (i = 0; i < height; i++) { + + /* Compute which bank this scanline is in */ + bank = (scrn_offset / 65536L); + + /* Set a new bank */ + if (bank != last_bank) { + SetVESAWindow(bank); + last_bank = bank; + bank_offset = (scrn_offset % 65536L); + } + + /* Copy a full scanline */ + if ((bank_offset + width) < 65536L) { + Copy_Row((char *)buf, (char *)bank_offset, width); + buf += width; + scrn_offset += disp->XRes; + bank_offset += disp->XRes; + } + + /* Copy two partial scanlines */ + else { + part1 = (65536L - bank_offset); + part2 = (width - part1); + Copy_Row((char *)buf, (char *)bank_offset, part1); + + buf += part1; + bank += grains_per_win; + last_bank += grains_per_win; + SetVESAWindow(bank); + Copy_Row((char *)buf, (char *)0, part2); + + buf += part2; + scrn_offset += disp->XRes; + bank_offset = (scrn_offset % 65536L); + } + } +} diff --git a/VQ/VQM32/VESABLIT.H b/VQ/VQM32/VESABLIT.H new file mode 100644 index 0000000..245d966 --- /dev/null +++ b/VQ/VQM32/VESABLIT.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +#ifndef VQMVESABLIT_H +#define VQMVESABLIT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESABlit.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA bitblit routines. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl Blit_VESA640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +void cdecl Buf_320x200_To_VESA_320x200(unsigned char *buffer, long grain); +void cdecl Buf_320x200_To_VESA_640x400(unsigned char *buffer, long grain); +void cdecl Buf_320x200_To_VESA_32K(unsigned char *buffer, + unsigned char *palette, long grain); + +void cdecl Copy_Row(char *, char *, long); +void cdecl Copy_Word_Row(char *source, char *dest, char *palette, + long numbytes); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVESABLIT_H */ + diff --git a/VQ/VQM32/VESABUF.ASM b/VQ/VQM32/VESABUF.ASM new file mode 100644 index 0000000..a47d435 --- /dev/null +++ b/VQ/VQM32/VESABUF.ASM @@ -0,0 +1,722 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vesabuf.asm +;* +;* DESCRIPTION +;* VESA buffered blit routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Buf_320x200_To_VESA_320x200 - Buffer copy, unscaled +;* Buf_320x200_To_VESA_640x400 - Scales and copies 320x200 to 640x400 +;* Buf_320x200_To_VESA_32K - Copies 320x200 buffer to VESA 32K +;* colors +;* Copy_Row - Copy a row of pixels to VRAM. +;* Copy_Word_Row - Copy a row of 15-bit pixels to VRAM +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "video.i" + CODESEG + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROWS: draws 'numrows' rows of 2x2 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx, dx +;--------------------------------------------------------------------------- + MACRO DRAW_BLOCK_ROWS numrows + LOCAL ??Start_row + LOCAL ??Not_finished_a_line + LOCAL ??Done + + mov edx,numrows +??Start_row: + mov ecx,320 + +??Not_finished_a_line: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + mov [WORD PTR es:edi+640],ax + ELSE + mov [WORD PTR edi],ax + mov [WORD PTR edi+640],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_finished_a_line + + add edi,640 + dec edx + jz ??Done + jmp ??Start_row + +??Done: + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROW: draws one row of 'numblks' 2x2 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx +;--------------------------------------------------------------------------- + MACRO DRAW_BLOCK_ROW numblks + LOCAL ??Not_done + + mov ecx,numblks + +??Not_done: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + mov [WORD PTR es:edi+640],ax + ELSE + mov [WORD PTR edi],ax + mov [WORD PTR edi+640],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_done + + ENDM + +;--------------------------------------------------------------------------- +; DRAW_PIXEL_ROW: draws 'numblks' 2x1 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx +;--------------------------------------------------------------------------- + MACRO DRAW_PIXEL_ROW numblks + LOCAL ??Not_done + + mov ecx,numblks + +??Not_done: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_done + + ENDM + +;**************************************************************************** +;* +;* NAME +;* Blit_VESA640x480 - Blit to 640x480 256 color VESA mode. +;* +;* SYNOPSIS +;* Blit_VESA640x480(DisplayInfo, Buffer, X, Y, Width, Height) +;* +;* void Blit_VESA640x480(DisplayInfo *, char *, long, long, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* DisplayInfo - Pointer to display information structure. +;* Buffer - Pointer to buffer to blit to VRAM. +;* X - Destination X coordinate of blit (upper left). +;* Y - Destination Y coordinate of blit (upper left). +;* Width - Width of blit. +;* Height - Height of blit. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +; GLOBAL C Blit_VESA640x480:NEAR +; PROC Blit_VESA640x480 C NEAR USES +; +; ARG disp:NEAR PTR DisplayInfo +; ARG buffer:NEAR PTR +; ARG x:DWORD +; ARG y:DWORD +; ARG width:DWORD +; ARG height:DWORD +; +; LOCAL grain:DWORD +; LOCAL scrn_offset:DWORD +; LOCAL bank_offset:DWORD +; LOCAL bank:DWORD +; LOCAL xres:DWORD +; +;;---------------------------------------------------------------------------- +;; INITIALIZE +;;---------------------------------------------------------------------------- +; +; pushad +; +;; Calculate granularity units per window +; +; mov esi,[disp] +; xor eax,eax +; mov edi,[(DisplayInfo esi).Extended] +; xor ebx,ebx +; mov ax,[(VESAModeInfo edi).WinSize] +; xor edx,edx +; mov bx,[(VESAModeInfo edi).WinGranularity] +; idiv ebx +; mov [grain],eax +; +;; Calculate screen offset +; +; mov eax,[(DisplayInfo esi).XRes] +; mov [xres],eax +; imul [y] +; add eax,[x] +; mov [scrn_offset],eax +; +;; Calculate bank offset +; +; mov ebx,65536 +; idiv ebx +; mov [bank_offset],edx +; mov [bank],eax +; +; popad +; ret +; +; ENDP Blit_VESA640x480 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_320x200 - Buffer copy, unscaled +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_320x200(Buffer, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_320x200(char *, long); +;* +;* FUNCTION +;* To center the buffer on the screen, it's upper-left corner goes at +;* (160,140). This means the buffer spans VESA banks 1,2 & 3, so it must +;* be copied in 3 parts: +;* +;* Bank 1: starting offset 24224, 65 lines +;* Bank 2: starting offset 288, 102 lines +;* Bank 3: starting offset 32, 33 lines +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_320x200:NEAR + PROC Buf_320x200_To_VESA_320x200 C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG grains_per_win:DWORD + + LOCAL grain_num:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov eax,[grains_per_win] + mov esi,[buffer] + mov [grain_num],eax + +;---------------------------------------------------------------------------- +; Copy Bank 1 +;---------------------------------------------------------------------------- + + SET_WINDOW [grain_num] + mov edi,24224 ;Starting screen address + mov edx,65 ;Lines to copy + +??SetBank1: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank1 ;Draw more lines + +;---------------------------------------------------------------------------- +; Copy Bank 2 +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + + SET_WINDOW [grain_num] + mov edi,288 ;Starting screen address + mov edx,102 ;Lines to copy + +??SetBank2: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank2 ;Draw more lines + +;---------------------------------------------------------------------------- +; Copy Bank 3 +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + + SET_WINDOW [grain_num] + mov edi,32 ;Starting screen address + mov edx,33 ;Lines to copy + +??SetBank3: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank3 ;Draw more lines + + IF PHARLAP_TNT + pop es + ENDIF + ret + + ENDP Buf_320x200_To_VESA_320x200 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_640x400 - Scales and copies 320x200 to 640x400 +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_640x400(Buffer, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_640x400(char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_640x400:NEAR + PROC Buf_320x200_To_VESA_640x400 C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG grains_per_win:DWORD + + LOCAL grain_num:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov [grain_num],0 + +;---------------------------------------------------------------------------- +; Copy Bank 0 +; - Skip down 40 scanlines (to center the image) +; - Draw 62 scanlines (31 rows of blocks) +; - Draw top half of 128 blocks +;---------------------------------------------------------------------------- + + SET_WINDOW [grain_num] + mov edi,25600 ;Starting screen address + + DRAW_BLOCK_ROWS 62/2 ;Draw 31 rows of blocks + DRAW_PIXEL_ROW 256/2 ;Draw top half of next 128 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 1 +; - Draw bottom half of previous 128 blocks +; - Finish the scan line with full blocks +; - Draw 100 scanlines of blocks +; - last line: top half of 256 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,256/2 ;Draw bottom half of prev 128 blks + mov edi,384 + DRAW_PIXEL_ROW 256/2 + + mov edi,0 + DRAW_BLOCK_ROW 384/2 ;Fill rest of this block row + + add edi,640 + DRAW_BLOCK_ROWS 100/2 ;Draw the block rows + DRAW_PIXEL_ROW 512/2 ;Draw top half of next 512 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 2 +; - Draw bottom half of previous 256 blocks +; - Finish the scan line with full blocks +; - Draw 101 scanlines of blocks +; - last line: 64 full blocks plus top half of 256 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,512/2 ;Draw bottom half of prev 256 blks + mov edi,128 + DRAW_PIXEL_ROW 512/2 + + mov edi,0 + DRAW_BLOCK_ROW 128/2 ;Fill rest of this block row + + add edi,640 + DRAW_BLOCK_ROWS 101/2 ;Draw the block rows + DRAW_BLOCK_ROW 128/2 ;Draw next 64 blocks + DRAW_PIXEL_ROW 512/2 ;Top half of 256 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 3 +; - Draw bottom half of previous 256 blocks +; - Finish the scan line with full blocks +; - Draw 101 scanlines of blocks +; - last line: 192 full blocks, top half of 128 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,512/2 ;Draw bottom half of prev 256 blks + mov edi,0 + DRAW_PIXEL_ROW 512/2 + DRAW_BLOCK_ROWS 101/2 ;Draw the block rows + DRAW_BLOCK_ROW 384/2 ;Last row of full blocks + DRAW_PIXEL_ROW 256/2 ;Top half of 128 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 4 +; - Draw bottom half of previous 128 blocks +; - Draw 30 scanlines of blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,256/2 ;Draw bottom half of prev 256 blks + mov edi,0 + DRAW_PIXEL_ROW 256/2 + DRAW_BLOCK_ROWS 30/2 ;Draw the block rows + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Buf_320x200_To_VESA_640x400 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_32K - Copies 320x200 buffer to VESA 32K colors +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_32K(Buffer, Palette, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_32K(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* Palette - Pointer to 15-bit palette to use. +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_32K:NEAR + PROC Buf_320x200_To_VESA_32K C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG palette:NEAR PTR + ARG grains_per_win:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + +;---------------------------------------------------------------------------- +; Copy Bank 0 +;---------------------------------------------------------------------------- + + SET_WINDOW 0 + mov edi,0 ;Start at Bank 0, offset 0 + mov ecx,32768 ;# words we'll be setting + +; Get the pixel's offset into the palette + +??Buf0Loop: + xor eax,eax + mov ebx,[palette] + mov al,[BYTE PTR esi] + add ebx,eax + inc esi + add ebx,eax + +; store the 15-bit palette value + + mov ax,[WORD PTR ebx] + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Buf0Loop + +;---------------------------------------------------------------------------- +; Copy Bank 1 +;---------------------------------------------------------------------------- + + SET_WINDOW [grains_per_win] + mov edi,0 ;Start at Bank 1, offset 0 + mov ecx,31232 ;# words we'll be setting + +; Get the pixel's offset into the palette + +??Buf1Loop: + xor eax,eax + mov ebx,[palette] + mov al,[BYTE PTR esi] + add ebx,eax + inc esi + add ebx,eax + +; Store the 15-bit palette value + + mov ax,[WORD PTR ebx] + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Buf1Loop + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Buf_320x200_To_VESA_32K + + +;**************************************************************************** +;* +;* NAME +;* Copy_Row - Copy a row of pixels to VRAM. +;* +;* SYNOPSIS +;* Copy_Row(Source, Dest, Length) +;* +;* void Copy_Row(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to copy to VRAM +;* Dest - Destination VRAM address. +;* Length - Number of bytes to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Copy_Row:NEAR + PROC Copy_Row C NEAR USES ecx esi edi + + ARG source:NEAR PTR + ARG dest:NEAR PTR + ARG numbytes:DWORD + + IF PHARLAP_TNT + push es + mov eax,01Ch + mov es,ax + ENDIF + + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[numbytes] + rep movsb + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Copy_Row + + +;**************************************************************************** +;* +;* NAME +;* Copy_Word_Row - Copy a row of 15-bit pixels to VRAM +;* +;* SYNOPSIS +;* Copy_Word_Row(Source, Dest, Palette, Length) +;* +;* void Copy_Word_Row(char *, char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to transfer. +;* Dest - Destination screen address. +;* Palette - 15bit palette to use. +;* Length - Bytes to transfer. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Copy_Word_Row:NEAR + PROC Copy_Word_Row C NEAR USES ebx ecx esi edi + + ARG source:NEAR PTR + ARG dest:NEAR PTR + ARG palette:NEAR PTR + ARG numbytes:DWORD + + IF PHARLAP_TNT + push es + mov eax,01Ch + mov es,ax + ENDIF + + mov esi,[source] + mov edi,[dest] + mov ecx,[numbytes] + +??loop: + mov ebx,[palette] + xor eax,eax + mov al,[esi] ;Get pixel value + shl eax,1 ;Adjust for word entry + add ebx,eax ;Compute color address + mov ax,[ebx] ;Get color + + IF PHARLAP_TNT + mov [es:edi],ax ;Set 16bit pixel + ELSE + mov [edi],ax ;Set 16bit pixel + ENDIF + + inc esi ;Next source pixel + add edi,2 ;Next dest pixel + dec ecx ;Decrement pixel count + jnz ??loop + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Copy_Word_Row + + END + diff --git a/VQ/VQM32/VESAVID.CPP b/VQ/VQM32/VESAVID.CPP new file mode 100644 index 0000000..5c8a4c3 --- /dev/null +++ b/VQ/VQM32/VESAVID.CPP @@ -0,0 +1,457 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vesavid.c +* +* DESCRIPTION +* VESA video manager. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* InitVESA - Initialize the VESA video manager. +* UninitVESA - Uninitialize the VESA video manager. +* SetVESAMode - Set the display to the specified VESA video mode. +* ReadVESAModeInfo - Read the VESA mode information from the video card. +* SetVESAWindow - Set VESA window A's start address. +* +****************************************************************************/ + +#include +#include +#include + +#ifndef __WATCOMC__ +#include +#include +#else +#include "realmode.h" +#endif + +#include "vesavid.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +static short _VInfoSel = NULL; +static short _VInfoSeg = NULL; +static short _ModeInfoSel = NULL; +static short _ModeInfoSeg = NULL; + +#else /* __WATCOMC__ */ +/* _regs - Registers used for calling software interrupts. + * _rpVInfo - Real pointer to VInfo structure in conventional memory. + * _rpModeInfo - Real pointer to ModeInfo structure in conventional memory. + * _VInfo - Protected mode copy of VInfo structure. + * _ModeInfo - Protected mode copy of ModeInfo structure. + */ +static SWI_REGS _regs; +static REALPTR _rpVInfo = NULL; +static REALPTR _rpModeInfo = NULL; +static VESAInfo _VInfo; +static VESAModeInfo _ModeInfo; + +#endif /* __WATCOMC__ */ + + +/**************************************************************************** +* +* NAME +* InitVESA - Initialize the VESA video manager. +* +* SYNOPSIS +* Error = InitVESA() +* +* long InitVESA(void); +* +* FUNCTION +* Initialize the VESA video system. Get the VESA information from the +* VESA video bios. +* +* INPUTS +* NONE +* +* RESULT +* Error - 0 if successful, or -1 error/VESA not supported. +* +****************************************************************************/ + +long InitVESA(void) +{ + #ifdef __WATCOMC__ + union REGS r; + struct SREGS sr; + RMInfo rmi; + long error = -1; + + /* Allocate real-mode memory for VESA structure. */ + r.x.eax = 0x0100; + r.x.ebx = (sizeof(VESAInfo) + 15) >> 4; + int386(0x31, &r, &r); + + if (r.x.cflag == 0) { + _VInfoSel = r.w.dx; + _VInfoSeg = r.w.ax; + + /* Allocate real-mode memory for VESAModeInfo structure. */ + r.x.eax = 0x0100; + r.x.ebx = (sizeof(VESAModeInfo) + 15) >> 4; + int386(0x31, &r, &r); + + if (r.x.cflag == 0) { + _ModeInfoSel = r.w.dx; + _ModeInfoSeg = r.w.ax; + + /* Clear VESAInfo structure. */ + memset(MK_PTR(0, _VInfoSeg), 0, sizeof(VESAInfo)); + + /* Get VESA information. */ + memset(&rmi, 0, sizeof(RMInfo)); + rmi.eax = 0x4F00; + rmi.edi = 0; + rmi.es = _VInfoSeg; + + segread(&sr); + r.w.ax = 0x0300; + r.h.bl = 0x10; + r.h.bh = 0; + r.w.cx = 0; + sr.es = FP_SEG(&rmi); + r.x.edi = FP_OFF(&rmi); + int386x(0x31, &r, &r, &sr); + + if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) { + error = 0; + } + } + } + + + if (error != 0) { + UninitVESA(); + } + + return (error); + + #else /* __WATCOMC__ */ + + unsigned short rseg; + long paras; + long error = -1; + + /* Calculate size of VESAInfo structure in paragraphs */ + paras = (sizeof(VESAInfo) + 15) >> 4; + + /* Allocate real-mode memory for VESA structure. */ + if (_dx_real_alloc(paras, (unsigned short *)&rseg, + (unsigned short *)¶s) == 0) { + + RP_SET(_rpVInfo, 0, rseg); + + /* Calculate size of VESAModeInfo structure in paragraphs */ + paras = (sizeof(VESAModeInfo) + 15) >> 4; + + /* Allocate real-mode memory for VESAModeInfo structure. */ + if (_dx_real_alloc(paras, (unsigned short *)&rseg, + (unsigned short *)¶s) == 0) { + + RP_SET(_rpModeInfo, 0, rseg); + + /* Clear the input buffer */ + FillRealMem(_rpVInfo, 0, sizeof(VESAInfo)); + + /* Set up function call */ + _regs.eax = 0x4F00; + _regs.edi = RP_OFF(_rpVInfo); + _regs.es = RP_SEG(_rpVInfo); + _dx_real_int(0x10, &_regs); + + if (_regs.eax == 0x004F) { + ReadRealMem(&_VInfo, _rpVInfo, sizeof(VESAInfo)); + error = 0; + } + } + } + + if (error != 0) { + UninitVESA(); + } + + return (error); + + #endif /* __WATCOMC__ */ +} + + +/**************************************************************************** +* +* NAME +* UninitVESA - Uninitialize the VESA video manager. +* +* SYNOPSIS +* UninitVESA() +* +* void UninitVESA(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void UninitVESA(void) +{ + #ifdef __WATCOMC__ + union REGS r; + + /* Free VESAInfo structure */ + if (_VInfoSeg != NULL) { + r.x.eax = 0x0101; + r.x.edx = _VInfoSel; + int386(0x31, &r, &r); + + _VInfoSeg = NULL; + _VInfoSel = NULL; + } + + /* Free VESAModeInfo structure */ + if (_ModeInfoSeg != NULL) { + r.x.eax = 0x0101; + r.x.edx = _VInfoSel; + int386(0x31, &r, &r); + + _ModeInfoSeg = NULL; + _ModeInfoSel = NULL; + } + + #else /* __WATCOMC__ */ + + /* Free VESAInfo structure */ + if (_rpVInfo != NULL) { + _dx_real_free(RP_SEG(_rpVInfo)); + _rpVInfo = NULL; + } + + /* Free VESAModeInfo structure */ + if (_rpModeInfo != NULL) { + _dx_real_free(RP_SEG(_rpModeInfo)); + _rpModeInfo = NULL; + } + #endif /* __WATCOMC__ */ +} + + +/**************************************************************************** +* +* NAME +* SetVESAMode - Set the display adapter to the given VESA video mode. +* +* SYNOPSIS +* VESAModeInfo = SetVESAMode(Mode) +* +* VESAModeInfo *SetVESAMode(long); +* +* FUNCTION +* Set the display adapter to the specified VESA video mode. +* +* INPUTS +* Mode - VESA video mode to set the display to. +* +* RESULT +* VESAModeInfo - Pointer to VESA mode information structure or NULL if +* error. +* +****************************************************************************/ + +VESAModeInfo *SetVESAMode(long mode) +{ + VESAModeInfo *vminfo; + + /* Get mode info */ + if ((vminfo = ReadVESAModeInfo(mode)) != NULL) { + + /* If the mode is supported, set it. */ + if ((vminfo->Attributes & 0x01) != 0) { + + #ifdef __WATCOMC__ + { + union REGS r; + + r.x.eax = 0x4F02; + r.x.ebx = mode; + int386(0x10, &r, &r); + + if (r.x.eax != 0x004F) + vminfo = NULL; + } + + #else /* __WATCOMC__ */ + + /* Set up function call */ + _regs.eax = 0x4F02; + _regs.ebx = mode; + _dx_real_int(0x10, &_regs); + + if (_regs.eax != 0x004F) { + vminfo = NULL; + } + #endif /* __WATCOMC__ */ + } + } + + return (vminfo); +} + + +/**************************************************************************** +* +* NAME +* ReadVESAModeInfo - Read the VESA mode information from the video card. +* +* SYNOPSIS +* VESAModeInfo = ReadVESAModeInfo(Mode) +* +* VESAModeInfo *ReadVESAModeInfo(long); +* +* FUNCTION +* Read information about the specified mode from the VESA video BIOS. +* +* INPUTS +* Mode - Mode ID to get information about. +* +* RESULT +* VESAModeInfo - Pointer to VESA mode information structure or NULL if +* error. +* +****************************************************************************/ + +VESAModeInfo *ReadVESAModeInfo(long mode) +{ + VESAModeInfo *vminfo = NULL; + + #ifdef __WATCOMC__ + union REGS r; + struct SREGS sr; + RMInfo rmi; + + /* Make sure we have real-mode memory. */ + if (_ModeInfoSeg != NULL) { + memset(MK_PTR(0, _ModeInfoSeg), 0, sizeof(VESAModeInfo)); + + /* Get mode information. */ + memset(&rmi, 0, sizeof(RMInfo)); + rmi.eax = 0x4F01; + rmi.ecx = mode; + rmi.edi = 0; + rmi.es = _ModeInfoSeg; + + segread(&sr); + r.w.ax = 0x0300; + r.w.bx = 0x0010; + r.w.cx = 0; + sr.es = FP_SEG(&rmi); + r.x.edi = FP_OFF(&rmi); + int386x(0x31, &r, &r, &sr); + + if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) { + vminfo = (VESAModeInfo *)MK_PTR(0, _ModeInfoSeg); + } + } + + #else /* __WATCOMC__ */ + + /* Make sure we have real-mode memory. */ + if (_rpModeInfo != NULL) { + + /* Clear the input buffer */ + FillRealMem(_rpModeInfo, 0, sizeof(VESAModeInfo)); + + /* Set up function call */ + _regs.eax = 0x4F01; + _regs.ecx = mode; + _regs.edi = RP_OFF(_rpModeInfo); + _regs.es = RP_SEG(_rpModeInfo); + _dx_real_int(0x10, &_regs); + + if (_regs.eax == 0x004F) { + ReadRealMem(&_ModeInfo, _rpModeInfo, sizeof(VESAModeInfo)); + vminfo = &_ModeInfo; + } + } + #endif /* __WATCOMC__ */ + + return (vminfo); +} + + +/**************************************************************************** +* +* NAME +* SetVESAWindow - Set VESA window A's start address. +* +* SYNOPSIS +* Error = SetVESAWindow(GrainNum) +* +* long SetVESAWindow(long); +* +* FUNCTION +* This function invokes the Window Function, whose address is provided +* in the VESAModeInfo structure. The 'GrainNum' must be in granularity +* units as specified in the ModeInfo structure. +* +* INPUTS +* GrainNum - Granularity number to set window to. +* +* RESULT +* NONE +* +****************************************************************************/ + +void SetVESAWindow(long grain) +{ + #ifdef __WATCOMC__ + #else /* __WATCOMC__ */ + + RMC_BLK regp; + + regp.eax = 0x4F05; + regp.ebx = 0x00; + regp.edx = grain; + _dx_call_real(_ModeInfo.WinFunc, ®p, 0); + #endif /* __WATCOMC__ */ +} + diff --git a/VQ/VQM32/VESAVID.H b/VQ/VQM32/VESAVID.H new file mode 100644 index 0000000..01723dd --- /dev/null +++ b/VQ/VQM32/VESAVID.H @@ -0,0 +1,182 @@ +/* +** 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 . +*/ + +#ifndef VQMVESAVID_H +#define VQMVESAVID_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESAVid.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifndef __WATCOMC__ +#include +#else +#include "realmode.h" +#endif + +/*--------------------------------------------------------------------------- + * VESA Video Modes + *-------------------------------------------------------------------------*/ + +#define VESA_640X400_256 0x100 +#define VESA_640X480_256 0x101 +#define VESA_800X600_16 0x102 +#define VESA_800X600_256 0x103 +#define VESA_1024X768_16 0x104 +#define VESA_1024X768_256 0x105 +#define VESA_1280X400_16 0x106 +#define VESA_1280X400_256 0x107 +#define VESA_TEXT_80X60 0x108 +#define VESA_TEXT_132X25 0x109 +#define VESA_TEXT_132X60 0x10C +#define VESA_320X200_32K_1 0x10D +#define VESA_320X200_32K_2 0x10E +#define VESA_640X480_32K 0x110 +#define VESA_640X480_65K 0x111 +#define VESA_640X480_16M 0x112 +#define VESA_800X600_32K 0x113 +#define VESA_800X600_65K 0x114 +#define VESA_1024X768_32K 0x116 +#define VESA_1024X768_65K 0x117 + +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_1024X768_65K + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* VESAInfo - General information about this VESA implementation. + * (Filled in by VESA BIOS Function 0) + * + * Signature - Will always be 'VESA' + * Version - Version # + * OEMString - OEM ID string + * Capabilities - Not defined by VESA yet + * AvailModes - List of available modes; terminated with -1 (0xffff) + * TotalMemory - ??? + * Reserved - Pads structure to 256 bytes total + */ +typedef struct _VESAInfo { + char Signature[4]; + unsigned short Version; + REALPTR OEMString; + unsigned long Capabilities; + REALPTR AvailModes; + unsigned short TotalMemory; + unsigned char Reserved[236]; +} VESAInfo; + +/* VESAModeInfo - Information about this VESA mode. + * (Filled in by VESA BIOS Function 1) + * + * Attributes - bit 0: 1 = mode is supported + * bit 1: 1 = optional info available + * bit 2: 1 = std BIOS output functions valid in this mode + * bit 3: 0 = monochrome, 1 = color + * bit 4: 0 = text mode, 1 = graphics + * WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinGranularity - smallest address boundary window can be placed upon; + * size is in KB (ie 64, 32, 4) + * WinSize - size of windows in KB (ie 64, 32) + * WinA_Segment - location of Window A in CPU space (usually 0xa000) + * WinB_Segment - location of Window B in CPU space (usually 0xb000) + * WinFunc - address of window-setting function (This is provided + * as an alternative to Int 10 for speed.) + * BytesPerScanline - # bytes per scan line + * + * Optional info (available if bit 1 of Attributes is set): + * + * XRes - X-resolution + * YRes - Y-resolution + * XCharSize - Horizontal size of char cell + * YCharSize - Vertical size of char cell + * NumPlanes - # of memory planes (???) + * BitsPerPixel - # bites per pixel + * NumBanks - # of banks (ie planes) + * MemoryModel - 00h = Text mode + * 01h = CGA mode + * 02h = Hercules + * 03h = 4 plane planar mode + * 04h = packed pixel mode (1 byte/pixel) + * 05h = non-chain 4, 256-color mode + * 06-0Fh = + * 10-FFh = OEM-specific + * BankSize - Bank size in KB + */ +typedef struct _VESAModeInfo { + unsigned short Attributes; + unsigned char WinA_Attributes; + unsigned char WinB_Attributes; + unsigned short WinGranularity; + unsigned short WinSize; + unsigned short WinA_Segment; + unsigned short WinB_Segment; + REALPTR WinFunc; + unsigned short BytesPerScanline; + unsigned short XRes; + unsigned short YRes; + unsigned char XCharSize; + unsigned char YCharSize; + unsigned char NumPlanes; + unsigned char BitsPerPixel; + unsigned char NumBanks; + unsigned char MemoryModel; + unsigned char BankSize; + unsigned char NumInputPages; + unsigned char Reserved; + unsigned char RedMaskSize; + unsigned char RedFieldPosition; + unsigned char GreenMaskSize; + unsigned char GreenFieldPosition; + unsigned char BlueMaskSize; + unsigned char BlueFieldPosition; + unsigned char RsvdMaskSize; + unsigned char RsvdFieldPosition; + unsigned char DirectColorModeInfo; + unsigned char pad[216]; +} VESAModeInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +long InitVESA(void); +void UninitVESA(void); +VESAModeInfo *SetVESAMode(long mode); +VESAModeInfo *ReadVESAModeInfo(long mode); +void SetVESAWindow(long grain_num); + +#endif /* VQMVESAVID_H */ + diff --git a/VQ/VQM32/VESAVID.I b/VQ/VQM32/VESAVID.I new file mode 100644 index 0000000..f305e09 --- /dev/null +++ b/VQ/VQM32/VESAVID.I @@ -0,0 +1,203 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* vesavid.i +;* +;* DESCRIPTION +;* VESA video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VESA video modes +;---------------------------------------------------------------------------- + +VESA_640X400_256 EQU 0x100 +VESA_640X480_256 EQU 0x101 +VESA_800X600_16 EQU 0x102 +VESA_800X600_256 EQU 0x103 +VESA_1024X768_16 EQU 0x104 +VESA_1024X768_256 EQU 0x105 +VESA_1280X400_16 EQU 0x106 +VESA_1280X400_256 EQU 0x107 +VESA_TEXT_80X60 EQU 0x108 +VESA_TEXT_132X25 EQU 0x109 +VESA_TEXT_132X60 EQU 0x10C +VESA_320X200_32K_1 EQU 0x10D +VESA_320X200_32K_2 EQU 0x10E +VESA_640X480_32K EQU 0x110 +VESA_640X480_65K EQU 0x111 +VESA_640X480_16M EQU 0x112 +VESA_800X600_32K EQU 0x113 +VESA_800X600_65K EQU 0x114 +VESA_1024X768_32K EQU 0x116 +VESA_1024X768_65K EQU 0x117 + +VESA_MIN EQU VESA_640X400_256 +VESA_MAX EQU VESA_1024X768_65K + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; VESAInfo - General information about this VESA implementation. +; (Filled in by VESA BIOS Function 0) +; +; Signature - Will always be 'VESA' +; Ver - Version # +; OEMString - OEM ID string +; Capabilities - Not defined by VESA yet +; AvailModes - List of available modes; terminated with -1 (0xffff) +; TotalMemory - ??? +; Reserved - Pads structure to 256 bytes total + + STRUC VESAInfo +Signature DD ? +Ver DW ? +OEMString DD ? +Capabilities DD ? +AvailModes DD ? +TotalMemory DW ? +Reserved DB 236 DUP (?) + ENDS VESAInfo + +; VESAModeInfo - Information about this VESA mode. +; (Filled in by VESA BIOS Function 1) +; +; Attributes - bit 0: 1 = mode is supported +; bit 1: 1 = optional info available +; bit 2: 1 = std BIOS output functions valid in this mode +; bit 3: 0 = monochrome, 1 = color +; bit 4: 0 = text mode, 1 = graphics +; WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinGranularity - smallest address boundary window can be placed upon; +; size is in KB (ie 64, 32, 4) +; WinSize - size of windows in KB (ie 64, 32) +; WinA_Segment - location of Window A in CPU space (usually 0xa000) +; WinB_Segment - location of Window B in CPU space (usually 0xb000) +; WinFunc - address of window-setting function (This is provided +; as an alternative to Int 10 for speed.) +; BytesPerScanline - # bytes per scan line +; +; Optional info (available if bit 1 of Attributes is set): +; +; XRes - X-resolution +; YRes - Y-resolution +; XCharSize - Horizontal size of char cell +; YCharSize - Vertical size of char cell +; NumPlanes - # of memory planes (???) +; BitsPerPixel - # bites per pixel +; NumBanks - # of banks (ie planes) +; MemoryModel - 00h = Text mode +; 01h = CGA mode +; 02h = Hercules +; 03h = 4 plane planar mode +; 04h = packed pixel mode (1 byte/pixel) +; 05h = non-chain 4, 256-color mode +; 06-0Fh = +; 10-FFh = OEM-specific +; BankSize - Bank size in KB + + STRUC VESAModeInfo +Attributes DW ? +WinA_Attributes DB ? +WinB_Attributes DB ? +WinGranularity DW ? +WinSize DW ? +WinA_Segment DW ? +WinB_Segment DW ? +WinFunc DD ? +BytesPerScanline DW ? +XRes DW ? +YRes DW ? +XCharSize DB ? +YCharSize DB ? +NumPlanes DB ? +BitsPerPixel DB ? +NumBanks DB ? +MemoryModel DB ? +BankSize DB ? +NumInputPages DB ? +Reserved DB ? +RedMaskSize DB ? +RedFieldPosition DB ? +GreenMaskSize DB ? +GreenFieldPosition DB ? +BlueMaskSize DB ? +BlueFieldPosition DB ? +RsvdMaskSize DB ? +RsvdFieldPosition DB ? +DirectColorModeInfo DB ? +pad DB 216 DUP (?) + ENDS VESAModeInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C InitVESA:PROC + GLOBAL C UninitVESA:PROC + GLOBAL C SetVESAMode:PROC + GLOBAL C ReadVESAModeInfo:PROC + GLOBAL C SetVESAWindow:PROC + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WINDOW - Sets window A to the specified bank. +; +; SYNOPSIS +; SET_WINDOW GrainNum +; +; FUNCTION +; This routine uses the C Set_Window function rather than going through +; the BIOS, for two reasons: (1) Speed, and (2) On the Cirrus Logic 54xx +; VESA cards, BIOS calls make noise while playing digital audio. +; +; INPUTS +; GrainNum - Granularity number. +; +; RESULT +; NONE +; +;---------------------------------------------------------------------------- + + MACRO SET_WINDOW grain_num + push esi + push edi + push es + call SetVESAWindow C,grain_num + pop es + pop edi + pop esi + ENDM + diff --git a/VQ/VQM32/VGA.I b/VQ/VQM32/VGA.I new file mode 100644 index 0000000..f04d9a0 --- /dev/null +++ b/VQ/VQM32/VGA.I @@ -0,0 +1,217 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vga.i +;* +;* DESCRIPTION +;* VGA hardware definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VGA Registers +;---------------------------------------------------------------------------- + +R_SEQUENCER EQU 03C4h ;Sequencer Controller Index reg +SEQ_RESET EQU 00h ;Reset +SEQ_MAP_MASK EQU 02h ;Index in Sequencer of Map Mask reg +SEQ_MEMORY_MODE EQU 04h ;Memory Mode + +R_GRAPHICS_CONTROLLER EQU 03CEh ;Graphics Controller Index reg +GC_READ_MAP EQU 04h ;Index in GController of Read Map reg +GC_MODE EQU 05h ;Read/Write Modes +GC_MISC EQU 06h ;Read/Write Modes +GC_BITMASK EQU 08h ;Index in GController of BitMask reg + +R_CRT_CONTROLLER EQU 03D4h ;CRT Controller Index reg +CRT_VERT_TOTAL EQU 06h ;Vertical total +CRT_OVERFLOW EQU 07h ;Overflow +CRT_MAX_SCANLINE EQU 09h ;Max scan line +CRT_STARTADDR_HIGH EQU 0Ch ;Bitmap start address high byte +CRT_STARTADDR_LOW EQU 0Dh ;Bitmap start address low byte +CRT_VERTRET_START EQU 010h ;Vertical retrace pulse start +CRT_VERTRET_END EQU 011h ;Vertical retrace pulse end +CRT_VERTDISP_END EQU 012h ;Vertical display end +CRT_UNDERLINE EQU 014h ;Underline location +CRT_START_VB EQU 015h ;Start vertical blank +CRT_END_VB EQU 016h ;End vertical blank +CRT_MODE_CONTROL EQU 017h ;Mode control +R_MISC_OUTPUT EQU 03C2h ;Miscellaneous Output reg + +;---------------------------------------------------------------------------- +; Palette Registers +;---------------------------------------------------------------------------- + +PEL_READ_ADDR EQU 03C7h +PEL_WRITE_ADDR EQU 03C8h +PEL_DATA EQU 03C9h + +;---------------------------------------------------------------------------- +; XMode planes, for the Map Mask register +;---------------------------------------------------------------------------- + +XPLANE_1 EQU 1 +XPLANE_2 EQU 2 +XPLANE_3 EQU 4 +XPLANE_4 EQU 8 + +;---------------------------------------------------------------------------- +; +; NAME +; SET_PLANE - Set an XMode plane. +; +; SYNOPSIS +; SET_PLANE plane +; +; INPUTS +; plane - Number of Xmode plane to set. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_PLANE plane + mov edx,R_SEQUENCER + mov eax,SEQ_MAP_MASK + out dx,al + inc edx + mov eax,plane + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_BITMASK - Set the BitMask register. +; +; SYNOPSIS +; SET_BITMASK mask +; +; INPUTS +; mask - Bitmask to use. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_BITMASK mask + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_BITMASK + out dx,al + inc edx + mov eax,mask + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WRITEMODE - Set the VGA writemode. +; +; SYNOPSIS +; SET_WRITEMODE mode +; +; INPUTS +; mode - Write mode. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_WRITEMODE mode + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_MODE + out dx,al + inc edx + in al,dx ;Read the register + and al,0FCh ;Turn off 2 lower bits + or al,mode ;Set write mode + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; OUTPORT - Output data to a VGA register. +; +; SYNOPSIS +; OUTPORT port,register,data +; +; INPUTS +; port - Port address. +; register - Register to write to. +; data - Data to write. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO OUTPORT port,register,data + mov edx,port + mov al,register + out dx,al + inc edx + mov al,data + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; INPORT - Input data from a VGA register. +; +; SYNOPSIS +; data = INPORT port,register +; +; INPUTS +; port - Port address. +; register - Register to read from. +; +; RESULT +; data - Value read from port in AL. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO INPORT port,register + mov edx,port + mov al,register + out dx,al + inc edx + in al,dx + ENDM + diff --git a/VQ/VQM32/VIDEO.CPP b/VQ/VQM32/VIDEO.CPP new file mode 100644 index 0000000..0c5e551 --- /dev/null +++ b/VQ/VQM32/VIDEO.CPP @@ -0,0 +1,282 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* video.c +* +* DESCRIPTION +* Video mode setting. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SetVideoMode - Set the video mode. +* GetDisplayInfo - Get the display info for the current video mode. +* GetVBIBit - Get the vertical blank bit polarity. +* +****************************************************************************/ + +#include +#include +#include + +#ifndef __WATCOMC__ +#include +#include +#else +#include "realmode.h" +#endif + +#include "video.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static DisplayInfo _Display = {-1,0,0,0,NULL}; + + +/**************************************************************************** +* +* NAME +* SetVideoMode - Set the display to the specified video mode. +* +* SYNOPSIS +* DisplayInfo = SetVideoMode(Mode) +* +* DisplayInfo *SetVideoMode(long); +* +* FUNCTION +* Set the video display adapter to the desired video mode. +* +* INPUTS +* Mode - Desired video mode. +* +* RESULT +* DisplayInfo - Pointer to DisplayInfo structure, otherwise 0 for error. +* +****************************************************************************/ + +DisplayInfo *SetVideoMode(long mode) +{ + #ifdef __WATCOMC__ + union REGS regs; + struct SREGS sregs; + #else + union _REGS regs; + #endif + + DisplayInfo *di = NULL; + VESAModeInfo *vminfo; + long error; + + /* Initialize the video manager on the first invocation of + * SetVideoMode() + */ + if (_Display.Mode == -1) { + _Display.VBIbit = TestVBIBit(); + } + + /* Clear the VRAM before enabling the mode so that there is + * not any garbage on the screen. + */ + ClearVRAM(); + + /* If the requested mode is the same as the current mode then + * we do not need to do anything. + */ + if (mode != _Display.Mode) { + + /* Uninitialize VESA if the previous mode was a VESA mode and the new + * mode is not. + */ + if (((_Display.Mode >= VESA_MIN) && (_Display.Mode <= VESA_MAX)) + && ((mode < VESA_MIN) && (mode > VESA_MAX))) { + + UninitVESA(); + } + + /* Set display to an XMode. */ + if ((mode >= XMODE_MIN) && (mode <= XMODE_MAX)) { + SetXMode(mode); + ClearXMode(); + SetupXPaging(); + ShowXPage(0); + _Display.Mode = mode; + _Display.Extended = NULL; + di = &_Display; + + /* Set display resolution information */ + switch (mode) { + case XMODE_320X200: + _Display.XRes = 320; + _Display.YRes = 200; + break; + + case XMODE_320X240: + _Display.XRes = 320; + _Display.YRes = 240; + break; + + case XMODE_320X400: + _Display.XRes = 320; + _Display.YRes = 400; + break; + + case XMODE_320X480: + _Display.XRes = 320; + _Display.YRes = 480; + break; + + case XMODE_360X400: + _Display.XRes = 360; + _Display.YRes = 400; + break; + + case XMODE_360X480: + _Display.XRes = 360; + _Display.YRes = 480; + break; + } + } + else if ((mode >= VESA_MIN) && (mode <= VESA_MAX)) { + + /* Initialize the VESA manager if the current mode is not a VESA + * mode. + */ + if ((_Display.Mode < VESA_MIN) || (_Display.Mode > VESA_MAX)) { + error = InitVESA(); + } + + if (!error) { + + /* Set the display to MCGA before going into VESA. This needs to be + * done to ensure that the video ram selector is initialized. This + * fixes a bug in some VESA BIOS'. + */ + #ifndef __WATCOMC__ + regs.x.ax = mode; + _int86(0x10, ®s, ®s); + #else + segread(&sregs); + regs.x.eax = mode; + int386x(0x10, ®s, ®s, &sregs); + #endif + + if ((vminfo = SetVESAMode(mode)) != NULL) { + _Display.Mode = mode; + _Display.XRes = (long)vminfo->XRes; + _Display.YRes = (long)vminfo->YRes; + _Display.Extended = vminfo; + di = &_Display; + } + } + } + else { + #ifndef __WATCOMC__ + regs.x.ax = mode; + _int86(0x10, ®s, ®s); + #else + segread(&sregs); + regs.x.eax = mode; + int386x(0x10, ®s, ®s, &sregs); + #endif + + _Display.Mode = mode; + _Display.XRes = 320; + _Display.YRes = 200; + _Display.Extended = NULL; + di = &_Display; + } + } else { + di = &_Display; + } + + return (di); +} + + +/**************************************************************************** +* +* NAME +* GetDisplayInfo - Get the display info for the current video mode. +* +* SYNOPSIS +* DisplayInfo = GetDisplayInfo() +* +* DisplayInfo *GetDisplayInfo(void); +* +* FUNCTION +* Return a pointer to the current display information structure. +* +* INPUTS +* NONE +* +* RESULT +* DisplayInfo - Pointer to initialized display info or NULL if not valid. +* +****************************************************************************/ + +DisplayInfo *GetDisplayInfo(void) +{ + if (_Display.Mode != 0) { + return (&_Display); + } else { + return (NULL); + } +} + + +/**************************************************************************** +* +* NAME +* GetVBIBit - Get the vertical blank bit polarity. +* +* SYNOPSIS +* VBIBit = GetVBIBit() +* +* long GetVBIBit(void); +* +* FUNCTION +* Return the polarity of the vertical blank bit. +* +* INPUTS +* NONE +* +* RESULT +* VBIBit - Vertical blank bit polarity. +* +****************************************************************************/ + +long GetVBIBit(void) +{ + return (_Display.VBIbit); +} + diff --git a/VQ/VQM32/VIDEO.H b/VQ/VQM32/VIDEO.H new file mode 100644 index 0000000..c54bb58 --- /dev/null +++ b/VQ/VQM32/VIDEO.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +#ifndef VQMVIDEO_H +#define VQMVIDEO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Video.h (32-Bit protected mode) +* +* DESCRIPTION +* Video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * VGA video modes + *-------------------------------------------------------------------------*/ + +#define TEXT 0x02 +#define MCGA 0x13 +#define XMODE_320X200 0x50 +#define XMODE_320X240 0x51 +#define XMODE_320X400 0x52 +#define XMODE_320X480 0x53 +#define XMODE_360X400 0x54 +#define XMODE_360X480 0x55 + +#define XMODE_MIN 0x50 +#define XMODE_MAX 0x55 + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* DisplayInfo - Information about the current display. + * + * Mode - Mode identification. + * XRes - X resolution of mode. + * YRes - Y resolution of mode. + * VBIbit - Polarity of vertical blank bit. + * Extended - Pointer to mode specific data structure. + */ +typedef struct _DisplayInfo { + long Mode; + long XRes; + long YRes; + long VBIbit; + void *Extended; +} DisplayInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +DisplayInfo *SetVideoMode(long mode); +DisplayInfo *GetDisplayInfo(void); +long TestVBIBit(void); +long GetVBIBit(void); + +void SetupXPaging(void); +void FlipXPage(void); +unsigned char *GetXHidPage(void); +unsigned char *GetXSeenPage(void); +void DisplayXPage(long page); + +#ifdef __cplusplus +extern "C" { +#endif + +void cdecl WaitNoVB(short vbibit); +void cdecl WaitVB(short vbibit); +void cdecl ClearVRAM(void); +long cdecl SetXMode(long mode); +void cdecl ClearXMode(void); +void cdecl ShowXPage(unsigned long StartOffset); +void cdecl Xmode_BufferCopy_320x200(void *buff, void *screen); +void cdecl Xmode_Blit(void *buffer, void *screen, long imgwidth, long imgheight); +void cdecl MCGA_BufferCopy(unsigned char *buffer, unsigned char *dummy); +void cdecl MCGA_Blit(unsigned char *buffer, unsigned char *screen, + long imgwidth, long imgheight); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVIDEO_H */ + diff --git a/VQ/VQM32/VIDEO.I b/VQ/VQM32/VIDEO.I new file mode 100644 index 0000000..d2fc384 --- /dev/null +++ b/VQ/VQM32/VIDEO.I @@ -0,0 +1,80 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* video.i +;* +;* DESCRIPTION +;* Video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + + INCLUDE "vesavid.i" + +;---------------------------------------------------------------------------- +; Video Modes +;---------------------------------------------------------------------------- + +TEXT EQU 002h +MCGA EQU 013h +XMODE_320X200 EQU 050h +XMODE_320X240 EQU 051h +XMODE_320X400 EQU 052h +XMODE_320X480 EQU 053h +XMODE_360X400 EQU 054h +XMODE_360X480 EQU 055h + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; DisplayInfo - Information about the current display. +; +; Mode - Mode identification +; XRes - X resolution +; YRes - Y resolution +; VBIbit - Polarity of vertical blank bit. +; Extended - Pointer to mode specified data structure. + + STRUC DisplayInfo +Mode DD ? +XRes DD ? +YRes DD ? +VBIbit DD ? +Extended DD ? + ENDS DisplayInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C GetDisplayInfo:NEAR + GLOBAL C GetVBIBit:NEAR + diff --git a/VQ/VQM32/XMODE.ASM b/VQ/VQM32/XMODE.ASM new file mode 100644 index 0000000..cec1fb7 --- /dev/null +++ b/VQ/VQM32/XMODE.ASM @@ -0,0 +1,748 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* xmode.asm +;* +;* DESCRIPTION +;* Xmode graphics display routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* SetXMode - Set the specified Xmode video mode. +;* ClearXMode - Clear the XMode VRAM. +;* ShowXPage - Set a specific page for XMode display. +;* Xmode_BufferCopy_320x200 - Copy 320x200 buffer to Xmode VRAM. +;* Xmode_Blit - Bit blit a block to the XMode display. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "video.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* SetXMode - Set the specified Xmode video mode. +;* +;* SYNOPSIS +;* Error = SetXMode(Mode) +;* +;* long SetXMode(long); +;* +;* FUNCTION +;* This routines set the current display adapter to the specified Xmode. +;* Portions of this routine were take from Dr. Dobb's, written in C, and +;* portions were taken from Dominic's 320x200 code. +;* +;* INPUTS +;* Mode - Xmode mode to set display to. +;* +;* RESULT +;* Error - 0 if successful, or -1 if error. +;* +;**************************************************************************** + + GLOBAL C SetXMode:NEAR + PROC SetXMode C NEAR USES edx + + ARG mode:DWORD + +??Set320x200: + cmp [mode],XMODE_320X200 ;320x200? + jne ??Set320x240 + + IF 0 + mov eax,MCGA + int 10h + +; Memory Mode: +; bit3 (chain) = 0 (planes are accessed via Map Mask) +; bit2 (odd/even) = 1 (use sequential addressing mode) + + INPORT R_SEQUENCER,SEQ_MEMORY_MODE + and al,not 08h ;Turn off chain 4 + or al,04h ;Turn off odd/even + out dx,al + + INPORT R_GRAPHICS_CONTROLLER,GC_MODE + and al,not 10h ;Turn off odd/even + out dx,al + + INPORT R_GRAPHICS_CONTROLLER,GC_MISC + and al,not 02h ;Turn off chain + out dx,al + + OUTPORT R_SEQUENCER,SEQ_MAP_MASK,0Fh + INPORT R_CRT_CONTROLLER, CRT_MAX_SCANLINE + and al,not 1fh ;Clear low 5 bits + or al,1 ;Mode = 0 => 400 lines + out dx,al ;Mode =1 => 200 + + INPORT R_CRT_CONTROLLER,CRT_UNDERLINE + and al,not 40h ;Turn off doubleword + out dx,al + + INPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL + or al,40h ;Turn on byte mode bit, + out dx,al ; so mem scanned linearly + ENDIF + +; The following section of code is from Roger Stevens' XMode +; example code; it's the same as 320x400, except the value sent +; to CRT_MAX_SCANLINE is 41, not 40. + + mov eax,MCGA + int 10h + + OUTPORT R_SEQUENCER,SEQ_MEMORY_MODE,06h + INPORT R_CRT_CONTROLLER,CRT_VERTRET_END + and al,07Fh + out dx,al + + OUTPORT R_CRT_CONTROLLER,CRT_MAX_SCANLINE,41h + OUTPORT R_CRT_CONTROLLER,CRT_UNDERLINE,00h + OUTPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL,0E3h + + mov eax,0 + jmp ??Done + +??Set320x240: + cmp [mode],XMODE_320X240 ;320x240? + jne ??Set320x400 + +; Start by setting MCGA to let the BIOS program the registers; +; then, reprogram the registers that need it. + + mov eax,MCGA + int 10h + +; Memory Mode: +; bit3 (chain) = 0 (planes are accessed via Map Mask) +; bit2 (odd/even) = 1 (use sequential addressing mode) +; bit1 (extended mem) = 1 (>64K video RAM) +; bit0 (alpha/graph) = 0 (graphics mode) + + OUTPORT R_SEQUENCER,SEQ_MEMORY_MODE,06h + +; Issue a Sequencer Reset + OUTPORT R_SEQUENCER,SEQ_RESET,01h + +; Misc Output: (set to 1100 0011) +; Bit 7: VSync polarity (1=negative) +; Bit 6: HSync polarity (1=negative) +; Bit 5: page bit for odd/even (0=low 64K) +; Bit 4: Video drivers (0=enable) +; Bit 3,2: clock select (0=25-MHz clock) +; Bit 1: VRAM access (1 = enable CPU to access) +; Bit 0: I/O Address (1=color emulation) + + mov edx,R_MISC_OUTPUT + mov al,0C3h + out dx,al + +; Clear Sequencer Reset + + OUTPORT R_SEQUENCER,SEQ_RESET,03h + +; Read Vertical Retrace End, and with 07f to clear high bit +; (clearing bit 7 enables writing to registers 0-7) + + INPORT R_CRT_CONTROLLER,CRT_VERTRET_END + and al,07Fh + out dx,al + +; Program the CRT Controller to display 480 scanlines, but to +; double each scanline so only 240 are displayed: + + OUTPORT R_CRT_CONTROLLER,CRT_UNDERLINE,00h + OUTPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL,0E3h + OUTPORT R_CRT_CONTROLLER,CRT_VERT_TOTAL,0Dh + OUTPORT R_CRT_CONTROLLER,CRT_OVERFLOW,03Eh + OUTPORT R_CRT_CONTROLLER,CRT_VERTRET_START,0EAh + OUTPORT R_CRT_CONTROLLER,CRT_VERTRET_END,0ACh + OUTPORT R_CRT_CONTROLLER,CRT_VERTDISP_END,0DFh + OUTPORT R_CRT_CONTROLLER,CRT_START_VB,0E7h + OUTPORT R_CRT_CONTROLLER,CRT_END_VB,06h + OUTPORT R_CRT_CONTROLLER,CRT_MAX_SCANLINE,041h + + xor eax,eax + jmp ??Done + +??Set320x400: + cmp [mode],XMODE_320X400 ;320x400 + jne ??Set320x480 + + mov eax,MCGA + int 10h + + OUTPORT R_SEQUENCER,04h,06h + INPORT R_CRT_CONTROLLER,011h + and al,07Fh + out dx,al + + OUTPORT R_CRT_CONTROLLER,09h,40h + OUTPORT R_CRT_CONTROLLER,014h,00h + OUTPORT R_CRT_CONTROLLER,017h,0E3h + + xor eax,eax + jmp ??Done + +??Set320x480: + cmp [mode],XMODE_320X480 ;320x480? + jne ??Set360x400 + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,0C3h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,04009h + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,00D06h + out dx,ax + mov eax,03E07h + out dx,ax + mov eax,0EA10h + out dx,ax + mov eax,0AC11h + out dx,ax + mov eax,0DF12h + out dx,ax + mov eax,0E715h + out dx,ax + mov eax,00616h + out dx,ax + mov eax,04009h + out dx,ax + + xor eax,eax + jmp ??Done + +??Set360x400: + cmp [mode],XMODE_360X400 ;360x400 + jne ??Set360x480 + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,067h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,06B00h + out dx,ax + mov eax,05901h + out dx,ax + mov eax,05A02h + out dx,ax + mov eax,08E03h + out dx,ax + mov eax,05E04h + out dx,ax + mov eax,08A05h + out dx,ax + mov eax,04009 + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,02D13h + out dx,ax + + xor eax,eax + jmp ??Done + +??Set360x480: + cmp [mode],XMODE_360X480 ;360x480? + jne ??Unknown_mode + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,0E7h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,06B00h + out dx,ax + mov eax,05901h + out dx,ax + mov eax,05A02h + out dx,ax + mov eax,08E03h + out dx,ax + mov eax,05E04h + out dx,ax + mov eax,08A05h + out dx,ax + mov eax,04009h + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,00D06h + out dx,ax + mov eax,03E07h + out dx,ax + mov eax,0EA10h + out dx,ax + mov eax,0AC11h + out dx,ax + mov eax,0DF12h + out dx,ax + mov eax,0E715h + out dx,ax + mov eax,00616h + out dx,ax + mov eax,02D13h + out dx,ax + + xor eax,eax + jmp ??Done + +??Unknown_mode: + mov eax,0FFFFFFFFh ;Unknown mode + +??Done: + ret + + ENDP SetXMode + + +;**************************************************************************** +;* +;* NAME +;* ClearXMode - Clear the XMode VRAM. +;* +;* SYNOPSIS +;* ClearXMode() +;* +;* void ClearXMode(void); +;* +;* FUNCTION +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ClearXMode:NEAR + PROC ClearXMode C NEAR USES eax ecx edi es + + IF PHARLAP_TNT + mov eax,01Ch + mov es,ax ;Set ES selector to VRAM + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + SET_PLANE 0Fh + mov ecx,((320*240*2)/4/4) + xor eax,eax + rep stosd + ret + + ENDP ClearXMode + + +;**************************************************************************** +;* +;* NAME +;* ShowXPage - Set a specific page for XMode display. +;* +;* SYNOPSIS +;* ShowXPage(Offset) +;* +;* void ShowXPage(); +;* +;* FUNCTION +;* Show the page at the specified offset in the bitmap. Page-flip takes +;* effect on the next active scan cycle. +;* +;* INPUTS +;* Offset - Offset to set page to. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ShowXPage:NEAR + PROC ShowXPage C NEAR USES eax ebx ecx edx + + ARG StartOffset:DWORD + + mov edx,R_CRT_CONTROLLER + mov bl,CRT_STARTADDR_LOW + mov bh,[byte ptr StartOffset] + mov cl,CRT_STARTADDR_HIGH + mov ch,[byte ptr StartOffset+1] + mov eax,ebx + out dx,ax + mov eax,ecx + out dx,ax + ret + + ENDP ShowXPage + + +;**************************************************************************** +;* +;* NAME +;* Xmode_BufferCopy_320x200 - Copy 320x200 buffer to Xmode VRAM. +;* +;* SYNOPSIS +;* Xmode_BufferCopy_320x200(Buffer, Screen) +;* +;* void Xmode_BufferCopy_320x200(char *, char *); +;* +;* FUNCTION +;* BitBlt copy to VRAM. +;* +;* INPUTS +;* Buffer - Pointer to buffer to copy to XMode VRAM. +;* Screen - XMode VRAM screen address to copy buffer to. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Xmode_BufferCopy_320x200:NEAR + PROC Xmode_BufferCopy_320x200 C NEAR USES eax ecx edx edi esi es + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + + LOCAL save_esi:DWORD + LOCAL save_edi:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to VRAM. + mov es,ax + ENDIF + + mov esi,[buffer] ;Set pointers + mov edi,[screen] + mov [save_esi],esi + mov [save_edi],edi + +;---------------------------------------------------------------------------- +; Copy plane 1 +;---------------------------------------------------------------------------- + + SET_PLANE XPLANE_1 + mov ecx,4000 + +x_loop_1: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 1 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_1 + +;---------------------------------------------------------------------------- +; Copy plane 2 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + inc esi ;Adjust source pointer to plane 2 + + SET_PLANE XPLANE_2 + mov ecx,4000 + +x_loop_2: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 2 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_2 + +;---------------------------------------------------------------------------- +; Copy plane 3 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + add esi,2 ;Adjust source pointer to plane 3 + + SET_PLANE XPLANE_3 + mov ecx,4000 + +x_loop_3: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 3 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_3 + +;---------------------------------------------------------------------------- +; Copy plane 4 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + add esi,3 ;Adjust source pointer to plane 4 + + SET_PLANE XPLANE_4 + mov ecx,4000 +x_loop_4: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 4 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next screen position + dec ecx + jnz short x_loop_4 + ret + + ENDP Xmode_BufferCopy_320x200 + + +;**************************************************************************** +;* +;* NAME +;* Xmode_Blit - Bit blit a block to the XMode display. +;* +;* SYNOPSIS +;* XMode_Blit(Buffer, Screen, Width, Height) +;* +;* void XMode_Blit(char *, char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer buffer to blit to screen. +;* Screen - Screen address to blit buffer to. +;* Width - Width of buffer. +;* Height - Height of buffer. +;* +;* RESULT +;* NONE +;* +;* WARNINGS +;* Assumes the screen to be 320 pixels wide and the source buffer width +;* to be divisible by 16. +;* +;**************************************************************************** + + GLOBAL C Xmode_Blit:NEAR + PROC Xmode_Blit C NEAR USES ecx edx esi edi es + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + ARG imgwidth:DWORD + ARG imgheight:DWORD + + LOCAL rowcount:DWORD + LOCAL xplane:DWORD + LOCAL edi_startval:DWORD + LOCAL esi_startval:DWORD + LOCAL xadd:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov edi,[screen] + mov [esi_startval],esi + mov [edi_startval],edi + + mov edx,320 ;Compute modulo + sub edx,[imgwidth] + shr edx,2 + mov [xadd],edx + +;---------------------------------------------------------------------------- +; Transfer the data on plane at a time. +;---------------------------------------------------------------------------- + + mov [xplane],1 + +??Do_plane: + SET_PLANE [xplane] ;Set plane to transfer to + mov eax,[imgheight] + mov [rowcount],eax + mov edx,[xadd] + +??Do_row: + mov ecx,[imgwidth] ;Length of row to copy in DWORDS + shr ecx,4 + +; Transfer a row of pixels + +??Not_done: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write pixels to VRAM plane + + add esi,16 ;Next source position + add edi,4 ;Next VRAM position + dec ecx + jnz ??Not_done + + add edi,edx ;Next VRAM row + dec [rowcount] ;Decrement the row count + jnz ??Do_row + +; Go to next X-Plane + + inc [esi_startval] + mov eax,[esi_startval] + mov esi,eax + mov eax,[edi_startval] + mov edi,eax + shl [xplane],1 + cmp [xplane],16 + jnz ??Do_plane + ret + + ENDP Xmode_Blit + + END + diff --git a/VQ/VQM32/XMODEPG.CPP b/VQ/VQM32/XMODEPG.CPP new file mode 100644 index 0000000..b2a8604 --- /dev/null +++ b/VQ/VQM32/XMODEPG.CPP @@ -0,0 +1,201 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* xmodepg.c +* +* DESCRIPTION +* Xmode page access. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SetupXPaging - Setup Xmode paging variables. +* FlipXPage - Page flip to next Xmode page. +* ShowXPage - Show the specified Xmode page. +* GetXHidPage - Get the address of the current Xmode HidPage. +* GetXSeenPage - Get the address of the current Xmode SeenPage. +* +****************************************************************************/ + +#include +#include "video.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define PAGE0_START_OFFSET 0 +#define PAGE1_START_OFFSET ((320 * 240) / 4) + +/* PageFlip page values. */ +static unsigned long PageStartOffsets[2] = { + PAGE0_START_OFFSET, + PAGE1_START_OFFSET +}; + +static long DisplayedPage; +static long NonDisplayedPage; + +/**************************************************************************** +* +* NAME +* SetupXPaging - Setup Xmode paging variables. +* +* SYNOPSIS +* SetupXPaging() +* +* void SetupXPaging(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void SetupXPaging(void) +{ + DisplayedPage = 1; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* FlipXPage - Page flip to next Xmode page. +* +* SYNOPSIS +* FlipXPage() +* +* void FlipXPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void FlipXPage(void) +{ + ShowXPage(PageStartOffsets[NonDisplayedPage]); + DisplayedPage = NonDisplayedPage; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* ShowXPage - Show the specified Xmode page. +* +* SYNOPSIS +* ShowXPage(page) +* +* void ShowXPage(long); +* +* FUNCTION +* +* INPUTS +* Page - Xmode page number to show. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DisplayXPage(long page) +{ + ShowXPage(PageStartOffsets[page & 1]); + DisplayedPage = page; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* GetXHidPage - Get the address of the current Xmode HidPage. +* +* SYNOPSIS +* HidPage = GetXHidPage() +* +* unsigned char *GetXHidPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* HidPage - Address of Xmode HidPage. +* +****************************************************************************/ + +unsigned char *GetXHidPage(void) +{ + return((unsigned char *)PageStartOffsets[NonDisplayedPage]); +} + + +/**************************************************************************** +* +* NAME +* GetXSeenPage - Get the address of the current Xmode SeenPage. +* +* SYNOPSIS +* SeenPage = GetXSeenPage() +* +* unsigned char *GetXSeenPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* SeePage - Address of the Xmode SeenPage. +* +****************************************************************************/ + +unsigned char *GetXSeenPage(void) +{ + return ((unsigned char *)PageStartOffsets[DisplayedPage]); +} + diff --git a/WIN32LIB/AUDIO/AUDIO.H b/WIN32LIB/AUDIO/AUDIO.H new file mode 100644 index 0000000..841408a --- /dev/null +++ b/WIN32LIB/AUDIO/AUDIO.H @@ -0,0 +1,160 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; + +extern int StreamLowImpact; diff --git a/WIN32LIB/AUDIO/AUDIO.IDE b/WIN32LIB/AUDIO/AUDIO.IDE new file mode 100644 index 0000000..c30a34c Binary files /dev/null and b/WIN32LIB/AUDIO/AUDIO.IDE differ diff --git a/WIN32LIB/AUDIO/AUDUNCMP.ASM b/WIN32LIB/AUDIO/AUDUNCMP.ASM new file mode 100644 index 0000000..cd320bf --- /dev/null +++ b/WIN32LIB/AUDIO/AUDUNCMP.ASM @@ -0,0 +1,374 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Audio Library * +;* * +;* File Name : AUDUNCMP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : March 14, 1995 * +;* * +;* Last Update : June 26, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Decompress_Frame_Lock -- locks the JLB audio decompression code * +;* Decompress_Frame_Unlock -- Unlocks the JLB audio compression code * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +DPMI_INTR equ 31h + +LABEL LockedCodeStart BYTE + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + + +;*************************************************************************** +;* DECOMPRESS_FRAME -- Uncompresses a WW compressed audio frame * +;* * +;* INPUT: void * source - pointer to encoded audio data * +;* void * dest - pointer to decompression area * +;* long size - the maximum size of destination buffer * +;* * +;* OUTPUT: long - the number of bytes we uncompressed * +;* * +;* PROTO: long Decompress_Frame(void *, void *, long); * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Decompress_Frame:NEAR + PROC Decompress_Frame C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + pushfd + cld + mov [incount],0 ;Bytes read from source + + +; Source, Dest and count must be valid. + + cmp [source],0 + je ??fini + + cmp [dest],0 + je ??fini + + cmp [count],0 + je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popfd + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popfd + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popfd + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popfd + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popfd + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popfd + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popfd + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popfd + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popfd + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popfd + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popfd + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popfd + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + popfd + mov eax,[incount] + ret + + ENDP Decompress_Frame + +LABEL LockedCodeEnd BYTE + + + + + + END \ No newline at end of file diff --git a/WIN32LIB/AUDIO/DIFFTB.INC b/WIN32LIB/AUDIO/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WIN32LIB/AUDIO/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WIN32LIB/AUDIO/FUNCTION.H b/WIN32LIB/AUDIO/FUNCTION.H new file mode 100644 index 0000000..1b7b679 --- /dev/null +++ b/WIN32LIB/AUDIO/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/WIN32LIB/AUDIO/INDEXTB.INC b/WIN32LIB/AUDIO/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WIN32LIB/AUDIO/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WIN32LIB/AUDIO/MAKEFILE b/WIN32LIB/AUDIO/MAKEFILE new file mode 100644 index 0000000..af73e7e --- /dev/null +++ b/WIN32LIB/AUDIO/MAKEFILE @@ -0,0 +1,186 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = AUDIO +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + soundio.obj & + soundint.obj & + soundlck.obj & + soscodec.obj & + olsosdec.obj & + auduncmp.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;..\..\dxsdk\lib;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;..\..\dxsdk\inc;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.lib $(%WIN32LIB)\lib + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + copy *.lib $(%WIN32LIB)\lib + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + copy *.386 test + +#**************************** End of makefile ****************************** diff --git a/WIN32LIB/AUDIO/MAKEFILE.BOR b/WIN32LIB/AUDIO/MAKEFILE.BOR new file mode 100644 index 0000000..3674c16 --- /dev/null +++ b/WIN32LIB/AUDIO/MAKEFILE.BOR @@ -0,0 +1,177 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = audio +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + soundio.obj \ + soundint.obj \ + soundlck.obj \ + soscodec.obj \ + auduncmp.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-soundio.obj & ++-soundint.obj & ++-soundlck.obj & ++-soscodec.obj & ++-auduncmp.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/AUDIO/NYBBTB.INC b/WIN32LIB/AUDIO/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WIN32LIB/AUDIO/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WIN32LIB/AUDIO/OLD/AUDIO.H b/WIN32LIB/AUDIO/OLD/AUDIO.H new file mode 100644 index 0000000..b62d847 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/AUDIO.H @@ -0,0 +1,158 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; diff --git a/WIN32LIB/AUDIO/OLD/AUDIO.IDE b/WIN32LIB/AUDIO/OLD/AUDIO.IDE new file mode 100644 index 0000000..c30a34c Binary files /dev/null and b/WIN32LIB/AUDIO/OLD/AUDIO.IDE differ diff --git a/WIN32LIB/AUDIO/OLD/AUDUNCMP.ASM b/WIN32LIB/AUDIO/OLD/AUDUNCMP.ASM new file mode 100644 index 0000000..cd320bf --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/AUDUNCMP.ASM @@ -0,0 +1,374 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Audio Library * +;* * +;* File Name : AUDUNCMP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : March 14, 1995 * +;* * +;* Last Update : June 26, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Decompress_Frame_Lock -- locks the JLB audio decompression code * +;* Decompress_Frame_Unlock -- Unlocks the JLB audio compression code * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +DPMI_INTR equ 31h + +LABEL LockedCodeStart BYTE + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + + +;*************************************************************************** +;* DECOMPRESS_FRAME -- Uncompresses a WW compressed audio frame * +;* * +;* INPUT: void * source - pointer to encoded audio data * +;* void * dest - pointer to decompression area * +;* long size - the maximum size of destination buffer * +;* * +;* OUTPUT: long - the number of bytes we uncompressed * +;* * +;* PROTO: long Decompress_Frame(void *, void *, long); * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Decompress_Frame:NEAR + PROC Decompress_Frame C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + pushfd + cld + mov [incount],0 ;Bytes read from source + + +; Source, Dest and count must be valid. + + cmp [source],0 + je ??fini + + cmp [dest],0 + je ??fini + + cmp [count],0 + je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popfd + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popfd + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popfd + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popfd + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popfd + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popfd + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popfd + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popfd + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popfd + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popfd + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popfd + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popfd + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + popfd + mov eax,[incount] + ret + + ENDP Decompress_Frame + +LABEL LockedCodeEnd BYTE + + + + + + END \ No newline at end of file diff --git a/WIN32LIB/AUDIO/OLD/DIFFTB.INC b/WIN32LIB/AUDIO/OLD/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WIN32LIB/AUDIO/OLD/FUNCTION.H b/WIN32LIB/AUDIO/OLD/FUNCTION.H new file mode 100644 index 0000000..1b7b679 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/WIN32LIB/AUDIO/OLD/INDEXTB.INC b/WIN32LIB/AUDIO/OLD/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WIN32LIB/AUDIO/OLD/MAKEFILE b/WIN32LIB/AUDIO/OLD/MAKEFILE new file mode 100644 index 0000000..a16a74c --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/MAKEFILE @@ -0,0 +1,186 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = AUDIO +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + soundio.obj & + soundint.obj & + soundlck.obj & + soscodec.obj & + olsosdec.obj & + auduncmp.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.lib $(%WIN32LIB)\lib + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + copy *.lib $(%WIN32LIB)\lib + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + copy *.386 test + +#**************************** End of makefile ****************************** diff --git a/WIN32LIB/AUDIO/OLD/MAKEFILE.BOR b/WIN32LIB/AUDIO/OLD/MAKEFILE.BOR new file mode 100644 index 0000000..3674c16 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/MAKEFILE.BOR @@ -0,0 +1,177 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = audio +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + soundio.obj \ + soundint.obj \ + soundlck.obj \ + soscodec.obj \ + auduncmp.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-soundio.obj & ++-soundint.obj & ++-soundlck.obj & ++-soscodec.obj & ++-auduncmp.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/AUDIO/OLD/MAKEFILE.WAT b/WIN32LIB/AUDIO/OLD/MAKEFILE.WAT new file mode 100644 index 0000000..3674c16 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/MAKEFILE.WAT @@ -0,0 +1,177 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = audio +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + soundio.obj \ + soundint.obj \ + soundlck.obj \ + soscodec.obj \ + auduncmp.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-soundio.obj & ++-soundint.obj & ++-soundlck.obj & ++-soscodec.obj & ++-auduncmp.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/AUDIO/OLD/NYBBTB.INC b/WIN32LIB/AUDIO/OLD/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WIN32LIB/AUDIO/OLD/OLSOSDEC.ASM b/WIN32LIB/AUDIO/OLD/OLSOSDEC.ASM new file mode 100644 index 0000000..71233f6 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/OLSOSDEC.ASM @@ -0,0 +1,755 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 diff --git a/WIN32LIB/AUDIO/OLD/SOS.H b/WIN32LIB/AUDIO/OLD/SOS.H new file mode 100644 index 0000000..9a77c15 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far __cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far __cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif \ No newline at end of file diff --git a/WIN32LIB/AUDIO/OLD/SOSCODEC.ASM b/WIN32LIB/AUDIO/OLD/SOSCODEC.ASM new file mode 100644 index 0000000..6e6011b --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSCODEC.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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) +;* Jonathan Lanier +;* +;* DATE +;* Febuary 15, 1995 +;* +;* LAST MODIFIED +;* 08/07/95 [jdl] - Rewrote/optimized sosCODECDecompressData +;* +;*--------------------------------------------------------------------------- +;* +;* 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 + +INCLUDE "difftb.inc" +INCLUDE "indextb.inc" +INCLUDE "nybbtb.inc" + +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 sosCODECInitStream:NEAR + PROC sosCODECInitStream C NEAR + + ARG sSOSInfo:NEAR PTR + + mov eax,[sSOSInfo] + mov [(sCompInfo eax).wIndex],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted],0 ; no predicted value + mov [(sCompInfo eax).wIndex2],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted2],0 ; no predicted value + 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 decompress. +;* +;* RESULT +;* Size - Size of decompressed data. +;* +;* +;* NOTES +;* This routine has been optimized for pipelining on both +;* 486 and Pentium processors. Changing, inserting, or moving any +;* instructions will most likely slow down the code, in some cases by +;* as much as 20%. It can burst-decompress 16384 samples in about +;* 1940æs on a Pentium 90Mhz, and about 3960æs on a 486 66Mhz. +;* Instruction reordering could bring this down to below 1870æs on +;* the Pentium, but this would cause a great degradation in 486 +;* performance. Since slow 486's are the reason this code was +;* written to be fast, it has been optimized for the Pentium only where +;* it would not degrade 486 performance. So, be careful when changing +;* ANY of this code, because it is very carefully balanced... +;**************************************************************************** + + GLOBAL C sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + push esi + push edi + push ebx + push ecx + push edx ;save all the regs + + mov ebx,[sSOSInfo] ;get base of sCompInfo struct + mov cx,[(sCompInfo ebx).wBitSize] ;check the bit size + mov dx,[(sCompInfo ebx).wChannels] ;check the number of channels +; +; +; Determine the correct routine to use for decoding +; (for now only ADPCM 4:1 Mono 16-bit is implemented) + cmp cx,8 + jne ??do16Bits + +??do8Bits: + cmp dx,2 + jne ??not8Stereo +; jmp ??decomp8Stereo + jmp ??byeBye + +??not8Stereo: + cmp dx,1 + jne ??byeBye +; jmp decomp8Mono + jmp ??byeBye + +??do16Bits: + cmp cx,16 + jne ??byeBye + + cmp dx,2 + jne ??not16Stereo +; jmp ??decomp16Stereo + jmp ??byeBye + +??not16Stereo: + cmp dx,1 + jne ??byeBye + + push ebp +; +; +; 16 bit ADPCM 4:1 Mono pre-loop initialization +??decomp16Mono: + push ebx ;save struct base + xor edx,edx ;clear index + mov eax,[(sCompInfo ebx).dwPredicted] ;get last sample + mov dx,[(sCompInfo ebx).wIndex] ;get last index value + mov esi,[(sCompInfo ebx).lpSource] ;get source address + mov edi,[(sCompInfo ebx).lpDest] ;get dest address + + mov ebp,[wBytes] ;get the number of dest. bytes + cmp ebp,16 ;less than 16? (less than 8 samples) + jl ??fixAlign16Mono0 ;if so, don't bother with alignment +; +; +; Check to see if we need to fix an alignment problem on the source buffer +; (non-aligned buffers are MUCH slower; if we're given a non-DWORD aligned +; source address, we do as many samples as needed to get to the nearest +; DWORD boundary, then finish the bulk as a DWORD-aligned decompress). + mov ebx,esi ;get source address + and ebx,03h ;check LSB + jnz ??fixalign16Mono ;if non-zero, need to align for + ;warp speed +??fixAlign16Mono0: + push ebp ;save for later + shr ebp,4 ;divide by 16 for 16-bit, + ;because we do 8 nybbles per loop, + ;and there are two samples per + ;byte, so there are n/16 iterations + ;required + xor ebx,ebx ;clear our nybble index + or ebp,ebp ;set flags for EBP + jmp ??start16Mono ;start with test... don't go if + ;we have zero bytes to do +??fixalign16Mono: + jmp [DWORD PTR dwMono16AlignJmpTable+ebx*4] ;do non-aligned first + + align 4 +??fixAlign16Mono1: + sub ebp,12 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono6 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono2: + sub ebp,8 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono4 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono3: + sub ebp,4 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono2 ;borrow exit code to go through a + ;piece of a loop +; "The Loop" +; +; Process 1st nybble + align 4 +??loop16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix1Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix1Smp16MonoU +??fixed1Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi],ax ;save the sample +; +; +; Process 2nd nybble +??finish7Smp16Mono: + mov bl,ch ;get next 2 nybbles in ebx + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix2Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix2Smp16MonoU +??fixed2Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+02h],ax ;save the sample + shr ecx,16 ;move top four nybbles into bottom +; +; +; Process 3rd nybble +??finish6Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix3Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix3Smp16MonoU +??fixed3Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+04h],ax ;save the sample +; +; +; Process 4th nybble +??finish5Smp16Mono: + mov bl,cl + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix4Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix4Smp16MonoU +??fixed4Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+06h],ax ;save the sample +; +; +; Process 5th nybble +??finish4Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix5Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix5Smp16MonoU +??fixed5Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+08h],ax ;save the sample +; +; +; Process 6th nybble +??finish3Smp16Mono: + mov bl,ch + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix6Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix6Smp16MonoU +??fixed6Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+0Ah],ax ;save the sample +; +; +; Process 7th nybble +??finish2Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix7Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix7Smp16MonoU +??fixed7Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+0Ch],ax ;save the sample +; +; +; Process 8th nybble +??finish1Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix8Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix8Smp16MonoU +; +; +; Loop cleanup for next pass +??fixed8Smp16Mono: + mov [edi+0Eh],ax ;save the sample + add esi,04h ;bump esi to point to next longword + add edi,10h ;incr. the destination buffer ptr + dec ebp ;count down the number of samples/8 +??start16Mono: + jng ??cleanup16Mono ;if done, clean up + mov ecx,[esi] ;get 4 nybbles in one whack (whee!) + mov bl,cl ;get next 2 nybbles in ebx + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??loop16Mono ;loop until done + +??cleanup16Mono: + jnz ??done16Mono ;if ebp is non-zero, we're DONE + ;if exactly zero, finish the tail-end + ;of the conversion (may be a non- + ;multiple of 8 nybbles) +; +; +; Loop cleanup for last (incomplete) pass + pop ecx ;restore # of words + shr ecx,1 ;divide by two to get samples + and ecx,07h ;get # of samples we missed + jmp [DWORD PTR dwMono16JmpTable+ecx*4] ;go finish the job... +; +; +; Structure cleanup +??done16Mono: + pop ebx ;restore struct base + pop ebp ;restore stack frame pointer + mov [(sCompInfo ebx).dwPredicted],eax ;save last sample + mov [(sCompInfo ebx).wIndex],dx ;save last index value + mov eax,[wBytes] ;get # of bytes we did +??byeBye: + pop edx ;restore all the regs + pop ecx + pop ebx + pop edi + pop esi + ret +; +; +; Jumps for -32768/+32767 bounds check go to these vvvv + align 4 +??fix1Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix1Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix2Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix2Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix3Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix3Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix4Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix4Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix5Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix5Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix6Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix6Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix7Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix7Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix8Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed8Smp16Mono ;go back + + align 4 +??fix8Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed8Smp16Mono ;go back +; +; +; Jump tables for cleanup after loop unroll point to these vvvv + align 4 +??finish16Mono1: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 1 nybble (1 byte) + shl ch,4 ;move it over + mov bl,ch ;get nybble in ebx + sub edi,0Eh ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish1Smp16Mono ;go finish it + + align 4 +??finish16Mono2: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 2 nybbles (1 byte) + mov bl,ch ;get nybbles in ebx + sub edi,0Ch ;back edi up + sub esi,3 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish2Smp16Mono ;go finish it + + align 4 +??finish16Mono3: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 3 nybbles (2 bytes) + shl cx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,0Ah ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish3Smp16Mono ;go finish it + + align 4 +??finish16Mono4: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + sub edi,08h ;back edi up + sub esi,2 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish4Smp16Mono ;go finish it + + align 4 +??finish16Mono5: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 1 nybble (1 byte) + shl ecx,16 ;shift it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,4 ;move it over + shl bl,4 ;move it over + sub edi,06h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish5Smp16Mono ;go finish it + + align 4 +??finish16Mono6: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 2 nybbles (1 byte) + shl ecx,16 ;move it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,8 ;move it over + sub esi,1 ;adjust esi (used for dword aligning) + sub edi,04h ;back edi up + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish6Smp16Mono ;go finish it + + align 4 +??finish16Mono7: + xor ecx,ecx ;clear nybble bucket + mov ecx,[esi] ;get 7 nybbles (4 bytes) + shl ecx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,02h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish7Smp16Mono ;go finish it +; +; +; Jump Tables + align 4 + +dwMono16JmpTable DD ??done16Mono + DD ??finish16Mono1 + DD ??finish16Mono2 + DD ??finish16Mono3 + DD ??finish16Mono4 + DD ??finish16Mono5 + DD ??finish16Mono6 + DD ??finish16Mono7 + + align 4 +dwMono16AlignJmpTable DD ??fixAlign16Mono0 + DD ??fixAlign16Mono1 + DD ??fixAlign16Mono2 + DD ??fixAlign16Mono3 + + ENDP 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 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 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 + diff --git a/WIN32LIB/AUDIO/OLD/SOSCOMP.H b/WIN32LIB/AUDIO/OLD/SOSCOMP.H new file mode 100644 index 0000000..de369d7 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void __cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long __cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl General_sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif diff --git a/WIN32LIB/AUDIO/OLD/SOSDATA.H b/WIN32LIB/AUDIO/OLD/SOSDATA.H new file mode 100644 index 0000000..f6686a5 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WIN32LIB/AUDIO/OLD/SOSDEFS.H b/WIN32LIB/AUDIO/OLD/SOSDEFS.H new file mode 100644 index 0000000..9bb29e7 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WIN32LIB/AUDIO/OLD/SOSFNCT.H b/WIN32LIB/AUDIO/OLD/SOSFNCT.H new file mode 100644 index 0000000..2945aa4 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSFNCT.H @@ -0,0 +1,219 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int __cdecl sosRealFree ( int ); +extern BOOL __cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int __cdecl sosRealAlloc( int, int *, int * ); +extern void __cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int __cdecl sosGetCS( VOID ); +extern int __cdecl sosGetES( VOID ); +#else +extern int __cdecl sosRealAlloc ( int, int *, int * ); +extern int __cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD __cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD __cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void __cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void __cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void __cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void __cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void __cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void __cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void __cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void __cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD __cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD __cdecl sosDIGIInitForWindows( WORD ); +extern WORD __cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR __cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR __cdecl sosCreateAliasCS ( LPSTR ); +extern VOID __cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR __cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID __cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR __cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID __cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID __cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD __cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD __cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD __cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD __cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD __cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD __cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD __cdecl getDS( VOID ); +extern WORD __cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD __cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void __cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void __cdecl sosTIMERDRVUnInit( void ); +extern void __cdecl sosTIMERDRVHandler( void ); +extern void __cdecl sosTIMERDRVFHandler( void ); +extern void __cdecl sosTIMERDRVEnable( void ); +extern void __cdecl sosTIMERDRVDisable( void ); +extern void __cdecl sosTIMERDRVCallOld( void ); +extern void __cdecl sosTIMERDRVSetRate( WORD ); +extern void __cdecl sosDIGITimer_Start( void ); +extern void __cdecl sosDIGITimer_End( void ); +extern void __cdecl sosDIGIDrv_Start( void ); +extern void __cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif + diff --git a/WIN32LIB/AUDIO/OLD/SOSRES.H b/WIN32LIB/AUDIO/OLD/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WIN32LIB/AUDIO/OLD/SOUND.H b/WIN32LIB/AUDIO/OLD/SOUND.H new file mode 100644 index 0000000..fdeb78b --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOUND.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/WIN32LIB/AUDIO/OLD/SOUNDINT.CPP b/WIN32LIB/AUDIO/OLD/SOUNDINT.CPP new file mode 100644 index 0000000..806cf34 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOUNDINT.CPP @@ -0,0 +1,538 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include "dsound.h" +#include +#include "soundint.h" +#include "memflag.h" +#include "audio.h" + +extern DebugBuffer[]; + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + + + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * , short int *) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + if (st->sosinfo.wBitSize==16 && st->sosinfo.wChannels==1){ + sosCODECDecompressData(&st->sosinfo, dsize); + } else { + General_sosCODECDecompressData(&st->sosinfo, dsize); + } + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + + + + + + + +/*********************************************************************************************** + * maintenance_callback -- routine to service the direct play secondary buffers * + * and other stuff..? * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * ....Unknown * + * 10/17/95 10:15PM ST : tidied up a tad for direct sound * + *=============================================================================================*/ +VOID far __cdecl maintenance_callback(VOID) +{ + + int index; //index used in for loop + SampleTrackerType *st; //ptr to SampleTracker structure + DWORD play_cursor; //Position that direct sound is reading from + DWORD write_cursor; //Position in buffer that we can write to + int bytes_copied; //Number of bytes copied into the buffer + BOOL write_more; //Flag to set if we need to write more into the buffer + LPVOID play_buffer_ptr; //Beginning of locked area of buffer + LPVOID dummy_buffer_ptr; //Length of locked area in buffer + DWORD lock_length1; //Beginning of second locked area in buffer + DWORD lock_length2; //Length of second locked area in buffer + HRESULT return_code; + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the direct sound buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service && !st->DontTouch ) { + + //EnterCriticalSection (&st->AudioCriticalSection); + + st->DontTouch = TRUE; + + /* + ** Get the current position of the direct sound play cursor within the buffer + */ + return_code = st->PlayBuffer->GetCurrentPosition ( &play_cursor , &write_cursor ); + + /* + ** Check for unusual situations like a focus loss + */ + if (return_code != DS_OK){ + if (return_code == DSERR_BUFFERLOST){ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + //LeaveCriticalSection (&st->AudioCriticalSection); + return; //Our app has lost focus or something else nasty has happened + } //so dont update the sound buffers + + + if (st->MoreSource){ + + /* + ** If the direct sound read pointer is less than a quarter + ** of a buffer away from the end of the data then copy some + ** more. + */ + write_more = FALSE; + + if ( play_cursor < (unsigned)st->DestPtr ){ + if ( (unsigned)st->DestPtr - (unsigned)play_cursor <= SECONDARY_BUFFER_SIZE/4 ){ + write_more=TRUE; + } + } else { + /* The only time that play_cursor can be greater than DestPtr is + ** if we wrote right to the end of the buffer last time and DestPtr + ** looped back to the beginning of the buffer. + ** That being the case, all we have to do is see if play_cursor is + ** within the last 25% of the buffer + */ + if ( ( (int)play_cursor > SECONDARY_BUFFER_SIZE*3/4) &&st->DestPtr==0 ){ + write_more=TRUE; + } + } + + if (write_more){ + + /* + ** Lock a 1/2 of the direct sound buffer so we can write to it + */ + if ( DS_OK== st->PlayBuffer->Lock ( (DWORD)st->DestPtr , + (DWORD)SECONDARY_BUFFER_SIZE/2, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 )){ + + bytes_copied = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + + if ( bytes_copied != (SECONDARY_BUFFER_SIZE/4) ){ + /* + ** We must have reached the end of the sample + */ + st->MoreSource=FALSE; + memset (((char*)play_buffer_ptr)+bytes_copied , + 0 , + (SECONDARY_BUFFER_SIZE/4)-bytes_copied); + + /* + ** Clear out an extra area in the buffer ahead of the play cursor + ** to give us a quiet period of grace in which to stop the buffer playing + */ + if ( (unsigned)st->DestPtr == SECONDARY_BUFFER_SIZE*3/4 ){ + if ( dummy_buffer_ptr && lock_length2 ){ + memset (dummy_buffer_ptr , 0 , lock_length2); + } + } else { + memset ((char*)play_buffer_ptr+SECONDARY_BUFFER_SIZE/4 , 0 , SECONDARY_BUFFER_SIZE/4); + } + } + + /* + ** Update our pointer into the direct sound buffer + ** + */ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,bytes_copied); + + if ( (unsigned)st->DestPtr >= (unsigned)SECONDARY_BUFFER_SIZE ){ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,(long)-SECONDARY_BUFFER_SIZE); + } + + + /* + ** Unlock the direct sound buffer + */ + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + } + + } //write_more + + } else { //!more_source + + /* + ** no more source to write - check if the buffer play + ** has overrun the end of the sample and stop it if it has + */ + if ( ( (play_cursor >= (unsigned)st->DestPtr) && ( ((unsigned)play_cursor - (unsigned)st->DestPtr) OneShot &&( (play_cursor < (unsigned)st->DestPtr) && ( ((unsigned)st->DestPtr - (unsigned)play_cursor) >(SECONDARY_BUFFER_SIZE*3/4) ) )) ){ + st->PlayBuffer->Stop(); + st->Service = FALSE; + Stop_Sample( index ); + } + } //more_source + + st->DontTouch = FALSE; + + //LeaveCriticalSection (&st->AudioCriticalSection); + } + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + //EnterCriticalSection (&st->AudioCriticalSection); + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + +#ifdef NO_VOLUME_CONTROL + st->PlayBuffer->SetVolume (-( ( (32768-st->Volume)*1000) >>15 ) ); +#endif //ifdef NO_VOLUME_CONTROL + //LeaveCriticalSection (&st->AudioCriticalSection); + } + st++; + } + LockedData._int--; + } + + //LeaveCriticalSection(&GlobalAudioCriticalSection); +} + + + + + + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WIN32LIB/AUDIO/OLD/SOUNDINT.H b/WIN32LIB/AUDIO/OLD/SOUNDINT.H new file mode 100644 index 0000000..1a69aab --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOUNDINT.H @@ -0,0 +1,292 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/WIN32LIB/AUDIO/OLD/SOUNDIO.CPP b/WIN32LIB/AUDIO/OLD/SOUNDIO.CPP new file mode 100644 index 0000000..f7d9bc1 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOUNDIO.CPP @@ -0,0 +1,2121 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Load_Long_Sample -- Loads a sample into XMS for double buffer system. * + * Read_Long_Sample -- Loads/Processes/Formats/Builds offset. * + * Save_Table_Entry -- Put an entry in the offset table. * + * Play_Long_Sample -- Calls Init_Long_Sample and Start_Long_Sample. * + * Start_Long_Sample -- Starts a sample playing that has be initialized. * + * Get_Table_Entry -- Gets next entry in table. * + * Long_Sample_Ticks -- Gets number of ticks in sample if in header. * + * Long_Sample_Status -- Returns the status of the sample. * + * Find_Table_Entry -- Finds next entry in table that matches mask. * + * Get_Table_Start -- Returns a pointer to first entry in table. * + * Long_Sample_Ticks_Played -- Number of ticks since sample started. * + * Install_Sample_Driver_Callback -- Pokes callback function into JM driver * + * Stop_Long_Sample -- Stops current long sample from playing. * + * Long_Sample_Loaded_Size -- Max buffer size to load a long sample. * + * Sound_Callback -- Audio driver callback function. * + * DigiCallback -- Low level double buffering handler. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Stream_Sample -- Streams a sample directly from a file. * + * Sample_Read -- Reads sample data from an openned file. * + * Continue_Sample -- Tags another block of data onto the currently playing. * + * Sample_Copy -- Copies sound data from source format to raw format. * + * File_Stream_Preload -- Handles initial proload of a streaming samples bu* + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern void Colour_Debug (int call_number); +#pragma pack(4) + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#include +#include +#include "dsound.h" + +#include +#include +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + +LPDIRECTSOUNDBUFFER DumpBuffer; +HANDLE SoundThreadHandle = NULL; +BOOL SoundThreadActive = FALSE; + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This is the number of times per sec that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 40 //30 times per sec plus a safety margin + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +//#define STREAM_BUFFER_SIZE (128L*1024L) +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +//void *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +short StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; +LPDIRECTSOUND SoundObject; //Direct sound object +LPDIRECTSOUNDBUFFER PrimaryBufferPtr; //Pointer to the buffer that the +unsigned SoundTimerHandle=0; //Windows Handle for sound timer +WAVEFORMATEX DsBuffFormat; //format of direct sound buffer +DSBUFFERDESC BufferDesc; //Buffer description for creating buffers +WAVEFORMATEX PrimaryBuffFormat; //Copy of format of direct sound primary buffer +DSBUFFERDESC PrimaryBufferDesc; //Copy of buffer description for re-creating primary buffer +CRITICAL_SECTION GlobalAudioCriticalSection; +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void) = NULL; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(short id, short *odd, void **buffer, long *size); +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle); +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ); +//static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size)); +void Sound_Thread (void *); +volatile BOOL AudioDone; +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +// This callback is called whenever the queue buffer playback has begun +// and another buffer is needed for queuing up. Returns TRUE if there +// is more data to read from the file. +static BOOL File_Callback(short id, short *odd, void **buffer, long *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + void *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = (short)(*odd + 1); + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != WW_ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + while (num_empty_buffers && (st->FileHandle != WW_ERROR)) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return(TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + + } + return(FALSE); +} + + + + + +// Generic streaming sample playback initialization. +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; +// ServiceSomething = TRUE; + } + } + return (playid); +} + + + +#if (0) +static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} +#endif + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT:LockedData.SampleTracker * to the header which tracks this samples* + * processing.* + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + StartingFileStream = TRUE; + + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = WW_ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + +} + + + + + + + +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + *=============================================================================================*/ + +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static void *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < MAX_SFX; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= MAX_SFX) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + File_Stream_Preload(handle); + } + + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +void __cdecl Sound_Callback(void) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + + /* + ** Call the timer callback now as we may block it in this function + */ + Sound_Timer_Callback(0,0,0,0,0); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Stop_Sample(index); + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + + if ((!st->QueueBuffer || + (st->FileHandle != WW_ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback((short)index, (short int *)&st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != WW_ERROR) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +void *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + void *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* +** Conversion formula for TCrate and Hz rate. +** +** TC = 256 - 1m/rate +** rate = 1m / (256-TC) +*/ + + if (!buffer || fh == WW_ERROR || size <= sizeof(RawHeader)) return(NULL); + + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + actual_bytes_read = Read_File(fh, &RawHeader, sizeof(RawHeader)); + actual_bytes_read +=Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +void Free_Sample(void const *sample) +{ + if (sample) Free((void *)sample); +} + + + + + + + + + + + +/*********************************************************************************************** + * Sound_Timer_Callback -- windows timer callback for sound maintenance * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:01PM ST : Created * + *=============================================================================================*/ + +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ) +{ + //if (!InTimerCallback){ + //InTimerCallback++; + //Colour_Debug (5); + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + //Colour_Debug (0); + //InTimerCallback--; + //} +} + +void Sound_Thread (void *) +{ + DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&SoundThreadHandle , THREAD_ALL_ACCESS , TRUE , 0); + SetThreadPriority (SoundThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); + SoundThreadActive = TRUE; + + while (!AudioDone){ + + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sleep(1000/40); + } + + SoundThreadActive = FALSE; +} + + + + + + + +/*********************************************************************************************** + * Set_Primary_Buffer_Format -- set the format of the primary sound buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if successfully set * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/22/95 4:06PM ST : Created * + *=============================================================================================*/ + +BOOL Set_Primary_Buffer_Format(void) +{ + if (SoundObject && PrimaryBufferPtr){ + return (PrimaryBufferPtr->SetFormat ( &PrimaryBuffFormat ) == DS_OK); + } + return (FALSE); +} + + + +/*********************************************************************************************** + * Print_Sound_Error -- show error messages from failed sound initialisation * + * * + * * + * * + * INPUT: error text * + * handle to window * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/7/96 10:17AM ST : Created * + *=============================================================================================*/ + +void Print_Sound_Error(char *sound_error, HWND window) +{ + MessageBox(window, sound_error, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); +} + + + +/*********************************************************************************************** + * Audio_Init -- Initialise the sound system * + * * + * * + * * + * INPUT: window - window to send callback messages to * + * maximum bits_per_sample - 8 or 16 * + * stereo - will stereo samples be played * + * rate - maximum sample rate required * + * reverse_channels * + * * + * OUTPUT: TRUE if correctly initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * Unknown.... * + * 08-24-95 10:01am ST : Modified for Windows 95 Direct Sound * + *=============================================================================================*/ + +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels) +{ + int index; + int sample=1; + short old_bits_per_sample; + short old_block_align; + long old_bytes_per_sec; + + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + + if ( !SoundObject ){ + + /* + ** Create the direct sound object + */ + if ( DirectSoundCreate (NULL,&SoundObject,NULL) !=DS_OK ) { + Print_Sound_Error("Warning - Unable to create Direct Sound Object",window); + return (FALSE); + } + + /* + ** Give ourselves exclusive access to it + */ + if ( SoundObject->SetCooperativeLevel( window, DSSCL_EXCLUSIVE ) != DS_OK){ + Print_Sound_Error("Warning - Unable to set Direct Sound cooperative level",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set up the primary buffer structure + */ + memset (&BufferDesc , 0 , sizeof(DSBUFFERDESC)); + BufferDesc.dwSize=sizeof(DSBUFFERDESC); + BufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; + + /* + ** Set up the primary buffer format + */ + memset (&DsBuffFormat , 0 , sizeof(WAVEFORMATEX)); + DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM; + DsBuffFormat.nChannels = (unsigned short) (1 + stereo); + DsBuffFormat.nSamplesPerSec = rate; + DsBuffFormat.wBitsPerSample = (short) bits_per_sample; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + DsBuffFormat.cbSize = 0; + + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + + /* + ** Create the primary buffer object + */ + if ( SoundObject->CreateSoundBuffer (&PrimaryBufferDesc , + &PrimaryBufferPtr , + NULL ) !=DS_OK ){ + Print_Sound_Error("Warning - Unable to create Direct Sound primary buffer",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set the format of the primary sound buffer + ** + */ + if (!Set_Primary_Buffer_Format()){ + + /* + ** If we failed to create a 16 bit primary buffer - try for an 8bit one + */ + if (DsBuffFormat.wBitsPerSample == 16){ + /* + ** Save the old values + */ + old_bits_per_sample = DsBuffFormat.wBitsPerSample; + old_block_align = DsBuffFormat.nBlockAlign; + old_bytes_per_sec = DsBuffFormat.nAvgBytesPerSec; + + /* + ** Set up the 8-bit ones + */ + DsBuffFormat.wBitsPerSample = 8; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + } + + if (!Set_Primary_Buffer_Format()){ + + /* + ** We failed to set any useful format so print up an error message and give up + */ + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + + Print_Sound_Error("Warning - Your sound card does not match C&C's audio requirements",window); + + return (FALSE); + }else{ + + /* + ** OK, got an 8bit sound buffer. Not perfect but it will do + ** We still want 16 bit secondary buffers so restore those values + */ + DsBuffFormat.wBitsPerSample = old_bits_per_sample; + DsBuffFormat.nBlockAlign = old_block_align; + DsBuffFormat.nAvgBytesPerSec = old_bytes_per_sec; + } + } + + + /* + ** Start the primary sound buffer playing + ** + */if ( PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING) != DS_OK ){ + Print_Sound_Error("Unable to play Direct Sound primary buffer",window); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + LockedData.DigiHandle=1; + + + /* + ** Initialise the global critical section object for sound thread syncronisation + */ + InitializeCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Initialise the Windows timer system to provide us with a callback + ** + */ + SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC); + AudioDone = FALSE; + //_beginthread(&Sound_Thread, NULL, 16*1024, NULL); + + /* + ** Define the format for the secondary sound buffers + */ + BufferDesc.dwFlags=DSBCAPS_CTRLVOLUME; + BufferDesc.dwBufferBytes=SECONDARY_BUFFER_SIZE; + BufferDesc.lpwfxFormat = (LPWAVEFORMATEX) &DsBuffFormat; + + + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + + /* + ** Allocate once secondary direct sound buffer for each simultaneous sound effect + ** + */ + for (index = 0; index < MAX_SFX; index++) { + + SoundObject->CreateSoundBuffer (&BufferDesc , &LockedData.SampleTracker[index].PlayBuffer , NULL); + + LockedData.SampleTracker[index].PlaybackRate = rate; + LockedData.SampleTracker[index].Stereo = (stereo) ? AUD_FLAG_STEREO : 0; + LockedData.SampleTracker[index].BitSize = (bits_per_sample == 16) ? AUD_FLAG_16BIT : 0; + LockedData.SampleTracker[index].FileHandle = WW_ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + InitializeCriticalSection (&LockedData.SampleTracker[index].AudioCriticalSection); + + } + + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + * 11/02/1995 ST : Modified for Direct Sound * + *=============================================================================================*/ +void Sound_End(void) +{ + + int index; + + if (SoundObject && PrimaryBufferPtr){ + /* + ** Stop all sounds and release the Direct Sound secondary sound buffers + */ + for (index=0 ; index < MAX_SFX; index++){ + if ( LockedData.SampleTracker[index].PlayBuffer ){ + Stop_Sample (index); + LockedData.SampleTracker[index].PlayBuffer->Stop(); + LockedData.SampleTracker[index].PlayBuffer->Release(); + LockedData.SampleTracker[index].PlayBuffer = NULL; + DeleteCriticalSection(&LockedData.SampleTracker[index].AudioCriticalSection); + } + } + } + + /* + ** Stop and release the direct sound primary buffer + */ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + } + + /* + ** Release the Direct Sound Object + */ + if (SoundObject){ + SoundObject->Release(); + SoundObject = NULL; + } + + + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + + /* + ** Remove the Windows timer event we installed for the sound callback + */ + if (SoundTimerHandle){ + timeKillEvent(SoundTimerHandle); + SoundTimerHandle = 0; + } + AudioDone = TRUE; + + /* + ** Since the timer has stopped, we are finished with our global critical section. + */ + DeleteCriticalSection(&GlobalAudioCriticalSection); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + * 11/2/95 4:09PM ST : Modified for Direct Sound * + *=============================================================================================*/ +void Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < MAX_SFX) { + + EnterCriticalSection (&GlobalAudioCriticalSection); + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + LockedData.SampleTracker[handle].Active = FALSE; + + if (!LockedData.SampleTracker[handle].IsScore) { + LockedData.SampleTracker[handle].Original = NULL; + } + + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + LockedData.SampleTracker[handle].PlayBuffer->Stop(); + } + + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = WW_ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + LeaveCriticalSection (&GlobalAudioCriticalSection); + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + DWORD status; + + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= MAX_SFX) return(FALSE); + + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + DumpBuffer = LockedData.SampleTracker[handle].PlayBuffer; + if (LockedData.SampleTracker[handle].PlayBuffer->GetStatus( &status ) == DS_OK){ + return ( (DSBSTATUS_PLAYING & status) || (DSBSTATUS_LOOPING & status) ); + }else{ + return (TRUE); + } +} + + + +/*********************************************************************************************** + * Is_Sample_Playing -- returns the play state of a sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: TRUE if sample is currently playing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:11PM ST : Commented * + *=============================================================================================*/ + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + if (!sample) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return FALSE; + } + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Sample_Playing -- stops a playing sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:13PM ST : Commented * + *=============================================================================================*/ + +void Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} + + + + + +/*********************************************************************************************** + * Get_Free_Sample_Handle -- finds a free slot in which to play a new sample * + * * + * * + * * + * INPUT: priority of sample we want to play * + * * + * OUTPUT: Handle or -1 if none free * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:14PM ST : Added function header * + *=============================================================================================*/ + +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = MAX_SFX - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < MAX_SFX; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + + if (id == MAX_SFX) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + + if (id == -1) { + return -1; + } + + if (LockedData.SampleTracker[id].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = WW_ERROR; + } + + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} + + + + + +/*********************************************************************************************** + * Attempt_Audio_Restore -- tries to restore the direct sound buffers * + * * + * * + * * + * INPUT: ptr to direct sound buffer * + * * + * OUTPUT: TRUE if buffer was successfully restored * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 9:47AM ST : Created * + *=============================================================================================*/ + +BOOL Attempt_Audio_Restore (LPDIRECTSOUNDBUFFER sound_buffer) +{ + + int return_code; + DWORD play_status; + int restore_attempts=0; + + /* + ** Call the audio focus loss function if it has been set up + */ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + + /* + ** Try to restore the sound buffer + */ + do{ + Restore_Sound_Buffers(); + return_code = sound_buffer->GetStatus ( &play_status ); + + } while (restore_attempts++<2 && return_code == DSERR_BUFFERLOST); + + return ((BOOL) ~(return_code == DSERR_BUFFERLOST)); +} + + + + +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + * 11/02/1995 ST : Windows Direct Sound support * + *=============================================================================================*/ +extern BOOL Any_Locked(void); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short , int id) +{ + AUDHeaderType RawHeader; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + LPVOID play_buffer_ptr; //pointer to locked direct sound buffer + LPVOID dummy_buffer_ptr; //dummy pointer to second area of locked direct sound buffer + DWORD lock_length1; + DWORD lock_length2; + DWORD play_status; + HRESULT return_code; + int retries=0; + + + if (Any_Locked()) return(0); + + st = &LockedData.SampleTracker[id]; + //EnterCriticalSection (&GlobalAudioCriticalSection); + + if (!sample || LockedData.DigiHandle == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + if (id == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Fudge the sample rate to 22k + */ + if (RawHeader.Rate <24000 && RawHeader.Rate >20000) RawHeader.Rate = 22050; + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + st->Priority = (short)priority; + st->DontTouch = TRUE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** If the sample rate , bits per sample or stereo capabilities of the buffer do not + ** match the sample then reallocate the direct sound buffer with the required capabilities + */ + if ( ( RawHeader.Rate != st->PlaybackRate ) || + ( ( RawHeader.Flags & AUD_FLAG_16BIT ) != ( st->BitSize & AUD_FLAG_16BIT ) ) || + ( ( RawHeader.Flags & AUD_FLAG_STEREO) != ( st->Stereo & AUD_FLAG_STEREO ) ) ) { + + st->Active=0; + st->Service=0; + st->MoreSource=0; + + /* + ** Stop the sound buffer playing + */ + DumpBuffer = st->PlayBuffer; + do { + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + }while (return_code == DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->PlayBuffer->Stop(); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } + + st->PlayBuffer->Release(); + st->PlayBuffer=NULL; + + DsBuffFormat.nSamplesPerSec = (unsigned short int) RawHeader.Rate; + DsBuffFormat.nChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1 ; + DsBuffFormat.wBitsPerSample = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8 ; + DsBuffFormat.nBlockAlign = (short) ((DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Create the new sound buffer + */ + return_code= SoundObject->CreateSoundBuffer (&BufferDesc , &st->PlayBuffer , NULL); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + /* + ** Just return if the create failed unexpectedly + ** + ** If we failed then flag the buffer as having an impossible format so it wont match + ** any sample. This will ensure that we try and create the buffer again next time its used. + */ + if (return_code!=DS_OK && return_code!=DSERR_BUFFERLOST){ + st->PlaybackRate = 0; + st->Stereo = 0; + st->BitSize = 0; + return(-1); + } + + /* + ** Remember the format of the new buffer + */ + st->PlaybackRate = RawHeader.Rate; + st->Stereo = RawHeader.Flags & AUD_FLAG_STEREO; + st->BitSize = RawHeader.Flags & AUD_FLAG_16BIT; + } + + /* + ** Fill in 3/4 of the play buffer. + */ + + // + // Stop the sound buffer playing before we lock it + // + do { + DumpBuffer = st->PlayBuffer; + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->Active=0; + st->Service=0; + st->MoreSource=0; + st->PlayBuffer->Stop(); + } + // + // Lock the direct sound buffer so we can write to it + // + do { + return_code = st->PlayBuffer->Lock ( 0 , + SECONDARY_BUFFER_SIZE, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (return_code != DS_OK) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (-1); + } + + // + // Decompress the sample into the direct sound buffer + // + st->DestPtr=(void*)Sample_Copy ( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE*1/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + if ( st->DestPtr==(void*) (SECONDARY_BUFFER_SIZE*1/4) ){ + + // Must be more data to copy so we dont need to zero the buffer + st->MoreSource=TRUE; + st->Service=TRUE; + st->OneShot=FALSE; + } else { + + // Whole sample is in the buffer so flag that we dont need to + // copy more. Clear out the end of the buffer so that it + // goes quiet if we play past the end + st->MoreSource=FALSE; + st->OneShot=TRUE; + st->Service=TRUE; //We still need to service it so that we can stop it when + // it plays past the end of the sample data + //memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE - (unsigned)st->DestPtr ); + memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE/4); + } + + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + + + /* + ** + ** Set the volume of the sample. + ** + */ + st->Volume = (volume << 7); +#ifdef NO_VOLUME_CONTROL + do { + return_code = st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.SoundVolume) ) + *1000) >>15 ) ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + +#endif //NO_VOLUME_CONTROL + + /* + ** Make sure the primary sound buffer is playing + */ + if (!Start_Primary_Sound_Buffer(FALSE)){ + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + + /* + ** Set the buffers play pointer to the beginning of the buffer + */ + do { + return_code = st->PlayBuffer->SetCurrentPosition (0); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + + /* + ** Start the sample playing now. + */ + do + { + return_code = st->PlayBuffer->Play (0,0,DSBPLAY_LOOPING); + + switch (return_code){ + + case DS_OK : + EnterCriticalSection (&GlobalAudioCriticalSection); + st->Active=TRUE; + st->Handle=(short)id; + st->DontTouch = FALSE; + LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); + + case DSERR_BUFFERLOST : + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + break; + + default: + st->Active=FALSE; + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); + } + + } while (return_code==DSERR_BUFFERLOST); + + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); +} + + + + + +/*********************************************************************************************** + * Restore_Sound_Buffers -- restore the sound buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:53PM ST : Created * + *=============================================================================================*/ + +void Restore_Sound_Buffers ( void ) +{ + + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Restore(); + } + + + for ( int index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].PlayBuffer){ + LockedData.SampleTracker[index].PlayBuffer->Restore(); + } + } +} + + + + + + + + +/*********************************************************************************************** + * Set_Sound_Vol -- sets the overall volume for sampled sounds * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +/*********************************************************************************************** + * Set_Score_Vol -- sets the overall volume for music scores * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ +int Set_Score_Vol(int volume) +{ + int old; + SampleTrackerType *st; //ptr to SampleTracker structure + + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + + for (int index=0 ; indexIsScore && st->Active){ +#ifdef NO_VOLUME_CONTROL + st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.ScoreVolume) ) + *1000) >>15 ) ); +#endif //ifdef NO_VOLUME_CONTROL + } + } + return(old); +} + + + +/*********************************************************************************************** + * Fade_Sample -- Start a sample fading * + * * + * * + * * + * INPUT: Sample handle * + * fade rate * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:21PM ST : Added function header * + *=============================================================================================*/ + +void Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + st = &LockedData.SampleTracker[handle]; + st->Reducer = (short) ((st->Volume / ticks)+1); + } + } +} + + + +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + + + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} + + + +/*********************************************************************************************** + * Start_Primary_Sound_Buffer -- start the primary sound buffer playing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ +extern BOOL GameInFocus; +BOOL Start_Primary_Sound_Buffer (BOOL forced) +{ + DWORD status; + + if (PrimaryBufferPtr && GameInFocus){ + if (forced){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + } else { + + if (PrimaryBufferPtr->GetStatus (&status) == DS_OK){ + if (! ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_LOOPING))){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + }else{ + return (TRUE); + } + } + } + } + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Primary_Sound_Buffer -- stops the primary sound buffer from playing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This stops all sound playback * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ + +void Stop_Primary_Sound_Buffer (void) +{ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Stop(); // Oh I + PrimaryBufferPtr->Stop(); // Hate Direct Sound + PrimaryBufferPtr->Stop(); // So much..... + } + + for ( int index = 0; index < MAX_SFX; index++) { + Stop_Sample(index); + } + +} + + +void Suspend_Audio_Thread(void) +{ + if (SoundThreadActive){ + SuspendThread(SoundThreadHandle); + SoundThreadActive = FALSE; + } +} + + + + +void Resume_Audio_Thread(void) +{ + if (!SoundThreadActive){ + ResumeThread(SoundThreadHandle); + SoundThreadActive = TRUE; + } +} + + diff --git a/WIN32LIB/AUDIO/OLD/SOUNDLCK.CPP b/WIN32LIB/AUDIO/OLD/SOUNDLCK.CPP new file mode 100644 index 0000000..27dae7d --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/SOUNDLCK.CPP @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDLCK.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#include +#include +#include +#include "dsound.h" +#include +#include "wwmem.h" +#include "wwstd.h" +#include "soundint.h" + + + +LockedDataType LockedData; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * INIT_LOCKED_DATA -- Initializes sound driver locked data * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void Init_Locked_Data(void) +{ + + /* + ** Initialize all of the data elements that need to be locked. + */ + LockedData.DigiHandle = -1; + LockedData.ServiceSomething = FALSE; + LockedData.MagicNumber = 0xDEAF; + LockedData.UncompBuffer = NULL; +// LockedData.StreamBufferSize = (2*SECONDARY_BUFFER_SIZE)+128; + LockedData.StreamBufferSize = (SECONDARY_BUFFER_SIZE/4)+128; + LockedData.StreamBufferCount = 16; + LockedData.SoundVolume = 255; + LockedData.ScoreVolume = 255; + LockedData._int = FALSE; + + #ifdef cuts + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Lock(&LockedData, 4096L); + DPMI_Lock(Simple_Copy, 4096L); + DPMI_Lock(Sample_Copy, 4096L); + DPMI_Lock((void *)maintenance_callback, 4096L); + DPMI_Lock((void *)DigiCallback, 4096L); + DPMI_Lock((void *)HMI_TimerCallback, 4096L); + DPMI_Lock(Audio_Add_Long_To_Pointer, 4096L); + DPMI_Lock(DPMI_Unlock, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Lock(Mem_Copy, 4096L); + DPMI_Lock(Audio_Mem_Set, 4096L); + DPMI_Lock(__GETDS, 4096L); + + /* + ** Finally lock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Lock(); + sosCODEC_Lock(); + + #endif //cuts +} + diff --git a/WIN32LIB/AUDIO/OLD/TEST.CPP b/WIN32LIB/AUDIO/OLD/TEST.CPP new file mode 100644 index 0000000..e4c28fd --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/TEST.CPP @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "audio.h" + + + +int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) +{ + + + + + + + File_Stream_Sample_Vol("file", 0, 0); + + return (0); +} \ No newline at end of file diff --git a/WIN32LIB/AUDIO/OLD/TST.CPP b/WIN32LIB/AUDIO/OLD/TST.CPP new file mode 100644 index 0000000..95128a5 --- /dev/null +++ b/WIN32LIB/AUDIO/OLD/TST.CPP @@ -0,0 +1,32 @@ +/* +** 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 . +*/ + + +#undef _WINDOWS_16_ +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#define __WIN32 + +#include +#include +#include + +main +{} diff --git a/WIN32LIB/AUDIO/OLSOSDEC.ASM b/WIN32LIB/AUDIO/OLSOSDEC.ASM new file mode 100644 index 0000000..71233f6 --- /dev/null +++ b/WIN32LIB/AUDIO/OLSOSDEC.ASM @@ -0,0 +1,755 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 diff --git a/WIN32LIB/AUDIO/SOS.H b/WIN32LIB/AUDIO/SOS.H new file mode 100644 index 0000000..9a77c15 --- /dev/null +++ b/WIN32LIB/AUDIO/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far __cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far __cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif \ No newline at end of file diff --git a/WIN32LIB/AUDIO/SOSCODEC.ASM b/WIN32LIB/AUDIO/SOSCODEC.ASM new file mode 100644 index 0000000..6e6011b --- /dev/null +++ b/WIN32LIB/AUDIO/SOSCODEC.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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) +;* Jonathan Lanier +;* +;* DATE +;* Febuary 15, 1995 +;* +;* LAST MODIFIED +;* 08/07/95 [jdl] - Rewrote/optimized sosCODECDecompressData +;* +;*--------------------------------------------------------------------------- +;* +;* 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 + +INCLUDE "difftb.inc" +INCLUDE "indextb.inc" +INCLUDE "nybbtb.inc" + +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 sosCODECInitStream:NEAR + PROC sosCODECInitStream C NEAR + + ARG sSOSInfo:NEAR PTR + + mov eax,[sSOSInfo] + mov [(sCompInfo eax).wIndex],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted],0 ; no predicted value + mov [(sCompInfo eax).wIndex2],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted2],0 ; no predicted value + 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 decompress. +;* +;* RESULT +;* Size - Size of decompressed data. +;* +;* +;* NOTES +;* This routine has been optimized for pipelining on both +;* 486 and Pentium processors. Changing, inserting, or moving any +;* instructions will most likely slow down the code, in some cases by +;* as much as 20%. It can burst-decompress 16384 samples in about +;* 1940æs on a Pentium 90Mhz, and about 3960æs on a 486 66Mhz. +;* Instruction reordering could bring this down to below 1870æs on +;* the Pentium, but this would cause a great degradation in 486 +;* performance. Since slow 486's are the reason this code was +;* written to be fast, it has been optimized for the Pentium only where +;* it would not degrade 486 performance. So, be careful when changing +;* ANY of this code, because it is very carefully balanced... +;**************************************************************************** + + GLOBAL C sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + push esi + push edi + push ebx + push ecx + push edx ;save all the regs + + mov ebx,[sSOSInfo] ;get base of sCompInfo struct + mov cx,[(sCompInfo ebx).wBitSize] ;check the bit size + mov dx,[(sCompInfo ebx).wChannels] ;check the number of channels +; +; +; Determine the correct routine to use for decoding +; (for now only ADPCM 4:1 Mono 16-bit is implemented) + cmp cx,8 + jne ??do16Bits + +??do8Bits: + cmp dx,2 + jne ??not8Stereo +; jmp ??decomp8Stereo + jmp ??byeBye + +??not8Stereo: + cmp dx,1 + jne ??byeBye +; jmp decomp8Mono + jmp ??byeBye + +??do16Bits: + cmp cx,16 + jne ??byeBye + + cmp dx,2 + jne ??not16Stereo +; jmp ??decomp16Stereo + jmp ??byeBye + +??not16Stereo: + cmp dx,1 + jne ??byeBye + + push ebp +; +; +; 16 bit ADPCM 4:1 Mono pre-loop initialization +??decomp16Mono: + push ebx ;save struct base + xor edx,edx ;clear index + mov eax,[(sCompInfo ebx).dwPredicted] ;get last sample + mov dx,[(sCompInfo ebx).wIndex] ;get last index value + mov esi,[(sCompInfo ebx).lpSource] ;get source address + mov edi,[(sCompInfo ebx).lpDest] ;get dest address + + mov ebp,[wBytes] ;get the number of dest. bytes + cmp ebp,16 ;less than 16? (less than 8 samples) + jl ??fixAlign16Mono0 ;if so, don't bother with alignment +; +; +; Check to see if we need to fix an alignment problem on the source buffer +; (non-aligned buffers are MUCH slower; if we're given a non-DWORD aligned +; source address, we do as many samples as needed to get to the nearest +; DWORD boundary, then finish the bulk as a DWORD-aligned decompress). + mov ebx,esi ;get source address + and ebx,03h ;check LSB + jnz ??fixalign16Mono ;if non-zero, need to align for + ;warp speed +??fixAlign16Mono0: + push ebp ;save for later + shr ebp,4 ;divide by 16 for 16-bit, + ;because we do 8 nybbles per loop, + ;and there are two samples per + ;byte, so there are n/16 iterations + ;required + xor ebx,ebx ;clear our nybble index + or ebp,ebp ;set flags for EBP + jmp ??start16Mono ;start with test... don't go if + ;we have zero bytes to do +??fixalign16Mono: + jmp [DWORD PTR dwMono16AlignJmpTable+ebx*4] ;do non-aligned first + + align 4 +??fixAlign16Mono1: + sub ebp,12 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono6 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono2: + sub ebp,8 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono4 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono3: + sub ebp,4 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono2 ;borrow exit code to go through a + ;piece of a loop +; "The Loop" +; +; Process 1st nybble + align 4 +??loop16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix1Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix1Smp16MonoU +??fixed1Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi],ax ;save the sample +; +; +; Process 2nd nybble +??finish7Smp16Mono: + mov bl,ch ;get next 2 nybbles in ebx + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix2Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix2Smp16MonoU +??fixed2Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+02h],ax ;save the sample + shr ecx,16 ;move top four nybbles into bottom +; +; +; Process 3rd nybble +??finish6Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix3Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix3Smp16MonoU +??fixed3Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+04h],ax ;save the sample +; +; +; Process 4th nybble +??finish5Smp16Mono: + mov bl,cl + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix4Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix4Smp16MonoU +??fixed4Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+06h],ax ;save the sample +; +; +; Process 5th nybble +??finish4Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix5Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix5Smp16MonoU +??fixed5Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+08h],ax ;save the sample +; +; +; Process 6th nybble +??finish3Smp16Mono: + mov bl,ch + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix6Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix6Smp16MonoU +??fixed6Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+0Ah],ax ;save the sample +; +; +; Process 7th nybble +??finish2Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix7Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix7Smp16MonoU +??fixed7Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+0Ch],ax ;save the sample +; +; +; Process 8th nybble +??finish1Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix8Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix8Smp16MonoU +; +; +; Loop cleanup for next pass +??fixed8Smp16Mono: + mov [edi+0Eh],ax ;save the sample + add esi,04h ;bump esi to point to next longword + add edi,10h ;incr. the destination buffer ptr + dec ebp ;count down the number of samples/8 +??start16Mono: + jng ??cleanup16Mono ;if done, clean up + mov ecx,[esi] ;get 4 nybbles in one whack (whee!) + mov bl,cl ;get next 2 nybbles in ebx + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??loop16Mono ;loop until done + +??cleanup16Mono: + jnz ??done16Mono ;if ebp is non-zero, we're DONE + ;if exactly zero, finish the tail-end + ;of the conversion (may be a non- + ;multiple of 8 nybbles) +; +; +; Loop cleanup for last (incomplete) pass + pop ecx ;restore # of words + shr ecx,1 ;divide by two to get samples + and ecx,07h ;get # of samples we missed + jmp [DWORD PTR dwMono16JmpTable+ecx*4] ;go finish the job... +; +; +; Structure cleanup +??done16Mono: + pop ebx ;restore struct base + pop ebp ;restore stack frame pointer + mov [(sCompInfo ebx).dwPredicted],eax ;save last sample + mov [(sCompInfo ebx).wIndex],dx ;save last index value + mov eax,[wBytes] ;get # of bytes we did +??byeBye: + pop edx ;restore all the regs + pop ecx + pop ebx + pop edi + pop esi + ret +; +; +; Jumps for -32768/+32767 bounds check go to these vvvv + align 4 +??fix1Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix1Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix2Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix2Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix3Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix3Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix4Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix4Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix5Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix5Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix6Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix6Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix7Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix7Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix8Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed8Smp16Mono ;go back + + align 4 +??fix8Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed8Smp16Mono ;go back +; +; +; Jump tables for cleanup after loop unroll point to these vvvv + align 4 +??finish16Mono1: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 1 nybble (1 byte) + shl ch,4 ;move it over + mov bl,ch ;get nybble in ebx + sub edi,0Eh ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish1Smp16Mono ;go finish it + + align 4 +??finish16Mono2: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 2 nybbles (1 byte) + mov bl,ch ;get nybbles in ebx + sub edi,0Ch ;back edi up + sub esi,3 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish2Smp16Mono ;go finish it + + align 4 +??finish16Mono3: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 3 nybbles (2 bytes) + shl cx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,0Ah ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish3Smp16Mono ;go finish it + + align 4 +??finish16Mono4: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + sub edi,08h ;back edi up + sub esi,2 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish4Smp16Mono ;go finish it + + align 4 +??finish16Mono5: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 1 nybble (1 byte) + shl ecx,16 ;shift it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,4 ;move it over + shl bl,4 ;move it over + sub edi,06h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish5Smp16Mono ;go finish it + + align 4 +??finish16Mono6: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 2 nybbles (1 byte) + shl ecx,16 ;move it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,8 ;move it over + sub esi,1 ;adjust esi (used for dword aligning) + sub edi,04h ;back edi up + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish6Smp16Mono ;go finish it + + align 4 +??finish16Mono7: + xor ecx,ecx ;clear nybble bucket + mov ecx,[esi] ;get 7 nybbles (4 bytes) + shl ecx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,02h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish7Smp16Mono ;go finish it +; +; +; Jump Tables + align 4 + +dwMono16JmpTable DD ??done16Mono + DD ??finish16Mono1 + DD ??finish16Mono2 + DD ??finish16Mono3 + DD ??finish16Mono4 + DD ??finish16Mono5 + DD ??finish16Mono6 + DD ??finish16Mono7 + + align 4 +dwMono16AlignJmpTable DD ??fixAlign16Mono0 + DD ??fixAlign16Mono1 + DD ??fixAlign16Mono2 + DD ??fixAlign16Mono3 + + ENDP 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 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 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 + diff --git a/WIN32LIB/AUDIO/SOSCOMP.H b/WIN32LIB/AUDIO/SOSCOMP.H new file mode 100644 index 0000000..de369d7 --- /dev/null +++ b/WIN32LIB/AUDIO/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void __cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long __cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl General_sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif diff --git a/WIN32LIB/AUDIO/SOSDATA.H b/WIN32LIB/AUDIO/SOSDATA.H new file mode 100644 index 0000000..f6686a5 --- /dev/null +++ b/WIN32LIB/AUDIO/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WIN32LIB/AUDIO/SOSDEFS.H b/WIN32LIB/AUDIO/SOSDEFS.H new file mode 100644 index 0000000..9bb29e7 --- /dev/null +++ b/WIN32LIB/AUDIO/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WIN32LIB/AUDIO/SOSFNCT.H b/WIN32LIB/AUDIO/SOSFNCT.H new file mode 100644 index 0000000..2945aa4 --- /dev/null +++ b/WIN32LIB/AUDIO/SOSFNCT.H @@ -0,0 +1,219 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int __cdecl sosRealFree ( int ); +extern BOOL __cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int __cdecl sosRealAlloc( int, int *, int * ); +extern void __cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int __cdecl sosGetCS( VOID ); +extern int __cdecl sosGetES( VOID ); +#else +extern int __cdecl sosRealAlloc ( int, int *, int * ); +extern int __cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD __cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD __cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void __cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void __cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void __cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void __cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void __cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void __cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void __cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void __cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD __cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD __cdecl sosDIGIInitForWindows( WORD ); +extern WORD __cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR __cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR __cdecl sosCreateAliasCS ( LPSTR ); +extern VOID __cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR __cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID __cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR __cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID __cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID __cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD __cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD __cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD __cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD __cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD __cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD __cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD __cdecl getDS( VOID ); +extern WORD __cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD __cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void __cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void __cdecl sosTIMERDRVUnInit( void ); +extern void __cdecl sosTIMERDRVHandler( void ); +extern void __cdecl sosTIMERDRVFHandler( void ); +extern void __cdecl sosTIMERDRVEnable( void ); +extern void __cdecl sosTIMERDRVDisable( void ); +extern void __cdecl sosTIMERDRVCallOld( void ); +extern void __cdecl sosTIMERDRVSetRate( WORD ); +extern void __cdecl sosDIGITimer_Start( void ); +extern void __cdecl sosDIGITimer_End( void ); +extern void __cdecl sosDIGIDrv_Start( void ); +extern void __cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif + diff --git a/WIN32LIB/AUDIO/SOSRES.H b/WIN32LIB/AUDIO/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WIN32LIB/AUDIO/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WIN32LIB/AUDIO/SOUND.H b/WIN32LIB/AUDIO/SOUND.H new file mode 100644 index 0000000..fdeb78b --- /dev/null +++ b/WIN32LIB/AUDIO/SOUND.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/WIN32LIB/AUDIO/SOUNDINT.CPP b/WIN32LIB/AUDIO/SOUNDINT.CPP new file mode 100644 index 0000000..4089487 --- /dev/null +++ b/WIN32LIB/AUDIO/SOUNDINT.CPP @@ -0,0 +1,544 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include "dsound.h" +#include +#include "soundint.h" +#include "memflag.h" +#include "audio.h" + +extern DebugBuffer[]; + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + + + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * , short int *) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + if (st->sosinfo.wBitSize==16 && st->sosinfo.wChannels==1){ + sosCODECDecompressData(&st->sosinfo, dsize); + } else { + General_sosCODECDecompressData(&st->sosinfo, dsize); + } + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + + + + + + +extern int Convert_HMI_To_Direct_Sound_Volume(int volume); + +/*********************************************************************************************** + * maintenance_callback -- routine to service the direct play secondary buffers * + * and other stuff..? * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * ....Unknown * + * 10/17/95 10:15PM ST : tidied up a tad for direct sound * + *=============================================================================================*/ +VOID far __cdecl maintenance_callback(VOID) +{ + + int index; //index used in for loop + SampleTrackerType *st; //ptr to SampleTracker structure + DWORD play_cursor; //Position that direct sound is reading from + DWORD write_cursor; //Position in buffer that we can write to + int bytes_copied; //Number of bytes copied into the buffer + BOOL write_more; //Flag to set if we need to write more into the buffer + LPVOID play_buffer_ptr; //Beginning of locked area of buffer + LPVOID dummy_buffer_ptr; //Length of locked area in buffer + DWORD lock_length1; //Beginning of second locked area in buffer + DWORD lock_length2; //Length of second locked area in buffer + HRESULT return_code; + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the direct sound buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service && !st->DontTouch ) { + + //EnterCriticalSection (&st->AudioCriticalSection); + + st->DontTouch = TRUE; + + /* + ** Get the current position of the direct sound play cursor within the buffer + */ + return_code = st->PlayBuffer->GetCurrentPosition ( &play_cursor , &write_cursor ); + + /* + ** Check for unusual situations like a focus loss + */ + if (return_code != DS_OK){ + if (return_code == DSERR_BUFFERLOST){ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + //LeaveCriticalSection (&st->AudioCriticalSection); + return; //Our app has lost focus or something else nasty has happened + } //so dont update the sound buffers + + + if (st->MoreSource){ + + /* + ** If the direct sound read pointer is less than a quarter + ** of a buffer away from the end of the data then copy some + ** more. + */ + write_more = FALSE; + + if ( play_cursor < (unsigned)st->DestPtr ){ + if ( (unsigned)st->DestPtr - (unsigned)play_cursor <= SECONDARY_BUFFER_SIZE/4 ){ + write_more=TRUE; + } + } else { + /* The only time that play_cursor can be greater than DestPtr is + ** if we wrote right to the end of the buffer last time and DestPtr + ** looped back to the beginning of the buffer. + ** That being the case, all we have to do is see if play_cursor is + ** within the last 25% of the buffer + */ + if ( ( (int)play_cursor > SECONDARY_BUFFER_SIZE*3/4) &&st->DestPtr==0 ){ + write_more=TRUE; + } + } + + if (write_more){ + + /* + ** Lock a 1/2 of the direct sound buffer so we can write to it + */ + if ( DS_OK== st->PlayBuffer->Lock ( (DWORD)st->DestPtr , + (DWORD)SECONDARY_BUFFER_SIZE/2, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 )){ + + bytes_copied = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + + if ( bytes_copied != (SECONDARY_BUFFER_SIZE/4) ){ + /* + ** We must have reached the end of the sample + */ + st->MoreSource=FALSE; + memset (((char*)play_buffer_ptr)+bytes_copied , + 0 , + (SECONDARY_BUFFER_SIZE/4)-bytes_copied); + + /* + ** Clear out an extra area in the buffer ahead of the play cursor + ** to give us a quiet period of grace in which to stop the buffer playing + */ + if ( (unsigned)st->DestPtr == SECONDARY_BUFFER_SIZE*3/4 ){ + if ( dummy_buffer_ptr && lock_length2 ){ + memset (dummy_buffer_ptr , 0 , lock_length2); + } + } else { + memset ((char*)play_buffer_ptr+SECONDARY_BUFFER_SIZE/4 , 0 , SECONDARY_BUFFER_SIZE/4); + } + } + + /* + ** Update our pointer into the direct sound buffer + ** + */ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,bytes_copied); + + if ( (unsigned)st->DestPtr >= (unsigned)SECONDARY_BUFFER_SIZE ){ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,(long)-SECONDARY_BUFFER_SIZE); + } + + + /* + ** Unlock the direct sound buffer + */ + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + } + + } //write_more + + } else { //!more_source + + /* + ** no more source to write - check if the buffer play + ** has overrun the end of the sample and stop it if it has + */ + if ( ( (play_cursor >= (unsigned)st->DestPtr) && ( ((unsigned)play_cursor - (unsigned)st->DestPtr) OneShot &&( (play_cursor < (unsigned)st->DestPtr) && ( ((unsigned)st->DestPtr - (unsigned)play_cursor) >(SECONDARY_BUFFER_SIZE*3/4) ) )) ){ + st->PlayBuffer->Stop(); + st->Service = FALSE; + Stop_Sample( index ); + } + } //more_source + + st->DontTouch = FALSE; + + //LeaveCriticalSection (&st->AudioCriticalSection); + } + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + //EnterCriticalSection (&st->AudioCriticalSection); + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + + //st->PlayBuffer->SetVolume (-( ( (32768-st->Volume)*1000) >>15 ) ); + + if (st->IsScore){ + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.ScoreVolume*(st->Volume >>7))/256) ); + }else{ + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.SoundVolume*(st->Volume >>7))/256) ); + } + + //LeaveCriticalSection (&st->AudioCriticalSection); + } + st++; + } + LockedData._int--; + } + + //LeaveCriticalSection(&GlobalAudioCriticalSection); +} + + + + + + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WIN32LIB/AUDIO/SOUNDINT.H b/WIN32LIB/AUDIO/SOUNDINT.H new file mode 100644 index 0000000..1a69aab --- /dev/null +++ b/WIN32LIB/AUDIO/SOUNDINT.H @@ -0,0 +1,292 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/WIN32LIB/AUDIO/SOUNDIO.CPP b/WIN32LIB/AUDIO/SOUNDIO.CPP new file mode 100644 index 0000000..b302acf --- /dev/null +++ b/WIN32LIB/AUDIO/SOUNDIO.CPP @@ -0,0 +1,2207 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Load_Long_Sample -- Loads a sample into XMS for double buffer system. * + * Read_Long_Sample -- Loads/Processes/Formats/Builds offset. * + * Save_Table_Entry -- Put an entry in the offset table. * + * Play_Long_Sample -- Calls Init_Long_Sample and Start_Long_Sample. * + * Start_Long_Sample -- Starts a sample playing that has be initialized. * + * Get_Table_Entry -- Gets next entry in table. * + * Long_Sample_Ticks -- Gets number of ticks in sample if in header. * + * Long_Sample_Status -- Returns the status of the sample. * + * Find_Table_Entry -- Finds next entry in table that matches mask. * + * Get_Table_Start -- Returns a pointer to first entry in table. * + * Long_Sample_Ticks_Played -- Number of ticks since sample started. * + * Install_Sample_Driver_Callback -- Pokes callback function into JM driver * + * Stop_Long_Sample -- Stops current long sample from playing. * + * Long_Sample_Loaded_Size -- Max buffer size to load a long sample. * + * Sound_Callback -- Audio driver callback function. * + * DigiCallback -- Low level double buffering handler. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Stream_Sample -- Streams a sample directly from a file. * + * Sample_Read -- Reads sample data from an openned file. * + * Continue_Sample -- Tags another block of data onto the currently playing. * + * Sample_Copy -- Copies sound data from source format to raw format. * + * File_Stream_Preload -- Handles initial proload of a streaming samples bu* + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern void Colour_Debug (int call_number); +#pragma pack(4) + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#include +#include +#include "dsound.h" + +#include +#include +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + +LPDIRECTSOUNDBUFFER DumpBuffer; +HANDLE SoundThreadHandle = NULL; +BOOL SoundThreadActive = FALSE; + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This is the number of times per sec that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 40 //30 times per sec plus a safety margin + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +//#define STREAM_BUFFER_SIZE (128L*1024L) +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +//void *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +int StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; +LPDIRECTSOUND SoundObject; //Direct sound object +LPDIRECTSOUNDBUFFER PrimaryBufferPtr; //Pointer to the buffer that the +unsigned SoundTimerHandle=0; //Windows Handle for sound timer +WAVEFORMATEX DsBuffFormat; //format of direct sound buffer +DSBUFFERDESC BufferDesc; //Buffer description for creating buffers +WAVEFORMATEX PrimaryBuffFormat; //Copy of format of direct sound primary buffer +DSBUFFERDESC PrimaryBufferDesc; //Copy of buffer description for re-creating primary buffer +CRITICAL_SECTION GlobalAudioCriticalSection; +void *FileStreamBuffer = NULL; //Buffer for streaming audio from CD +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void) = NULL; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(short id, short *odd, void **buffer, long *size); +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle); +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ); +//static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size)); +void Sound_Thread (void *); +volatile BOOL AudioDone; +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +// This callback is called whenever the queue buffer playback has begun +// and another buffer is needed for queuing up. Returns TRUE if there +// is more data to read from the file. +static BOOL File_Callback(short id, short *odd, void **buffer, long *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + void *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + } + + st->DontTouch = TRUE; + + if (!AudioDone){ + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = (short)(*odd + 1); + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + } + st->DontTouch = FALSE; + if (!AudioDone){ + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != WW_ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + while (num_empty_buffers && (st->FileHandle != WW_ERROR)) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + } + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + if (!AudioDone){ + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + } + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + if (!AudioDone){ + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return(TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + + } + return(FALSE); +} + + + + + +// Generic streaming sample playback initialization. +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (!AudioDone && buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; +// ServiceSomething = TRUE; + } + } + return (playid); +} + + + +#if (0) +static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} +#endif + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT:LockedData.SampleTracker * to the header which tracks this samples* + * processing.* + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + StartingFileStream = TRUE; + + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = WW_ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + +} + + + + + + + +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + *=============================================================================================*/ + +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + SampleTrackerType *st; + int fh; + int handle = -1; + int index; + + if (!AudioDone && LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!FileStreamBuffer) { + FileStreamBuffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < MAX_SFX; index++) { + LockedData.SampleTracker[index].FileBuffer = FileStreamBuffer; + } + } + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!FileStreamBuffer) return(-1); + + + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= MAX_SFX) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + File_Stream_Preload(handle); + } + + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +void __cdecl Sound_Callback(void) +{ + int index; + SampleTrackerType *st; + + if (!AudioDone && LockedData.DigiHandle != -1) { + + /* + ** Call the timer callback now as we may block it in this function + */ + Sound_Timer_Callback(0,0,0,0,0); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Stop_Sample(index); + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + + if ((!st->QueueBuffer || + (st->FileHandle != WW_ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback((short)index, (short int *)&st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != WW_ERROR) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +void *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + void *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* +** Conversion formula for TCrate and Hz rate. +** +** TC = 256 - 1m/rate +** rate = 1m / (256-TC) +*/ + + if (!buffer || fh == WW_ERROR || size <= sizeof(RawHeader)) return(NULL); + + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + actual_bytes_read = Read_File(fh, &RawHeader, sizeof(RawHeader)); + actual_bytes_read +=Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +void Free_Sample(void const *sample) +{ + if (sample) Free((void *)sample); +} + + + + + + + + + + + +/*********************************************************************************************** + * Sound_Timer_Callback -- windows timer callback for sound maintenance * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:01PM ST : Created * + *=============================================================================================*/ + +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ) +{ + //if (!InTimerCallback){ + //InTimerCallback++; + //Colour_Debug (5); + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + //Colour_Debug (0); + //InTimerCallback--; + //} +} + +void Sound_Thread (void *) +{ + DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&SoundThreadHandle , THREAD_ALL_ACCESS , TRUE , 0); + SetThreadPriority (SoundThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); + SoundThreadActive = TRUE; + + while (!AudioDone){ + + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sleep(1000/40); + } + + SoundThreadActive = FALSE; +} + + + + + + + +/*********************************************************************************************** + * Set_Primary_Buffer_Format -- set the format of the primary sound buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if successfully set * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/22/95 4:06PM ST : Created * + *=============================================================================================*/ + +BOOL Set_Primary_Buffer_Format(void) +{ + if (SoundObject && PrimaryBufferPtr){ + return (PrimaryBufferPtr->SetFormat ( &PrimaryBuffFormat ) == DS_OK); + } + return (FALSE); +} + + + +/*********************************************************************************************** + * Print_Sound_Error -- show error messages from failed sound initialisation * + * * + * * + * * + * INPUT: error text * + * handle to window * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/7/96 10:17AM ST : Created * + *=============================================================================================*/ + +void Print_Sound_Error(char *sound_error, HWND window) +{ + MessageBox(window, sound_error, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); +} + + + +/*********************************************************************************************** + * Audio_Init -- Initialise the sound system * + * * + * * + * * + * INPUT: window - window to send callback messages to * + * maximum bits_per_sample - 8 or 16 * + * stereo - will stereo samples be played * + * rate - maximum sample rate required * + * reverse_channels * + * * + * OUTPUT: TRUE if correctly initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * Unknown.... * + * 08-24-95 10:01am ST : Modified for Windows 95 Direct Sound * + *=============================================================================================*/ + +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels) +{ + int index; + int sample=1; + short old_bits_per_sample; + short old_block_align; + long old_bytes_per_sec; + + + Init_Locked_Data(); + FileStreamBuffer = NULL; + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + + if ( !SoundObject ){ + + /* + ** Create the direct sound object + */ + if ( DirectSoundCreate (NULL,&SoundObject,NULL) !=DS_OK ) { + Print_Sound_Error("Warning - Unable to create Direct Sound Object",window); + return (FALSE); + } + + /* + ** Give ourselves exclusive access to it + */ + if ( SoundObject->SetCooperativeLevel( window, DSSCL_PRIORITY ) != DS_OK){ + Print_Sound_Error("Warning - Unable to set Direct Sound cooperative level",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set up the primary buffer structure + */ + memset (&BufferDesc , 0 , sizeof(DSBUFFERDESC)); + BufferDesc.dwSize=sizeof(DSBUFFERDESC); + BufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; + + /* + ** Set up the primary buffer format + */ + memset (&DsBuffFormat , 0 , sizeof(WAVEFORMATEX)); + DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM; + DsBuffFormat.nChannels = (unsigned short) (1 + stereo); + DsBuffFormat.nSamplesPerSec = rate; + DsBuffFormat.wBitsPerSample = (short) bits_per_sample; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + DsBuffFormat.cbSize = 0; + + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + + /* + ** Create the primary buffer object + */ + if ( SoundObject->CreateSoundBuffer (&PrimaryBufferDesc , + &PrimaryBufferPtr , + NULL ) !=DS_OK ){ + Print_Sound_Error("Warning - Unable to create Direct Sound primary buffer",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set the format of the primary sound buffer + ** + */ + if (!Set_Primary_Buffer_Format()){ + + /* + ** If we failed to create a 16 bit primary buffer - try for an 8bit one + */ + if (DsBuffFormat.wBitsPerSample == 16){ + /* + ** Save the old values + */ + old_bits_per_sample = DsBuffFormat.wBitsPerSample; + old_block_align = DsBuffFormat.nBlockAlign; + old_bytes_per_sec = DsBuffFormat.nAvgBytesPerSec; + + /* + ** Set up the 8-bit ones + */ + DsBuffFormat.wBitsPerSample = 8; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + } + + if (!Set_Primary_Buffer_Format()){ + + /* + ** We failed to set any useful format so print up an error message and give up + */ + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + + Print_Sound_Error("Warning - Your sound card does not meet the products audio requirements",window); + + return (FALSE); + }else{ + + /* + ** OK, got an 8bit sound buffer. Not perfect but it will do + ** We still want 16 bit secondary buffers so restore those values + */ + DsBuffFormat.wBitsPerSample = old_bits_per_sample; + DsBuffFormat.nBlockAlign = old_block_align; + DsBuffFormat.nAvgBytesPerSec = old_bytes_per_sec; + } + } + + + /* + ** Start the primary sound buffer playing + ** + */if ( PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING) != DS_OK ){ + Print_Sound_Error("Unable to play Direct Sound primary buffer",window); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + LockedData.DigiHandle=1; + + + /* + ** Initialise the global critical section object for sound thread syncronisation + */ + InitializeCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Initialise the Windows timer system to provide us with a callback + ** + */ + SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC); + AudioDone = FALSE; + //_beginthread(&Sound_Thread, NULL, 16*1024, NULL); + + /* + ** Define the format for the secondary sound buffers + */ + BufferDesc.dwFlags=DSBCAPS_CTRLVOLUME; + BufferDesc.dwBufferBytes=SECONDARY_BUFFER_SIZE; + BufferDesc.lpwfxFormat = (LPWAVEFORMATEX) &DsBuffFormat; + + + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + + /* + ** Allocate once secondary direct sound buffer for each simultaneous sound effect + ** + */ + for (index = 0; index < MAX_SFX; index++) { + + SoundObject->CreateSoundBuffer (&BufferDesc , &LockedData.SampleTracker[index].PlayBuffer , NULL); + + LockedData.SampleTracker[index].PlaybackRate = rate; + LockedData.SampleTracker[index].Stereo = (stereo) ? AUD_FLAG_STEREO : 0; + LockedData.SampleTracker[index].BitSize = (bits_per_sample == 16) ? AUD_FLAG_16BIT : 0; + LockedData.SampleTracker[index].FileHandle = WW_ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + InitializeCriticalSection (&LockedData.SampleTracker[index].AudioCriticalSection); + + } + + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + * 11/02/1995 ST : Modified for Direct Sound * + *=============================================================================================*/ +void Sound_End(void) +{ + + int index; + + /* + ** Remove the Windows timer event we installed for the sound callback + */ + if (SoundTimerHandle){ + timeKillEvent(SoundTimerHandle); + SoundTimerHandle = 0; + } + + + if (SoundObject && PrimaryBufferPtr){ + /* + ** Stop all sounds and release the Direct Sound secondary sound buffers + */ + for (index=0 ; index < MAX_SFX; index++){ + if ( LockedData.SampleTracker[index].PlayBuffer ){ + Stop_Sample (index); + LockedData.SampleTracker[index].PlayBuffer->Stop(); + LockedData.SampleTracker[index].PlayBuffer->Release(); + LockedData.SampleTracker[index].PlayBuffer = NULL; + DeleteCriticalSection(&LockedData.SampleTracker[index].AudioCriticalSection); + } + } + } + + AudioDone = TRUE; + + if (FileStreamBuffer){ + Free (FileStreamBuffer); + FileStreamBuffer = NULL; + } + + /* + ** Stop and release the direct sound primary buffer + */ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + } + + /* + ** Release the Direct Sound Object + */ + if (SoundObject){ + SoundObject->Release(); + SoundObject = NULL; + } + + + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + + /* + ** Since the timer has stopped, we are finished with our global critical section. + */ + DeleteCriticalSection(&GlobalAudioCriticalSection); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + * 11/2/95 4:09PM ST : Modified for Direct Sound * + *=============================================================================================*/ +void Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < MAX_SFX) { + + if (AudioDone) return; + + EnterCriticalSection (&GlobalAudioCriticalSection); + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + LockedData.SampleTracker[handle].Active = FALSE; + + if (!LockedData.SampleTracker[handle].IsScore) { + LockedData.SampleTracker[handle].Original = NULL; + } + + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + LockedData.SampleTracker[handle].PlayBuffer->Stop(); + } + + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = WW_ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + LeaveCriticalSection (&GlobalAudioCriticalSection); + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + DWORD status; + + if (AudioDone) return (FALSE); + + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= MAX_SFX) return(FALSE); + + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + DumpBuffer = LockedData.SampleTracker[handle].PlayBuffer; + if (LockedData.SampleTracker[handle].PlayBuffer->GetStatus( &status ) == DS_OK){ + return ( (DSBSTATUS_PLAYING & status) || (DSBSTATUS_LOOPING & status) ); + }else{ + return (TRUE); + } +} + + + +/*********************************************************************************************** + * Is_Sample_Playing -- returns the play state of a sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: TRUE if sample is currently playing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:11PM ST : Commented * + *=============================================================================================*/ + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + if (AudioDone) return (FALSE); + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + if (!sample) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return FALSE; + } + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Sample_Playing -- stops a playing sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:13PM ST : Commented * + *=============================================================================================*/ + +void Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} + + + + + +/*********************************************************************************************** + * Get_Free_Sample_Handle -- finds a free slot in which to play a new sample * + * * + * * + * * + * INPUT: priority of sample we want to play * + * * + * OUTPUT: Handle or -1 if none free * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:14PM ST : Added function header * + *=============================================================================================*/ + +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = MAX_SFX - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < MAX_SFX; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + + if (id == MAX_SFX) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + + if (id == -1) { + return -1; + } + + if (LockedData.SampleTracker[id].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = WW_ERROR; + } + + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} + + + + + +/*********************************************************************************************** + * Attempt_Audio_Restore -- tries to restore the direct sound buffers * + * * + * * + * * + * INPUT: ptr to direct sound buffer * + * * + * OUTPUT: TRUE if buffer was successfully restored * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 9:47AM ST : Created * + *=============================================================================================*/ + +BOOL Attempt_Audio_Restore (LPDIRECTSOUNDBUFFER sound_buffer) +{ + + int return_code; + DWORD play_status; + int restore_attempts=0; + + if (AudioDone){ + return (FALSE); + } + + /* + ** Call the audio focus loss function if it has been set up + */ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + + /* + ** Try to restore the sound buffer + */ + do{ + Restore_Sound_Buffers(); + return_code = sound_buffer->GetStatus ( &play_status ); + + } while (restore_attempts++<2 && return_code == DSERR_BUFFERLOST); + + return ((BOOL) ~(return_code == DSERR_BUFFERLOST)); +} + + + +/*********************************************************************************************** + * Convert_HMI_To_Direct_Sound_Volume -- Converts a linear volume value into an expotential * + * value * + * * + * This function converts a linear C&C volume in the range 0-255 (255 loudest) to a direct * + * sound volume in the range 0 to -10000 (with 0 being the loadest) * + * * + * INPUT: volume in range 0-255 * + * * + * OUTPUT: volume in range -10000 to 0 * + * * + * WARNINGS: None * + * * + * Note: The 27.685 value comes from 255 divided by the log of 10001 * + * * + * HISTORY: * + * 9/18/96 11:36AM ST : Created * + *=============================================================================================*/ +int Convert_HMI_To_Direct_Sound_Volume(int volume) +{ + if (volume == 0) return -10000; + if (volume == 255) return 0; + + float vol = (float)volume; + float retval = exp ( (255.0-vol)/27.68597374) -1; + return ((int)-retval); +} + + + + + +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + * 11/02/1995 ST : Windows Direct Sound support * + *=============================================================================================*/ +extern BOOL Any_Locked(void); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short , int id) +{ + AUDHeaderType RawHeader; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + LPVOID play_buffer_ptr; //pointer to locked direct sound buffer + LPVOID dummy_buffer_ptr; //dummy pointer to second area of locked direct sound buffer + DWORD lock_length1; + DWORD lock_length2; + DWORD play_status; + HRESULT return_code; + int retries=0; + + + if (Any_Locked()) return(0); + + if (AudioDone) return (0); + + st = &LockedData.SampleTracker[id]; + //EnterCriticalSection (&GlobalAudioCriticalSection); + + if (!sample || LockedData.DigiHandle == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + if (id == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Fudge the sample rate to 22k + */ + if (RawHeader.Rate <24000 && RawHeader.Rate >20000) RawHeader.Rate = 22050; + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + if (!AudioDone){ + EnterCriticalSection(&GlobalAudioCriticalSection); + } + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + st->Priority = (short)priority; + st->DontTouch = TRUE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + if (!AudioDone){ + LeaveCriticalSection(&GlobalAudioCriticalSection); + } + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** If the sample rate , bits per sample or stereo capabilities of the buffer do not + ** match the sample then reallocate the direct sound buffer with the required capabilities + */ + if ( ( RawHeader.Rate != st->PlaybackRate ) || + ( ( RawHeader.Flags & AUD_FLAG_16BIT ) != ( st->BitSize & AUD_FLAG_16BIT ) ) || + ( ( RawHeader.Flags & AUD_FLAG_STEREO) != ( st->Stereo & AUD_FLAG_STEREO ) ) ) { + + st->Active=0; + st->Service=0; + st->MoreSource=0; + + /* + ** Stop the sound buffer playing + */ + DumpBuffer = st->PlayBuffer; + do { + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + }while (return_code == DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->PlayBuffer->Stop(); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } + + st->PlayBuffer->Release(); + st->PlayBuffer=NULL; + + DsBuffFormat.nSamplesPerSec = (unsigned short int) RawHeader.Rate; + DsBuffFormat.nChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1 ; + DsBuffFormat.wBitsPerSample = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8 ; + DsBuffFormat.nBlockAlign = (short) ((DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Create the new sound buffer + */ + return_code= SoundObject->CreateSoundBuffer (&BufferDesc , &st->PlayBuffer , NULL); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + /* + ** Just return if the create failed unexpectedly + ** + ** If we failed then flag the buffer as having an impossible format so it wont match + ** any sample. This will ensure that we try and create the buffer again next time its used. + */ + if (return_code!=DS_OK && return_code!=DSERR_BUFFERLOST){ + st->PlaybackRate = 0; + st->Stereo = 0; + st->BitSize = 0; + return(-1); + } + + /* + ** Remember the format of the new buffer + */ + st->PlaybackRate = RawHeader.Rate; + st->Stereo = RawHeader.Flags & AUD_FLAG_STEREO; + st->BitSize = RawHeader.Flags & AUD_FLAG_16BIT; + } + + /* + ** Fill in 3/4 of the play buffer. + */ + + // + // Stop the sound buffer playing before we lock it + // + do { + DumpBuffer = st->PlayBuffer; + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->Active=0; + st->Service=0; + st->MoreSource=0; + st->PlayBuffer->Stop(); + } + // + // Lock the direct sound buffer so we can write to it + // + do { + return_code = st->PlayBuffer->Lock ( 0 , + SECONDARY_BUFFER_SIZE, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (return_code != DS_OK) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (-1); + } + + // + // Decompress the sample into the direct sound buffer + // + st->DestPtr=(void*)Sample_Copy ( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE*1/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + if ( st->DestPtr==(void*) (SECONDARY_BUFFER_SIZE*1/4) ){ + + // Must be more data to copy so we dont need to zero the buffer + st->MoreSource=TRUE; + st->Service=TRUE; + st->OneShot=FALSE; + } else { + + // Whole sample is in the buffer so flag that we dont need to + // copy more. Clear out the end of the buffer so that it + // goes quiet if we play past the end + st->MoreSource=FALSE; + st->OneShot=TRUE; + st->Service=TRUE; //We still need to service it so that we can stop it when + // it plays past the end of the sample data + //memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE - (unsigned)st->DestPtr ); + memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE/4); + } + + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + + + /* + ** + ** Set the volume of the sample. + ** + */ + st->Volume = (volume << 7); + do { + + //return_code = st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume(volume)); + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.SoundVolume*volume)/256) ); + //return_code = st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.SoundVolume) ) + // *1000) >>15 ) ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + + /* + ** Make sure the primary sound buffer is playing + */ + if (!Start_Primary_Sound_Buffer(FALSE)){ + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + + /* + ** Set the buffers play pointer to the beginning of the buffer + */ + do { + return_code = st->PlayBuffer->SetCurrentPosition (0); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + + /* + ** Start the sample playing now. + */ + do + { + return_code = st->PlayBuffer->Play (0,0,DSBPLAY_LOOPING); + + switch (return_code){ + + case DS_OK : + if (!AudioDone){ + EnterCriticalSection (&GlobalAudioCriticalSection); + } + st->Active=TRUE; + st->Handle=(short)id; + st->DontTouch = FALSE; + if (!AudioDone){ + LeaveCriticalSection (&GlobalAudioCriticalSection); + } + return (st->Handle); + + case DSERR_BUFFERLOST : + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + break; + + default: + st->Active=FALSE; + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); + } + + } while (return_code==DSERR_BUFFERLOST); + + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); +} + + + + + +/*********************************************************************************************** + * Restore_Sound_Buffers -- restore the sound buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:53PM ST : Created * + *=============================================================================================*/ + +void Restore_Sound_Buffers ( void ) +{ + + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Restore(); + } + + + for ( int index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].PlayBuffer){ + LockedData.SampleTracker[index].PlayBuffer->Restore(); + } + } +} + + + + + + + + +/*********************************************************************************************** + * Set_Sound_Vol -- sets the overall volume for sampled sounds * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +/*********************************************************************************************** + * Set_Score_Vol -- sets the overall volume for music scores * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ +int Set_Score_Vol(int volume) +{ + int old; + SampleTrackerType *st; //ptr to SampleTracker structure + + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + + for (int index=0 ; indexIsScore && st->Active){ + //st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.ScoreVolume) ) + // *1000) >>15 ) ); + + //st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume ( st->Volume >>7 ) ); + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.ScoreVolume*(st->Volume >>7))/256) ); + } + } + return(old); +} + + + +/*********************************************************************************************** + * Fade_Sample -- Start a sample fading * + * * + * * + * * + * INPUT: Sample handle * + * fade rate * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:21PM ST : Added function header * + *=============================================================================================*/ + +void Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + st = &LockedData.SampleTracker[handle]; + st->Reducer = (short) ((st->Volume / ticks)+1); + } + } +} + + + +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + + + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} + + + +/*********************************************************************************************** + * Start_Primary_Sound_Buffer -- start the primary sound buffer playing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ +extern BOOL GameInFocus; +BOOL Start_Primary_Sound_Buffer (BOOL forced) +{ + DWORD status; + + if (PrimaryBufferPtr && GameInFocus){ + if (forced){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + } else { + + if (PrimaryBufferPtr->GetStatus (&status) == DS_OK){ + if (! ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_LOOPING))){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + }else{ + return (TRUE); + } + } + } + } + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Primary_Sound_Buffer -- stops the primary sound buffer from playing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This stops all sound playback * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ + +void Stop_Primary_Sound_Buffer (void) +{ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Stop(); // Oh I + PrimaryBufferPtr->Stop(); // Hate Direct Sound + PrimaryBufferPtr->Stop(); // So much..... + } + + for ( int index = 0; index < MAX_SFX; index++) { + Stop_Sample(index); + } + +} + + +void Suspend_Audio_Thread(void) +{ + if (SoundThreadActive){ + //SuspendThread(SoundThreadHandle); + timeKillEvent(SoundTimerHandle); + SoundTimerHandle = NULL; + SoundThreadActive = FALSE; + } +} + + + + +void Resume_Audio_Thread(void) +{ + if (!SoundThreadActive){ + //ResumeThread(SoundThreadHandle); + SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC); + SoundThreadActive = TRUE; + } +} + + diff --git a/WIN32LIB/AUDIO/SOUNDLCK.CPP b/WIN32LIB/AUDIO/SOUNDLCK.CPP new file mode 100644 index 0000000..27dae7d --- /dev/null +++ b/WIN32LIB/AUDIO/SOUNDLCK.CPP @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDLCK.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#include +#include +#include +#include "dsound.h" +#include +#include "wwmem.h" +#include "wwstd.h" +#include "soundint.h" + + + +LockedDataType LockedData; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * INIT_LOCKED_DATA -- Initializes sound driver locked data * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void Init_Locked_Data(void) +{ + + /* + ** Initialize all of the data elements that need to be locked. + */ + LockedData.DigiHandle = -1; + LockedData.ServiceSomething = FALSE; + LockedData.MagicNumber = 0xDEAF; + LockedData.UncompBuffer = NULL; +// LockedData.StreamBufferSize = (2*SECONDARY_BUFFER_SIZE)+128; + LockedData.StreamBufferSize = (SECONDARY_BUFFER_SIZE/4)+128; + LockedData.StreamBufferCount = 16; + LockedData.SoundVolume = 255; + LockedData.ScoreVolume = 255; + LockedData._int = FALSE; + + #ifdef cuts + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Lock(&LockedData, 4096L); + DPMI_Lock(Simple_Copy, 4096L); + DPMI_Lock(Sample_Copy, 4096L); + DPMI_Lock((void *)maintenance_callback, 4096L); + DPMI_Lock((void *)DigiCallback, 4096L); + DPMI_Lock((void *)HMI_TimerCallback, 4096L); + DPMI_Lock(Audio_Add_Long_To_Pointer, 4096L); + DPMI_Lock(DPMI_Unlock, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Lock(Mem_Copy, 4096L); + DPMI_Lock(Audio_Mem_Set, 4096L); + DPMI_Lock(__GETDS, 4096L); + + /* + ** Finally lock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Lock(); + sosCODEC_Lock(); + + #endif //cuts +} + diff --git a/WIN32LIB/AUDIO/TEST.CPP b/WIN32LIB/AUDIO/TEST.CPP new file mode 100644 index 0000000..e4c28fd --- /dev/null +++ b/WIN32LIB/AUDIO/TEST.CPP @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "audio.h" + + + +int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) +{ + + + + + + + File_Stream_Sample_Vol("file", 0, 0); + + return (0); +} \ No newline at end of file diff --git a/WIN32LIB/AUDIO/TST.CPP b/WIN32LIB/AUDIO/TST.CPP new file mode 100644 index 0000000..95128a5 --- /dev/null +++ b/WIN32LIB/AUDIO/TST.CPP @@ -0,0 +1,32 @@ +/* +** 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 . +*/ + + +#undef _WINDOWS_16_ +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#define __WIN32 + +#include +#include +#include + +main +{} diff --git a/WIN32LIB/DIPTHONG/DIPTHONG.CPP b/WIN32LIB/DIPTHONG/DIPTHONG.CPP new file mode 100644 index 0000000..53b1e36 --- /dev/null +++ b/WIN32LIB/DIPTHONG/DIPTHONG.CPP @@ -0,0 +1,328 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + char temp; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + temp = *src++; + temp += 127; + *dest++ = temp; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int common, // Common character index. + dipthong; // Dipthong character index. + + unsigned long length=0; // Length of output string + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = (char)KA_EXTEND; + first -= 127; + length++; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char) (common << 3); + first |= (unsigned char)dipthong; + first |= (unsigned char)0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + length++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(length); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ + +#define TXT_GUEST 4567+3 +#define TXT_LOGIN 4567+4 +#define TXT_LOGIN_TO_INTERNET 4567+5 +#define TXT_YOUR_HANDLE 4567+6 +#define TXT_YOUR_PASSWORD 4567+7 +#define TXT_INTERNET_HOST 4567+8 +#define TXT_INTERNET_JOIN 4567+9 +#define TXT_INTERNET_GAME_TYPE 4567+10 +#define TXT_JOIN_INTERNET_GAME 4567+11 +#define TXT_ENTER_IP_ADDRESS 4567+12 +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + +static char InternetTxt[22][40]={ + "Internet H2H", + "Host Internet Game", + "Join Internet Game", + "Guest", + "Login", + "Login to Planet Westwood", + "Planet Westwood Handle", + "Planet Westwood Password", + "Host Game", + "Join Game", + "Choose Type of Internet Game", + "Join Internet Game", + "Address of Host", + "Connecting...", + "Connection Error!", + "Unable to connect to host!", + "Connecting to host...", + "Unable to resolve host address!", + "Unable to accept client connection", + "Unable to connect!", + "Connection lost!", + "Resolving address of host..." +}; + +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + + if (!data || string < 0) return(NULL); + + if (string >= 4567) return (InternetTxt[string-4567]); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/WIN32LIB/DIPTHONG/DIPTHONG.H b/WIN32LIB/DIPTHONG/DIPTHONG.H new file mode 100644 index 0000000..2f5ed70 --- /dev/null +++ b/WIN32LIB/DIPTHONG/DIPTHONG.H @@ -0,0 +1,24 @@ +/* +** 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 . +*/ + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/WIN32LIB/DIPTHONG/MAKEFILE b/WIN32LIB/DIPTHONG/MAKEFILE new file mode 100644 index 0000000..f8f7f87 --- /dev/null +++ b/WIN32LIB/DIPTHONG/MAKEFILE @@ -0,0 +1,192 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = dipthong +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + dipthong.obj & + _diptabl.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/DIPTHONG/MAKEFILE.BOR b/WIN32LIB/DIPTHONG/MAKEFILE.BOR new file mode 100644 index 0000000..023dc39 --- /dev/null +++ b/WIN32LIB/DIPTHONG/MAKEFILE.BOR @@ -0,0 +1,170 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = dipthong +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + dipthong.obj \ + _diptabl.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-dipthong.obj & ++-_diptabl.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/DIPTHONG/MAKEFILE.WAT b/WIN32LIB/DIPTHONG/MAKEFILE.WAT new file mode 100644 index 0000000..2eb7b92 --- /dev/null +++ b/WIN32LIB/DIPTHONG/MAKEFILE.WAT @@ -0,0 +1,192 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = dipthong +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + dipthong.obj & + _diptabl.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/DIPTHONG/_DIPTABL.CPP b/WIN32LIB/DIPTHONG/_DIPTABL.CPP new file mode 100644 index 0000000..885fb41 --- /dev/null +++ b/WIN32LIB/DIPTHONG/_DIPTABL.CPP @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/BITBLIT.ASM b/WIN32LIB/DRAWBUFF/BITBLIT.ASM new file mode 100644 index 0000000..ce6d353 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/BITBLIT.ASM @@ -0,0 +1,462 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + + PROC Linear_Blit_To_Linear C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, display rectangle) +; - trivially rejected (return with no action) +; - retangle must be iteratively clipped again edges of the clipping window +; and the remaining retangle is display. + +; Clip Source Rectangle against source Window boundaries. + mov esi,[this_object] ; get ptr to src + xor ecx,ecx ; Set sutherland code to zero + xor edx,edx ; Set sutherland code to zero + + ; compute the difference in the X axis and get the bit signs into ecx , edx + mov edi,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov ebx,[x_pixel] ; Get first end point x_pixel into register + mov eax,[x_pixel] ; Get second end point x_pixel into register + add ebx,[pixel_width] ; second point x1_pixel = x + width + shld ecx, eax,1 ; the sign bit of x_pixel is sutherland code0 bit4 + mov [x1_pixel],ebx ; save second for future use + inc edi ; move the right edge by one unit + shld edx,ebx,1 ; the sign bit of x1_pixel is sutherland code0 bit4 + sub eax,edi ; compute the difference x0_pixel - width + sub ebx,edi ; compute the difference x1_pixel - width + shld ecx,eax,1 ; the sign bit of the difference is sutherland code0 bit3 + shld edx,ebx,1 ; the sign bit of the difference is sutherland code0 bit3 + + ; the following code is just a repeticion of the above code + ; in the Y axis. + mov edi,[(GraphicViewPort esi).GVPHeight] ; get height into register + mov ebx,[y_pixel] + mov eax,[y_pixel] + add ebx,[pixel_height] + shld ecx,eax,1 + mov [y1_pixel ],ebx + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + ; Here we have the to Sutherland code into cl and dl + xor cl,5 ; bit 2 and 0 are complented, reverse then + xor dl,5 ; bit 2 and 0 are complented, reverse then + mov al,cl ; save code1 in case we have to clip iteratively + test dl,cl ; if any bit in code0 and its counter bit + jnz ??real_out ; in code1 is set then the rectangle in outside + or al,dl ; if all bit of code0 the counter bit in + jz ??clip_against_dest ; in code1 is set to zero, then all + ; end points of the rectangle are + ; inside the clipping window + + ; if we are here the polygon have to be clip iteratively + test cl,1000b ; if bit 4 in code0 is set then + jz ??scr_left_ok ; x_pixel is smaller than zero + mov [x_pixel],0 ; set x_pixel to cero. + +??scr_left_ok: + test cl,0010b ; if bit 2 in code0 is set then + jz ??scr_bottom_ok ; y_pixel is smaller than zero + mov [ y_pixel ],0 ; set y_pixel to cero. + +??scr_bottom_ok: + test dl,0100b ; if bit 3 in code1 is set then + jz ??scr_right_ok ; x1_pixel is greater than the width + mov eax,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov [ x1_pixel ],eax ; set x1_pixel to width. +??scr_right_ok: + test dl,0001b ; if bit 0 in code1 is set then + jz ??clip_against_dest ; y1_pixel is greater than the width + mov eax,[(GraphicViewPort esi).GVPHeight] ; get height into register + mov [ y1_pixel ],eax ; set y1_pixel to height. + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + ; build the destination rectangle before clipping + ; dest_x1 = dest_x0 + ( x1_pixel - x_pixel ) + ; dest_y1 = dest_y0 + ( y1_pixel - y_pixel ) + mov eax,[dest_x0] ; get dest_x0 into eax + mov ebx,[dest_y0] ; get dest_y0 into ebx + sub eax,[x_pixel] ; subtract x_pixel from eax + sub ebx,[y_pixel] ; subtract y_pixel from ebx + add eax,[x1_pixel] ; add x1_pixel to eax + add ebx,[y1_pixel] ; add y1_pixel to ebx + mov [dest_x1],eax ; save eax into dest_x1 + mov [dest_y1],ebx ; save eax into dest_y1 + + + ; The followin code is a repeticion of the Sutherland clipping + ; descrived above. + mov esi,[dest] ; get ptr to src + xor ecx,ecx + xor edx,edx + mov edi,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov eax,[dest_x0] + mov ebx,[dest_x1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + mov edi,[( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax,[dest_y0] + mov ebx,[dest_y1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + xor cl,5 + xor dl,5 + mov al,cl + test dl,cl + jnz ??real_out + or al,dl + jz ??do_blit + + test cl,1000b + jz ??dest_left_ok + mov eax,[ dest_x0 ] + mov [ dest_x0 ],0 + sub [ x_pixel ],eax + +??dest_left_ok: + test cl,0010b + jz ??dest_bottom_ok + mov eax,[ dest_y0 ] + mov [ dest_y0 ],0 + sub [ y_pixel ],eax + + +??dest_bottom_ok: + test dl,0100b + jz ??dest_right_ok + mov ebx,[ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax,[ dest_x1 ] + mov [ dest_x1 ],ebx + sub eax,ebx + sub [ x1_pixel ],eax + +??dest_right_ok: + test dl,0001b + jz ??do_blit + mov ebx,[ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov eax,[ dest_y1 ] + mov [ dest_y1 ],ebx + sub eax,ebx + sub [ y1_pixel ],eax + + +; Here is where we do the actual blit +??do_blit: + cld + mov ebx,[this_object] + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov ecx,eax + mul [y_pixel] + add esi,[x_pixel] + mov [source_area],ecx + add esi,eax + + add ecx,[x_pixel ] + sub ecx,[x1_pixel ] + mov [scr_ajust_width ],ecx + + mov ebx,[dest] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov ecx,eax + mul [ dest_y0 ] + add edi,[ dest_x0 ] + mov [ dest_area ],ecx + add edi,eax + + mov eax,[ dest_x1 ] + sub eax,[ dest_x0 ] + jle ??real_out + sub ecx,eax + mov [ dest_ajust_width ],ecx + + mov edx,[ dest_y1 ] + sub edx,[ dest_y0 ] + jle ??real_out + + cmp esi,edi + jz ??real_out + jl ??backupward_blit + +; ******************************************************************** +; Forward bitblit + + test [ trans ],1 + jnz ??forward_Blit_trans + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax,10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx,edi + mov ebx,eax + neg ecx + and ecx,3 + sub ebx,ecx + rep movsb + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,3 + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx,eax + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_bytes + ret + +??forward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??forward_loop_trans: + mov ecx,eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret + + +; ************************************************************************ +; backward bitblit + +??backupward_blit: + + mov ebx,[ source_area ] + dec edx + add esi,eax + imul ebx,edx + std + lea esi,[ esi + ebx - 1 ] + + mov ebx,[ dest_area ] + add edi,eax + imul ebx,edx + lea edi,[ edi + ebx - 1] + + test [ trans ],1 + jnz ??backward_Blit_trans + + cmp eax,15 + jl ??backward_loop_bytes + +??backward_loop_dword: + push edi + push esi + lea ecx,[edi+1] + mov ebx,eax + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_dword + cld + ret + +??backward_loop_bytes: + push edi + mov ecx,eax ; remove that from the total size to be copied later. + push esi + rep movsb ; do the copy. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_bytes + cld + ret + +??backward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??back_transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??backward_loop_trans: + mov ecx,eax + push edi + push esi + jmp [ y1_pixel ] +??backward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + dec esi + dec edi + ENDM + ??back_transp_reference: + dec ecx + jge ??backward_trans_line + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_trans + cld + ret + +??real_out: + ret + ENDP Linear_Blit_To_Linear + + + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/BUFFER.CPP b/WIN32LIB/DRAWBUFF/BUFFER.CPP new file mode 100644 index 0000000..b3bea0e --- /dev/null +++ b/WIN32LIB/DRAWBUFF/BUFFER.CPP @@ -0,0 +1,131 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/WIN32LIB/DRAWBUFF/BUFFER.H b/WIN32LIB/DRAWBUFF/BUFFER.H new file mode 100644 index 0000000..ef2e66f --- /dev/null +++ b/WIN32LIB/DRAWBUFF/BUFFER.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif diff --git a/WIN32LIB/DRAWBUFF/BUFFGLBL.CPP b/WIN32LIB/DRAWBUFF/BUFFGLBL.CPP new file mode 100644 index 0000000..603d2e7 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/BUFFGLBL.CPP @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "gbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +#ifdef not_any_more_it_doesnt +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +#endif //not_any_more_it_doesnt + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicViewPortClass *LogicPage; + +BOOL IconCacheAllowed = TRUE; + +/* +** Pointer to a function we will call if we detect loss of focus +*/ +void (*Gbuffer_Focus_Loss_Function)(void) = NULL; diff --git a/WIN32LIB/DRAWBUFF/CLEAR.ASM b/WIN32LIB/DRAWBUFF/CLEAR.ASM new file mode 100644 index 0000000..0f66283 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/CLEAR.ASM @@ -0,0 +1,130 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Graphics Buffer * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : August 23, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* + PROC Buffer_Clear C near + USES eax,ebx,ecx,edx,esi,edi + + ARG this_object:DWORD ; this is a member function + ARG color:BYTE ; what color should we clear to + + cld ; always go forward + + mov ebx,[this_object] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov edx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov esi,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + push [dword (GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + mov ebx,[(GraphicViewPort ebx).GVPXAdd] ; esi = add for each line + add ebx,[esp] ; Yes, I know its nasty but + add esp,4 ; it works! + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??byte_by_byte ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + push ebx +??dword_aligned_loop: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??dword_aligned_loop ; if more to do than do it + pop eax + ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??byte_by_byte: + mov ecx,esi ; get total width in bytes + rep stosb ; store the width + add edi,ebx ; handle the xadd + dec edx ; decrement the height + jnz ??byte_by_byte ; if any left then next line +??exit: + ret + ENDP Buffer_Clear +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/DRAWBUFF.H b/WIN32LIB/DRAWBUFF/DRAWBUFF.H new file mode 100644 index 0000000..98b57b7 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/DRAWBUFF.H @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + + +#ifndef DRAWBUFF_H +#define DRAWBUFF_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +extern "C" { + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + long __cdecl Buffer_Size_Of_Region(void *thisptr, int w, int h); + + void __cdecl Buffer_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + int __cdecl Buffer_Get_Pixel(void * thisptr, int x, int y); + void __cdecl Buffer_Clear(void *thisptr, unsigned char color); + long __cdecl Buffer_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + BOOL __cdecl Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + BOOL __cdecl Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + LONG __cdecl Buffer_Print(void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); +} + +extern GraphicViewPortClass *LogicPage; +extern BOOL AllowHardwareBlitFills; +#endif diff --git a/WIN32LIB/DRAWBUFF/DRAWBUFF.INC b/WIN32LIB/DRAWBUFF/DRAWBUFF.INC new file mode 100644 index 0000000..bcb0b1f --- /dev/null +++ b/WIN32LIB/DRAWBUFF/DRAWBUFF.INC @@ -0,0 +1,93 @@ +; +; 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 . +; + + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : DRAWBUFF.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Clear :NEAR + +; Externs from BITBLIT.ASM module of the DRAWBUFF library +GLOBAL C Linear_Blit_To_Linear :NEAR + +; Externs from TOBUFF.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the DRAWBUFF library +GLOBAL C Linear_Scale_To_Linear :NEAR + +; Externs from TXTPRNT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Print :NEAR + + +;*-------------------------------------------------------------------------* +;* Define Buffer only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Line:NEAR + +; Externs from FILLQUAD.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Remap :NEAR + +; Externs from STAMP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Stamp :NEAR + +GLOBAL C get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + diff --git a/WIN32LIB/DRAWBUFF/DRAWLINE.ASM b/WIN32LIB/DRAWBUFF/DRAWLINE.ASM new file mode 100644 index 0000000..a4c9a7a --- /dev/null +++ b/WIN32LIB/DRAWBUFF/DRAWLINE.ASM @@ -0,0 +1,464 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + + +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg ??big_line + mov al,[byte color] + rep stosb ; write as many words as possible + jmp short ??out ; get outt + + +??big_line: + mov al,[byte color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + +??aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp ??out + + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP Buffer_Draw_Line + + +END diff --git a/WIN32LIB/DRAWBUFF/DRAWRECT.CPP b/WIN32LIB/DRAWBUFF/DRAWRECT.CPP new file mode 100644 index 0000000..9c7a523 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/DRAWRECT.CPP @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "gbuffer.h" + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Lock(); + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); + Unlock(); +} diff --git a/WIN32LIB/DRAWBUFF/FILLQUAD.ASM b/WIN32LIB/DRAWBUFF/FILLQUAD.ASM new file mode 100644 index 0000000..e610c55 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/FILLQUAD.ASM @@ -0,0 +1,669 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : FILLQUAD.ASM * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Fill_Quad -- Flood fills an arbitrary convex quadrilateral * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +SLOT_VACANT EQU 80008000h +NULL EQU 0h + + +CODESEG + +;*************************************************************************** +;* VVC::FILL_QUAD -- Flood fills an arbitrary convex quadrilateral * +;* * +;* INPUT: DWORD this_object - associated graphic viewport * +;* DWORD span_buff - pointer to span array * +;* DWORD x0_pixel - the zeroth x pixel position * +;* DWORD y0_pixel - the zeroth y pixel position * +;* DWORD x1_pixel - the first x pixel position * +;* DWORD y1_pixel - the first y pixel position * +;* DWORD x2_pixel - the second x pixel position * +;* DWORD y2_pixel - the second y pixel position * +;* DWORD x3_pixel - the third x pixel position * +;* DWORD y3_pixel - the third y pixel position * +;* DWORD color - the color of the quad to fill * +;* * +;* Bounds Checking: Compares quad points with the graphic viewport it * +;* has been assigned to. * +;* * +;* Rasterization Rules: FILL_QUAD is designed to be used within a quad * +;* mesh. There is no pixel overlapping or stitching * +;* effects at shared borders. FILL_QUAD is NOT * +;* recommended for isolated quads. * * +;* HISTORY: * +;* 08/11/1994 IML : Created. * +;* 08/26/1994 IML : Various optimizations. * +;* 08/30/1994 IML : Added rasterization rules for shared borders. * +;*=========================================================================* + PROC Buffer_Fill_Quad C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*================================================================== + ;* Define the arguments that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic viewport + ARG span_buff:DWORD ; pointer to span array + ARG x0_pixel:DWORD ; the zeroth x pixel position + ARG y0_pixel:DWORD ; the zeroth y pixel position + ARG x1_pixel:DWORD ; the first x pixel position + ARG y1_pixel:DWORD ; the first y pixel position + ARG x2_pixel:DWORD ; the second x pixel position + ARG y2_pixel:DWORD ; the second y pixel position + ARG x3_pixel:DWORD ; the third x pixel position + ARG y3_pixel:DWORD ; the third y pixel position + ARG color:DWORD ; the color of the quad + + + ;*================================================================== + ;* Define the local variables that we will use on the stack. + ;*================================================================== + LOCAL clip_min_x:DWORD ; boundary of viewport + LOCAL clip_max_x:DWORD ; + LOCAL clip_min_y:DWORD ; + LOCAL clip_max_y:DWORD ; + LOCAL clip_var:DWORD + LOCAL left_clip_base:DWORD:2 ; storage for additional edges + LOCAL left_clip_index:DWORD ; generated by clipping + LOCAL right_clip_base:DWORD:2 ; + LOCAL right_clip_index:DWORD ; + LOCAL scanline_min:DWORD ; vertical extent of quad + LOCAL scanline_max:DWORD + LOCAL realignment:DWORD + LOCAL bpr:DWORD ; bytes per row of associated buffer + + + ;*================================================================== + ;* Extract essential GraphicViewPort info. + ;*================================================================== + mov ebx,[this_object] + mov eax,[(GraphicViewPort ebx).GVPXPos] + mov [clip_min_x],eax + mov eax,[(GraphicViewPort ebx).GVPYPos] + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + + ;*================================================================== + ;* Adjust top and right edges of viewport for rasterization rules. + ;*================================================================== + dec [clip_max_y] + dec [clip_min_y] + + + ;*================================================================== + ;* Find the vertical extent of the quad BEFORE clipping. + ;* y0_pixel = y0, y1_pixel = y1, y2_pixel = y2, y3_pixel = y3 + ;*================================================================== + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jle short ??y1_not_smaller + mov eax,[y1_pixel] + +??y1_not_smaller: + cmp eax,[y2_pixel] + jle short ??y2_not_smaller + mov eax,[y2_pixel] + +??y2_not_smaller: + cmp eax,[y3_pixel] + jle short ??y3_not_smaller + mov eax,[y3_pixel] + +??y3_not_smaller: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_min + mov eax,[clip_min_y] + +??no_clamp_min_min: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_min + mov eax,[clip_max_y] + ; scanline_min = MIN (y0, y1, y2, y3) +??no_clamp_max_min: ; scanline_min = MAX (scanline_min, clip_min_y) + mov [scanline_min],eax ; scanline_min = MIN (scanline_min, clip_max_y) + + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jge short ??y1_not_greater + mov eax,[y1_pixel] + +??y1_not_greater: + cmp eax,[y2_pixel] + jge short ??y2_not_greater + mov eax,[y2_pixel] + +??y2_not_greater: + cmp eax,[y3_pixel] + jge short ??y3_not_greater + mov eax,[y3_pixel] + +??y3_not_greater: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_max + mov eax,[clip_min_y] + +??no_clamp_min_max: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_max + mov eax,[clip_max_y] + ; scanline_max = MAX (y0, y1, y2, y3) +??no_clamp_max_max: ; scanline_max = MAX (scanline_max, clip_min_y) + mov [scanline_max],eax ; scanline_max = MIN (scanline_max, clip_max_y) + + + ;*================================================================== + ;* Initialize memory for spans. + ;*================================================================== + sub eax,[scanline_min] + je ??abort_fill_quad ; don't render quads with zero height + mov ebx,eax + mov eax,[span_buff] ; check span_buff for NULL ptr + cmp eax,NULL + je ??abort_fill_quad + sal ebx,2 + +??span_initialize_loop: + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jge short ??span_initialize_loop + + + ;*================================================================== + ;* Clip and scan convert the four edges defining the quad. + ;*================================================================== +??exit_span_initialize: + mov [left_clip_index],0 + mov [right_clip_index],0 + + mov eax,[x0_pixel] + mov ebx,[y0_pixel] + mov ecx,[x1_pixel] + mov edx,[y1_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x1_pixel] + mov ebx,[y1_pixel] + mov ecx,[x2_pixel] + mov edx,[y2_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x2_pixel] + mov ebx,[y2_pixel] + mov ecx,[x3_pixel] + mov edx,[y3_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x3_pixel] + mov ebx,[y3_pixel] + mov ecx,[x0_pixel] + mov edx,[y0_pixel] + call NEAR PTR ??clip_and_scan_convert + + + ;*================================================================== + ;* Scan convert up to 2 additional left and right vertical edges + ;* generated by the clipping process. + ;*================================================================== + cmp [left_clip_index],0 + je short ??no_left_edge + mov eax,[clip_min_x] + mov ebx,[left_clip_base] + mov ecx,eax + mov edx,[left_clip_base + 4] + call NEAR PTR ??scan_convert + +??no_left_edge: + cmp [right_clip_index],0 + je short ??no_right_edge + mov eax,[clip_max_x] + mov ebx,[right_clip_base] + mov ecx,eax + mov edx,[right_clip_base + 4] + call NEAR PTR ??scan_convert + + + ;*================================================================== + ;* Fill the quad with specified color. Use DWORD copies where + ;* appropriate. + ;*================================================================== +??no_right_edge: + mov eax,[this_object] + mov edi,[(GraphicViewPort eax).GVPOffset] + mov eax,[scanline_min] ; eax = scanline_min + + mov ebx,[scanline_max] + sub ebx,[scanline_min] ; ebx = span count + + mov esi,[span_buff] ; esi = address of top span + + mul [bpr] + add edi,eax ; edi = address of top scanline + ; containing quad + mov al,[BYTE PTR color] ; extend pixel color into eax ready + mov ah,al ; for DWORD copies + mov edx,eax + shl eax,16 + mov ax,dx + + cld ; only fill forwards + + jmp ??skip_span ; rasterization rule: don't + ; render topmost span + +??quad_fill_loop: + cmp [DWORD PTR esi],SLOT_VACANT ; test for unused spans due to clipping + je ??skip_span + xor ecx,ecx + xor edx,edx + mov cx,[WORD PTR esi] + mov dx,[WORD PTR esi + 2] + sub ecx,edx + push edi + jns short ??not_negative_count + add edi,ecx + neg ecx ; ecx = span width + +??not_negative_count: + add edi,edx ; edi = address of start of span + cmp ecx,OPTIMAL_BYTE_COPY ; does span width justify DWORD copies? + jl short ??byte_copy + mov edx,ecx + mov ecx,edi + and ecx,3 ; if (ecx == 0) edi is already + jz short ??dword_copy_no_alignment ; DWORD aligned + xor ecx,3 + inc ecx ; ecx = number of pixels before alignment + sub edx,ecx + rep stosb + +??dword_copy_no_alignment: + mov ecx,edx ; ecx = remaining pixels on span + shr ecx,2 ; copy (ecx / 4) DWORDS + rep stosd + mov ecx,edx + and ecx,3 ; ecx = remaining pixels on span + +??byte_copy: + rep stosb ; byte copy remaining pixels on span + pop edi + +??skip_span: + add edi,[bpr] ; edi = address of start of next scanline + add esi,4 ; esi = address of next span + dec ebx + jge short ??quad_fill_loop ; is span count >= 0? + +??abort_fill_quad: + ret + + + ;*================================================================== + ;* This is the section that "pushes" the edge into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars clip_min_x, clip_min_y, clip_max_x, clip_max_y + ;* are used to clip the edge (default is the screen). + ;* PORTABLE start. + ;*================================================================== + + + ;*================================================================== + ;* Clip an edge against the viewport. + ;*================================================================== +??clip_and_scan_convert: + call NEAR PTR ??set_left_right_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_left_right_bits + mov [clip_var],edi + or [clip_var],esi + jz ??clip_up_down ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + +??clip_up_down: + call NEAR PTR ??set_up_down_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_up_down_bits + mov [clip_var],edi + or [clip_var],esi + jz ??scan_convert ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??scan_convert + + + ;*================================================================== + ;* Subroutine table for clipping conditions. + ;*================================================================== +??clip_tbl DD ??nada,??a_lft,??a_rgt,??nada + DD ??a_up,??nada,??nada,??nada + DD ??a_dwn + + + ;*================================================================== + ;* Subroutines for clipping conditions. + ;*================================================================== +??nada: + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + retn + +??a_dwn: + mov esi,[clip_max_y] + call NEAR PTR ??clip_vert + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[left_clip_index] + cmp ebx,[clip_min_y] + jge ??no_left_min_clip + mov ebx,[clip_min_y] + +??no_left_min_clip: + cmp ebx,[clip_max_y] + jle ??no_left_max_clip + mov ebx,[clip_max_y] + +??no_left_max_clip: + mov [left_clip_base + esi],ebx ; a left edge will be generated + mov [left_clip_index],4 ; store off yb + pop ebx + retn + +??a_rgt: + mov esi,[clip_max_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[right_clip_index] + cmp ebx,[clip_min_y] + jge ??no_right_min_clip + mov ebx,[clip_min_y] + +??no_right_min_clip: + cmp ebx,[clip_max_y] + jle ??no_right_max_clip + mov ebx,[clip_max_y] + +??no_right_max_clip: + mov [right_clip_base + esi],ebx ; a right edge will be generated + mov [right_clip_index],4 ; store off yb + pop ebx + retn + + + ;*================================================================== + ;* Clip a line against a horizontal edge at clip_y. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;* xa' = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + ;* ya' = clip_y + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax = -xa + add eax,ecx ; eax = (xb-xa) + mov edx,esi ; edx = clip_y + sub edx,ebx ; edx = (clip_y-ya) + imul edx ; eax = (clip_y-ya)(xb-xa) + idiv [clip_var] ; eax = (clip_y-ya)(xb-xa)/(yb-ya) + pop edx + add eax,edx ; eax = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + pop edx + mov ebx,esi ; ebx = clip_y + retn + + + ;*================================================================== + ;* Clip a line against a vertical edge at clip_x. + ;* (eax,ebxx) = (xa,ya), (ecx,edxx) = (xb,yb) + ;* ya' = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + ;* xa' = clip_x + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (clip_x-xa) + imul edx ; eax = (clip_x-xa)(yb-ya) + idiv [clip_var] ; eax = (clip_x-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi ; eax = clip_x + retn + + + ;*================================================================== + ;* Set the condition bits for the subroutine table. + ;*================================================================== +??set_left_right_bits: + xor esi,esi + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,1 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,2 + +??a_not_right: + retn + +??set_up_down_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,4 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,8 + +??a_not_down: + retn + + + ;*================================================================== + ;* PORTABLE end. + ;*================================================================== + + ;*================================================================== + ;* Scan convert an edge. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;*================================================================== +??scan_convert: + cmp ebx,edx + je ??exit ; if (ya == yb) don't scan convert + jl short ??no_swap ; if (ya < yb) swap vertices + xchg eax,ecx + xchg ebx,edx + +??no_swap: + sub edx,ebx ; edx = (yb - ya) + sub ebx,[scanline_min] + sal ebx,2 + add ebx,[span_buff] ; ebx = span_buff + 4(ya - clip_min_y) + sub ecx,eax ; ecx = (xb - xa) + je ??v_scan ; if the edge is vertical use a + ; special case routine + push eax + mov eax,ecx ; eax = (xb - xa) + mov ecx,edx ; ecx = (yb - ya) + sal edx,1 + mov [realignment],edx ; realignment = 2(yb - ya) + cwd + idiv cx + cwde + movsx edx,dx + mov edi,eax ; edi = (xb - xa) / (yb - ya) + mov esi,edx + mov edx,ecx + pop eax ; eax = xa + neg edx ; edx = -(yb - ya) + sal esi,1 ; esi = 2[(xb - xa) % (yb - ya)] + jns short ??r_scan ; scan to the left or right? + neg esi + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the left. + ;* eax = xpos, ebx = span to reference + ;*================================================================== + cmp ebx,[span_buff] + jg ??l_scan_convert + +??l_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??l_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??l_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??l_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??l_scan_convert_loop + dec eax + sub edx,[realignment] + jmp ??l_scan_convert_loop + + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the right. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??r_scan: + cmp ebx,[span_buff] + jg ??r_scan_convert + +??r_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??r_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??r_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??r_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??r_scan_convert_loop + inc eax + sub edx,[realignment] + jmp ??r_scan_convert_loop + + + ;*================================================================== + ;* Scan convert a vertical edge. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??v_scan: + cmp ebx,[span_buff] + jg ??v_scan_convert + +??v_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??v_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??v_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??v_scan_convert: + add ebx,4 + dec edx + jge ??v_scan_convert_loop + +??exit: + retn + + ENDP Buffer_Fill_Quad + +END diff --git a/WIN32LIB/DRAWBUFF/FILLRECT.ASM b/WIN32LIB/DRAWBUFF/FILLRECT.ASM new file mode 100644 index 0000000..5c5bf5f --- /dev/null +++ b/WIN32LIB/DRAWBUFF/FILLRECT.ASM @@ -0,0 +1,275 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Fill_Rect -- draws a filled rectangle to a graphics buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Fill_Rect C near + USES eax,ebx,ecx,edx,esi,edi,ebp + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a member function + ARG x1_pixel:WORD + ARG y1_pixel:WORD + ARG x2_pixel:WORD + ARG y2_pixel:WORD + ARG color:BYTE ; what color should we clear to + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL VPwidth:DWORD ; the width of the viewport + LOCAL VPheight:DWORD ; the height of the viewport + LOCAL VPxadd:DWORD ; the additional x offset of viewport + LOCAL VPbpr:DWORD ; the number of bytes per row of viewport + + ;*=================================================================== + ;* save off the viewport characteristics on the stack + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width from viewport + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; get height from viewport + mov edx,[(GraphicViewPort ebx).GVPXAdd] ; get xadd from viewport + add edx,[(GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + mov [VPwidth],eax ; store the width of locally + mov [VPheight],ecx + mov [VPxadd],edx + add eax,edx + mov [VPbpr],eax + + ;*=================================================================== + ;* move the important parameters into local registers + ;*=================================================================== + movsx eax,[x1_pixel] + movsx ebx,[y1_pixel] + movsx ecx,[x2_pixel] + movsx edx,[y2_pixel] + + ;*=================================================================== + ;* Convert the x2 and y2 pixel to a width and height + ;*=================================================================== + cmp eax,ecx + jl ??no_swap_x + xchg eax,ecx + +??no_swap_x: + sub ecx,eax + cmp ebx,edx + jl ??no_swap_y + xchg ebx,edx +??no_swap_y: + sub edx,ebx + inc ecx + inc edx + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + cmp eax, [VPwidth] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sx_done ; if it's not negative, it's ok + + ;------ Clip source X to left edge of screen. + add ecx, eax ; Reduce width (add in negative src X). + xor eax, eax ; Clip to left of screen. +??sx_done: + + ;*=================================================================== + ;* Bounds check source Y. + ;*=================================================================== + cmp ebx, [VPheight] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sy_done ; if it's not negative, it's ok + + ;------ Clip source Y to top edge of screen. + add edx, ebx ; Reduce height (add in negative src Y). + xor ebx, ebx ; Clip to top of screen. + +??sy_done: + ;*=================================================================== + ;* Bounds check width versus width of source and dest view ports + ;*=================================================================== + push ebx ; save off ebx for later use + mov ebx,[VPwidth] ; get the source width + sub ebx, eax ; Maximum allowed pixel width (given coordinates). + sub ebx, ecx ; Pixel width undershoot. + jns short ??width_ok ; if not signed no adjustment necessary + add ecx, ebx ; Reduce width to screen limits. + +??width_ok: + pop ebx ; restore ebx to old value + + ;*=================================================================== + ;* Bounds check height versus height of source view port + ;*=================================================================== + push eax ; save of eax for later use + mov eax, [VPheight] ; get the source height + sub eax, ebx ; Maximum allowed pixel height (given coordinates). + sub eax, edx ; Pixel height undershoot. + jns short ??height_ok ; if not signed no adjustment necessary + add edx, eax ; Reduce height to screen limits. +??height_ok: + pop eax ; restore eax to old value + + ;*=================================================================== + ;* Perform the last minute checks on the width and height + ;*=================================================================== + or ecx,ecx + jz ??out + + or edx,edx + jz ??out + + cmp ecx,[VPwidth] + ja ??out + cmp edx,[VPheight] + ja ??out + + ;*=================================================================== + ;* Get the offset into the virtual viewport. + ;*=================================================================== + xchg edi,eax ; save off the contents of eax + xchg esi,edx ; and edx for size test + mov eax,ebx ; move the y pixel into eax + mul [VPbpr] ; multiply by bytes per row + add edi,eax ; add the result into the x position + mov ebx,[this_object] + add edi,[(GraphicViewPort ebx).GVPOffset] + + mov edx,esi ; restore edx back to real value + mov eax,ecx ; store total width in ecx + sub eax,[VPwidth] ; modify xadd value to include clipped + sub [VPxadd],eax ; width bytes (subtract a negative number) + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ebx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,bx + + ;*=================================================================== + ; If there is no row offset then adjust the width to be the size of + ; the entire viewport and adjust the height to be 1 + ;*=================================================================== + mov esi,[VPxadd] + or esi,esi ; set the flags for esi + jnz ??row_by_row_aligned ; and act on them + + xchg eax,ecx ; switch bit pattern and width + mul edx ; multiply by edx to get size + xchg eax,ecx ; switch size and bit pattern + mov edx,1 ; only 1 line off view port size to do + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== +??row_by_row_aligned: + mov ebp,ecx ; width saved in ebp + cmp ecx,OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??row_by_row ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + mov ebx,edi ; get output position + and ebx,3 ; is there a remainder? + jz ??aligned_loop ; if not we are aligned + xor ebx,3 ; find number of align bytes + inc ebx ; this number is off by one + sub ebp,ebx ; subtract from width + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== +??aligned_loop: + mov ecx,ebx ; get number of bytes to align + rep stosb ; and move them over + mov ecx,ebp ; get number of aligned bytes + shr ecx,2 ; convert to DWORDS + rep stosd ; and move them over + mov ecx,ebp ; get number of aligned bytes + and ecx,3 ; find the remainder + rep stosb ; and move it over + add edi,esi ; fix the line offset + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + jmp ??exit ; we are all done + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??row_by_row: + mov ecx,ebp ; get total width in bytes + rep stosb ; store the width + add edi,esi ; handle the xadd + dec edx ; decrement the height + jnz ??row_by_row ; if any left then next line +??out: +??exit: + ret + ENDP Buffer_Fill_Rect + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/FTPUTPIX.ASM b/WIN32LIB/DRAWBUFF/FTPUTPIX.ASM new file mode 100644 index 0000000..d2c7cd9 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/FTPUTPIX.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Buffer_Get_Pixel -- get the colour of a pixel at given coords * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Buffer_Get_Pixel + +END diff --git a/WIN32LIB/DRAWBUFF/GBUFFER.CPP b/WIN32LIB/DRAWBUFF/GBUFFER.CPP new file mode 100644 index 0000000..56f3b8f --- /dev/null +++ b/WIN32LIB/DRAWBUFF/GBUFFER.CPP @@ -0,0 +1,711 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * GBC::DD_Init -- Inits a direct draw surface for a GBC * + * GBC::Init -- Core function responsible for initing a GBC * + * GBC::Lock -- Locks a Direct Draw Surface * + * GBC::Unlock -- Unlocks a direct draw surface * + * GBC::GraphicBufferClass -- Default constructor (requires explicit init)* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif +#pragma inline + +int TotalLocks; +BOOL AllowHardwareBlitFills = TRUE; + + +//int CacheAllowed; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) : + LockCount(0), + GraphicBuff(NULL) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ + Offset = 0; + Width = 0; // Record width of Buffer + Height = 0; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + Pitch = 0; // Record width of Buffer + IsDirectDraw = FALSE; + LockCount = 0; + GraphicBuff = NULL; +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not attach a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return; + } + + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + ((gbuffer->Get_Width()+gbuffer->Get_Pitch()) * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + Pitch = gbuffer->Get_Pitch(); + GraphicBuff = gbuffer; + IsDirectDraw= gbuffer->IsDirectDraw; +} + + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + + +/*************************************************************************** + * GBC::DD_INIT -- Inits a direct draw surface for a GBC * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::DD_Init(GBC_Enum flags) +{ + // + // Create the direct draw surface description + // + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.dwFlags = DDSD_CAPS; + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + + if (!(flags & GBC_VISIBLE)) { + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags |= DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = Height; + VideoSurfaceDescription.dwWidth = Width; + } + + // + // Need to set the DDSCAPS_MODEX flag if we want a 320 wide mode + // + if ( Width == 320 ) { + VideoSurfaceDescription.ddsCaps.dwCaps |= DDSCAPS_MODEX; + } + + // + // Call CreateSurface + // + DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &VideoSurfacePtr , NULL); + AllSurfaces.Add_DD_Surface (VideoSurfacePtr); + + if ( GBC_VISIBLE & flags ){ + PaletteSurface=VideoSurfacePtr; + } + + Allocated = FALSE; // even if system alloced, dont flag it cuz + // we dont want it freed. + IsDirectDraw = TRUE; // flag it as a video surface + Offset = NOT_LOCKED; // flag it as unavailable for reading or writing + LockCount = 0; // surface is not locked +} + + +void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer) +{ + VideoSurfacePtr->AddAttachedSurface (attach_buffer->Get_DD_Surface()); +} + + +/*************************************************************************** + * GBC::INIT -- Core function responsible for initing a GBC * + * * + * INPUT: int - the width in pixels of the GraphicBufferClass * + * int - the heigh in pixels of the GraphicBufferClass * + * void * - pointer to user supplied buffer (system will * + * allocate space if buffer is NULL) * + * long - size of the user provided buffer * + * GBC_Enum - flags if this is defined as a direct draw * + * surface * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::Init(int w, int h, void *buffer, long size, GBC_Enum flags) +{ + Size = size; // find size of physical buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + + // + // If the surface we are creating is a direct draw object then + // we need to do a direct draw init. Otherwise we will do + // a normal alloc. + // + if (flags & (GBC_VIDEOMEM | GBC_VISIBLE)) { + DD_Init(flags); + } else { + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + if (!Size) Size = w*h; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + Offset = (long)Buffer; // Get offset to the buffer + IsDirectDraw = FALSE; + } + + Pitch = 0; // Record width of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} + + +/*********************************************************************************************** + * GBC::Un_Init -- releases the video surface belonging to this gbuffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:44PM ST : Created * + *=============================================================================================*/ + +void GraphicBufferClass::Un_Init (void) +{ + if ( IsDirectDraw ){ + + if ( VideoSurfacePtr ){ + + while ( LockCount ){ + + if (VideoSurfacePtr->Unlock ( NULL ) == DDERR_SURFACELOST){ + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + } + } + + AllSurfaces.Remove_DD_Surface (VideoSurfacePtr); + VideoSurfacePtr->Release(); + VideoSurfacePtr = NULL; + } + } +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Default constructor (requires explicit init) * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(void) +{ + GraphicBuff = this; // Get a pointer to our self + VideoSurfacePtr = NULL; + memset(&VideoSurfaceDescription, 0, sizeof(DDSURFACEDESC)); +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer, long size) +{ + Init(w, h, buffer, size, GBC_NONE); +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Init(w, h, buffer, w * h, GBC_NONE); +} + +/*====================================================================================* + * GBC::GRAPHICBUFFERCLASS -- contructor for GraphicsBufferClass with special flags * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - unused * + * unsigned flags - flags for creation of special buffer types * + * GBC_VISIBLE - buffer is a visible screen surface * + * GBC_VIDEOMEM - buffer resides in video memory * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09-21-95 04:19pm ST : Created * + *====================================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, GBC_Enum flags) +{ + Init(w, h, NULL, w * h, flags); +} + +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ + +// +// Release the direct draw surface if it exists +// + Un_Init(); +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} + + +/*************************************************************************** + * GBC::LOCK -- Locks a Direct Draw Surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ +extern void Colour_Debug (int call_number); +extern BOOL GameInFocus; + +extern void Block_Mouse(GraphicBufferClass *buffer); +extern void Unblock_Mouse(GraphicBufferClass *buffer); + +BOOL GraphicBufferClass::Lock(void) +{ + HRESULT result; + int restore_attempts=0; + + // + // If its not a direct draw surface then the lock is always sucessful. + // + if (!IsDirectDraw) return(TRUE); + + /* + ** If the video surface pointer is null then return + */ + if (!VideoSurfacePtr) return (FALSE); + + /* + ** If we dont have focus then return failure + */ + if (!GameInFocus) return (FALSE); + + + Block_Mouse(this); + + + // + // If surface is already locked then inc the lock count and return true + // + if (LockCount){ + LockCount++; + Unblock_Mouse(this); + return(TRUE); + } + + // + // If it isn't locked at all then we will have to request that Direct + // Draw actually lock the surface. + // + + if (VideoSurfacePtr){ + while (!LockCount && restore_attempts<2) { + result = VideoSurfacePtr->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL); + + switch (result){ + case DD_OK : + Offset = (unsigned long)VideoSurfaceDescription.lpSurface; + Pitch = VideoSurfaceDescription.lPitch; + Pitch -= Width; + LockCount++; // increment count so we can track if + TotalLocks++; // Total number of times we have locked (for debugging) + //Colour_Debug (1); + Unblock_Mouse(this); + return (TRUE); // we locked it multiple times. + + case DDERR_SURFACELOST : + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + restore_attempts++; + break; + + default : + Unblock_Mouse(this); + return (FALSE); + } + } + } + //Colour_Debug(1); + Unblock_Mouse(this); + return (FALSE); //Return false because we couldnt lock or restore the surface +} + +/*************************************************************************** + * GBC::UNLOCK -- Unlocks a direct draw surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ + + +BOOL GraphicBufferClass::Unlock(void) +{ + // + // If there is no lock count or this is not a direct draw surface + // then just return true as there is no harm done. + // + if (!(LockCount && IsDirectDraw)) { + return(TRUE); + } + + // + // If lock count is directly equal to one then we actually need to + // unlock so just give it a shot. + // + if (LockCount == 1 && VideoSurfacePtr) { + Block_Mouse(this); + if ( VideoSurfacePtr->Unlock ( NULL ) != DD_OK ){ + Unblock_Mouse(this); + return(FALSE); + } else { + Offset=NOT_LOCKED; + LockCount--; + Unblock_Mouse(this); + return(TRUE); + } + } + //Colour_Debug (0); + LockCount--; + return(TRUE); +} + + +/*********************************************************************************************** + * GVPC::DD_Linear_Blit_To_Linear -- blit using the hardware blitter * + * * + * * + * * + * INPUT: destination vvpc * + * x coord to blit from * + * y coord to blit from * + * x coord to blit to * + * y coord to blit to * + * width to blit * + * height to blit * + * * + * OUTPUT: DD_OK if successful * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-22-95 11:05am ST : Created * + *=============================================================================================*/ + +HRESULT GraphicViewPortClass::DD_Linear_Blit_To_Linear ( + GraphicViewPortClass &dest + , int source_x + , int source_y + , int dest_x + , int dest_y + , int width + , int height + , BOOL mask ) + +{ + RECT source_rectangle; + RECT dest_rectangle; + int key_source=0; + + if ( mask ){ + key_source=DDBLT_KEYSRC; + } + + + source_rectangle.left = source_x; + source_rectangle.top = source_y; + source_rectangle.right = source_x+width; + source_rectangle.bottom = source_y+height; + + dest_rectangle.left = dest_x; + dest_rectangle.top = dest_y; + dest_rectangle.right = dest_x+width; + dest_rectangle.bottom = dest_y+height; + + return ( dest.GraphicBuff->Get_DD_Surface()->Blt ( &dest_rectangle, + GraphicBuff->Get_DD_Surface(), + &source_rectangle, + key_source | DDBLT_WAIT | DDBLT_ASYNC, + NULL ) ); +} + + + + diff --git a/WIN32LIB/DRAWBUFF/GBUFFER.H b/WIN32LIB/DRAWBUFF/GBUFFER.H new file mode 100644 index 0000000..1dc2926 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/GBUFFER.H @@ -0,0 +1,1366 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef DRAWBUFF_H +#include "drawbuff.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#ifndef WINDOWS_H +#include "ww_win.h" +#endif +#include + +#include "iconcach.h" + + + +#ifndef FUNCTION_H + +#pragma off (unreferenced) + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +#pragma on (unreferenced) +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// Defines for direct draw +// +// +extern LPDIRECTDRAW DirectDrawObject; //pointer to direct draw object +extern HWND MainWindow; //handle to programs main window + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Gbuffer_Focus_Loss_Function)(void); + +enum GBC_Enum { + GBC_NONE = 0, + GBC_VIDEOMEM = 1, + GBC_VISIBLE = 2, +}; + +#define NOT_LOCKED NULL + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr); +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + int Get_Pitch(void); + inline BOOL Get_IsDirectDraw(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + HRESULT Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + +// This doesnt seem to exist anywhere?? - Steve T 9/26/95 6:05PM +// VOID Grey_Out_Region(int x, int y, int width, int height, int color); + + // + // New members to lock and unlock the direct draw video memory + // + inline BOOL Lock (); + inline BOOL Unlock(); + inline int Get_LockCount(); + + // Member to blit using direct draw access to hardware blitter + HRESULT DD_Linear_Blit_To_Linear ( GraphicViewPortClass &dest, int source_x, int source_y, int dest_x, int dest_y, int width , int height, BOOL mask ); + + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + protected: + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + long Pitch; //Distance from one line to the next + GraphicBufferClass *GraphicBuff; // related graphic buff + BOOL IsDirectDraw; //Flag to let us know if it is a direct draw surface + int LockCount; // Count for stacking locks if non-zero the buffer +}; // is a locked DD surface + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/* long Pitch - modulo of buffer for reading and writing */ +/* BOOL IsDirectDraw - flag if its a direct draw surface */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + + public: + GraphicBufferClass(int w, int h, GBC_Enum flags); + GraphicBufferClass(int w, int h, void *buffer, long size); + GraphicBufferClass(int w, int h, void *buffer = 0); + GraphicBufferClass(void); + ~GraphicBufferClass(); + + void DD_Init(GBC_Enum flags); + void Init(int w, int h, void *buffer, long size, GBC_Enum flags); + void Un_Init(void); + void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer); + BOOL Lock(void); + BOOL Unlock(void); + + void Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle); + + // Member to get a pointer to a direct draw surface + LPDIRECTDRAWSURFACE Get_DD_Surface ( void ); + + protected: + LPDIRECTDRAWSURFACE VideoSurfacePtr; //Pointer to the related direct draw surface + DDSURFACEDESC VideoSurfaceDescription;//Description of the said surface + +}; + + + +inline int GraphicViewPortClass::Get_LockCount(void) +{ + return (LockCount); +} + + + +/*********************************************************************************************** + * GVPC::Get_IsDirectDraw -- provide read access to the IsDirectDraw flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsDirectDraw * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 1:02PM ST : Created * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Get_IsDirectDraw(void) +{ + return (IsDirectDraw); +} + + + +/*********************************************************************************************** + * GBC::Get_DD_Surface -- returns a pointer to the buffer direct draw surface * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to direct draw surface * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/29/95 9:43AM ST : Created * + *=============================================================================================*/ +inline LPDIRECTDRAWSURFACE GraphicBufferClass::Get_DD_Surface ( void ) +{ + return ( VideoSurfacePtr ); + +} + + + +/*********************************************************************************************** + * GVPC::Lock -- lock the graphics buffer for reading or writing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully locked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 12:33pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Lock(void) +{ + BOOL lock = GraphicBuff->Lock(); + if ( !lock ) return(FALSE); + + if (this != GraphicBuff) { + Attach(GraphicBuff, XPos, YPos, Width, Height); + } + return(TRUE); +} + +/*********************************************************************************************** + * GVPC::Unlock -- unlock the video buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully unlocked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 02:20pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Unlock(void) +{ + BOOL unlock = GraphicBuff->Unlock(); + if (!unlock) return(FALSE); + if (this != GraphicBuff && IsDirectDraw && !GraphicBuff->LockCount) { + Offset = 0; + } + return(TRUE); +} + + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return Buffer_Size_Of_Region(this, w, h); +} + + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + + if (Lock()){ + Buffer_Put_Pixel(this, x, y, color); + } + Unlock(); + + +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + int return_code=0; + + if (Lock()){ + return_code=(Buffer_Get_Pixel(this, x, y)); + } + Unlock(); + return(return_code); + +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + if (Lock()){ + Buffer_Clear(this, color); + } + Unlock(); + +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff, size)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + HRESULT return_code=0; + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos+x_pixel, YPos+y_pixel + , dest.Get_XPos()+dx_pixel, dest.Get_YPos()+dy_pixel + , pixel_width, pixel_height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel + , dx_pixel, dy_pixel + , pixel_width, pixel_height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos()+dx, dest.Get_YPos()+dy + , Width, Height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , dx, dy + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos(), dest.Get_YPos() + , MAX( Width, dest.Get_Width()) + , MAX( Height, dest.Get_Height()) + , trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, str, x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + if (Lock()){ + Buffer_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); + } + Unlock(); +} + + + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +extern BOOL IconCacheAllowed; +inline void GraphicViewPortClass::Draw_Stamp(void const * icondata, int icon, int x_pixel, int y_pixel, void const * remap, int clip_window) +{ + int cache_index=-1; + + int drewit = 0; + if (IconCacheAllowed){ + if (IsDirectDraw){ + if (!remap){ + cache_index = Is_Icon_Cached(icondata,icon); + } + + if (cache_index != -1){ + if (CachedIcons[cache_index].Get_Is_Cached() ){ + CachedIcons[cache_index].Draw_It (GraphicBuff->Get_DD_Surface() , x_pixel, y_pixel, + WindowList[clip_window][WINDOWX] + XPos, + WindowList[clip_window][WINDOWY] +YPos, + WindowList[clip_window][WINDOWWIDTH], + WindowList[clip_window][WINDOWHEIGHT]); + CachedIconsDrawn++; + drewit = 1; + } + } + } + } + + + if (drewit == 0) { + if (Lock()){ + UnCachedIconsDrawn++; + Buffer_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); + } + } + Unlock(); +} + + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + if (Lock()){ + Buffer_Draw_Line(this, sx, sy, dx, dy, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + if ( AllowHardwareBlitFills + && IsDirectDraw + && ( (dx-sx) * (dy-sy) >= (32*32) ) + && GraphicBuff->Get_DD_Surface()->GetBltStatus(DDGBS_CANBLT) == DD_OK){ + DDBLTFX blit_effects; + RECT dest_rectangle; + + dest_rectangle.left =sx+XPos; + dest_rectangle.top =sy+YPos; + dest_rectangle.right =dx+XPos; + dest_rectangle.bottom=dy+YPos; + + if (dest_rectangle.left= Width + XPos){ + dest_rectangle.right = Width +XPos -1; + } + + if (dest_rectangle.top= Height + YPos){ + dest_rectangle.bottom = Height + YPos -1; + } + + if (dest_rectangle.left >= dest_rectangle.right) return; + if (dest_rectangle.top >= dest_rectangle.bottom) return; + + dest_rectangle.right++; + dest_rectangle.bottom++; + + blit_effects.dwSize=sizeof(blit_effects); + blit_effects.dwFillColor = color; + GraphicBuff->Get_DD_Surface()->Blt(&dest_rectangle, + NULL, + NULL, + DDBLT_WAIT | DDBLT_ASYNC | DDBLT_COLORFILL, + &blit_effects); + } else { + if (Lock()){ + Buffer_Fill_Rect(this, sx, sy, dx, dy, color); + Unlock(); + } + } +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, sx, sy, width, height, remap); + } + Unlock(); +} + + +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + if (Lock()){ + Buffer_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, 0, 0, Width, Height, remap); + } + Unlock(); +} + +inline int GraphicViewPortClass::Get_Pitch(void) +{ + return(Pitch); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + + +#endif diff --git a/WIN32LIB/DRAWBUFF/GBUFFER.INC b/WIN32LIB/DRAWBUFF/GBUFFER.INC new file mode 100644 index 0000000..4da79b7 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/GBUFFER.INC @@ -0,0 +1,51 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/GETCLIP.ASM b/WIN32LIB/DRAWBUFF/GETCLIP.ASM new file mode 100644 index 0000000..d7e9eed --- /dev/null +++ b/WIN32LIB/DRAWBUFF/GETCLIP.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE ".\drawbuff.inc" +INCLUDE "gbuffer.inc" + + +; typedef struct { +; int x0 , y0 ; +; int x1 , y1 ; +; } CLIP_WIN ; +; Note for efficiency reasons x1 must be >= x0 and y1 >= y0 +; int get_clip ( CLIP_WIN * window , CLIP_WIN * sorce_rect ) ; + +CODESEG + + PROC get_clip C near + USES eax , ebx + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG win : dword + ARG rect : dword + + + mov edi , [ rect ] + mov esi , [ win ] + xor eax , eax + xor edx , edx + + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x0 ] + sub ebx , [ (RECTANGLE esi) . x0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . x1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x1 ] + sub ebx , [ (RECTANGLE esi) . x1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y0 ] + sub ebx , [ (RECTANGLE esi) . y0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . y1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y1 ] + sub ebx , [ (RECTANGLE esi) . y1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , dl + ret + ENDP get_clip + + + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/GETPIX.ASM b/WIN32LIB/DRAWBUFF/GETPIX.ASM new file mode 100644 index 0000000..89a16bc --- /dev/null +++ b/WIN32LIB/DRAWBUFF/GETPIX.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Buffer_Get_Pixel -- get the colour of a pixel at given coords * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Buffer_Get_Pixel + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/ICONCACH.CPP b/WIN32LIB/DRAWBUFF/ICONCACH.CPP new file mode 100644 index 0000000..71ff4cc --- /dev/null +++ b/WIN32LIB/DRAWBUFF/ICONCACH.CPP @@ -0,0 +1,609 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 13th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains members of the IconCacheClass and associated non member * + * functions. All functions are to do with caching individual icons from icon sets * + * into video memory to improve the speed of subsequent drawing * + * * + * Functions: * + * Cache_New_Icon -- Call the Cache_It member to cache a registered icon to video memory * + * Invalidate_Cached_Icons -- Uncache all the icons * + * Restore_Cached_Icons -- restore cached icons after a focus loss * + * Register_Icon_Set -- register an icon set as cachable * + * Get_Free_Cache_Slot -- find an empty cache slot * + * IconCacheClass::IconCacheClass -- IconCacheClass constructor * + * IconCacheClass::~IconCacheClass -- IconCacheClass destructor * + * IconCacheClass::Restore -- restore the icons surface and recache it * + * IconCacheClass::Cache_It -- cache an icon into video memory * + * IconCacheClass::Uncache_It -- restore the video memory used by a cached icon * + * IconCacheClass::Draw_It -- use the blitter to draw the cached icon * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32_LEAN_AND_MEAN +#define _WIN32 + +#include +#include "ddraw.h" +#include "misc.h" +#include "iconcach.h" +#include "gbuffer.h" + + +static DDSURFACEDESC VideoSurfaceDescription; + +IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern "C"{ +IconSetType IconSetList[MAX_ICON_SETS]; +short IconCacheLookup[MAX_LOOKUP_ENTRIES]; +} + +int CachedIconsDrawn=0; //Counter of number of cache hits +int UnCachedIconsDrawn=0; //Counter of number of cache misses +BOOL CacheMemoryExhausted; //Flag set if we have run out of video RAM + + + + +/*********************************************************************************************** + * Optimise_Video_Memory_Cache -- optimises usage of video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if memory was freed up * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:47PM ST : Created * + *=============================================================================================*/ +BOOL Optimize_Video_Memory_Cache (void) +{ + + if (CacheMemoryExhausted && + (UnCachedIconsDrawn+CachedIconsDrawn > 1000) && + UnCachedIconsDrawn > CachedIconsDrawn){ + + int cache_misses[MAX_CACHED_ICONS]; + int cache_hits[MAX_CACHED_ICONS]; + int total_cache_misses=0; + int total_cache_hits=0; + int counter; + int i; + int j; + int temp; + BOOL swapped; + + /* + ** make list of icons that have failed to cache more than 5 times + */ + for (counter=0 ; counter5){ + cache_misses[total_cache_misses++] = counter; + } + } + + /* + ** Make list of icons that have been drawn less than 3 times + */ + for (counter=0 ; counter 1){ + for (i = 0 ; i CachedIcons[cache_hits[j+1]].TimesDrawn){ + temp=cache_hits[j]; + cache_hits[j]=cache_hits[j+1]; + cache_hits[j+1]=temp; + swapped = TRUE; + } + } + if (!swapped) break; + } + } + + + /* + ** Uncache icons up to the number of failed icons + */ + + for (counter=0 ; counterCount)*2; + if (IconSetList[i].IconListOffset > MAX_LOOKUP_ENTRIES*2){ + IconSetList[i].IconSetPtr = NULL; + } + } else { + IconSetList[i].IconListOffset = 0; + } + + if (pre_cache){ + for (i=0 ; i<256 ; i++){ + Is_Icon_Cached(icon_data,i); + } + } + return; + } + } +} + + + +/*********************************************************************************************** + * Get_Free_Cache_Slot -- find a free slot in which to cache an icon * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: int - icon index * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:40AM ST : Created * + *=============================================================================================*/ +int Get_Free_Cache_Slot (void) +{ + for (int i=0 ; iRelease(); + } +} + + + + +/*********************************************************************************************** + * ICC::Restore -- Restores the icons video surface memory and reloads it based on the original* + * icon pointer * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Relies on the icons original pointer still being valie * + * * + * HISTORY: * + * 11/13/95 9:43AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Restore (void) +{ + if (IsCached && CacheSurface){ + CacheSurface->Restore(); + if (IconSource){ + Cache_It(IconSource); + } + } +} + + +/*********************************************************************************************** + * ICC::Cache_It -- allocate video memory and copy an icon to it * + * * + * * + * * + * INPUT: icon_ptr -- ptr to icon data * + * * + * OUTPUT: bool -- success? * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:44AM ST : Created * + *=============================================================================================*/ +BOOL IconCacheClass::Cache_It (void *icon_ptr) +{ + DDSCAPS surface_capabilities; + BOOL return_value; + + /* + ** If we dont have a direct draw interface yet then just fail + */ + if (!DirectDrawObject) return(FALSE); + + /* + ** Set up the description of the surface we want to create + */ + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = ICON_WIDTH; + VideoSurfaceDescription.dwWidth = ICON_HEIGHT; + + /* + ** If this cache object doesnt already have a surface then create one + */ + if (!CacheSurface){ + if (DD_OK!=DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &CacheSurface , NULL)){ + CacheMemoryExhausted = TRUE; + return(FALSE); + } + + } + + /* + ** Make sure the surface we created isnt really in system memory + */ + if (DD_OK != CacheSurface->GetCaps(&surface_capabilities)){ + return(FALSE); + } + + if ((DDSCAPS_SYSTEMMEMORY & surface_capabilities.dwCaps) == DDSCAPS_SYSTEMMEMORY){ + CacheSurface->Release(); + return(FALSE); + } + + return_value=FALSE; + /* + ** Lock the surface so we can copy the icon to it + */ + if (DD_OK== CacheSurface->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL)){ + /* + ** Copy the icon to the surface and flag that icon is cached + */ + Cache_Copy_Icon (icon_ptr , VideoSurfaceDescription.lpSurface , VideoSurfaceDescription.lPitch); + IsCached=TRUE; + SurfaceLost=FALSE; + IconSource=icon_ptr; + return_value=TRUE; + } + CacheSurface->Unlock(NULL); + return (return_value); +} + + +/*********************************************************************************************** + * ICC::Uncache_It -- release the video memory used to cache an icon * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:48AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Uncache_It(void) +{ + + if (IsCached && CacheSurface){ + CacheSurface->Release(); + IsCached=FALSE; + CacheSurface=NULL; + IconSource=NULL; + CacheMemoryExhausted=FALSE; + } +} + + + +/*********************************************************************************************** + * ICC::Draw_It -- use the blitter to draw a cached icon * + * * + * * + * * + * INPUT: surface to draw to * + * x coord to draw to (relative to window) * + * y coord to draw to (relative to window) * + * window left coord * + * window top coord * + * window width * + * window height * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:48AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height) +{ + RECT source_rectangle; + RECT dest_rectangle; + int clip; + HRESULT return_code; + + /* + ** Set up the source and destination coordinates as required by direct draw + */ + source_rectangle.left = 0; + source_rectangle.top = 0; + source_rectangle.right = ICON_WIDTH; + source_rectangle.bottom = ICON_HEIGHT; + + dest_rectangle.left = window_left+x_pixel; + dest_rectangle.top = window_top+y_pixel; + dest_rectangle.right = dest_rectangle.left+ICON_WIDTH; + dest_rectangle.bottom = dest_rectangle.top+ICON_HEIGHT; + + /* + ** Clip the coordinates to the window + */ + if (dest_rectangle.left=window_left+window_width){ + clip = dest_rectangle.right-(window_left+window_width); + source_rectangle.right -= clip; + dest_rectangle.right -= clip; + } + + if (dest_rectangle.top=window_top+window_height){ + clip = dest_rectangle.bottom-(window_top+window_height); + source_rectangle.bottom -= clip; + dest_rectangle.bottom -= clip; + } + + if (source_rectangle.left>=source_rectangle.right){ + return; + } + + if (source_rectangle.top>=source_rectangle.bottom){ + return; + } + + /* + ** Do the blit + */ + return_code = dest_surface->Blt (&dest_rectangle , + CacheSurface , + &source_rectangle , + DDBLT_WAIT | + DDBLT_ASYNC , + NULL); + + if (return_code == DDERR_SURFACELOST && Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + + if ( return_code != DDERR_SURFACELOST && return_code != DD_OK ) { + char temp[100]; + sprintf(temp,"DD Error code %d\n", return_code & 0xFFFF); + OutputDebugString(temp); + } + + TimesDrawn++; + +} + diff --git a/WIN32LIB/DRAWBUFF/ICONCACH.H b/WIN32LIB/DRAWBUFF/ICONCACH.H new file mode 100644 index 0000000..5201908 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/ICONCACH.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 16th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains definition of the IconCacheClass and associated non member * + * function prototypes. * + * * + * Functions: * + * IconCacheClass::Get_Is_Cached -- member to allow access to private IsCached flag * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#ifndef ICONCACH_H +#define ICONCACH_H + +#include + +#define ICON_WIDTH 24 // Icons must be this width to be cached +#define ICON_HEIGHT 24 // Icons must be this height to be cached +#define MAX_CACHED_ICONS 500 // Maximum number of icons that can be cached +#define MAX_ICON_SETS 100 // Maximum number of icon sets that can be registered +#define MAX_LOOKUP_ENTRIES 3000 // Size of icon index table + + +/* +** IconCacheClass for tracking individual icons cached into video memory +** +** Use Register_Icon_Set to identify a set of icons as cachable. Once registered, the icons +** will be cached automatically when drawn. +** Use Invalidate_Cached_Icons at the end of icon drawing to release the video memory used by the +** caching system. +** Restore_Cached_Icons may be used to reload the icons into video memory after a focus loss. +** +*/ + +class IconCacheClass { + + public: + + IconCacheClass (void); // class constructor + ~IconCacheClass (void); // class destructor + + void Restore(void); // restore the surface + BOOL Cache_It (void * icon_ptr); // Cache the icon to video memory + void Uncache_It (void); // Restore the video memory and flag the icon as uncached + void Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height); + inline BOOL Get_Is_Cached(void); // Return the IsCached member + + int TimesDrawn; // counter of times cached icon has been drawn + int TimesFailed; // counter of times cached icon has failed to draw + + + private: + + LPDIRECTDRAWSURFACE CacheSurface; // Ptr to direct draw surface where icon resides + BOOL IsCached; // Flag to say whether an icon is cached + BOOL SurfaceLost; // Flag to indicate that our icons surface has been lost + int DrawFrequency; // Number of times icon has been drawn + void *IconSource; // Ptr to original icon data in system memory + +}; + + + +/* +** Structure to keep track of registered icon sets +** +*/ + +typedef struct tIconSetType{ + IControl_Type *IconSetPtr; // Ptr to icon set data + int IconListOffset; // Offset into icon index table for this icon set +}IconSetType; + + +extern IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern void Invalidate_Cached_Icons (void); +extern void Restore_Cached_Icons (void); +extern void Register_Icon_Set (void *icon_data , BOOL pre_cache); + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void Clear_Icon_Pointers (void); +extern "C" void Cache_Copy_Icon (void const *icon_ptr ,void * , int); +extern "C" int Is_Icon_Cached (void const *icon_data , int icon); +extern "C" int Get_Icon_Index (void *icon_ptr); +extern "C" int Get_Free_Index (void); +extern "C" BOOL Cache_New_Icon (int icon_index, void *icon_ptr); +extern "C" int Get_Free_Cache_Slot(void); + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/*********************************************************************************************** + * ICC::Get_Is_Cached -- member to allow access to the private IsCached flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsCached * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:42AM ST : Created * + *=============================================================================================*/ +inline BOOL IconCacheClass::Get_Is_Cached (void) +{ + return (IsCached); +} + + + + + +#endif //ICONCACH_H + diff --git a/WIN32LIB/DRAWBUFF/MAKEFILE b/WIN32LIB/DRAWBUFF/MAKEFILE new file mode 100644 index 0000000..c060071 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/MAKEFILE @@ -0,0 +1,203 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = drawbuff +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + bitblit.obj & + buffer.obj & + buffglbl.obj & + clear.obj & + drawline.obj & + drawrect.obj & + fillquad.obj & + fillrect.obj & + gbuffer.obj & + getclip.obj & + getpix.obj & + putpix.obj & + regionsz.obj & + remap.obj & + scale.obj & + stamp.obj & + szregion.obj & + tobuff.obj & + topage.obj & + txtprnt.obj & + iconcach.obj & + stmpcach.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/DRAWBUFF/MAKEFILE.BOR b/WIN32LIB/DRAWBUFF/MAKEFILE.BOR new file mode 100644 index 0000000..57080e9 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/MAKEFILE.BOR @@ -0,0 +1,206 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = drawbuff +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + bitblit.obj \ + buffer.obj \ + buffglbl.obj \ + clear.obj \ + drawline.obj \ + drawrect.obj \ + fillquad.obj \ + fillrect.obj \ + gbuffer.obj \ + getclip.obj \ + getpix.obj \ + putpix.obj \ + regionsz.obj \ + remap.obj \ + scale.obj \ + stamp.obj \ + szregion.obj \ + tobuff.obj \ + topage.obj \ + txtprnt.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+bitblit.obj & +-+buffer.obj & +-+buffglbl.obj & +-+clear.obj & +-+drawline.obj & +-+drawrect.obj & +-+fillquad.obj & +-+fillrect.obj & +-+gbuffer.obj & +-+getclip.obj & +-+getpix.obj & +-+putpix.obj & +-+regionsz.obj & +-+remap.obj & +-+scale.obj & +-+stamp.obj & +-+szregion.obj & +-+tobuff.obj & +-+topage.obj & +-+txtprnt.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/DRAWBUFF/PUTPIX.ASM b/WIN32LIB/DRAWBUFF/PUTPIX.ASM new file mode 100644 index 0000000..cd41819 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/PUTPIX.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Put_Pixel C near + USES eax,ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; add in direct draw pitch + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen +??exit: + ret + ENDP Buffer_Put_Pixel + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/REGIONSZ.CPP b/WIN32LIB/DRAWBUFF/REGIONSZ.CPP new file mode 100644 index 0000000..7dd9965 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/REGIONSZ.CPP @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB 32 * + * * + * File Name : REGIONSZ.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : November 3, 1994 * + * * + * Last Update : November 3, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Size_Of_Region -- Calculates the size of a given region * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * SIZE_OF_REGION -- Calculates the size of a given region * + * * + * INPUT: int width - the width of the region * + * int height - the height of the region * + * * + * OUTPUT: long - the size of the region * + * * + * HISTORY: * + * 11/03/1994 PWG : Created. * + *=========================================================================*/ +long Size_Of_Region(int width, int height) +{ + return(width * height); +} diff --git a/WIN32LIB/DRAWBUFF/REMAP.ASM b/WIN32LIB/DRAWBUFF/REMAP.ASM new file mode 100644 index 0000000..65c5469 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/REMAP.ASM @@ -0,0 +1,175 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword + + + cmp [ remap ] , 0 + jz ??real_out + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_remap + + test cl , 1000b + jz ??scr_left_ok + mov [ x0_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y0_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_remap + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +??do_remap: + cld + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + mov ebx , [ x1_pixel ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle ??real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle ??real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +??outer_loop: + mov ebx , [ counter_x ] +??inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz ??inner_loop + add edi , esi + dec ecx + jnz ??outer_loop + + + + +??real_out: + ret + + ENDP Buffer_Remap + + END diff --git a/WIN32LIB/DRAWBUFF/SCALE.ASM b/WIN32LIB/DRAWBUFF/SCALE.ASM new file mode 100644 index 0000000..afc7e56 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/SCALE.ASM @@ -0,0 +1,570 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : June 21, 1994 [PWG] * +;* New version : feb 12, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* Normal_Remapped_Draw -- jump loc for draw scaled line of remap pixel * +;* Transparent_Draw -- jump loc for scaled line of transparent pixels * +;* Transparent_Remapped_Draw -- jump loc for scaled remap trans pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je ??all_done + cmp [dst_height],0 + je ??all_done + cmp [src_width],0 + je ??all_done + cmp [src_height],0 + je ??all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??clip_against_dest + mov bl , dl + test cl , 1000b + jz ??src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + +??src_left_ok: + test cl , 0010b + jz ??src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + +??src_bottom_ok: + test bl , 0100b + jz ??src_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + +??src_right_ok: + test bl , 0001b + jz ??clip_against_dest + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + +; Clip destination Rectangle against source Window boundaries. +??clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??do_scaling + mov bl , dl + test cl , 1000b + jz ??dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + +??dst_left_ok: + test cl , 0010b + jz ??dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + +??dst_bottom_ok: + test bl , 0100b + jz ??dst_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + +??dst_right_ok: + test bl , 0001b + jz ??do_scaling + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + +??do_scaling: + + cld + mov ebx , [ this_object ] + mov esi , [ (GraphicViewPort ebx) . GVPOffset ] + mov eax , [ (GraphicViewPort ebx) . GVPXAdd ] + add eax , [ (GraphicViewPort ebx) . GVPWidth ] + add eax , [ (GraphicViewPort ebx) . GVPPitch ] + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ (GraphicViewPort ebx) . GVPOffset ] + mov eax , [ (GraphicViewPort ebx) . GVPXAdd ] + add eax , [ (GraphicViewPort ebx) . GVPWidth ] + add eax , [ (GraphicViewPort ebx) . GVPPitch ] + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jle ??all_done + sub ebx , [ dst_x0 ] + jle ??all_done + + mov [ counter_y ] , ecx + + cmp [ trans ] , 0 + jnz ??transparency + + cmp [ remap ] , 0 + jnz ??normal_remap + +; ************************************************************************* +; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ??ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + ??outter_loop: + push esi + push edi + xor ecx , ecx + mov ebx , [ counter_x ] + jmp [ entry ] + ??inner_loop: + REPT 32 + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + ENDM + ??ref_point: + dec ebx + jge ??inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??normal_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ ??remapref_point + ecx ] + mov [ entry ] , ecx + + ??remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??remapinner_loop: + REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + ENDM + ??remapref_point: + dec [ remap_counter ] + jge ??remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??remapoutter_loop + ret + + +;**************************************************************************** +; scale with trnsparency + +??transparency: + cmp [ remap ] , 0 + jnz ??trans_remap + +; ************************************************************************* +; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_ref_point + ecx ] + mov [ entry ] , ecx + + ??trans_outter_loop: + xor ecx , ecx + push esi + push edi + mov ebx , [ counter_x ] + jmp [ entry ] + ??trans_inner_loop: + REPT 32 + local trans_pixel + mov cl , [ esi ] + test cl , cl + jz trans_pixel + mov [ edi ] , cl + trans_pixel: + add ecx , eax + adc esi , edx + inc edi + ENDM + ??trans_ref_point: + dec ebx + jge ??trans_inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_remapref_point + ecx ] + mov [ entry ] , ecx + + ??trans_remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??trans_remapinner_loop: + REPT 32 + local trans_pixel + mov bl , [ esi ] + test bl , bl + jz trans_pixel + mov cl , [ eax + ebx ] + mov [ edi ] , cl + trans_pixel: + add ecx , [ dx_frac ] + adc esi , edx + inc edi + ENDM + ??trans_remapref_point: + dec [ remap_counter ] + jge ??trans_remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_remapoutter_loop + ret + + + + + +??all_done: + ret +endp + + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/SHADOW.ASM b/WIN32LIB/DRAWBUFF/SHADOW.ASM new file mode 100644 index 0000000..1b77ec6 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/SHADOW.ASM @@ -0,0 +1,210 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./shadow.asm 1.9 1994/05/20 15:30:49 joe_bostic Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : SHADOW.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : February 28, 1995 [BG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, GVPC dst, void *shadowbuff); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" +INCLUDE ".\keystruc.inc" + +GLOBAL C Shadow_Blit : NEAR + +GLOBAL C RealModePtr : DWORD +GLOBAL C Hide_Mouse : NEAR +GLOBAL C Show_Mouse : NEAR + + CODESEG + +; void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, VBC dst, void *shadowbuff); +; Warning: Shadow_Blit appears to be relatively stupid, in that while it is +; optimized to perform word or dword blits, it only does so if the +; entire region is word or dword-sized. In other words, if you blit +; a region that is 200 pixels wide (clearly dword-sized), then it +; will use the dword loop. However, if you blit a region that is +; 201 pixels wide, the dumb thing will use the byte loop for the +; entire blit. + PROC Shadow_Blit C near + USES eax,ebx,ecx,edx,esi,edi + + ARG x:DWORD + ARG y:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG srcpage:DWORD + ARG dstpage:DWORD + ARG shadow:DWORD + + LOCAL modulo:DWORD ; Row modulo + LOCAL hidemouse:DWORD ; Was the mouse hidden? + LOCAL dwordwidth:DWORD + LOCAL bytewidth:DWORD + + cld ; Always move forward. + mov [hidemouse],0 ; Presume mouse hasn't been hidden. + +; Fetch the segment of the seenpage. + mov ebx,[dstpage] + mov ebx,[(GraphicViewPort ebx).GVPOffset] + + ; Determine if the mouse needs to be hidden at all. If this happens to be + ; a shadow blit to non visible page (who knows why!?) then don't bother to + ; hide the mouse since it isn't necessary. +; cmp ebx,0A0000h +; setne [BYTE PTR hidemouse] ; Flag that mouse need not be hidden. +; jne short ??itsok + mov esi,[RealModePtr] + cmp [(KeyboardType esi).MState],0 + je short ??itsok + mov [hidemouse],1 + call Hide_Mouse C ; Hides mouse again (just to be sure). +??itsok: + mov edi,[srcpage] + mov esi,[(GraphicViewPort edi).GVPOffset] + + mov eax,[(GraphicViewPort edi).GVPWidth] + add eax,[(GraphicViewPort edi).GVPXAdd] + add eax,[(GraphicViewPort edi).GVPPitch] + push eax ; save width+xadd for later calc + mov edx,[y] + mul edx + add eax,[x] + add esi,eax +; At this point, esi points to the source page and ebx points to the dest page + sub ebx,esi ; esi+ebx == dest page pointer + + mov edi,[shadow] ; EDI points to shadow buffer. + + mov ecx,[region_height] ; get the height of the window in cx + + mov edx,[RealModePtr] + + ; Calculate the row add module. + pop eax ; restore width+xadd + sub eax,[region_width] + mov [modulo],eax + + mov eax,[region_width] + shr eax,2 + mov [dwordwidth],eax + mov eax,[region_width] + and eax,3 + mov [bytewidth],eax + +;--------------------------------------- +; DOUBLE WORD shadow blit if possible. +;--------------------------------------- +??dloop_top: + push ecx + mov ecx,[dwordwidth] + +??lcontinue: + repe cmpsd ; check the entire row for changed longs + je short ??loop_top + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??dok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??dok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jb short ??dok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??dok: + + mov eax,[esi-4] + mov [ebx+esi-4],eax ; Update destination page. + mov [edi-4],eax ; Update shadow buffer. + or ecx,ecx + jne short ??lcontinue + +;--------------------------------------- +; Row loop start for BYTES. +;--------------------------------------- +??loop_top: + mov ecx,[bytewidth] + +; Column loop start -- by bytes. +??continue: + repe cmpsb ; check the entire row for changed longs + je short ??done_x + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??bok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??bok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jl short ??bok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??bok: + + mov al,[esi-1] + mov [ebx+esi-1],al ; Update destination page. + mov [edi-1],al ; Update shadow buffer. + + or ecx,ecx + jne short ??continue + +??done_x: + inc [y] + add esi,[modulo] + pop ecx + dec ecx + jnz ??dloop_top + +??fini: + ; Re show the mouse if it was hidden by this routine. + cmp [hidemouse],0 + je short ??reallyfini + call Show_Mouse C +??reallyfini: + ret + + ENDP Shadow_Blit + + END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/STAMP.ASM b/WIN32LIB/DRAWBUFF/STAMP.ASM new file mode 100644 index 0000000..2b4c720 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/STAMP.ASM @@ -0,0 +1,600 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : STAMP.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 23, 1993 * +;* * +;* Last Update : August 23, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +INCLUDE "stamp.inc" + +global C Init_Stamps:near +global LastIconset:dword +global MapPtr:dword +global IconCount:dword +global IconSize:dword +global StampPtr:dword + + DATASEG + + +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + EVEN + + CODESEG + + +GLOBAL C Buffer_Draw_Stamp:near +GLOBAL C Buffer_Draw_Stamp_Clip:near + +; 256 color icon system. + +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* + PROC Init_Stamps C near USES eax ebx edx edi + ARG icondata:DWORD + + ; Verify legality of parameter. + cmp [icondata],0 + je short ??fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short ??fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[(IControl_Type edi).Count] + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[(IControl_Type edi).Width] + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[(IControl_Type edi).Height] + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[(IControl_Type edi).Map] + add eax,edi + mov [MapPtr],eax + +??nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[(IControl_Type edi).Icons] + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[(IControl_Type edi).TransFlag] + mov [IsTrans],eax + +??fini: + ret + + ENDP Init_Stamps + + +;*********************************************************** + + +;*********************************************************** +; DRAW_STAMP +; +; VOID cdecl Buffer_Draw_Stamp(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC Buffer_Draw_Stamp C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset + call Init_Stamps C,eax +??noreset: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap: + cmp ebx,[IconCount] + jae ??out + mov [icon],ebx ; Updated icon number. + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[IconWidth] + mov [modulo],eax + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. +;;; mov edx,[remap] + mov ebx,[remap] + xor eax,eax +??xrowloop: + push ecx + mov ecx,[iwidth] + +??xcolumnloop: + lodsb +;;; mov ebx,edx +;;; add ebx,eax +;;; mov al,[ebx] ; New real color to draw. + xlatb + or al,al + jz short ??xskip1 ; Transparency skip check. + mov [edi],al +??xskip1: + inc edi + loop ??xcolumnloop + + pop ecx + add edi,[modulo] + loop ??xrowloop + jmp short ??out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloop + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + shr ebx,2 + mov edx,[modulo] + mov eax,[iwidth] + shr eax,2 +??loop1: + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + dec ebx + jnz ??loop1 + jmp short ??out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloop: + push ecx + mov ecx,[iwidth] + +??columnloop: + lodsb + or al,al + jz short ??skip1 ; Transparency check. + mov [edi],al +??skip1: + inc edi + loop ??columnloop + + pop ecx + add edi,[modulo] + loop ??rowloop + + ; Cleanup and exit icon drawing routine. +??out: + popad + ret + + ENDP Buffer_Draw_Stamp + + + + +;*********************************************************** +; DRAW_STAMP_CLIP +; +; VOID cdecl MCGA_Draw_Stamp_Clip(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap, LONG min_x, LONG min_y, LONG max_x, LONG max_y); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC Buffer_Draw_Stamp_Clip C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + ARG min_x:DWORD ; Clipping rectangle boundary + ARG min_y:DWORD ; Clipping rectangle boundary + ARG max_x:DWORD ; Clipping rectangle boundary + ARG max_y:DWORD ; Clipping rectangle boundary + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL skip:DWORD ; amount to skip per row of icon data + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out2 + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset2 + call Init_Stamps C,eax +??noreset2: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap2 + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap2: + cmp ebx,[IconCount] + jae ??out2 + mov [icon],ebx ; Updated icon number. + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Update the clipping window coordinates to be valid maxes instead of width & height + ; , and change the coordinates to be window-relative + mov ebx,[min_x] + add [max_x],ebx + add [x_pixel],ebx ; make it window-relative + mov ebx,[min_y] + add [max_y],ebx + add [y_pixel],ebx ; make it window-relative + + ; See if the icon is within the clipping window + ; First, verify that the icon position is less than the maximums + mov ebx,[x_pixel] + cmp ebx,[max_x] + jge ??out2 + mov ebx,[y_pixel] + cmp ebx,[max_y] + jge ??out2 + ; Now verify that the icon position is >= the minimums + add ebx,[IconHeight] + cmp ebx,[min_y] + jle ??out2 + mov ebx,[x_pixel] + add ebx,[IconWidth] + cmp ebx,[min_x] + jle ??out2 + + ; Now, clip the x, y, width, and height variables to be within the + ; clipping rectangle + mov ebx,[x_pixel] + cmp ebx,[min_x] + jge ??nominxclip + ; x < minx, so must clip + mov ebx,[min_x] + sub ebx,[x_pixel] + add esi,ebx ; source ptr += (minx - x) + sub [iwidth],ebx ; icon width -= (minx - x) + mov ebx,[min_x] + mov [x_pixel],ebx + +??nominxclip: + mov eax,[IconWidth] + sub eax,[iwidth] + mov [skip],eax + + ; Check for x+width > max_x + mov eax,[x_pixel] + add eax,[iwidth] + cmp eax,[max_x] + jle ??nomaxxclip + ; x+width is greater than max_x, so must clip width down + mov eax,[iwidth] ; eax = old width + mov ebx,[max_x] + sub ebx,[x_pixel] + mov [iwidth],ebx ; iwidth = max_x - xpixel + sub eax,ebx + add [skip],eax ; skip += (old width - iwidth) +??nomaxxclip: + ; check if y < miny + mov eax,[min_y] + cmp eax,[y_pixel] ; if(miny <= y_pixel), no clip needed + jle ??nominyclip + sub eax,[y_pixel] + sub ecx,eax ; height -= (miny - y) + mul [IconWidth] + add esi,eax ; icon source ptr += (width * (miny - y)) + mov eax,[min_y] + mov [y_pixel],eax ; y = miny +??nominyclip: + ; check if (y+height) > max y + mov eax,[y_pixel] + add eax,ecx + cmp eax,[max_y] ; if (y + height <= max_y), no clip needed + jle ??nomaxyclip + mov ecx,[max_y] ; height = max_y - y_pixel + sub ecx,[y_pixel] +??nomaxyclip: + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[iwidth] + mov [modulo],eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck2 + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + mov ebx,[remap] + xor eax,eax +??xrowloopc: + push ecx + mov ecx,[iwidth] + +??xcolumnloopc: + lodsb + xlatb + or al,al + jz short ??xskip1c ; Transparency skip check. + mov [edi],al +??xskip1c: + inc edi + loop ??xcolumnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??xrowloopc + jmp short ??out2 + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck2: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloopc + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + mov edx,[modulo] + mov eax,[iwidth] + + ; + ; Optimise copy by dword aligning the destination + ; +??loop1c: + push eax + rept 3 + test edi,3 + jz ??aligned + movsb + dec eax + jz ??finishedit + endm +??aligned: + mov ecx,eax + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,3 + rep movsb + +??finishedit: + add edi,edx + add esi,[skip] + pop eax + + dec ebx + jnz ??loop1c + jmp short ??out2 + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloopc: + push ecx + mov ecx,[iwidth] + +??columnloopc: + lodsb + or al,al + jz short ??skip1c ; Transparency check. + mov [edi],al +??skip1c: + inc edi + loop ??columnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??rowloopc + + ; Cleanup and exit icon drawing routine. +??out2: + popad + ret + + ENDP Buffer_Draw_Stamp_Clip + + END diff --git a/WIN32LIB/DRAWBUFF/STAMP.INC b/WIN32LIB/DRAWBUFF/STAMP.INC new file mode 100644 index 0000000..41ebb35 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/STAMP.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + +ifdef NO_WAY_THIS_WILL_BE_DEFINED_HAHAHAHA + + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +else + + + STRUC IControl_Type + +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? +MapHeight DW ? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? +Map DD ? ; Icon map offset. + ENDS + +endif + +ICON256 EQU 1 diff --git a/WIN32LIB/DRAWBUFF/STMPCACH.ASM b/WIN32LIB/DRAWBUFF/STMPCACH.ASM new file mode 100644 index 0000000..29e149e --- /dev/null +++ b/WIN32LIB/DRAWBUFF/STMPCACH.ASM @@ -0,0 +1,284 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : STAMP.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 23, 1993 * +;* * +;* Last Update : August 23, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +JUMPS + +MAX_CACHED_ICONS=300 +ICON_WIDTH =24 +ICON_HEIGHT =24 +MAX_ICON_SETS =100 + + +global C IconPointers:dword +global C Init_Stamps:near +global C IconCacheLookup:word +global LastIconset:dword +global MapPtr:dword +global IconCount:dword +global IconSize:dword +global StampPtr:dword +global IconEntry:dword +global IconData:dword + +global Clear_Icon_Pointers_:near +global Cache_Copy_Icon_:near +global Is_Icon_Cached_:near +global Get_Icon_Index_:near +global Get_Free_Index_:near +global Cache_New_Icon_:near +global Is_Stamp_Registered_:near +global Get_Free_Cache_Slot_:near + + + struc IconSetType + + IconSetPtr dd ? + IconListOffset dd ? + + ends + +global C IconSetList:IconSetType + + codeseg + + + +;************************************************************************************************ +;* Cache_Copy_Icon -- copy an icon to its video memory cache * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* edx - ptr to video surface * +;* ebx - pitch of video surface * +;* * +;* OUTPUT: none * +;* * +;* PROTO: extern "C" Cache_Copy_Icon (void const *icon_ptr , * +;* VideoSurfaceDescription.lpSurface , * +;* VideoSurfaceDescription.lPitch); * +;* * +;* HISTORY: * +;* 11/8/95 3:16PM ST: Created * +;*==============================================================================================* + +proc Cache_Copy_Icon_ near + pushad + + mov esi,eax + mov edi,edx + sub ebx,ICON_WIDTH + + mov dl,ICON_HEIGHT ;icon height + +??each_line_lp: mov ecx,ICON_WIDTH/4 + rep movsd + lea edi,[edi+ebx] + dec dl + jnz ??each_line_lp + + popad + ret + +endp Cache_Copy_Icon_ + + + + + +;************************************************************************************************ +;* Is_Icon_Cached -- has an icon been cached? If not, is it cacheable? * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* edx - icon number * +;* * +;* OUTPUT: eax - index of cached icon or -1 if not cached * +;* * +;* PROTO: extern "C" int Is_Icon_Cached (void const *icon_data , int icon); * +;* * +;* HISTORY: * +;* 11/8/95 2:16PM ST: Created * +;*==============================================================================================* + +proc Is_Icon_Cached_ near + + mov [IconData],eax ;save the icon data ptr for later + push edx + test eax,eax + je ??out + +; Initialize the stamp data if necessary. + cmp [LastIconset],eax + je short ??noreset + call Init_Stamps C,eax + +; Determine if the icon number requested is actually in the set. +; Perform the logical icon to actual icon number remap if necessary. +??noreset: cmp [MapPtr],0 + je short ??notmap + push edi + mov edi,[MapPtr] + mov dl,[edi+edx] + pop edi + +??notmap: cmp edx,[IconCount] + jl ??in_range + pop edx + mov eax,-1 + ret + +; See if the stamp is registered - if not then it cant be cached +??in_range: mov eax,[IconData] + call Is_Stamp_Registered_ + cmp eax,-1 + jnz ??got_entry + pop edx + ret + +; Stamp is registered - if its cached already then just return the index +??got_entry: mov eax,[(IconSetType eax).IconListOffset] + cmp [word eax+edx*2+IconCacheLookup],-1 + jz ??not_cached + +; it is cached and [eax+edx] is the index + movzx eax,[word eax+edx*2+IconCacheLookup] + pop edx + ret + + + +; +; The stamps set is registered but we havn't seen this stamp before +; so try caching it +; + +??not_cached: mov [IconEntry],eax + add [IconEntry],edx + add [IconEntry],edx + call Get_Free_Cache_Slot_ + test eax,eax + jge ??got_free_slot + pop edx ;eax is -1 here anyway + ret + +; We found a free caching slot so try caching the stamp into it +??got_free_slot:imul edx,[IconSize] + add edx,[StampPtr] + push eax + call Cache_New_Icon_ ;takes icon index in eax, ptr to icon in edx + test eax,eax + jz ??cache_failed + + +; Success! Add the index into the table + pop eax + mov edx,[IconEntry] + mov [edx+IconCacheLookup],ax + and eax,0ffffh + pop edx + ret + +; Couldnt cache the new Icon - return -1 to say icon isnt cached +??cache_failed: pop eax +??out: pop edx + mov eax,-1 + ret + + +endp Is_Icon_Cached_ + + + + + + + +;************************************************************************************************ +;* Is_Stamp_Registered -- has an icon's set been previously registered? * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* * +;* OUTPUT: eax - ptr to registration entry or -1 if not registered * +;* * +;* PROTO: extern "C" int Is_Stamp_Registered (void const *icon_data); * +;* * +;* HISTORY: * +;* 11/10/95 10:00AM ST: Created * +;*==============================================================================================* + +proc Is_Stamp_Registered_ + + push edi + push ecx + mov edi,offset IconSetList + mov ecx,MAX_ICON_SETS + +??each_set_lp: cmp eax,[edi] + jz ??got_icon_set + add edi,size IconSetType + dec ecx + jnz ??each_set_lp + mov eax,-1 + pop ecx + pop edi + ret + +??got_icon_set: mov eax,edi + pop ecx + pop edi + ret + +endp Is_Stamp_Registered_ + + + + + + + + dataseg + + +IconEntry dd 0 ;Temporary pointer to icon index entry in table +IconData dd 0 ;Temporary ptr to icon set data + + + +end diff --git a/WIN32LIB/DRAWBUFF/SZREGION.ASM b/WIN32LIB/DRAWBUFF/SZREGION.ASM new file mode 100644 index 0000000..affce31 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/SZREGION.ASM @@ -0,0 +1,101 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Calculate size of an MCGA region * +;* * +;* File Name : REGIONSZ.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : March 1, 1995 * +;* * +;* Last Update : March 1, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Size_Of_Region - calculate graphic buffer region size * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +GLOBAL Buffer_Size_Of_Region : NEAR + +CODESEG + +;*************************************************************************** +;* VVPC::Size_Of_Region - calculate buffer region size * +;* * +;* INPUT: DWORD the width of the region * +;* * +;* DWORD the height of the region * +;* * +;* OUTPUT: calculated size of the region (size = width * height) * +;* * +;* * +;* HISTORY: * +;* 03/01/1995 BWG : Created. * +;*=========================================================================* + PROC Buffer_Size_Of_Region C near + USES ebx,ecx,edx + + ARG this_object:DWORD ; this is a member function + ARG region_width:DWORD ; width of region + ARG region_height:DWORD ; height of region + + ;*=================================================================== + ; Get the viewport information + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + + ;*=================================================================== + ; Verify that the width is legal + ;*=================================================================== + mov eax,[region_width] ; find the width + cmp eax,edx ; is it too wide? + jb short ??wok ; if not, leave it alone + mov eax,edx ; otherwise clip it + + ;*=================================================================== + ; Verify that the height is ok + ;*=================================================================== +??wok: mov ebx,[region_height] ; get the height + cmp ebx,ecx ; is it too tall? + jb ??hok ; if not, leave it alone + mov ebx,ecx ; otherwise clip it + + ;*=================================================================== + ; Now multiply 'em to calculate the size of the region + ;*=================================================================== +??hok: mul ebx ; size = w * h + + ret + ENDP Buffer_Size_Of_Region + + END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/TEST/HOLD.DEF b/WIN32LIB/DRAWBUFF/TEST/HOLD.DEF new file mode 100644 index 0000000..1412668 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/HOLD.DEF @@ -0,0 +1,14 @@ +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + + + + + + + + + diff --git a/WIN32LIB/DRAWBUFF/TEST/MAKEFILE b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE new file mode 100644 index 0000000..58e9f4b --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE @@ -0,0 +1,183 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WIN32LIB)\drawbuff\TEST +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj & + testasm.obj + +CPS_FILES = & + test1.cps & + test2.cps + +PROJ_LIBS = & + drawbuff.lib & + mem.lib & + misc.lib & + iff.lib & + rawfile.lib & + tile.lib & + font.lib & + profile.lib + +#PROJ_LIBS = & +# drawbuff.lib & +# win32lib.lib + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB386\NT +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) $(CPS_FILES) + $(LINK_CMD) $(LINK_CFG) name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + %append $^@ library $(LIB_DIR)\ddraw.lib + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.BOR b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.BOR new file mode 100644 index 0000000..75ddf1b --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.BOR @@ -0,0 +1,187 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = test +PROJ_DIR = $(WIN32LIB)\drawbuff\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = \ + $(PROJ_NAME).obj + +CPS_FILES = \ + test1.cps \ + test2.cps +!if 0 +PROJ_LIBS = \ + drawbuff.lib \ + win32lib.lib +!else +PROJ_LIBS = \ + drawbuff.lib \ + mem.lib \ + misc.lib \ + iff.lib \ + rawfile.lib \ + tile.lib \ + font.lib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +.lbm.cps: + $(WIN32LIB)\\TOOLS\WWCOMP $*.lbm $*.cps + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe $(CPS_FILES) + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_LIBS) + $(LINK_CMD) @&&| +$(LINK_CFG) + +$(COMPILER)\\LIB\\c0w32.obj+ +$(OBJECTS) +$<,$* +$(PROJ_LIBS) + +import32.lib + +cw32i.lib +$*.def +| + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + make + cd $(PROJ_DIR) + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.WAT b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.WAT new file mode 100644 index 0000000..c596e07 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/MAKEFILE.WAT @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WIN32LIB)\drawbuff\TEST +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj + +CPS_FILES = & + test1.cps & + test2.cps + +PROJ_LIBS = & + drawbuff.lib & + mem.lib & + misc.lib & + iff.lib & + rawfile.lib & + tile.lib & + font.lib +#PROJ_LIBS = & +# drawbuff.lib & +# win32lib.lib + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB386\NT +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) $(CPS_FILES) + $(LINK_CMD) $(LINK_CFG) name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + %append $^@ library $(LIB_DIR)\ddraw.lib + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/DRAWBUFF/TEST/TEST.BAK b/WIN32LIB/DRAWBUFF/TEST/TEST.BAK new file mode 100644 index 0000000..b35e67f --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/TEST.BAK @@ -0,0 +1,1087 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : GraphicBufferClass Test Program * + * * + * File Name : DRAWTEST.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 25, 1995 * + * * + * Last Update : September 27, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WinMain -- Program entry point * + * Set_Mode -- Set up direct draw system * + * WndProc -- Callback procedure for main window * + * Font_Test -- Test GVPC::Print * + * Rect_Test -- Test GVPC::Draw_Rect and GVPC::Draw_Line * + * Clear_Test -- Test GVPC::Clear * + * Pixel_Test -- Test GVPC::Put_Pixel * + * Read_Pixel_Test -- Test GVPC::Read_Pixel * + * Scale_Test -- Test GVPC::Scale * + * Blit_Test1 -- Test GVPC::Blit from video mem to video mem * + * Blit_Test1 -- Test GVPC::Blit from system mem to video mem * + * Fill_Rect_Test -- Test GVPC::Fill_Rect * + * Remap_Test -- Test GVPC::Remap * + * Quad_Test -- Test GVPC::Fill_Quad * + * Copy_Test -- Test GVPC::To_Buffer and BufferClass::To_Page * + * Test_All -- Calls the other tests * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "..\gbuffer.h" +#include +#include +#include +#include + + +#define NAME "DRAWBUFF test" +#define TITLE "DRAWBUFF library test" + + +void Test_All (void); + +// +// Misc globals for testing +// +int Test=0; // Number of test currently in progress +BOOL AllDone; // Flag that we should exit + +int x1,y1,x2,y2; // Coordinates for rectangle drawing + +int BlitXAdd; // +int BlitXDir; // Vars for blit testing +int BlitYAdd; // +int BlitYDir; // + +int BlitStretchX; // +int BlitStretchY; // Vars for scale testing +int BlitStretchXAdd; // +int BlitStretchYAdd; // + +char RemapTable[256]; // Table for colour remap testing + +int FontPrintX; // Vars for test font printing +int FontPrintY; // + +int StampX; // +int StampY; // Vars for moving the stamp test icon around +int StampXDir; // the screen +int StampYDir; // + +#define MAX_TESTS 13 // Number of tests we have defined +#define MODE_WIDTH 640 // Width in pixels of required video mode +#define MODE_HEIGHT 400 // Height in pixels of required video mode + +GraphicBufferClass *ScreenBuffer=NULL; // Global pointer to screen GraphicBufferClass +GraphicBufferClass *BackBuffer=NULL; // Global pointer to a back buffer + +void *IconPointer; // Global pointer to our test icons + +PALETTEENTRY pe[256]; // DD Palette entries +unsigned char Palette[256*3]; // Place to load palette to +extern LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object + +// +// Prototypes +// +long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ; +void Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); + + +// +// Externs +// +extern LPDIRECTDRAW DirectDrawObject; +extern HWND MainWindow; + +/*********************************************************************************************** + * WinMain -- Program entry point * + * * + * * + * * + * INPUT: Standard Windows startup parameters * + * * + * OUTPUT: msg.wParam * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:28PM ST : Created * + *=============================================================================================*/ + +int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, + LPSTR lpszCmdParam, int nCmdShow) +{ + static char szAppName[] = "HelloWin" ; + HWND hwnd ; + MSG msg ; + WNDCLASS wndclass ; + int i,j,k; + + + // + // Register the window class + // + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = WndProc ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = hInstance ; + wndclass.hIcon = LoadIcon (hInstance, IDI_APPLICATION) ; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = NAME; + + RegisterClass (&wndclass) ; + } + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + NAME, + TITLE, + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + MODE_WIDTH, + MODE_HEIGHT, + NULL, + NULL, + hInstance, + NULL ); + + ShowWindow (hwnd, nCmdShow) ; + UpdateWindow (hwnd) ; + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + // (Dangerous if Windoze can change the handle) + + + + // + // Allocate space for the icons and load them + // + IconPointer=malloc (20000); + Load_File ( "bridge3.des" , IconPointer ); + + // + // Load the desert palette + // + Load_File ( "desert.pal" , Palette ); + k=0; + for( j=0 ; j<768 ; j+=3 ) + { + pe[k].peRed = Palette[j]; + pe[k].peGreen = Palette[j+1]; + pe[k].peBlue = Palette[j+2]; + k++; + } + + + // + // Initialise global stuff required for the tests + // + + // Force load of font + FontPtr=NULL; + + + // Set the video mode + Set_Video_Mode( MainWindow , MODE_WIDTH , MODE_HEIGHT , 8 ); + + // + // Init the stuff for drawing rectangles + // + x1=0; + y1=0; + x2=319; + y2=199; + + + // + // Init the stamp test variables + // + + StampX=100; + StampY=100; + StampXDir=1; + StampYDir=1; + + // + // Create the GraphicBufferClass that will be the screen buffer + // + ScreenBuffer = new GraphicBufferClass ( MODE_WIDTH , MODE_HEIGHT , (GBC_Enum)(GBC_VIDEOMEM | GBC_VISIBLE)); + + + // + // Set the palette + // + DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &pe[0] , &PalettePtr ,NULL); + ScreenBuffer->Get_Graphic_Buffer()->Get_DD_Surface()->SetPalette( PalettePtr ); + PalettePtr->SetEntries( DDPSETPAL_VSYNC , 0 , 256 , &pe[0] ); + + + // + // Set up the remap table for the Buffer_Remap test + // + for ( i=0 ; i<256 ; i++ ){ + RemapTable[i]=255-i; + } + + + // + // Get rid of the windows cursor + // + ShowCursor (FALSE); + + AllDone = FALSE; + + // + // Windows message loop + // + while ( ! AllDone ){ + + Test_All(); // Perform a test + + if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ){ + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return msg.wParam; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } + + return msg.wParam; +} + + + + +#ifdef cuts //this is now in a seperate file +/*********************************************************************************************** + * Set_Mode -- temporary replacement for library set mode function * + * * + * * + * * + * INPUT: int - mode width * + * int - mode height * + * int - bits per pixel * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:51PM ST : Created * + *=============================================================================================*/ + +void Set_Mode ( int width , int height , int bpp ) +{ + + // + // Setup the direct draw system by + // 1. Creating the DirectDraw object + // 2. Setting the cooperative level to exclusive so we own the screen + // 3. Setting the video mode + // + + if ( DirectDrawObject == NULL ){ + + // Create the direct draw object + DirectDrawCreate ( NULL , &DirectDrawObject , NULL); + + // Set the cooperative level + DirectDrawObject->SetCooperativeLevel ( MainWindow , DDSCL_EXCLUSIVE + | DDSCL_FULLSCREEN + | DDSCL_ALLOWMODEX ); + // Set the required display mode with 8 bits per pixel + DirectDrawObject->SetDisplayMode ( width , height , bpp ); + } + +} +#endif //cuts + + +/*********************************************************************************************** + * WndProc -- windows message callback * + * * + * Pilfered from a windows example program - HELLOWIN.C * + * * + * * + * INPUT: Standard Windoze callback parameters * + * * + * OUTPUT: long * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:39PM ST : Pilfered * + *=============================================================================================*/ + +long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, + LONG lParam) + { + + switch (message){ + + + case WM_KEYDOWN: + + switch ( wParam ){ + + case VK_SPACE: + Test++; + if ( Test>=MAX_TESTS ) { + Test=0; + } + if ( BackBuffer ){ + delete BackBuffer; + BackBuffer=NULL; + } + break; + } + break; + + + case WM_DESTROY: + // + // Tidy up + // + + if ( BackBuffer ) { + delete BackBuffer; + } + delete ScreenBuffer; + + if ( DirectDrawObject ){ + Reset_Video_Mode(); + } + + AllDone = TRUE; + PostQuitMessage (0) ; + return(0); + } + + return DefWindowProc (hwnd, message, wParam, lParam) ; +} + + + + + + +/*********************************************************************************************** + * Font_Test -- test the font print member of the GBC * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 3:29PM ST : Created * + *=============================================================================================*/ +void Font_Test ( void ) +{ + const char font_name[]={"font.fnt"}; + + if ( !FontPtr ){ + FontPtr = Load_Font ( &font_name[0] ); + FontXSpacing=0; + FontYSpacing=0; + FontPrintX=0; + FontPrintY=0; + } + + if ( FontPtr ){ + ScreenBuffer->Print ( "Hello Windoze" , FontPrintX , FontPrintY ,255 , 0); + } + + if ( FontPrintX<550 ){ + FontPrintX+=8; + } + + if ( FontPrintY<380 ){ + FontPrintY+=8; + } + + +} + + + +/*********************************************************************************************** + * Rect_Test -- test the rectangle and line functions of the GBC * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 3:03PM ST : Created * + *=============================================================================================*/ + +void Rect_Test ( GraphicBufferClass *screen_buffer ) +{ + + screen_buffer->Draw_Rect ( x1 , y1 , x2 , y2 , 255 ); + if ( x1<90 ){ + x1+=2; + } + + if ( y1<90 ){ + y1+=2; + } + + if ( x2>230 ){ + x2-=2; + } + + if ( y2>110 ){ + y2-=2; + } + +} + + + + +/*********************************************************************************************** + * Clear_Test -- test the clear function of the GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 3:05PM ST : Created * + *=============================================================================================*/ + +void Clear_Test ( void ) +{ + + x1=0; + y1=0; + x2=319; + y2=199; + + FontPrintX=0; + FontPrintY=0; + + ScreenBuffer->Clear ( rand()&255 ); +} + + + + + +/*********************************************************************************************** + * Stamp_Test -- test the Draw_Stamp member of GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/28/95 6:02PM ST : Created * + *=============================================================================================*/ +void Stamp_Test ( void ) +{ + int x; + int y; + int icon=0; + + // + // Use stacking locks to speed things up + // + ScreenBuffer->Lock(); + + for ( y=0 ; y<5*24 ; y+=24 ){ + + for ( x=0 ; x<6*24 ; x+=24 ){ + + ScreenBuffer->Draw_Stamp( IconPointer, icon++ , x+StampX, y+StampY, NULL ); + + } + } + + ScreenBuffer->Unlock(); + + StampX+=StampXDir; + StampY+=StampYDir; + + if ( StampX>640-6*24 || StampX<1 ){ + StampXDir=-StampXDir; + } + + if ( StampY>400-6*24 || StampY<1 ){ + StampYDir=-StampYDir; + } + +} + + + +/*********************************************************************************************** + * Pixel_Test -- test the GVPclass put_pixel member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 5:29PM ST : Created * + *=============================================================================================*/ +void Pixel_Test ( void ) +{ + ScreenBuffer->Put_Pixel ( rand() & 511 , rand() & 255 , rand () &255 ); + +} + + + + + + + + + + +/*********************************************************************************************** + * Read_Pixel_Test -- test the GVPclass Read_Pixel member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 4:51PM ST : Created * + *=============================================================================================*/ +void Read_Pixel_Test ( void ) +{ + + int i; + int colour; + int pixel_x; + int pixel_y; + + if ( !BackBuffer ) { + + BackBuffer = new GraphicBufferClass(640,400, GBC_VIDEOMEM ); + + BackBuffer->Clear( 0 ); + ScreenBuffer->Clear ( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + } + + + // + // Use the stacking lock feature to lock the surfaces once only and + // then draw 10 pixels + // + + ScreenBuffer->Lock(); + BackBuffer->Lock(); + + for ( i=0 ; i<10 ; i++ ){ + + pixel_x = rand() &511; + pixel_y = rand() &255; + + colour=BackBuffer->Get_Pixel ( pixel_x , pixel_y ); + + ScreenBuffer->Put_Pixel ( pixel_x , pixel_y , colour ); + } + + BackBuffer->Unlock(); + ScreenBuffer->Unlock(); + +} + + + +/*********************************************************************************************** + * Scale_Test -- test the GVPclass scale member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 10:27AM ST : Created * + *=============================================================================================*/ +void Scale_Test ( void ) +{ + + int i; + + if ( !BackBuffer ) { + + BackBuffer = new GraphicBufferClass(640, 400); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitStretchX=0; + BlitStretchY=0; + BlitStretchXAdd=1; + BlitStretchYAdd=1; + } + + BackBuffer->Scale ( *ScreenBuffer , 0 , 0 , 10 , 10 + ,320 , 200 , 320+BlitStretchX , 200+BlitStretchY ); + + BlitStretchX += BlitStretchXAdd; + BlitStretchY += BlitStretchYAdd; + + if ( (BlitStretchX == 100) || (BlitStretchX==-100) ){ + BlitStretchXAdd=-BlitStretchXAdd; + } + + if ( (BlitStretchY == 100) || (BlitStretchY==-100) ){ + BlitStretchYAdd=-BlitStretchYAdd; + } + + + + +} + + + +/*********************************************************************************************** + * Blit_Test1 -- test the blit member of the GraphicBufferClass * + * * + * Blits from video memory to video memory * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 5:39PM ST : Created * + *=============================================================================================*/ +void Blit_Test1 ( void ) +{ + + int i; + + if ( !BackBuffer ) { + BackBuffer = new GraphicBufferClass(640, 400, GBC_VIDEOMEM); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitXAdd=0; + BlitXDir=1; + BlitYAdd=0; + BlitYDir=1; + } + + + BackBuffer->Blit ( *ScreenBuffer , 0 , 0 , BlitXAdd , BlitYAdd , 320 , 200 , (BOOL) FALSE ); + + BlitXAdd+=BlitXDir; + + if ( (BlitXAdd == 0) || (BlitXAdd == 320 )){ + BlitXDir=-BlitXDir; + } + + + BlitYAdd+=BlitYDir; + + if ( (BlitYAdd == 0) || (BlitYAdd == 200 )){ + BlitYDir=-BlitYDir; + } + + +} + + + + + +/*********************************************************************************************** + * Blit_Test2 -- test the blit member of the GraphicBufferClass * + * * + * Blits from system memory to video memory * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 9:36AM ST : Created * + *=============================================================================================*/ +void Blit_Test2 ( void ) +{ + + int i; + + if ( !BackBuffer ) { + BackBuffer = new GraphicBufferClass(640, 400); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitXAdd=0; + BlitXDir=1; + BlitYAdd=0; + BlitYDir=1; + } + + + BackBuffer->Blit ( *ScreenBuffer , 0 , 0 , BlitXAdd , BlitYAdd , 320 , 200 , (BOOL) FALSE ); + + BlitXAdd+=BlitXDir; + + if ( (BlitXAdd == 0) || (BlitXAdd == 320 )){ + BlitXDir=-BlitXDir; + } + + + BlitYAdd+=BlitYDir; + + if ( (BlitYAdd == 0) || (BlitYAdd == 200 )){ + BlitYDir=-BlitYDir; + } + + +} + + + + + +/*********************************************************************************************** + * Fill_Rect_Test -- test the GraphicBufferClass fill rectangle member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 5:23PM ST : Created * + *=============================================================================================*/ +void Fill_Rect_Test ( void ) +{ + + int x1; + int y1; + int x2; + int y2; + + x1= rand()&511; + x2= rand()&511; + y1= rand()&255; + y2= rand()&255; + + ScreenBuffer->Fill_Rect ( min ( x1,x2 ) , min ( y1,y2) , max ( x1,x2 ) , max ( y1,y2 ) , rand()&255 ); + + +} + + + + + +/*********************************************************************************************** + * Remap_Test -- test the Remap member of GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 6:26PM ST : Created * + *=============================================================================================*/ +void Remap_Test ( void ) +{ + ScreenBuffer->Remap ( 20 , 20 , 580 , 350 , &RemapTable[0] ); +} + + + + + + + + +/*********************************************************************************************** + * Quad_Test -- test the quad fill member of GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 11:12AM ST : Created * + *=============================================================================================*/ + +void Quad_Test ( void ) +{ + char span_buff[500000]; //Dont know how big this should be so its *BIG* + int colour; + int x0; + int x1; + int x2; + int x3; + int y0; + int y1; + int y2; + int y3; + + x0 = rand() & 255; + x1 = rand() & 255+320; + x2 = rand() & 255+320; + x3 = rand() & 255; + + y0 = rand() & 127; + y1 = rand() & 127; + y2 = rand() & 127+250; + y3 = rand() & 127+250; + + colour= rand()&255; + + ScreenBuffer->Fill_Quad ( span_buff , x0,y0,x1,y1,x2,y2,x3,y3,colour); + +} + + + + + + +/*********************************************************************************************** + * Copy_Test - test the To_Buffer member of GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 11:36AM ST : Created * + *=============================================================================================*/ +void Copy_Test ( void ) +{ + + BufferClass *buffer; + char *buffer_ptr; + + + buffer = new BufferClass (640*400); + + ScreenBuffer->To_Buffer(buffer); + + buffer_ptr = (char*)buffer->Get_Buffer(); + memcpy ( buffer_ptr , buffer_ptr+4 , 640*400-4 ); + + buffer->To_Page ( *ScreenBuffer ); + + delete buffer; + +} + + + + + + +/*********************************************************************************************** + * Test_All -- perform tests on GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 2:53PM ST : Created * + *=============================================================================================*/ + +void Test_All ( void ) + +{ + + switch ( Test ){ + + case 0: + Clear_Test(); + break; + + case 1: + Rect_Test( ScreenBuffer ); + break; + + case 2: + Pixel_Test(); + break; + + case 3: + Blit_Test1(); + break; + + case 4: + Blit_Test2(); + break; + + case 5: + Scale_Test(); + break; + + case 6: + Read_Pixel_Test(); + break; + + case 7: + Fill_Rect_Test(); + break; + + case 8: + Remap_Test(); + break; + + case 9: + Quad_Test(); + break; + + case 10: + Copy_Test(); + break; + + case 11: + Font_Test(); + break; + + case 12: + Stamp_Test(); + break; + + } + +} diff --git a/WIN32LIB/DRAWBUFF/TEST/TEST.CPP b/WIN32LIB/DRAWBUFF/TEST/TEST.CPP new file mode 100644 index 0000000..42ed40c --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/TEST.CPP @@ -0,0 +1,1242 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : GraphicBufferClass Test Program * + * * + * File Name : DRAWTEST.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 25, 1995 * + * * + * Last Update : September 27, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WinMain -- Program entry point * + * Set_Mode -- Set up direct draw system * + * WndProc -- Callback procedure for main window * + * Font_Test -- Test GVPC::Print * + * Rect_Test -- Test GVPC::Draw_Rect and GVPC::Draw_Line * + * Clear_Test -- Test GVPC::Clear * + * Pixel_Test -- Test GVPC::Put_Pixel * + * Read_Pixel_Test -- Test GVPC::Read_Pixel * + * Scale_Test -- Test GVPC::Scale * + * Blit_Test1 -- Test GVPC::Blit from video mem to video mem * + * Blit_Test1 -- Test GVPC::Blit from system mem to video mem * + * Fill_Rect_Test -- Test GVPC::Fill_Rect * + * Remap_Test -- Test GVPC::Remap * + * Quad_Test -- Test GVPC::Fill_Quad * + * Copy_Test -- Test GVPC::To_Buffer and BufferClass::To_Page * + * Test_All -- Calls the other tests * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define WIN32 +#define WIN32 +#include +#include +#include "..\gbuffer.h" +#include +#include +#include +#include +#include +#include + +#define NAME "DRAWBUFF test" +#define TITLE "DRAWBUFF library test" + + +void Test_All (void); + +// +// Misc globals for testing +// +int Test=0; // Number of test currently in progress +BOOL AllDone; // Flag that we should exit + +int x1,y1,x2,y2; // Coordinates for rectangle drawing + +int BlitXAdd; // +int BlitXDir; // Vars for blit testing +int BlitYAdd; // +int BlitYDir; // + +int BlitStretchX; // +int BlitStretchY; // Vars for scale testing +int BlitStretchXAdd; // +int BlitStretchYAdd; // + +char RemapTable[256]; // Table for colour remap testing + +int FontPrintX; // Vars for test font printing +int FontPrintY; // + +int StampX; // +int StampY; // Vars for moving the stamp test icon around +int StampXDir; // the screen +int StampYDir; // + + +#define MAX_TESTS 14 // Number of tests we have defined +#define MODE_WIDTH 640 // Width in pixels of required video mode +#define MODE_HEIGHT 400 // Height in pixels of required video mode + +int ScreenWidth=MODE_WIDTH; + +BOOL GameInFocus = TRUE; + +extern "C"{ +char CurrentPalette [768]; +} + +int WindowList[][8] = { + {20>>3,20,(MODE_WIDTH-20)>>3,MODE_HEIGHT-20,0,0,0,0} /* screen window */ +}; +char SpanBuff[500000]; //Dont know how big this should be so its *BIG* + +GraphicBufferClass *ScreenBuffer=NULL; // Global pointer to screen GraphicBufferClass +GraphicBufferClass *BackBuffer=NULL; // Global pointer to a back buffer + +void *IconPointer; // Global pointer to our test icons + +PALETTEENTRY pe[256]; // DD Palette entries +unsigned char Palette[256*3]; // Place to load palette to +extern LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object + +// +// Prototypes +// +long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ; +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +extern "C" void Wait_Vert_Blank(void); +void Block_Mouse(GraphicBufferClass *ptr){} +void Unblock_Mouse(GraphicBufferClass *ptr){} + + +// +// Externs +// +extern LPDIRECTDRAW DirectDrawObject; +extern HWND MainWindow; + + + + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + + +extern "C" void Set_Palette_Register(int number,int red ,int green ,int blue); + +void Colour_Debug (int call_number) +{ + Set_Palette_Register (0,ColourLookup[call_number].Red , + ColourLookup[call_number].Green, + ColourLookup[call_number].Blue); +} + + + + + + + + + + + + + + + + + + + +/*********************************************************************************************** + * WinMain -- Program entry point * + * * + * * + * * + * INPUT: Standard Windows startup parameters * + * * + * OUTPUT: msg.wParam * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:28PM ST : Created * + *=============================================================================================*/ + +int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, + LPSTR lpszCmdParam, int nCmdShow) +{ + static char szAppName[] = "HelloWin" ; + HWND hwnd ; + MSG msg ; + WNDCLASS wndclass ; + int i,j,k; + char a[10]; + char b[10]; + + + // + // Register the window class + // + + Mem_Copy(a,b,10); + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = WndProc ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = hInstance ; + wndclass.hIcon = LoadIcon (hInstance, IDI_APPLICATION) ; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = NAME; + + RegisterClass (&wndclass) ; + } + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + NAME, + TITLE, + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + MODE_WIDTH, + MODE_HEIGHT, + NULL, + NULL, + hInstance, + NULL ); + + ShowWindow (hwnd, nCmdShow) ; + UpdateWindow (hwnd) ; + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + // (Dangerous if Windoze can change the handle) + + + + // + // Allocate space for the icons and load them + // + IconPointer=malloc (20000); + Load_File ( "bridge3.des" , IconPointer ); + Invalidate_Cached_Icons(); + Register_Icon_Set (IconPointer,TRUE); + + // + // Load the desert palette + // + Load_File ( "desert.pal" , Palette ); + k=0; + for( j=0 ; j<768 ; j+=3 ) + { + pe[k].peRed = Palette[j]; + pe[k].peGreen = Palette[j+1]; + pe[k].peBlue = Palette[j+2]; + k++; + } + + + // + // Initialise global stuff required for the tests + // + + // Force load of font + FontPtr=NULL; + + + // Set the video mode + Set_Video_Mode( MainWindow , MODE_WIDTH , MODE_HEIGHT , 8 ); + + + // + // Init the stuff for drawing rectangles + // + x1=0; + y1=0; + x2=319; + y2=199; + + + // + // Init the stamp test variables + // + + StampX=100; + StampY=100; + StampXDir=1; + StampYDir=1; + + // + // Create the GraphicBufferClass that will be the screen buffer + // + ScreenBuffer = new GraphicBufferClass ( MODE_WIDTH , MODE_HEIGHT , (GBC_Enum)(GBC_VIDEOMEM | GBC_VISIBLE)); + + + // + // Set the palette + // + DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &pe[0] , &PalettePtr ,NULL); + ScreenBuffer->Get_Graphic_Buffer()->Get_DD_Surface()->SetPalette( PalettePtr ); + PalettePtr->SetEntries( DDPSETPAL_VSYNC , 0 , 256 , &pe[0] ); + + + // + // Set up the remap table for the Buffer_Remap test + // + for ( i=0 ; i<256 ; i++ ){ + RemapTable[i]=(char)255-i; + } + + + // + // Get rid of the windows cursor + // + ShowCursor (FALSE); + + AllDone = FALSE; + + // + // Windows message loop + // + while ( ! AllDone ){ + + Test_All(); // Perform a test + + if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ){ + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return msg.wParam; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } + + return msg.wParam; +} + + + + +#ifdef cuts //this is now in a seperate file +/*********************************************************************************************** + * Set_Mode -- temporary replacement for library set mode function * + * * + * * + * * + * INPUT: int - mode width * + * int - mode height * + * int - bits per pixel * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:51PM ST : Created * + *=============================================================================================*/ + +void Set_Mode ( int width , int height , int bpp ) +{ + + // + // Setup the direct draw system by + // 1. Creating the DirectDraw object + // 2. Setting the cooperative level to exclusive so we own the screen + // 3. Setting the video mode + // + + if ( DirectDrawObject == NULL ){ + + // Create the direct draw object + DirectDrawCreate ( NULL , &DirectDrawObject , NULL); + + // Set the cooperative level + DirectDrawObject->SetCooperativeLevel ( MainWindow , DDSCL_EXCLUSIVE + | DDSCL_FULLSCREEN + | DDSCL_ALLOWMODEX ); + // Set the required display mode with 8 bits per pixel + DirectDrawObject->SetDisplayMode ( width , height , bpp ); + } + +} +#endif //cuts + + +/*********************************************************************************************** + * WndProc -- windows message callback * + * * + * Pilfered from a windows example program - HELLOWIN.C * + * * + * * + * INPUT: Standard Windoze callback parameters * + * * + * OUTPUT: long * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:39PM ST : Pilfered * + *=============================================================================================*/ + +long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, + LONG lParam) + { + + switch (message){ + + + case WM_KEYDOWN: + + switch ( wParam ){ + + case VK_SPACE: + Test++; + if ( Test>=MAX_TESTS ) { + Test=0; + } + if ( BackBuffer ){ + delete BackBuffer; + BackBuffer=NULL; + } + break; + } + break; + + case WM_ACTIVATEAPP: + if ((BOOL)wParam) { + if (ScreenBuffer) { + ScreenBuffer->Get_DD_Surface()->Restore(); + Restore_Cached_Icons(); + } + } else { + Test++; + Test--; + } + break; + + case WM_DESTROY: + // + // Tidy up + // + + if ( BackBuffer ) { + delete BackBuffer; + } + delete ScreenBuffer; + + Invalidate_Cached_Icons(); + + if ( DirectDrawObject ){ + Reset_Video_Mode(); + } + + AllDone = TRUE; + PostQuitMessage (0) ; + return(0); + } + + return DefWindowProc (hwnd, message, wParam, lParam) ; +} + + + + + + +/*********************************************************************************************** + * Font_Test -- test the font print member of the GBC * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 3:29PM ST : Created * + *=============================================================================================*/ +void Font_Test ( void ) +{ + const char font_name[]={"font.fnt"}; + + if ( !FontPtr ){ + FontPtr = Load_Font ( &font_name[0] ); + FontXSpacing=0; + FontYSpacing=0; + FontPrintX=0; + FontPrintY=0; + } + + if ( FontPtr ){ + ScreenBuffer->Print ( "Hello Windoze" , FontPrintX , FontPrintY ,255 , 0); + } + + if ( FontPrintX<550 ){ + FontPrintX+=8; + } + + if ( FontPrintY<380 ){ + FontPrintY+=8; + } + + +} + + + +/*********************************************************************************************** + * Rect_Test -- test the rectangle and line functions of the GBC * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 3:03PM ST : Created * + *=============================================================================================*/ + +void Rect_Test ( GraphicBufferClass *screen_buffer ) +{ + ScreenBuffer->Lock(); + + screen_buffer->Draw_Rect ( x1 , y1 , x2 , y2 , 255 ); + if ( x1<90 ){ + x1+=2; + } + + if ( y1<90 ){ + y1+=2; + } + + if ( x2>230 ){ + x2-=2; + } + + if ( y2>110 ){ + y2-=2; + } + + ScreenBuffer->Unlock(); +} + + + + +/*********************************************************************************************** + * Clear_Test -- test the clear function of the GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 3:05PM ST : Created * + *=============================================================================================*/ + +void Clear_Test ( void ) +{ + + x1=0; + y1=0; + x2=319; + y2=199; + + FontPrintX=0; + FontPrintY=0; + + ScreenBuffer->Clear ( rand()&255 ); +} + + + + + +/*********************************************************************************************** + * Stamp_Test -- test the Draw_Stamp member of GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/28/95 6:02PM ST : Created * + *=============================================================================================*/ +void Stamp_Test ( void ) +{ + int x; + int y; + int icon=0; + + // + // Use stacking locks to speed things up + // + //ScreenBuffer->Lock(); + + Wait_Vert_Blank(); + Colour_Debug(1); + for ( y=0 ; y<5*24 ; y+=24 ){ + + for ( x=0 ; x<6*24 ; x+=24 ){ + + ScreenBuffer->Draw_Stamp( IconPointer, icon++ , x+StampX, y+StampY, NULL ,0); + + } + } + Colour_Debug(0); + + //ScreenBuffer->Unlock(); + + StampX+=StampXDir; + StampY+=StampYDir; + + if ( StampX>640-6*24 || StampX<1 ){ + StampXDir=-StampXDir; + } + + if ( StampY>400-6*24 || StampY<1 ){ + StampYDir=-StampYDir; + } + +} + + + +/*********************************************************************************************** + * Stamp_Test2 -- test the Draw_Stamp member of GraphicBufferClass (no caching) * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/28/95 6:02PM ST : Created * + *=============================================================================================*/ +void Stamp_Test2 ( void ) +{ + int x; + int y; + int icon=0; + + // + // Use stacking locks to speed things up + // + //ScreenBuffer->Lock(); + + Wait_Vert_Blank(); + Colour_Debug(2); + for ( y=0 ; y<5*24 ; y+=24 ){ + + for ( x=0 ; x<6*24 ; x+=24 ){ + + ScreenBuffer->Draw_Stamp( IconPointer, icon++ , x+StampX, y+StampY, NULL); + + } + } + Colour_Debug(0); + + //ScreenBuffer->Unlock(); + + StampX+=StampXDir; + StampY+=StampYDir; + + if ( StampX>640-6*24 || StampX<1 ){ + StampXDir=-StampXDir; + } + + if ( StampY>400-6*24 || StampY<1 ){ + StampYDir=-StampYDir; + } + +} + + + + +/*********************************************************************************************** + * Pixel_Test -- test the GVPclass put_pixel member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 5:29PM ST : Created * + *=============================================================================================*/ +void Pixel_Test ( void ) +{ + ScreenBuffer->Put_Pixel ( rand() & 511 , rand() & 255 , rand () &255 ); + +} + + + + + + + + + + +/*********************************************************************************************** + * Read_Pixel_Test -- test the GVPclass Read_Pixel member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 4:51PM ST : Created * + *=============================================================================================*/ +void Read_Pixel_Test ( void ) +{ + + int i; + int colour; + int pixel_x; + int pixel_y; + + if ( !BackBuffer ) { + + BackBuffer = new GraphicBufferClass(640,400, GBC_VIDEOMEM ); + + BackBuffer->Clear( 0 ); + ScreenBuffer->Clear ( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + } + + + // + // Use the stacking lock feature to lock the surfaces once only and + // then draw 10 pixels + // + + ScreenBuffer->Lock(); + BackBuffer->Lock(); + + for ( i=0 ; i<10 ; i++ ){ + + pixel_x = rand() &511; + pixel_y = rand() &255; + + colour=BackBuffer->Get_Pixel ( pixel_x , pixel_y ); + + ScreenBuffer->Put_Pixel ( pixel_x , pixel_y , colour ); + } + + BackBuffer->Unlock(); + ScreenBuffer->Unlock(); + +} + + + +/*********************************************************************************************** + * Scale_Test -- test the GVPclass scale member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 10:27AM ST : Created * + *=============================================================================================*/ +void Scale_Test ( void ) +{ + + int i; + + if ( !BackBuffer ) { + + BackBuffer = new GraphicBufferClass(640, 400); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitStretchX=0; + BlitStretchY=0; + BlitStretchXAdd=1; + BlitStretchYAdd=1; + } + + BackBuffer->Scale ( *ScreenBuffer , 0 , 0 , 10 , 10 + ,320 , 200 , 320+BlitStretchX , 200+BlitStretchY ); + + BlitStretchX += BlitStretchXAdd; + BlitStretchY += BlitStretchYAdd; + + if ( (BlitStretchX == 100) || (BlitStretchX==-100) ){ + BlitStretchXAdd=-BlitStretchXAdd; + } + + if ( (BlitStretchY == 100) || (BlitStretchY==-100) ){ + BlitStretchYAdd=-BlitStretchYAdd; + } + + + + +} + + + +/*********************************************************************************************** + * Blit_Test1 -- test the blit member of the GraphicBufferClass * + * * + * Blits from video memory to video memory * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 5:39PM ST : Created * + *=============================================================================================*/ +void Blit_Test1 ( void ) +{ + + int i; + + if ( !BackBuffer ) { + BackBuffer = new GraphicBufferClass(640, 400, GBC_VIDEOMEM); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitXAdd=0; + BlitXDir=1; + BlitYAdd=0; + BlitYDir=1; + } + + + BackBuffer->Blit ( *ScreenBuffer , 0 , 0 , BlitXAdd , BlitYAdd , 320 , 200 , (BOOL) FALSE ); + + BlitXAdd+=BlitXDir; + + if ( (BlitXAdd == 0) || (BlitXAdd == 320 )){ + BlitXDir=-BlitXDir; + } + + + BlitYAdd+=BlitYDir; + + if ( (BlitYAdd == 0) || (BlitYAdd == 200 )){ + BlitYDir=-BlitYDir; + } + + +} + + + + + +/*********************************************************************************************** + * Blit_Test2 -- test the blit member of the GraphicBufferClass * + * * + * Blits from system memory to video memory * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 9:36AM ST : Created * + *=============================================================================================*/ +void Blit_Test2 ( void ) +{ + + int i; + + if ( !BackBuffer ) { + BackBuffer = new GraphicBufferClass(640, 400); + + BackBuffer->Clear( 0 ); + + x1=0; + y1=0; + x2=319; + y2=199; + + for ( i=0 ; i<50 ; i++ ) { + Rect_Test ( BackBuffer ); + } + + BlitXAdd=0; + BlitXDir=1; + BlitYAdd=0; + BlitYDir=1; + } + + + BackBuffer->Blit ( *ScreenBuffer , 0 , 0 , BlitXAdd , BlitYAdd , 320 , 200 , (BOOL) FALSE ); + + BlitXAdd+=BlitXDir; + + if ( (BlitXAdd == 0) || (BlitXAdd == 320 )){ + BlitXDir=-BlitXDir; + } + + + BlitYAdd+=BlitYDir; + + if ( (BlitYAdd == 0) || (BlitYAdd == 200 )){ + BlitYDir=-BlitYDir; + } + + +} + + + + + +/*********************************************************************************************** + * Fill_Rect_Test -- test the GraphicBufferClass fill rectangle member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 5:23PM ST : Created * + *=============================================================================================*/ +void Fill_Rect_Test ( void ) +{ + + int x1; + int y1; + int x2; + int y2; + + x1= rand()&511; + x2= rand()&511; + y1= rand()&255; + y2= rand()&255; + + ScreenBuffer->Fill_Rect ( min ( x1,x2 ) , min ( y1,y2) , max ( x1,x2 ) , max ( y1,y2 ) , rand()&255 ); + + +} + + + + + +/*********************************************************************************************** + * Remap_Test -- test the Remap member of GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/26/95 6:26PM ST : Created * + *=============================================================================================*/ +void Remap_Test ( void ) +{ + ScreenBuffer->Remap ( 0 , 0 , 640 , 400 , &RemapTable[0] ); +} + + + + + + + + +/*********************************************************************************************** + * Quad_Test -- test the quad fill member of GraphicBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 11:12AM ST : Created * + *=============================================================================================*/ + +void Quad_Test ( void ) +{ + int colour; + int x0; + int x1; + int x2; + int x3; + int y0; + int y1; + int y2; + int y3; + + x0 = rand() & 255; + x1 = rand() & 255+320; + x2 = rand() & 255+320; + x3 = rand() & 255; + + y0 = rand() & 127; + y1 = rand() & 127; + y2 = rand() & 127+250; + y3 = rand() & 127+250; + + colour= rand()&255; + + ScreenBuffer->Fill_Quad ( SpanBuff , x0,y0,x1,y1,x2,y2,x3,y3,colour); + +} + + + + + + +/*********************************************************************************************** + * Copy_Test - test the To_Buffer member of GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 11:36AM ST : Created * + *=============================================================================================*/ +void Copy_Test ( void ) +{ + + BufferClass *buffer; + char *buffer_ptr; + + + buffer = new BufferClass (640*400); + + ScreenBuffer->To_Buffer(buffer); + + buffer_ptr = (char*)buffer->Get_Buffer(); + memcpy ( buffer_ptr , buffer_ptr+4 , 640*400-4 ); + + buffer->To_Page ( *ScreenBuffer ); + + delete buffer; + +} + + + + + + +/*********************************************************************************************** + * Test_All -- perform tests on GraphicsBufferClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/25/95 2:53PM ST : Created * + *=============================================================================================*/ + +void Test_All ( void ) + +{ + + switch ( Test ){ + + case 0: + Clear_Test(); + break; + + case 1: + Rect_Test( ScreenBuffer ); + break; + + case 2: + Pixel_Test(); + break; + + case 3: + Blit_Test1(); + break; + + case 4: + Blit_Test2(); + break; + + case 5: + Scale_Test(); + break; + + case 6: + Read_Pixel_Test(); + break; + + case 7: + Fill_Rect_Test(); + break; + + case 8: + Remap_Test(); + break; + + case 9: + Quad_Test(); + break; + + case 10: + Copy_Test(); + break; + + case 11: + Font_Test(); + break; + + case 12: + Stamp_Test(); + break; + + case 13: + Stamp_Test2(); + break; + + } + +} diff --git a/WIN32LIB/DRAWBUFF/TEST/TEST.DEF b/WIN32LIB/DRAWBUFF/TEST/TEST.DEF new file mode 100644 index 0000000..1412668 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/TEST.DEF @@ -0,0 +1,14 @@ +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + + + + + + + + + diff --git a/WIN32LIB/DRAWBUFF/TEST/TEST/TEST.CPP b/WIN32LIB/DRAWBUFF/TEST/TEST/TEST.CPP new file mode 100644 index 0000000..e0b4a4d --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/TEST/TEST.CPP @@ -0,0 +1,34 @@ +/* +** 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 . +*/ + +#include "stdio.h" + +extern "C" { + void func_1(void); +} + +void func_2(void) +{ + printf("There\r"); +} + +void main(void) +{ + func_1(); + func_2(); +} diff --git a/WIN32LIB/DRAWBUFF/TEST/TESTASM.ASM b/WIN32LIB/DRAWBUFF/TEST/TESTASM.ASM new file mode 100644 index 0000000..687ecd4 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TEST/TESTASM.ASM @@ -0,0 +1,80 @@ +; +; 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 . +; + +IDEAL +P386 +MODEL USE32 FLAT +jumps + + codeseg + + masm +; +; Change a DAC colour register directly +; +; register number in al +; +; bh=red bl=green cl=blue +; + +set_dac_col proc near + pushad + cli + push eax + mov dx,03dah + in al,dx + jmp @@1 +@@1: mov dx,03c8h + pop eax + out dx,al + jmp @@2 +@@2: inc dl + mov al,bh + out dx,al + jmp @@3 +@@3: mov al,bl + out dx,al + jmp @@4 +@@4: mov al,cl + out dx,al + jmp @@5 +@@5: sti + popad + ret +set_dac_col endp + + ideal + + +global Set_Palette_Register_:near + + +proc Set_Palette_Register_ near + + pushad + and cl,63 + mov bh,dl + and bh,63 + and bl,63 + call set_dac_col + popad + ret + +endp Set_Palette_Register_ + +end diff --git a/WIN32LIB/DRAWBUFF/TOBUFF.ASM b/WIN32LIB/DRAWBUFF/TOBUFF.ASM new file mode 100644 index 0000000..4b372b7 --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TOBUFF.ASM @@ -0,0 +1,292 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* Last Update : Feb 10, 1995 [jrj] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_To_Buffer C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a class member function + ARG x_pixel:DWORD ; Page X pixel coordinate. + ARG y_pixel:DWORD ; Page Y pixel coordinate. + ARG pixel_width:DWORD ; Width of region in pixels. + ARG pixel_height:DWORD ; Height of region in pixels. + ARG dest:DWORD ; the buffer to copy to + ARG buffer_size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this_object ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov esi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ buffer_size ] + jg ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + cmp [ transp ] , 0 + jnz ??forward_Blit_trans +ENDIF + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + +ENDP Buffer_To_Buffer + +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/TOPAGE.ASM b/WIN32LIB/DRAWBUFF/TOPAGE.ASM new file mode 100644 index 0000000..b02c47c --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TOPAGE.ASM @@ -0,0 +1,294 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + local scr_x : dword + local scr_y : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + + cmp [ src ] , 0 + jz ??real_out + + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + + +??forward_Blit_trans: + + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + + ENDP Buffer_To_Page +END \ No newline at end of file diff --git a/WIN32LIB/DRAWBUFF/TXTPRNT.ASM b/WIN32LIB/DRAWBUFF/TXTPRNT.ASM new file mode 100644 index 0000000..73921ca --- /dev/null +++ b/WIN32LIB/DRAWBUFF/TXTPRNT.ASM @@ -0,0 +1,502 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Print -- Assembly Buffer text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL C FontPtr:DWORD +GLOBAL C FontXSpacing:DWORD +GLOBAL C FontYSpacing:DWORD +GLOBAL C ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like object: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* Buffer_PRINT -- Assembly buffer text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Buffer_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this_object:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + local ptr_string:dword ; pointer to string + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + mov eax,[string] ; check that the string is not NULL + mov [ptr_string],eax + cmp eax,0 + jz ??done + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this_object] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch of direct draw surface + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a carry return? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Buffer_Print + +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Get_Font_Palette_Ptr:NEAR + PROC Get_Font_Palette_Ptr C near + mov eax, OFFSET ColorXlat + ret + ENDP Get_Font_Palette_Ptr + + +END + +END \ No newline at end of file diff --git a/WIN32LIB/EXAMPLE/DEFINES.H b/WIN32LIB/EXAMPLE/DEFINES.H new file mode 100644 index 0000000..9496789 --- /dev/null +++ b/WIN32LIB/EXAMPLE/DEFINES.H @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/WIN32LIB/EXAMPLE/EXTERNS.H b/WIN32LIB/EXAMPLE/EXTERNS.H new file mode 100644 index 0000000..27d38cd --- /dev/null +++ b/WIN32LIB/EXAMPLE/EXTERNS.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/WIN32LIB/EXAMPLE/FUNCTION.H b/WIN32LIB/EXAMPLE/FUNCTION.H new file mode 100644 index 0000000..654ce12 --- /dev/null +++ b/WIN32LIB/EXAMPLE/FUNCTION.H @@ -0,0 +1,50 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwlib32.h" +#include "defines.h" +#include "structs.h" +#include "externs.h" + + +/*=========================================================================*/ +/* The following prototypes are for the file: MAIN.CPP */ +/*=========================================================================*/ + +extern WORD Main_Program(WORD argc, BYTE *argv[]); + + +/*=========================================================================*/ diff --git a/WIN32LIB/EXAMPLE/GLOBALS.CPP b/WIN32LIB/EXAMPLE/GLOBALS.CPP new file mode 100644 index 0000000..259ac31 --- /dev/null +++ b/WIN32LIB/EXAMPLE/GLOBALS.CPP @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Examples * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +char NoTimer; +char NoKeyBoard; diff --git a/WIN32LIB/EXAMPLE/MAIN.CPP b/WIN32LIB/EXAMPLE/MAIN.CPP new file mode 100644 index 0000000..9a904b1 --- /dev/null +++ b/WIN32LIB/EXAMPLE/MAIN.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : libtest * + * * + * File Name : LIBTEST.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 27, 1994 * + * * + * Last Update : May 3, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Main_Program -- user-defined main routine, called from startup.c * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + + +/*************************************************************************** + * Main_Program -- user-defined main routine, called from startup.c * + * * + * INPUT: * + * WORD argc * + * UBYTE *argv[] * + * * + * OUTPUT: * + * Returns: TRUE * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1994 BR : Created. * + *=========================================================================*/ +#pragma argsused +WORD Main_Program(WORD argc, BYTE *argv[]) +{ + return (TRUE); +} + + +//////////////////////////////////// End of File ///////////////////////////////////// diff --git a/WIN32LIB/EXAMPLE/MAKEFILE b/WIN32LIB/EXAMPLE/MAKEFILE new file mode 100644 index 0000000..b1d8522 --- /dev/null +++ b/WIN32LIB/EXAMPLE/MAKEFILE @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\example\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + main.obj & + startup.obj & + globals.obj + +PROJ_LIBS = & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/EXAMPLE/MAKEFILE.BOR b/WIN32LIB/EXAMPLE/MAKEFILE.BOR new file mode 100644 index 0000000..d1a2d3e --- /dev/null +++ b/WIN32LIB/EXAMPLE/MAKEFILE.BOR @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\example\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + main.obj & + startup.obj & + globals.obj + +PROJ_LIBS = & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/EXAMPLE/MAKEFILE.WAT b/WIN32LIB/EXAMPLE/MAKEFILE.WAT new file mode 100644 index 0000000..d1a2d3e --- /dev/null +++ b/WIN32LIB/EXAMPLE/MAKEFILE.WAT @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\example\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + main.obj & + startup.obj & + globals.obj + +PROJ_LIBS = & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/EXAMPLE/STARTUP.CPP b/WIN32LIB/EXAMPLE/STARTUP.CPP new file mode 100644 index 0000000..78dc421 --- /dev/null +++ b/WIN32LIB/EXAMPLE/STARTUP.CPP @@ -0,0 +1,194 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Library startup routine. * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 14, 1994 * + * * + * Last Update : August 1, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Prog_End -- Called to shutdown Westood's library. * + * main -- Programs main entry point. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +#define GRAPHICS TRUE +void *ShapeBuffer = NULL; + + +/*************************************************************************** + * MAIN -- Programs main entry point. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/01/1994 SKB : Created. * + *=========================================================================*/ +#pragma argsused +WORD main(WORD argc, BYTE *argv[]) +{ + void *fontptr; + + /*======================================================================*/ + /* Install page fault handle in case of fatal crash. */ + /*======================================================================*/ + Install_Page_Fault_Handle (); + + /*======================================================================*/ + /* Setup the monochrome monitor for testing. */ + /*======================================================================*/ + + MonoEnabled = (Find_Argv("-MONO") ? TRUE : FALSE); + Mono_Clear_Screen(); + + /*======================================================================*/ + /* Initialize the file data table. */ + /*======================================================================*/ + WWDOS_Init(200, NULL, NULL); + + /*======================================================================*/ + /* Initialize the system font. */ + /*======================================================================*/ +#if GRAPHICS + fontptr = Load_Font("STD6P.FNT"); + if (!fontptr) { + printf("Unable to load font."); + exit(1); + } + Set_Font(fontptr); +#endif + + /*======================================================================*/ + /* Setup the timer system. */ + /*======================================================================*/ + if (Find_Argv("-NOTIME")) { + NoTimer = TRUE; + } else { + Init_Timer_System(USER_TIMER_FREQ); + NoTimer = FALSE; + } + + /*======================================================================*/ + /* Get the initial graphic mode. */ + /*======================================================================*/ +#if GRAPHICS + if ( Set_Video_Mode(MCGA_MODE) == FALSE ) + { + printf("Unable to Set Graphic Mode\n"); + exit ( 0 ) ; + } +#endif + + + /*======================================================================*/ + /* Now we get a keyboard handler. */ + /*======================================================================*/ + if (Find_Argv("-NOKEY")) { + NoKeyBoard = TRUE; + } else { + NoKeyBoard = FALSE; + Install_Keyboard_Interrupt( Get_RM_Keyboard_Address(), Get_RM_Keyboard_Size()); + + ShapeBuffer = Alloc(5000, MEM_NORMAL); + Set_Shape_Buffer(ShapeBuffer, 5000); + Install_Mouse(20, 20, 320, 200); + } + + /*======================================================================*/ + /* Give the game some variance. */ + /*======================================================================*/ + randomize(); + + /*======================================================================*/ + /* Call the user main program. */ + /*======================================================================*/ + Main_Program(argc, argv); + + /*======================================================================*/ + /* Exit gracefully. */ + /*======================================================================*/ + Prog_End(); + + return(0); +} + + + +/*************************************************************************** + * PROG_END -- Called to shutdown Westood's library. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: exit() should not be called until this has been called * + * * + * HISTORY: * + * 08/01/1994 SKB : Created. * + *=========================================================================*/ +VOID Prog_End(VOID) +{ + + /*======================================================================*/ + /* Get rid of the keyboard handler. */ + /*======================================================================*/ + if (!NoKeyBoard) { + Remove_Mouse(); + Free(ShapeBuffer); + Remove_Keyboard_Interrupt(); + } + + /*======================================================================*/ + /* Get rid of the timer system. */ + /*======================================================================*/ + if (!NoTimer) { + Remove_Timer_System(); + } + + /*======================================================================*/ + /* Restore the Video mode. */ + /*======================================================================*/ +#if GRAPHICS + Set_Video_Mode(RESET_MODE); +#endif + + /*======================================================================*/ + /* Close down the file system. */ + /*======================================================================*/ + WWDOS_Shutdown(); +} diff --git a/WIN32LIB/EXAMPLE/STRUCTS.H b/WIN32LIB/EXAMPLE/STRUCTS.H new file mode 100644 index 0000000..3184818 --- /dev/null +++ b/WIN32LIB/EXAMPLE/STRUCTS.H @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/WIN32LIB/EXAMPLE/WWLIB32.H b/WIN32LIB/EXAMPLE/WWLIB32.H new file mode 100644 index 0000000..f8cba6d --- /dev/null +++ b/WIN32LIB/EXAMPLE/WWLIB32.H @@ -0,0 +1,63 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H diff --git a/WIN32LIB/FONT/FONT.CPP b/WIN32LIB/FONT/FONT.CPP new file mode 100644 index 0000000..8458274 --- /dev/null +++ b/WIN32LIB/FONT/FONT.CPP @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + + /*************************************************************************** + ** 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 * + * * + * File Name : FONT.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : July 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Char_Pixel_Width -- Return pixel width of a character. * + * String_Pixel_Width -- Return pixel width of a string of characters. * + * Get_Next_Text_Print_XY -- Calculates X and Y given ret value from Text_P* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "font.h" +#include +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + * CHAR_PIXEL_WIDTH -- Return pixel width of a character. * + * * + * Retreives the pixel width of a character from the font width block. * + * * + * INPUT: Character. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/31/1992 DRD : Created. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +int __cdecl Char_Pixel_Width(char chr) +{ + int width; + + width = (unsigned char)*(FontWidthBlockPtr + (unsigned char)chr) + FontXSpacing; + + return(width); +} + + +/*************************************************************************** + * STRING_PIXEL_WIDTH -- Return pixel width of a string of characters. * + * * + * Calculates the pixel width of a string of characters. This uses * + * the font width block for the widths. * + * * + * INPUT: Pointer to string of characters. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/30/1992 DRD : Created. * + * 01/31/1992 DRD : Use Char_Pixel_Width. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +unsigned int __cdecl String_Pixel_Width(char const *string) +{ + WORD width; // Working accumulator of string width. + WORD largest = 0; // Largest recorded width of the string. + + if (!string) return(0); + + width = 0; + while (*string) { + if (*string == '\r') { + string++; + largest = MAX(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); // add each char's width + } + } + largest = MAX(largest, width); + return(largest); +} + + + +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * unsigned long offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& gp, unsigned long offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = gp.Get_Width() + gp.Get_XAdd(); + offset -= gp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} diff --git a/WIN32LIB/FONT/FONT.H b/WIN32LIB/FONT/FONT.H new file mode 100644 index 0000000..e2f9096 --- /dev/null +++ b/WIN32LIB/FONT/FONT.H @@ -0,0 +1,118 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Set_Font(void const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +int __cdecl Char_Pixel_Width(char chr); +unsigned int __cdecl String_Pixel_Width(char const *string); +void __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, unsigned long offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Load_Font(char const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern char FontWidth ; +extern char FontHeight; +extern char *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/WIN32LIB/FONT/LOADFONT.CPP b/WIN32LIB/FONT/LOADFONT.CPP new file mode 100644 index 0000000..039dd66 --- /dev/null +++ b/WIN32LIB/FONT/LOADFONT.CPP @@ -0,0 +1,140 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include +#include +#include + +#if(IBM) +#include +#include + +#include + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +char FontWidth = 8; +char FontHeight = 8; + +// only font.c and set_font.c use the following +char *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Load_Font(char const *name) +{ + char valid; + int fh; // DOS file handle for font file. + unsigned short size; // Size of the data in the file (-2); + char *ptr = NULL; // Pointer to newly loaded font. + + + + fh=Open_File(name,READ); + if ( fh>=0 ){ + if ( Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size , MEM_NORMAL ); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return ((void*)errno); + } + + + +#ifdef cuts + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size, MEM_NORMAL); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } +#endif + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} + +#endif diff --git a/WIN32LIB/FONT/MAKEFILE b/WIN32LIB/FONT/MAKEFILE new file mode 100644 index 0000000..93167ce --- /dev/null +++ b/WIN32LIB/FONT/MAKEFILE @@ -0,0 +1,194 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = font +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + font.obj & + loadfont.obj & + set_font.obj & + setfpal.obj & + textprnt.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/FONT/MAKEFILE.BOR b/WIN32LIB/FONT/MAKEFILE.BOR new file mode 100644 index 0000000..69a9e7a --- /dev/null +++ b/WIN32LIB/FONT/MAKEFILE.BOR @@ -0,0 +1,175 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = font +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + font.obj \ + loadfont.obj \ + set_font.obj \ + setfpal.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+font.obj & +-+loadfont.obj & +-+set_font.obj & +-+setfpal.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/FONT/SETFPAL.ASM b/WIN32LIB/FONT/SETFPAL.ASM new file mode 100644 index 0000000..8256d2d --- /dev/null +++ b/WIN32LIB/FONT/SETFPAL.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL C ColorXlat:BYTE +GLOBAL C Set_Font_Palette_Range:NEAR + + CODESEG + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + PROC Set_Font_Palette_Range C near + USES eax, ebx, ecx,edi,esi + ARG palette:DWORD + ARG start:DWORD + ARG endval:DWORD + + cld + + mov esi,[palette] + + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + dec ecx + jnz ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** + + END \ No newline at end of file diff --git a/WIN32LIB/FONT/SET_FONT.CPP b/WIN32LIB/FONT/SET_FONT.CPP new file mode 100644 index 0000000..127b603 --- /dev/null +++ b/WIN32LIB/FONT/SET_FONT.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SET_FONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Font -- Changes the default text printing font. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include + + + +/*************************************************************************** + * SET_FONT -- Changes the default text printing font. * + * * + * This routine will change the default text printing font for all * + * text output. It handles updating the system where necessary. * + * * + * INPUT: fontptr -- Pointer to the font to change to. * + * * + * OUTPUT: Returns with a pointer to the previous font. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/06/1991 JLB : Created. * + * 09/17/1991 JLB : Fixed return value bug. * + * 01/31/1992 DRD : Modified to use new font format. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Set_Font(void const *fontptr) +{ + void *oldfont; + char const *blockptr; + + oldfont = (void *) FontPtr; + + if (fontptr) { + FontPtr = (void *) fontptr; + + /* + ** Inform the system about the new font. + */ + + FontWidthBlockPtr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTWIDTHBLOCK); + blockptr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTINFOBLOCK); + FontHeight = *(blockptr + FONTINFOMAXHEIGHT); + FontWidth = *(blockptr + FONTINFOMAXWIDTH); + //Draw_Char_Setup(); + +#if FALSE + WindowLines = WinH / FontHeight; + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / FontWidth; +#endif + } + + return(oldfont); +} diff --git a/WIN32LIB/FONT/TEXTPRNT.ASM b/WIN32LIB/FONT/TEXTPRNT.ASM new file mode 100644 index 0000000..730a3f0 --- /dev/null +++ b/WIN32LIB/FONT/TEXTPRNT.ASM @@ -0,0 +1,436 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + + +GLOBAL FontPtr:DWORD +GLOBAL Text_Print:NEAR + +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + +LOCALS ?? + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* TEXT_PRINT -- Assembly text print routine. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/28/1994 SKB : Created. * +;*=========================================================================* + PROC Text_Print C near + USES ebx,ecx,edx,esi,edi + ARG vpselector:WORD + ARG vpoffset:DWORD + ARG vpwidth:DWORD + ARG vpheight:DWORD + ARG vpxadd:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + movzx eax,[vpselector] + mov es,ax ; Set up selector to write to. + + mov eax,[vpwidth] ; get the width of the viewport + add eax,[vpxadd] ; add amount to add to get to left edge of next line. + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[vpoffset] ; get start of the viewport + add edi,eax ; add x,y position to start of vp to get starting row address. + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + ; Now go into the line feed code..... + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Text_Print + + + +;*********************************************************** + + END diff --git a/WIN32LIB/IFF/FILEPCX.H b/WIN32LIB/IFF/FILEPCX.H new file mode 100644 index 0000000..3091ccb --- /dev/null +++ b/WIN32LIB/IFF/FILEPCX.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/WIN32LIB/IFF/IFF.CPP b/WIN32LIB/IFF/IFF.CPP new file mode 100644 index 0000000..794881d --- /dev/null +++ b/WIN32LIB/IFF/IFF.CPP @@ -0,0 +1,328 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +#ifdef MIN +#undef MIN +#endif + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +int __cdecl Open_Iff_File(char const *filename) +{ + int fh; // File handle. + long type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = WW_ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +void __cdecl Close_Iff_File(int fh) +{ + if (fh != WW_ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: int file handle to open IFF file, long id to get size of * + * * + * OUTPUT: long size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id) +{ + long form; // Chunk iff form name. + long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize) +{ + long form; // Chunk iff form name. + unsigned long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length) +{ + long pos; // Current position in the IFF file. + long oldpos; // Record of start of chunk offset. + long endpos; // end of file offset before we write our data + long value; + BOOL odd; // Is length odd? + char pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (short)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size long. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/WIN32LIB/IFF/IFF.H b/WIN32LIB/IFF/IFF.H new file mode 100644 index 0000000..851eb6b --- /dev/null +++ b/WIN32LIB/IFF/IFF.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((long) ((long) d << 24) | ((long) c << 16) | ((long) b << 8) | (long)(a)) +#define IFFize_WORD(a) Reverse_Word(a) +#define IFFize_LONG(a) Reverse_Long(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + char Method; // Compression method (CompressionType). + char pad; // Reserved pad byte (always 0). + long Size; // Size of the uncompressed data. + short Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +int __cdecl Open_Iff_File(char const *filename); +void __cdecl Close_Iff_File(int fh); +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id); +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize); +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size); +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size); +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags); +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data=NULL); +unsigned long __cdecl Uncompress_Data(void const *src, void *dst); +void __cdecl Set_Uncomp_Buffer(int buffer_segment, int size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern void __cdecl Pack_2_Plane(void *buffer, void * pageptr, int planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Compress(void *source, void *dest, unsigned long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H diff --git a/WIN32LIB/IFF/LCWCOMP.ASM b/WIN32LIB/IFF/LCWCOMP.ASM new file mode 100644 index 0000000..c7deecc --- /dev/null +++ b/WIN32LIB/IFF/LCWCOMP.ASM @@ -0,0 +1,286 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwcomp.asm 1.1 1994/04/11 15:31:10 jeff_wilson Exp $ +;*************************************************************************** +;** 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: * +;* * +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL LCW_Compress :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_Compress 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_Compress + + +END + \ No newline at end of file diff --git a/WIN32LIB/IFF/LCWUNCMP.ASM b/WIN32LIB/IFF/LCWUNCMP.ASM new file mode 100644 index 0000000..f9e28bb --- /dev/null +++ b/WIN32LIB/IFF/LCWUNCMP.ASM @@ -0,0 +1,292 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwuncmp.asm 1.1 1994/04/11 15:31:21 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +PROC LCW_Uncompress C near + + USES ebx,ecx,edx,edi,esi + + ARG source:DWORD + ARG dest:DWORD + ARG length:DWORD +;LOCALS + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + LOCAL lastcom:DWORD + LOCAL lastcom1:DWORD + + + mov edi,[dest] + mov esi,[source] + mov edx,[length] + +; +; +; uncompress 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 copy copy w1 bytes from offset w2 +; n=11111110,w1,b1 long run run byte b1 for w1 bytes +; n=10000000 end end of data reached +; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + +??loop: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short ??out ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short ??notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp ??loop + +??notshort: + test al,40h ; is it a length? + jne short ??notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short ??out ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp ??exit + +??notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short ??notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runlenok: + test ecx,0ffe0h + jnz ??dont_use_stosb + rep stosb + jmp ??loop + + +??dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz ??aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + +??aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz ??loop + mov ecx,edx + rep stosb + jmp ??loop + + + + + + +??notrunlength: + cmp al,0FFh ; is it a long run? + jne short ??notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + +??notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short ??runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runok: + test ecx,0ffe0h + jnz ??dont_use_movsb + rep movsb + jmp ??loop + + + + +??dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja ??use_movsb + + test edi,3 + jz ??aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + +??aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx +??use_movsb: + rep movsb + jmp ??loop + + + + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + ret + +ENDP LCW_Uncompress + +;*********************************************************** + + + END + diff --git a/WIN32LIB/IFF/LOAD.CPP b/WIN32LIB/IFF/LOAD.CPP new file mode 100644 index 0000000..98f56d0 --- /dev/null +++ b/WIN32LIB/IFF/LOAD.CPP @@ -0,0 +1,378 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include +#include +#include +#include + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + void *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * void * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize=0; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((short *)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * void *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_Word(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((short*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_Word(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Uncompress_Data(void const *src, void *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_Long(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_Word(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((void *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((void *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((void *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((void *) src, (void *) dst, (unsigned long) uncomp_size); + break; + + } + + return(uncomp_size); +} + + diff --git a/WIN32LIB/IFF/LOADPCX.CPP b/WIN32LIB/IFF/LOADPCX.CPP new file mode 100644 index 0000000..8be7f05 --- /dev/null +++ b/WIN32LIB/IFF/LOADPCX.CPP @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : IFF * + * * + * File Name : LOADPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 3, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * GraphicBufferClass* Read_PCX_File (char* name, void *Buff, long size ); * + * int Get_PCX_Palette (char * name, void& palette ) * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optinal, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointd by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Read_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size) +{ + unsigned i , j ; + unsigned rle ; + unsigned color ; + unsigned scan_pos ; + char * file_ptr ; + int width ; + int height ; + int file_handle ; + char * buffer ; + PCX_HEADER header ; + RGB * pal ; + char pool [ POOL_SIZE ] ; + GraphicBufferClass * pic ; + + // Open file name + file_handle = Open_File ( name , READ ) ; + if ( file_handle == WW_ERROR ) return NULL ; + + Read_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + if ( header.id != 10 && header.version != 5 && + header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1 ; + height = header.height - header.y + 1 ; + + if ( Buff ) { + buffer = ( char * ) Buff; + i = Size / width; + height = MIN ( i - 1, height); + pic = new GraphicBufferClass( width, height, buffer ,Size); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } else { + pic = new GraphicBufferClass( width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } + + buffer = (char *) pic->Get_Buffer() ; + file_ptr = pool ; + Read_File ( file_handle, pool , POOL_SIZE ) ; + + if ( header.byte_per_line != width ) + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ) ; + i += rle ; + } else * ( buffer + scan_pos + i ++ ) = (char)rle ; + } + if ( i == width ) + rle = READ_CHAR () ; +// if ( rle > 192 ) rle = READ_CHAR (); + } + + + else for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ) ; + i += rle ; + } else * ( buffer + i ++ ) = (char)rle ; + } + + if ( Palette ) { + //Seek_File ( file_handle , - 256 * sizeof ( RGB ) , SEEK_END ) ; + Seek_File ( file_handle , 256 * sizeof ( RGB ) , SEEK_END ) ; + Read_File ( file_handle, Palette , 256L * sizeof ( RGB )) ; + + pal = ( RGB * ) Palette ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red >>= 2 ; + pal -> green >>= 2 ; + pal -> blue >>= 2 ; + pal ++ ; + } + } + Close_File (file_handle) ; + return pic ; +} + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff, * + * char* palette) * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * Buff is a pointer to a BufferClass the will hold the pcx file * + * at location pointd by Buffer; * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette) +{ + return Read_PCX_File(name, palette, (void*)Buff.Get_Buffer(), Buff.Get_Size()); +} \ No newline at end of file diff --git a/WIN32LIB/IFF/LOADPICT.CPP b/WIN32LIB/IFF/LOADPICT.CPP new file mode 100644 index 0000000..4b38532 --- /dev/null +++ b/WIN32LIB/IFF/LOADPICT.CPP @@ -0,0 +1,552 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/loadpict.cpp 1.1 1994/04/20 14:38:08 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFFEXTRA.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 11, 1991 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * ILBM_To_Amiga -- Convert ILBM to bitplane Amiga format. * + * ILBM_To_MCGA -- Converts ILBM picture into MCGA format. * + * PBM_To_Amiga -- Converts a PBM picture into Amiga format. * + * Load_Picture -- Loads a picture file (CPS or LBM format). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include // For Alloc. + +#if(IBM) +#include +#endif + +// Since we are not currently using AMIGA, this has been put in to +// give us back some code space. If it is needed for a utility, +// this module should be recompiled with that utility and set the +// define to TRUE. +#define MAKE_AMIGA_ART FALSE + +/* +** An IFF picture file can have one of two formats: +** ILBM - InterLeaved Bit Map +** PBM - Packed Bit Map +*/ +typedef enum { + FORM_ILBM, + FORM_PBM +} IFFForm_Type; + +/* +** These are the various chunks that compose an IFF picture file. +*/ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_ILBM MAKE_ID('I','L','B','M') +#define ID_PBM MAKE_ID('P','B','M',' ') +#define ID_CMAP MAKE_ID('C','M','A','P') +#define ID_BODY MAKE_ID('B','O','D','Y') +#define ID_BMHD MAKE_ID('B','M','H','D') + + +/* +** The BMHD (Bit Map HeaDer) chunk in an IFF picture file contains the +** information necessary to extract that picture from the BODY chunk. +** It also indicates the size and depth of the source art. +*/ +typedef struct { + unsigned short W, H; // Raster width and height in pixels. + short X, Y; // Pixel postion for this image. + char BPlanes; // Number of bitplanes. + unsigned char Masking; // Masking control byte. + // 0 = No masking. + // 1 = Has a mask. + // 2 = Has transparent color. + // 3 = Lasso. + unsigned char Compression; // Compression method. + // 0 = No compression. + // 1 = Byte run compression. + char pad; + unsigned short Transparent; // Transparent color number. + unsigned char XAspect, // Pixel aspect ratio of source art. + YAspect; + short PageWidth, // Source 'page' size in pixels. + PageHeight; +} BitMapHeader_Type; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void __cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, int planes); +PRIVATE void __cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes); +PRIVATE void __cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * ILBM_TO_MCGA -- Converts ILBM picture into MCGA format. * + * * + * This converts an ILBM picture (typical of DPaint LBM files) and * + * converts it to MCGA mode (byte per pixel). This function would be * + * used after the body of an ILBM picture is loaded. Because the * + * number of bitplanes can vary greatly, it is necessary to pass the * + * bitplane count to this function. The size (320 by 200) of the * + * source picture is presumed. * + * * + * INPUT: src - Buffer number for source ILBM picture. * + * * + * dest - Buffer number for place to put MCGA format. * + * * + * planes- The number of bitplanes in the ILBM picture. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + *=========================================================================*/ +PRIVATE void __cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, int planes) +{ + char *source; // Source pointer. + char *destination; // Destination pointer. + int index,j,i; // Working index values. + int bplane; // Bit plane counter. + char bytes[8]; // Byte array holding max bitplanes (8). + char value; // Composed byte(pixel) value. + + source = (char *) src.Get_Buffer(); + destination = (char *) dest.Get_Buffer(); + + memset(bytes, '\0', 8); // Makes sure upper bits will be clear. + + // Each row is grouped and processed together. + + for (index = 0; index < 200 /*bmhd.H*/; index++) { + + // Process each line in groups of 8 bytes. + + for (j = 0; j < 40 /*(bmhd.W>>3)*/; j++) { + + // Get the bitplane bytes. + + for (bplane = 0; bplane < planes /*bmhd.BPlanes*/; bplane++) { + bytes[bplane] = *(source + (bplane * 40 /*(bmhd.W>>3)*/)); + } + source++; + + // Roll the bits out to create 8 pixels (by bytes). + for (i = 0; i < 8; i++) { + + // 8 bits per byte. + value = 0; + for (bplane = planes - 1/*bmhd.BPlanes-1*/; bplane >= 0; bplane--) { + value <<= 1; // Make room for next bit. + if (bytes[bplane] & 0x80) value |= 1; // Set the bit. + bytes[bplane] <<= 1; + } + *destination++ = value; // Output the pixel byte. + } + } + + // Advance to next scan line. + source += 40 /* (bmhd.W >> 3)*/ * (planes /* bmhd.BPlanes */ - 1); + } +} + + +/*************************************************************************** + * ILBM_TO_AMIGA -- Convert ILBM to bitplane Amiga format. * + * * + * This converts an InterLeaved BitMap picture into Amiga bitplane * + * format (8K per bitplane). The data of an ILBM picture is controlled * + * by the number of bitplanes it contains. The bitplane count is * + * passed into this program. * + * * + * INPUT: src - Buffer number for source ILBM picture data. * + * * + * dest - Buffer number for destination Amiga picture data. * + * * + * planes- The number of bitplanes in the source ILBM data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE void __cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes) +{ + int row; // Working row counter. + int bp; // Working bitplane counter. + char *srcptr, // Source buffer pointer. + *dstptr; // Destination buffer pointer. + + srcptr = (char *) src.Get_Buffer(); // Source buffer pointer. + dstptr = (char *) dest.Get_Buffer(); // Destination buffer pointer. + + for (row = 0; row < 200; row++) { + for (bp = 0; bp < planes; bp++) { + Mem_Copy(srcptr,dstptr+(8000*bp),40); + srcptr += 40; + } + dstptr += 40; + } +} +#endif + + +/*************************************************************************** + * PBM_TO_AMIGA -- Converts a PBM picture into Amiga format. * + * * + * This converts a PBM (Packed Bit Map) MCGA picture into Amiga * + * bitplane format. A PBM picture presumes 8 bitplanes, but this * + * can be controlled by the 'plane' parameter passed in. * + * * + * INPUT: src - Buffer number for the source PBM data. * + * * + * dest - Buffer number to place the Amiga format picture. * + * * + * planes- The number of bitplanes to extract from the PBM source * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE void __cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes) +{ + int row, // Working row counter. + col, // Working column (by byte) counter. + bit; // Working bitplane counter. + unsigned char *destptr, // Destination byte pointer. + *srcptr; // Source byte pointer. + unsigned char value; // Working input MCGA pixel number. + + + destptr = (unsigned char *) dest.Get_Buffer(); + srcptr = (unsigned char *) src.Get_Buffer(); + + memset(destptr, 0, 32000); + memset(destptr+32000, 0, 32000); + + for (row = 0; row < 200; row++) { + + for (col = 0; col < 320; col++) { + value = *srcptr++; + + for (bit = 0; bit < planes; bit++) { + if (value & (0x01 << bit)) { + destptr[(short)((8000L * (long)bit) + (col>>3))] |= 0x80 >> (col & 0x07); + } + } + } + + destptr += 40; + } +} +#endif + +/*************************************************************************** + * LOAD_PICTURE -- Loads a picture file (CPS or LBM format). * + * * + * This loads a picture file into a page buffer. The loaded file will * + * be in MCGA or Amiga mode as requested. Supported source formats * + * are CPS or all forms of IFF dpaint files. * + * * + * INPUT: filename - Source filename. The only files that are * + * processed as IFF are those files that end with * + * ".LBM". * + * * + * loadbuf - Buffer type number for the temporary loading * + * buffer. It will be trashed. * + * * + * destbuf - Buffer type number for the picture to be placed. * + * * + * palette - Palette buffer pointer. If this value is NULL * + * then no palette is loaded. * + * * + * format - Desired destination format. * + * BM_AMIGA - Destination buffer will contain the * + * picture in bitplane format (Amiga). * + * The buffer will contain data equal to * + * 8K times the number of bit planes. * + * * + * BM_MCGA - Destination buffer will contain the * + * picture in MCGA format (byte per pixel).* + * The buffer will be 64K in size. * + * * + * OUTPUT: int number of bitplanes read into the dest buffer * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 05/20/1991 JLB : Handles Amiga and IBM destination formats. * + *=========================================================================*/ +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette, PicturePlaneType format) +{ + int fh; // Input file handle. + long ifftype; // Iff form type. + int counter; // Count of the bytes decompressed. + int value; // Working compression code value. + int len; // int sized length value. + int index; // Working index values. + BitMapHeader_Type bmhd; // BMHD chunk data. + IFFForm_Type formtype; // ILBM, PBM. + char *src; // Working source body pointer. + char *dest; // Working destination body pointer. + + + //len = strlen(filename); + //strupr(filename); + + fh = Open_File(filename,READ); + if (fh == WW_ERROR) return(FALSE); + Read_File(fh,&ifftype,4L); + Close_File(fh); + + if (ifftype != ID_FORM) { + return((int)Load_Uncompress(filename, scratchbuf, destbuf, palette ) / 8000 ) ; + } else { + + fh = Open_Iff_File(filename); // Opens and checks for IFF form. + if (fh == WW_ERROR) return(FALSE); + + Read_File(fh, &ifftype, 4L); + if (ifftype == ID_ILBM) { + formtype = FORM_ILBM; // Inter-Leaved Bit Map. + } else { + if (ifftype == ID_PBM) { + formtype = FORM_PBM; // Packed Bit Map. + } else { + return FALSE; // Not a recognizable picture file. + } + } + + // Load the BMHD chunk. + if (Read_Iff_Chunk(fh,ID_BMHD,(char*)&bmhd,sizeof(BitMapHeader_Type))) { + + #if(IBM) + // Perform necessary IBM conversions to the data. + bmhd.W = Reverse_Short(bmhd.W); + bmhd.H = Reverse_Short(bmhd.H); + bmhd.X = Reverse_Short(bmhd.X); + bmhd.Y = Reverse_Short(bmhd.Y); + + // this is a mistake Xaspect and YAspect are char type + // bmhd.XAspect = Reverse_Short(bmhd.XAspect); + // bmhd.YAspect = Reverse_Short(bmhd.YAspect); + value = bmhd.XAspect ; + bmhd.XAspect = bmhd.YAspect ; + bmhd.YAspect = ( unsigned char ) value ; + + bmhd.PageWidth = Reverse_Short(bmhd.PageWidth); + bmhd.PageHeight = Reverse_Short(bmhd.PageHeight); + #endif + + if (bmhd.Masking > 2) return FALSE; // Don't allow brushes. + if (bmhd.Compression > 1) return FALSE; // Unknown compression. + + } else { + return FALSE; // Unable to read the required BMHD chunk. + } + + // Load the palette if asked. + if (palette) + { + int pbytes ; // Number of CMAP bytes required. + unsigned char color; // Palette color value. + unsigned char *paletteptr; // Allocated buffer for palette conversions. + unsigned char *source; // Scratch source CMAP data pointer. + unsigned char *dest2; // Scratch destination palette pointer. + + // Number of CMAP bytes that are needed. + pbytes = (1 << bmhd.BPlanes) * 3; + + // Allocate the temporary palette buffer. + paletteptr = (unsigned char *)Alloc(pbytes, MEM_CLEAR); + source = paletteptr; + dest2 = palette; + + // Read in only the bytes that are needed. + pbytes = (int)Read_Iff_Chunk(fh, ID_CMAP, (char *) paletteptr, pbytes); + + if (pbytes) { + + /* + ** CMAP to machine specific palette conversion code. Conversion + ** goes from CMAP three bytes per color register to the machine + ** specific form. + */ + switch(format) { + default: + case BM_MCGA: + // Convert CMAP to IBM MCGA palette form. + for (index = 0; index < pbytes; index++) { + *dest2++ = *source++ >> 2; + } + break; +#if MAKE_AMIGA_ART + + case BM_AMIGA: + // Convert CMAP to Amiga nibble packed palette form. + for (index = 0; index < pbytes; index += 3) { + *dest2++ = *(source++) >> 4; + color = (*(source++) & 0xf0); + color += *(source++) >> 4; + *dest2++ = color; + } + + break; +#endif + } + } + + Free(paletteptr); + } + + + // Load in BODY chunk. + dest = (char *) scratchbuf.Get_Buffer(); + src = (char *) destbuf.Get_Buffer(); + + if (Read_Iff_Chunk(fh, ID_BODY, src, destbuf.Get_Size())) + { + for (index = 0; index < (short)bmhd.H; index++) + { + /* Height of source */ + // Transfer (possibly uncompress) one row of data. + // PBM or ILBM reader. Bytes per row (all bitplanes). + + counter = bmhd.BPlanes * (bmhd.W >> 3); + + // If there is a mask then there is one more bitplane. + if (bmhd.Masking == 1) + counter += bmhd.W >> 3 ; + + if (bmhd.Compression == 1) + { + // The data is compressed. + // Decompress one scanline (all bitplanes) at a time. + while (counter) + { + value = ( signed char ) *src++; // Decompression code. + if (value == -128) continue; // NOOP code. + + if (value >= 0) + { + // Copy N+1 bytes. + len = ((short) value) + 1; + + // Ignore the masking bitplane. + if ( bmhd.Masking != 1 || + (bmhd.Masking==1 && counter > ((short)bmhd.W >> 3) ) ) + { + memcpy(dest, src, len); + dest += len; + } + counter -= len; + src += len; + + } + else + { + // Replicate -N+1 bytes. + len = (-((short) value)) + 1; + value = *src++; + + // Ignore the masking bitplane. + if (bmhd.Masking != 1 || (bmhd.Masking==1 && counter > ((short)bmhd.W >> 3))) + { + memset(dest,value,len); + dest += len; + } + counter -= len; + } + } + } + + else + { + // Plain data is just copied. + memcpy(dest,src,counter); + dest += counter; + src += counter; + } + } + + /* + ** Perform necessary conversions to the data in order to reach + ** the desired format. + */ + switch (format) { + default: + case BM_MCGA: // Byte per pixel desired. + if (formtype == FORM_ILBM) { + ILBM_To_MCGA(scratchbuf, destbuf, bmhd.BPlanes); + } else { + Mem_Copy(scratchbuf.Get_Buffer(), destbuf.Get_Buffer(), 64000L); + } + break; + +#if MAKE_AMIGA_ART + case BM_AMIGA: // Bitplane format desired. + if (formtype == FORM_ILBM) { + ILBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } else { + PBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } + break; +#endif + } + } + + Close_Iff_File(fh); + } + + return((short)bmhd.BPlanes); // Loaded the picture successfully. +} diff --git a/WIN32LIB/IFF/MAKEFILE b/WIN32LIB/IFF/MAKEFILE new file mode 100644 index 0000000..6a77731 --- /dev/null +++ b/WIN32LIB/IFF/MAKEFILE @@ -0,0 +1,189 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = iff +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iff.obj & + load.obj & + loadpict.obj & + writelbm.obj & + lcwcomp.obj & + lcwuncmp.obj & + loadpcx.obj & + writepcx.obj & + pack2pln.obj + + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\srcdEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/IFF/MAKEFILE.BOR b/WIN32LIB/IFF/MAKEFILE.BOR new file mode 100644 index 0000000..e317451 --- /dev/null +++ b/WIN32LIB/IFF/MAKEFILE.BOR @@ -0,0 +1,181 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = iff +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + iff.obj \ + load.obj \ + loadpict.obj \ + writelbm.obj \ + lcwcomp.obj \ + lcwuncmp.obj \ + pack2pln.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-iff.obj \ ++-load.obj \ ++-loadpict.obj \ ++-writelbm.obj \ ++-lcwcomp.obj \ ++-lcwuncmp.obj \ ++-pack2pln.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/IFF/MAKEFILE.WAT b/WIN32LIB/IFF/MAKEFILE.WAT new file mode 100644 index 0000000..97778ba --- /dev/null +++ b/WIN32LIB/IFF/MAKEFILE.WAT @@ -0,0 +1,187 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = iff +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iff.obj & + load.obj & + loadpict.obj & + writelbm.obj & + lcwcomp.obj & + lcwuncmp.obj & + pack2pln.obj + + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\srcdEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/IFF/PACK2PLN.ASM b/WIN32LIB/IFF/PACK2PLN.ASM new file mode 100644 index 0000000..59ffa71 --- /dev/null +++ b/WIN32LIB/IFF/PACK2PLN.ASM @@ -0,0 +1,132 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/file/rcs/pack2pln.asm 1.1 1994/04/22 18:07:46 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : PACK2PLN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : November 20, 1991 * +;* * +;* Last Update : April 22, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Pack_2_Plane:NEAR + + CODESEG + +;*************************************************************************** +;* PACK_2_PLANE -- packed to planar scanline conversion * +;* * +;* INPUT: BYTE *buffer (far) -- pointer to planar output buffer * +;* BYTE *pageptr (far) -- pointer to current row in packed page * +;* WORD planebit -- current bit used in plane -- use only low byte * +;* * +;* OUTPUT: * +;* Return result in buffer. * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 11/20/1991 SB : Created. * +;* 04/22/1994 SKB : Converted to 32 bit library. * +;*=========================================================================* +; * +; This is the original function that is converted to asm +; +;PRIVATE VOID Pack_2_Plane(UBYTE * buffer, BYTE * pageptr, BYTE planebit) +;{ +; WORD currbit=0x80; // current bit to be written to +; WORD pixel; // current pixel in row used as a counter; +; +; buffer--; // will be incremented at the start +; for (currbit = 0, pixel = 0; pixel < 320; pixel++) { +; if (!currbit) { +; currbit = 0x80; // reset bit 7 +; buffer++; // go to next byte in buffer +; *buffer = 0; // clear byte so we only need to set bits needed +; } +; if (planebit & *pageptr++) +; *buffer |= currbit; // set bit in destination if plane was set is source +; +; currbit >>= 1; // shift destination bit one right +; } +;} + +PROC Pack_2_Plane C NEAR + USES ebx,ecx,esi,edi + ARG buffer:DWORD + ARG page:DWORD + ARG planebit:WORD + + + mov edi,[buffer] + mov esi,[page] + + mov ax,[planebit] ; move bit set for current plane (planebit) to ax + ; the low byte will only be used + + mov ecx,320d ; set counter to 320 columns (320x200 picture) + mov ah,80h ; set bit 7 of current_bit + dec edi ; this will get incremented at the start + +??top_loop: ; while (columns left) + cmp ah,80h ; if current_bit is bit 7 + jnz short ??same_dest + ; Then + inc edi ; buffer++ increment pointer + mov [BYTE PTR edi],0 ; *buffer = 0 + +??same_dest: ; EndIf + mov bl,al + and bl,[esi] ; if (planebit & *pageptr) + jz short ??no_set_bit + + or [BYTE PTR edi],ah ; Then *buffer |= current_bit + +??no_set_bit: + inc esi ; pageptr++ goto next in source byte + ror ah,1 ; rotate current_bit right one + dec ecx ; + jnz ??top_loop + + ret + + ENDP Pack_2_Plane + + END + \ No newline at end of file diff --git a/WIN32LIB/IFF/WRITELBM.CPP b/WIN32LIB/IFF/WRITELBM.CPP new file mode 100644 index 0000000..d3c42c0 --- /dev/null +++ b/WIN32LIB/IFF/WRITELBM.CPP @@ -0,0 +1,423 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/writelbm.cpp 1.1 1994/04/20 14:38:57 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Write LBM File * + * * + * File Name : writelbm.c * + * * + * Programmer : Scott Bowen * + * * + * Start Date : November 18, 1991 * + * * + * Last Update : November 19, 1991 [SB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Line -- convert one plane of one row to a packed plane * + * Write_BMHD -- writes out the bit map header (LocalHeader) * + * Write_Body -- writes out compressed data in an LBM file * + * Write_CMAP -- Writes out CMAP (palette) information * + * Write_LBM_File -- Writes out a file in LBM format * + * Write_Row -- compresses and writes a row plane to .lbm file * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +// At the end of this file there is an IFF definition for a .LBM file. + +#include "iff.h" +#include "file.h" +#include +#include +#include + + + // A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size +typedef struct { + unsigned short w, h; // raster width & height in pixels + unsigned short x, y; // position for this image + unsigned char planes; // # source bitplanes + unsigned char masking; // masking technique + unsigned char compression; // compression algoithm + unsigned char pad1; // UNUSED. For consistency, put 0 here. + unsigned short transcolor; // transparent "color number" + unsigned char xaspect, yaspect; // aspect ratio, a rational number x/y + unsigned short pagewidth, pageheight; // source "page" size in pixels +} BitMapHeaderType; + + +// All values in LocalHeader are always the same except planes. This is set in Write_BMHD +// the short values must be in low-high order for compatibility. + +PRIVATE BitMapHeaderType LocalHeader = { + 0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask + 1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect + 0x4001, 0xC800 }; // pagewidth, pageheight + + + // Used to verify that the write of the header was valid +#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L) + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE long Write_BMHD(int lbmhandle, int bitplanes); +PRIVATE long Write_CMAP(int lbmhandle, unsigned char * palette, int bitplanes); +PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes); +PRIVATE long Write_Row(int lbmhandle, unsigned char *buffer); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * WRITE_LBM_FILE -- Writes out a file in LBM format * + * * + * INPUT: int lbmhandle -- lbm file handle already opened by caller * + * BufferClass buff -- buff where MCGA picture is * + * int bitplane -- number of bitplanes to convert to * + * char *palette -- pointer to palette for buff * + * * + * OUTPUT: Returns BOOL -- successfull or not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/18/1991 SB : Created. * + *=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette) +{ + long filesize; + + + Seek_File(lbmhandle, 0L, SEEK_SET); // goto beginning of file + + Write_File(lbmhandle, "FORM????ILBM", 12L); // First 12 bytes of all .lbm files + // size is unkown so write ???? + filesize = 12L; // 4 bytes for "ILBM" + + filesize += Write_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size) + filesize += Write_CMAP(lbmhandle, palette, bitplanes); // write out CMAP + + // Write out the body, or compressed picture image. This size will depend + // on the compression, but the value passed back is what the compressor + // assumed was written to file + + filesize += Write_BODY(lbmhandle, buff, bitplanes); + + // Verify that we were able to write out the file without running out of space + if (Seek_File(lbmhandle, 0L, SEEK_END) != filesize) { + return(FALSE); + } + + Seek_File(lbmhandle, 4L, SEEK_SET); // goto beginning of file + filesize = Reverse_Long(filesize - 8L); // - 8 because of "FORM" + WORD (size) + Write_File(lbmhandle, (char *) &filesize, 4L); // patch in filesize + + return(TRUE); +} + + +/*************************************************************************** + * WRITE_BMHD -- writes out the bit map header (LocalHeader) * + * * + * INPUT: int lbmhandle -- file handle for lbm file * + * int pitplanes -- number of bitplanes to write out * + * * + * OUTPUT: long number of bytes hopefully written out to .LBM file * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +PRIVATE long Write_BMHD(int lbmhandle, int bitplanes) +{ + long size; + + Write_File(lbmhandle, "BMHD", 4L); // write out chunk title + size = Reverse_Long(sizeof(LocalHeader)); // write out size of LocalHeader chunk + Write_File(lbmhandle, (char *) &size, 4L); + + LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader + + // Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure + + return(Write_File(lbmhandle, (char *) &LocalHeader, + (sizeof(LocalHeader) + 1) & 0xFFFE) + 8L); +} + + +/*************************************************************************** + * WRITE_CMAP -- Writes out CMAP (palette) information * + * * + * * + * INPUT: int lbmhandle -- file handle of lbm file * + * char * palette -- pointer to paletter information * + * int bitplanes -- used to figure out size of palette * + * * + * OUTPUT: long number of bytes that should have been written out to .LBM. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE long Write_CMAP(int lbmhandle, unsigned char * palette, int bitplanes) +{ + int color, r, g, b, colors; + long size; + unsigned char *pal_ptr; + char rgb[3]; + + + Write_File(lbmhandle, "CMAP", 4L); // write out palette info + colors = 1 << bitplanes; // colors = 2 to the bitplanes + size = Reverse_Long(colors * 3L); // size = colors * 3 guns + + Write_File(lbmhandle, (char *) &size, 4L); + + for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color + + if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values + r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM + } // so LBM_val = (DP_val * 4) | 3 if DP_val != 0 + if ((g = *pal_ptr++) != 0) { + g = (g << 2) | 0x03; + } + if ((b = *pal_ptr++) != 0) { + b = (b << 2) | 0x03; + } + rgb[0] = r; // assign gun values to an array to write out + rgb[1] = g; + rgb[2] = b; + + Write_File(lbmhandle, rgb, 3L); + } + // size = colors * 3 + return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + WORD (size) +} + + +/*************************************************************************** + * WRITE_BODY -- writes out compressed data in an LBM file * + * * + * INPUT: int lbmhandle -- file handle of lbm file * + * * + * OUTPUT: long - number of byte written * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes) +{ + long bodysize = 0; + long actualsize; + long size; + int planebit; + int line, plane; + unsigned char buffer[40]; + unsigned char *buffptr; + + Write_File(lbmhandle, "BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize + + buffptr = (unsigned char *) buff.Get_Buffer(); // point to beginning of buff + + for (line = 0; line < 200; line++) { + planebit = 1; // start with bit 1 set + + for (plane = 0; plane < bitplanes; plane++) { + Pack_2_Plane(buffer, buffptr, planebit); // convert to planar + bodysize += Write_Row(lbmhandle, buffer); // write to to the BODY in the LBM + + planebit <<= 1; // set next bit + } + + buffptr += 320; // row size is 320 + } + + actualsize = bodysize + (bodysize&0x01); + + if (actualsize != bodysize) { + Write_File(lbmhandle, buffer, 1); // Padd the block. + } + + Seek_File(lbmhandle, -(actualsize + 4L), SEEK_CUR); // Patch in chunksize + size = Reverse_Long(bodysize); + Write_File(lbmhandle, (char *) &size ,4L); + + return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes +} + +/*************************************************************************** + * WRITE_ROW -- compresses and writes a row plane to .lbm file * + * * + * INPUT: int lbmhandle -- lbm file handle * + * unsigned char *buffer -- pointer to buffer to be written out * + * * + * OUTPUT: long size of chunk that should have been written out * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +// this algorithm was taken from WILBM.c written by EA that was in the +// 1985 yearbook. This is the compression method that DP.EXE uses. +// Change only if DP.EXE changes. + +PRIVATE long Write_Row(int lbmhandle, unsigned char *buffer) +{ + int i; + int chunksize = 0; + int dataLength = 40; // 320 rows / 8 ( 1 plane per row) + unsigned char repCode, current, curr_plus_2; + unsigned char *buffptr; + + while (dataLength) { + + // If at least 2 more bytes and they are equal, then replicate + + if ((dataLength >= 2) && (buffer[0] == buffer[1])) { + buffptr = buffer; + for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) { + if (*buffptr != buffptr[1]) { + break; + } + buffptr++; + } + i++; + repCode = -i + 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as -count+1 + Write_File(lbmhandle, buffer, 1L); // Write byte to replicate + buffer += i; + dataLength -= i; + chunksize += 2; + + } + else { // Copy literally till 3 byte run or two 2 byte runs found + + for (i = 0; (i <= 128) && (i < dataLength); i++) { + current = buffer[i]; + curr_plus_2 = buffer[i + 2]; + + if (i == dataLength - 1) + continue; + if (current != buffer[i + 1]) + continue; + if (i == dataLength - 2) + continue; + if (current == curr_plus_2) + break; + if (i == dataLength - 3) + continue; + if (curr_plus_2 == buffer[i + 3]) + break; + } + repCode = i - 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as count-1 + Write_File(lbmhandle, buffer, (long) i); // Write 'count' bytes + buffer += i; + dataLength -= i; + chunksize += i + 1; + } + } // end while + + return(chunksize); +} + + +#if(FALSE) + +This is a definition of a DPII .LBM file. +Below this definition are differences in DPIIe .LMB files. + +Created by : Scott K. Bowen Nov 18, 1991 + +Start with .LBM to read definition : + +.LBM -> "FORM" + FILESIZE + "ILMB" + CHUNKS + +BITPLANES -> (word) // number of bit planes used +BLUE -> (byte) // blue color gun value +BMHD -> "BMHD" + CHUNKSIZE + CONTEXT +BODY -> + +CHUNKS -> BMHD | BODY | CMAP | DPPV | CRNG | ???? +CHUNKSIZE -> (long) // size of chunk not including header or size. +CMAP -> "CMAP" + CHUNKSIZE + PALETTE_INFO +COMPRESS_METHOD -> (byte) // compression method used +CONTEXT -> WIDTH + HEIGHT + POSX + POSY + #_BITPLANES + MASKING + + COMPRESS_METHOD + PAD + TRANS_COL + XASPECT + YASPECT + + PAGEWIDTH + PAGEHEIGHT +CRNG -> // we do not use + +DPPV -> // we do not use + +FILESIZE -> (long) //size of file minus (sizeof(FORM) + sizeof(FILESIZE) + +GREEN -> (byte) // green color gun value + +HEIGHT -> (word) // of picture + +MASKING -> (byte) // masking type ? + +NUM_COLORS -> //number of colors used depending on format + +PAGE_WIDTH -> (word) // width of page +PAGE_HEIGHT -> (word) // height of page +PALETTE_INFO-> (RED+GREEN+BLUE) @ NUM_COLORS +PAD -> (byte) // not used. used as a padding +POSX -> (word) // staring position +POSY -> (word) // staring position + +RED -> (byte) // red color gun value + +TRANS_COL -> (word) // transparrent color + +WIDTH -> (word) // of picture + +XASPECT -> (byte) // x aspect ratio + +YASPECT -> (byte) // y aspect ratio + +???? -> // other possibilities + + +Differences in DPII enhance version + +.LBM -> "FORM" + FILESIZE + "PBM " + CHUNKS +DPPV -> DPPS // uses dpps instead of dppv +CHUNKS -> + TINY // add these to old definition + + + + +#endif + + diff --git a/WIN32LIB/IFF/WRITEPCX.CPP b/WIN32LIB/IFF/WRITEPCX.CPP new file mode 100644 index 0000000..459d1e2 --- /dev/null +++ b/WIN32LIB/IFF/WRITEPCX.CPP @@ -0,0 +1,181 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +#include +static void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + *=========================================================================*/ +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ) +{ + unsigned char palcopy[256 * 3]; + unsigned i ; + unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + + for ( i = 0 ; i < header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, ptr + i * VP_Scan_Line ) ; + + Mem_Copy(palette, palcopy, 256 * 3); + pal = ( RGB * ) palcopy ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palcopy , 256 * sizeof (RGB) ) ; + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + + +void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + char * file_ptr ; + char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + + for ( i = 1 ; i < scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else { + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last ) ; + } + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} + diff --git a/WIN32LIB/INCLUDE/AUDIO.H b/WIN32LIB/INCLUDE/AUDIO.H new file mode 100644 index 0000000..841408a --- /dev/null +++ b/WIN32LIB/INCLUDE/AUDIO.H @@ -0,0 +1,160 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; + +extern int StreamLowImpact; diff --git a/WIN32LIB/INCLUDE/BUFFER.H b/WIN32LIB/INCLUDE/BUFFER.H new file mode 100644 index 0000000..ef2e66f --- /dev/null +++ b/WIN32LIB/INCLUDE/BUFFER.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif diff --git a/WIN32LIB/INCLUDE/DEFINES.H b/WIN32LIB/INCLUDE/DEFINES.H new file mode 100644 index 0000000..9496789 --- /dev/null +++ b/WIN32LIB/INCLUDE/DEFINES.H @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/WIN32LIB/INCLUDE/DESCMGMT.H b/WIN32LIB/INCLUDE/DESCMGMT.H new file mode 100644 index 0000000..83f34ca --- /dev/null +++ b/WIN32LIB/INCLUDE/DESCMGMT.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/WIN32LIB/INCLUDE/DIFFTB.INC b/WIN32LIB/INCLUDE/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WIN32LIB/INCLUDE/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WIN32LIB/INCLUDE/DIPTHONG.H b/WIN32LIB/INCLUDE/DIPTHONG.H new file mode 100644 index 0000000..2f5ed70 --- /dev/null +++ b/WIN32LIB/INCLUDE/DIPTHONG.H @@ -0,0 +1,24 @@ +/* +** 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 . +*/ + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/WIN32LIB/INCLUDE/DRAWBUFF.H b/WIN32LIB/INCLUDE/DRAWBUFF.H new file mode 100644 index 0000000..98b57b7 --- /dev/null +++ b/WIN32LIB/INCLUDE/DRAWBUFF.H @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + + +#ifndef DRAWBUFF_H +#define DRAWBUFF_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +extern "C" { + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + long __cdecl Buffer_Size_Of_Region(void *thisptr, int w, int h); + + void __cdecl Buffer_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + int __cdecl Buffer_Get_Pixel(void * thisptr, int x, int y); + void __cdecl Buffer_Clear(void *thisptr, unsigned char color); + long __cdecl Buffer_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + BOOL __cdecl Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + BOOL __cdecl Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + LONG __cdecl Buffer_Print(void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); +} + +extern GraphicViewPortClass *LogicPage; +extern BOOL AllowHardwareBlitFills; +#endif diff --git a/WIN32LIB/INCLUDE/DRAWBUFF.INC b/WIN32LIB/INCLUDE/DRAWBUFF.INC new file mode 100644 index 0000000..bcb0b1f --- /dev/null +++ b/WIN32LIB/INCLUDE/DRAWBUFF.INC @@ -0,0 +1,93 @@ +; +; 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 . +; + + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : DRAWBUFF.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Clear :NEAR + +; Externs from BITBLIT.ASM module of the DRAWBUFF library +GLOBAL C Linear_Blit_To_Linear :NEAR + +; Externs from TOBUFF.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the DRAWBUFF library +GLOBAL C Linear_Scale_To_Linear :NEAR + +; Externs from TXTPRNT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Print :NEAR + + +;*-------------------------------------------------------------------------* +;* Define Buffer only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Line:NEAR + +; Externs from FILLQUAD.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Remap :NEAR + +; Externs from STAMP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Stamp :NEAR + +GLOBAL C get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + diff --git a/WIN32LIB/INCLUDE/EXTERNS.H b/WIN32LIB/INCLUDE/EXTERNS.H new file mode 100644 index 0000000..27d38cd --- /dev/null +++ b/WIN32LIB/INCLUDE/EXTERNS.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/WIN32LIB/INCLUDE/FILE.H b/WIN32LIB/INCLUDE/FILE.H new file mode 100644 index 0000000..69866c7 --- /dev/null +++ b/WIN32LIB/INCLUDE/FILE.H @@ -0,0 +1,260 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#ifndef READ +#define READ 1 // Read access. +#endif +#ifndef WRITE +#define WRITE 2 // Write access. +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Find_First(unsigned char *fname, unsigned int mode, struct find_t *ffblk); +extern int __cdecl Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/WIN32LIB/INCLUDE/FILEPCX.H b/WIN32LIB/INCLUDE/FILEPCX.H new file mode 100644 index 0000000..3091ccb --- /dev/null +++ b/WIN32LIB/INCLUDE/FILEPCX.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/WIN32LIB/INCLUDE/FILETEMP.H b/WIN32LIB/INCLUDE/FILETEMP.H new file mode 100644 index 0000000..796d225 --- /dev/null +++ b/WIN32LIB/INCLUDE/FILETEMP.H @@ -0,0 +1,61 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD __cdecl ( __cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID __cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H + diff --git a/WIN32LIB/INCLUDE/FONT.H b/WIN32LIB/INCLUDE/FONT.H new file mode 100644 index 0000000..e2f9096 --- /dev/null +++ b/WIN32LIB/INCLUDE/FONT.H @@ -0,0 +1,118 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Set_Font(void const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +int __cdecl Char_Pixel_Width(char chr); +unsigned int __cdecl String_Pixel_Width(char const *string); +void __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, unsigned long offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Load_Font(char const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern char FontWidth ; +extern char FontHeight; +extern char *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/WIN32LIB/INCLUDE/FUNCTION.H b/WIN32LIB/INCLUDE/FUNCTION.H new file mode 100644 index 0000000..1b7b679 --- /dev/null +++ b/WIN32LIB/INCLUDE/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/GBUFFER.H b/WIN32LIB/INCLUDE/GBUFFER.H new file mode 100644 index 0000000..b13811f --- /dev/null +++ b/WIN32LIB/INCLUDE/GBUFFER.H @@ -0,0 +1,1366 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef DRAWBUFF_H +#include "drawbuff.h" +#endif + +//#ifndef BUFFER_H +#include "buffer.h" +//#endif + +#ifndef WINDOWS_H +#include "ww_win.h" +#endif +#include + +#include "iconcach.h" + + + +#ifndef FUNCTION_H + +#pragma off (unreferenced) + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +#pragma on (unreferenced) +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// Defines for direct draw +// +// +extern LPDIRECTDRAW DirectDrawObject; //pointer to direct draw object +extern HWND MainWindow; //handle to programs main window + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Gbuffer_Focus_Loss_Function)(void); + +enum GBC_Enum { + GBC_NONE = 0, + GBC_VIDEOMEM = 1, + GBC_VISIBLE = 2, +}; + +#define NOT_LOCKED NULL + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr); +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + int Get_Pitch(void); + inline BOOL Get_IsDirectDraw(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + HRESULT Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + +// This doesnt seem to exist anywhere?? - Steve T 9/26/95 6:05PM +// VOID Grey_Out_Region(int x, int y, int width, int height, int color); + + // + // New members to lock and unlock the direct draw video memory + // + inline BOOL Lock (); + inline BOOL Unlock(); + inline int Get_LockCount(); + + // Member to blit using direct draw access to hardware blitter + HRESULT DD_Linear_Blit_To_Linear ( GraphicViewPortClass &dest, int source_x, int source_y, int dest_x, int dest_y, int width , int height, BOOL mask ); + + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + protected: + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + long Pitch; //Distance from one line to the next + GraphicBufferClass *GraphicBuff; // related graphic buff + BOOL IsDirectDraw; //Flag to let us know if it is a direct draw surface + int LockCount; // Count for stacking locks if non-zero the buffer +}; // is a locked DD surface + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/* long Pitch - modulo of buffer for reading and writing */ +/* BOOL IsDirectDraw - flag if its a direct draw surface */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + + public: + GraphicBufferClass(int w, int h, GBC_Enum flags); + GraphicBufferClass(int w, int h, void *buffer, long size); + GraphicBufferClass(int w, int h, void *buffer = 0); + GraphicBufferClass(void); + ~GraphicBufferClass(); + + void DD_Init(GBC_Enum flags); + void Init(int w, int h, void *buffer, long size, GBC_Enum flags); + void Un_Init(void); + void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer); + BOOL Lock(void); + BOOL Unlock(void); + + void Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle); + + // Member to get a pointer to a direct draw surface + LPDIRECTDRAWSURFACE Get_DD_Surface ( void ); + + protected: + LPDIRECTDRAWSURFACE VideoSurfacePtr; //Pointer to the related direct draw surface + DDSURFACEDESC VideoSurfaceDescription;//Description of the said surface + +}; + + + +inline int GraphicViewPortClass::Get_LockCount(void) +{ + return (LockCount); +} + + + +/*********************************************************************************************** + * GVPC::Get_IsDirectDraw -- provide read access to the IsDirectDraw flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsDirectDraw * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 1:02PM ST : Created * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Get_IsDirectDraw(void) +{ + return (IsDirectDraw); +} + + + +/*********************************************************************************************** + * GBC::Get_DD_Surface -- returns a pointer to the buffer direct draw surface * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to direct draw surface * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/29/95 9:43AM ST : Created * + *=============================================================================================*/ +inline LPDIRECTDRAWSURFACE GraphicBufferClass::Get_DD_Surface ( void ) +{ + return ( VideoSurfacePtr ); + +} + + + +/*********************************************************************************************** + * GVPC::Lock -- lock the graphics buffer for reading or writing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully locked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 12:33pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Lock(void) +{ + BOOL lock = GraphicBuff->Lock(); + if ( !lock ) return(FALSE); + + if (this != GraphicBuff) { + Attach(GraphicBuff, XPos, YPos, Width, Height); + } + return(TRUE); +} + +/*********************************************************************************************** + * GVPC::Unlock -- unlock the video buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully unlocked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 02:20pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Unlock(void) +{ + BOOL unlock = GraphicBuff->Unlock(); + if (!unlock) return(FALSE); + if (this != GraphicBuff && IsDirectDraw && !GraphicBuff->LockCount) { + Offset = 0; + } + return(TRUE); +} + + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return Buffer_Size_Of_Region(this, w, h); +} + + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + + if (Lock()){ + Buffer_Put_Pixel(this, x, y, color); + } + Unlock(); + + +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + int return_code=0; + + if (Lock()){ + return_code=(Buffer_Get_Pixel(this, x, y)); + } + Unlock(); + return(return_code); + +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + if (Lock()){ + Buffer_Clear(this, color); + } + Unlock(); + +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff, size)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + HRESULT return_code=0; + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos+x_pixel, YPos+y_pixel + , dest.Get_XPos()+dx_pixel, dest.Get_YPos()+dy_pixel + , pixel_width, pixel_height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel + , dx_pixel, dy_pixel + , pixel_width, pixel_height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos()+dx, dest.Get_YPos()+dy + , Width, Height, trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , dx, dy + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code=0; + + + if ( IsDirectDraw && dest.IsDirectDraw ){ + return(DD_Linear_Blit_To_Linear( dest, XPos, YPos + , dest.Get_XPos(), dest.Get_YPos() + , MAX( Width, dest.Get_Width()) + , MAX( Height, dest.Get_Height()) + , trans)); + } else { + + if (Lock()){ + if (dest.Lock()){ + return_code=(Linear_Blit_To_Linear(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + } + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, str, x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + if (Lock()){ + Buffer_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); + } + Unlock(); +} + + + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +extern BOOL IconCacheAllowed; +inline void GraphicViewPortClass::Draw_Stamp(void const * icondata, int icon, int x_pixel, int y_pixel, void const * remap, int clip_window) +{ + int cache_index=-1; + + int drewit = 0; + if (IconCacheAllowed){ + if (IsDirectDraw){ + if (!remap){ + cache_index = Is_Icon_Cached(icondata,icon); + } + + if (cache_index != -1){ + if (CachedIcons[cache_index].Get_Is_Cached() ){ + CachedIcons[cache_index].Draw_It (GraphicBuff->Get_DD_Surface() , x_pixel, y_pixel, + WindowList[clip_window][WINDOWX] + XPos, + WindowList[clip_window][WINDOWY] +YPos, + WindowList[clip_window][WINDOWWIDTH], + WindowList[clip_window][WINDOWHEIGHT]); + CachedIconsDrawn++; + drewit = 1; + } + } + } + } + + + if (drewit == 0) { + if (Lock()){ + UnCachedIconsDrawn++; + Buffer_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); + } + } + Unlock(); +} + + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + if (Lock()){ + Buffer_Draw_Line(this, sx, sy, dx, dy, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + if ( AllowHardwareBlitFills + && IsDirectDraw + && ( (dx-sx) * (dy-sy) >= (32*32) ) + && GraphicBuff->Get_DD_Surface()->GetBltStatus(DDGBS_CANBLT) == DD_OK){ + DDBLTFX blit_effects; + RECT dest_rectangle; + + dest_rectangle.left =sx+XPos; + dest_rectangle.top =sy+YPos; + dest_rectangle.right =dx+XPos; + dest_rectangle.bottom=dy+YPos; + + if (dest_rectangle.left= Width + XPos){ + dest_rectangle.right = Width +XPos -1; + } + + if (dest_rectangle.top= Height + YPos){ + dest_rectangle.bottom = Height + YPos -1; + } + + if (dest_rectangle.left >= dest_rectangle.right) return; + if (dest_rectangle.top >= dest_rectangle.bottom) return; + + dest_rectangle.right++; + dest_rectangle.bottom++; + + blit_effects.dwSize=sizeof(blit_effects); + blit_effects.dwFillColor = color; + GraphicBuff->Get_DD_Surface()->Blt(&dest_rectangle, + NULL, + NULL, + DDBLT_WAIT | DDBLT_ASYNC | DDBLT_COLORFILL, + &blit_effects); + } else { + if (Lock()){ + Buffer_Fill_Rect(this, sx, sy, dx, dy, color); + Unlock(); + } + } +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, sx, sy, width, height, remap); + } + Unlock(); +} + + +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + if (Lock()){ + Buffer_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, 0, 0, Width, Height, remap); + } + Unlock(); +} + +inline int GraphicViewPortClass::Get_Pitch(void) +{ + return(Pitch); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + + +#endif diff --git a/WIN32LIB/INCLUDE/GBUFFER.INC b/WIN32LIB/INCLUDE/GBUFFER.INC new file mode 100644 index 0000000..4da79b7 --- /dev/null +++ b/WIN32LIB/INCLUDE/GBUFFER.INC @@ -0,0 +1,51 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/ICONCACH.H b/WIN32LIB/INCLUDE/ICONCACH.H new file mode 100644 index 0000000..5201908 --- /dev/null +++ b/WIN32LIB/INCLUDE/ICONCACH.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 16th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains definition of the IconCacheClass and associated non member * + * function prototypes. * + * * + * Functions: * + * IconCacheClass::Get_Is_Cached -- member to allow access to private IsCached flag * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#ifndef ICONCACH_H +#define ICONCACH_H + +#include + +#define ICON_WIDTH 24 // Icons must be this width to be cached +#define ICON_HEIGHT 24 // Icons must be this height to be cached +#define MAX_CACHED_ICONS 500 // Maximum number of icons that can be cached +#define MAX_ICON_SETS 100 // Maximum number of icon sets that can be registered +#define MAX_LOOKUP_ENTRIES 3000 // Size of icon index table + + +/* +** IconCacheClass for tracking individual icons cached into video memory +** +** Use Register_Icon_Set to identify a set of icons as cachable. Once registered, the icons +** will be cached automatically when drawn. +** Use Invalidate_Cached_Icons at the end of icon drawing to release the video memory used by the +** caching system. +** Restore_Cached_Icons may be used to reload the icons into video memory after a focus loss. +** +*/ + +class IconCacheClass { + + public: + + IconCacheClass (void); // class constructor + ~IconCacheClass (void); // class destructor + + void Restore(void); // restore the surface + BOOL Cache_It (void * icon_ptr); // Cache the icon to video memory + void Uncache_It (void); // Restore the video memory and flag the icon as uncached + void Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height); + inline BOOL Get_Is_Cached(void); // Return the IsCached member + + int TimesDrawn; // counter of times cached icon has been drawn + int TimesFailed; // counter of times cached icon has failed to draw + + + private: + + LPDIRECTDRAWSURFACE CacheSurface; // Ptr to direct draw surface where icon resides + BOOL IsCached; // Flag to say whether an icon is cached + BOOL SurfaceLost; // Flag to indicate that our icons surface has been lost + int DrawFrequency; // Number of times icon has been drawn + void *IconSource; // Ptr to original icon data in system memory + +}; + + + +/* +** Structure to keep track of registered icon sets +** +*/ + +typedef struct tIconSetType{ + IControl_Type *IconSetPtr; // Ptr to icon set data + int IconListOffset; // Offset into icon index table for this icon set +}IconSetType; + + +extern IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern void Invalidate_Cached_Icons (void); +extern void Restore_Cached_Icons (void); +extern void Register_Icon_Set (void *icon_data , BOOL pre_cache); + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void Clear_Icon_Pointers (void); +extern "C" void Cache_Copy_Icon (void const *icon_ptr ,void * , int); +extern "C" int Is_Icon_Cached (void const *icon_data , int icon); +extern "C" int Get_Icon_Index (void *icon_ptr); +extern "C" int Get_Free_Index (void); +extern "C" BOOL Cache_New_Icon (int icon_index, void *icon_ptr); +extern "C" int Get_Free_Cache_Slot(void); + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/*********************************************************************************************** + * ICC::Get_Is_Cached -- member to allow access to the private IsCached flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsCached * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:42AM ST : Created * + *=============================================================================================*/ +inline BOOL IconCacheClass::Get_Is_Cached (void) +{ + return (IsCached); +} + + + + + +#endif //ICONCACH_H + diff --git a/WIN32LIB/INCLUDE/IFF.H b/WIN32LIB/INCLUDE/IFF.H new file mode 100644 index 0000000..851eb6b --- /dev/null +++ b/WIN32LIB/INCLUDE/IFF.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((long) ((long) d << 24) | ((long) c << 16) | ((long) b << 8) | (long)(a)) +#define IFFize_WORD(a) Reverse_Word(a) +#define IFFize_LONG(a) Reverse_Long(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + char Method; // Compression method (CompressionType). + char pad; // Reserved pad byte (always 0). + long Size; // Size of the uncompressed data. + short Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +int __cdecl Open_Iff_File(char const *filename); +void __cdecl Close_Iff_File(int fh); +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id); +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize); +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size); +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size); +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags); +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data=NULL); +unsigned long __cdecl Uncompress_Data(void const *src, void *dst); +void __cdecl Set_Uncomp_Buffer(int buffer_segment, int size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern void __cdecl Pack_2_Plane(void *buffer, void * pageptr, int planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Compress(void *source, void *dest, unsigned long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H diff --git a/WIN32LIB/INCLUDE/INDEXTB.INC b/WIN32LIB/INCLUDE/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WIN32LIB/INCLUDE/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WIN32LIB/INCLUDE/KEYBOARD.H b/WIN32LIB/INCLUDE/KEYBOARD.H new file mode 100644 index 0000000..9df1f7e --- /dev/null +++ b/WIN32LIB/INCLUDE/KEYBOARD.H @@ -0,0 +1,678 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + unsigned char VKRemap[256]; // gives vk for any ascii char + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT | WWKEY_VK_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT | WWKEY_VK_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); +int KN_To_VK(int key); + +#endif diff --git a/WIN32LIB/INCLUDE/KEYBOARD.INC b/WIN32LIB/INCLUDE/KEYBOARD.INC new file mode 100644 index 0000000..94ed506 --- /dev/null +++ b/WIN32LIB/INCLUDE/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WIN32LIB/INCLUDE/KEYSTRUC.INC b/WIN32LIB/INCLUDE/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WIN32LIB/INCLUDE/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WIN32LIB/INCLUDE/MCGAPRIM.INC b/WIN32LIB/INCLUDE/MCGAPRIM.INC new file mode 100644 index 0000000..4bbd349 --- /dev/null +++ b/WIN32LIB/INCLUDE/MCGAPRIM.INC @@ -0,0 +1,122 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/MEMFLAG.H b/WIN32LIB/INCLUDE/MEMFLAG.H new file mode 100644 index 0000000..c28d920 --- /dev/null +++ b/WIN32LIB/INCLUDE/MEMFLAG.H @@ -0,0 +1,109 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/* +** Prototypes for VMPAGEIN.ASM +*/ +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + +#pragma option -Jgd + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + +#pragma option -Jgd + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +extern "C" { + void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); +} + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif diff --git a/WIN32LIB/INCLUDE/MISC.H b/WIN32LIB/INCLUDE/MISC.H new file mode 100644 index 0000000..3f39969 --- /dev/null +++ b/WIN32LIB/INCLUDE/MISC.H @@ -0,0 +1,272 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +#define WIN32_LEAN_AND_MEAN // eliminates unecessary definitions in windows.h +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include + +extern LPDIRECTDRAWSURFACE PaletteSurface; + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: DDRAW.CPP */ +/*=========================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg); +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +unsigned Get_Free_Video_Memory(void); +void Wait_Blit(void); +unsigned Get_Video_Hardware_Capabilities(void); + +extern "C" void Wait_Vert_Blank(void); +extern "C" void Set_DD_Palette (void *palette); + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void); +/* +** Pointer to function to call if we detect a surface restore +*/ +extern void (*Misc_Focus_Restore_Function)(void); + + +/* + * Flags returned by Get_Video_Hardware_Capabilities + */ +/* Hardware blits supported? */ +#define VIDEO_BLITTER 1 + +/* Hardware blits asyncronous? */ +#define VIDEO_BLITTER_ASYNC 2 + +/* Can palette changes be synced to vertical refresh? */ +#define VIDEO_SYNC_PALETTE 4 + +/* Is the video cards memory bank switched? */ +#define VIDEO_BANK_SWITCHED 8 + +/* Can the blitter do filled rectangles? */ +#define VIDEO_COLOR_FILL 16 + +/* Is there no hardware assistance avaailable at all? */ +#define VIDEO_NO_HARDWARE_ASSIST 32 + + + +/* + * Definition of surface monitor class + * + * This class keeps track of all the graphic buffers we generate in video memory so they + * can be restored after a focus switch. +*/ + +#define MAX_SURFACES 20 + +class SurfaceMonitorClass { + + public: + + SurfaceMonitorClass(); + + void Add_DD_Surface (LPDIRECTDRAWSURFACE); + void Remove_DD_Surface (LPDIRECTDRAWSURFACE); + BOOL Got_Surface_Already (LPDIRECTDRAWSURFACE); + void Restore_Surfaces (void); + void Set_Surface_Focus ( BOOL in_focus ); + void Release(void); + + BOOL SurfacesRestored; + + private: + + LPDIRECTDRAWSURFACE Surface[MAX_SURFACES]; + BOOL InFocus; + +}; + +extern SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces + + +/*=========================================================================*/ +/* The following variables are declared in: DDRAW.CPP */ +/*=========================================================================*/ +extern LPDIRECTDRAW DirectDrawObject; +extern LPDIRECTDRAW2 DirectDraw2Interface; +extern HWND MainWindow; +extern BOOL SystemToVideoBlits; +extern BOOL VideoToSystemBlits; +extern BOOL SystemToSystemBlits; +extern BOOL OverlappedVideoBlits; // Can video driver blit overlapped regions? + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID __cdecl Prog_End(VOID); +VOID __cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +unsigned char __cdecl Random(void); +int __cdecl Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void __cdecl Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +long __cdecl Reverse_Long(long number); +short __cdecl Reverse_Short(short number); +long __cdecl Swap_Long(long number); +#if (0) +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ +#endif + +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long __cdecl Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD __cdecl Processor(void); +extern WORD __cdecl Operating_System(void); +extern unsigned long random ( unsigned long mod ) ; +//extern void randomize ( void ) ; + +extern int __cdecl Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int __cdecl Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + diff --git a/WIN32LIB/INCLUDE/MODEMREG.H b/WIN32LIB/INCLUDE/MODEMREG.H new file mode 100644 index 0000000..c883378 --- /dev/null +++ b/WIN32LIB/INCLUDE/MODEMREG.H @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + + +#ifndef WIN32 +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif //WIN32 +#include + + + +class ModemRegistryEntryClass { + + public: + + ModemRegistryEntryClass (int modem_number); + ~ModemRegistryEntryClass (void); + + + char *Get_Modem_Name (void) { return (ModemName); } + + char *Get_Modem_Device_Name (void) { return (ModemDeviceName); } + + char *Get_Modem_Error_Correction_Enable (void) { return (ErrorCorrectionEnable); } + + char *Get_Modem_Error_Correction_Disable (void) { return (ErrorCorrectionDisable); } + + char *Get_Modem_Compression_Enable (void) { return (CompressionEnable); } + + char *Get_Modem_Compression_Disable (void) { return (CompressionDisable); } + + char *Get_Modem_Hardware_Flow_Control (void) { return (HardwareFlowControl); } + + char *Get_Modem_No_Flow_Control (void) { return (HardwareFlowControl); } + + private: + + char *ModemName; + char *ModemDeviceName; + char *ErrorCorrectionEnable; + char *ErrorCorrectionDisable; + char *CompressionEnable; + char *CompressionDisable; + char *HardwareFlowControl; + char *NoFlowControl; + +}; + + + + + + + diff --git a/WIN32LIB/INCLUDE/MONO.H b/WIN32LIB/INCLUDE/MONO.H new file mode 100644 index 0000000..07a991f --- /dev/null +++ b/WIN32LIB/INCLUDE/MONO.H @@ -0,0 +1,185 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.16 06 Sep 1995 16:29:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif + diff --git a/WIN32LIB/INCLUDE/MOUSE.H b/WIN32LIB/INCLUDE/MOUSE.H new file mode 100644 index 0000000..3291bab --- /dev/null +++ b/WIN32LIB/INCLUDE/MOUSE.H @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +class WWMouseClass { + public: + WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, void *cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicViewPortClass *scr); + void Erase_Mouse(GraphicViewPortClass *scr, int forced = FALSE); + + void Block_Mouse(GraphicBufferClass *buffer); + void Unblock_Mouse(GraphicBufferClass *buffer); + void Set_Cursor_Clip(void); + void Clear_Cursor_Clip(void); + + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicViewPortClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + CRITICAL_SECTION MouseCriticalSection; // Control for mouse re-enterancy + unsigned TimerHandle; + +}; + +extern "C" { + void __cdecl Mouse_Shadow_Buffer(void *thisptr, GraphicViewPortClass *srcdst, void *buffer, int x, int y, int hotx, int hoty, int store); + void __cdecl Draw_Mouse(void *thisptr, GraphicViewPortClass *srcdst, int x, int y); + void * __cdecl ASM_Set_Mouse_Cursor(void * thisptr, int hotspotx, int hotspoty, VOID *cursor); +}; + +void Hide_Mouse(void); +void Show_Mouse(void); +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/WIN32LIB/INCLUDE/MOUSE.INC b/WIN32LIB/INCLUDE/MOUSE.INC new file mode 100644 index 0000000..f936787 --- /dev/null +++ b/WIN32LIB/INCLUDE/MOUSE.INC @@ -0,0 +1,67 @@ +; +; 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 . +; + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/WIN32LIB/INCLUDE/NYBBTB.INC b/WIN32LIB/INCLUDE/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WIN32LIB/INCLUDE/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WIN32LIB/INCLUDE/PALETTE.H b/WIN32LIB/INCLUDE/PALETTE.H new file mode 100644 index 0000000..37393ca --- /dev/null +++ b/WIN32LIB/INCLUDE/PALETTE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +#include +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +void __cdecl Set_Palette(void *palette); +void __cdecl Set_Palette_Color(void *palette, int color, void *data); +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +void __cdecl Morph_Palette (void *src_palette, void *dst_palette, unsigned int delay, + void *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern void __cdecl Set_Palette_Range(void *palette); +extern BOOL __cdecl Bump_Color(void *palette, int changable, int target); + +#ifdef __cplusplus +} +#endif +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ diff --git a/WIN32LIB/INCLUDE/PLAYCD.H b/WIN32LIB/INCLUDE/PLAYCD.H new file mode 100644 index 0000000..b809834 --- /dev/null +++ b/WIN32LIB/INCLUDE/PLAYCD.H @@ -0,0 +1,309 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + + +#ifdef NOT_FOR_WIN95 +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +}; + + + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + + + + + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#ifdef NOT_FOR_WIN95 +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + +#endif //NOT_FOR_WIN95 +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/WIN32LIB/INCLUDE/PROFILE.H b/WIN32LIB/INCLUDE/PROFILE.H new file mode 100644 index 0000000..ddf81e3 --- /dev/null +++ b/WIN32LIB/INCLUDE/PROFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : PROFILE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define MAX_PROFILE_TIME 60*1 //1 minute(s) @ 14.4 Mb per hour +#define PROFILE_RATE 1000 //samples per sec (max 1000) + + +/* + * Defines for choosing between the old and new profiler system + * +*/ + +#define OLD_PROFILE_SYSTEM 1 +#define NEW_PROFILE_SYSTEM 2 + +//#define PROFILE_SYSTEM OLD_PROFILE_SYSTEM +#define PROFILE_SYSTEM NEW_PROFILE_SYSTEM + + + +extern "C"{ + void __cdecl Profile_Init(void); + void __cdecl Profile_End(void); + void __cdecl Start_Profiler(void); + void __cdecl Stop_Profiler(void); +} + diff --git a/WIN32LIB/INCLUDE/PROFILE.INC b/WIN32LIB/INCLUDE/PROFILE.INC new file mode 100644 index 0000000..3f220e0 --- /dev/null +++ b/WIN32LIB/INCLUDE/PROFILE.INC @@ -0,0 +1,44 @@ +; +; 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 . +; + + +;USE_PROFILER =1 + + +macro prologue +Ifdef USE_PROFILER + global __PRO:near + + call __PRO +endif ;USE_PROFILER +endm + + + +macro epilogue +ifdef USE_PROFILER + global __EPI:near + + push ebp + call __EPI + pop ebp +endif ;USE_PROFILER +endm + + + diff --git a/WIN32LIB/INCLUDE/RAWFILE.H b/WIN32LIB/INCLUDE/RAWFILE.H new file mode 100644 index 0000000..1b55720 --- /dev/null +++ b/WIN32LIB/INCLUDE/RAWFILE.H @@ -0,0 +1,257 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.15 06 Sep 1995 16:29:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include + +//#include +#include +#include +#include +//#include +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void) {if (Allocated && Filename) free((char *)Filename);}; + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + virtual void Set_Buffer_Size(int size); + + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return Filename; +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Filename(0) +{ + Handle = -1; + Allocated = false; +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle != -1); +} + +#endif diff --git a/WIN32LIB/INCLUDE/SHAPE.H b/WIN32LIB/INCLUDE/SHAPE.H new file mode 100644 index 0000000..2c8f7d2 --- /dev/null +++ b/WIN32LIB/INCLUDE/SHAPE.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_FADING = 0x0100, // Fading effect (void * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (void * color_table) +} ShapeFlags_Type; + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + unsigned short ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + unsigned char Height; // Height of the shape in scan lines + unsigned short Width; // Width of the shape in bytes + unsigned char OriginalHeight; // Original height of shape in scan lines + unsigned short ShapeSize; // Size of the shape, including header + unsigned short DataLength; // Size of the uncompressed shape (just data) + unsigned char Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +typedef struct { + unsigned short NumShapes; // number of shapes in the block + long Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern void *MaskPage; +extern void *BackGroundPage; +extern long _ShapeBufferSize; +extern char *_ShapeBuffer; +} + + +void __cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +int Draw_Shape(GraphicViewPortClass *gvp, void const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short __cdecl Get_Shape_Data(void const *shape, int data); +int __cdecl Extract_Shape_Count(void const *buffer); +void * __cdecl Extract_Shape(void const *buffer, int shape); +int __cdecl Restore_Shape_Height(void *shape); +int __cdecl Set_Shape_Height(void const *shape, int newheight); + +extern "C" { +int __cdecl Get_Shape_Width(void const *shape); +int __cdecl Get_Shape_Height(void const *shape); +int __cdecl Get_Shape_Original_Height(void const *shape); +int __cdecl Get_Shape_Uncomp_Size(void const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +void __cdecl Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +int __cdecl Get_Shape_Flags(void const *shape); +int __cdecl Get_Shape_Size(void const *shape); +int __cdecl Get_Shape_Scaled_Width(void const *shape, int scale); +int __cdecl Get_Shape_Scaled_Height(void const *shape, int scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + diff --git a/WIN32LIB/INCLUDE/SHAPE.INC b/WIN32LIB/INCLUDE/SHAPE.INC new file mode 100644 index 0000000..b5aa807 --- /dev/null +++ b/WIN32LIB/INCLUDE/SHAPE.INC @@ -0,0 +1,214 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL C ShapeBuffer:DWORD +GLOBAL C ShapeBufferSize:DWORD +GLOBAL C _MaskPage:DWORD +GLOBAL C _BackGroundPage:DWORD +GLOBAL C PredCount:DWORD +GLOBAL C PredTable:BYTE +GLOBAL C PredValue:DWORD +GLOBAL C PartialPred:DWORD +GLOBAL C PartialCount:DWORD +GLOBAL C Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/WIN32LIB/INCLUDE/SOS.H b/WIN32LIB/INCLUDE/SOS.H new file mode 100644 index 0000000..9a77c15 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far __cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far __cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/SOSCOMP.H b/WIN32LIB/INCLUDE/SOSCOMP.H new file mode 100644 index 0000000..de369d7 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void __cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long __cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl General_sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif diff --git a/WIN32LIB/INCLUDE/SOSDATA.H b/WIN32LIB/INCLUDE/SOSDATA.H new file mode 100644 index 0000000..f6686a5 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WIN32LIB/INCLUDE/SOSDEFS.H b/WIN32LIB/INCLUDE/SOSDEFS.H new file mode 100644 index 0000000..9bb29e7 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WIN32LIB/INCLUDE/SOSFNCT.H b/WIN32LIB/INCLUDE/SOSFNCT.H new file mode 100644 index 0000000..2945aa4 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOSFNCT.H @@ -0,0 +1,219 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int __cdecl sosRealFree ( int ); +extern BOOL __cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int __cdecl sosRealAlloc( int, int *, int * ); +extern void __cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int __cdecl sosGetCS( VOID ); +extern int __cdecl sosGetES( VOID ); +#else +extern int __cdecl sosRealAlloc ( int, int *, int * ); +extern int __cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD __cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD __cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void __cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void __cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void __cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void __cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void __cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void __cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void __cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void __cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD __cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD __cdecl sosDIGIInitForWindows( WORD ); +extern WORD __cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR __cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR __cdecl sosCreateAliasCS ( LPSTR ); +extern VOID __cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR __cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID __cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR __cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID __cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID __cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD __cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD __cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD __cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD __cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD __cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD __cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD __cdecl getDS( VOID ); +extern WORD __cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD __cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void __cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void __cdecl sosTIMERDRVUnInit( void ); +extern void __cdecl sosTIMERDRVHandler( void ); +extern void __cdecl sosTIMERDRVFHandler( void ); +extern void __cdecl sosTIMERDRVEnable( void ); +extern void __cdecl sosTIMERDRVDisable( void ); +extern void __cdecl sosTIMERDRVCallOld( void ); +extern void __cdecl sosTIMERDRVSetRate( WORD ); +extern void __cdecl sosDIGITimer_Start( void ); +extern void __cdecl sosDIGITimer_End( void ); +extern void __cdecl sosDIGIDrv_Start( void ); +extern void __cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif + diff --git a/WIN32LIB/INCLUDE/SOSRES.H b/WIN32LIB/INCLUDE/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WIN32LIB/INCLUDE/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/SOUND.H b/WIN32LIB/INCLUDE/SOUND.H new file mode 100644 index 0000000..43c4afc --- /dev/null +++ b/WIN32LIB/INCLUDE/SOUND.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" +#include "dsound.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/WIN32LIB/INCLUDE/SOUNDINT.H b/WIN32LIB/INCLUDE/SOUNDINT.H new file mode 100644 index 0000000..1a69aab --- /dev/null +++ b/WIN32LIB/INCLUDE/SOUNDINT.H @@ -0,0 +1,292 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/WIN32LIB/INCLUDE/STAMP.INC b/WIN32LIB/INCLUDE/STAMP.INC new file mode 100644 index 0000000..41ebb35 --- /dev/null +++ b/WIN32LIB/INCLUDE/STAMP.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + +ifdef NO_WAY_THIS_WILL_BE_DEFINED_HAHAHAHA + + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +else + + + STRUC IControl_Type + +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? +MapHeight DW ? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? +Map DD ? ; Icon map offset. + ENDS + +endif + +ICON256 EQU 1 diff --git a/WIN32LIB/INCLUDE/STRUCTS.H b/WIN32LIB/INCLUDE/STRUCTS.H new file mode 100644 index 0000000..3184818 --- /dev/null +++ b/WIN32LIB/INCLUDE/STRUCTS.H @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/WIN32LIB/INCLUDE/SVGAPRIM.INC b/WIN32LIB/INCLUDE/SVGAPRIM.INC new file mode 100644 index 0000000..2651d21 --- /dev/null +++ b/WIN32LIB/INCLUDE/SVGAPRIM.INC @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/WIN32LIB/INCLUDE/TILE.H b/WIN32LIB/INCLUDE/TILE.H new file mode 100644 index 0000000..eca2d21 --- /dev/null +++ b/WIN32LIB/INCLUDE/TILE.H @@ -0,0 +1,94 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); +#if (0) +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char *Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char *Map; // Icon map offset (if present). +} IControl_Type; +#endif //(0) + +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; + + +#endif //TILE_H + diff --git a/WIN32LIB/INCLUDE/TIMER.H b/WIN32LIB/INCLUDE/TIMER.H new file mode 100644 index 0000000..17d85b7 --- /dev/null +++ b/WIN32LIB/INCLUDE/TIMER.H @@ -0,0 +1,201 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include +#include + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; +extern HANDLE TimerThreadHandle; //Handle of timer thread +extern int InTimerCallback; //true if we are currently in a callback + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: +// long (*Get_Ticks)(void); // System timer fetch. + BaseTimerEnum TickType; + long Get_Ticks (void); +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + + +class WinTimerClass { + + public: + WinTimerClass ( UINT freq=60 , BOOL partial=0 ); + ~WinTimerClass(); + + void Update_Tick_Count ( void ); + unsigned Get_System_Tick_Count ( void ); + unsigned Get_User_Tick_Count ( void ); + + private: + + unsigned TimerHandle; //Handle for windows timer event + unsigned Frequency; //Frequency of our windows timer in ticks per second + + unsigned TrueRate; //True rate of clock. (only use word) + unsigned SysTicks; //Tick count of timer. + unsigned UserTicks; //Tick count of timer. + unsigned UserRate; //Desired rate of timer. + + +}; + + +extern WinTimerClass *WindowsTimer; + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// +#ifndef FUNCTION_H +extern TimerClass TickCount; +#endif +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long __cdecl Get_System_Tick_Count(void); + long __cdecl Get_User_Tick_Count(void); + void far __cdecl Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void __cdecl Disable_Timer_Interrupt(void); + void __cdecl Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL __cdecl Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL __cdecl Remove_Timer_System(VOID); + + +#endif // TIMER_H + diff --git a/WIN32LIB/INCLUDE/VIDEO.H b/WIN32LIB/INCLUDE/VIDEO.H new file mode 100644 index 0000000..67adecf --- /dev/null +++ b/WIN32LIB/INCLUDE/VIDEO.H @@ -0,0 +1,219 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + +#endif //NOT_FOR_WIN95 + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + +extern REALPTR VesaFunc; + +#endif //NOT_FOR_WIN95 + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/WINCOMM.H b/WIN32LIB/INCLUDE/WINCOMM.H new file mode 100644 index 0000000..2ceae18 --- /dev/null +++ b/WIN32LIB/INCLUDE/WINCOMM.H @@ -0,0 +1,457 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WIN32 +#define WIN32 +#define _WIN32 +#endif //WIN32 +#include + +typedef enum WinCommDialMethodType { + WC_TOUCH_TONE = 0, + WC_PULSE +} WinCommDialMethodType; + + + +#define COMMSUCCESS 0 +#define ASTIMEOUT -10 +#define COMMUSERABORT -16 + + +/* +** The size of our serial buffer within the class. +** +** !!!!!! THIS MUST BE A POWER OF 2 !!!!!! +** +*/ +#define SIZE_OF_WINDOWS_SERIAL_BUFFER 2048 + + + +/* +** WinModemClass. +** +** This class provides access to modems under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +*/ + +class WinModemClass +{ + + public: + + WinModemClass (void); //WinModemClass Contructor + virtual ~WinModemClass (void); //WinModemClass Destructor + + + /* + ** Serial port open should be called to get a handle to the COM port + ** This needs to be called first as other class members rely on the handle + ** + ** Replacement for Greenleaf function: PortOpenGreenleafFast + */ + //virtual HANDLE Serial_Port_Open (int port, int baud, int parity, int wordlen, int stopbits); + virtual HANDLE Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol); + + /* + ** This function releases the COM port handle and should be called after + ** communications have finished + ** + ** Replacement for Greenleaf function: PortClose + */ + void Serial_Port_Close (void); + + /* + ** This member copies any bytes from the internal class serial buffer + ** into your user buffer. + ** + ** Replacement for Greenleaf function: ReadBuffer + */ + int Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len); + + /* + ** Write chars to the serial port + ** + ** Replacement for Greenleaf function: WriteBuffer + */ + void Write_To_Serial_Port (unsigned char *buffer, int length); + + /* + ** Wait for the outgoing buffer to empty + */ + void Wait_For_Serial_Write (void); + + /* + ** Set the dial type to DIAL_TOUCH_TONE or DIAL_PULSE + ** + ** Replacement for Greenleaf function: HMSetDiallingMethod + */ + virtual void Set_Modem_Dial_Type (WinCommDialMethodType method); + + /* + ** Get the status of the modem control lines + ** Possible flags are: CTS_SET DSR_SET RI_SET & CD_SET + ** + ** Replacement for Greenleaf function: GetModemStatus + */ + virtual unsigned Get_Modem_Status (void); + + /* + ** Set the DTR line to the given state + ** + ** Replacement for Greenleaf function: SetDtr + */ + virtual void Set_Serial_DTR (BOOL state); + + /* + ** Get the result code from the modem after issuing an 'AT' command + ** + ** Replacement for Greenleaf function: HMInputLine + */ + virtual int Get_Modem_Result (int delay, char *buffer, int buffer_len); + + /* + ** Issue a dial command to the modem. + ** Use Set_Modem_Dial_Type to select pulse or tone dial + ** + ** Replacement for Greenleaf function: HMDial + */ + virtual void Dial_Modem (char *dial_number); + + /* + ** Send a command to the modem. This is usually an 'AT' command. + ** Function will optionally retry until 'OK' is received. + */ + virtual int Send_Command_To_Modem (char *command, char terminator, char *buffer, int buflen, int delay, int retries); + + /* + ** Sets a pointer to a function that will be called for each incoming serial char + ** + ** Replacement for Greenleaf function: HMSetUpEchoRoutine + */ + virtual void Set_Echo_Function (void(*func)(char c)); + + /* + ** Sets a pointer to a function that will be called if ESC is pressed during a dial + ** + ** Replacement for Greenleaf function: HMSetUpAbortKey + */ + virtual void Set_Abort_Function (int (*func)(void)); + + /* + ** Member to allow access to the serial port handle + */ + HANDLE Get_Port_Handle(void); + + /* + ** Status vars for debugging purposes + */ + int FramingErrors; + int IOErrors; + int BufferOverruns; + int InBufferOverflows; + int ParityErrors; + int OutBufferOverflows; + int InQueue; + int OutQueue; + + /* + ** Modem send result codes + */ + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + + /* + ** Enums for modem status flags + */ + enum { + CTS_SET = 0x10, + DSR_SET = 0x20, + RI_SET = 0x40, + CD_SET = 0x80 + }; + + + protected: + + + /* + ** Copy incoming data from the windows file buffer into the internal class buffer + */ + BOOL Read_Serial_Chars(void); + + /* + ** Pointer to the internal class circular buffer for incoming data + */ + unsigned char *SerialBuffer; + + /* + ** Overlap object for asyncronous reads from the serial port + */ + OVERLAPPED ReadOverlap; + + /* + ** Overlap object for asyncronous writes to the serial port + */ + OVERLAPPED WriteOverlap; + + /* + ** Flag that there is no outstanding incoming data in the windows buffer + */ + BOOL WaitingForSerialCharRead; + + /* + ** Flag that we are waiting for the last write to port operation to complete + */ + BOOL WaitingForSerialCharWrite; + + /* + ** Head and Tail pointers for our internal serial buffer + */ + int SerialBufferReadPtr; + int SerialBufferWritePtr; + + /* + ** Windows handle to the COM port device + */ + HANDLE PortHandle; + + /* + ** Dialing method - DIAL_TOUCH_TONE or DIAL_PULSE + */ + WinCommDialMethodType DialingMethod; + + /* + ** Pointer to function for echoing incoming data - can be NULL + */ + void (*EchoFunction)(char c); + + /* + ** Pointer to function for aborting when ESC pressed - can be NULL + */ + int (*AbortFunction)(void); + + /* + ** Serial buffer for asyncronous reads + */ + char TempSerialBuffer[SIZE_OF_WINDOWS_SERIAL_BUFFER]; +}; + + + + + + + + + + +/* +** WinNullModemClass. +** +** This class provides access to serial ports under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +** +** This class just overloads the WinModemClass members that arent required for direct serial communications +** via a 'null modem' cable. +*/ +class WinNullModemClass : public WinModemClass +{ + + public: + + virtual inline void Set_Modem_Dial_Type (int){}; + virtual inline unsigned Get_Modem_Status (void){return (0);}; + virtual inline void Set_Serial_DTR (BOOL){}; + virtual inline int Get_Modem_Result (int, char*, int){return(0);}; + virtual inline void Dial_Modem (char*){}; + virtual inline int Send_Command_To_Modem (char*, char, char*, int, int, int){return (0);}; + virtual inline void Set_Echo_Function (void(*)(char)){}; + virtual inline void Set_Abort_Function (int(*)(void)){}; + +}; + + +extern WinModemClass *SerialPort; + + + + + + + + + +// +// +// This bit swiped from the SDK because its not in the Watcom headers yet +// +// + +/************************************************************************ +* * +* mcx.h -- This module defines the 32-Bit Windows MCX APIs * +* * +* Copyright (c) 1990-1995, Microsoft Corp. All rights reserved. * +* * +************************************************************************/ + +#ifndef _MCX_H_ +#define _MCX_H_ + +typedef struct _MODEMDEVCAPS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // product and version identification + DWORD dwModemProviderVersion; + DWORD dwModemManufacturerOffset; + DWORD dwModemManufacturerSize; + DWORD dwModemModelOffset; + DWORD dwModemModelSize; + DWORD dwModemVersionOffset; + DWORD dwModemVersionSize; + + // local option capabilities + DWORD dwDialOptions; // bitmap of supported values + DWORD dwCallSetupFailTimer; // maximum in seconds + DWORD dwInactivityTimeout; // maximum in seconds + DWORD dwSpeakerVolume; // bitmap of supported values + DWORD dwSpeakerMode; // bitmap of supported values + DWORD dwModemOptions; // bitmap of supported values + DWORD dwMaxDTERate; // maximum value in bit/s + DWORD dwMaxDCERate; // maximum value in bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS; + +typedef struct _MODEMSETTINGS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // static local options (read/write) + DWORD dwCallSetupFailTimer; // seconds + DWORD dwInactivityTimeout; // seconds + DWORD dwSpeakerVolume; // level + DWORD dwSpeakerMode; // mode + DWORD dwPreferredModemOptions; // bitmap + + // negotiated options (read only) for current or last call + DWORD dwNegotiatedModemOptions; // bitmap + DWORD dwNegotiatedDCERate; // bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMSETTINGS, *PMODEMSETTINGS, *LPMODEMSETTINGS; + +// Dial Options +#define DIALOPTION_BILLING 0x00000040 // Supports wait for bong "$" +#define DIALOPTION_QUIET 0x00000080 // Supports wait for quiet "@" +#define DIALOPTION_DIALTONE 0x00000100 // Supports wait for dial tone "W" + +// SpeakerVolume for MODEMDEVCAPS +#define MDMVOLFLAG_LOW 0x00000001 +#define MDMVOLFLAG_MEDIUM 0x00000002 +#define MDMVOLFLAG_HIGH 0x00000004 + +// SpeakerVolume for MODEMSETTINGS +#define MDMVOL_LOW 0x00000000 +#define MDMVOL_MEDIUM 0x00000001 +#define MDMVOL_HIGH 0x00000002 + +// SpeakerMode for MODEMDEVCAPS +#define MDMSPKRFLAG_OFF 0x00000001 +#define MDMSPKRFLAG_DIAL 0x00000002 +#define MDMSPKRFLAG_ON 0x00000004 +#define MDMSPKRFLAG_CALLSETUP 0x00000008 + +// SpeakerMode for MODEMSETTINGS +#define MDMSPKR_OFF 0x00000000 +#define MDMSPKR_DIAL 0x00000001 +#define MDMSPKR_ON 0x00000002 +#define MDMSPKR_CALLSETUP 0x00000003 + +// Modem Options +#define MDM_COMPRESSION 0x00000001 +#define MDM_ERROR_CONTROL 0x00000002 +#define MDM_FORCED_EC 0x00000004 +#define MDM_CELLULAR 0x00000008 +#define MDM_FLOWCONTROL_HARD 0x00000010 +#define MDM_FLOWCONTROL_SOFT 0x00000020 +#define MDM_CCITT_OVERRIDE 0x00000040 +#define MDM_SPEED_ADJUST 0x00000080 +#define MDM_TONE_DIAL 0x00000100 +#define MDM_BLIND_DIAL 0x00000200 +#define MDM_V23_OVERRIDE 0x00000400 + +#endif /* _MCX_H_ */ + + + + + + + + + + + + + + + + diff --git a/WIN32LIB/INCLUDE/WSA.H b/WIN32LIB/INCLUDE/WSA.H new file mode 100644 index 0000000..12141b4 --- /dev/null +++ b/WIN32LIB/INCLUDE/WSA.H @@ -0,0 +1,163 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette=NULL); +void __cdecl Close_Animation( void *handle ); +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +BOOL __cdecl Animate_Frame(void *handle, VideoViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +int __cdecl Get_Animation_X(void const *handle); +int __cdecl Get_Animation_Y(void const *handle); +int __cdecl Get_Animation_Width(void const *handle); +int __cdecl Get_Animation_Height(void const *handle); +int __cdecl Get_Animation_Palette(void const *handle); +unsigned long __cdecl Get_Animation_Size(void const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, BufferClass& buffer, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, (char *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +unsigned int __cdecl Apply_XOR_Delta(char *source_ptr, char *delta_ptr); +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy); +} + + + +#endif // WSA_H + diff --git a/WIN32LIB/INCLUDE/WWFILE.H b/WIN32LIB/INCLUDE/WWFILE.H new file mode 100644 index 0000000..4913695 --- /dev/null +++ b/WIN32LIB/INCLUDE/WWFILE.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.14 06 Sep 1995 16:30:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/WIN32LIB/INCLUDE/WWLIB32.H b/WIN32LIB/INCLUDE/WWLIB32.H new file mode 100644 index 0000000..829e653 --- /dev/null +++ b/WIN32LIB/INCLUDE/WWLIB32.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H + + diff --git a/WIN32LIB/INCLUDE/WWMEM.H b/WIN32LIB/INCLUDE/WWMEM.H new file mode 100644 index 0000000..917a10d --- /dev/null +++ b/WIN32LIB/INCLUDE/WWMEM.H @@ -0,0 +1,70 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WIN32LIB/INCLUDE/WWMEM.INC b/WIN32LIB/INCLUDE/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WIN32LIB/INCLUDE/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WIN32LIB/INCLUDE/WWSTD.H b/WIN32LIB/INCLUDE/WWSTD.H new file mode 100644 index 0000000..89e047a --- /dev/null +++ b/WIN32LIB/INCLUDE/WWSTD.H @@ -0,0 +1,338 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// +// Win 95 includes +// + +#ifndef WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 1 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define WW_ERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +#pragma option -Jg +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +int ABS(int); +long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +short MIN(short, short); +int MIN(int, int); +long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +short MAX(short, short); +int MAX(int, int); +long MAX(long, long); +#pragma option -Jgd + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +//#define true 1 +//#define false 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. +#if(0) +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif +#endif + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif diff --git a/WIN32LIB/INCLUDE/WW_WIN.H b/WIN32LIB/INCLUDE/WW_WIN.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/WIN32LIB/INCLUDE/WW_WIN.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WIN32LIB/INCLUDE/_FILE.H b/WIN32LIB/INCLUDE/_FILE.H new file mode 100644 index 0000000..727c0d3 --- /dev/null +++ b/WIN32LIB/INCLUDE/_FILE.H @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/WIN32LIB/KEYBOARD/KEYBOARD.CPP b/WIN32LIB/KEYBOARD/KEYBOARD.CPP new file mode 100644 index 0000000..4cef027 --- /dev/null +++ b/WIN32LIB/KEYBOARD/KEYBOARD.CPP @@ -0,0 +1,533 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 26, 1995 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" +#include "timer.h" +#include "mono.h" + +void Message_Loop(void); + +WWKeyboardClass *_Kbd; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + _Kbd = this; + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 256); + memset(AsciiRemap, 0, 2048); + for (short lp = 31; lp < 255; lp ++) { + if (isprint(lp)) { + int vk_key = VkKeyScan((unsigned char)lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = (unsigned char)lp; + VKRemap[lp] = (unsigned char)(vk_key & 0xFF); + } + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; + MState = 0; + Conditional = 0; + CurrentCursor = NULL; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[(Head + 1) & 255]; // get the x and y pos + MouseQY = Buffer[(Head + 2) & 255]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + Message_Loop(); + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = (short)key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. Note + // that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + // would be incompatible with the dos version. + // + if (vk_key != VK_LBUTTON && vk_key != VK_MBUTTON && vk_key != VK_RBUTTON) { + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + +int WWKeyboardClass::To_ASCII(int key) +{ + if ( key && WWKEY_RLS_BIT) + return(KN_NONE); + return(key); +} + +WWKeyboardClass::Down(int key) +{ + return(GetAsyncKeyState(key&0xFF)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + +#pragma off(unreferenced) +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message(wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + case WM_MOUSEMOVE: + if (CurrentCursor) + SetCursor(CurrentCursor); + break; + } +} +#pragma on(unreferenced) + + + + +void Message_Loop(void) +{ + + MSG msg; + + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + + + +} + + + +/*************************************************************************** + * CHECK_KEY -- compatability routine for old 32 bit library * + * * + * This routine checks to see if there is a key in the keyboard buffer * + * and returns it to the sender if there is. It does not remove the key * + * from the buffer. * + * * + * INPUT: none * + * * + * OUTPUT: The key that was pressed. * + * * + * WARNINGS: You must declare a WWKeyboardClass object before calling * + * this routine. * + * * + * HISTORY: * + * 10/26/1995 : Created. * + *=========================================================================*/ +int Check_Key(void) +{ + if (!_Kbd) return(KA_NONE); + return(_Kbd->Check() & ~WWKEY_SHIFT_BIT); +} + +void Clear_KeyBuffer(void) +{ + if (!_Kbd) return; + _Kbd->Clear(); +} + +int Check_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Check(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !flags & WWKEY_VK_BIT ) { + flags |= WWKEY_SHIFT_BIT; + } + } + + return(key | flags); +} + +int Get_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Get(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !flags & WWKEY_VK_BIT ) { + flags |= WWKEY_SHIFT_BIT; + } + } + return(key | flags); +} + +int KN_To_KA(int key) +{ + if ( key & WWKEY_RLS_BIT) { + return(KA_NONE); + } + if (!(key & WWKEY_VK_BIT)) { + int flags = key & 0xFF00; + key = key & 0x00FF; + if (flags & WWKEY_SHIFT_BIT) { + key = toupper(key); + flags &= ~WWKEY_SHIFT_BIT; + } + }else{ + /* + ** If its a numeric keypad key then fix it up + */ + if ((key & 0xff) >=VK_NUMPAD0 && (key & 0xff) <=VK_NUMPAD9){ + key = (key & 0xff) - VK_NUMPAD0 + KA_0; + } + } + return(key); +} + +int KN_To_VK(int key) +{ + if (!_Kbd) return(KN_NONE); + if ( key & WWKEY_RLS_BIT) { + return(VK_NONE); + } + + int flags = key & 0xFF00; + if (!(flags & WWKEY_VK_BIT)) { + key = _Kbd->VKRemap[key & 0x00FF] | flags; + } + key &= ~WWKEY_VK_BIT; + return(key); +} + +int Key_Down(int key) +{ + if (!_Kbd) return(FALSE); + return(_Kbd->Down(key)); +} + +int Get_Key(void) +{ + int retval; + + if (!_Kbd) return(KN_NONE); + retval = _Kbd->Get() & ~WWKEY_SHIFT_BIT; + if (retval & WWKEY_RLS_BIT) { + retval = KN_NONE; + } + return(retval); +} + diff --git a/WIN32LIB/KEYBOARD/KEYBOARD.H b/WIN32LIB/KEYBOARD/KEYBOARD.H new file mode 100644 index 0000000..9df1f7e --- /dev/null +++ b/WIN32LIB/KEYBOARD/KEYBOARD.H @@ -0,0 +1,678 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + unsigned char VKRemap[256]; // gives vk for any ascii char + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT | WWKEY_VK_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT | WWKEY_VK_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); +int KN_To_VK(int key); + +#endif diff --git a/WIN32LIB/KEYBOARD/MAKEFILE b/WIN32LIB/KEYBOARD/MAKEFILE new file mode 100644 index 0000000..c0f0861 --- /dev/null +++ b/WIN32LIB/KEYBOARD/MAKEFILE @@ -0,0 +1,181 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + wwmouse.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/MAKEFILE.BOR b/WIN32LIB/KEYBOARD/MAKEFILE.BOR new file mode 100644 index 0000000..0ffea62 --- /dev/null +++ b/WIN32LIB/KEYBOARD/MAKEFILE.BOR @@ -0,0 +1,168 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + keyboard.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+keyboard.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/KEYBOARD/MAKEFILE.WAT b/WIN32LIB/KEYBOARD/MAKEFILE.WAT new file mode 100644 index 0000000..7732760 --- /dev/null +++ b/WIN32LIB/KEYBOARD/MAKEFILE.WAT @@ -0,0 +1,179 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/MOUSE.CPP b/WIN32LIB/KEYBOARD/MOUSE.CPP new file mode 100644 index 0000000..3520afe --- /dev/null +++ b/WIN32LIB/KEYBOARD/MOUSE.CPP @@ -0,0 +1,673 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); +extern BOOL GameInFocus; + + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + InitializeCriticalSection (&MouseCriticalSection); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); + + /* + ** Force the windows mouse pointer to stay withing the graphic view port region + */ + Set_Cursor_Clip(); +} + +WWMouseClass::~WWMouseClass() +{ + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + } + timeEndPeriod (1000/60); + DeleteCriticalSection(&MouseCriticalSection); + + /* + ** Free up the windows mouse pointer movement + */ + Clear_Cursor_Clip(); +} + + +void Block_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Block_Mouse(buffer); + } +} + + +void Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Unblock_Mouse(buffer); + } +} + + + +void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + EnterCriticalSection(&MouseCriticalSection); + } +} + + +void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + LeaveCriticalSection(&MouseCriticalSection); + } +} + + + + + +void WWMouseClass::Set_Cursor_Clip(void) +{ + + if (Screen){ + RECT region; + + region.left = 0; + region.top = 0; + region.right = Screen->Get_Width(); + region.bottom = Screen->Get_Height(); + + ClipCursor(®ion); + } +} + + + +void WWMouseClass::Clear_Cursor_Clip(void) +{ + ClipCursor(NULL); +} + + + +void WWMouseClass::Process_Mouse(void) +{ + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!Screen || !_Mouse || State > 0 || MouseUpdate || EraseFlags || !GameInFocus) + return; + + // + // Make sure there are no conflicts with other + // threads that may try and lock the screen buffer + // + //Block_Mouse(Screen->Get_Graphic_Buffer()); + + // + // If the screen is already locked by another thread then just exit + // + if (Screen->Get_LockCount()!=0){ + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); + return; + } + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } + + // + // Allow other threads to lock the screen again + // + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +} + +void WWMouseClass::Low_Hide_Mouse() +{ + if (!State) { + if (MouseBuffX != -1 || MouseBuffY != -1) { + if (Screen->Lock()){ + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + Screen->Unlock(); + } + } + MouseBuffX = -1; + MouseBuffY = -1; + } + State++; +} +void WWMouseClass::Hide_Mouse() +{ + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + if (Screen->Lock()){ + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } + } +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) { + Low_Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; +} + + +void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr) +{ + POINT pt; + + if (State != 0) return; + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen - dont do video stuff if we cant. + // + if (scr->Lock()){ + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + } + + MouseUpdate--; +} + +void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced) +{ + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + if (scr->Lock()){ + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + scr->Unlock(); + } + EraseBuffX = -1; + EraseBuffY = -1; + } + } + MouseUpdate--; + EraseFlags = FALSE; +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +#pragma off(unreferenced) + +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + static BOOL in_mouse_callback = false; + + if (_Mouse && !in_mouse_callback) { + in_mouse_callback = TRUE; + _Mouse->Process_Mouse(); + in_mouse_callback = FALSE; + } +} +#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +int Get_Mouse_X(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} diff --git a/WIN32LIB/KEYBOARD/MOUSE.H b/WIN32LIB/KEYBOARD/MOUSE.H new file mode 100644 index 0000000..3291bab --- /dev/null +++ b/WIN32LIB/KEYBOARD/MOUSE.H @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +class WWMouseClass { + public: + WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, void *cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicViewPortClass *scr); + void Erase_Mouse(GraphicViewPortClass *scr, int forced = FALSE); + + void Block_Mouse(GraphicBufferClass *buffer); + void Unblock_Mouse(GraphicBufferClass *buffer); + void Set_Cursor_Clip(void); + void Clear_Cursor_Clip(void); + + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicViewPortClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + CRITICAL_SECTION MouseCriticalSection; // Control for mouse re-enterancy + unsigned TimerHandle; + +}; + +extern "C" { + void __cdecl Mouse_Shadow_Buffer(void *thisptr, GraphicViewPortClass *srcdst, void *buffer, int x, int y, int hotx, int hoty, int store); + void __cdecl Draw_Mouse(void *thisptr, GraphicViewPortClass *srcdst, int x, int y); + void * __cdecl ASM_Set_Mouse_Cursor(void * thisptr, int hotspotx, int hotspoty, VOID *cursor); +}; + +void Hide_Mouse(void); +void Show_Mouse(void); +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/WIN32LIB/KEYBOARD/MOUSE.INC b/WIN32LIB/KEYBOARD/MOUSE.INC new file mode 100644 index 0000000..f936787 --- /dev/null +++ b/WIN32LIB/KEYBOARD/MOUSE.INC @@ -0,0 +1,67 @@ +; +; 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 . +; + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/WIN32LIB/KEYBOARD/OLD/KEYBOARD.ASM b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.ASM new file mode 100644 index 0000000..c426941 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.ASM @@ -0,0 +1,2520 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + ; This routine will do what it needs to, + ; then it will decide if the old vector should be called. + ; if so, it calls it and never returns to this function. + ; if not, we do our own return. + ; the method for doing this is found in: + ; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142" + ; It says: + ; 1 - Execute a PUSHFD to save the current flags. + ; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler + ; 3 - Push register I use. + ; 4 - Do any processing. + ; 5 - Put the address of the original handler in the reserved slot. + ; 6 - Pop saved register values + ; 7 - Execute an IRETD to transfer control to original handler. + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + +IF DEBUG + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + + ; Step 5. + ; Now it is time to set up for the call by returning by poking + ; the old interupt handle address in. + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + mov eax,[(KeyboardType PTR esi).KeyOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).KeyOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ; Step 6. + pop fs gs es ds + popad + pop ebp + + ; Step 7. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + pop fs gs es ds + popad + + pop ebp + add esp,8 + popfd + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WIN32LIB/KEYBOARD/OLD/KEYBOARD.BAK b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.BAK new file mode 100644 index 0000000..95ec9cb --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.BAK @@ -0,0 +1,490 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 26, 1995 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +void Message_Loop(void); + +WWKeyboardClass *_Kbd; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + _Kbd = this; + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 256); + memset(AsciiRemap, 0, 2048); + for (short lp = 31; lp < 255; lp ++) { +!----- Conflict 1 - Base File: C:\TEMP\KEYBOARD.CPP( *** PARENT *** ) +!----- File 1: D:\WIN32LIB\KEYBOARD\KEYBOARD.CPP( *** Primary *** ) + int vk_key = VkKeyScan((unsigned char)lp); +!----- File 2: I:\WIN32LIB\KEYBOARD\KEYBOARD.CPP( *** Secondary *** ) + int vk_key = (int)VkKeyScan((TCHAR)lp); +!----- End Conflict + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = (unsigned char)lp; + VKRemap[lp] = (unsigned char)(vk_key & 0xFF); + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; + MState = 0; + Conditional = 0; + CurrentCursor = NULL; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[Head+1]; // get the x and y pos + MouseQY = Buffer[Head+2]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + Message_Loop(); + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = (short)key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. + // + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + +int WWKeyboardClass::To_ASCII(int key) +{ + return(key); +} + +WWKeyboardClass::Down(int key) +{ + return(GetAsyncKeyState(key&0xFF)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + +#pragma off(unreferenced) +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message(wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + case WM_MOUSEMOVE: + if (CurrentCursor) + SetCursor(CurrentCursor); + break; + } +} +#pragma on(unreferenced) + + + +extern BOOL GameInFocus; + +void Message_Loop(void) +{ + MSG msg; + + do { + + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } while (!GameInFocus); +} + + +/*************************************************************************** + * CHECK_KEY -- compatability routine for old 32 bit library * + * * + * This routine checks to see if there is a key in the keyboard buffer * + * and returns it to the sender if there is. It does not remove the key * + * from the buffer. * + * * + * INPUT: none * + * * + * OUTPUT: The key that was pressed. * + * * + * WARNINGS: You must declare a WWKeyboardClass object before calling * + * this routine. * + * * + * HISTORY: * + * 10/26/1995 : Created. * + *=========================================================================*/ +int Check_Key(void) +{ + if (!_Kbd) return(KA_NONE); + return(_Kbd->Check() & ~WWKEY_SHIFT_BIT); +} + +void Clear_KeyBuffer(void) +{ + if (!_Kbd) return; + _Kbd->Clear(); +} + +int Check_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Check(); + int flags = key & 0xFF00; + key = key & 0x00FF; + if (isupper(key)) { + key = tolower(key); + flags |= WWKEY_SHIFT_BIT; + } + + return(key | flags); +} + +int Get_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Get(); + int flags = key & 0xFF00; + key = key & 0x00FF; + if (isupper(key)) { + key = tolower(key); + flags |= WWKEY_SHIFT_BIT; + } + + return(key | flags); +} + +int KN_To_KA(int key) +{ + if (!(key & WWKEY_VK_BIT)) { + int flags = key & 0xFF00; + key = key & 0x00FF; + if (flags & WWKEY_SHIFT_BIT) { + key = toupper(key); + flags &= ~WWKEY_SHIFT_BIT; + } + } + return(key); +} + +int Key_Down(int key) +{ + if (!_Kbd) return(FALSE); + return(_Kbd->Down(key)); +} + +int Get_Key(void) +{ + if (!_Kbd) return(KN_NONE); + return(_Kbd->Get() & ~WWKEY_SHIFT_BIT); +} + diff --git a/WIN32LIB/KEYBOARD/OLD/KEYBOARD.CPP b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.CPP new file mode 100644 index 0000000..8208d6d --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.CPP @@ -0,0 +1,485 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 26, 1995 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +void Message_Loop(void); + +WWKeyboardClass *_Kbd; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + _Kbd = this; + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 256); + memset(AsciiRemap, 0, 2048); + for (short lp = 31; lp < 255; lp ++) { + int vk_key = VkKeyScan((unsigned char)lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = (unsigned char)lp; + VKRemap[lp] = (unsigned char)(vk_key & 0xFF); + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; + MState = 0; + Conditional = 0; + CurrentCursor = NULL; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[Head+1]; // get the x and y pos + MouseQY = Buffer[Head+2]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + Message_Loop(); + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = (short)key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. + // + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + +int WWKeyboardClass::To_ASCII(int key) +{ + return(key); +} + +WWKeyboardClass::Down(int key) +{ + return(GetAsyncKeyState(key&0xFF)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + +#pragma off(unreferenced) +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message(wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + case WM_MOUSEMOVE: + if (CurrentCursor) + SetCursor(CurrentCursor); + break; + } +} +#pragma on(unreferenced) + + + +extern BOOL GameInFocus; + +void Message_Loop(void) +{ + MSG msg; + + do { + + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } while (!GameInFocus); +} + + +/*************************************************************************** + * CHECK_KEY -- compatability routine for old 32 bit library * + * * + * This routine checks to see if there is a key in the keyboard buffer * + * and returns it to the sender if there is. It does not remove the key * + * from the buffer. * + * * + * INPUT: none * + * * + * OUTPUT: The key that was pressed. * + * * + * WARNINGS: You must declare a WWKeyboardClass object before calling * + * this routine. * + * * + * HISTORY: * + * 10/26/1995 : Created. * + *=========================================================================*/ +int Check_Key(void) +{ + if (!_Kbd) return(KA_NONE); + return(_Kbd->Check() & ~WWKEY_SHIFT_BIT); +} + +void Clear_KeyBuffer(void) +{ + if (!_Kbd) return; + _Kbd->Clear(); +} + +int Check_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Check(); + int flags = key & 0xFF00; + key = key & 0x00FF; + if (isupper(key)) { + key = tolower(key); + flags |= WWKEY_SHIFT_BIT; + } + + return(key | flags); +} + +int Get_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Get(); + int flags = key & 0xFF00; + key = key & 0x00FF; + if (isupper(key)) { + key = tolower(key); + flags |= WWKEY_SHIFT_BIT; + } + + return(key | flags); +} + +int KN_To_KA(int key) +{ + if (!(key & WWKEY_VK_BIT)) { + int flags = key & 0xFF00; + key = key & 0x00FF; + if (flags & WWKEY_SHIFT_BIT) { + key = toupper(key); + flags &= ~WWKEY_SHIFT_BIT; + } + } + return(key); +} + +int Key_Down(int key) +{ + if (!_Kbd) return(FALSE); + return(_Kbd->Down(key)); +} + +int Get_Key(void) +{ + if (!_Kbd) return(KN_NONE); + return(_Kbd->Get() & ~WWKEY_SHIFT_BIT); +} + diff --git a/WIN32LIB/KEYBOARD/OLD/KEYBOARD.H b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.H new file mode 100644 index 0000000..f0c8bfc --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.H @@ -0,0 +1,671 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned char VKRemap[256]; // gives vk for any ascii char + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); diff --git a/WIN32LIB/KEYBOARD/OLD/KEYBOARD.INC b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.INC new file mode 100644 index 0000000..960ab3a --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left + ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WIN32LIB/KEYBOARD/OLD/KEYIPROT.ASM b/WIN32LIB/KEYBOARD/OLD/KEYIPROT.ASM new file mode 100644 index 0000000..a621713 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYIPROT.ASM @@ -0,0 +1,1023 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] + retf + +STACK ; Don't really need this + +;*********************************************************** +END diff --git a/WIN32LIB/KEYBOARD/OLD/KEYIREAL.ASM b/WIN32LIB/KEYBOARD/OLD/KEYIREAL.ASM new file mode 100644 index 0000000..338c45e --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYIREAL.ASM @@ -0,0 +1,2476 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:KeyOldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + +;------ Current mouse state button flag globals update. + mov [state],bx + mov [cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + + cmp [MInput],0 + je ??exit + + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[state] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WIN32LIB/KEYBOARD/OLD/KEYIREAL.IBN b/WIN32LIB/KEYBOARD/OLD/KEYIREAL.IBN new file mode 100644 index 0000000..99ff17a --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYIREAL.IBN @@ -0,0 +1,279 @@ + DB 001h,000h,001h,000h,011h,000h,000h,000h,000h,000h,0FFh,000h,001h,0F0h,000h + DB 010h,0A0h,000h,000h,000h,03Fh,001h,000h,000h,03Fh,001h,045h,000h,03Fh,001h + DB 089h,000h,0A0h,000h,089h,000h,000h,000h,089h,000h,000h,000h,045h,000h,000h + DB 000h,000h,000h,0A0h,000h,045h,000h,001h,002h,004h,008h,010h,020h,040h,080h + DB 020h,002h,020h,003h,00Ch,006h,00Dh,007h,06Ah,006h,069h,006h,030h,002h,030h + DB 003h,07Dh,000h,07Dh,001h,05Ah,002h,05Ah,003h,000h,002h,010h,004h,06Eh,004h + DB 06Eh,002h,07Ch,000h,008h,000h,008h,000h,010h,000h,010h,000h,010h,000h,010h + DB 000h,020h,000h,020h,000h,040h,000h,040h,000h,080h,000h,080h,000h,000h,001h + DB 000h,004h,000h,004h,000h,004h,000h,001h,000h,000h,000h,000h,038h,01Dh,052h + DB 053h,04Bh,047h,04Fh,048h,050h,049h,051h,04Dh,035h,01Ch,037h,046h,03Eh,040h + DB 04Bh,04Ch,04Fh,050h,051h,053h,054h,055h,056h,059h,05Fh,06Ch,07Ch,000h,03Ch + DB 03Ah,063h,068h,05Ch,05Bh,05Dh,060h,062h,065h,067h,066h,037h,02Bh,07Ch,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,06Eh + DB 002h,003h,004h,005h,006h,007h,008h,009h,00Ah,00Bh,00Ch,00Dh,00Fh,010h,011h + DB 012h,013h,014h,015h,016h,017h,018h,019h,01Ah,01Bh,01Ch,02Bh,03Ah,01Fh,020h + DB 021h,022h,023h,024h,025h,026h,027h,028h,029h,001h,02Ch,01Dh,02Eh,02Fh,030h + DB 031h,032h,033h,034h,035h,036h,037h,039h,064h,03Ch,03Dh,01Eh,070h,071h,072h + DB 073h,074h,075h,076h,077h,078h,079h,05Ah,07Dh,05Bh,060h,065h,069h,05Ch,061h + DB 066h,06Ah,05Dh,062h,067h,063h,068h,07Fh,07Fh,07Fh,07Ah,07Bh,000h,000h,0FEh + DB 087h,0FFh,0C0h,01Fh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,038h,0EFh,001h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,01Eh,02Ch,039h,03Ah,03Ch,03Eh,040h,05Ah + DB 080h,080h,080h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,0FFh,001h,000h + DB 000h,000h,000h,000h,0FFh,000h,000h,000h,001h,000h,000h,000h,000h,001h,0FFh + DB 001h,000h,001h,001h,008h,000h,002h,000h,008h,000h,006h,000h,004h,000h,003h + DB 000h,008h,000h,005h,000h,008h,000h,008h,000h,008h,000h,008h,000h,000h,000h + DB 001h,000h,008h,000h,007h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,018h,006h,07Ch,008h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,06Bh + DB 008h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,06Eh + DB 009h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0E4h,00Bh + DB 090h,00Ah,095h,00Eh,01Ch,00Dh,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,0C8h,000h,000h,000h,051h,057h,006h,01Eh + DB 08Ch,0C8h,08Eh,0D8h,08Eh,0C0h,08Bh,046h,006h,0F7h,006h,004h,000h,002h,000h + DB 075h,013h,0B9h,010h,000h,0BFh,094h,000h,0F2h,0AEh,0E3h,009h,0BFh,0B4h,000h + DB 04Fh,02Bh,0F9h,026h,08Ah,005h,01Fh,007h,05Fh,059h,0C9h,0CBh,0C8h,000h,000h + DB 000h,056h,053h,01Eh,08Ch,0C8h,08Eh,0D8h,0A1h,0BAh,001h,08Bh,0F0h,005h,002h + DB 000h,025h,0FFh,000h,039h,006h,0B6h,001h,074h,011h,08Bh,05Eh,006h,089h,09Ch + DB 0B6h,000h,0A3h,0BAh,001h,033h,0C0h,01Fh,05Bh,05Eh,0C9h,0CBh,0B8h,001h,000h + DB 01Fh,05Bh,05Eh,0C9h,0CBh,0C8h,004h,000h,000h,053h,051h,052h,057h,056h,01Eh + DB 09Ch,0FAh,08Ch,0C8h,08Eh,0D8h,0A1h,0ABh,002h,0A3h,0AFh,002h,0A1h,0B1h,002h + DB 0A3h,0B5h,002h,08Bh,046h,006h,00Bh,0C0h,075h,003h,0E9h,0F7h,001h,0F7h,006h + DB 004h,000h,000h,010h,00Fh,084h,03Ah,001h,0F6h,0C4h,004h,00Fh,085h,033h,001h + DB 0F7h,006h,004h,000h,000h,020h,074h,00Ch,03Ch,02Bh,074h,00Ch,03Ch,03Dh,074h + DB 008h,03Ch,06Ch,074h,004h,03Ch,063h,075h,016h,0B0h,041h,080h,00Eh,0F6h,002h + DB 001h,0F6h,0C4h,008h,00Fh,084h,00Ah,001h,080h,026h,0F6h,002h,0FEh,0E9h,002h + DB 001h,03Ch,068h,075h,016h,0B0h,042h,080h,00Eh,0F6h,002h,002h,0F6h,0C4h,008h + DB 00Fh,084h,0F0h,000h,080h,026h,0F6h,002h,0FDh,0E9h,0E8h,000h,0F6h,0C4h,008h + DB 00Fh,085h,0E4h,000h,03Ch,061h,074h,01Eh,03Ch,05Bh,00Fh,082h,0DAh,000h,03Ch + DB 067h,00Fh,087h,0D4h,000h,03Ch,05Dh,076h,00Eh,03Ch,065h,073h,00Ah,03Ch,060h + DB 074h,006h,03Ch,062h,00Fh,085h,0C2h,000h,08Ah,0ECh,032h,0E4h,02Ch,05Bh,08Bh + DB 0D8h,0D1h,0E3h,081h,0C3h,06Fh,002h,08Bh,007h,08Ah,0DCh,098h,093h,098h,093h + DB 0F6h,0C5h,002h,075h,021h,0BAh,001h,000h,0F6h,0C5h,001h,074h,003h,083h,0C2h + DB 003h,003h,0DAh,08Ah,09Fh,00Ah,000h,093h,098h,093h,087h,0DAh,003h,0D8h,08Ah + DB 087h,00Ah,000h,098h,087h,0DAh,0EBh,034h,083h,0E3h,003h,025h,003h,000h,083h + DB 0E3h,003h,025h,003h,000h,0D1h,0E3h,0D1h,0E3h,00Bh,0D8h,0D1h,0E3h,08Bh,09Fh + DB 089h,002h,0D1h,0E3h,0D1h,0E3h,08Bh,087h,010h,000h,08Bh,09Fh,012h,000h,0A3h + DB 0AFh,002h,089h,01Eh,0B5h,002h,0A1h,0AFh,002h,08Bh,01Eh,0B5h,002h,0EBh,02Fh + DB 001h,006h,0AFh,002h,079h,005h,033h,0C0h,0A3h,0AFh,002h,001h,01Eh,0B5h,002h + DB 079h,006h,033h,0DBh,089h,01Eh,0B5h,002h,0A1h,0AFh,002h,08Bh,01Eh,0B5h,002h + DB 03Dh,03Fh,001h,07Eh,003h,0B8h,03Fh,001h,081h,0FBh,0C7h,000h,07Eh,003h,0BBh + DB 0C7h,000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h,0A3h,0ABh,002h,089h,01Eh,0B1h + DB 002h,083h,03Eh,0A9h,002h,000h,075h,008h,00Eh,0E8h,030h,003h,00Eh,0E8h,05Fh + DB 003h,0B8h,02Dh,000h,089h,046h,006h,08Bh,036h,0BAh,001h,089h,076h,0FEh,08Bh + DB 03Eh,0B6h,001h,050h,00Eh,0E8h,051h,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,011h + DB 08Bh,046h,006h,03Ch,02Dh,074h,00Dh,03Ch,041h,074h,009h,03Ch,042h,074h,005h + DB 0EBh,029h,0E9h,080h,000h,0FFh,036h,0AFh,002h,00Eh,0E8h,02Eh,0FEh,083h,0C4h + DB 002h,00Bh,0C0h,075h,0EEh,083h,046h,0FCh,002h,0FFh,036h,0B5h,002h,00Eh,0E8h + DB 01Bh,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0DBh,083h,046h,0FCh,002h,0BBh,001h + DB 001h,08Bh,046h,006h,03Ch,02Dh,074h,01Bh,03Ch,07Fh,074h,017h,0F6h,0C4h,008h + DB 074h,016h,032h,0DBh,0F7h,006h,004h,000h,000h,008h,075h,00Ch,03Ch,041h,074h + DB 008h,03Ch,042h,074h,004h,089h,036h,0BAh,001h,08Bh,0F8h,083h,0E7h,07Fh,0B1h + DB 003h,0D3h,0EFh,08Ah,0C8h,080h,0E1h,007h,0D3h,0E3h,0F6h,0D7h,084h,09Dh,039h + DB 002h,074h,00Ch,0F7h,006h,004h,000h,001h,000h,075h,004h,089h,036h,0BAh,001h + DB 020h,0BDh,039h,002h,008h,09Dh,039h,002h,0B8h,001h,000h,0EBh,008h,08Bh,046h + DB 0FEh,0A3h,0BAh,001h,033h,0C0h,09Dh,01Fh,05Eh,05Fh,05Ah,059h,05Bh,0C9h,0CBh + DB 050h,053h,051h,057h,01Eh,052h,006h,056h,0FCh,08Ch,0C8h,08Eh,0D8h,083h,03Eh + DB 0CEh,002h,000h,0C7h,006h,0CEh,002h,000h,000h,00Fh,085h,0FDh,001h,08Bh,016h + DB 004h,000h,083h,026h,0BEh,001h,0FCh,0B8h,040h,000h,08Eh,0C0h,026h,0F6h,006h + DB 017h,000h,040h,074h,005h,083h,00Eh,0BEh,001h,001h,026h,0F6h,006h,017h,000h + DB 020h,074h,005h,083h,00Eh,0BEh,001h,002h,0C6h,006h,0B5h,000h,001h,026h,0F6h + DB 006h,096h,000h,010h,075h,005h,0C6h,006h,0B5h,000h,000h,08Ch,0C8h,08Eh,0C0h + DB 0FCh,032h,0E4h,0BBh,001h,001h,0E4h,060h,08Bh,01Eh,05Bh,002h,088h,087h,049h + DB 002h,043h,083h,0E3h,00Fh,089h,01Eh,05Bh,002h,0BBh,001h,001h,03Ch,0E1h,075h + DB 005h,0C6h,006h,05Eh,002h,003h,080h,03Eh,05Eh,002h,000h,074h,00Fh,0FEh,00Eh + DB 05Eh,002h,0F7h,0C2h,080h,000h,00Fh,085h,088h,001h,0E9h,097h,001h,03Ch,0E0h + DB 075h,008h,0C6h,006h,05Dh,002h,001h,0E9h,079h,001h,0A8h,080h,074h,007h,032h + DB 0DBh,024h,07Fh,080h,0CCh,008h,080h,03Eh,05Dh,002h,000h,074h,023h,0C6h,006h + DB 05Dh,002h,000h,0C6h,006h,0B7h,002h,001h,0BFh,084h,000h,0B9h,010h,000h,0F2h + DB 0AEh,0E3h,002h,0EBh,003h,0E9h,060h,001h,08Ah,045h,00Fh,0C6h,006h,0B7h,002h + DB 000h,0EBh,011h,03Ch,07Ah,075h,004h,0B0h,080h,0EBh,009h,08Bh,0F8h,083h,0E7h + DB 07Fh,08Ah,085h,0C0h,001h,0F6h,006h,041h,002h,001h,075h,01Bh,0F6h,006h,040h + DB 002h,004h,074h,017h,03Ch,05Ah,075h,010h,080h,03Eh,0B5h,000h,001h,074h,009h + DB 0F7h,0C2h,080h,000h,075h,003h,0E9h,023h,001h,080h,0CCh,002h,0F6h,006h,040h + DB 002h,050h,074h,003h,080h,0CCh,004h,050h,00Eh,0E8h,059h,0FCh,083h,0C4h,002h + DB 0F6h,006h,03Eh,002h,010h,075h,007h,0F6h,006h,040h,002h,002h,074h,003h,080h + DB 0CCh,001h,08Bh,0F8h,083h,0E7h,07Fh,0D1h,0EFh,0D1h,0EFh,0D1h,0EFh,08Bh,0D8h + DB 083h,0E3h,07Fh,080h,0E3h,007h,08Ah,0AFh,034h,000h,0F7h,006h,0BEh,001h,001h + DB 000h,074h,009h,084h,0ADh,019h,002h,074h,003h,080h,0F4h,001h,0F7h,006h,0BEh + DB 001h,002h,000h,074h,009h,084h,0ADh,029h,002h,074h,003h,080h,0F4h,001h,088h + DB 026h,06Ah,002h,066h,083h,03Eh,0EAh,002h,001h,075h,01Bh,03Dh,073h,000h,074h + DB 00Ah,03Dh,076h,000h,072h,00Eh,03Dh,078h,000h,077h,009h,066h,0C7h,006h,0EAh + DB 002h,000h,000h,000h,000h,0E9h,08Bh,000h,03Dh,07Dh,000h,075h,00Ch,066h,0C7h + DB 006h,0EAh,002h,001h,000h,000h,000h,0E9h,08Ch,000h,03Dh,068h,006h,074h,075h + DB 03Dh,04Ch,006h,074h,070h,03Dh,030h,002h,074h,005h,03Dh,07Eh,002h,075h,006h + DB 0C7h,006h,008h,000h,001h,000h,03Dh,020h,004h,075h,00Ch,050h,0A1h,000h,000h + DB 035h,001h,000h,050h,083h,0C4h,002h,058h,03Dh,034h,004h,075h,00Ch,050h,0A1h + DB 002h,000h,035h,001h,000h,050h,083h,0C4h,002h,058h,050h,00Eh,0E8h,0FAh,0FBh + DB 058h,03Ch,06Eh,075h,00Dh,083h,03Eh,082h,000h,000h,074h,006h,050h,0FFh,01Eh + DB 080h,000h,058h,0BFh,05Fh,002h,0B9h,00Ah,000h,0F2h,0AEh,00Bh,0C9h,075h,01Bh + DB 0BFh,03Ch,000h,0B9h,022h,000h,0D1h,0E9h,0F2h,0AFh,0E3h,009h,08Bh,05Dh,020h + DB 023h,0DAh,074h,002h,0EBh,006h,0F7h,0C2h,004h,000h,074h,012h,05Eh,007h,05Ah + DB 01Fh,05Fh,059h,05Bh,058h,02Eh,0FFh,006h,059h,002h,02Eh,0FFh,02Eh,0BAh,002h + DB 0E4h,061h,08Ah,0E0h,00Ch,080h,0E6h,061h,086h,0E0h,0E6h,061h,0B8h,040h,000h + DB 08Eh,0C0h,026h,0A0h,096h,000h,024h,0FDh,026h,0A2h,096h,000h,0B0h,020h,0E6h + DB 020h,05Eh,007h,05Ah,01Fh,05Fh,059h,05Bh,058h,0CFh,09Ch,050h,006h,0B8h,000h + DB 0B0h,08Eh,0C0h,026h,0FEh,006h,000h,000h,007h,058h,09Dh,0CFh,09Ch,00Eh,0E8h + DB 097h,0FDh,0CBh,050h,053h,051h,052h,01Eh,08Ch,0C8h,08Eh,0D8h,080h,03Eh,0F7h + DB 002h,000h,075h,01Dh,083h,03Eh,003h,003h,000h,075h,00Ch,0B8h,000h,000h,050h + DB 00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,083h,006h,003h,003h,001h,083h,016h + DB 003h,003h,000h,01Fh,05Ah,059h,05Bh,058h,0CBh,0C8h,004h,000h,000h,050h,053h + DB 051h,052h,056h,057h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,080h,03Eh,0F7h,002h,000h + DB 075h,050h,083h,03Eh,003h,003h,000h,074h,049h,0FFh,00Eh,003h,003h,083h,03Eh + DB 003h,003h,000h,075h,03Eh,08Bh,00Eh,03Fh,003h,08Bh,016h,043h,003h,0A1h,0ABh + DB 002h,089h,046h,0FEh,08Bh,01Eh,0B1h,002h,089h,05Eh,0FCh,0A3h,02Fh,003h,089h + DB 01Eh,033h,003h,089h,00Eh,037h,003h,089h,016h,03Bh,003h,0B8h,001h,000h,050h + DB 00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,0FFh,076h,0FCh,0FFh,076h,0FEh,00Eh + DB 0FFh,016h,053h,003h,083h,0C4h,004h,007h,01Fh,05Fh,05Eh,05Ah,059h,05Bh,058h + DB 0C9h,0CBh,0C8h,002h,000h,000h,053h,08Ch,0C8h,08Eh,0D8h,0C7h,046h,0FEh,02Dh + DB 000h,08Bh,05Eh,006h,08Bh,0C3h,032h,01Eh,0F6h,002h,074h,029h,0A2h,0F6h,002h + DB 0F6h,0C3h,002h,074h,00Eh,0C7h,046h,0FEh,042h,000h,0A8h,002h,075h,005h,081h + DB 04Eh,0FEh,000h,008h,0F6h,0C3h,001h,074h,00Eh,0C7h,046h,0FEh,041h,000h,0A8h + DB 001h,075h,005h,081h,04Eh,0FEh,000h,008h,08Bh,046h,0FEh,05Bh,0C9h,0CBh,0C8h + DB 004h,000h,000h,050h,053h,051h,052h,01Eh,056h,006h,057h,089h,05Eh,0FCh,089h + DB 046h,0FEh,08Ch,0C8h,08Eh,0D8h,080h,03Eh,0F7h,002h,000h,00Fh,085h,0F9h,000h + DB 080h,03Eh,0F8h,002h,000h,00Fh,084h,0F0h,000h,08Bh,046h,0FEh,025h,01Eh,000h + DB 075h,009h,083h,03Eh,0A9h,002h,000h,00Fh,085h,0DFh,000h,083h,03Eh,0F9h,002h + DB 001h,075h,002h,0D1h,0E9h,03Bh,00Eh,049h,003h,072h,005h,08Bh,00Eh,049h,003h + DB 049h,03Bh,016h,04Dh,003h,072h,005h,08Bh,016h,04Dh,003h,04Ah,089h,00Eh,0ABh + DB 002h,089h,016h,0B1h,002h,066h,0F7h,006h,004h,000h,000h,010h,000h,000h,075h + DB 012h,0FFh,076h,0FCh,00Eh,0E8h,046h,0FFh,083h,0C4h,002h,050h,00Eh,0E8h,00Ah + DB 0FAh,083h,0C4h,002h,083h,03Eh,0A9h,002h,000h,00Fh,085h,092h,000h,083h,03Eh + DB 0FBh,002h,000h,074h,019h,08Bh,0C1h,08Bh,0CAh,02Bh,006h,0FFh,002h,08Bh,01Eh + DB 0FBh,002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,0FFh,002h,08Bh,0D1h,08Bh,0C8h + DB 083h,03Eh,0FDh,002h,000h,074h,015h,08Bh,0C2h,02Bh,006h,001h,003h,08Bh,01Eh + DB 0FDh,002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,001h,003h,08Bh,0D0h,083h,03Eh + DB 003h,003h,000h,075h,047h,039h,00Eh,007h,003h,075h,006h,039h,016h,009h,003h + DB 074h,03Bh,0A1h,00Bh,003h,025h,000h,0C0h,03Dh,000h,0C0h,074h,00Ch,00Eh,0E8h + DB 030h,0FEh,0F7h,006h,00Bh,003h,000h,080h,074h,020h,03Bh,00Eh,00Dh,003h,072h + DB 01Ah,03Bh,00Eh,015h,003h,077h,014h,03Bh,016h,011h,003h,072h,00Eh,03Bh,016h + DB 019h,003h,077h,008h,081h,00Eh,00Bh,003h,000h,040h,0EBh,004h,00Eh,0E8h,037h + DB 0FEh,089h,00Eh,007h,003h,089h,016h,009h,003h,05Fh,007h,05Eh,01Fh,05Ah,059h + DB 05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh + DB 006h,08Ch,0C8h,08Eh,0D8h,0FCh,0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h + DB 027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h + DB 003h,003h,01Eh,03Bh,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h + DB 000h,0A1h,023h,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh + DB 05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh + DB 0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h + DB 003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h + DB 0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh + DB 04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h + DB 0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,0AEh,000h,00Ah,0C2h,074h,040h,0F6h + DB 0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh + DB 000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h + DB 037h,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h + DB 006h,0A1h,049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h + DB 089h,046h,0F8h,0B8h,000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h + DB 08Bh,016h,049h,003h,08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h,0C5h,036h + DB 023h,003h,08Bh,076h,0F4h,003h,076h,0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh + DB 046h,0FEh,07Eh,03Ch,02Bh,04Eh,0FCh,07Eh,037h,02Bh,0D0h,02Bh,0D8h,055h,083h + DB 07Eh,006h,000h,075h,018h,08Bh,0E9h,08Bh,0C8h,0F3h,0A4h,003h,0F3h,003h,0FAh + DB 04Dh,075h,0F5h,05Dh,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,087h + DB 0F7h,08Bh,0E9h,006h,01Eh,007h,01Fh,08Bh,0C8h,0F3h,0A4h,003h,0F2h,003h,0FBh + DB 04Dh,075h,0F5h,05Dh,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h + DB 00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h + DB 08Bh,046h,006h,08Bh,05Eh,008h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h + DB 046h,0FEh,089h,05Eh,0FCh,003h,006h,03Fh,003h,003h,01Eh,043h,003h,089h,046h + DB 0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0C4h,006h,01Dh,003h,089h,046h + DB 0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h + DB 003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh + DB 01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh + DB 08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h + DB 00Fh,085h,093h,000h,00Ah,0C2h,074h,042h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh + DB 0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h + DB 013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h,0C7h + DB 046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,007h,08Bh,01Eh,049h,003h,089h + DB 05Eh,0FAh,0F6h,0C2h,001h,074h,007h,08Bh,01Eh,04Dh,003h,089h,05Eh,0F8h,08Bh + DB 046h,0FCh,0F7h,026h,049h,003h,08Bh,016h,049h,003h,08Bh,0F8h,003h,07Eh,0FEh + DB 08Bh,01Eh,03Fh,003h,08Bh,076h,0F4h,003h,076h,0F6h,0B8h,000h,0A0h,08Eh,0D8h + DB 08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,023h,02Bh,04Eh,0FCh,07Eh + DB 01Eh,02Bh,0D0h,02Bh,0D8h,08Ah,0E0h,026h,08Ah,02Ch,066h,046h,00Ah,0EDh,074h + DB 002h,088h,02Dh,047h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,0FEh,0C9h,075h + DB 0E6h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h + DB 050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h,0B7h,002h + DB 089h,056h,0F2h,0FCh,0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h,027h,003h + DB 02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h,003h,003h + DB 01Eh,03Bh,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0A1h + DB 023h,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh + DB 00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh + DB 013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h + DB 0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h + DB 049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h + DB 08Ah,0E0h,084h,0D0h,00Fh,085h,0C7h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h + DB 074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h + DB 0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,037h,003h + DB 001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h + DB 049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h + DB 0F8h,0B8h,000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h,003h,046h + DB 0FEh,083h,0D2h,000h,08Bh,0F8h,0E8h,0F1h,001h,08Bh,016h,049h,003h,08Bh,01Eh + DB 03Fh,003h,0C5h,036h,023h,003h,08Bh,076h,0F4h,003h,076h,0F6h,08Bh,046h,0FAh + DB 08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,04Fh,02Bh,04Eh,0FCh,07Eh,04Ah,02Bh,0D0h + DB 02Bh,0D8h,083h,07Eh,006h,000h,075h,021h,08Ah,0E0h,08Ah,02Ch,026h,088h,02Dh + DB 046h,047h,075h,003h,0E8h,0D9h,001h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh + DB 073h,003h,0E8h,0CCh,001h,0FEh,0C9h,075h,0E1h,0EBh,01Fh,08Ah,0E0h,026h,08Ah + DB 02Dh,088h,02Ch,046h,047h,075h,003h,0E8h,0B8h,001h,0FEh,0CCh,075h,0F0h,003h + DB 0F3h,003h,0FAh,073h,003h,0E8h,0ABh,001h,0FEh,0C9h,075h,0E1h,08Bh,056h,0F2h + DB 0E8h,06Ch,001h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Eh + DB 000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h + DB 03Eh,001h,089h,056h,0F2h,08Bh,046h,006h,08Bh,05Eh,008h,02Bh,006h,027h,003h + DB 02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,03Fh,003h,003h + DB 01Eh,043h,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0C4h + DB 006h,01Dh,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh + DB 0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh + DB 02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h + DB 0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh + DB 013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh + DB 003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h + DB 005h,08Ah,0E0h,084h,0D0h,00Fh,085h,0A0h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h + DB 008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h + DB 000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,03Fh + DB 003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h + DB 0A1h,049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h + DB 046h,0F8h,08Bh,046h,0FCh,0F7h,026h,049h,003h,003h,046h,0FEh,083h,0D2h,000h + DB 08Bh,0F8h,0E8h,07Eh,000h,08Bh,016h,049h,003h,08Bh,01Eh,03Fh,003h,08Bh,076h + DB 0F4h,003h,076h,0F6h,0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh,04Eh,0F8h + DB 02Bh,046h,0FEh,07Eh,02Ch,02Bh,04Eh,0FCh,07Eh,027h,02Bh,0D0h,02Bh,0D8h,08Ah + DB 0E0h,026h,08Ah,02Ch,046h,00Ah,0EDh,074h,002h,088h,02Dh,047h,075h,003h,0E8h + DB 067h,000h,0FEh,0CCh,075h,0ECh,003h,0F3h,003h,0FAh,073h,003h,0E8h,05Ah,000h + DB 0FEh,0C9h,075h,0DDh,08Bh,056h,0F2h,0E8h,01Bh,000h,007h,01Fh,05Eh,05Fh,05Ah + DB 059h,05Bh,058h,0C9h,0CBh,050h,053h,0B8h,005h,04Fh,0B7h,001h,0B3h,000h,02Eh + DB 0FFh,01Eh,05Dh,003h,05Bh,058h,0C3h,050h,053h,052h,0B8h,005h,04Fh,0B7h,000h + DB 0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h,050h,053h,052h,08Bh + DB 0DAh,0C1h,0E3h,002h,02Eh,089h,01Eh,089h,003h,02Eh,08Bh,097h,061h,003h,0B8h + DB 005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h + DB 050h,053h,052h,02Eh,08Bh,01Eh,089h,003h,083h,0C3h,004h,02Eh,089h,01Eh,089h + DB 003h,02Eh,08Bh,097h,061h,003h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh + DB 01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h diff --git a/WIN32LIB/KEYBOARD/OLD/KEYSTRUC.INC b/WIN32LIB/KEYBOARD/OLD/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WIN32LIB/KEYBOARD/OLD/MAKEFILE b/WIN32LIB/KEYBOARD/OLD/MAKEFILE new file mode 100644 index 0000000..a6a4b1d --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MAKEFILE @@ -0,0 +1,181 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + wwmouse.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BAK b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BAK new file mode 100644 index 0000000..a6a4b1d --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BAK @@ -0,0 +1,181 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + wwmouse.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BOR b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BOR new file mode 100644 index 0000000..0ffea62 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.BOR @@ -0,0 +1,168 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + keyboard.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+keyboard.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/KEYBOARD/OLD/MAKEFILE.WAT b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.WAT new file mode 100644 index 0000000..7732760 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MAKEFILE.WAT @@ -0,0 +1,179 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLD/MOUSE.ASM b/WIN32LIB/KEYBOARD/OLD/MOUSE.ASM new file mode 100644 index 0000000..8530f9b --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MOUSE.ASM @@ -0,0 +1,2225 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "svgaprim.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL RealModePtr:DWORD +GLOBAL MInstalled:DWORD +GLOBAL MouseBuffer:DWORD +GLOBAL LCW_Uncompress:NEAR + +GLOBAL Get_Shape_Uncomp_Size :NEAR +GLOBAL Get_Shape_Width :NEAR +GLOBAL Get_Shape_Original_Height :NEAR +GLOBAL Size_Of_Region :NEAR +GLOBAL _ShapeBuffer :DWORD + +GLOBAL XRes : dword +GLOBAL YRes : dword + + +GLOBAL VesaFunc : dword +GLOBAL Vesa_XRes : near +GLOBAL Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + or cx,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR + + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi + + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi + + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi + + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD + + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END diff --git a/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP b/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP new file mode 100644 index 0000000..548928b --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP @@ -0,0 +1,629 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicBufferClass * screen - pointer to screen mouse is created for * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicBufferClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + MutexObject = CreateMutex(NULL, FALSE, "WWLIB32MOUSEMUTEX"); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); +} + +WWMouseClass::~WWMouseClass() +{ + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + } + timeEndPeriod (1); + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Process_Mouse(void) +{ + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!_Mouse || State > 0 || MouseUpdate || EraseFlags) + return; + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + if (!EraseFlags) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } else { + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + State++; + } + } + } + ReleaseMutex(MutexObject); +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Inform Windows that we are done using the exclusive data we requested. + // + ReleaseMutex(MutexObject); + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +} + +void WWMouseClass::Low_Hide_Mouse() +{ + if (!State) { + while (!Screen->Lock()) {} + if (MouseBuffX != -1 || MouseBuffY != -1) { + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + } + MouseBuffX = -1; + MouseBuffY = -1; + Screen->Unlock(); + } + State++; +} +void WWMouseClass::Hide_Mouse() +{ + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; + ReleaseMutex(MutexObject); +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + while (!Screen->Lock()) {} + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + ReleaseMutex(MutexObject); + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; + ReleaseMutex(MutexObject); +} + + +void WWMouseClass::Draw_Mouse(GraphicBufferClass *scr) +{ + POINT pt; + + if (State != 0) return; + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen til we sucessfully get a lock. + // + while (!scr->Lock()) {} + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + + MouseUpdate--; + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Erase_Mouse(GraphicBufferClass *scr, int forced) +{ +// if (!EraseFlags) return; +// if (State != 0) return; + + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + // + // If we don't own the Mutex Object, then we need to because we may + // update the mouse. + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If this is a forced call then wait til we are allowed to update the buffer + // + while (!scr->Lock()) {} + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } + MouseUpdate--; + ReleaseMutex(MutexObject); + EraseFlags = FALSE; +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +#pragma off(unreferenced) +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + if (_Mouse) { + _Mouse->Process_Mouse(); + } +} +#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +int Get_Mouse_X(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} diff --git a/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP.BAK b/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP.BAK new file mode 100644 index 0000000..e1eb2ab --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MOUSE.CPP.BAK @@ -0,0 +1,629 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicBufferClass * screen - pointer to screen mouse is created for * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicBufferClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + MutexObject = CreateMutex(NULL, FALSE, "WWLIB32MOUSEMUTEX"); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseFlags = FALSE; + + _Mouse = this; + TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); +} + +WWMouseClass::~WWMouseClass() +{ + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + } + timeEndPeriod (1); + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Process_Mouse(void) +{ + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!_Mouse || State > 0 || MouseUpdate || EraseFlags) + return; + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + if (!EraseFlags) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } else { + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + State++; + } + } + } + ReleaseMutex(MutexObject); +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Inform Windows that we are done using the exclusive data we requested. + // + ReleaseMutex(MutexObject); + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +} + +void WWMouseClass::Low_Hide_Mouse() +{ + if (!State) { + while (!Screen->Lock()) {} + if (MouseBuffX != -1 || MouseBuffY != -1) { + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, 0); + } + MouseBuffX = -1; + MouseBuffY = -1; + Screen->Unlock(); + } + State++; +} +void WWMouseClass::Hide_Mouse() +{ + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; + ReleaseMutex(MutexObject); +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + while (!Screen->Lock()) {} + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + ReleaseMutex(MutexObject); + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; + ReleaseMutex(MutexObject); +} + + +void WWMouseClass::Draw_Mouse(GraphicBufferClass *scr) +{ + POINT pt; + + if (State != 0) return; + // + // Wait until we have exclusive access to our data + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE) { + if (pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen til we sucessfully get a lock. + // + while (!scr->Lock()) {} + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + + MouseUpdate--; + ReleaseMutex(MutexObject); +} + +void WWMouseClass::Erase_Mouse(GraphicBufferClass *scr, int forced) +{ + if (!EraseFlags) return; +// if (State != 0) return; + + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + // + // If we don't own the Mutex Object, then we need to because we may + // update the mouse. + // + WaitForSingleObject(MutexObject, INFINITE); + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If this is a forced call then wait til we are allowed to update the buffer + // + while (!scr->Lock()) {} + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } + MouseUpdate--; + ReleaseMutex(MutexObject); + EraseFlags = FALSE; +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +#pragma off(unreferenced) +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + if (_Mouse) { + _Mouse->Process_Mouse(); + } +} +#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +int Get_Mouse_X(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} diff --git a/WIN32LIB/KEYBOARD/OLD/MOUSE.H b/WIN32LIB/KEYBOARD/OLD/MOUSE.H new file mode 100644 index 0000000..5385d0d --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MOUSE.H @@ -0,0 +1,121 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +class WWMouseClass { + public: + WWMouseClass(GraphicBufferClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, void *cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicBufferClass *scr); + void Erase_Mouse(GraphicBufferClass *scr, int forced = FALSE); + + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicBufferClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + HANDLE MutexObject; // Handle the Mutex Object created when mouse is + unsigned TimerHandle; + +}; + +extern "C" { + void __cdecl Mouse_Shadow_Buffer(void *thisptr, GraphicBufferClass *srcdst, void *buffer, int x, int y, int hotx, int hoty, int store); + void __cdecl Draw_Mouse(void *thisptr, GraphicBufferClass *srcdst, int x, int y); + void __cdecl *ASM_Set_Mouse_Cursor(void * thisptr, int hotspotx, int hotspoty, VOID *cursor); +}; + +void Hide_Mouse(void); +void Show_Mouse(void); +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/WIN32LIB/KEYBOARD/OLD/MOUSE.INC b/WIN32LIB/KEYBOARD/OLD/MOUSE.INC new file mode 100644 index 0000000..f936787 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/MOUSE.INC @@ -0,0 +1,67 @@ +; +; 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 . +; + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/WIN32LIB/KEYBOARD/OLD/PAGFAULT.ASM b/WIN32LIB/KEYBOARD/OLD/PAGFAULT.ASM new file mode 100644 index 0000000..546dd27 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/PAGFAULT.ASM @@ -0,0 +1,153 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL Install_Page_Fault_Handle : NEAR +GLOBAL Set_Video_Mode : NEAR +GLOBAL Remove_Mouse : NEAR +GLOBAL Remove_Keyboard_Interrupt : NEAR +GLOBAL Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM b/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM new file mode 100644 index 0000000..ae0921f --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM @@ -0,0 +1,659 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : December 12, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "drawbuff.inc" +INCLUDE "gbuffer.inc" +INCLUDE "shape.inc" +INCLUDE ".\mouse.inc" + +GLOBAL C LCW_Uncompress:NEAR +GLOBAL C Get_Shape_Uncomp_Size :NEAR +GLOBAL C Get_Shape_Width :NEAR +GLOBAL C Get_Shape_Original_Height :NEAR +GLOBAL _ShapeBuffer :DWORD + +CODESEG + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* int store - whether to store buffer or save * +;* * +;* OUTPUT: none * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Mouse_Shadow_Buffer(GraphicBufferClass *src/dest, * +;* void *buffer +;* int x_pixel, * +;* int y_pixel, * +;* int store); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Mouse_Shadow_Buffer:NEAR + PROC Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this: DWORD + ARG src_dst_obj: DWORD + ARG buffer:DWORD + ARG x: DWORD + ARG y: DWORD + ARG hotx: DWORD + ARG hoty: DWORD + ARG store: DWORD + + local x0: dword + local y0: dword + local x1: dword + local y1: dword + local buffx0: dword + local buffy0: dword + + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov esi, [this] ; get offset to mouse data + mov edi, [src_dst_obj] ; get offset to mouse data + + mov eax, [x] + mov ebx, [y] + sub eax, [hotx] + sub ebx, [hoty] + + mov [x0], eax + mov [y0], ebx + add eax, [(MouseType esi).CursorWidth] + add ebx, [(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [buffer] + mov [buffy0], eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1], eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1], eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + push ebp + cmp [store], 1 ; are we storing page? + je ??store_entry ; if so go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp, ecx +??restore_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi, edi ; xchg the source and the dest + mov ebp, ecx +??store_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, edx ; move past right clipped pixels + add edi, ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Draw_Mouse( MouseClass * mouse_data, * +;* GraphicBufferClass *destination, * +;* int x_pixel, * +;* int y_pixel); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Draw_Mouse:NEAR + PROC Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD + ARG dest:DWORD + ARG mousex:DWORD + ARG mousey:DWORD + + local x0:dword + local y0:dword + local x1:dword + local y1:dword + local buffx0:dword + local buffy0:dword + + + mov esi, [this] ; get 32 bit offset to mouse data + mov edi, [dest] ; get 32 bit offset to dest buffer + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax, [mousex] + mov ebx, [mousey] + sub eax, [(MouseType esi).MouseXHot] + sub ebx, [(MouseType esi).MouseYHot] + + mov [x0], eax + mov [y0], ebx + add eax,[(MouseType esi).CursorWidth] + add ebx,[(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [(MouseType esi).MouseCursor] + mov [buffy0], eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1] , eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah, al +??inner_loop: + mov ch, [esi] + inc esi + or ch, ch + jz ??inc_edi + mov [edi], ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP Draw_Mouse +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL C ASM_Set_Mouse_Cursor:NEAR + PROC ASM_Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD ; pointer to mouse cursor struct + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[this] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[(MouseType esi).MaxWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[(MouseType esi).MaxHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[this] + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[ShapeBuffer] + jmp ??copy_type + +??done_copy: + mov esi,[this] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(MouseType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(MouseType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(MouseType esi).CursorHeight],ebx + mov ebx,[swidth] + mov [(MouseType esi).CursorWidth],ebx + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + push [cursor] + push [(MouseType esi).PrevCursor] + pop eax + pop [(MouseType esi).PrevCursor] + ret ; and return back to the world + + ENDP ASM_Set_Mouse_Cursor + +END diff --git a/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM.BAK b/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM.BAK new file mode 100644 index 0000000..e6d86ff --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLD/WWMOUSE.ASM.BAK @@ -0,0 +1,655 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : December 12, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "drawbuff.inc" +INCLUDE "gbuffer.inc" +INCLUDE "shape.inc" +INCLUDE ".\mouse.inc" + +GLOBAL C LCW_Uncompress:NEAR +GLOBAL C Get_Shape_Uncomp_Size :NEAR +GLOBAL C Get_Shape_Width :NEAR +GLOBAL C Get_Shape_Original_Height :NEAR +GLOBAL _ShapeBuffer :DWORD + +CODESEG + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* int store - whether to store buffer or save * +;* * +;* OUTPUT: none * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Mouse_Shadow_Buffer(GraphicBufferClass *src/dest, * +;* int x_pixel, * +;* int y_pixel, * +;* int store); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Mouse_Shadow_Buffer:NEAR + PROC Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this: DWORD + ARG src_dst_obj: DWORD + ARG x: DWORD + ARG y: DWORD + ARG store: DWORD + + local x0: dword + local y0: dword + local x1: dword + local y1: dword + local buffx0: dword + local buffy0: dword + + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov esi, [this] ; get offset to mouse data + mov edi, [src_dst_obj] ; get offset to mouse data + + mov eax, [x] + mov ebx, [y] + sub eax, [(MouseType esi).MouseXHot] + sub ebx, [(MouseType esi).MouseYHot] + + mov [x0], eax + mov [y0], ebx + add eax, [(MouseType esi).CursorWidth] + add ebx, [(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [(MouseType esi).MouseBuffer] + mov [buffy0], eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1], eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1], eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + push ebp + cmp [store], 1 ; are we storing page? + je ??store_entry ; if so go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp, ecx +??restore_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi, edi ; xchg the source and the dest + mov ebp, ecx +??store_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, edx ; move past right clipped pixels + add edi, ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Draw_Mouse( MouseClass * mouse_data, * +;* GraphicBufferClass *destination, * +;* int x_pixel, * +;* int y_pixel); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Draw_Mouse:NEAR + PROC Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD + ARG dest:DWORD + ARG mousex:DWORD + ARG mousey:DWORD + + local x0:dword + local y0:dword + local x1:dword + local y1:dword + local buffx0:dword + local buffy0:dword + + + mov esi, [this] ; get 32 bit offset to mouse data + mov edi, [dest] ; get 32 bit offset to dest buffer + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax, [mousex] + mov ebx, [mousey] + sub eax, [(MouseType esi).MouseXHot] + sub ebx, [(MouseType esi).MouseYHot] + + mov [x0], eax + mov [y0], ebx + add eax,[(MouseType esi).CursorWidth] + add ebx,[(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [(MouseType esi).MouseCursor] + mov [buffy0], eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1] , eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah, al +??inner_loop: + mov ch, [esi] + inc esi + or ch, ch + jz ??inc_edi + mov [edi], ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP Draw_Mouse +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL C ASM_Set_Mouse_Cursor:NEAR + PROC ASM_Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD ; pointer to mouse cursor struct + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[this] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[(MouseType esi).MaxWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[(MouseType esi).MaxHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[this] + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[ShapeBuffer] + jmp ??copy_type + +??done_copy: + mov esi,[this] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(MouseType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(MouseType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(MouseType esi).CursorHeight],ebx + mov ebx,[swidth] + mov [(MouseType esi).CursorWidth],ebx + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + push [cursor] + push [(MouseType esi).PrevCursor] + pop eax + pop [(MouseType esi).PrevCursor] + ret ; and return back to the world + + ENDP ASM_Set_Mouse_Cursor + +END diff --git a/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.CPP b/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.CPP new file mode 100644 index 0000000..4090613 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.CPP @@ -0,0 +1,401 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 17, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 2048); + memset(AsciiRemap, 0, 256); + for (short lp = 1; lp < 255; lp ++) { + int vk_key = VkKeyScan(lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = lp; + VKRemap[vk_key] = vk_key & 0xFF; + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[Head+1]; // get the x and y pos + MouseQY = Buffer[Head+2]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. + // + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + +#pragma argsused +void WWKeyboardClass::Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + Put_Key_Message(wParam); + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + } +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} \ No newline at end of file diff --git a/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.H b/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.H new file mode 100644 index 0000000..831dae7 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/KEYBOARD.H @@ -0,0 +1,577 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_VK_BIT = 0x800, + WWKEY_RLS_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x4000, +} WWKey_Type; + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KA_Type; + + + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); +// ~WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_Num(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + /*===================================================================*/ + /* Routines to sneak through and get the mouses position. */ + /*===================================================================*/ + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned char VKRemap[256]; // gives vk for any ascii char + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer +}; diff --git a/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE new file mode 100644 index 0000000..27cb5f3 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = test +PROJ_DIR = $(WIN32LIB)\keyboard\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = \ + $(PROJ_NAME).obj + +!if 0 +PROJ_LIBS = \ + drawbuff.lib \ + win32lib.lib +!else +PROJ_LIBS = \ + drawbuff.lib \ + mem.lib \ + misc.lib \ + iff.lib \ + rawfile.lib \ + tile.lib \ + font.lib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +.lbm.cps: + $(WIN32LIB)\\TOOLS\WWCOMP $*.lbm $*.cps + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_LIBS) + $(LINK_CMD) @&&| +$(LINK_CFG) + +$(COMPILER)\\LIB\\c0w32.obj+ +$(OBJECTS) +$<,$* +$(PROJ_LIBS) + +import32.lib + +cw32i.lib +$*.def +| + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + make + cd $(PROJ_DIR) + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.BOR b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.BOR new file mode 100644 index 0000000..7136c39 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.BOR @@ -0,0 +1,185 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = test +PROJ_DIR = $(WIN32LIB)\keyboard\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = \ + $(PROJ_NAME).obj \ + keyboard.obj + +!if 0 +PROJ_LIBS = \ + drawbuff.lib \ + win32lib.lib +!else +PROJ_LIBS = \ + drawbuff.lib \ + mem.lib \ + misc.lib \ + iff.lib \ + rawfile.lib \ + tile.lib \ + font.lib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +.lbm.cps: + $(WIN32LIB)\\TOOLS\WWCOMP $*.lbm $*.cps + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_LIBS) + $(LINK_CMD) @&&| +$(LINK_CFG) + +$(COMPILER)\\LIB\\c0w32.obj+ +$(OBJECTS) +$<,$* +$(PROJ_LIBS) + +import32.lib + +cw32i.lib +$*.def +| + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + make + cd $(PROJ_DIR) + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.WAT b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.WAT new file mode 100644 index 0000000..358a8b3 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/MAKEFILE.WAT @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = test +PROJ_DIR = $(WIN32LIB)\keyboard\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = \ + $(PROJ_NAME).obj + +!if 0 +PROJ_LIBS = \ + drawbuff.lib \ + win32lib.lib +!else +PROJ_LIBS = \ + drawbuff.lib \ + mem.lib \ + misc.lib \ + iff.lib \ + rawfile.lib \ + tile.lib \ + font.lib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +.lbm.cps: + $(WIN32LIB)\\TOOLS\WWCOMP $*.lbm $*.cps + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_LIBS) + $(LINK_CMD) @&&| +$(LINK_CFG) + +$(COMPILER)\\LIB\\c0w32.obj+ +$(OBJECTS) +$<,$* +$(PROJ_LIBS) + +import32.lib + +cw32i.lib +$*.def +| + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + make + cd $(PROJ_DIR) + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/OLDTEST/TEST.CPP b/WIN32LIB/KEYBOARD/OLDTEST/TEST.CPP new file mode 100644 index 0000000..94f6a16 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/TEST.CPP @@ -0,0 +1,1037 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Keyboard Test Program * + * * + * File Name : TEST.CPP * + * * + * Programmer : Phil Gorrow * + * * + * Start Date : October 10, 1995 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WndProc -- Callback procedure for main window * + * WinMain -- Program entry point * + * Init_Keyboard_Remap_Table -- initializes the keyboard remap table * + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "keyboard.h" + +#define NAME "DRAWBUFF test" +#define TITLE "DRAWBUFF library test" +BOOL Running = TRUE; +HANDLE ThreadHandle; +BOOL MessageLoopThread = FALSE; +#define MODE_WIDTH 640 // Width in pixels of required video mode +#define MODE_HEIGHT 400 // Height in pixels of required video mode +void Message_Loop(void); +HANDLE ActiveEvent; +WWKeyboardClass Kbd; +#if(0) +#define VK_NONE_00 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_0 0x3A +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF +#endif + +char *VK_Name[] = { + "VK_NONE_00", + "VK_LBUTTON", + "VK_RBUTTON", + "VK_CANCEL", + "VK_MBUTTON", + "VK_NONE_05", + "VK_NONE_06", + "VK_NONE_07", + "VK_BACK", + "VK_TAB", + "VK_NONE_0A", + "VK_NONE_0B", + "VK_CLEAR", + "VK_RETURN", + "VK_NONE_0E", + "VK_NONE_0F", + "VK_SHIFT", + "VK_CONTROL", + "VK_MENU", + "VK_PAUSE", + "VK_CAPITAL", + "VK_NONE_15", + "VK_NONE_16", + "VK_NONE_17", + "VK_NONE_18", + "VK_NONE_19", + "VK_NONE_1A", + "VK_ESCAPE", + "VK_NONE_1C", + "VK_NONE_1D", + "VK_NONE_1E", + "VK_NONE_1F", + "VK_SPACE", + "VK_PRIOR", + "VK_NEXT", + "VK_END", + "VK_HOME", + "VK_LEFT", + "VK_UP", + "VK_RIGHT", + "VK_DOWN", + "VK_SELECT", + "VK_PRINT", + "VK_EXECUTE", + "VK_SNAPSHOT", + "VK_INSERT", + "VK_DELETE", + "VK_HELP", + "VK_0", + "VK_1", + "VK_2", + "VK_3", + "VK_4", + "VK_5", + "VK_6", + "VK_7", + "VK_8", + "VK_9", + "VK_0", + "VK_NONE_3B", + "VK_NONE_3C", + "VK_NONE_3D", + "VK_NONE_3E", + "VK_NONE_3F", + "VK_NONE_40", + "VK_A", + "VK_B", + "VK_C", + "VK_D", + "VK_E", + "VK_F", + "VK_G", + "VK_H", + "VK_I", + "VK_J", + "VK_K", + "VK_L", + "VK_M", + "VK_N", + "VK_O", + "VK_P", + "VK_Q", + "VK_R", + "VK_S", + "VK_T", + "VK_U", + "VK_V", + "VK_W", + "VK_X", + "VK_Y", + "VK_Z", + "VK_NONE_5B", + "VK_NONE_5C", + "VK_NONE_5D", + "VK_NONE_5E", + "VK_NONE_5F", + "VK_NUMPAD0", + "VK_NUMPAD1", + "VK_NUMPAD2", + "VK_NUMPAD3", + "VK_NUMPAD4", + "VK_NUMPAD5", + "VK_NUMPAD6", + "VK_NUMPAD7", + "VK_NUMPAD8", + "VK_NUMPAD9", + "VK_MULTIPLY", + "VK_ADD", + "VK_SEPARATOR", + "VK_SUBTRACT", + "VK_DECIMAL", + "VK_DIVIDE", + "VK_F1", + "VK_F2", + "VK_F3", + "VK_F4", + "VK_F5", + "VK_F6", + "VK_F7", + "VK_F8", + "VK_F9", + "VK_F10", + "VK_F11", + "VK_F12", + "VK_F13", + "VK_F14", + "VK_F15", + "VK_F16", + "VK_F17", + "VK_F18", + "VK_F19", + "VK_F20", + "VK_F21", + "VK_F22", + "VK_F23", + "VK_F24", + "VK_NONE_88", + "VK_NONE_89", + "VK_NONE_8A", + "VK_NONE_8B", + "VK_NONE_8C", + "VK_NONE_8D", + "VK_NONE_8E", + "VK_NONE_8F", + "VK_NUMLOCK", + "VK_SCROLL", + "VK_NONE_92", + "VK_NONE_93", + "VK_NONE_94", + "VK_NONE_95", + "VK_NONE_96", + "VK_NONE_97", + "VK_NONE_98", + "VK_NONE_99", + "VK_NONE_9A", + "VK_NONE_9B", + "VK_NONE_9C", + "VK_NONE_9D", + "VK_NONE_9E", + "VK_NONE_9F", + "VK_NONE_A0", + "VK_NONE_A1", + "VK_NONE_A2", + "VK_NONE_A3", + "VK_NONE_A4", + "VK_NONE_A5", + "VK_NONE_A6", + "VK_NONE_A7", + "VK_NONE_A8", + "VK_NONE_A9", + "VK_NONE_AA", + "VK_NONE_AB", + "VK_NONE_AC", + "VK_NONE_AD", + "VK_NONE_AE", + "VK_NONE_AF", + "VK_NONE_B0", + "VK_NONE_B1", + "VK_NONE_B2", + "VK_NONE_B3", + "VK_NONE_B4", + "VK_NONE_B5", + "VK_NONE_B6", + "VK_NONE_B7", + "VK_NONE_B8", + "VK_NONE_B9", + "VK_NONE_BA", + "VK_NONE_BB", + "VK_NONE_BC", + "VK_NONE_BD", + "VK_NONE_BE", + "VK_NONE_BF", + "VK_NONE_C0", + "VK_NONE_C1", + "VK_NONE_C2", + "VK_NONE_C3", + "VK_NONE_C4", + "VK_NONE_C5", + "VK_NONE_C6", + "VK_NONE_C7", + "VK_NONE_C8", + "VK_NONE_C9", + "VK_NONE_CA", + "VK_NONE_CB", + "VK_NONE_CC", + "VK_NONE_CD", + "VK_NONE_CE", + "VK_NONE_CF", + "VK_NONE_D0", + "VK_NONE_D1", + "VK_NONE_D2", + "VK_NONE_D3", + "VK_NONE_D4", + "VK_NONE_D5", + "VK_NONE_D6", + "VK_NONE_D7", + "VK_NONE_D8", + "VK_NONE_D9", + "VK_NONE_DA", + "VK_NONE_DB", + "VK_NONE_DC", + "VK_NONE_DD", + "VK_NONE_DE", + "VK_NONE_DF", + "VK_NONE_E0", + "VK_NONE_E1", + "VK_NONE_E2", + "VK_NONE_E3", + "VK_NONE_E4", + "VK_NONE_E5", + "VK_NONE_E6", + "VK_NONE_E7", + "VK_NONE_E8", + "VK_NONE_E9", + "VK_NONE_EA", + "VK_NONE_EB", + "VK_NONE_EC", + "VK_NONE_ED", + "VK_NONE_EE", + "VK_NONE_EF", + "VK_NONE_F0", + "VK_NONE_F1", + "VK_NONE_F2", + "VK_NONE_F3", + "VK_NONE_F4", + "VK_NONE_F5", + "VK_NONE_F6", + "VK_NONE_F7", + "VK_NONE_F8", + "VK_NONE_F9", + "VK_NONE_FA", + "VK_NONE_FB", + "VK_NONE_FC", + "VK_NONE_FD", + "VK_NONE_FE", + "VK_NONE_FF", +}; + +char *_keyname[] = { + "KN_NONE", + "KN_GRAVE", + "KN_1", + "KN_2", + "KN_3", + "KN_4", + "KN_5", + "KN_6", + "KN_7", + "KN_8", + "KN_9", + "KN_0", + "KN_MINUS", + "KN_EQUAL", + "KN_RESERVED1", + "KN_BACKSPACE", + "KN_TAB", + "KN_Q", + "KN_W", + "KN_E", + "KN_R", + "KN_T", + "KN_Y", + "KN_U", + "KN_I", + "KN_O", + "KN_P", + "KN_LBRACKET", + "KN_RBRACKET", + "KN_BACKSLASH", + "KN_CAPSLOCK", + "KN_A", + "KN_S", + "KN_D", + "KN_F", + "KN_G", + "KN_H", + "KN_J", + "KN_K", + "KN_L", + "KN_SEMICOLON", + "KN_SQUOTE", + "KN_BACKSLASH2", + "KN_RETURN", + "KN_LSHIFT", + "KN_MOUSE_MOVE", + "KN_Z", + "KN_X", + "KN_C", + "KN_V", + "KN_B", + "KN_N", + "KN_M", + "KN_COMMA", + "KN_PERIOD", + "KN_SLASH", + "KN_RESERVED3", + "KN_RSHIFT", + "KN_LCTRL", + "KN_LCOMM", + "KN_LALT", + "KN_SPACE", + "KN_RALT", + "KN_RCOMM", + "KN_RCTRL", + "KN_LMOUSE", + "KN_RMOUSE", + "KN_JBUTTON1", + "KN_JBUTTON2", + "KN_J_UP", + "KN_J_RIGHT", + "KN_J_DOWN", + "KN_J_LEFT", + "KN_SPECIAL9", + "KN_SPECIAL10", + "KN_E_INSERT", + "KN_E_DELETE", + "KN_RESERVED4", + "KN_RESERVED5", + "KN_E_LEFT", + "KN_E_HOME", + "KN_E_END", + "KN_RESERVED6", + "KN_E_UP", + "KN_E_DOWN", + "KN_E_PGUP", + "KN_E_PGDN", + "KN_K_LPAREN", + "KN_K_RPAREN", + "KN_E_RIGHT", + "KN_NUMLOCK", + "KN_HOME", + "KN_UPLEFT/KN_HOME", + "KN_LEFT", + "KN_END", + "KN_DOWNLEFT/KN_END", + "KN_RESERVED7", + "KN_KEYPAD_SLASH", + "KN_UP", + "KN_CENTER", + "KN_DOWN", + "KN_INSERT", + "KN_KEYPAD_ASTERISK", + "KN_PGUP", + "KN_UPRIGHT/KN_PGUP", + "KN_RIGHT", + "KN_PGDN", + "KN_DOWNRIGHT/KN_PGDN", + "KN_DELETE", + "KN_KEYPAD_MINUS", + "KN_KEYPAD_PLUS", + "KN_RESERVED8", + "KN_KEYPAD_RETURN", + "KN_RESERVED9", + "KN_ESC", + "KN_HELP", + "KN_F1", + "KN_F2", + "KN_F3", + "KN_F4", + "KN_F5", + "KN_F6", + "KN_F7", + "KN_F8", + "KN_F9", + "KN_F10", + "KN_F11", + "KN_F12", + "KN_PRNTSCRN", + "KN_SCROLLLOCK", + "KN_PAUSE" +}; +char *_steve_translate[]={ + "?", + "ESC-27", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "0", + "-", + "=", + "BACKSPACE", + "TAB", + "q", + "w", + "e", + "r", + "t", + "y", + "u", + "i", + "o", + "p", + "[", + "]", + "return", + "?", + "a", + "s", + "d", + "f", + "g", + "h", + "j", + "k", + "l", + ";", + "'", + "ESC-27", + "?", + "\\", + "z", + "x", + "c", + "v", + "b", + "n", + "m", + ",", + ".", + "/", + "?", + "*", + "?", + " ", + "?", + "f1-127", + "127", + "?", + "?", + "?", + "?", + "?", + "?", + "?", + "?", + "?", + "?", + "7", + "8", + "9", + "-", + "4", + "5", + "6", + "+", + "1", + "2", + "3", + "?", + "?", + "?", + "?", + "?", + "?", + "F12", +}; +static short cxChar, cxCaps, cyChar; + +/*********************************************************************************************** + * WndProc -- windows message callback * + * * + * Pilfered from a windows example program - HELLOWIN.C * + * * + * * + * INPUT: Standard Windoze callback parameters * + * * + * OUTPUT: long * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:39PM ST : Pilfered * + *=============================================================================================*/ + +long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + HDC hdc; + PAINTSTRUCT ps; + static int line = 1; + char buffer[100]; + TEXTMETRIC tm; + RECT rect; + int transition; + int previous; + int context; + int extended; + int oem; + int rep; + int shift,ctrl,alt,caps,nums; + int vk_key; + BYTE remaptbl[256]; + WORD key; + char srcstr[2]; + char dststr[2]; + + switch (message) { + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + Kbd.Message_Handler(hwnd, message, wParam, lParam); + break; + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + Kbd.Message_Handler(hwnd, message, wParam, lParam); +// Message_Loop(); + return(0); + + case WM_CREATE: + hdc = GetDC(hwnd); + GetTextMetrics(hdc, &tm); + cxChar = tm.tmAveCharWidth; + cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2; + cyChar = tm.tmHeight + tm.tmExternalLeading; + ReleaseDC(hwnd, hdc); + return(0); + + + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + GetTextMetrics(hdc, &tm); + EndPaint(hwnd, &ps); + return(0); + +// case WM_CLOSE: + case WM_DESTROY: + Running = FALSE; + WaitForSingleObject(ThreadHandle, INFINITE); + PostQuitMessage (0); + Reset_Video_Mode(); + return(0); + case WM_ACTIVATEAPP: + if ((BOOL)wParam) { + SetEvent(ActiveEvent); + } else { + ResetEvent(ActiveEvent); + } + break; + } + + if (line == 23) { + InvalidateRect(hwnd, NULL, TRUE); + line = 1; + } + + return DefWindowProc (hwnd, message, wParam, lParam) ; +} + +void Message_Loop(void) +{ + int key,shift,ctrl,alt,rls,dbl; + char buffer[255]; + HDC hdc; + static int line=0; + + while (Running) { + WaitForSingleObject(ActiveEvent, INFINITE); + if (Kbd.Check()) { + int key = Kbd.Get(); + if (key & WWKEY_VK_BIT) { + Kbd.Split(key, shift, ctrl, alt,rls,dbl); + if (!Kbd.Is_Mouse_Key(key)) { + sprintf(buffer, "Key (VK) = %d (%s) shift = %d control = %d menu = %d rls = %d dbl = %d", key, VK_Name[key], shift, ctrl, alt, rls, dbl); + if (key == 27) { + PostMessage (MainWindow, WM_DESTROY,0,0); + } + } else { + sprintf(buffer, "Mouse = %d @ (%d,%d) shift = %d control = %d menu = %d", key, Kbd.MouseQX, Kbd.MouseQY, shift, ctrl, alt); + } + } else { + Kbd.Split(key, shift, ctrl, alt, rls, dbl); + sprintf(buffer, "Key (ACSII) = %d (%c) shift = %d control = %d menu = %d rls = %d dbl = %d", key, key, shift, ctrl, alt, rls, dbl); + } + hdc = GetDC(MainWindow); + TextOut(hdc, cxChar, cyChar * (line + 1), buffer, lstrlen(buffer)); + sprintf(buffer, "Mouses current status: x = %3d, y = %3d", Kbd.Get_Mouse_X(), Kbd.Get_Mouse_Y()); + TextOut(hdc, cxChar, cyChar * (1), buffer, lstrlen(buffer)); + line++; + if (line == 23) { + InvalidateRect(MainWindow, NULL, TRUE); + line = 1; + } + ReleaseDC(MainWindow, hdc); + } + } + +} + +/*********************************************************************************************** + * WinMain -- Program entry point * + * * + * * + * * + * INPUT: Standard Windows startup parameters * + * * + * OUTPUT: msg.wParam * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:28PM ST : Created * + *=============================================================================================*/ + +int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, + LPSTR /*lpszCmdParam*/, int nCmdShow) +{ + HWND hwnd ; + WNDCLASS wndclass ; + MSG msg ; + int i,j,k; + int key,shift,ctrl,alt,rls,dbl; + char buffer[255]; + HDC hdc; + + // + // Register the window class + // + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = WndProc ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = hInstance ; + wndclass.hIcon = LoadIcon (hInstance, IDI_APPLICATION) ; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = NAME; + + RegisterClass (&wndclass) ; + } + + + // + // Create our main window + // + hwnd = MainWindow = CreateWindowEx ( + WS_EX_TOPMOST, + NAME, + TITLE, + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + MODE_WIDTH, + MODE_HEIGHT, + NULL, + NULL, + hInstance, + NULL ); + + ShowWindow (hwnd, nCmdShow) ; + UpdateWindow (hwnd) ; + SetFocus (hwnd); + + Set_Video_Mode( MainWindow , MODE_WIDTH , MODE_HEIGHT , 8 ); +// ShowWindow (hwnd, nCmdShow) ; +// UpdateWindow (hwnd) ; +// SetFocus (hwnd); + // (Dangerous if Windoze can change the handle) + +// Set_Video_Mode( MainWindow , MODE_WIDTH , MODE_HEIGHT , 8 ); +#if(TRUE) + DWORD threadid; + ActiveEvent = CreateEvent(NULL, TRUE, TRUE, "Active Event"); + ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Message_Loop, NULL, 0, &threadid); + if (!ThreadHandle) { + DWORD error = GetLastError(); + sprintf(buffer, "Last Error was equal to %d", error); + MessageBox(hwnd, buffer,"Error",MB_ICONEXCLAMATION|MB_OK); + return(0); + } +#endif + + while (1) { + if (!GetMessage(&msg, NULL, 0, 0)) { + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return(0); +// return msg.wParam; +} + + + diff --git a/WIN32LIB/KEYBOARD/OLDTEST/TEST.DEF b/WIN32LIB/KEYBOARD/OLDTEST/TEST.DEF new file mode 100644 index 0000000..1412668 --- /dev/null +++ b/WIN32LIB/KEYBOARD/OLDTEST/TEST.DEF @@ -0,0 +1,14 @@ +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + + + + + + + + + diff --git a/WIN32LIB/KEYBOARD/TEST/HOLD.DEF b/WIN32LIB/KEYBOARD/TEST/HOLD.DEF new file mode 100644 index 0000000..1412668 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/HOLD.DEF @@ -0,0 +1,14 @@ +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + + + + + + + + + diff --git a/WIN32LIB/KEYBOARD/TEST/KEYBOARD.CPP b/WIN32LIB/KEYBOARD/TEST/KEYBOARD.CPP new file mode 100644 index 0000000..4090613 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/KEYBOARD.CPP @@ -0,0 +1,401 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 17, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 2048); + memset(AsciiRemap, 0, 256); + for (short lp = 1; lp < 255; lp ++) { + int vk_key = VkKeyScan(lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = lp; + VKRemap[vk_key] = vk_key & 0xFF; + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[Head+1]; // get the x and y pos + MouseQY = Buffer[Head+2]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. + // + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + +#pragma argsused +void WWKeyboardClass::Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + Put_Key_Message(wParam); + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + } +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} \ No newline at end of file diff --git a/WIN32LIB/KEYBOARD/TEST/KEYBOARD.H b/WIN32LIB/KEYBOARD/TEST/KEYBOARD.H new file mode 100644 index 0000000..831dae7 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/KEYBOARD.H @@ -0,0 +1,577 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_VK_BIT = 0x800, + WWKEY_RLS_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x4000, +} WWKey_Type; + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KA_Type; + + + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); +// ~WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_Num(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + /*===================================================================*/ + /* Routines to sneak through and get the mouses position. */ + /*===================================================================*/ + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + int MouseQX; + int MouseQY; + + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned char VKRemap[256]; // gives vk for any ascii char + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer +}; diff --git a/WIN32LIB/KEYBOARD/TEST/MAKEFILE b/WIN32LIB/KEYBOARD/TEST/MAKEFILE new file mode 100644 index 0000000..4d61b48 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/MAKEFILE @@ -0,0 +1,174 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WIN32LIB)\keyboard\TEST +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj & + testasm.obj + +CPS_FILES = & + test1.cps & + test2.cps + + +PROJ_LIBS = & + keyboard.lib & + win32lib.lib + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB386\NT +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) $(CPS_FILES) + $(LINK_CMD) $(LINK_CFG) name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + %append $^@ library $(LIB_DIR)\ddraw.lib + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/TEST/MAKEFILE.BOR b/WIN32LIB/KEYBOARD/TEST/MAKEFILE.BOR new file mode 100644 index 0000000..75ddf1b --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/MAKEFILE.BOR @@ -0,0 +1,187 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = test +PROJ_DIR = $(WIN32LIB)\drawbuff\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = \ + $(PROJ_NAME).obj + +CPS_FILES = \ + test1.cps \ + test2.cps +!if 0 +PROJ_LIBS = \ + drawbuff.lib \ + win32lib.lib +!else +PROJ_LIBS = \ + drawbuff.lib \ + mem.lib \ + misc.lib \ + iff.lib \ + rawfile.lib \ + tile.lib \ + font.lib +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +.lbm.cps: + $(WIN32LIB)\\TOOLS\WWCOMP $*.lbm $*.cps + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe $(CPS_FILES) + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_LIBS) + $(LINK_CMD) @&&| +$(LINK_CFG) + +$(COMPILER)\\LIB\\c0w32.obj+ +$(OBJECTS) +$<,$* +$(PROJ_LIBS) + +import32.lib + +cw32i.lib +$*.def +| + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + make + cd $(PROJ_DIR) + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/TEST/MAKEFILE.WAT b/WIN32LIB/KEYBOARD/TEST/MAKEFILE.WAT new file mode 100644 index 0000000..c596e07 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/MAKEFILE.WAT @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WIN32LIB)\drawbuff\TEST +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj + +CPS_FILES = & + test1.cps & + test2.cps + +PROJ_LIBS = & + drawbuff.lib & + mem.lib & + misc.lib & + iff.lib & + rawfile.lib & + tile.lib & + font.lib +#PROJ_LIBS = & +# drawbuff.lib & +# win32lib.lib + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB386\NT +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) $(CPS_FILES) + $(LINK_CMD) $(LINK_CFG) name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + %append $^@ library $(LIB_DIR)\ddraw.lib + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/KEYBOARD/TEST/TEST.CPP b/WIN32LIB/KEYBOARD/TEST/TEST.CPP new file mode 100644 index 0000000..1d9ae12 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/TEST.CPP @@ -0,0 +1,342 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : GraphicBufferClass Test Program * + * * + * File Name : DRAWTEST.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 25, 1995 * + * * + * Last Update : September 27, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WinMain -- Program entry point * + * WndProc -- Callback procedure for main window * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "..\mouse.h" +#define NAME "KEYBOARD/MOUSE test" +#define TITLE "KEYBOARD/MOUSE library test" + + +// +// Misc globals for testing +// +BOOL AllDone = FALSE; // Flag that we should exit + +#define MODE_WIDTH 640 // Width in pixels of required video mode +#define MODE_HEIGHT 400 // Height in pixels of required video mode + +int ScreenWidth=MODE_WIDTH; + + + +GraphicBufferClass *ScreenBuffer=NULL; // Global pointer to screen GraphicBufferClass +GraphicBufferClass *HidBuffer = NULL; +GraphicBufferClass SysMemPage(320,200); // page in real memory +WWMouseClass *Mouse=NULL; // Global pointer to mouse information +WinTimerClass *WindowsTimer=NULL; +PALETTEENTRY pe[256]; // DD Palette entries +unsigned char Palette[256*3]; // Place to load palette to +extern LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object + +// +// Prototypes +// +long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ; + +// +// Externs +// +extern LPDIRECTDRAW DirectDrawObject; +extern HWND MainWindow; +VOID *ShapeFile; +int CurrentShape; + + + +/*********************************************************************************************** + * WinMain -- Program entry point * + * * + * * + * * + * INPUT: Standard Windows startup parameters * + * * + * OUTPUT: msg.wParam * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:28PM ST : Created * + *=============================================================================================*/ +#pragma off(unreferenced) +int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, + LPSTR lpszCmdParam, int nCmdShow) +{ + static char szAppName[] = "HelloWin" ; + HWND hwnd ; + MSG msg ; + WNDCLASS wndclass ; + char path_to_exe[132]; + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + unsigned olddrive; + char oldpath[PATH_MAX]; + + /* + ** Get a path to the executable and make sure that we are pointing + ** at the location our datafiles are located at. + */ + GetModuleFileName (hInstance, &path_to_exe[0], 132); + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + + _splitpath(path_to_exe, drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = (char)(('A' + olddrive)-1); + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); + + + // + // Register the window class + // + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = WndProc ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = hInstance ; + wndclass.hIcon = LoadIcon (hInstance, IDI_APPLICATION) ; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = NAME; + + RegisterClass (&wndclass) ; + } + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + NAME, + TITLE, + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + MODE_WIDTH, + MODE_HEIGHT, + NULL, + NULL, + hInstance, + NULL ); + + ShowWindow (hwnd, nCmdShow) ; + UpdateWindow (hwnd) ; + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + // (Dangerous if Windoze can change the handle) + + + + + + // Set the video mode + Set_Video_Mode( MainWindow , MODE_WIDTH , MODE_HEIGHT , 8 ); + Set_Shape_Buffer(new unsigned char[5000], 5000); + // + // Create the GraphicBufferClass that will be the screen buffer + // + ScreenBuffer = new GraphicBufferClass ( MODE_WIDTH , MODE_HEIGHT , (GBC_Enum)(GBC_VIDEOMEM | GBC_VISIBLE)); + HidBuffer = new GraphicBufferClass ( MODE_WIDTH , MODE_HEIGHT , (GBC_Enum)(GBC_VIDEOMEM)); + ShowCursor (FALSE); + Mouse = new WWMouseClass(ScreenBuffer,32,32); + + // + // Load up the picture and display it on the scene page + // + + Load_Picture("TITLE.CPS", SysMemPage, SysMemPage, Palette, BM_DEFAULT); + FontPtr = Load_Font("font.fnt"); + + ShapeFile = Load_Alloc_Data("MOUSE.SHP", MEM_NORMAL); + CurrentShape = 0; + Mouse->Set_Cursor(0,0,Extract_Shape(ShapeFile,CurrentShape)); + Set_Palette(Palette); + SysMemPage.Scale(*HidBuffer); + Mouse->Show_Mouse(); + WindowsTimer = new WinTimerClass(60,FALSE); + + // + // Get rid of the windows cursor + // + + AllDone = FALSE; + + // + // Windows message loop + // + int count = 0; + char temp[100]; + CountDownTimerClass timer(BT_SYSTEM, 0); + timer.Set(60); + timer.Start(); + while ( ! AllDone ){ + if (timer.Time() == 0) { + sprintf(temp,"%d frames per second",count); + Mouse->Erase_Mouse(HidBuffer, TRUE); + HidBuffer->Print(temp,0,0,255,1); + count = 0; + timer.Set(60); + timer.Start(); + } else { + count++; + Mouse->Erase_Mouse(HidBuffer, TRUE); + } + Mouse->Draw_Mouse(HidBuffer); + HidBuffer->Blit(*ScreenBuffer); + Mouse->Erase_Mouse(HidBuffer, FALSE); + + if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ){ + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } + delete Mouse; + return msg.wParam; +} + +#pragma on(unreferenced) + + + +/*********************************************************************************************** + * WndProc -- windows message callback * + * * + * Pilfered from a windows example program - HELLOWIN.C * + * * + * * + * INPUT: Standard Windoze callback parameters * + * * + * OUTPUT: long * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/27/95 1:39PM ST : Pilfered * + *=============================================================================================*/ + +long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, + LONG lParam) + { + static int condhide = 0; + switch (message){ + + +// case WM_MOUSEMOVE: +// if (Mouse) +// Mouse->Process_Mouse(); +// break; + + case WM_LBUTTONDOWN: + if (Mouse) { + Mouse->Set_Cursor(0,0,Extract_Shape(ShapeFile,++CurrentShape)); + if (CurrentShape>160) CurrentShape = 0; + } + + break; + + case WM_RBUTTONDOWN: + if (condhide) { + Mouse->Show_Mouse(); + Mouse->Conditional_Show_Mouse(); + Mouse->Conditional_Show_Mouse(); + condhide = FALSE; + } else { + Mouse->Hide_Mouse(); + Mouse->Conditional_Hide_Mouse(0,0,320,200); + Mouse->Conditional_Hide_Mouse(0,0,320,200); + condhide = TRUE; + } + break; + + + case WM_ACTIVATEAPP: + if ((BOOL)wParam) { + if (ScreenBuffer) { + ScreenBuffer->Get_DD_Surface()->Restore(); + } + } + break; + + case WM_DESTROY: + // + // Tidy up + // + + delete ScreenBuffer; + delete Mouse; + + if ( DirectDrawObject ){ + Reset_Video_Mode(); + } + + AllDone = TRUE; + PostQuitMessage (0) ; + return(0); + } + + return DefWindowProc (hwnd, message, wParam, lParam) ; +} + + + + + diff --git a/WIN32LIB/KEYBOARD/TEST/TEST.DEF b/WIN32LIB/KEYBOARD/TEST/TEST.DEF new file mode 100644 index 0000000..1412668 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/TEST.DEF @@ -0,0 +1,14 @@ +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + + + + + + + + + diff --git a/WIN32LIB/KEYBOARD/TEST/TESTASM.ASM b/WIN32LIB/KEYBOARD/TEST/TESTASM.ASM new file mode 100644 index 0000000..687ecd4 --- /dev/null +++ b/WIN32LIB/KEYBOARD/TEST/TESTASM.ASM @@ -0,0 +1,80 @@ +; +; 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 . +; + +IDEAL +P386 +MODEL USE32 FLAT +jumps + + codeseg + + masm +; +; Change a DAC colour register directly +; +; register number in al +; +; bh=red bl=green cl=blue +; + +set_dac_col proc near + pushad + cli + push eax + mov dx,03dah + in al,dx + jmp @@1 +@@1: mov dx,03c8h + pop eax + out dx,al + jmp @@2 +@@2: inc dl + mov al,bh + out dx,al + jmp @@3 +@@3: mov al,bl + out dx,al + jmp @@4 +@@4: mov al,cl + out dx,al + jmp @@5 +@@5: sti + popad + ret +set_dac_col endp + + ideal + + +global Set_Palette_Register_:near + + +proc Set_Palette_Register_ near + + pushad + and cl,63 + mov bh,dl + and bh,63 + and bl,63 + call set_dac_col + popad + ret + +endp Set_Palette_Register_ + +end diff --git a/WIN32LIB/KEYBOARD/WWMOUSE.ASM b/WIN32LIB/KEYBOARD/WWMOUSE.ASM new file mode 100644 index 0000000..53f1a35 --- /dev/null +++ b/WIN32LIB/KEYBOARD/WWMOUSE.ASM @@ -0,0 +1,662 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : December 12, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "drawbuff.inc" +INCLUDE "gbuffer.inc" +INCLUDE "shape.inc" +INCLUDE ".\mouse.inc" + +GLOBAL C LCW_Uncompress:NEAR +GLOBAL C Get_Shape_Uncomp_Size :NEAR +GLOBAL C Get_Shape_Width :NEAR +GLOBAL C Get_Shape_Original_Height :NEAR +GLOBAL _ShapeBuffer :DWORD + +CODESEG + + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* int store - whether to store buffer or save * +;* * +;* OUTPUT: none * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Mouse_Shadow_Buffer(GraphicBufferClass *src/dest, * +;* void *buffer +;* int x_pixel, * +;* int y_pixel, * +;* int store); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Mouse_Shadow_Buffer:NEAR + PROC Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this: DWORD + ARG src_dst_obj: DWORD + ARG buffer:DWORD + ARG x: DWORD + ARG y: DWORD + ARG hotx: DWORD + ARG hoty: DWORD + ARG store: DWORD + + local x0: dword + local y0: dword + local x1: dword + local y1: dword + local buffx0: dword + local buffy0: dword + + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov esi, [this] ; get offset to mouse data + mov edi, [src_dst_obj] ; get offset to mouse data + + mov eax, [x] + mov ebx, [y] + sub eax, [hotx] + sub ebx, [hoty] + + mov [x0], eax + mov [y0], ebx + add eax, [(MouseType esi).CursorWidth] + add ebx, [(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [buffer] + mov [buffy0], eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1], eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1], eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + push ebp + cmp [store], 1 ; are we storing page? + je ??store_entry ; if so go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp, ecx +??restore_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi, edi ; xchg the source and the dest + mov ebp, ecx + +??store_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, edx ; move past right clipped pixels + add edi, ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Draw_Mouse( MouseClass * mouse_data, * +;* GraphicBufferClass *destination, * +;* int x_pixel, * +;* int y_pixel); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Draw_Mouse:NEAR + PROC Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD + ARG dest:DWORD + ARG mousex:DWORD + ARG mousey:DWORD + + local x0:dword + local y0:dword + local x1:dword + local y1:dword + local buffx0:dword + local buffy0:dword + + + mov esi, [this] ; get 32 bit offset to mouse data + mov edi, [dest] ; get 32 bit offset to dest buffer + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax, [mousex] + mov ebx, [mousey] + sub eax, [(MouseType esi).MouseXHot] + sub ebx, [(MouseType esi).MouseYHot] + + mov [x0], eax + mov [y0], ebx + add eax,[(MouseType esi).CursorWidth] + add ebx,[(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [(MouseType esi).MouseCursor] + mov [buffy0], eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1] , eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah, al +??inner_loop: + mov ch, [esi] + inc esi + test ch, ch + jz ??inc_edi + mov [edi], ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP Draw_Mouse +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL C ASM_Set_Mouse_Cursor:NEAR + PROC ASM_Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD ; pointer to mouse cursor struct + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[this] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[(MouseType esi).MaxWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[(MouseType esi).MaxHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[this] + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[ShapeBuffer] + jmp ??copy_type + +??done_copy: + mov esi,[this] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(MouseType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(MouseType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(MouseType esi).CursorHeight],ebx + mov ebx,[swidth] + mov [(MouseType esi).CursorWidth],ebx + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + push [cursor] + push [(MouseType esi).PrevCursor] + pop eax + pop [(MouseType esi).PrevCursor] + ret ; and return back to the world + + ENDP ASM_Set_Mouse_Cursor + +END diff --git a/WIN32LIB/MAKE.BAT b/WIN32LIB/MAKE.BAT new file mode 100644 index 0000000..437293d --- /dev/null +++ b/WIN32LIB/MAKE.BAT @@ -0,0 +1,2 @@ +SET WATCOM=D:\RADVD\SOURCE\CODE\WATCOM +..\CODE\WMAKE WIN32=1 diff --git a/WIN32LIB/MAKEFILE b/WIN32LIB/MAKEFILE new file mode 100644 index 0000000..fb7dd84 --- /dev/null +++ b/WIN32LIB/MAKEFILE @@ -0,0 +1,286 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* LIBS = list of all component libraries * +#* * +#* NOTE: For this makefile to work, each library directory MUST have the * +#* same name as its library. * +#* * +#* "make install" installs the library on your drive * +#* "make update" updates all source files in your slice * +#* * +#* To install or update just one library you may type: * +#* "make -DLIBS=misc.lib install" * +#* * +#*************************************************************************** + +!ifndef %WIN32LIB +WIN32LIB=. +!else +WIN32LIB=$(%WIN32LIB) +!endif + +!ifndef %WIN32VCS +WIN32VCS=. +!else +WIN32VCS=$(%WIN32VCS) +!endif + + +#=========================================================================== +# User-defined section: list each library in this macro +# NOTE: These are some order dependencies: +# 1. The directory MISC must always be made first. +# 2. The directory VIDEO must be made before the SYSTEM directory. +#=========================================================================== +LIB_NAME = win32lib +LIB_DIR = $(WIN32LIB)\lib + +#--------------------------------------------------------------------------- +# LIBS macro: a list of all component libraries +# "make LIBS=xxxx.lib [target]" makes/installs/updates only that library +#--------------------------------------------------------------------------- + +LIBS = & + audio.lib & + dipthong.lib & + drawbuff.lib & + font.lib & + iff.lib & + keyboard.lib & + mem.lib & + misc.lib & + mono.lib & + palette.lib & + shape.lib & + tile.lib & + timer.lib & + ww_win.lib & + wsa.lib & + profile.lib & + playcd.lib & + wincomm.lib + +LIB_INSTALL = & + audio.ins & + dipthong.ins & + drawbuff.ins & + font.ins & + iff.ins & + keyboard.ins & + mem.ins & + misc.ins & + mono.ins & + palette.ins & + rawfile.ins & + shape.ins & + tile.ins & + timer.ins & + ww_win.ins & + wsa.ins & + profile.ins & + playcd.ins & + wincomm.ins + + + +LIB_UPDATE = & + audio.upd & + dipthong.upd & + drawbuff.upd & + font.upd & + iff.upd & + keyboard.upd & + mem.upd & + misc.upd & + mono.upd & + palette.upd & + rawfile.upd & + shape.upd & + tile.upd & + timer.upd & + ww_win.upd & + wsa.upd & + profile.upd & + playcd.upd & + wincomm.upd + + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.lib: $(WIN32LIB)\lib + + +#--------------------------------------------------------------------------- +# Tools/commands +# LIB_CMD: library command +# LIB_CFG: library configuration file +# VCS_UPDATE: version control update command; this command should update +# all relevant files in a given directory with read-only +# copies from the archive +#--------------------------------------------------------------------------- +!include $(WIN32LIB)\project.cfg +LIB_CMD = ..\code\watcom\binnt\wlib +VCS_UPDATE = update + +#--------------------------------------------------------------------------- +# If you like to have this makefile switch to the mono screen automatically, +# you will need to do a "set MONO=Y" or to anything you like. +#--------------------------------------------------------------------------- +!ifdef %MONO +.BEFORE + mode mono + +.AFTER + mode co80 + +.ERROR + mode co80 +!endif + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(LIB_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(LIB_NAME).lib: $(LIBS) libs.lbc + $(LIB_CMD) $(LIB_CFG) $^@ @libs.lbc + + +#--------------------------------------------------------------------------- +# This construct tells make how to make all component libraries +# The commands get executed for every item in the macro. +# The macro $: extracts only the directory name from the macro item. +#--------------------------------------------------------------------------- +$(LIBS): + echo Making $^&... + cd $^& + wmake + cd .. + +#--------------------------------------------------------------------------- +# Libs is librarian response file that is only genarated if any of the +# sublibrary in the golbal variable LIBS was updated. +#--------------------------------------------------------------------------- +libs.lbc : $(LIBS) + %create $^@ + for %index in ($(LIBS)) do %append $^@ +$(LIB_DIR)\%index + + +#--------------------------------------------------------------------------- +# "make install" installs the library on your drive +#--------------------------------------------------------------------------- +install: install_dirs $(LIB_INSTALL) .SYMBOLIC + echo Compiling library... + wmake + echo Library installation complete. + +#--------------------------------------------------------------------------- +# At installation time, this target makes all non-library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +install_dirs: .SYMBOLIC + echo Making directories... + mkdir example + mkdir include + mkdir lib + mkdir srcdebug + mkdir tools + cd tools + copy $(%WWVCS)\tools\vcs.cfg + $(VCS_UPDATE) + cd.. + copy $(%WWVCS)\vcs.cfg + $(VCS_UPDATE) + cd example + copy $(%WWVCS)\example\vcs.cfg + $(VCS_UPDATE) + cd .. + + +#--------------------------------------------------------------------------- +# This target installs all library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +$(LIB_INSTALL): .SYMBOLIC + echo Installing $^&... + md $^& + cd $^& + copy $(%WWVCS)\$^&\vcs.cfg + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. + + + + + +#--------------------------------------------------------------------------- +# "make update" updates all source files in your slice +#--------------------------------------------------------------------------- +update: $(LIB_UPDATE) .SYMBOLIC + echo Library updated. + + +#--------------------------------------------------------------------------- +# This target updates all library directories +# This is a dependency for 'updates' +#--------------------------------------------------------------------------- +$(LIB_UPDATE): .SYMBOLIC + echo Updating $^&... + cd $^& + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. diff --git a/WIN32LIB/MAKEFILE.BOR b/WIN32LIB/MAKEFILE.BOR new file mode 100644 index 0000000..c8b77c5 --- /dev/null +++ b/WIN32LIB/MAKEFILE.BOR @@ -0,0 +1,267 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* LIBS = list of all component libraries * +#* * +#* NOTE: For this makefile to work, each library directory MUST have the * +#* same name as its library. * +#* * +#* "make install" installs the library on your drive * +#* "make update" updates all source files in your slice * +#* * +#* To install or update just one library you may type: * +#* "make -DLIBS=misc.lib install" * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: list each library in this macro +# NOTE: These are some order dependencies: +# 1. The directory MISC must always be made first. +# 2. The directory VIDEO must be made before the SYSTEM directory. +#=========================================================================== +LIB_NAME = win32lib +LIB_DIR = $(WIN32LIB)\lib +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# LIBS macro: a list of all component libraries +# "make LIBS=xxxx.lib [target]" makes/installs/updates only that library +#--------------------------------------------------------------------------- + +LIBS = \ + audio.lib \ + dipthong.lib \ + drawbuff.lib \ + font.lib \ + iff.lib \ + keyboard.lib \ + mem.lib \ + misc.lib \ + mono.lib \ + palette.lib \ + rawfile.lib \ + tile.lib \ + timer.lib \ + ww_win.lib \ + wsa.lib + +LIB_INSTALL = \ + audio.ins \ + dipthong.ins \ + drawbuff.ins \ + font.ins \ + iff.ins \ + keyboard.ins \ + mem.ins \ + misc.ins \ + mono.ins \ + palette.ins \ + rawfile.ins \ + tile.ins \ + timer.ins \ + ww_win.ins \ + wsa.ins + +LIB_UPDATE = \ + audio.upd \ + dipthong.upd \ + drawbuff.upd \ + font.upd \ + iff.upd \ + keyboard.upd \ + mem.upd \ + misc.upd \ + mono.upd \ + palette.upd \ + rawfile.upd \ + tile.upd \ + timer.upd \ + ww_win.upd \ + wsa.upd + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.lib = $(WIN32LIB)\\lib + + +#--------------------------------------------------------------------------- +# Tools/commands +# LIB_CMD: library command +# LIB_CFG: library configuration file +# VCS_UPDATE: version control update command; this command should update +# all relevant files in a given directory with read-only +# copies from the archive +#--------------------------------------------------------------------------- +LIB_CMD = tlib +VCS_UPDATE = update + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(LIB_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_NAME).lib: $(LIBS) + del $< + $(LIB_CMD) $< $(LIB_CFG) @&&| + +$(WIN32LIB)\\lib\\audio.lib & ++$(WIN32LIB)\\lib\\dipthong.lib & ++$(WIN32LIB)\\lib\\drawbuff.lib & ++$(WIN32LIB)\\lib\\font.lib & ++$(WIN32LIB)\\lib\\iff.lib & ++$(WIN32LIB)\\lib\\keyboard.lib & ++$(WIN32LIB)\\lib\\mem.lib & ++$(WIN32LIB)\\lib\\misc.lib & ++$(WIN32LIB)\\lib\\mono.lib & ++$(WIN32LIB)\\lib\\palette.lib & ++$(WIN32LIB)\\lib\\rawfile.lib & ++$(WIN32LIB)\\lib\\tile.lib & ++$(WIN32LIB)\\lib\\timer.lib & ++$(WIN32LIB)\\lib\\ww_win.lib & ++$(WIN32LIB)\\lib\\wsa.lib +| + +#--------------------------------------------------------------------------- +# This construct tells make how to make all component libraries +# The commands get executed for every item in the macro. +# The macro $: extracts only the directory name from the macro item. +#--------------------------------------------------------------------------- +$(LIBS): + echo Making $^&... + cd $^& + make + cd .. + +#--------------------------------------------------------------------------- +# "make install" installs the library on your drive +#--------------------------------------------------------------------------- +install: install_dirs $(LIB_INSTALL) .SYMBOLIC + echo Compiling library... + wmake + echo Library installation complete. + +#--------------------------------------------------------------------------- +# At installation time, this target makes all non-library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +install_dirs: .SYMBOLIC + echo Making directories... + mkdir example + mkdir include + mkdir lib + mkdir srcdebug + mkdir tools + cd tools + copy $(%WWVCS)\tools\vcs.cfg + $(VCS_UPDATE) + cd.. + copy $(%WWVCS)\vcs.cfg + $(VCS_UPDATE) + cd example + copy $(%WWVCS)\example\vcs.cfg + $(VCS_UPDATE) + cd .. + + +#--------------------------------------------------------------------------- +# This target installs all library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +$(LIB_INSTALL): .SYMBOLIC + echo Installing $^&... + md $^& + cd $^& + copy $(%WWVCS)\$^&\vcs.cfg + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. + + + + + +#--------------------------------------------------------------------------- +# "make update" updates all source files in your slice +#--------------------------------------------------------------------------- +update: $(LIB_UPDATE) .SYMBOLIC + echo Library updated. + + +#--------------------------------------------------------------------------- +# This target updates all library directories +# This is a dependency for 'updates' +#--------------------------------------------------------------------------- +$(LIB_UPDATE): .SYMBOLIC + echo Updating $^&... + cd $^& + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. \ No newline at end of file diff --git a/WIN32LIB/MAKE_EXE.MAK b/WIN32LIB/MAKE_EXE.MAK new file mode 100644 index 0000000..16f06d2 --- /dev/null +++ b/WIN32LIB/MAKE_EXE.MAK @@ -0,0 +1,173 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\MEM\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj & + object0.obj & + object1.obj + +PROJ_LIBS = & + lib1.lib & + lib2.lib & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/MAKE_LIB.MAK b/WIN32LIB/MAKE_LIB.MAK new file mode 100644 index 0000000..b55d01d --- /dev/null +++ b/WIN32LIB/MAKE_LIB.MAK @@ -0,0 +1,182 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = project_name +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + object1.obj & + object2.obj & + object3.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/ALLOC.CPP b/WIN32LIB/MEM/ALLOC.CPP new file mode 100644 index 0000000..932c484 --- /dev/null +++ b/WIN32LIB/MEM/ALLOC.CPP @@ -0,0 +1,512 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; +extern void (*Memory_Error_Exit)(char *string)=NULL; + + +//#define MEM_CHECK + +#ifdef MEM_CHECK +extern "C"{ + extern void __cdecl Int3(void); +} +#endif //MEM_CHECK + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *, long const ) +{ +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *, long const ) +{ +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + * 09/28/1995 ST : Simplified for win95 * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + +#ifdef WIN32 + + void *mem_ptr; + +#ifdef MEM_CHECK + bytes_to_alloc += 32; +#endif //MEM_CHECK + + mem_ptr = malloc ( bytes_to_alloc ); + + if ( !mem_ptr && Memory_Error ){ + Memory_Error(); + } + + if ( mem_ptr && ( flags & MEM_CLEAR ) ){ + memset ( mem_ptr , 0 , bytes_to_alloc ); + } + +#ifdef MEM_CHECK + mem_ptr = (void*)((char*)mem_ptr + 16); + unsigned long *magic_ptr =(unsigned long*) ( ((char *)mem_ptr) - 16 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = bytes_to_alloc - 32; + magic_ptr = (unsigned long*) ( ((char*)mem_ptr) + bytes_to_alloc - 32 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = (unsigned long)mem_ptr; +#endif //MEM_CHECK + + Memory_Calls++; + return ( mem_ptr ); + +#else + + + + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); + +#endif +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +#ifdef WIN32 + +void Free(void const *pointer) +{ + + if ( pointer ){ + +#ifdef MEM_CHECK + + unsigned long *magic_ptr = (unsigned long*) ( ((char*)pointer) - 16 ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + magic_ptr = (unsigned long*) ( ((char*)pointer) + *magic_ptr ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + pointer = (void*) (((char*)pointer)-16); +#endif //MEM_CHECK + + free ( (void*)pointer ); + Memory_Calls--; + } + +#else + +void Free(void const *pointer) +{ + + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } + +#endif +} + + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + return ( 64*1024*1024 ); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + + return ( 64*1024*1024 ); +} + diff --git a/WIN32LIB/MEM/MAKEFILE b/WIN32LIB/MEM/MAKEFILE new file mode 100644 index 0000000..61c99a2 --- /dev/null +++ b/WIN32LIB/MEM/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj & + vmpagein.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/MAKEFILE.BOR b/WIN32LIB/MEM/MAKEFILE.BOR new file mode 100644 index 0000000..072d586 --- /dev/null +++ b/WIN32LIB/MEM/MAKEFILE.BOR @@ -0,0 +1,175 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + alloc.obj \ + mem.obj \ + newdel.obj \ + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+alloc.obj & +-+mem.obj & +-+newdel.obj & +-+mem_copy.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/MEM/MAKEFILE.WAT b/WIN32LIB/MEM/MAKEFILE.WAT new file mode 100644 index 0000000..2965600 --- /dev/null +++ b/WIN32LIB/MEM/MAKEFILE.WAT @@ -0,0 +1,183 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/MEM.CPP b/WIN32LIB/MEM/MEM.CPP new file mode 100644 index 0000000..6829c57 --- /dev/null +++ b/WIN32LIB/MEM/MEM.CPP @@ -0,0 +1,1089 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned long Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned long FreeMem; // Current amount of free ram (in paragraphs). + unsigned long TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WIN32LIB/MEM/MEMFLAG.H b/WIN32LIB/MEM/MEMFLAG.H new file mode 100644 index 0000000..c28d920 --- /dev/null +++ b/WIN32LIB/MEM/MEMFLAG.H @@ -0,0 +1,109 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/* +** Prototypes for VMPAGEIN.ASM +*/ +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + +#pragma option -Jgd + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + +#pragma option -Jgd + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +extern "C" { + void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); +} + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif diff --git a/WIN32LIB/MEM/MEM_COPY.ASM b/WIN32LIB/MEM/MEM_COPY.ASM new file mode 100644 index 0000000..16bfc80 --- /dev/null +++ b/WIN32LIB/MEM/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL C Mem_Copy : NEAR +GLOBAL C Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy +IF 0 +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block +ENDIF +END + + + diff --git a/WIN32LIB/MEM/MSVC/ALLOC.CPP b/WIN32LIB/MEM/MSVC/ALLOC.CPP new file mode 100644 index 0000000..3a914d1 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/ALLOC.CPP @@ -0,0 +1,465 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ +#ifdef cuts + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +#endif +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + +#ifdef cuts + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +#endif + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ +// union REGS regs ; +// struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + +#ifdef cuts + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + +#endif + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ +// union REGS regs ; +// struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. +// if (*byteptr & MEM_REAL) { +// regs.x.eax = 0x101; +// regs.x.edx = *(((long *)pointer) - 1); +// segread ( & sregs ) ; +// int386x(0x31, ®s, ®s, &sregs); +// } else { + free((void *)pointer); +// } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); + return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +// return(_memavl()); + return Largest_Mem_Block () ; +} + diff --git a/WIN32LIB/MEM/MSVC/MAKEFILE b/WIN32LIB/MEM/MSVC/MAKEFILE new file mode 100644 index 0000000..5aee611 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/MSVC/MAKEFILE.BOR b/WIN32LIB/MEM/MSVC/MAKEFILE.BOR new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MAKEFILE.BOR @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/MSVC/MAKEFILE.WAT b/WIN32LIB/MEM/MSVC/MAKEFILE.WAT new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MAKEFILE.WAT @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/MSVC/MEM.CPP b/WIN32LIB/MEM/MSVC/MEM.CPP new file mode 100644 index 0000000..dda0b68 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MEM.CPP @@ -0,0 +1,1091 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)GetTickCount() >> 8; + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(GetTickCount() >> 8); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = __max(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = __max(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = __max(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(GetTickCount() >> 8); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(GetTickCount() >> 8); + node->ID = id; + } +} + + + + + + diff --git a/WIN32LIB/MEM/MSVC/MEM.MAK b/WIN32LIB/MEM/MSVC/MEM.MAK new file mode 100644 index 0000000..d50291d --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MEM.MAK @@ -0,0 +1,221 @@ +# +# 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 . +# + +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mem.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/mem.lib $(OUTDIR)/mem.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"mem.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"mem.bsc" +BSC32_SBRS= \ + $(INTDIR)/NEWDEL.SBR \ + $(INTDIR)/ALLOC.SBR \ + $(INTDIR)/MEM.SBR + +$(OUTDIR)/mem.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LIB32=lib.exe +# ADD BASE LIB32 /NOLOGO +# ADD LIB32 /NOLOGO +LIB32_FLAGS=/NOLOGO /OUT:$(OUTDIR)\"mem.lib" +DEF_FLAGS= +DEF_FILE= +LIB32_OBJS= \ + $(INTDIR)/NEWDEL.OBJ \ + $(INTDIR)/ALLOC.OBJ \ + $(INTDIR)/MEM.OBJ \ + .\MEM_COPY.OBJ + +$(OUTDIR)/mem.lib : $(OUTDIR) $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/mem.lib $(OUTDIR)/mem.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Z7 /YX /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /c +# ADD CPP /nologo /W3 /GX /Z7 /YX /Od /I "c:\wwlib32\include" /D "_DEBUG" /D "_WINDOWS" /FR /c +CPP_PROJ=/nologo /W3 /GX /Z7 /YX /Od /I "c:\wwlib32\include" /D "_DEBUG" /D\ + "_WINDOWS" /FR$(INTDIR)/ /Fp$(OUTDIR)/"mem.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinDebug/ +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"mem.bsc" +BSC32_SBRS= \ + $(INTDIR)/NEWDEL.SBR \ + $(INTDIR)/ALLOC.SBR \ + $(INTDIR)/MEM.SBR + +$(OUTDIR)/mem.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LIB32=lib.exe +# ADD BASE LIB32 /NOLOGO +# ADD LIB32 /NOLOGO +LIB32_FLAGS=/NOLOGO /OUT:$(OUTDIR)\"mem.lib" +DEF_FLAGS= +DEF_FILE= +LIB32_OBJS= \ + $(INTDIR)/NEWDEL.OBJ \ + $(INTDIR)/ALLOC.OBJ \ + $(INTDIR)/MEM.OBJ \ + .\MEM_COPY.OBJ + +$(OUTDIR)/mem.lib : $(OUTDIR) $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\MEM_COPY.ASM +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\NEWDEL.CPP +DEP_NEWDE=\ + .\WWMEM.H\ + \wwlib32\include\wwstd.h\ + .\MEMFLAG.H + +$(INTDIR)/NEWDEL.OBJ : $(SOURCE) $(DEP_NEWDE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\ALLOC.CPP +DEP_ALLOC=\ + .\WWMEM.H\ + \wwlib32\include\mono.h\ + \wwlib32\include\wwstd.h\ + .\MEMFLAG.H + +$(INTDIR)/ALLOC.OBJ : $(SOURCE) $(DEP_ALLOC) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\MEM.CPP +DEP_MEM_C=\ + \wwlib32\include\wwstd.h\ + .\WWMEM.H\ + \wwlib32\include\timer.h\ + .\MEMFLAG.H + +$(INTDIR)/MEM.OBJ : $(SOURCE) $(DEP_MEM_C) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\MEM_COPY.OBJ +# End Source File +# End Group +# End Project +################################################################################ diff --git a/WIN32LIB/MEM/MSVC/MEMFLAG.H b/WIN32LIB/MEM/MSVC/MEMFLAG.H new file mode 100644 index 0000000..690b2f7 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MEMFLAG.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +//inline void * operator new[] (size_t size, MemoryFlagType flag) +//{ +// return(Alloc(size, flag)); +//} + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/WIN32LIB/MEM/MSVC/MEM_COPY.ASM b/WIN32LIB/MEM/MSVC/MEM_COPY.ASM new file mode 100644 index 0000000..6ce638d --- /dev/null +++ b/WIN32LIB/MEM/MSVC/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL C Mem_Copy : NEAR +GLOBAL C Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy + +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block + +END + + + \ No newline at end of file diff --git a/WIN32LIB/MEM/MSVC/NEWDEL.CPP b/WIN32LIB/MEM/MSVC/NEWDEL.CPP new file mode 100644 index 0000000..165f7f8 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/NEWDEL.CPP @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : October 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +//void * operator new[](size_t size) +//{ +// return (Alloc((unsigned long) size, MEM_NEW)); +//} + + + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +//void operator delete[](void *ptr) +//{ +// Free(ptr); +//} + + + diff --git a/WIN32LIB/MEM/MSVC/WWMEM.H b/WIN32LIB/MEM/MSVC/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WIN32LIB/MEM/MSVC/WWMEM.INC b/WIN32LIB/MEM/MSVC/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WIN32LIB/MEM/MSVC/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WIN32LIB/MEM/NEWDEL.CPP b/WIN32LIB/MEM/NEWDEL.CPP new file mode 100644 index 0000000..289f815 --- /dev/null +++ b/WIN32LIB/MEM/NEWDEL.CPP @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : October 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WIN32LIB/MEM/OLDMEM/ALLOC.CPP b/WIN32LIB/MEM/OLDMEM/ALLOC.CPP new file mode 100644 index 0000000..d047ec2 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/ALLOC.CPP @@ -0,0 +1,455 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); + return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +// return(_memavl()); + return Largest_Mem_Block () ; +} + diff --git a/WIN32LIB/MEM/OLDMEM/MAKEFILE b/WIN32LIB/MEM/OLDMEM/MAKEFILE new file mode 100644 index 0000000..5aee611 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/OLDMEM/MAKEFILE.BOR b/WIN32LIB/MEM/OLDMEM/MAKEFILE.BOR new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MAKEFILE.BOR @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/OLDMEM/MAKEFILE.WAT b/WIN32LIB/MEM/OLDMEM/MAKEFILE.WAT new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MAKEFILE.WAT @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/MEM/OLDMEM/MEM.CPP b/WIN32LIB/MEM/OLDMEM/MEM.CPP new file mode 100644 index 0000000..1df2df6 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MEM.CPP @@ -0,0 +1,1089 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WIN32LIB/MEM/OLDMEM/MEMFLAG.H b/WIN32LIB/MEM/OLDMEM/MEMFLAG.H new file mode 100644 index 0000000..a05800d --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MEMFLAG.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/WIN32LIB/MEM/OLDMEM/MEM_COPY.ASM b/WIN32LIB/MEM/OLDMEM/MEM_COPY.ASM new file mode 100644 index 0000000..01fb8be --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Mem_Copy : NEAR +GLOBAL Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy + +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block + +END + + + diff --git a/WIN32LIB/MEM/OLDMEM/NEWDEL.CPP b/WIN32LIB/MEM/OLDMEM/NEWDEL.CPP new file mode 100644 index 0000000..289f815 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/NEWDEL.CPP @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : October 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WIN32LIB/MEM/OLDMEM/WWMEM.H b/WIN32LIB/MEM/OLDMEM/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WIN32LIB/MEM/OLDMEM/WWMEM.INC b/WIN32LIB/MEM/OLDMEM/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WIN32LIB/MEM/OLDMEM/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WIN32LIB/MEM/VMPAGEIN.ASM b/WIN32LIB/MEM/VMPAGEIN.ASM new file mode 100644 index 0000000..be471a7 --- /dev/null +++ b/WIN32LIB/MEM/VMPAGEIN.ASM @@ -0,0 +1,90 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : VMPAGEIN.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Last Update : March 8th, 1996 [ST] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Force_VM_Page_In -- forces a buffer to be paged in under win95 * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + + + LOCALS ?? + + +GLOBAL C Force_VM_Page_In:near + +CODESEG + + +;*************************************************************************** +;* Force_VM_Page_In -- forces a buffer to be paged in under win95 * +;* * +;* * +;* INPUT: ptr to start of buffer * +;* length of buffer * +;* * +;* OUTPUT: * +;* * +;* Prototype: * +;* void Force_VM_Page_In (void *buffer, int length); * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 3/8/96 4:18PM ST : Created. * +;*=========================================================================* + + +proc Force_VM_Page_In C near + USES eax,ecx,edx,edi + ARG buffer_start:dword + ARG buffer_length:dword + + mov ecx,[buffer_length] + xor al,al + test ecx,ecx + mov edx,4096 + jz ??out + + mov edi,[buffer_start] + + +??next_page: add [edi],al + add edi,edx + sub ecx,edx + jg ??next_page + +??out: ret + +endp Force_VM_Page_In + +END diff --git a/WIN32LIB/MEM/WWMEM.H b/WIN32LIB/MEM/WWMEM.H new file mode 100644 index 0000000..917a10d --- /dev/null +++ b/WIN32LIB/MEM/WWMEM.H @@ -0,0 +1,70 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WIN32LIB/MEM/WWMEM.INC b/WIN32LIB/MEM/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WIN32LIB/MEM/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WIN32LIB/MISC/CLIPRECT.ASM b/WIN32LIB/MISC/CLIPRECT.ASM new file mode 100644 index 0000000..d595722 --- /dev/null +++ b/WIN32LIB/MISC/CLIPRECT.ASM @@ -0,0 +1,269 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Clip_Rect :NEAR +GLOBAL C Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * + + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, return the oroginal data) +; - trivially rejected (return with no action, return error code) +; - retangle must be iteratively clipped again edges of the clipping window +; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz ??clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz ??clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz ??left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + +??left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz ??bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + +??bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz ??right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz ??clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify +??clip_out: + ret + ENDP Clip_Rect + + +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * + + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl ??x_axix_ok + mov eax,1 + + test esi,esi + jl ??shift_right + mov [dword ptr ebx],0 + jmp ??x_axix_ok +??shift_right: + inc edi + sub [ebx],edi +??x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl ??confi_out + mov eax,1 + + test esi,esi + jl ??shift_top + mov [dword ptr ebx],0 + ret +??shift_top: + inc edi + sub [ebx],edi +??confi_out: + ret + + ENDP Confine_Rect + + END diff --git a/WIN32LIB/MISC/CRC.ASM b/WIN32LIB/MISC/CRC.ASM new file mode 100644 index 0000000..76a133c --- /dev/null +++ b/WIN32LIB/MISC/CRC.ASM @@ -0,0 +1,114 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Calculate_CRC :NEAR + + CODESEG + +; LONG Calculate_CRC(VOID *buffer, LONG length); + PROC Calculate_CRC C near + USES esi + + ARG buffer:DWORD + ARG length:DWORD + + LOCAL crc:DWORD + + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + ; Fetch the length of the data block to CRC. + mov ecx,[length] + jecxz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short ??remainder +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov [crc],ebx + popad + mov eax,[crc] + ret + + ENDP Calculate_CRC + + END \ No newline at end of file diff --git a/WIN32LIB/MISC/DDRAW.CPP b/WIN32LIB/MISC/DDRAW.CPP new file mode 100644 index 0000000..9e76f43 --- /dev/null +++ b/WIN32LIB/MISC/DDRAW.CPP @@ -0,0 +1,1000 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Win32 Library * + * * + * File Name : DDRAW.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : October 10, 1995 * + * * + * Last Update : October 10, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +#include "misc.h" +#include +#include + + +LPDIRECTDRAW DirectDrawObject=NULL; // Pointer to the direct draw object +LPDIRECTDRAW2 DirectDraw2Interface = NULL; // Pointer to direct draw 2 interface + +HWND MainWindow; // Handle to programs main window + // this is passed to SetCooperativeLevel + // so DirectDraw knows which window is ours + + +PALETTEENTRY PaletteEntries[256]; // 256 windows palette entries +LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object +BOOL FirstPaletteSet=FALSE; // Is this the first time 'Set_Palette' has been called? +LPDIRECTDRAWSURFACE PaletteSurface=NULL; +SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces +BOOL CanVblankSync = TRUE; + +BOOL SystemToVideoBlits =FALSE; // Does hardware support system mem to video mem blits? +BOOL VideoToSystemBlits =FALSE; // Does hardware support video mem to system mem blits? +BOOL SystemToSystemBlits = FALSE; // Does hardware support system mem to system mem blits? +BOOL OverlappedVideoBlits = TRUE; // Can video driver blit overlapped regions? + +/* +** Function to call if we detect focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void) = NULL; +extern void (*Misc_Focus_Restore_Function)(void) = NULL; + + +/*********************************************************************************************** + * Process_DD_Result -- Does a message box based on the result of a DD command * + * * + * INPUT: HRESULT result - the result returned from the direct draw command * + * int display_ok_msg - should a message be displayed if command ok * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/27/1995 PWG : Created. * + *=============================================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg) +{ + switch (result) { + case DD_OK: + if (display_ok_msg) { + MessageBox(MainWindow, "Direct Draw request went ok.", "Note", MB_ICONEXCLAMATION|MB_OK); + } + break; + case DDERR_ALREADYINITIALIZED: + MessageBox(MainWindow, "This object is already initialized ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_BLTFASTCANTCLIP: + MessageBox(MainWindow, "Return if a clipper object is attached to the source surface passed into a BltFast call.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTATTACHSURFACE: + MessageBox(MainWindow, "This surface can not be attached to the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTDETACHSURFACE: + MessageBox(MainWindow, "This surface can not be detached from the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTCREATEDC: + MessageBox(MainWindow, "Windows can not create any more DCs","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTDUPLICATE: + MessageBox(MainWindow, "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTLOCKSURFACE: + MessageBox(MainWindow, "Unable to lock surface because no driver exists which can supply a pointer to the surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CLIPPERISUSINGHWND: + MessageBox(MainWindow, "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_COLORKEYNOTSET: + MessageBox(MainWindow, "No src color key specified for this operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CURRENTLYNOTAVAIL: + MessageBox(MainWindow, "Support is currently not available. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_DIRECTDRAWALREADYCREATED: + MessageBox(MainWindow, "A DirectDraw object representing this driver has already been created for this process. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCEPTION: + MessageBox(MainWindow, "An exception was encountered while performing the requested operation. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCLUSIVEMODEALREADYSET: + MessageBox(MainWindow, "An attempt was made to set the cooperative level when it was already set to exclusive.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_GENERIC: + MessageBox(MainWindow, "Generic failure.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HEIGHTALIGN: + MessageBox(MainWindow, "Height of rectangle provided is not a multiple of reqd alignment.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDALREADYSET: + MessageBox(MainWindow, "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDSUBCLASSED: + MessageBox(MainWindow, "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_IMPLICITLYCREATED: + MessageBox(MainWindow, "This surface can not be restored because it is an implicitly created surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INCOMPATIBLEPRIMARY: + MessageBox(MainWindow, "Unable to match primary surface creation request with existing primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCAPS: + MessageBox(MainWindow, "One or more of the caps bits passed to the callback are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCLIPLIST: + MessageBox(MainWindow, "DirectDraw does not support the provided cliplist.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDDIRECTDRAWGUID: + MessageBox(MainWindow, "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDMODE: + MessageBox(MainWindow, "DirectDraw does not support the requested mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDOBJECT: + MessageBox(MainWindow, "DirectDraw received a pointer that was an invalid DIRECTDRAW object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPARAMS: + MessageBox(MainWindow, "One or more of the parameters passed to the function are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPIXELFORMAT: + MessageBox(MainWindow, "The pixel format was invalid as specified.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPOSITION: + MessageBox(MainWindow, "Returned when the position of the overlay on the destination is no longer legal for that destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDRECT: + MessageBox(MainWindow, "Rectangle provided was invalid.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + + case DDERR_INVALIDSURFACETYPE: + MessageBox(MainWindow, "The requested action could not be performed because the surface was of the wrong type.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_LOCKEDSURFACES: + MessageBox(MainWindow, "Operation could not be carried out because one or more surfaces are locked.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NO3D: + MessageBox(MainWindow, "There is no 3D present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOALPHAHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no alpha accleration hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOANTITEARHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for synchronizing blts to avoid tearing. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOBLTHW: + MessageBox(MainWindow, "No blter hardware present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOBLTQUEUEHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for asynchronous blting. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOCLIPLIST: + MessageBox(MainWindow, "No cliplist available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCLIPPERATTACHED: + MessageBox(MainWindow, "No clipper object attached to surface object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORCONVHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no color conversion hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEY: + MessageBox(MainWindow, "Surface doesn't currently have a color key","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support of the destination color key.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOOPERATIVELEVELSET: + MessageBox(MainWindow, "Create function called without DirectDraw object method SetCooperativeLevel being called.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODC: + MessageBox(MainWindow, "No DC was ever created for this surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODDROPSHW: + MessageBox(MainWindow, "No DirectDraw ROP hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWHW: + MessageBox(MainWindow, "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWSUPPORT: + MessageBox(MainWindow, "No DirectDraw support possible with current display driver.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEMULATION: + MessageBox(MainWindow, "Software emulation not available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEXCLUSIVEMODE: + MessageBox(MainWindow, "Operation requires the application to have exclusive mode but the application does not have exclusive mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOFLIPHW: + MessageBox(MainWindow, "Flipping visible surfaces is not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOGDI: + MessageBox(MainWindow, "There is no GDI present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOHWND: + MessageBox(MainWindow, "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOMIRRORHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYDEST: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no overlay hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEATTACHED: + MessageBox(MainWindow, "No palette object attached to this surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEHW: + MessageBox(MainWindow, "No hardware support for 16 or 256 color palettes.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NORASTEROPHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no appropriate raster op hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOROTATIONHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no rotation hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOSTRETCHHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for stretching.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLORINDEX: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT8BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTAOVERLAYSURFACE: + MessageBox(MainWindow, "Returned when an overlay member is called for a non-overlay surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTEXTUREHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no texture mapping hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFLIPPABLE: + MessageBox(MainWindow, "An attempt has been made to flip a surface that is not flippable.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFOUND: + MessageBox(MainWindow, "Requested item was not found.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTLOCKED: + MessageBox(MainWindow, "Surface was not locked. An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTPALETTIZED: + MessageBox(MainWindow, "The surface being used is not a palette-based surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOVSYNCHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZBUFFERHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for zbuffer blting.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZOVERLAYHW: + MessageBox(MainWindow, "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFCAPS: + MessageBox(MainWindow, "The hardware needed for the requested operation has already been allocated.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFVIDEOMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCANTCLIP: + MessageBox(MainWindow, "The hardware does not support clipped overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + MessageBox(MainWindow, "Can only have ony color key active at one time for overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYNOTVISIBLE: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on a hidden overlay.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PALETTEBUSY: + MessageBox(MainWindow, "Access to this palette is being refused because the palette is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PRIMARYSURFACEALREADYEXISTS: + MessageBox(MainWindow, "This process already has created a primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_REGIONTOOSMALL: + MessageBox(MainWindow, "Region passed to Clipper::GetClipList is too small.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYATTACHED: + MessageBox(MainWindow, "This surface is already attached to the surface it is being attached to.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYDEPENDENT: + MessageBox(MainWindow, "This surface is already a dependency of the surface it is being made a dependency of.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEBUSY: + MessageBox(MainWindow, "Access to this surface is being refused because the surface is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEISOBSCURED: + MessageBox(MainWindow, "Access to surface refused because the surface is obscured.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACELOST: + MessageBox(MainWindow, "Access to this surface is being refused because the surface memory is gone. The DirectDrawSurface object representing this surface should have Restore called on it.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACENOTATTACHED: + MessageBox(MainWindow, "The requested surface is not attached.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGHEIGHT: + MessageBox(MainWindow, "Height requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGSIZE: + MessageBox(MainWindow, "Size requested by DirectDraw is too large -- the individual height and width are OK.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGWIDTH: + MessageBox(MainWindow, "Width requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTED: + MessageBox(MainWindow, "Action not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDFORMAT: + MessageBox(MainWindow, "FOURCC format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDMASK: + MessageBox(MainWindow, "Bitmask in the pixel format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_VERTICALBLANKINPROGRESS: + MessageBox(MainWindow, "Vertical blank is in progress.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WASSTILLDRAWING: + MessageBox(MainWindow, "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WRONGMODE: + MessageBox(MainWindow, "This surface can not be restored because it was created in a different mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_XALIGN: + MessageBox(MainWindow, "Rectangle provided was not horizontally aligned on required boundary.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + default: + char string[256]; + sprintf (string, "Unrecognised Direct Draw result code: %d", result & 0xffff); + MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + break; + } +} + + + +/*********************************************************************************************** + * Check_Overlapped_Blit_Capability -- See if video driver supports blitting overlapped regions* + * * + * We will check for this by drawing something to a video page and blitting it over itself. * + * If we end up with the top line repeating then overlapped region blits dont work. * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 5:06PM ST : Created * + *=============================================================================================*/ +void Check_Overlapped_Blit_Capability(void) +{ + + /* + ** Assume we can until we find out otherwise + */ + OverlappedVideoBlits = TRUE; + + GraphicBufferClass test_buffer; + + test_buffer.Init (64, 64, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + test_buffer.Clear(); + + /* + ** Plot a pixel in the top left corner of the buffer. + */ + test_buffer.Put_Pixel(0, 0, 255); + + /* + ** Blit the buffer down by one line. If we end up with a vertical strip of pixel 255's then + ** overlapped blits dont work + */ + + test_buffer.Blit(test_buffer, 0, 0, 0, 1, test_buffer.Get_Width(), test_buffer.Get_Height()-1); + + if (test_buffer.Get_Pixel (0 ,5) == 255) OverlappedVideoBlits = FALSE; +} + + + +/*********************************************************************************************** + * Set_Video_Mode -- Initializes Direct Draw and sets the required Video Mode * + * * + * INPUT: int width - the width of the video mode in pixels * + * int height - the height of the video mode in pixels * + * int bits_per_pixel - the number of bits per pixel the video mode supports * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel) +{ + HRESULT result; + // + // If there is not currently a direct draw object then we need to define one. + // + if ( DirectDrawObject == NULL ){ + //MessageBox(MainWindow, "In Set_Video_Mode. About to call DirectDrawCreate.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawCreate(NULL, &DirectDrawObject, NULL); + Process_DD_Result(result, FALSE); + if (result == DD_OK){ + if (w==320){ + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + } else { + //MessageBox(MainWindow, "In Set_Video_Mode. About to call SetCooperativeLevel.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + Process_DD_Result(result, FALSE); + }else{ + return (FALSE); + } + } + + // + // Set the required display mode with 8 bits per pixel + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call call SetDisplayMode.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetDisplayMode ( w , h , bits_per_pixel ); + if (result != DD_OK){ +// Process_DD_Result(result, FALSE); + DirectDrawObject->Release(); + DirectDrawObject = NULL; + return(FALSE); + } + + // + // Create a direct draw palette object + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call CreatePalette.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &PaletteEntries[0] , &PalettePtr ,NULL); + Process_DD_Result(result, FALSE); + if (result != DD_OK){ + return (FALSE); + } + + Check_Overlapped_Blit_Capability(); + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); +#if (0) + /* + ** Find out if DirectX 2 extensions are available + */ + result = DirectDrawObject->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DirectDraw2Interface); + SystemToVideoBlits = FALSE; + VideoToSystemBlits = FALSE; + SystemToSystemBlits= FALSE; + if (result != DD_OK){ + DirectDraw2Interface = NULL; + }else{ + DDCAPS capabilities; + DDCAPS emulated_capabilities; + + memset ((char*)&capabilities, 0, sizeof(capabilities)); + memset ((char*)&emulated_capabilities, 0, sizeof(emulated_capabilities)); + capabilities.dwSize = sizeof (capabilities); + emulated_capabilities.dwSize = sizeof (emulated_capabilities); + + DirectDrawObject->GetCaps (&capabilities, &emulated_capabilities); + + if (capabilities.dwCaps & DDCAPS_CANBLTSYSMEM){ + SystemToVideoBlits = (capabilities.dwSVBCaps & DDCAPS_BLT) ? TRUE : FALSE; + VideoToSystemBlits = (capabilities.dwVSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + SystemToSystemBlits = (capabilities.dwSSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + } + } +#endif //(0) + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + + return (TRUE); + +} + +/*********************************************************************************************** + * Reset_Video_Mode -- Resets video mode and deletes Direct Draw Object * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +void Reset_Video_Mode(void) +{ + HRESULT result; + + // + // If a direct draw object has been declared and a video mode has been set + // then reset the video mode and release the direct draw object. + // + if ( DirectDrawObject ) { + result = DirectDrawObject->RestoreDisplayMode(); + Process_DD_Result(result, FALSE); + result = DirectDrawObject->Release(); + Process_DD_Result(result, FALSE); + + DirectDrawObject = NULL; + } +} + + + + +/*********************************************************************************************** + * Get_Free_Video_Memory -- returns amount of free video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: bytes of available video RAM * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:52PM ST : Created * + *=============================================================================================*/ +unsigned int Get_Free_Video_Memory(void) +{ + + DDCAPS video_capabilities; + + if (DirectDrawObject){ + + video_capabilities.dwSize = sizeof (video_capabilities); + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + char string [256]; + sprintf (string, "In Get_Free_Video_Memory. About to return %d bytes",video_capabilities.dwVidMemFree); + //MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + return (video_capabilities.dwVidMemFree); + } + } + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to return failure","Note", MB_ICONEXCLAMATION|MB_OK); + return (0); +} + + + + +/*********************************************************************************************** + * Get_Video_Hardware_Caps -- returns bitmask of direct draw video hardware support * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: hardware flags * + * * + * WARNINGS: Must call Set_Video_Mode 1st to create the direct draw object * + * * + * HISTORY: * + * 1/12/96 9:14AM ST : Created * + *=============================================================================================*/ +unsigned Get_Video_Hardware_Capabilities(void) +{ + DDCAPS video_capabilities; + unsigned video; + + /* + ** Fail if the direct draw object has not been initialised + */ + if (!DirectDrawObject) return (0); + + /* + ** Get the capabilities of the direct draw object + */ + video_capabilities.dwSize = sizeof(video_capabilities); + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + HRESULT result = DirectDrawObject->GetCaps (&video_capabilities, NULL); + if (result != DD_OK){ + Process_DD_Result(result, FALSE); + return (0); + } + + /* + ** Set flags to indicate the presence of the features we are interested in + */ + video = 0; + + /* Hardware blits supported? */ + if (video_capabilities.dwCaps & DDCAPS_BLT) video |= VIDEO_BLITTER; + + /* Hardware blits asyncronous? */ + if (video_capabilities.dwCaps & DDCAPS_BLTQUEUE) video |= VIDEO_BLITTER_ASYNC; + + /* Can palette changes be synced to vertical refresh? */ + if (video_capabilities.dwCaps & DDCAPS_PALETTEVSYNC) video |= VIDEO_SYNC_PALETTE; + + /* Is the video cards memory bank switched? */ + if (video_capabilities.dwCaps & DDCAPS_BANKSWITCHED) video |= VIDEO_BANK_SWITCHED; + + /* Can the blitter do filled rectangles? */ + if (video_capabilities.dwCaps & DDCAPS_BLTCOLORFILL) video |= VIDEO_COLOR_FILL; + + /* Is there no hardware assistance avaailable at all? */ + if (video_capabilities.dwCaps & DDCAPS_NOHARDWARE) video |= VIDEO_NO_HARDWARE_ASSIST; + + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + return (video); +} + + + + +/*********************************************************************************************** + * Wait_Vert_Blank -- Waits for the start (leading edge) of a vertical blank * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=============================================================================================*/ +extern int ScreenWidth; +void Wait_Vert_Blank(void) +{ + if( ScreenWidth!=320 && CanVblankSync){ + HRESULT result = DirectDrawObject->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); + if (result == E_NOTIMPL){ + CanVblankSync = FALSE; + return; + } + Process_DD_Result(result, FALSE); + } +} + + + + + +/*********************************************************************************************** + * Set_Palette -- set a direct draw palette * + * * + * * + * * + * INPUT: ptr to 768 rgb palette bytes * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/11/95 3:33PM ST : Created * + *=============================================================================================*/ +void Set_DD_Palette ( void *palette ) +{ + + /* + ** Trap null ptr + */ + if (!palette) return; + + int j; + int k; + char *palette_get; + + if ( DirectDrawObject && PaletteSurface ){ + + k=0; + + palette_get = (char*)palette; + + for( j=0 ; j<768 ; j+=3 ) + { + PaletteEntries[k].peRed = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peGreen = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peBlue = (unsigned char)((*palette_get++)<<2); + k++; + } + + if ( !FirstPaletteSet ){ + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetPalette","Note", MB_ICONEXCLAMATION|MB_OK); + PaletteSurface->SetPalette( PalettePtr ); + FirstPaletteSet=TRUE; + } + + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetEntries","Note", MB_ICONEXCLAMATION|MB_OK); + PalettePtr->SetEntries( 0 , 0 , 256 , &PaletteEntries[0] ); + } + //MessageBox(MainWindow, "Leaving Set_DD_Palette","Note", MB_ICONEXCLAMATION|MB_OK); + +} + + + + + +/*********************************************************************************************** + * Wait_Blit -- waits for the DirectDraw blitter to become idle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 07-25-95 03:53pm ST : Created * + *=============================================================================================*/ + +void Wait_Blit (void) +{ + HRESULT return_code; + + do { + return_code=PaletteSurface->GetBltStatus (DDGBS_ISBLTDONE); + } while (return_code != DD_OK && return_code != DDERR_SURFACELOST); + +} + + + +/*********************************************************************************************** + * SMC::SurfaceMonitorClass -- constructor for surface monitor class * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:23PM ST : Created * + *=============================================================================================*/ + +SurfaceMonitorClass::SurfaceMonitorClass(void) +{ + for (int i=0 ; iRestore() != DD_OK){ + if (Misc_Focus_Loss_Function){ + Misc_Focus_Loss_Function(); + } + return; + } + } + } + + /* + ** PWG/ST: Now that we know all the surfaces are restored call + ** the function pointer to notify the program that it has + ** happened. This function pointer is used to clear the pages, + ** etc. + */ + if (Misc_Focus_Restore_Function){ + Misc_Focus_Restore_Function(); + } + + SurfacesRestored = TRUE; + + /* + ** Restore the palette + */ + Set_DD_Palette (CurrentPalette); + } +} + + +/*********************************************************************************************** + * SMC::Set_Surface_Focus -- set the InFocus flag to the given state * + * * + * The InFocus flag is used to keep track of whether our application is currently in focus. * + * We dont want to be restoring video surfaces when we are supposed to be running in the * + * background. * + * * + * INPUT: bool in focus * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/6/95 12:21PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Set_Surface_Focus ( BOOL in_focus ) +{ + InFocus=in_focus; +} + + + + +/*********************************************************************************************** + * SMC::Release -- releases all direct draw surfaces * + * * + * Call this at the end of the game before called RestoreDisplayMode * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:23PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Release(void) +{ + /* + ** Call release for each Direct Draw surface + */ + for (int i=0 ; iRelease(); + Surface[i] = 0; + } + } + +} + diff --git a/WIN32LIB/MISC/DELAY.CPP b/WIN32LIB/MISC/DELAY.CPP new file mode 100644 index 0000000..bbbb437 --- /dev/null +++ b/WIN32LIB/MISC/DELAY.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : DELAY.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : 27 March, 1991 [CY] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include + +void Delay(int duration) +{ + unsigned long count; + TimerClass timer(BT_SYSTEM,TRUE); + + while (duration--) { + count = timer.Time() + 1L; + while (count >= timer.Time()) { + ; + } + } + +#if(FALSE) + while (duration--) + Wait_Vert_Blank(VertBlank); +#endif +} + +#if(FALSE) +void Vsync() +{ + Wait_Vert_Blank(VertBlank); +} +#endif + \ No newline at end of file diff --git a/WIN32LIB/MISC/DETPROC.ASM b/WIN32LIB/MISC/DETPROC.ASM new file mode 100644 index 0000000..0cdbd9c --- /dev/null +++ b/WIN32LIB/MISC/DETPROC.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/detproc.asm 1.1 1994/04/18 09:13:53 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PROC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 11, 1993 * +;* * +;* Last Update : May 11, 1993 [JLB] * +;* * +;* Converted to 32Bit -- JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Processor :NEAR + + +PROC_80386 equ 0 +PROC_80486 equ 1 +PROC_80586 equ 2 + +DATASEG +cpu_id_586 dw 0 + +CODESEG + +PROC Processor C near + USES ebx + LOCAL ptype:WORD + + pushfd + +; At least a 386 -- check for 486. + mov [WORD PTR ptype],PROC_80386 ; 80386 + pushfd + pop eax + mov ebx,eax + xor eax,40000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 486 -- check for 586(Pentium) + mov [ptype],PROC_80486 ; 80486 + + ; Some machines have a problem with this fLAG + ; and thus make us think they are a 586 but they are + ; really a 486. A possible way around this is to + ; capture the Illegal instruction vector, then do + ; an instruction only available on the 586. + + ; for now this is just commented out + pushfd + pop eax + mov ebx,eax + xor eax,200000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 586(Pentium) -- check for higher. + mov [ptype],PROC_80586 ; 80486 +; mov eax,1 +; DW 0fA2h ; CPUID opcode. +; shr ax,8 +; and ax,0fh +; inc ax +; inc ax +; mov [cpu_id_586],ax + +; Final cleanup and exit. +??fini: + popfd + sub eax,eax + mov ax,[ptype] + ret + +ENDP Processor + +END \ No newline at end of file diff --git a/WIN32LIB/MISC/EXIT.CPP b/WIN32LIB/MISC/EXIT.CPP new file mode 100644 index 0000000..2af49ad --- /dev/null +++ b/WIN32LIB/MISC/EXIT.CPP @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library source * + * * + * File Name : EXIT.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Exit -- Exit routine with message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "misc.h" + +#include + +#include +#include +#include + + + + +/*************************************************************************** + * EXIT -- Exit routine with message. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/03/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Exit(INT errorval, const char *message, ...) +{ + va_list argptr; + char errstring[256]; + + Prog_End(); + + if (message && *message) { + va_start (argptr, message); + vsprintf ((char *)errstring, (const char *)message, argptr); + va_end (argptr); + printf(errstring); + } + + ::exit(errorval); + +} + +void randomize ( void ) +{ + srand ( time ( NULL ) ) ; +} + +#if(0) +unsigned long random ( unsigned long mod ) +{ + return rand () * mod / RAND_MAX ; +} +#endif diff --git a/WIN32LIB/MISC/FACING16.ASM b/WIN32LIB/MISC/FACING16.ASM new file mode 100644 index 0000000..6e05705 --- /dev/null +++ b/WIN32LIB/MISC/FACING16.ASM @@ -0,0 +1,148 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./facing16.asm 1.10 1994/05/20 15:32:36 joe_bostic Exp $ +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +; long Desired_Facing16(long x1, long y1, long x2, long y2); + + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END + + + \ No newline at end of file diff --git a/WIN32LIB/MISC/FACING8.ASM b/WIN32LIB/MISC/FACING8.ASM new file mode 100644 index 0000000..b20ae3c --- /dev/null +++ b/WIN32LIB/MISC/FACING8.ASM @@ -0,0 +1,140 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +; long Desired_Facing8(long x1, long y1, long x2, long y2); + + PROC Desired_Facing8 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 ; Close to major axis bit. + + xor eax,eax + mov al,[NewFacing8+ebx] + + ; Normalize to 0..FF range. + shl eax,5 + + ret + + ENDP Desired_Facing8 + + + END diff --git a/WIN32LIB/MISC/FACINGFF.ASM b/WIN32LIB/MISC/FACINGFF.ASM new file mode 100644 index 0000000..e9bff57 --- /dev/null +++ b/WIN32LIB/MISC/FACINGFF.ASM @@ -0,0 +1,165 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACINGFF.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing256 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing256 :NEAR +; INCLUDE "wwlib.i" +INCLUDE "..\include\gbuffer.inc" + + CODESEG + +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ +; LONG cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) + PROC Desired_Facing256 C near + USES ebx, ecx, edx + + ARG srcx:DWORD + ARG srcy:DWORD + ARG dstx:DWORD + ARG dsty:DWORD + + xor ebx,ebx ; Facing number. + + ; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short ??xnotneg + neg ecx + mov ebx,11000000b ; Set bit 7 and 6 for leftward. +??xnotneg: + + ; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ??ynotneg + xor ebx,01000000b ; Complement bit 6 for downward. + neg eax +??ynotneg: + + ; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + ; Determine if the direction is closer to the Y axis and make sure that + ; CX holds the larger of the two deltas. This is in preparation for the + ; divide. + cmp eax,ecx + jb short ??gotaxis + xchg eax,ecx + xor edx,01000000b ; Closer to Y axis so make DX=64 for quad 0 and 2. +??gotaxis: + + ; If closer to the X axis then add 64 for quadrants 0 and 2. If + ; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + ; add value is in DX and save on stack. + push edx + + ; Make sure that the division won't overflow. Reduce precision until + ; the larger number is less than 256 if it appears that an overflow + ; will occur. If the high byte of the divisor is not zero, then this + ; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short ??nooverflow +??again: + test ecx,0FFFFFF00h + jz short ??nooverflow + shr ecx,1 + shr eax,1 + jmp short ??again +??nooverflow: + + ; Make sure that the division won't underflow (divide by zero). If + ; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short ??nounderflow + mov eax,0FFFFFFFFh + jmp short ??divcomplete + + ; Derive a pseudo angle number for the octant. The angle is based + ; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +??nounderflow: + xor edx,edx + shld edx,eax,8 ; shift high byte of eax into dl + shl eax,8 + div ecx +??divcomplete: + + ; Integrate the 5 most significant bits into the angle index. If DX + ; is not zero, then it is 64. This means that the dividend must be negated + ; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short ??noneg + dec edx + neg eax +??noneg: + add eax,edx + add eax,ebx + and eax,0FFH + ret + + ENDP Desired_Facing256 + + + + END \ No newline at end of file diff --git a/WIN32LIB/MISC/FADING.ASM b/WIN32LIB/MISC/FADING.ASM new file mode 100644 index 0000000..854d09a --- /dev/null +++ b/WIN32LIB/MISC/FADING.ASM @@ -0,0 +1,215 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini + cmp [dest],0 + je ??fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 +??innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short ??notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne ??mainloop + +??fini: + mov eax,[dest] + ret + + ENDP Build_Fading_Table + + + END \ No newline at end of file diff --git a/WIN32LIB/MISC/FINDARGV.CPP b/WIN32LIB/MISC/FINDARGV.CPP new file mode 100644 index 0000000..3ac4fea --- /dev/null +++ b/WIN32LIB/MISC/FINDARGV.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/misc/rcs/findargv.cpp 1.2 1994/04/22 10:29:28 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : findargv * + * * + * File Name : findargv.C * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Argv -- Checks to see if string is in arguement * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "wwstd.h" +#include +#include +#include +#include + + + + +/*************************************************************************** + * Find_Argv -- Checks to see if string is in arguement * + * * + * INPUT: char *str - string to search for. * + * * + * OUTPUT: NULL if not found else pointer to string. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/14/1993 SB : Created. * + *=========================================================================*/ + +//static char command [ 256 ] ; +#pragma on (argsused) +char * __cdecl Find_Argv(char const) +{ + return (NULL); + + +#ifdef NOT_FOR_WIN95 +char * __cdecl Find_Argv(char const *str) +{ + char * ptr ; + static startup_flag = 0 ; + + if ( ! startup_flag ) + { + startup_flag = 1 ; + getcmd ( command ) ; + } + + if ( ! strlen(str) ) return NULL ; + return strstr ( command , str ) ; +#endif +} + + + + + + diff --git a/WIN32LIB/MISC/IRANDOM.CPP b/WIN32LIB/MISC/IRANDOM.CPP new file mode 100644 index 0000000..0bfff25 --- /dev/null +++ b/WIN32LIB/MISC/IRANDOM.CPP @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "misc.h" + + + + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + diff --git a/WIN32LIB/MISC/LIB.CPP b/WIN32LIB/MISC/LIB.CPP new file mode 100644 index 0000000..2ed05b5 --- /dev/null +++ b/WIN32LIB/MISC/LIB.CPP @@ -0,0 +1,217 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./lib.c 1.16 1994/05/20 15:34:33 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Routines * + * * + * File Name : LIB.C * + * * + * Programmer : Scott Bowen * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Add_Long_To_Pointer -- Pointer arithmatic when pointer could be XMS. * + * Find_Argv -- Checks to see if string is in arguement * + * Mono_Mem_Dump -- Dumps memory to mono monitor with hex and char. * + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include "misc.h" + +//PRIVATE unsigned Divide_With_Round(unsigned num, unsigned den); + + +/*************************************************************************** + * Divide_With_Round -- Divides integers and round to nearest integer. * + * * + * INPUT: int numberator. * + * int denominator. * + * * + * OUTPUT: Returns value rounded. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/13/1992 SB : Created. * + *=========================================================================*/ +static unsigned Divide_With_Round(unsigned num, unsigned den) +{ + // return num/den + (0 ro 1). 1 if the remainder is more than half the denominator. + return( (num / den) + (unsigned)((num % den) >= ((den + 1) >> 1)) ); +} + +#define HSV_BASE 255 // This is used to get a little better persion on HSV conversion. +#define RGB_BASE 63 // Not 64, this is really the max value. + + +/*************************************************************************** + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * * + * INPUT: int r,g, and b values. * + * int *h, *s, and *v pointers. * + * * + * OUTPUT: Assigns values to *h, *s, and *v. * + * * + * WARNINGS: The reason we use a different base for HSV then RGB is * + * because we loose alot of persision by not using floating * + * point. Using the same base value (63) made it so that * + * about 50% of the time one RGB value would be one different * + * then the original if you went from RGB to HSV to RGB. * + * Using 255 drop it down to about 9% of the time we get an * + * off value. To get it perfect, we would have to make the * + * HSV base larger - but then you need to do all calculations * + * in long instead of unsigned int. * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v) +{ + unsigned int m, r1, g1, b1, tmp; + + // Convert RGB base to HSV base. + r = Divide_With_Round((r * HSV_BASE), RGB_BASE); + g = Divide_With_Round((g * HSV_BASE), RGB_BASE); + b = Divide_With_Round((b * HSV_BASE), RGB_BASE); + + // Set hue to default. + *h = 0; + + // Set v = Max(r,g,b) to find dominant primary color. + *v = (r > g) ? r : g; + if (b > *v) *v = b; + + // Set m = min(r,g,b) to find amount of white. + m = (r < g) ? r : g; + if (b < m) m = b; + + // Determine the normalized saturation. + if (*v != 0) { + *s = Divide_With_Round( (*v - m) * HSV_BASE ,*v); + } else { + *s = 0; + } + + if (*s != 0) { + tmp = *v - m; + r1 = Divide_With_Round( (*v - r) * HSV_BASE, tmp); + g1 = Divide_With_Round( (*v - g) * HSV_BASE, tmp); + b1 = Divide_With_Round( (*v - b) * HSV_BASE, tmp); + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if ((*v) == r) { + if (m == g) { + *h = 5 * HSV_BASE + b1; + } else { + *h = 1 * HSV_BASE - g1; + } + } else { + if ((*v) == g) { + if (m == b) { + *h = 1 * HSV_BASE + r1; + } else { + *h = 3 * HSV_BASE - b1; + } + } else { + // *v == b + if (m == r) { + *h = 3 * HSV_BASE + g1; + } else { + + *h = 5 * HSV_BASE - r1; + } + } + } + + // Divide by six and round. + *h = Divide_With_Round(*h, 6); + } +} + +/*************************************************************************** + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * * + * INPUT: int h,s, and v coordinates * + * int *r, *g, and *b pointers. * + * * + * OUTPUT: Assigns values to *r, *g, and *b. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b) +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Tempary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + + h *= 6; + f = h % HSV_BASE; + + // Set up possible red, green and blue values. + values[1] = + values[2] = v; + + // + // The following lines of code change + // values[3] = (v * (HSV_BASE - ( (s * f) / HSV_BASE) )) / HSV_BASE; + // values[4] = values[5] = (v * (HSV_BASE - s)) / HSV_BASE; + // values[6] = (v * (HSV_BASE - (s * (HSV_BASE - f)) / HSV_BASE)) / HSV_BASE; + // so that the are rounded divides. + // + + tmp = Divide_With_Round(s * f, HSV_BASE); + values[3] = Divide_With_Round(v * (HSV_BASE - tmp), HSV_BASE); + + values[4] = + values[5] = Divide_With_Round(v * (HSV_BASE - s), HSV_BASE); + + tmp = HSV_BASE - Divide_With_Round(s * (HSV_BASE - f), HSV_BASE); + values[6] = Divide_With_Round(v * tmp, HSV_BASE); + + + // This should not be rounded. + i = h / HSV_BASE; + + i += (i > 4) ? -4 : 2; + *r = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *b = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *g = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); +} diff --git a/WIN32LIB/MISC/MAKEFILE b/WIN32LIB/MISC/MAKEFILE new file mode 100644 index 0000000..5f4a565 --- /dev/null +++ b/WIN32LIB/MISC/MAKEFILE @@ -0,0 +1,196 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = misc +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + crc.obj & + ddraw.obj & + delay.obj & + detproc.obj & + facing8.obj & + facing16.obj & + facingFF.obj & + fading.obj & + findargv.obj & + irandom.obj & + lib.obj & + opsys.obj & + random.obj & + reverse.obj & + shakescr.obj & + cliprect.obj & + exit.obj & + version.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + make + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/MISC/MAKEFILE.BOR b/WIN32LIB/MISC/MAKEFILE.BOR new file mode 100644 index 0000000..07024ee --- /dev/null +++ b/WIN32LIB/MISC/MAKEFILE.BOR @@ -0,0 +1,205 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = misc +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + crc.obj \ + ddraw.obj \ + delay.obj \ + detproc.obj \ + facing8.obj \ + facing16.obj \ + facingFF.obj \ + fading.obj \ + findargv.obj \ + irandom.obj \ + lib.obj \ + opsys.obj \ + random.obj \ + reverse.obj \ + shakescr.obj \ + exit.obj \ + cliprect.obj \ + version.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) wwlib32.h + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-crc.obj & ++-ddraw.obj & ++-delay.obj & ++-detproc.obj & ++-exit.obj & ++-facing8.obj & ++-facing16.obj & ++-facingFF.obj & ++-fading.obj & ++-findargv.obj & ++-irandom.obj & ++-lib.obj & ++-opsys.obj & ++-random.obj & ++-reverse.obj & ++-shakescr.obj & ++-cliprect.obj & ++-version.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/MISC/MAKEFILE.WAT b/WIN32LIB/MISC/MAKEFILE.WAT new file mode 100644 index 0000000..147f684 --- /dev/null +++ b/WIN32LIB/MISC/MAKEFILE.WAT @@ -0,0 +1,196 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = misc +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + crc.obj & + ddraw.obj & + delay.obj & + detproc.obj & + facing8.obj & + facing16.obj & + facingFF.obj & + fading.obj & + findargv.obj & + irandom.obj & + lib.obj & + opsys.obj & + random.obj & + reverse.obj & + shakescr.obj & + cliprect.obj & + exit.obj & + version.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + make + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/MISC/MISC.H b/WIN32LIB/MISC/MISC.H new file mode 100644 index 0000000..3f39969 --- /dev/null +++ b/WIN32LIB/MISC/MISC.H @@ -0,0 +1,272 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +#define WIN32_LEAN_AND_MEAN // eliminates unecessary definitions in windows.h +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include + +extern LPDIRECTDRAWSURFACE PaletteSurface; + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: DDRAW.CPP */ +/*=========================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg); +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +unsigned Get_Free_Video_Memory(void); +void Wait_Blit(void); +unsigned Get_Video_Hardware_Capabilities(void); + +extern "C" void Wait_Vert_Blank(void); +extern "C" void Set_DD_Palette (void *palette); + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void); +/* +** Pointer to function to call if we detect a surface restore +*/ +extern void (*Misc_Focus_Restore_Function)(void); + + +/* + * Flags returned by Get_Video_Hardware_Capabilities + */ +/* Hardware blits supported? */ +#define VIDEO_BLITTER 1 + +/* Hardware blits asyncronous? */ +#define VIDEO_BLITTER_ASYNC 2 + +/* Can palette changes be synced to vertical refresh? */ +#define VIDEO_SYNC_PALETTE 4 + +/* Is the video cards memory bank switched? */ +#define VIDEO_BANK_SWITCHED 8 + +/* Can the blitter do filled rectangles? */ +#define VIDEO_COLOR_FILL 16 + +/* Is there no hardware assistance avaailable at all? */ +#define VIDEO_NO_HARDWARE_ASSIST 32 + + + +/* + * Definition of surface monitor class + * + * This class keeps track of all the graphic buffers we generate in video memory so they + * can be restored after a focus switch. +*/ + +#define MAX_SURFACES 20 + +class SurfaceMonitorClass { + + public: + + SurfaceMonitorClass(); + + void Add_DD_Surface (LPDIRECTDRAWSURFACE); + void Remove_DD_Surface (LPDIRECTDRAWSURFACE); + BOOL Got_Surface_Already (LPDIRECTDRAWSURFACE); + void Restore_Surfaces (void); + void Set_Surface_Focus ( BOOL in_focus ); + void Release(void); + + BOOL SurfacesRestored; + + private: + + LPDIRECTDRAWSURFACE Surface[MAX_SURFACES]; + BOOL InFocus; + +}; + +extern SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces + + +/*=========================================================================*/ +/* The following variables are declared in: DDRAW.CPP */ +/*=========================================================================*/ +extern LPDIRECTDRAW DirectDrawObject; +extern LPDIRECTDRAW2 DirectDraw2Interface; +extern HWND MainWindow; +extern BOOL SystemToVideoBlits; +extern BOOL VideoToSystemBlits; +extern BOOL SystemToSystemBlits; +extern BOOL OverlappedVideoBlits; // Can video driver blit overlapped regions? + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID __cdecl Prog_End(VOID); +VOID __cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +unsigned char __cdecl Random(void); +int __cdecl Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void __cdecl Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +long __cdecl Reverse_Long(long number); +short __cdecl Reverse_Short(short number); +long __cdecl Swap_Long(long number); +#if (0) +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ +#endif + +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long __cdecl Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD __cdecl Processor(void); +extern WORD __cdecl Operating_System(void); +extern unsigned long random ( unsigned long mod ) ; +//extern void randomize ( void ) ; + +extern int __cdecl Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int __cdecl Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + diff --git a/WIN32LIB/MISC/OPSYS.ASM b/WIN32LIB/MISC/OPSYS.ASM new file mode 100644 index 0000000..f2bfeaa --- /dev/null +++ b/WIN32LIB/MISC/OPSYS.ASM @@ -0,0 +1,150 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/opsys.asm 1.1 1994/04/18 09:14:12 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Operating System Flags * +;* * +;* File Name : OPSYS.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : January 26, 1993 * +;* * +;* Last Update : January 26, 1993 [SB] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Operating_System -- Determines what the operating system is. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Operating_System :NEAR +GLOBAL C OperatingSystem :WORD + +DOS equ 1 +WIN31STD equ 2 +WIN31ENH equ 3 +WIN30ENH equ 4 +WIN30STD equ 5 +WIN30REAL equ 6 + +DATASEG + +OperatingSystem dw 0 + + +CODESEG + +;*************************************************************************** +;* Operating_System -- Determines what the operating system is. * +;* * +;* INPUT: NONE. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/26/1993 SB : Created. * +;*=========================================================================* +PROC Operating_System C near + USES ebx,ecx,edx,es,edi + + + ; Check for Windows 3.1 + mov eax,160Ah ; WIN31CHECK + int 2fh + or ax,ax + jz short RunningUnderWin31 + + ;check for Windows 3.0 enhanced mode + mov eax,1600h ; WIN386CHECK + int 2fh + mov bl,al + mov eax,WIN30ENH + test bl,7fh + jnz short Exit + + ;check for 3.0 WINOLDAP + mov eax,4680h ; IS_WINOLDAP_ACTIVE + int 2fh + or eax,eax + jnz short NotRunningUnderWin + + ; rule out MS-DOS 5.0 task switcher + mov eax,4b02h ; detect switcher + push ebx + push es + push edi + xor ebx,ebx + mov edi,ebx + mov es,bx + int 2fh + pop edi + pop es + pop ebx + or eax,eax + jz short NotRunningUnderWin ; MS-DOS 5.0 task switcher found. + + ; check for standrd mode Windows 3.0 + mov eax,1605h ;PMODE_START + int 2fh + mov eax,WIN30STD + cmp ecx,-1 + jz short Exit + + ;check for real mode Windows 3.0 + mov eax,1606h ; PMODE_STOP + int 2fh + mov eax,WIN30REAL + jmp SHORT Exit + +RunningUnderWin31: + ; At this point: CX == 3 means Windows 3.1 enhanced mode. + ; CX == 2 means Windows 3.1 standard mode. + mov eax,WIN31STD + cmp ecx,2 + je short Exit + + mov eax,WIN31ENH + jmp SHORT Exit + +NotRunningUnderWin: + mov eax,DOS + +Exit: + mov [WORD PTR OperatingSystem], ax + ret + +ENDP Operating_System + + + +;---------------------------------------------------------------------------- + +END \ No newline at end of file diff --git a/WIN32LIB/MISC/RANDOM.ASM b/WIN32LIB/MISC/RANDOM.ASM new file mode 100644 index 0000000..87170e9 --- /dev/null +++ b/WIN32LIB/MISC/RANDOM.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : RANDOM.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* UBYTE Random(VOID); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +Global C Random :NEAR +Global C Get_Random_Mask :NEAR +Global C RandNumb :DWORD + + DATASEG + +RandNumb DD 12349876H + + CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END diff --git a/WIN32LIB/MISC/REVERSE.ASM b/WIN32LIB/MISC/REVERSE.ASM new file mode 100644 index 0000000..29677f2 --- /dev/null +++ b/WIN32LIB/MISC/REVERSE.ASM @@ -0,0 +1,139 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/reverse.asm 1.3 1994/04/25 12:22:45 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : REVERSE.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : April 20, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; LONG Reverse_Long(LONG number); * +; WORD Reverse_Short(WORD number); * +; LONG Swap_LONG(LONG number); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Reverse_Short :NEAR +GLOBAL C Swap_Long :NEAR +GLOBAL C Reverse_Long :NEAR + +CODESEG + + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; LONG Reverse_LONG(LONG number); +; WORD Reverse_Short(WORD number); +; LONG Swap_LONG(LONG number); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_LONG +; +; LONG Reverse_LONG(LONG number); +; +;* + PROC Reverse_Long C near + ARG number:DWORD + +IF 1 + mov eax,[DWORD PTR number] + xchg al,ah + ror eax,16 + xchg al,ah +ELSE + + ; This is old 16 bit code. + mov ax,[WORD PTR number] + mov dx,[WORD PTR number+2] + xchg ah,dl + xchg al,dh +ENDIF + + ret + + ENDP Reverse_Long + +;----------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_WORD +; +; WORD Reverse_Short(WORD number); +; +;* + PROC Reverse_Short C near + ARG number:WORD + + mov ax,[number] + xchg ah,al + ret + + ENDP Reverse_Short + +;----------------------------------------------------------------- + + +;----------------------------------------------------------------- +; +; SWAP_Long +; +; Long Swap_Long(Long number); +; +;* + PROC Swap_Long C near + ARG number:DWORD + +IF 1 + ; 32 bit code. + mov eax,[DWORD PTR number] + ror eax,16 +ELSE + ; 16 bit code. + mov ax,[WORD PTR number+2] + mov dx,[WORD PTR number] +ENDIF + + ret + + + ENDP Swap_Long + +;----------------------------------------------------------------- + + END + diff --git a/WIN32LIB/MISC/SHAKESCR.ASM b/WIN32LIB/MISC/SHAKESCR.ASM new file mode 100644 index 0000000..7b413bd --- /dev/null +++ b/WIN32LIB/MISC/SHAKESCR.ASM @@ -0,0 +1,158 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : SHAKESCR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 19, 1993 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + ret + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END diff --git a/WIN32LIB/MISC/VERSION.CPP b/WIN32LIB/MISC/VERSION.CPP new file mode 100644 index 0000000..9cb40aa --- /dev/null +++ b/WIN32LIB/MISC/VERSION.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VERSION.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : July 26, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Version -- Returns with current library version text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + + + +PRIVATE char *version = "Westwood Studios - 32 Bit Library Version " + __DATE__ + "\r\n"; + +/*************************************************************************** + * VERSION -- Returns with current library version text. * + * * + * Use this routine to determine the current library version. The * + * version text contains the date that it was created. * + * * + * INPUT: none * + * * + * OUTPUT: Returns pointer to version text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1991 JLB : Created. * + *=========================================================================*/ +char * __cdecl Version(void) +{ + return(version); +} diff --git a/WIN32LIB/MISC/WWLIB32.H b/WIN32LIB/MISC/WWLIB32.H new file mode 100644 index 0000000..829e653 --- /dev/null +++ b/WIN32LIB/MISC/WWLIB32.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H + + diff --git a/WIN32LIB/MISC/WWSTD.H b/WIN32LIB/MISC/WWSTD.H new file mode 100644 index 0000000..89e047a --- /dev/null +++ b/WIN32LIB/MISC/WWSTD.H @@ -0,0 +1,338 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// +// Win 95 includes +// + +#ifndef WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 1 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define WW_ERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +#pragma option -Jg +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +int ABS(int); +long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +short MIN(short, short); +int MIN(int, int); +long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +short MAX(short, short); +int MAX(int, int); +long MAX(long, long); +#pragma option -Jgd + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +//#define true 1 +//#define false 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. +#if(0) +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif +#endif + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif diff --git a/WIN32LIB/MONO/MAKEFILE b/WIN32LIB/MONO/MAKEFILE new file mode 100644 index 0000000..c14337f --- /dev/null +++ b/WIN32LIB/MONO/MAKEFILE @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mono +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + mono.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/MONO/MAKEFILE.BOR b/WIN32LIB/MONO/MAKEFILE.BOR new file mode 100644 index 0000000..b7f828f --- /dev/null +++ b/WIN32LIB/MONO/MAKEFILE.BOR @@ -0,0 +1,169 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mono +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + mono.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+mono.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/MONO/MAKEFILE.WAT b/WIN32LIB/MONO/MAKEFILE.WAT new file mode 100644 index 0000000..3e7716a --- /dev/null +++ b/WIN32LIB/MONO/MAKEFILE.WAT @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mono +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + mono.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/MONO/MONO.CPP b/WIN32LIB/MONO/MONO.CPP new file mode 100644 index 0000000..b255d20 --- /dev/null +++ b/WIN32LIB/MONO/MONO.CPP @@ -0,0 +1,789 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.cpv 2.12 06 Sep 1995 16:37:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#pragma inline +#include "mono.h" + +#include +#include +#include +#include +#include +#include + + +extern void output(short port, short data); +#pragma aux output parm [dx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" + +int MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES] = {0,0,0,0,0,0,0,0}; +void * MonoClass::MonoSegment = (void*)0x000b0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) +{ + int index; + + Attrib = DEFAULT_ATTRIBUTE; // Normal text color. + X = Y = 0; + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, char attrib, BoxStyleType thick) +{ + CellType cell; + char oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Store_Cell(cell, x+xpos+1, y); + cell.Character = CharData[thick].BottomEdge; + Store_Cell(cell, x+xpos+1, y+h-1); + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Store_Cell(cell, x, y+ypos+1); + cell.Character = CharData[thick].RightEdge; + Store_Cell(cell, x+w-1, y+ypos+1); + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Store_Cell(cell, x, y); + cell.Character = CharData[thick].UpperRight; + Store_Cell(cell, x+w-1, y); + cell.Character = CharData[thick].BottomRight; + Store_Cell(cell, x+w-1, y+h-1); + cell.Character = CharData[thick].BottomLeft; + Store_Cell(cell, x, y+h-1); + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ +#if(0) + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + _DX = CONTROL_PORT; + _AX = (short)(0x0E|(pos&0xFF00)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + _DX = CONTROL_PORT; + _AX = (short)(0x0F|(pos<<8)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + } +#else + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + output(CONTROL_PORT, + (short)0x0E|(short)(pos&0xFF00)); + output(CONTROL_PORT, + (short)0x0F|(short)(pos<<8)); + } +#endif +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + CellType cell; + + if (!Enabled) return; + + Set_Cursor(0, 0); + + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int y = 0; y < LINES; y++) { + for (int x = 0; x < COLUMNS; x++) { + Store_Cell(cell, x, y); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + CellType cell; + + if (!Enabled || lines <= 0) return; + + memmove( (void*)((long)MonoSegment + Offset(0, 0)), + (void*)((long)MonoSegment + Offset(0, lines)), + (LINES-lines)*COLUMNS*sizeof(CellType)); + + + Y--; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int l = LINES-lines; l < LINES; l++) { + for (int index = 0; index < COLUMNS; index++) { + Store_Cell(cell, index, l); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +#ifdef NEVER +void MonoClass::Printf(int text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} +#endif + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const *ptr) +{ + char startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; + while (*text) { + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (*text) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + X = startcol; + Y++; + Scroll(Y-(LINES-1)); + break; + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + X = 0; + Y++; + Scroll(Y-(LINES-1)); + break; + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge is it moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + cell.Character = *text; + Store_Cell(cell, X, Y); + + X++; + if (X >= COLUMNS) { + X = 0; + Y++; + + if (Y > (LINES-1)) { + Scroll(Y-(LINES-1)); + } + } + break; + + } + text++; + } + + Set_Cursor(X, Y); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + +#ifdef NEVER +void MonoClass::Text_Print(int text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + if (text != TXT_NONE) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} +#endif + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memcpy((void*)((long)MonoSegment + src.Offset(0, 0)), (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + MonoClass *displace; // The page that is being displaced. + + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + displace = Get_Current(); + if (displace) { + char temp[SIZE_OF_PAGE]; + + memcpy(&temp[0], MonoSegment, SIZE_OF_PAGE); + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + memcpy((void*)((long)MonoSegment + Offset(0, 0)), &temp[0], SIZE_OF_PAGE); + + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (char)attrib); + } +} + +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (char)attrib, (MonoClass::BoxStyleType)thick); + } +} + +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + + +#ifdef NEVER +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} +#endif + diff --git a/WIN32LIB/MONO/MONO.H b/WIN32LIB/MONO/MONO.H new file mode 100644 index 0000000..07a991f --- /dev/null +++ b/WIN32LIB/MONO/MONO.H @@ -0,0 +1,185 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.16 06 Sep 1995 16:29:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif + diff --git a/WIN32LIB/MOVIE/MAKE.BAT b/WIN32LIB/MOVIE/MAKE.BAT new file mode 100644 index 0000000..4a3199f --- /dev/null +++ b/WIN32LIB/MOVIE/MAKE.BAT @@ -0,0 +1,2 @@ +SET WATCOM=D:\RADVD\SOURCE\WAT11 +..\..\code\wmake diff --git a/WIN32LIB/MOVIE/MAKEFILE b/WIN32LIB/MOVIE/MAKEFILE new file mode 100644 index 0000000..0ddfa85 --- /dev/null +++ b/WIN32LIB/MOVIE/MAKEFILE @@ -0,0 +1,122 @@ +# +# 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 . +# + +#* $Header$ +#*********************************************************************************************** +#*** 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 * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : 03/02/95 * +#* * +#* Last Update : March 2, 1995 [JLB] * +#* * +#*---------------------------------------------------------------------------------------------* +#* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +.SILENT +.OPTIMIZE +.ERASE + +#--------------------------------------------------------------------------- +# Working values depending on what target executable will be created. +#--------------------------------------------------------------------------- +OBJ=obj +CC=..\..\WAT11\BINNT\wpp386 +LIB=..\..\WATCOM\BINNT\wlib + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.cpp: . +.h: . +.obj: $(OBJ) + + +#=========================================================================== +# Compiler and assembler flags. +#=========================================================================== +CC_CFG = /d0 # No debugging information +CC_CFG += /DWIN32=1 +CC_CFG += /D_WIN32 +CC_CFG += /i=..\..\dxmedia\include +CC_CFG += /i=..\..\dxmedia\classes\base +CC_CFG += /i=..\..\dxsdk\inc +CC_CFG += /i=..\..\wat11\h\nt # NT include directory. +CC_CFG += /i=..\..\wat11\H # Normal Watcom include directory. +CC_CFG += /bt=NT +CC_CFG += /otxan +CC_CFG += /5r # Pentium optimized register calling conventions. +CC_CFG += /of+ # Generate traceable stack frames. +CC_CFG += /zp1 # Pack structures on byte boundary. +CC_CFG += /s # Remove stack check calls. +CC_CFG += /j # char is now signed. +CC_CFG += /fhq +CC_CFG += /we # Treat all warnings as errors. +CC_CFG += /w8 # Most warnings enabled. +CC_CFG += /ri # char and shorts are returned as int. +CC_CFG += /zq # Operate quietly. + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: .AUTODEPEND + echo Compiling $< + *$(CC) $(CC_CFG) -fo$(OBJ)\$^. $< + +.cpp.obj: .AUTODEPEND + echo Compiling $< + *$(CC) $(CC_CFG) -fo$(OBJ)\$^. $< + +#--------------------------------------------------------------------------- +# Object modules. +#--------------------------------------------------------------------------- +OBJECTS = & + movie.obj + +############################################################################ +# If there is an abnormal termination in make process (e.g., error in compile). +.ERROR + + +############################################################################# +# Default target +all: mpeg.lib + + +############################################################################# +# Builds the MPEG.LIB file. +mpeg.lib: $(OBJECTS) mpeg.lnk + $(LIB) -c $^@ @mpeg.lnk + +mpeg.lnk: makefile + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ -+ $(OBJ)\%index diff --git a/WIN32LIB/MOVIE/MOVIE.CPP b/WIN32LIB/MOVIE/MOVIE.CPP new file mode 100644 index 0000000..fa14671 --- /dev/null +++ b/WIN32LIB/MOVIE/MOVIE.CPP @@ -0,0 +1,329 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* FILE +* Movie.cpp +* +* DESCRIPTION +* Movie playback using DirectShow Multimedia streaming and DirectDraw +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 27, 1998 +* +****************************************************************************/ + +#include "movie.h" +#include +#include +#include +#include +#include + +// We declare these variables global because RA doesn't like the DirectXMedia +// header files. +IDirectDraw* gDDraw = NULL; +IGraphBuilder* gGB = NULL; +IMultiMediaStream* gMMStream = NULL; +IMediaStream* gVideoStream = NULL; +IDirectDrawMediaStream* gDDStream = NULL; +IDirectDrawStreamSample* gDDSample = NULL; + + +/**************************************************************************** +* +* NAME +* Movie - Constructor +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +Movie::Movie(IDirectDraw* dd) + { + mPlaying = false; + + Close(); + + gDDraw = dd; + + // Initialize COM library + CoInitialize(NULL); + } + + +/**************************************************************************** +* +* NAME +* ~Movie - Destructor +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +Movie::~Movie() + { + // Release all + Close(); + + // Release COM library + CoUninitialize(); + } + + +/**************************************************************************** +* +* NAME +* Open +* +* DESCRIPTION +* +* INPUTS +* Name - Name of movie +* +* RESULT +* +****************************************************************************/ + +bool Movie::Open(const char* name) + { + WCHAR wFile[MAX_PATH]; + IAMMultiMediaStream* amStream; + DDSURFACEDESC ddsd; + + Close(); + + //------------------------------------------------------------------------- + // CREATE FILTER GRAPH FOR FILE TYPE + //------------------------------------------------------------------------- + if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + IID_IGraphBuilder, (void **)&gGB))) + { + MessageBox(GetDesktopWindow(), "Couldn't create a CLSID_FilterGraph object.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + //------------------------------------------------------------------------- + // CREATE MULTIMEDIA STREAM + //------------------------------------------------------------------------- + if (FAILED(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, + CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void**)&amStream))) + { + Close(); + MessageBox(GetDesktopWindow(), "Couldn't create a CLSID_MultiMediaStream object.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + // Initialize stream for reading + if (FAILED(amStream->Initialize(STREAMTYPE_READ, 0, gGB))) + { + amStream->Release(); + Close(); + MessageBox(GetDesktopWindow(), "Couldn't initialize MultiMediaStream object.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + // We want video + if (FAILED(amStream->AddMediaStream(gDDraw, &MSPID_PrimaryVideo, 0, NULL))) + { + amStream->Release(); + Close(); + MessageBox(GetDesktopWindow(), "Couldn't add primary video stream.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + // We want audio + if (FAILED(amStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, + AMMSF_ADDDEFAULTRENDERER, NULL))) + { + amStream->Release(); + Close(); + MessageBox(GetDesktopWindow(), "Couldn't add primary audio stream.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + gMMStream = amStream; + + // Convert the filename + MultiByteToWideChar(CP_ACP,0,name,-1,wFile,sizeof(wFile)/sizeof(wFile[0])); + + if (FAILED(gGB->RenderFile(wFile, NULL))) + { + Close(); + MessageBox(GetDesktopWindow(), "Couldn't build filter graph.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + // Get video stream + if (FAILED(gMMStream->GetMediaStream(MSPID_PrimaryVideo, &gVideoStream))) + { + Close(); + MessageBox(GetDesktopWindow(), "Couldn't obtain video stream.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + if (FAILED(gVideoStream->QueryInterface(IID_IDirectDrawMediaStream, + (void **)&gDDStream))) + { + Close(); + MessageBox(GetDesktopWindow(), "Couldn't obtain video stream interface.\n", + "DirectMedia", MB_ICONSTOP|MB_OK); + return false; + } + + ddsd.dwSize = sizeof(ddsd); + gDDStream->GetFormat(&ddsd, NULL, NULL, NULL); + mHeight = ddsd.dwHeight; + mWidth = ddsd.dwWidth; + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +void Movie::Close(void) + { + // Stop currently playing movie + if ((gMMStream != NULL) && (mPlaying == true)) + { + gMMStream->SetState(STREAMSTATE_STOP); + mPlaying = false; + } + + // Release video sample + if (gDDSample) + { + gDDSample->Release(); + gDDSample = NULL; + } + + // Release DirectDrawMediaStream interface + if (gDDStream) + { + gDDStream->Release(); + gDDStream = NULL; + } + + // Release video stream + if (gVideoStream) + { + gVideoStream->Release(); + gVideoStream = NULL; + } + + // Release multimedia stream + if (gMMStream) + { + gMMStream->Release(); + gMMStream = NULL; + } + + // Release filter graph + if (gGB) + { + gGB->Release(); + gGB = NULL; + } + } + + +/**************************************************************************** +* +* NAME +* Play +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool Movie::Play(IDirectDrawSurface* surface) + { + RECT rect; + + rect.top = rect.left = 0; + rect.bottom = mHeight; + rect.right = mWidth; + + if (FAILED(gDDStream->CreateSample(surface, &rect, 0, &gDDSample))) + { + return false; + } + + gMMStream->SetState(STREAMSTATE_RUN); + mPlaying = true; + return true; + } + + +/**************************************************************************** +* +* NAME +* Update +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool Movie::Update(void) + { + // Update each frame + if (gDDSample->Update(0, NULL, NULL, 0) != S_OK) + { + gMMStream->SetState(STREAMSTATE_STOP); + return false; + } + + return true; + } diff --git a/WIN32LIB/MOVIE/MOVIE.H b/WIN32LIB/MOVIE/MOVIE.H new file mode 100644 index 0000000..be9d23e --- /dev/null +++ b/WIN32LIB/MOVIE/MOVIE.H @@ -0,0 +1,65 @@ +/* +** 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 . +*/ + +#ifndef _MOVIE_H_ +#define _MOVIE_H_ +/**************************************************************************** +* +* FILE +* Movie.h +* +* DESCRIPTION +* Movie playback using DirectShow Multimedia streaming and DirectDraw +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 27, 1998 +* +****************************************************************************/ + +#include + +class Movie + { + public: + Movie(IDirectDraw* dd); + ~Movie(); + + bool Open(const char* name); + bool Play(IDirectDrawSurface* surface); + bool Update(void); + void Close(void); + + bool IsPlaying(void) const + {return mPlaying;} + + DWORD GetWidth(void) const + {return mWidth;} + + DWORD GetHeight(void) const + {return mHeight;} + + protected: + DWORD mWidth; + DWORD mHeight; + bool mPlaying; + }; + +#endif // _MOVIE_H_ diff --git a/WIN32LIB/PALETTE/LOADPAL.CPP b/WIN32LIB/PALETTE/LOADPAL.CPP new file mode 100644 index 0000000..f9f53a0 --- /dev/null +++ b/WIN32LIB/PALETTE/LOADPAL.CPP @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Load_Palette * + * * + * File Name : LOADPAL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 27, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the file I/O system, * + * specifically Load_Data(). * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Palette -- Loads a palette file into the given palette buffer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "iff.h" +#include "palette.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +/*************************************************************************** + * Load_Palette -- Loads a palette file into the given palette buffer. * + * * + * INPUT: * + * BYTE * file_name - name of the file to load. * + * BYTE * palette_pointer - pointer to palette buffer. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer) +{ + #if(IBM) + Load_Data(palette_file_name, palette_pointer, 768); + #else + Load_Data(palette_file_name, palette_pointer, (ULONG)(2<. +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = palette +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + loadpal.obj & + morphpal.obj & + palette.obj & + pal.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/PALETTE/MAKEFILE.BOR b/WIN32LIB/PALETTE/MAKEFILE.BOR new file mode 100644 index 0000000..d39e78f --- /dev/null +++ b/WIN32LIB/PALETTE/MAKEFILE.BOR @@ -0,0 +1,174 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = palette +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + loadpal.obj \ + morphpal.obj \ + palette.obj \ + pal.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+loadpal.obj & +-+morphpal.obj & +-+palette.obj & +-+pal.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/PALETTE/MORPHPAL.CPP b/WIN32LIB/PALETTE/MORPHPAL.CPP new file mode 100644 index 0000000..1daffe5 --- /dev/null +++ b/WIN32LIB/PALETTE/MORPHPAL.CPP @@ -0,0 +1,176 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwlib32 * + * * + * File Name : PALTOPAL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : May 2, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Morph_Palette -- morphs a palette from source to destination * + * Palette_To_Palette -- morph src palette to a dst palette * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ +#define SCALE(a,b,c) (((((long)(a)<<8) / (long)(b) ) * (unsigned long)(c)) >>8) + + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE int __cdecl Palette_To_Palette(void *src_palette, void *dst_palette, unsigned long current_time, unsigned long delay); + + +/*************************************************************************** + * Morph_Palette -- morphs a palette from source to destination * + * * + * INPUT: * + * void *src_pal - starting palette * + * void *dst_pal - ending palette * + * unsigned int delay - time delay in 60ths of a second * + * void *callback - user-defined callback, NULL if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/02/1994 BR : Created. * + *=========================================================================*/ +void cdecl Morph_Palette (void *src_pal, void *dst_pal, unsigned int delay, + void (*callback) (void) ) +{ + int result; + unsigned long pal_start = TickCount.Time(); + extern void (*cb_ptr) ( void ) ; // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback ; + + /*===================================================================*/ + /* Make sure that we don't go too fast but also make sure we keep */ + /* processing the morph palette if we have one. */ + /*===================================================================*/ + while (1) { + if (src_pal && dst_pal) { + result = Palette_To_Palette (src_pal, dst_pal, + (TickCount.Time() - pal_start), (unsigned long)delay); + if (!result) + break; + + if (callback) { + (*cb_ptr)(); + } + } + } + + return; + +} /* end of Morph_Palette */ + + +/*************************************************************************** + * Palette_To_Palette -- morph src palette to a dst palette * + * * + * Creates & sets a palette that's in-between 'src_palette' & * + * 'dst_palette'; how close it is to dst_palette is based on how close * + * 'current_time' is to 'delay'. 'current_time' & 'delay' are based on * + * 0 being the start time. * + * * + * INPUT: void *src_palette = palette we want to morph from * + * void *dst_palette = palette we want to morph to * + * long current_time = time we started morph pal * + * long delay = time we want the morph to take* + * * + * OUTPUT: int if the time had elapsed and no chages were * + * necessary this routine returns FALSE * + * otherwise it will always return TRUE (this * + * was necessary to detect the end of the ice * + * effect. * + * * + * HISTORY: * + * 05/24/1993 MC : Created. * + *=========================================================================*/ +PRIVATE int cdecl Palette_To_Palette(void *src_palette, void *dst_palette, + unsigned long current_time, unsigned long delay) +{ + char colour; + char diff; + int chgval; + int lp; + int change; + static char palette[768]; + char *src_pal = (char*)src_palette; + char *dst_pal = (char*)dst_palette; + + /*======================================================================*/ + /* Loop through each RGB value attempting to change it to the correct */ + /* color. */ + /*======================================================================*/ + for (change = lp = 0; lp < 768; lp++) { + if (current_time < delay ) { + diff = dst_pal[lp] & (char)63; + diff -= src_pal[lp] & (char)63; + if (diff) + change = TRUE; + chgval = SCALE(diff, delay, current_time); + colour = src_pal[lp] & (char)63; + colour +=(char)chgval; + } + else { + colour = dst_pal[lp] & (char)63; + change = FALSE; + } + palette[lp] = colour; + } + /*======================================================================*/ + /* Set the palette to the color that we created. */ + /*======================================================================*/ + Set_Palette(palette); + return(change); + +} /* end of Palette_To_Palette */ + + +/*************************** End of morphpal.cpp ***************************/ + + diff --git a/WIN32LIB/PALETTE/PAL.ASM b/WIN32LIB/PALETTE/PAL.ASM new file mode 100644 index 0000000..ec29427 --- /dev/null +++ b/WIN32LIB/PALETTE/PAL.ASM @@ -0,0 +1,410 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;include "keyboard.inc" +FALSE = 0 +TRUE = 1 + +;****************************** Declarations ******************************** +GLOBAL C Set_Palette_Range:NEAR +GLOBAL C Bump_Color:NEAR +GLOBAL C CurrentPalette:BYTE:768 +GLOBAL C PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG + + + +IF 1 +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* + + PROC Set_Palette_Range C NEAR + ARG palette:DWORD + + GLOBAL Set_DD_Palette_:near + GLOBAL Wait_Vert_Blank_:near + + pushad + mov esi,[palette] + mov ecx,768/4 + mov edi,offset CurrentPalette + cld + rep movsd + ;call Wait_Vert_Blank_ + mov eax,[palette] + call Set_DD_Palette_ + popad + ret + + +ifdef NOT_FOR_WIN95 + USES eax,ebx,ecx,edx,edi,esi + + cld + + ;*=================================================================*/ + ;* Set up pointers to begin making palette comparison */ + ;*=================================================================*/ + mov esi, [palette] + mov edi, OFFSET CurrentPalette + mov ebx, OFFSET PaletteTable + mov ecx, 0 + +??loop_top: + mov eax,[esi] ; read a dword from palette source + mov edx,[edi] ; read a dword from compare palette + and eax,00FFFFFFh ; palette entrys are only 3 bytes + and edx,00FFFFFFh ; long so and of extra + cmp eax,edx ; if they are not the same then + jne ??set_table ; add them into the table + add esi,3 + add edi,3 + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + jmp ??set_pal ; so now go set the palette +??set_table: + shl eax,8 ; shift bgr value up register + mov al,cl ; store which palette entry num + mov [ebx],eax + add ebx,4 + movsw ; copy the three gun values into + movsb ; the shadow palette. Use movsb + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + +??set_pal: + mov esi,ebx + mov ebx,OFFSET PaletteTable + sub esi,ebx ; if ebx didn't change there + jz ??exit ; is nothing to set + shr esi,2 ; find how many entrys + + mov eax,[ebx] + + movzx ecx,al ; we are currently on entry 0 + add ebx,4 + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + + push ebx + mov bx,[VertBlank] + and bl,001h + shl bl,3 + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + pop ebx + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + cli +??loop: + shr eax,8 ; shift down the red gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write the green value to video card + jmp $ + 2 ; force cache to flush, to create a time + inc ecx ; move edx to next palette entry + + mov eax,[ebx] ; get next value to set + add ebx,4 ; and post increment the palette value + cmp al,cl ; check if DAC position already correct + je ??correct_pos + + mov edx,03C8h ; Tell DAC of the color gun to start setting. + out dx,al ; First color set. + mov dx,03C9h + +??correct_pos: + dec esi + jnz ??loop + sti +;***************** Time Critical Section End ******************* +??exit: + ret +endif ;NOT_FOR_WIN95 + + ENDP Set_Palette_Range +ELSE +;*************************************************************************** +;* Set_Palette_Range -- Sets changed values in the palette. * +;* * +;* INPUT: * +;* VOID *palette - pointer to the new palette. * +;* * +;* OUTPUT: * +;* none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/25/1994 SKB : Created. * +;* 04/27/1994 BR : Converted to 32-bit * +;*=========================================================================* +; VOID cdecl Set_Palette_Range(VOID *palette); + PROC Set_Palette_Range C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG palette:DWORD + LOCAL remain:DWORD ; 32-bit: converted to LONG + + cld + + mov bx,[VertBlank] + and bl,001h + shl bl,3 + + ; Make a copy of the palette passed in. + mov edi,OFFSET CurrentPalette + mov esi,[palette] + mov [remain],768 + + ; Search for differences between the current palette and the + ; new palette. When a difference is found, output a block + ; of color registers and keep scanning. +??bodyloop: + mov ecx,[remain] + + repe cmpsb ; Search for differences. + je short ??exit + dec esi + dec edi + inc ecx + + mov edx,0 ; clear EDX + mov eax,ecx + mov ecx,3 + div ecx ; EAX = # of colors to set, EDX = Fraction. + or edx,edx + jz short ??nofrac + neg edx + add edx,3 ; Back offset skip needed. + inc eax ; Fractional color rounds up to whole color to set. +??nofrac: + + ; Set CX to be the number of color guns to set. + mov ecx,eax ; Colors * 3 bytes per color. + add ecx,eax + add ecx,eax + + ; Chop this DAC dump short if necessary in order to reduce + ; sparkling. + mov [remain],0 + cmp ecx,86*3 ; Number of color guns to set per vert retrace + jbe short ??ok + sub ecx,86*3 + mov [remain],ecx + mov ecx,86*3 +??ok: + + ; Adjust the palette offsets back to point to the RED color gun. + sub esi,edx + sub edi,edx + + ; Determine the color number to start setting. + neg eax + add eax,256 ; AX = Color to start setting (0..255). + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + +;??wait: +; in al,dx +; test al,01000b +; jnz ??wait + +;??retrace: +; in al,dx +; test al,01000b +; jz ??retrace + + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + pushf + cli +??loop: + lodsb + stosb + out dx,al + jmp $ + 2 ; force cache to flush, to create a time + ; delay to give DAC time to get value + loop ??loop + popf +;***************** Time Critical Section End ******************* + + cmp [remain],0 + jnz ??bodyloop + +??exit: + ret + + ENDP Set_Palette_Range +ENDIF + + + +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. +??colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short ??gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short ??gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] +??gotit: + inc edi + inc esi + loop ??colorloop + + mov ax,[changed] + ret + + ENDP Bump_Color + + END + +;*************************** End of pal.asm ******************************** + diff --git a/WIN32LIB/PALETTE/PALETTE.CPP b/WIN32LIB/PALETTE/PALETTE.CPP new file mode 100644 index 0000000..8125eed --- /dev/null +++ b/WIN32LIB/PALETTE/PALETTE.CPP @@ -0,0 +1,382 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate); +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * void *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette(void *palette) +{ + + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * void *palette - palette to set color in * + * int color - which color index to set * + * void *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette_Color(void *palette, int color, void *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(unsigned short*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * char *palette1 - this is the palette to fade to. * + * unsigned int delay - fade with this timer count down * + * void *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + short jump; // Gun values to jump per palette set. + unsigned long timer; // Tick count timer used for timing. + short ticksper; // The ticks (fixed point) per bit jump. + int tickaccum; + + + extern void (*cb_ptr)(void); // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * unsigned char *palette - palette to fade to * + * int delay - desired time delay in 60ths of a second * + * short *ticks - output: loop ticks per color jump * + * short *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, + short *rate) +{ + int gun1; // Palette 1 gun value. + int gun2; // Palette 2 gun value. + int diff; // Maximum color gun difference. + int tp; // Temporary tick accumulator. + int index; // Color gun working index. + long t; // Working tick intermediate value. + int adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((long)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (short)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + int index; // Index to DAC register gun. + int gun1,gun2; // Palette 1 gun value. + unsigned char palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = (unsigned char)gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +void (*cb_ptr)(void); // callback function pointer + +/**************************** End of palette.cpp ***************************/ + + + diff --git a/WIN32LIB/PALETTE/PALETTE.H b/WIN32LIB/PALETTE/PALETTE.H new file mode 100644 index 0000000..37393ca --- /dev/null +++ b/WIN32LIB/PALETTE/PALETTE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +#include +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +void __cdecl Set_Palette(void *palette); +void __cdecl Set_Palette_Color(void *palette, int color, void *data); +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +void __cdecl Morph_Palette (void *src_palette, void *dst_palette, unsigned int delay, + void *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern void __cdecl Set_Palette_Range(void *palette); +extern BOOL __cdecl Bump_Color(void *palette, int changable, int target); + +#ifdef __cplusplus +} +#endif +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ diff --git a/WIN32LIB/PLAYCD/GETCD.CPP b/WIN32LIB/PLAYCD/GETCD.CPP new file mode 100644 index 0000000..42b37dd --- /dev/null +++ b/WIN32LIB/PLAYCD/GETCD.CPP @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : GETCD.CPP * + * * + * Programmer : STEVE WETHERILL BASED ON JOE BOSTIC CODE * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * GetCDClass::GetCDClass -- default constructor * + * GetCDClass::~GetCDClass -- destructor * + * GetCDClass::GetCDDrive -- returns the logical CD drive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * GetCDClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ + +GetCDClass::GetCDClass(VOID) +{ + char path[]={"a:\\"}; + + CDCount = 0; + CDIndex = 0; + + /* + ** Set all CD drive placeholders to empty + */ + memset (CDDrives, NO_CD_DRIVE, MAX_CD_DRIVES); + + + for (char i='c' ; i<='z' ; i++){ + path[0]=i; + if (GetDriveType (path) == DRIVE_CDROM){ + CDDrives[CDCount++] = (int) (i-'a'); + } + } + + /* + ** Catch the case when there are NO CD-ROM drives available + */ + if (CDCount == 0) { + for (char i='a' ; i<='b' ; i++){ + path[0]=i; + if (GetDriveType (path) == DRIVE_CDROM){ + CDDrives[CDCount++] = (int) (i-'a'); + } + } + } +} + +/*************************************************************************** + * GetCDClass -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW: Created. * + * 12/4/95 ST: fixed for Win95 * + *=========================================================================*/ + +GetCDClass::~GetCDClass(VOID) +{ +// if(cdDrive_addrp.seg) +// DPMI_real_free(cdDrive_addrp); // free up those conventional buffers +} + +/* ==================================================================== */ + diff --git a/WIN32LIB/PLAYCD/MAKEFILE b/WIN32LIB/PLAYCD/MAKEFILE new file mode 100644 index 0000000..c648a02 --- /dev/null +++ b/WIN32LIB/PLAYCD/MAKEFILE @@ -0,0 +1,178 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = playcd +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getcd.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** diff --git a/WIN32LIB/PLAYCD/MAKEFILE.BOR b/WIN32LIB/PLAYCD/MAKEFILE.BOR new file mode 100644 index 0000000..4a54ead --- /dev/null +++ b/WIN32LIB/PLAYCD/MAKEFILE.BOR @@ -0,0 +1,183 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = playcd +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getcd.obj & + redbook.obj & + playcd.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/PLAYCD/MAKEFILE.WAT b/WIN32LIB/PLAYCD/MAKEFILE.WAT new file mode 100644 index 0000000..4a54ead --- /dev/null +++ b/WIN32LIB/PLAYCD/MAKEFILE.WAT @@ -0,0 +1,183 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = playcd +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getcd.obj & + redbook.obj & + playcd.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/PLAYCD/PLAYCD.ASM b/WIN32LIB/PLAYCD/PLAYCD.ASM new file mode 100644 index 0000000..d6e054c --- /dev/null +++ b/WIN32LIB/PLAYCD/PLAYCD.ASM @@ -0,0 +1,289 @@ +; +; 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 . +; + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + + +DPMI_INTR equ 031h + +GLOBAL DPMI_real_alloc : near +GLOBAL DPMI_real_free : near +GLOBAL DPMI_real_intr : near +GLOBAL DPMI_real_call : near + + +STRUC SEGSEL + segmen dw ? + select dw ? +ENDS + +STRUC REGS + _eax dd ? + _ebx dd ? + _ecx dd ? + _edx dd ? + _esi dd ? + _edi dd ? + _cflag dd ? +ENDS + +STRUC SREGS + _es dw ? + _cs dw ? + _ss dw ? + _ds dw ? + _fs dw ? + _gs dw ? +ENDS + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + size_ref db ? +ENDS + + +CODESEG + +; int DPMI_real_alloc ( UINT , SEGREG * , USHORT * ) ; + +PROC DPMI_real_alloc C near + USES ebx , edx + ARG paragra : DWORD + ARG blk_segptr : DWORD + ARG largest_blkptr : DWORD + + mov eax, 0100h + mov ebx, [paragra] + int DPMI_INTR + + jnc ??dpmi_succed + mov ebx, [largest_blkptr] + mov [ word ptr ebx ] , bx + movzx eax , al + ret + +??dpmi_succed: + mov ebx, [blk_segptr] + mov [(type SEGSEL ptr ebx). segmen ] , ax + mov [(type SEGSEL ptr ebx). select ] , dx + xor eax , eax + ret + +ENDP DPMI_real_alloc + + +;************************************************************************** +; int DPMI_real_free ( UINT ) ; + +PROC DPMI_real_free C near + USES eax , edx + ARG blk_selec : DWORD + + mov eax, 0101h + mov edx, [blk_selec] + shr edx , 16 + int DPMI_INTR + ret +ENDP DPMI_real_free + + +PROC DPMI_real_intr C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + lea edi , [ regblk ] + xor eax , eax + lea ecx , [ regblk . size_ref ] + sub ecx , edi + shr ecx , 2 + rep stosd + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + + mov eax , 0300h + mov ebx , [ vector ] + xor bh , bh + xor ecx , ecx + lea edi , [ regblk ] + + int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_intr + + +PROC DPMI_real_call C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + + lea edi , [ regblk ] + xor al , al + lea ecx , [ regblk . size_ref ] + sub ecx , edi + rep movsb + + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + +; mov eax , 0300h +; mov ebx , [ vector ] +; xor bh , bh +; xor ecx , ecx +; lea edi , [ regblk ] + +; int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_call + +END + + + diff --git a/WIN32LIB/PLAYCD/PLAYCD.H b/WIN32LIB/PLAYCD/PLAYCD.H new file mode 100644 index 0000000..b809834 --- /dev/null +++ b/WIN32LIB/PLAYCD/PLAYCD.H @@ -0,0 +1,309 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + + +#ifdef NOT_FOR_WIN95 +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +}; + + + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + + + + + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#ifdef NOT_FOR_WIN95 +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + +#endif //NOT_FOR_WIN95 +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/WIN32LIB/PLAYCD/REDBOOK.CPP b/WIN32LIB/PLAYCD/REDBOOK.CPP new file mode 100644 index 0000000..d85db1c --- /dev/null +++ b/WIN32LIB/PLAYCD/REDBOOK.CPP @@ -0,0 +1,518 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : REDBOOK.CPP * + * * + * Programmer : STEVE WETHERILL (FROM SCOTT BOWEN CODE) * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * RedBookClass::~RedBookClass(VOID) * + * RedBookClass::RedToHS(ULONG i) * + * RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) * + * RedBookClass::FullCDVolume(UBYTE chan) * + * RedBookClass::PlayTrack(UWORD track) * + * RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) * + * RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, * + * UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) * + * RedBookClass::CheckCDMusic(VOID) * + * RedBookClass::StopCDMusic(VOID) * + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * RedBookClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * calls GetCDDrive() * + * HISTORY: * + * 05/25/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::RedBookClass(VOID) + : GetCDClass() // call the base constructor + +{ + SEGSEL tmpadr ; + + tmpadr = cdDrive_addrp; + memset ( this , 0 , sizeof ( RedBookClass ) ) ; + cdDrive_addrp = tmpadr ; + + Stop.Length = 13; + Stop.Command = 133; + + Tinfo.Length = 26; + Tinfo.Command = 3; + Tinfo.CntTrns = 7; + Tinfo.TrInfo = 11; + + Play.Length = 22; + Play.Command = 132; + Play.AddrMd = 1; + + Volm.Length = 26; + Volm.Command = 12; + Volm.CntTrns = 9; + Volm.TrInfo = 3; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + + Stat.Length = 26; + Stat.Command = 3; + Stat.CntTrns = 11; + Stat.StatInfo = 15; + + if (DPMI_real_alloc(sizeof(TinfoType)/16+1, &Tinfo_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StatType)/16+1, &Stat_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(VolmType)/16+1, &Volm_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(PlayType)/16+1, &Play_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StopType)/16+1, &Stop_addrp, &largestp)) + exit(1); + + GetCDDrive(); + +} + +/*************************************************************************** + * REDBOOKCLASS -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::~RedBookClass(VOID) +{ + if(Tinfo_addrp.seg) + DPMI_real_free(Tinfo_addrp); // free up those conventional buffers + + if(Stat_addrp.seg) + DPMI_real_free(Stat_addrp); + + if(Volm_addrp.seg) + DPMI_real_free(Volm_addrp); + + if(Play_addrp.seg) + DPMI_real_free(Play_addrp); + + if(Stop_addrp.seg) + DPMI_real_free(Stop_addrp); +} + +/*************************************************************************** + * REDTOHS -- RedBook to High-Sierra conversion * + * * + * * + * * + * INPUT: * + * ULONG * + * OUTPUT: * + * ULONG * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::RedToHS(ULONG i) +{ + return( ((i>>16) & 0xFF) * 60 * 75) + ( ((i>>8) & 0xFF) * 75) + (i & 0xFF); +} + +/*************************************************************************** + * MSFTORED -- Minute, Second, Frame to RedBook conversion * + * * + * * + * * + * INPUT: * + * UBYTE minute * + * UBYTE second * + * UBYTE frame * + * OUTPUT: * + * ULONG RedBook * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) +{ + return( ((ULONG)m << 16) + ((ULONG)s << 8) + (ULONG)f ); +} + +/*************************************************************************** + * FULLCDVOLUME -- set full volume * + * * + * * + * * + * INPUT: * + * UBYTE channel * + * * + * CHLEFT * + * CHRIGHT * + * CHBOTH * + * * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::FullCDVolume(UBYTE chan) +{ + + Volm.Vol0 = Volm.Vol1 = Volm.Vol2 = Volm.Vol3 = 255; + + Volm.TrnsAdOff = offsetof (VolmType, TrInfo); + Volm.TrnsAdSeg = Volm_addrp.seg; + + if(chan == CHLEFT) + { +// Volm.In0 = 0; +// Volm.In1 = 3; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol1 = 0; + } + else if(chan == CHRIGHT) + { +// Volm.In0 = 3; +// Volm.In1 = 1; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol0 = 0; + } + else /* both channels */ + { + Volm.In0 = 0; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + } + +// WriteRealMem(REALPTR(Volm_addrp) << 16, &Volm, sizeof(VolmType)); + Mem_Copy ( &Volm , (void *) ( Volm_addrp.seg << 4 ) , sizeof(VolmType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Volm_addrp.seg; + + DPMI_real_intr(0x2F, ®s, & sregs); + +// ReadRealMem(&Volm, REALPTR(Volm_addrp) << 16, sizeof(VolmType)); + Mem_Copy ( (void *) ( Volm_addrp . seg << 4 ), &Volm ,sizeof(VolmType)); +} + + + +/*************************************************************************** + * PLAYTRACK -- play a track * + * * + * * + * * + * INPUT: * + * UWORD track * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::PlayTrack(UWORD track) +{ + + StopCDMusic(); + + Tinfo.Track = track; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); // gets start time of track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + + Play.Start = Tinfo.Start; + Tinfo.Track++; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s , &sregs); // gets start time of following track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + Play.CntSect = RedToHS(Tinfo.Start) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + + FullCDVolume(CHBOTH); +} + + +/*************************************************************************** + * PLAY_CD_MSL -- play cd from start min_sec for len * + * * + * * + * * + * INPUT: * + * UWORD min_sec * + * UWORD Len * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) +{ + UWORD startM, startS, startF; + UWORD endM, endS, endF; + + if (!len) + return; + + endM = startM = (min_sec >> 8) + AUDIO_START_MIN; + endS = startS = (min_sec & 0xFF) + AUDIO_START_SEC; + startF = endF = 0; + + while (len > 59) { + endM++; + len -= 60; + } + + endS += len; + if (endS > 59) { + endM++; + endS -= 60; + } + + PlayMSF((UBYTE) startM, (UBYTE)startS, (UBYTE)startF, (UBYTE)endM, (UBYTE)endS, (UBYTE)endF, 2 /* chan */); +} + + +/*************************************************************************** + * PlayMSF -- Play Minute, Second, Frame to Minute, Second, Frame * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) +{ + + Play.Start = MSFtoRed(startM, startS, startF); + Play.CntSect = RedToHS(MSFtoRed(endM, endS, endF)) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (PlayType, Length); + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + FullCDVolume(chan); + +} + +/*************************************************************************** + * CheckCDMusic -- Check for CD playing * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * UWORD TRUE if playing else FALSE * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +UWORD RedBookClass::CheckCDMusic(VOID) +{ + + Stat.TrnsAdOff = offsetof (StatType, StatInfo); + Stat.TrnsAdSeg = Stat_addrp.seg; + +// WriteRealMem(REALPTR(Stat_addrp) << 16, &Stat, sizeof(StatType)); + Mem_Copy ( &Stat , (void *) ( Stat_addrp.seg << 4 ) , sizeof(StatType)); + + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StatType, Length); + regs.x.eax = 0x1510; + sregs.es = Stat_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stat, REALPTR(Stat_addrp) << 16, sizeof(StatType)); + Mem_Copy ( (void *) ( Stat_addrp.seg << 4 ) , &Stat, sizeof(StatType)); + + return (Stat.Status&0x200); +} + +/*************************************************************************** + * STOPCDMUSIC -- stop CD playing * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::StopCDMusic(VOID) +{ + +// WriteRealMem(REALPTR(Stop_addrp) << 16, &Stop, sizeof(StopType)); + Mem_Copy ( &Stop , (void *) ( Stop_addrp.seg << 4 ) , sizeof(StopType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StopType, Length); + sregs.es = Stop_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stop, REALPTR(Stop_addrp) << 16, sizeof(StopType)); + Mem_Copy ( (void *) ( Stop_addrp.seg << 4 ) , &Stop, sizeof(StopType)); + +} + diff --git a/WIN32LIB/PROFILE/APROFILE.ASM b/WIN32LIB/PROFILE/APROFILE.ASM new file mode 100644 index 0000000..7562b3a --- /dev/null +++ b/WIN32LIB/PROFILE/APROFILE.ASM @@ -0,0 +1,298 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Profiler * +;* * +;* File Name : APROFILE.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : November 17th, 1995 * +;* * +;* Last Update : November 20th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* __PRO -- must be called at the beginning of each function * +;* __EPI -- must be called at the end of each function * +;* Copy_CHL -- initialise the profiler asm data * +;* Profiler_Callback -- windows callback for millisecond timer * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + + p386 + model flat + ideal + jumps + + +MAX_PROFILE_TIME = 60*1 ;1 minute(s) +PROFILE_RATE = 1000 ;1000 samples per sec + + +; +; Externs +; +; +global C ProfileFunctionAddress:dword +global C ProfilePtr:dword +global C ProfileList:dword +global C Stop_Profiler:near +global New_Profiler_Callback_:near +global Old_Profiler_Callback_:near +global C Profile_Init:near +global C Profile_End:near +global ___begtext:near +global BaseAddress:dword +global __PRO:near +global __EPI:near +global MyStack:dword +global MyStackPtr:dword +global ProAddress:dword +global EpiAddress:dword + + + codeseg + + +;********************************************************************************************* +;* __PRO -- registers the current procedure * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that ss:Esp points to return address in function to be registered * +;* * +;* HISTORY: * +;* 11/20/95 4:39PM ST : Created. * +;*===========================================================================================* + +proc __PRO near + + jmp [ProAddress] +; safe version of prologue code +Pro_Start: push eax + mov eax,[MyStackPtr] + push [ProfileFunctionAddress] + pop [eax*4+MyStack] + inc [MyStackPtr] + pop eax + push [dword ss:esp] + pop [ProfileFunctionAddress] +Pro_End: ret + +; unsafe (but much faster) prologue code +; pop [ProfileFunctionAddress] +; jmp [ProfileFunctionAddress] + +endp __PRO + + +;********************************************************************************************* +;* __EPI -- Registers the privious procedure as current again * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that calling procedure will pop ebp immediately on return so we dont have to * +;* preserve it. * +;* * +;* HISTORY: * +;* 11/20/95 4:42PM ST : Created. * +;*===========================================================================================* + +proc __EPI near + + jmp [EpiAddress] +; Safe version of epilogue code. Uncomment the push and pop for ultimate safety +Epi_Start: dec [MyStackPtr] +; push ebp + mov ebp,[MyStackPtr] + mov ebp,[ebp*4+MyStack] + mov [ProfileFunctionAddress],ebp +; pop ebp +Epi_End: ret + +; Unsafe (but much faster) epilogue code. Makes lots of assumptions. +; push [dword esp+8] +; pop [ProfileFunctionAddress] +; ret + +endp __EPI + + + +;********************************************************************************************* +;* Profile_Init -- Initialises the .asm data required for profile session * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that '___begtext' is the first label in the code segment and that its * +;* address is within 15 bytes of the start of the code segment * +;* * +;* HISTORY: * +;* 11/20/95 4:44PM ST : Created. * +;*===========================================================================================* + +proc Profile_Init C near + + mov eax,offset ___begtext + and eax,0fffffff0h + mov [BaseAddress],eax + ;mov [MyStackPtr],0 + mov [ProfileList],PROFILE_RATE + mov [ProfilePtr],1 + mov [ProAddress],offset Pro_Start + mov [EpiAddress],offset Epi_Start + ret + +endp Profile_Init + + + + + +;********************************************************************************************* +;* Profile_End -- disables the __PRO and __EPI procedures * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/20/95 4:44PM ST : Created. * +;*===========================================================================================* + +proc Profile_End C near + + mov [ProAddress],offset Pro_End + mov [EpiAddress],offset Epi_End + ret + +endp Profile_End + + + + + + +;********************************************************************************************* +;* New_Profiler_Callback -- Windows callback used to register function hits * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Note: * +;* The frequency that this is called depends on MAX_PROFILE_RATE defined here and in * +;* profile.h * +;* * +;* HISTORY: * +;* 11/20/95 4:47PM ST : Created. * +;*===========================================================================================* +proc New_Profiler_Callback_ near + + push eax + push esi + mov esi,[ProfilePtr] + cmp esi,MAX_PROFILE_TIME*PROFILE_RATE + jge @@out + mov eax,[ProfileFunctionAddress] + sub eax,[BaseAddress] + mov [ProfileList+esi*4],eax + inc [ProfilePtr] + pop esi + pop eax + ret + +@@out: call Stop_Profiler + pop esi + pop eax + ret + +endp New_Profiler_Callback_ + + + +;********************************************************************************************* +;* Old_Profiler_Callback -- Windows callback used to register function hits * +;* * +;* INPUT: Windows timer callback stuff - not used * +;* * +;* OUTPUT: none * +;* * +;* Note: * +;* The frequency that this is called depends on MAX_PROFILE_RATE defined here and in * +;* profile.h * +;* * +;* HISTORY: * +;* 11/20/95 4:47PM ST : Created. * +;*===========================================================================================* + +proc Old_Profiler_Callback_ near + + push eax + push esi + mov esi,[ProfilePtr] + cmp esi,MAX_PROFILE_TIME*PROFILE_RATE + jge @@out + mov eax,[ProfileFunctionAddress] + sub eax,[BaseAddress] + mov [ProfileList+esi*4],eax + inc [ProfilePtr] + pop esi + pop eax + ret 14h + +@@out: call Stop_Profiler + pop esi + pop eax + ret 14h + +endp Old_Profiler_Callback_ + + + + dataseg + align 4 + +ProfileFunctionAddress dd 0 ;Ptr to function we are currently in +BaseAddress dd 0 ;Address of the code segment start +MyStackPtr dd 0 ;offset into my stack table +ProAddress dd Pro_Start ;jmp ptr for __PRO procedure +EpiAddress dd Epi_Start ;jmp ptr for __EPI procedure + +label MyStack dword ;my stack table + dd 16*1024 dup (?) + +end diff --git a/WIN32LIB/PROFILE/MAKEFILE b/WIN32LIB/PROFILE/MAKEFILE new file mode 100644 index 0000000..a9bbe9e --- /dev/null +++ b/WIN32LIB/PROFILE/MAKEFILE @@ -0,0 +1,179 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = profile +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + wprofile.obj & + aprofile.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** diff --git a/WIN32LIB/PROFILE/PROFILE.CPP b/WIN32LIB/PROFILE/PROFILE.CPP new file mode 100644 index 0000000..f9e11a5 --- /dev/null +++ b/WIN32LIB/PROFILE/PROFILE.CPP @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second. * + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * Use * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Start_Profiler -- initialises the profiler data and starts gathering data * + * Stop_Profiler -- stops the timer and writes the profile data to disk * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include + +#include +#include +#include +#include "profile.h" + +extern "C"{ +unsigned ProfileList [PROFILE_RATE*60*MAX_PROFILE_TIME]; +unsigned ProfilePtr; +} + +extern "C" void Profiler_Callback ( UINT, UINT , DWORD, DWORD, DWORD ); + +unsigned ProfilerEvent; + +void Start_Profiler (void) +{ + memset (&ProfileList[0],-1,PROFILE_RATE*60*MAX_PROFILE_TIME*4); + Copy_CHK(); + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , (void CALLBACK (UINT,UINT,DWORD,DWORD,DWORD))Profiler_Callback , 0 , TIME_PERIODIC); +} + +void Stop_Profiler (void) +{ + if (ProfilerEvent){ + timeKillEvent(ProfilerEvent); + ProfilerEvent=NULL; + + int handle = Open_File ( "profile.bin" , WRITE ); + if (handle != WW_ERROR){ + Write_File (handle , &ProfileList[0] , ProfilePtr*4); + Close_File (handle); + } + } +} + + diff --git a/WIN32LIB/PROFILE/PROFILE.H b/WIN32LIB/PROFILE/PROFILE.H new file mode 100644 index 0000000..ddf81e3 --- /dev/null +++ b/WIN32LIB/PROFILE/PROFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : PROFILE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define MAX_PROFILE_TIME 60*1 //1 minute(s) @ 14.4 Mb per hour +#define PROFILE_RATE 1000 //samples per sec (max 1000) + + +/* + * Defines for choosing between the old and new profiler system + * +*/ + +#define OLD_PROFILE_SYSTEM 1 +#define NEW_PROFILE_SYSTEM 2 + +//#define PROFILE_SYSTEM OLD_PROFILE_SYSTEM +#define PROFILE_SYSTEM NEW_PROFILE_SYSTEM + + + +extern "C"{ + void __cdecl Profile_Init(void); + void __cdecl Profile_End(void); + void __cdecl Start_Profiler(void); + void __cdecl Stop_Profiler(void); +} + diff --git a/WIN32LIB/PROFILE/PROFILE.INC b/WIN32LIB/PROFILE/PROFILE.INC new file mode 100644 index 0000000..3f220e0 --- /dev/null +++ b/WIN32LIB/PROFILE/PROFILE.INC @@ -0,0 +1,44 @@ +; +; 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 . +; + + +;USE_PROFILER =1 + + +macro prologue +Ifdef USE_PROFILER + global __PRO:near + + call __PRO +endif ;USE_PROFILER +endm + + + +macro epilogue +ifdef USE_PROFILER + global __EPI:near + + push ebp + call __EPI + pop ebp +endif ;USE_PROFILER +endm + + + diff --git a/WIN32LIB/PROFILE/UTIL/PROFILE.CPP b/WIN32LIB/PROFILE/UTIL/PROFILE.CPP new file mode 100644 index 0000000..ebd2c0b --- /dev/null +++ b/WIN32LIB/PROFILE/UTIL/PROFILE.CPP @@ -0,0 +1,891 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Uses a map file to match addresses of functions in the sample file with their names * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Start_Profiler -- initialises the profiler data and starts gathering data * + * Stop_Profiler -- stops the timer and writes the profile data to disk * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define bool int +#define true 1 +#define false 0 +#define NAME_TABLE_SIZE 1000000 //Storage space for function names +#define SAMPLE_START 1 //Offset (in dwords) of sample data in sample file + + +/* +** Function prototypes +*/ +void Print_My_Name(void); +void Print_Usage(void); +int Load_File(char *file_name , unsigned *file_ptr , unsigned mode); +bool Extract_Function_Addresses(void); +unsigned Get_Hex (char string[] , int length); +char *Search_For_Char (char character , char buffer_ptr[] , int buffer_length); +char *Search_For_String (char *string , char *buffer_ptr , int buffer_length); +void Map_Profiler_Hits (void); +void Sort_Functions(void); +void Sort_Functions_Again(void); +void Output_Profile(void); + +char *SampleFile; //Ptr to sample file name +char *MapFile; //Ptr to map file name +unsigned *SampleFileBuffer; //Ptr to buffer that sample file is loaded in to +char *MapFileBuffer; //Ptr to buffer that map file is loaded in to +unsigned SampleFileLength; //Length of sample file +unsigned MapFileLength; //Length of map file +char FunctionNames[NAME_TABLE_SIZE]; //Buffer to store function names in +char *FunctionNamePtr=&FunctionNames[0]; //Ptr to end of last function name in buffer +int TotalFunctions; //Total number of functions extracted from map file +int SampleRate; //Number of samples/sec that data was collected at +unsigned EndCodeSegment; //Length of the sampled programs code segments + +/* +** Structure for collating function data +*/ +typedef struct tFunction { + unsigned FunctionAddress; //Address of function relative to start of code seg + char *FunctionName; //Ptr to name of function in FunctionNames buffer + int Hits; //Number of times function was 'hit' when sampling +} Function; + +Function FunctionList[10000]; //max 10,000 functions in map file. + + + + +/*********************************************************************************************** + * main -- program entry point * + * * + * * + * * + * INPUT: argc , argv * + * * + * OUTPUT: 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:21PM ST : Created * + *=============================================================================================*/ + +int main(int argc, char *argv[]) + +{ + Print_My_Name(); // print the programs name + + + /* + ** If the arguments dont make sense then print the usage + */ + if (argc!=3 || + !strcmpi(argv[1],"/?") || + !strcmpi(argv[1],"/h") || + !strcmpi(argv[1],"/help") || + !strcmpi(argv[1],"-?") || + !strcmpi(argv[1],"-h") || + !strcmpi(argv[1],"-help") || + !strcmpi(argv[1],"?") || + !strcmpi(argv[1],"h") || + !strcmpi(argv[1],"help")){ + Print_Usage(); + return(0); + } + + /* + ** Get the names of the files to load + */ + SampleFile=argv[1]; + MapFile=argv[2]; + + /* + ** Load the profile sample file + */ + SampleFileLength = Load_File (SampleFile , (unsigned*)&SampleFileBuffer , O_BINARY); + if (!SampleFileLength) return(0); + + /* + ** The sample rate is the 1st dword in the file + */ + SampleRate=*SampleFileBuffer; + + /* + ** Load the .map file + */ + MapFileLength = Load_File (MapFile , (unsigned*)&MapFileBuffer , O_BINARY); + if (!MapFileLength){ + free (SampleFileBuffer); + return(0); + } + + /* + ** Get the function names from the map file + */ + cprintf ("Extracting function data from map file.\n"); + if (!Extract_Function_Addresses()){ + cprintf ("Error parsing .MAP file - aborting\n\n"); + return (0); + } + + /* + ** Sort the functions into address order to make it easier to map the functions + */ + cprintf ("Sorting function list by address"); + Sort_Functions(); + + /* + ** Map the addresses in the sample file to the function addresses + */ + cprintf ("\nMapping profiler hits to functions"); + Map_Profiler_Hits(); + + /* + ** Sort the functions into order of usage for output + */ + cprintf ("\nSorting function list by activity"); + Sort_Functions_Again(); + cprintf ("\n\n"); + + /* + ** Print the function usage statistics + */ + Output_Profile(); + + /* + ** Cleanup and out + */ + free (SampleFileBuffer); + free (MapFileBuffer); + return(0); +} + + + +/*********************************************************************************************** + * Print_My_Name -- print the programs name and version * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:25PM ST : Created * + *=============================================================================================*/ + +void Print_My_Name(void) +{ + cprintf("Westwood profile data analyzer.\n"); + cprintf("V 1.0 - 11/17/95\n"); + cprintf("Programmer - Steve Tall.\n\n"); +} + + +/*********************************************************************************************** + * Print_Usage -- print the instructions * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:26PM ST : Created * + *=============================================================================================*/ + +void Print_Usage (void) +{ + cprintf("Usage: PROFILE =0 ; j--){ + if (FunctionList[j].FunctionAddress < function_hit){ + FunctionList[j].Hits++; + break; + } + } + } + +} + + +/*********************************************************************************************** + * Sort_Functions -- hideous bubble sort of functions into address order * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:29PM ST : Created * + *=============================================================================================*/ + +void Sort_Functions (void) +{ + Function address_swap; + + if (TotalFunctions>1){ + + for (int outer=0 ; outer FunctionList[inner+1].FunctionAddress ){ + + memcpy (&address_swap , &FunctionList[inner] , sizeof(Function)); + memcpy (&FunctionList[inner] , &FunctionList[inner+1] , sizeof(Function)); + memcpy (&FunctionList[inner+1] , &address_swap , sizeof(Function)); + } + } + + if (!address_swap.FunctionAddress) break; + } + } +} + + +/*********************************************************************************************** + * Sort_Functions -- hideous bubble sort of functions into usage order * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:29PM ST : Created * + *=============================================================================================*/ + +void Sort_Functions_Again (void) +{ + Function address_swap; + + if (TotalFunctions>1){ + + for (int outer=0 ; outer EndCodeSegment){ + EndCodeSegment = code_segment_start+code_segment_size; + } + + chars_left = end_str_ptr - segment_ptr; + segment_ptr = Search_For_Char ( 13 , segment_ptr , chars_left ); + chars_left = end_str_ptr - segment_ptr; + + } while (chars_left > 0); + + + + chars_left=MapFileLength; + /* + ** Search for the 'Memory Map' segment of the map file + */ + map_ptr = Search_For_String ("Memory Map" , MapFileBuffer , chars_left); + if (!map_ptr){ + return (false); + } + chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); + + /* + ** Get the length of the memory map segment by searching for the start of the next segment + */ + end_str_ptr = Search_For_String ("+-----" , map_ptr , chars_left); + if (end_str_ptr){ + MapFileLength = ((unsigned)MapFileBuffer + MapFileLength) - (unsigned)end_str_ptr; + } + chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); + + /* + ** Reset the total number of functions found + */ + TotalFunctions = 0; + + /* + ** + ** Find each occurrence of 0001: as all the functions we want are in the 1st segment + ** + */ + do { + /* + ** Find '0001:' + */ + map_ptr = Search_For_String ("0001:" , map_ptr , chars_left); + if (!map_ptr){ + break; + } + chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); + + /* + ** Skip the '0001:' portion of the address and get the hext offset of the function + */ + map_ptr+=5; + FunctionList[TotalFunctions].FunctionAddress=Get_Hex(map_ptr,8); + + /* + ** Skip to the function name and get its length by searching for the end of the line + */ + map_ptr+=10; + chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); + end_str_ptr = Search_For_Char (13 , map_ptr , chars_left); + if (!end_str_ptr){ + break; + } + function_name_length = (unsigned)end_str_ptr - (unsigned)map_ptr; + + /* + ** Copy the function name into the name list and keep a pointer to it + */ + memcpy (FunctionNamePtr , map_ptr , function_name_length); + FunctionList[TotalFunctions].FunctionName = FunctionNamePtr; + FunctionNamePtr += function_name_length+1; //Leave an extra 0 on the end as a terminator + FunctionList[TotalFunctions].Hits = 0; //We dont yet know how many times we hit it + TotalFunctions++; + + } while (1); + + + /* + ** Add in a dummy function at the highest address to represent unknown code hits + */ + FunctionList[TotalFunctions].FunctionAddress = EndCodeSegment; + memcpy (FunctionNamePtr , &unknown , sizeof (unknown)); + FunctionList[TotalFunctions].FunctionName = FunctionNamePtr; + FunctionNamePtr += sizeof (unknown); + FunctionList[TotalFunctions].Hits = 0; + TotalFunctions++; + + return (true); +} + + + + +/*********************************************************************************************** + * Get_Hex -- nasty function to convert an ascii hex number to an unsigned int * + * I'm sure there must be a lovely 'c' way of doing this but I dont know what it is * + * * + * * + * INPUT: ptr to ascii hex string , number of digits in string * + * * + * OUTPUT: value of hex string * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/20/95 5:39PM ST : Created * + *=============================================================================================*/ + +unsigned Get_Hex (char string[] , int length) +{ + unsigned hex_val=0; + int multiplier=1; + char hex_char; + + for (int i=0 ; i. +*/ + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : WPROFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Start_Profiler -- initialises the profiler data and starts gathering data * + * Stop_Profiler -- stops the timer and writes the profile data to disk * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include +#include +#include +#include "profile.h" +#include +#include + +#define PROFILE + +extern "C"{ +#ifdef PROFILE +unsigned ProfileList [PROFILE_RATE*MAX_PROFILE_TIME]; +#else +unsigned ProfileList [2]; +#endif +unsigned ProfilePtr; +} + +extern "C" void Old_Profiler_Callback ( UINT, UINT , DWORD, DWORD, DWORD ); +extern "C" void New_Profiler_Callback (void); +extern "C" { + extern unsigned ProfileFunctionAddress; +} + +unsigned long ProfilerEvent; //Handle for profiler callback +unsigned long ProfilerThread; //Handle for profiler thread + + +HANDLE CCThreadHandle; +CONTEXT ThreadContext; + + +#if (PROFILE_SYSTEM == NEW_PROFILE_SYSTEM) + +/*********************************************************************************************** + * Thread_Callback -- gets the IP address of our thread and registers it * + * * + * * + * * + * INPUT: Windows timer callback parms - not used * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/2/96 6:37AM ST : Created * + *=============================================================================================*/ + +void CALLBACK Thread_Callback (UINT,UINT,DWORD,DWORD,DWORD) +{ + ThreadContext.ContextFlags = VDMCONTEXT_CONTROL; + if (!InTimerCallback){ + GetThreadContext ( CCThreadHandle , &ThreadContext ); + }else{ + GetThreadContext (TimerThreadHandle , &ThreadContext); + } + + ProfileFunctionAddress = ThreadContext.Eip; + New_Profiler_Callback(); +} + + +/*********************************************************************************************** + * Profile_Thread -- this is the thread our profiler runs in. It just starts off a timer and * + * then buggers off into an infinite message loop. We shouldnt get messages * + * here as this isnt our primary thread * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/2/96 6:39AM ST : Created * + *=============================================================================================*/ +void Profile_Thread (void) +{ + MSG msg; + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , Thread_Callback , 0 , TIME_PERIODIC); + //ProfilerEvent = timeSetEvent (100 , 1 , Thread_Callback , 0 , TIME_ONESHOT); + do { + GetMessage(&msg,NULL,0,0); + } while(1); +} + +#endif //(PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + + +/*********************************************************************************************** + * Start_Profiler -- initialises the profiler system and starts sampling * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: There may be a pause when sampling starts as Win95 does some VMM stuff * + * * + * HISTORY: * + * 11/20/95 5:12PM ST : Created * + *=============================================================================================*/ + +void __cdecl Start_Profiler (void) +{ +#ifdef PROFILE + if (!ProfilerEvent){ + memset (&ProfileList[0],-1,PROFILE_RATE*MAX_PROFILE_TIME*4); + } + + Profile_Init(); + + if (!ProfilerEvent){ + +#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + /* + ** Old profile system - just set up a timer to monitor the global variable based on + ** the last place __PRO was called from + */ + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , (void CALLBACK (UINT,UINT,DWORD,DWORD,DWORD))Old_Profiler_Callback , 0 , TIME_PERIODIC); +#else + /* + ** New profile system - create a second thread that will do all the profiling + ** using GetThreadContext + */ + if ( DuplicateHandle( GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&CCThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS) ){ + ProfilerEvent= (unsigned)CreateThread(NULL,2048,(LPTHREAD_START_ROUTINE)&Profile_Thread,NULL,0,&ProfilerThread); + } +#endif + + } + +#else + ProfilerEvent = 0; +#endif + +} + + + +/*********************************************************************************************** + * Stop_Profiler -- stops the sampling timer and writes the colledted data to disk * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Writes to file PROFILE.BIN * + * * + * HISTORY: * + * 11/20/95 5:13PM ST : Created * + *=============================================================================================*/ + +void __cdecl Stop_Profiler (void) +{ + if (ProfilerEvent){ + +#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + // + // Old system - just remove the timer event + // + timeKillEvent(ProfilerEvent); +#else + // + // New system - kill the profiling thread + // + TerminateThread((HANDLE)ProfilerThread,0); +#endif + + ProfilerEvent=NULL; + + Profile_End(); + + int handle = Open_File ( "profile.bin" , WRITE ); + if (handle != WW_ERROR){ + Write_File (handle , &ProfileList[0] , ProfilePtr*4); + Close_File (handle); + } + } +} + + diff --git a/WIN32LIB/PROJECT.CFG b/WIN32LIB/PROJECT.CFG new file mode 100644 index 0000000..a0ea729 --- /dev/null +++ b/WIN32LIB/PROJECT.CFG @@ -0,0 +1,22 @@ +.SILENT +.OPTIMIZE +.ERASE +#*************************************************************************** +# development configuration switches +#CC_CFG = -bt=nt /i=$(%WIN32LIB)\include -W3 -d2 -orilt /4 -s -j -DNUMEGA=1 +#CC_CFG = -bt=nt /i=$(%WIN32LIB)\include -W3 -d2 -od /4 -s -j -DNUMEGA=1 +ASM_CFG = /i. /i$(%WIN32LIB)\include /zi /t /m /w+ /jJUMPS /ml +LIB_CFG = /b /n /n +LINK_CFG = debug codeview system nt_win option stack=64k op map +#ASM_CFG = /i. /i$(%WIN32LIB)\include /zd /t /m /w+ /jJUMPS /ml +#LIB_CFG = /b /n /n +#LINK_CFG = option stack=32k debug all + +#*************************************************************************** +# Release configuration switches + +CC_CFG = /i=$(%WIN32LIB)\include /W3 /d1 /otxan /s /4 /j +#ASM_CFG = /i$(%WIN32LIB)\include /zd /t /m /w+ /jJUMPS /ml +#LIB_CFG = /b /n /n +#LINK_CFG = option stack=32k debug all + diff --git a/WIN32LIB/RAWFILE/CCFILE.CPP b/WIN32LIB/RAWFILE/CCFILE.CPP new file mode 100644 index 0000000..3b694cb --- /dev/null +++ b/WIN32LIB/RAWFILE/CCFILE.CPP @@ -0,0 +1,604 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\ccfile.cpv 2.20 27 Sep 1995 12:45:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : March 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * CCFileClass::Error -- Handles displaying a file error message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const *filename) +{ + Set_Name(filename); + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) : CDFileClass() +{ + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const *buffer, long size) +{ + + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Pointer || FromDisk) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void *buffer, long size) +{ + int opened = false; + + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Pointer) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size) { + Mem_Copy(Add_Long_To_Pointer(Pointer, Position), buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + /* + ** If the file is part of a mixfile, but the mixfile is located + ** on disk, then a special read operation is necessary. + */ + if (FromDisk) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size > 0) { + CDFileClass::Seek(Start + Position, SEEK_SET); + size = CDFileClass::Read(buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + if (opened) Close(); + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + if (Pointer || FromDisk) { + switch (dir) { + case SEEK_END: + Position = Length; + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + if (Position < 0) Position = 0; + if (Position > Length) Position = Length; + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + if (Pointer || FromDisk) return(Length); + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + if (MixFileClass::Offset(File_Name())) { + return(true); + } + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Pointer) return(true); + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; // Starts at beginning offset. + Start = 0; + Length = 0; + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MixFileClass *mixfile = 0; + if (MixFileClass::Offset(File_Name(), &Pointer, &mixfile, &Start, &Length)) { + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (!Pointer) { + long start = Start; + long length = Length; + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char const * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + free((void *)dupfile); + Start = start; + Length = length; + FromDisk = true; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + +static CCFileClass Handles[10]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(BYTE const *) +{ + CCFileClass::Set_Search_Path(path); + return(true); +} +#endif + +WORD __cdecl Open_File(BYTE const *file_name, WORD mode) +{ + for (int index = 0; index < sizeof(Handles)/sizeof(Handles[0]); index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(ERROR); +} + +VOID __cdecl Close_File(WORD handle) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(WORD handle, VOID *buf, ULONG bytes) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(WORD handle, VOID const *buf, ULONG bytes) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +WORD __cdecl Find_File(BYTE const *file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +WORD __cdecl Delete_File(BYTE const *file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +WORD __cdecl Create_File(BYTE const *file_name) +{ + return(CCFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +VOID * __cdecl Load_Alloc_Data(BYTE const *name, WORD ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +ULONG __cdecl File_Size(WORD handle) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(BYTE const *name, VOID const *ptr, ULONG size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(WORD handle, LONG offset, WORD starting) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +WORD __cdecl Find_Disk_Number(BYTE const *) +{ + return(0); +} +#endif + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} + + +void Unfragment_File_Cache(void) +{ +} + + diff --git a/WIN32LIB/RAWFILE/FILE.H b/WIN32LIB/RAWFILE/FILE.H new file mode 100644 index 0000000..69866c7 --- /dev/null +++ b/WIN32LIB/RAWFILE/FILE.H @@ -0,0 +1,260 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#ifndef READ +#define READ 1 // Read access. +#endif +#ifndef WRITE +#define WRITE 2 // Write access. +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Find_First(unsigned char *fname, unsigned int mode, struct find_t *ffblk); +extern int __cdecl Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/WIN32LIB/RAWFILE/FILETEMP.H b/WIN32LIB/RAWFILE/FILETEMP.H new file mode 100644 index 0000000..796d225 --- /dev/null +++ b/WIN32LIB/RAWFILE/FILETEMP.H @@ -0,0 +1,61 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD __cdecl ( __cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID __cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H + diff --git a/WIN32LIB/RAWFILE/MAKEFILE b/WIN32LIB/RAWFILE/MAKEFILE new file mode 100644 index 0000000..bd096f2 --- /dev/null +++ b/WIN32LIB/RAWFILE/MAKEFILE @@ -0,0 +1,179 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = rawfile +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + rawfile.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/RAWFILE/MAKEFILE.BOR b/WIN32LIB/RAWFILE/MAKEFILE.BOR new file mode 100644 index 0000000..7176dee --- /dev/null +++ b/WIN32LIB/RAWFILE/MAKEFILE.BOR @@ -0,0 +1,169 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = rawfile +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + rawfile.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+rawfile +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/RAWFILE/MAKEFILE.WAT b/WIN32LIB/RAWFILE/MAKEFILE.WAT new file mode 100644 index 0000000..0d0488b --- /dev/null +++ b/WIN32LIB/RAWFILE/MAKEFILE.WAT @@ -0,0 +1,179 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = rawfile +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + rawfile.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/RAWFILE/RAWFILE.CPP b/WIN32LIB/RAWFILE/RAWFILE.CPP new file mode 100644 index 0000000..e1823e0 --- /dev/null +++ b/WIN32LIB/RAWFILE/RAWFILE.CPP @@ -0,0 +1,1303 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.cpv 2.17 06 Sep 1995 16:38:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + #define true 1 + #define false 0 + +#define WIN32 +#define _WIN32 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "wwlib32.h" +//#include "compat.h" +#include "rawfile.h" + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +#pragma argsused +//void RawFileClass::Error(int error, int canretry, char const * filename) +void RawFileClass::Error(int error, int , char const *) +{ + char buffer[256]; + + wsprintf(buffer, "File Error #%d", error); +/////// MessageBox(NULL, buffer, "Error", MB_OK); + +#ifdef NEVER + char message[256]; // Staging buffer for error message string. + + /* + ** Build the complete text of the error message. This text is used in either the graphic + ** mode or the text mode version. + */ +#ifdef GERMAN + strcpy(message, "DATEIFEHLER"); +#else +#ifdef FRENCH + strcpy(message, "ERREUR DE FICHIER"); +#else + strcpy(message, "FILE ERROR"); +#endif +#endif + if (filename) { + strcat(message, "("); + strcat(message, filename); + strcat(message, ")"); + } + strcat(message, ": "); +//BG: Borland only strcat(message, _sys_errlist[error]); + strcat(message, strerror(error) ); + strcat(message, ". "); + + /* + ** If it can't properly handle displaying the error message in the + ** current graphic mode, then this forces the error to become non + ** recoverable. Go into text mode and proceed. + */ + if (!FontPtr && GraphicMode != TXT_MODE) { + Set_Video_Mode(RESET_MODE); + canretry = false; + GraphicMode = TXT_MODE; + } + + /* + ** Add the text explaining the valid actions to take. + */ + if (canretry) { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken fr erneuten Versuch."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour recommencer."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Appuyez sur Echap pour quitter le programme."); +#else + strcat(message, " Press any key to retry."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Press to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } else { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour quitter le programme."); +#else + strcat(message, " Press any key to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } + + /* + ** In text mode, the error handler is very simple. It just prints the error message + ** to the screen and waits for a response. + */ + if (GraphicMode == TXT_MODE) { + int input; + + /* + ** Display the error message and wait for a response. + */ + printf(message); + Keyboard::Clear(); + input = Keyboard::Get(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + } else { + + /* + ** The graphic mode version of the error handler will display a simple message + ** box on the screen. If the palette is black at this point, then the error will + ** be invisible. For more thorough and pleasing results, you should replace this + ** virtual function with one of your own, that is more aware of the environment + ** in which is exists. + */ + void *background; // Pointer to background saving buffer. + GraphicBufferClass * oldpage; // Copy of old logic page. + int oldwindow; // Copy of old window number. + void const *oldfont; // Copy of old font pointer. + int oldspacing; // Old font X spacing. + + /* + ** Setup display in preparation for printing the error message. + */ + oldpage = Set_Logic_Page(SeenBuff); + oldwindow = Change_Window(ErrorWindow); + oldfont = Set_Font(FontPtr); + oldspacing = FontXSpacing; FontXSpacing = 0; + Hide_Mouse(); + + /* + ** Try to allocate a storage buffer for the background to the + ** error window. + */ + background = new char [Size_Of_Region(WinW<<3, WinH)]; + + /* + ** If there is memory for the background storage, then save the + ** screen image area to that buffer. + */ + if (background) { + SeenPage.To_Buffer(WinX<<3, WinY, WinW<<3, WinH, background, Size_Of_Region(WinW<<3, WinH)); + } + + /* + ** Draw a rudimentary box. + */ + New_Window(); + LogicPage->Draw_Rect(WinX<<3, WinY, (WinX+WinW)<<3, WinY+WinH, WinC); + + /* + ** shrinks window down one byte in all directions. + */ + WindowList[Window][WINDOWX] += 1; + WindowList[Window][WINDOWY] += 1<<3; + WindowList[Window][WINDOWWIDTH] -= 1<<1; + WindowList[Window][WINDOWHEIGHT] -= 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + Window_Print(message); + Keyboard::Clear(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + int input = Keyboard::Get(); + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + /* + ** Restore the window back to its original size. + */ + WindowList[Window][WINDOWX] -= 1; + WindowList[Window][WINDOWY] -= 1<<3; + WindowList[Window][WINDOWWIDTH] += 1<<1; + WindowList[Window][WINDOWHEIGHT] += 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + /* + ** If the background was saved off, then restore it. + */ + if (background) { + Buffer_To_Page(WinX<<3, WinY, WinW<<3, WinH, background, SeenPage); + delete [] background; + } + + /* + ** Restore the system global settings to original values. + */ + Show_Mouse(); + Change_Window(oldwindow); + Set_Font(oldfont); + Set_Logic_Page(oldpage); + FontXSpacing = oldspacing; + } +#endif +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const *filename) : Filename(filename) +{ + Handle = -1; + Allocated = false; +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const *filename) +{ + if (Filename && Allocated) { +// Heap_Dump_Check( "Before raw free" ); + free((char *)Filename); +// Heap_Dump_Check( "After raw free" ); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (!filename) return(NULL); + +// Heap_Dump_Check( "Before raw strdup" ); + ((char *&)Filename) = strdup(filename); +// Heap_Dump_Check( "After raw strdup" ); + if (!Filename) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const *filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetatively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ +// Hard_Error_Occured = 0; + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_READ); + //Handle = open(Filename, O_RDONLY|O_BINARY); + break; + + case WRITE: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_WRITE | MMIO_CREATE); + //Handle = open(Filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , S_IWRITE); + break; + + case READ|WRITE: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_READWRITE); + //Handle = open(Filename, O_RDWR|O_CREAT|O_BINARY); + break; + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == 0) { + +// Error(errno, false, Filename); //this kills windoze!!!! ST - 9/28/95 5:33PM +#ifdef NEVER + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } +#endif + continue; + } + break; + } + return(true); +} + + + + +void RawFileClass::Set_Buffer_Size (int size) +{ + if (Handle){ + mmioSetBuffer((HMMIO)Handle, NULL, size, 0); + } +} + + + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ + int file; // Working file handle. + int open_failed; + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +// Hard_Error_Occured = 0; + file = open(Filename, O_RDONLY|O_BINARY); + open_failed = (file == -1); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } +// } + if (!open_failed) break; + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ + if (close(file)) { + Error(errno, false, Filename); + } + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ +// Hard_Error_Occured = 0; + if (mmioClose((HMMIO)Handle, 0)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } + break; + } + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = -1; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void *buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + int readresult; + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size; + +// Hard_Error_Occured = 0; + readresult = 0; + actual = mmioRead((HMMIO)Handle, (char*)buffer, desired); + //actual = read(Handle, buffer, desired); + if (actual != desired) readresult = errno; + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; // Not technically needed, but to be consistent... +// } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + }// else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (void *) ((long)buffer + actual); + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + //} +// } + } + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const *buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + int writeresult; + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + +// Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = 0; + actual = mmioWrite((HMMIO)Handle, (char*)buffer, desired); + //actual = write(Handle, buffer, desired); + if (actual != desired) writeresult = errno; + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; // Not technically needed, but to be consistent... +// } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (void *)((long)buffer + actual); + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } +// } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ +// Hard_Error_Occured = 0; + pos = mmioSeek((HMMIO)Handle, pos, dir); + //pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } +// } + break; + } + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + int handle = 0; + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { +// Hard_Error_Occured = 0; + + handle = open(Filename, O_RDONLY|O_BINARY); + if (handle > 0){ + size = filelength(handle); + close(handle); + } + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + if (size == -1) { + Error(errno, false, Filename); + } +// } + break; + } + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + return(size); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +// Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + + + + + +//extern "C" { + +#define MAX_HANDLES 10 +static RawFileClass Handles[MAX_HANDLES]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(BYTE const *) +{ + RawFileClass::Set_Search_Path(path); + return(true); +} +#endif + + + +int __cdecl Open_File(char const *file_name, int mode) +{ + for (int index = 0; index < MAX_HANDLES; index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name,mode)) { + return(index); + } + break; + } + } + return(WW_ERROR); +} + +VOID __cdecl Close_File(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(int handle, VOID *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(int handle, VOID const *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const *file_name) +{ + RawFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(BYTE const *file_name) +{ + return(RawFileClass(file_name).Delete()); +} + +int __cdecl Create_File(BYTE const *file_name) +{ + return(RawFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size) +{ + return(RawFileClass(name).Read(ptr, size)); +} + +VOID * __cdecl Load_Alloc_Data(char const *name, int ) +{ + RawFileClass file(name); + + return(Load_Alloc_Data(file)); +} +#endif + +ULONG __cdecl File_Size(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(BYTE const *name, VOID const *ptr, ULONG size) +{ + return(RawFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(int handle, LONG offset, int starting) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(RawFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +int __cdecl Find_Disk_Number(BYTE const *) +{ + return(0); +} +#endif + + + + +/*********************************************************************************************** + * Load_File -- load an entire file into memory * + * * + * * + * * + * INPUT: File name * + * Load address * + * * + * OUTPUT: bytes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/28/95 5:09PM ST : Created * + *=============================================================================================*/ + +int __cdecl Load_File ( const char *file_name , void *load_addr ) +{ + int bytes_read=0; + int handle; + + handle=Open_File ( file_name , READ ); + + if ( handle>=0 ){ + bytes_read = Read_File ( handle , load_addr , File_Size ( handle ) ); + Close_File ( handle ); + } + return ( bytes_read ); +} + + + + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(RawFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(RawFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} + + +void Unfragment_File_Cache(void) +{ +} diff --git a/WIN32LIB/RAWFILE/RAWFILE.H b/WIN32LIB/RAWFILE/RAWFILE.H new file mode 100644 index 0000000..83ac3b4 --- /dev/null +++ b/WIN32LIB/RAWFILE/RAWFILE.H @@ -0,0 +1,255 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.15 06 Sep 1995 16:29:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include + +//#include +#include +#include +#include +//#include +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void) {if (Allocated && Filename) free((char *)Filename);}; + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + virtual void Set_Buffer_Size(int size); + + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return Filename; +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Filename(0) +{ + Handle = -1; + Allocated = false; +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle != -1); +} + +#endif diff --git a/WIN32LIB/RAWFILE/WWFILE.H b/WIN32LIB/RAWFILE/WWFILE.H new file mode 100644 index 0000000..4913695 --- /dev/null +++ b/WIN32LIB/RAWFILE/WWFILE.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.14 06 Sep 1995 16:30:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/WIN32LIB/REBUILD.BAT b/WIN32LIB/REBUILD.BAT new file mode 100644 index 0000000..32f4737 --- /dev/null +++ b/WIN32LIB/REBUILD.BAT @@ -0,0 +1,3 @@ +global /i del *.obj +del /y lib +wmake diff --git a/WIN32LIB/SHAPE/DRAWSHP.ASM b/WIN32LIB/SHAPE/DRAWSHP.ASM new file mode 100644 index 0000000..526893d --- /dev/null +++ b/WIN32LIB/SHAPE/DRAWSHP.ASM @@ -0,0 +1,1128 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : DRAWSHP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : April 13, 1992 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* Not_Supported -- Replacement function for Draw_Shape routines not used* +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +; this struct is here to remove the hardwire way of programing +; implemented in the funtion Draw_Shape in ian image of + +STRUC VVPC_IMAGE + Off dd ? + Width dd ? + Height dd ? + Page dd ? +ENDS + + + +STRUC GVPC_IMAGE + vvpc VVPC_IMAGE <> + Xpos dd ? + Ypos dd ? + GraphicBuff dd ? +ENDS + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;****************************** Declarations ******************************** +GLOBAL Draw_Shape:NEAR +GLOBAL LCW_Uncompress:NEAR +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD + +GLOBAL MaskPage : dword +GLOBAL BackGroundPage : dword + + +;********************************* Data ************************************ + DATASEG +;--------------------------------------------------------------------------- +; Shape buffer & its size, set by Set_Shape_Buffer() +;--------------------------------------------------------------------------- +_ShapeBuffer DD 0 +_ShapeBufferSize DD 0 + +;--------------------------------------------------------------------------- +; Address of MaskPage & BackGroundPage, set by Init_Priority_System() +;--------------------------------------------------------------------------- +MaskPage DD 0 +BackGroundPage DD 0 + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +PredCount DD 0 +PredTable DB 1, 3, 2, 5, 4, 3, 2, 1 +PredValue DD 1 +PartialPred DD 0 ; partially faded predator effect value +PartialCount DD 0 + +;--------------------------------------------------------------------------- +; 32 bit versions of 16 bit stack variables +;--------------------------------------------------------------------------- +Flags DD ? ; globally accessible copy of flags + +viewport_ptr DD ? ; pointer to upper-left corner of viewport +viewport_width DD ? ; viewport width +viewport_height DD ? ; viewport height +viewport_yadd DD ? ; viewport y add +viewport_x DD ? ; viewport x-coord +viewport_y DD ? ; viewport y-coord +buff_ptr DD ? ; pointer to buffer containing viewport + +;********************************* Code ************************************ + CODESEG + + +;*************************************************************************** +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* * +;* INPUT: * +;* DWORD gvp_ptr ; pointer to graphic viewport info * +;* DWORD shape_ptr ; the shape pointer to draw * +;* DWORD draw_x ; x-coord of hotspot in viewport * +;* DWORD draw_y ; y-coord of hotspot in viewport * +;* DWORD flags ; the flags for drawing the shape * +;* * +;* Optional Arguments: If the following flags are used, the given args * +;* MUST be present. Note that, if more than one one set of args is used, * +;* they must appear in this order (alphabetical). * +;* SHAPE_COLOR: DWORD color_table (256 bytes) * +;* SHAPE_FADING: DWORD fade_table (256 bytes), DWORD fade_count * +;* SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl * +;* SHAPE_PARTIAL: DWORD predator partial_value (0-255) * +;* SHAPE_PRIORITY: DWORD priority_level * +;* SHAPE_SCALING: DWORD x_scale, WORD y_scale * +;* SHAPE_SHADOW: DWORD shadowing_table (256 bytes) * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* File Organization: * +;* drawshp.asm : this file * +;* shape.inc : main shape header file; contains declarations for all * +;* globals, procedures and constants * +;* ds_table.asm: contains the procedure address tables for LSkipRout, * +;* RSkipRout, DrawRout, & PixelRout * +;* ds_l*.asm : left-skip routines * +;* ds_r*.asm : right-skip routines * +;* ds_d*.asm : drawing routines * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Shape format: * +;* Header: * +;* UWORD SType (0=normal, 1=16-color, 2=uncompressed, 4=variable-color) * +;* UBYTE Height * +;* UWORD Width * +;* UBYTE unmodified height * +;* UWORD size of shape in memory, including this header * +;* UWORD uncompressed data size * +;* Normal Shape: * +;* UBYTE [compressed] Shape data * +;* 16-color shape: * +;* UBYTE 16-color table * +;* UBYTE [compressed] Shape data * +;* variable-color shape: * +;* UBYTE # colors * +;* UBYTE color table (variable-length) * +;* UBYTE [compressed] Shape data * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Uncompressed shape data format: * +;* Data is stored as a bitmap, with 0's treated as a special case. Every * +;* 0 byte is followed by a repetition count byte. Every scan line is * +;* compressed separately. Thus, the following bitmap results in the * +;* following shape data: * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* * +;*-------------------------------------------------------------------------* +;* * +;* How scaling is handled: * +;* Scaling is done by accumulating the x & y scale values. When the high * +;* byte of the accumulated value is set, the pixel (for x-scaling) or * +;* the line (for y-scaling) is drawn. The high byte is then cleared, * +;* and the low byte is left so roundoffs continue to accumulate. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Drawing Procedures: * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* RSkipRout: skips given # bytes of data on the right-hand side of a * +;* shape. Just has to handle changing the current byte offset in the * +;* shape data buffer, since the draw routine knows where the left-hand * +;* side of the drawable region is. The routine may skip more bytes than * +;* it was told if it encounters a run of 0's, but it's assumed that a * +;* run of 0's will never go past the right edge of a shape. * +;* Input: * +;* ECX - number of uncompressed bytes to skip * +;* ESI - shape buffer data address * +;* Output: * +;* ECX - negative # bytes overrun, or 0 * +;* ESI - updated to the current location in the shape data * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* LSkipRout: skips given # bytes of data on the left-hand side of a * +;* shape. This routine must update the shape data byte offset, and it * +;* must properly update the current drawing position due to scaling, so * +;* it's a little more involved than the RSkip routine. The routine may * +;* skip more bytes than it's told if it encounters a run of 0's. If this * +;* happens, the draw routine must take these extra bytes into * +;* consideration. * +;* Input: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width in bytes * +;* Output: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - accumulated XTotal value at new pixel location * +;* ESI - updated to the current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* DrawRout: draws one row of pixels, handles scaling, reversal and * +;* any per pixel effects like predator, shadow etc. * +;* EDX must be set up as follows: * +;* - No scaling: 0 * +;* - No left-clipping: 0 * +;* - Left clipping, but no overrun: set to computed initial value for * +;* that viewport coordinate * +;* - Left clipping, with overrun: set to XTotal value for that coordinate * +;* In any case, only the low byte of DL should contain data; the current * +;* byte in the shapebuffer should always be the first drawable pixel, * +;* even if it's partially clipped (in which case DL will contain data). * +;* Input: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* Output: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - decremented by # bytes (not pixels) drawn * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Algorithm: * +;* - Initialize globals * +;* - Pull optional arguments off the stack * +;* - Set up drawing procedure pointers based on drawing flags * +;* - Read the values from the shape header * +;* - Compute the shape's scaled width & height * +;* - Adjust the shape's drawing coordinates based on centering & * +;* viewport-relative flag settings * +;* - Compute the clipped areas of the shape * +;* - Compute the number of drawn pixels horizontally & vertically * +;* - Compute the starting drawing offset in the viewport * +;* - Draw the shape * +;* * +;*-------------------------------------------------------------------------* +;* * +;* HISTORY: * +;* 04/13/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/26/1994 BR : Converted to 32-bit, restructured quite a bit. * +;* 08/09/1994 IML : Added C++ style interface. Various optimizations. * +;* 09/06/1994 IML : Ammendments for integration of p_* and ds_* routines.* +;* 09/14/1994 IML : Now handles LCW compression. * +;*=========================================================================* +PROC Draw_Shape C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ;-------------------------------------------------------------------- + ; Arguments: + ;-------------------------------------------------------------------- + ARG gvp_ptr:DWORD ; pointer to graphic viewport info + ARG shape_ptr:DWORD ; the shape pointer to draw + ARG draw_x:DWORD ; the destination x pixel + ARG draw_y:DWORD ; the destination y pixel + ARG flags:DWORD ; the flags for drawing the shape + +IF FALSE + ;-------------------------------------------------------------------- + ; Define the local stack variables that Draw_Shape needs. These + ; parameters are defined in shape.inc. They're included here + ; just for reference. + ;-------------------------------------------------------------------- + ; + ;...................... proc addresses .............................. + ; + LOCAL LSkipRout:DWORD ; pointer to the skip routine + LOCAL RSkipRout:DWORD ; pointer to the skip routine + LOCAL DrawRout:DWORD ; pointer to the draw routine + ; + ;.................... optional arguments ............................ + ; + LOCAL ColorTable:DWORD ; ptr to the shapes color table + LOCAL FadingTable:DWORD ; extracted fading table pointer + LOCAL FadingNum:DWORD ; get the number of times to fade + LOCAL IsTranslucent:DWORD ; ptr to "are we translucent?" tbl + LOCAL Translucent:DWORD ; ptr to "ok we are translucent!" tbl + LOCAL PriLevel:BYTE ; the priority level of the object + LOCAL ScaleX:DWORD ; the x increment to scale by + LOCAL ScaleY:DWORD ; the y increment to scale by + LOCAL ShadowingTable:DWORD ; ptr to the shadowing table + ; + ;.................... Shape header values ........................... + ; + LOCAL ShapeType:DWORD ; shape type + LOCAL ShapeWidth:DWORD ; shape's unscaled width + LOCAL ShapeHeight:DWORD ; shape's unscaled height + LOCAL UncompDataLen:DWORD ; uncompressed data length + LOCAL ShapeData:DWORD ; pointer to [compressed] shape data + ; + ;.................. Scaled shape dimensions ......................... + ; + LOCAL ScaledWidth:DWORD ; shape's scaled width + LOCAL ScaledHeight:DWORD ; shape's scaled height + ; + ;.................. Pixel clipping variables ........................ + ; + LOCAL LeftClipPixels:DWORD ; # left-clipped pixels + LOCAL RightClipPixels:DWORD ; # right-clipped pixels + LOCAL TopClipPixels:DWORD ; # top-clipped pixels + LOCAL BotClipPixels:DWORD ; # bottom-clipped pixels + LOCAL PixelWidth:DWORD ; width of drawable area in pixels + LOCAL PixelHeight:DWORD ; height of drawable area in pixels + ; + ;..................... Drawing variables ............................ + ; + LOCAL NumColors:DWORD ; # colors for 16-color shapes + LOCAL StartDraw:DWORD ; ptr to starting draw position + LOCAL NextLine:DWORD ; offset of next drawing line + LOCAL LeftClipBytes:DWORD ; # left-clipped bytes + LOCAL XTotal:DWORD ; accumulated x-pixels for scaling + LOCAL XTotalInit:DWORD ; initial roundoff bits for XTotal + LOCAL YTotal:DWORD ; accumulated y-pixels for scaling + LOCAL HeightCount:DWORD ; height counter for drawing lines + LOCAL LineStart:DWORD ; address of start of line + LOCAL WidthCount:DWORD ; counts down # bytes skipped + LOCAL StashReg:DWORD ; temp variable for draw routines + LOCAL MaskAdjust:DWORD ; priority buffer offset + LOCAL BackAdjust:DWORD ; background buffer offset + LOCAL StashECX:DWORD ; temp variable for ECX register + LOCAL StashEDX:DWORD ; temp variable for EDX register + +ENDIF + + ;==================================================================== + ; Initialization: + ; - allocate space for globals + ; - validate shape pointer + ; - set SHAPE_COMPACT flag if needed + ;==================================================================== + ;-------------------------------------------------------------------- + ; Allocate stack space for our local variables. + ;-------------------------------------------------------------------- + LOCAL Local_Stack:BYTE:Local_Size + + ;-------------------------------------------------------------------- + ; Make sure the shape pointer is not NULL + ;-------------------------------------------------------------------- + cmp [shape_ptr],0 ; compare shape ptr value to NULL + jnz ??valid_shp ; if non-zero, it's valid + jmp ??exit ; otherwise get the heck outta here + + ;-------------------------------------------------------------------- + ; Move gvp info into local variables + ;-------------------------------------------------------------------- +??valid_shp: + mov edi,[gvp_ptr] ; get pointer to graphic viewport info + mov esi, [(type GVPC_IMAGE ptr edi). vvpc . Off ] ; extract viewport pointer + mov [viewport_ptr],esi + mov ebx,[(type GVPC_IMAGE ptr edi) . vvpc . Width ] ; extract viewport width + mov [viewport_width],ebx + mov eax,[(type GVPC_IMAGE ptr edi) . vvpc . Height ] ; extract viewport height + mov [viewport_height],eax + mov ecx,[(type GVPC_IMAGE ptr edi) . vvpc . Page ] ; calculate y add value + add ecx,ebx + mov [viewport_yadd],ecx + mov eax,[(type GVPC_IMAGE ptr edi) . Xpos ] ; extract viewport x-coord + mov [viewport_x],eax + mov eax, [(type GVPC_IMAGE ptr edi) . Ypos ] ; extract viewport y-coord + mov [viewport_y],eax + mul ecx ; calculate buffer pointer + add eax,[viewport_x] + sub esi,eax + mov [buff_ptr],esi + + + ;-------------------------------------------------------------------- + ; If this shape is a compact shape, set that bit in the flags arg + ;-------------------------------------------------------------------- + mov edi,[shape_ptr] ; check for compact shape flag + test [BYTE PTR edi],MAKESHAPE_COMPACT + + jz ??do_args ; if not process flags as is + or [flags],SHAPE_COMPACT ; mark it as a compact shape + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi,4 ; optional params start past flags + + ;-------------------------------------------------------------------- + ; Initialize optional argument values: + ;-------------------------------------------------------------------- + mov [ColorTable],0 ; default = NULL + mov [FadingTable],0 ; default = NULL + mov [FadingNum],0 ; default = no fading + mov [IsTranslucent],0 ; default = NULL + mov [Translucent],0 ; default = NULL + mov [PriLevel],0 ; default = no priority + mov [ScaleX],100h ; default = unity X scaling + mov [ScaleY],100h ; default = unity Y scaling + mov [ShadowingTable],0 ; default = NULL + + ;-------------------------------------------------------------------- + ; SHAPE_COLOR: DWORD color_table[256] + ;-------------------------------------------------------------------- +??color: + test [flags],SHAPE_COLOR ; does it have a color table + jz ??fading ; if not skip to fading + or [flags],SHAPE_COMPACT ; mark it as a compact shape + ; (for remapping purposes only) + mov eax,[flags + edi] + mov [ColorTable],eax ; save address of color table + add edi,4 ; point to next optional argument + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- +??fading: + test [flags],SHAPE_FADING ; are we fading this shape + jz ??ghost ; skip to ghosting check + mov eax,[flags + edi] + mov [FadingTable],eax ; save address of fading tbl + + mov eax,[flags + edi + 4] ; get fade num + + add edi,8 ; next argument + cmp eax,0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + and [flags],NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [FadingNum],eax + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl + ;-------------------------------------------------------------------- +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz ??init_predator ; skip to predator check + mov eax,[flags + edi] + mov [IsTranslucent],eax ; save ptr to is_trans. tbl + mov eax,[flags + edi + 4] + mov [Translucent],eax ; save ptr to translucent tbl + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: Initialize the predator effect variables + ;-------------------------------------------------------------------- +??init_predator: + test [flags],SHAPE_PREDATOR ; is predator effect on + jz ??partial ; if not skip to partial + inc [PredCount] ; the pred table is byte aligned + and [PredCount],PRED_MASK ; keep entries within bounds + mov eax,[PredCount] + mov al,[BYTE PTR PredTable + eax] + mov [PredValue],eax ; put the pred value cs + mov [PartialCount],0 ; clear the partial count + mov [PartialPred],100h ; init partial to off + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??partial: + test [flags],SHAPE_PARTIAL ; is this a partial pred? + jz ??priority ; if not check priority + mov eax,[flags + edi] ; pull the partial value + mov [PartialPred],eax ; store it off + add edi,4 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PRIORITY: DWORD priority_level + ;-------------------------------------------------------------------- +??priority: + test [flags],SHAPE_PRIORITY ; is this a priority draw + jz ??scale ; if not skip to scale + mov eax,[flags + edi] + mov [PriLevel],al ; store priority level + add edi,4 ; next argument + mov eax,[MaskPage] ; calculate priority buffer + sub eax,[buff_ptr] ; offset + mov [MaskAdjust],eax + mov eax,[BackGroundPage] ; calculate background buffer + sub eax,[buff_ptr] ; offset + mov [BackAdjust],eax + + ;-------------------------------------------------------------------- + ; SHAPE_SCALING: DWORD x_scale, WORD y_scale + ;-------------------------------------------------------------------- +??scale: + test [flags],SHAPE_SCALING ; are we scaling this shape. + jz ??shadow ; if not then skip scale y value + mov eax,[flags + edi] + mov [ScaleX],eax + mov eax,[flags + edi + 4] + mov [ScaleY],eax + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_SHADOW: DWORD shadow_table[256] + ;-------------------------------------------------------------------- +??shadow: + test [flags],SHAPE_SHADOW ; are we ghosting this shape + jz short ??get_header ; if not then skip + mov eax,[flags + edi] + mov [ShadowingTable],eax ; save address of shadow table + add edi,4 ; next argument + + +??get_header: + ;==================================================================== + ; Get Shape header values + ;==================================================================== + mov esi,[shape_ptr] ; prepare to read header + movzx eax,[WORD PTR esi] + mov [ShapeType],eax ; extract shape type + movzx eax,[BYTE PTR esi + 2] + mov [ShapeHeight],eax + movzx eax,[WORD PTR esi + 3] ; extract shape height + mov [ShapeWidth],eax + movzx eax,[WORD PTR esi + 8] ; extract uncompressed data length + mov [UncompDataLen],eax + add esi,10 ; reposition index + + ;-------------------------------------------------------------------- + ; Now get NumColors, ColorTable address, & data pointer: + ; <16-color shape: + ; shape.Colortable[0] = # colors + ; shape data is after that many colors + ; 16-color shape: + ; shape.Colortable[] contains colors + ; shape data is after those colors + ; default 256-color shape: + ; shape data starts at shape.Colortable[0] + ; Note: ColorTable is set only if flags & SHAPE_COLOR is 0; otherwise, + ; the color table was passed in & we already have a pointer to it + ;-------------------------------------------------------------------- + ; + ;....................... <16-color shape: ........................... + ; + test [ShapeType],MAKESHAPE_VARIABLE + jz ??check_16 + movzx eax,[BYTE PTR esi] ; read # colors + mov [NumColors],eax ; save # colors + inc esi + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??norm_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??norm_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + + ;....................... 16-color shape: ............................ +??check_16: + test [ShapeType],MAKESHAPE_COMPACT + jz ??256_get_data_addr + mov [NumColors],16 ; save # colors + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??16_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??16_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + ; + ;....................... 256-color shape: ........................... + ; +??256_get_data_addr: + mov [ShapeData],esi ; set data address + + ;==================================================================== + ; Set up the drawing procedure addresses + ;==================================================================== + ;-------------------------------------------------------------------- + ; This code uses HORZ_REV, VERT_REV, & SCALING flags as an + ; offset into the LSkipTable, RSkipTable, and DrawTable. These + ; flags combined have values from 00h-07h, so each table must have + ; at least 8 entries. + ;-------------------------------------------------------------------- +??setup_procs: + mov ebx,[flags] ; load flags value + and ebx,07h ; clip high bits + add ebx,ebx ; mult by 4 to get DWORD offset + add ebx,ebx + mov eax,[LSkipTable + ebx] ; get table value + mov [LSkipRout],eax ; store it in the function pointer + mov eax,[RSkipTable + ebx] ; get table value + mov [RSkipRout],eax ; store it in the function pointer + mov eax,[DrawTable + ebx] ; get table value + mov [DrawRout],eax ; store it in the function pointer + +??compute_scalevals: + ;==================================================================== + ; Now compute scaled width & height. If the shape scales down to 0 + ; either horizontally or vertically, exit. + ;==================================================================== + test [flags],SHAPE_SCALING ; skip if no scaling + jz ??no_scaling + ; + ;........................ scaled width: ............................. + ; + mov eax,[ShapeWidth] ; get byte width + mov ebx,[ScaleX] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledWidth],eax ; save the scaled width + ; + ;........................ scaled height: ............................ + ; + mov eax,[ShapeHeight] ; get byte height + mov ebx,[ScaleY] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledHeight],eax ; save the scaled height + jmp ??handle_centering + ; + ;......................... no scaling: .............................. + ; +??no_scaling: + mov eax,[ShapeWidth] + mov [ScaledWidth],eax ; pixel width = byte width + mov eax,[ShapeHeight] + mov [ScaledHeight],eax ; pixel height = byte height + + ;==================================================================== + ; Allow for SHAPE_CENTER by adjusting the draw_x & draw_y arguments: + ; draw_x -= ScaledWidth / 2 + ; draw_y -= ScaledHeight / 2 + ;==================================================================== +??handle_centering: + ; + ;........................ adjust draw_x ............................. + ; + test [flags],SHAPE_CENTER ; skip if not centered + jz ??handle_vp_rel + mov eax,[draw_x] ; load in draw_x + mov edx,[ScaledWidth] ; load in ScaledWidth + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_x],eax ; store it back into draw_x + ; + ;........................ adjust draw_y ............................. + ; + mov eax,[draw_y] ; load in draw_y + mov edx,[ScaledHeight] ; load in ScaledHeight + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_y],eax ; store it back into draw_y + + ;==================================================================== + ; Allow for SHAPE_VIEWPORT_REL by adjusting draw_x & draw_y by the + ; viewport's coordinates + ;==================================================================== +??handle_vp_rel: + test [flags],SHAPE_VIEWPORT_REL ; skip if not vp-relative + jz ??compute_horz_clip + mov eax,[viewport_x] + add [draw_x],eax ; draw_x += viewport_x + mov eax,[viewport_y] + add [draw_y],eax ; draw_y += viewport_y + + ;==================================================================== + ; Now that we have the scaled size and adjusted x & y drawing + ; coordinates, we can compute the clipped areas of the shape: + ; LeftClipPixels = viewport_x - draw_x + ; - if negative, set to 0 + ; RightClipPixels = (draw_x + ScaledWidth) - + ; (viewport_x + viewport_width) + ; - if negative, set to 0 + ; + ; TopClipPixels = viewport_y - draw_y + ; - if negative, set to 0 + ; BotClipPixels = (draw_y + ScaledHeight) - + ; (viewport_y + viewport_height) + ; - if negative, set to 0 + ;==================================================================== +??compute_horz_clip: + ; + ;...................... left-clipped pixels ......................... + ; + mov eax,[viewport_x] + sub eax,[draw_x] ; EAX = viewport_x - draw_x + jge ??set_left_clip + mov eax,0 ; if EAX<0, set to 0 +??set_left_clip: + mov [LeftClipPixels],eax ; store # left-clipped pixels + ; + ;...................... right-clipped pixels ........................ + ; + mov eax,[draw_x] + add eax,[ScaledWidth] ; EAX = draw_x + ScaledWidth + mov edx,[viewport_x] + add edx,[viewport_width] ; EDX = viewport_x + viewport_width + sub eax,edx + jge ??set_right_clip + mov eax,0 ; if EAX<0, set to 0 +??set_right_clip: + mov [RightClipPixels],eax ; store # right-clipped pixels + ; + ;...................... top-clipped pixels .......................... + ; +??compute_vert_clip: + mov eax,[viewport_y] + sub eax,[draw_y] ; EAX = viewport_y - draw_y + jge ??set_top_clip + mov eax,0 ; if EAX<0, set to 0 +??set_top_clip: + mov [TopClipPixels],eax ; store # top-clipped pixels + ; + ;.................... bottom-clipped pixels ......................... + ; + mov eax,[draw_y] + add eax,[ScaledHeight] ; EAX = draw_y + ScaledHeight + mov edx,[viewport_y] + add edx,[viewport_height] ; EDX = viewport_y + viewport_height + sub eax,edx + jge ??set_bottom_clip + mov eax,0 ; if EAX<0, set to 0 +??set_bottom_clip: + mov [BotClipPixels],eax ; store # bottom-clipped pixels + + ;==================================================================== + ; Now compute the number of pixels actually drawn, horizontally and + ; vertically; exit if either is <= 0 + ;==================================================================== +??compute_drawn_pixels: + ; + ;.................... pixel width of drawn area ..................... + ; + mov eax,[ScaledWidth] ; get total width in pixels + sub eax,[LeftClipPixels] ; subtract off left-clipped pixels + sub eax,[RightClipPixels] ; subtract off right-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelWidth],eax ; store drawn pixel width + ; + ;.................... pixel height of drawn area .................... + ; + mov eax,[ScaledHeight] ; get total height in pixels + sub eax,[TopClipPixels] ; subtract off top-clipped pixels + sub eax,[BotClipPixels] ; subtract off bottom-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelHeight],eax ; store drawn pixel height + + ;==================================================================== + ; So, we're actually going to draw something; if (ShapeType & + ; MAKESHAPE_NOCOMP == 0) decompress the shape data into _ShapeBuffer: + ; LCW_Uncompress(ShapeData, _ShapeBuffer, UncompDataLen); + ; shape.DataLength + ; &_ShapeBuffer + ; &(shape's data) + ; - otherwise the shape data is already uncompressed + ;==================================================================== + test [ShapeType],MAKESHAPE_NOCOMP + jnz ??uncompressed + + mov eax,[UncompDataLen] + push eax ; push arg 3 + mov eax,[_ShapeBuffer] + push eax ; push arg 2 + mov eax,[ShapeData] + push eax ; push arg 1 + call LCW_Uncompress ; call routine + add esp,12 ; restore stack + mov eax,[_ShapeBuffer] + mov [ShapeData],eax + jmp ??copy_flags + +??uncompressed: +; mov eax,[ShapeData] ; set up pointer to shape data +; mov [_ShapeBuffer],eax + + + ;-------------------------------------------------------------------- + ; Set the global Flags variable + ;-------------------------------------------------------------------- +??copy_flags: + mov eax,[flags] + mov [Flags],eax + + ;==================================================================== + ; Now compute the actual buffer offset where drawing (not skipping) + ; will begin + ;==================================================================== + ;-------------------------------------------------------------------- + ; First, compute the x & y offsets of the shape's clipped upper-left + ; corner, relative to the viewport's upper-left corner: + ; x-offset = draw_x + LeftClipPixels - viewport_x + ; y-offset = draw_y + TopClipPixels - viewport_y + ;-------------------------------------------------------------------- + mov ebx,[draw_x] + add ebx,[LeftClipPixels] + sub ebx,[viewport_x] ; EBX = viewport x-offset + + mov eax,[draw_y] + add eax,[TopClipPixels] + sub eax,[viewport_y] ; EAX = viewport y-offset + + ;-------------------------------------------------------------------- + ; Then, adjust the viewport offsets due to horizontal & vertical + ; reversal: + ; if HORZ_REV, x-offset += (PixelWidth - 1) + ; if VERT_REV, y-offset += (PixelHeight - 1) + ;-------------------------------------------------------------------- + ; + ;................. Adjust for horizontal reversal ................... + ; + test [flags],SHAPE_HORZ_REV + jz ??adjust_vert_offset + add ebx,[PixelWidth] + dec ebx ; EBX = true x-offset + ; + ;................ Swap LeftClip & RightClip pixels .................. + ; + mov edx,[LeftClipPixels] ; exchange left & right-clipped pixels + xchg edx,[RightClipPixels] + mov [LeftClipPixels],edx + + ; + ;.................. Adjust for vertical reversal .................... + ; +??adjust_vert_offset: + test [flags],SHAPE_VERT_REV + jz ??adjust_pointer + add eax,[PixelHeight] + dec eax ; EAX = true y-offset + ; + ;.................. Swap TopClip & BotClip pixels ................... + ; + mov edx,[TopClipPixels] + xchg edx,[BotClipPixels] + mov [TopClipPixels],edx + + ;-------------------------------------------------------------------- + ; Now, adjust the starting position pointer: + ;-------------------------------------------------------------------- +??adjust_pointer: ;!!!!!!! convert to register mul for speed !!!!!!!! + add ebx,[viewport_ptr] ; add initial ptr to x-offset + mul [viewport_yadd] ; convert y-offset (EAX) to bytes + add ebx,eax ; add those bytes in + mov [StartDraw],ebx ; store the starting pointer + + ;-------------------------------------------------------------------- + ; Finally, if VERT_REV, negate yadd to move up not down: + ;-------------------------------------------------------------------- + test [flags],SHAPE_VERT_REV + jz ??init_xtotal + neg [viewport_yadd] ; move up, not down + + ;==================================================================== + ; Initialize the horizontal scale accumulation value: + ; If there are any left-clipped pixels, the scale accumulator will + ; have to be initialized with the value it >would< have by stepping + ; over that many pixels. This initial value can be computed by + ; dividing the # of left-clipped pixels by the x-scale value itself, + ; picking off the remainder from this division & negating it. This + ; sets the low byte of the remainder to the correct accumulation + ; value (the high bytes will be garbage). + ; (The alternative to this approach would be to multiply the + ; scale factor by the # clipped bytes, which is the result of the + ; division; however, negating the remainder is much faster than + ; the multiply would be.) + ;==================================================================== +??init_xtotal: + mov edx,0 ; prepare for divide + mov eax,[LeftClipPixels] ; get # left-clipped pixels + shl eax,8 ; multiply by 100h + mov ebx,[ScaleX] ; load ScaleX value + div bx ; 16-bit div: AX = rslt, DX = rem + mov [LeftClipBytes],eax ; save # left-clipped bytes + neg edx ; generate roundoff bits + and edx,0Fh ; only save low byte + mov [XTotalInit],edx ; save initial roundoff value + + ;==================================================================== + ; Initialize drawing variables: + ;==================================================================== + mov esi,[ShapeData] ; ESI = shape buffer starting point + mov edi,[StartDraw] ; EDI = drawing address + mov [YTotal],0 ; initialize accumulated scale + + ;==================================================================== + ; Clip the top-clipped lines. The object here is to set ESI to the + ; first drawable line in the _ShapeBuffer, and YTotal to: + ; high byte = # times to draw that line, + ; low byte = roundoff bits + ; + ; - Initialize values (ESI, HeightCount, YTotal) + ; - Skip loop if no top lines to clip + ; - Loop: + ; - save this line's byte position in _ShapeBuffer, in case we + ; overrun + ; - call RSkipRout with ECX set to # bytes to skip (one row) + ; - accumulate ScaleY into YTotal + ; - if high byte is non-zero, there are that many drawn lines: + ; - decrement HeightCount by that many lines + ; - clear the high byte in YTotal, but keep the roundoff bits + ; - if HeightCount > 0, loop again to clip more lines + ; - if HeightCount is 0, start drawing: + ; - ESI points to first non-clipped line in _ShapeBuffer + ; - YTotal contains 0 in high byte, roundoff bits in low byte + ; - otherwise, we've clipped too many lines: + ; - put ESI back to the line we just clipped + ; - set high byte of YTotal to # lines overrun + ; - subtract ScaleY from YTotal, to set it up for drawing loop + ;==================================================================== + ; + ;..................... skip if nothing to clip ...................... + ; + mov eax,[TopClipPixels] + cmp eax,0 ; see if any top-clipped pixels + jz ??draw_loop ; if not, skip this + mov [HeightCount],eax ; save off # lines to clip + +??clip_y_loop: + ; + ;...................... skip this row of bytes ...................... + ; + mov [LineStart],esi ; save this line's byte position + mov ecx,[ShapeWidth] ; set up ECX for RSkipRout + call [RSkipRout] ; skip 'ShapeWidth' bytes + ; + ;............... see if this row would have been drawn .............. + ; + mov eax,[ScaleY] + add [YTotal],eax ; accumulate scale factor + test [YTotal],0FF00h ; check to see if we draw the line + jz ??clip_y_loop ; if not loop again + ; + ;...................... decrement HeightCount ....................... + ; + mov eax,0 ; clear EAX + xchg al,[BYTE PTR YTotal+1] ; get # lines, clear it in YTotal + sub [HeightCount],eax ; subtract # drawn lines from HtCt + jg ??clip_y_loop ; if more lines remain, loop again + jns ??draw_loop ; is exactly 0; we're done clipping + ; + ;....................... adjust for overrun ......................... + ; + mov esi,[LineStart] ; point ESI back to this line + mov eax,[HeightCount] + neg eax ; EAX = # lines overrun + shl eax,8 ; multiply by 100h + add eax,[YTotal] ; add in roundoff bits + sub eax,[ScaleY] ; adjust down by y-scale + mov [YTotal],eax ; store in YTotal + + ;==================================================================== + ; The drawing loop (at long last!): + ; - Accumulate YTotal; if high byte is 0, skip this row of bytes & + ; loop again + ; - Skip left-clipped pixels: + ; - If we've skipped all the bytes on the line, just go to the + ; next line + ; - Draw middle pixels: + ; - Add the shape's pixel width to ECX (which could be negative + ; if we left-skipped into the drawable area) + ; - If ECX is still 0, there are no pixels to draw + ; - Otherwise, leave ECX as is & draw the pixels + ; - Skip right-clipped pixels: + ; - Add # right-clipped pixels to ECX (which could be negative if + ; the draw routine uncompressed 0's into the right-clipped + ; region) + ; - if ECX > 0, skip the remaining bytes + ; - Go to the next line: + ; - point EDI to the start of the next line in the viewport + ; - decrement the height counter, exit if it's 0 + ; - decrement YTotal's high byte + ; - if it's 0, go to the loop top + ; - otherwise, reset ESI to this line's start & redraw the line, + ; starting at left-clipped pixels + ; (NOTE: why not start drawing at middle pixels??????????) + ;==================================================================== +??draw_loop: + ; + ;................... accumulate YTotal & test it .................... + ; + mov eax,[ScaleY] ; get y scaling factor + add [YTotal],eax ; accumulate YTotal + test [YTotal],0FF00h ; see if we need to draw anything + jnz ??draw_line ; draw this line + ; + ;......................... skip this line ........................... + ; + mov ecx,[ShapeWidth] ; load shape's width in bytes + call [RSkipRout] ; skip this row & loop again + jmp ??draw_loop + + ; + ;--------------------- start drawing this line ---------------------- + ; +??draw_line: + mov [LineStart],esi ; save current byte position + ;.................................................................... + ; Skip left-clipped pixels: + ; - initialize [WidthCount] to total shape width in bytes + ; - set ECX to # >bytes< to clip + ; When LSkipRout returns: + ; - ECX will contain # >pixels< overrun + ; - EDX will contain the XTotal init value + ; - [WidthCount] will be decremented by total bytes skipped + ;.................................................................... +??draw_left: + mov eax,[ShapeWidth] ; load shape width + mov [WidthCount],eax ; set up for LSkipRout + mov ecx,[LeftClipBytes] ; bytes, not pixels! + call [LSkipRout] ; skip the bytes + cmp [WidthCount],0 + jz ??next_line ; The whole line was 0's + ;.................................................................... + ; Draw middle pixels: + ; - add PixelWidth to ECX (which may be negative) + ; - if ECX is 0, don't bother drawing + ; When DrawRout returns: + ; - ECX will contain # >pixels< overrun + ; - [WidthCount] will be decremented by # bytes drawn + ;.................................................................... +??draw_middle: + add ecx,[PixelWidth] ; since ECX could overrun, add width + jle ??draw_right ; if ECX<=0, no middle pixels to draw + call [DrawRout] ; draw the pixels + ; + ;................... skip past right-clipped pixels ................. + ; +??draw_right: + mov ecx,[WidthCount] ; ECX = remaining # bytes + jecxz ??next_line ; don't bother if no bytes remain + call [RSkipRout] ; skip right-clipped bytes + ; + ;----------------------- go to the next line ------------------------ + ; +??next_line: + ; + ;................. adjust EDI to start of next line ................. + ; + mov eax,[viewport_yadd] ; get yadd + add [StartDraw],eax ; add it to this line's position + mov edi,[StartDraw] ; EDI = next line + ; + ;................. decrement our pixel row counter .................. + ; + dec [PixelHeight] ; count down a line + jz ??exit ; we're done! + ; + ;.................. decrement YTotal's high byte .................... + ; + dec [BYTE PTR YTotal + 1] ; decrement high byte + jz ??draw_loop ; draw next line if 0 + ; + ;....................... re-draw this line .......................... + ; + mov esi,[LineStart] ; reset to this line's start + jmp ??draw_left ; redraw this line + +??exit: + ret + + ENDP Draw_Shape + + +;*************************************************************************** +;* Not_Supported -- Replacement function for Draw_Shape routines not used. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 08/24/1993 SKB : Created. * +;*=========================================================================* +PROC Not_Supported NOLANGUAGE NEAR + + ret + + ENDP Not_Supported + + END + +;************************** End of drawshp.asm ***************************** + + diff --git a/WIN32LIB/SHAPE/DS_DN.ASM b/WIN32LIB/SHAPE/DS_DN.ASM new file mode 100644 index 0000000..b0a8ac5 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_DN.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Normal NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - add it to EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp short ??test_fading + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Normal + + END + +;**************************** End of ds_dn.asm ***************************** diff --git a/WIN32LIB/SHAPE/DS_DR.ASM b/WIN32LIB/SHAPE/DS_DR.ASM new file mode 100644 index 0000000..b46629c --- /dev/null +++ b/WIN32LIB/SHAPE/DS_DR.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Reverse NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - subtract it from EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Reverse + + + END + +;**************************** End of ds_dr.asm ***************************** diff --git a/WIN32LIB/SHAPE/DS_DS.ASM b/WIN32LIB/SHAPE/DS_DS.ASM new file mode 100644 index 0000000..a8d312f --- /dev/null +++ b/WIN32LIB/SHAPE/DS_DS.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + ENDP Draw_Scale + + END + +;**************************** End of ds_ds.asm ****************************** diff --git a/WIN32LIB/SHAPE/DS_DSR.ASM b/WIN32LIB/SHAPE/DS_DSR.ASM new file mode 100644 index 0000000..fcd6180 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_DSR.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DSR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*******p******************************************************************** +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale_Reverse NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + + ENDP Draw_Scale_Reverse + + END + +;*************************** End of ds_dsr.asm ****************************** diff --git a/WIN32LIB/SHAPE/DS_LRS.ASM b/WIN32LIB/SHAPE/DS_LRS.ASM new file mode 100644 index 0000000..a4d85e4 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_LRS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + add edi,ecx ; decrement EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Reverse_Skip + + END + +;*************************** End of ds_lrs.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_LS.ASM b/WIN32LIB/SHAPE/DS_LS.ASM new file mode 100644 index 0000000..cbc2ae6 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_LS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + sub edi,ecx ; increment EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Skip + + END + +;**************************** End of ds_ls.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_LSRS.ASM b/WIN32LIB/SHAPE/DS_LSRS.ASM new file mode 100644 index 0000000..77effd1 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_LSRS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + sub esi,eax ; decrement ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Reverse_Skip + + END + +;**************************** End of ds_lsrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_LSS.ASM b/WIN32LIB/SHAPE/DS_LSS.ASM new file mode 100644 index 0000000..bf8221e --- /dev/null +++ b/WIN32LIB/SHAPE/DS_LSS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/08/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + add esi,eax ; increment ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Skip + + END + +;**************************** End of ds_lss.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_RRS.ASM b/WIN32LIB/SHAPE/DS_RRS.ASM new file mode 100644 index 0000000..c90e468 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_RRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Reverse_Skip + + END + +;**************************** End of ds_rrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_RS.ASM b/WIN32LIB/SHAPE/DS_RS.ASM new file mode 100644 index 0000000..caec534 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_RS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Skip + + END + +;**************************** End of ds_rs.asm ****************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_RSRS.ASM b/WIN32LIB/SHAPE/DS_RSRS.ASM new file mode 100644 index 0000000..3315082 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_RSRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Reverse_Skip + + END + +;*************************** End of ds_rsrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_RSS.ASM b/WIN32LIB/SHAPE/DS_RSS.ASM new file mode 100644 index 0000000..798e207 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_RSS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Skip -- Skips past a scaled row of pixels on right side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Skip -- Skips past a scaled row of pixels on the right side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Skip + + END + +;*************************** End of ds_rss.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SHAPE/DS_TABLE.ASM b/WIN32LIB/SHAPE/DS_TABLE.ASM new file mode 100644 index 0000000..a351aa0 --- /dev/null +++ b/WIN32LIB/SHAPE/DS_TABLE.ASM @@ -0,0 +1,187 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_TABLE.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;* This module sets up a table of procedure addresses for combinations of * +;* NORMAL, HORZ_REV and SCALING flags. * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;******************************** Equates ********************************** +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Draw_Shape. * +;*=========================================================================*/ +USE_NORMAL EQU TRUE +USE_HORZ_REV EQU TRUE +USE_VERT_REV EQU TRUE +USE_SCALING EQU TRUE + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IFNB + GLOBAL varname:DWORD + ENDIF + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + + +;--------------------------------------------------------------------------- +; Data Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; LSkipTable +; RSkipTable +; DrawTable +;--------------------------------------------------------------------------- + + DATASEG + +;--------------------------------------------------------------------------- + +WANT equ +USE Left_Skip, LSkipTable + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Skip + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Right_Skip, RSkipTable + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Skip + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Draw_Normal, DrawTable + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Normal + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse +;--------------------------------------------------------------------------- + +;--------------------------------------------------------------------------- + END + +;************************** End of ds_table.asm **************************** diff --git a/WIN32LIB/SHAPE/GETSHAPE.CPP b/WIN32LIB/SHAPE/GETSHAPE.CPP new file mode 100644 index 0000000..bcb4ba3 --- /dev/null +++ b/WIN32LIB/SHAPE/GETSHAPE.CPP @@ -0,0 +1,362 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ + diff --git a/WIN32LIB/SHAPE/MAKEFILE b/WIN32LIB/SHAPE/MAKEFILE new file mode 100644 index 0000000..00116a2 --- /dev/null +++ b/WIN32LIB/SHAPE/MAKEFILE @@ -0,0 +1,194 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = shape +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getshape.obj & + shape.obj +# prioinit.obj & +# drawshp.obj & +# ds_dn.obj & +# ds_dr.obj & +# ds_ds.obj & +# ds_dsr.obj & +# ds_lrs.obj & +# ds_ls.obj & +# ds_lsrs.obj & +# ds_lss.obj & +# ds_rrs.obj & +# ds_rs.obj & +# ds_rsrs.obj & +# ds_rss.obj & +# ds_table.obj & +# setshape.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/SHAPE/MAKEFILE.BOR b/WIN32LIB/SHAPE/MAKEFILE.BOR new file mode 100644 index 0000000..a006c3a --- /dev/null +++ b/WIN32LIB/SHAPE/MAKEFILE.BOR @@ -0,0 +1,195 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = shape +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getshape.obj & + prioinit.obj & + drawshp.obj & + ds_dn.obj & + ds_dr.obj & + ds_ds.obj & + ds_dsr.obj & + ds_lrs.obj & + ds_ls.obj & + ds_lsrs.obj & + ds_lss.obj & + ds_rrs.obj & + ds_rs.obj & + ds_rsrs.obj & + ds_rss.obj & + ds_table.obj & + setshape.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/SHAPE/MAKEFILE.WAT b/WIN32LIB/SHAPE/MAKEFILE.WAT new file mode 100644 index 0000000..a006c3a --- /dev/null +++ b/WIN32LIB/SHAPE/MAKEFILE.WAT @@ -0,0 +1,195 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = shape +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getshape.obj & + prioinit.obj & + drawshp.obj & + ds_dn.obj & + ds_dr.obj & + ds_ds.obj & + ds_dsr.obj & + ds_lrs.obj & + ds_ls.obj & + ds_lsrs.obj & + ds_lss.obj & + ds_rrs.obj & + ds_rs.obj & + ds_rsrs.obj & + ds_rss.obj & + ds_table.obj & + setshape.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/SHAPE/PRIOINIT.CPP b/WIN32LIB/SHAPE/PRIOINIT.CPP new file mode 100644 index 0000000..d69706c --- /dev/null +++ b/WIN32LIB/SHAPE/PRIOINIT.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : PRIOINIT.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : August 9, 1993 * + * * + * Last Update : August 9, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * INIT_PRIORITY_SYSTEM -- Sets the buffer addresses for the priority * + * system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "shape.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Init_Priority_System -- set the buffer addresses for the priority * + * system. * + * * + * INPUT: * + * mask - pointer to priority buffer class * + * back - pointer to background buffer class * + * * + * OUTPUT: * + * none * + * * + * HISTORY: * + * 08/09/1994 IML : Created. * + *=========================================================================*/ + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back) +{ + MaskPage = mask->Get_Buffer(); + BackGroundPage = back->Get_Buffer(); +} + + +/************************** end of prioinit.cpp ****************************/ diff --git a/WIN32LIB/SHAPE/SETSHAPE.ASM b/WIN32LIB/SHAPE/SETSHAPE.ASM new file mode 100644 index 0000000..52cf574 --- /dev/null +++ b/WIN32LIB/SHAPE/SETSHAPE.ASM @@ -0,0 +1,81 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SETSHAPE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : October 26, 1994 * +;* * +;* Last Update : October 26, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Shape_Buffer -- Sets the shape buffer to the given pointer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + diff --git a/WIN32LIB/SHAPE/SHAPE.ASM b/WIN32LIB/SHAPE/SHAPE.ASM new file mode 100644 index 0000000..08372c8 --- /dev/null +++ b/WIN32LIB/SHAPE/SHAPE.ASM @@ -0,0 +1,94 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : April 13, 1992 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* Not_Supported -- Replacement function for Draw_Shape routines not used* +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "shape.inc" + +global C ShapeBuffer :dword +global C ShapeBufferSize :dword +global C _ShapeBuffer :dword +global C _ShapeBufferSize :dword +global C Set_Shape_Buffer :near + +DATASEG +label ShapeBuffer dword +_ShapeBuffer dd 0 + +label ShapeBufferSize dword +_ShapeBufferSize dd 0 + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL C Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + diff --git a/WIN32LIB/SHAPE/SHAPE.H b/WIN32LIB/SHAPE/SHAPE.H new file mode 100644 index 0000000..2c8f7d2 --- /dev/null +++ b/WIN32LIB/SHAPE/SHAPE.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_FADING = 0x0100, // Fading effect (void * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (void * color_table) +} ShapeFlags_Type; + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + unsigned short ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + unsigned char Height; // Height of the shape in scan lines + unsigned short Width; // Width of the shape in bytes + unsigned char OriginalHeight; // Original height of shape in scan lines + unsigned short ShapeSize; // Size of the shape, including header + unsigned short DataLength; // Size of the uncompressed shape (just data) + unsigned char Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +typedef struct { + unsigned short NumShapes; // number of shapes in the block + long Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern void *MaskPage; +extern void *BackGroundPage; +extern long _ShapeBufferSize; +extern char *_ShapeBuffer; +} + + +void __cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +int Draw_Shape(GraphicViewPortClass *gvp, void const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short __cdecl Get_Shape_Data(void const *shape, int data); +int __cdecl Extract_Shape_Count(void const *buffer); +void * __cdecl Extract_Shape(void const *buffer, int shape); +int __cdecl Restore_Shape_Height(void *shape); +int __cdecl Set_Shape_Height(void const *shape, int newheight); + +extern "C" { +int __cdecl Get_Shape_Width(void const *shape); +int __cdecl Get_Shape_Height(void const *shape); +int __cdecl Get_Shape_Original_Height(void const *shape); +int __cdecl Get_Shape_Uncomp_Size(void const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +void __cdecl Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +int __cdecl Get_Shape_Flags(void const *shape); +int __cdecl Get_Shape_Size(void const *shape); +int __cdecl Get_Shape_Scaled_Width(void const *shape, int scale); +int __cdecl Get_Shape_Scaled_Height(void const *shape, int scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + diff --git a/WIN32LIB/SHAPE/SHAPE.INC b/WIN32LIB/SHAPE/SHAPE.INC new file mode 100644 index 0000000..b5aa807 --- /dev/null +++ b/WIN32LIB/SHAPE/SHAPE.INC @@ -0,0 +1,214 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL C ShapeBuffer:DWORD +GLOBAL C ShapeBufferSize:DWORD +GLOBAL C _MaskPage:DWORD +GLOBAL C _BackGroundPage:DWORD +GLOBAL C PredCount:DWORD +GLOBAL C PredTable:BYTE +GLOBAL C PredValue:DWORD +GLOBAL C PartialPred:DWORD +GLOBAL C PartialCount:DWORD +GLOBAL C Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/WIN32LIB/SRCDEBUG/ALLOC.CPP b/WIN32LIB/SRCDEBUG/ALLOC.CPP new file mode 100644 index 0000000..932c484 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/ALLOC.CPP @@ -0,0 +1,512 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; +extern void (*Memory_Error_Exit)(char *string)=NULL; + + +//#define MEM_CHECK + +#ifdef MEM_CHECK +extern "C"{ + extern void __cdecl Int3(void); +} +#endif //MEM_CHECK + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *, long const ) +{ +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *, long const ) +{ +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + * 09/28/1995 ST : Simplified for win95 * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + +#ifdef WIN32 + + void *mem_ptr; + +#ifdef MEM_CHECK + bytes_to_alloc += 32; +#endif //MEM_CHECK + + mem_ptr = malloc ( bytes_to_alloc ); + + if ( !mem_ptr && Memory_Error ){ + Memory_Error(); + } + + if ( mem_ptr && ( flags & MEM_CLEAR ) ){ + memset ( mem_ptr , 0 , bytes_to_alloc ); + } + +#ifdef MEM_CHECK + mem_ptr = (void*)((char*)mem_ptr + 16); + unsigned long *magic_ptr =(unsigned long*) ( ((char *)mem_ptr) - 16 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = bytes_to_alloc - 32; + magic_ptr = (unsigned long*) ( ((char*)mem_ptr) + bytes_to_alloc - 32 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = (unsigned long)mem_ptr; +#endif //MEM_CHECK + + Memory_Calls++; + return ( mem_ptr ); + +#else + + + + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); + +#endif +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +#ifdef WIN32 + +void Free(void const *pointer) +{ + + if ( pointer ){ + +#ifdef MEM_CHECK + + unsigned long *magic_ptr = (unsigned long*) ( ((char*)pointer) - 16 ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + magic_ptr = (unsigned long*) ( ((char*)pointer) + *magic_ptr ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + pointer = (void*) (((char*)pointer)-16); +#endif //MEM_CHECK + + free ( (void*)pointer ); + Memory_Calls--; + } + +#else + +void Free(void const *pointer) +{ + + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } + +#endif +} + + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + return ( 64*1024*1024 ); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + + return ( 64*1024*1024 ); +} + diff --git a/WIN32LIB/SRCDEBUG/APROFILE.ASM b/WIN32LIB/SRCDEBUG/APROFILE.ASM new file mode 100644 index 0000000..7562b3a --- /dev/null +++ b/WIN32LIB/SRCDEBUG/APROFILE.ASM @@ -0,0 +1,298 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Profiler * +;* * +;* File Name : APROFILE.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : November 17th, 1995 * +;* * +;* Last Update : November 20th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* __PRO -- must be called at the beginning of each function * +;* __EPI -- must be called at the end of each function * +;* Copy_CHL -- initialise the profiler asm data * +;* Profiler_Callback -- windows callback for millisecond timer * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + + p386 + model flat + ideal + jumps + + +MAX_PROFILE_TIME = 60*1 ;1 minute(s) +PROFILE_RATE = 1000 ;1000 samples per sec + + +; +; Externs +; +; +global C ProfileFunctionAddress:dword +global C ProfilePtr:dword +global C ProfileList:dword +global C Stop_Profiler:near +global New_Profiler_Callback_:near +global Old_Profiler_Callback_:near +global C Profile_Init:near +global C Profile_End:near +global ___begtext:near +global BaseAddress:dword +global __PRO:near +global __EPI:near +global MyStack:dword +global MyStackPtr:dword +global ProAddress:dword +global EpiAddress:dword + + + codeseg + + +;********************************************************************************************* +;* __PRO -- registers the current procedure * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that ss:Esp points to return address in function to be registered * +;* * +;* HISTORY: * +;* 11/20/95 4:39PM ST : Created. * +;*===========================================================================================* + +proc __PRO near + + jmp [ProAddress] +; safe version of prologue code +Pro_Start: push eax + mov eax,[MyStackPtr] + push [ProfileFunctionAddress] + pop [eax*4+MyStack] + inc [MyStackPtr] + pop eax + push [dword ss:esp] + pop [ProfileFunctionAddress] +Pro_End: ret + +; unsafe (but much faster) prologue code +; pop [ProfileFunctionAddress] +; jmp [ProfileFunctionAddress] + +endp __PRO + + +;********************************************************************************************* +;* __EPI -- Registers the privious procedure as current again * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that calling procedure will pop ebp immediately on return so we dont have to * +;* preserve it. * +;* * +;* HISTORY: * +;* 11/20/95 4:42PM ST : Created. * +;*===========================================================================================* + +proc __EPI near + + jmp [EpiAddress] +; Safe version of epilogue code. Uncomment the push and pop for ultimate safety +Epi_Start: dec [MyStackPtr] +; push ebp + mov ebp,[MyStackPtr] + mov ebp,[ebp*4+MyStack] + mov [ProfileFunctionAddress],ebp +; pop ebp +Epi_End: ret + +; Unsafe (but much faster) epilogue code. Makes lots of assumptions. +; push [dword esp+8] +; pop [ProfileFunctionAddress] +; ret + +endp __EPI + + + +;********************************************************************************************* +;* Profile_Init -- Initialises the .asm data required for profile session * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* Assumes that '___begtext' is the first label in the code segment and that its * +;* address is within 15 bytes of the start of the code segment * +;* * +;* HISTORY: * +;* 11/20/95 4:44PM ST : Created. * +;*===========================================================================================* + +proc Profile_Init C near + + mov eax,offset ___begtext + and eax,0fffffff0h + mov [BaseAddress],eax + ;mov [MyStackPtr],0 + mov [ProfileList],PROFILE_RATE + mov [ProfilePtr],1 + mov [ProAddress],offset Pro_Start + mov [EpiAddress],offset Epi_Start + ret + +endp Profile_Init + + + + + +;********************************************************************************************* +;* Profile_End -- disables the __PRO and __EPI procedures * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/20/95 4:44PM ST : Created. * +;*===========================================================================================* + +proc Profile_End C near + + mov [ProAddress],offset Pro_End + mov [EpiAddress],offset Epi_End + ret + +endp Profile_End + + + + + + +;********************************************************************************************* +;* New_Profiler_Callback -- Windows callback used to register function hits * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: none * +;* * +;* Note: * +;* The frequency that this is called depends on MAX_PROFILE_RATE defined here and in * +;* profile.h * +;* * +;* HISTORY: * +;* 11/20/95 4:47PM ST : Created. * +;*===========================================================================================* +proc New_Profiler_Callback_ near + + push eax + push esi + mov esi,[ProfilePtr] + cmp esi,MAX_PROFILE_TIME*PROFILE_RATE + jge @@out + mov eax,[ProfileFunctionAddress] + sub eax,[BaseAddress] + mov [ProfileList+esi*4],eax + inc [ProfilePtr] + pop esi + pop eax + ret + +@@out: call Stop_Profiler + pop esi + pop eax + ret + +endp New_Profiler_Callback_ + + + +;********************************************************************************************* +;* Old_Profiler_Callback -- Windows callback used to register function hits * +;* * +;* INPUT: Windows timer callback stuff - not used * +;* * +;* OUTPUT: none * +;* * +;* Note: * +;* The frequency that this is called depends on MAX_PROFILE_RATE defined here and in * +;* profile.h * +;* * +;* HISTORY: * +;* 11/20/95 4:47PM ST : Created. * +;*===========================================================================================* + +proc Old_Profiler_Callback_ near + + push eax + push esi + mov esi,[ProfilePtr] + cmp esi,MAX_PROFILE_TIME*PROFILE_RATE + jge @@out + mov eax,[ProfileFunctionAddress] + sub eax,[BaseAddress] + mov [ProfileList+esi*4],eax + inc [ProfilePtr] + pop esi + pop eax + ret 14h + +@@out: call Stop_Profiler + pop esi + pop eax + ret 14h + +endp Old_Profiler_Callback_ + + + + dataseg + align 4 + +ProfileFunctionAddress dd 0 ;Ptr to function we are currently in +BaseAddress dd 0 ;Address of the code segment start +MyStackPtr dd 0 ;offset into my stack table +ProAddress dd Pro_Start ;jmp ptr for __PRO procedure +EpiAddress dd Epi_Start ;jmp ptr for __EPI procedure + +label MyStack dword ;my stack table + dd 16*1024 dup (?) + +end diff --git a/WIN32LIB/SRCDEBUG/AUDUNCMP.ASM b/WIN32LIB/SRCDEBUG/AUDUNCMP.ASM new file mode 100644 index 0000000..cd320bf --- /dev/null +++ b/WIN32LIB/SRCDEBUG/AUDUNCMP.ASM @@ -0,0 +1,374 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Audio Library * +;* * +;* File Name : AUDUNCMP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : March 14, 1995 * +;* * +;* Last Update : June 26, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Decompress_Frame_Lock -- locks the JLB audio decompression code * +;* Decompress_Frame_Unlock -- Unlocks the JLB audio compression code * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +DPMI_INTR equ 31h + +LABEL LockedCodeStart BYTE + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + + +;*************************************************************************** +;* DECOMPRESS_FRAME -- Uncompresses a WW compressed audio frame * +;* * +;* INPUT: void * source - pointer to encoded audio data * +;* void * dest - pointer to decompression area * +;* long size - the maximum size of destination buffer * +;* * +;* OUTPUT: long - the number of bytes we uncompressed * +;* * +;* PROTO: long Decompress_Frame(void *, void *, long); * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Decompress_Frame:NEAR + PROC Decompress_Frame C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + pushfd + cld + mov [incount],0 ;Bytes read from source + + +; Source, Dest and count must be valid. + + cmp [source],0 + je ??fini + + cmp [dest],0 + je ??fini + + cmp [count],0 + je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popfd + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popfd + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushfd + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popfd + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popfd + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popfd + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popfd + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popfd + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popfd + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popfd + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popfd + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushfd + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popfd + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popfd + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + popfd + mov eax,[incount] + ret + + ENDP Decompress_Frame + +LABEL LockedCodeEnd BYTE + + + + + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/BITBLIT.ASM b/WIN32LIB/SRCDEBUG/BITBLIT.ASM new file mode 100644 index 0000000..ce6d353 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/BITBLIT.ASM @@ -0,0 +1,462 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + + PROC Linear_Blit_To_Linear C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, display rectangle) +; - trivially rejected (return with no action) +; - retangle must be iteratively clipped again edges of the clipping window +; and the remaining retangle is display. + +; Clip Source Rectangle against source Window boundaries. + mov esi,[this_object] ; get ptr to src + xor ecx,ecx ; Set sutherland code to zero + xor edx,edx ; Set sutherland code to zero + + ; compute the difference in the X axis and get the bit signs into ecx , edx + mov edi,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov ebx,[x_pixel] ; Get first end point x_pixel into register + mov eax,[x_pixel] ; Get second end point x_pixel into register + add ebx,[pixel_width] ; second point x1_pixel = x + width + shld ecx, eax,1 ; the sign bit of x_pixel is sutherland code0 bit4 + mov [x1_pixel],ebx ; save second for future use + inc edi ; move the right edge by one unit + shld edx,ebx,1 ; the sign bit of x1_pixel is sutherland code0 bit4 + sub eax,edi ; compute the difference x0_pixel - width + sub ebx,edi ; compute the difference x1_pixel - width + shld ecx,eax,1 ; the sign bit of the difference is sutherland code0 bit3 + shld edx,ebx,1 ; the sign bit of the difference is sutherland code0 bit3 + + ; the following code is just a repeticion of the above code + ; in the Y axis. + mov edi,[(GraphicViewPort esi).GVPHeight] ; get height into register + mov ebx,[y_pixel] + mov eax,[y_pixel] + add ebx,[pixel_height] + shld ecx,eax,1 + mov [y1_pixel ],ebx + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + ; Here we have the to Sutherland code into cl and dl + xor cl,5 ; bit 2 and 0 are complented, reverse then + xor dl,5 ; bit 2 and 0 are complented, reverse then + mov al,cl ; save code1 in case we have to clip iteratively + test dl,cl ; if any bit in code0 and its counter bit + jnz ??real_out ; in code1 is set then the rectangle in outside + or al,dl ; if all bit of code0 the counter bit in + jz ??clip_against_dest ; in code1 is set to zero, then all + ; end points of the rectangle are + ; inside the clipping window + + ; if we are here the polygon have to be clip iteratively + test cl,1000b ; if bit 4 in code0 is set then + jz ??scr_left_ok ; x_pixel is smaller than zero + mov [x_pixel],0 ; set x_pixel to cero. + +??scr_left_ok: + test cl,0010b ; if bit 2 in code0 is set then + jz ??scr_bottom_ok ; y_pixel is smaller than zero + mov [ y_pixel ],0 ; set y_pixel to cero. + +??scr_bottom_ok: + test dl,0100b ; if bit 3 in code1 is set then + jz ??scr_right_ok ; x1_pixel is greater than the width + mov eax,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov [ x1_pixel ],eax ; set x1_pixel to width. +??scr_right_ok: + test dl,0001b ; if bit 0 in code1 is set then + jz ??clip_against_dest ; y1_pixel is greater than the width + mov eax,[(GraphicViewPort esi).GVPHeight] ; get height into register + mov [ y1_pixel ],eax ; set y1_pixel to height. + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + ; build the destination rectangle before clipping + ; dest_x1 = dest_x0 + ( x1_pixel - x_pixel ) + ; dest_y1 = dest_y0 + ( y1_pixel - y_pixel ) + mov eax,[dest_x0] ; get dest_x0 into eax + mov ebx,[dest_y0] ; get dest_y0 into ebx + sub eax,[x_pixel] ; subtract x_pixel from eax + sub ebx,[y_pixel] ; subtract y_pixel from ebx + add eax,[x1_pixel] ; add x1_pixel to eax + add ebx,[y1_pixel] ; add y1_pixel to ebx + mov [dest_x1],eax ; save eax into dest_x1 + mov [dest_y1],ebx ; save eax into dest_y1 + + + ; The followin code is a repeticion of the Sutherland clipping + ; descrived above. + mov esi,[dest] ; get ptr to src + xor ecx,ecx + xor edx,edx + mov edi,[(GraphicViewPort esi).GVPWidth] ; get width into register + mov eax,[dest_x0] + mov ebx,[dest_x1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + mov edi,[( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax,[dest_y0] + mov ebx,[dest_y1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + xor cl,5 + xor dl,5 + mov al,cl + test dl,cl + jnz ??real_out + or al,dl + jz ??do_blit + + test cl,1000b + jz ??dest_left_ok + mov eax,[ dest_x0 ] + mov [ dest_x0 ],0 + sub [ x_pixel ],eax + +??dest_left_ok: + test cl,0010b + jz ??dest_bottom_ok + mov eax,[ dest_y0 ] + mov [ dest_y0 ],0 + sub [ y_pixel ],eax + + +??dest_bottom_ok: + test dl,0100b + jz ??dest_right_ok + mov ebx,[ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax,[ dest_x1 ] + mov [ dest_x1 ],ebx + sub eax,ebx + sub [ x1_pixel ],eax + +??dest_right_ok: + test dl,0001b + jz ??do_blit + mov ebx,[ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov eax,[ dest_y1 ] + mov [ dest_y1 ],ebx + sub eax,ebx + sub [ y1_pixel ],eax + + +; Here is where we do the actual blit +??do_blit: + cld + mov ebx,[this_object] + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov ecx,eax + mul [y_pixel] + add esi,[x_pixel] + mov [source_area],ecx + add esi,eax + + add ecx,[x_pixel ] + sub ecx,[x1_pixel ] + mov [scr_ajust_width ],ecx + + mov ebx,[dest] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov ecx,eax + mul [ dest_y0 ] + add edi,[ dest_x0 ] + mov [ dest_area ],ecx + add edi,eax + + mov eax,[ dest_x1 ] + sub eax,[ dest_x0 ] + jle ??real_out + sub ecx,eax + mov [ dest_ajust_width ],ecx + + mov edx,[ dest_y1 ] + sub edx,[ dest_y0 ] + jle ??real_out + + cmp esi,edi + jz ??real_out + jl ??backupward_blit + +; ******************************************************************** +; Forward bitblit + + test [ trans ],1 + jnz ??forward_Blit_trans + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax,10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx,edi + mov ebx,eax + neg ecx + and ecx,3 + sub ebx,ecx + rep movsb + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,3 + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx,eax + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_bytes + ret + +??forward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??forward_loop_trans: + mov ecx,eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret + + +; ************************************************************************ +; backward bitblit + +??backupward_blit: + + mov ebx,[ source_area ] + dec edx + add esi,eax + imul ebx,edx + std + lea esi,[ esi + ebx - 1 ] + + mov ebx,[ dest_area ] + add edi,eax + imul ebx,edx + lea edi,[ edi + ebx - 1] + + test [ trans ],1 + jnz ??backward_Blit_trans + + cmp eax,15 + jl ??backward_loop_bytes + +??backward_loop_dword: + push edi + push esi + lea ecx,[edi+1] + mov ebx,eax + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_dword + cld + ret + +??backward_loop_bytes: + push edi + mov ecx,eax ; remove that from the total size to be copied later. + push esi + rep movsb ; do the copy. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_bytes + cld + ret + +??backward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??back_transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??backward_loop_trans: + mov ecx,eax + push edi + push esi + jmp [ y1_pixel ] +??backward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + dec esi + dec edi + ENDM + ??back_transp_reference: + dec ecx + jge ??backward_trans_line + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_trans + cld + ret + +??real_out: + ret + ENDP Linear_Blit_To_Linear + + + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/BUFFER.CPP b/WIN32LIB/SRCDEBUG/BUFFER.CPP new file mode 100644 index 0000000..b3bea0e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/BUFFER.CPP @@ -0,0 +1,131 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/WIN32LIB/SRCDEBUG/BUFFGLBL.CPP b/WIN32LIB/SRCDEBUG/BUFFGLBL.CPP new file mode 100644 index 0000000..603d2e7 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/BUFFGLBL.CPP @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "gbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +#ifdef not_any_more_it_doesnt +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +#endif //not_any_more_it_doesnt + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicViewPortClass *LogicPage; + +BOOL IconCacheAllowed = TRUE; + +/* +** Pointer to a function we will call if we detect loss of focus +*/ +void (*Gbuffer_Focus_Loss_Function)(void) = NULL; diff --git a/WIN32LIB/SRCDEBUG/CCFILE.CPP b/WIN32LIB/SRCDEBUG/CCFILE.CPP new file mode 100644 index 0000000..3b694cb --- /dev/null +++ b/WIN32LIB/SRCDEBUG/CCFILE.CPP @@ -0,0 +1,604 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\ccfile.cpv 2.20 27 Sep 1995 12:45:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : March 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * CCFileClass::Error -- Handles displaying a file error message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const *filename) +{ + Set_Name(filename); + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) : CDFileClass() +{ + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const *buffer, long size) +{ + + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Pointer || FromDisk) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void *buffer, long size) +{ + int opened = false; + + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Pointer) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size) { + Mem_Copy(Add_Long_To_Pointer(Pointer, Position), buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + /* + ** If the file is part of a mixfile, but the mixfile is located + ** on disk, then a special read operation is necessary. + */ + if (FromDisk) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size > 0) { + CDFileClass::Seek(Start + Position, SEEK_SET); + size = CDFileClass::Read(buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + if (opened) Close(); + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + if (Pointer || FromDisk) { + switch (dir) { + case SEEK_END: + Position = Length; + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + if (Position < 0) Position = 0; + if (Position > Length) Position = Length; + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + if (Pointer || FromDisk) return(Length); + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + if (MixFileClass::Offset(File_Name())) { + return(true); + } + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Pointer) return(true); + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; // Starts at beginning offset. + Start = 0; + Length = 0; + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MixFileClass *mixfile = 0; + if (MixFileClass::Offset(File_Name(), &Pointer, &mixfile, &Start, &Length)) { + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (!Pointer) { + long start = Start; + long length = Length; + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char const * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + free((void *)dupfile); + Start = start; + Length = length; + FromDisk = true; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + +static CCFileClass Handles[10]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(BYTE const *) +{ + CCFileClass::Set_Search_Path(path); + return(true); +} +#endif + +WORD __cdecl Open_File(BYTE const *file_name, WORD mode) +{ + for (int index = 0; index < sizeof(Handles)/sizeof(Handles[0]); index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(ERROR); +} + +VOID __cdecl Close_File(WORD handle) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(WORD handle, VOID *buf, ULONG bytes) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(WORD handle, VOID const *buf, ULONG bytes) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +WORD __cdecl Find_File(BYTE const *file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +WORD __cdecl Delete_File(BYTE const *file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +WORD __cdecl Create_File(BYTE const *file_name) +{ + return(CCFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +VOID * __cdecl Load_Alloc_Data(BYTE const *name, WORD ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +ULONG __cdecl File_Size(WORD handle) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(BYTE const *name, VOID const *ptr, ULONG size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(WORD handle, LONG offset, WORD starting) +{ + if (handle != ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +WORD __cdecl Find_Disk_Number(BYTE const *) +{ + return(0); +} +#endif + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} + + +void Unfragment_File_Cache(void) +{ +} + + diff --git a/WIN32LIB/SRCDEBUG/CLEAR.ASM b/WIN32LIB/SRCDEBUG/CLEAR.ASM new file mode 100644 index 0000000..0f66283 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/CLEAR.ASM @@ -0,0 +1,130 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Graphics Buffer * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : August 23, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* + PROC Buffer_Clear C near + USES eax,ebx,ecx,edx,esi,edi + + ARG this_object:DWORD ; this is a member function + ARG color:BYTE ; what color should we clear to + + cld ; always go forward + + mov ebx,[this_object] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov edx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov esi,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + push [dword (GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + mov ebx,[(GraphicViewPort ebx).GVPXAdd] ; esi = add for each line + add ebx,[esp] ; Yes, I know its nasty but + add esp,4 ; it works! + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??byte_by_byte ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + push ebx +??dword_aligned_loop: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??dword_aligned_loop ; if more to do than do it + pop eax + ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??byte_by_byte: + mov ecx,esi ; get total width in bytes + rep stosb ; store the width + add edi,ebx ; handle the xadd + dec edx ; decrement the height + jnz ??byte_by_byte ; if any left then next line +??exit: + ret + ENDP Buffer_Clear +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/CLIPRECT.ASM b/WIN32LIB/SRCDEBUG/CLIPRECT.ASM new file mode 100644 index 0000000..d595722 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/CLIPRECT.ASM @@ -0,0 +1,269 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Clip_Rect :NEAR +GLOBAL C Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * + + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, return the oroginal data) +; - trivially rejected (return with no action, return error code) +; - retangle must be iteratively clipped again edges of the clipping window +; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz ??clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz ??clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz ??left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + +??left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz ??bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + +??bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz ??right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz ??clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify +??clip_out: + ret + ENDP Clip_Rect + + +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * + + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl ??x_axix_ok + mov eax,1 + + test esi,esi + jl ??shift_right + mov [dword ptr ebx],0 + jmp ??x_axix_ok +??shift_right: + inc edi + sub [ebx],edi +??x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl ??confi_out + mov eax,1 + + test esi,esi + jl ??shift_top + mov [dword ptr ebx],0 + ret +??shift_top: + inc edi + sub [ebx],edi +??confi_out: + ret + + ENDP Confine_Rect + + END diff --git a/WIN32LIB/SRCDEBUG/CRC.ASM b/WIN32LIB/SRCDEBUG/CRC.ASM new file mode 100644 index 0000000..76a133c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/CRC.ASM @@ -0,0 +1,114 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Calculate_CRC :NEAR + + CODESEG + +; LONG Calculate_CRC(VOID *buffer, LONG length); + PROC Calculate_CRC C near + USES esi + + ARG buffer:DWORD + ARG length:DWORD + + LOCAL crc:DWORD + + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + ; Fetch the length of the data block to CRC. + mov ecx,[length] + jecxz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short ??remainder +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov [crc],ebx + popad + mov eax,[crc] + ret + + ENDP Calculate_CRC + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DDRAW.CPP b/WIN32LIB/SRCDEBUG/DDRAW.CPP new file mode 100644 index 0000000..9e76f43 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DDRAW.CPP @@ -0,0 +1,1000 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Win32 Library * + * * + * File Name : DDRAW.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : October 10, 1995 * + * * + * Last Update : October 10, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +#include "misc.h" +#include +#include + + +LPDIRECTDRAW DirectDrawObject=NULL; // Pointer to the direct draw object +LPDIRECTDRAW2 DirectDraw2Interface = NULL; // Pointer to direct draw 2 interface + +HWND MainWindow; // Handle to programs main window + // this is passed to SetCooperativeLevel + // so DirectDraw knows which window is ours + + +PALETTEENTRY PaletteEntries[256]; // 256 windows palette entries +LPDIRECTDRAWPALETTE PalettePtr; // Pointer to direct draw palette object +BOOL FirstPaletteSet=FALSE; // Is this the first time 'Set_Palette' has been called? +LPDIRECTDRAWSURFACE PaletteSurface=NULL; +SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces +BOOL CanVblankSync = TRUE; + +BOOL SystemToVideoBlits =FALSE; // Does hardware support system mem to video mem blits? +BOOL VideoToSystemBlits =FALSE; // Does hardware support video mem to system mem blits? +BOOL SystemToSystemBlits = FALSE; // Does hardware support system mem to system mem blits? +BOOL OverlappedVideoBlits = TRUE; // Can video driver blit overlapped regions? + +/* +** Function to call if we detect focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void) = NULL; +extern void (*Misc_Focus_Restore_Function)(void) = NULL; + + +/*********************************************************************************************** + * Process_DD_Result -- Does a message box based on the result of a DD command * + * * + * INPUT: HRESULT result - the result returned from the direct draw command * + * int display_ok_msg - should a message be displayed if command ok * * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/27/1995 PWG : Created. * + *=============================================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg) +{ + switch (result) { + case DD_OK: + if (display_ok_msg) { + MessageBox(MainWindow, "Direct Draw request went ok.", "Note", MB_ICONEXCLAMATION|MB_OK); + } + break; + case DDERR_ALREADYINITIALIZED: + MessageBox(MainWindow, "This object is already initialized ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_BLTFASTCANTCLIP: + MessageBox(MainWindow, "Return if a clipper object is attached to the source surface passed into a BltFast call.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTATTACHSURFACE: + MessageBox(MainWindow, "This surface can not be attached to the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANNOTDETACHSURFACE: + MessageBox(MainWindow, "This surface can not be detached from the requested surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTCREATEDC: + MessageBox(MainWindow, "Windows can not create any more DCs","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTDUPLICATE: + MessageBox(MainWindow, "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CANTLOCKSURFACE: + MessageBox(MainWindow, "Unable to lock surface because no driver exists which can supply a pointer to the surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CLIPPERISUSINGHWND: + MessageBox(MainWindow, "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_COLORKEYNOTSET: + MessageBox(MainWindow, "No src color key specified for this operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_CURRENTLYNOTAVAIL: + MessageBox(MainWindow, "Support is currently not available. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_DIRECTDRAWALREADYCREATED: + MessageBox(MainWindow, "A DirectDraw object representing this driver has already been created for this process. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCEPTION: + MessageBox(MainWindow, "An exception was encountered while performing the requested operation. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_EXCLUSIVEMODEALREADYSET: + MessageBox(MainWindow, "An attempt was made to set the cooperative level when it was already set to exclusive.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_GENERIC: + MessageBox(MainWindow, "Generic failure.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HEIGHTALIGN: + MessageBox(MainWindow, "Height of rectangle provided is not a multiple of reqd alignment.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDALREADYSET: + MessageBox(MainWindow, "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_HWNDSUBCLASSED: + MessageBox(MainWindow, "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_IMPLICITLYCREATED: + MessageBox(MainWindow, "This surface can not be restored because it is an implicitly created surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INCOMPATIBLEPRIMARY: + MessageBox(MainWindow, "Unable to match primary surface creation request with existing primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCAPS: + MessageBox(MainWindow, "One or more of the caps bits passed to the callback are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDCLIPLIST: + MessageBox(MainWindow, "DirectDraw does not support the provided cliplist.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDDIRECTDRAWGUID: + MessageBox(MainWindow, "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDMODE: + MessageBox(MainWindow, "DirectDraw does not support the requested mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDOBJECT: + MessageBox(MainWindow, "DirectDraw received a pointer that was an invalid DIRECTDRAW object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPARAMS: + MessageBox(MainWindow, "One or more of the parameters passed to the function are incorrect.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPIXELFORMAT: + MessageBox(MainWindow, "The pixel format was invalid as specified.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDPOSITION: + MessageBox(MainWindow, "Returned when the position of the overlay on the destination is no longer legal for that destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_INVALIDRECT: + MessageBox(MainWindow, "Rectangle provided was invalid.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + + case DDERR_INVALIDSURFACETYPE: + MessageBox(MainWindow, "The requested action could not be performed because the surface was of the wrong type.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_LOCKEDSURFACES: + MessageBox(MainWindow, "Operation could not be carried out because one or more surfaces are locked.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NO3D: + MessageBox(MainWindow, "There is no 3D present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOALPHAHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no alpha accleration hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOANTITEARHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for synchronizing blts to avoid tearing. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOBLTHW: + MessageBox(MainWindow, "No blter hardware present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#if(0) + case DDERR_NOBLTQUEUEHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for asynchronous blting. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; +#endif + case DDERR_NOCLIPLIST: + MessageBox(MainWindow, "No cliplist available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCLIPPERATTACHED: + MessageBox(MainWindow, "No clipper object attached to surface object.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORCONVHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no color conversion hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEY: + MessageBox(MainWindow, "Surface doesn't currently have a color key","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOLORKEYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support of the destination color key.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOCOOPERATIVELEVELSET: + MessageBox(MainWindow, "Create function called without DirectDraw object method SetCooperativeLevel being called.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODC: + MessageBox(MainWindow, "No DC was ever created for this surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODDROPSHW: + MessageBox(MainWindow, "No DirectDraw ROP hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWHW: + MessageBox(MainWindow, "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NODIRECTDRAWSUPPORT: + MessageBox(MainWindow, "No DirectDraw support possible with current display driver.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEMULATION: + MessageBox(MainWindow, "Software emulation not available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOEXCLUSIVEMODE: + MessageBox(MainWindow, "Operation requires the application to have exclusive mode but the application does not have exclusive mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOFLIPHW: + MessageBox(MainWindow, "Flipping visible surfaces is not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOGDI: + MessageBox(MainWindow, "There is no GDI present.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOHWND: + MessageBox(MainWindow, "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOMIRRORHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYDEST: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOOVERLAYHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no overlay hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEATTACHED: + MessageBox(MainWindow, "No palette object attached to this surface. ","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOPALETTEHW: + MessageBox(MainWindow, "No hardware support for 16 or 256 color palettes.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NORASTEROPHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no appropriate raster op hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOROTATIONHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no rotation hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOSTRETCHHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for stretching.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT4BITCOLORINDEX: + MessageBox(MainWindow, "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOT8BITCOLOR: + MessageBox(MainWindow, "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTAOVERLAYSURFACE: + MessageBox(MainWindow, "Returned when an overlay member is called for a non-overlay surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTEXTUREHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no texture mapping hardware present or available.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFLIPPABLE: + MessageBox(MainWindow, "An attempt has been made to flip a surface that is not flippable.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTFOUND: + MessageBox(MainWindow, "Requested item was not found.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTLOCKED: + MessageBox(MainWindow, "Surface was not locked. An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOTPALETTIZED: + MessageBox(MainWindow, "The surface being used is not a palette-based surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOVSYNCHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZBUFFERHW: + MessageBox(MainWindow, "Operation could not be carried out because there is no hardware support for zbuffer blting.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_NOZOVERLAYHW: + MessageBox(MainWindow, "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFCAPS: + MessageBox(MainWindow, "The hardware needed for the requested operation has already been allocated.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OUTOFVIDEOMEMORY: + MessageBox(MainWindow, "DirectDraw does not have enough memory to perform the operation.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCANTCLIP: + MessageBox(MainWindow, "The hardware does not support clipped overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + MessageBox(MainWindow, "Can only have ony color key active at one time for overlays.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_OVERLAYNOTVISIBLE: + MessageBox(MainWindow, "Returned when GetOverlayPosition is called on a hidden overlay.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PALETTEBUSY: + MessageBox(MainWindow, "Access to this palette is being refused because the palette is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_PRIMARYSURFACEALREADYEXISTS: + MessageBox(MainWindow, "This process already has created a primary surface.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_REGIONTOOSMALL: + MessageBox(MainWindow, "Region passed to Clipper::GetClipList is too small.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYATTACHED: + MessageBox(MainWindow, "This surface is already attached to the surface it is being attached to.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEALREADYDEPENDENT: + MessageBox(MainWindow, "This surface is already a dependency of the surface it is being made a dependency of.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEBUSY: + MessageBox(MainWindow, "Access to this surface is being refused because the surface is already locked by another thread.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACEISOBSCURED: + MessageBox(MainWindow, "Access to surface refused because the surface is obscured.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACELOST: + MessageBox(MainWindow, "Access to this surface is being refused because the surface memory is gone. The DirectDrawSurface object representing this surface should have Restore called on it.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_SURFACENOTATTACHED: + MessageBox(MainWindow, "The requested surface is not attached.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGHEIGHT: + MessageBox(MainWindow, "Height requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGSIZE: + MessageBox(MainWindow, "Size requested by DirectDraw is too large -- the individual height and width are OK.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_TOOBIGWIDTH: + MessageBox(MainWindow, "Width requested by DirectDraw is too large.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTED: + MessageBox(MainWindow, "Action not supported.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDFORMAT: + MessageBox(MainWindow, "FOURCC format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_UNSUPPORTEDMASK: + MessageBox(MainWindow, "Bitmask in the pixel format requested is unsupported by DirectDraw.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_VERTICALBLANKINPROGRESS: + MessageBox(MainWindow, "Vertical blank is in progress.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WASSTILLDRAWING: + MessageBox(MainWindow, "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_WRONGMODE: + MessageBox(MainWindow, "This surface can not be restored because it was created in a different mode.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + case DDERR_XALIGN: + MessageBox(MainWindow, "Rectangle provided was not horizontally aligned on required boundary.","Note", MB_ICONEXCLAMATION|MB_OK); + break; + default: + char string[256]; + sprintf (string, "Unrecognised Direct Draw result code: %d", result & 0xffff); + MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + break; + } +} + + + +/*********************************************************************************************** + * Check_Overlapped_Blit_Capability -- See if video driver supports blitting overlapped regions* + * * + * We will check for this by drawing something to a video page and blitting it over itself. * + * If we end up with the top line repeating then overlapped region blits dont work. * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 5:06PM ST : Created * + *=============================================================================================*/ +void Check_Overlapped_Blit_Capability(void) +{ + + /* + ** Assume we can until we find out otherwise + */ + OverlappedVideoBlits = TRUE; + + GraphicBufferClass test_buffer; + + test_buffer.Init (64, 64, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + test_buffer.Clear(); + + /* + ** Plot a pixel in the top left corner of the buffer. + */ + test_buffer.Put_Pixel(0, 0, 255); + + /* + ** Blit the buffer down by one line. If we end up with a vertical strip of pixel 255's then + ** overlapped blits dont work + */ + + test_buffer.Blit(test_buffer, 0, 0, 0, 1, test_buffer.Get_Width(), test_buffer.Get_Height()-1); + + if (test_buffer.Get_Pixel (0 ,5) == 255) OverlappedVideoBlits = FALSE; +} + + + +/*********************************************************************************************** + * Set_Video_Mode -- Initializes Direct Draw and sets the required Video Mode * + * * + * INPUT: int width - the width of the video mode in pixels * + * int height - the height of the video mode in pixels * + * int bits_per_pixel - the number of bits per pixel the video mode supports * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel) +{ + HRESULT result; + // + // If there is not currently a direct draw object then we need to define one. + // + if ( DirectDrawObject == NULL ){ + //MessageBox(MainWindow, "In Set_Video_Mode. About to call DirectDrawCreate.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawCreate(NULL, &DirectDrawObject, NULL); + Process_DD_Result(result, FALSE); + if (result == DD_OK){ + if (w==320){ + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + } else { + //MessageBox(MainWindow, "In Set_Video_Mode. About to call SetCooperativeLevel.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + Process_DD_Result(result, FALSE); + }else{ + return (FALSE); + } + } + + // + // Set the required display mode with 8 bits per pixel + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call call SetDisplayMode.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->SetDisplayMode ( w , h , bits_per_pixel ); + if (result != DD_OK){ +// Process_DD_Result(result, FALSE); + DirectDrawObject->Release(); + DirectDrawObject = NULL; + return(FALSE); + } + + // + // Create a direct draw palette object + // + //MessageBox(MainWindow, "In Set_Video_Mode. About to call CreatePalette.","Note", MB_ICONEXCLAMATION|MB_OK); + result = DirectDrawObject->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, &PaletteEntries[0] , &PalettePtr ,NULL); + Process_DD_Result(result, FALSE); + if (result != DD_OK){ + return (FALSE); + } + + Check_Overlapped_Blit_Capability(); + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); +#if (0) + /* + ** Find out if DirectX 2 extensions are available + */ + result = DirectDrawObject->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DirectDraw2Interface); + SystemToVideoBlits = FALSE; + VideoToSystemBlits = FALSE; + SystemToSystemBlits= FALSE; + if (result != DD_OK){ + DirectDraw2Interface = NULL; + }else{ + DDCAPS capabilities; + DDCAPS emulated_capabilities; + + memset ((char*)&capabilities, 0, sizeof(capabilities)); + memset ((char*)&emulated_capabilities, 0, sizeof(emulated_capabilities)); + capabilities.dwSize = sizeof (capabilities); + emulated_capabilities.dwSize = sizeof (emulated_capabilities); + + DirectDrawObject->GetCaps (&capabilities, &emulated_capabilities); + + if (capabilities.dwCaps & DDCAPS_CANBLTSYSMEM){ + SystemToVideoBlits = (capabilities.dwSVBCaps & DDCAPS_BLT) ? TRUE : FALSE; + VideoToSystemBlits = (capabilities.dwVSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + SystemToSystemBlits = (capabilities.dwSSBCaps & DDCAPS_BLT) ? TRUE : FALSE; + } + } +#endif //(0) + + //MessageBox(MainWindow, "In Set_Video_Mode. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + + return (TRUE); + +} + +/*********************************************************************************************** + * Reset_Video_Mode -- Resets video mode and deletes Direct Draw Object * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/26/1995 PWG : Created. * + *=============================================================================================*/ +void Reset_Video_Mode(void) +{ + HRESULT result; + + // + // If a direct draw object has been declared and a video mode has been set + // then reset the video mode and release the direct draw object. + // + if ( DirectDrawObject ) { + result = DirectDrawObject->RestoreDisplayMode(); + Process_DD_Result(result, FALSE); + result = DirectDrawObject->Release(); + Process_DD_Result(result, FALSE); + + DirectDrawObject = NULL; + } +} + + + + +/*********************************************************************************************** + * Get_Free_Video_Memory -- returns amount of free video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: bytes of available video RAM * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:52PM ST : Created * + *=============================================================================================*/ +unsigned int Get_Free_Video_Memory(void) +{ + + DDCAPS video_capabilities; + + if (DirectDrawObject){ + + video_capabilities.dwSize = sizeof (video_capabilities); + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + char string [256]; + sprintf (string, "In Get_Free_Video_Memory. About to return %d bytes",video_capabilities.dwVidMemFree); + //MessageBox(MainWindow, string,"Note", MB_ICONEXCLAMATION|MB_OK); + return (video_capabilities.dwVidMemFree); + } + } + + //MessageBox(MainWindow, "In Get_Free_Video_Memory. About to return failure","Note", MB_ICONEXCLAMATION|MB_OK); + return (0); +} + + + + +/*********************************************************************************************** + * Get_Video_Hardware_Caps -- returns bitmask of direct draw video hardware support * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: hardware flags * + * * + * WARNINGS: Must call Set_Video_Mode 1st to create the direct draw object * + * * + * HISTORY: * + * 1/12/96 9:14AM ST : Created * + *=============================================================================================*/ +unsigned Get_Video_Hardware_Capabilities(void) +{ + DDCAPS video_capabilities; + unsigned video; + + /* + ** Fail if the direct draw object has not been initialised + */ + if (!DirectDrawObject) return (0); + + /* + ** Get the capabilities of the direct draw object + */ + video_capabilities.dwSize = sizeof(video_capabilities); + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to call GetCaps","Note", MB_ICONEXCLAMATION|MB_OK); + HRESULT result = DirectDrawObject->GetCaps (&video_capabilities, NULL); + if (result != DD_OK){ + Process_DD_Result(result, FALSE); + return (0); + } + + /* + ** Set flags to indicate the presence of the features we are interested in + */ + video = 0; + + /* Hardware blits supported? */ + if (video_capabilities.dwCaps & DDCAPS_BLT) video |= VIDEO_BLITTER; + + /* Hardware blits asyncronous? */ + if (video_capabilities.dwCaps & DDCAPS_BLTQUEUE) video |= VIDEO_BLITTER_ASYNC; + + /* Can palette changes be synced to vertical refresh? */ + if (video_capabilities.dwCaps & DDCAPS_PALETTEVSYNC) video |= VIDEO_SYNC_PALETTE; + + /* Is the video cards memory bank switched? */ + if (video_capabilities.dwCaps & DDCAPS_BANKSWITCHED) video |= VIDEO_BANK_SWITCHED; + + /* Can the blitter do filled rectangles? */ + if (video_capabilities.dwCaps & DDCAPS_BLTCOLORFILL) video |= VIDEO_COLOR_FILL; + + /* Is there no hardware assistance avaailable at all? */ + if (video_capabilities.dwCaps & DDCAPS_NOHARDWARE) video |= VIDEO_NO_HARDWARE_ASSIST; + + //MessageBox(MainWindow, "In Get_Video_Hardware_Capabilities. About to return success.","Note", MB_ICONEXCLAMATION|MB_OK); + return (video); +} + + + + +/*********************************************************************************************** + * Wait_Vert_Blank -- Waits for the start (leading edge) of a vertical blank * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=============================================================================================*/ +extern int ScreenWidth; +void Wait_Vert_Blank(void) +{ + if( ScreenWidth!=320 && CanVblankSync){ + HRESULT result = DirectDrawObject->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); + if (result == E_NOTIMPL){ + CanVblankSync = FALSE; + return; + } + Process_DD_Result(result, FALSE); + } +} + + + + + +/*********************************************************************************************** + * Set_Palette -- set a direct draw palette * + * * + * * + * * + * INPUT: ptr to 768 rgb palette bytes * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/11/95 3:33PM ST : Created * + *=============================================================================================*/ +void Set_DD_Palette ( void *palette ) +{ + + /* + ** Trap null ptr + */ + if (!palette) return; + + int j; + int k; + char *palette_get; + + if ( DirectDrawObject && PaletteSurface ){ + + k=0; + + palette_get = (char*)palette; + + for( j=0 ; j<768 ; j+=3 ) + { + PaletteEntries[k].peRed = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peGreen = (unsigned char)((*palette_get++)<<2); + PaletteEntries[k].peBlue = (unsigned char)((*palette_get++)<<2); + k++; + } + + if ( !FirstPaletteSet ){ + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetPalette","Note", MB_ICONEXCLAMATION|MB_OK); + PaletteSurface->SetPalette( PalettePtr ); + FirstPaletteSet=TRUE; + } + + //MessageBox(MainWindow, "In Set_DD_Palette. About to call SetEntries","Note", MB_ICONEXCLAMATION|MB_OK); + PalettePtr->SetEntries( 0 , 0 , 256 , &PaletteEntries[0] ); + } + //MessageBox(MainWindow, "Leaving Set_DD_Palette","Note", MB_ICONEXCLAMATION|MB_OK); + +} + + + + + +/*********************************************************************************************** + * Wait_Blit -- waits for the DirectDraw blitter to become idle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 07-25-95 03:53pm ST : Created * + *=============================================================================================*/ + +void Wait_Blit (void) +{ + HRESULT return_code; + + do { + return_code=PaletteSurface->GetBltStatus (DDGBS_ISBLTDONE); + } while (return_code != DD_OK && return_code != DDERR_SURFACELOST); + +} + + + +/*********************************************************************************************** + * SMC::SurfaceMonitorClass -- constructor for surface monitor class * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:23PM ST : Created * + *=============================================================================================*/ + +SurfaceMonitorClass::SurfaceMonitorClass(void) +{ + for (int i=0 ; iRestore() != DD_OK){ + if (Misc_Focus_Loss_Function){ + Misc_Focus_Loss_Function(); + } + return; + } + } + } + + /* + ** PWG/ST: Now that we know all the surfaces are restored call + ** the function pointer to notify the program that it has + ** happened. This function pointer is used to clear the pages, + ** etc. + */ + if (Misc_Focus_Restore_Function){ + Misc_Focus_Restore_Function(); + } + + SurfacesRestored = TRUE; + + /* + ** Restore the palette + */ + Set_DD_Palette (CurrentPalette); + } +} + + +/*********************************************************************************************** + * SMC::Set_Surface_Focus -- set the InFocus flag to the given state * + * * + * The InFocus flag is used to keep track of whether our application is currently in focus. * + * We dont want to be restoring video surfaces when we are supposed to be running in the * + * background. * + * * + * INPUT: bool in focus * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/6/95 12:21PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Set_Surface_Focus ( BOOL in_focus ) +{ + InFocus=in_focus; +} + + + + +/*********************************************************************************************** + * SMC::Release -- releases all direct draw surfaces * + * * + * Call this at the end of the game before called RestoreDisplayMode * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:23PM ST : Created * + *=============================================================================================*/ + +void SurfaceMonitorClass::Release(void) +{ + /* + ** Call release for each Direct Draw surface + */ + for (int i=0 ; iRelease(); + Surface[i] = 0; + } + } + +} + diff --git a/WIN32LIB/SRCDEBUG/DELAY.CPP b/WIN32LIB/SRCDEBUG/DELAY.CPP new file mode 100644 index 0000000..bbbb437 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DELAY.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : DELAY.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : 27 March, 1991 [CY] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include + +void Delay(int duration) +{ + unsigned long count; + TimerClass timer(BT_SYSTEM,TRUE); + + while (duration--) { + count = timer.Time() + 1L; + while (count >= timer.Time()) { + ; + } + } + +#if(FALSE) + while (duration--) + Wait_Vert_Blank(VertBlank); +#endif +} + +#if(FALSE) +void Vsync() +{ + Wait_Vert_Blank(VertBlank); +} +#endif + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DESCMGMT.CPP b/WIN32LIB/SRCDEBUG/DESCMGMT.CPP new file mode 100644 index 0000000..687eba5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DESCMGMT.CPP @@ -0,0 +1,113 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Descriptor management * + * * + * File Name : DESCMGMT.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MAP_SEGMENT_TO_ADDRESS -- Maps a physical address into a selector * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "descmgmt.h" +#include "misc.h" + + +/*************************************************************************** + * MAP_SEGMENT_TO_ADDRESS -- Maps a physical address into a selector * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: selector UWORD The selector mapped to address. exit on error. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/25/1994 jaw: Created. * + *=========================================================================*/ + +ULONG Map_Segment_To_Address(ULONG address, ULONG length) +{ + +// this function is not longer needed by RATIONAL SYSTEM DOS4GW +// linear addressing mode. +// a> the first megabyte of memory is mapped to linear adress 0 - 0x10000h +// b> all other addresses are linear offset from either ds: or es: + +/* + UWORD segment; + UWORD curDS; + CD_DES desc; + CD_DES cur_desc; + + // allocate a selector + if(_dos_allocmem(0, &segment) != 0) { + Exit(1, "Allocation of Descriptor.\n"); + } + + // get the data for this selector + if(_dx_ldt_rd(segment, (UCHAR *)&desc) != 0) { + Exit(1, "Reading Descriptor.\n"); + } + + // get the data for current data segment + curDS = GetDs(); + if(_dx_ldt_rd(curDS, (UCHAR *)&cur_desc) != 0) { + Exit(1, "Reading Descriptor.\n"); + } + + // set limit + desc.limit0_15 = (USHORT)(length & 0xffff); + desc.limit16_19 = ((UCHAR)(length >> 16L)) | DOS_32; + + // set base address + desc.base0_15 = (USHORT)(address & 0xffff); + desc.base16_23 = (UCHAR)((address >> 16) & 0xff); + desc.base24_31 = (UCHAR)((address >> 24) & 0xff); + + // set rights mark as icurrent data segment + desc.arights = cur_desc.arights; + + // write to LDT selector + if(_dx_ldt_wr(segment, (UCHAR *)&desc) != 0) { + Exit(1, "Failed writing descriptor.\n"); + } + + // return selector number + return segment; + */ + + if ( address & 0xfff0ffff ) + Exit ( 1 , "Error mapping real address to lineal address.\n" ) ; + + return address ; +} + diff --git a/WIN32LIB/SRCDEBUG/DETPROC.ASM b/WIN32LIB/SRCDEBUG/DETPROC.ASM new file mode 100644 index 0000000..0cdbd9c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DETPROC.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/detproc.asm 1.1 1994/04/18 09:13:53 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PROC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 11, 1993 * +;* * +;* Last Update : May 11, 1993 [JLB] * +;* * +;* Converted to 32Bit -- JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Processor :NEAR + + +PROC_80386 equ 0 +PROC_80486 equ 1 +PROC_80586 equ 2 + +DATASEG +cpu_id_586 dw 0 + +CODESEG + +PROC Processor C near + USES ebx + LOCAL ptype:WORD + + pushfd + +; At least a 386 -- check for 486. + mov [WORD PTR ptype],PROC_80386 ; 80386 + pushfd + pop eax + mov ebx,eax + xor eax,40000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 486 -- check for 586(Pentium) + mov [ptype],PROC_80486 ; 80486 + + ; Some machines have a problem with this fLAG + ; and thus make us think they are a 586 but they are + ; really a 486. A possible way around this is to + ; capture the Illegal instruction vector, then do + ; an instruction only available on the 586. + + ; for now this is just commented out + pushfd + pop eax + mov ebx,eax + xor eax,200000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 586(Pentium) -- check for higher. + mov [ptype],PROC_80586 ; 80486 +; mov eax,1 +; DW 0fA2h ; CPUID opcode. +; shr ax,8 +; and ax,0fh +; inc ax +; inc ax +; mov [cpu_id_586],ax + +; Final cleanup and exit. +??fini: + popfd + sub eax,eax + mov ax,[ptype] + ret + +ENDP Processor + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DEVICES.ASM b/WIN32LIB/SRCDEBUG/DEVICES.ASM new file mode 100644 index 0000000..bb17f9d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DEVICES.ASM @@ -0,0 +1,165 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/devices.asm 1.2 1994/04/28 12:41:41 jeff_wilson Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : DEVICES.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 12 December, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Get_Devices(VOID); * +; WORD Is_Device_Real(WORD drive); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +;---------------------------------------------------------------------------- + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Get_Devices :NEAR +GLOBAL Is_Device_Real :NEAR + +GLOBAL MaxDevice :BYTE +GLOBAL DefaultDrive :BYTE + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; VOID Get_Devices(VOID); +; WORD Is_Device_Real(WORD drive); +; +; ---------------------------------------------------------------- + +CODESEG + +;*********************************************************** +; +; GET_DEVICES +; +; VOID Get_Devices(VOID); +; +; This routine establishes the default disk drive and the maximum drive +; available in the current system. +; +;* +DOS equ 21h + +PROC Get_Devices C near + USES eax,ebx,edx + + sub eax,eax + mov ah,25 ; get current drive service + int DOS ; drive returned in al + mov [DefaultDrive],al ; save it + mov dl,al + mov ah,14 ; set current as current drive + int DOS + dec al ; al = max drives, make it n - 1 + xor ah,ah ; clear high byte + mov edx,eax ; use dx to go backward to find out + sub ebx,ebx + +??back_loop: + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + jnc short ??later ; if c clear, no error + cmp al,0Fh ; was it invalid? (0Fh = invalid) + jne short ??later ; yes, so LATER + dec edx + jmp ??back_loop ; try, try again + +??later: + mov eax,edx ; restore ax + mov [MaxDevice],al ; save the max drive # + + ret + +ENDP Get_Devices + +;*************************************************************** + + +;*************************************************************** +; +; IS_DEVICE_REAL +; +; WORD Is_Device_Real(WORD drive); +; +; This routine will tell whether or not a device is a true +; phisical one. Send it the drive # to check. +; +;* +PROC Is_Device_Real C near + USES ebx,edx + ARG drive:WORD + + sub edx,edx + mov dx,[drive] + +??next_drive: + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + + jnc short ??it_is_real ; jump if no error + cmp al,01 ; 1 = invalid command, + ; 0F = invalid device + je short ??real ; 1? it is ok (RAM device) + jmp short ??invalid ; 0Fh, it was not a device + +??it_is_real: + cmp al,0 ; was it a fixed device? + je short ??real ; yes, it's ok + dec al ; make it a drive # + cmp al,dl ; is it a valid drive? + je short ??real + +??invalid: ; The device is invalid. + mov eax,0 + jmp short ??out + +??real: ; Return true, for valid device. + mov eax,1 + +??out: + ret +ENDP Is_Device_Real + +;*************************************************************** + +END + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DEVTABLE.ASM b/WIN32LIB/SRCDEBUG/DEVTABLE.ASM new file mode 100644 index 0000000..9dfcce7 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DEVTABLE.ASM @@ -0,0 +1,204 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/devtable.asm 1.2 1994/04/28 12:41:29 jeff_wilson Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : DEVTABLE.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 12 December, 1990 [CY] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Init_Device_Table(BYTE *table); * +; WORD Max_Device(VOID); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +DOS equ 21h + +GLOBAL Max_Device :NEAR +GLOBAL get_max_device :NEAR +GLOBAL Init_Device_Table :NEAR + + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; VOID Init_Device_Table(BYTE *table); +; WORD Max_Device(VOID); +; +; ---------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; WORD Max_Device(VOID); +; + +PROC Max_Device C NEAR + + call get_max_device ; get max devices in ax + ret + +ENDP Max_Device + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; +; returns max devices in AX + +PROC get_max_device C NEAR + USES ebx,edx + + mov ah,25 ; get current drive service + int DOS ; drive returned in al + mov dl,al + mov ah,14 ; set current as current drive + int DOS + dec al ; al = max drives, make it n - 1 + xor ah,ah ; clear high byte + sub edx,edx + mov edx,eax ; use dx to go backward to find out + ; if DOS is lying (down) + +??back_loop: + push ds + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + pop ds + jnc short ??later ; if c clear, no error + + cmp al,0Fh ; was it invalid? (0Fh = invalid) + jne short ??later ; yes, so LATER + + dec edx + jmp ??back_loop ; try, try again + +??later: + mov eax,edx ; restore ax + ret + +ENDP get_max_device + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; VOID Init_Device_Table(BYTE *table); +; + +PROC Init_Device_Table C NEAR + + USES eax,ebx,edi,edx + ARG table:DWORD ; Pointer to device table. + LOCAL curr_drive:BYTE ; Copy of current drive number. + + mov edi,[table] + + call get_max_device ; get max devices in ax + add edi,eax + std + mov [curr_drive],al ; save it + +??next_drive: + mov dl,[curr_drive] ; copy current drive # + cmp dl,0FFh ; are we done? + je short ??later ; if so, later + + dec [curr_drive] ; dec our local drive # + + push ds + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + pop ds + + jnc short ??it_is_real ; jump if no error + cmp al,01 ; 1 = invalid command, + ; 0F = invalid device + je short ??set_as_current ; 1? it is ok (RAM device) + jmp short ??invalid ; 0Fh, it was not a device + + +??it_is_real: + cmp al,0 ; was it a fixed device? + je short ??set_as_current ; yes, it's ok + + dec al ; make it a drive # + cmp al,dl ; is it a valid drive? + je short ??set_as_current + +; +; Device was logical and not active, so clear the entry +; +??invalid: + xor al,al + stosb + cmp [curr_drive],0 ; are we done checking? + jge ??next_drive ; no, go to next + + jmp short ??later + +??set_as_current: + mov al,1 + stosb + cmp dl,0 ; are we before the A drive (invalid) + jl short ??later ; yes, we are done checking + + jmp ??next_drive ; keep processing + +??later: + cld + ret + +ENDP Init_Device_Table + +;---------------------------------------------------------------------------- + +END + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DIPTHONG.CPP b/WIN32LIB/SRCDEBUG/DIPTHONG.CPP new file mode 100644 index 0000000..53b1e36 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DIPTHONG.CPP @@ -0,0 +1,328 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + char temp; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + temp = *src++; + temp += 127; + *dest++ = temp; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int common, // Common character index. + dipthong; // Dipthong character index. + + unsigned long length=0; // Length of output string + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = (char)KA_EXTEND; + first -= 127; + length++; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char) (common << 3); + first |= (unsigned char)dipthong; + first |= (unsigned char)0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + length++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(length); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ + +#define TXT_GUEST 4567+3 +#define TXT_LOGIN 4567+4 +#define TXT_LOGIN_TO_INTERNET 4567+5 +#define TXT_YOUR_HANDLE 4567+6 +#define TXT_YOUR_PASSWORD 4567+7 +#define TXT_INTERNET_HOST 4567+8 +#define TXT_INTERNET_JOIN 4567+9 +#define TXT_INTERNET_GAME_TYPE 4567+10 +#define TXT_JOIN_INTERNET_GAME 4567+11 +#define TXT_ENTER_IP_ADDRESS 4567+12 +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + +static char InternetTxt[22][40]={ + "Internet H2H", + "Host Internet Game", + "Join Internet Game", + "Guest", + "Login", + "Login to Planet Westwood", + "Planet Westwood Handle", + "Planet Westwood Password", + "Host Game", + "Join Game", + "Choose Type of Internet Game", + "Join Internet Game", + "Address of Host", + "Connecting...", + "Connection Error!", + "Unable to connect to host!", + "Connecting to host...", + "Unable to resolve host address!", + "Unable to accept client connection", + "Unable to connect!", + "Connection lost!", + "Resolving address of host..." +}; + +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + + if (!data || string < 0) return(NULL); + + if (string >= 4567) return (InternetTxt[string-4567]); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/WIN32LIB/SRCDEBUG/DRAWLINE.ASM b/WIN32LIB/SRCDEBUG/DRAWLINE.ASM new file mode 100644 index 0000000..a4c9a7a --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DRAWLINE.ASM @@ -0,0 +1,464 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + + +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg ??big_line + mov al,[byte color] + rep stosb ; write as many words as possible + jmp short ??out ; get outt + + +??big_line: + mov al,[byte color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + +??aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp ??out + + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP Buffer_Draw_Line + + +END diff --git a/WIN32LIB/SRCDEBUG/DRAWRECT.CPP b/WIN32LIB/SRCDEBUG/DRAWRECT.CPP new file mode 100644 index 0000000..9c7a523 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DRAWRECT.CPP @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "gbuffer.h" + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Lock(); + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); + Unlock(); +} diff --git a/WIN32LIB/SRCDEBUG/DRAWSHP.ASM b/WIN32LIB/SRCDEBUG/DRAWSHP.ASM new file mode 100644 index 0000000..526893d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DRAWSHP.ASM @@ -0,0 +1,1128 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : DRAWSHP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : April 13, 1992 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* Not_Supported -- Replacement function for Draw_Shape routines not used* +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +; this struct is here to remove the hardwire way of programing +; implemented in the funtion Draw_Shape in ian image of + +STRUC VVPC_IMAGE + Off dd ? + Width dd ? + Height dd ? + Page dd ? +ENDS + + + +STRUC GVPC_IMAGE + vvpc VVPC_IMAGE <> + Xpos dd ? + Ypos dd ? + GraphicBuff dd ? +ENDS + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;****************************** Declarations ******************************** +GLOBAL Draw_Shape:NEAR +GLOBAL LCW_Uncompress:NEAR +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD + +GLOBAL MaskPage : dword +GLOBAL BackGroundPage : dword + + +;********************************* Data ************************************ + DATASEG +;--------------------------------------------------------------------------- +; Shape buffer & its size, set by Set_Shape_Buffer() +;--------------------------------------------------------------------------- +_ShapeBuffer DD 0 +_ShapeBufferSize DD 0 + +;--------------------------------------------------------------------------- +; Address of MaskPage & BackGroundPage, set by Init_Priority_System() +;--------------------------------------------------------------------------- +MaskPage DD 0 +BackGroundPage DD 0 + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +PredCount DD 0 +PredTable DB 1, 3, 2, 5, 4, 3, 2, 1 +PredValue DD 1 +PartialPred DD 0 ; partially faded predator effect value +PartialCount DD 0 + +;--------------------------------------------------------------------------- +; 32 bit versions of 16 bit stack variables +;--------------------------------------------------------------------------- +Flags DD ? ; globally accessible copy of flags + +viewport_ptr DD ? ; pointer to upper-left corner of viewport +viewport_width DD ? ; viewport width +viewport_height DD ? ; viewport height +viewport_yadd DD ? ; viewport y add +viewport_x DD ? ; viewport x-coord +viewport_y DD ? ; viewport y-coord +buff_ptr DD ? ; pointer to buffer containing viewport + +;********************************* Code ************************************ + CODESEG + + +;*************************************************************************** +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* * +;* INPUT: * +;* DWORD gvp_ptr ; pointer to graphic viewport info * +;* DWORD shape_ptr ; the shape pointer to draw * +;* DWORD draw_x ; x-coord of hotspot in viewport * +;* DWORD draw_y ; y-coord of hotspot in viewport * +;* DWORD flags ; the flags for drawing the shape * +;* * +;* Optional Arguments: If the following flags are used, the given args * +;* MUST be present. Note that, if more than one one set of args is used, * +;* they must appear in this order (alphabetical). * +;* SHAPE_COLOR: DWORD color_table (256 bytes) * +;* SHAPE_FADING: DWORD fade_table (256 bytes), DWORD fade_count * +;* SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl * +;* SHAPE_PARTIAL: DWORD predator partial_value (0-255) * +;* SHAPE_PRIORITY: DWORD priority_level * +;* SHAPE_SCALING: DWORD x_scale, WORD y_scale * +;* SHAPE_SHADOW: DWORD shadowing_table (256 bytes) * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* File Organization: * +;* drawshp.asm : this file * +;* shape.inc : main shape header file; contains declarations for all * +;* globals, procedures and constants * +;* ds_table.asm: contains the procedure address tables for LSkipRout, * +;* RSkipRout, DrawRout, & PixelRout * +;* ds_l*.asm : left-skip routines * +;* ds_r*.asm : right-skip routines * +;* ds_d*.asm : drawing routines * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Shape format: * +;* Header: * +;* UWORD SType (0=normal, 1=16-color, 2=uncompressed, 4=variable-color) * +;* UBYTE Height * +;* UWORD Width * +;* UBYTE unmodified height * +;* UWORD size of shape in memory, including this header * +;* UWORD uncompressed data size * +;* Normal Shape: * +;* UBYTE [compressed] Shape data * +;* 16-color shape: * +;* UBYTE 16-color table * +;* UBYTE [compressed] Shape data * +;* variable-color shape: * +;* UBYTE # colors * +;* UBYTE color table (variable-length) * +;* UBYTE [compressed] Shape data * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Uncompressed shape data format: * +;* Data is stored as a bitmap, with 0's treated as a special case. Every * +;* 0 byte is followed by a repetition count byte. Every scan line is * +;* compressed separately. Thus, the following bitmap results in the * +;* following shape data: * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* * +;*-------------------------------------------------------------------------* +;* * +;* How scaling is handled: * +;* Scaling is done by accumulating the x & y scale values. When the high * +;* byte of the accumulated value is set, the pixel (for x-scaling) or * +;* the line (for y-scaling) is drawn. The high byte is then cleared, * +;* and the low byte is left so roundoffs continue to accumulate. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Drawing Procedures: * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* RSkipRout: skips given # bytes of data on the right-hand side of a * +;* shape. Just has to handle changing the current byte offset in the * +;* shape data buffer, since the draw routine knows where the left-hand * +;* side of the drawable region is. The routine may skip more bytes than * +;* it was told if it encounters a run of 0's, but it's assumed that a * +;* run of 0's will never go past the right edge of a shape. * +;* Input: * +;* ECX - number of uncompressed bytes to skip * +;* ESI - shape buffer data address * +;* Output: * +;* ECX - negative # bytes overrun, or 0 * +;* ESI - updated to the current location in the shape data * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* LSkipRout: skips given # bytes of data on the left-hand side of a * +;* shape. This routine must update the shape data byte offset, and it * +;* must properly update the current drawing position due to scaling, so * +;* it's a little more involved than the RSkip routine. The routine may * +;* skip more bytes than it's told if it encounters a run of 0's. If this * +;* happens, the draw routine must take these extra bytes into * +;* consideration. * +;* Input: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width in bytes * +;* Output: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - accumulated XTotal value at new pixel location * +;* ESI - updated to the current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* DrawRout: draws one row of pixels, handles scaling, reversal and * +;* any per pixel effects like predator, shadow etc. * +;* EDX must be set up as follows: * +;* - No scaling: 0 * +;* - No left-clipping: 0 * +;* - Left clipping, but no overrun: set to computed initial value for * +;* that viewport coordinate * +;* - Left clipping, with overrun: set to XTotal value for that coordinate * +;* In any case, only the low byte of DL should contain data; the current * +;* byte in the shapebuffer should always be the first drawable pixel, * +;* even if it's partially clipped (in which case DL will contain data). * +;* Input: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* Output: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - decremented by # bytes (not pixels) drawn * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Algorithm: * +;* - Initialize globals * +;* - Pull optional arguments off the stack * +;* - Set up drawing procedure pointers based on drawing flags * +;* - Read the values from the shape header * +;* - Compute the shape's scaled width & height * +;* - Adjust the shape's drawing coordinates based on centering & * +;* viewport-relative flag settings * +;* - Compute the clipped areas of the shape * +;* - Compute the number of drawn pixels horizontally & vertically * +;* - Compute the starting drawing offset in the viewport * +;* - Draw the shape * +;* * +;*-------------------------------------------------------------------------* +;* * +;* HISTORY: * +;* 04/13/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/26/1994 BR : Converted to 32-bit, restructured quite a bit. * +;* 08/09/1994 IML : Added C++ style interface. Various optimizations. * +;* 09/06/1994 IML : Ammendments for integration of p_* and ds_* routines.* +;* 09/14/1994 IML : Now handles LCW compression. * +;*=========================================================================* +PROC Draw_Shape C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ;-------------------------------------------------------------------- + ; Arguments: + ;-------------------------------------------------------------------- + ARG gvp_ptr:DWORD ; pointer to graphic viewport info + ARG shape_ptr:DWORD ; the shape pointer to draw + ARG draw_x:DWORD ; the destination x pixel + ARG draw_y:DWORD ; the destination y pixel + ARG flags:DWORD ; the flags for drawing the shape + +IF FALSE + ;-------------------------------------------------------------------- + ; Define the local stack variables that Draw_Shape needs. These + ; parameters are defined in shape.inc. They're included here + ; just for reference. + ;-------------------------------------------------------------------- + ; + ;...................... proc addresses .............................. + ; + LOCAL LSkipRout:DWORD ; pointer to the skip routine + LOCAL RSkipRout:DWORD ; pointer to the skip routine + LOCAL DrawRout:DWORD ; pointer to the draw routine + ; + ;.................... optional arguments ............................ + ; + LOCAL ColorTable:DWORD ; ptr to the shapes color table + LOCAL FadingTable:DWORD ; extracted fading table pointer + LOCAL FadingNum:DWORD ; get the number of times to fade + LOCAL IsTranslucent:DWORD ; ptr to "are we translucent?" tbl + LOCAL Translucent:DWORD ; ptr to "ok we are translucent!" tbl + LOCAL PriLevel:BYTE ; the priority level of the object + LOCAL ScaleX:DWORD ; the x increment to scale by + LOCAL ScaleY:DWORD ; the y increment to scale by + LOCAL ShadowingTable:DWORD ; ptr to the shadowing table + ; + ;.................... Shape header values ........................... + ; + LOCAL ShapeType:DWORD ; shape type + LOCAL ShapeWidth:DWORD ; shape's unscaled width + LOCAL ShapeHeight:DWORD ; shape's unscaled height + LOCAL UncompDataLen:DWORD ; uncompressed data length + LOCAL ShapeData:DWORD ; pointer to [compressed] shape data + ; + ;.................. Scaled shape dimensions ......................... + ; + LOCAL ScaledWidth:DWORD ; shape's scaled width + LOCAL ScaledHeight:DWORD ; shape's scaled height + ; + ;.................. Pixel clipping variables ........................ + ; + LOCAL LeftClipPixels:DWORD ; # left-clipped pixels + LOCAL RightClipPixels:DWORD ; # right-clipped pixels + LOCAL TopClipPixels:DWORD ; # top-clipped pixels + LOCAL BotClipPixels:DWORD ; # bottom-clipped pixels + LOCAL PixelWidth:DWORD ; width of drawable area in pixels + LOCAL PixelHeight:DWORD ; height of drawable area in pixels + ; + ;..................... Drawing variables ............................ + ; + LOCAL NumColors:DWORD ; # colors for 16-color shapes + LOCAL StartDraw:DWORD ; ptr to starting draw position + LOCAL NextLine:DWORD ; offset of next drawing line + LOCAL LeftClipBytes:DWORD ; # left-clipped bytes + LOCAL XTotal:DWORD ; accumulated x-pixels for scaling + LOCAL XTotalInit:DWORD ; initial roundoff bits for XTotal + LOCAL YTotal:DWORD ; accumulated y-pixels for scaling + LOCAL HeightCount:DWORD ; height counter for drawing lines + LOCAL LineStart:DWORD ; address of start of line + LOCAL WidthCount:DWORD ; counts down # bytes skipped + LOCAL StashReg:DWORD ; temp variable for draw routines + LOCAL MaskAdjust:DWORD ; priority buffer offset + LOCAL BackAdjust:DWORD ; background buffer offset + LOCAL StashECX:DWORD ; temp variable for ECX register + LOCAL StashEDX:DWORD ; temp variable for EDX register + +ENDIF + + ;==================================================================== + ; Initialization: + ; - allocate space for globals + ; - validate shape pointer + ; - set SHAPE_COMPACT flag if needed + ;==================================================================== + ;-------------------------------------------------------------------- + ; Allocate stack space for our local variables. + ;-------------------------------------------------------------------- + LOCAL Local_Stack:BYTE:Local_Size + + ;-------------------------------------------------------------------- + ; Make sure the shape pointer is not NULL + ;-------------------------------------------------------------------- + cmp [shape_ptr],0 ; compare shape ptr value to NULL + jnz ??valid_shp ; if non-zero, it's valid + jmp ??exit ; otherwise get the heck outta here + + ;-------------------------------------------------------------------- + ; Move gvp info into local variables + ;-------------------------------------------------------------------- +??valid_shp: + mov edi,[gvp_ptr] ; get pointer to graphic viewport info + mov esi, [(type GVPC_IMAGE ptr edi). vvpc . Off ] ; extract viewport pointer + mov [viewport_ptr],esi + mov ebx,[(type GVPC_IMAGE ptr edi) . vvpc . Width ] ; extract viewport width + mov [viewport_width],ebx + mov eax,[(type GVPC_IMAGE ptr edi) . vvpc . Height ] ; extract viewport height + mov [viewport_height],eax + mov ecx,[(type GVPC_IMAGE ptr edi) . vvpc . Page ] ; calculate y add value + add ecx,ebx + mov [viewport_yadd],ecx + mov eax,[(type GVPC_IMAGE ptr edi) . Xpos ] ; extract viewport x-coord + mov [viewport_x],eax + mov eax, [(type GVPC_IMAGE ptr edi) . Ypos ] ; extract viewport y-coord + mov [viewport_y],eax + mul ecx ; calculate buffer pointer + add eax,[viewport_x] + sub esi,eax + mov [buff_ptr],esi + + + ;-------------------------------------------------------------------- + ; If this shape is a compact shape, set that bit in the flags arg + ;-------------------------------------------------------------------- + mov edi,[shape_ptr] ; check for compact shape flag + test [BYTE PTR edi],MAKESHAPE_COMPACT + + jz ??do_args ; if not process flags as is + or [flags],SHAPE_COMPACT ; mark it as a compact shape + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi,4 ; optional params start past flags + + ;-------------------------------------------------------------------- + ; Initialize optional argument values: + ;-------------------------------------------------------------------- + mov [ColorTable],0 ; default = NULL + mov [FadingTable],0 ; default = NULL + mov [FadingNum],0 ; default = no fading + mov [IsTranslucent],0 ; default = NULL + mov [Translucent],0 ; default = NULL + mov [PriLevel],0 ; default = no priority + mov [ScaleX],100h ; default = unity X scaling + mov [ScaleY],100h ; default = unity Y scaling + mov [ShadowingTable],0 ; default = NULL + + ;-------------------------------------------------------------------- + ; SHAPE_COLOR: DWORD color_table[256] + ;-------------------------------------------------------------------- +??color: + test [flags],SHAPE_COLOR ; does it have a color table + jz ??fading ; if not skip to fading + or [flags],SHAPE_COMPACT ; mark it as a compact shape + ; (for remapping purposes only) + mov eax,[flags + edi] + mov [ColorTable],eax ; save address of color table + add edi,4 ; point to next optional argument + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- +??fading: + test [flags],SHAPE_FADING ; are we fading this shape + jz ??ghost ; skip to ghosting check + mov eax,[flags + edi] + mov [FadingTable],eax ; save address of fading tbl + + mov eax,[flags + edi + 4] ; get fade num + + add edi,8 ; next argument + cmp eax,0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + and [flags],NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [FadingNum],eax + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl + ;-------------------------------------------------------------------- +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz ??init_predator ; skip to predator check + mov eax,[flags + edi] + mov [IsTranslucent],eax ; save ptr to is_trans. tbl + mov eax,[flags + edi + 4] + mov [Translucent],eax ; save ptr to translucent tbl + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: Initialize the predator effect variables + ;-------------------------------------------------------------------- +??init_predator: + test [flags],SHAPE_PREDATOR ; is predator effect on + jz ??partial ; if not skip to partial + inc [PredCount] ; the pred table is byte aligned + and [PredCount],PRED_MASK ; keep entries within bounds + mov eax,[PredCount] + mov al,[BYTE PTR PredTable + eax] + mov [PredValue],eax ; put the pred value cs + mov [PartialCount],0 ; clear the partial count + mov [PartialPred],100h ; init partial to off + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??partial: + test [flags],SHAPE_PARTIAL ; is this a partial pred? + jz ??priority ; if not check priority + mov eax,[flags + edi] ; pull the partial value + mov [PartialPred],eax ; store it off + add edi,4 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PRIORITY: DWORD priority_level + ;-------------------------------------------------------------------- +??priority: + test [flags],SHAPE_PRIORITY ; is this a priority draw + jz ??scale ; if not skip to scale + mov eax,[flags + edi] + mov [PriLevel],al ; store priority level + add edi,4 ; next argument + mov eax,[MaskPage] ; calculate priority buffer + sub eax,[buff_ptr] ; offset + mov [MaskAdjust],eax + mov eax,[BackGroundPage] ; calculate background buffer + sub eax,[buff_ptr] ; offset + mov [BackAdjust],eax + + ;-------------------------------------------------------------------- + ; SHAPE_SCALING: DWORD x_scale, WORD y_scale + ;-------------------------------------------------------------------- +??scale: + test [flags],SHAPE_SCALING ; are we scaling this shape. + jz ??shadow ; if not then skip scale y value + mov eax,[flags + edi] + mov [ScaleX],eax + mov eax,[flags + edi + 4] + mov [ScaleY],eax + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_SHADOW: DWORD shadow_table[256] + ;-------------------------------------------------------------------- +??shadow: + test [flags],SHAPE_SHADOW ; are we ghosting this shape + jz short ??get_header ; if not then skip + mov eax,[flags + edi] + mov [ShadowingTable],eax ; save address of shadow table + add edi,4 ; next argument + + +??get_header: + ;==================================================================== + ; Get Shape header values + ;==================================================================== + mov esi,[shape_ptr] ; prepare to read header + movzx eax,[WORD PTR esi] + mov [ShapeType],eax ; extract shape type + movzx eax,[BYTE PTR esi + 2] + mov [ShapeHeight],eax + movzx eax,[WORD PTR esi + 3] ; extract shape height + mov [ShapeWidth],eax + movzx eax,[WORD PTR esi + 8] ; extract uncompressed data length + mov [UncompDataLen],eax + add esi,10 ; reposition index + + ;-------------------------------------------------------------------- + ; Now get NumColors, ColorTable address, & data pointer: + ; <16-color shape: + ; shape.Colortable[0] = # colors + ; shape data is after that many colors + ; 16-color shape: + ; shape.Colortable[] contains colors + ; shape data is after those colors + ; default 256-color shape: + ; shape data starts at shape.Colortable[0] + ; Note: ColorTable is set only if flags & SHAPE_COLOR is 0; otherwise, + ; the color table was passed in & we already have a pointer to it + ;-------------------------------------------------------------------- + ; + ;....................... <16-color shape: ........................... + ; + test [ShapeType],MAKESHAPE_VARIABLE + jz ??check_16 + movzx eax,[BYTE PTR esi] ; read # colors + mov [NumColors],eax ; save # colors + inc esi + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??norm_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??norm_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + + ;....................... 16-color shape: ............................ +??check_16: + test [ShapeType],MAKESHAPE_COMPACT + jz ??256_get_data_addr + mov [NumColors],16 ; save # colors + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??16_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??16_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + ; + ;....................... 256-color shape: ........................... + ; +??256_get_data_addr: + mov [ShapeData],esi ; set data address + + ;==================================================================== + ; Set up the drawing procedure addresses + ;==================================================================== + ;-------------------------------------------------------------------- + ; This code uses HORZ_REV, VERT_REV, & SCALING flags as an + ; offset into the LSkipTable, RSkipTable, and DrawTable. These + ; flags combined have values from 00h-07h, so each table must have + ; at least 8 entries. + ;-------------------------------------------------------------------- +??setup_procs: + mov ebx,[flags] ; load flags value + and ebx,07h ; clip high bits + add ebx,ebx ; mult by 4 to get DWORD offset + add ebx,ebx + mov eax,[LSkipTable + ebx] ; get table value + mov [LSkipRout],eax ; store it in the function pointer + mov eax,[RSkipTable + ebx] ; get table value + mov [RSkipRout],eax ; store it in the function pointer + mov eax,[DrawTable + ebx] ; get table value + mov [DrawRout],eax ; store it in the function pointer + +??compute_scalevals: + ;==================================================================== + ; Now compute scaled width & height. If the shape scales down to 0 + ; either horizontally or vertically, exit. + ;==================================================================== + test [flags],SHAPE_SCALING ; skip if no scaling + jz ??no_scaling + ; + ;........................ scaled width: ............................. + ; + mov eax,[ShapeWidth] ; get byte width + mov ebx,[ScaleX] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledWidth],eax ; save the scaled width + ; + ;........................ scaled height: ............................ + ; + mov eax,[ShapeHeight] ; get byte height + mov ebx,[ScaleY] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledHeight],eax ; save the scaled height + jmp ??handle_centering + ; + ;......................... no scaling: .............................. + ; +??no_scaling: + mov eax,[ShapeWidth] + mov [ScaledWidth],eax ; pixel width = byte width + mov eax,[ShapeHeight] + mov [ScaledHeight],eax ; pixel height = byte height + + ;==================================================================== + ; Allow for SHAPE_CENTER by adjusting the draw_x & draw_y arguments: + ; draw_x -= ScaledWidth / 2 + ; draw_y -= ScaledHeight / 2 + ;==================================================================== +??handle_centering: + ; + ;........................ adjust draw_x ............................. + ; + test [flags],SHAPE_CENTER ; skip if not centered + jz ??handle_vp_rel + mov eax,[draw_x] ; load in draw_x + mov edx,[ScaledWidth] ; load in ScaledWidth + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_x],eax ; store it back into draw_x + ; + ;........................ adjust draw_y ............................. + ; + mov eax,[draw_y] ; load in draw_y + mov edx,[ScaledHeight] ; load in ScaledHeight + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_y],eax ; store it back into draw_y + + ;==================================================================== + ; Allow for SHAPE_VIEWPORT_REL by adjusting draw_x & draw_y by the + ; viewport's coordinates + ;==================================================================== +??handle_vp_rel: + test [flags],SHAPE_VIEWPORT_REL ; skip if not vp-relative + jz ??compute_horz_clip + mov eax,[viewport_x] + add [draw_x],eax ; draw_x += viewport_x + mov eax,[viewport_y] + add [draw_y],eax ; draw_y += viewport_y + + ;==================================================================== + ; Now that we have the scaled size and adjusted x & y drawing + ; coordinates, we can compute the clipped areas of the shape: + ; LeftClipPixels = viewport_x - draw_x + ; - if negative, set to 0 + ; RightClipPixels = (draw_x + ScaledWidth) - + ; (viewport_x + viewport_width) + ; - if negative, set to 0 + ; + ; TopClipPixels = viewport_y - draw_y + ; - if negative, set to 0 + ; BotClipPixels = (draw_y + ScaledHeight) - + ; (viewport_y + viewport_height) + ; - if negative, set to 0 + ;==================================================================== +??compute_horz_clip: + ; + ;...................... left-clipped pixels ......................... + ; + mov eax,[viewport_x] + sub eax,[draw_x] ; EAX = viewport_x - draw_x + jge ??set_left_clip + mov eax,0 ; if EAX<0, set to 0 +??set_left_clip: + mov [LeftClipPixels],eax ; store # left-clipped pixels + ; + ;...................... right-clipped pixels ........................ + ; + mov eax,[draw_x] + add eax,[ScaledWidth] ; EAX = draw_x + ScaledWidth + mov edx,[viewport_x] + add edx,[viewport_width] ; EDX = viewport_x + viewport_width + sub eax,edx + jge ??set_right_clip + mov eax,0 ; if EAX<0, set to 0 +??set_right_clip: + mov [RightClipPixels],eax ; store # right-clipped pixels + ; + ;...................... top-clipped pixels .......................... + ; +??compute_vert_clip: + mov eax,[viewport_y] + sub eax,[draw_y] ; EAX = viewport_y - draw_y + jge ??set_top_clip + mov eax,0 ; if EAX<0, set to 0 +??set_top_clip: + mov [TopClipPixels],eax ; store # top-clipped pixels + ; + ;.................... bottom-clipped pixels ......................... + ; + mov eax,[draw_y] + add eax,[ScaledHeight] ; EAX = draw_y + ScaledHeight + mov edx,[viewport_y] + add edx,[viewport_height] ; EDX = viewport_y + viewport_height + sub eax,edx + jge ??set_bottom_clip + mov eax,0 ; if EAX<0, set to 0 +??set_bottom_clip: + mov [BotClipPixels],eax ; store # bottom-clipped pixels + + ;==================================================================== + ; Now compute the number of pixels actually drawn, horizontally and + ; vertically; exit if either is <= 0 + ;==================================================================== +??compute_drawn_pixels: + ; + ;.................... pixel width of drawn area ..................... + ; + mov eax,[ScaledWidth] ; get total width in pixels + sub eax,[LeftClipPixels] ; subtract off left-clipped pixels + sub eax,[RightClipPixels] ; subtract off right-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelWidth],eax ; store drawn pixel width + ; + ;.................... pixel height of drawn area .................... + ; + mov eax,[ScaledHeight] ; get total height in pixels + sub eax,[TopClipPixels] ; subtract off top-clipped pixels + sub eax,[BotClipPixels] ; subtract off bottom-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelHeight],eax ; store drawn pixel height + + ;==================================================================== + ; So, we're actually going to draw something; if (ShapeType & + ; MAKESHAPE_NOCOMP == 0) decompress the shape data into _ShapeBuffer: + ; LCW_Uncompress(ShapeData, _ShapeBuffer, UncompDataLen); + ; shape.DataLength + ; &_ShapeBuffer + ; &(shape's data) + ; - otherwise the shape data is already uncompressed + ;==================================================================== + test [ShapeType],MAKESHAPE_NOCOMP + jnz ??uncompressed + + mov eax,[UncompDataLen] + push eax ; push arg 3 + mov eax,[_ShapeBuffer] + push eax ; push arg 2 + mov eax,[ShapeData] + push eax ; push arg 1 + call LCW_Uncompress ; call routine + add esp,12 ; restore stack + mov eax,[_ShapeBuffer] + mov [ShapeData],eax + jmp ??copy_flags + +??uncompressed: +; mov eax,[ShapeData] ; set up pointer to shape data +; mov [_ShapeBuffer],eax + + + ;-------------------------------------------------------------------- + ; Set the global Flags variable + ;-------------------------------------------------------------------- +??copy_flags: + mov eax,[flags] + mov [Flags],eax + + ;==================================================================== + ; Now compute the actual buffer offset where drawing (not skipping) + ; will begin + ;==================================================================== + ;-------------------------------------------------------------------- + ; First, compute the x & y offsets of the shape's clipped upper-left + ; corner, relative to the viewport's upper-left corner: + ; x-offset = draw_x + LeftClipPixels - viewport_x + ; y-offset = draw_y + TopClipPixels - viewport_y + ;-------------------------------------------------------------------- + mov ebx,[draw_x] + add ebx,[LeftClipPixels] + sub ebx,[viewport_x] ; EBX = viewport x-offset + + mov eax,[draw_y] + add eax,[TopClipPixels] + sub eax,[viewport_y] ; EAX = viewport y-offset + + ;-------------------------------------------------------------------- + ; Then, adjust the viewport offsets due to horizontal & vertical + ; reversal: + ; if HORZ_REV, x-offset += (PixelWidth - 1) + ; if VERT_REV, y-offset += (PixelHeight - 1) + ;-------------------------------------------------------------------- + ; + ;................. Adjust for horizontal reversal ................... + ; + test [flags],SHAPE_HORZ_REV + jz ??adjust_vert_offset + add ebx,[PixelWidth] + dec ebx ; EBX = true x-offset + ; + ;................ Swap LeftClip & RightClip pixels .................. + ; + mov edx,[LeftClipPixels] ; exchange left & right-clipped pixels + xchg edx,[RightClipPixels] + mov [LeftClipPixels],edx + + ; + ;.................. Adjust for vertical reversal .................... + ; +??adjust_vert_offset: + test [flags],SHAPE_VERT_REV + jz ??adjust_pointer + add eax,[PixelHeight] + dec eax ; EAX = true y-offset + ; + ;.................. Swap TopClip & BotClip pixels ................... + ; + mov edx,[TopClipPixels] + xchg edx,[BotClipPixels] + mov [TopClipPixels],edx + + ;-------------------------------------------------------------------- + ; Now, adjust the starting position pointer: + ;-------------------------------------------------------------------- +??adjust_pointer: ;!!!!!!! convert to register mul for speed !!!!!!!! + add ebx,[viewport_ptr] ; add initial ptr to x-offset + mul [viewport_yadd] ; convert y-offset (EAX) to bytes + add ebx,eax ; add those bytes in + mov [StartDraw],ebx ; store the starting pointer + + ;-------------------------------------------------------------------- + ; Finally, if VERT_REV, negate yadd to move up not down: + ;-------------------------------------------------------------------- + test [flags],SHAPE_VERT_REV + jz ??init_xtotal + neg [viewport_yadd] ; move up, not down + + ;==================================================================== + ; Initialize the horizontal scale accumulation value: + ; If there are any left-clipped pixels, the scale accumulator will + ; have to be initialized with the value it >would< have by stepping + ; over that many pixels. This initial value can be computed by + ; dividing the # of left-clipped pixels by the x-scale value itself, + ; picking off the remainder from this division & negating it. This + ; sets the low byte of the remainder to the correct accumulation + ; value (the high bytes will be garbage). + ; (The alternative to this approach would be to multiply the + ; scale factor by the # clipped bytes, which is the result of the + ; division; however, negating the remainder is much faster than + ; the multiply would be.) + ;==================================================================== +??init_xtotal: + mov edx,0 ; prepare for divide + mov eax,[LeftClipPixels] ; get # left-clipped pixels + shl eax,8 ; multiply by 100h + mov ebx,[ScaleX] ; load ScaleX value + div bx ; 16-bit div: AX = rslt, DX = rem + mov [LeftClipBytes],eax ; save # left-clipped bytes + neg edx ; generate roundoff bits + and edx,0Fh ; only save low byte + mov [XTotalInit],edx ; save initial roundoff value + + ;==================================================================== + ; Initialize drawing variables: + ;==================================================================== + mov esi,[ShapeData] ; ESI = shape buffer starting point + mov edi,[StartDraw] ; EDI = drawing address + mov [YTotal],0 ; initialize accumulated scale + + ;==================================================================== + ; Clip the top-clipped lines. The object here is to set ESI to the + ; first drawable line in the _ShapeBuffer, and YTotal to: + ; high byte = # times to draw that line, + ; low byte = roundoff bits + ; + ; - Initialize values (ESI, HeightCount, YTotal) + ; - Skip loop if no top lines to clip + ; - Loop: + ; - save this line's byte position in _ShapeBuffer, in case we + ; overrun + ; - call RSkipRout with ECX set to # bytes to skip (one row) + ; - accumulate ScaleY into YTotal + ; - if high byte is non-zero, there are that many drawn lines: + ; - decrement HeightCount by that many lines + ; - clear the high byte in YTotal, but keep the roundoff bits + ; - if HeightCount > 0, loop again to clip more lines + ; - if HeightCount is 0, start drawing: + ; - ESI points to first non-clipped line in _ShapeBuffer + ; - YTotal contains 0 in high byte, roundoff bits in low byte + ; - otherwise, we've clipped too many lines: + ; - put ESI back to the line we just clipped + ; - set high byte of YTotal to # lines overrun + ; - subtract ScaleY from YTotal, to set it up for drawing loop + ;==================================================================== + ; + ;..................... skip if nothing to clip ...................... + ; + mov eax,[TopClipPixels] + cmp eax,0 ; see if any top-clipped pixels + jz ??draw_loop ; if not, skip this + mov [HeightCount],eax ; save off # lines to clip + +??clip_y_loop: + ; + ;...................... skip this row of bytes ...................... + ; + mov [LineStart],esi ; save this line's byte position + mov ecx,[ShapeWidth] ; set up ECX for RSkipRout + call [RSkipRout] ; skip 'ShapeWidth' bytes + ; + ;............... see if this row would have been drawn .............. + ; + mov eax,[ScaleY] + add [YTotal],eax ; accumulate scale factor + test [YTotal],0FF00h ; check to see if we draw the line + jz ??clip_y_loop ; if not loop again + ; + ;...................... decrement HeightCount ....................... + ; + mov eax,0 ; clear EAX + xchg al,[BYTE PTR YTotal+1] ; get # lines, clear it in YTotal + sub [HeightCount],eax ; subtract # drawn lines from HtCt + jg ??clip_y_loop ; if more lines remain, loop again + jns ??draw_loop ; is exactly 0; we're done clipping + ; + ;....................... adjust for overrun ......................... + ; + mov esi,[LineStart] ; point ESI back to this line + mov eax,[HeightCount] + neg eax ; EAX = # lines overrun + shl eax,8 ; multiply by 100h + add eax,[YTotal] ; add in roundoff bits + sub eax,[ScaleY] ; adjust down by y-scale + mov [YTotal],eax ; store in YTotal + + ;==================================================================== + ; The drawing loop (at long last!): + ; - Accumulate YTotal; if high byte is 0, skip this row of bytes & + ; loop again + ; - Skip left-clipped pixels: + ; - If we've skipped all the bytes on the line, just go to the + ; next line + ; - Draw middle pixels: + ; - Add the shape's pixel width to ECX (which could be negative + ; if we left-skipped into the drawable area) + ; - If ECX is still 0, there are no pixels to draw + ; - Otherwise, leave ECX as is & draw the pixels + ; - Skip right-clipped pixels: + ; - Add # right-clipped pixels to ECX (which could be negative if + ; the draw routine uncompressed 0's into the right-clipped + ; region) + ; - if ECX > 0, skip the remaining bytes + ; - Go to the next line: + ; - point EDI to the start of the next line in the viewport + ; - decrement the height counter, exit if it's 0 + ; - decrement YTotal's high byte + ; - if it's 0, go to the loop top + ; - otherwise, reset ESI to this line's start & redraw the line, + ; starting at left-clipped pixels + ; (NOTE: why not start drawing at middle pixels??????????) + ;==================================================================== +??draw_loop: + ; + ;................... accumulate YTotal & test it .................... + ; + mov eax,[ScaleY] ; get y scaling factor + add [YTotal],eax ; accumulate YTotal + test [YTotal],0FF00h ; see if we need to draw anything + jnz ??draw_line ; draw this line + ; + ;......................... skip this line ........................... + ; + mov ecx,[ShapeWidth] ; load shape's width in bytes + call [RSkipRout] ; skip this row & loop again + jmp ??draw_loop + + ; + ;--------------------- start drawing this line ---------------------- + ; +??draw_line: + mov [LineStart],esi ; save current byte position + ;.................................................................... + ; Skip left-clipped pixels: + ; - initialize [WidthCount] to total shape width in bytes + ; - set ECX to # >bytes< to clip + ; When LSkipRout returns: + ; - ECX will contain # >pixels< overrun + ; - EDX will contain the XTotal init value + ; - [WidthCount] will be decremented by total bytes skipped + ;.................................................................... +??draw_left: + mov eax,[ShapeWidth] ; load shape width + mov [WidthCount],eax ; set up for LSkipRout + mov ecx,[LeftClipBytes] ; bytes, not pixels! + call [LSkipRout] ; skip the bytes + cmp [WidthCount],0 + jz ??next_line ; The whole line was 0's + ;.................................................................... + ; Draw middle pixels: + ; - add PixelWidth to ECX (which may be negative) + ; - if ECX is 0, don't bother drawing + ; When DrawRout returns: + ; - ECX will contain # >pixels< overrun + ; - [WidthCount] will be decremented by # bytes drawn + ;.................................................................... +??draw_middle: + add ecx,[PixelWidth] ; since ECX could overrun, add width + jle ??draw_right ; if ECX<=0, no middle pixels to draw + call [DrawRout] ; draw the pixels + ; + ;................... skip past right-clipped pixels ................. + ; +??draw_right: + mov ecx,[WidthCount] ; ECX = remaining # bytes + jecxz ??next_line ; don't bother if no bytes remain + call [RSkipRout] ; skip right-clipped bytes + ; + ;----------------------- go to the next line ------------------------ + ; +??next_line: + ; + ;................. adjust EDI to start of next line ................. + ; + mov eax,[viewport_yadd] ; get yadd + add [StartDraw],eax ; add it to this line's position + mov edi,[StartDraw] ; EDI = next line + ; + ;................. decrement our pixel row counter .................. + ; + dec [PixelHeight] ; count down a line + jz ??exit ; we're done! + ; + ;.................. decrement YTotal's high byte .................... + ; + dec [BYTE PTR YTotal + 1] ; decrement high byte + jz ??draw_loop ; draw next line if 0 + ; + ;....................... re-draw this line .......................... + ; + mov esi,[LineStart] ; reset to this line's start + jmp ??draw_left ; redraw this line + +??exit: + ret + + ENDP Draw_Shape + + +;*************************************************************************** +;* Not_Supported -- Replacement function for Draw_Shape routines not used. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 08/24/1993 SKB : Created. * +;*=========================================================================* +PROC Not_Supported NOLANGUAGE NEAR + + ret + + ENDP Not_Supported + + END + +;************************** End of drawshp.asm ***************************** + + diff --git a/WIN32LIB/SRCDEBUG/DS_DN.ASM b/WIN32LIB/SRCDEBUG/DS_DN.ASM new file mode 100644 index 0000000..b0a8ac5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_DN.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Normal NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - add it to EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp short ??test_fading + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Normal + + END + +;**************************** End of ds_dn.asm ***************************** diff --git a/WIN32LIB/SRCDEBUG/DS_DR.ASM b/WIN32LIB/SRCDEBUG/DS_DR.ASM new file mode 100644 index 0000000..b46629c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_DR.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Reverse NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - subtract it from EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Reverse + + + END + +;**************************** End of ds_dr.asm ***************************** diff --git a/WIN32LIB/SRCDEBUG/DS_DS.ASM b/WIN32LIB/SRCDEBUG/DS_DS.ASM new file mode 100644 index 0000000..a8d312f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_DS.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + ENDP Draw_Scale + + END + +;**************************** End of ds_ds.asm ****************************** diff --git a/WIN32LIB/SRCDEBUG/DS_DSR.ASM b/WIN32LIB/SRCDEBUG/DS_DSR.ASM new file mode 100644 index 0000000..fcd6180 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_DSR.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DSR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*******p******************************************************************** +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale_Reverse NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + + ENDP Draw_Scale_Reverse + + END + +;*************************** End of ds_dsr.asm ****************************** diff --git a/WIN32LIB/SRCDEBUG/DS_LRS.ASM b/WIN32LIB/SRCDEBUG/DS_LRS.ASM new file mode 100644 index 0000000..a4d85e4 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_LRS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + add edi,ecx ; decrement EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Reverse_Skip + + END + +;*************************** End of ds_lrs.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_LS.ASM b/WIN32LIB/SRCDEBUG/DS_LS.ASM new file mode 100644 index 0000000..cbc2ae6 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_LS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + sub edi,ecx ; increment EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Skip + + END + +;**************************** End of ds_ls.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_LSRS.ASM b/WIN32LIB/SRCDEBUG/DS_LSRS.ASM new file mode 100644 index 0000000..77effd1 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_LSRS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + sub esi,eax ; decrement ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Reverse_Skip + + END + +;**************************** End of ds_lsrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_LSS.ASM b/WIN32LIB/SRCDEBUG/DS_LSS.ASM new file mode 100644 index 0000000..bf8221e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_LSS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/08/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + add esi,eax ; increment ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Skip + + END + +;**************************** End of ds_lss.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_RRS.ASM b/WIN32LIB/SRCDEBUG/DS_RRS.ASM new file mode 100644 index 0000000..c90e468 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_RRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Reverse_Skip + + END + +;**************************** End of ds_rrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_RS.ASM b/WIN32LIB/SRCDEBUG/DS_RS.ASM new file mode 100644 index 0000000..caec534 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_RS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Skip + + END + +;**************************** End of ds_rs.asm ****************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_RSRS.ASM b/WIN32LIB/SRCDEBUG/DS_RSRS.ASM new file mode 100644 index 0000000..3315082 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_RSRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Reverse_Skip + + END + +;*************************** End of ds_rsrs.asm **************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_RSS.ASM b/WIN32LIB/SRCDEBUG/DS_RSS.ASM new file mode 100644 index 0000000..798e207 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_RSS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Skip -- Skips past a scaled row of pixels on right side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Skip -- Skips past a scaled row of pixels on the right side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Skip + + END + +;*************************** End of ds_rss.asm ***************************** + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/DS_TABLE.ASM b/WIN32LIB/SRCDEBUG/DS_TABLE.ASM new file mode 100644 index 0000000..a351aa0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/DS_TABLE.ASM @@ -0,0 +1,187 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_TABLE.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;* This module sets up a table of procedure addresses for combinations of * +;* NORMAL, HORZ_REV and SCALING flags. * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;******************************** Equates ********************************** +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Draw_Shape. * +;*=========================================================================*/ +USE_NORMAL EQU TRUE +USE_HORZ_REV EQU TRUE +USE_VERT_REV EQU TRUE +USE_SCALING EQU TRUE + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IFNB + GLOBAL varname:DWORD + ENDIF + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + + +;--------------------------------------------------------------------------- +; Data Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; LSkipTable +; RSkipTable +; DrawTable +;--------------------------------------------------------------------------- + + DATASEG + +;--------------------------------------------------------------------------- + +WANT equ +USE Left_Skip, LSkipTable + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Skip + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Right_Skip, RSkipTable + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Skip + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Draw_Normal, DrawTable + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Normal + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse +;--------------------------------------------------------------------------- + +;--------------------------------------------------------------------------- + END + +;************************** End of ds_table.asm **************************** diff --git a/WIN32LIB/SRCDEBUG/EXIT.CPP b/WIN32LIB/SRCDEBUG/EXIT.CPP new file mode 100644 index 0000000..2af49ad --- /dev/null +++ b/WIN32LIB/SRCDEBUG/EXIT.CPP @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library source * + * * + * File Name : EXIT.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Exit -- Exit routine with message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "misc.h" + +#include + +#include +#include +#include + + + + +/*************************************************************************** + * EXIT -- Exit routine with message. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/03/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Exit(INT errorval, const char *message, ...) +{ + va_list argptr; + char errstring[256]; + + Prog_End(); + + if (message && *message) { + va_start (argptr, message); + vsprintf ((char *)errstring, (const char *)message, argptr); + va_end (argptr); + printf(errstring); + } + + ::exit(errorval); + +} + +void randomize ( void ) +{ + srand ( time ( NULL ) ) ; +} + +#if(0) +unsigned long random ( unsigned long mod ) +{ + return rand () * mod / RAND_MAX ; +} +#endif diff --git a/WIN32LIB/SRCDEBUG/FACING16.ASM b/WIN32LIB/SRCDEBUG/FACING16.ASM new file mode 100644 index 0000000..6e05705 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FACING16.ASM @@ -0,0 +1,148 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./facing16.asm 1.10 1994/05/20 15:32:36 joe_bostic Exp $ +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +; long Desired_Facing16(long x1, long y1, long x2, long y2); + + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END + + + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/FACING8.ASM b/WIN32LIB/SRCDEBUG/FACING8.ASM new file mode 100644 index 0000000..b20ae3c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FACING8.ASM @@ -0,0 +1,140 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +; long Desired_Facing8(long x1, long y1, long x2, long y2); + + PROC Desired_Facing8 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 ; Close to major axis bit. + + xor eax,eax + mov al,[NewFacing8+ebx] + + ; Normalize to 0..FF range. + shl eax,5 + + ret + + ENDP Desired_Facing8 + + + END diff --git a/WIN32LIB/SRCDEBUG/FACINGFF.ASM b/WIN32LIB/SRCDEBUG/FACINGFF.ASM new file mode 100644 index 0000000..e9bff57 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FACINGFF.ASM @@ -0,0 +1,165 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACINGFF.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing256 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing256 :NEAR +; INCLUDE "wwlib.i" +INCLUDE "..\include\gbuffer.inc" + + CODESEG + +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ +; LONG cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) + PROC Desired_Facing256 C near + USES ebx, ecx, edx + + ARG srcx:DWORD + ARG srcy:DWORD + ARG dstx:DWORD + ARG dsty:DWORD + + xor ebx,ebx ; Facing number. + + ; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short ??xnotneg + neg ecx + mov ebx,11000000b ; Set bit 7 and 6 for leftward. +??xnotneg: + + ; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ??ynotneg + xor ebx,01000000b ; Complement bit 6 for downward. + neg eax +??ynotneg: + + ; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + ; Determine if the direction is closer to the Y axis and make sure that + ; CX holds the larger of the two deltas. This is in preparation for the + ; divide. + cmp eax,ecx + jb short ??gotaxis + xchg eax,ecx + xor edx,01000000b ; Closer to Y axis so make DX=64 for quad 0 and 2. +??gotaxis: + + ; If closer to the X axis then add 64 for quadrants 0 and 2. If + ; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + ; add value is in DX and save on stack. + push edx + + ; Make sure that the division won't overflow. Reduce precision until + ; the larger number is less than 256 if it appears that an overflow + ; will occur. If the high byte of the divisor is not zero, then this + ; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short ??nooverflow +??again: + test ecx,0FFFFFF00h + jz short ??nooverflow + shr ecx,1 + shr eax,1 + jmp short ??again +??nooverflow: + + ; Make sure that the division won't underflow (divide by zero). If + ; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short ??nounderflow + mov eax,0FFFFFFFFh + jmp short ??divcomplete + + ; Derive a pseudo angle number for the octant. The angle is based + ; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +??nounderflow: + xor edx,edx + shld edx,eax,8 ; shift high byte of eax into dl + shl eax,8 + div ecx +??divcomplete: + + ; Integrate the 5 most significant bits into the angle index. If DX + ; is not zero, then it is 64. This means that the dividend must be negated + ; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short ??noneg + dec edx + neg eax +??noneg: + add eax,edx + add eax,ebx + and eax,0FFH + ret + + ENDP Desired_Facing256 + + + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/FADING.ASM b/WIN32LIB/SRCDEBUG/FADING.ASM new file mode 100644 index 0000000..854d09a --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FADING.ASM @@ -0,0 +1,215 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini + cmp [dest],0 + je ??fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 +??innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short ??notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne ??mainloop + +??fini: + mov eax,[dest] + ret + + ENDP Build_Fading_Table + + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/FFIRST.ASM b/WIN32LIB/SRCDEBUG/FFIRST.ASM new file mode 100644 index 0000000..edda9ba --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FFIRST.ASM @@ -0,0 +1,238 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : First First * +;* * +;* File Name : FFIRST.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : April 15, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Find_First -- Find a file spec * +;* Find_Next -- Find next file in sreach params * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +GLOBAL Find_First :NEAR +GLOBAL Find_Next :NEAR + +;============================================================================ +CODESEG + + +;*************************************************************************** +;* FIND_FIRST -- Find a file spec * +;* * +;* * +;* * +;* INPUT: * +;* file_name File spec to find. Maybe a wildcard name * +;* mode File type * +;* ffblk file data block ptr to write info into * +;* * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/15/1994 jaw: Created. * +;*=========================================================================* + +PROC Find_First C near + USES ebx,ecx,edx,esi,edi,es,ds + ARG file_name:DWORD,mode:WORD,ffblk:DWORD + + mov edx,[file_name] + mov cx,[mode] + + mov eax,4e00h ;first firstg function + + int 21h + ;Find it? + jnc ??found_it ;=>yes + + ; ax holds the error code + ;insure high word of eax is clear + or eax,0ffffffffh + jmp ??exit + +??found_it: + ; found something + ;copy the DTA into the user block + mov eax,2f00h ;get DTA address + int 21h + + mov ax,es ;switch selectors + mov dx,ds + mov ds,ax + mov es,dx + + mov esi,ebx + mov edi,[ffblk] + + add esi,21 ;SKIP RESERVED + add edi,4 ;SKIP RESERVED + + sub eax,eax + mov al,[esi] ;get attrib byte + mov [es:edi+4],eax + inc esi + + ;get time + mov ax,[esi] + add esi,2 + mov [es:edi+8],ax + + ;get date + mov ax,[esi] + add esi,2 + mov [es:edi+10],ax + + ;get file size + mov eax,[esi] + add esi,4 + mov [es:edi],eax + + add edi,12 + + mov ecx,13 + + rep movsb ;copy the DTA name + + mov ax,es + mov ds,ax + + xor eax,eax +??exit: + ret +;==================== +ENDP Find_First + + + +;*************************************************************************** +;* FIND_NEXT -- Find next file in sreach params * +;* * +;* * +;* * +;* INPUT: * +;* NONE * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/15/1994 jaw: Created. * +;*=========================================================================* + +PROC Find_Next C near + USES ebx,ecx,edx,esi,edi,ds,es + + ARG ffblk:DWORD + + mov eax,04f00h ;Find Next function + + int 21h + ;Find anything? + jnc ??found_it ;=>no + + ; ax holds the error code + ;insure high word of eax is clear + or eax,0ffffffffh + jmp ??exit + +??found_it: + ; found something + ;copy the DTA into the user block + mov eax,2f00h ;get DTA address + int 21h + + mov ax,es ;switch selectors + mov dx,ds + mov ds,ax + mov es,dx + + mov esi,ebx + mov edi,[ffblk] + + add esi,21 ;SKIP RESERVED + add edi,4 ;SKIP RESERVED + + sub eax,eax + mov al,[esi] ;get attrib byte + mov [es:edi+4],eax + inc esi + + ;get time + mov ax,[esi] + add esi,2 + mov [es:edi+8],ax + + ;get date + mov ax,[esi] + add esi,2 + mov [es:edi+10],ax + + ;get file size + mov eax,[esi] + add esi,4 + mov [es:edi],eax + + add edi,12 + + mov ecx,13 + + rep movsb ;copy the DTA name + + mov ax,es + mov ds,ax + + xor eax,eax +??exit: + ret + +ENDP Find_Next + + +END + + + + + + + + + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/FFIXSEL.ASM b/WIN32LIB/SRCDEBUG/FFIXSEL.ASM new file mode 100644 index 0000000..f618003 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FFIXSEL.ASM @@ -0,0 +1,94 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Fix a selector * +;* * +;* File Name : FFIXSEL.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* FixSelector -- Fix the Priviledge level of a selector * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + +IDEAL +P386 +MODEL USE32 FLAT + + +EXTRN exit : near +GLOBAL FixSelector :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* FIXSELECTOR -- Fix the Priviledge level of a selector * +;* * +;* * +;* * +;* INPUT: sel the selector to fix-up * +;* * +;* OUTPUT: UWORD The fixed up selector * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw Created. * +;*=========================================================================* + +PROC FixSelector C near + + USES ecx,edx + + ARG sel:WORD + +; Copy the Table Bit and IOPL from the Current CS + +; Something is wrong the program should not be here unthe any circunstance +; movzx ecx,[sel] +; xor eax,eax +; mov ax,cs +; and ax,7 +; or ax,cx +push 0 +call exit + + ret +;==================== +ENDP FixSelector + + + +END + + + + + diff --git a/WIN32LIB/SRCDEBUG/FGETCS.ASM b/WIN32LIB/SRCDEBUG/FGETCS.ASM new file mode 100644 index 0000000..e2259fc --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FGETCS.ASM @@ -0,0 +1,78 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the code selector * +;* * +;* File Name : FGETCS.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetCs -- Return the current Data selector. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetCs :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GETCS -- Return the current Data selector. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: UWORD selector of the default code segment * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetCs C near + + xor eax,eax + mov ax,cs + ret +;==================== +ENDP GetCs + + +END + + + + + diff --git a/WIN32LIB/SRCDEBUG/FGETDS.ASM b/WIN32LIB/SRCDEBUG/FGETDS.ASM new file mode 100644 index 0000000..379b1ac --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FGETDS.ASM @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the data selector * +;* * +;* File Name : FGETDS.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetDs -- Return the current Data selector. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetDs :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GETDS -- Return the current Data selector. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: UWORD selector of the default data segment * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetDs C near + + xor eax,eax + mov ax,ds + ret +;==================== +ENDP GetDs + + +END + + + + diff --git a/WIN32LIB/SRCDEBUG/FGETSEL.ASM b/WIN32LIB/SRCDEBUG/FGETSEL.ASM new file mode 100644 index 0000000..5fa0519 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FGETSEL.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the Defines selectors * +;* * +;* File Name : FGETSEL.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetDefaultSelectors -- Return the current default selectors. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetDefaultSelectors :NEAR + +GLOBAL DataSelector :WORD +GLOBAL ScreenSelector :WORD +GLOBAL GraphicsSelector :WORD +GLOBAL PspSelector :WORD +GLOBAL EnvSelector :WORD +GLOBAL DosMemSelector :WORD +GLOBAL Fp1167Selector :WORD +GLOBAL FpWeitekSelector :WORD +GLOBAL FpCyrixSelector :WORD +GLOBAL CodeSelector :WORD + + +DATASEG + +; It is very important that this section remain untouch +; is not really needed by Rational System but is here to +; keep compatibility with the TNT dos extender. +DataSelector dw 0 +ScreenSelector dw 0 +GraphicsSelector dw 0 + +PspSelector dw 0 +EnvSelector dw 0 +DosMemSelector dw 0 + +Fp1167Selector dw 0 +FpWeitekSelector dw 0 +FpCyrixSelector dw 0 + +CodeSelector dw 0 + + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GetDefaultSelectors -- Setup the defaults selector values to have the * +;* Correct Descriptor table and IOPL. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetDefaultSelectors C near + USES eax,esi,ecx + + lea edi,[DataSelector] + lea ecx,[CodeSelector] + sub ecx,edi + shr ecx,1 + mov ax,ds + rep stosw + mov ax,cs + mov [word ptr CodeSelector] , ax + + ret +;==================== +ENDP GetDefaultSelectors + + +END + + + + diff --git a/WIN32LIB/SRCDEBUG/FGLOB2.CPP b/WIN32LIB/SRCDEBUG/FGLOB2.CPP new file mode 100644 index 0000000..ded3620 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FGLOB2.CPP @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library * + * * + * File Name : FILEGLOB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "_file.h" + +/* Global varaiables */ +WORD Hard_Error_Occured=0; diff --git a/WIN32LIB/SRCDEBUG/FILE.CPP b/WIN32LIB/SRCDEBUG/FILE.CPP new file mode 100644 index 0000000..10deeff --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILE.CPP @@ -0,0 +1,1070 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : FILE.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * File_Exists -- Searches for and FINDS the specified file. * + * File_Size -- Determine the size of the specified file. * + * Open_File -- Opens a file for access. * + * Close_File -- Closes a file previously opened with Open_File. * + * Seek_File -- Adjusts the position of the file pointer. * + * Read_File -- Reads a block of data from a file. * + * Write_File -- Writes a block of data to a file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include +#endif + +#ifndef FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + + +#include +#include +#include + +#define DEBUGPRINT FALSE + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_FILE -- Opens a file for access. * + * * + * This routine will open the specified file for access. It functions * + * in a similar manner to the regular DOS open command. * + * * + * INPUT: filename -- Pointer to the filename to open. * + * * + * mode -- Access mode to use. * + * READ: A file opened for READ access, MUST be available. * + * This may cause a disk swap message to appear. * + * * + * WRITE:A file opened for WRITE access (only), need not be * + * available. If it can't be found, then it will be * + * created in the current directory. * + * * + * OUTPUT: Returns with the westwood file handle. If ERROR is returned * + * it means that the programmer took over the file error handler * + * and signaled that an open access failure should not keep trying* + * for success. * + * * + * WARNINGS: If you take over the file error handling code, you must be * + * sure to anticipate a possible ERROR value being returned. * + * If you open a file for READ or READ|WRITE, then the file * + * must previously exist. To create a file for read and write * + * access, first open it for WRITE, then re-open it for * + * read and write. * + * * + * HISTORY: * + * 11/11/1991 JLB : Created. * + *=========================================================================*/ +WORD cdecl Open_File(BYTE const *file_name, WORD mode) +{ + WORD file_handle; // Westwood file handle. + WORD handle; // DOS file handle. + UWORD local_mode; // DOS access mode number. + WORD index; // FileData index (if available). + BOOL immediate; // Is the file immediately available? + FileDataType *filedata; // Pointer to the current FileData. + FileDataType *parent; // Pointer to the current FileData. + FileHandleType *filehandletable; // Pointer to the current file handle. + + CallingDOSInt++; + + + #if(DEBUGPRINT) + Mono_Printf("Open_File('%s')\r", file_name); + #endif + + /* + ** Is there a slot in the FileHandleTable? If not, then exit with + ** a file error. + */ + filehandletable = FileHandleTable; + for (file_handle = 0; file_handle < TABLE_MAX; file_handle++, filehandletable++) { + if (filehandletable->Empty) break; + } + if (file_handle == TABLE_MAX) { + Do_IO_Error(TOO_MANY_FILES, file_name); + } + + /* + ** Find the file in the FileTable (if it exists there). + */ + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + // Start out looking at the start drive. + ibm_setdisk(*StartPath - 'A'); + + /* + ** If the file exists in the current directory, then by all + ** means, use that file instead of any corresponding pack + ** file representation. This is used as a means of + ** overriding packed files (quite handy). + */ + immediate = FALSE; + + /* + ** Check the current directory by attempting to open with READ access. + ** Only check if the file is not already cached and it is a packed file. + */ + if ((index != ERROR) && (filedata->Flag & FILEF_PACKED) && (!filedata->Ptr)) + { + WORD handle2; + + handle2 = FILEOPEN(file_name, MODE_OLDFILE); + if (handle2 != FILEOPENERROR) { + FILECLOSE(handle2); + immediate = TRUE; + } + } + + + /* + ** Check to see if any WRITE permission is requested for the + ** file. If so, delete the file from RAM and mark it as non-resident. + ** + ** NOTE: If the file is packed, we CAN NOT alter it in ANY way. + ** We will return an ERROR to the user, and hope he/she is + ** smart enough to handle it. + */ + if ((mode & WRITE) && index != ERROR && filedata->Ptr) { + if(filedata->Flag & FILEF_PACKED) { // is file packed? + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } else { // file is packed + Do_IO_Error(WRITING_RESIDENT, file_name); + } + } + + // If the file is cached already, then let us use it and leave. + if (filedata->Ptr && index != ERROR) { + /* + ** File is resident. Initialize the local table. + */ + filehandletable->Index = index; + filehandletable->Empty = FALSE; + filehandletable->Pos = 0L; + filehandletable->Name = filedata->Name; + filehandletable->Handle = 0; // Resident flag. + filehandletable->Mode = mode; + + // If the cached file is packed and its parent is resident, then + // add one to the parent open count. + if (filedata->Flag & FILEF_PACKED) { + parent = &FileDataPtr[filedata->Disk]; + } + else { + parent = NULL; + } + + if (parent && (parent->Ptr == filedata->Ptr)) { + filehandletable->Start = filedata->Start; + parent->OpenCount++; + } else { + filehandletable->Start = 0; + } + + // Mark the pointer in use so that it is never returned as the oldest + // pointer location in memory. + Mem_In_Use(filedata->Ptr); + + } else { + + /* + ** At this time we determine if it is a packed file. If so, the + ** opening process takes a dramatically different turn. + */ + if (index != ERROR && (filedata->Flag & FILEF_PACKED) && !immediate && (mode & WRITE) == 0) { + + /* + ** This is a packed file. Perform recursive open process to reach + ** the parent packed file. + */ + parent = &FileDataPtr[filedata->Disk]; + file_handle = Open_File(parent->Name, mode); + + // Reread the parents information. + parent = &FileDataPtr[filedata->Disk]; + filehandletable = &FileHandleTable[file_handle]; + + /* + ** Process the packed file header. Update the start offset for every + ** file ellaborated in the packed file. + ** Exception: When a file is specified in the packed file and an + ** entry exists for it in the file table, BUT the entry + ** is not marked as packed, THEN ignore the packed version + ** of the file. + */ + + if (FileData == FileDataPtr && + !(FileDataPtr[filehandletable->Index].Flag & FILEF_PROCESSED)) { + LONG offset; // Offset of sub-file start. + WORD i; // Sub-file FileData index. + BYTE name[FILENAMESIZE]; // Name of sub-file. + FileDataType *cur=NULL; // Current entry in FileData. + FileDataType *last=NULL; // Last entry in FileData. + + + #if(DEBUGPRINT) + Mono_Printf("Processing packed file '%s'\r", FileDataPtr[filedata->Disk].Name); + #endif + + Read_File(file_handle, &offset, (ULONG)sizeof(LONG)); + while (offset) { + + /* + ** Due to the fact that the embedded file name is of arbitrary + ** length, we must read it in a byte at a time. Reading stops + ** when the terminating NULL is found. + */ + i=0; + do { + Read_File(file_handle, &name[i], (ULONG)sizeof(name[0])); + } while (name[i++]); + + i = Find_File_Index(name); + if (i != ERROR) { + cur = &FileDataPtr[i]; + if ((cur->Flag & FILEF_PACKED) && cur->Disk == filehandletable->Index) { + + cur->Start = offset + FileDataPtr[ filehandletable->Index ].Start; + if (last != NULL) { + last->Size = cur->Start - last->Start; + } + + last = cur; + } + } else { + // ERROR: File specified in packed file has no table entry. + // When this occurs, the embedded file is ignored. + } + + Read_File(file_handle, &offset, (ULONG)sizeof(LONG)); + } + + /* + ** Fixup the size record of the last embedded file. + */ + if (last != NULL) { + last->Size = File_Size(file_handle) - last->Start; + } + + FileDataPtr[filehandletable->Index].Flag |= FILEF_PROCESSED; + } + + // if the parent is resident, the the child must have the same address. + filedata->Ptr = parent->Ptr; + + /* + ** Set starting position to start of embedded file. + */ + filehandletable->Index = index; + filehandletable->Name = filedata->Name; + filehandletable->Start = filedata->Start; + + Seek_File(file_handle, NULL, SEEK_SET); + + // Attempt to cache the file if wanted, and room. + Cache_File(index, file_handle); + + } else { + + /* + ** Convert the Westwood access mode into the system specific mode. + */ + local_mode = MODE_OLDFILE; + switch (mode) { + case READ: + local_mode = MODE_OLDFILE; + break; + + case READ | WRITE: + #if(IBM) + local_mode = MODE_READWRITE; + #else + local_mode = MODE_OLDFILE; + #endif + break; + + case WRITE: + local_mode = MODE_NEWFILE; + break; + + + default: + Do_IO_Error(BAD_OPEN_MODE, file_name); + break; + } + + + /* + ** The file is disk based. Perform the DOS open processing. + */ + + /* + ** Make sure the file is available or the proper disk is inserted. + ** The file MUST exist if READ access is requested. A mild attempt + ** will be made to find the file if open for WRITE only. + */ + if (mode & READ) { + if (!File_Exists(file_name)) { + CallingDOSInt--; + return(ERROR); /* Ignore the error */ + } + } + + + /* + ** Repetitively try to open the file until the error handler + ** routine indicates otherwise. + */ + handle = FILEOPENERROR; + for (;;) { + handle = Open_File_With_Recovery( file_name, local_mode ); + if (handle != FILEOPENERROR) { + break; + } else { + CHANGEDIR( DataPath ); + CHANGEDIR( StartPath ); + if (!Do_Open_Error(COULD_NOT_OPEN, file_name)) { + CallingDOSInt--; + return(FILEOPENERROR); + } + } + } + + /* + ** A file that is read off of disk cannot be part of + ** a pack file regardless of the bit settting. + */ + if (immediate && index != ERROR) { + filedata->Flag &= ~FILEF_PACKED; + filedata->Size = 0; + } + + if (index != ERROR) { + + // The true file size is needed. + // Go back to beginning of file. + filedata->Size = FILESEEK(handle, 0L, SEEK_END); + FILESEEK(handle, 0L, SEEK_SET); + } + + /* + ** Initialize the local file handle table. + */ + filehandletable->Index = index; + filehandletable->Pos = 0L; + filehandletable->Start = 0L; + filehandletable->Empty = FALSE; + filehandletable->Handle = handle; + filehandletable->Mode = mode; + if (index != ERROR) { + filehandletable->Name = filedata->Name; + } else { + filehandletable->Name = NULL; + } + + /* + ** If the file should be made resident, then do it at this time. + ** Perform all necessary adjustments to the file tables. + */ + if ((mode & WRITE) == 0) { + Cache_File(index, file_handle); + } + } + } + + // If in the file table, increment the number of opens on this file. + if (index != ERROR) { + filedata->OpenCount++; + } + + /* + ** Return with valid file handle. + */ + CallingDOSInt--; + return(file_handle); +} + + +/*************************************************************************** + * CLOSE_FILE -- Closes a file previously opened with Open_File. * + * * + * Use this routine to close a file that was opened with Open_File. * + * This is the only way to close a file that was opened with the * + * Westwood file I/O system. * + * * + * INPUT: handle -- Westwood file handle. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1991 JLB : Created. * + *=========================================================================*/ +VOID cdecl Close_File(WORD handle) +{ + WORD index; // File data table index. + BOOL flushed = FALSE; // If file flushed from cache, do change time stamp. + BOOL stillopen; + FileDataType *parent = NULL; + FileDataType *filedata = NULL; + VOID *memptr=NULL; + + + #if(DEBUGPRINT) + Mono_Printf("Close_File(%d)\r", handle); + #endif + + + /* + ** Check for file handle validity. + */ + if (!Is_Handle_Valid(handle, CLOSING_NON_HANDLE, NULL)) { + return; + } + + CallingDOSInt++; + + index = FileHandleTable[handle].Index; + filedata = &FileDataPtr[index]; + + // Remove the OpenCount on the file. + if (index != ERROR) { + + filedata->OpenCount--; + stillopen = filedata->OpenCount; + + // If this file packed in another, then decrement the parents open count. + if (filedata->Flag & FILEF_PACKED) { + parent = &FileDataPtr[filedata->Disk]; + + // Do not dec OpenCount if the child was cached but not the parent. + if (parent->Ptr == filedata->Ptr) { + parent->OpenCount--; + stillopen = parent->OpenCount; + } + } + } + + + // If the file was resident, then handle bookeeping. + if (index != ERROR && filedata->Ptr) { + + // Get a pointer to the memory area for later use. + memptr = filedata->Ptr; + + // If file has a parent, and it is the cached file... + if (parent && (parent->Ptr == filedata->Ptr)) { + + // The PAK'd file is closed just by setting the pointer. + if (!filedata->OpenCount) { + filedata->Ptr = NULL; + } + + // Uncache parent if no other sons are open and it should be flushed. + if ((!parent->OpenCount) && (parent->Flag & FILEF_FLUSH)) { + Mem_Free(FileCacheHeap, parent->Ptr); + parent->Ptr = NULL; + flushed = TRUE; + } + } + // Else should the file be flushed? Only flush it if the flag says + // so AND there are no other open counts on it. + else if ((filedata->Flag & FILEF_FLUSH) && !filedata->OpenCount) { + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + flushed = TRUE; + } + + // + // Set the time stamp on the close IF + // the file was not flush AND + // is not open AND + // its time stamp should be changed by being a FILEF_KEEP|PROIORTY file. + // + if (!flushed && !stillopen && !(filedata->Flag & (FILEF_KEEP|FILEF_PRIORITY))) { + Mem_Reference(memptr); + } + } + // Just a simple close will do. + else { + FILECLOSE(FileHandleTable[handle].Handle); + } + + // The WWS handle is no longer being used. + FileHandleTable[handle].Empty = TRUE; + + CallingDOSInt--; +} + + + +/*************************************************************************** + * READ_FILE -- Reads a block of data from a file. * + * * + * This routine is used to read a block of data from a previously * + * opened file. * + * * + * INPUT: handle -- Westwood file handle returned from Open_File. * + * * + * buf -- Pointer to buffer to load the data into. * + * * + * bytes -- Number of bytes to load. * + * * + * OUTPUT: Returns with the number of bytes actually read from the file. * + * If this number is less than the number requested, this * + * indicates the end of the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +LONG cdecl Read_File(WORD handle, VOID *buf, ULONG bytes) +{ + WORD doshandle; // DOS file handle. + WORD fileindex; // File table index. + LONG bytesread; // Accumulation of number of bytes read. + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Read_File(%d, %08lx, %ld)\r", handle, buf, bytes); + #endif + + /* + ** The handle must be valid or else it is an error. + */ + if (!buf || !bytes || !Is_Handle_Valid(handle, READING_NON_HANDLE, NULL)) { + return(NULL); + } + + CallingDOSInt++; + + /* + ** Prepare some working variables. + */ + doshandle = FileHandleTable[handle].Handle; + fileindex = FileHandleTable[handle].Index; + + + /* + ** Limit the number of bytes to read according to the size of the + ** file. If no file size is specified, then don't perform the check. + */ + if (fileindex != ERROR) { + filedata = &FileDataPtr[fileindex]; + + if (filedata->Size) { + bytes = MIN((ULONG)filedata->Size - FileHandleTable[handle].Pos, bytes); + } + } + + /* + ** Starts with no bytes being read in. + */ + bytesread = 0; + + if (bytes) { + + if (doshandle) { + /* + ** Perform a DOS read of the file. Read the file in chunks. + */ + while (bytes) { + LONG number; // Number of bytes read. + + /* + ** Read in a chunk of file data. + */ + Hard_Error_Occured = 0; + number = (LONG)FILEREAD(doshandle, buf, MIN(bytes, IO_CHUNK_SIZE)); + + /* + ** if a hard error occurs, read its copy on a different directory + */ + if ( Hard_Error_Occured ) { + number = Read_File_With_Recovery( handle, buf, (UWORD)MIN(bytes, IO_CHUNK_SIZE)); + doshandle = FileHandleTable[handle].Handle; + } + + /* + ** Adjust the remaining bytes to read counter and adjust the count + ** of the number of bytes actually read from the file. + */ + bytes -= number; + bytesread += number; + + /* + ** Adjust the file position (manually). + */ + FileHandleTable[handle].Pos += number; + + /* + ** If less than requested bytes were read from the file, then + ** we KNOW that the reading loop must terminate. + ** Was there an error? + */ + if (number < IO_CHUNK_SIZE) { + break; + } + + /* + ** Adjust the destination pointer in anticipation of the next + ** file read operation. + */ + // this is a BCC bug + // (BYTE *)buf += number; + buf = (BYTE* ) buf + number; + + } + + } else { + + #if (DEBUGPRINT) + Mono_Printf("Resident read '%s' %08lx[%ld].%ld\r", filedata->Name, (LONG)filedata->Start, FileHandleTable[handle].Pos, bytes); + #endif + + /* + ** Copy the block of the file into the specified buffer. + */ + Mem_Copy((VOID*)((LONG)filedata->Ptr + FileHandleTable[handle].Start + FileHandleTable[handle].Pos), buf, bytes); + bytesread = bytes; + + /* + ** Adjust the file position (manually). + */ + FileHandleTable[handle].Pos += bytes; + } + } + + /* + ** Return with the number of bytes read in from the file. + */ + CallingDOSInt--; + return(bytesread); +} + + +/*************************************************************************** + * WRITE_FILE -- Writes a block of data to a file. * + * * + * This routine will write a block of data to a file. The file must * + * have been previously opened with WRITE or READ|WRITE access. * + * Writing cannot occur to a resident or packed file. * + * * + * INPUT: handle -- File handle as returned by Open_File. * + * * + * buf -- Pointer to the buffer that holds the data to be * + * written out. * + * * + * bytes -- The number of bytes to write out. * + * * + * OUTPUT: Returns with the actual number of bytes written to the file. * + * * + * WARNINGS: If the value returned from this function is less than the * + * number of bytes requested to be written, then this * + * indicates some kind of error NOT caught by the file I/O * + * system. * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +LONG cdecl Write_File(WORD handle, VOID const *buf, ULONG bytes) +{ + WORD doshandle; // DOS specific file handle. + WORD fileindex; // FileData table index (if any). + LONG byteswritten; // Accumulated number of bytes written. + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Write_File(%d, %08lx, %ld)\r", handle, buf, bytes); + #endif + + /* + ** Check to make sure the file handle is valid. + */ + if (!Is_Handle_Valid(handle, WRITING_NON_HANDLE, NULL)) { + return(NULL); + } + doshandle = FileHandleTable[handle].Handle; + fileindex = FileHandleTable[handle].Index; + filedata = &FileDataPtr[fileindex]; + + /* + ** Writing to a resident file is an error condition. + */ + if (!doshandle) { + Do_IO_Error(WRITING_RESIDENT, filedata->Name); + return(NULL); + } + + CallingDOSInt++; + + /* + ** Perform a DOS write of the data. This write is performed in blocks. + */ + byteswritten = NULL; + + while (bytes) { + LONG outbytes; // Number bytes written in one block. + + /* + ** Write out one block of data. + */ + outbytes = FILEWRITE(doshandle, (void*)buf, MIN((LONG)bytes, IO_CHUNK_SIZE)); + + /* + ** Reduce the bytes remaining to output counter and adjust the + ** file position. + */ + bytes -= outbytes; + byteswritten += outbytes; + FileHandleTable[handle].Pos += outbytes; + + /* + ** If NO bytes were written out, then this is some kind of unknown + ** error (possibly disk full?). + */ + if (!outbytes) { + break; + } + + /* + ** Possible adjustment to the file's size. + */ + if (fileindex != ERROR) { + filedata->Size = MAX(filedata->Size, FileHandleTable[handle].Pos); + } + + /* + ** Adjust the source pointer in anticipation of the next block write. + */ + buf = (BYTE*)(((LONG)buf) + outbytes); + } + + /* + ** Return with the actual number of bytes written. + */ + CallingDOSInt--; + return (byteswritten); +} + + +/*************************************************************************** + * SEEK_FILE -- Adjusts the position of the file pointer. * + * * + * This routine adjusts the position of the file pointer. Use this * + * to control where the next Read_File or Write_File will occur. * + * * + * INPUT: handle -- File handle as returned by Open_File. * + * * + * offset -- Signed offset from indicated starting position. * + * * + * starting -- SEEK_CUR: Seeks from the current position. * + * SEEK_SET: Seeks from the start of the file. * + * SEEK_END: Seeks backward from the end. * + * * + * OUTPUT: Returns with the new file position. * + * * + * WARNINGS: Seeking past the end or before the beginning of the file * + * is not allowed. * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +ULONG cdecl Seek_File(WORD handle, LONG offset, WORD starting) +{ + WORD doshandle; // DOS specific file handle. + WORD fileindex; // FileData index (if any). + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Seek_File(%d, %ld, %d)\r", handle, offset, starting); + #endif + + /* + ** Check to make sure that the file handle is valid. + */ + if (!Is_Handle_Valid(handle, SEEKING_NON_HANDLE, NULL)) { + return(NULL); + } + + CallingDOSInt++; + + fileindex = FileHandleTable[handle].Index; + doshandle = FileHandleTable[handle].Handle; + filedata = &FileDataPtr[fileindex]; + + if (!doshandle) { + + /* + ** Resident file seek is merely an adjustment to the position value. + */ + switch (starting) { + + /* + ** Manually position from start of file. + */ + case SEEK_SET: + offset = MAX((long)0, offset); // Only positive offsets allowed. + FileHandleTable[handle].Pos = NULL; + break; + + /* + ** Position is relative to end of file. + */ + case SEEK_END: + offset = MIN((long)0, offset); // Only negative offsets allowed. + FileHandleTable[handle].Pos = filedata->Size; + break; + + case SEEK_CUR: + break; + + default: + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + break; + } + + /* + ** Perform the resident file position adjustment. + */ + FileHandleTable[handle].Pos += offset; + + FileHandleTable[handle].Pos = MIN(FileHandleTable[handle].Pos, filedata->Size-1); + FileHandleTable[handle].Pos = MAX(FileHandleTable[handle].Pos, (long)0); + + } else { + + /* + ** Special handling for packed file seeks. + */ + if (fileindex != ERROR && (filedata->Flag & FILEF_PACKED)) { + switch (starting) { + case SEEK_SET: + FileHandleTable[handle].Pos = offset; + break; + + case SEEK_END: + // Only negative offsets allowed. + // Do not allow seeking past the beginning. + offset = MIN(0L, offset); + if (-offset > filedata->Size) { + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + } + FileHandleTable[handle].Pos = filedata->Size + offset; + break; + + case SEEK_CUR: + FileHandleTable[handle].Pos += offset; + if (FileHandleTable[handle].Pos < 0) { + FileHandleTable[handle].Pos = 0; + } + if (FileHandleTable[handle].Pos >= filedata->Size) { + FileHandleTable[handle].Pos = filedata->Size; + } + break; + + default: + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + break; + } + FILESEEK(doshandle, FileHandleTable[handle].Pos+FileHandleTable[handle].Start, SEEK_SET); + CallingDOSInt--; + return (FileHandleTable[handle].Pos); + } + /* + ** Perform the straight DOS seek. + */ + FileHandleTable[handle].Pos = FILESEEK(doshandle, offset, starting); + + /* + ** File position does not recognize packed offset. + */ + if (fileindex != ERROR) { + FileHandleTable[handle].Pos -= FileHandleTable[handle].Start; + //SKB FileHandleTable[handle].Pos -= filedata->Start; + } + } + + /* + ** Return with the current file position. + */ + CallingDOSInt--; + return (FileHandleTable[handle].Pos); +} + + +/*************************************************************************** + * FILE_EXISTS -- Searches for and FINDS the specified file. * + * * + * This routine will scan the available drives and return when the file * + * is accessable. This routine is used when the programmer MUST be * + * sure of a file's existance before proceeding. This process is * + * automatically performed on a Open_File with READ access. * + * * + * INPUT: file_name -- Name of the file to check for. * + * * + * OUTPUT: Returns the disk number that the file exits on (A=1, B=2, etc) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1991 JLB : Created. * + *=========================================================================*/ +WORD cdecl File_Exists(BYTE const *file_name) +{ + /* + ** If the filename is invalid then it errors out. + */ + if (!file_name) return(FALSE); + /* + ** Continually search for the file (prompting as needed). Only return + ** upon success or error function control reasons. + */ + while (!Find_File(file_name)) { + + /* + ** If the file could not be found then request for proper disk. + */ + do { + //struct find_t ffblk; + //BYTE path[MAXPATH]; + + if (!Do_Open_Error(COULD_NOT_OPEN, file_name)) { + return(FALSE); + } + ibm_setdisk(*DataPath - 'A'); + + // Search for the volume ID so that the disk information get read in again. + //_dos_findfirst("*.*", _A_VOLID, &ffblk); + } while (CHANGEDIR( DataPath )); + } + return (TRUE); +} + + +/*************************************************************************** + * FILE_SIZE -- Determine the size of the specified file. * + * * + * This routine will return with the size of the file specified by the * + * file handle passed in. * + * * + * INPUT: handle -- Westwood file handle. * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/22/1991 JLB : Created. * + *=========================================================================*/ +ULONG cdecl File_Size(WORD handle) +{ + FileDataType *filedata; // Pointer to the current FileData. + + + if (FileHandleTable[handle].Index != ERROR) { + filedata = &FileDataPtr[FileHandleTable[handle].Index]; + + if (filedata->Size) { + return(filedata->Size); + } + } + return (filelength(FileHandleTable[handle].Handle)); +} + + +/*************************************************************************** + * IS_HANDLE_VALID -- Determines validity of the specified file handle. * + * * + * Use this routine to determine if a file handle is valid or not. It * + * checks to see if it falls within legal limits and does in fact * + * reference an open file. This routine will call the error handler * + * with the specified error number if the handle is not valid. * + * * + * INPUT: handle -- Handle to check for validity. * + * * + * error -- Error number to use when calling the IO_Error * + * handler. * + * * + * name -- The file name (if known). * + * * + * OUTPUT: Returns with the file table index, if one exists for this * + * file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +BOOL cdecl Is_Handle_Valid(WORD handle, FileErrorType error, BYTE const *name) +{ + /* + ** The handle must be valid or else it is an error. + */ + if (handle < 0 || handle >= TABLE_MAX) { + /* + ** ERROR: Invalid file handle. + */ + Do_IO_Error(error, name); + } + + /* + ** An unused file handle causes an error. + */ + if (FileHandleTable[handle].Empty) { + Do_IO_Error(error, name); + } + + return (TRUE); +} + + + diff --git a/WIN32LIB/SRCDEBUG/FILECACH.CPP b/WIN32LIB/SRCDEBUG/FILECACH.CPP new file mode 100644 index 0000000..b584c85 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILECACH.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - File Caching routines * + * * + * File Name : FILECACH.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 18, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Make_File_Resident -- Makes a file resident even if not flaged so. * + * Flush_Unused_File_Cache -- Flushes the file cache of any non opened fi* + * Free_Resident_File -- Free the given file if it is resident. * + * Unfragment_File_Cache -- Does a garbage collection on the file heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * UNFRAGMENT_FILE_CACHE -- Does a garbage collection on the file heap. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: Can be a lengthy process. * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +VOID Unfragment_File_Cache(VOID) +{ + FileDataType *filedata; + FileDataType *parent; + UWORD idx; + + + // Let the memory system clean up the file heap. + Mem_Cleanup(FileCacheHeap); + + // Now get our pointers back. + // Start after the parent PAK files since we will need to check our pointers + // with them. + filedata = &FileDataPtr[NumPAKFiles]; + for (idx = NumPAKFiles; idx < NumPAKFiles; idx++, filedata++) { + while (filedata->Name) { + + // Only process files that are in the file cache. + if (filedata->Ptr) { + + // Is a inner PAK file? + if (filedata->Flag & FILEF_PACKED) { + + parent = &FileDataPtr[filedata->Disk]; + + // Is it just a copied pointer of the parent? + if (parent->Ptr == filedata->Ptr) { + filedata->Ptr = Mem_Find(FileCacheHeap, filedata->Disk); + } + else + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } + else { + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } + } + + // Now that the children have been taken care of, let us do the parents. + for (filedata = FileDataPtr, idx = 0; idx < NumPAKFiles; idx++, filedata++) { + + // Only process files that are in the file cache. + if (filedata->Ptr) { + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } +} + +/*************************************************************************** + * MAKE_FILE_RESIDENT -- Makes a file resident even if not flaged so. * + * * + * INPUT: BYTE *filename - name of file to be made resident. * + * * + * OUTPUT: BOOL if successful. could fail in not enouph RAM or not found. * + * * + * WARNINGS: File must be in FileData table. * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL Make_File_Resident(BYTE const *filename) +{ + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + WORD fileindex; + WORD oldflag; + WORD handle; + + fileindex = Find_File_Index(filename); + + // if the file is not in the table, we can't make it resident + if (fileindex == ERROR) return(FALSE); + + // Get a pointer for quicker pointer action. + filedata = &FileDataPtr[fileindex]; + + // Change the flags for a moment. + oldflag = filedata->Flag; + filedata->Flag |= FILEF_RESIDENT; + filedata->Flag &= ~FILEF_FLUSH; + + // Make the file resident. + handle = Open_File(filename, READ); + Close_File(handle); + + // Set flags back to normal. + filedata->Flag = oldflag; + + return(TRUE); +} + +/*************************************************************************** + * Flush_Unused_File_Cache -- Flushes the file cache of any non opened files. * + * * + * INPUT: WORD flush_keep - TRUE to flush even files marked FILEF_KEEP.* + * * + * OUTPUT: WORD Number of file flushed. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1993 SB : Created. * + *=========================================================================*/ +WORD Flush_Unused_File_Cache(WORD flush_keeps) +{ + WORD index; + WORD freed = 0; + FileDataType *filedata = NULL; + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + // Loop throuph the file table looking for files that could be freed. + index = 0; + filedata = &FileDataPtr[index];; + while (filedata->Name && strlen(filedata->Name)) { + + if (filedata->Ptr && !filedata->OpenCount && + (flush_keeps || !(filedata->Flag & FILEF_KEEP)) ) { + + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + freed++; + } + index++; + filedata = &FileDataPtr[index];; + } + return (freed); +} + +/*************************************************************************** + * FREE_RESIDENT_FILE -- Free the given file if it is resident. * + * * + * INPUT: BYTE *file to free * + * * + * OUTPUT: TRUE if file was free'd, FALSE otherwise * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1992 CY : Created. * + *=========================================================================*/ +BOOL cdecl Free_Resident_File(BYTE const *file) +{ + WORD fileindex; + BOOL oldflag; // Previous file flag. + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + + // if the file is not in the table, we can't free it + if ((fileindex = Find_File_Index(file)) == ERROR) { + return(FALSE); + } + + // get a pointer for quicker calculations. + filedata = &FileDataPtr[fileindex]; + + // If it isn't resident, don't try to Free it + if (filedata->Ptr == NULL) { + return(TRUE); + } + + // Change the flags for a moment. + oldflag = filedata->Flag; + filedata->Flag &= ~(FILEF_RESIDENT|FILEF_KEEP); + filedata->Flag |= FILEF_FLUSH; + + // Get the file out of Memory if it was there. + Close_File(Open_File(file, READ)); + + // Set flags back to original. + filedata->Flag = oldflag; + + return(TRUE); +} + diff --git a/WIN32LIB/SRCDEBUG/FILECHNG.CPP b/WIN32LIB/SRCDEBUG/FILECHNG.CPP new file mode 100644 index 0000000..e36a0f1 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILECHNG.CPP @@ -0,0 +1,154 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - File functions. * + * * + * File Name : FILECHNG.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Delete_File -- Deletes the file from the disk. * + * Create_File -- Creates an empty file on disk. * + * Change_File_Size -- Change the size of a writting file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * CREATE_FILE -- Creates an empty file on disk. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1992 JLB : Created. * + *=========================================================================*/ +WORD cdecl Create_File(BYTE const *file_name) +{ + WORD fd; + + if (!file_name) return(FALSE); + + fd = Open_File(file_name, WRITE); + if (fd != ERROR) { + Close_File(fd); + return(TRUE); + } + return(FALSE); +} + + +/*************************************************************************** + * DELETE_FILE -- Deletes the file from the disk. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1992 JLB : Created. * + *=========================================================================*/ +WORD cdecl Delete_File(BYTE const *file_name) +{ + WORD index; + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + if (!file_name) return(FALSE); + + CallingDOSInt++; + + ibm_setdisk(*StartPath - 'A'); + + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + if (index != ERROR && filedata->Ptr) { + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } + + index = !FILEDELETE(file_name); + CallingDOSInt--; + return(index); +} + + + +/*************************************************************************** + * CHANGE_FILE_SIZE -- Change the size of a writting file. * + * * + * INPUT: WORD handle - handle of file. * + * ULONG new_size - size of new handle. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Change_File_Size(WORD handle, ULONG new_size) +{ + WORD entry; + + if (Is_Handle_Valid(handle, WRITING_NON_HANDLE, NULL)) { + entry = Get_DOS_Handle(handle); + if (entry != ERROR) { + return(chsize(entry, new_size) != ERROR); + } + } + return(FALSE); +} diff --git a/WIN32LIB/SRCDEBUG/FILEDATA.CPP b/WIN32LIB/SRCDEBUG/FILEDATA.CPP new file mode 100644 index 0000000..789a923 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILEDATA.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : File IO System LIbrary * + * * + * File Name : FILEDATA.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/* + structure for FileDataType is: + + BYTE *filename: initialize to actual file name on disk. + LONG size: initialize to actual file size on disk. + BYTE *ptr: initialize to a 0L below. + WORD disk: which floppy disk number (1+) file resides on. + LONG pos: initialize to a 0L below. + UBYTE priority: file priorities can be from 0 to 127. (127 = highest) + if you want the file to be attempted to be made + resident at runtime, add 128 to the file priority + to set the high bit. even though the files + priority will appear to be 128 to 255, it will + still remain 0 to 127. +*/ + +FileDataType FileData[] = { + { "", 0L, 0L, 0, 0L, 0 } + /* Must have an empty entry!!! */ +}; diff --git a/WIN32LIB/SRCDEBUG/FILEGLOB.CPP b/WIN32LIB/SRCDEBUG/FILEGLOB.CPP new file mode 100644 index 0000000..49ed933 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILEGLOB.CPP @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library * + * * + * File Name : FILEGLOB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include +#endif + +#ifndef FILE_H +#include "_file.h" +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/* Global varaiables */ +BYTE ExecPath[XMAXPATH + 1]; +BYTE DataPath[XMAXPATH + 1]; +BYTE StartPath[XMAXPATH + 1]; +BOOL UseCD; + +/* File System only Global varaiables */ +BYTE CallingDOSInt; // Indicate we are performing a DOS function +BYTE MaxDevice,DefaultDrive; +BYTE MultiDriveSearch = TRUE; // Multiple drive search flag +FileDataType *FileDataPtr = NULL; +FileHandleType FileHandleTable[TABLE_MAX]; +UWORD NumFiles; // Number of files, except PAK, in file table. +UWORD NumPAKFiles; // Number of PAK files in filetable. +VOID *FileCacheHeap = NULL; // Pointer to the cache in memory. +WORD DiskNumber; // Where file was found (-1 == current directory). +WORD MaxDirNum = 0; + + +WORD (*Open_Error)(FileErrorType, BYTE const *) = NULL; diff --git a/WIN32LIB/SRCDEBUG/FILEINFO.CPP b/WIN32LIB/SRCDEBUG/FILEINFO.CPP new file mode 100644 index 0000000..fc39d04 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILEINFO.CPP @@ -0,0 +1,276 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Fileio information functions. * + * * + * File Name : FILE.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_DOS_Handle -- Fetches system specific DOS file handle. * + * Find_Disk_Number -- Determine disk a file resides upon. * + * Set_File_Flags -- Sets flags for file if FileData table. * + * Get_File_Flags -- Gets the flags on a file in the FileData table. * + * Free_Handles -- Returns number of free file handles in WW system. * + * Multi_Drive_Search -- Turns Multi search drive on and off. * + * Clear_File_Flags -- Clears flags specified for file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * GET_DOS_HANDLE -- Fetches system specific DOS file handle. * + * * + * This routine will return with the system specific DOS file handle. * + * On the IBM, this is a WORD, on the Amiga, it is a LONG (BPTR). Use * + * this routine with caution, because the value returned is NOT * + * portable. * + * * + * INPUT: fh -- Westwood file system handle. * + * * + * OUTPUT: Returns with the system DOS file handle (WORD or LONG). * + * * + * WARNINGS: If you pass in an invalid file handle, or a file handle * + * that references a resident file, then the ERROR code is * + * returned. Be SURE to check for this. * + * * + * HISTORY: * + * 08/21/1991 JLB : Created. * + * 11/09/1991 JLB : Checks for illegal file handle passed in. * + *=========================================================================*/ +WORD cdecl Get_DOS_Handle(WORD fh) +{ + /* + ** If an illegal file handle is passed in then always abort. + */ + if (fh >= 0 && fh < TABLE_MAX) { + if (!FileHandleTable[fh].Empty || FileHandleTable[fh].Handle) { + return(FileHandleTable[fh].Handle); + } + + /* + ** If it falls through here, then the file must be resident. It is + ** illegal to get a DOS handle to a resident file. + */ + } + return(FILEOPENERROR); +} + +/*************************************************************************** + * FREE_HANDLES -- Returns number of free file handles in WW system. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Free_Handles(VOID) +{ + WORD count; // Count of the number of free file handles. + WORD index; // Working file handle index var. + + count = 0; + for (index = 0; index < TABLE_MAX; index++) { + if (FileHandleTable[index].Empty) count++; + } + return(count); +} + + + +/*************************************************************************** + * FIND_DISK_NUMBER -- Determine disk a file resides upon. * + * * + * This routine will determine the disk number that the specified * + * file resides upon. It determines this by scanning through the * + * FileData table. If the specified file is a packed file, then it * + * will reference the parent packed file to determine the disk number. * + * * + * INPUT: file_name -- Pointer to the file name to check. * + * * + * OUTPUT: Returns with the disk number that the file resides upon. If * + * ERROR is returned, then the file does not exist in the * + * FileTable. The number returned is 0=A, 1=B, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/22/1991 JLB : Created. * + *=========================================================================*/ +WORD cdecl Find_Disk_Number(BYTE const *file_name) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(file_name); + + if (index != ERROR) { + + filedata = &FileDataPtr[index]; + + if (filedata->Flag & FILEF_PACKED) { + return (Find_Disk_Number(FileDataPtr[filedata->Disk].Name)); + } + return(filedata->Disk); + } + return (index); +} + + + + + +/*************************************************************************** + * SET_FILE_FLAGS -- Sets flags for file if FileData table. * + * * + * INPUT: BYTE *filename - file to modify. * + * WORD flags - flags to set in file. * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/04/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Set_File_Flags(BYTE const *filename, WORD flags) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + filedata->Flag |= flags; + return (TRUE); + } + + return (FALSE); +} + + +/*************************************************************************** + * CLEAR_FILE_FLAGS -- Clears flags specified for file. * + * * + * INPUT: BYTE *filename - file to modify. * + * WORD flags - flags to set in file. * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +WORD cdecl Clear_File_Flags(BYTE const *filename, WORD flags) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + filedata->Flag &= ~flags; + return (TRUE); + } + + return (FALSE); +} + + + +/*************************************************************************** + * GET_FILE_FLAGS -- Gets the flags on a file in the FileData table. * + * * + * * + * INPUT: BYTE *filename - file to modify. * + * * + * OUTPUT: * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * HISTORY: * + * 10/04/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Get_File_Flags(BYTE const *filename) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + return (filedata->Flag); + } + return (FALSE); +} + + +/*************************************************************************** + * MULTI_DRIVE_SEARCH -- Turns Multi search drive on and off. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Multi_Drive_Search(BOOL on) +{ + BOOL old; + + Hard_Error_Occured = 0; + old = MultiDriveSearch; + MultiDriveSearch = on; + return(old); +} diff --git a/WIN32LIB/SRCDEBUG/FILEINIT.CPP b/WIN32LIB/SRCDEBUG/FILEINIT.CPP new file mode 100644 index 0000000..3eee800 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILEINIT.CPP @@ -0,0 +1,511 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Fileio init routines. * + * * + * File Name : FILEINIT.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * WWDOS_Init -- Initialize the fileio WWS fileio system. * + * WWDOS_Shutdown -- Clean up any things that needs to be to exit game. * + * Init_FileData_Table -- Initializes or reads in FileData Table. * + * Sort_FileData_Table -- Sorts the FileData table that is in memory. * + * Preload_Files -- Loads files marked with FILEF_PRELOAD into cache. * + * Init_File_Cache -- Initializes and allocs the file cache heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#ifndef MISC_H +#include +#endif + +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize); +PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename); +PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ); +PRIVATE FileInitErrorType cdecl Preload_Files(VOID); +PRIVATE int QSort_Comp_Func(const void *p1, const void *p2); +PRIVATE VOID Sort_FileData_Table(VOID); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * WWDOS_INIT -- Initialize the fileio WWS fileio system. * + * * + * * + * INPUT: ULONG cachesize - size wanted for the cache. * + * BYTE *filedat - NULL or name of filedata table file. * + * BYTE *cdpath - NULL or secondary search path on a CD. * + * * + * OUTPUT: Returns all errors encountered or'd together. * + * * + * WARNINGS: User should call the WWDOS_Init function for all file * + * initialization. * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +FileInitErrorType cdecl WWDOS_Init(ULONG cachesize, BYTE *filedata, BYTE *cdpath) +{ +// FileInitErrorType errors; + unsigned errors ; + + // This has not been completed yet, when it is, uncomment it and add errors. + Install_Hard_Error_Handler () ; + Get_Devices(); + + if (cachesize) { + errors = Init_File_Cache(cachesize); + } else { + errors = FI_SUCCESS; + } + + + errors = errors | Init_FileData_Table(filedata); + + errors = errors | Set_Search_Drives(cdpath); + + errors = errors | Preload_Files(); + + + return ( FileInitErrorType ) errors ; +} + +/*************************************************************************** + * WWDOS_SHUTDOWN -- Clean up any things that needs to be in file syste to * + * exit game. * + * One could shut down the file system and open it back * + * up with a different size cache or filetable. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl WWDOS_Shutdown(VOID) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD file_handle; + FileHandleType *filehandletable; // Pointer to the current file handle. + + // Close all open files. + filehandletable = FileHandleTable; + for (file_handle = 0; file_handle < TABLE_MAX; file_handle++, filehandletable++) { + if (!filehandletable->Empty) { + Close_File(file_handle); + } + } + + // Free the file cache heap. + if (FileCacheHeap) { + + // Get a pointer to the current filedata. + if (FileDataPtr) { + filedata = FileDataPtr; + } else { + filedata = FileData; + } + + while(filedata->Name && filedata->Name[0]) { + filedata->Ptr = NULL; + filedata++; + } + + Free(FileCacheHeap); + FileCacheHeap = NULL; + } + + // Free up the file data. + if (FileDataPtr != FileData) { + Free(FileDataPtr); + } + FileDataPtr = NULL; + + chdir(StartPath); + ibm_setdisk(*StartPath - 'A'); + + // This has not been completed yet, when it is, uncomment it and add errors. + Remove_Hard_Error_Handler(); +} + + +/*************************************************************************** + * INIT_FILE_CACHE -- Initializes and allocs the file cache heap. * + * * + * INPUT: ULONG cachesize - size of heap cache.. * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize) +{ + // Allocate and initialize the file cache heap. + if (FileCacheHeap) { + return (FI_CACHE_ALREADY_INIT); + } + + if ((Ram_Free(MEM_NORMAL) >= cachesize)) { + FileCacheHeap = Alloc(cachesize, MEM_NORMAL); + Mem_Init(FileCacheHeap, cachesize); + } + + if (!FileCacheHeap) { + return (FI_CACHE_TOO_BIG); + } + + return (FI_SUCCESS); +} + + + + +/*************************************************************************** + * INIT_FILEDATA_TABLE -- Initializes or reads in FileData Table. * + * * + * INPUT: * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename) +{ + WORD fd; + ULONG fsize; + FileDataType *ptr; + WORD index; + BYTE fname[13]; + + /* + ** Inialize the file handle table to reflect no files open. + */ + for (index = 0; index < TABLE_MAX; index++) { + FileHandleTable[index].Empty = TRUE; + } + + // Set up our FileData ptr to be the initial FileData table. + FileDataPtr = FileData; + + // Sort the filedata table. + // This needs to be done even if we load it off disk since the initial file data + // table might contain a filename. + Sort_FileData_Table(); + + // If there is a file name, then the filedata table will be loaded from disk. + if (filename) { + if (!Find_File(filename)) { + return (FI_FILEDATA_FILE_NOT_FOUND); + } + fd = Open_File(filename, READ); + + fsize = File_Size(fd); + + if ((Ram_Free(MEM_NORMAL) < fsize)) { + Close_File(fd); + return (FI_FILEDATA_TOO_BIG); + } + + // Allocate some system memory. + // Setup the new FileDataPtr and this time. + FileDataPtr = ptr = (FileDataType *) Alloc(fsize, MEM_NORMAL); + + // Load the file up into memory. + Read_File(fd, FileDataPtr, fsize); + Close_File(fd); + + // Process the filetable. The filenames need their pointers adjusted. + // At this time we will also count the number of files and number of PAK files. + NumPAKFiles = NumFiles = 0; + + // Make sure that the file name will have a NUL at the end. + fname[12] = 0; + while(TRUE) { + // Have we reached the end of the list? + if (!ptr->Name) break; + + // Adjust the name pointer to point the the correct area. + ptr->Name = (BYTE *)FileDataPtr + (LONG) ptr->Name; + + // Count up weather it is a PAK file or a normal file. + if (!NumFiles && strstr((char *) ptr->Name, (char *) ".PAK")) { + NumPAKFiles++; + + // Mark that it has been processed so that Open_File() will not do it. + ptr->Flag |= FILEF_PROCESSED; + + } else { + NumFiles++; + } + + // Next record. + ptr++; + } + } + + return (FI_SUCCESS); +} + + + + +/*************************************************************************** + * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * + * * + * INPUT: BYTE *cdpath - path of data files on a CD. * + * Should pass in NULL for non CD products. * + * * + * OUTPUT: FileInitErrorType error code. * + * Varibable defined: * + * ExecPath = Full path of EXE file. * + * StartPath = Directory user started in. * + * DataPath = secondary search path (typically CD-ROM). * + * Note: format of paths is "C:\PATH" * + * * + * WARNINGS: The cdpath may be overiden by a "-CD" command line * + * arguement that specifies another drive (HARD or CD) and path * + * where the data resides. Whenever a file is opened, it checks * + * the startup drive first, then the CD search path if the first * + * search was unsuccessful. * + * * + * HISTORY: * + * 01/14/1993 SB : Created. * + * 04/19/1994 SKB : Mods for 32 bit library. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ) +{ + BYTE *ptr; + + +#if LIB_EXTERNS_RESOLVED + // NOTE: THIS IS WRONG, THIS IS NOT THE WAY TO GET THE EXE's PATH. + // Locate the executable. + strcpy(ExecPath, _argv[0]); + + // Find the very last '\' on the path. + ptr = strrchr((char *) ExecPath, (int) '\\'); +#else + ptr = NULL; +#endif + + // Remove the exe name to just have the path. + if (ptr == NULL) { + *ExecPath = 0; + } + else { + *ptr = 0; + } + + // Did the user specify a second path? + ptr = Find_Argv("-CD"); + + // If so, set the data path to that. + if (ptr) { + strcpy(DataPath, ptr + 3); + } + // Otherwise check to see if there is a CD-Rom drive. + else { + if (cdpath && *cdpath) { + +#if LIB_EXTERNS_RESOLVED + UseCD = GetCDDrive(); +#else + UseCD = FALSE; +#endif + } + else { + UseCD = FALSE; + } + + // If so, set the Drive to it and find out if any directories. + if ( UseCD ) { + strcpy( DataPath, "A:" ); + strcat( DataPath, cdpath); + *DataPath = 'A'+UseCD; + } + // If not, set the Data path to the execacutable path. + else { + strcpy(DataPath, ExecPath); + } + } + + // Finnally, set the starting path. + getcwd(StartPath, XMAXPATH); + + // Make sure they are all uppercase. + strupr(StartPath); + strupr(DataPath); + strupr(ExecPath); + + // Change directories to the secondary search path (DataPath). + if (*DataPath && chdir(DataPath)) { + return (FI_SEARCH_PATH_NOT_FOUND); + } + + // Lastley, Make sure we are in the startup directory. This will overide + // the secondary data path if they are on the same drive. + if (chdir(StartPath)) { + return (FI_STARTUP_PATH_NOT_FOUND); + } + + return (FI_SUCCESS); +} + + +/*************************************************************************** + * PRELOAD_FILES -- Loads files marked with FILEF_PRELOAD into cache. * + * * + * * + * INPUT: none. * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: The FileData must be initialized and the file heap initialized* + * in order for this to work. * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Preload_Files(VOID) +{ + FileDataType *filedata; // Working file data table pointer. + BOOL oldflag; // Previous file flag. + + if (!FileDataPtr) { + return (FI_FILETABLE_NOT_INIT); + } + + if (!FileCacheHeap) { + return (FI_NO_CACHE_FOR_PRELOAD); + } + + /* + ** Make all files flagged to be made resident at startup, resident. + */ + filedata = FileDataPtr; + + while (filedata->Name && strlen(filedata->Name)) { + if (filedata->Flag & FILEF_PRELOAD) { + + oldflag = filedata->Flag; + filedata->Flag |= FILEF_RESIDENT; // Make it resident. + filedata->Flag &= ~FILEF_FLUSH; // Don't purge on Close_File. + + Close_File(Open_File(filedata->Name, READ)); + + filedata->Flag &= ~(FILEF_RESIDENT|FILEF_FLUSH); // Clear bits. + filedata->Flag |= oldflag & (FILEF_RESIDENT|FILEF_FLUSH); // Restore bits. + + } + filedata++; + } + return (FI_SUCCESS); +} + + + +/*************************************************************************** + * SORT_FILEDATA_TABLE -- Sorts the FileData table that is in memory. * + * * + * INPUT: NONE * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ + +PRIVATE int QSort_Comp_Func(const void *p1, const void *p2) +{ + return(strcmp(((FileDataType*)p1)->Name, ((FileDataType*)p2)->Name)); +} +PRIVATE VOID Sort_FileData_Table(VOID) +{ + /* + ** Sort the filetable it but keep the pack file indexes correct. + */ + + /* + ** The number of pak files in the file table. + */ + NumPAKFiles = 0; + strupr(FileData[NumPAKFiles].Name); + while (strstr((char *) FileData[NumPAKFiles].Name, (char *) ".PAK")) { + strupr(FileData[NumPAKFiles].Name); + NumPAKFiles++; + } + + /* + ** Count the remaining files within the file table. + */ + NumFiles = 0; + while(FileData[NumFiles+NumPAKFiles].Name && FileData[NumFiles+NumPAKFiles].Name[0]) { + strupr(FileData[NumFiles+NumPAKFiles].Name); + NumFiles++; + } + + /* + ** Sort the file entries (past the pak files). + */ + if (NumFiles) { + qsort(&FileData[NumPAKFiles], NumFiles, sizeof(FileDataType), QSort_Comp_Func); + } +} + diff --git a/WIN32LIB/SRCDEBUG/FILEIO.CPP b/WIN32LIB/SRCDEBUG/FILEIO.CPP new file mode 100644 index 0000000..c1f4fd9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILEIO.CPP @@ -0,0 +1,151 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : FILEIO.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 21, 1991 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +WORD ibm_getdisk(VOID) +{ + unsigned disk; + + CallingDOSInt++; + // disk = getdisk(); + _dos_getdrive ( & disk ) ; + CallingDOSInt--; + return(disk-1); +} + +WORD ibm_setdisk(WORD drive) +{ +// WORD disk; + unsigned disk ; + + CallingDOSInt++; +// disk = setdisk(drive); + _dos_setdrive ( drive+1 , & disk ) ; + CallingDOSInt--; + return(disk); +} + +WORD ibm_close(WORD handle) +{ + WORD success; + + CallingDOSInt++; + success = close(handle); + CallingDOSInt--; + return(success); +} + +WORD ibm_unlink(BYTE const *name) +{ + WORD success; + + CallingDOSInt++; + success = unlink(name); + CallingDOSInt--; + return(success); +} + +LONG ibm_lseek(WORD handle, LONG offset, WORD where) +{ + LONG new_offset; + + CallingDOSInt++; + new_offset = lseek(handle, offset, where); + CallingDOSInt--; + return(new_offset); +} + +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes) +{ + UWORD bytes_read; + + CallingDOSInt++; + bytes_read = read(handle, ptr, bytes); + CallingDOSInt--; + return(bytes_read); +} + +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes) +{ + UWORD bytes_written; + + CallingDOSInt++; + bytes_written = write(handle, ptr, bytes); + CallingDOSInt--; + return(bytes_written); +} + +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib) +{ + WORD handle; + + CallingDOSInt++; + handle = open(name, mode, attrib); + CallingDOSInt--; + return(handle); +} + +WORD ibm_chdir(BYTE const *path) +{ + WORD retval; + + CallingDOSInt++; + retval = chdir(path); + CallingDOSInt--; + return(retval); +} + + + + diff --git a/WIN32LIB/SRCDEBUG/FILELIB.CPP b/WIN32LIB/SRCDEBUG/FILELIB.CPP new file mode 100644 index 0000000..3a9de39 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILELIB.CPP @@ -0,0 +1,388 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library support routines. * + * * + * File Name : FILELIB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + * * + * * + *-------------------------------------------------------------------------* + * Notes: This file contains private functions to the fileio system. * + * While these functions may be used by any module in the fileio * + * system, they cannot be used by a user program. For this reason * + * they are put into this module. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Cache_File -- Attempts to cache file in XMS if flags set. * + * Do_IO_Error -- Performs a non-recoverable error message display. * + * Do_Open_Error -- Does an error message that could return. * + * Is_Handle_Valid -- Determines validity of the specified file handle. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + + +#include +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * DO_ERROR -- Does an error message that could return. * + * * + * This routine displays a file error message and unless the player * + * presses , it will return. If the player presses , then * + * it will terminate the program. * + * * + * INPUT: error -- Error message number. * + * * + * filename -- File name that the error occured on. * + * * + * OUTPUT: TRUE/FALSE; Should the process be repeated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + *=========================================================================*/ +#pragma argsused +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name) +{ + BYTE *ptr=NULL; // Working file name pointer (just name and extension). + + /* + ** Since the file name may include a path, we must extract the true + ** file name from the given string. + */ + if (file_name) { +#if LIB_EXTERNS_RESOLVED + ptr = strrchr((char *) file_name, (int) '\\'); +#else + ptr = NULL; +#endif + if (ptr) { + ptr++; + } else { + ptr = (BYTE *) file_name; + } + } + +#if LIB_EXTERNS_RESOLVED + strupr(ptr); + return (IO_Error(errormsgnum, ptr)); +#else + return(0); + +#endif + +} + + +/*************************************************************************** + * DO_IO_ERROR -- Performs a non-recoverable error message display. * + * * + * This routine will perform a non-recoverable file error message * + * display. It is called when an error is detected that has no * + * recovery or retry process defined. * + * * + * INPUT: errornum -- Error number detected. * + * * + * filename -- Name of the file that caused the error. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + *=========================================================================*/ +#pragma argsused +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename) +{ +#if LIB_EXTERNS_RESOLVED + (VOID)IO_Error(errormsgnum, filename); +#endif + #if(TRUE) + Prog_End(); + exit((int)errormsgnum); + #else + Program_End(); + #endif +} + + +/*************************************************************************** + * Read_File_With_Recovery -- read the same file on another directory if an error * + * occurs. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/16/1993 QY : Created. * + *=========================================================================*/ +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ) +{ + WORD newhandle; + LONG bytes_read; + + do { + Hard_Error_Occured = 0; + + // Make sure we are in the right path. + CHANGEDIR( DataPath ); + + // open the same file + newhandle = Open_File( FileHandleTable[ handle ].Name, FileHandleTable[ handle ].Mode ); + Seek_File( newhandle, FileHandleTable[ handle ].Pos, SEEK_SET ); + + // dos close the old file + FILECLOSE( FileHandleTable[ handle ].Handle ); + + // copy FileHandleTable[ newhandle ] to FileHandleTable[ handle ] + Mem_Copy( &FileHandleTable[ newhandle ], &FileHandleTable[ handle ], + ( ULONG ) sizeof( FileHandleTable[ newhandle ] ) ); + + // delete FileHandleTable[newhandle] + + FileHandleTable[ newhandle ].Empty = TRUE; + + // continue reading file + bytes_read = ( LONG ) FILEREAD( FileHandleTable[ handle ].Handle, buf, bytes ); + + // if still error, do it again; else return the number of bytes read + if ( !Hard_Error_Occured ) { + return( bytes_read ); + } + if (!Do_Open_Error(COULD_NOT_OPEN, FileHandleTable[ handle ].Name)) { + return(FALSE); + } + + } while (CHANGEDIR( DataPath )); + + return (NULL); +} + +/*************************************************************************** + * Open_File_With_Recovery -- open the same file on another directory * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/16/1993 QY : Created. * + *=========================================================================*/ +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ) +{ + WORD handle; + + Hard_Error_Occured = FALSE; + handle = FILEOPEN(file_name, mode); + + // Do not return if there was a HardError and Using a CD and we are looking at + // the CD. + if (!Hard_Error_Occured || !UseCD || (ibm_getdisk() != (*DataPath - 'A'))) { + return (handle); + } + +#if DEBUGPRINT + Mono_Print(file_name); Mono_Print(":OPENERROR "); +#endif + + Hard_Error_Occured = 0; + + // It is possible that the CD has been poped out and put back in, let us + // change there and then try again. + ibm_setdisk(*DataPath - 'A'); + CHANGEDIR( DataPath ); + + // open the same file + handle = FILEOPEN( file_name, mode ); + + // if still error, do it again; else return the dos handle + if ( !Hard_Error_Occured ) { + return( handle ); + } + + Hard_Error_Occured = 0; + return (FILEOPENERROR); +} + + +/*************************************************************************** + * CACHE_FILE -- Attempts to cache file in XMS if flags set. * + * * + * * + * INPUT: WORD index - the index of the file in the FileData table. * + * WORD file_handle - WWS file handle of file. * + * * + * OUTPUT: BOOL : was it cached? * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Cache_File(WORD index, WORD file_handle) +{ + LONG filesize; // Size of the memory block needed. + LONG freecache; // Amount of free XMS. + FileDataType *filedata = NULL; + FileDataType hold; + FileHandleType *filehandletable; + WORD flag; // Type of system memory to cache file. + WORD file; + + // Only files in the file table can be cached. + if (index == ERROR) { + return FALSE; + } + + // Setup our pointer to the file we may want to cache. + filedata = &FileDataPtr[index]; + + // Should this be cached, and is it not yet cached? + if ((filedata->Flag & (FILEF_RESIDENT|FILEF_PRELOAD)) && !filedata->Ptr) { + + filesize = filedata->Size; + + /* + ** If there is o room to cache the file, then turn off its cache file. + */ + if (filesize > Mem_Pool_Size(FileCacheHeap)) { + + // Remove resident flags so that it will not keep trying to cache itself + // since there will never be enough room for it. + filedata->Flag &= ~(FILEF_PRELOAD|FILEF_KEEP|FILEF_RESIDENT|FILEF_FLUSH); + + return FALSE; + } + + + // Go through freeing files until there is enouph space in the + // memory pool. + while (filesize > Mem_Avail(FileCacheHeap)) { + VOID *node; + + // Get the oldest non used file pointer. + node = Mem_Find_Oldest(FileCacheHeap); + + // If non was found, sorry no room for the new file. + if (!node) { + return (FALSE); + } + + // Get a pointer to the structure for convenience. + filedata = &FileDataPtr[Mem_Get_ID(node)]; + + // Free it from the heap and update the file system so it knows that + // the file is no longer in memory. + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } + + + // If there is not a big enough space we will have to take garbage + // collection hit. (OUCH!!!!!!) + if (filesize > Mem_Largest_Avail(FileCacheHeap)) { + Unfragment_File_Cache(); + } + + // Make sure we have a big enough space and if so, put the file into memory. + if (filesize < Mem_Largest_Avail(FileCacheHeap)) { + + // Get some pointers to save code space and time. + filehandletable = &FileHandleTable[file_handle]; + filedata = &FileDataPtr[index]; + + // Alloc the buffer in our file cache, then read the file in. + filedata->Ptr = Mem_Alloc(FileCacheHeap, filesize, index); + + // Extra check - it should not fail. + if (!filedata->Ptr) return(FALSE); + + // Mark it so that it never comes back as Oldest used. + Mem_In_Use(filedata->Ptr); + + // Get the file into memory. + Read_File(file_handle, filedata->Ptr, filesize); + + // reset the read index from the above read. + filehandletable->Pos = 0L; + + // This makes caching inner pak file possible. No longer is the + // PAK'd file based off the parent file. + filehandletable->Start = 0; + + // Close the parent file. Remove it's open count. + if (filedata->Flag & FILEF_PACKED) { + FileDataType p_hold; + FileDataType *parent; + + parent = &FileDataPtr[filedata->Disk]; + parent->OpenCount--; + } + FILECLOSE(filehandletable->Handle); + filehandletable->Handle = 0; + + return (TRUE); + } + } + + // The file was not cached, let the caller know. + return (FALSE); +} diff --git a/WIN32LIB/SRCDEBUG/FILESTUB.CPP b/WIN32LIB/SRCDEBUG/FILESTUB.CPP new file mode 100644 index 0000000..f6d0438 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILESTUB.CPP @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwlib32 * + * * + * File Name : FILESTUB.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : May 3, 1994 [BR] * + * * + * This module is a temorary stub that contains IO_Error. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "wwstd.h" +#include "file.h" + +WORD Text_IO_Error(FileErrorType, BYTE const *){return FALSE;} +WORD (*IO_Error)(FileErrorType, BYTE const *) = Text_IO_Error; + +/************************* End of filestub.cpp *****************************/ diff --git a/WIN32LIB/SRCDEBUG/FILLQUAD.ASM b/WIN32LIB/SRCDEBUG/FILLQUAD.ASM new file mode 100644 index 0000000..e610c55 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILLQUAD.ASM @@ -0,0 +1,669 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : FILLQUAD.ASM * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Fill_Quad -- Flood fills an arbitrary convex quadrilateral * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +SLOT_VACANT EQU 80008000h +NULL EQU 0h + + +CODESEG + +;*************************************************************************** +;* VVC::FILL_QUAD -- Flood fills an arbitrary convex quadrilateral * +;* * +;* INPUT: DWORD this_object - associated graphic viewport * +;* DWORD span_buff - pointer to span array * +;* DWORD x0_pixel - the zeroth x pixel position * +;* DWORD y0_pixel - the zeroth y pixel position * +;* DWORD x1_pixel - the first x pixel position * +;* DWORD y1_pixel - the first y pixel position * +;* DWORD x2_pixel - the second x pixel position * +;* DWORD y2_pixel - the second y pixel position * +;* DWORD x3_pixel - the third x pixel position * +;* DWORD y3_pixel - the third y pixel position * +;* DWORD color - the color of the quad to fill * +;* * +;* Bounds Checking: Compares quad points with the graphic viewport it * +;* has been assigned to. * +;* * +;* Rasterization Rules: FILL_QUAD is designed to be used within a quad * +;* mesh. There is no pixel overlapping or stitching * +;* effects at shared borders. FILL_QUAD is NOT * +;* recommended for isolated quads. * * +;* HISTORY: * +;* 08/11/1994 IML : Created. * +;* 08/26/1994 IML : Various optimizations. * +;* 08/30/1994 IML : Added rasterization rules for shared borders. * +;*=========================================================================* + PROC Buffer_Fill_Quad C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*================================================================== + ;* Define the arguments that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic viewport + ARG span_buff:DWORD ; pointer to span array + ARG x0_pixel:DWORD ; the zeroth x pixel position + ARG y0_pixel:DWORD ; the zeroth y pixel position + ARG x1_pixel:DWORD ; the first x pixel position + ARG y1_pixel:DWORD ; the first y pixel position + ARG x2_pixel:DWORD ; the second x pixel position + ARG y2_pixel:DWORD ; the second y pixel position + ARG x3_pixel:DWORD ; the third x pixel position + ARG y3_pixel:DWORD ; the third y pixel position + ARG color:DWORD ; the color of the quad + + + ;*================================================================== + ;* Define the local variables that we will use on the stack. + ;*================================================================== + LOCAL clip_min_x:DWORD ; boundary of viewport + LOCAL clip_max_x:DWORD ; + LOCAL clip_min_y:DWORD ; + LOCAL clip_max_y:DWORD ; + LOCAL clip_var:DWORD + LOCAL left_clip_base:DWORD:2 ; storage for additional edges + LOCAL left_clip_index:DWORD ; generated by clipping + LOCAL right_clip_base:DWORD:2 ; + LOCAL right_clip_index:DWORD ; + LOCAL scanline_min:DWORD ; vertical extent of quad + LOCAL scanline_max:DWORD + LOCAL realignment:DWORD + LOCAL bpr:DWORD ; bytes per row of associated buffer + + + ;*================================================================== + ;* Extract essential GraphicViewPort info. + ;*================================================================== + mov ebx,[this_object] + mov eax,[(GraphicViewPort ebx).GVPXPos] + mov [clip_min_x],eax + mov eax,[(GraphicViewPort ebx).GVPYPos] + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + + ;*================================================================== + ;* Adjust top and right edges of viewport for rasterization rules. + ;*================================================================== + dec [clip_max_y] + dec [clip_min_y] + + + ;*================================================================== + ;* Find the vertical extent of the quad BEFORE clipping. + ;* y0_pixel = y0, y1_pixel = y1, y2_pixel = y2, y3_pixel = y3 + ;*================================================================== + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jle short ??y1_not_smaller + mov eax,[y1_pixel] + +??y1_not_smaller: + cmp eax,[y2_pixel] + jle short ??y2_not_smaller + mov eax,[y2_pixel] + +??y2_not_smaller: + cmp eax,[y3_pixel] + jle short ??y3_not_smaller + mov eax,[y3_pixel] + +??y3_not_smaller: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_min + mov eax,[clip_min_y] + +??no_clamp_min_min: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_min + mov eax,[clip_max_y] + ; scanline_min = MIN (y0, y1, y2, y3) +??no_clamp_max_min: ; scanline_min = MAX (scanline_min, clip_min_y) + mov [scanline_min],eax ; scanline_min = MIN (scanline_min, clip_max_y) + + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jge short ??y1_not_greater + mov eax,[y1_pixel] + +??y1_not_greater: + cmp eax,[y2_pixel] + jge short ??y2_not_greater + mov eax,[y2_pixel] + +??y2_not_greater: + cmp eax,[y3_pixel] + jge short ??y3_not_greater + mov eax,[y3_pixel] + +??y3_not_greater: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_max + mov eax,[clip_min_y] + +??no_clamp_min_max: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_max + mov eax,[clip_max_y] + ; scanline_max = MAX (y0, y1, y2, y3) +??no_clamp_max_max: ; scanline_max = MAX (scanline_max, clip_min_y) + mov [scanline_max],eax ; scanline_max = MIN (scanline_max, clip_max_y) + + + ;*================================================================== + ;* Initialize memory for spans. + ;*================================================================== + sub eax,[scanline_min] + je ??abort_fill_quad ; don't render quads with zero height + mov ebx,eax + mov eax,[span_buff] ; check span_buff for NULL ptr + cmp eax,NULL + je ??abort_fill_quad + sal ebx,2 + +??span_initialize_loop: + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jge short ??span_initialize_loop + + + ;*================================================================== + ;* Clip and scan convert the four edges defining the quad. + ;*================================================================== +??exit_span_initialize: + mov [left_clip_index],0 + mov [right_clip_index],0 + + mov eax,[x0_pixel] + mov ebx,[y0_pixel] + mov ecx,[x1_pixel] + mov edx,[y1_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x1_pixel] + mov ebx,[y1_pixel] + mov ecx,[x2_pixel] + mov edx,[y2_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x2_pixel] + mov ebx,[y2_pixel] + mov ecx,[x3_pixel] + mov edx,[y3_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x3_pixel] + mov ebx,[y3_pixel] + mov ecx,[x0_pixel] + mov edx,[y0_pixel] + call NEAR PTR ??clip_and_scan_convert + + + ;*================================================================== + ;* Scan convert up to 2 additional left and right vertical edges + ;* generated by the clipping process. + ;*================================================================== + cmp [left_clip_index],0 + je short ??no_left_edge + mov eax,[clip_min_x] + mov ebx,[left_clip_base] + mov ecx,eax + mov edx,[left_clip_base + 4] + call NEAR PTR ??scan_convert + +??no_left_edge: + cmp [right_clip_index],0 + je short ??no_right_edge + mov eax,[clip_max_x] + mov ebx,[right_clip_base] + mov ecx,eax + mov edx,[right_clip_base + 4] + call NEAR PTR ??scan_convert + + + ;*================================================================== + ;* Fill the quad with specified color. Use DWORD copies where + ;* appropriate. + ;*================================================================== +??no_right_edge: + mov eax,[this_object] + mov edi,[(GraphicViewPort eax).GVPOffset] + mov eax,[scanline_min] ; eax = scanline_min + + mov ebx,[scanline_max] + sub ebx,[scanline_min] ; ebx = span count + + mov esi,[span_buff] ; esi = address of top span + + mul [bpr] + add edi,eax ; edi = address of top scanline + ; containing quad + mov al,[BYTE PTR color] ; extend pixel color into eax ready + mov ah,al ; for DWORD copies + mov edx,eax + shl eax,16 + mov ax,dx + + cld ; only fill forwards + + jmp ??skip_span ; rasterization rule: don't + ; render topmost span + +??quad_fill_loop: + cmp [DWORD PTR esi],SLOT_VACANT ; test for unused spans due to clipping + je ??skip_span + xor ecx,ecx + xor edx,edx + mov cx,[WORD PTR esi] + mov dx,[WORD PTR esi + 2] + sub ecx,edx + push edi + jns short ??not_negative_count + add edi,ecx + neg ecx ; ecx = span width + +??not_negative_count: + add edi,edx ; edi = address of start of span + cmp ecx,OPTIMAL_BYTE_COPY ; does span width justify DWORD copies? + jl short ??byte_copy + mov edx,ecx + mov ecx,edi + and ecx,3 ; if (ecx == 0) edi is already + jz short ??dword_copy_no_alignment ; DWORD aligned + xor ecx,3 + inc ecx ; ecx = number of pixels before alignment + sub edx,ecx + rep stosb + +??dword_copy_no_alignment: + mov ecx,edx ; ecx = remaining pixels on span + shr ecx,2 ; copy (ecx / 4) DWORDS + rep stosd + mov ecx,edx + and ecx,3 ; ecx = remaining pixels on span + +??byte_copy: + rep stosb ; byte copy remaining pixels on span + pop edi + +??skip_span: + add edi,[bpr] ; edi = address of start of next scanline + add esi,4 ; esi = address of next span + dec ebx + jge short ??quad_fill_loop ; is span count >= 0? + +??abort_fill_quad: + ret + + + ;*================================================================== + ;* This is the section that "pushes" the edge into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars clip_min_x, clip_min_y, clip_max_x, clip_max_y + ;* are used to clip the edge (default is the screen). + ;* PORTABLE start. + ;*================================================================== + + + ;*================================================================== + ;* Clip an edge against the viewport. + ;*================================================================== +??clip_and_scan_convert: + call NEAR PTR ??set_left_right_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_left_right_bits + mov [clip_var],edi + or [clip_var],esi + jz ??clip_up_down ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + +??clip_up_down: + call NEAR PTR ??set_up_down_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_up_down_bits + mov [clip_var],edi + or [clip_var],esi + jz ??scan_convert ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??scan_convert + + + ;*================================================================== + ;* Subroutine table for clipping conditions. + ;*================================================================== +??clip_tbl DD ??nada,??a_lft,??a_rgt,??nada + DD ??a_up,??nada,??nada,??nada + DD ??a_dwn + + + ;*================================================================== + ;* Subroutines for clipping conditions. + ;*================================================================== +??nada: + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + retn + +??a_dwn: + mov esi,[clip_max_y] + call NEAR PTR ??clip_vert + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[left_clip_index] + cmp ebx,[clip_min_y] + jge ??no_left_min_clip + mov ebx,[clip_min_y] + +??no_left_min_clip: + cmp ebx,[clip_max_y] + jle ??no_left_max_clip + mov ebx,[clip_max_y] + +??no_left_max_clip: + mov [left_clip_base + esi],ebx ; a left edge will be generated + mov [left_clip_index],4 ; store off yb + pop ebx + retn + +??a_rgt: + mov esi,[clip_max_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[right_clip_index] + cmp ebx,[clip_min_y] + jge ??no_right_min_clip + mov ebx,[clip_min_y] + +??no_right_min_clip: + cmp ebx,[clip_max_y] + jle ??no_right_max_clip + mov ebx,[clip_max_y] + +??no_right_max_clip: + mov [right_clip_base + esi],ebx ; a right edge will be generated + mov [right_clip_index],4 ; store off yb + pop ebx + retn + + + ;*================================================================== + ;* Clip a line against a horizontal edge at clip_y. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;* xa' = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + ;* ya' = clip_y + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax = -xa + add eax,ecx ; eax = (xb-xa) + mov edx,esi ; edx = clip_y + sub edx,ebx ; edx = (clip_y-ya) + imul edx ; eax = (clip_y-ya)(xb-xa) + idiv [clip_var] ; eax = (clip_y-ya)(xb-xa)/(yb-ya) + pop edx + add eax,edx ; eax = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + pop edx + mov ebx,esi ; ebx = clip_y + retn + + + ;*================================================================== + ;* Clip a line against a vertical edge at clip_x. + ;* (eax,ebxx) = (xa,ya), (ecx,edxx) = (xb,yb) + ;* ya' = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + ;* xa' = clip_x + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (clip_x-xa) + imul edx ; eax = (clip_x-xa)(yb-ya) + idiv [clip_var] ; eax = (clip_x-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi ; eax = clip_x + retn + + + ;*================================================================== + ;* Set the condition bits for the subroutine table. + ;*================================================================== +??set_left_right_bits: + xor esi,esi + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,1 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,2 + +??a_not_right: + retn + +??set_up_down_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,4 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,8 + +??a_not_down: + retn + + + ;*================================================================== + ;* PORTABLE end. + ;*================================================================== + + ;*================================================================== + ;* Scan convert an edge. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;*================================================================== +??scan_convert: + cmp ebx,edx + je ??exit ; if (ya == yb) don't scan convert + jl short ??no_swap ; if (ya < yb) swap vertices + xchg eax,ecx + xchg ebx,edx + +??no_swap: + sub edx,ebx ; edx = (yb - ya) + sub ebx,[scanline_min] + sal ebx,2 + add ebx,[span_buff] ; ebx = span_buff + 4(ya - clip_min_y) + sub ecx,eax ; ecx = (xb - xa) + je ??v_scan ; if the edge is vertical use a + ; special case routine + push eax + mov eax,ecx ; eax = (xb - xa) + mov ecx,edx ; ecx = (yb - ya) + sal edx,1 + mov [realignment],edx ; realignment = 2(yb - ya) + cwd + idiv cx + cwde + movsx edx,dx + mov edi,eax ; edi = (xb - xa) / (yb - ya) + mov esi,edx + mov edx,ecx + pop eax ; eax = xa + neg edx ; edx = -(yb - ya) + sal esi,1 ; esi = 2[(xb - xa) % (yb - ya)] + jns short ??r_scan ; scan to the left or right? + neg esi + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the left. + ;* eax = xpos, ebx = span to reference + ;*================================================================== + cmp ebx,[span_buff] + jg ??l_scan_convert + +??l_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??l_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??l_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??l_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??l_scan_convert_loop + dec eax + sub edx,[realignment] + jmp ??l_scan_convert_loop + + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the right. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??r_scan: + cmp ebx,[span_buff] + jg ??r_scan_convert + +??r_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??r_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??r_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??r_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??r_scan_convert_loop + inc eax + sub edx,[realignment] + jmp ??r_scan_convert_loop + + + ;*================================================================== + ;* Scan convert a vertical edge. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??v_scan: + cmp ebx,[span_buff] + jg ??v_scan_convert + +??v_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??v_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??v_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??v_scan_convert: + add ebx,4 + dec edx + jge ??v_scan_convert_loop + +??exit: + retn + + ENDP Buffer_Fill_Quad + +END diff --git a/WIN32LIB/SRCDEBUG/FILLRECT.ASM b/WIN32LIB/SRCDEBUG/FILLRECT.ASM new file mode 100644 index 0000000..5c5bf5f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FILLRECT.ASM @@ -0,0 +1,275 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Fill_Rect -- draws a filled rectangle to a graphics buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Fill_Rect C near + USES eax,ebx,ecx,edx,esi,edi,ebp + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a member function + ARG x1_pixel:WORD + ARG y1_pixel:WORD + ARG x2_pixel:WORD + ARG y2_pixel:WORD + ARG color:BYTE ; what color should we clear to + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL VPwidth:DWORD ; the width of the viewport + LOCAL VPheight:DWORD ; the height of the viewport + LOCAL VPxadd:DWORD ; the additional x offset of viewport + LOCAL VPbpr:DWORD ; the number of bytes per row of viewport + + ;*=================================================================== + ;* save off the viewport characteristics on the stack + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width from viewport + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; get height from viewport + mov edx,[(GraphicViewPort ebx).GVPXAdd] ; get xadd from viewport + add edx,[(GraphicViewPort ebx).GVPPitch] ; extra pitch of direct draw surface + mov [VPwidth],eax ; store the width of locally + mov [VPheight],ecx + mov [VPxadd],edx + add eax,edx + mov [VPbpr],eax + + ;*=================================================================== + ;* move the important parameters into local registers + ;*=================================================================== + movsx eax,[x1_pixel] + movsx ebx,[y1_pixel] + movsx ecx,[x2_pixel] + movsx edx,[y2_pixel] + + ;*=================================================================== + ;* Convert the x2 and y2 pixel to a width and height + ;*=================================================================== + cmp eax,ecx + jl ??no_swap_x + xchg eax,ecx + +??no_swap_x: + sub ecx,eax + cmp ebx,edx + jl ??no_swap_y + xchg ebx,edx +??no_swap_y: + sub edx,ebx + inc ecx + inc edx + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + cmp eax, [VPwidth] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sx_done ; if it's not negative, it's ok + + ;------ Clip source X to left edge of screen. + add ecx, eax ; Reduce width (add in negative src X). + xor eax, eax ; Clip to left of screen. +??sx_done: + + ;*=================================================================== + ;* Bounds check source Y. + ;*=================================================================== + cmp ebx, [VPheight] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sy_done ; if it's not negative, it's ok + + ;------ Clip source Y to top edge of screen. + add edx, ebx ; Reduce height (add in negative src Y). + xor ebx, ebx ; Clip to top of screen. + +??sy_done: + ;*=================================================================== + ;* Bounds check width versus width of source and dest view ports + ;*=================================================================== + push ebx ; save off ebx for later use + mov ebx,[VPwidth] ; get the source width + sub ebx, eax ; Maximum allowed pixel width (given coordinates). + sub ebx, ecx ; Pixel width undershoot. + jns short ??width_ok ; if not signed no adjustment necessary + add ecx, ebx ; Reduce width to screen limits. + +??width_ok: + pop ebx ; restore ebx to old value + + ;*=================================================================== + ;* Bounds check height versus height of source view port + ;*=================================================================== + push eax ; save of eax for later use + mov eax, [VPheight] ; get the source height + sub eax, ebx ; Maximum allowed pixel height (given coordinates). + sub eax, edx ; Pixel height undershoot. + jns short ??height_ok ; if not signed no adjustment necessary + add edx, eax ; Reduce height to screen limits. +??height_ok: + pop eax ; restore eax to old value + + ;*=================================================================== + ;* Perform the last minute checks on the width and height + ;*=================================================================== + or ecx,ecx + jz ??out + + or edx,edx + jz ??out + + cmp ecx,[VPwidth] + ja ??out + cmp edx,[VPheight] + ja ??out + + ;*=================================================================== + ;* Get the offset into the virtual viewport. + ;*=================================================================== + xchg edi,eax ; save off the contents of eax + xchg esi,edx ; and edx for size test + mov eax,ebx ; move the y pixel into eax + mul [VPbpr] ; multiply by bytes per row + add edi,eax ; add the result into the x position + mov ebx,[this_object] + add edi,[(GraphicViewPort ebx).GVPOffset] + + mov edx,esi ; restore edx back to real value + mov eax,ecx ; store total width in ecx + sub eax,[VPwidth] ; modify xadd value to include clipped + sub [VPxadd],eax ; width bytes (subtract a negative number) + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ebx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,bx + + ;*=================================================================== + ; If there is no row offset then adjust the width to be the size of + ; the entire viewport and adjust the height to be 1 + ;*=================================================================== + mov esi,[VPxadd] + or esi,esi ; set the flags for esi + jnz ??row_by_row_aligned ; and act on them + + xchg eax,ecx ; switch bit pattern and width + mul edx ; multiply by edx to get size + xchg eax,ecx ; switch size and bit pattern + mov edx,1 ; only 1 line off view port size to do + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== +??row_by_row_aligned: + mov ebp,ecx ; width saved in ebp + cmp ecx,OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??row_by_row ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + mov ebx,edi ; get output position + and ebx,3 ; is there a remainder? + jz ??aligned_loop ; if not we are aligned + xor ebx,3 ; find number of align bytes + inc ebx ; this number is off by one + sub ebp,ebx ; subtract from width + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== +??aligned_loop: + mov ecx,ebx ; get number of bytes to align + rep stosb ; and move them over + mov ecx,ebp ; get number of aligned bytes + shr ecx,2 ; convert to DWORDS + rep stosd ; and move them over + mov ecx,ebp ; get number of aligned bytes + and ecx,3 ; find the remainder + rep stosb ; and move it over + add edi,esi ; fix the line offset + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + jmp ??exit ; we are all done + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??row_by_row: + mov ecx,ebp ; get total width in bytes + rep stosb ; store the width + add edi,esi ; handle the xadd + dec edx ; decrement the height + jnz ??row_by_row ; if any left then next line +??out: +??exit: + ret + ENDP Buffer_Fill_Rect + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/FINDARGV.CPP b/WIN32LIB/SRCDEBUG/FINDARGV.CPP new file mode 100644 index 0000000..3ac4fea --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FINDARGV.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/misc/rcs/findargv.cpp 1.2 1994/04/22 10:29:28 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : findargv * + * * + * File Name : findargv.C * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Argv -- Checks to see if string is in arguement * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "wwstd.h" +#include +#include +#include +#include + + + + +/*************************************************************************** + * Find_Argv -- Checks to see if string is in arguement * + * * + * INPUT: char *str - string to search for. * + * * + * OUTPUT: NULL if not found else pointer to string. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/14/1993 SB : Created. * + *=========================================================================*/ + +//static char command [ 256 ] ; +#pragma on (argsused) +char * __cdecl Find_Argv(char const) +{ + return (NULL); + + +#ifdef NOT_FOR_WIN95 +char * __cdecl Find_Argv(char const *str) +{ + char * ptr ; + static startup_flag = 0 ; + + if ( ! startup_flag ) + { + startup_flag = 1 ; + getcmd ( command ) ; + } + + if ( ! strlen(str) ) return NULL ; + return strstr ( command , str ) ; +#endif +} + + + + + + diff --git a/WIN32LIB/SRCDEBUG/FINDFILE.CPP b/WIN32LIB/SRCDEBUG/FINDFILE.CPP new file mode 100644 index 0000000..f1d8e5a --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FINDFILE.CPP @@ -0,0 +1,312 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : FINDFILE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 21, 1991 * + * * + * Last Update : September 29, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_File_Index -- Finds the FileTable index number for a given file. * + * Find_File -- Checks if a file is immediatly available. * + * Get_FileData -- Gets a pointer back to the correct file. * + * Find_File -- Checks if a file is immediatly available. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/*************************************************************************** + * FIND_FILE -- Checks if a file is immediatly available. * + * * + * Use this function to determine if a file is immediatly available. * + * This routine will NOT request for the proper disk to be inserted * + * if the file could not be found. Use File_Exists for that feature. * + * The Westwood file I/O system does NOT have to be initialized as * + * a prerequisit to using this function. * + * * + * INPUT: file_name -- Name of the file to check. * + * * + * OUTPUT: Returns the disk number that the file exits on (A=1, B=2, etc) * + * * + * WARNINGS: This sets the current drive to the drive that contains the * + * specified file (if it is found). * + * * + * HISTORY: * + * 11/14/1991 JLB : Created. * + * 03/14/1992 JLB : Modified for Amiga compatability. * + * 01/11/1993 SKB : Modified for CD-ROM searches. * + *=========================================================================*/ +WORD cdecl Find_File(BYTE const *file_name) +{ + FileDataType *filedata = NULL; + WORD index; // File index (if any). + WORD disk; // Disk number of file (if in filetable). + + /* + ** If the filename is invalid then it errors out as if the file wasn't + ** found (naturally). + */ + if (!file_name) return(FALSE); + + /* + ** Determine if the file has a file table entry. If it does, then + ** special checks and processing must occur. + ** Also, if it is in memory, return with it. + */ + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + if (index != ERROR) { + + // If the file is currently cached, return TRUE that it was found. + if (filedata->Ptr) { + return (TRUE); + } + } + + + /* + ** Always check the current directory for the file. Only if it can't + ** be found are furthur measures required. + */ + DiskNumber = ERROR; // This indicates file exists in current directory. + + + #if (LIB_CDROM) + ibm_setdisk(*StartPath - 'A'); + #endif + + /* + ** Check the current directory by attempting to open with READ access. + */ + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) + { + // WORD d; + unsigned d ; + + CallingDOSInt++; + close(handle); + // d = getdisk(); + _dos_getdrive ( & d) ; + CallingDOSInt--; + return(d); + } + } + + + if (index != ERROR) { + + disk = filedata->Disk; + /* + ** If the file is in a packed file, then search for the packed file + ** instead of the specified one. + */ + if (index != ERROR && (filedata->Flag & FILEF_PACKED)) { + filedata = &FileDataPtr[disk]; + return (Find_File(filedata->Name)); + } + + } + + /* + ** It could not be found on the current drive, so search the other + ** drives if allowed to do so. + */ + if (!MultiDriveSearch) { + return(FALSE); + } + +#if (LIB_CDROM) + // If we were unable to find the file on the hard drive, change + // drives to the CD rom drive and see if it is there. + ibm_setdisk(*DataPath - 'A'); + + { + WORD handle; + + Hard_Error_Occured = 0; + + handle = Open_File_With_Recovery( file_name, MODE_OLDFILE ); + + if (handle != FILEOPENERROR) { + FILECLOSE(handle); + return(ibm_getdisk() + 1); + } + } + + ibm_setdisk(*StartPath - 'A'); + return (FALSE); +#else + + { + WORD start_drive; // Original current drive number. + + /* + ** Record the current drive for restoring later in case of failure. + */ + CallingDOSInt++; + start_drive = getdisk(); + CallingDOSInt--; + + /* + ** Sweep backward from the last real drive to the first, looking for the + ** file on each in turn. + */ + for (index = MaxDevice; index != -1; index--) { + if (Is_Device_Real(index)) { + CallingDOSInt++; + setdisk(index); + CallingDOSInt--; + + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) { + CallingDOSInt++; + close(handle); + CallingDOSInt--; + DiskNumber = index+1; + return (DiskNumber); + } + } + } + } + CallingDOSInt++; + setdisk(start_drive); + CallingDOSInt--; + } + + return(FALSE); +#endif + +} + + +/*************************************************************************** + * FIND_FILE_INDEX -- Finds the FileTable index number for a given file. * + * * + * This function searches the FileTable and returns with the index of * + * the matching file. If the file doesn't exist in the table, then * + * ERROR is returned. It does not care about case. * + * * + * INPUT: filename -- Pointer to the filename to check. * + * * + * OUTPUT: Returns with the index into the FileTable. If the file does * + * not exist in the file table, then ERROR is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + * 06/11/1993 JLB : Sorts and binary searches the file table. * + *=========================================================================*/ +PRIVATE int Comp_Func(const void *p1, const void *p2) +{ + return(strcmp((char *) ((FileDataType*)p1)->Name, (char *) ((FileDataType*)p2)->Name)); +} +WORD cdecl Find_File_Index(BYTE const *filename) +{ + FileDataType *filedata; // File entry pointer. + FileDataType key; // Working file data type var. + + /* + ** Perform a binary search on the presorted filetable. + */ + if (filename) { + + filedata = NULL; + key.Name = (BYTE *) strupr((char *)filename); + if (strstr((char *)key.Name, (char *)".PAK")) { + + /* + ** If the FileData table was not loaded from the disk then the PAK files are + ** not sorted so Perform a linear search for the pak files. + ** Otherwise the files are sorted so speed things up by doing a bsearch. + */ + if (FileData == FileDataPtr) { + filedata = (FileDataType *) lfind(&key, FileDataPtr, (size_t *) &NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + else { + filedata = (FileDataType *)bsearch(&key, FileDataPtr, NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + + } else { + + /* + ** Perform a binary search for the regular files. + */ + filedata = (FileDataType *)bsearch(&key, &FileDataPtr[NumPAKFiles], NumFiles, sizeof(FileDataType), Comp_Func); + } + + // Return the element in the array if file was found in table. + if (filedata) { + return (filedata - FileDataPtr); + //return ((WORD)((((LONG)filedata) - ((LONG)FileDataPtr)) / sizeof(FileDataType))); + } + } + return(ERROR); +} + + + + diff --git a/WIN32LIB/SRCDEBUG/FONT.CPP b/WIN32LIB/SRCDEBUG/FONT.CPP new file mode 100644 index 0000000..8458274 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FONT.CPP @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + + /*************************************************************************** + ** 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 * + * * + * File Name : FONT.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : July 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Char_Pixel_Width -- Return pixel width of a character. * + * String_Pixel_Width -- Return pixel width of a string of characters. * + * Get_Next_Text_Print_XY -- Calculates X and Y given ret value from Text_P* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "font.h" +#include +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + * CHAR_PIXEL_WIDTH -- Return pixel width of a character. * + * * + * Retreives the pixel width of a character from the font width block. * + * * + * INPUT: Character. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/31/1992 DRD : Created. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +int __cdecl Char_Pixel_Width(char chr) +{ + int width; + + width = (unsigned char)*(FontWidthBlockPtr + (unsigned char)chr) + FontXSpacing; + + return(width); +} + + +/*************************************************************************** + * STRING_PIXEL_WIDTH -- Return pixel width of a string of characters. * + * * + * Calculates the pixel width of a string of characters. This uses * + * the font width block for the widths. * + * * + * INPUT: Pointer to string of characters. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/30/1992 DRD : Created. * + * 01/31/1992 DRD : Use Char_Pixel_Width. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +unsigned int __cdecl String_Pixel_Width(char const *string) +{ + WORD width; // Working accumulator of string width. + WORD largest = 0; // Largest recorded width of the string. + + if (!string) return(0); + + width = 0; + while (*string) { + if (*string == '\r') { + string++; + largest = MAX(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); // add each char's width + } + } + largest = MAX(largest, width); + return(largest); +} + + + +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * unsigned long offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& gp, unsigned long offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = gp.Get_Width() + gp.Get_XAdd(); + offset -= gp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} diff --git a/WIN32LIB/SRCDEBUG/FTPUTPIX.ASM b/WIN32LIB/SRCDEBUG/FTPUTPIX.ASM new file mode 100644 index 0000000..d2c7cd9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/FTPUTPIX.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Buffer_Get_Pixel -- get the colour of a pixel at given coords * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Buffer_Get_Pixel + +END diff --git a/WIN32LIB/SRCDEBUG/GBUFFER.CPP b/WIN32LIB/SRCDEBUG/GBUFFER.CPP new file mode 100644 index 0000000..56f3b8f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/GBUFFER.CPP @@ -0,0 +1,711 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * GBC::DD_Init -- Inits a direct draw surface for a GBC * + * GBC::Init -- Core function responsible for initing a GBC * + * GBC::Lock -- Locks a Direct Draw Surface * + * GBC::Unlock -- Unlocks a direct draw surface * + * GBC::GraphicBufferClass -- Default constructor (requires explicit init)* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif +#pragma inline + +int TotalLocks; +BOOL AllowHardwareBlitFills = TRUE; + + +//int CacheAllowed; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) : + LockCount(0), + GraphicBuff(NULL) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ + Offset = 0; + Width = 0; // Record width of Buffer + Height = 0; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + Pitch = 0; // Record width of Buffer + IsDirectDraw = FALSE; + LockCount = 0; + GraphicBuff = NULL; +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not attach a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return; + } + + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + ((gbuffer->Get_Width()+gbuffer->Get_Pitch()) * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + Pitch = gbuffer->Get_Pitch(); + GraphicBuff = gbuffer; + IsDirectDraw= gbuffer->IsDirectDraw; +} + + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + + +/*************************************************************************** + * GBC::DD_INIT -- Inits a direct draw surface for a GBC * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::DD_Init(GBC_Enum flags) +{ + // + // Create the direct draw surface description + // + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.dwFlags = DDSD_CAPS; + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + + if (!(flags & GBC_VISIBLE)) { + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags |= DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = Height; + VideoSurfaceDescription.dwWidth = Width; + } + + // + // Need to set the DDSCAPS_MODEX flag if we want a 320 wide mode + // + if ( Width == 320 ) { + VideoSurfaceDescription.ddsCaps.dwCaps |= DDSCAPS_MODEX; + } + + // + // Call CreateSurface + // + DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &VideoSurfacePtr , NULL); + AllSurfaces.Add_DD_Surface (VideoSurfacePtr); + + if ( GBC_VISIBLE & flags ){ + PaletteSurface=VideoSurfacePtr; + } + + Allocated = FALSE; // even if system alloced, dont flag it cuz + // we dont want it freed. + IsDirectDraw = TRUE; // flag it as a video surface + Offset = NOT_LOCKED; // flag it as unavailable for reading or writing + LockCount = 0; // surface is not locked +} + + +void GraphicBufferClass::Attach_DD_Surface (GraphicBufferClass * attach_buffer) +{ + VideoSurfacePtr->AddAttachedSurface (attach_buffer->Get_DD_Surface()); +} + + +/*************************************************************************** + * GBC::INIT -- Core function responsible for initing a GBC * + * * + * INPUT: int - the width in pixels of the GraphicBufferClass * + * int - the heigh in pixels of the GraphicBufferClass * + * void * - pointer to user supplied buffer (system will * + * allocate space if buffer is NULL) * + * long - size of the user provided buffer * + * GBC_Enum - flags if this is defined as a direct draw * + * surface * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::Init(int w, int h, void *buffer, long size, GBC_Enum flags) +{ + Size = size; // find size of physical buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + + // + // If the surface we are creating is a direct draw object then + // we need to do a direct draw init. Otherwise we will do + // a normal alloc. + // + if (flags & (GBC_VIDEOMEM | GBC_VISIBLE)) { + DD_Init(flags); + } else { + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + if (!Size) Size = w*h; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + Offset = (long)Buffer; // Get offset to the buffer + IsDirectDraw = FALSE; + } + + Pitch = 0; // Record width of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} + + +/*********************************************************************************************** + * GBC::Un_Init -- releases the video surface belonging to this gbuffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:44PM ST : Created * + *=============================================================================================*/ + +void GraphicBufferClass::Un_Init (void) +{ + if ( IsDirectDraw ){ + + if ( VideoSurfacePtr ){ + + while ( LockCount ){ + + if (VideoSurfacePtr->Unlock ( NULL ) == DDERR_SURFACELOST){ + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + } + } + + AllSurfaces.Remove_DD_Surface (VideoSurfacePtr); + VideoSurfacePtr->Release(); + VideoSurfacePtr = NULL; + } + } +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Default constructor (requires explicit init) * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(void) +{ + GraphicBuff = this; // Get a pointer to our self + VideoSurfacePtr = NULL; + memset(&VideoSurfaceDescription, 0, sizeof(DDSURFACEDESC)); +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer, long size) +{ + Init(w, h, buffer, size, GBC_NONE); +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Init(w, h, buffer, w * h, GBC_NONE); +} + +/*====================================================================================* + * GBC::GRAPHICBUFFERCLASS -- contructor for GraphicsBufferClass with special flags * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - unused * + * unsigned flags - flags for creation of special buffer types * + * GBC_VISIBLE - buffer is a visible screen surface * + * GBC_VIDEOMEM - buffer resides in video memory * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09-21-95 04:19pm ST : Created * + *====================================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, GBC_Enum flags) +{ + Init(w, h, NULL, w * h, flags); +} + +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ + +// +// Release the direct draw surface if it exists +// + Un_Init(); +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} + + +/*************************************************************************** + * GBC::LOCK -- Locks a Direct Draw Surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ +extern void Colour_Debug (int call_number); +extern BOOL GameInFocus; + +extern void Block_Mouse(GraphicBufferClass *buffer); +extern void Unblock_Mouse(GraphicBufferClass *buffer); + +BOOL GraphicBufferClass::Lock(void) +{ + HRESULT result; + int restore_attempts=0; + + // + // If its not a direct draw surface then the lock is always sucessful. + // + if (!IsDirectDraw) return(TRUE); + + /* + ** If the video surface pointer is null then return + */ + if (!VideoSurfacePtr) return (FALSE); + + /* + ** If we dont have focus then return failure + */ + if (!GameInFocus) return (FALSE); + + + Block_Mouse(this); + + + // + // If surface is already locked then inc the lock count and return true + // + if (LockCount){ + LockCount++; + Unblock_Mouse(this); + return(TRUE); + } + + // + // If it isn't locked at all then we will have to request that Direct + // Draw actually lock the surface. + // + + if (VideoSurfacePtr){ + while (!LockCount && restore_attempts<2) { + result = VideoSurfacePtr->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL); + + switch (result){ + case DD_OK : + Offset = (unsigned long)VideoSurfaceDescription.lpSurface; + Pitch = VideoSurfaceDescription.lPitch; + Pitch -= Width; + LockCount++; // increment count so we can track if + TotalLocks++; // Total number of times we have locked (for debugging) + //Colour_Debug (1); + Unblock_Mouse(this); + return (TRUE); // we locked it multiple times. + + case DDERR_SURFACELOST : + if (Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + AllSurfaces.Restore_Surfaces(); + restore_attempts++; + break; + + default : + Unblock_Mouse(this); + return (FALSE); + } + } + } + //Colour_Debug(1); + Unblock_Mouse(this); + return (FALSE); //Return false because we couldnt lock or restore the surface +} + +/*************************************************************************** + * GBC::UNLOCK -- Unlocks a direct draw surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ + + +BOOL GraphicBufferClass::Unlock(void) +{ + // + // If there is no lock count or this is not a direct draw surface + // then just return true as there is no harm done. + // + if (!(LockCount && IsDirectDraw)) { + return(TRUE); + } + + // + // If lock count is directly equal to one then we actually need to + // unlock so just give it a shot. + // + if (LockCount == 1 && VideoSurfacePtr) { + Block_Mouse(this); + if ( VideoSurfacePtr->Unlock ( NULL ) != DD_OK ){ + Unblock_Mouse(this); + return(FALSE); + } else { + Offset=NOT_LOCKED; + LockCount--; + Unblock_Mouse(this); + return(TRUE); + } + } + //Colour_Debug (0); + LockCount--; + return(TRUE); +} + + +/*********************************************************************************************** + * GVPC::DD_Linear_Blit_To_Linear -- blit using the hardware blitter * + * * + * * + * * + * INPUT: destination vvpc * + * x coord to blit from * + * y coord to blit from * + * x coord to blit to * + * y coord to blit to * + * width to blit * + * height to blit * + * * + * OUTPUT: DD_OK if successful * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-22-95 11:05am ST : Created * + *=============================================================================================*/ + +HRESULT GraphicViewPortClass::DD_Linear_Blit_To_Linear ( + GraphicViewPortClass &dest + , int source_x + , int source_y + , int dest_x + , int dest_y + , int width + , int height + , BOOL mask ) + +{ + RECT source_rectangle; + RECT dest_rectangle; + int key_source=0; + + if ( mask ){ + key_source=DDBLT_KEYSRC; + } + + + source_rectangle.left = source_x; + source_rectangle.top = source_y; + source_rectangle.right = source_x+width; + source_rectangle.bottom = source_y+height; + + dest_rectangle.left = dest_x; + dest_rectangle.top = dest_y; + dest_rectangle.right = dest_x+width; + dest_rectangle.bottom = dest_y+height; + + return ( dest.GraphicBuff->Get_DD_Surface()->Blt ( &dest_rectangle, + GraphicBuff->Get_DD_Surface(), + &source_rectangle, + key_source | DDBLT_WAIT | DDBLT_ASYNC, + NULL ) ); +} + + + + diff --git a/WIN32LIB/SRCDEBUG/GETCD.CPP b/WIN32LIB/SRCDEBUG/GETCD.CPP new file mode 100644 index 0000000..42b37dd --- /dev/null +++ b/WIN32LIB/SRCDEBUG/GETCD.CPP @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : GETCD.CPP * + * * + * Programmer : STEVE WETHERILL BASED ON JOE BOSTIC CODE * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * GetCDClass::GetCDClass -- default constructor * + * GetCDClass::~GetCDClass -- destructor * + * GetCDClass::GetCDDrive -- returns the logical CD drive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * GetCDClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ + +GetCDClass::GetCDClass(VOID) +{ + char path[]={"a:\\"}; + + CDCount = 0; + CDIndex = 0; + + /* + ** Set all CD drive placeholders to empty + */ + memset (CDDrives, NO_CD_DRIVE, MAX_CD_DRIVES); + + + for (char i='c' ; i<='z' ; i++){ + path[0]=i; + if (GetDriveType (path) == DRIVE_CDROM){ + CDDrives[CDCount++] = (int) (i-'a'); + } + } + + /* + ** Catch the case when there are NO CD-ROM drives available + */ + if (CDCount == 0) { + for (char i='a' ; i<='b' ; i++){ + path[0]=i; + if (GetDriveType (path) == DRIVE_CDROM){ + CDDrives[CDCount++] = (int) (i-'a'); + } + } + } +} + +/*************************************************************************** + * GetCDClass -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW: Created. * + * 12/4/95 ST: fixed for Win95 * + *=========================================================================*/ + +GetCDClass::~GetCDClass(VOID) +{ +// if(cdDrive_addrp.seg) +// DPMI_real_free(cdDrive_addrp); // free up those conventional buffers +} + +/* ==================================================================== */ + diff --git a/WIN32LIB/SRCDEBUG/GETCLIP.ASM b/WIN32LIB/SRCDEBUG/GETCLIP.ASM new file mode 100644 index 0000000..d7e9eed --- /dev/null +++ b/WIN32LIB/SRCDEBUG/GETCLIP.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE ".\drawbuff.inc" +INCLUDE "gbuffer.inc" + + +; typedef struct { +; int x0 , y0 ; +; int x1 , y1 ; +; } CLIP_WIN ; +; Note for efficiency reasons x1 must be >= x0 and y1 >= y0 +; int get_clip ( CLIP_WIN * window , CLIP_WIN * sorce_rect ) ; + +CODESEG + + PROC get_clip C near + USES eax , ebx + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG win : dword + ARG rect : dword + + + mov edi , [ rect ] + mov esi , [ win ] + xor eax , eax + xor edx , edx + + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x0 ] + sub ebx , [ (RECTANGLE esi) . x0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . x1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x1 ] + sub ebx , [ (RECTANGLE esi) . x1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y0 ] + sub ebx , [ (RECTANGLE esi) . y0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . y1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y1 ] + sub ebx , [ (RECTANGLE esi) . y1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , dl + ret + ENDP get_clip + + + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/GETPIX.ASM b/WIN32LIB/SRCDEBUG/GETPIX.ASM new file mode 100644 index 0000000..89a16bc --- /dev/null +++ b/WIN32LIB/SRCDEBUG/GETPIX.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Buffer_Get_Pixel -- get the colour of a pixel at given coords * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Buffer_Get_Pixel + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/GETSHAPE.CPP b/WIN32LIB/SRCDEBUG/GETSHAPE.CPP new file mode 100644 index 0000000..bcb4ba3 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/GETSHAPE.CPP @@ -0,0 +1,362 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ + diff --git a/WIN32LIB/SRCDEBUG/HARDERR.ASM b/WIN32LIB/SRCDEBUG/HARDERR.ASM new file mode 100644 index 0000000..0029b79 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/HARDERR.ASM @@ -0,0 +1,216 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Hard/Critical Error Handler * +;* * +;* File Name : harderr.asm * +;* * +;* Programmer : Scott K. Bowen. * +;* * +;* Start Date : July 18, 1994 * +;* * +;* Last Update : July 26, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Hard_Error_Handler -- Setup for handling critical errors * +;* Remove_Hard_Erroror_Handler -- Remove the critical error handler stuff* +;* Critical_Error_Handler -- Catch critical error interrupt. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +;INCLUDE "tntdos.inc" + + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CRITERR_INT_NUM EQU 24h + +DISK_ERROR_BIT EQU 01000000000000000b ; bit 7 of dh. +DISK_ERROR EQU 1 ; Value of Hard_Error_Occured if disk/floppy error. +OTHER_ERROR EQU 2 ; Value of Hard_Error_Occured if other type of error. + +; +; Interrupt handler stack frame +; +_FLGS equ [DWORD PTR ebp+52] ; 386|DOS-Extender flags +_GS equ [WORD PTR ebp+48] ; original GS +_FS equ [WORD PTR ebp+44] ; original FS +_DS equ [WORD PTR ebp+40] ; original DS +_ES equ [WORD PTR ebp+36] ; original ES +_SS equ [WORD PTR ebp+32] ; original SS +_ESP equ [DWORD PTR ebp+28] ; original ESP +_EFLAGS equ [DWORD PTR ebp+24] ; original EFLAGS +_CS equ [DWORD PTR ebp+20] ; original CS +_EIP equ [DWORD PTR ebp+16] ; original EIP +_EBP equ [DWORD PTR ebp] ; original EBP + +; +; DOS critical error stack frame +; +_DOS_FLAGS equ [WORD PTR es:ebx+22] ; interrupt stack frame from real +_DOS_CS equ [WORD PTR es:ebx+20] ; mode INT 21h +_DOS_IP equ [WORD PTR es:ebx+18] ; +_DOS_ES equ [WORD PTR es:ebx+16] ; regs at time INT 21h was issued +_DOS_DS equ [WORD PTR es:ebx+14] ; in real mode +_DOS_BP equ [WORD PTR es:ebx+12] ; +_DOS_DI equ [WORD PTR es:ebx+10] ; +_DOS_SI equ [WORD PTR es:ebx+8] ; +_DOS_DX equ [WORD PTR es:ebx+6] ; +_DOS_CX equ [WORD PTR es:ebx+4] ; +_DOS_BX equ [WORD PTR es:ebx+2] ; +_DOS_AX equ [WORD PTR es:ebx] ; + + +; +; Error codes put into Hard_Error_Code +; +DISK_WRITE_PROTECTED equ 00h +UNKOWN_DEVICE equ 01h +DRIVE_NOT_READY equ 02h +UNKOWN_COMMAND equ 03h +CRC_ERROR equ 04h +WRONG_DATA_LENGTH equ 05h +SEEK_ERROR equ 06h +UNKOWN_DEVICE_TYPE equ 07h +SECTOR_NOT_FOUND equ 08h +OUT_OF_PAPER equ 09h +WRITE_ERROR equ 0Ah +READ_ERROR equ 0Bh +GENERAL_ERROR equ 0Ch + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes /////////////////////////////////////// + +GLOBAL Install_Hard_Error_Handler :NEAR +GLOBAL Remove_Hard_Error_Handler :NEAR + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////// Global/Local Data ////////////////////////////////// + +DATASEG + +LABEL LockedDataStart BYTE +Hard_Error_Occured DB 0 ; Hard disk error or other error. +Hard_Error_Code DB 0 ; Error Code. +LABEL LockedDataEnd BYTE + + +OldRMI DD ? ; original real mode critical err vector +OldPMIOffset DD ? ; original protected mode critical err vector +OldPMISelector DD ? ; original PM crit error selector. + +InitFlags DD 0 ; Flags to indicate what has been initialized. + +; InitFlags that are set to have a fully functional interrupt. +IF_SET_VECTORS equ 1 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 2 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 4 ; Locked PM data for DPMI. +IF_FUNCTIONAL equ 8 ; crit error is in and functional. + + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Code ////////////////////////////////////////// + +CODESEG + +;*************************************************************************** +;* INSTALL_HARD_ERROR_HANDLER -- Setup for handling critical errors. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + PROC Install_Hard_Error_Handler C near + USES eax,ebx,ecx,ds,es + ret + + ENDP Install_Hard_Error_Handler + + +;*************************************************************************** +;* REMOVE_HARD_ERROROR_HANDLER -- Remove the critical error handler stuff * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + + PROC Remove_Hard_Error_Handler C near + USES ebx,ecx,edx,ds,es +; +; Restore the original interrupt vectors and exit +; + + ret + + ENDP Remove_Hard_Error_Handler + + +;*************************************************************************** +;* Critical_Error_Handler -- Catch critical error interrupt. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + +LABEL LockedCodeStart BYTE + + PROC Critical_Error_Handler NEAR + + ENDP Critical_Error_Handler + +LABEL LockedCodeEnd BYTE + +END + diff --git a/WIN32LIB/SRCDEBUG/ICONCACH.CPP b/WIN32LIB/SRCDEBUG/ICONCACH.CPP new file mode 100644 index 0000000..71ff4cc --- /dev/null +++ b/WIN32LIB/SRCDEBUG/ICONCACH.CPP @@ -0,0 +1,609 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 13th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains members of the IconCacheClass and associated non member * + * functions. All functions are to do with caching individual icons from icon sets * + * into video memory to improve the speed of subsequent drawing * + * * + * Functions: * + * Cache_New_Icon -- Call the Cache_It member to cache a registered icon to video memory * + * Invalidate_Cached_Icons -- Uncache all the icons * + * Restore_Cached_Icons -- restore cached icons after a focus loss * + * Register_Icon_Set -- register an icon set as cachable * + * Get_Free_Cache_Slot -- find an empty cache slot * + * IconCacheClass::IconCacheClass -- IconCacheClass constructor * + * IconCacheClass::~IconCacheClass -- IconCacheClass destructor * + * IconCacheClass::Restore -- restore the icons surface and recache it * + * IconCacheClass::Cache_It -- cache an icon into video memory * + * IconCacheClass::Uncache_It -- restore the video memory used by a cached icon * + * IconCacheClass::Draw_It -- use the blitter to draw the cached icon * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32_LEAN_AND_MEAN +#define _WIN32 + +#include +#include "ddraw.h" +#include "misc.h" +#include "iconcach.h" +#include "gbuffer.h" + + +static DDSURFACEDESC VideoSurfaceDescription; + +IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern "C"{ +IconSetType IconSetList[MAX_ICON_SETS]; +short IconCacheLookup[MAX_LOOKUP_ENTRIES]; +} + +int CachedIconsDrawn=0; //Counter of number of cache hits +int UnCachedIconsDrawn=0; //Counter of number of cache misses +BOOL CacheMemoryExhausted; //Flag set if we have run out of video RAM + + + + +/*********************************************************************************************** + * Optimise_Video_Memory_Cache -- optimises usage of video memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if memory was freed up * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 12:47PM ST : Created * + *=============================================================================================*/ +BOOL Optimize_Video_Memory_Cache (void) +{ + + if (CacheMemoryExhausted && + (UnCachedIconsDrawn+CachedIconsDrawn > 1000) && + UnCachedIconsDrawn > CachedIconsDrawn){ + + int cache_misses[MAX_CACHED_ICONS]; + int cache_hits[MAX_CACHED_ICONS]; + int total_cache_misses=0; + int total_cache_hits=0; + int counter; + int i; + int j; + int temp; + BOOL swapped; + + /* + ** make list of icons that have failed to cache more than 5 times + */ + for (counter=0 ; counter5){ + cache_misses[total_cache_misses++] = counter; + } + } + + /* + ** Make list of icons that have been drawn less than 3 times + */ + for (counter=0 ; counter 1){ + for (i = 0 ; i CachedIcons[cache_hits[j+1]].TimesDrawn){ + temp=cache_hits[j]; + cache_hits[j]=cache_hits[j+1]; + cache_hits[j+1]=temp; + swapped = TRUE; + } + } + if (!swapped) break; + } + } + + + /* + ** Uncache icons up to the number of failed icons + */ + + for (counter=0 ; counterCount)*2; + if (IconSetList[i].IconListOffset > MAX_LOOKUP_ENTRIES*2){ + IconSetList[i].IconSetPtr = NULL; + } + } else { + IconSetList[i].IconListOffset = 0; + } + + if (pre_cache){ + for (i=0 ; i<256 ; i++){ + Is_Icon_Cached(icon_data,i); + } + } + return; + } + } +} + + + +/*********************************************************************************************** + * Get_Free_Cache_Slot -- find a free slot in which to cache an icon * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: int - icon index * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:40AM ST : Created * + *=============================================================================================*/ +int Get_Free_Cache_Slot (void) +{ + for (int i=0 ; iRelease(); + } +} + + + + +/*********************************************************************************************** + * ICC::Restore -- Restores the icons video surface memory and reloads it based on the original* + * icon pointer * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Relies on the icons original pointer still being valie * + * * + * HISTORY: * + * 11/13/95 9:43AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Restore (void) +{ + if (IsCached && CacheSurface){ + CacheSurface->Restore(); + if (IconSource){ + Cache_It(IconSource); + } + } +} + + +/*********************************************************************************************** + * ICC::Cache_It -- allocate video memory and copy an icon to it * + * * + * * + * * + * INPUT: icon_ptr -- ptr to icon data * + * * + * OUTPUT: bool -- success? * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:44AM ST : Created * + *=============================================================================================*/ +BOOL IconCacheClass::Cache_It (void *icon_ptr) +{ + DDSCAPS surface_capabilities; + BOOL return_value; + + /* + ** If we dont have a direct draw interface yet then just fail + */ + if (!DirectDrawObject) return(FALSE); + + /* + ** Set up the description of the surface we want to create + */ + memset (&VideoSurfaceDescription , 0 , sizeof ( VideoSurfaceDescription )); + + VideoSurfaceDescription.dwSize = sizeof( VideoSurfaceDescription ); + VideoSurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + VideoSurfaceDescription.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + VideoSurfaceDescription.dwHeight = ICON_WIDTH; + VideoSurfaceDescription.dwWidth = ICON_HEIGHT; + + /* + ** If this cache object doesnt already have a surface then create one + */ + if (!CacheSurface){ + if (DD_OK!=DirectDrawObject->CreateSurface( &VideoSurfaceDescription , &CacheSurface , NULL)){ + CacheMemoryExhausted = TRUE; + return(FALSE); + } + + } + + /* + ** Make sure the surface we created isnt really in system memory + */ + if (DD_OK != CacheSurface->GetCaps(&surface_capabilities)){ + return(FALSE); + } + + if ((DDSCAPS_SYSTEMMEMORY & surface_capabilities.dwCaps) == DDSCAPS_SYSTEMMEMORY){ + CacheSurface->Release(); + return(FALSE); + } + + return_value=FALSE; + /* + ** Lock the surface so we can copy the icon to it + */ + if (DD_OK== CacheSurface->Lock ( NULL + , &(VideoSurfaceDescription) + , DDLOCK_WAIT + , NULL)){ + /* + ** Copy the icon to the surface and flag that icon is cached + */ + Cache_Copy_Icon (icon_ptr , VideoSurfaceDescription.lpSurface , VideoSurfaceDescription.lPitch); + IsCached=TRUE; + SurfaceLost=FALSE; + IconSource=icon_ptr; + return_value=TRUE; + } + CacheSurface->Unlock(NULL); + return (return_value); +} + + +/*********************************************************************************************** + * ICC::Uncache_It -- release the video memory used to cache an icon * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:48AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Uncache_It(void) +{ + + if (IsCached && CacheSurface){ + CacheSurface->Release(); + IsCached=FALSE; + CacheSurface=NULL; + IconSource=NULL; + CacheMemoryExhausted=FALSE; + } +} + + + +/*********************************************************************************************** + * ICC::Draw_It -- use the blitter to draw a cached icon * + * * + * * + * * + * INPUT: surface to draw to * + * x coord to draw to (relative to window) * + * y coord to draw to (relative to window) * + * window left coord * + * window top coord * + * window width * + * window height * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:48AM ST : Created * + *=============================================================================================*/ +void IconCacheClass::Draw_It (LPDIRECTDRAWSURFACE dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height) +{ + RECT source_rectangle; + RECT dest_rectangle; + int clip; + HRESULT return_code; + + /* + ** Set up the source and destination coordinates as required by direct draw + */ + source_rectangle.left = 0; + source_rectangle.top = 0; + source_rectangle.right = ICON_WIDTH; + source_rectangle.bottom = ICON_HEIGHT; + + dest_rectangle.left = window_left+x_pixel; + dest_rectangle.top = window_top+y_pixel; + dest_rectangle.right = dest_rectangle.left+ICON_WIDTH; + dest_rectangle.bottom = dest_rectangle.top+ICON_HEIGHT; + + /* + ** Clip the coordinates to the window + */ + if (dest_rectangle.left=window_left+window_width){ + clip = dest_rectangle.right-(window_left+window_width); + source_rectangle.right -= clip; + dest_rectangle.right -= clip; + } + + if (dest_rectangle.top=window_top+window_height){ + clip = dest_rectangle.bottom-(window_top+window_height); + source_rectangle.bottom -= clip; + dest_rectangle.bottom -= clip; + } + + if (source_rectangle.left>=source_rectangle.right){ + return; + } + + if (source_rectangle.top>=source_rectangle.bottom){ + return; + } + + /* + ** Do the blit + */ + return_code = dest_surface->Blt (&dest_rectangle , + CacheSurface , + &source_rectangle , + DDBLT_WAIT | + DDBLT_ASYNC , + NULL); + + if (return_code == DDERR_SURFACELOST && Gbuffer_Focus_Loss_Function){ + Gbuffer_Focus_Loss_Function(); + } + + if ( return_code != DDERR_SURFACELOST && return_code != DD_OK ) { + char temp[100]; + sprintf(temp,"DD Error code %d\n", return_code & 0xFFFF); + OutputDebugString(temp); + } + + TimesDrawn++; + +} + diff --git a/WIN32LIB/SRCDEBUG/ICONSET.CPP b/WIN32LIB/SRCDEBUG/ICONSET.CPP new file mode 100644 index 0000000..67a1a91 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/ICONSET.CPP @@ -0,0 +1,342 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" + +#define _WIN32 +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include "tile.h" +#include + + + +extern int Misc; + +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != WW_ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = (short)(((short)sinf.Width)<<3); + idata->Height = (short)(((short)sinf.Height)<<3); + idata->Allocated = (short)allocated; + idata->Icons = (long)iconsetptr + sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = sizeof(IControl_Type) + size + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if (buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = (void*)idata->Map; + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Icons)); + return(NULL); +} + +void * Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Map)); + return(NULL); +} diff --git a/WIN32LIB/SRCDEBUG/IFF.CPP b/WIN32LIB/SRCDEBUG/IFF.CPP new file mode 100644 index 0000000..794881d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/IFF.CPP @@ -0,0 +1,328 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +#ifdef MIN +#undef MIN +#endif + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +int __cdecl Open_Iff_File(char const *filename) +{ + int fh; // File handle. + long type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = WW_ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +void __cdecl Close_Iff_File(int fh) +{ + if (fh != WW_ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: int file handle to open IFF file, long id to get size of * + * * + * OUTPUT: long size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id) +{ + long form; // Chunk iff form name. + long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize) +{ + long form; // Chunk iff form name. + unsigned long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length) +{ + long pos; // Current position in the IFF file. + long oldpos; // Record of start of chunk offset. + long endpos; // end of file offset before we write our data + long value; + BOOL odd; // Is length odd? + char pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (short)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size long. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/WIN32LIB/SRCDEBUG/INITDLAY.CPP b/WIN32LIB/SRCDEBUG/INITDLAY.CPP new file mode 100644 index 0000000..888c18c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/INITDLAY.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : INITDLAY.C * + * * + * Programmer : Barry Green * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Delay -- I am not sure * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include "timer.h" +#include "video.h" + +BOOL VertBlank; + +/*************************************************************************** + * INIT_DELAY -- I am not sure * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/03/1994 SKB : Created. * + *=========================================================================*/ +VOID Init_Delay(VOID) +{ + WORD nz, nnz; + + nz = nnz = 0; + + CountDown.Set(15, TRUE); // set to 1/4 second and start it + + do { + if (Get_Vert_Blank()) + nnz++; + else + nz++; + } while (CountDown.Time()); + + VertBlank = (nnz > nz); +} diff --git a/WIN32LIB/SRCDEBUG/INITMONO.CPP b/WIN32LIB/SRCDEBUG/INITMONO.CPP new file mode 100644 index 0000000..889ec92 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/INITMONO.CPP @@ -0,0 +1,75 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Initialize mono * + * * + * File Name : INITMONO.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MONO_H +#include "mono.h" +#endif + +#ifndef DESCMGMT_H +#include "descmgmt.h" +#endif + +/*************************************************************************** + * INITIALIZE_MONO_SCREEN -- Initializes the Mono display data * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/21/1994 jaw: Created. * + *========================================================================*/ + +int Initialize_Mono_Screen(void) +{ + // get a valid selector to mono screen. +// Map_Segment_To_Address(0x0b0000UL, 0x8000UL); + + MonoScreen = 0xb0000 ; + + return (int)0; +} + + + diff --git a/WIN32LIB/SRCDEBUG/IRANDOM.CPP b/WIN32LIB/SRCDEBUG/IRANDOM.CPP new file mode 100644 index 0000000..0bfff25 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/IRANDOM.CPP @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "misc.h" + + + + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + diff --git a/WIN32LIB/SRCDEBUG/KEYBOARD.ASM b/WIN32LIB/SRCDEBUG/KEYBOARD.ASM new file mode 100644 index 0000000..a7d7091 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/KEYBOARD.ASM @@ -0,0 +1,2568 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "drawbuff.inc" + + GLOBAL C RealModePtr:DWORD + + GLOBAL C Install_Keyboard_Interrupt:NEAR + GLOBAL C Get_RM_Keyboard_Address:Near + GLOBAL C Get_RM_Keyboard_Size:Near + GLOBAL C Remove_Keyboard_Interrupt:NEAR + GLOBAL C Check_Key_Num:NEAR + GLOBAL C Get_Key_Num:NEAR + GLOBAL C KN_To_KA:NEAR + GLOBAL C KeyNum_Translate:NEAR + GLOBAL C Check_Key:NEAR + GLOBAL C Get_Key:NEAR + GLOBAL C Keyboard_Attributes_On:NEAR + GLOBAL C Clear_KeyBuffer:NEAR + GLOBAL C Key_Down:NEAR + GLOBAL C Keyboard_Attributes_Off:NEAR + GLOBAL C Check_Key_Bits:NEAR + GLOBAL C Get_Key_Bits:NEAR + GLOBAL C Key_Satisfied:NEAR + GLOBAL C Stuff_Key_WORD:NEAR + GLOBAL C Stuff_Key_Num:NEAR + GLOBAL C MouseQX:DWORD + GLOBAL C MouseQY:DWORD +;DBG + GLOBAL C Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near +ifdef NOT_FOR_WIN95 + + mov eax, OFFSET RealBinStart + ret +endif;ifdef NOT_FOR_WIN95 + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + +ret + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + +ifdef NOT_FOR_WIN95 + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: +endif ;NOT_FOR_WIN95 + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + +ifdef NOT_FOR_WIN95 + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +endif; NOT_FOR_WIN95 + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi +ifdef NOT_FOR_WIN95 + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf +endif; NOT_FOR_WIN95 + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts +endif; NOT_FOR_WIN95 + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + +ifdef NOT_FOR_WIN95 + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + +endif; NOT_FOR_WIN95 + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + +ifdef NOT_FOR_WIN95 + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: +endif; NOT_FOR_WIN95 + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + +ifdef NOT_FOR_WIN95 + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: +endif; NOT_FOR_WIN95 + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + +ifdef NOT_FOR_WIN95 + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf +endif; NOT_FOR_WIN95 + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + +ifdef NOT_FOR_WIN95 + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah +endif; NOT_FOR_WIN95 + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] +endif; NOT_FOR_WIN95 + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] +endif; NOT_FOR_WIN95 + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi +ifdef NOT_FOR_WIN95 + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + +endif; NOT_FOR_WIN95 + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + +ifdef NOT_FOR_WIN95 + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] +endif; NOT_FOR_WIN95 + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + +ifdef NOT_FOR_WIN95 + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf +endif; NOT_FOR_WIN95 + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + +ifdef NOT_FOR_WIN95 + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + +endif; NOT_FOR_WIN95 + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + +ifdef NOT_FOR_WIN95 + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax +endif ;NOT_FOR_WIN95 + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE +ifdef cuts +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: +endif ;NOT_FOR_WIN95 + ret + + ENDP KeyNum_Translate +endif + ifdef cuts +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 +endif ;NOT_FOR_WIN95 + ret + + ENDP Stuff_Key_WORD +endif +ifdef cuts +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + +ifdef NOT_FOR_WIN95 + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: +endif; NOT_FOR_WIN95 + ret + + ENDP Stuff_Key_Num +endif + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== +ifdef NOT_FOR_WIN95 + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== +endif; NOT_FOR_WIN95 + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + ; This routine will do what it needs to, + ; then it will decide if the old vector should be called. + ; if so, it calls it and never returns to this function. + ; if not, we do our own return. + ; the method for doing this is found in: + ; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142" + ; It says: + ; 1 - Execute a PUSHFD to save the current flags. + ; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler + ; 3 - Push register I use. + ; 4 - Do any processing. + ; 5 - Put the address of the original handler in the reserved slot. + ; 6 - Pop saved register values + ; 7 - Execute an IRETD to transfer control to original handler. +ifdef NOT_FOR_WIN95 + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + +IF DEBUG + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + + ; Step 5. + ; Now it is time to set up for the call by returning by poking + ; the old interupt handle address in. + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + mov eax,[(KeyboardType PTR esi).KeyOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).KeyOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ; Step 6. + pop fs gs es ds + popad + pop ebp + + ; Step 7. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + pop fs gs es ds + popad + + pop ebp + add esp,8 + popfd +endif;ifdef NOT_FOR_WIN95 + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + +ifdef NOT_FOR_WIN95 + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad +endif;ifdef NOT_FOR_WIN95 + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/KEYBOARD.CPP b/WIN32LIB/SRCDEBUG/KEYBOARD.CPP new file mode 100644 index 0000000..4cef027 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/KEYBOARD.CPP @@ -0,0 +1,533 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 26, 1995 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * Check_Key -- compatability routine for old 32 bit library * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "keyboard.h" +#include "timer.h" +#include "mono.h" + +void Message_Loop(void); + +WWKeyboardClass *_Kbd; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) +{ + _Kbd = this; + // + // Initialize the keyboard remap table for our system (note it would be bad if someone + // switched keyboard modes after this happened. + // + memset(VKRemap, 0, 256); + memset(AsciiRemap, 0, 2048); + for (short lp = 31; lp < 255; lp ++) { + if (isprint(lp)) { + int vk_key = VkKeyScan((unsigned char)lp); + if (vk_key > 0 && vk_key < 2048) { + AsciiRemap[vk_key] = (unsigned char)lp; + VKRemap[lp] = (unsigned char)(vk_key & 0xFF); + } + } + } + + // + // Build a remap table of the different keys which are affected by the caps lock and + // the num lock. + // + memset(ToggleKeys, 0, 256); + for (lp = 0; lp < 255; lp++ ) { + if (isalpha(lp) && isupper(lp)) { + ToggleKeys[lp] = 1; + } + if (lp >= VK_NUMPAD0 && lp <= VK_DIVIDE) { + ToggleKeys[lp] = 2; + } + } + + // + // Our buffer should start devoid of keys. + // + memset(Buffer, 0, 256); + Head = 0; + Tail = 0; + + // + // There should be no starting queued mouse events for us to have to worry + // about. + // + MouseQX = 0; + MouseQY = 0; + MState = 0; + Conditional = 0; + CurrentCursor = NULL; +} + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + int temp = Buffer[Head]; // get key out of the buffer + int newhead = Head; // save off head for manipulation + if (Is_Mouse_Key(temp)) { // if key is a mouse then + MouseQX = Buffer[(Head + 1) & 255]; // get the x and y pos + MouseQY = Buffer[(Head + 2) & 255]; // from the buffer + newhead += 3; // adjust head forward + } else { + newhead += 1; // adjust head forward + } + newhead &= 255; + Head = newhead; + return(temp); +} + +BOOL WWKeyboardClass::Is_Mouse_Key(int key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Check(void) +{ + Message_Loop(); + unsigned short temp; // store temp holding spot for key + if (Head == Tail) return(FALSE); // if no keys in buff then get out + temp = Buffer[Head]; // get key out of the buffer + return(temp); // send it back to main program +} + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Get(void) +{ + int temp,bits; // store temp holding spot for key + + while (!Check()) {} // wait for key in buffer + temp = Buff_Get(); // get key from the buffer + + bits = temp & 0xFF00; // save of keyboard bits + + if (!(bits & WWKEY_VK_BIT)) { // if its not a virtual key + temp = AsciiRemap[temp&0x1FF] | bits; // convert to ascii equivalent + } + return(temp); // return the key that we pulled out +} + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +BOOL WWKeyboardClass::Put(int key) +{ + int temp = (Tail + 1) & 255; + if (temp != Head) + { + Buffer[Tail] = (short)key; + + // + // Critical Line + // + Tail = temp; + return(TRUE); + } + return(FALSE); +} +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::Put_Key_Message(UINT vk_key, BOOL release, BOOL dbl) +{ + int bits = 0; + // + // Get the status of all of the different keyboard modifiers. Note, only pay attention + // to numlock and caps lock if we are dealing with a key that is affected by them. Note + // that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + // would be incompatible with the dos version. + // + if (vk_key != VK_LBUTTON && vk_key != VK_MBUTTON && vk_key != VK_RBUTTON) { + int shift = (GetKeyState(VK_SHIFT) & 0xFF00) != 0; + int ctrl = (GetKeyState(VK_CONTROL) & 0xFF00) != 0; + int alt = (GetKeyState(VK_MENU) & 0xFF00) != 0; + int caps = ((GetKeyState(VK_CAPITAL) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 1); + int nums = ((GetKeyState(VK_NUMLOCK) & 0x00FF) != 0) && (ToggleKeys[vk_key] == 2); + + // + // Set the proper bits for whatever the key we got is. + // + if (shift || caps || nums) { + bits |= WWKEY_SHIFT_BIT; + } + if (ctrl) { + bits |= WWKEY_CTRL_BIT; + } + if (alt) { + bits |= WWKEY_ALT_BIT; + } + } + if (!AsciiRemap[vk_key|bits]) { + bits |= WWKEY_VK_BIT; + } + if (release) { + bits |= WWKEY_RLS_BIT; + } + if (dbl) { + bits |= WWKEY_DBL_BIT; + } + // + // Finally use the put command to enter the key into the keyboard + // system. + // + return(Put(vk_key|bits)); +} + +void WWKeyboardClass::Clear(void) +{ + Head = Tail; +} + +int WWKeyboardClass::To_ASCII(int key) +{ + if ( key && WWKEY_RLS_BIT) + return(KN_NONE); + return(key); +} + +WWKeyboardClass::Down(int key) +{ + return(GetAsyncKeyState(key&0xFF)); +} + +VOID WWKeyboardClass::Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl) +{ + shift = (key & WWKEY_SHIFT_BIT) != 0; + ctrl = (key & WWKEY_CTRL_BIT) != 0; + alt = (key & WWKEY_ALT_BIT) != 0; + rls = (key & WWKEY_RLS_BIT) != 0; + dbl = (key & WWKEY_DBL_BIT) != 0; + key = (key & 0xFF); + +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + +#pragma off(unreferenced) +void WWKeyboardClass::Message_Handler(HWND , UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if ( wParam==VK_SCROLL ){ + Stop_Execution(); + } else { + Put_Key_Message(wParam); + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message(wParam, TRUE); + break; + + case WM_LBUTTONDOWN: + Put_Key_Message(VK_LBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + Put_Key_Message(VK_LBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_LBUTTONDBLCLK: + Put_Key_Message(VK_LBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDOWN: + Put_Key_Message(VK_MBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONUP: + Put_Key_Message(VK_MBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_MBUTTONDBLCLK: + Put_Key_Message(VK_MBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDOWN: + Put_Key_Message(VK_RBUTTON); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONUP: + Put_Key_Message(VK_RBUTTON, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + + case WM_RBUTTONDBLCLK: + Put_Key_Message(VK_RBUTTON, TRUE, TRUE); + Put(LOWORD(lParam)); + Put(HIWORD(lParam)); + break; + case WM_MOUSEMOVE: + if (CurrentCursor) + SetCursor(CurrentCursor); + break; + } +} +#pragma on(unreferenced) + + + + +void Message_Loop(void) +{ + + MSG msg; + + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + + + +} + + + +/*************************************************************************** + * CHECK_KEY -- compatability routine for old 32 bit library * + * * + * This routine checks to see if there is a key in the keyboard buffer * + * and returns it to the sender if there is. It does not remove the key * + * from the buffer. * + * * + * INPUT: none * + * * + * OUTPUT: The key that was pressed. * + * * + * WARNINGS: You must declare a WWKeyboardClass object before calling * + * this routine. * + * * + * HISTORY: * + * 10/26/1995 : Created. * + *=========================================================================*/ +int Check_Key(void) +{ + if (!_Kbd) return(KA_NONE); + return(_Kbd->Check() & ~WWKEY_SHIFT_BIT); +} + +void Clear_KeyBuffer(void) +{ + if (!_Kbd) return; + _Kbd->Clear(); +} + +int Check_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Check(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !flags & WWKEY_VK_BIT ) { + flags |= WWKEY_SHIFT_BIT; + } + } + + return(key | flags); +} + +int Get_Key_Num(void) +{ + if (!_Kbd) return(KN_NONE); + int key = _Kbd->Get(); + int flags = key & 0xFF00; + key = key & 0x00FF; + + if (isupper(key)) { + key = tolower(key); + if ( !flags & WWKEY_VK_BIT ) { + flags |= WWKEY_SHIFT_BIT; + } + } + return(key | flags); +} + +int KN_To_KA(int key) +{ + if ( key & WWKEY_RLS_BIT) { + return(KA_NONE); + } + if (!(key & WWKEY_VK_BIT)) { + int flags = key & 0xFF00; + key = key & 0x00FF; + if (flags & WWKEY_SHIFT_BIT) { + key = toupper(key); + flags &= ~WWKEY_SHIFT_BIT; + } + }else{ + /* + ** If its a numeric keypad key then fix it up + */ + if ((key & 0xff) >=VK_NUMPAD0 && (key & 0xff) <=VK_NUMPAD9){ + key = (key & 0xff) - VK_NUMPAD0 + KA_0; + } + } + return(key); +} + +int KN_To_VK(int key) +{ + if (!_Kbd) return(KN_NONE); + if ( key & WWKEY_RLS_BIT) { + return(VK_NONE); + } + + int flags = key & 0xFF00; + if (!(flags & WWKEY_VK_BIT)) { + key = _Kbd->VKRemap[key & 0x00FF] | flags; + } + key &= ~WWKEY_VK_BIT; + return(key); +} + +int Key_Down(int key) +{ + if (!_Kbd) return(FALSE); + return(_Kbd->Down(key)); +} + +int Get_Key(void) +{ + int retval; + + if (!_Kbd) return(KN_NONE); + retval = _Kbd->Get() & ~WWKEY_SHIFT_BIT; + if (retval & WWKEY_RLS_BIT) { + retval = KN_NONE; + } + return(retval); +} + diff --git a/WIN32LIB/SRCDEBUG/KEYIPROT.ASM b/WIN32LIB/SRCDEBUG/KEYIPROT.ASM new file mode 100644 index 0000000..8c00182 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/KEYIPROT.ASM @@ -0,0 +1,1032 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD +ifdef NOT_FOR_WIN95 + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: +endif ;NOT_FOR_WIN95 + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + +ifdef NOT_FOR_WIN95 + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 +endif ; NOT_FOR_WIN95 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +ifdef NOT_FOR_WIN95 + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf +endif; NOT_FOR_WIN95 + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +ifdef NOT_FOR_WIN95 +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF +endif ;NOT_FOR_WIN95 + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +ifdef NOT_FOR_WIN95 +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] +endif; NOT_FOR_WIN95 + retf + +STACK ; Don't really need this + +;*********************************************************** +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/KEYIREAL.ASM b/WIN32LIB/SRCDEBUG/KEYIREAL.ASM new file mode 100644 index 0000000..a62396b --- /dev/null +++ b/WIN32LIB/SRCDEBUG/KEYIREAL.ASM @@ -0,0 +1,2638 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 512 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 512 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + + + + push ax + push dx + + mov dx,3c8h + xor al,al + out dx,al + inc dx + out dx,al + + mov dx,3c9h + mov ax,cx + shr ax,3 + out dx,al + jmp ??j1 +??j1: xor al,al + out dx,al + jmp ??j2 +??j2: out dx,al + jmp ??j3 +??j3: + + pop dx + pop ax + + + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WIN32LIB/SRCDEBUG/LCWCOMP.ASM b/WIN32LIB/SRCDEBUG/LCWCOMP.ASM new file mode 100644 index 0000000..c7deecc --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LCWCOMP.ASM @@ -0,0 +1,286 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwcomp.asm 1.1 1994/04/11 15:31:10 jeff_wilson Exp $ +;*************************************************************************** +;** 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: * +;* * +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL LCW_Compress :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_Compress 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_Compress + + +END + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/LCWUNCMP.ASM b/WIN32LIB/SRCDEBUG/LCWUNCMP.ASM new file mode 100644 index 0000000..f9e28bb --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LCWUNCMP.ASM @@ -0,0 +1,292 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwuncmp.asm 1.1 1994/04/11 15:31:21 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +PROC LCW_Uncompress C near + + USES ebx,ecx,edx,edi,esi + + ARG source:DWORD + ARG dest:DWORD + ARG length:DWORD +;LOCALS + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + LOCAL lastcom:DWORD + LOCAL lastcom1:DWORD + + + mov edi,[dest] + mov esi,[source] + mov edx,[length] + +; +; +; uncompress 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 copy copy w1 bytes from offset w2 +; n=11111110,w1,b1 long run run byte b1 for w1 bytes +; n=10000000 end end of data reached +; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + +??loop: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short ??out ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short ??notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp ??loop + +??notshort: + test al,40h ; is it a length? + jne short ??notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short ??out ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp ??exit + +??notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short ??notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runlenok: + test ecx,0ffe0h + jnz ??dont_use_stosb + rep stosb + jmp ??loop + + +??dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz ??aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + +??aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz ??loop + mov ecx,edx + rep stosb + jmp ??loop + + + + + + +??notrunlength: + cmp al,0FFh ; is it a long run? + jne short ??notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + +??notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short ??runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runok: + test ecx,0ffe0h + jnz ??dont_use_movsb + rep movsb + jmp ??loop + + + + +??dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja ??use_movsb + + test edi,3 + jz ??aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + +??aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx +??use_movsb: + rep movsb + jmp ??loop + + + + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + ret + +ENDP LCW_Uncompress + +;*********************************************************** + + + END + diff --git a/WIN32LIB/SRCDEBUG/LIB.CPP b/WIN32LIB/SRCDEBUG/LIB.CPP new file mode 100644 index 0000000..2ed05b5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LIB.CPP @@ -0,0 +1,217 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./lib.c 1.16 1994/05/20 15:34:33 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Routines * + * * + * File Name : LIB.C * + * * + * Programmer : Scott Bowen * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Add_Long_To_Pointer -- Pointer arithmatic when pointer could be XMS. * + * Find_Argv -- Checks to see if string is in arguement * + * Mono_Mem_Dump -- Dumps memory to mono monitor with hex and char. * + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include "misc.h" + +//PRIVATE unsigned Divide_With_Round(unsigned num, unsigned den); + + +/*************************************************************************** + * Divide_With_Round -- Divides integers and round to nearest integer. * + * * + * INPUT: int numberator. * + * int denominator. * + * * + * OUTPUT: Returns value rounded. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/13/1992 SB : Created. * + *=========================================================================*/ +static unsigned Divide_With_Round(unsigned num, unsigned den) +{ + // return num/den + (0 ro 1). 1 if the remainder is more than half the denominator. + return( (num / den) + (unsigned)((num % den) >= ((den + 1) >> 1)) ); +} + +#define HSV_BASE 255 // This is used to get a little better persion on HSV conversion. +#define RGB_BASE 63 // Not 64, this is really the max value. + + +/*************************************************************************** + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * * + * INPUT: int r,g, and b values. * + * int *h, *s, and *v pointers. * + * * + * OUTPUT: Assigns values to *h, *s, and *v. * + * * + * WARNINGS: The reason we use a different base for HSV then RGB is * + * because we loose alot of persision by not using floating * + * point. Using the same base value (63) made it so that * + * about 50% of the time one RGB value would be one different * + * then the original if you went from RGB to HSV to RGB. * + * Using 255 drop it down to about 9% of the time we get an * + * off value. To get it perfect, we would have to make the * + * HSV base larger - but then you need to do all calculations * + * in long instead of unsigned int. * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v) +{ + unsigned int m, r1, g1, b1, tmp; + + // Convert RGB base to HSV base. + r = Divide_With_Round((r * HSV_BASE), RGB_BASE); + g = Divide_With_Round((g * HSV_BASE), RGB_BASE); + b = Divide_With_Round((b * HSV_BASE), RGB_BASE); + + // Set hue to default. + *h = 0; + + // Set v = Max(r,g,b) to find dominant primary color. + *v = (r > g) ? r : g; + if (b > *v) *v = b; + + // Set m = min(r,g,b) to find amount of white. + m = (r < g) ? r : g; + if (b < m) m = b; + + // Determine the normalized saturation. + if (*v != 0) { + *s = Divide_With_Round( (*v - m) * HSV_BASE ,*v); + } else { + *s = 0; + } + + if (*s != 0) { + tmp = *v - m; + r1 = Divide_With_Round( (*v - r) * HSV_BASE, tmp); + g1 = Divide_With_Round( (*v - g) * HSV_BASE, tmp); + b1 = Divide_With_Round( (*v - b) * HSV_BASE, tmp); + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if ((*v) == r) { + if (m == g) { + *h = 5 * HSV_BASE + b1; + } else { + *h = 1 * HSV_BASE - g1; + } + } else { + if ((*v) == g) { + if (m == b) { + *h = 1 * HSV_BASE + r1; + } else { + *h = 3 * HSV_BASE - b1; + } + } else { + // *v == b + if (m == r) { + *h = 3 * HSV_BASE + g1; + } else { + + *h = 5 * HSV_BASE - r1; + } + } + } + + // Divide by six and round. + *h = Divide_With_Round(*h, 6); + } +} + +/*************************************************************************** + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * * + * INPUT: int h,s, and v coordinates * + * int *r, *g, and *b pointers. * + * * + * OUTPUT: Assigns values to *r, *g, and *b. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b) +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Tempary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + + h *= 6; + f = h % HSV_BASE; + + // Set up possible red, green and blue values. + values[1] = + values[2] = v; + + // + // The following lines of code change + // values[3] = (v * (HSV_BASE - ( (s * f) / HSV_BASE) )) / HSV_BASE; + // values[4] = values[5] = (v * (HSV_BASE - s)) / HSV_BASE; + // values[6] = (v * (HSV_BASE - (s * (HSV_BASE - f)) / HSV_BASE)) / HSV_BASE; + // so that the are rounded divides. + // + + tmp = Divide_With_Round(s * f, HSV_BASE); + values[3] = Divide_With_Round(v * (HSV_BASE - tmp), HSV_BASE); + + values[4] = + values[5] = Divide_With_Round(v * (HSV_BASE - s), HSV_BASE); + + tmp = HSV_BASE - Divide_With_Round(s * (HSV_BASE - f), HSV_BASE); + values[6] = Divide_With_Round(v * tmp, HSV_BASE); + + + // This should not be rounded. + i = h / HSV_BASE; + + i += (i > 4) ? -4 : 2; + *r = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *b = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *g = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); +} diff --git a/WIN32LIB/SRCDEBUG/LOAD.CPP b/WIN32LIB/SRCDEBUG/LOAD.CPP new file mode 100644 index 0000000..98f56d0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LOAD.CPP @@ -0,0 +1,378 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include +#include +#include +#include + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + void *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * void * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize=0; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((short *)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * void *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_Word(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((short*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_Word(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Uncompress_Data(void const *src, void *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_Long(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_Word(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((void *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((void *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((void *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((void *) src, (void *) dst, (unsigned long) uncomp_size); + break; + + } + + return(uncomp_size); +} + + diff --git a/WIN32LIB/SRCDEBUG/LOADFONT.CPP b/WIN32LIB/SRCDEBUG/LOADFONT.CPP new file mode 100644 index 0000000..039dd66 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LOADFONT.CPP @@ -0,0 +1,140 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include +#include +#include + +#if(IBM) +#include +#include + +#include + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +char FontWidth = 8; +char FontHeight = 8; + +// only font.c and set_font.c use the following +char *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Load_Font(char const *name) +{ + char valid; + int fh; // DOS file handle for font file. + unsigned short size; // Size of the data in the file (-2); + char *ptr = NULL; // Pointer to newly loaded font. + + + + fh=Open_File(name,READ); + if ( fh>=0 ){ + if ( Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size , MEM_NORMAL ); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return ((void*)errno); + } + + + +#ifdef cuts + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size, MEM_NORMAL); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } +#endif + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} + +#endif diff --git a/WIN32LIB/SRCDEBUG/LOADPAL.CPP b/WIN32LIB/SRCDEBUG/LOADPAL.CPP new file mode 100644 index 0000000..f9f53a0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LOADPAL.CPP @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Load_Palette * + * * + * File Name : LOADPAL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 27, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the file I/O system, * + * specifically Load_Data(). * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Palette -- Loads a palette file into the given palette buffer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "iff.h" +#include "palette.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +/*************************************************************************** + * Load_Palette -- Loads a palette file into the given palette buffer. * + * * + * INPUT: * + * BYTE * file_name - name of the file to load. * + * BYTE * palette_pointer - pointer to palette buffer. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer) +{ + #if(IBM) + Load_Data(palette_file_name, palette_pointer, 768); + #else + Load_Data(palette_file_name, palette_pointer, (ULONG)(2<. +*/ + +/*************************************************************************** + ** 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 : IFF * + * * + * File Name : LOADPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 3, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * GraphicBufferClass* Read_PCX_File (char* name, void *Buff, long size ); * + * int Get_PCX_Palette (char * name, void& palette ) * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optinal, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointd by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Read_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size) +{ + unsigned i , j ; + unsigned rle ; + unsigned color ; + unsigned scan_pos ; + char * file_ptr ; + int width ; + int height ; + int file_handle ; + char * buffer ; + PCX_HEADER header ; + RGB * pal ; + char pool [ POOL_SIZE ] ; + GraphicBufferClass * pic ; + + // Open file name + file_handle = Open_File ( name , READ ) ; + if ( file_handle == WW_ERROR ) return NULL ; + + Read_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + if ( header.id != 10 && header.version != 5 && + header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1 ; + height = header.height - header.y + 1 ; + + if ( Buff ) { + buffer = ( char * ) Buff; + i = Size / width; + height = MIN ( i - 1, height); + pic = new GraphicBufferClass( width, height, buffer ,Size); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } else { + pic = new GraphicBufferClass( width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } + + buffer = (char *) pic->Get_Buffer() ; + file_ptr = pool ; + Read_File ( file_handle, pool , POOL_SIZE ) ; + + if ( header.byte_per_line != width ) + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ) ; + i += rle ; + } else * ( buffer + scan_pos + i ++ ) = (char)rle ; + } + if ( i == width ) + rle = READ_CHAR () ; +// if ( rle > 192 ) rle = READ_CHAR (); + } + + + else for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ) ; + i += rle ; + } else * ( buffer + i ++ ) = (char)rle ; + } + + if ( Palette ) { + //Seek_File ( file_handle , - 256 * sizeof ( RGB ) , SEEK_END ) ; + Seek_File ( file_handle , 256 * sizeof ( RGB ) , SEEK_END ) ; + Read_File ( file_handle, Palette , 256L * sizeof ( RGB )) ; + + pal = ( RGB * ) Palette ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red >>= 2 ; + pal -> green >>= 2 ; + pal -> blue >>= 2 ; + pal ++ ; + } + } + Close_File (file_handle) ; + return pic ; +} + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff, * + * char* palette) * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * Buff is a pointer to a BufferClass the will hold the pcx file * + * at location pointd by Buffer; * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette) +{ + return Read_PCX_File(name, palette, (void*)Buff.Get_Buffer(), Buff.Get_Size()); +} \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/LOADPICT.CPP b/WIN32LIB/SRCDEBUG/LOADPICT.CPP new file mode 100644 index 0000000..4b38532 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/LOADPICT.CPP @@ -0,0 +1,552 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/loadpict.cpp 1.1 1994/04/20 14:38:08 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFFEXTRA.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 11, 1991 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * ILBM_To_Amiga -- Convert ILBM to bitplane Amiga format. * + * ILBM_To_MCGA -- Converts ILBM picture into MCGA format. * + * PBM_To_Amiga -- Converts a PBM picture into Amiga format. * + * Load_Picture -- Loads a picture file (CPS or LBM format). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include // For Alloc. + +#if(IBM) +#include +#endif + +// Since we are not currently using AMIGA, this has been put in to +// give us back some code space. If it is needed for a utility, +// this module should be recompiled with that utility and set the +// define to TRUE. +#define MAKE_AMIGA_ART FALSE + +/* +** An IFF picture file can have one of two formats: +** ILBM - InterLeaved Bit Map +** PBM - Packed Bit Map +*/ +typedef enum { + FORM_ILBM, + FORM_PBM +} IFFForm_Type; + +/* +** These are the various chunks that compose an IFF picture file. +*/ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_ILBM MAKE_ID('I','L','B','M') +#define ID_PBM MAKE_ID('P','B','M',' ') +#define ID_CMAP MAKE_ID('C','M','A','P') +#define ID_BODY MAKE_ID('B','O','D','Y') +#define ID_BMHD MAKE_ID('B','M','H','D') + + +/* +** The BMHD (Bit Map HeaDer) chunk in an IFF picture file contains the +** information necessary to extract that picture from the BODY chunk. +** It also indicates the size and depth of the source art. +*/ +typedef struct { + unsigned short W, H; // Raster width and height in pixels. + short X, Y; // Pixel postion for this image. + char BPlanes; // Number of bitplanes. + unsigned char Masking; // Masking control byte. + // 0 = No masking. + // 1 = Has a mask. + // 2 = Has transparent color. + // 3 = Lasso. + unsigned char Compression; // Compression method. + // 0 = No compression. + // 1 = Byte run compression. + char pad; + unsigned short Transparent; // Transparent color number. + unsigned char XAspect, // Pixel aspect ratio of source art. + YAspect; + short PageWidth, // Source 'page' size in pixels. + PageHeight; +} BitMapHeader_Type; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void __cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, int planes); +PRIVATE void __cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes); +PRIVATE void __cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * ILBM_TO_MCGA -- Converts ILBM picture into MCGA format. * + * * + * This converts an ILBM picture (typical of DPaint LBM files) and * + * converts it to MCGA mode (byte per pixel). This function would be * + * used after the body of an ILBM picture is loaded. Because the * + * number of bitplanes can vary greatly, it is necessary to pass the * + * bitplane count to this function. The size (320 by 200) of the * + * source picture is presumed. * + * * + * INPUT: src - Buffer number for source ILBM picture. * + * * + * dest - Buffer number for place to put MCGA format. * + * * + * planes- The number of bitplanes in the ILBM picture. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + *=========================================================================*/ +PRIVATE void __cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, int planes) +{ + char *source; // Source pointer. + char *destination; // Destination pointer. + int index,j,i; // Working index values. + int bplane; // Bit plane counter. + char bytes[8]; // Byte array holding max bitplanes (8). + char value; // Composed byte(pixel) value. + + source = (char *) src.Get_Buffer(); + destination = (char *) dest.Get_Buffer(); + + memset(bytes, '\0', 8); // Makes sure upper bits will be clear. + + // Each row is grouped and processed together. + + for (index = 0; index < 200 /*bmhd.H*/; index++) { + + // Process each line in groups of 8 bytes. + + for (j = 0; j < 40 /*(bmhd.W>>3)*/; j++) { + + // Get the bitplane bytes. + + for (bplane = 0; bplane < planes /*bmhd.BPlanes*/; bplane++) { + bytes[bplane] = *(source + (bplane * 40 /*(bmhd.W>>3)*/)); + } + source++; + + // Roll the bits out to create 8 pixels (by bytes). + for (i = 0; i < 8; i++) { + + // 8 bits per byte. + value = 0; + for (bplane = planes - 1/*bmhd.BPlanes-1*/; bplane >= 0; bplane--) { + value <<= 1; // Make room for next bit. + if (bytes[bplane] & 0x80) value |= 1; // Set the bit. + bytes[bplane] <<= 1; + } + *destination++ = value; // Output the pixel byte. + } + } + + // Advance to next scan line. + source += 40 /* (bmhd.W >> 3)*/ * (planes /* bmhd.BPlanes */ - 1); + } +} + + +/*************************************************************************** + * ILBM_TO_AMIGA -- Convert ILBM to bitplane Amiga format. * + * * + * This converts an InterLeaved BitMap picture into Amiga bitplane * + * format (8K per bitplane). The data of an ILBM picture is controlled * + * by the number of bitplanes it contains. The bitplane count is * + * passed into this program. * + * * + * INPUT: src - Buffer number for source ILBM picture data. * + * * + * dest - Buffer number for destination Amiga picture data. * + * * + * planes- The number of bitplanes in the source ILBM data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE void __cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes) +{ + int row; // Working row counter. + int bp; // Working bitplane counter. + char *srcptr, // Source buffer pointer. + *dstptr; // Destination buffer pointer. + + srcptr = (char *) src.Get_Buffer(); // Source buffer pointer. + dstptr = (char *) dest.Get_Buffer(); // Destination buffer pointer. + + for (row = 0; row < 200; row++) { + for (bp = 0; bp < planes; bp++) { + Mem_Copy(srcptr,dstptr+(8000*bp),40); + srcptr += 40; + } + dstptr += 40; + } +} +#endif + + +/*************************************************************************** + * PBM_TO_AMIGA -- Converts a PBM picture into Amiga format. * + * * + * This converts a PBM (Packed Bit Map) MCGA picture into Amiga * + * bitplane format. A PBM picture presumes 8 bitplanes, but this * + * can be controlled by the 'plane' parameter passed in. * + * * + * INPUT: src - Buffer number for the source PBM data. * + * * + * dest - Buffer number to place the Amiga format picture. * + * * + * planes- The number of bitplanes to extract from the PBM source * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE void __cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, int planes) +{ + int row, // Working row counter. + col, // Working column (by byte) counter. + bit; // Working bitplane counter. + unsigned char *destptr, // Destination byte pointer. + *srcptr; // Source byte pointer. + unsigned char value; // Working input MCGA pixel number. + + + destptr = (unsigned char *) dest.Get_Buffer(); + srcptr = (unsigned char *) src.Get_Buffer(); + + memset(destptr, 0, 32000); + memset(destptr+32000, 0, 32000); + + for (row = 0; row < 200; row++) { + + for (col = 0; col < 320; col++) { + value = *srcptr++; + + for (bit = 0; bit < planes; bit++) { + if (value & (0x01 << bit)) { + destptr[(short)((8000L * (long)bit) + (col>>3))] |= 0x80 >> (col & 0x07); + } + } + } + + destptr += 40; + } +} +#endif + +/*************************************************************************** + * LOAD_PICTURE -- Loads a picture file (CPS or LBM format). * + * * + * This loads a picture file into a page buffer. The loaded file will * + * be in MCGA or Amiga mode as requested. Supported source formats * + * are CPS or all forms of IFF dpaint files. * + * * + * INPUT: filename - Source filename. The only files that are * + * processed as IFF are those files that end with * + * ".LBM". * + * * + * loadbuf - Buffer type number for the temporary loading * + * buffer. It will be trashed. * + * * + * destbuf - Buffer type number for the picture to be placed. * + * * + * palette - Palette buffer pointer. If this value is NULL * + * then no palette is loaded. * + * * + * format - Desired destination format. * + * BM_AMIGA - Destination buffer will contain the * + * picture in bitplane format (Amiga). * + * The buffer will contain data equal to * + * 8K times the number of bit planes. * + * * + * BM_MCGA - Destination buffer will contain the * + * picture in MCGA format (byte per pixel).* + * The buffer will be 64K in size. * + * * + * OUTPUT: int number of bitplanes read into the dest buffer * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 05/20/1991 JLB : Handles Amiga and IBM destination formats. * + *=========================================================================*/ +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette, PicturePlaneType format) +{ + int fh; // Input file handle. + long ifftype; // Iff form type. + int counter; // Count of the bytes decompressed. + int value; // Working compression code value. + int len; // int sized length value. + int index; // Working index values. + BitMapHeader_Type bmhd; // BMHD chunk data. + IFFForm_Type formtype; // ILBM, PBM. + char *src; // Working source body pointer. + char *dest; // Working destination body pointer. + + + //len = strlen(filename); + //strupr(filename); + + fh = Open_File(filename,READ); + if (fh == WW_ERROR) return(FALSE); + Read_File(fh,&ifftype,4L); + Close_File(fh); + + if (ifftype != ID_FORM) { + return((int)Load_Uncompress(filename, scratchbuf, destbuf, palette ) / 8000 ) ; + } else { + + fh = Open_Iff_File(filename); // Opens and checks for IFF form. + if (fh == WW_ERROR) return(FALSE); + + Read_File(fh, &ifftype, 4L); + if (ifftype == ID_ILBM) { + formtype = FORM_ILBM; // Inter-Leaved Bit Map. + } else { + if (ifftype == ID_PBM) { + formtype = FORM_PBM; // Packed Bit Map. + } else { + return FALSE; // Not a recognizable picture file. + } + } + + // Load the BMHD chunk. + if (Read_Iff_Chunk(fh,ID_BMHD,(char*)&bmhd,sizeof(BitMapHeader_Type))) { + + #if(IBM) + // Perform necessary IBM conversions to the data. + bmhd.W = Reverse_Short(bmhd.W); + bmhd.H = Reverse_Short(bmhd.H); + bmhd.X = Reverse_Short(bmhd.X); + bmhd.Y = Reverse_Short(bmhd.Y); + + // this is a mistake Xaspect and YAspect are char type + // bmhd.XAspect = Reverse_Short(bmhd.XAspect); + // bmhd.YAspect = Reverse_Short(bmhd.YAspect); + value = bmhd.XAspect ; + bmhd.XAspect = bmhd.YAspect ; + bmhd.YAspect = ( unsigned char ) value ; + + bmhd.PageWidth = Reverse_Short(bmhd.PageWidth); + bmhd.PageHeight = Reverse_Short(bmhd.PageHeight); + #endif + + if (bmhd.Masking > 2) return FALSE; // Don't allow brushes. + if (bmhd.Compression > 1) return FALSE; // Unknown compression. + + } else { + return FALSE; // Unable to read the required BMHD chunk. + } + + // Load the palette if asked. + if (palette) + { + int pbytes ; // Number of CMAP bytes required. + unsigned char color; // Palette color value. + unsigned char *paletteptr; // Allocated buffer for palette conversions. + unsigned char *source; // Scratch source CMAP data pointer. + unsigned char *dest2; // Scratch destination palette pointer. + + // Number of CMAP bytes that are needed. + pbytes = (1 << bmhd.BPlanes) * 3; + + // Allocate the temporary palette buffer. + paletteptr = (unsigned char *)Alloc(pbytes, MEM_CLEAR); + source = paletteptr; + dest2 = palette; + + // Read in only the bytes that are needed. + pbytes = (int)Read_Iff_Chunk(fh, ID_CMAP, (char *) paletteptr, pbytes); + + if (pbytes) { + + /* + ** CMAP to machine specific palette conversion code. Conversion + ** goes from CMAP three bytes per color register to the machine + ** specific form. + */ + switch(format) { + default: + case BM_MCGA: + // Convert CMAP to IBM MCGA palette form. + for (index = 0; index < pbytes; index++) { + *dest2++ = *source++ >> 2; + } + break; +#if MAKE_AMIGA_ART + + case BM_AMIGA: + // Convert CMAP to Amiga nibble packed palette form. + for (index = 0; index < pbytes; index += 3) { + *dest2++ = *(source++) >> 4; + color = (*(source++) & 0xf0); + color += *(source++) >> 4; + *dest2++ = color; + } + + break; +#endif + } + } + + Free(paletteptr); + } + + + // Load in BODY chunk. + dest = (char *) scratchbuf.Get_Buffer(); + src = (char *) destbuf.Get_Buffer(); + + if (Read_Iff_Chunk(fh, ID_BODY, src, destbuf.Get_Size())) + { + for (index = 0; index < (short)bmhd.H; index++) + { + /* Height of source */ + // Transfer (possibly uncompress) one row of data. + // PBM or ILBM reader. Bytes per row (all bitplanes). + + counter = bmhd.BPlanes * (bmhd.W >> 3); + + // If there is a mask then there is one more bitplane. + if (bmhd.Masking == 1) + counter += bmhd.W >> 3 ; + + if (bmhd.Compression == 1) + { + // The data is compressed. + // Decompress one scanline (all bitplanes) at a time. + while (counter) + { + value = ( signed char ) *src++; // Decompression code. + if (value == -128) continue; // NOOP code. + + if (value >= 0) + { + // Copy N+1 bytes. + len = ((short) value) + 1; + + // Ignore the masking bitplane. + if ( bmhd.Masking != 1 || + (bmhd.Masking==1 && counter > ((short)bmhd.W >> 3) ) ) + { + memcpy(dest, src, len); + dest += len; + } + counter -= len; + src += len; + + } + else + { + // Replicate -N+1 bytes. + len = (-((short) value)) + 1; + value = *src++; + + // Ignore the masking bitplane. + if (bmhd.Masking != 1 || (bmhd.Masking==1 && counter > ((short)bmhd.W >> 3))) + { + memset(dest,value,len); + dest += len; + } + counter -= len; + } + } + } + + else + { + // Plain data is just copied. + memcpy(dest,src,counter); + dest += counter; + src += counter; + } + } + + /* + ** Perform necessary conversions to the data in order to reach + ** the desired format. + */ + switch (format) { + default: + case BM_MCGA: // Byte per pixel desired. + if (formtype == FORM_ILBM) { + ILBM_To_MCGA(scratchbuf, destbuf, bmhd.BPlanes); + } else { + Mem_Copy(scratchbuf.Get_Buffer(), destbuf.Get_Buffer(), 64000L); + } + break; + +#if MAKE_AMIGA_ART + case BM_AMIGA: // Bitplane format desired. + if (formtype == FORM_ILBM) { + ILBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } else { + PBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } + break; +#endif + } + } + + Close_Iff_File(fh); + } + + return((short)bmhd.BPlanes); // Loaded the picture successfully. +} diff --git a/WIN32LIB/SRCDEBUG/MEM.CPP b/WIN32LIB/SRCDEBUG/MEM.CPP new file mode 100644 index 0000000..6829c57 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MEM.CPP @@ -0,0 +1,1089 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned long Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned long FreeMem; // Current amount of free ram (in paragraphs). + unsigned long TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WIN32LIB/SRCDEBUG/MEM_COPY.ASM b/WIN32LIB/SRCDEBUG/MEM_COPY.ASM new file mode 100644 index 0000000..16bfc80 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL C Mem_Copy : NEAR +GLOBAL C Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy +IF 0 +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block +ENDIF +END + + + diff --git a/WIN32LIB/SRCDEBUG/MODEMREG.CPP b/WIN32LIB/SRCDEBUG/MODEMREG.CPP new file mode 100644 index 0000000..5c80a3f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MODEMREG.CPP @@ -0,0 +1,401 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : MODEMREG.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/18/96 * + * * + * Last Update : October 18th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for obtaining modem infommation from the Win95 registry * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * Search_Registry_Key -- Search a registry key and all its subkeys for a given value * + * MREC::ModemRegistryEntryClass -- Constructor for ModemRegistryEntryClass * + * MREC::~ModemRegistryEntryClass -- Destructor.Free all the memory we allocated for modem info* + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + + +#include "modemreg.h" +#include + + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + + +/*********************************************************************************************** + * Search_Registry_Key -- Search a registry key and all its subkeys for a given value * + * * + * * + * * + * INPUT: handle to key to search in * + * name of key to search for * + * value expected in key * + * * + * OUTPUT: Handle to key containing value. Null if not found. * + * * + * WARNINGS: This function reenters itself. * + * * + * HISTORY: * + * 10/18/96 4:01AM ST : Created * + *=============================================================================================*/ +HKEY Search_Registry_Key (HKEY key_in, char *value_name, char *search_string) +{ + + int top_key_index = 0; // Index of topmost key + int retval; // Result of registry api calls + HKEY next_key; // handle of next key examine + HKEY next_search; // handle of next key to search + + + char *subkey_name = new char [256]; // Area to contain result of key enumeration + unsigned long subkey_name_length = 256; // Length of enumeration result area + FILETIME filetime; // Time key was last touched. Not used. + unsigned long value_type; // Type of data that is contained in a key. + unsigned char *key_value = new unsigned char [256]; // Area to return key values into + unsigned long key_value_length = 256; // Length of key value area + + /* + ** Scan through and enumerate all subkeys of this key. Exit the loop when there are + ** no more sub keys to enumerate. + */ + do { + subkey_name_length = 256; // Has to be set each time through the loop + + /* + ** Get the next key + */ + retval = RegEnumKeyEx (key_in, top_key_index++, subkey_name, &subkey_name_length, NULL, NULL, NULL, &filetime); + + if ( retval == ERROR_SUCCESS ){ + + /* + ** Get a handle to this key so we can search it. + */ + next_key = Get_Registry_Sub_Key (key_in, subkey_name, FALSE); + + if (next_key){ + + key_value_length = 256; // Has to be set each time through the loop + + if ( RegQueryValueEx (next_key, value_name, NULL, &value_type, key_value, &key_value_length) == ERROR_SUCCESS){ + + /* + ** If this value is type string then do a compare with the value we are looking for + */ + if (value_type == REG_SZ && !strcmp ((char*)key_value, search_string)){ + /* + ** This is our man. Delete our workspace and return the key handle + */ + delete [] subkey_name; + delete [] key_value; + return (next_key); + } + } + + /* + ** We didnt find our search value so search this key for more sub keys by reentering + ** this function with the handle of the subkey. + */ + next_search = Search_Registry_Key (next_key, value_name, search_string); + RegCloseKey (next_key); + + /* + ** If the value was found in a subkey then just return with the key handle. + */ + if (next_search){ + delete [] subkey_name; + delete [] key_value; + return (next_search); + } + } + + } + } while (retval == ERROR_SUCCESS); + + /* + ** Clean up and exit. + */ + delete [] subkey_name; + delete [] key_value; + + return (0); +} + + + +/*********************************************************************************************** + * MREC::ModemRegistryEntryClass -- Constructor for ModemRegistryEntryClass * + * * + * This function does all the work in the class. All the registry searching is done here * + * * + * INPUT: Modem number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/18/96 4:12AM ST : Created * + *=============================================================================================*/ +ModemRegistryEntryClass::ModemRegistryEntryClass (int modem_number) +{ + HKEY key; + unsigned char return_buf[256]; + DWORD retbuf_size = sizeof(return_buf); + + int pnp = 0; //Not a plug n pray modem + + /* + ** Initialise all the info we expect from the registry to NULL. + ** Any entries we cant find will just stay NULL. + */ + ModemName = NULL; + ModemDeviceName = NULL; + ErrorCorrectionEnable = NULL; + ErrorCorrectionDisable = NULL; + CompressionEnable = NULL; + CompressionDisable = NULL; + HardwareFlowControl = NULL; + NoFlowControl = NULL; + + + /* + ** Modem info is stored under + ** HKEY_LOCAL_MACHINE / System / CurrentControlSet / Services / Class / Modem / nnnn + ** where nnnn is a four digit modem number. + */ + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "System", FALSE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "CurrentControlSet", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Services", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Class", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Modem", TRUE); + if (!key) return; + + char which_modem[5]; + sprintf (which_modem, "%04d", modem_number); + + /* + ** Get a handle to the modem key if it exists. Then extract the info we need. + */ + key = Get_Registry_Sub_Key (key, which_modem, TRUE); + if (!key) return; + + + /* + ** Get the name of the modem. This is what will be displayed in the modem list presented + ** to the user. + */ + if (RegQueryValueEx(key, "Model", NULL, NULL, return_buf, &retbuf_size) != ERROR_SUCCESS){ + RegCloseKey (key); + return; + } + ModemName = new char [retbuf_size]; + memcpy (ModemName, return_buf, retbuf_size); + + /* + ** Find out what COM port the modem is attached to. If this info isnt here, then its a + ** Plug n Pray modem. Set the flag so we know to do the pnp search later. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "AttachedTo", NULL, NULL, return_buf, &retbuf_size) != ERROR_SUCCESS){ + /* + ** Must be a plug n pray modem. Set the flag. We will look for the port later. + */ + pnp = 1; + ModemDeviceName = new char [strlen (ModemName)+1]; + strcpy (ModemDeviceName, ModemName); + }else{ + ModemDeviceName = new char [retbuf_size]; + memcpy (ModemDeviceName, return_buf, retbuf_size); + } + + + /* + ** The list of modem 'AT' commands is stored in the 'Settings' key. + */ + key = Get_Registry_Sub_Key (key, "Settings", TRUE); + if (!key) return; + + + /* + ** Extract the control strings for error control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "ErrorControl_On", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + ErrorCorrectionEnable = new char [retbuf_size]; + memcpy (ErrorCorrectionEnable, return_buf, retbuf_size); + } + + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "ErrorControl_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + ErrorCorrectionDisable = new char [retbuf_size]; + memcpy (ErrorCorrectionDisable, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for data compression. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "Compression_On", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + CompressionEnable = new char [retbuf_size]; + memcpy (CompressionEnable, return_buf, retbuf_size); + } + + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "Compression_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + CompressionDisable = new char [retbuf_size]; + memcpy (CompressionDisable, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for hardware flow control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "FlowControl_Hard", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + HardwareFlowControl = new char [retbuf_size]; + memcpy (HardwareFlowControl, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for no flow control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "FlowControl_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + NoFlowControl = new char [retbuf_size]; + memcpy (NoFlowControl, return_buf, retbuf_size); + } + + + + RegCloseKey (key); + + + + /* + ** If this is a plug n pray modem then we need to search for the COM port it is + ** attached to. + */ + if (pnp){ + + /* + ** The driver name in the HKEY_LOCAL_MACHINE / Enum section will be Modem\nnnn where nnnn + ** is a four digit modem number. + */ + char search_string [256] = {"Modem\\"}; + strcat (search_string, which_modem); + + /* + ** Search through all the registry entries under HKEY_LOCAL_MACHINE / Enum + */ + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "Enum", FALSE); + if (!key) return; + + HKEY newkey = Search_Registry_Key ( key, "Driver", search_string ); + + if (newkey){ + retbuf_size = sizeof (return_buf); + + /* + ** Extract the PORTNAME value. This is the name of the port to use to communicate + ** with the modem. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(newkey, "PORTNAME", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + + if (ModemDeviceName) delete [] ModemDeviceName; + + ModemDeviceName = new char [retbuf_size]; + memcpy (ModemDeviceName, return_buf, retbuf_size); + } + } + RegCloseKey (key); + } + +} + + + + + + + + +/*********************************************************************************************** + * MREC::~ModemRegistryEntryClass -- Destructor.Free all the memory we allocated for modem info* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/18/96 11:39AM ST : Created * + *=============================================================================================*/ +ModemRegistryEntryClass::~ModemRegistryEntryClass (void) +{ + if (ModemName) delete [] ModemName; + if (ModemDeviceName) delete [] ModemDeviceName; + + if (ErrorCorrectionEnable) delete [] ErrorCorrectionEnable; + if (ErrorCorrectionDisable) delete [] ErrorCorrectionDisable; + + if (CompressionEnable) delete [] CompressionEnable; + if (CompressionDisable) delete [] CompressionDisable; + + if (HardwareFlowControl) delete [] HardwareFlowControl; + if (NoFlowControl) delete [] NoFlowControl; +} + + + + + diff --git a/WIN32LIB/SRCDEBUG/MONO.ASM b/WIN32LIB/SRCDEBUG/MONO.ASM new file mode 100644 index 0000000..2156b99 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MONO.ASM @@ -0,0 +1,845 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Mono Screen system * +;* * +;* File Name : MONO.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : September 8, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;GLOBAL MonoScreen :DWORD +;GLOBAL MonoEnabled :DWORD +; +;GLOBAL C Mono_Set_Cursor :NEAR +;GLOBAL C Mono_Clear_Screen :NEAR +;GLOBAL C Mono_Scroll :NEAR +;GLOBAL C Mono_Put_Char :NEAR +;GLOBAL C Mono_Draw_Rect :NEAR +; +;GLOBAL C _Mono_Text_Print :NEAR +;GLOBAL C Mono_Text_Print :NEAR +; +;GLOBAL C Mono_Print :NEAR +; +;GLOBAL C Mono_View_Page :NEAR +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + + +; +; External declares so these functions can be called +; +GLOBAL MonoScreen :DWORD +GLOBAL MonoEnabled :DWORD + +GLOBAL Mono_Set_Cursor :NEAR ; done +GLOBAL Mono_Clear_Screen :NEAR ; done +GLOBAL Mono_Scroll :NEAR ; done +GLOBAL Mono_Put_Char :NEAR ; done +GLOBAL Mono_Draw_Rect :NEAR ; done +GLOBAL _Mono_Text_Print :NEAR ; done +GLOBAL Mono_Text_Print :NEAR ; done +GLOBAL Mono_Print :NEAR ; done +GLOBAL Mono_View_Page :NEAR ; done + +; +; Equates used in this file +; +NULL = 0 ; null code +CR = 13 ; carriage return code +CPL = 80 ; characters per line +LPS = 25 ; lines per screen + + +DATASEG + +MonoX DD 0 +MonoY DD 0 +MonoOff DD 0 +MonoScreen DD 0b0000h ;Deffault to Real mode! +MonoEnabled DD 0 ; Is mono printing enabled? + +;==================================================================== + +CharData DB 0DAh,0C4h,0BFh,0B3h,0D9h,0C4h,0C0h,0B3h ; Single line + DB 0D5h,0CDh,0B8h,0B3h,0BEh,0CDh,0D4h,0B3h ; Double horz. + DB 0D6h,0C4h,0B7h,0BAh,0BDh,0C4h,0D3h,0BAh ; Double vert. + DB 0C9h,0CDh,0BBh,0BAh,0BCh,0CDh,0C8h,0BAh ; Double line. + + +; x,y,dist +BoxData DB 1,0,0 ; Upper left corner. + DB 1,0,1 ; Top edge. + DB 0,1,0 ; Upper right corner. + DB 0,1,2 ; Right edge. + DB -1,0,0 ; Bottom right corner. + DB -1,0,1 ; Bottom edge. + DB 0,-1,0 ; Bottom left corner. + DB 0,-1,2 ; Left edge. + DB 0,0,-1 ; End of list. + +; Mono page segment layout array. +PageMap DD 0,1,2,3,4,5,6,7 + +;=================================================================== + +CODESEG + + +;*************************************************************************** +;* Map_Segment_To_Address_ -- Translate a 16bit Seg:Offset address to a * +;* Linear address. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; int Map_Segment_To_Address ( unsigned seg , unsigned offset ); + + + +;*************************************************************************** +;* MONO_SET_CURSOR -- Sets the mono cursor to specified coordinates. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Set_Cursor(int x, int y); + + +PROC Mono_Set_Cursor C near + + USES eax , ebx , edx + + ARG xpos : DWORD + ARG ypos : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; sub eax,eax + + mov eax,[ypos] +; mov ah,CPL +; imul ah + lea eax , [ eax + 4 * eax ] ; multiply by CPL + shl eax , 4 + +; sub ebx,ebx + mov ebx,[xpos] + add ebx,eax + + ; Update cursor position. + mov edx,03B4h + + mov al,0Eh ; High byte register set. + out dx,al + inc edx + mov al,bh + out dx,al ; Set high byte. + + dec edx + mov al,0Fh ; Low byte register set. + out dx,al + inc edx + mov al,bl + out dx,al ; Set low byte. + + ; Update the globals. + add ebx,ebx + mov [MonoOff],ebx + mov eax,[xpos] + mov [MonoX],eax + mov eax,[ypos] + mov [MonoY],eax + +??exit: + ret + +ENDP Mono_Set_Cursor + + +;*************************************************************************** +;* MONO_CLEAR_SCREEN -- Clears the mono screen and homes cursor. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Clear_Screen(void); + +PROC Mono_Clear_Screen C near + + USES eax , ecx , edi + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; mov eax,[MonoScreen] ; ES:DI = Mono RAM address. +; mov es,ax +; sub edi,edi + mov edi , [ MonoScreen ] + mov eax,02000200h ; Clear leave attrib bit normal. + mov ecx,8000h/4 ; Number of longs to clear. + rep stosd ; Clear the mono screen. + + push 0 + push 0 + + call Mono_Set_Cursor + add esp , 8 + +??exit: + ret + +ENDP Mono_Clear_Screen + + +;*************************************************************************** +;* MONO_SCROLL -- Scroll the mono screen up specified lines. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Scroll(DWORD lines); +PROC Mono_Scroll C near + + USES eax , ebx , ecx , edx , edi , esi + ARG lines : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; xor eax,eax ; clear eax so no need for sign extend + mov eax, [lines] ; get lines available + or eax,eax ; any lines to scroll? + je short ??fini ; =>NO + + mov ebx,eax ; set line counter + + mov edx,[MonoY] ; get row count + ror edx,16 ; store it in high half of register + mov dx,[WORD PTR MonoOff] ; get column offset + ror edx,16 + +; mov eax,[MonoScreen] ; get selector for mono screen +; push ds ; save off data seg for later +; mov ds,ax ; set data source register +; mov es,ax ; and extra source register + + sub eax,eax ; set to clear clear line + +??looper: + mov ecx,(80*24) ; Number of words to move. + + ; xor edi,edi ; dst start at top of screen area + ; mov esi,80*2 ; src start at next line down + mov edi , [ MonoScreen ] + lea esi , [ 80 * 2 + edi ] + rep movsw ; Scroll the screen upward. + + dec dx ; decrement Y counter + ror edx,16 ; switch to mono offset + sub dx,80*2 ; fix MonoOffset + ror edx,16 ; switch to y counter + + mov ecx,40 ; Clear out the last line. + rep stosd ; by storing words across it + dec ebx ; last line? + + jne ??looper ; =>NO + + ; reset data values + ; pop ds ; restore the ds segment + mov [WORD PTR MonoY],dx ; store of the mono y position + ror edx,16 ; switch to screen offset + mov [WORD PTR MonoOff],dx ; store of the mono offset + +??fini: +??exit: + ret + +ENDP Mono_Scroll + + +;*************************************************************************** +;* MONO_PUT_CHAR -- Output a character to the mono screen. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Put_Char(char character, int attrib=2); + +PROC Mono_Put_Char C near + + USES eax , edi + + ARG character : BYTE + ARG attrib : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax + + mov edi,[MonoOff] +; mov eax,[MonoScreen] +; mov es,ax ; ES:DI = First character output pointer. + add edi , [ MonoScreen ] + + ; Output character to monochrome monitor. + mov al,[character] + mov ah,[BYTE PTR attrib] +; stosw + mov [ edi ] , ax + + ; Update cursor position. + inc [MonoX] ; X position moves. + + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + + call Mono_Set_Cursor + add esp,8 + +??exit: + ret + +ENDP Mono_Put_Char + + +;*************************************************************************** +;* MONO_DRAW_RECT -- Draw a rectangle using mono characters. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + +PROC Mono_Draw_Rect C near + + USES eax , ebx , ecx , esi , edi + + ARG xpos:DWORD + ARG ypos:DWORD + ARG width:DWORD + ARG height:DWORD + ARG attrib:DWORD + ARG thick:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + mov esi,OFFSET BoxData + mov edi,OFFSET CharData + +; mov cl,3 +; sub eax,eax + mov eax,[thick] + and eax,011b + shl eax,3 + add edi,eax + + ; Prep width and height. + cmp [width],2 + jb ??fini + + cmp [height],2 + jb ??fini + + sub [width],2 + sub [height],2 + + ; Preserve cursor position for later restore. + mov ecx,[MonoX] + push ecx + mov ecx,[MonoY] + push ecx + + ; Cursor starts at upper left corner. + mov ecx,[ypos] + push ecx + mov ecx,[xpos] + push ecx + call Mono_Set_Cursor + add esp,8 + +??drawloop: + ; Determine the number of characters to output. + mov ecx,[width] + cmp [BYTE PTR esi+2],1 + je short ??gotlen + + mov ecx,[height] + cmp [BYTE PTR esi+2],2 + je short ??gotlen + + mov ecx,1 +??gotlen: + + jecxz ??donerun + +??runloop: + sub ebx,ebx + mov bl,[BYTE PTR edi] + +; mov ebx,eax + sub eax,eax + mov al,[BYTE PTR attrib] + push eax + push ebx + + call Mono_Put_Char + add esp,8 + + movsx eax,[BYTE PTR esi+1] +; cbw + add eax,[MonoY] + push eax + movsx eax,[BYTE PTR esi] +; cbw + add eax,[MonoX] + dec eax ; Undo cursor advance. + push eax + + call Mono_Set_Cursor ; Properly advance cursor. + add esp,8 + + loop ??runloop + +??donerun: + + ; Advance to next control entry. + add esi,3 + inc edi + cmp [BYTE PTR esi+2],-1 + jne ??drawloop + + ; Restore cursor to original position. + call Mono_Set_Cursor + add esp,8 + +??fini: +??exit: + ret + +ENDP Mono_Draw_Rect + + +;*************************************************************************** +;* MONO_TEXT_PRINT -- Prints text to the mono screen at coordinates. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Text_Print(void *text, int x, int y, int attrib, int update); + +PROC _Mono_Text_Print C near + + USES eax,ebx,ecx,edx,edi,esi + + ARG text:DWORD + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + ARG update:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + ; Preserve cursor coordinates for later restoration. + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + + cmp [text],NULL + je ??fini + + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + call Mono_Set_Cursor + add esp,8 + + mov esi,[text] + +??charloop: + xor eax,eax + mov al,[BYTE PTR esi] ; Fetch character to output. + inc esi + + ; Stop processing on a NULL character. + or eax,eax + je short ??fini + + ; Special processing for a '\r' characters. + cmp eax,CR + je short ??cr + + ; Output character to monochrome monitor. +??normal: +; xor ah,ah + + mov ebx,eax + mov eax,[attrib] + push eax + push ebx + call Mono_Put_Char + add esp,8 + + ; Perform adjustments if wrapping past right margin. + cmp [WORD PTR MonoX],CPL + jb short ??nowrap + + inc [ypos] + + mov eax,[ypos] + push eax +; sub eax,eax + push 0 + call Mono_Set_Cursor + add esp,8 + + jmp short ??nowrap + + ; Move to start of next line. +??cr: + inc [ypos] + + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + call Mono_Set_Cursor + add esp,8 + + ; Scroll the monochrome screen if necessary. +??nowrap: + cmp [MonoY],LPS + jb short ??noscroll + + push 1 + call Mono_Scroll + add esp,4 + + dec [ypos] + +??noscroll: + jmp short ??charloop + +??fini: + cmp [update],0 + jne short ??noupdate + + call Mono_Set_Cursor +??noupdate: + add esp,8 + +??exit: + ret + +ENDP _Mono_Text_Print + +;===================================================================== + +PROC Mono_Text_Print C near + USES eax + ARG text:DWORD + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + + + cmp [MonoEnabled],0 + je ??exit + +; sub eax,eax + push 0 + mov eax,[attrib] + push eax + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + mov eax,[text] + push eax + + call _Mono_Text_Print + add esp,20 + +??exit: + ret + +ENDP Mono_Text_Print + + + +;*************************************************************************** +;* MONO_PRINT -- Prints text to the mono screen at current pos. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Print(void *text); + +PROC Mono_Print C near + + USES eax + + ARG text:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; mov eax,1 + push 1 +; mov eax,2 + push 2 + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + mov eax,[text] + push eax + + call _Mono_Text_Print + add esp,20 + +??exit: + ret + +ENDP Mono_Print + +;*************************************************************************** +;* Mono_View_Page -- page in a mono screen * +;* * +;* Displays the specified page in displayable mono memory area. * +;* * +;* INPUT: WORD page = which page of memory we will use for storage * +;* * +;* OUTPUT: old_page * +;* * +;* WARNINGS: none. * +;* * +;* HISTORY: * +;*=========================================================================* +; int cdecl Mono_View_Page(int page); + + +PROC Mono_View_Page C near + + USES eax,ebx,ecx,edx,edi,esi + + ARG page:DWORD + + LOCAL oldpage:DWORD + + cmp [MonoEnabled],0 + je ??exit + + cld + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + ; Prepare the original page number for return to caller. + mov ebx,[PageMap] + mov [oldpage],ebx + + ; If the desired page is already displayed, then don't do anything. + mov eax,[page] + cmp eax,ebx + je short ??fini + + ; Verify that page specified is legal. + cmp eax,7 + ja short ??fini + + ; Find where the logical page to display is actually located. + mov ecx,8 + + mov edi,OFFSET PageMap + repne scasd + neg ecx + add ecx,7 ; ECX = where desired page is located. + + ; Swap the page ID bytes in the PageMap array. + sub edi,4 + mov ebx,[PageMap] + mov eax,[edi] + mov [edi],ebx + mov [PageMap],eax + + ; Set DS and ES to point to each page. +; mov eax,[MonoScreen] +; mov ds,ax + mov esi , [ MonoScreen ] +; shl ecx,8 + shl ecx , 12 +; add ecx,edi ; NO Addition to selectors! + lea edi , [ esi + ecx ] + +; mov edi,ecx +; xor esi,esi + + ; Exchange the two pages. + mov ecx,1000H/4 + +??looper: + mov edx,[edi] + mov ebx,[esi] + mov [edi],ebx + mov [esi],edx + add esi,4 + add edi,4 + loop ??looper + + ; Return with the original page number. +??fini: +??exit: + mov eax,[oldpage] + ret + +ENDP Mono_View_Page + +END + diff --git a/WIN32LIB/SRCDEBUG/MONO.CPP b/WIN32LIB/SRCDEBUG/MONO.CPP new file mode 100644 index 0000000..b255d20 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MONO.CPP @@ -0,0 +1,789 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.cpv 2.12 06 Sep 1995 16:37:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#pragma inline +#include "mono.h" + +#include +#include +#include +#include +#include +#include + + +extern void output(short port, short data); +#pragma aux output parm [dx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" + +int MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES] = {0,0,0,0,0,0,0,0}; +void * MonoClass::MonoSegment = (void*)0x000b0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) +{ + int index; + + Attrib = DEFAULT_ATTRIBUTE; // Normal text color. + X = Y = 0; + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, char attrib, BoxStyleType thick) +{ + CellType cell; + char oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Store_Cell(cell, x+xpos+1, y); + cell.Character = CharData[thick].BottomEdge; + Store_Cell(cell, x+xpos+1, y+h-1); + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Store_Cell(cell, x, y+ypos+1); + cell.Character = CharData[thick].RightEdge; + Store_Cell(cell, x+w-1, y+ypos+1); + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Store_Cell(cell, x, y); + cell.Character = CharData[thick].UpperRight; + Store_Cell(cell, x+w-1, y); + cell.Character = CharData[thick].BottomRight; + Store_Cell(cell, x+w-1, y+h-1); + cell.Character = CharData[thick].BottomLeft; + Store_Cell(cell, x, y+h-1); + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ +#if(0) + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + _DX = CONTROL_PORT; + _AX = (short)(0x0E|(pos&0xFF00)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + _DX = CONTROL_PORT; + _AX = (short)(0x0F|(pos<<8)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + } +#else + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + output(CONTROL_PORT, + (short)0x0E|(short)(pos&0xFF00)); + output(CONTROL_PORT, + (short)0x0F|(short)(pos<<8)); + } +#endif +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + CellType cell; + + if (!Enabled) return; + + Set_Cursor(0, 0); + + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int y = 0; y < LINES; y++) { + for (int x = 0; x < COLUMNS; x++) { + Store_Cell(cell, x, y); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + CellType cell; + + if (!Enabled || lines <= 0) return; + + memmove( (void*)((long)MonoSegment + Offset(0, 0)), + (void*)((long)MonoSegment + Offset(0, lines)), + (LINES-lines)*COLUMNS*sizeof(CellType)); + + + Y--; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int l = LINES-lines; l < LINES; l++) { + for (int index = 0; index < COLUMNS; index++) { + Store_Cell(cell, index, l); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +#ifdef NEVER +void MonoClass::Printf(int text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} +#endif + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const *ptr) +{ + char startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; + while (*text) { + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (*text) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + X = startcol; + Y++; + Scroll(Y-(LINES-1)); + break; + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + X = 0; + Y++; + Scroll(Y-(LINES-1)); + break; + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge is it moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + cell.Character = *text; + Store_Cell(cell, X, Y); + + X++; + if (X >= COLUMNS) { + X = 0; + Y++; + + if (Y > (LINES-1)) { + Scroll(Y-(LINES-1)); + } + } + break; + + } + text++; + } + + Set_Cursor(X, Y); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + +#ifdef NEVER +void MonoClass::Text_Print(int text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + if (text != TXT_NONE) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} +#endif + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memcpy((void*)((long)MonoSegment + src.Offset(0, 0)), (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + MonoClass *displace; // The page that is being displaced. + + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + displace = Get_Current(); + if (displace) { + char temp[SIZE_OF_PAGE]; + + memcpy(&temp[0], MonoSegment, SIZE_OF_PAGE); + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + memcpy((void*)((long)MonoSegment + Offset(0, 0)), &temp[0], SIZE_OF_PAGE); + + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (char)attrib); + } +} + +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (char)attrib, (MonoClass::BoxStyleType)thick); + } +} + +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + + +#ifdef NEVER +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} +#endif + diff --git a/WIN32LIB/SRCDEBUG/MONOPRNT.CPP b/WIN32LIB/SRCDEBUG/MONOPRNT.CPP new file mode 100644 index 0000000..4bc1112 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MONOPRNT.CPP @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MONOPRNT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 12, 1993 * + * * + * Last Update : November 2, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mono_Printf -- Prints formated text to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include "mono.h" + +/*************************************************************************** + * Mono_Printf -- Prints formated text to the monochrome screen. * + * * + * This routine is used to print formatted text (similar to printf) to * + * the monochrome screen. * + * * + * INPUT: string -- The string that is to be printed. * + * * + * ... -- Any optional parameters that the string requires * + * to format properly. * + * * + * OUTPUT: Returns with the length of the string that it printed to the * + * monochrome screen. * + * * + * WARNINGS: The total length of the formatted string must not exceed * + * 255 bytes. * + * * + * HISTORY: * + * 11/02/1993 JLB : Created. * + *=========================================================================*/ +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + if (!MonoEnabled) return(0); + + va_start(va, string); + + vsprintf(buffer, string, va); + Mono_Print(buffer); + + va_end(va); + return(strlen(buffer)); +} diff --git a/WIN32LIB/SRCDEBUG/MORPHPAL.CPP b/WIN32LIB/SRCDEBUG/MORPHPAL.CPP new file mode 100644 index 0000000..1daffe5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MORPHPAL.CPP @@ -0,0 +1,176 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwlib32 * + * * + * File Name : PALTOPAL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : May 2, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Morph_Palette -- morphs a palette from source to destination * + * Palette_To_Palette -- morph src palette to a dst palette * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ +#define SCALE(a,b,c) (((((long)(a)<<8) / (long)(b) ) * (unsigned long)(c)) >>8) + + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE int __cdecl Palette_To_Palette(void *src_palette, void *dst_palette, unsigned long current_time, unsigned long delay); + + +/*************************************************************************** + * Morph_Palette -- morphs a palette from source to destination * + * * + * INPUT: * + * void *src_pal - starting palette * + * void *dst_pal - ending palette * + * unsigned int delay - time delay in 60ths of a second * + * void *callback - user-defined callback, NULL if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/02/1994 BR : Created. * + *=========================================================================*/ +void cdecl Morph_Palette (void *src_pal, void *dst_pal, unsigned int delay, + void (*callback) (void) ) +{ + int result; + unsigned long pal_start = TickCount.Time(); + extern void (*cb_ptr) ( void ) ; // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback ; + + /*===================================================================*/ + /* Make sure that we don't go too fast but also make sure we keep */ + /* processing the morph palette if we have one. */ + /*===================================================================*/ + while (1) { + if (src_pal && dst_pal) { + result = Palette_To_Palette (src_pal, dst_pal, + (TickCount.Time() - pal_start), (unsigned long)delay); + if (!result) + break; + + if (callback) { + (*cb_ptr)(); + } + } + } + + return; + +} /* end of Morph_Palette */ + + +/*************************************************************************** + * Palette_To_Palette -- morph src palette to a dst palette * + * * + * Creates & sets a palette that's in-between 'src_palette' & * + * 'dst_palette'; how close it is to dst_palette is based on how close * + * 'current_time' is to 'delay'. 'current_time' & 'delay' are based on * + * 0 being the start time. * + * * + * INPUT: void *src_palette = palette we want to morph from * + * void *dst_palette = palette we want to morph to * + * long current_time = time we started morph pal * + * long delay = time we want the morph to take* + * * + * OUTPUT: int if the time had elapsed and no chages were * + * necessary this routine returns FALSE * + * otherwise it will always return TRUE (this * + * was necessary to detect the end of the ice * + * effect. * + * * + * HISTORY: * + * 05/24/1993 MC : Created. * + *=========================================================================*/ +PRIVATE int cdecl Palette_To_Palette(void *src_palette, void *dst_palette, + unsigned long current_time, unsigned long delay) +{ + char colour; + char diff; + int chgval; + int lp; + int change; + static char palette[768]; + char *src_pal = (char*)src_palette; + char *dst_pal = (char*)dst_palette; + + /*======================================================================*/ + /* Loop through each RGB value attempting to change it to the correct */ + /* color. */ + /*======================================================================*/ + for (change = lp = 0; lp < 768; lp++) { + if (current_time < delay ) { + diff = dst_pal[lp] & (char)63; + diff -= src_pal[lp] & (char)63; + if (diff) + change = TRUE; + chgval = SCALE(diff, delay, current_time); + colour = src_pal[lp] & (char)63; + colour +=(char)chgval; + } + else { + colour = dst_pal[lp] & (char)63; + change = FALSE; + } + palette[lp] = colour; + } + /*======================================================================*/ + /* Set the palette to the color that we created. */ + /*======================================================================*/ + Set_Palette(palette); + return(change); + +} /* end of Palette_To_Palette */ + + +/*************************** End of morphpal.cpp ***************************/ + + diff --git a/WIN32LIB/SRCDEBUG/MOUSE.ASM b/WIN32LIB/SRCDEBUG/MOUSE.ASM new file mode 100644 index 0000000..56f628f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MOUSE.ASM @@ -0,0 +1,2246 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "drawbuff.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL C RealModePtr:DWORD +GLOBAL C MInstalled:DWORD +GLOBAL C MouseBuffer:DWORD +GLOBAL C LCW_Uncompress:NEAR + +GLOBAL C Get_Shape_Uncomp_Size :NEAR +GLOBAL C Get_Shape_Width :NEAR +GLOBAL C Get_Shape_Original_Height :NEAR +GLOBAL C Size_Of_Region :NEAR +GLOBAL C _ShapeBuffer :DWORD + +GLOBAL C XRes : dword +GLOBAL C YRes : dword + + +GLOBAL C VesaFunc : dword +GLOBAL C Vesa_XRes : near +GLOBAL C Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL C Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + +ifdef NOT_FOR_WIN95 + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + or cx,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: +endif ; NOT_FOR_WIN95 + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL C Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + +ifdef NOT_FOR_WIN95 + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: +endif ;NOT_FOR_WIN95 + + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + +ifdef NOT_FOR_WIN95 + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: +endif ;NOT_FOR_WIN95 + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL C Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR +ifdef NOT_FOR_WIN95 + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax +endif; NOT_FOR_WIN95 + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] +endif;NOT_FOR_WIN95 + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] +endif;ifdef NOT_FOR_WIN95 + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] +endif; NOT_FOR_WIN95 + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] +endif; NOT_FOR_WIN95 + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] +endif; NOT_FOR_WIN95 + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL C Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. +ifdef NOT_FOR_WIN95 + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] +endif; NOT_FOR_WIN95 + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi +ifdef NOT_FOR_WIN95 + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: +endif; NOT_FOR_WIN95 + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: +endif; NOT_FOR_WIN95 + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse +endif; NOT_FOR_WIN95 + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse +endif;ifdef NOT_FOR_WIN95 + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] +endif; NOT_FOR_WIN95 + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] +endif;ifdef NOT_FOR_WIN95 + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: +endif;ifdef NOT_FOR_WIN95 + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + +ifdef NOT_FOR_WIN95 + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: +endif; NOT_FOR_WIN95 + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword +ifdef NOT_FOR_WIN95 + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + +endif; NOT_FOR_WIN95 + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword +ifdef NOT_FOR_WIN95 + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + +endif; NOT_FOR_WIN95 + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/MOUSE.CPP b/WIN32LIB/SRCDEBUG/MOUSE.CPP new file mode 100644 index 0000000..3520afe --- /dev/null +++ b/WIN32LIB/SRCDEBUG/MOUSE.CPP @@ -0,0 +1,673 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mouse.h" +#include + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); +extern BOOL GameInFocus; + + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + InitializeCriticalSection (&MouseCriticalSection); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); + + /* + ** Force the windows mouse pointer to stay withing the graphic view port region + */ + Set_Cursor_Clip(); +} + +WWMouseClass::~WWMouseClass() +{ + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + } + timeEndPeriod (1000/60); + DeleteCriticalSection(&MouseCriticalSection); + + /* + ** Free up the windows mouse pointer movement + */ + Clear_Cursor_Clip(); +} + + +void Block_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Block_Mouse(buffer); + } +} + + +void Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Unblock_Mouse(buffer); + } +} + + + +void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + EnterCriticalSection(&MouseCriticalSection); + } +} + + +void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + LeaveCriticalSection(&MouseCriticalSection); + } +} + + + + + +void WWMouseClass::Set_Cursor_Clip(void) +{ + + if (Screen){ + RECT region; + + region.left = 0; + region.top = 0; + region.right = Screen->Get_Width(); + region.bottom = Screen->Get_Height(); + + ClipCursor(®ion); + } +} + + + +void WWMouseClass::Clear_Cursor_Clip(void) +{ + ClipCursor(NULL); +} + + + +void WWMouseClass::Process_Mouse(void) +{ + POINT pt; // define a structure to hold current cursor pos + + // + // If the mouse is currently hidden or it has not been installed, then we + // have no need to redraw the mouse. + // + if (!Screen || !_Mouse || State > 0 || MouseUpdate || EraseFlags || !GameInFocus) + return; + + // + // Make sure there are no conflicts with other + // threads that may try and lock the screen buffer + // + //Block_Mouse(Screen->Get_Graphic_Buffer()); + + // + // If the screen is already locked by another thread then just exit + // + if (Screen->Get_LockCount()!=0){ + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); + return; + } + + // + // Get the mouse's current real cursor position + // + GetCursorPos(&pt); // get the current cursor position + // + // If the mouse has moved then we are responsible to redraw the mouse + // + if (pt.x != MouseBuffX || pt.y != MouseBuffY) { + // + // If we can't lock the surface we need to draw to, we cannot update + // the mouse. + // + if (Screen->Lock()) { + // + // Erase the old mouse by dumping the mouses shadow buff + // to the screen (if its position had ever been recorded). + // + Low_Hide_Mouse(); + + // + // Verify that the mouse has not gone into a conditional hiden area + // If it has, mark it as being in one. + // + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + MCFlags |= CONDHIDDEN; + } + + // + // Show the mouse if we are allowed to. + // + if (!(MCFlags & CONDHIDDEN)) { + Low_Show_Mouse(pt.x, pt.y); + } + // + // Finally unlock the destination surface as we have sucessfully + // updated the mouse. + // + Screen->Unlock(); + } + } + + // + // Allow other threads to lock the screen again + // + //Unblock_Mouse(Screen->Get_Graphic_Buffer()); +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor) +{ + // + // If the pointer to the cursor we got is invalid, or its the same as the + // currently set cursor then just return. + if (!cursor || cursor == PrevCursor) + return(cursor); + + // + // Wait until we have exclusive access to our data + // + MouseUpdate++; + // + // Since we are updating the mouse we need to hide the cursor so we + // do not get some sort of weird transformation. + // + Hide_Mouse(); + // + // Now convert the shape to a mouse cursor with the given hotspots and + // set it as our current mouse. + // + void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor); + // + // Show the mouse which will force it to appear with the new shape we + // have assigned. + // + Show_Mouse(); + // + // We are done updating the mouse cursor so on to bigger and better things. + // + MouseUpdate--; + // + // Return the previous mouse cursor which as conveniantly passed back by + // Asm_Set_Mouse_Cursor. + // + return(retval); +} + +void WWMouseClass::Low_Hide_Mouse() +{ + if (!State) { + if (MouseBuffX != -1 || MouseBuffY != -1) { + if (Screen->Lock()){ + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + Screen->Unlock(); + } + } + MouseBuffX = -1; + MouseBuffY = -1; + } + State++; +} +void WWMouseClass::Hide_Mouse() +{ + MouseUpdate++; + Low_Hide_Mouse(); + MouseUpdate--; +} + + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + // + // If the mouse is already visible then just ignore the problem. + // + if (State == 0) return; + // + // Make the mouse a little bit more visible + // + State--; + + // + // If the mouse is completely visible then draw it at its current + // position. + // + if (!State) { + // + // Try to lock the screen til we sucessfully get a lock. + // + if (Screen->Lock()){ + // + // Save off the area behind the mouse. + // + Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, Screen, x, y); + // + // Save off the positions that we saved the buffer from + // + MouseBuffX = x; + MouseBuffY = y; + // + // Unlock the screen and lets get moving. + // + Screen->Unlock(); + } + } +} + +void WWMouseClass::Show_Mouse() +{ + POINT pt; + GetCursorPos(&pt); + + MouseUpdate++; + Low_Show_Mouse(pt.x, pt.y); + MouseUpdate--; +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) { + Low_Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; +} + + +void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr) +{ + POINT pt; + + if (State != 0) return; + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen - dont do video stuff if we cant. + // + if (scr->Lock()){ + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + } + + MouseUpdate--; +} + +void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced) +{ + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + if (scr->Lock()){ + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + scr->Unlock(); + } + EraseBuffX = -1; + EraseBuffY = -1; + } + } + MouseUpdate--; + EraseFlags = FALSE; +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.x); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + POINT pt; + GetCursorPos(&pt); + return(pt.y); +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + POINT pt; + + GetCursorPos(&pt); + x = pt.x; + y = pt.y; +} + +#pragma off(unreferenced) + +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + static BOOL in_mouse_callback = false; + + if (_Mouse && !in_mouse_callback) { + in_mouse_callback = TRUE; + _Mouse->Process_Mouse(); + in_mouse_callback = FALSE; + } +} +#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +int Get_Mouse_X(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_X()); +} + +int Get_Mouse_Y(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_Y()); +} diff --git a/WIN32LIB/SRCDEBUG/NEWDEL.CPP b/WIN32LIB/SRCDEBUG/NEWDEL.CPP new file mode 100644 index 0000000..289f815 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/NEWDEL.CPP @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : October 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WIN32LIB/SRCDEBUG/OLSOSDEC.ASM b/WIN32LIB/SRCDEBUG/OLSOSDEC.ASM new file mode 100644 index 0000000..71233f6 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/OLSOSDEC.ASM @@ -0,0 +1,755 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 diff --git a/WIN32LIB/SRCDEBUG/OPSYS.ASM b/WIN32LIB/SRCDEBUG/OPSYS.ASM new file mode 100644 index 0000000..f2bfeaa --- /dev/null +++ b/WIN32LIB/SRCDEBUG/OPSYS.ASM @@ -0,0 +1,150 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/opsys.asm 1.1 1994/04/18 09:14:12 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Operating System Flags * +;* * +;* File Name : OPSYS.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : January 26, 1993 * +;* * +;* Last Update : January 26, 1993 [SB] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Operating_System -- Determines what the operating system is. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Operating_System :NEAR +GLOBAL C OperatingSystem :WORD + +DOS equ 1 +WIN31STD equ 2 +WIN31ENH equ 3 +WIN30ENH equ 4 +WIN30STD equ 5 +WIN30REAL equ 6 + +DATASEG + +OperatingSystem dw 0 + + +CODESEG + +;*************************************************************************** +;* Operating_System -- Determines what the operating system is. * +;* * +;* INPUT: NONE. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/26/1993 SB : Created. * +;*=========================================================================* +PROC Operating_System C near + USES ebx,ecx,edx,es,edi + + + ; Check for Windows 3.1 + mov eax,160Ah ; WIN31CHECK + int 2fh + or ax,ax + jz short RunningUnderWin31 + + ;check for Windows 3.0 enhanced mode + mov eax,1600h ; WIN386CHECK + int 2fh + mov bl,al + mov eax,WIN30ENH + test bl,7fh + jnz short Exit + + ;check for 3.0 WINOLDAP + mov eax,4680h ; IS_WINOLDAP_ACTIVE + int 2fh + or eax,eax + jnz short NotRunningUnderWin + + ; rule out MS-DOS 5.0 task switcher + mov eax,4b02h ; detect switcher + push ebx + push es + push edi + xor ebx,ebx + mov edi,ebx + mov es,bx + int 2fh + pop edi + pop es + pop ebx + or eax,eax + jz short NotRunningUnderWin ; MS-DOS 5.0 task switcher found. + + ; check for standrd mode Windows 3.0 + mov eax,1605h ;PMODE_START + int 2fh + mov eax,WIN30STD + cmp ecx,-1 + jz short Exit + + ;check for real mode Windows 3.0 + mov eax,1606h ; PMODE_STOP + int 2fh + mov eax,WIN30REAL + jmp SHORT Exit + +RunningUnderWin31: + ; At this point: CX == 3 means Windows 3.1 enhanced mode. + ; CX == 2 means Windows 3.1 standard mode. + mov eax,WIN31STD + cmp ecx,2 + je short Exit + + mov eax,WIN31ENH + jmp SHORT Exit + +NotRunningUnderWin: + mov eax,DOS + +Exit: + mov [WORD PTR OperatingSystem], ax + ret + +ENDP Operating_System + + + +;---------------------------------------------------------------------------- + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/PACK2PLN.ASM b/WIN32LIB/SRCDEBUG/PACK2PLN.ASM new file mode 100644 index 0000000..59ffa71 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PACK2PLN.ASM @@ -0,0 +1,132 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/file/rcs/pack2pln.asm 1.1 1994/04/22 18:07:46 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : PACK2PLN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : November 20, 1991 * +;* * +;* Last Update : April 22, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Pack_2_Plane:NEAR + + CODESEG + +;*************************************************************************** +;* PACK_2_PLANE -- packed to planar scanline conversion * +;* * +;* INPUT: BYTE *buffer (far) -- pointer to planar output buffer * +;* BYTE *pageptr (far) -- pointer to current row in packed page * +;* WORD planebit -- current bit used in plane -- use only low byte * +;* * +;* OUTPUT: * +;* Return result in buffer. * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 11/20/1991 SB : Created. * +;* 04/22/1994 SKB : Converted to 32 bit library. * +;*=========================================================================* +; * +; This is the original function that is converted to asm +; +;PRIVATE VOID Pack_2_Plane(UBYTE * buffer, BYTE * pageptr, BYTE planebit) +;{ +; WORD currbit=0x80; // current bit to be written to +; WORD pixel; // current pixel in row used as a counter; +; +; buffer--; // will be incremented at the start +; for (currbit = 0, pixel = 0; pixel < 320; pixel++) { +; if (!currbit) { +; currbit = 0x80; // reset bit 7 +; buffer++; // go to next byte in buffer +; *buffer = 0; // clear byte so we only need to set bits needed +; } +; if (planebit & *pageptr++) +; *buffer |= currbit; // set bit in destination if plane was set is source +; +; currbit >>= 1; // shift destination bit one right +; } +;} + +PROC Pack_2_Plane C NEAR + USES ebx,ecx,esi,edi + ARG buffer:DWORD + ARG page:DWORD + ARG planebit:WORD + + + mov edi,[buffer] + mov esi,[page] + + mov ax,[planebit] ; move bit set for current plane (planebit) to ax + ; the low byte will only be used + + mov ecx,320d ; set counter to 320 columns (320x200 picture) + mov ah,80h ; set bit 7 of current_bit + dec edi ; this will get incremented at the start + +??top_loop: ; while (columns left) + cmp ah,80h ; if current_bit is bit 7 + jnz short ??same_dest + ; Then + inc edi ; buffer++ increment pointer + mov [BYTE PTR edi],0 ; *buffer = 0 + +??same_dest: ; EndIf + mov bl,al + and bl,[esi] ; if (planebit & *pageptr) + jz short ??no_set_bit + + or [BYTE PTR edi],ah ; Then *buffer |= current_bit + +??no_set_bit: + inc esi ; pageptr++ goto next in source byte + ror ah,1 ; rotate current_bit right one + dec ecx ; + jnz ??top_loop + + ret + + ENDP Pack_2_Plane + + END + \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/PAGFAULT.ASM b/WIN32LIB/SRCDEBUG/PAGFAULT.ASM new file mode 100644 index 0000000..ed1f4bb --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PAGFAULT.ASM @@ -0,0 +1,152 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL C Install_Page_Fault_Handle : NEAR +GLOBAL C Set_Video_Mode : NEAR +GLOBAL C Remove_Mouse : NEAR +GLOBAL C Remove_Keyboard_Interrupt : NEAR +GLOBAL C Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END diff --git a/WIN32LIB/SRCDEBUG/PAL.ASM b/WIN32LIB/SRCDEBUG/PAL.ASM new file mode 100644 index 0000000..ec29427 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PAL.ASM @@ -0,0 +1,410 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;include "keyboard.inc" +FALSE = 0 +TRUE = 1 + +;****************************** Declarations ******************************** +GLOBAL C Set_Palette_Range:NEAR +GLOBAL C Bump_Color:NEAR +GLOBAL C CurrentPalette:BYTE:768 +GLOBAL C PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG + + + +IF 1 +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* + + PROC Set_Palette_Range C NEAR + ARG palette:DWORD + + GLOBAL Set_DD_Palette_:near + GLOBAL Wait_Vert_Blank_:near + + pushad + mov esi,[palette] + mov ecx,768/4 + mov edi,offset CurrentPalette + cld + rep movsd + ;call Wait_Vert_Blank_ + mov eax,[palette] + call Set_DD_Palette_ + popad + ret + + +ifdef NOT_FOR_WIN95 + USES eax,ebx,ecx,edx,edi,esi + + cld + + ;*=================================================================*/ + ;* Set up pointers to begin making palette comparison */ + ;*=================================================================*/ + mov esi, [palette] + mov edi, OFFSET CurrentPalette + mov ebx, OFFSET PaletteTable + mov ecx, 0 + +??loop_top: + mov eax,[esi] ; read a dword from palette source + mov edx,[edi] ; read a dword from compare palette + and eax,00FFFFFFh ; palette entrys are only 3 bytes + and edx,00FFFFFFh ; long so and of extra + cmp eax,edx ; if they are not the same then + jne ??set_table ; add them into the table + add esi,3 + add edi,3 + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + jmp ??set_pal ; so now go set the palette +??set_table: + shl eax,8 ; shift bgr value up register + mov al,cl ; store which palette entry num + mov [ebx],eax + add ebx,4 + movsw ; copy the three gun values into + movsb ; the shadow palette. Use movsb + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + +??set_pal: + mov esi,ebx + mov ebx,OFFSET PaletteTable + sub esi,ebx ; if ebx didn't change there + jz ??exit ; is nothing to set + shr esi,2 ; find how many entrys + + mov eax,[ebx] + + movzx ecx,al ; we are currently on entry 0 + add ebx,4 + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + + push ebx + mov bx,[VertBlank] + and bl,001h + shl bl,3 + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + pop ebx + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + cli +??loop: + shr eax,8 ; shift down the red gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write the green value to video card + jmp $ + 2 ; force cache to flush, to create a time + inc ecx ; move edx to next palette entry + + mov eax,[ebx] ; get next value to set + add ebx,4 ; and post increment the palette value + cmp al,cl ; check if DAC position already correct + je ??correct_pos + + mov edx,03C8h ; Tell DAC of the color gun to start setting. + out dx,al ; First color set. + mov dx,03C9h + +??correct_pos: + dec esi + jnz ??loop + sti +;***************** Time Critical Section End ******************* +??exit: + ret +endif ;NOT_FOR_WIN95 + + ENDP Set_Palette_Range +ELSE +;*************************************************************************** +;* Set_Palette_Range -- Sets changed values in the palette. * +;* * +;* INPUT: * +;* VOID *palette - pointer to the new palette. * +;* * +;* OUTPUT: * +;* none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/25/1994 SKB : Created. * +;* 04/27/1994 BR : Converted to 32-bit * +;*=========================================================================* +; VOID cdecl Set_Palette_Range(VOID *palette); + PROC Set_Palette_Range C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG palette:DWORD + LOCAL remain:DWORD ; 32-bit: converted to LONG + + cld + + mov bx,[VertBlank] + and bl,001h + shl bl,3 + + ; Make a copy of the palette passed in. + mov edi,OFFSET CurrentPalette + mov esi,[palette] + mov [remain],768 + + ; Search for differences between the current palette and the + ; new palette. When a difference is found, output a block + ; of color registers and keep scanning. +??bodyloop: + mov ecx,[remain] + + repe cmpsb ; Search for differences. + je short ??exit + dec esi + dec edi + inc ecx + + mov edx,0 ; clear EDX + mov eax,ecx + mov ecx,3 + div ecx ; EAX = # of colors to set, EDX = Fraction. + or edx,edx + jz short ??nofrac + neg edx + add edx,3 ; Back offset skip needed. + inc eax ; Fractional color rounds up to whole color to set. +??nofrac: + + ; Set CX to be the number of color guns to set. + mov ecx,eax ; Colors * 3 bytes per color. + add ecx,eax + add ecx,eax + + ; Chop this DAC dump short if necessary in order to reduce + ; sparkling. + mov [remain],0 + cmp ecx,86*3 ; Number of color guns to set per vert retrace + jbe short ??ok + sub ecx,86*3 + mov [remain],ecx + mov ecx,86*3 +??ok: + + ; Adjust the palette offsets back to point to the RED color gun. + sub esi,edx + sub edi,edx + + ; Determine the color number to start setting. + neg eax + add eax,256 ; AX = Color to start setting (0..255). + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + +;??wait: +; in al,dx +; test al,01000b +; jnz ??wait + +;??retrace: +; in al,dx +; test al,01000b +; jz ??retrace + + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + pushf + cli +??loop: + lodsb + stosb + out dx,al + jmp $ + 2 ; force cache to flush, to create a time + ; delay to give DAC time to get value + loop ??loop + popf +;***************** Time Critical Section End ******************* + + cmp [remain],0 + jnz ??bodyloop + +??exit: + ret + + ENDP Set_Palette_Range +ENDIF + + + +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. +??colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short ??gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short ??gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] +??gotit: + inc edi + inc esi + loop ??colorloop + + mov ax,[changed] + ret + + ENDP Bump_Color + + END + +;*************************** End of pal.asm ******************************** + diff --git a/WIN32LIB/SRCDEBUG/PALETTE.CPP b/WIN32LIB/SRCDEBUG/PALETTE.CPP new file mode 100644 index 0000000..8125eed --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PALETTE.CPP @@ -0,0 +1,382 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate); +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * void *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette(void *palette) +{ + + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * void *palette - palette to set color in * + * int color - which color index to set * + * void *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette_Color(void *palette, int color, void *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(unsigned short*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * char *palette1 - this is the palette to fade to. * + * unsigned int delay - fade with this timer count down * + * void *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + short jump; // Gun values to jump per palette set. + unsigned long timer; // Tick count timer used for timing. + short ticksper; // The ticks (fixed point) per bit jump. + int tickaccum; + + + extern void (*cb_ptr)(void); // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * unsigned char *palette - palette to fade to * + * int delay - desired time delay in 60ths of a second * + * short *ticks - output: loop ticks per color jump * + * short *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, + short *rate) +{ + int gun1; // Palette 1 gun value. + int gun2; // Palette 2 gun value. + int diff; // Maximum color gun difference. + int tp; // Temporary tick accumulator. + int index; // Color gun working index. + long t; // Working tick intermediate value. + int adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((long)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (short)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + int index; // Index to DAC register gun. + int gun1,gun2; // Palette 1 gun value. + unsigned char palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = (unsigned char)gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +void (*cb_ptr)(void); // callback function pointer + +/**************************** End of palette.cpp ***************************/ + + + diff --git a/WIN32LIB/SRCDEBUG/PLAYCD.ASM b/WIN32LIB/SRCDEBUG/PLAYCD.ASM new file mode 100644 index 0000000..d6e054c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PLAYCD.ASM @@ -0,0 +1,289 @@ +; +; 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 . +; + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + + +DPMI_INTR equ 031h + +GLOBAL DPMI_real_alloc : near +GLOBAL DPMI_real_free : near +GLOBAL DPMI_real_intr : near +GLOBAL DPMI_real_call : near + + +STRUC SEGSEL + segmen dw ? + select dw ? +ENDS + +STRUC REGS + _eax dd ? + _ebx dd ? + _ecx dd ? + _edx dd ? + _esi dd ? + _edi dd ? + _cflag dd ? +ENDS + +STRUC SREGS + _es dw ? + _cs dw ? + _ss dw ? + _ds dw ? + _fs dw ? + _gs dw ? +ENDS + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + size_ref db ? +ENDS + + +CODESEG + +; int DPMI_real_alloc ( UINT , SEGREG * , USHORT * ) ; + +PROC DPMI_real_alloc C near + USES ebx , edx + ARG paragra : DWORD + ARG blk_segptr : DWORD + ARG largest_blkptr : DWORD + + mov eax, 0100h + mov ebx, [paragra] + int DPMI_INTR + + jnc ??dpmi_succed + mov ebx, [largest_blkptr] + mov [ word ptr ebx ] , bx + movzx eax , al + ret + +??dpmi_succed: + mov ebx, [blk_segptr] + mov [(type SEGSEL ptr ebx). segmen ] , ax + mov [(type SEGSEL ptr ebx). select ] , dx + xor eax , eax + ret + +ENDP DPMI_real_alloc + + +;************************************************************************** +; int DPMI_real_free ( UINT ) ; + +PROC DPMI_real_free C near + USES eax , edx + ARG blk_selec : DWORD + + mov eax, 0101h + mov edx, [blk_selec] + shr edx , 16 + int DPMI_INTR + ret +ENDP DPMI_real_free + + +PROC DPMI_real_intr C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + lea edi , [ regblk ] + xor eax , eax + lea ecx , [ regblk . size_ref ] + sub ecx , edi + shr ecx , 2 + rep stosd + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + + mov eax , 0300h + mov ebx , [ vector ] + xor bh , bh + xor ecx , ecx + lea edi , [ regblk ] + + int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_intr + + +PROC DPMI_real_call C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + + lea edi , [ regblk ] + xor al , al + lea ecx , [ regblk . size_ref ] + sub ecx , edi + rep movsb + + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + +; mov eax , 0300h +; mov ebx , [ vector ] +; xor bh , bh +; xor ecx , ecx +; lea edi , [ regblk ] + +; int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_call + +END + + + diff --git a/WIN32LIB/SRCDEBUG/PRIOINIT.CPP b/WIN32LIB/SRCDEBUG/PRIOINIT.CPP new file mode 100644 index 0000000..d69706c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PRIOINIT.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : PRIOINIT.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : August 9, 1993 * + * * + * Last Update : August 9, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * INIT_PRIORITY_SYSTEM -- Sets the buffer addresses for the priority * + * system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "shape.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Init_Priority_System -- set the buffer addresses for the priority * + * system. * + * * + * INPUT: * + * mask - pointer to priority buffer class * + * back - pointer to background buffer class * + * * + * OUTPUT: * + * none * + * * + * HISTORY: * + * 08/09/1994 IML : Created. * + *=========================================================================*/ + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back) +{ + MaskPage = mask->Get_Buffer(); + BackGroundPage = back->Get_Buffer(); +} + + +/************************** end of prioinit.cpp ****************************/ diff --git a/WIN32LIB/SRCDEBUG/PROFILE.CPP b/WIN32LIB/SRCDEBUG/PROFILE.CPP new file mode 100644 index 0000000..f9e11a5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PROFILE.CPP @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second. * + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * Use * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Start_Profiler -- initialises the profiler data and starts gathering data * + * Stop_Profiler -- stops the timer and writes the profile data to disk * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include + +#include +#include +#include +#include "profile.h" + +extern "C"{ +unsigned ProfileList [PROFILE_RATE*60*MAX_PROFILE_TIME]; +unsigned ProfilePtr; +} + +extern "C" void Profiler_Callback ( UINT, UINT , DWORD, DWORD, DWORD ); + +unsigned ProfilerEvent; + +void Start_Profiler (void) +{ + memset (&ProfileList[0],-1,PROFILE_RATE*60*MAX_PROFILE_TIME*4); + Copy_CHK(); + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , (void CALLBACK (UINT,UINT,DWORD,DWORD,DWORD))Profiler_Callback , 0 , TIME_PERIODIC); +} + +void Stop_Profiler (void) +{ + if (ProfilerEvent){ + timeKillEvent(ProfilerEvent); + ProfilerEvent=NULL; + + int handle = Open_File ( "profile.bin" , WRITE ); + if (handle != WW_ERROR){ + Write_File (handle , &ProfileList[0] , ProfilePtr*4); + Close_File (handle); + } + } +} + + diff --git a/WIN32LIB/SRCDEBUG/PUTPIX.ASM b/WIN32LIB/SRCDEBUG/PUTPIX.ASM new file mode 100644 index 0000000..cd41819 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/PUTPIX.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : GraphicViewPortClass * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Put_Pixel C near + USES eax,ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + add edx,[(GraphicViewPort ebx).GVPPitch] ; add in direct draw pitch + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen +??exit: + ret + ENDP Buffer_Put_Pixel + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/RANDOM.ASM b/WIN32LIB/SRCDEBUG/RANDOM.ASM new file mode 100644 index 0000000..87170e9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/RANDOM.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : RANDOM.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* UBYTE Random(VOID); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +Global C Random :NEAR +Global C Get_Random_Mask :NEAR +Global C RandNumb :DWORD + + DATASEG + +RandNumb DD 12349876H + + CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END diff --git a/WIN32LIB/SRCDEBUG/RAWFILE.CPP b/WIN32LIB/SRCDEBUG/RAWFILE.CPP new file mode 100644 index 0000000..e1823e0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/RAWFILE.CPP @@ -0,0 +1,1303 @@ +/* +** 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 . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.cpv 2.17 06 Sep 1995 16:38:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** 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 : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + #define true 1 + #define false 0 + +#define WIN32 +#define _WIN32 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "wwlib32.h" +//#include "compat.h" +#include "rawfile.h" + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +#pragma argsused +//void RawFileClass::Error(int error, int canretry, char const * filename) +void RawFileClass::Error(int error, int , char const *) +{ + char buffer[256]; + + wsprintf(buffer, "File Error #%d", error); +/////// MessageBox(NULL, buffer, "Error", MB_OK); + +#ifdef NEVER + char message[256]; // Staging buffer for error message string. + + /* + ** Build the complete text of the error message. This text is used in either the graphic + ** mode or the text mode version. + */ +#ifdef GERMAN + strcpy(message, "DATEIFEHLER"); +#else +#ifdef FRENCH + strcpy(message, "ERREUR DE FICHIER"); +#else + strcpy(message, "FILE ERROR"); +#endif +#endif + if (filename) { + strcat(message, "("); + strcat(message, filename); + strcat(message, ")"); + } + strcat(message, ": "); +//BG: Borland only strcat(message, _sys_errlist[error]); + strcat(message, strerror(error) ); + strcat(message, ". "); + + /* + ** If it can't properly handle displaying the error message in the + ** current graphic mode, then this forces the error to become non + ** recoverable. Go into text mode and proceed. + */ + if (!FontPtr && GraphicMode != TXT_MODE) { + Set_Video_Mode(RESET_MODE); + canretry = false; + GraphicMode = TXT_MODE; + } + + /* + ** Add the text explaining the valid actions to take. + */ + if (canretry) { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken fr erneuten Versuch."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour recommencer."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Appuyez sur Echap pour quitter le programme."); +#else + strcat(message, " Press any key to retry."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Press to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } else { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour quitter le programme."); +#else + strcat(message, " Press any key to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } + + /* + ** In text mode, the error handler is very simple. It just prints the error message + ** to the screen and waits for a response. + */ + if (GraphicMode == TXT_MODE) { + int input; + + /* + ** Display the error message and wait for a response. + */ + printf(message); + Keyboard::Clear(); + input = Keyboard::Get(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + } else { + + /* + ** The graphic mode version of the error handler will display a simple message + ** box on the screen. If the palette is black at this point, then the error will + ** be invisible. For more thorough and pleasing results, you should replace this + ** virtual function with one of your own, that is more aware of the environment + ** in which is exists. + */ + void *background; // Pointer to background saving buffer. + GraphicBufferClass * oldpage; // Copy of old logic page. + int oldwindow; // Copy of old window number. + void const *oldfont; // Copy of old font pointer. + int oldspacing; // Old font X spacing. + + /* + ** Setup display in preparation for printing the error message. + */ + oldpage = Set_Logic_Page(SeenBuff); + oldwindow = Change_Window(ErrorWindow); + oldfont = Set_Font(FontPtr); + oldspacing = FontXSpacing; FontXSpacing = 0; + Hide_Mouse(); + + /* + ** Try to allocate a storage buffer for the background to the + ** error window. + */ + background = new char [Size_Of_Region(WinW<<3, WinH)]; + + /* + ** If there is memory for the background storage, then save the + ** screen image area to that buffer. + */ + if (background) { + SeenPage.To_Buffer(WinX<<3, WinY, WinW<<3, WinH, background, Size_Of_Region(WinW<<3, WinH)); + } + + /* + ** Draw a rudimentary box. + */ + New_Window(); + LogicPage->Draw_Rect(WinX<<3, WinY, (WinX+WinW)<<3, WinY+WinH, WinC); + + /* + ** shrinks window down one byte in all directions. + */ + WindowList[Window][WINDOWX] += 1; + WindowList[Window][WINDOWY] += 1<<3; + WindowList[Window][WINDOWWIDTH] -= 1<<1; + WindowList[Window][WINDOWHEIGHT] -= 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + Window_Print(message); + Keyboard::Clear(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + int input = Keyboard::Get(); + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + /* + ** Restore the window back to its original size. + */ + WindowList[Window][WINDOWX] -= 1; + WindowList[Window][WINDOWY] -= 1<<3; + WindowList[Window][WINDOWWIDTH] += 1<<1; + WindowList[Window][WINDOWHEIGHT] += 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + /* + ** If the background was saved off, then restore it. + */ + if (background) { + Buffer_To_Page(WinX<<3, WinY, WinW<<3, WinH, background, SeenPage); + delete [] background; + } + + /* + ** Restore the system global settings to original values. + */ + Show_Mouse(); + Change_Window(oldwindow); + Set_Font(oldfont); + Set_Logic_Page(oldpage); + FontXSpacing = oldspacing; + } +#endif +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const *filename) : Filename(filename) +{ + Handle = -1; + Allocated = false; +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const *filename) +{ + if (Filename && Allocated) { +// Heap_Dump_Check( "Before raw free" ); + free((char *)Filename); +// Heap_Dump_Check( "After raw free" ); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (!filename) return(NULL); + +// Heap_Dump_Check( "Before raw strdup" ); + ((char *&)Filename) = strdup(filename); +// Heap_Dump_Check( "After raw strdup" ); + if (!Filename) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const *filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetatively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ +// Hard_Error_Occured = 0; + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_READ); + //Handle = open(Filename, O_RDONLY|O_BINARY); + break; + + case WRITE: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_WRITE | MMIO_CREATE); + //Handle = open(Filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY , S_IWRITE); + break; + + case READ|WRITE: + Handle = (int)mmioOpen((char*)Filename, NULL, MMIO_READWRITE); + //Handle = open(Filename, O_RDWR|O_CREAT|O_BINARY); + break; + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == 0) { + +// Error(errno, false, Filename); //this kills windoze!!!! ST - 9/28/95 5:33PM +#ifdef NEVER + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } +#endif + continue; + } + break; + } + return(true); +} + + + + +void RawFileClass::Set_Buffer_Size (int size) +{ + if (Handle){ + mmioSetBuffer((HMMIO)Handle, NULL, size, 0); + } +} + + + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ + int file; // Working file handle. + int open_failed; + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +// Hard_Error_Occured = 0; + file = open(Filename, O_RDONLY|O_BINARY); + open_failed = (file == -1); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } +// } + if (!open_failed) break; + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ + if (close(file)) { + Error(errno, false, Filename); + } + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ +// Hard_Error_Occured = 0; + if (mmioClose((HMMIO)Handle, 0)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } + break; + } + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = -1; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void *buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + int readresult; + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size; + +// Hard_Error_Occured = 0; + readresult = 0; + actual = mmioRead((HMMIO)Handle, (char*)buffer, desired); + //actual = read(Handle, buffer, desired); + if (actual != desired) readresult = errno; + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; // Not technically needed, but to be consistent... +// } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + }// else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (void *) ((long)buffer + actual); + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + //} +// } + } + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const *buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + int writeresult; + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + +// Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = 0; + actual = mmioWrite((HMMIO)Handle, (char*)buffer, desired); + //actual = write(Handle, buffer, desired); + if (actual != desired) writeresult = errno; + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; // Not technically needed, but to be consistent... +// } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (void *)((long)buffer + actual); + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } +// } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ +// Hard_Error_Occured = 0; + pos = mmioSeek((HMMIO)Handle, pos, dir); + //pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } +// } + break; + } + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + int handle = 0; + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { +// Hard_Error_Occured = 0; + + handle = open(Filename, O_RDONLY|O_BINARY); + if (handle > 0){ + size = filelength(handle); + close(handle); + } + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } else { + if (size == -1) { + Error(errno, false, Filename); + } +// } + break; + } + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + return(size); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +// Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ +// if (Hard_Error_Occured) { +// Error(Hard_Error_Occured, true, Filename); +// continue; +// } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + + + + + +//extern "C" { + +#define MAX_HANDLES 10 +static RawFileClass Handles[MAX_HANDLES]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(BYTE const *) +{ + RawFileClass::Set_Search_Path(path); + return(true); +} +#endif + + + +int __cdecl Open_File(char const *file_name, int mode) +{ + for (int index = 0; index < MAX_HANDLES; index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name,mode)) { + return(index); + } + break; + } + } + return(WW_ERROR); +} + +VOID __cdecl Close_File(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(int handle, VOID *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(int handle, VOID const *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const *file_name) +{ + RawFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(BYTE const *file_name) +{ + return(RawFileClass(file_name).Delete()); +} + +int __cdecl Create_File(BYTE const *file_name) +{ + return(RawFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size) +{ + return(RawFileClass(name).Read(ptr, size)); +} + +VOID * __cdecl Load_Alloc_Data(char const *name, int ) +{ + RawFileClass file(name); + + return(Load_Alloc_Data(file)); +} +#endif + +ULONG __cdecl File_Size(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(BYTE const *name, VOID const *ptr, ULONG size) +{ + return(RawFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(int handle, LONG offset, int starting) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(RawFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +int __cdecl Find_Disk_Number(BYTE const *) +{ + return(0); +} +#endif + + + + +/*********************************************************************************************** + * Load_File -- load an entire file into memory * + * * + * * + * * + * INPUT: File name * + * Load address * + * * + * OUTPUT: bytes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/28/95 5:09PM ST : Created * + *=============================================================================================*/ + +int __cdecl Load_File ( const char *file_name , void *load_addr ) +{ + int bytes_read=0; + int handle; + + handle=Open_File ( file_name , READ ); + + if ( handle>=0 ){ + bytes_read = Read_File ( handle , load_addr , File_Size ( handle ) ); + Close_File ( handle ); + } + return ( bytes_read ); +} + + + + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(RawFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(RawFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} + + +void Unfragment_File_Cache(void) +{ +} diff --git a/WIN32LIB/SRCDEBUG/REDBOOK.CPP b/WIN32LIB/SRCDEBUG/REDBOOK.CPP new file mode 100644 index 0000000..d85db1c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/REDBOOK.CPP @@ -0,0 +1,518 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : REDBOOK.CPP * + * * + * Programmer : STEVE WETHERILL (FROM SCOTT BOWEN CODE) * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * RedBookClass::~RedBookClass(VOID) * + * RedBookClass::RedToHS(ULONG i) * + * RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) * + * RedBookClass::FullCDVolume(UBYTE chan) * + * RedBookClass::PlayTrack(UWORD track) * + * RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) * + * RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, * + * UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) * + * RedBookClass::CheckCDMusic(VOID) * + * RedBookClass::StopCDMusic(VOID) * + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * RedBookClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * calls GetCDDrive() * + * HISTORY: * + * 05/25/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::RedBookClass(VOID) + : GetCDClass() // call the base constructor + +{ + SEGSEL tmpadr ; + + tmpadr = cdDrive_addrp; + memset ( this , 0 , sizeof ( RedBookClass ) ) ; + cdDrive_addrp = tmpadr ; + + Stop.Length = 13; + Stop.Command = 133; + + Tinfo.Length = 26; + Tinfo.Command = 3; + Tinfo.CntTrns = 7; + Tinfo.TrInfo = 11; + + Play.Length = 22; + Play.Command = 132; + Play.AddrMd = 1; + + Volm.Length = 26; + Volm.Command = 12; + Volm.CntTrns = 9; + Volm.TrInfo = 3; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + + Stat.Length = 26; + Stat.Command = 3; + Stat.CntTrns = 11; + Stat.StatInfo = 15; + + if (DPMI_real_alloc(sizeof(TinfoType)/16+1, &Tinfo_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StatType)/16+1, &Stat_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(VolmType)/16+1, &Volm_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(PlayType)/16+1, &Play_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StopType)/16+1, &Stop_addrp, &largestp)) + exit(1); + + GetCDDrive(); + +} + +/*************************************************************************** + * REDBOOKCLASS -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::~RedBookClass(VOID) +{ + if(Tinfo_addrp.seg) + DPMI_real_free(Tinfo_addrp); // free up those conventional buffers + + if(Stat_addrp.seg) + DPMI_real_free(Stat_addrp); + + if(Volm_addrp.seg) + DPMI_real_free(Volm_addrp); + + if(Play_addrp.seg) + DPMI_real_free(Play_addrp); + + if(Stop_addrp.seg) + DPMI_real_free(Stop_addrp); +} + +/*************************************************************************** + * REDTOHS -- RedBook to High-Sierra conversion * + * * + * * + * * + * INPUT: * + * ULONG * + * OUTPUT: * + * ULONG * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::RedToHS(ULONG i) +{ + return( ((i>>16) & 0xFF) * 60 * 75) + ( ((i>>8) & 0xFF) * 75) + (i & 0xFF); +} + +/*************************************************************************** + * MSFTORED -- Minute, Second, Frame to RedBook conversion * + * * + * * + * * + * INPUT: * + * UBYTE minute * + * UBYTE second * + * UBYTE frame * + * OUTPUT: * + * ULONG RedBook * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) +{ + return( ((ULONG)m << 16) + ((ULONG)s << 8) + (ULONG)f ); +} + +/*************************************************************************** + * FULLCDVOLUME -- set full volume * + * * + * * + * * + * INPUT: * + * UBYTE channel * + * * + * CHLEFT * + * CHRIGHT * + * CHBOTH * + * * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::FullCDVolume(UBYTE chan) +{ + + Volm.Vol0 = Volm.Vol1 = Volm.Vol2 = Volm.Vol3 = 255; + + Volm.TrnsAdOff = offsetof (VolmType, TrInfo); + Volm.TrnsAdSeg = Volm_addrp.seg; + + if(chan == CHLEFT) + { +// Volm.In0 = 0; +// Volm.In1 = 3; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol1 = 0; + } + else if(chan == CHRIGHT) + { +// Volm.In0 = 3; +// Volm.In1 = 1; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol0 = 0; + } + else /* both channels */ + { + Volm.In0 = 0; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + } + +// WriteRealMem(REALPTR(Volm_addrp) << 16, &Volm, sizeof(VolmType)); + Mem_Copy ( &Volm , (void *) ( Volm_addrp.seg << 4 ) , sizeof(VolmType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Volm_addrp.seg; + + DPMI_real_intr(0x2F, ®s, & sregs); + +// ReadRealMem(&Volm, REALPTR(Volm_addrp) << 16, sizeof(VolmType)); + Mem_Copy ( (void *) ( Volm_addrp . seg << 4 ), &Volm ,sizeof(VolmType)); +} + + + +/*************************************************************************** + * PLAYTRACK -- play a track * + * * + * * + * * + * INPUT: * + * UWORD track * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::PlayTrack(UWORD track) +{ + + StopCDMusic(); + + Tinfo.Track = track; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); // gets start time of track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + + Play.Start = Tinfo.Start; + Tinfo.Track++; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s , &sregs); // gets start time of following track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + Play.CntSect = RedToHS(Tinfo.Start) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + + FullCDVolume(CHBOTH); +} + + +/*************************************************************************** + * PLAY_CD_MSL -- play cd from start min_sec for len * + * * + * * + * * + * INPUT: * + * UWORD min_sec * + * UWORD Len * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) +{ + UWORD startM, startS, startF; + UWORD endM, endS, endF; + + if (!len) + return; + + endM = startM = (min_sec >> 8) + AUDIO_START_MIN; + endS = startS = (min_sec & 0xFF) + AUDIO_START_SEC; + startF = endF = 0; + + while (len > 59) { + endM++; + len -= 60; + } + + endS += len; + if (endS > 59) { + endM++; + endS -= 60; + } + + PlayMSF((UBYTE) startM, (UBYTE)startS, (UBYTE)startF, (UBYTE)endM, (UBYTE)endS, (UBYTE)endF, 2 /* chan */); +} + + +/*************************************************************************** + * PlayMSF -- Play Minute, Second, Frame to Minute, Second, Frame * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) +{ + + Play.Start = MSFtoRed(startM, startS, startF); + Play.CntSect = RedToHS(MSFtoRed(endM, endS, endF)) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (PlayType, Length); + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + FullCDVolume(chan); + +} + +/*************************************************************************** + * CheckCDMusic -- Check for CD playing * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * UWORD TRUE if playing else FALSE * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +UWORD RedBookClass::CheckCDMusic(VOID) +{ + + Stat.TrnsAdOff = offsetof (StatType, StatInfo); + Stat.TrnsAdSeg = Stat_addrp.seg; + +// WriteRealMem(REALPTR(Stat_addrp) << 16, &Stat, sizeof(StatType)); + Mem_Copy ( &Stat , (void *) ( Stat_addrp.seg << 4 ) , sizeof(StatType)); + + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StatType, Length); + regs.x.eax = 0x1510; + sregs.es = Stat_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stat, REALPTR(Stat_addrp) << 16, sizeof(StatType)); + Mem_Copy ( (void *) ( Stat_addrp.seg << 4 ) , &Stat, sizeof(StatType)); + + return (Stat.Status&0x200); +} + +/*************************************************************************** + * STOPCDMUSIC -- stop CD playing * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::StopCDMusic(VOID) +{ + +// WriteRealMem(REALPTR(Stop_addrp) << 16, &Stop, sizeof(StopType)); + Mem_Copy ( &Stop , (void *) ( Stop_addrp.seg << 4 ) , sizeof(StopType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StopType, Length); + sregs.es = Stop_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stop, REALPTR(Stop_addrp) << 16, sizeof(StopType)); + Mem_Copy ( (void *) ( Stop_addrp.seg << 4 ) , &Stop, sizeof(StopType)); + +} + diff --git a/WIN32LIB/SRCDEBUG/REGIONSZ.CPP b/WIN32LIB/SRCDEBUG/REGIONSZ.CPP new file mode 100644 index 0000000..7dd9965 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/REGIONSZ.CPP @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB 32 * + * * + * File Name : REGIONSZ.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : November 3, 1994 * + * * + * Last Update : November 3, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Size_Of_Region -- Calculates the size of a given region * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * SIZE_OF_REGION -- Calculates the size of a given region * + * * + * INPUT: int width - the width of the region * + * int height - the height of the region * + * * + * OUTPUT: long - the size of the region * + * * + * HISTORY: * + * 11/03/1994 PWG : Created. * + *=========================================================================*/ +long Size_Of_Region(int width, int height) +{ + return(width * height); +} diff --git a/WIN32LIB/SRCDEBUG/REMAP.ASM b/WIN32LIB/SRCDEBUG/REMAP.ASM new file mode 100644 index 0000000..65c5469 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/REMAP.ASM @@ -0,0 +1,175 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword + + + cmp [ remap ] , 0 + jz ??real_out + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_remap + + test cl , 1000b + jz ??scr_left_ok + mov [ x0_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y0_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_remap + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +??do_remap: + cld + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + mov ebx , [ x1_pixel ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle ??real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle ??real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +??outer_loop: + mov ebx , [ counter_x ] +??inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz ??inner_loop + add edi , esi + dec ecx + jnz ??outer_loop + + + + +??real_out: + ret + + ENDP Buffer_Remap + + END diff --git a/WIN32LIB/SRCDEBUG/REVERSE.ASM b/WIN32LIB/SRCDEBUG/REVERSE.ASM new file mode 100644 index 0000000..29677f2 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/REVERSE.ASM @@ -0,0 +1,139 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/reverse.asm 1.3 1994/04/25 12:22:45 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : REVERSE.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : April 20, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; LONG Reverse_Long(LONG number); * +; WORD Reverse_Short(WORD number); * +; LONG Swap_LONG(LONG number); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Reverse_Short :NEAR +GLOBAL C Swap_Long :NEAR +GLOBAL C Reverse_Long :NEAR + +CODESEG + + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; LONG Reverse_LONG(LONG number); +; WORD Reverse_Short(WORD number); +; LONG Swap_LONG(LONG number); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_LONG +; +; LONG Reverse_LONG(LONG number); +; +;* + PROC Reverse_Long C near + ARG number:DWORD + +IF 1 + mov eax,[DWORD PTR number] + xchg al,ah + ror eax,16 + xchg al,ah +ELSE + + ; This is old 16 bit code. + mov ax,[WORD PTR number] + mov dx,[WORD PTR number+2] + xchg ah,dl + xchg al,dh +ENDIF + + ret + + ENDP Reverse_Long + +;----------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_WORD +; +; WORD Reverse_Short(WORD number); +; +;* + PROC Reverse_Short C near + ARG number:WORD + + mov ax,[number] + xchg ah,al + ret + + ENDP Reverse_Short + +;----------------------------------------------------------------- + + +;----------------------------------------------------------------- +; +; SWAP_Long +; +; Long Swap_Long(Long number); +; +;* + PROC Swap_Long C near + ARG number:DWORD + +IF 1 + ; 32 bit code. + mov eax,[DWORD PTR number] + ror eax,16 +ELSE + ; 16 bit code. + mov ax,[WORD PTR number+2] + mov dx,[WORD PTR number] +ENDIF + + ret + + + ENDP Swap_Long + +;----------------------------------------------------------------- + + END + diff --git a/WIN32LIB/SRCDEBUG/SCALE.ASM b/WIN32LIB/SRCDEBUG/SCALE.ASM new file mode 100644 index 0000000..afc7e56 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SCALE.ASM @@ -0,0 +1,570 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : June 21, 1994 [PWG] * +;* New version : feb 12, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* Normal_Remapped_Draw -- jump loc for draw scaled line of remap pixel * +;* Transparent_Draw -- jump loc for scaled line of transparent pixels * +;* Transparent_Remapped_Draw -- jump loc for scaled remap trans pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je ??all_done + cmp [dst_height],0 + je ??all_done + cmp [src_width],0 + je ??all_done + cmp [src_height],0 + je ??all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??clip_against_dest + mov bl , dl + test cl , 1000b + jz ??src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + +??src_left_ok: + test cl , 0010b + jz ??src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + +??src_bottom_ok: + test bl , 0100b + jz ??src_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + +??src_right_ok: + test bl , 0001b + jz ??clip_against_dest + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + +; Clip destination Rectangle against source Window boundaries. +??clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??do_scaling + mov bl , dl + test cl , 1000b + jz ??dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + +??dst_left_ok: + test cl , 0010b + jz ??dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + +??dst_bottom_ok: + test bl , 0100b + jz ??dst_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + +??dst_right_ok: + test bl , 0001b + jz ??do_scaling + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + +??do_scaling: + + cld + mov ebx , [ this_object ] + mov esi , [ (GraphicViewPort ebx) . GVPOffset ] + mov eax , [ (GraphicViewPort ebx) . GVPXAdd ] + add eax , [ (GraphicViewPort ebx) . GVPWidth ] + add eax , [ (GraphicViewPort ebx) . GVPPitch ] + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ (GraphicViewPort ebx) . GVPOffset ] + mov eax , [ (GraphicViewPort ebx) . GVPXAdd ] + add eax , [ (GraphicViewPort ebx) . GVPWidth ] + add eax , [ (GraphicViewPort ebx) . GVPPitch ] + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jle ??all_done + sub ebx , [ dst_x0 ] + jle ??all_done + + mov [ counter_y ] , ecx + + cmp [ trans ] , 0 + jnz ??transparency + + cmp [ remap ] , 0 + jnz ??normal_remap + +; ************************************************************************* +; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ??ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + ??outter_loop: + push esi + push edi + xor ecx , ecx + mov ebx , [ counter_x ] + jmp [ entry ] + ??inner_loop: + REPT 32 + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + ENDM + ??ref_point: + dec ebx + jge ??inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??normal_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ ??remapref_point + ecx ] + mov [ entry ] , ecx + + ??remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??remapinner_loop: + REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + ENDM + ??remapref_point: + dec [ remap_counter ] + jge ??remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??remapoutter_loop + ret + + +;**************************************************************************** +; scale with trnsparency + +??transparency: + cmp [ remap ] , 0 + jnz ??trans_remap + +; ************************************************************************* +; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_ref_point + ecx ] + mov [ entry ] , ecx + + ??trans_outter_loop: + xor ecx , ecx + push esi + push edi + mov ebx , [ counter_x ] + jmp [ entry ] + ??trans_inner_loop: + REPT 32 + local trans_pixel + mov cl , [ esi ] + test cl , cl + jz trans_pixel + mov [ edi ] , cl + trans_pixel: + add ecx , eax + adc esi , edx + inc edi + ENDM + ??trans_ref_point: + dec ebx + jge ??trans_inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_remapref_point + ecx ] + mov [ entry ] , ecx + + ??trans_remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??trans_remapinner_loop: + REPT 32 + local trans_pixel + mov bl , [ esi ] + test bl , bl + jz trans_pixel + mov cl , [ eax + ebx ] + mov [ edi ] , cl + trans_pixel: + add ecx , [ dx_frac ] + adc esi , edx + inc edi + ENDM + ??trans_remapref_point: + dec [ remap_counter ] + jge ??trans_remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_remapoutter_loop + ret + + + + + +??all_done: + ret +endp + + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/SETFPAL.ASM b/WIN32LIB/SRCDEBUG/SETFPAL.ASM new file mode 100644 index 0000000..8256d2d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SETFPAL.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL C ColorXlat:BYTE +GLOBAL C Set_Font_Palette_Range:NEAR + + CODESEG + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + PROC Set_Font_Palette_Range C near + USES eax, ebx, ecx,edi,esi + ARG palette:DWORD + ARG start:DWORD + ARG endval:DWORD + + cld + + mov esi,[palette] + + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + dec ecx + jnz ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/SETSHAPE.ASM b/WIN32LIB/SRCDEBUG/SETSHAPE.ASM new file mode 100644 index 0000000..52cf574 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SETSHAPE.ASM @@ -0,0 +1,81 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SETSHAPE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : October 26, 1994 * +;* * +;* Last Update : October 26, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Shape_Buffer -- Sets the shape buffer to the given pointer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + diff --git a/WIN32LIB/SRCDEBUG/SET_FONT.CPP b/WIN32LIB/SRCDEBUG/SET_FONT.CPP new file mode 100644 index 0000000..127b603 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SET_FONT.CPP @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SET_FONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Font -- Changes the default text printing font. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include + + + +/*************************************************************************** + * SET_FONT -- Changes the default text printing font. * + * * + * This routine will change the default text printing font for all * + * text output. It handles updating the system where necessary. * + * * + * INPUT: fontptr -- Pointer to the font to change to. * + * * + * OUTPUT: Returns with a pointer to the previous font. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/06/1991 JLB : Created. * + * 09/17/1991 JLB : Fixed return value bug. * + * 01/31/1992 DRD : Modified to use new font format. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Set_Font(void const *fontptr) +{ + void *oldfont; + char const *blockptr; + + oldfont = (void *) FontPtr; + + if (fontptr) { + FontPtr = (void *) fontptr; + + /* + ** Inform the system about the new font. + */ + + FontWidthBlockPtr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTWIDTHBLOCK); + blockptr = (char*)fontptr + *(unsigned short *)((char*)fontptr + FONTINFOBLOCK); + FontHeight = *(blockptr + FONTINFOMAXHEIGHT); + FontWidth = *(blockptr + FONTINFOMAXWIDTH); + //Draw_Char_Setup(); + +#if FALSE + WindowLines = WinH / FontHeight; + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / FontWidth; +#endif + } + + return(oldfont); +} diff --git a/WIN32LIB/SRCDEBUG/SHADOW.ASM b/WIN32LIB/SRCDEBUG/SHADOW.ASM new file mode 100644 index 0000000..1b77ec6 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SHADOW.ASM @@ -0,0 +1,210 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./shadow.asm 1.9 1994/05/20 15:30:49 joe_bostic Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : SHADOW.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : February 28, 1995 [BG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, GVPC dst, void *shadowbuff); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" +INCLUDE ".\keystruc.inc" + +GLOBAL C Shadow_Blit : NEAR + +GLOBAL C RealModePtr : DWORD +GLOBAL C Hide_Mouse : NEAR +GLOBAL C Show_Mouse : NEAR + + CODESEG + +; void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, VBC dst, void *shadowbuff); +; Warning: Shadow_Blit appears to be relatively stupid, in that while it is +; optimized to perform word or dword blits, it only does so if the +; entire region is word or dword-sized. In other words, if you blit +; a region that is 200 pixels wide (clearly dword-sized), then it +; will use the dword loop. However, if you blit a region that is +; 201 pixels wide, the dumb thing will use the byte loop for the +; entire blit. + PROC Shadow_Blit C near + USES eax,ebx,ecx,edx,esi,edi + + ARG x:DWORD + ARG y:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG srcpage:DWORD + ARG dstpage:DWORD + ARG shadow:DWORD + + LOCAL modulo:DWORD ; Row modulo + LOCAL hidemouse:DWORD ; Was the mouse hidden? + LOCAL dwordwidth:DWORD + LOCAL bytewidth:DWORD + + cld ; Always move forward. + mov [hidemouse],0 ; Presume mouse hasn't been hidden. + +; Fetch the segment of the seenpage. + mov ebx,[dstpage] + mov ebx,[(GraphicViewPort ebx).GVPOffset] + + ; Determine if the mouse needs to be hidden at all. If this happens to be + ; a shadow blit to non visible page (who knows why!?) then don't bother to + ; hide the mouse since it isn't necessary. +; cmp ebx,0A0000h +; setne [BYTE PTR hidemouse] ; Flag that mouse need not be hidden. +; jne short ??itsok + mov esi,[RealModePtr] + cmp [(KeyboardType esi).MState],0 + je short ??itsok + mov [hidemouse],1 + call Hide_Mouse C ; Hides mouse again (just to be sure). +??itsok: + mov edi,[srcpage] + mov esi,[(GraphicViewPort edi).GVPOffset] + + mov eax,[(GraphicViewPort edi).GVPWidth] + add eax,[(GraphicViewPort edi).GVPXAdd] + add eax,[(GraphicViewPort edi).GVPPitch] + push eax ; save width+xadd for later calc + mov edx,[y] + mul edx + add eax,[x] + add esi,eax +; At this point, esi points to the source page and ebx points to the dest page + sub ebx,esi ; esi+ebx == dest page pointer + + mov edi,[shadow] ; EDI points to shadow buffer. + + mov ecx,[region_height] ; get the height of the window in cx + + mov edx,[RealModePtr] + + ; Calculate the row add module. + pop eax ; restore width+xadd + sub eax,[region_width] + mov [modulo],eax + + mov eax,[region_width] + shr eax,2 + mov [dwordwidth],eax + mov eax,[region_width] + and eax,3 + mov [bytewidth],eax + +;--------------------------------------- +; DOUBLE WORD shadow blit if possible. +;--------------------------------------- +??dloop_top: + push ecx + mov ecx,[dwordwidth] + +??lcontinue: + repe cmpsd ; check the entire row for changed longs + je short ??loop_top + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??dok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??dok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jb short ??dok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??dok: + + mov eax,[esi-4] + mov [ebx+esi-4],eax ; Update destination page. + mov [edi-4],eax ; Update shadow buffer. + or ecx,ecx + jne short ??lcontinue + +;--------------------------------------- +; Row loop start for BYTES. +;--------------------------------------- +??loop_top: + mov ecx,[bytewidth] + +; Column loop start -- by bytes. +??continue: + repe cmpsb ; check the entire row for changed longs + je short ??done_x + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??bok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??bok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jl short ??bok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??bok: + + mov al,[esi-1] + mov [ebx+esi-1],al ; Update destination page. + mov [edi-1],al ; Update shadow buffer. + + or ecx,ecx + jne short ??continue + +??done_x: + inc [y] + add esi,[modulo] + pop ecx + dec ecx + jnz ??dloop_top + +??fini: + ; Re show the mouse if it was hidden by this routine. + cmp [hidemouse],0 + je short ??reallyfini + call Show_Mouse C +??reallyfini: + ret + + ENDP Shadow_Blit + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/SHAKESCR.ASM b/WIN32LIB/SRCDEBUG/SHAKESCR.ASM new file mode 100644 index 0000000..7b413bd --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SHAKESCR.ASM @@ -0,0 +1,158 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : SHAKESCR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 19, 1993 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + ret + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END diff --git a/WIN32LIB/SRCDEBUG/SHAPE.ASM b/WIN32LIB/SRCDEBUG/SHAPE.ASM new file mode 100644 index 0000000..08372c8 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SHAPE.ASM @@ -0,0 +1,94 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : April 13, 1992 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* Not_Supported -- Replacement function for Draw_Shape routines not used* +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "shape.inc" + +global C ShapeBuffer :dword +global C ShapeBufferSize :dword +global C _ShapeBuffer :dword +global C _ShapeBufferSize :dword +global C Set_Shape_Buffer :near + +DATASEG +label ShapeBuffer dword +_ShapeBuffer dd 0 + +label ShapeBufferSize dword +_ShapeBufferSize dd 0 + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL C Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + diff --git a/WIN32LIB/SRCDEBUG/SOSCODEC.ASM b/WIN32LIB/SRCDEBUG/SOSCODEC.ASM new file mode 100644 index 0000000..6e6011b --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SOSCODEC.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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) +;* Jonathan Lanier +;* +;* DATE +;* Febuary 15, 1995 +;* +;* LAST MODIFIED +;* 08/07/95 [jdl] - Rewrote/optimized sosCODECDecompressData +;* +;*--------------------------------------------------------------------------- +;* +;* 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 + +INCLUDE "difftb.inc" +INCLUDE "indextb.inc" +INCLUDE "nybbtb.inc" + +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 sosCODECInitStream:NEAR + PROC sosCODECInitStream C NEAR + + ARG sSOSInfo:NEAR PTR + + mov eax,[sSOSInfo] + mov [(sCompInfo eax).wIndex],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted],0 ; no predicted value + mov [(sCompInfo eax).wIndex2],0 ; starting index 0 + mov [(sCompInfo eax).dwPredicted2],0 ; no predicted value + 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 decompress. +;* +;* RESULT +;* Size - Size of decompressed data. +;* +;* +;* NOTES +;* This routine has been optimized for pipelining on both +;* 486 and Pentium processors. Changing, inserting, or moving any +;* instructions will most likely slow down the code, in some cases by +;* as much as 20%. It can burst-decompress 16384 samples in about +;* 1940æs on a Pentium 90Mhz, and about 3960æs on a 486 66Mhz. +;* Instruction reordering could bring this down to below 1870æs on +;* the Pentium, but this would cause a great degradation in 486 +;* performance. Since slow 486's are the reason this code was +;* written to be fast, it has been optimized for the Pentium only where +;* it would not degrade 486 performance. So, be careful when changing +;* ANY of this code, because it is very carefully balanced... +;**************************************************************************** + + GLOBAL C sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + push esi + push edi + push ebx + push ecx + push edx ;save all the regs + + mov ebx,[sSOSInfo] ;get base of sCompInfo struct + mov cx,[(sCompInfo ebx).wBitSize] ;check the bit size + mov dx,[(sCompInfo ebx).wChannels] ;check the number of channels +; +; +; Determine the correct routine to use for decoding +; (for now only ADPCM 4:1 Mono 16-bit is implemented) + cmp cx,8 + jne ??do16Bits + +??do8Bits: + cmp dx,2 + jne ??not8Stereo +; jmp ??decomp8Stereo + jmp ??byeBye + +??not8Stereo: + cmp dx,1 + jne ??byeBye +; jmp decomp8Mono + jmp ??byeBye + +??do16Bits: + cmp cx,16 + jne ??byeBye + + cmp dx,2 + jne ??not16Stereo +; jmp ??decomp16Stereo + jmp ??byeBye + +??not16Stereo: + cmp dx,1 + jne ??byeBye + + push ebp +; +; +; 16 bit ADPCM 4:1 Mono pre-loop initialization +??decomp16Mono: + push ebx ;save struct base + xor edx,edx ;clear index + mov eax,[(sCompInfo ebx).dwPredicted] ;get last sample + mov dx,[(sCompInfo ebx).wIndex] ;get last index value + mov esi,[(sCompInfo ebx).lpSource] ;get source address + mov edi,[(sCompInfo ebx).lpDest] ;get dest address + + mov ebp,[wBytes] ;get the number of dest. bytes + cmp ebp,16 ;less than 16? (less than 8 samples) + jl ??fixAlign16Mono0 ;if so, don't bother with alignment +; +; +; Check to see if we need to fix an alignment problem on the source buffer +; (non-aligned buffers are MUCH slower; if we're given a non-DWORD aligned +; source address, we do as many samples as needed to get to the nearest +; DWORD boundary, then finish the bulk as a DWORD-aligned decompress). + mov ebx,esi ;get source address + and ebx,03h ;check LSB + jnz ??fixalign16Mono ;if non-zero, need to align for + ;warp speed +??fixAlign16Mono0: + push ebp ;save for later + shr ebp,4 ;divide by 16 for 16-bit, + ;because we do 8 nybbles per loop, + ;and there are two samples per + ;byte, so there are n/16 iterations + ;required + xor ebx,ebx ;clear our nybble index + or ebp,ebp ;set flags for EBP + jmp ??start16Mono ;start with test... don't go if + ;we have zero bytes to do +??fixalign16Mono: + jmp [DWORD PTR dwMono16AlignJmpTable+ebx*4] ;do non-aligned first + + align 4 +??fixAlign16Mono1: + sub ebp,12 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono6 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono2: + sub ebp,8 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono4 ;borrow exit code to go through a + ;piece of a loop + align 4 +??fixAlign16Mono3: + sub ebp,4 ;adjust # of dest. bytes + push ebp ;save it + shr ebp,4 ;divide by 16 to get samples/8 + xor ebx,ebx ;clear our nybble index + inc ebp ;adjust ebp for loop + jmp ??finish16Mono2 ;borrow exit code to go through a + ;piece of a loop +; "The Loop" +; +; Process 1st nybble + align 4 +??loop16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix1Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix1Smp16MonoU +??fixed1Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi],ax ;save the sample +; +; +; Process 2nd nybble +??finish7Smp16Mono: + mov bl,ch ;get next 2 nybbles in ebx + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix2Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix2Smp16MonoU +??fixed2Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+02h],ax ;save the sample + shr ecx,16 ;move top four nybbles into bottom +; +; +; Process 3rd nybble +??finish6Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix3Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix3Smp16MonoU +??fixed3Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+04h],ax ;save the sample +; +; +; Process 4th nybble +??finish5Smp16Mono: + mov bl,cl + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix4Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix4Smp16MonoU +??fixed4Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+06h],ax ;save the sample +; +; +; Process 5th nybble +??finish4Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix5Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix5Smp16MonoU +??fixed5Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+08h],ax ;save the sample +; +; +; Process 6th nybble +??finish3Smp16Mono: + mov bl,ch + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix6Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix6Smp16MonoU +??fixed6Smp16Mono: + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + mov [edi+0Ah],ax ;save the sample +; +; +; Process 7th nybble +??finish2Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix7Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix7Smp16MonoU +??fixed7Smp16Mono: + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + mov [edi+0Ch],ax ;save the sample +; +; +; Process 8th nybble +??finish1Smp16Mono: + add eax,[dwDiffTable+edx*2] ;add difference to prev. sample + mov dx,[wIndexTable+edx] ;adjust dx to next index base + cmp eax,00007FFFh ;check for overflow + jg ??fix8Smp16MonoO + cmp eax,0FFFF8000h ;check for underflow + jl ??fix8Smp16MonoU +; +; +; Loop cleanup for next pass +??fixed8Smp16Mono: + mov [edi+0Eh],ax ;save the sample + add esi,04h ;bump esi to point to next longword + add edi,10h ;incr. the destination buffer ptr + dec ebp ;count down the number of samples/8 +??start16Mono: + jng ??cleanup16Mono ;if done, clean up + mov ecx,[esi] ;get 4 nybbles in one whack (whee!) + mov bl,cl ;get next 2 nybbles in ebx + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??loop16Mono ;loop until done + +??cleanup16Mono: + jnz ??done16Mono ;if ebp is non-zero, we're DONE + ;if exactly zero, finish the tail-end + ;of the conversion (may be a non- + ;multiple of 8 nybbles) +; +; +; Loop cleanup for last (incomplete) pass + pop ecx ;restore # of words + shr ecx,1 ;divide by two to get samples + and ecx,07h ;get # of samples we missed + jmp [DWORD PTR dwMono16JmpTable+ecx*4] ;go finish the job... +; +; +; Structure cleanup +??done16Mono: + pop ebx ;restore struct base + pop ebp ;restore stack frame pointer + mov [(sCompInfo ebx).dwPredicted],eax ;save last sample + mov [(sCompInfo ebx).wIndex],dx ;save last index value + mov eax,[wBytes] ;get # of bytes we did +??byeBye: + pop edx ;restore all the regs + pop ecx + pop ebx + pop edi + pop esi + ret +; +; +; Jumps for -32768/+32767 bounds check go to these vvvv + align 4 +??fix1Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix1Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed1Smp16Mono ;go back + + align 4 +??fix2Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix2Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed2Smp16Mono ;go back + + align 4 +??fix3Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix3Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed3Smp16Mono ;go back + + align 4 +??fix4Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix4Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed4Smp16Mono ;go back + + align 4 +??fix5Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix5Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed5Smp16Mono ;go back + + align 4 +??fix6Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix6Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed6Smp16Mono ;go back + + align 4 +??fix7Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix7Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed7Smp16Mono ;go back + + align 4 +??fix8Smp16MonoO: + mov eax,00007FFFh ;Overflow - truncate to +32767 + jmp ??fixed8Smp16Mono ;go back + + align 4 +??fix8Smp16MonoU: + mov eax,0FFFF8000h ;Underflow - truncate to -32768 + jmp ??fixed8Smp16Mono ;go back +; +; +; Jump tables for cleanup after loop unroll point to these vvvv + align 4 +??finish16Mono1: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 1 nybble (1 byte) + shl ch,4 ;move it over + mov bl,ch ;get nybble in ebx + sub edi,0Eh ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish1Smp16Mono ;go finish it + + align 4 +??finish16Mono2: + xor ecx,ecx ;clear nybble bucket + mov ch,[esi] ;get 2 nybbles (1 byte) + mov bl,ch ;get nybbles in ebx + sub edi,0Ch ;back edi up + sub esi,3 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish2Smp16Mono ;go finish it + + align 4 +??finish16Mono3: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 3 nybbles (2 bytes) + shl cx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,0Ah ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish3Smp16Mono ;go finish it + + align 4 +??finish16Mono4: + xor ecx,ecx ;clear nybble bucket + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + sub edi,08h ;back edi up + sub esi,2 ;adjust esi (used for dword aligning) + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish4Smp16Mono ;go finish it + + align 4 +??finish16Mono5: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 1 nybble (1 byte) + shl ecx,16 ;shift it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,4 ;move it over + shl bl,4 ;move it over + sub edi,06h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish5Smp16Mono ;go finish it + + align 4 +??finish16Mono6: + xor ecx,ecx ;clear nybble bucket + mov cl,[esi+2] ;get 2 nybbles (1 byte) + shl ecx,16 ;move it over + mov cx,[esi] ;get 4 nybbles (2 bytes) + mov bl,cl ;get nybbles in ebx + shr ecx,8 ;move it over + sub esi,1 ;adjust esi (used for dword aligning) + sub edi,04h ;back edi up + or dl,[BYTE PTR bNybbleTableLow+ebx] ;adjust index for nybble + jmp ??finish6Smp16Mono ;go finish it + + align 4 +??finish16Mono7: + xor ecx,ecx ;clear nybble bucket + mov ecx,[esi] ;get 7 nybbles (4 bytes) + shl ecx,4 ;move it over + mov bl,cl ;get nybbles in ebx + sub edi,02h ;back edi up + or dl,[BYTE PTR bNybbleTableHigh+ebx] ;adjust index for nybble + jmp ??finish7Smp16Mono ;go finish it +; +; +; Jump Tables + align 4 + +dwMono16JmpTable DD ??done16Mono + DD ??finish16Mono1 + DD ??finish16Mono2 + DD ??finish16Mono3 + DD ??finish16Mono4 + DD ??finish16Mono5 + DD ??finish16Mono6 + DD ??finish16Mono7 + + align 4 +dwMono16AlignJmpTable DD ??fixAlign16Mono0 + DD ??fixAlign16Mono1 + DD ??fixAlign16Mono2 + DD ??fixAlign16Mono3 + + ENDP 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 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 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 + diff --git a/WIN32LIB/SRCDEBUG/SOUNDINT.CPP b/WIN32LIB/SRCDEBUG/SOUNDINT.CPP new file mode 100644 index 0000000..4089487 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SOUNDINT.CPP @@ -0,0 +1,544 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include "dsound.h" +#include +#include "soundint.h" +#include "memflag.h" +#include "audio.h" + +extern DebugBuffer[]; + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + + + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * , short int *) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + if (st->sosinfo.wBitSize==16 && st->sosinfo.wChannels==1){ + sosCODECDecompressData(&st->sosinfo, dsize); + } else { + General_sosCODECDecompressData(&st->sosinfo, dsize); + } + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + + + + + + +extern int Convert_HMI_To_Direct_Sound_Volume(int volume); + +/*********************************************************************************************** + * maintenance_callback -- routine to service the direct play secondary buffers * + * and other stuff..? * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * ....Unknown * + * 10/17/95 10:15PM ST : tidied up a tad for direct sound * + *=============================================================================================*/ +VOID far __cdecl maintenance_callback(VOID) +{ + + int index; //index used in for loop + SampleTrackerType *st; //ptr to SampleTracker structure + DWORD play_cursor; //Position that direct sound is reading from + DWORD write_cursor; //Position in buffer that we can write to + int bytes_copied; //Number of bytes copied into the buffer + BOOL write_more; //Flag to set if we need to write more into the buffer + LPVOID play_buffer_ptr; //Beginning of locked area of buffer + LPVOID dummy_buffer_ptr; //Length of locked area in buffer + DWORD lock_length1; //Beginning of second locked area in buffer + DWORD lock_length2; //Length of second locked area in buffer + HRESULT return_code; + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the direct sound buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service && !st->DontTouch ) { + + //EnterCriticalSection (&st->AudioCriticalSection); + + st->DontTouch = TRUE; + + /* + ** Get the current position of the direct sound play cursor within the buffer + */ + return_code = st->PlayBuffer->GetCurrentPosition ( &play_cursor , &write_cursor ); + + /* + ** Check for unusual situations like a focus loss + */ + if (return_code != DS_OK){ + if (return_code == DSERR_BUFFERLOST){ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + //LeaveCriticalSection (&st->AudioCriticalSection); + return; //Our app has lost focus or something else nasty has happened + } //so dont update the sound buffers + + + if (st->MoreSource){ + + /* + ** If the direct sound read pointer is less than a quarter + ** of a buffer away from the end of the data then copy some + ** more. + */ + write_more = FALSE; + + if ( play_cursor < (unsigned)st->DestPtr ){ + if ( (unsigned)st->DestPtr - (unsigned)play_cursor <= SECONDARY_BUFFER_SIZE/4 ){ + write_more=TRUE; + } + } else { + /* The only time that play_cursor can be greater than DestPtr is + ** if we wrote right to the end of the buffer last time and DestPtr + ** looped back to the beginning of the buffer. + ** That being the case, all we have to do is see if play_cursor is + ** within the last 25% of the buffer + */ + if ( ( (int)play_cursor > SECONDARY_BUFFER_SIZE*3/4) &&st->DestPtr==0 ){ + write_more=TRUE; + } + } + + if (write_more){ + + /* + ** Lock a 1/2 of the direct sound buffer so we can write to it + */ + if ( DS_OK== st->PlayBuffer->Lock ( (DWORD)st->DestPtr , + (DWORD)SECONDARY_BUFFER_SIZE/2, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 )){ + + bytes_copied = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + + if ( bytes_copied != (SECONDARY_BUFFER_SIZE/4) ){ + /* + ** We must have reached the end of the sample + */ + st->MoreSource=FALSE; + memset (((char*)play_buffer_ptr)+bytes_copied , + 0 , + (SECONDARY_BUFFER_SIZE/4)-bytes_copied); + + /* + ** Clear out an extra area in the buffer ahead of the play cursor + ** to give us a quiet period of grace in which to stop the buffer playing + */ + if ( (unsigned)st->DestPtr == SECONDARY_BUFFER_SIZE*3/4 ){ + if ( dummy_buffer_ptr && lock_length2 ){ + memset (dummy_buffer_ptr , 0 , lock_length2); + } + } else { + memset ((char*)play_buffer_ptr+SECONDARY_BUFFER_SIZE/4 , 0 , SECONDARY_BUFFER_SIZE/4); + } + } + + /* + ** Update our pointer into the direct sound buffer + ** + */ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,bytes_copied); + + if ( (unsigned)st->DestPtr >= (unsigned)SECONDARY_BUFFER_SIZE ){ + st->DestPtr = Audio_Add_Long_To_Pointer (st->DestPtr,(long)-SECONDARY_BUFFER_SIZE); + } + + + /* + ** Unlock the direct sound buffer + */ + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + } + + } //write_more + + } else { //!more_source + + /* + ** no more source to write - check if the buffer play + ** has overrun the end of the sample and stop it if it has + */ + if ( ( (play_cursor >= (unsigned)st->DestPtr) && ( ((unsigned)play_cursor - (unsigned)st->DestPtr) OneShot &&( (play_cursor < (unsigned)st->DestPtr) && ( ((unsigned)st->DestPtr - (unsigned)play_cursor) >(SECONDARY_BUFFER_SIZE*3/4) ) )) ){ + st->PlayBuffer->Stop(); + st->Service = FALSE; + Stop_Sample( index ); + } + } //more_source + + st->DontTouch = FALSE; + + //LeaveCriticalSection (&st->AudioCriticalSection); + } + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + //EnterCriticalSection (&st->AudioCriticalSection); + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + + //st->PlayBuffer->SetVolume (-( ( (32768-st->Volume)*1000) >>15 ) ); + + if (st->IsScore){ + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.ScoreVolume*(st->Volume >>7))/256) ); + }else{ + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.SoundVolume*(st->Volume >>7))/256) ); + } + + //LeaveCriticalSection (&st->AudioCriticalSection); + } + st++; + } + LockedData._int--; + } + + //LeaveCriticalSection(&GlobalAudioCriticalSection); +} + + + + + + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WIN32LIB/SRCDEBUG/SOUNDIO.CPP b/WIN32LIB/SRCDEBUG/SOUNDIO.CPP new file mode 100644 index 0000000..785bebe --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SOUNDIO.CPP @@ -0,0 +1,2158 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Load_Long_Sample -- Loads a sample into XMS for double buffer system. * + * Read_Long_Sample -- Loads/Processes/Formats/Builds offset. * + * Save_Table_Entry -- Put an entry in the offset table. * + * Play_Long_Sample -- Calls Init_Long_Sample and Start_Long_Sample. * + * Start_Long_Sample -- Starts a sample playing that has be initialized. * + * Get_Table_Entry -- Gets next entry in table. * + * Long_Sample_Ticks -- Gets number of ticks in sample if in header. * + * Long_Sample_Status -- Returns the status of the sample. * + * Find_Table_Entry -- Finds next entry in table that matches mask. * + * Get_Table_Start -- Returns a pointer to first entry in table. * + * Long_Sample_Ticks_Played -- Number of ticks since sample started. * + * Install_Sample_Driver_Callback -- Pokes callback function into JM driver * + * Stop_Long_Sample -- Stops current long sample from playing. * + * Long_Sample_Loaded_Size -- Max buffer size to load a long sample. * + * Sound_Callback -- Audio driver callback function. * + * DigiCallback -- Low level double buffering handler. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Stream_Sample -- Streams a sample directly from a file. * + * Sample_Read -- Reads sample data from an openned file. * + * Continue_Sample -- Tags another block of data onto the currently playing. * + * Sample_Copy -- Copies sound data from source format to raw format. * + * File_Stream_Preload -- Handles initial proload of a streaming samples bu* + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern void Colour_Debug (int call_number); +#pragma pack(4) + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#include +#include +#include "dsound.h" + +#include +#include +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + +LPDIRECTSOUNDBUFFER DumpBuffer; +HANDLE SoundThreadHandle = NULL; +BOOL SoundThreadActive = FALSE; + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This is the number of times per sec that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 40 //30 times per sec plus a safety margin + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +//#define STREAM_BUFFER_SIZE (128L*1024L) +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +//void *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +int StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; +LPDIRECTSOUND SoundObject; //Direct sound object +LPDIRECTSOUNDBUFFER PrimaryBufferPtr; //Pointer to the buffer that the +unsigned SoundTimerHandle=0; //Windows Handle for sound timer +WAVEFORMATEX DsBuffFormat; //format of direct sound buffer +DSBUFFERDESC BufferDesc; //Buffer description for creating buffers +WAVEFORMATEX PrimaryBuffFormat; //Copy of format of direct sound primary buffer +DSBUFFERDESC PrimaryBufferDesc; //Copy of buffer description for re-creating primary buffer +CRITICAL_SECTION GlobalAudioCriticalSection; +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void) = NULL; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(short id, short *odd, void **buffer, long *size); +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle); +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ); +//static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size)); +void Sound_Thread (void *); +volatile BOOL AudioDone; +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +// This callback is called whenever the queue buffer playback has begun +// and another buffer is needed for queuing up. Returns TRUE if there +// is more data to read from the file. +static BOOL File_Callback(short id, short *odd, void **buffer, long *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + void *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = (short)(*odd + 1); + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != WW_ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + while (num_empty_buffers && (st->FileHandle != WW_ERROR)) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return(TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + + } + return(FALSE); +} + + + + + +// Generic streaming sample playback initialization. +static int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; +// ServiceSomething = TRUE; + } + } + return (playid); +} + + + +#if (0) +static int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(short id, short *odd, void **buffer, long *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} +#endif + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT:LockedData.SampleTracker * to the header which tracks this samples* + * processing.* + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + Sound_Timer_Callback(0,0,0,0,0); //Shouldnt block as we are calling it from the same thread + + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + StartingFileStream = TRUE; + + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = WW_ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + +} + + + + + + + +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + *=============================================================================================*/ + +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static void *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < MAX_SFX; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= MAX_SFX) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + File_Stream_Preload(handle); + } + + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +void __cdecl Sound_Callback(void) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + + /* + ** Call the timer callback now as we may block it in this function + */ + Sound_Timer_Callback(0,0,0,0,0); + + st = &LockedData.SampleTracker[0]; + for (index = 0; index < MAX_SFX; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Stop_Sample(index); + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + + if ((!st->QueueBuffer || + (st->FileHandle != WW_ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback((short)index, (short int *)&st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != WW_ERROR) { + //EnterCriticalSection(&GlobalAudioCriticalSection); + Close_File(st->FileHandle); + st->FileHandle = WW_ERROR; + //LeaveCriticalSection(&GlobalAudioCriticalSection); + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +void *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != WW_ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + void *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* +** Conversion formula for TCrate and Hz rate. +** +** TC = 256 - 1m/rate +** rate = 1m / (256-TC) +*/ + + if (!buffer || fh == WW_ERROR || size <= sizeof(RawHeader)) return(NULL); + + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + actual_bytes_read = Read_File(fh, &RawHeader, sizeof(RawHeader)); + actual_bytes_read +=Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +void Free_Sample(void const *sample) +{ + if (sample) Free((void *)sample); +} + + + + + + + + + + + +/*********************************************************************************************** + * Sound_Timer_Callback -- windows timer callback for sound maintenance * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:01PM ST : Created * + *=============================================================================================*/ + +void CALLBACK Sound_Timer_Callback ( UINT, UINT, DWORD, DWORD, DWORD ) +{ + //if (!InTimerCallback){ + //InTimerCallback++; + //Colour_Debug (5); + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + //Colour_Debug (0); + //InTimerCallback--; + //} +} + +void Sound_Thread (void *) +{ + DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&SoundThreadHandle , THREAD_ALL_ACCESS , TRUE , 0); + SetThreadPriority (SoundThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); + SoundThreadActive = TRUE; + + while (!AudioDone){ + + EnterCriticalSection(&GlobalAudioCriticalSection); + maintenance_callback(); + LeaveCriticalSection(&GlobalAudioCriticalSection); + Sleep(1000/40); + } + + SoundThreadActive = FALSE; +} + + + + + + + +/*********************************************************************************************** + * Set_Primary_Buffer_Format -- set the format of the primary sound buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if successfully set * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/22/95 4:06PM ST : Created * + *=============================================================================================*/ + +BOOL Set_Primary_Buffer_Format(void) +{ + if (SoundObject && PrimaryBufferPtr){ + return (PrimaryBufferPtr->SetFormat ( &PrimaryBuffFormat ) == DS_OK); + } + return (FALSE); +} + + + +/*********************************************************************************************** + * Print_Sound_Error -- show error messages from failed sound initialisation * + * * + * * + * * + * INPUT: error text * + * handle to window * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/7/96 10:17AM ST : Created * + *=============================================================================================*/ + +void Print_Sound_Error(char *sound_error, HWND window) +{ + MessageBox(window, sound_error, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); +} + + + +/*********************************************************************************************** + * Audio_Init -- Initialise the sound system * + * * + * * + * * + * INPUT: window - window to send callback messages to * + * maximum bits_per_sample - 8 or 16 * + * stereo - will stereo samples be played * + * rate - maximum sample rate required * + * reverse_channels * + * * + * OUTPUT: TRUE if correctly initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * Unknown.... * + * 08-24-95 10:01am ST : Modified for Windows 95 Direct Sound * + *=============================================================================================*/ + +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels) +{ + int index; + int sample=1; + short old_bits_per_sample; + short old_block_align; + long old_bytes_per_sec; + + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + + if ( !SoundObject ){ + + /* + ** Create the direct sound object + */ + if ( DirectSoundCreate (NULL,&SoundObject,NULL) !=DS_OK ) { + Print_Sound_Error("Warning - Unable to create Direct Sound Object",window); + return (FALSE); + } + + /* + ** Give ourselves exclusive access to it + */ + if ( SoundObject->SetCooperativeLevel( window, DSSCL_PRIORITY ) != DS_OK){ + Print_Sound_Error("Warning - Unable to set Direct Sound cooperative level",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set up the primary buffer structure + */ + memset (&BufferDesc , 0 , sizeof(DSBUFFERDESC)); + BufferDesc.dwSize=sizeof(DSBUFFERDESC); + BufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; + + /* + ** Set up the primary buffer format + */ + memset (&DsBuffFormat , 0 , sizeof(WAVEFORMATEX)); + DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM; + DsBuffFormat.nChannels = (unsigned short) (1 + stereo); + DsBuffFormat.nSamplesPerSec = rate; + DsBuffFormat.wBitsPerSample = (short) bits_per_sample; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + DsBuffFormat.cbSize = 0; + + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + + /* + ** Create the primary buffer object + */ + if ( SoundObject->CreateSoundBuffer (&PrimaryBufferDesc , + &PrimaryBufferPtr , + NULL ) !=DS_OK ){ + Print_Sound_Error("Warning - Unable to create Direct Sound primary buffer",window); + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + /* + ** Set the format of the primary sound buffer + ** + */ + if (!Set_Primary_Buffer_Format()){ + + /* + ** If we failed to create a 16 bit primary buffer - try for an 8bit one + */ + if (DsBuffFormat.wBitsPerSample == 16){ + /* + ** Save the old values + */ + old_bits_per_sample = DsBuffFormat.wBitsPerSample; + old_block_align = DsBuffFormat.nBlockAlign; + old_bytes_per_sec = DsBuffFormat.nAvgBytesPerSec; + + /* + ** Set up the 8-bit ones + */ + DsBuffFormat.wBitsPerSample = 8; + DsBuffFormat.nBlockAlign = (unsigned short)( (DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Make a copy of the primary buffer description so we can reset its format later + */ + memcpy (&PrimaryBufferDesc , &BufferDesc , sizeof(DSBUFFERDESC)); + memcpy (&PrimaryBuffFormat , &DsBuffFormat , sizeof(WAVEFORMATEX)); + } + + if (!Set_Primary_Buffer_Format()){ + + /* + ** We failed to set any useful format so print up an error message and give up + */ + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + + Print_Sound_Error("Warning - Your sound card does not meet the products audio requirements",window); + + return (FALSE); + }else{ + + /* + ** OK, got an 8bit sound buffer. Not perfect but it will do + ** We still want 16 bit secondary buffers so restore those values + */ + DsBuffFormat.wBitsPerSample = old_bits_per_sample; + DsBuffFormat.nBlockAlign = old_block_align; + DsBuffFormat.nAvgBytesPerSec = old_bytes_per_sec; + } + } + + + /* + ** Start the primary sound buffer playing + ** + */if ( PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING) != DS_OK ){ + Print_Sound_Error("Unable to play Direct Sound primary buffer",window); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + SoundObject->Release(); + SoundObject = NULL; + return (FALSE); + } + + LockedData.DigiHandle=1; + + + /* + ** Initialise the global critical section object for sound thread syncronisation + */ + InitializeCriticalSection(&GlobalAudioCriticalSection); + + /* + ** Initialise the Windows timer system to provide us with a callback + ** + */ + SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC); + AudioDone = FALSE; + //_beginthread(&Sound_Thread, NULL, 16*1024, NULL); + + /* + ** Define the format for the secondary sound buffers + */ + BufferDesc.dwFlags=DSBCAPS_CTRLVOLUME; + BufferDesc.dwBufferBytes=SECONDARY_BUFFER_SIZE; + BufferDesc.lpwfxFormat = (LPWAVEFORMATEX) &DsBuffFormat; + + + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + + /* + ** Allocate once secondary direct sound buffer for each simultaneous sound effect + ** + */ + for (index = 0; index < MAX_SFX; index++) { + + SoundObject->CreateSoundBuffer (&BufferDesc , &LockedData.SampleTracker[index].PlayBuffer , NULL); + + LockedData.SampleTracker[index].PlaybackRate = rate; + LockedData.SampleTracker[index].Stereo = (stereo) ? AUD_FLAG_STEREO : 0; + LockedData.SampleTracker[index].BitSize = (bits_per_sample == 16) ? AUD_FLAG_16BIT : 0; + LockedData.SampleTracker[index].FileHandle = WW_ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + InitializeCriticalSection (&LockedData.SampleTracker[index].AudioCriticalSection); + + } + + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + * 11/02/1995 ST : Modified for Direct Sound * + *=============================================================================================*/ +void Sound_End(void) +{ + + int index; + + if (SoundObject && PrimaryBufferPtr){ + /* + ** Stop all sounds and release the Direct Sound secondary sound buffers + */ + for (index=0 ; index < MAX_SFX; index++){ + if ( LockedData.SampleTracker[index].PlayBuffer ){ + Stop_Sample (index); + LockedData.SampleTracker[index].PlayBuffer->Stop(); + LockedData.SampleTracker[index].PlayBuffer->Release(); + LockedData.SampleTracker[index].PlayBuffer = NULL; + DeleteCriticalSection(&LockedData.SampleTracker[index].AudioCriticalSection); + } + } + } + + /* + ** Stop and release the direct sound primary buffer + */ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Release(); + PrimaryBufferPtr = NULL; + } + + /* + ** Release the Direct Sound Object + */ + if (SoundObject){ + SoundObject->Release(); + SoundObject = NULL; + } + + + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + + /* + ** Remove the Windows timer event we installed for the sound callback + */ + if (SoundTimerHandle){ + timeKillEvent(SoundTimerHandle); + SoundTimerHandle = 0; + } + AudioDone = TRUE; + + /* + ** Since the timer has stopped, we are finished with our global critical section. + */ + DeleteCriticalSection(&GlobalAudioCriticalSection); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + * 11/2/95 4:09PM ST : Modified for Direct Sound * + *=============================================================================================*/ +void Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < MAX_SFX) { + + EnterCriticalSection (&GlobalAudioCriticalSection); + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + LockedData.SampleTracker[handle].Active = FALSE; + + if (!LockedData.SampleTracker[handle].IsScore) { + LockedData.SampleTracker[handle].Original = NULL; + } + + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + LockedData.SampleTracker[handle].PlayBuffer->Stop(); + } + + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = WW_ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + LeaveCriticalSection (&GlobalAudioCriticalSection); + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + DWORD status; + + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= MAX_SFX) return(FALSE); + + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + DumpBuffer = LockedData.SampleTracker[handle].PlayBuffer; + if (LockedData.SampleTracker[handle].PlayBuffer->GetStatus( &status ) == DS_OK){ + return ( (DSBSTATUS_PLAYING & status) || (DSBSTATUS_LOOPING & status) ); + }else{ + return (TRUE); + } +} + + + +/*********************************************************************************************** + * Is_Sample_Playing -- returns the play state of a sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: TRUE if sample is currently playing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:11PM ST : Commented * + *=============================================================================================*/ + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + //EnterCriticalSection(&GlobalAudioCriticalSection); + + if (!sample) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return FALSE; + } + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (TRUE); + } + } + //LeaveCriticalSection(&GlobalAudioCriticalSection); + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Sample_Playing -- stops a playing sample * + * * + * * + * * + * INPUT: ptr to sample data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:13PM ST : Commented * + *=============================================================================================*/ + +void Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} + + + + + +/*********************************************************************************************** + * Get_Free_Sample_Handle -- finds a free slot in which to play a new sample * + * * + * * + * * + * INPUT: priority of sample we want to play * + * * + * OUTPUT: Handle or -1 if none free * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:14PM ST : Added function header * + *=============================================================================================*/ + +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = MAX_SFX - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < MAX_SFX; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + + if (id == MAX_SFX) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + + if (id == -1) { + return -1; + } + + if (LockedData.SampleTracker[id].FileHandle != WW_ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = WW_ERROR; + } + + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} + + + + + +/*********************************************************************************************** + * Attempt_Audio_Restore -- tries to restore the direct sound buffers * + * * + * * + * * + * INPUT: ptr to direct sound buffer * + * * + * OUTPUT: TRUE if buffer was successfully restored * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 9:47AM ST : Created * + *=============================================================================================*/ + +BOOL Attempt_Audio_Restore (LPDIRECTSOUNDBUFFER sound_buffer) +{ + + int return_code; + DWORD play_status; + int restore_attempts=0; + + /* + ** Call the audio focus loss function if it has been set up + */ + if (Audio_Focus_Loss_Function){ + Audio_Focus_Loss_Function(); + } + + /* + ** Try to restore the sound buffer + */ + do{ + Restore_Sound_Buffers(); + return_code = sound_buffer->GetStatus ( &play_status ); + + } while (restore_attempts++<2 && return_code == DSERR_BUFFERLOST); + + return ((BOOL) ~(return_code == DSERR_BUFFERLOST)); +} + + + +/*********************************************************************************************** + * Convert_HMI_To_Direct_Sound_Volume -- Converts a linear volume value into an expotential * + * value * + * * + * This function converts a linear C&C volume in the range 0-255 (255 loudest) to a direct * + * sound volume in the range 0 to -10000 (with 0 being the loadest) * + * * + * INPUT: volume in range 0-255 * + * * + * OUTPUT: volume in range -10000 to 0 * + * * + * WARNINGS: None * + * * + * Note: The 27.685 value comes from 255 divided by the log of 10001 * + * * + * HISTORY: * + * 9/18/96 11:36AM ST : Created * + *=============================================================================================*/ +int Convert_HMI_To_Direct_Sound_Volume(int volume) +{ + if (volume == 0) return -10000; + if (volume == 255) return 0; + + float vol = (float)volume; + float retval = exp ( (255.0-vol)/27.68597374) -1; + return ((int)-retval); +} + + + + + +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + * 11/02/1995 ST : Windows Direct Sound support * + *=============================================================================================*/ +extern BOOL Any_Locked(void); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short , int id) +{ + AUDHeaderType RawHeader; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + LPVOID play_buffer_ptr; //pointer to locked direct sound buffer + LPVOID dummy_buffer_ptr; //dummy pointer to second area of locked direct sound buffer + DWORD lock_length1; + DWORD lock_length2; + DWORD play_status; + HRESULT return_code; + int retries=0; + + + if (Any_Locked()) return(0); + + st = &LockedData.SampleTracker[id]; + //EnterCriticalSection (&GlobalAudioCriticalSection); + + if (!sample || LockedData.DigiHandle == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + if (id == -1) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Fudge the sample rate to 22k + */ + if (RawHeader.Rate <24000 && RawHeader.Rate >20000) RawHeader.Rate = 22050; + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + EnterCriticalSection(&GlobalAudioCriticalSection); + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + st->Priority = (short)priority; + st->DontTouch = TRUE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + LeaveCriticalSection(&GlobalAudioCriticalSection); + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** If the sample rate , bits per sample or stereo capabilities of the buffer do not + ** match the sample then reallocate the direct sound buffer with the required capabilities + */ + if ( ( RawHeader.Rate != st->PlaybackRate ) || + ( ( RawHeader.Flags & AUD_FLAG_16BIT ) != ( st->BitSize & AUD_FLAG_16BIT ) ) || + ( ( RawHeader.Flags & AUD_FLAG_STEREO) != ( st->Stereo & AUD_FLAG_STEREO ) ) ) { + + st->Active=0; + st->Service=0; + st->MoreSource=0; + + /* + ** Stop the sound buffer playing + */ + DumpBuffer = st->PlayBuffer; + do { + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + }while (return_code == DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->PlayBuffer->Stop(); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } + + st->PlayBuffer->Release(); + st->PlayBuffer=NULL; + + DsBuffFormat.nSamplesPerSec = (unsigned short int) RawHeader.Rate; + DsBuffFormat.nChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1 ; + DsBuffFormat.wBitsPerSample = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8 ; + DsBuffFormat.nBlockAlign = (short) ((DsBuffFormat.wBitsPerSample/8) * DsBuffFormat.nChannels); + DsBuffFormat.nAvgBytesPerSec= DsBuffFormat.nSamplesPerSec * DsBuffFormat.nBlockAlign; + + /* + ** Create the new sound buffer + */ + return_code= SoundObject->CreateSoundBuffer (&BufferDesc , &st->PlayBuffer , NULL); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + /* + ** Just return if the create failed unexpectedly + ** + ** If we failed then flag the buffer as having an impossible format so it wont match + ** any sample. This will ensure that we try and create the buffer again next time its used. + */ + if (return_code!=DS_OK && return_code!=DSERR_BUFFERLOST){ + st->PlaybackRate = 0; + st->Stereo = 0; + st->BitSize = 0; + return(-1); + } + + /* + ** Remember the format of the new buffer + */ + st->PlaybackRate = RawHeader.Rate; + st->Stereo = RawHeader.Flags & AUD_FLAG_STEREO; + st->BitSize = RawHeader.Flags & AUD_FLAG_16BIT; + } + + /* + ** Fill in 3/4 of the play buffer. + */ + + // + // Stop the sound buffer playing before we lock it + // + do { + DumpBuffer = st->PlayBuffer; + return_code = st->PlayBuffer->GetStatus ( &play_status ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (play_status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING) ){ + st->Active=0; + st->Service=0; + st->MoreSource=0; + st->PlayBuffer->Stop(); + } + // + // Lock the direct sound buffer so we can write to it + // + do { + return_code = st->PlayBuffer->Lock ( 0 , + SECONDARY_BUFFER_SIZE, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + if (return_code != DS_OK) { + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (-1); + } + + // + // Decompress the sample into the direct sound buffer + // + st->DestPtr=(void*)Sample_Copy ( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + play_buffer_ptr, + SECONDARY_BUFFER_SIZE*1/4, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + + if ( st->DestPtr==(void*) (SECONDARY_BUFFER_SIZE*1/4) ){ + + // Must be more data to copy so we dont need to zero the buffer + st->MoreSource=TRUE; + st->Service=TRUE; + st->OneShot=FALSE; + } else { + + // Whole sample is in the buffer so flag that we dont need to + // copy more. Clear out the end of the buffer so that it + // goes quiet if we play past the end + st->MoreSource=FALSE; + st->OneShot=TRUE; + st->Service=TRUE; //We still need to service it so that we can stop it when + // it plays past the end of the sample data + //memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE - (unsigned)st->DestPtr ); + memset ( (char*)( (unsigned)play_buffer_ptr + (unsigned)st->DestPtr ), 0 , SECONDARY_BUFFER_SIZE/4); + } + + st->PlayBuffer->Unlock( play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + + + /* + ** + ** Set the volume of the sample. + ** + */ + st->Volume = (volume << 7); + do { + + //return_code = st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume(volume)); + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.SoundVolume*volume)/256) ); + //return_code = st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.SoundVolume) ) + // *1000) >>15 ) ); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + + /* + ** Make sure the primary sound buffer is playing + */ + if (!Start_Primary_Sound_Buffer(FALSE)){ + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return(-1); + } + + + /* + ** Set the buffers play pointer to the beginning of the buffer + */ + do { + return_code = st->PlayBuffer->SetCurrentPosition (0); + if (return_code==DSERR_BUFFERLOST){ + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + } + } while (return_code==DSERR_BUFFERLOST); + + + /* + ** Start the sample playing now. + */ + do + { + return_code = st->PlayBuffer->Play (0,0,DSBPLAY_LOOPING); + + switch (return_code){ + + case DS_OK : + EnterCriticalSection (&GlobalAudioCriticalSection); + st->Active=TRUE; + st->Handle=(short)id; + st->DontTouch = FALSE; + LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); + + case DSERR_BUFFERLOST : + if (!Attempt_Audio_Restore(st->PlayBuffer)) return(-1); + break; + + default: + st->Active=FALSE; + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); + } + + } while (return_code==DSERR_BUFFERLOST); + + //LeaveCriticalSection (&GlobalAudioCriticalSection); + return (st->Handle); +} + + + + + +/*********************************************************************************************** + * Restore_Sound_Buffers -- restore the sound buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/3/95 3:53PM ST : Created * + *=============================================================================================*/ + +void Restore_Sound_Buffers ( void ) +{ + + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Restore(); + } + + + for ( int index = 0; index < MAX_SFX; index++) { + if (LockedData.SampleTracker[index].PlayBuffer){ + LockedData.SampleTracker[index].PlayBuffer->Restore(); + } + } +} + + + + + + + + +/*********************************************************************************************** + * Set_Sound_Vol -- sets the overall volume for sampled sounds * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +/*********************************************************************************************** + * Set_Score_Vol -- sets the overall volume for music scores * + * * + * * + * * + * INPUT: volume * + * * + * OUTPUT: the previous volume setting * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:19PM ST : Added function header * + *=============================================================================================*/ +int Set_Score_Vol(int volume) +{ + int old; + SampleTrackerType *st; //ptr to SampleTracker structure + + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + + for (int index=0 ; indexIsScore && st->Active){ + //st->PlayBuffer->SetVolume (- ( ( (32768- ( (st->Volume >> 8) *LockedData.ScoreVolume) ) + // *1000) >>15 ) ); + + //st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume ( st->Volume >>7 ) ); + st->PlayBuffer->SetVolume ( Convert_HMI_To_Direct_Sound_Volume( ( LockedData.ScoreVolume*(st->Volume >>7))/256) ); + } + } + return(old); +} + + + +/*********************************************************************************************** + * Fade_Sample -- Start a sample fading * + * * + * * + * * + * INPUT: Sample handle * + * fade rate * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/2/95 4:21PM ST : Added function header * + *=============================================================================================*/ + +void Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + st = &LockedData.SampleTracker[handle]; + st->Reducer = (short) ((st->Volume / ticks)+1); + } + } +} + + + +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + + + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} + + + +/*********************************************************************************************** + * Start_Primary_Sound_Buffer -- start the primary sound buffer playing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ +extern BOOL GameInFocus; +BOOL Start_Primary_Sound_Buffer (BOOL forced) +{ + DWORD status; + + if (PrimaryBufferPtr && GameInFocus){ + if (forced){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + } else { + + if (PrimaryBufferPtr->GetStatus (&status) == DS_OK){ + if (! ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_LOOPING))){ + PrimaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + return (TRUE); + }else{ + return (TRUE); + } + } + } + } + return (FALSE); +} + + +/*********************************************************************************************** + * Stop_Primary_Sound_Buffer -- stops the primary sound buffer from playing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This stops all sound playback * + * * + * HISTORY: * + * 2/1/96 12:28PM ST : Created * + *=============================================================================================*/ + +void Stop_Primary_Sound_Buffer (void) +{ + if (PrimaryBufferPtr){ + PrimaryBufferPtr->Stop(); + PrimaryBufferPtr->Stop(); // Oh I + PrimaryBufferPtr->Stop(); // Hate Direct Sound + PrimaryBufferPtr->Stop(); // So much..... + } + + for ( int index = 0; index < MAX_SFX; index++) { + Stop_Sample(index); + } + +} + + +void Suspend_Audio_Thread(void) +{ + if (SoundThreadActive){ + //SuspendThread(SoundThreadHandle); + timeKillEvent(SoundTimerHandle); + SoundTimerHandle = NULL; + SoundThreadActive = FALSE; + } +} + + + + +void Resume_Audio_Thread(void) +{ + if (!SoundThreadActive){ + //ResumeThread(SoundThreadHandle); + SoundTimerHandle = timeSetEvent ( 1000/MAINTENANCE_RATE , 1 , Sound_Timer_Callback , 0 , TIME_PERIODIC); + SoundThreadActive = TRUE; + } +} + + diff --git a/WIN32LIB/SRCDEBUG/SOUNDLCK.CPP b/WIN32LIB/SRCDEBUG/SOUNDLCK.CPP new file mode 100644 index 0000000..27dae7d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SOUNDLCK.CPP @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDLCK.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#include +#include +#include +#include "dsound.h" +#include +#include "wwmem.h" +#include "wwstd.h" +#include "soundint.h" + + + +LockedDataType LockedData; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * INIT_LOCKED_DATA -- Initializes sound driver locked data * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void Init_Locked_Data(void) +{ + + /* + ** Initialize all of the data elements that need to be locked. + */ + LockedData.DigiHandle = -1; + LockedData.ServiceSomething = FALSE; + LockedData.MagicNumber = 0xDEAF; + LockedData.UncompBuffer = NULL; +// LockedData.StreamBufferSize = (2*SECONDARY_BUFFER_SIZE)+128; + LockedData.StreamBufferSize = (SECONDARY_BUFFER_SIZE/4)+128; + LockedData.StreamBufferCount = 16; + LockedData.SoundVolume = 255; + LockedData.ScoreVolume = 255; + LockedData._int = FALSE; + + #ifdef cuts + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Lock(&LockedData, 4096L); + DPMI_Lock(Simple_Copy, 4096L); + DPMI_Lock(Sample_Copy, 4096L); + DPMI_Lock((void *)maintenance_callback, 4096L); + DPMI_Lock((void *)DigiCallback, 4096L); + DPMI_Lock((void *)HMI_TimerCallback, 4096L); + DPMI_Lock(Audio_Add_Long_To_Pointer, 4096L); + DPMI_Lock(DPMI_Unlock, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Lock(Mem_Copy, 4096L); + DPMI_Lock(Audio_Mem_Set, 4096L); + DPMI_Lock(__GETDS, 4096L); + + /* + ** Finally lock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Lock(); + sosCODEC_Lock(); + + #endif //cuts +} + diff --git a/WIN32LIB/SRCDEBUG/STAMP.ASM b/WIN32LIB/SRCDEBUG/STAMP.ASM new file mode 100644 index 0000000..2b4c720 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/STAMP.ASM @@ -0,0 +1,600 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : STAMP.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 23, 1993 * +;* * +;* Last Update : August 23, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +INCLUDE "stamp.inc" + +global C Init_Stamps:near +global LastIconset:dword +global MapPtr:dword +global IconCount:dword +global IconSize:dword +global StampPtr:dword + + DATASEG + + +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + EVEN + + CODESEG + + +GLOBAL C Buffer_Draw_Stamp:near +GLOBAL C Buffer_Draw_Stamp_Clip:near + +; 256 color icon system. + +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* + PROC Init_Stamps C near USES eax ebx edx edi + ARG icondata:DWORD + + ; Verify legality of parameter. + cmp [icondata],0 + je short ??fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short ??fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[(IControl_Type edi).Count] + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[(IControl_Type edi).Width] + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[(IControl_Type edi).Height] + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[(IControl_Type edi).Map] + add eax,edi + mov [MapPtr],eax + +??nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[(IControl_Type edi).Icons] + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[(IControl_Type edi).TransFlag] + mov [IsTrans],eax + +??fini: + ret + + ENDP Init_Stamps + + +;*********************************************************** + + +;*********************************************************** +; DRAW_STAMP +; +; VOID cdecl Buffer_Draw_Stamp(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC Buffer_Draw_Stamp C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset + call Init_Stamps C,eax +??noreset: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap: + cmp ebx,[IconCount] + jae ??out + mov [icon],ebx ; Updated icon number. + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[IconWidth] + mov [modulo],eax + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. +;;; mov edx,[remap] + mov ebx,[remap] + xor eax,eax +??xrowloop: + push ecx + mov ecx,[iwidth] + +??xcolumnloop: + lodsb +;;; mov ebx,edx +;;; add ebx,eax +;;; mov al,[ebx] ; New real color to draw. + xlatb + or al,al + jz short ??xskip1 ; Transparency skip check. + mov [edi],al +??xskip1: + inc edi + loop ??xcolumnloop + + pop ecx + add edi,[modulo] + loop ??xrowloop + jmp short ??out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloop + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + shr ebx,2 + mov edx,[modulo] + mov eax,[iwidth] + shr eax,2 +??loop1: + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + dec ebx + jnz ??loop1 + jmp short ??out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloop: + push ecx + mov ecx,[iwidth] + +??columnloop: + lodsb + or al,al + jz short ??skip1 ; Transparency check. + mov [edi],al +??skip1: + inc edi + loop ??columnloop + + pop ecx + add edi,[modulo] + loop ??rowloop + + ; Cleanup and exit icon drawing routine. +??out: + popad + ret + + ENDP Buffer_Draw_Stamp + + + + +;*********************************************************** +; DRAW_STAMP_CLIP +; +; VOID cdecl MCGA_Draw_Stamp_Clip(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap, LONG min_x, LONG min_y, LONG max_x, LONG max_y); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC Buffer_Draw_Stamp_Clip C near + + ARG this_object:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + ARG min_x:DWORD ; Clipping rectangle boundary + ARG min_y:DWORD ; Clipping rectangle boundary + ARG max_x:DWORD ; Clipping rectangle boundary + ARG max_y:DWORD ; Clipping rectangle boundary + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL skip:DWORD ; amount to skip per row of icon data + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out2 + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset2 + call Init_Stamps C,eax +??noreset2: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap2 + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap2: + cmp ebx,[IconCount] + jae ??out2 + mov [icon],ebx ; Updated icon number. + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Update the clipping window coordinates to be valid maxes instead of width & height + ; , and change the coordinates to be window-relative + mov ebx,[min_x] + add [max_x],ebx + add [x_pixel],ebx ; make it window-relative + mov ebx,[min_y] + add [max_y],ebx + add [y_pixel],ebx ; make it window-relative + + ; See if the icon is within the clipping window + ; First, verify that the icon position is less than the maximums + mov ebx,[x_pixel] + cmp ebx,[max_x] + jge ??out2 + mov ebx,[y_pixel] + cmp ebx,[max_y] + jge ??out2 + ; Now verify that the icon position is >= the minimums + add ebx,[IconHeight] + cmp ebx,[min_y] + jle ??out2 + mov ebx,[x_pixel] + add ebx,[IconWidth] + cmp ebx,[min_x] + jle ??out2 + + ; Now, clip the x, y, width, and height variables to be within the + ; clipping rectangle + mov ebx,[x_pixel] + cmp ebx,[min_x] + jge ??nominxclip + ; x < minx, so must clip + mov ebx,[min_x] + sub ebx,[x_pixel] + add esi,ebx ; source ptr += (minx - x) + sub [iwidth],ebx ; icon width -= (minx - x) + mov ebx,[min_x] + mov [x_pixel],ebx + +??nominxclip: + mov eax,[IconWidth] + sub eax,[iwidth] + mov [skip],eax + + ; Check for x+width > max_x + mov eax,[x_pixel] + add eax,[iwidth] + cmp eax,[max_x] + jle ??nomaxxclip + ; x+width is greater than max_x, so must clip width down + mov eax,[iwidth] ; eax = old width + mov ebx,[max_x] + sub ebx,[x_pixel] + mov [iwidth],ebx ; iwidth = max_x - xpixel + sub eax,ebx + add [skip],eax ; skip += (old width - iwidth) +??nomaxxclip: + ; check if y < miny + mov eax,[min_y] + cmp eax,[y_pixel] ; if(miny <= y_pixel), no clip needed + jle ??nominyclip + sub eax,[y_pixel] + sub ecx,eax ; height -= (miny - y) + mul [IconWidth] + add esi,eax ; icon source ptr += (width * (miny - y)) + mov eax,[min_y] + mov [y_pixel],eax ; y = miny +??nominyclip: + ; check if (y+height) > max y + mov eax,[y_pixel] + add eax,ecx + cmp eax,[max_y] ; if (y + height <= max_y), no clip needed + jle ??nomaxyclip + mov ecx,[max_y] ; height = max_y - y_pixel + sub ecx,[y_pixel] +??nomaxyclip: + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this_object] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[iwidth] + mov [modulo],eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck2 + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + mov ebx,[remap] + xor eax,eax +??xrowloopc: + push ecx + mov ecx,[iwidth] + +??xcolumnloopc: + lodsb + xlatb + or al,al + jz short ??xskip1c ; Transparency skip check. + mov [edi],al +??xskip1c: + inc edi + loop ??xcolumnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??xrowloopc + jmp short ??out2 + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck2: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloopc + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + mov edx,[modulo] + mov eax,[iwidth] + + ; + ; Optimise copy by dword aligning the destination + ; +??loop1c: + push eax + rept 3 + test edi,3 + jz ??aligned + movsb + dec eax + jz ??finishedit + endm +??aligned: + mov ecx,eax + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,3 + rep movsb + +??finishedit: + add edi,edx + add esi,[skip] + pop eax + + dec ebx + jnz ??loop1c + jmp short ??out2 + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloopc: + push ecx + mov ecx,[iwidth] + +??columnloopc: + lodsb + or al,al + jz short ??skip1c ; Transparency check. + mov [edi],al +??skip1c: + inc edi + loop ??columnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??rowloopc + + ; Cleanup and exit icon drawing routine. +??out2: + popad + ret + + ENDP Buffer_Draw_Stamp_Clip + + END diff --git a/WIN32LIB/SRCDEBUG/STMPCACH.ASM b/WIN32LIB/SRCDEBUG/STMPCACH.ASM new file mode 100644 index 0000000..29e149e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/STMPCACH.ASM @@ -0,0 +1,284 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : STAMP.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 23, 1993 * +;* * +;* Last Update : August 23, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +JUMPS + +MAX_CACHED_ICONS=300 +ICON_WIDTH =24 +ICON_HEIGHT =24 +MAX_ICON_SETS =100 + + +global C IconPointers:dword +global C Init_Stamps:near +global C IconCacheLookup:word +global LastIconset:dword +global MapPtr:dword +global IconCount:dword +global IconSize:dword +global StampPtr:dword +global IconEntry:dword +global IconData:dword + +global Clear_Icon_Pointers_:near +global Cache_Copy_Icon_:near +global Is_Icon_Cached_:near +global Get_Icon_Index_:near +global Get_Free_Index_:near +global Cache_New_Icon_:near +global Is_Stamp_Registered_:near +global Get_Free_Cache_Slot_:near + + + struc IconSetType + + IconSetPtr dd ? + IconListOffset dd ? + + ends + +global C IconSetList:IconSetType + + codeseg + + + +;************************************************************************************************ +;* Cache_Copy_Icon -- copy an icon to its video memory cache * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* edx - ptr to video surface * +;* ebx - pitch of video surface * +;* * +;* OUTPUT: none * +;* * +;* PROTO: extern "C" Cache_Copy_Icon (void const *icon_ptr , * +;* VideoSurfaceDescription.lpSurface , * +;* VideoSurfaceDescription.lPitch); * +;* * +;* HISTORY: * +;* 11/8/95 3:16PM ST: Created * +;*==============================================================================================* + +proc Cache_Copy_Icon_ near + pushad + + mov esi,eax + mov edi,edx + sub ebx,ICON_WIDTH + + mov dl,ICON_HEIGHT ;icon height + +??each_line_lp: mov ecx,ICON_WIDTH/4 + rep movsd + lea edi,[edi+ebx] + dec dl + jnz ??each_line_lp + + popad + ret + +endp Cache_Copy_Icon_ + + + + + +;************************************************************************************************ +;* Is_Icon_Cached -- has an icon been cached? If not, is it cacheable? * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* edx - icon number * +;* * +;* OUTPUT: eax - index of cached icon or -1 if not cached * +;* * +;* PROTO: extern "C" int Is_Icon_Cached (void const *icon_data , int icon); * +;* * +;* HISTORY: * +;* 11/8/95 2:16PM ST: Created * +;*==============================================================================================* + +proc Is_Icon_Cached_ near + + mov [IconData],eax ;save the icon data ptr for later + push edx + test eax,eax + je ??out + +; Initialize the stamp data if necessary. + cmp [LastIconset],eax + je short ??noreset + call Init_Stamps C,eax + +; Determine if the icon number requested is actually in the set. +; Perform the logical icon to actual icon number remap if necessary. +??noreset: cmp [MapPtr],0 + je short ??notmap + push edi + mov edi,[MapPtr] + mov dl,[edi+edx] + pop edi + +??notmap: cmp edx,[IconCount] + jl ??in_range + pop edx + mov eax,-1 + ret + +; See if the stamp is registered - if not then it cant be cached +??in_range: mov eax,[IconData] + call Is_Stamp_Registered_ + cmp eax,-1 + jnz ??got_entry + pop edx + ret + +; Stamp is registered - if its cached already then just return the index +??got_entry: mov eax,[(IconSetType eax).IconListOffset] + cmp [word eax+edx*2+IconCacheLookup],-1 + jz ??not_cached + +; it is cached and [eax+edx] is the index + movzx eax,[word eax+edx*2+IconCacheLookup] + pop edx + ret + + + +; +; The stamps set is registered but we havn't seen this stamp before +; so try caching it +; + +??not_cached: mov [IconEntry],eax + add [IconEntry],edx + add [IconEntry],edx + call Get_Free_Cache_Slot_ + test eax,eax + jge ??got_free_slot + pop edx ;eax is -1 here anyway + ret + +; We found a free caching slot so try caching the stamp into it +??got_free_slot:imul edx,[IconSize] + add edx,[StampPtr] + push eax + call Cache_New_Icon_ ;takes icon index in eax, ptr to icon in edx + test eax,eax + jz ??cache_failed + + +; Success! Add the index into the table + pop eax + mov edx,[IconEntry] + mov [edx+IconCacheLookup],ax + and eax,0ffffh + pop edx + ret + +; Couldnt cache the new Icon - return -1 to say icon isnt cached +??cache_failed: pop eax +??out: pop edx + mov eax,-1 + ret + + +endp Is_Icon_Cached_ + + + + + + + +;************************************************************************************************ +;* Is_Stamp_Registered -- has an icon's set been previously registered? * +;* * +;* * +;* INPUT: eax - ptr to icon_data * +;* * +;* OUTPUT: eax - ptr to registration entry or -1 if not registered * +;* * +;* PROTO: extern "C" int Is_Stamp_Registered (void const *icon_data); * +;* * +;* HISTORY: * +;* 11/10/95 10:00AM ST: Created * +;*==============================================================================================* + +proc Is_Stamp_Registered_ + + push edi + push ecx + mov edi,offset IconSetList + mov ecx,MAX_ICON_SETS + +??each_set_lp: cmp eax,[edi] + jz ??got_icon_set + add edi,size IconSetType + dec ecx + jnz ??each_set_lp + mov eax,-1 + pop ecx + pop edi + ret + +??got_icon_set: mov eax,edi + pop ecx + pop edi + ret + +endp Is_Stamp_Registered_ + + + + + + + + dataseg + + +IconEntry dd 0 ;Temporary pointer to icon index entry in table +IconData dd 0 ;Temporary ptr to icon set data + + + +end diff --git a/WIN32LIB/SRCDEBUG/SZREGION.ASM b/WIN32LIB/SRCDEBUG/SZREGION.ASM new file mode 100644 index 0000000..affce31 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/SZREGION.ASM @@ -0,0 +1,101 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Calculate size of an MCGA region * +;* * +;* File Name : REGIONSZ.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : March 1, 1995 * +;* * +;* Last Update : March 1, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Size_Of_Region - calculate graphic buffer region size * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +GLOBAL Buffer_Size_Of_Region : NEAR + +CODESEG + +;*************************************************************************** +;* VVPC::Size_Of_Region - calculate buffer region size * +;* * +;* INPUT: DWORD the width of the region * +;* * +;* DWORD the height of the region * +;* * +;* OUTPUT: calculated size of the region (size = width * height) * +;* * +;* * +;* HISTORY: * +;* 03/01/1995 BWG : Created. * +;*=========================================================================* + PROC Buffer_Size_Of_Region C near + USES ebx,ecx,edx + + ARG this_object:DWORD ; this is a member function + ARG region_width:DWORD ; width of region + ARG region_height:DWORD ; height of region + + ;*=================================================================== + ; Get the viewport information + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + + ;*=================================================================== + ; Verify that the width is legal + ;*=================================================================== + mov eax,[region_width] ; find the width + cmp eax,edx ; is it too wide? + jb short ??wok ; if not, leave it alone + mov eax,edx ; otherwise clip it + + ;*=================================================================== + ; Verify that the height is ok + ;*=================================================================== +??wok: mov ebx,[region_height] ; get the height + cmp ebx,ecx ; is it too tall? + jb ??hok ; if not, leave it alone + mov ebx,ecx ; otherwise clip it + + ;*=================================================================== + ; Now multiply 'em to calculate the size of the region + ;*=================================================================== +??hok: mul ebx ; size = w * h + + ret + ENDP Buffer_Size_Of_Region + + END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/TEST.CPP b/WIN32LIB/SRCDEBUG/TEST.CPP new file mode 100644 index 0000000..e4c28fd --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TEST.CPP @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "audio.h" + + + +int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) +{ + + + + + + + File_Stream_Sample_Vol("file", 0, 0); + + return (0); +} \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/TEXTPRNT.ASM b/WIN32LIB/SRCDEBUG/TEXTPRNT.ASM new file mode 100644 index 0000000..730a3f0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TEXTPRNT.ASM @@ -0,0 +1,436 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + + +GLOBAL FontPtr:DWORD +GLOBAL Text_Print:NEAR + +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + +LOCALS ?? + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* TEXT_PRINT -- Assembly text print routine. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/28/1994 SKB : Created. * +;*=========================================================================* + PROC Text_Print C near + USES ebx,ecx,edx,esi,edi + ARG vpselector:WORD + ARG vpoffset:DWORD + ARG vpwidth:DWORD + ARG vpheight:DWORD + ARG vpxadd:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + movzx eax,[vpselector] + mov es,ax ; Set up selector to write to. + + mov eax,[vpwidth] ; get the width of the viewport + add eax,[vpxadd] ; add amount to add to get to left edge of next line. + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[vpoffset] ; get start of the viewport + add edi,eax ; add x,y position to start of vp to get starting row address. + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + ; Now go into the line feed code..... + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Text_Print + + + +;*********************************************************** + + END diff --git a/WIN32LIB/SRCDEBUG/TIMER.CPP b/WIN32LIB/SRCDEBUG/TIMER.CPP new file mode 100644 index 0000000..a9aecc1 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TIMER.CPP @@ -0,0 +1,203 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + TickType=timer; + + if (on && TimerSystemOn) Start(); +} + + + + +/*********************************************************************************************** + * TC:Get_Ticks -- return the number of ticks on the system or user timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:17PM ST : Created * + *=============================================================================================*/ + +long TimerClass::Get_Ticks ( void ) + +{ + if ( WindowsTimer ){ + switch ( TickType ){ + + case BT_SYSTEM : + return ( WindowsTimer->Get_System_Tick_Count() ); + + case BT_USER : + return ( WindowsTimer->Get_User_Tick_Count() ); + + } + } + return 0; +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/TIMERA.ASM b/WIN32LIB/SRCDEBUG/TIMERA.ASM new file mode 100644 index 0000000..cee3b7f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TIMERA.ASM @@ -0,0 +1,1014 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer stuff * +;* * +;* File Name : TIMERA.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 6, 1994 * +;* * +;* Last Update : March 14, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Timer_Size -- return size of real mode timer code. * +;* Increment_Tick_Count -- Increments WW system timer. * +;* Timer_Interrupt -- Temp routine to mimic a timer system calling our. * +;* Install_Timer_Interrupt -- Installs the timer interrupt routine. * +;* Remove_Timer_Interrupt -- Removes the timer interrupt vectors. ** +;* Set_Timer_Frequency -- Set the frequency of the timer. * +;* Set_Timer_Rate -- Set the rate of the timer. * +;* Get_System_Tick_Count -- Returns the system tick count. * +;* Get_User_Tick_Count -- Get tick count of user clock. * +;* Increment_Timers -- Increments system and user timers. * +;* Get_Num_Interrupts -- Returns the number of interrupts that have occured* +;* Timer_Interrupt_Func -- Handles core timer code * +;* Disable_Timer_Interrupt -- Disables at the hardware level * +;* Enable_Timer_Interrupt -- Enables at the hardware level * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +DPMI_INTR equ 31h +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// + +SYSTEM_TIMER_FREQ equ 60 ; Frequency of system timer. + +;******************************************************************** +; There are two ways to call our interrupt chain in protected mode. +; The obvious way it to call the address that we replaced in the +; PM interrupt chain. This method is a little difficult but works and +; should always work. + + +;******************************************************************** +; The RM and PM interrupts can be installed at the same time or seperately. +; Installing at the same time is the best method, the other method +; can be used to faciliated debugging when you only want one or the other +; called. Both methods work. +; -SKB July 21, 1994. +INSTALL_SEPERATE equ FALSE + + +INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0 +CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts. +IRQ0INTNUM EQU 08h ; IRQ0 interrupt vector number. +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT + +TIMER_CONST EQU 1193180 ; TIMER_CONST / FREQ = rate to set 8259 chip + +TIMERCMDREG EQU 043H ; timer command register port. +TIMER0PORT EQU 040H ; timer channel 0 port +TIMETYPE EQU 036H ; type of timer. + ; 36H = 0011 0110 + ; -- select channel 0 + ; -- read/load low byte then high byte for timer + ; --- timer mode 3 + ; - 0 - binary , 1 - BCD data + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Structs //////////////////////////////////////// + + +; Structure of memory in real mode handler. +; This is at the very start of the real mode code. +; This information may not change unless the real mode version is also changed. + +STRUC TimerType +; For speed, PM uses a DD while RM used DW +; For speed, SysRate and SysError are DD in PM and are DW in real mode. + + TrueRate DD ? ; True rate of clock. (only use word) + SysTicks DD ? ; Tick count of timer. + SysRate DD ? ; Desired rate of timer. + SysError DD ? ; Amount of error in clock rate for desired frequency. + SysCurRate DW ? ; Keeps track of when to increment timer. + SysCurError DW ? ; Keeps track of amount of error in timer. + + UserTicks DD ? ; Tick count of timer. + UserRate DD ? ; Desired rate of timer. + UserError DD ? ; Amount of error in clock rate for desired frequency. + UserCurRate DW ? ; Keeps track of when to increment timer. + UserCurError DW ? ; Keeps track of amount of error in timer. + + DosAdder DW ? ; amount to add to DosFraction each interrupt. + DosFraction DW ? ; Call dos when overflowed. + + OldRMI DD ? ; The origianl RM interrupt seg:off. + OldPMIOffset DD ? ; The origianl PM interrupt offset + OldPMISelector DD ? ; The original PM interrupt segment. + + CodeOffset DW ? ; Offset of the code in the RM stuff. + CallRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + + PMIssuedInt DD 0 ; PM signals RM to just call Int chain. + +; These are just used for information on testing. When all is done, they can +; be removed, but why? The don't add too much proccessing time and can +; be useful. + NumPMInts DD ? ; Number of PM interrupts + NumRMInts DD ? ; Number of RM interrupts. + +ENDS + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes ////////////////////////////////////// + +GLOBAL Get_System_Tick_Count:NEAR +GLOBAL Get_User_Tick_Count:NEAR +GLOBAL Get_RM_Timer_Address:NEAR +GLOBAL Get_RM_Timer_Size:NEAR +GLOBAL Set_Timer_Frequency:NEAR +GLOBAL Timer_Interrupt:NEAR +GLOBAL Install_Timer_Interrupt:NEAR +GLOBAL Remove_Timer_Interrupt:NEAR +GLOBAL Get_Num_Interrupts:NEAR +GLOBAL Timer_Interrupt_Func:FAR +GLOBAL Disable_Timer_Interrupt:NEAR +GLOBAL Enable_Timer_Interrupt:NEAR + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Data //////////////////////////////////////// + DATASEG + +; The current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "timereal.ibn" +LABEL RealBinEnd BYTE + + + + +LABEL LockedDataStart BYTE +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. +LABEL LockedDataEnd BYTE + + +InitFlags DD 0 ; Flags to indicate what has been initialized. + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1h ; Allocation of RM was successful. +IF_SET_VECTORS equ 2h ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4h ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8h ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10h ; Timer rate was changed. +IF_FUNCTIONAL equ 20h ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40h ; Timer is in and functional. + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Code //////////////////////////////////////// + + CODESEG + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Init routines //////////////////////////////////////// + +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_TIMER_SIZE -- return size of real mode timer code. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + +;*************************************************************************** +;* SET_TIMER_RATE -- Set the rate of the timer. * +;* * +;* * +;* INPUT: ebx = rate to set timer were rate = 1193180 / freq * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/11/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Rate Near + + push eax + pushf ; to save int enable flag + cli ; disable interupts while setting up swapper + + mov al,TIMETYPE ; setup to modify timer 0 + out TIMERCMDREG,al ; send command. + mov eax,ebx ; get rate. + out TIMER0PORT,al ; output low byte + mov al,ah + out TIMER0PORT,al ; output high byte + + sti + popf ; get int enable flag. + pop eax + + ret +ENDP Set_Timer_Rate + +;*************************************************************************** +;* SET_TIMER_FREQUENCY -- Set the frequency of the timer. * +;* * +;* * +;* INPUT: INT Frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Frequency C NEAR + USES eax,ebx,ecx,edx + ARG freq:DWORD + LOCAL clockrate:DWORD + LOCAL clockfreq:DWORD + + test [InitFlags],IF_FUNCTIONAL ; Is our timer system installed? + jz ??timer_not_installed ; if not, this is not legal. + + + ; Find out the greater of the frequencies (user or system.) + ; Assign the True rate value based of of that. + ; store the max frequency in ecx. + mov ecx,[freq] ; get user frequency. + cmp ecx,SYSTEM_TIMER_FREQ ; compare it with system frequency + jg ??user_is_fastest ; is user frequency faster? + mov ecx,SYSTEM_TIMER_FREQ ; no, set clock freq to system frequency. +??user_is_fastest: + + ; now get the rate that the clock will be set at. + ; ecx is still max frequency. + mov esi,[RealModePtr] + + mov eax,TIMER_CONST ; get the clock constant value. + xor edx,edx ; zero for divide. + div ecx ; rate = TC/freq => eax = eax/ecx; + mov [(TimerType PTR esi).TrueRate],eax ; Set our true rate. + mov ebx,eax ; save for later. DO NOT USE UNTIL CALL + + ; Set up variables to call DOS correctly. + ; When DosFraction overflows, DOS is called. + mov [(TimerType PTR esi).DosAdder],ax ; Init count to until call dos. + mov [(TimerType PTR esi).DosFraction],0 ; init the fraction. + + ; now set up the system timer. + mov ecx,SYSTEM_TIMER_FREQ ; get frequency. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).SysCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).SysCurError],ax ; Init current stuff. + mov [(TimerType PTR esi).SysRate],eax ; Save rate of timer + mov [(TimerType PTR esi).SysError],edx ; Save error of timer + ; Do not set SysTicks to zero since it always has the same frequency. It + ; should be zero out only when the system clock is installed. + + ; now set up the user timer. + mov ecx,[freq] ; get frequency of user timer. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).UserCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).UserCurError],ax; Init current stuff. + mov [(TimerType PTR esi).UserRate],eax ; Save rate of timer + mov [(TimerType PTR esi).UserError],edx ; Save error of timer + mov [(TimerType PTR esi).UserTicks],0 ; User timer sets to zero when freq change. + + ; Call function to set the rate of the chip, ebx = rate from above. + call Set_Timer_Rate + +??timer_not_installed: + + ret + + ENDP + + +;*************************************************************************** +;* INSTALL_TIMER_Interrupt -- Installs the timer interrupt routine. * +;* * +;* * +;* INPUT: VOID * pointer to RM binary in PM memory. * +;* LONG Size of RM binary. * +;* INT frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Install_Timer_Interrupt C Near + USES ebx,ecx,edx + ARG rm_ptr:DWORD + ARG rm_size:DWORD + ARG freq:DWORD + ARG partial:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + mov [ RealModeSel ] , edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated, just in + ; case it needs to be. + + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[rm_size] ; edi will have size of region in bytes. + mov [RealModeSize],edi ; save off the size for the unlock + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr] + mov esi,[rm_ptr] ; Set up our source pointer. + or [InitFlags],IF_ALLOC_RM ; set successful + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi ; get real mode 32 offset. + shl eax,12 ; make seg in high eax. + mov ax,[(TimerType PTR esi).CallRMIntOffset] ; create RM addr of call chain. + mov [(TimerType PTR esi).CallRMIntAddr],eax ; save it for use in PM int. + + cmp [partial],0 + jne ??partial_exit + + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(TimerType PTR esi).OldPMIOffset],edx ; save offset. + mov [(TimerType PTR esi).OldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, CX:DX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(TimerType PTR esi).OldRMI],ecx + + ;========================================================================== + ; only separate method of installation is posible. + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + mov eax, 0205h + mov bl,IRQ0INTNUM + mov cx , cs + lea edx, [Timer_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(TimerType PTR esi).CodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ; now set the frequency. + mov eax,[freq] + or [InitFlags],IF_FUNCTIONAL + push eax + call NEAR Set_Timer_Frequency + mov [(TimerType PTR esi).SysTicks],0 ; Only place SysTicks in inited. + mov [(TimerType PTR esi).UserTicks],0 ; Timers start off on same foot. + pop eax + or [InitFlags],IF_RATE_CHANGE + + ; we have finished with success. +??partial_exit: + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + +;*************************************************************************** +;* REMOVE_TIMER_INTERRUPT -- Removes the timer interrupt vectors. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Remove_Timer_Interrupt C NEAR + USES ebx,ecx,edx + + ; check if timer was previosly install + mov esi,[RealModePtr] + test esi,esi + jz ??error + + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;==================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov edx,[(TimerType esi).OldRMI] ; get the RM address. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ0INTNUM + mov ecx,[(TimerType esi).OldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(TimerType esi).OldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + +??vectors_not_set: + + ; Call function to set the rate of the chip, ebx = rate. + test [InitFlags],IF_RATE_CHANGE ; was it changed? + jz ??rate_not_changed + mov ebx,0FFFFh ; back to 18.2 time per second. + call Set_Timer_Rate ; call function to set timer. +??rate_not_changed: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + + mov eax , 0601h + mov ecx, [RealModePtr] + mov edi, [RealModeSize] + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error + +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov eax,1 ; signal success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Access routines ////////////////////////////////////// + + +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + +;*************************************************************************** +;* GET_SYSTEM_TICK_COUNT -- Returns the system tick count. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_System_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + mov eax,[(TimerType PTR esi).SysTicks] + ret + + ENDP + + +;*************************************************************************** +;* GET_USER_TICK_COUNT -- Get tick count of user clock. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_User_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + mov eax,[(TimerType PTR esi).UserTicks] + ret + + ENDP + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Interrupt routines /////////////////////////////////// + +; These macros are placed here to handle to duplicate code in 2 methods in +; the Timer_Interrupt function. + + +MACRO SET_DS_ESI_TO_RM + ; Sets DS : ES to point to DGROUP _DATA selector + ; Set esi to point to RealModePtr + ; Corrupts eax + + mov ax , _DATA + mov ds , ax + mov es , ax + mov esi,[RealModePtr] ; Point to start of RM data. +ENDM + + +MACRO INCREMENT_TIMERS + ; Expects ESI to point to real mode memory. + + inc [(TimerType PTR esi).NumPMInts] ; For testing. + + mov eax,[(TimerType PTR esi).TrueRate] ; Get the rate of the PC clock. + sub [(TimerType PTR esi).SysCurRate],ax ; Sub from our rate counter. + ja ??end_sys ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).SysRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).SysError] ; Get amount of error. + add [(TimerType PTR esi).SysCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).SysCurError],cx ; Subtract err from error count. + jb ??error_adj_sys ; If wrapped don't inc. + inc [(TimerType PTR esi).SysTicks] ; increment the timer. + jmp short ??end_sys +??error_adj_sys: + add [(TimerType PTR esi).SysCurError],bx ; reajust the error by timer rate. +??end_sys: + sub [(TimerType PTR esi).UserCurRate],ax ; Sub from our rate counter. + ja ??end_user ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).UserRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).UserError] ; Get amount of error. + add [(TimerType PTR esi).UserCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).UserCurError],cx; Subtract err from error count. + jb ??error_adj_user ; If wrapped don't inc. + inc [(TimerType PTR esi).UserTicks] ; increment the timer. + jmp short ??end_user +??error_adj_user: + add [(TimerType PTR esi).UserCurError],bx; reajust the error by timer rate. +??end_user: + +ENDM + + +MACRO ENABLE_CLOCK_INT + ; Signal 8259 that we are done and that it can bug us again. + ; Corrupts al. + + mov al,CLEARISR ; Signal EOI + sti ; enable interrupts. + out INTCHIP0,al ; 8259 interrupt chip controller 0 +ENDM + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* TIMER_INTERRUPT -- Temp routine to mimic a timer system calling ours * +;* This is a temp routine to call our tick count routine. It will be * +;* replaced once another timer system is put in (such as HMI). * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + PROC Timer_Interrupt Near +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +; This routine will do what it needs to, +; then it will decide if the old vector should be called. +; if so, it calls it and never returns to this function. +; if not, we do our own return. +; the method for doing this is found in: +; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142" +; It says: +; 1 - Execute a PUSHFD to save the current flags. +; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler +; 3 - Push register I use. +; 4 - Do any processing. +; 5 - Put the address of the original handler in the reserved slot. +; 6 - Pop saved register values +; 7 - Execute an IRETD to transfer control to original handler. + + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ; Step 3 continued. Push used varables. + pushad + push fs gs es ds + + + ; Step 4. Now do processing before I chain. + ; Set up ds:esi to point at start of real memory block (data is first) + + call far Timer_Interrupt_Func + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + + ; Now take care of calling the old DOS timer interrupt vector. + ; This must be the last operation of this module since if we call + ; DOS, we will never return. + mov ax,[(TimerType PTR ds:esi).DosAdder] + add [(TimerType PTR esi).DosFraction],ax + jnc ??no_dos_call ; if not, skip the call. + + ; Tell RM that we forced the int and not to update timers. + mov [(TimerType PTR esi).PMIssuedInt],1 ; Make it TRUE + + ; Step 5. + ; Now it is time to set up for the call by returning by poking + ; the old interrupt handle address in. + mov eax,[(TimerType PTR esi).OldPMIOffset] ; Get orig offset. + mov ebx,[(TimerType PTR esi).OldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ; Step 6. + pop ds es gs fs + popad + pop ebp + + ; Step 7. + iretd ; transfer control to original handler. + +??no_dos_call: + + ENABLE_CLOCK_INT + + ; Restore all registers. + pop ds es gs fs + popad + + pop ebp + add esp,8 + popfd + + iretd +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +;////////////////////////////////////////////////////////////////////////////////////////////////////// + ENDP + + + +;*************************************************************************** +;* TIMER_INTERRUPT_FUNC -- Handles core timer code * +;* * +;* This function exists so that we can call it from the core HMI driver * +;* code when our timer interrupt has not been installed. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + PROC Timer_Interrupt_Func C Far + pushfd ; save off the flags + pushad ; save off the main registers + push fs gs es ds ; save off the seg registers + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + INCREMENT_TIMERS ; Increment Westwoods timers + + pop ds es gs fs + popad + popfd + retf + ENDP + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* DISABLE_TIMER_INTERRUPT_ONLY -- Disables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Disable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + or al,001h ; setup to disable irq 0 + out 021h,al ; disable irq 0 + + popf ; possibly enable all interrupts (except 0) + pop eax + retf + ENDP + + +;*************************************************************************** +;* ENABLE_TIMER_INTERRUPT_ONLY -- Enables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Enable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + and al,0FEh ; setup to enable irq 0 + out 021h,al ; enable irq 0 + + popf ; possibly enable all interrupts + pop eax + retf + ENDP + + + END + + + diff --git a/WIN32LIB/SRCDEBUG/TIMERDWN.CPP b/WIN32LIB/SRCDEBUG/TIMERDWN.CPP new file mode 100644 index 0000000..993a8ef --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TIMERDWN.CPP @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/WIN32LIB/SRCDEBUG/TIMEREAL.ASM b/WIN32LIB/SRCDEBUG/TIMEREAL.ASM new file mode 100644 index 0000000..5c4524f --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TIMEREAL.ASM @@ -0,0 +1,241 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer interrupt for RM * +;* * +;* File Name : TIMEREAL.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 8, 1994 * +;* * +;* Last Update : July 8, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +MODEL TINY +P386N + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// + +INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0 +CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts. + +CODESEG +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Data //////////////////////////////////////// +; This information may not change unless the protected mode version is +; also changed. +; For speed, PM uses a DD while RM used DW +TrueRate DW 0 ; True rate of clock. (only use word) +TrueRateNA DW 0 ; used for speed in prot mode. + +; For speed, SysRate and SysError are DD in PM and are DW in real mode. +SysTicks DD 0 ; Tick count of timer. +SysRate DD 0 ; Desired rate of timer. +SysError DD 0 ; Amount of error in clock rate for desired frequency. +SysCurRate DW 0 ; Keeps track of when to increment timer. +SysCurError DW 0 ; Keeps track of amount of error in timer. + +UserTicks DD 0 ; Tick count of timer. +UserRate DD 0 ; Desired rate of timer. +UserError DD 0 ; Amount of error in clock rate for desired frequency. +UserCurRate DW 0 ; Keeps track of when to increment timer. +UserCurError DW 0 ; Keeps track of amount of error in timer. + +DosAdder DW 0 ; amount to add to DosFraction each interrupt. +DosFraction DW 0 ; Call dos when overflowed. + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Timer_Interrupt_Handler ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +PMIssuedInt DD 0 ; PM signals RM to just call Int chain. + +; These are just used for information on testing. When all is done, they can +; be removed, but why? The don't add too much proccessing time and can +; be useful. +NumPMInts DD 0 ; Number of PM interrupts +NumRMInts DD 0 ; Number of RM interrupts. + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Code //////////////////////////////////////// + + +; This is here for easy comparison with the PM version. +MACRO INCREMENT_TIMERS + inc [NumRMInts] ; For testing. + + + ; At this point, increment the system and user timers. + mov ax,[TrueRate] ; Get the rate of the PC clock. + + sub [SysCurRate],ax ; Sub from our rate counter. + ja ??end_sys ; If !below zero, do not inc. + + mov bx,[WORD PTR SysRate] ; Get rate of timer. + mov dx,[WORD PTR SysError] ; Get amount of error. + add [SysCurRate],bx ; Add rate to the current. + + sub [SysCurError],dx ; Subtract err from error count. + jb ??error_adj_sys ; If !below 0, increment counter. + + inc [SysTicks] ; increment the timer. + jmp short ??end_sys ; don't modify SysCurError. +??error_adj_sys: + add [SysCurError],bx ; reajust the error by timer rate. +??end_sys: + + sub [UserCurRate],ax ; Sub from our rate counter. + ja ??end_user ; If !below zero, do not inc. + + mov bx,[WORD PTR UserRate] ; Get rate of timer. + mov dx,[WORD PTR UserError] ; Get amount of error. + add [UserCurRate],bx ; Add rate to the current. + + sub [UserCurError],dx ; Subtract err from error count. + jb ??error_adj_user ; If !below 0, increment counter. + + inc [UserTicks] ; increment the timer. + jmp short ??end_user ; don't modify UserCurError. +??error_adj_user: + add [UserCurError],bx ; reajust the error by timer rate. +??end_user: + +ENDM + + + + +;************************************************************************** +;* RM_INTERRUPT_HANDLER -- Called when processor interrupted in real mode. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +label RM_Timer_Interrupt_Handler +start_RM_Timer_Interrupt_Handler: + + push ax + push bx + push dx + push ds + + mov ax,cs ; Set data segment to code segment + mov ds,ax ; since data is in code seg. + + cmp [WORD PTR PMIssuedInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedInt],0; Make it false. + jne ??call_int_chain ; if so, just call Int Chain. + + + INCREMENT_TIMERS + + + ; Now check to see if we should call the old timer handler. + mov ax,[DosAdder] ; Get amount to add each tick. + add [DosFraction],ax ; add it to the fraction. + jnc ??no_int_chain ; Skip call if no overflow. + +??call_int_chain: + pushf ; Push flags for interrupt call. + call [OldRMI] ; chain the call. + +??no_int_chain: + sti + mov al,CLEARISR ; value to clear In Service Register + mov dx,INTCHIP0 + out dx,al ; 8259 interrupt chip controller 0 +exit1: + + pop ds + pop dx + pop bx + pop ax + iret + + + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + + pushf + call [OldRMI] ; + retf + +STACK ; Don't really need this + +END + + + + +IF 0 +; mono print stuff. + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + pop es + pop di + pop ax + popf +ENDIF diff --git a/WIN32LIB/SRCDEBUG/TIMERINI.CPP b/WIN32LIB/SRCDEBUG/TIMERINI.CPP new file mode 100644 index 0000000..83756a0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TIMERINI.CPP @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" +#include + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + + +// Prototype for timer callback +void CALLBACK Timer_Callback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +HANDLE TimerThreadHandle = 0; //Handle of timer thread +int InTimerCallback = 0; //Flag to say if we are in a timer callback + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + + + + +/*************************************************************************** + * WinTimerClass::WinTimerClass -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::WinTimerClass (UINT freq, BOOL partial) +{ + BOOL success; + + // + // Inform windows that we want a higher than normal + // timer resolution + // +#ifdef __SW_EP + timeBeginPeriod(1000/PROFILE_RATE); + Frequency = PROFILE_RATE; +#else + timeBeginPeriod ( 1000/freq ); + Frequency = freq; +#endif + + + // + // Install the timer callback event handler + // + TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC); + TimerSystemOn = success = ( TimerHandle !=0 ); + + if (success) { + if (!partial) { + WindowsTimer=this; + TickCount.Start(); + } + }else{ + char error_str [128]; + sprintf (error_str, "Error - timer system failed to start. Error code %d\n", GetLastError()); + OutputDebugString(error_str); + } +} + + + +/*************************************************************************** + * WinTimerClass::~WinTimerClass -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::~WinTimerClass( void ) +{ + + if ( TimerHandle ){ + timeKillEvent ( TimerHandle ); + } + + TimerSystemOn = FALSE; + timeEndPeriod ( 1000/Frequency ); +} + + + + + +/*********************************************************************************************** + * Timer_Callback -- Main timer callback. Equivalent to a timer interrupt handler * + * * + * * + * * + * INPUT: uint timer ID * + * uint reserved * + * long 0 (application defined) * + * long reserved * + * long reserved * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:19PM ST : Created * + *=============================================================================================*/ + + +void CALLBACK Timer_Callback (UINT , UINT , DWORD , DWORD , DWORD) +{ + //CONTEXT context; + + InTimerCallback++; + if (!TimerThreadHandle){ + DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&TimerThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS); + } + + + if (WindowsTimer) { + WindowsTimer->Update_Tick_Count(); + } + InTimerCallback--; +} + + + + + + +/*********************************************************************************************** + * WinTimerClass::Update_Tick_Count -- update westwood timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:58PM ST : Created * + *=============================================================================================*/ + +void WinTimerClass::Update_Tick_Count ( void ) +{ +/* + * + * Increment westwood timers + * + */ + SysTicks++; + UserTicks++; + +} + + + + + + + + +/* +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + */ + + + + +/*********************************************************************************************** + * WinTimerClass::Get_System_Tick_Count -- returns the system tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_System_Tick_Count ( void ) +{ + return ( SysTicks ); +} + + + +/*********************************************************************************************** + * WinTimerClass::Get_User_Tick_Count -- returns the user tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_User_Tick_Count ( void ) +{ + return ( UserTicks ); +} diff --git a/WIN32LIB/SRCDEBUG/TOBUFF.ASM b/WIN32LIB/SRCDEBUG/TOBUFF.ASM new file mode 100644 index 0000000..4b372b7 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TOBUFF.ASM @@ -0,0 +1,292 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* Last Update : Feb 10, 1995 [jrj] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_To_Buffer C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD ; this is a class member function + ARG x_pixel:DWORD ; Page X pixel coordinate. + ARG y_pixel:DWORD ; Page Y pixel coordinate. + ARG pixel_width:DWORD ; Width of region in pixels. + ARG pixel_height:DWORD ; Height of region in pixels. + ARG dest:DWORD ; the buffer to copy to + ARG buffer_size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this_object ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov esi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ buffer_size ] + jg ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + cmp [ transp ] , 0 + jnz ??forward_Blit_trans +ENDIF + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + +ENDP Buffer_To_Buffer + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/TOPAGE.ASM b/WIN32LIB/SRCDEBUG/TOPAGE.ASM new file mode 100644 index 0000000..b02c47c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TOPAGE.ASM @@ -0,0 +1,294 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + local scr_x : dword + local scr_y : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + + cmp [ src ] , 0 + jz ??real_out + + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + + +??forward_Blit_trans: + + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + + ENDP Buffer_To_Page +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/TST.CPP b/WIN32LIB/SRCDEBUG/TST.CPP new file mode 100644 index 0000000..95128a5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TST.CPP @@ -0,0 +1,32 @@ +/* +** 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 . +*/ + + +#undef _WINDOWS_16_ +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 +#define __WIN32 + +#include +#include +#include + +main +{} diff --git a/WIN32LIB/SRCDEBUG/TXTPRNT.ASM b/WIN32LIB/SRCDEBUG/TXTPRNT.ASM new file mode 100644 index 0000000..73921ca --- /dev/null +++ b/WIN32LIB/SRCDEBUG/TXTPRNT.ASM @@ -0,0 +1,502 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Print -- Assembly Buffer text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL C FontPtr:DWORD +GLOBAL C FontXSpacing:DWORD +GLOBAL C FontYSpacing:DWORD +GLOBAL C ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like object: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* Buffer_PRINT -- Assembly buffer text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Buffer_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this_object:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + local ptr_string:dword ; pointer to string + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + mov eax,[string] ; check that the string is not NULL + mov [ptr_string],eax + cmp eax,0 + jz ??done + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this_object] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch of direct draw surface + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a carry return? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Buffer_Print + +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Get_Font_Palette_Ptr:NEAR + PROC Get_Font_Palette_Ptr C near + mov eax, OFFSET ColorXlat + ret + ENDP Get_Font_Palette_Ptr + + +END + +END \ No newline at end of file diff --git a/WIN32LIB/SRCDEBUG/VBITBLIT.ASM b/WIN32LIB/SRCDEBUG/VBITBLIT.ASM new file mode 100644 index 0000000..c506537 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VBITBLIT.ASM @@ -0,0 +1,111 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VBITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Blit_To_Vesa -- copies graphic buffer to vesa screen * +;* Vesa_Blit_To_Linear -- Copies vesa screen to graphic buffer * +;* Vesa_Blit_To_Vesa -- Copies a section of vesa screen to vesa screen * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +CODESEG + +;*************************************************************************** +;* LINEAR_BLIT_TO_VESA -- copies graphic buffer to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Blit_To_Vesa C near + ret + ENDP Linear_Blit_To_Vesa + +;*************************************************************************** +;* VESA_BLIT_TO_LINEAR -- Copies vesa screen to graphic buffer * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Linear C near + ret + ENDP Vesa_Blit_To_Linear + +;*************************************************************************** +;* VESA_BLIT_TO_VESA -- Copies a section of vesa screen to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Vesa C near + ret + ENDP Vesa_Blit_To_Vesa +END + + diff --git a/WIN32LIB/SRCDEBUG/VBUFFER.CPP b/WIN32LIB/SRCDEBUG/VBUFFER.CPP new file mode 100644 index 0000000..b930eea --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VBUFFER.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : VBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 9, 1995 * + * * + * Last Update : January 9, 1995 [PWG] * + * * + * This module contains the C++ class definitions for the video buffer * + * class. This include routines for class creation and modification * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VideoViewPortClass -- Constructor for basic view port class * + * VVPC::VideoViewPortClass -- Default constructor for view port class * + * VVPC::~VideoViewPortClass -- Destructor for GraphicViewPortClass * + * VVPC::Attach -- Attaches a viewport to a buffer class * + * VVPC::Change -- Changes position and size of a Video View Port * + * VBC::VideoBufferClass -- Default constuctor for video buffers * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * VBC::~VideoBufferClass -- Destructor for the video buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "vbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * VVPC::VIDEOVIEWPORTCLASS -- Constructor for basic view port class * + * * + * INPUT: VideoBufferClass * vbuffer - buffer to attach to * + * WORD x - x offset into buffer * + * WORD y - y offset into buffer * + * WORD w - view port width in pixels * + * WORD h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::VideoViewPortClass(VideoBufferClass *vbuffer, int x, int y, int w, int h) +{ + Attach(vbuffer, x, y, w, h); +} + +/*************************************************************************** + * VVPC::VIDEOVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::VideoViewPortClass(void) +{ +} + +/*************************************************************************** + * VVPC::~VIDEOVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::~VideoViewPortClass(void) +{ +} + +/*************************************************************************** + * VVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * WORD x - x position to attach to * + * WORD y - y position to attach to * + * WORD w - width of the view port * + * WORD h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void VideoViewPortClass::Attach(VideoBufferClass *vbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= vbuffer->Get_Width()) // you cannot place left edge off + x = vbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= vbuffer->Get_Height()) // you cannot place view port off + y = vbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > vbuffer->Get_Width()) // if the x plus width is larger + w = vbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > vbuffer->Get_Height()) // if the y plus height is larger + h = vbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = vbuffer->Get_Offset() + (vbuffer->Get_Width() * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = vbuffer->Get_Width() - w; + Width = w; + Height = h; + VideoBuff = vbuffer; +} +/*************************************************************************** + * VVPC::CHANGE -- Changes position and size of a Video View Port * + * * + * INPUT: WORD the new x pixel position of the Video view port * + * WORD the new y pixel position of the Video view port * + * WORD the new width of the viewport in pixels * + * WORD the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Video View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Video View Port which is derived * + * from a Video View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL VideoViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Video View Port if it is actually the physical */ + /* representation of a Video Buffer. */ + /*======================================================================*/ + if (this == Get_Video_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing Video buffer as if we were creating the */ + /* VideoViewPort. */ + /*======================================================================*/ + Attach(Get_Video_Buffer(), x, y, w, h); + return(TRUE); +} +/*************************************************************************** + * VBC::VIDEOBUFFERCLASS -- Default constuctor for video buffers * + * * + * INPUT: WORD w - width of buffer in pixels (default = 320) * + * WORD h - height of buffer in pixels (default = 200) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::VideoBufferClass(int w, int h) +{ + Offset = 0xa0000; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + VideoBuff = this; // Get a pointer to our self +} + +/*************************************************************************** + * VBC::VIDEOBUFFERCLASS -- Lowlevel constructor for video buffer class * + * * + * INPUT: UWORD the selector of the memory reference * + * long the offset of the memory reference * + * WORD the width of the video buffer * + * WORD the height of the video buffer * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::VideoBufferClass(unsigned short selector, long offset, int w, int h) +{ + Offset = offset; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + VideoBuff = this; // Get a pointer to our self +} +/*=========================================================================* + * VBC::~VIDEOBUFFERCLASS -- Destructor for the video buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::~VideoBufferClass() +{ +} + diff --git a/WIN32LIB/SRCDEBUG/VCLEAR.ASM b/WIN32LIB/SRCDEBUG/VCLEAR.ASM new file mode 100644 index 0000000..eae579d --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VCLEAR.ASM @@ -0,0 +1,171 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : August 23, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +CODESEG + +PROC Vesa_Clear C near + USES eax,ebx,ecx,edx,esi,edi + + ARG this:DWORD ; this is a member function + ARG color:BYTE ; what color should we clear to + + cld ; always go forward + + mov ebx,[this] ; get a pointer to viewport + mov esi,[(GraphicViewPort ebx).GVPXAdd] ; esi = add for each line + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + push esi + mov edx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov esi,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + Call Vesa_Asm_Set_Win ; set the window + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + +??row_by_row_aligned: + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??row_by_row ; if not then skip + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== + + +??aligned_loop: + lea ebx , [ edi + esi ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + + xor ecx , ecx + mov ebx , esi + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep stosb +??no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + pop eax + ret + +??in_range: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + pop eax + ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??row_by_row: + + lea ebx , [ edi + esi ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , esi + jl ??in_range_bytes + + xor ecx , ecx + mov ebx , esi + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep stosb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + +??in_range_bytes: + + mov ecx , ebx + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??row_by_row ; if more to do than do it + pop eax + ret + +ENDP Vesa_Clear + + +END diff --git a/WIN32LIB/SRCDEBUG/VERSION.CPP b/WIN32LIB/SRCDEBUG/VERSION.CPP new file mode 100644 index 0000000..9cb40aa --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VERSION.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VERSION.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : July 26, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Version -- Returns with current library version text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + + + +PRIVATE char *version = "Westwood Studios - 32 Bit Library Version " + __DATE__ + "\r\n"; + +/*************************************************************************** + * VERSION -- Returns with current library version text. * + * * + * Use this routine to determine the current library version. The * + * version text contains the date that it was created. * + * * + * INPUT: none * + * * + * OUTPUT: Returns pointer to version text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1991 JLB : Created. * + *=========================================================================*/ +char * __cdecl Version(void) +{ + return(version); +} diff --git a/WIN32LIB/SRCDEBUG/VERTBLNK.ASM b/WIN32LIB/SRCDEBUG/VERTBLNK.ASM new file mode 100644 index 0000000..4a6f991 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VERTBLNK.ASM @@ -0,0 +1,139 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/video/rcs/vertblnk.asm 1.1 1994/04/18 09:34:51 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : VERTBLNK.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; WORD Get_Vert_Blank(VOID); * +; VOID Wait_Vert_Blank(VOID); * +; WORD get_vga_state (VOID) ; +; VOID set_vga_mode (WORD) ; +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +GLOBAL Get_Vert_Blank : NEAR +GLOBAL Wait_Vert_Blank : NEAR +GLOBAL get_vga_state : NEAR +GLOBAL set_vga_mode : NEAR + + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; WORD Get_Vert_Blank(VOID); +; VOID Wait_Vert_Blank(VOID); +; WORD get_vga_state (VOID) ; +; VOID set_vga_mode (WORD) ; + +; +; ---------------------------------------------------------------- + +;---------------------------------------------------------------------------- + +PROC Get_Vert_Blank C near + USES edx + + mov dx,03DAH ; CRTC status register + in al,dx + and al,008H ; look at bit 3 vertical sync + xor ah,ah ; zero ah + ret + +ENDP Get_Vert_Blank + +;---------------------------------------------------------------------------- + +;---------------------------------------------------------------------------- +PROC Wait_Vert_Blank C near + USES eax,ebx,edx + ARG blank:DWORD + + mov ebx,[blank] ; get vertical blank 0 or 1 for on + + mov edx,03DAH ; CRTC status register + + and bl,01b + shl bl,3 + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + + ret + +ENDP Wait_Vert_Blank + +;---------------------------------------------------------------------------- + +; WORD get_vga_state (VOID) ; + +PROC get_vga_state C near + USES ebx + mov eax,0f00h + int 10h + and eax, 0ffh + ret +ENDP get_vga_state + +;---------------------------------------------------------------------------- + +; VOID set_vga_mode (WORD) ; + +PROC set_vga_mode C near + ARG mode:dword + mov eax , [mode] + and eax , 0ffh + int 10h + ret +ENDP set_vga_mode + + + +END + + diff --git a/WIN32LIB/SRCDEBUG/VESA.ASM b/WIN32LIB/SRCDEBUG/VESA.ASM new file mode 100644 index 0000000..fd7266b --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VESA.ASM @@ -0,0 +1,144 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VESA.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Asm_Set_Win -- Sets the current vesa window from Asm * +;* Vesa_Asm_Next_Window -- Sets to the next vesa window * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" + +GLOBAL RMVesaVector : DWORD +GLOBAL RMVesaRegs : DWORD + + +DPMI_INTR EQU 31h + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? +ENDS + + +DATASEG + +cpu_video_page dd 0h +cpu_page_limit dd 0b0000h +CurrentBank DD 0h ; current vesa bank +RealFunc DPMI_REGS ? ; structure to call a real mode int + + + CODESEG + + +;*************************************************************************** +;* VESA_ASM_SET_WIN -- Sets the current vesa window from Asm * +;* * +;* INPUT: edi - offset to set the window for * +;* * +;* OUTPUT: edi - adjusted offset for window * +;* * +;* PROTO: void Vesa_Asm_Set_Win(void); * +;* * +;* HISTORY: * +;* 12/08/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Asm_Set_Win C near + + push eax + push edi + push ecx + push ebx + + + mov eax , edi + and eax , 0ffff0000h + add eax , 010000h + mov [ cpu_page_limit ] , eax + + lea eax , [ edi - 0a0000h ] + and eax , 0ffff0000h + mov [ cpu_video_page ] , eax + + shr eax , 14 + cmp eax , [ CurrentBank ] + jz ??no_change + mov [CurrentBank],eax ; it will be new current bank + mov ebx , [ BankTable + eax ] ; find gran value of new bank + + mov edi , [ RMVesaRegs ] ; clean up RMRegister bank + xor eax , eax + mov ecx , 34h / 4 ; size of RMRegs Bank + rep stosd + + mov edi , [ RMVesaRegs ] + mov [(type DPMI_REGS ptr edi ) . _eax ] , 04f05h + mov [(type DPMI_REGS ptr edi ) . _ebx ] , 0 + mov [(type DPMI_REGS ptr edi ) . _edx ] , ebx + + mov eax , 0300h + xor ecx , ecx ; set amount to copy of stack + mov ebx , [ RMVesaVector] ; set pointer to func to call + int DPMI_INTR ; make the call + ??no_change: + pop ebx + pop ecx + pop edi + pop eax + and edi,0000ffffh ; adjust edi to be 64k + add edi,0a0000h + ret + ENDP Vesa_Asm_Set_Win + +END diff --git a/WIN32LIB/SRCDEBUG/VESAHOOK.ASM b/WIN32LIB/SRCDEBUG/VESAHOOK.ASM new file mode 100644 index 0000000..189980e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VESAHOOK.ASM @@ -0,0 +1,207 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Vesa Hookup * +;* * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Jan 31, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* + + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "svgaprim.inc" + + +DPMI_INTR equ 031h + + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes ////////////////////////////////////// + +GLOBAL RMVesaVector : DWORD +GLOBAL RMVesaRegs : DWORD +GLOBAL RMVesaVectorSel : DWORD +GLOBAL Vesa_Hook : NEAR +GLOBAL Remove_Vesa : NEAR +GLOBAL VesaFunc : dword + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Data //////////////////////////////////////// + DATASEG + +; The current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealDataStart BYTE +include "VesaReal.ibn" +LABEL RealDataEnd BYTE + +RMVesaVectorSel DD 0 + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Code //////////////////////////////////////// + + CODESEG + +PROC Vesa_Hook C Near + USES ebx,ecx,edx,ecx,edi,esi + ARG vesa_ptr:DWORD + + ; Are they attempting to set timer again? + cmp [RMVesaVectorSel],0 + jnz ??exit + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + lea ebx , [RealDataEnd] + sub ebx , offset RealDataStart + push ebx + + add ebx , 15 + 040h + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + pop ecx + jc ??error ; check for error. + + ; set up source and destination pointers for the copy. + shl edx , 16 + mov [ RMVesaVectorSel ] , edx + shl eax,4 ; convert segment to offset. + + mov edi , ecx + xor esi , esi + +; push ecx +; push eax +; mov ecx , eax +; shld ebx,ecx,16 +; mov eax,0600h ; function number. +; int DPMI_INTR ; do call. +; pop eax +; pop ecx +; jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region. + + lea esi , [ RealDataStart] + lea edi , [ eax + 040h ] ; put it into esi for copy. + rep movsb ; write RM bin to RM memory. + + mov edx,[vesa_ptr] ; Setup vesa funtion + mov [ eax + 40h - 4 ] , edx + + + ; Chain the Real Vesa funcion interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov [ RMVesaRegs ] , eax + shl eax , 12 + lea edi , [ eax + 040h ] ; ofsset of VesaFunc + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + mov eax , 0201h + mov edx , edi + shld ecx , edx , 16 + and ebx , 0ffh + mov [ RMVesaVector ] , ebx + int DPMI_INTR + jc ??error + + ??exit: + xor eax,eax ; signal an error. + ret + ??error: + mov eax , -1 + ret + ENDP + + + +PROC Remove_Vesa C Near + USES ebx,ecx,edx,ecx,edi,esi + ARG vesa_ptr:DWORD + + + ; Are they attempting to set timer again? + mov [VesaFunc],0 + cmp [RMVesaVectorSel],0 + jz ??exit + + mov ebx , [ RMVesaVector ] + test ebx , ebx + jz ??exit + mov eax , 0201h + xor edx , edx + xor ecx , ecx + int DPMI_INTR + mov [ RMVesaVector ] , 0 + jc ??exit + +; mov eax , 0601h +; mov ecx , [ RMVesaRegs ] +; shld ebx , ecx , 16 +; lea edi , [RealDataEnd] +; sub edi , offset RealDataStart +; add edi , 15 + 040h +; xor esi , esi +; int DPMI_INTR +; jc ??exit + + mov eax,0101h ; set function number + mov edx ,[RMVesaVectorSel] + test edx , edx + jz ??exit + shr edx , 16 + int DPMI_INTR ; do call. + mov [RMVesaVectorSel],0 +??exit: + ret + ENDP + + +END + + + diff --git a/WIN32LIB/SRCDEBUG/VESAINFO.CPP b/WIN32LIB/SRCDEBUG/VESAINFO.CPP new file mode 100644 index 0000000..6ba438e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VESAINFO.CPP @@ -0,0 +1,213 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VIDEO.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : January 12, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Video_Mode -- Converts a dos video mode to a WWLIB video mode * + * Get_Video_Mode -- Returns the current video mode. * + * Set_Video_Mode -- Sets the requested video mode * + * Set_Lores_Function_Pointers -- Sets up the lowres function pointers * + * Set_HiRes_Function_Pointers -- Sets the HiRes function pointers * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iostream.h" +#include "video.h" +#include "descmgmt.h" +#include "mcgaprim.h" +#include "gbuffer.h" +#include "vbuffer.h" +#include "wwmem.h" + +#include "playcd.h" + + +/*************************************************************************** + * VESA_INFO -- Debug routine which displays vesa info to stdout * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * * + * HISTORY: * + * 11/22/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Info(int vesa_mode) +{ + UINT paras; + USHORT longest ; + union REGS regs; + struct SREGS sregs; + SEGSEL VInfoSel ; + VesaInfoType * VInfo ; + SEGSEL ModeInfoSel ; + VesaModeInfoType * ModeInfo ; + unsigned temp ; + short * ptr ; + char buff [ 256 ] ; + short mode_table [][4] = { { 0x100 , 640 , 400 , 256 }, + { 0x101 , 640 , 480 , 256 }, + { 0x103 , 800 , 600 , 256 }, + } ; + + cout << "\n\nWWESTWOOD STUDIOS. Vesa Driver attributes.\n" ; + + // verifie that this is a standard VESA MODE + if ( (vesa_mode < VESA_640X400_256) || ( vesa_mode > VESA_TEXT_132X60 )) { + cout << "this is not a standard VESA mode\n" ; + return ; + } + + // Compute size of VesaInfo structure in paragraphs + paras = ( sizeof(VesaInfoType) + 15 ) >> 4 ; + + // Alloc real-mode memory for VESA structure. + if ( DPMI_real_alloc ( paras , & VInfoSel , & longest ) ) return ; + VInfo = ( VesaInfoType * ) ( VInfoSel . seg << 4 ) ; + + // Compute size of VesaModeInfo structure in paragraphs + paras = ( sizeof(VesaModeInfoType) + 15 ) >> 4 ; + + //Alloc real-mode memory for VesaModeInfoType structure. + if ( DPMI_real_alloc ( paras , & ModeInfoSel , & longest ) ) + { + DPMI_real_free ( VInfoSel ) ; + return ; + } + ModeInfo = ( VesaModeInfoType * ) ( ModeInfoSel . seg << 4 ) ; + + // Get Read Vesa Driver Vesa + regs . x . eax = 0x4f00 ; + regs . x . edi = 0 ; + sregs . es = VInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) { + cout << "\nNot Vesa Driver Present\n" ; + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; + return ; + } + + temp = ( unsigned ) VInfo->AvailModes ; + ptr = ( short * ) ( ( ( temp & 0xffff0000 ) >> 12 ) + ( temp & 0xffff ) ) ; + cout << "Available Video Modes\n" ; + for ( ; * ptr != -1 ; ptr ++ ) + for ( temp = 0 ; temp < 3 ; temp ++ ) + if ( * ptr == mode_table [ temp ] [ 0 ] ) { + sprintf ( buff , "%d\t%d x %d x %d\n" , + mode_table [ temp ] [ 0 ], + mode_table [ temp ] [ 1 ], + mode_table [ temp ] [ 2 ], + mode_table [ temp ] [ 3 ] ) ; + cout << buff ; + } + + // Get Info for this particular graphic mode + regs . x . eax = 0x4F01; + regs . x . ecx = vesa_mode; + regs . x . edi = 0 ; + sregs . es = ModeInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) { + cout << "\nGraphic mode " << vesa_mode << " is not supported by this video card\n" ; + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; + return ; + } + + cout << "\nMode attributes\n" ; + temp = ( unsigned ) ModeInfo->Attributes ; + if ( temp & 0x01 ) cout << "\tMode supported in hardware\n" ; + else cout << "\tMode is not supported in hardware\n" ; + if ( temp & 0x20 ) cout << "\tMode is not VGA Windowed memory compatible\n" ; + else cout << "\tMode is VGA Windowed memory compatible\n" ; + + cout << "Window A attributes\n" ; + temp = ( unsigned ) ModeInfo->WinA_Attributes; ; + if ( temp & 0x02 ) cout << "\tWindow A is Readable\n" ; + else cout << "\tWindow A is not Readable\n" ; + if ( temp & 0x04 ) cout << "\tWindow A is Writeable\n" ; + else cout << "\tWindow A is not Writeable\n" ; + sprintf ( buff , "%P\n" , ModeInfo->WinA_Segment ) ; + cout << "\tWindow A segment address 0x" << buff + 4 ; + + + + cout << "Window B attributes\n" ; + temp = ( unsigned ) ModeInfo->WinB_Attributes; ; + if ( temp & 0x02 ) cout << "\tWindow B is Readable\n" ; + else cout << "\tWindow B is not Readable\n" ; + if ( temp & 0x04 ) cout << "\tWindow B is Writeable\n" ; + else cout << "\tWindow B is not Writeable\n" ; + sprintf ( buff , "%P\n" , ModeInfo->WinB_Segment ) ; + cout << "\tWindow B segment address 0x" << buff + 4 ; + + cout << "Window shared attributes\n" ; + cout << "\tWindow Granularity (KB) :" << ModeInfo->WinGranularity << "\n" ; + cout << "\tWindow Size (KB) : " << ModeInfo->WinSize << "\n"; + cout << "\tNumber of Banks : " << (long)ModeInfo->NumBanks << "\n"; + cout << "\tBytes per ScanLine : " << (long)ModeInfo->BytesPerScanline << "\n"; + cout << "\tXResolution : " << (long)ModeInfo->XRes << "\n"; + cout << "\tYResolution : " << (long)ModeInfo->YRes << "\n"; + cout << "\tX Char Size : " << (long)ModeInfo->XCharSize << "\n"; + cout << "\tY Char Size : " << (long)ModeInfo->YCharSize << "\n"; + cout << "\tMemory Model : " << (long)ModeInfo->MemoryModel << "\n"; + cout << "\tNumber of planes : " << (long)ModeInfo->NumPlanes << "\n" ; + cout << "\tBits per pixels : " << (long)ModeInfo->BitsPerPixel << "\n" ; + + +/* + cout << "Bttributes: " << (long)ModeInfo.Bttributes << "\n" + << "Win B Bttributes: " << (long)ModeInfo.WinB_Bttributes << "\n" + << "Win B Bttributes: " << (long)ModeInfo.WinB_Bttributes << "\n" + << "Win Granularity " << (long)ModeInfo.WinGranularity << "\n" + << "Win Size: " << (long)ModeInfo.WinSize << "\n" + << "Win B Segment: " << hex << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Win B Segment: " << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Bytes per scan line: " << dec << (unsigned short)ModeInfo.BytesPerScanline << "\n" + << "X resolution: " << (long)ModeInfo.XRes << "\n" + << "Y resolution: " << (long)ModeInfo.YRes << "\n" + << "X Char Size: " << (long)ModeInfo.XCharSize << "\n" + << "Y Char Size: " << (long)ModeInfo.YCharSize << "\n" + << "Number of planes: " << (long)ModeInfo.NumPlanes << "\n" + << "Bits per pixels: " << (long)ModeInfo.BitsPerPixel << "\n" + << "Number of Banks: " << (long)ModeInfo.NumBanks << "\n" + << "Memory Model: " << (long)ModeInfo.MemoryModel << "\n" + << "Bank Size: " << (long)ModeInfo.BankSize << "\n"; +*/ + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; +} diff --git a/WIN32LIB/SRCDEBUG/VESAREAL.ASM b/WIN32LIB/SRCDEBUG/VESAREAL.ASM new file mode 100644 index 0000000..f9f2eef --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VESAREAL.ASM @@ -0,0 +1,55 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer interrupt for RM * +;* * +;* File Name : TIMEREAL.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : jan 31 , 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +MODEL TINY +P386N + + + +LOCALS ?? + +CODESEG +;///////////////////////////////////// Code //////////////////////////////////////// + +label Vesa_Funcion_Call +start: + call [dword ptr cs: 40h - 4] + iret + + + +END + diff --git a/WIN32LIB/SRCDEBUG/VGETPIX.ASM b/WIN32LIB/SRCDEBUG/VGETPIX.ASM new file mode 100644 index 0000000..27a9126 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VGETPIX.ASM @@ -0,0 +1,116 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Get_Pixel -- Gets a pixel from the current graphic view port * +;* VVPC::Get_Pixel -- Gets a pixel from the current view port * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + xor eax,eax + mov edi,[(VideoViewPort ebx).VIVPOffset] ; get the correct offset + mov ecx,[(VideoViewPort ebx).VIVPHeight] ; edx = height of viewport + mov edx,[(VideoViewPort ebx).VIVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(VideoViewPort ebx).VIVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ;* Figure out what bank we are in and set it, then adjust the + ;* offset. + ;*=================================================================== + + call Vesa_Asm_Set_Win + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== +??in_range: + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Vesa_Get_Pixel + +END diff --git a/WIN32LIB/SRCDEBUG/VIDEO.CPP b/WIN32LIB/SRCDEBUG/VIDEO.CPP new file mode 100644 index 0000000..060c30c --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VIDEO.CPP @@ -0,0 +1,651 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VIDEO.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : June 29, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Video_Mode -- Converts a dos video mode to a WWLIB video mode * + * Get_Video_Mode -- Returns the current video mode. * + * Set_Video_Mode -- Sets the requested video mode * + * Set_Lores_Function_Pointers -- Sets up the lowres function pointers * + * Set_HiRes_Function_Pointers -- Sets the HiRes function pointers * + * Set_Original_Video_Mode -- sets mode to restore system to on exit * + * Get_Original_Video_Mode -- Gets the original video mode value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iostream.h" +#include "video.h" +#include "descmgmt.h" +#include "mcgaprim.h" +#include "gbuffer.h" +#include "vbuffer.h" +#include "wwmem.h" + +#include "playcd.h" + +extern "C" int MInstalled ; +extern "C" void Hide_Mouse(void); +extern "C" void Show_Mouse(void); + +extern "C" void Reset_Mouse (void) ; +extern "C" int Vesa_Hook ( REALPTR function ) ; +extern "C" void Remove_Vesa (void) ; +extern "C" SEGSEL RMVesaVectorSel ; +extern "C" REALPTR VesaFunc; + +/*=========================================================================*/ +/* Define the global variables that we require. */ +/*=========================================================================*/ +long BankTable[MAX_BANK_ENTRIES]; +int GraphicMode = UNINITIALIZED_MODE; +long XRes = 0; +long YRes = 0; +REALPTR VesaFunc; +unsigned long RMVesaVector ; +unsigned long RMVesaRegs ; + +/*=========================================================================* + * Private Varirables * + * * + * VInfo - Protected mode copy of VInfo structure. * + * ModeInfo - Protected mode copy of ModeInfo structure. * + * rpModeInfo - Real ptr to ModeInfo structure in conventional memory. * + * rpVInfo - Real ptr to VInfo structure in conventional memory. * + * _regs - Registers used for calling software interrupts. * + *=========================================================================*/ +PRIVATE void * rpModeInfo = NULL; +PRIVATE SEGSEL rpModeInfoSel ; +PRIVATE void * rpVInfo = NULL; +PRIVATE SEGSEL rpVInfoSel ; + +PRIVATE long _OriginalVideoMode = UNINITIALIZED_MODE; +PRIVATE VesaInfoType VInfo; +PRIVATE VesaModeInfoType ModeInfo; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE long Install_Vesa(void); +PRIVATE long Vesa_Get_Mode_Info(long mode); +PRIVATE long Vesa_Set_Mode(long mode); +PRIVATE void Init_Bank_Table(void); +PRIVATE VOID Set_LoRes_Function_Pointers(VOID); +PRIVATE VOID Set_HiRes_Function_Pointers(VOID); + +extern "C" long Vesa_XRes(void); +extern "C" long Vesa_YRes(void); + +extern "C" char CurrentPalette [ 256 * 3 ] ; +extern "C" char PaletteTable [ 1024 ] ; + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * GET_VIDEO_MODE -- Returns the current video mode. * + * * + * INPUT: none * + * * + * OUTPUT: returns the graphic mode as a WWLIB library define * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1994 : Created. * + *=========================================================================*/ +int Get_Video_Mode(void) +{ + union REGS regs; + + if( GraphicMode == UNINITIALIZED_MODE ) + { + regs . x . eax = 0xf00 ; // get graphic mode + int386 ( 0x10 , & regs , & regs ) ; + return ( regs . w . ax & 0xff ) ; + } + return(GraphicMode); // return the graphic mode +} + +/*************************************************************************** + * Install_Vesa -- Initializes the vesa driver if it exists * + * * + * INPUT: none * + * * + * OUTPUT: return -1 if error/VESA not supported. * + * * + * WARNINGS: This function will not work unless a vesa compatable driver * + * is installed in either hardware or software. * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Install_Vesa(void) +{ + UINT paras; + USHORT longest ; + union REGS regs; + struct SREGS sregs; + + /* Calculate size of VesaInfo structure in paragraphs */ + paras = ( sizeof(VesaInfoType) + 15 ) >> 4 ; + + /* Allocate real-mode memory for VESA structure. */ + if ( DPMI_real_alloc ( paras , & rpVInfoSel , & longest ) ) return - 1 ; + rpVInfo = ( void * ) ( rpVInfoSel . seg << 4 ) ; + + /* Calculate size of VesaModeInfo structure in paragraphs */ + paras = ( sizeof(VesaModeInfoType) + 15 ) >> 4 ; + + /* Allocate real-mode memory for VESA structure. */ + if ( DPMI_real_alloc ( paras , & rpModeInfoSel , & longest ) ) + { + Remove_Vesa(); + return (-1); + } + rpModeInfo = ( void * ) ( rpModeInfoSel . seg << 4 ) ; + + /* Clear the input buffer */ + memset ( rpVInfo , 0 , sizeof ( VesaInfoType) ) ; + + + /* Read Vesa information */ + regs . x . eax = 0x4f00 ; + regs . x . edi = 0 ; + sregs . es = rpVInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) return (-1); + Mem_Copy ( rpVInfo , & VInfo , sizeof(VesaInfoType)) ; + + return 0; +} + + +/*************************************************************************** + * VESA_GET_MODE_INFO -- Gets info about specified video mode * + * * + * INPUT: long - the mode we are requesting info about * + * * + * OUTPUT: long - 0 if sucessful, -1 if failure * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Vesa_Get_Mode_Info(long mode) +{ + union REGS regs; + struct SREGS sregs; + + if (rpModeInfo ) + { + /* Clear the input buffer */ + memset ( rpModeInfo, 0, sizeof(VesaModeInfoType)); + + /* Set up function call */ + regs . x . eax = 0x4F01; + regs . x . ecx = mode; + regs . x . edi = 0 ; + sregs . es = rpModeInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax == 0x004F) + { + Mem_Copy ( rpModeInfo , &ModeInfo , sizeof(VesaModeInfoType)); + + // this part is only temporary until Phil get the documentation + // for DPMI funtion 0x301 simulate real call + // in the mean time we will be making call to the vesa driver + // thru a real interrupt + if ( Vesa_Hook ( ModeInfo . WinFunc ) ) return ( -1 ) ; + return (0); + } + } + return (-1); +} + +/*************************************************************************** + * VESA_SET_MODE -- Sets the specified Vesa mode * + * * + * INPUT: long - the mode we wish to set * + * * + * OUTPUT: long - 0 if all is ok, -1 if we cannot set this mode * + * * + * WARNINGS: You must call Install_Vesa before calling Vesa_Set_Mode * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Vesa_Set_Mode(long mode) +{ + union REGS regs; + struct SREGS sregs; + + /* Get mode info */ + if ( Vesa_Get_Mode_Info(mode) ) return (-1) ; + + if ((ModeInfo.Attributes & 0x01) == 0) return (-1); + + /* Set vesa VIDEO MODE */ + regs . x . eax = 0x4F02; // + regs . x . ebx = mode; + int386 ( 0x10 , & regs , & regs ) ; +// delay ( 1000 ) ; + + if ( VInfo . Capabilities & 1 ) + { + /* Set DAC to 6 bit per color register */ + regs . x . eax = 0x4F08; + regs . h . bl = 0; + regs . h . bh = 6 ; + int386 ( 0x10 , & regs , & regs ) ; +// delay ( 1000 ) ; + } + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) return (-1); + + Init_Bank_Table(); + return (0); + +} + +/*************************************************************************** + * VESA_XRES -- Returns horizontal resolution of vesa mode * + * * + * INPUT: none * + * * + * OUTPUT: long - the horizontal resolution of vesa mode * + * * + * WARNINGS: You must call Install_Vesa and Vesa_Set_Mode before calling * + * Vesa_XRes(). * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +long Vesa_XRes(void) +{ + return ((long)ModeInfo.XRes); +} + +/*************************************************************************** + * VESA_YRES -- Returns vertical resolution of a vesa mode * + * * + * INPUT: none * + * * + * OUTPUT: long - the horizontal resolution of vesa mode * + * * + * WARNINGS: You must call Install_Vesa and Vesa_Set_Mode before calling * + * Vesa_XRes(). * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +long Vesa_YRes(void) +{ + return ((long)ModeInfo.YRes); +} + +/*************************************************************************** + * INIT_BANK_TABLE -- Initializes the values in the bank table * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/02/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE void Init_Bank_Table(void) +{ + long size,bankval; + int num_banks,lp; + union REGS regs; + struct SREGS sregs; + + + size = Vesa_XRes() * Vesa_YRes(); // get video mode size + num_banks = size / 65536; // get number of banks + + if (size % 65536) // adjust number of banks if + num_banks++; // they dont break evenly + + bankval = 64 / ModeInfo.WinGranularity; + VesaFunc = ModeInfo.WinFunc; + + for (lp = 0; lp < num_banks; lp++) + BankTable[lp] = lp * bankval; + + + regs . x . eax = 0x4F05; // Reset Vesa to Point to Bank 0 + regs . x . ebx = 0x0000; + regs . x . edx = 0x0000; + int386 ( 0x10 , & regs , & regs ) ; + +} + +/*************************************************************************** + * SET_MODE -- Used to set a graphic mode * + * * + * INPUT: int - the mode that we are setting * + * * + * OUTPUT: int - FALSE if failure, TRUE otherwise * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +int Set_Video_Mode(int mode) +{ + union REGS inregs,outregs; + static first_time = 0 ; + int oldmode ; + + /*======================================================================*/ + /* If this is the first time we have set the mode we need to store */ + /* of the original mode. */ + /*======================================================================*/ + if ( ! first_time ) { + first_time = 1 ; + Set_Original_Video_Mode(Get_Video_Mode()); + } + + /*======================================================================*/ + /* If the mode we are trying to set is mode zero than we are actually */ + /* trying to restore the system to its original video mode. */ + /*======================================================================*/ + if (mode == RESET_MODE) + mode = Get_Original_Video_Mode(); // set mode properly + + if (mode == GraphicMode) // if mode already correct + return(TRUE); // get out of here + + oldmode = GraphicMode ; + GraphicMode = mode ; + + /**********************************************************************/ + /* make sure the mouse is term off before any change */ + /*********************************************************************/ + if ( MInstalled == TRUE ) Hide_Mouse () ; + + + /* clear color palette */ + memset ( CurrentPalette , 255 , sizeof ( CurrentPalette ) ) ; + memset ( PaletteTable , 0 , sizeof ( PaletteTable ) ) ; + + /*======================================================================*/ + /* If we are currently in a vesa mode, free it up so that we can do it */ + /* again. */ + /*======================================================================*/ + if ((oldmode >= VESA_MIN) && (oldmode <= VESA_MAX)) + Remove_Vesa(); + + + /*======================================================================*/ + /* If we are requesting a vesa mode, than use the vesa calls to handle */ + /* it. */ + /*======================================================================*/ + if ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX) + { + inregs . x . eax = MCGA_MODE; + int386 ( 0x10 , &inregs , &outregs); +// delay ( 1000 ) ; + + if ( Install_Vesa () != 0 ) { + Set_Video_Mode(oldmode); + return(FALSE); + } + + if (Vesa_Set_Mode(GraphicMode) != 0) + { + Set_Video_Mode(oldmode); + return(FALSE); + } + XRes = Vesa_XRes(); + YRes = Vesa_YRes(); + Set_HiRes_Function_Pointers(); + } + else + { + /*===================================================================*/ + /* If not a vesa mode, then handle that as well. */ + /*===================================================================*/ + inregs . x . eax = GraphicMode ; + int386( 0x10, &inregs, &outregs); +// delay ( 1000 ) ; + + if ( GraphicMode == MCGA_MODE ) { + XRes = 320; + YRes = 200; + Set_LoRes_Function_Pointers(); + } + } + if ( MInstalled == TRUE ) { + Reset_Mouse () ; + Show_Mouse () ; + } + return(TRUE); +} +/*************************************************************************** + * VESA_INFO -- Debug routine which displays vesa info to stdout * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Must call Install_Vesa before attempting to get the vesa * + * info. * + * * + * HISTORY: * + * 11/22/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Info(void) +{ + cout << "Attributes: " << (long)ModeInfo.Attributes << "\n" + << "Win A Attributes: " << (long)ModeInfo.WinA_Attributes << "\n" + << "Win B Attributes: " << (long)ModeInfo.WinB_Attributes << "\n" + << "Win Granularity " << (long)ModeInfo.WinGranularity << "\n" + << "Win Size: " << (long)ModeInfo.WinSize << "\n" + << "Win A Segment: " << hex << (unsigned short)ModeInfo.WinA_Segment << "\n" + << "Win B Segment: " << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Bytes per scan line: " << dec << (unsigned short)ModeInfo.BytesPerScanline << "\n" + << "X resolution: " << (long)ModeInfo.XRes << "\n" + << "Y resolution: " << (long)ModeInfo.YRes << "\n" + << "X Char Size: " << (long)ModeInfo.XCharSize << "\n" + << "Y Char Size: " << (long)ModeInfo.YCharSize << "\n" + << "Number of planes: " << (long)ModeInfo.NumPlanes << "\n" + << "Bits per pixels: " << (long)ModeInfo.BitsPerPixel << "\n" + << "Number of Banks: " << (long)ModeInfo.NumBanks << "\n" + << "Memory Model: " << (long)ModeInfo.MemoryModel << "\n" + << "Bank Size: " << (long)ModeInfo.BankSize << "\n"; +} + + +/*************************************************************************** + * VESA_SET_WINDOW -- Sets given vesa window to given grain * + * * + * INPUT: int window - 0 for window A, 1 for window B * + * int grain - the granularity point for window * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/05/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Set_Window(long grain_num) +{ + union REGS regs; + struct SREGS sregs; + + regs . x . eax = 0x4f05 ; + regs . x . ebx = 0 ; + regs . x . edx = grain_num; +// DPMI_real_call ( ModeInfo.WinFunc , & regs , & sregs ) ; + DPMI_real_intr ( VesaFunc , & regs , & sregs ) ; + +} + + +/*************************************************************************** + * SET_LORES_FUNCTION_POINTERS -- Sets up the lowres function pointers * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +PRIVATE VOID Set_LoRes_Function_Pointers(VOID) +{ + VVPC_Clear_Func = MCGA_Clear; + VVPC_To_Buffer_Func = MCGA_To_Buffer; + VVPC_Put_Pixel_Func = MCGA_Put_Pixel; + VVPC_Get_Pixel_Func = MCGA_Get_Pixel; + GVPC_Blit_to_VVPC_Func = Linear_Blit_To_Linear; + VVPC_Blit_to_GVPC_Func = Linear_Blit_To_Linear; + VVPC_Blit_to_VVPC_Func = Linear_Blit_To_Linear; + VVPC_Buffer_To_Page = MCGA_Buffer_To_Page; + GVPC_Scale_To_VVPC = Linear_Scale_To_Linear; + VVPC_Scale_To_GVPC = Linear_Scale_To_Linear; + VVPC_Scale_To_VVPC = Linear_Scale_To_Linear; + VVPC_Print_Func = MCGA_Print; +} +/*************************************************************************** + * SET_HIRES_FUNCTION_POINTERS -- Sets the HiRes function pointers * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +PRIVATE VOID Set_HiRes_Function_Pointers(VOID) +{ + VVPC_Clear_Func = Vesa_Clear; + VVPC_To_Buffer_Func = Vesa_To_Buffer; + VVPC_Put_Pixel_Func = Vesa_Put_Pixel; + VVPC_Get_Pixel_Func = Vesa_Get_Pixel; + GVPC_Blit_to_VVPC_Func = Linear_Blit_To_Vesa; + VVPC_Blit_to_GVPC_Func = Vesa_Blit_To_Linear; + VVPC_Blit_to_VVPC_Func = Vesa_Blit_To_Vesa; + VVPC_Buffer_To_Page = Vesa_Buffer_To_Page; + GVPC_Scale_To_VVPC = Linear_Scale_To_Vesa; + VVPC_Scale_To_GVPC = Vesa_Scale_To_Linear; + VVPC_Scale_To_VVPC = Vesa_Scale_To_Vesa; + VVPC_Print_Func = Vesa_Print; +} + +/*************************************************************************** + * Update_Video_Mode -- used to reprogram the current graphic mode afte * + * a task swicthing from windows * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 03/18/94 JRJ : Created. * + *=========================================================================*/ +void Update_Video_Mode (void) +{ + union REGS inregs,outregs; + + /* clear color palette */ + memset ( CurrentPalette , 255 , sizeof ( CurrentPalette ) ) ; + memset ( PaletteTable , 0 , sizeof ( PaletteTable ) ) ; + + + /**********************************************************************/ + /* make sure the mouse is term off before any change */ + /*********************************************************************/ + if ( MInstalled == TRUE ) + Hide_Mouse () ; + + /*======================================================================*/ + /* If we are requesting a vesa mode, than use the vesa calls to handle */ + /* it. */ + /*======================================================================*/ + if ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX) { + inregs . x . eax = MCGA_MODE; + int386 ( 0x10 , &inregs , &outregs); + Vesa_Set_Mode(GraphicMode); + } + else { + /*===================================================================*/ + /* If not a vesa mode, then handle that as well. */ + /*===================================================================*/ + inregs . x . eax = GraphicMode ; + int386( 0x10, &inregs, &outregs); + } + if ( MInstalled == TRUE ) { + Reset_Mouse () ; + Show_Mouse () ; + } +} + + +/*************************************************************************** + * SET_ORIGINAL_VIDEO_MODE -- sets mode to restore system to on exit * + * * + * INPUT: int video mode number * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/29/1995 PWG : Created. * + *=========================================================================*/ +void Set_Original_Video_Mode(int mode) +{ + _OriginalVideoMode = mode; +} + + +/*************************************************************************** + * GET_ORIGINAL_VIDEO_MODE -- Gets the original video mode value * + * * + * INPUT: none * + * * + * OUTPUT: int the video mode set when we entered * + * * + * HISTORY: * + * 06/29/1995 PWG : Created. * + *=========================================================================*/ + +int Get_Original_Video_Mode(void) +{ + return(_OriginalVideoMode); +} diff --git a/WIN32LIB/SRCDEBUG/VLBTOVE.ASM b/WIN32LIB/SRCDEBUG/VLBTOVE.ASM new file mode 100644 index 0000000..d0543b9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VLBTOVE.ASM @@ -0,0 +1,403 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +TRANSP equ 0 + +CODESEG + + +;*************************************************************************** +;* GVC::BLIT -- Copies a buffer to a graphic page non-linearly * +;* * +;* NOTE: All coordinate values are expressed in pixels * +;* * +;* INPUT: VideoViewPortClass *dest - Video View Port to copy to * +;* WORD src_x - Src x position to copy from * +;* WORD src_y - Src y position to copy from * +;* WORD dst_x - Dest x position to copy to * +;* WORD dst_y - Dest y position to copy to * +;* WORD w - Width of region to copy * +;* WORD h - Height of region to copy * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. * +;* * +;* HISTORY: * +;* 05/11/1994 PWG : Created. * +;* 08/05/1994 PWG : Fixed clipping problem * +;*=========================================================================* + PROC Linear_Blit_To_Vesa C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + mov [ source_area ] , ecx + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + Call Vesa_Asm_Set_Win ; set the window + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret +ENDP Linear_Blit_To_Vesa + +END diff --git a/WIN32LIB/SRCDEBUG/VMPAGEIN.ASM b/WIN32LIB/SRCDEBUG/VMPAGEIN.ASM new file mode 100644 index 0000000..be471a7 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VMPAGEIN.ASM @@ -0,0 +1,90 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : VMPAGEIN.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Last Update : March 8th, 1996 [ST] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Force_VM_Page_In -- forces a buffer to be paged in under win95 * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + + + LOCALS ?? + + +GLOBAL C Force_VM_Page_In:near + +CODESEG + + +;*************************************************************************** +;* Force_VM_Page_In -- forces a buffer to be paged in under win95 * +;* * +;* * +;* INPUT: ptr to start of buffer * +;* length of buffer * +;* * +;* OUTPUT: * +;* * +;* Prototype: * +;* void Force_VM_Page_In (void *buffer, int length); * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 3/8/96 4:18PM ST : Created. * +;*=========================================================================* + + +proc Force_VM_Page_In C near + USES eax,ecx,edx,edi + ARG buffer_start:dword + ARG buffer_length:dword + + mov ecx,[buffer_length] + xor al,al + test ecx,ecx + mov edx,4096 + jz ??out + + mov edi,[buffer_start] + + +??next_page: add [edi],al + add edi,edx + sub ecx,edx + jg ??next_page + +??out: ret + +endp Force_VM_Page_In + +END diff --git a/WIN32LIB/SRCDEBUG/VPUTPIX.ASM b/WIN32LIB/SRCDEBUG/VPUTPIX.ASM new file mode 100644 index 0000000..3155776 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VPUTPIX.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Put_Pixel -- Puts a pixel on a graphic viewport * +;* VIVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + + CODESEG + +;*************************************************************************** +;* VIVPC::PUT_PIXEL -- Puts a pixel on a video viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Put_Pixel C near + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + mov edi,[(VideoViewPort ebx).VIVPOffset] ; get the correct offset + mov ecx,[(VideoViewPort ebx).VIVPHeight] ; edx = height of viewport + mov edx,[(VideoViewPort ebx).VIVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(VideoViewPort ebx).VIVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ;* Figure out what bank we are in and set it, then adjust the + ;* offset. + ;*=================================================================== + call Vesa_Asm_Set_Win + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen +??exit: + ret + ENDP Vesa_Put_Pixel +END diff --git a/WIN32LIB/SRCDEBUG/VSCALE.ASM b/WIN32LIB/SRCDEBUG/VSCALE.ASM new file mode 100644 index 0000000..09e10ab --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VSCALE.ASM @@ -0,0 +1,68 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Scale_To_Vesa -- Scales a vesa viewport to a vesa viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_SCALE_TO_VESA -- Scales a vesa viewport to a vesa viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Scale_To_Vesa C near + ret + ENDP Vesa_Scale_To_Vesa + + +END diff --git a/WIN32LIB/SRCDEBUG/VSCALE.CPP b/WIN32LIB/SRCDEBUG/VSCALE.CPP new file mode 100644 index 0000000..604eca9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VSCALE.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Testing memory. * + * * + * File Name : TEST.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : January 18, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* +*/ +#include +#include +#include + +//VideoBufferClass SeenPage(640,480); +//GraphicBufferClass HidBuff; +//GraphicBufferClass BackBuff; + +int Vesa_Scale_To_Vesa ( void * scr, void * dst, + int src_x , int src_y , int dst_x , int dst_y , + int src_wd , int src_hg , int dst_wd, int dst_hg, + BOOL trans , char * remap ) +{ + int area ; + int width , height ; + char * temp ; + + VideoViewPortClass * scr1 = ( VideoViewPortClass * ) scr ; + + width = src_wd - src_x ; + height = src_hg - src_y ; + area = width * height ; + temp = ( char * ) malloc ( area ) ; + if ( ! temp ) return 0 ; + + scr1 -> To_Buffer ( 0, 0, width , height , temp, area ); + GraphicBufferClass tempbuffer ( area , width , height , temp ) ; + + + tempbuffer . Scale ( * scr1 , + 0, 0, dst_x, dst_y, + width, height , dst_wd, dst_hg , + trans , remap ); + + + free ( temp ) ; + return 0 ; +} diff --git a/WIN32LIB/SRCDEBUG/VSCLTOVE.ASM b/WIN32LIB/SRCDEBUG/VSCLTOVE.ASM new file mode 100644 index 0000000..9c4fe87 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VSCLTOVE.ASM @@ -0,0 +1,730 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Scale_To_Vesa -- Scales a graphic viewport to a vesa viewport * +;* Vesa_Scale_To_Linear -- Scales a Vesa viewport to a graphic viewport * +;* Vesa_Scale_To_Vesa -- Scales a vesa viewport to a vesa viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +GLOBAL Vesa_Asm_Set_Win : near +GLOBAL cpu_video_page : dword +GLOBAL cpu_page_limit : dword + + +CODESEG + + +;*************************************************************************** +;* LINEAR_SCALE_TO_VESA -- Scales a graphic viewport to a vesa viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Vesa C near + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + local dx_intr : dword + + local scan_line : dword + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword + + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je ??all_done + cmp [dst_height],0 + je ??all_done + cmp [src_width],0 + je ??all_done + cmp [src_height],0 + je ??all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??clip_against_dest + mov bl , dl + test cl , 1000b + jz ??src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + +??src_left_ok: + test cl , 0010b + jz ??src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + +??src_bottom_ok: + test bl , 0100b + jz ??src_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + +??src_right_ok: + test bl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + +; Clip destination Rectangle against source Window boundaries. +??clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??do_scaling + mov bl , dl + test cl , 1000b + jz ??dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + +??dst_left_ok: + test cl , 0010b + jz ??dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + +??dst_bottom_ok: + test bl , 0100b + jz ??dst_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + +??dst_right_ok: + test bl , 0001b + jz ??do_scaling + + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + +??do_scaling: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + call Vesa_Asm_Set_Win ; set the window + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jz ??all_done + sub ebx , [ dst_x0 ] + jz ??all_done + + mov [ counter_y ] , ecx + mov [ scan_line ] , ebx + + cmp [ trans ] , 0 + jnz ??transparency + + cmp [ remap ] , 0 + jnz ??normal_remap + +; ************************************************************************* +; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ??ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + ??outter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??in_range + mov ebx , [ scan_line ] + jmp ??trailing_entry + ??trailing_bytes: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + dec ebx + ??trailing_entry: + cmp edi , 0b0000h + jl ??trailing_bytes + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??end_of_scanline: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + dec ebx + jg ??end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??next_line + +??in_range: + mov ebx , [ counter_x ] + jmp [ entry ] + ??inner_loop: + REPT 32 + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + ENDM + ??ref_point: + dec ebx + jge ??inner_loop + ??next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??normal_remap: + mov ecx , ebx + mov [ dx_frac ] , eax + mov [ dx_intr ] , edx + + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ ??remapref_point + ecx ] + mov [ entry ] , ecx + + ??remapoutter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??remap_in_range + mov edx , [ scan_line ] + xor ebx , ebx + jmp ??remap_trailing_entry + ??remap_trailing_bytes: + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + dec edx + ??remap_trailing_entry: + cmp edi , 0b0000h + jl ??remap_trailing_bytes +??remap_no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??remap_end_of_scanline: + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + dec edx + jg ??remap_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??remap_next_line +??remap_in_range: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + mov edx , [ dx_intr ] + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??remapinner_loop: + REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + ENDM + ??remapref_point: + dec [ remap_counter ] + jge ??remapinner_loop + ??remap_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??remapoutter_loop + ret + + +;**************************************************************************** +; scale with transparency + +??transparency: + cmp [ remap ] , 0 + jnz ??trans_remap + +; ************************************************************************* +; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ ??tr_ref_point + ecx ] + mov [ entry ] , ecx + + ??tr_outter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??tr_in_range + + mov ebx , [ scan_line ] + jmp ??tr_trailing_entry + ??tr_trailing_bytes: + mov cl , [ esi ] + test cl , cl + jz ??tr_skip + mov [ edi ] , cl + ??tr_skip: + add ecx , eax + adc esi , edx + inc edi + dec ebx + ??tr_trailing_entry: + cmp edi , 0b0000h + jl ??tr_trailing_bytes + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??tr_end_of_scanline: + mov cl , [ esi ] + test cl , cl + jz ??tr_skip1 + mov [ edi ] , cl + ??tr_skip1: + add ecx , eax + adc esi , edx + inc edi + dec ebx + jg ??tr_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??tr_next_line + +??tr_in_range: + mov ebx , [ counter_x ] + jmp [ entry ] + ??tr_inner_loop: + REPT 32 + local skip + mov cl , [ esi ] + test cl , cl + jz skip + mov [ edi ] , cl + skip: + add ecx , eax + adc esi , edx + inc edi + ENDM + ??tr_ref_point: + dec ebx + jge ??tr_inner_loop + ??tr_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??tr_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??tr_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??tr_outter_loop + ret + + + +; ************************************************************************* +; normal scale with remap and transparency + +??trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + mov [ dx_intr ] , edx + + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_remapref_point + ecx ] + mov [ entry ] , ecx + + ??trans_remapoutter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??trans_remap_in_range + mov edx , [ scan_line ] + xor ebx , ebx + jmp ??trans_remap_trailing_bytes1 + ??trans_remap_trailing_bytes: + mov bl , [ esi ] + test bl , bl + jz ??trans_remp + mov cl , [ eax + ebx ] + mov [ edi ] , cl + ??trans_remp: + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + inc edi + dec edx + ??trans_remap_trailing_bytes1: + cmp edi , 0b0000h + jl ??trans_remap_trailing_bytes +??trans_remap_no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??trans_remap_end_of_scanline: + mov bl , [ esi ] + test bl , bl + jz ??trans_remp1 + mov cl , [ eax + ebx ] + mov [ edi ] , cl + ??trans_remp1: + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + inc edi + dec edx + jg ??trans_remap_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??trans_remap_next_line + +??trans_remap_in_range: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + mov edx , [ dx_intr ] + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??trans_remapinner_loop: + REPT 32 + local skip + mov bl , [ esi ] + test bl , bl + jz skip + mov cl , [ eax + ebx ] + mov [ edi ] , cl + skip: + + add ecx , [ dx_frac ] + adc esi , edx + inc edi + ENDM + ??trans_remapref_point: + dec [ remap_counter ] + jge ??trans_remapinner_loop + ??trans_remap_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_remapoutter_loop + ret + +??all_done: + ret + + ENDP Linear_Scale_To_Vesa + +END diff --git a/WIN32LIB/SRCDEBUG/VTOBUFF.ASM b/WIN32LIB/SRCDEBUG/VTOBUFF.ASM new file mode 100644 index 0000000..a7adfd7 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VTOBUFF.ASM @@ -0,0 +1,344 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +TRANSP equ 0 + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_To_Buffer C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; this is a class member function + ARG x_pixel:DWORD ; Page X pixel coordinate. + ARG y_pixel:DWORD ; Page Y pixel coordinate. + ARG pixel_width:DWORD ; Width of region in pixels. + ARG pixel_height:DWORD ; Height of region in pixels. + ARG dest:DWORD ; the buffer to copy to + ARG size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + Call Vesa_Asm_Set_Win ; set the window + mov esi , edi + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + sub edx , [ y_pixel ] + jz ??real_out + + mov eax , [ x1_pixel ] + sub eax , [ x_pixel ] + jz ??real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ size ] + jg ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + cmp esi , 0b0000h + mov ebx , eax + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + + + +IF TRANSP + + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + +ENDP Vesa_To_Buffer +END + diff --git a/WIN32LIB/SRCDEBUG/VTOPAGE.ASM b/WIN32LIB/SRCDEBUG/VTOPAGE.ASM new file mode 100644 index 0000000..52ef462 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VTOPAGE.ASM @@ -0,0 +1,343 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 9, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page(GVC) -- Copies a linear buffer to a graphic viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +TRANSP equ 0 + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;* 12/09/1994 PWG : Made SVGA Modifications * +;*=========================================================================* + PROC Vesa_Buffer_To_Page C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL scr_x1 : dword + LOCAL scr_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + + cmp [ src ] , 0 + jz ??real_out + + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x1 ] , 0 + mov [ scr_y1 ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x1 ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y1 ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + call Vesa_Asm_Set_Win ; set the window + + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y1 ] + mul [ pixel_width ] + add eax , [ scr_x1 ] + add esi , eax + + + mov edx , [ y1_pixel ] + sub edx , [ y_pixel ] + jz ??real_out + + mov eax , [ x1_pixel ] + sub eax , [ x_pixel ] + jz ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + + +??forward_Blit_trans: + + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + + ENDP Vesa_Buffer_To_Page +END + diff --git a/WIN32LIB/SRCDEBUG/VTXTPRNT.ASM b/WIN32LIB/SRCDEBUG/VTXTPRNT.ASM new file mode 100644 index 0000000..f6e8c7ab --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VTXTPRNT.ASM @@ -0,0 +1,444 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VESA_Print -- Assembly VESA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "svgaprim.inc" +INCLUDE ".\gbuffer.inc" + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* + GLOBAL FontPtr:DWORD + GLOBAL ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + + CODESEG + + +;*************************************************************************** +;* VESA_PRINT -- Assembly VESA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + local ptr_string:dword ; pointer to string + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL remainder:DWORD + LOCAL fullwidth:DWORD + LOCAL currwidth:DWORD + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??done + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??done ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + call Vesa_Asm_Set_Win ; adjust edi & vesa page + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + cmp edi,0b0000h ; have we gone over win edge + jl ??draw_char ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + +??loop_top: + stosb ; store the value + cmp edi,0b0000h ; have we gone over win edge + jl ??top_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??top_no_window_change: + + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + cmp edi,0b0000h ; have we gone over win edge + jl ??top2_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window + +??top2_no_window_change: + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + cmp edi,0b0000h ; have we gone over win edge + jl ??lo_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??lo_no_window_change: + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [edi],al ; write it out +??skiphi: + inc edi + cmp edi,0b0000h ; have we gone over win edge + jl ??hi_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??hi_no_window_change: + + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + cmp edi,0b0000h ; have we gone over win edge + jl ??next_row_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??next_row_no_window_change: + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + cmp edi,0b0000h ; have we gone over win edge + jl ??bottom_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??bottom_no_window_change: + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + cmp edi,0b0000h ; have we gone over win edge + jl ??bottom2_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??bottom2_no_window_change: + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + +;----------------------------------- special case line feeds ---------------------------------------- +; JRJ 05/01/95 This is the problem However made this change introduced +; a error in the code, this function is not supposed to handle +; Text wrapping +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + dec [dword ptr string] ; overflow by one charater + jmp ??done + + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??done ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char +??done: + mov eax,[string] ; return the number of charaters + sub eax,[ptr_string] ; printed + ret + + ENDP Vesa_Print + +END diff --git a/WIN32LIB/SRCDEBUG/VVBLIT.ASM b/WIN32LIB/SRCDEBUG/VVBLIT.ASM new file mode 100644 index 0000000..ee66b26 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VVBLIT.ASM @@ -0,0 +1,597 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +TRANSP equ 0 + +POOLSIZE equ 8000 + +CODESEG + + PROC Vesa_Blit_To_Vesa C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + local total_lines : dword + local count_dy : dword + local mem_page : dword + local vesa_page : dword + local mem_pool : byte : POOLSIZE + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + mov [ source_area ] , ecx + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + + push eax + mov [ mem_page ] , 0 + mov [ vesa_page ] , 0 + + mov [ total_lines ] , edx + mov eax , POOLSIZE + xor edx , edx + idiv [ dword ptr esp ] + mov [ count_dy ] , eax + pop eax + +; ************************************************************************** +; check direction of motions + cmp esi , edi + jl ??backupward_blit + + ret + +;*********************************************************************** +; Backupward blit + +??back_mem_loop: + push edi + lea edi , [ mem_pool ] + mov edx , [ count_dy ] + call ??vesa_to_memory + pop edi + + push esi + lea esi , [ mem_pool ] + mov edx , [ count_dy ] + call ??memory_to_vesa + pop esi + +??backupward_blit: + mov edx , [ total_lines ] + sub edx , [ count_dy ] + mov [ total_lines ] , edx + jg ??back_mem_loop + + add edx , [ count_dy ] + push edi + push edx + lea edi , [ mem_pool ] + call ??vesa_to_memory + pop edx + pop edi + + push esi + lea esi , [ mem_pool ] + call ??memory_to_vesa + pop esi + ret + + + +??real_out: + ret + + + + +; ******************************************************************** +; Move Vesa video page to memory buffer + +??vesa_to_memory: + + xchg edi , esi + add edi , [ mem_page ] + call Vesa_Asm_Set_Win + xchg edi , esi + +IF TRANSP + test [ trans ] , 1 + jnz ??tomem_forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??tomem_forward_loop_bytes + +??tomem_forward_loop_dword: + + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??tomem_in_range + + xor ecx , ecx + mov ebx , eax + cmp esi , 0b0000h + jge ??tomem_no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??tomem_no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + dec edx ; decrement the height + jnz ??tomem_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +??tomem_in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + dec edx + jnz ??tomem_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +??tomem_forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??tomem_in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??tomem_no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??tomem_no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??tomem_in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + dec edx ; decrement the height + jnz ??tomem_forward_loop_bytes + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +IF TRANSP +??tomem_forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??tomem_transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??tomem_forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??tomem_forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc edi + ENDM + ??tomem_transp_reference: + dec ecx + jge ??tomem_forward_trans_line + add esi , [ scr_ajust_width ] + dec edx + jnz ??tomem_forward_loop_trans + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn +ENDIF + + + + + +;************************************************************************* +; copy from memory to vesa page + +??memory_to_vesa: + add edi , [ vesa_page ] + Call Vesa_Asm_Set_Win + +IF TRANSP + test [ trans ] , 1 + jnz ??tovesa_forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??tovesa_forward_loop_bytes + +??tovesa_forward_loop_dword: + + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??tovesa_in_range + + xor ecx , ecx + cmp edi , 0b0000h + mov ebx , eax + jge ??tovesa_no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??tovesa_no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??tovesa_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +??tovesa_in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add edi , [ dest_ajust_width ] + dec edx + jnz ??tovesa_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +??tovesa_forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??tovesa_in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??tovesa_no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??tovesa_no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??tovesa_in_range_bytes: + mov ecx , ebx + rep movsb + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??tovesa_forward_loop_bytes + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +IF TRANSP + + +??tovesa_forward_Blit_trans: + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??tovesa_transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??tovesa_forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??tovesa_forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??tovesa_transp_reference: + dec ecx + jge ??tovesa_forward_trans_line + add edi , [ dest_ajust_width ] + dec edx + jnz ??tovesa_forward_loop_trans + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn +ENDIF + + +ENDP Vesa_Blit_To_Vesa + + + +END diff --git a/WIN32LIB/SRCDEBUG/VVETOLB.ASM b/WIN32LIB/SRCDEBUG/VVETOLB.ASM new file mode 100644 index 0000000..2666d3e --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VVETOLB.ASM @@ -0,0 +1,409 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +TRANSP equ 0 + +CODESEG + + +;*************************************************************************** +;* GVC::BLIT -- Copies a buffer to a graphic page non-linearly * +;* * +;* NOTE: All coordinate values are expressed in pixels * +;* * +;* INPUT: VirtualViewPortClass *dest - Virtual View Port to copy to * +;* WORD src_x - Src x position to copy from * +;* WORD src_y - Src y position to copy from * +;* WORD dst_x - Dest x position to copy to * +;* WORD dst_y - Dest y position to copy to * +;* WORD w - Width of region to copy * +;* WORD h - Height of region to copy * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. * +;* * +;* HISTORY: * +;* 05/11/1994 PWG : Created. * +;* 08/05/1994 PWG : Fixed clipping problem * +;*=========================================================================* + PROC Vesa_Blit_To_Linear C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + mov [ source_area ] , ecx + add edi , eax + call Vesa_Asm_Set_Win ; set the window + mov esi , edi + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp esi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + +ENDP Vesa_Blit_To_Linear + + +END diff --git a/WIN32LIB/SRCDEBUG/VVETOSCL.ASM b/WIN32LIB/SRCDEBUG/VVETOSCL.ASM new file mode 100644 index 0000000..9aa3db9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VVETOSCL.ASM @@ -0,0 +1,65 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Scale_To_Linear -- Scales a Vesa viewport to a graphic viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG +;*************************************************************************** +;* VESA_SCALE_TO_LINEAR -- Scales a Vesa viewport to a graphic viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Scale_To_Linear C near + ret + ENDP Vesa_Scale_To_Linear + +END diff --git a/WIN32LIB/SRCDEBUG/VVETOSCL.CPP b/WIN32LIB/SRCDEBUG/VVETOSCL.CPP new file mode 100644 index 0000000..d1e7bba --- /dev/null +++ b/WIN32LIB/SRCDEBUG/VVETOSCL.CPP @@ -0,0 +1,65 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Testing memory. * + * * + * File Name : TEST.CPP * + * * + * Programmer : Julio Jerez * + * * + * Start Date : Feb 13, 1995 * + * * + * * + *-------------------------------------------------------------------------* +*/ +#include +#include +#include + +int Vesa_Scale_To_Linear ( void * scr, void * dst, + int src_x , int src_y , int dst_x , int dst_y , + int src_wd , int src_hg , int dst_wd, int dst_hg, + BOOL trans , char * remap ) +{ + int area ; + int width , height ; + char * temp ; + + VideoViewPortClass * scr1 = ( VideoViewPortClass * ) scr ; + + width = src_wd - src_x ; + height = src_hg - src_y ; + area = width * height ; + temp = ( char * ) malloc ( area ) ; + if ( ! temp ) return 0 ; + + scr1 -> To_Buffer ( 0, 0, width , height , temp, area ); + GraphicBufferClass tempbuffer ( area , width , height , temp ) ; + + Linear_Scale_To_Linear ( & tempbuffer , dst , + 0 , 0 , dst_x , dst_y , + width , height , dst_wd , dst_hg , + trans , remap ) ; + + free ( temp ) ; + return 0 ; +} diff --git a/WIN32LIB/SRCDEBUG/WINCOMM.CPP b/WIN32LIB/SRCDEBUG/WINCOMM.CPP new file mode 100644 index 0000000..ed9ccc4 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WINCOMM.CPP @@ -0,0 +1,1487 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for WinModemClass & WinNullModemClass * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +//#include "function.h" +#include "wincomm.h" +#include "timer.h" +#include "keyboard.h" +#include "misc.h" +#include +#include +#include +#include + + +/* +** Define this to log modem activity to disk. +*/ +//#define LOG_MODEM + +/* +** Object represents a serial port +*/ +WinModemClass *SerialPort = NULL; + + + +/*********************************************************************************************** + * WMC::WinModemClass -- WinModemClass constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:14PM ST : Created * + *=============================================================================================*/ + +WinModemClass::WinModemClass(void) +{ + /* + ** Allocate memory for our internal circular serial input buffer + */ + SerialBuffer = new unsigned char [SIZE_OF_WINDOWS_SERIAL_BUFFER]; + + /* + ** Initialise the serial buffer pointers + */ + SerialBufferReadPtr = 0; + SerialBufferWritePtr = 0; + + /* + ** Clear the waiting flags + */ + WaitingForSerialCharRead = FALSE; + WaitingForSerialCharWrite = FALSE; + + /* + ** No default abort or echo function + */ + AbortFunction = NULL; + EchoFunction = NULL; + + + /* + ** Clear the running error count + */ + FramingErrors=0; + IOErrors=0; + BufferOverruns=0; + InBufferOverflows=0; + ParityErrors=0; + OutBufferOverflows=0; + + /* + ** We havnt opened a port yet so... + */ + PortHandle = 0; +} + + + +/*********************************************************************************************** + * WMC::~WinModemClass -- destructor for WinModemClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:15PM ST : Created * + *=============================================================================================*/ + +WinModemClass::~WinModemClass(void) +{ + /* + ** Close the port + */ + if (PortHandle){ + Serial_Port_Close(); + } + + /* + ** Free the serial buffer + */ + if (SerialBuffer){ + delete [] SerialBuffer; + } +} + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close) +{ + + char class_string[1024]; + DWORD string_size = 1024; + DWORD num_sub_keys; + DWORD longest_sub_key_name; + DWORD longest_class_string; + DWORD num_value_entries; + DWORD longest_value_name_length; + DWORD longest_value_data_length; + DWORD security_descriptor_length; + FILETIME last_write_time; + HKEY result_key; + DWORD sub_key_buffer_size; + + char *sub_key_buffer; + char *sub_key_class; + + + if (RegQueryInfoKey (base_key, + &class_string[0], + &string_size, + NULL, + &num_sub_keys, + &longest_sub_key_name, + &longest_class_string, + &num_value_entries, + &longest_value_name_length, + &longest_value_data_length, + &security_descriptor_length, + &last_write_time) != ERROR_SUCCESS) return (0); + + sub_key_buffer_size = longest_sub_key_name+16; + sub_key_buffer = new char [sub_key_buffer_size]; + sub_key_class = new char [longest_class_string+1]; + + for (int key_num=0 ; key_numdwProviderSubType == PST_MODEM){ + temp = modem_config->dwProviderOffset; + temp += (int)modem_config; + modem_caps = (MODEMDEVCAPS*)temp; + modem_caps->dwModemOptions &= ~( MDM_COMPRESSION | + MDM_ERROR_CONTROL | + MDM_FLOWCONTROL_HARD); + SetCommConfig(PortHandle, modem_config ,(unsigned)config_size); + } + } + + + /* + ** Set the device timeouts + */ + timeouts.ReadIntervalTimeout = 10000; //10 seconds between incoming packets + timeouts.ReadTotalTimeoutMultiplier = 0; //disable total timeouts + timeouts.ReadTotalTimeoutConstant = 0; //disable total timeouts + timeouts.WriteTotalTimeoutMultiplier= 0; //disable total timeouts + timeouts.WriteTotalTimeoutConstant = 0; //disable total timeouts + + if (SetCommTimeouts(com_handle, &timeouts) !=TRUE){ + Serial_Port_Close(); + return (INVALID_HANDLE_VALUE); + } + + return (com_handle); +} +#endif + + + + + +/*********************************************************************************************** + * WMC::Serial_Port_Open -- opens a com port for asyncronous read/write and gets a handle to it* + * * + * * + * * + * INPUT: Com port - 0=com1, 1=com2 etc. * + * baud rate - bits per second * + * parity - true or false * + * word length - 5 to 8 bits * + * stop bits - 0=1 stop bit, 1=1.5 & 2=2 * + * flow control- 0 = none, 1 = hardware * + * * + * OUTPUT: Handle to port * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:17PM ST : Created * + *=============================================================================================*/ +HANDLE WinModemClass::Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol) +{ + HANDLE com_handle; //temporary storage for the port handle + DCB device_control; //device control block + COMMTIMEOUTS timeouts; //timeout values +#if (0) + /* + ** Map for com port values to device names + */ + static char com_ids[8][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8" + }; +#endif //(0) + + int errorval; + char errortxt[128]; + + char devname[266]={"\\\\.\\"}; //device name to open + strcat (devname, device_name); + + PortHandle = 0; + + /* + ** Open the com port for asyncronous reads/writes + */ + //com_handle = CreateFile (&com_ids[com][0], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + com_handle = CreateFile (devname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (com_handle == INVALID_HANDLE_VALUE) return (com_handle); + + PortHandle = com_handle; + + /* + ** Set the size of the windows communication buffers + */ + SetupComm(com_handle, SIZE_OF_WINDOWS_SERIAL_BUFFER, SIZE_OF_WINDOWS_SERIAL_BUFFER); + + /* + ** Reset any read or write operation and purge the buffers + */ + PurgeComm (PortHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); + + /* + ** Create an event object for asyncronous reads + */ + ReadOverlap.Internal = 0; + ReadOverlap.InternalHigh = 0; + ReadOverlap.Offset = 0; + ReadOverlap.OffsetHigh = 0; + ReadOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); + + /* + ** Create an event object for asyncronous writes + */ + WriteOverlap.Internal = 0; + WriteOverlap.InternalHigh = 0; + WriteOverlap.Offset = 0; + WriteOverlap.OffsetHigh = 0; + WriteOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); + + + /* + ** Get the current state of the com port as a basis for our device control block + */ + if (GetCommState (com_handle , &device_control)){ + + /* + ** Communications settings + */ + device_control.BaudRate = baud; + device_control.fParity = parity; + device_control.ByteSize = (char)wordlen; + device_control.StopBits = (char)(stopbits-1); + + /* + ** Misc settings for flow control etc. + */ + device_control.fBinary = TRUE; // Binary mode data transfer + device_control.fOutxCtsFlow = TRUE; // CTS flow control + device_control.fRtsControl = RTS_CONTROL_HANDSHAKE; // RTS flow control + device_control.fErrorChar = FALSE; // Dont put an error char into our input stream + device_control.fOutxDsrFlow = FALSE; // No DSR flow control + device_control.fDtrControl = DTR_CONTROL_ENABLE; // Enable control of DTR line + device_control.fOutX = FALSE; // No XON/XOF flow control + device_control.fInX = FALSE; // No XON/XOF flow control + device_control.fAbortOnError = FALSE; // Device continues to send after an error + + /* + ** Disable hardware flow control if required + */ + if (!flowcontrol){ + device_control.fOutxCtsFlow = FALSE; // CTS flow control + device_control.fRtsControl = RTS_CONTROL_DISABLE; // RTS flow control + } + + /* + ** Pass the device settings to windows + */ + if ( !SetCommState (com_handle , &device_control)){ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- SetCommState returned error code %d.\n", errorval); + OutputDebugString (errortxt); + //Serial_Port_Close(); + //return (INVALID_HANDLE_VALUE); + } + }else{ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- GetCommState returned error code %d.\n", errorval); + OutputDebugString (errortxt); + } + + + + /* + ** Set the device timeouts + */ + timeouts.ReadIntervalTimeout = 1000; //1 second between incoming bytes will time-out the read + timeouts.ReadTotalTimeoutMultiplier = 0; //disable per byte timeouts + timeouts.ReadTotalTimeoutConstant = 3000; //Read operations time out after 3 secs if no data received + timeouts.WriteTotalTimeoutMultiplier= 500; //Allow 1/2 ms between each char write + timeouts.WriteTotalTimeoutConstant = 1000; //Write operations time out after 1 sec + 1/2 sec per char if data wasnt sent + + if ( !SetCommTimeouts(com_handle, &timeouts) ){ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- SetCommTimeouts returned error code %d.\n", errorval); + OutputDebugString (errortxt); + //Serial_Port_Close(); + //return (INVALID_HANDLE_VALUE); + } + + return (com_handle); +} + + + + + +/*********************************************************************************************** + * WMC::Set_Modem_Dial_Type -- sets dial type to WC_TOUCH_TONE or WC_PULSE * + * * + * * + * * + * INPUT: WC_TOUCH_TONE or WC_PULSE * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:22PM ST : Created * + *=============================================================================================*/ + +void WinModemClass::Set_Modem_Dial_Type(WinCommDialMethodType method) +{ + DialingMethod = method; +} + + + + +/*********************************************************************************************** + * WMC::Get_Modem_Status -- gets the status of the modem control lines * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status. Any of the following bits CTS_SET, DSR_SET, RI_SET or CD_SET * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:24PM ST : Created * + *=============================================================================================*/ + +unsigned WinModemClass::Get_Modem_Status(void) +{ + DWORD modem_stat = 0; + unsigned long return_stat = 0; + + /* + ** Get the modem status + */ + GetCommModemStatus(PortHandle, &modem_stat); + + /* + ** Translate the windows status flags to greenleaf flags + */ + if (MS_CTS_ON & modem_stat) return_stat |= CTS_SET; + if (MS_DSR_ON & modem_stat) return_stat |= DSR_SET; + if (MS_RING_ON & modem_stat) return_stat |= RI_SET; + if (MS_RLSD_ON & modem_stat) return_stat |= CD_SET; + + return (return_stat); + +} + + + +/*********************************************************************************************** + * WMC::Set_Serial_DTR -- set the state of the modems DTR control line * + * * + * * + * * + * INPUT: state - true or false * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:25PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Serial_DTR(BOOL state) +{ + if (state){ + EscapeCommFunction(PortHandle, SETDTR); + }else{ + EscapeCommFunction(PortHandle, CLRDTR); + } +} + + + + + +/*********************************************************************************************** + * WMC::Serial_Port_Close -- close the port and free the port handle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:26PM ST : Created * + *=============================================================================================*/ + +void WinModemClass::Serial_Port_Close (void) +{ + if (PortHandle){ + CloseHandle(PortHandle); + PortHandle = 0; + } +} + + + +/*********************************************************************************************** + * WMC::Read_Serial_Chars -- copys chars from the windows serial buffer to the class buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if any chars read * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:26PM ST : Created * + *=============================================================================================*/ + +void Smart_Printf( char *format, ... ); +BOOL WinModemClass::Read_Serial_Chars (void) +{ + DWORD bytes_read; //amount of data read this time + BOOL read_result; //result of ReadFile + BOOL overlap_result; //result of GetOverlappedResult + DWORD total_bytes_read=0; //total amount of data read + int bytes_to_read; + int i; + + /* + ** Are we were still waiting for the last read operation to finish? + */ + if (WaitingForSerialCharRead){ + + /* + ** Check the result of the last read operation + */ + bytes_read = 0; + overlap_result = GetOverlappedResult(PortHandle, &ReadOverlap, &bytes_read, FALSE); + + /* + ** If we got a good result from GetOverlappedResult and data was read then move it + ** to our circular buffer + */ + if (overlap_result){ + WaitingForSerialCharRead = FALSE; //Flag that we are no longer waiting for a read + + if (bytes_read){ + for (i=0 ; ibuffer_len) bytes_to_copy = buffer_len; + + /* + ** Loop to copy the data from the internal class buffer to the users buffer + */ + for (int i=0 ; iCheck()) { + + //OutputDebugString ("Wincomm - About to call abort function.\n"); + int abort = AbortFunction(); + sprintf (abuffer ,"Wincomm - About function returned %d.\n", abort); + //OutputDebugString (abuffer); + if (abort != COMMSUCCESS) return (abort); + } + + /* + ** If we had lost focus then abort + */ + if (AllSurfaces.SurfacesRestored){ + //OutputDebugString ("Wincomm - Aborting due to loss of focus.\n"); + return (0); + } + + //OutputDebugString ("Wincomm - About to call Read_From_Serial_Port.\n"); + dest_ptr += Read_From_Serial_Port((unsigned char*)(buffer + dest_ptr), (int)buffer_len-dest_ptr); + sprintf (abuffer, "Wincomm - End of inner do loop. Time is %d.\n", timer.Time()); + //OutputDebugString (abuffer); + } while (timer.Time() && + dest_ptr < buffer_len && + !strchr (buffer, 13) ); + + //OutputDebugString ("Wincomm - Exited inner do loop.\n"); + + + /* + ** We need to discard this result if it is just an echo of the 'AT' command we sent + */ + cr_ptr = strstr(buffer,"AT"); + if (cr_ptr){ + if (*buffer == 'A' && *(buffer+1) == 'T' && strchr(buffer,13)){ + //OutputDebugString ("Wincomm - Discarding command echo.\n"); + cr_ptr = strchr(buffer,13); + } + } + + /* + ** If it wasnt an AT echo then strip off any leading CR/LF characters + */ + if (!cr_ptr && (*buffer==13 || *buffer==10)){ + cr_ptr = strchr(buffer,13); + lf_ptr = strchr(buffer,10); + if (!cr_ptr || (lf_ptr && lf_ptr < cr_ptr)){ + //OutputDebugString ("Wincomm - Stripping CR/LF.\n"); + cr_ptr = strchr(buffer,10); + } + } + + /* + ** Copy the good stuff at the end of the buffer over the 'AT' or CR/LF chars + */ + if (cr_ptr){ + //OutputDebugString ("Wincomm - Copying over start of buffer.\n"); + while(*cr_ptr == 13 || *cr_ptr == 10){ + cr_ptr++; + } + + if (cr_ptr != buffer){ + copy_bytes = (int)cr_ptr - (int)buffer; + memcpy(buffer, cr_ptr, buffer_len - copy_bytes); + dest_ptr -= copy_bytes; + } + } + + //OutputDebugString ("Wincomm - End of outer do loop.\n"); + }while(cr_ptr); + + + /* + ** Terminate the string at the first CR character as this is what Greenleaf does + */ + if (strchr(buffer, 13)){ + //OutputDebugString ("Truncating result string.\n"); + *(strchr(buffer, 13)) = 0; + } + + //sprintf (abuffer, "Wincomm - returning remaining delay of %d.\n", timer.Time()); + //OutputDebugString (abuffer); + return (timer.Time()); +} + + + + +/*********************************************************************************************** + * WMC::Dial_Modem -- issue an 'ATD' command to the modem * + * * + * * + * * + * INPUT: string - number to dial * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Use Set_Modem_Dial_Type to choose pulse or tone dialling * + * * + * HISTORY: * + * 1/10/96 2:32PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Dial_Modem(char *dial_number) +{ + char dial_string[80]; + + /* + ** Create the dial command to send to the modem + */ + strcpy (dial_string, "ATD"); + if (DialingMethod == WC_TOUCH_TONE){ + strcat(dial_string, "T"); + }else{ + strcat(dial_string, "P"); + } + + /* + ** Stick a carriage return on the end + */ + strcat (dial_string, dial_number); + strcat (dial_string, "\r"); + + /* + ** Write the dial command to the serial port and wait for the write to complete + */ + Write_To_Serial_Port ((unsigned char*)dial_string, strlen(dial_string)); + Wait_For_Serial_Write(); +} + + + + + +/*********************************************************************************************** + * WMC::Send_Command_To_Modem -- send an 'AT' command to the modem and await a response * + * * + * * + * * + * INPUT: command string * + * terminator byte (usually "\r") * + * ptr to buffer to receive modem result code * + * length of buffer * + * timeout delay for waiting for result * + * number of times to retry the command * + * * + * OUTPUT: result code * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:33PM ST : Created * + *=============================================================================================*/ + +int WinModemClass::Send_Command_To_Modem(char *command, char terminator, char *buffer, int buflen, int delay, int retries) +{ + + int times; + unsigned char tmp_string[80]; + char tmp_buff[80]; + char term_string[2]; + int time; + + /* + ** Build the terminator string + */ + term_string[0] = terminator; + term_string[1] = 0; + + /* + ** Create the command from the supplied command and terminator + */ + strcpy((char*)tmp_string, command); + strcat((char*)tmp_string, term_string); + + + /* + ** Flush out any pending characters from the port + */ + unsigned char nothing_buff[80]; + Read_From_Serial_Port(nothing_buff,80); + Sleep (100); + Read_From_Serial_Port(nothing_buff,80); + + + for (times = 0 ; times= '0' && tmp_buff[0] <='9') && + (tmp_buff[1] >= '0' && tmp_buff[1] <='9') && + (tmp_buff[2] >= '0' && tmp_buff[2] <='9')) { + strncpy(buffer, tmp_buff, MIN(buflen, 80)); + return (MODEM_CMD_OK); + } + } + + /* + ** It was a non-standard(ish) result so copy it to the users buffer + */ + strncpy(buffer, tmp_buff, MIN(buflen, 80)); + + /* + ** Spurious write for no apparent reason. Well it was there in the DOS version so... + */ + Sleep (100); + Write_To_Serial_Port((unsigned char*)"\r",1); + Wait_For_Serial_Write(); + Sleep (100); + } + + return (ASTIMEOUT); +} + + + +/*********************************************************************************************** + * WMC::Set_Echo_Function -- set up the echo function pointer * + * * + * * + * * + * INPUT: ptr to echo function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:35PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Echo_Function( void ( *func )( char c) ) +{ + EchoFunction = func; +} + + + +/*********************************************************************************************** + * WMC::Set_Abort_Function -- set up the abort function pointer * + * * + * * + * * + * INPUT: ptr to abort function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:35PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Abort_Function(int (*func)(void)) +{ + AbortFunction = func; +} + + +/*********************************************************************************************** + * WMC::Get_Port_Handle -- returns a handle to the communications port * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Com port handle * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/23/96 1:25PM ST : Created * + *=============================================================================================*/ +HANDLE WinModemClass::Get_Port_Handle(void) +{ + return (PortHandle); +} + diff --git a/WIN32LIB/SRCDEBUG/WINDOWS.CPP b/WIN32LIB/SRCDEBUG/WINDOWS.CPP new file mode 100644 index 0000000..1903746 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WINDOWS.CPP @@ -0,0 +1,976 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "ww_win.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; + +extern GraphicViewPortClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + // PWG - have to figure out how to do this in windows library + +// Clear_KeyBuffer(); +// Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ +// case KA_LITERAL: +// if (c != (char) 127) { // check if fell thru from extend case +// c = 0; // set to zero for literal case +// } +// c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + (unsigned char)WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & (char)0x07; + c = (char)((c & (char)0x78) >> 3); + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + + diff --git a/WIN32LIB/SRCDEBUG/WINHIDE.CPP b/WIN32LIB/SRCDEBUG/WINHIDE.CPP new file mode 100644 index 0000000..f32de99 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WINHIDE.CPP @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "ww_win.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; +// Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ +// Conditional_Show_Mouse(); +} +#endif + + + diff --git a/WIN32LIB/SRCDEBUG/WPROFILE.CPP b/WIN32LIB/SRCDEBUG/WPROFILE.CPP new file mode 100644 index 0000000..84534f5 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WPROFILE.CPP @@ -0,0 +1,272 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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 : Library profiler * + * * + * File Name : WPROFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Start_Profiler -- initialises the profiler data and starts gathering data * + * Stop_Profiler -- stops the timer and writes the profile data to disk * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +#include +#include +#include +#include "profile.h" +#include +#include + +#define PROFILE + +extern "C"{ +#ifdef PROFILE +unsigned ProfileList [PROFILE_RATE*MAX_PROFILE_TIME]; +#else +unsigned ProfileList [2]; +#endif +unsigned ProfilePtr; +} + +extern "C" void Old_Profiler_Callback ( UINT, UINT , DWORD, DWORD, DWORD ); +extern "C" void New_Profiler_Callback (void); +extern "C" { + extern unsigned ProfileFunctionAddress; +} + +unsigned long ProfilerEvent; //Handle for profiler callback +unsigned long ProfilerThread; //Handle for profiler thread + + +HANDLE CCThreadHandle; +CONTEXT ThreadContext; + + +#if (PROFILE_SYSTEM == NEW_PROFILE_SYSTEM) + +/*********************************************************************************************** + * Thread_Callback -- gets the IP address of our thread and registers it * + * * + * * + * * + * INPUT: Windows timer callback parms - not used * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/2/96 6:37AM ST : Created * + *=============================================================================================*/ + +void CALLBACK Thread_Callback (UINT,UINT,DWORD,DWORD,DWORD) +{ + ThreadContext.ContextFlags = VDMCONTEXT_CONTROL; + if (!InTimerCallback){ + GetThreadContext ( CCThreadHandle , &ThreadContext ); + }else{ + GetThreadContext (TimerThreadHandle , &ThreadContext); + } + + ProfileFunctionAddress = ThreadContext.Eip; + New_Profiler_Callback(); +} + + +/*********************************************************************************************** + * Profile_Thread -- this is the thread our profiler runs in. It just starts off a timer and * + * then buggers off into an infinite message loop. We shouldnt get messages * + * here as this isnt our primary thread * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/2/96 6:39AM ST : Created * + *=============================================================================================*/ +void Profile_Thread (void) +{ + MSG msg; + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , Thread_Callback , 0 , TIME_PERIODIC); + //ProfilerEvent = timeSetEvent (100 , 1 , Thread_Callback , 0 , TIME_ONESHOT); + do { + GetMessage(&msg,NULL,0,0); + } while(1); +} + +#endif //(PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + + +/*********************************************************************************************** + * Start_Profiler -- initialises the profiler system and starts sampling * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: There may be a pause when sampling starts as Win95 does some VMM stuff * + * * + * HISTORY: * + * 11/20/95 5:12PM ST : Created * + *=============================================================================================*/ + +void __cdecl Start_Profiler (void) +{ +#ifdef PROFILE + if (!ProfilerEvent){ + memset (&ProfileList[0],-1,PROFILE_RATE*MAX_PROFILE_TIME*4); + } + + Profile_Init(); + + if (!ProfilerEvent){ + +#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + /* + ** Old profile system - just set up a timer to monitor the global variable based on + ** the last place __PRO was called from + */ + ProfilerEvent = timeSetEvent (1000/PROFILE_RATE , 1 , (void CALLBACK (UINT,UINT,DWORD,DWORD,DWORD))Old_Profiler_Callback , 0 , TIME_PERIODIC); +#else + /* + ** New profile system - create a second thread that will do all the profiling + ** using GetThreadContext + */ + if ( DuplicateHandle( GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&CCThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS) ){ + ProfilerEvent= (unsigned)CreateThread(NULL,2048,(LPTHREAD_START_ROUTINE)&Profile_Thread,NULL,0,&ProfilerThread); + } +#endif + + } + +#else + ProfilerEvent = 0; +#endif + +} + + + +/*********************************************************************************************** + * Stop_Profiler -- stops the sampling timer and writes the colledted data to disk * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Writes to file PROFILE.BIN * + * * + * HISTORY: * + * 11/20/95 5:13PM ST : Created * + *=============================================================================================*/ + +void __cdecl Stop_Profiler (void) +{ + if (ProfilerEvent){ + +#if (PROFILE_SYSTEM == OLD_PROFILE_SYSTEM) + // + // Old system - just remove the timer event + // + timeKillEvent(ProfilerEvent); +#else + // + // New system - kill the profiling thread + // + TerminateThread((HANDLE)ProfilerThread,0); +#endif + + ProfilerEvent=NULL; + + Profile_End(); + + int handle = Open_File ( "profile.bin" , WRITE ); + if (handle != WW_ERROR){ + Write_File (handle , &ProfileList[0] , ProfilePtr*4); + Close_File (handle); + } + } +} + + diff --git a/WIN32LIB/SRCDEBUG/WRITELBM.CPP b/WIN32LIB/SRCDEBUG/WRITELBM.CPP new file mode 100644 index 0000000..d3c42c0 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WRITELBM.CPP @@ -0,0 +1,423 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/writelbm.cpp 1.1 1994/04/20 14:38:57 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Write LBM File * + * * + * File Name : writelbm.c * + * * + * Programmer : Scott Bowen * + * * + * Start Date : November 18, 1991 * + * * + * Last Update : November 19, 1991 [SB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Line -- convert one plane of one row to a packed plane * + * Write_BMHD -- writes out the bit map header (LocalHeader) * + * Write_Body -- writes out compressed data in an LBM file * + * Write_CMAP -- Writes out CMAP (palette) information * + * Write_LBM_File -- Writes out a file in LBM format * + * Write_Row -- compresses and writes a row plane to .lbm file * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +// At the end of this file there is an IFF definition for a .LBM file. + +#include "iff.h" +#include "file.h" +#include +#include +#include + + + // A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size +typedef struct { + unsigned short w, h; // raster width & height in pixels + unsigned short x, y; // position for this image + unsigned char planes; // # source bitplanes + unsigned char masking; // masking technique + unsigned char compression; // compression algoithm + unsigned char pad1; // UNUSED. For consistency, put 0 here. + unsigned short transcolor; // transparent "color number" + unsigned char xaspect, yaspect; // aspect ratio, a rational number x/y + unsigned short pagewidth, pageheight; // source "page" size in pixels +} BitMapHeaderType; + + +// All values in LocalHeader are always the same except planes. This is set in Write_BMHD +// the short values must be in low-high order for compatibility. + +PRIVATE BitMapHeaderType LocalHeader = { + 0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask + 1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect + 0x4001, 0xC800 }; // pagewidth, pageheight + + + // Used to verify that the write of the header was valid +#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L) + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE long Write_BMHD(int lbmhandle, int bitplanes); +PRIVATE long Write_CMAP(int lbmhandle, unsigned char * palette, int bitplanes); +PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes); +PRIVATE long Write_Row(int lbmhandle, unsigned char *buffer); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * WRITE_LBM_FILE -- Writes out a file in LBM format * + * * + * INPUT: int lbmhandle -- lbm file handle already opened by caller * + * BufferClass buff -- buff where MCGA picture is * + * int bitplane -- number of bitplanes to convert to * + * char *palette -- pointer to palette for buff * + * * + * OUTPUT: Returns BOOL -- successfull or not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/18/1991 SB : Created. * + *=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette) +{ + long filesize; + + + Seek_File(lbmhandle, 0L, SEEK_SET); // goto beginning of file + + Write_File(lbmhandle, "FORM????ILBM", 12L); // First 12 bytes of all .lbm files + // size is unkown so write ???? + filesize = 12L; // 4 bytes for "ILBM" + + filesize += Write_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size) + filesize += Write_CMAP(lbmhandle, palette, bitplanes); // write out CMAP + + // Write out the body, or compressed picture image. This size will depend + // on the compression, but the value passed back is what the compressor + // assumed was written to file + + filesize += Write_BODY(lbmhandle, buff, bitplanes); + + // Verify that we were able to write out the file without running out of space + if (Seek_File(lbmhandle, 0L, SEEK_END) != filesize) { + return(FALSE); + } + + Seek_File(lbmhandle, 4L, SEEK_SET); // goto beginning of file + filesize = Reverse_Long(filesize - 8L); // - 8 because of "FORM" + WORD (size) + Write_File(lbmhandle, (char *) &filesize, 4L); // patch in filesize + + return(TRUE); +} + + +/*************************************************************************** + * WRITE_BMHD -- writes out the bit map header (LocalHeader) * + * * + * INPUT: int lbmhandle -- file handle for lbm file * + * int pitplanes -- number of bitplanes to write out * + * * + * OUTPUT: long number of bytes hopefully written out to .LBM file * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +PRIVATE long Write_BMHD(int lbmhandle, int bitplanes) +{ + long size; + + Write_File(lbmhandle, "BMHD", 4L); // write out chunk title + size = Reverse_Long(sizeof(LocalHeader)); // write out size of LocalHeader chunk + Write_File(lbmhandle, (char *) &size, 4L); + + LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader + + // Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure + + return(Write_File(lbmhandle, (char *) &LocalHeader, + (sizeof(LocalHeader) + 1) & 0xFFFE) + 8L); +} + + +/*************************************************************************** + * WRITE_CMAP -- Writes out CMAP (palette) information * + * * + * * + * INPUT: int lbmhandle -- file handle of lbm file * + * char * palette -- pointer to paletter information * + * int bitplanes -- used to figure out size of palette * + * * + * OUTPUT: long number of bytes that should have been written out to .LBM. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE long Write_CMAP(int lbmhandle, unsigned char * palette, int bitplanes) +{ + int color, r, g, b, colors; + long size; + unsigned char *pal_ptr; + char rgb[3]; + + + Write_File(lbmhandle, "CMAP", 4L); // write out palette info + colors = 1 << bitplanes; // colors = 2 to the bitplanes + size = Reverse_Long(colors * 3L); // size = colors * 3 guns + + Write_File(lbmhandle, (char *) &size, 4L); + + for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color + + if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values + r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM + } // so LBM_val = (DP_val * 4) | 3 if DP_val != 0 + if ((g = *pal_ptr++) != 0) { + g = (g << 2) | 0x03; + } + if ((b = *pal_ptr++) != 0) { + b = (b << 2) | 0x03; + } + rgb[0] = r; // assign gun values to an array to write out + rgb[1] = g; + rgb[2] = b; + + Write_File(lbmhandle, rgb, 3L); + } + // size = colors * 3 + return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + WORD (size) +} + + +/*************************************************************************** + * WRITE_BODY -- writes out compressed data in an LBM file * + * * + * INPUT: int lbmhandle -- file handle of lbm file * + * * + * OUTPUT: long - number of byte written * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE long Write_BODY(int lbmhandle, BufferClass& buff, int bitplanes) +{ + long bodysize = 0; + long actualsize; + long size; + int planebit; + int line, plane; + unsigned char buffer[40]; + unsigned char *buffptr; + + Write_File(lbmhandle, "BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize + + buffptr = (unsigned char *) buff.Get_Buffer(); // point to beginning of buff + + for (line = 0; line < 200; line++) { + planebit = 1; // start with bit 1 set + + for (plane = 0; plane < bitplanes; plane++) { + Pack_2_Plane(buffer, buffptr, planebit); // convert to planar + bodysize += Write_Row(lbmhandle, buffer); // write to to the BODY in the LBM + + planebit <<= 1; // set next bit + } + + buffptr += 320; // row size is 320 + } + + actualsize = bodysize + (bodysize&0x01); + + if (actualsize != bodysize) { + Write_File(lbmhandle, buffer, 1); // Padd the block. + } + + Seek_File(lbmhandle, -(actualsize + 4L), SEEK_CUR); // Patch in chunksize + size = Reverse_Long(bodysize); + Write_File(lbmhandle, (char *) &size ,4L); + + return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes +} + +/*************************************************************************** + * WRITE_ROW -- compresses and writes a row plane to .lbm file * + * * + * INPUT: int lbmhandle -- lbm file handle * + * unsigned char *buffer -- pointer to buffer to be written out * + * * + * OUTPUT: long size of chunk that should have been written out * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +// this algorithm was taken from WILBM.c written by EA that was in the +// 1985 yearbook. This is the compression method that DP.EXE uses. +// Change only if DP.EXE changes. + +PRIVATE long Write_Row(int lbmhandle, unsigned char *buffer) +{ + int i; + int chunksize = 0; + int dataLength = 40; // 320 rows / 8 ( 1 plane per row) + unsigned char repCode, current, curr_plus_2; + unsigned char *buffptr; + + while (dataLength) { + + // If at least 2 more bytes and they are equal, then replicate + + if ((dataLength >= 2) && (buffer[0] == buffer[1])) { + buffptr = buffer; + for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) { + if (*buffptr != buffptr[1]) { + break; + } + buffptr++; + } + i++; + repCode = -i + 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as -count+1 + Write_File(lbmhandle, buffer, 1L); // Write byte to replicate + buffer += i; + dataLength -= i; + chunksize += 2; + + } + else { // Copy literally till 3 byte run or two 2 byte runs found + + for (i = 0; (i <= 128) && (i < dataLength); i++) { + current = buffer[i]; + curr_plus_2 = buffer[i + 2]; + + if (i == dataLength - 1) + continue; + if (current != buffer[i + 1]) + continue; + if (i == dataLength - 2) + continue; + if (current == curr_plus_2) + break; + if (i == dataLength - 3) + continue; + if (curr_plus_2 == buffer[i + 3]) + break; + } + repCode = i - 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as count-1 + Write_File(lbmhandle, buffer, (long) i); // Write 'count' bytes + buffer += i; + dataLength -= i; + chunksize += i + 1; + } + } // end while + + return(chunksize); +} + + +#if(FALSE) + +This is a definition of a DPII .LBM file. +Below this definition are differences in DPIIe .LMB files. + +Created by : Scott K. Bowen Nov 18, 1991 + +Start with .LBM to read definition : + +.LBM -> "FORM" + FILESIZE + "ILMB" + CHUNKS + +BITPLANES -> (word) // number of bit planes used +BLUE -> (byte) // blue color gun value +BMHD -> "BMHD" + CHUNKSIZE + CONTEXT +BODY -> + +CHUNKS -> BMHD | BODY | CMAP | DPPV | CRNG | ???? +CHUNKSIZE -> (long) // size of chunk not including header or size. +CMAP -> "CMAP" + CHUNKSIZE + PALETTE_INFO +COMPRESS_METHOD -> (byte) // compression method used +CONTEXT -> WIDTH + HEIGHT + POSX + POSY + #_BITPLANES + MASKING + + COMPRESS_METHOD + PAD + TRANS_COL + XASPECT + YASPECT + + PAGEWIDTH + PAGEHEIGHT +CRNG -> // we do not use + +DPPV -> // we do not use + +FILESIZE -> (long) //size of file minus (sizeof(FORM) + sizeof(FILESIZE) + +GREEN -> (byte) // green color gun value + +HEIGHT -> (word) // of picture + +MASKING -> (byte) // masking type ? + +NUM_COLORS -> //number of colors used depending on format + +PAGE_WIDTH -> (word) // width of page +PAGE_HEIGHT -> (word) // height of page +PALETTE_INFO-> (RED+GREEN+BLUE) @ NUM_COLORS +PAD -> (byte) // not used. used as a padding +POSX -> (word) // staring position +POSY -> (word) // staring position + +RED -> (byte) // red color gun value + +TRANS_COL -> (word) // transparrent color + +WIDTH -> (word) // of picture + +XASPECT -> (byte) // x aspect ratio + +YASPECT -> (byte) // y aspect ratio + +???? -> // other possibilities + + +Differences in DPII enhance version + +.LBM -> "FORM" + FILESIZE + "PBM " + CHUNKS +DPPV -> DPPS // uses dpps instead of dppv +CHUNKS -> + TINY // add these to old definition + + + + +#endif + + diff --git a/WIN32LIB/SRCDEBUG/WRITEPCX.CPP b/WIN32LIB/SRCDEBUG/WRITEPCX.CPP new file mode 100644 index 0000000..459d1e2 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WRITEPCX.CPP @@ -0,0 +1,181 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +#include +static void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + *=========================================================================*/ +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ) +{ + unsigned char palcopy[256 * 3]; + unsigned i ; + unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + + for ( i = 0 ; i < header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, ptr + i * VP_Scan_Line ) ; + + Mem_Copy(palette, palcopy, 256 * 3); + pal = ( RGB * ) palcopy ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palcopy , 256 * sizeof (RGB) ) ; + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + + +void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + char * file_ptr ; + char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + + for ( i = 1 ; i < scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else { + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last ) ; + } + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} + diff --git a/WIN32LIB/SRCDEBUG/WSA.CPP b/WIN32LIB/SRCDEBUG/WSA.CPP new file mode 100644 index 0000000..dc23416 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WSA.CPP @@ -0,0 +1,1156 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./wsa.c 1.16 1994/05/20 15:35:27 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Animation System * + * * + * File Name : WSA.C * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 20, 1991 * + * * + *-------------------------------------------------------------------------* + * There are many different ways that the user can use the WSA library * + * module. The options are as follows : * + * * + * System Allocation vs User Buffer - The user may request that the * + * system allocate the needed buffer from the heap or the user may * + * pass his own buffer in for the animator to use. * + * * + * Resident vs File based - If there is enough RAM, the user may put the * + * entire animation into RAM for fastest animations. If there is * + * not enouph RAM, the system will automatically make it so each * + * frame will be read off disk when needed. * + * * + * Direct to Page vs Use of a user buffer -- Noramally Direct to page * + * is the best method both in speed and in RAM need to hold anim. * + * One may want to use the write to user buffer method if they * + * are using the animation in a non sequencial order. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Animate_Frame -- Displays a frame of a given animation * + * Get_Animation_Frame_Count -- Return Number of frames in an animation. * + * Get_Animation_X -- Gets the x from an animation * + * Get_Animation_Y -- Gets the y from an animation * + * Get_Animation_Width -- Gets the width from an animation * + * Get_Animation_Height -- The height of the animation we are processing * + * Apply_Delta -- Copies frame into delta buffer, then applies to target * + * Close_Animation -- Close the animation, freeing the space if necessary* + * Get_File_Frame_Offset -- Get offset of a delta frame from animate file* + * Get_Resident_Frame_Offset -- Gets frame offset of animate file in RAM * + * Open_Animation -- Opens an animation file and reads into buffer * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "wsa.h" +#include +#include +#include +#include +#include +#include + +// +// WSA animation header allocation type. +// If we need more then 8 flags for the flags variable, we can combine +// USER_ALLOCATED with SYS_ALLOCATED and combine FILE with RESIDENT. +// +#define WSA_USER_ALLOCATED 0x01 +#define WSA_SYS_ALLOCATED 0x02 +#define WSA_FILE 0x04 +#define WSA_RESIDENT 0x08 +#define WSA_TARGET_IN_BUFFER 0x10 +#define WSA_LINEAR_ONLY 0x20 +#define WSA_FRAME_0_ON_PAGE 0x40 +#define WSA_AMIGA_ANIMATION 0x80 +#define WSA_PALETTE_PRESENT 0x100 +#define WSA_FRAME_0_IS_DELTA 0x200 + +// 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 lp_asm.asm. +#define DO_XOR 0x0 +#define DO_COPY 0x01 +#define TO_VIEWPORT 0x0 +#define TO_PAGE 0x02 + + + + +typedef struct { + unsigned short current_frame; + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + char *delta_buffer; + char *file_buffer; + char file_name[ 13 ]; + short flags; + // New fields that animate does not know about below this point. SEE EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT + short file_handle; + unsigned long anim_mem_size; +} SysAnimHeaderType; + +// NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE +// UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING +// IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. +#define EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT (sizeof(short) + sizeof(unsigned long)) + + +// +// Header structure for the file. +// NOTE: The 'total_frames' field is used to differentiate between Amiga and IBM +// animations. Amiga animations have the HIGH bit set. +// +typedef struct { + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + short flags; + unsigned long frame0_offset; + unsigned long frame0_end; + /* unsigned long data_seek_offset, unsigned short frame_size ... */ +} WSA_FileHeaderType; + +#define WSA_FILE_HEADER_SIZE ( sizeof(WSA_FileHeaderType) - (2 * sizeof(unsigned long)) ) + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ); +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust); +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_ANIMATION -- Opens an animation file and reads into buffer * + * * + * INPUT: char *file_name of animation sequence file. * + * char *user_buffer pointer if one exists (NULL ok) * + * unsigned long user_buffer_size if known (NULL ok) * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to palette space for return (NULL ok) * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: May return NULL, please check. * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette) +{ + int fh, anim_flags; + int palette_adjust; + unsigned int offsets_size; + unsigned int frame0_size; + long target_buffer_size, delta_buffer_size, file_buffer_size; + long max_buffer_size, min_buffer_size; + char *sys_anim_header_buffer; + char *target_buffer; + char *delta_buffer, *delta_back; + SysAnimHeaderType *sys_header; + WSA_FileHeaderType file_header; + + /*======================================================================*/ + /* Open the file to get the header information */ + /*======================================================================*/ + + anim_flags = 0; + fh = Open_File(file_name, READ); + Read_File(fh, (char *) &file_header, sizeof(WSA_FileHeaderType)); + + /*======================================================================*/ + /* If the file has an attached palette then if we have a valid palette */ + /* pointer we need to read it in. */ + /*======================================================================*/ + + if (file_header.flags & 1) { + anim_flags |= WSA_PALETTE_PRESENT; + palette_adjust = 768; + + if (palette != NULL) { + Seek_File(fh, sizeof(unsigned long) * (file_header.total_frames), SEEK_CUR); + Read_File(fh, palette, 768L); + } + + } else { + palette_adjust = 0; + } + + // Check for flag from ANIMATE indicating that this animation was + // created from a .LBM and a .ANM. These means that the first + // frame is a XOR Delta from a picture, not black. + if (file_header.flags & 2) { + anim_flags |= WSA_FRAME_0_IS_DELTA; + } + + + // Get the total file size minus the size of the first frame and the size + // of the file header. These will not be read in to save even more space. + file_buffer_size = Seek_File(fh, 0L, SEEK_END); + + if (file_header.frame0_offset) { + long tlong; + + tlong = file_header.frame0_end - file_header.frame0_offset; + frame0_size = (unsigned short) tlong; + } + else { + anim_flags |= WSA_FRAME_0_ON_PAGE; + frame0_size = 0; + } + + file_buffer_size -= palette_adjust + frame0_size + WSA_FILE_HEADER_SIZE; + + // We need to determine the buffer sizes required for the animation. At a + // minimum, we need a target buffer for the uncompressed frame and a delta + // buffer for the delta data. We may be able to make the file resident, + // so we will determine the file size. + // + // If the target buffer is in the user buffer + // Then figure its size + // and set the allocation flag + // Else size is zero. + // + if (user_flags & WSA_OPEN_DIRECT) { + target_buffer_size = 0L; + } + else { + anim_flags |= WSA_TARGET_IN_BUFFER; + target_buffer_size = (unsigned long) file_header.pixel_width * file_header.pixel_height; + } + + // NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE + // UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING + // IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. + delta_buffer_size = (unsigned long) file_header.largest_frame_size + EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT; + min_buffer_size = target_buffer_size + delta_buffer_size; + max_buffer_size = min_buffer_size + file_buffer_size; + + // check to see if buffer size is big enough for at least min required + if (user_buffer && (user_buffer_size < min_buffer_size)) { + Close_File(fh); + return(NULL); + } + + // A buffer was not passed in, so do allocations + if (user_buffer == NULL) { + + // If the user wants it from the disk, then let us give it to him, + // otherwise, try to give a max allocation he can have. + if (user_flags & WSA_OPEN_FROM_DISK) { + user_buffer_size = min_buffer_size; + } + // else no buffer size, then try max configuration. + else if (!user_buffer_size) { + user_buffer_size = max_buffer_size; + } + // else if buffer specified is less then max needed, give min. + else if (user_buffer_size < max_buffer_size) { + user_buffer_size = min_buffer_size; + } + // otherwise we only want to alloc what we need. + else { + user_buffer_size = max_buffer_size; + } + + + // Check to see if enough RAM available for buffer_size. + if (user_buffer_size > Ram_Free(MEM_NORMAL)) { + + // If not enough room for even the min, return no buffer. + + if (min_buffer_size > Ram_Free(MEM_NORMAL)) { + Close_File(fh); + return(NULL); + } + + // Else make buffer size the min and allocate it. + user_buffer_size = min_buffer_size; + } + + // allocate buffer needed + user_buffer = (char *) Alloc(user_buffer_size, MEM_CLEAR); + + anim_flags |= WSA_SYS_ALLOCATED; + } + else { + // Check to see if the user_buffer_size should be min or max. + if ((user_flags & WSA_OPEN_FROM_DISK) || (user_buffer_size < max_buffer_size)) { + user_buffer_size = min_buffer_size; + } + else { + user_buffer_size = max_buffer_size; + } + anim_flags |= WSA_USER_ALLOCATED; + } + + + // Set the pointers to the RAM buffers + sys_anim_header_buffer = user_buffer; + target_buffer = (char *) Add_Long_To_Pointer(sys_anim_header_buffer, sizeof(SysAnimHeaderType)); + delta_buffer = (char *) Add_Long_To_Pointer(target_buffer, target_buffer_size); + + // Clear target buffer if it is in the user buffer. + if (target_buffer_size) { + memset( target_buffer, 0, (unsigned short) target_buffer_size ); + } + + // Poke data into the system animation header (start of user_buffer) + // current_frame is set to total_frames so that Animate_Frame() knows that + // it needs to clear the target buffer. + + sys_header = ( SysAnimHeaderType * ) sys_anim_header_buffer; + sys_header -> current_frame = + sys_header -> total_frames = file_header.total_frames; + sys_header -> pixel_x = file_header.pixel_x; + sys_header -> pixel_y = file_header.pixel_y; + sys_header -> pixel_width = file_header.pixel_width; + sys_header -> pixel_height = file_header.pixel_height; + sys_header -> anim_mem_size = user_buffer_size; + sys_header -> delta_buffer = delta_buffer; + sys_header -> largest_frame_size = + (unsigned short) (delta_buffer_size - sizeof(SysAnimHeaderType)); + + strcpy(sys_header->file_name, file_name); + + // Figure how much room the frame offsets take up in the file. + // Add 2 - one for the wrap around and one for the final end offset. + offsets_size = (file_header.total_frames + 2) << 2; + + // Can the user_buffer_size handle the maximum case buffer? + if ( user_buffer_size == max_buffer_size) { + + // + // set the file buffer pointer, + // Skip over the header information. + // Read in the offsets. + // Skip over the first frame. + // Read in remaining frames. + // + + sys_header->file_buffer = (char *)Add_Long_To_Pointer(delta_buffer,sys_header->largest_frame_size); + Seek_File( fh, WSA_FILE_HEADER_SIZE, SEEK_SET); + Read_File( fh, sys_header->file_buffer, offsets_size); + Seek_File( fh, frame0_size + palette_adjust, SEEK_CUR); + Read_File( fh, sys_header->file_buffer + offsets_size, + file_buffer_size - offsets_size); + + // + // Find out if there is an ending value for the last frame. + // If there is not, then this animation will not be able to + // loop back to the beginning. + // + if (Get_Resident_Frame_Offset( sys_header->file_buffer, sys_header->total_frames + 1)) + anim_flags |= WSA_RESIDENT; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_RESIDENT; + } + else { // buffer cannot handle max_size of buffer + + if(Get_File_Frame_Offset( fh, sys_header->total_frames + 1, palette_adjust)) + anim_flags |= WSA_FILE; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_FILE; +//// + sys_header->file_buffer = NULL; + } + + // Figure where to back load frame 0 into the delta buffer. + delta_back = (char *)Add_Long_To_Pointer(delta_buffer, + sys_header->largest_frame_size - frame0_size); + + // Read the first frame into the delta buffer and uncompress it. + // Then close it. + Seek_File( fh, WSA_FILE_HEADER_SIZE + offsets_size + palette_adjust, SEEK_SET); + Read_File( fh, delta_back, frame0_size); + + // We do not use the file handle when it is in RAM. + if (anim_flags & WSA_RESIDENT) { + sys_header -> file_handle = (short) -1; + Close_File(fh); + } + else { + sys_header -> file_handle = (short)fh; + } + + LCW_Uncompress(delta_back, delta_buffer, sys_header->largest_frame_size); + + // Finally set the flags, + sys_header->flags = (short)anim_flags; + + // return valid handle + return( user_buffer ); +} + + +/*************************************************************************** + * CLOSE_ANIMATION -- Close the animation, freeing the space if necessary. * + * * + * INPUT: void *handle to the animation data buffer * + * * + * OUTPUT: none * + * * + * WARNINGS: handle MUST have been returned by Open_Animation * + * * + * HISTORY: * + * 11/23/1991 ML : Created. * + *=========================================================================*/ +void __cdecl Close_Animation( void *handle ) +{ + SysAnimHeaderType *sys_header; + + // Assign our local system header pointer to the beginning of the handle space + sys_header = (SysAnimHeaderType *) handle; + + // Close the WSA file in it was disk based. + if (sys_header->flags & WSA_FILE) { + Close_File(sys_header->file_handle); + } + + // Check to see if the buffer was allocated OR the programmer provided the buffer + if (handle && sys_header->flags & WSA_SYS_ALLOCATED) { + Free(handle); + } +} + +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma off (unreferenced) +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + if (view.Lock()!=TRUE) return (FALSE); + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd() + view.Get_Pitch(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + view.Unlock(); + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = (short)frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + view.Unlock(); + return TRUE; +} +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma argsused +#ifdef cuts +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} +#endif + + +/*************************************************************************** + * ANIMATE_FRAME_COUNT -- Return Number of frames in an animation. * + * * + * INPUT: void *handle to the animation. * + * * + * OUTPUT: int number of frames in animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/05/1991 SB : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Frame_Count(void *handle) +{ + SysAnimHeaderType *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return((short)sys_header->total_frames); +} + + +/*************************************************************************** + * GET_ANIM_X -- Gets the x from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the x of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1992 DRD : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_X(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_x); +} + +/*************************************************************************** + * GET_ANIM_Y -- Gets the y from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the y of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Y(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_y); +} + +/*************************************************************************** + * GET_ANIM_WIDTH -- Gets the width from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the width of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Width(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_width); +} + +/*************************************************************************** + * GET_ANIM_HEIGHT -- The height of the animation we are processing * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the height of the animation we are processing * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Height(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_height); +} + + +/*************************************************************************** + * GET_ANIM_PALETTE -- Returns true if the anim had an attached palette * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int True if the animation has a set palette. False if the * + * animation does not. * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Palette(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->flags & WSA_PALETTE_PRESENT); +} + + +/*************************************************************************** + * GET_ANIMATION_SIZE -- Return the amount of memory the animation is using* + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: unsigned long number of byte used by animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/23/1994 SKB : Created. * + *=========================================================================*/ +unsigned long __cdecl Get_Animation_Size(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->anim_mem_size); +} + +/* :::::::::::::::::::::::::::: PRIVATE FUNCTIONS :::::::::::::::::::::::::::::: */ + + +/*************************************************************************** + * GET_RESIDENT_FRAME_OFFSET -- Gets frame offset of animate file in RAM * + * * + * INPUT: char *file_buffer in RAM of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ) +{ + unsigned long frame0_size; + unsigned long *lptr; + + // If there is a frame 0, the calculate its size. + lptr = (unsigned long *) file_buffer; + + if (*lptr) { + frame0_size = lptr[1] - *lptr; + } else { + frame0_size = 0; + } + + // Return the offset into RAM for the frame. + lptr += frame; + if (*lptr) + return (*lptr - (frame0_size + WSA_FILE_HEADER_SIZE)); + else + return (0L); +} + + +/*************************************************************************** + * GET_FILE_FRAME_OFFSET -- Get offset of a delta frame from animate file. * + * * + * INPUT: int file_handle of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust) +{ + unsigned long offset; + + Seek_File(file_handle, (frame << 2) + WSA_FILE_HEADER_SIZE, SEEK_SET); + + if (Read_File(file_handle, (char *) &offset, sizeof(unsigned long)) != sizeof(unsigned long)) { + offset = 0L; + } + offset += palette_adjust; + return( offset ); +} + + +/*************************************************************************** + * APPLY_DELTA -- Copies frame into delta buffer, then applies to target * + * * + * INPUT: SysAnimHeaderType *sys_header - pointer to animation buffer.* + * int curr_frame - frame to put into target buffer. * + * * + * OUTPUT: BOOL - Return wether or not it worked. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w) +{ + char *data_ptr, *delta_back; + int file_handle, palette_adjust; + unsigned long frame_data_size, frame_offset; + + + palette_adjust = ((sys_header->flags & WSA_PALETTE_PRESENT) ? 768 : 0); + delta_back = sys_header->delta_buffer; + + if (sys_header->flags & WSA_RESIDENT) { + // Get offset of the given frame in the resident file + // Get the size of the frame <- (frame+1 offset) - (offset) + // Point at the delta data + // figure offset to load data into end of delta buffer + // copy it into buffer + + frame_offset = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame); + frame_data_size = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame + 1) - frame_offset; + + data_ptr = (char *)Add_Long_To_Pointer(sys_header->file_buffer, frame_offset); + delta_back = (char *)Add_Long_To_Pointer(delta_back, + sys_header->largest_frame_size - frame_data_size); + + Mem_Copy( data_ptr, delta_back, frame_data_size ); + + } else if (sys_header -> flags & WSA_FILE) { + + // Open up file because not file not in RAM. + // Get offset of the given frame in the file on disk + // Get the size of the frame <- (frame+1 offset) - (offset) + // Return if Get_.._offset() failed. -- need error handling???? + // Seek to delta data. + // figure offset to load data into end of delta buffer + // Read it into buffer -- Return if correct amount not read.-- errors?? + + file_handle = sys_header->file_handle; + Seek_File(file_handle, 0L, SEEK_SET); + + frame_offset = Get_File_Frame_Offset(file_handle, curr_frame, palette_adjust); + frame_data_size = Get_File_Frame_Offset(file_handle, curr_frame + 1, palette_adjust) - frame_offset; + + if (!frame_offset || !frame_data_size) { + return(FALSE); + } + + Seek_File(file_handle, frame_offset, SEEK_SET); + delta_back = (char *)Add_Long_To_Pointer(delta_back, sys_header->largest_frame_size - frame_data_size); + + if (Read_File(file_handle, delta_back, frame_data_size) != frame_data_size) { + return(FALSE); + } + } + + // Uncompress data at end of delta buffer to the beginning of delta buffer. + // Find start of target buffer. + // Apply the XOR delta. + + LCW_Uncompress(delta_back, sys_header->delta_buffer, sys_header->largest_frame_size); + + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + Apply_XOR_Delta(dest_ptr, sys_header->delta_buffer); + } + else { + Apply_XOR_Delta_To_Page_Or_Viewport(dest_ptr, sys_header->delta_buffer, + sys_header->pixel_width, dest_w, DO_XOR); + } + + return(TRUE); +} + diff --git a/WIN32LIB/SRCDEBUG/WWMOUSE.ASM b/WIN32LIB/SRCDEBUG/WWMOUSE.ASM new file mode 100644 index 0000000..53f1a35 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/WWMOUSE.ASM @@ -0,0 +1,662 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : December 12, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "drawbuff.inc" +INCLUDE "gbuffer.inc" +INCLUDE "shape.inc" +INCLUDE ".\mouse.inc" + +GLOBAL C LCW_Uncompress:NEAR +GLOBAL C Get_Shape_Uncomp_Size :NEAR +GLOBAL C Get_Shape_Width :NEAR +GLOBAL C Get_Shape_Original_Height :NEAR +GLOBAL _ShapeBuffer :DWORD + +CODESEG + + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* int store - whether to store buffer or save * +;* * +;* OUTPUT: none * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Mouse_Shadow_Buffer(GraphicBufferClass *src/dest, * +;* void *buffer +;* int x_pixel, * +;* int y_pixel, * +;* int store); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Mouse_Shadow_Buffer:NEAR + PROC Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this: DWORD + ARG src_dst_obj: DWORD + ARG buffer:DWORD + ARG x: DWORD + ARG y: DWORD + ARG hotx: DWORD + ARG hoty: DWORD + ARG store: DWORD + + local x0: dword + local y0: dword + local x1: dword + local y1: dword + local buffx0: dword + local buffy0: dword + + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov esi, [this] ; get offset to mouse data + mov edi, [src_dst_obj] ; get offset to mouse data + + mov eax, [x] + mov ebx, [y] + sub eax, [hotx] + sub ebx, [hoty] + + mov [x0], eax + mov [y0], ebx + add eax, [(MouseType esi).CursorWidth] + add ebx, [(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [buffer] + mov [buffy0], eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1], eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1], eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + push ebp + cmp [store], 1 ; are we storing page? + je ??store_entry ; if so go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp, ecx +??restore_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi, edi ; xchg the source and the dest + mov ebp, ecx + +??store_loop: + mov ecx, eax ; get number to really write + rep movsb ; store them into the buffer + add esi, edx ; move past right clipped pixels + add edi, ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: MouseClass * - pointer to mouse class data * +;* GraphicBufferClass * - screen to draw the mouse on * +;* int x - x position to store the mouse at * +;* int y - y position to store the mouse at * +;* * +;* Note: The x and y that this routine expects to receive are based on * +;* the mouse cursor position. This routine automatically adjusts * +;* for hot spot, so that adjustment should not be made prior to * +;* this point. * +;* * +;* PROTO: void Draw_Mouse( MouseClass * mouse_data, * +;* GraphicBufferClass *destination, * +;* int x_pixel, * +;* int y_pixel); * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Draw_Mouse:NEAR + PROC Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD + ARG dest:DWORD + ARG mousex:DWORD + ARG mousey:DWORD + + local x0:dword + local y0:dword + local x1:dword + local y1:dword + local buffx0:dword + local buffy0:dword + + + mov esi, [this] ; get 32 bit offset to mouse data + mov edi, [dest] ; get 32 bit offset to dest buffer + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax, [mousex] + mov ebx, [mousey] + sub eax, [(MouseType esi).MouseXHot] + sub ebx, [(MouseType esi).MouseYHot] + + mov [x0], eax + mov [y0], ebx + add eax,[(MouseType esi).CursorWidth] + add ebx,[(MouseType esi).CursorHeight] + mov [x1], eax + mov [y1], ebx + + mov [buffx0], 0 + mov eax, [(MouseType esi).MouseCursor] + mov [buffy0], eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax, eax + xor edx, edx + + mov ecx, [x0] + mov ebx, [x1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [x0] + mov ebx, [x1] + sub ecx, [(GraphicViewPort edi).GVPWidth] + sub ebx, [(GraphicViewPort edi).GVPWidth] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + shld eax, ecx, 1 + shld edx, ebx, 1 + + mov ecx, [y0] + mov ebx, [y1] + sub ecx, [(GraphicViewPort edi).GVPHeight] + sub ebx, [(GraphicViewPort edi).GVPHeight] + dec ecx + dec ebx + shld eax, ecx, 1 + shld edx, ebx, 1 + + xor al, 5 + xor dl, 5 + mov ah, al + test dl, al + jnz ??out + or al, dl + jz ??not_clip + + test ah, 1000b + jz ??scr_left_ok + mov ebx, [x0] + neg ebx + mov [buffx0], ebx + mov [x0], 0 + +??scr_left_ok: + test ah, 0010b + jz ??scr_bottom_ok + mov ebx, [y0] + neg ebx + imul ebx, [(MouseType esi).CursorWidth] + add [buffy0], ebx + mov [y0], 0 + +??scr_bottom_ok: + test dl, 0100b + jz ??scr_right_ok + mov eax, [(GraphicViewPort edi).GVPWidth] ; get width into register + mov [x1] , eax +??scr_right_ok: + test dl, 0001b + jz ??not_clip + mov eax, [(GraphicViewPort edi).GVPHeight] ; get width into register + mov [y1] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax, [y0] + mov edx, [(GraphicViewPort edi).GVPWidth] + add edx, [(GraphicViewPort edi).GVPXAdd] + add edx, [(GraphicViewPort edi).GVPPitch] + imul eax, edx + add eax, [x0] + mov edi, [(GraphicViewPort edi).GVPOffset] + add edi, eax + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx, [(MouseType esi).CursorWidth] ; turn this into an offset + mov esi, [buffy0] ; edx points to source + add esi, [buffx0] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax, [x1] + mov ecx, [y1] + sub eax, [x0] + jle ??out + sub ecx, [y0] + jle ??out + + sub edx, eax + sub ebx, eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah, al +??inner_loop: + mov ch, [esi] + inc esi + test ch, ch + jz ??inc_edi + mov [edi], ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi, ebx ; move past right clipped pixels + add edi, edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP Draw_Mouse +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL C ASM_Set_Mouse_Cursor:NEAR + PROC ASM_Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD ; pointer to mouse cursor struct + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[this] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[(MouseType esi).MaxWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[(MouseType esi).MaxHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[this] + mov edi,[(MouseType esi).MouseCursor] ; set edi to point to mouse buffer + mov esi,[ShapeBuffer] + jmp ??copy_type + +??done_copy: + mov esi,[this] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(MouseType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(MouseType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(MouseType esi).CursorHeight],ebx + mov ebx,[swidth] + mov [(MouseType esi).CursorWidth],ebx + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + push [cursor] + push [(MouseType esi).PrevCursor] + pop eax + pop [(MouseType esi).PrevCursor] + ret ; and return back to the world + + ENDP ASM_Set_Mouse_Cursor + +END diff --git a/WIN32LIB/SRCDEBUG/XORDELTA.ASM b/WIN32LIB/SRCDEBUG/XORDELTA.ASM new file mode 100644 index 0000000..1b9e4a9 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/XORDELTA.ASM @@ -0,0 +1,669 @@ +; +; 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 . +; + +; ************************************************************************** +; ** 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 + + + + +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 +; + +GLOBAL C Apply_XOR_Delta:NEAR +GLOBAL C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR + + + + CODESEG + + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Apply_XOR_Delta C near + 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 + + ENDP Apply_XOR_Delta + + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +;PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD flags, WORD descriptor) +PROC Apply_XOR_Delta_To_Page_Or_Viewport C near + 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,[width] ; 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 + + ENDP Apply_XOR_Delta_To_Page_Or_Viewport +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +PROC XOR_Delta_Buffer C near + ARG nextrow:DWORD + +??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 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_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 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_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] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + + ENDP XOR_Delta_Buffer + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Copy_Delta_Buffer C near + ARG nextrow:DWORD + +??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 + + 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 ; 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_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: + 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 ; 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_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] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + ENDP Copy_Delta_Buffer + +;---------------------------------------------------------------------------- + + 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); +;} +; + + diff --git a/WIN32LIB/SRCDEBUG/_DIPTABL.CPP b/WIN32LIB/SRCDEBUG/_DIPTABL.CPP new file mode 100644 index 0000000..885fb41 --- /dev/null +++ b/WIN32LIB/SRCDEBUG/_DIPTABL.CPP @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/WIN32LIB/TILE/ICONSET.CPP b/WIN32LIB/TILE/ICONSET.CPP new file mode 100644 index 0000000..67a1a91 --- /dev/null +++ b/WIN32LIB/TILE/ICONSET.CPP @@ -0,0 +1,342 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" + +#define _WIN32 +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include "tile.h" +#include + + + +extern int Misc; + +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != WW_ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = (short)(((short)sinf.Width)<<3); + idata->Height = (short)(((short)sinf.Height)<<3); + idata->Allocated = (short)allocated; + idata->Icons = (long)iconsetptr + sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = sizeof(IControl_Type) + size + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if (buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = (void*)idata->Map; + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Icons)); + return(NULL); +} + +void * Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Map)); + return(NULL); +} diff --git a/WIN32LIB/TILE/MAKEFILE b/WIN32LIB/TILE/MAKEFILE new file mode 100644 index 0000000..65cc501 --- /dev/null +++ b/WIN32LIB/TILE/MAKEFILE @@ -0,0 +1,191 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = tile +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iconset.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/TILE/MAKEFILE.BOR b/WIN32LIB/TILE/MAKEFILE.BOR new file mode 100644 index 0000000..3800dbf --- /dev/null +++ b/WIN32LIB/TILE/MAKEFILE.BOR @@ -0,0 +1,169 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = tile +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + iconset.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + -+iconset.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/TILE/MAKEFILE.WAT b/WIN32LIB/TILE/MAKEFILE.WAT new file mode 100644 index 0000000..3f8e263 --- /dev/null +++ b/WIN32LIB/TILE/MAKEFILE.WAT @@ -0,0 +1,191 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = tile +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iconset.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/TILE/TILE.H b/WIN32LIB/TILE/TILE.H new file mode 100644 index 0000000..eca2d21 --- /dev/null +++ b/WIN32LIB/TILE/TILE.H @@ -0,0 +1,94 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); +#if (0) +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char *Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char *Map; // Icon map offset (if present). +} IControl_Type; +#endif //(0) + +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; + + +#endif //TILE_H + diff --git a/WIN32LIB/TIMER/MAKEFILE b/WIN32LIB/TIMER/MAKEFILE new file mode 100644 index 0000000..5c0cf1a --- /dev/null +++ b/WIN32LIB/TIMER/MAKEFILE @@ -0,0 +1,203 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = timer +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + timer.obj & + timerdwn.obj & + timerini.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +timereal.ibn: timereal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip timereal.exe + ebn timereal.exe + +timereal.obj: timereal.asm + tasm /zn /la /ml /m2 timereal.asm + +timera.obj: timereal.ibn timera.asm + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/TIMER/MAKEFILE.BOR b/WIN32LIB/TIMER/MAKEFILE.BOR new file mode 100644 index 0000000..beffaa3 --- /dev/null +++ b/WIN32LIB/TIMER/MAKEFILE.BOR @@ -0,0 +1,173 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = timer +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + timer.obj \ + timerdwn.obj \ + timerini.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-timer.obj & ++-timerdwn.obj & ++-timerini.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/TIMER/MAKEFILE.WAT b/WIN32LIB/TIMER/MAKEFILE.WAT new file mode 100644 index 0000000..8943404 --- /dev/null +++ b/WIN32LIB/TIMER/MAKEFILE.WAT @@ -0,0 +1,203 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = timer +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + timer.obj & + timerdwn.obj & + timerini.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +timereal.ibn: timereal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip timereal.exe + ebn timereal.exe + +timereal.obj: timereal.asm + tasm /zn /la /ml /m2 timereal.asm + +timera.obj: timereal.ibn timera.asm + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/TIMER/TIMER.CPP b/WIN32LIB/TIMER/TIMER.CPP new file mode 100644 index 0000000..a9aecc1 --- /dev/null +++ b/WIN32LIB/TIMER/TIMER.CPP @@ -0,0 +1,203 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + TickType=timer; + + if (on && TimerSystemOn) Start(); +} + + + + +/*********************************************************************************************** + * TC:Get_Ticks -- return the number of ticks on the system or user timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:17PM ST : Created * + *=============================================================================================*/ + +long TimerClass::Get_Ticks ( void ) + +{ + if ( WindowsTimer ){ + switch ( TickType ){ + + case BT_SYSTEM : + return ( WindowsTimer->Get_System_Tick_Count() ); + + case BT_USER : + return ( WindowsTimer->Get_User_Tick_Count() ); + + } + } + return 0; +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} \ No newline at end of file diff --git a/WIN32LIB/TIMER/TIMER.H b/WIN32LIB/TIMER/TIMER.H new file mode 100644 index 0000000..29c2090 --- /dev/null +++ b/WIN32LIB/TIMER/TIMER.H @@ -0,0 +1,199 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + + +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; +extern HANDLE TimerThreadHandle; //Handle of timer thread +extern int InTimerCallback; //true if we are currently in a callback + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: +// long (*Get_Ticks)(void); // System timer fetch. + BaseTimerEnum TickType; + long Get_Ticks (void); +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + + +class WinTimerClass { + + public: + WinTimerClass ( UINT freq=60 , BOOL partial=0 ); + ~WinTimerClass(); + + void Update_Tick_Count ( void ); + unsigned Get_System_Tick_Count ( void ); + unsigned Get_User_Tick_Count ( void ); + + private: + + unsigned TimerHandle; //Handle for windows timer event + unsigned Frequency; //Frequency of our windows timer in ticks per second + + unsigned TrueRate; //True rate of clock. (only use word) + unsigned SysTicks; //Tick count of timer. + unsigned UserTicks; //Tick count of timer. + unsigned UserRate; //Desired rate of timer. + + +}; + + +extern WinTimerClass *WindowsTimer; + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// +#ifndef FUNCTION_H +extern TimerClass TickCount; +#endif +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long __cdecl Get_System_Tick_Count(void); + long __cdecl Get_User_Tick_Count(void); + void far __cdecl Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void __cdecl Disable_Timer_Interrupt(void); + void __cdecl Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL __cdecl Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL __cdecl Remove_Timer_System(VOID); + + +#endif // TIMER_H + diff --git a/WIN32LIB/TIMER/TIMER.IDE b/WIN32LIB/TIMER/TIMER.IDE new file mode 100644 index 0000000..a78615c Binary files /dev/null and b/WIN32LIB/TIMER/TIMER.IDE differ diff --git a/WIN32LIB/TIMER/TIMERDWN.CPP b/WIN32LIB/TIMER/TIMERDWN.CPP new file mode 100644 index 0000000..993a8ef --- /dev/null +++ b/WIN32LIB/TIMER/TIMERDWN.CPP @@ -0,0 +1,126 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/WIN32LIB/TIMER/TIMERINI.CPP b/WIN32LIB/TIMER/TIMERINI.CPP new file mode 100644 index 0000000..83756a0 --- /dev/null +++ b/WIN32LIB/TIMER/TIMERINI.CPP @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" +#include + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + + +// Prototype for timer callback +void CALLBACK Timer_Callback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +HANDLE TimerThreadHandle = 0; //Handle of timer thread +int InTimerCallback = 0; //Flag to say if we are in a timer callback + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + + + + +/*************************************************************************** + * WinTimerClass::WinTimerClass -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::WinTimerClass (UINT freq, BOOL partial) +{ + BOOL success; + + // + // Inform windows that we want a higher than normal + // timer resolution + // +#ifdef __SW_EP + timeBeginPeriod(1000/PROFILE_RATE); + Frequency = PROFILE_RATE; +#else + timeBeginPeriod ( 1000/freq ); + Frequency = freq; +#endif + + + // + // Install the timer callback event handler + // + TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC); + TimerSystemOn = success = ( TimerHandle !=0 ); + + if (success) { + if (!partial) { + WindowsTimer=this; + TickCount.Start(); + } + }else{ + char error_str [128]; + sprintf (error_str, "Error - timer system failed to start. Error code %d\n", GetLastError()); + OutputDebugString(error_str); + } +} + + + +/*************************************************************************** + * WinTimerClass::~WinTimerClass -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::~WinTimerClass( void ) +{ + + if ( TimerHandle ){ + timeKillEvent ( TimerHandle ); + } + + TimerSystemOn = FALSE; + timeEndPeriod ( 1000/Frequency ); +} + + + + + +/*********************************************************************************************** + * Timer_Callback -- Main timer callback. Equivalent to a timer interrupt handler * + * * + * * + * * + * INPUT: uint timer ID * + * uint reserved * + * long 0 (application defined) * + * long reserved * + * long reserved * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:19PM ST : Created * + *=============================================================================================*/ + + +void CALLBACK Timer_Callback (UINT , UINT , DWORD , DWORD , DWORD) +{ + //CONTEXT context; + + InTimerCallback++; + if (!TimerThreadHandle){ + DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&TimerThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS); + } + + + if (WindowsTimer) { + WindowsTimer->Update_Tick_Count(); + } + InTimerCallback--; +} + + + + + + +/*********************************************************************************************** + * WinTimerClass::Update_Tick_Count -- update westwood timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:58PM ST : Created * + *=============================================================================================*/ + +void WinTimerClass::Update_Tick_Count ( void ) +{ +/* + * + * Increment westwood timers + * + */ + SysTicks++; + UserTicks++; + +} + + + + + + + + +/* +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + */ + + + + +/*********************************************************************************************** + * WinTimerClass::Get_System_Tick_Count -- returns the system tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_System_Tick_Count ( void ) +{ + return ( SysTicks ); +} + + + +/*********************************************************************************************** + * WinTimerClass::Get_User_Tick_Count -- returns the user tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_User_Tick_Count ( void ) +{ + return ( UserTicks ); +} diff --git a/WIN32LIB/WINCOMM/MAKEFILE b/WIN32LIB/WINCOMM/MAKEFILE new file mode 100644 index 0000000..71c4b9a --- /dev/null +++ b/WIN32LIB/WINCOMM/MAKEFILE @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = wincomm +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + wincomm.obj & + modemreg.obj & + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** diff --git a/WIN32LIB/WINCOMM/MODEMREG.CPP b/WIN32LIB/WINCOMM/MODEMREG.CPP new file mode 100644 index 0000000..5c80a3f --- /dev/null +++ b/WIN32LIB/WINCOMM/MODEMREG.CPP @@ -0,0 +1,401 @@ +/* +** 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 . +*/ + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : MODEMREG.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/18/96 * + * * + * Last Update : October 18th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for obtaining modem infommation from the Win95 registry * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * Search_Registry_Key -- Search a registry key and all its subkeys for a given value * + * MREC::ModemRegistryEntryClass -- Constructor for ModemRegistryEntryClass * + * MREC::~ModemRegistryEntryClass -- Destructor.Free all the memory we allocated for modem info* + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + + +#include "modemreg.h" +#include + + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + + +/*********************************************************************************************** + * Search_Registry_Key -- Search a registry key and all its subkeys for a given value * + * * + * * + * * + * INPUT: handle to key to search in * + * name of key to search for * + * value expected in key * + * * + * OUTPUT: Handle to key containing value. Null if not found. * + * * + * WARNINGS: This function reenters itself. * + * * + * HISTORY: * + * 10/18/96 4:01AM ST : Created * + *=============================================================================================*/ +HKEY Search_Registry_Key (HKEY key_in, char *value_name, char *search_string) +{ + + int top_key_index = 0; // Index of topmost key + int retval; // Result of registry api calls + HKEY next_key; // handle of next key examine + HKEY next_search; // handle of next key to search + + + char *subkey_name = new char [256]; // Area to contain result of key enumeration + unsigned long subkey_name_length = 256; // Length of enumeration result area + FILETIME filetime; // Time key was last touched. Not used. + unsigned long value_type; // Type of data that is contained in a key. + unsigned char *key_value = new unsigned char [256]; // Area to return key values into + unsigned long key_value_length = 256; // Length of key value area + + /* + ** Scan through and enumerate all subkeys of this key. Exit the loop when there are + ** no more sub keys to enumerate. + */ + do { + subkey_name_length = 256; // Has to be set each time through the loop + + /* + ** Get the next key + */ + retval = RegEnumKeyEx (key_in, top_key_index++, subkey_name, &subkey_name_length, NULL, NULL, NULL, &filetime); + + if ( retval == ERROR_SUCCESS ){ + + /* + ** Get a handle to this key so we can search it. + */ + next_key = Get_Registry_Sub_Key (key_in, subkey_name, FALSE); + + if (next_key){ + + key_value_length = 256; // Has to be set each time through the loop + + if ( RegQueryValueEx (next_key, value_name, NULL, &value_type, key_value, &key_value_length) == ERROR_SUCCESS){ + + /* + ** If this value is type string then do a compare with the value we are looking for + */ + if (value_type == REG_SZ && !strcmp ((char*)key_value, search_string)){ + /* + ** This is our man. Delete our workspace and return the key handle + */ + delete [] subkey_name; + delete [] key_value; + return (next_key); + } + } + + /* + ** We didnt find our search value so search this key for more sub keys by reentering + ** this function with the handle of the subkey. + */ + next_search = Search_Registry_Key (next_key, value_name, search_string); + RegCloseKey (next_key); + + /* + ** If the value was found in a subkey then just return with the key handle. + */ + if (next_search){ + delete [] subkey_name; + delete [] key_value; + return (next_search); + } + } + + } + } while (retval == ERROR_SUCCESS); + + /* + ** Clean up and exit. + */ + delete [] subkey_name; + delete [] key_value; + + return (0); +} + + + +/*********************************************************************************************** + * MREC::ModemRegistryEntryClass -- Constructor for ModemRegistryEntryClass * + * * + * This function does all the work in the class. All the registry searching is done here * + * * + * INPUT: Modem number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/18/96 4:12AM ST : Created * + *=============================================================================================*/ +ModemRegistryEntryClass::ModemRegistryEntryClass (int modem_number) +{ + HKEY key; + unsigned char return_buf[256]; + DWORD retbuf_size = sizeof(return_buf); + + int pnp = 0; //Not a plug n pray modem + + /* + ** Initialise all the info we expect from the registry to NULL. + ** Any entries we cant find will just stay NULL. + */ + ModemName = NULL; + ModemDeviceName = NULL; + ErrorCorrectionEnable = NULL; + ErrorCorrectionDisable = NULL; + CompressionEnable = NULL; + CompressionDisable = NULL; + HardwareFlowControl = NULL; + NoFlowControl = NULL; + + + /* + ** Modem info is stored under + ** HKEY_LOCAL_MACHINE / System / CurrentControlSet / Services / Class / Modem / nnnn + ** where nnnn is a four digit modem number. + */ + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "System", FALSE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "CurrentControlSet", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Services", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Class", TRUE); + if (!key) return; + + key = Get_Registry_Sub_Key (key, "Modem", TRUE); + if (!key) return; + + char which_modem[5]; + sprintf (which_modem, "%04d", modem_number); + + /* + ** Get a handle to the modem key if it exists. Then extract the info we need. + */ + key = Get_Registry_Sub_Key (key, which_modem, TRUE); + if (!key) return; + + + /* + ** Get the name of the modem. This is what will be displayed in the modem list presented + ** to the user. + */ + if (RegQueryValueEx(key, "Model", NULL, NULL, return_buf, &retbuf_size) != ERROR_SUCCESS){ + RegCloseKey (key); + return; + } + ModemName = new char [retbuf_size]; + memcpy (ModemName, return_buf, retbuf_size); + + /* + ** Find out what COM port the modem is attached to. If this info isnt here, then its a + ** Plug n Pray modem. Set the flag so we know to do the pnp search later. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "AttachedTo", NULL, NULL, return_buf, &retbuf_size) != ERROR_SUCCESS){ + /* + ** Must be a plug n pray modem. Set the flag. We will look for the port later. + */ + pnp = 1; + ModemDeviceName = new char [strlen (ModemName)+1]; + strcpy (ModemDeviceName, ModemName); + }else{ + ModemDeviceName = new char [retbuf_size]; + memcpy (ModemDeviceName, return_buf, retbuf_size); + } + + + /* + ** The list of modem 'AT' commands is stored in the 'Settings' key. + */ + key = Get_Registry_Sub_Key (key, "Settings", TRUE); + if (!key) return; + + + /* + ** Extract the control strings for error control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "ErrorControl_On", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + ErrorCorrectionEnable = new char [retbuf_size]; + memcpy (ErrorCorrectionEnable, return_buf, retbuf_size); + } + + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "ErrorControl_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + ErrorCorrectionDisable = new char [retbuf_size]; + memcpy (ErrorCorrectionDisable, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for data compression. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "Compression_On", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + CompressionEnable = new char [retbuf_size]; + memcpy (CompressionEnable, return_buf, retbuf_size); + } + + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "Compression_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + CompressionDisable = new char [retbuf_size]; + memcpy (CompressionDisable, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for hardware flow control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "FlowControl_Hard", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + HardwareFlowControl = new char [retbuf_size]; + memcpy (HardwareFlowControl, return_buf, retbuf_size); + } + + /* + ** Extract the control strings for no flow control. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(key, "FlowControl_Off", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + NoFlowControl = new char [retbuf_size]; + memcpy (NoFlowControl, return_buf, retbuf_size); + } + + + + RegCloseKey (key); + + + + /* + ** If this is a plug n pray modem then we need to search for the COM port it is + ** attached to. + */ + if (pnp){ + + /* + ** The driver name in the HKEY_LOCAL_MACHINE / Enum section will be Modem\nnnn where nnnn + ** is a four digit modem number. + */ + char search_string [256] = {"Modem\\"}; + strcat (search_string, which_modem); + + /* + ** Search through all the registry entries under HKEY_LOCAL_MACHINE / Enum + */ + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "Enum", FALSE); + if (!key) return; + + HKEY newkey = Search_Registry_Key ( key, "Driver", search_string ); + + if (newkey){ + retbuf_size = sizeof (return_buf); + + /* + ** Extract the PORTNAME value. This is the name of the port to use to communicate + ** with the modem. + */ + retbuf_size = sizeof (return_buf); + if (RegQueryValueEx(newkey, "PORTNAME", NULL, NULL, return_buf, &retbuf_size) == ERROR_SUCCESS){ + + if (ModemDeviceName) delete [] ModemDeviceName; + + ModemDeviceName = new char [retbuf_size]; + memcpy (ModemDeviceName, return_buf, retbuf_size); + } + } + RegCloseKey (key); + } + +} + + + + + + + + +/*********************************************************************************************** + * MREC::~ModemRegistryEntryClass -- Destructor.Free all the memory we allocated for modem info* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/18/96 11:39AM ST : Created * + *=============================================================================================*/ +ModemRegistryEntryClass::~ModemRegistryEntryClass (void) +{ + if (ModemName) delete [] ModemName; + if (ModemDeviceName) delete [] ModemDeviceName; + + if (ErrorCorrectionEnable) delete [] ErrorCorrectionEnable; + if (ErrorCorrectionDisable) delete [] ErrorCorrectionDisable; + + if (CompressionEnable) delete [] CompressionEnable; + if (CompressionDisable) delete [] CompressionDisable; + + if (HardwareFlowControl) delete [] HardwareFlowControl; + if (NoFlowControl) delete [] NoFlowControl; +} + + + + + diff --git a/WIN32LIB/WINCOMM/MODEMREG.H b/WIN32LIB/WINCOMM/MODEMREG.H new file mode 100644 index 0000000..c883378 --- /dev/null +++ b/WIN32LIB/WINCOMM/MODEMREG.H @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + + +#ifndef WIN32 +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif //WIN32 +#include + + + +class ModemRegistryEntryClass { + + public: + + ModemRegistryEntryClass (int modem_number); + ~ModemRegistryEntryClass (void); + + + char *Get_Modem_Name (void) { return (ModemName); } + + char *Get_Modem_Device_Name (void) { return (ModemDeviceName); } + + char *Get_Modem_Error_Correction_Enable (void) { return (ErrorCorrectionEnable); } + + char *Get_Modem_Error_Correction_Disable (void) { return (ErrorCorrectionDisable); } + + char *Get_Modem_Compression_Enable (void) { return (CompressionEnable); } + + char *Get_Modem_Compression_Disable (void) { return (CompressionDisable); } + + char *Get_Modem_Hardware_Flow_Control (void) { return (HardwareFlowControl); } + + char *Get_Modem_No_Flow_Control (void) { return (HardwareFlowControl); } + + private: + + char *ModemName; + char *ModemDeviceName; + char *ErrorCorrectionEnable; + char *ErrorCorrectionDisable; + char *CompressionEnable; + char *CompressionDisable; + char *HardwareFlowControl; + char *NoFlowControl; + +}; + + + + + + + diff --git a/WIN32LIB/WINCOMM/WINCOMM.CPP b/WIN32LIB/WINCOMM/WINCOMM.CPP new file mode 100644 index 0000000..ed9ccc4 --- /dev/null +++ b/WIN32LIB/WINCOMM/WINCOMM.CPP @@ -0,0 +1,1487 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for WinModemClass & WinNullModemClass * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +//#include "function.h" +#include "wincomm.h" +#include "timer.h" +#include "keyboard.h" +#include "misc.h" +#include +#include +#include +#include + + +/* +** Define this to log modem activity to disk. +*/ +//#define LOG_MODEM + +/* +** Object represents a serial port +*/ +WinModemClass *SerialPort = NULL; + + + +/*********************************************************************************************** + * WMC::WinModemClass -- WinModemClass constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:14PM ST : Created * + *=============================================================================================*/ + +WinModemClass::WinModemClass(void) +{ + /* + ** Allocate memory for our internal circular serial input buffer + */ + SerialBuffer = new unsigned char [SIZE_OF_WINDOWS_SERIAL_BUFFER]; + + /* + ** Initialise the serial buffer pointers + */ + SerialBufferReadPtr = 0; + SerialBufferWritePtr = 0; + + /* + ** Clear the waiting flags + */ + WaitingForSerialCharRead = FALSE; + WaitingForSerialCharWrite = FALSE; + + /* + ** No default abort or echo function + */ + AbortFunction = NULL; + EchoFunction = NULL; + + + /* + ** Clear the running error count + */ + FramingErrors=0; + IOErrors=0; + BufferOverruns=0; + InBufferOverflows=0; + ParityErrors=0; + OutBufferOverflows=0; + + /* + ** We havnt opened a port yet so... + */ + PortHandle = 0; +} + + + +/*********************************************************************************************** + * WMC::~WinModemClass -- destructor for WinModemClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:15PM ST : Created * + *=============================================================================================*/ + +WinModemClass::~WinModemClass(void) +{ + /* + ** Close the port + */ + if (PortHandle){ + Serial_Port_Close(); + } + + /* + ** Free the serial buffer + */ + if (SerialBuffer){ + delete [] SerialBuffer; + } +} + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close) +{ + + char class_string[1024]; + DWORD string_size = 1024; + DWORD num_sub_keys; + DWORD longest_sub_key_name; + DWORD longest_class_string; + DWORD num_value_entries; + DWORD longest_value_name_length; + DWORD longest_value_data_length; + DWORD security_descriptor_length; + FILETIME last_write_time; + HKEY result_key; + DWORD sub_key_buffer_size; + + char *sub_key_buffer; + char *sub_key_class; + + + if (RegQueryInfoKey (base_key, + &class_string[0], + &string_size, + NULL, + &num_sub_keys, + &longest_sub_key_name, + &longest_class_string, + &num_value_entries, + &longest_value_name_length, + &longest_value_data_length, + &security_descriptor_length, + &last_write_time) != ERROR_SUCCESS) return (0); + + sub_key_buffer_size = longest_sub_key_name+16; + sub_key_buffer = new char [sub_key_buffer_size]; + sub_key_class = new char [longest_class_string+1]; + + for (int key_num=0 ; key_numdwProviderSubType == PST_MODEM){ + temp = modem_config->dwProviderOffset; + temp += (int)modem_config; + modem_caps = (MODEMDEVCAPS*)temp; + modem_caps->dwModemOptions &= ~( MDM_COMPRESSION | + MDM_ERROR_CONTROL | + MDM_FLOWCONTROL_HARD); + SetCommConfig(PortHandle, modem_config ,(unsigned)config_size); + } + } + + + /* + ** Set the device timeouts + */ + timeouts.ReadIntervalTimeout = 10000; //10 seconds between incoming packets + timeouts.ReadTotalTimeoutMultiplier = 0; //disable total timeouts + timeouts.ReadTotalTimeoutConstant = 0; //disable total timeouts + timeouts.WriteTotalTimeoutMultiplier= 0; //disable total timeouts + timeouts.WriteTotalTimeoutConstant = 0; //disable total timeouts + + if (SetCommTimeouts(com_handle, &timeouts) !=TRUE){ + Serial_Port_Close(); + return (INVALID_HANDLE_VALUE); + } + + return (com_handle); +} +#endif + + + + + +/*********************************************************************************************** + * WMC::Serial_Port_Open -- opens a com port for asyncronous read/write and gets a handle to it* + * * + * * + * * + * INPUT: Com port - 0=com1, 1=com2 etc. * + * baud rate - bits per second * + * parity - true or false * + * word length - 5 to 8 bits * + * stop bits - 0=1 stop bit, 1=1.5 & 2=2 * + * flow control- 0 = none, 1 = hardware * + * * + * OUTPUT: Handle to port * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:17PM ST : Created * + *=============================================================================================*/ +HANDLE WinModemClass::Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol) +{ + HANDLE com_handle; //temporary storage for the port handle + DCB device_control; //device control block + COMMTIMEOUTS timeouts; //timeout values +#if (0) + /* + ** Map for com port values to device names + */ + static char com_ids[8][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8" + }; +#endif //(0) + + int errorval; + char errortxt[128]; + + char devname[266]={"\\\\.\\"}; //device name to open + strcat (devname, device_name); + + PortHandle = 0; + + /* + ** Open the com port for asyncronous reads/writes + */ + //com_handle = CreateFile (&com_ids[com][0], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + com_handle = CreateFile (devname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (com_handle == INVALID_HANDLE_VALUE) return (com_handle); + + PortHandle = com_handle; + + /* + ** Set the size of the windows communication buffers + */ + SetupComm(com_handle, SIZE_OF_WINDOWS_SERIAL_BUFFER, SIZE_OF_WINDOWS_SERIAL_BUFFER); + + /* + ** Reset any read or write operation and purge the buffers + */ + PurgeComm (PortHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); + + /* + ** Create an event object for asyncronous reads + */ + ReadOverlap.Internal = 0; + ReadOverlap.InternalHigh = 0; + ReadOverlap.Offset = 0; + ReadOverlap.OffsetHigh = 0; + ReadOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); + + /* + ** Create an event object for asyncronous writes + */ + WriteOverlap.Internal = 0; + WriteOverlap.InternalHigh = 0; + WriteOverlap.Offset = 0; + WriteOverlap.OffsetHigh = 0; + WriteOverlap.hEvent = CreateEvent (NULL, TRUE, TRUE, NULL); + + + /* + ** Get the current state of the com port as a basis for our device control block + */ + if (GetCommState (com_handle , &device_control)){ + + /* + ** Communications settings + */ + device_control.BaudRate = baud; + device_control.fParity = parity; + device_control.ByteSize = (char)wordlen; + device_control.StopBits = (char)(stopbits-1); + + /* + ** Misc settings for flow control etc. + */ + device_control.fBinary = TRUE; // Binary mode data transfer + device_control.fOutxCtsFlow = TRUE; // CTS flow control + device_control.fRtsControl = RTS_CONTROL_HANDSHAKE; // RTS flow control + device_control.fErrorChar = FALSE; // Dont put an error char into our input stream + device_control.fOutxDsrFlow = FALSE; // No DSR flow control + device_control.fDtrControl = DTR_CONTROL_ENABLE; // Enable control of DTR line + device_control.fOutX = FALSE; // No XON/XOF flow control + device_control.fInX = FALSE; // No XON/XOF flow control + device_control.fAbortOnError = FALSE; // Device continues to send after an error + + /* + ** Disable hardware flow control if required + */ + if (!flowcontrol){ + device_control.fOutxCtsFlow = FALSE; // CTS flow control + device_control.fRtsControl = RTS_CONTROL_DISABLE; // RTS flow control + } + + /* + ** Pass the device settings to windows + */ + if ( !SetCommState (com_handle , &device_control)){ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- SetCommState returned error code %d.\n", errorval); + OutputDebugString (errortxt); + //Serial_Port_Close(); + //return (INVALID_HANDLE_VALUE); + } + }else{ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- GetCommState returned error code %d.\n", errorval); + OutputDebugString (errortxt); + } + + + + /* + ** Set the device timeouts + */ + timeouts.ReadIntervalTimeout = 1000; //1 second between incoming bytes will time-out the read + timeouts.ReadTotalTimeoutMultiplier = 0; //disable per byte timeouts + timeouts.ReadTotalTimeoutConstant = 3000; //Read operations time out after 3 secs if no data received + timeouts.WriteTotalTimeoutMultiplier= 500; //Allow 1/2 ms between each char write + timeouts.WriteTotalTimeoutConstant = 1000; //Write operations time out after 1 sec + 1/2 sec per char if data wasnt sent + + if ( !SetCommTimeouts(com_handle, &timeouts) ){ + errorval = GetLastError(); + sprintf (errortxt, "RA95 -- SetCommTimeouts returned error code %d.\n", errorval); + OutputDebugString (errortxt); + //Serial_Port_Close(); + //return (INVALID_HANDLE_VALUE); + } + + return (com_handle); +} + + + + + +/*********************************************************************************************** + * WMC::Set_Modem_Dial_Type -- sets dial type to WC_TOUCH_TONE or WC_PULSE * + * * + * * + * * + * INPUT: WC_TOUCH_TONE or WC_PULSE * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:22PM ST : Created * + *=============================================================================================*/ + +void WinModemClass::Set_Modem_Dial_Type(WinCommDialMethodType method) +{ + DialingMethod = method; +} + + + + +/*********************************************************************************************** + * WMC::Get_Modem_Status -- gets the status of the modem control lines * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status. Any of the following bits CTS_SET, DSR_SET, RI_SET or CD_SET * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:24PM ST : Created * + *=============================================================================================*/ + +unsigned WinModemClass::Get_Modem_Status(void) +{ + DWORD modem_stat = 0; + unsigned long return_stat = 0; + + /* + ** Get the modem status + */ + GetCommModemStatus(PortHandle, &modem_stat); + + /* + ** Translate the windows status flags to greenleaf flags + */ + if (MS_CTS_ON & modem_stat) return_stat |= CTS_SET; + if (MS_DSR_ON & modem_stat) return_stat |= DSR_SET; + if (MS_RING_ON & modem_stat) return_stat |= RI_SET; + if (MS_RLSD_ON & modem_stat) return_stat |= CD_SET; + + return (return_stat); + +} + + + +/*********************************************************************************************** + * WMC::Set_Serial_DTR -- set the state of the modems DTR control line * + * * + * * + * * + * INPUT: state - true or false * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:25PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Serial_DTR(BOOL state) +{ + if (state){ + EscapeCommFunction(PortHandle, SETDTR); + }else{ + EscapeCommFunction(PortHandle, CLRDTR); + } +} + + + + + +/*********************************************************************************************** + * WMC::Serial_Port_Close -- close the port and free the port handle * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:26PM ST : Created * + *=============================================================================================*/ + +void WinModemClass::Serial_Port_Close (void) +{ + if (PortHandle){ + CloseHandle(PortHandle); + PortHandle = 0; + } +} + + + +/*********************************************************************************************** + * WMC::Read_Serial_Chars -- copys chars from the windows serial buffer to the class buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if any chars read * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:26PM ST : Created * + *=============================================================================================*/ + +void Smart_Printf( char *format, ... ); +BOOL WinModemClass::Read_Serial_Chars (void) +{ + DWORD bytes_read; //amount of data read this time + BOOL read_result; //result of ReadFile + BOOL overlap_result; //result of GetOverlappedResult + DWORD total_bytes_read=0; //total amount of data read + int bytes_to_read; + int i; + + /* + ** Are we were still waiting for the last read operation to finish? + */ + if (WaitingForSerialCharRead){ + + /* + ** Check the result of the last read operation + */ + bytes_read = 0; + overlap_result = GetOverlappedResult(PortHandle, &ReadOverlap, &bytes_read, FALSE); + + /* + ** If we got a good result from GetOverlappedResult and data was read then move it + ** to our circular buffer + */ + if (overlap_result){ + WaitingForSerialCharRead = FALSE; //Flag that we are no longer waiting for a read + + if (bytes_read){ + for (i=0 ; ibuffer_len) bytes_to_copy = buffer_len; + + /* + ** Loop to copy the data from the internal class buffer to the users buffer + */ + for (int i=0 ; iCheck()) { + + //OutputDebugString ("Wincomm - About to call abort function.\n"); + int abort = AbortFunction(); + sprintf (abuffer ,"Wincomm - About function returned %d.\n", abort); + //OutputDebugString (abuffer); + if (abort != COMMSUCCESS) return (abort); + } + + /* + ** If we had lost focus then abort + */ + if (AllSurfaces.SurfacesRestored){ + //OutputDebugString ("Wincomm - Aborting due to loss of focus.\n"); + return (0); + } + + //OutputDebugString ("Wincomm - About to call Read_From_Serial_Port.\n"); + dest_ptr += Read_From_Serial_Port((unsigned char*)(buffer + dest_ptr), (int)buffer_len-dest_ptr); + sprintf (abuffer, "Wincomm - End of inner do loop. Time is %d.\n", timer.Time()); + //OutputDebugString (abuffer); + } while (timer.Time() && + dest_ptr < buffer_len && + !strchr (buffer, 13) ); + + //OutputDebugString ("Wincomm - Exited inner do loop.\n"); + + + /* + ** We need to discard this result if it is just an echo of the 'AT' command we sent + */ + cr_ptr = strstr(buffer,"AT"); + if (cr_ptr){ + if (*buffer == 'A' && *(buffer+1) == 'T' && strchr(buffer,13)){ + //OutputDebugString ("Wincomm - Discarding command echo.\n"); + cr_ptr = strchr(buffer,13); + } + } + + /* + ** If it wasnt an AT echo then strip off any leading CR/LF characters + */ + if (!cr_ptr && (*buffer==13 || *buffer==10)){ + cr_ptr = strchr(buffer,13); + lf_ptr = strchr(buffer,10); + if (!cr_ptr || (lf_ptr && lf_ptr < cr_ptr)){ + //OutputDebugString ("Wincomm - Stripping CR/LF.\n"); + cr_ptr = strchr(buffer,10); + } + } + + /* + ** Copy the good stuff at the end of the buffer over the 'AT' or CR/LF chars + */ + if (cr_ptr){ + //OutputDebugString ("Wincomm - Copying over start of buffer.\n"); + while(*cr_ptr == 13 || *cr_ptr == 10){ + cr_ptr++; + } + + if (cr_ptr != buffer){ + copy_bytes = (int)cr_ptr - (int)buffer; + memcpy(buffer, cr_ptr, buffer_len - copy_bytes); + dest_ptr -= copy_bytes; + } + } + + //OutputDebugString ("Wincomm - End of outer do loop.\n"); + }while(cr_ptr); + + + /* + ** Terminate the string at the first CR character as this is what Greenleaf does + */ + if (strchr(buffer, 13)){ + //OutputDebugString ("Truncating result string.\n"); + *(strchr(buffer, 13)) = 0; + } + + //sprintf (abuffer, "Wincomm - returning remaining delay of %d.\n", timer.Time()); + //OutputDebugString (abuffer); + return (timer.Time()); +} + + + + +/*********************************************************************************************** + * WMC::Dial_Modem -- issue an 'ATD' command to the modem * + * * + * * + * * + * INPUT: string - number to dial * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Use Set_Modem_Dial_Type to choose pulse or tone dialling * + * * + * HISTORY: * + * 1/10/96 2:32PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Dial_Modem(char *dial_number) +{ + char dial_string[80]; + + /* + ** Create the dial command to send to the modem + */ + strcpy (dial_string, "ATD"); + if (DialingMethod == WC_TOUCH_TONE){ + strcat(dial_string, "T"); + }else{ + strcat(dial_string, "P"); + } + + /* + ** Stick a carriage return on the end + */ + strcat (dial_string, dial_number); + strcat (dial_string, "\r"); + + /* + ** Write the dial command to the serial port and wait for the write to complete + */ + Write_To_Serial_Port ((unsigned char*)dial_string, strlen(dial_string)); + Wait_For_Serial_Write(); +} + + + + + +/*********************************************************************************************** + * WMC::Send_Command_To_Modem -- send an 'AT' command to the modem and await a response * + * * + * * + * * + * INPUT: command string * + * terminator byte (usually "\r") * + * ptr to buffer to receive modem result code * + * length of buffer * + * timeout delay for waiting for result * + * number of times to retry the command * + * * + * OUTPUT: result code * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:33PM ST : Created * + *=============================================================================================*/ + +int WinModemClass::Send_Command_To_Modem(char *command, char terminator, char *buffer, int buflen, int delay, int retries) +{ + + int times; + unsigned char tmp_string[80]; + char tmp_buff[80]; + char term_string[2]; + int time; + + /* + ** Build the terminator string + */ + term_string[0] = terminator; + term_string[1] = 0; + + /* + ** Create the command from the supplied command and terminator + */ + strcpy((char*)tmp_string, command); + strcat((char*)tmp_string, term_string); + + + /* + ** Flush out any pending characters from the port + */ + unsigned char nothing_buff[80]; + Read_From_Serial_Port(nothing_buff,80); + Sleep (100); + Read_From_Serial_Port(nothing_buff,80); + + + for (times = 0 ; times= '0' && tmp_buff[0] <='9') && + (tmp_buff[1] >= '0' && tmp_buff[1] <='9') && + (tmp_buff[2] >= '0' && tmp_buff[2] <='9')) { + strncpy(buffer, tmp_buff, MIN(buflen, 80)); + return (MODEM_CMD_OK); + } + } + + /* + ** It was a non-standard(ish) result so copy it to the users buffer + */ + strncpy(buffer, tmp_buff, MIN(buflen, 80)); + + /* + ** Spurious write for no apparent reason. Well it was there in the DOS version so... + */ + Sleep (100); + Write_To_Serial_Port((unsigned char*)"\r",1); + Wait_For_Serial_Write(); + Sleep (100); + } + + return (ASTIMEOUT); +} + + + +/*********************************************************************************************** + * WMC::Set_Echo_Function -- set up the echo function pointer * + * * + * * + * * + * INPUT: ptr to echo function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:35PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Echo_Function( void ( *func )( char c) ) +{ + EchoFunction = func; +} + + + +/*********************************************************************************************** + * WMC::Set_Abort_Function -- set up the abort function pointer * + * * + * * + * * + * INPUT: ptr to abort function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/10/96 2:35PM ST : Created * + *=============================================================================================*/ +void WinModemClass::Set_Abort_Function(int (*func)(void)) +{ + AbortFunction = func; +} + + +/*********************************************************************************************** + * WMC::Get_Port_Handle -- returns a handle to the communications port * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Com port handle * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/23/96 1:25PM ST : Created * + *=============================================================================================*/ +HANDLE WinModemClass::Get_Port_Handle(void) +{ + return (PortHandle); +} + diff --git a/WIN32LIB/WINCOMM/WINCOMM.H b/WIN32LIB/WINCOMM/WINCOMM.H new file mode 100644 index 0000000..2ceae18 --- /dev/null +++ b/WIN32LIB/WINCOMM/WINCOMM.H @@ -0,0 +1,457 @@ +/* +** 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 . +*/ + + +/*********************************************************************************************** + *** 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/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WIN32 +#define WIN32 +#define _WIN32 +#endif //WIN32 +#include + +typedef enum WinCommDialMethodType { + WC_TOUCH_TONE = 0, + WC_PULSE +} WinCommDialMethodType; + + + +#define COMMSUCCESS 0 +#define ASTIMEOUT -10 +#define COMMUSERABORT -16 + + +/* +** The size of our serial buffer within the class. +** +** !!!!!! THIS MUST BE A POWER OF 2 !!!!!! +** +*/ +#define SIZE_OF_WINDOWS_SERIAL_BUFFER 2048 + + + +/* +** WinModemClass. +** +** This class provides access to modems under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +*/ + +class WinModemClass +{ + + public: + + WinModemClass (void); //WinModemClass Contructor + virtual ~WinModemClass (void); //WinModemClass Destructor + + + /* + ** Serial port open should be called to get a handle to the COM port + ** This needs to be called first as other class members rely on the handle + ** + ** Replacement for Greenleaf function: PortOpenGreenleafFast + */ + //virtual HANDLE Serial_Port_Open (int port, int baud, int parity, int wordlen, int stopbits); + virtual HANDLE Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol); + + /* + ** This function releases the COM port handle and should be called after + ** communications have finished + ** + ** Replacement for Greenleaf function: PortClose + */ + void Serial_Port_Close (void); + + /* + ** This member copies any bytes from the internal class serial buffer + ** into your user buffer. + ** + ** Replacement for Greenleaf function: ReadBuffer + */ + int Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len); + + /* + ** Write chars to the serial port + ** + ** Replacement for Greenleaf function: WriteBuffer + */ + void Write_To_Serial_Port (unsigned char *buffer, int length); + + /* + ** Wait for the outgoing buffer to empty + */ + void Wait_For_Serial_Write (void); + + /* + ** Set the dial type to DIAL_TOUCH_TONE or DIAL_PULSE + ** + ** Replacement for Greenleaf function: HMSetDiallingMethod + */ + virtual void Set_Modem_Dial_Type (WinCommDialMethodType method); + + /* + ** Get the status of the modem control lines + ** Possible flags are: CTS_SET DSR_SET RI_SET & CD_SET + ** + ** Replacement for Greenleaf function: GetModemStatus + */ + virtual unsigned Get_Modem_Status (void); + + /* + ** Set the DTR line to the given state + ** + ** Replacement for Greenleaf function: SetDtr + */ + virtual void Set_Serial_DTR (BOOL state); + + /* + ** Get the result code from the modem after issuing an 'AT' command + ** + ** Replacement for Greenleaf function: HMInputLine + */ + virtual int Get_Modem_Result (int delay, char *buffer, int buffer_len); + + /* + ** Issue a dial command to the modem. + ** Use Set_Modem_Dial_Type to select pulse or tone dial + ** + ** Replacement for Greenleaf function: HMDial + */ + virtual void Dial_Modem (char *dial_number); + + /* + ** Send a command to the modem. This is usually an 'AT' command. + ** Function will optionally retry until 'OK' is received. + */ + virtual int Send_Command_To_Modem (char *command, char terminator, char *buffer, int buflen, int delay, int retries); + + /* + ** Sets a pointer to a function that will be called for each incoming serial char + ** + ** Replacement for Greenleaf function: HMSetUpEchoRoutine + */ + virtual void Set_Echo_Function (void(*func)(char c)); + + /* + ** Sets a pointer to a function that will be called if ESC is pressed during a dial + ** + ** Replacement for Greenleaf function: HMSetUpAbortKey + */ + virtual void Set_Abort_Function (int (*func)(void)); + + /* + ** Member to allow access to the serial port handle + */ + HANDLE Get_Port_Handle(void); + + /* + ** Status vars for debugging purposes + */ + int FramingErrors; + int IOErrors; + int BufferOverruns; + int InBufferOverflows; + int ParityErrors; + int OutBufferOverflows; + int InQueue; + int OutQueue; + + /* + ** Modem send result codes + */ + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + + /* + ** Enums for modem status flags + */ + enum { + CTS_SET = 0x10, + DSR_SET = 0x20, + RI_SET = 0x40, + CD_SET = 0x80 + }; + + + protected: + + + /* + ** Copy incoming data from the windows file buffer into the internal class buffer + */ + BOOL Read_Serial_Chars(void); + + /* + ** Pointer to the internal class circular buffer for incoming data + */ + unsigned char *SerialBuffer; + + /* + ** Overlap object for asyncronous reads from the serial port + */ + OVERLAPPED ReadOverlap; + + /* + ** Overlap object for asyncronous writes to the serial port + */ + OVERLAPPED WriteOverlap; + + /* + ** Flag that there is no outstanding incoming data in the windows buffer + */ + BOOL WaitingForSerialCharRead; + + /* + ** Flag that we are waiting for the last write to port operation to complete + */ + BOOL WaitingForSerialCharWrite; + + /* + ** Head and Tail pointers for our internal serial buffer + */ + int SerialBufferReadPtr; + int SerialBufferWritePtr; + + /* + ** Windows handle to the COM port device + */ + HANDLE PortHandle; + + /* + ** Dialing method - DIAL_TOUCH_TONE or DIAL_PULSE + */ + WinCommDialMethodType DialingMethod; + + /* + ** Pointer to function for echoing incoming data - can be NULL + */ + void (*EchoFunction)(char c); + + /* + ** Pointer to function for aborting when ESC pressed - can be NULL + */ + int (*AbortFunction)(void); + + /* + ** Serial buffer for asyncronous reads + */ + char TempSerialBuffer[SIZE_OF_WINDOWS_SERIAL_BUFFER]; +}; + + + + + + + + + + +/* +** WinNullModemClass. +** +** This class provides access to serial ports under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +** +** This class just overloads the WinModemClass members that arent required for direct serial communications +** via a 'null modem' cable. +*/ +class WinNullModemClass : public WinModemClass +{ + + public: + + virtual inline void Set_Modem_Dial_Type (int){}; + virtual inline unsigned Get_Modem_Status (void){return (0);}; + virtual inline void Set_Serial_DTR (BOOL){}; + virtual inline int Get_Modem_Result (int, char*, int){return(0);}; + virtual inline void Dial_Modem (char*){}; + virtual inline int Send_Command_To_Modem (char*, char, char*, int, int, int){return (0);}; + virtual inline void Set_Echo_Function (void(*)(char)){}; + virtual inline void Set_Abort_Function (int(*)(void)){}; + +}; + + +extern WinModemClass *SerialPort; + + + + + + + + + +// +// +// This bit swiped from the SDK because its not in the Watcom headers yet +// +// + +/************************************************************************ +* * +* mcx.h -- This module defines the 32-Bit Windows MCX APIs * +* * +* Copyright (c) 1990-1995, Microsoft Corp. All rights reserved. * +* * +************************************************************************/ + +#ifndef _MCX_H_ +#define _MCX_H_ + +typedef struct _MODEMDEVCAPS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // product and version identification + DWORD dwModemProviderVersion; + DWORD dwModemManufacturerOffset; + DWORD dwModemManufacturerSize; + DWORD dwModemModelOffset; + DWORD dwModemModelSize; + DWORD dwModemVersionOffset; + DWORD dwModemVersionSize; + + // local option capabilities + DWORD dwDialOptions; // bitmap of supported values + DWORD dwCallSetupFailTimer; // maximum in seconds + DWORD dwInactivityTimeout; // maximum in seconds + DWORD dwSpeakerVolume; // bitmap of supported values + DWORD dwSpeakerMode; // bitmap of supported values + DWORD dwModemOptions; // bitmap of supported values + DWORD dwMaxDTERate; // maximum value in bit/s + DWORD dwMaxDCERate; // maximum value in bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS; + +typedef struct _MODEMSETTINGS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // static local options (read/write) + DWORD dwCallSetupFailTimer; // seconds + DWORD dwInactivityTimeout; // seconds + DWORD dwSpeakerVolume; // level + DWORD dwSpeakerMode; // mode + DWORD dwPreferredModemOptions; // bitmap + + // negotiated options (read only) for current or last call + DWORD dwNegotiatedModemOptions; // bitmap + DWORD dwNegotiatedDCERate; // bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMSETTINGS, *PMODEMSETTINGS, *LPMODEMSETTINGS; + +// Dial Options +#define DIALOPTION_BILLING 0x00000040 // Supports wait for bong "$" +#define DIALOPTION_QUIET 0x00000080 // Supports wait for quiet "@" +#define DIALOPTION_DIALTONE 0x00000100 // Supports wait for dial tone "W" + +// SpeakerVolume for MODEMDEVCAPS +#define MDMVOLFLAG_LOW 0x00000001 +#define MDMVOLFLAG_MEDIUM 0x00000002 +#define MDMVOLFLAG_HIGH 0x00000004 + +// SpeakerVolume for MODEMSETTINGS +#define MDMVOL_LOW 0x00000000 +#define MDMVOL_MEDIUM 0x00000001 +#define MDMVOL_HIGH 0x00000002 + +// SpeakerMode for MODEMDEVCAPS +#define MDMSPKRFLAG_OFF 0x00000001 +#define MDMSPKRFLAG_DIAL 0x00000002 +#define MDMSPKRFLAG_ON 0x00000004 +#define MDMSPKRFLAG_CALLSETUP 0x00000008 + +// SpeakerMode for MODEMSETTINGS +#define MDMSPKR_OFF 0x00000000 +#define MDMSPKR_DIAL 0x00000001 +#define MDMSPKR_ON 0x00000002 +#define MDMSPKR_CALLSETUP 0x00000003 + +// Modem Options +#define MDM_COMPRESSION 0x00000001 +#define MDM_ERROR_CONTROL 0x00000002 +#define MDM_FORCED_EC 0x00000004 +#define MDM_CELLULAR 0x00000008 +#define MDM_FLOWCONTROL_HARD 0x00000010 +#define MDM_FLOWCONTROL_SOFT 0x00000020 +#define MDM_CCITT_OVERRIDE 0x00000040 +#define MDM_SPEED_ADJUST 0x00000080 +#define MDM_TONE_DIAL 0x00000100 +#define MDM_BLIND_DIAL 0x00000200 +#define MDM_V23_OVERRIDE 0x00000400 + +#endif /* _MCX_H_ */ + + + + + + + + + + + + + + + + diff --git a/WIN32LIB/WSA/MAKEFILE b/WIN32LIB/WSA/MAKEFILE new file mode 100644 index 0000000..0fd7433 --- /dev/null +++ b/WIN32LIB/WSA/MAKEFILE @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = wsa +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + wsa.obj & + xordelta.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB32)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/WSA/MAKEFILE.BOR b/WIN32LIB/WSA/MAKEFILE.BOR new file mode 100644 index 0000000..ff28454 --- /dev/null +++ b/WIN32LIB/WSA/MAKEFILE.BOR @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = wsa +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + wsa.obj \ + xordelta.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-wsa.obj & ++-xordelta.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/WSA/MAKEFILE.WAT b/WIN32LIB/WSA/MAKEFILE.WAT new file mode 100644 index 0000000..112015b --- /dev/null +++ b/WIN32LIB/WSA/MAKEFILE.WAT @@ -0,0 +1,180 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = wsa +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + wsa.obj & + xordelta.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB32)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WIN32LIB/WSA/WSA.CPP b/WIN32LIB/WSA/WSA.CPP new file mode 100644 index 0000000..dc23416 --- /dev/null +++ b/WIN32LIB/WSA/WSA.CPP @@ -0,0 +1,1156 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./wsa.c 1.16 1994/05/20 15:35:27 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Animation System * + * * + * File Name : WSA.C * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 20, 1991 * + * * + *-------------------------------------------------------------------------* + * There are many different ways that the user can use the WSA library * + * module. The options are as follows : * + * * + * System Allocation vs User Buffer - The user may request that the * + * system allocate the needed buffer from the heap or the user may * + * pass his own buffer in for the animator to use. * + * * + * Resident vs File based - If there is enough RAM, the user may put the * + * entire animation into RAM for fastest animations. If there is * + * not enouph RAM, the system will automatically make it so each * + * frame will be read off disk when needed. * + * * + * Direct to Page vs Use of a user buffer -- Noramally Direct to page * + * is the best method both in speed and in RAM need to hold anim. * + * One may want to use the write to user buffer method if they * + * are using the animation in a non sequencial order. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Animate_Frame -- Displays a frame of a given animation * + * Get_Animation_Frame_Count -- Return Number of frames in an animation. * + * Get_Animation_X -- Gets the x from an animation * + * Get_Animation_Y -- Gets the y from an animation * + * Get_Animation_Width -- Gets the width from an animation * + * Get_Animation_Height -- The height of the animation we are processing * + * Apply_Delta -- Copies frame into delta buffer, then applies to target * + * Close_Animation -- Close the animation, freeing the space if necessary* + * Get_File_Frame_Offset -- Get offset of a delta frame from animate file* + * Get_Resident_Frame_Offset -- Gets frame offset of animate file in RAM * + * Open_Animation -- Opens an animation file and reads into buffer * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "wsa.h" +#include +#include +#include +#include +#include +#include + +// +// WSA animation header allocation type. +// If we need more then 8 flags for the flags variable, we can combine +// USER_ALLOCATED with SYS_ALLOCATED and combine FILE with RESIDENT. +// +#define WSA_USER_ALLOCATED 0x01 +#define WSA_SYS_ALLOCATED 0x02 +#define WSA_FILE 0x04 +#define WSA_RESIDENT 0x08 +#define WSA_TARGET_IN_BUFFER 0x10 +#define WSA_LINEAR_ONLY 0x20 +#define WSA_FRAME_0_ON_PAGE 0x40 +#define WSA_AMIGA_ANIMATION 0x80 +#define WSA_PALETTE_PRESENT 0x100 +#define WSA_FRAME_0_IS_DELTA 0x200 + +// 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 lp_asm.asm. +#define DO_XOR 0x0 +#define DO_COPY 0x01 +#define TO_VIEWPORT 0x0 +#define TO_PAGE 0x02 + + + + +typedef struct { + unsigned short current_frame; + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + char *delta_buffer; + char *file_buffer; + char file_name[ 13 ]; + short flags; + // New fields that animate does not know about below this point. SEE EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT + short file_handle; + unsigned long anim_mem_size; +} SysAnimHeaderType; + +// NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE +// UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING +// IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. +#define EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT (sizeof(short) + sizeof(unsigned long)) + + +// +// Header structure for the file. +// NOTE: The 'total_frames' field is used to differentiate between Amiga and IBM +// animations. Amiga animations have the HIGH bit set. +// +typedef struct { + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + short flags; + unsigned long frame0_offset; + unsigned long frame0_end; + /* unsigned long data_seek_offset, unsigned short frame_size ... */ +} WSA_FileHeaderType; + +#define WSA_FILE_HEADER_SIZE ( sizeof(WSA_FileHeaderType) - (2 * sizeof(unsigned long)) ) + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ); +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust); +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_ANIMATION -- Opens an animation file and reads into buffer * + * * + * INPUT: char *file_name of animation sequence file. * + * char *user_buffer pointer if one exists (NULL ok) * + * unsigned long user_buffer_size if known (NULL ok) * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to palette space for return (NULL ok) * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: May return NULL, please check. * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette) +{ + int fh, anim_flags; + int palette_adjust; + unsigned int offsets_size; + unsigned int frame0_size; + long target_buffer_size, delta_buffer_size, file_buffer_size; + long max_buffer_size, min_buffer_size; + char *sys_anim_header_buffer; + char *target_buffer; + char *delta_buffer, *delta_back; + SysAnimHeaderType *sys_header; + WSA_FileHeaderType file_header; + + /*======================================================================*/ + /* Open the file to get the header information */ + /*======================================================================*/ + + anim_flags = 0; + fh = Open_File(file_name, READ); + Read_File(fh, (char *) &file_header, sizeof(WSA_FileHeaderType)); + + /*======================================================================*/ + /* If the file has an attached palette then if we have a valid palette */ + /* pointer we need to read it in. */ + /*======================================================================*/ + + if (file_header.flags & 1) { + anim_flags |= WSA_PALETTE_PRESENT; + palette_adjust = 768; + + if (palette != NULL) { + Seek_File(fh, sizeof(unsigned long) * (file_header.total_frames), SEEK_CUR); + Read_File(fh, palette, 768L); + } + + } else { + palette_adjust = 0; + } + + // Check for flag from ANIMATE indicating that this animation was + // created from a .LBM and a .ANM. These means that the first + // frame is a XOR Delta from a picture, not black. + if (file_header.flags & 2) { + anim_flags |= WSA_FRAME_0_IS_DELTA; + } + + + // Get the total file size minus the size of the first frame and the size + // of the file header. These will not be read in to save even more space. + file_buffer_size = Seek_File(fh, 0L, SEEK_END); + + if (file_header.frame0_offset) { + long tlong; + + tlong = file_header.frame0_end - file_header.frame0_offset; + frame0_size = (unsigned short) tlong; + } + else { + anim_flags |= WSA_FRAME_0_ON_PAGE; + frame0_size = 0; + } + + file_buffer_size -= palette_adjust + frame0_size + WSA_FILE_HEADER_SIZE; + + // We need to determine the buffer sizes required for the animation. At a + // minimum, we need a target buffer for the uncompressed frame and a delta + // buffer for the delta data. We may be able to make the file resident, + // so we will determine the file size. + // + // If the target buffer is in the user buffer + // Then figure its size + // and set the allocation flag + // Else size is zero. + // + if (user_flags & WSA_OPEN_DIRECT) { + target_buffer_size = 0L; + } + else { + anim_flags |= WSA_TARGET_IN_BUFFER; + target_buffer_size = (unsigned long) file_header.pixel_width * file_header.pixel_height; + } + + // NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE + // UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING + // IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. + delta_buffer_size = (unsigned long) file_header.largest_frame_size + EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT; + min_buffer_size = target_buffer_size + delta_buffer_size; + max_buffer_size = min_buffer_size + file_buffer_size; + + // check to see if buffer size is big enough for at least min required + if (user_buffer && (user_buffer_size < min_buffer_size)) { + Close_File(fh); + return(NULL); + } + + // A buffer was not passed in, so do allocations + if (user_buffer == NULL) { + + // If the user wants it from the disk, then let us give it to him, + // otherwise, try to give a max allocation he can have. + if (user_flags & WSA_OPEN_FROM_DISK) { + user_buffer_size = min_buffer_size; + } + // else no buffer size, then try max configuration. + else if (!user_buffer_size) { + user_buffer_size = max_buffer_size; + } + // else if buffer specified is less then max needed, give min. + else if (user_buffer_size < max_buffer_size) { + user_buffer_size = min_buffer_size; + } + // otherwise we only want to alloc what we need. + else { + user_buffer_size = max_buffer_size; + } + + + // Check to see if enough RAM available for buffer_size. + if (user_buffer_size > Ram_Free(MEM_NORMAL)) { + + // If not enough room for even the min, return no buffer. + + if (min_buffer_size > Ram_Free(MEM_NORMAL)) { + Close_File(fh); + return(NULL); + } + + // Else make buffer size the min and allocate it. + user_buffer_size = min_buffer_size; + } + + // allocate buffer needed + user_buffer = (char *) Alloc(user_buffer_size, MEM_CLEAR); + + anim_flags |= WSA_SYS_ALLOCATED; + } + else { + // Check to see if the user_buffer_size should be min or max. + if ((user_flags & WSA_OPEN_FROM_DISK) || (user_buffer_size < max_buffer_size)) { + user_buffer_size = min_buffer_size; + } + else { + user_buffer_size = max_buffer_size; + } + anim_flags |= WSA_USER_ALLOCATED; + } + + + // Set the pointers to the RAM buffers + sys_anim_header_buffer = user_buffer; + target_buffer = (char *) Add_Long_To_Pointer(sys_anim_header_buffer, sizeof(SysAnimHeaderType)); + delta_buffer = (char *) Add_Long_To_Pointer(target_buffer, target_buffer_size); + + // Clear target buffer if it is in the user buffer. + if (target_buffer_size) { + memset( target_buffer, 0, (unsigned short) target_buffer_size ); + } + + // Poke data into the system animation header (start of user_buffer) + // current_frame is set to total_frames so that Animate_Frame() knows that + // it needs to clear the target buffer. + + sys_header = ( SysAnimHeaderType * ) sys_anim_header_buffer; + sys_header -> current_frame = + sys_header -> total_frames = file_header.total_frames; + sys_header -> pixel_x = file_header.pixel_x; + sys_header -> pixel_y = file_header.pixel_y; + sys_header -> pixel_width = file_header.pixel_width; + sys_header -> pixel_height = file_header.pixel_height; + sys_header -> anim_mem_size = user_buffer_size; + sys_header -> delta_buffer = delta_buffer; + sys_header -> largest_frame_size = + (unsigned short) (delta_buffer_size - sizeof(SysAnimHeaderType)); + + strcpy(sys_header->file_name, file_name); + + // Figure how much room the frame offsets take up in the file. + // Add 2 - one for the wrap around and one for the final end offset. + offsets_size = (file_header.total_frames + 2) << 2; + + // Can the user_buffer_size handle the maximum case buffer? + if ( user_buffer_size == max_buffer_size) { + + // + // set the file buffer pointer, + // Skip over the header information. + // Read in the offsets. + // Skip over the first frame. + // Read in remaining frames. + // + + sys_header->file_buffer = (char *)Add_Long_To_Pointer(delta_buffer,sys_header->largest_frame_size); + Seek_File( fh, WSA_FILE_HEADER_SIZE, SEEK_SET); + Read_File( fh, sys_header->file_buffer, offsets_size); + Seek_File( fh, frame0_size + palette_adjust, SEEK_CUR); + Read_File( fh, sys_header->file_buffer + offsets_size, + file_buffer_size - offsets_size); + + // + // Find out if there is an ending value for the last frame. + // If there is not, then this animation will not be able to + // loop back to the beginning. + // + if (Get_Resident_Frame_Offset( sys_header->file_buffer, sys_header->total_frames + 1)) + anim_flags |= WSA_RESIDENT; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_RESIDENT; + } + else { // buffer cannot handle max_size of buffer + + if(Get_File_Frame_Offset( fh, sys_header->total_frames + 1, palette_adjust)) + anim_flags |= WSA_FILE; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_FILE; +//// + sys_header->file_buffer = NULL; + } + + // Figure where to back load frame 0 into the delta buffer. + delta_back = (char *)Add_Long_To_Pointer(delta_buffer, + sys_header->largest_frame_size - frame0_size); + + // Read the first frame into the delta buffer and uncompress it. + // Then close it. + Seek_File( fh, WSA_FILE_HEADER_SIZE + offsets_size + palette_adjust, SEEK_SET); + Read_File( fh, delta_back, frame0_size); + + // We do not use the file handle when it is in RAM. + if (anim_flags & WSA_RESIDENT) { + sys_header -> file_handle = (short) -1; + Close_File(fh); + } + else { + sys_header -> file_handle = (short)fh; + } + + LCW_Uncompress(delta_back, delta_buffer, sys_header->largest_frame_size); + + // Finally set the flags, + sys_header->flags = (short)anim_flags; + + // return valid handle + return( user_buffer ); +} + + +/*************************************************************************** + * CLOSE_ANIMATION -- Close the animation, freeing the space if necessary. * + * * + * INPUT: void *handle to the animation data buffer * + * * + * OUTPUT: none * + * * + * WARNINGS: handle MUST have been returned by Open_Animation * + * * + * HISTORY: * + * 11/23/1991 ML : Created. * + *=========================================================================*/ +void __cdecl Close_Animation( void *handle ) +{ + SysAnimHeaderType *sys_header; + + // Assign our local system header pointer to the beginning of the handle space + sys_header = (SysAnimHeaderType *) handle; + + // Close the WSA file in it was disk based. + if (sys_header->flags & WSA_FILE) { + Close_File(sys_header->file_handle); + } + + // Check to see if the buffer was allocated OR the programmer provided the buffer + if (handle && sys_header->flags & WSA_SYS_ALLOCATED) { + Free(handle); + } +} + +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma off (unreferenced) +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + if (view.Lock()!=TRUE) return (FALSE); + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd() + view.Get_Pitch(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + view.Unlock(); + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = (short)frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + view.Unlock(); + return TRUE; +} +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma argsused +#ifdef cuts +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} +#endif + + +/*************************************************************************** + * ANIMATE_FRAME_COUNT -- Return Number of frames in an animation. * + * * + * INPUT: void *handle to the animation. * + * * + * OUTPUT: int number of frames in animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/05/1991 SB : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Frame_Count(void *handle) +{ + SysAnimHeaderType *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return((short)sys_header->total_frames); +} + + +/*************************************************************************** + * GET_ANIM_X -- Gets the x from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the x of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1992 DRD : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_X(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_x); +} + +/*************************************************************************** + * GET_ANIM_Y -- Gets the y from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the y of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Y(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_y); +} + +/*************************************************************************** + * GET_ANIM_WIDTH -- Gets the width from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the width of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Width(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_width); +} + +/*************************************************************************** + * GET_ANIM_HEIGHT -- The height of the animation we are processing * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the height of the animation we are processing * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Height(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_height); +} + + +/*************************************************************************** + * GET_ANIM_PALETTE -- Returns true if the anim had an attached palette * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int True if the animation has a set palette. False if the * + * animation does not. * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Palette(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->flags & WSA_PALETTE_PRESENT); +} + + +/*************************************************************************** + * GET_ANIMATION_SIZE -- Return the amount of memory the animation is using* + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: unsigned long number of byte used by animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/23/1994 SKB : Created. * + *=========================================================================*/ +unsigned long __cdecl Get_Animation_Size(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->anim_mem_size); +} + +/* :::::::::::::::::::::::::::: PRIVATE FUNCTIONS :::::::::::::::::::::::::::::: */ + + +/*************************************************************************** + * GET_RESIDENT_FRAME_OFFSET -- Gets frame offset of animate file in RAM * + * * + * INPUT: char *file_buffer in RAM of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ) +{ + unsigned long frame0_size; + unsigned long *lptr; + + // If there is a frame 0, the calculate its size. + lptr = (unsigned long *) file_buffer; + + if (*lptr) { + frame0_size = lptr[1] - *lptr; + } else { + frame0_size = 0; + } + + // Return the offset into RAM for the frame. + lptr += frame; + if (*lptr) + return (*lptr - (frame0_size + WSA_FILE_HEADER_SIZE)); + else + return (0L); +} + + +/*************************************************************************** + * GET_FILE_FRAME_OFFSET -- Get offset of a delta frame from animate file. * + * * + * INPUT: int file_handle of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust) +{ + unsigned long offset; + + Seek_File(file_handle, (frame << 2) + WSA_FILE_HEADER_SIZE, SEEK_SET); + + if (Read_File(file_handle, (char *) &offset, sizeof(unsigned long)) != sizeof(unsigned long)) { + offset = 0L; + } + offset += palette_adjust; + return( offset ); +} + + +/*************************************************************************** + * APPLY_DELTA -- Copies frame into delta buffer, then applies to target * + * * + * INPUT: SysAnimHeaderType *sys_header - pointer to animation buffer.* + * int curr_frame - frame to put into target buffer. * + * * + * OUTPUT: BOOL - Return wether or not it worked. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w) +{ + char *data_ptr, *delta_back; + int file_handle, palette_adjust; + unsigned long frame_data_size, frame_offset; + + + palette_adjust = ((sys_header->flags & WSA_PALETTE_PRESENT) ? 768 : 0); + delta_back = sys_header->delta_buffer; + + if (sys_header->flags & WSA_RESIDENT) { + // Get offset of the given frame in the resident file + // Get the size of the frame <- (frame+1 offset) - (offset) + // Point at the delta data + // figure offset to load data into end of delta buffer + // copy it into buffer + + frame_offset = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame); + frame_data_size = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame + 1) - frame_offset; + + data_ptr = (char *)Add_Long_To_Pointer(sys_header->file_buffer, frame_offset); + delta_back = (char *)Add_Long_To_Pointer(delta_back, + sys_header->largest_frame_size - frame_data_size); + + Mem_Copy( data_ptr, delta_back, frame_data_size ); + + } else if (sys_header -> flags & WSA_FILE) { + + // Open up file because not file not in RAM. + // Get offset of the given frame in the file on disk + // Get the size of the frame <- (frame+1 offset) - (offset) + // Return if Get_.._offset() failed. -- need error handling???? + // Seek to delta data. + // figure offset to load data into end of delta buffer + // Read it into buffer -- Return if correct amount not read.-- errors?? + + file_handle = sys_header->file_handle; + Seek_File(file_handle, 0L, SEEK_SET); + + frame_offset = Get_File_Frame_Offset(file_handle, curr_frame, palette_adjust); + frame_data_size = Get_File_Frame_Offset(file_handle, curr_frame + 1, palette_adjust) - frame_offset; + + if (!frame_offset || !frame_data_size) { + return(FALSE); + } + + Seek_File(file_handle, frame_offset, SEEK_SET); + delta_back = (char *)Add_Long_To_Pointer(delta_back, sys_header->largest_frame_size - frame_data_size); + + if (Read_File(file_handle, delta_back, frame_data_size) != frame_data_size) { + return(FALSE); + } + } + + // Uncompress data at end of delta buffer to the beginning of delta buffer. + // Find start of target buffer. + // Apply the XOR delta. + + LCW_Uncompress(delta_back, sys_header->delta_buffer, sys_header->largest_frame_size); + + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + Apply_XOR_Delta(dest_ptr, sys_header->delta_buffer); + } + else { + Apply_XOR_Delta_To_Page_Or_Viewport(dest_ptr, sys_header->delta_buffer, + sys_header->pixel_width, dest_w, DO_XOR); + } + + return(TRUE); +} + diff --git a/WIN32LIB/WSA/WSA.H b/WIN32LIB/WSA/WSA.H new file mode 100644 index 0000000..12141b4 --- /dev/null +++ b/WIN32LIB/WSA/WSA.H @@ -0,0 +1,163 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette=NULL); +void __cdecl Close_Animation( void *handle ); +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +BOOL __cdecl Animate_Frame(void *handle, VideoViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +int __cdecl Get_Animation_X(void const *handle); +int __cdecl Get_Animation_Y(void const *handle); +int __cdecl Get_Animation_Width(void const *handle); +int __cdecl Get_Animation_Height(void const *handle); +int __cdecl Get_Animation_Palette(void const *handle); +unsigned long __cdecl Get_Animation_Size(void const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, BufferClass& buffer, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, (char *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +unsigned int __cdecl Apply_XOR_Delta(char *source_ptr, char *delta_ptr); +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy); +} + + + +#endif // WSA_H + diff --git a/WIN32LIB/WSA/XORDELTA.ASM b/WIN32LIB/WSA/XORDELTA.ASM new file mode 100644 index 0000000..1b9e4a9 --- /dev/null +++ b/WIN32LIB/WSA/XORDELTA.ASM @@ -0,0 +1,669 @@ +; +; 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 . +; + +; ************************************************************************** +; ** 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 + + + + +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 +; + +GLOBAL C Apply_XOR_Delta:NEAR +GLOBAL C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR + + + + CODESEG + + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Apply_XOR_Delta C near + 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 + + ENDP Apply_XOR_Delta + + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +;PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD flags, WORD descriptor) +PROC Apply_XOR_Delta_To_Page_Or_Viewport C near + 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,[width] ; 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 + + ENDP Apply_XOR_Delta_To_Page_Or_Viewport +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +PROC XOR_Delta_Buffer C near + ARG nextrow:DWORD + +??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 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_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 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_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] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + + ENDP XOR_Delta_Buffer + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Copy_Delta_Buffer C near + ARG nextrow:DWORD + +??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 + + 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 ; 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_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: + 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 ; 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_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] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + ENDP Copy_Delta_Buffer + +;---------------------------------------------------------------------------- + + 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); +;} +; + + diff --git a/WIN32LIB/WW_WIN/MAKEFILE b/WIN32LIB/WW_WIN/MAKEFILE new file mode 100644 index 0000000..23e3250 --- /dev/null +++ b/WIN32LIB/WW_WIN/MAKEFILE @@ -0,0 +1,192 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = ww_win +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + *$(CPP_CMD) $(CC_CFG) $(PROJ_DIR)\$^*.cpp + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/WW_WIN/MAKEFILE.BOR b/WIN32LIB/WW_WIN/MAKEFILE.BOR new file mode 100644 index 0000000..ae22549 --- /dev/null +++ b/WIN32LIB/WW_WIN/MAKEFILE.BOR @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WWFLAT path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +.AUTODEPEND +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef COMPILER +!error COMPILER Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = ww_win +PROJ_DIR = $(WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(WIN32LIB)\lib + +!include $(WIN32LIB)\\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = \ + windows.obj \ + winhide.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.path.asm = $(PROJ_DIR) +.path.c = $(PROJ_DIR) +.path.cpp = $(PROJ_DIR) +.path.h = $(PROJ_DIR) +.path.obj = $(PROJ_DIR) +.path.lib = $(WIN32LIB)\lib +.path.exe = $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = bcc32 +CPP_CMD = bcc32 +LIB_CMD = tlib +LINK_CMD = tlink32 +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(WIN32LIB)\LIB;$(COMPILER)\LIB +INCLUDEPATH = $(WIN32LIB)\INCLUDE;$(COMPILER)\INCLUDE + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\\$(PROJ_NAME).lib: $(OBJECTS) + copy *.h $(WIN32LIB)\\include + copy *.inc $(WIN32LIB)\\include + copy *.cpp $(WIN32LIB)\\srcdebug + copy *.asm $(WIN32LIB)\\srcdebug + $(LIB_CMD) $< $(LIB_CFG) @&&| + +-windows.obj \ ++-winhide.obj +| + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(WWVCS)\\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + diff --git a/WIN32LIB/WW_WIN/MAKEFILE.WAT b/WIN32LIB/WW_WIN/MAKEFILE.WAT new file mode 100644 index 0000000..f897cf6 --- /dev/null +++ b/WIN32LIB/WW_WIN/MAKEFILE.WAT @@ -0,0 +1,192 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = your root WIN32LIB path * +#* WIN32VCS = root directory for wwlib version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = ww_win +PROJ_DIR = $(%WIN32LIB)\$(PROJ_NAME) +LIB_DIR = $(%WIN32LIB)\lib + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WIN32LIB)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WIN32LIB\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WIN32LIB)\include + copy *.inc $(%WIN32LIB)\include + copy *.cpp $(%WIN32LIB)\srcdebug + copy *.asm $(%WIN32LIB)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/WW_WIN/OLD/MAKEFILE b/WIN32LIB/WW_WIN/OLD/MAKEFILE new file mode 100644 index 0000000..505b4c4 --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/MAKEFILE @@ -0,0 +1,193 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = windows +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/WW_WIN/OLD/MAKEFILE.BOR b/WIN32LIB/WW_WIN/OLD/MAKEFILE.BOR new file mode 100644 index 0000000..3f0f79b --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/MAKEFILE.BOR @@ -0,0 +1,193 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = windows +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/WW_WIN/OLD/MAKEFILE.WAT b/WIN32LIB/WW_WIN/OLD/MAKEFILE.WAT new file mode 100644 index 0000000..3f0f79b --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/MAKEFILE.WAT @@ -0,0 +1,193 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = windows +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WIN32LIB/WW_WIN/OLD/WINDOWS.CPP b/WIN32LIB/WW_WIN/OLD/WINDOWS.CPP new file mode 100644 index 0000000..a407953 --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/WINDOWS.CPP @@ -0,0 +1,973 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "windows.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(BYTE const *,int,int,int) = Standard_More_Prompt; + +extern GraphicBufferClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + Clear_KeyBuffer(); + Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ + case KA_LITERAL: + if (c != (char) 127) { // check if fell thru from extend case + c = 0; // set to zero for literal case + } + c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & 0x07; + c = (c & 0x78) >> 3; + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + diff --git a/WIN32LIB/WW_WIN/OLD/WINHIDE.CPP b/WIN32LIB/WW_WIN/OLD/WINHIDE.CPP new file mode 100644 index 0000000..dbcb447 --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/WINHIDE.CPP @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "windows.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; + Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ + Conditional_Show_Mouse(); +} +#endif + + + \ No newline at end of file diff --git a/WIN32LIB/WW_WIN/OLD/WW_WIN.H b/WIN32LIB/WW_WIN/OLD/WW_WIN.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/WIN32LIB/WW_WIN/OLD/WW_WIN.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WIN32LIB/WW_WIN/WINDOWS.CPP b/WIN32LIB/WW_WIN/WINDOWS.CPP new file mode 100644 index 0000000..1903746 --- /dev/null +++ b/WIN32LIB/WW_WIN/WINDOWS.CPP @@ -0,0 +1,976 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "ww_win.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; + +extern GraphicViewPortClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + // PWG - have to figure out how to do this in windows library + +// Clear_KeyBuffer(); +// Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ +// case KA_LITERAL: +// if (c != (char) 127) { // check if fell thru from extend case +// c = 0; // set to zero for literal case +// } +// c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + (unsigned char)WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & (char)0x07; + c = (char)((c & (char)0x78) >> 3); + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + + diff --git a/WIN32LIB/WW_WIN/WINHIDE.CPP b/WIN32LIB/WW_WIN/WINHIDE.CPP new file mode 100644 index 0000000..f32de99 --- /dev/null +++ b/WIN32LIB/WW_WIN/WINHIDE.CPP @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "ww_win.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; +// Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ +// Conditional_Show_Mouse(); +} +#endif + + + diff --git a/WIN32LIB/WW_WIN/WW_WIN.H b/WIN32LIB/WW_WIN/WW_WIN.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/WIN32LIB/WW_WIN/WW_WIN.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WIN32LIB/WW_WIN/WW_WIN.IDE b/WIN32LIB/WW_WIN/WW_WIN.IDE new file mode 100644 index 0000000..a57d585 Binary files /dev/null and b/WIN32LIB/WW_WIN/WW_WIN.IDE differ diff --git a/WIN32LIB/WW_WIN/WW_WIN.IDE.BAK b/WIN32LIB/WW_WIN/WW_WIN.IDE.BAK new file mode 100644 index 0000000..256c651 Binary files /dev/null and b/WIN32LIB/WW_WIN/WW_WIN.IDE.BAK differ diff --git a/WINVQ/INCLUDE/VOCFILE.H b/WINVQ/INCLUDE/VOCFILE.H new file mode 100644 index 0000000..bbdd54d --- /dev/null +++ b/WINVQ/INCLUDE/VOCFILE.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +#ifndef VOCFILE_H +#define VOCFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vocfile.h +* +* DESCRIPTION +* VOC audio file definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +****************************************************************************/ + +/* VOC file header. + * + * type - File type description + * offset - Offset of data block from the start of VOC file. + * ver - File format version number + * id - File identification code. + */ +typedef struct _VOCHeader { + char type[0x14]; + unsigned short offset; + unsigned short ver; + unsigned short id; +} VOCHeader; + +/* VOC sub-block block types */ +#define VBT_TERMINATE 0 /* Last block of file (no size field) */ +#define VBT_VOICEDATA 1 /* New set of voice data */ +#define VBT_VOICECONT 2 /* Continuation of voice data */ +#define VBT_SILENCE 3 /* Silence period */ +#define VBT_MARKER 4 /* Syncronization marker */ +#define VBT_ASCII 5 /* NULL terminated string */ +#define VBT_REPEAT 6 /* Mark beginning of repeat loop */ +#define VBT_ENDREPEAT 7 /* Mark end of repeat loop */ +#define VBT_EXTENDED 8 + +#define IS_VOC(a,b) (((~a)+0x1234)==b) +#define BLOCK_LEN(a) ((a&0x00FFFFFFL)) + +#endif /* VOCFILE_H */ + + diff --git a/WINVQ/INCLUDE/VQ.H b/WINVQ/INCLUDE/VQ.H new file mode 100644 index 0000000..ff34617 --- /dev/null +++ b/WINVQ/INCLUDE/VQ.H @@ -0,0 +1,106 @@ +/* +** 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 . +*/ + +#ifndef VQ_H +#define VQ_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* NAME +* vq.h +* +* DESCRIPTION +* VQ file definition +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* September 28, 1995 +* +* NOTES +* VQ file organization: +* +* Header: This structure contains all the data pertaining to the VQ. +* (See structure definition for details) +* +* Size: 40 bytes +* +* Palette: Color palette used by this VQ. Stored as 24 bit RGB entries. +* +* Size: (PaletteRange * 3) bytes +* +* Codebook: Sequence of vector blocks used to represent the VQ image. +* Also called the dictionary. The sequence is sorted by +* usage, so the first entry is the most commonly used block. +* +* Size: ((BlockWidth * BlockHeight) * CodebookSize) bytes +* +* Pointers: Vector pointer map used to indicate the construction of +* the vector blocks. Also called codewords. +* +* Size: (((ImageWidth / BlockWidth) +* * (ImageHeight / BlockHeight)) * 2) +* +* Pointer format: +* +* Single color - Value less than or equal to 'Num1Color' +* Multi Color - Value greater than 'Num1Color' +* +****************************************************************************/ + +/* VQHeader - VQ header structure. + * + * ImageSize - Compressed image size + * ImageWidth - Image x-dimension + * ImageHeight - Image y-dimension + * BlockWidth - Block x-dimension + * BlockHeight - Block y-dimension + * BlockType - Block type + * PaletteRange - Number of palette colors + * Num1Color - Number of 1-color blocks & 1-color colors + * CodebookSize - Number of actual codebook entries + * CodingFlag - Flag for 2-color blocks + * FrameDiffMethod - Frame differencing method + * ForcedPalette - Force a palette on image + * F555Palette - Flag for output 15-bit palette + * VQVersion - VQ Version # + * pad[5] - Pad out to 40 bytes total + */ +typedef struct _VQHeader { + unsigned long ImageSize; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned short BlockWidth; + unsigned short BlockHeight; + unsigned short BlockType; + unsigned short PaletteRange; + unsigned short Num1Color; + unsigned short CodebookSize; + unsigned short CodingFlag; + unsigned short FrameDiffMethod; + unsigned short ForcedPalette; + unsigned short F555Palette; + unsigned short VQVersion; + unsigned short pad[5]; +} VQHeader; + +#endif /* VQ_H */ diff --git a/WINVQ/INCLUDE/VQA32/CAPTION.H b/WINVQ/INCLUDE/VQA32/CAPTION.H new file mode 100644 index 0000000..cda7a97 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/CAPTION.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQACAPTION_H +#define VQACAPTION_H +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.h +* +* DESCRIPTION +* Text caption definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + + +#define VQACAPTIONS_ON 0 + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* CaptionNode: Node describing a caption to process. + * + * Succ - Pointer to the next node in the list (successor). + * Pred - Pointer to the previous node in the list (predecessor). + * Flags - Status flags. + * CapText - Pointer to the CaptionText being processed. + * Char - Pointer to current character in the string. + * CurX - Current X position. + * CurY - Current Y position. + * BoundW - Bounding width of text. + * BoundH - Bounding height of text. + */ +typedef struct _CaptionNode { + struct _CaptionNode *Succ; + struct _CaptionNode *Pred; + unsigned short Flags; + CaptionText *Captext; + char *Char; + unsigned short CurX; + unsigned short CurY; + unsigned short BoundW; + unsigned short BoundH; +} CaptionNode; + +/* CaptionNode flag definitions. */ +#define CNB_USED 0 /* This node is being used. */ +#define CNF_USED (1<. +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WINVQ/INCLUDE/VQA32/SOSDATA.H b/WINVQ/INCLUDE/VQA32/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WINVQ/INCLUDE/VQA32/SOSDEFS.H b/WINVQ/INCLUDE/VQA32/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WINVQ/INCLUDE/VQA32/SOSFNCT.H b/WINVQ/INCLUDE/VQA32/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WINVQ/INCLUDE/VQA32/SOSRES.H b/WINVQ/INCLUDE/VQA32/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WINVQ/INCLUDE/VQA32/UNVQ.H b/WINVQ/INCLUDE/VQA32/UNVQ.H new file mode 100644 index 0000000..0279898 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/UNVQ.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQAUNVQ_H +#define VQAUNVQ_H +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* unvq.h +* +* DESCRIPTION +* VQ frame decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +#ifdef PHARLAP_TNT +#include +#endif + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* unvqbuff.asm */ +#ifndef PHARLAP_TNT +void __cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x2_Woofer(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +/* unvqvesa.asm */ +void __cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, unsigned char *palette, + unsigned long grains_per_win,unsigned long dummy1,unsigned long dummy2); + +#else /* PHARLAP_TNT */ + +void __cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +/* unvqvesa.asm */ +void __cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, FARPTR palette, unsigned long grains_per_win, + unsigned long dummy1, unsigned long dummy2); + +#endif /* PHARLAP_TNT */ + +/* unvqxmde.asm */ +void __cdecl UnVQ_4x2_Xmode(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void __cdecl UnVQ_4x2_XmodeCB(unsigned char *cbdummy, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void __cdecl Upload_4x2CB(unsigned char *codebook, unsigned long numentries); +void __cdecl XlatePointers(unsigned char *pointers, unsigned long numpointers); + +#ifdef __cplusplus +} +#endif + +#endif /* VQAUNVQ_H */ diff --git a/WINVQ/INCLUDE/VQA32/VQAFILE.H b/WINVQ/INCLUDE/VQA32/VQAFILE.H new file mode 100644 index 0000000..9e14532 --- /dev/null +++ b/WINVQ/INCLUDE/VQA32/VQAFILE.H @@ -0,0 +1,197 @@ +/* +** 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 . +*/ + +#ifndef VQAFILE_H +#define VQAFILE_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqafile.h +* +* DESCRIPTION +* VQA file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +#include + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED DEFINES. + *-------------------------------------------------------------------------*/ + +/* VQAHeader: VQA movie description header. (VQHD) + * + * Version - VQA version. + * Flags - Various flags. (See below) + * ImageWidth - Image width in pixels. + * ImageHeight - Image height in pixels. + * BlockWidth - Block width in pixels. + * BlockHeight - Block height in pixels. + * Frames - Total number of frames in the movie. + * FPS - Playback rate (Frame Per Second). + * Groupsize - Frame grouping size (frames per codebook). + * Num1Colors - Number of 1 color colors. + * CBentries - Number of codebook entries. + * Xpos - X position to draw frames. (-1 = Center) + * Ypos - Y position to draw frames. (-1 = Center) + * MaxFramesize - Size of largest frame. + * SampleRate - Sample rate of primary audio stream. + * Channels - Number of channels in primary audio stream. + * BitsPerSample - Sample bit size in primary audio stream. + * FutureUse - Reserved for future expansion. + */ +typedef struct _VQAHeader { + unsigned short Version; + unsigned short Flags; + unsigned short Frames; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char BlockWidth; + unsigned char BlockHeight; + unsigned char FPS; + unsigned char Groupsize; + unsigned short Num1Colors; + unsigned short CBentries; + unsigned short Xpos; + unsigned short Ypos; + unsigned short MaxFramesize; + unsigned short SampleRate; + unsigned char Channels; + unsigned char BitsPerSample; + unsigned short AltSampleRate; + unsigned char AltChannels; + unsigned char AltBitsPerSample; + unsigned short FutureUse[5]; +} VQAHeader; + +/* Version type. */ +#define VQAHD_VER1 1 +#define VQAHD_VER2 2 + +/* VQA header flag definitions */ +#define VQAHDB_AUDIO 0 /* Audio track present. */ +#define VQAHDB_ALTAUDIO 1 /* Alternate audio track present. */ +#define VQAHDF_AUDIO (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +// MEG - 11.28.95 - added for debug +extern void Debug_Printf( char *format_string, ... ); + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + + +#if (VQAAUDIO_ON && VQADIRECT_SOUND) +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#undef WIN32_LEAN_AND_MEAN +#include +#include +#include "dsound.h" +#endif + + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * SoundObject - Ptr to callers Direct Sound Object (Default =NULL) + * PrimaryBufferPtr- Ptr to callers Primary Sound Buffer. (Default = NULL) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; +#if (VQADIRECT_SOUND) + LPDIRECTSOUND SoundObject; + LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +#endif //(VQADIRECT_SOUND) + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +// MEG - 11.28.95 - added for debug +extern void Debug_Printf( char *format_string, ... ); + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + + +#if (VQAAUDIO_ON && VQADIRECT_SOUND) +#ifndef WIN32 1 +#define WIN32 +#define _WIN32 +#endif +#undef WIN32_LEAN_AND_MEAN +#include +#include +#include "dsound.h" +#endif + + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * SoundObject - Ptr to callers Direct Sound Object (Default =NULL) + * PrimaryBufferPtr- Ptr to callers Primary Sound Buffer. (Default = NULL) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; +#if (VQADIRECT_SOUND) + LPDIRECTSOUND SoundObject; + LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +#endif //(VQADIRECT_SOUND) + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQAPLAYP_H +#define VQAPLAYP_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplayp.h +* +* DESCRIPTION +* VQAPlay private library definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* August 21, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include "vqafile.h" +#include "vqaplay.h" +#include "caption.h" + +#if(VQAAUDIO_ON) +#if(VQADIRECT_SOUND) +extern HWND MainWindow; +#else +#include "sos.h" +#endif +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Internal library version. */ +#define VQA_VERSION "2.42" +#define VQA_DATE __DATE__" "__TIME__ + +#define VQA_IDSTRING "VQA32 "VQA_VERSION" ("VQA_DATE")" +#define VQA_REQUIRES "VQM32 2.12 or better." + +/* Block dimensions macro and identifiers. */ +#define BLOCK_DIM(a,b) (((a&0xFF)<<8)|(b&0xFF)) +#define BLOCK_2X2 BLOCK_DIM(2,2) +#define BLOCK_2X3 BLOCK_DIM(2,3) +#define BLOCK_4X2 BLOCK_DIM(4,2) +#define BLOCK_4X4 BLOCK_DIM(4,4) + +/* Memory limits */ +#define VQA_MAX_CBBUFS 10 /* Maximum number of codebook buffers */ +#define VQA_MAX_FRAMEBUFS 30 /* Maximum number of frame buffers */ + +/* Special Constants */ +#define VQA_MASK_POINTER 0x8000 /* Pointer value to use for masking. */ + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* ChunkHeader: IFF chunk identifier header. + * + * id - 4 Byte chunk id. + * size - Size of chunk. + */ +typedef struct _ChunkHeader { + unsigned long id; + unsigned long size; +} ChunkHeader; + + +/* ZAPHeader: ZAP audio compression header. NOTE: If the uncompressed size + * and the compressed size are equal then the audio frame is RAW + * (NOT COMPRESSED). + * + * UnCompSize - Uncompressed size in bytes. + * CompSize - Compressed size in bytes. + */ +typedef struct _ZAPHeader { + unsigned short UnCompSize; + unsigned short CompSize; +} ZAPHeader; + + +/* VQACBNode: A circular list of codebook buffers, used by the load task. + * If the data is compressed, it is loaded into the end of the + * buffer and the compression flags is set. Otherwise the data + * is loaded into the start of the buffer. + * (Make sure this structure's size is always DWORD aligned.) + * + * Buffer - Pointer to Codebook data. + * Next - Pointer to next VQACBNode in the codebook list. + * Flags - Used by the drawer to tell if certain operations have been + * performed on this codebook, such as downloading to VRAM, + * or pre-scaling it. This field is cleared by the Loader when a + * new codebook is loaded. + * CBOffset - Offset into the buffer of the compressed data. + */ +typedef struct _VQACBNode { + unsigned char *Buffer; + struct _VQACBNode *Next; + unsigned long Flags; + unsigned long CBOffset; +} VQACBNode; + +/* VQACBNode flags */ +#define VQACBB_DOWNLOADED 0 /* Download codebook to VRAM (XMODE VRAM) */ +#define VQACBB_CBCOMP 1 /* Codebook is compressed */ +#define VQACBF_DOWNLOADED (1<. +*/ + +#ifndef VQFILE_H +#define VQFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VQFile.h +* +* DESCRIPTION +* VQ file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* September 27, 1995 +* +****************************************************************************/ + +#include + +/* Disable Watcom structure alignment. */ +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED INFORMATION + *-------------------------------------------------------------------------*/ + +/* VQHeader: VQ header. + * + * Version - VQ file version + * Flags - Various flags (see below) + * ImageWidth - Width of constructed image in pixels. + * ImageHeight - Height of contructed image in pixels. + * BlockType - Type of codebook block. (EX: rectangle) + * BlockWidth - Width of a codebook block in pixels. + * BlockHeight - Height of a codebook block in pixels. + * BlockDepth - Depth of a codebook block in bits. + * CBEntries - Number of entries in the codebook. + * VPtrType - Vector pointer type. (IE: Banked) + * PalStart - Starting entry position of palette. + * PalLength - Number of palette entries the palette. + * PalDepth - Depth of palette entries in bits. + * ColorModel - Color model of this VQ + */ +typedef struct _VQHeader { + short Version; + short Flags; + short ImageWidth; + short ImageHeight; + short BlockType; + short BlockWidth; + short BlockHeight; + short BlockDepth; + short CBEntries; + short VPtrType; + short PalStart; + short PalLengh; + short PalDepth; + short ColorModel; +} VQHeader; + + +/* VQ file flags */ +#define VQHB_CBCOMP 0 /* Codebook compressed */ +#define VQHB_CTCOMP 1 /* Color table compressed */ +#define VQHB_VPCOMP 2 /* Vector pointers compressed */ +#define VQHF_CBCOMP (1<. +*/ + +#ifndef VQMALL_H +#define VQMALL_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* all.h +* +* DESCRIPTION +* All VQMisc32 library definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* VQMALL_H */ + diff --git a/WINVQ/INCLUDE/VQM32/CAPTOKEN.H b/WINVQ/INCLUDE/VQM32/CAPTOKEN.H new file mode 100644 index 0000000..17dc8e0 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/CAPTOKEN.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +#ifndef VQMCAPTOKEN_H +#define VQMCAPTOKEN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.h +* +* DESCRIPTION +* Text caption script definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +/* CaptionText: This structure describes a line of text to be displayed + * with the video/audio. + * + * Size - Size of caption in bytes. + * OnFrame - Frame number to display caption. + * OffFrame - Frame number to clear caption. + * Flags - Display modifiers. + * CPF - Characters to draw per frame. + * Xpos - X pixel position to display caption. + * Ypos - Y pixel position to display caption. + * BgPen - Background pen to use. + * FgPen - Foreground pen to use. + * Text - Text string to display. (WORD aligned) + */ +typedef struct _CaptionText { + unsigned short Size; + unsigned short OnFrame; + unsigned short OffFrame; + unsigned char Flags; + char CPF; + unsigned short Xpos; + unsigned short Ypos; + char BgPen; + char FgPen; + char Text[]; +} CaptionText; + +/* CaptionText flag definitions. */ +#define CTF_JUSTIFY (3<<0) /* Justification field. */ +#define CTF_ABS (0<<0) /* Use absolute X,Y positions. */ +#define CTF_CENTER (1<<0) /* Justify on Center */ +#define CTF_LEFT (2<<0) /* Justify on left */ +#define CTF_RIGHT (3<<0) /* Justify on right */ +#define CTF_FLASH (1<<4) /* Flash text. */ + +/* Function prototypes. */ +long BuildCaptions(char *name, char *buffer); + +#endif /* VQMCAPTOKEN_H */ + diff --git a/WINVQ/INCLUDE/VQM32/COMPRESS.H b/WINVQ/INCLUDE/VQM32/COMPRESS.H new file mode 100644 index 0000000..3db788b --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/COMPRESS.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMCOMP_H +#define VQMCOMP_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* compress.h +* +* DESCRIPTION +* Compression definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long __cdecl LCW_Compress(char const *source, char *dest, + unsigned long length); +unsigned long __cdecl LCW_Uncompress(char const *source, char *dest, + unsigned long length); +long AudioZap(void *source, void *dest, long size); +long __cdecl AudioUnzap(void *source, void *dest, long); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCOMP_H */ + diff --git a/WINVQ/INCLUDE/VQM32/CRC.H b/WINVQ/INCLUDE/VQM32/CRC.H new file mode 100644 index 0000000..fd18a00 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/CRC.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef VQMCRC_H +#define VQMCRC_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* CRC.h +* +* DESCRIPTION +* CRC calculation definitions. +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl Calculate_CRC(void const *buffer, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCRC_H */ diff --git a/WINVQ/INCLUDE/VQM32/FONT.H b/WINVQ/INCLUDE/VQM32/FONT.H new file mode 100644 index 0000000..d84cd42 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/FONT.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMFONT_H +#define VQMFONT_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.h +* +* DESCRIPTION +* Font definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +****************************************************************************/ + +/* Font: A Westwood style font. + * + * Size - Size of font. + * CompMethod - Compression method of font. (0 = none) + * NumBlks - Number of data blocks. + * InfoBlk - Offset to font information block. + * OffsetBlk - Offset to character offset block. + * WidthBlk - Offset to character width block. + * DataBlk - Offset to character data block. + * HeightBlk - Offset to character height block. + */ +typedef struct _Font { + unsigned short Size; + unsigned char CompMethod; + unsigned char NumBlks; + unsigned short InfoBlk; + unsigned short OffsetBlk; + unsigned short WidthBlk; + unsigned short DataBlk; + unsigned short HeightBlk; +} Font; + +typedef struct _FontInfo { + long huh; + unsigned char MaxHeight; + unsigned char MaxWidth; +} FontInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void const *FontPtr; +extern int FontXSpacing; +extern int FontYSpacing; + +#ifdef __cplusplus +} +#endif + +extern char FontWidth; +extern char FontHeight; +extern char *FontWidthBlockPtr; + +/* Function prototypes. */ +void *cdecl Load_Font(char const *name); +void *cdecl Set_Font(void const *font); +unsigned short __cdecl String_Pixel_Width(char const *string); + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __WATCOMC__ +long __cdecl __saveregs Char_Pixel_Width(char chr); +#else +long __cdecl Char_Pixel_Width(char chr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VQMFONT_H */ + diff --git a/WINVQ/INCLUDE/VQM32/GRAPHICS.H b/WINVQ/INCLUDE/VQM32/GRAPHICS.H new file mode 100644 index 0000000..0aa51d2 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/GRAPHICS.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +#ifndef VQMGRAPHICS_H +#define VQMGRAPHICS_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* graphics.h +* +* DESCRIPTION +* Graphic rendering and manipulation definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 27, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Eor_Region(long sx, long sy, long dx, long dy, long color); +void __cdecl Fill_Rect(long x1, long y1, long x2, long y2, long color); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMGRAPHICS_H */ + diff --git a/WINVQ/INCLUDE/VQM32/HUFFMAN.H b/WINVQ/INCLUDE/VQM32/HUFFMAN.H new file mode 100644 index 0000000..044c3f4 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/HUFFMAN.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +#ifndef HUFFMAN_H +#define HUFFMAN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffman.h +* +* DESCRIPTION +* Huffman order 0 compress/decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +****************************************************************************/ + + +/* TreeNode: Huffman decoding tree node. + * + * count - Weight of the node in the tree. + * child0 - Child node 0 + * child1 - Child node 1 + */ +typedef struct _TreeNode { + unsigned long count; + unsigned short child0; + unsigned short child1; +} TreeNode; + + +/* HuffCode: This structure is used for storing the code for each symbol + * during encoding. A table of codes for each symbol is built + * from the Huffman tree. + * + * code - Code used to represent a symbol. + * bits - Length of code in bits. + */ +typedef struct _HuffCode { + unsigned short code; + short bits; +} HuffCode; + + +#define HUFF_EOS 256 /* End of stream symbol */ + +/* Prototypes */ +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +long __cdecl HuffDecompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +void __cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero); + +void __cdecl HuffScaleCounts(TreeNode *nodes); +long __cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer); +long __cdecl BuildHuffTree(TreeNode *nodes); + +void __cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node); + +long __cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* HUFFMAN_H */ + diff --git a/WINVQ/INCLUDE/VQM32/IFF.H b/WINVQ/INCLUDE/VQM32/IFF.H new file mode 100644 index 0000000..c81c7e1 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/IFF.H @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + +#ifndef VQMIFF_H +#define VQMIFF_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.h +* +* DESCRIPTION +* IFF (Interchange File Format) manager definitions. +* (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* FormHeader - Structure associated with IFF forms. + * + * id - IFF form id (IE: "FORM") + * size - Length of IFF in bytes + * type - Form type (IE: "ILBM") + */ +typedef struct _FormHeader { + long id; + long size; + long type; +} FormHeader; + +/* Context - Structure associated with chunks. + * + * id - Chunk identifier. + * size - Size of chunk in bytes. + * scan - Bytes read/written. + */ +typedef struct _Context { + long id; + long size; + long scan; +} Context; + +/* IFFHandle - Structure associated with an active IFF read\write session. + * + * fh - DOS filehandle + * flags - Internal flags used by IFF routines. + * form - IFF form information. + * scan - Bytes read/written + * cn - Context of current chunk. + */ +typedef struct _IFFHandle { + long fh; + long flags; + FormHeader form; + long scan; + Context cn; +} IFFHandle; + +/* bit masks for "flags" field. */ +#define IFFB_READ 0 +#define IFFB_WRITE 1 +#define IFFF_READ (1<>24) \ + &0x000000FFL)|(((unsigned long)(id)>>8) \ + &0x0000FF00L)|(((unsigned long)(id)<<8) \ + &0x00FF0000L)|(((unsigned long)(id)<<24)&0xFF000000L)) + +#define REVERSE_WORD(id) ((unsigned short)((((unsigned short)(id)<<8) \ + &0x00FF00)|(((unsigned short)(id)>>8)&0x0FF))) + +#define PADSIZE(size) (((size)+1)&(~1)) + +#ifndef MAKE_ID +#define MAKE_ID(a,b,c,d) ((long)((long)(d)<<24)|((long)(c)<<16)| \ + ((long)(b)<<8)|(long)(a)) +#endif + +/* Universal IFF identifiers */ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_LIST MAKE_ID('L','I','S','T') +#define ID_PROP MAKE_ID('P','R','O','P') +#define ID_NULL MAKE_ID(' ',' ',' ',' ') + +/* Prototypes */ +IFFHandle *OpenIFF(char *, long); +void CloseIFF(IFFHandle *); +long ReadForm(IFFHandle *, FormHeader *); +long WriteForm(IFFHandle *, FormHeader *); +long ReadChunkHeader(IFFHandle *); +long WriteChunkHeader(IFFHandle *, long, long); +long WriteChunk(IFFHandle *, long, char *, long); +long WriteChunkBytes(IFFHandle *, char *, long); +long ReadChunkBytes(IFFHandle *, char *, long); +long SkipChunkBytes(IFFHandle *, long); +long FindChunk(IFFHandle *, long); +char *IDtoStr(long, char *); +long CurrentFilePos(IFFHandle *); + +#endif /* VQMIFF_H */ + diff --git a/WINVQ/INCLUDE/VQM32/MEM.H b/WINVQ/INCLUDE/VQM32/MEM.H new file mode 100644 index 0000000..69b0c45 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/MEM.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQAMEM_H +#define VQAMEM_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.h +* +* DESCRIPTION +* Memory management definitions. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +/* Definitions */ +#define DPMI_INT 0x0031 +#define DPMI_LOCK 0x0600 +#define DPMI_UNLOCK 0x0601 + +/* Prototypes */ +#ifdef __WATCOMC__ +void DPMI_Lock(void const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +#else +#define DPMI_Lock(a,b) +#define DPMI_Unlock(a,b) +#endif + +#endif /* VQAMEM_H */ diff --git a/WINVQ/INCLUDE/VQM32/MIXFILE.H b/WINVQ/INCLUDE/VQM32/MIXFILE.H new file mode 100644 index 0000000..5d6b012 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/MIXFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMMIXFILE_H +#define VQMMIXFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.h +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These definitions are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Disable structure alignment.*/ +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* MIXHeader: Mix file data header. + * + * Count - Number of entries contained in this mix file. + * Size - Size of Mix file. + */ +typedef struct _MIXHeader { + short Count; + long Size; +} MIXHeader; + +/* MIXSubBlock: Mix file entry descriptor. + * + * CRC - Unique entry identifier. + * Offset - Offset from beginning of data segment to entry. + * Size - Size of entry. + */ +typedef struct _MIXSubBlock { + long CRC; + long Offset; + long Size; +} MIXSubBlock; + +/* MIXHandle: Mix file handle. + * + * Name - Pointer to the name of the mix file this handle is for. + * Size - Size of entire mix file. + * FH - DOS file handle of opened entry. + * Count - Number of files contained in this mix. + * Entries - Array of 'Count' MIXSubBlock structure entries. + */ +typedef struct _MIXHandle { + char *Name; + long Size; + long FH; + long Count; + MIXSubBlock Entries[]; +} MIXHandle; + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +MIXHandle *OpenMix(char *name); +void CloseMix(MIXHandle *mix); +long OpenMixEntry(MIXHandle *mix, char *name); + +/* Restore original alignment */ +#ifdef __WATCOMC__ +#pragma pack(); +#endif + +#endif /* VQMMIXFILE_H */ + diff --git a/WINVQ/INCLUDE/VQM32/MONO.H b/WINVQ/INCLUDE/VQM32/MONO.H new file mode 100644 index 0000000..991662c --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/MONO.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +#ifndef VQMMONO_H +#define VQMMONO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mono.h +* +* DESCRIPTION +* Mono screen definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Mono_Enable(void); +void __cdecl Mono_Disable(void); +void __cdecl Mono_Set_Cursor(long x, long y); +void __cdecl Mono_Clear_Screen(void); +void __cdecl Mono_Scroll(long lines); +void __cdecl Mono_Put_Char(long character, long attrib); +void __cdecl Mono_Draw_Rect(long x, long y, long w, long h, long attrib, + long thick); + +void __cdecl Mono_Text_Print(void const *text, long x, long y, long attrib); +void __cdecl Mono_Print(void const *text); +short __cdecl Mono_View_Page(long page); +short __cdecl Mono_X(void); +short __cdecl Mono_Y(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMMONO_H */ diff --git a/WINVQ/INCLUDE/VQM32/PALETTE.H b/WINVQ/INCLUDE/VQM32/PALETTE.H new file mode 100644 index 0000000..45f510c --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/PALETTE.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMPALETTE_H +#define VQMPALETTE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Palette.h (32-Bit protected mode) +* +* DESCRIPTION +* Palette definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +void __cdecl ReadPalette(void *palette); +void __cdecl SetDAC(long color, long red, long green, long blue); +void __cdecl TranslatePalette(void *pal24, void *pal15, long numbytes); + +#ifdef __cplusplus +} +#endif + +void SortPalette(unsigned char *pal, long numcolors); + +#endif /* VQMPALETTE_H */ + diff --git a/WINVQ/INCLUDE/VQM32/PORTIO.H b/WINVQ/INCLUDE/VQM32/PORTIO.H new file mode 100644 index 0000000..2566613 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/PORTIO.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +#ifndef VQMPORTIO_H +#define VQMPORTIO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Portio.h (32-Bit protected mode) +* +* DESCRIPTION +* Hardware port I/O +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifdef __BORLANDC__ + +#ifdef __cplusplus +extern "C" { +#endif + +short __cdecl inp(unsigned short portid); +void __cdecl outp(unsigned short portid, short value); + +#ifdef __cplusplus +} +#endif + +#endif /* __BORLANDC__ */ + +#endif /* VQMPORTIO_H */ + diff --git a/WINVQ/INCLUDE/VQM32/PROFILE.H b/WINVQ/INCLUDE/VQM32/PROFILE.H new file mode 100644 index 0000000..d4641bd --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/PROFILE.H @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +#ifndef VQMPROFILE_H +#define VQMPROFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Profile.h (32-Bit protected mode) +* +* DESCRIPTION +* INI file profiling definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ +long GetINIInt(char const *section, char const *entry, + long deflt, char *fname); + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname); + +long Get_Frame_Pathname(char *inifile,long anim_frame,char *ext, + char *outbuf); + +#endif /* VQMPROFILE_H */ + diff --git a/WINVQ/INCLUDE/VQM32/REALMODE.H b/WINVQ/INCLUDE/VQM32/REALMODE.H new file mode 100644 index 0000000..593151b --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/REALMODE.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +#ifndef VQMREALMODE_H +#define VQMREALMODE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* realmode.h +* +* DESCRIPTION +* Real-mode interfacing definitions and equates. Many of the definitions +* and descriptions in this file were taken from other sources and +* compiled here for use in MISC32 library. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 6, 1995 +* +****************************************************************************/ + +/* REALPTR: Real-mode pointer (segment:offset16). + * + * The REALPTR data type is used in protected mode to hold real-mode + * pointers. The type is an unsigned long value, were the upper 16 bits + * are the segment number and the lower 16 bit are an offset. This type + * and the associated macros are identical to that of the PHARLAP "pltypes.h" + * definitions for easy of conversion to WATCOM/4GW. + */ +typedef unsigned long REALPTR; + +#define RP_OFF(rp) ((unsigned short)(((unsigned long)(rp)) & 0xFFFF)) +#define RP_SEG(rp) ((unsigned short)(((unsigned long)(rp)) >> 16)) +#define RP_SET(rp, off, seg) (rp = ((unsigned long)(seg) << 16) + (off)) +#define RP_INCR(rp, incr) (rp += ((unsigned long)(incr)) & 0xFFFF) + +#define MK_PTR(off, seg) (void *)((((unsigned long)seg&0xFFFF)<<4)+off) + +/* RMInfo: Real-mode interrupt call structure. + * + * Information that needs to be passed down to the real-mode interrupt is + * transfered using this structure. The address to this protected-mode + * structure (allocated by user) is passed into DPMI function 0x300. DOS/4GW + * will then use this information to set up the real-mode registers, switch + * to real-mode and then execute the interrupt in real-mode. + */ +typedef struct _RMInfo { + long edi; + long esi; + long ebp; + long reservedbysystem; + long ebx; + long edx; + long ecx; + long eax; + short flags; + short es,ds,fs,gs,ip,cs,sp,ss; +} RMInfo; + +#endif /* VQMREALMODE_H */ + diff --git a/WINVQ/INCLUDE/VQM32/SOSCOMP.H b/WINVQ/INCLUDE/VQM32/SOSCOMP.H new file mode 100644 index 0000000..570e76a --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/SOSCOMP.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + short wBitSize; + short wChannels; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl VQA_sosCODECInitStream(_SOS_COMPRESS_INFO *); +unsigned long __cdecl VQA_sosCODECCompressData(_SOS_COMPRESS_INFO *,unsigned long); +unsigned long __cdecl VQA_sosCODECDecompressData(_SOS_COMPRESS_INFO *,unsigned long); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/WINVQ/INCLUDE/VQM32/TARGA.H b/WINVQ/INCLUDE/VQM32/TARGA.H new file mode 100644 index 0000000..25bafc9 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/TARGA.H @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +#ifndef VQMTARGA_H +#define VQMTARGA_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Targa.h (32-Bit protected mode) +* +* DESCRIPTION +* Targa Image File definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * Targa Header definitions + *-------------------------------------------------------------------------*/ + +/* TGAHeader - Targa Image File header. + * + * IDLength - Size of Image ID field + * ColorMapType - Color map type. + * ImageType - Image type code. + * CMapStart - Color map origin. + * CMapLength - Color map length. + * CMapDepth - Depth of color map entries. + * XOffset - X origin of image. + * YOffset - Y origin of image. + * Width - Width of image. + * Height - Height of image. + * PixelDepth - Image pixel size + * ImageDescriptor - Image descriptor byte. + */ +typedef struct _TGAHeader { + char IDLength; + char ColorMapType; + char ImageType; + short CMapStart; + short CMapLength; + char CMapDepth; + short XOffset; + short YOffset; + short Width; + short Height; + char PixelDepth; + char ImageDescriptor; +} TGAHeader; + +/* ImageType definiton */ +#define TGA_NOIMAGE 0 /* No image data included in file */ +#define TGA_CMAPPED 1 /* Color-mapped image data */ +#define TGA_TRUECOLOR 2 /* Truecolor image data */ +#define TGA_MONO 3 /* Monochrome image data */ +#define TGA_CMAPPED_ENCODED 9 /* Color-mapped image data (Encoded) */ +#define TGA_TRUECOLOR_ENCODED 10 /* Truecolor image data (Encoded) */ +#define TGA_MONO_ENCODED 11 /* Monochrome image data (Encoded) */ + +/* ImageDescriptor definition */ +#define TGAF_ATTRIB_BITS (0x0F<<0) /* Number of attribute bits per pixel */ +#define TGAF_XORIGIN (1<<4) +#define TGAF_YORIGIN (1<<5) + +/*--------------------------------------------------------------------------- + * Targa Handle definitions + *-------------------------------------------------------------------------*/ + +/* TGAHandle - Targa Image File handle. + * + * fh - File handle returned by open(). + * mode - Access mode. + * header - TGAHeader structure. + */ +typedef struct _TGAHandle { + short fh; + unsigned short mode; + TGAHeader header; +} TGAHandle; + +/* Access modes. */ +#define TGA_READMODE 0 +#define TGA_WRITEMODE 1 +#define TGA_RDWRMODE 2 + +/* Error codes */ +#define TGAERR_OPEN -1 +#define TGAERR_READ -2 +#define TGAERR_WRITE -3 +#define TGAERR_SYNTAX -4 +#define TGAERR_NOMEM -5 +#define TGAERR_NOTSUPPORTED -6 + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +TGAHandle *OpenTarga(char *, unsigned short); +void CloseTarga(TGAHandle *); +long LoadTarga(char *, char *, char *); +long SaveTarga(char *, TGAHeader *, char *, char *); +void XFlipTarga(TGAHeader *, char *); +void YFlipTarga(TGAHeader *, char *); + +#endif /* VQMTARGA_H */ + diff --git a/WINVQ/INCLUDE/VQM32/TEXT.H b/WINVQ/INCLUDE/VQM32/TEXT.H new file mode 100644 index 0000000..afaca4e --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/TEXT.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQMTEXT_H +#define VQMTEXT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* text.h +* +* DESCRIPTION +* Text printing definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 13, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl Text_Print(char *string, long x, long y, long fcol, long bcol); +void __cdecl Draw_Char(long character, long x, long y); +void __cdecl Set_Font_Palette_Range(void *palette, long start, long end); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMTEXT_H */ + diff --git a/WINVQ/INCLUDE/VQM32/VESABLIT.H b/WINVQ/INCLUDE/VQM32/VESABLIT.H new file mode 100644 index 0000000..65e9a91 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VESABLIT.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +#ifndef VQMVESABLIT_H +#define VQMVESABLIT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESABlit.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA bitblit routines. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Blit_VESA640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +void __cdecl Buf_320x200_To_VESA_320x200(unsigned char *buffer, long grain); +void __cdecl Buf_320x200_To_VESA_640x400(unsigned char *buffer, long grain); +void __cdecl Buf_320x200_To_VESA_32K(unsigned char *buffer, + unsigned char *palette, long grain); + +void __cdecl Copy_Row(char *, char *, long); +void __cdecl Copy_Word_Row(char *source, char *dest, char *palette, + long numbytes); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVESABLIT_H */ + diff --git a/WINVQ/INCLUDE/VQM32/VESAVID.H b/WINVQ/INCLUDE/VQM32/VESAVID.H new file mode 100644 index 0000000..01723dd --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VESAVID.H @@ -0,0 +1,182 @@ +/* +** 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 . +*/ + +#ifndef VQMVESAVID_H +#define VQMVESAVID_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESAVid.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifndef __WATCOMC__ +#include +#else +#include "realmode.h" +#endif + +/*--------------------------------------------------------------------------- + * VESA Video Modes + *-------------------------------------------------------------------------*/ + +#define VESA_640X400_256 0x100 +#define VESA_640X480_256 0x101 +#define VESA_800X600_16 0x102 +#define VESA_800X600_256 0x103 +#define VESA_1024X768_16 0x104 +#define VESA_1024X768_256 0x105 +#define VESA_1280X400_16 0x106 +#define VESA_1280X400_256 0x107 +#define VESA_TEXT_80X60 0x108 +#define VESA_TEXT_132X25 0x109 +#define VESA_TEXT_132X60 0x10C +#define VESA_320X200_32K_1 0x10D +#define VESA_320X200_32K_2 0x10E +#define VESA_640X480_32K 0x110 +#define VESA_640X480_65K 0x111 +#define VESA_640X480_16M 0x112 +#define VESA_800X600_32K 0x113 +#define VESA_800X600_65K 0x114 +#define VESA_1024X768_32K 0x116 +#define VESA_1024X768_65K 0x117 + +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_1024X768_65K + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* VESAInfo - General information about this VESA implementation. + * (Filled in by VESA BIOS Function 0) + * + * Signature - Will always be 'VESA' + * Version - Version # + * OEMString - OEM ID string + * Capabilities - Not defined by VESA yet + * AvailModes - List of available modes; terminated with -1 (0xffff) + * TotalMemory - ??? + * Reserved - Pads structure to 256 bytes total + */ +typedef struct _VESAInfo { + char Signature[4]; + unsigned short Version; + REALPTR OEMString; + unsigned long Capabilities; + REALPTR AvailModes; + unsigned short TotalMemory; + unsigned char Reserved[236]; +} VESAInfo; + +/* VESAModeInfo - Information about this VESA mode. + * (Filled in by VESA BIOS Function 1) + * + * Attributes - bit 0: 1 = mode is supported + * bit 1: 1 = optional info available + * bit 2: 1 = std BIOS output functions valid in this mode + * bit 3: 0 = monochrome, 1 = color + * bit 4: 0 = text mode, 1 = graphics + * WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinGranularity - smallest address boundary window can be placed upon; + * size is in KB (ie 64, 32, 4) + * WinSize - size of windows in KB (ie 64, 32) + * WinA_Segment - location of Window A in CPU space (usually 0xa000) + * WinB_Segment - location of Window B in CPU space (usually 0xb000) + * WinFunc - address of window-setting function (This is provided + * as an alternative to Int 10 for speed.) + * BytesPerScanline - # bytes per scan line + * + * Optional info (available if bit 1 of Attributes is set): + * + * XRes - X-resolution + * YRes - Y-resolution + * XCharSize - Horizontal size of char cell + * YCharSize - Vertical size of char cell + * NumPlanes - # of memory planes (???) + * BitsPerPixel - # bites per pixel + * NumBanks - # of banks (ie planes) + * MemoryModel - 00h = Text mode + * 01h = CGA mode + * 02h = Hercules + * 03h = 4 plane planar mode + * 04h = packed pixel mode (1 byte/pixel) + * 05h = non-chain 4, 256-color mode + * 06-0Fh = + * 10-FFh = OEM-specific + * BankSize - Bank size in KB + */ +typedef struct _VESAModeInfo { + unsigned short Attributes; + unsigned char WinA_Attributes; + unsigned char WinB_Attributes; + unsigned short WinGranularity; + unsigned short WinSize; + unsigned short WinA_Segment; + unsigned short WinB_Segment; + REALPTR WinFunc; + unsigned short BytesPerScanline; + unsigned short XRes; + unsigned short YRes; + unsigned char XCharSize; + unsigned char YCharSize; + unsigned char NumPlanes; + unsigned char BitsPerPixel; + unsigned char NumBanks; + unsigned char MemoryModel; + unsigned char BankSize; + unsigned char NumInputPages; + unsigned char Reserved; + unsigned char RedMaskSize; + unsigned char RedFieldPosition; + unsigned char GreenMaskSize; + unsigned char GreenFieldPosition; + unsigned char BlueMaskSize; + unsigned char BlueFieldPosition; + unsigned char RsvdMaskSize; + unsigned char RsvdFieldPosition; + unsigned char DirectColorModeInfo; + unsigned char pad[216]; +} VESAModeInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +long InitVESA(void); +void UninitVESA(void); +VESAModeInfo *SetVESAMode(long mode); +VESAModeInfo *ReadVESAModeInfo(long mode); +void SetVESAWindow(long grain_num); + +#endif /* VQMVESAVID_H */ + diff --git a/WINVQ/INCLUDE/VQM32/VESAVID.I b/WINVQ/INCLUDE/VQM32/VESAVID.I new file mode 100644 index 0000000..f305e09 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VESAVID.I @@ -0,0 +1,203 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* vesavid.i +;* +;* DESCRIPTION +;* VESA video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VESA video modes +;---------------------------------------------------------------------------- + +VESA_640X400_256 EQU 0x100 +VESA_640X480_256 EQU 0x101 +VESA_800X600_16 EQU 0x102 +VESA_800X600_256 EQU 0x103 +VESA_1024X768_16 EQU 0x104 +VESA_1024X768_256 EQU 0x105 +VESA_1280X400_16 EQU 0x106 +VESA_1280X400_256 EQU 0x107 +VESA_TEXT_80X60 EQU 0x108 +VESA_TEXT_132X25 EQU 0x109 +VESA_TEXT_132X60 EQU 0x10C +VESA_320X200_32K_1 EQU 0x10D +VESA_320X200_32K_2 EQU 0x10E +VESA_640X480_32K EQU 0x110 +VESA_640X480_65K EQU 0x111 +VESA_640X480_16M EQU 0x112 +VESA_800X600_32K EQU 0x113 +VESA_800X600_65K EQU 0x114 +VESA_1024X768_32K EQU 0x116 +VESA_1024X768_65K EQU 0x117 + +VESA_MIN EQU VESA_640X400_256 +VESA_MAX EQU VESA_1024X768_65K + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; VESAInfo - General information about this VESA implementation. +; (Filled in by VESA BIOS Function 0) +; +; Signature - Will always be 'VESA' +; Ver - Version # +; OEMString - OEM ID string +; Capabilities - Not defined by VESA yet +; AvailModes - List of available modes; terminated with -1 (0xffff) +; TotalMemory - ??? +; Reserved - Pads structure to 256 bytes total + + STRUC VESAInfo +Signature DD ? +Ver DW ? +OEMString DD ? +Capabilities DD ? +AvailModes DD ? +TotalMemory DW ? +Reserved DB 236 DUP (?) + ENDS VESAInfo + +; VESAModeInfo - Information about this VESA mode. +; (Filled in by VESA BIOS Function 1) +; +; Attributes - bit 0: 1 = mode is supported +; bit 1: 1 = optional info available +; bit 2: 1 = std BIOS output functions valid in this mode +; bit 3: 0 = monochrome, 1 = color +; bit 4: 0 = text mode, 1 = graphics +; WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinGranularity - smallest address boundary window can be placed upon; +; size is in KB (ie 64, 32, 4) +; WinSize - size of windows in KB (ie 64, 32) +; WinA_Segment - location of Window A in CPU space (usually 0xa000) +; WinB_Segment - location of Window B in CPU space (usually 0xb000) +; WinFunc - address of window-setting function (This is provided +; as an alternative to Int 10 for speed.) +; BytesPerScanline - # bytes per scan line +; +; Optional info (available if bit 1 of Attributes is set): +; +; XRes - X-resolution +; YRes - Y-resolution +; XCharSize - Horizontal size of char cell +; YCharSize - Vertical size of char cell +; NumPlanes - # of memory planes (???) +; BitsPerPixel - # bites per pixel +; NumBanks - # of banks (ie planes) +; MemoryModel - 00h = Text mode +; 01h = CGA mode +; 02h = Hercules +; 03h = 4 plane planar mode +; 04h = packed pixel mode (1 byte/pixel) +; 05h = non-chain 4, 256-color mode +; 06-0Fh = +; 10-FFh = OEM-specific +; BankSize - Bank size in KB + + STRUC VESAModeInfo +Attributes DW ? +WinA_Attributes DB ? +WinB_Attributes DB ? +WinGranularity DW ? +WinSize DW ? +WinA_Segment DW ? +WinB_Segment DW ? +WinFunc DD ? +BytesPerScanline DW ? +XRes DW ? +YRes DW ? +XCharSize DB ? +YCharSize DB ? +NumPlanes DB ? +BitsPerPixel DB ? +NumBanks DB ? +MemoryModel DB ? +BankSize DB ? +NumInputPages DB ? +Reserved DB ? +RedMaskSize DB ? +RedFieldPosition DB ? +GreenMaskSize DB ? +GreenFieldPosition DB ? +BlueMaskSize DB ? +BlueFieldPosition DB ? +RsvdMaskSize DB ? +RsvdFieldPosition DB ? +DirectColorModeInfo DB ? +pad DB 216 DUP (?) + ENDS VESAModeInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C InitVESA:PROC + GLOBAL C UninitVESA:PROC + GLOBAL C SetVESAMode:PROC + GLOBAL C ReadVESAModeInfo:PROC + GLOBAL C SetVESAWindow:PROC + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WINDOW - Sets window A to the specified bank. +; +; SYNOPSIS +; SET_WINDOW GrainNum +; +; FUNCTION +; This routine uses the C Set_Window function rather than going through +; the BIOS, for two reasons: (1) Speed, and (2) On the Cirrus Logic 54xx +; VESA cards, BIOS calls make noise while playing digital audio. +; +; INPUTS +; GrainNum - Granularity number. +; +; RESULT +; NONE +; +;---------------------------------------------------------------------------- + + MACRO SET_WINDOW grain_num + push esi + push edi + push es + call SetVESAWindow C,grain_num + pop es + pop edi + pop esi + ENDM + diff --git a/WINVQ/INCLUDE/VQM32/VGA.I b/WINVQ/INCLUDE/VQM32/VGA.I new file mode 100644 index 0000000..f04d9a0 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VGA.I @@ -0,0 +1,217 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vga.i +;* +;* DESCRIPTION +;* VGA hardware definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VGA Registers +;---------------------------------------------------------------------------- + +R_SEQUENCER EQU 03C4h ;Sequencer Controller Index reg +SEQ_RESET EQU 00h ;Reset +SEQ_MAP_MASK EQU 02h ;Index in Sequencer of Map Mask reg +SEQ_MEMORY_MODE EQU 04h ;Memory Mode + +R_GRAPHICS_CONTROLLER EQU 03CEh ;Graphics Controller Index reg +GC_READ_MAP EQU 04h ;Index in GController of Read Map reg +GC_MODE EQU 05h ;Read/Write Modes +GC_MISC EQU 06h ;Read/Write Modes +GC_BITMASK EQU 08h ;Index in GController of BitMask reg + +R_CRT_CONTROLLER EQU 03D4h ;CRT Controller Index reg +CRT_VERT_TOTAL EQU 06h ;Vertical total +CRT_OVERFLOW EQU 07h ;Overflow +CRT_MAX_SCANLINE EQU 09h ;Max scan line +CRT_STARTADDR_HIGH EQU 0Ch ;Bitmap start address high byte +CRT_STARTADDR_LOW EQU 0Dh ;Bitmap start address low byte +CRT_VERTRET_START EQU 010h ;Vertical retrace pulse start +CRT_VERTRET_END EQU 011h ;Vertical retrace pulse end +CRT_VERTDISP_END EQU 012h ;Vertical display end +CRT_UNDERLINE EQU 014h ;Underline location +CRT_START_VB EQU 015h ;Start vertical blank +CRT_END_VB EQU 016h ;End vertical blank +CRT_MODE_CONTROL EQU 017h ;Mode control +R_MISC_OUTPUT EQU 03C2h ;Miscellaneous Output reg + +;---------------------------------------------------------------------------- +; Palette Registers +;---------------------------------------------------------------------------- + +PEL_READ_ADDR EQU 03C7h +PEL_WRITE_ADDR EQU 03C8h +PEL_DATA EQU 03C9h + +;---------------------------------------------------------------------------- +; XMode planes, for the Map Mask register +;---------------------------------------------------------------------------- + +XPLANE_1 EQU 1 +XPLANE_2 EQU 2 +XPLANE_3 EQU 4 +XPLANE_4 EQU 8 + +;---------------------------------------------------------------------------- +; +; NAME +; SET_PLANE - Set an XMode plane. +; +; SYNOPSIS +; SET_PLANE plane +; +; INPUTS +; plane - Number of Xmode plane to set. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_PLANE plane + mov edx,R_SEQUENCER + mov eax,SEQ_MAP_MASK + out dx,al + inc edx + mov eax,plane + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_BITMASK - Set the BitMask register. +; +; SYNOPSIS +; SET_BITMASK mask +; +; INPUTS +; mask - Bitmask to use. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_BITMASK mask + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_BITMASK + out dx,al + inc edx + mov eax,mask + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WRITEMODE - Set the VGA writemode. +; +; SYNOPSIS +; SET_WRITEMODE mode +; +; INPUTS +; mode - Write mode. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_WRITEMODE mode + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_MODE + out dx,al + inc edx + in al,dx ;Read the register + and al,0FCh ;Turn off 2 lower bits + or al,mode ;Set write mode + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; OUTPORT - Output data to a VGA register. +; +; SYNOPSIS +; OUTPORT port,register,data +; +; INPUTS +; port - Port address. +; register - Register to write to. +; data - Data to write. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO OUTPORT port,register,data + mov edx,port + mov al,register + out dx,al + inc edx + mov al,data + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; INPORT - Input data from a VGA register. +; +; SYNOPSIS +; data = INPORT port,register +; +; INPUTS +; port - Port address. +; register - Register to read from. +; +; RESULT +; data - Value read from port in AL. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO INPORT port,register + mov edx,port + mov al,register + out dx,al + inc edx + in al,dx + ENDM + diff --git a/WINVQ/INCLUDE/VQM32/VIDEO.H b/WINVQ/INCLUDE/VQM32/VIDEO.H new file mode 100644 index 0000000..326e25c --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VIDEO.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +#ifndef VQMVIDEO_H +#define VQMVIDEO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Video.h (32-Bit protected mode) +* +* DESCRIPTION +* Video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * VGA video modes + *-------------------------------------------------------------------------*/ + +#define TEXT_VIDEO 0x02 +#define MCGA 0x13 +#define XMODE_320X200 0x50 +#define XMODE_320X240 0x51 +#define XMODE_320X400 0x52 +#define XMODE_320X480 0x53 +#define XMODE_360X400 0x54 +#define XMODE_360X480 0x55 + +#define XMODE_MIN 0x50 +#define XMODE_MAX 0x55 + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* DisplayInfo - Information about the current display. + * + * Mode - Mode identification. + * XRes - X resolution of mode. + * YRes - Y resolution of mode. + * VBIbit - Polarity of vertical blank bit. + * Extended - Pointer to mode specific data structure. + */ +typedef struct _DisplayInfo { + long Mode; + long XRes; + long YRes; + long VBIbit; + void *Extended; +} DisplayInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +DisplayInfo *SetVideoMode(long mode); +DisplayInfo *GetDisplayInfo(void); +long TestVBIBit(void); +long GetVBIBit(void); + +void SetupXPaging(void); +void FlipXPage(void); +unsigned char *GetXHidPage(void); +unsigned char *GetXSeenPage(void); +void DisplayXPage(long page); + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl WaitNoVB(short vbibit); +void __cdecl WaitVB(short vbibit); +void __cdecl ClearVRAM(void); +long __cdecl SetXMode(long mode); +void __cdecl ClearXMode(void); +void __cdecl ShowXPage(unsigned long StartOffset); +void __cdecl Xmode_BufferCopy_320x200(void *buff, void *screen); +void __cdecl Xmode_Blit(void *buffer, void *screen, long imgwidth, long imgheight); +void __cdecl MCGA_BufferCopy(unsigned char *buffer, unsigned char *dummy); +void __cdecl MCGA_Blit(unsigned char *buffer, unsigned char *screen, + long imgwidth, long imgheight); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVIDEO_H */ + diff --git a/WINVQ/INCLUDE/VQM32/VIDEO.I b/WINVQ/INCLUDE/VQM32/VIDEO.I new file mode 100644 index 0000000..d2fc384 --- /dev/null +++ b/WINVQ/INCLUDE/VQM32/VIDEO.I @@ -0,0 +1,80 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* video.i +;* +;* DESCRIPTION +;* Video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + + INCLUDE "vesavid.i" + +;---------------------------------------------------------------------------- +; Video Modes +;---------------------------------------------------------------------------- + +TEXT EQU 002h +MCGA EQU 013h +XMODE_320X200 EQU 050h +XMODE_320X240 EQU 051h +XMODE_320X400 EQU 052h +XMODE_320X480 EQU 053h +XMODE_360X400 EQU 054h +XMODE_360X480 EQU 055h + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; DisplayInfo - Information about the current display. +; +; Mode - Mode identification +; XRes - X resolution +; YRes - Y resolution +; VBIbit - Polarity of vertical blank bit. +; Extended - Pointer to mode specified data structure. + + STRUC DisplayInfo +Mode DD ? +XRes DD ? +YRes DD ? +VBIbit DD ? +Extended DD ? + ENDS DisplayInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C GetDisplayInfo:NEAR + GLOBAL C GetVBIBit:NEAR + diff --git a/WINVQ/INCLUDE/WAVEFILE.H b/WINVQ/INCLUDE/WAVEFILE.H new file mode 100644 index 0000000..aad2eb9 --- /dev/null +++ b/WINVQ/INCLUDE/WAVEFILE.H @@ -0,0 +1,73 @@ +/* +** 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 . +*/ + +#ifndef WAVEFILE_H +#define WAVEFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* wavefile.c +* +* DESCRIPTION +* WAVE file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* February 21, 1995 +* +****************************************************************************/ + +/* WAVHeader: WAVE file header. + * + * RIFF - 4 byte identifier (always "RIFF"). + * Size - Size in bytes of entire file. + * WAVE - 4 byte identifier (always "WAVE"). + */ +typedef struct _WAVHeader { + long RIFF; + long Size; + long WAVE; +} WAVHeader; + +/* WAVFormat: This structure describes the format of the audio data contained + * in the WAV file. + * + * FormatTag - + * Channels - Number of channels (1 = mono, 2 = stereo). + * SamplesPerSec - Sampling rate. + * AvgBytesPerSec - Bytes in 1 second of audio (Rate * SampleSize * Channels) + * BlockAlign - padding. + * BitsPerSample - Number of bits per sample (8, 16). + */ +typedef struct _WAVFormat { + short FormatTag; + short Channels; + long SamplesPerSec; + long AvgBytesPerSec; + short BlockAlign; + short BitsPerSample; +} WAVFormat; + +#endif /* WAVEFILE_H */ + diff --git a/WINVQ/INCLUDE/WWTYPES.H b/WINVQ/INCLUDE/WWTYPES.H new file mode 100644 index 0000000..ef92aa7 --- /dev/null +++ b/WINVQ/INCLUDE/WWTYPES.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +#ifndef WWTYPES_H +#define WWTYPES_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILENAME +* wwtypes.h +* +* DESCRIPTION +* Westwood standard types definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +****************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +#define VOID void +#define PRIVATE static +#define PUBLIC + +#define BOOL short +#define BYTE char +#define UBYTE unsigned char +#define WORD signed short +#define UWORD unsigned short +#define LONG signed long +#define ULONG unsigned long + +#endif /* WWTYPES_H */ + diff --git a/WINVQ/LIB/README.TXT b/WINVQ/LIB/README.TXT new file mode 100644 index 0000000..d739995 --- /dev/null +++ b/WINVQ/LIB/README.TXT @@ -0,0 +1,10 @@ +Library naming convention. + + nnnssctm.lib + + Where: + nnn = ID (such as 'VQA') 3 characters + ss = Size field (32bit / 16bit) 2 characters + c = Compiler name (W = Watcom, B = Borland) + t = Compiler type (P = "C++", C = Standard "C") + m = Memory model (F = flat, T = TNT) diff --git a/WINVQ/LIB/SOSDBLC.LIB b/WINVQ/LIB/SOSDBLC.LIB new file mode 100644 index 0000000..2a633ff Binary files /dev/null and b/WINVQ/LIB/SOSDBLC.LIB differ diff --git a/WINVQ/LIB/SOSDW1CR.LIB b/WINVQ/LIB/SOSDW1CR.LIB new file mode 100644 index 0000000..4a1bb9c Binary files /dev/null and b/WINVQ/LIB/SOSDW1CR.LIB differ diff --git a/WINVQ/LIB/SOSDW1CS.LIB b/WINVQ/LIB/SOSDW1CS.LIB new file mode 100644 index 0000000..290e89a Binary files /dev/null and b/WINVQ/LIB/SOSDW1CS.LIB differ diff --git a/WINVQ/LIB/SOSDW1PR.LIB b/WINVQ/LIB/SOSDW1PR.LIB new file mode 100644 index 0000000..c617ea1 Binary files /dev/null and b/WINVQ/LIB/SOSDW1PR.LIB differ diff --git a/WINVQ/LIB/SOSDW1PS.LIB b/WINVQ/LIB/SOSDW1PS.LIB new file mode 100644 index 0000000..775054e Binary files /dev/null and b/WINVQ/LIB/SOSDW1PS.LIB differ diff --git a/WINVQ/LIB/SOSMBLC.LIB b/WINVQ/LIB/SOSMBLC.LIB new file mode 100644 index 0000000..f18185b Binary files /dev/null and b/WINVQ/LIB/SOSMBLC.LIB differ diff --git a/WINVQ/VPLAY32/BCC32.CFG b/WINVQ/VPLAY32/BCC32.CFG new file mode 100644 index 0000000..6cf02a2 --- /dev/null +++ b/WINVQ/VPLAY32/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vplay32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/WINVQ/VPLAY32/CAPTION.FNT b/WINVQ/VPLAY32/CAPTION.FNT new file mode 100644 index 0000000..6029e17 Binary files /dev/null and b/WINVQ/VPLAY32/CAPTION.FNT differ diff --git a/WINVQ/VPLAY32/EVA.FNT b/WINVQ/VPLAY32/EVA.FNT new file mode 100644 index 0000000..d9555ce Binary files /dev/null and b/WINVQ/VPLAY32/EVA.FNT differ diff --git a/WINVQ/VPLAY32/MAKEFILE b/WINVQ/VPLAY32/MAKEFILE new file mode 100644 index 0000000..0ae6e86 --- /dev/null +++ b/WINVQ/VPLAY32/MAKEFILE @@ -0,0 +1,165 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# +#---------------------------------------------------------------------------- +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for test program +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECT DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = VPLAY4GW +PRJDIR = $(%PRJ)\VPLAY32 + +OBJECTS = & + plyvqa32.obj + +#LIBRARYS = vqm32w.lib vqa32w.lib sosdw1cs.lib +LIBRARYS = vqm32wp.lib vqa32wp.lib sosdw1ps.lib + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq -d2 +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q /t +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(LINK_CFG) $(PRJNAME).exe .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + +.asm.obj : + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + +#---------------------------------------------------------------------------- +# BUILD THE APPLICATION +#---------------------------------------------------------------------------- + +$(PRJNAME).exe : $(OBJECTS) $(LIBRARYS) $(LINK_CFG) + $(LINK_CMD) @$(LINK_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG) : makefile + @echo Updating $(LINK_CFG)! + @%create $(LINK_CFG) + @%write $(LINK_CFG) SYSTEM dos4g + @%write $(LINK_CFG) OPTION map + @%write $(LINK_CFG) NAME $(PRJNAME) + @%write $(LINK_CFG) DEBUG ALL + @%write $(LINK_CFG) LIBPATH $(LIBPATH) + @for %i in ($(OBJECTS)) do @%write $(LINK_CFG) FILE $(path_obj)\%i + @for %i in ($(LIBRARYS)) do @%write $(LINK_CFG) LIBRARY %i + diff --git a/WINVQ/VPLAY32/MAKEFILE.BOR b/WINVQ/VPLAY32/MAKEFILE.BOR new file mode 100644 index 0000000..5353c68 --- /dev/null +++ b/WINVQ/VPLAY32/MAKEFILE.BOR @@ -0,0 +1,217 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VPlay32 - VQA player (32-bit protected mode) +# +# FILE +# Makefile +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 9, 1995 +# +# ENVIROMENT +# PRJ = Root projects path +# PRJVCS = Root path for projects version control archive +# BCDIR = Root Borland installation path +# TNTDIR = Root Pharlap TNT-DOS Extender path +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# PROJECT DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vplaytnt +PRJDIR = $(PRJ)\VPLAY32 + +OBJECTS = \ + obj\plyvqa32.obj \ + +PRJLIBS = \ + vqa32bp.lib \ + vqm32bp.lib \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.def = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.map = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib +TNT_CMD = cfig386 + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) +TNT_FLAGS = -minreal 256 + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).exe + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD THE APPLICATION +#---------------------------------------------------------------------------- + +$(PRJNAME).exe: $(OBJECTS) $(PRJLIBS) $(PRJNAME).def + $(LINK_CMD) @&&| +c0x32.obj+ +$(OBJECTS) +$(.path.exe)\$(PRJNAME).exe +$(.path.map)\$(PRJNAME).map +$(PRJLIBS)+ +dos32.lib+ +dosx32.lib+ +errno.lib+ +import32.lib+ +cw32.lib +$(.path.def)\$(PRJNAME).def +| + + $(TNT_CMD) $(PRJNAME) $(TNT_FLAGS) + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/la +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE +/zi +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + + diff --git a/WINVQ/VPLAY32/MAKEFILE.WAT b/WINVQ/VPLAY32/MAKEFILE.WAT new file mode 100644 index 0000000..45c4daa --- /dev/null +++ b/WINVQ/VPLAY32/MAKEFILE.WAT @@ -0,0 +1,165 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# +#---------------------------------------------------------------------------- +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for test program +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECT DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = VPLAY4GW +PRJDIR = $(%PRJ)\VPLAY32 + +OBJECTS = & + plyvqa32.obj + +#LIBRARYS = vqm32w.lib vqa32w.lib sosdw1cs.lib +LIBRARYS = vqm32wp.lib vqa32wp.lib sosdw1ps.lib + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q /t +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(LINK_CFG) $(PRJNAME).exe .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + +.asm.obj : + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + +#---------------------------------------------------------------------------- +# BUILD THE APPLICATION +#---------------------------------------------------------------------------- + +$(PRJNAME).exe : $(OBJECTS) $(LIBRARYS) $(LINK_CFG) + $(LINK_CMD) @$(LINK_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG) : makefile + @echo Updating $(LINK_CFG)! + @%create $(LINK_CFG) + @%write $(LINK_CFG) SYSTEM dos4g + @%write $(LINK_CFG) OPTION map + @%write $(LINK_CFG) NAME $(PRJNAME) + @%write $(LINK_CFG) DEBUG ALL + @%write $(LINK_CFG) LIBPATH $(LIBPATH) + @for %i in ($(OBJECTS)) do @%write $(LINK_CFG) FILE $(path_obj)\%i + @for %i in ($(LIBRARYS)) do @%write $(LINK_CFG) LIBRARY %i + diff --git a/WINVQ/VPLAY32/PLYVQA32.CPP b/WINVQ/VPLAY32/PLYVQA32.CPP new file mode 100644 index 0000000..5f141c6 --- /dev/null +++ b/WINVQ/VPLAY32/PLYVQA32.CPP @@ -0,0 +1,1019 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA stand-alone player. (32Bit protected mode) +* +* FILE +* Plyvqa32.c +* +* DESCRIPTION +* This program is a stand-alone VQA Player, as well as an example of how +* to incorporate VQAPlay into a program. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 7, 1995 +* +*---------------------------------------------------------------------------- +* +* FUNCTIONS +* main - Standard 'C' entry point. +* Usage - Display usage information. +* Options - Parse user options. +* Find_File_Name - Find a filename on the command line. +* Print_Play_Stats - Print player statistics. +* Check_Key - Check keyboard for keypress. +* Get_Key - Get a key from the keyboard buffer. +* HardErr_Handler - Hardware error handle. +* +****************************************************************************/ + +#define CAPTIONS 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if(CAPTIONS) +#include +#endif + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +long OutputStats = 0; +long OutputTime = 0; +long LastKey; + +/* Embedded version string */ +char VerTag[] = {"$VER$VQAPlay 2.6 (07/07/95) 32Bit"}; +char ReqTag[] = {"$REQ$VQA32 Version 2.37 & VQM32 Version 2.11 or better."}; + +/* Prototypes */ +void main(long argc, char **argv); +static void Usage(long showall); +static void Options(long argc, char *argv[], VQAConfig *config); +static char *Find_File_Name(long argc, char *argv[], char *desired_ext); +static char *GetFilePart(char *path); +void Print_Play_Stats(VQAConfig *config, VQAStatistics *stats); +long VQCallback(unsigned char *screen, long framenum); + +#ifdef __cplusplus +extern "C" { +#endif + +int __cdecl Check_Key(void); +int __cdecl Get_Key(void); + +#ifdef __cplusplus +} +#endif + +#ifndef __WATCOMC__ +#if(0) +int HardErr_Handler(int errval, int ax, int bp, int si); +#endif +#else +int __far HardErr_Handler(unsigned deverror, unsigned errcode, + unsigned __far *devhdr); +#endif + + +/**************************************************************************** +* +* NAME +* main - Standard 'C' entry point. +* +* SYNOPSIS +* main(ArgC, ArgV) +* +* void main(short, char *[]); +* +* FUNCTION +* Initial 'C' user-routine called by startup code. +* +* INPUTS +* ArgC - Argument count, number of arguments passed in. +* ArgV - Argument array, pointers to arguments. +* +* RESULT +* NONE +* +****************************************************************************/ + +void main(long argc, char **argv) +{ + VQAConfig myconfig; + VQAStatistics stats; + VQAHandle *vqa; + char *name; + long i; + + /* Parse command-line */ + if (argc < 2) { + Usage(1); + exit(0); + } + + /* Get filename */ + name = Find_File_Name(argc, argv, ".VQA"); + + if (name == NULL) { + Usage(1); + exit(0); + } + + /*------------------------------------------------------------------------- + * INITIALIZE PLAYBACK CONFIGURATION + *-----------------------------------------------------------------------*/ + VQA_DefaultConfig(&myconfig); + myconfig.HMIBufSize = 2048*2; + + #if(CAPTIONS) + myconfig.CapFont = (char *)Load_Font("caption.fnt"); + myconfig.OptionFlags |= VQAOPTF_CAPTIONS; + Set_Font(myconfig.CapFont); + SetDAC(251,255,255,255); // WHITE + SetDAC(252,255,000,000); // RED + SetDAC(253,000,255,000); // GREEN + SetDAC(254,0,0,0); + SetDAC(255,255,000,255); // CYCLE + #endif + + /* Configure player with command-line */ + Options(argc, argv, &myconfig); + + /*------------------------------------------------------------------------- + * INSTALL THE CUSTOM CRITICAL ERROR HANDLER + *-----------------------------------------------------------------------*/ + #ifndef __WATCOMC__ + #if(0) + harderr(HardErr_Handler); + #endif + #else + _harderr(HardErr_Handler); + #endif + + /*------------------------------------------------------------------------- + * SET THE VIDEO MODE AND VBI POLARITY BIT + *-----------------------------------------------------------------------*/ + SetVideoMode(myconfig.Vmode); + myconfig.VBIBit = GetVBIBit(); + + /*------------------------------------------------------------------------- + * PLAY THE MOVIE + *-----------------------------------------------------------------------*/ + + /* Allocate a VQA handle. */ + if ((vqa = VQA_Alloc()) != NULL) { + + /* Initialize the handle as a standard DOS handle. */ + VQA_InitAsDOS(vqa); + + /* Open the movie for playback. */ + if (VQA_Open(vqa, name, &myconfig) == 0) { + + #if(1) + VQA_Reset(vqa); + VQA_SetStop(vqa, 100); + VQA_SeekFrame(vqa, 50, 0); + + for (i = 0; i < 3; i++) { + /* Actually set the movie off. */ + VQA_Play(vqa, VQAMODE_RUN); + VQA_Reset(vqa); + VQA_SeekFrame(vqa, 50, 0); + } + #else + { + long done = 0; + long mode = VQAMODE_WALK; + long key; + + do { + switch (VQA_Play(vqa, mode)) { + case VQAERR_NONE: + case VQAERR_PAUSED: + case VQAERR_NOT_TIME: + case VQAERR_SLEEPING: + key = Check_Key(); + + if (key != 0) { + switch (key) { + case ' ': + mode = VQAMODE_PAUSE; + break; + + case 0x1B: + done = 1; + break; + + default: + mode = VQAMODE_WALK; + break; + } + } + break; + + default: + done = 1; + break; + } + } while (done == 0); + } + #endif + + /* Retrieve playback statistics (FPS, time, etc...) */ + VQA_GetStats(vqa, &stats); + + /* Close the movie. */ + VQA_Close(vqa); + + } + + /* Free the VQA handle. */ + VQA_Free(vqa); + } + + /*------------------------------------------------------------------------- + * RESTORE DISPLAY TO TEXT AND PRINT STATISTICS + *-----------------------------------------------------------------------*/ + SetVideoMode(TEXT); + + #if(CAPTIONS) + if (myconfig.CapFont != NULL) free(myconfig.CapFont); + #endif + + Usage(0); + + /* Print play statistics */ + if (OutputStats) { + printf("Movie Name: %s\n",strupr(name)); + Print_Play_Stats(&myconfig, &stats); + } + + exit(0); +} + + +/**************************************************************************** +* +* NAME +* Usage - Display usage information. +* +* SYNOPSIS +* Usage() +* +* void Usage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Usage(long showall) +{ + printf("\n%s Copyright (c) 1995 Westwood Studios.\n", &VerTag[5]); + printf("Playback library: %s\n", VQA_IDString()); + puts("Written by Denzil E. Long, Jr.\n"); + + if (showall) { + puts("Usage: VPLAY [options]"); + + puts(" -z: Single-step"); + puts(" -d: No drawing"); + puts(" -fn: Draw at 'n' frames per second"); + puts(" -ln: Load at 'n' frames per second"); + puts(" -b: Use buffered video"); + puts(" -o: Output play statistics"); + + puts(" -p: Enable slow palette setting"); + puts(" -w: Enable woofer drawing."); + + #if(VQAMONO_ON) + puts(" -m: Enable mono screen output"); + #endif + + #if(VQAAUDIO_ON) + puts(" -a: Audio playback rate"); + puts(" -ac: Compatibility mode (Force SoundBlaster)."); + puts(" -alt: Play alternate audio track."); + puts(" -s: Disable sound"); + + #if(VQAVOC_ON) + puts(" -cname: Name of VOC file to play instead of interleaved audio"); + #endif + #endif + + puts(" -t_: Timer method:"); + puts(" a = Audio DMA position"); + puts(" i = Interrupt"); + puts(" d = DOS"); + + puts(" -v_: Video mode:"); + + #if(VQAMCGA_ON) + puts(" m = MCGA"); + #endif + + #if(VQAXMODE_ON) + puts(" w = XMODE 320x200"); + puts(" x = XMODE 320x240"); + puts(" y = XMODE 320x200, VRAM mode"); + puts(" z = XMODE 320x240, VRAM mode"); + #endif + + #if(VQAVESA_ON) + puts(" u = VESA 320x200"); + puts(" v = VESA 640x480 in a window (buffered only)"); + puts(" s = VESA 640x480 scaled to 640x400 (buffered only)"); + #endif + } +} + + +/**************************************************************************** +* +* NAME +* Options - Parse user options. +* +* SYNOPSIS +* Options(ArgC, ArgV, Anim) +* +* void Options(long, char *[], VQAnim *); +* +* FUNCTION +* +* INPUTS +* ArgC - Argument count, same as passed to main(). +* ArgV - Argument array, same as passed to main(). +* Anim - Pointer to VQAnim structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Options(long argc, char *argv[], VQAConfig *config) +{ + long l; + long i; + + /* Scan arguments for any options ( / or - followed by a letter) */ + for (l = 0; l < argc; l++) { + if (argv[l][0] == '/' || argv[l][0] == '-') { + + /* Convert the argument to uppercase. */ + for (i = 1; argv[l][i] != 0; i++) { + argv[l][i] = toupper(argv[l][i]); + } + + switch (argv[l][1]) { + + /* Set Audio Playback rate */ + #if(VQAAUDIO_ON) + case 'A': + if (isdigit(argv[l][2])) { + config->AudioRate = atoi(&argv[l][2]); + } else { + if (argv[l][2] == 'C') { + config->DigiCard = 0xE000; + } else { + if (strcmpi(&argv[l][2], "LT") == 0) { + config->OptionFlags |= VQAOPTF_ALTAUDIO; + } + } + } + break; + #endif + + /* Turn off UnVQ to screen */ + case 'B': + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* VOC File Name */ + #if(VQAAUDIO_ON && VQAVOC_ON) + case 'C': + config->VocFile = (argv[l] + 2); + break; + #endif + + /* Turn off vertical blank wait */ + case 'D': + config->DrawFlags |= VQACFGF_NODRAW; + break; + + /* Set Drawer's frame rate */ + case 'F': + config->DrawRate = atoi(&argv[l][2]); + break; + + /* Set Loader's frame rate */ + case 'L': + config->FrameRate = atoi(&argv[l][2]); + break; + + /* Mono mode */ + #if(VQAMONO_ON) + case 'M': + config->OptionFlags |= VQAOPTF_MONO; + break; + #endif + + /* Output statistics */ + case 'O': + OutputStats = 1; + break; + + /* Slow palette mode */ + case 'P': + config->OptionFlags |= VQAOPTF_SLOWPAL; + break; + + /* Run with no sound */ + #if(VQAAUDIO_ON) + case 'S': + config->OptionFlags &= (~VQAOPTF_AUDIO); + break; + #endif + + case 'T': + switch (argv[l][2]) { + case 'A': + config->TimerMethod = VQA_TMETHOD_AUDIO; + break; + + case 'I': + config->TimerMethod = VQA_TMETHOD_INT; + break; + + case 'D': + config->TimerMethod = VQA_TMETHOD_DOS; + break; + + default: + break; + } + break; + + /* Set video mode */ + case 'V': + if (argv[l][2] == 'M') { + config->Vmode = MCGA; + } + #if(VQAXMODE_ON) + else if (argv[l][2] == 'W') { + config->Vmode = XMODE_320X200; + } + else if (argv[l][2] == 'X') { + config->Vmode = XMODE_320X240; + } + else if (argv[l][2] == 'Y') { + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_VRAMCB; + } + else if (argv[l][2] == 'Z') { + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_VRAMCB; + } + #endif + + #if(VQAVESA_ON) + else if (argv[l][2] == 'V') { + config->Vmode = VESA_640X480_256; + config->DrawFlags |= VQACFGF_BUFFER; + } + else if (argv[l][2] == 'S') { + config->Vmode = VESA_640X480_256; + config->DrawFlags |= (VQACFGF_BUFFER|VQACFGF_SCALEX2); + } + else if (argv[l][2] == 'U') { + config->Vmode = VESA_320X200_32K_1; + } + #endif + else { + printf("Unsupported video mode flag: %c\n", argv[l][2]); + exit(0); + } + break; + + #if(VQAWOOFER_ON) + case 'W': + config->DrawFlags |= VQACFGB_WOOFER; + break; + #endif + + /* Single-step */ + case 'Z': + config->OptionFlags |= VQAOPTF_STEP; + config->DrawFlags |= VQACFGF_NOSKIP; + break; + + default: + break; + } + } + } + + return; +} + + +/**************************************************************************** +* +* NAME +* Find_File_Name - Find a filename on the command line. +* +* SYNOPSIS +* Name = Fine_File_Name(ArgC, ArgV, Ext, Anim) +* +* char * Fine_File_Name(short, char *[], char *, VQAnim); +* +* FUNCTION +* Finds a file name on the command line, excluding anything with "/" or +* "-" on it. The given extension is added to the name if there is no +* extension in the filename. +* +* INPUTS +* ArgC - Argument count same as in main(). +* ArgV - Argument array same as in main(). +* Ext - Pointer to filename extension. +* Anim - Pointer to VQAnim structure. +* +* RESULT +* Name = Pointer to filename. +* +****************************************************************************/ + +static char *Find_File_Name(long argc, char *argv[], char *desired_ext) +{ + long opt = 1; + static char drive[_MAX_DRIVE] = {0}; + static char dir[_MAX_DIR] = {0}; + static char fname[_MAX_FNAME] = {0}; + static char ext[_MAX_EXT] = {0}; + static char pathname[_MAX_PATH] = {0}; + + /* Search for a non '-' option */ + while ((argv[opt][0] == '/') || (argv[opt][0] == '-')) { + opt++; + } + + if (argc == opt) { + return (NULL); + } + + /* Split the filename into its components */ + _splitpath(argv[opt],drive,dir,fname,ext); + + if (strlen(ext) == 0) { + strcpy(ext,desired_ext); + } + + /* Rebuild the complete filename */ + _makepath(pathname, drive, dir, fname, ext); + + return (pathname); +} + + +/**************************************************************************** +* +* NAME +* Print_Play_Stats - Print player statistics. +* +* SYNOPSIS +* Print_Play_Stats(Anim) +* +* void Print_Play_Stats(VQAnim); +* +* FUNCTION +* +* INPUTS +* Anim - Pointer to VQAnim structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void Print_Play_Stats(VQAConfig *config, VQAStatistics *stats) +{ + long tim1,tim2; + long fps1,fps2; + + /* Video mode name */ + printf("Video Mode: "); + + if (config->Vmode == MCGA) { + if (config->DrawFlags & VQACFGF_BUFFER) { + puts("MCGA Buffered"); + } else { + puts("MCGA"); + } + } + #if(VQAXMODE_ON) + else if (config->Vmode == XMODE_320X200) { + if (config->DrawFlags & VQACFGF_BUFFER) { + puts("XMODE 320x200 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + puts("XMODE 320x200 VRAM"); + } else { + puts("XMODE 320x200"); + } + } + } + else if (config->Vmode == XMODE_320X240) { + if (config->DrawFlags & VQACFGF_BUFFER) { + puts("XMODE 320x240 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + puts("XMODE 320x240 VRAM"); + } else { + puts("XMODE 320x240"); + } + } + } + #endif + + #if(VQAVESA_ON) + else if (config->Vmode == VESA_640X480_256) { + if (config->DrawFlags & VQACFGF_SCALEX2) { + puts("VESA 640x480 Scaled"); + } else { + puts("VESA 640x480 Windowed"); + } + } else if (config->Vmode==VESA_320X200_32K_1) { + if (config->DrawFlags & VQACFGF_BUFFER) { + puts("VESA 320x200 Buffered"); + } else { + puts("VESA 320x200"); + } + } + #endif + else { + puts("UNKNOWN"); + } + + printf("%lu bytes used.\n", stats->MemUsed); + + tim1 = ((stats->EndTime - stats->StartTime) / VQA_TIMETICKS); + tim2 = (((stats->EndTime - stats->StartTime) * 10) / VQA_TIMETICKS); + printf("Elapsed time %d.%d seconds.\n", tim1, tim2 - (tim1 * 10)); + + printf("%u frames loaded.\n", stats->FramesLoaded); + printf("%u frames drawn.\n", stats->FramesDrawn); + printf("%u frames skipped.\n", stats->FramesSkipped); + + /* Frame rates */ + fps1 = ((stats->FramesLoaded * VQA_TIMETICKS) + / (stats->EndTime - stats->StartTime)); + fps2 = (stats->FramesLoaded * VQA_TIMETICKS * 10) + / (stats->EndTime - stats->StartTime); + printf("Load rate: %d.%d FPS\n", fps1, fps2 - (fps1 * 10)); + + fps1 = (stats->FramesDrawn * VQA_TIMETICKS) + / (stats->EndTime -stats->StartTime); + fps2 = (stats->FramesDrawn * VQA_TIMETICKS * 10) + / (stats->EndTime - stats->StartTime); + printf("Draw rate: %d.%d FPS\n", fps1, fps2 - (fps1 * 10)); + + /* Audio */ + printf("Audio samples played: %lu\n", stats->SamplesPlayed); +} + + +/**************************************************************************** +* +* NAME +* Check_Key - Check keyboard for keypress. +* +* SYNOPSIS +* Key = Check_Key() +* +* short Check_Key(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* Key - Value of key pressed. +* +****************************************************************************/ + +int Check_Key(void) +{ + if (kbhit()) { + LastKey = getch(); + return (LastKey); + } else { + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* Get_Key - Get a key from the keyboard buffer. +* +* SYNOPSIS +* Key = Get_Key() +* +* short Get_Key(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* Key - Value of key. +* +****************************************************************************/ + +int Get_Key(void) +{ + return (LastKey); +} + + +#ifndef __WATCOMC__ +#if(0) +/**************************************************************************** +* +* NAME +* HardErr_Handler - Hardware error handle. +* +* SYNOPSIS +* HardErr_Handler(Error, AX, BP, SI) +* +* int HardErr_Handler(int, int, int, int); +* +* FUNCTION +* DOS calls 1 through 0xc are OK; any other will corrupt DOS. +* Important safety tip: The arguments for this function are NOT the +* same as those for the Microsoft '_harderr()' function. +* +* INPUTS +* Error - Error type value. Low order byte can be: +* +* 0 = Attempt to write to write-protected disk. +* 1 = Unknown unit. +* 2 = Drive not ready. +* 3 = Unknown command. +* 4 = CRC error. +* 5 = Bad drive-request structure length. +* 6 = Seek error. +* 7 = Unknown media type. +* 8 = Sector not found. +* 9 = Printer out of paper. +* 10 = Write fault. +* 11 = Read fault. +* 12 = General failure. +* +* AX - Will be non-negative if this is a disk error, negative +* otherwise. low-order byte of ax gives failing drive number +* high bits: +* +* 15 0 = disk error +* 14 not used +* 13 0 = "Ignore" not allowed +* 12 0 = "Retry" not allowed +* 11 0 = "Fail" not allowed (fail is same as abort) +* 10,9: +* 00 DOS +* 01 File allocation table +* 10 Directory +* 11 Data area +* 8 0 = Read error, 1 = Write error +* +* BP,SI - Not used +* +* RESULT +* NONE +* +****************************************************************************/ + +int HardErr_Handler(int errval, int ax, int bp, int si) +{ + /* Suppress compiler warnings */ + errval = errval; + bp = bp; + si = si; + + /* If AX < 0, this was not a disk error, so return the ABORT code */ + if (ax < 0) { + hardretn(_HARDERR_ABORT); + } + + /* Otherwise, if this is a drive-not-ready error, retry 5 times */ + if (kbhit() != 0) { + if (ax & (1 << 13)) { + hardresume(_HARDERR_IGNORE); + } else if (ax & (1 << 11)) { + hardresume(_HARDERR_FAIL); + } else { + hardresume(_HARDERR_RETRY); + } + } else { + if (ax & (1 << 12)) { + hardresume(_HARDERR_RETRY); + } else { + hardresume(_HARDERR_IGNORE); + } + } + + return (0); +} +#endif + +#else +/**************************************************************************** +* +* NAME +* HardErr_Handler - Critical error handler for INT 0x24. +* +* SYNOPSIS +* Action = HardErr_Handler(DeviceError, ErrorCode, DeviceHeader) +* +* int HardErr_Handler(unsigned, unsigned, unsigned __far *); +* +* FUNCTION +* +* INPUTS +* DeviceError - Device error description. +* +* bit 15 0 indicates disk error. +* bit 14 not used +* bit 13 0 indicates "Ignore" response not allowed. +* bit 12 0 indicates "Retry" response not allowed. +* bit 11 0 indicates "Fail" response not allowed. +* bit 9,10 location of error. +* +* Value Meaning +* +* 00 MS-DOS +* 01 File Allocation Table (FAT) +* 10 Directory +* 11 Data area +* +* bit 8 0 indicates read error,1 indicates write error +* +* The low-order byte indicates the drive where the error +* occurred; (0 = drive A, 1 = drive B, etc.). +* +* ErrorCode - Type of error. +* +* The low-order byte can be one of the following values: +* +* 0x00 Attempt to write to a write-protected disk. +* 0x01 Unknown unit. +* 0x02 Drive not ready. +* 0x03 Unknown command. +* 0x04 CRC error in data. +* 0x05 Bad drive-request structure length. +* 0x06 Seek error. +* 0x07 Unknown media type. +* 0x08 Sector not found. +* 0x09 Printer out of paper. +* 0x0A Write fault. +* 0x0B Read fault. +* 0x0C General fault. +* +* DeviceHeader - Pointer to a device header control-block that contains +* information about the device on which the error +* occurred. +* +* RESULT +* Action - Indication of what action to take using one of the following +* values: +* +* Value Meaning +* +* _HARDERR_IGNORE Ignore the error. +* _HARDERR_RETRY Retry the operation. +* _HARDERR_ABORT Abort the program issuing INT 0x23 +* _HARDERR_FAIL Fail the system call that is in progress +* (DOS 3.0 or higher) +* +****************************************************************************/ + +int __far HardErr_Handler(unsigned deverror, unsigned errcode, + unsigned __far *devhdr) +{ + /* Prevent compiler warnings. */ + errcode = errcode; + devhdr = devhdr; + + /* If this is not a disk error, then return the ABORT code. */ + if (deverror & (1 << 15)) { + return (_HARDERR_ABORT); + } + + /* If this is a drive-not-ready error then continue to retry. */ + if (kbhit() != 0) { + if (deverror & (1 << 13)) { + _hardresume(_HARDERR_IGNORE); + } + else if (deverror & (1 << 11)) { + _hardresume(_HARDERR_FAIL); + } + else { + _hardresume(_HARDERR_RETRY); + } + } else { + if (deverror & (1 << 12)) { + _hardresume(_HARDERR_RETRY); + } else { + _hardresume(_HARDERR_IGNORE); + } + } + + return (0); +} +#endif + + +/**************************************************************************** +* +* NAME +* GetFilePart - Get the filename part of path/filename. +* +* SYNOPSIS +* Filename = GetFilePart(Path) +* +* char *GetFilePart(char *); +* +* FUNCTION +* +* INPUTS +* Path - Full path to retrieve filename from. +* +* RESULT +* Filename - Pointer to filename or NULL if no filename. +* +****************************************************************************/ + +static char *GetFilePart(char *path) +{ + char *ptr; + + ptr = strrchr(path, '\\'); + + if (ptr == NULL) { + ptr = strrchr(path, ':'); + } + + if (strlen(ptr) > 1) { + ptr++; + } + + return (ptr); +} + diff --git a/WINVQ/VPLAY32/TASM32.CFG b/WINVQ/VPLAY32/TASM32.CFG new file mode 100644 index 0000000..0b48e6a --- /dev/null +++ b/WINVQ/VPLAY32/TASM32.CFG @@ -0,0 +1,10 @@ +/la +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE +/zi diff --git a/WINVQ/VPLAY32/TLIB.CFG b/WINVQ/VPLAY32/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/WINVQ/VPLAY32/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/WINVQ/VPLAY32/TLINK32.CFG b/WINVQ/VPLAY32/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/WINVQ/VPLAY32/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/WINVQ/VPLAY32/VPLAYTNT.DEF b/WINVQ/VPLAY32/VPLAYTNT.DEF new file mode 100644 index 0000000..09b1b36 --- /dev/null +++ b/WINVQ/VPLAY32/VPLAYTNT.DEF @@ -0,0 +1 @@ +stub 'gotnt.exe' \ No newline at end of file diff --git a/WINVQ/VQA32/AUDIO.CPP b/WINVQ/VQA32/AUDIO.CPP new file mode 100644 index 0000000..7f4a759 --- /dev/null +++ b/WINVQ/VQA32/AUDIO.CPP @@ -0,0 +1,2285 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* audio.c +* +* DESCRIPTION +* Audio playback and timing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 4, 1995 +* +* +* HISTORY: +* Modified for Win95 Direct Sound - Steve T 1/2/96 6:35AM +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_StartTimerInt - Initialize system timer interrupt. +* VQA_StopTimerInt - Remove system timer interrupt. +* VQA_SetTimer - Resets current time to given tick value. +* VQA_GetTime - Return current time. +* VQA_TimerMethod - Get timer method being used. +* VQA_OpenAudio - Open sound system. +* VQA_CloseAudio - Close sound system +* VQA_StartAudio - Starts audio playback +* VQA_StopAudio - Stop audio playback. +* CopyAudio - Copy data from Audio Temp buf into Audio play buf. +* +* PRIVATE +* TimerCallback - VQA timer event. (Called by HMI) +* AutoDetect - Auto detect the sound card. +* AudioCallback - Sound system callback. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +#ifdef __WATCOMC__ +#pragma pack(4); +#endif + + +/*--------------------------------------------------------------------------- + * AUDIO DEFINITIONS + *-------------------------------------------------------------------------*/ + +#define USE_WINDOWS_TIME 1 + +#define HMI_SAMPLE 0x1000 +#define MAKE_LONG(a,b) (((long)(a)<<16)|(long)((b)&0x0000FFFFL)) + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +#if( USE_WINDOWS_TIME ) + extern unsigned long Get_Game_Time( void ); +#endif + +#if(VQAAUDIO_ON) +#if(VQADIRECT_SOUND) +void CALLBACK TimerCallback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); +BOOL Move_HMI_Audio_Block_To_Direct_Sound_Buffer (void); +void CALLBACK AudioCallback ( UINT , UINT , DWORD , DWORD , DWORD ); + +#else + +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels); +void far TimerCallback(void); +void far __cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID); + +/* Dummy functions used to mark the start/end address of the file. */ +static void StartAddr(void); +static void EndAddr(void); + +#endif +/*--------------------------------------------------------------------------- + * GLOBAL DATA + *-------------------------------------------------------------------------*/ + +static VQAHandleP *VQAP = NULL; +static long AudioFlags = 0; +static long TimerSysCount = 0; +static long TimerIntCount = 0; +static WORD VQATimer = 0; +static long TimerMethod; +static long VQATickCount = 0; +#endif /* VQAAUDIO_ON */ + +static long TickOffset = 0; +char *HMIDevName = ""; + +#if(VQAAUDIO_ON) + +#if(!VQADIRECT_SOUND) + +/* This is a dummy function that is used to mark the start of the module. + * It is necessary for locking the memory the module occupies. This prevents + * the virtual memory manager from swapping out this memory. + */ +static void StartAddr(void) +{ +} + + +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(VQA, Init) +* +* long VQA_StartTimerInt(VQAHandeP *, long); +* +* FUNCTION +* Initialize the HMI timer system and add our own timer event. If the +* system has already been initialized then we are given access to the +* the timer system. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(VQAHandleP *vqap, long init) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Does the caller want me to initialize the timer system? */ + if (init == 0) { + + /* If the timer system is uninitialized then it is the players + * responsibility to do it. + */ + if ((AudioFlags & VQAAUDF_TIMERINIT)==(HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } else { + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + } + + /* Increment the timer system usage count. */ + TimerSysCount++; + + /* Register the VQA_TickCount timer event. */ + if ((AudioFlags & VQAAUDF_HMITIMER) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_HMITIMER); + + /* Increment the timer interrupt usage count. */ + TimerIntCount++; + + /* Lock the memory occupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock((void *)&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Decrement the timer interrupt usage count. */ + if (TimerIntCount) { + TimerIntCount--; + } + + /* Remove the timer interrrupt if it is initialized and the use count is + * zero. Otherwise, clear the callers timer interrupt availability flag. + */ + if (((AudioFlags & VQAAUDF_HMITIMER) == (HMI_VQAINIT<Flags &= ~VQAAUDF_HMITIMER; + } + + /* Derement the timer system usage count. */ + if (TimerSysCount) { + TimerSysCount--; + } + + /* Close the timer system if it has been opened and the use count is zero. + * Otherwise, clear the callers timer system availability flag. + */ + if (((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + } + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* TimerCallback - VQA timer event. (Called by HMI) +* +* SYNOPSIS +* TimerCallback() +* +* void TimerCallback(void); +* +* FUNCTION +* Our custom timer event. This is the timer event that we register with +* HMI for system timing. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void far TimerCallback(void) +{ + VQATickCount++; +} + + +/**************************************************************************** +* +* NAME +* VQA_OpenAudio - Open sound system. +* +* SYNOPSIS +* Error = VQA_OpenAudio(VQAHandleP) +* +* long VQA_OpenAudio(VQAHandleP *); +* +* FUNCTION +* Initialize the sound system by setting the DigiHandle to and HMI +* driver handle. The timer must first be initialized before calling +* this function. +* +* INPUTS +* VQAHandleP - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_OpenAudio(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAAudio *audio; + VQAConfig *config; + VQAHeader *header; + unsigned char *driver_path; + WORD port; + long rc; + + /* Dereference data memebers for quicker access. */ + config = &vqap->Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Fail if no audio buffer or DigiCard is 0 (no sound) */ + if ((audio->Buffer == NULL) || (config->DigiCard == 0)) { + return (-1); + } + + /* Reset the buffer position to the beginning. */ + audio->CurBlock = 0; + + /* Initialize the HMI driver structure. */ + audio->sSOSInitDriver.wAllocateBuffer = _TRUE; + audio->sSOSInitDriver.wParam = 19; + audio->sSOSInitDriver.wTimerID = _SOS_NORMAL_TIMER; + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + audio->sSOSInitDriver.wSampleRate = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + audio->sSOSInitDriver.wSampleRate = ((audio->SampleRate*config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = audio->sSOSInitDriver.wSampleRate; + } else { + audio->sSOSInitDriver.wSampleRate = audio->SampleRate; + config->AudioRate = audio->SampleRate; + } + + /* If the application has already initialized HMI then set the + * necessary variables. Otherwise we must setup HMI ourself. + */ + if ((config->OptionFlags & VQAOPTF_HMIINIT) + || ((AudioFlags & VQAAUDF_DIGIINIT) != 0)) { + + /* The application MUST provide the card type! */ + if (config->DigiCard == -1) { + return (-1); + } + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /* Get the capabilities of the card being used. */ + rc = sosDIGIDetectGetCaps(config->DigiCard, &audio->DigiCaps); + sosDIGIDetectUnInit(); + + if (rc != 0) { + return (rc); + } + + audio->DigiHandle = config->DigiHandle; + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + audio->Flags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + + if ((AudioFlags & (VQAAUDF_TIMERINIT|VQAAUDF_DIGIINIT)) == 0) { + HMIDevName = "App-Specific"; + AudioFlags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + } + } else { + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /*----------------------------------------------------------------------- + * Initialize DigiHardware with port, IRQ, and DMA, and make sure + * Config.DigiCard contains the HMI ID we want to use: + * + * - If Config.DigiCard is -1, auto-detect the hardware; then, do a + * FindHardware so the GetSettings will work + * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use + * FindHardware & GetSettings to get the settings + * - If all are filled in, just use them as they are + *---------------------------------------------------------------------*/ + + /* Auto-Detect */ + if (config->DigiCard == -1) { + + /* Version 1 VQA's have only 8 bit mono audio streams. */ + if (header->Version == VQAHD_VER1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } else { + config->DigiCard = AutoDetect(&audio->DigiCaps, audio->BitsPerSample, + audio->Channels); + + /* Resort to 8bit mono */ + if (config->DigiCard == -1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } + } + + if (config->DigiCard == -1) { + sosDIGIDetectUnInit(); + return (-1); + } + } + + /* Do a FindHardware & GetSettings */ + if (config->DigiPort == -1) { + if (sosDIGIDetectFindHardware(config->DigiCard, &audio->DigiCaps, + &port)) { + + sosDIGIDetectUnInit(); + return (-1); + } + + if (sosDIGIDetectGetSettings(&audio->DigiHardware)) { + sosDIGIDetectUnInit(); + return (-1); + } + + config->DigiPort = audio->DigiHardware.wPort; + config->DigiIRQ = audio->DigiHardware.wIRQ; + config->DigiDMA = audio->DigiHardware.wDMA; + HMIDevName = (char *)audio->DigiCaps.szDeviceName; + } else { + audio->DigiHardware.wPort = config->DigiPort; + audio->DigiHardware.wIRQ = config->DigiIRQ; + audio->DigiHardware.wDMA = config->DigiDMA; + HMIDevName = "App-Specific"; + } + + sosDIGIDetectUnInit(); + + /* Initialize the DIGI system & driver */ + sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL); + audio->sSOSInitDriver.wBufferSize = config->HMIBufSize; + audio->sSOSInitDriver.lpBuffer = NULL; + audio->sSOSInitDriver.lpFillHandler = NULL; + audio->sSOSInitDriver.lpDriverMemory = NULL; + audio->sSOSInitDriver.lpTimerMemory = NULL; + audio->DigiHandle = -1; + + if ((rc = sosDIGIInitDriver(config->DigiCard, &audio->DigiHardware, + &audio->sSOSInitDriver, &audio->DigiHandle)) != 0) { + + return (rc); + } + + /*----------------------------------------------------------------------- + * Register the timer event + *---------------------------------------------------------------------*/ + + /* If the timer hasn't been init'd, do it now */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } + + /* Register the event */ + rc = sosTIMERRegisterEvent(VQA_TIMETICKS, + audio->sSOSInitDriver.lpFillHandler, &audio->DigiTimer); + + if (rc) { + sosDIGIUnInitDriver(audio->DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + return (rc); + } + + config->DigiHandle = audio->DigiHandle; + audio->Flags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + } + + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Remove the Digi event */ + if ((AudioFlags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiTimer); + } + + /* Un-init timer if necessary */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + AudioFlags &= ~VQAAUDF_TIMERINIT; + + /* Remove the driver */ + if ((audio->Flags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + } + + audio->Flags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_ISPLAYING; + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* AutoDetect - Auto detect the sound card. +* +* SYNOPSIS +* CardID = AutoDetect(DigiCaps, BitSize, Channels) +* +* long AutoDetect(_SOS_CAPABILITIES *, long, long); +* +* FUNCTION +* Autodetects the type of sound card present in the system. +* +* INPUTS +* DigiCaps - Pointer to HMI digital card capabilities structure. +* BitSize - Bits per sample size. +* Channels - Number of desired channels. +* +* RESULT +* CardID - HMI ID of the sound card found, -1 if none. +* +****************************************************************************/ + +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels) +{ + long device_id = -1; + WORD port; + long i; + long rc; + + /* Search for an 8-bit mono device */ + if (sosDIGIDetectFindFirst(digicaps, &port)) { + return (-1); + } + + channels--; + + i = 0; + while (i < 6) { + i++; + + if ((digicaps->wBitsPerSample == bitsize) + && (digicaps->wChannels == channels)) { + + break; + } + + if (sosDIGIDetectFindNext(digicaps, &port)) { + return (-1); + } + } + + /* Exit if failed to find the required device */ + if ((digicaps->wBitsPerSample != bitsize) + || (digicaps->wChannels != channels)) { + + return (-1); + } + + /* Stash the ID */ + device_id = digicaps->wDeviceID; + + /* Now that we have handled the initial pass, verify that if we found an + * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise. + */ + if ((WORD)digicaps->wDeviceID == _ADLIB_GOLD_8_MONO) { + rc = sosDIGIDetectFindNext(digicaps, &port); + + while ((i < 6) && (rc == 0)) { + i++; + + if ((digicaps->wBitsPerSample == 8) && (digicaps->wChannels == 0)) { + break; + } + + if ((rc = sosDIGIDetectFindNext(digicaps, &port)) != 0) { + break; + } + } + + /* If we don't have an error use the secondary device ID, after all, + * anything's better than an Adlib Gold. If we do have an error or there + * is nothing we can use then the device ID is already set to the adlib + * gold so just let it rip. + */ + if ((rc == 0) && ((WORD)digicaps->wDeviceID == _SBPRO_8_MONO)) { + return (digicaps->wDeviceID); + } + } + + return (device_id); +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* Set my driver handle */ + if (config->DigiHandle != -1) { + audio->DigiHandle = config->DigiHandle; + } + + if (audio->DigiHandle == (WORD)-1) { + return (-1); + } + + /*------------------------------------------------------------------------- + * Initialize the sample structure. + *-----------------------------------------------------------------------*/ + memset(&audio->sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE)); + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)audio->Buffer; + audio->sSOSSampleData.dwSampleSize = config->HMIBufSize; + audio->sSOSSampleData.wVolume = (config->Volume << 7); + audio->sSOSSampleData.wSampleID = HMI_SAMPLE; + audio->sSOSSampleData.lpCallback = AudioCallback; + + /* Set the channel flags for the type of data we have. */ + if (audio->Channels == 2) { + audio->sSOSSampleData.wChannel = _INTERLEAVED; + } else { + audio->sSOSSampleData.wChannel = _CENTER_CHANNEL; + } + + /* If the card is unable to handle stereo data the we must notify the + * sound system to convert the stereo data to mono data during playback. + */ + if ((audio->Channels - 1) > audio->DigiCaps.wChannels) { + audio->sSOSSampleData.wSampleFlags |= _STEREOTOMONO; + } + + /* If the card is unable to handle the sample size of the audio data + * then we must notify the sound system to convert the audio data to + * the proper format. + */ + if (audio->BitsPerSample != audio->DigiCaps.wBitsPerSample) { + if (audio->BitsPerSample > audio->DigiCaps.wBitsPerSample) { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8; + } else { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* Adjust the pitch if the driver is setup to playback at a different + * rate than what the sample was recorded at. + */ + if (audio->sSOSInitDriver.wSampleRate != audio->SampleRate) { + ldiv_t result; + + result = ldiv(audio->SampleRate, audio->sSOSInitDriver.wSampleRate); + audio->sSOSSampleData.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, + (short)(((long)result.rem * 0x10000L) / audio->sSOSInitDriver.wSampleRate)); + audio->sSOSSampleData.wSampleFlags |= _PITCH_SHIFT; + } + + /* Start playback */ + audio->SampleHandle = sosDIGIStartSample(audio->DigiHandle, + &audio->sSOSSampleData); + + audio->Flags |= VQAAUDF_ISPLAYING; + AudioFlags |= VQAAUDF_ISPLAYING; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Just return if not playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + sosDIGIStopSample(audio->DigiHandle, audio->SampleHandle); + audio->Flags &= ~VQAAUDF_ISPLAYING; + AudioFlags &= ~VQAAUDF_ISPLAYING; + } + + VQAP = NULL; +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void far __cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + /* Suppress compiler warnings */ + wDriverHandle = wDriverHandle; + wSampleID = wSampleID; + + /* See if we're called because the buffer is empty */ + if (wAction == _SAMPLE_PROCESSED) { + + /* Compute the 'NextBlock' index */ + audio->NextBlock = audio->CurBlock + 1; + + if (audio->NextBlock >= audio->NumAudBlocks) { + audio->NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[audio->NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[audio->CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + audio->CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + audio->CurBlock = 0; + } + } else { + audio->NumSkipped++; + } + + /* Start the new buffer playing */ + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer) + + audio->PlayPosition; + + sosDIGIContinueSample(audio->DigiHandle, audio->SampleHandle, + &audio->sSOSSampleData); + + audio->SamplesPlayed += config->HMIBufSize; + } +} + +/* Dummy function used to mark the beginning address of the file. */ +static void EndAddr(void) +{ +} + + + +#else //!VQADIRECT_SOUND + +/************************************************************************** +* +* Start of Direct Sound code +* +* +* The direct sound implementation works by taking what would have been the +* HMI sound buffer and feeding the contents to a direct sound secondary +* buffer. +* +* Steve T. - 12/15/95 +* +* +* +* +***************************************************************************/ + +BOOL SuspendAudioCallback = FALSE; + +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(VQA, Init) +* +* long VQA_StartTimerInt(VQAHandeP *, long); +* +* FUNCTION +* Initialize the HMI timer system and add our own timer event. If the +* system has already been initialized then we are given access to the +* the timer system. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(VQAHandleP *vqap, long init) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Register the VQA_TickCount timer event. */ + if ((AudioFlags & VQAAUDF_HMITIMER) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_HMITIMER); + + /* Increment the timer interrupt usage count. */ + TimerIntCount++; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Decrement the timer interrupt usage count. */ + if (TimerIntCount) { + TimerIntCount--; + } + + /* Remove the timer interrrupt if it is initialized and the use count is + * zero. Otherwise, clear the callers timer interrupt availability flag. + */ + if (((AudioFlags & VQAAUDF_HMITIMER) == (HMI_VQAINIT<Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Reset the buffer position to the beginning. */ + audio->CurBlock = 0; + + + /* + ** create the direct sound object if it doesnt already exist + */ + if (!config->SoundObject){ + + if ( DirectSoundCreate (NULL,&config->SoundObject,NULL) !=DS_OK ) { + return (-1); + }else{ + audio->CreatedSoundObject = TRUE; + /* + ** Give ourselves exclusive access to the sound card + */ + if ( config->SoundObject->SetCooperativeLevel( window, DSSCL_EXCLUSIVE ) != DS_OK){ + config->SoundObject->Release(); + return (-1); + } + } + + + if (!config->PrimaryBufferPtr){ + /* + ** Define the format of the primary sound buffer + */ + memset (&audio->BufferDesc , 0 , sizeof(DSBUFFERDESC)); + audio->BufferDesc.dwSize=sizeof(DSBUFFERDESC); + audio->BufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; + + memset (&audio->DsBuffFormat , 0 , sizeof(WAVEFORMATEX)); + audio->DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM; + audio->DsBuffFormat.nChannels = (unsigned short) 2; + + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + audio->DsBuffFormat.nSamplesPerSec = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + audio->DsBuffFormat.nSamplesPerSec = ((audio->SampleRate*config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = audio->DsBuffFormat.nSamplesPerSec; + } else { + audio->DsBuffFormat.nSamplesPerSec = audio->SampleRate; + config->AudioRate = audio->SampleRate; + } + + audio->DsBuffFormat.wBitsPerSample = (short) 16; + audio->DsBuffFormat.nBlockAlign = (unsigned short)( (audio->DsBuffFormat.wBitsPerSample/8) * audio->DsBuffFormat.nChannels); + audio->DsBuffFormat.nAvgBytesPerSec= audio->DsBuffFormat.nSamplesPerSec * audio->DsBuffFormat.nBlockAlign; + audio->DsBuffFormat.cbSize = 0; + + /* + ** Create the direct sound primary sound buffer object + */ + if ( config->SoundObject->CreateSoundBuffer (&audio->BufferDesc , + &config->PrimaryBufferPtr , + NULL ) !=DS_OK ){ + if (audio->CreatedSoundObject){ + config->SoundObject->Release(); + audio->CreatedSoundObject = FALSE; + } + return (-1); + } + + audio->CreatedSoundBuffer = TRUE; + } + } + + audio->Flags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* + ** If the audio is still playing then stop it + */ + VQA_StopAudio(vqap); + + /* Remove the windows callback event */ + //if ((AudioFlags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<TimerHandle); + //} + + audio->Flags &= ~VQAAUDF_TIMERINIT; + AudioFlags &= ~VQAAUDF_TIMERINIT; + + /* + ** Remove the direct sound primary buffer if we created it + */ + if (audio->CreatedSoundBuffer){ + config->PrimaryBufferPtr->Stop(); + config->PrimaryBufferPtr->Release(); + config->PrimaryBufferPtr = NULL; + audio->CreatedSoundBuffer = FALSE; + } + + /* + ** If we created the sound object then remove that as well + */ + if (audio->CreatedSoundObject){ + config->SoundObject->Release(); + config->SoundObject = NULL; + } + + audio->Flags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_ISPLAYING; + +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* + ** If we already have a direct sound secondary buffer then get rid of it + */ + if (audio->SecondaryBufferPtr != NULL){ + audio->SecondaryBufferPtr->Stop(); + audio->SecondaryBufferPtr->Release(); + audio->SecondaryBufferPtr = NULL; + } + /* + ** Make it big enough for 4 blocks of HMI data + */ + audio->SecondaryBufferSize = config->HMIBufSize*4; + + /* + ** Define the format for the secondary sound buffer + */ + memset (&audio->BufferDesc , 0 , sizeof(DSBUFFERDESC)); + audio->BufferDesc.dwSize = sizeof(DSBUFFERDESC); + audio->BufferDesc.dwFlags = DSBCAPS_CTRLVOLUME; + audio->BufferDesc.dwBufferBytes = audio->SecondaryBufferSize; + audio->BufferDesc.lpwfxFormat = (LPWAVEFORMATEX) &audio->DsBuffFormat; + memset (&audio->DsBuffFormat , 0 , sizeof(WAVEFORMATEX)); + audio->DsBuffFormat.wFormatTag = WAVE_FORMAT_PCM; + audio->DsBuffFormat.nSamplesPerSec = audio->SampleRate; + audio->DsBuffFormat.nChannels = audio->Channels; + audio->DsBuffFormat.wBitsPerSample = audio->BitsPerSample; + audio->DsBuffFormat.nBlockAlign = (short) ((audio->DsBuffFormat.wBitsPerSample/8) * audio->DsBuffFormat.nChannels); + audio->DsBuffFormat.nAvgBytesPerSec = audio->DsBuffFormat.nSamplesPerSec * audio->DsBuffFormat.nBlockAlign; + + /* + ** Create the secondary sound buffer object + */ + config->SoundObject->CreateSoundBuffer (&audio->BufferDesc , &audio->SecondaryBufferPtr , NULL); + + /* + ** Set the format of the primary buffer to the same as the secondary buffer + */ + config->PrimaryBufferPtr->Stop(); + config->PrimaryBufferPtr->SetFormat (&audio->DsBuffFormat); + + if (config->PrimaryBufferPtr->Play(0, 0, DSBPLAY_LOOPING) != DS_OK){ + return (-1); + } + + /* Start playback */ + audio->EndLastAudioChunk = 0; + audio->ChunksMovedToAudioBuffer = 0; + Move_HMI_Audio_Block_To_Direct_Sound_Buffer(); + Move_HMI_Audio_Block_To_Direct_Sound_Buffer(); + audio->SecondaryBufferPtr->SetCurrentPosition (0); + if (audio->SecondaryBufferPtr->Play(0, 0, DSBPLAY_LOOPING) != DS_OK){ + return (-1); + } + + /* + ** Set the volume + */ + long volume = config->Volume << 7; + audio->SecondaryBufferPtr->SetVolume(- ( ( (32768 - volume)*1000) >>15 ) ); + + // Set orf 60hz timer + audio->TimerHandle = timeSetEvent ( 1000/60 , 1 , AudioCallback , 0 , TIME_PERIODIC); + + audio->Flags |= VQAAUDF_ISPLAYING; + AudioFlags |= VQAAUDF_ISPLAYING; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Just return if not playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + + /* + ** Stop the primary buffer so the audio doesnt glitch + */ + config->PrimaryBufferPtr->Stop(); + + /* + ** Remove the windows timer event + */ + timeKillEvent(audio->TimerHandle); + audio->TimerHandle = NULL; + + /* + ** Kill the secondary sound buffer + */ + if (audio->SecondaryBufferPtr){ + audio->SecondaryBufferPtr->Stop(); + audio->SecondaryBufferPtr->Release(); + audio->SecondaryBufferPtr = NULL; + } + audio->Flags &= ~VQAAUDF_ISPLAYING; + AudioFlags &= ~VQAAUDF_ISPLAYING; + } + + VQAP = NULL; +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +BOOL VQAAudioPaused = FALSE; + +void VQA_PauseAudio(void) +{ + VQAAudio *audio; + if (VQAP && VQAP->VQABuf){ + + audio = &VQAP->VQABuf->Audio; + + if (audio->SecondaryBufferPtr){ + + if (AudioFlags & VQAAUDF_ISPLAYING && !VQAAudioPaused) { + + audio->SecondaryBufferPtr->Stop(); + VQAAudioPaused = TRUE; + } + } + } +} + + +void VQA_ResumeAudio(void) +{ + VQAAudio *audio; + if (VQAP && VQAP->VQABuf){ + + audio = &VQAP->VQABuf->Audio; + + if (audio->SecondaryBufferPtr){ + + if (AudioFlags & VQAAUDF_ISPLAYING && VQAAudioPaused) { + + audio->SecondaryBufferPtr->SetCurrentPosition (0); + audio->LastChunkPosition = 0; + audio->EndLastAudioChunk = 0; + Move_HMI_Audio_Block_To_Direct_Sound_Buffer(); + audio->SecondaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + VQAAudioPaused = FALSE; + } + } + } +} + + + + + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void CALLBACK AudioCallback ( UINT, UINT, DWORD, DWORD, DWORD ) + +{ + VQAAudio *audio; + VQAConfig *config; + DWORD play_cursor; //Position that direct sound is reading from + DWORD write_cursor; //Position in buffer that we can write to + HRESULT return_code; + BOOL buffer_stopped = FALSE; + DWORD status; + + + if (SuspendAudioCallback || VQAAudioPaused) return; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + if (!audio->SecondaryBufferPtr) return; + + /* + ** See if we are nearing the end of the meaningful data in the direct sound buffer + */ + return_code = audio->SecondaryBufferPtr->GetCurrentPosition (&play_cursor , &write_cursor); + + BOOL write_more = FALSE; + + if (return_code == DSERR_BUFFERLOST || config->PrimaryBufferPtr->GetStatus(&status) == DSERR_BUFFERLOST){ + config->PrimaryBufferPtr->Restore(); + audio->SecondaryBufferPtr->Restore(); + audio->SecondaryBufferPtr->Stop(); + buffer_stopped = TRUE; + audio->SecondaryBufferPtr->SetCurrentPosition (0); + audio->LastChunkPosition = 0; + audio->EndLastAudioChunk = 0; + write_more = TRUE; + } + + + if (play_cursor < audio->EndLastAudioChunk){ + if (audio->EndLastAudioChunk - play_cursor <= audio->SecondaryBufferSize/4){ + write_more = TRUE; + } + }else{ + if (( play_cursor > audio->SecondaryBufferSize*3/4) && audio->EndLastAudioChunk==0 ){ + write_more = TRUE; + } + } + + /* + ** See if we need to fill the buffer + */ + if (write_more){ + if (Move_HMI_Audio_Block_To_Direct_Sound_Buffer()){ + + /* + ** Start the buffer playing again if we had to stop it + */ + if (buffer_stopped){ + audio->SecondaryBufferPtr->Play(0,0,DSBPLAY_LOOPING); + } + } + } +} + + + + + + + + + +/*********************************************************************************************** + * Move_HMI_Audio_Block_To_Direct_Sound_Buffer -- moves an audio block which would have been * + * played by HMI into a direct sound * + * secondary buffer * + * * + * INPUT: Nothing * + * * + * OUTPUT: BOOL was block moved * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/21/95 9:51AM ST : Created * + *=============================================================================================*/ +extern int VQAMovieDone; +BOOL Move_HMI_Audio_Block_To_Direct_Sound_Buffer (void) +{ + VQAAudio *audio; + VQAConfig *config; + + LPVOID play_buffer_ptr; //Beginning of locked area of buffer + LPVOID dummy_buffer_ptr; //Length of locked area in buffer + DWORD lock_length1; //Beginning of second locked area in buffer + DWORD lock_length2; //Length of second locked area in buffer + unsigned next_fill_pos; + HRESULT return_code; + DWORD status; + + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + + /************************************************************************* + ** + ** Copy the data from the HMI play position into the direct sound buffer + ** + */ + next_fill_pos = audio->EndLastAudioChunk; + + /* + ** Lock the buffer to get a pointer to it + */ + return_code= audio->SecondaryBufferPtr->Lock ((DWORD)next_fill_pos, + (DWORD)config->HMIBufSize, + &play_buffer_ptr, + &lock_length1, + &dummy_buffer_ptr, + &lock_length2, + 0 ); + + if (return_code!=DS_OK) return(FALSE); + if (config->PrimaryBufferPtr->GetStatus(&status) == DSERR_BUFFERLOST) return(FALSE); + + /* + ** Copy the HMI audio buffer to the direct sound buffer + */ + memcpy ((char*)play_buffer_ptr , (char*)((unsigned)audio->Buffer + (unsigned)audio->PlayPosition) , config->HMIBufSize); + + /* + ** Unlock the direct sound buffer + */ + audio->SecondaryBufferPtr->Unlock(play_buffer_ptr, + lock_length1, + dummy_buffer_ptr, + lock_length2); + /* + ** Update our audio data pointers + */ + audio->LastChunkPosition = next_fill_pos; + audio->EndLastAudioChunk = next_fill_pos + config->HMIBufSize; + if (audio->EndLastAudioChunk >= audio->SecondaryBufferSize){ + audio->EndLastAudioChunk = 0; + } + + /* Compute the 'NextBlock' index */ + audio->NextBlock = audio->CurBlock + 1; + + if (audio->NextBlock >= audio->NumAudBlocks) { + audio->NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[audio->NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[audio->CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + audio->CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + audio->CurBlock = 0; + } + audio->ChunksMovedToAudioBuffer++; + return (TRUE); + } else { + if (VQAMovieDone){ + audio->ChunksMovedToAudioBuffer++; + } + audio->NumSkipped++; + /* + ** Enable frame skipping to prevent this happening again + */ + config->DrawFlags &= ~VQACFGF_NOSKIP; + return (FALSE); + } +} + + + + + +#endif //!VQADIRECT_SOUND + + +#endif /* VQAAUDIO_ON */ + + + + + + + + + + + + +/**************************************************************************** +* +* NAME +* VQA_SetTimer - Resets current time to given tick value. +* +* SYNOPSIS +* VQA_SetTimer(Time, Method) +* +* void VQA_SetTimer(long, long); +* +* FUNCTION +* Sets 'TickOffset' to a value that will make the current time look like +* the time passed in. This function allows the player to be "paused", +* by recording the time of the pause, and then setting the timer to +* that time. The timer method used by the player is also set. The method +* selected is not neccesarily the method that will be used because some +* timer methods work with only certain playback conditions. (EX: The +* audio DMA timer method cannot be used if there is not any audio +* playing.) +* +* INPUTS +* Time - Value to set current time to. +* Method - Timer method to use. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_SetTimer(VQAHandleP *vqap, long time, long method) +{ + unsigned long curtime; + + #if(VQAAUDIO_ON) + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* If the client does not have a preferencee then pick a method + * based on the state of the player. + */ + if (method == VQA_TMETHOD_DEFAULT) { + + /* If we are playing audio, use the audio DMA position. */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + method = VQA_TMETHOD_AUDIO; + } + + /* Otherwise use the HMI timer if it is initialized. */ + else if (AudioFlags & VQAAUDF_HMITIMER) { + method = VQA_TMETHOD_INT; + } + + /* If all else fails resort the the "jerky" DOS time. */ + else { + method = VQA_TMETHOD_DOS; + } + } else { + + /* We cannot use the DMA position if there isn't any audio playing. */ + if (!(AudioFlags & VQAAUDF_ISPLAYING) && (method == VQA_TMETHOD_AUDIO)) { + method = VQA_TMETHOD_INT; + } + + /* We cannot use the timer if it has not been initialized. */ + if (!(AudioFlags & VQAAUDF_HMITIMER) && (method == VQA_TMETHOD_INT)) { + method = VQA_TMETHOD_DOS; + } + } + + TimerMethod = method; + #else + method = method; + #endif + + TickOffset = 0L; + curtime = VQA_GetTime(vqap); + TickOffset = (time - curtime); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetTime - Return current time. +* +* SYNOPSIS +* Time = VQA_GetTime() +* +* unsigned long VQA_GetTime(void); +* +* FUNCTION +* This routine returns timer ticks computed one of 3 ways: +* +* 1) If audio is playing, the timer is based on the DMA buffer position: +* Compute the number of audio samples that have actually been played. +* The following internal HMI variables are used: +* +* _lpSOSDMAFillCount[drv_handle]: current DMA buffer position +* _lpSOSSampleList[drv_handle][samp_handle]: +* sampleTotalBytes: total bytes sent by HMI to the DMA buffer +* sampleLastFill: HMI's last fill position in DMA buffer +* +* So, the number of samples actually played is: +* +* sampleTotalBytes - +* where is how far ahead sampleLastFill is in front of +* _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount) +* +* These values are indices into a circular DMA buffer, so: +* +* if (sampleLastFill >= _lpSOSDMAFillCount) +* = sampleLastFill - _lpSOSDMAFillCount +* else +* = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill +* +* Note that, if using the stereo driver with mono data, you must +* divide LastFill & FillCount by 2, but not TotalBytes. If using the +* stereo driver with stereo data, you must divide all 3 variables +* by 2. +* +* 2) If no audio is playing, but the timer interrupt is running, +* VQATickCount is used as the timer +* +* 3) If no audio is playing & no timer interrupt is going, the DOS 18.2 +* system timer is used. +* +* Regardless of the method, TickOffset is used as an offset from the +* computed time. +* +* INPUTS +* NONE +* +* RESULT +* Time - Time in VQA_TIMETICKS +* +****************************************************************************/ +unsigned long VQA_GetTime(VQAHandleP *vqap) +{ + #if(VQAAUDIO_ON) + VQAAudio *audio; + VQAConfig *config; + unsigned long fillcount; + unsigned long lastfill; + unsigned long dma_diff; + unsigned long totalbytes; + unsigned long samples; + DWORD play_cursor; //Position that direct sound is reading from + DWORD write_cursor; //Position in buffer that we can write to + unsigned temp; + #endif + + // MEG 09.25.95 - changed from long to unsigned long + unsigned long ticks; + + #if(VQAAUDIO_ON) + switch (TimerMethod) { + + /* If Audio is playing then timing is based on the audio DMA buffer + * position. + */ + case VQA_TMETHOD_AUDIO: + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + //vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + #if (VQADIRECT_SOUND) + + totalbytes = (audio->ChunksMovedToAudioBuffer) * config->HMIBufSize; + + if (audio->SecondaryBufferPtr && + audio->SecondaryBufferPtr->GetCurrentPosition (&play_cursor , &write_cursor) == DS_OK){ + totalbytes = (audio->ChunksMovedToAudioBuffer) * config->HMIBufSize; + + if (audio->LastChunkPosition){ + totalbytes += play_cursor - audio->LastChunkPosition; + }else { + if (play_cursor > audio->SecondaryBufferSize*3/4){ + totalbytes -= audio->SecondaryBufferSize - play_cursor; + } else { + totalbytes += play_cursor - audio->LastChunkPosition; + } + } + + + }else{ + totalbytes = (audio->ChunksMovedToAudioBuffer-1) * config->HMIBufSize; + } + + + samples = totalbytes/audio->DsBuffFormat.nChannels; + samples = samples/(audio->DsBuffFormat.wBitsPerSample >> 3); + + #else //VQADIRECT_SOUND + + /* Compute our current position in the audio track by getting the + * bytes processed by HMI. Then adjust the bytes processed by the + * position of the DMA fill handler, this should tell us exactly + * where we are in the audio track. + */ + fillcount = (unsigned long)(*_lpSOSDMAFillCount[audio->DigiHandle]); + lastfill = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleLastFill; + totalbytes = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleTotalBytes; + + if (totalbytes == 0) { + dma_diff = 0; + } else { + if (lastfill > fillcount) { + dma_diff = lastfill - fillcount; + } else { + dma_diff = (config->HMIBufSize - fillcount) + lastfill; + } + + if (dma_diff > totalbytes) { + dma_diff = totalbytes; + } + } + + /* Calculate the number of samples by taking the total number of + * bytes processed and divide it by the number of channels and + * bits per sample. + */ + samples = totalbytes - dma_diff; + samples -= (audio->NumSkipped * config->HMIBufSize); + samples /= (audio->Channels * (audio->BitsPerSample >> 3)); + + #endif //VQADIRECT_SOUND + + /* The elapsed ticks is calculated by the number of samples + * processed times the tick resolution per second divided by the + * sample rate. + */ + ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate); + ticks += TickOffset; + break; + + /* No audio playing, but timer interrupt is going; use VQATickCount */ + case VQA_TMETHOD_INT: + ticks = (VQATickCount + TickOffset); + break; + + /* No interrupts are going at all; use DOS's time */ + default: + case VQA_TMETHOD_DOS: + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + break; + } + #else + { + // MEG 09.23.95 - Use Windows timer. +#if( ! USE_WINDOWS_TIME ) + + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + +#else + + ticks = Get_Game_Time(); + +#endif + +// ticks = ((ticks * VQA_TIMETICKS) / 1000L); + + // MEG 09.25.95 - multiply by 3, divide by 50 instead of multiplying by + // 60 and dividing by 1000. The reason for this is that multiplying by + // 60 causes an overflow pretty quickly if the value from Get_Game_Time + // is high. + ticks = ((ticks * 3L) / 50L); + + ticks += TickOffset; + } + #endif + + return (ticks); +} + + +/**************************************************************************** +* +* NAME +* VQA_TimerMethod - Get timer method being used. +* +* SYNOPSIS +* Method = VQA_TimerMethod() +* +* long VQA_TimerMethod(void); +* +* FUNCTION +* Returns the ID of the current timer method being used. +* +* INPUTS +* NONE +* +* RESULT +* Method - Method used for the timer. +* +****************************************************************************/ + +long VQA_TimerMethod(void) +{ + #if(VQAAUDIO_ON) + return (TimerMethod); + #else + return (VQA_TMETHOD_DOS); + #endif +} + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif diff --git a/WINVQ/VQA32/BCC32.CFG b/WINVQ/VQA32/BCC32.CFG new file mode 100644 index 0000000..7e1d1b9 --- /dev/null +++ b/WINVQ/VQA32/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vqa32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/WINVQ/VQA32/CAPTION.CPP b/WINVQ/VQA32/CAPTION.CPP new file mode 100644 index 0000000..baa2b50 --- /dev/null +++ b/WINVQ/VQA32/CAPTION.CPP @@ -0,0 +1,403 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.c +* +* DESCRIPTION +* Text caption process/display manager. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "caption.h" + +#if( VQACAPTIONS_ON ) + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define NUM_NODES 3 + +/* Function prototypes. */ +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext); +static void RemCaptionNode(CaptionList *list, CaptionNode *node); + + +/**************************************************************************** +* +* NAME +* OpenCaptions - Initialize the caption system. +* +* SYNOPSIS +* OpenCaptions(Captions, Font) +* +* CaptionInfo *OpenCaptions(void *, void *); +* +* FUNCTION +* Allocate and initialize resources used by the caption system. +* +* INPUTS +* Captions - Captions to process. +* Font - Font to use to display captions. +* +* RESULT +* CaptionInfo - Caption information structure. +* +* SEE ALSO +* CloseCaption +* +****************************************************************************/ + +CaptionInfo *OpenCaptions(void *captions, void *font) +{ + CaptionInfo *cap = NULL; + CaptionNode *node; + FontInfo *fi; + long i; + + /* Allocate memory for the captioning system. */ + cap = (CaptionInfo *)malloc(sizeof(CaptionInfo) + (sizeof(CaptionNode) + * NUM_NODES)); + + if (cap != NULL) { + memset(cap,0,(sizeof(CaptionInfo) + (sizeof(CaptionNode) * NUM_NODES))); + cap->Buffer = captions; + cap->Next = (CaptionText *)captions; + + /* Initialize font */ + fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk); + cap->Font = font; + cap->FontHeight = fi->MaxHeight; + cap->FontWidth = fi->MaxWidth; + + /* Initialize list header. */ + cap->List.Head = (CaptionNode *)&cap->List.Tail; + cap->List.Tail = NULL; + cap->List.TailPred = (CaptionNode *)&cap->List.Head; + + /* Link nodes. */ + node = (CaptionNode *)((char *)cap + sizeof(CaptionInfo)); + + for (i = 0; i < NUM_NODES; i++) { + node->Succ = cap->List.Head; + cap->List.Head = node; + node->Pred = (CaptionNode *)&cap->List.Head; + node->Succ->Pred = node; + + /* Next node. */ + node = (CaptionNode *)((char *)node + sizeof(CaptionNode)); + } + } + + return (cap); +} + + +/**************************************************************************** +* +* NAME +* CloseCaptions - Shutdown the caption system. +* +* SYNOPSIS +* CloseCaptions(CaptionInfo) +* +* void CloseCaptions(CaptionInfo *); +* +* FUNCTION +* Free the resources allocated by the caption system. +* +* INPUTS +* CaptionInfo - Caption information structure. +* +* RESULT +* NONE +* +* SEE ALSO +* OpenCaptions +* +****************************************************************************/ + +void CloseCaptions(CaptionInfo *cap) +{ + free(cap); +} + + +/**************************************************************************** +* +* NAME +* DoCaption - Process and display caption text. +* +* SYNOPSIS +* DoCaption(Captions, Frame) +* +* void DoCaption(CaptionInfo *, unsigned long); +* +* FUNCTION +* Process the caption events. +* +* INPUTS +* Captions - Pointer to CaptionInfo structure. +* Frame - Current frame number being processed. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DoCaptions(CaptionInfo *cap, unsigned long frame) +{ + CaptionText *captext; + CaptionNode *node; + void const *oldfont; + long width; + long i; + + /* Initialize variables. */ + oldfont = Set_Font((char *)cap->Font); + + /*------------------------------------------------------------------------- + * Process the captions that are on the active queue. + *-----------------------------------------------------------------------*/ + node = cap->List.Head; + + while ((node->Succ != NULL) && (node->Flags & CNF_USED)) { + captext = node->Captext; + + /* Clear the any previous captions that have expired. */ + if (captext->OffFrame <= frame) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + + /* Remove the caption from the active list. */ + node = node->Pred; + RemCaptionNode(&cap->List, node->Succ); + } else { + if (captext->CPF != 0) { + + /* If a NULL terminator is not found then display the next set of + * characters, otherwise remove the node. + */ + if (*node->Char != 0) { + Set_Font_Palette_Range(&captext->BgPen, 0, 1); + + for (i = 0; i < captext->CPF; i++) { + + /* Check for terminator. */ + if (*node->Char == 0) { + captext->CPF = 0; + break; + } + /* Check for newline. */ + else if (*node->Char == 0x0D) { + node->Char++; + node->CurX = captext->Xpos; + node->CurY += cap->FontHeight; + node->BoundH += cap->FontHeight; + } + + Draw_Char(*node->Char, node->CurX, node->CurY); + node->CurX += Char_Pixel_Width(*node->Char); + node->Char++; + } + } + } else if (captext->Flags & CTF_FLASH) { + if (frame & 4) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + } else { + Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + } + } + } + + /* Next node. */ + node = node->Succ; + } + + /*------------------------------------------------------------------------- + * Process any captions that are waiting to be activated. + *-----------------------------------------------------------------------*/ + captext = cap->Next; + + while (captext->OnFrame <= frame) { + + width = String_Pixel_Width(captext->Text); + + switch (captext->Flags & CTF_JUSTIFY) { + case CTF_RIGHT: + captext->Xpos = (319 - width); + break; + + case CTF_LEFT: + captext->Xpos = 0; + break; + + case CTF_CENTER: + captext->Xpos = (160 - (width / 2)); + break; + + default: + break; + } + + /* Display the text and record its bounding box. */ + if (captext->CPF == 0) { + i = Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = (cap->FontHeight * i); + } else { + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = cap->FontHeight; + } + + /* Next */ + cap->Next = (CaptionText *)(((char *)captext) + captext->Size); + captext = cap->Next; + } + + Set_Font(oldfont); +} + + +/**************************************************************************** +* +* NAME +* AddCaptionNode - Add a caption to the processing list. +* +* SYNOPSIS +* Node = AddCaptionNode(List, Caption) +* +* CaptionNode *AddCaptionNode(CaptionList *, CaptionText *); +* +* FUNCTION +* Add a caption the caption processing list. +* +* INPUTS +* List - Caption processing list. +* Caption - Caption to add to the list. +* +* RESULT +* Node - Pointer to node, otherwise NULL if error. +* +****************************************************************************/ + +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext) +{ + CaptionNode *node = NULL; + + /* If this list is not full. */ + node = list->TailPred; + + if (!(node->Flags & CNF_USED)) { + + /* Remove the node from the tail. */ + node->Pred->Succ = node->Succ; + list->TailPred = node->Pred; + + node->Flags |= CNF_USED; + node->Captext = captext; + node->Char = captext->Text; + node->CurX = captext->Xpos; + node->CurY = captext->Ypos; + + /* Add the node to the head. */ + node->Succ = list->Head; + list->Head = node; + node->Pred = (CaptionNode *)&list->Head; + node->Succ->Pred = node; + } + + return (node); +} + + +/**************************************************************************** +* +* NAME +* RemCaptionNode - Remove a caption from the processing list. +* +* SYNOPSIS +* RemCaptionNode(List, Node) +* +* void RemCaptionNode(CaptionList *, CaptionNode *); +* +* FUNCTION +* Remove the caption from the processing list. Mark the node as unused +* and put it at the tail of the list. +* +* INPUTS +* List - Caption processing list. +* Node - Caption node to remove. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void RemCaptionNode(CaptionList *list, CaptionNode *node) +{ + /* If the nodes successor is null then we are at the tail. */ + if (node->Succ != NULL) { + + /* Mark the node as unused. */ + node->Flags = 0; + + /* Relink the node to the tail. */ + node->Succ->Pred = node->Pred; + node->Pred->Succ = node->Succ; + node->Succ = (CaptionNode *)&list->Tail; + node->Pred = list->TailPred; + list->TailPred->Succ = node; + list->TailPred = node; + } +} + + +#endif // VQACAPTIONS_ON + \ No newline at end of file diff --git a/WINVQ/VQA32/CAPTION.H b/WINVQ/VQA32/CAPTION.H new file mode 100644 index 0000000..cda7a97 --- /dev/null +++ b/WINVQ/VQA32/CAPTION.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQACAPTION_H +#define VQACAPTION_H +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.h +* +* DESCRIPTION +* Text caption definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + + +#define VQACAPTIONS_ON 0 + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* CaptionNode: Node describing a caption to process. + * + * Succ - Pointer to the next node in the list (successor). + * Pred - Pointer to the previous node in the list (predecessor). + * Flags - Status flags. + * CapText - Pointer to the CaptionText being processed. + * Char - Pointer to current character in the string. + * CurX - Current X position. + * CurY - Current Y position. + * BoundW - Bounding width of text. + * BoundH - Bounding height of text. + */ +typedef struct _CaptionNode { + struct _CaptionNode *Succ; + struct _CaptionNode *Pred; + unsigned short Flags; + CaptionText *Captext; + char *Char; + unsigned short CurX; + unsigned short CurY; + unsigned short BoundW; + unsigned short BoundH; +} CaptionNode; + +/* CaptionNode flag definitions. */ +#define CNB_USED 0 /* This node is being used. */ +#define CNF_USED (1<. +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* config.c +* +* DESCRIPTION +* Player configuration routines. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +****************************************************************************/ + +#include +#include +#include +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Default configuration structure. */ +static VQAConfig _defaultconfig = { + + /* DrawerCallback: This is a function that is called for every frame + * in the movie. + */ + NULL, + + /* EventHandler: This is a function that is called for every event that + * the client requested to be notified about. + */ + NULL, + + /* NotifyFlags: Flags representing the events the client wishes to be + * notified about during playback. + */ + NULL, + + /* Vmode: Video mode to use. */ + MCGA, + + /* VBIBit: Vertical blank bit polarity. */ + -1, + + /* ImageBuf: Pointer to image buffer to draw into. */ + NULL, + + /* ImageWidth, ImageHeight: Width and height dimensions of image buffer. + * A width and height value of -1 tells the player to consider the image + * buffer as having the same dimensions as the frames in the movie. + */ + 320, 200, /* Image width and height */ + + /* X1, Y1: These are the coordinates to put the movies frame in the image + * buffer. Values of -1 tell the drawer to center the frames in the buffer. + */ + -1, -1, + + /* FrameRate: The rate to load the frames at. A value of -1 tells the + * player to use the framerate of the movie. + */ + -1, + + /* DrawRate: The rate to draw the frames at. A value of -1 tells the + * player to use the framerate of the movie. A value of 0 tells the player + * to use a fixed rate based on the frame size. + */ + -1, + + /* TimerMethod: Timer method to use for playback. */ + -1, + + /* DrawFlags: Various drawing related flags. */ + 0, + + /* OptionFlags: Various player options. */ + VQAOPTF_AUDIO, + + /* NumFrameBufs: The number of frame buffers to allocate/use. */ + 6, + + /* NumCBBufs: The number of codebook buffers to allocate/use. */ + 3, + +#if (VQADIRECT_SOUND) + /* -----------------12/15/95 10:40AM----------------- + * SoundObject - ptr to games direct sound object. Null if VQ should create + * --------------------------------------------------*/ + NULL, + + /* -----------------12/15/95 10:41AM----------------- + * PrimaryBufferPtr - ptr to games primary sound buffer. Null if VQ should create + * --------------------------------------------------*/ + NULL, +#endif //(VQADIRECT_SOUND) + + /* VocFile: Filename of audio track override. A value of 0 tells the + * player not to override the movies audio track. + */ + NULL, + + /* AudioBuf: Audio buffer to use. A value of 0 tells the player that + * it has to allocate a buffer itself. + */ + NULL, + + /* AudioBufSize: Size of audio buffer to use/allocate. A value of -1 + * tells the player to compute the buffer size from the audio + * information in the movie. + */ + -1, + + /* AudioRate: Audio playback rate in samples per second. A value of -1 + * tells the player to use the audio rate of the movie. + */ + -1, + + /* Volume: Volume level to playback audio track. */ + 0x00FF, + + /* HMIBufSize: Size of HMIs internal buffer. */ +#if (VQADIRECT_SOUND) + 8192L, +#else + 2048L, +#endif + /* DigiHandle: Handle to an initialized HMI sound driver. A value of -1 + * tells the player it must initialize the HMI sound driver itself. + */ + -1, + + /* DigiCard: HMI ID of audio card to use. A value of 0 tells the player + * not to use any card. A value of -1 tells the player to autodetect the + * card in the system. + */ + -1, + + /* DigiPort: Port address of the sound card. A value of -1 tells the player + * to autodetect this address. + */ + -1, + + /* DigiIRQ: Interrupt number of sound card. A value of -1 tells the player + * to autodetect the interrupt used by the card. + */ + -1, + + /* DigiDMA: DMA channel of the sound card. A value of -1 tells the player + * to autodetect the channel used by the card. + */ + -1, + + /* Language: Prefered language. */ + 0, + + /* CaptionFont: Caption text font. */ + NULL, + + /* EVAFont: EVA text font. */ + NULL, + +}; + +/* Supported video modes. */ +#if(VQAVIDEO_ON) +enum VMTAGS { + VMTAG_NONE = 0, + + #if(VQAMCGA_ON) + VMTAG_MCGA, + VMTAG_MCGA_BUF, + #endif + + #if(VQAXMODE_ON) + VMTAG_XMODE320X200, + VMTAG_XMODE320X200_BUF, + VMTAG_XMODE320X200_VRAM, + VMTAG_XMODE320X240, + VMTAG_XMODE320X240_BUF, + VMTAG_XMODE320X240_VRAM, + #endif + + #if(VQAVESA_ON) + VMTAG_VESA640X480_BUF, + VMTAG_VESA640X480_X2, + VMTAG_VESA320X200, + VMTAG_VESA320X200_BUF, + #endif +}; + +typedef struct _VideoModeTag { + char const *token; + long id; +} VideoModeTag; + +VideoModeTag VideoModeTags[] = { + {"NONE",VMTAG_NONE}, + + #if(VQAMCGA_ON) + {"MCGA", VMTAG_MCGA}, + {"MCGA_BUF",VMTAG_MCGA_BUF}, + #endif /* VQAMCGA_ON */ + + #if(VQAXMODE_ON) + {"XMODE_320X200", VMTAG_XMODE320X200}, + {"XMODE_320X200_BUF", VMTAG_XMODE320X200_BUF}, + {"XMODE_320X200_VRAM",VMTAG_XMODE320X200_VRAM}, + {"XMODE_320X240", VMTAG_XMODE320X240}, + {"XMODE_320X240_BUF", VMTAG_XMODE320X240_BUF}, + {"XMODE_320X240_VRAM",VMTAG_XMODE320X240_VRAM}, + #endif /* VQAXMODE_ON */ + + #if(VQAVESA_ON) + {"VESA_640X480_BUF",VMTAG_VESA640X480_BUF}, + {"VESA_640X480_X2", VMTAG_VESA640X480_X2}, + {"VESA_320X200", VMTAG_VESA320X200}, + {"VESA_320X200_BUF",VMTAG_VESA320X200_BUF}, + #endif /* VQAVESA_ON */ + + {NULL, NULL} +}; +#endif /* VQAVIDEO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* +* SYNOPSIS +* VQA_INIConfig(Config) +* +* void VQA_INIConfig(VQAConfig *); +* +* FUNCTION +* Initializes the configuration structure from the player INI file. +* +* INPUTS +* Config - Pointer to VQAConfig structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_INIConfig(VQAConfig *config) +{ + char *ininame; + char buf[80]; + long i; + + /* Set all Config entries to 0. */ + memset(config, 0, sizeof(VQAConfig)); + + /* Retrieve player INI filename from an enviroment variable if + * it is provided. + */ + if ((ininame = getenv("VQACFG")) == NULL) { + ininame = "PLAYER.INI"; + } + + /*------------------------------------------------------------------------- + * VIDEO MODE AND DRAW FLAGS + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + /* Get video mode from INI */ + GetINIString("Player", "PlayerMode", "MCGA", buf, 80, ininame); + + /* Search supported modes for a match. */ + i = 0; + + while (VideoModeTags[i].token != NULL) { + if (stricmp(buf, VideoModeTags[i].token) == 0) { + break; + } + i++; + } + + /* Setup for requested mode */ + switch (VideoModeTags[i].id) { + + /* MCGA direct */ + #if(VQAMONO_ON) + case VMTAG_MCGA: + config->Vmode = MCGA; + break; + + /* MCGA buffered */ + case VMTAG_MCGA_BUF: + config->Vmode = MCGA; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAMCGA_ON */ + + /* XMODE direct (320x200) */ + #if(VQAXMODE_ON) + case VMTAG_XMODE320X200: + config->Vmode = XMODE_320X200; + break; + + /* XMODE buffered (320x200) */ + case VMTAG_XMODE320X200_BUF: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x200) */ + case VMTAG_XMODE320X200_VRAM: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + + /* XMODE direct (320x240) */ + case VMTAG_XMODE320X240: + config->Vmode = XMODE_320X240; + break; + + /* XMODE buffered (320x240) */ + case VMTAG_XMODE320X240_BUF: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x240) */ + case VMTAG_XMODE320X240_VRAM: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + #endif /* VQAXMODE_ON */ + + /* VESA buffered (640x480_256) */ + #if(VQAVESA_ON) + case VMTAG_VESA640X480_BUF: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* VESA buffered scaled (640x480_256) */ + case VMTAG_VESA640X480_X2: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= (VQACFGF_BUFFER|VQACFGF_SCALEX2); + break; + + /* VESA direct (320x200_32k) */ + case VMTAG_VESA320X200: + config->Vmode = VESA_320X200_32K_1; + break; + + /* VESA buffered (320x200_32k) */ + case VMTAG_VESA320X200_BUF: + config->Vmode = VESA_320X200_32K_1; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAVESA_ON */ + + /* Default to MCGA direct */ + VMTAG_NONE: + default: + config->Vmode = MCGA; + break; + } + #endif /* VQAVIDEO_ON */ + + /* Get framerate and drawrate. */ + GetINIString("Player", "FrameRate", "-1", buf, 80, ininame); + config->FrameRate = atoi(buf); + + GetINIString("Player", "DrawRate", "Variable", buf, 80, ininame); + + if (!stricmp(buf, "Variable")) { + config->DrawRate = -1; + } else { + config->DrawRate = 0; + } + + /*------------------------------------------------------------------------- + * AUDIO SETTINGS + *-----------------------------------------------------------------------*/ + GetINIString("Player", "AudioRate", "-1", buf, 80, ininame); + config->AudioRate = atoi(buf); + + /* OptionFlags */ + GetINIString("Player", "SoundEnabled", "True", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_AUDIO; + } else { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + + /* Default audio settings. */ + config->AudioBufSize = 32768U; + config->HMIBufSize = 2048; + config->DigiHandle = -1; + config->Volume = 0x00FF; + config->DigiCard = 0xFFFF; + config->DigiPort = -1; + config->DigiIRQ = -1; + config->DigiDMA = -1; + + /* Configure sound hardware */ + GetINIString("Player", "Port", "-1", buf, 80, ininame); + + if (!stricmp(buf, "-1")) { + config->DigiPort = -1; + } else { + sscanf(buf, "%x", &config->DigiPort); + } + + GetINIString("Player", "IRQ", "-1", buf, 80, ininame); + config->DigiIRQ = atoi(buf); + GetINIString("Player", "DMA", "-1", buf, 80, ininame); + config->DigiDMA = atoi(buf); + + /*------------------------------------------------------------------------- + * GENERAL OPTIONS + *-----------------------------------------------------------------------*/ + + /* Enable/Disable single stepping */ + GetINIString("Player", "SingleStep", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_STEP; + config->DrawFlags |= VQACFGF_NOSKIP; + } else { + config->OptionFlags &= (~VQAOPTF_STEP); + } + + /* Enable/Disable Slowpalette */ + GetINIString("Player", "SlowPalette", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_SLOWPAL; + } else { + config->OptionFlags &= (~VQAOPTF_SLOWPAL); + } + + /* Enable/Disable monochrome display */ + GetINIString("Player", "MonoOutput", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_MONO; + } else { + config->OptionFlags &= (~VQAOPTF_MONO); + } + + /* Frame and codebook buffers */ + config->NumFrameBufs = 6; + config->NumCBBufs = 3; +} + + +/**************************************************************************** +* +* NAME +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +* SYNOPSIS +* VQA_DefaultConfig(Config); +* +* void VQA_DefaultConfig(VQAConfig *); +* +* FUNCTION +* Initialize configuration with default settings. +* +* INPUTS +* Config - Pointer to VQA configuration structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_DefaultConfig(VQAConfig *config) +{ + memcpy(config, &_defaultconfig, sizeof(VQAConfig)); +} + + + + diff --git a/WINVQ/VQA32/DRAWER.CPP b/WINVQ/VQA32/DRAWER.CPP new file mode 100644 index 0000000..2e97abe --- /dev/null +++ b/WINVQ/VQA32/DRAWER.CPP @@ -0,0 +1,2442 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* drawer.c +* +* DESCRIPTION +* Frame drawing and page flip control. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* June 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Configure_Drawer - Configure the drawer routines. +* +* PRIVATE +* Select_Frame - Selects frame to draw and preforms frame +* skip. +* Prepare_Frame - Process/Decompress frame information. +* DrawFrame_Xmode - Draws a frame directly to Xmode screen. +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident +* Codebook. +* PageFlip_Xmode - Page flip Xmode display. +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* PageFlip_MCGA - Page flip MCGA display. +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* DrawFrame_VESA640 - Draws a frame in VESA640 format. +* DrawFrame_VESA320_32K - Draws a frame to VESA320_32K screen. +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* PageFlip_VESA - Page flip VESA display. +* DrawFrame_Buffer - Draw a frame to a buffer. +* PageFlip_Nop - Do nothing page flip. +* UnVQ_Nop - Do nothing UnVQ. +* Mask_Rect - Sets non-drawable rectangle in image. +* Mask_Pointers - Mask vector pointer that are in the mask +* rectangle. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "unvq.h" +#include "vqaplayp.h" +#include +#include "caption.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ +static long Select_Frame(VQAHandleP *vqap); +static void Prepare_Frame(VQAData *vqabuf); + +#if(VQAMCGA_ON) +static long DrawFrame_MCGABuf(VQAHandle *vqa); +static long PageFlip_MCGABuf(VQAHandle *vqa); +static long DrawFrame_MCGA(VQAHandle *vqa); +static long PageFlip_MCGA(VQAHandle *vqa); +#endif /* VQAMCGA_ON */ + +#if(VQAXMODE_ON) +static long DrawFrame_XmodeBuf(VQAHandle *vqa); +static long DrawFrame_Xmode(VQAHandle *vqa); +static long DrawFrame_XmodeVRAM(VQAHandle *vqa); +static void PageFlip_Xmode(VQAHandle *vqabuf); +#endif /* VQAXMODE_ON */ + +#if(VQAVESA_ON) +static long DrawFrame_VESA640(VQAHandle *vqa); +static long DrawFrame_VESA320_32K(VQAHandle *vqa); +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa); +static void PageFlip_VESA(VQAHandle *vqabuf); +#endif /* VQAVESA_ON */ + +static long DrawFrame_Buffer(VQAHandle *vqa); +static long PageFlip_Nop(VQAHandle *vqa); + +#ifndef PHARLAP_TNT +static void __cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); +#else +static void __cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); +#endif + +#if(0) +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2); +static void Mask_Pointers(VQAData *vqabuf); +#endif + + +/**************************************************************************** +* +* NAME +* VQA_GetPalette - Get the palette used in the movie. +* +* SYNOPSIS +* Palette = VQA_GetPalette(VQA) +* +* char *VQA_GetPalette(VQAHandle *); +* +* FUNCTION +* Retrieve the address of the current palette used in the movie. If there +* isn't a palette available then a NULL value will be returned. +* +* INPUTS +* VQA - Pointer to VQAHandle to get palette for. +* +* RESULT +* Palette - Pointer to palette or NULL if no palette available. +* +****************************************************************************/ + +unsigned char *VQA_GetPalette(VQAHandle *vqa) +{ + VQADrawer *drawer; + unsigned char *palette = NULL; + + /* Dereference commonly used data members for quick access. */ + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + + if (drawer->CurPalSize > 0) { + palette = drawer->Palette_24; + } + + return (palette); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetPaletteSize - Get the size of the palette used in the movie. +* +* SYNOPSIS +* PalSize = VQA_GetPaletteSize(VQA) +* +* long VQA_GetPaletteSize(VQAHandle *); +* +* FUNCTION +* Retrieve the size of the current palette used in the movie. If there +* isn't a palette available then a zero size will be returned. +* +* INPUTS +* VQA - Pointer to VQAHandle to get palette for. +* +* RESULT +* PalSize - Size in bytes of the current palette. +* +****************************************************************************/ + +long VQA_GetPaletteSize(VQAHandle *vqa) +{ + VQADrawer *drawer; + + /* Dereference commonly used data members for quick access. */ + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + + return (drawer->CurPalSize); +} + + +/**************************************************************************** +* +* NAME +* VQA_Set_DrawBuffer - Set the buffer to draw the images to. +* +* SYNOPSIS +* VQA_Set_DrawBuffer(VQA, Buffer, Width, Height, XPos, YPos) +* +* void VQA_Set_DrawBuffer(VQAHandle *, unsigned char *, +* unsigned long, unsigned long, unsigned long, +* unsigned long); +* +* FUNCTION +* Set the draw buffer to the buffer provided by the client. +* +* INPUTS +* VQA - Pointer to VQAHandle to set buffer for. +* Buffer - Pointer to new image buffer. +* Width - Width of the buffer in pixels. +* Height - Height of the buffer in pixels. +* XPos - X pixel position in buffer to draw image. +* YPos - Y pixel position in buffer to draw image. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Set_DrawBuffer(VQAHandle *vqa, unsigned char *buffer, + unsigned long width, unsigned long height, + long xpos, long ypos) +{ + VQAHeader *header; + VQADrawer *drawer; + VQAConfig *config; + long origin; + + /* Dereference commonly used data members for quick access. */ + header = &((VQAHandleP *)vqa)->Header; + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /* Set the drawer buffer information. */ + drawer->ImageBuf = buffer; + drawer->ImageWidth = width; + drawer->ImageHeight = height; + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((xpos == -1) && (ypos == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((width - header->ImageWidth) / 2); + drawer->Y1 = ((height - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = xpos; + drawer->Y1 = ypos; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = xpos; + drawer->Y1 = (height - ypos); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (width - xpos); + drawer->Y1 = (height - ypos); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((width * drawer->Y1) + drawer->X1); +} + + +/**************************************************************************** +* +* NAME +* VQA_Configure_Drawer - Configure the drawer routines. +* +* SYNOPSIS +* VQA_Configure_Drawer(VQA) +* +* void VQA_Configure_Drawer(VQAHandleP *); +* +* FUNCTION +* Configure the drawing system for the current movie and configuration +* options. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Configure_Drawer(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAHeader *header; + VQADrawer *drawer; + long origin; + long blkdim; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + header = &vqap->Header; + config = &vqap->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((config->X1 == -1) && (config->Y1 == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((drawer->ImageWidth - header->ImageWidth) / 2); + drawer->Y1 = ((drawer->ImageHeight - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = config->X1; + drawer->Y1 = config->Y1; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = config->X1; + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (drawer->ImageWidth - config->X1); + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE UNVQ ROUTINE FOR THE SPECIFIED VIDEO MODE AND BLOCK SIZE. + *-----------------------------------------------------------------------*/ + + /* Pre-compute commonly used values for speed. */ + drawer->BlocksPerRow = header->ImageWidth / header->BlockWidth; + drawer->NumRows = header->ImageHeight / header->BlockHeight; + drawer->NumBlocks = drawer->BlocksPerRow * drawer->NumRows; + blkdim = BLOCK_DIM(header->BlockWidth, header->BlockHeight); + + /* Initialize draw routine vectors to a NOP routine in order to prevent + * a crash. + */ + vqabuf->UnVQ = UnVQ_Nop; + vqabuf->Page_Flip = PageFlip_Nop; + + /* If the client specifies buffering then go ahead an set the unvq + * vector. All of the buffered modes use the same unvq routines. + */ + if (config->DrawFlags & VQACFGF_BUFFER) { + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2; + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + + default: + break; + } + } + + /* Initialize the draw vectors for the specified video mode. */ + switch (config->Vmode) { + + /* MCGA */ + #if(VQAMCGA_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_MCGABuf; + vqabuf->Page_Flip = PageFlip_MCGABuf; + } else { + vqabuf->Draw_Frame = DrawFrame_MCGA; + vqabuf->Page_Flip = PageFlip_MCGA; + + /* MCGA uses the same unvq routines that are used for unvqing + * to a buffer because MCGA mode is just another buffer. However, + * instead of drawing to an allocated RAM buffer we are drawing + * directly to the VRAM video buffer. + */ + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + #if(!VQAWOOFER_ON) + vqabuf->UnVQ = UnVQ_4x2; + #else + if (config->DrawFlags & VQACFGF_WOOFER) { + vqabuf->UnVQ = UnVQ_4x2_Woofer; + } else { + vqabuf->UnVQ = UnVQ_4x2; + } + #endif + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((320 * drawer->Y1) + drawer->X1); + break; + #endif /* VQAMCGA_ON */ + + /* XMODE */ + #if(VQAXMODE_ON) + case XMODE_320X200: + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_XmodeBuf; + vqabuf->Page_Flip = PageFlip_Xmode; + } else { + vqabuf->Page_Flip = PageFlip_Xmode; + + if (config->DrawFlags & VQACFGF_VRAMCB) { + vqabuf->Draw_Frame = DrawFrame_XmodeVRAM; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_XmodeCB; + break; + #endif /* VQABLOCK_4X2 */ + } + } else { + vqabuf->Draw_Frame = DrawFrame_Xmode; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_Xmode; + break; + #endif /* VQABLOCK_4X2 */ + } + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = (((320 / 4) * drawer->Y1) + (drawer->X1 / 4)); + break; + #endif /* VQAXMODE_ON */ + + /* VESA */ + #if(VQAVESA_ON) + + /* Currently this is a buffered mode, but should be optimized for + * for screen direct. + */ + case VESA_640X480_256: + vqabuf->Draw_Frame = DrawFrame_VESA640; + vqabuf->Page_Flip = PageFlip_VESA; + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_VESA320_32KBuf; + } else { + vqabuf->Draw_Frame = DrawFrame_VESA320_32K; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_VESA320_32K; + break; + #endif + } + } + break; + #endif /* VQAVESA_ON */ + + /* Purely buffered (Video refresh is up to the client. */ + default: + vqabuf->Draw_Frame = DrawFrame_Buffer; + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((drawer->ImageWidth * drawer->Y1)+drawer->X1); + break; + } +} + + +/**************************************************************************** +* +* NAME +* Select_Frame - Selects frame to draw and preforms frame skip. +* +* SYNOPSIS +* Error = Select_Frame(VQA) +* +* long Select_Frame(VQAHandleP *); +* +* FUNCTION +* Select a frame to draw. This is were the frame skipping/delay is +* performed. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long Select_Frame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAConfig *config; + VQAFrameNode *curframe; + long desiredframe; + // MEG 11.29.95 - changed from long to unsigned long + unsigned long curtime; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Make sure the current frame is drawable. If the frame is not ready + * then we must wait for the loader to catch up. + */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + drawer->WaitsOnLoader++; + return (VQAERR_NOBUFFER); + } + + /* If single stepping then return with the next frame.*/ + if (config->OptionFlags & VQAOPTF_STEP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Find the frame # we should play (rounded to nearest frame): */ + curtime = VQA_GetTime(vqap); +// desiredframe = ((curtime * config->FrameRate) / VQA_TIMETICKS); + // MEG MOD 06.22.95 - Should look for the desired frame to draw, not load, + // right? + desiredframe = ((curtime * config->DrawRate) / VQA_TIMETICKS); + + #if(VQAMONO_ON) + drawer->DesiredFrame = desiredframe; + #endif + + /* Handle the cases where the player is going so fast that it's not time + * to draw this frame yet. + * + * - If the Drawer is using a slower frame rate than the Loader, use a + * delta-time-based wait; otherwise, use the frame number as the wait. + */ + if (config->DrawRate != config->FrameRate) { + if (curtime - drawer->LastTime < (VQA_TIMETICKS / config->DrawRate)) { + return (VQAERR_NOT_TIME); + } + } else { + if (curframe->FrameNum > desiredframe) { + return (VQAERR_NOT_TIME); + } + } + + /* Make sure we draw at least 5 frames per second */ + if ((curframe->FrameNum - drawer->LastFrame) >= (config->FrameRate / 5)) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* If frame skipping is disabled then draw every frame. */ + if (config->DrawFlags & VQACFGF_NOSKIP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Handle the case where the player is going too slow, so we have to skip + * some frames: + * + * - If this is a Key Frame, draw it + * - If this frame's # is less than what we're supposed to draw, skip it + * (Because the 1st 'desiredframe' will be 0, FrameNum MUST be typecast + * to signed WORD for the comparison; otherwise, the comparison uses + * UWORDs, and the first frame is always skipped.) + * - If this is a palette-set frame, set the palette before skipping it + * - Loop until we get the frame we need, or there's no frames available + */ + while (1) { + + /* No frame available; return */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + return (VQAERR_NOBUFFER); + } + + /* Force drawing of a Key Frame */ + if (curframe->Flags & VQAFRMF_KEY) { + break; + } + + /* Skip the frame */ + if (curframe->FrameNum < desiredframe) { + + /* Handle a palette in a skipped frame: + * + * - Stash the palette in Drawer.Palette_24 + * - Set the Drawer.Flags VQADRWF_SETPAL bit, to tell the page-flip + * routines that this palette must be set + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + + /* Un-LCW if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)curframe->Palette, + vqabuf->Max_Pal_Size); + + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Stash the palette */ + memcpy(drawer->Palette_24, curframe->Palette, curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + drawer->Flags |= VQADRWF_SETPAL; + } + + /* Invoke callback with NULL screen ptr */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Skip the frame */ + curframe->Flags = 0L; + curframe = curframe->Next; + drawer->CurFrame = curframe; + drawer->NumSkipped++; + } else { + break; + } + } + + drawer->LastFrame = curframe->FrameNum; + drawer->LastTime = curtime; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Prepare_Frame - Process/Decompress frame information. +* +* SYNOPSIS +* Prepare_Frame(VQAData) +* +* void Prepare_Frame(VQAData *); +* +* FUNCTION +* Decompress and preprocess the various frame elements (codebook, +* pointers, palette, etc...) +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Prepare_Frame(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + VQACBNode *codebook; + + /* Dereference commonly used data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + codebook = curframe->Codebook; + + /* Decompress the codebook, if needed */ + if (codebook->Flags & VQACBF_CBCOMP) { + + /* Decompress the codebook. */ + LCW_Uncompress((char *)codebook->Buffer + codebook->CBOffset, + (char *)codebook->Buffer, vqabuf->Max_CB_Size); + + /* Mark as uncompressed for the next time we use it */ + codebook->Flags &= (~VQACBF_CBCOMP); + } + + /* Decompress the palette, if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset,(char *)curframe->Palette,vqabuf->Max_Pal_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Decompress the pointer data, if needed */ + if (curframe->Flags & VQAFRMF_PTRCOMP) { + LCW_Uncompress((char *)curframe->Pointers + curframe->PtrOffset, + (char *)curframe->Pointers, vqabuf->Max_Ptr_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + } + + /* Mask the pointers */ + #if(0) + Mask_Pointers(vqabuf); + #endif +} + + +#if(VQAXMODE_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_Xmode - Draws a frame in Xmode format (Screen direct). +* +* SYNOPSIS +* Error = DrawFrame_Xmode(VQA) +* +* long DrawFrame_Xmode(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_XmodeBuf(VQA) +* +* long DrawFrame_XmodeBuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into ImageBuf +* - Copy ImageBuf to video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf into the correct part of the +* Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAHeader *header; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + header = &((VQAHandleP *)vqa)->Header; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + header->ImageWidth); + + /* Copy the buffer */ + scrn = GetXHidPage(); + + if ((header->ImageWidth < 320) || (header->ImageHeight < 200)) { + Xmode_Blit(drawer->ImageBuf, scrn + drawer->ScreenOffset, + header->ImageWidth, header->ImageHeight); + } else { + Xmode_BufferCopy_320x200(drawer->ImageBuf,scrn + drawer->ScreenOffset); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident Codebook. +* +* SYNOPSIS +* Error = DrawFrame_XmodeVRAM(VQA) +* +* long DrawFrame_XmodeVRAM(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Download Codebook if it isn't already (do this before skipping +* frames, so Select_Frame will smooth out the delay) +* - Skip frames +* - UnLCW frame +* - Convert Pointers to VRAM format +* - Un-VQ by copying codebook blocks within VRAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeVRAM(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Download codebook to VRAM */ + if ((curframe->Codebook->Flags & VQACBF_DOWNLOADED) == 0) { + Upload_4x2CB(curframe->Codebook->Buffer, + ((VQAHandleP *)vqa)->Header.CBentries + + ((VQAHandleP *)vqa)->Header.Num1Colors); + + curframe->Codebook->Flags |= VQACBF_DOWNLOADED; + } + + /* Convert pointers to VRAM format */ + XlatePointers(curframe->Pointers, drawer->NumBlocks); + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Xmode - Page flip Xmode display. +* +* SYNOPSIS +* PageFlip_Xmode(VQA) +* +* void PageFlip_Xmode(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned char *pal; + VQAConfig *config; + long palsize; + long slowpal; + + /* Dereference commonly used data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for NoVB (active scan) + * - Flip the page (doesn't take effect until next active scan + * - Wait for VB + * - Set the palette + * - Otherwise, just flip the page + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } else { + FlipXPage(); + } +} +#endif /* VQAXMODE_ON */ + + +#if(VQAMCGA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* +* SYNOPSIS +* Error = DrawFrame_MCGA(VQA) +* +* long DrawFrame_MCGA(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set palette +* UnVQ to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + /* Dereference commonly used data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Select the frame to draw. */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGA - Page flip MCGA display. +* +* SYNOPSIS +* PageFlip_MCGA(VQA) +* +* long PageFlip_MCGA(VQAHandle *); +* +* FUNCTION +* Since the MCGA mode only has one buffer, the drawing is actually done +* at this point. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *ptrs; + unsigned char *cb; + long palsize; + long slowpal; + long blocksperrow; + long numrows; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /*------------------------------------------------------------------------- + * WAIT FOR THE VERTICAL BLANK TO SET THE PALETTE. + *-----------------------------------------------------------------------*/ + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Wait for the VBlank. */ + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + /* Set the palette. */ + if (curframe->Flags & VQAFRMF_PALETTE) { + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + } + + /*------------------------------------------------------------------------- + * UNVQ THE FRAME DIRECTLY TO THE MCGA SCREEN. + *-----------------------------------------------------------------------*/ + + /* Get screen address, blocks per row and number of rows. */ + #ifndef PHARLAP_TNT + scrn = (unsigned char *)(0xA0000 + (unsigned long)drawer->ScreenOffset); + #else + FP_SET(scrn, drawer->ScreenOffset, 0x1C); + #endif + + blocksperrow = drawer->BlocksPerRow; + numrows = drawer->NumRows; + ptrs = curframe->Pointers; + cb = curframe->Codebook->Buffer; + vqabuf->UnVQ(cb, ptrs, scrn, blocksperrow, numrows, 320); + + #if(VQACAPTIONS_ON) + /* Process captions. */ + if ((((VQAHandleP *)vqa)->Caption != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + DoCaptions(((VQAHandleP *)vqa)->Caption, curframe->FrameNum); + } + + if ((((VQAHandleP *)vqa)->EVA != NULL) + && (config->OptionFlags & VQAOPTF_EVA)) { + DoCaptions(((VQAHandleP *)vqa)->EVA, curframe->FrameNum); + } + + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_MCGABuf(VQA) +* +* long DrawFrame_MCGABuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled (can't use imgbuf til User_Update's done) +* - Un-VQ into ImageBuf +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set Palette from Flipper.CurFrame +* copy ImageBuf to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf onto the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf; + #else + FP_SET(scrn, drawer->ImageBuf, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* +* SYNOPSIS +* PageFlip_MCGABuf(VQA) +* +* void PageFlip_MCGABuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *buf; + unsigned char *scrn; + long palsize; + long slowpal; + long imgwidth; + long imgheight; + + /* Derefernce commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for the vertical blank + * - Set the palette + * - Copy ImageBuf to SEENPAGE: + * - use blit routine if image is smaller than full-screen, since the + * buffer copy assumes a full-screen image + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Draw image to the screen. */ + imgwidth = ((VQAHandleP *)vqa)->Header.ImageWidth; + imgheight = ((VQAHandleP *)vqa)->Header.ImageHeight; + buf = drawer->ImageBuf; + + #ifndef PHARLAP_TNT + scrn = (unsigned char *)0xA0000; + #endif + + if ((imgwidth < 320) || (imgheight < 200)) { + #ifndef PHARLAP_TNT + scrn += drawer->ScreenOffset; + #else + scrn = (unsigned char *)drawer->ScreenOffset; + #endif + + MCGA_Blit(buf, scrn, imgwidth, imgheight); + } else { + MCGA_BufferCopy(buf, NULL); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} +#endif /* VQAMCGA_ON */ + + +#if(VQAVESA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_VESA640 - Draws a frame in VESA 640 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA640(VQA) +* +* long DrawFrame_VESA640(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA640(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Drawer.WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf + drawer->ScreenOffset; + #else + FP_SET(scrn, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32K - Draws a frame in VESA 320 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32K(VQA) +* +* long DrawFrame_VESA320_32K(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32K(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Translate palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* UnVQ directly to screen */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->Palette_15, grain, 0, 0); + #else + { + FARPTR temp; + + FP_SET(temp, drawer->Palette_15, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, temp, + grain, 0, 0); + } + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for VQ_Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32KBuf(VQA) +* +* long DrawFrame_VESA320_32KBuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* UnVQ buffered mode */ + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + #else + { + FARPTR scrn; + + FP_SET(scrn, drawer->ImageBuf, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + } + #endif + + /* Translate the palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + Buf_320x200_To_VESA_32K(drawer->ImageBuf, drawer->Palette_15, grain); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Flags &= (~VQADATF_DSLEEP); + drawer->WaitsOnFlipper++; + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_VESA - Page flip VESA display. +* +* SYNOPSIS +* PageFlip_VESA(VQA) +* +* void PageFlip_VESA(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_VESA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + long palsize; + long slowpal; + long grain; + + /* Dereference date members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Set the palette */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + switch (((VQAHandleP *)vqa)->Header.image_width) { + case 320: + if (config->DrawFlags & VQACFGF_SCALEX2) { + Buf_320x200_To_VESA_640x400(drawer->ImageBuf, grain); + } else { + Buf_320x200_To_VESA_320x200(drawer->ImageBuf, grain); + } + break; + + default: + VESA_Blit_640x480(drawer->Display, drawer->ImageBuf, drawer->X1, + drawer->Y1, ((VQAHandleP *)vqa)->Header.image_width, + ((VQAHandleP *)vqa)->Header.image_height); + break; + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } +} +#endif /* VQAVESA_ON */ + + +/**************************************************************************** +* +* NAME +* DrawFrame_Buffer - Draw a frame to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_Buffer(VQA) +* +* long DrawFrame_Buffere(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* Error - 0 if successful, otherwise VQAERR_??? error code. +* +****************************************************************************/ + +extern void __cdecl Set_Palette(void *palette); +extern void Flag_To_Set_Palette(unsigned char *palette,long numbytes,unsigned long slowpal); +static long DrawFrame_Buffer(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + unsigned char *pal; + long palsize; + long slowpal; + + #ifndef PHARLAP_TNT + unsigned char *buff; + #else + FARPTR buff; + #endif + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + buff = (unsigned char *)(drawer->ImageBuf + drawer->ScreenOffset); + #else + FP_SET(buff, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Set the palette if neccessary */ + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + Flag_To_Set_Palette(pal, palsize, slowpal); + curframe->Flags &= ~VQAFRMF_PALETTE; + drawer->Flags &= ~VQADRWF_SETPAL; + } + + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, buff, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* UnVQ_Nop - Do nothing UnVQ. +* +* SYNOPSIS +* UnVQ_Nop(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +* +* void UnVQ_Nop(unsigned char *, unsigned char *, unsigned char *, +* unsigned long, unsigned long, unsigned long); +* FUNCTION +* +* INPUTS +* Codebook - Not used. (Prototype placeholder) +* Pointers - Not used. (Prototype placeholder) +* Buffer - Not used. (Prototype placeholder) +* BPR - Not used. (Prototype placeholder) +* Rows - Not used. (Prototype placeholder) +* BufWidth - Not used. (Prototype placeholder) +* +* RESULT +* NONE +* +****************************************************************************/ + +#ifndef PHARLAP_TNT +static void __cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth) +#else +static void __cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth) +#endif +{ + /* Suppress compiler warnings */ + codebook = codebook; + pointers = pointers; + buffer = buffer; + blocksperrow = blocksperrow; + numrows = numrows; + bufwidth = bufwidth; +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Nop - Do nothing page flip. +* +* SYNOPSIS +* PageFlip_Nop(VQA) +* +* void PageFlip_Nop(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static long PageFlip_Nop(VQAHandle *vqa) +{ + vqa = vqa; + + return (0); +} + + +#if(0) +/**************************************************************************** +* +* NAME +* Mask_Rect - Sets non-drawable rectangle in image. +* +* SYNOPSIS +* Mask_Rect(VQA, X1, Y1, X2, Y2) +* +* void Mask_Rect(VQAHandle *, unsigned long, unsigned long, +* unsigned long, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* X1 - X coordinate of upper-left corner +* Y1 - Y coordinate of upper-left corner +* X2 - X coordinate of lower-right corner +* Y2 - Y coordinate of lower-right corner +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAHeader *header; + long blocks_per_row; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + header = &((VQAHandleP *)vqa)->Header; + drawer = &vqabuf->Drawer; + + /* Clip coords to image size */ + if (x1 < vqabuf->Drawer.X1) { + x1 = vqabuf->Drawer.X1; + } + + if (y1 < vqabuf->Drawer.Y1) { + y1 = vqabuf->Drawer.Y1; + } + + if (x2 > vqabuf->Drawer.X2) { + x2 = vqabuf->Drawer.X2; + } + + if (y2 > vqabuf->Drawer.Y2) { + y2 = vqabuf->Drawer.Y2; + } + + /* Convert pixel coords to block coords */ + x1 /= header->block_width; + x2 /= header->block_width; + y1 /= header->block_height; + y2 /= header->block_height; + + /* Compute the mask values */ + blocks_per_row = (header->image_width / header->block_width); + vqabuf->Drawer.MaskStart = blocks_per_row * y1 + x1; + + if (x1 == x2) { + drawer->MaskWidth = 0; + } else { + drawer->MaskWidth = x2 - x1 + 1; + } + + if (y1 == y2) { + drawer->MaskHeight = 0; + } else { + drawer->MaskHeight = y2 - y1 + 1; + } +} + + +/**************************************************************************** +* +* NAME +* Mask_Pointers - Mask vector pointer that are in the mask rectangle. +* +* SYNOPSIS +* Mask_Pointers(VQAData) +* +* void Mask_Pointers(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Pointers(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned long *ptr; + unsigned long i,j; + unsigned long start; + + /* Dereference data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + start = vqabuf->Drawer.MaskStart; + + for (i = 0; i < drawer->MaskHeight; i++) { + ptr = (unsigned long *)(curframe->Pointers) + start; + + for (j = 0; j < drawer->MaskWidth; j++) { + ptr[j] = VQA_MASK_POINTER; + } + + start += drawer->BlocksPerRow; + } +} +#endif + diff --git a/WINVQ/VQA32/DSTREAM.CPP b/WINVQ/VQA32/DSTREAM.CPP new file mode 100644 index 0000000..047e259 --- /dev/null +++ b/WINVQ/VQA32/DSTREAM.CPP @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. +* +* FILE +* dstream.c +* +* DESCRIPTION +* DOS IO handler. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* PRIVATE +* VQADOSHandler - Standard DOS IO handler. +* +****************************************************************************/ + +#include +#include +#include "vqaplayp.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes); + + +/**************************************************************************** +* +* NAME +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* SYNOPSIS +* VQA_InitAsDOS(VQA) +* +* VQA_InitAsDOS(VQAHandle *); +* +* FUNCTION +* Initialize the IO of the specified handle as a standard DOS access. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize as DOS. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_InitAsDOS(VQAHandle *vqa) +{ + ((VQAHandleP *)vqa)->IOHandler = VQADOSHandler; +} + + +/**************************************************************************** +* +* NAME +* VQADOSHandler - Standard DOS IO handler. +* +* SYNOPSIS +* Error = VQADOSHandler(VQA, Action, Buffer, NBytes) +* +* unsigned long VQADOSHandler(VQAHandle *, long, long, long); +* +* FUNCTION +* Perform the requested action on the standard DOS file system. +* +* INPUTS +* VQA - VQAHandle to operate on. +* Action - Action to perform. +* Buffer - Buffer to Read/Write to/from. +* NBytes - Number of bytes to operate on. +* +* RESULT +* Error - 0 if successful, otherwise error. +* +****************************************************************************/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes) +{ + long fh; + long error; + + fh = vqa->VQAio; + + /* Perform the action specified by the IO command */ + switch (action) { + + /* VQACMD_READ means read NBytes and place it in the memory + * pointed to by Buffer. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_READ. + */ + case VQACMD_READ: + error = (read(fh, buffer, nbytes) != nbytes); + break; + + /* VQACMD_WRITE is analogous to VQACMD_READ. + * + * Writing is not allowed to the VQA file, VQA library will remap the + * error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* VQACMD_SEEK asks that you perform a seek relative to the current + * position. NBytes is a signed number, indicating seek direction + * (positive for forward, negative for backward). Buffer has no meaning + * here. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (lseek(fh, nbytes, (long)buffer) == -1); + break; + + /* VQACMD_OPEN asks that you open the file for access. */ + case VQACMD_OPEN: + error = open((char *)buffer, (O_RDONLY|O_BINARY)); + + if (error != -1) { + vqa->VQAio = error; + error = 0; + } + break; + + case VQACMD_CLOSE: + close(fh); + error = 0; + break; + + /* VQACMD_INIT means to prepare your IO for reading. This is used for + * certain IOs that can't be read immediately upon opening, and need + * further preparation. This operation is allowed to fail; the error code + * will be returned directly to the client. + */ + case VQACMD_INIT: + + /* IFFCMD_CLEANUP means to terminate the transaction with the associated + * IO. This is used for IOs that can't simply be closed. This operation + * is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return (error); +} + diff --git a/WINVQ/VQA32/LOADER.CPP b/WINVQ/VQA32/LOADER.CPP new file mode 100644 index 0000000..63401ef --- /dev/null +++ b/WINVQ/VQA32/LOADER.CPP @@ -0,0 +1,2953 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* loader.c +* +* DESCRIPTION +* Stream loading and pre-processing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 21, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Open - Open a VQA file to play. +* VQA_Close - Close an opened VQA file. +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* PRIVATE +* AllocBuffers - Allocates the numerous VQA play buffers +* FreeBuffers - Frees the VQA play buffers +* PrimeBuffers - Pre-Load the internal buffers. +* Load_FINF - Loads the Frame Info Table. +* Load_VQHD - Loads a VQA Header. +* Load_CBF0 - Loads a full, uncompressed codebook +* Load_CBFZ - Loads a full, compressed codebook +* Load_CBP0 - Loads a partial uncompressed codebook +* Load_CBPZ - Loads a partial compressed codebook +* Load_CPL0 - Loads an uncompressed palette +* Load_CPLZ - Loads a compressed palette +* Load_VPT0 - Loads uncompressed pointers +* Load_VPTZ - Loads compressed pointers +* Load_VQF - Loads a VQ Frame chunk +* Load_SND0 - Loads an uncompressed sound chunk +* Load_SND1 - Loads a compressed sound chunk +* Load_AudFrame - Loads blocks from separate audio file, if needed. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include +#include "caption.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config); +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header); +static long PrimeBuffers(VQAHandle *vqa); +static long Load_VQF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAAUDIO_ON) +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAVOC_ON) +static void Load_AudFrame(VQAHandleP *vqap); +#endif /* VQAVOC_ON */ + +#endif /* VQAAUDIO_ON */ + + +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/**************************************************************************** +* +* NAME +* VQA_Open - Open a VQA file to play. +* +* SYNOPSIS +* Error = VQA_Open(VQA, Name, Config) +* +* long VQA_Open(VQAHandle *, char *, VQAConfig *); +* +* FUNCTION +* - Open a VQA file for reading. +* - Validate that it is an IFF file, of the VQA type. +* - Read the VQA header. +* - Open a VOC file for playback, if requested. +* - Set the Loader's frame rate, if the caller's Config structure's +* FrameRate is set to -1 +* - Set the Drawer's frame rate, if the caller's Config structure's +* DrawRate is set to -1 +* +* INPUTS +* VQA - Pointer to initialized handle. Obtained by VQA_Alloc(). +* Name - Pointer to name of VQA file to open. +* Config - Pointer to initialized VQA configuration structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_ error code. +* +****************************************************************************/ + +#define OPEN_VQHD (1<<0) +#define OPEN_FINF (1<<1) +#define OPEN_CAPTIONS (1<<2) +#define OPEN_EVA (1<<3) +extern int VQAMovieDone; +long VQA_Open(VQAHandle *vqa, char const *filename, VQAConfig *config) +{ + VQAHandleP *vqap; + VQAHeader *header; + ChunkHeader chunk; + long max_frm_size; + long i; + long done; + long found; + char *ptr; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + header = &vqap->Header; + + VQAMovieDone = 0; + /*------------------------------------------------------------------------- + * VERIFY VALIDITY OF VQA FILE. + *-----------------------------------------------------------------------*/ + + /* Open the file. */ + if (vqap->IOHandler(vqa, VQACMD_OPEN, (void *)filename, 0)) { + return (VQAERR_OPEN); + } + + /* Read the file ID & Size */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify an IFF FORM */ + if ((chunk.id != ID_FORM) || (chunk.size == 0)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read in WVQA ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 4)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify VQA */ + if (chunk.id != ID_WVQA) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE PLAYERS CONFIGURATION + *-----------------------------------------------------------------------*/ + + /* Use the clients configuration if they provided one. */ + if (config != NULL) { + memcpy(&vqap->Config, config, sizeof(VQAConfig)); + } else { + VQA_DefaultConfig(&vqap->Config); + } + + /* Use the internal configuration structure from now on. */ + config = &vqap->Config; + + /*------------------------------------------------------------------------- + * PROCESS THE PRE-FRAME CHUNKS (VQHD, CAP, FINF, ETC...) + *-----------------------------------------------------------------------*/ + found = 0; + done = 0; + + while (!done) { + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + chunk.size = REVERSE_LONG(chunk.size); + + switch (chunk.id) { + + /*--------------------------------------------------------------------- + * READ IN THE VQA HEADER. + *-------------------------------------------------------------------*/ + case ID_VQHD: + if (chunk.size != sizeof(VQAHeader)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read the header data. */ + if (vqap->IOHandler(vqa, VQACMD_READ, header, PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /*------------------------------------------------------------------- + * SETUP THE CONFIGURATION FROM THE HEADER. + *-----------------------------------------------------------------*/ + if (config->ImageWidth == -1) { + config->ImageWidth = header->ImageWidth; + } + + if (config->ImageHeight == -1) { + config->ImageHeight = header->ImageHeight; + } + + /* If Loaders frame rate is -1 then use the value from the header. */ + if (config->FrameRate == -1) { + config->FrameRate = header->FPS; + } + + /* If Drawers frame rate is -1 then use the value from the header, + * which will result in a "variable" frame rate. + */ + if (config->DrawRate == -1) { + config->DrawRate = header->FPS; + } + + /* Finally, if the DrawRate was set to -1 or 0 (ie MaxRate contained + * bogus values), set it to the header value. + */ + if ((config->DrawRate == -1) || (config->DrawRate == 0)) { + config->DrawRate = header->FPS; + } + + #if(VQAAUDIO_ON) + /* If an alternate audio track is not available then turn it off. + * This enables the primary audio track to be played. + */ + if ((header->Version > VQAHD_VER1) + && !(header->Flags & VQAHDF_ALTAUDIO)) { + config->OptionFlags &= ~VQAOPTF_ALTAUDIO; + } + #endif + + /*------------------------------------------------------------------- + * ALLOCATE THE BUFFERS THAT WE NEED TO PLAY THE VQA. + *-----------------------------------------------------------------*/ + if ((vqap->VQABuf = AllocBuffers(header, config)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_VQHD; + break; + + #if( VQACAPTIONS_ON ) + + /*--------------------------------------------------------------------- + * READ IN AND OPEN THE CAPTIONS STREAM. + *-------------------------------------------------------------------*/ + case ID_CAP0: + if ((config->CapFont != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + + short size = 0; + + /* Get uncompressed size of captions. */ + if (vqap->IOHandler(vqa, VQACMD_READ, &size, sizeof(short))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Allocate buffer for captions. */ + i = size + 50; + + if ((ptr = (char *)malloc(i)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /* Read in the captions chunk. */ + i -= PADSIZE(chunk.size); + + if (vqap->IOHandler(vqa, VQACMD_READ, (ptr + i), + PADSIZE(chunk.size - sizeof(short)))) { + + free(ptr); + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Decompress the captions. */ + LCW_Uncompress((ptr + i), ptr, size); + vqap->Caption = OpenCaptions(ptr, config->CapFont); + + if (vqap->Caption == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_CAPTIONS; + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + } + break; + + + case ID_EVA0: + if ((config->EVAFont != NULL) + && (config->OptionFlags & VQAOPTF_EVA)) { + + short size = 0; + + /* Get uncompressed size of captions. */ + if (vqap->IOHandler(vqa, VQACMD_READ, &size, sizeof(short))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Allocate buffer for captions. */ + i = size + 50; + + if ((ptr = (char *)malloc(i)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /* Read in the captions chunk. */ + i -= PADSIZE(chunk.size); + + if (vqap->IOHandler(vqa, VQACMD_READ, (ptr + i), + PADSIZE(chunk.size - sizeof(short)))) { + free (ptr); + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Decompress the captions. */ + LCW_Uncompress((ptr + i), ptr, size); + vqap->EVA = OpenCaptions(ptr, config->EVAFont); + + if (vqap->EVA == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_EVA; + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + } + break; + + + + #endif + + /*--------------------------------------------------------------------- + * READ FRAME INFORMATION + *-------------------------------------------------------------------*/ + case ID_FINF: + if (Load_FINF(vqap, chunk.size)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + done = 1; + break; + + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VIDEO SYSTEM IF WE ARE REQUIRED TO HANDLE THAT. + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + if ((vqap->VQABuf->Drawer.Display = SetVideoMode(config->Vmode)) == 0) { + VQA_Close(vqa); + return (VQAERR_VIDEO); + } + + /* Set the VBIBit polarity. */ + vqap->VQABuf->VBIBit = GetVBIBit(); + #else + if (config->VBIBit == -1) { + config->VBIBit = TestVBIBit(); + } + + vqap->VQABuf->VBIBit = config->VBIBit; + + #if( VQACAPTIONS_ON ) + + if (found & OPEN_CAPTIONS|OPEN_EVA) { + SetDAC(251,255,255,255); /* White */ + SetDAC(252,255,000,000); /* Red */ + SetDAC(253,000,255,000); /* Green */ + SetDAC(254,255,255,255); + SetDAC(255,255,000,255); /* Cycle */ + } + + #endif + + #endif /* VQAVIDEO_ON */ + + /*------------------------------------------------------------------------- + * AUDIO TRACK OVERRIDE FROM EXTERNAL FILE (.VOC) + *-----------------------------------------------------------------------*/ + + /* Open VOC file if one is requested. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (config->VocFile != NULL) { + vqap->vocfh = open(config->VocFile, (O_RDONLY|O_BINARY)); + } else { + vqap->vocfh = -1; + } + + /* Make sure we won't try to play audio. */ + if ((vqap->vocfh == -1) && ((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #else /* VQAVOC_ON */ + + /* If the movie does not contain an audio track make sure we won't try + * to play one. + */ + if (((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #endif /* VQAVOC_ON */ + + /*------------------------------------------------------------------------- + * INITIALIZE THE AUDIO PLAYBACK/TIMING SYSTEM. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Open HMI audio resource for playback. */ + if (VQA_OpenAudio(vqap , MainWindow)) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + + /* Initialize ADPCM information structure for audio stream. */ + VQA_sosCODECInitStream(&audio->ADPCM_Info); + + if (header->Version == VQAHD_VER1) { + audio->ADPCM_Info.wBitSize = 8; + audio->ADPCM_Info.dwUnCompSize = (22050L/header->FPS) * header->Frames; + audio->ADPCM_Info.wChannels = 1; + } else { + audio->ADPCM_Info.wBitSize = audio->BitsPerSample; + audio->ADPCM_Info.dwUnCompSize = (((audio->SampleRate / header->FPS) + * (audio->BitsPerSample >> 3)) * audio->Channels) * header->Frames; + + audio->ADPCM_Info.wChannels = audio->Channels; + } + + audio->ADPCM_Info.dwCompSize = audio->ADPCM_Info.dwUnCompSize + / (audio->ADPCM_Info.wBitSize / 4); + } + + /* Turn off audio if the HMI DigiHandle is invalid. */ +#if (!VQADIRECT_SOUND) + if (config->DigiHandle == -1) { + config->OptionFlags &= ~VQAOPTF_AUDIO; + } + + /* Setup the timer interrupt if the client requests it for the timing + * source. + */ + if (!(config->OptionFlags & VQAOPTF_AUDIO) + || (config->TimerMethod == VQA_TMETHOD_INT)) { + + /* Start HMI timer system for timing. */ + if (VQA_StartTimerInt(vqap, (config->OptionFlags & VQAOPTF_HMIINIT))) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + } +#endif //VQADIRECT_SOUND + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * PRIME THE BUFFERS BY PRE-LOADING THEM WITH FRAME DATA. + *-----------------------------------------------------------------------*/ + if (PrimeBuffers(vqa) != 0) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_Close - Close an opened VQA file. +* +* SYNOPSIS +* VQA_Close(VQA) +* +* void VQA_Close(VQAHandle *); +* +* FUNCTION +* Close the file that was opened with VQA_Open(). +* +* INPUTS +* VQA - Pointer VQAHandle to close. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Close(VQAHandle *vqa) +{ + long (*iohandler)(VQAHandle *, long, void *, long); + + /* Restore video mode to text. */ + #if(VQAVIDEO_ON) + SetVideoMode(TEXT_VIDEO); + #endif /* VQAVIDEO_ON */ + + /* Shutdown audio/timing system. */ + #if(VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->Config.OptionFlags & VQAOPTF_AUDIO) { + VQA_CloseAudio((VQAHandleP *)vqa); + } else { + VQA_StopTimerInt((VQAHandleP *)vqa); + } + #endif /* VQAAUDIO_ON */ + + #if( VQACAPTIONS_ON ) + + /* Free captions. */ + if (((VQAHandleP *)vqa)->Caption != NULL) { + if (((VQAHandleP *)vqa)->Caption->Buffer != NULL) { + free(((VQAHandleP *)vqa)->Caption->Buffer); + } + + CloseCaptions(((VQAHandleP *)vqa)->Caption); + } + + /* Free EVA. */ + if (((VQAHandleP *)vqa)->EVA != NULL) { + if (((VQAHandleP *)vqa)->EVA->Buffer != NULL) { + free(((VQAHandleP *)vqa)->EVA->Buffer); + } + + CloseCaptions(((VQAHandleP *)vqa)->EVA); + } + + + #endif + + /* Free memory */ + if (((VQAHandleP *)vqa)->VQABuf != NULL) { + FreeBuffers(((VQAHandleP *)vqa)->VQABuf, &((VQAHandleP *)vqa)->Config, + &((VQAHandleP *)vqa)->Header); + } + + /* Close the VOC override file if one was opened */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->vocfh != -1) { + close(((VQAHandleP *)vqa)->vocfh); + } + #endif /* VQAVOC_ON */ + + /* Close the VQA file */ + ((VQAHandleP *)vqa)->IOHandler(vqa, VQACMD_CLOSE, NULL, 0); + + /* Reset the VQAHandle */ + iohandler = ((VQAHandleP *)vqa)->IOHandler; + memset(vqa, 0, sizeof(VQAHandleP)); + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* +* SYNOPSIS +* Error = VQA_LoadFrame(VQA) +* +* long VQA_LoadFrame(VQAHandle *); +* +* FUNCTION +* The codebook is split up such that the last frame of every group gets +* a new, complete codebook, ready for the next group. The first codebook +* in the VQA is a full codebook, and goes with the first frame's data. +* Partial codebooks are stored per frame after that, and they add up to +* a full codebook just before the first frame for the next group is read. +* +* (Currently, this routine can read either the older non-frame-grouped +* VQA file format, or the new frame-chunk format. For the older format, +* it's assumed that the last chunk in a frame is the pointer data.) +* +* This routine also does a sort of "cooperative multitasking". If the +* Loader hits a "wait state" where it has to wait on the audio to finish +* playing before it can continue to load, it sets a "sleep" flag and +* just returns. The sleep flag is checked on entry to see if it needs +* to jump to the proper execution point. This may improve performance on +* some platforms, but it also allows the Loader to be called regardless +* of the size of the buffers; if the buffers fill up or the audio fails +* to play, the Loader won't just get stuck. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long VQA_LoadFrame(VQAHandle *vqa) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQADrawer *drawer; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + long frame_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + curframe = loader->CurFrame; + chunk = &loader->CurChunkHdr; + + /* We have reached the end of the file if we loaded all the frames. */ + if (loader->CurFrameNum >= vqap->Header.Frames) { + return (VQAERR_EOF); + } + + /* If we're reading audio from a VOC file then service that requirement. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (vqap->vocfh != -1) { + Load_AudFrame(vqap); + } + #endif /* VQAAUDIO_ON & VQAVOC_ON */ + + /* If no buffer is available for loading then return. This allows the + * drawer to service one of the buffers more readily. (We'll wait for one + * to free up). + */ + if (curframe->Flags & VQAFRMF_LOADED) { + loader->WaitsOnDrawer++; + return (VQAERR_NOBUFFER); + } + + /* If we're not sleeping, initialize */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + frame_loaded = 0; + loader->FrameSize = 0; + + /* Initialize the codebook ptr for the frame we're about to load: + * (This frame's codebook is the last full codebook; we have to init it + * now, because if we're on the last frame in a group, we'll get a new + * FullCB pointer.) + */ + curframe->Codebook = loader->FullCB; + } + + /*------------------------------------------------------------------------- + * THE MAIN LOADER LOOP + *-----------------------------------------------------------------------*/ + while (frame_loaded == 0) { + + /* Read new chunk, only if we're not sleeping */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + + /* Read chunk ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + loader->FrameSize += iffsize; + } + + /* Handle each chunk type */ + switch (chunk->id) { + + /* VQ Normal Frame */ + case ID_VQFR: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* VQ Key Frame */ + case ID_VQFK: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + memcpy(drawer->Palette_24,curframe->Palette,curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + drawer->CurPalSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)drawer->Palette_24, + vqabuf->Max_Pal_Size); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Pointer data Key (Must draw) */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Uncompressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + #if(VQAAUDIO_ON) + case ID_SND0: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA0: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* Compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND1: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA1: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* HMI ADPCM compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND2: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA2: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + #endif + + /* Skip any unknown chunks. */ + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + break; + } + } + + /* Update maximum frame size stat. */ + if ((loader->CurFrameNum>0) && (loader->FrameSize>loader->MaxFrameSize)) { + loader->MaxFrameSize = loader->FrameSize; + } + + /*------------------------------------------------------------------------- + * SET UP THE FRAME FOR DRAWING. + *-----------------------------------------------------------------------*/ + + /* Set the frame # */ + curframe->FrameNum = loader->CurFrameNum; + loader->CurFrameNum++; + + /* Update data for mono output */ + loader->LastFrameNum = loader->CurFrameNum; + + /* Loader is finished with this frame; tell Drawer to draw it */ + curframe->Flags |= VQAFRMF_LOADED; + loader->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* SYNOPSIS +* Frame = VQA_SeekFrame(VQA, Frame, FromWhere) +* +* long VQA_SeekFrame(VQAHandle *, long, long); +* +* FUNCTION +* This function sets the movie stream to the new frame specified by +* the 'offset' parameter. 'FromWhere' is a symbolic constant that is used +* to specify from where in the stream offset should be applied. +* +* INPUTS +* VQA - Pointer to VQAHandle of movie to seek into. +* Frame - Frame to seek to. +* FromWhere - Relative position indicator. +* +* RESULT +* Frame - New frame position or -1 if error. +* +****************************************************************************/ + +long VQA_SeekFrame(VQAHandle *vqa, long framenum, long fromwhere) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQAHeader *header; + VQAFrameNode *frame; + VQAConfig *config; + long group; + long i; + long rc = VQAERR_NONE; + + #if(VQAAUDIO_ON) + VQAAudio *audio; + long audio_on; + #endif + + /* Dereference commonly used data members for quick access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + header = &vqap->Header; + config = &vqap->Config; + + fromwhere = fromwhere; + + #if(VQAAUDIO_ON) + audio = &vqabuf->Audio; + + /* Stop audio playback. */ + audio_on = (audio->Flags & VQAAUDF_ISPLAYING); + VQA_StopAudio(vqap); + #endif + + /* Make sure the requested frame is valid and the frame information + * array is allocated before continuing. + */ + if ((framenum < header->Frames) && (vqabuf->Foff != NULL)) { + + /* Find and load the most recent palette. */ + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + + /* Get the current frame. */ + frame = loader->CurFrame; + + for (i = framenum; i >= 0; i--) { + if (vqabuf->Foff[i] & VQAFINF_PAL) { + + /* Seek to the palette frame. */ + rc = vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[i])); + + /* Fool the loader into thinking this frame is empty. */ + if (!rc) { + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = 0; + frame->Flags = 0; + + /* Load the frame with the palette. */ + if (VQA_LoadFrame(vqa) == 0) { + + /* Decompress the palette if neccessary.*/ + if (frame->Flags & VQAFRMF_PALCOMP) { + frame->PaletteSize = LCW_Uncompress((char *)frame->Palette + + frame->PalOffset, (char *)frame->Palette, + vqabuf->Max_Pal_Size); + } + + SetPalette(frame->Palette, frame->PaletteSize, 0); + } + } else { + rc = VQAERR_SEEK; + } + break; + } + } + } + + /* Build the codebook for the frame we are seeking to. */ + if (!rc) { + + /* Compute the starting group frame of the requested frame. */ + group = (framenum / header->Groupsize); + group = (group * header->Groupsize); + + /* The codebook for the group we want to goto is found in the previous + * group, with the exception of the very first group. + */ + if (group >= header->Groupsize) { + group -= header->Groupsize; + } + + /* Seek to the start of the group containing the partial codebooks for + * the target frame. + */ + if (!vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[group]))) { + + /* Throw away any audio frames that were loaded. */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) + && (audio->Buffer != NULL)) { + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + memset(audio->Buffer, 0, config->AudioBufSize); + + /* Position the audio buffer to 1/2 second. */ + audio->AudBufPos = (long)(((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)) / 2); + + /* Mark 1/2 second of the audio buffer as loaded. */ + for (i = 0; i < (audio->AudBufPos / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + } + #endif + + /* Force the loader to the desired frame. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = group; + + /* Load frames up to the target frame collecting partial codebooks + * along the way. + */ + for (i = 0; i < (framenum - group); i++) { + + /* Fool the loader into thinking the frame has been drawn. */ + loader->CurFrame->Flags = 0; + + #if(VQAAUDIO_ON) + audio->TempBufLen = 0; + #endif + + /* Load the frame. */ + if ((rc = VQA_LoadFrame(vqa)) != 0) { + if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + break; + } else { + rc = 0; + } + } + } + + /* If everything is okay, then re-prime the buffers. */ + if (!rc) { + + /* Mark all the frames except the current one as empty. */ + loader->CurFrame->Flags = 0; + frame = loader->CurFrame->Next; + + while (frame != loader->CurFrame) { + frame->Flags = 0; + frame = frame->Next; + } + + /* Set the drawer to the current frame and the loader + * to the next. + */ + vqabuf->Drawer.CurFrame = loader->CurFrame; + + /* Prime the buffers for the new position. */ + rc = PrimeBuffers(vqa); + + /* An end of file is not considered and error. */ + if ((rc == 0) || (rc == VQAERR_EOF)) { + rc = framenum; + } + } + } else { + rc = VQAERR_SEEK; + } + } + } + + /* Restart audio playback. */ + #if(VQAAUDIO_ON) + if (audio_on) { + VQA_StartAudio(vqap); + } + #endif + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* AllocBuffers - Allocate VQA play buffers. +* +* SYNOPSIS +* VQAData = AllocBuffers(Header, Config) +* +* VQAData *AllocBuffers(VQAHeader *, VQAConfig *); +* +* FUNCTION +* For those structures that contain buffer pointers (codebook nodes, +* frame buffer nodes), enough memory is allocated for both the structure +* and its associated buffers, then the buffer pointers are pointed to +* the appropriate offset from the structure pointer. This allows us +* to perform only one malloc & free for each node. +* +* Buffers allocated: +* - vqa +* - vqa->CBData (list) +* - vqa->FrameData (list) +* - vqa->Drawer.ImageBuf +* - vqa->Audio.Buffer +* - vqa->Audio.IsLoaded +* - vqa->Foff +* +* INPUTS +* Header - Pointer to VQAHeader structure. +* Config - Pointer to VQA configuration structure. +* +* RESULT +* VQAData - Pointer to initialized VQAData structure. +* +****************************************************************************/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config) +{ + VQAData *vqa; + VQACBNode *cbnode; + VQACBNode *this_cb; + VQAFrameNode *framenode; + VQAFrameNode *this_frame; + long i; + + /* Check the configuration for valid values. */ + if ((config->NumCBBufs == 0) || (config->NumFrameBufs == 0)) { + return (NULL); + } + + /* Allocate the master structure */ + if ((vqa = (VQAData *)malloc(sizeof(VQAData))) == NULL) { + return (NULL); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VQA DATA STRUCTURES. + * + * Pointers are set to NULL initially, and filled in as the buffers are + * allocated. The Max buffer sizes are computed with 1K of padding, + * and'd with 0xFFFC to make the size divisible by 4, to ensure DWORD + * alignment. + *-----------------------------------------------------------------------*/ +#if (!VQADIRECT_SOUND) + DPMI_Lock(vqa, sizeof(VQAData)); +#endif //VQADIRECT_SOUND + + memset(vqa, 0, sizeof(VQAData)); + vqa->MemUsed = sizeof(VQAData); + vqa->Drawer.LastTime = (-VQA_TIMETICKS); + + /* Set maximum codebook size. */ + vqa->Max_CB_Size = ((header->CBentries) * header->BlockWidth + * header->BlockHeight + 250) & 0xFFFC; + + /* Set maximum palette size. */ + vqa->Max_Pal_Size = (768 + 1024) & 0xFFFC; + + /* Set maximum vector pointers size. */ + vqa->Max_Ptr_Size = ((header->ImageWidth / header->BlockWidth) + * (header->ImageHeight / header->BlockHeight) + * sizeof(short) + 1024) & 0xFFFC; + + /* Set the frame number of the frame containing the last codebook. */ + vqa->Loader.LastCBFrame = (((header->Frames - 1) / header->Groupsize) + * header->Groupsize); + + /*------------------------------------------------------------------------- + * ALLOCATE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumCBBufs; i++) { + + /* Allocate a codebook node. */ + cbnode = (VQACBNode *)malloc((sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* If failure then clean up and exit. */ + if (cbnode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(cbnode, (sizeof(VQACBNode) + vqa->Max_CB_Size)); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(cbnode, (sizeof(VQACBNode) + vqa->Max_CB_Size)); +#endif //(!VQADIRECT_SOUND) + + + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQACBNode) + vqa->Max_CB_Size); + + /* Initialize the node */ + memset(cbnode, 0, sizeof(VQACBNode)); + cbnode->Buffer = (unsigned char *)cbnode + sizeof(VQACBNode); + + /* Install the node */ + if (i == 0) { + vqa->CBData = cbnode; + this_cb = cbnode; + } else { + this_cb->Next = cbnode; + this_cb = cbnode; + } + } + + /* Make the list circular */ + cbnode->Next = vqa->CBData; + + /* Install the Codebook list */ + vqa->Loader.CurCB = vqa->CBData; + vqa->Loader.FullCB = vqa->CBData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumFrameBufs; i++) { + + /* Allocate a pointer node */ + framenode = (VQAFrameNode *)malloc((sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size + vqa->Max_Pal_Size)); + + /* If failure then clean up and exit. */ + if (framenode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(framenode, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(framenode, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); +#endif //(!VQADIRECT_SOUND) + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Initialize the node */ + memset(framenode, 0, sizeof(VQAFrameNode)); + framenode->Pointers = (unsigned char *)framenode + sizeof(VQAFrameNode); + framenode->Palette = (unsigned char *)framenode + sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size; + + framenode->Codebook = vqa->CBData; + + /* Install the node */ + if (i == 0) { + vqa->FrameData = framenode; + this_frame = framenode; + } else { + this_frame->Next = framenode; + this_frame = framenode; + } + } + + /* Make the list circular */ + framenode->Next = vqa->FrameData; + + /* Install the Frame Buffer list */ + vqa->Loader.CurFrame = vqa->FrameData; + vqa->Drawer.CurFrame = vqa->FrameData; + vqa->Flipper.CurFrame = vqa->FrameData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE IMAGE BUFFERS IF ONE IS NOT ALREADY PROVIDED. + *-----------------------------------------------------------------------*/ + if (config->ImageBuf == NULL) { + + /* Allocate our own buffer. */ + if (config->DrawFlags & VQACFGF_BUFFER) { + vqa->Drawer.ImageBuf = (unsigned char *)malloc((header->ImageWidth + * header->ImageHeight)); + + /* If the allocation failed we must free up and exit. */ + if (vqa->Drawer.ImageBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); +#endif //(!VQADIRECT_SOUND) + + /* Plugin image buffer information. */ + vqa->Drawer.ImageWidth = header->ImageWidth; + vqa->Drawer.ImageHeight = header->ImageHeight; + vqa->MemUsed += (long)(header->ImageWidth * header->ImageHeight); + } else { + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + } else { + + /* Use caller provided buffer */ + vqa->Drawer.ImageBuf = config->ImageBuf; + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + + /*------------------------------------------------------------------------- + * ALLOCATE AND INITIALIZE AUDIO BUFFERS AND STRUCTURES. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if ((header->Flags & VQAHDF_AUDIO) + && (config->OptionFlags & VQAOPTF_AUDIO)) { + + /* Dereference audio structure for quick access. */ + VQAAudio *audio = &vqa->Audio; + + /* Version 1 VQA's only supported 22050 8 bit mono audio. */ + if (header->Version < VQAHD_VER2) { + audio->SampleRate = 22050U; + audio->Channels = 1; + audio->BitsPerSample = 8; + audio->BytesPerSec = 22050; + } else { + if ((config->OptionFlags & VQAOPTF_ALTAUDIO) + && (header->Flags & VQAHDF_ALTAUDIO)) { + audio->SampleRate = header->AltSampleRate; + audio->Channels = header->AltChannels; + audio->BitsPerSample = header->AltBitsPerSample; + } else { + audio->SampleRate = header->SampleRate; + audio->Channels = header->Channels; + audio->BitsPerSample = header->BitsPerSample; + } + + audio->BytesPerSec = ((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)); + } + + /* Adjust the HMI buffer to accomodate the amount of data. */ + #if(0) + config->HMIBufSize *= (audio->SampleRate / 22050); + config->HMIBufSize *= audio->Channels * (audio->BitsPerSample >> 3); + #endif + + /* The default audio buffer size should be large enough to hold + * 1.5 seconds of data. + */ + if (config->AudioBufSize == -1) { + + /* Compute the number of HMI buffers that will completly fit into + * 1.5 seconds of audio data. + */ + i = ((audio->BytesPerSec+(audio->BytesPerSec/2))/config->HMIBufSize); + config->AudioBufSize = (config->HMIBufSize * i); + } + + /* Do not allocate anything if the audio buffer is zero length. */ + if (config->AudioBufSize > 0) { + + /* Allocate an audio buffer if the user did not provide one. + * Otherwise, use the user supplied buffer. + */ + if (config->AudioBuf == NULL) { + audio->Buffer = (unsigned char *)malloc(config->AudioBufSize); + + /* If failure then clean up and exit. */ + if (audio->Buffer == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + +#if (!VQADIRECT_SOUND) + DPMI_Lock(audio->Buffer, config->AudioBufSize); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(audio->Buffer, config->AudioBufSize); +#endif //(!VQADIRECT_SOUND) + + /* Add audio buffer size to memory usage. */ + vqa->MemUsed += config->AudioBufSize; + } else { + audio->Buffer = config->AudioBuf; + } + + /* Allocate IsLoaded flags */ + audio->NumAudBlocks = (config->AudioBufSize / config->HMIBufSize); + audio->IsLoaded = (short *)malloc(audio->NumAudBlocks * sizeof(short)); + + /* If failure then clean up and exit. */ + if (audio->IsLoaded == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(audio->IsLoaded, audio->NumAudBlocks * sizeof(short)); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(audio->IsLoaded, audio->NumAudBlocks * sizeof(short)); +#endif //(!VQADIRECT_SOUND) + + /* Add IsLoaded flags array to memory usage. */ + vqa->MemUsed += (audio->NumAudBlocks * sizeof(short)); + + /* Initialize audio IsLoaded flags to false. */ + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + + /* Allocate temporary staging buffer for the audio frames. */ + audio->TempBufSize = ((audio->BytesPerSec / header->FPS) * 2) + 100; + audio->TempBuf = (unsigned char *)malloc(audio->TempBufSize); + + if (audio->TempBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(audio->TempBuf, audio->TempBufSize); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(audio->TempBuf, audio->TempBufSize); +#endif //(!VQADIRECT_SOUND) + + /* Add temporary buffer size to memory usage. */ + vqa->MemUsed += audio->TempBufSize; + } + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME INFORMATION TABLE IF REQUESTED. + *-----------------------------------------------------------------------*/ + vqa->Foff = (long *)malloc(header->Frames * sizeof(long)); + + if (vqa->Foff == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ +#if (!VQADIRECT_SOUND) + DPMI_Lock(vqa->Foff, header->Frames * sizeof(long)); +#else //!VQADIRECT_SOUND + /* Make sure the allocated memory is paged in */ + Force_VM_Page_In(vqa->Foff, header->Frames * sizeof(long)); +#endif //(!VQADIRECT_SOUND) + + /* Keep a running total of memory usage. */ + vqa->MemUsed += (header->Frames * sizeof(long)); + + return (vqa); +} + + +/**************************************************************************** +* +* NAME +* FreeBuffers - Free VQA play buffers. +* +* SYNOPSIS +* FreeBuffers(VQAData, Config, Header) +* +* void FreeBuffers(VQAData *, VQAConfig *, VQAHeader *); +* +* FUNCTION +* Free the buffers allocated by AllocBuffers(). +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* Config - Pointer to configuration structure. +* Header - Pointer to movie header structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header) +{ + VQACBNode *cb_this, + *cb_next; + VQAFrameNode *frame_this, + *frame_next; + long i; + + /*------------------------------------------------------------------------- + * FREE THE FRAME INFORMATION TABLE. + *-----------------------------------------------------------------------*/ + if (vqa->Foff) { +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa->Foff, header->Frames * sizeof(long)); +#endif //(!VQADIRECT_SOUND) + free(vqa->Foff); + } + + /*------------------------------------------------------------------------- + * FREE THE AUDIO BUFFERS. + *-----------------------------------------------------------------------*/ + + #if(VQAAUDIO_ON) + if ((config->AudioBuf == NULL) && (vqa->Audio.Buffer)) { +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa->Audio.Buffer, config->AudioBufSize); +#endif //(!VQADIRECT_SOUND) + free(vqa->Audio.Buffer); + } + + /* Free the audio segments loaded flag array. */ + if (vqa->Audio.IsLoaded) { +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa->Audio.IsLoaded,vqa->Audio.NumAudBlocks * sizeof(short)); +#endif //(!VQADIRECT_SOUND) + free(vqa->Audio.IsLoaded); + } + + /* Free the temporary audio buffer. */ + if (vqa->Audio.TempBuf) { +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa->Audio.TempBuf, vqa->Audio.TempBufSize); +#endif //(!VQADIRECT_SOUND) + free(vqa->Audio.TempBuf); + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * FREE THE IMAGE BUFFER ONLY IF WE ALLOCATED IT. + *-----------------------------------------------------------------------*/ + if ((config->ImageBuf == NULL) && vqa->Drawer.ImageBuf) { +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); +#endif //(!VQADIRECT_SOUND) + free(vqa->Drawer.ImageBuf); + } + + /*------------------------------------------------------------------------- + * FREE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + frame_this = vqa->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame_this) { + frame_next = frame_this->Next; +#if (!VQADIRECT_SOUND) + DPMI_Unlock(frame_this, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); +#endif //(!VQADIRECT_SOUND) + free(frame_this); + frame_this = frame_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + cb_this = vqa->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (cb_this) { + cb_next = cb_this->Next; +#if (!VQADIRECT_SOUND) + DPMI_Unlock(cb_this, sizeof(VQACBNode) + vqa->Max_CB_Size); +#endif //(!VQADIRECT_SOUND) + free(cb_this); + cb_this = cb_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE VQA DATA STRUCTURES. + *-----------------------------------------------------------------------*/ +#if (!VQADIRECT_SOUND) + DPMI_Unlock(vqa, sizeof(VQAData)); +#endif //(!VQADIRECT_SOUND) + free(vqa); +} + + +/**************************************************************************** +* +* NAME +* PrimeBuffers - Pre-Load the internal buffers. +* +* SYNOPSIS +* Error = PrimeBuffers(VQA) +* +* long = PrimeBuffers(VQAHandle *); +* +* FUNCTION +* Pre-load the internal buffers in order to give the player some slack +* in the playback of large frames. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +long PrimeBuffers(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQAConfig *config; + long rc; + long i; + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + + /* Pre-load the buffers */ + for (i = 0; i < config->NumFrameBufs; i++) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + return (rc); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQF - Loads a VQ Frame chunk. +* +* SYNOPSIS +* Error = Load_VQF(VQA, Iffsize) +* +* long Load_VQF(VQAHandleP *, unsigned long); +* +* FUNCTION +* The VQ Frame Chunk contains a set of other chunks (codebooks, +* palettes, pointers). This routine reads the frame's chunk size, +* then loops until it's read that many bytes. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQF(VQAHandleP *vqap, unsigned long frame_iffsize) +{ + VQAData *vqabuf; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + unsigned long framesize; + unsigned long bytes_loaded = 0; + VQADrawer *drawer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + curframe = vqabuf->Loader.CurFrame; + framesize = PADSIZE(frame_iffsize); + drawer = &(vqap->VQABuf->Drawer); + chunk = &vqabuf->Loader.CurChunkHdr; + + /*------------------------------------------------------------------------- + * FRAME LOADING LOOP. + *-----------------------------------------------------------------------*/ + while (bytes_loaded < framesize) { + + /* Read chunk ID */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + bytes_loaded += 8; + bytes_loaded += PADSIZE(iffsize); + + /* Handle each chunk type */ + switch (chunk->id) { + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + memcpy(drawer->Palette_24,curframe->Palette,curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + drawer->CurPalSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)drawer->Palette_24, + vqabuf->Max_Pal_Size); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + break; + + /* An unknown chunk in the video frame is an error. */ + default: + return (VQAERR_READ); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_FINF - Load Frame Info chunk. +* +* SYNOPSIS +* Error = Load_FINF(VQA, Iffsize) +* +* long Load_FINF(VQAHandleP *, unsigned long); +* +* FUNCTION +* Load FINF chunk if buffer available, otherwise skip it. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + + /* Load the frame information table if we need to, otherwise we will + * skip it. + */ + if (vqabuf->Foff != NULL) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, vqabuf->Foff, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQHD - Load VQA header chunk. +* +* SYNOPSIS +* Error = Load_VQHD(VQA, Iffsize) +* +* long Load_VQHD(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize) +{ + /* Read the header */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &vqap->Header, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reconfigure the Drawer for the new settings */ + VQA_Configure_Drawer(vqap); + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBF0 - Load full uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBF0(VQA, Iffsize) +* +* long Load_CBF0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + + /* Read into the start of the buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curcb->Buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as uncompressed. */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Clock pointers to next CB Buffer. */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBFZ - Load full compressed codebook. +* +* SYNOPSIS +* Error = Load_CBFZ(VQA, Iffsize) +* +* long Load_CBFZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Load the codebook into the end of the buffer. */ + lcwoffset = vqap->VQABuf->Max_CB_Size - padsize; + buffer = curcb->Buffer + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as compressed */ + curcb->Flags |= VQACBF_CBCOMP; + curcb->CBOffset = lcwoffset; + + /* Clock pointers to next CB Buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBP0 - Load partial uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBP0(VQA, Iffsize) +* +* long Load_CBP0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + + /*------------------------------------------------------------------------- + * ASSEMBLY PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = curcb->Buffer + loader->PartialCBSize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Accumulate the partial codebook values. */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as uncompressed */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBPZ - Load partial compressed codebook. +* +* SYNOPSIS +* Error = Load_CBPZ(VQA, Iffsize) +* +* long Load_CBPZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + + /* Dereference commonly used data members for quicker access */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Attempt to compute the LCW offset into the codebook buffer by + * multiplying the size of this chunk by the # frames/group, and adding + * a small fudge factor on, then subtracting that from the CB buffer size. + */ + if (loader->PartialCBSize == 0) { + curcb->CBOffset = (vqabuf->Max_CB_Size + - (padsize * vqap->Header.Groupsize + 100)); + } + + /*------------------------------------------------------------------------- + * ASSEMBLE PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = ((curcb->Buffer + curcb->CBOffset) + loader->PartialCBSize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Accumulate partial codebook values */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as compressed. */ + curcb->Flags |= VQACBF_CBCOMP; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPL0 - Load an uncompressed palette. +* +* SYNOPSIS +* Error = Load_CPL0(VQA, Iffsize) +* +* long Load_CPL0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the palette into the palette buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Palette, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag the palette as uncompressed. */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + curframe->PalOffset = 0; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPLZ - Load compressed palette. +* +* SYNOPSIS +* Error = Load_CPLZ(VQA, Iffsize) +* +* long Load_CPLZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + + /* Read the palette into the end of the palette buffer. */ + lcwoffset = vqap->VQABuf->Max_Pal_Size - padsize; + buffer = curframe->Palette + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this palette as compressed. */ + curframe->Flags |= VQAFRMF_PALCOMP; + curframe->PalOffset = lcwoffset; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPT0 - Load uncompressed pointers. +* +* SYNOPSIS +* Error = Load_VPT0(VQA, Iffsize) +* +* long Load_VPT0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the pointers into start of the pointer buffer. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Pointers, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag this frame as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + curframe->PtrOffset = 0; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPTZ - Load compressed pointers. +* +* SYNOPSIS +* Error = Load_VPTZ(VQA, Iffsize) +* +* long Load_VPTZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + lcwoffset = vqap->VQABuf->Max_Ptr_Size - padsize; + + /* Read the pointers into end of the pointer buffer. */ + buffer = curframe->Pointers + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as compressed. */ + curframe->Flags |= VQAFRMF_PTRCOMP; + curframe->PtrOffset = lcwoffset; + + return (0); +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* Load_SND0 - Load uncompressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND0(VQA, Iffsize) +* +* long Load_SND0(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned long padsize; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk. + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (audio->Buffer == NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read large startup chunk directly into AudioBuf */ + if ((padsize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + + audio->AudBufPos += iffsize; + + /* Flag the audio frame flags as loaded for the initial audio frame. */ + for (i = 0; i < (iffsize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Read data into TempBuf */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } + + /* Set the TempBufLen */ + audio->TempBufLen = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND1 - Load compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND1(VQA, Iffsize) +* +* long Load_SND1(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + ZAPHeader zap; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read the ZAP audio frame header. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &zap, + sizeof(ZAPHeader))) { + + return (VQAERR_READ); + } + + /* Adjust chunk size */ + padsize -= sizeof(ZAPHeader); + + /* Read large startup chunk directly into AudioBuf */ + if ((zap.UnCompSize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load RAW uncompressed data. */ + if (zap.UnCompSize == zap.CompSize) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, + padsize)) { + + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->Buffer, zap.UnCompSize); + } + + /* Set buffer positions & flags */ + audio->AudBufPos += zap.UnCompSize; + + for (i = 0; i < (zap.UnCompSize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + if (zap.UnCompSize == zap.CompSize) { + + /* If the frame is uncompressed the load it in directly. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load the audio frame into the end of the buffer. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->TempBuf, zap.UnCompSize); + } + + /* Set the TempBufLen */ + audio->TempBufLen = zap.UnCompSize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND2 - Load ADPCM compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND2(VQA, Iffsize) +* +* long Load_SND2(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + unsigned long uncomp_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + uncomp_size = iffsize * (audio->BitsPerSample / 4); + + /* Read large startup chunk directly into AudioBuf */ + if ((uncomp_size > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->Buffer; + VQA_sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set buffer positions & flags */ + audio->AudBufPos += uncomp_size; + + for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->TempBuf; + VQA_sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set the TempBufLen */ + audio->TempBufLen = uncomp_size; + + return (0); +} + + +#if(VQAVOC_ON) +/**************************************************************************** +* +* NAME +* Load_AudFrame - Loads blocks from seperate VOC file. +* +* SYNOPSIS +* Load_AudFrame(VQA) +* +* void Load_AudFrame(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Load_AudFrame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + static long lastplayblock = -1; + static long myblock = 0; + static long firsttime = 1; + long numblocks; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + + /* Do nothing if no buffer */ + if (audio->Buffer == NULL) { + return; + } + + /* If this is the first time we're called, pre-load the 1st 'n' audio + * blocks, where 'n' is half the total audio buffer size; this way, we'll + * always stay ahead of HMI. + */ + if (firsttime) { + numblocks = (audio->NumAudBlocks / 2); + read(vqap->vocfh, audio->Buffer, config->HMIBufSize * numblocks); + audio->AudBufPos += config->HMIBufSize * numblocks; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + for (i = 0; i < numblocks; i++) { + audio->IsLoaded[i] = 1; + } + + myblock += numblocks; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + + firsttime = 0; + } + + /* If HMI's block has changed, load the next block & mark it as loaded */ + if (audio->PlayPosition / config->HMIBufSize != lastplayblock) { + + /* update HMI's last known block position */ + lastplayblock = audio->PlayPosition / config->HMIBufSize; + + /* read the VOC data */ + read(vqap->vocfh, (audio->Buffer + myblock * config->HMIBufSize), + config->HMIBufSize); + + audio->AudBufPos += config->HMIBufSize; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + /* set the IsLoaded flags */ + audio->IsLoaded[myblock] = 1; + + /* increment my block counter */ + myblock++; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + } +} +#endif /* VQAVOC_ON */ +#endif /* VQAAUDIO_ON */ + diff --git a/WINVQ/VQA32/MAKEFILE b/WINVQ/VQA32/MAKEFILE new file mode 100644 index 0000000..0356e20 --- /dev/null +++ b/WINVQ/VQA32/MAKEFILE @@ -0,0 +1,161 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJ = .. +WATCOM = C:\WATCOM +PRJNAME = vqa32wp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5r -mf -oaeilrt -s -zq -d1 +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/WINVQ/VQA32/MAKEFILE.BAK b/WINVQ/VQA32/MAKEFILE.BAK new file mode 100644 index 0000000..f174b30 --- /dev/null +++ b/WINVQ/VQA32/MAKEFILE.BAK @@ -0,0 +1,162 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJ = .. +WATCOM = C:\WATCOM +PRJNAME = vqa32wp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5r -mf -oaeilrt -s -zq -d2 +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + + \ No newline at end of file diff --git a/WINVQ/VQA32/MAKEFILE.BOR b/WINVQ/VQA32/MAKEFILE.BOR new file mode 100644 index 0000000..df30026 --- /dev/null +++ b/WINVQ/VQA32/MAKEFILE.BOR @@ -0,0 +1,216 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay32 library. +# +# FILE +# makefile (Borland/TNT) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 7, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# BCDIR - Borland C installation path. +# TNTDIR - Pharlap/TNT installation path. +# +#**************************************************************************** + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32bp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = \ + config.obj \ + task.obj \ + loader.obj \ + drawer.obj \ + audio.obj \ + monodisp.obj \ + dstream.obj \ + unvqbuff.obj \ + unvqvesa.obj \ + vertag.obj \ + caption.obj \ +# unvqxmde.obj \ + +PRJLIBS = \ + vqm32bp.lib \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMAND AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).lib + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD LIBRARY +#---------------------------------------------------------------------------- + +$(PRJNAME).lib: $(OBJECTS) + - @del $(.path.lib)\$(PRJNAME).lib >&NUL + $(LIB_CMD) $(.path.lib)\$(PRJNAME).lib @$(LIB_CFG) @&&| +-+$(**: = -+) +| + - @copy vqaplay.h $(PRJ)\INCLUDE\VQA32 >&NUL + - @copy vqafile.h $(PRJ)\INCLUDE\VQA32 >&NUL + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + diff --git a/WINVQ/VQA32/MAKEFILE.WAT b/WINVQ/VQA32/MAKEFILE.WAT new file mode 100644 index 0000000..cdd42a6 --- /dev/null +++ b/WINVQ/VQA32/MAKEFILE.WAT @@ -0,0 +1,163 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32wp +PRJDIR = $(%PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(%PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/WINVQ/VQA32/MONODISP.CPP b/WINVQ/VQA32/MONODISP.CPP new file mode 100644 index 0000000..a69c1e7 --- /dev/null +++ b/WINVQ/VQA32/MONODISP.CPP @@ -0,0 +1,512 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* monodisp.c +* +* DESCRIPTION +* Monochrome display (debug) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 6, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitMono - Initialize the player's mono screen. +* VQA_UpdateMono - Update the player's mono output. +* +****************************************************************************/ + +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +#if(VQAMONO_ON) + +/* Main window */ +#define MAIN_WX1 0 +#define MAIN_WX2 79 +#define MAIN_WY1 0 +#define MAIN_WY2 9 +#define MAIN_TITLE "VQA Player" + +/* Loader data window */ +#define LOADER_WX1 0 +#define LOADER_WX2 39 +#define LOADER_WY1 10 +#define LOADER_WY2 20 +#define LOADER_TITLE " Frame Loader " + +/* Drawer data window */ +#define DRAWER_WX1 40 +#define DRAWER_WX2 79 +#define DRAWER_WY1 10 +#define DRAWER_WY2 20 +#define DRAWER_TITLE " Frame Drawer " + +/* Audio data window */ +#define AUDIO_WX1 0 +#define AUDIO_WX2 39 +#define AUDIO_WY1 21 +#define AUDIO_WY2 24 +#define AUDIO_TITLE " Audio Callback " + +/* Flipper data window */ +#define FLIPPER_WX1 40 +#define FLIPPER_WX2 79 +#define FLIPPER_WY1 21 +#define FLIPPER_WY2 24 +#define FLIPPER_TITLE " Frame Flipper " + +extern char *HMIDevName; + + +/**************************************************************************** +* +* NAME +* VQA_InitMono - Initialize the player's mono screen. +* +* SYNOPSIS +* VQA_InitMono(VQA) +* +* void VQA_InitMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_InitMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + char txt[80]; + + /* Dereference commonly used data members of quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Enable and clear the mono screen */ + Mono_Enable(); + Mono_Clear_Screen(); + + /* Init main window */ + Mono_Draw_Rect(MAIN_WX1, MAIN_WY1, (MAIN_WX2 - MAIN_WX1 + 1), + (MAIN_WY2 - MAIN_WY1 + 1), 2, 1); + + Mono_Set_Cursor((MAIN_WX2 + MAIN_WX1 - strlen(MAIN_TITLE)) / 2, MAIN_WY1); + Mono_Print(MAIN_TITLE); + + /* Video mode */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 1); + Mono_Print("Video Mode: "); + + switch (config->Vmode) { + + #if(VQAMONO_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("MCGA Buffered"); + } else { + Mono_Print("MCGA Direct to screen"); + } + break; + #endif + + #if(VQAXMODE_ON) + case XMODE_320X200: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x200 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x200 VRAM Copy"); + } else { + Mono_Print("XMODE 320x200 Linear->Banked"); + } + } + break; + + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x240 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x240 VRAM Copy"); + } else { + Mono_Print("XMODE 320x240 Linear->Banked"); + } + } + break; + #endif + + #if(VQAVESA_ON) + case VESA_640X480_256: + Mono_Print("VESA 640x480"); + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("VESA 320x200 Buffered"); + } else { + Mono_Print("VESA 320x200 Direct to screen"); + } + break; + #endif + + default: + Mono_Print("UNKNOWN"); + break; + } + + /* Sound status */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 2); + Mono_Print(" Sound: "); + + if (config->OptionFlags & VQAOPTF_AUDIO) { + sprintf(txt,"%u Hz", config->AudioRate); + Mono_Print(txt); + } else { + Mono_Print("OFF"); + } + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 3); + Mono_Print(" Driver Name: "); + Mono_Print(HMIDevName); + + /* Frame rates */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 4); + sprintf(txt," Load Frame Rate: %d", config->FrameRate); + Mono_Print(txt); + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 5); + sprintf(txt," Draw Frame Rate: %d", config->DrawRate); + Mono_Print(txt); + + /* Slow palette */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 6); + Mono_Print(" Slow palette: "); + + if (config->OptionFlags & VQAOPTF_SLOWPAL) { + Mono_Print("ON"); + } else { + Mono_Print("OFF"); + } + + /* Memory Usage */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 7); + sprintf(txt," Memory Used: %ld", vqabuf->MemUsed); + Mono_Print(txt); + + /* Timer Method */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 8); + + if (VQA_TimerMethod() == VQA_TMETHOD_DOS) { + Mono_Print(" DOS Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_INT) { + Mono_Print(" Interrupt Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_AUDIO) { + Mono_Print(" Audio DMA Timer:"); + } else { + Mono_Print(" Defualt:"); + } + + /* Init loader data window */ + Mono_Draw_Rect(LOADER_WX1, LOADER_WY1, (LOADER_WX2 - LOADER_WX1 + 1), + (LOADER_WY2 - LOADER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((LOADER_WX2 + LOADER_WX1 - strlen(LOADER_TITLE)) / 2, + LOADER_WY1); + + Mono_Print(LOADER_TITLE); + + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 2); + Mono_Print("# Waits on Drawer:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 3); + Mono_Print(" # Waits on Audio:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 4); + Mono_Print(" Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 5); + Mono_Print(" Max Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY2 - 2); + Mono_Print("Audio:"); + + /* Init drawer data window */ + Mono_Draw_Rect(DRAWER_WX1, DRAWER_WY1, (DRAWER_WX2 - DRAWER_WX1 + 1), + (DRAWER_WY2 - DRAWER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((DRAWER_WX2 + DRAWER_WX1 - strlen(DRAWER_TITLE)) / 2, + DRAWER_WY1); + + Mono_Print(DRAWER_TITLE); + + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 2); + Mono_Print(" Desired Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 3); + Mono_Print("# Waits on Flipper:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 4); + Mono_Print(" # Waits on Loader:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 5); + Mono_Print(" # Frames Skipped:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 6); + Mono_Print(" VQ Block Size:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY2 - 2); + Mono_Print("Frames: Cbooks:"); + + /* Init audio data window */ + Mono_Draw_Rect(AUDIO_WX1, AUDIO_WY1, (AUDIO_WX2 - AUDIO_WX1 + 1), + (AUDIO_WY2 - AUDIO_WY1 + 1), 2, 1); + + Mono_Set_Cursor((AUDIO_WX2 + AUDIO_WX1 - strlen(AUDIO_TITLE)) / 2, + AUDIO_WY1); + + Mono_Print(AUDIO_TITLE); + + Mono_Set_Cursor(AUDIO_WX1 + 2, AUDIO_WY1 + 1); + Mono_Print("# Repeated Buffers:"); + + /* Init flipper data window */ + Mono_Draw_Rect(FLIPPER_WX1, FLIPPER_WY1, (FLIPPER_WX2 - FLIPPER_WX1 + 1), + (FLIPPER_WY2 - FLIPPER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((FLIPPER_WX2 + FLIPPER_WX1 - strlen(FLIPPER_TITLE)) / 2, + FLIPPER_WY1); + + Mono_Print(FLIPPER_TITLE); + + Mono_Set_Cursor(FLIPPER_WX1 + 2, FLIPPER_WY1 + 1); + Mono_Print("Current Frame #:"); +} + + +/**************************************************************************** +* +* NAME +* VQA_UpdateMono - Update the player's mono output. +* +* SYNOPSIS +* VQA_UpdateMono(VQA) +* +* void VQA_UpdateMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_UpdateMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAFrameNode *frame; + VQACBNode *cbook; + long frameindex = -1; + long loadcb = -1; + long drawcb = -1; + long i; + unsigned long curtime; + char txt[80]; + + /* Dereference commonly used data members for quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Timer value */ + curtime = VQA_GetTime(vqap); + Mono_Set_Cursor(MAIN_WX1 + 40, MAIN_WY1 + 8); + sprintf(txt,"%02ld:%02ld.%02ld",curtime / (VQA_TIMETICKS * VQA_TIMETICKS), + curtime / VQA_TIMETICKS,((curtime * 100L) / VQA_TIMETICKS) + -((curtime / VQA_TIMETICKS) * 100L)); + + Mono_Print(txt); + + /* Loader data */ + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 1); + sprintf(txt,"%4d",vqabuf->Loader.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 2); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnDrawer); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 3); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnAudio); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 4); + sprintf(txt,"%5u",vqabuf->Loader.FrameSize); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 5); + sprintf(txt,"%5u",vqabuf->Loader.MaxFrameSize); + Mono_Print(txt); + + #if(VQAAUDIO_ON) + /* Draw a picture of the audio buffers */ + for (i = 0; i < vqabuf->Audio.NumAudBlocks; i++) { + if (vqabuf->Audio.IsLoaded[i] == 0) { + txt[i] = '_'; + } else { + txt[i] = 'X'; + } + } + + txt[i] = 0; + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2-1); + Mono_Print(" "); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.PlayPosition + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("P"); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.AudBufPos + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("L"); + #endif /* VQAAUDIO_ON */ + + /* Drawer data */ + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Drawer.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 2); + sprintf(txt,"%4d", vqabuf->Drawer.DesiredFrame); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 3); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnFlipper); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 4); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnLoader); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 5); + sprintf(txt,"%4d", vqabuf->Drawer.NumSkipped); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 6); + sprintf(txt," %dx%d", vqap->Header.BlockWidth, vqap->Header.BlockHeight); + Mono_Print(txt); + + /* Draw a picture of the frame buffers */ + frame = vqabuf->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame->Flags & VQAFRMF_LOADED) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + + if (vqabuf->Flipper.CurFrame == frame) { + frameindex = i; + } + + frame = frame->Next; + } + + txt[i] = 0; + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 10 + frameindex,DRAWER_WY2 - 1); + Mono_Print("^"); + + /* Draw a picture of the codebook buffers */ + cbook = vqabuf->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (vqabuf->Loader.CurCB == cbook) { + loadcb = i; + } + + if (vqabuf->Flipper.CurFrame->Codebook == cbook) { + drawcb = i; + } + + cbook = cbook->Next; + } + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 2); + Mono_Print("___"); + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + loadcb,DRAWER_WY2 - 1); + Mono_Print("L"); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + drawcb,DRAWER_WY2 - 1); + Mono_Print("D"); + + /* Audio data */ + #if(VQAAUDIO_ON) + Mono_Set_Cursor(AUDIO_WX1 + 22, AUDIO_WY1 + 1); + sprintf(txt,"%4ld", vqabuf->Audio.NumSkipped); + Mono_Print(txt); + #endif + + /* Flipper data */ + Mono_Set_Cursor(FLIPPER_WX1 + 22,FLIPPER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Flipper.LastFrameNum); + Mono_Print(txt); + Mono_Set_Cursor(0,0); +} + +#endif /* VQAMONO_ON */ + diff --git a/WINVQ/VQA32/OLD/AUDIO.CPP b/WINVQ/VQA32/OLD/AUDIO.CPP new file mode 100644 index 0000000..8e8dd50 --- /dev/null +++ b/WINVQ/VQA32/OLD/AUDIO.CPP @@ -0,0 +1,1332 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* audio.c +* +* DESCRIPTION +* Audio playback and timing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 4, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_StartTimerInt - Initialize system timer interrupt. +* VQA_StopTimerInt - Remove system timer interrupt. +* VQA_SetTimer - Resets current time to given tick value. +* VQA_GetTime - Return current time. +* VQA_TimerMethod - Get timer method being used. +* VQA_OpenAudio - Open sound system. +* VQA_CloseAudio - Close sound system +* VQA_StartAudio - Starts audio playback +* VQA_StopAudio - Stop audio playback. +* CopyAudio - Copy data from Audio Temp buf into Audio play buf. +* +* PRIVATE +* TimerCallback - VQA timer event. (Called by HMI) +* AutoDetect - Auto detect the sound card. +* AudioCallback - Sound system callback. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +#ifdef __WATCOMC__ +#pragma pack(4); +#endif + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + + +/*--------------------------------------------------------------------------- + * AUDIO DEFINITIONS + *-------------------------------------------------------------------------*/ + +#define USE_WINDOWS_TIME 1 + +#define HMI_SAMPLE 0x1000 +#define MAKE_LONG(a,b) (((long)(a)<<16)|(long)((b)&0x0000FFFFL)) + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +#if( USE_WINDOWS_TIME ) + extern unsigned long Get_Game_Time( void ); +#endif + +#if(VQAAUDIO_ON) +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels); +void far TimerCallback(void); +void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction, + WORD wSampleID); + +/* Dummy functions used to mark the start/end address of the file. */ +static void StartAddr(void); +static void EndAddr(void); + + +/*--------------------------------------------------------------------------- + * GLOBAL DATA + *-------------------------------------------------------------------------*/ + +static VQAHandleP *VQAP = NULL; +static long AudioFlags = 0; +static long TimerSysCount = 0; +static long TimerIntCount = 0; +static WORD VQATimer = 0; +static long TimerMethod; +static long VQATickCount = 0; +#endif /* VQAAUDIO_ON */ + +static long TickOffset = 0; +char *HMIDevName = ""; + +#if(VQAAUDIO_ON) +/* This is a dummy function that is used to mark the start of the module. + * It is necessary for locking the memory the module occupies. This prevents + * the virtual memory manager from swapping out this memory. + */ +static void StartAddr(void) +{ +} + + +/**************************************************************************** +* +* NAME +* VQA_StartTimerInt - Initialize system timer interrupt. +* +* SYNOPSIS +* Error = VQA_StartTimerInt(VQA, Init) +* +* long VQA_StartTimerInt(VQAHandeP *, long); +* +* FUNCTION +* Initialize the HMI timer system and add our own timer event. If the +* system has already been initialized then we are given access to the +* the timer system. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* Init - Initialize HMI timer system flag. (TRUE = Initialize) +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_StartTimerInt(VQAHandleP *vqap, long init) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Does the caller want me to initialize the timer system? */ + if (init == 0) { + + /* If the timer system is uninitialized then it is the players + * responsibility to do it. + */ + if ((AudioFlags & VQAAUDF_TIMERINIT)==(HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } else { + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + } + + /* Increment the timer system usage count. */ + TimerSysCount++; + + /* Register the VQA_TickCount timer event. */ + if ((AudioFlags & VQAAUDF_HMITIMER) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_HMITIMER); + + /* Increment the timer interrupt usage count. */ + TimerIntCount++; + + /* Lock the memory occupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock((void *)&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopTimerInt - Remove system timer interrupt. +* +* SYNOPSIS +* VQA_StopTimerInt() +* +* void VQA_StopTimerInt(void); +* +* FUNCTION +* Remove our timer event from the HMI timer system. Uninitialize the +* HMI timer system if we initialized it. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopTimerInt(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Decrement the timer interrupt usage count. */ + if (TimerIntCount) { + TimerIntCount--; + } + + /* Remove the timer interrrupt if it is initialized and the use count is + * zero. Otherwise, clear the callers timer interrupt availability flag. + */ + if (((AudioFlags & VQAAUDF_HMITIMER) == (HMI_VQAINIT<Flags &= ~VQAAUDF_HMITIMER; + } + + /* Derement the timer system usage count. */ + if (TimerSysCount) { + TimerSysCount--; + } + + /* Close the timer system if it has been opened and the use count is zero. + * Otherwise, clear the callers timer system availability flag. + */ + if (((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + } + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* TimerCallback - VQA timer event. (Called by HMI) +* +* SYNOPSIS +* TimerCallback() +* +* void TimerCallback(void); +* +* FUNCTION +* Our custom timer event. This is the timer event that we register with +* HMI for system timing. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void far TimerCallback(void) +{ + VQATickCount++; +} + + +/**************************************************************************** +* +* NAME +* VQA_OpenAudio - Open sound system. +* +* SYNOPSIS +* Error = VQA_OpenAudio(VQAHandleP) +* +* long VQA_OpenAudio(VQAHandleP *); +* +* FUNCTION +* Initialize the sound system by setting the DigiHandle to and HMI +* driver handle. The timer must first be initialized before calling +* this function. +* +* INPUTS +* VQAHandleP - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, -1 if error. +* +****************************************************************************/ + +long VQA_OpenAudio(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAAudio *audio; + VQAConfig *config; + VQAHeader *header; + unsigned char *driver_path; + WORD port; + long rc; + + /* Dereference data memebers for quicker access. */ + config = &vqap->Config; + header = &vqap->Header; + vqabuf = vqap->VQABuf; + audio = &vqabuf->Audio; + + /* Fail if no audio buffer or DigiCard is 0 (no sound) */ + if ((audio->Buffer == NULL) || (config->DigiCard == 0)) { + return (-1); + } + + /* Reset the buffer position to the beginning. */ + audio->CurBlock = 0; + + /* Initialize the HMI driver structure. */ + audio->sSOSInitDriver.wAllocateBuffer = _TRUE; + audio->sSOSInitDriver.wParam = 19; + audio->sSOSInitDriver.wTimerID = _SOS_NORMAL_TIMER; + + /*------------------------------------------------------------------------- + * Compute the playback rate: + * + * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified + * frame rate (so the audio plays faster if we're playing faster) + * - otherwise, use the specified rate + *-----------------------------------------------------------------------*/ + if (config->AudioRate != -1) { + audio->sSOSInitDriver.wSampleRate = config->AudioRate; + } + else if (config->FrameRate != header->FPS) { + audio->sSOSInitDriver.wSampleRate = ((audio->SampleRate*config->FrameRate) + / (unsigned long)header->FPS); + + config->AudioRate = audio->sSOSInitDriver.wSampleRate; + } else { + audio->sSOSInitDriver.wSampleRate = audio->SampleRate; + config->AudioRate = audio->SampleRate; + } + + /* If the application has already initialized HMI then set the + * necessary variables. Otherwise we must setup HMI ourself. + */ + if ((config->OptionFlags & VQAOPTF_HMIINIT) + || ((AudioFlags & VQAAUDF_DIGIINIT) != 0)) { + + /* The application MUST provide the card type! */ + if (config->DigiCard == -1) { + return (-1); + } + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /* Get the capabilities of the card being used. */ + rc = sosDIGIDetectGetCaps(config->DigiCard, &audio->DigiCaps); + sosDIGIDetectUnInit(); + + if (rc != 0) { + return (rc); + } + + audio->DigiHandle = config->DigiHandle; + audio->Flags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + audio->Flags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + + if ((AudioFlags & (VQAAUDF_TIMERINIT|VQAAUDF_DIGIINIT)) == 0) { + HMIDevName = "App-Specific"; + AudioFlags |= (HMI_APPINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_APPINIT << VQAAUDB_DIGIINIT); + } + } else { + + /* Init the detection system */ + driver_path = (unsigned char *)".\\"; + + if ((rc = sosDIGIDetectInit(driver_path)) != 0) { + return (rc); + } + + /*----------------------------------------------------------------------- + * Initialize DigiHardware with port, IRQ, and DMA, and make sure + * Config.DigiCard contains the HMI ID we want to use: + * + * - If Config.DigiCard is -1, auto-detect the hardware; then, do a + * FindHardware so the GetSettings will work + * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use + * FindHardware & GetSettings to get the settings + * - If all are filled in, just use them as they are + *---------------------------------------------------------------------*/ + + /* Auto-Detect */ + if (config->DigiCard == -1) { + + /* Version 1 VQA's have only 8 bit mono audio streams. */ + if (header->Version == VQAHD_VER1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } else { + config->DigiCard = AutoDetect(&audio->DigiCaps, audio->BitsPerSample, + audio->Channels); + + /* Resort to 8bit mono */ + if (config->DigiCard == -1) { + config->DigiCard = AutoDetect(&audio->DigiCaps, 8, 1); + } + } + + if (config->DigiCard == -1) { + sosDIGIDetectUnInit(); + return (-1); + } + } + + /* Do a FindHardware & GetSettings */ + if (config->DigiPort == -1) { + if (sosDIGIDetectFindHardware(config->DigiCard, &audio->DigiCaps, + &port)) { + + sosDIGIDetectUnInit(); + return (-1); + } + + if (sosDIGIDetectGetSettings(&audio->DigiHardware)) { + sosDIGIDetectUnInit(); + return (-1); + } + + config->DigiPort = audio->DigiHardware.wPort; + config->DigiIRQ = audio->DigiHardware.wIRQ; + config->DigiDMA = audio->DigiHardware.wDMA; + HMIDevName = (char *)audio->DigiCaps.szDeviceName; + } else { + audio->DigiHardware.wPort = config->DigiPort; + audio->DigiHardware.wIRQ = config->DigiIRQ; + audio->DigiHardware.wDMA = config->DigiDMA; + HMIDevName = "App-Specific"; + } + + sosDIGIDetectUnInit(); + + /* Initialize the DIGI system & driver */ + sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL); + audio->sSOSInitDriver.wBufferSize = config->HMIBufSize; + audio->sSOSInitDriver.lpBuffer = NULL; + audio->sSOSInitDriver.lpFillHandler = NULL; + audio->sSOSInitDriver.lpDriverMemory = NULL; + audio->sSOSInitDriver.lpTimerMemory = NULL; + audio->DigiHandle = -1; + + if ((rc = sosDIGIInitDriver(config->DigiCard, &audio->DigiHardware, + &audio->sSOSInitDriver, &audio->DigiHandle)) != 0) { + + return (rc); + } + + /*----------------------------------------------------------------------- + * Register the timer event + *---------------------------------------------------------------------*/ + + /* If the timer hasn't been init'd, do it now */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_UNINIT<Flags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_TIMERINIT); + } + + /* Register the event */ + rc = sosTIMERRegisterEvent(VQA_TIMETICKS, + audio->sSOSInitDriver.lpFillHandler, &audio->DigiTimer); + + if (rc) { + sosDIGIUnInitDriver(audio->DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + return (rc); + } + + config->DigiHandle = audio->DigiHandle; + audio->Flags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + AudioFlags |= (HMI_VQAINIT << VQAAUDB_DIGIINIT); + } + + if ((audio->Flags & VQAAUDF_MODLOCKED) == 0) { + DPMI_Lock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags |= VQAAUDF_MODLOCKED; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_CloseAudio - Close sound system +* +* SYNOPSIS +* VQA_CloseAudio() +* +* void VQA_CloseAudio(void); +* +* FUNCTION +* Removes VQA's involvement in the audio system. +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_CloseAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Remove the Digi event */ + if ((AudioFlags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiTimer); + } + + /* Un-init timer if necessary */ + if ((AudioFlags & VQAAUDF_TIMERINIT) == (HMI_VQAINIT<Flags &= ~VQAAUDF_TIMERINIT; + AudioFlags &= ~VQAAUDF_TIMERINIT; + + /* Remove the driver */ + if ((audio->Flags & VQAAUDF_DIGIINIT) == (HMI_VQAINIT<DigiHandle, _TRUE, _TRUE); + sosDIGIUnInitSystem(); + } + + audio->Flags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_DIGIINIT; + AudioFlags &= ~VQAAUDF_ISPLAYING; + + /* Unlock the memory accupied by this module. */ + if ((audio->Flags & VQAAUDF_MODLOCKED) == 1) { + DPMI_Unlock(&StartAddr, (long)&EndAddr - (long)&StartAddr); + audio->Flags &= ~VQAAUDF_MODLOCKED; + } +} + + +/**************************************************************************** +* +* NAME +* AutoDetect - Auto detect the sound card. +* +* SYNOPSIS +* CardID = AutoDetect(DigiCaps, BitSize, Channels) +* +* long AutoDetect(_SOS_CAPABILITIES *, long, long); +* +* FUNCTION +* Autodetects the type of sound card present in the system. +* +* INPUTS +* DigiCaps - Pointer to HMI digital card capabilities structure. +* BitSize - Bits per sample size. +* Channels - Number of desired channels. +* +* RESULT +* CardID - HMI ID of the sound card found, -1 if none. +* +****************************************************************************/ + +long AutoDetect(_SOS_CAPABILITIES *digicaps, long bitsize, long channels) +{ + long device_id = -1; + WORD port; + long i; + long rc; + + /* Search for an 8-bit mono device */ + if (sosDIGIDetectFindFirst(digicaps, &port)) { + return (-1); + } + + channels--; + + i = 0; + while (i < 6) { + i++; + + if ((digicaps->wBitsPerSample == bitsize) + && (digicaps->wChannels == channels)) { + + break; + } + + if (sosDIGIDetectFindNext(digicaps, &port)) { + return (-1); + } + } + + /* Exit if failed to find the required device */ + if ((digicaps->wBitsPerSample != bitsize) + || (digicaps->wChannels != channels)) { + + return (-1); + } + + /* Stash the ID */ + device_id = digicaps->wDeviceID; + + /* Now that we have handled the initial pass, verify that if we found an + * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise. + */ + if ((WORD)digicaps->wDeviceID == _ADLIB_GOLD_8_MONO) { + rc = sosDIGIDetectFindNext(digicaps, &port); + + while ((i < 6) && (rc == 0)) { + i++; + + if ((digicaps->wBitsPerSample == 8) && (digicaps->wChannels == 0)) { + break; + } + + if ((rc = sosDIGIDetectFindNext(digicaps, &port)) != 0) { + break; + } + } + + /* If we don't have an error use the secondary device ID, after all, + * anything's better than an Adlib Gold. If we do have an error or there + * is nothing we can use then the device ID is already set to the adlib + * gold so just let it rip. + */ + if ((rc == 0) && ((WORD)digicaps->wDeviceID == _SBPRO_8_MONO)) { + return (digicaps->wDeviceID); + } + } + + return (device_id); +} + + +/**************************************************************************** +* +* NAME +* VQA_StartAudio - Starts audio playback +* +* SYNOPSIS +* Error = VQA_StartAudio(VQA) +* +* long VQA_StartAudio(VQAHandleP *); +* +* FUNCTION +* Start the audio playback for the movie. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* Error - 0 if successful, or -1 error code. +* +****************************************************************************/ + +long VQA_StartAudio(VQAHandleP *vqap) +{ + VQAConfig *config; + VQAAudio *audio; + + /* Save buffers for the callback routine */ + VQAP = vqap; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + audio = &vqap->VQABuf->Audio; + + /* Return if already playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + return (-1); + } + + /* Set my driver handle */ + if (config->DigiHandle != -1) { + audio->DigiHandle = config->DigiHandle; + } + + if (audio->DigiHandle == (WORD)-1) { + return (-1); + } + + /*------------------------------------------------------------------------- + * Initialize the sample structure. + *-----------------------------------------------------------------------*/ + memset(&audio->sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE)); + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)audio->Buffer; + audio->sSOSSampleData.dwSampleSize = config->HMIBufSize; + audio->sSOSSampleData.wVolume = (config->Volume << 7); + audio->sSOSSampleData.wSampleID = HMI_SAMPLE; + audio->sSOSSampleData.lpCallback = AudioCallback; + + /* Set the channel flags for the type of data we have. */ + if (audio->Channels == 2) { + audio->sSOSSampleData.wChannel = _INTERLEAVED; + } else { + audio->sSOSSampleData.wChannel = _CENTER_CHANNEL; + } + + /* If the card is unable to handle stereo data the we must notify the + * sound system to convert the stereo data to mono data during playback. + */ + if ((audio->Channels - 1) > audio->DigiCaps.wChannels) { + audio->sSOSSampleData.wSampleFlags |= _STEREOTOMONO; + } + + /* If the card is unable to handle the sample size of the audio data + * then we must notify the sound system to convert the audio data to + * the proper format. + */ + if (audio->BitsPerSample != audio->DigiCaps.wBitsPerSample) { + if (audio->BitsPerSample > audio->DigiCaps.wBitsPerSample) { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8; + } else { + audio->sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* Adjust the pitch if the driver is setup to playback at a different + * rate than what the sample was recorded at. + */ + if (audio->sSOSInitDriver.wSampleRate != audio->SampleRate) { + ldiv_t result; + + result = ldiv(audio->SampleRate, audio->sSOSInitDriver.wSampleRate); + audio->sSOSSampleData.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, + (short)(((long)result.rem * 0x10000L) / audio->sSOSInitDriver.wSampleRate)); + audio->sSOSSampleData.wSampleFlags |= _PITCH_SHIFT; + } + + /* Start playback */ + audio->SampleHandle = sosDIGIStartSample(audio->DigiHandle, + &audio->sSOSSampleData); + + audio->Flags |= VQAAUDF_ISPLAYING; + AudioFlags |= VQAAUDF_ISPLAYING; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_StopAudio - Stop audio playback. +* +* SYNOPSIS +* VQA_StopAudio(VQA) +* +* void VQA_StopAudio(VQAHandleP *); +* +* FUNCTION +* Halts the currently playing audio stream. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_StopAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Just return if not playing */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + sosDIGIStopSample(audio->DigiHandle, audio->SampleHandle); + audio->Flags &= ~VQAAUDF_ISPLAYING; + AudioFlags &= ~VQAAUDF_ISPLAYING; + } + + VQAP = NULL; +} + + +/**************************************************************************** +* +* NAME +* CopyAudio - Copy data from Audio Temp buffer into Audio play buffer. +* +* SYNOPSIS +* Error = CopyAudio(VQA) +* +* long CopyAudio(VQAHandleP *); +* +* FUNCTION +* This routine just copies the data in the TempBuf into the correct +* spots in the audio play buffer. If there is no room available in the +* audio play buffer, the routine returns VQAERR_SLEEPING, which will put +* the whole Loader to "sleep" while it waits for a free buffer. +* +* If there's no data in the TempBuf to copy, the routine just returns 0. +* +* INPUTS +* VQA - Pointer to private VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long CopyAudio(VQAHandleP *vqap) +{ + VQAAudio *audio; + VQAConfig *config; + long startblock; + long endblock; + long len1,len2; + long i; + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* If audio is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, or if there's no data to copy, just return 0 + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL) + || (audio->TempBufLen == 0)) { + #endif /* VQAVOC_ON */ + + return (0); + } + + /* Compute start & end blocks to copy into */ + startblock = (audio->AudBufPos / config->HMIBufSize); + endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize; + + if (endblock >= audio->NumAudBlocks) { + endblock -= audio->NumAudBlocks; + } + + /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */ + if (audio->IsLoaded[endblock] == 1) { + return (VQAERR_SLEEPING); + } + + /* Copy the data: + * + * - If 'startblock' < 'endblock', copy the entire buffer + * - Otherwise, fill to the end of the buffer with part of the data, then + * copy the rest to the beginning of the buffer + */ + if (startblock <= endblock) { + + /* Copy data */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, + audio->TempBufLen); + + /* Adjust current load position */ + audio->AudBufPos += audio->TempBufLen; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set all blocks to loaded */ + for (i = startblock; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Compute length of each piece */ + len1 = config->AudioBufSize - audio->AudBufPos; + len2 = audio->TempBufLen - len1; + + /* Copy 1st piece into end of Audio Buffer */ + memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1); + + /* Copy 2nd piece into start of Audio Buffer */ + memcpy(audio->Buffer, audio->TempBuf + len1, len2); + + /* Adjust load position */ + audio->AudBufPos = len2; + + /* Mark buffer as empty */ + audio->TempBufLen = 0; + + /* Set blocks to loaded */ + for (i = startblock; i < audio->NumAudBlocks; i++) { + audio->IsLoaded[i] = 1; + } + + for (i = 0; i < endblock; i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* AudioCallback - Sound system callback. +* +* SYNOPSIS +* AudioCallback(DriverHandle, Action, SampleID) +* +* void AudioCallback(WORD, WORD, WORD); +* +* FUNCTION +* Our custom audio callback routine that services HMI. +* +* INPUTS +* DriverHandle - HMI driver handle. +* Action - Action taken. +* SampleID - ID of sample. +* +* RESULT +* NONE +* +****************************************************************************/ + +void far cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID) +{ + VQAAudio *audio; + VQAConfig *config; + + /* Dereference commonly used data members for quicker access. */ + audio = &VQAP->VQABuf->Audio; + config = &VQAP->Config; + + /* Suppress compiler warnings */ + wDriverHandle = wDriverHandle; + wSampleID = wSampleID; + + /* See if we're called because the buffer is empty */ + if (wAction == _SAMPLE_PROCESSED) { + + /* Compute the 'NextBlock' index */ + audio->NextBlock = audio->CurBlock + 1; + + if (audio->NextBlock >= audio->NumAudBlocks) { + audio->NextBlock = 0; + } + + /* See if the next block has data in it; if so, update the audio + * buffer play position & the 'CurBlock' value. + * If not, don't change anything and replay this block. + */ + if (audio->IsLoaded[audio->NextBlock] == 1) { + + /* Update this block's status to loadable (0) */ + audio->IsLoaded[audio->CurBlock] = 0; + + /* Update position within audio buffer */ + audio->PlayPosition += config->HMIBufSize; + audio->CurBlock++; + + if (audio->PlayPosition >= config->AudioBufSize) { + audio->PlayPosition = 0; + audio->CurBlock = 0; + } + } else { + audio->NumSkipped++; + } + + /* Start the new buffer playing */ + audio->sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer) + + audio->PlayPosition; + + sosDIGIContinueSample(audio->DigiHandle, audio->SampleHandle, + &audio->sSOSSampleData); + + audio->SamplesPlayed += config->HMIBufSize; + } +} + +/* Dummy function used to mark the beginning address of the file. */ +static void EndAddr(void) +{ +} +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_SetTimer - Resets current time to given tick value. +* +* SYNOPSIS +* VQA_SetTimer(Time, Method) +* +* void VQA_SetTimer(long, long); +* +* FUNCTION +* Sets 'TickOffset' to a value that will make the current time look like +* the time passed in. This function allows the player to be "paused", +* by recording the time of the pause, and then setting the timer to +* that time. The timer method used by the player is also set. The method +* selected is not neccesarily the method that will be used because some +* timer methods work with only certain playback conditions. (EX: The +* audio DMA timer method cannot be used if there is not any audio +* playing.) +* +* INPUTS +* Time - Value to set current time to. +* Method - Timer method to use. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_SetTimer(VQAHandleP *vqap, long time, long method) +{ + unsigned long curtime; + + #if(VQAAUDIO_ON) + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* If the client does not have a preferencee then pick a method + * based on the state of the player. + */ + if (method == VQA_TMETHOD_DEFAULT) { + + /* If we are playing audio, use the audio DMA position. */ + if (AudioFlags & VQAAUDF_ISPLAYING) { + method = VQA_TMETHOD_AUDIO; + } + + /* Otherwise use the HMI timer if it is initialized. */ + else if (AudioFlags & VQAAUDF_HMITIMER) { + method = VQA_TMETHOD_INT; + } + + /* If all else fails resort the the "jerky" DOS time. */ + else { + method = VQA_TMETHOD_DOS; + } + } else { + + /* We cannot use the DMA position if there isn't any audio playing. */ + if (!(AudioFlags & VQAAUDF_ISPLAYING) && (method == VQA_TMETHOD_AUDIO)) { + method = VQA_TMETHOD_INT; + } + + /* We cannot use the timer if it has not been initialized. */ + if (!(AudioFlags & VQAAUDF_HMITIMER) && (method == VQA_TMETHOD_INT)) { + method = VQA_TMETHOD_DOS; + } + } + + TimerMethod = method; + #else + method = method; + #endif + + TickOffset = 0L; + curtime = VQA_GetTime(vqap); + TickOffset = (time - curtime); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetTime - Return current time. +* +* SYNOPSIS +* Time = VQA_GetTime() +* +* unsigned long VQA_GetTime(void); +* +* FUNCTION +* This routine returns timer ticks computed one of 3 ways: +* +* 1) If audio is playing, the timer is based on the DMA buffer position: +* Compute the number of audio samples that have actually been played. +* The following internal HMI variables are used: +* +* _lpSOSDMAFillCount[drv_handle]: current DMA buffer position +* _lpSOSSampleList[drv_handle][samp_handle]: +* sampleTotalBytes: total bytes sent by HMI to the DMA buffer +* sampleLastFill: HMI's last fill position in DMA buffer +* +* So, the number of samples actually played is: +* +* sampleTotalBytes - +* where is how far ahead sampleLastFill is in front of +* _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount) +* +* These values are indices into a circular DMA buffer, so: +* +* if (sampleLastFill >= _lpSOSDMAFillCount) +* = sampleLastFill - _lpSOSDMAFillCount +* else +* = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill +* +* Note that, if using the stereo driver with mono data, you must +* divide LastFill & FillCount by 2, but not TotalBytes. If using the +* stereo driver with stereo data, you must divide all 3 variables +* by 2. +* +* 2) If no audio is playing, but the timer interrupt is running, +* VQATickCount is used as the timer +* +* 3) If no audio is playing & no timer interrupt is going, the DOS 18.2 +* system timer is used. +* +* Regardless of the method, TickOffset is used as an offset from the +* computed time. +* +* INPUTS +* NONE +* +* RESULT +* Time - Time in VQA_TIMETICKS +* +****************************************************************************/ + +unsigned long VQA_GetTime(VQAHandleP *vqap) +{ + #if(VQAAUDIO_ON) + VQAAudio *audio; + VQAConfig *config; + unsigned long fillcount; + unsigned long lastfill; + unsigned long dma_diff; + unsigned long totalbytes; + unsigned long samples; + #endif + + // MEG 09.25.95 - changed from long to unsigned long + unsigned long ticks; + + #if(VQAAUDIO_ON) + switch (TimerMethod) { + + /* If Audio is playing then timing is based on the audio DMA buffer + * position. + */ + case VQA_TMETHOD_AUDIO: + + /* Dereference commonly used data members for quicker access. */ + audio = &vqap->VQABuf->Audio; + config = &vqap->Config; + + /* Compute our current position in the audio track by getting the + * bytes processed by HMI. Then adjust the bytes processed by the + * position of the DMA fill handler, this should tell us exactly + * where we are in the audio track. + */ + fillcount = (unsigned long)(*_lpSOSDMAFillCount[audio->DigiHandle]); + lastfill = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleLastFill; + totalbytes = _lpSOSSampleList[audio->DigiHandle][audio->SampleHandle]->sampleTotalBytes; + + if (totalbytes == 0) { + dma_diff = 0; + } else { + if (lastfill > fillcount) { + dma_diff = lastfill - fillcount; + } else { + dma_diff = (config->HMIBufSize - fillcount) + lastfill; + } + + if (dma_diff > totalbytes) { + dma_diff = totalbytes; + } + } + + /* Calculate the number of samples by taking the total number of + * bytes processed and divide it by the number of channels and + * bits per sample. + */ + samples = totalbytes - dma_diff; + samples -= (audio->NumSkipped * config->HMIBufSize); + samples /= (audio->Channels * (audio->BitsPerSample >> 3)); + + /* The elapsed ticks is calculated by the number of samples + * processed times the tick resolution per second divided by the + * sample rate. + */ + ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate); + ticks += TickOffset; + break; + + /* No audio playing, but timer interrupt is going; use VQATickCount */ + case VQA_TMETHOD_INT: + ticks = (VQATickCount + TickOffset); + break; + + /* No interrupts are going at all; use DOS's time */ + default: + case VQA_TMETHOD_DOS: + { + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + ticks = ((ticks * VQA_TIMETICKS) / 1000L); + ticks += TickOffset; + } + break; + } + #else + { + // MEG 09.23.95 - Use Windows timer. +#if( ! USE_WINDOWS_TIME ) + + struct timeb mytime; + + ftime(&mytime); + ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm; + +#else + + ticks = Get_Game_Time(); + +#endif + +// ticks = ((ticks * VQA_TIMETICKS) / 1000L); + + // MEG 09.25.95 - multiply by 3, divide by 50 instead of multiplying by + // 60 and dividing by 1000. The reason for this is that multiplying by + // 60 causes an overflow pretty quickly if the value from Get_Game_Time + // is high. + ticks = ((ticks * 3L) / 50L); + + ticks += TickOffset; + } + #endif + + return (ticks); +} + + +/**************************************************************************** +* +* NAME +* VQA_TimerMethod - Get timer method being used. +* +* SYNOPSIS +* Method = VQA_TimerMethod() +* +* long VQA_TimerMethod(void); +* +* FUNCTION +* Returns the ID of the current timer method being used. +* +* INPUTS +* NONE +* +* RESULT +* Method - Method used for the timer. +* +****************************************************************************/ + +long VQA_TimerMethod(void) +{ + #if(VQAAUDIO_ON) + return (TimerMethod); + #else + return (VQA_TMETHOD_DOS); + #endif +} + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + + \ No newline at end of file diff --git a/WINVQ/VQA32/OLD/BCC32.CFG b/WINVQ/VQA32/OLD/BCC32.CFG new file mode 100644 index 0000000..7e1d1b9 --- /dev/null +++ b/WINVQ/VQA32/OLD/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vqa32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/WINVQ/VQA32/OLD/CAPTION.CPP b/WINVQ/VQA32/OLD/CAPTION.CPP new file mode 100644 index 0000000..baa2b50 --- /dev/null +++ b/WINVQ/VQA32/OLD/CAPTION.CPP @@ -0,0 +1,403 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.c +* +* DESCRIPTION +* Text caption process/display manager. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "caption.h" + +#if( VQACAPTIONS_ON ) + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define NUM_NODES 3 + +/* Function prototypes. */ +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext); +static void RemCaptionNode(CaptionList *list, CaptionNode *node); + + +/**************************************************************************** +* +* NAME +* OpenCaptions - Initialize the caption system. +* +* SYNOPSIS +* OpenCaptions(Captions, Font) +* +* CaptionInfo *OpenCaptions(void *, void *); +* +* FUNCTION +* Allocate and initialize resources used by the caption system. +* +* INPUTS +* Captions - Captions to process. +* Font - Font to use to display captions. +* +* RESULT +* CaptionInfo - Caption information structure. +* +* SEE ALSO +* CloseCaption +* +****************************************************************************/ + +CaptionInfo *OpenCaptions(void *captions, void *font) +{ + CaptionInfo *cap = NULL; + CaptionNode *node; + FontInfo *fi; + long i; + + /* Allocate memory for the captioning system. */ + cap = (CaptionInfo *)malloc(sizeof(CaptionInfo) + (sizeof(CaptionNode) + * NUM_NODES)); + + if (cap != NULL) { + memset(cap,0,(sizeof(CaptionInfo) + (sizeof(CaptionNode) * NUM_NODES))); + cap->Buffer = captions; + cap->Next = (CaptionText *)captions; + + /* Initialize font */ + fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk); + cap->Font = font; + cap->FontHeight = fi->MaxHeight; + cap->FontWidth = fi->MaxWidth; + + /* Initialize list header. */ + cap->List.Head = (CaptionNode *)&cap->List.Tail; + cap->List.Tail = NULL; + cap->List.TailPred = (CaptionNode *)&cap->List.Head; + + /* Link nodes. */ + node = (CaptionNode *)((char *)cap + sizeof(CaptionInfo)); + + for (i = 0; i < NUM_NODES; i++) { + node->Succ = cap->List.Head; + cap->List.Head = node; + node->Pred = (CaptionNode *)&cap->List.Head; + node->Succ->Pred = node; + + /* Next node. */ + node = (CaptionNode *)((char *)node + sizeof(CaptionNode)); + } + } + + return (cap); +} + + +/**************************************************************************** +* +* NAME +* CloseCaptions - Shutdown the caption system. +* +* SYNOPSIS +* CloseCaptions(CaptionInfo) +* +* void CloseCaptions(CaptionInfo *); +* +* FUNCTION +* Free the resources allocated by the caption system. +* +* INPUTS +* CaptionInfo - Caption information structure. +* +* RESULT +* NONE +* +* SEE ALSO +* OpenCaptions +* +****************************************************************************/ + +void CloseCaptions(CaptionInfo *cap) +{ + free(cap); +} + + +/**************************************************************************** +* +* NAME +* DoCaption - Process and display caption text. +* +* SYNOPSIS +* DoCaption(Captions, Frame) +* +* void DoCaption(CaptionInfo *, unsigned long); +* +* FUNCTION +* Process the caption events. +* +* INPUTS +* Captions - Pointer to CaptionInfo structure. +* Frame - Current frame number being processed. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DoCaptions(CaptionInfo *cap, unsigned long frame) +{ + CaptionText *captext; + CaptionNode *node; + void const *oldfont; + long width; + long i; + + /* Initialize variables. */ + oldfont = Set_Font((char *)cap->Font); + + /*------------------------------------------------------------------------- + * Process the captions that are on the active queue. + *-----------------------------------------------------------------------*/ + node = cap->List.Head; + + while ((node->Succ != NULL) && (node->Flags & CNF_USED)) { + captext = node->Captext; + + /* Clear the any previous captions that have expired. */ + if (captext->OffFrame <= frame) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + + /* Remove the caption from the active list. */ + node = node->Pred; + RemCaptionNode(&cap->List, node->Succ); + } else { + if (captext->CPF != 0) { + + /* If a NULL terminator is not found then display the next set of + * characters, otherwise remove the node. + */ + if (*node->Char != 0) { + Set_Font_Palette_Range(&captext->BgPen, 0, 1); + + for (i = 0; i < captext->CPF; i++) { + + /* Check for terminator. */ + if (*node->Char == 0) { + captext->CPF = 0; + break; + } + /* Check for newline. */ + else if (*node->Char == 0x0D) { + node->Char++; + node->CurX = captext->Xpos; + node->CurY += cap->FontHeight; + node->BoundH += cap->FontHeight; + } + + Draw_Char(*node->Char, node->CurX, node->CurY); + node->CurX += Char_Pixel_Width(*node->Char); + node->Char++; + } + } + } else if (captext->Flags & CTF_FLASH) { + if (frame & 4) { + Fill_Rect(captext->Xpos, captext->Ypos, + (captext->Xpos + node->BoundW - 1), + (captext->Ypos + node->BoundH - 1), 0); + } else { + Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + } + } + } + + /* Next node. */ + node = node->Succ; + } + + /*------------------------------------------------------------------------- + * Process any captions that are waiting to be activated. + *-----------------------------------------------------------------------*/ + captext = cap->Next; + + while (captext->OnFrame <= frame) { + + width = String_Pixel_Width(captext->Text); + + switch (captext->Flags & CTF_JUSTIFY) { + case CTF_RIGHT: + captext->Xpos = (319 - width); + break; + + case CTF_LEFT: + captext->Xpos = 0; + break; + + case CTF_CENTER: + captext->Xpos = (160 - (width / 2)); + break; + + default: + break; + } + + /* Display the text and record its bounding box. */ + if (captext->CPF == 0) { + i = Text_Print(captext->Text, captext->Xpos, captext->Ypos, + captext->FgPen, captext->BgPen); + + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = (cap->FontHeight * i); + } else { + node = AddCaptionNode(&cap->List, captext); + node->BoundW = width; + node->BoundH = cap->FontHeight; + } + + /* Next */ + cap->Next = (CaptionText *)(((char *)captext) + captext->Size); + captext = cap->Next; + } + + Set_Font(oldfont); +} + + +/**************************************************************************** +* +* NAME +* AddCaptionNode - Add a caption to the processing list. +* +* SYNOPSIS +* Node = AddCaptionNode(List, Caption) +* +* CaptionNode *AddCaptionNode(CaptionList *, CaptionText *); +* +* FUNCTION +* Add a caption the caption processing list. +* +* INPUTS +* List - Caption processing list. +* Caption - Caption to add to the list. +* +* RESULT +* Node - Pointer to node, otherwise NULL if error. +* +****************************************************************************/ + +static CaptionNode *AddCaptionNode(CaptionList *list, CaptionText *captext) +{ + CaptionNode *node = NULL; + + /* If this list is not full. */ + node = list->TailPred; + + if (!(node->Flags & CNF_USED)) { + + /* Remove the node from the tail. */ + node->Pred->Succ = node->Succ; + list->TailPred = node->Pred; + + node->Flags |= CNF_USED; + node->Captext = captext; + node->Char = captext->Text; + node->CurX = captext->Xpos; + node->CurY = captext->Ypos; + + /* Add the node to the head. */ + node->Succ = list->Head; + list->Head = node; + node->Pred = (CaptionNode *)&list->Head; + node->Succ->Pred = node; + } + + return (node); +} + + +/**************************************************************************** +* +* NAME +* RemCaptionNode - Remove a caption from the processing list. +* +* SYNOPSIS +* RemCaptionNode(List, Node) +* +* void RemCaptionNode(CaptionList *, CaptionNode *); +* +* FUNCTION +* Remove the caption from the processing list. Mark the node as unused +* and put it at the tail of the list. +* +* INPUTS +* List - Caption processing list. +* Node - Caption node to remove. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void RemCaptionNode(CaptionList *list, CaptionNode *node) +{ + /* If the nodes successor is null then we are at the tail. */ + if (node->Succ != NULL) { + + /* Mark the node as unused. */ + node->Flags = 0; + + /* Relink the node to the tail. */ + node->Succ->Pred = node->Pred; + node->Pred->Succ = node->Succ; + node->Succ = (CaptionNode *)&list->Tail; + node->Pred = list->TailPred; + list->TailPred->Succ = node; + list->TailPred = node; + } +} + + +#endif // VQACAPTIONS_ON + \ No newline at end of file diff --git a/WINVQ/VQA32/OLD/CAPTION.H b/WINVQ/VQA32/OLD/CAPTION.H new file mode 100644 index 0000000..cda7a97 --- /dev/null +++ b/WINVQ/VQA32/OLD/CAPTION.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQACAPTION_H +#define VQACAPTION_H +/**************************************************************************** +* +* 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 +* VQA player library (32 bit protected mode) +* +* FILE +* caption.h +* +* DESCRIPTION +* Text caption definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + + +#define VQACAPTIONS_ON 0 + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* CaptionNode: Node describing a caption to process. + * + * Succ - Pointer to the next node in the list (successor). + * Pred - Pointer to the previous node in the list (predecessor). + * Flags - Status flags. + * CapText - Pointer to the CaptionText being processed. + * Char - Pointer to current character in the string. + * CurX - Current X position. + * CurY - Current Y position. + * BoundW - Bounding width of text. + * BoundH - Bounding height of text. + */ +typedef struct _CaptionNode { + struct _CaptionNode *Succ; + struct _CaptionNode *Pred; + unsigned short Flags; + CaptionText *Captext; + char *Char; + unsigned short CurX; + unsigned short CurY; + unsigned short BoundW; + unsigned short BoundH; +} CaptionNode; + +/* CaptionNode flag definitions. */ +#define CNB_USED 0 /* This node is being used. */ +#define CNF_USED (1<. +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* config.c +* +* DESCRIPTION +* Player configuration routines. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +****************************************************************************/ + +#include +#include +#include +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Default configuration structure. */ +static VQAConfig _defaultconfig = { + + /* DrawerCallback: This is a function that is called for every frame + * in the movie. + */ + NULL, + + /* EventHandler: This is a function that is called for every event that + * the client requested to be notified about. + */ + NULL, + + /* NotifyFlags: Flags representing the events the client wishes to be + * notified about during playback. + */ + NULL, + + /* Vmode: Video mode to use. */ + MCGA, + + /* VBIBit: Vertical blank bit polarity. */ + -1, + + /* ImageBuf: Pointer to image buffer to draw into. */ + NULL, + + /* ImageWidth, ImageHeight: Width and height dimensions of image buffer. + * A width and height value of -1 tells the player to consider the image + * buffer as having the same dimensions as the frames in the movie. + */ + 320, 200, /* Image width and height */ + + /* X1, Y1: These are the coordinates to put the movies frame in the image + * buffer. Values of -1 tell the drawer to center the frames in the buffer. + */ + -1, -1, + + /* FrameRate: The rate to load the frames at. A value of -1 tells the + * player to use the framerate of the movie. + */ + -1, + + /* DrawRate: The rate to draw the frames at. A value of -1 tells the + * player to use the framerate of the movie. A value of 0 tells the player + * to use a fixed rate based on the frame size. + */ + -1, + + /* TimerMethod: Timer method to use for playback. */ + -1, + + /* DrawFlags: Various drawing related flags. */ + 0, + + /* OptionFlags: Various player options. */ + VQAOPTF_AUDIO, + + /* NumFrameBufs: The number of frame buffers to allocate/use. */ + 6, + + /* NumCBBufs: The number of codebook buffers to allocate/use. */ + 3, + + /* VocFile: Filename of audio track override. A value of 0 tells the + * player not to override the movies audio track. + */ + NULL, + + /* AudioBuf: Audio buffer to use. A value of 0 tells the player that + * it has to allocate a buffer itself. + */ + NULL, + + /* AudioBufSize: Size of audio buffer to use/allocate. A value of -1 + * tells the player to compute the buffer size from the audio + * information in the movie. + */ + -1, + + /* AudioRate: Audio playback rate in samples per second. A value of -1 + * tells the player to use the audio rate of the movie. + */ + -1, + + /* Volume: Volume level to playback audio track. */ + 0x00FF, + + /* HMIBufSize: Size of HMIs internal buffer. */ + 2048L, + + /* DigiHandle: Handle to an initialized HMI sound driver. A value of -1 + * tells the player it must initialize the HMI sound driver itself. + */ + -1, + + /* DigiCard: HMI ID of audio card to use. A value of 0 tells the player + * not to use any card. A value of -1 tells the player to autodetect the + * card in the system. + */ + -1, + + /* DigiPort: Port address of the sound card. A value of -1 tells the player + * to autodetect this address. + */ + -1, + + /* DigiIRQ: Interrupt number of sound card. A value of -1 tells the player + * to autodetect the interrupt used by the card. + */ + -1, + + /* DigiDMA: DMA channel of the sound card. A value of -1 tells the player + * to autodetect the channel used by the card. + */ + -1, + + /* Language: Prefered language. */ + 0, + + /* CaptionFont: Caption text font. */ + NULL, +}; + +/* Supported video modes. */ +#if(VQAVIDEO_ON) +enum VMTAGS { + VMTAG_NONE = 0, + + #if(VQAMCGA_ON) + VMTAG_MCGA, + VMTAG_MCGA_BUF, + #endif + + #if(VQAXMODE_ON) + VMTAG_XMODE320X200, + VMTAG_XMODE320X200_BUF, + VMTAG_XMODE320X200_VRAM, + VMTAG_XMODE320X240, + VMTAG_XMODE320X240_BUF, + VMTAG_XMODE320X240_VRAM, + #endif + + #if(VQAVESA_ON) + VMTAG_VESA640X480_BUF, + VMTAG_VESA640X480_X2, + VMTAG_VESA320X200, + VMTAG_VESA320X200_BUF, + #endif +}; + +typedef struct _VideoModeTag { + char const *token; + long id; +} VideoModeTag; + +VideoModeTag VideoModeTags[] = { + {"NONE",VMTAG_NONE}, + + #if(VQAMCGA_ON) + {"MCGA", VMTAG_MCGA}, + {"MCGA_BUF",VMTAG_MCGA_BUF}, + #endif /* VQAMCGA_ON */ + + #if(VQAXMODE_ON) + {"XMODE_320X200", VMTAG_XMODE320X200}, + {"XMODE_320X200_BUF", VMTAG_XMODE320X200_BUF}, + {"XMODE_320X200_VRAM",VMTAG_XMODE320X200_VRAM}, + {"XMODE_320X240", VMTAG_XMODE320X240}, + {"XMODE_320X240_BUF", VMTAG_XMODE320X240_BUF}, + {"XMODE_320X240_VRAM",VMTAG_XMODE320X240_VRAM}, + #endif /* VQAXMODE_ON */ + + #if(VQAVESA_ON) + {"VESA_640X480_BUF",VMTAG_VESA640X480_BUF}, + {"VESA_640X480_X2", VMTAG_VESA640X480_X2}, + {"VESA_320X200", VMTAG_VESA320X200}, + {"VESA_320X200_BUF",VMTAG_VESA320X200_BUF}, + #endif /* VQAVESA_ON */ + + {NULL, NULL} +}; +#endif /* VQAVIDEO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_INIConfig - Initialize VQAConfig structure with INI settings. +* +* SYNOPSIS +* VQA_INIConfig(Config) +* +* void VQA_INIConfig(VQAConfig *); +* +* FUNCTION +* Initializes the configuration structure from the player INI file. +* +* INPUTS +* Config - Pointer to VQAConfig structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_INIConfig(VQAConfig *config) +{ + char *ininame; + char buf[80]; + long i; + + /* Set all Config entries to 0. */ + memset(config, 0, sizeof(VQAConfig)); + + /* Retrieve player INI filename from an enviroment variable if + * it is provided. + */ + if ((ininame = getenv("VQACFG")) == NULL) { + ininame = "PLAYER.INI"; + } + + /*------------------------------------------------------------------------- + * VIDEO MODE AND DRAW FLAGS + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + /* Get video mode from INI */ + GetINIString("Player", "PlayerMode", "MCGA", buf, 80, ininame); + + /* Search supported modes for a match. */ + i = 0; + + while (VideoModeTags[i].token != NULL) { + if (stricmp(buf, VideoModeTags[i].token) == 0) { + break; + } + i++; + } + + /* Setup for requested mode */ + switch (VideoModeTags[i].id) { + + /* MCGA direct */ + #if(VQAMONO_ON) + case VMTAG_MCGA: + config->Vmode = MCGA; + break; + + /* MCGA buffered */ + case VMTAG_MCGA_BUF: + config->Vmode = MCGA; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAMCGA_ON */ + + /* XMODE direct (320x200) */ + #if(VQAXMODE_ON) + case VMTAG_XMODE320X200: + config->Vmode = XMODE_320X200; + break; + + /* XMODE buffered (320x200) */ + case VMTAG_XMODE320X200_BUF: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x200) */ + case VMTAG_XMODE320X200_VRAM: + config->Vmode = XMODE_320X200; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + + /* XMODE direct (320x240) */ + case VMTAG_XMODE320X240: + config->Vmode = XMODE_320X240; + break; + + /* XMODE buffered (320x240) */ + case VMTAG_XMODE320X240_BUF: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* XMODE VRAM codebook (320x240) */ + case VMTAG_XMODE320X240_VRAM: + config->Vmode = XMODE_320X240; + config->DrawFlags |= VQACFGF_VRAMCB; + break; + #endif /* VQAXMODE_ON */ + + /* VESA buffered (640x480_256) */ + #if(VQAVESA_ON) + case VMTAG_VESA640X480_BUF: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= VQACFGF_BUFFER; + break; + + /* VESA buffered scaled (640x480_256) */ + case VMTAG_VESA640X480_X2: + config->Vmode = VESA_640X480_256; + config->DrawFlags |= (VQACFGF_BUFFER|VQACFGF_SCALEX2); + break; + + /* VESA direct (320x200_32k) */ + case VMTAG_VESA320X200: + config->Vmode = VESA_320X200_32K_1; + break; + + /* VESA buffered (320x200_32k) */ + case VMTAG_VESA320X200_BUF: + config->Vmode = VESA_320X200_32K_1; + config->DrawFlags |= VQACFGF_BUFFER; + break; + #endif /* VQAVESA_ON */ + + /* Default to MCGA direct */ + VMTAG_NONE: + default: + config->Vmode = MCGA; + break; + } + #endif /* VQAVIDEO_ON */ + + /* Get framerate and drawrate. */ + GetINIString("Player", "FrameRate", "-1", buf, 80, ininame); + config->FrameRate = atoi(buf); + + GetINIString("Player", "DrawRate", "Variable", buf, 80, ininame); + + if (!stricmp(buf, "Variable")) { + config->DrawRate = -1; + } else { + config->DrawRate = 0; + } + + /*------------------------------------------------------------------------- + * AUDIO SETTINGS + *-----------------------------------------------------------------------*/ + GetINIString("Player", "AudioRate", "-1", buf, 80, ininame); + config->AudioRate = atoi(buf); + + /* OptionFlags */ + GetINIString("Player", "SoundEnabled", "True", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_AUDIO; + } else { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + + /* Default audio settings. */ + config->AudioBufSize = 32768U; + config->HMIBufSize = 2048; + config->DigiHandle = -1; + config->Volume = 0x00FF; + config->DigiCard = 0xFFFF; + config->DigiPort = -1; + config->DigiIRQ = -1; + config->DigiDMA = -1; + + /* Configure sound hardware */ + GetINIString("Player", "Port", "-1", buf, 80, ininame); + + if (!stricmp(buf, "-1")) { + config->DigiPort = -1; + } else { + sscanf(buf, "%x", &config->DigiPort); + } + + GetINIString("Player", "IRQ", "-1", buf, 80, ininame); + config->DigiIRQ = atoi(buf); + GetINIString("Player", "DMA", "-1", buf, 80, ininame); + config->DigiDMA = atoi(buf); + + /*------------------------------------------------------------------------- + * GENERAL OPTIONS + *-----------------------------------------------------------------------*/ + + /* Enable/Disable single stepping */ + GetINIString("Player", "SingleStep", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_STEP; + config->DrawFlags |= VQACFGF_NOSKIP; + } else { + config->OptionFlags &= (~VQAOPTF_STEP); + } + + /* Enable/Disable Slowpalette */ + GetINIString("Player", "SlowPalette", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_SLOWPAL; + } else { + config->OptionFlags &= (~VQAOPTF_SLOWPAL); + } + + /* Enable/Disable monochrome display */ + GetINIString("Player", "MonoOutput", "False", buf, 80, ininame); + + if (!stricmp(buf, "True") || !stricmp(buf, "1")) { + config->OptionFlags |= VQAOPTF_MONO; + } else { + config->OptionFlags &= (~VQAOPTF_MONO); + } + + /* Frame and codebook buffers */ + config->NumFrameBufs = 6; + config->NumCBBufs = 3; +} + + +/**************************************************************************** +* +* NAME +* VQA_DefaultConfig - Initialize VQAConfig structure with defaults. +* +* SYNOPSIS +* VQA_DefaultConfig(Config); +* +* void VQA_DefaultConfig(VQAConfig *); +* +* FUNCTION +* Initialize configuration with default settings. +* +* INPUTS +* Config - Pointer to VQA configuration structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_DefaultConfig(VQAConfig *config) +{ + memcpy(config, &_defaultconfig, sizeof(VQAConfig)); +} + + + + diff --git a/WINVQ/VQA32/OLD/DRAWER.CPP b/WINVQ/VQA32/OLD/DRAWER.CPP new file mode 100644 index 0000000..364f810 --- /dev/null +++ b/WINVQ/VQA32/OLD/DRAWER.CPP @@ -0,0 +1,2418 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* drawer.c +* +* DESCRIPTION +* Frame drawing and page flip control. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* June 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Configure_Drawer - Configure the drawer routines. +* +* PRIVATE +* Select_Frame - Selects frame to draw and preforms frame +* skip. +* Prepare_Frame - Process/Decompress frame information. +* DrawFrame_Xmode - Draws a frame directly to Xmode screen. +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident +* Codebook. +* PageFlip_Xmode - Page flip Xmode display. +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* PageFlip_MCGA - Page flip MCGA display. +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* DrawFrame_VESA640 - Draws a frame in VESA640 format. +* DrawFrame_VESA320_32K - Draws a frame to VESA320_32K screen. +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* PageFlip_VESA - Page flip VESA display. +* DrawFrame_Buffer - Draw a frame to a buffer. +* PageFlip_Nop - Do nothing page flip. +* UnVQ_Nop - Do nothing UnVQ. +* Mask_Rect - Sets non-drawable rectangle in image. +* Mask_Pointers - Mask vector pointer that are in the mask +* rectangle. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "unvq.h" +#include "vqaplayp.h" +#include +#include "caption.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ +static long Select_Frame(VQAHandleP *vqap); +static void Prepare_Frame(VQAData *vqabuf); + +#if(VQAMCGA_ON) +static long DrawFrame_MCGABuf(VQAHandle *vqa); +static long PageFlip_MCGABuf(VQAHandle *vqa); +static long DrawFrame_MCGA(VQAHandle *vqa); +static long PageFlip_MCGA(VQAHandle *vqa); +#endif /* VQAMCGA_ON */ + +#if(VQAXMODE_ON) +static long DrawFrame_XmodeBuf(VQAHandle *vqa); +static long DrawFrame_Xmode(VQAHandle *vqa); +static long DrawFrame_XmodeVRAM(VQAHandle *vqa); +static void PageFlip_Xmode(VQAHandle *vqabuf); +#endif /* VQAXMODE_ON */ + +#if(VQAVESA_ON) +static long DrawFrame_VESA640(VQAHandle *vqa); +static long DrawFrame_VESA320_32K(VQAHandle *vqa); +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa); +static void PageFlip_VESA(VQAHandle *vqabuf); +#endif /* VQAVESA_ON */ + +static long DrawFrame_Buffer(VQAHandle *vqa); +static long PageFlip_Nop(VQAHandle *vqa); + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); +#endif + +#if(0) +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2); +static void Mask_Pointers(VQAData *vqabuf); +#endif + + +/**************************************************************************** +* +* NAME +* VQA_GetPalette - Get the palette used in the movie. +* +* SYNOPSIS +* Palette = VQA_GetPalette(VQA) +* +* char *VQA_GetPalette(VQAHandle *); +* +* FUNCTION +* Retrieve the address of the current palette used in the movie. If there +* isn't a palette available then a NULL value will be returned. +* +* INPUTS +* VQA - Pointer to VQAHandle to get palette for. +* +* RESULT +* Palette - Pointer to palette or NULL if no palette available. +* +****************************************************************************/ + +unsigned char *VQA_GetPalette(VQAHandle *vqa) +{ + VQADrawer *drawer; + unsigned char *palette = NULL; + + /* Dereference commonly used data members for quick access. */ + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + + if (drawer->CurPalSize > 0) { + palette = drawer->Palette_24; + } + + return (palette); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetPaletteSize - Get the size of the palette used in the movie. +* +* SYNOPSIS +* PalSize = VQA_GetPaletteSize(VQA) +* +* long VQA_GetPaletteSize(VQAHandle *); +* +* FUNCTION +* Retrieve the size of the current palette used in the movie. If there +* isn't a palette available then a zero size will be returned. +* +* INPUTS +* VQA - Pointer to VQAHandle to get palette for. +* +* RESULT +* PalSize - Size in bytes of the current palette. +* +****************************************************************************/ + +long VQA_GetPaletteSize(VQAHandle *vqa) +{ + VQADrawer *drawer; + + /* Dereference commonly used data members for quick access. */ + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + + return (drawer->CurPalSize); +} + + +/**************************************************************************** +* +* NAME +* VQA_Set_DrawBuffer - Set the buffer to draw the images to. +* +* SYNOPSIS +* VQA_Set_DrawBuffer(VQA, Buffer, Width, Height, XPos, YPos) +* +* void VQA_Set_DrawBuffer(VQAHandle *, unsigned char *, +* unsigned long, unsigned long, unsigned long, +* unsigned long); +* +* FUNCTION +* Set the draw buffer to the buffer provided by the client. +* +* INPUTS +* VQA - Pointer to VQAHandle to set buffer for. +* Buffer - Pointer to new image buffer. +* Width - Width of the buffer in pixels. +* Height - Height of the buffer in pixels. +* XPos - X pixel position in buffer to draw image. +* YPos - Y pixel position in buffer to draw image. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Set_DrawBuffer(VQAHandle *vqa, unsigned char *buffer, + unsigned long width, unsigned long height, + long xpos, long ypos) +{ + VQAHeader *header; + VQADrawer *drawer; + VQAConfig *config; + long origin; + + /* Dereference commonly used data members for quick access. */ + header = &((VQAHandleP *)vqa)->Header; + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /* Set the drawer buffer information. */ + drawer->ImageBuf = buffer; + drawer->ImageWidth = width; + drawer->ImageHeight = height; + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((xpos == -1) && (ypos == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((width - header->ImageWidth) / 2); + drawer->Y1 = ((height - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = xpos; + drawer->Y1 = ypos; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = xpos; + drawer->Y1 = (height - ypos); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (width - xpos); + drawer->Y1 = (height - ypos); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((width * drawer->Y1) + drawer->X1); +} + + +/**************************************************************************** +* +* NAME +* VQA_Configure_Drawer - Configure the drawer routines. +* +* SYNOPSIS +* VQA_Configure_Drawer(VQA) +* +* void VQA_Configure_Drawer(VQAHandleP *); +* +* FUNCTION +* Configure the drawing system for the current movie and configuration +* options. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Configure_Drawer(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAHeader *header; + VQADrawer *drawer; + long origin; + long blkdim; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + header = &vqap->Header; + config = &vqap->Config; + origin = (config->DrawFlags & VQACFGF_ORIGIN); + + /*------------------------------------------------------------------------- + * SET THE DRAW POSITION OF THE MOVIE. + * + * X1 = -1 -- Center image of the X axis, otherwise use X1 value. + * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value. + *-----------------------------------------------------------------------*/ + if ((config->X1 == -1) && (config->Y1 == -1)) { + + #if(VQAVIDEO_ON) + drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2); + drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2); + #else /* VQAVIDEO_ON */ + drawer->X1 = ((drawer->ImageWidth - header->ImageWidth) / 2); + drawer->Y1 = ((drawer->ImageHeight - header->ImageHeight) / 2); + #endif /* VQAVIDEO_ON */ + + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + } else { + switch (origin) { + default: + case VQACFGF_TOPLEFT: + drawer->X1 = config->X1; + drawer->Y1 = config->Y1; + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1); + break; + + case VQACFGF_BOTLEFT: + drawer->X1 = config->X1; + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1); + drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1); + break; + + case VQACFGF_BOTRIGHT: + drawer->X1 = (drawer->ImageWidth - config->X1); + drawer->Y1 = (drawer->ImageHeight - config->Y1); + drawer->X2 = (drawer->X1 - header->ImageWidth); + drawer->Y2 = (drawer->Y1 - header->ImageHeight); + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE UNVQ ROUTINE FOR THE SPECIFIED VIDEO MODE AND BLOCK SIZE. + *-----------------------------------------------------------------------*/ + + /* Pre-compute commonly used values for speed. */ + drawer->BlocksPerRow = header->ImageWidth / header->BlockWidth; + drawer->NumRows = header->ImageHeight / header->BlockHeight; + drawer->NumBlocks = drawer->BlocksPerRow * drawer->NumRows; + blkdim = BLOCK_DIM(header->BlockWidth, header->BlockHeight); + + /* Initialize draw routine vectors to a NOP routine in order to prevent + * a crash. + */ + vqabuf->UnVQ = UnVQ_Nop; + vqabuf->Page_Flip = PageFlip_Nop; + + /* If the client specifies buffering then go ahead an set the unvq + * vector. All of the buffered modes use the same unvq routines. + */ + if (config->DrawFlags & VQACFGF_BUFFER) { + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2; + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + + default: + break; + } + } + + /* Initialize the draw vectors for the specified video mode. */ + switch (config->Vmode) { + + /* MCGA */ + #if(VQAMCGA_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_MCGABuf; + vqabuf->Page_Flip = PageFlip_MCGABuf; + } else { + vqabuf->Draw_Frame = DrawFrame_MCGA; + vqabuf->Page_Flip = PageFlip_MCGA; + + /* MCGA uses the same unvq routines that are used for unvqing + * to a buffer because MCGA mode is just another buffer. However, + * instead of drawing to an allocated RAM buffer we are drawing + * directly to the VRAM video buffer. + */ + switch (blkdim) { + #if(VQABLOCK_2X2) + case BLOCK_2X2: + vqabuf->UnVQ = UnVQ_2x2; + break; + #endif + + #if(VQABLOCK_2X3) + case BLOCK_2X3: + vqabuf->UnVQ = UnVQ_2x3; + break; + #endif + + #if(VQABLOCK_4X2) + case BLOCK_4X2: + #if(!VQAWOOFER_ON) + vqabuf->UnVQ = UnVQ_4x2; + #else + if (config->DrawFlags & VQACFGF_WOOFER) { + vqabuf->UnVQ = UnVQ_4x2_Woofer; + } else { + vqabuf->UnVQ = UnVQ_4x2; + } + #endif + break; + #endif + + #if(VQABLOCK_4X4) + case BLOCK_4X4: + vqabuf->UnVQ = UnVQ_4x4; + break; + #endif + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((320 * drawer->Y1) + drawer->X1); + break; + #endif /* VQAMCGA_ON */ + + /* XMODE */ + #if(VQAXMODE_ON) + case XMODE_320X200: + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_XmodeBuf; + vqabuf->Page_Flip = PageFlip_Xmode; + } else { + vqabuf->Page_Flip = PageFlip_Xmode; + + if (config->DrawFlags & VQACFGF_VRAMCB) { + vqabuf->Draw_Frame = DrawFrame_XmodeVRAM; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_XmodeCB; + break; + #endif /* VQABLOCK_4X2 */ + } + } else { + vqabuf->Draw_Frame = DrawFrame_Xmode; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_Xmode; + break; + #endif /* VQABLOCK_4X2 */ + } + } + } + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = (((320 / 4) * drawer->Y1) + (drawer->X1 / 4)); + break; + #endif /* VQAXMODE_ON */ + + /* VESA */ + #if(VQAVESA_ON) + + /* Currently this is a buffered mode, but should be optimized for + * for screen direct. + */ + case VESA_640X480_256: + vqabuf->Draw_Frame = DrawFrame_VESA640; + vqabuf->Page_Flip = PageFlip_VESA; + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + vqabuf->Draw_Frame = DrawFrame_VESA320_32KBuf; + } else { + vqabuf->Draw_Frame = DrawFrame_VESA320_32K; + + switch (blkdim) { + #if(VQABLOCK_4X2) + case BLOCK_4X2: + vqabuf->UnVQ = UnVQ_4x2_VESA320_32K; + break; + #endif + } + } + break; + #endif /* VQAVESA_ON */ + + /* Purely buffered (Video refresh is up to the client. */ + default: + vqabuf->Draw_Frame = DrawFrame_Buffer; + + /* Pre-compute the draw offset for speed. */ + drawer->ScreenOffset = ((drawer->ImageWidth * drawer->Y1)+drawer->X1); + break; + } +} + + +/**************************************************************************** +* +* NAME +* Select_Frame - Selects frame to draw and preforms frame skip. +* +* SYNOPSIS +* Error = Select_Frame(VQA) +* +* long Select_Frame(VQAHandleP *); +* +* FUNCTION +* Select a frame to draw. This is were the frame skipping/delay is +* performed. +* +* INPUTS +* VQA - Pointer to private VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long Select_Frame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAConfig *config; + VQAFrameNode *curframe; + long desiredframe; + // MEG 11.29.95 - changed from long to unsigned long + unsigned long curtime; + + /* Dereference commonly used data members for quicker access. */ + config = &vqap->Config; + vqabuf = vqap->VQABuf; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Make sure the current frame is drawable. If the frame is not ready + * then we must wait for the loader to catch up. + */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + drawer->WaitsOnLoader++; + return (VQAERR_NOBUFFER); + } + + /* If single stepping then return with the next frame.*/ + if (config->OptionFlags & VQAOPTF_STEP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Find the frame # we should play (rounded to nearest frame): */ + curtime = VQA_GetTime(vqap); +// desiredframe = ((curtime * config->FrameRate) / VQA_TIMETICKS); + // MEG MOD 06.22.95 - Should look for the desired frame to draw, not load, + // right? + desiredframe = ((curtime * config->DrawRate) / VQA_TIMETICKS); + + #if(VQAMONO_ON) + drawer->DesiredFrame = desiredframe; + #endif + + /* Handle the cases where the player is going so fast that it's not time + * to draw this frame yet. + * + * - If the Drawer is using a slower frame rate than the Loader, use a + * delta-time-based wait; otherwise, use the frame number as the wait. + */ + if (config->DrawRate != config->FrameRate) { + if (curtime - drawer->LastTime < (VQA_TIMETICKS / config->DrawRate)) { + return (VQAERR_NOT_TIME); + } + } else { + if (curframe->FrameNum > desiredframe) { + return (VQAERR_NOT_TIME); + } + } + + /* Make sure we draw at least 5 frames per second */ + if ((curframe->FrameNum - drawer->LastFrame) >= (config->FrameRate / 5)) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* If frame skipping is disabled then draw every frame. */ + if (config->DrawFlags & VQACFGF_NOSKIP) { + drawer->LastFrame = curframe->FrameNum; + return (0); + } + + /* Handle the case where the player is going too slow, so we have to skip + * some frames: + * + * - If this is a Key Frame, draw it + * - If this frame's # is less than what we're supposed to draw, skip it + * (Because the 1st 'desiredframe' will be 0, FrameNum MUST be typecast + * to signed WORD for the comparison; otherwise, the comparison uses + * UWORDs, and the first frame is always skipped.) + * - If this is a palette-set frame, set the palette before skipping it + * - Loop until we get the frame we need, or there's no frames available + */ + while (1) { + + /* No frame available; return */ + if ((curframe->Flags & VQAFRMF_LOADED) == 0) { + return (VQAERR_NOBUFFER); + } + + /* Force drawing of a Key Frame */ + if (curframe->Flags & VQAFRMF_KEY) { + break; + } + + /* Skip the frame */ + if (curframe->FrameNum < desiredframe) { + + /* Handle a palette in a skipped frame: + * + * - Stash the palette in Drawer.Palette_24 + * - Set the Drawer.Flags VQADRWF_SETPAL bit, to tell the page-flip + * routines that this palette must be set + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + + /* Un-LCW if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)curframe->Palette, + vqabuf->Max_Pal_Size); + + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Stash the palette */ + memcpy(drawer->Palette_24, curframe->Palette, curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + drawer->Flags |= VQADRWF_SETPAL; + } + + /* Invoke callback with NULL screen ptr */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Skip the frame */ + curframe->Flags = 0L; + curframe = curframe->Next; + drawer->CurFrame = curframe; + drawer->NumSkipped++; + } else { + break; + } + } + + drawer->LastFrame = curframe->FrameNum; + drawer->LastTime = curtime; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Prepare_Frame - Process/Decompress frame information. +* +* SYNOPSIS +* Prepare_Frame(VQAData) +* +* void Prepare_Frame(VQAData *); +* +* FUNCTION +* Decompress and preprocess the various frame elements (codebook, +* pointers, palette, etc...) +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Prepare_Frame(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + VQACBNode *codebook; + + /* Dereference commonly used data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + codebook = curframe->Codebook; + + /* Decompress the codebook, if needed */ + if (codebook->Flags & VQACBF_CBCOMP) { + + /* Decompress the codebook. */ + LCW_Uncompress((char *)codebook->Buffer + codebook->CBOffset, + (char *)codebook->Buffer, vqabuf->Max_CB_Size); + + /* Mark as uncompressed for the next time we use it */ + codebook->Flags &= (~VQACBF_CBCOMP); + } + + /* Decompress the palette, if needed */ + if (curframe->Flags & VQAFRMF_PALCOMP) { + curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset,(char *)curframe->Palette,vqabuf->Max_Pal_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + } + + /* Decompress the pointer data, if needed */ + if (curframe->Flags & VQAFRMF_PTRCOMP) { + LCW_Uncompress((char *)curframe->Pointers + curframe->PtrOffset, + (char *)curframe->Pointers, vqabuf->Max_Ptr_Size); + + /* Mark as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + } + + /* Mask the pointers */ + #if(0) + Mask_Pointers(vqabuf); + #endif +} + + +#if(VQAXMODE_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_Xmode - Draws a frame in Xmode format (Screen direct). +* +* SYNOPSIS +* Error = DrawFrame_Xmode(VQA) +* +* long DrawFrame_Xmode(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_XmodeBuf(VQA) +* +* long DrawFrame_XmodeBuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Un-VQ into ImageBuf +* - Copy ImageBuf to video RAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf into the correct part of the +* Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAHeader *header; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + header = &((VQAHandleP *)vqa)->Header; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + header->ImageWidth); + + /* Copy the buffer */ + scrn = GetXHidPage(); + + if ((header->ImageWidth < 320) || (header->ImageHeight < 200)) { + Xmode_Blit(drawer->ImageBuf, scrn + drawer->ScreenOffset, + header->ImageWidth, header->ImageHeight); + } else { + Xmode_BufferCopy_320x200(drawer->ImageBuf,scrn + drawer->ScreenOffset); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident Codebook. +* +* SYNOPSIS +* Error = DrawFrame_XmodeVRAM(VQA) +* +* long DrawFrame_XmodeVRAM(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Download Codebook if it isn't already (do this before skipping +* frames, so Select_Frame will smooth out the delay) +* - Skip frames +* - UnLCW frame +* - Convert Pointers to VRAM format +* - Un-VQ by copying codebook blocks within VRAM +* - Wait on Update_Enabled +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* Flip X-Page +* Set Palette from Flipper.CurFrame +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the Xmode screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_XmodeVRAM(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *scrn; + long rc; + + /* Dereference data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Download codebook to VRAM */ + if ((curframe->Codebook->Flags & VQACBF_DOWNLOADED) == 0) { + Upload_4x2CB(curframe->Codebook->Buffer, + ((VQAHandleP *)vqa)->Header.CBentries + + ((VQAHandleP *)vqa)->Header.Num1Colors); + + curframe->Codebook->Flags |= VQACBF_DOWNLOADED; + } + + /* Convert pointers to VRAM format */ + XlatePointers(curframe->Pointers, drawer->NumBlocks); + + /* Un-VQ the image */ + scrn = GetXHidPage(); + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + scrn + drawer->ScreenOffset, drawer->BlocksPerRow, + drawer->NumRows, 320); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(scrn, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Xmode - Page flip Xmode display. +* +* SYNOPSIS +* PageFlip_Xmode(VQA) +* +* void PageFlip_Xmode(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_Xmode(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned char *pal; + VQAConfig *config; + long palsize; + long slowpal; + + /* Dereference commonly used data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for NoVB (active scan) + * - Flip the page (doesn't take effect until next active scan + * - Wait for VB + * - Set the palette + * - Otherwise, just flip the page + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + FlipXPage(); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } else { + FlipXPage(); + } +} +#endif /* VQAXMODE_ON */ + + +#if(VQAMCGA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_MCGA - Draws a frame directly to MCGA screen. +* +* SYNOPSIS +* Error = DrawFrame_MCGA(VQA) +* +* long DrawFrame_MCGA(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set palette +* UnVQ to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the correct spot on +* the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + /* Dereference commonly used data members for quicker access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Select the frame to draw. */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGA - Page flip MCGA display. +* +* SYNOPSIS +* PageFlip_MCGA(VQA) +* +* long PageFlip_MCGA(VQAHandle *); +* +* FUNCTION +* Since the MCGA mode only has one buffer, the drawing is actually done +* at this point. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *ptrs; + unsigned char *cb; + long palsize; + long slowpal; + long blocksperrow; + long numrows; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /*------------------------------------------------------------------------- + * WAIT FOR THE VERTICAL BLANK TO SET THE PALETTE. + *-----------------------------------------------------------------------*/ + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Wait for the VBlank. */ + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + /* Set the palette. */ + if (curframe->Flags & VQAFRMF_PALETTE) { + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + } + + /*------------------------------------------------------------------------- + * UNVQ THE FRAME DIRECTLY TO THE MCGA SCREEN. + *-----------------------------------------------------------------------*/ + + /* Get screen address, blocks per row and number of rows. */ + #ifndef PHARLAP_TNT + scrn = (unsigned char *)(0xA0000 + (unsigned long)drawer->ScreenOffset); + #else + FP_SET(scrn, drawer->ScreenOffset, 0x1C); + #endif + + blocksperrow = drawer->BlocksPerRow; + numrows = drawer->NumRows; + ptrs = curframe->Pointers; + cb = curframe->Codebook->Buffer; + vqabuf->UnVQ(cb, ptrs, scrn, blocksperrow, numrows, 320); + + #if(VQACAPTIONS_ON) + /* Process captions. */ + if ((((VQAHandleP *)vqa)->Caption != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + DoCaptions(((VQAHandleP *)vqa)->Caption, curframe->FrameNum); + } + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_MCGABuf(VQA) +* +* long DrawFrame_MCGABuf(VQAHandle *); +* +* FUNCTION +* Algorithm: +* - Skip frames +* - UnLCW frame +* - Wait on Update_Enabled (can't use imgbuf til User_Update's done) +* - Un-VQ into ImageBuf +* - Set the Flipper's CurFrame +* - Set Update_Enabled +* - Go to next frame +* - User_Update: +* set Palette from Flipper.CurFrame +* copy ImageBuf to screen +* +* This function implements a sort of cooperative multitasking. If the +* Drawer hits a "wait state", where it has to wait for Update_Enabled +* to toggle, it sets a flag and returns. This flag is checked on entry +* to see if we need to jump to the proper execution point. This should +* improve performance on some platforms. +* +* This routine handles small images by UnVQ'ing into the upper-left +* corner of ImageBuf, then copying ImageBuf onto the screen. +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf; + #else + FP_SET(scrn, drawer->ImageBuf, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_MCGABuf - Page flip a buffered MCGA display. +* +* SYNOPSIS +* PageFlip_MCGABuf(VQA) +* +* void PageFlip_MCGABuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successfull, otherwise VQAERR_??? +* +****************************************************************************/ + +static long PageFlip_MCGABuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + unsigned char *buf; + unsigned char *scrn; + long palsize; + long slowpal; + long imgwidth; + long imgheight; + + /* Derefernce commonly used data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* - If this is a palette-set frame: + * - Wait for the vertical blank + * - Set the palette + * - Copy ImageBuf to SEENPAGE: + * - use blit routine if image is smaller than full-screen, since the + * buffer copy assumes a full-screen image + */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Draw image to the screen. */ + imgwidth = ((VQAHandleP *)vqa)->Header.ImageWidth; + imgheight = ((VQAHandleP *)vqa)->Header.ImageHeight; + buf = drawer->ImageBuf; + + #ifndef PHARLAP_TNT + scrn = (unsigned char *)0xA0000; + #endif + + if ((imgwidth < 320) || (imgheight < 200)) { + #ifndef PHARLAP_TNT + scrn += drawer->ScreenOffset; + #else + scrn = (unsigned char *)drawer->ScreenOffset; + #endif + + MCGA_Blit(buf, scrn, imgwidth, imgheight); + } else { + MCGA_BufferCopy(buf, NULL); + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(NULL, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + return (0); +} +#endif /* VQAMCGA_ON */ + + +#if(VQAVESA_ON) +/**************************************************************************** +* +* NAME +* DrawFrame_VESA640 - Draws a frame in VESA 640 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA640(VQA) +* +* long DrawFrame_VESA640(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA640(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *scrn; + #else + FARPTR scrn; + #endif + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Drawer.WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + #ifndef PHARLAP_TNT + scrn = drawer->ImageBuf + drawer->ScreenOffset; + #else + FP_SET(scrn, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32K - Draws a frame in VESA 320 format. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32K(VQA) +* +* long DrawFrame_VESA320_32K(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32K(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* Dereference current frame. */ + curframe = drawer->CurFrame; + + /* Translate palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* UnVQ directly to screen */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->Palette_15, grain, 0, 0); + #else + { + FARPTR temp; + + FP_SET(temp, drawer->Palette_15, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, temp, + grain, 0, 0); + } + #endif + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for VQ_Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a +* buffer. +* +* SYNOPSIS +* Error = DrawFrame_VESA320_32KBuf(VQA) +* +* long DrawFrame_VESA320_32KBuf(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + long grain; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + + /* UnVQ buffered mode */ + #ifndef PHARLAP_TNT + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, + drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + #else + { + FARPTR scrn; + + FP_SET(scrn, drawer->ImageBuf, 0x14); + + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn, + drawer->BlocksPerRow, drawer->NumRows, + ((VQAHandleP *)vqa)->Header.image_width); + } + #endif + + /* Translate the palette to 15-bit */ + if (curframe->Flags & VQAFRMF_PALETTE) { + TranslatePalette(curframe->Palette, drawer->Palette_15, + curframe->PaletteSize); + } + else if (drawer->Flags & VQADRWF_SETPAL) { + TranslatePalette(drawer->Palette_24, drawer->Palette_15, + drawer->CurPalSize); + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + Buf_320x200_To_VESA_32K(drawer->ImageBuf, drawer->Palette_15, grain); + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(0, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + vqabuf->Flags &= (~VQADATF_DSLEEP); + drawer->WaitsOnFlipper++; + } + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* PageFlip_VESA - Page flip VESA display. +* +* SYNOPSIS +* PageFlip_VESA(VQA) +* +* void PageFlip_VESA(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQAHandle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void PageFlip_VESA(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + unsigned char *pal; + long palsize; + long slowpal; + long grain; + + /* Dereference date members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + drawer = &vqabuf->Drawer; + curframe = vqabuf->Flipper.CurFrame; + + /* Pre-decode 'vqa' for speed */ + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + + /* Set the palette */ + if (curframe->Flags & VQAFRMF_PALETTE) { + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(pal, palsize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize); + } + } + else if (drawer->Flags & VQADRWF_SETPAL) { + drawer->Flags &= (~VQADRWF_SETPAL); + WaitNoVB(vqabuf->VBIBit); + WaitVB(vqabuf->VBIBit); + + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal); + } + + /* Notify the client of the palette change. */ + if ((config->NotifyFlags & VQAEVENT_PALETTE) + && (config->EventHandler != NULL)) { + + config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24, + (long)drawer->CurPalSize); + } + } + + /* Copy the buffer */ + grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize + / ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity; + + switch (((VQAHandleP *)vqa)->Header.image_width) { + case 320: + if (config->DrawFlags & VQACFGF_SCALEX2) { + Buf_320x200_To_VESA_640x400(drawer->ImageBuf, grain); + } else { + Buf_320x200_To_VESA_320x200(drawer->ImageBuf, grain); + } + break; + + default: + VESA_Blit_640x480(drawer->Display, drawer->ImageBuf, drawer->X1, + drawer->Y1, ((VQAHandleP *)vqa)->Header.image_width, + ((VQAHandleP *)vqa)->Header.image_height); + break; + } + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } +} +#endif /* VQAVESA_ON */ + + +/**************************************************************************** +* +* NAME +* DrawFrame_Buffer - Draw a frame to a buffer. +* +* SYNOPSIS +* Error = DrawFrame_Buffer(VQA) +* +* long DrawFrame_Buffere(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* Error - 0 if successful, otherwise VQAERR_??? error code. +* +****************************************************************************/ + +static long DrawFrame_Buffer(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAFrameNode *curframe; + VQAConfig *config; + long rc; + + #ifndef PHARLAP_TNT + unsigned char *buff; + #else + FARPTR buff; + #endif + + /* Dereference data members for quicker access. */ + config = &((VQAHandleP *)vqa)->Config; + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + + /* Check our "sleep" state */ + if (!(vqabuf->Flags & VQADATF_DSLEEP)) { + + /* Find the frame to draw */ + if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) { + return (rc); + } + + /* Uncompress the frame data */ + Prepare_Frame(vqabuf); + } + + /* Wait for Update_Enabled to be set low */ + if (vqabuf->Flags & VQADATF_UPDATE) { + vqabuf->Flags |= VQADATF_DSLEEP; + return (VQAERR_SLEEPING); + } + + if (vqabuf->Flags & VQADATF_DSLEEP) { + drawer->WaitsOnFlipper++; + vqabuf->Flags &= (~VQADATF_DSLEEP); + } + + /* Dereference current frame for quicker access. */ + curframe = drawer->CurFrame; + + #ifndef PHARLAP_TNT + buff = (unsigned char *)(drawer->ImageBuf + drawer->ScreenOffset); + #else + FP_SET(buff, drawer->ImageBuf + drawer->ScreenOffset, 0x14); + #endif + + /* Un-VQ the image */ + vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, buff, + drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth); + + /* Update data for mono output */ + drawer->LastFrameNum = curframe->FrameNum; + + /* Tell the flipper which frame to use */ + vqabuf->Flipper.CurFrame = curframe; + + /* Set the page-avail flag for the flipper */ + vqabuf->Flags |= VQADATF_UPDATE; + + /* Invoke user's callback routine */ + if (config->DrawerCallback != NULL) { + if ((config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum)) != 0) { + return (VQAERR_EOF); + } + } + + /* Move to the next frame */ + drawer->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* UnVQ_Nop - Do nothing UnVQ. +* +* SYNOPSIS +* UnVQ_Nop(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +* +* void UnVQ_Nop(unsigned char *, unsigned char *, unsigned char *, +* unsigned long, unsigned long, unsigned long); +* FUNCTION +* +* INPUTS +* Codebook - Not used. (Prototype placeholder) +* Pointers - Not used. (Prototype placeholder) +* Buffer - Not used. (Prototype placeholder) +* BPR - Not used. (Prototype placeholder) +* Rows - Not used. (Prototype placeholder) +* BufWidth - Not used. (Prototype placeholder) +* +* RESULT +* NONE +* +****************************************************************************/ + +#ifndef PHARLAP_TNT +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth) +#else +static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth) +#endif +{ + /* Suppress compiler warnings */ + codebook = codebook; + pointers = pointers; + buffer = buffer; + blocksperrow = blocksperrow; + numrows = numrows; + bufwidth = bufwidth; +} + + +/**************************************************************************** +* +* NAME +* PageFlip_Nop - Do nothing page flip. +* +* SYNOPSIS +* PageFlip_Nop(VQA) +* +* void PageFlip_Nop(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static long PageFlip_Nop(VQAHandle *vqa) +{ + vqa = vqa; + + return (0); +} + + +#if(0) +/**************************************************************************** +* +* NAME +* Mask_Rect - Sets non-drawable rectangle in image. +* +* SYNOPSIS +* Mask_Rect(VQA, X1, Y1, X2, Y2) +* +* void Mask_Rect(VQAHandle *, unsigned long, unsigned long, +* unsigned long, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to VQA handle. +* X1 - X coordinate of upper-left corner +* Y1 - Y coordinate of upper-left corner +* X2 - X coordinate of lower-right corner +* Y2 - Y coordinate of lower-right corner +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1, + unsigned long x2, unsigned long y2) +{ + VQAData *vqabuf; + VQADrawer *drawer; + VQAHeader *header; + long blocks_per_row; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + header = &((VQAHandleP *)vqa)->Header; + drawer = &vqabuf->Drawer; + + /* Clip coords to image size */ + if (x1 < vqabuf->Drawer.X1) { + x1 = vqabuf->Drawer.X1; + } + + if (y1 < vqabuf->Drawer.Y1) { + y1 = vqabuf->Drawer.Y1; + } + + if (x2 > vqabuf->Drawer.X2) { + x2 = vqabuf->Drawer.X2; + } + + if (y2 > vqabuf->Drawer.Y2) { + y2 = vqabuf->Drawer.Y2; + } + + /* Convert pixel coords to block coords */ + x1 /= header->block_width; + x2 /= header->block_width; + y1 /= header->block_height; + y2 /= header->block_height; + + /* Compute the mask values */ + blocks_per_row = (header->image_width / header->block_width); + vqabuf->Drawer.MaskStart = blocks_per_row * y1 + x1; + + if (x1 == x2) { + drawer->MaskWidth = 0; + } else { + drawer->MaskWidth = x2 - x1 + 1; + } + + if (y1 == y2) { + drawer->MaskHeight = 0; + } else { + drawer->MaskHeight = y2 - y1 + 1; + } +} + + +/**************************************************************************** +* +* NAME +* Mask_Pointers - Mask vector pointer that are in the mask rectangle. +* +* SYNOPSIS +* Mask_Pointers(VQAData) +* +* void Mask_Pointers(VQAData *); +* +* FUNCTION +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Mask_Pointers(VQAData *vqabuf) +{ + VQADrawer *drawer; + VQAFrameNode *curframe; + unsigned long *ptr; + unsigned long i,j; + unsigned long start; + + /* Dereference data members for quicker access. */ + drawer = &vqabuf->Drawer; + curframe = drawer->CurFrame; + start = vqabuf->Drawer.MaskStart; + + for (i = 0; i < drawer->MaskHeight; i++) { + ptr = (unsigned long *)(curframe->Pointers) + start; + + for (j = 0; j < drawer->MaskWidth; j++) { + ptr[j] = VQA_MASK_POINTER; + } + + start += drawer->BlocksPerRow; + } +} +#endif + + \ No newline at end of file diff --git a/WINVQ/VQA32/OLD/DSTREAM.CPP b/WINVQ/VQA32/OLD/DSTREAM.CPP new file mode 100644 index 0000000..047e259 --- /dev/null +++ b/WINVQ/VQA32/OLD/DSTREAM.CPP @@ -0,0 +1,188 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQAPlay32 library. +* +* FILE +* dstream.c +* +* DESCRIPTION +* DOS IO handler. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* PRIVATE +* VQADOSHandler - Standard DOS IO handler. +* +****************************************************************************/ + +#include +#include +#include "vqaplayp.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes); + + +/**************************************************************************** +* +* NAME +* VQA_InitAsDOS - Initialize IO with the standard DOS handler. +* +* SYNOPSIS +* VQA_InitAsDOS(VQA) +* +* VQA_InitAsDOS(VQAHandle *); +* +* FUNCTION +* Initialize the IO of the specified handle as a standard DOS access. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize as DOS. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_InitAsDOS(VQAHandle *vqa) +{ + ((VQAHandleP *)vqa)->IOHandler = VQADOSHandler; +} + + +/**************************************************************************** +* +* NAME +* VQADOSHandler - Standard DOS IO handler. +* +* SYNOPSIS +* Error = VQADOSHandler(VQA, Action, Buffer, NBytes) +* +* unsigned long VQADOSHandler(VQAHandle *, long, long, long); +* +* FUNCTION +* Perform the requested action on the standard DOS file system. +* +* INPUTS +* VQA - VQAHandle to operate on. +* Action - Action to perform. +* Buffer - Buffer to Read/Write to/from. +* NBytes - Number of bytes to operate on. +* +* RESULT +* Error - 0 if successful, otherwise error. +* +****************************************************************************/ + +static long VQADOSHandler(VQAHandle *vqa, long action, void *buffer, + long nbytes) +{ + long fh; + long error; + + fh = vqa->VQAio; + + /* Perform the action specified by the IO command */ + switch (action) { + + /* VQACMD_READ means read NBytes and place it in the memory + * pointed to by Buffer. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_READ. + */ + case VQACMD_READ: + error = (read(fh, buffer, nbytes) != nbytes); + break; + + /* VQACMD_WRITE is analogous to VQACMD_READ. + * + * Writing is not allowed to the VQA file, VQA library will remap the + * error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* VQACMD_SEEK asks that you perform a seek relative to the current + * position. NBytes is a signed number, indicating seek direction + * (positive for forward, negative for backward). Buffer has no meaning + * here. + * + * Any error code returned will be remapped by VQA library into + * VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (lseek(fh, nbytes, (long)buffer) == -1); + break; + + /* VQACMD_OPEN asks that you open the file for access. */ + case VQACMD_OPEN: + error = open((char *)buffer, (O_RDONLY|O_BINARY)); + + if (error != -1) { + vqa->VQAio = error; + error = 0; + } + break; + + case VQACMD_CLOSE: + close(fh); + error = 0; + break; + + /* VQACMD_INIT means to prepare your IO for reading. This is used for + * certain IOs that can't be read immediately upon opening, and need + * further preparation. This operation is allowed to fail; the error code + * will be returned directly to the client. + */ + case VQACMD_INIT: + + /* IFFCMD_CLEANUP means to terminate the transaction with the associated + * IO. This is used for IOs that can't simply be closed. This operation + * is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return (error); +} + diff --git a/WINVQ/VQA32/OLD/LOADER.CPP b/WINVQ/VQA32/OLD/LOADER.CPP new file mode 100644 index 0000000..fea4c13 --- /dev/null +++ b/WINVQ/VQA32/OLD/LOADER.CPP @@ -0,0 +1,2826 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* loader.c +* +* DESCRIPTION +* Stream loading and pre-processing. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* August 21, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Open - Open a VQA file to play. +* VQA_Close - Close an opened VQA file. +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* PRIVATE +* AllocBuffers - Allocates the numerous VQA play buffers +* FreeBuffers - Frees the VQA play buffers +* PrimeBuffers - Pre-Load the internal buffers. +* Load_FINF - Loads the Frame Info Table. +* Load_VQHD - Loads a VQA Header. +* Load_CBF0 - Loads a full, uncompressed codebook +* Load_CBFZ - Loads a full, compressed codebook +* Load_CBP0 - Loads a partial uncompressed codebook +* Load_CBPZ - Loads a partial compressed codebook +* Load_CPL0 - Loads an uncompressed palette +* Load_CPLZ - Loads a compressed palette +* Load_VPT0 - Loads uncompressed pointers +* Load_VPTZ - Loads compressed pointers +* Load_VQF - Loads a VQ Frame chunk +* Load_SND0 - Loads an uncompressed sound chunk +* Load_SND1 - Loads a compressed sound chunk +* Load_AudFrame - Loads blocks from separate audio file, if needed. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include +#include "caption.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config); +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header); +static long PrimeBuffers(VQAHandle *vqa); +static long Load_VQF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAAUDIO_ON) +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize); +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize); + +#if(VQAVOC_ON) +static void Load_AudFrame(VQAHandleP *vqap); +#endif /* VQAVOC_ON */ + +#endif /* VQAAUDIO_ON */ + + +/**************************************************************************** +* +* NAME +* VQA_Open - Open a VQA file to play. +* +* SYNOPSIS +* Error = VQA_Open(VQA, Name, Config) +* +* long VQA_Open(VQAHandle *, char *, VQAConfig *); +* +* FUNCTION +* - Open a VQA file for reading. +* - Validate that it is an IFF file, of the VQA type. +* - Read the VQA header. +* - Open a VOC file for playback, if requested. +* - Set the Loader's frame rate, if the caller's Config structure's +* FrameRate is set to -1 +* - Set the Drawer's frame rate, if the caller's Config structure's +* DrawRate is set to -1 +* +* INPUTS +* VQA - Pointer to initialized handle. Obtained by VQA_Alloc(). +* Name - Pointer to name of VQA file to open. +* Config - Pointer to initialized VQA configuration structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_ error code. +* +****************************************************************************/ + +#define OPEN_VQHD (1<<0) +#define OPEN_FINF (1<<1) +#define OPEN_CAPTIONS (1<<2) + +long VQA_Open(VQAHandle *vqa, char const *filename, VQAConfig *config) +{ + VQAHandleP *vqap; + VQAHeader *header; + ChunkHeader chunk; + long max_frm_size; + long i; + long done; + long found; + char *ptr; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + header = &vqap->Header; + + /*------------------------------------------------------------------------- + * VERIFY VALIDITY OF VQA FILE. + *-----------------------------------------------------------------------*/ + + /* Open the file. */ + if (vqap->IOHandler(vqa, VQACMD_OPEN, (void *)filename, 0)) { + return (VQAERR_OPEN); + } + + /* Read the file ID & Size */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify an IFF FORM */ + if ((chunk.id != ID_FORM) || (chunk.size == 0)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read in WVQA ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 4)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Verify VQA */ + if (chunk.id != ID_WVQA) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE PLAYERS CONFIGURATION + *-----------------------------------------------------------------------*/ + + /* Use the clients configuration if they provided one. */ + if (config != NULL) { + memcpy(&vqap->Config, config, sizeof(VQAConfig)); + } else { + VQA_DefaultConfig(&vqap->Config); + } + + /* Use the internal configuration structure from now on. */ + config = &vqap->Config; + + /*------------------------------------------------------------------------- + * PROCESS THE PRE-FRAME CHUNKS (VQHD, CAP, FINF, ETC...) + *-----------------------------------------------------------------------*/ + found = 0; + done = 0; + + while (!done) { + if (vqap->IOHandler(vqa, VQACMD_READ, &chunk, 8)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + chunk.size = REVERSE_LONG(chunk.size); + + switch (chunk.id) { + + /*--------------------------------------------------------------------- + * READ IN THE VQA HEADER. + *-------------------------------------------------------------------*/ + case ID_VQHD: + if (chunk.size != sizeof(VQAHeader)) { + VQA_Close(vqa); + return (VQAERR_NOTVQA); + } + + /* Read the header data. */ + if (vqap->IOHandler(vqa, VQACMD_READ, header, PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /*------------------------------------------------------------------- + * SETUP THE CONFIGURATION FROM THE HEADER. + *-----------------------------------------------------------------*/ + if (config->ImageWidth == -1) { + config->ImageWidth = header->ImageWidth; + } + + if (config->ImageHeight == -1) { + config->ImageHeight = header->ImageHeight; + } + + /* If Loaders frame rate is -1 then use the value from the header. */ + if (config->FrameRate == -1) { + config->FrameRate = header->FPS; + } + + /* If Drawers frame rate is -1 then use the value from the header, + * which will result in a "variable" frame rate. + */ + if (config->DrawRate == -1) { + config->DrawRate = header->FPS; + } + + /* Finally, if the DrawRate was set to -1 or 0 (ie MaxRate contained + * bogus values), set it to the header value. + */ + if ((config->DrawRate == -1) || (config->DrawRate == 0)) { + config->DrawRate = header->FPS; + } + + #if(VQAAUDIO_ON) + /* If an alternate audio track is not available then turn it off. + * This enables the primary audio track to be played. + */ + if ((header->Version > VQAHD_VER1) + && !(header->Flags & VQAHDF_ALTAUDIO)) { + config->OptionFlags &= ~VQAOPTF_ALTAUDIO; + } + #endif + + /*------------------------------------------------------------------- + * ALLOCATE THE BUFFERS THAT WE NEED TO PLAY THE VQA. + *-----------------------------------------------------------------*/ + if ((vqap->VQABuf = AllocBuffers(header, config)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_VQHD; + break; + + #if( VQACAPTIONS_ON ) + + /*--------------------------------------------------------------------- + * READ IN AND OPEN THE CAPTIONS STREAM. + *-------------------------------------------------------------------*/ + case ID_CAP0: + if ((config->CapFont != NULL) + && (config->OptionFlags & VQAOPTF_CAPTIONS)) { + + short size = 0; + + /* Get uncompressed size of captions. */ + if (vqap->IOHandler(vqa, VQACMD_READ, &size, sizeof(short))) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Allocate buffer for captions. */ + i = size + 50; + + if ((ptr = (char *)malloc(i)) == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + /* Read in the captions chunk. */ + i -= PADSIZE(chunk.size); + + if (vqap->IOHandler(vqa, VQACMD_READ, (ptr + i), + PADSIZE(chunk.size - sizeof(short)))) { + + free(ptr); + VQA_Close(vqa); + return (VQAERR_READ); + } + + /* Decompress the captions. */ + LCW_Uncompress((ptr + i), ptr, size); + vqap->Caption = OpenCaptions(ptr, config->CapFont); + + if (vqap->Caption == NULL) { + VQA_Close(vqa); + return (VQAERR_NOMEM); + } + + found |= OPEN_CAPTIONS; + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + } + break; + + #endif + + /*--------------------------------------------------------------------- + * READ FRAME INFORMATION + *-------------------------------------------------------------------*/ + case ID_FINF: + if (Load_FINF(vqap, chunk.size)) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + done = 1; + break; + + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(chunk.size))) { + VQA_Close(vqa); + return (VQAERR_SEEK); + } + break; + } + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VIDEO SYSTEM IF WE ARE REQUIRED TO HANDLE THAT. + *-----------------------------------------------------------------------*/ + #if(VQAVIDEO_ON) + if ((vqap->VQABuf->Drawer.Display = SetVideoMode(config->Vmode)) == 0) { + VQA_Close(vqa); + return (VQAERR_VIDEO); + } + + /* Set the VBIBit polarity. */ + vqap->VQABuf->VBIBit = GetVBIBit(); + #else + if (config->VBIBit == -1) { + config->VBIBit = TestVBIBit(); + } + + vqap->VQABuf->VBIBit = config->VBIBit; + + #if( VQACAPTIONS_ON ) + + if (found & OPEN_CAPTIONS) { + SetDAC(251,255,255,255); /* White */ + SetDAC(252,255,000,000); /* Red */ + SetDAC(253,000,255,000); /* Green */ + SetDAC(254,255,255,255); + SetDAC(255,255,000,255); /* Cycle */ + } + + #endif + + #endif /* VQAVIDEO_ON */ + + /*------------------------------------------------------------------------- + * AUDIO TRACK OVERRIDE FROM EXTERNAL FILE (.VOC) + *-----------------------------------------------------------------------*/ + + /* Open VOC file if one is requested. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (config->VocFile != NULL) { + vqap->vocfh = open(config->VocFile, (O_RDONLY|O_BINARY)); + } else { + vqap->vocfh = -1; + } + + /* Make sure we won't try to play audio. */ + if ((vqap->vocfh == -1) && ((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #else /* VQAVOC_ON */ + + /* If the movie does not contain an audio track make sure we won't try + * to play one. + */ + if (((header->Flags & VQAHDF_AUDIO) == 0)) { + config->OptionFlags &= (~VQAOPTF_AUDIO); + } + #endif /* VQAVOC_ON */ + + /*------------------------------------------------------------------------- + * INITIALIZE THE AUDIO PLAYBACK/TIMING SYSTEM. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + VQAAudio *audio; + + /* Dereference for quick access. */ + audio = &vqap->VQABuf->Audio; + + /* Open HMI audio resource for playback. */ + if (VQA_OpenAudio(vqap)) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + + /* Initialize ADPCM information structure for audio stream. */ + sosCODECInitStream(&audio->ADPCM_Info); + + if (header->Version == VQAHD_VER1) { + audio->ADPCM_Info.wBitSize = 8; + audio->ADPCM_Info.dwUnCompSize = (22050L/header->FPS) * header->Frames; + audio->ADPCM_Info.wChannels = 1; + } else { + audio->ADPCM_Info.wBitSize = audio->BitsPerSample; + audio->ADPCM_Info.dwUnCompSize = (((audio->SampleRate / header->FPS) + * (audio->BitsPerSample >> 3)) * audio->Channels) * header->Frames; + + audio->ADPCM_Info.wChannels = audio->Channels; + } + + audio->ADPCM_Info.dwCompSize = audio->ADPCM_Info.dwUnCompSize + / (audio->ADPCM_Info.wBitSize / 4); + } + + /* Turn off audio if the HMI DigiHandle is invalid. */ + if (config->DigiHandle == -1) { + config->OptionFlags &= ~VQAOPTF_AUDIO; + } + + /* Setup the timer interrupt if the client requests it for the timing + * source. + */ + if (!(config->OptionFlags & VQAOPTF_AUDIO) + || (config->TimerMethod == VQA_TMETHOD_INT)) { + + /* Start HMI timer system for timing. */ + if (VQA_StartTimerInt(vqap, (config->OptionFlags & VQAOPTF_HMIINIT))) { + VQA_Close(vqa); + return (VQAERR_AUDIO); + } + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * PRIME THE BUFFERS BY PRE-LOADING THEM WITH FRAME DATA. + *-----------------------------------------------------------------------*/ + if (PrimeBuffers(vqa) != 0) { + VQA_Close(vqa); + return (VQAERR_READ); + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_Close - Close an opened VQA file. +* +* SYNOPSIS +* VQA_Close(VQA) +* +* void VQA_Close(VQAHandle *); +* +* FUNCTION +* Close the file that was opened with VQA_Open(). +* +* INPUTS +* VQA - Pointer VQAHandle to close. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Close(VQAHandle *vqa) +{ + long (*iohandler)(VQAHandle *, long, void *, long); + + /* Restore video mode to text. */ + #if(VQAVIDEO_ON) + SetVideoMode(TEXT); + #endif /* VQAVIDEO_ON */ + + /* Shutdown audio/timing system. */ + #if(VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->Config.OptionFlags & VQAOPTF_AUDIO) { + VQA_CloseAudio((VQAHandleP *)vqa); + } else { + VQA_StopTimerInt((VQAHandleP *)vqa); + } + #endif /* VQAAUDIO_ON */ + + #if( VQACAPTIONS_ON ) + + /* Free captions. */ + if (((VQAHandleP *)vqa)->Caption != NULL) { + if (((VQAHandleP *)vqa)->Caption->Buffer != NULL) { + free(((VQAHandleP *)vqa)->Caption->Buffer); + } + + CloseCaptions(((VQAHandleP *)vqa)->Caption); + } + + #endif + + /* Free memory */ + if (((VQAHandleP *)vqa)->VQABuf != NULL) { + FreeBuffers(((VQAHandleP *)vqa)->VQABuf, &((VQAHandleP *)vqa)->Config, + &((VQAHandleP *)vqa)->Header); + } + + /* Close the VOC override file if one was opened */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((VQAHandleP *)vqa)->vocfh != -1) { + close(((VQAHandleP *)vqa)->vocfh); + } + #endif /* VQAVOC_ON */ + + /* Close the VQA file */ + ((VQAHandleP *)vqa)->IOHandler(vqa, VQACMD_CLOSE, NULL, 0); + + /* Reset the VQAHandle */ + iohandler = ((VQAHandleP *)vqa)->IOHandler; + memset(vqa, 0, sizeof(VQAHandleP)); + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_LoadFrame - Load the next video frame from the VQA data stream. +* +* SYNOPSIS +* Error = VQA_LoadFrame(VQA) +* +* long VQA_LoadFrame(VQAHandle *); +* +* FUNCTION +* The codebook is split up such that the last frame of every group gets +* a new, complete codebook, ready for the next group. The first codebook +* in the VQA is a full codebook, and goes with the first frame's data. +* Partial codebooks are stored per frame after that, and they add up to +* a full codebook just before the first frame for the next group is read. +* +* (Currently, this routine can read either the older non-frame-grouped +* VQA file format, or the new frame-chunk format. For the older format, +* it's assumed that the last chunk in a frame is the pointer data.) +* +* This routine also does a sort of "cooperative multitasking". If the +* Loader hits a "wait state" where it has to wait on the audio to finish +* playing before it can continue to load, it sets a "sleep" flag and +* just returns. The sleep flag is checked on entry to see if it needs +* to jump to the proper execution point. This may improve performance on +* some platforms, but it also allows the Loader to be called regardless +* of the size of the buffers; if the buffers fill up or the audio fails +* to play, the Loader won't just get stuck. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +long VQA_LoadFrame(VQAHandle *vqa) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQADrawer *drawer; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + long frame_loaded = 0; + + /* Dereference commonly used data members for quicker access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + drawer = &((VQAHandleP *)vqa)->VQABuf->Drawer; + curframe = loader->CurFrame; + chunk = &loader->CurChunkHdr; + + /* We have reached the end of the file if we loaded all the frames. */ + if (loader->CurFrameNum >= vqap->Header.Frames) { + return (VQAERR_EOF); + } + + /* If we're reading audio from a VOC file then service that requirement. */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (vqap->vocfh != -1) { + Load_AudFrame(vqap); + } + #endif /* VQAAUDIO_ON & VQAVOC_ON */ + + /* If no buffer is available for loading then return. This allows the + * drawer to service one of the buffers more readily. (We'll wait for one + * to free up). + */ + if (curframe->Flags & VQAFRMF_LOADED) { + loader->WaitsOnDrawer++; + return (VQAERR_NOBUFFER); + } + + /* If we're not sleeping, initialize */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + frame_loaded = 0; + loader->FrameSize = 0; + + /* Initialize the codebook ptr for the frame we're about to load: + * (This frame's codebook is the last full codebook; we have to init it + * now, because if we're on the last frame in a group, we'll get a new + * FullCB pointer.) + */ + curframe->Codebook = loader->FullCB; + } + + /*------------------------------------------------------------------------- + * THE MAIN LOADER LOOP + *-----------------------------------------------------------------------*/ + while (frame_loaded == 0) { + + /* Read new chunk, only if we're not sleeping */ + if (!(vqabuf->Flags & VQADATF_LSLEEP)) { + + /* Read chunk ID */ + if (vqap->IOHandler(vqa, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + loader->FrameSize += iffsize; + } + + /* Handle each chunk type */ + switch (chunk->id) { + + /* VQ Normal Frame */ + case ID_VQFR: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* VQ Key Frame */ + case ID_VQFK: + if (Load_VQF(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + memcpy(drawer->Palette_24,curframe->Palette,curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + drawer->CurPalSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)drawer->Palette_24, + vqabuf->Max_Pal_Size); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + frame_loaded = 1; + break; + + /* Pointer data Key (Must draw) */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + frame_loaded = 1; + break; + + /* Uncompressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + #if(VQAAUDIO_ON) + case ID_SND0: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA0: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load an uncompressed audio frame. */ + if (Load_SND0(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* Compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND1: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA1: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND1(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + /* HMI ADPCM compressed audio frame. + * + * - Make sure the sound load buffer (Audio.TempBuf) is empty; if not + * go into a sleep state. + * - Load the data into TempBuf. + */ + case ID_SND2: + if (!(vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO)) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + + case ID_SNA2: + if (vqap->Config.OptionFlags & VQAOPTF_ALTAUDIO) { + + /* Move the last audio frame to the play buffer. */ + if (CopyAudio(vqap) == VQAERR_SLEEPING) { + vqabuf->Flags |= VQADATF_LSLEEP; + return (VQAERR_SLEEPING); + } else { + vqabuf->Flags &= (~VQADATF_LSLEEP); + } + + /* Load a compressed audio frame. */ + if (Load_SND2(vqap, iffsize) != 0) { + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + break; + #endif + + /* Skip any unknown chunks. */ + default: + if (vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + break; + } + } + + /* Update maximum frame size stat. */ + if ((loader->CurFrameNum>0) && (loader->FrameSize>loader->MaxFrameSize)) { + loader->MaxFrameSize = loader->FrameSize; + } + + /*------------------------------------------------------------------------- + * SET UP THE FRAME FOR DRAWING. + *-----------------------------------------------------------------------*/ + + /* Set the frame # */ + curframe->FrameNum = loader->CurFrameNum; + loader->CurFrameNum++; + + /* Update data for mono output */ + loader->LastFrameNum = loader->CurFrameNum; + + /* Loader is finished with this frame; tell Drawer to draw it */ + curframe->Flags |= VQAFRMF_LOADED; + loader->CurFrame = curframe->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* VQA_SeekFrame - Position the movie stream to the specified frame. +* +* SYNOPSIS +* Frame = VQA_SeekFrame(VQA, Frame, FromWhere) +* +* long VQA_SeekFrame(VQAHandle *, long, long); +* +* FUNCTION +* This function sets the movie stream to the new frame specified by +* the 'offset' parameter. 'FromWhere' is a symbolic constant that is used +* to specify from where in the stream offset should be applied. +* +* INPUTS +* VQA - Pointer to VQAHandle of movie to seek into. +* Frame - Frame to seek to. +* FromWhere - Relative position indicator. +* +* RESULT +* Frame - New frame position or -1 if error. +* +****************************************************************************/ + +long VQA_SeekFrame(VQAHandle *vqa, long framenum, long fromwhere) +{ + VQAHandleP *vqap; + VQAData *vqabuf; + VQALoader *loader; + VQAHeader *header; + VQAFrameNode *frame; + VQAConfig *config; + long group; + long i; + long rc = VQAERR_NONE; + + #if(VQAAUDIO_ON) + VQAAudio *audio; + long audio_on; + #endif + + /* Dereference commonly used data members for quick access. */ + vqap = (VQAHandleP *)vqa; + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + header = &vqap->Header; + config = &vqap->Config; + + fromwhere = fromwhere; + + #if(VQAAUDIO_ON) + audio = &vqabuf->Audio; + + /* Stop audio playback. */ + audio_on = (audio->Flags & VQAAUDF_ISPLAYING); + VQA_StopAudio(vqap); + #endif + + /* Make sure the requested frame is valid and the frame information + * array is allocated before continuing. + */ + if ((framenum < header->Frames) && (vqabuf->Foff != NULL)) { + + /* Find and load the most recent palette. */ + if (!(config->OptionFlags & VQAOPTF_PALOFF)) { + + /* Get the current frame. */ + frame = loader->CurFrame; + + for (i = framenum; i >= 0; i--) { + if (vqabuf->Foff[i] & VQAFINF_PAL) { + + /* Seek to the palette frame. */ + rc = vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[i])); + + /* Fool the loader into thinking this frame is empty. */ + if (!rc) { + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = 0; + frame->Flags = 0; + + /* Load the frame with the palette. */ + if (VQA_LoadFrame(vqa) == 0) { + + /* Decompress the palette if neccessary.*/ + if (frame->Flags & VQAFRMF_PALCOMP) { + frame->PaletteSize = LCW_Uncompress((char *)frame->Palette + + frame->PalOffset, (char *)frame->Palette, + vqabuf->Max_Pal_Size); + } + + SetPalette(frame->Palette, frame->PaletteSize, 0); + } + } else { + rc = VQAERR_SEEK; + } + break; + } + } + } + + /* Build the codebook for the frame we are seeking to. */ + if (!rc) { + + /* Compute the starting group frame of the requested frame. */ + group = (framenum / header->Groupsize); + group = (group * header->Groupsize); + + /* The codebook for the group we want to goto is found in the previous + * group, with the exception of the very first group. + */ + if (group >= header->Groupsize) { + group -= header->Groupsize; + } + + /* Seek to the start of the group containing the partial codebooks for + * the target frame. + */ + if (!vqap->IOHandler(vqa, VQACMD_SEEK, (void *)SEEK_SET, + VQAFRAME_OFFSET(vqabuf->Foff[group]))) { + + /* Throw away any audio frames that were loaded. */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) + && (audio->Buffer != NULL)) { + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + memset(audio->Buffer, 0, config->AudioBufSize); + + /* Position the audio buffer to 1/2 second. */ + audio->AudBufPos = (long)(((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)) / 2); + + /* Mark 1/2 second of the audio buffer as loaded. */ + for (i = 0; i < (audio->AudBufPos / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + } + #endif + + /* Force the loader to the desired frame. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + loader->FullCB = vqabuf->CBData; + loader->CurCB = vqabuf->CBData; + loader->CurFrameNum = group; + + /* Load frames up to the target frame collecting partial codebooks + * along the way. + */ + for (i = 0; i < (framenum - group); i++) { + + /* Fool the loader into thinking the frame has been drawn. */ + loader->CurFrame->Flags = 0; + + #if(VQAAUDIO_ON) + audio->TempBufLen = 0; + #endif + + /* Load the frame. */ + if ((rc = VQA_LoadFrame(vqa)) != 0) { + if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + break; + } else { + rc = 0; + } + } + } + + /* If everything is okay, then re-prime the buffers. */ + if (!rc) { + + /* Mark all the frames except the current one as empty. */ + loader->CurFrame->Flags = 0; + frame = loader->CurFrame->Next; + + while (frame != loader->CurFrame) { + frame->Flags = 0; + frame = frame->Next; + } + + /* Set the drawer to the current frame and the loader + * to the next. + */ + vqabuf->Drawer.CurFrame = loader->CurFrame; + + /* Prime the buffers for the new position. */ + rc = PrimeBuffers(vqa); + + /* An end of file is not considered and error. */ + if ((rc == 0) || (rc == VQAERR_EOF)) { + rc = framenum; + } + } + } else { + rc = VQAERR_SEEK; + } + } + } + + /* Restart audio playback. */ + #if(VQAAUDIO_ON) + if (audio_on) { + VQA_StartAudio(vqap); + } + #endif + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* AllocBuffers - Allocate VQA play buffers. +* +* SYNOPSIS +* VQAData = AllocBuffers(Header, Config) +* +* VQAData *AllocBuffers(VQAHeader *, VQAConfig *); +* +* FUNCTION +* For those structures that contain buffer pointers (codebook nodes, +* frame buffer nodes), enough memory is allocated for both the structure +* and its associated buffers, then the buffer pointers are pointed to +* the appropriate offset from the structure pointer. This allows us +* to perform only one malloc & free for each node. +* +* Buffers allocated: +* - vqa +* - vqa->CBData (list) +* - vqa->FrameData (list) +* - vqa->Drawer.ImageBuf +* - vqa->Audio.Buffer +* - vqa->Audio.IsLoaded +* - vqa->Foff +* +* INPUTS +* Header - Pointer to VQAHeader structure. +* Config - Pointer to VQA configuration structure. +* +* RESULT +* VQAData - Pointer to initialized VQAData structure. +* +****************************************************************************/ + +static VQAData *AllocBuffers(VQAHeader *header, VQAConfig *config) +{ + VQAData *vqa; + VQACBNode *cbnode; + VQACBNode *this_cb; + VQAFrameNode *framenode; + VQAFrameNode *this_frame; + long i; + + /* Check the configuration for valid values. */ + if ((config->NumCBBufs == 0) || (config->NumFrameBufs == 0)) { + return (NULL); + } + + /* Allocate the master structure */ + if ((vqa = (VQAData *)malloc(sizeof(VQAData))) == NULL) { + return (NULL); + } + + /*------------------------------------------------------------------------- + * INITIALIZE THE VQA DATA STRUCTURES. + * + * Pointers are set to NULL initially, and filled in as the buffers are + * allocated. The Max buffer sizes are computed with 1K of padding, + * and'd with 0xFFFC to make the size divisible by 4, to ensure DWORD + * alignment. + *-----------------------------------------------------------------------*/ + DPMI_Lock(vqa, sizeof(VQAData)); + memset(vqa, 0, sizeof(VQAData)); + vqa->MemUsed = sizeof(VQAData); + vqa->Drawer.LastTime = (-VQA_TIMETICKS); + + /* Set maximum codebook size. */ + vqa->Max_CB_Size = ((header->CBentries) * header->BlockWidth + * header->BlockHeight + 250) & 0xFFFC; + + /* Set maximum palette size. */ + vqa->Max_Pal_Size = (768 + 1024) & 0xFFFC; + + /* Set maximum vector pointers size. */ + vqa->Max_Ptr_Size = ((header->ImageWidth / header->BlockWidth) + * (header->ImageHeight / header->BlockHeight) + * sizeof(short) + 1024) & 0xFFFC; + + /* Set the frame number of the frame containing the last codebook. */ + vqa->Loader.LastCBFrame = (((header->Frames - 1) / header->Groupsize) + * header->Groupsize); + + /*------------------------------------------------------------------------- + * ALLOCATE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumCBBufs; i++) { + + /* Allocate a codebook node. */ + cbnode = (VQACBNode *)malloc((sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* If failure then clean up and exit. */ + if (cbnode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ + DPMI_Lock(cbnode, (sizeof(VQACBNode) + vqa->Max_CB_Size)); + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQACBNode) + vqa->Max_CB_Size); + + /* Initialize the node */ + memset(cbnode, 0, sizeof(VQACBNode)); + cbnode->Buffer = (unsigned char *)cbnode + sizeof(VQACBNode); + + /* Install the node */ + if (i == 0) { + vqa->CBData = cbnode; + this_cb = cbnode; + } else { + this_cb->Next = cbnode; + this_cb = cbnode; + } + } + + /* Make the list circular */ + cbnode->Next = vqa->CBData; + + /* Install the Codebook list */ + vqa->Loader.CurCB = vqa->CBData; + vqa->Loader.FullCB = vqa->CBData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + for (i = 0; i < config->NumFrameBufs; i++) { + + /* Allocate a pointer node */ + framenode = (VQAFrameNode *)malloc((sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size + vqa->Max_Pal_Size)); + + /* If failure then clean up and exit. */ + if (framenode == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock the buffer to prevent page swapping. */ + DPMI_Lock(framenode, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Keep count of the memory usage. */ + vqa->MemUsed += (long)(sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + + /* Initialize the node */ + memset(framenode, 0, sizeof(VQAFrameNode)); + framenode->Pointers = (unsigned char *)framenode + sizeof(VQAFrameNode); + framenode->Palette = (unsigned char *)framenode + sizeof(VQAFrameNode) + + vqa->Max_Ptr_Size; + + framenode->Codebook = vqa->CBData; + + /* Install the node */ + if (i == 0) { + vqa->FrameData = framenode; + this_frame = framenode; + } else { + this_frame->Next = framenode; + this_frame = framenode; + } + } + + /* Make the list circular */ + framenode->Next = vqa->FrameData; + + /* Install the Frame Buffer list */ + vqa->Loader.CurFrame = vqa->FrameData; + vqa->Drawer.CurFrame = vqa->FrameData; + vqa->Flipper.CurFrame = vqa->FrameData; + + /*------------------------------------------------------------------------- + * ALLOCATE THE IMAGE BUFFERS IF ONE IS NOT ALREADY PROVIDED. + *-----------------------------------------------------------------------*/ + if (config->ImageBuf == NULL) { + + /* Allocate our own buffer. */ + if (config->DrawFlags & VQACFGF_BUFFER) { + vqa->Drawer.ImageBuf = (unsigned char *)malloc((header->ImageWidth + * header->ImageHeight)); + + /* If the allocation failed we must free up and exit. */ + if (vqa->Drawer.ImageBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); + + /* Plugin image buffer information. */ + vqa->Drawer.ImageWidth = header->ImageWidth; + vqa->Drawer.ImageHeight = header->ImageHeight; + vqa->MemUsed += (long)(header->ImageWidth * header->ImageHeight); + } else { + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + } else { + + /* Use caller provided buffer */ + vqa->Drawer.ImageBuf = config->ImageBuf; + vqa->Drawer.ImageWidth = config->ImageWidth; + vqa->Drawer.ImageHeight = config->ImageHeight; + } + + /*------------------------------------------------------------------------- + * ALLOCATE AND INITIALIZE AUDIO BUFFERS AND STRUCTURES. + *-----------------------------------------------------------------------*/ + #if(VQAAUDIO_ON) + if ((header->Flags & VQAHDF_AUDIO) + && (config->OptionFlags & VQAOPTF_AUDIO)) { + + /* Dereference audio structure for quick access. */ + VQAAudio *audio = &vqa->Audio; + + /* Version 1 VQA's only supported 22050 8 bit mono audio. */ + if (header->Version < VQAHD_VER2) { + audio->SampleRate = 22050U; + audio->Channels = 1; + audio->BitsPerSample = 8; + audio->BytesPerSec = 22050; + } else { + if ((config->OptionFlags & VQAOPTF_ALTAUDIO) + && (header->Flags & VQAHDF_ALTAUDIO)) { + audio->SampleRate = header->AltSampleRate; + audio->Channels = header->AltChannels; + audio->BitsPerSample = header->AltBitsPerSample; + } else { + audio->SampleRate = header->SampleRate; + audio->Channels = header->Channels; + audio->BitsPerSample = header->BitsPerSample; + } + + audio->BytesPerSec = ((audio->SampleRate * audio->Channels) + * (audio->BitsPerSample >> 3)); + } + + /* Adjust the HMI buffer to accomodate the amount of data. */ + #if(0) + config->HMIBufSize *= (audio->SampleRate / 22050); + config->HMIBufSize *= audio->Channels * (audio->BitsPerSample >> 3); + #endif + + /* The default audio buffer size should be large enough to hold + * 1.5 seconds of data. + */ + if (config->AudioBufSize == -1) { + + /* Compute the number of HMI buffers that will completly fit into + * 1.5 seconds of audio data. + */ + i = ((audio->BytesPerSec+(audio->BytesPerSec/2))/config->HMIBufSize); + config->AudioBufSize = (config->HMIBufSize * i); + } + + /* Do not allocate anything if the audio buffer is zero length. */ + if (config->AudioBufSize > 0) { + + /* Allocate an audio buffer if the user did not provide one. + * Otherwise, use the user supplied buffer. + */ + if (config->AudioBuf == NULL) { + audio->Buffer = (unsigned char *)malloc(config->AudioBufSize); + + /* If failure then clean up and exit. */ + if (audio->Buffer == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + DPMI_Lock(audio->Buffer, config->AudioBufSize); + + /* Add audio buffer size to memory usage. */ + vqa->MemUsed += config->AudioBufSize; + } else { + audio->Buffer = config->AudioBuf; + } + + /* Allocate IsLoaded flags */ + audio->NumAudBlocks = (config->AudioBufSize / config->HMIBufSize); + audio->IsLoaded = (short *)malloc(audio->NumAudBlocks * sizeof(short)); + + /* If failure then clean up and exit. */ + if (audio->IsLoaded == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(audio->IsLoaded, audio->NumAudBlocks * sizeof(short)); + + /* Add IsLoaded flags array to memory usage. */ + vqa->MemUsed += (audio->NumAudBlocks * sizeof(short)); + + /* Initialize audio IsLoaded flags to false. */ + memset(audio->IsLoaded, 0, audio->NumAudBlocks * sizeof(short)); + + /* Allocate temporary staging buffer for the audio frames. */ + audio->TempBufSize = ((audio->BytesPerSec / header->FPS) * 2) + 100; + audio->TempBuf = (unsigned char *)malloc(audio->TempBufSize); + + if (audio->TempBuf == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(audio->TempBuf, audio->TempBufSize); + + /* Add temporary buffer size to memory usage. */ + vqa->MemUsed += audio->TempBufSize; + } + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * ALLOCATE THE FRAME INFORMATION TABLE IF REQUESTED. + *-----------------------------------------------------------------------*/ + vqa->Foff = (long *)malloc(header->Frames * sizeof(long)); + + if (vqa->Foff == NULL) { + FreeBuffers(vqa, config, header); + return (NULL); + } + + /* Lock to prevent page swapping. */ + DPMI_Lock(vqa->Foff, header->Frames * sizeof(long)); + + /* Keep a running total of memory usage. */ + vqa->MemUsed += (header->Frames * sizeof(long)); + + return (vqa); +} + + +/**************************************************************************** +* +* NAME +* FreeBuffers - Free VQA play buffers. +* +* SYNOPSIS +* FreeBuffers(VQAData, Config, Header) +* +* void FreeBuffers(VQAData *, VQAConfig *, VQAHeader *); +* +* FUNCTION +* Free the buffers allocated by AllocBuffers(). +* +* INPUTS +* VQAData - Pointer to VQAData structure. +* Config - Pointer to configuration structure. +* Header - Pointer to movie header structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FreeBuffers(VQAData *vqa, VQAConfig *config, VQAHeader *header) +{ + VQACBNode *cb_this, + *cb_next; + VQAFrameNode *frame_this, + *frame_next; + long i; + + /*------------------------------------------------------------------------- + * FREE THE FRAME INFORMATION TABLE. + *-----------------------------------------------------------------------*/ + if (vqa->Foff) { + DPMI_Unlock(vqa->Foff, header->Frames * sizeof(long)); + free(vqa->Foff); + } + + /*------------------------------------------------------------------------- + * FREE THE AUDIO BUFFERS. + *-----------------------------------------------------------------------*/ + + #if(VQAAUDIO_ON) + if ((config->AudioBuf == NULL) && (vqa->Audio.Buffer)) { + DPMI_Unlock(vqa->Audio.Buffer, config->AudioBufSize); + free(vqa->Audio.Buffer); + } + + /* Free the audio segments loaded flag array. */ + if (vqa->Audio.IsLoaded) { + DPMI_Unlock(vqa->Audio.IsLoaded,vqa->Audio.NumAudBlocks * sizeof(short)); + free(vqa->Audio.IsLoaded); + } + + /* Free the temporary audio buffer. */ + if (vqa->Audio.TempBuf) { + DPMI_Unlock(vqa->Audio.TempBuf, vqa->Audio.TempBufSize); + free(vqa->Audio.TempBuf); + } + #endif /* VQAAUDIO_ON */ + + /*------------------------------------------------------------------------- + * FREE THE IMAGE BUFFER ONLY IF WE ALLOCATED IT. + *-----------------------------------------------------------------------*/ + if ((config->ImageBuf == NULL) && vqa->Drawer.ImageBuf) { + DPMI_Unlock(vqa->Drawer.ImageBuf,header->ImageWidth*header->ImageHeight); + free(vqa->Drawer.ImageBuf); + } + + /*------------------------------------------------------------------------- + * FREE THE FRAME BUFFERS. + *-----------------------------------------------------------------------*/ + frame_this = vqa->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame_this) { + frame_next = frame_this->Next; + DPMI_Unlock(frame_this, sizeof(VQAFrameNode) + vqa->Max_Ptr_Size + + vqa->Max_Pal_Size); + free(frame_this); + frame_this = frame_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE CODEBOOK BUFFERS. + *-----------------------------------------------------------------------*/ + cb_this = vqa->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (cb_this) { + cb_next = cb_this->Next; + DPMI_Unlock(cb_this, sizeof(VQACBNode) + vqa->Max_CB_Size); + free(cb_this); + cb_this = cb_next; + } else { + break; + } + } + + /*------------------------------------------------------------------------- + * FREE THE VQA DATA STRUCTURES. + *-----------------------------------------------------------------------*/ + DPMI_Unlock(vqa, sizeof(VQAData)); + free(vqa); +} + + +/**************************************************************************** +* +* NAME +* PrimeBuffers - Pre-Load the internal buffers. +* +* SYNOPSIS +* Error = PrimeBuffers(VQA) +* +* long = PrimeBuffers(VQAHandle *); +* +* FUNCTION +* Pre-load the internal buffers in order to give the player some slack +* in the playback of large frames. +* +* INPUTS +* VQA - Pointer to VQAHandle structure. +* +* RESULT +* Error - 0 if successful, or VQAERR_??? error code. +* +****************************************************************************/ + +long PrimeBuffers(VQAHandle *vqa) +{ + VQAData *vqabuf; + VQAConfig *config; + long rc; + long i; + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + config = &((VQAHandleP *)vqa)->Config; + + /* Pre-load the buffers */ + for (i = 0; i < config->NumFrameBufs; i++) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + return (rc); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQF - Loads a VQ Frame chunk. +* +* SYNOPSIS +* Error = Load_VQF(VQA, Iffsize) +* +* long Load_VQF(VQAHandleP *, unsigned long); +* +* FUNCTION +* The VQ Frame Chunk contains a set of other chunks (codebooks, +* palettes, pointers). This routine reads the frame's chunk size, +* then loops until it's read that many bytes. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQF(VQAHandleP *vqap, unsigned long frame_iffsize) +{ + VQAData *vqabuf; + VQAFrameNode *curframe; + ChunkHeader *chunk; + unsigned long iffsize; + unsigned long framesize; + unsigned long bytes_loaded = 0; + VQADrawer *drawer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + curframe = vqabuf->Loader.CurFrame; + framesize = PADSIZE(frame_iffsize); + drawer = &(vqap->VQABuf->Drawer); + chunk = &vqabuf->Loader.CurChunkHdr; + + /*------------------------------------------------------------------------- + * FRAME LOADING LOOP. + *-----------------------------------------------------------------------*/ + while (bytes_loaded < framesize) { + + /* Read chunk ID */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, chunk, 8)) { + return (VQAERR_EOF); + } + + iffsize = REVERSE_LONG(chunk->size); + bytes_loaded += 8; + bytes_loaded += PADSIZE(iffsize); + + /* Handle each chunk type */ + switch (chunk->id) { + + /* Full uncompressed codebook */ + case ID_CBF0: + if (Load_CBF0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Full compressed codebook */ + case ID_CBFZ: + if (Load_CBFZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial uncompressed codebook */ + case ID_CBP0: + if (Load_CBP0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Partial compressed codebook */ + case ID_CBPZ: + if (Load_CBPZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Uncompressed palette */ + case ID_CPL0: + if (Load_CPL0(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + memcpy(drawer->Palette_24,curframe->Palette,curframe->PaletteSize); + drawer->CurPalSize = curframe->PaletteSize; + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Compressed palette */ + case ID_CPLZ: + if (Load_CPLZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* If this is the first occurance of a palette then store it now. + * This functionality is needed for Monopoly! + */ + if (drawer->CurPalSize == 0) { + drawer->CurPalSize = LCW_Uncompress((char *)curframe->Palette + + curframe->PalOffset, (char *)drawer->Palette_24, + vqabuf->Max_Pal_Size); + } + + /* Flag this frame as having a palette. */ + curframe->Flags |= VQAFRMF_PALETTE; + break; + + /* Uncompressed pointer data */ + case ID_VPT0: + if (Load_VPT0(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTZ: + case ID_VPTD: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + break; + + /* Compressed pointer data */ + case ID_VPTK: + if (Load_VPTZ(vqap, iffsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as being key. */ + curframe->Flags |= VQAFRMF_KEY; + break; + + /* An unknown chunk in the video frame is an error. */ + default: + return (VQAERR_READ); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_FINF - Load Frame Info chunk. +* +* SYNOPSIS +* Error = Load_FINF(VQA, Iffsize) +* +* long Load_FINF(VQAHandleP *, unsigned long); +* +* FUNCTION +* Load FINF chunk if buffer available, otherwise skip it. +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_FINF(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + + /* Load the frame information table if we need to, otherwise we will + * skip it. + */ + if (vqabuf->Foff != NULL) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, vqabuf->Foff, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + } else { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + PADSIZE(iffsize))) { + return (VQAERR_SEEK); + } + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VQHD - Load VQA header chunk. +* +* SYNOPSIS +* Error = Load_VQHD(VQA, Iffsize) +* +* long Load_VQHD(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VQHD(VQAHandleP *vqap, unsigned long iffsize) +{ + /* Read the header */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &vqap->Header, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reconfigure the Drawer for the new settings */ + VQA_Configure_Drawer(vqap); + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBF0 - Load full uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBF0(VQA, Iffsize) +* +* long Load_CBF0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBF0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + + /* Read into the start of the buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curcb->Buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as uncompressed. */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Clock pointers to next CB Buffer. */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBFZ - Load full compressed codebook. +* +* SYNOPSIS +* Error = Load_CBFZ(VQA, Iffsize) +* +* long Load_CBFZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBFZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + loader = &vqap->VQABuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Load the codebook into the end of the buffer. */ + lcwoffset = vqap->VQABuf->Max_CB_Size - padsize; + buffer = curcb->Buffer + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Reset the partial codebook counter. */ + loader->NumPartialCB = 0; + + /* Flag this codebook as compressed */ + curcb->Flags |= VQACBF_CBCOMP; + curcb->CBOffset = lcwoffset; + + /* Clock pointers to next CB Buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBP0 - Load partial uncompressed codebook. +* +* SYNOPSIS +* Error = Load_CBP0(VQA, Iffsize) +* +* long Load_CBP0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQA_??? error code. +* +****************************************************************************/ + +static long Load_CBP0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + + /*------------------------------------------------------------------------- + * ASSEMBLY PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = curcb->Buffer + loader->PartialCBSize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Accumulate the partial codebook values. */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as uncompressed */ + curcb->Flags &= (~VQACBF_CBCOMP); + curcb->CBOffset = 0; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CBPZ - Load partial compressed codebook. +* +* SYNOPSIS +* Error = Load_CBPZ(VQA, Iffsize) +* +* long Load_CBPZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CBPZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQACBNode *curcb; + void *buffer; + unsigned long padsize; + + /* Dereference commonly used data members for quicker access */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + curcb = loader->CurCB; + padsize = PADSIZE(iffsize); + + /* Attempt to compute the LCW offset into the codebook buffer by + * multiplying the size of this chunk by the # frames/group, and adding + * a small fudge factor on, then subtracting that from the CB buffer size. + */ + if (loader->PartialCBSize == 0) { + curcb->CBOffset = (vqabuf->Max_CB_Size + - (padsize * vqap->Header.Groupsize + 100)); + } + + /*------------------------------------------------------------------------- + * ASSEMBLE PARTIAL CODEBOOKS. + *-----------------------------------------------------------------------*/ + + /* Read the partial codebook into the next position in the buffer. */ + buffer = ((curcb->Buffer + curcb->CBOffset) + loader->PartialCBSize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Accumulate partial codebook values */ + loader->PartialCBSize += iffsize; + loader->NumPartialCB++; + + /*------------------------------------------------------------------------- + * PROCESS FULL CODEBOOK. + *-----------------------------------------------------------------------*/ + if (loader->NumPartialCB == vqap->Header.Groupsize) { + + /* Reset the codebook accumulator values. */ + loader->NumPartialCB = 0; + loader->PartialCBSize = 0; + + /* Flag this codebook as compressed. */ + curcb->Flags |= VQACBF_CBCOMP; + + /* Go to the next codebook buffer */ + loader->FullCB = curcb; + loader->FullCB->Flags &= (~VQACBF_DOWNLOADED); + loader->CurCB = curcb->Next; + } + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPL0 - Load an uncompressed palette. +* +* SYNOPSIS +* Error = Load_CPL0(VQA, Iffsize) +* +* long Load_CPL0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPL0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the palette into the palette buffer */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Palette, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag the palette as uncompressed. */ + curframe->Flags &= ~VQAFRMF_PALCOMP; + curframe->PalOffset = 0; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_CPLZ - Load compressed palette. +* +* SYNOPSIS +* Error = Load_CPLZ(VQA, Iffsize) +* +* long Load_CPLZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_CPLZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + + /* Read the palette into the end of the palette buffer. */ + lcwoffset = vqap->VQABuf->Max_Pal_Size - padsize; + buffer = curframe->Palette + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this palette as compressed. */ + curframe->Flags |= VQAFRMF_PALCOMP; + curframe->PalOffset = lcwoffset; + curframe->PaletteSize = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPT0 - Load uncompressed pointers. +* +* SYNOPSIS +* Error = Load_VPT0(VQA, Iffsize) +* +* long Load_VPT0(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPT0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + + /* Read the pointers into start of the pointer buffer. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, curframe->Pointers, + PADSIZE(iffsize))) { + + return (VQAERR_READ); + } + + /* Flag this frame as uncompressed */ + curframe->Flags &= ~VQAFRMF_PTRCOMP; + curframe->PtrOffset = 0; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_VPTZ - Load compressed pointers. +* +* SYNOPSIS +* Error = Load_VPTZ(VQA, Iffsize) +* +* long Load_VPTZ(VQAHandleP *, unsigned long); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_VPTZ(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAFrameNode *curframe; + void *buffer; + unsigned long padsize; + unsigned long lcwoffset; + + /* Dereference commonly used data members for quicker access. */ + curframe = vqap->VQABuf->Loader.CurFrame; + padsize = PADSIZE(iffsize); + lcwoffset = vqap->VQABuf->Max_Ptr_Size - padsize; + + /* Read the pointers into end of the pointer buffer. */ + buffer = curframe->Pointers + lcwoffset; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, buffer, padsize)) { + return (VQAERR_READ); + } + + /* Flag this frame as compressed. */ + curframe->Flags |= VQAFRMF_PTRCOMP; + curframe->PtrOffset = lcwoffset; + + return (0); +} + + +#if(VQAAUDIO_ON) +/**************************************************************************** +* +* NAME +* Load_SND0 - Load uncompressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND0(VQA, Iffsize) +* +* long Load_SND0(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND0(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned long padsize; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk. + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (vqap->vocfh != -1) || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) + || (audio->Buffer == NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read large startup chunk directly into AudioBuf */ + if ((padsize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + + audio->AudBufPos += iffsize; + + /* Flag the audio frame flags as loaded for the initial audio frame. */ + for (i = 0; i < (iffsize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } else { + + /* Read data into TempBuf */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } + + /* Set the TempBufLen */ + audio->TempBufLen = iffsize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND1 - Load compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND1(VQA, Iffsize) +* +* long Load_SND1(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND1(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + ZAPHeader zap; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + /* Read the ZAP audio frame header. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, &zap, + sizeof(ZAPHeader))) { + + return (VQAERR_READ); + } + + /* Adjust chunk size */ + padsize -= sizeof(ZAPHeader); + + /* Read large startup chunk directly into AudioBuf */ + if ((zap.UnCompSize > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load RAW uncompressed data. */ + if (zap.UnCompSize == zap.CompSize) { + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->Buffer, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, + padsize)) { + + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->Buffer, zap.UnCompSize); + } + + /* Set buffer positions & flags */ + audio->AudBufPos += zap.UnCompSize; + + for (i = 0; i < (zap.UnCompSize / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + if (zap.UnCompSize == zap.CompSize) { + + /* If the frame is uncompressed the load it in directly. */ + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, audio->TempBuf, + padsize)) { + + return (VQAERR_READ); + } + } else { + + /* Load the audio frame into the end of the buffer. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + AudioUnzap(loadbuf, audio->TempBuf, zap.UnCompSize); + } + + /* Set the TempBufLen */ + audio->TempBufLen = zap.UnCompSize; + + return (0); +} + + +/**************************************************************************** +* +* NAME +* Load_SND2 - Load ADPCM compressed sound chunk. +* +* SYNOPSIS +* Error = Load_SND2(VQA, Iffsize) +* +* long Load_SND2(VQAHandleP *, unsigned long); +* +* FUNCTION +* This routine normally loads the chunk into the TempBuf, unless the +* chunk is larger than the temp buffer size, in which case it puts it +* directly into the audio buffer itself. This assumes that the only +* such chunk will be the first audio chunk! +* +* INPUTS +* VQA - Pointer to private VQA handle. +* Iffsize - Size of IFF chunk. +* +* RESULT +* Error - 0 if successful or VQAERR_??? error code. +* +****************************************************************************/ + +static long Load_SND2(VQAHandleP *vqap, unsigned long iffsize) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + unsigned char *loadbuf; + unsigned long padsize; + unsigned long uncomp_size; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + padsize = PADSIZE(iffsize); + + /* If sound is disabled, or if we're playing from a VOC file, or if + * there's no Audio Buffer, just skip the chunk + */ + #if(VQAVOC_ON && VQAAUDIO_ON) + if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1) + || (audio->Buffer == NULL)) { + #else /* VQAVOC_ON */ + if (((config->OptionFlags & VQAOPTF_AUDIO)==0) || (audio->Buffer==NULL)) { + #endif /* VQAVOC_ON */ + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_SEEK, (void *)SEEK_CUR, + padsize)) { + return (VQAERR_SEEK); + } else { + return (0); + } + } + + uncomp_size = iffsize * (audio->BitsPerSample / 4); + + /* Read large startup chunk directly into AudioBuf */ + if ((uncomp_size > audio->TempBufSize) && (audio->AudBufPos == 0)) { + + /* Load compressed data into the end of the buffer. */ + loadbuf = (audio->Buffer + config->AudioBufSize) - padsize; + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->Buffer; + sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set buffer positions & flags */ + audio->AudBufPos += uncomp_size; + + for (i = 0; i < (uncomp_size / config->HMIBufSize); i++) { + audio->IsLoaded[i] = 1; + } + + return (0); + } + + /* Load an audio frame. */ + loadbuf = ((audio->TempBuf + audio->TempBufSize) - padsize); + + if (vqap->IOHandler((VQAHandle *)vqap, VQACMD_READ, loadbuf, padsize)) { + return (VQAERR_READ); + } + + /* Uncompress the audio frame. */ + audio->ADPCM_Info.lpSource = (char *)loadbuf; + audio->ADPCM_Info.lpDest = (char *)audio->TempBuf; + sosCODECDecompressData(&audio->ADPCM_Info, uncomp_size); + + /* Set the TempBufLen */ + audio->TempBufLen = uncomp_size; + + return (0); +} + + +#if(VQAVOC_ON) +/**************************************************************************** +* +* NAME +* Load_AudFrame - Loads blocks from seperate VOC file. +* +* SYNOPSIS +* Load_AudFrame(VQA) +* +* void Load_AudFrame(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void Load_AudFrame(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQALoader *loader; + VQAAudio *audio; + VQAConfig *config; + static long lastplayblock = -1; + static long myblock = 0; + static long firsttime = 1; + long numblocks; + long i; + + /* Dereference commonly used data members for quicker access. */ + vqabuf = vqap->VQABuf; + loader = &vqabuf->Loader; + audio = &vqabuf->Audio; + config = &vqap->Config; + + /* Do nothing if no buffer */ + if (audio->Buffer == NULL) { + return; + } + + /* If this is the first time we're called, pre-load the 1st 'n' audio + * blocks, where 'n' is half the total audio buffer size; this way, we'll + * always stay ahead of HMI. + */ + if (firsttime) { + numblocks = (audio->NumAudBlocks / 2); + read(vqap->vocfh, audio->Buffer, config->HMIBufSize * numblocks); + audio->AudBufPos += config->HMIBufSize * numblocks; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + for (i = 0; i < numblocks; i++) { + audio->IsLoaded[i] = 1; + } + + myblock += numblocks; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + + firsttime = 0; + } + + /* If HMI's block has changed, load the next block & mark it as loaded */ + if (audio->PlayPosition / config->HMIBufSize != lastplayblock) { + + /* update HMI's last known block position */ + lastplayblock = audio->PlayPosition / config->HMIBufSize; + + /* read the VOC data */ + read(vqap->vocfh, (audio->Buffer + myblock * config->HMIBufSize), + config->HMIBufSize); + + audio->AudBufPos += config->HMIBufSize; + + if (audio->AudBufPos >= config->AudioBufSize) { + audio->AudBufPos = 0; + } + + /* set the IsLoaded flags */ + audio->IsLoaded[myblock] = 1; + + /* increment my block counter */ + myblock++; + + if (myblock >= audio->NumAudBlocks) { + myblock = 0; + } + } +} +#endif /* VQAVOC_ON */ +#endif /* VQAAUDIO_ON */ + diff --git a/WINVQ/VQA32/OLD/MAKEFILE b/WINVQ/VQA32/OLD/MAKEFILE new file mode 100644 index 0000000..f174b30 --- /dev/null +++ b/WINVQ/VQA32/OLD/MAKEFILE @@ -0,0 +1,162 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJ = .. +WATCOM = C:\WATCOM +PRJNAME = vqa32wp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5r -mf -oaeilrt -s -zq -d2 +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + + \ No newline at end of file diff --git a/WINVQ/VQA32/OLD/MAKEFILE.BOR b/WINVQ/VQA32/OLD/MAKEFILE.BOR new file mode 100644 index 0000000..df30026 --- /dev/null +++ b/WINVQ/VQA32/OLD/MAKEFILE.BOR @@ -0,0 +1,216 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay32 library. +# +# FILE +# makefile (Borland/TNT) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 7, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# BCDIR - Borland C installation path. +# TNTDIR - Pharlap/TNT installation path. +# +#**************************************************************************** + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32bp +PRJDIR = $(PRJ)\VQA32 + +OBJECTS = \ + config.obj \ + task.obj \ + loader.obj \ + drawer.obj \ + audio.obj \ + monodisp.obj \ + dstream.obj \ + unvqbuff.obj \ + unvqvesa.obj \ + vertag.obj \ + caption.obj \ +# unvqxmde.obj \ + +PRJLIBS = \ + vqm32bp.lib \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMAND AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).lib + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD LIBRARY +#---------------------------------------------------------------------------- + +$(PRJNAME).lib: $(OBJECTS) + - @del $(.path.lib)\$(PRJNAME).lib >&NUL + $(LIB_CMD) $(.path.lib)\$(PRJNAME).lib @$(LIB_CFG) @&&| +-+$(**: = -+) +| + - @copy vqaplay.h $(PRJ)\INCLUDE\VQA32 >&NUL + - @copy vqafile.h $(PRJ)\INCLUDE\VQA32 >&NUL + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + diff --git a/WINVQ/VQA32/OLD/MAKEFILE.WAT b/WINVQ/VQA32/OLD/MAKEFILE.WAT new file mode 100644 index 0000000..cdd42a6 --- /dev/null +++ b/WINVQ/VQA32/OLD/MAKEFILE.WAT @@ -0,0 +1,163 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQAPlay 32-bit library. +# +# FILE +# makefile (Watcom C/C++ 10.0a) +# +# DESCRIPTION +# Makefile for generating the 32-bit VQAPlay library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# February 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqa32wp +PRJDIR = $(%PRJ)\VQA32 + +OBJECTS = & + config.obj & + task.obj & + loader.obj & + drawer.obj & + audio.obj & + monodisp.obj & + dstream.obj & + unvqbuff.obj & + unvqvesa.obj & + vertag.obj & + caption.obj & +# unvqxmde.obj + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# Tools/commands & configurations +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /i$(%PRJ)\INCLUDE\VQM32 /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQAPlay32 header files! + @copy vqaplay.h ..\include\vqa32 >NUL + @copy vqafile.h ..\include\vqa32 >NUL + diff --git a/WINVQ/VQA32/OLD/MONODISP.CPP b/WINVQ/VQA32/OLD/MONODISP.CPP new file mode 100644 index 0000000..a69c1e7 --- /dev/null +++ b/WINVQ/VQA32/OLD/MONODISP.CPP @@ -0,0 +1,512 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* monodisp.c +* +* DESCRIPTION +* Monochrome display (debug) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 6, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_InitMono - Initialize the player's mono screen. +* VQA_UpdateMono - Update the player's mono output. +* +****************************************************************************/ + +#include +#include +#include "vq.h" +#include "vqaplayp.h" +#include + +#if(VQAMONO_ON) + +/* Main window */ +#define MAIN_WX1 0 +#define MAIN_WX2 79 +#define MAIN_WY1 0 +#define MAIN_WY2 9 +#define MAIN_TITLE "VQA Player" + +/* Loader data window */ +#define LOADER_WX1 0 +#define LOADER_WX2 39 +#define LOADER_WY1 10 +#define LOADER_WY2 20 +#define LOADER_TITLE " Frame Loader " + +/* Drawer data window */ +#define DRAWER_WX1 40 +#define DRAWER_WX2 79 +#define DRAWER_WY1 10 +#define DRAWER_WY2 20 +#define DRAWER_TITLE " Frame Drawer " + +/* Audio data window */ +#define AUDIO_WX1 0 +#define AUDIO_WX2 39 +#define AUDIO_WY1 21 +#define AUDIO_WY2 24 +#define AUDIO_TITLE " Audio Callback " + +/* Flipper data window */ +#define FLIPPER_WX1 40 +#define FLIPPER_WX2 79 +#define FLIPPER_WY1 21 +#define FLIPPER_WY2 24 +#define FLIPPER_TITLE " Frame Flipper " + +extern char *HMIDevName; + + +/**************************************************************************** +* +* NAME +* VQA_InitMono - Initialize the player's mono screen. +* +* SYNOPSIS +* VQA_InitMono(VQA) +* +* void VQA_InitMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_InitMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + char txt[80]; + + /* Dereference commonly used data members of quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Enable and clear the mono screen */ + Mono_Enable(); + Mono_Clear_Screen(); + + /* Init main window */ + Mono_Draw_Rect(MAIN_WX1, MAIN_WY1, (MAIN_WX2 - MAIN_WX1 + 1), + (MAIN_WY2 - MAIN_WY1 + 1), 2, 1); + + Mono_Set_Cursor((MAIN_WX2 + MAIN_WX1 - strlen(MAIN_TITLE)) / 2, MAIN_WY1); + Mono_Print(MAIN_TITLE); + + /* Video mode */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 1); + Mono_Print("Video Mode: "); + + switch (config->Vmode) { + + #if(VQAMONO_ON) + case MCGA: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("MCGA Buffered"); + } else { + Mono_Print("MCGA Direct to screen"); + } + break; + #endif + + #if(VQAXMODE_ON) + case XMODE_320X200: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x200 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x200 VRAM Copy"); + } else { + Mono_Print("XMODE 320x200 Linear->Banked"); + } + } + break; + + case XMODE_320X240: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("XMODE 320x240 Buffered"); + } else { + if (config->DrawFlags & VQACFGF_VRAMCB) { + Mono_Print("XMODE 320x240 VRAM Copy"); + } else { + Mono_Print("XMODE 320x240 Linear->Banked"); + } + } + break; + #endif + + #if(VQAVESA_ON) + case VESA_640X480_256: + Mono_Print("VESA 640x480"); + break; + + case VESA_320X200_32K_1: + if (config->DrawFlags & VQACFGF_BUFFER) { + Mono_Print("VESA 320x200 Buffered"); + } else { + Mono_Print("VESA 320x200 Direct to screen"); + } + break; + #endif + + default: + Mono_Print("UNKNOWN"); + break; + } + + /* Sound status */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 2); + Mono_Print(" Sound: "); + + if (config->OptionFlags & VQAOPTF_AUDIO) { + sprintf(txt,"%u Hz", config->AudioRate); + Mono_Print(txt); + } else { + Mono_Print("OFF"); + } + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 3); + Mono_Print(" Driver Name: "); + Mono_Print(HMIDevName); + + /* Frame rates */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 4); + sprintf(txt," Load Frame Rate: %d", config->FrameRate); + Mono_Print(txt); + + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 5); + sprintf(txt," Draw Frame Rate: %d", config->DrawRate); + Mono_Print(txt); + + /* Slow palette */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 6); + Mono_Print(" Slow palette: "); + + if (config->OptionFlags & VQAOPTF_SLOWPAL) { + Mono_Print("ON"); + } else { + Mono_Print("OFF"); + } + + /* Memory Usage */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 7); + sprintf(txt," Memory Used: %ld", vqabuf->MemUsed); + Mono_Print(txt); + + /* Timer Method */ + Mono_Set_Cursor(MAIN_WX1 + 18, MAIN_WY1 + 8); + + if (VQA_TimerMethod() == VQA_TMETHOD_DOS) { + Mono_Print(" DOS Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_INT) { + Mono_Print(" Interrupt Timer:"); + } else if (VQA_TimerMethod() == VQA_TMETHOD_AUDIO) { + Mono_Print(" Audio DMA Timer:"); + } else { + Mono_Print(" Defualt:"); + } + + /* Init loader data window */ + Mono_Draw_Rect(LOADER_WX1, LOADER_WY1, (LOADER_WX2 - LOADER_WX1 + 1), + (LOADER_WY2 - LOADER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((LOADER_WX2 + LOADER_WX1 - strlen(LOADER_TITLE)) / 2, + LOADER_WY1); + + Mono_Print(LOADER_TITLE); + + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 2); + Mono_Print("# Waits on Drawer:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 3); + Mono_Print(" # Waits on Audio:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 4); + Mono_Print(" Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY1 + 5); + Mono_Print(" Max Frame Size:"); + Mono_Set_Cursor(LOADER_WX1 + 2, LOADER_WY2 - 2); + Mono_Print("Audio:"); + + /* Init drawer data window */ + Mono_Draw_Rect(DRAWER_WX1, DRAWER_WY1, (DRAWER_WX2 - DRAWER_WX1 + 1), + (DRAWER_WY2 - DRAWER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((DRAWER_WX2 + DRAWER_WX1 - strlen(DRAWER_TITLE)) / 2, + DRAWER_WY1); + + Mono_Print(DRAWER_TITLE); + + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 1); + Mono_Print(" Current Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 2); + Mono_Print(" Desired Frame #:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 3); + Mono_Print("# Waits on Flipper:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 4); + Mono_Print(" # Waits on Loader:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 5); + Mono_Print(" # Frames Skipped:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY1 + 6); + Mono_Print(" VQ Block Size:"); + Mono_Set_Cursor(DRAWER_WX1 + 2, DRAWER_WY2 - 2); + Mono_Print("Frames: Cbooks:"); + + /* Init audio data window */ + Mono_Draw_Rect(AUDIO_WX1, AUDIO_WY1, (AUDIO_WX2 - AUDIO_WX1 + 1), + (AUDIO_WY2 - AUDIO_WY1 + 1), 2, 1); + + Mono_Set_Cursor((AUDIO_WX2 + AUDIO_WX1 - strlen(AUDIO_TITLE)) / 2, + AUDIO_WY1); + + Mono_Print(AUDIO_TITLE); + + Mono_Set_Cursor(AUDIO_WX1 + 2, AUDIO_WY1 + 1); + Mono_Print("# Repeated Buffers:"); + + /* Init flipper data window */ + Mono_Draw_Rect(FLIPPER_WX1, FLIPPER_WY1, (FLIPPER_WX2 - FLIPPER_WX1 + 1), + (FLIPPER_WY2 - FLIPPER_WY1 + 1), 2, 1); + + Mono_Set_Cursor((FLIPPER_WX2 + FLIPPER_WX1 - strlen(FLIPPER_TITLE)) / 2, + FLIPPER_WY1); + + Mono_Print(FLIPPER_TITLE); + + Mono_Set_Cursor(FLIPPER_WX1 + 2, FLIPPER_WY1 + 1); + Mono_Print("Current Frame #:"); +} + + +/**************************************************************************** +* +* NAME +* VQA_UpdateMono - Update the player's mono output. +* +* SYNOPSIS +* VQA_UpdateMono(VQA) +* +* void VQA_UpdateMono(VQAHandleP *); +* +* FUNCTION +* +* INPUTS +* VQA - Pointer to private VQA handle. +* +* RESULT +* NONE +* +****************************************************************************/ + +#pragma argsused +void VQA_UpdateMono(VQAHandleP *vqap) +{ + VQAData *vqabuf; + VQAConfig *config; + VQAFrameNode *frame; + VQACBNode *cbook; + long frameindex = -1; + long loadcb = -1; + long drawcb = -1; + long i; + unsigned long curtime; + char txt[80]; + + /* Dereference commonly used data members for quick access. */ + vqabuf = vqap->VQABuf; + config = &vqap->Config; + + /* Timer value */ + curtime = VQA_GetTime(vqap); + Mono_Set_Cursor(MAIN_WX1 + 40, MAIN_WY1 + 8); + sprintf(txt,"%02ld:%02ld.%02ld",curtime / (VQA_TIMETICKS * VQA_TIMETICKS), + curtime / VQA_TIMETICKS,((curtime * 100L) / VQA_TIMETICKS) + -((curtime / VQA_TIMETICKS) * 100L)); + + Mono_Print(txt); + + /* Loader data */ + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 1); + sprintf(txt,"%4d",vqabuf->Loader.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 2); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnDrawer); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 3); + sprintf(txt,"%4ld",vqabuf->Loader.WaitsOnAudio); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 4); + sprintf(txt,"%5u",vqabuf->Loader.FrameSize); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 22, LOADER_WY1 + 5); + sprintf(txt,"%5u",vqabuf->Loader.MaxFrameSize); + Mono_Print(txt); + + #if(VQAAUDIO_ON) + /* Draw a picture of the audio buffers */ + for (i = 0; i < vqabuf->Audio.NumAudBlocks; i++) { + if (vqabuf->Audio.IsLoaded[i] == 0) { + txt[i] = '_'; + } else { + txt[i] = 'X'; + } + } + + txt[i] = 0; + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(LOADER_WX1 + 9,LOADER_WY2-1); + Mono_Print(" "); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.PlayPosition + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("P"); + + Mono_Set_Cursor(LOADER_WX1 + 9 + vqabuf->Audio.AudBufPos + / config->HMIBufSize,LOADER_WY2 - 1); + + Mono_Print("L"); + #endif /* VQAAUDIO_ON */ + + /* Drawer data */ + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Drawer.LastFrameNum); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 2); + sprintf(txt,"%4d", vqabuf->Drawer.DesiredFrame); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 3); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnFlipper); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 4); + sprintf(txt,"%4ld", vqabuf->Drawer.WaitsOnLoader); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 5); + sprintf(txt,"%4d", vqabuf->Drawer.NumSkipped); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 22,DRAWER_WY1 + 6); + sprintf(txt," %dx%d", vqap->Header.BlockWidth, vqap->Header.BlockHeight); + Mono_Print(txt); + + /* Draw a picture of the frame buffers */ + frame = vqabuf->FrameData; + + for (i = 0; i < config->NumFrameBufs; i++) { + if (frame->Flags & VQAFRMF_LOADED) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + + if (vqabuf->Flipper.CurFrame == frame) { + frameindex = i; + } + + frame = frame->Next; + } + + txt[i] = 0; + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 2); + Mono_Print(txt); + + Mono_Set_Cursor(DRAWER_WX1 + 10,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 10 + frameindex,DRAWER_WY2 - 1); + Mono_Print("^"); + + /* Draw a picture of the codebook buffers */ + cbook = vqabuf->CBData; + + for (i = 0; i < config->NumCBBufs; i++) { + if (vqabuf->Loader.CurCB == cbook) { + loadcb = i; + } + + if (vqabuf->Flipper.CurFrame->Codebook == cbook) { + drawcb = i; + } + + cbook = cbook->Next; + } + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 2); + Mono_Print("___"); + + Mono_Set_Cursor(DRAWER_WX1 + 34,DRAWER_WY2 - 1); + Mono_Print(" "); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + loadcb,DRAWER_WY2 - 1); + Mono_Print("L"); + + Mono_Set_Cursor(DRAWER_WX1 + 34 + drawcb,DRAWER_WY2 - 1); + Mono_Print("D"); + + /* Audio data */ + #if(VQAAUDIO_ON) + Mono_Set_Cursor(AUDIO_WX1 + 22, AUDIO_WY1 + 1); + sprintf(txt,"%4ld", vqabuf->Audio.NumSkipped); + Mono_Print(txt); + #endif + + /* Flipper data */ + Mono_Set_Cursor(FLIPPER_WX1 + 22,FLIPPER_WY1 + 1); + sprintf(txt,"%4d", vqabuf->Flipper.LastFrameNum); + Mono_Print(txt); + Mono_Set_Cursor(0,0); +} + +#endif /* VQAMONO_ON */ + diff --git a/WINVQ/VQA32/OLD/SOS.H b/WINVQ/VQA32/OLD/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/WINVQ/VQA32/OLD/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WINVQ/VQA32/OLD/SOSDATA.H b/WINVQ/VQA32/OLD/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WINVQ/VQA32/OLD/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WINVQ/VQA32/OLD/SOSDEFS.H b/WINVQ/VQA32/OLD/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WINVQ/VQA32/OLD/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WINVQ/VQA32/OLD/SOSFNCT.H b/WINVQ/VQA32/OLD/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WINVQ/VQA32/OLD/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WINVQ/VQA32/OLD/SOSRES.H b/WINVQ/VQA32/OLD/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WINVQ/VQA32/OLD/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WINVQ/VQA32/OLD/TASK.CPP b/WINVQ/VQA32/OLD/TASK.CPP new file mode 100644 index 0000000..5dfef86 --- /dev/null +++ b/WINVQ/VQA32/OLD/TASK.CPP @@ -0,0 +1,639 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* task.c +* +* DESCRIPTION +* Loading and drawing delegation +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* July 25, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Alloc - Allocate a VQAHandle to use. +* VQA_Free - Free a VQAHandle. +* VQA_Init - Initialize the VQAHandle IO. +* VQA_Play - Play the VQA movie. +* VQA_SetStop - Set the frame the player should stop on. +* VQA_GetInfo - Get VQA movie information. +* VQA_GetStats - Get VQA movie statistics. +* VQA_Version - Get VQA library version number. +* VQA_IDString - Get the VQA player library's ID string. +* +* PRIVATE +* VQA_IO_Task - Loader task for multitasking. +* VQA_Rendering_Task - Drawer task for multitasking. +* User_Update - Page flip routine called by the task interrupt. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Externals */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Check_Key(void); +extern int __cdecl Get_Key(void); + +#ifdef __cplusplus +} +#endif + + +/**************************************************************************** +* +* NAME +* VQA_Alloc - Allocate a VQAHandle to use. +* +* SYNOPSIS +* VQAHandle = VQA_Alloc() +* +* VQAHandle *VQA_Alloc(void); +* +* FUNCTION +* Obtain a VQAHandle. This handle is used by most VQA library functions, +* and contains the current position in the file. This is the only legal +* way to obtain a VQAHandle. +* +* INPUTS +* NONE +* +* RESULT +* VQA - Handle of a VQA. +* +****************************************************************************/ + +VQAHandle *VQA_Alloc(void) +{ + VQAHandleP *vqa; + + if ((vqa = (VQAHandleP *)malloc(sizeof(VQAHandleP))) != NULL) { + memset(vqa, 0, sizeof(VQAHandleP)); + } + + return ((VQAHandle *)vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Free - Free a VQAHandle. +* +* SYNOPSIS +* VQA_Free(VQA) +* +* void VQA_Free(VQAHandle *); +* +* FUNCTION +* Dispose of a VQAHandle. This is the only legal way to dispose of a +* VQAHandle. +* +* INPUTS +* VQA - Pointer to VQAHandle to dispose of. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Free(VQAHandle *vqa) +{ + if (vqa) free(vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Init - Initialize the VQAHandle IO handler. +* +* SYNOPSIS +* VQA_Init(VQA, IOHandler) +* +* void VQA_Init(VQAHandle *, IOHandler *); +* +* FUNCTION +* Initialize the specified VQAHandle IO with the client provided custom +* IO handler. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize. +* IOHandler - Pointer to custom file I/O handler function. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Init(VQAHandle *vqa, long(*iohandler)(VQAHandle *vqa, long action, + void *buffer, long nbytes)) +{ + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_Reset - Reset the VQAHandle. +* +* SYNOPSIS +* VQA_Reset(VQA) +* +* void VQA_Reset(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - VQAHandle to reset. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Reset(VQAHandle *vqa) +{ + VQAData *vqabuf; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + vqabuf->Flags = 0; + vqabuf->LoadedFrames = 0; + vqabuf->DrawnFrames = 0; + vqabuf->StartTime = 0; + vqabuf->EndTime = 0; +} + + +/**************************************************************************** +* +* NAME +* VQA_Play - Play the VQA movie. +* +* SYNOPSIS +* Error = VQA_Play(VQA, Mode) +* +* long VQA_Play(VQAHandle *, long); +* +* FUNCTION +* Playback the movie associated with the specified VQAHandle. +* +* INPUTS +* VQA - Pointer to handle of movie to play. +* Mode - Playback mode. +* VQAMODE_RUN - Run the movie until completion. +* VQAMODE_WALK - Walk the movie frame by frame. +* VQAMODE_PAUSE - Pause the movie. +* VQAMODE_STOP - Stop the movie (Shutdown). +* +* RESULT +* Error - 0 if successful, or error code. +* +****************************************************************************/ + +long VQA_Play(VQAHandle *vqa, long mode) +{ + VQAData *vqabuf; + VQAConfig *config; + VQADrawer *drawer; + long rc; + long i; + long key; + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* One time player priming. */ + if (!(vqabuf->Flags & VQADATF_PRIMED)) { + + /* Init the Drawer's configuration */ + VQA_Configure_Drawer((VQAHandleP *)vqa); + + /* If audio enabled & loaded, start playing */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) && vqabuf->Audio.IsLoaded[0]) { + VQA_StartAudio((VQAHandleP *)vqa); + } + #endif + + /* Initialize the timer */ + i = ((vqabuf->Drawer.CurFrame->FrameNum * VQA_TIMETICKS) + / config->DrawRate); + + VQA_SetTimer((VQAHandleP *)vqa, i, config->TimerMethod); + vqabuf->StartTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Set up the Mono screen */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_InitMono((VQAHandleP *)vqa); + } + #endif + + /* Priming is complete. */ + vqabuf->Flags |= VQADATF_PRIMED; + } + + /* Main Player Loop */ + switch (mode) { + case VQAMODE_PAUSE: + if ((vqabuf->Flags & VQADATF_PAUSED) == 0) { + vqabuf->Flags |= VQADATF_PAUSED; + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Stop the audio while the movie is paused. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + } + + rc = VQAERR_PAUSED; + break; + + case VQAMODE_RUN: + case VQAMODE_WALK: + default: + + /* Start up the movie if is it currently paused. */ + if (vqabuf->Flags & VQADATF_PAUSED) { + vqabuf->Flags &= ~VQADATF_PAUSED; + + /* Start the audio if it was previously on. */ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + VQA_StartAudio((VQAHandleP *)vqa); + } + #endif + + VQA_SetTimer((VQAHandleP *)vqa, vqabuf->EndTime, config->TimerMethod); + } + + /* Load, Draw, Load, Draw, Load, Draw ... */ + while ((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + != (VQADATF_DDONE|VQADATF_LDONE)) { + + /* Load a frame */ + if (!(vqabuf->Flags & VQADATF_LDONE)) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + vqabuf->Flags |= VQADATF_LDONE; + rc = 0; + } + } + + /* Draw a frame */ + if ((config->DrawFlags & VQACFGF_NODRAW) == 0) { + if ((rc = (*(vqabuf->Draw_Frame))(vqa)) == 0) { + vqabuf->DrawnFrames++; + rc = vqabuf->Drawer.LastFrameNum; + + if (User_Update(vqa)) { + vqabuf->Flags |= (VQADATF_DDONE|VQADATF_LDONE); + } + } + else if ((vqabuf->Flags & VQADATF_LDONE) + && (rc == VQAERR_NOBUFFER)) { + vqabuf->Flags |= VQADATF_DDONE; + } + } else { + vqabuf->Flags |= VQADATF_DDONE; + drawer->CurFrame->Flags = 0L; + drawer->CurFrame = drawer->CurFrame->Next; + } + + /* Update Mono output */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_UpdateMono((VQAHandleP *)vqa); + } + #endif + + if (mode == VQAMODE_WALK) { + break; + } + #if(VQASTANDALONE) + else { + + /* Do single-stepping check. */ + if (config->OptionFlags & VQAOPTF_STEP) { + while ((key = Check_Key()) == 0); + Get_Key(); + + /* Escape key still quits. */ + if (key == 27) { + break; + } + } + + /* Check for ESC */ + if ((key = Check_Key()) != 0) { + mode = VQAMODE_STOP; + break; + } + } + #endif + } + break; + } + + /* If the movie is finished or we are requested to stop then shutdown. */ + if (((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + == (VQADATF_DDONE|VQADATF_LDONE)) || (mode == VQAMODE_STOP)) { + + /* Record the end time; must be done before stopping audio, since we're + * getting the elapsed time from the audio DMA position. + */ + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Stop audio, if it's playing. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + + /* Movie is finished. */ + rc = VQAERR_EOF; + } + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* VQA_SetStop - Set the frame the player should stop on. +* +* SYNOPSIS +* OldStop = VQA_SetStop(VQA, Frame) +* +* long = VQA_SetStop(VQAHandle *, long); +* +* FUNCTION +* Set the frame that the player should stop on. This function will only +* work on movies that are already open. +* +* INPUTS +* VQA - VQAHandle of movie to set the stop frame for. +* Frame - Frame number to stop on. +* +* RESULT +* OldStop - Previous stop frame. (-1 = invalid stop frame) +* +****************************************************************************/ + +long VQA_SetStop(VQAHandle *vqa, long stop) +{ + long oldstop = -1; + VQAHeader *header; + + /* Get a local pointer to the header. */ + header = &((VQAHandleP *)vqa)->Header; + + if ((stop > 0) && (header->Frames >= stop)) { + oldstop = header->Frames; + header->Frames = stop; + } + + return (oldstop); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetInfo - Get VQA movie information. +* +* SYNOPSIS +* VQA_GetInfo(VQA, Info) +* +* void VQA_GetInfo(VQAHandle *, VQAInfo *); +* +* FUNCTION +* Retrieve information about the opened movie. +* +* INPUTS +* VQA - Pointer to VQAHandle of opened movie. +* Info - Pointer to VQAInfo structure to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetInfo(VQAHandle *vqa, VQAInfo *info) +{ + VQAHeader *header; + + /* Dereference header structure. */ + header = &((VQAHandleP *)vqa)->Header; + + info->NumFrames = header->Frames; + info->ImageHeight = header->ImageHeight; + info->ImageWidth = header->ImageWidth; + info->ImageBuf = ((VQAHandleP *)vqa)->VQABuf->Drawer.ImageBuf; +} + + +/**************************************************************************** +* +* NAME +* VQA_GetStats - Get VQA movie statistics. +* +* SYNOPSIS +* VQA_GetStats(VQA, Stats) +* +* void VQA_GetStats(VQAHandle *, VQAStatistics *); +* +* FUNCTION +* Retrieve the statistics for the VQA movie. +* +* INPUTS +* VQA - Handle of VQA movie to get statistics for. +* Stats - Pointer to VQAStatistics to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetStats(VQAHandle *vqa, VQAStatistics *stats) +{ + VQAData *vqabuf; + + /* Dereference VQAData structure from VQAHandle */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + stats->MemUsed = vqabuf->MemUsed; + stats->StartTime = vqabuf->StartTime; + stats->EndTime = vqabuf->EndTime; + stats->FramesLoaded = vqabuf->LoadedFrames; + stats->FramesDrawn = vqabuf->DrawnFrames; + stats->FramesSkipped = vqabuf->Drawer.NumSkipped; + stats->MaxFrameSize = vqabuf->Loader.MaxFrameSize; + + #if(VQAAUDIO_ON) + stats->SamplesPlayed = vqabuf->Audio.SamplesPlayed; + #else + stats->SamplesPlayed = 0; + #endif +} + + +/**************************************************************************** +* +* NAME +* VQA_Version - Get VQA library version number. +* +* SYNOPSIS +* Version = VQA_Version() +* +* char *VQA_Version(void); +* +* FUNCTION +* Return the version of the VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* Version - Pointer to version number string. +* +****************************************************************************/ + +char *VQA_Version(void) +{ + return(VQA_VERSION); +} + + +/**************************************************************************** +* +* NAME +* VQA_IDString - Get the VQA player library's ID string. +* +* SYNOPSIS +* IDString = VQA_IDString() +* +* char *VQA_IDString(void); +* +* FUNCTION +* Return the ID string of this VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* IDString - Pointer to ID string. +* +****************************************************************************/ + +char *VQA_IDString(void) +{ + return (VQA_IDSTRING); +} + + +/**************************************************************************** +* +* NAME +* User_Update - Page flip routine called by the task interrupt. +* +* SYNOPSIS +* User_Update(VQA) +* +* long User_Update(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Handle of VQA movie. +* +* RESULT +* NONE +* +****************************************************************************/ + +long User_Update(VQAHandle *vqa) +{ + VQAData *vqabuf; + long rc = 0; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + if (vqabuf->Flags & VQADATF_UPDATE) { + + /* Invoke the page flip routine */ + rc = (*(vqabuf->Page_Flip))(vqa); + + /* Update data for mono output */ + vqabuf->Flipper.LastFrameNum = vqabuf->Flipper.CurFrame->FrameNum; + + /* Mark the frame as loadable */ + vqabuf->Flipper.CurFrame->Flags = 0L; + vqabuf->Flags &= (~VQADATF_UPDATE); + } + + return (rc); +} + +void VQA_Dummy(void) +{ + Set_Font(NULL); +} diff --git a/WINVQ/VQA32/OLD/TASM32.CFG b/WINVQ/VQA32/OLD/TASM32.CFG new file mode 100644 index 0000000..da37b1d --- /dev/null +++ b/WINVQ/VQA32/OLD/TASM32.CFG @@ -0,0 +1,10 @@ +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 diff --git a/WINVQ/VQA32/OLD/TLIB.CFG b/WINVQ/VQA32/OLD/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/WINVQ/VQA32/OLD/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/WINVQ/VQA32/OLD/TLINK32.CFG b/WINVQ/VQA32/OLD/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/WINVQ/VQA32/OLD/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/WINVQ/VQA32/OLD/UNVQ.H b/WINVQ/VQA32/OLD/UNVQ.H new file mode 100644 index 0000000..9f94fab --- /dev/null +++ b/WINVQ/VQA32/OLD/UNVQ.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQAUNVQ_H +#define VQAUNVQ_H +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* unvq.h +* +* DESCRIPTION +* VQ frame decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +#ifdef PHARLAP_TNT +#include +#endif + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* unvqbuff.asm */ +#ifndef PHARLAP_TNT +void cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void cdecl UnVQ_4x2_Woofer(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +/* unvqvesa.asm */ +void cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, unsigned char *palette, + unsigned long grains_per_win,unsigned long dummy1,unsigned long dummy2); + +#else /* PHARLAP_TNT */ + +void cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +/* unvqvesa.asm */ +void cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, FARPTR palette, unsigned long grains_per_win, + unsigned long dummy1, unsigned long dummy2); + +#endif /* PHARLAP_TNT */ + +/* unvqxmde.asm */ +void cdecl UnVQ_4x2_Xmode(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void cdecl UnVQ_4x2_XmodeCB(unsigned char *cbdummy, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void cdecl Upload_4x2CB(unsigned char *codebook, unsigned long numentries); +void cdecl XlatePointers(unsigned char *pointers, unsigned long numpointers); + +#ifdef __cplusplus +} +#endif + +#endif /* VQAUNVQ_H */ diff --git a/WINVQ/VQA32/OLD/UNVQBUFF.ASM b/WINVQ/VQA32/OLD/UNVQBUFF.ASM new file mode 100644 index 0000000..7bdea8c --- /dev/null +++ b/WINVQ/VQA32/OLD/UNVQBUFF.ASM @@ -0,0 +1,1153 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQBuff.asm +;* +;* DESCRIPTION +;* Buffered VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* Feburary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + + IF VQABLOCK_2X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x2(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x2:NEAR + PROC UnVQ_2x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x2 + ENDIF ;VQABLOCK_2X2 + + + IF VQABLOCK_2X3 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x3(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x3(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x3:NEAR + PROC UnVQ_2x3 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + add eax,[bufwidth] + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov dx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],dx + ELSE + mov [edi+ebx],dx + ENDIF + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2rd row to dest + ELSE + mov [edi+ebx],ax ;Write 2rd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x3 + ENDIF ;VQABLOCK_2X3 + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x2(unsigned char *, unsigned char *, unsigned char *, +;* long, long, long); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;;; GLOBAL C UnVQ_4x2:NEAR +;;; PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi +;;; +;;; ARG codebook:NEAR PTR +;;; ARG pointers:NEAR PTR +;;; +;;; IF PHARLAP_TNT +;;; ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; ARG buffer:NEAR PTR +;;; ENDIF +;;; +;;; ARG blocksperrow:DWORD +;;; ARG numrows:DWORD +;;; ARG bufwidth:DWORD +;;; +;;; LOCAL data_end:DWORD +;;; LOCAL cb_offset:DWORD +;;; LOCAL edi_startval:DWORD +;;; LOCAL rowoffset:DWORD +;;; +;;;;---------------------------------------------------------------------------- +;;;; Initialize +;;;;---------------------------------------------------------------------------- +;;; +;;; mov eax,[codebook] ;Adjust the codebook address so +;;; sub eax,4 ; that the pointer offsets will +;;; mov [cb_offset],eax ; point directly at the codeword. +;;; +;;; mov eax,[bufwidth] ;Compute the offset to the next +;;; shl eax,1 ; row of blocks. +;;; mov [rowoffset],eax +;;; +;;; mov esi,[pointers] +;;; mov eax,[numrows] ;Compute the end address of the +;;; mul [blocksperrow] ; pointer data. +;;; shl eax,1 +;;; add eax,esi +;;; mov [data_end],eax +;;; +;;; IF PHARLAP_TNT +;;; push es +;;; les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; mov edi,[buffer] +;;; ENDIF +;;; +;;; mov [edi_startval],edi +;;; +;;;;---------------------------------------------------------------------------- +;;;; Drawing loop +;;;;---------------------------------------------------------------------------- +;;; +;;;??Start_row: +;;; mov ecx,[blocksperrow] ;Number of blocks in a line +;;; +;;;??Not_finished_a_line: +;;; sub ebx,ebx +;;; mov bx,[WORD PTR esi] ;Get the codebook pointer value +;;; add esi,2 ; then advance to the next one. +;;; +;;; or bx,bx ;Is it a one color block? +;;; js short ??One_color +;;; +;;;; Draw multi-color block +;;; +;;; add ebx,[cb_offset] ;Codeword address +;;; mov eax,[ebx] ;Read 1st row of codeword +;;; mov edx,[ebx+4] ;Read 2nd row of codeword +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],edx ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],edx ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;; Draw 1-color block +;;; +;;;??One_color: +;;; cmp bx,SKIP_PTR ;Is this a skip block? +;;; jne ??Draw_One_Color +;;; +;;; add edi,4 ;Move to next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; jmp ??Next_row +;;; +;;;??Draw_One_Color: +;;; not bx ;NOT pointer value to get color +;;; mov bh,bl ;Duplicate color through the +;;; mov ax,bx ; entire dword register. +;;; rol eax,16 +;;; mov ax,bx +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],eax ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],eax ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block positionw +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;;??Next_row: +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;??End_of_data: +;;; IF PHARLAP_TNT +;;; pop es +;;; ENDIF +;;; +;;; ret +;;; +;;; ENDP UnVQ_4x2 +;;; ENDIF ;VQABLOCK_4X2 + + GLOBAL C UnVQ_4x2:NEAR + PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + ARG buffer:NEAR PTR + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so +; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax + add eax,esi + mov [data_end],eax + + mov edi,[buffer] + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx + mov bl,[esi] + mov bh,[esi + eax] ;Get the codebook pointer value + inc esi ; then advance to the next one. + + cmp bh,00Fh ;Is it a one color block? + je short ??One_color + +; Draw multi-color block + + shl ebx,3 + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],edx ;Write 2nd row to dest + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: +; cmp bx,SKIP_PTR ;Is this a skip block? +; jne ??Draw_One_Color +; +; add edi,4 ;Move to next dest block position +; dec ecx ;More blocks for this row? +; jnz short ??Not_finished_a_line +; jmp ??Next_row + +??Draw_One_Color: +; not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + ret + + ENDP UnVQ_4x2 + ENDIF ;VQABLOCK_4X2 + + + IF VQABLOCK_4X4 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x4(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x4(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x4:NEAR + PROC UnVQ_4x4 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + + ;---------------------------------------------------------------------------- + ; Initialize + ;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,2 ; row of blocks + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + + ;---------------------------------------------------------------------------- + ; Drawing loop + ;---------------------------------------------------------------------------- + + ??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + + ??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + + ; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + ENDIF + + mov eax,ebx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + mov ebx,eax + mov eax,[ebx+8] + mov edx,[ebx+12] + mov ebx,[bufwidth] + shl ebx,1 + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 3rd row to dest + ELSE + mov [edi+ebx],eax ;Write 3rd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 4th row to dest + ELSE + mov [edi+ebx],eax ;Write 4th row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + ??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x4 + ENDIF ;VQABLOCK_4X4 + + + + IF VQABLOCK_WOOFER + IF VQABLOCK_4X2 + ;**************************************************************************** + ;* + ;* NAME + ;* UnVQ_4x2_Woofer - Draw 4x2 block VQ frame to a buffer. + ;* + ;* SYNOPSIS + ;* UnVQ_4x2_Woofer(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) + ;* + ;* void UnVQ_4x2_Woofer(unsigned char *, unsigned char *, + ;* unsigned char *, long, long, long); + ;* + ;* FUNCTION + ;* This function draws an image into the specified buffer from the + ;* pointers and codebook provided. This routine has been optimized for + ;* a 320x200 image. + ;* + ;* INPUTS + ;* Codebook - Pointer to codebook used to draw image. + ;* Pointers - Pointer to vector pointer data. + ;* Buffer - Pointer to buffer to draw image into. + ;* BPR - Number of blocks per row. + ;* Rows - Number of rows. + ;* BufWidth - Width of destination buffer in pixels. + ;* + ;* RESULT + ;* NONE + ;* + ;**************************************************************************** + + GLOBAL C UnVQ_4x2_Woofer:NEAR + PROC UnVQ_4x2_Woofer C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + + ;---------------------------------------------------------------------------- + ; Initialize + ;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + ; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax + ; shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + + ;---------------------------------------------------------------------------- + ; Drawing loop + ;---------------------------------------------------------------------------- + + ??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + + ??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx + ; mov bx,[WORD PTR esi] ;Get the codebook pointer value + ; add esi,2 ; then advance to the next one. + mov bl,[esi] + mov bh,[esi + eax] + inc esi + + ; or bx,bx ;Is it a one color block? + ; js short ??One_color + + cmp bh,00Fh + je short ??One_color + + ; Draw multi-color block + + shl ebx,3 + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al ;Write 1st row to dest + shr eax,16 + mov [es:edi+2],al + mov [es:edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [es:edi+ebx+3],dh + ELSE + mov [edi],al ;Write 1st row to dest + shr eax,16 + mov [edi+2],al + mov [edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [edi+ebx+3],dh + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ; Draw 1-color block + + ??One_color: + ; cmp bx,SKIP_PTR ;Is this a skip block? + ; jne ??Draw_One_Color + + ; add edi,4 ;Move to next dest block position + ; dec ecx ;More blocks for this row? + ; jnz short ??Not_finished_a_line + ; jmp ??Next_row + + ??Draw_One_Color: + ; not bx ;NOT pointer value to get color + mov al,bl + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al + mov [es:edi+2],al + mov [es:edi+ebx+1],al ;Write 2nd row to dest + mov [es:edi+ebx+3],al ;Write 2nd row to dest + ELSE + mov [edi],al + mov [edi+2],al + mov [edi+ebx+1],al ;Write 2nd row to dest + mov [edi+ebx+3],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + ??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x2_Woofer + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQABLOCK_WOOFER + + END + diff --git a/WINVQ/VQA32/OLD/UNVQVESA.ASM b/WINVQ/VQA32/OLD/UNVQVESA.ASM new file mode 100644 index 0000000..c25488a --- /dev/null +++ b/WINVQ/VQA32/OLD/UNVQVESA.ASM @@ -0,0 +1,380 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay32 library. +;* +;* FILE +;* unvqvesa.asm +;* +;* DESCRIPTION +;* VESA VQ decompress/draw routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + IF VQAVESA_ON + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;* SYNOPSIS +;* UnVQ_4x2_VESA320_32K(Codebook, Pointers, Palette, GrainPerWin) +;* +;* void UnVQ_4x2_VESA320_32K(char *, char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Codebook - Pointer to codebook. +;* Pointers - Pointer to vector pointer data to unvq. +;* Palette - Pointer to 15-bit palette. +;* GrainPerWin - Granularity units for 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;--------------------------------------------------------------------------- +; SET_2_PIXELS: +; - Loads 2 bytes from codebook, expands them into words in eax +; - Sets them on the screen +; cb_index: offset into codebook of start of 2 bytes to load +; di_index: offset from current di to draw 2 pixels +; BX: offset into codebook of this block +; Uses: EAX, DX, SI! +;--------------------------------------------------------------------------- + + MACRO SET_2_PIXELS cb_index,edi_index + xor edx,edx + mov dl,[BYTE PTR ebx+1+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + shl eax,16 + xor edx,edx + mov dl,[BYTE PTR ebx+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + + IF PHARLAP_TNT + mov [DWORD PTR es:edi+edi_index],eax + ELSE + mov [DWORD PTR edi+edi_index],eax + ENDIF + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROWS macro: +; Draws 'numrows' rows of 'blocksperrow' blocks each +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_ROWS numrows,blocksperrow + LOCAL ??Start_row + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Next_row + LOCAL ??Done + + mov [rowcount],numrows ; initialize row counter + + ;---------------------------------------- Start a new row: +??Start_row: + mov ecx,blocksperrow ; # blocks to fill a line + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS 0,0 + SET_2_PIXELS 2,4 + SET_2_PIXELS 4,640 + SET_2_PIXELS 6,644 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz ??Not_finished_a_line + jmp ??Next_row +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + mov [DWORD PTR es:edi+640],eax ; set 2 pixels + mov [DWORD PTR es:edi+644],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + mov [DWORD PTR edi+640],eax ; set 2 pixels + mov [DWORD PTR edi+644],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: +??Next_row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row +??Done: + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_PART_ROW macro: +; Draws top or bottom half of blocks on a row +; 'numblocks' = # blocks to draw +; 'cb_add' = amt to add to codebook (0=top half, 4=bottom half) +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_PART_ROW numblocks,cb_add + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Done + + mov ecx,numblocks + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js short ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS cb_add,0 + SET_2_PIXELS cb_add+2,4 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz short ??Not_finished_a_line + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz short ??Not_finished_a_line + jmp ??Done +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line +??Done: + ENDM + +;=========================================================================== +; The actual procedure: +;=========================================================================== + + GLOBAL C UnVQ_4x2_VESA320_32K:NEAR + PROC UnVQ_4x2_VESA320_32K C NEAR + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG pal:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG palette:NEAR PTR + ENDIF + + ARG grains_per_win:DWORD + ARG dummy1:DWORD + ARG dummy2:DWORD + + LOCAL rowcount:DWORD ; # rows drawn + LOCAL esi_save:DWORD + LOCAL cb_offset:DWORD + + IF PHARLAP_TNT + LOCAL palette:NEAR PTR + ENDIF + + ;------------------------------------------------------------------- + ; Save registers + ;------------------------------------------------------------------- + pushad + + ;------------------------------------------------------------------- + ; Set GS:[cb_offset] to codebook + ;------------------------------------------------------------------- + mov eax,[codebook] + sub eax,4 + mov [cb_offset],eax + mov esi,[pointers] + + ;------------------------------------------------------------------- + ; Set ES:DI to screen + ;------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + les edi,[FWORD pal] + mov [palette],edi + mov eax,01Ch + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + ;------------------------------------------------------------------- + ; Do Bank 0: + ; - 102 full scanlines (51 rows of blocks) + ; - 128 pixels of the top half of blocks (32 top-half blocks) + ;------------------------------------------------------------------- + SET_WINDOW 0 + DRAW_BLOCK_ROWS 51,80 + DRAW_BLOCK_PART_ROW 32,0 ; do top half + + ;------------------------------------------------------------------- + ; Do Bank 1: + ; - 128 pixels of the bottom half of the previous 32 blocks + ; - 192 pixels of full blocks (1 row of 48 blocks) + ; - 96 full scanlines (48 rows of blocks) + ;------------------------------------------------------------------- + SET_WINDOW [grains_per_win] + sub esi,64 ; subtract word size of last 32 blks + mov edi,384 + DRAW_BLOCK_PART_ROW 32,4 ; do bottom half + mov edi,0 + DRAW_BLOCK_ROWS 1,48 ; draw one row of 48 full blocks + DRAW_BLOCK_ROWS 48,80 ; draw 48 full block rows + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + popad + ret + + ENDP UnVQ_4x2_VESA320_32K + + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQAVESA_ON + + END + diff --git a/WINVQ/VQA32/OLD/UNVQXMDE.ASM b/WINVQ/VQA32/OLD/UNVQXMDE.ASM new file mode 100644 index 0000000..3c641f2 --- /dev/null +++ b/WINVQ/VQA32/OLD/UNVQXMDE.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQxmde.asm +;* +;* DESCRIPTION +;* XMode VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* May 18, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + + IF VQAXMODE_ON + +SKIP_PTR EQU 8000h +CBOOK_SEG EQU (0B0000h - 270h) + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* +;* SYNOPSIS +;* UnVQ_4x2_Xmode(Codebook, Pointers, Buffer, BPR, Rows, Dummy) +;* +;* void UnVQ_4x2_Xmode(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image to the Xmode display from the pointers +;* and codebook provided. This routine has been optimized for a 320x200 +;* image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* Dummy - Not used (prototype placeholder) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_Xmode:NEAR + PROC UnVQ_4x2_Xmode C NEAR USES + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + + SET_PLANE XPLANE_1 + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line1: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color1 + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],dl ;Write 2nd row to dest + ENDIF + +; add edi,4 ;Next dest block position + inc edi ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color1: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + ENDP UnVQ_4x2_Xmode + + +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* +;* SYNOPSIS +;* UnVQ_4x2_XmodeCB(Dummy, Pointers, Buffer, BPR, Rows) +;* +;* void UnVQ_4x2_XmodeCB(unsigned char *, unsigned char *, +;* unsigned char *, unsigned short, +;* unsigned short); +;* +;* FUNCTION +;* This routine copies codebook entries from video RAM to video RAM. +;* The procedure for Write Mode 1 is: +;* +;* - Perform a CPU read at the address of the 4-byte codebook entry; +;* this will load each byte at that address from all 4 bitplanes +;* into the VGA's internal latches. +;* +;* - Perform a CPU write at the destination address, with the BitMask +;* register set to 0 (this tells the VGA hardware to only use the +;* data stored in the latches to write with), and the Map Mask +;* register set to 0Fh (all bitplanes enabled). +;* +;* Optimized for 320x200. +;* The codebook must have been downloaded to video RAM before this +;* routine is called. This routine assumes the multicolor block pointers +;* have been pre-divided by 4, and have a total of 512 added to them, so +;* the pointer is an exact offset into the codebook. +;* +;* INPUTS +;* Dummy - Not used (prototype placeholder) +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to Xmode buffer. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_XmodeCB:NEAR + PROC UnVQ_4x2_XmodeCB C NEAR USES + + ARG cbdummy:FAR PTR + ARG pointers:FAR PTR + ARG buffer:FAR PTR + ARG blocksperrow:WORD + ARG numrows:WORD + +; ;------------------------------------------------------------------- +; ; Local variables: +; ;------------------------------------------------------------------- +; LOCAL di_startval:WORD ; init value for DI, for new row +; +; ;=================================================================== +; ; Initialization +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Save our registers +; ;------------------------------------------------------------------- +; push ds +; push es +; push fs +; pushad +; +; ;------------------------------------------------------------------- +; ; Save codebook's segment in DS +; ;------------------------------------------------------------------- +; mov ax,CBOOK_SEG +; mov ds,ax +; +; ;------------------------------------------------------------------- +; ; Load pointers into FS:BX +; ;------------------------------------------------------------------- +; les di,[pointers] +; mov ax,es +; mov fs,ax +; mov bx,di +; +; ;------------------------------------------------------------------- +; ; Load screen address into ES:DI +; ;------------------------------------------------------------------- +; les di, [buffer] ; point ES:DI to dest +; mov [di_startval],di ; store it +; +; ;------------------------------------------------------------------- +; ; Initialize VGA registers: +; ; - Enable all bitplanes for writing +; ; - Set the BitMask register to 0, so only the data from the +; ; VGA latches is written into the bitplanes +; ;------------------------------------------------------------------- +; SET_PLANE 0fh ; enable all planes for write +; SET_WRITEMODE 1 +; +; ;=================================================================== +; ; The drawing loop: +; ; DS:SI = codebook +; ; ES:DI = drawing buffer +; ; FS:BX = pointers +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Start a new row of drawing +; ;------------------------------------------------------------------- +;??Start_row: +; mov cx,[blocksperrow] ; # blocks to fill a line +; +; ;------------------------------------------------------------------- +; ; Start a new block +; ;------------------------------------------------------------------- +;??Not_finished_a_line: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov si,[WORD PTR fs:bx] ; SI = ptr word (cbook offset) +; add bx,2 ; next ptr word +; ; +; ;................... skip ptr value SKIP_PTR ....................... +; ; +; cmp si,SKIP_PTR +; jne ??Draw_Block +; inc di +; dec cx +; jnz short ??Not_finished_a_line +; jmp ??Next_row +; +; ;------------------------------------------------------------------- +; ; Draw a block via the VGA internal latches: +; ; DS:SI = codebook address +; ; ES:DI = buffer position to draw at +; ; - Load the VGA latches from the 1st 4 codebook bytes +; ; - write 4 pixels with one CPU write +; ; - If this is a one-color block, skip the next codebook read +; ; (Video RAM reads are very slow); otherwise, latch the next 4 +; ; codebook bytes +; ; - write the next 4 pixels +; ;------------------------------------------------------------------- +; ; +; ;..................... draw 1st 4 pixels ........................... +; ; +;??Draw_Block: +; mov al,[ds:si] ; latch 1st 4 cbook bytes +; mov [es:di],al ; write 4 pixels +; ; +; ;.................. check for 1-color block ........................ +; ; +; cmp si,512 ; if 1color blk, don't read +; jb ??One_Color +; ; +; ;..................... draw next 4 pixels .......................... +; ; +; mov al,[ds:si+1] ; latch next 4 cbook bytes +;??One_Color: +; mov [es:di+80],al ; write next 4 pixels +; inc di ; next block position +; ; +; ;...................... check block count .......................... +; ; +; dec cx ; decrement block count +; jnz short ??Not_finished_a_line +; +; ;------------------------------------------------------------------- +; ; Go to the next block row: +; ; - Add (80*2/16) to ES, and set DI to its start-row value +; ; (Incrementing the segment allows us to unpack up to 1MB of data) +; ;------------------------------------------------------------------- +; ; +; ;...................... add (320*2/16) to ES ....................... +; ; +;??Next_row: +; mov ax,es +; add ax,10 ; add 80*2/16 +; mov es,ax +; ; +; ;.................. set DI to its start-row value .................. +; ; +; mov di,[di_startval] +; ; +; ;............ see if we're past the end of the ptr data ............ +; ; +; dec [numrows] +; jg ??Start_row +; +;??End_of_data: +; ;------------------------------------------------------------------- +; ; Restore VGA Write Mode to 0 +; ;------------------------------------------------------------------- +; SET_WRITEMODE 0 +; +; popad +; pop fs +; pop es +; pop ds + ret + + ENDP UnVQ_4x2_XmodeCB + + +;**************************************************************************** +;* +;* NAME +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* +;* SYNOPSIS +;* Upload_4x2CB(Codebook, Entries) +;* +;* void Upload_4x2CB(unsigned char *, unsigned short); +;* +;* FUNCTION +;* This routine copies the given codebook into Xmode VRAM, so that it +;* can be used later for direct video-to-video copies. The address used +;* is the end of video memory minus 02700h (10K). This should be plenty +;* for a 3000-entry codebook; each 4x2 codebook entry will take up 8 +;* 8 bytes, or 2 addresses in XMode (6000 addresses). +;* +;* The routine also creates a 1-color-block table in VRAM, so the 1-color +;* blocks can be generated the same way as the multicolor blocks. +;* +;* XMode 320x200 uses 320x200/4 addresses per page, for a total of 32000 +;* addresses. XMode 320x240 uses 320x240/4 addresses per page, for a +;* total of 38400 addresses. This leaves 27136 addresses unused. +;* +;* INPUTS +;* Codebook - Pointer to codebook to copy. +;* Entries - Number of codebook entries to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Upload_4x2CB:NEAR + PROC Upload_4x2CB C NEAR USES + + ARG codebook:NEAR PTR + ARG numentries:DWORD + +; ;=================================================================== +; ; Generate the 1-color block table by writing each color value from +; ; 0-255 into all 4 bitplanes, at 256 consecutive addresses: +; ;=================================================================== +; SET_PLANE 0fh ; enable all bitplanes for writing +; ;................................................................... +; ; Set ES:DI to destination address, set up CX for the loop +; ;................................................................... +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,0 +; mov cx,256 +; mov ax,0 +;??1_Color_Loop: +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; inc ax ; next color # +; dec cx ; decrement loop counter +; jnz ??1_Color_Loop +; +; ;=================================================================== +; ; Copy the codebook into video RAM, one plane at a time: +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Copy codebook byte 0 into Plane 1 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_1 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_1: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_1 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 1 Plane 2 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,1 ; use 2nd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_2 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_2: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_2 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 2 Plane 3 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,2 ; use 3rd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_3 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_3: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_3 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 3 Plane 4 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,3 ; use 4th byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_4 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_4: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_4 +; + ret + + ENDP Upload_4x2CB + + ENDIF ;VQABLOCK_4X2 + +;**************************************************************************** +;* +;* NAME +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;* SYNOPSIS +;* XlatePointers(Pointers, Entries) +;* +;* void XlatePointers(unsigned char *, unsigned short); +;* +;* FUNCTION +;* +;* INPUTS +;* Pointers - Pointer to vector pointers to translate. +;* Entries - Number of pointer entries. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C XlatePointers:NEAR + PROC XlatePointers C NEAR USES + + ARG pointers:NEAR PTR + ARG numpointers:DWORD + +; ;------------------------------------------------------------------- +; ; Load pointers into DS:SI +; ;------------------------------------------------------------------- +; lds si,[pointers] +; +; mov cx,[numpointers] ; init to # pointers on scrn +; +;??Process_pointer: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov ax,[WORD PTR ds:si] ; SI = ptr word (cbook offset) +; ; +; ;.................... check for a 1-color block .................... +; ; +; or ax,ax ; check to see if high bit set +; js short ??One_color +; ; +; ;....................... multi-color pointer ....................... +; ; +; sub ax,4 ; subtract 4 +; shr ax,2 ; divide by 4 +; add ax,512 ; add 512 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; jmp ??Done +; +;??One_color: +; ; +; ;......................... 1-color pointer ......................... +; ; +; not ax ; get actual color value +; shl ax,1 ; multiply by 2 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; +;??Done: + ret + + ENDP XlatePointers + + ENDIF ;VQAXMODE_ON + END + + + + + diff --git a/WINVQ/VQA32/OLD/VERTAG.CPP b/WINVQ/VQA32/OLD/VERTAG.CPP new file mode 100644 index 0000000..85fbbe9 --- /dev/null +++ b/WINVQ/VQA32/OLD/VERTAG.CPP @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vertag.c +* +* DESCRIPTION +* Version Tag +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +****************************************************************************/ + +#include "vqaplayp.h" + +#ifdef __WATCOMC__ +#define DEVNAME "Watcom/4GW" +#else +#define DEVNAME "Borland/TNT" +#endif + +char VerTag[] = {"$VER$" VQA_IDSTRING" "DEVNAME" ("VQA_DATE")"}; +char ReqTag[] = {"$REQ$" VQA_REQUIRES}; + diff --git a/WINVQ/VQA32/OLD/VQAFILE.H b/WINVQ/VQA32/OLD/VQAFILE.H new file mode 100644 index 0000000..75c57f3 --- /dev/null +++ b/WINVQ/VQA32/OLD/VQAFILE.H @@ -0,0 +1,196 @@ +/* +** 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 . +*/ + +#ifndef VQAFILE_H +#define VQAFILE_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqafile.h +* +* DESCRIPTION +* VQA file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +#include + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED DEFINES. + *-------------------------------------------------------------------------*/ + +/* VQAHeader: VQA movie description header. (VQHD) + * + * Version - VQA version. + * Flags - Various flags. (See below) + * ImageWidth - Image width in pixels. + * ImageHeight - Image height in pixels. + * BlockWidth - Block width in pixels. + * BlockHeight - Block height in pixels. + * Frames - Total number of frames in the movie. + * FPS - Playback rate (Frame Per Second). + * Groupsize - Frame grouping size (frames per codebook). + * Num1Colors - Number of 1 color colors. + * CBentries - Number of codebook entries. + * Xpos - X position to draw frames. (-1 = Center) + * Ypos - Y position to draw frames. (-1 = Center) + * MaxFramesize - Size of largest frame. + * SampleRate - Sample rate of primary audio stream. + * Channels - Number of channels in primary audio stream. + * BitsPerSample - Sample bit size in primary audio stream. + * FutureUse - Reserved for future expansion. + */ +typedef struct _VQAHeader { + unsigned short Version; + unsigned short Flags; + unsigned short Frames; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char BlockWidth; + unsigned char BlockHeight; + unsigned char FPS; + unsigned char Groupsize; + unsigned short Num1Colors; + unsigned short CBentries; + unsigned short Xpos; + unsigned short Ypos; + unsigned short MaxFramesize; + unsigned short SampleRate; + unsigned char Channels; + unsigned char BitsPerSample; + unsigned short AltSampleRate; + unsigned char AltChannels; + unsigned char AltBitsPerSample; + unsigned short FutureUse[5]; +} VQAHeader; + +/* Version type. */ +#define VQAHD_VER1 1 +#define VQAHD_VER2 2 + +/* VQA header flag definitions */ +#define VQAHDB_AUDIO 0 /* Audio track present. */ +#define VQAHDB_ALTAUDIO 1 /* Alternate audio track present. */ +#define VQAHDF_AUDIO (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +// MEG - 11.28.95 - added for debug +extern void Debug_Printf( char *format_string, ... ); + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQAAUDIO_ON 0 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay 32 library. +;* +;* FILE +;* vqaplay.i +;* +;* DESCRIPTION +;* VQA player definitions. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 30, 1995 +;* +;**************************************************************************** + +;*--------------------------------------------------------------------------- +;* CONDITIONAL COMPILATION FLAGS +;*--------------------------------------------------------------------------- + + IF PHARLAP_TNT + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 1 ;Mono display output enable/disable +VQAAUDIO_ON EQU 0 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 0 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ELSE + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 0 ;Mono display output enable/disable +VQAAUDIO_ON EQU 1 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 0 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ENDIF diff --git a/WINVQ/VQA32/OLD/VQAPLAYP.H b/WINVQ/VQA32/OLD/VQAPLAYP.H new file mode 100644 index 0000000..021c1c6 --- /dev/null +++ b/WINVQ/VQA32/OLD/VQAPLAYP.H @@ -0,0 +1,510 @@ +/* +** 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 . +*/ + +#ifndef VQAPLAYP_H +#define VQAPLAYP_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplayp.h +* +* DESCRIPTION +* VQAPlay private library definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* August 21, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include "vqafile.h" +#include "vqaplay.h" +#include "caption.h" + +#if(VQAAUDIO_ON) +#include "sos.h" +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Internal library version. */ +#define VQA_VERSION "2.42" +#define VQA_DATE __DATE__" "__TIME__ + +#define VQA_IDSTRING "VQA32 "VQA_VERSION" ("VQA_DATE")" +#define VQA_REQUIRES "VQM32 2.12 or better." + +/* Block dimensions macro and identifiers. */ +#define BLOCK_DIM(a,b) (((a&0xFF)<<8)|(b&0xFF)) +#define BLOCK_2X2 BLOCK_DIM(2,2) +#define BLOCK_2X3 BLOCK_DIM(2,3) +#define BLOCK_4X2 BLOCK_DIM(4,2) +#define BLOCK_4X4 BLOCK_DIM(4,4) + +/* Memory limits */ +#define VQA_MAX_CBBUFS 10 /* Maximum number of codebook buffers */ +#define VQA_MAX_FRAMEBUFS 30 /* Maximum number of frame buffers */ + +/* Special Constants */ +#define VQA_MASK_POINTER 0x8000 /* Pointer value to use for masking. */ + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* ChunkHeader: IFF chunk identifier header. + * + * id - 4 Byte chunk id. + * size - Size of chunk. + */ +typedef struct _ChunkHeader { + unsigned long id; + unsigned long size; +} ChunkHeader; + + +/* ZAPHeader: ZAP audio compression header. NOTE: If the uncompressed size + * and the compressed size are equal then the audio frame is RAW + * (NOT COMPRESSED). + * + * UnCompSize - Uncompressed size in bytes. + * CompSize - Compressed size in bytes. + */ +typedef struct _ZAPHeader { + unsigned short UnCompSize; + unsigned short CompSize; +} ZAPHeader; + + +/* VQACBNode: A circular list of codebook buffers, used by the load task. + * If the data is compressed, it is loaded into the end of the + * buffer and the compression flags is set. Otherwise the data + * is loaded into the start of the buffer. + * (Make sure this structure's size is always DWORD aligned.) + * + * Buffer - Pointer to Codebook data. + * Next - Pointer to next VQACBNode in the codebook list. + * Flags - Used by the drawer to tell if certain operations have been + * performed on this codebook, such as downloading to VRAM, + * or pre-scaling it. This field is cleared by the Loader when a + * new codebook is loaded. + * CBOffset - Offset into the buffer of the compressed data. + */ +typedef struct _VQACBNode { + unsigned char *Buffer; + struct _VQACBNode *Next; + unsigned long Flags; + unsigned long CBOffset; +} VQACBNode; + +/* VQACBNode flags */ +#define VQACBB_DOWNLOADED 0 /* Download codebook to VRAM (XMODE VRAM) */ +#define VQACBB_CBCOMP 1 /* Codebook is compressed */ +#define VQACBF_DOWNLOADED (1<. +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WINVQ/VQA32/SOSDATA.H b/WINVQ/VQA32/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WINVQ/VQA32/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WINVQ/VQA32/SOSDEFS.H b/WINVQ/VQA32/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WINVQ/VQA32/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WINVQ/VQA32/SOSFNCT.H b/WINVQ/VQA32/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WINVQ/VQA32/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WINVQ/VQA32/SOSRES.H b/WINVQ/VQA32/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WINVQ/VQA32/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WINVQ/VQA32/TASK.CPP b/WINVQ/VQA32/TASK.CPP new file mode 100644 index 0000000..731ea9a --- /dev/null +++ b/WINVQ/VQA32/TASK.CPP @@ -0,0 +1,706 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* task.c +* +* DESCRIPTION +* Loading and drawing delegation +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* July 25, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VQA_Alloc - Allocate a VQAHandle to use. +* VQA_Free - Free a VQAHandle. +* VQA_Init - Initialize the VQAHandle IO. +* VQA_Play - Play the VQA movie. +* VQA_SetStop - Set the frame the player should stop on. +* VQA_GetInfo - Get VQA movie information. +* VQA_GetStats - Get VQA movie statistics. +* VQA_Version - Get VQA library version number. +* VQA_IDString - Get the VQA player library's ID string. +* +* PRIVATE +* VQA_IO_Task - Loader task for multitasking. +* VQA_Rendering_Task - Drawer task for multitasking. +* User_Update - Page flip routine called by the task interrupt. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "vqaplayp.h" +#include + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* Externals */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Check_Key(void); +extern int __cdecl Get_Key(void); + +#ifdef __cplusplus +} +#endif + + +/**************************************************************************** +* +* NAME +* VQA_Alloc - Allocate a VQAHandle to use. +* +* SYNOPSIS +* VQAHandle = VQA_Alloc() +* +* VQAHandle *VQA_Alloc(void); +* +* FUNCTION +* Obtain a VQAHandle. This handle is used by most VQA library functions, +* and contains the current position in the file. This is the only legal +* way to obtain a VQAHandle. +* +* INPUTS +* NONE +* +* RESULT +* VQA - Handle of a VQA. +* +****************************************************************************/ + +VQAHandle *VQA_Alloc(void) +{ + VQAHandleP *vqa; + + if ((vqa = (VQAHandleP *)malloc(sizeof(VQAHandleP))) != NULL) { + memset(vqa, 0, sizeof(VQAHandleP)); + } + + return ((VQAHandle *)vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Free - Free a VQAHandle. +* +* SYNOPSIS +* VQA_Free(VQA) +* +* void VQA_Free(VQAHandle *); +* +* FUNCTION +* Dispose of a VQAHandle. This is the only legal way to dispose of a +* VQAHandle. +* +* INPUTS +* VQA - Pointer to VQAHandle to dispose of. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Free(VQAHandle *vqa) +{ + if (vqa) free(vqa); +} + + +/**************************************************************************** +* +* NAME +* VQA_Init - Initialize the VQAHandle IO handler. +* +* SYNOPSIS +* VQA_Init(VQA, IOHandler) +* +* void VQA_Init(VQAHandle *, IOHandler *); +* +* FUNCTION +* Initialize the specified VQAHandle IO with the client provided custom +* IO handler. +* +* INPUTS +* VQA - Pointer to VQAHandle to initialize. +* IOHandler - Pointer to custom file I/O handler function. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Init(VQAHandle *vqa, long(*iohandler)(VQAHandle *vqa, long action, + void *buffer, long nbytes)) +{ + ((VQAHandleP *)vqa)->IOHandler = iohandler; +} + + +/**************************************************************************** +* +* NAME +* VQA_Reset - Reset the VQAHandle. +* +* SYNOPSIS +* VQA_Reset(VQA) +* +* void VQA_Reset(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - VQAHandle to reset. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_Reset(VQAHandle *vqa) +{ + VQAData *vqabuf; + + /* Dereference data members for quick access */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + vqabuf->Flags = 0; + vqabuf->LoadedFrames = 0; + vqabuf->DrawnFrames = 0; + vqabuf->StartTime = 0; + vqabuf->EndTime = 0; +} + +int VQAMovieDone; +/**************************************************************************** +* +* NAME +* VQA_Play - Play the VQA movie. +* +* SYNOPSIS +* Error = VQA_Play(VQA, Mode) +* +* long VQA_Play(VQAHandle *, long); +* +* FUNCTION +* Playback the movie associated with the specified VQAHandle. +* +* INPUTS +* VQA - Pointer to handle of movie to play. +* Mode - Playback mode. +* VQAMODE_RUN - Run the movie until completion. +* VQAMODE_WALK - Walk the movie frame by frame. +* VQAMODE_PAUSE - Pause the movie. +* VQAMODE_STOP - Stop the movie (Shutdown). +* +* RESULT +* Error - 0 if successful, or error code. +* +****************************************************************************/ + +long VQA_Play(VQAHandle *vqa, long mode) +{ + VQAData *vqabuf; + VQAConfig *config; + VQADrawer *drawer; + long rc; + long i; + long key; + +#ifdef WIN32 + unsigned char *pal; + long palsize; + long slowpal; + VQAFrameNode *curframe; + int setpalette; +#endif //WIN32 + + + +#ifdef WIN32 + /* + ** Save the process priority level then bump it up + */ + DWORD process_priority = GetPriorityClass(GetCurrentProcess()); + SetPriorityClass (GetCurrentProcess() , HIGH_PRIORITY_CLASS); +#endif //WIN32 + + /* Dereference commonly used data members for quick access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + drawer = &vqabuf->Drawer; + config = &((VQAHandleP *)vqa)->Config; + + /* One time player priming. */ + if (!(vqabuf->Flags & VQADATF_PRIMED)) { + + /* Init the Drawer's configuration */ + VQA_Configure_Drawer((VQAHandleP *)vqa); + + /* If audio enabled & loaded, start playing */ + #if(VQAAUDIO_ON) + if ((config->OptionFlags & VQAOPTF_AUDIO) && vqabuf->Audio.IsLoaded[0]) { + VQA_StartAudio((VQAHandleP *)vqa); + } + #endif + + /* Initialize the timer */ + i = ((vqabuf->Drawer.CurFrame->FrameNum * VQA_TIMETICKS) + / config->DrawRate); + + VQA_SetTimer((VQAHandleP *)vqa, i, config->TimerMethod); + vqabuf->StartTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Set up the Mono screen */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_InitMono((VQAHandleP *)vqa); + } + #endif + + /* Priming is complete. */ + vqabuf->Flags |= VQADATF_PRIMED; + } + + /* Main Player Loop */ + switch (mode) { + case VQAMODE_PAUSE: + if ((vqabuf->Flags & VQADATF_PAUSED) == 0) { + vqabuf->Flags |= VQADATF_PAUSED; + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Stop the audio while the movie is paused. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + } + + rc = VQAERR_PAUSED; + break; + + case VQAMODE_RUN: + case VQAMODE_WALK: + default: + + /* Start up the movie if is it currently paused. */ + if (vqabuf->Flags & VQADATF_PAUSED) { + vqabuf->Flags &= ~VQADATF_PAUSED; + + /* Start the audio if it was previously on. */ + #if(VQAAUDIO_ON) + if (config->OptionFlags & VQAOPTF_AUDIO) { + if (VQA_StartAudio((VQAHandleP *)vqa) != 0){ + + /* Stop audio, if it's playing. */ + VQA_StopAudio((VQAHandleP *)vqa); + #ifdef WIN32 + /* + ** Restore the process priority level + */ + SetPriorityClass (GetCurrentProcess() , process_priority); + #endif //WIN32 + return (VQAERR_EOF); + } + } + #endif + + VQA_SetTimer((VQAHandleP *)vqa, vqabuf->EndTime, config->TimerMethod); + } + + /* Load, Draw, Load, Draw, Load, Draw ... */ + while ((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + != (VQADATF_DDONE|VQADATF_LDONE)) { + + /* Load a frame */ + if (!(vqabuf->Flags & VQADATF_LDONE)) { + if ((rc = VQA_LoadFrame(vqa)) == 0) { + vqabuf->LoadedFrames++; + } + else { + if ((rc != VQAERR_NOBUFFER) && (rc != VQAERR_SLEEPING)) { + vqabuf->Flags |= VQADATF_LDONE; + rc = 0; + } + } + }else{ + VQAMovieDone++; + } + + + /* Draw a frame */ + if ((config->DrawFlags & VQACFGF_NODRAW) == 0) { + if ((rc = (*(vqabuf->Draw_Frame))(vqa)) == 0) { + vqabuf->DrawnFrames++; + rc = vqabuf->Drawer.LastFrameNum; + + #ifdef WIN32never + curframe = drawer->CurFrame; + pal = curframe->Palette; + palsize = curframe->PaletteSize; + slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0; + if ((curframe->Flags & VQAFRMF_PALETTE) + || (drawer->Flags & VQADRWF_SETPAL)) { + setpalette = TRUE; + }else{ + setpalette = FALSE; + } + #endif + + if (User_Update(vqa)) { + vqabuf->Flags |= (VQADATF_DDONE|VQADATF_LDONE); + } + #ifdef WIN32never + /* + ** Set the palette if neccessary + */ + if (setpalette) { + SetPalette(pal, palsize, slowpal); + curframe->Flags &= ~VQAFRMF_PALETTE; + drawer->Flags &= ~VQADRWF_SETPAL; + } + #endif //WIN32 + + }else { + if (rc==VQAERR_EOF) break; + if ((vqabuf->Flags & VQADATF_LDONE) && (rc == VQAERR_NOBUFFER)) { + vqabuf->Flags |= VQADATF_DDONE; + } + } + } else { + vqabuf->Flags |= VQADATF_DDONE; + drawer->CurFrame->Flags = 0L; + drawer->CurFrame = drawer->CurFrame->Next; + } + + /* Update Mono output */ + #if(VQAMONO_ON) + if (config->OptionFlags & VQAOPTF_MONO) { + VQA_UpdateMono((VQAHandleP *)vqa); + } + #endif + + if (mode == VQAMODE_WALK) { + break; + } + #if(VQASTANDALONE) + else { + + /* Do single-stepping check. */ + if (config->OptionFlags & VQAOPTF_STEP) { + while ((key = Check_Key()) == 0); + Get_Key(); + + /* Escape key still quits. */ + if (key == 27) { + break; + } + } + + /* Check for ESC */ + if ((key = Check_Key()) != 0) { + mode = VQAMODE_STOP; + break; + } + } + #endif + } + break; + } + + /* If the movie is finished or we are requested to stop then shutdown. */ + if (((vqabuf->Flags & (VQADATF_DDONE|VQADATF_LDONE)) + == (VQADATF_DDONE|VQADATF_LDONE)) || (mode == VQAMODE_STOP)) { + + /* Record the end time; must be done before stopping audio, since we're + * getting the elapsed time from the audio DMA position. + */ + vqabuf->EndTime = VQA_GetTime((VQAHandleP *)vqa); + + /* Movie is finished. */ + rc = VQAERR_EOF; + } + + /* Stop audio, if it's playing. */ + #if(VQAAUDIO_ON) + if (vqabuf->Audio.Flags & VQAAUDF_ISPLAYING) { + VQA_StopAudio((VQAHandleP *)vqa); + } + #endif + + +#ifdef WIN32 + /* + ** Restore the process priority level + */ + SetPriorityClass (GetCurrentProcess() , process_priority); +#endif //WIN32 + + return (rc); +} + + +/**************************************************************************** +* +* NAME +* VQA_SetStop - Set the frame the player should stop on. +* +* SYNOPSIS +* OldStop = VQA_SetStop(VQA, Frame) +* +* long = VQA_SetStop(VQAHandle *, long); +* +* FUNCTION +* Set the frame that the player should stop on. This function will only +* work on movies that are already open. +* +* INPUTS +* VQA - VQAHandle of movie to set the stop frame for. +* Frame - Frame number to stop on. +* +* RESULT +* OldStop - Previous stop frame. (-1 = invalid stop frame) +* +****************************************************************************/ + +long VQA_SetStop(VQAHandle *vqa, long stop) +{ + long oldstop = -1; + VQAHeader *header; + + /* Get a local pointer to the header. */ + header = &((VQAHandleP *)vqa)->Header; + + if ((stop > 0) && (header->Frames >= stop)) { + oldstop = header->Frames; + header->Frames = stop; + } + + return (oldstop); +} + + +/**************************************************************************** +* +* NAME +* VQA_GetInfo - Get VQA movie information. +* +* SYNOPSIS +* VQA_GetInfo(VQA, Info) +* +* void VQA_GetInfo(VQAHandle *, VQAInfo *); +* +* FUNCTION +* Retrieve information about the opened movie. +* +* INPUTS +* VQA - Pointer to VQAHandle of opened movie. +* Info - Pointer to VQAInfo structure to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetInfo(VQAHandle *vqa, VQAInfo *info) +{ + VQAHeader *header; + + /* Dereference header structure. */ + header = &((VQAHandleP *)vqa)->Header; + + info->NumFrames = header->Frames; + info->ImageHeight = header->ImageHeight; + info->ImageWidth = header->ImageWidth; + info->ImageBuf = ((VQAHandleP *)vqa)->VQABuf->Drawer.ImageBuf; +} + + +/**************************************************************************** +* +* NAME +* VQA_GetStats - Get VQA movie statistics. +* +* SYNOPSIS +* VQA_GetStats(VQA, Stats) +* +* void VQA_GetStats(VQAHandle *, VQAStatistics *); +* +* FUNCTION +* Retrieve the statistics for the VQA movie. +* +* INPUTS +* VQA - Handle of VQA movie to get statistics for. +* Stats - Pointer to VQAStatistics to fill. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VQA_GetStats(VQAHandle *vqa, VQAStatistics *stats) +{ + VQAData *vqabuf; + + /* Dereference VQAData structure from VQAHandle */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + stats->MemUsed = vqabuf->MemUsed; + stats->StartTime = vqabuf->StartTime; + stats->EndTime = vqabuf->EndTime; + stats->FramesLoaded = vqabuf->LoadedFrames; + stats->FramesDrawn = vqabuf->DrawnFrames; + stats->FramesSkipped = vqabuf->Drawer.NumSkipped; + stats->MaxFrameSize = vqabuf->Loader.MaxFrameSize; + + #if(VQAAUDIO_ON) + stats->SamplesPlayed = vqabuf->Audio.SamplesPlayed; + #else + stats->SamplesPlayed = 0; + #endif +} + + +/**************************************************************************** +* +* NAME +* VQA_Version - Get VQA library version number. +* +* SYNOPSIS +* Version = VQA_Version() +* +* char *VQA_Version(void); +* +* FUNCTION +* Return the version of the VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* Version - Pointer to version number string. +* +****************************************************************************/ + +char *VQA_Version(void) +{ + return(VQA_VERSION); +} + + +/**************************************************************************** +* +* NAME +* VQA_IDString - Get the VQA player library's ID string. +* +* SYNOPSIS +* IDString = VQA_IDString() +* +* char *VQA_IDString(void); +* +* FUNCTION +* Return the ID string of this VQA player library. +* +* INPUTS +* NONE +* +* RESULT +* IDString - Pointer to ID string. +* +****************************************************************************/ + +char *VQA_IDString(void) +{ + return (VQA_IDSTRING); +} + + +/**************************************************************************** +* +* NAME +* User_Update - Page flip routine called by the task interrupt. +* +* SYNOPSIS +* User_Update(VQA) +* +* long User_Update(VQAHandle *); +* +* FUNCTION +* +* INPUTS +* VQA - Handle of VQA movie. +* +* RESULT +* NONE +* +****************************************************************************/ + +long User_Update(VQAHandle *vqa) +{ + VQAData *vqabuf; + long rc = 0; + + /* Dereference data members for quicker access. */ + vqabuf = ((VQAHandleP *)vqa)->VQABuf; + + if (vqabuf->Flags & VQADATF_UPDATE) { + + /* Invoke the page flip routine */ + rc = (*(vqabuf->Page_Flip))(vqa); + + /* Update data for mono output */ + vqabuf->Flipper.LastFrameNum = vqabuf->Flipper.CurFrame->FrameNum; + + /* Mark the frame as loadable */ + vqabuf->Flipper.CurFrame->Flags = 0L; + vqabuf->Flags &= (~VQADATF_UPDATE); + } + + return (rc); +} + +void VQA_Dummy(void) +{ + Set_Font(NULL); +} diff --git a/WINVQ/VQA32/TASM32.CFG b/WINVQ/VQA32/TASM32.CFG new file mode 100644 index 0000000..da37b1d --- /dev/null +++ b/WINVQ/VQA32/TASM32.CFG @@ -0,0 +1,10 @@ +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE\VQM32 +/zi +/dPHARLAP_TNT=1 diff --git a/WINVQ/VQA32/TLIB.CFG b/WINVQ/VQA32/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/WINVQ/VQA32/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/WINVQ/VQA32/TLINK32.CFG b/WINVQ/VQA32/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/WINVQ/VQA32/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/WINVQ/VQA32/UNVQ.H b/WINVQ/VQA32/UNVQ.H new file mode 100644 index 0000000..0279898 --- /dev/null +++ b/WINVQ/VQA32/UNVQ.H @@ -0,0 +1,124 @@ +/* +** 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 . +*/ + +#ifndef VQAUNVQ_H +#define VQAUNVQ_H +/**************************************************************************** +* +* 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 +* VQAPlay32 library. (32-Bit protected mode) +* +* FILE +* unvq.h +* +* DESCRIPTION +* VQ frame decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +#ifdef PHARLAP_TNT +#include +#endif + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* unvqbuff.asm */ +#ifndef PHARLAP_TNT +void __cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +void __cdecl UnVQ_4x2_Woofer(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long bufwidth); + +/* unvqvesa.asm */ +void __cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, unsigned char *palette, + unsigned long grains_per_win,unsigned long dummy1,unsigned long dummy2); + +#else /* PHARLAP_TNT */ + +void __cdecl UnVQ_2x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_2x3(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_4x2(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +void __cdecl UnVQ_4x4(unsigned char *codebook, unsigned char *pointers, + FARPTR buffer, unsigned long blocksperrow, unsigned long numrows, + unsigned long bufwidth); + +/* unvqvesa.asm */ +void __cdecl UnVQ_4x2_VESA320_32K(unsigned char *codebook, + unsigned char *pointers, FARPTR palette, unsigned long grains_per_win, + unsigned long dummy1, unsigned long dummy2); + +#endif /* PHARLAP_TNT */ + +/* unvqxmde.asm */ +void __cdecl UnVQ_4x2_Xmode(unsigned char *codebook, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void __cdecl UnVQ_4x2_XmodeCB(unsigned char *cbdummy, unsigned char *pointers, + unsigned char *buffer, unsigned long blocksperrow, + unsigned long numrows, unsigned long dummy); + +void __cdecl Upload_4x2CB(unsigned char *codebook, unsigned long numentries); +void __cdecl XlatePointers(unsigned char *pointers, unsigned long numpointers); + +#ifdef __cplusplus +} +#endif + +#endif /* VQAUNVQ_H */ diff --git a/WINVQ/VQA32/UNVQBUFF.ASM b/WINVQ/VQA32/UNVQBUFF.ASM new file mode 100644 index 0000000..7bdea8c --- /dev/null +++ b/WINVQ/VQA32/UNVQBUFF.ASM @@ -0,0 +1,1153 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQBuff.asm +;* +;* DESCRIPTION +;* Buffered VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* Feburary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + + IF VQABLOCK_2X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x2 - Draw 2x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x2(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x2:NEAR + PROC UnVQ_2x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x2 + ENDIF ;VQABLOCK_2X2 + + + IF VQABLOCK_2X3 +;**************************************************************************** +;* +;* NAME +;* UnVQ_2x3 - Draw 2x3 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_2x3(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_2x3(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_2x3:NEAR + PROC UnVQ_2x3 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + add eax,[bufwidth] + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov dx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + ENDIF + + shr eax,16 + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],dx + ELSE + mov [edi+ebx],dx + ENDIF + + add edi,2 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,2 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],ax ;Write 1st row to dest + mov [es:edi+ebx],ax ;Write 2nd row to dest + ELSE + mov [edi],ax ;Write 1st row to dest + mov [edi+ebx],ax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],ax ;Write 2rd row to dest + ELSE + mov [edi+ebx],ax ;Write 2rd row to dest + ENDIF + + add edi,2 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_2x3 + ENDIF ;VQABLOCK_2X3 + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2 - Draw 4x2 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x2(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x2(unsigned char *, unsigned char *, unsigned char *, +;* long, long, long); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;;; GLOBAL C UnVQ_4x2:NEAR +;;; PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi +;;; +;;; ARG codebook:NEAR PTR +;;; ARG pointers:NEAR PTR +;;; +;;; IF PHARLAP_TNT +;;; ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; ARG buffer:NEAR PTR +;;; ENDIF +;;; +;;; ARG blocksperrow:DWORD +;;; ARG numrows:DWORD +;;; ARG bufwidth:DWORD +;;; +;;; LOCAL data_end:DWORD +;;; LOCAL cb_offset:DWORD +;;; LOCAL edi_startval:DWORD +;;; LOCAL rowoffset:DWORD +;;; +;;;;---------------------------------------------------------------------------- +;;;; Initialize +;;;;---------------------------------------------------------------------------- +;;; +;;; mov eax,[codebook] ;Adjust the codebook address so +;;; sub eax,4 ; that the pointer offsets will +;;; mov [cb_offset],eax ; point directly at the codeword. +;;; +;;; mov eax,[bufwidth] ;Compute the offset to the next +;;; shl eax,1 ; row of blocks. +;;; mov [rowoffset],eax +;;; +;;; mov esi,[pointers] +;;; mov eax,[numrows] ;Compute the end address of the +;;; mul [blocksperrow] ; pointer data. +;;; shl eax,1 +;;; add eax,esi +;;; mov [data_end],eax +;;; +;;; IF PHARLAP_TNT +;;; push es +;;; les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR +;;; ELSE +;;; mov edi,[buffer] +;;; ENDIF +;;; +;;; mov [edi_startval],edi +;;; +;;;;---------------------------------------------------------------------------- +;;;; Drawing loop +;;;;---------------------------------------------------------------------------- +;;; +;;;??Start_row: +;;; mov ecx,[blocksperrow] ;Number of blocks in a line +;;; +;;;??Not_finished_a_line: +;;; sub ebx,ebx +;;; mov bx,[WORD PTR esi] ;Get the codebook pointer value +;;; add esi,2 ; then advance to the next one. +;;; +;;; or bx,bx ;Is it a one color block? +;;; js short ??One_color +;;; +;;;; Draw multi-color block +;;; +;;; add ebx,[cb_offset] ;Codeword address +;;; mov eax,[ebx] ;Read 1st row of codeword +;;; mov edx,[ebx+4] ;Read 2nd row of codeword +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],edx ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],edx ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;; Draw 1-color block +;;; +;;;??One_color: +;;; cmp bx,SKIP_PTR ;Is this a skip block? +;;; jne ??Draw_One_Color +;;; +;;; add edi,4 ;Move to next dest block position +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; jmp ??Next_row +;;; +;;;??Draw_One_Color: +;;; not bx ;NOT pointer value to get color +;;; mov bh,bl ;Duplicate color through the +;;; mov ax,bx ; entire dword register. +;;; rol eax,16 +;;; mov ax,bx +;;; mov ebx,[bufwidth] +;;; +;;; IF PHARLAP_TNT +;;; mov [es:edi],eax ;Write 1st row to dest +;;; mov [es:edi+ebx],eax ;Write 2nd row to dest +;;; ELSE +;;; mov [edi],eax ;Write 1st row to dest +;;; mov [edi+ebx],eax ;Write 2nd row to dest +;;; ENDIF +;;; +;;; add edi,4 ;Next dest block positionw +;;; dec ecx ;More blocks for this row? +;;; jnz short ??Not_finished_a_line +;;; +;;;; Advance to the next destination row of blocks. +;;; +;;;??Next_row: +;;; mov edi,[edi_startval] +;;; add edi,[rowoffset] +;;; mov [edi_startval],edi +;;; +;;; cmp esi,[data_end] ;Have we reached the end of the +;;; jnb short ??End_of_data ; pointers buffer? +;;; jmp ??Start_row +;;; +;;;??End_of_data: +;;; IF PHARLAP_TNT +;;; pop es +;;; ENDIF +;;; +;;; ret +;;; +;;; ENDP UnVQ_4x2 +;;; ENDIF ;VQABLOCK_4X2 + + GLOBAL C UnVQ_4x2:NEAR + PROC UnVQ_4x2 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + ARG buffer:NEAR PTR + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so +; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax + add eax,esi + mov [data_end],eax + + mov edi,[buffer] + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx + mov bl,[esi] + mov bh,[esi + eax] ;Get the codebook pointer value + inc esi ; then advance to the next one. + + cmp bh,00Fh ;Is it a one color block? + je short ??One_color + +; Draw multi-color block + + shl ebx,3 + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],edx ;Write 2nd row to dest + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: +; cmp bx,SKIP_PTR ;Is this a skip block? +; jne ??Draw_One_Color +; +; add edi,4 ;Move to next dest block position +; dec ecx ;More blocks for this row? +; jnz short ??Not_finished_a_line +; jmp ??Next_row + +??Draw_One_Color: +; not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + ret + + ENDP UnVQ_4x2 + ENDIF ;VQABLOCK_4X2 + + + IF VQABLOCK_4X4 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x4 - Draw 4x4 block VQ frame to a buffer. +;* +;* SYNOPSIS +;* UnVQ_4x4(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) +;* +;* void UnVQ_4x4(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image into the specified buffer from the +;* pointers and codebook provided. This routine has been optimized for +;* a 320x200 image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* BufWidth - Width of destination buffer in pixels. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x4:NEAR + PROC UnVQ_4x4 C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + + ;---------------------------------------------------------------------------- + ; Initialize + ;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,2 ; row of blocks + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + + ;---------------------------------------------------------------------------- + ; Drawing loop + ;---------------------------------------------------------------------------- + + ??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + + ??Not_finished_a_line: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color + + ; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + ENDIF + + mov eax,ebx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + mov ebx,eax + mov eax,[ebx+8] + mov edx,[ebx+12] + mov ebx,[bufwidth] + shl ebx,1 + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi+ebx],edx ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],eax ;Write 1st row to dest + mov [edi+ebx],eax ;Write 2nd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 3rd row to dest + ELSE + mov [edi+ebx],eax ;Write 3rd row to dest + ENDIF + + add ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi+ebx],eax ;Write 4th row to dest + ELSE + mov [edi+ebx],eax ;Write 4th row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + ??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x4 + ENDIF ;VQABLOCK_4X4 + + + + IF VQABLOCK_WOOFER + IF VQABLOCK_4X2 + ;**************************************************************************** + ;* + ;* NAME + ;* UnVQ_4x2_Woofer - Draw 4x2 block VQ frame to a buffer. + ;* + ;* SYNOPSIS + ;* UnVQ_4x2_Woofer(Codebook, Pointers, Buffer, BPR, Rows, BufWidth) + ;* + ;* void UnVQ_4x2_Woofer(unsigned char *, unsigned char *, + ;* unsigned char *, long, long, long); + ;* + ;* FUNCTION + ;* This function draws an image into the specified buffer from the + ;* pointers and codebook provided. This routine has been optimized for + ;* a 320x200 image. + ;* + ;* INPUTS + ;* Codebook - Pointer to codebook used to draw image. + ;* Pointers - Pointer to vector pointer data. + ;* Buffer - Pointer to buffer to draw image into. + ;* BPR - Number of blocks per row. + ;* Rows - Number of rows. + ;* BufWidth - Width of destination buffer in pixels. + ;* + ;* RESULT + ;* NONE + ;* + ;**************************************************************************** + + GLOBAL C UnVQ_4x2_Woofer:NEAR + PROC UnVQ_4x2_Woofer C NEAR USES ebx ecx edx esi edi + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + LOCAL entries:DWORD + + ;---------------------------------------------------------------------------- + ; Initialize + ;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + ; sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + mov [entries],eax + ; shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + + ;---------------------------------------------------------------------------- + ; Drawing loop + ;---------------------------------------------------------------------------- + + ??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + + ??Not_finished_a_line: + mov eax,[entries] + xor ebx,ebx + ; mov bx,[WORD PTR esi] ;Get the codebook pointer value + ; add esi,2 ; then advance to the next one. + mov bl,[esi] + mov bh,[esi + eax] + inc esi + + ; or bx,bx ;Is it a one color block? + ; js short ??One_color + + cmp bh,00Fh + je short ??One_color + + ; Draw multi-color block + + shl ebx,3 + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al ;Write 1st row to dest + shr eax,16 + mov [es:edi+2],al + mov [es:edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [es:edi+ebx+3],dh + ELSE + mov [edi],al ;Write 1st row to dest + shr eax,16 + mov [edi+2],al + mov [edi+ebx+1],dh ;Write 1st row to dest + shr edx,16 + mov [edi+ebx+3],dh + ENDIF + + add edi,4 ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ; Draw 1-color block + + ??One_color: + ; cmp bx,SKIP_PTR ;Is this a skip block? + ; jne ??Draw_One_Color + + ; add edi,4 ;Move to next dest block position + ; dec ecx ;More blocks for this row? + ; jnz short ??Not_finished_a_line + ; jmp ??Next_row + + ??Draw_One_Color: + ; not bx ;NOT pointer value to get color + mov al,bl + mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],al + mov [es:edi+2],al + mov [es:edi+ebx+1],al ;Write 2nd row to dest + mov [es:edi+ebx+3],al ;Write 2nd row to dest + ELSE + mov [edi],al + mov [edi+2],al + mov [edi+ebx+1],al ;Write 2nd row to dest + mov [edi+ebx+3],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz ??Not_finished_a_line + + ; Advance to the next destination row of blocks. + + ??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + + ??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP UnVQ_4x2_Woofer + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQABLOCK_WOOFER + + END + diff --git a/WINVQ/VQA32/UNVQVESA.ASM b/WINVQ/VQA32/UNVQVESA.ASM new file mode 100644 index 0000000..c25488a --- /dev/null +++ b/WINVQ/VQA32/UNVQVESA.ASM @@ -0,0 +1,380 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay32 library. +;* +;* FILE +;* unvqvesa.asm +;* +;* DESCRIPTION +;* VESA VQ decompress/draw routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + +SKIP_PTR EQU 8000h + + IF VQAVESA_ON + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* +;* SYNOPSIS +;* UnVQ_4x2_VESA320_32K(Codebook, Pointers, Palette, GrainPerWin) +;* +;* void UnVQ_4x2_VESA320_32K(char *, char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Codebook - Pointer to codebook. +;* Pointers - Pointer to vector pointer data to unvq. +;* Palette - Pointer to 15-bit palette. +;* GrainPerWin - Granularity units for 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +;--------------------------------------------------------------------------- +; SET_2_PIXELS: +; - Loads 2 bytes from codebook, expands them into words in eax +; - Sets them on the screen +; cb_index: offset into codebook of start of 2 bytes to load +; di_index: offset from current di to draw 2 pixels +; BX: offset into codebook of this block +; Uses: EAX, DX, SI! +;--------------------------------------------------------------------------- + + MACRO SET_2_PIXELS cb_index,edi_index + xor edx,edx + mov dl,[BYTE PTR ebx+1+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + shl eax,16 + xor edx,edx + mov dl,[BYTE PTR ebx+cb_index] + shl edx,1 + add edx,[palette] + mov esi,edx + mov ax,[WORD PTR esi] + + IF PHARLAP_TNT + mov [DWORD PTR es:edi+edi_index],eax + ELSE + mov [DWORD PTR edi+edi_index],eax + ENDIF + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROWS macro: +; Draws 'numrows' rows of 'blocksperrow' blocks each +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_ROWS numrows,blocksperrow + LOCAL ??Start_row + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Next_row + LOCAL ??Done + + mov [rowcount],numrows ; initialize row counter + + ;---------------------------------------- Start a new row: +??Start_row: + mov ecx,blocksperrow ; # blocks to fill a line + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS 0,0 + SET_2_PIXELS 2,4 + SET_2_PIXELS 4,640 + SET_2_PIXELS 6,644 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz ??Not_finished_a_line + jmp ??Next_row +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + mov [DWORD PTR es:edi+640],eax ; set 2 pixels + mov [DWORD PTR es:edi+644],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + mov [DWORD PTR edi+640],eax ; set 2 pixels + mov [DWORD PTR edi+644],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line + + ;---------------------------------------- Next block row: +??Next_row: + add edi,640 + ; + ;............ see if we're past the end of the ptr data ............ + ; + dec [rowcount] + jnz ??Start_row +??Done: + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_PART_ROW macro: +; Draws top or bottom half of blocks on a row +; 'numblocks' = # blocks to draw +; 'cb_add' = amt to add to codebook (0=top half, 4=bottom half) +;--------------------------------------------------------------------------- + + MACRO DRAW_BLOCK_PART_ROW numblocks,cb_add + LOCAL ??Not_finished_a_line + LOCAL ??One_color + LOCAL ??Draw_One_Color + LOCAL ??Done + + mov ecx,numblocks + + ;---------------------------------------- Start a new block: +??Not_finished_a_line: + ;........................................ Get next pointer word: + xor ebx,ebx + mov bx,[WORD PTR esi] ; BX = pointer word + add esi,2 + + ;........................................ Check for a 1-color block: + or bx,bx ; see if high bit is set + js short ??One_color + + ;---------------------------------------- Multi-color block: + mov [esi_save],esi ; save current SI + add ebx,[cb_offset] ; get codebook offset + SET_2_PIXELS cb_add,0 + SET_2_PIXELS cb_add+2,4 + add edi,8 ; next block position + mov esi,[esi_save] ; get current SI + + ;........................................ Check block count + dec ecx ; decrement block count + jnz short ??Not_finished_a_line + jmp ??Done + + ;---------------------------------------- 1-color block: +??One_color: + ;................... skip ptr value SKIP_PTR ....................... + cmp bx,SKIP_PTR + jne ??Draw_One_Color + add edi,8 + dec ecx + jnz short ??Not_finished_a_line + jmp ??Done +??Draw_One_Color: + not bx ; get palette index + shl bx,1 ; convert to WORD offset + add ebx,[palette] + mov ax,[WORD PTR ebx] ; get 15-bit palette value + mov dx,ax ; copy it into dx + shl eax,16 + mov ax,dx ; eax = 2 pixels, same color + mov edx,eax ; edx = 2 pixels, same color + + IF PHARLAP_TNT + mov [DWORD PTR es:edi],eax ; set 2 pixels + mov [DWORD PTR es:edi+4],edx ; set 2 pixels + ELSE + mov [DWORD PTR edi],eax ; set 2 pixels + mov [DWORD PTR edi+4],edx ; set 2 pixels + ENDIF + + add edi,8 ; next block position + + ;........................................ Check block count + dec ecx ; decrement block count + jnz ??Not_finished_a_line +??Done: + ENDM + +;=========================================================================== +; The actual procedure: +;=========================================================================== + + GLOBAL C UnVQ_4x2_VESA320_32K:NEAR + PROC UnVQ_4x2_VESA320_32K C NEAR + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG pal:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG palette:NEAR PTR + ENDIF + + ARG grains_per_win:DWORD + ARG dummy1:DWORD + ARG dummy2:DWORD + + LOCAL rowcount:DWORD ; # rows drawn + LOCAL esi_save:DWORD + LOCAL cb_offset:DWORD + + IF PHARLAP_TNT + LOCAL palette:NEAR PTR + ENDIF + + ;------------------------------------------------------------------- + ; Save registers + ;------------------------------------------------------------------- + pushad + + ;------------------------------------------------------------------- + ; Set GS:[cb_offset] to codebook + ;------------------------------------------------------------------- + mov eax,[codebook] + sub eax,4 + mov [cb_offset],eax + mov esi,[pointers] + + ;------------------------------------------------------------------- + ; Set ES:DI to screen + ;------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + les edi,[FWORD pal] + mov [palette],edi + mov eax,01Ch + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + ;------------------------------------------------------------------- + ; Do Bank 0: + ; - 102 full scanlines (51 rows of blocks) + ; - 128 pixels of the top half of blocks (32 top-half blocks) + ;------------------------------------------------------------------- + SET_WINDOW 0 + DRAW_BLOCK_ROWS 51,80 + DRAW_BLOCK_PART_ROW 32,0 ; do top half + + ;------------------------------------------------------------------- + ; Do Bank 1: + ; - 128 pixels of the bottom half of the previous 32 blocks + ; - 192 pixels of full blocks (1 row of 48 blocks) + ; - 96 full scanlines (48 rows of blocks) + ;------------------------------------------------------------------- + SET_WINDOW [grains_per_win] + sub esi,64 ; subtract word size of last 32 blks + mov edi,384 + DRAW_BLOCK_PART_ROW 32,4 ; do bottom half + mov edi,0 + DRAW_BLOCK_ROWS 1,48 ; draw one row of 48 full blocks + DRAW_BLOCK_ROWS 48,80 ; draw 48 full block rows + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + popad + ret + + ENDP UnVQ_4x2_VESA320_32K + + ENDIF ;VQABLOCK_4X2 + ENDIF ;VQAVESA_ON + + END + diff --git a/WINVQ/VQA32/UNVQXMDE.ASM b/WINVQ/VQA32/UNVQXMDE.ASM new file mode 100644 index 0000000..3c641f2 --- /dev/null +++ b/WINVQ/VQA32/UNVQXMDE.ASM @@ -0,0 +1,724 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay library. +;* +;* FILE +;* UnVQxmde.asm +;* +;* DESCRIPTION +;* XMode VQ decompress/draw routines. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* May 18, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* UnVQ_4x2_VESA320_32K - Draw 4x2 VQ frame to VESA320 32k color. +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "vesavid.i" + INCLUDE "vqaplay.i" + CODESEG + + IF VQAXMODE_ON + +SKIP_PTR EQU 8000h +CBOOK_SEG EQU (0B0000h - 270h) + + + IF VQABLOCK_4X2 +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_Xmode - Draw 4x2 VQ frame directly to Xmode. +;* +;* SYNOPSIS +;* UnVQ_4x2_Xmode(Codebook, Pointers, Buffer, BPR, Rows, Dummy) +;* +;* void UnVQ_4x2_Xmode(unsigned char *, unsigned char *, unsigned char *, +;* unsigned short, unsigned short, unsigned short); +;* +;* FUNCTION +;* This function draws an image to the Xmode display from the pointers +;* and codebook provided. This routine has been optimized for a 320x200 +;* image. +;* +;* INPUTS +;* Codebook - Pointer to codebook used to draw image. +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to buffer to draw image into. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* Dummy - Not used (prototype placeholder) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_Xmode:NEAR + PROC UnVQ_4x2_Xmode C NEAR USES + + ARG codebook:NEAR PTR + ARG pointers:NEAR PTR + + IF PHARLAP_TNT + ARG buffer:QWORD ;KLUDGE - bcc32 pads FARPTR + ELSE + ARG buffer:NEAR PTR + ENDIF + + ARG blocksperrow:DWORD + ARG numrows:DWORD + ARG bufwidth:DWORD + + LOCAL data_end:DWORD + LOCAL cb_offset:DWORD + LOCAL edi_startval:DWORD + LOCAL rowoffset:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + mov eax,[codebook] ;Adjust the codebook address so + sub eax,4 ; that the pointer offsets will + mov [cb_offset],eax ; point directly at the codeword. + + mov eax,[bufwidth] ;Compute the offset to the next + shl eax,1 ; row of blocks. + mov [rowoffset],eax + + mov esi,[pointers] + mov eax,[numrows] ;Compute the end address of the + mul [blocksperrow] ; pointer data. + shl eax,1 + add eax,esi + mov [data_end],eax + + IF PHARLAP_TNT + push es + les edi,[FWORD buffer] ;KLUDGE - bcc32 pads FARPTR + ELSE + mov edi,[buffer] + ENDIF + + mov [edi_startval],edi + +;---------------------------------------------------------------------------- +; Drawing loop +;---------------------------------------------------------------------------- + + SET_PLANE XPLANE_1 + +??Start_row: + mov ecx,[blocksperrow] ;Number of blocks in a line + +??Not_finished_a_line1: + sub ebx,ebx + mov bx,[WORD PTR esi] ;Get the codebook pointer value + add esi,2 ; then advance to the next one. + + or bx,bx ;Is it a one color block? + js short ??One_color1 + +; Draw multi-color block + + add ebx,[cb_offset] ;Codeword address + mov eax,[ebx] ;Read 1st row of codeword + mov edx,[ebx+4] ;Read 2nd row of codeword +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],edx ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],dl ;Write 2nd row to dest + ENDIF + +; add edi,4 ;Next dest block position + inc edi ;Next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +; Draw 1-color block + +??One_color1: + cmp bx,SKIP_PTR ;Is this a skip block? + jne ??Draw_One_Color + + add edi,4 ;Move to next dest block position + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + jmp ??Next_row + +??Draw_One_Color: + not bx ;NOT pointer value to get color + mov bh,bl ;Duplicate color through the + mov ax,bx ; entire dword register. + rol eax,16 + mov ax,bx +; mov ebx,[bufwidth] + + IF PHARLAP_TNT + mov [es:edi],eax ;Write 1st row to dest + mov [es:edi+ebx],eax ;Write 2nd row to dest + ELSE + mov [edi],al ;Write 1st row to dest + mov [edi+80],al ;Write 2nd row to dest + ENDIF + + add edi,4 ;Next dest block positionw + dec ecx ;More blocks for this row? + jnz short ??Not_finished_a_line + +; Advance to the next destination row of blocks. + +??Next_row: + mov edi,[edi_startval] + add edi,[rowoffset] + mov [edi_startval],edi + + cmp esi,[data_end] ;Have we reached the end of the + jnb short ??End_of_data ; pointers buffer? + jmp ??Start_row + +??End_of_data: + IF PHARLAP_TNT + pop es + ENDIF + + ret + ENDP UnVQ_4x2_Xmode + + +;**************************************************************************** +;* +;* NAME +;* UnVQ_4x2_XmodeCB - Draw 4x2 VQ frame to Xmode from VRAM codebook. +;* +;* SYNOPSIS +;* UnVQ_4x2_XmodeCB(Dummy, Pointers, Buffer, BPR, Rows) +;* +;* void UnVQ_4x2_XmodeCB(unsigned char *, unsigned char *, +;* unsigned char *, unsigned short, +;* unsigned short); +;* +;* FUNCTION +;* This routine copies codebook entries from video RAM to video RAM. +;* The procedure for Write Mode 1 is: +;* +;* - Perform a CPU read at the address of the 4-byte codebook entry; +;* this will load each byte at that address from all 4 bitplanes +;* into the VGA's internal latches. +;* +;* - Perform a CPU write at the destination address, with the BitMask +;* register set to 0 (this tells the VGA hardware to only use the +;* data stored in the latches to write with), and the Map Mask +;* register set to 0Fh (all bitplanes enabled). +;* +;* Optimized for 320x200. +;* The codebook must have been downloaded to video RAM before this +;* routine is called. This routine assumes the multicolor block pointers +;* have been pre-divided by 4, and have a total of 512 added to them, so +;* the pointer is an exact offset into the codebook. +;* +;* INPUTS +;* Dummy - Not used (prototype placeholder) +;* Pointers - Pointer to vector pointer data. +;* Buffer - Pointer to Xmode buffer. +;* BPR - Number of blocks per row. +;* Rows - Number of rows. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C UnVQ_4x2_XmodeCB:NEAR + PROC UnVQ_4x2_XmodeCB C NEAR USES + + ARG cbdummy:FAR PTR + ARG pointers:FAR PTR + ARG buffer:FAR PTR + ARG blocksperrow:WORD + ARG numrows:WORD + +; ;------------------------------------------------------------------- +; ; Local variables: +; ;------------------------------------------------------------------- +; LOCAL di_startval:WORD ; init value for DI, for new row +; +; ;=================================================================== +; ; Initialization +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Save our registers +; ;------------------------------------------------------------------- +; push ds +; push es +; push fs +; pushad +; +; ;------------------------------------------------------------------- +; ; Save codebook's segment in DS +; ;------------------------------------------------------------------- +; mov ax,CBOOK_SEG +; mov ds,ax +; +; ;------------------------------------------------------------------- +; ; Load pointers into FS:BX +; ;------------------------------------------------------------------- +; les di,[pointers] +; mov ax,es +; mov fs,ax +; mov bx,di +; +; ;------------------------------------------------------------------- +; ; Load screen address into ES:DI +; ;------------------------------------------------------------------- +; les di, [buffer] ; point ES:DI to dest +; mov [di_startval],di ; store it +; +; ;------------------------------------------------------------------- +; ; Initialize VGA registers: +; ; - Enable all bitplanes for writing +; ; - Set the BitMask register to 0, so only the data from the +; ; VGA latches is written into the bitplanes +; ;------------------------------------------------------------------- +; SET_PLANE 0fh ; enable all planes for write +; SET_WRITEMODE 1 +; +; ;=================================================================== +; ; The drawing loop: +; ; DS:SI = codebook +; ; ES:DI = drawing buffer +; ; FS:BX = pointers +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Start a new row of drawing +; ;------------------------------------------------------------------- +;??Start_row: +; mov cx,[blocksperrow] ; # blocks to fill a line +; +; ;------------------------------------------------------------------- +; ; Start a new block +; ;------------------------------------------------------------------- +;??Not_finished_a_line: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov si,[WORD PTR fs:bx] ; SI = ptr word (cbook offset) +; add bx,2 ; next ptr word +; ; +; ;................... skip ptr value SKIP_PTR ....................... +; ; +; cmp si,SKIP_PTR +; jne ??Draw_Block +; inc di +; dec cx +; jnz short ??Not_finished_a_line +; jmp ??Next_row +; +; ;------------------------------------------------------------------- +; ; Draw a block via the VGA internal latches: +; ; DS:SI = codebook address +; ; ES:DI = buffer position to draw at +; ; - Load the VGA latches from the 1st 4 codebook bytes +; ; - write 4 pixels with one CPU write +; ; - If this is a one-color block, skip the next codebook read +; ; (Video RAM reads are very slow); otherwise, latch the next 4 +; ; codebook bytes +; ; - write the next 4 pixels +; ;------------------------------------------------------------------- +; ; +; ;..................... draw 1st 4 pixels ........................... +; ; +;??Draw_Block: +; mov al,[ds:si] ; latch 1st 4 cbook bytes +; mov [es:di],al ; write 4 pixels +; ; +; ;.................. check for 1-color block ........................ +; ; +; cmp si,512 ; if 1color blk, don't read +; jb ??One_Color +; ; +; ;..................... draw next 4 pixels .......................... +; ; +; mov al,[ds:si+1] ; latch next 4 cbook bytes +;??One_Color: +; mov [es:di+80],al ; write next 4 pixels +; inc di ; next block position +; ; +; ;...................... check block count .......................... +; ; +; dec cx ; decrement block count +; jnz short ??Not_finished_a_line +; +; ;------------------------------------------------------------------- +; ; Go to the next block row: +; ; - Add (80*2/16) to ES, and set DI to its start-row value +; ; (Incrementing the segment allows us to unpack up to 1MB of data) +; ;------------------------------------------------------------------- +; ; +; ;...................... add (320*2/16) to ES ....................... +; ; +;??Next_row: +; mov ax,es +; add ax,10 ; add 80*2/16 +; mov es,ax +; ; +; ;.................. set DI to its start-row value .................. +; ; +; mov di,[di_startval] +; ; +; ;............ see if we're past the end of the ptr data ............ +; ; +; dec [numrows] +; jg ??Start_row +; +;??End_of_data: +; ;------------------------------------------------------------------- +; ; Restore VGA Write Mode to 0 +; ;------------------------------------------------------------------- +; SET_WRITEMODE 0 +; +; popad +; pop fs +; pop es +; pop ds + ret + + ENDP UnVQ_4x2_XmodeCB + + +;**************************************************************************** +;* +;* NAME +;* Upload_4x2CB - Upload 4x2 codebook into XMode VRAM. +;* +;* SYNOPSIS +;* Upload_4x2CB(Codebook, Entries) +;* +;* void Upload_4x2CB(unsigned char *, unsigned short); +;* +;* FUNCTION +;* This routine copies the given codebook into Xmode VRAM, so that it +;* can be used later for direct video-to-video copies. The address used +;* is the end of video memory minus 02700h (10K). This should be plenty +;* for a 3000-entry codebook; each 4x2 codebook entry will take up 8 +;* 8 bytes, or 2 addresses in XMode (6000 addresses). +;* +;* The routine also creates a 1-color-block table in VRAM, so the 1-color +;* blocks can be generated the same way as the multicolor blocks. +;* +;* XMode 320x200 uses 320x200/4 addresses per page, for a total of 32000 +;* addresses. XMode 320x240 uses 320x240/4 addresses per page, for a +;* total of 38400 addresses. This leaves 27136 addresses unused. +;* +;* INPUTS +;* Codebook - Pointer to codebook to copy. +;* Entries - Number of codebook entries to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Upload_4x2CB:NEAR + PROC Upload_4x2CB C NEAR USES + + ARG codebook:NEAR PTR + ARG numentries:DWORD + +; ;=================================================================== +; ; Generate the 1-color block table by writing each color value from +; ; 0-255 into all 4 bitplanes, at 256 consecutive addresses: +; ;=================================================================== +; SET_PLANE 0fh ; enable all bitplanes for writing +; ;................................................................... +; ; Set ES:DI to destination address, set up CX for the loop +; ;................................................................... +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,0 +; mov cx,256 +; mov ax,0 +;??1_Color_Loop: +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; mov [es:di],al ; write 4 bytes +; inc di ; next 4-byte position +; inc ax ; next color # +; dec cx ; decrement loop counter +; jnz ??1_Color_Loop +; +; ;=================================================================== +; ; Copy the codebook into video RAM, one plane at a time: +; ;=================================================================== +; ;------------------------------------------------------------------- +; ; Copy codebook byte 0 into Plane 1 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_1 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_1: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_1 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 1 Plane 2 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,1 ; use 2nd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_2 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_2: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_2 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 2 Plane 3 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,2 ; use 3rd byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_3 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_3: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_3 +; +; ;------------------------------------------------------------------- +; ; Copy codebook byte 3 Plane 4 +; ;------------------------------------------------------------------- +; ;................................................................... +; ; Set DS:SI to codebook address, ES:DI to screen address +; ; (Codebook is stored at offset 1, so the pointers will point at +; ; exactly the right offset.) +; ;................................................................... +; lds si, [codebook] ; DS:SI = codebook +; mov ax,CBOOK_SEG +; mov es,ax +; mov di,512 +; add si,3 ; use 4th byte value +; +; ;................................................................... +; ; Set up the loop +; ;................................................................... +; SET_PLANE XPLANE_4 +; mov cx,[numentries] ; set loop counter +; shl cx,1 ; do 2 DWORDS per cbook entry +; +; ;................................................................... +; ; Loop through codebook entries +; ;................................................................... +;??CB_Loop_4: +; mov al,[ds:si] +; mov [es:di],al +; add si,4 +; inc di +; dec cx +; jnz ??CB_Loop_4 +; + ret + + ENDP Upload_4x2CB + + ENDIF ;VQABLOCK_4X2 + +;**************************************************************************** +;* +;* NAME +;* XlatePointers - Translate pointer to optimal XMode format. +;* +;* SYNOPSIS +;* XlatePointers(Pointers, Entries) +;* +;* void XlatePointers(unsigned char *, unsigned short); +;* +;* FUNCTION +;* +;* INPUTS +;* Pointers - Pointer to vector pointers to translate. +;* Entries - Number of pointer entries. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C XlatePointers:NEAR + PROC XlatePointers C NEAR USES + + ARG pointers:NEAR PTR + ARG numpointers:DWORD + +; ;------------------------------------------------------------------- +; ; Load pointers into DS:SI +; ;------------------------------------------------------------------- +; lds si,[pointers] +; +; mov cx,[numpointers] ; init to # pointers on scrn +; +;??Process_pointer: +; ; +; ;..................... get next pointer word ....................... +; ; +; mov ax,[WORD PTR ds:si] ; SI = ptr word (cbook offset) +; ; +; ;.................... check for a 1-color block .................... +; ; +; or ax,ax ; check to see if high bit set +; js short ??One_color +; ; +; ;....................... multi-color pointer ....................... +; ; +; sub ax,4 ; subtract 4 +; shr ax,2 ; divide by 4 +; add ax,512 ; add 512 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; jmp ??Done +; +;??One_color: +; ; +; ;......................... 1-color pointer ......................... +; ; +; not ax ; get actual color value +; shl ax,1 ; multiply by 2 +; mov [WORD PTR ds:si],ax ; save new value +; add si,2 ; next ptr word +; ; +; ;....................... see if we're done ......................... +; ; +; dec cx +; jnz ??Process_pointer +; +;??Done: + ret + + ENDP XlatePointers + + ENDIF ;VQAXMODE_ON + END + + + + + diff --git a/WINVQ/VQA32/VERTAG.CPP b/WINVQ/VQA32/VERTAG.CPP new file mode 100644 index 0000000..85fbbe9 --- /dev/null +++ b/WINVQ/VQA32/VERTAG.CPP @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vertag.c +* +* DESCRIPTION +* Version Tag +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* February 23, 1995 +* +****************************************************************************/ + +#include "vqaplayp.h" + +#ifdef __WATCOMC__ +#define DEVNAME "Watcom/4GW" +#else +#define DEVNAME "Borland/TNT" +#endif + +char VerTag[] = {"$VER$" VQA_IDSTRING" "DEVNAME" ("VQA_DATE")"}; +char ReqTag[] = {"$REQ$" VQA_REQUIRES}; + diff --git a/WINVQ/VQA32/VQAFILE.H b/WINVQ/VQA32/VQAFILE.H new file mode 100644 index 0000000..9e14532 --- /dev/null +++ b/WINVQ/VQA32/VQAFILE.H @@ -0,0 +1,197 @@ +/* +** 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 . +*/ + +#ifndef VQAFILE_H +#define VQAFILE_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqafile.h +* +* DESCRIPTION +* VQA file format definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +#include + +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS AND RELATED DEFINES. + *-------------------------------------------------------------------------*/ + +/* VQAHeader: VQA movie description header. (VQHD) + * + * Version - VQA version. + * Flags - Various flags. (See below) + * ImageWidth - Image width in pixels. + * ImageHeight - Image height in pixels. + * BlockWidth - Block width in pixels. + * BlockHeight - Block height in pixels. + * Frames - Total number of frames in the movie. + * FPS - Playback rate (Frame Per Second). + * Groupsize - Frame grouping size (frames per codebook). + * Num1Colors - Number of 1 color colors. + * CBentries - Number of codebook entries. + * Xpos - X position to draw frames. (-1 = Center) + * Ypos - Y position to draw frames. (-1 = Center) + * MaxFramesize - Size of largest frame. + * SampleRate - Sample rate of primary audio stream. + * Channels - Number of channels in primary audio stream. + * BitsPerSample - Sample bit size in primary audio stream. + * FutureUse - Reserved for future expansion. + */ +typedef struct _VQAHeader { + unsigned short Version; + unsigned short Flags; + unsigned short Frames; + unsigned short ImageWidth; + unsigned short ImageHeight; + unsigned char BlockWidth; + unsigned char BlockHeight; + unsigned char FPS; + unsigned char Groupsize; + unsigned short Num1Colors; + unsigned short CBentries; + unsigned short Xpos; + unsigned short Ypos; + unsigned short MaxFramesize; + unsigned short SampleRate; + unsigned char Channels; + unsigned char BitsPerSample; + unsigned short AltSampleRate; + unsigned char AltChannels; + unsigned char AltBitsPerSample; + unsigned short FutureUse[5]; +} VQAHeader; + +/* Version type. */ +#define VQAHD_VER1 1 +#define VQAHD_VER2 2 + +/* VQA header flag definitions */ +#define VQAHDB_AUDIO 0 /* Audio track present. */ +#define VQAHDB_ALTAUDIO 1 /* Alternate audio track present. */ +#define VQAHDF_AUDIO (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +// MEG - 11.28.95 - added for debug +extern void Debug_Printf( char *format_string, ... ); + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + + +#if (VQAAUDIO_ON && VQADIRECT_SOUND) +#define WIN32 +#define _WIN32 +#undef WIN32_LEAN_AND_MEAN +#include +#include +#include "dsound.h" +#endif + + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * SoundObject - Ptr to callers Direct Sound Object (Default =NULL) + * PrimaryBufferPtr- Ptr to callers Primary Sound Buffer. (Default = NULL) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; +#if (VQADIRECT_SOUND) + LPDIRECTSOUND SoundObject; + LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +#endif //(VQADIRECT_SOUND) + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +*/ + +#ifndef VQAPLAY_H +#define VQAPLAY_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplay.h +* +* DESCRIPTION +* VQAPlay library definitions. +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* April 10, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * CONDITIONAL COMPILATION FLAGS + *-------------------------------------------------------------------------*/ + +// MEG - 11.28.95 - added for debug +extern void Debug_Printf( char *format_string, ... ); + +#ifdef __WATCOMC__ +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 0 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#else +#define VQASTANDALONE 0 /* Stand alone player */ +#define VQAVOC_ON 0 /* Enable VOC file override */ +#define VQAMONO_ON 1 /* Mono display output enable/disable */ +#define VQADIRECT_SOUND 1 /* Use windows direct sound system */ +#define VQAAUDIO_ON 1 /* Audio playback enable/disable */ +#define VQAVIDEO_ON 0 /* Video manager enable/disable */ +#define VQAMCGA_ON 1 /* MCGA enable/disable */ +#define VQAXMODE_ON 0 /* Xmode enable/disable */ +#define VQAVESA_ON 0 /* VESA enable/disable */ +#define VQABLOCK_2X2 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_2X3 0 /* 2x2 block decode enable/disable */ +#define VQABLOCK_4X2 1 /* 4x2 block decode enable/disable */ +#define VQABLOCK_4X4 0 /* 4x4 block decode enable/disable */ +#define VQAWOOFER_ON 0 +#endif + + + +#if (VQAAUDIO_ON && VQADIRECT_SOUND) +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#undef WIN32_LEAN_AND_MEAN +#include +#include +#include "dsound.h" +#endif + + + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Playback modes. */ +#define VQAMODE_RUN 0 /* Run the movie through the end. */ +#define VQAMODE_WALK 1 /* Draw the next frame then return. */ +#define VQAMODE_PAUSE 2 /* Suspend movie playback. */ +#define VQAMODE_STOP 3 /* Stop the movie. */ + +/* Playback timer methods */ +#define VQA_TMETHOD_DEFAULT -1 /* Use default timer method. */ +#define VQA_TMETHOD_DOS 1 /* DOS timer method */ +#define VQA_TMETHOD_INT 2 /* Interrupt timer method */ +#define VQA_TMETHOD_AUDIO 3 /* Audio timer method */ + +#define VQA_TIMETICKS 60 /* Clock ticks per second */ + +/* Error/Status conditions */ +#define VQAERR_NONE 0 /* No error */ +#define VQAERR_EOF -1 /* Valid end of file */ +#define VQAERR_OPEN -2 /* Unable to open */ +#define VQAERR_READ -3 /* Read error */ +#define VQAERR_WRITE -4 /* Write error */ +#define VQAERR_SEEK -5 /* Seek error */ +#define VQAERR_NOTVQA -6 /* Not a valid VQA file. */ +#define VQAERR_NOMEM -7 /* Unable to allocate memory */ +#define VQAERR_NOBUFFER -8 /* No buffer avail for load/draw */ +#define VQAERR_NOT_TIME -9 /* Not time for frame yet */ +#define VQAERR_SLEEPING -10 /* Function is in a sleep state */ +#define VQAERR_VIDEO -11 /* Video related error. */ +#define VQAERR_AUDIO -12 /* Audio related error. */ +#define VQAERR_PAUSED -13 /* In paused state. */ + +/* Event flags. */ +#define VQAEVENT_PALETTE (1<<0) +#define VQAEVENT_SYNC (1<<1) + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* VQAConfig: Player configuration structure + * + * DrawerCallback - User routine for Drawer to call each frame (NULL = none) + * EventHandler - User routine for notification to client of events. + * NotifyFlags - User specified events to be notified about. + * Vmode - Requested Video mode (May be promoted). + * VBIBit - Vertical blank bit polarity. + * ImageBuf - Pointer to caller's buffer for the Drawer to use as its + * ImageBuf; NULL = player will allocate its own, if + * VQACFGF_BUFFER is set in DrawFlags. + * ImageWidth - Width of Image buffer. + * ImageHeight - Height of Image buffer. + * X1 - Draw window X coordinate (-1 = Center). + * Y1 - Draw window Y coordinate (-1 = Center). + * FrameRate - Desired frames per second (-1 = use VQA header's value). + * DrawRate - Desired drawing frame rate; allows the Drawer to draw at + * a separate rate from the Loader. + * TimerMethod - Timer method to use during playback. + * DrawFlags - Bits control various special drawing options. (See below) + * OptionFlags - Bits control various special misc options. (See below) + * NumFrameBufs - Desired number of frame buffers. (Default = 6) + * NumCBBufs - Desired number of codebook buffers. (Default = 3) + * SoundObject - Ptr to callers Direct Sound Object (Default =NULL) + * PrimaryBufferPtr- Ptr to callers Primary Sound Buffer. (Default = NULL) + * VocFile - Name of VOC file to play instead of VQA audio track. + * AudioBuf - Pointer to audio buffer. + * AudioBufSize - Size of audio buffer. (Default = 32768) + * AudioRate - Audio data playback rate (-1 = use samplerate scaled + * to the frame rate) + * Volume - Audio playback volume. (0x7FFF = max) + * HMIBufSize - Desired HMI buffer size. (Default = 2000) + * DigiHandle - Handle to an initialized sound driver. (-1 = none) + * DigiCard - HMI ID of card to use. (0 = none, -1 = auto-detect) + * DigiPort - Audio port address. (-1 = auto-detect) + * DigiIRQ - Audio IRQ. (-1 = auto-detect) + * DigiDMA - Audio DMA channel. (-1 = auto-detect) + * Language - Language identifier. (Not used) + * CapFont - Pointer to font to use for subtitle text captions. + * EVAFont - Pointer to font to use for E.V.A text cations. (For C&C) + */ +typedef struct _VQAConfig { + long (*DrawerCallback)(unsigned char *screen, long framenum); + long (*EventHandler)(unsigned long event,void *buffer,long nbytes); + unsigned long NotifyFlags; + long Vmode; + long VBIBit; + unsigned char *ImageBuf; + long ImageWidth; + long ImageHeight; + long X1,Y1; + long FrameRate; + long DrawRate; + long TimerMethod; + long DrawFlags; + long OptionFlags; + long NumFrameBufs; + long NumCBBufs; +#if (VQADIRECT_SOUND) + LPDIRECTSOUND SoundObject; + LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +#endif //(VQADIRECT_SOUND) + char *VocFile; + unsigned char *AudioBuf; + long AudioBufSize; + long AudioRate; + long Volume; + long HMIBufSize; + long DigiHandle; + long DigiCard; + long DigiPort; + long DigiIRQ; + long DigiDMA; + long Language; + char *CapFont; + char *EVAFont; /* For C&C Only */ +} VQAConfig; + +/* Drawer Configuration flags (DrawFlags) */ +#define VQACFGB_BUFFER 0 /* Buffer UnVQ enable */ +#define VQACFGB_NODRAW 1 /* Drawing disable */ +#define VQACFGB_NOSKIP 2 /* Disable frame skipping. */ +#define VQACFGB_VRAMCB 3 /* XMode VRAM copy enable */ +#define VQACFGB_ORIGIN 4 /* 0,0 origin position */ +#define VQACFGB_SCALEX2 6 /* Scale X2 enable (VESA 320x200 to 640x400) */ +#define VQACFGB_WOOFER 7 +#define VQACFGF_BUFFER (1<. +; + +;**************************************************************************** +;* +;* 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 +;* VQAPlay 32 library. +;* +;* FILE +;* vqaplay.i +;* +;* DESCRIPTION +;* VQA player definitions. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 30, 1995 +;* +;**************************************************************************** + +;*--------------------------------------------------------------------------- +;* CONDITIONAL COMPILATION FLAGS +;*--------------------------------------------------------------------------- + + IF PHARLAP_TNT + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 1 ;Mono display output enable/disable +VQAAUDIO_ON EQU 0 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 1 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ELSE + +VQASTANDALONE EQU 0 ;Stand alone player +VQAMONO_ON EQU 0 ;Mono display output enable/disable +VQAAUDIO_ON EQU 1 ;Audio playback enable/disable +VQAVIDEO_ON EQU 0 ;Video manager enable/disable +VQAMCGA_ON EQU 1 ;MCGA enable/disable +VQAXMODE_ON EQU 0 ;Xmode enable/disable +VQAVESA_ON EQU 0 ;VESA enable/disable +VQABLOCK_2X2 EQU 0 ;2x2 block decode enable/disable +VQABLOCK_2X3 EQU 0 ;2x3 block decode enable/disable +VQABLOCK_4X2 EQU 1 ;4x2 block decode enable/disable +VQABLOCK_4X4 EQU 1 ;4x4 block decode enable/disable +VQABLOCK_WOOFER EQU 0 + + ENDIF diff --git a/WINVQ/VQA32/VQAPLAYP.H b/WINVQ/VQA32/VQAPLAYP.H new file mode 100644 index 0000000..16fe5e4 --- /dev/null +++ b/WINVQ/VQA32/VQAPLAYP.H @@ -0,0 +1,540 @@ +/* +** 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 . +*/ + +#ifndef VQAPLAYP_H +#define VQAPLAYP_H +/**************************************************************************** +* +* 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 +* VQA player library. (32-Bit protected mode) +* +* FILE +* vqaplayp.h +* +* DESCRIPTION +* VQAPlay private library definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* August 21, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include "vqafile.h" +#include "vqaplay.h" +#include "caption.h" + +#if(VQAAUDIO_ON) +#if(VQADIRECT_SOUND) +extern HWND MainWindow; +#else +#include "sos.h" +#endif +#endif + +/*--------------------------------------------------------------------------- + * GENERAL CONSTANT DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* Internal library version. */ +#define VQA_VERSION "2.42" +#define VQA_DATE __DATE__" "__TIME__ + +#define VQA_IDSTRING "VQA32 "VQA_VERSION" ("VQA_DATE")" +#define VQA_REQUIRES "VQM32 2.12 or better." + +/* Block dimensions macro and identifiers. */ +#define BLOCK_DIM(a,b) (((a&0xFF)<<8)|(b&0xFF)) +#define BLOCK_2X2 BLOCK_DIM(2,2) +#define BLOCK_2X3 BLOCK_DIM(2,3) +#define BLOCK_4X2 BLOCK_DIM(4,2) +#define BLOCK_4X4 BLOCK_DIM(4,4) + +/* Memory limits */ +#define VQA_MAX_CBBUFS 10 /* Maximum number of codebook buffers */ +#define VQA_MAX_FRAMEBUFS 30 /* Maximum number of frame buffers */ + +/* Special Constants */ +#define VQA_MASK_POINTER 0x8000 /* Pointer value to use for masking. */ + + +/*--------------------------------------------------------------------------- + * STRUCTURES AND RELATED DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* ChunkHeader: IFF chunk identifier header. + * + * id - 4 Byte chunk id. + * size - Size of chunk. + */ +typedef struct _ChunkHeader { + unsigned long id; + unsigned long size; +} ChunkHeader; + + +/* ZAPHeader: ZAP audio compression header. NOTE: If the uncompressed size + * and the compressed size are equal then the audio frame is RAW + * (NOT COMPRESSED). + * + * UnCompSize - Uncompressed size in bytes. + * CompSize - Compressed size in bytes. + */ +typedef struct _ZAPHeader { + unsigned short UnCompSize; + unsigned short CompSize; +} ZAPHeader; + + +/* VQACBNode: A circular list of codebook buffers, used by the load task. + * If the data is compressed, it is loaded into the end of the + * buffer and the compression flags is set. Otherwise the data + * is loaded into the start of the buffer. + * (Make sure this structure's size is always DWORD aligned.) + * + * Buffer - Pointer to Codebook data. + * Next - Pointer to next VQACBNode in the codebook list. + * Flags - Used by the drawer to tell if certain operations have been + * performed on this codebook, such as downloading to VRAM, + * or pre-scaling it. This field is cleared by the Loader when a + * new codebook is loaded. + * CBOffset - Offset into the buffer of the compressed data. + */ +typedef struct _VQACBNode { + unsigned char *Buffer; + struct _VQACBNode *Next; + unsigned long Flags; + unsigned long CBOffset; +} VQACBNode; + +/* VQACBNode flags */ +#define VQACBB_DOWNLOADED 0 /* Download codebook to VRAM (XMODE VRAM) */ +#define VQACBB_CBCOMP 1 /* Codebook is compressed */ +#define VQACBF_DOWNLOADED (1<. +*/ + +/*************************************************************************** + ** 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 : Virtual Monopoly * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 22, 1994 * + * * + * Last Update : May 30, 1995 [ML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_And_Display_Windows_Debug -- GetWinDebugInfo() called for checking* + * Debug_Mono_Message -- Post a line to the monochrome monitor * + * Debug_Window_Message -- One of many ways to pop up a debug msg window * + * Debug_Show_Palette -- Display the current 256 color palette * + * Debug_Draw_Line_Test -- Tests Westwood library Draw_Line() * + * Debug_Put_Pixel_Test -- Tests the Westwood library Put_Pixel() * + * Debug_Shape_Test -- Tests the Westwood Library Draw_Shape() * + * Debug_Print_Game_Stats -- Code to display player stats w/ text * + * Debug_Rig_Roll -- Pops up dialog to fix the next dice roll * + * Debug_Adjust_Players_Cash -- Changes the current players money * + * Debug_Rig_Roll_Dialog_Procedure -- Handles commands to dialog * + * Debug_Adjust_Cash_Dialog_Proc -- Handles dialog messages for this * + * Debug_Mono_Print_Whose_Turn -- Displays current turn on MONO display * + * Build_Debug_Log_File_Name -- Based on the date and time of system * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "debug.rh" +#include "strings.rh" + +#if( RIG_ROLL_FOR_PLAYTEST ) +VOID Debug_Rig_Roll( WindowHandle window_handle ); +PRIVATE BOOL Debug_Rig_Roll_Dialog_Procedure( WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ); +#endif + +#if( DEBUG_CODE ) +/* + public functions +*/ +VOID Get_And_Display_Windows_Debug_Info( BYTE *command_line_string ); +VOID Debug_Mono_Message( BYTE *message ); +//VOID Debug_Window_Message( BYTE *message ); +VOID Debug_Show_Palette( VOID ); +VOID Debug_Draw_Line_Test( VOID ); +VOID Debug_Put_Pixel_Test( VOID ); +VOID Debug_Shape_Test( VOID ); +VOID Debug_Print_Game_Stats( VOID ); +VOID Debug_Adjust_Players_Cash( WindowHandle window_handle ); +VOID Debug_Mono_Print_Whose_Turn( PlayerType player ); +VOID Build_Debug_Log_File_Name( BYTE *name_string ); + +/* + private functions +*/ +PRIVATE BOOL Debug_Adjust_Cash_Dialog_Procedure( WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ); +#endif + + + +#if( DEBUG_CHECKSUM_ALL_SITES_DATA ) +PRIVATE VOID Give_To_The_Mikes( VOID ); +#endif + +#if( DEBUG_CODE ) +/* + code begins... +*/ + +/*************************************************************************** + * DEBUG_MONO_MESSAGE -- Post a line to the monochrome monitor * + * * + * INPUT: the message to print * + * * + * OUTPUT: a message on the monochrome monitor display * + * * + * WARNINGS: try to end messages with \r\n! * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +#pragma argsused +VOID Debug_Mono_Message( BYTE *message ) +{ + #if( DEBUG_LOG_TO_MONO_DISPLAY ) + BYTE *temp_string; + BYTE *temp_null_position; + BYTE temp_char; + INT length_left; + #endif + #if( DEBUG_LOG_TO_FILE ) + INT fh; + static BOOL _errored_once = FALSE; + BYTE name_path_string[ _MAX_PATH ]; + BOOL danger; + #endif + + #if( DEBUG_LOG_TO_FILE ) + + danger = Danger_Test_Hard_Drive_Space(); + + if ( ! _errored_once && ! danger ) { + + if ( *(Executable_Path+0) != '\0' ) { + sprintf( name_path_string, + "%s%s", + Executable_Path, + Debug_Log_File_Name ); + fh = Open_File( name_path_string, READ|WRITE ); + if ( fh == FILEOPENERROR || fh == -1 ) { + sprintf( Text_String, + "Unable to open %s for WRITE.\r\n", + Debug_Log_File_Name ); + #if( DEBUG_CODE ) + Debug_Error_Window_Message( Text_String ); + #endif + _errored_once = TRUE; + } + else { + Seek_File( fh, 0, SEEK_END ); + Write_File( (INT)fh, (BYTE *)message, (ULONG)(strlen(message)) ); + Close_File( fh ); + } + } + } + #endif + + + #if( DEBUG_LOG_TO_MONO_DISPLAY ) + + if ( ! Debug_Mono_Messages ) { + return; + } + + if ( ! Windows_95_Detected_Flag ) { + OutputDebugString( "." ); + } +// else { +// Monochrome_Output( "> " ); +// } + + length_left = strlen( message ); + temp_null_position = message; + while ( length_left > 80 ) { + temp_string = temp_null_position; + temp_null_position += 80; + temp_char = *temp_null_position; + temp_null_position[ 0 ] = '\0'; + + if ( Windows_95_Detected_Flag ) { + Monochrome_Output( temp_string ); + } + else { + OutputDebugString( temp_string ); + } + + *temp_null_position = temp_char; + length_left -= 80; + } + temp_string = temp_null_position; + + if ( Windows_95_Detected_Flag ) { + Monochrome_Output( temp_string ); + } + else { + OutputDebugString( temp_string ); + } + + #endif + +} + +/*************************************************************************** + * DEBUG_SHOW_PALETTE -- Display the current 256 color palette * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Show_Palette( VOID ) +{ + #define PIXEL_SIZE 19 + INT i, j, x, y; + UBYTE color; + DeviceContextHandle screen_dc; + + if ( Game_Is_Now_Currently_Interrupted ) { + Standard_Window_Message( "Game_Is_Now_Currently_Interrupted = TRUE", "X" ); + if ( Current_Interrupting_Player > NO_PLAYER ) { + sprintf( Text_String, + "Current_Interrupting_Player = %s\r\n", + Players[ Current_Interrupting_Player ].Get_Name() ); + } + else { + sprintf( Text_String, + "Current_Interrupting_Player = unknown\r\n" ); + } + Standard_Window_Message( Text_String, "X" ); + } + else { + Standard_Window_Message( "Game_Is_Now_Currently_Interrupted = FALSE", "X" ); + } + + color = 0; + for ( i = 0; i < 16; i ++ ) { + for ( j = 0; j < 16; j ++ ) { + x = VQ_MOVIE_X + (j * PIXEL_SIZE); + y = VQ_MOVIE_Y + (i * PIXEL_SIZE); + Monopoly_Fill_Rect( x, y, x+PIXEL_SIZE-1, y+PIXEL_SIZE-1, COLOR_BLACK ); + Monopoly_Fill_Rect( x+1, y+1, x+PIXEL_SIZE-2, y+PIXEL_SIZE-2, color++ ); + } + } + + screen_dc = GetDC( Main_Window_Handle ); +// WinGBitBlt( Screen_DC, + WinGBitBlt( screen_dc, + VQ_MOVIE_X, VQ_MOVIE_Y, + VQ_MOVIE_WIDTH, + VQ_MOVIE_HEIGHT, + WinG_DC, + VQ_MOVIE_X, VQ_MOVIE_Y ); + ReleaseDC( Main_Window_Handle, screen_dc ); +} + + +/*************************************************************************** + * DEBUG_DRAW_LINE_TEST -- Tests Westwood library Draw_Line() * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: make sure WinG buffer is active! * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Draw_Line_Test( VOID ) +{ +#if( 0 ) +// INT x1=0, y1=0, x2=638, y2=438; + INT x1, y1=0, x2=638, y2=438; + + for ( x1=0; x1<=638; x1+=10 ) { + HidBuff_Class_Ptr->Draw_Line( x1,y1,x2,y2,255 ); + WinGBitBlt( Screen_DC, + min(x1,x2), min(y1,y2), + abs(x2-x1)+1, + abs(y2-y1)+1, + WinG_DC, + min(x1,x2), min(y1,y2) ); + x2-=10; + } + x1=0; +// y1=0; + x2=638; + y2=438; + for ( y1=0; y1<=438; y1+=10 ) { + HidBuff_Class_Ptr->Draw_Line( x1,y1,x2,y2,255 ); + WinGBitBlt( Screen_DC, + min(x1,x2), min(y1,y2), + abs(x2-x1)+1, + abs(y2-y1)+1, + WinG_DC, + min(x1,x2), min(y1,y2) ); + y2-=10; + } +#endif +} + + +/*************************************************************************** + * DEBUG_PUT_PIXEL_TEST -- Tests the Westwood library Put_Pixel() * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure WinG buffer is active * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Put_Pixel_Test( VOID ) +{ +#if( 0 ) + INT x = random( 640 ); + INT y = random( 440 ); + INT xweight=1; int yweight=1; + LONG i; + static UBYTE color = 0; + + color++; + for ( i=0; i<100000; i ++ ) { + + HidBuff_Class_Ptr->Put_Pixel( x, y, color ); + WinGBitBlt( Screen_DC, + x, y, + 1, + 1, + WinG_DC, + x, y ); + + if ( x <= 0 ) { + x = 0; + xweight = 1; + } + if ( y <= 0 ) { + y = 0; + yweight = 1; + } + if ( x >= WING_BITMAP_WIDTH-1 ) { + x = WING_BITMAP_WIDTH-1; + xweight = -1; + } + if ( y >= WING_BITMAP_HEIGHT-1 ) { + y = WING_BITMAP_HEIGHT-1; + yweight = -1; + } + if ( xweight > 0 ) { + /* 0-9 */ + /* 0-9 */ + if ( random( 10 ) <= 4 ) { + x ++; + } + else { /* 0 */ + x --; + } + } + else { + /* 0-9 */ + if ( random( 10 ) <= 4 ) { + x --; + } + else { /* 0 */ + x ++; + } + } + if ( yweight > 0 ) { + /* 0-9 */ + if ( random( 10 ) <= 4 ) { + y ++; + } + else { /* 0 */ + y --; + } + } + else { + /* 0-9 */ + if ( random( 10 ) <= 4 ) { + y --; + } + else { /* 0 */ + y ++; + } + } + } +#endif +} + + +/*************************************************************************** + * DEBUG_SHAPE_TEST -- Tests the Westwood Library Draw_Shape() * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure the WinG buffer is active! * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Shape_Test( VOID ) +{ +#if( 0 ) + BYTE *shapebuf, *shapes; + LONG fsize; + CHAR *shape_ptr[ 37 ]; + INT i, j, x; + + INT wwfh; + + Debug_Mono_Message( "------------------------------------------\r\n" ); + sprintf( Text_String, "Mem before shape buff alloc(5000): %ld\r\n", Ram_Free( MEM_NORMAL ) ); + Debug_Mono_Message( Text_String ); + + shapebuf = (char *) Alloc( 5000, MEM_NORMAL ); + + sprintf( Text_String, "Mem after shape buff alloc(5000): %ld\r\n", Ram_Free( MEM_NORMAL ) ); + Debug_Mono_Message( Text_String ); + + if ( shapebuf == NULL ) { + Debug_Mono_Message("shapebuf alloc failed.\n"); + } + Set_Shape_Buffer (shapebuf,5000); + + wwfh = Open_File( "MSW00.SHP", READ ); + sprintf( Text_String, "File opened with wwfh %d\r\n", wwfh ); + Debug_Mono_Message( Text_String ); + if( wwfh == -1 ) { + Debug_Mono_Message("File not found\n"); + } + fsize = File_Size( wwfh ); + sprintf( Text_String, "File_Size( %d ) = %d\r\n", wwfh, fsize ); + Debug_Mono_Message( Text_String ); + if ( fsize > 0 ) { + shapes = (char *) Alloc( fsize, MEM_NORMAL ); + if( shapes == NULL) { + Debug_Mono_Message("shapes alloc failed\n"); + } + Read_File( wwfh, shapes, fsize ); + Close_File( wwfh ); + } + + sprintf( Text_String, "Mem after shapedata alloc: %ld\r\n", Ram_Free( MEM_NORMAL ) ); + Debug_Mono_Message( Text_String ); + + for( i = 0; i <= 36; i++ ) { + shape_ptr[ i ] = Get_Shape( shapes, i ); + } + + sprintf( Text_String, "Mem after all shapes alloc: %ld\r\n", Ram_Free( MEM_NORMAL ) ); + Debug_Mono_Message( Text_String ); + + Free( shapes ); + + x = -64; + for ( j = 0; j < 20; j ++ ) { + for ( i = 21; i <= 28; i ++ ) { + Refresh_Board_BMP_On_Hid(); + if ( shape_ptr[ i ] ) { +// Draw_Shape( HidBuff_Class_Ptr, shape_ptr[ i ], x, 71, SHAPE_NORMAL ); + Draw_Shape( HidBuff_Class_Ptr, shape_ptr[ i ], x, 0, SHAPE_SCALING, 340, 340 ); + HidBuff_Class_Ptr->Put_Pixel( x, 100, 3 ); + + WinGBitBlt( Screen_DC, + x, 0, + 200, 200, + WinG_DC, + x, 0 ); + } + x += 4; + } + } + if ( shape_ptr[ 0 ] ) { + Refresh_Board_BMP_On_Hid(); +// Draw_Shape( HidBuff_Class_Ptr, shape_ptr[ 0 ], x, 71, SHAPE_NORMAL ); + Draw_Shape( HidBuff_Class_Ptr, shape_ptr[ 0 ], x, 0, SHAPE_SCALING, 340, 340 ); + HidBuff_Class_Ptr->Put_Pixel( x, 100, 3 ); + WinGBitBlt( Screen_DC, + x, 0, + 200, 200, + WinG_DC, + x, 0 ); + } + + for( i = 0; i <= 36; i++ ) { + Free( shape_ptr[ i ] ); + } + + Free( shapebuf ); + + sprintf( Text_String, "Mem when all done: %ld\r\n", Ram_Free( MEM_NORMAL ) ); + Debug_Mono_Message( Text_String ); + Debug_Mono_Message( "------------------------------------------\r\n" ); +#endif +} + +#if( 0 ) +#define DEBUG_TEXT_X 4 +#define DEBUG_TEXT_Y 12 +#define DEBUG_TEXT_SPACING 13 +/*************************************************************************** + * DEBUG_PRINT_GAME_STATS -- Code to display player stats w/ text * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Print_Game_Stats( VOID ) +{ + INT i; + INT x; + INT y; + PlayerType whos_turn; + BYTE temp_token_string[ 50 ]; + BYTE temp_ai_string[ 50 ]; + BYTE temp_string[ 50 ]; + + /* + no game, no stats + */ + if ( ! Game_Status.Is_Game_In_Progress() ) { + return; + } + + x = DEBUG_TEXT_X; + y = DEBUG_TEXT_Y; + + whos_turn = Game_Status.Get_Whos_Turn_Is_It_Now(); + + SetTextAlign( Screen_DC, TA_LEFT ); + SelectObject( Screen_DC, GetStockObject( ANSI_VAR_FONT ) ); + SetTextCharacterExtra( Screen_DC, 0 ); + + for ( i = PLAYER_1; i < TOTAL_PLAYERS; i ++ ) { + if ( Players[ i ].Is_Active() ) { + if ( whos_turn == i ) { + strcpy( Text_String_2, "*" ); + rgb_vals = PALETTERGB( 0, 0, 255 ); + } + else { + strcpy( Text_String_2, " " ); + rgb_vals = WHITE_RGB_COLOR_REF; + } + + sprintf( temp_string, "%d: ", i+1 ); + strcat( Text_String_2, temp_string ); + + // grab token name + if ( Players[ i ].Get_Token() >= TOKEN_FIRST ) { + LoadString( Global_Instance_Handle, + STRING_TOKEN_FIRST + Players[ i ].Get_Token(), + temp_token_string, + 50 ); + } + else { + *temp_token_string = '\0'; + } + + // grab ai name + if ( Players[ i ].Get_AI_Type() >= AI_FIRST ) { + LoadString( Global_Instance_Handle, + STRING_AI_FIRST + Players[ i ].Get_AI_Type(), + temp_ai_string, + 50 ); + } + else { + *temp_ai_string = '\0'; + } + + sprintf( Text_String, + "%s%s, $%d, %s ", + Text_String_2, + Players[ i ].Get_Name(), + Players[ i ].Get_Cash(), + temp_token_string ); + + SetTextColor( Screen_DC, rgb_vals ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); + + /* + now print liquid assets, available assets, tradable assets, + total assets and monopolies... + */ + y += DEBUG_TEXT_SPACING; + sprintf( Text_String, " AI: $%s ", temp_ai_string ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); + y += DEBUG_TEXT_SPACING; + sprintf( Text_String, " Liquid Assets: $%u ", Players[ i ].Get_Liquid_Assets() ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); + y += DEBUG_TEXT_SPACING; + sprintf( Text_String, " Available Assets: $%u ", Players[ i ].Get_Available_Assets() ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); +// y += DEBUG_TEXT_SPACING; +// sprintf( Text_String, " Tradable Assets: $%u ", Players[ i ].Get_Tradable_Assets() ); +// TextOut( Screen_DC, +// x, +// y, +// Text_String, +// strlen( Text_String ) ); + y += DEBUG_TEXT_SPACING; + sprintf( Text_String, " Total Assets: $%u ", Players[ i ].Get_Total_Assets( FALSE ) ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); + y += DEBUG_TEXT_SPACING; +// sprintf( Text_String, " Monopolies: %u ", Players[ i ].Get_Monopolies() ); +// TextOut( Screen_DC, +// x, +// y, +// Text_String, +// strlen( Text_String ) ); +// y += DEBUG_TEXT_SPACING; + } + } + + /* + Free parking! + */ + y += DEBUG_TEXT_SPACING; + if ( Game_Status.Does_Free_Parking_Collect_Cash() ) { + strcpy( Text_String_2, "Active" ); + } + else { + strcpy( Text_String_2, "Inactive" ); + } + sprintf( Text_String, + "Free Parking: $%d, %s ", + Game_Status.Get_Free_Parking_Money(), + Text_String_2 ); + + rgb_vals = BLACK_RGB_COLOR_REF; + SetTextColor( Screen_DC, rgb_vals ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); + + /* + Bank + */ + y += DEBUG_TEXT_SPACING; + sprintf( Text_String, + "Bank: %d houses, %d hotels, $infinite ", + Bank.Get_Houses(), + Bank.Get_Hotels() ); + TextOut( Screen_DC, + x, + y, + Text_String, + strlen( Text_String ) ); +} +#endif + + +/*************************************************************************** + * DEBUG_ADJUST_PLAYERS_CASH -- Changes the current players money * + * * + * INPUT: parent window handle * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Adjust_Players_Cash( WindowHandle window_handle ) +{ + if ( ! Game_Status.Is_Game_In_Progress() ) { + Debug_Error_Window_Message( "Game not in progress!" ); + return; + } + + DialogBox( Global_Instance_Handle, + "DebugSetCashDlgBox", + window_handle, + (DLGPROC) Debug_Adjust_Cash_Dialog_Procedure ); + +} + +#pragma argsused + +/*************************************************************************** + * DEBUG_ADJUST_CASH_DIALOG_PROC -- Handles dialog messages for this * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: TRUE if cash modified, FALSE if not * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +PRIVATE BOOL Debug_Adjust_Cash_Dialog_Procedure( WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ) +{ + INT cash; + PlayerType player; + + switch( message ) { + + case WM_COMMAND: + + if ( LOWORD( w_param ) == IDOK ) { + + GetDlgItemText( window_handle, + CASH_EDIT1, + Text_String, + SIZE_OF_TEXT_STRING ); + cash = atoi( Text_String ); + if ( cash < 0 ) { + cash = 0; + } + if ( cash > 100000 ) { + cash = 100000; + } + + player = Game_Status.Get_Whos_Turn_Is_It_Now(); +// Players[ player ].Set_Money( cash ); + // FALSE = not incoming, so outgoing to other systems + Send_Game_Command( FALSE, CMD_SET_CASH, player, cash ); + + sprintf( Text_String, + "%s now has $%d.\r\n", + Players[ player ].Get_Name(), cash ); + Debug_Mono_Message( Text_String ); + Refresh_Player_Interface( player, TRUE ); + + EndDialog( window_handle, IDOK ); + return( TRUE ); + } + break; + + case WM_INITDIALOG: + /* + Default cash for player + */ + player = Game_Status.Get_Whos_Turn_Is_It_Now(); + itoa( Players[ player ].Get_Cash(), Text_String, 10 ); + SetDlgItemText( window_handle, + CASH_EDIT1, + Text_String ); + SendDlgItemMessage( window_handle, + CASH_EDIT1, + EM_SETSEL, + (WPARAM) 0, + (LPARAM) -1 ); + return( TRUE ); + + default: break; + + } + return( FALSE ); +} + +// TC +extern BYTE *Debug_Chance_Card_Text[]; + +VOID Debug_Show_Chance_Deck( VOID ) +{ + ChanceCardType card; + INT i; + + for ( i = 0; i < TOTAL_CHANCE_CARDS_IN_MAX_DECK; i ++ ) { + card = Game_Cards.Query_Chance_Card( i ); + if ( card > CHANCE_NO_CARD ) { + sprintf( Text_String, + "Chance card[ %d ] = %s\r\n", + i, + Debug_Chance_Card_Text[ card ] ); + } + else { + sprintf( Text_String, + "Chance card[ %d ] = -1\r\n", + i ); + } + Debug_Mono_Message( Text_String ); + } +} + +// TC +extern BYTE *Debug_CC_Card_Text[]; + +VOID Debug_Show_CC_Deck( VOID ) +{ + CommunityChestCardType card; + INT i; + + for ( i = 0; i < TOTAL_COMMUNITY_CHEST_CARDS_IN_MAX_DECK; i ++ ) { + card = Game_Cards.Query_CC_Card( i ); + if ( card > CC_NO_CARD ) { + sprintf( Text_String, + "C. Chest card[ %d ] = %s\r\n", + i, + Debug_CC_Card_Text[ card ] ); + } + else { + sprintf( Text_String, + "C. Chest card[ %d ] = -1\r\n", + i ); + } + Debug_Mono_Message( Text_String ); + } +} + +/*************************************************************************** + * DEBUG_MONO_PRINT_WHOSE_TURN -- Displays current turn on MONO display * + * * + * INPUT: player whos turn it is * + * * + * OUTPUT: a message to the MONO display * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Mono_Print_Whose_Turn( PlayerType player ) +{ + Debug_Mono_Message( "--------------------------------------------------------------------------\r\n" ); + sprintf( Text_String, "--- %s's turn...\r\n", Players[ player ].Get_Name() ); + Debug_Mono_Message( Text_String ); + Debug_Mono_Message( "--------------------------------------------------------------------------\r\n" ); +} + +VOID Debug_Display_Bank_Status( VOID ) +{ + sprintf( Text_String, + "Hotels in bank = %d, Houses in bank = %d", + Bank.Get_Hotels(), + Bank.Get_Houses() ); + Standard_Window_Message( Text_String, "DEBUG" ); +} + +VOID Debug_Show_Player_Info( VOID ) +{ + BYTE *tokens[ TOTAL_TOKENS ] = { + "CANNON ", + "CAR ", + "DOG ", + "HAT ", + "IRON ", + "HORSE ", + "SHIP ", + "SHOE ", + "THIMBLE", + "WBARROW", + }; + BYTE *ais[ TOTAL_AI_TYPES ] = { + "LOCAL HUMAN ", + "LOCAL AI ", + "REMOTE HUMAN", + "REMOTE AI ", + }; + BYTE *status[ TOTAL_ACTIVE_STATUSES ] = { + "INACTIVE", + "ACTIVE ", + "BANKRUPT", + }; + INT i; + + Debug_Mono_Message( "****************************************************\r\n" ); + sprintf( Text_String, + "Game_Is_Now_Currently_Interrupted = %d\r\n", + Game_Is_Now_Currently_Interrupted ); + Debug_Mono_Message( Text_String ); + for ( i = FIRST_PLAYER; i <= LAST_PLAYER; i ++ ) { + if ( Players[ i ].Get_Active_Status() != PLAYER_INACTIVE ) { + sprintf( Text_String, + "Player[ %d ] - %s | %s | %s | %s | %s\r\n", + i, + tokens[ Players[ i ].Get_Token() ], + ais[ Players[ i ].Get_AI_Type() ], + Players[ i ].Get_IP_Address(), + status[ Players[ i ].Get_Active_Status() ], + Players[ i ].Get_Name() ); + } + else { + sprintf( Text_String, + "Player[ %d ] INACTIVE\r\n", + i ); + + } + Debug_Mono_Message( Text_String ); + } + Debug_Mono_Message( "****************************************************\r\n" ); + +} + +VOID Debug_Give_All_Properties( PlayerType player ) +{ + INT i; + + if ( player >= FIRST_PLAYER ) { + for ( i = PROP_FIRST; i <= PROP_LAST; i ++ ) { +// Property_Data[ i ].Set_Owner( player ); + Send_Game_Command( FALSE, CMD_SET_OWNER, player, i ); + } +// Refresh_Player_Interface( player, TRUE ); + Send_Game_Command( FALSE, CMD_FULLY_REFRESH_SCREEN ); + } +} + +#endif /* DEBUG_CODE */ + +#if( DEBUG_CHECKSUM_ALL_SITES_DATA ) +#if( COMPILE_WINSOCK ) +VOID Debug_Broadcast_Save_Game_Checksum_Other_Sites( VOID ) +{ + ULONG file_size; + INT fh; + INT i; + ULONG check_sum; + BYTE *buffer; +// SOCKET temp_socket; +// INT status; + INT low_int; + INT high_int; + UBYTE add_byte; + BYTE *file_name; + + /* + don't do this unless we are multi-site + */ + if ( ! Multiple_Site_Game_In_Progress ) { + return; + } + + file_name = Text_String_3; + sprintf( file_name, + "%s%s", + Executable_Path, + "TEMPSEND.TMP" ); + + /* + first save out the current game data + */ + DOS_Save_A_Game( file_name, TRUE ); // TRUE = site-to-site verification + /* + now allocate a buffer and load the save data back up + */ + fh = Open_File( file_name, READ ); + if ( fh != -1 ) { + file_size = File_Size( fh ); + buffer = (BYTE *) Alloc( (ULONG) file_size + 10, MEM_NORMAL ); + if ( buffer == NULL ) { + Close_File( fh ); + #if ( DEBUG_CODE ) + Debug_Error_Window_Message( "Unable to alloc buffer in Debug_Broadcast_Save_Game_Data_To_Other_Sites()" ); + #endif + return; + } + Read_File( fh, buffer, file_size ); + Close_File( fh ); + } + + /* + now checksum the buffer + */ + check_sum = 0L; + for ( i = 0; i < file_size; i ++ ) { + add_byte = (UBYTE) *(buffer + i); + check_sum += (add_byte * (i+1)); + } + low_int = LOWORD( check_sum ); + high_int = HIWORD( check_sum ); + + #if( DEBUG_CODE ) + sprintf( Text_String, + "Local checksum of %s is %ld, sending %d (low) & %d (high)...\r\n", + file_name, + check_sum, + low_int, + high_int ); + Debug_Mono_Message( Text_String ); + #endif + + /* + send a system command with the checksum game data + */ + WinSock_Broadcast_Command( CMD_SENDING_CHECKSUM_DATA, 3, (INT) file_size, low_int, high_int ); + + if ( buffer ) { + Free( buffer ); +// buffer = NULL; + } +// Delete_File( file_name ); +} +#endif +#endif + +#if( DEBUG_CHECKSUM_ALL_SITES_DATA ) +#if( COMPILE_WINSOCK ) +VOID Debug_Compare_Local_Checksum_With_Received_Checksum( INT received_file_size, INT low_int, INT high_int ) +{ + INT fh; + ULONG received_check_sum; + ULONG local_check_sum; + BYTE *buffer = NULL; + INT local_file_size; + INT i; + UBYTE add_byte; + BYTE *file_name; + static BOOL _errored_already = FALSE; + + if ( _errored_already ) { + return; + } + + /* + don't do this unless we are multi-site + */ + if ( ! Multiple_Site_Game_In_Progress ) { + return; + } + + received_check_sum = high_int; + received_check_sum <<= 16; + received_check_sum += low_int; + + #if( DEBUG_CODE ) + sprintf( Text_String, + "Getting ready to compare other sites file, size = %d, checksum = %ld\r\n", + received_file_size, + received_check_sum ); + Debug_Mono_Message( Text_String ); + #endif + + file_name = Text_String_3; + + sprintf( file_name, + "%s%s", + Executable_Path, + "TEMPSAVE.TMP" ); + + /* + save out out current game right now! + */ + DOS_Save_A_Game( file_name, TRUE ); // TRUE = site-to-site verification + /* + now load it, and check sum it + */ + /* + now allocate a buffer and load the save data back up + */ + fh = Open_File( file_name, READ ); + if ( fh != -1 ) { + local_file_size = File_Size( fh ); + buffer = (BYTE *) Alloc( (ULONG) local_file_size + 10, MEM_NORMAL ); + if ( buffer == NULL ) { + Close_File( fh ); + #if ( DEBUG_CODE ) + Debug_Error_Window_Message( "Unable to alloc buffer in Debug_Broadcast_Save_Game_Data_To_Other_Sites()" ); + #endif + return; + } + Read_File( fh, buffer, local_file_size ); + Close_File( fh ); + } + /* + now checksum the buffer + */ + local_check_sum = 0L; + for ( i = 0; i < local_file_size; i ++ ) { + add_byte = (UBYTE) *(buffer + i); + local_check_sum += (add_byte * (i+1)); + } + /* + free the buffer + */ + if( buffer ) { + Free( buffer ); + } + + /* + now compare files sizes and check sum! + */ + if ( local_file_size != received_file_size ) { + sprintf( Text_String, + "ERROR! TEMPSAVE.TMP (local, %d) & TEMPSEND.TMP (remote, %d) file sizes don't match!\r\n", + local_file_size, received_file_size ); + #if( DEBUG_CODE ) + Debug_Mono_Message( Text_String ); +// don't post anything! + Debug_Error_Window_Message( Text_String ); + #endif + + return; + } + if ( local_check_sum != received_check_sum ) { + + LOCAL_RESET_SITE_OPTION( SITE_OPTION_COMPUTER_ROLLS_FOR_ITSELF ); + Send_Game_Command( FALSE, CMD_SET_GAME_OVER_FLAG, TRUE ); + _errored_already = TRUE; + + #if( DEBUG_CODE ) + sprintf( Text_String, + "ERROR! TEMPSAVE.TMP (local, %ld) & TEMPSEND.TMP (remote, %ld) checksums don't match!\r\n", + local_check_sum, received_check_sum ); +// Give_To_The_Mikes(); + Debug_Mono_Message( Text_String ); + Debug_Error_Window_Message( Text_String ); + #endif + + return; + } + /* + okay! everything matches! + */ + #if( DEBUG_CODE ) + sprintf( Text_String, + "TEMPSAVE.TMP (local, %ld) & TEMPSEND.TMP (remote, %ld) checksums match!\r\n", + local_check_sum, received_check_sum ); + Debug_Mono_Message( Text_String ); + #endif +} +#endif +#endif + +#if( DEBUG_CHECKSUM_ALL_SITES_DATA ) +PRIVATE VOID Give_To_The_Mikes( VOID ) +{ + Real_Error_Window_Message( "Put TEMPSAVE.TMP from your system on a floppy..." ); + Real_Error_Window_Message( "and put TEMPSEND.TMP from your oppenent systems on the floppy..." ); + Real_Error_Window_Message( "...and then give it to the Mike's for error checking!" ); +} +#endif + +#if( DEBUG_WRITE_IDS_IN_SAVES ) +LONG Debug_Write_ID_Save_Game_File( INT file_handle, BYTE *three_byte_id ) +{ + LONG bytes_read_or_written = 0L; + + if ( file_handle > -1 ) { + bytes_read_or_written += Write_File( file_handle, three_byte_id, 3 ); + } + + return( bytes_read_or_written ); +} +#endif + +#if( DEBUG_WRITE_IDS_IN_SAVES ) +LONG Debug_Read_ID_Save_Game_File( INT file_handle ) +{ + LONG bytes_read_or_written = 0L; + BYTE junk[ 4 ]; + + if ( file_handle > -1 ) { + bytes_read_or_written += Read_File( file_handle, &junk, 3 ); + } + + return( bytes_read_or_written ); +} +#endif + +#if( DEBUG_WRITE_IDS_IN_SAVES ) +LONG Debug_Write_ID_Save_Game_Memory( MemorySaveBufferType buffer_num, BYTE *three_byte_id ) +{ + LONG bytes_read_or_written = 0L; + + bytes_read_or_written += Memory_Save_Games[ buffer_num ].Write_Data( (UBYTE *) three_byte_id, 3 ); + + return( bytes_read_or_written ); +} +#endif + +#if( DEBUG_WRITE_IDS_IN_SAVES ) +LONG Debug_Read_ID_Save_Game_Memory( MemorySaveBufferType buffer_num ) +{ + LONG bytes_read_or_written = 0L; + BYTE junk[ 4 ]; + + bytes_read_or_written += Memory_Save_Games[ buffer_num ].Read_Data( (UBYTE *) &junk, 3 ); + + return( bytes_read_or_written ); +} +#endif + +/*************************************************************************** + * BUILD_DEBUG_LOG_FILE_NAME -- Based on the date and time of system * + * * + * INPUT: buffer to put file name in * + * * + * OUTPUT: none * + * * + * WARNINGS: the buffer had better be at least 13 characters for filename * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +#if( DEBUG_LOG_TO_FILE ) +VOID Build_Debug_Log_File_Name( BYTE *name_string_buffer, BYTE *system_id_string_buffer ) +{ + SYSTEMTIME data; + BYTE *ptr; + INT fh; + BYTE *file_name; + + /* + build file in following format: + + AA BB CC DD . 0EE -> AABBCCDD.0EE + + AA = month 01-12 + BB = day 01-31 + CC = hour 00-23 + DD = minute 00-59 + EE = second 00-59 + */ + + /* + get system time and data information and build a file name + */ + GetSystemTime( &data ); +// sprintf( name_string, +// "%2d%2d%2d%2d.%2d", +// data.wMonth, +// data.wDay, +// data.wHour, +// data.wMinute, +// data.wSecond ); + sprintf( name_string_buffer, + "%2d%2d%2d%2d.%3d", + data.wMonth, + data.wDay, + data.wHour, + data.wMinute, + 0 ); + + /* + replace spaces w/ 0's in file name + */ + ptr = name_string_buffer; + while ( *ptr != '\0' ) { + if ( *ptr == ' ' ) { + *ptr = '0'; + } + ptr ++; + } + +// #if( DEBUG_CODE ) +// sprintf( Text_String, +// "Log file name for this session: %s\r\n", +// name_string ); +// Debug_Mono_Message( Text_String ); +// #endif + + /* + build .INI file name (with directory path) in Text_String_2 + */ + sprintf( Text_String_2, + "%sMONOPOLY.INI", + Executable_Path ); + + /* + now get the system id from "monopoly.ini" in Text_String_3, error if not in there + */ + if ( ! Find_File( Text_String_2 ) ) { + sprintf( Text_String, + "%s not found!", + Text_String_2 ); + Debug_Error_Window_Message( Text_String ); + return; + } + + /* + get the DebugSystemName in Text_String_3 + */ + GetPrivateProfileString( "Monopoly", + "DebugSystemName", + "No DebugSystemName found!", + Text_String_3, + SIZE_OF_TEXT_STRING, + Text_String_2 ); + + /* + Now store the system name + */ + if( strlen( Text_String_3 ) >= SIZE_OF_DEBUG_SYSTEM_ID_NAME ) { + *( Text_String_3 + SIZE_OF_DEBUG_SYSTEM_ID_NAME - 1 ) = '\0'; + } + strcpy( system_id_string_buffer, Text_String_3 ); + +// #if( DEBUG_CODE ) +// sprintf( Text_String, +// "Legal DebugSystemName for this session: %s\r\n", +// Text_String_3 ); +// Debug_Mono_Message( Text_String ); +// #endif + + /* + open and write header to the log file + */ + strcpy( Text_String_2, + "----------------------------------------------------------------\r\n" ); + sprintf( Text_String, + "---File: %s \r\n---Running on %02d/%02d/%02d at %02d:%02d \r\n---System: %s \r\n", + name_string_buffer, + data.wMonth, data.wDay, data.wYear, + data.wHour, data.wMinute, + Text_String_3 ); + +// #if( DEBUG_CODE ) +// Debug_Mono_Message( Text_String ); +// #endif + + /* + open the file and write out the header info! + */ + + file_name = Text_String_3; + sprintf( file_name, + "%s%s", + Executable_Path, + name_string_buffer ); + + fh = Open_File( file_name, WRITE ); + Write_File( (INT)fh, (BYTE *)Text_String_2, (ULONG)(strlen(Text_String_2)) ); + Write_File( (INT)fh, (BYTE *)Text_String, (ULONG)(strlen(Text_String)) ); + Write_File( (INT)fh, (BYTE *)Text_String_2, (ULONG)(strlen(Text_String_2)) ); + Close_File( fh ); +} + +#endif + +#if( DEBUG_LOG_TO_FILE ) +VOID Debug_Print_Log_File_Name_On_Board_Hid( VOID ) +{ +#if( 0 ) + SetTextAlign( WinG_DC, TA_LEFT ); + SelectObject( WinG_DC, GetStockObject( ANSI_VAR_FONT ) ); + SetTextColor( WinG_DC, BLACK_RGB_COLOR_REF ); + SetTextCharacterExtra( WinG_DC, 0 ); + SetBkMode( WinG_DC, TRANSPARENT ); + + sprintf( Text_String, + "Debug file: %s", + Debug_Log_File_Name ); + + TextOut( WinG_DC, + VQ_MOVIE_X+2, + VQ_MOVIE_Y-2, + Text_String, + strlen( Text_String ) ); + + sprintf( Text_String, + "System name: %s", + Debug_System_ID_Name ); + + TextOut( WinG_DC, + VQ_MOVIE_X+2, + VQ_MOVIE_Y-2+10, + Text_String, + strlen( Text_String ) ); + + sprintf( Text_String, + "Vers: %s", + VERSION_NUMBER_STRING ); + + TextOut( WinG_DC, + VQ_MOVIE_X+2, + VQ_MOVIE_Y-2+20, + Text_String, + strlen( Text_String ) ); +#endif +} +#endif // DEBUG_CODE + +/* + This needs to be outside DEBUG_CODE +*/ +/*************************************************************************** + * GET_AND_DISPLAY_WINDOWS_DEBUG -- GetWinDebugInfo() is called for checkin* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: avoid the raptor pit on Jurassic Park * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +BOOL Get_Windows_System_Info( VOID ) +{ + OSVERSIONINFO version_info; + DWORD major; + DWORD minor; + FLOAT float_version; + +//#if( 0 ) +// WINDEBUGINFO current_windows_debug_info; +// GetWinDebugInfo( ¤t_windows_debug_info, +// WDI_OPTIONS | WDI_FILTER | WDI_ALLOC_BREAK ); +// sprintf( Text_String, +// "Debug info:\r\nflags = %d, options = %d, filter = %d, alloc module = %s, \r\nalloc break = %ld, alloc count = %ld.\r\n", +// current_windows_debug_info.flags, +// current_windows_debug_info.dwOptions, +// current_windows_debug_info.dwFilter, +// current_windows_debug_info.achAllocModule, +// current_windows_debug_info.dwAllocBreak, +// current_windows_debug_info.dwAllocCount ); +// Debug_Mono_Message( Text_String ); +//#endif + + /*------------------------------------------------------------------------*/ + + Windows_95_Detected_Flag = FALSE; + + version_info.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + if ( GetVersionEx( &version_info ) ) { + sprintf( Text_String, + "Windows OS version: %ld.%ld, build #%ld, %s\r\n", + version_info.dwMajorVersion, + version_info.dwMinorVersion, + version_info.dwBuildNumber, + version_info.szCSDVersion ); + Debug_Mono_Message( Text_String ); + + if ( version_info.dwPlatformId == VER_PLATFORM_WIN32s ) { // 0 + #if( DEBUG_CODE ) + Debug_Mono_Message( "Windows OS Platform: Win32s on Windows 3.1\r\n" ); + #endif + + Windows_95_Detected_Flag = FALSE; + /* + if Win32s, make sure that we have version 1.25 or greater + */ + major = version_info.dwMajorVersion; + minor = version_info.dwMinorVersion; + sprintf( Text_String, + "%ld.%ld", + major, minor ); + sscanf( Text_String, "%f", &float_version ); +// if ( major < REQUIRED_WIN32S_MAJOR_VERSION || +// ( major == REQUIRED_WIN32S_MAJOR_VERSION && minor < REQUIRED_WIN32S_MINOR_VERSION ) ) { +// Real_Error_Window_Message( "Win32s version 1.25 or greater is required. Run SETUP.EXE from the Monopoly CD-ROM CD." ); +// return( FALSE ); +// } + if ( float_version < REQUIRED_WIN32S_FLOAT_VERSION ) { + /* + do both in case TRR not set up + */ + Real_Error_Window_Message_String( "Win32s version 1.25 or greater is required. Run SETUP.EXE from the Monopoly CD-ROM CD." ); + Real_Error_Window_Message( STR_TRR_SYSTEM_ERROR_76 ); + return( FALSE ); + } + + } +// else if ( version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { // 1 + else if ( version_info.dwPlatformId == 1 ) { // 1 + #if( DEBUG_CODE ) + Debug_Mono_Message( "Windows OS Platform: Win32 on Chicago\r\n" ); + #endif + Windows_95_Detected_Flag = TRUE; + } + else if ( version_info.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // 2 + #if( DEBUG_CODE ) + Debug_Mono_Message( "Windows OS Platform: Win32 NT\r\n" ); + #endif + Windows_95_Detected_Flag = FALSE; + } + else { + #if( DEBUG_CODE ) + Debug_Mono_Message( "Windows OS Platform: Unknown!\r\n" ); + #endif + Windows_95_Detected_Flag = TRUE; // just to be safe! future versions! + } + } + else { + Debug_Mono_Message( "Unable to GetVersionEx()!\r\n" ); + } + + /*------------------------------------------------------------------------*/ + +// /* +// display command line string, if any +// */ +// sprintf( Text_String, +// "WinMain() command line string = %s\r\n", +// command_line_string ); +// Debug_Mono_Message( Text_String ); + + /* + Show the current directory + */ +// GetCurrentDirectory( SIZE_OF_TEXT_STRING, Text_String_3 ); +// sprintf( Text_String, +// "GetCurrentDirectory() = %s\r\n", +// Text_String_3 ); +// Debug_Mono_Message( Text_String ); + + sprintf( Text_String, + "Executable_Path[] = %s\r\n", + Executable_Path ); + Debug_Mono_Message( Text_String ); + + sprintf( Text_String, + "CD_Root_Path[] = %s\r\n", + CD_Root_Path ); + Debug_Mono_Message( Text_String ); + + /* + display argv arguments + */ +// for ( i = 0; i < _argc; i ++ ) { +// sprintf( Text_String, +// "WinMain() argv[ %d ] = %s\r\n", +// i, +// _argv[ i ] ); +// Debug_Mono_Message( Text_String ); +// } + + return( TRUE ); +} + +//#if( DEBUG_VERIFY_ALL_SITES_DATA ) +//VOID Debug_Broadcast_Save_Game_Data_To_Other_Sites( VOID ) +//{ +// BYTE *file_name = "TEMPSEND.TMP"; +// ULONG file_size; +// INT fh; +// INT i; +// BYTE *buffer; +// SOCKET temp_socket; +// INT status; +// +// /* +// first save out the current game data +// */ +// DOS_Save_Game( file_name, TRUE ); // TRUE = site-to-site verification +// /* +// now allocate a buffer and load the save data back up +// */ +// fh = Open_File( file_name, READ ); +// if ( fh != -1 ) { +// file_size = File_Size( fh ); +// buffer = (BYTE *) Alloc( (ULONG) file_size + 10, MEM_NORMAL ); +// if ( buffer == NULL ) { +// Close_File( fh ); +// Generic_Window_Message( "Unable to alloc buffer in Debug_Broadcast_Save_Game_Data_To_Other_Sites()", " " ); +// return; +// } +// Read_File( fh, buffer, file_size ); +// Close_File( fh ); +// } +// +// #if( COMPILE_WINSOCK ) +// /* +// send a system command that the save game data is coming over +// */ +// WinSock_Broadcast_Command( CMD_HERE_COMES_MY_GAME_STATUS_BUFFER, 1, (INT) file_size ); +// /* +// now blast the data over! +// */ +// /* if this system was the original host */ +// if ( Waiting_Socket_Index > -1 ) { +// /* send the command to all the "waiter links" */ +// for ( i = 0; i <= Waiting_Socket_Index; i ++ ) { +// temp_socket = Hosts_Waiting_Sockets[ i ].accepted_connection_waiting_socket; +// if ( temp_socket != INVALID_SOCKET ) { +// status = WINSOCK_SEND( temp_socket, buffer, file_size, 0 ); +// if ( status == SOCKET_ERROR ) { +// Generic_Window_Message( "Bad send() of save game data", " " ); +// } +// else { +// sprintf( Text_String, "%d size save game data buffer sent\r\n", status ); +// Debug_Mono_Message( Text_String ); +// } +// } +// } +// } +// /* else this system is a webbee */ +// else { +// /* send the command to the original host */ +// if ( WC_Temp_Waiter_Socket != INVALID_SOCKET ) { +// status = WINSOCK_SEND( WC_Temp_Waiter_Socket, buffer, file_size, 0 ); +// if ( status == SOCKET_ERROR ) { +// Generic_Window_Message( "Bad send() of save game data", " " ); +// } +// else { +// sprintf( Text_String, "%d size save game data buffer sent\r\n", status ); +// Debug_Mono_Message( Text_String ); +// } +// } +// /* then send the command to all other connected webbees */ +// for ( i = 0; i <= Web_Accepted_Socket_Index; i ++ ) { +// temp_socket = Web_Accepted_Sockets[ i ]; +// if ( temp_socket != INVALID_SOCKET ) { +// status = WINSOCK_SEND( temp_socket, buffer, file_size, 0 ); +// if ( status == SOCKET_ERROR ) { +// Generic_Window_Message( "Bad send() of save game data", " " ); +// } +// else { +// sprintf( Text_String, "%d size save game data buffer sent\r\n", status ); +// Debug_Mono_Message( Text_String ); +// } +// } +// } +// } +// #endif +// +// if ( buffer ) { +// Free( buffer ); +//// buffer = NULL; +// } +//// Delete_File( file_name ); +//} +//#endif /* DEBUG_VERIFY_ALL_SITE_DATA */ + +//#if( DEBUG_VERIFY_ALL_SITES_DATA ) +//VOID Debug_Prepare_To_Receive_Other_Site_Save_Game_Data( INT buffer_size ) +//{ +// if ( Incoming_Save_Game_Data ) { +// Generic_Window_Message( "Incoming_Save_Game_Data already allocated!", " " ); +// Free( Incoming_Save_Game_Data ); +// Incoming_Save_Game_Data = NULL; +// } +// +// Incoming_Save_Game_Data = (BYTE *) Alloc( (buffer_size) + 10, MEM_NORMAL ); +// Incoming_Save_Game_Data_Size = buffer_size; +// if ( ! Incoming_Save_Game_Data ) { +// Generic_Window_Message( "Unable to allocate Incoming_Save_Game_Data\r\n", " " ); +// Incoming_Save_Game_Data_Size = 0; +// } +// else { +// Debug_Mono_Message( "Incoming_Save_Game_Data allocated and ready for receive...\r\n" ); +// } +//} +//#endif /* DEBUG_VERIFY_ALL_SITE_DATA */ +// +//#if( DEBUG_VERIFY_ALL_SITES_DATA ) +//VOID Debug_Verify_Incoming_Save_Data( INT bytes_actually_received ) +//{ +// INT fh; +// BYTE *buffer = NULL; +// BYTE *file_name = "TEMPSAVE.TMP"; +// LONG file_compare; +// +// if ( ! Incoming_Save_Game_Data || ! Incoming_Save_Game_Data_Size ) { +// return; +// } +// +// sprintf( Text_String, +// "Save game data rec'd for verify, expected %d, actual %d\r\n", +// Incoming_Save_Game_Data_Size, +// bytes_actually_received ); +// Debug_Mono_Message( Text_String ); +// +// /* +// temp save the "just received save game" buffer out +// */ +// fh = Open_File( "TEMPRECV.MNO", WRITE ); +// if ( fh != -1 ) { +// Write_File( fh, (BYTE *) Incoming_Save_Game_Data, Incoming_Save_Game_Data_Size ); +// Close_File( fh ); +// } +// else { +// Generic_Window_Message( "Unable to save out temporary rec'd save game", " " ); +// } +// +// /* +// now compare the data just rec'd with our current save game +// */ +// /* +// first save out the local current game data on this system +// */ +// DOS_Save_Game( file_name, TRUE ); // TRUE = site-to-site verification +// +// /* +// do file compare here between file_name (local) and +// temprecv.mno (rec'd from remote) +// */ +// file_compare = Debug_Compare_Two_Files( file_name, "TEMPRECV.MNO" ); +// switch( file_compare ) { +// case -4L: Debug_Mono_Message( "Received game status data fine!\r\n" ); break; +// case -3L: Debug_Mono_Message( "Memory error occured w/ file compare!\r\n" ); break; +// case -2L: Debug_Mono_Message( "File error occured w/ file compare!\r\n" ); break; +// case -1L: Generic_Window_Message( "Error! Data verification file sizes different!", " " ); +// default: +// sprintf( Text_String, +// "Error! Data verification files differ at byte %ld", +// file_compare ); +// Generic_Window_Message( Text_String, " " ); +// sprintf( Text_String, +// "Compare %s (our system) and TEMPRECV.MNO (rec'd)!", +// file_name ); +// Generic_Window_Message( Text_String, " " ); +// } +// +// /* +// free the temporary buffer +// */ +// if( buffer ) { +// Free( buffer ); +//// buffer = NULL; +// } +// /* +// free it all up for next time +// */ +// if( Incoming_Save_Game_Data ) { +// Free( Incoming_Save_Game_Data ); +// Incoming_Save_Game_Data = NULL; +// } +// Incoming_Save_Game_Data_Size = 0; +//} +//#endif /* DEBUG_VERIFY_ALL_SITE_DATA */ + +///* +// 0+ = different at byte 0+ +// -1 = size different +// -2 = file / DOS error +// -3 = allocation / memory error +// -4 = files are the same! +//*/ +//#if( DEBUG_VERIFY_ALL_SITES_DATA ) +//LONG Debug_Compare_Two_Files( BYTE *file_name_1, BYTE *file_name_2 ) +//{ +// INT fh_1; +// INT fh_2; +// LONG file_size_1; +// LONG file_size_2; +// UBYTE *buffer_1; +// UBYTE *buffer_2; +// UBYTE *ptr_1; +// UBYTE *ptr_2; +// LONG bytes_read_1; +// LONG bytes_read_2; +// LONG i; +// LONG error_byte_index; +// +// fh_1 = Open_File( file_name_1, READ ); +// if ( fh_1 == -1 ) { +// sprintf( Text_String, +// "File %s could not be opened\r\n", +// file_name_1 ); +// Debug_Mono_Message( Text_String ); +// return( -2L ); // ERROR! +// +// } +// fh_2 = Open_File( file_name_2, READ ); +// if ( fh_2 == -1 ) { +// sprintf( Text_String, +// "File %s could not be opened\r\n", +// file_name_2 ); +// Debug_Mono_Message( Text_String ); +// Close_File( fh_1 ); +// return( -2L ); // ERROR! +// +// } +// +// file_size_1 = File_Size( fh_1 ); +// file_size_2 = File_Size( fh_2 ); +//// file_size_1 = 662; +//// file_size_2 = 662; +// +// sprintf( Text_String, +// "File_Size() of %s = %d bytes, File_Size() of %s = %d bytes\r\n", +// file_name_1, +// file_size_1, +// file_name_2, +// file_size_2 ); +// Debug_Mono_Message( Text_String ); +// +// if ( file_size_1 != file_size_2 ) { +//// Debug_Mono_Message( "About to call Close_File()\r\n" ); +// Close_File( fh_1 ); +//// Debug_Mono_Message( "About to call Close_File()\r\n" ); +// Close_File( fh_2 ); +// return( -1L ); // ERROR! +// } +// +//// Debug_Mono_Message( "About to call Alloc()\r\n" ); +// buffer_1 = (UBYTE *) Alloc( (ULONG) (file_size_1 + 10L), MEM_NORMAL ); +//// Debug_Mono_Message( "About to call Alloc()\r\n" ); +// buffer_2 = (UBYTE *) Alloc( (ULONG) (file_size_2 + 10L), MEM_NORMAL ); +// if ( ! buffer_1 || ! buffer_2 ) { +// if ( buffer_1 ) { +//// Debug_Mono_Message( "error, About to call Free()\r\n" ); +// Free( buffer_1 ); +// } +// if ( buffer_2 ) { +//// Debug_Mono_Message( "error, About to call Free()\r\n" ); +// Free( buffer_2 ); +// } +//// Debug_Mono_Message( "error, About to call Close_File()\r\n" ); +// Close_File( fh_1 ); +//// Debug_Mono_Message( "error, About to call Close_File()\r\n" ); +// Close_File( fh_2 ); +// return( -3L ); // ERROR! +// } +// +//// Debug_Mono_Message( "About to call Read_File()\r\n" ); +// bytes_read_1 = Read_File( fh_1, buffer_1, file_size_1 ); +//// Debug_Mono_Message( "About to call Read_File()\r\n" ); +// bytes_read_2 = Read_File( fh_2, buffer_2, file_size_2 ); +//// Debug_Mono_Message( "About to call Close_File()\r\n" ); +// Close_File( fh_1 ); +//// Debug_Mono_Message( "About to call Close_File()\r\n" ); +// Close_File( fh_2 ); +// +// sprintf( Text_String, +// "Bytes read of %s = %d bytes, bytes read of %s = %d bytes\r\n", +// file_name_1, +// bytes_read_1, +// file_name_2, +// bytes_read_2 ); +// Debug_Mono_Message( Text_String ); +// +// if ( bytes_read_1 != bytes_read_2 ) { +//// Debug_Mono_Message( "Error, About to call Free()\r\n" ); +// Free( buffer_1 ); +//// Debug_Mono_Message( "Error, About to call Free()\r\n" ); +// Free( buffer_2 ); +// return( -2L ); // ERROR! +// } +// +//// Debug_Mono_Message( "About to call memcmp()\r\n" ); +// if ( memcmp( buffer_1, buffer_2, file_size_1 ) ) { +// +// Debug_Mono_Message( "Error: Files are different\r\n" ); +// +// ptr_1 = (UBYTE *) buffer_1; +// ptr_2 = (UBYTE *) buffer_2; +// /* +// scan the data and find which byte # the error occured on +// */ +// error_byte_index = 0L; +// for ( i = 0; i < file_size_1; i ++ ) { +// if ( *ptr_1 != *ptr_2 ) { +// error_byte_index = i; +// i = file_size_1; +// } +// ptr_1 += 1; +// ptr_2 += 1; +// } +// +//// Debug_Mono_Message( "Error, About to call Free()\r\n" ); +// Free( buffer_1 ); +//// Debug_Mono_Message( "Error, About to call Free()\r\n" ); +// Free( buffer_2 ); +// +// sprintf( Text_String, +// "Error, files are different at byte #%d\r\n", +// error_byte_index ); +// Debug_Mono_Message( Text_String ); +// +// return( (LONG) error_byte_index ); // ERROR! +// } +// +//// Debug_Mono_Message( "About to call Free()\r\n" ); +// Free( buffer_1 ); +//// Debug_Mono_Message( "About to call Free()\r\n" ); +// Free( buffer_2 ); +// +// Debug_Mono_Message( "Both files match!\r\n" ); +// return( -4L ); // SUCCESS! +//} +//#endif + +BYTE *Debug_Get_Time_String( VOID ) +{ + SYSTEMTIME data; + static BYTE _time_string[ 20 ]; + + /* + build file in following format: + + AA BB CC DD . 0EE -> AABBCCDD.0EE + + AA = month 01-12 + BB = day 01-31 + CC = hour 00-23 + DD = minute 00-59 + EE = second 00-59 + */ + + /* + get system time and data information and build a file name + */ + GetSystemTime( &data ); +// sprintf( _time_string, +// "%2d%2d%2d%2d.%2d", +// data.wMonth, +// data.wDay, +// data.wHour, +// data.wMinute, +// data.wSecond ); + sprintf( _time_string, + "%2d:%2d", + data.wHour, + data.wMinute ); + + return( _time_string ); +} + + +VOID Check_For_Duplicate_Tokens_Bug( VOID ) +{ +#if( DEBUG_CODE ) + INT i, j; + GameTokenType token; + + for ( i = FIRST_PLAYER; i < Game_Status.Get_Total_Players(); i ++ ) { + token = Players[ i ].Get_Token(); + for ( j = FIRST_PLAYER; j < Game_Status.Get_Total_Players(); j ++ ) { + if ( j != i ) { + if ( Players[ j ].Get_Token() == token ) { + /* + Sound the alarm! + */ + Play_Sound( SND_GOTOJAIL, PLAY_KILL_PREVIOUS_SOUND, BROADCAST_SOUND ); + Debug_Error_Window_Message( "Emergency! The annoying Double-token bug has been encountered!\r\n" ); + Debug_Error_Window_Message( "Give the .000 log file from this system/game to the Mikes!" ); + Debug_Error_Window_Message( "Get the .000 log files from the other systems in this game, too." ); + Debug_Error_Window_Message( "Other data may be hosed, so this game is probably now invalid." ); + Debug_Error_Window_Message( "With the log file, we should be able to find the stinkin' bug." ); + Debug_Error_Window_Message( "Excellent work, Mr. Bond!" ); + } + } + } + } +#endif +} + +#if( RIG_ROLL_FOR_PLAYTEST ) +/*************************************************************************** + * DEBUG_RIG_ROLL -- Pops up dialog to fix the next dice roll * + * * + * INPUT: parent window handle * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +VOID Debug_Rig_Roll( WindowHandle window_handle ) +{ + if ( ! Game_Status.Is_Game_In_Progress() ) { + #if( DEBUG_CODE ) + Debug_Error_Window_Message( "Game not in progress!" ); + #endif + return; + } + + DialogBox( Global_Instance_Handle, + "DebugDiceDlgBox", + window_handle, + (DLGPROC) Debug_Rig_Roll_Dialog_Procedure ); + + #if( DEBUG_CODE ) + Debug_Mono_Message( "***** The next roll has been rigged! *****\r\n" ); + #endif + + #if( COMPILE_WINSOCK ) +// if ( Multiple_Site_Game_In_Progress ) { +// WinSock_Broadcast_Cmd_String( CMD_SEND_YELL_MESSAGE_STRING, +// Game_Status.Get_Whos_Turn_Is_It_Now(), +// "The next dice roll has been rigged!\r\n" ); +// } + #endif +} + +#pragma argsused +/*************************************************************************** + * DEBUG_RIG_ROLL_DIALOG_PROCEDURE -- Handles commands to dialog * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: TRUE if dice rigged, FALSE if not * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +PRIVATE BOOL Debug_Rig_Roll_Dialog_Procedure( WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ) +{ + INT die1, die2; + + switch( message ) { + + case WM_COMMAND: + + if ( LOWORD( w_param ) == IDOK ) { + + GetDlgItemText( window_handle, + DIE_EDIT1, + Text_String, + SIZE_OF_TEXT_STRING ); + die1 = atoi( Text_String ); + + GetDlgItemText( window_handle, + DIE_EDIT2, + Text_String, + SIZE_OF_TEXT_STRING ); + die2 = atoi( Text_String ); + + if ( die1 < 1 || die1 > 6 || die2 < 1 || die2 > 6 ) { + #if( DEBUG_CODE ) + Debug_Error_Window_Message( "Illegal values, defaulting to 1 & 1.\r\n" ); + #endif + die1 = 1; + die2 = 1; + } + + Dice.Set_Debug_Die( 1, die1 ); + Dice.Set_Debug_Die( 2, die2 ); + + sprintf( Text_String, + "The next dice roll is rigged for a %d and a %d.\r\n", + die1, die2 ); + Debug_Mono_Message( Text_String ); + + EndDialog( window_handle, TRUE ); + return( TRUE ); + } + break; + + default: break; + + } + return( FALSE ); +} +#endif + + + diff --git a/WINVQ/VQAVIEW/DEBUG.RC b/WINVQ/VQAVIEW/DEBUG.RC new file mode 100644 index 0000000..b920a8f --- /dev/null +++ b/WINVQ/VQAVIEW/DEBUG.RC @@ -0,0 +1,67 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Virtual Monopoly * + * * + * File Name : DEBUG.RC * + * * + * Programmer : Michael Legg * + * * + * Start Date : February 9, 1995 * + * * + * Last Update : May 30, 1995 [ML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "defines.h" +#include "debug.rh" + +#if( RIG_ROLL_FOR_PLAYTEST ) + +DebugDiceDlgBox DIALOG 152, 52, 109, 85 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Deciding Next Roll..." +FONT 8, "MS Sans Serif" +{ + PUSHBUTTON "Ready to roll...", IDOK, 18, 59, 72, 16 + EDITTEXT DIE_EDIT1, 57, 15, 25, 12 + EDITTEXT DIE_EDIT2, 57, 35, 25, 12 + LTEXT "Bone #2", -1, 26, 36, 29, 8 + LTEXT "Bone #1", -1, 26, 18, 29, 9 +} + +#endif + +#if( DEBUG_CODE ) + +DebugSetCashDlgBox DIALOG 152, 52, 106, 64 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Set Player's Cash" +FONT 8, "MS Sans Serif" +{ + DEFPUSHBUTTON "OK", IDOK, 31, 39, 43, 13 + EDITTEXT CASH_EDIT1, 32, 15, 41, 12 +} + +#endif + diff --git a/WINVQ/VQAVIEW/DIALOGS.RC b/WINVQ/VQAVIEW/DIALOGS.RC new file mode 100644 index 0000000..6341c75 --- /dev/null +++ b/WINVQ/VQAVIEW/DIALOGS.RC @@ -0,0 +1,48 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQA VIEWER * + * * + * File Name : DIALOGS.RC * + * * + * Programmer : Mike Grayford * + * * + * Start Date : Dec 5, 1995 * + * * + * Last Update : Dec 5, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "windows.h" +#include "dialogs.rh" + +GEEB DIALOG 152, 52, 106, 64 +//STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +STYLE WS_POPUP +CAPTION "Hi" +{ + DEFPUSHBUTTON "OK", IDOK, 31, 39, 43, 13 + EDITTEXT FRAME_RATE, 32, 15, 41, 12 +} + + diff --git a/WINVQ/VQAVIEW/DIALOGS.RH b/WINVQ/VQAVIEW/DIALOGS.RH new file mode 100644 index 0000000..1aec292 --- /dev/null +++ b/WINVQ/VQAVIEW/DIALOGS.RH @@ -0,0 +1,38 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQA VIEWER * + * * + * File Name : DIALOGS.RH * + * * + * Programmer : Mike Grayford * + * * + * Start Date : Dec 5, 1995 * + * * + * Last Update : Dec 5, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define FRAME_RATE 100 + diff --git a/WINVQ/VQAVIEW/GAMETIME.CPP b/WINVQ/VQAVIEW/GAMETIME.CPP new file mode 100644 index 0000000..c28d5e8 --- /dev/null +++ b/WINVQ/VQAVIEW/GAMETIME.CPP @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : GAMETIME.CPP * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//========================================================================== +// INCLUDES +//========================================================================== + +#include +#include + +//========================================================================== +// PUBLIC DATA +//========================================================================== + +GameTimeClass Game_Time; + +/*************************************************************************** + * GameTimeClass - Constructor function for GameTimeClass * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/22/1995 MG : Created. * + *=========================================================================*/ +GameTimeClass::GameTimeClass( void ) +{ + game_start_time = timeGetTime(); +} + + +/*************************************************************************** + * Get_Time - returns the time in ms elapsed since game was started * + * * + * INPUT: * + * * + * OUTPUT: * + * unsigned long - time in milliseconds since game was started * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/22/1995 MG : Created. * + *=========================================================================*/ +unsigned long GameTimeClass::Get_Time( void ) +{ + unsigned long curr_windows_time; + unsigned long game_time; + + curr_windows_time = timeGetTime(); + if ( curr_windows_time <= game_start_time ) { + // Handles the case if the windows time wraps while playing the game. + game_time = MAX_ULONG - game_start_time + curr_windows_time; + } + else { + game_time = curr_windows_time - game_start_time; + } + return( game_time ); +} + + +/*************************************************************************** + * Get_Game_Time - returns the time in ms elapsed since game was started * + * * + * INPUT: * + * * + * OUTPUT: * + * unsigned long - time in milliseconds since game was started * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/22/1995 MG : Created. * + *=========================================================================*/ +unsigned long Get_Game_Time( void ) +{ + return( Game_Time.Get_Time() ); +} + diff --git a/WINVQ/VQAVIEW/GAMETIME.H b/WINVQ/VQAVIEW/GAMETIME.H new file mode 100644 index 0000000..b20d3df --- /dev/null +++ b/WINVQ/VQAVIEW/GAMETIME.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : GAMETIME.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +extern unsigned long Get_Game_Time(); + +//========================================================================== +// PUBLIC DEFINES +//========================================================================== + +#define MAX_ULONG 0xFFFFFFFF + +//========================================================================== +// CLASSES +//========================================================================== + +class GameTimeClass { + private: + unsigned long game_start_time; + + public: + GameTimeClass( void ); + unsigned long Get_Time( void ); + +}; /* VQAClass */ + + +//========================================================================== +// PUBLIC DATA +//========================================================================== + +extern GameTimeClass Game_Time; + + diff --git a/WINVQ/VQAVIEW/INTERPAL.ASM b/WINVQ/VQAVIEW/INTERPAL.ASM new file mode 100644 index 0000000..832acba --- /dev/null +++ b/WINVQ/VQAVIEW/INTERPAL.ASM @@ -0,0 +1,162 @@ +; +; 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 . +; + + +;********************************************************************************************** +;** 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 I N C ** +;********************************************************************************************** +;* * +;* Project Name : VQA Viewer * +;* * +;* File Name : INTERPAL.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : December 15th, 1995 * +;* * +;* Last Update : December 22nd, 1995 [ST] * +;* * +;*--------------------------------------------------------------------------------------------* +;* Functions: * +;* * +;* Asm_Interpolate -- interpolate a 320x200 buffer to a 640x400 screen * +;* * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + + + IDEAL + P386 + MODEL USE32 FLAT + + + segment mycode page public use32 'code' ; Need stricter segment alignment + +global C Asm_Interpolate:near +global C Palette_Interpolation_Table:byte + + + + + +;********************************************************************************************* +;* Asm_Interpolate -- interpolate a 320x200 buffer to a 640x400 screen * +;* * +;* INPUT: ptr to source buffer (320x200 image) * +;* ptr to dest buffer (640x400) * +;* height of source buffer * +;* width f source buffer * +;* width of dest buffer * +;* * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 12/15/95 ST : Created. * +;*===========================================================================================* + +PROC Asm_Interpolate C near + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL width_counter:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + +??each_line_loop: + mov [width_counter],0 + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov edi,[old_dest] + jmp ??interpolate_loop + + align 32 +; +; convert 2 pixels of source into 4 pixels of destination +; so we can write to video memory with dwords +; +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+Palette_Interpolation_Table] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + +;1st 3 pixels now in ebx + + shr eax,8 + mov bh,[eax+Palette_Interpolation_Table] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank on the end of a row + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + lea esi,[esi+2] + mov al,[eax+Palette_Interpolation_Table] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + mov edi,[dest_width] + add [old_dest],edi + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate + + + + +ends mycode + +end + + diff --git a/WINVQ/VQAVIEW/MAIN.CPP b/WINVQ/VQAVIEW/MAIN.CPP new file mode 100644 index 0000000..88afbf4 --- /dev/null +++ b/WINVQ/VQAVIEW/MAIN.CPP @@ -0,0 +1,142 @@ +/* +** 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 . +*/ + +/********************************************************************************************** + *** 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 : VQAVIEW * + * * + * File Name : MAIN.CPP * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 14, 1995 * + * * + * Last Update : November 20, 1995 [MG] * + * * + *--------------------------------------------------------------------------------------------* + * Functions: * + * WinMain -- Program entry point * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + + +//--------------------------------------------------------------------------------------------- +// INCLUDES +//--------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +extern void VQA_Test( char *filename ); + +//========================================================================== +// Public data +//========================================================================== + +GraphicBufferClass *Screen_Buffer = NULL; +int ScreenWidth = VIDEO_MODE_WIDTH; + +BOOL GameInFocus = TRUE; +void Block_Mouse(GraphicBufferClass*){} +void Unblock_Mouse(GraphicBufferClass*){} + + +/*********************************************************************************************** + * WinMain -- Program entry point * + * * + * * + * * + * INPUT: Standard Windows startup parameters * + * * + * OUTPUT: wParam of the message queue * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/15/95 1:28PM ST : Created * + *=============================================================================================*/ + +int WINAPI WinMain ( + Handle instance_handle, + Handle prev_instance_handle, + String command_line_string, + int show_window_command ) +{ + if (!prev_instance_handle){ + // + // If the program is already running, bail. + // + if ( prev_instance_handle ) { + return( 0 ); + } + + // + // Create the main window. + // + Main_Window.Create_Main_Window( instance_handle ); + + // + // Show the window. + // + Main_Window.Display_Window(); + + // + // Create the GraphicBufferClass that will be the screen buffer + // + Screen_Buffer = new GraphicBufferClass ( VIDEO_MODE_WIDTH, VIDEO_MODE_HEIGHT, (GBC_Enum)(GBC_VIDEOMEM | GBC_VISIBLE) ); + + // + // Initialize Movie system. + // + Initialize_Movie_System(); + + if (*command_line_string){ + VQA_Test(command_line_string); + }else{ + // + // Windows message loop + // + while ( Main_Window.Update_Windows_Messages() ) ; + } + + // + // Free the Movie system. + // + Free_Movie_System(); + + return( Main_Window.Get_Message_Queue_wParam() ); + }else{ + return (-1); + } + +} + + diff --git a/WINVQ/VQAVIEW/MAIN.H b/WINVQ/VQAVIEW/MAIN.H new file mode 100644 index 0000000..a2566e7 --- /dev/null +++ b/WINVQ/VQAVIEW/MAIN.H @@ -0,0 +1,49 @@ +/* +** 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 . +*/ + +/********************************************************************************************** + *** 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 : VQAVIEW * + * * + * File Name : MAIN.H * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 20, 1995 * + * * + * Last Update : November 20, 1995 [MG] * + * * + *--------------------------------------------------------------------------------------------* + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +//========================================================================== +// INCLUDES +//========================================================================== + +#include + +//========================================================================== +// PUBLIC DATA +//========================================================================== + +extern GraphicBufferClass *Screen_Buffer; + + diff --git a/WINVQ/VQAVIEW/MAINWIND.CPP b/WINVQ/VQAVIEW/MAINWIND.CPP new file mode 100644 index 0000000..080467e --- /dev/null +++ b/WINVQ/VQAVIEW/MAINWIND.CPP @@ -0,0 +1,209 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MAINWIND.CPP * + * * + * Programmer : Mike Grayford * + * * + * Start Date : Nov 15, 1995 * + * * + * Last Update : Nov 15, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include + +#include +#include +#include +#include +#include +#include + +//========================================================================== +// Private defines +//========================================================================== + +#define APPLICATION_NAME "VQAVIEW" +#define APPLICATION_TITLE "VQA Movie Viewer" + + +//========================================================================== +// Public data +//========================================================================== + +MainWindowClass Main_Window; + +//========================================================================== +// Private functions +//========================================================================== + +long WINAPI Main_Window_Message_Handler( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ); + + +/*************************************************************************** + * MainWindowClass::MainWindowClass -- Constructor for MainWindowClass * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: See PVCS log * + *=========================================================================*/ +MainWindowClass::MainWindowClass( void ) +{ + // Much ado about nothing. +} + + +void MainWindowClass::Create_Main_Window( InstanceHandle instance_handle ) +{ + WindowClass window_class; + + // + // Register the window class. + // + window_class.style = CS_OWNDC; + window_class.lpfnWndProc = Main_Window_Message_Handler; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = instance_handle; + window_class.hIcon = LoadIcon ( NULL, IDI_APPLICATION ); + window_class.hCursor = LoadCursor ( NULL, IDC_ARROW ); + window_class.hbrBackground = GetStockObject( BLACK_BRUSH ); + window_class.lpszMenuName = APPLICATION_NAME; + window_class.lpszClassName = APPLICATION_NAME; + + RegisterClass ( &window_class ); + + // + // Create our main window + // + main_window_handle = CreateWindowEx ( + WS_EX_TOPMOST, // extended window info + APPLICATION_NAME, // window class name + APPLICATION_TITLE, // window caption +// WS_POPUP | WS_MAXIMIZE, // window style + WS_OVERLAPPED, // window style + 0, // initial x position + 0, // initial y position + VIDEO_MODE_WIDTH, // initial width + VIDEO_MODE_HEIGHT, // initial height + NULL, // parent window handle + NULL, // window menu handle + instance_handle, // program instance handle + NULL ); // creation parameters + + MainWindow = main_window_handle; + + global_instance_handle = instance_handle; +} + + +void MainWindowClass::Display_Window( void ) +{ + // + // Bring up the window, force a paint, and make sure we are the foreground window. + // + ShowWindow( main_window_handle, SW_SHOWMAXIMIZED ); + UpdateWindow( main_window_handle ); + SetForegroundWindow( main_window_handle ); + + // + // Via direct draw, set the video mode. + // + Set_Video_Mode( main_window_handle, VIDEO_MODE_WIDTH, VIDEO_MODE_HEIGHT, VIDEO_MODE_BITS_PER_PIXEL ); + +} + + +BOOL MainWindowClass::Update_Windows_Messages( void ) +{ + if ( PeekMessage( &message_queue, NULL, 0, 0, PM_NOREMOVE ) ) { + if ( GetMessage( &message_queue, NULL, 0, 0 ) ) { + TranslateMessage( &message_queue ); + DispatchMessage( &message_queue ); + } + else { + return( FALSE ); + } + } + + return( TRUE ); +} + + +int MainWindowClass::Get_Message_Queue_wParam( void ) +{ + return( message_queue.wParam ); +} + + +WindowHandle MainWindowClass::Get_Window_Handle( void ) +{ + return( main_window_handle ); +} + + +long WINAPI Main_Window_Message_Handler( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ) +{ + switch ( message ) { + + case WM_COMMAND: + return( WM_Command_Func( window_handle, message, w_param, l_param ) ); + + case WM_TIMER: + break; + + case WM_ACTIVATEAPP: + WM_ActivateApp_Func( window_handle, message, w_param, l_param ); + break; + + case WM_DESTROY: + WM_Destroy_Func( window_handle, message, w_param, l_param ); + return( 0 ); + } + + return( DefWindowProc( window_handle, message, w_param, l_param ) ); +} + + +InstanceHandle MainWindowClass::Get_Instance_Handle( void ) +{ + return( global_instance_handle ); +} + + diff --git a/WINVQ/VQAVIEW/MAINWIND.H b/WINVQ/VQAVIEW/MAINWIND.H new file mode 100644 index 0000000..83a32a9 --- /dev/null +++ b/WINVQ/VQAVIEW/MAINWIND.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MAINWIND.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : Nov 15, 1995 * + * * + * Last Update : Nov 15, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// Public classes +//========================================================================== + +class MainWindowClass { + public: + + //========================================================================== + // Public functions + //========================================================================== + + MainWindowClass( void ); + void Create_Main_Window( InstanceHandle instance_handle ); + WindowHandle Get_Window_Handle( void ); + void Display_Window( void ); + BOOL Update_Windows_Messages( void ); + int Get_Message_Queue_wParam( void ); + InstanceHandle Get_Instance_Handle( void ); + + private: + + //========================================================================== + // Private data + //========================================================================== + + WindowHandle main_window_handle; + InstanceHandle global_instance_handle; + MessageQueue message_queue; + + //========================================================================== + // Private functions + //========================================================================== + +}; /* VQAClass */ + + +//========================================================================== +// Public data +//========================================================================== + +extern MainWindowClass Main_Window; + + diff --git a/WINVQ/VQAVIEW/MAKEFILE b/WINVQ/VQAVIEW/MAKEFILE new file mode 100644 index 0000000..50ec54d --- /dev/null +++ b/WINVQ/VQAVIEW/MAKEFILE @@ -0,0 +1,200 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Blade Runner * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Mike Grayford * +#* * +#* Start Date : Nov 14, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WIN32LIB = the root of the WW library * +#* WIN32VCS = root directory for WW library version control archive * +#* COMPILER = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WIN32LIB +!error WIN32LIB Environment var not configured. +!endif + +!ifndef %WIN32VCS +!error WIN32VCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = VQAVIEW +PROJ_DIR = $(%BLADE)\VQA\VQAVIEW +LIB_DIR = $(%WIN32LIB)\LIB +VQA_LIB_DIR = $(%BLADE)\VQA\LIB + +!include $(%WIN32LIB)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + gametime.obj & + main.obj & + mainwind.obj & + monochrm.obj & + movies.obj & + pal.obj & + vq.obj & + wm.obj & + interpal.obj + +RESOURCES = & + dialogs.res & + menus.res + +PROJ_LIBS = & + drawbuff.lib & + mem.lib & + misc.lib & + rawfile.lib + +VQA_LIBS = & + vqa32wp.lib & + vqm32wp.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- + +.EXTENSIONS: .res .rc + +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WIN32LIB)\lib;$(VQA_LIB_DIR) +.exe: $(PROJ_DIR) +.rc: $(PROJ_DIR) +.res: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm +RC_CMD = wrc + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WIN32LIB)\LIB;$(%WATCOM)\LIB386\NT;$(BLADE)\VQA\LIB +INCLUDEPATH = $(PROJ_DIR);$(%WIN32LIB)\INCLUDE;$(%WATCOM)\H;$(PROJ_DIR)\..;$(PROJ_DIR)\..\VQA32 + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WIN32LIB)\project.cfg .AUTODEPEND + $(CPP_CMD) -db $(CC_CFG) /i=$(INCLUDEPATH) $< + +.asm.obj: $(%WIN32LIB)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +.rc.res: + $(RC_CMD) /r $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) $(VQA_LIBS) $(RESOURCES) + $(LINK_CMD) $(LINK_CFG) name $^@ @$(PROJ_NAME).lnk + for %index in ($(RESOURCES)) do $(RC_CMD) -t %index $(PROJ_NAME).exe + + +#$(PROJ_LIBS): +# echo updating base library $^@ +# cd .. +# wmake +# cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) makefile + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + %append $^@ library $(LIB_DIR)\ddraw.lib + %append $^@ library $(LIB_DIR)\dsound.lib + for %index in ($(VQA_LIBS)) do %append $^@ library $(VQA_LIB_DIR)\%index + +#**************************** End of makefile ****************************** + diff --git a/WINVQ/VQAVIEW/MENUS.RC b/WINVQ/VQAVIEW/MENUS.RC new file mode 100644 index 0000000..31650cf --- /dev/null +++ b/WINVQ/VQAVIEW/MENUS.RC @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MENUS.RC * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 22, 1995 * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "menus.rh" + +VQAVIEW MENU +BEGIN + POPUP "File" + BEGIN + MENUITEM "&Open movie...", MENU_OPEN + MENUITEM SEPARATOR + MENUITEM "E&xit", MENU_EXIT + END + + POPUP "Options" + BEGIN + MENUITEM "Set Movie Frame &Rate", MENU_SET_MOVIE_FRAME_RATE + END +END + diff --git a/WINVQ/VQAVIEW/MENUS.RH b/WINVQ/VQAVIEW/MENUS.RH new file mode 100644 index 0000000..9a70433 --- /dev/null +++ b/WINVQ/VQAVIEW/MENUS.RH @@ -0,0 +1,48 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Virtual Monopoly * + * * + * File Name : MENUS.RH * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 22, 1995 * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* MENU BAR */ +#define MENU_BAR_FILE 0 + +/* FILE */ +#define MENU_OPEN 100 +#define MENU_EXIT 110 + +/* OPTIONS */ +#define MENU_SET_MOVIE_FRAME_RATE 200 + + + diff --git a/WINVQ/VQAVIEW/MONOCHRM.CPP b/WINVQ/VQAVIEW/MONOCHRM.CPP new file mode 100644 index 0000000..74a1646 --- /dev/null +++ b/WINVQ/VQAVIEW/MONOCHRM.CPP @@ -0,0 +1,261 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MONOCHRM.CPP * + * * + * Programmer : Michael Legg * + * * + * Start Date : May 10, 1995 * + * * + * Last Update : May 30, 1995 [ML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * These are not our functions, but Microsoft's from DBWIN.EXE * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + +typedef struct +{ + char ch; + char attr; +} CA; + +#define CCOLMAX 80 +#define CROWMAX 25 + +#define ESC 27 +#define BELL 7 +#define DEFATTR 0x07 +#define CA_SPACE ((DEFATTR << 8) | ' ') + +// row+1 = DBWIN +#define PCA(row, col) (0xB0000 + ( (((row+0)*CCOLMAX)+col) * sizeof(CA))) + +static void Monochrome_Output( char *string ); +static void CACopy( CA *pcaDst, CA *pcaSrc, int cca ); +static void CAFill( CA *pcaDst, int cca, int ca ); + +int rowCur = 0; +int colCur = 0; + +/*************************************************************************** + * DEBUG_PRINTF - works like printf, with limit of 10 variable args * + * * + * INPUT: same as printf * + * * + * OUTPUT: a message on the monochrome monitor display * + * * + * WARNINGS: try to end messages with \r\n! * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +void Debug_Printf( char *format_string, ... ) +{ + va_list ap; + int arg[ 10 ]; + char output_string[ 255 ]; + + va_start( ap, format_string ); + arg[ 0 ] = va_arg( ap, int ); + arg[ 1 ] = va_arg( ap, int ); + arg[ 2 ] = va_arg( ap, int ); + arg[ 3 ] = va_arg( ap, int ); + arg[ 4 ] = va_arg( ap, int ); + arg[ 5 ] = va_arg( ap, int ); + arg[ 6 ] = va_arg( ap, int ); + arg[ 7 ] = va_arg( ap, int ); + arg[ 8 ] = va_arg( ap, int ); + arg[ 9 ] = va_arg( ap, int ); + va_end( ap ); + + sprintf( (char *) output_string, + (char *) format_string, + arg[ 0 ], + arg[ 1 ], + arg[ 2 ], + arg[ 3 ], + arg[ 4 ], + arg[ 5 ], + arg[ 6 ], + arg[ 7 ], + arg[ 8 ], + arg[ 9 ] ); + + Debug_Mono_Message( (char *) output_string ); +} + +/*************************************************************************** + * DEBUG_MONO_MESSAGE -- Post a line to the monochrome monitor * + * * + * INPUT: the message to print * + * * + * OUTPUT: a message on the monochrome monitor display * + * * + * WARNINGS: try to end messages with \r\n! * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +void Debug_Mono_Message( char *message ) +{ + char *temp_string; + char *temp_null_position; + char temp_char; + int length_left; + + length_left = strlen( message ); + temp_null_position = message; + while ( length_left > CCOLMAX ) { + temp_string = temp_null_position; + temp_null_position += CCOLMAX; + temp_char = *temp_null_position; + temp_null_position[ 0 ] = '\0'; + + Monochrome_Output( temp_string ); + + *temp_null_position = temp_char; + length_left -= CCOLMAX; + } + temp_string = temp_null_position; + + Monochrome_Output( temp_string ); +} + +static void Monochrome_Output( char *string ) +{ + CA *pca; + char ch; + static int _initialized = FALSE; + + pca = (CA *) PCA(rowCur, colCur); + + if ( ! _initialized ) { + CAFill( (CA *) PCA(0, 0), + (CCOLMAX * CROWMAX), + CA_SPACE ); + colCur = 0; + rowCur = 0; + _initialized = TRUE; + } + + while ( TRUE ) { + + ch = *string++; + if ( ! ch ) { + break; + } + + switch (ch) { + + case '\b': + if (colCur > 0) + { + colCur--; + pca--; + pca->ch = ' '; + pca->attr = DEFATTR; + } + break; + + case BELL: +// MessageBeep(0); + break; + + case '\t': + pca += 8 - colCur % 8; + colCur += 8 - colCur % 8; + break; + + case '\r': + colCur = 0; + pca = (CA *) PCA(rowCur, colCur); + break; + + default: + pca->ch = ch; + pca->attr = DEFATTR; + pca++; + colCur++; + + if (colCur < CCOLMAX) + break; + + // fall through to handle LF + + case '\n': + colCur = 0; + rowCur++; + + if (rowCur >= CROWMAX) + { + CACopy( (CA *) PCA(0, 0), (CA *) PCA(1, 0), CCOLMAX * (CROWMAX - 1)); + CAFill( (CA *) PCA(CROWMAX - 1, 0), CCOLMAX, CA_SPACE); + rowCur = CROWMAX - 1; + } + + pca = (CA *) PCA(rowCur, colCur); + break; + + case ESC: + // + // ANSI clear screen escape + // + if (string[1] == '[' && string[2] == '2' && string[3] == 'J') + { + CAFill( (CA *)PCA(0,0), CCOLMAX * CROWMAX, CA_SPACE); + rowCur = colCur = 0; + string += 3; + } + } + } +} + +static void CACopy( CA *pcaDst, CA *pcaSrc, int cca ) +{ + int i; + + for ( i = 0; i < cca; i ++ ) { + *pcaDst = *pcaSrc; + pcaDst ++; + pcaSrc ++; + } +} + +static void CAFill( CA *pcaDst, int cca, int ca ) +{ + int i; + + for ( i = 0; i < cca; i ++ ) { + memcpy( pcaDst, &ca, sizeof( int ) ); + pcaDst ++; + } +} diff --git a/WINVQ/VQAVIEW/MONOCHRM.H b/WINVQ/VQAVIEW/MONOCHRM.H new file mode 100644 index 0000000..6c072ae --- /dev/null +++ b/WINVQ/VQAVIEW/MONOCHRM.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MONOCHRM.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 24, 1995 [ML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOCHRM_H +#define MONOCHRM_H + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +void Debug_Printf( char *format_string, ... ); +void Debug_Mono_Message( char *message ); + +#endif diff --git a/WINVQ/VQAVIEW/MOVIES.CPP b/WINVQ/VQAVIEW/MOVIES.CPP new file mode 100644 index 0000000..5dbcf4a --- /dev/null +++ b/WINVQ/VQAVIEW/MOVIES.CPP @@ -0,0 +1,739 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : MOVIES.CPP * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 29, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// INCLUDES +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include + +//========================================================================== +// PRIVATE FUNCTIONS +//========================================================================== + +void VQA_Test( char *filename ); +void Create_Palette_Interpolation_Table( void ); +void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest ); + + +//========================================================================== +// PRIVATE GLOBALS +//========================================================================== + +extern "C"{ + extern unsigned char *InterpolationPalette; + extern unsigned char Palette_Interpolation_Table[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern void __cdecl Asm_Create_Palette_Interpolation_Table(void); + extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} +unsigned char Palette_Interpolation_Table[ SIZE_OF_PALETTE ][ SIZE_OF_PALETTE ]; +unsigned char *InterpolationPalette; + +VQAClass *TestVqa; + +#if( ! DRAW_TO_VIDEO ) + GraphicBufferClass *Draw_Page = NULL; + GraphicBufferClass *Back_Page = NULL; + GraphicBufferClass *Hid_Page = NULL; +#endif + + + +/*************************************************************************** + * Initialize_Movie_System - Allocate memory needed for caching and any * + * permanent Movie data. * + * * + * INPUT: NONE * + * * + * OUTPUT: int - an error value * + * * + * WARNINGS: Yellow alert! * + * * + * HISTORY: * + * 09/29/1995 MG : Created. * + *=========================================================================*/ +int Initialize_Movie_System( void ) +{ + #if( ! DRAW_TO_VIDEO ) + // + // Set up the graphic pages: + // Draw_Page - the page that the VQA library draws to. (Normal RAM) + // Back_Page - the page that the drawn image is scaled to. (Normal RAM) + // Hid_Page - the page in video RAM that the scaled image is copied to before copy to screen. + // + Draw_Page = new GraphicBufferClass ( MOVIE_WIDTH, MOVIE_HEIGHT ); + Back_Page = new GraphicBufferClass ( VIDEO_MODE_WIDTH, VIDEO_MODE_HEIGHT ); + Hid_Page = new GraphicBufferClass ( VIDEO_MODE_WIDTH, VIDEO_MODE_HEIGHT, (GBC_Enum)(GBC_VIDEOMEM) ); + #endif + + // + // Okay, we're cool. + // + return( VQA_INIT_NO_ERROR ); +} + +/*************************************************************************** + * Free_Movie_System - Free up memory used by Movie cache system and any * + * permanent Movie data. * + * * + * INPUT: NONE * + * * + * OUTPUT: NONE * + * * + * WARNINGS: Red alert! * + * * + * HISTORY: * + * 09/29/1995 MG : Created. * + *=========================================================================*/ +void Free_Movie_System( void ) +{ + #if( ! DRAW_TO_VIDEO ) + if ( Draw_Page ) { + delete( Draw_Page ); + } + if ( Back_Page ) { + delete( Back_Page ); + } + if ( Hid_Page ) { + delete( Hid_Page ); + } + Draw_Page = NULL; + Back_Page = NULL; + Hid_Page = NULL; + #endif + + return; +} + + +/*************************************************************************** + * Choose_Movie - Brings up choice of VQA files. * + * * + * INPUT: NONE * + * * + * OUTPUT: NONE * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/15/1995 MG : Created. * + *=========================================================================*/ +void Choose_Movie( WindowHandle window_handle ) +{ + int i; + char temp_file[_MAX_FNAME + _MAX_EXT]; + static OPENFILENAME open_file_dlg; + static char path_filename[ _MAX_PATH*500 ]; + static char filename[ _MAX_FNAME + _MAX_EXT ]; + static char *filters[] = { + "VQA files (*.VQA)", "*.vqa", + "All Files", "*.*", + "" } ; + + filename[0]=0; + memset (&path_filename[0],0,_MAX_PATH*500); + + open_file_dlg.lStructSize = sizeof( OPENFILENAME ); + open_file_dlg.hwndOwner = window_handle; + open_file_dlg.hInstance = NULL; + open_file_dlg.lpstrFilter = filters[ 0 ]; + open_file_dlg.lpstrCustomFilter = NULL; + open_file_dlg.nMaxCustFilter = 0; + open_file_dlg.nFilterIndex = 1; + open_file_dlg.lpstrFile = path_filename; + open_file_dlg.nMaxFile = _MAX_PATH*500; + open_file_dlg.lpstrFileTitle = filename; + open_file_dlg.nMaxFileTitle = _MAX_FNAME + _MAX_EXT; + open_file_dlg.lpstrInitialDir = NULL; + open_file_dlg.lpstrTitle = "Choose a VQA to run"; + open_file_dlg.Flags = OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT; + open_file_dlg.nFileOffset = 0; + open_file_dlg.nFileExtension = 0; + open_file_dlg.lpstrDefExt = "vqa"; + open_file_dlg.lCustData = 0l; + open_file_dlg.lpfnHook = NULL; + open_file_dlg.lpTemplateName = NULL; + + if ( GetOpenFileName( &open_file_dlg ) ) { + + if ( filename[ 0 ] != '\0' ) { + VQA_Test( filename ); + } else { + + int last_file=0; + for (i=0 ; iLock(); + draw_surface_ptr = (char *) draw_buffer_ptr->Get_Offset(); + draw_buffer_ptr->Unlock(); + + // + // Clear the drawing surface with color 0. + // + draw_buffer_ptr->Lock(); + temp_ptr = draw_surface_ptr; + for ( i = 0; i < (MOVIE_WIDTH * MOVIE_HEIGHT); i ++ ) { + *temp_ptr = COLOR_BLACK; + temp_ptr ++; + } + draw_buffer_ptr->Unlock(); + + // + // Create a new VQA object. + // + TestVqa = new VQAClass( + filename, + draw_surface_ptr, + FROM_DISK, + callback_function_ptr ); + + // + // Allocate the buffers for the VQA. + // + if ( TestVqa->Open_And_Load_Buffers() ) { + + TestVqa->Read_Palettes(); + + // + // Play the VQA. + // + TestVqa->Play_VQA( PLAY_ALL_FRAMES ); + + // + // Close the VQA. + // + TestVqa->Close_And_Free_VQA(); + + TestVqa->Write_Palettes(); + + delete TestVqa; + } +} + + +/*************************************************************************** + * DRAW_TO_VIDEO_CALLBACK -- Callback after a VQA frame is drawn to video * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/22/1995 MG : Created. * + *=========================================================================*/ +long Draw_To_Video_Callback( unsigned char *buffer, long frame_number ) +{ + return( 0 ); +} + + + +/*************************************************************************** + * DRAW_TO_BUFFER_CALLBACK -- Copies the drawn frame to the screen * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/22/1995 MG : Created. * + *=========================================================================*/ +long Draw_To_Buffer_Callback( unsigned char *buffer, long frame_number ) +{ + // + // Frame was skipped, bail. + // + if ( buffer == NULL ) { + return( 0 ); + } + + #if( ! DRAW_TO_VIDEO ) + + // + // Do the cool interpolated, interlaced scale. + // + #if( 1 ) + Interpolate_2X_Scale( Draw_Page, Back_Page ); + Back_Page->Blit( *Screen_Buffer, 0, 0, 0, 100, 640, 314 ); + #endif + + // + // Draw 320x200 to Normal RAM, scale to Screen. + // + #if( 0 ) + Draw_Page->Scale( *Screen_Buffer ); + #endif + + // + // Draw 320x200 to Normal RAM, scale to Normal RAM, Blit to Hid, Blit to Screen. + // + #if( 0 ) + Draw_Page->Scale( *Back_Page ); + Back_Page->Blit( *Hid_Page ); + Hid_Page->Blit( *Screen_Buffer ); + #endif + + // + // Draw 320x200 to Normal RAM, scale to Normal RAM, Blit to Screen. + // + #if( 0 ) + Draw_Page->Scale( *Back_Page ); + Back_Page->Blit( *Screen_Buffer ); + #endif + + // + // Draw 320x200 to Normal RAM, Blit to Screen. + // + #if( 0 ) + Draw_Page->Blit( *Screen_Buffer ); + #endif + + #endif + + + return( 0 ); +} + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + +// InterpolationPalette = (unsigned char*)CurrentPalette; +// Asm_Create_Palette_Interpolation_Table(); +//#if 0 + int i; + int j; + int p; + unsigned char *first_palette_ptr; + unsigned char *second_palette_ptr; + unsigned char *match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) CurrentPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) CurrentPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) CurrentPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + Palette_Interpolation_Table[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + return; +//#endif +} + + + + +#if( 1 ) +/*************************************************************************** + * INTERPOLATE_2X_SCALE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest) +{ + unsigned char *src_ptr; + unsigned char *dest_ptr; + unsigned char *last_dest_ptr; + unsigned char *end_of_source; + int src_width; + int dest_width; +// int width_counter; + BOOL source_locked = FALSE; + BOOL dest_locked = FALSE; + + /* + ** Lock video surfaces if requred + */ + if (source->Get_IsDirectDraw()){ + if (!source->Lock()) return; + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()){ + if (!dest->Lock()) { + if (source_locked){ + source->Unlock(); + } + return; + } + dest_locked = TRUE; + } + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + + return; +} +#endif + + + + +#if( 0 ) +/*************************************************************************** + * INTERPOLATE_2X_SCALE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest ) +{ + unsigned char *src_ptr; + unsigned char *dest_ptr; + unsigned char *end_of_source; + int src_width; + int dest_width; + int width_counter; + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = dest->Get_Width(); + + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = Palette_Interpolation_Table[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + dest_ptr += dest_width; + } + } + + + return; +} +#endif + + +#if( 0 ) +/*************************************************************************** + * INTERPOLATE_2X_SCALE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest ) +{ + unsigned char *src_ptr; + unsigned char *dest_ptr; + unsigned char *dest2_ptr; + unsigned char *end_of_source; + int src_width; + int dest_width; + int width_counter; + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = dest->Get_Width(); + + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr += 2; + dest2_ptr = dest_ptr + dest_width + 1; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest2_ptr = Palette_Interpolation_Table[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest2_ptr += 2; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr += 2; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + dest_ptr += dest_width; + dest2_ptr += dest_width; + } + } + + + return; +} + +#endif + + diff --git a/WINVQ/VQAVIEW/MOVIES.H b/WINVQ/VQAVIEW/MOVIES.H new file mode 100644 index 0000000..038184d --- /dev/null +++ b/WINVQ/VQAVIEW/MOVIES.H @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : VQ.CPP * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : May 31, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Choose_VQA - Brings up choice of VQA files. * + * Draw_To_Video_Callback -- callback after draw VQA frame to video * + * Draw_To_Buffer_Callback -- callback after draw VQA frame to buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +extern int Initialize_Movie_System( void ); +extern void Free_Movie_System( void ); +extern void Choose_Movie( WindowHandle window_handle ); +extern long Draw_To_Video_Callback( unsigned char *buffer, long frame_number ); +extern long Draw_To_Buffer_Callback( unsigned char *buffer, long frame_number ); +extern void Create_Palette_Interpolation_Table( void ); + +//========================================================================== +// PRIVATE DEFINES +//========================================================================== + +#define DRAW_TO_VIDEO FALSE + + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +#if( DRAW_TO_VIDEO ) + +#define MOVIE_WIDTH 640 +#define MOVIE_HEIGHT 480 + +#else + +#define MOVIE_WIDTH 320 +#define MOVIE_HEIGHT 200 + +#endif + diff --git a/WINVQ/VQAVIEW/PAL.CPP b/WINVQ/VQAVIEW/PAL.CPP new file mode 100644 index 0000000..93c1570 --- /dev/null +++ b/WINVQ/VQAVIEW/PAL.CPP @@ -0,0 +1,114 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : PAL.CPP * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 20, 1995 * + * * + * Last Update : Nov 20, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * Update_Full_Palette -- Modifies the Windows palette * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//--------------------------------------------------------------------------------------------- +// PUBLIC DATA +//--------------------------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// PRIVATE GLOBALS +//--------------------------------------------------------------------------------------------- + + +/*************************************************************************** + * Update_Full_Palette -- Modifies the Windows palette * + * * + * INPUT: unsigned char *palette - pointer to the entire raw palette * + * * + * OUTPUT: NONE * + * * + * WARNINGS: Watch out for falling bricks. * + * * + * HISTORY: * + * 11/27/1995 MG : Created. * + *=========================================================================*/ +void Update_Full_Palette( unsigned char *palette ) +{ + PALETTEENTRY pe[ SIZE_OF_PALETTE ]; + unsigned char *pal_pos; + int i; + + pal_pos = palette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + pe[ i ].peRed = *pal_pos; + pal_pos ++; + pe[ i ].peGreen = *pal_pos; + pal_pos ++; + pe[ i ].peBlue = *pal_pos; + pal_pos ++; + } + + Mem_Copy( palette, CurrentPalette, SIZE_OF_PALETTE * 3 ); + + DirectDrawObject->CreatePalette( DDPCAPS_8BIT, &pe[ 0 ], &PalettePtr, NULL ); + Screen_Buffer->Get_Graphic_Buffer()->Get_DD_Surface()->SetPalette( PalettePtr ); + PalettePtr->SetEntries( DDPSETPAL_VSYNC , 0 , SIZE_OF_PALETTE , &pe[ 0 ] ); +} + + + + + + +extern VQAClass *TestVqa; + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette,long ,unsigned long ); +} + +void __cdecl SetPalette(unsigned char *palette,long ,unsigned long ) +{ + TestVqa->Update_Palette(palette); +} + + + + diff --git a/WINVQ/VQAVIEW/PAL.H b/WINVQ/VQAVIEW/PAL.H new file mode 100644 index 0000000..f8d27a4 --- /dev/null +++ b/WINVQ/VQAVIEW/PAL.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : PAL.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : Nov 27, 1995 * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +void Update_Full_Palette( unsigned char *palette ); + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +extern "C" { + char CurrentPalette[ SIZE_OF_PALETTE * 3 ]; +} + +//========================================================================== +// PUBLIC DEFINES +//========================================================================== + +#define COLOR_BLACK 0 + diff --git a/WINVQ/VQAVIEW/TEST.MAK b/WINVQ/VQAVIEW/TEST.MAK new file mode 100644 index 0000000..676ffc2 --- /dev/null +++ b/WINVQ/VQAVIEW/TEST.MAK @@ -0,0 +1,21 @@ +# +# 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 . +# + +.rc.res: + hello + \ No newline at end of file diff --git a/WINVQ/VQAVIEW/VIDMODE.H b/WINVQ/VQAVIEW/VIDMODE.H new file mode 100644 index 0000000..5bae048 --- /dev/null +++ b/WINVQ/VQAVIEW/VIDMODE.H @@ -0,0 +1,25 @@ +/* +** 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 . +*/ + + +#define VIDEO_MODE_WIDTH 640 +#define VIDEO_MODE_HEIGHT 480 +#define VIDEO_MODE_BITS_PER_PIXEL 8 +#define SIZE_OF_PALETTE 256 + + diff --git a/WINVQ/VQAVIEW/VQ.CPP b/WINVQ/VQAVIEW/VQ.CPP new file mode 100644 index 0000000..5ba4fbe --- /dev/null +++ b/WINVQ/VQAVIEW/VQ.CPP @@ -0,0 +1,1066 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : VQ.CPP * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : May 31, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Initialize_VQA_System - Allocate memory needed for caching and any * + * permanent VQA data. * + * Free_VQA_System - Free up memory used by VQA cache system and any * + * permanent VQA data. * + * Disk_VQA_Stream_Handler -- Handles file io for VQAs run from disk. * + * VQ_Drawer_Callback -- Blits the frame to the screen. * + * VQAClass::VQAClass -- Constructor for VQAClass object. * + * VQAClass::Update_Palette -- Updates the system palette. * + * VQAClass::Open_And_Load_Buffers -- Opens VQA file, fills frame buffers* + * VQAClass::Seek_To_Frame -- Performs file seek to specified frame. * + * VQAClass::Play_VQA - Plays from the current frame of the VQA up to * + * and including the last frame specified. * + * VQAClass::Play_Generic_VQA -- Private func to play any Monopoly VQA. * + * VQAClass::Play_VQA_Frame - Plays the specified frame,seek if necessary* + * VQAClass::Pause_VQA - Pauses a VQA in order to freeze the VQA timers. * + * VQAClass::Close_And_Free_VQA -- Closes vqa, frees instanc, frees cache* + * Check_Key -- NULL function for VQA play library. * + * Get_Key -- NULL function for VQA play library. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//========================================================================== +// INCLUDES +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//========================================================================== +// PRIVATE DEFINES +//========================================================================== + +#define DEBUG_CODE TRUE +#define DEBUG_MOVIE_FRAME_RATE -1 + + +//========================================================================== +// PRIVATE FUNCTIONS +//========================================================================== + +long Disk_VQA_Stream_Handler( + VQAHandle *vqa_handle, long action, void *buffer, long nbytes ); + +#if( DEBUG_CODE ) +BOOL Set_Frame_Rate_Dialog_Procedure( + WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ); +#endif + +//========================================================================== +// PRIVATE GLOBALS +//========================================================================== + +#if( DEBUG_CODE ) + int Debug_Movie_Frame_Rate = -1; +#endif + + +/*************************************************************************** + * VQAClass::VQAClass -- Constructor for VQAClass object. * + * * + * INPUT: * + * base_filename - vqa filename without the extension. * + * buffer - buffer to draw to. * + * callback - pointer to function that will blit the drawn frames. * + * media_source - either FROM_MEMORY or FROM_DISK. * + * id - more specific info about the vqa, like exact property location. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: See PVCS log * + *=========================================================================*/ +VQAClass::VQAClass( char *filename, char *buffer, short media_src, long (*callback) (unsigned char *, long) ) +{ + // Initialize config options. + VQA_DefaultConfig( &vqa_config ); + + //------------------------------------------------------------------------- + // Set up video config options. + //------------------------------------------------------------------------- + vqa_config.Vmode = 0; + vqa_config.ImageBuf = (unsigned char *)buffer; + + // + // Set up draw options. + // + vqa_config.DrawFlags = VQACFGF_TOPLEFT; + vqa_config.DrawFlags |= VQACFGF_BUFFER; + //vqa_config.DrawFlags |= VQACFGF_NOSKIP; + + // + // Set the dimensions of the draw buffer. + // + vqa_config.ImageWidth = MOVIE_WIDTH; + vqa_config.ImageHeight = MOVIE_HEIGHT; + vqa_config.X1 = 0; + vqa_config.Y1 = 0; + + // + // Set up the drawer callback for the VQA. + // + vqa_config.DrawerCallback = callback; + + // + // Set the load rate (it is misnamed FrameRate) + // + vqa_config.FrameRate = -1; + + // + // Set the frame rate (DrawRate) + // + vqa_config.DrawRate = -1; + + #if( DEBUG_CODE ) + vqa_config.FrameRate = Debug_Movie_Frame_Rate; + vqa_config.DrawRate = Debug_Movie_Frame_Rate; + vqa_config.FrameRate = DEBUG_MOVIE_FRAME_RATE; + vqa_config.DrawRate = DEBUG_MOVIE_FRAME_RATE; + #endif + + vqa_config.SoundObject = NULL; //Get_Sound_Object(); + vqa_config.PrimaryBufferPtr = NULL; //Get_Primart_Buffer(); + + //------------------------------------------------------------------------- + // Create a VQ instance for playing from memory - does not actually allocate buffers. + //------------------------------------------------------------------------- + vqa_handle = VQA_Alloc(); + + // + // Set the IO handler for this VQ instance. + // + media_source = media_src; + switch( media_source ) { + case FROM_DISK: + default: + vqa_config.NumFrameBufs = 6; + vqa_config.NumCBBufs = 3; + VQA_Init( vqa_handle, Disk_VQA_Stream_Handler ); + break; + } + + //------------------------------------------------------------------------- + // Initialize private class variables. + //------------------------------------------------------------------------- + + for (int i=0 ; i<50 ; i++){ + InterpolatedPalettes[i] = NULL; + } + + // Init file_handle + file_handle = NULL; + + // Initialize starting frame number to first frame of movie. + current_frame = 0; + + // Set total frames. + total_frames = 0; + + // Save the base filename for this VQA instance. + strcpy( base_filename, filename ); + + // Save the vqa filename for this VQA instance. + sprintf( vqa_filename, "%s.vqa", filename ); + + // Initially, vqa is not open. + vqa_is_open = FALSE; + +} + + +VQAClass::~VQAClass(void) +{ + for (int i=0 ; i<50 ;i++){ + if (InterpolatedPalettes[i]){ + free (InterpolatedPalettes[i]); + } + } +} + + +/*************************************************************************** + * DISK_VQA_STREAM_HANDLER -- Handles file io for VQAs run from disk. * + * * + * INPUT: * + * vqa_handle - pointer to the vqa instance. * + * action - specifies the io operation to be performed. * + * buffer - buffer to write out or read into. * + * nbytes - number of bytes to read, write, seek... * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: See PVCS log * + *=========================================================================*/ +long Disk_VQA_Stream_Handler( + VQAHandle *vqa_handle, long action, void *buffer, long nbytes) +{ + unsigned char temp_char; + int fh; + long error; + + fh = vqa_handle->VQAio; + + // + // Perform the action specified by the stream command. + // + switch ( action ) { + + // + // VQACMD_READ means read NBytes from the stream and place it in the + // memory pointed to by Buffer. + // + // Any error code returned will be remapped by VQA library into + // VQAERR_READ. + // + case VQACMD_READ: + error = ( Read_File( fh, buffer, nbytes ) != nbytes ); + break; + + // + // VQACMD_WRITE is analogous to VQACMD_READ. + // + // Writing is not allowed to the VQA file, VQA library will remap the + // error into VQAERR_WRITE. + // + case VQACMD_WRITE: + error = 1; + break; + + // + // VQACMD_SEEK asks that you perform a seek relative to the current + // position. NBytes is a signed number, indicating seek direction + // (positive for forward, negative for backward). Buffer has no meaning + // here. + // + // Any error code returned will be remapped by VQA library into + // VQAERR_SEEK. + // + case VQACMD_SEEK: + // + // In order to force a physical seek, we must seek to a position that + // is one byte before our real destination, and then read one byte. + // + if ( nbytes > 0 ) { + nbytes -= 1; + error = ( Seek_File( fh, nbytes, (long) buffer ) == 0 ); + Read_File( fh, &temp_char, 1 ); + } + else { + error = ( Seek_File( fh, nbytes, (long) buffer ) == 0 ); + } + break; + + // + // VQACMD_OPEN asks that you open your stream for access. + // + case VQACMD_OPEN: + error = Open_File( (char const *) buffer, READ ); + if ( error != WW_ERROR ) { + vqa_handle->VQAio = error; + error = 0; + } + else { + error = TRUE; + } + + break; + + case VQACMD_CLOSE: + Close_File( fh ); + error = 0; + break; + + // + // VQACMD_INIT means to prepare your stream for reading. This is used for + // certain streams that can't be read immediately upon opening, and need + // further preparation. This operation is allowed to fail; the error code + // will be returned directly to the client. + // + case VQACMD_INIT: + error = 0; + break; + + // + // VQACMD_CLEANUP means to terminate the transaction with the associated + // stream. This is used for streams that can't simply be closed. This + // operation is not allowed to fail; any error returned will be ignored. + // + case VQACMD_CLEANUP: + error = 0; + break; + } + + return ( error ); +} + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iIs_Available()){ + + PaletteFile->Open(READ); + + /* + ** Find out how many palettes are in the file + */ + PaletteFile->Read(&NumPalettes , 4); + + /* + ** Read each palette. Palettes are all the same size. + */ + for (i=0 ; iRead (InterpolatedPalettes[i] + y*256 , y+1); + } + + //PaletteFile->Read(&comp_size,4); + //PaletteFile->Read(comp_buff , comp_size); + //LCW_Uncompress((char const*)comp_buff,(char*)InterpolatedPalettes[i],65536); + + /* + ** Rebuild the entries that were lost when the palette was written to disk + */ + Rebuild_Interpolated_Palette(InterpolatedPalettes[i]); + } + + PalettesRead = TRUE; + PaletteFile->Close(); + NumPalettes = 0; + + } + delete PaletteFile; +} + + + + + +/*********************************************************************************************** + * VQAC::Write_Palettes -- write the palette interpolation tables to disk * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/21/95 10:37AM ST : Created * + *=============================================================================================*/ + +void VQAClass::Write_Palettes(void) +{ + unsigned char *comp_buff = (unsigned char*)malloc(65536); + unsigned comp_size; + + /* + ** If the palette file wasnt there when we started playing the VQ then create one + */ + if (NumPalettes && !PalettesRead){ + + PaletteFile = new RawFileClass (PaletteFilename); + PaletteFile->Open(WRITE); + + /* + ** Write the number of palettes we created + */ + PaletteFile->Write(&NumPalettes , 4); + + /* + ** Write the non-redundant palette table entries for each palette table + */ + for (int i=0 ; iWrite (InterpolatedPalettes[i] + y*256 , y+1); + } + } + PaletteFile->Close(); + delete PaletteFile; + } +} + + + +/*************************************************************************** + * VQACLASS::OPEN_AND_LOAD_BUFFERS -- Opens VQA file and fills frame buffrs* + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * TRUE if successful * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log * + *=========================================================================*/ +BOOL VQAClass::Open_And_Load_Buffers( void ) +{ + VQAInfo vqa_info; + unsigned char *pal_ptr; + INT i; + + + // + // Open the VQA file, allocate the buffers for this VQ instance, and pre- + // load the buffers. + // + if ( VQA_Open( vqa_handle, vqa_filename, &vqa_config ) != 0 ) { + vqa_is_open = FALSE; + return( FALSE ); + } +#if (0) + // + // Get the VQA's palette. + // + pal_ptr = (unsigned char *) VQA_GetPalette( vqa_handle ); + if ( pal_ptr ) { + + // + // Get a copy of the VQA's palette. + // + Mem_Copy( pal_ptr, palette, ( SIZE_OF_PALETTE * 3 ) ); + + // + // The palette in the VQA files is 6-bit per pixel, so shift all the + // values to make them 8-bits per pixel. + // + for ( i = 0; i < ( SIZE_OF_PALETTE * 3 ); i ++ ) { + palette[ i ] <<= 2; + } + } +#endif + // + // Get frame information about the VQA. + // + VQA_GetInfo( vqa_handle, &vqa_info ); + total_frames = vqa_info.NumFrames; + + vqa_is_open = TRUE; + + return( TRUE ); +} + + + +/*************************************************************************** + * VQACLASS::SEEK_TO_FRAME -- Performs file seek to specified frame. * + * * + * INPUT: * + * frame - frame number to seek to. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log * + *=========================================================================*/ +void VQAClass::Seek_To_Frame( unsigned long frame ) +{ + // + // Don't bother seeking if already there. + // + if ( frame == current_frame ) { + return; + } + + if ( VQA_SeekFrame( vqa_handle, frame, SEEK_SET ) != -1 ) { + current_frame = frame; + } +} + +/*************************************************************************** + * Play_VQA - Plays from the current frame of the VQA up to and including * + * the last frame specified. * + * * + * INPUT: INT last_frame - last frame to be displayed * + * * + * OUTPUT: NONE * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/20/1995 MG : Created. * + *=========================================================================*/ +int VQAClass::Play_VQA( int last_frame_to_play ) +{ + INT return_code; + + #if( DEBUG_CODE ) + VQAStatistics vqa_stats; + float frames_per_sec; + unsigned long start_time; + unsigned long end_time; + #endif + + // + // Before starting to play, make sure we have the correct palette. + // + //Update_Palette(); + +// +// Create table for scaling low-res VQA's. +// + + //Create_Palette_Interpolation_Table(); + + // + // Get current time for timing purposes. + // + #if( DEBUG_CODE ) + start_time = Get_Game_Time(); + #endif + + // + // Call the appropriate play routine. + // + return_code = Play_Generic_VQA( last_frame_to_play ); + + // + // Get VQA statistics. + // + #if( DEBUG_CODE ) + end_time = Get_Game_Time(); + VQA_GetStats( vqa_handle, &vqa_stats ); + if ( ( end_time - start_time ) <= 0.0 ) { + frames_per_sec = 0.0; + } + else { + frames_per_sec = vqa_stats.FramesDrawn / ( ( end_time - start_time ) / 1000.0 ); + } + Debug_Printf( "Frames drawn=%d. Frames/sec=%03.02f. Frames skipped = %d\r\n", vqa_stats.FramesDrawn, frames_per_sec, vqa_stats.FramesSkipped ); + #endif + + // + // If PLAY_ALL_FRAMES was specified, reset curr frame to beginning of movie. + // + if ( last_frame_to_play == PLAY_ALL_FRAMES ) { + current_frame = 0; + } + + return( return_code ); +} + + +/*************************************************************************** + * VQACLASS::PLAY_GENERIC_VQA -- Private func to play any Monopoly VQA. * + * * + * INPUT: * + * last_frame_to_play - last frame to be drawn. * + * * + * OUTPUT: * + * INT error code. * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log * + *=========================================================================*/ +int VQAClass::Play_Generic_VQA( int last_frame_to_play ) +{ + long errval; + + switch( last_frame_to_play ) { + case PLAY_ALL_FRAMES: + Seek_To_Frame( 0 ); + // FALL THROUGH... + case PLAY_TO_END_OF_MOVIE: + last_frame_to_play = total_frames - 1; + break; + } + + // + // Play frames until the last desired frame is drawn. + // + errval = 0; + + while ( + ( errval == VQAERR_NOT_TIME || errval >= 0 || errval == VQAERR_NOBUFFER || errval == VQAERR_SLEEPING ) && + ( current_frame <= last_frame_to_play ) ) { + + // + // Check for Windows Messages. + // + Main_Window.Update_Windows_Messages(); + + #if( DRAW_TO_VIDEO ) + Screen_Buffer->Lock(); + #endif + + // + // Maybe draw another frame. + // + errval = VQA_Play( vqa_handle, VQAMODE_WALK ); + + #if( DRAW_TO_VIDEO ) + Screen_Buffer->Unlock(); + #endif + + // If we actually played a frame update the current frame number. + if ( errval >= 0 ) { + current_frame = errval; + current_frame ++; + } + } + + return( VQA_PLAY_NO_ERROR ); +} + + +/*************************************************************************** + * Play_VQA_Frame - Plays the specified frame, seeking to it if necessary. * + * * + * INPUT: INT frame_number * + * * + * OUTPUT: NONE * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/20/1995 MG : Created. * + *=========================================================================*/ +void VQAClass::Play_VQA_Frame( INT frame_number ) +{ + if ( frame_number == PLAY_LAST_FRAME ) { + frame_number = total_frames - 1; + } + + // + // If not currently at the desired frame, seek to it. + // + if ( current_frame != frame_number ) { + Seek_To_Frame( frame_number ); + current_frame = frame_number; + } + + // + // Display the frame. + // + Play_VQA( frame_number ); + + // + // If at end of movie, reset to start of movie. + // + if ( current_frame >= total_frames ) { + current_frame = 0; + } +} + +/*************************************************************************** + * Pause_VQA - Pauses a VQA in order to freeze the VQA timers. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/29/1995 MG : Created. * + *=========================================================================*/ +void VQAClass::Pause_VQA( void ) +{ + VQA_Play( vqa_handle, VQAMODE_PAUSE ); +} + + + +/*************************************************************************** + * VQACLASS::CLOSE_AND_FREE_VQA -- Closes vqa, frees instance, frees cache * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log. * + *=========================================================================*/ +void VQAClass::Close_And_Free_VQA( void ) +{ + if ( vqa_is_open ) { + VQA_Close( vqa_handle ); + } + VQA_Free( vqa_handle ); +} + + + +/*************************************************************************** + * CHECK_KEY -- NULL function for VQA play library. * + * * + * INPUT: NONE * + * * + * OUTPUT: 0 * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log * + *=========================================================================*/ +unsigned short Check_Key( void ) +{ + return( 0 ); +} + + + +/*************************************************************************** + * GET_KEY -- NULL function for VQA play library. * + * * + * INPUT: NONE * + * * + * OUTPUT: 0 * + * * + * WARNINGS: * + * * + * HISTORY: * + * See PVCS log * + *=========================================================================*/ +unsigned short Get_Key( void ) +{ + return( 0 ); +} + + +/*************************************************************************** + * Set_Movie_Frame_Rate - Debug function for setting movie playback rate * + * * + * INPUT: NONE * + * * + * OUTPUT: NONE * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +void Set_Movie_Frame_Rate( void ) +{ + int retval; + + #if( 0 ) + + retval = DialogBox( + Main_Window.Get_Instance_Handle(), + "GEEB", + Main_Window.Get_Window_Handle(), + (DLGPROC) Set_Frame_Rate_Dialog_Procedure ); + + Debug_Printf( "retval = %d\r\n", retval ); + + #endif +} + + +/*************************************************************************** + * SET_FRAME_RATE_DIALOG_PROCEDURE * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: NONE * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +BOOL Set_Frame_Rate_Dialog_Procedure( + WindowHandle window_handle, + UINT message, + WPARAM w_param, + LPARAM l_param ) +{ + char frame_rate_string[ 5 ]; + + switch( message ) { + + case WM_COMMAND: + + if ( LOWORD( w_param ) == IDOK ) { + + GetDlgItemText( window_handle, FRAME_RATE, frame_rate_string, sizeof( frame_rate_string ) ); + Debug_Movie_Frame_Rate = atoi( frame_rate_string ); + + if ( Debug_Movie_Frame_Rate < -1 ) { + Debug_Movie_Frame_Rate = -1; + } + + Debug_Printf( "Setting movie frame rate to %d\r\n", Debug_Movie_Frame_Rate ); + + EndDialog( window_handle, TRUE ); + return( TRUE ); + } + break; + + default: + break; + + } + return( FALSE ); +} + + + diff --git a/WINVQ/VQAVIEW/VQ.H b/WINVQ/VQAVIEW/VQ.H new file mode 100644 index 0000000..74c0ee6 --- /dev/null +++ b/WINVQ/VQAVIEW/VQ.H @@ -0,0 +1,161 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : VQ.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//========================================================================== +// INCLUDES +//========================================================================== + +#include +#include +#define bool BOOL +#define true 1 +#define false 0 +#include + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +void Set_Movie_Frame_Rate( void ); + +//========================================================================== +// PRIVATE DEFINES +//========================================================================== + +//-------------------------------------------------------------------------- +// GENERAL DEFINES +//-------------------------------------------------------------------------- + +#define VQ_BLOCK_WIDTH 4 +#define VQ_BLOCK_HEIGHT 4 +#define VQ_COLUMNS ( VQ_MOVIE_WIDTH / VQ_BLOCK_WIDTH ) +#define VQ_ROWS ( VQ_MOVIE_HEIGHT / VQ_BLOCK_HEIGHT ) + +// Codes for media_src parameter of VQAClass constructor: +#define FROM_DISK 0 +#define FROM_MEMORY 1 + +// CODES for Play_VQA: +#define PLAY_ALL_FRAMES -1 +#define PLAY_TO_END_OF_MOVIE -2 + +// CODES for Play_VQA_Frame: +#define PLAY_LAST_FRAME -1 + +// Error codes returned by VQA_INIT: +#define VQA_INIT_NO_ERROR -1 +#define VQA_INIT_FAILED_MEM_POOL_ALLOC -2 +#define VQA_INIT_FAILED_SCRATCH_ALLOC -3 + +// Codes for Cache_VQA: +#define CACHE_REST_OF_FILE 0 + +// Error codes returned by CACHE_VQA +#define CACHE_NO_ERROR -1 +#define CACHE_EOF -2 +#define CACHE_FAILED_MEM_ALLOC -3 +#define CACHE_OPEN_FILE_ERROR -4 +#define CACHE_READ_ERROR -5 + +// Error codes returned by Play_VQA: +#define VQA_PLAY_NO_ERROR 0 +#define VQA_PLAY_USER_BREAK 1 + +#define VECTOR_FORMAT_OFFSETS 1 +#define VECTOR_FORMAT_INDEXES 2 +#define MAX_CODEBOOK_OFFSET 32766 + +//========================================================================== +// PUBLIC DATA +//========================================================================== + +extern unsigned char *Movie_Scratch_Buffer; + +//========================================================================== +// CLASSES +//========================================================================== + +class VQAClass { + private: + char base_filename[ _MAX_PATH ]; + char vqa_filename[ _MAX_PATH ]; + char PaletteFilename [_MAX_PATH]; + VQAConfig vqa_config; + VQAHandle *vqa_handle; + short media_source; + int file_handle; + unsigned char palette[ SIZE_OF_PALETTE * 3 ]; + int current_frame; + int total_frames; + BOOL vqa_is_open; + unsigned char *InterpolatedPalettes[50]; //Max 50 palette changes in a vq + BOOL PalettesRead; + RawFileClass *PaletteFile; + unsigned NumPalettes; + + + + /*=========================================================================*/ + /* Private functions. */ + /*=========================================================================*/ + int VQAClass::Play_Generic_VQA( int last_frame_to_play ); + + public: + VQAClass( char *filename, char *buffer, short media_src, long (*callback) (unsigned char *, long) ); + ~VQAClass (void); + BOOL VQAClass::Update_Palette( unsigned char *newpalette ); + BOOL Open_And_Load_Buffers( void ); + void Seek_To_Frame( unsigned long frame ); + int Play_VQA( int last_frame_to_play ); + void Play_VQA_Frame( int frame_number ); + void Pause_VQA( void ); + void Close_And_Free_VQA( void ); + void VQAClass::Read_Palettes(void); + void VQAClass::Write_Palettes(void); +}; /* VQAClass */ + + +//========================================================================== +// TYPES +//========================================================================== + +typedef struct { + unsigned long file_offset; + unsigned long file_size; +} VQACacheHeader; + + + diff --git a/WINVQ/VQAVIEW/VQAVIEW.EXE b/WINVQ/VQAVIEW/VQAVIEW.EXE new file mode 100644 index 0000000..38bd7af Binary files /dev/null and b/WINVQ/VQAVIEW/VQAVIEW.EXE differ diff --git a/WINVQ/VQAVIEW/VQAVIEW.IDE b/WINVQ/VQAVIEW/VQAVIEW.IDE new file mode 100644 index 0000000..84c9223 Binary files /dev/null and b/WINVQ/VQAVIEW/VQAVIEW.IDE differ diff --git a/WINVQ/VQAVIEW/VQAVIEW.IDE.BAK b/WINVQ/VQAVIEW/VQAVIEW.IDE.BAK new file mode 100644 index 0000000..a3f087a Binary files /dev/null and b/WINVQ/VQAVIEW/VQAVIEW.IDE.BAK differ diff --git a/WINVQ/VQAVIEW/WESTWOOD.H b/WINVQ/VQAVIEW/WESTWOOD.H new file mode 100644 index 0000000..dc2ee47 --- /dev/null +++ b/WINVQ/VQAVIEW/WESTWOOD.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Virtual_Monopoly * + * * + * File Name : WESTWOOD.H * + * * + * Programmer : Michael Legg / Mike Grayford * + * * + * Start Date : January 2, 1995 * + * * + * Last Update : Nov 15, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define BitmapHandle HBITMAP +#define DCHandle HDC +#define DIBPointer LPBITMAPINFOHEADER +#define Handle HANDLE +#define MenuHandle HMENU +#define MessageQueue MSG +#define PaintStruct PAINTSTRUCT +#define PaletteHandle HPALETTE +#define Rectangle RECT +#define String LPSTR +#define WindowClass WNDCLASS +#define WindowHandle HWND +#define FileHandle HFILE +#define CursorHandle HCURSOR +#define AcceleratorHandle HACCEL +#define IconHandle HICON +#define InstanceHandle HINSTANCE +#define DirectDrawPalettePtr LPDIRECTDRAWPALETTE +#define DirectDrawObjectPtr LPDIRECTDRAW +#define BOOL int +#define TRUE 1 +#define FALSE 0 + \ No newline at end of file diff --git a/WINVQ/VQAVIEW/WM.CPP b/WINVQ/VQAVIEW/WM.CPP new file mode 100644 index 0000000..4b96217 --- /dev/null +++ b/WINVQ/VQAVIEW/WM.CPP @@ -0,0 +1,254 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : WM.CPP * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 20, 1995 * + * * + * Last Update : Nov 20, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//========================================================================== +// INCLUDES +//========================================================================== + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +//========================================================================== +// PUBLIC FUNCTIONS +//========================================================================== + +void Menu_Exit_Game( void ); + + +/*************************************************************************** + * WM_COMMAND_FUNC -- Handles all main window commands * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: IDOK or IDCANCEL * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/20/95 MG : Created * + *=========================================================================*/ +long WM_Command_Func( + WindowHandle window_handle, + unsigned int message, + WPARAM w_param, + LPARAM l_param ) +{ + switch( w_param ) { + case MENU_EXIT: + Menu_Exit_Game(); + break; + + case MENU_OPEN: + Choose_Movie( Main_Window.Get_Window_Handle() ); + break; + + case MENU_SET_MOVIE_FRAME_RATE: + Set_Movie_Frame_Rate(); + break; + + default: + break; + } + + return( 0 ); +} + + +/*************************************************************************** + * WM_SYS_COMMAND_FUNC -- Handles all system menu commands * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: IDOK or IDCANCEL * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +#pragma argsused +long WM_Sys_Command_Func( + WindowHandle window_handle, + unsigned int message, + WPARAM w_param, + LPARAM l_param ) +{ + switch( w_param & 0xfff0 ) { + case SC_CLOSE: + break; + + case SC_MINIMIZE: + break; + + case SC_MAXIMIZE: + case SC_RESTORE: + break; + + case SC_KEYMENU: + case SC_MOUSEMENU: + break; + + default: + break; + } + + return( DefWindowProc( window_handle, message, w_param, l_param ) ); +} + + +/*************************************************************************** + * WM_PAINT_FUNC -- Code that is executed when WM_PAINT is sent * + * * + * INPUT: standard windows dialog command parameter passing * + * * + * OUTPUT: unused * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +#pragma argsused +long WM_Paint_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ) +{ + return( 0 ); +} + + +/*************************************************************************** + * WM_DESTROY_FUNC -- Handles when a WM_DESTROY hits the main window * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: unused * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +long WM_Destroy_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ) +{ + if ( Screen_Buffer ) { + delete( Screen_Buffer ); + } + + PostQuitMessage( w_param ); + + return( 0L ); +} + + +/*************************************************************************** + * WM_MOUSE_BUTTON_FUNC -- Handles when a MOUSE button command comes in * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: unused * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +long WM_Mouse_Button_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ) +{ + int x_pix; + int y_pix; + + x_pix = LOWORD( l_param ); + y_pix = HIWORD( l_param ); + + switch ( message ) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + break; + + default: + break; + } + return( 0 ); +} + + + +/*************************************************************************** + * WM_ACTIVATEAPP_FUNC -- Handles WM_ACTIVATEAPP * + * * + * INPUT: standard windows dialog command parameters * + * * + * OUTPUT: unused * + * * + * WARNINGS: none * + * * + * HISTORY: see PVCS log * + *=========================================================================*/ +long WM_ActivateApp_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ) +{ + return( 0 ); +} + + + +void Menu_Exit_Game( void ) +{ + PostMessage( Main_Window.Get_Window_Handle(), WM_CLOSE, 0, 0L ); +} + + diff --git a/WINVQ/VQAVIEW/WM.H b/WINVQ/VQAVIEW/WM.H new file mode 100644 index 0000000..21d5912 --- /dev/null +++ b/WINVQ/VQAVIEW/WM.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : WM.H * + * * + * Programmer : Mike Grayford * + * * + * Start Date : November 20, 1995 * + * * + * Last Update : Nov 20, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//========================================================================== +// Public functions +//========================================================================== + +long WM_Command_Func( + WindowHandle window_handle, + unsigned int message, + WPARAM w_param, + LPARAM l_param ); + +long WM_Sys_Command_Func( + WindowHandle window_handle, + unsigned int message, + WPARAM w_param, + LPARAM l_param ); + +long WM_Paint_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ); + +long WM_Mouse_Button_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ); + +long WM_ActivateApp_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ); + +long WM_Destroy_Func( + WindowHandle window_handle, + unsigned int message, + unsigned int w_param, + long l_param ); + + diff --git a/WINVQ/VQAVIEW/WWLIB.H b/WINVQ/VQAVIEW/WWLIB.H new file mode 100644 index 0000000..e6739cd --- /dev/null +++ b/WINVQ/VQAVIEW/WWLIB.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : VQAVIEW * + * * + * File Name : WWLIB.H * + * * + * Programmer : Michael Grayford * + * * + * Start Date : * + * * + * Last Update : Nov 22, 1995 [MG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern DirectDrawObjectPtr DirectDrawObject; +extern DirectDrawPalettePtr PalettePtr; +extern WindowHandle MainWindow; + diff --git a/WINVQ/VQM32/ALL.H b/WINVQ/VQM32/ALL.H new file mode 100644 index 0000000..19f93e6 --- /dev/null +++ b/WINVQ/VQM32/ALL.H @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +#ifndef VQMALL_H +#define VQMALL_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* all.h +* +* DESCRIPTION +* All VQMisc32 library definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* VQMALL_H */ + diff --git a/WINVQ/VQM32/AUDUNZAP.ASM b/WINVQ/VQM32/AUDUNZAP.ASM new file mode 100644 index 0000000..ea9a161 --- /dev/null +++ b/WINVQ/VQM32/AUDUNZAP.ASM @@ -0,0 +1,375 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* audunzap.asm +;* +;* DESCRIPTION +;* Audio uncompress (32-Bit protected mode) +;* +;* PROGRAMMER +;* Joe L. Bostic +;* Denzil E. Long, Jr. +;* +;* DATE +;* February 9, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* AudioUnzap - Uncompress zapped audio sample. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + +;**************************************************************************** +;* +;* NAME +;* AudioUnzap - Uncompress zapped audio sample. +;* +;* SYNOPSIS +;* Size = AudioUnzap(Source, Dest, Size) +;* +;* long AudioUnzap(void *, void *, long); +;* +;* FUNCTION +;* Decompress the zapped audio sample data into a buffer. +;* +;* INPUTS +;* Source - Pointer to encoded audio data. +;* Dest - Pointer to buffer to decompress into. +;* Size - Maximum size of dest buffer. +;* +;* RESULT +;* Size - Number of uncompressed bytes. +;* +;**************************************************************************** + + GLOBAL C AudioUnzap:NEAR + PROC AudioUnzap C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + mov [incount],0 ;Bytes read from source + +; Source, Dest and count must be valid. + +; cmp [source],0 +; je ??fini + +; cmp [dest],0 +; je ??fini + +; cmp [count],0 +; je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + cld + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popf + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popf + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popf + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popf + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popf + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popf + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popf + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popf + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popf + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popf + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popf + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popf + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + mov eax,[incount] + ret + + ENDP AudioUnzap + + END + diff --git a/WINVQ/VQM32/AUDZAP.CPP b/WINVQ/VQM32/AUDZAP.CPP new file mode 100644 index 0000000..2dc4a9a --- /dev/null +++ b/WINVQ/VQM32/AUDZAP.CPP @@ -0,0 +1,398 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Audzap.c +* +* DESCRIPTION +* Lossy audio compression. (32-Bit protected mode) +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* AudioZap - Compress audio sample data. +* +****************************************************************************/ + +#include +#include +#include "compress.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) + +typedef enum { + CODE_2BIT, /* Bit packed 2 bit delta. */ + CODE_4BIT, /* Nibble packed 4 bit delta. */ + CODE_RAW, /* Raw sample. */ + CODE_SILENCE /* Run of silence. */ +} SCodeType; + +char _2bitencode[5] = { + 0,1,2,3 +}; + +long _2bitdecode[4] = { + -2,-1,0,1 +}; + +char _4bitencode[19] = { + 0,1,2,2,3,4,5,6,7,(8), + 8,9,10,11,12,13,13,14,15 +}; + +long _4bitdecode[16] = { + -9,-8,-6,-5,-4,-3,-2, + -1,0,1,2,3,4,5,6,8 +}; + + +/**************************************************************************** +* +* NAME +* AudioZap - Compress audio sample data. +* +* SYNOPSIS +* Size = AudioZap(Source, Dest, Size) +* +* long AudioZap(void *, void *, long); +* +* FUNCTION +* NOTE - If the compressed size is equal to or greater than the original +* size then the data could not be compressed and the uncompressed data +* should be written. +* +* INPUTS +* Source - Pointer to buffer containing audio sample data. +* Dest - Pointer to buffer to put encoded data. +* Size - Number of bytes to compress. +* +* RESULT +* Size - Size in bytes of encoded data. +* +****************************************************************************/ + +long AudioZap(void *source, void *dest, long size) +{ + unsigned char *s = (unsigned char *)source; + unsigned char *d = (unsigned char *)dest; + long delta; + unsigned long previous = 0x80; + long outcount = 0; + unsigned char *s4; + unsigned long p4; + long max4; + unsigned char *lastraw = 0; + long osize = size; + unsigned long i; + unsigned long dd; + + /* Reduce very small amplitudes to silence. Usually a rather large + * portion of a sample is hovering around the silence value. This is + * due, in part, to the artifacting of the sample process. These low + * amplitudes are not audible. + */ + max4 = size; + s = (unsigned char *)source; + + while (size > 0 && outcount < osize) { + + /* First check for runs of zero deltas. If a run of at least + * any can be found, then output it. + */ + s4 = s; + max4 = MIN(63 + 1, size); + + for (i = 0; i < max4; i++) { + if (previous != *s4++) + break; + } + + /* When there is a code transition, terminate any run of raw + * samples. + */ + if (i > 2) { + lastraw = 0; + *d++ = ((i - 1)|(CODE_SILENCE << 6)); + outcount++; + s += i; + size -= i; + continue; + } + + /* If there are fewer than 4 samples remaining, then using delta + * compression is inefficient. Just drop into the raw routine + */ + if (size > 4) { + s4 = s; + p4 = previous; + + /* Find out the number of lossless 2 bit deltas available. These + * deltas are always present in quads. The compressed code is + * the delta quad count followed by the deltas in bit packed bytes. + */ + max4 = MIN(64L * 4L + 4L + 4L, size); + + for (i = 0; i < max4; i++) { + delta = (((int)*s4++) - p4); + + if ((delta < -2) || (delta > 1)) { + break; + } + + p4 += _2bitdecode[_2bitencode[delta + 2]]; + + if (((signed)p4) < 0) { + p4 = 0; + } + + if (((signed)p4) > 255) { + p4 = 255; + } + } + + i >>= 2; // Delta 2 always occur in quads -- force this. + + /* If there is the minimum benificial number of delta 2s available, + * then compress them. + */ + if (i) { + + /* When there is a code transition, terminate any run of raw + * samples. + */ + lastraw = 0; + + /* Output the delta 4 pair count. This is the number of pairs + * minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, (63 + 1)); + *d++ = ((i - 1)|(CODE_2BIT << 6)); + outcount++; + + for (dd = 0; dd < i; dd++) { + int delta1, delta2, delta3, delta4; + + delta1 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta1]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta2 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta2]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta3 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta3]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta4 = _2bitencode[((((int)*s++) - previous) + 2)]; + previous += _2bitdecode[delta4]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + *d++ = ((delta4 << 6)|(delta3 << 4)|(delta2 << 2)|delta1); + outcount++; + } + continue; + } else { + s4 = s; + p4 = previous; + + /* Find out the number of lossless 4 bit deltas follow. These + * deltas are always present in pairs. The compressed code is + * the delta pair count followed by the deltas in nibble packed + * bytes. + */ + max4 = MIN(64L * 2L + 4L + 4L, size); + + for (i = 0; i < max4; i++) { + delta = (((int)*s4++) - p4); + + if (delta < -9 || delta >= 9) { + break; + } + + p4 += _4bitdecode[_4bitencode[(delta + 9)]]; + + if (((signed)p4) < 0) { + p4 = 0; + } + + if (((signed)p4) > 255) { + p4 = 255; + } + } + + i >>= 1; // Delta 4 always occur in pairs -- force this. + + /* If there is the minimum benificial number of delta 4s available, + * then compress them. + */ + if (i) { + + /* When there is a code transition, terminate any run of raw + * samples. + */ + lastraw = 0; + + /* Output the delta 4 pair count. This is the number of pairs + * minus the 'free' two pairs already assumed to be there. + */ + i = MIN(i, (63 + 1)); + *d++ = ((i - 1)|(CODE_4BIT << 6)); + outcount++; + + for (dd = 0; dd < i; dd++) { + int delta1, delta2; + + delta1 = _4bitencode[((((int)*s++) - previous) + 9)]; + previous += _4bitdecode[delta1]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + delta2 = _4bitencode[((((int)*s++) - previous) + 9)]; + previous += _4bitdecode[delta2]; + + if (((signed)previous) < 0) { + previous = 0; + } + + if (((signed)previous) > 255) { + previous = 255; + } + + size--; + *d++ = ((delta2 << 4)|(delta1 & 0x0F)); + outcount++; + } + continue; + } + } + } + + /* Raw output since deltas were unsuccessful. If this is a run + * of raw output, then merely tack it onto the run rather than + * create a new code sequence. + */ + if (lastraw) { + *lastraw = ((*lastraw) + 1); + + /* There is only so much a run code can accomodate. If the limit + * has been reached, then terminate this code. A new one will be + * created if necessary. + */ + if ((*lastraw & 0x1F) == 0x1F) { + lastraw = 0; + } + } else { + + /* If there is no current raw dump of samples, then check to see if + * this sample can fit into a 5 bit delta. If it can, then store + * it as such as a parasite to the "raw" code. This will save a byte + * for any stray 5 bit deltas that happen along. It is expected that + * this is more frequent than 6 or more bit deltas that would necessitate + * the use of the RAW code. + */ + delta = (((int)*s) - previous); + + if ((delta >= -16) && (delta <= 15)) { + lastraw = 0; + *d++ = ((CODE_RAW << 6)|0x20|(delta & 0x1F)); + outcount++; + previous = *s++; + size--; + continue; + } else { + lastraw = d; + *d++ = (CODE_RAW << 6); + outcount++; + } + } + + *d++ = previous = *s++; + size--; + outcount++; + } + + /* Check to see if the compression process actually resulted in smaller + * data size. In some cases, the 'compressed' data is actually larger. In + * this case, just output the raw frame. If the compressed and actual frame + * size match, then it is presumed that no compression occurs. + */ + if (outcount >= osize) { + memcpy(dest, source, (size_t)osize); + outcount = osize; + } + + return(outcount); +} + diff --git a/WINVQ/VQM32/BCC32.CFG b/WINVQ/VQM32/BCC32.CFG new file mode 100644 index 0000000..6bab081 --- /dev/null +++ b/WINVQ/VQM32/BCC32.CFG @@ -0,0 +1,9 @@ +-c +-3 +-d +-H=c:\projects\vqm32\obj\headers.sym +-wpro +-weas +-wpre +-IC:\PROJECTS\INCLUDE;C:\DEV\BC4\INCLUDE;C:\DEV\TNT\INCLUDE +-DPHARLAP_TNT=1 diff --git a/WINVQ/VQM32/CAPTOKEN.CPP b/WINVQ/VQM32/CAPTOKEN.CPP new file mode 100644 index 0000000..c4dd511 --- /dev/null +++ b/WINVQ/VQM32/CAPTOKEN.CPP @@ -0,0 +1,458 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.c +* +* DESCRIPTION +* Tokenize a caption script for playback processing. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "captoken.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define STRING_LENGTH 256 +#define PADSIZE(size) (((size)+1)&(~1)) + +typedef enum { + TOKEN_NULL = 0, + TOKEN_BGCOL, + TOKEN_FGCOL, + TOKEN_XPOS, + TOKEN_YPOS, + TOKEN_ABS, + TOKEN_LEFT, + TOKEN_RIGHT, + TOKEN_CENTER, + TOKEN_FLASH, + TOKEN_CPF, + TOKEN_END +} TokenTag; + +typedef struct _Token { + char *name; + long tag; +} Token; + +/* Script tokens: + * BG/BGCOL - Background pen color (EX: BG=) + * FG/FGCOL - Foreground pen color (EX: FG=) + * X/XPOS - X pixel position of text (EX: X=) + * Y/YPOS - Y pixel position of text (EX: Y=) + * ABS/ABSOLUTE - Absolute justification + * LEFT - Left side justification + * RIGHT - Right side justification + * CENTER - Center justification + * FLASH - Enable flashing of text + * CPF - Print rate in characters per frame (EX: CPF=) + * END - Terminate compilation of caption script + */ +Token tokens[] = { + "BG", TOKEN_BGCOL, + "BGCOL", TOKEN_BGCOL, + "FG", TOKEN_FGCOL, + "FGCOL", TOKEN_FGCOL, + "X", TOKEN_XPOS, + "XPOS", TOKEN_XPOS, + "Y", TOKEN_YPOS, + "YPOS", TOKEN_YPOS, + "ABS", TOKEN_ABS, + "ABSOLUTE", TOKEN_ABS, + "LEFT", TOKEN_LEFT, + "RIGHT", TOKEN_RIGHT, + "CENTER", TOKEN_CENTER, + "FLASH", TOKEN_FLASH, + "CPF", TOKEN_CPF, + "END", TOKEN_END, + NULL, TOKEN_NULL, +}; + +Token colors[] = { + "BLACK", 0, + "WHITE", 251, + "RED", 252, + "GREEN", 253, + "SHADOW", 254, + "CYCLE", 255, + NULL, -1, +}; + +/* Prototypes. */ +static long GetColorNum(char *name); +static long IsNumeric(char *string); +static void FormatString(char *string); + + +/**************************************************************************** +* +* NAME +* BuildCaptions - Compile a caption script. +* +* SYNOPSIS +* Size = BuildCaptions(Name, Buffer) +* +* long BuildCaptions(char *, char *); +* +* FUNCTION +* Generate a compiled caption script for use in VQA playback. +* +* INPUTS +* Name - Name of caption script file to compile. +* Buffer - Buffer to put compiled captions into. +* +* RESULT +* Size - Size of compiled captions (in bytes). +* +****************************************************************************/ + +long BuildCaptions(char *name, char *buffer) +{ + FILE *fp; + char *ptr; + char *ptr1; + long size = 0; + long error; + long i; + long tag; + CaptionText caption; + char string[STRING_LENGTH]; + + /* Initialize the caption parameters. */ + memset(&caption, 0, sizeof(CaptionText)); + + /* Open the captions script file. */ + fp = fopen(name, "r"); + + if (fp != NULL) { + error = 0; + + while (!error) { + if (fgets(string, STRING_LENGTH, fp) == NULL) { + if (errno == 0) { + error = 1; + } + + break; + } + + /* Replace the newline with a NULL terminator. */ + string[strlen(string) - 1] = 0; + + /* Ignore comment lines. */ + if (string[0] == ';') continue; + + ptr = strtok(string, "="); + + if (ptr != NULL) { + + /* Check for a comma */ + ptr1 = strchr(ptr, ','); + + if (ptr1 != NULL) { + *ptr1++ = 0; + } + + /* Is this a frame number. */ + if (IsNumeric(ptr) && IsNumeric(ptr1)) { + i = atoi(ptr); + + /* Frames must be defined in ascending order. */ + if ((unsigned short)i >= caption.OnFrame) { + caption.OnFrame = i; + caption.OffFrame = atoi(ptr1); + caption.Size = sizeof(CaptionText); + + /* Get caption text. */ + ptr = strtok(NULL, ""); + + if (ptr != NULL) { + FormatString(ptr); + i = strlen(ptr) + 1; + caption.Size += PADSIZE(i); + size += caption.Size; + + /* Copy the caption structure. */ + memcpy(buffer, &caption, sizeof(CaptionText)); + buffer += sizeof(CaptionText); + + /* Copy the caption text. */ + memcpy(buffer, ptr, i); + buffer += i; + + /* WORD align */ + if (PADSIZE(i) > i) { + *buffer++ = 0; + } + } + } else { + error = 1; + break; + } + } else { + + /* Search for matching token. */ + tag = TOKEN_NULL; + i = 0; + + while (tokens[i].name != NULL) { + if (strcmpi(tokens[i].name, ptr) == 0) { + tag = tokens[i].tag; + break; + } + + i++; + } + + /* Get the data element. */ + ptr = strtok(NULL, ""); + + switch (tag) { + case TOKEN_BGCOL: + caption.BgPen = (char)GetColorNum(ptr); + break; + + case TOKEN_FGCOL: + caption.FgPen = (char)GetColorNum(ptr); + break; + + case TOKEN_XPOS: + caption.Xpos = (unsigned short)atoi(ptr); + break; + + case TOKEN_YPOS: + caption.Ypos = (unsigned short)atoi(ptr); + break; + + case TOKEN_ABS: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_ABS; + break; + + case TOKEN_LEFT: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_LEFT; + break; + + case TOKEN_RIGHT: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_RIGHT; + break; + + case TOKEN_CENTER: + caption.Flags &= ~CTF_JUSTIFY; + caption.Flags |= CTF_CENTER; + break; + + case TOKEN_FLASH: + if (strcmpi(ptr, "OFF") == 0) { + caption.Flags &= ~CTF_FLASH; + } else { + caption.Flags |= CTF_FLASH; + } + break; + + case TOKEN_CPF: + caption.CPF = (char)atoi(ptr); + break; + + /* Termination captions */ + case TOKEN_END: + caption.Size = sizeof(CaptionText); + caption.OnFrame = (unsigned short)-1; + memcpy(buffer, &caption, sizeof(CaptionText)); + buffer += sizeof(CaptionText); + break; + + default: + break; + } + } + } + } + + /* Close the script file. */ + fclose(fp); + } + + return (size); +} + + +/**************************************************************************** +* +* NAME +* GetColorNum - Get the color number from the color name. +* +* SYNOPSIS +* Color = GetColorNum(Name) +* +* long GetColorNum(char *); +* +* FUNCTION +* Look the color number that corresponds to the color name. +* +* INPUTS +* Name - Name of color. +* +* RESULT +* Color - Color number. +* +****************************************************************************/ + +static long GetColorNum(char *name) +{ + long color = -1; + long i; + + i = 0; + + /* Scan for a matching name and return the corresponding color number. */ + while (colors[i].name != NULL) { + if (strcmpi(colors[i].name, name) == 0) { + color = colors[i].tag; + } + + i++; + } + + return (color); +} + + +/**************************************************************************** +* +* NAME +* IsNumeric - Check if a string is numeric. +* +* SYNOPSIS +* Condition = IsNumeric(String) +* +* long IsNumeric(char *); +* +* FUNCTION +* Interogate the string to see if it represents a numeric value. Each +* byte of the string must be between 0x30 and 0x39 inclusively. +* +* INPUTS +* String - String to check. +* +* RESULT +* Condition - 1 if numeric, 0 if not. +* +****************************************************************************/ + +static long IsNumeric(char *string) +{ + long flag = 1; + + /* Ignore any proceeding sign designation. */ + if ((*string == '-') || (*string == '+')) { + string++; + } + + /* Check to see if every byte in the string is a digit. */ + while (flag && (*string != 0)) { + if (!isdigit(*string++)) { + flag = 0; + } + } + + return (flag); +} + + +/**************************************************************************** +* +* NAME +* FormatString - Parse any format codes in the string. +* +* SYNOPSIS +* FormatString(String) +* +* void FormatString(char *); +* +* FUNCTION +* Format a string with any embedded format commands contained in the +* input string. +* +* Supported format commands: +* /n - Insert carriage return. (0x0D) +* /r - Insert carriage return. (0x0D) +* // - Literal backslash. +* +* INPUTS +* String - Pointer to string to format. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void FormatString(char *string) +{ + char *ptr; + + /* NULL strings are invalid. */ + if (string != NULL) { + ptr = string; + + /* Scan the string for embedded format commands. */ + while ((ptr = strchr(ptr, '/')) != NULL) { + switch (*(ptr + 1)) { + + /* Carriage return. */ + case 'n': + case 'r': + *ptr = 0x0D; + break; + + /* Literal backslash. */ + case '/': + break; + + default: + break; + } + + /* Remove the unwanted character. */ + strcpy((ptr + 1), (ptr + 2)); + } + } +} + diff --git a/WINVQ/VQM32/CAPTOKEN.H b/WINVQ/VQM32/CAPTOKEN.H new file mode 100644 index 0000000..17dc8e0 --- /dev/null +++ b/WINVQ/VQM32/CAPTOKEN.H @@ -0,0 +1,80 @@ +/* +** 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 . +*/ + +#ifndef VQMCAPTOKEN_H +#define VQMCAPTOKEN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* captoken.h +* +* DESCRIPTION +* Text caption script definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* July 26, 1995 +* +****************************************************************************/ + +/* CaptionText: This structure describes a line of text to be displayed + * with the video/audio. + * + * Size - Size of caption in bytes. + * OnFrame - Frame number to display caption. + * OffFrame - Frame number to clear caption. + * Flags - Display modifiers. + * CPF - Characters to draw per frame. + * Xpos - X pixel position to display caption. + * Ypos - Y pixel position to display caption. + * BgPen - Background pen to use. + * FgPen - Foreground pen to use. + * Text - Text string to display. (WORD aligned) + */ +typedef struct _CaptionText { + unsigned short Size; + unsigned short OnFrame; + unsigned short OffFrame; + unsigned char Flags; + char CPF; + unsigned short Xpos; + unsigned short Ypos; + char BgPen; + char FgPen; + char Text[]; +} CaptionText; + +/* CaptionText flag definitions. */ +#define CTF_JUSTIFY (3<<0) /* Justification field. */ +#define CTF_ABS (0<<0) /* Use absolute X,Y positions. */ +#define CTF_CENTER (1<<0) /* Justify on Center */ +#define CTF_LEFT (2<<0) /* Justify on left */ +#define CTF_RIGHT (3<<0) /* Justify on right */ +#define CTF_FLASH (1<<4) /* Flash text. */ + +/* Function prototypes. */ +long BuildCaptions(char *name, char *buffer); + +#endif /* VQMCAPTOKEN_H */ + diff --git a/WINVQ/VQM32/CHRWIDTH.CPP b/WINVQ/VQM32/CHRWIDTH.CPP new file mode 100644 index 0000000..ff9d3e8 --- /dev/null +++ b/WINVQ/VQM32/CHRWIDTH.CPP @@ -0,0 +1,76 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* chrwidth.c +* +* DESCRIPTION +* Character pixel width. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* June 9, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* Char_Pixel_Width - Get the pixel width of a character. +* +****************************************************************************/ + +#include "font.h" + + +/**************************************************************************** +* +* NAME +* Char_Pixel_Width - Get the pixel width of a character. +* +* SYNOPSIS +* Width = Char_Pixel_Width(Character) +* +* long Char_Pixel_Width(char); +* +* FUNCTION +* Gets the pixel width of the specified character. +* +* INPUTS +* Character - Character to get the width for. +* +* RESULT +* Width - Width in pixels. +* +****************************************************************************/ + +#ifdef __WATCOMC__ +long __cdecl __saveregs Char_Pixel_Width(char chr) +#else +#pragma saveregs +long __cdecl Char_Pixel_Width(char chr) +#endif +{ + return (*(FontWidthBlockPtr + chr) + FontXSpacing); +} diff --git a/WINVQ/VQM32/COMPRESS.H b/WINVQ/VQM32/COMPRESS.H new file mode 100644 index 0000000..3db788b --- /dev/null +++ b/WINVQ/VQM32/COMPRESS.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMCOMP_H +#define VQMCOMP_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* compress.h +* +* DESCRIPTION +* Compression definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long __cdecl LCW_Compress(char const *source, char *dest, + unsigned long length); +unsigned long __cdecl LCW_Uncompress(char const *source, char *dest, + unsigned long length); +long AudioZap(void *source, void *dest, long size); +long __cdecl AudioUnzap(void *source, void *dest, long); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCOMP_H */ + diff --git a/WINVQ/VQM32/CRC.ASM b/WINVQ/VQM32/CRC.ASM new file mode 100644 index 0000000..b7443af --- /dev/null +++ b/WINVQ/VQM32/CRC.ASM @@ -0,0 +1,133 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* crc.asm +;* +;* DESCRIPTION +;* CRC checksum calculation. +;* +;* PROGRAMMER +;* Joe L. Bostic +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Calculate_CRC - Calculate CRC checksum. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Calculate_CRC - Calculate CRC checksum. +;* +;* SYNOPSIS +;* CRC = Calculate_CRC(Buffer, Length) +;* +;* long Calculate_CRC(void *, long); +;* +;* FUNCTION +;* Compute a CRC checksum for a block of memory. +;* +;* INPUTS +;* Buffer - Pointer to buffer to calculate CRC for. +;* Length - Length of buffer. +;* +;* RESULT +;* CRC - CRC value. +;* +;**************************************************************************** + + GLOBAL C Calculate_CRC:NEAR + PROC Calculate_CRC C NEAR USES esi ebx ecx edx + ARG buffer:NEAR PTR + ARG length:DWORD + + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + mov ecx,[length] ;Get length of data block + or ecx,ecx + jz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + or ecx,ecx + jz short ??remainder + +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov eax,ebx + ret + + ENDP Calculate_CRC + + END diff --git a/WINVQ/VQM32/CRC.H b/WINVQ/VQM32/CRC.H new file mode 100644 index 0000000..fd18a00 --- /dev/null +++ b/WINVQ/VQM32/CRC.H @@ -0,0 +1,51 @@ +/* +** 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 . +*/ + +#ifndef VQMCRC_H +#define VQMCRC_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* CRC.h +* +* DESCRIPTION +* CRC calculation definitions. +* +* PROGRAMMER +* Joe L. Bostic +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl Calculate_CRC(void const *buffer, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMCRC_H */ diff --git a/WINVQ/VQM32/DRAWCHAR.ASM b/WINVQ/VQM32/DRAWCHAR.ASM new file mode 100644 index 0000000..1da4fa9 --- /dev/null +++ b/WINVQ/VQM32/DRAWCHAR.ASM @@ -0,0 +1,395 @@ +; +; 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 . +; + +; $Header: U:\vq\projects\vqm32\drawchar.asv 1.1 08 May 1995 10:48:32 DENZIL_LONG $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : DRAWCHAR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +XPIXEL_MAX EQU 320 +YPIXEL_MAX EQU 200 + +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + EXTRN C FontPtr:NEAR PTR + +;*********************************************************** +; DRAW_CHAR +; +; VOID Draw_Char(BYTE fontchar, WORD x_pixel, WORD y_pixel); +; +; Draws a character to the screen only if given coordinates that will allow +; the entire character to be drawn on the screen else will exit. +; +; NOTE: This is a routine called by Text_Print. +; +;* + GLOBAL C Draw_Char:NEAR + PROC Draw_Char C NEAR USES eax ebx ecx edx esi edi + ARG fontchar:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + + LOCAL infoblock:DWORD + LOCAL offsetblock:DWORD + LOCAL widthblock:DWORD + LOCAL heightblock:DWORD + LOCAL fwidth:DWORD + LOCAL nextline:DWORD + LOCAL startv:BYTE + LOCAL dheight:BYTE + LOCAL wheight:BYTE + + mov esi,[FontPtr] + + ; Set up some working local variables. + + xor eax,eax + mov ax,[esi+FONTINFOBLOCK] ; get offset to info block + mov [infoblock],eax ; save offset to info block + mov ax,[esi+FONTOFFSETBLOCK] ; get offset to offset block + mov [offsetblock],eax ; save offset to offset block + mov ax,[esi+FONTWIDTHBLOCK] ; get offset to width block + mov [widthblock],eax ; save offset to width block + mov ax,[esi+FONTHEIGHTBLOCK] ; get offset to height block + mov [heightblock],eax ; save offset to height block + + ; Fetch character data offset -- if NULL then undefined character. + + mov ebx,[fontchar] + and ebx,0FFh + shl ebx,1 ; make word index + add ebx,[offsetblock] ; add offset to offset block + xor ecx,ecx + mov cx,[esi+ebx] ; load offset to font data + or ecx,ecx + jz ??exit ; is this character a null? if so exit + + ; If the character is off the left/right edge of the screen then abort. + + mov edx,[x_pixel] + cmp edx,XPIXEL_MAX + jae ??exit + + ; If the character is off the top/bottom edge of the screen then abort. + + mov ebx,[fontchar] ; get char + and ebx,0FFh + add ebx,[widthblock] ; add offset to width block + xor eax,eax + mov al,[esi+ebx] ; get width for character + mov [fwidth],eax ; save char width + add eax,edx ; ax = char len + x + cmp eax,XPIXEL_MAX + ja ??exit + + mov edi,edx ; save xpos in di + + mov edx,[y_pixel] + cmp edx,YPIXEL_MAX + jae ??exit + + mov ebx,[infoblock] ; get offset to offset block + xor eax,eax + ; get font max height from info block + mov al,[esi+ebx+FONTINFOMAXHEIGHT] + mov [wheight],al ; save max height of character + add eax,edx ; add height to y pos + cmp eax,YPIXEL_MAX ; will it go off the bottom + ja ??exit + +??vdraw: + mov ebx,[fontchar] ; get char + and ebx,0FFh + shl ebx,1 ; make 2 byte index + add ebx,[heightblock] ; add offset to height block + mov ah,[esi+ebx] ; get start vertical for character + mov [startv],ah ; save start vertical for later + mov al,[esi+ebx+1] ; get data height for character + mov [dheight],al ; save data height for later + add ah,al ; add background and data + sub [wheight],ah ; remaining background height + + add esi,ecx ; add font offset to font data + + push edx + mov eax,XPIXEL_MAX + mul edx + add edi,eax + pop edx + + mov eax,XPIXEL_MAX + sub eax,[fwidth] + mov [nextline],eax ; ?? to add to index for the nextline + + add edi,0A0000h + mov ebx,OFFSET ColorXlat ; setup up bx for xlat commands + xor ecx,ecx + mov cl,[startv] ; number of scan lines that are + ; background color + or ecx,ecx ; if starting vertical is zero + je short ??skiplead ; skip drawing top background lines + + mov al,0 + xlat [ebx] ; get background color + + or al,al ; check for none zero color + jne short ??lheight ; update background color + + push edx + mov eax,XPIXEL_MAX + mul ecx + add edi,eax + pop edx + + mov ebx,OFFSET ColorXlat ; restore bx for xlat commands + jmp SHORT ??skiplead + +??lheight: + mov edx,[fwidth] ; width of char + +??lwidth: + stosb ; write out line of pixels for width + dec edx + jne ??lwidth + +??lnext: + add edi,[nextline] ; goto next line at the start of char + loop ??lheight ; any more lines + +??skiplead: + mov cl,[dheight] ; number of scan lines that are data + or ecx,ecx ; is there any data to be drawn + je short ??exit + +??vheight: + mov edx,[fwidth] ; width of char + +??vwidth: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and al,00FH ; get lonibble + xlat [ebx] ; get new color + or al,al + je short ??chklowidth ; skip color zero + + mov [edi],al ; write out pixel of lonibble + +??chklowidth: + inc edi + dec edx + je short ??vnext ; check if done with width of char + + mov al,ah ; get byte value + and al,0F0H ; get hinibble + xlat [ebx] ; get new color + or al,al + je short ??chkhiwidth ; skip color zero + + mov [edi],al ; write out pixel of hinibble + +??chkhiwidth: + inc edi + dec edx + jne ??vwidth ; check if done with width of char + +??vnext: + add edi,[nextline] ; next line at start of char + loop ??vheight ; any more lines + + +??trail: + mov cl,[wheight] ; remaining height of background color + or ecx,ecx ; if trailing height is zero + jle short ??exit ; skip drawing bottom background lines + + mov al,0 + xlat [ebx] ; get background color + or al,al ; check for color zero + je short ??exit ; skip drawing + +??theight: + mov edx,[fwidth] ; width of char + +??twidth: + stosb ; write out line of pixels for width + dec edx + jne ??twidth + +??tnext: + add edi,[nextline] ; next line at start of char + loop ??theight ; any more lines + + +??exit: + ret + + ENDP Draw_Char + +;*********************************************************** + + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + GLOBAL C Set_Font_Palette_Range:NEAR + PROC Set_Font_Palette_Range C NEAR USES eax ebx ecx edi esi + ARG palette:NEAR PTR + ARG start:DWORD + ARG endval:DWORD + + cld + mov esi,[palette] + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + loop ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** +; DRAW_CHAR_SETUP +; +; VOID Draw_Char_Setup(VOID); +; +; This routine sets up code segment variables for Draw_Char. +; +; NOTE: This is a routine called by Set_Font. +; +;* + GLOBAL C Draw_Char_Setup:NEAR + PROC Draw_Char_Setup C NEAR + + ret + + ENDP Draw_Char_Setup + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + +;*********************************************************** + END + diff --git a/WINVQ/VQM32/DRAWCHAR.BAK b/WINVQ/VQM32/DRAWCHAR.BAK new file mode 100644 index 0000000..3791de9 --- /dev/null +++ b/WINVQ/VQM32/DRAWCHAR.BAK @@ -0,0 +1,395 @@ +; +; 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 . +; + +; $Header: U:\vq\projects\vqm32\drawchar.asv 1.1 08 May 1995 10:48:32 DENZIL_LONG $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : DRAWCHAR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +XPIXEL_MAX EQU 320 +YPIXEL_MAX EQU 200 + +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + EXTRN FontPtr:NEAR PTR + +;*********************************************************** +; DRAW_CHAR +; +; VOID Draw_Char(BYTE fontchar, WORD x_pixel, WORD y_pixel); +; +; Draws a character to the screen only if given coordinates that will allow +; the entire character to be drawn on the screen else will exit. +; +; NOTE: This is a routine called by Text_Print. +; +;* + GLOBAL C Draw_Char:NEAR + PROC Draw_Char C NEAR USES eax ebx ecx edx esi edi + ARG fontchar:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + + LOCAL infoblock:DWORD + LOCAL offsetblock:DWORD + LOCAL widthblock:DWORD + LOCAL heightblock:DWORD + LOCAL fwidth:DWORD + LOCAL nextline:DWORD + LOCAL startv:BYTE + LOCAL dheight:BYTE + LOCAL wheight:BYTE + + mov esi,[FontPtr] + + ; Set up some working local variables. + + xor eax,eax + mov ax,[esi+FONTINFOBLOCK] ; get offset to info block + mov [infoblock],eax ; save offset to info block + mov ax,[esi+FONTOFFSETBLOCK] ; get offset to offset block + mov [offsetblock],eax ; save offset to offset block + mov ax,[esi+FONTWIDTHBLOCK] ; get offset to width block + mov [widthblock],eax ; save offset to width block + mov ax,[esi+FONTHEIGHTBLOCK] ; get offset to height block + mov [heightblock],eax ; save offset to height block + + ; Fetch character data offset -- if NULL then undefined character. + + mov ebx,[fontchar] + and ebx,0FFh + shl ebx,1 ; make word index + add ebx,[offsetblock] ; add offset to offset block + xor ecx,ecx + mov cx,[esi+ebx] ; load offset to font data + or ecx,ecx + jz ??exit ; is this character a null? if so exit + + ; If the character is off the left/right edge of the screen then abort. + + mov edx,[x_pixel] + cmp edx,XPIXEL_MAX + jae ??exit + + ; If the character is off the top/bottom edge of the screen then abort. + + mov ebx,[fontchar] ; get char + and ebx,0FFh + add ebx,[widthblock] ; add offset to width block + xor eax,eax + mov al,[esi+ebx] ; get width for character + mov [fwidth],eax ; save char width + add eax,edx ; ax = char len + x + cmp eax,XPIXEL_MAX + ja ??exit + + mov edi,edx ; save xpos in di + + mov edx,[y_pixel] + cmp edx,YPIXEL_MAX + jae ??exit + + mov ebx,[infoblock] ; get offset to offset block + xor eax,eax + ; get font max height from info block + mov al,[esi+ebx+FONTINFOMAXHEIGHT] + mov [wheight],al ; save max height of character + add eax,edx ; add height to y pos + cmp eax,YPIXEL_MAX ; will it go off the bottom + ja ??exit + +??vdraw: + mov ebx,[fontchar] ; get char + and ebx,0FFh + shl ebx,1 ; make 2 byte index + add ebx,[heightblock] ; add offset to height block + mov ah,[esi+ebx] ; get start vertical for character + mov [startv],ah ; save start vertical for later + mov al,[esi+ebx+1] ; get data height for character + mov [dheight],al ; save data height for later + add ah,al ; add background and data + sub [wheight],ah ; remaining background height + + add esi,ecx ; add font offset to font data + + push edx + mov eax,XPIXEL_MAX + mul edx + add edi,eax + pop edx + + mov eax,XPIXEL_MAX + sub eax,[fwidth] + mov [nextline],eax ; ?? to add to index for the nextline + + add edi,0A0000h + mov ebx,OFFSET ColorXlat ; setup up bx for xlat commands + xor ecx,ecx + mov cl,[startv] ; number of scan lines that are + ; background color + or ecx,ecx ; if starting vertical is zero + je short ??skiplead ; skip drawing top background lines + + mov al,0 + xlat [ebx] ; get background color + + or al,al ; check for none zero color + jne short ??lheight ; update background color + + push edx + mov eax,XPIXEL_MAX + mul ecx + add edi,eax + pop edx + + mov ebx,OFFSET ColorXlat ; restore bx for xlat commands + jmp SHORT ??skiplead + +??lheight: + mov edx,[fwidth] ; width of char + +??lwidth: + stosb ; write out line of pixels for width + dec edx + jne ??lwidth + +??lnext: + add edi,[nextline] ; goto next line at the start of char + loop ??lheight ; any more lines + +??skiplead: + mov cl,[dheight] ; number of scan lines that are data + or ecx,ecx ; is there any data to be drawn + je short ??exit + +??vheight: + mov edx,[fwidth] ; width of char + +??vwidth: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and al,00FH ; get lonibble + xlat [ebx] ; get new color + or al,al + je short ??chklowidth ; skip color zero + + mov [edi],al ; write out pixel of lonibble + +??chklowidth: + inc edi + dec edx + je short ??vnext ; check if done with width of char + + mov al,ah ; get byte value + and al,0F0H ; get hinibble + xlat [ebx] ; get new color + or al,al + je short ??chkhiwidth ; skip color zero + + mov [edi],al ; write out pixel of hinibble + +??chkhiwidth: + inc edi + dec edx + jne ??vwidth ; check if done with width of char + +??vnext: + add edi,[nextline] ; next line at start of char + loop ??vheight ; any more lines + + +??trail: + mov cl,[wheight] ; remaining height of background color + or ecx,ecx ; if trailing height is zero + jle short ??exit ; skip drawing bottom background lines + + mov al,0 + xlat [ebx] ; get background color + or al,al ; check for color zero + je short ??exit ; skip drawing + +??theight: + mov edx,[fwidth] ; width of char + +??twidth: + stosb ; write out line of pixels for width + dec edx + jne ??twidth + +??tnext: + add edi,[nextline] ; next line at start of char + loop ??theight ; any more lines + + +??exit: + ret + + ENDP Draw_Char + +;*********************************************************** + + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + GLOBAL C Set_Font_Palette_Range:NEAR + PROC Set_Font_Palette_Range C NEAR USES eax ebx ecx edi esi + ARG palette:NEAR PTR + ARG start:DWORD + ARG endval:DWORD + + cld + mov esi,[palette] + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + loop ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** +; DRAW_CHAR_SETUP +; +; VOID Draw_Char_Setup(VOID); +; +; This routine sets up code segment variables for Draw_Char. +; +; NOTE: This is a routine called by Set_Font. +; +;* + GLOBAL C Draw_Char_Setup:NEAR + PROC Draw_Char_Setup C NEAR + + ret + + ENDP Draw_Char_Setup + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + +;*********************************************************** + END + diff --git a/WINVQ/VQM32/FILLRECT.ASM b/WINVQ/VQM32/FILLRECT.ASM new file mode 100644 index 0000000..405d761 --- /dev/null +++ b/WINVQ/VQM32/FILLRECT.ASM @@ -0,0 +1,216 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* fillrect.asm +;* +;* DESCRIPTION +;* Rectangle rendering. +;* +;* PROGRAMMER +;* Joe L. Bostic +;* +;* DATE +;* Febuary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Fill_Rect - Draw a filled rectangle. +;* Eor_Region - Hilights or unhilights a region by EOR it. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Eor_Region - Hilights or unhilights a region by EOR it. +;* +;* SYNOPSIS +;* Eor_Region(X1, Y1, X2, Y2, Color) +;* +;* void Eor_Region(long, long, long, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* X1 - Starting X position. +;* Y1 - Starting Y position. +;* X2 - Ending X position. +;* Y2 - Ending Y position. +;* Color - Color to EOR. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Eor_Region:NEAR + PROC Eor_Region C NEAR USES eax ebx ecx edx edi + ARG x1_pixel:DWORD + ARG y1_pixel:DWORD + ARG x2_pixel:DWORD + ARG y2_pixel:DWORD + ARG color:DWORD + LOCAL eorit:DWORD + + mov [eorit],1 + jmp short Fill_Rect_Entry + + ENDP Eor_Region + + +;**************************************************************************** +;* +;* NAME +;* Fill_Rect - Draw a filled rectangle. +;* +;* SYNOPSIS +;* Fill_Rect(X1, Y1, X2, Y2, Color) +;* +;* void Fill_Rect(long, long, long, long, long); +;* +;* FUNCTION +;* Fill a rectangle area with a specified color. +;* +;* INPUTS +;* X1 - Starting X position. +;* Y1 - Starting Y position. +;* X2 - Ending X position. +;* Y2 - Ending Y position. +;* Color - Color to draw rectangle +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Fill_Rect:NEAR + PROC Fill_Rect C NEAR USES eax ebx ecx edx edi + ARG x1_pixel:DWORD + ARG y1_pixel:DWORD + ARG x2_pixel:DWORD + ARG y2_pixel:DWORD + ARG color:DWORD + LOCAL eorit:DWORD + + mov [eorit],0 + +Fill_Rect_Entry: + cld ; always go forward + mov edi,0A0000h + + ; Verify bounds of x1_pixel. + mov eax,[x1_pixel] + cmp eax,320 ;XPIXEL_MAX + jae short ??exit + + ; Verify bounds of x2_pixel. + mov ebx,[x2_pixel] + cmp ebx,320 ;XPIXEL_MAX + jae short ??exit + + ; Backward rectangles are legal -- adjust for it. + cmp eax,ebx + jbe short ??okx + xchg eax,ebx +??okx: + + ; Verify bounds of y1_pixel. + mov ecx,[y1_pixel] + cmp ecx,200 ;YPIXEL_MAX + jae short ??exit + + ; Verify bounds of y2_pixel. + mov edx,[y2_pixel] + cmp edx,200 ;YPIXEL_MAX + jae short ??exit + + ; Backward rectangles are legal -- adjust for it. + cmp ecx,edx + jbe short ??oky + xchg ecx,edx +??oky: + + ; Set DX for height and BX for width. + sub edx,ecx + inc edx + sub ebx,eax + inc ebx + + ; Adjust DI to match offset into page of upper left corner. + push edx + push eax + mov eax,320 ;XPIXEL_MAX + mul ecx + add edi,eax ; Add in Y offset. + pop edx + add edi,edx ; Add in X offset. + pop edx + + ; Fill the region with the specified color. + mov eax,320 ;XPIXEL_MAX + sub eax,ebx + xchg eax,[color] + mov ah,al + cmp [eorit],0 + je short ??loop + +;------ EOR rectangle filling. +??loop2: + mov ecx,ebx ; Length of row +??loop2in: + lodsb + xor al,ah + stosb + loop ??loop2in + add edi,[color] ; Modulo add for next line prep. + dec edx + jg short ??loop2 + jmp short ??exit + +;------ Conventional rectangle filling. +??loop: + mov ecx,ebx ; Length of row + shr ecx,1 + rep stosw + adc ecx,0 + rep stosb + add edi,[color] ; Modulo add for next line prep. + dec edx + jg short ??loop + +??exit: + ret + + ENDP Fill_Rect + + END + diff --git a/WINVQ/VQM32/FONT.CPP b/WINVQ/VQM32/FONT.CPP new file mode 100644 index 0000000..227cc43 --- /dev/null +++ b/WINVQ/VQM32/FONT.CPP @@ -0,0 +1,222 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.c +* +* DESCRIPTION +* Font manipulation. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* Load_Font - Open a font for use. +* Set_Font - Set the default system font. +* String_Pixel_Width - Get the pixel width of a string. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include "font.h" + + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* min and max macros */ +#ifdef __cplusplus +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#endif + +void const *FontPtr = NULL; +char FontHeight = 8; +char FontWidth = 8; +int FontXSpacing = 0; +int FontYSpacing = 0; +char *FontWidthBlockPtr = NULL; + + +/**************************************************************************** +* +* NAME +* Load_Font - Open a font for use. +* +* SYNOPSIS +* Font = Load_Font(Name) +* +* char *Load_Font(char *); +* +* FUNCTION +* Open a graphics font for use by Text_Print(). Use free() to dispose +* of the font. +* +* INPUTS +* Name - Name of font file to open. +* +* RESULT +* Font - Pointer to font, NULL if error. +* +****************************************************************************/ + +void *cdecl Load_Font(char const *name) +{ + Font *font = NULL; + long fh; + short size; + short valid; + + /* Open the font. */ + if ((fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + + /* Get the size of the font. */ + if (read(fh, &size, 2) == 2) { + + /* Allocate memory to contain the font. */ + if ((font = (Font *)malloc((unsigned long)size)) != NULL) { + valid = 0; + + /* Read in the body of the font. */ + if (read(fh, &font->CompMethod, (unsigned long)(size - 2)) + == (unsigned long)(size - 2)) { + + /* Verify the validity of the font. */ + if ((font->CompMethod == 0) && (font->NumBlks == 5)) { + font->Size = size; + valid = 1; + } + } + + /* Free the font if it is not valid. */ + if (valid == 0) { + free(font); + font = NULL; + } + } + } + + /* Close the font. */ + close(fh); + } + + return ((char *)font); +} + + +/**************************************************************************** +* +* NAME +* Set_Font - Set the default system font. +* +* SYNOPSIS +* OldFont = Set_Font(Font) +* +* char *Set_Font(char *); +* +* FUNCTION +* Sets up the specified font as the default font used by the system. +* +* INPUTS +* Font - Pointer to Font to set as default. (NULL returns current font) +* +* RESULT +* OldFont - Previous font. +* +****************************************************************************/ + +void *cdecl Set_Font(void const *font) +{ + void const *oldfont; + FontInfo *fi; + + oldfont = FontPtr; + + if (font != NULL) { + FontWidthBlockPtr = ((char *)font + ((Font *)font)->WidthBlk); + fi = (FontInfo *)((char *)font + ((Font *)font)->InfoBlk); + FontHeight = fi->MaxHeight; + FontWidth = fi->MaxWidth; + FontPtr = font; + } + + return ((void *)oldfont); +} + + +/**************************************************************************** +* +* NAME +* String_Pixel_Width - Get the pixel width of a string. +* +* SYNOPSIS +* Width = String_Pixel_Width(String) +* +* long String_Pixel_Width(char *); +* +* FUNCTION +* Calculates the pixel width of a string of characters. +* +* INPUTS +* String - Pointer to string to calculate width for. +* +* RESULT +* Width - Width of string in pixels. +* +****************************************************************************/ + +unsigned short String_Pixel_Width(char const *string) +{ + long width = 0; + long largest = 0; + + while (*string != NULL) { + if (*string == '\r') { + string++; + largest = max(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); + } + } + + largest = max(largest, width); + + return (largest); +} + diff --git a/WINVQ/VQM32/FONT.H b/WINVQ/VQM32/FONT.H new file mode 100644 index 0000000..d84cd42 --- /dev/null +++ b/WINVQ/VQM32/FONT.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMFONT_H +#define VQMFONT_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* font.h +* +* DESCRIPTION +* Font definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 9, 1995 +* +****************************************************************************/ + +/* Font: A Westwood style font. + * + * Size - Size of font. + * CompMethod - Compression method of font. (0 = none) + * NumBlks - Number of data blocks. + * InfoBlk - Offset to font information block. + * OffsetBlk - Offset to character offset block. + * WidthBlk - Offset to character width block. + * DataBlk - Offset to character data block. + * HeightBlk - Offset to character height block. + */ +typedef struct _Font { + unsigned short Size; + unsigned char CompMethod; + unsigned char NumBlks; + unsigned short InfoBlk; + unsigned short OffsetBlk; + unsigned short WidthBlk; + unsigned short DataBlk; + unsigned short HeightBlk; +} Font; + +typedef struct _FontInfo { + long huh; + unsigned char MaxHeight; + unsigned char MaxWidth; +} FontInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void const *FontPtr; +extern int FontXSpacing; +extern int FontYSpacing; + +#ifdef __cplusplus +} +#endif + +extern char FontWidth; +extern char FontHeight; +extern char *FontWidthBlockPtr; + +/* Function prototypes. */ +void *cdecl Load_Font(char const *name); +void *cdecl Set_Font(void const *font); +unsigned short __cdecl String_Pixel_Width(char const *string); + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __WATCOMC__ +long __cdecl __saveregs Char_Pixel_Width(char chr); +#else +long __cdecl Char_Pixel_Width(char chr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VQMFONT_H */ + diff --git a/WINVQ/VQM32/GRAPHICS.H b/WINVQ/VQM32/GRAPHICS.H new file mode 100644 index 0000000..0aa51d2 --- /dev/null +++ b/WINVQ/VQM32/GRAPHICS.H @@ -0,0 +1,55 @@ +/* +** 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 . +*/ + +#ifndef VQMGRAPHICS_H +#define VQMGRAPHICS_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* graphics.h +* +* DESCRIPTION +* Graphic rendering and manipulation definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* April 27, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Eor_Region(long sx, long sy, long dx, long dy, long color); +void __cdecl Fill_Rect(long x1, long y1, long x2, long y2, long color); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMGRAPHICS_H */ + diff --git a/WINVQ/VQM32/HUFFCMP.CPP b/WINVQ/VQM32/HUFFCMP.CPP new file mode 100644 index 0000000..0a2142e --- /dev/null +++ b/WINVQ/VQM32/HUFFCMP.CPP @@ -0,0 +1,708 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffcmp.h +* +* DESCRIPTION +* Huffman order 0 compressor. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* HuffCompress - Compress data using huffman order 0 compression. +* HuffCount - Count the frequency of occurence of every byte. +* HuffScaleCounts - Scale down the frequency counts. +* RLEHuffCounts - Run Length Encode the Huffman counts. +* ConvertToCodes - Convert the Huffman tree into a table of codes. +* HuffEncode - Huffman encode the data. +* +****************************************************************************/ + +#include +#include "huffman.h" + + +/**************************************************************************** +* +* NAME +* HuffCompress - Compress data using huffman order 0 compression. +* +* SYNOPSIS +* Size = HuffCompress(Data, Buffer, Size, Temp) +* +* long HuffCompress(unsigned char *, unsigned char *, long, char *); +* +* FUNCTION +* This function performs an order 0 Huffman encoding of the input data. +* The algorithm used is fairly straightforward. First a count is made of +* all the bytes in the input data, then the counts are scaled down to +* a single byte representation in the node array. After the counts are +* scaled, a Huffman decoding tree is built from the node array. Then +* a code array is built by traversing the tree for each symbol. Finally, +* the input data is compressed. +* +* INPUTS +* Data - Pointer to data to compress. +* Buffer - Pointer to compressed data. +* Size - Length of data to compress. +* Temp - Pointer to temporary working buffer. (Must be >= 5120 bytes!) +* +* RESULT +* Size - Compressed size. +* +****************************************************************************/ + +long __cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *temp) +{ + #if(1) + TreeNode *nodes; + HuffCode *codes; + long size; + long root; + + /* Initialize variables */ + nodes = (TreeNode *)temp; + temp += (514 * sizeof(TreeNode)); + codes = (HuffCode *)temp; + + /* Analyze the frequency of the data. */ + HuffCount(data, nodes, length, 1); + HuffScaleCounts(nodes); + + /* Save the counts for the decompression. */ + size = RLEHuffCounts(nodes, buffer); + buffer += size; + + /* Build the Huffman decode tree and generate codes for encoding. */ + root = BuildHuffTree(nodes); + ConvertToCodes(nodes, codes, 0, 0, root); + + /* Encode the data. */ + size += HuffEncode(data, buffer, codes, length); + + return (size); + #else + TreeNode *nodes; + HuffCode *codes; + unsigned long *counts; + unsigned long max_count; + long i; + long size; + long first; + long last; + long next; + unsigned long symbol; + unsigned long mask; + + /* Initialize variables. */ + nodes = (TreeNode *)temp; + temp += (514 * sizeof(TreeNode)); + counts = (unsigned long *)temp; + codes = (HuffCode *)temp; + + /* Zero the initial counts. */ + memset(temp, 0, 256 * sizeof(unsigned long)); + size = 0; + + /*------------------------------------------------------------------------- + * Calculate the distribution of the data then scale down the counts so + * they fit in an 8 bit value. This is done in order to limit the size of + * the codes to 16 bits. + *-----------------------------------------------------------------------*/ + i = 0; + + while (i < length) { + counts[((unsigned char *)data)[i]]++; + i++; + } + + /* Scale down the counts. */ + max_count = 0; + + /* Find the maximum count. */ + for (i = 0; i < 256; i++) { + if (counts[i] > max_count) { + max_count = counts[i]; + } + } + + if (max_count == 0) { + counts[0] = 1; + max_count = 1; + } + + max_count /= 255; + max_count++; + + /* Scale down the counts. */ + for (i = 0; i < 256; i++) { + nodes[i].count = (unsigned long)(counts[i] / max_count); + + /* Make sure that a node with a non-zero count does not get scaled + * down to zero. + */ + if ((nodes[i].count == 0) && (counts[i] != 0)) { + nodes[i].count = 1; + } + } + + nodes[HUFF_EOS].count = 1; + + /*------------------------------------------------------------------------- + * OUTPUT THE COUNTS + * + * In order to save space, we store runs of counts in the following format: + * + * Start, Stop, Counts..., Start, Stop, Counts..., ..., 0 + * + * The list is terminated by storing a start value of zero (0). + * + * In order to efficiently use this format, we do not want to stop a run + * because of just one or two zero counts. So we include zero counts of + * less than three (3) in the run. + *-----------------------------------------------------------------------*/ + + /* Find the first occurance of a non-zero count. */ + first = 0; + + while ((first < 255) && (nodes[first].count == 0)) { + first++; + } + + /* Each time I hit the start of the loop, I assume that first is the + * number for a run of non-zero values. The rest of the loop is + * concerned with finding the value for last, which is the end of the + * run, and the value of next, which is the start of the next run. + * At the end of the loop, I assign next to first, so it starts in on + * the next run. + */ + for (; first < 256; first = next) { + last = first + 1; + + for (;;) { + + /* Find the end of the run of non-zeros. */ + for (; last < 256; last++) { + if (nodes[last].count == 0) { + break; + } + } + + last--; + + /* Check the beginning of the next run of non-zero counts. */ + for (next = last + 1; next < 256; next++) { + if (nodes[next].count != 0) { + break; + } + } + + /* Quit the run if we have reached the end. */ + if (next > 255) { + break; + } + + /* Quit the run if there is more than three non-zero counts. */ + if ((next - last) > 3) { + break; + } + + last = next; + }; + + /* Output Start and Stop. */ + *buffer++ = first; + *buffer++ = last; + + /* Output the run of counts. */ + for (i = first; i <= last; i++) { + *buffer++ = (char)nodes[i].count; + } + + size += (((last - first) + 1) + 2); + } + + /* Output terminator. */ + *buffer++ = 0; + size++; + + /*------------------------------------------------------------------------- + * Build the Huffman tree. All active nodes are scanned in order to locate + * the two nodes with the minimum weights. These two weights are added + * together and assigned to a new node. The new node makes the two minimum + * nodes into its 0 child and 1 child. The two minimum nodes are then + * marked as inactive. This process repeats until their is only one node + * left, which is the root node. + *-----------------------------------------------------------------------*/ + + /* Node 513 is used to arbitratilly provide a node with a guaranteed + * maximum value. + */ + nodes[513].count = 0xFFFF; + + for (next = (HUFF_EOS + 1); ; next++) { + first = 513; + last = 513; + + for (i = 0; i < next; i++) { + + /* We are only concerned with non-zero count nodes. */ + if (nodes[i].count != 0) { + if (nodes[i].count < nodes[first].count) { + last = first; + first = i; + } else if (nodes[i].count < nodes[last].count) { + last = i; + } + } + } + + if (last == 513) { + break; + } + + nodes[next].count = (nodes[first].count + nodes[last].count); + nodes[first].count = 0; + nodes[last].count = 0; + nodes[next].child0 = (first << 3); + nodes[next].child1 = (last << 3); + } + + next--; + + /*------------------------------------------------------------------------- + * Convert the Huffman tree into an encoding table then encode the data. + *-----------------------------------------------------------------------*/ + ConvertToCodes(nodes, codes, 0, 0, (next << 3)); + + /* Encode the data. */ + *buffer = 0; + next = 0x80; + i = 0; + + do { + if (i < length) { + symbol = ((unsigned char *)data)[i]; + } else { + symbol = HUFF_EOS; + } + + mask = 1L << (codes[symbol].bits - 1); + + while (mask != 0) { + + /* Set a bit in the output stream for each bit in the code. */ + if (mask & codes[symbol].code) { + *buffer |= next; + } + + /* Next bit position. */ + next >>= 1; + + /* Advance to the next byte in the output stream when the current + * byte is full. + */ + if (next == 0) { + buffer++; + *buffer = 0; + next = 0x80; + size++; + } + + /* Next bit in the code. */ + mask >>= 1; + } + + i++; + } while (symbol != HUFF_EOS); + + if (next != 0x80) { + size++; + } + + return (size); + #endif +} + + +/**************************************************************************** +* +* NAME +* HuffCount - Count the frequency of occurence of every byte. +* +* SYNOPSIS +* HuffCount(Data, Nodes, Length, Zero) +* +* void HuffCounts(unsigned char *, TreeNode *, long, long); +* +* FUNCTION +* This function counts the frequency of occurence of every byte in the +* input data. The nodes must be initialized to zero prior to calling this +* function for the first time, otherwise the counts will be flawed. +* +* INPUTS +* Data - Pointer to data to analyze. +* TreeNode - Pointer to array of nodes. +* Length - Length of data to analyze. +* Zero - Zero any previous counts flag. (TRUE = zero counts) +* +* RESULT +* Size - Amount of buffer used to hold counts. +* +****************************************************************************/ + +void __cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero) +{ + long i; + + /* Zero any previous counts. */ + if (zero) { + for (i = 0; i < 256; i++) { + nodes[i].count = 0; + } + } + + /* Calculate the distribution of the data. */ + i = 0; + + while (i < length) { + nodes[((unsigned char *)data)[i]].count++; + i++; + } +} + + +/**************************************************************************** +* +* NAME +* HuffScaleCounts - Scale down the frequency counts. +* +* SYNOPSIS +* HuffScaleCounts(Nodes) +* +* void HuffScaleCounts(TreeNode *); +* +* FUNCTION +* In order to limit the size of the Huffman codes to 16 bits, we must +* scale down the counts so they can be represented by a BYTE size value. +* +* INPUTS +* Nodes - Pointer to nodes to scale counts for. +* +* RESULT +* NONE +* +****************************************************************************/ + +void __cdecl HuffScaleCounts(TreeNode *nodes) +{ + unsigned long max_count; + unsigned long unscaled; + long i; + long first; + long last; + long next; + + /* Scale down the counts so they fit in an 8 bit value. This is done in + * order to limit the size of the codes to 16 bits. + */ + max_count = 0; + + /* Find the maximum count. */ + for (i = 0; i < 256; i++) { + if (nodes[i].count > max_count) { + max_count = nodes[i].count; + } + } + + if (max_count == 0) { + nodes[0].count = 1; + max_count = 1; + } + + max_count /= 255; + max_count++; + + /* Scale down the counts. */ + for (i = 0; i < 256; i++) { + unscaled = nodes[i].count; + nodes[i].count /= max_count; + + /* Make sure that a node with a non-zero count does not get scaled + * down to zero. + */ + if ((nodes[i].count == 0) && (unscaled != 0)) { + nodes[i].count = 1; + } + } + + nodes[HUFF_EOS].count = 1; +} + + +/**************************************************************************** +* +* NAME +* RLEHuffCounts - Run Length Encode the Huffman counts. +* +* SYNOPSIS +* Size = RLEHuffCounts(Nodes, Buffer) +* +* long RLEHuffCounts(TreeNode *, unsigned char *); +* +* FUNCTION +* In order for the decoder to build the same model, we have to transmit +* the symbol counts to it. To save space we do not save all 256 symbols +* unconditionally, instead we run length encode the counts. The format +* used to store the counts is as follows: +* +* Start, Stop, Counts[n], Start, Stop, Counts[n], .... 0 +* +* Note: The sequence is terminated by a start value of 0. Also at least +* 1 run of counts has to be stored, even if the first start value is 0. +* +* INPUTS +* Nodes - Pointer to initialized nodes. +* Buffer - Pointer to buffer to store RLE'd counts. +* +* RESULT +* Size - Size of the RLE'd counts. +* +****************************************************************************/ + +long __cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer) +{ + long i; + long first; + long last; + long next; + long size = 0; + + /* Find the first occurance of a non-zero count. */ + first = 0; + + while ((first < 255) && (nodes[first].count == 0)) { + first++; + } + + /* Each time I hit the start of the loop, I assume that first is the + * number for a run of non-zero values. The rest of the loop is + * concerned with finding the value for last, which is the end of the + * run, and the value of next, which is the start of the next run. + * At the end of the loop, I assign next to first, so it starts in on + * the next run. + */ + for (; first < 256; first = next) { + last = first + 1; + + for (;;) { + + /* Find the end of the run of non-zeros. */ + for (; last < 256; last++) { + if (nodes[last].count == 0) { + break; + } + } + + last--; + + /* Check the beginning of the next run of non-zero counts. */ + for (next = last + 1; next < 256; next++) { + if (nodes[next].count != 0) { + break; + } + } + + /* Quit the run if we have reached the end. */ + if (next > 255) { + break; + } + + /* Quit the run if there is more than three non-zero counts. */ + if ((next - last) > 3) { + break; + } + + last = next; + }; + + /* Output Start and Stop. */ + *buffer++ = first; + *buffer++ = last; + + /* Output the run of counts. */ + for (i = first; i <= last; i++) { + *buffer++ = (unsigned char)nodes[i].count; + } + + size += (((last - first) + 1) + 2); + } + + /* Output terminator. */ + *buffer = 0; + size++; + + return (size); +} + + +/**************************************************************************** +* +* NAME +* ConvertToCodes - Convert the Huffman tree into a table of codes. +* +* SYNOPSIS +* ConvertToCodes(Nodes, Codes, Code, Bits, Node) +* +* void ConvertToCodes(TreeNode *, HuffCode *, unsigned short, short, +* short); +* +* FUNCTION +* Since the Huffman tree is built as a decoding tree, there is no simple +* way to get the encoding values for each symbol. This routine +* recursively walks through the tree, adding the child bits to each code +* until it gets to a leaf. When it gets to a leaf, it stores the code +* value. +* +* INPUTS +* Nodes - Pointer to the Huffman tree. +* Codes - Pointer to the table of codes to generate. +* Code - Code being built (initialize with 0). +* Bits - Number of bits the code is comprised of (initialize with 0). +* Node - Number of the current node. +* +* RESULT +* NONE +* +****************************************************************************/ + +void __cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node) +{ + node >>= 3; + + if (node <= HUFF_EOS) { + codes[node].code = code; + codes[node].bits = bits; + return; + } + + code <<= 1; + bits++; + ConvertToCodes(nodes, codes, code, bits, nodes[node].child0); + ConvertToCodes(nodes, codes, code|1, bits, nodes[node].child1); +} + + +/**************************************************************************** +* +* NAME +* HuffEncode - Huffman encode the data. +* +* SYNOPSIS +* Size = HuffEncode(Data, Buffer, Codes, Length); +* +* long HuffEncode(unsigned char *, unsigned char *, HuffCodes *, long); +* +* FUNCTION +* Encoding of the data is simple. Each byte of data is taken as the index +* of the codes array, the corresponding code is then put in the output. +* +* INPUTS +* Data - Pointer to data to encode. +* Buffer - Pointer to buffer to hold encoded data. +* Codes - Pointer to array of Huffman codes. +* Length - Length of data buffer to encode. +* +* RESULT +* Size - Size of encoded data. +* +****************************************************************************/ + +long __cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length) +{ + long i; + long size; + long next; + unsigned long mask; + unsigned long symbol; + + /* Initialize */ + *buffer = 0; + next = 0x80; + i = 0; + + do { + if (i < length) { + symbol = ((unsigned char *)data)[i]; + } else { + symbol = HUFF_EOS; + } + + mask = 1L << (codes[symbol].bits - 1); + + while (mask != 0) { + + /* Set a bit in the output stream for each bit in the code. */ + if (mask & codes[symbol].code) { + *buffer |= next; + } + + /* Next bit position. */ + next >>= 1; + + /* Advance to the next byte in the output stream when the current + * byte is full. + */ + if (next == 0) { + buffer++; + *buffer = 0; + next = 0x80; + size++; + } + + /* Next bit in the code. */ + mask >>= 1; + } + + i++; + } while (symbol != HUFF_EOS); + + if (next != 0x80) { + size++; + } + + return (size); +} + diff --git a/WINVQ/VQM32/HUFFDCMP.ASM b/WINVQ/VQM32/HUFFDCMP.ASM new file mode 100644 index 0000000..39a41a7 --- /dev/null +++ b/WINVQ/VQM32/HUFFDCMP.ASM @@ -0,0 +1,391 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* huffdcmp.asm +;* +;* DESCRIPTION +;* Huffman order 0 decompressor. +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* May 22, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* HuffDecompress - Decompress Huffman order 0 encoded data. +;* BuildHuffTree - Build the Huffman decode tree. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + + STRUC TreeNode +count DD ? ;Weight of the node +child0 DW ? ;Child node 0 +child1 DW ? ;Child node 1 + ENDS TreeNode + +HUFF_EOS EQU 256 + + CODESEG + +;**************************************************************************** +;* +;* NAME +;* HuffDecompress - Decompress Huffman order 0 encoded data. +;* +;* SYNOPSIS +;* Size = HuffDecompress(Data, Buffer, Length, Temp) +;* +;* long = HuffDecompress(unsigned char *, unsigned char *, long, char *); +;* +;* FUNCTION +;* Expand data that has been compressed with order 0 Huffman coding. +;* The model (counts) are extracted from the data and a decode tree is +;* built. The data is expanded by reading a bit and traversing the tree +;* until a leaf node is encountered. +;* +;* INPUTS +;* Data - Pointer to Huffman encoded data. +;* Buffer - Pointer to decompress buffer. +;* Length - Maximum decompress length. +;* Temp - Pointer to temporary working buffer. (Must be >= 5120 bytes!) +;* +;* RESULT +;* Size - Size of decompressed data. +;* +;**************************************************************************** + + GLOBAL C HuffDecompress:NEAR + PROC HuffDecompress C NEAR USES esi edi ebx ecx edx + + ARG data:NEAR PTR + ARG buffer:NEAR PTR + ARG length:DWORD + ARG temp:NEAR PTR + + LOCAL next:DWORD + +;*--------------------------------------------------------------------------- +;* Read in the set of counts +;*--------------------------------------------------------------------------- + + mov esi,[data] ;Compressed data + mov ebx,[temp] ;Nodes array + + mov ax,[esi] ;Get first and last count + xor edx,edx ;i = 0 + xor ecx,ecx + add esi,2 + +??getcounts: + cmp al,dl ;Reached start of run? + jne ??zerocount + +;* Copy the run of counts to the nodes + + sub ah,al ;Run length = Stop - Start + xor ecx,ecx + mov cl,ah + xor eax,eax + inc ecx ;Run length + 1 + +??copycounts: + mov al,[esi] ;Get count + inc edx ;i++ + mov [ebx],eax ;Write count to node + inc esi + add ebx,8 ;Next node + dec ecx + jnz ??copycounts + + mov ax,[esi] ;Get next start + inc esi + cmp al,0 ;Terminator? + je short ??nextcount + + inc esi + jmp short ??nextcount + +;* Fill empty nodes with 0 + +??zerocount: + mov [DWORD PTR ebx],ecx + inc edx ;i++ + add ebx,8 ;Next node + +??nextcount: + cmp edx,256 + jl short ??getcounts + + mov [WORD PTR ebx],1 + mov [data],esi + +;*--------------------------------------------------------------------------- +;* Build the Huffman tree. All active nodes are scanned in order +;* to locate the two nodes with the minimum weights. These two +;* weights are added together and assigned a new node. The new +;* node makes the two minimum nodes into its 0 child and 1 child. +;* The two minimum nodes are then marked as inactive. This process +;* repeats until their is only one node left, which is the root. +;*--------------------------------------------------------------------------- + + mov eax,[temp] ;Nodes array + mov esi,eax + add eax,(513 * 8) ;Node[513] = guaranteed maximum + mov [DWORD PTR eax],-1 + + mov [next],((HUFF_EOS + 1) * 8) + +??sortnext: + mov edx,(513 * 8) ;first = 513 + mov edi,edx ;last = 513 + xor ecx,ecx ;i = 0 + mov ebx,esi ;nodes[i] + +??sortnodes: + cmp [WORD PTR ebx],0 ;Only check non-zero nodes + jz ??nextnode + +;* nodes[i].count < nodes[first].count + + mov eax,[DWORD PTR esi + edx] + cmp eax,[DWORD PTR ebx] + jbe ??checklast + + mov edi,edx ;last = first + mov edx,ecx ;first = i + jmp short ??nextnode + +;* nodes[i].count < nodes[last].count + +??checklast: + mov eax,[DWORD PTR esi + edi] + cmp eax,[DWORD PTR ebx] + jbe ??nextnode + + mov edi,ecx ;last = i + +??nextnode: + add ecx,8 ;i++ + add ebx,8 ;nodes[i] + cmp ecx,[next] + jne short ??sortnodes + +;* Tree done when last = 513 + + cmp edi,(513 * 8) + je short ??decode + + mov ebx,[next] + add ebx,esi + mov [WORD PTR ebx+4],dx ;nodes[next].child0 = first + mov [WORD PTR ebx+6],di ;nodes[next].child1 = last + + add edx,esi + mov eax,[DWORD PTR edx] ;nodes[first].count + add edi,esi + mov [DWORD PTR ebx],eax + + mov ecx,[DWORD PTR edi] ;nodes[last].count + xor eax,eax + add [DWORD PTR ebx],ecx + + mov [DWORD PTR edx],eax ;nodes[first].count = 0 + mov [DWORD PTR edi],eax ;nodes[lats].count = 0 + add [next],8 + jmp ??sortnext + +;*--------------------------------------------------------------------------- +;* Expand the compressed data. As each new symbol is decoded, the +;* tree is traversed, starting at the root node, reading a bit in, +;* and taking either the child0 or child1 path. Eventually, the +;* tree winds down to a leaf node, and the corresponding symbol is +;* output. If the symbol is the HUFF_EOS symbol the process +;* terminates. +;*--------------------------------------------------------------------------- + +??decode: + sub [next],8 ;rootnode - 1 + xor ecx,ecx + mov esi,[data] ;Input data buffer + mov al,080h ;mask = 0x80 + mov edi,[buffer] ;Output buffer + mov ah,[esi] ;Data byte + mov ebx,[temp] + inc esi + +??decodeloop: + mov edx,[next] ;node = root + +??walktree: + mov ecx,4 + add ecx,edx + test al,ah + jz short ??getnode + + add ecx,2 + +??getnode: + mov dx,[WORD PTR ebx + ecx] ;nodes[node].child + shr al,1 + jnz short ??checkleaf + + mov ah,[esi] ;Get next data byte + mov al,080h ;Reset mask + inc esi + +??checkleaf: + cmp edx,(HUFF_EOS * 8) + jg short ??walktree + je short ??done + + shr edx,3 + mov [edi],dl + inc edi + jmp short ??decodeloop + +??done: + mov eax,edi + sub eax,[buffer] + ret + + ENDP HuffDecompress + + +;**************************************************************************** +;* +;* NAME +;* BuildHuffTree - Build the Huffman decode tree. +;* +;* SYNOPSIS +;* Root = BuildHuffTree(Nodes) +;* +;* long BuildHuffTree(TreeNode *); +;* +;* FUNCTION +;* Build the Huffman tree. All active nodes are scanned in order to +;* locate the two nodes with the minimum weights. These two weights are +;* added together and assigned a new node. The new node makes the two +;* minimum nodes into its 0 child and 1 child. The two minimum nodes are +;* then marked as inactive. This process repeats until their is only one +;* node left, which is the root. +;* +;* INPUTS +;* Nodes - Pointer to array of nodes. +;* +;* RESULT +;* Root - Number of root node. +;* +;**************************************************************************** + + GLOBAL C BuildHuffTree:NEAR + PROC BuildHuffTree C NEAR USES esi edi ebx ecx edx + + ARG temp:NEAR PTR + + LOCAL next:DWORD + + mov eax,[temp] ;Nodes array + mov esi,eax + add eax,(513 * 8) ;Node[513] = guaranteed maximum + mov [DWORD PTR eax],-1 + + mov [next],((HUFF_EOS + 1) * 8) + +??sortnext: + mov edx,(513 * 8) ;first = 513 + mov edi,edx ;last = 513 + xor ecx,ecx ;i = 0 + mov ebx,esi ;nodes[i] + +??sortnodes: + cmp [WORD PTR ebx],0 ;Only check non-zero nodes + jz ??nextnode + +;* nodes[i].count < nodes[first].count + + mov eax,[DWORD PTR esi + edx] + cmp eax,[DWORD PTR ebx] + jbe ??checklast + + mov edi,edx ;last = first + mov edx,ecx ;first = i + jmp short ??nextnode + +;* nodes[i].count < nodes[last].count + +??checklast: + mov eax,[DWORD PTR esi + edi] + cmp eax,[DWORD PTR ebx] + jbe ??nextnode + + mov edi,ecx ;last = i + +??nextnode: + add ecx,8 ;i++ + add ebx,8 + cmp ecx,[next] + jne short ??sortnodes + +;* Tree done when last = 513 + + cmp edi,(513 * 8) + je short ??done + + mov ebx,[next] + add ebx,esi ;nodes[next] + mov [WORD PTR ebx+4],dx ;nodes[next].child0 = first + mov [WORD PTR ebx+6],di ;nodes[next].child1 = last + + add edx,esi + mov eax,[DWORD PTR edx] ;nodes[first].count + add edi,esi + mov [DWORD PTR ebx],eax + + mov ecx,[DWORD PTR edi] ;nodes[last].count + xor eax,eax + add [DWORD PTR ebx],ecx + + mov [DWORD PTR edx],eax ;nodes[first].count = 0 + mov [DWORD PTR edi],eax ;nodes[lats].count = 0 + add [next],8 + jmp ??sortnext + +??done: + mov eax,[next] + sub eax,8 + ret + + ENDP BuildHuffTree + + END + diff --git a/WINVQ/VQM32/HUFFMAN.H b/WINVQ/VQM32/HUFFMAN.H new file mode 100644 index 0000000..044c3f4 --- /dev/null +++ b/WINVQ/VQM32/HUFFMAN.H @@ -0,0 +1,99 @@ +/* +** 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 . +*/ + +#ifndef HUFFMAN_H +#define HUFFMAN_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* huffman.h +* +* DESCRIPTION +* Huffman order 0 compress/decompress definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 19, 1995 +* +****************************************************************************/ + + +/* TreeNode: Huffman decoding tree node. + * + * count - Weight of the node in the tree. + * child0 - Child node 0 + * child1 - Child node 1 + */ +typedef struct _TreeNode { + unsigned long count; + unsigned short child0; + unsigned short child1; +} TreeNode; + + +/* HuffCode: This structure is used for storing the code for each symbol + * during encoding. A table of codes for each symbol is built + * from the Huffman tree. + * + * code - Code used to represent a symbol. + * bits - Length of code in bits. + */ +typedef struct _HuffCode { + unsigned short code; + short bits; +} HuffCode; + + +#define HUFF_EOS 256 /* End of stream symbol */ + +/* Prototypes */ +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl HuffCompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +long __cdecl HuffDecompress(unsigned char *data, unsigned char *buffer, + long length, char *work); + +void __cdecl HuffCount(unsigned char *data, TreeNode *nodes, long length, + long zero); + +void __cdecl HuffScaleCounts(TreeNode *nodes); +long __cdecl RLEHuffCounts(TreeNode *nodes, unsigned char *buffer); +long __cdecl BuildHuffTree(TreeNode *nodes); + +void __cdecl ConvertToCodes(TreeNode *nodes, HuffCode *codes, + unsigned short code, short bits, short node); + +long __cdecl HuffEncode(unsigned char *data, unsigned char *buffer, + HuffCode *codes, long length); + +#ifdef __cplusplus +} +#endif + +#endif /* HUFFMAN_H */ + diff --git a/WINVQ/VQM32/IFF.CPP b/WINVQ/VQM32/IFF.CPP new file mode 100644 index 0000000..e40635e --- /dev/null +++ b/WINVQ/VQM32/IFF.CPP @@ -0,0 +1,699 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.c +* +* DESCRIPTION +* IFF manager. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenIFF - Open an IFF for reading or writting. +* CloseIFF - Close an IFF. +* ReadForm - Read the IFF FORM, size and type of the file. +* WriteForm - Write IFF form ID, size and type fields. +* ReadChunkHeader - Read the IFF chunk identification header. +* WriteChunkHeader - Write an IFF chunk identification header. +* WriteChunk - Write an IFF chunk with data from a buffer. +* WriteChunkBytes - Write data from a buffer to the IFF stream. +* SkipChunkBytes - Skip bytes in a chunk. +* FindChunk - Scan for a specific chunk name. +* IDtoStr - Convert a longword identifier to a NULL-terminated +* string. +* CurrentFilePos - Get the current file position. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "iff.h" + + +/**************************************************************************** +* +* NAME +* OpenIFF - Open an IFF for reading or writting. +* +* SYNOPSIS +* IFFHandle = OpenIFF(Name, Mode) +* +* IFFHandle *OpenIFF(char *, long); +* +* FUNCTION +* Opens an IFF for a new read or write. The direction of the I/O is +* given by the value of Mode, which can be either IFF_READ or IFF_WRITE. +* +* INPUTS +* Name - Pointer to name of file to open. +* Mode - IFF_READ or IFF_WRITE. +* +* RESULT +* IFFHandle - Pointer to IFFHandle structure or NULL if error. +* +****************************************************************************/ + +IFFHandle *OpenIFF(char *name, long mode) +{ + IFFHandle *iff; + + /* Allocate IFFHandle structure. */ + if ((iff = (IFFHandle *)malloc(sizeof(IFFHandle))) != NULL) { + + /* Initialize handle.*/ + memset(iff, 0, sizeof(IFFHandle)); + iff->flags = mode; + + switch (mode) { + case IFFF_READ: + iff->fh = open(name, O_RDONLY|O_BINARY); + break; + + case IFFF_WRITE: + iff->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY), + (S_IREAD|S_IWRITE)); + + printf("\r"); + break; + + case (IFFF_READ|IFFF_WRITE): + iff->fh = open(name, (O_RDWR|O_BINARY), (S_IREAD|S_IWRITE)); + break; + + default: + iff->fh = -1; + break; + } + + /* If something went wrong we must free up any resources + * that we have opened. + */ + if (iff->fh == -1) { + free(iff); + iff = NULL; + } + } + + return (iff); +} + + +/**************************************************************************** +* +* NAME +* CloseIFF - Close an IFF. +* +* SYNOPSIS +* CloseIFF(IFFHandle) +* +* void CloseIFF(IFFHandle *); +* +* FUNCTION +* Completes an IFF read or write operation. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseIFF(IFFHandle *iff) +{ + long length; + + /* Write the length of the FORM */ + if ((iff->flags & IFFF_WRITE) && ((iff->form.size == 0) + || (iff->scan > iff->form.size))) { + + lseek(iff->fh, 4, SEEK_SET); + length = REVERSE_LONG(iff->scan); + write(iff->fh, &length, 4); + } + + close(iff->fh); + free(iff); +} + + +/**************************************************************************** +* +* NAME +* ReadForm - Read the IFF FORM, size and type of the file. +* +* SYNOPSIS +* Error = ReadForm(IFFHandle, FormHeader) +* +* long ReadForm(IFFHandle *, FormHeader *); +* +* FUNCTION +* Read in the IFF form, size, type information. If the FormHeader +* structure pointer is NULL then the FORM will be read into the +* IFFHandles form structure. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* FormHeader - Pointer to FormHeader structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long ReadForm(IFFHandle *iff, FormHeader *form) +{ + FormHeader *ptr; + long error; + + /* Read the FORM into the IFFHandle or the provided FormHeader. */ + if (form == NULL) { + ptr = &iff->form; + } else { + ptr = form; + } + + /* Read in IFF FORM from the file stream.. */ + if ((error = read(iff->fh, ptr, 12)) == 12) { + ptr->size = REVERSE_LONG(ptr->size); + iff->scan = 4; + error = 0; + } else { + if (error == -1) + error = IFFERR_READ; + else if (error == 0) + error = IFFERR_EOF; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteForm - Write IFF form ID, size and type fields. +* +* SYNOPSIS +* Error = WriteForm(IFFHandle) +* +* long WriteForm(IFFHandle, FormHeader *); +* +* FUNCTION +* Write out the IFF form, size, type information. If the size field +* is zero then the IFF form size will be calculated and written by +* the CloseIFF() function. If the FormHeader structure pointer is NULL +* the the form from the IFFHandle will be written. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* FormHeader - Pointer to FormHeader structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long WriteForm(IFFHandle *iff, FormHeader *form) +{ + FormHeader *ptr; + long error = 0; + + /* Use the FORM from the IFFHandle or the provided FormHeader. */ + if (form == NULL) { + ptr = &iff->form; + } else { + ptr = form; + } + + /* Write the IFF form to the file stream. */ + if (iff->flags & IFFF_WRITE) { + ptr->size = REVERSE_LONG(ptr->size); + + if (write(iff->fh, ptr, 12) == 12) { + iff->scan = 4; + } else { + error = IFFERR_WRITE; + } + } else { + error = IFFERR_WRITE; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* ReadChunkHeader - Read the IFF chunk identification header. +* +* SYNOPSIS +* Error = ReadChunkHeader(IFFHandle) +* +* long ReadChunkHeader(IFFHandle *); +* +* FUNCTION +* Read the IFF identification header from the files data stream. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long ReadChunkHeader(IFFHandle *iff) +{ + long error = 0; + + /* Skip any part of the previous chunk that hasn't been processed. */ + if ((iff->cn.size != 0) && (iff->cn.scan < PADSIZE(iff->cn.size))) { + error = lseek(iff->fh, (PADSIZE(iff->cn.size) - iff->cn.scan), SEEK_CUR); + + if (error == -1) { + error = IFFERR_READ; + } else { + error = 0; + } + } + + /* Read in the next chunk header context. */ + if (!error) { + if ((error = read(iff->fh, &iff->cn, 8)) == 8) { + error = 0; + iff->scan += 8; + iff->cn.size = REVERSE_LONG(iff->cn.size); + iff->cn.scan = 0; + } else { + if (error == -1) { + error = IFFERR_READ; + } else if (error == 0) { + error = IFFERR_EOF; + } + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteChunkHeader - Write an IFF chunk identification header. +* +* SYNOPSIS +* Error = WriteChunkHeader(IFFHandle, ID, Size) +* +* long WriteChunkHeader(IFFHandle *, long, long); +* +* FUNCTION +* Write an IFF identification header to the files data stream. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* Size - Size of chunk in bytes (WORD aligned). +* +* RESULT +* Error - 0 if successful or IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunkHeader(IFFHandle *iff, long id, long size) +{ + long error = 0; + + /* Make sure it is okay to write. */ + if (iff->flags & IFFF_WRITE) { + iff->cn.id = id; + iff->cn.size = REVERSE_LONG(size); + iff->cn.scan = 0; + + if (write(iff->fh, &iff->cn, 8) == 8) { + iff->scan += 8; + } else { + error = IFFERR_WRITE; + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* WriteChunk - Write an IFF chunk with data from a buffer. +* +* SYNOPSIS +* Actual = WriteChunk(IFFHandle, ID, Buffer, Size) +* +* long WriteChunk(IFFHandle *, long, char *, long); +* +* FUNCTION +* Write a IFF chunk at the current file position. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* Buffer - Pointer to buffer area with bytes to be written. +* Size - Number of bytes to write. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunk(IFFHandle *iff, long id, char *buffer, long size) +{ + Context cn; + long actual; + + /* Make sure we can write to this file. */ + if (iff->flags & IFFF_WRITE) { + cn.id = id; + cn.size = REVERSE_LONG(size); + + /* Write chunk header. */ + if (write(iff->fh, &cn, 8) == 8) { + iff->scan += 8; + iff->cn.scan += 8; + + /* Write chunk data. */ + actual = write(iff->fh, buffer, size); + + if (actual == size) { + iff->scan += actual; + iff->cn.scan += actual; + + /* Write chunk padding if necessary. */ + if (PADSIZE(size) > size) { + id = 0; + + if (write(iff->fh, &id, 1) == 1) { + iff->scan++; + iff->cn.scan++; + } else { + actual = IFFERR_WRITE; + } + } + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* WriteChunkBytes - Write data from a buffer to the IFF stream. +* +* SYNOPSIS +* Actual = WriteChunkBytes(IFFHandle, Buffer, Size) +* +* long WriteChunk(IFFHandle *, char *, long); +* +* FUNCTION +* Write a IFF chunk at the current file position. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Buffer - Pointer to buffer area with bytes to be written. +* Size - Number of bytes to write. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long WriteChunkBytes(IFFHandle *iff, char *buffer, long size) +{ + long actual; + + /* Make sure we can write to this file. */ + if (iff->flags & IFFF_WRITE) { + + /* Write data. */ + if ((actual = (unsigned short)write(iff->fh, buffer, size)) == size) { + iff->scan += actual; + iff->cn.scan += actual; + } else { + actual = IFFERR_WRITE; + } + } else { + actual = IFFERR_WRITE; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* ReadChunkBytes - Read data from a chunk into a buffer. +* +* SYNOPSIS +* Actual = ReadChunkBytes(IFFHandle, Buffer, Length) +* +* long ReadChunkBytes(IFFHandle *, char *, long); +* +* FUNCTION +* Read in 'Length' number of bytes from the current chunk context. +* If the specified length exceeds the number of bytes remaining in the +* chunk ReadChunkBytes() will read in only the number of remaining +* bytes. ReadChunkBytes() will never read beyond the scope of the +* current chunk. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Buffer - Pointer to buffer to read data into. +* Length - Number of bytes to read. +* +* RESULT +* Actual - (positive) Bytes written or (negative) IFFERR_??? error code. +* +****************************************************************************/ + +long ReadChunkBytes(IFFHandle *iff, char *buffer, long size) +{ + long actual; + + /* If the actual bytes remaining in the current chunk is less than + * the requested bytes to read then adjust the read request size + * to only read in the bytes that remain in the chunk. + */ + actual = (iff->cn.size - iff->cn.scan); + + if (size > actual) { + size = actual; + } + + /* Read in the requested number of bytes. */ + if ((actual = read(iff->fh, buffer, size)) != size) { + actual = IFFERR_READ; + } else { + iff->scan += actual; + iff->cn.scan += actual; + } + + return (actual); +} + + +/**************************************************************************** +* +* NAME +* SkipChunkBytes - Skip bytes in a chunk. +* +* SYNOPSIS +* Error = SkipChunkBytes(IFFHandle, Skip) +* +* long SkipChunkBytes(IFFHandle *, long); +* +* FUNCTION +* Skip the specified number of bytes of the chunk. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* Skip - Number of bytes to skip. +* +* RESULT +* Error - 0 if successful or FAIL_??? error code. +* +****************************************************************************/ + +long SkipChunkBytes(IFFHandle *iff, long skip) +{ + long error = 0; + + if (lseek(iff->fh, skip, SEEK_CUR) == -1) { + error = IFFERR_READ; + } else { + iff->scan += skip; + iff->cn.scan += skip; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* FindChunk - Scan for a specific chunk name. +* +* SYNOPSIS +* Error = FindChunk(IFFHandle, ID) +* +* long FindChunk(IFFHandle *, long); +* +* FUNCTION +* Scan from the current file position for the next occurance of the +* specified chunk ID. When a match is found the function will return +* with the matching chunk as the current context. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* ID - ID code of chunk. +* +* RESULT +* Error - 0 if successful or FAIL_??? error code. +* +****************************************************************************/ + +long FindChunk(IFFHandle *iff, long id) +{ + long found = 0; + long error = 0; + + /* Invalid handle check. */ + if (iff != NULL) { + + /* Scan until we have a match or an error. */ + while ((found == 0) && !(error = ReadChunkHeader(iff))) { + + /* If we found a match the terminate scan, otherwise skip this + * chunk and process the next. + */ + if (iff->cn.id == id) { + found = 1; + } else { + error = SkipChunkBytes(iff, PADSIZE(iff->cn.size)); + } + } + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* IDtoStr - Convert a longword identifier to a NULL-terminated string. +* +* SYNOPSIS +* String = IDtoStr(ID, Buffer) +* +* char *IDtoStr(long, char *); +* +* FUNCTION +* Writes the ASCII equivalent of the given longword ID into buffer as a +* NULL-terminated string. +* +* INPUTS +* ID - Longword ID. +* Buffer - Character buffer to accept string (at least 5 characters). +* +* RESULT +* String - The value of "Buffer". +* +****************************************************************************/ + +char *IDtoStr(long id, char *buf) +{ + memcpy(buf, &id, 4); + *(buf + 4) = 0; + + return (buf); +} + + +/**************************************************************************** +* +* NAME +* CurrentFilePos - Get the current file position. +* +* SYNOPSIS +* Position = CurrentFilePos(IFFHandle) +* +* long CurrentFilePos(IFFHandle *); +* +* FUNCTION +* This function returns the offset in bytes of the current file position +* from the beginning of the IFF. +* +* INPUTS +* IFFHandle - Pointer to IFFHandle structure. +* +* RESULT +* Position - Offset in bytes from the beginning of the file to the +* current position. +* +****************************************************************************/ + +long CurrentFilePos(IFFHandle *iff) +{ + long offset; + + if ((offset = lseek(iff->fh, 0, SEEK_CUR)) == -1) { + offset = IFFERR_READ; + } + + return (offset); +} + diff --git a/WINVQ/VQM32/IFF.H b/WINVQ/VQM32/IFF.H new file mode 100644 index 0000000..c81c7e1 --- /dev/null +++ b/WINVQ/VQM32/IFF.H @@ -0,0 +1,141 @@ +/* +** 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 . +*/ + +#ifndef VQMIFF_H +#define VQMIFF_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* iff.h +* +* DESCRIPTION +* IFF (Interchange File Format) manager definitions. +* (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* FormHeader - Structure associated with IFF forms. + * + * id - IFF form id (IE: "FORM") + * size - Length of IFF in bytes + * type - Form type (IE: "ILBM") + */ +typedef struct _FormHeader { + long id; + long size; + long type; +} FormHeader; + +/* Context - Structure associated with chunks. + * + * id - Chunk identifier. + * size - Size of chunk in bytes. + * scan - Bytes read/written. + */ +typedef struct _Context { + long id; + long size; + long scan; +} Context; + +/* IFFHandle - Structure associated with an active IFF read\write session. + * + * fh - DOS filehandle + * flags - Internal flags used by IFF routines. + * form - IFF form information. + * scan - Bytes read/written + * cn - Context of current chunk. + */ +typedef struct _IFFHandle { + long fh; + long flags; + FormHeader form; + long scan; + Context cn; +} IFFHandle; + +/* bit masks for "flags" field. */ +#define IFFB_READ 0 +#define IFFB_WRITE 1 +#define IFFF_READ (1<>24) \ + &0x000000FFL)|(((unsigned long)(id)>>8) \ + &0x0000FF00L)|(((unsigned long)(id)<<8) \ + &0x00FF0000L)|(((unsigned long)(id)<<24)&0xFF000000L)) + +#define REVERSE_WORD(id) ((unsigned short)((((unsigned short)(id)<<8) \ + &0x00FF00)|(((unsigned short)(id)>>8)&0x0FF))) + +#define PADSIZE(size) (((size)+1)&(~1)) + +#ifndef MAKE_ID +#define MAKE_ID(a,b,c,d) ((long)((long)(d)<<24)|((long)(c)<<16)| \ + ((long)(b)<<8)|(long)(a)) +#endif + +/* Universal IFF identifiers */ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_LIST MAKE_ID('L','I','S','T') +#define ID_PROP MAKE_ID('P','R','O','P') +#define ID_NULL MAKE_ID(' ',' ',' ',' ') + +/* Prototypes */ +IFFHandle *OpenIFF(char *, long); +void CloseIFF(IFFHandle *); +long ReadForm(IFFHandle *, FormHeader *); +long WriteForm(IFFHandle *, FormHeader *); +long ReadChunkHeader(IFFHandle *); +long WriteChunkHeader(IFFHandle *, long, long); +long WriteChunk(IFFHandle *, long, char *, long); +long WriteChunkBytes(IFFHandle *, char *, long); +long ReadChunkBytes(IFFHandle *, char *, long); +long SkipChunkBytes(IFFHandle *, long); +long FindChunk(IFFHandle *, long); +char *IDtoStr(long, char *); +long CurrentFilePos(IFFHandle *); + +#endif /* VQMIFF_H */ + diff --git a/WINVQ/VQM32/LCWCOMP.ASM b/WINVQ/VQM32/LCWCOMP.ASM new file mode 100644 index 0000000..f402077 --- /dev/null +++ b/WINVQ/VQM32/LCWCOMP.ASM @@ -0,0 +1,266 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* lcwcomp.asm +;* +;* DESCRIPTION +;* LCW compression code. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Louis Castle +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* LCW_Compress - LCW compress a buffer of memory. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* LCW_Compress - LCW compress a buffer of memory. +;* +;* SYNOPSIS +;* Size = LCW_Compress(Source, Dest, Length) +;* +;* long LCW_Compress(void *, void *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to compress. +;* Dest - Pointer to buffer to put compressed data. +;* Length - Length in bytes of data to compress. +;* +;* RESULT +;* Size - Size in bytes of compresed data. +;* +;**************************************************************************** + + GLOBAL C LCW_Compress:NEAR + PROC LCW_Compress C NEAR USES ebx ecx edx esi edi + + 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 + + mov esi,[source] + mov edi,[dest] + mov edx,[datasize] + + cld ;Forward direction + mov ebx,esi + add ebx,edx + mov [end_of_data],ebx ;Save end of source address + mov [inlen],1 ;Set the in-length flag + mov [a1stdest],edi ;Save original dest + mov [a1stsrc],esi ;Save original source + mov [lenoff],edi ;Save offset length + + mov al,081h ;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 address of first byte + 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 [inlen],0 ;Clear the in-length flag + mov esi,edi + mov edi,[ndest] + 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 ;Address of the last byte +1 + sub ecx,edi ;Total number of bytes left + 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 address for the next search + mov ebx,edi ;Save address 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 + + inc edi + +??notend: + mov esi,edx + 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 paragraph 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: + mov bl,cl ;Get the length (3-10) + sub bl,3 ;Sub 3 for a 3 bit number 0-7 + shl bl,4 + add ah,bl + xchg ah,al + 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 [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 [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 + add esi,[count] ;Add in the length of the run to the source + mov [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 eax,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_Compress + + END diff --git a/WINVQ/VQM32/LCWUNCMP.ASM b/WINVQ/VQM32/LCWUNCMP.ASM new file mode 100644 index 0000000..8af488b --- /dev/null +++ b/WINVQ/VQM32/LCWUNCMP.ASM @@ -0,0 +1,221 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* lcwuncmp.asm +;* +;* DESCRIPTION +;* LCW uncompress routine. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Chris Yates +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* LCW_Uncompress - Uncompress LCW encoded data. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* LCW_Uncompress - Uncompress LCW encoded data. +;* +;* SYNOPSIS +;* Size = LCW_Uncompress(Source, Dest, Length) +;* +;* LONG LCW_Uncompress(void *, void *, long); +;* +;* FUNCTION +;* Uncompress 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 copy copy w1 bytes from offset w2 +;* n=11111110,w1,b1 long run run byte b1 for w1 bytes +;* n=10000000 end end of data reached +;* +;* INPUTS +;* Source - Pointer to LCW encoded data. +;* Dest - Pointer to buffer to uncompress into. +;* Length - +;* +;* RESULT +;* Size - Size of uncompressed data in bytes. +;* +;**************************************************************************** + + GLOBAL C LCW_Uncompress:NEAR + PROC LCW_Uncompress C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG length:DWORD + + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + LOCAL lastcom:DWORD + LOCAL lastcom1:DWORD + + mov esi,[source] ;ESI - Source address + mov edi,[dest] ;EDI - Destination address + mov edx,[length] ;EDX - Maximum length + + mov [a1stdest],edi ;Save dest address + add edx,edi ;Last address (Dest + length) + mov [lastbyte],edx + + cld ;Forward direction + mov ebx,esi ;Save source address + +??loop: +; Exit if no bytes are remaining. + + mov eax,[lastbyte] + sub eax,edi + jz short ??out + + mov [maxlen],eax ;Save for string commands + mov esi,ebx ;Restore source address + lodsb + or al,al ;See if its a short run + js short ??notshort + + mov ah,al ;Put rel offset high nibble in ah + and ah,0Fh ; Only 4 bits count + sub ecx,ecx + mov ch,al ;Put count nibble in ch + shr ch,4 + mov cl,ch + xor ch,ch + add ecx,3 ;Get actual run length + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??rsok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??rsok: + lodsb ;Get rel offset low byte + mov ebx,esi ;Save the source address + mov esi,edi ;Get the current dest + sub esi,eax ;Get relative offset + rep movsb + jmp ??loop + +??notshort: + test al,40h ;Is it a length? + jne short ??notlength ;If not it could be med or long run + +; If end code then exit. + + cmp al,80h + je short ??out + + mov cl,al ;Put the byte in count register + and ecx,3Fh ;Mask off the extra bits + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??lenok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??lenok: + rep movsb + mov ebx,esi ;Save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp short ??exit + +??notlength: + mov cl,al ;Get the entire code + and ecx,3Fh ;Mask off all but the size -3 + add ecx,3 ;Add 3 for byte count + cmp al,0FEh + jne short ??notrunlength + + sub eax,eax + lodsw + mov ecx,eax + sub eax,eax + lodsb + mov ebx,esi ;Save the source offset + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??runlenok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??runlenok: + rep stosb + jmp ??loop + +??notrunlength: + cmp al,0FFh ;Is it a long run? + jne short ??notlong ;If not use the code as the size + + sub eax,eax + lodsw ;If so, get the size + mov ecx,eax ;Put int the count byte + +??notlong: + lodsw ;Get the rel index + mov ebx,esi ;Save the source offset + add eax,[a1stdest] ;Add in the first index + mov esi,eax ;Use this as a source + cmp ecx,[maxlen] ;Is it too big to fit? + jbe short ??runok ;If not, its ok + + mov ecx,[maxlen] ;If so, max it out so it dosen't overrun + +??runok: + rep movsb + jmp ??loop + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx ;Calculate bytes uncompressed. + ret + + ENDP LCW_Uncompress + + END + diff --git a/WINVQ/VQM32/MAKEFILE b/WINVQ/VQM32/MAKEFILE new file mode 100644 index 0000000..a773f7a --- /dev/null +++ b/WINVQ/VQM32/MAKEFILE @@ -0,0 +1,173 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc 32-bit library. (Watcom/4GW) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 10, 1995 +# +#**************************************************************************** + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJ = .. +PRJNAME = vqm32wp +PRJDIR = $(PRJ)\VQM32 +WATCOM = c:\watcom + +OBJECTS = & + iff.obj & + profile.obj & + audzap.obj & + audunzap.obj & + targa.obj & + vertag.obj & + sortpal.obj & + palette.obj & + mono.obj & + lcwuncmp.obj & + lcwcomp.obj & + testvb.obj & + vb.obj & + mcgabuf.obj & + video.obj & + xmode.obj & + xmodepg.obj & + vesabuf.obj & + vesablit.obj & + vesavid.obj & + soscodec.obj & + chrwidth.obj & + mixfile.obj & + crc.obj & + fillrect.obj & + captoken.obj & + huffcmp.obj & + huffdcmp.obj & + mem.obj & + drawchar.obj & + textprnt.obj & + font.obj & + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(PRJ)\LIB;$(WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5r -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQMisc32 header files! + @copy *.h $(PRJ)\INCLUDE\VQM32 >NUL + @copy *.i $(PRJ)\INCLUDE\VQM32 >NUL + + \ No newline at end of file diff --git a/WINVQ/VQM32/MAKEFILE.BOR b/WINVQ/VQM32/MAKEFILE.BOR new file mode 100644 index 0000000..59ae8fd --- /dev/null +++ b/WINVQ/VQM32/MAKEFILE.BOR @@ -0,0 +1,234 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc32 library. (Borland/TNT) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Febuary 7, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# BCDIR - Borland C installation path. +# TNTDIR - Pharlap/TNT installation path. +# +#**************************************************************************** + +.AUTODEPEND + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!if !$d(PRJ) || !$d(PRJVCS) || !$d(BCDIR) || !$d(TNTDIR) +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECT DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = VQM32BP +PRJDIR = $(PRJ)\VQM32 + +OBJECTS = \ + iff.obj \ + profile.obj \ + audzap.obj \ + audunzap.obj \ + targa.obj \ + vertag.obj \ + sortpal.obj \ + palette.obj \ + mono.obj \ + lcwuncmp.obj \ + lcwcomp.obj \ + testvb.obj \ + vb.obj \ + mcgabuf.obj \ + video.obj \ + xmode.obj \ + xmodepg.obj \ + vesabuf.obj \ + vesablit.obj \ + vesavid.obj \ + soscodec.obj \ + drawchar.obj \ + textprnt.obj \ + font.obj \ + chrwidth.obj \ + mixfile.obj \ + crc.obj \ + fillrect.obj \ + captoken.obj \ + huffcmp.obj \ + huffdcmp.obj \ + mem.obj \ + portio.obj \ + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!if $d(LIB) +LIBPATH = $(PRJ)\LIB;$(LIB) +!else +LIBPATH = $(PRJ)\LIB;$(TNTDIR)\LIB;$(BCDIR)\LIB +!endif + +!if $d(INCLUDE) +INCLUDEPATH = $(PRJ)\INCLUDE;$(INCLUDE) +!else +INCLUDEPATH = $(PRJ)\INCLUDE;$(TNTDIR)\INCLUDE;$(BCDIR)\INCLUDE +!endif + +.path.asm = $(PRJDIR) +.path.c = $(PRJDIR) +.path.cpp = $(PRJDIR) +.path.h = $(PRJDIR) +.path.exe = $(PRJDIR) +.path.obj = $(PRJDIR)\OBJ +.path.sym = $(PRJDIR)\OBJ +.path.lib = $(PRJ)\LIB + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = bcc32 +ASM_CMD = tasm32 +LINK_CMD = tlink32 +LIB_CMD = tlib + +CC_CFG = bcc32.cfg +ASM_CFG = tasm32.cfg +LINK_CFG = tlink32.cfg +LIB_CFG = tlib.cfg +CFG_FILES = $(CC_CFG) $(ASM_CFG) $(LINK_CFG) $(LIB_CFG) + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all: $(PRJNAME).lib + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj: + $(CC_CMD) -P- -n$(.path.obj) {$< } + +.cpp.obj: + $(CC_CMD) -P -n$(.path.obj) {$< } + +.asm.obj: + $(ASM_CMD) @$(ASM_CFG) $<,$(.path.obj)\$& + +#---------------------------------------------------------------------------- +# BUILD THE LIBRARY +#---------------------------------------------------------------------------- + +$(PRJNAME).lib: $(OBJECTS) + - @del $(.path.lib)\$(PRJNAME).lib >&NUL + $(LIB_CMD) $(.path.lib)\$(PRJNAME).lib @$(LIB_CFG) @&&| +-+$(**: = -+) +| + - @copy *.h $(PRJ)\INCLUDE\VQM32 >&NUL + - @copy *.i $(PRJ)\INCLUDE\VQM32 >&NUL + +#---------------------------------------------------------------------------- +# ALL OBJECTS DEPEND ON THE CONFIGURATION FILES +#---------------------------------------------------------------------------- + +$(OBJECTS): $(CFG_FILES) + +#---------------------------------------------------------------------------- +# COMPILER CONFIGURATION +#---------------------------------------------------------------------------- + +$(CC_CFG): makefile + copy &&| +-c +-3 +-d +-H=$(.path.sym)\headers.sym +-wpro +-weas +-wpre +-I$(INCLUDEPATH) +-DPHARLAP_TNT=1 +#-v +| $(CC_CFG) + +#---------------------------------------------------------------------------- +# ASSEMBLER CONFIGURATION +#---------------------------------------------------------------------------- + +$(ASM_CFG): makefile + copy &&| +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/i$(PRJ)\INCLUDE +/zi +/dPHARLAP_TNT=1 +| $(ASM_CFG) + +#---------------------------------------------------------------------------- +# LINKER CONFIGURATION +#---------------------------------------------------------------------------- + +$(LINK_CFG): makefile + copy &&| +/c +/m +/Gm +-L$(LIBPATH) +-v +| $(LINK_CFG) + +#---------------------------------------------------------------------------- +# LIBRARIAN CONFIGURATION +#---------------------------------------------------------------------------- + +$(LIB_CFG): makefile + copy &&| +/C /E +| $(LIB_CFG) + diff --git a/WINVQ/VQM32/MAKEFILE.WAT b/WINVQ/VQM32/MAKEFILE.WAT new file mode 100644 index 0000000..5879824 --- /dev/null +++ b/WINVQ/VQM32/MAKEFILE.WAT @@ -0,0 +1,183 @@ +# +# 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 . +# + +#**************************************************************************** +# +# 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 +# VQMisc 32-bit library. (Watcom/4GW) +# +# FILE +# makefile +# +# DESCRIPTION +# Makefile for generating VQMisc32 library. +# +# PROGRAMMER +# Denzil E. Long, Jr. +# +# DATE +# Feburary 10, 1995 +# +# ENVIROMENT +# PRJ - Projects path. +# PRJVCS - Version control archive path. +# WATCOM - Watcom C installation path. +# +#**************************************************************************** + +.OPTIMIZE +.ERASE + +#---------------------------------------------------------------------------- +# VERIFY ENVIROMENT +#---------------------------------------------------------------------------- + +!ifndef %PRJ || %PRJVCS || %WATCOM +!error Environment not configured. +!endif + +#---------------------------------------------------------------------------- +# PROJECTS DEPENDENT MACROS +#---------------------------------------------------------------------------- + +PRJNAME = vqm32wp +PRJDIR = $(%PRJ)\VQM32 + +OBJECTS = & + iff.obj & + profile.obj & + audzap.obj & + audunzap.obj & + targa.obj & + vertag.obj & + sortpal.obj & + palette.obj & + mono.obj & + lcwuncmp.obj & + lcwcomp.obj & + testvb.obj & + vb.obj & + mcgabuf.obj & + video.obj & + xmode.obj & + xmodepg.obj & + vesabuf.obj & + vesablit.obj & + vesavid.obj & + soscodec.obj & + drawchar.obj & + textprnt.obj & + font.obj & + chrwidth.obj & + mixfile.obj & + crc.obj & + fillrect.obj & + captoken.obj & + huffcmp.obj & + huffdcmp.obj & + mem.obj & + +#---------------------------------------------------------------------------- +# PATH MACROS +#---------------------------------------------------------------------------- + +!ifdef %LIB +LIBPATH = $(%PRJ)\LIB;$(%LIB) +!else +LIBPATH = $(%PRJ)\LIB;$(%WATCOM)\LIB386 +!endif + +!ifdef %INCLUDE +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%INCLUDE) +!else +INCLUDEPATH = $(%PRJ)\INCLUDE;$(%WATCOM)\H +!endif + +path_c = .\ +path_cpp = .\ +path_h = .\ +path_asm = .\ +path_i = .\ +path_obj = .\O +path_lib = $(%PRJ)\LIB +path_exe = .\ + +#---------------------------------------------------------------------------- +# IMPLICIT RULE EXTENSIONS AND PATHS +#---------------------------------------------------------------------------- + +.EXTENSIONS : +.EXTENSIONS : .exe .lib .obj .c .cpp .asm .h .i + +.c : $(path_c) +.cpp : $(path_cpp) +.h : $(path_h) +.asm : $(path_asm) +.i : $(path_i) +.obj : $(path_obj) +.lib : $(path_lib) +.exe : $(path_exe) + +#---------------------------------------------------------------------------- +# TOOLS, COMMANDS AND CONFIGURATIONS +#---------------------------------------------------------------------------- + +CC_CMD = wcc386 +CCP_CMD = wpp386 +ASM_CMD = tasm32 +LINK_CMD = wlink +LIB_CMD = wlib + +CC_OPTS = -i$(INCLUDEPATH) -j -zp1 -5s -mf -oaeilrt -s -zq +ASM_OPTS = /t /m /w+ /jJUMPS /ml /p /z /dPHARLAP_TNT=0 +LIB_OPTS = /b /c /q +LINK_CFG = $(PRJNAME).lnk + +#---------------------------------------------------------------------------- +# DEFAULT TARGET +#---------------------------------------------------------------------------- + +all : $(PRJNAME).lib .SYMBOLIC + +$(PRJNAME).lib : $(OBJECTS) .SYMBOLIC + +#---------------------------------------------------------------------------- +# IMPLICIT RULES +#---------------------------------------------------------------------------- + +.c.obj : + $(CC_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.cpp.obj : + $(CCP_CMD) $(CC_OPTS) -fo=$(PATH_OBJ)\$^. $< + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +.asm.obj: + $(ASM_CMD) $(ASM_OPTS) $<,$(path_obj)\$^. + $(LIB_CMD) $(LIB_OPTS) $(path_lib)\$(PRJNAME).lib -+$(path_obj)\$]& + +update: .SYMBOLIC + @echo Updating VQMisc32 header files! + @copy *.h $(%PRJ)\INCLUDE\VQM32 >NUL + @copy *.i $(%PRJ)\INCLUDE\VQM32 >NUL + diff --git a/WINVQ/VQM32/MCGABUF.ASM b/WINVQ/VQM32/MCGABUF.ASM new file mode 100644 index 0000000..566cf10 --- /dev/null +++ b/WINVQ/VQM32/MCGABUF.ASM @@ -0,0 +1,196 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* mcgabuf.asm +;* +;* DESCRIPTION +;* MCGA display routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* ClearVRAM - Clear all VRAM. +;* MCGA_BufferCopy - Copy 320x200 buffer to MCGA VRAM +;* MCGA_Blit - Bit blit a block to the MCGA screen. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* ClearVRAM - Clear all VRAM. +;* +;* SYNOPSIS +;* ClearVRAM() +;* +;* void ClearVRAM(void); +;* +;* FUNCTION +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ClearVRAM:NEAR + PROC ClearVRAM C NEAR USES eax ecx edi es + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to video memory + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + SET_PLANE 0Fh ;Enable all planes for writing + cld + mov ecx,16000 ;Clear 320x200 + xor eax,eax + rep stosd + ret + + ENDP ClearVRAM + + +;**************************************************************************** +;* +;* NAME +;* MCGA_BufferCopy - Copy 320x200 buffer to MCGA VRAM +;* +;* SYNOPSIS +;* MCGA_BufferCopy(Buffer, Dummy) +;* +;* void MCGA_BufferCopy(char *, char *); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer. +;* Dummy - Prototype placeholder. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C MCGA_BufferCopy:NEAR + PROC MCGA_BufferCopy C NEAR USES ecx esi edi es + + ARG buffer:NEAR PTR + ARG dummy:NEAR PTR + + IF PHARLAP_TNT + mov eax,01Ch + mov es,ax + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + + mov esi, [buffer] + mov ecx,16000 + rep movsd ;Transfer the data + ret + + ENDP MCGA_BufferCopy + + +;**************************************************************************** +;* +;* NAME +;* MCGA_Blit - Bit blit a block to the MCGA screen. +;* +;* SYNOPSIS +;* MCGA_Blit(Buffer, Screen, Width, Height) +;* +;* void MCGA_Blit(char *, char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to copy. +;* Screen - Screen address to copy buffer to. +;* Width - Width of block. +;* Height - Height of block. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C MCGA_Blit:NEAR + PROC MCGA_Blit C NEAR USES ecx edx esi edi + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + ARG imgwidth:DWORD + ARG imgheight:DWORD + + IF PHARLAP_TNT + push es + mov eax,1Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov edi,[screen] + mov edx,320 + sub edx,[imgwidth] ;Compute modulo + +??Do_row: + mov ecx,[imgwidth] + rep movsb + add edi,edx + dec [imgheight] + jnz ??Do_row + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP MCGA_Blit + + END + diff --git a/WINVQ/VQM32/MEM.CPP b/WINVQ/VQM32/MEM.CPP new file mode 100644 index 0000000..a14f325 --- /dev/null +++ b/WINVQ/VQM32/MEM.CPP @@ -0,0 +1,134 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.c +* +* DESCRIPTION +* Memory management. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +*--------------------------------------------------------------------------- +* +* PUBLIC +* DPMI_Lock - Lock a memory page. +* DPMI_Unlock - Unlock a locked memory page. +* +****************************************************************************/ + +#ifdef __WATCOMC__ +#include +#include +#include + + +/**************************************************************************** +* +* NAME +* DPMI_Lock - Lock a memory page. +* +* SYNOPSIS +* DPMI_Lock(Address, Size) +* +* void DPMI_Lock(void *, long); +* +* FUNCTION +* +* INPUTS +* Address - Starting linear address of memory to lock. +* Size - Size of region to lock in bytes. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DPMI_Lock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + memset(®s, 0, sizeof(REGS)); + segread(&sregs); + + /* Lock the memory page. + * + * AX = 0x600 + * BX:CX = Starting linear address of memory to lock. + * SI:DI = Size of region to lock in bytes. + */ + regs.x.eax = DPMI_LOCK; + regs.x.ebx = ((long)ptr & 0xFFFF0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000FFFF); + regs.x.esi = ((long)size & 0xFFFF0000) >> 16; + regs.x.edi = ((long)size & 0x0000FFFF); + int386x(DPMI_INT, ®s, ®s, &sregs); +} + + +/**************************************************************************** +* +* NAME +* DPMI_Unlock - Unlock a locked memory page. +* +* SYNOPSIS +* DPMI_Unlock(Address, Size) +* +* void DPMI_Unlock(void *, long); +* +* FUNCTION +* +* INPUTS +* Address - Starting linear address of memory to unlock. +* Size - Size of region to unlock in bytes. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* Unlock memory page. */ + memset(®s, 0 ,sizeof(REGS)); + segread(&sregs); + + regs.x.eax = DPMI_UNLOCK; + regs.x.ebx = ((long)ptr & 0xFFFF0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000FFFF); + regs.x.esi = ((long)size & 0xFFFF0000) >> 16; + regs.x.edi = ((long)size & 0x0000FFFF); + int386x(DPMI_INT, ®s, ®s, &sregs); +} + +#endif /* __WATCOMC__ */ diff --git a/WINVQ/VQM32/MEM.H b/WINVQ/VQM32/MEM.H new file mode 100644 index 0000000..69b0c45 --- /dev/null +++ b/WINVQ/VQM32/MEM.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQAMEM_H +#define VQAMEM_H +/**************************************************************************** +* +* 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 +* +*--------------------------------------------------------------------------- +* +* FILE +* mem.h +* +* DESCRIPTION +* Memory management definitions. +* +* PROGRAMMER +* Phil Gorrow +* Denzil E. Long, Jr. +* +* DATE +* July 5, 1995 +* +****************************************************************************/ + +/* Definitions */ +#define DPMI_INT 0x0031 +#define DPMI_LOCK 0x0600 +#define DPMI_UNLOCK 0x0601 + +/* Prototypes */ +#ifdef __WATCOMC__ +void DPMI_Lock(void const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +#else +#define DPMI_Lock(a,b) +#define DPMI_Unlock(a,b) +#endif + +#endif /* VQAMEM_H */ diff --git a/WINVQ/VQM32/MIXFILE.CPP b/WINVQ/VQM32/MIXFILE.CPP new file mode 100644 index 0000000..a0d4d86 --- /dev/null +++ b/WINVQ/VQM32/MIXFILE.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.c +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These routines are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* MixFile format: +* +* HEADER +* (2 bytes) Count - The number of entries in this file. +* (4 bytes) Size - Size of the mix file in bytes. +* +* SUBBLOCKS (There are "Count" number of these.) +* (4 bytes) CRC - Entry descriptor (CRC of filename). +* (4 bytes) Offset - Offset in bytes from beginning of the DATA chunk. +* (4 bytes) Size - Size of entry. +* +* DATA +* Entry data. +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenMix - Open mix file for access. +* CloseMix - Close a mix file. +* OpenMixEntry - Open a mix file entry. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "mixfile.h" +#include "crc.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +int compfunc(void const *ptr1, void const *ptr2); + + +/**************************************************************************** +* +* NAME +* OpenMix - Open mix file for access. +* +* SYNOPSIS +* MixHandle = OpenMix(Name) +* +* MixHandle *OpenMix(char *); +* +* FUNCTION +* Open a mix file for access. +* +* INPUTS +* Name - Pointer to name of mix file to open. +* +* RESULT +* MixHandle - Pointer to handle for mix file. +* +****************************************************************************/ + +MIXHandle *OpenMix(char *name) +{ + MIXHeader mfhdr; + MIXHandle *mix = NULL; + long fh; + long sbsize; + long size; + + /* Open mix file and read in header. */ + if ((fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + if (read(fh, &mfhdr, sizeof(MIXHeader)) == sizeof(MIXHeader)) { + + /* Allocate handle based on the number of SubBlocks. */ + sbsize = (mfhdr.Count * sizeof(MIXSubBlock)); + size = sbsize + sizeof(MIXHandle); + + if ((mix = (MIXHandle *)malloc(size)) != NULL) { + memset(mix, 0, size); + mix->Name = name; + mix->Size = mfhdr.Size; + mix->Count = mfhdr.Count; + + /* Read in the SubBlock entries. */ + if (read(fh, &mix->Entries[0], sbsize) != sbsize) { + free(mix); + mix = NULL; + } + } + } + + close(fh); + } + + return (mix); +} + + +/**************************************************************************** +* +* NAME +* CloseMix - Close a mix file. +* +* SYNOPSIS +* CloseMix(MixHandle) +* +* void CloseMix(MixHandle *); +* +* FUNCTION +* Close a mix file by freeing its handle. +* +* INPUTS +* MixHandle - Pointer to MixHandle returned by OpenMix(). +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseMix(MIXHandle *mix) +{ + free(mix); +} + + +/**************************************************************************** +* +* NAME +* OpenMixEntry - Open a mix file entry. +* +* SYNOPSIS +* FH = OpenMixEntry(MixHandle, EntryName) +* +* short OpenMixEntry(MIXHandle *, char *); +* +* FUNCTION +* Opens an entry from the specified mix file handle. Use close() to close +* the file when done. +* +* INPUTS +* MixHandle - Pointer to MIXHandle containing entry to open. +* EntryName - Pointer to name of mix file entry to open. +* +* RESULT +* FH - DOS filehandle, -1 if unable to open. +* +****************************************************************************/ + +long OpenMixEntry(MIXHandle *mix, char *name) +{ + MIXSubBlock key; + MIXSubBlock *block; + long fh; + + /* Search for the specified file in the mix file. */ + key.CRC = Calculate_CRC(name, strlen(name)); + block = (MIXSubBlock *)bsearch(&key, &mix->Entries[0], mix->Count, + sizeof(MIXSubBlock), compfunc); + + /* If the block exists for the requested filename. */ + if (block != NULL) { + + /* Initialize the key for file access. */ + key.Offset = block->Offset; + key.Offset += (mix->Count * sizeof(MIXSubBlock)) + sizeof(MIXHeader); + + /* Open the mix file. */ + if ((fh = open(mix->Name, (O_RDONLY|O_BINARY))) != -1) { + + /* Seek to the start of the file. */ + if (lseek(fh, key.Offset, SEEK_SET) == -1) { + close(fh); + fh = -1; + } + } + } + + return (fh); +} + + +/**************************************************************************** +* +* NAME +* compfunc - Compare function for bsearch(). +* +* SYNOPSIS +* Result = compfunc(Entry1, Entry2); +* +* int compfunc(void const *, void const *); +* +* FUNCTION +* +* INPUTS +* Entry1 - Pointer to first entry to compare. +* Entry2 - Pointer to second entry to compare. +* +* RESULT +* Result - +* +****************************************************************************/ + +int compfunc(void const *ptr1, void const *ptr2) +{ + if (((MIXSubBlock *)ptr1)->CRC < ((MIXSubBlock *)ptr2)->CRC) return -1; + if (((MIXSubBlock *)ptr1)->CRC > ((MIXSubBlock *)ptr2)->CRC) return 1; + return(0); +} + + diff --git a/WINVQ/VQM32/MIXFILE.H b/WINVQ/VQM32/MIXFILE.H new file mode 100644 index 0000000..5d6b012 --- /dev/null +++ b/WINVQ/VQM32/MIXFILE.H @@ -0,0 +1,105 @@ +/* +** 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 . +*/ + +#ifndef VQMMIXFILE_H +#define VQMMIXFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mixfile.h +* +* DESCRIPTION +* A mix file is basically a group of files concatinated together +* proceeded by a header describing where in the file each individual +* entry is located. These definitions are provided to simplify the access +* to these file entries. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Disable structure alignment.*/ +#ifdef __WATCOMC__ +#pragma pack(1); +#endif + +/*--------------------------------------------------------------------------- + * STRUCTURE DEFINITIONS + *-------------------------------------------------------------------------*/ + +/* MIXHeader: Mix file data header. + * + * Count - Number of entries contained in this mix file. + * Size - Size of Mix file. + */ +typedef struct _MIXHeader { + short Count; + long Size; +} MIXHeader; + +/* MIXSubBlock: Mix file entry descriptor. + * + * CRC - Unique entry identifier. + * Offset - Offset from beginning of data segment to entry. + * Size - Size of entry. + */ +typedef struct _MIXSubBlock { + long CRC; + long Offset; + long Size; +} MIXSubBlock; + +/* MIXHandle: Mix file handle. + * + * Name - Pointer to the name of the mix file this handle is for. + * Size - Size of entire mix file. + * FH - DOS file handle of opened entry. + * Count - Number of files contained in this mix. + * Entries - Array of 'Count' MIXSubBlock structure entries. + */ +typedef struct _MIXHandle { + char *Name; + long Size; + long FH; + long Count; + MIXSubBlock Entries[]; +} MIXHandle; + +/*--------------------------------------------------------------------------- + * PROTOTYPES + *-------------------------------------------------------------------------*/ + +MIXHandle *OpenMix(char *name); +void CloseMix(MIXHandle *mix); +long OpenMixEntry(MIXHandle *mix, char *name); + +/* Restore original alignment */ +#ifdef __WATCOMC__ +#pragma pack(); +#endif + +#endif /* VQMMIXFILE_H */ + diff --git a/WINVQ/VQM32/MONO.ASM b/WINVQ/VQM32/MONO.ASM new file mode 100644 index 0000000..2fea785 --- /dev/null +++ b/WINVQ/VQM32/MONO.ASM @@ -0,0 +1,871 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* mono.asm +;* +;* DESCRIPTION +;* Mono screen print and output routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 8, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Mono_Enable - Enable mono output. +;* Mono_Disable - Disable mono output. +;* Mono_X - Get mono cursors X position. +;* Mono_Y - Get mono cursors Y position. +;* Mono_Set_Cursor - Set the mono cursor to specified coordinates. +;* Mono_Clear_Screen - Clear the mono screen. +;* Mono_Scroll - Scroll the mono screen up. +;* Mono_Put_Char - Ouput a character to the mono screen. +;* Mono_Draw_Rect - Draw a box on the mono screen. +;* Mono_Text_Print - Print a string to the mono screen at a specified +;* position. +;* Mono_Print - Print a string to the mono screen. +;* Mono_View_Page - View a mono page. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + + DATASEG + +MonoEnabled DD 1 +MonoX DD 0 +MonoY DD 0 +MonoOff DD 0 + +CharData DB 0DAh,0C4h,0BFh,0B3h,0D9h,0C4h,0C0h,0B3h ; Single line + DB 0D5h,0CDh,0B8h,0B3h,0BEh,0CDh,0D4h,0B3h ; Double horz. + DB 0D6h,0C4h,0B7h,0BAh,0BDh,0C4h,0D3h,0BAh ; Double vert. + DB 0C9h,0CDh,0BBh,0BAh,0BCh,0CDh,0C8h,0BAh ; Double line. + + +; x,y,dist +BoxData DB 1,0,0 ; Upper left corner. + DB 1,0,1 ; Top edge. + DB 0,1,0 ; Upper right corner. + DB 0,1,2 ; Right edge. + DB -1,0,0 ; Bottom right corner. + DB -1,0,1 ; Bottom edge. + DB 0,-1,0 ; Bottom left corner. + DB 0,-1,2 ; Left edge. + DB 0,0,-1 ; End of list. + +PageMap DD 0,1,2,3,4,5,6,7 + + CODESEG + +;**************************************************************************** +;* +;* NAME +;* Mono_Enable - Enable mono output. +;* +;* SYNOPSIS +;* Mono_Enable() +;* +;* void Mono_Enable(void); +;* +;* FUNCTION +;* Turn on the MonoEnabled flag. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Enable:NEAR + PROC Mono_Enable C NEAR + + mov [MonoEnabled],1 + ret + + ENDP Mono_Enable + + +;**************************************************************************** +;* +;* NAME +;* Mono_Disable - Disable mono output. +;* +;* SYNOPSIS +;* Mono_Disable() +;* +;* void Mono_Disable(void); +;* +;* FUNCTION +;* Turn off the MonoEnabled flag. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Disable:NEAR + PROC Mono_Disable C NEAR + + mov [MonoEnabled],0 + ret + + ENDP Mono_Disable + + +;**************************************************************************** +;* +;* NAME +;* Mono_X - Get mono cursors X position. +;* +;* SYNOPSIS +;* X = Mono_X() +;* +;* long Mono_X(void); +;* +;* FUNCTION +;* Return the X position of the mono screen cursor. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* X - X coordinate position. +;* +;**************************************************************************** + + GLOBAL C Mono_X:NEAR + PROC Mono_X C NEAR + + mov eax,[MonoX] + ret + + ENDP Mono_X + + +;**************************************************************************** +;* +;* NAME +;* Mono_Y - Get mono cursors Y position. +;* +;* SYNOPSIS +;* Y = Mono_Y() +;* +;* long Mono_Y(void); +;* +;* FUNCTION +;* Return the Y position of the mono screen cursor. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* Y - Y coordinate position. +;* +;**************************************************************************** + + GLOBAL C Mono_Y:NEAR + PROC Mono_Y C NEAR + + mov eax,[MonoY] + ret + + ENDP Mono_Y + + +;**************************************************************************** +;* +;* NAME +;* Mono_Set_Cursor - Set the mono cursor to specified coordinates. +;* +;* SYNOPSIS +;* Mono_Set_Cursor(X, Y) +;* +;* void Mono_Set_Cursor(long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* X - X coordinate position. +;* Y - Y coordinate position. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Set_Cursor:NEAR + PROC Mono_Set_Cursor C NEAR USES eax ebx edx + + ARG xpos:DWORD + ARG ypos:DWORD + + cmp [MonoEnabled],0 + je short ??fini + + mov eax,[ypos] + mov ah,80 + imul ah + add eax,[xpos] + mov ebx,eax + +; Update cursor position. + + mov dx,03B4h + mov al,0Eh ;High byte register set. + out dx,al + inc dx + mov al,bh + out dx,al ;Set high byte. + + dec dx + mov al,0Fh ;Low byte register set. + out dx,al + inc dx + mov al,bl + out dx,al ;Set low byte. + +; Update the globals. + + add ebx,ebx + mov [MonoOff],ebx + mov eax,[xpos] + mov [MonoX],eax + mov eax,[ypos] + mov [MonoY],eax + +??fini: + ret + + ENDP Mono_Set_Cursor + + +;**************************************************************************** +;* +;* NAME +;* Mono_Clear_Screen - Clear the mono screen. +;* +;* SYNOPSIS +;* Mono_Clear_Screen() +;* +;* void Mono_Clear_Screen(void); +;* +;* FUNCTION +;* Clear the mono screen and set the mono cursor to the upperleft corner +;* of the screen. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Clear_Screen:NEAR + PROC Mono_Clear_Screen C NEAR USES es eax ecx edi + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Clear the mono screen + + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + mov edi,0B0000h ;EDI = Mono screen address + xor eax,eax ;Set char & attributes to 0 + mov ecx,8000h/4 ;Number of longwords to clear + rep stosd ;Clear the mono screen. + call Mono_Set_Cursor C,eax,eax + +??fini: + ret + + ENDP Mono_Clear_Screen + + +;**************************************************************************** +;* +;* NAME +;* Mono_Scroll - Scroll the mono screen up. +;* +;* SYNOPSIS +;* Mono_Scroll(Lines) +;* +;* void Mono_Scroll(long); +;* +;* FUNCTION +;* Move the contents of the mono screen up the specified number of lines +;* while clearing out the bottom lines. +;* +;* INPUTS +;* Lines - Number of lines to scroll the screen up. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Scroll:NEAR + PROC Mono_Scroll C NEAR USES es eax ebx ecx esi edi + ARG lines:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Exit if lines to scroll is 0. + + mov eax,[lines] + or eax,eax + je short ??fini + +; Move the screen data up the specified lines + + mov ebx,eax + +??looper: + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + push ds ;Save DS selector + mov ds,ax ;Set DS selector to first MB + + mov ecx,((80*24)/2) ;Number of DWORDs to move + mov esi,0B00A0h + mov edi,0B0000h + rep movsd + + pop ds ;Restore DS selector + dec [MonoY] + sub [MonoOff],(80*2) + + xor eax,eax + mov ecx,(80/2) + rep stosd + + dec ebx + jne ??looper + +??fini: + ret + + ENDP Mono_Scroll + + +;**************************************************************************** +;* +;* NAME +;* Mono_Put_Char - Ouput a character to the mono screen. +;* +;* SYNOPSIS +;* Mono_Put_Char(Character, Attributes) +;* +;* void Mono_Put_Char(long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Character - ASCII character to output. +;* Attributes - Display attributes +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Put_Char:NEAR + PROC Mono_Put_Char C NEAR USES es eax edi + ARG character:DWORD + ARG attrib:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; Output character to the mono screen + + cld + + IF PHARLAP_TNT + mov ax,034h + mov es,ax ;Set ES selector to first MB + ENDIF + + mov edi,0B0000h ;EDI = mono screen + add edi,[MonoOff] ;Add cursor offset + mov eax,[character] + mov ah,[BYTE PTR attrib] + stosw + +; Update cursor position. + + inc [MonoX] ; X position moves. + call Mono_Set_Cursor C,[MonoX],[MonoY] + +??fini: + ret + + ENDP Mono_Put_Char + + +;**************************************************************************** +;* +;* NAME +;* Mono_Draw_Rect - Draw a box on the mono screen. +;* +;* SYNOPSIS +;* Mono_Draw_Rect(X, Y, Width, Height, Attributes, Thickness) +;* +;* void Mono_Draw_Rect(); +;* +;* FUNCTION +;* Draw a rectangle text box on the mono screen. +;* +;* INPUTS +;* X - X coordinate position of upperleft corner. +;* Y - Y coordinate position of upperleft corner. +;* Width - Desired width. +;* Height - Desired height. +;* Attributes - Display attributes. +;* Thickness - Line thickness. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Draw_Rect:NEAR + PROC Mono_Draw_Rect C NEAR + ARG xpos:DWORD + ARG ypos:DWORD + ARG width:DWORD + ARG height:DWORD + ARG attrib:DWORD + ARG thick:DWORD + +; Exit if mono disabled + + pushad + cmp [MonoEnabled],0 + je ??fini + +; Select the character table for the desired line thickness + + mov edi,OFFSET CharData + mov cl,3 + mov eax,[thick] + and eax,011b + shl eax,cl + add edi,eax + +; Prep width and height. + + cmp [width],2 + jb ??fini + + cmp [height],2 + jb ??fini + + sub [width],2 + sub [height],2 + +; Set cursor position to upperleft corner of box + + push [MonoY] + push [MonoX] ;Save current cursor position + call Mono_Set_Cursor C,[xpos],[ypos] + +; Draw the rectangle + + mov esi,OFFSET BoxData + +; Determine the number of characters to output + +??drawloop: + mov ecx,[width] + cmp [BYTE PTR esi+2],1 + je short ??gotlen + + mov ecx,[height] + cmp [BYTE PTR esi+2],2 + je short ??gotlen + + mov ecx,1 + +??gotlen: + jecxz ??donerun + +??runloop: + xor eax,eax + mov al,[BYTE PTR edi] + call Mono_Put_Char C,eax,[attrib] ;Output the character. + + mov al,[BYTE PTR esi+1] + cbw + cwde + add eax,[MonoY] + push eax + mov al,[BYTE PTR esi] + cbw + cwde + add eax,[MonoX] + dec eax ; Undo cursor advance. + push eax + call Mono_Set_Cursor ; Properly advance cursor. + add sp,8 + loop ??runloop + +; Advance to next control entry. + +??donerun: + add esi,3 + inc edi + cmp [BYTE PTR esi+2],-1 + jne ??drawloop + +; Restore cursor to original position. + + call Mono_Set_Cursor + add sp,8 + +??fini: + popad + ret + + ENDP Mono_Draw_Rect + + +;**************************************************************************** +;* +;* NAME +;* Mono_Text_Print - Print a string to the mono screen at a specified +;* position. +;* +;* SYNOPSIS +;* Mono_Text_Print(String, X, Y, Attributes, Update) +;* +;* void Mono_Text_Print(char *, long, long, long, long); +;* +;* FUNCTION +;* Print a NULL terminated string to the mono screen at the specified +;* cooridinates and attributes. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* X - X coordinate position. +;* Y - Y coordinate position. +;* Attributes - Display attributes +;* Update - Update cursor position flag. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C _Mono_Text_Print:NEAR + PROC _Mono_Text_Print C NEAR USES eax esi + ARG text:NEAR PTR + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + ARG update:DWORD + +; Save the current cursor position. + + push [MonoY] + push [MonoX] + cmp [text],0 ;Exit if the string is NULL + je short ??fini + + call Mono_Set_Cursor C,[xpos],[ypos] + +; Print string to the mono screen + + mov esi,[text] ;Text pointer + +??charloop: + mov eax,[esi] + inc esi + or al,al ;Stop on a NULL + je short ??fini + + cmp al,13 ;Special processing for '\r' + je short ??cr + +; Output character to mono screen + +??normal: + xor ah,ah + call Mono_Put_Char C,eax,[attrib] + +; Perform adjustments if wrapping past right margin. + + cmp [MonoX],80 + jb short ??nowrap + + inc [ypos] + call Mono_Set_Cursor C,0,[ypos] + jmp short ??nowrap + + ; Move to start of next line. + +??cr: + inc [ypos] + call Mono_Set_Cursor C,[xpos],[ypos] + +; Scroll the monochrome screen if necessary. + +??nowrap: + cmp [MonoY],25 + jb short ??noscroll + + call Mono_Scroll C,1 + dec [ypos] + +??noscroll: + jmp short ??charloop + +??fini: + cmp [update],0 + jne short ??noupdate + + call Mono_Set_Cursor + +??noupdate: + pop eax + pop eax + ret + + ENDP _Mono_Text_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_Text_Print - Print a string to the mono screen. (ASM call) +;* +;* SYNOPSIS +;* Mono_Text_Print(String, X, Y, Attributes) +;* +;* void Mono_Text_Print(char *, long, long, long); +;* +;* FUNCTION +;* Print a NULL terminated string to the mono screen at the specified +;* cooridinates and attributes. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* X - X coordinate position. +;* Y - Y coordinate position. +;* Attributes - Display attributes +;* +;* RESULT +;* NONE +;* +;* SEE ALSO +;* _Mono_Text_Print +;* +;**************************************************************************** + + GLOBAL C Mono_Text_Print:NEAR + PROC Mono_Text_Print C NEAR USES + ARG text:NEAR PTR + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + + call _Mono_Text_Print C,[text],[xpos],[ypos],[attrib],0 + +??fini: + ret + + ENDP Mono_Text_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_Print - Print a string to the mono screen. +;* +;* SYNOPSIS +;* Mono_Print(String) +;* +;* void Mono_Print(char *); +;* +;* FUNCTION +;* Print a string to the mono screen at the current cursor position and +;* update the cursor position. +;* +;* INPUTS +;* String - Pointer to NULL terminated string. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Mono_Print:NEAR + PROC Mono_Print C NEAR + ARG text:NEAR PTR + +; Exit if mono disabled + + cmp [MonoEnabled],0 + je short ??fini + + call _Mono_Text_Print C,[text],[MonoX],[MonoY],2,1 + +??fini: + ret + + ENDP Mono_Print + + +;**************************************************************************** +;* +;* NAME +;* Mono_View_Page - View a mono page. +;* +;* SYNOPSIS +;* Oldpage = Mono_View_Page(Page) +;* +;* long Mono_View_Page(long); +;* +;* FUNCTION +;* Displays the specified page in displayable mono memory. +;* +;* INPUTS +;* Page - Page to view. +;* +;* RESULT +;* Oldpage - Previous page. +;* +;**************************************************************************** + + GLOBAL C Mono_View_Page:NEAR + PROC Mono_View_Page C NEAR USES ds es eax ebx ecx edi esi + ARG page:DWORD + LOCAL oldpage:DWORD + +; Prepare the original page number for return to caller. + + cld + mov ebx,[PageMap] + mov [oldpage],ebx + +; Exit of mono disabled + + cmp [MonoEnabled],0 + je short ??fini + +; If the desired page is already displayed, then don't do anything. + + mov eax,[page] + cmp eax,ebx + je short ??fini + +; Verify that page specified is legal. + + cmp eax,7 + ja short ??fini + +; Find where the logical page to display is actually located. + + mov ecx,8 + push ds + pop es + lea edi,[PageMap] + repne scasw + neg ecx + add ecx,7 ; CX = where desired page is located. + +; Swap the page ID bytes in the PageMap array. + + sub edi,4 + mov ebx,[PageMap] + mov eax,[edi] + mov [edi],ebx + mov [PageMap],eax + + shl ecx,8 + add ecx,eax + mov esi,ecx + + IF PHARLAP_TNT + mov ax,034h + mov ds,ax + ENDIF + + mov edi,0B0000h + +; Exchange the two pages. + + mov ecx,1000H/4 + +??looper: + mov edx,[edi] + mov ebx,[esi] + mov [edi],ebx + mov [esi],edx + add esi,4 + add edi,4 + loop ??looper + +; Return with the original page number. + +??fini: + mov eax,[oldpage] + ret + + ENDP Mono_View_Page + + END + diff --git a/WINVQ/VQM32/MONO.H b/WINVQ/VQM32/MONO.H new file mode 100644 index 0000000..991662c --- /dev/null +++ b/WINVQ/VQM32/MONO.H @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +#ifndef VQMMONO_H +#define VQMMONO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* mono.h +* +* DESCRIPTION +* Mono screen definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Feburary 8, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Mono_Enable(void); +void __cdecl Mono_Disable(void); +void __cdecl Mono_Set_Cursor(long x, long y); +void __cdecl Mono_Clear_Screen(void); +void __cdecl Mono_Scroll(long lines); +void __cdecl Mono_Put_Char(long character, long attrib); +void __cdecl Mono_Draw_Rect(long x, long y, long w, long h, long attrib, + long thick); + +void __cdecl Mono_Text_Print(void const *text, long x, long y, long attrib); +void __cdecl Mono_Print(void const *text); +short __cdecl Mono_View_Page(long page); +short __cdecl Mono_X(void); +short __cdecl Mono_Y(void); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMMONO_H */ diff --git a/WINVQ/VQM32/PALETTE.ASM b/WINVQ/VQM32/PALETTE.ASM new file mode 100644 index 0000000..44e8833 --- /dev/null +++ b/WINVQ/VQM32/PALETTE.ASM @@ -0,0 +1,319 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* palette.asm +;* +;* DESCRIPTION +;* Hardware level palette routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* To write palette colors: +;* - Out color # to 3c8h +;* - Out RGB values to 3c9h (data must be written in three's; PEL address +;* register auto-increments after 3 reads or writes) +;* +;* A time interval of about 240 ns is required between successive reads/ +;* writes; on very fast machines, this means that the system may not be +;* able to handle a rapid-fire of RGB values. So, a "safe" routine is +;* provided that has wait states between each out. +;* +;* Reference: Progammers Guide to the EGA & VGA Cards, Ferraro, 2nd ed. +;* (Chapter 8.) +;* +;* Note that, if you set the palette in active scan, the screen will +;* flash; to prevent this, wait for vertical retrace (Vertical Blank +;* Interval), or turn the display off by using the Screen Off field in +;* the Clocking Mode register (Hmmmm....). +;* +;* To read palette colors: +;* - Out color # to 3c7h +;* - In RGB values from 3c9h (data must be read in three's; PEL address +;* register auto-increments after 3 reads or writes) +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* SetPalette - Set the palette without waiting to Vblank. +;* ReadPalette - Read the palette from the display adapter. +;* SetDAC - Set a single palette color in the DAC. +;* TranslatePalette - Translate 24-bit color to 15-bit color. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* SetPalette - Set the palette without waiting to Vblank. +;* +;* SYNOPSIS +;* SetPalette(Palette, Numbytes, SlowFlag) +;* +;* void SetPalette(char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Palette - Pointer to the palette to set. +;* NumBytes - Number of bytes of palette to transfer (multiple of 3). +;* SlowFlag - Slow palette set flag. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C SetPalette:NEAR + PROC SetPalette C NEAR USES eax ecx edx esi ds + + ARG palette:NEAR PTR + ARG numbytes:DWORD + ARG slowpal:DWORD + + pushf + cld + + cmp [slowpal],0 ;Do slow palette? + jne ??safe_palette_routine + +;---------------------------------------------------------------------------- +; Fast palette set +;---------------------------------------------------------------------------- + + mov esi,[palette] + mov edx,PEL_WRITE_ADDR + xor al,al + out dx,al ;Select color to write too. + inc al ;Step to the next color for next loop + inc edx ;DX = PEL_DATA + mov ecx,[numbytes] ;Max # colors to set + rep outsb ;Write 256 * RGB out to the palette + popf + ret + +;---------------------------------------------------------------------------- +; Safe palette set +;---------------------------------------------------------------------------- + +??safe_palette_routine: + mov esi,[palette] + mov ecx,[numbytes] + mov edx,PEL_WRITE_ADDR + sub eax,eax + out dx,al + mov edx,PEL_DATA + +??Write_loop: + lodsb + out dx,al ;Red + jmp $+02 ;Delay (flush instruction cache) + + lodsb + out dx,al ;Green + jmp $+02 ;Delay (flush instruction cache) + + lodsb + out dx,al ;Blue + jmp $+02 ;Delay (flush instruction cache) + + sub cx,3 + ja ??Write_loop + + popf + ret + + ENDP SetPalette + + +;**************************************************************************** +;* +;* NAME +;* ReadPalette - Read the palette from the display adapter. +;* +;* SYNOPSIS +;* ReadPalette(Palette) +;* +;* void SetPalette(char *); +;* +;* FUNCTION +;* +;* INPUTS +;* Palette - Pointer buffer to copy palette into. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ReadPalette:NEAR + PROC ReadPalette C NEAR USES ecx edx edi es + + ARG palette:NEAR PTR + + mov edi,[palette] + mov ecx,256 + mov edx,PEL_READ_ADDR + sub eax,eax + out dx,al + mov edx,PEL_DATA + +??Read_loop: + in al,dx ;Red + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + in al,dx ;Green + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + in al,dx ;Blue + stosb ;Save the byte + jmp $+02 ;Delay (flush instruction cache) + + dec ecx + jnz ??Read_loop + ret + + ENDP ReadPalette + + +;**************************************************************************** +;* +;* NAME +;* SetDAC - Set a single palette color in the DAC. +;* +;* SYNOPSIS +;* SetDAC(ColorNum, Red, Green, Blue) +;* +;* void SetPalette(long, char, char); +;* +;* FUNCTION +;* +;* INPUTS +;* ColorNum - Position number in palette of color to set. +;* Red - Red gun value. +;* Green - Green gun value. +;* Blue - Blue gun value. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C SetDAC:NEAR + PROC SetDAC C NEAR USES edx + + ARG color_num:DWORD + ARG red:BYTE + ARG green:BYTE + ARG blue:BYTE + + mov edx,PEL_WRITE_ADDR + mov eax,[color_num] + out dx,al ;Set color position + inc edx + jmp $+02 ;Delay (flush instruction cache) + + mov al,[red] + out dx,al ;Set red gun + jmp $+02 ;Delay (flush instruction cache) + + mov al,[green] + out dx,al ;Set green gun + jmp $+02 ;Delay (flush instruction cache) + + mov al,[blue] + out dx,al ;Set blue gun + ret + + ENDP SetDAC + + +;**************************************************************************** +;* +;* NAME +;* TranslatePalette - Translate 24-bit color to 15-bit color. +;* +;* SYNOPSIS +;* TranslatePalette(Pal24, Pal15, NumBytes) +;* +;* void TranslatePalette(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Pal24 - Pointer to 24-bit palette. (Input) +;* Pal15 - Pointer to 15-bit palette. (Output) +;* NumBytes - Number of bytes to translate. (divisible by 3) +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C TranslatePalette:NEAR + PROC TranslatePalette C NEAR USES ecx edx edi esi + + ARG pal24:NEAR PTR + ARG pal15:NEAR PTR + ARG numbytes:DWORD + + mov esi,[pal24] + mov edi,[pal15] + mov ecx,[numbytes] + +??TranslatePalette: + mov ah,[BYTE PTR esi] ;AH = red + mov al,[BYTE PTR esi+1] ;AL = green + mov dl,[BYTE PTR esi+2] ;DL = blue + shr ah,1 ;Red = lower 5 bits of AH + shl al,2 ;Green = upper 6 bits of AL + shr dl,1 ;Blue = lower 5 bits of DL + shl eax,2 ;Make room for blue + and al,0E0h ;Trim off bottom bit of green + or al,dl ;Load in blue bits + mov [WORD PTR edi],ax ;Store the value + add esi,3 ;Increment to next RGB values + add edi,2 ;Increment to next palette word + sub ecx,3 + ja ??TranslatePalette + ret + + ENDP TranslatePalette + + END diff --git a/WINVQ/VQM32/PALETTE.H b/WINVQ/VQM32/PALETTE.H new file mode 100644 index 0000000..45f510c --- /dev/null +++ b/WINVQ/VQM32/PALETTE.H @@ -0,0 +1,59 @@ +/* +** 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 . +*/ + +#ifndef VQMPALETTE_H +#define VQMPALETTE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Palette.h (32-Bit protected mode) +* +* DESCRIPTION +* Palette definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +void __cdecl ReadPalette(void *palette); +void __cdecl SetDAC(long color, long red, long green, long blue); +void __cdecl TranslatePalette(void *pal24, void *pal15, long numbytes); + +#ifdef __cplusplus +} +#endif + +void SortPalette(unsigned char *pal, long numcolors); + +#endif /* VQMPALETTE_H */ + diff --git a/WINVQ/VQM32/PORTIO.ASM b/WINVQ/VQM32/PORTIO.ASM new file mode 100644 index 0000000..0e855b7 --- /dev/null +++ b/WINVQ/VQM32/PORTIO.ASM @@ -0,0 +1,116 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* portio.asm +;* +;* DESCRIPTION +;* I/O Port access. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* inp - Read a byte from a hardware port. +;* outp - Write a byte to a hardware port. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +;**************************************************************************** +;* +;* NAME +;* inp - Read a byte from a hardware port. +;* +;* SYNOPSIS +;* Data = inp(PortID) +;* +;* short inp(unsinged short); +;* +;* FUNCTION +;* +;* INPUTS +;* PortID - Address if hardware port. +;* +;* RESULT +;* Data - Data read from port. +;* +;**************************************************************************** + + GLOBAL C inp:NEAR + PROC inp C NEAR USES edx + ARG port:WORD + + mov dx,[port] + xor eax,eax + in al,dx + ret + + ENDP inp + + +;**************************************************************************** +;* +;* NAME +;* outp - Write a byte to a hardware port. +;* +;* SYNOPSIS +;* outp(PortID, Value) +;* +;* void outp(unsinged short, short); +;* +;* FUNCTION +;* +;* INPUTS +;* PortID - Address if hardware port. +;* Value - Value to write. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C outp:NEAR + PROC outp C NEAR USES edx + ARG port:WORD + ARG value:WORD + + mov dx,[port] + mov ax,[value] + out dx,al + ret + + ENDP outp + + END diff --git a/WINVQ/VQM32/PORTIO.H b/WINVQ/VQM32/PORTIO.H new file mode 100644 index 0000000..2566613 --- /dev/null +++ b/WINVQ/VQM32/PORTIO.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +#ifndef VQMPORTIO_H +#define VQMPORTIO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Portio.h (32-Bit protected mode) +* +* DESCRIPTION +* Hardware port I/O +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifdef __BORLANDC__ + +#ifdef __cplusplus +extern "C" { +#endif + +short __cdecl inp(unsigned short portid); +void __cdecl outp(unsigned short portid, short value); + +#ifdef __cplusplus +} +#endif + +#endif /* __BORLANDC__ */ + +#endif /* VQMPORTIO_H */ + diff --git a/WINVQ/VQM32/PROFILE.CPP b/WINVQ/VQM32/PROFILE.CPP new file mode 100644 index 0000000..a571b65 --- /dev/null +++ b/WINVQ/VQM32/PROFILE.CPP @@ -0,0 +1,431 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* profile.c +* +* DESCRIPTION +* INI file processing. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* Get_Frame_Pathname - Get pathname for a given frame and file type. +* GetINIInt - Get an integer value from an INI file. +* GetINIString - Get a string from the INI file. +* +****************************************************************************/ + +#include +#include +#include +#include "profile.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +#define isspace(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r')?1:0) + +static char *strtrim(char *string); +static long FileGets(FILE *fp, char *buf, long buflen); + + +/**************************************************************************** +* +* NAME +* Get_Frame_Pathname - Get pathname for a given frame and file type. +* +* SYNOPSIS +* Error = Get_Frame_Pathname(IniFile, Frame, Extension, Buffer) +* +* long Get_Frame_Pathname(char *, long, char *, char *); +* +* FUNCTION +* +* INPUTS +* IniFile - Pointer to INI filename. +* Frame - Number of frame to get filename for. +* Extension - File extension type. +* Buffer - Pointer to buffer to put pathname into. +* +* RESULT +* Error - 0 if successful, or -1 if error. +* +***************************************************************************/ + +long Get_Frame_Pathname(char *inifile, long anim_frame, char *ext, + char *outbuf) +{ + char rootdir[_MAX_PATH]; // Root directory from INI file + char extdir[_MAX_PATH]; // this extension's directory + char entry_name[40]; // INI entry name + char inibuf[80]; // string returned from INI file + char *prefix; // 4-char prefix for this scene + char *startstr; // starting frame #, string + char *endstr; // ending frame #, string + char *palstr; // palette filename string + long startnum; // scene's starting frame # + long endnum; // scene's ending frame # + long total_frames; // accumulated frame total + long scene_frames; // # frames in a given scene + long scene_num; // scene # + long file_frame; // file's frame # + long rc; + + /* Get directory for this file type */ + GetINIString("Path", "Root", "", rootdir, 80, inifile); + + if (rootdir[strlen (rootdir) - 1] != '\\') { + strcat(rootdir,"\\"); + } + + GetINIString("Path", ext, "", extdir, 80, inifile); + + if (extdir[strlen (extdir) - 1] != '\\') { + strcat(extdir,"\\"); + } + + /* VQG is a special case: + * + * The VQG files are named based upon the 1st 4 characters of the 'Name' + * entry in the INI file, and their numbers match the actual animation + * frame numbers, not the scene frame numbers. + */ + if (!stricmp(ext, "VQG")) { + GetINIString("Common", "Name", "", inibuf, 80, inifile); + + if (strlen(inibuf) > 4) { + inibuf[4] = 0; + } + + sprintf(outbuf,"%s%s%s%04d.%s",rootdir,extdir,inibuf,anim_frame,ext); + return (0); + } + + /*------------------------------------------------------------------------- + * Loop through scenes until the desired frame # is found + *-----------------------------------------------------------------------*/ + total_frames = 0; + scene_num = 1; + + while (1) { + + /* Get this scene's entry */ + sprintf(entry_name, "Scene%d", scene_num); + rc = GetINIString("Scenes",entry_name,"",inibuf,80,inifile); + + if (rc == 0) { + return (-1); + } + + /* Parse the INI entry */ + prefix = strtok(inibuf, ","); + startstr = strtok(NULL, ","); + endstr = strtok(NULL, ","); + palstr = strtok(NULL, ","); + + if ((prefix == NULL) || (startstr == NULL) || (endstr == NULL)) { + return (-1); + } + + startnum = atoi(startstr); + endnum = atoi(endstr); + scene_frames = ((endnum - startnum) + 1); + + /* requested frame is found */ + if (anim_frame < (total_frames + scene_frames)) { + + /* Palette is a special case */ + if (!stricmp(ext, "PAL")) { + if (palstr == NULL) { + return (-1); + } else { + sprintf(outbuf, "%s%s%s.PAL", rootdir, extdir, palstr); + return (0); + } + } else { + file_frame = ((anim_frame - total_frames) + startnum); + sprintf(outbuf,"%s%s%s%04d.%s",rootdir,extdir,prefix,file_frame,ext); + return (0); + } + } + + /* Frame not found; go to next scene */ + total_frames += scene_frames; + scene_num++; + } +} + + +/**************************************************************************** +* +* NAME +* GetINIInt - Get an integer value from an INI file. +* +* SYNOPSIS +* Value = GetINIInt(Section, Entry, Default, ININame) +* +* long GetINIInt(char *, char *, long, char *); +* +* FUNCTION +* Retrieve an integer value from the INI file at the specified 'Section' +* and 'Entry' fields. If no value is defined then return the passed in +* 'Default' value. +* +* INPUTS +* Section - Pointer to section name. +* Entry - Pointer to entry name. +* Default - Default value. +* ININame - Pointer to INI filename. +* +* RESULT +* Value - Integer value from INI file or 'Default'. +* +****************************************************************************/ + +long GetINIInt(char const *section, char const *entry, long deflt, + char *fname) +{ + char buffer[20]; + + sprintf(buffer, "%d", deflt); + GetINIString(section, entry, buffer, buffer, sizeof(buffer), + fname); + + return (atoi(buffer)); +} + + +/**************************************************************************** +* +* NAME +* GetINIString - Get a string from the INI file. +* +* SYNOPSIS +* Length = GetINIString(Section, Entry, Default, Buffer, +* Length, ININame) +* +* long GetINIString(char *, char *, char *, char *, long, +* char *); +* +* FUNCTION +* +* INPUTS +* Section - Pointer to section name. +* Entry - Pointer to entry name. +* Default - Pointer to default string. +* Buffer - Pointer to buffer to copy string into. +* Length - Maximum length of string. +* ININame - Pointer to INI filename. +* +* RESULT +* Length - Length of string copied into the buffer. +* +****************************************************************************/ + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname) +{ + FILE *fp; + long retval; + char txt[80]; + char secname[40]; + long len; + char *workptr; + + /* Copy default value in case entry isn't found */ + strncpy(retbuffer, def, (retlen - 1)); + retbuffer[retlen - 1] = 0; + retval = min(strlen(def), (unsigned)retlen); + + /* Open the file */ + if ((fp = fopen(fname, "rt")) == NULL) { + return (retval); + } + + /* Generate section name for search */ + sprintf(secname, "[%s]", section); + len = strlen(secname); + + /* Scan file for section name */ + while (1) { + + /* Read line; return if end-of-file */ + if (FileGets(fp,txt,80)!=0) { + fclose(fp); + return (retval); + } + + /* Skip comments */ + if (txt[0] == ';') continue; + + /* Parse a section name */ + if (txt[0] == '[') { + if (!memicmp(secname, txt, len)) break; + } + } + + /* Scan file for desired entry */ + len = strlen(entry); + + while (1) { + + /* Read line; return if end-of-file */ + if (FileGets(fp, txt, 80) != 0) { + fclose(fp); + return (retval); + } + + /* Skip comments */ + if (txt[0] == ';') continue; + + /* Return if start of next section reached */ + if (txt[0] == '[') { + fclose(fp); + return (retval); + } + + /* Entry found; parse it */ + if (!memicmp(entry, txt, len) && (isspace(txt[len]) + || txt[len] == '=')) { + + fclose(fp); + + /* Find '=' character */ + workptr = strchr(txt, '='); + + /* Return if not found */ + if (workptr == NULL) return (retval); + + /* Skip past '=' */ + workptr++; + + /* Skip white space */ + while (isspace(*workptr) && strlen(workptr) > 0) { + workptr++; + } + + /* Return if no string left */ + if ((*workptr) == 0) return (retval); + + strtrim(workptr); + strcpy(retbuffer,workptr); + + return (strlen(workptr)); + } + } +} + + +/**************************************************************************** +* +* NAME +* strtrim - Trim off trailing spaces from a string. +* +* SYNOPSIS +* String = strtrim(String) +* +* char *strtrim(char *); +* +* FUNCTION +* +* INPUTS +* String - Pointer to string to trim. +* +* RESULT +* String - Pointer to trimmed string. +* +****************************************************************************/ + +static char *strtrim(char *string) +{ + long i; + + /* Return if NULL ptr or zero-length string */ + if ((string == NULL) || (strlen(string) == 0)) { + return (string); + } + + /* Find 1st non-white-space character from the right */ + i = (strlen(string) - 1); + + while ((i > 0) && isspace(string[i])) { + i--; + } + + /* Set end of string */ + i++; + string[i] = 0; + + return (string); +} + + +/**************************************************************************** +* +* NAME +* FileGets - A better fgets. +* +* SYNOPSIS +* Error = FileGets(FilePtr, Buffer, Length) +* +* long FileGets(FILE *, char *, long); +* +* FUNCTION +* +* INPUTS +* FilePtr - File pointer. +* Buffer - Pointer to buffer to fill. +* Length - Maximum length of buffer. +* +* RESULT +* Error = 0 if successfull, or -1 if error. +* +****************************************************************************/ + +static long FileGets(FILE *fp, char *buf, long buflen) +{ + if (fgets(buf, buflen, fp)) { + buf[(strlen(buf) - 1)] = 0; + return (0); + } else { + return (-1); + } +} + diff --git a/WINVQ/VQM32/PROFILE.H b/WINVQ/VQM32/PROFILE.H new file mode 100644 index 0000000..d4641bd --- /dev/null +++ b/WINVQ/VQM32/PROFILE.H @@ -0,0 +1,52 @@ +/* +** 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 . +*/ + +#ifndef VQMPROFILE_H +#define VQMPROFILE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Profile.h (32-Bit protected mode) +* +* DESCRIPTION +* INI file profiling definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/* Prototypes */ +long GetINIInt(char const *section, char const *entry, + long deflt, char *fname); + +long GetINIString(char const *section, char const *entry, + char const *def, char *retbuffer, long retlen, char *fname); + +long Get_Frame_Pathname(char *inifile,long anim_frame,char *ext, + char *outbuf); + +#endif /* VQMPROFILE_H */ + diff --git a/WINVQ/VQM32/REALMODE.H b/WINVQ/VQM32/REALMODE.H new file mode 100644 index 0000000..593151b --- /dev/null +++ b/WINVQ/VQM32/REALMODE.H @@ -0,0 +1,82 @@ +/* +** 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 . +*/ + +#ifndef VQMREALMODE_H +#define VQMREALMODE_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* realmode.h +* +* DESCRIPTION +* Real-mode interfacing definitions and equates. Many of the definitions +* and descriptions in this file were taken from other sources and +* compiled here for use in MISC32 library. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 6, 1995 +* +****************************************************************************/ + +/* REALPTR: Real-mode pointer (segment:offset16). + * + * The REALPTR data type is used in protected mode to hold real-mode + * pointers. The type is an unsigned long value, were the upper 16 bits + * are the segment number and the lower 16 bit are an offset. This type + * and the associated macros are identical to that of the PHARLAP "pltypes.h" + * definitions for easy of conversion to WATCOM/4GW. + */ +typedef unsigned long REALPTR; + +#define RP_OFF(rp) ((unsigned short)(((unsigned long)(rp)) & 0xFFFF)) +#define RP_SEG(rp) ((unsigned short)(((unsigned long)(rp)) >> 16)) +#define RP_SET(rp, off, seg) (rp = ((unsigned long)(seg) << 16) + (off)) +#define RP_INCR(rp, incr) (rp += ((unsigned long)(incr)) & 0xFFFF) + +#define MK_PTR(off, seg) (void *)((((unsigned long)seg&0xFFFF)<<4)+off) + +/* RMInfo: Real-mode interrupt call structure. + * + * Information that needs to be passed down to the real-mode interrupt is + * transfered using this structure. The address to this protected-mode + * structure (allocated by user) is passed into DPMI function 0x300. DOS/4GW + * will then use this information to set up the real-mode registers, switch + * to real-mode and then execute the interrupt in real-mode. + */ +typedef struct _RMInfo { + long edi; + long esi; + long ebp; + long reservedbysystem; + long ebx; + long edx; + long ecx; + long eax; + short flags; + short es,ds,fs,gs,ip,cs,sp,ss; +} RMInfo; + +#endif /* VQMREALMODE_H */ + diff --git a/WINVQ/VQM32/SORTPAL.CPP b/WINVQ/VQM32/SORTPAL.CPP new file mode 100644 index 0000000..e56a6e5 --- /dev/null +++ b/WINVQ/VQM32/SORTPAL.CPP @@ -0,0 +1,315 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* sortpal.c +* +* DESCRIPTION +* Palette sorting routines. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* Bill Randolph +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SortPalette - Sort a palette. +* Comp_Luminance - Compare the luminace of two 24-bit palette entries. +* Comp_HSV - Compare the HSV of two 24-bit palette entries. +* RGB_To_HSV - Convert RGB color to HSV color. +* +****************************************************************************/ + +#include +#include +#include "palette.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +/* HSV color model */ +#define DIVIDE_WITH_ROUND(n,d) (unsigned short)(((n)/(d))+ \ + (unsigned short)(((n)%(d)) >= (((d)+1)>>1))) + +#define HSV_BASE 255 +#define HUE_WEIGHT 10L +#define SATURATION_WEIGHT 100L +#define VALUE_WEIGHT 1000L + +/* Prototypes */ +static int Comp_Luminance(const void *elem1, const void *elem2); +static int Comp_HSV(const void *elem1, const void *elem2); +static void RGB_To_HSV(unsigned short r, unsigned short g, unsigned short b, + unsigned short *h, unsigned short *s, unsigned short *v); + + +/**************************************************************************** +* +* NAME +* SortPalette - Sort a palette. +* +* SYNOPSIS +* SortPalette(Palette, NumColors) +* +* void SortPalette(unsigned char *, long); +* +* FUNCTION +* Sort the palette colors. +* +* INPUTS +* Palette - Pointer to palette to sort. +* NumColors - Number of colors in the palette. +* +* RESULT +* NONE +* +****************************************************************************/ + +void SortPalette(unsigned char *pal, long numcolors) +{ + qsort(pal, numcolors, 3, Comp_Luminance); + + pal[0] = 0; + pal[1] = 0; + pal[2] = 0; +} + + +/**************************************************************************** +* +* NAME +* Comp_Luminance - Compare the luminace of two 24-bit palette entries. +* +* SYNOPSIS +* Result = Comp_Luminance(Color1, Color2) +* +* long Comp_Luminance(void *, void *); +* +* FUNCTION +* Compare the luminace of the two colors and determine which color is +* brighter than the other. +* +* The computation used is: +* Luminance = (red * .299) + (green * .587) + (blue * .114) +* +* INPUTS +* Color1 - Pointer to palette entry. +* Color2 - Pointer to palette entry. +* +* RESULT +* Result - 0 = same, 1 = Color1 > Color2, -1 = Color1 < Color2 +* +****************************************************************************/ + +static int Comp_Luminance(const void *elem1, const void *elem2) +{ + unsigned char *pal; + long r,g,b; + long total1,total2; + + /* Compute luminance for color1 */ + pal = (unsigned char *)elem1; + r = ((long)pal[0]); + g = ((long)pal[1]); + b = ((long)pal[2]); + total1 = ((r * 19595L) + (g * 38470L) + (b * 7471L)); + + /* Compute luminance for color2 */ + pal = (unsigned char *)elem2; + r = ((long)pal[0]); + g = ((long)pal[1]); + b = ((long)pal[2]); + total2 = ((r * 19595L) + (g * 38470L) + (b * 7471L)); + + if (total1 < total2) { + return (-1); + } else if (total1 > total2) { + return (1); + } else { + return (0); + } +} + + +/**************************************************************************** +* +* NAME +* Comp_HSV - Compare the HSV of two 24-bit palette entries. +* +* SYNOPSIS +* Result = Comp_HSV(Color1, Color2) +* +* long Comp_HSV(void *, void *); +* +* FUNCTION +* Compare the HSV color values of two colors and determine the +* relationship between the colors in the color space. +* +* INPUTS +* Color1 - Pointer to 1st palette entry. +* Color2 - Pointer to 2nd palette entry. +* +* RESULT +* Result - 0 = same, 1 = Color1 > Color2, -1 = Color1 < Color2 +* +****************************************************************************/ + +static int Comp_HSV(const void *elem1, const void *elem2) +{ + unsigned char *pal; + unsigned char r,g,b; + unsigned short h,s,v; + unsigned long key1,key2; + long retval; + + /* Convert 1st element to HSV */ + pal = (unsigned char *)elem1; + r = pal[0]; + g = pal[1]; + b = pal[2]; + + RGB_To_HSV((unsigned short)r,(unsigned short)g,(unsigned short)b,&h,&s,&v); + key1 = ((h * HUE_WEIGHT) + (s * SATURATION_WEIGHT) + (v * VALUE_WEIGHT)); + + /* Convert 2nd element to HSV */ + pal = (unsigned char *)elem2; + r = pal[0]; + g = pal[1]; + b = pal[2]; + + RGB_To_HSV((unsigned short)r,(unsigned short)g,(unsigned short)b,&h,&s,&v); + key2 = ((h * HUE_WEIGHT) + (s * SATURATION_WEIGHT) + (v * VALUE_WEIGHT)); + + if (key1 != key2) { + retval = ((key1 < key2) ? -1 : 1); + } else { + retval = 0; + } + + return (retval); +} + + +/*************************************************************************** +* +* NAME +* RGB_To_HSV - Convert RGB color to HSV color. +* +* SYNOPSIS +* RGB_To_HSV(R, G, B, H, S, V) +* +* void RGB_To_HSV(unsigned short, unsigned short, unsigned short, +* unsigned short *, unsigned short *, unsigned short *); +* +* FUNCTION +* Convert the RBG color to a HSV color. Assumes 8 bits per gun of R, G +* and B data. Also the HSV is based on a 255 degree scale rather than +* the more accurate 360 degree scale. +* +* INPUTS +* R - Red gun value. +* G - Green gun value. +* B - Blue gun value. +* H - Pointer to H value. (H will be set upon return of this function) +* S - Pointer to S value. (S will be set upon return of this function) +* V - Pointer to V value. (V will be set upon return of this function) +* +* RESULT +* NONE +* +***************************************************************************/ + +static void RGB_To_HSV(unsigned short r, unsigned short g, unsigned short b, + unsigned short *h, unsigned short *s, unsigned short *v) +{ + unsigned short m; + unsigned short r1; + unsigned short g1; + unsigned short b1; + unsigned short tmp; + + /* Set hue to default. */ + *h = 0; + + /* Set v = Max(r,g,b) to find dominant primary color. */ + *v = ((r > g) ? r : g); + + if (b > *v) { + *v = b; + } + + /* Set m = min(r,g,b) to find amount of white. */ + m = ((r < g) ? r : g); + + if (b < m) { + m = b; + } + + /* Determine the normalized saturation. */ + if (*v != 0) { + *s = DIVIDE_WITH_ROUND((*v - m) * HSV_BASE, *v); + } else { + *s = 0; + } + + if (*s != 0) { + tmp = *v - m; + r1 = DIVIDE_WITH_ROUND((*v - r) * HSV_BASE, tmp); + g1 = DIVIDE_WITH_ROUND((*v - g) * HSV_BASE, tmp); + b1 = DIVIDE_WITH_ROUND((*v - b) * HSV_BASE, tmp); + + /* Find effect of second most predominant color. + * In which section of the hexagon of colors does the color lie? + */ + if ((*v) == r) { + if (m == g) { + *h = 5 * HSV_BASE + b1; + } else { + *h = 1 * HSV_BASE - g1; + } + } else { + if ((*v) == g) { + if (m == b) { + *h = 1 * HSV_BASE + r1; + } else { + *h = 3 * HSV_BASE - b1; + } + } else { + if (m == r) { + *h = 3 * HSV_BASE + g1; + } else { + *h = 5 * HSV_BASE - r1; + } + } + } + + /* Divide by six and round. */ + *h = DIVIDE_WITH_ROUND(*h, 6); + } +} diff --git a/WINVQ/VQM32/SOSCODEC.ASM b/WINVQ/VQM32/SOSCODEC.ASM new file mode 100644 index 0000000..5910e5f --- /dev/null +++ b/WINVQ/VQM32/SOSCODEC.ASM @@ -0,0 +1,1271 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 ?? + + STRUC sCompInfo +lpSource DD ? ;Compressed data pointer +lpDest DD ? ;Uncompressed data pointer +dwCompSize DD ? ;Compressed size +dwUnCompSize DD ? ;Uncompressed size +wBitSize DW ? ;Bit size for decompression +wChannels DW ? ;number of channels + +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 + ENDS sCompInfo + + DATASEG + +;* 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 +dwCODECBytesProcessed DD 0 ; this is how many so far compressed +dwCODECTempStep DD 0 ; tempory storage for step value +wCODECMask DW 0 ; Current mask + CODESEG + +;**************************************************************************** +;* +;* 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 VQA_sosCODECInitStream:NEAR + PROC VQA_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 VQA_sosCODECInitStream + +;**************************************************************************** +;* +;* NAME +;* sosCODECCompressData - Compress audio data. +;* +;* SYNOPSIS +;* Size = sosCODECCompressData(CompInfo, NumBytes) +;* +;* long sosCODECCompressData(_SOS_COMPRESS_INFO *, long); +;* +;* FUNCTION +;* Compress an audio data stream into 4:1 ADPCM. 16 bit data is +;* compressed 4:1, 8 bit data is compressed 2:1. +;* +;* INPUTS +;* CompInfo - Pointer to initialized compress information structure. +;* NumBytes - Number of bytes to compress. +;* +;* RESULT +;* Size - Size of compressed data. +;* +;**************************************************************************** + + GLOBAL C VQA_sosCODECCompressData:NEAR + PROC VQA_sosCODECCompressData C NEAR + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 ;16 bit requested? + jne short ??skipByteDivide ;no so skip divide + shr eax,1 ;divide size by 2 + +??skipByteDivide: + mov [dwCODECByteIndex],eax + mov esi,[(sCompInfo ebx).lpSource] ;ESI = source + mov edi,[(sCompInfo ebx).lpDest] ;EDI = dest + + cmp [(sCompInfo ebx).wChannels],2 ;stereo check + je ??mainloopl + +;------------------------------------------------------------------------ +; Mono start +;------------------------------------------------------------------------ + +??mainloop: + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bit ;no. goto 8 bit input + + movsx eax,[word ptr esi] ;Get 16bit sample + add esi,2 + jmp short ??computeDiff ;skip 8 bit load + +??input8Bit: + mov ah,[esi] ;Get 8bit sample + inc esi + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax + +??computeDiff: + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted] + sub eax,ecx ;sample-predicted + xor ecx,ecx ;clear ecx + cmp eax,0 ;Diff > = 0 + jge ??positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positive: + mov [(sCompInfo ebx).wCode],cx ;Store code + movsx ecx,[(sCompInfo ebx).wStep] ;Get step value + mov [dwCODECTempStep],ecx + mov edx,4 ;mask value (i think) + mov ecx,3 ;loop count + +??quantizeLoop: + cmp eax,[dwCODECTempStep] ;Diff < step ? + jl short ??nextQLoop ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoop: + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoop ; back to quatize loop + +;----------------------------------------------------------------------------------------- +; now i'v got the new diff and code is masked right +;----------------------------------------------------------------------------------------- + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex],1 ; is it even? (starts at 0) + jne short ??storeToken ; if so goto store token + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf],ax ;wCodeBuf=ax + jmp short ??calcDifference ;goto calcDifference + +??storeToken: + ; fetch new token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=code + shl eax,4 ;shift low nibble to high + or ax,[(sCompInfo ebx).wCodeBuf] ;or in the stored nibble + mov [edi],al ;*dest=al + inc edi ;dest++ + +??calcDifference: + mov [(sCompInfo ebx).dwDifference],0 ;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4 + add [(sCompInfo ebx).dwDifference],ecx ;difference+=step + +??no4: + test eax,2 ;Check 0010 + je short ??no2 + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>1 + +??no2: + test eax,1 ;Check 0001 + je short ??no1 + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>2 + +??no1: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8 + neg [(sCompInfo ebx).dwDifference] ;Negate diff because sign bit was set + +??no8: + mov eax,[(sCompInfo ebx).dwPredicted] + add eax,[(sCompInfo ebx).dwDifference] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflow + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflow: + cmp eax,0FFFF8000h + jg short ??noUnderflow + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflow: + mov [(sCompInfo ebx).dwPredicted],eax ;store into predicted + xor ecx,ecx + mov cx,[(sCompInfo ebx).wCode] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex],ax ;wIndex+=ax + cmp [(sCompInfo ebx).wIndex],8000h ; check if wIndex < 0 + 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 ; }while !0 + + jmp ??exitout +;----------------------------------------------------------------------- +;Stereo Left Side +;----------------------------------------------------------------------- + +??mainloopl: +; determine bit size for input ;do{ + + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bitl ;no. goto 8 bit input ** + + movsx eax,[word ptr esi] ;load next word from source + add esi,4 ;inc source by 2 words ** + jmp short ??computeDiffl ;skip 8 bit load ** + +??input8Bitl: + mov ah,[esi] ;Get 8 bit sample + add esi,2 ;inc source by 2 bytes ** + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax ;sign extend into eax + +??computeDiffl: + ; compute difference + + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted] + ;load predicted (starts at 0) + sub eax,ecx ;difference=sample-preditcted + + ; check if dwDifference > 0. ECX is the + ; sign bit, it is initialized to positive. + + xor ecx,ecx ;clear ecx + cmp eax,0 ;if(difference>=0) + jge short ??positivel ;goto positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positivel: + mov [(sCompInfo ebx).wCode],cx ;Store code from cx into struct + + ; set up to quantize difference. initialize + ; wCODECTempStep = step value. + + movsx ecx,[(sCompInfo ebx).wStep] ;ecx=step value(starts at 7) + mov [dwCODECTempStep],ecx ;tempstep=step + mov edx,4 ;edx=4 mask value (i think) + mov ecx,3 ;ecx is loop number so loop 3 times + +??quantizeLoopl: + ; check to see if difference > tempstep value. + + cmp eax,[dwCODECTempStep] ;if(difference < tempstep) + jl short ??nextQLoopl ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoopl: + + ; shift down tempstep and mask + + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoopl ; back to quatize loop +;------------------------------------------------------------------------------------------ +; now i'v got the new diff and code is masked right + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex],1 ; is it even? (starts at 0) + jne short ??storeTokenl ; if so goto store token ** + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf],ax ;wCodeBuf=ax + jmp short ??calcDifferencel ;goto calcDifference ** + +??storeTokenl: + ; fetch new token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode] ;ax=code + shl eax,4 ;shift low nibble to hign nibble + or ax,[(sCompInfo ebx).wCodeBuf] ;or in the stored nibble + mov [edi],al ;*dest=al + add edi,2 ;dest+=2 ** + +??calcDifferencel: + mov [(sCompInfo ebx).dwDifference],0;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4l ; ** + add [(sCompInfo ebx).dwDifference],ecx ;difference+=step + +??no4l: + test eax,2 ;Check 0010 + je short ??no2l ; ** + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>1 + +??no2l: + test eax,1 ;Check 0001 + je short ??no1l ; ** + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>2 + +??no1l: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8l + + ;Negate diff because sign bit was set + + neg [(sCompInfo ebx).dwDifference] + +??no8l: + mov eax,[(sCompInfo ebx).dwPredicted] + add eax,[(sCompInfo ebx).dwDifference] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflowl + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflowl: + cmp eax,0FFFF8000h + jg short ??noUnderflowl + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflowl: + mov [(sCompInfo ebx).dwPredicted],eax ;store into predicted + xor ecx,ecx ;adjust index + mov cx,[(sCompInfo ebx).wCode] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex],ax ;wIndex+=ax + + + cmp [(sCompInfo ebx).wIndex],8000h ;check if wIndex < 0 + jb short ??checkOverflowl ; ** + mov [(sCompInfo ebx).wIndex],0 ; reset index to zero + jmp short ??adjustStepl ; ** + +??checkOverflowl: + ; check if wIndex > 88 + + cmp [(sCompInfo ebx).wIndex],88 + jbe short ??adjustStepl ; ** + + ; reset index to 88 + + mov [(sCompInfo ebx).wIndex],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 ; sub 2 for stereo ** + jne ??mainloopl ; }while !0 ** + +;------------------------------------------------------------------------- +;Right channel re-set up varibles +;------------------------------------------------------------------------- + + mov eax,[wBytes] + mov esi,[(sCompInfo ebx).lpSource] ; point to source buffer + mov edi,[(sCompInfo ebx).lpDest] ; point to destination buffer + inc esi ; skip first byte + inc edi ; ship first byte + +; Check for 16 bit compression + + cmp [(sCompInfo ebx).wBitSize],16 + je short ??do16bit + mov [dwCODECByteIndex],eax + jmp short ??mainloopr + +??do16bit: + shr eax,1 ;16 bit so half as many bytes + inc esi ;16 bit so 1 more byte to skip + mov [dwCODECByteIndex],eax + + +;----------------------------------------------------------------------- +;Start of Stereo Right Side +;----------------------------------------------------------------------- + +??mainloopr: +; determine bit size for input ;do{ + + cmp [(sCompInfo ebx).wBitSize],16 ;are we doing 16 bit + jne short ??input8Bitr ;no. goto 8 bit input ** + + movsx eax,[word ptr esi] ;load next word from source + add esi,4 ;inc source by 2 words ** + jmp short ??computeDiffr ;skip 8 bit load ** + +??input8Bitr: + mov ah,[esi] ;Get 8 bit sample + add esi,2 ;inc source by 2 bytes ** + xor al,al ;zero out low byte + xor ah,80h ;flip sign bit + movsx eax,ax ;sign extend into eax + +??computeDiffr: + ; compute difference + + movsx ecx,[word ptr (sCompInfo ebx).dwPredicted2] + ;load predicted (starts at 0) + sub eax,ecx ;difference=sample-preditcted + + ; check if dwDifference > 0. ECX is the + ; sign bit, it is initialized to positive. + + xor ecx,ecx ;clear ecx + cmp eax,0 ;if(difference>=0) + jge short ??positiver ;goto positive + + neg eax ;else difference= -difference + or ecx,8 ;set nibble sign bit in ecx + +??positiver: + mov [(sCompInfo ebx).wCode2],cx ;Store code from cx into struct + + ; set up to quantize difference. initialize + ; wCODECTempStep = step value. + + movsx ecx,[(sCompInfo ebx).wStep2] ;ecx=step value(starts at 7) + mov [dwCODECTempStep],ecx ;tempstep=step + mov edx,4 ;edx=4 mask value (i think) + mov ecx,3 ;ecx is loop number so loop 3 times + +??quantizeLoopr: + ; check to see if difference > tempstep value. + + cmp eax,[dwCODECTempStep] ;if(difference < tempstep) + jl short ??nextQLoopr ;goto nextQloop + + ; OR in mask value into code and adjust difference. + + or [(sCompInfo ebx).wCode2],dx ;else or mask into code + sub eax,[dwCODECTempStep] ;difference-=tempstep + +??nextQLoopr: + + ; shift down tempstep and mask + + shr [dwCODECTempStep],1 ; TempStep>>=1 + shr edx,1 ; mask>>=1 + loop ??quantizeLoopr ; back to quatize loop +;------------------------------------------------------------------------------------------ +; now i'v got the new diff and code is masked right + + ; store off new difference value + + mov [(sCompInfo ebx).dwDifference2],eax + + ; determine if sample index is even or odd. + ; this will determine if we need to get a new token or not. + + test [(sCompInfo ebx).dwSampleIndex2],1 ; is it even? (starts at 0) + jne short ??storeTokenr ; if so goto store token ** + + ; else its odd so get token + + xor eax,eax + mov ax,[(sCompInfo ebx).wCode2] ;ax=wCode + and eax,0Fh ;and off high nibble + mov [(sCompInfo ebx).wCodeBuf2],ax ;wCodeBuf=ax + jmp short ??calcDifferencer ;goto calcDifference ** + +??storeTokenr: + xor eax,eax + mov ax,[(sCompInfo ebx).wCode2] ;ax=code + shl eax,4 ;shift low nibble to hign nibble + or ax,[(sCompInfo ebx).wCodeBuf2] ;or in the stored nibble + mov [edi],al ;*dest=al + add edi,2 ;dest+=2 ** + +??calcDifferencer: + mov [(sCompInfo ebx).dwDifference2],0 ;dwDifference=0 + xor ecx,ecx ;ecx=0 + mov cx,[(sCompInfo ebx).wStep2] ;cx=Step + xor eax,eax ;eax=0 + mov ax,[(sCompInfo ebx).wCode2] ;ax=wCode + test eax,4 ;Check 0100 + je short ??no4r + add [(sCompInfo ebx).dwDifference2],ecx ;difference+=step + +??no4r: + test eax,2 ;Check 0010 + je short ??no2r + mov edx,ecx ;edx=wStep + shr edx,1 ;edx>>1 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>1 + +??no2r: + test eax,1 ;Check 0001 + je short ??no1r + mov edx,ecx ;edx=wStep + shr edx,2 ;edx>>2 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>2 + +??no1r: + mov edx,ecx + shr edx,3 + add [(sCompInfo ebx).dwDifference2],edx ;Difference=wstep>>3 + test eax,8 ;Check 1000 + je short ??no8r + neg [(sCompInfo ebx).dwDifference2] + +??no8r: + ; add difference to predicted value. + mov eax,[(sCompInfo ebx).dwPredicted2] + add eax,[(sCompInfo ebx).dwDifference2] ;eax=Preditcted+Difference + cmp eax,7FFFh + jl short ??noOverflowr + mov eax,7FFFh ;if overflow store 7fff in diff + +??noOverflowr: + cmp eax,0FFFF8000h + jg short ??noUnderflowr + mov eax,0FFFF8000h ;if overflow 0FFFF8000 in diff + +??noUnderflowr: + mov [(sCompInfo ebx).dwPredicted2],eax ;store into predicted + xor ecx,ecx ;adjust index + mov cx,[(sCompInfo ebx).wCode2] ;cx=Code + xor eax,eax + shl ecx,1 ;cx<<1 + mov ax,[wCODECIndexTab + ecx] ;ax=Indextab[ecx] + add [(sCompInfo ebx).wIndex2],ax ;wIndex+=ax + cmp [(sCompInfo ebx).wIndex2],8000h ;check if wIndex < 0 + jb short ??checkOverflowr + mov [(sCompInfo ebx).wIndex2],0 ;reset index to zero + jmp short ??adjustStepr + +??checkOverflowr: + + cmp [(sCompInfo ebx).wIndex2],88 ;check if wIndex > 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 ; sub 2 for stereo + jne ??mainloopr ; }while !0 + + +;------------------------------------------------------------------------- +;Final clean up +;------------------------------------------------------------------------- + +??exitout: + ; save off ESI and EDI back into compress info structure. + +; mov [(sCompInfo ebx).lpSource],esi +; mov [(sCompInfo ebx).lpDest],edi + + ; set up return value for number of bytes processed. + + mov eax,[dwCODECBytesProcessed] + shr eax,1 + cmp [(sCompInfo ebx).wBitSize],16 + jne ??leave + shr eax,1 ;if not 16 bit then div/2 + +??leave: + pop edx + pop ecx + pop ebx + pop edi + pop esi + ret + + ENDP VQA_sosCODECCompressData + + +;**************************************************************************** +;* +;* 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 VQA_sosCODECDecompressData:NEAR + PROC VQA_sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 + +??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 VQA_sosCODECDecompressData + END + + diff --git a/WINVQ/VQM32/SOSCOMP.H b/WINVQ/VQM32/SOSCOMP.H new file mode 100644 index 0000000..570e76a --- /dev/null +++ b/WINVQ/VQM32/SOSCOMP.H @@ -0,0 +1,90 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + short wBitSize; + short wChannels; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl VQA_sosCODECInitStream(_SOS_COMPRESS_INFO *); +unsigned long __cdecl VQA_sosCODECCompressData(_SOS_COMPRESS_INFO *,unsigned long); +unsigned long __cdecl VQA_sosCODECDecompressData(_SOS_COMPRESS_INFO *,unsigned long); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/WINVQ/VQM32/TARGA.CPP b/WINVQ/VQM32/TARGA.CPP new file mode 100644 index 0000000..363171f --- /dev/null +++ b/WINVQ/VQM32/TARGA.CPP @@ -0,0 +1,699 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* targa.c +* +* DESCRIPTION +* Targa Image File reader. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* OpenTarga - Open Targa image file. +* CloseTarga - Close Targa image file. +* LoadTarga - Load Targa image file. +* XFlipTarga - X flip the image. +* YFlipTarga - Y flip the image. +* +* PRIVATE +* DecodeImageData - Decompress Targa image data. +* +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "targa.h" + +/* Private data declerations. */ +static long DecodeImageData(TGAHandle *, char *); +static void InvertImageData(TGAHeader *, char *); + +/**************************************************************************** +* +* NAME +* OpenTarga - Open Targa image file. +* +* SYNOPSIS +* TGAHandle = OpenTarga(Name, Mode) +* +* TGAHandle *OpenTarga(char *, unsigned short); +* +* FUNCTION +* Open a Targa image file and read in its header. The file stream will +* positioned after the ID field (if there is one). +* +* INPUTS +* Name - Pointer to name of Targa file. +* Mode - Access mode. +* +* RESULT +* TGAHandle - Pointer to initialized TGAHandle or NULL if error. +* +****************************************************************************/ + +TGAHandle *OpenTarga(char *name, unsigned short mode) +{ + TGAHandle *tga; + long size; + long error = 0; + + /* Allocate TGAHandle */ + if ((tga = (TGAHandle *)malloc(sizeof(TGAHandle))) != NULL) { + + /* Initialize TGAHandle structure. */ + memset((void *)tga, 0, sizeof(TGAHandle)); + tga->mode = mode; + + switch (mode) { + + /* Open targa file for read. */ + case TGA_READMODE: + if ((tga->fh = open(name, (O_RDONLY|O_BINARY))) != -1) { + + /* Read in header. */ + size = read(tga->fh, &tga->header, sizeof(TGAHeader)); + + if (size != sizeof(TGAHeader)) { + error = 1; + } + + /* Skip the ID field */ + if (!error && (tga->header.IDLength != 0)) { + if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) { + error = 1; + } + } + } else { + error = 1; + } + break; + + /* Open targa file for write. */ + case TGA_WRITEMODE: + if ((tga->fh = open(name, (O_CREAT|O_TRUNC|O_WRONLY|O_BINARY), + (S_IREAD|S_IWRITE))) == -1) { + + error = 1; + } else { + printf("\r"); + } + break; + + /* Open targa file for read/write.*/ + case TGA_RDWRMODE: + if ((tga->fh = open(name, (O_RDWR|O_BINARY), + (S_IREAD|S_IWRITE))) != -1) { + + /* Read in header. */ + size = read(tga->fh, &tga->header, sizeof(TGAHeader)); + + if (size != sizeof(TGAHeader)) { + error = 1; + } + + /* Skip the ID field */ + if (!error && (tga->header.IDLength != 0)) { + if (lseek(tga->fh, tga->header.IDLength, SEEK_CUR) == -1) { + error = 1; + } + } + } else { + error = 1; + } + break; + } + + /* Close on any error! */ + if (error) { + CloseTarga(tga); + tga = NULL; + } + } + + return (tga); +} + + +/**************************************************************************** +* +* NAME +* CloseTarga - Close Targa image file. +* +* SYNOPSIS +* CloseTarga(TGAHandle) +* +* void CloseTarga(TGAHandle *); +* +* FUNCTION +* Close the Targa image file and free its handle. +* +* INPUTS +* TGAHandle - Pointer to TGAHandle returned by OpenTarga(). +* +* RESULT +* NONE +* +****************************************************************************/ + +void CloseTarga(TGAHandle *tga) +{ + /* Ensure valid handle. */ + if (tga) { + + /* Close the file if it is open. */ + if (tga->fh != -1) close(tga->fh); + + /* Free TGAHandle */ + free(tga); + } +} + + +/**************************************************************************** +* +* NAME +* LoadTarga - Load Targa Image File. +* +* SYNOPSIS +* Error = LoadTarga(Name, Palette, ImageBuffer) +* +* long LoadTarga(char *, char *, char *); +* +* FUNCTION +* Open and load the Targa into the specified buffers. If either buffer +* pointer is NULL then that field will not be processed. +* +* INPUTS +* Name - Name of Targa image file to load. +* Palette - Pointer to buffer to load the palette into. +* ImageBuffer - Pointer to buffer to load the image data into. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +long LoadTarga(char *name, char *palette, char *image) +{ + TGAHandle *tga; + long size; + long depth; + long i,n; + char c; + long error = 0; + + /* Open the Targa */ + if ((tga = OpenTarga(name, TGA_READMODE)) != NULL) { + + /* Process ColorMap (palette) */ + if (tga->header.ColorMapType == 1) { + depth = (tga->header.CMapDepth >> 3); + size = (tga->header.CMapLength * depth); + + /* Load the palette from the TGA if a palette buffer is provided + * otherwise we will skip it. + */ + if ((palette != NULL) && (tga->header.CMapLength > 0)) { + + /* Adjust palette to the starting color entry. */ + palette += (tga->header.CMapStart * depth); + + /* Read in the palette. */ + if (read(tga->fh, palette, size) == size) { + + /* Swap the byte ordering of the palette entries. */ + for (i = 0; i < tga->header.CMapLength; i++) { + #if(0) + for (n = 0; n < depth; n++) { + c = *(palette + n); + *(palette + n) = *(palette + ((depth - 1) - n)); + *(palette + ((depth - 1) - n)) = c; + } + #else + c = *palette; + *palette = *(palette + (depth - 1)); + *(palette + (depth - 1)) = c; + #endif + + /* Next entry */ + palette += depth; + } + } else { + error = TGAERR_READ; + } + } else { + if (lseek(tga->fh, size, SEEK_CUR) == -1) { + error = TGAERR_READ; + } + } + } + + /* Load the image data from the TGA if an image buffer is provided + * otherwise we are done. + */ + if (!error && (image != NULL)) { + depth = (tga->header.PixelDepth >> 3); + size = ((tga->header.Width * tga->header.Height) * depth); + + switch (tga->header.ImageType) { + case TGA_CMAPPED: + if (read(tga->fh, image, size) != size) { + error = TGAERR_READ; + } + break; + + case TGA_TRUECOLOR: + if (read(tga->fh, image, size) == size) { + InvertImageData(&tga->header, image); + } else { + error = TGAERR_READ; + } + break; + + case TGA_CMAPPED_ENCODED: + error = DecodeImageData(tga, image); + break; + + case TGA_TRUECOLOR_ENCODED: + if ((error = DecodeImageData(tga, image)) == NULL) { + InvertImageData(&tga->header, image); + } + break; + + default: + error = TGAERR_NOTSUPPORTED; + break; + } + + /* Arrange the image so that the origin position (coordinate 0,0) + * is the upperleft hand corner of the image. + */ + if (!error) { + if (tga->header.ImageDescriptor & TGAF_XORIGIN) { + XFlipTarga(&tga->header, image); + } + + if ((tga->header.ImageDescriptor & TGAF_YORIGIN) == 0) { + YFlipTarga(&tga->header, image); + } + } + } + + /* Close the Targa */ + CloseTarga(tga); + } else { + error = TGAERR_OPEN; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* SaveTarga - Save a Targa Image File. +* +* SYNOPSIS +* Error = SaveTarga(Name, TGAHeader, Palette, ImageBuffer) +* +* long SaveTarga(char *, TGAHeader *, char *, char *); +* +* FUNCTION +* +* INPUTS +* Name - Pointer to name of file to save. +* TGAHeader - Pointer to initialized targa header structure. +* Palette - Pointer to palette. +* ImageBuffer - Pointer to raw image data. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +long SaveTarga(char *name, TGAHeader *tgahd, char *palette, char *image) +{ + TGAHandle *tga; + long size; + long depth; + char *temppal; + char *ptr; + long i,n; + char c; + long error = 0; + + /* Open the Targa for write. */ + if ((tga = OpenTarga(name, TGA_WRITEMODE)) != NULL) { + + /* Write the header. */ + if (write(tga->fh, tgahd, sizeof(TGAHeader)) != sizeof(TGAHeader)) { + error = TGAERR_WRITE; + } + + /* Write the palette. */ + if (!error && (palette != NULL) && (tgahd->CMapLength > 0)) { + + /* Adjust palette to the starting color entry. */ + depth = (tgahd->CMapDepth >> 3); + palette += (tgahd->CMapStart * depth); + size = (tgahd->CMapLength * depth); + + /* Allocate temporary buffer for palette manipulation. */ + if ((temppal = (char *)malloc(size)) != NULL) { + memcpy(temppal, palette, size); + ptr = temppal; + + /* Swap the byte ordering of the palette entries. */ + for (i = 0; i < tga->header.CMapLength; i++) { + for (n = 0; n < (depth >> 1); n++) { + c = *(ptr + n); + *(ptr + n) = *(ptr + (depth - n)); + *(ptr + (depth - n)) = c; + } + + /* Next entry */ + palette += depth; + } + + /* Write the palette. */ + if (write(tga->fh, temppal, size) != size) { + error = TGAERR_WRITE; + } + + /* Free temporary palette buffer. */ + free(temppal); + } else { + error = TGAERR_NOMEM; + } + } + + /* Invert truecolor data. */ + if (tgahd->ImageType == TGA_TRUECOLOR) { + InvertImageData(tgahd, image); + } + + /* Write the image. */ + if (!error && (image != NULL)) { + depth = (tgahd->PixelDepth >> 3); + size = (((tgahd->Width * tgahd->Height)) * depth); + + if (write(tga->fh, image, size) != size) { + error = TGAERR_WRITE; + } + } + + /* Close targa file. */ + CloseTarga(tga); + } else { + error = TGAERR_OPEN; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* XFlipTarga - X flip the image. +* +* SYNOPSIS +* XFlipTarga(TGAHeader, Image) +* +* void XFlipTarga(TGAHeader *, char *); +* +* FUNCTION +* Flip the image in memory on its X axis. (left to right) +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* Image - Pointer to image buffer. +* +* RESULT +* NONE +* +****************************************************************************/ + +void XFlipTarga(TGAHeader *tga, char *image) +{ + char *ptr,*ptr1; + long x,y,d; + char v,v1; + char depth; + + /* Pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + for (y = 0; y < tga->Height; y++) { + ptr = (image + ((tga->Width * depth) * y)); + ptr1 = (ptr + ((tga->Width * depth) - depth)); + + for (x = 0; x < (tga->Width / 2); x++) { + for (d = 0; d < depth; d++) { + v = *(ptr + d); + v1 = *(ptr1 + d); + *(ptr + d) = v1; + *(ptr1 + d) = v; + } + + ptr += depth; + ptr1 -= depth; + } + } +} + + +/**************************************************************************** +* +* NAME +* YFlipTarga - Y flip the image. +* +* SYNOPSIS +* YFlipTarga(TGAHeader, Image) +* +* void YFlipTarga(TGAHeader *, char *); +* +* FUNCTION +* Flip the image in memory on its Y axis. (top to bottom) +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* Image - Pointer to image buffer. +* +* RESULT +* NONE +* +****************************************************************************/ + +void YFlipTarga(TGAHeader *tga, char *image) +{ + char *ptr,*ptr1; + long x,y; + char v,v1; + char depth; + + /* Pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + for (y = 0; y < (tga->Height >> 1); y++) { + + /* Compute address of lines to exchange. */ + ptr = (image + ((tga->Width * y) * depth)); + ptr1 = (image + ((tga->Width * (tga->Height - 1)) * depth)); + ptr1 -= ((tga->Width * y) * depth); + + /* Exchange all the pixels on this scan line. */ + for (x = 0; x < (tga->Width * depth); x++) { + v = *ptr; + v1 = *ptr1; + *ptr = v1; + *ptr1 = v; + ptr++; + ptr1++; + } + } +} + + +/**************************************************************************** +* +* NAME +* DecodeImageData - Decompress Targa image data. +* +* SYNOPSIS +* Error = DecodeImageData(TGAHandle, ImageBuffer) +* +* long DecodeImageData(TGAHandle *, char *); +* +* FUNCTION +* Decode the RLE compressed image data into the specified buffer from +* the file I/O stream. +* +* INPUTS +* TGAHandle - Pointer to TGAHandle returned by OpenTarga(). +* ImageBuffer - Pointer to buffer to decompress image into. +* +* RESULT +* Error - 0 if successful, or TGAERR_??? error code. +* +****************************************************************************/ + +static long DecodeImageData(TGAHandle *tga, char *image) +{ + char *packet; + unsigned char count; + unsigned char depth; + unsigned long pixel_count; + unsigned long size; + unsigned long c,i; + long error = 0; + + /* Compute pixel depth in bytes. */ + depth = (tga->header.PixelDepth >> 3); + + /* Total number of pixels compressed in this image. */ + pixel_count = (tga->header.Width * tga->header.Height); + + /* Allocate packet buffer to hold maximum encoded data run. */ + if ((packet = (char *)malloc(128 * depth)) != NULL) { + while ((pixel_count > 0) && !error) { + + /* Read count. */ + if (read(tga->fh, &count, 1) == 1) { + + /* If bit 8 of the count is set then we have a run of pixels, + * otherwise the data is raw pixels. + */ + if (count & 0x80) { + count &= 0x7F; + count++; + + /* Read in run pixel. */ + if (read(tga->fh, packet, depth) == depth) { + + /* Repeat the pixel for the run count in the image buffer. */ + for (c = 0; c < count; c++) { + for (i = 0; i < depth; i++) { + *image++ = *(packet + i); + } + } + } else { + error = TGAERR_READ; + } + } else { + count++; + size = (count * depth); + + /* Read in raw pixels. */ + if (read(tga->fh, packet, size) == size) { + + /* Copy the raw pixel data into the image buffer. */ + memcpy(image, packet, size); + image += size; + } else { + error = TGAERR_READ; + } + } + + /* Adjust the pixel count. */ + pixel_count -= count; + } else { + error = TGAERR_READ; + } + } + + /* Free packet buffer. */ + free(packet); + } else { + error = TGAERR_NOMEM; + } + + return (error); +} + + +/**************************************************************************** +* +* NAME +* InvertImageData - Invert TrueColor image data. +* +* SYNOPSIS +* InvertImageData(TGAHeader, ImageData) +* +* void InvertImageData(TGAHeader *, char *); +* +* FUNCTION +* +* INPUTS +* TGAHeader - Pointer to initialized TGAHeader structure. +* ImageData - Pointer to TrueColor image data. +* +* RESULT +* NONE +* +****************************************************************************/ + +static void InvertImageData(TGAHeader *tga, char *image) +{ + long depth; + long pixel_count; + long i; + char c; + + /* Compute the pixel depth in bytes. */ + depth = (tga->PixelDepth >> 3); + + /* Total number of pixels in this image. */ + pixel_count = (tga->Width * tga->Height); + + /* 16-bit pixel layout is different that 24-bit and 32-bit. */ + if (depth > 2) { + while (pixel_count > 0) { + for (i = 0; i < (depth / 2); i++) { + c = *(image + i); + *(image + i) = *(image + ((depth - 1) - i)); + *(image + ((depth - 1) - i)) = c; + } + + /* Next pixel */ + pixel_count--; + image += depth; + } + } else { + } +} + + diff --git a/WINVQ/VQM32/TARGA.H b/WINVQ/VQM32/TARGA.H new file mode 100644 index 0000000..25bafc9 --- /dev/null +++ b/WINVQ/VQM32/TARGA.H @@ -0,0 +1,130 @@ +/* +** 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 . +*/ + +#ifndef VQMTARGA_H +#define VQMTARGA_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Targa.h (32-Bit protected mode) +* +* DESCRIPTION +* Targa Image File definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +****************************************************************************/ + +/*--------------------------------------------------------------------------- + * Targa Header definitions + *-------------------------------------------------------------------------*/ + +/* TGAHeader - Targa Image File header. + * + * IDLength - Size of Image ID field + * ColorMapType - Color map type. + * ImageType - Image type code. + * CMapStart - Color map origin. + * CMapLength - Color map length. + * CMapDepth - Depth of color map entries. + * XOffset - X origin of image. + * YOffset - Y origin of image. + * Width - Width of image. + * Height - Height of image. + * PixelDepth - Image pixel size + * ImageDescriptor - Image descriptor byte. + */ +typedef struct _TGAHeader { + char IDLength; + char ColorMapType; + char ImageType; + short CMapStart; + short CMapLength; + char CMapDepth; + short XOffset; + short YOffset; + short Width; + short Height; + char PixelDepth; + char ImageDescriptor; +} TGAHeader; + +/* ImageType definiton */ +#define TGA_NOIMAGE 0 /* No image data included in file */ +#define TGA_CMAPPED 1 /* Color-mapped image data */ +#define TGA_TRUECOLOR 2 /* Truecolor image data */ +#define TGA_MONO 3 /* Monochrome image data */ +#define TGA_CMAPPED_ENCODED 9 /* Color-mapped image data (Encoded) */ +#define TGA_TRUECOLOR_ENCODED 10 /* Truecolor image data (Encoded) */ +#define TGA_MONO_ENCODED 11 /* Monochrome image data (Encoded) */ + +/* ImageDescriptor definition */ +#define TGAF_ATTRIB_BITS (0x0F<<0) /* Number of attribute bits per pixel */ +#define TGAF_XORIGIN (1<<4) +#define TGAF_YORIGIN (1<<5) + +/*--------------------------------------------------------------------------- + * Targa Handle definitions + *-------------------------------------------------------------------------*/ + +/* TGAHandle - Targa Image File handle. + * + * fh - File handle returned by open(). + * mode - Access mode. + * header - TGAHeader structure. + */ +typedef struct _TGAHandle { + short fh; + unsigned short mode; + TGAHeader header; +} TGAHandle; + +/* Access modes. */ +#define TGA_READMODE 0 +#define TGA_WRITEMODE 1 +#define TGA_RDWRMODE 2 + +/* Error codes */ +#define TGAERR_OPEN -1 +#define TGAERR_READ -2 +#define TGAERR_WRITE -3 +#define TGAERR_SYNTAX -4 +#define TGAERR_NOMEM -5 +#define TGAERR_NOTSUPPORTED -6 + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +TGAHandle *OpenTarga(char *, unsigned short); +void CloseTarga(TGAHandle *); +long LoadTarga(char *, char *, char *); +long SaveTarga(char *, TGAHeader *, char *, char *); +void XFlipTarga(TGAHeader *, char *); +void YFlipTarga(TGAHeader *, char *); + +#endif /* VQMTARGA_H */ + diff --git a/WINVQ/VQM32/TASM32.CFG b/WINVQ/VQM32/TASM32.CFG new file mode 100644 index 0000000..f664aa0 --- /dev/null +++ b/WINVQ/VQM32/TASM32.CFG @@ -0,0 +1,10 @@ +/t +/m +/w+ +/jJUMPS +/ml +/p +/z +/iC:\PROJECTS\INCLUDE +/zi +/dPHARLAP_TNT=1 diff --git a/WINVQ/VQM32/TESTVB.CPP b/WINVQ/VQM32/TESTVB.CPP new file mode 100644 index 0000000..c128aec --- /dev/null +++ b/WINVQ/VQM32/TESTVB.CPP @@ -0,0 +1,104 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* testvb.c +* +* DESCRIPTION +* Video mode setting. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* TestVBIBit - Test the polarity of the vertical blank bit. +* +****************************************************************************/ + +#include + +#ifdef __BORLANDC__ +#include "portio.h" +#else +#include +#endif + +/**************************************************************************** +* +* NAME +* TestVBIBit - Test the polarity of the vertical blank bit. +* +* SYNOPSIS +* Polarity = TestVBIBit() +* +* long TestVBIBit(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* Polarity - Polarity of the vertical blank bit. +* +****************************************************************************/ + +long TestVBIBit(void) +{ + static struct timeb mytime; + long curtime; + long endtime; + unsigned long high = 0; + unsigned long low = 0; + + /* Set the check time for .25 (1/4) of a second. */ + ftime(&mytime); + curtime = ((mytime.time * 1000) + mytime.millitm); + endtime = (curtime + (1000 / 4)); + + /* Sample the vertical blank bit for the specified period of time. + * The state in which it is in the least is the vertical blank state, + * the state in which it is in the most is the active scan state. + */ + while (endtime >= curtime) { + ftime(&mytime); + curtime = ((mytime.time * 1000) + mytime.millitm); + + if (inp(0x3DA) & 0x08) { + high++; + } else { + low++; + } + } + + return (high > low); +} + + + diff --git a/WINVQ/VQM32/TEXT.H b/WINVQ/VQM32/TEXT.H new file mode 100644 index 0000000..afaca4e --- /dev/null +++ b/WINVQ/VQM32/TEXT.H @@ -0,0 +1,56 @@ +/* +** 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 . +*/ + +#ifndef VQMTEXT_H +#define VQMTEXT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* text.h +* +* DESCRIPTION +* Text printing definitions. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* March 13, 1995 +* +****************************************************************************/ + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +long __cdecl Text_Print(char *string, long x, long y, long fcol, long bcol); +void __cdecl Draw_Char(long character, long x, long y); +void __cdecl Set_Font_Palette_Range(void *palette, long start, long end); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMTEXT_H */ + diff --git a/WINVQ/VQM32/TEXTPRNT.ASM b/WINVQ/VQM32/TEXTPRNT.ASM new file mode 100644 index 0000000..a62e2ca --- /dev/null +++ b/WINVQ/VQM32/TEXTPRNT.ASM @@ -0,0 +1,178 @@ +; +; 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 . +; + +; $Header: J:\vq\projects\vqm32\textprnt.asv 1.5 27 Jul 1995 13:57:04 DENZIL_LONG $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : January 28, 1992 * +;* * +;* Last Update : February 3, 1992 [DRD] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Text_Print(BYTE *string, WORD x_pixel, WORD y_pixel, * +; WORD fcolor, WORD bcolor); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +XPIXEL_MAX EQU 320 +YPIXEL_MAX EQU 200 + +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + EXTRN C Char_Pixel_Width:NEAR + EXTRN C Draw_Char:NEAR + EXTRN C Set_Font_Palette_Range:NEAR + EXTRN FontPtr:NEAR PTR + EXTRN FontYSpacing:DWORD + +;---------------------------------------------------------------------------- +; TEXT_PRINT +; +; VOID Text_Print(BYTE *string, WORD x_pixel, WORD y_pixel, +; WORD fcolor, WORD bcolor); +; +; Print the given string to the LogicPage. +; +; Bounds Checking: +; +; if x_pixel < 0, then x_pixel = 0 +; if x_pixel >= XPIXEL_MAX, then exit +; if y_pixel < 0, then y_pixel = 0 +; if y_pixel >= YPIXEL_MAX, then exit +;* + + GLOBAL C Text_Print:NEAR + PROC Text_Print C NEAR USES ebx ecx edx edi esi + ARG string:NEAR PTR + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcol:DWORD + ARG bcol:DWORD + + LOCAL fwidth:DWORD ;Pixel width of font. + LOCAL fgbg:DWORD ;Two bytes of background & foreground colors. + LOCAL lines:DWORD ;Number of lines + LOCAL fontheight:DWORD + + ; Make sure there is a font available. If not, then bail. + + xor eax,eax + mov [lines],eax + + mov eax,[FontPtr] + or eax,eax + je ??exit + + movzx ebx,[WORD PTR eax+FONTINFOBLOCK] + add ebx,eax + movzx eax,[BYTE PTR ebx+FONTINFOMAXHEIGHT] + mov [fontheight],eax + mov esi,[string] + + mov ebx,[x_pixel] ; x pixel + cmp ebx,XPIXEL_MAX ; check max x pos + jae short ??exit + + mov ecx,[y_pixel] ; y pixel + cmp ecx,YPIXEL_MAX ; check max y pos + jge short ??exit + + mov al,[BYTE PTR bcol] + mov ah,[BYTE PTR fcol] + mov [fgbg],eax + lea eax,[fgbg] + call Set_Font_Palette_Range C,eax,0,1 + +; start of loop to print string + + xor edx,edx + inc [lines] +??loop: + mov dl,[esi] + inc esi + + cmp edx,0 ; end of string + je short ??exit + + cmp edx,13 ; cmp to a '\r' + jne short ??chkxy + + ; Advance the screen to the left edge and down one line. Check + ; to see if the coordinate would still be visible. If not, then + ; bail. + +??onelinedown: + mov ebx,[x_pixel] ; get original x position + add ecx,[fontheight] + add ecx,[FontYSpacing] + cmp ecx,YPIXEL_MAX ; check y pos + jae short ??exit + inc [lines] + jmp ??loop + +??chkxy: + call Char_Pixel_Width C,edx + + ; Check to see if this character would spill past the right edge + ; of the screen. If it would then drop down a line. + + mov [fwidth],eax ; save width of char for later + add eax,ebx + + cmp eax,XPIXEL_MAX ; check x pos + ja short ??onelinedown + + call Draw_Char C,edx,ebx,ecx + + add ebx,[fwidth] ; add font width + jmp ??loop + + ; Exit routine and unlock string if it was in EMS. + +??exit: + mov eax,[lines] + ret + + ENDP Text_Print + +;---------------------------------------------------------------------------- + END + diff --git a/WINVQ/VQM32/TLIB.CFG b/WINVQ/VQM32/TLIB.CFG new file mode 100644 index 0000000..4a88a3c --- /dev/null +++ b/WINVQ/VQM32/TLIB.CFG @@ -0,0 +1 @@ +/C /E diff --git a/WINVQ/VQM32/TLINK32.CFG b/WINVQ/VQM32/TLINK32.CFG new file mode 100644 index 0000000..6e679ab --- /dev/null +++ b/WINVQ/VQM32/TLINK32.CFG @@ -0,0 +1,5 @@ +/c +/m +/Gm +-LC:\PROJECTS\LIB;C:\DEV\BC4\LIB +-v diff --git a/WINVQ/VQM32/VB.ASM b/WINVQ/VQM32/VB.ASM new file mode 100644 index 0000000..41d6ece --- /dev/null +++ b/WINVQ/VQM32/VB.ASM @@ -0,0 +1,137 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vb.asm +;* +;* DESCRIPTION +;* Vertical blank routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* WaitNoVB - Wait for active scan. +;* WaitVB - Wait for vertical blank. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "video.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* WaitNoVB - Wait for active scan. +;* +;* SYNOPSIS +;* WaitNoVB() +;* +;* void WaitNoVB(void); +;* +;* FUNCTION +;* Sit and wait for the active scan of the display. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C WaitNoVB:NEAR + PROC WaitNoVB C NEAR USES edx + ARG vbibit:DWORD + + mov eax,[vbibit] + and al,1 + shl al,3 + mov ah,al + +; loop while VBL bit != VQ_VertBlank + +??no_scan_yet: + mov edx,03DAH + in al,dx + and al,8 + xor al,ah + jnz short ??no_scan_yet + ret + + ENDP WaitNoVB + + +;**************************************************************************** +;* +;* NAME +;* WaitVB - Wait for vertical blank. +;* +;* SYNOPSIS +;* WaitVB() +;* +;* void WaitVB(void); +;* +;* FUNCTION +;* Sit and wait for the vertical blank of the display. +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C WaitVB:NEAR + PROC WaitVB C NEAR USES + ARG vbibit:DWORD + + mov eax,[vbibit] + and al,1 + shl al,3 + mov ah,al + +; Loop while VBL bit = VQ_VertBlank + +??no_vbl_yet: + mov edx,03DAH + in al,dx + and al,8 + xor al,ah + jz short ??no_vbl_yet + ret + + ENDP WaitVB + + END diff --git a/WINVQ/VQM32/VERTAG.CPP b/WINVQ/VQM32/VERTAG.CPP new file mode 100644 index 0000000..982c8fc --- /dev/null +++ b/WINVQ/VQM32/VERTAG.CPP @@ -0,0 +1,49 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* VQMisc32 library. +* +* FILE +* vertag.c (32-Bit protected mode) +* +* DESCRIPTION +* Embedded version string. This string is prefixed with a tag ("$VER$") +* which can be search for to find this string. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 8, 1995 +* +****************************************************************************/ + +#ifdef __WATCOMC__ +#define DEVNAME "Watcom/4GW" +#else +#define DEVNAME "Borland/TNT" +#endif + +char VerTag[] = {"$VER$VQM32 2.12 "DEVNAME" ("__DATE__" "__TIME__")"}; diff --git a/WINVQ/VQM32/VESABLIT.CPP b/WINVQ/VQM32/VESABLIT.CPP new file mode 100644 index 0000000..a5a0f3c --- /dev/null +++ b/WINVQ/VQM32/VESABLIT.CPP @@ -0,0 +1,135 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vesablit.c +* +* DESCRIPTION +* VESA bitblit routines. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* VESA_Blit_640x480 - Blit to 640x480 256 color VESA mode. +* +****************************************************************************/ + +#include +#include +#include +#include "video.h" +#include "vesavid.h" +#include "vesablit.h" + + +/**************************************************************************** +* +* NAME +* VESA_Blit_640x480 - Blit to 640x480 256 color VESA mode. +* +* SYNOPSIS +* VESA_Blit_640x480(DisplayInfo, Buffer, X, Y, Width, Height) +* +* void VESA_Blit_640x480(DisplayInfo *, char *, long, long, long, long); +* +* FUNCTION +* +* INPUTS +* DisplayInfo - Pointer to display information structure. +* Buffer - Pointer to buffer to blit to VRAM. +* X - Destination X coordinate of blit (upper left). +* Y - Destination Y coordinate of blit (upper left). +* Width - Width of blit. +* Height - Height of blit. +* +* RESULT +* NONE +* +****************************************************************************/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1,long y1, + long width,long height) +{ + VESAModeInfo *vminfo; + long bank; + long last_bank; + long bank_offset; + long scrn_offset; + long grains_per_win; + long part1; + long part2; + long i; + + /* Initialize values */ + vminfo = (VESAModeInfo *)disp->Extended; + scrn_offset = ((disp->XRes * y1) + x1); + grains_per_win = ((long)vminfo->WinSize / (long)vminfo->WinGranularity); + bank_offset = scrn_offset % 65536L; + last_bank = -1; + + for (i = 0; i < height; i++) { + + /* Compute which bank this scanline is in */ + bank = (scrn_offset / 65536L); + + /* Set a new bank */ + if (bank != last_bank) { + SetVESAWindow(bank); + last_bank = bank; + bank_offset = (scrn_offset % 65536L); + } + + /* Copy a full scanline */ + if ((bank_offset + width) < 65536L) { + Copy_Row((char *)buf, (char *)bank_offset, width); + buf += width; + scrn_offset += disp->XRes; + bank_offset += disp->XRes; + } + + /* Copy two partial scanlines */ + else { + part1 = (65536L - bank_offset); + part2 = (width - part1); + Copy_Row((char *)buf, (char *)bank_offset, part1); + + buf += part1; + bank += grains_per_win; + last_bank += grains_per_win; + SetVESAWindow(bank); + Copy_Row((char *)buf, (char *)0, part2); + + buf += part2; + scrn_offset += disp->XRes; + bank_offset = (scrn_offset % 65536L); + } + } +} diff --git a/WINVQ/VQM32/VESABLIT.H b/WINVQ/VQM32/VESABLIT.H new file mode 100644 index 0000000..65e9a91 --- /dev/null +++ b/WINVQ/VQM32/VESABLIT.H @@ -0,0 +1,71 @@ +/* +** 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 . +*/ + +#ifndef VQMVESABLIT_H +#define VQMVESABLIT_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESABlit.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA bitblit routines. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * FUNCTION PROTOTYPES + *-------------------------------------------------------------------------*/ + +void VESA_Blit_640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl Blit_VESA640x480(DisplayInfo *disp,unsigned char *buf,long x1, + long y1,long width,long height); + +void __cdecl Buf_320x200_To_VESA_320x200(unsigned char *buffer, long grain); +void __cdecl Buf_320x200_To_VESA_640x400(unsigned char *buffer, long grain); +void __cdecl Buf_320x200_To_VESA_32K(unsigned char *buffer, + unsigned char *palette, long grain); + +void __cdecl Copy_Row(char *, char *, long); +void __cdecl Copy_Word_Row(char *source, char *dest, char *palette, + long numbytes); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVESABLIT_H */ + diff --git a/WINVQ/VQM32/VESABUF.ASM b/WINVQ/VQM32/VESABUF.ASM new file mode 100644 index 0000000..a47d435 --- /dev/null +++ b/WINVQ/VQM32/VESABUF.ASM @@ -0,0 +1,722 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vesabuf.asm +;* +;* DESCRIPTION +;* VESA buffered blit routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* Buf_320x200_To_VESA_320x200 - Buffer copy, unscaled +;* Buf_320x200_To_VESA_640x400 - Scales and copies 320x200 to 640x400 +;* Buf_320x200_To_VESA_32K - Copies 320x200 buffer to VESA 32K +;* colors +;* Copy_Row - Copy a row of pixels to VRAM. +;* Copy_Word_Row - Copy a row of 15-bit pixels to VRAM +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "video.i" + CODESEG + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROWS: draws 'numrows' rows of 2x2 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx, dx +;--------------------------------------------------------------------------- + MACRO DRAW_BLOCK_ROWS numrows + LOCAL ??Start_row + LOCAL ??Not_finished_a_line + LOCAL ??Done + + mov edx,numrows +??Start_row: + mov ecx,320 + +??Not_finished_a_line: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + mov [WORD PTR es:edi+640],ax + ELSE + mov [WORD PTR edi],ax + mov [WORD PTR edi+640],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_finished_a_line + + add edi,640 + dec edx + jz ??Done + jmp ??Start_row + +??Done: + ENDM + +;--------------------------------------------------------------------------- +; DRAW_BLOCK_ROW: draws one row of 'numblks' 2x2 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx +;--------------------------------------------------------------------------- + MACRO DRAW_BLOCK_ROW numblks + LOCAL ??Not_done + + mov ecx,numblks + +??Not_done: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + mov [WORD PTR es:edi+640],ax + ELSE + mov [WORD PTR edi],ax + mov [WORD PTR edi+640],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_done + + ENDM + +;--------------------------------------------------------------------------- +; DRAW_PIXEL_ROW: draws 'numblks' 2x1 blocks +; Set ES:DI to current screen location +; Set DS:SI to current source location +; Uses: ax, cx +;--------------------------------------------------------------------------- + MACRO DRAW_PIXEL_ROW numblks + LOCAL ??Not_done + + mov ecx,numblks + +??Not_done: + mov al,[BYTE PTR esi] + mov ah,al + inc esi + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Not_done + + ENDM + +;**************************************************************************** +;* +;* NAME +;* Blit_VESA640x480 - Blit to 640x480 256 color VESA mode. +;* +;* SYNOPSIS +;* Blit_VESA640x480(DisplayInfo, Buffer, X, Y, Width, Height) +;* +;* void Blit_VESA640x480(DisplayInfo *, char *, long, long, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* DisplayInfo - Pointer to display information structure. +;* Buffer - Pointer to buffer to blit to VRAM. +;* X - Destination X coordinate of blit (upper left). +;* Y - Destination Y coordinate of blit (upper left). +;* Width - Width of blit. +;* Height - Height of blit. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + +; GLOBAL C Blit_VESA640x480:NEAR +; PROC Blit_VESA640x480 C NEAR USES +; +; ARG disp:NEAR PTR DisplayInfo +; ARG buffer:NEAR PTR +; ARG x:DWORD +; ARG y:DWORD +; ARG width:DWORD +; ARG height:DWORD +; +; LOCAL grain:DWORD +; LOCAL scrn_offset:DWORD +; LOCAL bank_offset:DWORD +; LOCAL bank:DWORD +; LOCAL xres:DWORD +; +;;---------------------------------------------------------------------------- +;; INITIALIZE +;;---------------------------------------------------------------------------- +; +; pushad +; +;; Calculate granularity units per window +; +; mov esi,[disp] +; xor eax,eax +; mov edi,[(DisplayInfo esi).Extended] +; xor ebx,ebx +; mov ax,[(VESAModeInfo edi).WinSize] +; xor edx,edx +; mov bx,[(VESAModeInfo edi).WinGranularity] +; idiv ebx +; mov [grain],eax +; +;; Calculate screen offset +; +; mov eax,[(DisplayInfo esi).XRes] +; mov [xres],eax +; imul [y] +; add eax,[x] +; mov [scrn_offset],eax +; +;; Calculate bank offset +; +; mov ebx,65536 +; idiv ebx +; mov [bank_offset],edx +; mov [bank],eax +; +; popad +; ret +; +; ENDP Blit_VESA640x480 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_320x200 - Buffer copy, unscaled +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_320x200(Buffer, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_320x200(char *, long); +;* +;* FUNCTION +;* To center the buffer on the screen, it's upper-left corner goes at +;* (160,140). This means the buffer spans VESA banks 1,2 & 3, so it must +;* be copied in 3 parts: +;* +;* Bank 1: starting offset 24224, 65 lines +;* Bank 2: starting offset 288, 102 lines +;* Bank 3: starting offset 32, 33 lines +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_320x200:NEAR + PROC Buf_320x200_To_VESA_320x200 C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG grains_per_win:DWORD + + LOCAL grain_num:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov eax,[grains_per_win] + mov esi,[buffer] + mov [grain_num],eax + +;---------------------------------------------------------------------------- +; Copy Bank 1 +;---------------------------------------------------------------------------- + + SET_WINDOW [grain_num] + mov edi,24224 ;Starting screen address + mov edx,65 ;Lines to copy + +??SetBank1: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank1 ;Draw more lines + +;---------------------------------------------------------------------------- +; Copy Bank 2 +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + + SET_WINDOW [grain_num] + mov edi,288 ;Starting screen address + mov edx,102 ;Lines to copy + +??SetBank2: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank2 ;Draw more lines + +;---------------------------------------------------------------------------- +; Copy Bank 3 +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + + SET_WINDOW [grain_num] + mov edi,32 ;Starting screen address + mov edx,33 ;Lines to copy + +??SetBank3: + mov ecx,80 ;DWORDS to copy + rep movsd ;Move the pixels + add edi,320 ;Wrap to start of next line + dec edx ;Decrement our line counter + jnz ??SetBank3 ;Draw more lines + + IF PHARLAP_TNT + pop es + ENDIF + ret + + ENDP Buf_320x200_To_VESA_320x200 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_640x400 - Scales and copies 320x200 to 640x400 +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_640x400(Buffer, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_640x400(char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_640x400:NEAR + PROC Buf_320x200_To_VESA_640x400 C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG grains_per_win:DWORD + + LOCAL grain_num:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov [grain_num],0 + +;---------------------------------------------------------------------------- +; Copy Bank 0 +; - Skip down 40 scanlines (to center the image) +; - Draw 62 scanlines (31 rows of blocks) +; - Draw top half of 128 blocks +;---------------------------------------------------------------------------- + + SET_WINDOW [grain_num] + mov edi,25600 ;Starting screen address + + DRAW_BLOCK_ROWS 62/2 ;Draw 31 rows of blocks + DRAW_PIXEL_ROW 256/2 ;Draw top half of next 128 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 1 +; - Draw bottom half of previous 128 blocks +; - Finish the scan line with full blocks +; - Draw 100 scanlines of blocks +; - last line: top half of 256 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,256/2 ;Draw bottom half of prev 128 blks + mov edi,384 + DRAW_PIXEL_ROW 256/2 + + mov edi,0 + DRAW_BLOCK_ROW 384/2 ;Fill rest of this block row + + add edi,640 + DRAW_BLOCK_ROWS 100/2 ;Draw the block rows + DRAW_PIXEL_ROW 512/2 ;Draw top half of next 512 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 2 +; - Draw bottom half of previous 256 blocks +; - Finish the scan line with full blocks +; - Draw 101 scanlines of blocks +; - last line: 64 full blocks plus top half of 256 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,512/2 ;Draw bottom half of prev 256 blks + mov edi,128 + DRAW_PIXEL_ROW 512/2 + + mov edi,0 + DRAW_BLOCK_ROW 128/2 ;Fill rest of this block row + + add edi,640 + DRAW_BLOCK_ROWS 101/2 ;Draw the block rows + DRAW_BLOCK_ROW 128/2 ;Draw next 64 blocks + DRAW_PIXEL_ROW 512/2 ;Top half of 256 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 3 +; - Draw bottom half of previous 256 blocks +; - Finish the scan line with full blocks +; - Draw 101 scanlines of blocks +; - last line: 192 full blocks, top half of 128 blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,512/2 ;Draw bottom half of prev 256 blks + mov edi,0 + DRAW_PIXEL_ROW 512/2 + DRAW_BLOCK_ROWS 101/2 ;Draw the block rows + DRAW_BLOCK_ROW 384/2 ;Last row of full blocks + DRAW_PIXEL_ROW 256/2 ;Top half of 128 blocks + +;---------------------------------------------------------------------------- +; Copy Bank 4 +; - Draw bottom half of previous 128 blocks +; - Draw 30 scanlines of blocks +;---------------------------------------------------------------------------- + + mov eax,[grains_per_win] + add [grain_num],eax + SET_WINDOW [grain_num] + + sub esi,256/2 ;Draw bottom half of prev 256 blks + mov edi,0 + DRAW_PIXEL_ROW 256/2 + DRAW_BLOCK_ROWS 30/2 ;Draw the block rows + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Buf_320x200_To_VESA_640x400 + + +;**************************************************************************** +;* +;* NAME +;* Buf_320x200_To_VESA_32K - Copies 320x200 buffer to VESA 32K colors +;* +;* SYNOPSIS +;* Buf_320x200_To_VESA_32K(Buffer, Palette, GrainPerWin) +;* +;* void Buf_320x200_To_VESA_32K(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer to buffer to transfer to VRAM +;* Palette - Pointer to 15-bit palette to use. +;* GrainPerWin - Granularity units per 64k window. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Buf_320x200_To_VESA_32K:NEAR + PROC Buf_320x200_To_VESA_32K C NEAR USES ebx ecx edx esi edi + + ARG buffer:NEAR PTR + ARG palette:NEAR PTR + ARG grains_per_win:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + push es + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + +;---------------------------------------------------------------------------- +; Copy Bank 0 +;---------------------------------------------------------------------------- + + SET_WINDOW 0 + mov edi,0 ;Start at Bank 0, offset 0 + mov ecx,32768 ;# words we'll be setting + +; Get the pixel's offset into the palette + +??Buf0Loop: + xor eax,eax + mov ebx,[palette] + mov al,[BYTE PTR esi] + add ebx,eax + inc esi + add ebx,eax + +; store the 15-bit palette value + + mov ax,[WORD PTR ebx] + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Buf0Loop + +;---------------------------------------------------------------------------- +; Copy Bank 1 +;---------------------------------------------------------------------------- + + SET_WINDOW [grains_per_win] + mov edi,0 ;Start at Bank 1, offset 0 + mov ecx,31232 ;# words we'll be setting + +; Get the pixel's offset into the palette + +??Buf1Loop: + xor eax,eax + mov ebx,[palette] + mov al,[BYTE PTR esi] + add ebx,eax + inc esi + add ebx,eax + +; Store the 15-bit palette value + + mov ax,[WORD PTR ebx] + + IF PHARLAP_TNT + mov [WORD PTR es:edi],ax + ELSE + mov [WORD PTR edi],ax + ENDIF + + add edi,2 + dec ecx + jnz ??Buf1Loop + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Buf_320x200_To_VESA_32K + + +;**************************************************************************** +;* +;* NAME +;* Copy_Row - Copy a row of pixels to VRAM. +;* +;* SYNOPSIS +;* Copy_Row(Source, Dest, Length) +;* +;* void Copy_Row(char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to copy to VRAM +;* Dest - Destination VRAM address. +;* Length - Number of bytes to copy. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Copy_Row:NEAR + PROC Copy_Row C NEAR USES ecx esi edi + + ARG source:NEAR PTR + ARG dest:NEAR PTR + ARG numbytes:DWORD + + IF PHARLAP_TNT + push es + mov eax,01Ch + mov es,ax + ENDIF + + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[numbytes] + rep movsb + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Copy_Row + + +;**************************************************************************** +;* +;* NAME +;* Copy_Word_Row - Copy a row of 15-bit pixels to VRAM +;* +;* SYNOPSIS +;* Copy_Word_Row(Source, Dest, Palette, Length) +;* +;* void Copy_Word_Row(char *, char *, char *, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Source - Pointer to data to transfer. +;* Dest - Destination screen address. +;* Palette - 15bit palette to use. +;* Length - Bytes to transfer. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Copy_Word_Row:NEAR + PROC Copy_Word_Row C NEAR USES ebx ecx esi edi + + ARG source:NEAR PTR + ARG dest:NEAR PTR + ARG palette:NEAR PTR + ARG numbytes:DWORD + + IF PHARLAP_TNT + push es + mov eax,01Ch + mov es,ax + ENDIF + + mov esi,[source] + mov edi,[dest] + mov ecx,[numbytes] + +??loop: + mov ebx,[palette] + xor eax,eax + mov al,[esi] ;Get pixel value + shl eax,1 ;Adjust for word entry + add ebx,eax ;Compute color address + mov ax,[ebx] ;Get color + + IF PHARLAP_TNT + mov [es:edi],ax ;Set 16bit pixel + ELSE + mov [edi],ax ;Set 16bit pixel + ENDIF + + inc esi ;Next source pixel + add edi,2 ;Next dest pixel + dec ecx ;Decrement pixel count + jnz ??loop + + IF PHARLAP_TNT + pop es + ENDIF + + ret + + ENDP Copy_Word_Row + + END + diff --git a/WINVQ/VQM32/VESAVID.CPP b/WINVQ/VQM32/VESAVID.CPP new file mode 100644 index 0000000..5c8a4c3 --- /dev/null +++ b/WINVQ/VQM32/VESAVID.CPP @@ -0,0 +1,457 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* vesavid.c +* +* DESCRIPTION +* VESA video manager. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* InitVESA - Initialize the VESA video manager. +* UninitVESA - Uninitialize the VESA video manager. +* SetVESAMode - Set the display to the specified VESA video mode. +* ReadVESAModeInfo - Read the VESA mode information from the video card. +* SetVESAWindow - Set VESA window A's start address. +* +****************************************************************************/ + +#include +#include +#include + +#ifndef __WATCOMC__ +#include +#include +#else +#include "realmode.h" +#endif + +#include "vesavid.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#ifdef __WATCOMC__ +static short _VInfoSel = NULL; +static short _VInfoSeg = NULL; +static short _ModeInfoSel = NULL; +static short _ModeInfoSeg = NULL; + +#else /* __WATCOMC__ */ +/* _regs - Registers used for calling software interrupts. + * _rpVInfo - Real pointer to VInfo structure in conventional memory. + * _rpModeInfo - Real pointer to ModeInfo structure in conventional memory. + * _VInfo - Protected mode copy of VInfo structure. + * _ModeInfo - Protected mode copy of ModeInfo structure. + */ +static SWI_REGS _regs; +static REALPTR _rpVInfo = NULL; +static REALPTR _rpModeInfo = NULL; +static VESAInfo _VInfo; +static VESAModeInfo _ModeInfo; + +#endif /* __WATCOMC__ */ + + +/**************************************************************************** +* +* NAME +* InitVESA - Initialize the VESA video manager. +* +* SYNOPSIS +* Error = InitVESA() +* +* long InitVESA(void); +* +* FUNCTION +* Initialize the VESA video system. Get the VESA information from the +* VESA video bios. +* +* INPUTS +* NONE +* +* RESULT +* Error - 0 if successful, or -1 error/VESA not supported. +* +****************************************************************************/ + +long InitVESA(void) +{ + #ifdef __WATCOMC__ + union REGS r; + struct SREGS sr; + RMInfo rmi; + long error = -1; + + /* Allocate real-mode memory for VESA structure. */ + r.x.eax = 0x0100; + r.x.ebx = (sizeof(VESAInfo) + 15) >> 4; + int386(0x31, &r, &r); + + if (r.x.cflag == 0) { + _VInfoSel = r.w.dx; + _VInfoSeg = r.w.ax; + + /* Allocate real-mode memory for VESAModeInfo structure. */ + r.x.eax = 0x0100; + r.x.ebx = (sizeof(VESAModeInfo) + 15) >> 4; + int386(0x31, &r, &r); + + if (r.x.cflag == 0) { + _ModeInfoSel = r.w.dx; + _ModeInfoSeg = r.w.ax; + + /* Clear VESAInfo structure. */ + memset(MK_PTR(0, _VInfoSeg), 0, sizeof(VESAInfo)); + + /* Get VESA information. */ + memset(&rmi, 0, sizeof(RMInfo)); + rmi.eax = 0x4F00; + rmi.edi = 0; + rmi.es = _VInfoSeg; + + segread(&sr); + r.w.ax = 0x0300; + r.h.bl = 0x10; + r.h.bh = 0; + r.w.cx = 0; + sr.es = FP_SEG(&rmi); + r.x.edi = FP_OFF(&rmi); + int386x(0x31, &r, &r, &sr); + + if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) { + error = 0; + } + } + } + + + if (error != 0) { + UninitVESA(); + } + + return (error); + + #else /* __WATCOMC__ */ + + unsigned short rseg; + long paras; + long error = -1; + + /* Calculate size of VESAInfo structure in paragraphs */ + paras = (sizeof(VESAInfo) + 15) >> 4; + + /* Allocate real-mode memory for VESA structure. */ + if (_dx_real_alloc(paras, (unsigned short *)&rseg, + (unsigned short *)¶s) == 0) { + + RP_SET(_rpVInfo, 0, rseg); + + /* Calculate size of VESAModeInfo structure in paragraphs */ + paras = (sizeof(VESAModeInfo) + 15) >> 4; + + /* Allocate real-mode memory for VESAModeInfo structure. */ + if (_dx_real_alloc(paras, (unsigned short *)&rseg, + (unsigned short *)¶s) == 0) { + + RP_SET(_rpModeInfo, 0, rseg); + + /* Clear the input buffer */ + FillRealMem(_rpVInfo, 0, sizeof(VESAInfo)); + + /* Set up function call */ + _regs.eax = 0x4F00; + _regs.edi = RP_OFF(_rpVInfo); + _regs.es = RP_SEG(_rpVInfo); + _dx_real_int(0x10, &_regs); + + if (_regs.eax == 0x004F) { + ReadRealMem(&_VInfo, _rpVInfo, sizeof(VESAInfo)); + error = 0; + } + } + } + + if (error != 0) { + UninitVESA(); + } + + return (error); + + #endif /* __WATCOMC__ */ +} + + +/**************************************************************************** +* +* NAME +* UninitVESA - Uninitialize the VESA video manager. +* +* SYNOPSIS +* UninitVESA() +* +* void UninitVESA(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void UninitVESA(void) +{ + #ifdef __WATCOMC__ + union REGS r; + + /* Free VESAInfo structure */ + if (_VInfoSeg != NULL) { + r.x.eax = 0x0101; + r.x.edx = _VInfoSel; + int386(0x31, &r, &r); + + _VInfoSeg = NULL; + _VInfoSel = NULL; + } + + /* Free VESAModeInfo structure */ + if (_ModeInfoSeg != NULL) { + r.x.eax = 0x0101; + r.x.edx = _VInfoSel; + int386(0x31, &r, &r); + + _ModeInfoSeg = NULL; + _ModeInfoSel = NULL; + } + + #else /* __WATCOMC__ */ + + /* Free VESAInfo structure */ + if (_rpVInfo != NULL) { + _dx_real_free(RP_SEG(_rpVInfo)); + _rpVInfo = NULL; + } + + /* Free VESAModeInfo structure */ + if (_rpModeInfo != NULL) { + _dx_real_free(RP_SEG(_rpModeInfo)); + _rpModeInfo = NULL; + } + #endif /* __WATCOMC__ */ +} + + +/**************************************************************************** +* +* NAME +* SetVESAMode - Set the display adapter to the given VESA video mode. +* +* SYNOPSIS +* VESAModeInfo = SetVESAMode(Mode) +* +* VESAModeInfo *SetVESAMode(long); +* +* FUNCTION +* Set the display adapter to the specified VESA video mode. +* +* INPUTS +* Mode - VESA video mode to set the display to. +* +* RESULT +* VESAModeInfo - Pointer to VESA mode information structure or NULL if +* error. +* +****************************************************************************/ + +VESAModeInfo *SetVESAMode(long mode) +{ + VESAModeInfo *vminfo; + + /* Get mode info */ + if ((vminfo = ReadVESAModeInfo(mode)) != NULL) { + + /* If the mode is supported, set it. */ + if ((vminfo->Attributes & 0x01) != 0) { + + #ifdef __WATCOMC__ + { + union REGS r; + + r.x.eax = 0x4F02; + r.x.ebx = mode; + int386(0x10, &r, &r); + + if (r.x.eax != 0x004F) + vminfo = NULL; + } + + #else /* __WATCOMC__ */ + + /* Set up function call */ + _regs.eax = 0x4F02; + _regs.ebx = mode; + _dx_real_int(0x10, &_regs); + + if (_regs.eax != 0x004F) { + vminfo = NULL; + } + #endif /* __WATCOMC__ */ + } + } + + return (vminfo); +} + + +/**************************************************************************** +* +* NAME +* ReadVESAModeInfo - Read the VESA mode information from the video card. +* +* SYNOPSIS +* VESAModeInfo = ReadVESAModeInfo(Mode) +* +* VESAModeInfo *ReadVESAModeInfo(long); +* +* FUNCTION +* Read information about the specified mode from the VESA video BIOS. +* +* INPUTS +* Mode - Mode ID to get information about. +* +* RESULT +* VESAModeInfo - Pointer to VESA mode information structure or NULL if +* error. +* +****************************************************************************/ + +VESAModeInfo *ReadVESAModeInfo(long mode) +{ + VESAModeInfo *vminfo = NULL; + + #ifdef __WATCOMC__ + union REGS r; + struct SREGS sr; + RMInfo rmi; + + /* Make sure we have real-mode memory. */ + if (_ModeInfoSeg != NULL) { + memset(MK_PTR(0, _ModeInfoSeg), 0, sizeof(VESAModeInfo)); + + /* Get mode information. */ + memset(&rmi, 0, sizeof(RMInfo)); + rmi.eax = 0x4F01; + rmi.ecx = mode; + rmi.edi = 0; + rmi.es = _ModeInfoSeg; + + segread(&sr); + r.w.ax = 0x0300; + r.w.bx = 0x0010; + r.w.cx = 0; + sr.es = FP_SEG(&rmi); + r.x.edi = FP_OFF(&rmi); + int386x(0x31, &r, &r, &sr); + + if ((r.x.cflag == 0) && (rmi.eax == 0x004F)) { + vminfo = (VESAModeInfo *)MK_PTR(0, _ModeInfoSeg); + } + } + + #else /* __WATCOMC__ */ + + /* Make sure we have real-mode memory. */ + if (_rpModeInfo != NULL) { + + /* Clear the input buffer */ + FillRealMem(_rpModeInfo, 0, sizeof(VESAModeInfo)); + + /* Set up function call */ + _regs.eax = 0x4F01; + _regs.ecx = mode; + _regs.edi = RP_OFF(_rpModeInfo); + _regs.es = RP_SEG(_rpModeInfo); + _dx_real_int(0x10, &_regs); + + if (_regs.eax == 0x004F) { + ReadRealMem(&_ModeInfo, _rpModeInfo, sizeof(VESAModeInfo)); + vminfo = &_ModeInfo; + } + } + #endif /* __WATCOMC__ */ + + return (vminfo); +} + + +/**************************************************************************** +* +* NAME +* SetVESAWindow - Set VESA window A's start address. +* +* SYNOPSIS +* Error = SetVESAWindow(GrainNum) +* +* long SetVESAWindow(long); +* +* FUNCTION +* This function invokes the Window Function, whose address is provided +* in the VESAModeInfo structure. The 'GrainNum' must be in granularity +* units as specified in the ModeInfo structure. +* +* INPUTS +* GrainNum - Granularity number to set window to. +* +* RESULT +* NONE +* +****************************************************************************/ + +void SetVESAWindow(long grain) +{ + #ifdef __WATCOMC__ + #else /* __WATCOMC__ */ + + RMC_BLK regp; + + regp.eax = 0x4F05; + regp.ebx = 0x00; + regp.edx = grain; + _dx_call_real(_ModeInfo.WinFunc, ®p, 0); + #endif /* __WATCOMC__ */ +} + diff --git a/WINVQ/VQM32/VESAVID.H b/WINVQ/VQM32/VESAVID.H new file mode 100644 index 0000000..01723dd --- /dev/null +++ b/WINVQ/VQM32/VESAVID.H @@ -0,0 +1,182 @@ +/* +** 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 . +*/ + +#ifndef VQMVESAVID_H +#define VQMVESAVID_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* VESAVid.h (32-Bit protected mode) +* +* DESCRIPTION +* VESA video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#ifndef __WATCOMC__ +#include +#else +#include "realmode.h" +#endif + +/*--------------------------------------------------------------------------- + * VESA Video Modes + *-------------------------------------------------------------------------*/ + +#define VESA_640X400_256 0x100 +#define VESA_640X480_256 0x101 +#define VESA_800X600_16 0x102 +#define VESA_800X600_256 0x103 +#define VESA_1024X768_16 0x104 +#define VESA_1024X768_256 0x105 +#define VESA_1280X400_16 0x106 +#define VESA_1280X400_256 0x107 +#define VESA_TEXT_80X60 0x108 +#define VESA_TEXT_132X25 0x109 +#define VESA_TEXT_132X60 0x10C +#define VESA_320X200_32K_1 0x10D +#define VESA_320X200_32K_2 0x10E +#define VESA_640X480_32K 0x110 +#define VESA_640X480_65K 0x111 +#define VESA_640X480_16M 0x112 +#define VESA_800X600_32K 0x113 +#define VESA_800X600_65K 0x114 +#define VESA_1024X768_32K 0x116 +#define VESA_1024X768_65K 0x117 + +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_1024X768_65K + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* VESAInfo - General information about this VESA implementation. + * (Filled in by VESA BIOS Function 0) + * + * Signature - Will always be 'VESA' + * Version - Version # + * OEMString - OEM ID string + * Capabilities - Not defined by VESA yet + * AvailModes - List of available modes; terminated with -1 (0xffff) + * TotalMemory - ??? + * Reserved - Pads structure to 256 bytes total + */ +typedef struct _VESAInfo { + char Signature[4]; + unsigned short Version; + REALPTR OEMString; + unsigned long Capabilities; + REALPTR AvailModes; + unsigned short TotalMemory; + unsigned char Reserved[236]; +} VESAInfo; + +/* VESAModeInfo - Information about this VESA mode. + * (Filled in by VESA BIOS Function 1) + * + * Attributes - bit 0: 1 = mode is supported + * bit 1: 1 = optional info available + * bit 2: 1 = std BIOS output functions valid in this mode + * bit 3: 0 = monochrome, 1 = color + * bit 4: 0 = text mode, 1 = graphics + * WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable + * WinGranularity - smallest address boundary window can be placed upon; + * size is in KB (ie 64, 32, 4) + * WinSize - size of windows in KB (ie 64, 32) + * WinA_Segment - location of Window A in CPU space (usually 0xa000) + * WinB_Segment - location of Window B in CPU space (usually 0xb000) + * WinFunc - address of window-setting function (This is provided + * as an alternative to Int 10 for speed.) + * BytesPerScanline - # bytes per scan line + * + * Optional info (available if bit 1 of Attributes is set): + * + * XRes - X-resolution + * YRes - Y-resolution + * XCharSize - Horizontal size of char cell + * YCharSize - Vertical size of char cell + * NumPlanes - # of memory planes (???) + * BitsPerPixel - # bites per pixel + * NumBanks - # of banks (ie planes) + * MemoryModel - 00h = Text mode + * 01h = CGA mode + * 02h = Hercules + * 03h = 4 plane planar mode + * 04h = packed pixel mode (1 byte/pixel) + * 05h = non-chain 4, 256-color mode + * 06-0Fh = + * 10-FFh = OEM-specific + * BankSize - Bank size in KB + */ +typedef struct _VESAModeInfo { + unsigned short Attributes; + unsigned char WinA_Attributes; + unsigned char WinB_Attributes; + unsigned short WinGranularity; + unsigned short WinSize; + unsigned short WinA_Segment; + unsigned short WinB_Segment; + REALPTR WinFunc; + unsigned short BytesPerScanline; + unsigned short XRes; + unsigned short YRes; + unsigned char XCharSize; + unsigned char YCharSize; + unsigned char NumPlanes; + unsigned char BitsPerPixel; + unsigned char NumBanks; + unsigned char MemoryModel; + unsigned char BankSize; + unsigned char NumInputPages; + unsigned char Reserved; + unsigned char RedMaskSize; + unsigned char RedFieldPosition; + unsigned char GreenMaskSize; + unsigned char GreenFieldPosition; + unsigned char BlueMaskSize; + unsigned char BlueFieldPosition; + unsigned char RsvdMaskSize; + unsigned char RsvdFieldPosition; + unsigned char DirectColorModeInfo; + unsigned char pad[216]; +} VESAModeInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +long InitVESA(void); +void UninitVESA(void); +VESAModeInfo *SetVESAMode(long mode); +VESAModeInfo *ReadVESAModeInfo(long mode); +void SetVESAWindow(long grain_num); + +#endif /* VQMVESAVID_H */ + diff --git a/WINVQ/VQM32/VESAVID.I b/WINVQ/VQM32/VESAVID.I new file mode 100644 index 0000000..f305e09 --- /dev/null +++ b/WINVQ/VQM32/VESAVID.I @@ -0,0 +1,203 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* vesavid.i +;* +;* DESCRIPTION +;* VESA video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VESA video modes +;---------------------------------------------------------------------------- + +VESA_640X400_256 EQU 0x100 +VESA_640X480_256 EQU 0x101 +VESA_800X600_16 EQU 0x102 +VESA_800X600_256 EQU 0x103 +VESA_1024X768_16 EQU 0x104 +VESA_1024X768_256 EQU 0x105 +VESA_1280X400_16 EQU 0x106 +VESA_1280X400_256 EQU 0x107 +VESA_TEXT_80X60 EQU 0x108 +VESA_TEXT_132X25 EQU 0x109 +VESA_TEXT_132X60 EQU 0x10C +VESA_320X200_32K_1 EQU 0x10D +VESA_320X200_32K_2 EQU 0x10E +VESA_640X480_32K EQU 0x110 +VESA_640X480_65K EQU 0x111 +VESA_640X480_16M EQU 0x112 +VESA_800X600_32K EQU 0x113 +VESA_800X600_65K EQU 0x114 +VESA_1024X768_32K EQU 0x116 +VESA_1024X768_65K EQU 0x117 + +VESA_MIN EQU VESA_640X400_256 +VESA_MAX EQU VESA_1024X768_65K + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; VESAInfo - General information about this VESA implementation. +; (Filled in by VESA BIOS Function 0) +; +; Signature - Will always be 'VESA' +; Ver - Version # +; OEMString - OEM ID string +; Capabilities - Not defined by VESA yet +; AvailModes - List of available modes; terminated with -1 (0xffff) +; TotalMemory - ??? +; Reserved - Pads structure to 256 bytes total + + STRUC VESAInfo +Signature DD ? +Ver DW ? +OEMString DD ? +Capabilities DD ? +AvailModes DD ? +TotalMemory DW ? +Reserved DB 236 DUP (?) + ENDS VESAInfo + +; VESAModeInfo - Information about this VESA mode. +; (Filled in by VESA BIOS Function 1) +; +; Attributes - bit 0: 1 = mode is supported +; bit 1: 1 = optional info available +; bit 2: 1 = std BIOS output functions valid in this mode +; bit 3: 0 = monochrome, 1 = color +; bit 4: 0 = text mode, 1 = graphics +; WinA_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinB_Attributes - bit 0 = window exists, bit 1=readable, bit 2= writable +; WinGranularity - smallest address boundary window can be placed upon; +; size is in KB (ie 64, 32, 4) +; WinSize - size of windows in KB (ie 64, 32) +; WinA_Segment - location of Window A in CPU space (usually 0xa000) +; WinB_Segment - location of Window B in CPU space (usually 0xb000) +; WinFunc - address of window-setting function (This is provided +; as an alternative to Int 10 for speed.) +; BytesPerScanline - # bytes per scan line +; +; Optional info (available if bit 1 of Attributes is set): +; +; XRes - X-resolution +; YRes - Y-resolution +; XCharSize - Horizontal size of char cell +; YCharSize - Vertical size of char cell +; NumPlanes - # of memory planes (???) +; BitsPerPixel - # bites per pixel +; NumBanks - # of banks (ie planes) +; MemoryModel - 00h = Text mode +; 01h = CGA mode +; 02h = Hercules +; 03h = 4 plane planar mode +; 04h = packed pixel mode (1 byte/pixel) +; 05h = non-chain 4, 256-color mode +; 06-0Fh = +; 10-FFh = OEM-specific +; BankSize - Bank size in KB + + STRUC VESAModeInfo +Attributes DW ? +WinA_Attributes DB ? +WinB_Attributes DB ? +WinGranularity DW ? +WinSize DW ? +WinA_Segment DW ? +WinB_Segment DW ? +WinFunc DD ? +BytesPerScanline DW ? +XRes DW ? +YRes DW ? +XCharSize DB ? +YCharSize DB ? +NumPlanes DB ? +BitsPerPixel DB ? +NumBanks DB ? +MemoryModel DB ? +BankSize DB ? +NumInputPages DB ? +Reserved DB ? +RedMaskSize DB ? +RedFieldPosition DB ? +GreenMaskSize DB ? +GreenFieldPosition DB ? +BlueMaskSize DB ? +BlueFieldPosition DB ? +RsvdMaskSize DB ? +RsvdFieldPosition DB ? +DirectColorModeInfo DB ? +pad DB 216 DUP (?) + ENDS VESAModeInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C InitVESA:PROC + GLOBAL C UninitVESA:PROC + GLOBAL C SetVESAMode:PROC + GLOBAL C ReadVESAModeInfo:PROC + GLOBAL C SetVESAWindow:PROC + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WINDOW - Sets window A to the specified bank. +; +; SYNOPSIS +; SET_WINDOW GrainNum +; +; FUNCTION +; This routine uses the C Set_Window function rather than going through +; the BIOS, for two reasons: (1) Speed, and (2) On the Cirrus Logic 54xx +; VESA cards, BIOS calls make noise while playing digital audio. +; +; INPUTS +; GrainNum - Granularity number. +; +; RESULT +; NONE +; +;---------------------------------------------------------------------------- + + MACRO SET_WINDOW grain_num + push esi + push edi + push es + call SetVESAWindow C,grain_num + pop es + pop edi + pop esi + ENDM + diff --git a/WINVQ/VQM32/VGA.I b/WINVQ/VQM32/VGA.I new file mode 100644 index 0000000..f04d9a0 --- /dev/null +++ b/WINVQ/VQM32/VGA.I @@ -0,0 +1,217 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* vga.i +;* +;* DESCRIPTION +;* VGA hardware definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + +;---------------------------------------------------------------------------- +; VGA Registers +;---------------------------------------------------------------------------- + +R_SEQUENCER EQU 03C4h ;Sequencer Controller Index reg +SEQ_RESET EQU 00h ;Reset +SEQ_MAP_MASK EQU 02h ;Index in Sequencer of Map Mask reg +SEQ_MEMORY_MODE EQU 04h ;Memory Mode + +R_GRAPHICS_CONTROLLER EQU 03CEh ;Graphics Controller Index reg +GC_READ_MAP EQU 04h ;Index in GController of Read Map reg +GC_MODE EQU 05h ;Read/Write Modes +GC_MISC EQU 06h ;Read/Write Modes +GC_BITMASK EQU 08h ;Index in GController of BitMask reg + +R_CRT_CONTROLLER EQU 03D4h ;CRT Controller Index reg +CRT_VERT_TOTAL EQU 06h ;Vertical total +CRT_OVERFLOW EQU 07h ;Overflow +CRT_MAX_SCANLINE EQU 09h ;Max scan line +CRT_STARTADDR_HIGH EQU 0Ch ;Bitmap start address high byte +CRT_STARTADDR_LOW EQU 0Dh ;Bitmap start address low byte +CRT_VERTRET_START EQU 010h ;Vertical retrace pulse start +CRT_VERTRET_END EQU 011h ;Vertical retrace pulse end +CRT_VERTDISP_END EQU 012h ;Vertical display end +CRT_UNDERLINE EQU 014h ;Underline location +CRT_START_VB EQU 015h ;Start vertical blank +CRT_END_VB EQU 016h ;End vertical blank +CRT_MODE_CONTROL EQU 017h ;Mode control +R_MISC_OUTPUT EQU 03C2h ;Miscellaneous Output reg + +;---------------------------------------------------------------------------- +; Palette Registers +;---------------------------------------------------------------------------- + +PEL_READ_ADDR EQU 03C7h +PEL_WRITE_ADDR EQU 03C8h +PEL_DATA EQU 03C9h + +;---------------------------------------------------------------------------- +; XMode planes, for the Map Mask register +;---------------------------------------------------------------------------- + +XPLANE_1 EQU 1 +XPLANE_2 EQU 2 +XPLANE_3 EQU 4 +XPLANE_4 EQU 8 + +;---------------------------------------------------------------------------- +; +; NAME +; SET_PLANE - Set an XMode plane. +; +; SYNOPSIS +; SET_PLANE plane +; +; INPUTS +; plane - Number of Xmode plane to set. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_PLANE plane + mov edx,R_SEQUENCER + mov eax,SEQ_MAP_MASK + out dx,al + inc edx + mov eax,plane + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_BITMASK - Set the BitMask register. +; +; SYNOPSIS +; SET_BITMASK mask +; +; INPUTS +; mask - Bitmask to use. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_BITMASK mask + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_BITMASK + out dx,al + inc edx + mov eax,mask + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; SET_WRITEMODE - Set the VGA writemode. +; +; SYNOPSIS +; SET_WRITEMODE mode +; +; INPUTS +; mode - Write mode. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO SET_WRITEMODE mode + mov edx,R_GRAPHICS_CONTROLLER + mov eax,GC_MODE + out dx,al + inc edx + in al,dx ;Read the register + and al,0FCh ;Turn off 2 lower bits + or al,mode ;Set write mode + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; OUTPORT - Output data to a VGA register. +; +; SYNOPSIS +; OUTPORT port,register,data +; +; INPUTS +; port - Port address. +; register - Register to write to. +; data - Data to write. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO OUTPORT port,register,data + mov edx,port + mov al,register + out dx,al + inc edx + mov al,data + out dx,al + ENDM + +;---------------------------------------------------------------------------- +; +; NAME +; INPORT - Input data from a VGA register. +; +; SYNOPSIS +; data = INPORT port,register +; +; INPUTS +; port - Port address. +; register - Register to read from. +; +; RESULT +; data - Value read from port in AL. +; +; USES +; eax, edx +; +;---------------------------------------------------------------------------- + + MACRO INPORT port,register + mov edx,port + mov al,register + out dx,al + inc edx + in al,dx + ENDM + diff --git a/WINVQ/VQM32/VIDEO.CPP b/WINVQ/VQM32/VIDEO.CPP new file mode 100644 index 0000000..0c5e551 --- /dev/null +++ b/WINVQ/VQM32/VIDEO.CPP @@ -0,0 +1,282 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* video.c +* +* DESCRIPTION +* Video mode setting. (32-Bit protected mode) +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SetVideoMode - Set the video mode. +* GetDisplayInfo - Get the display info for the current video mode. +* GetVBIBit - Get the vertical blank bit polarity. +* +****************************************************************************/ + +#include +#include +#include + +#ifndef __WATCOMC__ +#include +#include +#else +#include "realmode.h" +#endif + +#include "video.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +static DisplayInfo _Display = {-1,0,0,0,NULL}; + + +/**************************************************************************** +* +* NAME +* SetVideoMode - Set the display to the specified video mode. +* +* SYNOPSIS +* DisplayInfo = SetVideoMode(Mode) +* +* DisplayInfo *SetVideoMode(long); +* +* FUNCTION +* Set the video display adapter to the desired video mode. +* +* INPUTS +* Mode - Desired video mode. +* +* RESULT +* DisplayInfo - Pointer to DisplayInfo structure, otherwise 0 for error. +* +****************************************************************************/ + +DisplayInfo *SetVideoMode(long mode) +{ + #ifdef __WATCOMC__ + union REGS regs; + struct SREGS sregs; + #else + union _REGS regs; + #endif + + DisplayInfo *di = NULL; + VESAModeInfo *vminfo; + long error; + + /* Initialize the video manager on the first invocation of + * SetVideoMode() + */ + if (_Display.Mode == -1) { + _Display.VBIbit = TestVBIBit(); + } + + /* Clear the VRAM before enabling the mode so that there is + * not any garbage on the screen. + */ + ClearVRAM(); + + /* If the requested mode is the same as the current mode then + * we do not need to do anything. + */ + if (mode != _Display.Mode) { + + /* Uninitialize VESA if the previous mode was a VESA mode and the new + * mode is not. + */ + if (((_Display.Mode >= VESA_MIN) && (_Display.Mode <= VESA_MAX)) + && ((mode < VESA_MIN) && (mode > VESA_MAX))) { + + UninitVESA(); + } + + /* Set display to an XMode. */ + if ((mode >= XMODE_MIN) && (mode <= XMODE_MAX)) { + SetXMode(mode); + ClearXMode(); + SetupXPaging(); + ShowXPage(0); + _Display.Mode = mode; + _Display.Extended = NULL; + di = &_Display; + + /* Set display resolution information */ + switch (mode) { + case XMODE_320X200: + _Display.XRes = 320; + _Display.YRes = 200; + break; + + case XMODE_320X240: + _Display.XRes = 320; + _Display.YRes = 240; + break; + + case XMODE_320X400: + _Display.XRes = 320; + _Display.YRes = 400; + break; + + case XMODE_320X480: + _Display.XRes = 320; + _Display.YRes = 480; + break; + + case XMODE_360X400: + _Display.XRes = 360; + _Display.YRes = 400; + break; + + case XMODE_360X480: + _Display.XRes = 360; + _Display.YRes = 480; + break; + } + } + else if ((mode >= VESA_MIN) && (mode <= VESA_MAX)) { + + /* Initialize the VESA manager if the current mode is not a VESA + * mode. + */ + if ((_Display.Mode < VESA_MIN) || (_Display.Mode > VESA_MAX)) { + error = InitVESA(); + } + + if (!error) { + + /* Set the display to MCGA before going into VESA. This needs to be + * done to ensure that the video ram selector is initialized. This + * fixes a bug in some VESA BIOS'. + */ + #ifndef __WATCOMC__ + regs.x.ax = mode; + _int86(0x10, ®s, ®s); + #else + segread(&sregs); + regs.x.eax = mode; + int386x(0x10, ®s, ®s, &sregs); + #endif + + if ((vminfo = SetVESAMode(mode)) != NULL) { + _Display.Mode = mode; + _Display.XRes = (long)vminfo->XRes; + _Display.YRes = (long)vminfo->YRes; + _Display.Extended = vminfo; + di = &_Display; + } + } + } + else { + #ifndef __WATCOMC__ + regs.x.ax = mode; + _int86(0x10, ®s, ®s); + #else + segread(&sregs); + regs.x.eax = mode; + int386x(0x10, ®s, ®s, &sregs); + #endif + + _Display.Mode = mode; + _Display.XRes = 320; + _Display.YRes = 200; + _Display.Extended = NULL; + di = &_Display; + } + } else { + di = &_Display; + } + + return (di); +} + + +/**************************************************************************** +* +* NAME +* GetDisplayInfo - Get the display info for the current video mode. +* +* SYNOPSIS +* DisplayInfo = GetDisplayInfo() +* +* DisplayInfo *GetDisplayInfo(void); +* +* FUNCTION +* Return a pointer to the current display information structure. +* +* INPUTS +* NONE +* +* RESULT +* DisplayInfo - Pointer to initialized display info or NULL if not valid. +* +****************************************************************************/ + +DisplayInfo *GetDisplayInfo(void) +{ + if (_Display.Mode != 0) { + return (&_Display); + } else { + return (NULL); + } +} + + +/**************************************************************************** +* +* NAME +* GetVBIBit - Get the vertical blank bit polarity. +* +* SYNOPSIS +* VBIBit = GetVBIBit() +* +* long GetVBIBit(void); +* +* FUNCTION +* Return the polarity of the vertical blank bit. +* +* INPUTS +* NONE +* +* RESULT +* VBIBit - Vertical blank bit polarity. +* +****************************************************************************/ + +long GetVBIBit(void) +{ + return (_Display.VBIbit); +} + diff --git a/WINVQ/VQM32/VIDEO.H b/WINVQ/VQM32/VIDEO.H new file mode 100644 index 0000000..326e25c --- /dev/null +++ b/WINVQ/VQM32/VIDEO.H @@ -0,0 +1,115 @@ +/* +** 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 . +*/ + +#ifndef VQMVIDEO_H +#define VQMVIDEO_H +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* Video.h (32-Bit protected mode) +* +* DESCRIPTION +* Video manager definitions. +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* Febuary 3, 1995 +* +****************************************************************************/ + +#include + +/*--------------------------------------------------------------------------- + * VGA video modes + *-------------------------------------------------------------------------*/ + +#define TEXT_VIDEO 0x02 +#define MCGA 0x13 +#define XMODE_320X200 0x50 +#define XMODE_320X240 0x51 +#define XMODE_320X400 0x52 +#define XMODE_320X480 0x53 +#define XMODE_360X400 0x54 +#define XMODE_360X480 0x55 + +#define XMODE_MIN 0x50 +#define XMODE_MAX 0x55 + +/*--------------------------------------------------------------------------- + * Structure definitions + *-------------------------------------------------------------------------*/ + +/* DisplayInfo - Information about the current display. + * + * Mode - Mode identification. + * XRes - X resolution of mode. + * YRes - Y resolution of mode. + * VBIbit - Polarity of vertical blank bit. + * Extended - Pointer to mode specific data structure. + */ +typedef struct _DisplayInfo { + long Mode; + long XRes; + long YRes; + long VBIbit; + void *Extended; +} DisplayInfo; + +/*--------------------------------------------------------------------------- + * Function prototypes + *-------------------------------------------------------------------------*/ + +DisplayInfo *SetVideoMode(long mode); +DisplayInfo *GetDisplayInfo(void); +long TestVBIBit(void); +long GetVBIBit(void); + +void SetupXPaging(void); +void FlipXPage(void); +unsigned char *GetXHidPage(void); +unsigned char *GetXSeenPage(void); +void DisplayXPage(long page); + +#ifdef __cplusplus +extern "C" { +#endif + +void __cdecl WaitNoVB(short vbibit); +void __cdecl WaitVB(short vbibit); +void __cdecl ClearVRAM(void); +long __cdecl SetXMode(long mode); +void __cdecl ClearXMode(void); +void __cdecl ShowXPage(unsigned long StartOffset); +void __cdecl Xmode_BufferCopy_320x200(void *buff, void *screen); +void __cdecl Xmode_Blit(void *buffer, void *screen, long imgwidth, long imgheight); +void __cdecl MCGA_BufferCopy(unsigned char *buffer, unsigned char *dummy); +void __cdecl MCGA_Blit(unsigned char *buffer, unsigned char *screen, + long imgwidth, long imgheight); + +#ifdef __cplusplus +} +#endif + +#endif /* VQMVIDEO_H */ + diff --git a/WINVQ/VQM32/VIDEO.I b/WINVQ/VQM32/VIDEO.I new file mode 100644 index 0000000..d2fc384 --- /dev/null +++ b/WINVQ/VQM32/VIDEO.I @@ -0,0 +1,80 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*---------------------------------------------------------------------------- +;* +;* FILE +;* video.i +;* +;* DESCRIPTION +;* Video manager definitions. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Denzil E. Long, Jr. +;* +;* DATE +;* January 26, 1995 +;* +;**************************************************************************** + + INCLUDE "vesavid.i" + +;---------------------------------------------------------------------------- +; Video Modes +;---------------------------------------------------------------------------- + +TEXT EQU 002h +MCGA EQU 013h +XMODE_320X200 EQU 050h +XMODE_320X240 EQU 051h +XMODE_320X400 EQU 052h +XMODE_320X480 EQU 053h +XMODE_360X400 EQU 054h +XMODE_360X480 EQU 055h + +;---------------------------------------------------------------------------- +; Structure definitions +;---------------------------------------------------------------------------- + +; DisplayInfo - Information about the current display. +; +; Mode - Mode identification +; XRes - X resolution +; YRes - Y resolution +; VBIbit - Polarity of vertical blank bit. +; Extended - Pointer to mode specified data structure. + + STRUC DisplayInfo +Mode DD ? +XRes DD ? +YRes DD ? +VBIbit DD ? +Extended DD ? + ENDS DisplayInfo + +;---------------------------------------------------------------------------- +; Function definitions +;---------------------------------------------------------------------------- + + GLOBAL C GetDisplayInfo:NEAR + GLOBAL C GetVBIBit:NEAR + diff --git a/WINVQ/VQM32/XMODE.ASM b/WINVQ/VQM32/XMODE.ASM new file mode 100644 index 0000000..cec1fb7 --- /dev/null +++ b/WINVQ/VQM32/XMODE.ASM @@ -0,0 +1,748 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 +;* +;*--------------------------------------------------------------------------- +;* +;* FILE +;* xmode.asm +;* +;* DESCRIPTION +;* Xmode graphics display routines. (32-Bit protected mode) +;* +;* PROGRAMMER +;* Bill Randolph +;* Denzil E. Long, Jr. +;* +;* DATE +;* Febuary 3, 1995 +;* +;*--------------------------------------------------------------------------- +;* +;* PUBLIC +;* SetXMode - Set the specified Xmode video mode. +;* ClearXMode - Clear the XMode VRAM. +;* ShowXPage - Set a specific page for XMode display. +;* Xmode_BufferCopy_320x200 - Copy 320x200 buffer to Xmode VRAM. +;* Xmode_Blit - Bit blit a block to the XMode display. +;* +;**************************************************************************** + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + INCLUDE "vga.i" + INCLUDE "video.i" + CODESEG + +;**************************************************************************** +;* +;* NAME +;* SetXMode - Set the specified Xmode video mode. +;* +;* SYNOPSIS +;* Error = SetXMode(Mode) +;* +;* long SetXMode(long); +;* +;* FUNCTION +;* This routines set the current display adapter to the specified Xmode. +;* Portions of this routine were take from Dr. Dobb's, written in C, and +;* portions were taken from Dominic's 320x200 code. +;* +;* INPUTS +;* Mode - Xmode mode to set display to. +;* +;* RESULT +;* Error - 0 if successful, or -1 if error. +;* +;**************************************************************************** + + GLOBAL C SetXMode:NEAR + PROC SetXMode C NEAR USES edx + + ARG mode:DWORD + +??Set320x200: + cmp [mode],XMODE_320X200 ;320x200? + jne ??Set320x240 + + IF 0 + mov eax,MCGA + int 10h + +; Memory Mode: +; bit3 (chain) = 0 (planes are accessed via Map Mask) +; bit2 (odd/even) = 1 (use sequential addressing mode) + + INPORT R_SEQUENCER,SEQ_MEMORY_MODE + and al,not 08h ;Turn off chain 4 + or al,04h ;Turn off odd/even + out dx,al + + INPORT R_GRAPHICS_CONTROLLER,GC_MODE + and al,not 10h ;Turn off odd/even + out dx,al + + INPORT R_GRAPHICS_CONTROLLER,GC_MISC + and al,not 02h ;Turn off chain + out dx,al + + OUTPORT R_SEQUENCER,SEQ_MAP_MASK,0Fh + INPORT R_CRT_CONTROLLER, CRT_MAX_SCANLINE + and al,not 1fh ;Clear low 5 bits + or al,1 ;Mode = 0 => 400 lines + out dx,al ;Mode =1 => 200 + + INPORT R_CRT_CONTROLLER,CRT_UNDERLINE + and al,not 40h ;Turn off doubleword + out dx,al + + INPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL + or al,40h ;Turn on byte mode bit, + out dx,al ; so mem scanned linearly + ENDIF + +; The following section of code is from Roger Stevens' XMode +; example code; it's the same as 320x400, except the value sent +; to CRT_MAX_SCANLINE is 41, not 40. + + mov eax,MCGA + int 10h + + OUTPORT R_SEQUENCER,SEQ_MEMORY_MODE,06h + INPORT R_CRT_CONTROLLER,CRT_VERTRET_END + and al,07Fh + out dx,al + + OUTPORT R_CRT_CONTROLLER,CRT_MAX_SCANLINE,41h + OUTPORT R_CRT_CONTROLLER,CRT_UNDERLINE,00h + OUTPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL,0E3h + + mov eax,0 + jmp ??Done + +??Set320x240: + cmp [mode],XMODE_320X240 ;320x240? + jne ??Set320x400 + +; Start by setting MCGA to let the BIOS program the registers; +; then, reprogram the registers that need it. + + mov eax,MCGA + int 10h + +; Memory Mode: +; bit3 (chain) = 0 (planes are accessed via Map Mask) +; bit2 (odd/even) = 1 (use sequential addressing mode) +; bit1 (extended mem) = 1 (>64K video RAM) +; bit0 (alpha/graph) = 0 (graphics mode) + + OUTPORT R_SEQUENCER,SEQ_MEMORY_MODE,06h + +; Issue a Sequencer Reset + OUTPORT R_SEQUENCER,SEQ_RESET,01h + +; Misc Output: (set to 1100 0011) +; Bit 7: VSync polarity (1=negative) +; Bit 6: HSync polarity (1=negative) +; Bit 5: page bit for odd/even (0=low 64K) +; Bit 4: Video drivers (0=enable) +; Bit 3,2: clock select (0=25-MHz clock) +; Bit 1: VRAM access (1 = enable CPU to access) +; Bit 0: I/O Address (1=color emulation) + + mov edx,R_MISC_OUTPUT + mov al,0C3h + out dx,al + +; Clear Sequencer Reset + + OUTPORT R_SEQUENCER,SEQ_RESET,03h + +; Read Vertical Retrace End, and with 07f to clear high bit +; (clearing bit 7 enables writing to registers 0-7) + + INPORT R_CRT_CONTROLLER,CRT_VERTRET_END + and al,07Fh + out dx,al + +; Program the CRT Controller to display 480 scanlines, but to +; double each scanline so only 240 are displayed: + + OUTPORT R_CRT_CONTROLLER,CRT_UNDERLINE,00h + OUTPORT R_CRT_CONTROLLER,CRT_MODE_CONTROL,0E3h + OUTPORT R_CRT_CONTROLLER,CRT_VERT_TOTAL,0Dh + OUTPORT R_CRT_CONTROLLER,CRT_OVERFLOW,03Eh + OUTPORT R_CRT_CONTROLLER,CRT_VERTRET_START,0EAh + OUTPORT R_CRT_CONTROLLER,CRT_VERTRET_END,0ACh + OUTPORT R_CRT_CONTROLLER,CRT_VERTDISP_END,0DFh + OUTPORT R_CRT_CONTROLLER,CRT_START_VB,0E7h + OUTPORT R_CRT_CONTROLLER,CRT_END_VB,06h + OUTPORT R_CRT_CONTROLLER,CRT_MAX_SCANLINE,041h + + xor eax,eax + jmp ??Done + +??Set320x400: + cmp [mode],XMODE_320X400 ;320x400 + jne ??Set320x480 + + mov eax,MCGA + int 10h + + OUTPORT R_SEQUENCER,04h,06h + INPORT R_CRT_CONTROLLER,011h + and al,07Fh + out dx,al + + OUTPORT R_CRT_CONTROLLER,09h,40h + OUTPORT R_CRT_CONTROLLER,014h,00h + OUTPORT R_CRT_CONTROLLER,017h,0E3h + + xor eax,eax + jmp ??Done + +??Set320x480: + cmp [mode],XMODE_320X480 ;320x480? + jne ??Set360x400 + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,0C3h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,04009h + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,00D06h + out dx,ax + mov eax,03E07h + out dx,ax + mov eax,0EA10h + out dx,ax + mov eax,0AC11h + out dx,ax + mov eax,0DF12h + out dx,ax + mov eax,0E715h + out dx,ax + mov eax,00616h + out dx,ax + mov eax,04009h + out dx,ax + + xor eax,eax + jmp ??Done + +??Set360x400: + cmp [mode],XMODE_360X400 ;360x400 + jne ??Set360x480 + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,067h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,06B00h + out dx,ax + mov eax,05901h + out dx,ax + mov eax,05A02h + out dx,ax + mov eax,08E03h + out dx,ax + mov eax,05E04h + out dx,ax + mov eax,08A05h + out dx,ax + mov eax,04009 + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,02D13h + out dx,ax + + xor eax,eax + jmp ??Done + +??Set360x480: + cmp [mode],XMODE_360X480 ;360x480? + jne ??Unknown_mode + + mov eax,MCGA + int 10h + + mov edx,R_SEQUENCER + mov eax,0604h + out dx,ax + + mov eax,0100h + out dx,ax + + mov edx,R_MISC_OUTPUT + mov al,0E7h + out dx,al + + mov edx,R_SEQUENCER + mov eax,0300h + out dx,ax + + mov edx,R_CRT_CONTROLLER + mov al,011h + out dx,al + + mov edx,03D5h + in al,dx + and al,07Fh + out dx,al + + mov edx,R_CRT_CONTROLLER + mov eax,06B00h + out dx,ax + mov eax,05901h + out dx,ax + mov eax,05A02h + out dx,ax + mov eax,08E03h + out dx,ax + mov eax,05E04h + out dx,ax + mov eax,08A05h + out dx,ax + mov eax,04009h + out dx,ax + mov eax,00014h + out dx,ax + mov eax,0E317h + out dx,ax + mov eax,00D06h + out dx,ax + mov eax,03E07h + out dx,ax + mov eax,0EA10h + out dx,ax + mov eax,0AC11h + out dx,ax + mov eax,0DF12h + out dx,ax + mov eax,0E715h + out dx,ax + mov eax,00616h + out dx,ax + mov eax,02D13h + out dx,ax + + xor eax,eax + jmp ??Done + +??Unknown_mode: + mov eax,0FFFFFFFFh ;Unknown mode + +??Done: + ret + + ENDP SetXMode + + +;**************************************************************************** +;* +;* NAME +;* ClearXMode - Clear the XMode VRAM. +;* +;* SYNOPSIS +;* ClearXMode() +;* +;* void ClearXMode(void); +;* +;* FUNCTION +;* +;* INPUTS +;* NONE +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ClearXMode:NEAR + PROC ClearXMode C NEAR USES eax ecx edi es + + IF PHARLAP_TNT + mov eax,01Ch + mov es,ax ;Set ES selector to VRAM + mov edi,0 + ELSE + mov edi,0A0000h + ENDIF + SET_PLANE 0Fh + mov ecx,((320*240*2)/4/4) + xor eax,eax + rep stosd + ret + + ENDP ClearXMode + + +;**************************************************************************** +;* +;* NAME +;* ShowXPage - Set a specific page for XMode display. +;* +;* SYNOPSIS +;* ShowXPage(Offset) +;* +;* void ShowXPage(); +;* +;* FUNCTION +;* Show the page at the specified offset in the bitmap. Page-flip takes +;* effect on the next active scan cycle. +;* +;* INPUTS +;* Offset - Offset to set page to. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C ShowXPage:NEAR + PROC ShowXPage C NEAR USES eax ebx ecx edx + + ARG StartOffset:DWORD + + mov edx,R_CRT_CONTROLLER + mov bl,CRT_STARTADDR_LOW + mov bh,[byte ptr StartOffset] + mov cl,CRT_STARTADDR_HIGH + mov ch,[byte ptr StartOffset+1] + mov eax,ebx + out dx,ax + mov eax,ecx + out dx,ax + ret + + ENDP ShowXPage + + +;**************************************************************************** +;* +;* NAME +;* Xmode_BufferCopy_320x200 - Copy 320x200 buffer to Xmode VRAM. +;* +;* SYNOPSIS +;* Xmode_BufferCopy_320x200(Buffer, Screen) +;* +;* void Xmode_BufferCopy_320x200(char *, char *); +;* +;* FUNCTION +;* BitBlt copy to VRAM. +;* +;* INPUTS +;* Buffer - Pointer to buffer to copy to XMode VRAM. +;* Screen - XMode VRAM screen address to copy buffer to. +;* +;* RESULT +;* NONE +;* +;**************************************************************************** + + GLOBAL C Xmode_BufferCopy_320x200:NEAR + PROC Xmode_BufferCopy_320x200 C NEAR USES eax ecx edx edi esi es + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + + LOCAL save_esi:DWORD + LOCAL save_edi:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to VRAM. + mov es,ax + ENDIF + + mov esi,[buffer] ;Set pointers + mov edi,[screen] + mov [save_esi],esi + mov [save_edi],edi + +;---------------------------------------------------------------------------- +; Copy plane 1 +;---------------------------------------------------------------------------- + + SET_PLANE XPLANE_1 + mov ecx,4000 + +x_loop_1: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 1 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_1 + +;---------------------------------------------------------------------------- +; Copy plane 2 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + inc esi ;Adjust source pointer to plane 2 + + SET_PLANE XPLANE_2 + mov ecx,4000 + +x_loop_2: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 2 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_2 + +;---------------------------------------------------------------------------- +; Copy plane 3 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + add esi,2 ;Adjust source pointer to plane 3 + + SET_PLANE XPLANE_3 + mov ecx,4000 + +x_loop_3: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 3 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next VRAM position + dec ecx + jnz short x_loop_3 + +;---------------------------------------------------------------------------- +; Copy plane 4 +;---------------------------------------------------------------------------- + + mov esi,[save_esi] ;Restore pointers + mov edi,[save_edi] + add esi,3 ;Adjust source pointer to plane 4 + + SET_PLANE XPLANE_4 + mov ecx,4000 +x_loop_4: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write plane 4 pixels to VRAM + + add esi,16 ;Next source pixel position + add edi,4 ;Next screen position + dec ecx + jnz short x_loop_4 + ret + + ENDP Xmode_BufferCopy_320x200 + + +;**************************************************************************** +;* +;* NAME +;* Xmode_Blit - Bit blit a block to the XMode display. +;* +;* SYNOPSIS +;* XMode_Blit(Buffer, Screen, Width, Height) +;* +;* void XMode_Blit(char *, char *, long, long); +;* +;* FUNCTION +;* +;* INPUTS +;* Buffer - Pointer buffer to blit to screen. +;* Screen - Screen address to blit buffer to. +;* Width - Width of buffer. +;* Height - Height of buffer. +;* +;* RESULT +;* NONE +;* +;* WARNINGS +;* Assumes the screen to be 320 pixels wide and the source buffer width +;* to be divisible by 16. +;* +;**************************************************************************** + + GLOBAL C Xmode_Blit:NEAR + PROC Xmode_Blit C NEAR USES ecx edx esi edi es + + ARG buffer:NEAR PTR + ARG screen:NEAR PTR + ARG imgwidth:DWORD + ARG imgheight:DWORD + + LOCAL rowcount:DWORD + LOCAL xplane:DWORD + LOCAL edi_startval:DWORD + LOCAL esi_startval:DWORD + LOCAL xadd:DWORD + +;---------------------------------------------------------------------------- +; Initialize +;---------------------------------------------------------------------------- + + IF PHARLAP_TNT + mov eax,01Ch ;Set ES selector to VRAM + mov es,ax + ENDIF + + mov esi,[buffer] + mov edi,[screen] + mov [esi_startval],esi + mov [edi_startval],edi + + mov edx,320 ;Compute modulo + sub edx,[imgwidth] + shr edx,2 + mov [xadd],edx + +;---------------------------------------------------------------------------- +; Transfer the data on plane at a time. +;---------------------------------------------------------------------------- + + mov [xplane],1 + +??Do_plane: + SET_PLANE [xplane] ;Set plane to transfer to + mov eax,[imgheight] + mov [rowcount],eax + mov edx,[xadd] + +??Do_row: + mov ecx,[imgwidth] ;Length of row to copy in DWORDS + shr ecx,4 + +; Transfer a row of pixels + +??Not_done: + mov al,[esi + 8] ;Get pixel + mov ah,[esi + 12] ;Get pixel + ror eax,16 + mov al,[esi] ;Get pixel + mov ah,[esi + 4] ;Get pixel + mov [es:edi],eax ;Write pixels to VRAM plane + + add esi,16 ;Next source position + add edi,4 ;Next VRAM position + dec ecx + jnz ??Not_done + + add edi,edx ;Next VRAM row + dec [rowcount] ;Decrement the row count + jnz ??Do_row + +; Go to next X-Plane + + inc [esi_startval] + mov eax,[esi_startval] + mov esi,eax + mov eax,[edi_startval] + mov edi,eax + shl [xplane],1 + cmp [xplane],16 + jnz ??Do_plane + ret + + ENDP Xmode_Blit + + END + diff --git a/WINVQ/VQM32/XMODEPG.CPP b/WINVQ/VQM32/XMODEPG.CPP new file mode 100644 index 0000000..b2a8604 --- /dev/null +++ b/WINVQ/VQM32/XMODEPG.CPP @@ -0,0 +1,201 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* 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 +* +*---------------------------------------------------------------------------- +* +* FILE +* xmodepg.c +* +* DESCRIPTION +* Xmode page access. (32-Bit protected mode) +* +* PROGRAMMER +* Bill Randolph +* Denzil E. Long, Jr. +* +* DATE +* January 26, 1995 +* +*---------------------------------------------------------------------------- +* +* PUBLIC +* SetupXPaging - Setup Xmode paging variables. +* FlipXPage - Page flip to next Xmode page. +* ShowXPage - Show the specified Xmode page. +* GetXHidPage - Get the address of the current Xmode HidPage. +* GetXSeenPage - Get the address of the current Xmode SeenPage. +* +****************************************************************************/ + +#include +#include "video.h" + +/*--------------------------------------------------------------------------- + * PRIVATE DECLARATIONS + *-------------------------------------------------------------------------*/ + +#define PAGE0_START_OFFSET 0 +#define PAGE1_START_OFFSET ((320 * 240) / 4) + +/* PageFlip page values. */ +static unsigned long PageStartOffsets[2] = { + PAGE0_START_OFFSET, + PAGE1_START_OFFSET +}; + +static long DisplayedPage; +static long NonDisplayedPage; + +/**************************************************************************** +* +* NAME +* SetupXPaging - Setup Xmode paging variables. +* +* SYNOPSIS +* SetupXPaging() +* +* void SetupXPaging(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void SetupXPaging(void) +{ + DisplayedPage = 1; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* FlipXPage - Page flip to next Xmode page. +* +* SYNOPSIS +* FlipXPage() +* +* void FlipXPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void FlipXPage(void) +{ + ShowXPage(PageStartOffsets[NonDisplayedPage]); + DisplayedPage = NonDisplayedPage; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* ShowXPage - Show the specified Xmode page. +* +* SYNOPSIS +* ShowXPage(page) +* +* void ShowXPage(long); +* +* FUNCTION +* +* INPUTS +* Page - Xmode page number to show. +* +* RESULT +* NONE +* +****************************************************************************/ + +void DisplayXPage(long page) +{ + ShowXPage(PageStartOffsets[page & 1]); + DisplayedPage = page; + NonDisplayedPage = DisplayedPage ^ 1; +} + + +/**************************************************************************** +* +* NAME +* GetXHidPage - Get the address of the current Xmode HidPage. +* +* SYNOPSIS +* HidPage = GetXHidPage() +* +* unsigned char *GetXHidPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* HidPage - Address of Xmode HidPage. +* +****************************************************************************/ + +unsigned char *GetXHidPage(void) +{ + return((unsigned char *)PageStartOffsets[NonDisplayedPage]); +} + + +/**************************************************************************** +* +* NAME +* GetXSeenPage - Get the address of the current Xmode SeenPage. +* +* SYNOPSIS +* SeenPage = GetXSeenPage() +* +* unsigned char *GetXSeenPage(void); +* +* FUNCTION +* +* INPUTS +* NONE +* +* RESULT +* SeePage - Address of the Xmode SeenPage. +* +****************************************************************************/ + +unsigned char *GetXSeenPage(void) +{ + return ((unsigned char *)PageStartOffsets[DisplayedPage]); +} + diff --git a/WWFLAT32/AUDIO/AUDIO.BAK b/WWFLAT32/AUDIO/AUDIO.BAK new file mode 100644 index 0000000..b6134ca --- /dev/null +++ b/WWFLAT32/AUDIO/AUDIO.BAK @@ -0,0 +1,152 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + +typedef enum RateEnum { + PLAYBACK_RATE_SLOW =11025, + PLAYBACK_RATE_NORMAL =(11025 * 2), + PLAYBACK_RATE_FAST =(11025 * 4), +} RateType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void cdecl _saveregs Sound_Callback(void); +void cdecl far __saveregs __loadds maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels); +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels = FALSE); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; diff --git a/WWFLAT32/AUDIO/AUDIO.H b/WWFLAT32/AUDIO/AUDIO.H new file mode 100644 index 0000000..bef70ab --- /dev/null +++ b/WWFLAT32/AUDIO/AUDIO.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + +typedef enum RateEnum { + PLAYBACK_RATE_SLOW =11025, + PLAYBACK_RATE_NORMAL =(11025 * 2), + PLAYBACK_RATE_FAST =(11025 * 4), +} RateType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void cdecl _saveregs Sound_Callback(void); +void cdecl far __saveregs __loadds maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels); +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels = FALSE); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; +extern short StreamLowImpact; diff --git a/WWFLAT32/AUDIO/AUDUNCMP.ASM b/WWFLAT32/AUDIO/AUDUNCMP.ASM new file mode 100644 index 0000000..b4810f1 --- /dev/null +++ b/WWFLAT32/AUDIO/AUDUNCMP.ASM @@ -0,0 +1,437 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Audio Library * +;* * +;* File Name : AUDUNCMP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : March 14, 1995 * +;* * +;* Last Update : June 26, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Decompress_Frame_Lock -- locks the JLB audio decompression code * +;* Decompress_Frame_Unlock -- Unlocks the JLB audio compression code * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +DPMI_INTR equ 31h + +LABEL LockedCodeStart BYTE + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + + +;*************************************************************************** +;* DECOMPRESS_FRAME -- Uncompresses a WW compressed audio frame * +;* * +;* INPUT: void * source - pointer to encoded audio data * +;* void * dest - pointer to decompression area * +;* long size - the maximum size of destination buffer * +;* * +;* OUTPUT: long - the number of bytes we uncompressed * +;* * +;* PROTO: long Decompress_Frame(void *, void *, long); * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL Decompress_Frame:NEAR + PROC Decompress_Frame C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + pushf + cld + mov [incount],0 ;Bytes read from source + + +; Source, Dest and count must be valid. + + cmp [source],0 + je ??fini + + cmp [dest],0 + je ??fini + + cmp [count],0 + je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popf + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popf + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popf + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popf + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popf + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popf + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popf + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popf + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popf + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popf + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popf + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popf + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + popf + mov eax,[incount] + ret + + ENDP Decompress_Frame + +LABEL LockedCodeEnd BYTE + + +;*************************************************************************** +;* DECOMPRESS_FRAME_LOCK -- locks the JLB audio decompression code * +;* * +;* INPUT: none * +;* * +;* OUTPUT: BOOL true is lock sucessful, false otherwise * +;* * +;* PROTO: BOOL Decompress_Frame_Lock(void); * +;* * +;* HISTORY: * +;* 06/26/1995 PWG : Created. * +;*=========================================================================* + GLOBAL Decompress_Frame_Lock:NEAR + PROC Decompress_Frame_Lock C NEAR USES ebx ecx edx esi edi + + 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 + mov eax,1 + jmp ??exit +??error: + xor eax,eax +??exit: + ret + + ENDP Decompress_Frame_Lock + +;*************************************************************************** +;* DECOMPRESS_FRAME_UNLOCK -- Unlocks the JLB audio compression code * +;* * +;* INPUT: none * +;* * +;* OUTPUT: BOOL true is unlock sucessful, false otherwise * +;* * +;* PROTO: BOOL Decompress_Frame_Unlock(void); * +;* * +;* HISTORY: * +;* 06/26/1995 PWG : Created. * +;*=========================================================================* + GLOBAL Decompress_Frame_Unlock:NEAR + PROC Decompress_Frame_Unlock C NEAR USES ebx ecx edx esi edi + + 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 + mov eax,1 + jmp ??exit +??error: + xor eax,eax +??exit: + ret + + ENDP Decompress_Frame_Unlock + + + + END diff --git a/WWFLAT32/AUDIO/DIFFTB.INC b/WWFLAT32/AUDIO/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WWFLAT32/AUDIO/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WWFLAT32/AUDIO/FUNCTION.H b/WWFLAT32/AUDIO/FUNCTION.H new file mode 100644 index 0000000..7edf5f2 --- /dev/null +++ b/WWFLAT32/AUDIO/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int cdecl File_Stream_Sample(char const *filename); +int cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void cdecl _saveregs _loadds Sound_Callback(void); +void cdecl far _saveregs _loadds maintenance_callback(void); +void *cdecl Load_Sample(char const *filename); +long cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long cdecl Sample_Read(int fh, void *buffer, long size); +void cdecl Free_Sample(void const *sample); +BOOL cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void cdecl Sound_End(void); +void cdecl Stop_Sample(int handle); +BOOL cdecl Sample_Status(int handle); +BOOL cdecl Is_Sample_Playing(void const * sample); +void cdecl Stop_Sample_Playing(void const * sample); +int cdecl Play_Sample(void const *sample); +int cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int cdecl Set_Sound_Vol(int volume); +int cdecl Set_Score_Vol(int volume); +void cdecl Fade_Sample(int handle, int ticks); diff --git a/WWFLAT32/AUDIO/INDEXTB.INC b/WWFLAT32/AUDIO/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WWFLAT32/AUDIO/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WWFLAT32/AUDIO/MAKEFILE b/WWFLAT32/AUDIO/MAKEFILE new file mode 100644 index 0000000..cbf7aa6 --- /dev/null +++ b/WWFLAT32/AUDIO/MAKEFILE @@ -0,0 +1,191 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = AUDIO +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + soundio.obj & + soundint.obj & + soundlck.obj & + soscodec.obj & + auduncmp.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.lib $(%WWFLAT)\lib + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + copy *.lib $(%WWFLAT)\lib + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +soundio.obj: .AUTODEPEND + $(CPP_CMD) -zp4 -5s -mf -s -d2 /i=$(%WWFLAT)\include soundio.cpp + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + copy *.386 test + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/AUDIO/NEW/AUDIO.H b/WWFLAT32/AUDIO/NEW/AUDIO.H new file mode 100644 index 0000000..b6134ca --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/AUDIO.H @@ -0,0 +1,152 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + +typedef enum RateEnum { + PLAYBACK_RATE_SLOW =11025, + PLAYBACK_RATE_NORMAL =(11025 * 2), + PLAYBACK_RATE_FAST =(11025 * 4), +} RateType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void cdecl _saveregs Sound_Callback(void); +void cdecl far __saveregs __loadds maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels); +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels = FALSE); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; diff --git a/WWFLAT32/AUDIO/NEW/AUDUNCMP.ASM b/WWFLAT32/AUDIO/NEW/AUDUNCMP.ASM new file mode 100644 index 0000000..b4810f1 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/AUDUNCMP.ASM @@ -0,0 +1,437 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Audio Library * +;* * +;* File Name : AUDUNCMP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : March 14, 1995 * +;* * +;* Last Update : June 26, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Decompress_Frame_Lock -- locks the JLB audio decompression code * +;* Decompress_Frame_Unlock -- Unlocks the JLB audio compression code * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 + MODEL USE32 FLAT + LOCALS ?? + CODESEG + +DPMI_INTR equ 31h + +LABEL LockedCodeStart BYTE + +CODE_2BIT EQU 0 +CODE_4BIT EQU 1 +CODE_RAW EQU 2 +CODE_SILENCE EQU 3 +MAGICNUMBER EQU 00000DEAFh +MAGICNUMBER2 EQU 0BABEBABEh + +_2bitdecode DB -2,-1,0,1 +_4bitdecode DB -9,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8 + + +;*************************************************************************** +;* DECOMPRESS_FRAME -- Uncompresses a WW compressed audio frame * +;* * +;* INPUT: void * source - pointer to encoded audio data * +;* void * dest - pointer to decompression area * +;* long size - the maximum size of destination buffer * +;* * +;* OUTPUT: long - the number of bytes we uncompressed * +;* * +;* PROTO: long Decompress_Frame(void *, void *, long); * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL Decompress_Frame:NEAR + PROC Decompress_Frame C NEAR USES ebx ecx edx esi edi + + ARG source:DWORD + ARG dest:DWORD + ARG count:DWORD + + LOCAL previous:BYTE + LOCAL incount:DWORD + + pushf + cld + mov [incount],0 ;Bytes read from source + + +; Source, Dest and count must be valid. + + cmp [source],0 + je ??fini + + cmp [dest],0 + je ??fini + + cmp [count],0 + je ??fini + + mov esi,[source] ;Pointer to source data. + mov edi,[dest] ;Pointer to destination data. + mov ecx,[count] ;Number of bytes to fill dest buffer. + mov dl,080h ;Previous sample (starting value). + +??mainloop: + cmp ecx,0 ;If dest full then exit + jle ??fini + + xor eax,eax + mov al,[esi] ;Get code byte + inc [incount] + inc esi + shl eax,2 ;AH contains code. + shr al,2 ;AL contains sub-code data. + + cmp ah,CODE_RAW ;Raw sequence? + jne short ??try4bit + +; The code contains either a 5 bit delta or a count of +; raw samples to dump out. + + test al,00100000b + je short ??justraw + +; The lower 5 bits are actually a signed delta. +; Sign extend the delta and add it to the stream. + + shl al,3 + sar al,3 + add dl,al + mov [edi],dl + dec ecx + inc edi + jmp ??mainloop + +; The lower 5 bits hold a count of the number of raw +; samples that follow this code. Dump these samples to +; the output buffer. + +??justraw: + mov ebx,ecx + xor ah,ah + inc al + mov ecx,eax + shr ecx,1 + rep movsw + adc ecx,ecx + rep movsb + mov ecx,ebx + add [incount],eax + sub ecx,eax + dec edi + mov dl,[edi] ;Set "previous" value. + inc edi + jmp ??mainloop + +; Check to see if this is a 4 bit delta code sequence. + +??try4bit: + inc al ;Following codes use AL+1 + cmp ah,CODE_4BIT + jne short ??try2bit + +; A sequence of 4bit deltas follow. AL equals the +; number of nibble packed delta bytes to process. + +??bit4loop: + mov ah,[esi] ;Fetch nibble packed delta codes + mov bl,ah + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + and ebx,00001111b + add dl,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg1 + + popf + jnc short ??ok1 + mov dl,0FFh + jmp short ??ok1 + +??neg1: + popf + jc short ??ok1 + + xor dl,dl + +??ok1: + mov dh,dl ;DH now holds new 'previous' sample. + mov bl,ah + shr bl,4 + add dh,[_4bitdecode+ebx] + pushf + cmp [_4bitdecode+ebx],0 + jl short ??neg2 + + popf + jnc short ??ok2 + + mov dh,0FFh + jmp short ??ok2 + +??neg2: + popf + jc short ??ok2 + + xor dh,dh + +??ok2: + mov [edi],dx ;Output the two sample bytes + sub ecx,2 + add edi,2 + +; Put the correct 'previous' sample in DL where it belongs. + + mov dl,dh + +; If there are more deltas to process then loop back. + + dec al + jnz short ??bit4loop + jmp ??mainloop + +; Check to see if 2 bit deltas need to be processed. + +??try2bit: + cmp ah,CODE_2BIT + jne ??zerodelta + +; A sequence of 2bit deltas follow. AL equals the number of +; packed delta bytes to process. + +??bit2loop: + mov ah,[esi] ;Fetch packed delat codes + inc [incount] + inc esi + +; Add first delta to 'previous' sample already in DL. + + mov bl,ah + and ebx,000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg3 + + popf + jnc short ??ok3 + + mov dl,0FFh + jmp short ??ok3 + +??neg3: + popf + jc short ??ok3 + xor dl,dl + +??ok3: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,2 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg4 + + popf + jnc short ??ok4 + + mov dl,0FFh + jmp short ??ok4 + +??neg4: + popf + jc short ??ok4 + + xor dl,dl + +??ok4: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,4 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg5 + + popf + jnc short ??ok5 + + mov dl,0FFh + jmp short ??ok5 + +??neg5: + popf + jc short ??ok5 + + xor dl,dl + +??ok5: + mov dh,dl + ror edx,8 + mov bl,ah + shr ebx,6 + and bl,00000011b + add dl,[_2bitdecode+ebx] + pushf + cmp [_2bitdecode+ebx],0 + jl short ??neg6 + + popf + jnc short ??ok6 + + mov dl,0FFh + jmp short ??ok6 + +??neg6: + popf + jc short ??ok6 + + xor dl,dl + +??ok6: + ror edx,8 + mov [edi],edx ;Output two sample bytes + sub ecx,4 + add edi,4 + +; Put the correct 'previous' sample in DL where it belongs. + + rol edx,8 + +; If there are more deltas to process then loop back. + + dec al + jnz ??bit2loop + jmp ??mainloop + +; There is a run of zero deltas. Zero deltas merely duplicate +; the 'previous' sample the requested number of times. + +??zerodelta: + xor ebx,ebx + mov bl,al + mov al,dl + sub ecx,ebx + xchg ecx,ebx + rep stosb + mov ecx,ebx + jmp ??mainloop + +??fini: + popf + mov eax,[incount] + ret + + ENDP Decompress_Frame + +LABEL LockedCodeEnd BYTE + + +;*************************************************************************** +;* DECOMPRESS_FRAME_LOCK -- locks the JLB audio decompression code * +;* * +;* INPUT: none * +;* * +;* OUTPUT: BOOL true is lock sucessful, false otherwise * +;* * +;* PROTO: BOOL Decompress_Frame_Lock(void); * +;* * +;* HISTORY: * +;* 06/26/1995 PWG : Created. * +;*=========================================================================* + GLOBAL Decompress_Frame_Lock:NEAR + PROC Decompress_Frame_Lock C NEAR USES ebx ecx edx esi edi + + 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 + mov eax,1 + jmp ??exit +??error: + xor eax,eax +??exit: + ret + + ENDP Decompress_Frame_Lock + +;*************************************************************************** +;* DECOMPRESS_FRAME_UNLOCK -- Unlocks the JLB audio compression code * +;* * +;* INPUT: none * +;* * +;* OUTPUT: BOOL true is unlock sucessful, false otherwise * +;* * +;* PROTO: BOOL Decompress_Frame_Unlock(void); * +;* * +;* HISTORY: * +;* 06/26/1995 PWG : Created. * +;*=========================================================================* + GLOBAL Decompress_Frame_Unlock:NEAR + PROC Decompress_Frame_Unlock C NEAR USES ebx ecx edx esi edi + + 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 + mov eax,1 + jmp ??exit +??error: + xor eax,eax +??exit: + ret + + ENDP Decompress_Frame_Unlock + + + + END diff --git a/WWFLAT32/AUDIO/NEW/DIFFTB.INC b/WWFLAT32/AUDIO/NEW/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WWFLAT32/AUDIO/NEW/FUNCTION.H b/WWFLAT32/AUDIO/NEW/FUNCTION.H new file mode 100644 index 0000000..7edf5f2 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int cdecl File_Stream_Sample(char const *filename); +int cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void cdecl _saveregs _loadds Sound_Callback(void); +void cdecl far _saveregs _loadds maintenance_callback(void); +void *cdecl Load_Sample(char const *filename); +long cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long cdecl Sample_Read(int fh, void *buffer, long size); +void cdecl Free_Sample(void const *sample); +BOOL cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void cdecl Sound_End(void); +void cdecl Stop_Sample(int handle); +BOOL cdecl Sample_Status(int handle); +BOOL cdecl Is_Sample_Playing(void const * sample); +void cdecl Stop_Sample_Playing(void const * sample); +int cdecl Play_Sample(void const *sample); +int cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int cdecl Set_Sound_Vol(int volume); +int cdecl Set_Score_Vol(int volume); +void cdecl Fade_Sample(int handle, int ticks); diff --git a/WWFLAT32/AUDIO/NEW/INDEXTB.INC b/WWFLAT32/AUDIO/NEW/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WWFLAT32/AUDIO/NEW/MAKEFILE b/WWFLAT32/AUDIO/NEW/MAKEFILE new file mode 100644 index 0000000..cbf7aa6 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/MAKEFILE @@ -0,0 +1,191 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = AUDIO +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + soundio.obj & + soundint.obj & + soundlck.obj & + soscodec.obj & + auduncmp.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.lib $(%WWFLAT)\lib + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + copy *.lib $(%WWFLAT)\lib + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +soundio.obj: .AUTODEPEND + $(CPP_CMD) -zp4 -5s -mf -s -d2 /i=$(%WWFLAT)\include soundio.cpp + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + copy *.386 test + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/AUDIO/NEW/NYBBTB.INC b/WWFLAT32/AUDIO/NEW/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WWFLAT32/AUDIO/NEW/SOS.H b/WWFLAT32/AUDIO/NEW/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WWFLAT32/AUDIO/NEW/SOSCODEC.ASM b/WWFLAT32/AUDIO/NEW/SOSCODEC.ASM new file mode 100644 index 0000000..490823a --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSCODEC.ASM @@ -0,0 +1,871 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 DD -1,-1,-1,-1,2,4,6,8 + DD -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 DD 7, 8, 9, 10, 11, 12, 13,14 + DD 16, 17, 19, 21, 23, 25, 28, 31 + DD 34, 37, 41, 45, 50, 55, 60, 66 + DD 73, 80, 88, 97, 107, 118, 130, 143 + DD 157, 173, 190, 209, 230, 253, 279, 307 + DD 337, 371, 408, 449, 494, 544, 598, 658 + DD 724, 796, 876, 963, 1060, 1166, 1282, 1411 + DD 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024 + DD 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484 + DD 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899 + DD 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794 + DD 32767 + +wCode DD 0 ; this is to hold index into wCodeStep +dwCODECByteIndex DD 0 ; this is when to stop compressing +dwCODECBytesProcessed DD 0 ; this is how many so far compressed +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 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 sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 + + 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 +;--------------------------------------------------------------------------- + push ebp + movzx edx,[(sCompInfo ebx).wIndex] ;preload index + mov ebp, [dwCODECByteIndex] + mov ecx,[(sCompInfo ebx).dwSampleIndex] ;preload SampleIndex + +??mainloop: + xor eax,eax ;get a new token + test ecx,1 ;odd Sample?? + je short ??fetchToken ; if so get new token + mov ax,[(sCompInfo ebx).wCodeBuf] ;ored with Code + shr eax,4 + jmp short ??calcDifference + align 4 + +??fetchToken: + mov al,[esi] ;put in codebuf + inc esi + mov [(sCompInfo ebx).wCodeBuf],ax + + +??calcDifference: + xor ecx,ecx + and eax,000Fh + mov cx,[(sCompInfo ebx).wStep] ;cx is step value + mov [wCode],eax + jmp [DWORD PTR JumpTable + eax*4] + align 4 + +; note: it is important for speed reasons to keep the order the +; following jumps entries as well as the "align 4" after some of +; the jmp statements + +??7: + ; eax = x + x/2 + x/4 + x/8 = (8*x + 4*x +2*x + x)>>3 = + ; = ( x * ( 8 + 4 + 2 + 1 )) >> 3 = ( x * 15 ) >> 3 + lea ecx,[ecx+ecx*2] + lea eax,[ecx+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??6: + ; eax = x + x / 2 + x / 8 = (8*x + 4*x + x) >> 3 = + ; = ( x * 8 + x * 5 ) >> 8 + lea eax,[ecx+ecx*4] + lea eax,[eax+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??5: + ; eax = x + x / 4 + x / 8 = (8*x + 2*x + x) >> 3 = + ; = ( 8 * x + 3 * x) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??4: + ; eax = x + x / 8 = (8*x + x) >> 3 = (x * 9)>> 3 + lea eax,[ecx+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??3: + ; eax = x/2 + x/4 + x/8 = (4*x + 2*x + x) >> 3 + ; = ( 4x + 3x ) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??2: + ; eax = x/2 + x/8 = (4*x + x) >> 3 + lea eax,[ecx+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??1: + ; eax = x/4 + x/8 = (2x + x )>>8 + lea eax,[ecx+ecx*2] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??0: + ; eax = x/8 = x >> 3 + mov eax,ecx + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??15: + ; eax = x + x/2 + x/4 + x/8 = (8*x + 4*x +2*x + x)>>3 = + ; = ( x * ( 8 + 4 + 2 + 1 )) >> 3 = ( x * 15 ) >> 3 + lea ecx,[ecx+ecx*2] + lea eax,[ecx+ecx*4] + jmp ??neg_save_dif + align 4 + +??14: + ; eax = x + x / 2 + x / 8 = (8*x + 4*x + x) >> 3 = + ; = ( x * 8 + x * 5 ) >> 8 + lea eax,[ecx+ecx*4] + lea eax,[eax+ecx*8] + jmp ??neg_save_dif + align 4 + +??13: + ; eax = x + x / 4 + x / 8 = (8*x + 2*x + x) >> 3 = + ; = ( 8 * x + 3 * x) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*8] + jmp ??neg_save_dif + align 4 + +??12: + ; eax = x + x / 8 = (8*x + x) >> 3 = (x * 9)>> 3 + lea eax,[ecx+ecx*8] + jmp ??neg_save_dif + align 4 + +??11: + ; eax = x/2 + x/4 + x/8 = (4*x + 2*x + x) >> 3 + ; = ( 4*x - 3*x ) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*4] + jmp ??neg_save_dif + align 4 + +??10: + ; eax = x/2 + x/8 = (4*x + x) >> 3 + lea eax,[ecx+ecx*4] + jmp ??neg_save_dif + align 4 + +??9: + ; eax = x/4 + x/8 = (2x + x )>>8 + lea eax,[ecx+ecx*2] + jmp ??neg_save_dif + align 4 + +??8: + ; eax = x/8 = x >> 3 + mov eax,ecx ; !!important!! no need for align here + +??neg_save_dif: + sar eax,3 ; now we divide x>>3 + neg eax + +??save_dif: + mov ecx,[wCode] ; load offset into CODETab table + mov [(sCompInfo ebx).dwDifference],eax ;Store wStep + + ; add difference to predicted value. + add eax,[(sCompInfo ebx).dwPredicted] + + ; 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: + add edx,[wCODECIndexTab + ecx*4] ; won't hurt 486 + cmp [(sCompInfo ebx).wBitSize],16 + mov [(sCompInfo ebx).dwPredicted],eax + mov ecx,[(sCompInfo ebx).dwSampleIndex] ;load dwSampleindex + je short ??output16Bit + ; output 8 bit sample + xor ah,80h + mov [edi],ah + inc edi + jmp short ??adjustIndex + align 4 + +??output16Bit: + mov [edi],ax ;Output 16bit sample + add edi,2 + +??adjustIndex: + cmp edx,8000h + jb short ??checkOverflow + mov edx,0 ;reset index to zero + +??checkOverflow: + inc ecx ; advance index and store step value + cmp edx,88 ;check if wIndex > 88 + jbe short ??adjustStep + mov edx,88 ;reset index to 88 + +??adjustStep: + ; advance index and store step value + mov [(sCompInfo ebx).dwSampleIndex],ecx + + ; fetch wIndex so we can fetch new step value + mov eax,[wCODECStepTab + edx*4] + + ; decrement bytes processed and loop back. + dec ebp + mov [(sCompInfo ebx).wStep],ax + jne ??mainloop + pop ebp + + mov eax,[wCode] ; these three lines do not + mov [(sCompInfo ebx).wCode],ax ; seem to have any relevance + + mov [(sCompInfo ebx).wIndex],dx ; save index + 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,2 + mov eax,[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,2 + mov eax,[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,2 + mov eax,[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,2 + mov eax,[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 + +JumpTable DD ??0 + DD ??1 + DD ??2 + DD ??3 + DD ??4 + DD ??5 + DD ??6 + DD ??7 + DD ??8 + DD ??9 + DD ??10 + DD ??11 + DD ??12 + DD ??13 + DD ??14 + DD ??15 + + + ENDP 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 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 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 + diff --git a/WWFLAT32/AUDIO/NEW/SOSCOMP.H b/WWFLAT32/AUDIO/NEW/SOSCOMP.H new file mode 100644 index 0000000..7334764 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif + diff --git a/WWFLAT32/AUDIO/NEW/SOSDATA.H b/WWFLAT32/AUDIO/NEW/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WWFLAT32/AUDIO/NEW/SOSDEFS.H b/WWFLAT32/AUDIO/NEW/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WWFLAT32/AUDIO/NEW/SOSDW1PS.LIB b/WWFLAT32/AUDIO/NEW/SOSDW1PS.LIB new file mode 100644 index 0000000..775054e Binary files /dev/null and b/WWFLAT32/AUDIO/NEW/SOSDW1PS.LIB differ diff --git a/WWFLAT32/AUDIO/NEW/SOSFNCT.H b/WWFLAT32/AUDIO/NEW/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WWFLAT32/AUDIO/NEW/SOSRES.H b/WWFLAT32/AUDIO/NEW/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WWFLAT32/AUDIO/NEW/SOUND.BAK b/WWFLAT32/AUDIO/NEW/SOUND.BAK new file mode 100644 index 0000000..ad0d223 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUND.BAK @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 4 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + +#endif diff --git a/WWFLAT32/AUDIO/NEW/SOUND.H b/WWFLAT32/AUDIO/NEW/SOUND.H new file mode 100644 index 0000000..ca20107 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUND.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + +#endif diff --git a/WWFLAT32/AUDIO/NEW/SOUNDINT.BAK b/WWFLAT32/AUDIO/NEW/SOUNDINT.BAK new file mode 100644 index 0000000..9a63469 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDINT.BAK @@ -0,0 +1,253 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + unsigned Loading:1; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + unsigned DontTouch:1; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + unsigned IsScore:1; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers in low ram. + */ + VOID *Buffer[2]; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ + WORD Index; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + WORD Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + WORD Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + WORD Volume; + WORD Reducer; // Amount to reduce volume per tick. + + /* + ** This flags whether the sample is in stereo. + */ + WORD Stereo; + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + WORD TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + WORD Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(WORD id, WORD *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + WORD Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + WORD FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + _SOS_COMPRESS_INFO sosinfo; + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; + int MaxSamples; + int Rate; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +void Unlock_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize); +VOID far cdecl maintenance_callback(VOID); +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void Audio_Mem_Set(void const *ptr, unsigned char value, long size); + void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long Decompress_Frame(void * source, void * dest, long size); + int Decompress_Frame_Lock(void); + int Decompress_Frame_Unlock(void); + int sosCODEC_Lock(void); + int sosCODEC_Unlock(void); + void __GETDS(void); +} + diff --git a/WWFLAT32/AUDIO/NEW/SOUNDINT.CPP b/WWFLAT32/AUDIO/NEW/SOUNDINT.CPP new file mode 100644 index 0000000..0611f8a --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDINT.CPP @@ -0,0 +1,549 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#include "soundint.h" +//#include "mem.h" + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + +/*************************************************************************** + * SAVE_MY_REGS -- Inline function which will save assembly regs * + * * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux save_my_regs = \ + "pushfd" \ + "pushad" \ + "push ds" \ + "push es" \ + "push fs" \ + "push gs" \ + "push ds" \ + "pop es"; + +#pragma aux enable = \ + "sti"; + +#pragma aux disable = \ + "cli"; + +/*************************************************************************** + * RESTORE_MY_REGS -- Inline function which will restore saved registers * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux restore_my_regs = \ + "pop gs" \ + "pop fs" \ + "pop es" \ + "pop ds" \ + "popad" \ + "popfd"; + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + sosCODECDecompressData(&st->sosinfo, dsize); + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + +VOID far cdecl maintenance_callback(VOID) +{ + save_my_regs(); + int index; + SampleTrackerType *st; + + if (!LockedData._int && LockedData.DigiHandle != -1 && LockedData.ServiceSomething) { + + LockedData._int++; + enable(); + LockedData.ServiceSomething = FALSE; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service) { + if (st->DontTouch) { + LockedData.ServiceSomething = TRUE; + } else { + st->Service = FALSE; + +#if(FALSE) + st->DataLength = SFX_MINI_STAGE_BUFFER_SIZE; +#else + st->DataLength = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[st->Index], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + +#endif + } + } + + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + LockedData._int--; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + + if (st->IsScore) { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.ScoreVolume); + } else { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.SoundVolume); + } + } + st++; + } + LockedData._int--; + } + restore_my_regs(); +} +/*********************************************************************************************** + * DigiCallback -- Low level double buffering handler. * + * * + * This routine is called in an interrupt to handle the double * + * buffering of digital audio. This routine is the interface between * + * the buffers maintained by Sound_Callback() and the HMI driver * + * itself. * + * * + * INPUT: driverhandle -- The handle to the HMI driver. * + * * + * callsource -- Code indicating the reason for the callback. * + * * + * sampleid -- The ID number of the sample itself. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is called in an interrupt so it return as quickly as * + * possible. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid) +{ + SampleTrackerType *st; + int index; + + + /* + ** Find the correct control structure for the handle specified. + */ + for (index = 0; index < LockedData.MaxSamples; index++) { + st = &LockedData.SampleTracker[index]; + if (st->Active && st->Handle == sampleid) { + break; + } + } + if (index == LockedData.MaxSamples) { + return; + } + + if (driverhandle == LockedData.DigiHandle) { + + switch (callsource) { + + /* + ** The sample is now no longer audible. Don't stop the sample + ** tracking if a servicing is needed. If it is needed then + ** obviously the sample isn't quite done. + */ + case _SAMPLE_DONE: + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + break; + + /* + ** The sample is finished processing, but not necessarily finished playing. + */ + case _SAMPLE_PROCESSED: + if (st->DataLength && st->Active) { + _SOS_START_SAMPLE start; + long dlength; + + dlength = st->DataLength; + st->DataLength = 0; + + Audio_Mem_Set(&start, 0, sizeof(start)); + start.lpSamplePtr = (LPSTR)st->Buffer[st->Index]; + + start.dwSampleSize = dlength-1; + + start.wSampleFlags = st->Flags; + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + if (st->IsScore) { + start.wVolume = (st->Volume>>8) * LockedData.ScoreVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } else { + start.wVolume = (st->Volume>>8) * LockedData.SoundVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } + + start.wChannel = st->Stereo; + start.wSampleID = index+1; + start.dwSamplePitchAdd = st->Pitch; + st->Index ^= 1; + + if (st->Remainder || st->QueueBuffer || st->Callback || st->FilePending) { + st->Service = TRUE; + LockedData.ServiceSomething = TRUE; + } + + sosDIGIContinueSample(LockedData.DigiHandle, st->Handle, &start); + } else { + + /* + ** This is necessary because Stop_Sample may screw things + ** up, otherwise. Can't rely on the _SAMPLE_DONE call. + */ + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + + } + break; + + /* + ** Sample is now looping (not used). + */ + case _SAMPLE_LOOPING: + break; + } + } + +} + +void far HMI_TimerCallback(void) +{ +} + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WWFLAT32/AUDIO/NEW/SOUNDINT.H b/WWFLAT32/AUDIO/NEW/SOUNDINT.H new file mode 100644 index 0000000..49ef523 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDINT.H @@ -0,0 +1,253 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + unsigned Loading:1; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + unsigned DontTouch:1; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + unsigned IsScore:1; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers in low ram. + */ + VOID *Buffer[2]; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ + WORD Index; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + WORD Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + WORD Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + WORD Volume; + WORD Reducer; // Amount to reduce volume per tick. + + /* + ** This flags whether the sample is in stereo. + */ + WORD Stereo; + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + WORD TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + WORD Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(WORD id, WORD *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + WORD Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + WORD FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + _SOS_COMPRESS_INFO sosinfo; + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; + int MaxSamples; + int Rate; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +void Unlock_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize); +VOID far cdecl maintenance_callback(VOID); +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void Audio_Mem_Set(void const *ptr, unsigned char value, long size); + void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + long Decompress_Frame(void * source, void * dest, long size); + int Decompress_Frame_Lock(void); + int Decompress_Frame_Unlock(void); + int sosCODEC_Lock(void); + int sosCODEC_Unlock(void); + void __GETDS(void); +} + diff --git a/WWFLAT32/AUDIO/NEW/SOUNDIO.BAK b/WWFLAT32/AUDIO/NEW/SOUNDIO.BAK new file mode 100644 index 0000000..f57646b --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDIO.BAK @@ -0,0 +1,1463 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * File_Callback -- called to fill queue buffer for streaming sample * + * Stream_Sample_Volume -- generic streaming sample playback init * + * Stream_Sample -- generic streaming sample playback init * + * File_Stream_Sample -- Streams a sample directly from a file. * + * File_Stream_Preload -- Handles initial proload of a streaming samples * + * File_Stream_Sample_Volume -- Streams a sample directly from a file w/volume. * + * Sound_Callback -- Audio driver callback function. * + * Load_Sample -- Loads a digitized sample into RAM. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Free_Sample -- Frees a previously loaded digitized sample. * + * Sound_End -- Uninitializes the sound driver. * + * Stop_Sample -- Stops any currently playing sampled sound. * + * Sample_Status -- Queries the current playing sample status (if any). * + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma pack(4) +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This define (if present) enables the simple HMI hardware initialization process. +** The process does not do auto detection, but rather takes the value directly from +** the setup program and uses that as the sound card number. The only "detection" it +** does is to recognized the presence of the card and fetch its settings. +*/ +#define SIMPLE_HMI_INIT + + +/* +** This is the rate that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 60 + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD MidiHandle = -1; +static unsigned int far DigiTimer = 0; +static unsigned int far MaintainTimer = 0; +static unsigned int far SystemTimer = 0; +static int Bits_Per_Sample; +VOID *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +short StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size); +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle); +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size)); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + + +/*************************************************************************** + * FILE_CALLBACK -- called to fill queue buffer for streaming sample * + * * + * This callback is called whenever the queue buffer playback has begun * + * and another buffer is needed for queuing up. Returns TRUE if there * + * is more data to read from the file. * + * * + * INPUT: WORD id - the sample id number * + * WORD *odd - which sample buffer to put info in * + * VOID **buffer - the buffer pointer to load data into * + * LONG *size - the amount to load * + * * + * OUTPUT: BOOL true if more data to load, FALSE if done loading * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + VOID *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + st->DontTouch = TRUE; + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = *odd + 1; + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + + + while (num_empty_buffers && st->FileHandle != ERROR) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + maintenance_callback(); + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + return(TRUE); + } + } + } + return(FALSE); +} + +/*************************************************************************** + * STREAM_SAMPLE_VOLUME -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; + } + } + return (playid); +} + +/*************************************************************************** + * STREAM_SAMPLE -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} + + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT: LockedData.SampleTracker * to the header which tracks this samples * + * processing. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //printf("Before buffer load!\n"); + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + //printf("Reading block of size %d Stream Buffer = %d\n"); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + //printf("Before Stream Sample Volume!\n"); + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + { + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + + StartingFileStream = TRUE; + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + } + //printf("After Stream Sample Volume!\n"); + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } +} +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static VOID *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + long size; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + //printf("Before initialize memory!\n"); + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + //printf("Before Open File!\n"); + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + //printf("Before Get Free Handle!\n"); + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= LockedData.MaxSamples) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + //printf("Before Preload!\n"); + File_Stream_Preload(handle); + } + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl __saveregs __loadds Sound_Callback(VOID) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + Stop_Sample(index); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + if ((!st->QueueBuffer || + (st->FileHandle != ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback(index, &st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != ERROR) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +VOID *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + VOID *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* + Conversion formula for TCrate and Hz rate. + + TC = 256 - 1m/rate + rate = 1m / (256-TC) +*/ + + if (!buffer || fh == ERROR || size <= sizeof(RawHeader)) return(NULL); + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + + actual_bytes_read = + Read_File(fh, &RawHeader, sizeof(RawHeader)); + + actual_bytes_read += + Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + + + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +VOID Free_Sample(VOID const *sample) +{ + if (sample) Free((void *)sample); +} + +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + return Audio_Init(sample, -1, -1, -1, rate, bits_per_sample, max_samples, reverse_channels); +} + +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + int error; // Function error return code. + unsigned int port; + int index; + _SOS_INIT_DRIVER init; // Driver init structure. + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + LockedData.Rate = rate; + + Bits_Per_Sample = bits_per_sample; + + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + + if (TimerSystemOn) { + sosTIMERRegisterEvent(60, &Timer_Interrupt_Func, &SystemTimer); + TickCount.Start(); + } else + sosTIMERRegisterEvent(60, &HMI_TimerCallback, &SystemTimer); + + /* + ** Special code to handle the HMI problem with running under Windows. + */ + if (/*Operating_System() != OS_DOS ||*/ sample == SAMPLE_NONE) { + return(FALSE); + } + + while (sample) { + +#ifdef SIMPLE_HMI_INIT + + if (address == -1 || inter == -1 || dma == -1) { + error = sosDIGIDetectInit((LPSTR)NULL); + if (error) { + printf("Cannot initialize detection system (%d).\n", error); + Get_Key(); + break; + } + + error = sosDIGIDetectFindHardware(sample, &DigiCaps, &port); + if (error) { + printf("Cannot find sound card specified.\n"); + Get_Key(); + break; + } + + /* + ** Handle the override for Address, Interrupt, and DMA settings. + */ + error = sosDIGIDetectGetSettings(&DigiHardware); + sosDIGIDetectUnInit(); + if (error) { + printf("Cannot get sound card settings.\n"); + Get_Key(); + break; + } + } else { + DigiHardware.wPort = address; + DigiHardware.wIRQ = inter; + DigiHardware.wDMA = dma; + } +#endif + + /* + ** Initialize the digi-system and driver. + */ + sosDIGIInitSystem((LPSTR)NULL, _SOS_DEBUG_NORMAL); + init.wBufferSize = 1024*8; // Specify the DMA buf size + init.lpBuffer = (LPSTR)NULL; + init.wAllocateBuffer = TRUE; + init.wSampleRate = rate; // Sample playback rate. + init.wParam = 19; + init.dwParam = 0; + init.lpFillHandler = NULL; + init.lpDriverMemory = (LPSTR)NULL; + init.lpTimerMemory = (LPSTR)NULL; + init.wTimerID = _SOS_NORMAL_TIMER; + + error = 0; + error = sosDIGIInitDriver( (int)sample, &DigiHardware, &init, &LockedData.DigiHandle); + if (error) { + printf("Unable to initialize the sound driver.\n"); + Get_Key(); + break; + } + + error = 0; + + + if (sample >= _GUS_8_MONO || sample <= _GUS_16_ST) { + error = sosTIMERRegisterEvent(150, init.lpFillHandler, &DigiTimer); + } else { + error = sosTIMERRegisterEvent(60, init.lpFillHandler, &DigiTimer); + } + if (error) { + printf("Unable to regiser the fill handler.\n"); + Get_Key(); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + LockedData.MaxSamples = max_samples; + + /* + ** Allocate private staging buffers for double buffering sound into HMI + ** driver. + */ + DigiBuffer = Alloc(2048+(((LONG)LockedData.MaxSamples*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)), (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].Buffer[0] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].Buffer[1] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2+1) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].FileHandle = ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + } + error = sosTIMERRegisterEvent(MAINTENANCE_RATE, (void (__far *)(void))maintenance_callback, &MaintainTimer); + if (error) { + printf("Unable to initialize the maintenance callback.\n"); + Get_Key(); + sosTIMERRemoveEvent(DigiTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + break; + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + *=============================================================================================*/ +VOID Sound_End(VOID) +{ + + if (LockedData.DigiHandle != -1) { + sosTIMERRemoveEvent(DigiTimer); + sosTIMERRemoveEvent(MaintainTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + sosDIGIUnInitSystem(); + LockedData.DigiHandle = -1; + } + if (DigiBuffer) { + Free(DigiBuffer); + DigiBuffer = 0; + } + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + sosTIMERRemoveEvent(SystemTimer); + sosTIMERUnInitSystem(0); + Unlock_Locked_Data(); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +VOID Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < LockedData.MaxSamples) { + + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + + LockedData.SampleTracker[handle].Active = FALSE; + if (!LockedData.SampleTracker[handle].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[handle].Original, LockedData.SampleTracker[handle].OriginalSize); + LockedData.SampleTracker[handle].Original = NULL; + } + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + sosDIGIStopSample(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle); + } + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= LockedData.MaxSamples) return(FALSE); + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + return (!sosDIGISampleDone(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle)); +} + + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + if (!sample) return FALSE; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + return (TRUE); + } + } + return (FALSE); +} + + +VOID Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = LockedData.MaxSamples - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + if (id == LockedData.MaxSamples) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + if (id == -1) { + return -1; + } + if (LockedData.SampleTracker[id].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = ERROR; + } + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + *=============================================================================================*/ +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) +{ + AUDHeaderType RawHeader; + _SOS_START_SAMPLE start; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + if (!sample || LockedData.DigiHandle == -1) { + return(-1); + } + + if (id == -1) { + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + st = &LockedData.SampleTracker[id]; + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + if (!st->IsScore) { + DPMI_Lock(st->Original, st->OriginalSize); + } + st->Priority = priority; + st->DontTouch = FALSE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** Fill in one or both staging buffers if possible. + */ + _disable(); +// Disable_Timer_Interrupt(); + if (SFX_MINI_STAGE_BUFFER_SIZE == + Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[0], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen)) { + + st->DataLength = Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[1], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + st->Index = 1; + } else { + st->Index = 0; + st->DataLength = 0; + } + _enable(); +// Enable_Timer_Interrupt(); + + /* + ** Fill in the HMI start sample structure. + */ + memset(&start, 0, sizeof(start)); + + start.lpSamplePtr = (LPSTR)st->Buffer[0]; + if (st->Index == 1) { + start.dwSampleSize = SFX_MINI_STAGE_BUFFER_SIZE-1; + } else { + start.dwSampleSize = st->DataLength-1; + } + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + start.wSampleID = id; + + /* + ** Adjust pitch shifting as necessary so that lower playback + ** samples can be supported. + */ + if (RawHeader.Rate != LockedData.Rate) { + ldiv_t result; + + result = ldiv((long)RawHeader.Rate, LockedData.Rate); + start.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, (short)(((long)result.rem * 0x10000L) / LockedData.Rate)); + start.wSampleFlags |= _PITCH_SHIFT; + } + + /* + ** Sample translation flag. + */ + + if (RawHeader.Flags & AUD_FLAG_16BIT) { + if (Bits_Per_Sample == 8) { + start.wSampleFlags |= _TRANSLATE16TO8; + } + } else { + if (Bits_Per_Sample == 16) { + start.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* + ** Sample stereo flag. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + start.wChannel = _INTERLEAVED; + start.wSampleFlags |= _STEREOTOMONO; + } else { + start.wChannel = _CENTER_CHANNEL; + } + + /* + ** Sample volume control flags. Always give it volume control because + ** if not, then future volume control is either ignored or stops the + ** sample. + */ + st->Volume = volume << 7; + start.wVolume = (st->Volume >> 8) * LockedData.SoundVolume; + start.wSampleFlags |= _VOLUME; + + /* + ** If we have defined a panning location for the sound driver than + ** take care of it here. Panning will only work with a stereo driver + ** and only if the sample is played _CENTER_CHANNEL. + */ + if ((panloc != 0x0) && (!(RawHeader.Flags & AUD_FLAG_STEREO))) { + start.wChannel = _CENTER_CHANNEL; + panloc = ((ReverseChannels) ? ((-panloc) + 0x8000) : ((panloc) + 0x8000)); + start.wSampleFlags |= _PANNING; + start.wSamplePanLocation= panloc; + } + + st->Stereo = start.wChannel; + st->Pitch = start.dwSamplePitchAdd; + st->Flags = start.wSampleFlags; + + /* + ** Start the sample playing now. + */ + _disable(); // NEW +// Disable_Timer_Interrupt(); + st->Handle = sosDIGIStartSample(LockedData.DigiHandle, &start); + if (st->Handle == -1) { + id = -1; + } else { + + /* + ** Fill in the sample tracker structure with those values that are + ** determined AFTER the sample starts. + */ + st->Active = TRUE; + } + _enable(); // NEW +// Enable_Timer_Interrupt(); + + return(id); +} + + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +int Set_Score_Vol(int volume) +{ + int old; + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + return(old); +} + + +VOID Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + + st = &LockedData.SampleTracker[handle]; + st->Reducer = (st->Volume / ticks)+1; + } + } +} +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length * + * of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} diff --git a/WWFLAT32/AUDIO/NEW/SOUNDIO.CPP b/WWFLAT32/AUDIO/NEW/SOUNDIO.CPP new file mode 100644 index 0000000..e29f693 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDIO.CPP @@ -0,0 +1,1463 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * File_Callback -- called to fill queue buffer for streaming sample * + * Stream_Sample_Volume -- generic streaming sample playback init * + * Stream_Sample -- generic streaming sample playback init * + * File_Stream_Sample -- Streams a sample directly from a file. * + * File_Stream_Preload -- Handles initial proload of a streaming samples * + * File_Stream_Sample_Volume -- Streams a sample directly from a file w/volume. * + * Sound_Callback -- Audio driver callback function. * + * Load_Sample -- Loads a digitized sample into RAM. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Free_Sample -- Frees a previously loaded digitized sample. * + * Sound_End -- Uninitializes the sound driver. * + * Stop_Sample -- Stops any currently playing sampled sound. * + * Sample_Status -- Queries the current playing sample status (if any). * + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma pack(4) +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This define (if present) enables the simple HMI hardware initialization process. +** The process does not do auto detection, but rather takes the value directly from +** the setup program and uses that as the sound card number. The only "detection" it +** does is to recognized the presence of the card and fetch its settings. +*/ +#define SIMPLE_HMI_INIT + + +/* +** This is the rate that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 60 + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD MidiHandle = -1; +static unsigned int far DigiTimer = 0; +static unsigned int far MaintainTimer = 0; +static unsigned int far SystemTimer = 0; +static int Bits_Per_Sample; +VOID *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +short StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size); +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle); +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size)); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + + +/*************************************************************************** + * FILE_CALLBACK -- called to fill queue buffer for streaming sample * + * * + * This callback is called whenever the queue buffer playback has begun * + * and another buffer is needed for queuing up. Returns TRUE if there * + * is more data to read from the file. * + * * + * INPUT: WORD id - the sample id number * + * WORD *odd - which sample buffer to put info in * + * VOID **buffer - the buffer pointer to load data into * + * LONG *size - the amount to load * + * * + * OUTPUT: BOOL true if more data to load, FALSE if done loading * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + VOID *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + st->DontTouch = TRUE; + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = *odd + 1; + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + + + while (num_empty_buffers && st->FileHandle != ERROR) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + maintenance_callback(); + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + return(TRUE); + } + } + } + return(FALSE); +} + +/*************************************************************************** + * STREAM_SAMPLE_VOLUME -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; + } + } + return (playid); +} + +/*************************************************************************** + * STREAM_SAMPLE -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} + + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT: LockedData.SampleTracker * to the header which tracks this samples * + * processing. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //printf("Before buffer load!\n"); + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + //printf("Reading block of size %d Stream Buffer = %d\n"); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + //printf("Before Stream Sample Volume!\n"); + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + { + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + + StartingFileStream = TRUE; + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + } + //printf("After Stream Sample Volume!\n"); + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } +} +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static VOID *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + long size; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + //printf("Before initialize memory!\n"); + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + //printf("Before Open File!\n"); + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + //printf("Before Get Free Handle!\n"); + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= LockedData.MaxSamples) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + //printf("Before Preload!\n"); + File_Stream_Preload(handle); + } + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl __saveregs __loadds Sound_Callback(VOID) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + Stop_Sample(index); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + if ((!st->QueueBuffer || + (st->FileHandle != ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback(index, &st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != ERROR) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +VOID *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + VOID *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* + Conversion formula for TCrate and Hz rate. + + TC = 256 - 1m/rate + rate = 1m / (256-TC) +*/ + + if (!buffer || fh == ERROR || size <= sizeof(RawHeader)) return(NULL); + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + + actual_bytes_read = + Read_File(fh, &RawHeader, sizeof(RawHeader)); + + actual_bytes_read += + Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + + + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +VOID Free_Sample(VOID const *sample) +{ + if (sample) Free((void *)sample); +} + +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + return Audio_Init(sample, -1, -1, -1, rate, bits_per_sample, max_samples, reverse_channels); +} + +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + int error; // Function error return code. + unsigned int port; + int index; + _SOS_INIT_DRIVER init; // Driver init structure. + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + LockedData.Rate = rate; + + Bits_Per_Sample = bits_per_sample; + + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + + if (TimerSystemOn) { + sosTIMERRegisterEvent(60, &Timer_Interrupt_Func, &SystemTimer); + TickCount.Start(); + } else + sosTIMERRegisterEvent(60, &HMI_TimerCallback, &SystemTimer); + + /* + ** Special code to handle the HMI problem with running under Windows. + */ + if (/*Operating_System() != OS_DOS ||*/ sample == SAMPLE_NONE) { + return(FALSE); + } + + while (sample) { + +#ifdef SIMPLE_HMI_INIT + + if (address == -1 || inter == -1 || dma == -1) { + error = sosDIGIDetectInit((LPSTR)NULL); + if (error) { + printf("Cannot initialize detection system (%d).\n", error); + Get_Key(); + break; + } + + error = sosDIGIDetectFindHardware(sample, &DigiCaps, &port); + if (error) { + printf("Cannot find sound card specified.\n"); + Get_Key(); + break; + } + + /* + ** Handle the override for Address, Interrupt, and DMA settings. + */ + error = sosDIGIDetectGetSettings(&DigiHardware); + sosDIGIDetectUnInit(); + if (error) { + printf("Cannot get sound card settings.\n"); + Get_Key(); + break; + } + } else { + DigiHardware.wPort = address; + DigiHardware.wIRQ = inter; + DigiHardware.wDMA = dma; + } +#endif + + /* + ** Initialize the digi-system and driver. + */ + sosDIGIInitSystem((LPSTR)NULL, _SOS_DEBUG_NORMAL); + init.wBufferSize = 1024*8; // Specify the DMA buf size + init.lpBuffer = (LPSTR)NULL; + init.wAllocateBuffer = TRUE; + init.wSampleRate = rate; // Sample playback rate. + init.wParam = 19; + init.dwParam = 0; + init.lpFillHandler = NULL; + init.lpDriverMemory = (LPSTR)NULL; + init.lpTimerMemory = (LPSTR)NULL; + init.wTimerID = _SOS_NORMAL_TIMER; + + error = 0; + error = sosDIGIInitDriver( (int)sample, &DigiHardware, &init, &LockedData.DigiHandle); + if (error) { + printf("Unable to initialize the sound driver.\n"); + Get_Key(); + break; + } + + error = 0; + + + if (sample >= _GUS_8_MONO && sample <= _GUS_16_ST) { + error = sosTIMERRegisterEvent(120, init.lpFillHandler, &DigiTimer); + } else { + error = sosTIMERRegisterEvent(60, init.lpFillHandler, &DigiTimer); + } + if (error) { + printf("Unable to regiser the fill handler.\n"); + Get_Key(); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + LockedData.MaxSamples = max_samples; + + /* + ** Allocate private staging buffers for double buffering sound into HMI + ** driver. + */ + DigiBuffer = Alloc(2048+(((LONG)LockedData.MaxSamples*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)), (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].Buffer[0] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].Buffer[1] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2+1) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].FileHandle = ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + } + error = sosTIMERRegisterEvent(MAINTENANCE_RATE, (void (__far *)(void))maintenance_callback, &MaintainTimer); + if (error) { + printf("Unable to initialize the maintenance callback.\n"); + Get_Key(); + sosTIMERRemoveEvent(DigiTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + break; + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + *=============================================================================================*/ +VOID Sound_End(VOID) +{ + + if (LockedData.DigiHandle != -1) { + sosTIMERRemoveEvent(DigiTimer); + sosTIMERRemoveEvent(MaintainTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + sosDIGIUnInitSystem(); + LockedData.DigiHandle = -1; + } + if (DigiBuffer) { + Free(DigiBuffer); + DigiBuffer = 0; + } + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + sosTIMERRemoveEvent(SystemTimer); + sosTIMERUnInitSystem(0); + Unlock_Locked_Data(); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +VOID Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < LockedData.MaxSamples) { + + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + + LockedData.SampleTracker[handle].Active = FALSE; + if (!LockedData.SampleTracker[handle].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[handle].Original, LockedData.SampleTracker[handle].OriginalSize); + LockedData.SampleTracker[handle].Original = NULL; + } + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + sosDIGIStopSample(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle); + } + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= LockedData.MaxSamples) return(FALSE); + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + return (!sosDIGISampleDone(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle)); +} + + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + if (!sample) return FALSE; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + return (TRUE); + } + } + return (FALSE); +} + + +VOID Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = LockedData.MaxSamples - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + if (id == LockedData.MaxSamples) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + if (id == -1) { + return -1; + } + if (LockedData.SampleTracker[id].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = ERROR; + } + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + *=============================================================================================*/ +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) +{ + AUDHeaderType RawHeader; + _SOS_START_SAMPLE start; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + if (!sample || LockedData.DigiHandle == -1) { + return(-1); + } + + if (id == -1) { + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + st = &LockedData.SampleTracker[id]; + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + if (!st->IsScore) { + DPMI_Lock(st->Original, st->OriginalSize); + } + st->Priority = priority; + st->DontTouch = FALSE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** Fill in one or both staging buffers if possible. + */ + _disable(); +// Disable_Timer_Interrupt(); + if (SFX_MINI_STAGE_BUFFER_SIZE == + Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[0], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen)) { + + st->DataLength = Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[1], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + st->Index = 1; + } else { + st->Index = 0; + st->DataLength = 0; + } + _enable(); +// Enable_Timer_Interrupt(); + + /* + ** Fill in the HMI start sample structure. + */ + memset(&start, 0, sizeof(start)); + + start.lpSamplePtr = (LPSTR)st->Buffer[0]; + if (st->Index == 1) { + start.dwSampleSize = SFX_MINI_STAGE_BUFFER_SIZE-1; + } else { + start.dwSampleSize = st->DataLength-1; + } + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + start.wSampleID = id; + + /* + ** Adjust pitch shifting as necessary so that lower playback + ** samples can be supported. + */ + if (RawHeader.Rate != LockedData.Rate) { + ldiv_t result; + + result = ldiv((long)RawHeader.Rate, LockedData.Rate); + start.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, (short)(((long)result.rem * 0x10000L) / LockedData.Rate)); + start.wSampleFlags |= _PITCH_SHIFT; + } + + /* + ** Sample translation flag. + */ + + if (RawHeader.Flags & AUD_FLAG_16BIT) { + if (Bits_Per_Sample == 8) { + start.wSampleFlags |= _TRANSLATE16TO8; + } + } else { + if (Bits_Per_Sample == 16) { + start.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* + ** Sample stereo flag. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + start.wChannel = _INTERLEAVED; + start.wSampleFlags |= _STEREOTOMONO; + } else { + start.wChannel = _CENTER_CHANNEL; + } + + /* + ** Sample volume control flags. Always give it volume control because + ** if not, then future volume control is either ignored or stops the + ** sample. + */ + st->Volume = volume << 7; + start.wVolume = (st->Volume >> 8) * LockedData.SoundVolume; + start.wSampleFlags |= _VOLUME; + + /* + ** If we have defined a panning location for the sound driver than + ** take care of it here. Panning will only work with a stereo driver + ** and only if the sample is played _CENTER_CHANNEL. + */ + if ((panloc != 0x0) && (!(RawHeader.Flags & AUD_FLAG_STEREO))) { + start.wChannel = _CENTER_CHANNEL; + panloc = ((ReverseChannels) ? ((-panloc) + 0x8000) : ((panloc) + 0x8000)); + start.wSampleFlags |= _PANNING; + start.wSamplePanLocation= panloc; + } + + st->Stereo = start.wChannel; + st->Pitch = start.dwSamplePitchAdd; + st->Flags = start.wSampleFlags; + + /* + ** Start the sample playing now. + */ + _disable(); // NEW +// Disable_Timer_Interrupt(); + st->Handle = sosDIGIStartSample(LockedData.DigiHandle, &start); + if (st->Handle == -1) { + id = -1; + } else { + + /* + ** Fill in the sample tracker structure with those values that are + ** determined AFTER the sample starts. + */ + st->Active = TRUE; + } + _enable(); // NEW +// Enable_Timer_Interrupt(); + + return(id); +} + + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +int Set_Score_Vol(int volume) +{ + int old; + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + return(old); +} + + +VOID Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + + st = &LockedData.SampleTracker[handle]; + st->Reducer = (st->Volume / ticks)+1; + } + } +} +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length * + * of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} diff --git a/WWFLAT32/AUDIO/NEW/SOUNDLCK.CPP b/WWFLAT32/AUDIO/NEW/SOUNDLCK.CPP new file mode 100644 index 0000000..23c6a86 --- /dev/null +++ b/WWFLAT32/AUDIO/NEW/SOUNDLCK.CPP @@ -0,0 +1,147 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDLCK.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "soundint.h" +#include "wwmem.h" + +LockedDataType LockedData; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * INIT_LOCKED_DATA -- Initializes sound driver locked data * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void Init_Locked_Data(void) +{ + /* + ** Initialize all of the data elements that need to be locked. + */ + LockedData.DigiHandle = -1; + LockedData.ServiceSomething = FALSE; + LockedData.MagicNumber = 0xDEAF; + LockedData.UncompBuffer = NULL; + LockedData.StreamBufferSize = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + LockedData.StreamBufferCount = 32; + LockedData.SoundVolume = 255; + LockedData.ScoreVolume = 255; + LockedData._int = FALSE; + LockedData.MaxSamples = MAX_SFX; + + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Lock(&LockedData, 4096L); + DPMI_Lock(Simple_Copy, 4096L); + DPMI_Lock(Sample_Copy, 4096L); + DPMI_Lock((void *)maintenance_callback, 4096L); + DPMI_Lock((void *)DigiCallback, 4096L); + DPMI_Lock((void *)HMI_TimerCallback, 4096L); + DPMI_Lock(Audio_Add_Long_To_Pointer, 4096L); + DPMI_Lock(DPMI_Unlock, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Lock(Mem_Copy, 4096L); + DPMI_Lock(Audio_Mem_Set, 4096L); + DPMI_Lock(__GETDS, 4096L); + + /* + ** Finally lock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Lock(); + sosCODEC_Lock(); +} + +void Unlock_Locked_Data(void) +{ + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Unlock(&LockedData, 4096L); + DPMI_Unlock(Simple_Copy, 4096L); + DPMI_Unlock(Sample_Copy, 4096L); + DPMI_Unlock((void *)maintenance_callback, 4096L); + DPMI_Unlock((void *)DigiCallback, 4096L); + DPMI_Unlock((void *)HMI_TimerCallback, 4096L); + DPMI_Unlock(Audio_Add_Long_To_Pointer, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Unlock(Mem_Copy, 4096L); + DPMI_Unlock(Audio_Mem_Set, 4096L); + DPMI_Unlock(__GETDS, 4096L); + /* + ** Finally unlock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Unlock(); + sosCODEC_Unlock(); + + /* + ** As a last step go though all of the sample tracker structures and make + ** sure all the samples have been properly unlocked. + */ + for (int id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + } +} + diff --git a/WWFLAT32/AUDIO/NYBBTB.INC b/WWFLAT32/AUDIO/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WWFLAT32/AUDIO/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WWFLAT32/AUDIO/SOS.H b/WWFLAT32/AUDIO/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/WWFLAT32/AUDIO/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WWFLAT32/AUDIO/SOSCODEC.ASM b/WWFLAT32/AUDIO/SOSCODEC.ASM new file mode 100644 index 0000000..490823a --- /dev/null +++ b/WWFLAT32/AUDIO/SOSCODEC.ASM @@ -0,0 +1,871 @@ +; +; 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 . +; + +;**************************************************************************** +;* +;* 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 DD -1,-1,-1,-1,2,4,6,8 + DD -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 DD 7, 8, 9, 10, 11, 12, 13,14 + DD 16, 17, 19, 21, 23, 25, 28, 31 + DD 34, 37, 41, 45, 50, 55, 60, 66 + DD 73, 80, 88, 97, 107, 118, 130, 143 + DD 157, 173, 190, 209, 230, 253, 279, 307 + DD 337, 371, 408, 449, 494, 544, 598, 658 + DD 724, 796, 876, 963, 1060, 1166, 1282, 1411 + DD 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024 + DD 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484 + DD 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899 + DD 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794 + DD 32767 + +wCode DD 0 ; this is to hold index into wCodeStep +dwCODECByteIndex DD 0 ; this is when to stop compressing +dwCODECBytesProcessed DD 0 ; this is how many so far compressed +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 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 sosCODECDecompressData:NEAR + PROC sosCODECDecompressData C NEAR + + ARG sSOSInfo:NEAR PTR + ARG wBytes:DWORD + + 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 + + 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 +;--------------------------------------------------------------------------- + push ebp + movzx edx,[(sCompInfo ebx).wIndex] ;preload index + mov ebp, [dwCODECByteIndex] + mov ecx,[(sCompInfo ebx).dwSampleIndex] ;preload SampleIndex + +??mainloop: + xor eax,eax ;get a new token + test ecx,1 ;odd Sample?? + je short ??fetchToken ; if so get new token + mov ax,[(sCompInfo ebx).wCodeBuf] ;ored with Code + shr eax,4 + jmp short ??calcDifference + align 4 + +??fetchToken: + mov al,[esi] ;put in codebuf + inc esi + mov [(sCompInfo ebx).wCodeBuf],ax + + +??calcDifference: + xor ecx,ecx + and eax,000Fh + mov cx,[(sCompInfo ebx).wStep] ;cx is step value + mov [wCode],eax + jmp [DWORD PTR JumpTable + eax*4] + align 4 + +; note: it is important for speed reasons to keep the order the +; following jumps entries as well as the "align 4" after some of +; the jmp statements + +??7: + ; eax = x + x/2 + x/4 + x/8 = (8*x + 4*x +2*x + x)>>3 = + ; = ( x * ( 8 + 4 + 2 + 1 )) >> 3 = ( x * 15 ) >> 3 + lea ecx,[ecx+ecx*2] + lea eax,[ecx+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??6: + ; eax = x + x / 2 + x / 8 = (8*x + 4*x + x) >> 3 = + ; = ( x * 8 + x * 5 ) >> 8 + lea eax,[ecx+ecx*4] + lea eax,[eax+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??5: + ; eax = x + x / 4 + x / 8 = (8*x + 2*x + x) >> 3 = + ; = ( 8 * x + 3 * x) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??4: + ; eax = x + x / 8 = (8*x + x) >> 3 = (x * 9)>> 3 + lea eax,[ecx+ecx*8] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??3: + ; eax = x/2 + x/4 + x/8 = (4*x + 2*x + x) >> 3 + ; = ( 4x + 3x ) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??2: + ; eax = x/2 + x/8 = (4*x + x) >> 3 + lea eax,[ecx+ecx*4] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??1: + ; eax = x/4 + x/8 = (2x + x )>>8 + lea eax,[ecx+ecx*2] + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??0: + ; eax = x/8 = x >> 3 + mov eax,ecx + sar eax,3 ; now we divide x>>3 + jmp ??save_dif + align 4 + +??15: + ; eax = x + x/2 + x/4 + x/8 = (8*x + 4*x +2*x + x)>>3 = + ; = ( x * ( 8 + 4 + 2 + 1 )) >> 3 = ( x * 15 ) >> 3 + lea ecx,[ecx+ecx*2] + lea eax,[ecx+ecx*4] + jmp ??neg_save_dif + align 4 + +??14: + ; eax = x + x / 2 + x / 8 = (8*x + 4*x + x) >> 3 = + ; = ( x * 8 + x * 5 ) >> 8 + lea eax,[ecx+ecx*4] + lea eax,[eax+ecx*8] + jmp ??neg_save_dif + align 4 + +??13: + ; eax = x + x / 4 + x / 8 = (8*x + 2*x + x) >> 3 = + ; = ( 8 * x + 3 * x) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*8] + jmp ??neg_save_dif + align 4 + +??12: + ; eax = x + x / 8 = (8*x + x) >> 3 = (x * 9)>> 3 + lea eax,[ecx+ecx*8] + jmp ??neg_save_dif + align 4 + +??11: + ; eax = x/2 + x/4 + x/8 = (4*x + 2*x + x) >> 3 + ; = ( 4*x - 3*x ) >> 3 + lea eax,[ecx+ecx*2] + lea eax,[eax+ecx*4] + jmp ??neg_save_dif + align 4 + +??10: + ; eax = x/2 + x/8 = (4*x + x) >> 3 + lea eax,[ecx+ecx*4] + jmp ??neg_save_dif + align 4 + +??9: + ; eax = x/4 + x/8 = (2x + x )>>8 + lea eax,[ecx+ecx*2] + jmp ??neg_save_dif + align 4 + +??8: + ; eax = x/8 = x >> 3 + mov eax,ecx ; !!important!! no need for align here + +??neg_save_dif: + sar eax,3 ; now we divide x>>3 + neg eax + +??save_dif: + mov ecx,[wCode] ; load offset into CODETab table + mov [(sCompInfo ebx).dwDifference],eax ;Store wStep + + ; add difference to predicted value. + add eax,[(sCompInfo ebx).dwPredicted] + + ; 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: + add edx,[wCODECIndexTab + ecx*4] ; won't hurt 486 + cmp [(sCompInfo ebx).wBitSize],16 + mov [(sCompInfo ebx).dwPredicted],eax + mov ecx,[(sCompInfo ebx).dwSampleIndex] ;load dwSampleindex + je short ??output16Bit + ; output 8 bit sample + xor ah,80h + mov [edi],ah + inc edi + jmp short ??adjustIndex + align 4 + +??output16Bit: + mov [edi],ax ;Output 16bit sample + add edi,2 + +??adjustIndex: + cmp edx,8000h + jb short ??checkOverflow + mov edx,0 ;reset index to zero + +??checkOverflow: + inc ecx ; advance index and store step value + cmp edx,88 ;check if wIndex > 88 + jbe short ??adjustStep + mov edx,88 ;reset index to 88 + +??adjustStep: + ; advance index and store step value + mov [(sCompInfo ebx).dwSampleIndex],ecx + + ; fetch wIndex so we can fetch new step value + mov eax,[wCODECStepTab + edx*4] + + ; decrement bytes processed and loop back. + dec ebp + mov [(sCompInfo ebx).wStep],ax + jne ??mainloop + pop ebp + + mov eax,[wCode] ; these three lines do not + mov [(sCompInfo ebx).wCode],ax ; seem to have any relevance + + mov [(sCompInfo ebx).wIndex],dx ; save index + 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,2 + mov eax,[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,2 + mov eax,[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,2 + mov eax,[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,2 + mov eax,[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 + +JumpTable DD ??0 + DD ??1 + DD ??2 + DD ??3 + DD ??4 + DD ??5 + DD ??6 + DD ??7 + DD ??8 + DD ??9 + DD ??10 + DD ??11 + DD ??12 + DD ??13 + DD ??14 + DD ??15 + + + ENDP 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 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 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 + diff --git a/WWFLAT32/AUDIO/SOSCOMP.H b/WWFLAT32/AUDIO/SOSCOMP.H new file mode 100644 index 0000000..7334764 --- /dev/null +++ b/WWFLAT32/AUDIO/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif + diff --git a/WWFLAT32/AUDIO/SOSDATA.H b/WWFLAT32/AUDIO/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WWFLAT32/AUDIO/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WWFLAT32/AUDIO/SOSDEFS.H b/WWFLAT32/AUDIO/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WWFLAT32/AUDIO/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WWFLAT32/AUDIO/SOSDW1PS.LIB b/WWFLAT32/AUDIO/SOSDW1PS.LIB new file mode 100644 index 0000000..775054e Binary files /dev/null and b/WWFLAT32/AUDIO/SOSDW1PS.LIB differ diff --git a/WWFLAT32/AUDIO/SOSFNCT.H b/WWFLAT32/AUDIO/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WWFLAT32/AUDIO/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WWFLAT32/AUDIO/SOSRES.H b/WWFLAT32/AUDIO/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WWFLAT32/AUDIO/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WWFLAT32/AUDIO/SOUND.BAK b/WWFLAT32/AUDIO/SOUND.BAK new file mode 100644 index 0000000..ad0d223 --- /dev/null +++ b/WWFLAT32/AUDIO/SOUND.BAK @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 4 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + +#endif diff --git a/WWFLAT32/AUDIO/SOUND.H b/WWFLAT32/AUDIO/SOUND.H new file mode 100644 index 0000000..ca20107 --- /dev/null +++ b/WWFLAT32/AUDIO/SOUND.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + +#endif diff --git a/WWFLAT32/AUDIO/SOUNDINT.BAK b/WWFLAT32/AUDIO/SOUNDINT.BAK new file mode 100644 index 0000000..0611f8a --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDINT.BAK @@ -0,0 +1,549 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#include "soundint.h" +//#include "mem.h" + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + +/*************************************************************************** + * SAVE_MY_REGS -- Inline function which will save assembly regs * + * * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux save_my_regs = \ + "pushfd" \ + "pushad" \ + "push ds" \ + "push es" \ + "push fs" \ + "push gs" \ + "push ds" \ + "pop es"; + +#pragma aux enable = \ + "sti"; + +#pragma aux disable = \ + "cli"; + +/*************************************************************************** + * RESTORE_MY_REGS -- Inline function which will restore saved registers * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux restore_my_regs = \ + "pop gs" \ + "pop fs" \ + "pop es" \ + "pop ds" \ + "popad" \ + "popfd"; + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + sosCODECDecompressData(&st->sosinfo, dsize); + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + +VOID far cdecl maintenance_callback(VOID) +{ + save_my_regs(); + int index; + SampleTrackerType *st; + + if (!LockedData._int && LockedData.DigiHandle != -1 && LockedData.ServiceSomething) { + + LockedData._int++; + enable(); + LockedData.ServiceSomething = FALSE; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service) { + if (st->DontTouch) { + LockedData.ServiceSomething = TRUE; + } else { + st->Service = FALSE; + +#if(FALSE) + st->DataLength = SFX_MINI_STAGE_BUFFER_SIZE; +#else + st->DataLength = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[st->Index], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + +#endif + } + } + + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + LockedData._int--; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + + if (st->IsScore) { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.ScoreVolume); + } else { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.SoundVolume); + } + } + st++; + } + LockedData._int--; + } + restore_my_regs(); +} +/*********************************************************************************************** + * DigiCallback -- Low level double buffering handler. * + * * + * This routine is called in an interrupt to handle the double * + * buffering of digital audio. This routine is the interface between * + * the buffers maintained by Sound_Callback() and the HMI driver * + * itself. * + * * + * INPUT: driverhandle -- The handle to the HMI driver. * + * * + * callsource -- Code indicating the reason for the callback. * + * * + * sampleid -- The ID number of the sample itself. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is called in an interrupt so it return as quickly as * + * possible. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid) +{ + SampleTrackerType *st; + int index; + + + /* + ** Find the correct control structure for the handle specified. + */ + for (index = 0; index < LockedData.MaxSamples; index++) { + st = &LockedData.SampleTracker[index]; + if (st->Active && st->Handle == sampleid) { + break; + } + } + if (index == LockedData.MaxSamples) { + return; + } + + if (driverhandle == LockedData.DigiHandle) { + + switch (callsource) { + + /* + ** The sample is now no longer audible. Don't stop the sample + ** tracking if a servicing is needed. If it is needed then + ** obviously the sample isn't quite done. + */ + case _SAMPLE_DONE: + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + break; + + /* + ** The sample is finished processing, but not necessarily finished playing. + */ + case _SAMPLE_PROCESSED: + if (st->DataLength && st->Active) { + _SOS_START_SAMPLE start; + long dlength; + + dlength = st->DataLength; + st->DataLength = 0; + + Audio_Mem_Set(&start, 0, sizeof(start)); + start.lpSamplePtr = (LPSTR)st->Buffer[st->Index]; + + start.dwSampleSize = dlength-1; + + start.wSampleFlags = st->Flags; + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + if (st->IsScore) { + start.wVolume = (st->Volume>>8) * LockedData.ScoreVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } else { + start.wVolume = (st->Volume>>8) * LockedData.SoundVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } + + start.wChannel = st->Stereo; + start.wSampleID = index+1; + start.dwSamplePitchAdd = st->Pitch; + st->Index ^= 1; + + if (st->Remainder || st->QueueBuffer || st->Callback || st->FilePending) { + st->Service = TRUE; + LockedData.ServiceSomething = TRUE; + } + + sosDIGIContinueSample(LockedData.DigiHandle, st->Handle, &start); + } else { + + /* + ** This is necessary because Stop_Sample may screw things + ** up, otherwise. Can't rely on the _SAMPLE_DONE call. + */ + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + + } + break; + + /* + ** Sample is now looping (not used). + */ + case _SAMPLE_LOOPING: + break; + } + } + +} + +void far HMI_TimerCallback(void) +{ +} + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WWFLAT32/AUDIO/SOUNDINT.CPP b/WWFLAT32/AUDIO/SOUNDINT.CPP new file mode 100644 index 0000000..0611f8a --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDINT.CPP @@ -0,0 +1,549 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 28, 1995 [PWG] * + * * + * This module contains all of the functions that are used within our * + * sound interrupt. They are stored in a seperate module because memory * + * around these functions must be locked or they will cause a read to * + * be generated while in an interrupt. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Simple_Copy -- Copyies 1 or 2 source chuncks to a dest * + * Sample_Copy -- Copies sound data from source format to raw format. * + * DigiCallback -- Low level double buffering handler. * + * save_my_regs -- Inline function which will save assembly regs * + * restore_my_regs -- Inline function which will restore saved registes * + * Audio_Add_Long_To_Pointer -- Adds an offset to a ptr casted void * + * Init_Locked_Data -- Initializes sound driver locked data * + * Audio_Mem_Set -- Quick see routine to set memory to a value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#pragma pack(4) +#include "soundint.h" +//#include "mem.h" + +/*************************************************************************** +** All routines past this point must be locked for the sound driver to ** +** function under a VCPI memory manager. These locks are unnecessary if ** +** the driver does not have to run under windows or does not use virtual ** +** memory. ** +***************************************************************************/ + +/*************************************************************************** + * SAVE_MY_REGS -- Inline function which will save assembly regs * + * * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux save_my_regs = \ + "pushfd" \ + "pushad" \ + "push ds" \ + "push es" \ + "push fs" \ + "push gs" \ + "push ds" \ + "pop es"; + +#pragma aux enable = \ + "sti"; + +#pragma aux disable = \ + "cli"; + +/*************************************************************************** + * RESTORE_MY_REGS -- Inline function which will restore saved registers * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +#pragma aux restore_my_regs = \ + "pop gs" \ + "pop fs" \ + "pop es" \ + "pop ds" \ + "popad" \ + "popfd"; + +/*************************************************************************** + * SIMPLE_COPY -- Copyies 1 or 2 source chuncks to a dest * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size) +{ + + + long out = 0; // Number of bytes copied to the destination. + + /* + ** It could happen that entering this routine, the source buffer + ** has been exhausted, but the alternate buffer is still valid. + ** Move the alternate into the primary position before proceeding. + */ + if (!(*ssize)) { + *source = *alternate; + *ssize = *altsize; + *alternate = NULL; + *altsize = 0; + } + + if (*source && *ssize) { + long s; // Scratch length var. + + /* + ** Copy as much as possible from the primary source, but no + ** more than the primary source has to offer. + */ + s = size; + if (*ssize < s) s = *ssize; + Mem_Copy(*source, *dest, s); + *source = Audio_Add_Long_To_Pointer(*source, s); + *ssize -= s; + *dest = Audio_Add_Long_To_Pointer(*dest, s); + size -= s; + out += s; + + /* + ** If the primary source was insufficient to fill the request, then + ** move the alternate into the primary position and try again. + */ + if (size) { + *source = *alternate; + *ssize = *altsize; + *alternate = 0; + *altsize = 0; + out += Simple_Copy(source, ssize, alternate, altsize, dest, size); + } + } + + return(out); +} + +/*********************************************************************************************** + * Sample_Copy -- Copies sound data from source format to raw format. * + * * + * This routine is used to copy the sound data (possibly compressed) to the destination * + * buffer in raw format. * + * * + * INPUT: source -- Pointer to the source data (possibly compressed). * + * * + * dest -- Pointer to the destination buffer. * + * * + * size -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes placed into the output buffer. This is usually * + * the number of bytes requested except in the case when the source is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/03/1994 JLB : Created. * + * 09/04/1994 JLB : Revamped entirely. * + *=============================================================================================*/ +#pragma argsused +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize) +{ + + long s; + long datasize = 0; // Output bytes. + + switch (scomp) { + default: + + case SCOMP_NONE: + datasize = Simple_Copy(source, ssize, alternate, altsize, &dest, size); + break; + + case SCOMP_WESTWOOD: + case SCOMP_SOS: + while (size > 0) { + + /* + ** The block spans two buffers. It must be copied down to + ** a staging area before it can be decompressed. + */ + { + long magic; + unsigned short fsize; + unsigned short dsize; + void *fptr; + void *dptr; + void *mptr; + + fptr = &fsize; + dptr = &dsize; + mptr = &magic; + + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, sizeof(fsize)); + if (s < sizeof(fsize)) { + return datasize; + } + s = Simple_Copy(source, ssize, alternate, altsize, &dptr, sizeof(dsize)); + if (s < sizeof(dsize) || size < dsize) { + return datasize; + } + + s = Simple_Copy(source, ssize, alternate, altsize, &mptr, sizeof(magic)); + if (s < sizeof(magic) || magic != LockedData.MagicNumber) { + return datasize; + } + + /* + ** If the frame and uncompressed data size are identical, then this + ** indicates that the frame is not compressed. Just copy it directly + ** to the destination buffer in this case. + */ + if (fsize == dsize) { + s = Simple_Copy(source, ssize, alternate, altsize, &dest, fsize); + if (s < dsize) { + return (datasize); + } + } else { + + /* + ** The frame was compressed, so copy it to the staging buffer, and then + ** uncompress it into the final destination buffer. + */ + fptr = LockedData.UncompBuffer; + s = Simple_Copy(source, ssize, alternate, altsize, &fptr, fsize); + if (s < fsize) { + return (datasize); + } + if (scomp == SCOMP_WESTWOOD) { + Decompress_Frame(LockedData.UncompBuffer, dest, dsize); + } else { + st->sosinfo.lpSource = (char *)LockedData.UncompBuffer; + st->sosinfo.lpDest = (char *)dest; + sosCODECDecompressData(&st->sosinfo, dsize); + } + dest = Audio_Add_Long_To_Pointer(dest, dsize); + } + datasize += dsize; + size -= dsize; + } + } + + break; + } + return(datasize); +} + +VOID far cdecl maintenance_callback(VOID) +{ + save_my_regs(); + int index; + SampleTrackerType *st; + + if (!LockedData._int && LockedData.DigiHandle != -1 && LockedData.ServiceSomething) { + + LockedData._int++; + enable(); + LockedData.ServiceSomething = FALSE; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + if (st->Active) { + + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. If the source is + ** compressed, then this will also uncompress it as the copy + ** is performed. + */ + if (st->Service) { + if (st->DontTouch) { + LockedData.ServiceSomething = TRUE; + } else { + st->Service = FALSE; + +#if(FALSE) + st->DataLength = SFX_MINI_STAGE_BUFFER_SIZE; +#else + st->DataLength = Sample_Copy( st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[st->Index], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + +#endif + } + } + + /* + ** For file streamed samples, fill the queue pointer if needed. + ** This allows for delays in calling the Sound_Callback function. + */ + if (!st->DontTouch && !st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Audio_Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + LockedData._int--; + } + + + if (!LockedData._int) { + + LockedData._int++; + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + + /* + ** If there are any samples that require fading, then do so at this + ** time. + */ + if (st->Active && st->Reducer && st->Volume) { + if (st->Reducer >= st->Volume) { + st->Volume = 0; + } else { + st->Volume -= st->Reducer; + } + + if (st->IsScore) { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.ScoreVolume); + } else { + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, (st->Volume>>8) * LockedData.SoundVolume); + } + } + st++; + } + LockedData._int--; + } + restore_my_regs(); +} +/*********************************************************************************************** + * DigiCallback -- Low level double buffering handler. * + * * + * This routine is called in an interrupt to handle the double * + * buffering of digital audio. This routine is the interface between * + * the buffers maintained by Sound_Callback() and the HMI driver * + * itself. * + * * + * INPUT: driverhandle -- The handle to the HMI driver. * + * * + * callsource -- Code indicating the reason for the callback. * + * * + * sampleid -- The ID number of the sample itself. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is called in an interrupt so it return as quickly as * + * possible. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid) +{ + SampleTrackerType *st; + int index; + + + /* + ** Find the correct control structure for the handle specified. + */ + for (index = 0; index < LockedData.MaxSamples; index++) { + st = &LockedData.SampleTracker[index]; + if (st->Active && st->Handle == sampleid) { + break; + } + } + if (index == LockedData.MaxSamples) { + return; + } + + if (driverhandle == LockedData.DigiHandle) { + + switch (callsource) { + + /* + ** The sample is now no longer audible. Don't stop the sample + ** tracking if a servicing is needed. If it is needed then + ** obviously the sample isn't quite done. + */ + case _SAMPLE_DONE: + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + break; + + /* + ** The sample is finished processing, but not necessarily finished playing. + */ + case _SAMPLE_PROCESSED: + if (st->DataLength && st->Active) { + _SOS_START_SAMPLE start; + long dlength; + + dlength = st->DataLength; + st->DataLength = 0; + + Audio_Mem_Set(&start, 0, sizeof(start)); + start.lpSamplePtr = (LPSTR)st->Buffer[st->Index]; + + start.dwSampleSize = dlength-1; + + start.wSampleFlags = st->Flags; + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + if (st->IsScore) { + start.wVolume = (st->Volume>>8) * LockedData.ScoreVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } else { + start.wVolume = (st->Volume>>8) * LockedData.SoundVolume; + sosDIGISetSampleVolume(LockedData.DigiHandle, st->Handle, start.wVolume); + } + + start.wChannel = st->Stereo; + start.wSampleID = index+1; + start.dwSamplePitchAdd = st->Pitch; + st->Index ^= 1; + + if (st->Remainder || st->QueueBuffer || st->Callback || st->FilePending) { + st->Service = TRUE; + LockedData.ServiceSomething = TRUE; + } + + sosDIGIContinueSample(LockedData.DigiHandle, st->Handle, &start); + } else { + + /* + ** This is necessary because Stop_Sample may screw things + ** up, otherwise. Can't rely on the _SAMPLE_DONE call. + */ + st->Active = FALSE; + if (!st->IsScore) { +// DPMI_Unlock(st->Original, st->OriginalSize); + } + + } + break; + + /* + ** Sample is now looping (not used). + */ + case _SAMPLE_LOOPING: + break; + } + } + +} + +void far HMI_TimerCallback(void) +{ +} + + +/*************************************************************************** + * ADD_LONG_TO_POINTER -- Adds an offset to a ptr casted void * + * * + * INPUT: void * ptr - the pointer to add to * + * long size - the size to add to it * + * * + * OUTPUT: void * ptr - the new location it will point to * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ + +void *Audio_Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +/*************************************************************************** + * AUDIO_MEM_SET -- Quick see routine to set memory to a value * + * * + * INPUT: void const * - the memory that needs to be set * + * unsigned char - the value to set the memory to * + * long size - how big an area to set * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/28/1995 PWG : Created. * + *=========================================================================*/ +void Audio_Mem_Set(void const *ptr, unsigned char value, long size) +{ + unsigned char *temp = (unsigned char *)ptr; + for (int lp = 0; lp < size; lp ++) { + *temp++ = value; + } +} diff --git a/WWFLAT32/AUDIO/SOUNDINT.H b/WWFLAT32/AUDIO/SOUNDINT.H new file mode 100644 index 0000000..49ef523 --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDINT.H @@ -0,0 +1,253 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + unsigned Loading:1; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + unsigned DontTouch:1; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + unsigned IsScore:1; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers in low ram. + */ + VOID *Buffer[2]; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ + WORD Index; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + WORD Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + WORD Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + WORD Volume; + WORD Reducer; // Amount to reduce volume per tick. + + /* + ** This flags whether the sample is in stereo. + */ + WORD Stereo; + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + WORD TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + WORD Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(WORD id, WORD *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + WORD Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + WORD FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + _SOS_COMPRESS_INFO sosinfo; + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; + int MaxSamples; + int Rate; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +void Unlock_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize); +VOID far cdecl maintenance_callback(VOID); +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void Audio_Mem_Set(void const *ptr, unsigned char value, long size); + void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + long Decompress_Frame(void * source, void * dest, long size); + int Decompress_Frame_Lock(void); + int Decompress_Frame_Unlock(void); + int sosCODEC_Lock(void); + int sosCODEC_Unlock(void); + void __GETDS(void); +} + diff --git a/WWFLAT32/AUDIO/SOUNDIO.BAK b/WWFLAT32/AUDIO/SOUNDIO.BAK new file mode 100644 index 0000000..c2c1dae --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDIO.BAK @@ -0,0 +1,1467 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * File_Callback -- called to fill queue buffer for streaming sample * + * Stream_Sample_Volume -- generic streaming sample playback init * + * Stream_Sample -- generic streaming sample playback init * + * File_Stream_Sample -- Streams a sample directly from a file. * + * File_Stream_Preload -- Handles initial proload of a streaming samples * + * File_Stream_Sample_Volume -- Streams a sample directly from a file w/volume. * + * Sound_Callback -- Audio driver callback function. * + * Load_Sample -- Loads a digitized sample into RAM. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Free_Sample -- Frees a previously loaded digitized sample. * + * Sound_End -- Uninitializes the sound driver. * + * Stop_Sample -- Stops any currently playing sampled sound. * + * Sample_Status -- Queries the current playing sample status (if any). * + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma pack(4) +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + + +//int Mono_Printf(char const *string, ...); +#include + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This define (if present) enables the simple HMI hardware initialization process. +** The process does not do auto detection, but rather takes the value directly from +** the setup program and uses that as the sound card number. The only "detection" it +** does is to recognized the presence of the card and fetch its settings. +*/ +#define SIMPLE_HMI_INIT + + +/* +** This is the rate that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 60 + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD MidiHandle = -1; +static unsigned int far DigiTimer = 0; +static unsigned int far MaintainTimer = 0; +static unsigned int far SystemTimer = 0; +static int Bits_Per_Sample; +VOID *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +short StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size); +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle); +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size)); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + + +/*************************************************************************** + * FILE_CALLBACK -- called to fill queue buffer for streaming sample * + * * + * This callback is called whenever the queue buffer playback has begun * + * and another buffer is needed for queuing up. Returns TRUE if there * + * is more data to read from the file. * + * * + * INPUT: WORD id - the sample id number * + * WORD *odd - which sample buffer to put info in * + * VOID **buffer - the buffer pointer to load data into * + * LONG *size - the amount to load * + * * + * OUTPUT: BOOL true if more data to load, FALSE if done loading * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + VOID *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + st->DontTouch = TRUE; + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = *odd + 1; + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + + + while (num_empty_buffers && st->FileHandle != ERROR) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + maintenance_callback(); + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + return(TRUE); + } + } + } + return(FALSE); +} + +/*************************************************************************** + * STREAM_SAMPLE_VOLUME -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; + } + } + return (playid); +} + +/*************************************************************************** + * STREAM_SAMPLE -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} + + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT: LockedData.SampleTracker * to the header which tracks this samples * + * processing. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //printf("Before buffer load!\n"); + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + //printf("Reading block of size %d Stream Buffer = %d\n"); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + //printf("Before Stream Sample Volume!\n"); + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + { + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + + StartingFileStream = TRUE; + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + } + //printf("After Stream Sample Volume!\n"); + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } +} +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static VOID *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + long size; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + //printf("Before initialize memory!\n"); + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + //printf("Before Open File!\n"); + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + //printf("Before Get Free Handle!\n"); + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= LockedData.MaxSamples) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + //printf("Before Preload!\n"); + File_Stream_Preload(handle); + } + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl __saveregs __loadds Sound_Callback(VOID) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + Stop_Sample(index); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + if ((!st->QueueBuffer || + (st->FileHandle != ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback(index, &st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != ERROR) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +VOID *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + VOID *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* + Conversion formula for TCrate and Hz rate. + + TC = 256 - 1m/rate + rate = 1m / (256-TC) +*/ + + if (!buffer || fh == ERROR || size <= sizeof(RawHeader)) return(NULL); + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + + actual_bytes_read = + Read_File(fh, &RawHeader, sizeof(RawHeader)); + + actual_bytes_read += + Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + + + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +VOID Free_Sample(VOID const *sample) +{ + if (sample) Free((void *)sample); +} + +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + return Audio_Init(sample, -1, -1, -1, rate, bits_per_sample, max_samples, reverse_channels); +} + +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + int error; // Function error return code. + unsigned int port; + int index; + _SOS_INIT_DRIVER init; // Driver init structure. + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + LockedData.Rate = rate; + + Bits_Per_Sample = bits_per_sample; + + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + + if (TimerSystemOn) { + sosTIMERRegisterEvent(60, &Timer_Interrupt_Func, &SystemTimer); + TickCount.Start(); + } else + sosTIMERRegisterEvent(60, &HMI_TimerCallback, &SystemTimer); + + /* + ** Special code to handle the HMI problem with running under Windows. + */ + if (/*Operating_System() != OS_DOS ||*/ sample == SAMPLE_NONE) { + return(FALSE); + } + + while (sample) { + +#ifdef SIMPLE_HMI_INIT + + if (address == -1 || inter == -1 || dma == -1) { + error = sosDIGIDetectInit((LPSTR)NULL); + if (error) { + printf("Cannot initialize detection system (%d).\n", error); + Get_Key(); + break; + } + + error = sosDIGIDetectFindHardware(sample, &DigiCaps, &port); + if (error) { + printf("Cannot find sound card specified.\n"); + Get_Key(); + break; + } + + /* + ** Handle the override for Address, Interrupt, and DMA settings. + */ + error = sosDIGIDetectGetSettings(&DigiHardware); + sosDIGIDetectUnInit(); + if (error) { + printf("Cannot get sound card settings.\n"); + Get_Key(); + break; + } + } else { + DigiHardware.wPort = address; + DigiHardware.wIRQ = inter; + DigiHardware.wDMA = dma; + } +#endif + + /* + ** Initialize the digi-system and driver. + */ + sosDIGIInitSystem((LPSTR)NULL, _SOS_DEBUG_NORMAL); + init.wBufferSize = 1024*8; // Specify the DMA buf size + init.lpBuffer = (LPSTR)NULL; + init.wAllocateBuffer = TRUE; + init.wSampleRate = rate; // Sample playback rate. + init.wParam = 19; + init.dwParam = 0; + init.lpFillHandler = NULL; + init.lpDriverMemory = (LPSTR)NULL; + init.lpTimerMemory = (LPSTR)NULL; + init.wTimerID = _SOS_NORMAL_TIMER; + + error = 0; + error = sosDIGIInitDriver( (int)sample, &DigiHardware, &init, &LockedData.DigiHandle); + if (error) { + printf("Unable to initialize the sound driver.\n"); + Get_Key(); + break; + } + + error = 0; + + + if (sample >= _GUS_8_MONO && sample <= _GUS_16_ST) { + error = sosTIMERRegisterEvent(120, init.lpFillHandler, &DigiTimer); + } else { + error = sosTIMERRegisterEvent(60, init.lpFillHandler, &DigiTimer); + } + if (error) { + printf("Unable to regiser the fill handler.\n"); + Get_Key(); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + LockedData.MaxSamples = max_samples; + + /* + ** Allocate private staging buffers for double buffering sound into HMI + ** driver. + */ + DigiBuffer = Alloc(2048+(((LONG)LockedData.MaxSamples*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)), (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].Buffer[0] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].Buffer[1] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2+1) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].FileHandle = ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + } + error = sosTIMERRegisterEvent(MAINTENANCE_RATE, (void (__far *)(void))maintenance_callback, &MaintainTimer); + if (error) { + printf("Unable to initialize the maintenance callback.\n"); + Get_Key(); + sosTIMERRemoveEvent(DigiTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + break; + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + *=============================================================================================*/ +VOID Sound_End(VOID) +{ + + if (LockedData.DigiHandle != -1) { + sosTIMERRemoveEvent(DigiTimer); + sosTIMERRemoveEvent(MaintainTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + sosDIGIUnInitSystem(); + LockedData.DigiHandle = -1; + } + if (DigiBuffer) { + Free(DigiBuffer); + DigiBuffer = 0; + } + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + sosTIMERRemoveEvent(SystemTimer); + sosTIMERUnInitSystem(0); + Unlock_Locked_Data(); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +VOID Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < LockedData.MaxSamples) { + + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + + LockedData.SampleTracker[handle].Active = FALSE; + if (!LockedData.SampleTracker[handle].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[handle].Original, LockedData.SampleTracker[handle].OriginalSize); + LockedData.SampleTracker[handle].Original = NULL; + } + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + sosDIGIStopSample(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle); + } + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= LockedData.MaxSamples) return(FALSE); + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + return (!sosDIGISampleDone(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle)); +} + + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + if (!sample) return FALSE; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + return (TRUE); + } + } + return (FALSE); +} + + +VOID Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = LockedData.MaxSamples - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + if (id == LockedData.MaxSamples) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + if (id == -1) { + return -1; + } + if (LockedData.SampleTracker[id].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = ERROR; + } + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + *=============================================================================================*/ +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) +{ + AUDHeaderType RawHeader; + _SOS_START_SAMPLE start; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + if (!sample || LockedData.DigiHandle == -1) { + return(-1); + } + + if (id == -1) { + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + st = &LockedData.SampleTracker[id]; + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + if (!st->IsScore) { + DPMI_Lock(st->Original, st->OriginalSize); + } + st->Priority = priority; + st->DontTouch = FALSE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** Fill in one or both staging buffers if possible. + */ + _disable(); +// Disable_Timer_Interrupt(); + if (SFX_MINI_STAGE_BUFFER_SIZE == + Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[0], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen)) { + + st->DataLength = Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[1], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + st->Index = 1; + } else { + st->Index = 0; + st->DataLength = 0; + } + _enable(); +// Enable_Timer_Interrupt(); + + /* + ** Fill in the HMI start sample structure. + */ + memset(&start, 0, sizeof(start)); + + start.lpSamplePtr = (LPSTR)st->Buffer[0]; + if (st->Index == 1) { + start.dwSampleSize = SFX_MINI_STAGE_BUFFER_SIZE-1; + } else { + start.dwSampleSize = st->DataLength-1; + } + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + start.wSampleID = id; + + /* + ** Adjust pitch shifting as necessary so that lower playback + ** samples can be supported. + */ + if (RawHeader.Rate != LockedData.Rate) { + ldiv_t result; + + result = ldiv((long)RawHeader.Rate, LockedData.Rate); + start.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, (short)(((long)result.rem * 0x10000L) / LockedData.Rate)); + start.wSampleFlags |= _PITCH_SHIFT; + } + + /* + ** Sample translation flag. + */ + + if (RawHeader.Flags & AUD_FLAG_16BIT) { + if (Bits_Per_Sample == 8) { + start.wSampleFlags |= _TRANSLATE16TO8; + } + } else { + if (Bits_Per_Sample == 16) { + start.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* + ** Sample stereo flag. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + start.wChannel = _INTERLEAVED; + start.wSampleFlags |= _STEREOTOMONO; + } else { + start.wChannel = _CENTER_CHANNEL; + } + + /* + ** Sample volume control flags. Always give it volume control because + ** if not, then future volume control is either ignored or stops the + ** sample. + */ + st->Volume = volume << 7; + start.wVolume = (st->Volume >> 8) * LockedData.SoundVolume; + start.wSampleFlags |= _VOLUME; + + /* + ** If we have defined a panning location for the sound driver than + ** take care of it here. Panning will only work with a stereo driver + ** and only if the sample is played _CENTER_CHANNEL. + */ + if ((panloc != 0x0) && (!(RawHeader.Flags & AUD_FLAG_STEREO))) { + start.wChannel = _CENTER_CHANNEL; + panloc = ((ReverseChannels) ? ((-panloc) + 0x8000) : ((panloc) + 0x8000)); + start.wSampleFlags |= _PANNING; + start.wSamplePanLocation= panloc; + } + + st->Stereo = start.wChannel; + st->Pitch = start.dwSamplePitchAdd; + st->Flags = start.wSampleFlags; + + /* + ** Start the sample playing now. + */ + _disable(); // NEW +// Disable_Timer_Interrupt(); + st->Handle = sosDIGIStartSample(LockedData.DigiHandle, &start); + if (st->Handle == -1) { + id = -1; + } else { + + /* + ** Fill in the sample tracker structure with those values that are + ** determined AFTER the sample starts. + */ + st->Active = TRUE; + } + _enable(); // NEW +// Enable_Timer_Interrupt(); + + return(id); +} + + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +int Set_Score_Vol(int volume) +{ + int old; + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + return(old); +} + + +VOID Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + + st = &LockedData.SampleTracker[handle]; + st->Reducer = (st->Volume / ticks)+1; + } + } +} +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length * + * of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} diff --git a/WWFLAT32/AUDIO/SOUNDIO.CPP b/WWFLAT32/AUDIO/SOUNDIO.CPP new file mode 100644 index 0000000..066488f --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDIO.CPP @@ -0,0 +1,1467 @@ +/* +** 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 . +*/ + +/* $Id: soundio.cpp 1.41 1994/06/20 15:01:39 joe_bostic Exp $ */ +/*********************************************************************************************** + ** 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 : Sound Library * + * * + * File Name : SOUND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * File_Callback -- called to fill queue buffer for streaming sample * + * Stream_Sample_Volume -- generic streaming sample playback init * + * Stream_Sample -- generic streaming sample playback init * + * File_Stream_Sample -- Streams a sample directly from a file. * + * File_Stream_Preload -- Handles initial proload of a streaming samples * + * File_Stream_Sample_Volume -- Streams a sample directly from a file w/volume. * + * Sound_Callback -- Audio driver callback function. * + * Load_Sample -- Loads a digitized sample into RAM. * + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * Free_Sample -- Frees a previously loaded digitized sample. * + * Sound_End -- Uninitializes the sound driver. * + * Stop_Sample -- Stops any currently playing sampled sound. * + * Sample_Status -- Queries the current playing sample status (if any). * + * Sample_Length -- returns length of a sample in ticks * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma pack(4) +#include "soundint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma pack(1) +#include "audio.h" +#pragma pack(4) + + +//int Mono_Printf(char const *string, ...); +#include + +/* +** If this is defined, then the streaming audio buffer will be filled +** to maximum whenever filling is to occur. If undefined, it will fill +** the streaming buffer in smaller chunks. +*/ +#define SIMPLE_FILLING + +/* +** This define (if present) enables the simple HMI hardware initialization process. +** The process does not do auto detection, but rather takes the value directly from +** the setup program and uses that as the sound card number. The only "detection" it +** does is to recognized the presence of the card and fetch its settings. +*/ +#define SIMPLE_HMI_INIT + + +/* +** This is the rate that the maintenance callback gets called. +*/ +#define MAINTENANCE_RATE 60 + +/* +** Size of the temporary buffer in XMS/EMS that direct file +** streaming of sounds will allocate. +*/ +#define STREAM_BUFFER_SIZE (128L*1024L) + +/* +** Define the number of "StreamBufferSize" blocks that are read in +** at a minimum when the streaming sample load callback routine +** is called. We will IGNORE loads that are less that this in order +** to avoid constant seeking on the CD. +*/ +#define STREAM_CUSHION_BLOCKS 4 + + +/* +** This is the maximum size that a sonarc block can be. All sonarc blocks +** must be either a multiple of this value or a binary root of this value. +*/ +#define LARGEST_SONARC_BLOCK 2048 + + +////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////// structs /////////////////////////////////////// + +static _SOS_CAPABILITIES DigiCaps; +static _SOS_HARDWARE DigiHardware; +static WORD MidiHandle = -1; +static unsigned int far DigiTimer = 0; +static unsigned int far MaintainTimer = 0; +static unsigned int far SystemTimer = 0; +static int Bits_Per_Sample; +VOID *DigiBuffer = NULL; +static BOOL StartingFileStream = FALSE; +short StreamLowImpact = FALSE; +MemoryFlagType StreamBufferFlag = MEM_NORMAL; +int Misc; +SFX_Type SoundType; +Sample_Type SampleType; +int ReverseChannels = FALSE; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size); +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle); +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size)); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + + +/*************************************************************************** + * FILE_CALLBACK -- called to fill queue buffer for streaming sample * + * * + * This callback is called whenever the queue buffer playback has begun * + * and another buffer is needed for queuing up. Returns TRUE if there * + * is more data to read from the file. * + * * + * INPUT: WORD id - the sample id number * + * WORD *odd - which sample buffer to put info in * + * VOID **buffer - the buffer pointer to load data into * + * LONG *size - the amount to load * + * * + * OUTPUT: BOOL true if more data to load, FALSE if done loading * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static BOOL File_Callback(WORD id, WORD *odd, VOID **buffer, LONG *size) +{ + SampleTrackerType *st; // Pointer to sample playback control struct. + VOID *ptr; // Pointer to working portion of file buffer. + + if (id != -1) { + st = &LockedData.SampleTracker[id]; + ptr = st->FileBuffer; + if (ptr) { + + /* + ** Move the next pending block into the primary + ** position. Do this only if the queue pointer is + ** null. + */ + st->DontTouch = TRUE; + if (!*buffer && st->FilePending) { + *buffer = Add_Long_To_Pointer(ptr, (long)(*odd % LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + *odd = *odd + 1; + if (!st->FilePending) { + *size = st->FilePendingSize; + } else { + *size = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + + /* + ** If the file handle is still valid, then read in the next + ** block and add it to the next pending slot available. + */ + if (st->FilePending < + (StreamLowImpact ? (LockedData.StreamBufferCount>>1) : ((LockedData.StreamBufferCount-3))) && st->FileHandle != ERROR) { + + + int num_empty_buffers; + +#ifdef SIMPLE_FILLING + num_empty_buffers = (LockedData.StreamBufferCount-2) - st->FilePending; +#else + + // + // num_empty_buffers will be from 1 to StreamBufferCount + // + if (StreamLowImpact) { + num_empty_buffers = MIN((LockedData.StreamBufferCount >> 1)+STREAM_CUSHION_BLOCKS, (LockedData.StreamBufferCount - 2) - st->FilePending); + } + else { + num_empty_buffers = (LockedData.StreamBufferCount - 2) - st->FilePending; + } +#endif + + + + while (num_empty_buffers && st->FileHandle != ERROR) { + int tofill; + long psize; + + tofill = (*odd + st->FilePending) % LockedData.StreamBufferCount; + + ptr = Add_Long_To_Pointer(st->FileBuffer, (long)tofill * (long)LockedData.StreamBufferSize); + psize = Read_File(st->FileHandle, ptr, LockedData.StreamBufferSize); + + /* + ** If less than the requested amount of data was read, this + ** indicates that the source file is exhausted. Flag the source + ** file as closed so that no further reading is attempted. + */ + if (psize != LockedData.StreamBufferSize) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + + /* + ** If any real data went into the pending buffer, then flag + ** that this buffer is valid. + */ + if (psize) { + st->DontTouch = TRUE; + st->FilePendingSize = psize; + st->FilePending++; + st->DontTouch = FALSE; + maintenance_callback(); + } + num_empty_buffers--; + } + + /* + ** After filling all pending buffers, check to see if the queue buffer + ** is empty. If so, then assign the first available pending buffer to the + ** queue. + */ + st->DontTouch = TRUE; + if (!st->QueueBuffer && st->FilePending) { + st->QueueBuffer = Add_Long_To_Pointer(st->FileBuffer, (long)(st->Odd%LockedData.StreamBufferCount)*(long)LockedData.StreamBufferSize); + st->FilePending--; + st->Odd++; + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + st->DontTouch = FALSE; + maintenance_callback(); + } + + /* + ** If there are no more buffers that the callback routine + ** can slot into the primary position, then signal that + ** no furthur callbacks are needed. + */ + if (st->FilePending) { + return(TRUE); + } + } + } + return(FALSE); +} + +/*************************************************************************** + * STREAM_SAMPLE_VOLUME -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int volume, int handle) +{ + int playid=-1; // Sample play ID. + SampleTrackerType *st; // Working pointer to sample control structure. + long oldsize; // Copy of original sound size. + AUDHeaderType header; + + if (buffer && size && LockedData.DigiHandle != -1) { + + /* + ** Start the first section of the sound playing. + */ + Mem_Copy(buffer, &header, sizeof(header)); + oldsize = header.Size; + header.Size = size-sizeof(header); + Mem_Copy(&header, buffer, sizeof(header)); + playid = Play_Sample_Handle(buffer, 0xFF, volume, 0x0, handle); + header.Size = oldsize; + Mem_Copy(&header, buffer, sizeof(header)); + + /* + ** If the sample actually started playing, then flag this + ** sample as a streaming type and signal for a callback + ** to occur. + */ + if (playid != -1) { + st = &LockedData.SampleTracker[playid]; + + st->Callback = callback; + st->Odd = 0; + } + } + return (playid); +} + +/*************************************************************************** + * STREAM_SAMPLE -- generic streaming sample playback init * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +static int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback)(WORD id, WORD *odd, VOID **buffer, LONG *size), int handle) +{ + return Stream_Sample_Vol(buffer, size, callback, 0xFF, handle); +} + + + +/*********************************************************************************************** + * File_Stream_Sample -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start) +{ + return File_Stream_Sample_Vol(filename, 0xFF, real_time_start); +} + +/*************************************************************************** + * FILE_STREAM_PRELOAD -- Handles initial proload of streaming samples * + * * + * This function is called before a sample which streams from disk is * + * started. It can be called to either fill the buffer in small chunks * + * from the call back routine or to fill the entire buffer at once. This * + * is wholely dependant on whether the Loading bit is set within the * + * sample tracker. * + * * + * INPUT: LockedData.SampleTracker * to the header which tracks this samples * + * processing. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/05/1995 PWG : Created. * + *=========================================================================*/ + +void File_Stream_Preload(int handle) +{ + SampleTrackerType *st = &LockedData.SampleTracker[handle]; + int fh = st->FileHandle; + int maxnum = (LockedData.StreamBufferCount >> 1) + STREAM_CUSHION_BLOCKS; + void *buffer = st->FileBuffer; + int num; + + /* + ** Figure just how much we need to load. If we are doing the load in progress + ** then we will only load two blocks. + */ + if (st->Loading) { + num = st->FilePending + 2; + num = MIN(num, maxnum); + } else { + num = maxnum; + } + + //printf("Before buffer load!\n"); + /* + ** Loop through the blocks and load up the number we need. + */ + for (int index = st->FilePending; index < num; index++) { + long s = Read_File(fh, Add_Long_To_Pointer(buffer, (long)index * (long)LockedData.StreamBufferSize), LockedData.StreamBufferSize); + //printf("Reading block of size %d Stream Buffer = %d\n"); + if (s) { + st->FilePendingSize = s; + st->FilePending++; + } + if (s < LockedData.StreamBufferSize) break; + } + + /* + ** If the last block was incomplete (ie. it didn't completely fill the buffer) or + ** we have now filled up as much of the Streaming Buffer as we need to, then now is + ** the time to kick off the sample. + */ + if (st->FilePendingSize < LockedData.StreamBufferSize || index == maxnum) { + //printf("Before Stream Sample Volume!\n"); + /* + ** Actually start the sample playing, and don't worry about the file callback + ** it won't be called for a while. + */ + { + int old = LockedData.SoundVolume; + int size = (st->FilePending == 1) ? st->FilePendingSize : LockedData.StreamBufferSize; + LockedData.SoundVolume = LockedData.ScoreVolume; + + StartingFileStream = TRUE; + Stream_Sample_Vol(buffer, size, File_Callback, st->Volume, handle); + StartingFileStream = FALSE; + LockedData.SoundVolume = old; + } + //printf("After Stream Sample Volume!\n"); + /* + ** The Sample is finished loading (if it was loading in small pieces) so record that + ** so that it will now use the active logic in the file call back. + */ + st->Loading = FALSE; + + /* + ** Decrement the file pending because the first block is already playing thanks + ** to the play sample call above. + */ + st->FilePending--; + + /* + ** If File pending is now a zero, then we only preloaded one block and there + ** is nothing more to play. So clear the sample tracing structure of the + ** information it no longer needs. + */ + if (!st->FilePending) { + st->Odd = 0; + st->QueueBuffer = 0; + st->QueueSize = 0; + st->FilePendingSize = 0; + st->Callback = NULL; + Close_File(fh); + } else { + /* + ** The QueueBuffer counts as an already played block so remove it from the total. + ** Note: We didn't remove it before because there might not have been one. + */ + st->FilePending--; + + /* + ** When we start loading we need to start past the first two blocks. Why this + ** is called Odd, I haven't got the slightest. + */ + st->Odd = 2; + + /* + ** If the file pending size is less than the stream buffer, then the last block + ** we loaded was the last block period. So close the file and reset the handle. + */ + if (st->FilePendingSize != LockedData.StreamBufferSize) { + Close_File(fh); + st->FileHandle = ERROR; + } + + /* + ** The Queue buffer needs to point at the next block to be processed. The size + ** of the queue is dependant on how many more blocks there are. + */ + st->QueueBuffer = Add_Long_To_Pointer(buffer, LockedData.StreamBufferSize); + if (!st->FilePending) { + st->QueueSize = st->FilePendingSize; + } else { + st->QueueSize = LockedData.StreamBufferSize; + } + } + } +} +/*********************************************************************************************** + * File_Stream_Sample_Vol -- Streams a sample directly from a file. * + * * + * This will take the file specified and play it directly from disk. * + * It performs this by allocating a temporary buffer in XMS/EMS and * + * then keeping this buffer filled by the Sound_Callback() routine. * + * * + * INPUT: filename -- The name of the file to play. * + * * + * OUTPUT: Returns the handle to the sound -- just like Play_Sample(). * + * * + * WARNINGS: The temporary buffer is allocated when this routine is * + * called and then freed when the sound is finished. Keep * + * this in mind. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) +{ + static VOID *buffer = NULL; + SampleTrackerType *st; + int fh; + int handle = -1; + long size; + int index; + + if (LockedData.DigiHandle != -1 && filename && Find_File(filename)) { + //printf("Before initialize memory!\n"); + /* + ** Make sure all sample tracker structures point to the same + ** upper memory buffer. This allocation only occurs if at + ** least one sample gets "streamed". + */ + if (!buffer) { + buffer = Alloc(LockedData.StreamBufferSize * LockedData.StreamBufferCount, (MemoryFlagType)(StreamBufferFlag | MEM_TEMP | MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].FileBuffer = buffer; + } + } + + /* + ** If after trying to allocate the buffer we still fail then + ** we can stream this sample. + */ + if (!buffer) return(-1); + + //printf("Before Open File!\n"); + /* + ** Lets see if we can sucessfully open up the file. If we can't, + ** then there is no point in going any farther. + */ + if ((fh = Open_File(filename, READ)) == -1) { + return (-1); + } + + //printf("Before Get Free Handle!\n"); + /* + ** Reserve a handle so that we can fill in the sample tracker + ** with the needed information. If we dont get valid handle then + ** we might as well give up. + */ + if ((unsigned)(handle = Get_Free_Sample_Handle(0xFF)) >= LockedData.MaxSamples) { + return(-1); + } + + /* + ** Now lets get a pointer to the proper sample handler and start + ** our manipulations. + */ + st = &LockedData.SampleTracker[handle]; + st->IsScore = TRUE; + st->FilePending = 0; + st->FilePendingSize = 0; + st->Loading = real_time_start; + st->Volume = volume; + st->FileHandle = fh; + /* + ** Now that we have setup our initial data properly, let load up + ** the beginning of the sample we intend to stream. + */ + //printf("Before Preload!\n"); + File_Stream_Preload(handle); + } + return (handle); +} + +/*********************************************************************************************** + * Sound_Callback -- Audio driver callback function. * + * * + * Maintains the audio buffers. This routine must be called at least * + * 11 times per second or else audio glitches will occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If this routine is not called often enough then audio * + * glitches will occur. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +VOID cdecl __saveregs __loadds Sound_Callback(VOID) +{ + int index; + SampleTrackerType *st; + + if (LockedData.DigiHandle != -1) { + st = &LockedData.SampleTracker[0]; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (st->Loading) { + File_Stream_Preload(index); + } else { + /* + ** General service routine to handle moving small blocks from the + ** source into the low RAM staging buffers. + */ + + if (st->Active) { + + + /* + ** Special check to see if the sample is a fading one AND + ** it has faded to silence, then stop it here. + */ + if (st->Reducer && !st->Volume) { + Stop_Sample(index); + } else { + + /* + ** Fill the queuebuffer if it is currently empty + ** and there is a callback function defined to fill it. + ** + ** PWG/CDY & CO: We should be down by at least two blocks + ** before we bother with this + */ + if ((!st->QueueBuffer || + (st->FileHandle != ERROR && st->FilePending < LockedData.StreamBufferCount-3)) && + st->Callback) { + + if (!st->Callback(index, &st->Odd, &st->QueueBuffer, &st->QueueSize)) { + st->Callback = NULL; + } + } + } + + } else { + + /* + ** This catches the case where a streaming sample gets + ** aborted prematurely because of failure to call the + ** callback function frequently enough. In this case, the + ** sample will be flagged as inactive, but the file handle + ** will not have been closed. + */ + if (st->FileHandle != ERROR) { + Close_File(st->FileHandle); + st->FileHandle = ERROR; + } + } + } + + /* + ** Advance to the next sample control structure. + */ + st++; + } + } +} + + +/*********************************************************************************************** + * Load_Sample -- Loads a digitized sample into RAM. * + * * + * This routine loads a digitized sample into RAM. * + * * + * INPUT: filename -- Name of the sound file to load. * + * * + * OUTPUT: Returns with a pointer to the loaded sound file. This pointer * + * is passed to Play_Sample when playback is desired. * + * * + * WARNINGS: If there is insufficient memory to load the sample, then * + * NULL will be returned. * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 01/06/1994 JLB : HMI version. * + *=============================================================================================*/ +VOID *Load_Sample(char const *filename) +{ + void *buffer = NULL; + long size; + int fh; + + if (LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = File_Size(fh)+sizeof(AUDHeaderType); + buffer = Alloc(size, MEM_NORMAL); + + if (buffer) { + Sample_Read(fh, buffer, size); + } + Close_File(fh); + Misc = size; + } + return(buffer); +} + + +/*********************************************************************************************** + * Load_Sample_Into_Buffer -- Loads a digitized sample into a buffer. * + * * + * This routine is used to load a digitized sample into a buffer * + * provided by the programmer. This buffer can be in XMS or EMS. * + * * + * INPUT: filename -- The filename to load. * + * * + * buffer -- Pointer to the buffer to load into. * + * * + * size -- The size of the buffer to load into. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: This routine will not overflow the buffer provided. This * + * means that the buffer must be big enough to hold the data * + * or else the sound will be cut short. * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) +{ + int fh; + + /* + ** Verify legality of parameters. + */ + if (!buffer || !size || LockedData.DigiHandle == -1 || !filename || !Find_File(filename)) { + return (NULL); + } + + fh = Open_File(filename, READ); + if (fh != ERROR) { + size = Sample_Read(fh, buffer, size); + Close_File(fh); + } else { + return(0); + } + return(size); +} + + +/*********************************************************************************************** + * Sample_Read -- Reads sample data from an openned file. * + * * + * This routine reads a sample file. It is presumed that the file is * + * already positioned at the start of the sample. From this, it can * + * determine if it is a VOC or raw data and proceed accordingly. * + * * + * INPUT: fh -- File handle of already openned sample file. * + * * + * buffer -- Pointer to the buffer to load data into. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: Returns the number of bytes actually used in the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1994 JLB : Created. * + *=============================================================================================*/ +long Sample_Read(int fh, void *buffer, long size) +{ + + AUDHeaderType RawHeader; + VOID *outbuffer; // Pointer to start of raw data. + long actual_bytes_read; // Actual bytes read in, including header + +/* + Conversion formula for TCrate and Hz rate. + + TC = 256 - 1m/rate + rate = 1m / (256-TC) +*/ + + if (!buffer || fh == ERROR || size <= sizeof(RawHeader)) return(NULL); + size -= sizeof(RawHeader); + outbuffer = Add_Long_To_Pointer(buffer, sizeof(RawHeader)); + + actual_bytes_read = + Read_File(fh, &RawHeader, sizeof(RawHeader)); + + actual_bytes_read += + Read_File(fh, outbuffer, MIN(size, RawHeader.Size)); + + + Mem_Copy(&RawHeader, buffer, sizeof(RawHeader)); + return(actual_bytes_read); +} + + +/*********************************************************************************************** + * Free_Sample -- Frees a previously loaded digitized sample. * + * * + * Use this routine to free the memory allocated by a previous call to * + * Load_Sample. * + * * + * INPUT: sample -- Pointer to the sample to be freed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + *=============================================================================================*/ +VOID Free_Sample(VOID const *sample) +{ + if (sample) Free((void *)sample); +} + +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + return Audio_Init(sample, -1, -1, -1, rate, bits_per_sample, max_samples, reverse_channels); +} + +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels) +{ + int error; // Function error return code. + unsigned int port; + int index; + _SOS_INIT_DRIVER init; // Driver init structure. + + Init_Locked_Data(); + memset(&LockedData.SampleTracker[0], 0, sizeof(LockedData.SampleTracker)); + LockedData.Rate = rate; + + Bits_Per_Sample = bits_per_sample; + + sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL); + + if (TimerSystemOn) { + sosTIMERRegisterEvent(60, &Timer_Interrupt_Func, &SystemTimer); +// TickCount.Start(); + } else + sosTIMERRegisterEvent(60, &HMI_TimerCallback, &SystemTimer); + + /* + ** Special code to handle the HMI problem with running under Windows. + */ + if (/*Operating_System() != OS_DOS ||*/ sample == SAMPLE_NONE) { + return(FALSE); + } + + while (sample) { + +#ifdef SIMPLE_HMI_INIT + + if (address == -1 || inter == -1 || dma == -1) { + error = sosDIGIDetectInit((LPSTR)NULL); + if (error) { + printf("Cannot initialize detection system (%d).\n", error); + Get_Key(); + break; + } + + error = sosDIGIDetectFindHardware(sample, &DigiCaps, &port); + if (error) { + printf("Cannot find sound card specified.\n"); + Get_Key(); + break; + } + + /* + ** Handle the override for Address, Interrupt, and DMA settings. + */ + error = sosDIGIDetectGetSettings(&DigiHardware); + sosDIGIDetectUnInit(); + if (error) { + printf("Cannot get sound card settings.\n"); + Get_Key(); + break; + } + } else { + DigiHardware.wPort = address; + DigiHardware.wIRQ = inter; + DigiHardware.wDMA = dma; + } +#endif + + /* + ** Initialize the digi-system and driver. + */ + sosDIGIInitSystem((LPSTR)NULL, _SOS_DEBUG_NORMAL); + init.wBufferSize = 1024*8; // Specify the DMA buf size + init.lpBuffer = (LPSTR)NULL; + init.wAllocateBuffer = TRUE; + init.wSampleRate = rate; // Sample playback rate. + init.wParam = 19; + init.dwParam = 0; + init.lpFillHandler = NULL; + init.lpDriverMemory = (LPSTR)NULL; + init.lpTimerMemory = (LPSTR)NULL; + init.wTimerID = _SOS_NORMAL_TIMER; + + error = 0; + error = sosDIGIInitDriver( (int)sample, &DigiHardware, &init, &LockedData.DigiHandle); + if (error) { + printf("Unable to initialize the sound driver.\n"); + Get_Key(); + break; + } + + error = 0; + + + if (sample >= _GUS_8_MONO && sample <= _GUS_16_ST) { + error = sosTIMERRegisterEvent(120, init.lpFillHandler, &DigiTimer); + } else { + error = sosTIMERRegisterEvent(60, init.lpFillHandler, &DigiTimer); + } + if (error) { + printf("Unable to regiser the fill handler.\n"); + Get_Key(); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + + /* + ** Allocate a decompression buffer equal to the size of a SONARC frame + ** block. + */ + LockedData.UncompBuffer = Alloc(LARGEST_SONARC_BLOCK + 50, (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + LockedData.MaxSamples = max_samples; + + /* + ** Allocate private staging buffers for double buffering sound into HMI + ** driver. + */ + DigiBuffer = Alloc(2048+(((LONG)LockedData.MaxSamples*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)), (MemoryFlagType)(MEM_NORMAL|MEM_CLEAR|MEM_LOCK)); + for (index = 0; index < LockedData.MaxSamples; index++) { + LockedData.SampleTracker[index].Buffer[0] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].Buffer[1] = Add_Long_To_Pointer(DigiBuffer, ((LONG)index*2+1) * (LONG)(SFX_MINI_STAGE_BUFFER_SIZE+SONARC_MARGIN)); + LockedData.SampleTracker[index].FileHandle = ERROR; + LockedData.SampleTracker[index].QueueBuffer = NULL; + } + error = sosTIMERRegisterEvent(MAINTENANCE_RATE, (void (__far *)(void))maintenance_callback, &MaintainTimer); + if (error) { + printf("Unable to initialize the maintenance callback.\n"); + Get_Key(); + sosTIMERRemoveEvent(DigiTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + LockedData.DigiHandle = -1; + break; + } + SoundType = (SFX_Type)sample; + SampleType = (Sample_Type)sample; + ReverseChannels = reverse_channels; + + break; + } + + return(TRUE); +} + + +/*********************************************************************************************** + * Sound_End -- Uninitializes the sound driver. * + * * + * This routine will uninitialize the sound driver (if any was * + * installed). This routine must be called at program termination * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/23/1991 JLB : Created. * + *=============================================================================================*/ +VOID Sound_End(VOID) +{ + + if (LockedData.DigiHandle != -1) { + sosTIMERRemoveEvent(DigiTimer); + sosTIMERRemoveEvent(MaintainTimer); + sosDIGIUnInitDriver(LockedData.DigiHandle, TRUE, TRUE); + sosDIGIUnInitSystem(); + LockedData.DigiHandle = -1; + } + if (DigiBuffer) { + Free(DigiBuffer); + DigiBuffer = 0; + } + if (LockedData.UncompBuffer) { + Free(LockedData.UncompBuffer); + LockedData.UncompBuffer = 0; + } + sosTIMERRemoveEvent(SystemTimer); + sosTIMERUnInitSystem(0); + Unlock_Locked_Data(); +} + + + + +/*********************************************************************************************** + * Stop_Sample -- Stops any currently playing sampled sound. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +VOID Stop_Sample(int handle) +{ + if (LockedData.DigiHandle != -1 && (unsigned)handle < LockedData.MaxSamples) { + + + if (LockedData.SampleTracker[handle].Active || LockedData.SampleTracker[handle].Loading) { + + + LockedData.SampleTracker[handle].Active = FALSE; + if (!LockedData.SampleTracker[handle].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[handle].Original, LockedData.SampleTracker[handle].OriginalSize); + LockedData.SampleTracker[handle].Original = NULL; + } + LockedData.SampleTracker[handle].Priority = 0; + + /* + ** Stop the sample if it is playing. + */ + if (!LockedData.SampleTracker[handle].Loading) { + sosDIGIStopSample(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle); + } + LockedData.SampleTracker[handle].Loading = FALSE; + + /* + ** If this is a streaming sample, then close the source file. + */ + if (LockedData.SampleTracker[handle].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[handle].FileHandle); + LockedData.SampleTracker[handle].FileHandle = ERROR; + } + + LockedData.SampleTracker[handle].QueueBuffer = NULL; + } + } +} + + +/*********************************************************************************************** + * Sample_Status -- Queries the current playing sample status (if any). * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1992 JLB : Created. * + *=============================================================================================*/ +BOOL Sample_Status(int handle) +{ + /* + ** If its an invalid handle or we do not have a sound driver then + ** the sample in question is not playing. + */ + if (LockedData.DigiHandle == -1 || (unsigned)handle >= LockedData.MaxSamples) return(FALSE); + /* + ** If the sample is loading, then for all intents and purposes the + ** sample is playing. + */ + if (LockedData.SampleTracker[handle].Loading) return(TRUE); + /* + ** If the sample is not active, then it is not playing + */ + if (!LockedData.SampleTracker[handle].Active) return(FALSE); + /* + ** If we made it this far, then the Sample is still playing if sos says + ** that it is. + */ + return (!sosDIGISampleDone(LockedData.DigiHandle, LockedData.SampleTracker[handle].Handle)); +} + + +BOOL Is_Sample_Playing(void const * sample) +{ + int index; + + if (!sample) return FALSE; + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample && Sample_Status(index)) { + return (TRUE); + } + } + return (FALSE); +} + + +VOID Stop_Sample_Playing(void const * sample) +{ + int index; + + if (sample) { + for (index = 0; index < LockedData.MaxSamples; index++) { + if (LockedData.SampleTracker[index].Original == sample) { + Stop_Sample(index); + break; + } + } + } +} +int Get_Free_Sample_Handle(int priority) +{ + int id; + + /* + ** Find a free SFX holding buffer slot. + */ + for (id = LockedData.MaxSamples - 1; id >= 0; id--) { + if (!LockedData.SampleTracker[id].Active && !LockedData.SampleTracker[id].Loading) { + if (!StartingFileStream && LockedData.SampleTracker[id].IsScore) { + StartingFileStream = TRUE; // Ensures only one channel is kept free for scores. + continue; + } + break; + } + } + + if (id < 0) { + for (id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Priority <= priority) break; + } + if (id == LockedData.MaxSamples) { + return(-1); // Cannot play! + } + Stop_Sample(id); // This sample gets clobbered. + } + if (id == -1) { + return -1; + } + if (LockedData.SampleTracker[id].FileHandle != ERROR) { + Close_File(LockedData.SampleTracker[id].FileHandle); + LockedData.SampleTracker[id].FileHandle = ERROR; + } + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + LockedData.SampleTracker[id].IsScore = FALSE; + return(id); +} + +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) +{ + return(Play_Sample_Handle(sample, priority, volume, panloc, Get_Free_Sample_Handle(priority))); +} +/*********************************************************************************************** + * Play_Sample_Vol -- Plays a digitized sample. * + * * + * Use this routine to play a previously loaded digitized sample. * + * * + * INPUT: sample -- Sample pointer as returned from Load_Sample. * + * * + * volume -- The volume to play (0..255 with 255=loudest). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1992 JLB : Created. * + * 05/24/1992 JLB : Volume support -- Soundblaster Pro * + * 04/22/1994 JLB : Multiple sample playback rates. * + *=============================================================================================*/ +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) +{ + AUDHeaderType RawHeader; + _SOS_START_SAMPLE start; + SampleTrackerType *st=NULL; // Working pointer to sample tracker structure. + + if (!sample || LockedData.DigiHandle == -1) { + return(-1); + } + + if (id == -1) { + return -1; + } + + /* + ** Fetch the control bytes from the start of the sample data. + */ + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + /* + ** Prepare the sample tracker structure for processing of this + ** sample. Fill the structure with data that can be determined + ** before the sample is started. + */ + st = &LockedData.SampleTracker[id]; + st->Compression = (SCompressType) ((unsigned char)RawHeader.Compression); + st->Original = sample; + st->OriginalSize = RawHeader.Size + sizeof(RawHeader); + if (!st->IsScore) { + DPMI_Lock(st->Original, st->OriginalSize); + } + st->Priority = priority; + st->DontTouch = FALSE; + st->Odd = 0; + st->Reducer = 0; + st->Restart = FALSE; + st->QueueBuffer = NULL; + st->QueueSize = NULL; + st->TrailerLen = 0; + st->Remainder = RawHeader.Size; + st->Source = Add_Long_To_Pointer((void *)sample, sizeof(RawHeader)); + st->Service = FALSE; + + /* + ** If the code in question using HMI based compression then we need + ** to set up for uncompressing it. + */ + if (st->Compression == SCOMP_SOS) { + st->sosinfo.wChannels = (RawHeader.Flags & AUD_FLAG_STEREO) ? 2 : 1; + st->sosinfo.wBitSize = (RawHeader.Flags & AUD_FLAG_16BIT) ? 16 : 8; + st->sosinfo.dwCompSize = RawHeader.Size; + st->sosinfo.dwUnCompSize = RawHeader.Size * ( st->sosinfo.wBitSize / 4 ); + sosCODECInitStream(&st->sosinfo); + } + + /* + ** Fill in one or both staging buffers if possible. + */ + _disable(); +// Disable_Timer_Interrupt(); + if (SFX_MINI_STAGE_BUFFER_SIZE == + Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[0], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen)) { + + st->DataLength = Sample_Copy(st, + &st->Source, + &st->Remainder, + &st->QueueBuffer, + &st->QueueSize, + st->Buffer[1], + SFX_MINI_STAGE_BUFFER_SIZE, + st->Compression, + &st->Trailer[0], + &st->TrailerLen); + st->Index = 1; + } else { + st->Index = 0; + st->DataLength = 0; + } + _enable(); +// Enable_Timer_Interrupt(); + + /* + ** Fill in the HMI start sample structure. + */ + memset(&start, 0, sizeof(start)); + + start.lpSamplePtr = (LPSTR)st->Buffer[0]; + if (st->Index == 1) { + start.dwSampleSize = SFX_MINI_STAGE_BUFFER_SIZE-1; + } else { + start.dwSampleSize = st->DataLength-1; + } + start.lpCallback = (void cdecl (far *)(unsigned int, unsigned int, unsigned int))&DigiCallback; + start.wLoopCount = 0; + start.wSampleID = id; + + /* + ** Adjust pitch shifting as necessary so that lower playback + ** samples can be supported. + */ + if (RawHeader.Rate != LockedData.Rate) { + ldiv_t result; + + result = ldiv((long)RawHeader.Rate, LockedData.Rate); + start.dwSamplePitchAdd = (long)MAKE_LONG((short)result.quot, (short)(((long)result.rem * 0x10000L) / LockedData.Rate)); + start.wSampleFlags |= _PITCH_SHIFT; + } + + /* + ** Sample translation flag. + */ + + if (RawHeader.Flags & AUD_FLAG_16BIT) { + if (Bits_Per_Sample == 8) { + start.wSampleFlags |= _TRANSLATE16TO8; + } + } else { + if (Bits_Per_Sample == 16) { + start.wSampleFlags |= _TRANSLATE8TO16; + } + } + + /* + ** Sample stereo flag. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + start.wChannel = _INTERLEAVED; + start.wSampleFlags |= _STEREOTOMONO; + } else { + start.wChannel = _CENTER_CHANNEL; + } + + /* + ** Sample volume control flags. Always give it volume control because + ** if not, then future volume control is either ignored or stops the + ** sample. + */ + st->Volume = volume << 7; + start.wVolume = (st->Volume >> 8) * LockedData.SoundVolume; + start.wSampleFlags |= _VOLUME; + + /* + ** If we have defined a panning location for the sound driver than + ** take care of it here. Panning will only work with a stereo driver + ** and only if the sample is played _CENTER_CHANNEL. + */ + if ((panloc != 0x0) && (!(RawHeader.Flags & AUD_FLAG_STEREO))) { + start.wChannel = _CENTER_CHANNEL; + panloc = ((ReverseChannels) ? ((-panloc) + 0x8000) : ((panloc) + 0x8000)); + start.wSampleFlags |= _PANNING; + start.wSamplePanLocation= panloc; + } + + st->Stereo = start.wChannel; + st->Pitch = start.dwSamplePitchAdd; + st->Flags = start.wSampleFlags; + + /* + ** Start the sample playing now. + */ + _disable(); // NEW +// Disable_Timer_Interrupt(); + st->Handle = sosDIGIStartSample(LockedData.DigiHandle, &start); + if (st->Handle == -1) { + id = -1; + } else { + + /* + ** Fill in the sample tracker structure with those values that are + ** determined AFTER the sample starts. + */ + st->Active = TRUE; + } + _enable(); // NEW +// Enable_Timer_Interrupt(); + + return(id); +} + + +int Set_Sound_Vol(int volume) +{ + int old; + old = LockedData.SoundVolume; + LockedData.SoundVolume = volume & 0xFF; + return(old); +} + + +int Set_Score_Vol(int volume) +{ + int old; + old = LockedData.ScoreVolume; + LockedData.ScoreVolume = volume & 0xFF; + return(old); +} + + +VOID Fade_Sample(int handle, int ticks) +{ + if (Sample_Status(handle)) { + if (!ticks || LockedData.SampleTracker[handle].Loading) { + Stop_Sample(handle); + } else { + SampleTrackerType * st; + + st = &LockedData.SampleTracker[handle]; + st->Reducer = (st->Volume / ticks)+1; + } + } +} +int Get_Digi_Handle(void) +{ + return(LockedData.DigiHandle); +} + +/*************************************************************************** + * SAMPLE_LENGTH -- returns length of a sample in ticks * + * * + * INPUT: void const *sample - pointer to the sample to get length * + * of. * + * * + * OUTPUT: long - length of the sample in ticks (60/sec) * + * * + * HISTORY: * + * 07/05/1995 PWG : Created. * + *=========================================================================*/ +long Sample_Length(void const *sample) +{ + AUDHeaderType RawHeader; + + if (!sample) return(0); + + Mem_Copy((void *)sample, (void *)&RawHeader, sizeof(RawHeader)); + + long time = RawHeader.UncompSize; + + /* + ** If the sample is a 16 bit sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_16BIT) { + time >>= 1; + } + + /* + ** If the sample is a stereo sample, then it will take only half + ** as long to play. + */ + if (RawHeader.Flags & AUD_FLAG_STEREO) { + time >>= 1; + } + + if (RawHeader.Rate/60) { + time /= (RawHeader.Rate/60); + } + return(time); +} diff --git a/WWFLAT32/AUDIO/SOUNDLCK.CPP b/WWFLAT32/AUDIO/SOUNDLCK.CPP new file mode 100644 index 0000000..23c6a86 --- /dev/null +++ b/WWFLAT32/AUDIO/SOUNDLCK.CPP @@ -0,0 +1,147 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDLCK.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "soundint.h" +#include "wwmem.h" + +LockedDataType LockedData; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * INIT_LOCKED_DATA -- Initializes sound driver locked data * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void Init_Locked_Data(void) +{ + /* + ** Initialize all of the data elements that need to be locked. + */ + LockedData.DigiHandle = -1; + LockedData.ServiceSomething = FALSE; + LockedData.MagicNumber = 0xDEAF; + LockedData.UncompBuffer = NULL; + LockedData.StreamBufferSize = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + LockedData.StreamBufferCount = 32; + LockedData.SoundVolume = 255; + LockedData.ScoreVolume = 255; + LockedData._int = FALSE; + LockedData.MaxSamples = MAX_SFX; + + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Lock(&LockedData, 4096L); + DPMI_Lock(Simple_Copy, 4096L); + DPMI_Lock(Sample_Copy, 4096L); + DPMI_Lock((void *)maintenance_callback, 4096L); + DPMI_Lock((void *)DigiCallback, 4096L); + DPMI_Lock((void *)HMI_TimerCallback, 4096L); + DPMI_Lock(Audio_Add_Long_To_Pointer, 4096L); + DPMI_Lock(DPMI_Unlock, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Lock(Mem_Copy, 4096L); + DPMI_Lock(Audio_Mem_Set, 4096L); + DPMI_Lock(__GETDS, 4096L); + + /* + ** Finally lock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Lock(); + sosCODEC_Lock(); +} + +void Unlock_Locked_Data(void) +{ + /* + ** Lock the sound specific c functions that will cause us problems if + ** they are swapped out during an interrupt. + */ + DPMI_Unlock(&LockedData, 4096L); + DPMI_Unlock(Simple_Copy, 4096L); + DPMI_Unlock(Sample_Copy, 4096L); + DPMI_Unlock((void *)maintenance_callback, 4096L); + DPMI_Unlock((void *)DigiCallback, 4096L); + DPMI_Unlock((void *)HMI_TimerCallback, 4096L); + DPMI_Unlock(Audio_Add_Long_To_Pointer, 4096L); + + /* + ** Lock the library functions that will cause us problems if they are + ** swapped out during an interrupt. + */ + DPMI_Unlock(Mem_Copy, 4096L); + DPMI_Unlock(Audio_Mem_Set, 4096L); + DPMI_Unlock(__GETDS, 4096L); + /* + ** Finally unlock the two assembly modules that need locking. This can + ** be handled by calling the lock function that is local to thier module + ** swapped out during an interrupt. + */ + Decompress_Frame_Unlock(); + sosCODEC_Unlock(); + + /* + ** As a last step go though all of the sample tracker structures and make + ** sure all the samples have been properly unlocked. + */ + for (int id = 0; id < LockedData.MaxSamples; id++) { + if (LockedData.SampleTracker[id].Original && !LockedData.SampleTracker[id].IsScore) { + DPMI_Unlock(LockedData.SampleTracker[id].Original, LockedData.SampleTracker[id].OriginalSize); + LockedData.SampleTracker[id].Original = NULL; + } + } +} + diff --git a/WWFLAT32/DESCMGMT/DESCMGMT.CPP b/WWFLAT32/DESCMGMT/DESCMGMT.CPP new file mode 100644 index 0000000..687eba5 --- /dev/null +++ b/WWFLAT32/DESCMGMT/DESCMGMT.CPP @@ -0,0 +1,113 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Descriptor management * + * * + * File Name : DESCMGMT.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MAP_SEGMENT_TO_ADDRESS -- Maps a physical address into a selector * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "descmgmt.h" +#include "misc.h" + + +/*************************************************************************** + * MAP_SEGMENT_TO_ADDRESS -- Maps a physical address into a selector * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: selector UWORD The selector mapped to address. exit on error. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/25/1994 jaw: Created. * + *=========================================================================*/ + +ULONG Map_Segment_To_Address(ULONG address, ULONG length) +{ + +// this function is not longer needed by RATIONAL SYSTEM DOS4GW +// linear addressing mode. +// a> the first megabyte of memory is mapped to linear adress 0 - 0x10000h +// b> all other addresses are linear offset from either ds: or es: + +/* + UWORD segment; + UWORD curDS; + CD_DES desc; + CD_DES cur_desc; + + // allocate a selector + if(_dos_allocmem(0, &segment) != 0) { + Exit(1, "Allocation of Descriptor.\n"); + } + + // get the data for this selector + if(_dx_ldt_rd(segment, (UCHAR *)&desc) != 0) { + Exit(1, "Reading Descriptor.\n"); + } + + // get the data for current data segment + curDS = GetDs(); + if(_dx_ldt_rd(curDS, (UCHAR *)&cur_desc) != 0) { + Exit(1, "Reading Descriptor.\n"); + } + + // set limit + desc.limit0_15 = (USHORT)(length & 0xffff); + desc.limit16_19 = ((UCHAR)(length >> 16L)) | DOS_32; + + // set base address + desc.base0_15 = (USHORT)(address & 0xffff); + desc.base16_23 = (UCHAR)((address >> 16) & 0xff); + desc.base24_31 = (UCHAR)((address >> 24) & 0xff); + + // set rights mark as icurrent data segment + desc.arights = cur_desc.arights; + + // write to LDT selector + if(_dx_ldt_wr(segment, (UCHAR *)&desc) != 0) { + Exit(1, "Failed writing descriptor.\n"); + } + + // return selector number + return segment; + */ + + if ( address & 0xfff0ffff ) + Exit ( 1 , "Error mapping real address to lineal address.\n" ) ; + + return address ; +} + diff --git a/WWFLAT32/DESCMGMT/DESCMGMT.H b/WWFLAT32/DESCMGMT/DESCMGMT.H new file mode 100644 index 0000000..83f34ca --- /dev/null +++ b/WWFLAT32/DESCMGMT/DESCMGMT.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/WWFLAT32/DESCMGMT/FFIXSEL.ASM b/WWFLAT32/DESCMGMT/FFIXSEL.ASM new file mode 100644 index 0000000..f618003 --- /dev/null +++ b/WWFLAT32/DESCMGMT/FFIXSEL.ASM @@ -0,0 +1,94 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Fix a selector * +;* * +;* File Name : FFIXSEL.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* FixSelector -- Fix the Priviledge level of a selector * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + +IDEAL +P386 +MODEL USE32 FLAT + + +EXTRN exit : near +GLOBAL FixSelector :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* FIXSELECTOR -- Fix the Priviledge level of a selector * +;* * +;* * +;* * +;* INPUT: sel the selector to fix-up * +;* * +;* OUTPUT: UWORD The fixed up selector * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw Created. * +;*=========================================================================* + +PROC FixSelector C near + + USES ecx,edx + + ARG sel:WORD + +; Copy the Table Bit and IOPL from the Current CS + +; Something is wrong the program should not be here unthe any circunstance +; movzx ecx,[sel] +; xor eax,eax +; mov ax,cs +; and ax,7 +; or ax,cx +push 0 +call exit + + ret +;==================== +ENDP FixSelector + + + +END + + + + + diff --git a/WWFLAT32/DESCMGMT/FGETCS.ASM b/WWFLAT32/DESCMGMT/FGETCS.ASM new file mode 100644 index 0000000..e2259fc --- /dev/null +++ b/WWFLAT32/DESCMGMT/FGETCS.ASM @@ -0,0 +1,78 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the code selector * +;* * +;* File Name : FGETCS.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetCs -- Return the current Data selector. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetCs :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GETCS -- Return the current Data selector. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: UWORD selector of the default code segment * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetCs C near + + xor eax,eax + mov ax,cs + ret +;==================== +ENDP GetCs + + +END + + + + + diff --git a/WWFLAT32/DESCMGMT/FGETDS.ASM b/WWFLAT32/DESCMGMT/FGETDS.ASM new file mode 100644 index 0000000..379b1ac --- /dev/null +++ b/WWFLAT32/DESCMGMT/FGETDS.ASM @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the data selector * +;* * +;* File Name : FGETDS.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetDs -- Return the current Data selector. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetDs :NEAR + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GETDS -- Return the current Data selector. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: UWORD selector of the default data segment * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetDs C near + + xor eax,eax + mov ax,ds + ret +;==================== +ENDP GetDs + + +END + + + + diff --git a/WWFLAT32/DESCMGMT/FGETSEL.ASM b/WWFLAT32/DESCMGMT/FGETSEL.ASM new file mode 100644 index 0000000..5fa0519 --- /dev/null +++ b/WWFLAT32/DESCMGMT/FGETSEL.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Get the Defines selectors * +;* * +;* File Name : FGETSEL.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : March 28, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GetDefaultSelectors -- Return the current default selectors. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL GetDefaultSelectors :NEAR + +GLOBAL DataSelector :WORD +GLOBAL ScreenSelector :WORD +GLOBAL GraphicsSelector :WORD +GLOBAL PspSelector :WORD +GLOBAL EnvSelector :WORD +GLOBAL DosMemSelector :WORD +GLOBAL Fp1167Selector :WORD +GLOBAL FpWeitekSelector :WORD +GLOBAL FpCyrixSelector :WORD +GLOBAL CodeSelector :WORD + + +DATASEG + +; It is very important that this section remain untouch +; is not really needed by Rational System but is here to +; keep compatibility with the TNT dos extender. +DataSelector dw 0 +ScreenSelector dw 0 +GraphicsSelector dw 0 + +PspSelector dw 0 +EnvSelector dw 0 +DosMemSelector dw 0 + +Fp1167Selector dw 0 +FpWeitekSelector dw 0 +FpCyrixSelector dw 0 + +CodeSelector dw 0 + + +;============================================================================ +CODESEG + +;*************************************************************************** +;* GetDefaultSelectors -- Setup the defaults selector values to have the * +;* Correct Descriptor table and IOPL. * +;* * +;* * +;* * +;* INPUT: NONE * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/28/1994 jaw: Created. * +;*=========================================================================* + +PROC GetDefaultSelectors C near + USES eax,esi,ecx + + lea edi,[DataSelector] + lea ecx,[CodeSelector] + sub ecx,edi + shr ecx,1 + mov ax,ds + rep stosw + mov ax,cs + mov [word ptr CodeSelector] , ax + + ret +;==================== +ENDP GetDefaultSelectors + + +END + + + + diff --git a/WWFLAT32/DESCMGMT/MAKEFILE b/WWFLAT32/DESCMGMT/MAKEFILE new file mode 100644 index 0000000..1f55661 --- /dev/null +++ b/WWFLAT32/DESCMGMT/MAKEFILE @@ -0,0 +1,183 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = descmgmt +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_PATH = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + descmgmt.obj & + fgetsel.obj & + fgetcs.obj & + fgetds.obj & + ffixsel.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_PATH)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_PATH)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/DIPTHONG/DIPTHONG.CPP b/WWFLAT32/DIPTHONG/DIPTHONG.CPP new file mode 100644 index 0000000..7277a78 --- /dev/null +++ b/WWFLAT32/DIPTHONG/DIPTHONG.CPP @@ -0,0 +1,277 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + *dest++ = (*src++) + 127; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int len; // Length of output string. + int common, // Common character index. + dipthong; // Dipthong character index. + + len = 0; // No output characters YET. + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = KA_EXTEND; + len++; + first -= 127; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char)(common << 3) | (unsigned char)dipthong | 0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + len++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(len); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + unsigned int offset; + + if (!data || string < 0) return(NULL); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/WWFLAT32/DIPTHONG/DIPTHONG.H b/WWFLAT32/DIPTHONG/DIPTHONG.H new file mode 100644 index 0000000..2f5ed70 --- /dev/null +++ b/WWFLAT32/DIPTHONG/DIPTHONG.H @@ -0,0 +1,24 @@ +/* +** 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 . +*/ + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/WWFLAT32/DIPTHONG/MAKEFILE b/WWFLAT32/DIPTHONG/MAKEFILE new file mode 100644 index 0000000..e409e2d --- /dev/null +++ b/WWFLAT32/DIPTHONG/MAKEFILE @@ -0,0 +1,193 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = dipthong +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + dipthong.obj & + _diptabl.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/DIPTHONG/_DIPTABL.CPP b/WWFLAT32/DIPTHONG/_DIPTABL.CPP new file mode 100644 index 0000000..885fb41 --- /dev/null +++ b/WWFLAT32/DIPTHONG/_DIPTABL.CPP @@ -0,0 +1,58 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/WWFLAT32/EXAMPLE/DEFINES.H b/WWFLAT32/EXAMPLE/DEFINES.H new file mode 100644 index 0000000..9496789 --- /dev/null +++ b/WWFLAT32/EXAMPLE/DEFINES.H @@ -0,0 +1,39 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/WWFLAT32/EXAMPLE/EXTERNS.H b/WWFLAT32/EXAMPLE/EXTERNS.H new file mode 100644 index 0000000..27d38cd --- /dev/null +++ b/WWFLAT32/EXAMPLE/EXTERNS.H @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/WWFLAT32/EXAMPLE/FUNCTION.H b/WWFLAT32/EXAMPLE/FUNCTION.H new file mode 100644 index 0000000..654ce12 --- /dev/null +++ b/WWFLAT32/EXAMPLE/FUNCTION.H @@ -0,0 +1,50 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Example * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwlib32.h" +#include "defines.h" +#include "structs.h" +#include "externs.h" + + +/*=========================================================================*/ +/* The following prototypes are for the file: MAIN.CPP */ +/*=========================================================================*/ + +extern WORD Main_Program(WORD argc, BYTE *argv[]); + + +/*=========================================================================*/ diff --git a/WWFLAT32/EXAMPLE/GLOBALS.CPP b/WWFLAT32/EXAMPLE/GLOBALS.CPP new file mode 100644 index 0000000..259ac31 --- /dev/null +++ b/WWFLAT32/EXAMPLE/GLOBALS.CPP @@ -0,0 +1,40 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Examples * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +char NoTimer; +char NoKeyBoard; diff --git a/WWFLAT32/EXAMPLE/MAIN.CPP b/WWFLAT32/EXAMPLE/MAIN.CPP new file mode 100644 index 0000000..9a904b1 --- /dev/null +++ b/WWFLAT32/EXAMPLE/MAIN.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : libtest * + * * + * File Name : LIBTEST.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 27, 1994 * + * * + * Last Update : May 3, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Main_Program -- user-defined main routine, called from startup.c * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + + +/*************************************************************************** + * Main_Program -- user-defined main routine, called from startup.c * + * * + * INPUT: * + * WORD argc * + * UBYTE *argv[] * + * * + * OUTPUT: * + * Returns: TRUE * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1994 BR : Created. * + *=========================================================================*/ +#pragma argsused +WORD Main_Program(WORD argc, BYTE *argv[]) +{ + return (TRUE); +} + + +//////////////////////////////////// End of File ///////////////////////////////////// diff --git a/WWFLAT32/EXAMPLE/MAKEFILE b/WWFLAT32/EXAMPLE/MAKEFILE new file mode 100644 index 0000000..d1a2d3e --- /dev/null +++ b/WWFLAT32/EXAMPLE/MAKEFILE @@ -0,0 +1,171 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\example\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + main.obj & + startup.obj & + globals.obj + +PROJ_LIBS = & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/EXAMPLE/STARTUP.CPP b/WWFLAT32/EXAMPLE/STARTUP.CPP new file mode 100644 index 0000000..78dc421 --- /dev/null +++ b/WWFLAT32/EXAMPLE/STARTUP.CPP @@ -0,0 +1,194 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Library startup routine. * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 14, 1994 * + * * + * Last Update : August 1, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Prog_End -- Called to shutdown Westood's library. * + * main -- Programs main entry point. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +#define GRAPHICS TRUE +void *ShapeBuffer = NULL; + + +/*************************************************************************** + * MAIN -- Programs main entry point. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/01/1994 SKB : Created. * + *=========================================================================*/ +#pragma argsused +WORD main(WORD argc, BYTE *argv[]) +{ + void *fontptr; + + /*======================================================================*/ + /* Install page fault handle in case of fatal crash. */ + /*======================================================================*/ + Install_Page_Fault_Handle (); + + /*======================================================================*/ + /* Setup the monochrome monitor for testing. */ + /*======================================================================*/ + + MonoEnabled = (Find_Argv("-MONO") ? TRUE : FALSE); + Mono_Clear_Screen(); + + /*======================================================================*/ + /* Initialize the file data table. */ + /*======================================================================*/ + WWDOS_Init(200, NULL, NULL); + + /*======================================================================*/ + /* Initialize the system font. */ + /*======================================================================*/ +#if GRAPHICS + fontptr = Load_Font("STD6P.FNT"); + if (!fontptr) { + printf("Unable to load font."); + exit(1); + } + Set_Font(fontptr); +#endif + + /*======================================================================*/ + /* Setup the timer system. */ + /*======================================================================*/ + if (Find_Argv("-NOTIME")) { + NoTimer = TRUE; + } else { + Init_Timer_System(USER_TIMER_FREQ); + NoTimer = FALSE; + } + + /*======================================================================*/ + /* Get the initial graphic mode. */ + /*======================================================================*/ +#if GRAPHICS + if ( Set_Video_Mode(MCGA_MODE) == FALSE ) + { + printf("Unable to Set Graphic Mode\n"); + exit ( 0 ) ; + } +#endif + + + /*======================================================================*/ + /* Now we get a keyboard handler. */ + /*======================================================================*/ + if (Find_Argv("-NOKEY")) { + NoKeyBoard = TRUE; + } else { + NoKeyBoard = FALSE; + Install_Keyboard_Interrupt( Get_RM_Keyboard_Address(), Get_RM_Keyboard_Size()); + + ShapeBuffer = Alloc(5000, MEM_NORMAL); + Set_Shape_Buffer(ShapeBuffer, 5000); + Install_Mouse(20, 20, 320, 200); + } + + /*======================================================================*/ + /* Give the game some variance. */ + /*======================================================================*/ + randomize(); + + /*======================================================================*/ + /* Call the user main program. */ + /*======================================================================*/ + Main_Program(argc, argv); + + /*======================================================================*/ + /* Exit gracefully. */ + /*======================================================================*/ + Prog_End(); + + return(0); +} + + + +/*************************************************************************** + * PROG_END -- Called to shutdown Westood's library. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: exit() should not be called until this has been called * + * * + * HISTORY: * + * 08/01/1994 SKB : Created. * + *=========================================================================*/ +VOID Prog_End(VOID) +{ + + /*======================================================================*/ + /* Get rid of the keyboard handler. */ + /*======================================================================*/ + if (!NoKeyBoard) { + Remove_Mouse(); + Free(ShapeBuffer); + Remove_Keyboard_Interrupt(); + } + + /*======================================================================*/ + /* Get rid of the timer system. */ + /*======================================================================*/ + if (!NoTimer) { + Remove_Timer_System(); + } + + /*======================================================================*/ + /* Restore the Video mode. */ + /*======================================================================*/ +#if GRAPHICS + Set_Video_Mode(RESET_MODE); +#endif + + /*======================================================================*/ + /* Close down the file system. */ + /*======================================================================*/ + WWDOS_Shutdown(); +} diff --git a/WWFLAT32/EXAMPLE/STRUCTS.H b/WWFLAT32/EXAMPLE/STRUCTS.H new file mode 100644 index 0000000..3184818 --- /dev/null +++ b/WWFLAT32/EXAMPLE/STRUCTS.H @@ -0,0 +1,37 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/WWFLAT32/EXAMPLE/WWLIB32.H b/WWFLAT32/EXAMPLE/WWLIB32.H new file mode 100644 index 0000000..f8cba6d --- /dev/null +++ b/WWFLAT32/EXAMPLE/WWLIB32.H @@ -0,0 +1,63 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H diff --git a/WWFLAT32/FILE/DEVICES.ASM b/WWFLAT32/FILE/DEVICES.ASM new file mode 100644 index 0000000..bb17f9d --- /dev/null +++ b/WWFLAT32/FILE/DEVICES.ASM @@ -0,0 +1,165 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/devices.asm 1.2 1994/04/28 12:41:41 jeff_wilson Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : DEVICES.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 12 December, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Get_Devices(VOID); * +; WORD Is_Device_Real(WORD drive); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +;---------------------------------------------------------------------------- + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Get_Devices :NEAR +GLOBAL Is_Device_Real :NEAR + +GLOBAL MaxDevice :BYTE +GLOBAL DefaultDrive :BYTE + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; VOID Get_Devices(VOID); +; WORD Is_Device_Real(WORD drive); +; +; ---------------------------------------------------------------- + +CODESEG + +;*********************************************************** +; +; GET_DEVICES +; +; VOID Get_Devices(VOID); +; +; This routine establishes the default disk drive and the maximum drive +; available in the current system. +; +;* +DOS equ 21h + +PROC Get_Devices C near + USES eax,ebx,edx + + sub eax,eax + mov ah,25 ; get current drive service + int DOS ; drive returned in al + mov [DefaultDrive],al ; save it + mov dl,al + mov ah,14 ; set current as current drive + int DOS + dec al ; al = max drives, make it n - 1 + xor ah,ah ; clear high byte + mov edx,eax ; use dx to go backward to find out + sub ebx,ebx + +??back_loop: + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + jnc short ??later ; if c clear, no error + cmp al,0Fh ; was it invalid? (0Fh = invalid) + jne short ??later ; yes, so LATER + dec edx + jmp ??back_loop ; try, try again + +??later: + mov eax,edx ; restore ax + mov [MaxDevice],al ; save the max drive # + + ret + +ENDP Get_Devices + +;*************************************************************** + + +;*************************************************************** +; +; IS_DEVICE_REAL +; +; WORD Is_Device_Real(WORD drive); +; +; This routine will tell whether or not a device is a true +; phisical one. Send it the drive # to check. +; +;* +PROC Is_Device_Real C near + USES ebx,edx + ARG drive:WORD + + sub edx,edx + mov dx,[drive] + +??next_drive: + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + + jnc short ??it_is_real ; jump if no error + cmp al,01 ; 1 = invalid command, + ; 0F = invalid device + je short ??real ; 1? it is ok (RAM device) + jmp short ??invalid ; 0Fh, it was not a device + +??it_is_real: + cmp al,0 ; was it a fixed device? + je short ??real ; yes, it's ok + dec al ; make it a drive # + cmp al,dl ; is it a valid drive? + je short ??real + +??invalid: ; The device is invalid. + mov eax,0 + jmp short ??out + +??real: ; Return true, for valid device. + mov eax,1 + +??out: + ret +ENDP Is_Device_Real + +;*************************************************************** + +END + \ No newline at end of file diff --git a/WWFLAT32/FILE/DEVTABLE.ASM b/WWFLAT32/FILE/DEVTABLE.ASM new file mode 100644 index 0000000..9dfcce7 --- /dev/null +++ b/WWFLAT32/FILE/DEVTABLE.ASM @@ -0,0 +1,204 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/devtable.asm 1.2 1994/04/28 12:41:29 jeff_wilson Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : DEVTABLE.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 12 December, 1990 [CY] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; VOID Init_Device_Table(BYTE *table); * +; WORD Max_Device(VOID); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +DOS equ 21h + +GLOBAL Max_Device :NEAR +GLOBAL get_max_device :NEAR +GLOBAL Init_Device_Table :NEAR + + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; VOID Init_Device_Table(BYTE *table); +; WORD Max_Device(VOID); +; +; ---------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; WORD Max_Device(VOID); +; + +PROC Max_Device C NEAR + + call get_max_device ; get max devices in ax + ret + +ENDP Max_Device + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; +; returns max devices in AX + +PROC get_max_device C NEAR + USES ebx,edx + + mov ah,25 ; get current drive service + int DOS ; drive returned in al + mov dl,al + mov ah,14 ; set current as current drive + int DOS + dec al ; al = max drives, make it n - 1 + xor ah,ah ; clear high byte + sub edx,edx + mov edx,eax ; use dx to go backward to find out + ; if DOS is lying (down) + +??back_loop: + push ds + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + pop ds + jnc short ??later ; if c clear, no error + + cmp al,0Fh ; was it invalid? (0Fh = invalid) + jne short ??later ; yes, so LATER + + dec edx + jmp ??back_loop ; try, try again + +??later: + mov eax,edx ; restore ax + ret + +ENDP get_max_device + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; +; VOID Init_Device_Table(BYTE *table); +; + +PROC Init_Device_Table C NEAR + + USES eax,ebx,edi,edx + ARG table:DWORD ; Pointer to device table. + LOCAL curr_drive:BYTE ; Copy of current drive number. + + mov edi,[table] + + call get_max_device ; get max devices in ax + add edi,eax + std + mov [curr_drive],al ; save it + +??next_drive: + mov dl,[curr_drive] ; copy current drive # + cmp dl,0FFh ; are we done? + je short ??later ; if so, later + + dec [curr_drive] ; dec our local drive # + + push ds + push ebx + mov bl,dl ; find out about the drive in dl + inc bl + mov eax,0440Eh ; get the physical drive associated + int DOS ; with this letter + pop ebx + pop ds + + jnc short ??it_is_real ; jump if no error + cmp al,01 ; 1 = invalid command, + ; 0F = invalid device + je short ??set_as_current ; 1? it is ok (RAM device) + jmp short ??invalid ; 0Fh, it was not a device + + +??it_is_real: + cmp al,0 ; was it a fixed device? + je short ??set_as_current ; yes, it's ok + + dec al ; make it a drive # + cmp al,dl ; is it a valid drive? + je short ??set_as_current + +; +; Device was logical and not active, so clear the entry +; +??invalid: + xor al,al + stosb + cmp [curr_drive],0 ; are we done checking? + jge ??next_drive ; no, go to next + + jmp short ??later + +??set_as_current: + mov al,1 + stosb + cmp dl,0 ; are we before the A drive (invalid) + jl short ??later ; yes, we are done checking + + jmp ??next_drive ; keep processing + +??later: + cld + ret + +ENDP Init_Device_Table + +;---------------------------------------------------------------------------- + +END + \ No newline at end of file diff --git a/WWFLAT32/FILE/FFIRST.ASM b/WWFLAT32/FILE/FFIRST.ASM new file mode 100644 index 0000000..edda9ba --- /dev/null +++ b/WWFLAT32/FILE/FFIRST.ASM @@ -0,0 +1,238 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : First First * +;* * +;* File Name : FFIRST.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : April 15, 1994 [] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Find_First -- Find a file spec * +;* Find_Next -- Find next file in sreach params * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +GLOBAL Find_First :NEAR +GLOBAL Find_Next :NEAR + +;============================================================================ +CODESEG + + +;*************************************************************************** +;* FIND_FIRST -- Find a file spec * +;* * +;* * +;* * +;* INPUT: * +;* file_name File spec to find. Maybe a wildcard name * +;* mode File type * +;* ffblk file data block ptr to write info into * +;* * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/15/1994 jaw: Created. * +;*=========================================================================* + +PROC Find_First C near + USES ebx,ecx,edx,esi,edi,es,ds + ARG file_name:DWORD,mode:WORD,ffblk:DWORD + + mov edx,[file_name] + mov cx,[mode] + + mov eax,4e00h ;first firstg function + + int 21h + ;Find it? + jnc ??found_it ;=>yes + + ; ax holds the error code + ;insure high word of eax is clear + or eax,0ffffffffh + jmp ??exit + +??found_it: + ; found something + ;copy the DTA into the user block + mov eax,2f00h ;get DTA address + int 21h + + mov ax,es ;switch selectors + mov dx,ds + mov ds,ax + mov es,dx + + mov esi,ebx + mov edi,[ffblk] + + add esi,21 ;SKIP RESERVED + add edi,4 ;SKIP RESERVED + + sub eax,eax + mov al,[esi] ;get attrib byte + mov [es:edi+4],eax + inc esi + + ;get time + mov ax,[esi] + add esi,2 + mov [es:edi+8],ax + + ;get date + mov ax,[esi] + add esi,2 + mov [es:edi+10],ax + + ;get file size + mov eax,[esi] + add esi,4 + mov [es:edi],eax + + add edi,12 + + mov ecx,13 + + rep movsb ;copy the DTA name + + mov ax,es + mov ds,ax + + xor eax,eax +??exit: + ret +;==================== +ENDP Find_First + + + +;*************************************************************************** +;* FIND_NEXT -- Find next file in sreach params * +;* * +;* * +;* * +;* INPUT: * +;* NONE * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/15/1994 jaw: Created. * +;*=========================================================================* + +PROC Find_Next C near + USES ebx,ecx,edx,esi,edi,ds,es + + ARG ffblk:DWORD + + mov eax,04f00h ;Find Next function + + int 21h + ;Find anything? + jnc ??found_it ;=>no + + ; ax holds the error code + ;insure high word of eax is clear + or eax,0ffffffffh + jmp ??exit + +??found_it: + ; found something + ;copy the DTA into the user block + mov eax,2f00h ;get DTA address + int 21h + + mov ax,es ;switch selectors + mov dx,ds + mov ds,ax + mov es,dx + + mov esi,ebx + mov edi,[ffblk] + + add esi,21 ;SKIP RESERVED + add edi,4 ;SKIP RESERVED + + sub eax,eax + mov al,[esi] ;get attrib byte + mov [es:edi+4],eax + inc esi + + ;get time + mov ax,[esi] + add esi,2 + mov [es:edi+8],ax + + ;get date + mov ax,[esi] + add esi,2 + mov [es:edi+10],ax + + ;get file size + mov eax,[esi] + add esi,4 + mov [es:edi],eax + + add edi,12 + + mov ecx,13 + + rep movsb ;copy the DTA name + + mov ax,es + mov ds,ax + + xor eax,eax +??exit: + ret + +ENDP Find_Next + + +END + + + + + + + + + \ No newline at end of file diff --git a/WWFLAT32/FILE/FGLOB2.CPP b/WWFLAT32/FILE/FGLOB2.CPP new file mode 100644 index 0000000..ded3620 --- /dev/null +++ b/WWFLAT32/FILE/FGLOB2.CPP @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library * + * * + * File Name : FILEGLOB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "_file.h" + +/* Global varaiables */ +WORD Hard_Error_Occured=0; diff --git a/WWFLAT32/FILE/FILE.BAK b/WWFLAT32/FILE/FILE.BAK new file mode 100644 index 0000000..9722fc6 --- /dev/null +++ b/WWFLAT32/FILE/FILE.BAK @@ -0,0 +1,261 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + + +#ifndef ERROR +#define ERROR -1 +#endif + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern short Find_First(unsigned char *fname, unsigned short mode, struct find_t *ffblk); +extern short Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif // FILE_H + diff --git a/WWFLAT32/FILE/FILE.CPP b/WWFLAT32/FILE/FILE.CPP new file mode 100644 index 0000000..5c13e01 --- /dev/null +++ b/WWFLAT32/FILE/FILE.CPP @@ -0,0 +1,1070 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : FILE.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * File_Exists -- Searches for and FINDS the specified file. * + * File_Size -- Determine the size of the specified file. * + * Open_File -- Opens a file for access. * + * Close_File -- Closes a file previously opened with Open_File. * + * Seek_File -- Adjusts the position of the file pointer. * + * Read_File -- Reads a block of data from a file. * + * Write_File -- Writes a block of data to a file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include +#endif + +#ifndef FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + + +#include +#include +#include + +#define DEBUGPRINT FALSE + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_FILE -- Opens a file for access. * + * * + * This routine will open the specified file for access. It functions * + * in a similar manner to the regular DOS open command. * + * * + * INPUT: filename -- Pointer to the filename to open. * + * * + * mode -- Access mode to use. * + * READ: A file opened for READ access, MUST be available. * + * This may cause a disk swap message to appear. * + * * + * WRITE:A file opened for WRITE access (only), need not be * + * available. If it can't be found, then it will be * + * created in the current directory. * + * * + * OUTPUT: Returns with the westwood file handle. If ERROR is returned * + * it means that the programmer took over the file error handler * + * and signaled that an open access failure should not keep trying* + * for success. * + * * + * WARNINGS: If you take over the file error handling code, you must be * + * sure to anticipate a possible ERROR value being returned. * + * If you open a file for READ or READ|WRITE, then the file * + * must previously exist. To create a file for read and write * + * access, first open it for WRITE, then re-open it for * + * read and write. * + * * + * HISTORY: * + * 11/11/1991 JLB : Created. * + *=========================================================================*/ +int cdecl Open_File(char const *file_name, int mode) +{ + int file_handle; // Westwood file handle. + int handle; // DOS file handle. + unsigned int local_mode; // DOS access mode number. + int index; // FileData index (if available). + BOOL immediate; // Is the file immediately available? + FileDataType *filedata; // Pointer to the current FileData. + FileDataType *parent; // Pointer to the current FileData. + FileHandleType *filehandletable; // Pointer to the current file handle. + + CallingDOSInt++; + + + #if(DEBUGPRINT) + Mono_Printf("Open_File('%s')\r", file_name); + #endif + + /* + ** Is there a slot in the FileHandleTable? If not, then exit with + ** a file error. + */ + filehandletable = FileHandleTable; + for (file_handle = 0; file_handle < TABLE_MAX; file_handle++, filehandletable++) { + if (filehandletable->Empty) break; + } + if (file_handle == TABLE_MAX) { + Do_IO_Error(TOO_MANY_FILES, file_name); + } + + /* + ** Find the file in the FileTable (if it exists there). + */ + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + // Start out looking at the start drive. + ibm_setdisk(*StartPath - 'A'); + + /* + ** If the file exists in the current directory, then by all + ** means, use that file instead of any corresponding pack + ** file representation. This is used as a means of + ** overriding packed files (quite handy). + */ + immediate = FALSE; + + /* + ** Check the current directory by attempting to open with READ access. + ** Only check if the file is not already cached and it is a packed file. + */ + if ((index != ERROR) && (filedata->Flag & FILEF_PACKED) && (!filedata->Ptr)) + { + int handle2; + + handle2 = FILEOPEN(file_name, MODE_OLDFILE); + if (handle2 != FILEOPENERROR) { + FILECLOSE(handle2); + immediate = TRUE; + } + } + + + /* + ** Check to see if any WRITE permission is requested for the + ** file. If so, delete the file from RAM and mark it as non-resident. + ** + ** NOTE: If the file is packed, we CAN NOT alter it in ANY way. + ** We will return an ERROR to the user, and hope he/she is + ** smart enough to handle it. + */ + if ((mode & WRITE) && index != ERROR && filedata->Ptr) { + if(filedata->Flag & FILEF_PACKED) { // is file packed? + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } else { // file is packed + Do_IO_Error(WRITING_RESIDENT, file_name); + } + } + + // If the file is cached already, then let us use it and leave. + if (filedata->Ptr && index != ERROR) { + /* + ** File is resident. Initialize the local table. + */ + filehandletable->Index = index; + filehandletable->Empty = FALSE; + filehandletable->Pos = 0L; + filehandletable->Name = filedata->Name; + filehandletable->Handle = 0; // Resident flag. + filehandletable->Mode = mode; + + // If the cached file is packed and its parent is resident, then + // add one to the parent open count. + if (filedata->Flag & FILEF_PACKED) { + parent = &FileDataPtr[filedata->Disk]; + } + else { + parent = NULL; + } + + if (parent && (parent->Ptr == filedata->Ptr)) { + filehandletable->Start = filedata->Start; + parent->OpenCount++; + } else { + filehandletable->Start = 0; + } + + // Mark the pointer in use so that it is never returned as the oldest + // pointer location in memory. + Mem_In_Use(filedata->Ptr); + + } else { + + /* + ** At this time we determine if it is a packed file. If so, the + ** opening process takes a dramatically different turn. + */ + if (index != ERROR && (filedata->Flag & FILEF_PACKED) && !immediate && (mode & WRITE) == 0) { + + /* + ** This is a packed file. Perform recursive open process to reach + ** the parent packed file. + */ + parent = &FileDataPtr[filedata->Disk]; + file_handle = Open_File(parent->Name, mode); + + // Reread the parents information. + parent = &FileDataPtr[filedata->Disk]; + filehandletable = &FileHandleTable[file_handle]; + + /* + ** Process the packed file header. Update the start offset for every + ** file ellaborated in the packed file. + ** Exception: When a file is specified in the packed file and an + ** entry exists for it in the file table, BUT the entry + ** is not marked as packed, THEN ignore the packed version + ** of the file. + */ + + if (FileData == FileDataPtr && + !(FileDataPtr[filehandletable->Index].Flag & FILEF_PROCESSED)) { + long offset; // Offset of sub-file start. + int i; // Sub-file FileData index. + char name[FILENAMESIZE]; // Name of sub-file. + FileDataType *cur=NULL; // Current entry in FileData. + FileDataType *last=NULL; // Last entry in FileData. + + + #if(DEBUGPRINT) + Mono_Printf("Processing packed file '%s'\r", FileDataPtr[filedata->Disk].Name); + #endif + + Read_File(file_handle, &offset, (unsigned long)sizeof(long)); + while (offset) { + + /* + ** Due to the fact that the embedded file name is of arbitrary + ** length, we must read it in a byte at a time. Reading stops + ** when the terminating NULL is found. + */ + i=0; + do { + Read_File(file_handle, &name[i], (unsigned long)sizeof(name[0])); + } while (name[i++]); + + i = Find_File_Index(name); + if (i != ERROR) { + cur = &FileDataPtr[i]; + if ((cur->Flag & FILEF_PACKED) && cur->Disk == filehandletable->Index) { + + cur->Start = offset + FileDataPtr[ filehandletable->Index ].Start; + if (last != NULL) { + last->Size = cur->Start - last->Start; + } + + last = cur; + } + } else { + // ERROR: File specified in packed file has no table entry. + // When this occurs, the embedded file is ignored. + } + + Read_File(file_handle, &offset, (unsigned long)sizeof(long)); + } + + /* + ** Fixup the size record of the last embedded file. + */ + if (last != NULL) { + last->Size = File_Size(file_handle) - last->Start; + } + + FileDataPtr[filehandletable->Index].Flag |= FILEF_PROCESSED; + } + + // if the parent is resident, the the child must have the same address. + filedata->Ptr = parent->Ptr; + + /* + ** Set starting position to start of embedded file. + */ + filehandletable->Index = index; + filehandletable->Name = filedata->Name; + filehandletable->Start = filedata->Start; + + Seek_File(file_handle, NULL, SEEK_SET); + + // Attempt to cache the file if wanted, and room. + Cache_File(index, file_handle); + + } else { + + /* + ** Convert the Westwood access mode into the system specific mode. + */ + local_mode = MODE_OLDFILE; + switch (mode) { + case READ: + local_mode = MODE_OLDFILE; + break; + + case READ | WRITE: + #if(IBM) + local_mode = MODE_READWRITE; + #else + local_mode = MODE_OLDFILE; + #endif + break; + + case WRITE: + local_mode = MODE_NEWFILE; + break; + + + default: + Do_IO_Error(BAD_OPEN_MODE, file_name); + break; + } + + + /* + ** The file is disk based. Perform the DOS open processing. + */ + + /* + ** Make sure the file is available or the proper disk is inserted. + ** The file MUST exist if READ access is requested. A mild attempt + ** will be made to find the file if open for WRITE only. + */ + if (mode & READ) { + if (!File_Exists(file_name)) { + CallingDOSInt--; + return(ERROR); /* Ignore the error */ + } + } + + + /* + ** Repetitively try to open the file until the error handler + ** routine indicates otherwise. + */ + handle = FILEOPENERROR; + for (;;) { + handle = Open_File_With_Recovery( file_name, local_mode ); + if (handle != FILEOPENERROR) { + break; + } else { + CHANGEDIR( DataPath ); + CHANGEDIR( StartPath ); + if (!Do_Open_Error(COULD_NOT_OPEN, file_name)) { + CallingDOSInt--; + return(FILEOPENERROR); + } + } + } + + /* + ** A file that is read off of disk cannot be part of + ** a pack file regardless of the bit settting. + */ + if (immediate && index != ERROR) { + filedata->Flag &= ~FILEF_PACKED; + filedata->Size = 0; + } + + if (index != ERROR) { + + // The true file size is needed. + // Go back to beginning of file. + filedata->Size = FILESEEK(handle, 0L, SEEK_END); + FILESEEK(handle, 0L, SEEK_SET); + } + + /* + ** Initialize the local file handle table. + */ + filehandletable->Index = index; + filehandletable->Pos = 0L; + filehandletable->Start = 0L; + filehandletable->Empty = FALSE; + filehandletable->Handle = handle; + filehandletable->Mode = mode; + if (index != ERROR) { + filehandletable->Name = filedata->Name; + } else { + filehandletable->Name = NULL; + } + + /* + ** If the file should be made resident, then do it at this time. + ** Perform all necessary adjustments to the file tables. + */ + if ((mode & WRITE) == 0) { + Cache_File(index, file_handle); + } + } + } + + // If in the file table, increment the number of opens on this file. + if (index != ERROR) { + filedata->OpenCount++; + } + + /* + ** Return with valid file handle. + */ + CallingDOSInt--; + return(file_handle); +} + + +/*************************************************************************** + * CLOSE_FILE -- Closes a file previously opened with Open_File. * + * * + * Use this routine to close a file that was opened with Open_File. * + * This is the only way to close a file that was opened with the * + * Westwood file I/O system. * + * * + * INPUT: handle -- Westwood file handle. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1991 JLB : Created. * + *=========================================================================*/ +VOID cdecl Close_File(int handle) +{ + int index; // File data table index. + BOOL flushed = FALSE; // If file flushed from cache, do change time stamp. + BOOL stillopen; + FileDataType *parent = NULL; + FileDataType *filedata = NULL; + VOID *memptr=NULL; + + + #if(DEBUGPRINT) + Mono_Printf("Close_File(%d)\r", handle); + #endif + + + /* + ** Check for file handle validity. + */ + if (!Is_Handle_Valid(handle, CLOSING_NON_HANDLE, NULL)) { + return; + } + + CallingDOSInt++; + + index = FileHandleTable[handle].Index; + filedata = &FileDataPtr[index]; + + // Remove the OpenCount on the file. + if (index != ERROR) { + + filedata->OpenCount--; + stillopen = filedata->OpenCount; + + // If this file packed in another, then decrement the parents open count. + if (filedata->Flag & FILEF_PACKED) { + parent = &FileDataPtr[filedata->Disk]; + + // Do not dec OpenCount if the child was cached but not the parent. + if (parent->Ptr == filedata->Ptr) { + parent->OpenCount--; + stillopen = parent->OpenCount; + } + } + } + + + // If the file was resident, then handle bookeeping. + if (index != ERROR && filedata->Ptr) { + + // Get a pointer to the memory area for later use. + memptr = filedata->Ptr; + + // If file has a parent, and it is the cached file... + if (parent && (parent->Ptr == filedata->Ptr)) { + + // The PAK'd file is closed just by setting the pointer. + if (!filedata->OpenCount) { + filedata->Ptr = NULL; + } + + // Uncache parent if no other sons are open and it should be flushed. + if ((!parent->OpenCount) && (parent->Flag & FILEF_FLUSH)) { + Mem_Free(FileCacheHeap, parent->Ptr); + parent->Ptr = NULL; + flushed = TRUE; + } + } + // Else should the file be flushed? Only flush it if the flag says + // so AND there are no other open counts on it. + else if ((filedata->Flag & FILEF_FLUSH) && !filedata->OpenCount) { + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + flushed = TRUE; + } + + // + // Set the time stamp on the close IF + // the file was not flush AND + // is not open AND + // its time stamp should be changed by being a FILEF_KEEP|PROIORTY file. + // + if (!flushed && !stillopen && !(filedata->Flag & (FILEF_KEEP|FILEF_PRIORITY))) { + Mem_Reference(memptr); + } + } + // Just a simple close will do. + else { + FILECLOSE(FileHandleTable[handle].Handle); + } + + // The WWS handle is no longer being used. + FileHandleTable[handle].Empty = TRUE; + + CallingDOSInt--; +} + + + +/*************************************************************************** + * READ_FILE -- Reads a block of data from a file. * + * * + * This routine is used to read a block of data from a previously * + * opened file. * + * * + * INPUT: handle -- Westwood file handle returned from Open_File. * + * * + * buf -- Pointer to buffer to load the data into. * + * * + * bytes -- Number of bytes to load. * + * * + * OUTPUT: Returns with the number of bytes actually read from the file. * + * If this number is less than the number requested, this * + * indicates the end of the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +long cdecl Read_File(int handle, VOID *buf, unsigned long bytes) +{ + int doshandle; // DOS file handle. + int fileindex; // File table index. + long bytesread; // Accumulation of number of bytes read. + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Read_File(%d, %08lx, %ld)\r", handle, buf, bytes); + #endif + + /* + ** The handle must be valid or else it is an error. + */ + if (!buf || !bytes || !Is_Handle_Valid(handle, READING_NON_HANDLE, NULL)) { + return(NULL); + } + + CallingDOSInt++; + + /* + ** Prepare some working variables. + */ + doshandle = FileHandleTable[handle].Handle; + fileindex = FileHandleTable[handle].Index; + + + /* + ** Limit the number of bytes to read according to the size of the + ** file. If no file size is specified, then don't perform the check. + */ + if (fileindex != ERROR) { + filedata = &FileDataPtr[fileindex]; + + if (filedata->Size) { + bytes = MIN((unsigned long)filedata->Size - FileHandleTable[handle].Pos, bytes); + } + } + + /* + ** Starts with no bytes being read in. + */ + bytesread = 0; + + if (bytes) { + + if (doshandle) { + /* + ** Perform a DOS read of the file. Read the file in chunks. + */ + while (bytes) { + long number; // Number of bytes read. + + /* + ** Read in a chunk of file data. + */ + Hard_Error_Occured = 0; + number = (long)FILEREAD(doshandle, buf, MIN(bytes, IO_CHUNK_SIZE)); + + /* + ** if a hard error occurs, read its copy on a different directory + */ + if ( Hard_Error_Occured ) { + number = Read_File_With_Recovery( handle, buf, (unsigned int)MIN(bytes, IO_CHUNK_SIZE)); + doshandle = FileHandleTable[handle].Handle; + } + + /* + ** Adjust the remaining bytes to read counter and adjust the count + ** of the number of bytes actually read from the file. + */ + bytes -= number; + bytesread += number; + + /* + ** Adjust the file position (manually). + */ + FileHandleTable[handle].Pos += number; + + /* + ** If less than requested bytes were read from the file, then + ** we KNOW that the reading loop must terminate. + ** Was there an error? + */ + if (number < IO_CHUNK_SIZE) { + break; + } + + /* + ** Adjust the destination pointer in anticipation of the next + ** file read operation. + */ + // this is a BCC bug + // (char *)buf += number; + buf = (char* ) buf + number; + + } + + } else { + + #if (DEBUGPRINT) + Mono_Printf("Resident read '%s' %08lx[%ld].%ld\r", filedata->Name, (long)filedata->Start, FileHandleTable[handle].Pos, bytes); + #endif + + /* + ** Copy the block of the file into the specified buffer. + */ + Mem_Copy((VOID*)((long)filedata->Ptr + FileHandleTable[handle].Start + FileHandleTable[handle].Pos), buf, bytes); + bytesread = bytes; + + /* + ** Adjust the file position (manually). + */ + FileHandleTable[handle].Pos += bytes; + } + } + + /* + ** Return with the number of bytes read in from the file. + */ + CallingDOSInt--; + return(bytesread); +} + + +/*************************************************************************** + * WRITE_FILE -- Writes a block of data to a file. * + * * + * This routine will write a block of data to a file. The file must * + * have been previously opened with WRITE or READ|WRITE access. * + * Writing cannot occur to a resident or packed file. * + * * + * INPUT: handle -- File handle as returned by Open_File. * + * * + * buf -- Pointer to the buffer that holds the data to be * + * written out. * + * * + * bytes -- The number of bytes to write out. * + * * + * OUTPUT: Returns with the actual number of bytes written to the file. * + * * + * WARNINGS: If the value returned from this function is less than the * + * number of bytes requested to be written, then this * + * indicates some kind of error NOT caught by the file I/O * + * system. * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +long cdecl Write_File(int handle, VOID const *buf, unsigned long bytes) +{ + int doshandle; // DOS specific file handle. + int fileindex; // FileData table index (if any). + long byteswritten; // Accumulated number of bytes written. + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Write_File(%d, %08lx, %ld)\r", handle, buf, bytes); + #endif + + /* + ** Check to make sure the file handle is valid. + */ + if (!Is_Handle_Valid(handle, WRITING_NON_HANDLE, NULL)) { + return(NULL); + } + doshandle = FileHandleTable[handle].Handle; + fileindex = FileHandleTable[handle].Index; + filedata = &FileDataPtr[fileindex]; + + /* + ** Writing to a resident file is an error condition. + */ + if (!doshandle) { + Do_IO_Error(WRITING_RESIDENT, filedata->Name); + return(NULL); + } + + CallingDOSInt++; + + /* + ** Perform a DOS write of the data. This write is performed in blocks. + */ + byteswritten = NULL; + + while (bytes) { + long outbytes; // Number bytes written in one block. + + /* + ** Write out one block of data. + */ + outbytes = FILEWRITE(doshandle, (void*)buf, MIN((long)bytes, IO_CHUNK_SIZE)); + + /* + ** Reduce the bytes remaining to output counter and adjust the + ** file position. + */ + bytes -= outbytes; + byteswritten += outbytes; + FileHandleTable[handle].Pos += outbytes; + + /* + ** If NO bytes were written out, then this is some kind of unknown + ** error (possibly disk full?). + */ + if (!outbytes) { + break; + } + + /* + ** Possible adjustment to the file's size. + */ + if (fileindex != ERROR) { + filedata->Size = MAX(filedata->Size, FileHandleTable[handle].Pos); + } + + /* + ** Adjust the source pointer in anticipation of the next block write. + */ + buf = (char*)(((long)buf) + outbytes); + } + + /* + ** Return with the actual number of bytes written. + */ + CallingDOSInt--; + return (byteswritten); +} + + +/*************************************************************************** + * SEEK_FILE -- Adjusts the position of the file pointer. * + * * + * This routine adjusts the position of the file pointer. Use this * + * to control where the next Read_File or Write_File will occur. * + * * + * INPUT: handle -- File handle as returned by Open_File. * + * * + * offset -- Signed offset from indicated starting position. * + * * + * starting -- SEEK_CUR: Seeks from the current position. * + * SEEK_SET: Seeks from the start of the file. * + * SEEK_END: Seeks backward from the end. * + * * + * OUTPUT: Returns with the new file position. * + * * + * WARNINGS: Seeking past the end or before the beginning of the file * + * is not allowed. * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Seek_File(int handle, long offset, int starting) +{ + int doshandle; // DOS specific file handle. + int fileindex; // FileData index (if any). + FileDataType *filedata; // Pointer to the current FileData. + + #if(DEBUGPRINT) + Mono_Printf("Seek_File(%d, %ld, %d)\r", handle, offset, starting); + #endif + + /* + ** Check to make sure that the file handle is valid. + */ + if (!Is_Handle_Valid(handle, SEEKING_NON_HANDLE, NULL)) { + return(NULL); + } + + CallingDOSInt++; + + fileindex = FileHandleTable[handle].Index; + doshandle = FileHandleTable[handle].Handle; + filedata = &FileDataPtr[fileindex]; + + if (!doshandle) { + + /* + ** Resident file seek is merely an adjustment to the position value. + */ + switch (starting) { + + /* + ** Manually position from start of file. + */ + case SEEK_SET: + offset = MAX((long)0, offset); // Only positive offsets allowed. + FileHandleTable[handle].Pos = NULL; + break; + + /* + ** Position is relative to end of file. + */ + case SEEK_END: + offset = MIN((long)0, offset); // Only negative offsets allowed. + FileHandleTable[handle].Pos = filedata->Size; + break; + + case SEEK_CUR: + break; + + default: + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + break; + } + + /* + ** Perform the resident file position adjustment. + */ + FileHandleTable[handle].Pos += offset; + + FileHandleTable[handle].Pos = MIN(FileHandleTable[handle].Pos, filedata->Size-1); + FileHandleTable[handle].Pos = MAX(FileHandleTable[handle].Pos, (long)0); + + } else { + + /* + ** Special handling for packed file seeks. + */ + if (fileindex != ERROR && (filedata->Flag & FILEF_PACKED)) { + switch (starting) { + case SEEK_SET: + FileHandleTable[handle].Pos = offset; + break; + + case SEEK_END: + // Only negative offsets allowed. + // Do not allow seeking past the beginning. + offset = MIN(0L, offset); + if (-offset > filedata->Size) { + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + } + FileHandleTable[handle].Pos = filedata->Size + offset; + break; + + case SEEK_CUR: + FileHandleTable[handle].Pos += offset; + if (FileHandleTable[handle].Pos < 0) { + FileHandleTable[handle].Pos = 0; + } + if (FileHandleTable[handle].Pos >= filedata->Size) { + FileHandleTable[handle].Pos = filedata->Size; + } + break; + + default: + Do_IO_Error(SEEKING_BAD_OFFSET, FileHandleTable[handle].Name); + break; + } + FILESEEK(doshandle, FileHandleTable[handle].Pos+FileHandleTable[handle].Start, SEEK_SET); + CallingDOSInt--; + return (FileHandleTable[handle].Pos); + } + /* + ** Perform the straight DOS seek. + */ + FileHandleTable[handle].Pos = FILESEEK(doshandle, offset, starting); + + /* + ** File position does not recognize packed offset. + */ + if (fileindex != ERROR) { + FileHandleTable[handle].Pos -= FileHandleTable[handle].Start; + //SKB FileHandleTable[handle].Pos -= filedata->Start; + } + } + + /* + ** Return with the current file position. + */ + CallingDOSInt--; + return (FileHandleTable[handle].Pos); +} + + +/*************************************************************************** + * FILE_EXISTS -- Searches for and FINDS the specified file. * + * * + * This routine will scan the available drives and return when the file * + * is accessable. This routine is used when the programmer MUST be * + * sure of a file's existance before proceeding. This process is * + * automatically performed on a Open_File with READ access. * + * * + * INPUT: file_name -- Name of the file to check for. * + * * + * OUTPUT: Returns the disk number that the file exits on (A=1, B=2, etc) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1991 JLB : Created. * + *=========================================================================*/ +BOOL cdecl File_Exists(char const *file_name) +{ + /* + ** If the filename is invalid then it errors out. + */ + if (!file_name) return(FALSE); + /* + ** Continually search for the file (prompting as needed). Only return + ** upon success or error function control reasons. + */ + while (!Find_File(file_name)) { + + /* + ** If the file could not be found then request for proper disk. + */ + do { + //struct find_t ffblk; + //char path[MAXPATH]; + + if (!Do_Open_Error(COULD_NOT_OPEN, file_name)) { + return(FALSE); + } + ibm_setdisk(*DataPath - 'A'); + + // Search for the volume ID so that the disk information get read in again. + //_dos_findfirst("*.*", _A_VOLID, &ffblk); + } while (CHANGEDIR( DataPath )); + } + return (TRUE); +} + + +/*************************************************************************** + * FILE_SIZE -- Determine the size of the specified file. * + * * + * This routine will return with the size of the file specified by the * + * file handle passed in. * + * * + * INPUT: handle -- Westwood file handle. * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/22/1991 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl File_Size(int handle) +{ + FileDataType *filedata; // Pointer to the current FileData. + + + if (FileHandleTable[handle].Index != ERROR) { + filedata = &FileDataPtr[FileHandleTable[handle].Index]; + + if (filedata->Size) { + return(filedata->Size); + } + } + return (filelength(FileHandleTable[handle].Handle)); +} + + +/*************************************************************************** + * IS_HANDLE_VALID -- Determines validity of the specified file handle. * + * * + * Use this routine to determine if a file handle is valid or not. It * + * checks to see if it falls within legal limits and does in fact * + * reference an open file. This routine will call the error handler * + * with the specified error number if the handle is not valid. * + * * + * INPUT: handle -- Handle to check for validity. * + * * + * error -- Error number to use when calling the IO_Error * + * handler. * + * * + * name -- The file name (if known). * + * * + * OUTPUT: Returns with the file table index, if one exists for this * + * file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/13/1991 JLB : Created. * + *=========================================================================*/ +BOOL cdecl Is_Handle_Valid(int handle, FileErrorType error, char const *name) +{ + /* + ** The handle must be valid or else it is an error. + */ + if (handle < 0 || handle >= TABLE_MAX) { + /* + ** ERROR: Invalid file handle. + */ + Do_IO_Error(error, name); + } + + /* + ** An unused file handle causes an error. + */ + if (FileHandleTable[handle].Empty) { + Do_IO_Error(error, name); + } + + return (TRUE); +} + + + diff --git a/WWFLAT32/FILE/FILE.H b/WWFLAT32/FILE/FILE.H new file mode 100644 index 0000000..eded220 --- /dev/null +++ b/WWFLAT32/FILE/FILE.H @@ -0,0 +1,261 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + + +#ifndef ERROR +#define ERROR -1 +#endif + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern short Find_First(unsigned char *fname, unsigned short mode, struct find_t *ffblk); +extern short Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif // FILE_H + diff --git a/WWFLAT32/FILE/FILECACH.CPP b/WWFLAT32/FILE/FILECACH.CPP new file mode 100644 index 0000000..b584c85 --- /dev/null +++ b/WWFLAT32/FILE/FILECACH.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - File Caching routines * + * * + * File Name : FILECACH.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 18, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Make_File_Resident -- Makes a file resident even if not flaged so. * + * Flush_Unused_File_Cache -- Flushes the file cache of any non opened fi* + * Free_Resident_File -- Free the given file if it is resident. * + * Unfragment_File_Cache -- Does a garbage collection on the file heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * UNFRAGMENT_FILE_CACHE -- Does a garbage collection on the file heap. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: Can be a lengthy process. * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +VOID Unfragment_File_Cache(VOID) +{ + FileDataType *filedata; + FileDataType *parent; + UWORD idx; + + + // Let the memory system clean up the file heap. + Mem_Cleanup(FileCacheHeap); + + // Now get our pointers back. + // Start after the parent PAK files since we will need to check our pointers + // with them. + filedata = &FileDataPtr[NumPAKFiles]; + for (idx = NumPAKFiles; idx < NumPAKFiles; idx++, filedata++) { + while (filedata->Name) { + + // Only process files that are in the file cache. + if (filedata->Ptr) { + + // Is a inner PAK file? + if (filedata->Flag & FILEF_PACKED) { + + parent = &FileDataPtr[filedata->Disk]; + + // Is it just a copied pointer of the parent? + if (parent->Ptr == filedata->Ptr) { + filedata->Ptr = Mem_Find(FileCacheHeap, filedata->Disk); + } + else + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } + else { + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } + } + + // Now that the children have been taken care of, let us do the parents. + for (filedata = FileDataPtr, idx = 0; idx < NumPAKFiles; idx++, filedata++) { + + // Only process files that are in the file cache. + if (filedata->Ptr) { + filedata->Ptr = Mem_Find(FileCacheHeap, idx); + } + } +} + +/*************************************************************************** + * MAKE_FILE_RESIDENT -- Makes a file resident even if not flaged so. * + * * + * INPUT: BYTE *filename - name of file to be made resident. * + * * + * OUTPUT: BOOL if successful. could fail in not enouph RAM or not found. * + * * + * WARNINGS: File must be in FileData table. * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL Make_File_Resident(BYTE const *filename) +{ + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + WORD fileindex; + WORD oldflag; + WORD handle; + + fileindex = Find_File_Index(filename); + + // if the file is not in the table, we can't make it resident + if (fileindex == ERROR) return(FALSE); + + // Get a pointer for quicker pointer action. + filedata = &FileDataPtr[fileindex]; + + // Change the flags for a moment. + oldflag = filedata->Flag; + filedata->Flag |= FILEF_RESIDENT; + filedata->Flag &= ~FILEF_FLUSH; + + // Make the file resident. + handle = Open_File(filename, READ); + Close_File(handle); + + // Set flags back to normal. + filedata->Flag = oldflag; + + return(TRUE); +} + +/*************************************************************************** + * Flush_Unused_File_Cache -- Flushes the file cache of any non opened files. * + * * + * INPUT: WORD flush_keep - TRUE to flush even files marked FILEF_KEEP.* + * * + * OUTPUT: WORD Number of file flushed. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1993 SB : Created. * + *=========================================================================*/ +WORD Flush_Unused_File_Cache(WORD flush_keeps) +{ + WORD index; + WORD freed = 0; + FileDataType *filedata = NULL; + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + // Loop throuph the file table looking for files that could be freed. + index = 0; + filedata = &FileDataPtr[index];; + while (filedata->Name && strlen(filedata->Name)) { + + if (filedata->Ptr && !filedata->OpenCount && + (flush_keeps || !(filedata->Flag & FILEF_KEEP)) ) { + + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + freed++; + } + index++; + filedata = &FileDataPtr[index];; + } + return (freed); +} + +/*************************************************************************** + * FREE_RESIDENT_FILE -- Free the given file if it is resident. * + * * + * INPUT: BYTE *file to free * + * * + * OUTPUT: TRUE if file was free'd, FALSE otherwise * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1992 CY : Created. * + *=========================================================================*/ +BOOL cdecl Free_Resident_File(BYTE const *file) +{ + WORD fileindex; + BOOL oldflag; // Previous file flag. + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + + // if the file is not in the table, we can't free it + if ((fileindex = Find_File_Index(file)) == ERROR) { + return(FALSE); + } + + // get a pointer for quicker calculations. + filedata = &FileDataPtr[fileindex]; + + // If it isn't resident, don't try to Free it + if (filedata->Ptr == NULL) { + return(TRUE); + } + + // Change the flags for a moment. + oldflag = filedata->Flag; + filedata->Flag &= ~(FILEF_RESIDENT|FILEF_KEEP); + filedata->Flag |= FILEF_FLUSH; + + // Get the file out of Memory if it was there. + Close_File(Open_File(file, READ)); + + // Set flags back to original. + filedata->Flag = oldflag; + + return(TRUE); +} + diff --git a/WWFLAT32/FILE/FILECHNG.CPP b/WWFLAT32/FILE/FILECHNG.CPP new file mode 100644 index 0000000..e36a0f1 --- /dev/null +++ b/WWFLAT32/FILE/FILECHNG.CPP @@ -0,0 +1,154 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - File functions. * + * * + * File Name : FILECHNG.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Delete_File -- Deletes the file from the disk. * + * Create_File -- Creates an empty file on disk. * + * Change_File_Size -- Change the size of a writting file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * CREATE_FILE -- Creates an empty file on disk. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1992 JLB : Created. * + *=========================================================================*/ +WORD cdecl Create_File(BYTE const *file_name) +{ + WORD fd; + + if (!file_name) return(FALSE); + + fd = Open_File(file_name, WRITE); + if (fd != ERROR) { + Close_File(fd); + return(TRUE); + } + return(FALSE); +} + + +/*************************************************************************** + * DELETE_FILE -- Deletes the file from the disk. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1992 JLB : Created. * + *=========================================================================*/ +WORD cdecl Delete_File(BYTE const *file_name) +{ + WORD index; + FileDataType *filedata; // Pointer to the current FileData. + FileDataType hold; // Hold buffer for record (DO NOT ACCESS DIRECTLY) + + if (!file_name) return(FALSE); + + CallingDOSInt++; + + ibm_setdisk(*StartPath - 'A'); + + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + if (index != ERROR && filedata->Ptr) { + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } + + index = !FILEDELETE(file_name); + CallingDOSInt--; + return(index); +} + + + +/*************************************************************************** + * CHANGE_FILE_SIZE -- Change the size of a writting file. * + * * + * INPUT: WORD handle - handle of file. * + * ULONG new_size - size of new handle. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Change_File_Size(WORD handle, ULONG new_size) +{ + WORD entry; + + if (Is_Handle_Valid(handle, WRITING_NON_HANDLE, NULL)) { + entry = Get_DOS_Handle(handle); + if (entry != ERROR) { + return(chsize(entry, new_size) != ERROR); + } + } + return(FALSE); +} diff --git a/WWFLAT32/FILE/FILEDATA.CPP b/WWFLAT32/FILE/FILEDATA.CPP new file mode 100644 index 0000000..789a923 --- /dev/null +++ b/WWFLAT32/FILE/FILEDATA.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : File IO System LIbrary * + * * + * File Name : FILEDATA.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/* + structure for FileDataType is: + + BYTE *filename: initialize to actual file name on disk. + LONG size: initialize to actual file size on disk. + BYTE *ptr: initialize to a 0L below. + WORD disk: which floppy disk number (1+) file resides on. + LONG pos: initialize to a 0L below. + UBYTE priority: file priorities can be from 0 to 127. (127 = highest) + if you want the file to be attempted to be made + resident at runtime, add 128 to the file priority + to set the high bit. even though the files + priority will appear to be 128 to 255, it will + still remain 0 to 127. +*/ + +FileDataType FileData[] = { + { "", 0L, 0L, 0, 0L, 0 } + /* Must have an empty entry!!! */ +}; diff --git a/WWFLAT32/FILE/FILEGLOB.CPP b/WWFLAT32/FILE/FILEGLOB.CPP new file mode 100644 index 0000000..49ed933 --- /dev/null +++ b/WWFLAT32/FILE/FILEGLOB.CPP @@ -0,0 +1,74 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library * + * * + * File Name : FILEGLOB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include +#endif + +#ifndef FILE_H +#include "_file.h" +#endif + +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/* Global varaiables */ +BYTE ExecPath[XMAXPATH + 1]; +BYTE DataPath[XMAXPATH + 1]; +BYTE StartPath[XMAXPATH + 1]; +BOOL UseCD; + +/* File System only Global varaiables */ +BYTE CallingDOSInt; // Indicate we are performing a DOS function +BYTE MaxDevice,DefaultDrive; +BYTE MultiDriveSearch = TRUE; // Multiple drive search flag +FileDataType *FileDataPtr = NULL; +FileHandleType FileHandleTable[TABLE_MAX]; +UWORD NumFiles; // Number of files, except PAK, in file table. +UWORD NumPAKFiles; // Number of PAK files in filetable. +VOID *FileCacheHeap = NULL; // Pointer to the cache in memory. +WORD DiskNumber; // Where file was found (-1 == current directory). +WORD MaxDirNum = 0; + + +WORD (*Open_Error)(FileErrorType, BYTE const *) = NULL; diff --git a/WWFLAT32/FILE/FILEINFO.CPP b/WWFLAT32/FILE/FILEINFO.CPP new file mode 100644 index 0000000..fc39d04 --- /dev/null +++ b/WWFLAT32/FILE/FILEINFO.CPP @@ -0,0 +1,276 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Fileio information functions. * + * * + * File Name : FILE.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_DOS_Handle -- Fetches system specific DOS file handle. * + * Find_Disk_Number -- Determine disk a file resides upon. * + * Set_File_Flags -- Sets flags for file if FileData table. * + * Get_File_Flags -- Gets the flags on a file in the FileData table. * + * Free_Handles -- Returns number of free file handles in WW system. * + * Multi_Drive_Search -- Turns Multi search drive on and off. * + * Clear_File_Flags -- Clears flags specified for file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * GET_DOS_HANDLE -- Fetches system specific DOS file handle. * + * * + * This routine will return with the system specific DOS file handle. * + * On the IBM, this is a WORD, on the Amiga, it is a LONG (BPTR). Use * + * this routine with caution, because the value returned is NOT * + * portable. * + * * + * INPUT: fh -- Westwood file system handle. * + * * + * OUTPUT: Returns with the system DOS file handle (WORD or LONG). * + * * + * WARNINGS: If you pass in an invalid file handle, or a file handle * + * that references a resident file, then the ERROR code is * + * returned. Be SURE to check for this. * + * * + * HISTORY: * + * 08/21/1991 JLB : Created. * + * 11/09/1991 JLB : Checks for illegal file handle passed in. * + *=========================================================================*/ +WORD cdecl Get_DOS_Handle(WORD fh) +{ + /* + ** If an illegal file handle is passed in then always abort. + */ + if (fh >= 0 && fh < TABLE_MAX) { + if (!FileHandleTable[fh].Empty || FileHandleTable[fh].Handle) { + return(FileHandleTable[fh].Handle); + } + + /* + ** If it falls through here, then the file must be resident. It is + ** illegal to get a DOS handle to a resident file. + */ + } + return(FILEOPENERROR); +} + +/*************************************************************************** + * FREE_HANDLES -- Returns number of free file handles in WW system. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Free_Handles(VOID) +{ + WORD count; // Count of the number of free file handles. + WORD index; // Working file handle index var. + + count = 0; + for (index = 0; index < TABLE_MAX; index++) { + if (FileHandleTable[index].Empty) count++; + } + return(count); +} + + + +/*************************************************************************** + * FIND_DISK_NUMBER -- Determine disk a file resides upon. * + * * + * This routine will determine the disk number that the specified * + * file resides upon. It determines this by scanning through the * + * FileData table. If the specified file is a packed file, then it * + * will reference the parent packed file to determine the disk number. * + * * + * INPUT: file_name -- Pointer to the file name to check. * + * * + * OUTPUT: Returns with the disk number that the file resides upon. If * + * ERROR is returned, then the file does not exist in the * + * FileTable. The number returned is 0=A, 1=B, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/22/1991 JLB : Created. * + *=========================================================================*/ +WORD cdecl Find_Disk_Number(BYTE const *file_name) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(file_name); + + if (index != ERROR) { + + filedata = &FileDataPtr[index]; + + if (filedata->Flag & FILEF_PACKED) { + return (Find_Disk_Number(FileDataPtr[filedata->Disk].Name)); + } + return(filedata->Disk); + } + return (index); +} + + + + + +/*************************************************************************** + * SET_FILE_FLAGS -- Sets flags for file if FileData table. * + * * + * INPUT: BYTE *filename - file to modify. * + * WORD flags - flags to set in file. * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/04/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Set_File_Flags(BYTE const *filename, WORD flags) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + filedata->Flag |= flags; + return (TRUE); + } + + return (FALSE); +} + + +/*************************************************************************** + * CLEAR_FILE_FLAGS -- Clears flags specified for file. * + * * + * INPUT: BYTE *filename - file to modify. * + * WORD flags - flags to set in file. * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +WORD cdecl Clear_File_Flags(BYTE const *filename, WORD flags) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + filedata->Flag &= ~flags; + return (TRUE); + } + + return (FALSE); +} + + + +/*************************************************************************** + * GET_FILE_FLAGS -- Gets the flags on a file in the FileData table. * + * * + * * + * INPUT: BYTE *filename - file to modify. * + * * + * OUTPUT: * + * * + * OUTPUT: WORD - if file found in FileData table. * + * * + * HISTORY: * + * 10/04/1993 SKB : Created. * + *=========================================================================*/ +WORD cdecl Get_File_Flags(BYTE const *filename) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD index; // FileTable index. + + index = Find_File_Index(filename); + + if (index != ERROR) { + filedata = &FileDataPtr[index]; + return (filedata->Flag); + } + return (FALSE); +} + + +/*************************************************************************** + * MULTI_DRIVE_SEARCH -- Turns Multi search drive on and off. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Multi_Drive_Search(BOOL on) +{ + BOOL old; + + Hard_Error_Occured = 0; + old = MultiDriveSearch; + MultiDriveSearch = on; + return(old); +} diff --git a/WWFLAT32/FILE/FILEINIT.CPP b/WWFLAT32/FILE/FILEINIT.CPP new file mode 100644 index 0000000..3eee800 --- /dev/null +++ b/WWFLAT32/FILE/FILEINIT.CPP @@ -0,0 +1,511 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Fileio init routines. * + * * + * File Name : FILEINIT.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * WWDOS_Init -- Initialize the fileio WWS fileio system. * + * WWDOS_Shutdown -- Clean up any things that needs to be to exit game. * + * Init_FileData_Table -- Initializes or reads in FileData Table. * + * Sort_FileData_Table -- Sorts the FileData table that is in memory. * + * Preload_Files -- Loads files marked with FILEF_PRELOAD into cache. * + * Init_File_Cache -- Initializes and allocs the file cache heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + +#ifndef MISC_H +#include +#endif + +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize); +PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename); +PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ); +PRIVATE FileInitErrorType cdecl Preload_Files(VOID); +PRIVATE int QSort_Comp_Func(const void *p1, const void *p2); +PRIVATE VOID Sort_FileData_Table(VOID); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * WWDOS_INIT -- Initialize the fileio WWS fileio system. * + * * + * * + * INPUT: ULONG cachesize - size wanted for the cache. * + * BYTE *filedat - NULL or name of filedata table file. * + * BYTE *cdpath - NULL or secondary search path on a CD. * + * * + * OUTPUT: Returns all errors encountered or'd together. * + * * + * WARNINGS: User should call the WWDOS_Init function for all file * + * initialization. * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +FileInitErrorType cdecl WWDOS_Init(ULONG cachesize, BYTE *filedata, BYTE *cdpath) +{ +// FileInitErrorType errors; + unsigned errors ; + + // This has not been completed yet, when it is, uncomment it and add errors. + Install_Hard_Error_Handler () ; + Get_Devices(); + + if (cachesize) { + errors = Init_File_Cache(cachesize); + } else { + errors = FI_SUCCESS; + } + + + errors = errors | Init_FileData_Table(filedata); + + errors = errors | Set_Search_Drives(cdpath); + + errors = errors | Preload_Files(); + + + return ( FileInitErrorType ) errors ; +} + +/*************************************************************************** + * WWDOS_SHUTDOWN -- Clean up any things that needs to be in file syste to * + * exit game. * + * One could shut down the file system and open it back * + * up with a different size cache or filetable. * + * * + * INPUT: NONE. * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl WWDOS_Shutdown(VOID) +{ + FileDataType *filedata; // Pointer to the current FileData. + WORD file_handle; + FileHandleType *filehandletable; // Pointer to the current file handle. + + // Close all open files. + filehandletable = FileHandleTable; + for (file_handle = 0; file_handle < TABLE_MAX; file_handle++, filehandletable++) { + if (!filehandletable->Empty) { + Close_File(file_handle); + } + } + + // Free the file cache heap. + if (FileCacheHeap) { + + // Get a pointer to the current filedata. + if (FileDataPtr) { + filedata = FileDataPtr; + } else { + filedata = FileData; + } + + while(filedata->Name && filedata->Name[0]) { + filedata->Ptr = NULL; + filedata++; + } + + Free(FileCacheHeap); + FileCacheHeap = NULL; + } + + // Free up the file data. + if (FileDataPtr != FileData) { + Free(FileDataPtr); + } + FileDataPtr = NULL; + + chdir(StartPath); + ibm_setdisk(*StartPath - 'A'); + + // This has not been completed yet, when it is, uncomment it and add errors. + Remove_Hard_Error_Handler(); +} + + +/*************************************************************************** + * INIT_FILE_CACHE -- Initializes and allocs the file cache heap. * + * * + * INPUT: ULONG cachesize - size of heap cache.. * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize) +{ + // Allocate and initialize the file cache heap. + if (FileCacheHeap) { + return (FI_CACHE_ALREADY_INIT); + } + + if ((Ram_Free(MEM_NORMAL) >= cachesize)) { + FileCacheHeap = Alloc(cachesize, MEM_NORMAL); + Mem_Init(FileCacheHeap, cachesize); + } + + if (!FileCacheHeap) { + return (FI_CACHE_TOO_BIG); + } + + return (FI_SUCCESS); +} + + + + +/*************************************************************************** + * INIT_FILEDATA_TABLE -- Initializes or reads in FileData Table. * + * * + * INPUT: * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename) +{ + WORD fd; + ULONG fsize; + FileDataType *ptr; + WORD index; + BYTE fname[13]; + + /* + ** Inialize the file handle table to reflect no files open. + */ + for (index = 0; index < TABLE_MAX; index++) { + FileHandleTable[index].Empty = TRUE; + } + + // Set up our FileData ptr to be the initial FileData table. + FileDataPtr = FileData; + + // Sort the filedata table. + // This needs to be done even if we load it off disk since the initial file data + // table might contain a filename. + Sort_FileData_Table(); + + // If there is a file name, then the filedata table will be loaded from disk. + if (filename) { + if (!Find_File(filename)) { + return (FI_FILEDATA_FILE_NOT_FOUND); + } + fd = Open_File(filename, READ); + + fsize = File_Size(fd); + + if ((Ram_Free(MEM_NORMAL) < fsize)) { + Close_File(fd); + return (FI_FILEDATA_TOO_BIG); + } + + // Allocate some system memory. + // Setup the new FileDataPtr and this time. + FileDataPtr = ptr = (FileDataType *) Alloc(fsize, MEM_NORMAL); + + // Load the file up into memory. + Read_File(fd, FileDataPtr, fsize); + Close_File(fd); + + // Process the filetable. The filenames need their pointers adjusted. + // At this time we will also count the number of files and number of PAK files. + NumPAKFiles = NumFiles = 0; + + // Make sure that the file name will have a NUL at the end. + fname[12] = 0; + while(TRUE) { + // Have we reached the end of the list? + if (!ptr->Name) break; + + // Adjust the name pointer to point the the correct area. + ptr->Name = (BYTE *)FileDataPtr + (LONG) ptr->Name; + + // Count up weather it is a PAK file or a normal file. + if (!NumFiles && strstr((char *) ptr->Name, (char *) ".PAK")) { + NumPAKFiles++; + + // Mark that it has been processed so that Open_File() will not do it. + ptr->Flag |= FILEF_PROCESSED; + + } else { + NumFiles++; + } + + // Next record. + ptr++; + } + } + + return (FI_SUCCESS); +} + + + + +/*************************************************************************** + * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * + * * + * INPUT: BYTE *cdpath - path of data files on a CD. * + * Should pass in NULL for non CD products. * + * * + * OUTPUT: FileInitErrorType error code. * + * Varibable defined: * + * ExecPath = Full path of EXE file. * + * StartPath = Directory user started in. * + * DataPath = secondary search path (typically CD-ROM). * + * Note: format of paths is "C:\PATH" * + * * + * WARNINGS: The cdpath may be overiden by a "-CD" command line * + * arguement that specifies another drive (HARD or CD) and path * + * where the data resides. Whenever a file is opened, it checks * + * the startup drive first, then the CD search path if the first * + * search was unsuccessful. * + * * + * HISTORY: * + * 01/14/1993 SB : Created. * + * 04/19/1994 SKB : Mods for 32 bit library. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ) +{ + BYTE *ptr; + + +#if LIB_EXTERNS_RESOLVED + // NOTE: THIS IS WRONG, THIS IS NOT THE WAY TO GET THE EXE's PATH. + // Locate the executable. + strcpy(ExecPath, _argv[0]); + + // Find the very last '\' on the path. + ptr = strrchr((char *) ExecPath, (int) '\\'); +#else + ptr = NULL; +#endif + + // Remove the exe name to just have the path. + if (ptr == NULL) { + *ExecPath = 0; + } + else { + *ptr = 0; + } + + // Did the user specify a second path? + ptr = Find_Argv("-CD"); + + // If so, set the data path to that. + if (ptr) { + strcpy(DataPath, ptr + 3); + } + // Otherwise check to see if there is a CD-Rom drive. + else { + if (cdpath && *cdpath) { + +#if LIB_EXTERNS_RESOLVED + UseCD = GetCDDrive(); +#else + UseCD = FALSE; +#endif + } + else { + UseCD = FALSE; + } + + // If so, set the Drive to it and find out if any directories. + if ( UseCD ) { + strcpy( DataPath, "A:" ); + strcat( DataPath, cdpath); + *DataPath = 'A'+UseCD; + } + // If not, set the Data path to the execacutable path. + else { + strcpy(DataPath, ExecPath); + } + } + + // Finnally, set the starting path. + getcwd(StartPath, XMAXPATH); + + // Make sure they are all uppercase. + strupr(StartPath); + strupr(DataPath); + strupr(ExecPath); + + // Change directories to the secondary search path (DataPath). + if (*DataPath && chdir(DataPath)) { + return (FI_SEARCH_PATH_NOT_FOUND); + } + + // Lastley, Make sure we are in the startup directory. This will overide + // the secondary data path if they are on the same drive. + if (chdir(StartPath)) { + return (FI_STARTUP_PATH_NOT_FOUND); + } + + return (FI_SUCCESS); +} + + +/*************************************************************************** + * PRELOAD_FILES -- Loads files marked with FILEF_PRELOAD into cache. * + * * + * * + * INPUT: none. * + * * + * OUTPUT: FileInitErrorType error code. * + * * + * WARNINGS: The FileData must be initialized and the file heap initialized* + * in order for this to work. * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +PRIVATE FileInitErrorType cdecl Preload_Files(VOID) +{ + FileDataType *filedata; // Working file data table pointer. + BOOL oldflag; // Previous file flag. + + if (!FileDataPtr) { + return (FI_FILETABLE_NOT_INIT); + } + + if (!FileCacheHeap) { + return (FI_NO_CACHE_FOR_PRELOAD); + } + + /* + ** Make all files flagged to be made resident at startup, resident. + */ + filedata = FileDataPtr; + + while (filedata->Name && strlen(filedata->Name)) { + if (filedata->Flag & FILEF_PRELOAD) { + + oldflag = filedata->Flag; + filedata->Flag |= FILEF_RESIDENT; // Make it resident. + filedata->Flag &= ~FILEF_FLUSH; // Don't purge on Close_File. + + Close_File(Open_File(filedata->Name, READ)); + + filedata->Flag &= ~(FILEF_RESIDENT|FILEF_FLUSH); // Clear bits. + filedata->Flag |= oldflag & (FILEF_RESIDENT|FILEF_FLUSH); // Restore bits. + + } + filedata++; + } + return (FI_SUCCESS); +} + + + +/*************************************************************************** + * SORT_FILEDATA_TABLE -- Sorts the FileData table that is in memory. * + * * + * INPUT: NONE * + * * + * OUTPUT: NONE. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/13/1993 SKB : Created. * + *=========================================================================*/ + +PRIVATE int QSort_Comp_Func(const void *p1, const void *p2) +{ + return(strcmp(((FileDataType*)p1)->Name, ((FileDataType*)p2)->Name)); +} +PRIVATE VOID Sort_FileData_Table(VOID) +{ + /* + ** Sort the filetable it but keep the pack file indexes correct. + */ + + /* + ** The number of pak files in the file table. + */ + NumPAKFiles = 0; + strupr(FileData[NumPAKFiles].Name); + while (strstr((char *) FileData[NumPAKFiles].Name, (char *) ".PAK")) { + strupr(FileData[NumPAKFiles].Name); + NumPAKFiles++; + } + + /* + ** Count the remaining files within the file table. + */ + NumFiles = 0; + while(FileData[NumFiles+NumPAKFiles].Name && FileData[NumFiles+NumPAKFiles].Name[0]) { + strupr(FileData[NumFiles+NumPAKFiles].Name); + NumFiles++; + } + + /* + ** Sort the file entries (past the pak files). + */ + if (NumFiles) { + qsort(&FileData[NumPAKFiles], NumFiles, sizeof(FileDataType), QSort_Comp_Func); + } +} + diff --git a/WWFLAT32/FILE/FILEIO.CPP b/WWFLAT32/FILE/FILEIO.CPP new file mode 100644 index 0000000..c1f4fd9 --- /dev/null +++ b/WWFLAT32/FILE/FILEIO.CPP @@ -0,0 +1,151 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : FILEIO.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 21, 1991 * + * * + * Last Update : September 13, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +WORD ibm_getdisk(VOID) +{ + unsigned disk; + + CallingDOSInt++; + // disk = getdisk(); + _dos_getdrive ( & disk ) ; + CallingDOSInt--; + return(disk-1); +} + +WORD ibm_setdisk(WORD drive) +{ +// WORD disk; + unsigned disk ; + + CallingDOSInt++; +// disk = setdisk(drive); + _dos_setdrive ( drive+1 , & disk ) ; + CallingDOSInt--; + return(disk); +} + +WORD ibm_close(WORD handle) +{ + WORD success; + + CallingDOSInt++; + success = close(handle); + CallingDOSInt--; + return(success); +} + +WORD ibm_unlink(BYTE const *name) +{ + WORD success; + + CallingDOSInt++; + success = unlink(name); + CallingDOSInt--; + return(success); +} + +LONG ibm_lseek(WORD handle, LONG offset, WORD where) +{ + LONG new_offset; + + CallingDOSInt++; + new_offset = lseek(handle, offset, where); + CallingDOSInt--; + return(new_offset); +} + +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes) +{ + UWORD bytes_read; + + CallingDOSInt++; + bytes_read = read(handle, ptr, bytes); + CallingDOSInt--; + return(bytes_read); +} + +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes) +{ + UWORD bytes_written; + + CallingDOSInt++; + bytes_written = write(handle, ptr, bytes); + CallingDOSInt--; + return(bytes_written); +} + +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib) +{ + WORD handle; + + CallingDOSInt++; + handle = open(name, mode, attrib); + CallingDOSInt--; + return(handle); +} + +WORD ibm_chdir(BYTE const *path) +{ + WORD retval; + + CallingDOSInt++; + retval = chdir(path); + CallingDOSInt--; + return(retval); +} + + + + diff --git a/WWFLAT32/FILE/FILELIB.CPP b/WWFLAT32/FILE/FILELIB.CPP new file mode 100644 index 0000000..3a9de39 --- /dev/null +++ b/WWFLAT32/FILE/FILELIB.CPP @@ -0,0 +1,388 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : FILEIO Library support routines. * + * * + * File Name : FILELIB.C * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 11, 1994 * + * * + * Last Update : April 11, 1994 [SKB] * + * * + * * + * * + *-------------------------------------------------------------------------* + * Notes: This file contains private functions to the fileio system. * + * While these functions may be used by any module in the fileio * + * system, they cannot be used by a user program. For this reason * + * they are put into this module. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Cache_File -- Attempts to cache file in XMS if flags set. * + * Do_IO_Error -- Performs a non-recoverable error message display. * + * Do_Open_Error -- Does an error message that could return. * + * Is_Handle_Valid -- Determines validity of the specified file handle. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#ifndef WWMEM_H +#include +#endif + + +#include +#include +#include +#include + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * DO_ERROR -- Does an error message that could return. * + * * + * This routine displays a file error message and unless the player * + * presses , it will return. If the player presses , then * + * it will terminate the program. * + * * + * INPUT: error -- Error message number. * + * * + * filename -- File name that the error occured on. * + * * + * OUTPUT: TRUE/FALSE; Should the process be repeated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + *=========================================================================*/ +#pragma argsused +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name) +{ + BYTE *ptr=NULL; // Working file name pointer (just name and extension). + + /* + ** Since the file name may include a path, we must extract the true + ** file name from the given string. + */ + if (file_name) { +#if LIB_EXTERNS_RESOLVED + ptr = strrchr((char *) file_name, (int) '\\'); +#else + ptr = NULL; +#endif + if (ptr) { + ptr++; + } else { + ptr = (BYTE *) file_name; + } + } + +#if LIB_EXTERNS_RESOLVED + strupr(ptr); + return (IO_Error(errormsgnum, ptr)); +#else + return(0); + +#endif + +} + + +/*************************************************************************** + * DO_IO_ERROR -- Performs a non-recoverable error message display. * + * * + * This routine will perform a non-recoverable file error message * + * display. It is called when an error is detected that has no * + * recovery or retry process defined. * + * * + * INPUT: errornum -- Error number detected. * + * * + * filename -- Name of the file that caused the error. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + *=========================================================================*/ +#pragma argsused +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename) +{ +#if LIB_EXTERNS_RESOLVED + (VOID)IO_Error(errormsgnum, filename); +#endif + #if(TRUE) + Prog_End(); + exit((int)errormsgnum); + #else + Program_End(); + #endif +} + + +/*************************************************************************** + * Read_File_With_Recovery -- read the same file on another directory if an error * + * occurs. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/16/1993 QY : Created. * + *=========================================================================*/ +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ) +{ + WORD newhandle; + LONG bytes_read; + + do { + Hard_Error_Occured = 0; + + // Make sure we are in the right path. + CHANGEDIR( DataPath ); + + // open the same file + newhandle = Open_File( FileHandleTable[ handle ].Name, FileHandleTable[ handle ].Mode ); + Seek_File( newhandle, FileHandleTable[ handle ].Pos, SEEK_SET ); + + // dos close the old file + FILECLOSE( FileHandleTable[ handle ].Handle ); + + // copy FileHandleTable[ newhandle ] to FileHandleTable[ handle ] + Mem_Copy( &FileHandleTable[ newhandle ], &FileHandleTable[ handle ], + ( ULONG ) sizeof( FileHandleTable[ newhandle ] ) ); + + // delete FileHandleTable[newhandle] + + FileHandleTable[ newhandle ].Empty = TRUE; + + // continue reading file + bytes_read = ( LONG ) FILEREAD( FileHandleTable[ handle ].Handle, buf, bytes ); + + // if still error, do it again; else return the number of bytes read + if ( !Hard_Error_Occured ) { + return( bytes_read ); + } + if (!Do_Open_Error(COULD_NOT_OPEN, FileHandleTable[ handle ].Name)) { + return(FALSE); + } + + } while (CHANGEDIR( DataPath )); + + return (NULL); +} + +/*************************************************************************** + * Open_File_With_Recovery -- open the same file on another directory * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/16/1993 QY : Created. * + *=========================================================================*/ +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ) +{ + WORD handle; + + Hard_Error_Occured = FALSE; + handle = FILEOPEN(file_name, mode); + + // Do not return if there was a HardError and Using a CD and we are looking at + // the CD. + if (!Hard_Error_Occured || !UseCD || (ibm_getdisk() != (*DataPath - 'A'))) { + return (handle); + } + +#if DEBUGPRINT + Mono_Print(file_name); Mono_Print(":OPENERROR "); +#endif + + Hard_Error_Occured = 0; + + // It is possible that the CD has been poped out and put back in, let us + // change there and then try again. + ibm_setdisk(*DataPath - 'A'); + CHANGEDIR( DataPath ); + + // open the same file + handle = FILEOPEN( file_name, mode ); + + // if still error, do it again; else return the dos handle + if ( !Hard_Error_Occured ) { + return( handle ); + } + + Hard_Error_Occured = 0; + return (FILEOPENERROR); +} + + +/*************************************************************************** + * CACHE_FILE -- Attempts to cache file in XMS if flags set. * + * * + * * + * INPUT: WORD index - the index of the file in the FileData table. * + * WORD file_handle - WWS file handle of file. * + * * + * OUTPUT: BOOL : was it cached? * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1993 SKB : Created. * + *=========================================================================*/ +BOOL cdecl Cache_File(WORD index, WORD file_handle) +{ + LONG filesize; // Size of the memory block needed. + LONG freecache; // Amount of free XMS. + FileDataType *filedata = NULL; + FileDataType hold; + FileHandleType *filehandletable; + WORD flag; // Type of system memory to cache file. + WORD file; + + // Only files in the file table can be cached. + if (index == ERROR) { + return FALSE; + } + + // Setup our pointer to the file we may want to cache. + filedata = &FileDataPtr[index]; + + // Should this be cached, and is it not yet cached? + if ((filedata->Flag & (FILEF_RESIDENT|FILEF_PRELOAD)) && !filedata->Ptr) { + + filesize = filedata->Size; + + /* + ** If there is o room to cache the file, then turn off its cache file. + */ + if (filesize > Mem_Pool_Size(FileCacheHeap)) { + + // Remove resident flags so that it will not keep trying to cache itself + // since there will never be enough room for it. + filedata->Flag &= ~(FILEF_PRELOAD|FILEF_KEEP|FILEF_RESIDENT|FILEF_FLUSH); + + return FALSE; + } + + + // Go through freeing files until there is enouph space in the + // memory pool. + while (filesize > Mem_Avail(FileCacheHeap)) { + VOID *node; + + // Get the oldest non used file pointer. + node = Mem_Find_Oldest(FileCacheHeap); + + // If non was found, sorry no room for the new file. + if (!node) { + return (FALSE); + } + + // Get a pointer to the structure for convenience. + filedata = &FileDataPtr[Mem_Get_ID(node)]; + + // Free it from the heap and update the file system so it knows that + // the file is no longer in memory. + Mem_Free(FileCacheHeap, filedata->Ptr); + filedata->Ptr = NULL; + } + + + // If there is not a big enough space we will have to take garbage + // collection hit. (OUCH!!!!!!) + if (filesize > Mem_Largest_Avail(FileCacheHeap)) { + Unfragment_File_Cache(); + } + + // Make sure we have a big enough space and if so, put the file into memory. + if (filesize < Mem_Largest_Avail(FileCacheHeap)) { + + // Get some pointers to save code space and time. + filehandletable = &FileHandleTable[file_handle]; + filedata = &FileDataPtr[index]; + + // Alloc the buffer in our file cache, then read the file in. + filedata->Ptr = Mem_Alloc(FileCacheHeap, filesize, index); + + // Extra check - it should not fail. + if (!filedata->Ptr) return(FALSE); + + // Mark it so that it never comes back as Oldest used. + Mem_In_Use(filedata->Ptr); + + // Get the file into memory. + Read_File(file_handle, filedata->Ptr, filesize); + + // reset the read index from the above read. + filehandletable->Pos = 0L; + + // This makes caching inner pak file possible. No longer is the + // PAK'd file based off the parent file. + filehandletable->Start = 0; + + // Close the parent file. Remove it's open count. + if (filedata->Flag & FILEF_PACKED) { + FileDataType p_hold; + FileDataType *parent; + + parent = &FileDataPtr[filedata->Disk]; + parent->OpenCount--; + } + FILECLOSE(filehandletable->Handle); + filehandletable->Handle = 0; + + return (TRUE); + } + } + + // The file was not cached, let the caller know. + return (FALSE); +} diff --git a/WWFLAT32/FILE/FILESTUB.CPP b/WWFLAT32/FILE/FILESTUB.CPP new file mode 100644 index 0000000..f6d0438 --- /dev/null +++ b/WWFLAT32/FILE/FILESTUB.CPP @@ -0,0 +1,41 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwlib32 * + * * + * File Name : FILESTUB.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : May 3, 1994 [BR] * + * * + * This module is a temorary stub that contains IO_Error. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "wwstd.h" +#include "file.h" + +WORD Text_IO_Error(FileErrorType, BYTE const *){return FALSE;} +WORD (*IO_Error)(FileErrorType, BYTE const *) = Text_IO_Error; + +/************************* End of filestub.cpp *****************************/ diff --git a/WWFLAT32/FILE/FILETEMP.H b/WWFLAT32/FILE/FILETEMP.H new file mode 100644 index 0000000..210f85a --- /dev/null +++ b/WWFLAT32/FILE/FILETEMP.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD cdecl (*cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H diff --git a/WWFLAT32/FILE/FINDFILE.BAK b/WWFLAT32/FILE/FINDFILE.BAK new file mode 100644 index 0000000..602811e --- /dev/null +++ b/WWFLAT32/FILE/FINDFILE.BAK @@ -0,0 +1,312 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : FINDFILE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 21, 1991 * + * * + * Last Update : September 29, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_File_Index -- Finds the FileTable index number for a given file. * + * Find_File -- Checks if a file is immediatly available. * + * Get_FileData -- Gets a pointer back to the correct file. * + * Find_File -- Checks if a file is immediatly available. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/*************************************************************************** + * FIND_FILE -- Checks if a file is immediatly available. * + * * + * Use this function to determine if a file is immediatly available. * + * This routine will NOT request for the proper disk to be inserted * + * if the file could not be found. Use File_Exists for that feature. * + * The Westwood file I/O system does NOT have to be initialized as * + * a prerequisit to using this function. * + * * + * INPUT: file_name -- Name of the file to check. * + * * + * OUTPUT: Returns the disk number that the file exits on (A=1, B=2, etc) * + * * + * WARNINGS: This sets the current drive to the drive that contains the * + * specified file (if it is found). * + * * + * HISTORY: * + * 11/14/1991 JLB : Created. * + * 03/14/1992 JLB : Modified for Amiga compatability. * + * 01/11/1993 SKB : Modified for CD-ROM searches. * + *=========================================================================*/ +int cdecl Find_File(char const *file_name) +{ + FileDataType *filedata = NULL; + WORD index; // File index (if any). + WORD disk; // Disk number of file (if in filetable). + + /* + ** If the filename is invalid then it errors out as if the file wasn't + ** found (naturally). + */ + if (!file_name) return(FALSE); + + /* + ** Determine if the file has a file table entry. If it does, then + ** special checks and processing must occur. + ** Also, if it is in memory, return with it. + */ + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + if (index != ERROR) { + + // If the file is currently cached, return TRUE that it was found. + if (filedata->Ptr) { + return (TRUE); + } + } + + + /* + ** Always check the current directory for the file. Only if it can't + ** be found are furthur measures required. + */ + DiskNumber = ERROR; // This indicates file exists in current directory. + + + #if (LIB_CDROM) + ibm_setdisk(*StartPath - 'A'); + #endif + + /* + ** Check the current directory by attempting to open with READ access. + */ + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) + { + // WORD d; + unsigned d ; + + CallingDOSInt++; + close(handle); + // d = getdisk(); + _dos_getdrive ( & d) ; + CallingDOSInt--; + return(d); + } + } + + + if (index != ERROR) { + + disk = filedata->Disk; + /* + ** If the file is in a packed file, then search for the packed file + ** instead of the specified one. + */ + if (index != ERROR && (filedata->Flag & FILEF_PACKED)) { + filedata = &FileDataPtr[disk]; + return (Find_File(filedata->Name)); + } + + } + + /* + ** It could not be found on the current drive, so search the other + ** drives if allowed to do so. + */ + if (!MultiDriveSearch) { + return(FALSE); + } + +#if (LIB_CDROM) + // If we were unable to find the file on the hard drive, change + // drives to the CD rom drive and see if it is there. + ibm_setdisk(*DataPath - 'A'); + + { + WORD handle; + + Hard_Error_Occured = 0; + + handle = Open_File_With_Recovery( file_name, MODE_OLDFILE ); + + if (handle != FILEOPENERROR) { + FILECLOSE(handle); + return(ibm_getdisk() + 1); + } + } + + ibm_setdisk(*StartPath - 'A'); + return (FALSE); +#else + + { + WORD start_drive; // Original current drive number. + + /* + ** Record the current drive for restoring later in case of failure. + */ + CallingDOSInt++; + start_drive = getdisk(); + CallingDOSInt--; + + /* + ** Sweep backward from the last real drive to the first, looking for the + ** file on each in turn. + */ + for (index = MaxDevice; index != -1; index--) { + if (Is_Device_Real(index)) { + CallingDOSInt++; + setdisk(index); + CallingDOSInt--; + + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) { + CallingDOSInt++; + close(handle); + CallingDOSInt--; + DiskNumber = index+1; + return (DiskNumber); + } + } + } + } + CallingDOSInt++; + setdisk(start_drive); + CallingDOSInt--; + } + + return(FALSE); +#endif + +} + + +/*************************************************************************** + * FIND_FILE_INDEX -- Finds the FileTable index number for a given file. * + * * + * This function searches the FileTable and returns with the index of * + * the matching file. If the file doesn't exist in the table, then * + * ERROR is returned. It does not care about case. * + * * + * INPUT: filename -- Pointer to the filename to check. * + * * + * OUTPUT: Returns with the index into the FileTable. If the file does * + * not exist in the file table, then ERROR is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + * 06/11/1993 JLB : Sorts and binary searches the file table. * + *=========================================================================*/ +PRIVATE int Comp_Func(const void *p1, const void *p2) +{ + return(strcmp((char *) ((FileDataType*)p1)->Name, (char *) ((FileDataType*)p2)->Name)); +} +WORD cdecl Find_File_Index(BYTE const *filename) +{ + FileDataType *filedata; // File entry pointer. + FileDataType key; // Working file data type var. + + /* + ** Perform a binary search on the presorted filetable. + */ + if (filename) { + + filedata = NULL; + key.Name = (BYTE *) strupr((char *)filename); + if (strstr((char *)key.Name, (char *)".PAK")) { + + /* + ** If the FileData table was not loaded from the disk then the PAK files are + ** not sorted so Perform a linear search for the pak files. + ** Otherwise the files are sorted so speed things up by doing a bsearch. + */ + if (FileData == FileDataPtr) { + filedata = (FileDataType *) lfind(&key, FileDataPtr, (size_t *) &NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + else { + filedata = (FileDataType *)bsearch(&key, FileDataPtr, NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + + } else { + + /* + ** Perform a binary search for the regular files. + */ + filedata = (FileDataType *)bsearch(&key, &FileDataPtr[NumPAKFiles], NumFiles, sizeof(FileDataType), Comp_Func); + } + + // Return the element in the array if file was found in table. + if (filedata) { + return (filedata - FileDataPtr); + //return ((WORD)((((LONG)filedata) - ((LONG)FileDataPtr)) / sizeof(FileDataType))); + } + } + return(ERROR); +} + + + + diff --git a/WWFLAT32/FILE/FINDFILE.CPP b/WWFLAT32/FILE/FINDFILE.CPP new file mode 100644 index 0000000..97d62e7 --- /dev/null +++ b/WWFLAT32/FILE/FINDFILE.CPP @@ -0,0 +1,312 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : FINDFILE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 21, 1991 * + * * + * Last Update : September 29, 1993 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_File_Index -- Finds the FileTable index number for a given file. * + * Find_File -- Checks if a file is immediatly available. * + * Get_FileData -- Gets a pointer back to the correct file. * + * Find_File -- Checks if a file is immediatly available. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef _FILE_H +#include "_file.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/*************************************************************************** + * FIND_FILE -- Checks if a file is immediatly available. * + * * + * Use this function to determine if a file is immediatly available. * + * This routine will NOT request for the proper disk to be inserted * + * if the file could not be found. Use File_Exists for that feature. * + * The Westwood file I/O system does NOT have to be initialized as * + * a prerequisit to using this function. * + * * + * INPUT: file_name -- Name of the file to check. * + * * + * OUTPUT: Returns the disk number that the file exits on (A=1, B=2, etc) * + * * + * WARNINGS: This sets the current drive to the drive that contains the * + * specified file (if it is found). * + * * + * HISTORY: * + * 11/14/1991 JLB : Created. * + * 03/14/1992 JLB : Modified for Amiga compatability. * + * 01/11/1993 SKB : Modified for CD-ROM searches. * + *=========================================================================*/ +int cdecl Find_File(char const *file_name) +{ + FileDataType *filedata = NULL; + WORD index; // File index (if any). + WORD disk; // Disk number of file (if in filetable). + + /* + ** If the filename is invalid then it errors out as if the file wasn't + ** found (naturally). + */ + if (!file_name) return(FALSE); + + /* + ** Determine if the file has a file table entry. If it does, then + ** special checks and processing must occur. + ** Also, if it is in memory, return with it. + */ + index = Find_File_Index(file_name); + filedata = &FileDataPtr[index]; + + if (index != ERROR) { + + // If the file is currently cached, return TRUE that it was found. + if (filedata->Ptr) { + return (TRUE); + } + } + + + /* + ** Always check the current directory for the file. Only if it can't + ** be found are furthur measures required. + */ + DiskNumber = ERROR; // This indicates file exists in current directory. + + + #if (LIB_CDROM) + ibm_setdisk(*StartPath - 'A'); + #endif + + /* + ** Check the current directory by attempting to open with READ access. + */ + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) + { + // WORD d; + unsigned d ; + + CallingDOSInt++; + close(handle); + // d = getdisk(); + _dos_getdrive ( & d) ; + CallingDOSInt--; + return(d); + } + } + + + if (index != ERROR) { + + disk = filedata->Disk; + /* + ** If the file is in a packed file, then search for the packed file + ** instead of the specified one. + */ + if (index != ERROR && (filedata->Flag & FILEF_PACKED)) { + filedata = &FileDataPtr[disk]; + return (Find_File(filedata->Name)); + } + + } + + /* + ** It could not be found on the current drive, so search the other + ** drives if allowed to do so. + */ + if (!MultiDriveSearch) { + return(FALSE); + } + +#if (LIB_CDROM) + // If we were unable to find the file on the hard drive, change + // drives to the CD rom drive and see if it is there. + ibm_setdisk(*DataPath - 'A'); + + { + WORD handle; + + Hard_Error_Occured = 0; + + handle = Open_File_With_Recovery( file_name, MODE_OLDFILE ); + + if (handle != FILEOPENERROR) { + FILECLOSE(handle); + return(ibm_getdisk() + 1); + } + } + + ibm_setdisk(*StartPath - 'A'); + return (FALSE); +#else + + { + WORD start_drive; // Original current drive number. + + /* + ** Record the current drive for restoring later in case of failure. + */ + CallingDOSInt++; + start_drive = getdisk(); + CallingDOSInt--; + + /* + ** Sweep backward from the last real drive to the first, looking for the + ** file on each in turn. + */ + for (index = MaxDevice; index != -1; index--) { + if (Is_Device_Real(index)) { + CallingDOSInt++; + setdisk(index); + CallingDOSInt--; + + { + WORD handle; + + CallingDOSInt++; + handle = open(file_name, O_RDONLY | O_BINARY, S_IREAD); + CallingDOSInt--; + if (handle != ERROR) { + CallingDOSInt++; + close(handle); + CallingDOSInt--; + DiskNumber = index+1; + return (DiskNumber); + } + } + } + } + CallingDOSInt++; + setdisk(start_drive); + CallingDOSInt--; + } + + return(FALSE); +#endif + +} + + +/*************************************************************************** + * FIND_FILE_INDEX -- Finds the FileTable index number for a given file. * + * * + * This function searches the FileTable and returns with the index of * + * the matching file. If the file doesn't exist in the table, then * + * ERROR is returned. It does not care about case. * + * * + * INPUT: filename -- Pointer to the filename to check. * + * * + * OUTPUT: Returns with the index into the FileTable. If the file does * + * not exist in the file table, then ERROR is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1991 JLB : Created. * + * 06/11/1993 JLB : Sorts and binary searches the file table. * + *=========================================================================*/ +PRIVATE int Comp_Func(const void *p1, const void *p2) +{ + return(strcmp((char *) ((FileDataType*)p1)->Name, (char *) ((FileDataType*)p2)->Name)); +} +int cdecl Find_File_Index(char const *filename) +{ + FileDataType *filedata; // File entry pointer. + FileDataType key; // Working file data type var. + + /* + ** Perform a binary search on the presorted filetable. + */ + if (filename) { + + filedata = NULL; + key.Name = (BYTE *) strupr((char *)filename); + if (strstr((char *)key.Name, (char *)".PAK")) { + + /* + ** If the FileData table was not loaded from the disk then the PAK files are + ** not sorted so Perform a linear search for the pak files. + ** Otherwise the files are sorted so speed things up by doing a bsearch. + */ + if (FileData == FileDataPtr) { + filedata = (FileDataType *) lfind(&key, FileDataPtr, (size_t *) &NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + else { + filedata = (FileDataType *)bsearch(&key, FileDataPtr, NumPAKFiles, sizeof(FileDataType), Comp_Func); + } + + } else { + + /* + ** Perform a binary search for the regular files. + */ + filedata = (FileDataType *)bsearch(&key, &FileDataPtr[NumPAKFiles], NumFiles, sizeof(FileDataType), Comp_Func); + } + + // Return the element in the array if file was found in table. + if (filedata) { + return (filedata - FileDataPtr); + //return ((WORD)((((LONG)filedata) - ((LONG)FileDataPtr)) / sizeof(FileDataType))); + } + } + return(ERROR); +} + + + + diff --git a/WWFLAT32/FILE/HARDERR.ASM b/WWFLAT32/FILE/HARDERR.ASM new file mode 100644 index 0000000..0029b79 --- /dev/null +++ b/WWFLAT32/FILE/HARDERR.ASM @@ -0,0 +1,216 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Hard/Critical Error Handler * +;* * +;* File Name : harderr.asm * +;* * +;* Programmer : Scott K. Bowen. * +;* * +;* Start Date : July 18, 1994 * +;* * +;* Last Update : July 26, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Hard_Error_Handler -- Setup for handling critical errors * +;* Remove_Hard_Erroror_Handler -- Remove the critical error handler stuff* +;* Critical_Error_Handler -- Catch critical error interrupt. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +;INCLUDE "tntdos.inc" + + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CRITERR_INT_NUM EQU 24h + +DISK_ERROR_BIT EQU 01000000000000000b ; bit 7 of dh. +DISK_ERROR EQU 1 ; Value of Hard_Error_Occured if disk/floppy error. +OTHER_ERROR EQU 2 ; Value of Hard_Error_Occured if other type of error. + +; +; Interrupt handler stack frame +; +_FLGS equ [DWORD PTR ebp+52] ; 386|DOS-Extender flags +_GS equ [WORD PTR ebp+48] ; original GS +_FS equ [WORD PTR ebp+44] ; original FS +_DS equ [WORD PTR ebp+40] ; original DS +_ES equ [WORD PTR ebp+36] ; original ES +_SS equ [WORD PTR ebp+32] ; original SS +_ESP equ [DWORD PTR ebp+28] ; original ESP +_EFLAGS equ [DWORD PTR ebp+24] ; original EFLAGS +_CS equ [DWORD PTR ebp+20] ; original CS +_EIP equ [DWORD PTR ebp+16] ; original EIP +_EBP equ [DWORD PTR ebp] ; original EBP + +; +; DOS critical error stack frame +; +_DOS_FLAGS equ [WORD PTR es:ebx+22] ; interrupt stack frame from real +_DOS_CS equ [WORD PTR es:ebx+20] ; mode INT 21h +_DOS_IP equ [WORD PTR es:ebx+18] ; +_DOS_ES equ [WORD PTR es:ebx+16] ; regs at time INT 21h was issued +_DOS_DS equ [WORD PTR es:ebx+14] ; in real mode +_DOS_BP equ [WORD PTR es:ebx+12] ; +_DOS_DI equ [WORD PTR es:ebx+10] ; +_DOS_SI equ [WORD PTR es:ebx+8] ; +_DOS_DX equ [WORD PTR es:ebx+6] ; +_DOS_CX equ [WORD PTR es:ebx+4] ; +_DOS_BX equ [WORD PTR es:ebx+2] ; +_DOS_AX equ [WORD PTR es:ebx] ; + + +; +; Error codes put into Hard_Error_Code +; +DISK_WRITE_PROTECTED equ 00h +UNKOWN_DEVICE equ 01h +DRIVE_NOT_READY equ 02h +UNKOWN_COMMAND equ 03h +CRC_ERROR equ 04h +WRONG_DATA_LENGTH equ 05h +SEEK_ERROR equ 06h +UNKOWN_DEVICE_TYPE equ 07h +SECTOR_NOT_FOUND equ 08h +OUT_OF_PAPER equ 09h +WRITE_ERROR equ 0Ah +READ_ERROR equ 0Bh +GENERAL_ERROR equ 0Ch + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes /////////////////////////////////////// + +GLOBAL Install_Hard_Error_Handler :NEAR +GLOBAL Remove_Hard_Error_Handler :NEAR + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////// Global/Local Data ////////////////////////////////// + +DATASEG + +LABEL LockedDataStart BYTE +Hard_Error_Occured DB 0 ; Hard disk error or other error. +Hard_Error_Code DB 0 ; Error Code. +LABEL LockedDataEnd BYTE + + +OldRMI DD ? ; original real mode critical err vector +OldPMIOffset DD ? ; original protected mode critical err vector +OldPMISelector DD ? ; original PM crit error selector. + +InitFlags DD 0 ; Flags to indicate what has been initialized. + +; InitFlags that are set to have a fully functional interrupt. +IF_SET_VECTORS equ 1 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 2 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 4 ; Locked PM data for DPMI. +IF_FUNCTIONAL equ 8 ; crit error is in and functional. + + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Code ////////////////////////////////////////// + +CODESEG + +;*************************************************************************** +;* INSTALL_HARD_ERROR_HANDLER -- Setup for handling critical errors. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + PROC Install_Hard_Error_Handler C near + USES eax,ebx,ecx,ds,es + ret + + ENDP Install_Hard_Error_Handler + + +;*************************************************************************** +;* REMOVE_HARD_ERROROR_HANDLER -- Remove the critical error handler stuff * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + + PROC Remove_Hard_Error_Handler C near + USES ebx,ecx,edx,ds,es +; +; Restore the original interrupt vectors and exit +; + + ret + + ENDP Remove_Hard_Error_Handler + + +;*************************************************************************** +;* Critical_Error_Handler -- Catch critical error interrupt. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/26/1994 SKB : Created. * +;*=========================================================================* + +LABEL LockedCodeStart BYTE + + PROC Critical_Error_Handler NEAR + + ENDP Critical_Error_Handler + +LABEL LockedCodeEnd BYTE + +END + diff --git a/WWFLAT32/FILE/MAKEFILE b/WWFLAT32/FILE/MAKEFILE new file mode 100644 index 0000000..ba681c9 --- /dev/null +++ b/WWFLAT32/FILE/MAKEFILE @@ -0,0 +1,194 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = file +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + file.obj & + filecach.obj & + filechng.obj & + filedata.obj & + fileglob.obj & + fglob2.obj & + fileinfo.obj & + fileinit.obj & + fileio.obj & + filelib.obj & + filestub.obj & + findfile.obj & + devices.obj & + devtable.obj & + ffirst.obj & + harderr.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/FILE/_FILE.BAK b/WWFLAT32/FILE/_FILE.BAK new file mode 100644 index 0000000..727c0d3 --- /dev/null +++ b/WWFLAT32/FILE/_FILE.BAK @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/WWFLAT32/FILE/_FILE.H b/WWFLAT32/FILE/_FILE.H new file mode 100644 index 0000000..a966684 --- /dev/null +++ b/WWFLAT32/FILE/_FILE.H @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +int cdecl Open_File_With_Recovery( BYTE const *file_name, unsigned int mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/WWFLAT32/FONT/FONT.BAK b/WWFLAT32/FONT/FONT.BAK new file mode 100644 index 0000000..7999c1d --- /dev/null +++ b/WWFLAT32/FONT/FONT.BAK @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef VBUFFER_H +#include +#endif + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Set_Font(VOID const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +WORD cdecl Char_Pixel_Width(BYTE chr); +UWORD cdecl String_Pixel_Width(BYTE const *string); +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y); +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Load_Font(BYTE const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID Set_Font_Palette_Range(VOID const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern BYTE FontWidth ; +extern BYTE FontHeight; +extern BYTE *FontWidthBlockPtr; + + +extern "C" VOID const *FontPtr; + + + + +#endif // FONT_H diff --git a/WWFLAT32/FONT/FONT.CPP b/WWFLAT32/FONT/FONT.CPP new file mode 100644 index 0000000..b00a4cc --- /dev/null +++ b/WWFLAT32/FONT/FONT.CPP @@ -0,0 +1,170 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : FONT.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : July 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Char_Pixel_Width -- Return pixel width of a character. * + * String_Pixel_Width -- Return pixel width of a string of characters. * + * Get_Next_Text_Print_XY -- Calculates X and Y given ret value from Text_P* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "font.h" +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + * CHAR_PIXEL_WIDTH -- Return pixel width of a character. * + * * + * Retreives the pixel width of a character from the font width block. * + * * + * INPUT: Character. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/31/1992 DRD : Created. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +WORD cdecl Char_Pixel_Width(BYTE chr) +{ + WORD width; + + width = (UBYTE)*(FontWidthBlockPtr + (UBYTE)chr) + FontXSpacing; + + return(width); +} + + +/*************************************************************************** + * STRING_PIXEL_WIDTH -- Return pixel width of a string of characters. * + * * + * Calculates the pixel width of a string of characters. This uses * + * the font width block for the widths. * + * * + * INPUT: Pointer to string of characters. * + * * + * OUTPUT: Pixel width of a string of characters. * + * * + * WARNINGS: Set_Font must have been called first. * + * * + * HISTORY: * + * 01/30/1992 DRD : Created. * + * 01/31/1992 DRD : Use Char_Pixel_Width. * + * 06/30/1994 SKB : Converted to 32 bit library. * + *=========================================================================*/ +UWORD cdecl String_Pixel_Width(BYTE const *string) +{ + WORD width; // Working accumulator of string width. + WORD largest = 0; // Largest recorded width of the string. + + if (!string) return(0); + + width = 0; + while (*string) { + if (*string == '\r') { + string++; + largest = MAX(largest, width); + width = 0; + } else { + width += Char_Pixel_Width(*string++); // add each char's width + } + } + largest = MAX(largest, width); + return(largest); +} + + + +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * ULONG offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = vp.Get_Width() + vp.Get_XAdd(); + offset -= vp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} +/*************************************************************************** + * GET_NEXT_TEXT_PRINT_XY -- Calculates X and Y given ret value from Text_P* + * * + * * + * INPUT: VVPC& vp - viewport that was printed to. * + * ULONG offset - offset that Text_Print returned. * + * INT *x - x return value. * + * INT *y - y return value. * + * * + * OUTPUT: x and y are set. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/20/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y) +{ + INT buffwidth; + + if (offset) { + buffwidth = vp.Get_Width() + vp.Get_XAdd(); + offset -= vp.Get_Offset(); + *x = offset % buffwidth; + *y = offset / buffwidth; + } else { + *x = *y = 0; + } +} diff --git a/WWFLAT32/FONT/FONT.H b/WWFLAT32/FONT/FONT.H new file mode 100644 index 0000000..705b056 --- /dev/null +++ b/WWFLAT32/FONT/FONT.H @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef VBUFFER_H +#include +#endif + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Set_Font(VOID const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +WORD cdecl Char_Pixel_Width(BYTE chr); +UWORD cdecl String_Pixel_Width(BYTE const *string); +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y); +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Load_Font(BYTE const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID Set_Font_Palette_Range(VOID const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern BYTE FontWidth ; +extern BYTE FontHeight; +extern BYTE *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/WWFLAT32/FONT/LOADFONT.BAK b/WWFLAT32/FONT/LOADFONT.BAK new file mode 100644 index 0000000..c3382a8 --- /dev/null +++ b/WWFLAT32/FONT/LOADFONT.BAK @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "font.h" +#include +#include + +#if(IBM) +#include +#include +#endif + + + +extern "C" { + int FontXSpacing = 0; + int FontYSpacing = 0; + void const *FontPtr = NULL; +} +BYTE FontWidth = 8; +BYTE FontHeight = 8; + +// only font.c and set_font.c use the following +BYTE *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +VOID *cdecl Load_Font(BYTE const *name) +{ + BYTE valid; + WORD fh; // DOS file handle for font file. + UWORD size; // Size of the data in the file (-2); + BYTE *ptr = NULL; // Pointer to newly loaded font. + + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (BYTE *) &size, 2) != 2) return(NULL); + + ptr = (BYTE *) Alloc(size, MEM_NORMAL); + *(WORD *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} diff --git a/WWFLAT32/FONT/LOADFONT.CPP b/WWFLAT32/FONT/LOADFONT.CPP new file mode 100644 index 0000000..ceb645a --- /dev/null +++ b/WWFLAT32/FONT/LOADFONT.CPP @@ -0,0 +1,120 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "font.h" +#include +#include + +#if(IBM) +#include +#include +#endif + + + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +BYTE FontWidth = 8; +BYTE FontHeight = 8; + +// only font.c and set_font.c use the following +BYTE *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +VOID *cdecl Load_Font(BYTE const *name) +{ + BYTE valid; + WORD fh; // DOS file handle for font file. + UWORD size; // Size of the data in the file (-2); + BYTE *ptr = NULL; // Pointer to newly loaded font. + + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (BYTE *) &size, 2) != 2) return(NULL); + + ptr = (BYTE *) Alloc(size, MEM_NORMAL); + *(WORD *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} diff --git a/WWFLAT32/FONT/MAKEFILE b/WWFLAT32/FONT/MAKEFILE new file mode 100644 index 0000000..6203760 --- /dev/null +++ b/WWFLAT32/FONT/MAKEFILE @@ -0,0 +1,195 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = font +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + font.obj & + loadfont.obj & + set_font.obj & + setfpal.obj & + textprnt.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/FONT/SETFPAL.ASM b/WWFLAT32/FONT/SETFPAL.ASM new file mode 100644 index 0000000..cb2e734 --- /dev/null +++ b/WWFLAT32/FONT/SETFPAL.ASM @@ -0,0 +1,106 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL ColorXlat:BYTE +GLOBAL Set_Font_Palette_Range:NEAR + + CODESEG + +;*********************************************************** +; SET_FONT_PALETTE_RANGE +; +; VOID Set_Font_Palette_Range(VOID *palette, WORD start, WORD end); +; +; This routine changes the local Draw_Char color translation table +; with the color numbers in palette. +; +; Bounds Checking: forces start and end to a range of 0-15 +;* + PROC Set_Font_Palette_Range C near + USES eax, ebx, ecx,edi,esi + ARG palette:DWORD + ARG start:DWORD + ARG endval:DWORD + + cld + + mov esi,[palette] + + mov ebx,[start] + and ebx,0FH ; value 0-15 + + mov ecx,[endval] + and ecx,0FH ; value 0-15 + + cmp ecx,ebx ; if end < start then exit + jl short ??exit + + sub ecx,ebx ; number of colors = end - start + 1 + inc ecx + + mov edi,OFFSET ColorXlat ; get start of xlat table + add edi,ebx ; add starting offset + shl ebx,4 ; multiply start offset by 16 + add ebx,OFFSET ColorXlat ; add start of xlat table + +; updates 0-15 for lonibble xlat +; updates 0,16,32,...,240 for hinibble xlat + +??setpal: + lodsb ; get color number + stosb ; save color number for lonibble xlat + mov [ebx],al ; save color number for hinibble xlat + add ebx,010H ; add 16 to index for hinibble offset + dec ecx + jnz ??setpal + +??exit: + ret + + ENDP Set_Font_Palette_Range + +;*********************************************************** + + +;*********************************************************** + + END diff --git a/WWFLAT32/FONT/SET_FONT.CPP b/WWFLAT32/FONT/SET_FONT.CPP new file mode 100644 index 0000000..1b64c58 --- /dev/null +++ b/WWFLAT32/FONT/SET_FONT.CPP @@ -0,0 +1,89 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SET_FONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Font -- Changes the default text printing font. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "font.h" + + + +/*************************************************************************** + * SET_FONT -- Changes the default text printing font. * + * * + * This routine will change the default text printing font for all * + * text output. It handles updating the system where necessary. * + * * + * INPUT: fontptr -- Pointer to the font to change to. * + * * + * OUTPUT: Returns with a pointer to the previous font. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/06/1991 JLB : Created. * + * 09/17/1991 JLB : Fixed return value bug. * + * 01/31/1992 DRD : Modified to use new font format. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +VOID *cdecl Set_Font(VOID const *fontptr) +{ + VOID *oldfont; + BYTE const *blockptr; + + oldfont = (VOID *) FontPtr; + + if (fontptr) { + FontPtr = (VOID *) fontptr; + + /* + ** Inform the system about the new font. + */ + + FontWidthBlockPtr = (BYTE*)fontptr + *(UWORD *)((BYTE*)fontptr + FONTWIDTHBLOCK); + blockptr = (BYTE*)fontptr + *(UWORD *)((BYTE*)fontptr + FONTINFOBLOCK); + FontHeight = *(blockptr + FONTINFOMAXHEIGHT); + FontWidth = *(blockptr + FONTINFOMAXWIDTH); + //Draw_Char_Setup(); + +#if FALSE + WindowLines = WinH / FontHeight; + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / FontWidth; +#endif + } + + return(oldfont); +} diff --git a/WWFLAT32/FONT/TEXTPRNT.ASM b/WWFLAT32/FONT/TEXTPRNT.ASM new file mode 100644 index 0000000..7f73e55 --- /dev/null +++ b/WWFLAT32/FONT/TEXTPRNT.ASM @@ -0,0 +1,436 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + + +GLOBAL C FontPtr:DWORD +GLOBAL Text_Print:NEAR + +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + +LOCALS ?? + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* TEXT_PRINT -- Assembly text print routine. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/28/1994 SKB : Created. * +;*=========================================================================* + PROC Text_Print C near + USES ebx,ecx,edx,esi,edi + ARG vpselector:WORD + ARG vpoffset:DWORD + ARG vpwidth:DWORD + ARG vpheight:DWORD + ARG vpxadd:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + movzx eax,[vpselector] + mov es,ax ; Set up selector to write to. + + mov eax,[vpwidth] ; get the width of the viewport + add eax,[vpxadd] ; add amount to add to get to left edge of next line. + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[vpoffset] ; get start of the viewport + add edi,eax ; add x,y position to start of vp to get starting row address. + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + ; Now go into the line feed code..... + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Text_Print + + + +;*********************************************************** + + END diff --git a/WWFLAT32/FONT/TEXTPRNT.BAK b/WWFLAT32/FONT/TEXTPRNT.BAK new file mode 100644 index 0000000..730a3f0 --- /dev/null +++ b/WWFLAT32/FONT/TEXTPRNT.BAK @@ -0,0 +1,436 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : 32 bit library Text Print * +;* * +;* File Name : TEXTPRNT.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 2, 1994 * +;* * +;* Last Update : July 2, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Text_Print -- Assembly text print routine. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + + +GLOBAL FontPtr:DWORD +GLOBAL Text_Print:NEAR + +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + +LOCALS ?? + + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* TEXT_PRINT -- Assembly text print routine. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/28/1994 SKB : Created. * +;*=========================================================================* + PROC Text_Print C near + USES ebx,ecx,edx,esi,edi + ARG vpselector:WORD + ARG vpoffset:DWORD + ARG vpwidth:DWORD + ARG vpheight:DWORD + ARG vpxadd:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + movzx eax,[vpselector] + mov es,ax ; Set up selector to write to. + + mov eax,[vpwidth] ; get the width of the viewport + add eax,[vpxadd] ; add amount to add to get to left edge of next line. + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[vpoffset] ; get start of the viewport + add edi,eax ; add x,y position to start of vp to get starting row address. + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + ; Now go into the line feed code..... + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Text_Print + + + +;*********************************************************** + + END diff --git a/WWFLAT32/IFF/FILEPCX.H b/WWFLAT32/IFF/FILEPCX.H new file mode 100644 index 0000000..f9650cd --- /dev/null +++ b/WWFLAT32/IFF/FILEPCX.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,BYTE* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic ,BYTE* palette ) ; + +#endif diff --git a/WWFLAT32/IFF/IFF.BAK b/WWFLAT32/IFF/IFF.BAK new file mode 100644 index 0000000..db1d46c --- /dev/null +++ b/WWFLAT32/IFF/IFF.BAK @@ -0,0 +1,168 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#ifndef GBUFFER_H +#include +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((LONG) ((LONG) d << 24) | ((LONG) c << 16) | ((LONG) b << 8) | (LONG)(a)) +#define IFFize_WORD(a) Reverse_WORD(a) +#define IFFize_LONG(a) Reverse_LONG(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + BYTE Method; // Compression method (CompressionType). + BYTE pad; // Reserved pad byte (always 0). + LONG Size; // Size of the uncompressed data. + WORD Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +WORD cdecl Open_Iff_File(BYTE const *filename); +VOID cdecl Close_Iff_File(WORD fh); +ULONG cdecl Get_Iff_Chunk_Size(WORD fh, LONG id); +ULONG cdecl Read_Iff_Chunk(WORD fh, LONG id, VOID *buffer, ULONG maxsize); +VOID cdecl Write_Iff_Chunk(WORD file, LONG id, VOID *buffer, LONG length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +ULONG cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size); +ULONG cdecl Write_Data(BYTE const *name, VOID *ptr, ULONG size); +VOID * cdecl Load_Alloc_Data(BYTE const *name, MemoryFlagType flags); +ULONG cdecl Load_Uncompress(BYTE const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data=NULL); +ULONG cdecl Uncompress_Data(VOID const *src, VOID *dst); +VOID cdecl Set_Uncomp_Buffer(WORD buffer_segment, UWORD size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern VOID Pack_2_Plane(VOID *buffer, VOID * pageptr, WORD planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Compress(VOID *source, VOID *dest, ULONG length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Uncompress(VOID *source, VOID *dest, ULONG length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H + \ No newline at end of file diff --git a/WWFLAT32/IFF/IFF.CPP b/WWFLAT32/IFF/IFF.CPP new file mode 100644 index 0000000..78ac02d --- /dev/null +++ b/WWFLAT32/IFF/IFF.CPP @@ -0,0 +1,325 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +WORD cdecl Open_Iff_File(BYTE const *filename) +{ + WORD fh; // File handle. + LONG type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +VOID cdecl Close_Iff_File(WORD fh) +{ + if (fh != ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: WORD file handle to open IFF file, LONG id to get size of * + * * + * OUTPUT: LONG size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +ULONG cdecl Get_Iff_Chunk_Size(WORD fh, LONG id) +{ + LONG form; // Chunk iff form name. + LONG chunksize; // Size of the chunk. + BYTE first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (BYTE *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_LONG(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +ULONG cdecl Read_Iff_Chunk(WORD fh, LONG id, VOID *buffer, ULONG maxsize) +{ + LONG form; // Chunk iff form name. + ULONG chunksize; // Size of the chunk. + BYTE first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (BYTE *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_LONG(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl Write_Iff_Chunk(WORD file, LONG id, VOID *buffer, LONG length) +{ + LONG pos; // Current position in the IFF file. + LONG oldpos; // Record of start of chunk offset. + LONG endpos; // end of file offset before we write our data + LONG value; + BOOL odd; // Is length odd? + BYTE pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (WORD)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size LONG. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/WWFLAT32/IFF/IFF.H b/WWFLAT32/IFF/IFF.H new file mode 100644 index 0000000..11b87af --- /dev/null +++ b/WWFLAT32/IFF/IFF.H @@ -0,0 +1,168 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#ifndef GBUFFER_H +#include +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((LONG) ((LONG) d << 24) | ((LONG) c << 16) | ((LONG) b << 8) | (LONG)(a)) +#define IFFize_WORD(a) Reverse_WORD(a) +#define IFFize_LONG(a) Reverse_LONG(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + BYTE Method; // Compression method (CompressionType). + BYTE pad; // Reserved pad byte (always 0). + LONG Size; // Size of the uncompressed data. + WORD Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +WORD cdecl Open_Iff_File(BYTE const *filename); +VOID cdecl Close_Iff_File(WORD fh); +ULONG cdecl Get_Iff_Chunk_Size(WORD fh, LONG id); +ULONG cdecl Read_Iff_Chunk(WORD fh, LONG id, VOID *buffer, ULONG maxsize); +VOID cdecl Write_Iff_Chunk(WORD file, LONG id, VOID *buffer, LONG length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +//WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +ULONG cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size); +ULONG cdecl Write_Data(BYTE const *name, VOID *ptr, ULONG size); +VOID * cdecl Load_Alloc_Data(BYTE const *name, MemoryFlagType flags); +//ULONG cdecl Load_Uncompress(BYTE const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data=NULL); +ULONG cdecl Uncompress_Data(VOID const *src, VOID *dst); +VOID cdecl Set_Uncomp_Buffer(WORD buffer_segment, UWORD size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern VOID Pack_2_Plane(VOID *buffer, VOID * pageptr, WORD planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Compress(VOID *source, VOID *dest, ULONG length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Uncompress(VOID *source, VOID *dest, ULONG length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H + diff --git a/WWFLAT32/IFF/LCWCOMP.ASM b/WWFLAT32/IFF/LCWCOMP.ASM new file mode 100644 index 0000000..c7deecc --- /dev/null +++ b/WWFLAT32/IFF/LCWCOMP.ASM @@ -0,0 +1,286 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwcomp.asm 1.1 1994/04/11 15:31:10 jeff_wilson Exp $ +;*************************************************************************** +;** 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: * +;* * +; ULONG LCW_Compress(BYTE *source,BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL LCW_Compress :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_Compress 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_Compress + + +END + \ No newline at end of file diff --git a/WWFLAT32/IFF/LCWUNCMP.ASM b/WWFLAT32/IFF/LCWUNCMP.ASM new file mode 100644 index 0000000..75a2823 --- /dev/null +++ b/WWFLAT32/IFF/LCWUNCMP.ASM @@ -0,0 +1,221 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/lcwuncmp.asm 1.1 1994/04/11 15:31:21 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- + + + +PROC LCW_Uncompress C near + + USES ebx,ecx,edx,edi,esi + + ARG source:DWORD + ARG dest:DWORD + ARG length:DWORD +;LOCALS + LOCAL a1stdest:DWORD + LOCAL maxlen:DWORD + LOCAL lastbyte:DWORD + LOCAL lastcom:DWORD + LOCAL lastcom1:DWORD + + + mov edi,[dest] + mov esi,[source] + mov edx,[length] + +; +; +; uncompress 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 copy copy w1 bytes from offset w2 +; n=11111110,w1,b1 long run run byte b1 for w1 bytes +; n=10000000 end end of data reached +; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + +??loop: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short ??out ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + sub eax,eax + lodsb + or al,al ; see if its a short run + js short ??notshort + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + sub ecx,ecx + mov cl,al ; put count nibble in ch + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??rsok: + lodsb ; get rel offset low byte + mov ebx,esi ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp ??loop + +??notshort: + test al,40h ; is it a length? + jne short ??notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short ??out ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp ??loop + +??out: + mov eax,edi + sub eax,[a1stdest] + jmp short ??exit + +??notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short ??notrunlength + + sub eax,eax + lodsw + + mov ecx,eax + + sub eax,eax + lodsb + + mov ebx,esi ; save the source offset + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runlenok: + rep stosb + + jmp ??loop + +??notrunlength: + cmp al,0FFh ; is it a long run? + jne short ??notlong ; if not use the code as the size + + sub eax,eax + lodsw ; if so, get the size + mov ecx,eax ; put int the count byte + +??notlong: + lodsw ; get the rel index + mov ebx,esi ; save the source offset + add eax,[a1stdest] ; add in the first index + mov esi,eax ; use this as a source + cmp ecx,[maxlen] ; is it too big to fit? + jbe short ??runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + +??runok: + rep movsb + + jmp ??loop + +??exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + ret + +ENDP LCW_Uncompress + +;*********************************************************** + + + END + + \ No newline at end of file diff --git a/WWFLAT32/IFF/LOAD.BAK b/WWFLAT32/IFF/LOAD.BAK new file mode 100644 index 0000000..66c3326 --- /dev/null +++ b/WWFLAT32/IFF/LOAD.BAK @@ -0,0 +1,443 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "file.h" +#include "iff.h" +#include +#include +#include + + + +#if(LZW_SUPPORTED) + +/* These are our local pointer and size variables for the LZW table. They + are set through the Set_Uncomp_Buffer routine. */ + +PRIVATE int LZW_Table = 0; /* No current paragraph */ +PRIVATE unsigned int LZW_Table_Size = 0; /* No current size */ + +#endif + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Load_Data(char const *name, VOID *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Write_Data(char const *name, VOID *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +VOID * cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + VOID *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: BYTE * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * VOID * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + VOID *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((int*)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((int*)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: BYTE *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * VOID *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + VOID *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_WORD(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((int*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_WORD(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((int*)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + //newuncomp_ptr = (char*)MK_FP(PageArray[uncomp_buff],0); + //newuncomp_ptr += (unsigned int)(PageArraySize[uncomp_buff] - (isize+8)); + //newuncomp_ptr = Normalize_Pointer(newuncomp_ptr); + //newuncomp_ptr = MK_FP(FP_SEG(newuncomp_ptr),0); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Uncompress_Data(VOID const *src, VOID *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; +#if(LZW_SUPPORTED) + VOID *table_buffer; +#endif + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_LONG(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_WORD(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((VOID *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((VOID *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((VOID *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((VOID *) src, (VOID *) dst, (unsigned long) uncomp_size); + break; + +#if(LZW_SUPPORTED) + case LZW12: + /* If the current buffer isn't big enough, try to + allocate one that is */ + + if (LZW_Table_Size < LZW12BUFFERSIZE) { + table_buffer = Alloc((long) LZW12BUFFERSIZE, MEM_PARA); + LZW12_Uncompress(FP_SEG(src), FP_SEG(dst), + FP_SEG(table_buffer)); + Free(table_buffer); + } + else { + LZW12_Uncompress(FP_SEG(src), FP_SEG(dst), LZW_Table); + } + break; + + case LZW14: + /* If the current buffer isn't big enough, try to + allocate one that is */ + + if (LZW_Table_Size < LZW14BUFFERSIZE) { + table_buffer = Alloc((long) LZW14BUFFERSIZE, MEM_PARA); + LZW14_Uncompress(FP_SEG(src), FP_SEG(dst), + FP_SEG(table_buffer)); + Free(table_buffer); + } + else { + LZW14_Uncompress(FP_SEG(src), FP_SEG(dst), LZW_Table); + } + break; +#endif + } + + return(uncomp_size); +} + + +#if(LZW_SUPPORTED) +/* ARGSUSED */ +#pragma argsused +VOID cdecl Set_Uncomp_Buffer(int buffer_segment, unsigned int size_of_buffer) +{ + + if ((LZW_Table = buffer_segment) == NULL) { + /* ERROR HERE */ + } + if ((LZW_Table_Size = size_of_buffer) == 0U) { + /* ERROR HERE */ + } +} +#endif + diff --git a/WWFLAT32/IFF/LOAD.CPP b/WWFLAT32/IFF/LOAD.CPP new file mode 100644 index 0000000..132e96b --- /dev/null +++ b/WWFLAT32/IFF/LOAD.CPP @@ -0,0 +1,444 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "file.h" +#include "iff.h" +#include +#include +#include + + + +#if(LZW_SUPPORTED) + +/* These are our local pointer and size variables for the LZW table. They + are set through the Set_Uncomp_Buffer routine. */ + +PRIVATE int LZW_Table = 0; /* No current paragraph */ +PRIVATE unsigned int LZW_Table_Size = 0; /* No current size */ + +#endif + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Load_Data(char const *name, VOID *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Write_Data(char const *name, VOID *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + +#ifdef NEVER +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +VOID * cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + VOID *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} +#endif + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: BYTE * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * VOID * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + VOID *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((int*)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((int*)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: BYTE *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * VOID *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + VOID *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_WORD(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((int*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_WORD(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((int*)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + //newuncomp_ptr = (char*)MK_FP(PageArray[uncomp_buff],0); + //newuncomp_ptr += (unsigned int)(PageArraySize[uncomp_buff] - (isize+8)); + //newuncomp_ptr = Normalize_Pointer(newuncomp_ptr); + //newuncomp_ptr = MK_FP(FP_SEG(newuncomp_ptr),0); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long cdecl Uncompress_Data(VOID const *src, VOID *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; +#if(LZW_SUPPORTED) + VOID *table_buffer; +#endif + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_LONG(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_WORD(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((VOID *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((VOID *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((VOID *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((VOID *) src, (VOID *) dst, (unsigned long) uncomp_size); + break; + +#if(LZW_SUPPORTED) + case LZW12: + /* If the current buffer isn't big enough, try to + allocate one that is */ + + if (LZW_Table_Size < LZW12BUFFERSIZE) { + table_buffer = Alloc((long) LZW12BUFFERSIZE, MEM_PARA); + LZW12_Uncompress(FP_SEG(src), FP_SEG(dst), + FP_SEG(table_buffer)); + Free(table_buffer); + } + else { + LZW12_Uncompress(FP_SEG(src), FP_SEG(dst), LZW_Table); + } + break; + + case LZW14: + /* If the current buffer isn't big enough, try to + allocate one that is */ + + if (LZW_Table_Size < LZW14BUFFERSIZE) { + table_buffer = Alloc((long) LZW14BUFFERSIZE, MEM_PARA); + LZW14_Uncompress(FP_SEG(src), FP_SEG(dst), + FP_SEG(table_buffer)); + Free(table_buffer); + } + else { + LZW14_Uncompress(FP_SEG(src), FP_SEG(dst), LZW_Table); + } + break; +#endif + } + + return(uncomp_size); +} + + +#if(LZW_SUPPORTED) +/* ARGSUSED */ +#pragma argsused +VOID cdecl Set_Uncomp_Buffer(int buffer_segment, unsigned int size_of_buffer) +{ + + if ((LZW_Table = buffer_segment) == NULL) { + /* ERROR HERE */ + } + if ((LZW_Table_Size = size_of_buffer) == 0U) { + /* ERROR HERE */ + } +} +#endif + diff --git a/WWFLAT32/IFF/LOADPCX.CPP b/WWFLAT32/IFF/LOADPCX.CPP new file mode 100644 index 0000000..f6b6b8e --- /dev/null +++ b/WWFLAT32/IFF/LOADPCX.CPP @@ -0,0 +1,187 @@ +/* +** 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 . +*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +/*************************************************************************** + ** 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 : IFF * + * * + * File Name : LOADPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 3, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * GraphicBufferClass* Read_PCX_File (char* name, void *Buff, long size ); * + * int Get_PCX_Palette (char * name, void& palette ) * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optinal, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointd by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() * file_ptr ++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Read_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } + +GraphicBufferClass* Read_PCX_File (char* name, BYTE* Palette,void *Buff, long Size) +{ + unsigned i , j ; + unsigned rle ; + unsigned color ; + unsigned scan_pos ; + unsigned file_size ; + char * file_ptr ; + int width ; + int height ; + int file_handle ; + char * buffer ; + PCX_HEADER header ; + RGB * pal ; + char pool [ POOL_SIZE ] ; + GraphicBufferClass * pic ; + + // Open file name + file_handle = Open_File ( name , READ ) ; + if ( file_handle == ERROR ) return NULL ; + + Read_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + if ( header.id != 10 && header.version != 5 && + header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1 ; + height = header.height - header.y + 1 ; + + if ( Buff ) { + buffer = ( char * ) Buff; + i = Size / width; + height = MIN ( i - 1, height); + pic = new GraphicBufferClass( Size, width, height, buffer); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } else { + pic = new GraphicBufferClass(width*(height+4), width, height); + if ( !(pic && pic->Get_Buffer()))return NULL ; + } + + buffer = (char *) pic->Get_Buffer() ; + file_ptr = pool ; + Read_File ( file_handle, pool , POOL_SIZE ) ; + + if ( header.byte_per_line != width ) + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ) ; + i += rle ; + } else * ( buffer + scan_pos + i ++ ) = rle ; + } + if ( i == width ) + rle = READ_CHAR () ; +// if ( rle > 192 ) rle = READ_CHAR (); + } + + + else for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ) ; + i += rle ; + } else * ( buffer + i ++ ) = rle ; + } + + if ( Palette ) { + Seek_File ( file_handle , - 256 * sizeof ( RGB ) , SEEK_END ) ; + Read_File ( file_handle, Palette , 256 * sizeof ( RGB )) ; + + pal = ( RGB * ) Palette ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red >>= 2 ; + pal -> green >>= 2 ; + pal -> blue >>= 2 ; + pal ++ ; + } + } + Close_File (file_handle) ; + return pic ; +} + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff, * + * char* palette) * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * Buff is a pointer to a BufferClass the will hold the pcx file * + * at location pointd by Buffer; * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + *=========================================================================*/ + +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette) +{ + return Read_PCX_File(name, palette, (void*)Buff.Get_Buffer(), Buff.Get_Size()); +} diff --git a/WWFLAT32/IFF/LOADPICT.BAK b/WWFLAT32/IFF/LOADPICT.BAK new file mode 100644 index 0000000..b02557f --- /dev/null +++ b/WWFLAT32/IFF/LOADPICT.BAK @@ -0,0 +1,571 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/loadpict.cpp 1.1 1994/04/20 14:38:08 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFFEXTRA.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 11, 1991 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * ILBM_To_Amiga -- Convert ILBM to bitplane Amiga format. * + * ILBM_To_MCGA -- Converts ILBM picture into MCGA format. * + * PBM_To_Amiga -- Converts a PBM picture into Amiga format. * + * Load_Picture -- Loads a picture file (CPS or LBM format). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iff.h" +#include "file.h" +#include // For GraphicMode and MCGA_MODE. +#include // For Alloc. + +#if(IBM) +#include +#endif + +// Since we are not currently using AMIGA, this has been put in to +// give us back some code space. If it is needed for a utility, +// this module should be recompiled with that utility and set the +// define to TRUE. +#define MAKE_AMIGA_ART FALSE + +/* +** An IFF picture file can have one of two formats: +** ILBM - InterLeaved Bit Map +** PBM - Packed Bit Map +*/ +typedef enum { + FORM_ILBM, + FORM_PBM +} IFFForm_Type; + +/* +** These are the various chunks that compose an IFF picture file. +*/ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_ILBM MAKE_ID('I','L','B','M') +#define ID_PBM MAKE_ID('P','B','M',' ') +#define ID_CMAP MAKE_ID('C','M','A','P') +#define ID_BODY MAKE_ID('B','O','D','Y') +#define ID_BMHD MAKE_ID('B','M','H','D') + + +/* +** The BMHD (Bit Map HeaDer) chunk in an IFF picture file contains the +** information necessary to extract that picture from the BODY chunk. +** It also indicates the size and depth of the source art. +*/ +typedef struct { + UWORD W, H; // Raster width and height in pixels. + WORD X, Y; // Pixel postion for this image. + BYTE BPlanes; // Number of bitplanes. + UBYTE Masking; // Masking control byte. + // 0 = No masking. + // 1 = Has a mask. + // 2 = Has transparent color. + // 3 = Lasso. + UBYTE Compression; // Compression method. + // 0 = No compression. + // 1 = Byte run compression. + BYTE pad; + UWORD Transparent; // Transparent color number. + UBYTE XAspect, // Pixel aspect ratio of source art. + YAspect; + WORD PageWidth, // Source 'page' size in pixels. + PageHeight; +} BitMapHeader_Type; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE VOID cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, WORD planes); +PRIVATE VOID cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes); +PRIVATE VOID cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * ILBM_TO_MCGA -- Converts ILBM picture into MCGA format. * + * * + * This converts an ILBM picture (typical of DPaint LBM files) and * + * converts it to MCGA mode (byte per pixel). This function would be * + * used after the body of an ILBM picture is loaded. Because the * + * number of bitplanes can vary greatly, it is necessary to pass the * + * bitplane count to this function. The size (320 by 200) of the * + * source picture is presumed. * + * * + * INPUT: src - Buffer number for source ILBM picture. * + * * + * dest - Buffer number for place to put MCGA format. * + * * + * planes- The number of bitplanes in the ILBM picture. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + *=========================================================================*/ +PRIVATE VOID cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, WORD planes) +{ + BYTE *source; // Source pointer. + BYTE *destination; // Destination pointer. + WORD index,j,i; // Working index values. + WORD bplane; // Bit plane counter. + BYTE bytes[8]; // Byte array holding max bitplanes (8). + BYTE value; // Composed byte(pixel) value. + + source = (BYTE *) src.Get_Buffer(); + destination = (BYTE *) dest.Get_Buffer(); + + memset(bytes, '\0', 8); // Makes sure upper bits will be clear. + + // Each row is grouped and processed together. + + for (index = 0; index < 200 /*bmhd.H*/; index++) { + + // Process each line in groups of 8 bytes. + + for (j = 0; j < 40 /*(bmhd.W>>3)*/; j++) { + + // Get the bitplane bytes. + + for (bplane = 0; bplane < planes /*bmhd.BPlanes*/; bplane++) { + bytes[bplane] = *(source + (bplane * 40 /*(bmhd.W>>3)*/)); + } + source++; + + // Roll the bits out to create 8 pixels (by bytes). + for (i = 0; i < 8; i++) { + + // 8 bits per byte. + value = 0; + for (bplane = planes - 1/*bmhd.BPlanes-1*/; bplane >= 0; bplane--) { + value <<= 1; // Make room for next bit. + if (bytes[bplane] & 0x80) value |= 1; // Set the bit. + bytes[bplane] <<= 1; + } + *destination++ = value; // Output the pixel byte. + } + } + + // Advance to next scan line. + source += 40 /* (bmhd.W >> 3)*/ * (planes /* bmhd.BPlanes */ - 1); + } +} + + +/*************************************************************************** + * ILBM_TO_AMIGA -- Convert ILBM to bitplane Amiga format. * + * * + * This converts an InterLeaved BitMap picture into Amiga bitplane * + * format (8K per bitplane). The data of an ILBM picture is controlled * + * by the number of bitplanes it contains. The bitplane count is * + * passed into this program. * + * * + * INPUT: src - Buffer number for source ILBM picture data. * + * * + * dest - Buffer number for destination Amiga picture data. * + * * + * planes- The number of bitplanes in the source ILBM data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE VOID cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes) +{ + WORD row; // Working row counter. + WORD bp; // Working bitplane counter. + BYTE *srcptr, // Source buffer pointer. + *dstptr; // Destination buffer pointer. + + srcptr = (BYTE *) src.Get_Buffer(); // Source buffer pointer. + dstptr = (BYTE *) dest.Get_Buffer(); // Destination buffer pointer. + + for (row = 0; row < 200; row++) { + for (bp = 0; bp < planes; bp++) { + Mem_Copy(srcptr,dstptr+(8000*bp),40); + srcptr += 40; + } + dstptr += 40; + } +} +#endif + + +/*************************************************************************** + * PBM_TO_AMIGA -- Converts a PBM picture into Amiga format. * + * * + * This converts a PBM (Packed Bit Map) MCGA picture into Amiga * + * bitplane format. A PBM picture presumes 8 bitplanes, but this * + * can be controlled by the 'plane' parameter passed in. * + * * + * INPUT: src - Buffer number for the source PBM data. * + * * + * dest - Buffer number to place the Amiga format picture. * + * * + * planes- The number of bitplanes to extract from the PBM source * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE VOID cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes) +{ + WORD row, // Working row counter. + col, // Working column (by byte) counter. + bit; // Working bitplane counter. + UBYTE *destptr, // Destination byte pointer. + *srcptr; // Source byte pointer. + UBYTE value; // Working input MCGA pixel number. + + + destptr = (UBYTE *) dest.Get_Buffer(); + srcptr = (UBYTE *) src.Get_Buffer(); + + memset(destptr, 0, 32000); + memset(destptr+32000, 0, 32000); + + for (row = 0; row < 200; row++) { + + for (col = 0; col < 320; col++) { + value = *srcptr++; + + for (bit = 0; bit < planes; bit++) { + if (value & (0x01 << bit)) { + destptr[(WORD)((8000L * (LONG)bit) + (col>>3))] |= 0x80 >> (col & 0x07); + } + } + } + + destptr += 40; + } +} +#endif + +/*************************************************************************** + * LOAD_PICTURE -- Loads a picture file (CPS or LBM format). * + * * + * This loads a picture file into a page buffer. The loaded file will * + * be in MCGA or Amiga mode as requested. Supported source formats * + * are CPS or all forms of IFF dpaint files. * + * * + * INPUT: filename - Source filename. The only files that are * + * processed as IFF are those files that end with * + * ".LBM". * + * * + * loadbuf - Buffer type number for the temporary loading * + * buffer. It will be trashed. * + * * + * destbuf - Buffer type number for the picture to be placed. * + * * + * palette - Palette buffer pointer. If this value is NULL * + * then no palette is loaded. * + * * + * format - Desired destination format. * + * BM_AMIGA - Destination buffer will contain the * + * picture in bitplane format (Amiga). * + * The buffer will contain data equal to * + * 8K times the number of bit planes. * + * * + * BM_MCGA - Destination buffer will contain the * + * picture in MCGA format (byte per pixel).* + * The buffer will be 64K in size. * + * * + * OUTPUT: WORD number of bitplanes read into the dest buffer * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 05/20/1991 JLB : Handles Amiga and IBM destination formats. * + *=========================================================================*/ +WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette, PicturePlaneType format) +{ + int fh; // Input file handle. + long ifftype; // Iff form type. + int counter; // Count of the bytes decompressed. + int value; // Working compression code value. + int len; // WORD sized length value. + int index; // Working index values. + BitMapHeader_Type bmhd; // BMHD chunk data. + IFFForm_Type formtype; // ILBM, PBM. + char *src; // Working source body pointer. + char *dest; // Working destination body pointer. + + + //len = strlen(filename); + //strupr(filename); + + fh = Open_File(filename,READ); + if (fh == ERROR) return(FALSE); + Read_File(fh,&ifftype,4L); + Close_File(fh); + + if (ifftype != ID_FORM) + { + UBYTE * ptr = NULL ; + + if ( GraphicMode == MCGA_MODE || + ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX ) ) + ptr = palette ; + +// return((WORD)Load_Uncompress(filename, scratchbuf, destbuf, (GraphicMode == MCGA_MODE) ? palette : NULL) / 8000); + return((WORD)Load_Uncompress(filename, scratchbuf, destbuf, palette ) / 8000 ) ; + } + else + { + + fh = Open_Iff_File(filename); // Opens and checks for IFF form. + if (fh == ERROR) return(FALSE); + + Read_File(fh, &ifftype, 4L); + if (ifftype == ID_ILBM) { + formtype = FORM_ILBM; // Inter-Leaved Bit Map. + } else { + if (ifftype == ID_PBM) { + formtype = FORM_PBM; // Packed Bit Map. + } else { + return FALSE; // Not a recognizable picture file. + } + } + + // Load the BMHD chunk. + if (Read_Iff_Chunk(fh,ID_BMHD,(BYTE*)&bmhd,sizeof(BitMapHeader_Type))) { + + #if(IBM) + // Perform necessary IBM conversions to the data. + bmhd.W = Reverse_WORD(bmhd.W); + bmhd.H = Reverse_WORD(bmhd.H); + bmhd.X = Reverse_WORD(bmhd.X); + bmhd.Y = Reverse_WORD(bmhd.Y); + + // this is a mistake Xaspect and YAspect are char type + // bmhd.XAspect = Reverse_WORD(bmhd.XAspect); + // bmhd.YAspect = Reverse_WORD(bmhd.YAspect); + value = bmhd.XAspect ; + bmhd.XAspect = bmhd.YAspect ; + bmhd.YAspect = ( unsigned char ) value ; + + bmhd.PageWidth = Reverse_WORD(bmhd.PageWidth); + bmhd.PageHeight = Reverse_WORD(bmhd.PageHeight); + #endif + + if (bmhd.Masking > 2) return FALSE; // Don't allow brushes. + if (bmhd.Compression > 1) return FALSE; // Unknown compression. + + } else { + return FALSE; // Unable to read the required BMHD chunk. + } + + // Load the palette if asked. + if (palette) + { + int pbytes ; // Number of CMAP bytes required. + unsigned char color; // Palette color value. + unsigned char *paletteptr; // Allocated buffer for palette conversions. + unsigned char *source; // Scratch source CMAP data pointer. + unsigned char *dest2; // Scratch destination palette pointer. + + // Number of CMAP bytes that are needed. + pbytes = (1 << bmhd.BPlanes) * 3; + + // Allocate the temporary palette buffer. + paletteptr = (UBYTE *)Alloc(pbytes, MEM_CLEAR); + source = paletteptr; + dest2 = palette; + + // Read in only the bytes that are needed. + pbytes = (WORD)Read_Iff_Chunk(fh, ID_CMAP, (BYTE *) paletteptr, pbytes); + +#if(IBM) + if ( pbytes && + ( ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX ) || + GraphicMode == MCGA_MODE ) ) + { +#else + if (pbytes) { +#endif + + /* + ** CMAP to machine specific palette conversion code. Conversion + ** goes from CMAP three bytes per color register to the machine + ** specific form. + */ + switch(format) { + default: + case BM_MCGA: + // Convert CMAP to IBM MCGA palette form. + for (index = 0; index < pbytes; index++) { + *dest2++ = *source++ >> 2; + } + break; +#if MAKE_AMIGA_ART + + case BM_AMIGA: + // Convert CMAP to Amiga nibble packed palette form. + for (index = 0; index < pbytes; index += 3) { + *dest2++ = *(source++) >> 4; + color = (*(source++) & 0xf0); + color += *(source++) >> 4; + *dest2++ = color; + } + + break; +#endif + } + } + + Free(paletteptr); + } + + + // Load in BODY chunk. + dest = (BYTE *) scratchbuf.Get_Buffer(); + src = (BYTE *) destbuf.Get_Buffer(); + + if (Read_Iff_Chunk(fh, ID_BODY, src, destbuf.Get_Size())) + { + for (index = 0; index < (WORD)bmhd.H; index++) + { + /* Height of source */ + // Transfer (possibly uncompress) one row of data. + // PBM or ILBM reader. Bytes per row (all bitplanes). + + counter = bmhd.BPlanes * (bmhd.W >> 3); + + // If there is a mask then there is one more bitplane. + if (bmhd.Masking == 1) + counter += bmhd.W >> 3 ; + + if (bmhd.Compression == 1) + { + // The data is compressed. + // Decompress one scanline (all bitplanes) at a time. + while (counter) + { + value = ( signed char ) *src++; // Decompression code. + if (value == -128) continue; // NOOP code. + + if (value >= 0) + { + // Copy N+1 bytes. + len = ((WORD) value) + 1; + + // Ignore the masking bitplane. + if ( bmhd.Masking != 1 || + (bmhd.Masking==1 && counter > ((WORD)bmhd.W >> 3) ) ) + { + memcpy(dest, src, len); + dest += len; + } + counter -= len; + src += len; + + } + else + { + // Replicate -N+1 bytes. + len = (-((WORD) value)) + 1; + value = *src++; + + // Ignore the masking bitplane. + if (bmhd.Masking != 1 || (bmhd.Masking==1 && counter > ((WORD)bmhd.W >> 3))) + { + memset(dest,value,len); + dest += len; + } + counter -= len; + } + } + } + + else + { + // Plain data is just copied. + memcpy(dest,src,counter); + dest += counter; + src += counter; + } + } + + /* + ** Perform necessary conversions to the data in order to reach + ** the desired format. + */ + switch (format) { + default: + case BM_MCGA: // Byte per pixel desired. + if (formtype == FORM_ILBM) { + ILBM_To_MCGA(scratchbuf, destbuf, bmhd.BPlanes); + } else { + Mem_Copy(scratchbuf.Get_Buffer(), destbuf.Get_Buffer(), 64000L); + } + break; + +#if MAKE_AMIGA_ART + case BM_AMIGA: // Bitplane format desired. + if (formtype == FORM_ILBM) { + ILBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } else { + PBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } + break; +#endif + } + } + + Close_Iff_File(fh); + } + + return((WORD)bmhd.BPlanes); // Loaded the picture successfully. +} diff --git a/WWFLAT32/IFF/LOADPICT.CPP b/WWFLAT32/IFF/LOADPICT.CPP new file mode 100644 index 0000000..8e4c1c8 --- /dev/null +++ b/WWFLAT32/IFF/LOADPICT.CPP @@ -0,0 +1,574 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/loadpict.cpp 1.1 1994/04/20 14:38:08 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : IFFEXTRA.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 11, 1991 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * ILBM_To_Amiga -- Convert ILBM to bitplane Amiga format. * + * ILBM_To_MCGA -- Converts ILBM picture into MCGA format. * + * PBM_To_Amiga -- Converts a PBM picture into Amiga format. * + * Load_Picture -- Loads a picture file (CPS or LBM format). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iff.h" +#include "file.h" +#include // For GraphicMode and MCGA_MODE. +#include // For Alloc. + +#if(IBM) +#include +#endif + +// Since we are not currently using AMIGA, this has been put in to +// give us back some code space. If it is needed for a utility, +// this module should be recompiled with that utility and set the +// define to TRUE. +#define MAKE_AMIGA_ART FALSE + +/* +** An IFF picture file can have one of two formats: +** ILBM - InterLeaved Bit Map +** PBM - Packed Bit Map +*/ +typedef enum { + FORM_ILBM, + FORM_PBM +} IFFForm_Type; + +/* +** These are the various chunks that compose an IFF picture file. +*/ +#define ID_FORM MAKE_ID('F','O','R','M') +#define ID_ILBM MAKE_ID('I','L','B','M') +#define ID_PBM MAKE_ID('P','B','M',' ') +#define ID_CMAP MAKE_ID('C','M','A','P') +#define ID_BODY MAKE_ID('B','O','D','Y') +#define ID_BMHD MAKE_ID('B','M','H','D') + + +/* +** The BMHD (Bit Map HeaDer) chunk in an IFF picture file contains the +** information necessary to extract that picture from the BODY chunk. +** It also indicates the size and depth of the source art. +*/ +typedef struct { + UWORD W, H; // Raster width and height in pixels. + WORD X, Y; // Pixel postion for this image. + BYTE BPlanes; // Number of bitplanes. + UBYTE Masking; // Masking control byte. + // 0 = No masking. + // 1 = Has a mask. + // 2 = Has transparent color. + // 3 = Lasso. + UBYTE Compression; // Compression method. + // 0 = No compression. + // 1 = Byte run compression. + BYTE pad; + UWORD Transparent; // Transparent color number. + UBYTE XAspect, // Pixel aspect ratio of source art. + YAspect; + WORD PageWidth, // Source 'page' size in pixels. + PageHeight; +} BitMapHeader_Type; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE VOID cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, WORD planes); +PRIVATE VOID cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes); +PRIVATE VOID cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * ILBM_TO_MCGA -- Converts ILBM picture into MCGA format. * + * * + * This converts an ILBM picture (typical of DPaint LBM files) and * + * converts it to MCGA mode (byte per pixel). This function would be * + * used after the body of an ILBM picture is loaded. Because the * + * number of bitplanes can vary greatly, it is necessary to pass the * + * bitplane count to this function. The size (320 by 200) of the * + * source picture is presumed. * + * * + * INPUT: src - Buffer number for source ILBM picture. * + * * + * dest - Buffer number for place to put MCGA format. * + * * + * planes- The number of bitplanes in the ILBM picture. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + *=========================================================================*/ +PRIVATE VOID cdecl ILBM_To_MCGA(BufferClass& src, BufferClass& dest, WORD planes) +{ + BYTE *source; // Source pointer. + BYTE *destination; // Destination pointer. + WORD index,j,i; // Working index values. + WORD bplane; // Bit plane counter. + BYTE bytes[8]; // Byte array holding max bitplanes (8). + BYTE value; // Composed byte(pixel) value. + + source = (BYTE *) src.Get_Buffer(); + destination = (BYTE *) dest.Get_Buffer(); + + memset(bytes, '\0', 8); // Makes sure upper bits will be clear. + + // Each row is grouped and processed together. + + for (index = 0; index < 200 /*bmhd.H*/; index++) { + + // Process each line in groups of 8 bytes. + + for (j = 0; j < 40 /*(bmhd.W>>3)*/; j++) { + + // Get the bitplane bytes. + + for (bplane = 0; bplane < planes /*bmhd.BPlanes*/; bplane++) { + bytes[bplane] = *(source + (bplane * 40 /*(bmhd.W>>3)*/)); + } + source++; + + // Roll the bits out to create 8 pixels (by bytes). + for (i = 0; i < 8; i++) { + + // 8 bits per byte. + value = 0; + for (bplane = planes - 1/*bmhd.BPlanes-1*/; bplane >= 0; bplane--) { + value <<= 1; // Make room for next bit. + if (bytes[bplane] & 0x80) value |= 1; // Set the bit. + bytes[bplane] <<= 1; + } + *destination++ = value; // Output the pixel byte. + } + } + + // Advance to next scan line. + source += 40 /* (bmhd.W >> 3)*/ * (planes /* bmhd.BPlanes */ - 1); + } +} + + +/*************************************************************************** + * ILBM_TO_AMIGA -- Convert ILBM to bitplane Amiga format. * + * * + * This converts an InterLeaved BitMap picture into Amiga bitplane * + * format (8K per bitplane). The data of an ILBM picture is controlled * + * by the number of bitplanes it contains. The bitplane count is * + * passed into this program. * + * * + * INPUT: src - Buffer number for source ILBM picture data. * + * * + * dest - Buffer number for destination Amiga picture data. * + * * + * planes- The number of bitplanes in the source ILBM data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE VOID cdecl ILBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes) +{ + WORD row; // Working row counter. + WORD bp; // Working bitplane counter. + BYTE *srcptr, // Source buffer pointer. + *dstptr; // Destination buffer pointer. + + srcptr = (BYTE *) src.Get_Buffer(); // Source buffer pointer. + dstptr = (BYTE *) dest.Get_Buffer(); // Destination buffer pointer. + + for (row = 0; row < 200; row++) { + for (bp = 0; bp < planes; bp++) { + Mem_Copy(srcptr,dstptr+(8000*bp),40); + srcptr += 40; + } + dstptr += 40; + } +} +#endif + + +/*************************************************************************** + * PBM_TO_AMIGA -- Converts a PBM picture into Amiga format. * + * * + * This converts a PBM (Packed Bit Map) MCGA picture into Amiga * + * bitplane format. A PBM picture presumes 8 bitplanes, but this * + * can be controlled by the 'plane' parameter passed in. * + * * + * INPUT: src - Buffer number for the source PBM data. * + * * + * dest - Buffer number to place the Amiga format picture. * + * * + * planes- The number of bitplanes to extract from the PBM source * + * * + * OUTPUT: none * + * * + * WARNINGS: The amount of data placed into the destination buffer is * + * controlled by the number of bitplanes specified. It is * + * 8000 per bitplane. * + * * + * HISTORY: * + * 05/20/1991 JLB : Created. * + * 04/20/1994 SKB : Update to 32 bit library and make private. * + * 04/20/1994 SKB : #if out for main library. Only used in utils maybe. * + *=========================================================================*/ +#if MAKE_AMIGA_ART +PRIVATE VOID cdecl PBM_To_Amiga(BufferClass& src, BufferClass& dest, WORD planes) +{ + WORD row, // Working row counter. + col, // Working column (by byte) counter. + bit; // Working bitplane counter. + UBYTE *destptr, // Destination byte pointer. + *srcptr; // Source byte pointer. + UBYTE value; // Working input MCGA pixel number. + + + destptr = (UBYTE *) dest.Get_Buffer(); + srcptr = (UBYTE *) src.Get_Buffer(); + + memset(destptr, 0, 32000); + memset(destptr+32000, 0, 32000); + + for (row = 0; row < 200; row++) { + + for (col = 0; col < 320; col++) { + value = *srcptr++; + + for (bit = 0; bit < planes; bit++) { + if (value & (0x01 << bit)) { + destptr[(WORD)((8000L * (LONG)bit) + (col>>3))] |= 0x80 >> (col & 0x07); + } + } + } + + destptr += 40; + } +} +#endif + + +#ifdef NEVER +/*************************************************************************** + * LOAD_PICTURE -- Loads a picture file (CPS or LBM format). * + * * + * This loads a picture file into a page buffer. The loaded file will * + * be in MCGA or Amiga mode as requested. Supported source formats * + * are CPS or all forms of IFF dpaint files. * + * * + * INPUT: filename - Source filename. The only files that are * + * processed as IFF are those files that end with * + * ".LBM". * + * * + * loadbuf - Buffer type number for the temporary loading * + * buffer. It will be trashed. * + * * + * destbuf - Buffer type number for the picture to be placed. * + * * + * palette - Palette buffer pointer. If this value is NULL * + * then no palette is loaded. * + * * + * format - Desired destination format. * + * BM_AMIGA - Destination buffer will contain the * + * picture in bitplane format (Amiga). * + * The buffer will contain data equal to * + * 8K times the number of bit planes. * + * * + * BM_MCGA - Destination buffer will contain the * + * picture in MCGA format (byte per pixel).* + * The buffer will be 64K in size. * + * * + * OUTPUT: WORD number of bitplanes read into the dest buffer * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 05/20/1991 JLB : Handles Amiga and IBM destination formats. * + *=========================================================================*/ +WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette, PicturePlaneType format) +{ + int fh; // Input file handle. + long ifftype; // Iff form type. + int counter; // Count of the bytes decompressed. + int value; // Working compression code value. + int len; // WORD sized length value. + int index; // Working index values. + BitMapHeader_Type bmhd; // BMHD chunk data. + IFFForm_Type formtype; // ILBM, PBM. + char *src; // Working source body pointer. + char *dest; // Working destination body pointer. + + + //len = strlen(filename); + //strupr(filename); + + fh = Open_File(filename,READ); + if (fh == ERROR) return(FALSE); + Read_File(fh,&ifftype,4L); + Close_File(fh); + + if (ifftype != ID_FORM) + { + UBYTE * ptr = NULL ; + + if ( GraphicMode == MCGA_MODE || + ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX ) ) + ptr = palette ; + +// return((WORD)Load_Uncompress(filename, scratchbuf, destbuf, (GraphicMode == MCGA_MODE) ? palette : NULL) / 8000); + return((WORD)Load_Uncompress(filename, scratchbuf, destbuf, palette ) / 8000 ) ; + } + else + { + + fh = Open_Iff_File(filename); // Opens and checks for IFF form. + if (fh == ERROR) return(FALSE); + + Read_File(fh, &ifftype, 4L); + if (ifftype == ID_ILBM) { + formtype = FORM_ILBM; // Inter-Leaved Bit Map. + } else { + if (ifftype == ID_PBM) { + formtype = FORM_PBM; // Packed Bit Map. + } else { + return FALSE; // Not a recognizable picture file. + } + } + + // Load the BMHD chunk. + if (Read_Iff_Chunk(fh,ID_BMHD,(BYTE*)&bmhd,sizeof(BitMapHeader_Type))) { + + #if(IBM) + // Perform necessary IBM conversions to the data. + bmhd.W = Reverse_WORD(bmhd.W); + bmhd.H = Reverse_WORD(bmhd.H); + bmhd.X = Reverse_WORD(bmhd.X); + bmhd.Y = Reverse_WORD(bmhd.Y); + + // this is a mistake Xaspect and YAspect are char type + // bmhd.XAspect = Reverse_WORD(bmhd.XAspect); + // bmhd.YAspect = Reverse_WORD(bmhd.YAspect); + value = bmhd.XAspect ; + bmhd.XAspect = bmhd.YAspect ; + bmhd.YAspect = ( unsigned char ) value ; + + bmhd.PageWidth = Reverse_WORD(bmhd.PageWidth); + bmhd.PageHeight = Reverse_WORD(bmhd.PageHeight); + #endif + + if (bmhd.Masking > 2) return FALSE; // Don't allow brushes. + if (bmhd.Compression > 1) return FALSE; // Unknown compression. + + } else { + return FALSE; // Unable to read the required BMHD chunk. + } + + // Load the palette if asked. + if (palette) + { + int pbytes ; // Number of CMAP bytes required. + unsigned char color; // Palette color value. + unsigned char *paletteptr; // Allocated buffer for palette conversions. + unsigned char *source; // Scratch source CMAP data pointer. + unsigned char *dest2; // Scratch destination palette pointer. + + // Number of CMAP bytes that are needed. + pbytes = (1 << bmhd.BPlanes) * 3; + + // Allocate the temporary palette buffer. + paletteptr = (UBYTE *)Alloc(pbytes, MEM_CLEAR); + source = paletteptr; + dest2 = palette; + + // Read in only the bytes that are needed. + pbytes = (WORD)Read_Iff_Chunk(fh, ID_CMAP, (BYTE *) paletteptr, pbytes); + +#if(IBM) + if ( pbytes && + ( ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX ) || + GraphicMode == MCGA_MODE ) ) + { +#else + if (pbytes) { +#endif + + /* + ** CMAP to machine specific palette conversion code. Conversion + ** goes from CMAP three bytes per color register to the machine + ** specific form. + */ + switch(format) { + default: + case BM_MCGA: + // Convert CMAP to IBM MCGA palette form. + for (index = 0; index < pbytes; index++) { + *dest2++ = *source++ >> 2; + } + break; +#if MAKE_AMIGA_ART + + case BM_AMIGA: + // Convert CMAP to Amiga nibble packed palette form. + for (index = 0; index < pbytes; index += 3) { + *dest2++ = *(source++) >> 4; + color = (*(source++) & 0xf0); + color += *(source++) >> 4; + *dest2++ = color; + } + + break; +#endif + } + } + + Free(paletteptr); + } + + + // Load in BODY chunk. + dest = (BYTE *) scratchbuf.Get_Buffer(); + src = (BYTE *) destbuf.Get_Buffer(); + + if (Read_Iff_Chunk(fh, ID_BODY, src, destbuf.Get_Size())) + { + for (index = 0; index < (WORD)bmhd.H; index++) + { + /* Height of source */ + // Transfer (possibly uncompress) one row of data. + // PBM or ILBM reader. Bytes per row (all bitplanes). + + counter = bmhd.BPlanes * (bmhd.W >> 3); + + // If there is a mask then there is one more bitplane. + if (bmhd.Masking == 1) + counter += bmhd.W >> 3 ; + + if (bmhd.Compression == 1) + { + // The data is compressed. + // Decompress one scanline (all bitplanes) at a time. + while (counter) + { + value = ( signed char ) *src++; // Decompression code. + if (value == -128) continue; // NOOP code. + + if (value >= 0) + { + // Copy N+1 bytes. + len = ((WORD) value) + 1; + + // Ignore the masking bitplane. + if ( bmhd.Masking != 1 || + (bmhd.Masking==1 && counter > ((WORD)bmhd.W >> 3) ) ) + { + memcpy(dest, src, len); + dest += len; + } + counter -= len; + src += len; + + } + else + { + // Replicate -N+1 bytes. + len = (-((WORD) value)) + 1; + value = *src++; + + // Ignore the masking bitplane. + if (bmhd.Masking != 1 || (bmhd.Masking==1 && counter > ((WORD)bmhd.W >> 3))) + { + memset(dest,value,len); + dest += len; + } + counter -= len; + } + } + } + + else + { + // Plain data is just copied. + memcpy(dest,src,counter); + dest += counter; + src += counter; + } + } + + /* + ** Perform necessary conversions to the data in order to reach + ** the desired format. + */ + switch (format) { + default: + case BM_MCGA: // Byte per pixel desired. + if (formtype == FORM_ILBM) { + ILBM_To_MCGA(scratchbuf, destbuf, bmhd.BPlanes); + } else { + Mem_Copy(scratchbuf.Get_Buffer(), destbuf.Get_Buffer(), 64000L); + } + break; + +#if MAKE_AMIGA_ART + case BM_AMIGA: // Bitplane format desired. + if (formtype == FORM_ILBM) { + ILBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } else { + PBM_To_Amiga(scratchbuf, destbuf, bmhd.BPlanes); + } + break; +#endif + } + } + + Close_Iff_File(fh); + } + + return((WORD)bmhd.BPlanes); // Loaded the picture successfully. +} +#endif diff --git a/WWFLAT32/IFF/MAKEFILE b/WWFLAT32/IFF/MAKEFILE new file mode 100644 index 0000000..5a1f11a --- /dev/null +++ b/WWFLAT32/IFF/MAKEFILE @@ -0,0 +1,189 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = iff +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iff.obj & + load.obj & + loadpict.obj & + writelbm.obj & + lcwcomp.obj & + lcwuncmp.obj & + pack2pln.obj + + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\srcdEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/IFF/PACK2PLN.ASM b/WWFLAT32/IFF/PACK2PLN.ASM new file mode 100644 index 0000000..59ffa71 --- /dev/null +++ b/WWFLAT32/IFF/PACK2PLN.ASM @@ -0,0 +1,132 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/file/rcs/pack2pln.asm 1.1 1994/04/22 18:07:46 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : PACK2PLN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : November 20, 1991 * +;* * +;* Last Update : April 22, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Pack_2_Plane:NEAR + + CODESEG + +;*************************************************************************** +;* PACK_2_PLANE -- packed to planar scanline conversion * +;* * +;* INPUT: BYTE *buffer (far) -- pointer to planar output buffer * +;* BYTE *pageptr (far) -- pointer to current row in packed page * +;* WORD planebit -- current bit used in plane -- use only low byte * +;* * +;* OUTPUT: * +;* Return result in buffer. * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 11/20/1991 SB : Created. * +;* 04/22/1994 SKB : Converted to 32 bit library. * +;*=========================================================================* +; * +; This is the original function that is converted to asm +; +;PRIVATE VOID Pack_2_Plane(UBYTE * buffer, BYTE * pageptr, BYTE planebit) +;{ +; WORD currbit=0x80; // current bit to be written to +; WORD pixel; // current pixel in row used as a counter; +; +; buffer--; // will be incremented at the start +; for (currbit = 0, pixel = 0; pixel < 320; pixel++) { +; if (!currbit) { +; currbit = 0x80; // reset bit 7 +; buffer++; // go to next byte in buffer +; *buffer = 0; // clear byte so we only need to set bits needed +; } +; if (planebit & *pageptr++) +; *buffer |= currbit; // set bit in destination if plane was set is source +; +; currbit >>= 1; // shift destination bit one right +; } +;} + +PROC Pack_2_Plane C NEAR + USES ebx,ecx,esi,edi + ARG buffer:DWORD + ARG page:DWORD + ARG planebit:WORD + + + mov edi,[buffer] + mov esi,[page] + + mov ax,[planebit] ; move bit set for current plane (planebit) to ax + ; the low byte will only be used + + mov ecx,320d ; set counter to 320 columns (320x200 picture) + mov ah,80h ; set bit 7 of current_bit + dec edi ; this will get incremented at the start + +??top_loop: ; while (columns left) + cmp ah,80h ; if current_bit is bit 7 + jnz short ??same_dest + ; Then + inc edi ; buffer++ increment pointer + mov [BYTE PTR edi],0 ; *buffer = 0 + +??same_dest: ; EndIf + mov bl,al + and bl,[esi] ; if (planebit & *pageptr) + jz short ??no_set_bit + + or [BYTE PTR edi],ah ; Then *buffer |= current_bit + +??no_set_bit: + inc esi ; pageptr++ goto next in source byte + ror ah,1 ; rotate current_bit right one + dec ecx ; + jnz ??top_loop + + ret + + ENDP Pack_2_Plane + + END + \ No newline at end of file diff --git a/WWFLAT32/IFF/WRITELBM.CPP b/WWFLAT32/IFF/WRITELBM.CPP new file mode 100644 index 0000000..c1fb8b2 --- /dev/null +++ b/WWFLAT32/IFF/WRITELBM.CPP @@ -0,0 +1,422 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/file/rcs/writelbm.cpp 1.1 1994/04/20 14:38:57 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : Write LBM File * + * * + * File Name : writelbm.c * + * * + * Programmer : Scott Bowen * + * * + * Start Date : November 18, 1991 * + * * + * Last Update : November 19, 1991 [SB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Line -- convert one plane of one row to a packed plane * + * Write_BMHD -- writes out the bit map header (LocalHeader) * + * Write_Body -- writes out compressed data in an LBM file * + * Write_CMAP -- Writes out CMAP (palette) information * + * Write_LBM_File -- Writes out a file in LBM format * + * Write_Row -- compresses and writes a row plane to .lbm file * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +// At the end of this file there is an IFF definition for a .LBM file. + +#include +#include "iff.h" +#include "file.h" +#include +#include + + + // A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size +typedef struct { + UWORD w, h; // raster width & height in pixels + UWORD x, y; // position for this image + UBYTE planes; // # source bitplanes + UBYTE masking; // masking technique + UBYTE compression; // compression algoithm + UBYTE pad1; // UNUSED. For consistency, put 0 here. + UWORD transcolor; // transparent "color number" + UBYTE xaspect, yaspect; // aspect ratio, a rational number x/y + UWORD pagewidth, pageheight; // source "page" size in pixels +} BitMapHeaderType; + + +// All values in LocalHeader are always the same except planes. This is set in Write_BMHD +// the WORD values must be in low-high order for compatibility. + +PRIVATE BitMapHeaderType LocalHeader = { + 0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask + 1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect + 0x4001, 0xC800 }; // pagewidth, pageheight + + + // Used to verify that the write of the header was valid +#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L) + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE LONG Write_BMHD(WORD lbmhandle, WORD bitplanes); +PRIVATE LONG Write_CMAP(WORD lbmhandle, UBYTE * palette, WORD bitplanes); +PRIVATE LONG Write_BODY(WORD lbmhandle, BufferClass& buff, WORD bitplanes); +PRIVATE LONG Write_Row(WORD lbmhandle, UBYTE *buffer); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * WRITE_LBM_FILE -- Writes out a file in LBM format * + * * + * INPUT: WORD lbmhandle -- lbm file handle already opened by caller * + * BufferClass buff -- buff where MCGA picture is * + * WORD bitplane -- number of bitplanes to convert to * + * BYTE *palette -- pointer to palette for buff * + * * + * OUTPUT: Returns BOOL -- successfull or not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/18/1991 SB : Created. * + *=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette) +{ + LONG filesize; + + + Seek_File(lbmhandle, 0L, SEEK_SET); // goto beginning of file + + Write_File(lbmhandle, "FORM????ILBM", 12L); // First 12 bytes of all .lbm files + // size is unkown so write ???? + filesize = 12L; // 4 bytes for "ILBM" + + filesize += Write_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size) + filesize += Write_CMAP(lbmhandle, palette, bitplanes); // write out CMAP + + // Write out the body, or compressed picture image. This size will depend + // on the compression, but the value passed back is what the compressor + // assumed was written to file + + filesize += Write_BODY(lbmhandle, buff, bitplanes); + + // Verify that we were able to write out the file without running out of space + if (Seek_File(lbmhandle, 0L, SEEK_END) != filesize) { + return(FALSE); + } + + Seek_File(lbmhandle, 4L, SEEK_SET); // goto beginning of file + filesize = Reverse_LONG(filesize - 8L); // - 8 because of "FORM" + WORD (size) + Write_File(lbmhandle, (BYTE *) &filesize, 4L); // patch in filesize + + return(TRUE); +} + + +/*************************************************************************** + * WRITE_BMHD -- writes out the bit map header (LocalHeader) * + * * + * INPUT: WORD lbmhandle -- file handle for lbm file * + * WORD pitplanes -- number of bitplanes to write out * + * * + * OUTPUT: LONG number of bytes hopefully written out to .LBM file * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +PRIVATE LONG Write_BMHD(WORD lbmhandle, WORD bitplanes) +{ + LONG size; + + Write_File(lbmhandle, "BMHD", 4L); // write out chunk title + size = Reverse_LONG(sizeof(LocalHeader)); // write out size of LocalHeader chunk + Write_File(lbmhandle, (BYTE *) &size, 4L); + + LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader + + // Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure + + return(Write_File(lbmhandle, (BYTE *) &LocalHeader, + (sizeof(LocalHeader) + 1) & 0xFFFE) + 8L); +} + + +/*************************************************************************** + * WRITE_CMAP -- Writes out CMAP (palette) information * + * * + * * + * INPUT: WORD lbmhandle -- file handle of lbm file * + * BYTE * palette -- pointer to paletter information * + * WORD bitplanes -- used to figure out size of palette * + * * + * OUTPUT: LONG number of bytes that should have been written out to .LBM. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE LONG Write_CMAP(WORD lbmhandle, UBYTE * palette, WORD bitplanes) +{ + WORD color, r, g, b, colors; + LONG size; + UBYTE *pal_ptr; + BYTE rgb[3]; + + + Write_File(lbmhandle, "CMAP", 4L); // write out palette info + colors = 1 << bitplanes; // colors = 2 to the bitplanes + size = Reverse_LONG(colors * 3L); // size = colors * 3 guns + + Write_File(lbmhandle, (BYTE *) &size, 4L); + + for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color + + if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values + r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM + } // so LBM_val = (DP_val * 4) | 3 if DP_val != 0 + if ((g = *pal_ptr++) != 0) { + g = (g << 2) | 0x03; + } + if ((b = *pal_ptr++) != 0) { + b = (b << 2) | 0x03; + } + rgb[0] = r; // assign gun values to an array to write out + rgb[1] = g; + rgb[2] = b; + + Write_File(lbmhandle, rgb, 3L); + } + // size = colors * 3 + return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + WORD (size) +} + + +/*************************************************************************** + * WRITE_BODY -- writes out compressed data in an LBM file * + * * + * INPUT: WORD lbmhandle -- file handle of lbm file * + * * + * OUTPUT: LONG - number of byte written * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ + +PRIVATE LONG Write_BODY(WORD lbmhandle, BufferClass& buff, WORD bitplanes) +{ + LONG bodysize = 0; + LONG actualsize; + LONG size; + WORD planebit; + WORD line, plane; + UBYTE buffer[40]; + UBYTE *buffptr; + + Write_File(lbmhandle, "BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize + + buffptr = (UBYTE *) buff.Get_Buffer(); // point to beginning of buff + + for (line = 0; line < 200; line++) { + planebit = 1; // start with bit 1 set + + for (plane = 0; plane < bitplanes; plane++) { + Pack_2_Plane(buffer, buffptr, planebit); // convert to planar + bodysize += Write_Row(lbmhandle, buffer); // write to to the BODY in the LBM + + planebit <<= 1; // set next bit + } + + buffptr += 320; // row size is 320 + } + + actualsize = bodysize + (bodysize&0x01); + + if (actualsize != bodysize) { + Write_File(lbmhandle, buffer, 1); // Padd the block. + } + + Seek_File(lbmhandle, -(actualsize + 4L), SEEK_CUR); // Patch in chunksize + size = Reverse_LONG(bodysize); + Write_File(lbmhandle, (BYTE *) &size ,4L); + + return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes +} + +/*************************************************************************** + * WRITE_ROW -- compresses and writes a row plane to .lbm file * + * * + * INPUT: WORD lbmhandle -- lbm file handle * + * UBYTE *buffer -- pointer to buffer to be written out * + * * + * OUTPUT: LONG size of chunk that should have been written out * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +// this algorithm was taken from WILBM.c written by EA that was in the +// 1985 yearbook. This is the compression method that DP.EXE uses. +// Change only if DP.EXE changes. + +PRIVATE LONG Write_Row(WORD lbmhandle, UBYTE *buffer) +{ + WORD i; + WORD chunksize = 0; + WORD dataLength = 40; // 320 rows / 8 ( 1 plane per row) + UBYTE repCode, current, curr_plus_2; + UBYTE *buffptr; + + while (dataLength) { + + // If at least 2 more bytes and they are equal, then replicate + + if ((dataLength >= 2) && (buffer[0] == buffer[1])) { + buffptr = buffer; + for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) { + if (*buffptr != buffptr[1]) { + break; + } + buffptr++; + } + i++; + repCode = -i + 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as -count+1 + Write_File(lbmhandle, buffer, 1L); // Write byte to replicate + buffer += i; + dataLength -= i; + chunksize += 2; + + } + else { // Copy literally till 3 byte run or two 2 byte runs found + + for (i = 0; (i <= 128) && (i < dataLength); i++) { + current = buffer[i]; + curr_plus_2 = buffer[i + 2]; + + if (i == dataLength - 1) + continue; + if (current != buffer[i + 1]) + continue; + if (i == dataLength - 2) + continue; + if (current == curr_plus_2) + break; + if (i == dataLength - 3) + continue; + if (curr_plus_2 == buffer[i + 3]) + break; + } + repCode = i - 1; + Write_File(lbmhandle, &repCode, 1L); // Write count as count-1 + Write_File(lbmhandle, buffer, (LONG) i); // Write 'count' bytes + buffer += i; + dataLength -= i; + chunksize += i + 1; + } + } // end while + + return(chunksize); +} + + +#if(FALSE) + +This is a definition of a DPII .LBM file. +Below this definition are differences in DPIIe .LMB files. + +Created by : Scott K. Bowen Nov 18, 1991 + +Start with .LBM to read definition : + +.LBM -> "FORM" + FILESIZE + "ILMB" + CHUNKS + +BITPLANES -> (word) // number of bit planes used +BLUE -> (byte) // blue color gun value +BMHD -> "BMHD" + CHUNKSIZE + CONTEXT +BODY -> + +CHUNKS -> BMHD | BODY | CMAP | DPPV | CRNG | ???? +CHUNKSIZE -> (long) // size of chunk not including header or size. +CMAP -> "CMAP" + CHUNKSIZE + PALETTE_INFO +COMPRESS_METHOD -> (byte) // compression method used +CONTEXT -> WIDTH + HEIGHT + POSX + POSY + #_BITPLANES + MASKING + + COMPRESS_METHOD + PAD + TRANS_COL + XASPECT + YASPECT + + PAGEWIDTH + PAGEHEIGHT +CRNG -> // we do not use + +DPPV -> // we do not use + +FILESIZE -> (long) //size of file minus (sizeof(FORM) + sizeof(FILESIZE) + +GREEN -> (byte) // green color gun value + +HEIGHT -> (word) // of picture + +MASKING -> (byte) // masking type ? + +NUM_COLORS -> //number of colors used depending on format + +PAGE_WIDTH -> (word) // width of page +PAGE_HEIGHT -> (word) // height of page +PALETTE_INFO-> (RED+GREEN+BLUE) @ NUM_COLORS +PAD -> (byte) // not used. used as a padding +POSX -> (word) // staring position +POSY -> (word) // staring position + +RED -> (byte) // red color gun value + +TRANS_COL -> (word) // transparrent color + +WIDTH -> (word) // of picture + +XASPECT -> (byte) // x aspect ratio + +YASPECT -> (byte) // y aspect ratio + +???? -> // other possibilities + + +Differences in DPII enhance version + +.LBM -> "FORM" + FILESIZE + "PBM " + CHUNKS +DPPV -> DPPS // uses dpps instead of dppv +CHUNKS -> + TINY // add these to old definition + + + + +#endif + diff --git a/WWFLAT32/IFF/WRITEPCX.CPP b/WWFLAT32/IFF/WRITEPCX.CPP new file mode 100644 index 0000000..2db6397 --- /dev/null +++ b/WWFLAT32/IFF/WRITEPCX.CPP @@ -0,0 +1,175 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +static void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette ) +{ + unsigned i ; + unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + for ( i = 0 ; i < header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, ptr + i *VP_Scan_Line ) ; + + pal = ( RGB * ) palette ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palette , 256 * sizeof (RGB) ) ; + + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + +void Write_Pcx_ScanLine ( int file_handle , int scansize , char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + char * file_ptr ; + char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + for ( i = 1 ; i < scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle + 192 ) ; + WRITE_CHAR ( last ) ; + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle + 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} + + diff --git a/WWFLAT32/INCLUDE/AUDIO.H b/WWFLAT32/INCLUDE/AUDIO.H new file mode 100644 index 0000000..bef70ab --- /dev/null +++ b/WWFLAT32/INCLUDE/AUDIO.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +#pragma pack(1); +typedef struct { + short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + +typedef enum RateEnum { + PLAYBACK_RATE_SLOW =11025, + PLAYBACK_RATE_NORMAL =(11025 * 2), + PLAYBACK_RATE_FAST =(11025 * 4), +} RateType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void cdecl _saveregs Sound_Callback(void); +void cdecl far __saveregs __loadds maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Sound_Init(int sfx, int score, int sample, RateType rate, int bits_per_sample, int max_samples, int reverse_channels); +BOOL Audio_Init(int sample, int address, int inter, int dma, RateType rate, int bits_per_sample, int max_samples, int reverse_channels = FALSE); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; +extern short StreamLowImpact; diff --git a/WWFLAT32/INCLUDE/BUFFER.H b/WWFLAT32/INCLUDE/BUFFER.H new file mode 100644 index 0000000..bc3c188 --- /dev/null +++ b/WWFLAT32/INCLUDE/BUFFER.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class VideoViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + long To_Page(VideoViewPortClass &view); + long To_Page(int w, int h, VideoViewPortClass &view); + long To_Page(int x, int y, int w, int h, VideoViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif + diff --git a/WWFLAT32/INCLUDE/DESCMGMT.H b/WWFLAT32/INCLUDE/DESCMGMT.H new file mode 100644 index 0000000..83f34ca --- /dev/null +++ b/WWFLAT32/INCLUDE/DESCMGMT.H @@ -0,0 +1,93 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/WWFLAT32/INCLUDE/DIFFTB.INC b/WWFLAT32/INCLUDE/DIFFTB.INC new file mode 100644 index 0000000..16b8a2d --- /dev/null +++ b/WWFLAT32/INCLUDE/DIFFTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/WWFLAT32/INCLUDE/DIPTHONG.H b/WWFLAT32/INCLUDE/DIPTHONG.H new file mode 100644 index 0000000..2f5ed70 --- /dev/null +++ b/WWFLAT32/INCLUDE/DIPTHONG.H @@ -0,0 +1,24 @@ +/* +** 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 . +*/ + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/WWFLAT32/INCLUDE/FILE.H b/WWFLAT32/INCLUDE/FILE.H new file mode 100644 index 0000000..eded220 --- /dev/null +++ b/WWFLAT32/INCLUDE/FILE.H @@ -0,0 +1,261 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + + +#ifndef ERROR +#define ERROR -1 +#endif + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern short Find_First(unsigned char *fname, unsigned short mode, struct find_t *ffblk); +extern short Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif // FILE_H + diff --git a/WWFLAT32/INCLUDE/FILEPCX.H b/WWFLAT32/INCLUDE/FILEPCX.H new file mode 100644 index 0000000..f9650cd --- /dev/null +++ b/WWFLAT32/INCLUDE/FILEPCX.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,BYTE* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic ,BYTE* palette ) ; + +#endif diff --git a/WWFLAT32/INCLUDE/FILETEMP.H b/WWFLAT32/INCLUDE/FILETEMP.H new file mode 100644 index 0000000..210f85a --- /dev/null +++ b/WWFLAT32/INCLUDE/FILETEMP.H @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD cdecl (*cdecl IO_Error)(FileErrorType error, BYTE const *filename); +VOID cdecl Prog_End(VOID); +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H diff --git a/WWFLAT32/INCLUDE/FONT.BAK b/WWFLAT32/INCLUDE/FONT.BAK new file mode 100644 index 0000000..7999c1d --- /dev/null +++ b/WWFLAT32/INCLUDE/FONT.BAK @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef VBUFFER_H +#include +#endif + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Set_Font(VOID const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +WORD cdecl Char_Pixel_Width(BYTE chr); +UWORD cdecl String_Pixel_Width(BYTE const *string); +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y); +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Load_Font(BYTE const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID Set_Font_Palette_Range(VOID const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern BYTE FontWidth ; +extern BYTE FontHeight; +extern BYTE *FontWidthBlockPtr; + + +extern "C" VOID const *FontPtr; + + + + +#endif // FONT_H diff --git a/WWFLAT32/INCLUDE/FONT.H b/WWFLAT32/INCLUDE/FONT.H new file mode 100644 index 0000000..705b056 --- /dev/null +++ b/WWFLAT32/INCLUDE/FONT.H @@ -0,0 +1,122 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef VBUFFER_H +#include +#endif + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Set_Font(VOID const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +WORD cdecl Char_Pixel_Width(BYTE chr); +UWORD cdecl String_Pixel_Width(BYTE const *string); +VOID cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, ULONG offset, INT *x, INT *y); +VOID cdecl Get_Next_Text_Print_XY(VideoViewPortClass& vp, ULONG offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +VOID *cdecl Load_Font(BYTE const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID Set_Font_Palette_Range(VOID const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern BYTE FontWidth ; +extern BYTE FontHeight; +extern BYTE *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/WWFLAT32/INCLUDE/FUNCTION.H b/WWFLAT32/INCLUDE/FUNCTION.H new file mode 100644 index 0000000..7edf5f2 --- /dev/null +++ b/WWFLAT32/INCLUDE/FUNCTION.H @@ -0,0 +1,47 @@ +/* +** 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 . +*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int cdecl File_Stream_Sample(char const *filename); +int cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void cdecl _saveregs _loadds Sound_Callback(void); +void cdecl far _saveregs _loadds maintenance_callback(void); +void *cdecl Load_Sample(char const *filename); +long cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long cdecl Sample_Read(int fh, void *buffer, long size); +void cdecl Free_Sample(void const *sample); +BOOL cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void cdecl Sound_End(void); +void cdecl Stop_Sample(int handle); +BOOL cdecl Sample_Status(int handle); +BOOL cdecl Is_Sample_Playing(void const * sample); +void cdecl Stop_Sample_Playing(void const * sample); +int cdecl Play_Sample(void const *sample); +int cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int cdecl Set_Sound_Vol(int volume); +int cdecl Set_Score_Vol(int volume); +void cdecl Fade_Sample(int handle, int ticks); diff --git a/WWFLAT32/INCLUDE/GBUFFER.BAK b/WWFLAT32/INCLUDE/GBUFFER.BAK new file mode 100644 index 0000000..7449b24 --- /dev/null +++ b/WWFLAT32/INCLUDE/GBUFFER.BAK @@ -0,0 +1,892 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "window.h" + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MCGAPRIM_H +#include "mcgaprim.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#include + + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr); +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + int Lock(void) const {return(TRUE);} + void Unlock(void) const {} + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + VOID Grey_Out_Region(int x, int y, int width, int height, int color); + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + GraphicBufferClass *GraphicBuff; // related graphic buff +}; + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + public: + GraphicBufferClass( long size = 64500, int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT, + VOID *buffer = 0); + GraphicBufferClass(int w, int h, void *buffer = 0); + ~GraphicBufferClass(); + +// void Scale_Rotate(BitmapClass &bmp,const TPoint2D &pt,long scale=0x0100,unsigned char angle=0); +}; + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return MCGA_Size_Of_Region(this, w, h); +} + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + MCGA_Put_Pixel(this, x, y, color); +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + return(MCGA_Get_Pixel(this, x, y)); +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + MCGA_Clear(this, color); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + return(MCGA_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(MCGA_Print(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + return(MCGA_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + MCGA_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window) +{ + MCGA_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); +} + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Draw_Line(this, sx, sy, dx, dy, color); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Fill_Rect(this, sx, sy, dx, dy, color); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + MCGA_Remap(this, sx, sy, width, height, remap); +} +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + MCGA_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + MCGA_Remap(this, 0, 0, Width, Height, remap); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + + +#endif diff --git a/WWFLAT32/INCLUDE/GBUFFER.H b/WWFLAT32/INCLUDE/GBUFFER.H new file mode 100644 index 0000000..1300059 --- /dev/null +++ b/WWFLAT32/INCLUDE/GBUFFER.H @@ -0,0 +1,892 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "window.h" + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MCGAPRIM_H +#include "mcgaprim.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#include + + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr); +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + int Lock(void) const {return(TRUE);} + void Unlock(void) const {} + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + VOID Grey_Out_Region(int x, int y, int width, int height, int color); + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + GraphicBufferClass *GraphicBuff; // related graphic buff +}; + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + public: + GraphicBufferClass( long size = 64500, int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT, + VOID *buffer = 0); + GraphicBufferClass(int w, int h, void *buffer = 0); + ~GraphicBufferClass(); + + void Scale_Rotate(BitmapClass &bmp,const TPoint2D &pt,long scale=0x0100,unsigned char angle=0); +}; + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return MCGA_Size_Of_Region(this, w, h); +} + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + MCGA_Put_Pixel(this, x, y, color); +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + return(MCGA_Get_Pixel(this, x, y)); +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + MCGA_Clear(this, color); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + return(MCGA_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(MCGA_Print(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + return(MCGA_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + MCGA_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window) +{ + MCGA_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); +} + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Draw_Line(this, sx, sy, dx, dy, color); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Fill_Rect(this, sx, sy, dx, dy, color); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + MCGA_Remap(this, sx, sy, width, height, remap); +} +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + MCGA_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + MCGA_Remap(this, 0, 0, Width, Height, remap); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + + +#endif diff --git a/WWFLAT32/INCLUDE/GBUFFER.INC b/WWFLAT32/INCLUDE/GBUFFER.INC new file mode 100644 index 0000000..457af5e --- /dev/null +++ b/WWFLAT32/INCLUDE/GBUFFER.INC @@ -0,0 +1,60 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + +STRUC VideoViewPort +VIVPOffset DD ? ; offset to virtual viewport +VIVPWidth DD ? ; width of virtual viewport +VIVPHeight DD ? ; height of virtual viewport +VIVPXAdd DD ? ; x mod to get to next line +VIVPXPos DD ? ; x pos relative to Graphic Buff +VIVPYPos DD ? ; y pos relative to Graphic Buff +VIVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS diff --git a/WWFLAT32/INCLUDE/IFF.H b/WWFLAT32/INCLUDE/IFF.H new file mode 100644 index 0000000..11b87af --- /dev/null +++ b/WWFLAT32/INCLUDE/IFF.H @@ -0,0 +1,168 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#ifndef GBUFFER_H +#include +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((LONG) ((LONG) d << 24) | ((LONG) c << 16) | ((LONG) b << 8) | (LONG)(a)) +#define IFFize_WORD(a) Reverse_WORD(a) +#define IFFize_LONG(a) Reverse_LONG(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + BYTE Method; // Compression method (CompressionType). + BYTE pad; // Reserved pad byte (always 0). + LONG Size; // Size of the uncompressed data. + WORD Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +WORD cdecl Open_Iff_File(BYTE const *filename); +VOID cdecl Close_Iff_File(WORD fh); +ULONG cdecl Get_Iff_Chunk_Size(WORD fh, LONG id); +ULONG cdecl Read_Iff_Chunk(WORD fh, LONG id, VOID *buffer, ULONG maxsize); +VOID cdecl Write_Iff_Chunk(WORD file, LONG id, VOID *buffer, LONG length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +//WORD cdecl Load_Picture(BYTE const *filename, BufferClass& scratchbuf, BufferClass& destbuf, UBYTE *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +ULONG cdecl Load_Data(BYTE const *name, VOID *ptr, ULONG size); +ULONG cdecl Write_Data(BYTE const *name, VOID *ptr, ULONG size); +VOID * cdecl Load_Alloc_Data(BYTE const *name, MemoryFlagType flags); +//ULONG cdecl Load_Uncompress(BYTE const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, VOID *reserved_data=NULL); +ULONG cdecl Uncompress_Data(VOID const *src, VOID *dst); +VOID cdecl Set_Uncomp_Buffer(WORD buffer_segment, UWORD size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(WORD lbmhandle, BufferClass& buff, WORD bitplanes, UBYTE *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern VOID Pack_2_Plane(VOID *buffer, VOID * pageptr, WORD planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Compress(VOID *source, VOID *dest, ULONG length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern ULONG LCW_Uncompress(VOID *source, VOID *dest, ULONG length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H + diff --git a/WWFLAT32/INCLUDE/INDEXTB.INC b/WWFLAT32/INCLUDE/INDEXTB.INC new file mode 100644 index 0000000..f9d2fba --- /dev/null +++ b/WWFLAT32/INCLUDE/INDEXTB.INC @@ -0,0 +1,1448 @@ +; +; 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 . +; + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/WWFLAT32/INCLUDE/KEYBOARD.H b/WWFLAT32/INCLUDE/KEYBOARD.H new file mode 100644 index 0000000..7f06b37 --- /dev/null +++ b/WWFLAT32/INCLUDE/KEYBOARD.H @@ -0,0 +1,447 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : July 13, 1994 * + * * + * Last Update : July 13, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +extern "C" { + void Install_Keyboard_Interrupt(void *addr, long size); + void Install_Page_Fault_Handle(void) ; + void *Get_RM_Keyboard_Address(void); + long Get_RM_Keyboard_Size(void); + void Remove_Keyboard_Interrupt(void); + int Check_Key_Num(void); + int Check_Key_Bits(void); + int Check_Key(void); + short Get_Key_Num(void); + short Get_Key_Bits(void); + int Get_Key(void); + int KN_To_KA(int keynum); + int Keyboard_Attributes_On(int key_flags); + int Keyboard_Attributes_Off(int key_flags); + void Clear_KeyBuffer(void); + int Key_Down(int key); + int Key_Satisfied(int key); + void Stuff_Key_WORD(int code); + void Stuff_Key_Num(int key); + int Install_Mouse(int max_width, int max_height, int scr_width, int scr_height); + void Reset_Mouse (void) ; + void Remove_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + int Get_Mouse_Disabled(void); + void *Set_Mouse_Cursor(int xhotspot, int yhotspot, void *cursor); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int sx_pixel, int sy_pixel, int dx_pixel, int dy_pixel); + void Conditional_Show_Mouse(void); + + + void __interrupt far Keyboard_Interrupt(void); + extern int MouseQX; + extern int MouseQY; + +} + + +enum { + REPEATON = 0x0001, /* 1:all makes into buffer, 0:only 1st make */ + TRACKEXT = 0x0002, /* 1:Home != keypad Home, 0:Home=keypad Home */ + FILTERONLY = 0x0004, /* 1:Normal BIOS operation with filter */ + CTRLALTTURBO = 0x0010, /* 1:Allow turbo up and down in application */ + NONUMLOCK = 0x0200, /* 1:do NOT remap keypad to numbers */ + TASKSWITCHABLE = 0x400, /* 1:allows task switching keys thru ALT-TAB, */ + /* ALT-ESC,CTRL-ESC */ + PASSBREAKS = 0x0800, // Pass all breaks to the keyboard buffer. + + /* The following flags, if turned on, should only be used for + debugging purposes (remember to take out the calls when BETA */ + + CTRLSON = 0x0008, /* 1:pass scroll lock sequence into BIOS */ + CTRLCON = 0x0020, /* 1:pass stop code to BIOS */ + SCROLLLOCKON = 0x0040, /* 1:pass scroll lock key into BIOS */ + PAUSEON = 0x0080, /* 1:pass the pause key and seq to BIOS */ + /* make sure FILTERONLY is set */ + BREAKON = 0x0100, /* 1:pass the ctrl break seq to BIOS */ + KEYMOUSE = 0x1000, /* 1:keypad translates into mouse moves */ + SIMLBUTTON = 0x2000, /* 1:have space and enter keys simulate Left */ + /* mouse button when KEYMOUSE is set */ + DEBUGINT = 0x4000 /* Use scroll lock to disable keyboard int */ +}; + + +/* +** These are the codes for the various key codes that are returned from the +** keyboard input routines Get_Key() and Input_ASCII(). +*/ +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KeyASCIIType; + +/* +** These are the keyboard codes that are returned from the input routines +** Get_Key_Num and Input_Num. +*/ +typedef enum { + KN_NONE = 0, + KN_GRAVE = 1, /* ` */ + KN_1, KN_2, KN_3, KN_4, KN_5, KN_6, KN_7, KN_8, KN_9, KN_0, + KN_MINUS, /* - */ + KN_EQUAL, /* = */ + + KN_RESERVED1, + + KN_BACKSPACE, /* */ + + KN_TAB, /* */ + KN_Q, KN_W, KN_E, KN_R, KN_T, KN_Y, KN_U, KN_I, KN_O, KN_P, + KN_LBRACKET, /* [ */ + KN_RBRACKET, /* ] */ + KN_BACKSLASH, /* \ */ + + KN_CAPSLOCK, /* */ + KN_A, KN_S, KN_D, KN_F, KN_G, KN_H, KN_J, KN_K, KN_L, + KN_SEMICOLON, /* ; */ + KN_SQUOTE, /* ' */ + KN_BACKSLASH2, + KN_RETURN, /* or */ + + KN_LSHIFT, /* */ + + KN_MOUSE_MOVE, // Indicate a mouse move (for playback of + + KN_Z, KN_X, KN_C, KN_V, KN_B, KN_N, KN_M, + KN_COMMA, /* , */ + KN_PERIOD, /* . */ + KN_SLASH, /* / */ + + KN_RESERVED3, + + KN_RSHIFT, /* */ + + KN_LCTRL, /* */ + KN_LCOMM, /* for AMIGA */ + KN_LALT, /* */ + KN_SPACE, /* */ + KN_RALT, /* */ + KN_RCOMM, /* for AMIGA */ + KN_RCTRL, /* */ + /* the following are forced into key buffer */ + KN_LMOUSE, + KN_RMOUSE, + KN_JBUTTON1, + KN_JBUTTON2, + KN_J_UP, + KN_J_RIGHT, + KN_J_DOWN, + KN_J_LEFT, + + KN_SPECIAL9, + + KN_SPECIAL10, + + KN_E_INSERT, /* extended */ + KN_E_DELETE, /* extended */ + + KN_RESERVED4, + KN_RESERVED5, + + KN_E_LEFT, /* extended */ + KN_E_HOME, /* extended */ + KN_E_END, /* extended */ + + KN_RESERVED6, + + KN_E_UP, /* extended */ + KN_E_DOWN, /* extended */ + KN_E_PGUP, /* extended */ + KN_E_PGDN, /* extended */ + KN_K_LPAREN, /* for AMIGA */ + KN_K_RPAREN, /* for AMIGA */ + KN_E_RIGHT, /* extended */ + + KN_NUMLOCK, /* */ + KN_HOME, /* num key pad 7 */ + KN_UPLEFT = KN_HOME, + KN_LEFT, /* num key pad 4 */ + KN_END, /* num key pad 1 */ + KN_DOWNLEFT = KN_END, + + KN_RESERVED7, + + KN_KEYPAD_SLASH, /* num key pad / */ + KN_UP, /* num key pad 8 */ + KN_CENTER, /* num key pad 5 */ + KN_DOWN, /* num key pad 2 */ + KN_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK, /* num key pad * */ + KN_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KN_PGUP, + KN_RIGHT, /* num key pad 6 */ + KN_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KN_PGDN, + KN_DELETE, /* num key pad . */ + KN_KEYPAD_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS, /* num key pad + */ + + KN_RESERVED8, + + KN_KEYPAD_RETURN, /* num key pad */ + + KN_RESERVED9, + + KN_ESC, + KN_HELP, /* for AMIGA */ + KN_F1, KN_F2, KN_F3, KN_F4, KN_F5, KN_F6, KN_F7, KN_F8, KN_F9, KN_F10, + KN_F11, + KN_F12, + KN_PRNTSCRN, /* */ + KN_SCROLLLOCK, /* */ + KN_PAUSE, /* */ + + KN_SHIFT_BIT = 0x0100, + KN_CTRL_BIT = 0x0200, + KN_ALT_BIT = 0x0400, + KN_RLSE_BIT = 0x0800, + KN_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KN_RCOMM_BIT = 0x2000, /* Amiga Right Comm key */ + KN_BUTTON = 0x8000 /* Amiga Right Comm key */ +} KeyNumType; + +#endif diff --git a/WWFLAT32/INCLUDE/KEYBOARD.INC b/WWFLAT32/INCLUDE/KEYBOARD.INC new file mode 100644 index 0000000..94ed506 --- /dev/null +++ b/WWFLAT32/INCLUDE/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WWFLAT32/INCLUDE/KEYSTRUC.INC b/WWFLAT32/INCLUDE/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WWFLAT32/INCLUDE/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WWFLAT32/INCLUDE/MCGAPRIM.H b/WWFLAT32/INCLUDE/MCGAPRIM.H new file mode 100644 index 0000000..7e924f2 --- /dev/null +++ b/WWFLAT32/INCLUDE/MCGAPRIM.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + + + +#ifndef MCGAPRIM_H +#define MCGAPRIM_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + extern long MCGA_Size_Of_Region(void *thisptr, int w, int h); + + extern void MCGA_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int MCGA_Get_Pixel(void * thisptr, int x, int y); + extern void MCGA_Clear(void *thisptr, unsigned char color); + extern long MCGA_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long MCGA_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + extern BOOL Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + extern void Vesa_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int Vesa_Get_Pixel(void * thisptr, int x, int y); + extern void Vesa_Clear(void *thisptr, unsigned char color); + extern long Vesa_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long Vesa_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + + extern BOOL Linear_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern LONG MCGA_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + extern LONG Vesa_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + extern VOID MCGA_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + extern VOID MCGA_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + extern void MCGA_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + extern void MCGA_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int min_x, int min_y, int max_x, int max_y); + + extern void Shadow_Blit(long int xpix, long int ypix, long int width, long int height, GraphicViewPortClass &src, VideoBufferClass &dst, void *shadowbuff); + + extern void *Get_Font_Palette_Ptr(void); +// extern int Get_Standard_Selector(VOID); +// extern VOID Set_Selector(UWORD sel); +#ifdef __cplusplus +} +#endif + +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern void (*VVPC_Clear_Func)(void *, unsigned char); +extern long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +extern void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +extern int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +extern long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *buffer, void *view); +extern BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern LONG (*VVPC_Print_Func)(void *, const char *, int, int, int, int); +extern GraphicBufferClass *LogicPage; +#endif diff --git a/WWFLAT32/INCLUDE/MCGAPRIM.INC b/WWFLAT32/INCLUDE/MCGAPRIM.INC new file mode 100644 index 0000000..87a0ec3 --- /dev/null +++ b/WWFLAT32/INCLUDE/MCGAPRIM.INC @@ -0,0 +1,124 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR +GLOBAL C Get_Font_Palette_Ptr :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR +GLOBAL MCGA_Draw_Stamp_Clip :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + diff --git a/WWFLAT32/INCLUDE/MEMFLAG.H b/WWFLAT32/INCLUDE/MEMFLAG.H new file mode 100644 index 0000000..30651d4 --- /dev/null +++ b/WWFLAT32/INCLUDE/MEMFLAG.H @@ -0,0 +1,98 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/WWFLAT32/INCLUDE/MISC.BAK b/WWFLAT32/INCLUDE/MISC.BAK new file mode 100644 index 0000000..e75d494 --- /dev/null +++ b/WWFLAT32/INCLUDE/MISC.BAK @@ -0,0 +1,178 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID cdecl Prog_End(VOID); +VOID cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PROC.ASM */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +int Get_CPU(VOID); + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +UBYTE Random(VOID); +int Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +LONG Reverse_LONG(LONG number); +WORD Reverse_WORD(WORD number); +LONG Swap_LONG(LONG number); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +//int Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +//int Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ + +void *Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); + +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD Processor(VOID); +extern WORD Operating_System(VOID); +extern ULONG random ( ULONG mod ) ; +extern void randomize ( void ) ; + +extern int Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + + diff --git a/WWFLAT32/INCLUDE/MISC.H b/WWFLAT32/INCLUDE/MISC.H new file mode 100644 index 0000000..9a2e8e5 --- /dev/null +++ b/WWFLAT32/INCLUDE/MISC.H @@ -0,0 +1,178 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID cdecl Prog_End(VOID); +VOID cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +//char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PROC.ASM */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +int Get_CPU(VOID); + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +UBYTE Random(VOID); +int Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +LONG Reverse_LONG(LONG number); +WORD Reverse_WORD(WORD number); +LONG Swap_LONG(LONG number); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +//int Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +//int Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ + +void *Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); + +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD Processor(VOID); +extern WORD Operating_System(VOID); +extern ULONG random ( ULONG mod ) ; +extern void randomize ( void ) ; + +extern int Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + + diff --git a/WWFLAT32/INCLUDE/MONO.BAK b/WWFLAT32/INCLUDE/MONO.BAK new file mode 100644 index 0000000..b028af1 --- /dev/null +++ b/WWFLAT32/INCLUDE/MONO.BAK @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Mono Sub-system * + * * + * File Name : MONO.H * + * * + * Programmer : Joe Bostic * + * 32Bit Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef MONO_H +#define MONO_H + +// C++ Routines +//================================================================== + +// Mono Screen routines +//================================================================== +int Initialize_Mono_Screen ( void ); + + +// C Routines +//================================================================== +#ifdef __cplusplus +extern "C" { +#endif + +// Mono Screen routines +//=================================================================== + extern unsigned MonoScreen; + extern unsigned MonoEnabled; + + extern void Mono_Set_Cursor(int x, int y); + extern void Mono_Clear_Screen(void); + extern void Mono_Scroll(int lines); + extern void Mono_Put_Char(char character, int attrib=2); + extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + extern void Mono_Text_Print(void const *text, int x, int y, int attrib=2); + extern void Mono_Print(void const *text); + extern void Mono_View_Page(int page); + extern int Mono_Printf(char const *string, ...); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/WWFLAT32/INCLUDE/MONO.H b/WWFLAT32/INCLUDE/MONO.H new file mode 100644 index 0000000..86bfdfe --- /dev/null +++ b/WWFLAT32/INCLUDE/MONO.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Mono Sub-system * + * * + * File Name : MONO.H * + * * + * Programmer : Joe Bostic * + * 32Bit Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef MONO_H +#define MONO_H + +// C++ Routines +//================================================================== + +// Mono Screen routines +//================================================================== +int Initialize_Mono_Screen ( void ); + + +// C Routines +//================================================================== +#ifdef __cplusplus +//extern "C" { +#endif + +// Mono Screen routines +//=================================================================== + extern unsigned MonoScreen; + extern unsigned MonoEnabled; + + extern void Mono_Set_Cursor(int x, int y); + extern void Mono_Clear_Screen(void); + extern void Mono_Scroll(int lines); + extern void Mono_Put_Char(char character, int attrib=2); + extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + extern void Mono_Text_Print(void const *text, int x, int y, int attrib=2); + extern void Mono_Print(void const *text); + extern void Mono_View_Page(int page); + extern int Mono_Printf(char const *string, ...); + +#ifdef __cplusplus +//} +#endif + + +#endif + diff --git a/WWFLAT32/INCLUDE/NYBBTB.INC b/WWFLAT32/INCLUDE/NYBBTB.INC new file mode 100644 index 0000000..789ff2b --- /dev/null +++ b/WWFLAT32/INCLUDE/NYBBTB.INC @@ -0,0 +1,59 @@ +; +; 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 . +; + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/WWFLAT32/INCLUDE/PALETTE.BAK b/WWFLAT32/INCLUDE/PALETTE.BAK new file mode 100644 index 0000000..748c840 --- /dev/null +++ b/WWFLAT32/INCLUDE/PALETTE.BAK @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +VOID cdecl Set_Palette(VOID *palette); +VOID cdecl Set_Palette_Color(VOID *palette, WORD color, VOID *data); +VOID Fade_Palette_To(VOID *palette1, UWORD delay, VOID (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +VOID cdecl Load_Palette(BYTE *palette_file_name, VOID *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +VOID cdecl Morph_Palette (VOID *src_palette, VOID *dst_palette, UWORD delay, + VOID *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Set_Palette_Range(VOID *palette); +extern BOOL Bump_Color(VOID *palette, WORD changable, WORD target); + +#ifdef __cplusplus +} +#endif +extern "C" extern UBYTE CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ + diff --git a/WWFLAT32/INCLUDE/PALETTE.H b/WWFLAT32/INCLUDE/PALETTE.H new file mode 100644 index 0000000..0bab261 --- /dev/null +++ b/WWFLAT32/INCLUDE/PALETTE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +VOID cdecl Set_Palette(VOID *palette); +VOID cdecl Set_Palette_Color(VOID *palette, WORD color, VOID *data); +VOID Fade_Palette_To(VOID *palette1, UWORD delay, VOID (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +VOID cdecl Load_Palette(BYTE *palette_file_name, VOID *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +VOID cdecl Morph_Palette (VOID *src_palette, VOID *dst_palette, UWORD delay, + VOID *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Set_Palette_Range(VOID *palette); +extern BOOL Bump_Color(VOID *palette, WORD changable, WORD target); +extern UBYTE CurrentPalette[]; /* in pal.asm */ + +#ifdef __cplusplus +} +#endif + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ + diff --git a/WWFLAT32/INCLUDE/PLAYCD.H b/WWFLAT32/INCLUDE/PLAYCD.H new file mode 100644 index 0000000..8a529af --- /dev/null +++ b/WWFLAT32/INCLUDE/PLAYCD.H @@ -0,0 +1,304 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + union REGS regs; + struct SREGS sregs; + + SEGSEL cdDrive_addrp; + UWORD largestp; + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +private: + VOID GetCDDrives(VOID); + +}; + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + + +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H diff --git a/WWFLAT32/INCLUDE/SHAPE.H b/WWFLAT32/INCLUDE/SHAPE.H new file mode 100644 index 0000000..daadf3c --- /dev/null +++ b/WWFLAT32/INCLUDE/SHAPE.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_FADING = 0x0100, // Fading effect (VOID * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (VOID * color_table) +} ShapeFlags_Type; + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + UWORD ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + UBYTE Height; // Height of the shape in scan lines + UWORD Width; // Width of the shape in bytes + UBYTE OriginalHeight; // Original height of shape in scan lines + UWORD ShapeSize; // Size of the shape, including header + UWORD DataLength; // Size of the uncompressed shape (just data) + UBYTE Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +typedef struct { + UWORD NumShapes; // number of shapes in the block + LONG Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern VOID *MaskPage; +extern VOID *BackGroundPage; +extern LONG _ShapeBufferSize; +extern BYTE *_ShapeBuffer; +} + + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +WORD Draw_Shape(GraphicViewPortClass *gvp, VOID const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short cdecl Get_Shape_Data(VOID const *shape, WORD data); +int cdecl Extract_Shape_Count(VOID const *buffer); +void * cdecl Extract_Shape(VOID const *buffer, int shape); +int cdecl Restore_Shape_Height(VOID *shape); +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight); + +extern "C" { +int Get_Shape_Width(VOID const *shape); +int Get_Shape_Height(VOID const *shape); +int Get_Shape_Original_Height(VOID const *shape); +int Get_Shape_Uncomp_Size(VOID const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +VOID Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +WORD cdecl Get_Shape_Flags(VOID const *shape); +int cdecl Get_Shape_Size(VOID const *shape); +int cdecl Get_Shape_Scaled_Width(VOID const *shape, WORD scale); +int cdecl Get_Shape_Scaled_Height(VOID const *shape, WORD scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + diff --git a/WWFLAT32/INCLUDE/SHAPE.INC b/WWFLAT32/INCLUDE/SHAPE.INC new file mode 100644 index 0000000..e6a4042 --- /dev/null +++ b/WWFLAT32/INCLUDE/SHAPE.INC @@ -0,0 +1,214 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD +GLOBAL _MaskPage:DWORD +GLOBAL _BackGroundPage:DWORD +GLOBAL PredCount:DWORD +GLOBAL PredTable:BYTE +GLOBAL PredValue:DWORD +GLOBAL PartialPred:DWORD +GLOBAL PartialCount:DWORD +GLOBAL Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/WWFLAT32/INCLUDE/SOS.H b/WWFLAT32/INCLUDE/SOS.H new file mode 100644 index 0000000..648a01f --- /dev/null +++ b/WWFLAT32/INCLUDE/SOS.H @@ -0,0 +1,565 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif diff --git a/WWFLAT32/INCLUDE/SOSCOMP.H b/WWFLAT32/INCLUDE/SOSCOMP.H new file mode 100644 index 0000000..7334764 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOSCOMP.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif + diff --git a/WWFLAT32/INCLUDE/SOSDATA.H b/WWFLAT32/INCLUDE/SOSDATA.H new file mode 100644 index 0000000..c36aef1 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOSDATA.H @@ -0,0 +1,128 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/WWFLAT32/INCLUDE/SOSDEFS.H b/WWFLAT32/INCLUDE/SOSDEFS.H new file mode 100644 index 0000000..74d153b --- /dev/null +++ b/WWFLAT32/INCLUDE/SOSDEFS.H @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/WWFLAT32/INCLUDE/SOSFNCT.H b/WWFLAT32/INCLUDE/SOSFNCT.H new file mode 100644 index 0000000..af1c876 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOSFNCT.H @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int cdecl sosRealFree ( int ); +extern BOOL cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int cdecl sosRealAlloc( int, int *, int * ); +extern void cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int cdecl sosGetCS( VOID ); +extern int cdecl sosGetES( VOID ); +#else +extern int cdecl sosRealAlloc ( int, int *, int * ); +extern int cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD cdecl sosDIGIInitForWindows( WORD ); +extern WORD cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR cdecl sosCreateAliasCS ( LPSTR ); +extern VOID cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD cdecl getDS( VOID ); +extern WORD cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void cdecl sosTIMERDRVUnInit( void ); +extern void cdecl sosTIMERDRVHandler( void ); +extern void cdecl sosTIMERDRVFHandler( void ); +extern void cdecl sosTIMERDRVEnable( void ); +extern void cdecl sosTIMERDRVDisable( void ); +extern void cdecl sosTIMERDRVCallOld( void ); +extern void cdecl sosTIMERDRVSetRate( WORD ); +extern void cdecl sosDIGITimer_Start( void ); +extern void cdecl sosDIGITimer_End( void ); +extern void cdecl sosDIGIDrv_Start( void ); +extern void cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif diff --git a/WWFLAT32/INCLUDE/SOSRES.H b/WWFLAT32/INCLUDE/SOSRES.H new file mode 100644 index 0000000..0279933 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOSRES.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/WWFLAT32/INCLUDE/SOUND.H b/WWFLAT32/INCLUDE/SOUND.H new file mode 100644 index 0000000..ad0d223 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOUND.H @@ -0,0 +1,54 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +#define HMI_DRIVER TRUE +#include "sos.h" +#include "soscomp.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 4 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SFX_MINI_STAGE_BUFFER_SIZE (1024*4) + +#endif diff --git a/WWFLAT32/INCLUDE/SOUNDINT.H b/WWFLAT32/INCLUDE/SOUNDINT.H new file mode 100644 index 0000000..49ef523 --- /dev/null +++ b/WWFLAT32/INCLUDE/SOUNDINT.H @@ -0,0 +1,253 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + unsigned Loading:1; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + unsigned DontTouch:1; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + unsigned IsScore:1; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers in low ram. + */ + VOID *Buffer[2]; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ + WORD Index; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + WORD Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + WORD Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + WORD Volume; + WORD Reducer; // Amount to reduce volume per tick. + + /* + ** This flags whether the sample is in stereo. + */ + WORD Stereo; + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + WORD TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + WORD Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(WORD id, WORD *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + WORD Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + WORD FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + _SOS_COMPRESS_INFO sosinfo; + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SFX_MINI_STAGE_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; + int MaxSamples; + int Rate; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +void Unlock_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, WORD *trailersize); +VOID far cdecl maintenance_callback(VOID); +VOID cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void Audio_Mem_Set(void const *ptr, unsigned char value, long size); + void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + long Decompress_Frame(void * source, void * dest, long size); + int Decompress_Frame_Lock(void); + int Decompress_Frame_Unlock(void); + int sosCODEC_Lock(void); + int sosCODEC_Unlock(void); + void __GETDS(void); +} + diff --git a/WWFLAT32/INCLUDE/STAMP.INC b/WWFLAT32/INCLUDE/STAMP.INC new file mode 100644 index 0000000..d0c60e0 --- /dev/null +++ b/WWFLAT32/INCLUDE/STAMP.INC @@ -0,0 +1,38 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? ; Width of icon map (in icons). +MapHeight DW ? ; Height of icon map (in icons). +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? ; Color control map offset. +Map DD ? ; Icon map offset. + ENDS + +ICON256 EQU 1 + diff --git a/WWFLAT32/INCLUDE/SVGAPRIM.INC b/WWFLAT32/INCLUDE/SVGAPRIM.INC new file mode 100644 index 0000000..2651d21 --- /dev/null +++ b/WWFLAT32/INCLUDE/SVGAPRIM.INC @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/WWFLAT32/INCLUDE/TILE.H b/WWFLAT32/INCLUDE/TILE.H new file mode 100644 index 0000000..e8f2c44 --- /dev/null +++ b/WWFLAT32/INCLUDE/TILE.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + +#endif //TILE_H + + diff --git a/WWFLAT32/INCLUDE/TIMER.BAK b/WWFLAT32/INCLUDE/TIMER.BAK new file mode 100644 index 0000000..b1b705d --- /dev/null +++ b/WWFLAT32/INCLUDE/TIMER.BAK @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: + long (*Get_Ticks)(void); // System timer fetch. +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// + +//extern TimerClass TickCount; +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); + void far Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void Disable_Timer_Interrupt(void); + void Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); + + +#endif // TIMER_H + + diff --git a/WWFLAT32/INCLUDE/TIMER.H b/WWFLAT32/INCLUDE/TIMER.H new file mode 100644 index 0000000..87d6905 --- /dev/null +++ b/WWFLAT32/INCLUDE/TIMER.H @@ -0,0 +1,154 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: + long (*Get_Ticks)(void); // System timer fetch. +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +//lint -esym(1509,TimerClass) +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// + +//extern TimerClass TickCount; +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); + void far Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void Disable_Timer_Interrupt(void); + void Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); + + +#endif // TIMER_H + + diff --git a/WWFLAT32/INCLUDE/VBUFFER.H b/WWFLAT32/INCLUDE/VBUFFER.H new file mode 100644 index 0000000..e86db77 --- /dev/null +++ b/WWFLAT32/INCLUDE/VBUFFER.H @@ -0,0 +1,1034 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : VBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 6, 1995 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + * This module contains the definition for the video buffer class. The * + * primary functionality of the video buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * This header files gives the definition for all VideoViewPort and * + * VideoBuffer functions. These functions mirror some of the basic * + * functionality of the GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Video ViewPort - The Video ViewPort holds all of the functions which * + * can be used on a Video Buffer. The video viewport but cannot be * + * attached to a Graphic Buffer. * + * * + * Video Buffer - A refers to a physical object which has been mapped to * + * ram, like a graphic card. The SeenPage is a good example of a Video * + * Buffer. * + + * Below is a tree which shows the relationship of the GraphicBuffer and * + * Buffer classes to the VideoBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Get_Selector -- Get selector for virtual viewport instance * + * VVPC::Get_Offset -- Get offset for virtual view port class instance * + * VVPC::Get_Height -- Gets the height of a virtual viewport instance * + * VVPC::Get_Width -- Get the width of a virtual viewport instance * + * VVPC::Get_XAdd -- Get the X add offset for virtual viewport instance * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * VVPC::Get_Grasphic_Buffer -- Get the graphic buffer of the VP. * + * VVPC::Put_Pixel -- stub to call curr graphic mode Put_Pixel * + * VVPC::Get_Pixel -- stub to call curr graphic mode Get_Pixel * + * VVPC::Clear -- stub to call curr graphic mode Clear * + * VVPC::To_Buffer -- stub func 1 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 2 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 3 to call curr graphic mode To_Buffer * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to VVPC * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable x, y, w, h * + * Buffer_To_Page -- Generic 'c' callable form of Buffer_To_Page * + * GVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * VVPC::Print -- Stub function to print string on a Video View Port * + * VVPC::Print -- Stub function to print an int on a Graphic Viewport Class* + * VVPC::Print -- Stub function to print long on a graphic viewport class* + * VVPC::Print -- Stub function to print a short on a Video Viewport * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VBUFFER_H +#define VBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +/*=========================================================================*/ +/* Define external assembly funcs which deals with buffers and viewports. */ +/*=========================================================================*/ +long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view); + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ +#ifdef __cplusplus +extern "C" { + extern UWORD Get_MCGA_Selector(VOID); +} +#endif + +/*=========================================================================*/ +/* Let the compiler know that a VideoBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class VideoBufferClass; + + +/*=========================================================================*/ +/* VideoViewPortClass - Holds viewport information which has been attached */ +/* to a VideoBuffer. A viewport is effectively a rectangular subset */ +/* of the full buffer which is used for clipping and the like. */ +/* */ +/* int Selector - is the selector to view port buffer */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoViewPortClass(VideoBufferClass* graphic_buff, int x, int y, int w, int h); + VideoViewPortClass(); + ~VideoViewPortClass(); + /*===================================================================*/ + /* Define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + VideoBufferClass *Get_Video_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of graphic functions that are members of a Video */ + /* ViewPort class. */ + /*===================================================================*/ + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x, int y, int fcolor, int bcolor); + unsigned long Print(int num, int x, int y, int fcol, int bcol); + unsigned long Print(short num, int x, int y, int fcol, int bcol); + unsigned long Print(long num, int x, int y, int fcol, int bcol); + + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + void Attach(VideoBufferClass *video_buff, int x, int y, int w, int h); + void Attach(VideoBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + VideoBufferClass *VideoBuff; // related graphic buff +}; + + +/*=========================================================================*/ +/* VideoBufferClass - A VideoBuffer refers to an actual instance of a */ +/* physcial device which has been mapped to a memory address like a */ +/* video card. The VideoBuffer can be drawn to directly because it */ +/* inherits a Video ViewPort which represent its full size. */ +/* size. */ +/* */ +/* int Selector - is now the selector to graphic buffer */ +/* char *Buffer - is now the offset to graphic buffer */ +/* int Width - is now the width of graphic buffer */ +/* int Height - is now the height of graphic buffer */ +/* int XAdd - is now the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class VideoBufferClass: public VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoBufferClass(unsigned short selector, long offset, int w, int h); + VideoBufferClass(int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT); + ~VideoBufferClass(); +}; + +/*************************************************************************** + * VVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * VVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * VVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * VVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * VVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * VVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * VVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline VideoBufferClass *VideoViewPortClass::Get_Video_Buffer(void) +{ + return (VideoBuff); +} + +/*************************************************************************** + * VVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + VVPC_Put_Pixel_Func(this, x, y, color); +} + +/*************************************************************************** + * VVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Pixel(int x, int y) +{ + return(VVPC_Get_Pixel_Func(this, x, y)); +} + +/*************************************************************************** + * VVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Clear(unsigned char color) +{ + VVPC_Clear_Func(this, color); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print string on a Video View Port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(VVPC_Print_Func(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print an int on a Video Viewport * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print a short on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print long on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + BYTE str[33]; + + return(VVPC_Print_Func(this, ltoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*=========================================================================*/ +/*= The following BufferClass functions are defined here because they act =*/ +/*= on video viewports. =*/ +/*=========================================================================*/ + + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*=========================================================================*/ +/* The following GraphicBufferClass functions are defined here because */ +/* they act on graphic viewports. */ +/*=========================================================================*/ +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +#endif // VBUFFER_H diff --git a/WWFLAT32/INCLUDE/VIDEO.H b/WWFLAT32/INCLUDE/VIDEO.H new file mode 100644 index 0000000..36ce916 --- /dev/null +++ b/WWFLAT32/INCLUDE/VIDEO.H @@ -0,0 +1,213 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern REALPTR VesaFunc; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H diff --git a/WWFLAT32/INCLUDE/WINDOW.H b/WWFLAT32/INCLUDE/WINDOW.H new file mode 100644 index 0000000..69e5a1e --- /dev/null +++ b/WWFLAT32/INCLUDE/WINDOW.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWSs_H +#define WINDOWSs_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WWFLAT32/INCLUDE/WSA.H b/WWFLAT32/INCLUDE/WSA.H new file mode 100644 index 0000000..22e4007 --- /dev/null +++ b/WWFLAT32/INCLUDE/WSA.H @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +#ifndef VBUFFER_H +#include "vbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +VOID * cdecl Open_Animation(BYTE const *file_name, BYTE *user_buffer, LONG user_buffer_size, WSAOpenType user_flags, UBYTE *palette=NULL); +VOID cdecl Close_Animation( VOID *handle ); +BOOL cdecl Animate_Frame(VOID *handle, GraphicViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +BOOL cdecl Animate_Frame(VOID *handle, VideoViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +WORD cdecl Get_Animation_X(VOID const *handle); +WORD cdecl Get_Animation_Y(VOID const *handle); +WORD cdecl Get_Animation_Width(VOID const *handle); +WORD cdecl Get_Animation_Height(VOID const *handle); +WORD cdecl Get_Animation_Palette(VOID const *handle); +ULONG cdecl Get_Animation_Size(VOID const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, BufferClass& buffer, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, (BYTE *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +UWORD Apply_XOR_Delta(BYTE *source_ptr, BYTE *delta_ptr); +VOID Apply_XOR_Delta_To_Page_Or_Viewport(VOID *target, VOID *delta, WORD width, WORD nextrow, WORD copy); +} + + + +#endif // WSA_H diff --git a/WWFLAT32/INCLUDE/WWLIB32.H b/WWFLAT32/INCLUDE/WWLIB32.H new file mode 100644 index 0000000..b2e65ed --- /dev/null +++ b/WWFLAT32/INCLUDE/WWLIB32.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +#endif // WWLIB32_H + diff --git a/WWFLAT32/INCLUDE/WWMEM.H b/WWFLAT32/INCLUDE/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/WWFLAT32/INCLUDE/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WWFLAT32/INCLUDE/WWMEM.INC b/WWFLAT32/INCLUDE/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WWFLAT32/INCLUDE/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WWFLAT32/INCLUDE/WWSTD.H b/WWFLAT32/INCLUDE/WWSTD.H new file mode 100644 index 0000000..57914f3 --- /dev/null +++ b/WWFLAT32/INCLUDE/WWSTD.H @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define WWERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +inline int ABS(int); +inline long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +inline short MIN(short, short); +inline int MIN(int, int); +inline long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +inline short MAX(short, short); +inline int MAX(int, int); +inline long MAX(long, long); + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. + +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif + + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) (a |= (b)) +#define Bit_Flags_Off(a,b) (a &= (~(b))) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) (a ^= (b)) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif + + diff --git a/WWFLAT32/INCLUDE/WWSTD.H.BAK b/WWFLAT32/INCLUDE/WWSTD.H.BAK new file mode 100644 index 0000000..537643a --- /dev/null +++ b/WWFLAT32/INCLUDE/WWSTD.H.BAK @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define ERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +inline int ABS(int); +inline long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +inline short MIN(short, short); +inline int MIN(int, int); +inline long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +inline short MAX(short, short); +inline int MAX(int, int); +inline long MAX(long, long); + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. + +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif + + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) (a) |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif + + diff --git a/WWFLAT32/INCLUDE/_FILE.H b/WWFLAT32/INCLUDE/_FILE.H new file mode 100644 index 0000000..a966684 --- /dev/null +++ b/WWFLAT32/INCLUDE/_FILE.H @@ -0,0 +1,183 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 - Filio header stuff. * +;* * +;* File Name : FILE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : September 13, 1993 * +;* * +;* Last Update : April 11, 1994 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#include "file.h" +#endif + +#ifndef _FILE_H +#define _FILE_H + + +/*=========================================================================*/ +/* Fileio defines */ +/*=========================================================================*/ + +#define LIB_CDROM TRUE + +#define MODE_OLDFILE (O_RDONLY | O_BINARY) +#define MODE_NEWFILE (O_WRONLY | O_BINARY | O_CREAT | O_TRUNC) +#define MODE_READWRITE (O_RDWR | O_BINARY) + +#define FILEOPENERROR -1 +#define FILEOPEN(f,m) ibm_open(f, m, (((UWORD) m) == MODE_OLDFILE) ? S_IREAD : (S_IREAD | S_IWRITE)) + +#define FILECLOSE(fd) ibm_close(fd) +#define FILEREAD(f,b,n) ibm_read(f,b,(WORD)(n)) +#define FILEWRITE(f,b,n) ibm_write(f,b,(WORD)(n)) +#define FILESEEK(f,b,n) ibm_lseek(f, b, n) +#define FILEDELETE(f) ibm_unlink(f) +#define CHANGEDIR(p) ibm_chdir(p) + +#define FILENAMESIZE 13 +#define IO_CHUNK_SIZE 0xfff0UL + +/* +** Maximum number of file handles +*/ +#define TABLE_MAX 20 + + +/*=========================================================================*/ +/* The file handle table */ +/*=========================================================================*/ +typedef struct { + BOOL Empty; // Is this handle empty? + WORD Handle; // DOS file handle (0 = resident). + LONG Pos; // Current file position. + LONG Start; // Offset of file from pointer. + WORD Index; // FileData[] index. + WORD Mode; // Access mode (WW). + BYTE *Name; // File name pointer. +} FileHandleType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: FILEIO.CPP */ +/*=========================================================================*/ + +WORD ibm_getdisk(VOID); +WORD ibm_setdisk(WORD drive); +WORD ibm_close(WORD handle); +WORD ibm_unlink(BYTE const *name); +LONG ibm_lseek(WORD handle, LONG offset, WORD where); +UWORD ibm_read(WORD handle, VOID *ptr, UWORD bytes); +UWORD ibm_write(WORD handle, VOID *ptr, UWORD bytes); +WORD ibm_open(BYTE const *name, UWORD mode, WORD attrib); +WORD ibm_chdir(BYTE const *path); + +/*=========================================================================*/ +/* The following prototypes are for the file: FILELIB.CPP */ +/*=========================================================================*/ + +WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name); +VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename); +LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes ); +int cdecl Open_File_With_Recovery( BYTE const *file_name, unsigned int mode ); +BOOL cdecl Cache_File(WORD index, WORD file_handle); + + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVICES.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Get_Devices(VOID); +extern WORD Is_Device_Real(WORD device); + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: DEVTABLE.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Init_Device_Table(BYTE *table); +extern WORD Max_Device(VOID); + + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* The following prototypes are for the file: HARDERR.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Install_Hard_Error_Handler(VOID); +extern VOID Remove_Hard_Error_Handler(VOID); + +#ifdef __cplusplus +} +#endif + + +/*=========================================================================*/ +/* Globale variables in the fileio system. */ +/*=========================================================================*/ + +extern BYTE CallingDOSInt; +extern "C" extern BYTE MaxDevice,DefaultDrive; +extern BYTE MultiDriveSearch; +extern FileDataType *FileDataPtr; +extern FileHandleType FileHandleTable[TABLE_MAX]; +extern UWORD NumFiles; // Number of files, except PAK, in file table. +extern UWORD NumPAKFiles; // Number of PAK files in filetable. +extern VOID *FileCacheHeap; // Pointer to the cache in memory. +extern WORD DiskNumber; +extern WORD MaxDirNum; + + +/*=========================================================================*/ + + + +#endif // _FILE_H + + diff --git a/WWFLAT32/KEYBOARD/KEYBOARD.ASM b/WWFLAT32/KEYBOARD/KEYBOARD.ASM new file mode 100644 index 0000000..a51e098 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYBOARD.ASM @@ -0,0 +1,2568 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 512 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + test ah,SHIFTPRESS + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: + +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/KEYBOARD.BAK b/WWFLAT32/KEYBOARD/KEYBOARD.BAK new file mode 100644 index 0000000..f456e48 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYBOARD.BAK @@ -0,0 +1,2565 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 255 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/KEYBOARD.H b/WWFLAT32/KEYBOARD/KEYBOARD.H new file mode 100644 index 0000000..7f06b37 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYBOARD.H @@ -0,0 +1,447 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : July 13, 1994 * + * * + * Last Update : July 13, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +extern "C" { + void Install_Keyboard_Interrupt(void *addr, long size); + void Install_Page_Fault_Handle(void) ; + void *Get_RM_Keyboard_Address(void); + long Get_RM_Keyboard_Size(void); + void Remove_Keyboard_Interrupt(void); + int Check_Key_Num(void); + int Check_Key_Bits(void); + int Check_Key(void); + short Get_Key_Num(void); + short Get_Key_Bits(void); + int Get_Key(void); + int KN_To_KA(int keynum); + int Keyboard_Attributes_On(int key_flags); + int Keyboard_Attributes_Off(int key_flags); + void Clear_KeyBuffer(void); + int Key_Down(int key); + int Key_Satisfied(int key); + void Stuff_Key_WORD(int code); + void Stuff_Key_Num(int key); + int Install_Mouse(int max_width, int max_height, int scr_width, int scr_height); + void Reset_Mouse (void) ; + void Remove_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + int Get_Mouse_Disabled(void); + void *Set_Mouse_Cursor(int xhotspot, int yhotspot, void *cursor); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int sx_pixel, int sy_pixel, int dx_pixel, int dy_pixel); + void Conditional_Show_Mouse(void); + + + void __interrupt far Keyboard_Interrupt(void); + extern int MouseQX; + extern int MouseQY; + +} + + +enum { + REPEATON = 0x0001, /* 1:all makes into buffer, 0:only 1st make */ + TRACKEXT = 0x0002, /* 1:Home != keypad Home, 0:Home=keypad Home */ + FILTERONLY = 0x0004, /* 1:Normal BIOS operation with filter */ + CTRLALTTURBO = 0x0010, /* 1:Allow turbo up and down in application */ + NONUMLOCK = 0x0200, /* 1:do NOT remap keypad to numbers */ + TASKSWITCHABLE = 0x400, /* 1:allows task switching keys thru ALT-TAB, */ + /* ALT-ESC,CTRL-ESC */ + PASSBREAKS = 0x0800, // Pass all breaks to the keyboard buffer. + + /* The following flags, if turned on, should only be used for + debugging purposes (remember to take out the calls when BETA */ + + CTRLSON = 0x0008, /* 1:pass scroll lock sequence into BIOS */ + CTRLCON = 0x0020, /* 1:pass stop code to BIOS */ + SCROLLLOCKON = 0x0040, /* 1:pass scroll lock key into BIOS */ + PAUSEON = 0x0080, /* 1:pass the pause key and seq to BIOS */ + /* make sure FILTERONLY is set */ + BREAKON = 0x0100, /* 1:pass the ctrl break seq to BIOS */ + KEYMOUSE = 0x1000, /* 1:keypad translates into mouse moves */ + SIMLBUTTON = 0x2000, /* 1:have space and enter keys simulate Left */ + /* mouse button when KEYMOUSE is set */ + DEBUGINT = 0x4000 /* Use scroll lock to disable keyboard int */ +}; + + +/* +** These are the codes for the various key codes that are returned from the +** keyboard input routines Get_Key() and Input_ASCII(). +*/ +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KeyASCIIType; + +/* +** These are the keyboard codes that are returned from the input routines +** Get_Key_Num and Input_Num. +*/ +typedef enum { + KN_NONE = 0, + KN_GRAVE = 1, /* ` */ + KN_1, KN_2, KN_3, KN_4, KN_5, KN_6, KN_7, KN_8, KN_9, KN_0, + KN_MINUS, /* - */ + KN_EQUAL, /* = */ + + KN_RESERVED1, + + KN_BACKSPACE, /* */ + + KN_TAB, /* */ + KN_Q, KN_W, KN_E, KN_R, KN_T, KN_Y, KN_U, KN_I, KN_O, KN_P, + KN_LBRACKET, /* [ */ + KN_RBRACKET, /* ] */ + KN_BACKSLASH, /* \ */ + + KN_CAPSLOCK, /* */ + KN_A, KN_S, KN_D, KN_F, KN_G, KN_H, KN_J, KN_K, KN_L, + KN_SEMICOLON, /* ; */ + KN_SQUOTE, /* ' */ + KN_BACKSLASH2, + KN_RETURN, /* or */ + + KN_LSHIFT, /* */ + + KN_MOUSE_MOVE, // Indicate a mouse move (for playback of + + KN_Z, KN_X, KN_C, KN_V, KN_B, KN_N, KN_M, + KN_COMMA, /* , */ + KN_PERIOD, /* . */ + KN_SLASH, /* / */ + + KN_RESERVED3, + + KN_RSHIFT, /* */ + + KN_LCTRL, /* */ + KN_LCOMM, /* for AMIGA */ + KN_LALT, /* */ + KN_SPACE, /* */ + KN_RALT, /* */ + KN_RCOMM, /* for AMIGA */ + KN_RCTRL, /* */ + /* the following are forced into key buffer */ + KN_LMOUSE, + KN_RMOUSE, + KN_JBUTTON1, + KN_JBUTTON2, + KN_J_UP, + KN_J_RIGHT, + KN_J_DOWN, + KN_J_LEFT, + + KN_SPECIAL9, + + KN_SPECIAL10, + + KN_E_INSERT, /* extended */ + KN_E_DELETE, /* extended */ + + KN_RESERVED4, + KN_RESERVED5, + + KN_E_LEFT, /* extended */ + KN_E_HOME, /* extended */ + KN_E_END, /* extended */ + + KN_RESERVED6, + + KN_E_UP, /* extended */ + KN_E_DOWN, /* extended */ + KN_E_PGUP, /* extended */ + KN_E_PGDN, /* extended */ + KN_K_LPAREN, /* for AMIGA */ + KN_K_RPAREN, /* for AMIGA */ + KN_E_RIGHT, /* extended */ + + KN_NUMLOCK, /* */ + KN_HOME, /* num key pad 7 */ + KN_UPLEFT = KN_HOME, + KN_LEFT, /* num key pad 4 */ + KN_END, /* num key pad 1 */ + KN_DOWNLEFT = KN_END, + + KN_RESERVED7, + + KN_KEYPAD_SLASH, /* num key pad / */ + KN_UP, /* num key pad 8 */ + KN_CENTER, /* num key pad 5 */ + KN_DOWN, /* num key pad 2 */ + KN_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK, /* num key pad * */ + KN_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KN_PGUP, + KN_RIGHT, /* num key pad 6 */ + KN_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KN_PGDN, + KN_DELETE, /* num key pad . */ + KN_KEYPAD_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS, /* num key pad + */ + + KN_RESERVED8, + + KN_KEYPAD_RETURN, /* num key pad */ + + KN_RESERVED9, + + KN_ESC, + KN_HELP, /* for AMIGA */ + KN_F1, KN_F2, KN_F3, KN_F4, KN_F5, KN_F6, KN_F7, KN_F8, KN_F9, KN_F10, + KN_F11, + KN_F12, + KN_PRNTSCRN, /* */ + KN_SCROLLLOCK, /* */ + KN_PAUSE, /* */ + + KN_SHIFT_BIT = 0x0100, + KN_CTRL_BIT = 0x0200, + KN_ALT_BIT = 0x0400, + KN_RLSE_BIT = 0x0800, + KN_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KN_RCOMM_BIT = 0x2000, /* Amiga Right Comm key */ + KN_BUTTON = 0x8000 /* Amiga Right Comm key */ +} KeyNumType; + +#endif diff --git a/WWFLAT32/KEYBOARD/KEYBOARD.INC b/WWFLAT32/KEYBOARD/KEYBOARD.INC new file mode 100644 index 0000000..94ed506 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WWFLAT32/KEYBOARD/KEYIPROT.ASM b/WWFLAT32/KEYBOARD/KEYIPROT.ASM new file mode 100644 index 0000000..a621713 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYIPROT.ASM @@ -0,0 +1,1023 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] + retf + +STACK ; Don't really need this + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/KEYIREAL.ASM b/WWFLAT32/KEYBOARD/KEYIREAL.ASM new file mode 100644 index 0000000..a90604b --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYIREAL.ASM @@ -0,0 +1,2610 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 512 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 512 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/KEYIREAL.BAK b/WWFLAT32/KEYBOARD/KEYIREAL.BAK new file mode 100644 index 0000000..87b6875 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYIREAL.BAK @@ -0,0 +1,2610 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 255 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 255 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/KEYIREAL.IBN b/WWFLAT32/KEYBOARD/KEYIREAL.IBN new file mode 100644 index 0000000..e2fa7b3 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYIREAL.IBN @@ -0,0 +1,429 @@ + DB 001h,000h,001h,000h,011h,000h,000h,000h,000h,000h,0FFh,000h,001h,0F0h,000h + DB 010h,0A0h,000h,000h,000h,03Fh,001h,000h,000h,03Fh,001h,045h,000h,03Fh,001h + DB 089h,000h,0A0h,000h,089h,000h,000h,000h,089h,000h,000h,000h,045h,000h,000h + DB 000h,000h,000h,0A0h,000h,045h,000h,001h,002h,004h,008h,010h,020h,040h,080h + DB 020h,002h,020h,003h,00Ch,006h,00Dh,007h,06Ah,006h,069h,006h,030h,002h,030h + DB 003h,07Dh,000h,07Dh,001h,05Ah,002h,05Ah,003h,000h,002h,010h,004h,06Eh,004h + DB 06Eh,002h,07Ch,000h,008h,000h,008h,000h,010h,000h,010h,000h,010h,000h,010h + DB 000h,020h,000h,020h,000h,040h,000h,040h,000h,080h,000h,080h,000h,000h,001h + DB 000h,004h,000h,004h,000h,004h,000h,001h,000h,000h,000h,000h,038h,01Dh,052h + DB 053h,04Bh,047h,04Fh,048h,050h,049h,051h,04Dh,035h,01Ch,037h,046h,03Eh,040h + DB 04Bh,04Ch,04Fh,050h,051h,053h,054h,055h,056h,059h,05Fh,06Ch,07Ch,000h,03Ch + DB 03Ah,063h,068h,05Ch,05Bh,05Dh,060h,062h,065h,067h,066h,037h,02Bh,07Ch,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,06Eh + DB 002h,003h,004h,005h,006h,007h,008h,009h,00Ah,00Bh,00Ch,00Dh,00Fh,010h,011h + DB 012h,013h,014h,015h,016h,017h,018h,019h,01Ah,01Bh,01Ch,02Bh,03Ah,01Fh,020h + DB 021h,022h,023h,024h,025h,026h,027h,028h,029h,001h,02Ch,01Dh,02Eh,02Fh,030h + DB 031h,032h,033h,034h,035h,036h,037h,039h,064h,03Ch,03Dh,01Eh,070h,071h,072h + DB 073h,074h,075h,076h,077h,078h,079h,05Ah,07Dh,05Bh,060h,065h,069h,05Ch,061h + DB 066h,06Ah,05Dh,062h,067h,063h,068h,07Fh,07Fh,07Fh,07Ah,07Bh,000h,000h,0FEh + DB 087h,0FFh,0C0h,01Fh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,038h,0EFh,001h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,01Eh,02Ch,039h,03Ah,03Ch,03Eh,040h,05Ah + DB 080h,080h,080h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,0FFh,001h,000h + DB 000h,000h,000h,000h,0FFh,000h,000h,000h,001h,000h,000h,000h,000h,001h,0FFh + DB 001h,000h,001h,001h,008h,000h,002h,000h,008h,000h,006h,000h,004h,000h,003h + DB 000h,008h,000h,005h,000h,008h,000h,008h,000h,008h,000h,008h,000h,000h,000h + DB 001h,000h,008h,000h,007h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,02Dh,00Eh,006h,011h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F5h + DB 010h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F8h + DB 011h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0B2h,014h + DB 05Eh,013h,063h,017h,0EAh,015h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,090h,000h,000h,000h,000h,0ADh,0DEh,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0ADh,0DEh,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,0C8h,000h,000h,000h,051h,057h,006h,01Eh,08Ch + DB 0C8h,08Eh,0D8h,08Eh,0C0h,08Bh,046h,006h,0F7h,006h,004h,000h,002h,000h,075h + DB 013h,0B9h,010h,000h,0BFh,094h,000h,0F2h,0AEh,0E3h,009h,0BFh,0B4h,000h,04Fh + DB 02Bh,0F9h,026h,08Ah,005h,01Fh,007h,05Fh,059h,0C9h,0CBh,0C8h,000h,000h,000h + DB 056h,053h,01Eh,08Ch,0C8h,08Eh,0D8h,0A1h,0BAh,001h,08Bh,0F0h,005h,002h,000h + DB 025h,0FFh,000h,039h,006h,0B6h,001h,074h,011h,08Bh,05Eh,006h,089h,09Ch,0B6h + DB 000h,0A3h,0BAh,001h,033h,0C0h,01Fh,05Bh,05Eh,0C9h,0CBh,0B8h,001h,000h,01Fh + DB 05Bh,05Eh,0C9h,0CBh,0C8h,004h,000h,000h,053h,051h,052h,057h,056h,01Eh,09Ch + DB 0FAh,08Ch,0C8h,08Eh,0D8h,0A1h,0ABh,002h,0A3h,0AFh,002h,0A1h,0B1h,002h,0A3h + DB 0B5h,002h,08Bh,046h,006h,00Bh,0C0h,075h,003h,0E9h,0F7h,001h,0F7h,006h,004h + DB 000h,000h,010h,00Fh,084h,03Ah,001h,0F6h,0C4h,004h,00Fh,085h,033h,001h,0F7h + DB 006h,004h,000h,000h,020h,074h,00Ch,03Ch,02Bh,074h,00Ch,03Ch,03Dh,074h,008h + DB 03Ch,06Ch,074h,004h,03Ch,063h,075h,016h,0B0h,041h,080h,00Eh,0F6h,002h,001h + DB 0F6h,0C4h,008h,00Fh,084h,00Ah,001h,080h,026h,0F6h,002h,0FEh,0E9h,002h,001h + DB 03Ch,068h,075h,016h,0B0h,042h,080h,00Eh,0F6h,002h,002h,0F6h,0C4h,008h,00Fh + DB 084h,0F0h,000h,080h,026h,0F6h,002h,0FDh,0E9h,0E8h,000h,0F6h,0C4h,008h,00Fh + DB 085h,0E4h,000h,03Ch,061h,074h,01Eh,03Ch,05Bh,00Fh,082h,0DAh,000h,03Ch,067h + DB 00Fh,087h,0D4h,000h,03Ch,05Dh,076h,00Eh,03Ch,065h,073h,00Ah,03Ch,060h,074h + DB 006h,03Ch,062h,00Fh,085h,0C2h,000h,08Ah,0ECh,032h,0E4h,02Ch,05Bh,08Bh,0D8h + DB 0D1h,0E3h,081h,0C3h,06Fh,002h,08Bh,007h,08Ah,0DCh,098h,093h,098h,093h,0F6h + DB 0C5h,002h,075h,021h,0BAh,001h,000h,0F6h,0C5h,001h,074h,003h,083h,0C2h,003h + DB 003h,0DAh,08Ah,09Fh,00Ah,000h,093h,098h,093h,087h,0DAh,003h,0D8h,08Ah,087h + DB 00Ah,000h,098h,087h,0DAh,0EBh,034h,083h,0E3h,003h,025h,003h,000h,083h,0E3h + DB 003h,025h,003h,000h,0D1h,0E3h,0D1h,0E3h,00Bh,0D8h,0D1h,0E3h,08Bh,09Fh,089h + DB 002h,0D1h,0E3h,0D1h,0E3h,08Bh,087h,010h,000h,08Bh,09Fh,012h,000h,0A3h,0AFh + DB 002h,089h,01Eh,0B5h,002h,0A1h,0AFh,002h,08Bh,01Eh,0B5h,002h,0EBh,02Fh,001h + DB 006h,0AFh,002h,079h,005h,033h,0C0h,0A3h,0AFh,002h,001h,01Eh,0B5h,002h,079h + DB 006h,033h,0DBh,089h,01Eh,0B5h,002h,0A1h,0AFh,002h,08Bh,01Eh,0B5h,002h,03Dh + DB 03Fh,001h,07Eh,003h,0B8h,03Fh,001h,081h,0FBh,0C7h,000h,07Eh,003h,0BBh,0C7h + DB 000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h,0A3h,0ABh,002h,089h,01Eh,0B1h,002h + DB 083h,03Eh,0A9h,002h,000h,075h,008h,00Eh,0E8h,0A5h,003h,00Eh,0E8h,0D4h,003h + DB 0B8h,02Dh,000h,089h,046h,006h,08Bh,036h,0BAh,001h,089h,076h,0FEh,08Bh,03Eh + DB 0B6h,001h,050h,00Eh,0E8h,051h,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,011h,08Bh + DB 046h,006h,03Ch,02Dh,074h,00Dh,03Ch,041h,074h,009h,03Ch,042h,074h,005h,0EBh + DB 029h,0E9h,080h,000h,0FFh,036h,0AFh,002h,00Eh,0E8h,02Eh,0FEh,083h,0C4h,002h + DB 00Bh,0C0h,075h,0EEh,083h,046h,0FCh,002h,0FFh,036h,0B5h,002h,00Eh,0E8h,01Bh + DB 0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0DBh,083h,046h,0FCh,002h,0BBh,001h,001h + DB 08Bh,046h,006h,03Ch,02Dh,074h,01Bh,03Ch,07Fh,074h,017h,0F6h,0C4h,008h,074h + DB 016h,032h,0DBh,0F7h,006h,004h,000h,000h,008h,075h,00Ch,03Ch,041h,074h,008h + DB 03Ch,042h,074h,004h,089h,036h,0BAh,001h,08Bh,0F8h,083h,0E7h,07Fh,0B1h,003h + DB 0D3h,0EFh,08Ah,0C8h,080h,0E1h,007h,0D3h,0E3h,0F6h,0D7h,084h,09Dh,039h,002h + DB 074h,00Ch,0F7h,006h,004h,000h,001h,000h,075h,004h,089h,036h,0BAh,001h,020h + DB 0BDh,039h,002h,008h,09Dh,039h,002h,0B8h,001h,000h,0EBh,008h,08Bh,046h,0FEh + DB 0A3h,0BAh,001h,033h,0C0h,09Dh,01Fh,05Eh,05Fh,05Ah,059h,05Bh,0C9h,0CBh,050h + DB 053h,051h,057h,01Eh,052h,006h,056h,0FCh,08Ch,0C8h,08Eh,0D8h,08Ch,0D1h,089h + DB 026h,08Ah,003h,089h,00Eh,08Ch,003h,0BAh,090h,007h,083h,0E2h,0FEh,0FAh,08Eh + DB 0D0h,08Bh,0E2h,0FBh,083h,03Eh,0CEh,002h,000h,0C7h,006h,0CEh,002h,000h,000h + DB 00Fh,085h,003h,002h,08Bh,016h,004h,000h,083h,026h,0BEh,001h,0FCh,0B8h,040h + DB 000h,08Eh,0C0h,026h,0F6h,006h,017h,000h,040h,074h,005h,083h,00Eh,0BEh,001h + DB 001h,026h,0F6h,006h,017h,000h,020h,074h,005h,083h,00Eh,0BEh,001h,002h,0C6h + DB 006h,0B5h,000h,001h,026h,0F6h,006h,096h,000h,010h,075h,005h,0C6h,006h,0B5h + DB 000h,000h,08Ch,0C8h,08Eh,0C0h,0FCh,032h,0E4h,0BBh,001h,001h,0E4h,060h,08Bh + DB 01Eh,05Bh,002h,088h,087h,049h,002h,043h,083h,0E3h,00Fh,089h,01Eh,05Bh,002h + DB 0BBh,001h,001h,03Ch,0E1h,075h,005h,0C6h,006h,05Eh,002h,003h,080h,03Eh,05Eh + DB 002h,000h,074h,00Fh,0FEh,00Eh,05Eh,002h,0F7h,0C2h,080h,000h,00Fh,085h,08Eh + DB 001h,0E9h,0CBh,001h,03Ch,0E0h,075h,008h,0C6h,006h,05Dh,002h,001h,0E9h,07Fh + DB 001h,0A8h,080h,074h,007h,032h,0DBh,024h,07Fh,080h,0CCh,008h,080h,03Eh,05Dh + DB 002h,000h,074h,023h,0C6h,006h,05Dh,002h,000h,0C6h,006h,0B7h,002h,001h,0BFh + DB 084h,000h,0B9h,010h,000h,0F2h,0AEh,0E3h,002h,0EBh,003h,0E9h,094h,001h,08Ah + DB 045h,00Fh,0C6h,006h,0B7h,002h,000h,0EBh,011h,03Ch,07Ah,075h,004h,0B0h,080h + DB 0EBh,009h,08Bh,0F8h,083h,0E7h,07Fh,08Ah,085h,0C0h,001h,0F6h,006h,041h,002h + DB 001h,075h,01Bh,0F6h,006h,040h,002h,004h,074h,017h,03Ch,05Ah,075h,010h,080h + DB 03Eh,0B5h,000h,001h,074h,009h,0F7h,0C2h,080h,000h,075h,003h,0E9h,057h,001h + DB 080h,0CCh,002h,0F6h,006h,040h,002h,050h,074h,003h,080h,0CCh,004h,050h,00Eh + DB 0E8h,043h,0FCh,083h,0C4h,002h,0F6h,006h,03Eh,002h,010h,075h,007h,0F6h,006h + DB 040h,002h,002h,074h,003h,080h,0CCh,001h,08Bh,0F8h,083h,0E7h,07Fh,0D1h,0EFh + DB 0D1h,0EFh,0D1h,0EFh,08Bh,0D8h,083h,0E3h,07Fh,080h,0E3h,007h,08Ah,0AFh,034h + DB 000h,0F7h,006h,0BEh,001h,001h,000h,074h,009h,084h,0ADh,019h,002h,074h,003h + DB 080h,0F4h,001h,0F7h,006h,0BEh,001h,002h,000h,074h,009h,084h,0ADh,029h,002h + DB 074h,003h,080h,0F4h,001h,088h,026h,06Ah,002h,0F7h,0C2h,000h,040h,074h,034h + DB 066h,083h,03Eh,0EAh,002h,001h,075h,01Bh,03Dh,073h,000h,074h,00Ah,03Dh,076h + DB 000h,072h,00Eh,03Dh,078h,000h,077h,009h,066h,0C7h,006h,0EAh,002h,000h,000h + DB 000h,000h,0E9h,08Bh,000h,03Dh,07Dh,000h,075h,00Ch,066h,0C7h,006h,0EAh,002h + DB 001h,000h,000h,000h,0E9h,0BAh,000h,03Dh,068h,006h,074h,075h,03Dh,04Ch,006h + DB 074h,070h,03Dh,030h,002h,074h,005h,03Dh,07Eh,002h,075h,006h,0C7h,006h,008h + DB 000h,001h,000h,03Dh,020h,004h,075h,00Ch,050h,0A1h,000h,000h,035h,001h,000h + DB 050h,083h,0C4h,002h,058h,03Dh,034h,004h,075h,00Ch,050h,0A1h,002h,000h,035h + DB 001h,000h,050h,083h,0C4h,002h,058h,050h,00Eh,0E8h,0DEh,0FBh,058h,03Ch,06Eh + DB 075h,00Dh,083h,03Eh,082h,000h,000h,074h,006h,050h,0FFh,01Eh,080h,000h,058h + DB 0BFh,05Fh,002h,0B9h,00Ah,000h,0F2h,0AEh,00Bh,0C9h,075h,01Bh,0BFh,03Ch,000h + DB 0B9h,022h,000h,0D1h,0E9h,0F2h,0AFh,0E3h,009h,08Bh,05Dh,020h,023h,0DAh,074h + DB 002h,0EBh,006h,0F7h,0C2h,004h,000h,074h,040h,02Eh,0FFh,006h,059h,002h,081h + DB 03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h,057h,050h,006h,0B8h,000h,0A0h,08Eh + DB 0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h,000h,0F3h,0AAh,007h,058h,05Fh,059h + DB 0FAh,08Bh,016h,08Ch,003h,08Eh,0D2h,08Bh,026h,08Ah,003h,0FBh,0BAh,0ECh,010h + DB 09Ch,00Eh,052h,09Ch,0FFh,036h,0BCh,002h,0FFh,036h,0BAh,002h,0CFh,0E4h,061h + DB 08Ah,0E0h,00Ch,080h,0E6h,061h,086h,0E0h,0E6h,061h,0B8h,040h,000h,08Eh,0C0h + DB 026h,0A0h,096h,000h,024h,0FDh,026h,0A2h,096h,000h,0B0h,020h,0E6h,020h,081h + DB 03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h,057h,050h,006h,0B8h,000h,0A0h,08Eh + DB 0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h,000h,0F3h,0AAh,007h,058h,05Fh,059h + DB 08Bh,016h,08Ch,003h,0FAh,08Eh,0D2h,08Bh,026h,08Ah,003h,0FBh,05Eh,007h,05Ah + DB 01Fh,05Fh,059h,05Bh,058h,0CFh,09Ch,050h,006h,0B8h,000h,0B0h,08Eh,0C0h,026h + DB 0FEh,006h,000h,000h,007h,058h,09Dh,0CFh,09Ch,00Eh,0E8h,022h,0FDh,0CBh,050h + DB 053h,051h,052h,01Eh,08Ch,0C8h,08Eh,0D8h,080h,03Eh,0F7h,002h,000h,075h,01Dh + DB 083h,03Eh,003h,003h,000h,075h,00Ch,0B8h,000h,000h,050h,00Eh,0FFh,016h,051h + DB 003h,083h,0C4h,002h,083h,006h,003h,003h,001h,083h,016h,003h,003h,000h,01Fh + DB 05Ah,059h,05Bh,058h,0CBh,0C8h,004h,000h,000h,050h,053h,051h,052h,056h,057h + DB 01Eh,006h,08Ch,0C8h,08Eh,0D8h,080h,03Eh,0F7h,002h,000h,075h,050h,083h,03Eh + DB 003h,003h,000h,074h,049h,0FFh,00Eh,003h,003h,083h,03Eh,003h,003h,000h,075h + DB 03Eh,08Bh,00Eh,03Fh,003h,08Bh,016h,043h,003h,0A1h,0ABh,002h,089h,046h,0FEh + DB 08Bh,01Eh,0B1h,002h,089h,05Eh,0FCh,0A3h,02Fh,003h,089h,01Eh,033h,003h,089h + DB 00Eh,037h,003h,089h,016h,03Bh,003h,0B8h,001h,000h,050h,00Eh,0FFh,016h,051h + DB 003h,083h,0C4h,002h,0FFh,076h,0FCh,0FFh,076h,0FEh,00Eh,0FFh,016h,053h,003h + DB 083h,0C4h,004h,007h,01Fh,05Fh,05Eh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,002h + DB 000h,000h,053h,08Ch,0C8h,08Eh,0D8h,0C7h,046h,0FEh,02Dh,000h,08Bh,05Eh,006h + DB 08Bh,0C3h,032h,01Eh,0F6h,002h,074h,029h,0A2h,0F6h,002h,0F6h,0C3h,002h,074h + DB 00Eh,0C7h,046h,0FEh,042h,000h,0A8h,002h,075h,005h,081h,04Eh,0FEh,000h,008h + DB 0F6h,0C3h,001h,074h,00Eh,0C7h,046h,0FEh,041h,000h,0A8h,001h,075h,005h,081h + DB 04Eh,0FEh,000h,008h,08Bh,046h,0FEh,05Bh,0C9h,0CBh,0C8h,004h,000h,000h,050h + DB 053h,051h,052h,01Eh,056h,006h,057h,02Eh,089h,01Eh,092h,007h,02Eh,0A3h,094h + DB 007h,08Ch,0C8h,08Eh,0D8h,08Ch,0D3h,089h,026h,096h,007h,089h,01Eh,098h,007h + DB 0BBh,09Ch,00Bh,083h,0E3h,0FEh,0FAh,08Eh,0D0h,08Bh,0E3h,0FBh,080h,03Eh,0F7h + DB 002h,000h,00Fh,085h,0FAh,000h,080h,03Eh,0F8h,002h,000h,00Fh,084h,0F1h,000h + DB 0A1h,094h,007h,025h,01Eh,000h,075h,009h,083h,03Eh,0A9h,002h,000h,00Fh,085h + DB 0E0h,000h,083h,03Eh,0F9h,002h,001h,075h,002h,0D1h,0E9h,03Bh,00Eh,049h,003h + DB 072h,005h,08Bh,00Eh,049h,003h,049h,03Bh,016h,04Dh,003h,072h,005h,08Bh,016h + DB 04Dh,003h,04Ah,089h,00Eh,0ABh,002h,089h,016h,0B1h,002h,066h,0F7h,006h,004h + DB 000h,000h,010h,000h,000h,075h,013h,0FFh,036h,092h,007h,00Eh,0E8h,02Ch,0FFh + DB 083h,0C4h,002h,050h,00Eh,0E8h,07Bh,0F9h,083h,0C4h,002h,083h,03Eh,0A9h,002h + DB 000h,00Fh,085h,092h,000h,083h,03Eh,0FBh,002h,000h,074h,019h,08Bh,0C1h,08Bh + DB 0CAh,02Bh,006h,0FFh,002h,08Bh,01Eh,0FBh,002h,099h,0F7h,0FBh,0F7h,0EBh,003h + DB 006h,0FFh,002h,08Bh,0D1h,08Bh,0C8h,083h,03Eh,0FDh,002h,000h,074h,015h,08Bh + DB 0C2h,02Bh,006h,001h,003h,08Bh,01Eh,0FDh,002h,099h,0F7h,0FBh,0F7h,0EBh,003h + DB 006h,001h,003h,08Bh,0D0h,083h,03Eh,003h,003h,000h,075h,047h,039h,00Eh,007h + DB 003h,075h,006h,039h,016h,009h,003h,074h,03Bh,0A1h,00Bh,003h,025h,000h,0C0h + DB 03Dh,000h,0C0h,074h,00Ch,00Eh,0E8h,016h,0FEh,0F7h,006h,00Bh,003h,000h,080h + DB 074h,020h,03Bh,00Eh,00Dh,003h,072h,01Ah,03Bh,00Eh,015h,003h,077h,014h,03Bh + DB 016h,011h,003h,072h,00Eh,03Bh,016h,019h,003h,077h,008h,081h,00Eh,00Bh,003h + DB 000h,040h,0EBh,004h,00Eh,0E8h,01Dh,0FEh,089h,00Eh,007h,003h,089h,016h,009h + DB 003h,081h,03Eh,09Ah,007h,0ADh,0DEh,074h,017h,051h,057h,050h,006h,0B8h,000h + DB 0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h,000h,0F3h,0AAh,007h,058h + DB 05Fh,059h,0A1h,098h,007h,0FAh,08Eh,0D0h,08Bh,026h,096h,007h,0FBh,05Fh,007h + DB 05Eh,01Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h,053h,051h + DB 052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0FCh,0A1h,02Fh,003h,08Bh,01Eh + DB 033h,003h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh + DB 0FCh,003h,006h,037h,003h,003h,01Eh,03Bh,003h,089h,046h,0FAh,089h,05Eh,0F8h + DB 0C7h,046h,0F6h,000h,000h,0A1h,023h,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h + DB 08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh + DB 04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h + DB 0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh + DB 04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h + DB 0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,0AEh,000h,00Ah + DB 0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh + DB 0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh + DB 0F7h,0D8h,0F7h,026h,037h,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah + DB 0F6h,0C2h,004h,074h,006h,0A1h,049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h + DB 006h,0A1h,04Dh,003h,089h,046h,0F8h,0B8h,000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh + DB 0F7h,026h,049h,003h,08Bh,016h,049h,003h,08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh + DB 03Fh,003h,0C5h,036h,023h,003h,08Bh,076h,0F4h,003h,076h,0F6h,08Bh,046h,0FAh + DB 08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,03Ch,02Bh,04Eh,0FCh,07Eh,037h,02Bh,0D0h + DB 02Bh,0D8h,055h,083h,07Eh,006h,000h,075h,018h,08Bh,0E9h,08Bh,0C8h,0F3h,0A4h + DB 003h,0F3h,003h,0FAh,04Dh,075h,0F5h,05Dh,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh + DB 058h,0C9h,0CBh,087h,0F7h,08Bh,0E9h,006h,01Eh,007h,01Fh,08Bh,0C8h,0F3h,0A4h + DB 003h,0F2h,003h,0FBh,04Dh,075h,0F5h,05Dh,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh + DB 058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h + DB 08Ch,0C8h,08Eh,0D8h,08Bh,046h,006h,08Bh,05Eh,008h,02Bh,006h,027h,003h,02Bh + DB 01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,03Fh,003h,003h,01Eh + DB 043h,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0C4h,006h + DB 01Dh,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh + DB 00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh + DB 013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h + DB 0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h + DB 049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h + DB 08Ah,0E0h,084h,0D0h,00Fh,085h,093h,000h,00Ah,0C2h,074h,042h,0F6h,0C4h,008h + DB 074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h + DB 0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h + DB 001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,007h,08Bh + DB 01Eh,049h,003h,089h,05Eh,0FAh,0F6h,0C2h,001h,074h,007h,08Bh,01Eh,04Dh,003h + DB 089h,05Eh,0F8h,08Bh,046h,0FCh,0F7h,026h,049h,003h,08Bh,016h,049h,003h,08Bh + DB 0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h,08Bh,076h,0F4h,003h,076h,0F6h,0B8h + DB 000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,023h + DB 02Bh,04Eh,0FCh,07Eh,01Eh,02Bh,0D0h,02Bh,0D8h,08Ah,0E0h,026h,08Ah,02Ch,066h + DB 046h,00Ah,0EDh,074h,002h,088h,02Dh,047h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h + DB 0FAh,0FEh,0C9h,075h,0E6h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh + DB 0C8h,00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh + DB 0D8h,0E8h,0B7h,002h,089h,056h,0F2h,0FCh,0A1h,02Fh,003h,08Bh,01Eh,033h,003h + DB 02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h + DB 006h,037h,003h,003h,01Eh,03Bh,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h + DB 0F6h,000h,000h,0A1h,023h,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh + DB 0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh + DB 08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h + DB 0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h + DB 02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h + DB 005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,0C7h,000h,00Ah,0C2h,074h + DB 040h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h + DB 046h,0FEh,000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h + DB 0F7h,026h,037h,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h + DB 004h,074h,006h,0A1h,049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h + DB 04Dh,003h,089h,046h,0F8h,0B8h,000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h + DB 049h,003h,003h,046h,0FEh,083h,0D2h,000h,08Bh,0F8h,0E8h,0F1h,001h,08Bh,016h + DB 049h,003h,08Bh,01Eh,03Fh,003h,0C5h,036h,023h,003h,08Bh,076h,0F4h,003h,076h + DB 0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,04Fh,02Bh,04Eh,0FCh + DB 07Eh,04Ah,02Bh,0D0h,02Bh,0D8h,083h,07Eh,006h,000h,075h,021h,08Ah,0E0h,08Ah + DB 02Ch,026h,088h,02Dh,046h,047h,075h,003h,0E8h,0D9h,001h,0FEh,0CCh,075h,0F0h + DB 003h,0F3h,003h,0FAh,073h,003h,0E8h,0CCh,001h,0FEh,0C9h,075h,0E1h,0EBh,01Fh + DB 08Ah,0E0h,026h,08Ah,02Dh,088h,02Ch,046h,047h,075h,003h,0E8h,0B8h,001h,0FEh + DB 0CCh,075h,0F0h,003h,0F3h,003h,0FAh,073h,003h,0E8h,0ABh,001h,0FEh,0C9h,075h + DB 0E1h,08Bh,056h,0F2h,0E8h,06Ch,001h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h + DB 0C9h,0CBh,0C8h,00Eh,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch + DB 0C8h,08Eh,0D8h,0E8h,03Eh,001h,089h,056h,0F2h,08Bh,046h,006h,08Bh,05Eh,008h + DB 02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h + DB 006h,03Fh,003h,003h,01Eh,043h,003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h + DB 0F6h,000h,000h,0C4h,006h,01Dh,003h,089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh + DB 04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h,04Bh,003h + DB 0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh + DB 003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h + DB 034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,0A0h,000h,00Ah,0C2h + DB 074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h + DB 0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h,0FCh,0F7h + DB 0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h + DB 0C2h,004h,074h,006h,0A1h,049h,003h,089h,046h,0FAh,0F6h,0C2h,001h,074h,006h + DB 0A1h,04Dh,003h,089h,046h,0F8h,08Bh,046h,0FCh,0F7h,026h,049h,003h,003h,046h + DB 0FEh,083h,0D2h,000h,08Bh,0F8h,0E8h,07Eh,000h,08Bh,016h,049h,003h,08Bh,01Eh + DB 03Fh,003h,08Bh,076h,0F4h,003h,076h,0F6h,0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h + DB 0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,02Ch,02Bh,04Eh,0FCh,07Eh,027h,02Bh + DB 0D0h,02Bh,0D8h,08Ah,0E0h,026h,08Ah,02Ch,046h,00Ah,0EDh,074h,002h,088h,02Dh + DB 047h,075h,003h,0E8h,067h,000h,0FEh,0CCh,075h,0ECh,003h,0F3h,003h,0FAh,073h + DB 003h,0E8h,05Ah,000h,0FEh,0C9h,075h,0DDh,08Bh,056h,0F2h,0E8h,01Bh,000h,007h + DB 01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,050h,053h,0B8h,005h,04Fh,0B7h + DB 001h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Bh,058h,0C3h,050h,053h,052h,0B8h + DB 005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h + DB 050h,053h,052h,08Bh,0DAh,0C1h,0E3h,002h,02Eh,089h,01Eh,09Eh,00Bh,02Eh,08Bh + DB 097h,061h,003h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h + DB 05Ah,05Bh,058h,0C3h,050h,053h,052h,02Eh,08Bh,01Eh,09Eh,00Bh,083h,0C3h,004h + DB 02Eh,089h,01Eh,09Eh,00Bh,02Eh,08Bh,097h,061h,003h,0B8h,005h,04Fh,0B7h,000h + DB 0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h diff --git a/WWFLAT32/KEYBOARD/KEYSTRUC.INC b/WWFLAT32/KEYBOARD/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WWFLAT32/KEYBOARD/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WWFLAT32/KEYBOARD/MAKEFILE b/WWFLAT32/KEYBOARD/MAKEFILE new file mode 100644 index 0000000..cc8f5e2 --- /dev/null +++ b/WWFLAT32/KEYBOARD/MAKEFILE @@ -0,0 +1,200 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + pagfault.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +keyireal.ibn: keyireal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip keyireal.exe + ebn keyireal.exe + +keyireal.obj: keyireal.asm + tasm /zn /la /ml /m2 keyireal.asm + +keyboard.obj: keyireal.ibn keyboard.asm + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/KEYBOARD/MOUSE.ASM b/WWFLAT32/KEYBOARD/MOUSE.ASM new file mode 100644 index 0000000..818fa9c --- /dev/null +++ b/WWFLAT32/KEYBOARD/MOUSE.ASM @@ -0,0 +1,2226 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "svgaprim.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL RealModePtr:DWORD +GLOBAL MInstalled:DWORD +GLOBAL MouseBuffer:DWORD +GLOBAL LCW_Uncompress:NEAR + +GLOBAL Get_Shape_Uncomp_Size :NEAR +GLOBAL Get_Shape_Width :NEAR +GLOBAL Get_Shape_Original_Height :NEAR +GLOBAL Size_Of_Region :NEAR +GLOBAL _ShapeBuffer :DWORD + +GLOBAL XRes : dword +GLOBAL YRes : dword + + +GLOBAL VesaFunc : dword +GLOBAL Vesa_XRes : near +GLOBAL Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + mov ax,cx + or ax,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR + + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi + + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi + + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi + + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD + + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END diff --git a/WWFLAT32/KEYBOARD/NEW/KEYBOARD.ASM b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.ASM new file mode 100644 index 0000000..f456e48 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.ASM @@ -0,0 +1,2565 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 255 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/NEW/KEYBOARD.BAK b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.BAK new file mode 100644 index 0000000..2db7031 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.BAK @@ -0,0 +1,2547 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 255 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + mov [BYTE PTR 0B0000h],'*' + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + mov [BYTE PTR 0B0000h],'*' + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/NEW/KEYBOARD.H b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.H new file mode 100644 index 0000000..7f06b37 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.H @@ -0,0 +1,447 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : July 13, 1994 * + * * + * Last Update : July 13, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +extern "C" { + void Install_Keyboard_Interrupt(void *addr, long size); + void Install_Page_Fault_Handle(void) ; + void *Get_RM_Keyboard_Address(void); + long Get_RM_Keyboard_Size(void); + void Remove_Keyboard_Interrupt(void); + int Check_Key_Num(void); + int Check_Key_Bits(void); + int Check_Key(void); + short Get_Key_Num(void); + short Get_Key_Bits(void); + int Get_Key(void); + int KN_To_KA(int keynum); + int Keyboard_Attributes_On(int key_flags); + int Keyboard_Attributes_Off(int key_flags); + void Clear_KeyBuffer(void); + int Key_Down(int key); + int Key_Satisfied(int key); + void Stuff_Key_WORD(int code); + void Stuff_Key_Num(int key); + int Install_Mouse(int max_width, int max_height, int scr_width, int scr_height); + void Reset_Mouse (void) ; + void Remove_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + int Get_Mouse_Disabled(void); + void *Set_Mouse_Cursor(int xhotspot, int yhotspot, void *cursor); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int sx_pixel, int sy_pixel, int dx_pixel, int dy_pixel); + void Conditional_Show_Mouse(void); + + + void __interrupt far Keyboard_Interrupt(void); + extern int MouseQX; + extern int MouseQY; + +} + + +enum { + REPEATON = 0x0001, /* 1:all makes into buffer, 0:only 1st make */ + TRACKEXT = 0x0002, /* 1:Home != keypad Home, 0:Home=keypad Home */ + FILTERONLY = 0x0004, /* 1:Normal BIOS operation with filter */ + CTRLALTTURBO = 0x0010, /* 1:Allow turbo up and down in application */ + NONUMLOCK = 0x0200, /* 1:do NOT remap keypad to numbers */ + TASKSWITCHABLE = 0x400, /* 1:allows task switching keys thru ALT-TAB, */ + /* ALT-ESC,CTRL-ESC */ + PASSBREAKS = 0x0800, // Pass all breaks to the keyboard buffer. + + /* The following flags, if turned on, should only be used for + debugging purposes (remember to take out the calls when BETA */ + + CTRLSON = 0x0008, /* 1:pass scroll lock sequence into BIOS */ + CTRLCON = 0x0020, /* 1:pass stop code to BIOS */ + SCROLLLOCKON = 0x0040, /* 1:pass scroll lock key into BIOS */ + PAUSEON = 0x0080, /* 1:pass the pause key and seq to BIOS */ + /* make sure FILTERONLY is set */ + BREAKON = 0x0100, /* 1:pass the ctrl break seq to BIOS */ + KEYMOUSE = 0x1000, /* 1:keypad translates into mouse moves */ + SIMLBUTTON = 0x2000, /* 1:have space and enter keys simulate Left */ + /* mouse button when KEYMOUSE is set */ + DEBUGINT = 0x4000 /* Use scroll lock to disable keyboard int */ +}; + + +/* +** These are the codes for the various key codes that are returned from the +** keyboard input routines Get_Key() and Input_ASCII(). +*/ +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KeyASCIIType; + +/* +** These are the keyboard codes that are returned from the input routines +** Get_Key_Num and Input_Num. +*/ +typedef enum { + KN_NONE = 0, + KN_GRAVE = 1, /* ` */ + KN_1, KN_2, KN_3, KN_4, KN_5, KN_6, KN_7, KN_8, KN_9, KN_0, + KN_MINUS, /* - */ + KN_EQUAL, /* = */ + + KN_RESERVED1, + + KN_BACKSPACE, /* */ + + KN_TAB, /* */ + KN_Q, KN_W, KN_E, KN_R, KN_T, KN_Y, KN_U, KN_I, KN_O, KN_P, + KN_LBRACKET, /* [ */ + KN_RBRACKET, /* ] */ + KN_BACKSLASH, /* \ */ + + KN_CAPSLOCK, /* */ + KN_A, KN_S, KN_D, KN_F, KN_G, KN_H, KN_J, KN_K, KN_L, + KN_SEMICOLON, /* ; */ + KN_SQUOTE, /* ' */ + KN_BACKSLASH2, + KN_RETURN, /* or */ + + KN_LSHIFT, /* */ + + KN_MOUSE_MOVE, // Indicate a mouse move (for playback of + + KN_Z, KN_X, KN_C, KN_V, KN_B, KN_N, KN_M, + KN_COMMA, /* , */ + KN_PERIOD, /* . */ + KN_SLASH, /* / */ + + KN_RESERVED3, + + KN_RSHIFT, /* */ + + KN_LCTRL, /* */ + KN_LCOMM, /* for AMIGA */ + KN_LALT, /* */ + KN_SPACE, /* */ + KN_RALT, /* */ + KN_RCOMM, /* for AMIGA */ + KN_RCTRL, /* */ + /* the following are forced into key buffer */ + KN_LMOUSE, + KN_RMOUSE, + KN_JBUTTON1, + KN_JBUTTON2, + KN_J_UP, + KN_J_RIGHT, + KN_J_DOWN, + KN_J_LEFT, + + KN_SPECIAL9, + + KN_SPECIAL10, + + KN_E_INSERT, /* extended */ + KN_E_DELETE, /* extended */ + + KN_RESERVED4, + KN_RESERVED5, + + KN_E_LEFT, /* extended */ + KN_E_HOME, /* extended */ + KN_E_END, /* extended */ + + KN_RESERVED6, + + KN_E_UP, /* extended */ + KN_E_DOWN, /* extended */ + KN_E_PGUP, /* extended */ + KN_E_PGDN, /* extended */ + KN_K_LPAREN, /* for AMIGA */ + KN_K_RPAREN, /* for AMIGA */ + KN_E_RIGHT, /* extended */ + + KN_NUMLOCK, /* */ + KN_HOME, /* num key pad 7 */ + KN_UPLEFT = KN_HOME, + KN_LEFT, /* num key pad 4 */ + KN_END, /* num key pad 1 */ + KN_DOWNLEFT = KN_END, + + KN_RESERVED7, + + KN_KEYPAD_SLASH, /* num key pad / */ + KN_UP, /* num key pad 8 */ + KN_CENTER, /* num key pad 5 */ + KN_DOWN, /* num key pad 2 */ + KN_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK, /* num key pad * */ + KN_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KN_PGUP, + KN_RIGHT, /* num key pad 6 */ + KN_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KN_PGDN, + KN_DELETE, /* num key pad . */ + KN_KEYPAD_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS, /* num key pad + */ + + KN_RESERVED8, + + KN_KEYPAD_RETURN, /* num key pad */ + + KN_RESERVED9, + + KN_ESC, + KN_HELP, /* for AMIGA */ + KN_F1, KN_F2, KN_F3, KN_F4, KN_F5, KN_F6, KN_F7, KN_F8, KN_F9, KN_F10, + KN_F11, + KN_F12, + KN_PRNTSCRN, /* */ + KN_SCROLLLOCK, /* */ + KN_PAUSE, /* */ + + KN_SHIFT_BIT = 0x0100, + KN_CTRL_BIT = 0x0200, + KN_ALT_BIT = 0x0400, + KN_RLSE_BIT = 0x0800, + KN_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KN_RCOMM_BIT = 0x2000, /* Amiga Right Comm key */ + KN_BUTTON = 0x8000 /* Amiga Right Comm key */ +} KeyNumType; + +#endif diff --git a/WWFLAT32/KEYBOARD/NEW/KEYBOARD.INC b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.INC new file mode 100644 index 0000000..94ed506 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WWFLAT32/KEYBOARD/NEW/KEYIPROT.ASM b/WWFLAT32/KEYBOARD/NEW/KEYIPROT.ASM new file mode 100644 index 0000000..a621713 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYIPROT.ASM @@ -0,0 +1,1023 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] + retf + +STACK ; Don't really need this + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/NEW/KEYIREAL.ASM b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.ASM new file mode 100644 index 0000000..87b6875 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.ASM @@ -0,0 +1,2610 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 255 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 255 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/NEW/KEYIREAL.BAK b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.BAK new file mode 100644 index 0000000..632a2e6 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.BAK @@ -0,0 +1,2571 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 255 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 255 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + mov [BYTE PTR 0B000h:0h],'&' +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + mov [BYTE PTR 0B000h:0h],'&' +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + mov [BYTE PTR 0B000h:0h],'#' + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/NEW/KEYIREAL.IBN b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.IBN new file mode 100644 index 0000000..bd15ece --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYIREAL.IBN @@ -0,0 +1,361 @@ + DB 001h,000h,001h,000h,011h,000h,000h,000h,000h,000h,0FFh,000h,001h,0F0h,000h + DB 010h,0A0h,000h,000h,000h,03Fh,001h,000h,000h,03Fh,001h,045h,000h,03Fh,001h + DB 089h,000h,0A0h,000h,089h,000h,000h,000h,089h,000h,000h,000h,045h,000h,000h + DB 000h,000h,000h,0A0h,000h,045h,000h,001h,002h,004h,008h,010h,020h,040h,080h + DB 020h,002h,020h,003h,00Ch,006h,00Dh,007h,06Ah,006h,069h,006h,030h,002h,030h + DB 003h,07Dh,000h,07Dh,001h,05Ah,002h,05Ah,003h,000h,002h,010h,004h,06Eh,004h + DB 06Eh,002h,07Ch,000h,008h,000h,008h,000h,010h,000h,010h,000h,010h,000h,010h + DB 000h,020h,000h,020h,000h,040h,000h,040h,000h,080h,000h,080h,000h,000h,001h + DB 000h,004h,000h,004h,000h,004h,000h,001h,000h,000h,000h,000h,038h,01Dh,052h + DB 053h,04Bh,047h,04Fh,048h,050h,049h,051h,04Dh,035h,01Ch,037h,046h,03Eh,040h + DB 04Bh,04Ch,04Fh,050h,051h,053h,054h,055h,056h,059h,05Fh,06Ch,07Ch,000h,03Ch + DB 03Ah,063h,068h,05Ch,05Bh,05Dh,060h,062h,065h,067h,066h,037h,02Bh,07Ch,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,06Eh + DB 002h,003h,004h,005h,006h,007h,008h,009h,00Ah,00Bh,00Ch,00Dh,00Fh,010h,011h + DB 012h,013h,014h,015h,016h,017h,018h,019h,01Ah,01Bh,01Ch,02Bh,03Ah,01Fh,020h + DB 021h,022h,023h,024h,025h,026h,027h,028h,029h,001h,02Ch,01Dh,02Eh,02Fh,030h + DB 031h,032h,033h,034h,035h,036h,037h,039h,064h,03Ch,03Dh,01Eh,070h,071h,072h + DB 073h,074h,075h,076h,077h,078h,079h,05Ah,07Dh,05Bh,060h,065h,069h,05Ch,061h + DB 066h,06Ah,05Dh,062h,067h,063h,068h,07Fh,07Fh,07Fh,07Ah,07Bh,000h,000h,0FEh + DB 087h,0FFh,0C0h,01Fh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,038h,0EFh,001h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,01Eh,02Ch,039h,03Ah,03Ch,03Eh,040h,05Ah + DB 080h,080h,080h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,0FFh,001h,000h + DB 000h,000h,000h,000h,0FFh,000h,000h,000h,001h,000h,000h,000h,000h,001h,0FFh + DB 001h,000h,001h,001h,008h,000h,002h,000h,008h,000h,006h,000h,004h,000h,003h + DB 000h,008h,000h,005h,000h,008h,000h,008h,000h,008h,000h,008h,000h,000h,000h + DB 001h,000h,008h,000h,007h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,029h,00Ah,002h,00Dh,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F1h + DB 00Ch,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F4h + DB 00Dh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0AEh,010h + DB 05Ah,00Fh,05Fh,013h,0E6h,011h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,090h,000h,000h,000h,000h,0ADh,0DEh,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,0ADh,0DEh,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0C8h,000h + DB 000h,000h,051h,057h,006h,01Eh,08Ch,0C8h,08Eh,0D8h,08Eh,0C0h,08Bh,046h,006h + DB 0F7h,006h,004h,000h,002h,000h,075h,013h,0B9h,010h,000h,0BFh,094h,000h,0F2h + DB 0AEh,0E3h,009h,0BFh,0B4h,000h,04Fh,02Bh,0F9h,026h,08Ah,005h,01Fh,007h,05Fh + DB 059h,0C9h,0CBh,0C8h,000h,000h,000h,056h,053h,01Eh,08Ch,0C8h,08Eh,0D8h,0A1h + DB 0BAh,001h,08Bh,0F0h,005h,002h,000h,025h,0FFh,000h,039h,006h,0B6h,001h,074h + DB 011h,08Bh,05Eh,006h,089h,09Ch,0B6h,000h,0A3h,0BAh,001h,033h,0C0h,01Fh,05Bh + DB 05Eh,0C9h,0CBh,0B8h,001h,000h,01Fh,05Bh,05Eh,0C9h,0CBh,0C8h,004h,000h,000h + DB 053h,051h,052h,057h,056h,01Eh,09Ch,0FAh,08Ch,0C8h,08Eh,0D8h,0A1h,0ABh,002h + DB 0A3h,0AFh,002h,0A1h,0B1h,002h,0A3h,0B5h,002h,08Bh,046h,006h,00Bh,0C0h,075h + DB 003h,0E9h,0F7h,001h,0F7h,006h,004h,000h,000h,010h,00Fh,084h,03Ah,001h,0F6h + DB 0C4h,004h,00Fh,085h,033h,001h,0F7h,006h,004h,000h,000h,020h,074h,00Ch,03Ch + DB 02Bh,074h,00Ch,03Ch,03Dh,074h,008h,03Ch,06Ch,074h,004h,03Ch,063h,075h,016h + DB 0B0h,041h,080h,00Eh,0F6h,002h,001h,0F6h,0C4h,008h,00Fh,084h,00Ah,001h,080h + DB 026h,0F6h,002h,0FEh,0E9h,002h,001h,03Ch,068h,075h,016h,0B0h,042h,080h,00Eh + DB 0F6h,002h,002h,0F6h,0C4h,008h,00Fh,084h,0F0h,000h,080h,026h,0F6h,002h,0FDh + DB 0E9h,0E8h,000h,0F6h,0C4h,008h,00Fh,085h,0E4h,000h,03Ch,061h,074h,01Eh,03Ch + DB 05Bh,00Fh,082h,0DAh,000h,03Ch,067h,00Fh,087h,0D4h,000h,03Ch,05Dh,076h,00Eh + DB 03Ch,065h,073h,00Ah,03Ch,060h,074h,006h,03Ch,062h,00Fh,085h,0C2h,000h,08Ah + DB 0ECh,032h,0E4h,02Ch,05Bh,08Bh,0D8h,0D1h,0E3h,081h,0C3h,06Fh,002h,08Bh,007h + DB 08Ah,0DCh,098h,093h,098h,093h,0F6h,0C5h,002h,075h,021h,0BAh,001h,000h,0F6h + DB 0C5h,001h,074h,003h,083h,0C2h,003h,003h,0DAh,08Ah,09Fh,00Ah,000h,093h,098h + DB 093h,087h,0DAh,003h,0D8h,08Ah,087h,00Ah,000h,098h,087h,0DAh,0EBh,034h,083h + DB 0E3h,003h,025h,003h,000h,083h,0E3h,003h,025h,003h,000h,0D1h,0E3h,0D1h,0E3h + DB 00Bh,0D8h,0D1h,0E3h,08Bh,09Fh,089h,002h,0D1h,0E3h,0D1h,0E3h,08Bh,087h,010h + DB 000h,08Bh,09Fh,012h,000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h,0A1h,0AFh,002h + DB 08Bh,01Eh,0B5h,002h,0EBh,02Fh,001h,006h,0AFh,002h,079h,005h,033h,0C0h,0A3h + DB 0AFh,002h,001h,01Eh,0B5h,002h,079h,006h,033h,0DBh,089h,01Eh,0B5h,002h,0A1h + DB 0AFh,002h,08Bh,01Eh,0B5h,002h,03Dh,03Fh,001h,07Eh,003h,0B8h,03Fh,001h,081h + DB 0FBh,0C7h,000h,07Eh,003h,0BBh,0C7h,000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h + DB 0A3h,0ABh,002h,089h,01Eh,0B1h,002h,083h,03Eh,0A9h,002h,000h,075h,008h,00Eh + DB 0E8h,0A5h,003h,00Eh,0E8h,0D4h,003h,0B8h,02Dh,000h,089h,046h,006h,08Bh,036h + DB 0BAh,001h,089h,076h,0FEh,08Bh,03Eh,0B6h,001h,050h,00Eh,0E8h,051h,0FEh,083h + DB 0C4h,002h,00Bh,0C0h,075h,011h,08Bh,046h,006h,03Ch,02Dh,074h,00Dh,03Ch,041h + DB 074h,009h,03Ch,042h,074h,005h,0EBh,029h,0E9h,080h,000h,0FFh,036h,0AFh,002h + DB 00Eh,0E8h,02Eh,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0EEh,083h,046h,0FCh,002h + DB 0FFh,036h,0B5h,002h,00Eh,0E8h,01Bh,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0DBh + DB 083h,046h,0FCh,002h,0BBh,001h,001h,08Bh,046h,006h,03Ch,02Dh,074h,01Bh,03Ch + DB 07Fh,074h,017h,0F6h,0C4h,008h,074h,016h,032h,0DBh,0F7h,006h,004h,000h,000h + DB 008h,075h,00Ch,03Ch,041h,074h,008h,03Ch,042h,074h,004h,089h,036h,0BAh,001h + DB 08Bh,0F8h,083h,0E7h,07Fh,0B1h,003h,0D3h,0EFh,08Ah,0C8h,080h,0E1h,007h,0D3h + DB 0E3h,0F6h,0D7h,084h,09Dh,039h,002h,074h,00Ch,0F7h,006h,004h,000h,001h,000h + DB 075h,004h,089h,036h,0BAh,001h,020h,0BDh,039h,002h,008h,09Dh,039h,002h,0B8h + DB 001h,000h,0EBh,008h,08Bh,046h,0FEh,0A3h,0BAh,001h,033h,0C0h,09Dh,01Fh,05Eh + DB 05Fh,05Ah,059h,05Bh,0C9h,0CBh,050h,053h,051h,057h,01Eh,052h,006h,056h,0FCh + DB 08Ch,0C8h,08Eh,0D8h,08Ch,0D1h,089h,026h,08Ah,003h,089h,00Eh,08Ch,003h,0BAh + DB 08Eh,005h,083h,0E2h,0FEh,0FAh,08Eh,0D0h,08Bh,0E2h,0FBh,083h,03Eh,0CEh,002h + DB 000h,0C7h,006h,0CEh,002h,000h,000h,00Fh,085h,003h,002h,08Bh,016h,004h,000h + DB 083h,026h,0BEh,001h,0FCh,0B8h,040h,000h,08Eh,0C0h,026h,0F6h,006h,017h,000h + DB 040h,074h,005h,083h,00Eh,0BEh,001h,001h,026h,0F6h,006h,017h,000h,020h,074h + DB 005h,083h,00Eh,0BEh,001h,002h,0C6h,006h,0B5h,000h,001h,026h,0F6h,006h,096h + DB 000h,010h,075h,005h,0C6h,006h,0B5h,000h,000h,08Ch,0C8h,08Eh,0C0h,0FCh,032h + DB 0E4h,0BBh,001h,001h,0E4h,060h,08Bh,01Eh,05Bh,002h,088h,087h,049h,002h,043h + DB 083h,0E3h,00Fh,089h,01Eh,05Bh,002h,0BBh,001h,001h,03Ch,0E1h,075h,005h,0C6h + DB 006h,05Eh,002h,003h,080h,03Eh,05Eh,002h,000h,074h,00Fh,0FEh,00Eh,05Eh,002h + DB 0F7h,0C2h,080h,000h,00Fh,085h,08Eh,001h,0E9h,0CBh,001h,03Ch,0E0h,075h,008h + DB 0C6h,006h,05Dh,002h,001h,0E9h,07Fh,001h,0A8h,080h,074h,007h,032h,0DBh,024h + DB 07Fh,080h,0CCh,008h,080h,03Eh,05Dh,002h,000h,074h,023h,0C6h,006h,05Dh,002h + DB 000h,0C6h,006h,0B7h,002h,001h,0BFh,084h,000h,0B9h,010h,000h,0F2h,0AEh,0E3h + DB 002h,0EBh,003h,0E9h,094h,001h,08Ah,045h,00Fh,0C6h,006h,0B7h,002h,000h,0EBh + DB 011h,03Ch,07Ah,075h,004h,0B0h,080h,0EBh,009h,08Bh,0F8h,083h,0E7h,07Fh,08Ah + DB 085h,0C0h,001h,0F6h,006h,041h,002h,001h,075h,01Bh,0F6h,006h,040h,002h,004h + DB 074h,017h,03Ch,05Ah,075h,010h,080h,03Eh,0B5h,000h,001h,074h,009h,0F7h,0C2h + DB 080h,000h,075h,003h,0E9h,057h,001h,080h,0CCh,002h,0F6h,006h,040h,002h,050h + DB 074h,003h,080h,0CCh,004h,050h,00Eh,0E8h,043h,0FCh,083h,0C4h,002h,0F6h,006h + DB 03Eh,002h,010h,075h,007h,0F6h,006h,040h,002h,002h,074h,003h,080h,0CCh,001h + DB 08Bh,0F8h,083h,0E7h,07Fh,0D1h,0EFh,0D1h,0EFh,0D1h,0EFh,08Bh,0D8h,083h,0E3h + DB 07Fh,080h,0E3h,007h,08Ah,0AFh,034h,000h,0F7h,006h,0BEh,001h,001h,000h,074h + DB 009h,084h,0ADh,019h,002h,074h,003h,080h,0F4h,001h,0F7h,006h,0BEh,001h,002h + DB 000h,074h,009h,084h,0ADh,029h,002h,074h,003h,080h,0F4h,001h,088h,026h,06Ah + DB 002h,0F7h,0C2h,000h,040h,074h,034h,066h,083h,03Eh,0EAh,002h,001h,075h,01Bh + DB 03Dh,073h,000h,074h,00Ah,03Dh,076h,000h,072h,00Eh,03Dh,078h,000h,077h,009h + DB 066h,0C7h,006h,0EAh,002h,000h,000h,000h,000h,0E9h,08Bh,000h,03Dh,07Dh,000h + DB 075h,00Ch,066h,0C7h,006h,0EAh,002h,001h,000h,000h,000h,0E9h,0BAh,000h,03Dh + DB 068h,006h,074h,075h,03Dh,04Ch,006h,074h,070h,03Dh,030h,002h,074h,005h,03Dh + DB 07Eh,002h,075h,006h,0C7h,006h,008h,000h,001h,000h,03Dh,020h,004h,075h,00Ch + DB 050h,0A1h,000h,000h,035h,001h,000h,050h,083h,0C4h,002h,058h,03Dh,034h,004h + DB 075h,00Ch,050h,0A1h,002h,000h,035h,001h,000h,050h,083h,0C4h,002h,058h,050h + DB 00Eh,0E8h,0DEh,0FBh,058h,03Ch,06Eh,075h,00Dh,083h,03Eh,082h,000h,000h,074h + DB 006h,050h,0FFh,01Eh,080h,000h,058h,0BFh,05Fh,002h,0B9h,00Ah,000h,0F2h,0AEh + DB 00Bh,0C9h,075h,01Bh,0BFh,03Ch,000h,0B9h,022h,000h,0D1h,0E9h,0F2h,0AFh,0E3h + DB 009h,08Bh,05Dh,020h,023h,0DAh,074h,002h,0EBh,006h,0F7h,0C2h,004h,000h,074h + DB 040h,02Eh,0FFh,006h,059h,002h,081h,03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h + DB 057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h + DB 000h,0F3h,0AAh,007h,058h,05Fh,059h,0FAh,08Bh,016h,08Ch,003h,08Eh,0D2h,08Bh + DB 026h,08Ah,003h,0FBh,0BAh,0E8h,00Ch,09Ch,00Eh,052h,09Ch,0FFh,036h,0BCh,002h + DB 0FFh,036h,0BAh,002h,0CFh,0E4h,061h,08Ah,0E0h,00Ch,080h,0E6h,061h,086h,0E0h + DB 0E6h,061h,0B8h,040h,000h,08Eh,0C0h,026h,0A0h,096h,000h,024h,0FDh,026h,0A2h + DB 096h,000h,0B0h,020h,0E6h,020h,081h,03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h + DB 057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h + DB 000h,0F3h,0AAh,007h,058h,05Fh,059h,08Bh,016h,08Ch,003h,0FAh,08Eh,0D2h,08Bh + DB 026h,08Ah,003h,0FBh,05Eh,007h,05Ah,01Fh,05Fh,059h,05Bh,058h,0CFh,09Ch,050h + DB 006h,0B8h,000h,0B0h,08Eh,0C0h,026h,0FEh,006h,000h,000h,007h,058h,09Dh,0CFh + DB 09Ch,00Eh,0E8h,022h,0FDh,0CBh,050h,053h,051h,052h,01Eh,08Ch,0C8h,08Eh,0D8h + DB 080h,03Eh,0F7h,002h,000h,075h,01Dh,083h,03Eh,003h,003h,000h,075h,00Ch,0B8h + DB 000h,000h,050h,00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,083h,006h,003h,003h + DB 001h,083h,016h,003h,003h,000h,01Fh,05Ah,059h,05Bh,058h,0CBh,0C8h,004h,000h + DB 000h,050h,053h,051h,052h,056h,057h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,080h,03Eh + DB 0F7h,002h,000h,075h,050h,083h,03Eh,003h,003h,000h,074h,049h,0FFh,00Eh,003h + DB 003h,083h,03Eh,003h,003h,000h,075h,03Eh,08Bh,00Eh,03Fh,003h,08Bh,016h,043h + DB 003h,0A1h,0ABh,002h,089h,046h,0FEh,08Bh,01Eh,0B1h,002h,089h,05Eh,0FCh,0A3h + DB 02Fh,003h,089h,01Eh,033h,003h,089h,00Eh,037h,003h,089h,016h,03Bh,003h,0B8h + DB 001h,000h,050h,00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,0FFh,076h,0FCh,0FFh + DB 076h,0FEh,00Eh,0FFh,016h,053h,003h,083h,0C4h,004h,007h,01Fh,05Fh,05Eh,05Ah + DB 059h,05Bh,058h,0C9h,0CBh,0C8h,002h,000h,000h,053h,08Ch,0C8h,08Eh,0D8h,0C7h + DB 046h,0FEh,02Dh,000h,08Bh,05Eh,006h,08Bh,0C3h,032h,01Eh,0F6h,002h,074h,029h + DB 0A2h,0F6h,002h,0F6h,0C3h,002h,074h,00Eh,0C7h,046h,0FEh,042h,000h,0A8h,002h + DB 075h,005h,081h,04Eh,0FEh,000h,008h,0F6h,0C3h,001h,074h,00Eh,0C7h,046h,0FEh + DB 041h,000h,0A8h,001h,075h,005h,081h,04Eh,0FEh,000h,008h,08Bh,046h,0FEh,05Bh + DB 0C9h,0CBh,0C8h,004h,000h,000h,050h,053h,051h,052h,01Eh,056h,006h,057h,02Eh + DB 089h,01Eh,090h,005h,02Eh,0A3h,092h,005h,08Ch,0C8h,08Eh,0D8h,08Ch,0D3h,089h + DB 026h,094h,005h,089h,01Eh,096h,005h,0BBh,098h,007h,083h,0E3h,0FEh,0FAh,08Eh + DB 0D0h,08Bh,0E3h,0FBh,080h,03Eh,0F7h,002h,000h,00Fh,085h,0FAh,000h,080h,03Eh + DB 0F8h,002h,000h,00Fh,084h,0F1h,000h,0A1h,092h,005h,025h,01Eh,000h,075h,009h + DB 083h,03Eh,0A9h,002h,000h,00Fh,085h,0E0h,000h,083h,03Eh,0F9h,002h,001h,075h + DB 002h,0D1h,0E9h,03Bh,00Eh,049h,003h,072h,005h,08Bh,00Eh,049h,003h,049h,03Bh + DB 016h,04Dh,003h,072h,005h,08Bh,016h,04Dh,003h,04Ah,089h,00Eh,0ABh,002h,089h + DB 016h,0B1h,002h,066h,0F7h,006h,004h,000h,000h,010h,000h,000h,075h,013h,0FFh + DB 036h,090h,005h,00Eh,0E8h,02Ch,0FFh,083h,0C4h,002h,050h,00Eh,0E8h,07Bh,0F9h + DB 083h,0C4h,002h,083h,03Eh,0A9h,002h,000h,00Fh,085h,092h,000h,083h,03Eh,0FBh + DB 002h,000h,074h,019h,08Bh,0C1h,08Bh,0CAh,02Bh,006h,0FFh,002h,08Bh,01Eh,0FBh + DB 002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,0FFh,002h,08Bh,0D1h,08Bh,0C8h,083h + DB 03Eh,0FDh,002h,000h,074h,015h,08Bh,0C2h,02Bh,006h,001h,003h,08Bh,01Eh,0FDh + DB 002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,001h,003h,08Bh,0D0h,083h,03Eh,003h + DB 003h,000h,075h,047h,039h,00Eh,007h,003h,075h,006h,039h,016h,009h,003h,074h + DB 03Bh,0A1h,00Bh,003h,025h,000h,0C0h,03Dh,000h,0C0h,074h,00Ch,00Eh,0E8h,016h + DB 0FEh,0F7h,006h,00Bh,003h,000h,080h,074h,020h,03Bh,00Eh,00Dh,003h,072h,01Ah + DB 03Bh,00Eh,015h,003h,077h,014h,03Bh,016h,011h,003h,072h,00Eh,03Bh,016h,019h + DB 003h,077h,008h,081h,00Eh,00Bh,003h,000h,040h,0EBh,004h,00Eh,0E8h,01Dh,0FEh + DB 089h,00Eh,007h,003h,089h,016h,009h,003h,081h,03Eh,098h,005h,0ADh,0DEh,074h + DB 017h,051h,057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh + DB 0B8h,001h,000h,0F3h,0AAh,007h,058h,05Fh,059h,0A1h,096h,005h,0FAh,08Eh,0D0h + DB 08Bh,026h,094h,005h,0FBh,05Fh,007h,05Eh,01Fh,05Ah,059h,05Bh,058h,0C9h,0CBh + DB 0C8h,00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh + DB 0D8h,0FCh,0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h,027h,003h,02Bh,01Eh + DB 02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h,003h,003h,01Eh,03Bh + DB 003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0A1h,023h,003h + DB 089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h + DB 003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h + DB 08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh + DB 04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h + DB 084h,0D0h,00Fh,085h,0AEh,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh + DB 08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h + DB 002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,037h,003h,001h,046h + DB 0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h + DB 089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,0B8h + DB 000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h,08Bh,016h,049h,003h + DB 08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h,0C5h,036h,023h,003h,08Bh,076h + DB 0F4h,003h,076h,0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,03Ch + DB 02Bh,04Eh,0FCh,07Eh,037h,02Bh,0D0h,02Bh,0D8h,055h,083h,07Eh,006h,000h,075h + DB 018h,08Bh,0E9h,08Bh,0C8h,0F3h,0A4h,003h,0F3h,003h,0FAh,04Dh,075h,0F5h,05Dh + DB 007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,087h,0F7h,08Bh,0E9h,006h + DB 01Eh,007h,01Fh,08Bh,0C8h,0F3h,0A4h,003h,0F2h,003h,0FBh,04Dh,075h,0F5h,05Dh + DB 007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h + DB 053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,08Bh,046h,006h,08Bh + DB 05Eh,008h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh + DB 0FCh,003h,006h,03Fh,003h,003h,01Eh,043h,003h,089h,046h,0FAh,089h,05Eh,0F8h + DB 0C7h,046h,0F6h,000h,000h,0C4h,006h,01Dh,003h,089h,046h,0F4h,033h,0C0h,033h + DB 0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h + DB 08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h + DB 04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh + DB 00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh + DB 013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,093h,000h + DB 00Ah,0C2h,074h,042h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h + DB 05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h + DB 0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h + DB 05Ah,0F6h,0C2h,004h,074h,007h,08Bh,01Eh,049h,003h,089h,05Eh,0FAh,0F6h,0C2h + DB 001h,074h,007h,08Bh,01Eh,04Dh,003h,089h,05Eh,0F8h,08Bh,046h,0FCh,0F7h,026h + DB 049h,003h,08Bh,016h,049h,003h,08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h + DB 08Bh,076h,0F4h,003h,076h,0F6h,0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh + DB 04Eh,0F8h,02Bh,046h,0FEh,07Eh,023h,02Bh,04Eh,0FCh,07Eh,01Eh,02Bh,0D0h,02Bh + DB 0D8h,08Ah,0E0h,026h,08Ah,02Ch,066h,046h,00Ah,0EDh,074h,002h,088h,02Dh,047h + DB 0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,0FEh,0C9h,075h,0E6h,007h,01Fh,05Eh + DB 05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h,053h,051h,052h + DB 057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h,0B7h,002h,089h,056h,0F2h,0FCh + DB 0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h + DB 089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h,003h,003h,01Eh,03Bh,003h,089h + DB 046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0A1h,023h,003h,089h,046h + DB 0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h + DB 003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh + DB 01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh + DB 08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h + DB 00Fh,085h,0C7h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh + DB 0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h + DB 013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,037h,003h,001h,046h,0F4h,0C7h + DB 046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h,089h,046h + DB 0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,0B8h,000h,0A0h + DB 08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h,003h,046h,0FEh,083h,0D2h,000h + DB 08Bh,0F8h,0E8h,0F1h,001h,08Bh,016h,049h,003h,08Bh,01Eh,03Fh,003h,0C5h,036h + DB 023h,003h,08Bh,076h,0F4h,003h,076h,0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh + DB 046h,0FEh,07Eh,04Fh,02Bh,04Eh,0FCh,07Eh,04Ah,02Bh,0D0h,02Bh,0D8h,083h,07Eh + DB 006h,000h,075h,021h,08Ah,0E0h,08Ah,02Ch,026h,088h,02Dh,046h,047h,075h,003h + DB 0E8h,0D9h,001h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,073h,003h,0E8h,0CCh + DB 001h,0FEh,0C9h,075h,0E1h,0EBh,01Fh,08Ah,0E0h,026h,08Ah,02Dh,088h,02Ch,046h + DB 047h,075h,003h,0E8h,0B8h,001h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,073h + DB 003h,0E8h,0ABh,001h,0FEh,0C9h,075h,0E1h,08Bh,056h,0F2h,0E8h,06Ch,001h,007h + DB 01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Eh,000h,000h,050h,053h + DB 051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h,03Eh,001h,089h,056h + DB 0F2h,08Bh,046h,006h,08Bh,05Eh,008h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h + DB 089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,03Fh,003h,003h,01Eh,043h,003h,089h + DB 046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0C4h,006h,01Dh,003h,089h + DB 046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h + DB 0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h + DB 02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh + DB 04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h + DB 0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h + DB 0D0h,00Fh,085h,0A0h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh + DB 05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h + DB 074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h + DB 0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h,089h + DB 046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,08Bh,046h + DB 0FCh,0F7h,026h,049h,003h,003h,046h,0FEh,083h,0D2h,000h,08Bh,0F8h,0E8h,07Eh + DB 000h,08Bh,016h,049h,003h,08Bh,01Eh,03Fh,003h,08Bh,076h,0F4h,003h,076h,0F6h + DB 0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh + DB 02Ch,02Bh,04Eh,0FCh,07Eh,027h,02Bh,0D0h,02Bh,0D8h,08Ah,0E0h,026h,08Ah,02Ch + DB 046h,00Ah,0EDh,074h,002h,088h,02Dh,047h,075h,003h,0E8h,067h,000h,0FEh,0CCh + DB 075h,0ECh,003h,0F3h,003h,0FAh,073h,003h,0E8h,05Ah,000h,0FEh,0C9h,075h,0DDh + DB 08Bh,056h,0F2h,0E8h,01Bh,000h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h + DB 0CBh,050h,053h,0B8h,005h,04Fh,0B7h,001h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h + DB 05Bh,058h,0C3h,050h,053h,052h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh + DB 01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h,050h,053h,052h,08Bh,0DAh,0C1h,0E3h,002h + DB 02Eh,089h,01Eh,09Ah,007h,02Eh,08Bh,097h,061h,003h,0B8h,005h,04Fh,0B7h,000h + DB 0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h,050h,053h,052h,02Eh + DB 08Bh,01Eh,09Ah,007h,083h,0C3h,004h,02Eh,089h,01Eh,09Ah,007h,02Eh,08Bh,097h + DB 061h,003h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah + DB 05Bh,058h,0C3h diff --git a/WWFLAT32/KEYBOARD/NEW/KEYSTRUC.INC b/WWFLAT32/KEYBOARD/NEW/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WWFLAT32/KEYBOARD/NEW/MAKEFILE b/WWFLAT32/KEYBOARD/NEW/MAKEFILE new file mode 100644 index 0000000..cc8f5e2 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/MAKEFILE @@ -0,0 +1,200 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + pagfault.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +keyireal.ibn: keyireal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip keyireal.exe + ebn keyireal.exe + +keyireal.obj: keyireal.asm + tasm /zn /la /ml /m2 keyireal.asm + +keyboard.obj: keyireal.ibn keyboard.asm + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/KEYBOARD/NEW/MOUSE.ASM b/WWFLAT32/KEYBOARD/NEW/MOUSE.ASM new file mode 100644 index 0000000..818fa9c --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/MOUSE.ASM @@ -0,0 +1,2226 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "svgaprim.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL RealModePtr:DWORD +GLOBAL MInstalled:DWORD +GLOBAL MouseBuffer:DWORD +GLOBAL LCW_Uncompress:NEAR + +GLOBAL Get_Shape_Uncomp_Size :NEAR +GLOBAL Get_Shape_Width :NEAR +GLOBAL Get_Shape_Original_Height :NEAR +GLOBAL Size_Of_Region :NEAR +GLOBAL _ShapeBuffer :DWORD + +GLOBAL XRes : dword +GLOBAL YRes : dword + + +GLOBAL VesaFunc : dword +GLOBAL Vesa_XRes : near +GLOBAL Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + mov ax,cx + or ax,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR + + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi + + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi + + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi + + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD + + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END diff --git a/WWFLAT32/KEYBOARD/NEW/PAGFAULT.ASM b/WWFLAT32/KEYBOARD/NEW/PAGFAULT.ASM new file mode 100644 index 0000000..546dd27 --- /dev/null +++ b/WWFLAT32/KEYBOARD/NEW/PAGFAULT.ASM @@ -0,0 +1,153 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL Install_Page_Fault_Handle : NEAR +GLOBAL Set_Video_Mode : NEAR +GLOBAL Remove_Mouse : NEAR +GLOBAL Remove_Keyboard_Interrupt : NEAR +GLOBAL Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/PAGFAULT.ASM b/WWFLAT32/KEYBOARD/PAGFAULT.ASM new file mode 100644 index 0000000..546dd27 --- /dev/null +++ b/WWFLAT32/KEYBOARD/PAGFAULT.ASM @@ -0,0 +1,153 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL Install_Page_Fault_Handle : NEAR +GLOBAL Set_Video_Mode : NEAR +GLOBAL Remove_Mouse : NEAR +GLOBAL Remove_Keyboard_Interrupt : NEAR +GLOBAL Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/X/KEYBOARD.ASM b/WWFLAT32/KEYBOARD/X/KEYBOARD.ASM new file mode 100644 index 0000000..f456e48 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYBOARD.ASM @@ -0,0 +1,2565 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 255 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/X/KEYBOARD.BAK b/WWFLAT32/KEYBOARD/X/KEYBOARD.BAK new file mode 100644 index 0000000..2db7031 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYBOARD.BAK @@ -0,0 +1,2547 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 255 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + mov [BYTE PTR 0B0000h],'*' + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + mov [BYTE PTR 0B0000h],'*' + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/KEYBOARD/X/KEYBOARD.H b/WWFLAT32/KEYBOARD/X/KEYBOARD.H new file mode 100644 index 0000000..7f06b37 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYBOARD.H @@ -0,0 +1,447 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : July 13, 1994 * + * * + * Last Update : July 13, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +extern "C" { + void Install_Keyboard_Interrupt(void *addr, long size); + void Install_Page_Fault_Handle(void) ; + void *Get_RM_Keyboard_Address(void); + long Get_RM_Keyboard_Size(void); + void Remove_Keyboard_Interrupt(void); + int Check_Key_Num(void); + int Check_Key_Bits(void); + int Check_Key(void); + short Get_Key_Num(void); + short Get_Key_Bits(void); + int Get_Key(void); + int KN_To_KA(int keynum); + int Keyboard_Attributes_On(int key_flags); + int Keyboard_Attributes_Off(int key_flags); + void Clear_KeyBuffer(void); + int Key_Down(int key); + int Key_Satisfied(int key); + void Stuff_Key_WORD(int code); + void Stuff_Key_Num(int key); + int Install_Mouse(int max_width, int max_height, int scr_width, int scr_height); + void Reset_Mouse (void) ; + void Remove_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + int Get_Mouse_Disabled(void); + void *Set_Mouse_Cursor(int xhotspot, int yhotspot, void *cursor); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int sx_pixel, int sy_pixel, int dx_pixel, int dy_pixel); + void Conditional_Show_Mouse(void); + + + void __interrupt far Keyboard_Interrupt(void); + extern int MouseQX; + extern int MouseQY; + +} + + +enum { + REPEATON = 0x0001, /* 1:all makes into buffer, 0:only 1st make */ + TRACKEXT = 0x0002, /* 1:Home != keypad Home, 0:Home=keypad Home */ + FILTERONLY = 0x0004, /* 1:Normal BIOS operation with filter */ + CTRLALTTURBO = 0x0010, /* 1:Allow turbo up and down in application */ + NONUMLOCK = 0x0200, /* 1:do NOT remap keypad to numbers */ + TASKSWITCHABLE = 0x400, /* 1:allows task switching keys thru ALT-TAB, */ + /* ALT-ESC,CTRL-ESC */ + PASSBREAKS = 0x0800, // Pass all breaks to the keyboard buffer. + + /* The following flags, if turned on, should only be used for + debugging purposes (remember to take out the calls when BETA */ + + CTRLSON = 0x0008, /* 1:pass scroll lock sequence into BIOS */ + CTRLCON = 0x0020, /* 1:pass stop code to BIOS */ + SCROLLLOCKON = 0x0040, /* 1:pass scroll lock key into BIOS */ + PAUSEON = 0x0080, /* 1:pass the pause key and seq to BIOS */ + /* make sure FILTERONLY is set */ + BREAKON = 0x0100, /* 1:pass the ctrl break seq to BIOS */ + KEYMOUSE = 0x1000, /* 1:keypad translates into mouse moves */ + SIMLBUTTON = 0x2000, /* 1:have space and enter keys simulate Left */ + /* mouse button when KEYMOUSE is set */ + DEBUGINT = 0x4000 /* Use scroll lock to disable keyboard int */ +}; + + +/* +** These are the codes for the various key codes that are returned from the +** keyboard input routines Get_Key() and Input_ASCII(). +*/ +typedef enum { + KA_CTRL_AT = 0, + KA_CTRL_A, + KA_MORE = KA_CTRL_A, + KA_CTRL_B, + KA_SETBKGDCOL = KA_CTRL_B, + KA_CTRL_C, + KA_CTRL_D, + KA_CTRL_E, + KA_CTRL_F, + KA_SETFORECOL = KA_CTRL_F, + KA_CTRL_G, + KA_CTRL_H, + KA_BACKSPACE = KA_CTRL_H, + KA_CTRL_I, + KA_TAB = KA_CTRL_I, + KA_CTRL_J, + KA_CTRL_K, + KA_CTRL_L, + KA_FORMFEED = KA_CTRL_L, + KA_CTRL_M, + KA_RETURN = KA_CTRL_M, + KA_CTRL_N, + + KA_CTRL_O, + KA_CTRL_P, + KA_CTRL_Q, + KA_CTRL_R, + KA_CTRL_S, + KA_SPCTAB = KA_CTRL_S, + KA_CTRL_T, + KA_CTRL_U, + KA_CTRL_V, + KA_CTRL_W, + KA_CTRL_X, + KA_SETX = KA_CTRL_X, + KA_CTRL_Y, + KA_SETY = KA_CTRL_Y, + KA_CTRL_Z, + KA_CTRL_LBRACKET, + KA_ESC = KA_CTRL_LBRACKET, + KA_EXTEND = KA_ESC, + KA_CTRL_BACKSLASH, + KA_CTRL_RBRACKET, + KA_LITERAL = KA_CTRL_RBRACKET, + KA_CTRL_CARROT, + KA_CTRL_UNDERLINE, + + KA_SPACE, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + KA_DEL, /* not used */ + + KA_ALT_F10 = 143, + KA_ALT_F9, KA_ALT_F8, KA_ALT_F7, KA_ALT_F6, KA_ALT_F5, + KA_ALT_F4, KA_ALT_F3, KA_ALT_F2, KA_ALT_F1, + + KA_CTRL_F10, KA_CTRL_F9, KA_CTRL_F8, KA_CTRL_F7, KA_CTRL_F6, + KA_CTRL_F5, KA_CTRL_F4, KA_CTRL_F3, KA_CTRL_F2, KA_CTRL_F1, + + KA_SHIFT_F10, KA_SHIFT_F9, KA_SHIFT_F8, KA_SHIFT_F7, KA_SHIFT_F6, + KA_SHIFT_F5, KA_SHIFT_F4, KA_SHIFT_F3, KA_SHIFT_F2, KA_SHIFT_F1, + + KA_DELETE, /* */ + KA_INSERT, /* */ + KA_PGDN, /* */ + KA_DOWNRIGHT = KA_PGDN, + KA_DOWN, /* */ + KA_END, /* */ + KA_DOWNLEFT = KA_END, + + KA_RESERVED1, + + KA_RIGHT, /* */ + KA_KEYPAD5, /* NUMERIC KEY PAD <5> */ + KA_LEFT, /* */ + + KA_RESERVED2, + + KA_PGUP, /* */ + KA_UPRIGHT = KA_PGUP, + KA_UP, /* */ + KA_HOME, /* */ + KA_UPLEFT = KA_HOME, + + KA_RESERVED3, + KA_RESERVED4, + + KA_F10, KA_F9, KA_F8, KA_F7, KA_F6, KA_F5, KA_F4, KA_F3, KA_F2, KA_F1, + + KA_LMOUSE, + KA_RMOUSE, + KA_JBUTTON1, + KA_JBUTTON2, + KA_J_UP, + KA_J_RIGHT, + KA_J_DOWN, + KA_J_LEFT, + + KA_SHIFT_BIT = 0x0100, + KA_CTRL_BIT = 0x0200, + KA_ALT_BIT = 0x0400, + KA_RLSE_BIT = 0x0800, + KA_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KA_RCOMM_BIT = 0x2000 /* Amiga Right Comm key */ +} KeyASCIIType; + +/* +** These are the keyboard codes that are returned from the input routines +** Get_Key_Num and Input_Num. +*/ +typedef enum { + KN_NONE = 0, + KN_GRAVE = 1, /* ` */ + KN_1, KN_2, KN_3, KN_4, KN_5, KN_6, KN_7, KN_8, KN_9, KN_0, + KN_MINUS, /* - */ + KN_EQUAL, /* = */ + + KN_RESERVED1, + + KN_BACKSPACE, /* */ + + KN_TAB, /* */ + KN_Q, KN_W, KN_E, KN_R, KN_T, KN_Y, KN_U, KN_I, KN_O, KN_P, + KN_LBRACKET, /* [ */ + KN_RBRACKET, /* ] */ + KN_BACKSLASH, /* \ */ + + KN_CAPSLOCK, /* */ + KN_A, KN_S, KN_D, KN_F, KN_G, KN_H, KN_J, KN_K, KN_L, + KN_SEMICOLON, /* ; */ + KN_SQUOTE, /* ' */ + KN_BACKSLASH2, + KN_RETURN, /* or */ + + KN_LSHIFT, /* */ + + KN_MOUSE_MOVE, // Indicate a mouse move (for playback of + + KN_Z, KN_X, KN_C, KN_V, KN_B, KN_N, KN_M, + KN_COMMA, /* , */ + KN_PERIOD, /* . */ + KN_SLASH, /* / */ + + KN_RESERVED3, + + KN_RSHIFT, /* */ + + KN_LCTRL, /* */ + KN_LCOMM, /* for AMIGA */ + KN_LALT, /* */ + KN_SPACE, /* */ + KN_RALT, /* */ + KN_RCOMM, /* for AMIGA */ + KN_RCTRL, /* */ + /* the following are forced into key buffer */ + KN_LMOUSE, + KN_RMOUSE, + KN_JBUTTON1, + KN_JBUTTON2, + KN_J_UP, + KN_J_RIGHT, + KN_J_DOWN, + KN_J_LEFT, + + KN_SPECIAL9, + + KN_SPECIAL10, + + KN_E_INSERT, /* extended */ + KN_E_DELETE, /* extended */ + + KN_RESERVED4, + KN_RESERVED5, + + KN_E_LEFT, /* extended */ + KN_E_HOME, /* extended */ + KN_E_END, /* extended */ + + KN_RESERVED6, + + KN_E_UP, /* extended */ + KN_E_DOWN, /* extended */ + KN_E_PGUP, /* extended */ + KN_E_PGDN, /* extended */ + KN_K_LPAREN, /* for AMIGA */ + KN_K_RPAREN, /* for AMIGA */ + KN_E_RIGHT, /* extended */ + + KN_NUMLOCK, /* */ + KN_HOME, /* num key pad 7 */ + KN_UPLEFT = KN_HOME, + KN_LEFT, /* num key pad 4 */ + KN_END, /* num key pad 1 */ + KN_DOWNLEFT = KN_END, + + KN_RESERVED7, + + KN_KEYPAD_SLASH, /* num key pad / */ + KN_UP, /* num key pad 8 */ + KN_CENTER, /* num key pad 5 */ + KN_DOWN, /* num key pad 2 */ + KN_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK, /* num key pad * */ + KN_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KN_PGUP, + KN_RIGHT, /* num key pad 6 */ + KN_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KN_PGDN, + KN_DELETE, /* num key pad . */ + KN_KEYPAD_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS, /* num key pad + */ + + KN_RESERVED8, + + KN_KEYPAD_RETURN, /* num key pad */ + + KN_RESERVED9, + + KN_ESC, + KN_HELP, /* for AMIGA */ + KN_F1, KN_F2, KN_F3, KN_F4, KN_F5, KN_F6, KN_F7, KN_F8, KN_F9, KN_F10, + KN_F11, + KN_F12, + KN_PRNTSCRN, /* */ + KN_SCROLLLOCK, /* */ + KN_PAUSE, /* */ + + KN_SHIFT_BIT = 0x0100, + KN_CTRL_BIT = 0x0200, + KN_ALT_BIT = 0x0400, + KN_RLSE_BIT = 0x0800, + KN_LCOMM_BIT = 0x1000, /* Amiga Left Comm key */ + KN_RCOMM_BIT = 0x2000, /* Amiga Right Comm key */ + KN_BUTTON = 0x8000 /* Amiga Right Comm key */ +} KeyNumType; + +#endif diff --git a/WWFLAT32/KEYBOARD/X/KEYBOARD.INC b/WWFLAT32/KEYBOARD/X/KEYBOARD.INC new file mode 100644 index 0000000..94ed506 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYBOARD.INC @@ -0,0 +1,129 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/WWFLAT32/KEYBOARD/X/KEYIPROT.ASM b/WWFLAT32/KEYBOARD/X/KEYIPROT.ASM new file mode 100644 index 0000000..a621713 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYIPROT.ASM @@ -0,0 +1,1023 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] + retf + +STACK ; Don't really need this + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/X/KEYIREAL.ASM b/WWFLAT32/KEYBOARD/X/KEYIREAL.ASM new file mode 100644 index 0000000..87b6875 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYIREAL.ASM @@ -0,0 +1,2610 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 255 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 255 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/X/KEYIREAL.BAK b/WWFLAT32/KEYBOARD/X/KEYIREAL.BAK new file mode 100644 index 0000000..632a2e6 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYIREAL.BAK @@ -0,0 +1,2571 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 255 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 255 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + mov [BYTE PTR 0B000h:0h],'&' +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + mov [BYTE PTR 0B000h:0h],'&' +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + mov [BYTE PTR 0B000h:0h],'#' + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/KEYBOARD/X/KEYIREAL.IBN b/WWFLAT32/KEYBOARD/X/KEYIREAL.IBN new file mode 100644 index 0000000..bd15ece --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYIREAL.IBN @@ -0,0 +1,361 @@ + DB 001h,000h,001h,000h,011h,000h,000h,000h,000h,000h,0FFh,000h,001h,0F0h,000h + DB 010h,0A0h,000h,000h,000h,03Fh,001h,000h,000h,03Fh,001h,045h,000h,03Fh,001h + DB 089h,000h,0A0h,000h,089h,000h,000h,000h,089h,000h,000h,000h,045h,000h,000h + DB 000h,000h,000h,0A0h,000h,045h,000h,001h,002h,004h,008h,010h,020h,040h,080h + DB 020h,002h,020h,003h,00Ch,006h,00Dh,007h,06Ah,006h,069h,006h,030h,002h,030h + DB 003h,07Dh,000h,07Dh,001h,05Ah,002h,05Ah,003h,000h,002h,010h,004h,06Eh,004h + DB 06Eh,002h,07Ch,000h,008h,000h,008h,000h,010h,000h,010h,000h,010h,000h,010h + DB 000h,020h,000h,020h,000h,040h,000h,040h,000h,080h,000h,080h,000h,000h,001h + DB 000h,004h,000h,004h,000h,004h,000h,001h,000h,000h,000h,000h,038h,01Dh,052h + DB 053h,04Bh,047h,04Fh,048h,050h,049h,051h,04Dh,035h,01Ch,037h,046h,03Eh,040h + DB 04Bh,04Ch,04Fh,050h,051h,053h,054h,055h,056h,059h,05Fh,06Ch,07Ch,000h,03Ch + DB 03Ah,063h,068h,05Ch,05Bh,05Dh,060h,062h,065h,067h,066h,037h,02Bh,07Ch,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,07Fh,06Eh + DB 002h,003h,004h,005h,006h,007h,008h,009h,00Ah,00Bh,00Ch,00Dh,00Fh,010h,011h + DB 012h,013h,014h,015h,016h,017h,018h,019h,01Ah,01Bh,01Ch,02Bh,03Ah,01Fh,020h + DB 021h,022h,023h,024h,025h,026h,027h,028h,029h,001h,02Ch,01Dh,02Eh,02Fh,030h + DB 031h,032h,033h,034h,035h,036h,037h,039h,064h,03Ch,03Dh,01Eh,070h,071h,072h + DB 073h,074h,075h,076h,077h,078h,079h,05Ah,07Dh,05Bh,060h,065h,069h,05Ch,061h + DB 066h,06Ah,05Dh,062h,067h,063h,068h,07Fh,07Fh,07Fh,07Ah,07Bh,000h,000h,0FEh + DB 087h,0FFh,0C0h,01Fh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,038h,0EFh,001h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,01Eh,02Ch,039h,03Ah,03Ch,03Eh,040h,05Ah + DB 080h,080h,080h,000h,000h,000h,000h,000h,0FFh,0FFh,0FFh,000h,0FFh,001h,000h + DB 000h,000h,000h,000h,0FFh,000h,000h,000h,001h,000h,000h,000h,000h,001h,0FFh + DB 001h,000h,001h,001h,008h,000h,002h,000h,008h,000h,006h,000h,004h,000h,003h + DB 000h,008h,000h,005h,000h,008h,000h,008h,000h,008h,000h,008h,000h,000h,000h + DB 001h,000h,008h,000h,007h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,029h,00Ah,002h,00Dh,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F1h + DB 00Ch,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,001h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0F4h + DB 00Dh,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0AEh,010h + DB 05Ah,00Fh,05Fh,013h,0E6h,011h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,090h,000h,000h,000h,000h,0ADh,0DEh,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,0ADh,0DEh,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,0C8h,000h + DB 000h,000h,051h,057h,006h,01Eh,08Ch,0C8h,08Eh,0D8h,08Eh,0C0h,08Bh,046h,006h + DB 0F7h,006h,004h,000h,002h,000h,075h,013h,0B9h,010h,000h,0BFh,094h,000h,0F2h + DB 0AEh,0E3h,009h,0BFh,0B4h,000h,04Fh,02Bh,0F9h,026h,08Ah,005h,01Fh,007h,05Fh + DB 059h,0C9h,0CBh,0C8h,000h,000h,000h,056h,053h,01Eh,08Ch,0C8h,08Eh,0D8h,0A1h + DB 0BAh,001h,08Bh,0F0h,005h,002h,000h,025h,0FFh,000h,039h,006h,0B6h,001h,074h + DB 011h,08Bh,05Eh,006h,089h,09Ch,0B6h,000h,0A3h,0BAh,001h,033h,0C0h,01Fh,05Bh + DB 05Eh,0C9h,0CBh,0B8h,001h,000h,01Fh,05Bh,05Eh,0C9h,0CBh,0C8h,004h,000h,000h + DB 053h,051h,052h,057h,056h,01Eh,09Ch,0FAh,08Ch,0C8h,08Eh,0D8h,0A1h,0ABh,002h + DB 0A3h,0AFh,002h,0A1h,0B1h,002h,0A3h,0B5h,002h,08Bh,046h,006h,00Bh,0C0h,075h + DB 003h,0E9h,0F7h,001h,0F7h,006h,004h,000h,000h,010h,00Fh,084h,03Ah,001h,0F6h + DB 0C4h,004h,00Fh,085h,033h,001h,0F7h,006h,004h,000h,000h,020h,074h,00Ch,03Ch + DB 02Bh,074h,00Ch,03Ch,03Dh,074h,008h,03Ch,06Ch,074h,004h,03Ch,063h,075h,016h + DB 0B0h,041h,080h,00Eh,0F6h,002h,001h,0F6h,0C4h,008h,00Fh,084h,00Ah,001h,080h + DB 026h,0F6h,002h,0FEh,0E9h,002h,001h,03Ch,068h,075h,016h,0B0h,042h,080h,00Eh + DB 0F6h,002h,002h,0F6h,0C4h,008h,00Fh,084h,0F0h,000h,080h,026h,0F6h,002h,0FDh + DB 0E9h,0E8h,000h,0F6h,0C4h,008h,00Fh,085h,0E4h,000h,03Ch,061h,074h,01Eh,03Ch + DB 05Bh,00Fh,082h,0DAh,000h,03Ch,067h,00Fh,087h,0D4h,000h,03Ch,05Dh,076h,00Eh + DB 03Ch,065h,073h,00Ah,03Ch,060h,074h,006h,03Ch,062h,00Fh,085h,0C2h,000h,08Ah + DB 0ECh,032h,0E4h,02Ch,05Bh,08Bh,0D8h,0D1h,0E3h,081h,0C3h,06Fh,002h,08Bh,007h + DB 08Ah,0DCh,098h,093h,098h,093h,0F6h,0C5h,002h,075h,021h,0BAh,001h,000h,0F6h + DB 0C5h,001h,074h,003h,083h,0C2h,003h,003h,0DAh,08Ah,09Fh,00Ah,000h,093h,098h + DB 093h,087h,0DAh,003h,0D8h,08Ah,087h,00Ah,000h,098h,087h,0DAh,0EBh,034h,083h + DB 0E3h,003h,025h,003h,000h,083h,0E3h,003h,025h,003h,000h,0D1h,0E3h,0D1h,0E3h + DB 00Bh,0D8h,0D1h,0E3h,08Bh,09Fh,089h,002h,0D1h,0E3h,0D1h,0E3h,08Bh,087h,010h + DB 000h,08Bh,09Fh,012h,000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h,0A1h,0AFh,002h + DB 08Bh,01Eh,0B5h,002h,0EBh,02Fh,001h,006h,0AFh,002h,079h,005h,033h,0C0h,0A3h + DB 0AFh,002h,001h,01Eh,0B5h,002h,079h,006h,033h,0DBh,089h,01Eh,0B5h,002h,0A1h + DB 0AFh,002h,08Bh,01Eh,0B5h,002h,03Dh,03Fh,001h,07Eh,003h,0B8h,03Fh,001h,081h + DB 0FBh,0C7h,000h,07Eh,003h,0BBh,0C7h,000h,0A3h,0AFh,002h,089h,01Eh,0B5h,002h + DB 0A3h,0ABh,002h,089h,01Eh,0B1h,002h,083h,03Eh,0A9h,002h,000h,075h,008h,00Eh + DB 0E8h,0A5h,003h,00Eh,0E8h,0D4h,003h,0B8h,02Dh,000h,089h,046h,006h,08Bh,036h + DB 0BAh,001h,089h,076h,0FEh,08Bh,03Eh,0B6h,001h,050h,00Eh,0E8h,051h,0FEh,083h + DB 0C4h,002h,00Bh,0C0h,075h,011h,08Bh,046h,006h,03Ch,02Dh,074h,00Dh,03Ch,041h + DB 074h,009h,03Ch,042h,074h,005h,0EBh,029h,0E9h,080h,000h,0FFh,036h,0AFh,002h + DB 00Eh,0E8h,02Eh,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0EEh,083h,046h,0FCh,002h + DB 0FFh,036h,0B5h,002h,00Eh,0E8h,01Bh,0FEh,083h,0C4h,002h,00Bh,0C0h,075h,0DBh + DB 083h,046h,0FCh,002h,0BBh,001h,001h,08Bh,046h,006h,03Ch,02Dh,074h,01Bh,03Ch + DB 07Fh,074h,017h,0F6h,0C4h,008h,074h,016h,032h,0DBh,0F7h,006h,004h,000h,000h + DB 008h,075h,00Ch,03Ch,041h,074h,008h,03Ch,042h,074h,004h,089h,036h,0BAh,001h + DB 08Bh,0F8h,083h,0E7h,07Fh,0B1h,003h,0D3h,0EFh,08Ah,0C8h,080h,0E1h,007h,0D3h + DB 0E3h,0F6h,0D7h,084h,09Dh,039h,002h,074h,00Ch,0F7h,006h,004h,000h,001h,000h + DB 075h,004h,089h,036h,0BAh,001h,020h,0BDh,039h,002h,008h,09Dh,039h,002h,0B8h + DB 001h,000h,0EBh,008h,08Bh,046h,0FEh,0A3h,0BAh,001h,033h,0C0h,09Dh,01Fh,05Eh + DB 05Fh,05Ah,059h,05Bh,0C9h,0CBh,050h,053h,051h,057h,01Eh,052h,006h,056h,0FCh + DB 08Ch,0C8h,08Eh,0D8h,08Ch,0D1h,089h,026h,08Ah,003h,089h,00Eh,08Ch,003h,0BAh + DB 08Eh,005h,083h,0E2h,0FEh,0FAh,08Eh,0D0h,08Bh,0E2h,0FBh,083h,03Eh,0CEh,002h + DB 000h,0C7h,006h,0CEh,002h,000h,000h,00Fh,085h,003h,002h,08Bh,016h,004h,000h + DB 083h,026h,0BEh,001h,0FCh,0B8h,040h,000h,08Eh,0C0h,026h,0F6h,006h,017h,000h + DB 040h,074h,005h,083h,00Eh,0BEh,001h,001h,026h,0F6h,006h,017h,000h,020h,074h + DB 005h,083h,00Eh,0BEh,001h,002h,0C6h,006h,0B5h,000h,001h,026h,0F6h,006h,096h + DB 000h,010h,075h,005h,0C6h,006h,0B5h,000h,000h,08Ch,0C8h,08Eh,0C0h,0FCh,032h + DB 0E4h,0BBh,001h,001h,0E4h,060h,08Bh,01Eh,05Bh,002h,088h,087h,049h,002h,043h + DB 083h,0E3h,00Fh,089h,01Eh,05Bh,002h,0BBh,001h,001h,03Ch,0E1h,075h,005h,0C6h + DB 006h,05Eh,002h,003h,080h,03Eh,05Eh,002h,000h,074h,00Fh,0FEh,00Eh,05Eh,002h + DB 0F7h,0C2h,080h,000h,00Fh,085h,08Eh,001h,0E9h,0CBh,001h,03Ch,0E0h,075h,008h + DB 0C6h,006h,05Dh,002h,001h,0E9h,07Fh,001h,0A8h,080h,074h,007h,032h,0DBh,024h + DB 07Fh,080h,0CCh,008h,080h,03Eh,05Dh,002h,000h,074h,023h,0C6h,006h,05Dh,002h + DB 000h,0C6h,006h,0B7h,002h,001h,0BFh,084h,000h,0B9h,010h,000h,0F2h,0AEh,0E3h + DB 002h,0EBh,003h,0E9h,094h,001h,08Ah,045h,00Fh,0C6h,006h,0B7h,002h,000h,0EBh + DB 011h,03Ch,07Ah,075h,004h,0B0h,080h,0EBh,009h,08Bh,0F8h,083h,0E7h,07Fh,08Ah + DB 085h,0C0h,001h,0F6h,006h,041h,002h,001h,075h,01Bh,0F6h,006h,040h,002h,004h + DB 074h,017h,03Ch,05Ah,075h,010h,080h,03Eh,0B5h,000h,001h,074h,009h,0F7h,0C2h + DB 080h,000h,075h,003h,0E9h,057h,001h,080h,0CCh,002h,0F6h,006h,040h,002h,050h + DB 074h,003h,080h,0CCh,004h,050h,00Eh,0E8h,043h,0FCh,083h,0C4h,002h,0F6h,006h + DB 03Eh,002h,010h,075h,007h,0F6h,006h,040h,002h,002h,074h,003h,080h,0CCh,001h + DB 08Bh,0F8h,083h,0E7h,07Fh,0D1h,0EFh,0D1h,0EFh,0D1h,0EFh,08Bh,0D8h,083h,0E3h + DB 07Fh,080h,0E3h,007h,08Ah,0AFh,034h,000h,0F7h,006h,0BEh,001h,001h,000h,074h + DB 009h,084h,0ADh,019h,002h,074h,003h,080h,0F4h,001h,0F7h,006h,0BEh,001h,002h + DB 000h,074h,009h,084h,0ADh,029h,002h,074h,003h,080h,0F4h,001h,088h,026h,06Ah + DB 002h,0F7h,0C2h,000h,040h,074h,034h,066h,083h,03Eh,0EAh,002h,001h,075h,01Bh + DB 03Dh,073h,000h,074h,00Ah,03Dh,076h,000h,072h,00Eh,03Dh,078h,000h,077h,009h + DB 066h,0C7h,006h,0EAh,002h,000h,000h,000h,000h,0E9h,08Bh,000h,03Dh,07Dh,000h + DB 075h,00Ch,066h,0C7h,006h,0EAh,002h,001h,000h,000h,000h,0E9h,0BAh,000h,03Dh + DB 068h,006h,074h,075h,03Dh,04Ch,006h,074h,070h,03Dh,030h,002h,074h,005h,03Dh + DB 07Eh,002h,075h,006h,0C7h,006h,008h,000h,001h,000h,03Dh,020h,004h,075h,00Ch + DB 050h,0A1h,000h,000h,035h,001h,000h,050h,083h,0C4h,002h,058h,03Dh,034h,004h + DB 075h,00Ch,050h,0A1h,002h,000h,035h,001h,000h,050h,083h,0C4h,002h,058h,050h + DB 00Eh,0E8h,0DEh,0FBh,058h,03Ch,06Eh,075h,00Dh,083h,03Eh,082h,000h,000h,074h + DB 006h,050h,0FFh,01Eh,080h,000h,058h,0BFh,05Fh,002h,0B9h,00Ah,000h,0F2h,0AEh + DB 00Bh,0C9h,075h,01Bh,0BFh,03Ch,000h,0B9h,022h,000h,0D1h,0E9h,0F2h,0AFh,0E3h + DB 009h,08Bh,05Dh,020h,023h,0DAh,074h,002h,0EBh,006h,0F7h,0C2h,004h,000h,074h + DB 040h,02Eh,0FFh,006h,059h,002h,081h,03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h + DB 057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h + DB 000h,0F3h,0AAh,007h,058h,05Fh,059h,0FAh,08Bh,016h,08Ch,003h,08Eh,0D2h,08Bh + DB 026h,08Ah,003h,0FBh,0BAh,0E8h,00Ch,09Ch,00Eh,052h,09Ch,0FFh,036h,0BCh,002h + DB 0FFh,036h,0BAh,002h,0CFh,0E4h,061h,08Ah,0E0h,00Ch,080h,0E6h,061h,086h,0E0h + DB 0E6h,061h,0B8h,040h,000h,08Eh,0C0h,026h,0A0h,096h,000h,024h,0FDh,026h,0A2h + DB 096h,000h,0B0h,020h,0E6h,020h,081h,03Eh,08Eh,003h,0ADh,0DEh,074h,017h,051h + DB 057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh,0B8h,001h + DB 000h,0F3h,0AAh,007h,058h,05Fh,059h,08Bh,016h,08Ch,003h,0FAh,08Eh,0D2h,08Bh + DB 026h,08Ah,003h,0FBh,05Eh,007h,05Ah,01Fh,05Fh,059h,05Bh,058h,0CFh,09Ch,050h + DB 006h,0B8h,000h,0B0h,08Eh,0C0h,026h,0FEh,006h,000h,000h,007h,058h,09Dh,0CFh + DB 09Ch,00Eh,0E8h,022h,0FDh,0CBh,050h,053h,051h,052h,01Eh,08Ch,0C8h,08Eh,0D8h + DB 080h,03Eh,0F7h,002h,000h,075h,01Dh,083h,03Eh,003h,003h,000h,075h,00Ch,0B8h + DB 000h,000h,050h,00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,083h,006h,003h,003h + DB 001h,083h,016h,003h,003h,000h,01Fh,05Ah,059h,05Bh,058h,0CBh,0C8h,004h,000h + DB 000h,050h,053h,051h,052h,056h,057h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,080h,03Eh + DB 0F7h,002h,000h,075h,050h,083h,03Eh,003h,003h,000h,074h,049h,0FFh,00Eh,003h + DB 003h,083h,03Eh,003h,003h,000h,075h,03Eh,08Bh,00Eh,03Fh,003h,08Bh,016h,043h + DB 003h,0A1h,0ABh,002h,089h,046h,0FEh,08Bh,01Eh,0B1h,002h,089h,05Eh,0FCh,0A3h + DB 02Fh,003h,089h,01Eh,033h,003h,089h,00Eh,037h,003h,089h,016h,03Bh,003h,0B8h + DB 001h,000h,050h,00Eh,0FFh,016h,051h,003h,083h,0C4h,002h,0FFh,076h,0FCh,0FFh + DB 076h,0FEh,00Eh,0FFh,016h,053h,003h,083h,0C4h,004h,007h,01Fh,05Fh,05Eh,05Ah + DB 059h,05Bh,058h,0C9h,0CBh,0C8h,002h,000h,000h,053h,08Ch,0C8h,08Eh,0D8h,0C7h + DB 046h,0FEh,02Dh,000h,08Bh,05Eh,006h,08Bh,0C3h,032h,01Eh,0F6h,002h,074h,029h + DB 0A2h,0F6h,002h,0F6h,0C3h,002h,074h,00Eh,0C7h,046h,0FEh,042h,000h,0A8h,002h + DB 075h,005h,081h,04Eh,0FEh,000h,008h,0F6h,0C3h,001h,074h,00Eh,0C7h,046h,0FEh + DB 041h,000h,0A8h,001h,075h,005h,081h,04Eh,0FEh,000h,008h,08Bh,046h,0FEh,05Bh + DB 0C9h,0CBh,0C8h,004h,000h,000h,050h,053h,051h,052h,01Eh,056h,006h,057h,02Eh + DB 089h,01Eh,090h,005h,02Eh,0A3h,092h,005h,08Ch,0C8h,08Eh,0D8h,08Ch,0D3h,089h + DB 026h,094h,005h,089h,01Eh,096h,005h,0BBh,098h,007h,083h,0E3h,0FEh,0FAh,08Eh + DB 0D0h,08Bh,0E3h,0FBh,080h,03Eh,0F7h,002h,000h,00Fh,085h,0FAh,000h,080h,03Eh + DB 0F8h,002h,000h,00Fh,084h,0F1h,000h,0A1h,092h,005h,025h,01Eh,000h,075h,009h + DB 083h,03Eh,0A9h,002h,000h,00Fh,085h,0E0h,000h,083h,03Eh,0F9h,002h,001h,075h + DB 002h,0D1h,0E9h,03Bh,00Eh,049h,003h,072h,005h,08Bh,00Eh,049h,003h,049h,03Bh + DB 016h,04Dh,003h,072h,005h,08Bh,016h,04Dh,003h,04Ah,089h,00Eh,0ABh,002h,089h + DB 016h,0B1h,002h,066h,0F7h,006h,004h,000h,000h,010h,000h,000h,075h,013h,0FFh + DB 036h,090h,005h,00Eh,0E8h,02Ch,0FFh,083h,0C4h,002h,050h,00Eh,0E8h,07Bh,0F9h + DB 083h,0C4h,002h,083h,03Eh,0A9h,002h,000h,00Fh,085h,092h,000h,083h,03Eh,0FBh + DB 002h,000h,074h,019h,08Bh,0C1h,08Bh,0CAh,02Bh,006h,0FFh,002h,08Bh,01Eh,0FBh + DB 002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,0FFh,002h,08Bh,0D1h,08Bh,0C8h,083h + DB 03Eh,0FDh,002h,000h,074h,015h,08Bh,0C2h,02Bh,006h,001h,003h,08Bh,01Eh,0FDh + DB 002h,099h,0F7h,0FBh,0F7h,0EBh,003h,006h,001h,003h,08Bh,0D0h,083h,03Eh,003h + DB 003h,000h,075h,047h,039h,00Eh,007h,003h,075h,006h,039h,016h,009h,003h,074h + DB 03Bh,0A1h,00Bh,003h,025h,000h,0C0h,03Dh,000h,0C0h,074h,00Ch,00Eh,0E8h,016h + DB 0FEh,0F7h,006h,00Bh,003h,000h,080h,074h,020h,03Bh,00Eh,00Dh,003h,072h,01Ah + DB 03Bh,00Eh,015h,003h,077h,014h,03Bh,016h,011h,003h,072h,00Eh,03Bh,016h,019h + DB 003h,077h,008h,081h,00Eh,00Bh,003h,000h,040h,0EBh,004h,00Eh,0E8h,01Dh,0FEh + DB 089h,00Eh,007h,003h,089h,016h,009h,003h,081h,03Eh,098h,005h,0ADh,0DEh,074h + DB 017h,051h,057h,050h,006h,0B8h,000h,0A0h,08Eh,0C0h,033h,0FFh,0B9h,000h,0FAh + DB 0B8h,001h,000h,0F3h,0AAh,007h,058h,05Fh,059h,0A1h,096h,005h,0FAh,08Eh,0D0h + DB 08Bh,026h,094h,005h,0FBh,05Fh,007h,05Eh,01Fh,05Ah,059h,05Bh,058h,0C9h,0CBh + DB 0C8h,00Ch,000h,000h,050h,053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh + DB 0D8h,0FCh,0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h,027h,003h,02Bh,01Eh + DB 02Bh,003h,089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h,003h,003h,01Eh,03Bh + DB 003h,089h,046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0A1h,023h,003h + DB 089h,046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h + DB 003h,02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h + DB 08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh + DB 04Eh,0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h + DB 084h,0D0h,00Fh,085h,0AEh,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh + DB 08Bh,05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h + DB 002h,074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,037h,003h,001h,046h + DB 0F4h,0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h + DB 089h,046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,0B8h + DB 000h,0A0h,08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h,08Bh,016h,049h,003h + DB 08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h,0C5h,036h,023h,003h,08Bh,076h + DB 0F4h,003h,076h,0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh,03Ch + DB 02Bh,04Eh,0FCh,07Eh,037h,02Bh,0D0h,02Bh,0D8h,055h,083h,07Eh,006h,000h,075h + DB 018h,08Bh,0E9h,08Bh,0C8h,0F3h,0A4h,003h,0F3h,003h,0FAh,04Dh,075h,0F5h,05Dh + DB 007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,087h,0F7h,08Bh,0E9h,006h + DB 01Eh,007h,01Fh,08Bh,0C8h,0F3h,0A4h,003h,0F2h,003h,0FBh,04Dh,075h,0F5h,05Dh + DB 007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h + DB 053h,051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,08Bh,046h,006h,08Bh + DB 05Eh,008h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h,089h,046h,0FEh,089h,05Eh + DB 0FCh,003h,006h,03Fh,003h,003h,01Eh,043h,003h,089h,046h,0FAh,089h,05Eh,0F8h + DB 0C7h,046h,0F6h,000h,000h,0C4h,006h,01Dh,003h,089h,046h,0F4h,033h,0C0h,033h + DB 0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h + DB 08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh,01Eh,049h,003h,049h + DB 04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h + DB 003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh,08Bh,05Eh,0F8h,02Bh + DB 00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh + DB 013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h,00Fh,085h,093h,000h + DB 00Ah,0C2h,074h,042h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh,0FEh,0F7h,0DBh,089h + DB 05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h,013h,052h,08Bh,046h + DB 0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h,0C7h,046h,0FCh,000h,000h + DB 05Ah,0F6h,0C2h,004h,074h,007h,08Bh,01Eh,049h,003h,089h,05Eh,0FAh,0F6h,0C2h + DB 001h,074h,007h,08Bh,01Eh,04Dh,003h,089h,05Eh,0F8h,08Bh,046h,0FCh,0F7h,026h + DB 049h,003h,08Bh,016h,049h,003h,08Bh,0F8h,003h,07Eh,0FEh,08Bh,01Eh,03Fh,003h + DB 08Bh,076h,0F4h,003h,076h,0F6h,0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh + DB 04Eh,0F8h,02Bh,046h,0FEh,07Eh,023h,02Bh,04Eh,0FCh,07Eh,01Eh,02Bh,0D0h,02Bh + DB 0D8h,08Ah,0E0h,026h,08Ah,02Ch,066h,046h,00Ah,0EDh,074h,002h,088h,02Dh,047h + DB 0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,0FEh,0C9h,075h,0E6h,007h,01Fh,05Eh + DB 05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Ch,000h,000h,050h,053h,051h,052h + DB 057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h,0B7h,002h,089h,056h,0F2h,0FCh + DB 0A1h,02Fh,003h,08Bh,01Eh,033h,003h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h + DB 089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,037h,003h,003h,01Eh,03Bh,003h,089h + DB 046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0A1h,023h,003h,089h,046h + DB 0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h,0C0h + DB 003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h,02Bh + DB 01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FCh + DB 08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h,0C9h + DB 013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h,0D0h + DB 00Fh,085h,0C7h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh,05Eh + DB 0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h,074h + DB 013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,037h,003h,001h,046h,0F4h,0C7h + DB 046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h,089h,046h + DB 0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,0B8h,000h,0A0h + DB 08Eh,0C0h,08Bh,046h,0FCh,0F7h,026h,049h,003h,003h,046h,0FEh,083h,0D2h,000h + DB 08Bh,0F8h,0E8h,0F1h,001h,08Bh,016h,049h,003h,08Bh,01Eh,03Fh,003h,0C5h,036h + DB 023h,003h,08Bh,076h,0F4h,003h,076h,0F6h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh + DB 046h,0FEh,07Eh,04Fh,02Bh,04Eh,0FCh,07Eh,04Ah,02Bh,0D0h,02Bh,0D8h,083h,07Eh + DB 006h,000h,075h,021h,08Ah,0E0h,08Ah,02Ch,026h,088h,02Dh,046h,047h,075h,003h + DB 0E8h,0D9h,001h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,073h,003h,0E8h,0CCh + DB 001h,0FEh,0C9h,075h,0E1h,0EBh,01Fh,08Ah,0E0h,026h,08Ah,02Dh,088h,02Ch,046h + DB 047h,075h,003h,0E8h,0B8h,001h,0FEh,0CCh,075h,0F0h,003h,0F3h,003h,0FAh,073h + DB 003h,0E8h,0ABh,001h,0FEh,0C9h,075h,0E1h,08Bh,056h,0F2h,0E8h,06Ch,001h,007h + DB 01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h,0CBh,0C8h,00Eh,000h,000h,050h,053h + DB 051h,052h,057h,056h,01Eh,006h,08Ch,0C8h,08Eh,0D8h,0E8h,03Eh,001h,089h,056h + DB 0F2h,08Bh,046h,006h,08Bh,05Eh,008h,02Bh,006h,027h,003h,02Bh,01Eh,02Bh,003h + DB 089h,046h,0FEh,089h,05Eh,0FCh,003h,006h,03Fh,003h,003h,01Eh,043h,003h,089h + DB 046h,0FAh,089h,05Eh,0F8h,0C7h,046h,0F6h,000h,000h,0C4h,006h,01Dh,003h,089h + DB 046h,0F4h,033h,0C0h,033h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,003h,0C9h,013h + DB 0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh,0FEh,08Bh,05Eh,0FAh,02Bh,00Eh,049h,003h + DB 02Bh,01Eh,049h,003h,049h,04Bh,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh + DB 04Eh,0FCh,08Bh,05Eh,0F8h,003h,0C9h,013h,0C0h,003h,0DBh,013h,0D2h,08Bh,04Eh + DB 0FCh,08Bh,05Eh,0F8h,02Bh,00Eh,04Dh,003h,02Bh,01Eh,04Dh,003h,049h,04Bh,003h + DB 0C9h,013h,0C0h,003h,0DBh,013h,0D2h,034h,005h,080h,0F2h,005h,08Ah,0E0h,084h + DB 0D0h,00Fh,085h,0A0h,000h,00Ah,0C2h,074h,040h,0F6h,0C4h,008h,074h,00Dh,08Bh + DB 05Eh,0FEh,0F7h,0DBh,089h,05Eh,0F6h,0C7h,046h,0FEh,000h,000h,0F6h,0C4h,002h + DB 074h,013h,052h,08Bh,046h,0FCh,0F7h,0D8h,0F7h,026h,03Fh,003h,001h,046h,0F4h + DB 0C7h,046h,0FCh,000h,000h,05Ah,0F6h,0C2h,004h,074h,006h,0A1h,049h,003h,089h + DB 046h,0FAh,0F6h,0C2h,001h,074h,006h,0A1h,04Dh,003h,089h,046h,0F8h,08Bh,046h + DB 0FCh,0F7h,026h,049h,003h,003h,046h,0FEh,083h,0D2h,000h,08Bh,0F8h,0E8h,07Eh + DB 000h,08Bh,016h,049h,003h,08Bh,01Eh,03Fh,003h,08Bh,076h,0F4h,003h,076h,0F6h + DB 0B8h,000h,0A0h,08Eh,0D8h,08Bh,046h,0FAh,08Bh,04Eh,0F8h,02Bh,046h,0FEh,07Eh + DB 02Ch,02Bh,04Eh,0FCh,07Eh,027h,02Bh,0D0h,02Bh,0D8h,08Ah,0E0h,026h,08Ah,02Ch + DB 046h,00Ah,0EDh,074h,002h,088h,02Dh,047h,075h,003h,0E8h,067h,000h,0FEh,0CCh + DB 075h,0ECh,003h,0F3h,003h,0FAh,073h,003h,0E8h,05Ah,000h,0FEh,0C9h,075h,0DDh + DB 08Bh,056h,0F2h,0E8h,01Bh,000h,007h,01Fh,05Eh,05Fh,05Ah,059h,05Bh,058h,0C9h + DB 0CBh,050h,053h,0B8h,005h,04Fh,0B7h,001h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h + DB 05Bh,058h,0C3h,050h,053h,052h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh + DB 01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h,050h,053h,052h,08Bh,0DAh,0C1h,0E3h,002h + DB 02Eh,089h,01Eh,09Ah,007h,02Eh,08Bh,097h,061h,003h,0B8h,005h,04Fh,0B7h,000h + DB 0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah,05Bh,058h,0C3h,050h,053h,052h,02Eh + DB 08Bh,01Eh,09Ah,007h,083h,0C3h,004h,02Eh,089h,01Eh,09Ah,007h,02Eh,08Bh,097h + DB 061h,003h,0B8h,005h,04Fh,0B7h,000h,0B3h,000h,02Eh,0FFh,01Eh,05Dh,003h,05Ah + DB 05Bh,058h,0C3h diff --git a/WWFLAT32/KEYBOARD/X/KEYSTRUC.INC b/WWFLAT32/KEYBOARD/X/KEYSTRUC.INC new file mode 100644 index 0000000..1655b10 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/KEYSTRUC.INC @@ -0,0 +1,162 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/WWFLAT32/KEYBOARD/X/MAKEFILE b/WWFLAT32/KEYBOARD/X/MAKEFILE new file mode 100644 index 0000000..cc8f5e2 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/MAKEFILE @@ -0,0 +1,200 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = keyboard +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + keyboard.obj & + mouse.obj & + pagfault.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +keyireal.ibn: keyireal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip keyireal.exe + ebn keyireal.exe + +keyireal.obj: keyireal.asm + tasm /zn /la /ml /m2 keyireal.asm + +keyboard.obj: keyireal.ibn keyboard.asm + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/KEYBOARD/X/MOUSE.ASM b/WWFLAT32/KEYBOARD/X/MOUSE.ASM new file mode 100644 index 0000000..818fa9c --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/MOUSE.ASM @@ -0,0 +1,2226 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "svgaprim.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL RealModePtr:DWORD +GLOBAL MInstalled:DWORD +GLOBAL MouseBuffer:DWORD +GLOBAL LCW_Uncompress:NEAR + +GLOBAL Get_Shape_Uncomp_Size :NEAR +GLOBAL Get_Shape_Width :NEAR +GLOBAL Get_Shape_Original_Height :NEAR +GLOBAL Size_Of_Region :NEAR +GLOBAL _ShapeBuffer :DWORD + +GLOBAL XRes : dword +GLOBAL YRes : dword + + +GLOBAL VesaFunc : dword +GLOBAL Vesa_XRes : near +GLOBAL Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + mov ax,cx + or ax,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR + + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi + + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi + + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi + + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD + + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END diff --git a/WWFLAT32/KEYBOARD/X/PAGFAULT.ASM b/WWFLAT32/KEYBOARD/X/PAGFAULT.ASM new file mode 100644 index 0000000..546dd27 --- /dev/null +++ b/WWFLAT32/KEYBOARD/X/PAGFAULT.ASM @@ -0,0 +1,153 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL Install_Page_Fault_Handle : NEAR +GLOBAL Set_Video_Mode : NEAR +GLOBAL Remove_Mouse : NEAR +GLOBAL Remove_Keyboard_Interrupt : NEAR +GLOBAL Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/LIB/SOSDW1PS.LIB b/WWFLAT32/LIB/SOSDW1PS.LIB new file mode 100644 index 0000000..775054e Binary files /dev/null and b/WWFLAT32/LIB/SOSDW1PS.LIB differ diff --git a/WWFLAT32/MAKEFILE b/WWFLAT32/MAKEFILE new file mode 100644 index 0000000..3cb5fcf --- /dev/null +++ b/WWFLAT32/MAKEFILE @@ -0,0 +1,286 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : April 27, 1994 * +#* * +#* Last Update : May 12, 1994 [BRR] * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* LIBS = list of all component libraries * +#* * +#* NOTE: For this makefile to work, each library directory MUST have the * +#* same name as its library. * +#* * +#* "make install" installs the library on your drive * +#* "make update" updates all source files in your slice * +#* * +#* To install or update just one library you may type: * +#* "make -DLIBS=misc.lib install" * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + + +#=========================================================================== +# User-defined section: list each library in this macro +# NOTE: These are some order dependencies: +# 1. The directory MISC must always be made first. +# 2. The directory VIDEO must be made before the SYSTEM directory. +#=========================================================================== +LIB_NAME = wwflat32 +LIB_DIR = $(%WWFLAT)\lib + +#--------------------------------------------------------------------------- +# LIBS macro: a list of all component libraries +# "make LIBS=xxxx.lib [target]" makes/installs/updates only that library +#--------------------------------------------------------------------------- + +LIBS = & + audio.lib & + descmgmt.lib & + dipthong.lib & + file.lib & + font.lib & + iff.lib & + keyboard.lib & + mcgaprim.lib & + mem.lib & + misc.lib & + mono.lib & + palette.lib & + playcd.lib & + shape.lib & + sosdw1ps.lib & + svgaprim.lib & + tile.lib & + timer.lib & + video.lib & + windows.lib & + wsa.lib + +LIB_INSTALL = & + audio.ins & + descmgmt.ins & + dipthong.ins & + file.ins & + font.ins & + iff.ins & + keyboard.ins & + mcgaprim.ins & + mem.ins & + misc.ins & + mono.ins & + palette.ins & + playcd.ins & + shape.ins & + svgaprim.ins & + tile.ins & + timer.ins & + video.ins & + windows.ins & + wsa.ins + +LIB_UPDATE = & + audio.upd & + descmgmt.upd & + dipthong.upd & + file.upd & + font.upd & + iff.upd & + keyboard.upd & + mcgaprim.upd & + mem.upd & + misc.upd & + mono.upd & + palette.upd & + playcd.upd & + shape.upd & + svgaprim.upd & + tile.upd & + timer.upd & + video.upd & + windows.upd & + wsa.upd + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.lib: $(%WWFLAT)\lib + + +#--------------------------------------------------------------------------- +# Tools/commands +# LIB_CMD: library command +# LIB_CFG: library configuration file +# VCS_UPDATE: version control update command; this command should update +# all relevant files in a given directory with read-only +# copies from the archive +#--------------------------------------------------------------------------- +!include $(%WWFLAT)\project.cfg +LIB_CMD = wlib +VCS_UPDATE = update + +#--------------------------------------------------------------------------- +# If you like to have this makefile switch to the mono screen automatically, +# you will need to do a "set MONO=Y" or to anything you like. +#--------------------------------------------------------------------------- +!ifdef %MONO +.BEFORE + mode mono + +.AFTER + mode co80 + +.ERROR + mode co80 +!endif + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(LIB_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(LIB_NAME).lib: $(LIBS) libs.lbc + $(LIB_CMD) $(LIB_CFG) $^@ @libs.lbc + + +#--------------------------------------------------------------------------- +# This construct tells make how to make all component libraries +# The commands get executed for every item in the macro. +# The macro $: extracts only the directory name from the macro item. +#--------------------------------------------------------------------------- +$(LIBS): + echo Making $^&... + cd $^& + wmake + cd .. + +#--------------------------------------------------------------------------- +# Libs is librarian response file that is only genarated if any of the +# sublibrary in the golbal variable LIBS was updated. +#--------------------------------------------------------------------------- +libs.lbc : $(LIBS) + %create $^@ + for %index in ($(LIBS)) do %append $^@ +$(LIB_DIR)\%index + + + +#--------------------------------------------------------------------------- +# "make install" installs the library on your drive +#--------------------------------------------------------------------------- +install: install_dirs $(LIB_INSTALL) .SYMBOLIC + echo Compiling library... + wmake + echo Library installation complete. + +#--------------------------------------------------------------------------- +# At installation time, this target makes all non-library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +install_dirs: .SYMBOLIC + echo Making directories... + mkdir example + mkdir include + mkdir lib + mkdir srcdebug + mkdir tools + cd tools + copy $(%WWVCS)\tools\vcs.cfg + $(VCS_UPDATE) + cd.. + copy $(%WWVCS)\vcs.cfg + $(VCS_UPDATE) + cd example + copy $(%WWVCS)\example\vcs.cfg + $(VCS_UPDATE) + cd .. + + +#--------------------------------------------------------------------------- +# This target installs all library directories +# This is a dependency for 'install' +#--------------------------------------------------------------------------- +$(LIB_INSTALL): .SYMBOLIC + echo Installing $^&... + md $^& + cd $^& + copy $(%WWVCS)\$^&\vcs.cfg + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. + + + + + +#--------------------------------------------------------------------------- +# "make update" updates all source files in your slice +#--------------------------------------------------------------------------- +update: $(LIB_UPDATE) .SYMBOLIC + echo Library updated. + + +#--------------------------------------------------------------------------- +# This target updates all library directories +# This is a dependency for 'updates' +#--------------------------------------------------------------------------- +$(LIB_UPDATE): .SYMBOLIC + echo Updating $^&... + cd $^& + $(VCS_UPDATE) + copy *.h ..\include + copy *.inc ..\include + cd .. diff --git a/WWFLAT32/MAKE_EXE.MAK b/WWFLAT32/MAKE_EXE.MAK new file mode 100644 index 0000000..16f06d2 --- /dev/null +++ b/WWFLAT32/MAKE_EXE.MAK @@ -0,0 +1,173 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .EXE makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 20, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the executable program you're building * +#* PROJ_LIBS = Westwood libraries to link your EXE to * +#* OBJECTS = list of objects in your current working directory * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .xxx: = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== +#--------------------------------------------------------------------------- +# PROJ_NAME = library name +# PROJ_DIR = directory containing source & objects +#--------------------------------------------------------------------------- +PROJ_NAME = TEST +PROJ_DIR = $(%WWFLAT)\MEM\TEST +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + $(PROJ_NAME).obj & + object0.obj & + object1.obj + +PROJ_LIBS = & + lib1.lib & + lib2.lib & + wwflat32.lib + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR +#--------------------------------------------------------------------------- + +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + +#--------------------------------------------------------------------------- +# Default target +#--------------------------------------------------------------------------- +all: $(PROJ_NAME).exe + +#--------------------------------------------------------------------------- +# Build the EXE +#--------------------------------------------------------------------------- +$(PROJ_NAME).exe: $(OBJECTS) $(PROJ_NAME).lnk $(PROJ_LIBS) + $(LINK_CMD) $(LINK_CFG) system dos4g name $^@ @$(PROJ_NAME).lnk + + +$(PROJ_LIBS): + echo updating base library $^@ + cd .. + wmake + cd $(PROJ_DIR) + + +$(PROJ_NAME).lnk : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ file %index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(LIB_DIR)\%index + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/MAKE_LIB.MAK b/WWFLAT32/MAKE_LIB.MAK new file mode 100644 index 0000000..b55d01d --- /dev/null +++ b/WWFLAT32/MAKE_LIB.MAK @@ -0,0 +1,182 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* Last Update : * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = project_name +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + object1.obj & + object2.obj & + object3.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/MCGAPRIM/BITBLIT.ASM b/WWFLAT32/MCGAPRIM/BITBLIT.ASM new file mode 100644 index 0000000..023ce15 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/BITBLIT.ASM @@ -0,0 +1,460 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + +CODESEG + + PROC Linear_Blit_To_Linear C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, display rectangle) +; - trivially rejected (return with no action) +; - retangle must be iteratively clipped again edges of the clipping window +; and the remaining retangle is display. + +; Clip Source Rectangle against source Window boundaries. + mov esi,[this] ; get ptr to src + xor ecx,ecx ; Set sutherland code to zero + xor edx,edx ; Set sutherland code to zero + + ; compute the difference in the X axis and get the bit signs into ecx , edx + mov edi,[(VideoViewPort esi).VIVPWidth] ; get width into register + mov ebx,[x_pixel] ; Get first end point x_pixel into register + mov eax,[x_pixel] ; Get second end point x_pixel into register + add ebx,[pixel_width] ; second point x1_pixel = x + width + shld ecx, eax,1 ; the sign bit of x_pixel is sutherland code0 bit4 + mov [x1_pixel],ebx ; save second for future use + inc edi ; move the right edge by one unit + shld edx,ebx,1 ; the sign bit of x1_pixel is sutherland code0 bit4 + sub eax,edi ; compute the difference x0_pixel - width + sub ebx,edi ; compute the difference x1_pixel - width + shld ecx,eax,1 ; the sign bit of the difference is sutherland code0 bit3 + shld edx,ebx,1 ; the sign bit of the difference is sutherland code0 bit3 + + ; the following code is just a repeticion of the above code + ; in the Y axis. + mov edi,[(VideoViewPort esi).VIVPHeight] ; get height into register + mov ebx,[y_pixel] + mov eax,[y_pixel] + add ebx,[pixel_height] + shld ecx,eax,1 + mov [y1_pixel ],ebx + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + ; Here we have the to Sutherland code into cl and dl + xor cl,5 ; bit 2 and 0 are complented, reverse then + xor dl,5 ; bit 2 and 0 are complented, reverse then + mov al,cl ; save code1 in case we have to clip iteratively + test dl,cl ; if any bit in code0 and its counter bit + jnz ??real_out ; in code1 is set then the rectangle in outside + or al,dl ; if all bit of code0 the counter bit in + jz ??clip_against_dest ; in code1 is set to zero, then all + ; end points of the rectangle are + ; inside the clipping window + + ; if we are here the polygon have to be clip iteratively + test cl,1000b ; if bit 4 in code0 is set then + jz ??scr_left_ok ; x_pixel is smaller than zero + mov [x_pixel],0 ; set x_pixel to cero. + +??scr_left_ok: + test cl,0010b ; if bit 2 in code0 is set then + jz ??scr_bottom_ok ; y_pixel is smaller than zero + mov [ y_pixel ],0 ; set y_pixel to cero. + +??scr_bottom_ok: + test dl,0100b ; if bit 3 in code1 is set then + jz ??scr_right_ok ; x1_pixel is greater than the width + mov eax,[(VideoViewPort esi).VIVPWidth] ; get width into register + mov [ x1_pixel ],eax ; set x1_pixel to width. +??scr_right_ok: + test dl,0001b ; if bit 0 in code1 is set then + jz ??clip_against_dest ; y1_pixel is greater than the width + mov eax,[(VideoViewPort esi).VIVPHeight] ; get height into register + mov [ y1_pixel ],eax ; set y1_pixel to height. + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + ; build the destination rectangle before clipping + ; dest_x1 = dest_x0 + ( x1_pixel - x_pixel ) + ; dest_y1 = dest_y0 + ( y1_pixel - y_pixel ) + mov eax,[dest_x0] ; get dest_x0 into eax + mov ebx,[dest_y0] ; get dest_y0 into ebx + sub eax,[x_pixel] ; subtract x_pixel from eax + sub ebx,[y_pixel] ; subtract y_pixel from ebx + add eax,[x1_pixel] ; add x1_pixel to eax + add ebx,[y1_pixel] ; add y1_pixel to ebx + mov [dest_x1],eax ; save eax into dest_x1 + mov [dest_y1],ebx ; save eax into dest_y1 + + + ; The followin code is a repeticion of the Sutherland clipping + ; descrived above. + mov esi,[dest] ; get ptr to src + xor ecx,ecx + xor edx,edx + mov edi,[(VideoViewPort esi).VIVPWidth] ; get width into register + mov eax,[dest_x0] + mov ebx,[dest_x1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + mov edi,[( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax,[dest_y0] + mov ebx,[dest_y1] + shld ecx,eax,1 + inc edi + shld edx,ebx,1 + sub eax,edi + sub ebx,edi + shld ecx,eax,1 + shld edx,ebx,1 + + xor cl,5 + xor dl,5 + mov al,cl + test dl,cl + jnz ??real_out + or al,dl + jz ??do_blit + + test cl,1000b + jz ??dest_left_ok + mov eax,[ dest_x0 ] + mov [ dest_x0 ],0 + sub [ x_pixel ],eax + +??dest_left_ok: + test cl,0010b + jz ??dest_bottom_ok + mov eax,[ dest_y0 ] + mov [ dest_y0 ],0 + sub [ y_pixel ],eax + + +??dest_bottom_ok: + test dl,0100b + jz ??dest_right_ok + mov ebx,[ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax,[ dest_x1 ] + mov [ dest_x1 ],ebx + sub eax,ebx + sub [ x1_pixel ],eax + +??dest_right_ok: + test dl,0001b + jz ??do_blit + mov ebx,[ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax,[ dest_y1 ] + mov [ dest_y1 ],ebx + sub eax,ebx + sub [ y1_pixel ],eax + + +; Here is where we do the actual blit +??do_blit: + cld + mov ebx,[this] + mov esi,[(VideoViewPort ebx).VIVPOffset] + mov eax,[(VideoViewPort ebx).VIVPXAdd] + add eax,[(VideoViewPort ebx).VIVPWidth] + mov ecx,eax + mul [y_pixel] + add esi,[x_pixel] + mov [source_area],ecx + add esi,eax + + add ecx,[x_pixel ] + sub ecx,[x1_pixel ] + mov [scr_ajust_width ],ecx + + mov ebx,[dest] + mov edi,[(VideoViewPort ebx).VIVPOffset] + mov eax,[(VideoViewPort ebx).VIVPXAdd] + add eax,[ (VideoViewPort ebx).VIVPWidth] + mov ecx,eax + mul [ dest_y0 ] + add edi,[ dest_x0 ] + mov [ dest_area ],ecx + add edi,eax + + mov eax,[ dest_x1 ] + sub eax,[ dest_x0 ] + jle ??real_out + sub ecx,eax + mov [ dest_ajust_width ],ecx + + mov edx,[ dest_y1 ] + sub edx,[ dest_y0 ] + jle ??real_out + + cmp esi,edi + jz ??real_out + jl ??backupward_blit + +; ******************************************************************** +; Forward bitblit + + test [ trans ],1 + jnz ??forward_Blit_trans + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax,10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx,edi + mov ebx,eax + neg ecx + and ecx,3 + sub ebx,ecx + rep movsb + mov ecx,ebx + shr ecx,2 + rep movsd + mov ecx,ebx + and ecx,3 + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx,eax + rep movsb + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_bytes + ret + +??forward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??forward_loop_trans: + mov ecx,eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi,[ scr_ajust_width ] + add edi,[ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret + + +; ************************************************************************ +; backward bitblit + +??backupward_blit: + + mov ebx,[ source_area ] + dec edx + add esi,eax + imul ebx,edx + std + lea esi,[ esi + ebx - 1 ] + + mov ebx,[ dest_area ] + add edi,eax + imul ebx,edx + lea edi,[ edi + ebx - 1] + + test [ trans ],1 + jnz ??backward_Blit_trans + + cmp eax,15 + jl ??backward_loop_bytes + +??backward_loop_dword: + push edi + push esi + lea ecx,[edi+1] + mov ebx,eax + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_dword + cld + ret + +??backward_loop_bytes: + push edi + mov ecx,eax ; remove that from the total size to be copied later. + push esi + rep movsb ; do the copy. + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_bytes + cld + ret + +??backward_Blit_trans: + mov ecx,eax + and ecx,01fh + lea ecx,[ ecx + ecx * 4 ] + neg ecx + shr eax,5 + lea ecx,[ ??back_transp_reference + ecx * 2 ] + mov [ y1_pixel ],ecx + +??backward_loop_trans: + mov ecx,eax + push edi + push esi + jmp [ y1_pixel ] +??backward_trans_line: + REPT 32 + local transp_pixel + mov bl,[ esi ] + test bl,bl + jz transp_pixel + mov [ edi ],bl + transp_pixel: + dec esi + dec edi + ENDM + ??back_transp_reference: + dec ecx + jge ??backward_trans_line + pop esi + pop edi + sub esi,[ source_area ] + sub edi,[ dest_area ] + dec edx + jge ??backward_loop_trans + cld + ret + +??real_out: + ret + ENDP Linear_Blit_To_Linear + + + +END diff --git a/WWFLAT32/MCGAPRIM/BUFFER.CPP b/WWFLAT32/MCGAPRIM/BUFFER.CPP new file mode 100644 index 0000000..b3bea0e --- /dev/null +++ b/WWFLAT32/MCGAPRIM/BUFFER.CPP @@ -0,0 +1,131 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/WWFLAT32/MCGAPRIM/BUFFER.H b/WWFLAT32/MCGAPRIM/BUFFER.H new file mode 100644 index 0000000..bc3c188 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/BUFFER.H @@ -0,0 +1,129 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class VideoViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + long To_Page(VideoViewPortClass &view); + long To_Page(int w, int h, VideoViewPortClass &view); + long To_Page(int x, int y, int w, int h, VideoViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif + diff --git a/WWFLAT32/MCGAPRIM/BUFFGLBL.CPP b/WWFLAT32/MCGAPRIM/BUFFGLBL.CPP new file mode 100644 index 0000000..c5bc65a --- /dev/null +++ b/WWFLAT32/MCGAPRIM/BUFFGLBL.CPP @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include "gbuffer.h" +#include "vbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicBufferClass *LogicPage; + diff --git a/WWFLAT32/MCGAPRIM/CLEAR.ASM b/WWFLAT32/MCGAPRIM/CLEAR.ASM new file mode 100644 index 0000000..1a392a5 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/CLEAR.ASM @@ -0,0 +1,127 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : August 23, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* + PROC MCGA_Clear C near + USES eax,ebx,ecx,edx,esi,edi + + ARG this:DWORD ; this is a member function + ARG color:BYTE ; what color should we clear to + + cld ; always go forward + + mov ebx,[this] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov edx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov esi,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + mov ebx,[(GraphicViewPort ebx).GVPXAdd] ; esi = add for each line + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??byte_by_byte ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + push ebx +??dword_aligned_loop: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??dword_aligned_loop ; if more to do than do it + pop eax + ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??byte_by_byte: + mov ecx,esi ; get total width in bytes + rep stosb ; store the width + add edi,ebx ; handle the xadd + dec edx ; decrement the height + jnz ??byte_by_byte ; if any left then next line +??exit: + ret + ENDP MCGA_Clear +END diff --git a/WWFLAT32/MCGAPRIM/DRAWLINE.ASM b/WWFLAT32/MCGAPRIM/DRAWLINE.ASM new file mode 100644 index 0000000..44012e0 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/DRAWLINE.ASM @@ -0,0 +1,430 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC MCGA_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl short ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl short ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + mov eax,[color] + mov ah,al ; make it a word of color + shr ecx,1 ; convert to number of words to write + rep stosw ; write as many words as possible + adc ecx,ecx ; add the carry flag back in + rep stosb ; move odd one if any are odd + jmp short ??out ; get outt + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP MCGA_Draw_Line + + +END diff --git a/WWFLAT32/MCGAPRIM/DRAWRECT.CPP b/WWFLAT32/MCGAPRIM/DRAWRECT.CPP new file mode 100644 index 0000000..a9ba6f8 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/DRAWRECT.CPP @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "gbuffer.h" + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); +} diff --git a/WWFLAT32/MCGAPRIM/DRAWRECT.CPP.BAK b/WWFLAT32/MCGAPRIM/DRAWRECT.CPP.BAK new file mode 100644 index 0000000..a9ba6f8 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/DRAWRECT.CPP.BAK @@ -0,0 +1,66 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "gbuffer.h" + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); +} diff --git a/WWFLAT32/MCGAPRIM/FILLQUAD.ASM b/WWFLAT32/MCGAPRIM/FILLQUAD.ASM new file mode 100644 index 0000000..316217a --- /dev/null +++ b/WWFLAT32/MCGAPRIM/FILLQUAD.ASM @@ -0,0 +1,666 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : FILLQUAD.ASM * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Fill_Quad -- Flood fills an arbitrary convex quadrilateral * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +SLOT_VACANT EQU 80008000h +NULL EQU 0h + + +CODESEG + +;*************************************************************************** +;* VVC::FILL_QUAD -- Flood fills an arbitrary convex quadrilateral * +;* * +;* INPUT: DWORD this - associated graphic viewport * +;* DWORD span_buff - pointer to span array * +;* DWORD x0_pixel - the zeroth x pixel position * +;* DWORD y0_pixel - the zeroth y pixel position * +;* DWORD x1_pixel - the first x pixel position * +;* DWORD y1_pixel - the first y pixel position * +;* DWORD x2_pixel - the second x pixel position * +;* DWORD y2_pixel - the second y pixel position * +;* DWORD x3_pixel - the third x pixel position * +;* DWORD y3_pixel - the third y pixel position * +;* DWORD color - the color of the quad to fill * +;* * +;* Bounds Checking: Compares quad points with the graphic viewport it * +;* has been assigned to. * +;* * +;* Rasterization Rules: FILL_QUAD is designed to be used within a quad * +;* mesh. There is no pixel overlapping or stitching * +;* effects at shared borders. FILL_QUAD is NOT * +;* recommended for isolated quads. * * +;* HISTORY: * +;* 08/11/1994 IML : Created. * +;* 08/26/1994 IML : Various optimizations. * +;* 08/30/1994 IML : Added rasterization rules for shared borders. * +;*=========================================================================* + PROC MCGA_Fill_Quad C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*================================================================== + ;* Define the arguments that the function takes. + ;*================================================================== + ARG this:DWORD ; associated graphic viewport + ARG span_buff:DWORD ; pointer to span array + ARG x0_pixel:DWORD ; the zeroth x pixel position + ARG y0_pixel:DWORD ; the zeroth y pixel position + ARG x1_pixel:DWORD ; the first x pixel position + ARG y1_pixel:DWORD ; the first y pixel position + ARG x2_pixel:DWORD ; the second x pixel position + ARG y2_pixel:DWORD ; the second y pixel position + ARG x3_pixel:DWORD ; the third x pixel position + ARG y3_pixel:DWORD ; the third y pixel position + ARG color:DWORD ; the color of the quad + + + ;*================================================================== + ;* Define the local variables that we will use on the stack. + ;*================================================================== + LOCAL clip_min_x:DWORD ; boundary of viewport + LOCAL clip_max_x:DWORD ; + LOCAL clip_min_y:DWORD ; + LOCAL clip_max_y:DWORD ; + LOCAL clip_var:DWORD + LOCAL left_clip_base:DWORD:2 ; storage for additional edges + LOCAL left_clip_index:DWORD ; generated by clipping + LOCAL right_clip_base:DWORD:2 ; + LOCAL right_clip_index:DWORD ; + LOCAL scanline_min:DWORD ; vertical extent of quad + LOCAL scanline_max:DWORD + LOCAL realignment:DWORD + LOCAL bpr:DWORD ; bytes per row of associated buffer + + + ;*================================================================== + ;* Extract essential GraphicViewPort info. + ;*================================================================== + mov ebx,[this] + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + + ;*================================================================== + ;* Adjust top and right edges of viewport for rasterization rules. + ;*================================================================== + dec [clip_max_y] + dec [clip_min_y] + + + ;*================================================================== + ;* Find the vertical extent of the quad BEFORE clipping. + ;* y0_pixel = y0, y1_pixel = y1, y2_pixel = y2, y3_pixel = y3 + ;*================================================================== + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jle short ??y1_not_smaller + mov eax,[y1_pixel] + +??y1_not_smaller: + cmp eax,[y2_pixel] + jle short ??y2_not_smaller + mov eax,[y2_pixel] + +??y2_not_smaller: + cmp eax,[y3_pixel] + jle short ??y3_not_smaller + mov eax,[y3_pixel] + +??y3_not_smaller: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_min + mov eax,[clip_min_y] + +??no_clamp_min_min: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_min + mov eax,[clip_max_y] + ; scanline_min = MIN (y0, y1, y2, y3) +??no_clamp_max_min: ; scanline_min = MAX (scanline_min, clip_min_y) + mov [scanline_min],eax ; scanline_min = MIN (scanline_min, clip_max_y) + + mov eax,[y0_pixel] + cmp eax,[y1_pixel] + jge short ??y1_not_greater + mov eax,[y1_pixel] + +??y1_not_greater: + cmp eax,[y2_pixel] + jge short ??y2_not_greater + mov eax,[y2_pixel] + +??y2_not_greater: + cmp eax,[y3_pixel] + jge short ??y3_not_greater + mov eax,[y3_pixel] + +??y3_not_greater: + cmp eax,[clip_min_y] + jge short ??no_clamp_min_max + mov eax,[clip_min_y] + +??no_clamp_min_max: + cmp eax,[clip_max_y] + jle short ??no_clamp_max_max + mov eax,[clip_max_y] + ; scanline_max = MAX (y0, y1, y2, y3) +??no_clamp_max_max: ; scanline_max = MAX (scanline_max, clip_min_y) + mov [scanline_max],eax ; scanline_max = MIN (scanline_max, clip_max_y) + + + ;*================================================================== + ;* Initialize memory for spans. + ;*================================================================== + sub eax,[scanline_min] + je ??abort_fill_quad ; don't render quads with zero height + mov ebx,eax + mov eax,[span_buff] ; check span_buff for NULL ptr + cmp eax,NULL + je ??abort_fill_quad + sal ebx,2 + +??span_initialize_loop: + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jl short ??exit_span_initialize + mov [DWORD PTR eax + ebx],SLOT_VACANT + sub ebx,4 + jge short ??span_initialize_loop + + + ;*================================================================== + ;* Clip and scan convert the four edges defining the quad. + ;*================================================================== +??exit_span_initialize: + mov [left_clip_index],0 + mov [right_clip_index],0 + + mov eax,[x0_pixel] + mov ebx,[y0_pixel] + mov ecx,[x1_pixel] + mov edx,[y1_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x1_pixel] + mov ebx,[y1_pixel] + mov ecx,[x2_pixel] + mov edx,[y2_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x2_pixel] + mov ebx,[y2_pixel] + mov ecx,[x3_pixel] + mov edx,[y3_pixel] + call NEAR PTR ??clip_and_scan_convert + mov eax,[x3_pixel] + mov ebx,[y3_pixel] + mov ecx,[x0_pixel] + mov edx,[y0_pixel] + call NEAR PTR ??clip_and_scan_convert + + + ;*================================================================== + ;* Scan convert up to 2 additional left and right vertical edges + ;* generated by the clipping process. + ;*================================================================== + cmp [left_clip_index],0 + je short ??no_left_edge + mov eax,[clip_min_x] + mov ebx,[left_clip_base] + mov ecx,eax + mov edx,[left_clip_base + 4] + call NEAR PTR ??scan_convert + +??no_left_edge: + cmp [right_clip_index],0 + je short ??no_right_edge + mov eax,[clip_max_x] + mov ebx,[right_clip_base] + mov ecx,eax + mov edx,[right_clip_base + 4] + call NEAR PTR ??scan_convert + + + ;*================================================================== + ;* Fill the quad with specified color. Use DWORD copies where + ;* appropriate. + ;*================================================================== +??no_right_edge: + mov eax,[this] + mov edi,[(GraphicViewPort eax).GVPOffset] + mov eax,[scanline_min] ; eax = scanline_min + + mov ebx,[scanline_max] + sub ebx,[scanline_min] ; ebx = span count + + mov esi,[span_buff] ; esi = address of top span + + mul [bpr] + add edi,eax ; edi = address of top scanline + ; containing quad + mov al,[BYTE PTR color] ; extend pixel color into eax ready + mov ah,al ; for DWORD copies + mov edx,eax + shl eax,16 + mov ax,dx + + cld ; only fill forwards + + jmp ??skip_span ; rasterization rule: don't + ; render topmost span + +??quad_fill_loop: + cmp [DWORD PTR esi],SLOT_VACANT ; test for unused spans due to clipping + je ??skip_span + xor ecx,ecx + xor edx,edx + mov cx,[WORD PTR esi] + mov dx,[WORD PTR esi + 2] + sub ecx,edx + push edi + jns short ??not_negative_count + add edi,ecx + neg ecx ; ecx = span width + +??not_negative_count: + add edi,edx ; edi = address of start of span + cmp ecx,OPTIMAL_BYTE_COPY ; does span width justify DWORD copies? + jl short ??byte_copy + mov edx,ecx + mov ecx,edi + and ecx,3 ; if (ecx == 0) edi is already + jz short ??dword_copy_no_alignment ; DWORD aligned + xor ecx,3 + inc ecx ; ecx = number of pixels before alignment + sub edx,ecx + rep stosb + +??dword_copy_no_alignment: + mov ecx,edx ; ecx = remaining pixels on span + shr ecx,2 ; copy (ecx / 4) DWORDS + rep stosd + mov ecx,edx + and ecx,3 ; ecx = remaining pixels on span + +??byte_copy: + rep stosb ; byte copy remaining pixels on span + pop edi + +??skip_span: + add edi,[bpr] ; edi = address of start of next scanline + add esi,4 ; esi = address of next span + dec ebx + jge short ??quad_fill_loop ; is span count >= 0? + +??abort_fill_quad: + ret + + + ;*================================================================== + ;* This is the section that "pushes" the edge into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars clip_min_x, clip_min_y, clip_max_x, clip_max_y + ;* are used to clip the edge (default is the screen). + ;* PORTABLE start. + ;*================================================================== + + + ;*================================================================== + ;* Clip an edge against the viewport. + ;*================================================================== +??clip_and_scan_convert: + call NEAR PTR ??set_left_right_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_left_right_bits + mov [clip_var],edi + or [clip_var],esi + jz ??clip_up_down ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + +??clip_up_down: + call NEAR PTR ??set_up_down_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_up_down_bits + mov [clip_var],edi + or [clip_var],esi + jz ??scan_convert ; trivial acceptance? + test edi,esi + jne ??exit ; trivial rejection? + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??scan_convert + + + ;*================================================================== + ;* Subroutine table for clipping conditions. + ;*================================================================== +??clip_tbl DD ??nada,??a_lft,??a_rgt,??nada + DD ??a_up,??nada,??nada,??nada + DD ??a_dwn + + + ;*================================================================== + ;* Subroutines for clipping conditions. + ;*================================================================== +??nada: + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + retn + +??a_dwn: + mov esi,[clip_max_y] + call NEAR PTR ??clip_vert + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[left_clip_index] + cmp ebx,[clip_min_y] + jge ??no_left_min_clip + mov ebx,[clip_min_y] + +??no_left_min_clip: + cmp ebx,[clip_max_y] + jle ??no_left_max_clip + mov ebx,[clip_max_y] + +??no_left_max_clip: + mov [left_clip_base + esi],ebx ; a left edge will be generated + mov [left_clip_index],4 ; store off yb + pop ebx + retn + +??a_rgt: + mov esi,[clip_max_x] + call NEAR PTR ??clip_horiz + push ebx + mov esi,[right_clip_index] + cmp ebx,[clip_min_y] + jge ??no_right_min_clip + mov ebx,[clip_min_y] + +??no_right_min_clip: + cmp ebx,[clip_max_y] + jle ??no_right_max_clip + mov ebx,[clip_max_y] + +??no_right_max_clip: + mov [right_clip_base + esi],ebx ; a right edge will be generated + mov [right_clip_index],4 ; store off yb + pop ebx + retn + + + ;*================================================================== + ;* Clip a line against a horizontal edge at clip_y. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;* xa' = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + ;* ya' = clip_y + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax = -xa + add eax,ecx ; eax = (xb-xa) + mov edx,esi ; edx = clip_y + sub edx,ebx ; edx = (clip_y-ya) + imul edx ; eax = (clip_y-ya)(xb-xa) + idiv [clip_var] ; eax = (clip_y-ya)(xb-xa)/(yb-ya) + pop edx + add eax,edx ; eax = xa+[(clip_y-ya)(xb-xa)/(yb-ya)] + pop edx + mov ebx,esi ; ebx = clip_y + retn + + + ;*================================================================== + ;* Clip a line against a vertical edge at clip_x. + ;* (eax,ebxx) = (xa,ya), (ecx,edxx) = (xb,yb) + ;* ya' = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + ;* xa' = clip_x + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (clip_x-xa) + imul edx ; eax = (clip_x-xa)(yb-ya) + idiv [clip_var] ; eax = (clip_x-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = ya+[(clip_x-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi ; eax = clip_x + retn + + + ;*================================================================== + ;* Set the condition bits for the subroutine table. + ;*================================================================== +??set_left_right_bits: + xor esi,esi + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,1 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,2 + +??a_not_right: + retn + +??set_up_down_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,4 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,8 + +??a_not_down: + retn + + + ;*================================================================== + ;* PORTABLE end. + ;*================================================================== + + ;*================================================================== + ;* Scan convert an edge. + ;* (eax,ebx) = (xa,ya), (ecx,edx) = (xb,yb) + ;*================================================================== +??scan_convert: + cmp ebx,edx + je ??exit ; if (ya == yb) don't scan convert + jl short ??no_swap ; if (ya < yb) swap vertices + xchg eax,ecx + xchg ebx,edx + +??no_swap: + sub edx,ebx ; edx = (yb - ya) + sub ebx,[scanline_min] + sal ebx,2 + add ebx,[span_buff] ; ebx = span_buff + 4(ya - clip_min_y) + sub ecx,eax ; ecx = (xb - xa) + je ??v_scan ; if the edge is vertical use a + ; special case routine + push eax + mov eax,ecx ; eax = (xb - xa) + mov ecx,edx ; ecx = (yb - ya) + sal edx,1 + mov [realignment],edx ; realignment = 2(yb - ya) + cwd + idiv cx + cwde + movsx edx,dx + mov edi,eax ; edi = (xb - xa) / (yb - ya) + mov esi,edx + mov edx,ecx + pop eax ; eax = xa + neg edx ; edx = -(yb - ya) + sal esi,1 ; esi = 2[(xb - xa) % (yb - ya)] + jns short ??r_scan ; scan to the left or right? + neg esi + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the left. + ;* eax = xpos, ebx = span to reference + ;*================================================================== + cmp ebx,[span_buff] + jg ??l_scan_convert + +??l_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??l_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??l_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??l_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??l_scan_convert_loop + dec eax + sub edx,[realignment] + jmp ??l_scan_convert_loop + + + ;*================================================================== + ;* Edge scan conversion DDA moving down and to the right. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??r_scan: + cmp ebx,[span_buff] + jg ??r_scan_convert + +??r_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??r_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??r_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??r_scan_convert: + dec ecx + jl short ??exit + add ebx,4 + add eax,edi + add edx,esi + jle short ??r_scan_convert_loop + inc eax + sub edx,[realignment] + jmp ??r_scan_convert_loop + + + ;*================================================================== + ;* Scan convert a vertical edge. + ;* eax = xpos, ebx = span to reference + ;*================================================================== +??v_scan: + cmp ebx,[span_buff] + jg ??v_scan_convert + +??v_scan_convert_loop: + cmp [DWORD PTR ebx],SLOT_VACANT ; if the left slot of span is + jne short ??v_next_slot ; vacant fill it with xpos + mov [ebx],ax + +??v_next_slot: + mov [ebx + 2],ax ; otherwise fill the right slot + ; with xpos +??v_scan_convert: + add ebx,4 + dec edx + jge ??v_scan_convert_loop + +??exit: + retn + + ENDP MCGA_Fill_Quad + +END diff --git a/WWFLAT32/MCGAPRIM/FILLRECT.ASM b/WWFLAT32/MCGAPRIM/FILLRECT.ASM new file mode 100644 index 0000000..83a6b14 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/FILLRECT.ASM @@ -0,0 +1,274 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC MCGA_Fill_Rect C near + USES eax,ebx,ecx,edx,esi,edi,ebp + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; this is a member function + ARG x1_pixel:WORD + ARG y1_pixel:WORD + ARG x2_pixel:WORD + ARG y2_pixel:WORD + ARG color:BYTE ; what color should we clear to + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL VPwidth:DWORD ; the width of the viewport + LOCAL VPheight:DWORD ; the height of the viewport + LOCAL VPxadd:DWORD ; the additional x offset of viewport + LOCAL VPbpr:DWORD ; the number of bytes per row of viewport + + ;*=================================================================== + ;* save off the viewport characteristics on the stack + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width from viewport + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; get height from viewport + mov edx,[(GraphicViewPort ebx).GVPXAdd] ; get xadd from viewport + mov [VPwidth],eax ; store the width of locally + mov [VPheight],ecx + mov [VPxadd],edx + add eax,edx + mov [VPbpr],eax + + ;*=================================================================== + ;* move the important parameters into local registers + ;*=================================================================== + movsx eax,[x1_pixel] + movsx ebx,[y1_pixel] + movsx ecx,[x2_pixel] + movsx edx,[y2_pixel] + + ;*=================================================================== + ;* Convert the x2 and y2 pixel to a width and height + ;*=================================================================== + cmp eax,ecx + jl ??no_swap_x + xchg eax,ecx + +??no_swap_x: + sub ecx,eax + cmp ebx,edx + jl ??no_swap_y + xchg ebx,edx +??no_swap_y: + sub edx,ebx + inc ecx + inc edx + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + cmp eax, [VPwidth] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sx_done ; if it's not negative, it's ok + + ;------ Clip source X to left edge of screen. + add ecx, eax ; Reduce width (add in negative src X). + xor eax, eax ; Clip to left of screen. +??sx_done: + + ;*=================================================================== + ;* Bounds check source Y. + ;*=================================================================== + cmp ebx, [VPheight] ; compare with the max + jge ??out ; starts off screen, then later + jb short ??sy_done ; if it's not negative, it's ok + + ;------ Clip source Y to top edge of screen. + add edx, ebx ; Reduce height (add in negative src Y). + xor ebx, ebx ; Clip to top of screen. + +??sy_done: + ;*=================================================================== + ;* Bounds check width versus width of source and dest view ports + ;*=================================================================== + push ebx ; save off ebx for later use + mov ebx,[VPwidth] ; get the source width + sub ebx, eax ; Maximum allowed pixel width (given coordinates). + sub ebx, ecx ; Pixel width undershoot. + jns short ??width_ok ; if not signed no adjustment necessary + add ecx, ebx ; Reduce width to screen limits. + +??width_ok: + pop ebx ; restore ebx to old value + + ;*=================================================================== + ;* Bounds check height versus height of source view port + ;*=================================================================== + push eax ; save of eax for later use + mov eax, [VPheight] ; get the source height + sub eax, ebx ; Maximum allowed pixel height (given coordinates). + sub eax, edx ; Pixel height undershoot. + jns short ??height_ok ; if not signed no adjustment necessary + add edx, eax ; Reduce height to screen limits. +??height_ok: + pop eax ; restore eax to old value + + ;*=================================================================== + ;* Perform the last minute checks on the width and height + ;*=================================================================== + or ecx,ecx + jz ??out + + or edx,edx + jz ??out + + cmp ecx,[VPwidth] + ja ??out + cmp edx,[VPheight] + ja ??out + + ;*=================================================================== + ;* Get the offset into the virtual viewport. + ;*=================================================================== + xchg edi,eax ; save off the contents of eax + xchg esi,edx ; and edx for size test + mov eax,ebx ; move the y pixel into eax + mul [VPbpr] ; multiply by bytes per row + add edi,eax ; add the result into the x position + mov ebx,[this] + add edi,[(GraphicViewPort ebx).GVPOffset] + + mov edx,esi ; restore edx back to real value + mov eax,ecx ; store total width in ecx + sub eax,[VPwidth] ; modify xadd value to include clipped + sub [VPxadd],eax ; width bytes (subtract a negative number) + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ebx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,bx + + ;*=================================================================== + ; If there is no row offset then adjust the width to be the size of + ; the entire viewport and adjust the height to be 1 + ;*=================================================================== + mov esi,[VPxadd] + or esi,esi ; set the flags for esi + jnz ??row_by_row_aligned ; and act on them + + xchg eax,ecx ; switch bit pattern and width + mul edx ; multiply by edx to get size + xchg eax,ecx ; switch size and bit pattern + mov edx,1 ; only 1 line off view port size to do + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== +??row_by_row_aligned: + mov ebp,ecx ; width saved in ebp + cmp ecx,OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??row_by_row ; if not then skip + + ;*=================================================================== + ; Figure out the alignment offset if there is any + ;*=================================================================== + mov ebx,edi ; get output position + and ebx,3 ; is there a remainder? + jz ??aligned_loop ; if not we are aligned + xor ebx,3 ; find number of align bytes + inc ebx ; this number is off by one + sub ebp,ebx ; subtract from width + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== +??aligned_loop: + mov ecx,ebx ; get number of bytes to align + rep stosb ; and move them over + mov ecx,ebp ; get number of aligned bytes + shr ecx,2 ; convert to DWORDS + rep stosd ; and move them over + mov ecx,ebp ; get number of aligned bytes + and ecx,3 ; find the remainder + rep stosb ; and move it over + add edi,esi ; fix the line offset + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + jmp ??exit ; we are all done + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??row_by_row: + mov ecx,ebp ; get total width in bytes + rep stosb ; store the width + add edi,esi ; handle the xadd + dec edx ; decrement the height + jnz ??row_by_row ; if any left then next line +??out: +??exit: + ret + ENDP MCGA_Fill_Rect + +END diff --git a/WWFLAT32/MCGAPRIM/GBUFFER.CPP b/WWFLAT32/MCGAPRIM/GBUFFER.CPP new file mode 100644 index 0000000..e37971c --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GBUFFER.CPP @@ -0,0 +1,315 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : February 23, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +#pragma inline + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + (gbuffer->Get_Width() * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + GraphicBuff = gbuffer; +} + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(long size, int w, int h, void *buffer) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + + Offset = (long)Buffer; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Size = w * h; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + + Offset = (long)Buffer; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr) +{ + GraphicBufferClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr) +{ + GraphicBufferClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} diff --git a/WWFLAT32/MCGAPRIM/GBUFFER.CPP.BAK b/WWFLAT32/MCGAPRIM/GBUFFER.CPP.BAK new file mode 100644 index 0000000..e5039f6 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GBUFFER.CPP.BAK @@ -0,0 +1,892 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "window.h" + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MCGAPRIM_H +#include "mcgaprim.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#include + + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr); +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + int Lock(void) const {return(TRUE);} + void Unlock(void) const {} + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + VOID Grey_Out_Region(int x, int y, int width, int height, int color); + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + GraphicBufferClass *GraphicBuff; // related graphic buff +}; + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + public: + GraphicBufferClass( long size = 64500, int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT, + VOID *buffer = 0); + GraphicBufferClass(int w, int h, void *buffer = 0); + ~GraphicBufferClass(); + +// void Scale_Rotate(BitmapClass &bmp,const TPoint2D &pt,long scale=0x0100,unsigned char angle=0); +}; + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return MCGA_Size_Of_Region(this, w, h); +} + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + MCGA_Put_Pixel(this, x, y, color); +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + return(MCGA_Get_Pixel(this, x, y)); +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + MCGA_Clear(this, color); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + return(MCGA_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(MCGA_Print(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + return(MCGA_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + MCGA_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window) +{ + MCGA_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX]<<3, WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH]<<3, WindowList[clip_window][WINDOWHEIGHT]); +} + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Draw_Line(this, sx, sy, dx, dy, color); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Fill_Rect(this, sx, sy, dx, dy, color); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + MCGA_Remap(this, sx, sy, width, height, remap); +} +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + MCGA_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + MCGA_Remap(this, 0, 0, Width, Height, remap); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + + +#endif diff --git a/WWFLAT32/MCGAPRIM/GBUFFER.H b/WWFLAT32/MCGAPRIM/GBUFFER.H new file mode 100644 index 0000000..7449b24 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GBUFFER.H @@ -0,0 +1,892 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "window.h" + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MCGAPRIM_H +#include "mcgaprim.h" +#endif + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +#include + + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass *ptr); +GraphicBufferClass *Set_Logic_Page(GraphicBufferClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + int Lock(void) const {return(TRUE);} + void Unlock(void) const {} + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + VOID Grey_Out_Region(int x, int y, int width, int height, int color); + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + GraphicBufferClass *GraphicBuff; // related graphic buff +}; + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + public: + GraphicBufferClass( long size = 64500, int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT, + VOID *buffer = 0); + GraphicBufferClass(int w, int h, void *buffer = 0); + ~GraphicBufferClass(); + +// void Scale_Rotate(BitmapClass &bmp,const TPoint2D &pt,long scale=0x0100,unsigned char angle=0); +}; + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return MCGA_Size_Of_Region(this, w, h); +} + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + MCGA_Put_Pixel(this, x, y, color); +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + return(MCGA_Get_Pixel(this, x, y)); +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + MCGA_Clear(this, color); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(MCGA_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + return(MCGA_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(Linear_Blit_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(MCGA_Print(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + return(MCGA_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + return(MCGA_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + MCGA_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window) +{ + MCGA_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); +} + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Draw_Line(this, sx, sy, dx, dy, color); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + MCGA_Fill_Rect(this, sx, sy, dx, dy, color); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + MCGA_Remap(this, sx, sy, width, height, remap); +} +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + MCGA_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + MCGA_Remap(this, 0, 0, Width, Height, remap); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + return(MCGA_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + + +#endif diff --git a/WWFLAT32/MCGAPRIM/GBUFFER.INC b/WWFLAT32/MCGAPRIM/GBUFFER.INC new file mode 100644 index 0000000..457af5e --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GBUFFER.INC @@ -0,0 +1,60 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + +STRUC VideoViewPort +VIVPOffset DD ? ; offset to virtual viewport +VIVPWidth DD ? ; width of virtual viewport +VIVPHeight DD ? ; height of virtual viewport +VIVPXAdd DD ? ; x mod to get to next line +VIVPXPos DD ? ; x pos relative to Graphic Buff +VIVPYPos DD ? ; y pos relative to Graphic Buff +VIVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS diff --git a/WWFLAT32/MCGAPRIM/GETCLIP.ASM b/WWFLAT32/MCGAPRIM/GETCLIP.ASM new file mode 100644 index 0000000..3f4d2d6 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GETCLIP.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Feb 6, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +; typedef struct { +; int x0 , y0 ; +; int x1 , y1 ; +; } CLIP_WIN ; +; Note for efficiency reasons x1 must be >= x0 and y1 >= y0 +; int get_clip ( CLIP_WIN * window , CLIP_WIN * sorce_rect ) ; + +CODESEG + + PROC get_clip C near + USES eax , ebx + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG win : dword + ARG rect : dword + + + mov edi , [ rect ] + mov esi , [ win ] + xor eax , eax + xor edx , edx + + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x0 ] + sub ebx , [ (RECTANGLE esi) . x0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . x1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . x0 ] + mov ebx , [ (RECTANGLE edi) . x1 ] + sub ecx , [ (RECTANGLE esi) . x1 ] + sub ebx , [ (RECTANGLE esi) . x1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y0 ] + sub ebx , [ (RECTANGLE esi) . y0 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + +; mov ebx , [ (RECTANGLE esi) . y1 ] +; inc ebx +; mov [ rect ] , ebx + mov ecx , [ (RECTANGLE edi) . y0 ] + mov ebx , [ (RECTANGLE edi) . y1 ] + sub ecx , [ (RECTANGLE esi) . y1 ] + sub ebx , [ (RECTANGLE esi) . y1 ] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , dl + ret + ENDP get_clip + + + +END diff --git a/WWFLAT32/MCGAPRIM/GETPIX.ASM b/WWFLAT32/MCGAPRIM/GETPIX.ASM new file mode 100644 index 0000000..a239ece --- /dev/null +++ b/WWFLAT32/MCGAPRIM/GETPIX.ASM @@ -0,0 +1,105 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC MCGA_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP MCGA_Get_Pixel + +END diff --git a/WWFLAT32/MCGAPRIM/MAKEFILE b/WWFLAT32/MCGAPRIM/MAKEFILE new file mode 100644 index 0000000..e85c833 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/MAKEFILE @@ -0,0 +1,214 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mcgaprim +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- + +OBJECTS = & + bitblit.obj & + buffer.obj & + buffglbl.obj & + clear.obj & + drawline.obj & + drawrect.obj & + fillquad.obj & + fillrect.obj & + gbuffer.obj & + getclip.obj & + getpix.obj & + putpix.obj & + remap.obj & + scale.obj & + shadow.obj & + stamp.obj & + szregion.obj & + tobuff.obj & + topage.obj & + txtprnt.obj & + vbuffer.obj & + vclear.obj & + vesa.obj & + vgetpix.obj & + vlbtove.obj & + vputpix.obj & + vscale.obj & + vscltove.obj & + vtobuff.obj & + vtopage.obj & + vtxtprnt.obj & + vvblit.obj & + vvetolb.obj & + vvetoscl.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/MCGAPRIM/MCGAPRIM.H b/WWFLAT32/MCGAPRIM/MCGAPRIM.H new file mode 100644 index 0000000..7e924f2 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/MCGAPRIM.H @@ -0,0 +1,107 @@ +/* +** 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 . +*/ + + + +#ifndef MCGAPRIM_H +#define MCGAPRIM_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + extern long MCGA_Size_Of_Region(void *thisptr, int w, int h); + + extern void MCGA_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int MCGA_Get_Pixel(void * thisptr, int x, int y); + extern void MCGA_Clear(void *thisptr, unsigned char color); + extern long MCGA_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long MCGA_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + extern BOOL Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + extern void Vesa_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + extern int Vesa_Get_Pixel(void * thisptr, int x, int y); + extern void Vesa_Clear(void *thisptr, unsigned char color); + extern long Vesa_To_Buffer(void *thisptr, int x, int y, int w, int h, void *buff, long size); + extern long Vesa_Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + + extern BOOL Linear_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Vesa_Blit_To_Vesa( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + extern BOOL Linear_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern BOOL Vesa_Scale_To_Vesa( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + extern LONG MCGA_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + extern LONG Vesa_Print( void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + extern VOID MCGA_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + extern VOID MCGA_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + extern VOID MCGA_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + extern void MCGA_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + extern void MCGA_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int min_x, int min_y, int max_x, int max_y); + + extern void Shadow_Blit(long int xpix, long int ypix, long int width, long int height, GraphicViewPortClass &src, VideoBufferClass &dst, void *shadowbuff); + + extern void *Get_Font_Palette_Ptr(void); +// extern int Get_Standard_Selector(VOID); +// extern VOID Set_Selector(UWORD sel); +#ifdef __cplusplus +} +#endif + +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern void (*VVPC_Clear_Func)(void *, unsigned char); +extern long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +extern void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +extern int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +extern long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *buffer, void *view); +extern BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +extern BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +extern LONG (*VVPC_Print_Func)(void *, const char *, int, int, int, int); +extern GraphicBufferClass *LogicPage; +#endif diff --git a/WWFLAT32/MCGAPRIM/MCGAPRIM.INC b/WWFLAT32/MCGAPRIM/MCGAPRIM.INC new file mode 100644 index 0000000..87a0ec3 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/MCGAPRIM.INC @@ -0,0 +1,124 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR +GLOBAL C Get_Font_Palette_Ptr :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR +GLOBAL MCGA_Draw_Stamp_Clip :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + diff --git a/WWFLAT32/MCGAPRIM/MCGAPRIM.PJT b/WWFLAT32/MCGAPRIM/MCGAPRIM.PJT new file mode 100644 index 0000000..0e82f8b --- /dev/null +++ b/WWFLAT32/MCGAPRIM/MCGAPRIM.PJT @@ -0,0 +1,101 @@ +;Codewright Project File (do not remove or modify this line) +[ProjInit] +ProjSetConfigFlags=0x00010140 + +[Files] +d:\wwflat32\mcgaprim\bitblit.asm +d:\wwflat32\mcgaprim\buffer.cpp +d:\wwflat32\mcgaprim\buffer.h +d:\wwflat32\mcgaprim\buffglbl.cpp +d:\wwflat32\mcgaprim\clear.asm +d:\wwflat32\mcgaprim\drawline.asm +d:\wwflat32\mcgaprim\drawrect.cpp +d:\wwflat32\mcgaprim\fillquad.asm +d:\wwflat32\mcgaprim\fillrect.asm +d:\wwflat32\mcgaprim\gbuffer.cpp +d:\wwflat32\mcgaprim\gbuffer.h +d:\wwflat32\mcgaprim\gbuffer.inc +d:\wwflat32\mcgaprim\getclip.asm +d:\wwflat32\mcgaprim\getpix.asm +d:\wwflat32\mcgaprim\mcgaprim.h +d:\wwflat32\mcgaprim\mcgaprim.inc +d:\wwflat32\mcgaprim\putpix.asm +d:\wwflat32\mcgaprim\regionsz.cpp +d:\wwflat32\mcgaprim\remap.asm +d:\wwflat32\mcgaprim\scale.asm +d:\wwflat32\mcgaprim\shadow.asm +d:\wwflat32\mcgaprim\stamp.asm +d:\wwflat32\mcgaprim\stamp.inc +d:\wwflat32\mcgaprim\szregion.asm +d:\wwflat32\mcgaprim\tobuff.asm +d:\wwflat32\mcgaprim\topage.asm +d:\wwflat32\mcgaprim\txtprnt.asm +d:\wwflat32\mcgaprim\vbitblit.asm +d:\wwflat32\mcgaprim\vbuffer.cpp +d:\wwflat32\mcgaprim\vbuffer.h +d:\wwflat32\mcgaprim\vclear.asm +d:\wwflat32\mcgaprim\vesa.asm +d:\wwflat32\mcgaprim\vgetpix.asm +d:\wwflat32\mcgaprim\vlbtove.asm +d:\wwflat32\mcgaprim\vputpix.asm +d:\wwflat32\mcgaprim\vscale.asm +d:\wwflat32\mcgaprim\vscltove.asm +d:\wwflat32\mcgaprim\vtobuff.asm +d:\wwflat32\mcgaprim\vtopage.asm +d:\wwflat32\mcgaprim\vtxtprnt.asm +d:\wwflat32\mcgaprim\vvblit.asm +d:\wwflat32\mcgaprim\vvetolb.asm +d:\wwflat32\mcgaprim\vvetoscl.asm + + +[State] +SysSetCwd='C:\PROJECTS\C&CZERO\WWFLAT32\MCGAPRIM' +SrchSetFlags=0x20aa +FileSortMode=0x0 +StateWindowFrame=69,91,417,932,0x6989f5fa +_OutputWindowPosition=1,-3,-3,1286,1030,0,0 +_StateWindow=87,87,990,623,0x00000118,'c:\projects\c&czero\wwflat32\mcgaprim\stamp.inc',240,7,244,32,32,0,32,32,32,32,8,65535,65535,1,0,'Terminal',65520,255,49,0,4,243,14,15,247,247,253,2,1,400,0,246,252,245,242,241,247,0,0,0 +_StateBuffer='c:\projects\c&czero\wwflat32\mcgaprim\stamp.inc',0x0400048e,2,1,25,'9 17','',0x0,'' +_StateBuffer='c:\projects\c&czero\wwflat32\mcgaprim\stamp.asm',0x0400048e,1,9,25,'9 17','',0x0,'' +WrapEnable=0 +_StateHistory=FILELIST,'d:\wwflat32\mcgaprim\stamp.asm','c:\projects\c&czero\wwflat32\mcgaprim\stamp.asm','c:\projects\c&czero\wwflat32\mcgaprim\stamp.inc' +_StateHistory=DIRECTORY,'C:\PROJECTS\C&CZERO\WWFLAT32\MCGAPRIM' + +[Editor] +ClipboardSetTermStr='\r\n',0 +ClipboardEnableTermStr=1 +ClipboardSetSepStr='\r\n',0 +ClipboardEnableSepStr=1 +ScrapSetCount=1 +_RestoreSysFlags=0x6989f5fa, 0xfffffffc + +[Compiler] +BrowseSetFile='c:\projects\c&czero\wwflat32\mcgaprim\mcgaprim.ptg' +TagSetFile='c:\projects\c&czero\wwflat32\mcgaprim\mcgaprim.tag' +CompilerAddBuild='Microsoft Assembler',1073741880,'ftee masm -w2 -zi %r%e;','','','','','_MicrosoftErrorInfo','','%v%p','','','','','',0 +CompilerAddResponse='Microsoft Assembler', +CompilerAddBuild='Borland C++',1073741880,'ftee bcc -S %r.c','ftee make %r.obj','ftee make %r.obj','','','_BorlandCppErrorInfo','','%v%p','','','','','',0 +CompilerAddResponse='Borland C++', +CompilerAddBuild='Borland Turbo Assembler',1073741880,'ftee make %r.obj','ftee make %r.obj','','','','_TasmErrorInfo','','%v%p','','','','','',0 +CompilerAddResponse='Borland Turbo Assembler', +CompilerAddBuild='$_cw_proj_hash_$',1073741873,'','ftee wmake %r.obj','ftee wmake ','','','_MSLinkErrorInfo','proj.err','c:\projects\c&czero\wwflat32\mcgaprim','','_MicrosoftErrorInfo','_NMakeErrorInfo','','',197376 +CompilerAddResponse='$_cw_proj_hash_$', +CompilerAddBuild='Default Project',1073741880,'','ftee make','ftee make','','','_ErrorInfoDefault','proj.err','%v%p','','','','','',0 +CompilerAddResponse='Default Project', +CompilerAddBuild='Microsoft C',1073741880,'ftee cl -c -AL -Gsw -Ow -Zpe %r%e','','','','','_MicrosoftErrorInfo','','%v%p','','','','','',0 +CompilerAddResponse='Microsoft C', +CompilerAddBuild='Script',1073741880,'ftee make %r.inf','ftee make','ftee make','','','_BorlandCppErrorInfo','','','','','','','',0 +CompilerAddResponse='Script', +CompilerAddBuild='Zortech C++',1073741880,'ftee ztc -a -b -c -g -ml -W %r%e','','','','','_ZortechCppErrorInfo','','%v%p','','','','','',0 +CompilerAddResponse='Zortech C++', +CompilerAssign='Borland C++','.scr' +CompilerNewExt=.bas +CompilerAssign='Borland C++','.int' +CompilerAssign='Borland C++','.c' +CompilerNewExt=.prg +CompilerAssign='Microsoft C','.h' +CompilerAssign='Borland C++','.cpp' +CompilerAssign='Default Project','.*' +CompilerAssign='Zortech C++','.cxx' +CompilerAssign='Borland Turbo Assembler','.asm' +CompilerAssign='Borland C++','.hpp' diff --git a/WWFLAT32/MCGAPRIM/PUTPIX.ASM b/WWFLAT32/MCGAPRIM/PUTPIX.ASM new file mode 100644 index 0000000..599d461 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/PUTPIX.ASM @@ -0,0 +1,109 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC MCGA_Put_Pixel C near + USES eax,ebx,ecx,edx,edi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + xor eax,eax + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; edx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(GraphicViewPort ebx).GVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen +??exit: + ret + ENDP MCGA_Put_Pixel + +END diff --git a/WWFLAT32/MCGAPRIM/REGIONSZ.CPP b/WWFLAT32/MCGAPRIM/REGIONSZ.CPP new file mode 100644 index 0000000..7dd9965 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/REGIONSZ.CPP @@ -0,0 +1,60 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB 32 * + * * + * File Name : REGIONSZ.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : November 3, 1994 * + * * + * Last Update : November 3, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Size_Of_Region -- Calculates the size of a given region * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * SIZE_OF_REGION -- Calculates the size of a given region * + * * + * INPUT: int width - the width of the region * + * int height - the height of the region * + * * + * OUTPUT: long - the size of the region * + * * + * HISTORY: * + * 11/03/1994 PWG : Created. * + *=========================================================================*/ +long Size_Of_Region(int width, int height) +{ + return(width * height); +} diff --git a/WWFLAT32/MCGAPRIM/REMAP.ASM b/WWFLAT32/MCGAPRIM/REMAP.ASM new file mode 100644 index 0000000..e1278d1 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/REMAP.ASM @@ -0,0 +1,174 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + PROC MCGA_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG width :DWORD + ARG height :DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword + + + cmp [ remap ] , 0 + jz ??real_out + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_remap + + test cl , 1000b + jz ??scr_left_ok + mov [ x0_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y0_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_remap + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +??do_remap: + cld + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + mov ebx , [ x1_pixel ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle ??real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle ??real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +??outer_loop: + mov ebx , [ counter_x ] +??inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz ??inner_loop + add edi , esi + dec ecx + jnz ??outer_loop + + + + +??real_out: + ret + + ENDP MCGA_Remap + + END diff --git a/WWFLAT32/MCGAPRIM/SCALE.ASM b/WWFLAT32/MCGAPRIM/SCALE.ASM new file mode 100644 index 0000000..6f1e6c1 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/SCALE.ASM @@ -0,0 +1,568 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : June 21, 1994 [PWG] * +;* New version : feb 12, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* Normal_Remapped_Draw -- jump loc for draw scaled line of remap pixel * +;* Transparent_Draw -- jump loc for scaled line of transparent pixels * +;* Transparent_Remapped_Draw -- jump loc for scaled remap trans pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je ??all_done + cmp [dst_height],0 + je ??all_done + cmp [src_width],0 + je ??all_done + cmp [src_height],0 + je ??all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??clip_against_dest + mov bl , dl + test cl , 1000b + jz ??src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + +??src_left_ok: + test cl , 0010b + jz ??src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + +??src_bottom_ok: + test bl , 0100b + jz ??src_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + +??src_right_ok: + test bl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + +; Clip destination Rectangle against source Window boundaries. +??clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??do_scaling + mov bl , dl + test cl , 1000b + jz ??dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + +??dst_left_ok: + test cl , 0010b + jz ??dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + +??dst_bottom_ok: + test bl , 0100b + jz ??dst_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + +??dst_right_ok: + test bl , 0001b + jz ??do_scaling + + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + +??do_scaling: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jle ??all_done + sub ebx , [ dst_x0 ] + jle ??all_done + + mov [ counter_y ] , ecx + + cmp [ trans ] , 0 + jnz ??transparency + + cmp [ remap ] , 0 + jnz ??normal_remap + +; ************************************************************************* +; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ??ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + ??outter_loop: + push esi + push edi + xor ecx , ecx + mov ebx , [ counter_x ] + jmp [ entry ] + ??inner_loop: + REPT 32 + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + ENDM + ??ref_point: + dec ebx + jge ??inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??normal_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ ??remapref_point + ecx ] + mov [ entry ] , ecx + + ??remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??remapinner_loop: + REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + ENDM + ??remapref_point: + dec [ remap_counter ] + jge ??remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??remapoutter_loop + ret + + +;**************************************************************************** +; scale with trnsparency + +??transparency: + cmp [ remap ] , 0 + jnz ??trans_remap + +; ************************************************************************* +; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_ref_point + ecx ] + mov [ entry ] , ecx + + ??trans_outter_loop: + xor ecx , ecx + push esi + push edi + mov ebx , [ counter_x ] + jmp [ entry ] + ??trans_inner_loop: + REPT 32 + local trans_pixel + mov cl , [ esi ] + test cl , cl + jz trans_pixel + mov [ edi ] , cl + trans_pixel: + add ecx , eax + adc esi , edx + inc edi + ENDM + ??trans_ref_point: + dec ebx + jge ??trans_inner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_remapref_point + ecx ] + mov [ entry ] , ecx + + ??trans_remapoutter_loop: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??trans_remapinner_loop: + REPT 32 + local trans_pixel + mov bl , [ esi ] + test bl , bl + jz trans_pixel + mov cl , [ eax + ebx ] + mov [ edi ] , cl + trans_pixel: + add ecx , [ dx_frac ] + adc esi , edx + inc edi + ENDM + ??trans_remapref_point: + dec [ remap_counter ] + jge ??trans_remapinner_loop + + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_remapoutter_loop + ret + + + + + +??all_done: + ret +endp + + +END diff --git a/WWFLAT32/MCGAPRIM/SHADOW.ASM b/WWFLAT32/MCGAPRIM/SHADOW.ASM new file mode 100644 index 0000000..5c936a2 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/SHADOW.ASM @@ -0,0 +1,210 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./shadow.asm 1.9 1994/05/20 15:30:49 joe_bostic Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : SHADOW.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : February 28, 1995 [BG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, GVPC dst, void *shadowbuff); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" +INCLUDE ".\keystruc.inc" + +GLOBAL Shadow_Blit : NEAR + +GLOBAL RealModePtr : DWORD +GLOBAL Hide_Mouse : NEAR +GLOBAL Show_Mouse : NEAR + + CODESEG + +; void Shadow_Blit(int xpix, int ypix, int width, int height, GVPC src, VBC dst, void *shadowbuff); +; Warning: Shadow_Blit appears to be relatively stupid, in that while it is +; optimized to perform word or dword blits, it only does so if the +; entire region is word or dword-sized. In other words, if you blit +; a region that is 200 pixels wide (clearly dword-sized), then it +; will use the dword loop. However, if you blit a region that is +; 201 pixels wide, the dumb thing will use the byte loop for the +; entire blit. + PROC Shadow_Blit C near + USES eax,ebx,ecx,edx,esi,edi + + ARG x:DWORD + ARG y:DWORD + ARG width:DWORD + ARG height:DWORD + ARG srcpage:DWORD + ARG dstpage:DWORD + ARG shadow:DWORD + + LOCAL modulo:DWORD ; Row modulo + LOCAL hidemouse:DWORD ; Was the mouse hidden? + LOCAL dwordwidth:DWORD + LOCAL bytewidth:DWORD + + cld ; Always move forward. + mov [hidemouse],0 ; Presume mouse hasn't been hidden. + +; Fetch the segment of the seenpage. + mov ebx,[dstpage] + mov ebx,[(GraphicViewPort ebx).GVPOffset] + + ; Determine if the mouse needs to be hidden at all. If this happens to be + ; a shadow blit to non visible page (who knows why!?) then don't bother to + ; hide the mouse since it isn't necessary. +; cmp ebx,0A0000h +; setne [BYTE PTR hidemouse] ; Flag that mouse need not be hidden. +; jne short ??itsok + mov esi,[RealModePtr] + cmp [(KeyboardType esi).MState],0 + je short ??itsok + mov [hidemouse],1 + call Hide_Mouse C ; Hides mouse again (just to be sure). +??itsok: + mov edi,[srcpage] + mov esi,[(GraphicViewPort edi).GVPOffset] + + mov eax,[(GraphicViewPort edi).GVPWidth] + add eax,[(GraphicViewPort edi).GVPXAdd] + push eax ; save width+xadd for later calc + mov edx,[y] + mul edx + add eax,[x] + add esi,eax +; At this point, esi points to the source page and ebx points to the dest page + sub ebx,esi ; esi+ebx == dest page pointer + + mov edi,[shadow] ; EDI points to shadow buffer. + + mov ecx,[height] ; get the height of the window in cx + + mov edx,[RealModePtr] + + ; Calculate the row add module. + pop eax ; restore width+xadd + sub eax,[width] + mov [modulo],eax + + mov eax,[width] + shr eax,2 + mov [dwordwidth],eax + mov eax,[width] + and eax,3 + mov [bytewidth],eax + +;--------------------------------------- +; DOUBLE WORD shadow blit if possible. +;--------------------------------------- +??dloop_top: + push ecx + mov ecx,[dwordwidth] + +??lcontinue: + repe cmpsd ; check the entire row for changed longs + je short ??loop_top + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??dok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??dok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jb short ??dok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??dok: + + mov eax,[esi-4] + mov [ebx+esi-4],eax ; Update destination page. + mov [edi-4],eax ; Update shadow buffer. + or ecx,ecx + jne short ??lcontinue + +;--------------------------------------- +; Row loop start for BYTES. +;--------------------------------------- +??loop_top: + mov ecx,[bytewidth] + +; Column loop start -- by bytes. +??continue: + repe cmpsb ; check the entire row for changed longs + je short ??done_x + + ; If this row would interfere with the mouse image, then hide it. + cmp [hidemouse],0 + jnz short ??bok + mov eax,[(KeyboardType edx).MouseY] + sub eax,[(KeyboardType edx).MouseYHot] + cmp eax,[y] + jg short ??bok + add eax,[(KeyboardType edx).MouseHeight] + cmp eax,[y] + jl short ??bok + mov [hidemouse],1 ; Manual hide of the mouse. + call Hide_Mouse C +??bok: + + mov al,[esi-1] + mov [ebx+esi-1],al ; Update destination page. + mov [edi-1],al ; Update shadow buffer. + + or ecx,ecx + jne short ??continue + +??done_x: + inc [y] + add esi,[modulo] + pop ecx + loop ??dloop_top + +??fini: + ; Re show the mouse if it was hidden by this routine. + cmp [hidemouse],0 + je short ??reallyfini + call Show_Mouse C +??reallyfini: + ret + + ENDP Shadow_Blit + + END + + diff --git a/WWFLAT32/MCGAPRIM/STAMP.ASM b/WWFLAT32/MCGAPRIM/STAMP.ASM new file mode 100644 index 0000000..76ab5b1 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/STAMP.ASM @@ -0,0 +1,578 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : STAMP.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 23, 1993 * +;* * +;* Last Update : August 23, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +INCLUDE "stamp.inc" + + DATASEG + + +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + EVEN + + CODESEG + +; 256 color icon system. + +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* + PROC Init_Stamps C near USES eax ebx edi + ARG icondata:DWORD + + ; Verify legality of parameter. + cmp [icondata],0 + je short ??fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short ??fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[(IControl_Type edi).Count] + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[(IControl_Type edi).Width] + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[(IControl_Type edi).Height] + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[(IControl_Type edi).Map] + add eax,edi + mov [MapPtr],eax + +??nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[(IControl_Type edi).Icons] + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[(IControl_Type edi).TransFlag] + mov [IsTrans],eax + +??fini: + ret + + ENDP Init_Stamps + + +;*********************************************************** + + +;*********************************************************** +; DRAW_STAMP +; +; VOID cdecl MCGA_Draw_Stamp(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC MCGA_Draw_Stamp C near + + ARG this:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset + call Init_Stamps C,eax +??noreset: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap: + cmp ebx,[IconCount] + jae ??out + mov [icon],ebx ; Updated icon number. + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[IconWidth] + mov [modulo],eax + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. +;;; mov edx,[remap] + mov ebx,[remap] + xor eax,eax +??xrowloop: + push ecx + mov ecx,[iwidth] + +??xcolumnloop: + lodsb +;;; mov ebx,edx +;;; add ebx,eax +;;; mov al,[ebx] ; New real color to draw. + xlatb + or al,al + jz short ??xskip1 ; Transparency skip check. + mov [edi],al +??xskip1: + inc edi + loop ??xcolumnloop + + pop ecx + add edi,[modulo] + loop ??xrowloop + jmp short ??out + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloop + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + shr ebx,2 + mov edx,[modulo] + mov eax,[iwidth] + shr eax,2 +??loop1: + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + mov ecx,eax + rep movsd + add edi,edx + + dec ebx + jnz ??loop1 + jmp short ??out + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloop: + push ecx + mov ecx,[iwidth] + +??columnloop: + lodsb + or al,al + jz short ??skip1 ; Transparency check. + mov [edi],al +??skip1: + inc edi + loop ??columnloop + + pop ecx + add edi,[modulo] + loop ??rowloop + + ; Cleanup and exit icon drawing routine. +??out: + popad + ret + + ENDP MCGA_Draw_Stamp + +;*********************************************************** +; DRAW_STAMP_CLIP +; +; VOID cdecl MCGA_Draw_Stamp_Clip(VOID *icondata, WORD icon, WORD x_pixel, WORD y_pixel, VOID *remap, LONG min_x, LONG min_y, LONG max_x, LONG max_y); +; +; This routine renders the icon at the given coordinate. +; +; The remap table is a 256 byte simple pixel translation table to use when +; drawing the icon. Transparency check is performed AFTER the remap so it is possible to +; remap valid colors to be invisible (for special effect reasons). +; This routine is fastest when no remap table is passed in. +;* + PROC MCGA_Draw_Stamp_Clip C near + + ARG this:DWORD ; this is a member function + ARG icondata:DWORD ; Pointer to icondata. + ARG icon:DWORD ; Icon number to draw. + ARG x_pixel:DWORD ; X coordinate of icon. + ARG y_pixel:DWORD ; Y coordinate of icon. + ARG remap:DWORD ; Remap table. + ARG min_x:DWORD ; Clipping rectangle boundary + ARG min_y:DWORD ; Clipping rectangle boundary + ARG max_x:DWORD ; Clipping rectangle boundary + ARG max_y:DWORD ; Clipping rectangle boundary + + LOCAL modulo:DWORD ; Modulo to get to next row. + LOCAL iwidth:DWORD ; Icon width (here for speedy access). + LOCAL skip:DWORD ; amount to skip per row of icon data + LOCAL doremap:BYTE ; Should remapping occur? + + pushad + cmp [icondata],0 + je ??out2 + + ; Initialize the stamp data if necessary. + mov eax,[icondata] + cmp [LastIconset],eax + je short ??noreset2 + call Init_Stamps C,eax +??noreset2: + + ; Determine if the icon number requested is actually in the set. + ; Perform the logical icon to actual icon number remap if necessary. + mov ebx,[icon] + cmp [MapPtr],0 + je short ??notmap2 + mov edi,[MapPtr] + mov bl,[edi+ebx] +??notmap2: + cmp ebx,[IconCount] + jae ??out2 + mov [icon],ebx ; Updated icon number. + + ; Setup some working variables. + mov ecx,[IconHeight] ; Row counter. + mov eax,[IconWidth] + mov [iwidth],eax ; Stack copy of byte width for easy BP access. + + ; Fetch pointer to start of icon's data. ESI = ptr to icon data. + mov eax,[icon] + mul [IconSize] + mov esi,[StampPtr] + add esi,eax + + ; Update the clipping window coordinates to be valid maxes instead of width & height + ; , and change the coordinates to be window-relative + mov ebx,[min_x] + add [max_x],ebx + add [x_pixel],ebx ; make it window-relative + mov ebx,[min_y] + add [max_y],ebx + add [y_pixel],ebx ; make it window-relative + + ; See if the icon is within the clipping window + ; First, verify that the icon position is less than the maximums + mov ebx,[x_pixel] + cmp ebx,[max_x] + jge ??out2 + mov ebx,[y_pixel] + cmp ebx,[max_y] + jge ??out2 + ; Now verify that the icon position is >= the minimums + add ebx,[IconHeight] + cmp ebx,[min_y] + jle ??out2 + mov ebx,[x_pixel] + add ebx,[IconWidth] + cmp ebx,[min_x] + jle ??out2 + + ; Now, clip the x, y, width, and height variables to be within the + ; clipping rectangle + mov ebx,[x_pixel] + cmp ebx,[min_x] + jge ??nominxclip + ; x < minx, so must clip + mov ebx,[min_x] + sub ebx,[x_pixel] + add esi,ebx ; source ptr += (minx - x) + sub [iwidth],ebx ; icon width -= (minx - x) + mov ebx,[min_x] + mov [x_pixel],ebx + +??nominxclip: + mov eax,[IconWidth] + sub eax,[iwidth] + mov [skip],eax + + ; Check for x+width > max_x + mov eax,[x_pixel] + add eax,[iwidth] + cmp eax,[max_x] + jle ??nomaxxclip + ; x+width is greater than max_x, so must clip width down + mov eax,[iwidth] ; eax = old width + mov ebx,[max_x] + sub ebx,[x_pixel] + mov [iwidth],ebx ; iwidth = max_x - xpixel + sub eax,ebx + add [skip],eax ; skip += (old width - iwidth) +??nomaxxclip: + ; check if y < miny + mov eax,[min_y] + cmp eax,[y_pixel] ; if(miny <= y_pixel), no clip needed + jle ??nominyclip + sub eax,[y_pixel] + sub ecx,eax ; height -= (miny - y) + mul [IconWidth] + add esi,eax ; icon source ptr += (width * (miny - y)) + mov eax,[min_y] + mov [y_pixel],eax ; y = miny +??nominyclip: + ; check if (y+height) > max y + mov eax,[y_pixel] + add eax,ecx + cmp eax,[max_y] ; if (y + height <= max_y), no clip needed + jle ??nomaxyclip + mov ecx,[max_y] ; height = max_y - y_pixel + sub ecx,[y_pixel] +??nomaxyclip: + + ; If the remap table pointer passed in is NULL, then flag this condition + ; so that the faster (non-remapping) icon draw loop will be used. + cmp [remap],0 + setne [doremap] + + ; Get pointer to position to render icon. EDI = ptr to destination page. + mov ebx,[this] + mov edi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPWidth] + add eax,[(GraphicViewPort ebx).GVPXAdd] + push eax ; save viewport full width for lower + mul [y_pixel] + add edi,eax + add edi,[x_pixel] + + ; Determine row modulo for advancing to next line. + pop eax ; retrieve viewport width + sub eax,[iwidth] + mov [modulo],eax + + ; Determine whether simple icon draw is sufficient or whether the + ; extra remapping icon draw is needed. + cmp [BYTE PTR doremap],0 + je short ??istranscheck2 + + ;************************************************************ + ; Complex icon draw -- extended remap. + ; EBX = Palette pointer (ready for XLAT instruction). + ; EDI = Pointer to icon destination in page. + ; ESI = Pointer to icon data. + ; ECX = Number of pixel rows. + mov ebx,[remap] + xor eax,eax +??xrowloopc: + push ecx + mov ecx,[iwidth] + +??xcolumnloopc: + lodsb + xlatb + or al,al + jz short ??xskip1c ; Transparency skip check. + mov [edi],al +??xskip1c: + inc edi + loop ??xcolumnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??xrowloopc + jmp short ??out2 + + + ;************************************************************ + ; Check to see if transparent or generic draw is necessary. +??istranscheck2: + mov ebx,[IsTrans] + add ebx,[icon] + cmp [BYTE PTR ebx],0 + jne short ??rowloopc + + ;************************************************************ + ; Fast non-transparent icon draw routine. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. + mov ebx,ecx + mov edx,[modulo] + mov eax,[iwidth] + + ; Check for dword-optimized loop possible + test eax,3 + jnz ??loop1c ; if eax & 3, must use byte loop + shr eax,2 +??loop1dw: + mov ecx,eax + rep movsd + add edi,edx + add esi,[skip] + dec ebx + jnz ??loop1dw + jmp short ??out2 + +??loop1c: + mov ecx,eax + rep movsb + add edi,edx + add esi,[skip] + + dec ebx + jnz ??loop1c + jmp short ??out2 + + ;************************************************************ + ; Transparent icon draw routine -- no extended remap. + ; ES:DI = Pointer to icon destination in page. + ; DS:SI = Pointer to icon data. + ; CX = Number of pixel rows. +??rowloopc: + push ecx + mov ecx,[iwidth] + +??columnloopc: + lodsb + or al,al + jz short ??skip1c ; Transparency check. + mov [edi],al +??skip1c: + inc edi + loop ??columnloopc + + pop ecx + add edi,[modulo] + add esi,[skip] + loop ??rowloopc + + ; Cleanup and exit icon drawing routine. +??out2: + popad + ret + + ENDP MCGA_Draw_Stamp_Clip + + END diff --git a/WWFLAT32/MCGAPRIM/STAMP.BAK b/WWFLAT32/MCGAPRIM/STAMP.BAK new file mode 100644 index 0000000..60047dc --- /dev/null +++ b/WWFLAT32/MCGAPRIM/STAMP.BAK @@ -0,0 +1,38 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? ; Width of icon map (in icons). +MapHeight DW ? ; Height of icon map (in icons). +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? ; Color control map offset. +Map DD ? ; Icon map offset. + ENDS + +ICON256 EQU 1 + diff --git a/WWFLAT32/MCGAPRIM/STAMP.INC b/WWFLAT32/MCGAPRIM/STAMP.INC new file mode 100644 index 0000000..d0c60e0 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/STAMP.INC @@ -0,0 +1,38 @@ +; +; 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 . +; + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? ; Width of icon map (in icons). +MapHeight DW ? ; Height of icon map (in icons). +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? ; Color control map offset. +Map DD ? ; Icon map offset. + ENDS + +ICON256 EQU 1 + diff --git a/WWFLAT32/MCGAPRIM/SZREGION.ASM b/WWFLAT32/MCGAPRIM/SZREGION.ASM new file mode 100644 index 0000000..10ec81f --- /dev/null +++ b/WWFLAT32/MCGAPRIM/SZREGION.ASM @@ -0,0 +1,103 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Calculate size of an MCGA region * +;* * +;* File Name : REGIONSZ.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : March 1, 1995 * +;* * +;* Last Update : March 1, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Size_Of_Region - calculate MCGA region size * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +GLOBAL MCGA_Size_Of_Region : NEAR + +CODESEG + +;*************************************************************************** +;* VVPC::Size_Of_Region - calculate MCGA region size * +;* * +;* INPUT: DWORD the width of the region * +;* * +;* DWORD the height of the region * +;* * +;* OUTPUT: calculated size of the region (MCGA = width * height) * +;* * +;* * +;* HISTORY: * +;* 03/01/1995 BWG : Created. * +;*=========================================================================* + PROC MCGA_Size_Of_Region C near + USES ebx,ecx,edx + + ARG this:DWORD ; this is a member function + ARG width:DWORD ; width of region + ARG height:DWORD ; height of region + + ;*=================================================================== + ; Get the viewport information + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + xor eax,eax + mov ecx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov edx,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + + ;*=================================================================== + ; Verify that the width is legal + ;*=================================================================== + mov eax,[width] ; find the width + cmp eax,edx ; is it too wide? + jb short ??wok ; if not, leave it alone + mov eax,edx ; otherwise clip it + + ;*=================================================================== + ; Verify that the height is ok + ;*=================================================================== +??wok: mov ebx,[height] ; get the height + cmp ebx,ecx ; is it too tall? + jb ??hok ; if not, leave it alone + mov ebx,ecx ; otherwise clip it + + ;*=================================================================== + ; Now multiply 'em to calculate the size of the region + ;*=================================================================== +??hok: mul ebx ; size = w * h + + ret + ENDP MCGA_Size_Of_Region + + END + + \ No newline at end of file diff --git a/WWFLAT32/MCGAPRIM/TOBUFF.ASM b/WWFLAT32/MCGAPRIM/TOBUFF.ASM new file mode 100644 index 0000000..a923de8 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/TOBUFF.ASM @@ -0,0 +1,292 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* Last Update : Feb 10, 1995 [jrj] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +TRANSP equ 0 + + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC MCGA_To_Buffer C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; this is a class member function + ARG x_pixel:DWORD ; Page X pixel coordinate. + ARG y_pixel:DWORD ; Page Y pixel coordinate. + ARG pixel_width:DWORD ; Width of region in pixels. + ARG pixel_height:DWORD ; Height of region in pixels. + ARG dest:DWORD ; the buffer to copy to + ARG size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov esi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ size ] + jg ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + cmp [ transp ] , 0 + jnz ??forward_Blit_trans +ENDIF + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + +ENDP MCGA_To_Buffer + +END + diff --git a/WWFLAT32/MCGAPRIM/TOPAGE.ASM b/WWFLAT32/MCGAPRIM/TOPAGE.ASM new file mode 100644 index 0000000..2587ca9 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/TOPAGE.ASM @@ -0,0 +1,294 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC MCGA_Buffer_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + local scr_x : dword + local scr_y : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + + cmp [ src ] , 0 + jz ??real_out + + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + sub eax , [ x_pixel ] + jle ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + + +??forward_Blit_trans: + + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + + ENDP MCGA_Buffer_To_Page +END + diff --git a/WWFLAT32/MCGAPRIM/TXTPRNT.ASM b/WWFLAT32/MCGAPRIM/TXTPRNT.ASM new file mode 100644 index 0000000..1ea52b7 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/TXTPRNT.ASM @@ -0,0 +1,465 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* MCGA_Print -- Assembly MCGA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL FontPtr:DWORD +GLOBAL FontXSpacing:DWORD +GLOBAL FontYSpacing:DWORD +GLOBAL ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* MCGA_PRINT -- Assembly MCGA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC MCGA_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + local ptr_string:dword ; pointer to string + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + + mov eax,[string] ; check that the string is not NULL + mov [ptr_string],eax + cmp eax,0 + jz ??done + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??done + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test x_pixel and y_pixel for negative values + mov edx,[y_pixel] ; get position y + mov ecx,[x_pixel] ; get position x + cmp edx,0 ; check for negative + jl ??done ; if so, we're out let here. + cmp ecx,0 ; check for negative + jl ??done ; if so, we're out let here. + + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,edx ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jge ??done ; if so, we're outa here. + mov [y_pixel],eax ; save for next line feed. y value for next line. + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a carry return? + je ??line_feed ; if so, go to special case. + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- +; JRJ 05/01/95 This is the problem However made this change introduced +; a error in the code, this function is not supposed to handle +; Text wrapping +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + dec [dword ptr string] ; overflow by one charater + jmp ??done + +; Now go into the line feed code..... +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??done ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + jmp ??next_char + +??done: + mov eax,[string] ; return the number of charaters + sub eax,[ptr_string] ; printed + ret + + ENDP MCGA_Print + +END diff --git a/WWFLAT32/MCGAPRIM/VBITBLIT.ASM b/WWFLAT32/MCGAPRIM/VBITBLIT.ASM new file mode 100644 index 0000000..c506537 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VBITBLIT.ASM @@ -0,0 +1,111 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VBITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Blit_To_Vesa -- copies graphic buffer to vesa screen * +;* Vesa_Blit_To_Linear -- Copies vesa screen to graphic buffer * +;* Vesa_Blit_To_Vesa -- Copies a section of vesa screen to vesa screen * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +CODESEG + +;*************************************************************************** +;* LINEAR_BLIT_TO_VESA -- copies graphic buffer to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Blit_To_Vesa C near + ret + ENDP Linear_Blit_To_Vesa + +;*************************************************************************** +;* VESA_BLIT_TO_LINEAR -- Copies vesa screen to graphic buffer * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Linear C near + ret + ENDP Vesa_Blit_To_Linear + +;*************************************************************************** +;* VESA_BLIT_TO_VESA -- Copies a section of vesa screen to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Vesa C near + ret + ENDP Vesa_Blit_To_Vesa +END + + diff --git a/WWFLAT32/MCGAPRIM/VBUFFER.CPP b/WWFLAT32/MCGAPRIM/VBUFFER.CPP new file mode 100644 index 0000000..b930eea --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VBUFFER.CPP @@ -0,0 +1,250 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : VBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 9, 1995 * + * * + * Last Update : January 9, 1995 [PWG] * + * * + * This module contains the C++ class definitions for the video buffer * + * class. This include routines for class creation and modification * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VideoViewPortClass -- Constructor for basic view port class * + * VVPC::VideoViewPortClass -- Default constructor for view port class * + * VVPC::~VideoViewPortClass -- Destructor for GraphicViewPortClass * + * VVPC::Attach -- Attaches a viewport to a buffer class * + * VVPC::Change -- Changes position and size of a Video View Port * + * VBC::VideoBufferClass -- Default constuctor for video buffers * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * VBC::~VideoBufferClass -- Destructor for the video buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "vbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * VVPC::VIDEOVIEWPORTCLASS -- Constructor for basic view port class * + * * + * INPUT: VideoBufferClass * vbuffer - buffer to attach to * + * WORD x - x offset into buffer * + * WORD y - y offset into buffer * + * WORD w - view port width in pixels * + * WORD h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::VideoViewPortClass(VideoBufferClass *vbuffer, int x, int y, int w, int h) +{ + Attach(vbuffer, x, y, w, h); +} + +/*************************************************************************** + * VVPC::VIDEOVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::VideoViewPortClass(void) +{ +} + +/*************************************************************************** + * VVPC::~VIDEOVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +VideoViewPortClass::~VideoViewPortClass(void) +{ +} + +/*************************************************************************** + * VVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * WORD x - x position to attach to * + * WORD y - y position to attach to * + * WORD w - width of the view port * + * WORD h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void VideoViewPortClass::Attach(VideoBufferClass *vbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= vbuffer->Get_Width()) // you cannot place left edge off + x = vbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= vbuffer->Get_Height()) // you cannot place view port off + y = vbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > vbuffer->Get_Width()) // if the x plus width is larger + w = vbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > vbuffer->Get_Height()) // if the y plus height is larger + h = vbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = vbuffer->Get_Offset() + (vbuffer->Get_Width() * y) + x; + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = vbuffer->Get_Width() - w; + Width = w; + Height = h; + VideoBuff = vbuffer; +} +/*************************************************************************** + * VVPC::CHANGE -- Changes position and size of a Video View Port * + * * + * INPUT: WORD the new x pixel position of the Video view port * + * WORD the new y pixel position of the Video view port * + * WORD the new width of the viewport in pixels * + * WORD the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Video View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Video View Port which is derived * + * from a Video View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL VideoViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Video View Port if it is actually the physical */ + /* representation of a Video Buffer. */ + /*======================================================================*/ + if (this == Get_Video_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing Video buffer as if we were creating the */ + /* VideoViewPort. */ + /*======================================================================*/ + Attach(Get_Video_Buffer(), x, y, w, h); + return(TRUE); +} +/*************************************************************************** + * VBC::VIDEOBUFFERCLASS -- Default constuctor for video buffers * + * * + * INPUT: WORD w - width of buffer in pixels (default = 320) * + * WORD h - height of buffer in pixels (default = 200) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::VideoBufferClass(int w, int h) +{ + Offset = 0xa0000; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + VideoBuff = this; // Get a pointer to our self +} + +/*************************************************************************** + * VBC::VIDEOBUFFERCLASS -- Lowlevel constructor for video buffer class * + * * + * INPUT: UWORD the selector of the memory reference * + * long the offset of the memory reference * + * WORD the width of the video buffer * + * WORD the height of the video buffer * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::VideoBufferClass(unsigned short selector, long offset, int w, int h) +{ + Offset = offset; // Get offset to the buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + VideoBuff = this; // Get a pointer to our self +} +/*=========================================================================* + * VBC::~VIDEOBUFFERCLASS -- Destructor for the video buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +VideoBufferClass::~VideoBufferClass() +{ +} + diff --git a/WWFLAT32/MCGAPRIM/VBUFFER.H b/WWFLAT32/MCGAPRIM/VBUFFER.H new file mode 100644 index 0000000..e86db77 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VBUFFER.H @@ -0,0 +1,1034 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood 32 bit Library * + * * + * File Name : VBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 6, 1995 * + * * + * Last Update : January 17, 1995 [PWG] * + * * + * This module contains the definition for the video buffer class. The * + * primary functionality of the video buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * This header files gives the definition for all VideoViewPort and * + * VideoBuffer functions. These functions mirror some of the basic * + * functionality of the GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Video ViewPort - The Video ViewPort holds all of the functions which * + * can be used on a Video Buffer. The video viewport but cannot be * + * attached to a Graphic Buffer. * + * * + * Video Buffer - A refers to a physical object which has been mapped to * + * ram, like a graphic card. The SeenPage is a good example of a Video * + * Buffer. * + + * Below is a tree which shows the relationship of the GraphicBuffer and * + * Buffer classes to the VideoBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Get_Selector -- Get selector for virtual viewport instance * + * VVPC::Get_Offset -- Get offset for virtual view port class instance * + * VVPC::Get_Height -- Gets the height of a virtual viewport instance * + * VVPC::Get_Width -- Get the width of a virtual viewport instance * + * VVPC::Get_XAdd -- Get the X add offset for virtual viewport instance * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * VVPC::Get_Grasphic_Buffer -- Get the graphic buffer of the VP. * + * VVPC::Put_Pixel -- stub to call curr graphic mode Put_Pixel * + * VVPC::Get_Pixel -- stub to call curr graphic mode Get_Pixel * + * VVPC::Clear -- stub to call curr graphic mode Clear * + * VVPC::To_Buffer -- stub func 1 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 2 to call curr graphic mode To_Buffer * + * VVPC::To_Buffer -- stub func 3 to call curr graphic mode To_Buffer * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to GVPC * + * VVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * VVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to GVPC * + * VVPC::Scale -- stub 1 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 2 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 3 to call curr graphic mode Scale to VVPC * + * VVPC::Scale -- stub 4 to call curr graphic mode Scale to VVPC * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable w, h * + * BC::To_Page -- Copys buffer class to page with definable x, y, w, h * + * Buffer_To_Page -- Generic 'c' callable form of Buffer_To_Page * + * GVPC::Blit -- stub 1 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 2 to call curr graphic mode Blit to VVPC * + * GVPC::Blit -- stub 3 to call curr graphic mode Blit to VVPC * + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * VVPC::Print -- Stub function to print string on a Video View Port * + * VVPC::Print -- Stub function to print an int on a Graphic Viewport Class* + * VVPC::Print -- Stub function to print long on a graphic viewport class* + * VVPC::Print -- Stub function to print a short on a Video Viewport * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VBUFFER_H +#define VBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The video buffer class is dependant on a buffer class so we need to */ +/* include the definitions for that as well. */ +/*=========================================================================*/ +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +/*=========================================================================*/ +/* Define external assembly funcs which deals with buffers and viewports. */ +/*=========================================================================*/ +long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view); + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 320 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ +#ifdef __cplusplus +extern "C" { + extern UWORD Get_MCGA_Selector(VOID); +} +#endif + +/*=========================================================================*/ +/* Let the compiler know that a VideoBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class VideoBufferClass; + + +/*=========================================================================*/ +/* VideoViewPortClass - Holds viewport information which has been attached */ +/* to a VideoBuffer. A viewport is effectively a rectangular subset */ +/* of the full buffer which is used for clipping and the like. */ +/* */ +/* int Selector - is the selector to view port buffer */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoViewPortClass(VideoBufferClass* graphic_buff, int x, int y, int w, int h); + VideoViewPortClass(); + ~VideoViewPortClass(); + /*===================================================================*/ + /* Define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + VideoBufferClass *Get_Video_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of graphic functions that are members of a Video */ + /* ViewPort class. */ + /*===================================================================*/ + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + BOOL Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + BOOL Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x, int y, int fcolor, int bcolor); + unsigned long Print(int num, int x, int y, int fcol, int bcol); + unsigned long Print(short num, int x, int y, int fcol, int bcol); + unsigned long Print(long num, int x, int y, int fcol, int bcol); + + protected: + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + void Attach(VideoBufferClass *video_buff, int x, int y, int w, int h); + void Attach(VideoBufferClass *video_buff, int w, int h); + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + VideoBufferClass *VideoBuff; // related graphic buff +}; + + +/*=========================================================================*/ +/* VideoBufferClass - A VideoBuffer refers to an actual instance of a */ +/* physcial device which has been mapped to a memory address like a */ +/* video card. The VideoBuffer can be drawn to directly because it */ +/* inherits a Video ViewPort which represent its full size. */ +/* size. */ +/* */ +/* int Selector - is now the selector to graphic buffer */ +/* char *Buffer - is now the offset to graphic buffer */ +/* int Width - is now the width of graphic buffer */ +/* int Height - is now the height of graphic buffer */ +/* int XAdd - is now the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/*=========================================================================*/ +class VideoBufferClass: public VideoViewPortClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + VideoBufferClass(unsigned short selector, long offset, int w, int h); + VideoBufferClass(int w = DEFAULT_SCREEN_WIDTH, int h = DEFAULT_SCREEN_HEIGHT); + ~VideoBufferClass(); +}; + +/*************************************************************************** + * VVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::Get_Offset(void) +{ + return(Offset); +} + +/*************************************************************************** + * VVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * VVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * VVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: int the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * VVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * VVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: int the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * +;* 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * VVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline VideoBufferClass *VideoViewPortClass::Get_Video_Buffer(void) +{ + return (VideoBuff); +} + +/*************************************************************************** + * VVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + VVPC_Put_Pixel_Func(this, x, y, color); +} + +/*************************************************************************** + * VVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int VideoViewPortClass::Get_Pixel(int x, int y) +{ + return(VVPC_Get_Pixel_Func(this, x, y)); +} + +/*************************************************************************** + * VVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void VideoViewPortClass::Clear(unsigned char color) +{ + VVPC_Clear_Func(this, color); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff, size)); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long VideoViewPortClass::To_Buffer(BufferClass *buff) +{ + return(VVPC_To_Buffer_Func(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_GVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(VVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_GVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} +/*************************************************************************** + * VVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * VVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL VideoViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(VVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print string on a Video View Port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + return(VVPC_Print_Func(this, str, x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print an int on a Video Viewport * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print a short on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + BYTE str[17]; + + + return(VVPC_Print_Func(this, itoa(num, str, 10), x, y, fcol, bcol)); +} + +/*************************************************************************** + * VVPC::PRINT -- Stub function to print long on a Video Viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ +inline unsigned long VideoViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + BYTE str[33]; + + return(VVPC_Print_Func(this, ltoa(num, str, 10), x, y, fcol, bcol)); +} + + +/*=========================================================================*/ +/*= The following BufferClass functions are defined here because they act =*/ +/*= on video viewports. =*/ +/*=========================================================================*/ + + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, w, h, Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * VIVC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, VideoViewPortClass &view) +{ + return(VVPC_Buffer_To_Page(x, y, w, h, Buffer, &view)); +} + +/*=========================================================================*/ +/* The following GraphicBufferClass functions are defined here because */ +/* they act on graphic viewports. */ +/*=========================================================================*/ +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, x_pixel, y_pixel, dx_pixel, dy_pixel, pixel_width, pixel_height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 2 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, dx, dy, Width, Height, trans)); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Blit( VideoViewPortClass& dest, BOOL trans) +{ + return(GVPC_Blit_to_VVPC_Func(this, &dest, 0, 0, 0, 0, Width, Height, trans)); +} +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, BOOL trans, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to VVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( VideoViewPortClass &dest, char *remap) +{ + return(GVPC_Scale_To_VVPC( this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); +} + +#endif // VBUFFER_H diff --git a/WWFLAT32/MCGAPRIM/VCLEAR.ASM b/WWFLAT32/MCGAPRIM/VCLEAR.ASM new file mode 100644 index 0000000..933abac --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VCLEAR.ASM @@ -0,0 +1,66 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Clear -- Clears a vesa video viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_CLEAR -- Clears a vesa video viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Clear C near + ret + ENDP Vesa_Clear +END diff --git a/WWFLAT32/MCGAPRIM/VESA.ASM b/WWFLAT32/MCGAPRIM/VESA.ASM new file mode 100644 index 0000000..36f88a9 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VESA.ASM @@ -0,0 +1,71 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VESA.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Asm_Set_Win -- Sets the current vesa window from Asm * +;* Vesa_Asm_Next_Window -- Sets to the next vesa window * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +GLOBAL Vesa_Asm_Set_Win :near + +DATASEG + +CODESEG + + +;*************************************************************************** +;* VESA_ASM_SET_WIN -- Sets the current vesa window from Asm * +;* * +;* INPUT: edi - offset to set the window for * +;* * +;* OUTPUT: edi - adjusted offset for window * +;* * +;* PROTO: void Vesa_Asm_Set_Win(void); * +;* * +;* HISTORY: * +;* 12/08/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Asm_Set_Win C near + + ret + ENDP Vesa_Asm_Set_Win + + + +END diff --git a/WWFLAT32/MCGAPRIM/VGETPIX.ASM b/WWFLAT32/MCGAPRIM/VGETPIX.ASM new file mode 100644 index 0000000..66d3f8f --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VGETPIX.ASM @@ -0,0 +1,65 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* Vesa_Get_Pixel -- Gets a pixel from a video viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG +;*************************************************************************** +;* VESA_GET_PIXEL -- Gets a pixel from a video viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + + PROC Vesa_Get_Pixel C near + ret + ENDP Vesa_Get_Pixel +END diff --git a/WWFLAT32/MCGAPRIM/VLBTOVE.ASM b/WWFLAT32/MCGAPRIM/VLBTOVE.ASM new file mode 100644 index 0000000..e9956a3 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VLBTOVE.ASM @@ -0,0 +1,72 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VBITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Blit_To_Vesa -- copies graphic buffer to vesa screen * +;* Vesa_Blit_To_Linear -- Copies vesa screen to graphic buffer * +;* Vesa_Blit_To_Vesa -- Copies a section of vesa screen to vesa screen * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +GLOBAL Linear_Blit_To_Vesa :NEAR + +CODESEG + +;*************************************************************************** +;* LINEAR_BLIT_TO_VESA -- copies graphic buffer to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Blit_To_Vesa C near + ret + ENDP Linear_Blit_To_Vesa + +END + + diff --git a/WWFLAT32/MCGAPRIM/VPUTPIX.ASM b/WWFLAT32/MCGAPRIM/VPUTPIX.ASM new file mode 100644 index 0000000..e07ebaa --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VPUTPIX.ASM @@ -0,0 +1,66 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Put_Pixel -- Puts a pixel on a video viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_PUT_PIXEL -- Puts a pixel on a video viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Put_Pixel C near + ret + ENDP Vesa_Put_Pixel +END diff --git a/WWFLAT32/MCGAPRIM/VSCALE.ASM b/WWFLAT32/MCGAPRIM/VSCALE.ASM new file mode 100644 index 0000000..09e10ab --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VSCALE.ASM @@ -0,0 +1,68 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Scale_To_Vesa -- Scales a vesa viewport to a vesa viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_SCALE_TO_VESA -- Scales a vesa viewport to a vesa viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Scale_To_Vesa C near + ret + ENDP Vesa_Scale_To_Vesa + + +END diff --git a/WWFLAT32/MCGAPRIM/VSCLTOVE.ASM b/WWFLAT32/MCGAPRIM/VSCLTOVE.ASM new file mode 100644 index 0000000..1547425 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VSCLTOVE.ASM @@ -0,0 +1,69 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Scale_To_Vesa -- Scales a graphic viewport to a vesa viewport * +;* Vesa_Scale_To_Linear -- Scales a Vesa viewport to a graphic viewport * +;* Vesa_Scale_To_Vesa -- Scales a vesa viewport to a vesa viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* LINEAR_SCALE_TO_VESA -- Scales a graphic viewport to a vesa viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Vesa C near + ret + ENDP Linear_Scale_To_Vesa + +END diff --git a/WWFLAT32/MCGAPRIM/VTOBUFF.ASM b/WWFLAT32/MCGAPRIM/VTOBUFF.ASM new file mode 100644 index 0000000..897932f --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VTOBUFF.ASM @@ -0,0 +1,66 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VTOBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_To_Buffer -- Copies a vesa viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_TO_BUFFER -- Copies a vesa viewport to a linear buffer * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_To_Buffer C near + ret + ENDP Vesa_To_Buffer +END diff --git a/WWFLAT32/MCGAPRIM/VTOPAGE.ASM b/WWFLAT32/MCGAPRIM/VTOPAGE.ASM new file mode 100644 index 0000000..8ff22c5 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VTOPAGE.ASM @@ -0,0 +1,65 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VTOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Buffer_To_Page -- Copies a linear buffer to a vesa page * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG + +;*************************************************************************** +;* VESA_BUFFER_TO_PAGE -- Copies a linear buffer to a vesa page * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Buffer_To_Page C near + ret + ENDP Vesa_Buffer_To_Page +END diff --git a/WWFLAT32/MCGAPRIM/VTXTPRNT.ASM b/WWFLAT32/MCGAPRIM/VTXTPRNT.ASM new file mode 100644 index 0000000..2a0a298 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VTXTPRNT.ASM @@ -0,0 +1,68 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VTXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Print -- Prints a text string to a Vesa Viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VESA_PRINT -- Prints a text string to a Vesa Viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Print C near + ret + ENDP Vesa_Print + + +END \ No newline at end of file diff --git a/WWFLAT32/MCGAPRIM/VVBLIT.ASM b/WWFLAT32/MCGAPRIM/VVBLIT.ASM new file mode 100644 index 0000000..67ab975 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VVBLIT.ASM @@ -0,0 +1,71 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VBITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Blit_To_Vesa -- copies graphic buffer to vesa screen * +;* Vesa_Blit_To_Linear -- Copies vesa screen to graphic buffer * +;* Vesa_Blit_To_Vesa -- Copies a section of vesa screen to vesa screen * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +GLOBAL Vesa_Blit_To_Vesa :NEAR + +CODESEG + +;*************************************************************************** +;* VESA_BLIT_TO_VESA -- Copies a section of vesa screen to vesa screen * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Vesa C near + ret + ENDP Vesa_Blit_To_Vesa +END + + diff --git a/WWFLAT32/MCGAPRIM/VVETOLB.ASM b/WWFLAT32/MCGAPRIM/VVETOLB.ASM new file mode 100644 index 0000000..23b980e --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VVETOLB.ASM @@ -0,0 +1,74 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VBITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Blit_To_Vesa -- copies graphic buffer to vesa screen * +;* Vesa_Blit_To_Linear -- Copies vesa screen to graphic buffer * +;* Vesa_Blit_To_Vesa -- Copies a section of vesa screen to vesa screen * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE ".\gbuffer.inc" + + +GLOBAL Vesa_Blit_To_Linear :NEAR + +CODESEG + +;*************************************************************************** +;* VESA_BLIT_TO_LINEAR -- Copies vesa screen to graphic buffer * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Blit_To_Linear C near + ret + ENDP Vesa_Blit_To_Linear + +END + + diff --git a/WWFLAT32/MCGAPRIM/VVETOSCL.ASM b/WWFLAT32/MCGAPRIM/VVETOSCL.ASM new file mode 100644 index 0000000..9aa3db9 --- /dev/null +++ b/WWFLAT32/MCGAPRIM/VVETOSCL.ASM @@ -0,0 +1,65 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Scale_To_Linear -- Scales a Vesa viewport to a graphic viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG +;*************************************************************************** +;* VESA_SCALE_TO_LINEAR -- Scales a Vesa viewport to a graphic viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Scale_To_Linear C near + ret + ENDP Vesa_Scale_To_Linear + +END diff --git a/WWFLAT32/MEM/ALLOC.CPP b/WWFLAT32/MEM/ALLOC.CPP new file mode 100644 index 0000000..613043e --- /dev/null +++ b/WWFLAT32/MEM/ALLOC.CPP @@ -0,0 +1,590 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 16*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + diff --git a/WWFLAT32/MEM/ALLOC.CPP.BAK b/WWFLAT32/MEM/ALLOC.CPP.BAK new file mode 100644 index 0000000..71b19e1 --- /dev/null +++ b/WWFLAT32/MEM/ALLOC.CPP.BAK @@ -0,0 +1,590 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 12*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + diff --git a/WWFLAT32/MEM/MAKEFILE b/WWFLAT32/MEM/MAKEFILE new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WWFLAT32/MEM/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/MEM/MEM.CPP b/WWFLAT32/MEM/MEM.CPP new file mode 100644 index 0000000..562ca0d --- /dev/null +++ b/WWFLAT32/MEM/MEM.CPP @@ -0,0 +1,1091 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +extern TimerClass TickCount; + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WWFLAT32/MEM/MEM.CPP.BAK b/WWFLAT32/MEM/MEM.CPP.BAK new file mode 100644 index 0000000..1df2df6 --- /dev/null +++ b/WWFLAT32/MEM/MEM.CPP.BAK @@ -0,0 +1,1089 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WWFLAT32/MEM/MEMFLAG.H b/WWFLAT32/MEM/MEMFLAG.H new file mode 100644 index 0000000..30651d4 --- /dev/null +++ b/WWFLAT32/MEM/MEMFLAG.H @@ -0,0 +1,98 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/WWFLAT32/MEM/MEM_COPY.ASM b/WWFLAT32/MEM/MEM_COPY.ASM new file mode 100644 index 0000000..01fb8be --- /dev/null +++ b/WWFLAT32/MEM/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Mem_Copy : NEAR +GLOBAL Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy + +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block + +END + + + diff --git a/WWFLAT32/MEM/NEWDEL.CPP b/WWFLAT32/MEM/NEWDEL.CPP new file mode 100644 index 0000000..e402e89 --- /dev/null +++ b/WWFLAT32/MEM/NEWDEL.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : July 17, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * -- * + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + +/*************************************************************************** + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WWFLAT32/MEM/OLD/ALLOC.CPP b/WWFLAT32/MEM/OLD/ALLOC.CPP new file mode 100644 index 0000000..8c1d6f8 --- /dev/null +++ b/WWFLAT32/MEM/OLD/ALLOC.CPP @@ -0,0 +1,479 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); + return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +// return(_memavl()); + return Largest_Mem_Block () ; +} + diff --git a/WWFLAT32/MEM/OLD/MAKEFILE b/WWFLAT32/MEM/OLD/MAKEFILE new file mode 100644 index 0000000..4f0ebe6 --- /dev/null +++ b/WWFLAT32/MEM/OLD/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 24, 1994 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mem +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +CC_CFG += /zm # Each routine to be in its own segment. + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + alloc.obj & + mem.obj & + newdel.obj & + mem_copy.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/MEM/OLD/MEM.CPP b/WWFLAT32/MEM/OLD/MEM.CPP new file mode 100644 index 0000000..1df2df6 --- /dev/null +++ b/WWFLAT32/MEM/OLD/MEM.CPP @@ -0,0 +1,1089 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WWFLAT32/MEM/OLD/MEMFLAG.H b/WWFLAT32/MEM/OLD/MEMFLAG.H new file mode 100644 index 0000000..09f7b0f --- /dev/null +++ b/WWFLAT32/MEM/OLD/MEMFLAG.H @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + extern void Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); + +#ifdef __cplusplus +} +#endif + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif + + diff --git a/WWFLAT32/MEM/OLD/MEM_COPY.ASM b/WWFLAT32/MEM/OLD/MEM_COPY.ASM new file mode 100644 index 0000000..01fb8be --- /dev/null +++ b/WWFLAT32/MEM/OLD/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Mem_Copy : NEAR +GLOBAL Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy + +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block + +END + + + diff --git a/WWFLAT32/MEM/OLD/NEWDEL.CPP b/WWFLAT32/MEM/OLD/NEWDEL.CPP new file mode 100644 index 0000000..e402e89 --- /dev/null +++ b/WWFLAT32/MEM/OLD/NEWDEL.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : July 17, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * -- * + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + +/*************************************************************************** + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WWFLAT32/MEM/OLD/WWMEM.H b/WWFLAT32/MEM/OLD/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/WWFLAT32/MEM/OLD/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WWFLAT32/MEM/OLD/WWMEM.INC b/WWFLAT32/MEM/OLD/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WWFLAT32/MEM/OLD/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WWFLAT32/MEM/WWMEM.H b/WWFLAT32/MEM/WWMEM.H new file mode 100644 index 0000000..321a380 --- /dev/null +++ b/WWFLAT32/MEM/WWMEM.H @@ -0,0 +1,69 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/WWFLAT32/MEM/WWMEM.INC b/WWFLAT32/MEM/WWMEM.INC new file mode 100644 index 0000000..b98be01 --- /dev/null +++ b/WWFLAT32/MEM/WWMEM.INC @@ -0,0 +1,43 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/WWFLAT32/MISC/ASM.ASM b/WWFLAT32/MISC/ASM.ASM new file mode 100644 index 0000000..9c3e780 --- /dev/null +++ b/WWFLAT32/MISC/ASM.ASM @@ -0,0 +1,117 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +;MODEL USE32 FLAT +MODEL LARGE + +GLOBAL C Calculate_CRC:FAR + + CODESEG + +; LONG Calculate_CRC(VOID *buffer, LONG length); + PROC Calculate_CRC C far + USES esi,ebx,ecx + + ARG buffer:DWORD + ARG length:DWORD + + LOCAL crc:DWORD + + ; Load pointer to data block. + mov [crc],0 + pushf + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + ; Fetch the length of the data block to CRC. + mov ecx,[length] + jecxz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short ??remainder +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov [crc],ebx + popf + mov dx,[WORD PTR crc+2] + mov ax,[WORD PTR crc] + ret + + ENDP Calculate_CRC + + END + diff --git a/WWFLAT32/MISC/ASM.ASM.BAK b/WWFLAT32/MISC/ASM.ASM.BAK new file mode 100644 index 0000000..9c3e780 --- /dev/null +++ b/WWFLAT32/MISC/ASM.ASM.BAK @@ -0,0 +1,117 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +;MODEL USE32 FLAT +MODEL LARGE + +GLOBAL C Calculate_CRC:FAR + + CODESEG + +; LONG Calculate_CRC(VOID *buffer, LONG length); + PROC Calculate_CRC C far + USES esi,ebx,ecx + + ARG buffer:DWORD + ARG length:DWORD + + LOCAL crc:DWORD + + ; Load pointer to data block. + mov [crc],0 + pushf + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + ; Fetch the length of the data block to CRC. + mov ecx,[length] + jecxz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short ??remainder +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov [crc],ebx + popf + mov dx,[WORD PTR crc+2] + mov ax,[WORD PTR crc] + ret + + ENDP Calculate_CRC + + END + diff --git a/WWFLAT32/MISC/CLIPRECT.ASM b/WWFLAT32/MISC/CLIPRECT.ASM new file mode 100644 index 0000000..e64fd41 --- /dev/null +++ b/WWFLAT32/MISC/CLIPRECT.ASM @@ -0,0 +1,270 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Clip_Rect :NEAR +GLOBAL Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * + + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword + +;This Clipping algorithm is a derivation of the very well known +;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency +;it is probably the most commontly implemented algorithm both in software +;and hardware for clipping lines, rectangles, and convex polygons against +;a rectagular clipping window. For reference see +;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes +; pages 113 to 177". +; Briefly consist in computing the Sutherland code for both end point of +; the rectangle to find out if the rectangle is: +; - trivially accepted (no further clipping test, return the oroginal data) +; - trivially rejected (return with no action, return error code) +; - retangle must be iteratively clipped again edges of the clipping window +; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz ??clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz ??clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz ??left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + +??left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz ??bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + +??bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz ??right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz ??clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle ??clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done +??clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify +??clip_out: + ret + ENDP Clip_Rect + + +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * + + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl ??x_axix_ok + mov eax,1 + + test esi,esi + jl ??shift_right + mov [dword ptr ebx],0 + jmp ??x_axix_ok +??shift_right: + inc edi + sub [ebx],edi +??x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl ??confi_out + mov eax,1 + + test esi,esi + jl ??shift_top + mov [dword ptr ebx],0 + ret +??shift_top: + inc edi + sub [ebx],edi +??confi_out: + ret + + ENDP Confine_Rect + + END + diff --git a/WWFLAT32/MISC/CRC.ASM b/WWFLAT32/MISC/CRC.ASM new file mode 100644 index 0000000..04424a8 --- /dev/null +++ b/WWFLAT32/MISC/CRC.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Calculate_CRC :NEAR + + CODESEG + +; LONG Calculate_CRC(VOID *buffer, LONG length); + PROC Calculate_CRC C near + USES esi + + ARG buffer:DWORD + ARG length:DWORD + + LOCAL crc:DWORD + + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + ; Fetch the length of the data block to CRC. + mov ecx,[length] + jecxz short ??fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short ??remainder +??accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop ??accumloop + + ; Handle the remainder bytes. +??remainder: + or dl,dl + jz short ??fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx +??nextbyte: + lodsb + ror eax,8 + loop ??nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + +;??nextbyte: +; shl eax,8 +; lodsb +; loop ??nextbyte + rol ebx,1 + add ebx,eax + +??fini: + mov [crc],ebx + popad + mov eax,[crc] + ret + + ENDP Calculate_CRC + + END + \ No newline at end of file diff --git a/WWFLAT32/MISC/CRC.IDE b/WWFLAT32/MISC/CRC.IDE new file mode 100644 index 0000000..4650ad3 Binary files /dev/null and b/WWFLAT32/MISC/CRC.IDE differ diff --git a/WWFLAT32/MISC/DELAY.CPP b/WWFLAT32/MISC/DELAY.CPP new file mode 100644 index 0000000..bbbb437 --- /dev/null +++ b/WWFLAT32/MISC/DELAY.CPP @@ -0,0 +1,62 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : DELAY.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : 27 March, 1991 [CY] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include + +void Delay(int duration) +{ + unsigned long count; + TimerClass timer(BT_SYSTEM,TRUE); + + while (duration--) { + count = timer.Time() + 1L; + while (count >= timer.Time()) { + ; + } + } + +#if(FALSE) + while (duration--) + Wait_Vert_Blank(VertBlank); +#endif +} + +#if(FALSE) +void Vsync() +{ + Wait_Vert_Blank(VertBlank); +} +#endif + \ No newline at end of file diff --git a/WWFLAT32/MISC/DETPROC.ASM b/WWFLAT32/MISC/DETPROC.ASM new file mode 100644 index 0000000..a062a61 --- /dev/null +++ b/WWFLAT32/MISC/DETPROC.ASM @@ -0,0 +1,116 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/detproc.asm 1.1 1994/04/18 09:13:53 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PROC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 11, 1993 * +;* * +;* Last Update : May 11, 1993 [JLB] * +;* * +;* Converted to 32Bit -- JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Processor :NEAR + + +PROC_80386 equ 0 +PROC_80486 equ 1 +PROC_80586 equ 2 + +DATASEG +cpu_id_586 dw 0 + +CODESEG + +PROC Processor C near + USES ebx + LOCAL ptype:WORD + + pushfd + +; At least a 386 -- check for 486. + mov [WORD PTR ptype],PROC_80386 ; 80386 + pushfd + pop eax + mov ebx,eax + xor eax,40000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 486 -- check for 586(Pentium) + mov [ptype],PROC_80486 ; 80486 + + ; Some machines have a problem with this fLAG + ; and thus make us think they are a 586 but they are + ; really a 486. A possible way around this is to + ; capture the Illegal instruction vector, then do + ; an instruction only available on the 586. + + ; for now this is just commented out + pushfd + pop eax + mov ebx,eax + xor eax,200000h + push eax + popfd + pushfd + pop eax + xor eax,ebx + je short ??fini + +; At least a 586(Pentium) -- check for higher. + mov [ptype],PROC_80586 ; 80486 +; mov eax,1 +; DW 0fA2h ; CPUID opcode. +; shr ax,8 +; and ax,0fh +; inc ax +; inc ax +; mov [cpu_id_586],ax + +; Final cleanup and exit. +??fini: + popfd + sub eax,eax + mov ax,[ptype] + ret + +ENDP Processor + +END + \ No newline at end of file diff --git a/WWFLAT32/MISC/EXIT.CPP b/WWFLAT32/MISC/EXIT.CPP new file mode 100644 index 0000000..7365f1d --- /dev/null +++ b/WWFLAT32/MISC/EXIT.CPP @@ -0,0 +1,91 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 library source * + * * + * File Name : EXIT.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Exit -- Exit routine with message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include "misc.h" + +#include +#include +#include +#include + + + + +/*************************************************************************** + * EXIT -- Exit routine with message. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/03/1994 SKB : Created. * + *=========================================================================*/ +VOID cdecl Exit(INT errorval, const BYTE *message, ...) +{ + va_list argptr; + BYTE errstring[256]; + + Prog_End(); + + if (message && *message) { + va_start (argptr, message); + vsprintf ((char *)errstring, (const char *)message, argptr); + va_end (argptr); + printf(errstring); + } + + ::exit(errorval); + +} + +void randomize ( void ) +{ + srand ( time ( NULL ) ) ; +} + +#if(0) +ULONG random ( ULONG mod ) +{ + return rand () * mod / RAND_MAX ; +} +#endif diff --git a/WWFLAT32/MISC/FACING16.ASM b/WWFLAT32/MISC/FACING16.ASM new file mode 100644 index 0000000..710f4db --- /dev/null +++ b/WWFLAT32/MISC/FACING16.ASM @@ -0,0 +1,149 @@ +; +; 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 . +; + +; $Header: g:/library/source/rcs/./facing16.asm 1.10 1994/05/20 15:32:36 joe_bostic Exp $ +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +; long Desired_Facing16(long x1, long y1, long x2, long y2); + + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END + + + + \ No newline at end of file diff --git a/WWFLAT32/MISC/FACING8.ASM b/WWFLAT32/MISC/FACING8.ASM new file mode 100644 index 0000000..327abd5 --- /dev/null +++ b/WWFLAT32/MISC/FACING8.ASM @@ -0,0 +1,142 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +GLOBAL Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +; long Desired_Facing8(long x1, long y1, long x2, long y2); + + PROC Desired_Facing8 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 ; Close to major axis bit. + + xor eax,eax + mov al,[NewFacing8+ebx] + + ; Normalize to 0..FF range. + shl eax,5 + + ret + + ENDP Desired_Facing8 + + + END + + \ No newline at end of file diff --git a/WWFLAT32/MISC/FACINGFF.ASM b/WWFLAT32/MISC/FACINGFF.ASM new file mode 100644 index 0000000..56d1ad8 --- /dev/null +++ b/WWFLAT32/MISC/FACINGFF.ASM @@ -0,0 +1,166 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Support Library * +;* * +;* File Name : FACINGFF.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing256 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Desired_Facing256 :NEAR +; INCLUDE "wwlib.i" +INCLUDE ".\gbuffer.inc" + + CODESEG + +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ +; LONG cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) + PROC Desired_Facing256 C near + USES ebx, ecx, edx + + ARG srcx:DWORD + ARG srcy:DWORD + ARG dstx:DWORD + ARG dsty:DWORD + + xor ebx,ebx ; Facing number. + + ; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short ??xnotneg + neg ecx + mov ebx,11000000b ; Set bit 7 and 6 for leftward. +??xnotneg: + + ; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ??ynotneg + xor ebx,01000000b ; Complement bit 6 for downward. + neg eax +??ynotneg: + + ; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + ; Determine if the direction is closer to the Y axis and make sure that + ; CX holds the larger of the two deltas. This is in preparation for the + ; divide. + cmp eax,ecx + jb short ??gotaxis + xchg eax,ecx + xor edx,01000000b ; Closer to Y axis so make DX=64 for quad 0 and 2. +??gotaxis: + + ; If closer to the X axis then add 64 for quadrants 0 and 2. If + ; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + ; add value is in DX and save on stack. + push edx + + ; Make sure that the division won't overflow. Reduce precision until + ; the larger number is less than 256 if it appears that an overflow + ; will occur. If the high byte of the divisor is not zero, then this + ; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short ??nooverflow +??again: + test ecx,0FFFFFF00h + jz short ??nooverflow + shr ecx,1 + shr eax,1 + jmp short ??again +??nooverflow: + + ; Make sure that the division won't underflow (divide by zero). If + ; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short ??nounderflow + mov eax,0FFFFFFFFh + jmp short ??divcomplete + + ; Derive a pseudo angle number for the octant. The angle is based + ; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +??nounderflow: + xor edx,edx + shld edx,eax,8 ; shift high byte of eax into dl + shl eax,8 + div ecx +??divcomplete: + + ; Integrate the 5 most significant bits into the angle index. If DX + ; is not zero, then it is 64. This means that the dividend must be negated + ; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short ??noneg + dec edx + neg eax +??noneg: + add eax,edx + add eax,ebx + and eax,0FFH + ret + + ENDP Desired_Facing256 + + + + END + \ No newline at end of file diff --git a/WWFLAT32/MISC/FADING.ASM b/WWFLAT32/MISC/FADING.ASM new file mode 100644 index 0000000..3d36de2 --- /dev/null +++ b/WWFLAT32/MISC/FADING.ASM @@ -0,0 +1,216 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini + cmp [dest],0 + je ??fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 +??innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short ??notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne ??mainloop + +??fini: + mov eax,[dest] + ret + + ENDP Build_Fading_Table + + + END + \ No newline at end of file diff --git a/WWFLAT32/MISC/FINDARGV.CPP b/WWFLAT32/MISC/FINDARGV.CPP new file mode 100644 index 0000000..15052a5 --- /dev/null +++ b/WWFLAT32/MISC/FINDARGV.CPP @@ -0,0 +1,83 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/wwlib32/misc/rcs/findargv.cpp 1.2 1994/04/22 10:29:28 scott_bowen Exp $ */ +/*************************************************************************** + ** 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 : findargv * + * * + * File Name : findargv.C * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Argv -- Checks to see if string is in arguement * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "wwstd.h" +#include +#include +#include +#include + + + + +/*************************************************************************** + * Find_Argv -- Checks to see if string is in arguement * + * * + * INPUT: BYTE *str - string to search for. * + * * + * OUTPUT: NULL if not found else pointer to string. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/14/1993 SB : Created. * + *=========================================================================*/ + +static char command [ 256 ] ; + +BYTE *cdecl Find_Argv(BYTE const *str) +{ + char * ptr ; + static startup_flag = 0 ; + + if ( ! startup_flag ) + { + startup_flag = 1 ; + getcmd ( command ) ; + } + + if ( ! strlen(str) ) return NULL ; + return strstr ( command , str ) ; + +} + + + + + diff --git a/WWFLAT32/MISC/IRANDOM.CPP b/WWFLAT32/MISC/IRANDOM.CPP new file mode 100644 index 0000000..0ffdf67 --- /dev/null +++ b/WWFLAT32/MISC/IRANDOM.CPP @@ -0,0 +1,70 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "wwstd.h" +#include "misc.h" + + + + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + diff --git a/WWFLAT32/MISC/KEYCODE.CPP b/WWFLAT32/MISC/KEYCODE.CPP new file mode 100644 index 0000000..7207b76 --- /dev/null +++ b/WWFLAT32/MISC/KEYCODE.CPP @@ -0,0 +1,289 @@ +/* +** 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 . +*/ + + +#include +#include +#include +#include +#include + +#define true 1 +#define false 0 +typedef int bool; + +char KeyPhrase[128] = ""; +char KeyCheck[128] = ""; +long Code = 0x00000000L; + + +extern "C" { +long Calculate_CRC(void const * buffer, long length); +} +long Obfuscate(char const * string); + + +int main(int , char ** ) +{ + char buffer[128]; + + /* + ** Fetch the key phrase from the console. + */ + for (;;) { + + /* + ** Fetch the pass phrase. + */ + puts("\nEnter password phrase:"); + int key = 0; + int index = 0; + KeyPhrase[0] = '\0'; + bool process = true; + while (process) { + key = getche(); + + switch (key) { + case 0x08: + if (index) { + KeyPhrase[--index] = '\0'; + } + break; + + case 0x0D: + case 0x0A: + process = false; + break; + + default: + if (isprint(key)) { + KeyPhrase[index++] = key; + KeyPhrase[index] = '\0'; + } + break; + } + } + puts(""); + + /* + ** Verify that it is long enough. + */ + if (strlen(KeyPhrase) == 0) break; + + /* + ** Calculate the code for the key phrase. + */ + Code = Obfuscate(KeyPhrase); + + + sprintf(buffer, "0x%08lX", Code); + puts(buffer); + } + + puts("Terminated"); + return(0); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cyrptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[128]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + for (int index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + diff --git a/WWFLAT32/MISC/KEYCODE.CPP.BAK b/WWFLAT32/MISC/KEYCODE.CPP.BAK new file mode 100644 index 0000000..637586f --- /dev/null +++ b/WWFLAT32/MISC/KEYCODE.CPP.BAK @@ -0,0 +1,288 @@ +/* +** 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 . +*/ + +#include +#include +#include +#include +#include + +#define true 1 +#define false 0 +typedef int bool; + +char KeyPhrase[128] = ""; +char KeyCheck[128] = ""; +long Code = 0x00000000L; + + +extern "C" { +long Calculate_CRC(void const * buffer, long length); +} +long Obfuscate(char const * string); + + +int main(int , char ** ) +{ + char buffer[128]; + + /* + ** Fetch the key phrase from the console. + */ + for (;;) { + + /* + ** Fetch the pass phrase. + */ + puts("\nEnter password phrase:"); + int key = 0; + int index = 0; + KeyPhrase[0] = '\0'; + bool process = true; + while (process) { + key = getche(); + + switch (key) { + case 0x08: + if (index) { + KeyPhrase[--index] = '\0'; + } + break; + + case 0x0D: + case 0x0A: + process = false; + break; + + default: + if (isprint(key)) { + KeyPhrase[index++] = key; + KeyPhrase[index] = '\0'; + } + break; + } + } + puts(""); + + /* + ** Verify that it is long enough. + */ + if (strlen(KeyPhrase) == 0) break; + + /* + ** Calculate the code for the key phrase. + */ + Code = Obfuscate(KeyPhrase); + + + sprintf(buffer, "0x%08lX", Code); + puts(buffer); + } + + puts("Terminated"); + return(0); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cyrptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[128]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + for (int index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + diff --git a/WWFLAT32/MISC/LIB.CPP b/WWFLAT32/MISC/LIB.CPP new file mode 100644 index 0000000..5848188 --- /dev/null +++ b/WWFLAT32/MISC/LIB.CPP @@ -0,0 +1,218 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./lib.c 1.16 1994/05/20 15:34:33 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Routines * + * * + * File Name : LIB.C * + * * + * Programmer : Scott Bowen * + * * + * Start Date : January 14, 1993 * + * * + * Last Update : May 20, 1993 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Add_Long_To_Pointer -- Pointer arithmatic when pointer could be XMS. * + * Find_Argv -- Checks to see if string is in arguement * + * Mono_Mem_Dump -- Dumps memory to mono monitor with hex and char. * + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include "wwstd.h" +#include "misc.h" + +//PRIVATE unsigned Divide_With_Round(unsigned num, unsigned den); + + +/*************************************************************************** + * Divide_With_Round -- Divides integers and round to nearest integer. * + * * + * INPUT: int numberator. * + * int denominator. * + * * + * OUTPUT: Returns value rounded. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/13/1992 SB : Created. * + *=========================================================================*/ +static unsigned Divide_With_Round(unsigned num, unsigned den) +{ + // return num/den + (0 ro 1). 1 if the remainder is more than half the denominator. + return( (num / den) + (unsigned)((num % den) >= ((den + 1) >> 1)) ); +} + +#define HSV_BASE 255 // This is used to get a little better persion on HSV conversion. +#define RGB_BASE 63 // Not 64, this is really the max value. + + +/*************************************************************************** + * Convert_RGB_To_HSV -- Converts RGB to RSV coordinates. * + * * + * INPUT: int r,g, and b values. * + * int *h, *s, and *v pointers. * + * * + * OUTPUT: Assigns values to *h, *s, and *v. * + * * + * WARNINGS: The reason we use a different base for HSV then RGB is * + * because we loose alot of persision by not using floating * + * point. Using the same base value (63) made it so that * + * about 50% of the time one RGB value would be one different * + * then the original if you went from RGB to HSV to RGB. * + * Using 255 drop it down to about 9% of the time we get an * + * off value. To get it perfect, we would have to make the * + * HSV base larger - but then you need to do all calculations * + * in long instead of unsigned int. * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v) +{ + unsigned int m, r1, g1, b1, tmp; + + // Convert RGB base to HSV base. + r = Divide_With_Round((r * HSV_BASE), RGB_BASE); + g = Divide_With_Round((g * HSV_BASE), RGB_BASE); + b = Divide_With_Round((b * HSV_BASE), RGB_BASE); + + // Set hue to default. + *h = 0; + + // Set v = Max(r,g,b) to find dominant primary color. + *v = (r > g) ? r : g; + if (b > *v) *v = b; + + // Set m = min(r,g,b) to find amount of white. + m = (r < g) ? r : g; + if (b < m) m = b; + + // Determine the normalized saturation. + if (*v != 0) { + *s = Divide_With_Round( (*v - m) * HSV_BASE ,*v); + } else { + *s = 0; + } + + if (*s != 0) { + tmp = *v - m; + r1 = Divide_With_Round( (*v - r) * HSV_BASE, tmp); + g1 = Divide_With_Round( (*v - g) * HSV_BASE, tmp); + b1 = Divide_With_Round( (*v - b) * HSV_BASE, tmp); + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if ((*v) == r) { + if (m == g) { + *h = 5 * HSV_BASE + b1; + } else { + *h = 1 * HSV_BASE - g1; + } + } else { + if ((*v) == g) { + if (m == b) { + *h = 1 * HSV_BASE + r1; + } else { + *h = 3 * HSV_BASE - b1; + } + } else { + // *v == b + if (m == r) { + *h = 3 * HSV_BASE + g1; + } else { + + *h = 5 * HSV_BASE - r1; + } + } + } + + // Divide by six and round. + *h = Divide_With_Round(*h, 6); + } +} + +/*************************************************************************** + * Convert_HSV_To_RGB -- Converts HSV cordinates to RGB values * + * * + * INPUT: int h,s, and v coordinates * + * int *r, *g, and *b pointers. * + * * + * OUTPUT: Assigns values to *r, *g, and *b. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/11/1992 SB : Created. * + *=========================================================================*/ +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b) +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Tempary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + + h *= 6; + f = h % HSV_BASE; + + // Set up possible red, green and blue values. + values[1] = + values[2] = v; + + // + // The following lines of code change + // values[3] = (v * (HSV_BASE - ( (s * f) / HSV_BASE) )) / HSV_BASE; + // values[4] = values[5] = (v * (HSV_BASE - s)) / HSV_BASE; + // values[6] = (v * (HSV_BASE - (s * (HSV_BASE - f)) / HSV_BASE)) / HSV_BASE; + // so that the are rounded divides. + // + + tmp = Divide_With_Round(s * f, HSV_BASE); + values[3] = Divide_With_Round(v * (HSV_BASE - tmp), HSV_BASE); + + values[4] = + values[5] = Divide_With_Round(v * (HSV_BASE - s), HSV_BASE); + + tmp = HSV_BASE - Divide_With_Round(s * (HSV_BASE - f), HSV_BASE); + values[6] = Divide_With_Round(v * tmp, HSV_BASE); + + + // This should not be rounded. + i = h / HSV_BASE; + + i += (i > 4) ? -4 : 2; + *r = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *b = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); + + i += (i > 4) ? -4 : 2; + *g = Divide_With_Round(values[i] * RGB_BASE, HSV_BASE); +} diff --git a/WWFLAT32/MISC/MAKEFILE b/WWFLAT32/MISC/MAKEFILE new file mode 100644 index 0000000..38506b6 --- /dev/null +++ b/WWFLAT32/MISC/MAKEFILE @@ -0,0 +1,197 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 26, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = misc +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + crc.obj & + delay.obj & + detproc.obj & + exit.obj & + facing8.obj & + facing16.obj & + facingFF.obj & + fading.obj & + findargv.obj & + irandom.obj & + lib.obj & + opsys.obj & + random.obj & + reverse.obj & + shakescr.obj & + cliprect.obj & + proc.obj & + version.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + make + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/MISC/MISC.H b/WWFLAT32/MISC/MISC.H new file mode 100644 index 0000000..6a3abae --- /dev/null +++ b/WWFLAT32/MISC/MISC.H @@ -0,0 +1,178 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +VOID cdecl Prog_End(VOID); +VOID cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE *cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PROC.ASM */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +int Get_CPU(VOID); + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +UBYTE Random(VOID); +int Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +LONG Reverse_LONG(LONG number); +WORD Reverse_WORD(WORD number); +LONG Swap_LONG(LONG number); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ + +void *Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); + +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD Processor(VOID); +extern WORD Operating_System(VOID); +extern ULONG random ( ULONG mod ) ; +extern void randomize ( void ) ; + +extern int Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H + + diff --git a/WWFLAT32/MISC/OPSYS.ASM b/WWFLAT32/MISC/OPSYS.ASM new file mode 100644 index 0000000..6eef656 --- /dev/null +++ b/WWFLAT32/MISC/OPSYS.ASM @@ -0,0 +1,151 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/opsys.asm 1.1 1994/04/18 09:14:12 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Operating System Flags * +;* * +;* File Name : OPSYS.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : January 26, 1993 * +;* * +;* Last Update : January 26, 1993 [SB] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Operating_System -- Determines what the operating system is. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Operating_System :NEAR +GLOBAL OperatingSystem :WORD + +DOS equ 1 +WIN31STD equ 2 +WIN31ENH equ 3 +WIN30ENH equ 4 +WIN30STD equ 5 +WIN30REAL equ 6 + +DATASEG + +OperatingSystem dw 0 + + +CODESEG + +;*************************************************************************** +;* Operating_System -- Determines what the operating system is. * +;* * +;* INPUT: NONE. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/26/1993 SB : Created. * +;*=========================================================================* +PROC Operating_System C near + USES ebx,ecx,edx,es,edi + + + ; Check for Windows 3.1 + mov eax,160Ah ; WIN31CHECK + int 2fh + or ax,ax + jz short RunningUnderWin31 + + ;check for Windows 3.0 enhanced mode + mov eax,1600h ; WIN386CHECK + int 2fh + mov bl,al + mov eax,WIN30ENH + test bl,7fh + jnz short Exit + + ;check for 3.0 WINOLDAP + mov eax,4680h ; IS_WINOLDAP_ACTIVE + int 2fh + or eax,eax + jnz short NotRunningUnderWin + + ; rule out MS-DOS 5.0 task switcher + mov eax,4b02h ; detect switcher + push ebx + push es + push edi + xor ebx,ebx + mov edi,ebx + mov es,bx + int 2fh + pop edi + pop es + pop ebx + or eax,eax + jz short NotRunningUnderWin ; MS-DOS 5.0 task switcher found. + + ; check for standrd mode Windows 3.0 + mov eax,1605h ;PMODE_START + int 2fh + mov eax,WIN30STD + cmp ecx,-1 + jz short Exit + + ;check for real mode Windows 3.0 + mov eax,1606h ; PMODE_STOP + int 2fh + mov eax,WIN30REAL + jmp SHORT Exit + +RunningUnderWin31: + ; At this point: CX == 3 means Windows 3.1 enhanced mode. + ; CX == 2 means Windows 3.1 standard mode. + mov eax,WIN31STD + cmp ecx,2 + je short Exit + + mov eax,WIN31ENH + jmp SHORT Exit + +NotRunningUnderWin: + mov eax,DOS + +Exit: + mov [WORD PTR OperatingSystem], ax + ret + +ENDP Operating_System + + + +;---------------------------------------------------------------------------- + +END + \ No newline at end of file diff --git a/WWFLAT32/MISC/PROC.ASM b/WWFLAT32/MISC/PROC.ASM new file mode 100644 index 0000000..2c71946 --- /dev/null +++ b/WWFLAT32/MISC/PROC.ASM @@ -0,0 +1,140 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/system/rcs/opsys.asm 1.1 1994/04/18 09:14:12 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : Operating System Flags * +;* * +;* File Name : OPSYS.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : January 26, 1993 * +;* * +;* Last Update : January 26, 1993 [SB] * +;* * +;* Updated to 32bit protected mode JAW * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Operating_System -- Determines what the operating system is. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Get_CPU :NEAR + + +DATASEG + +OperatingSystem dw 0 + + +CODESEG + +;*************************************************************************** +;* Operating_System -- Determines what the operating system is. * +;* * +;* INPUT: NONE. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/26/1993 SB : Created. * +;*=========================================================================* + PROC Get_CPU C near + USES ebx,ecx,edx,es,edi +IF 0 + xor eax,eax + mov ax,1 + pushf + pop bx + and bh,0fh + push bx + popf + pushf + pop cx + and ch,0f0h + cmp ch,0f0h + je @@1 ;8086 or below 80286 + + inc ax + or bh,0f0h + push bx + popf + pushf + pop cx + and ch,0f0h + je @@1 ;80286 +ENDIF + mov eax,3 + mov ebx,esp + and esp,0fffffffch + pushfd + pop edx + mov ecx,edx + xor edx,000040000h + push edx + popfd + pushfd + pop edx + push ecx + popfd + xor edx,ecx + and edx,000040000h ;test alignment check bit + mov esp,ebx + jz @@1 ;80386 + ;.486 + inc eax + pushfd + pop edx + mov ecx,edx + xor edx,000200000h + push edx + popfd + pushfd + pop edx + push ecx + popfd + xor edx,ecx ;test id bit + jz @@1 ;80486 +P586 + mov eax,1 + ;.586 or higher, cpuid returns cpu generation number in ax bits 8-11 + cpuid + and eax,0f00h + shr eax,8 +P386 +@@1: ret + +ENDP Get_CPU + + + +;---------------------------------------------------------------------------- + +END + diff --git a/WWFLAT32/MISC/RANDOM.ASM b/WWFLAT32/MISC/RANDOM.ASM new file mode 100644 index 0000000..66c603b --- /dev/null +++ b/WWFLAT32/MISC/RANDOM.ASM @@ -0,0 +1,119 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : RANDOM.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +;* UBYTE Random(VOID); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +Global Random :NEAR +Global Get_Random_Mask :NEAR +Global RandNumb :DWORD + + DATASEG + +RandNumb DD 12349876H + + CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END + \ No newline at end of file diff --git a/WWFLAT32/MISC/REVERSE.ASM b/WWFLAT32/MISC/REVERSE.ASM new file mode 100644 index 0000000..1b77715 --- /dev/null +++ b/WWFLAT32/MISC/REVERSE.ASM @@ -0,0 +1,139 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/misc/rcs/reverse.asm 1.3 1994/04/25 12:22:45 scott_bowen Exp $ +;*************************************************************************** +;** 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 * +;* * +;* File Name : REVERSE.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : April 20, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; LONG Reverse_LONG(LONG number); * +; WORD Reverse_WORD(WORD number); * +; LONG Swap_LONG(LONG number); +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Reverse_WORD :NEAR +GLOBAL Swap_LONG :NEAR +GLOBAL Reverse_LONG :NEAR + +CODESEG + + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; LONG Reverse_LONG(LONG number); +; WORD Reverse_WORD(WORD number); +; LONG Swap_LONG(LONG number); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_LONG +; +; LONG Reverse_LONG(LONG number); +; +;* + PROC Reverse_LONG C near + ARG number:DWORD + +IF 1 + mov eax,[DWORD PTR number] + xchg al,ah + ror eax,16 + xchg al,ah +ELSE + + ; This is old 16 bit code. + mov ax,[WORD PTR number] + mov dx,[WORD PTR number+2] + xchg ah,dl + xchg al,dh +ENDIF + + ret + + ENDP Reverse_LONG + +;----------------------------------------------------------------- + +;----------------------------------------------------------------- +; +; REVERSE_WORD +; +; WORD Reverse_WORD(WORD number); +; +;* + PROC Reverse_WORD C near + ARG number:WORD + + mov ax,[number] + xchg ah,al + ret + + ENDP Reverse_WORD + +;----------------------------------------------------------------- + + +;----------------------------------------------------------------- +; +; SWAP_LONG +; +; LONG Swap_LONG(LONG number); +; +;* + PROC Swap_LONG C near + ARG number:DWORD + +IF 1 + ; 32 bit code. + mov eax,[DWORD PTR number] + ror eax,16 +ELSE + ; 16 bit code. + mov ax,[WORD PTR number+2] + mov dx,[WORD PTR number] +ENDIF + + ret + + + ENDP Swap_LONG + +;----------------------------------------------------------------- + + END + diff --git a/WWFLAT32/MISC/SHAKESCR.ASM b/WWFLAT32/MISC/SHAKESCR.ASM new file mode 100644 index 0000000..cd93772 --- /dev/null +++ b/WWFLAT32/MISC/SHAKESCR.ASM @@ -0,0 +1,158 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : SHAKESCR.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 19, 1993 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END + \ No newline at end of file diff --git a/WWFLAT32/MISC/VERSION.CPP b/WWFLAT32/MISC/VERSION.CPP new file mode 100644 index 0000000..2ea51c5 --- /dev/null +++ b/WWFLAT32/MISC/VERSION.CPP @@ -0,0 +1,63 @@ +/* +** 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 . +*/ + +/* +/*************************************************************************** + ** 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 * + * * + * File Name : VERSION.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : July 26, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Version -- Returns with current library version text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + + + +PRIVATE BYTE *version = "Westwood Studios - 32 Bit Library Version " + __DATE__ + "\r\n"; + +/*************************************************************************** + * VERSION -- Returns with current library version text. * + * * + * Use this routine to determine the current library version. The * + * version text contains the date that it was created. * + * * + * INPUT: none * + * * + * OUTPUT: Returns pointer to version text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1991 JLB : Created. * + *=========================================================================*/ +BYTE *cdecl Version(VOID) +{ + return(version); +} diff --git a/WWFLAT32/MISC/WWLIB32.H b/WWFLAT32/MISC/WWLIB32.H new file mode 100644 index 0000000..106364b --- /dev/null +++ b/WWFLAT32/MISC/WWLIB32.H @@ -0,0 +1,68 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + +#endif // WWLIB32_H + diff --git a/WWFLAT32/MISC/WWSTD.H b/WWFLAT32/MISC/WWSTD.H new file mode 100644 index 0000000..a9000cf --- /dev/null +++ b/WWFLAT32/MISC/WWSTD.H @@ -0,0 +1,308 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define ERROR -1 + +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +inline int ABS(int); +inline long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +inline short MIN(short, short); +inline int MIN(int, int); +inline long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +inline short MAX(short, short); +inline int MAX(int, int); +inline long MAX(long, long); + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. + +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif + + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif + + diff --git a/WWFLAT32/MONO/INITMONO.CPP b/WWFLAT32/MONO/INITMONO.CPP new file mode 100644 index 0000000..889ec92 --- /dev/null +++ b/WWFLAT32/MONO/INITMONO.CPP @@ -0,0 +1,75 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Initialize mono * + * * + * File Name : INITMONO.CPP * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef MONO_H +#include "mono.h" +#endif + +#ifndef DESCMGMT_H +#include "descmgmt.h" +#endif + +/*************************************************************************** + * INITIALIZE_MONO_SCREEN -- Initializes the Mono display data * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/21/1994 jaw: Created. * + *========================================================================*/ + +int Initialize_Mono_Screen(void) +{ + // get a valid selector to mono screen. +// Map_Segment_To_Address(0x0b0000UL, 0x8000UL); + + MonoScreen = 0xb0000 ; + + return (int)0; +} + + + diff --git a/WWFLAT32/MONO/MAKEFILE b/WWFLAT32/MONO/MAKEFILE new file mode 100644 index 0000000..c76b279 --- /dev/null +++ b/WWFLAT32/MONO/MAKEFILE @@ -0,0 +1,182 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 30, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = mono +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + monoprnt.obj & + mono.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/MONO/MONO.ASM b/WWFLAT32/MONO/MONO.ASM new file mode 100644 index 0000000..2156b99 --- /dev/null +++ b/WWFLAT32/MONO/MONO.ASM @@ -0,0 +1,845 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Mono Screen system * +;* * +;* File Name : MONO.ASM * +;* * +;* Programmer : Jeff Wilson * +;* * +;* Start Date : March 28, 1994 * +;* * +;* Last Update : September 8, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;GLOBAL MonoScreen :DWORD +;GLOBAL MonoEnabled :DWORD +; +;GLOBAL C Mono_Set_Cursor :NEAR +;GLOBAL C Mono_Clear_Screen :NEAR +;GLOBAL C Mono_Scroll :NEAR +;GLOBAL C Mono_Put_Char :NEAR +;GLOBAL C Mono_Draw_Rect :NEAR +; +;GLOBAL C _Mono_Text_Print :NEAR +;GLOBAL C Mono_Text_Print :NEAR +; +;GLOBAL C Mono_Print :NEAR +; +;GLOBAL C Mono_View_Page :NEAR +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + + +; +; External declares so these functions can be called +; +GLOBAL MonoScreen :DWORD +GLOBAL MonoEnabled :DWORD + +GLOBAL Mono_Set_Cursor :NEAR ; done +GLOBAL Mono_Clear_Screen :NEAR ; done +GLOBAL Mono_Scroll :NEAR ; done +GLOBAL Mono_Put_Char :NEAR ; done +GLOBAL Mono_Draw_Rect :NEAR ; done +GLOBAL _Mono_Text_Print :NEAR ; done +GLOBAL Mono_Text_Print :NEAR ; done +GLOBAL Mono_Print :NEAR ; done +GLOBAL Mono_View_Page :NEAR ; done + +; +; Equates used in this file +; +NULL = 0 ; null code +CR = 13 ; carriage return code +CPL = 80 ; characters per line +LPS = 25 ; lines per screen + + +DATASEG + +MonoX DD 0 +MonoY DD 0 +MonoOff DD 0 +MonoScreen DD 0b0000h ;Deffault to Real mode! +MonoEnabled DD 0 ; Is mono printing enabled? + +;==================================================================== + +CharData DB 0DAh,0C4h,0BFh,0B3h,0D9h,0C4h,0C0h,0B3h ; Single line + DB 0D5h,0CDh,0B8h,0B3h,0BEh,0CDh,0D4h,0B3h ; Double horz. + DB 0D6h,0C4h,0B7h,0BAh,0BDh,0C4h,0D3h,0BAh ; Double vert. + DB 0C9h,0CDh,0BBh,0BAh,0BCh,0CDh,0C8h,0BAh ; Double line. + + +; x,y,dist +BoxData DB 1,0,0 ; Upper left corner. + DB 1,0,1 ; Top edge. + DB 0,1,0 ; Upper right corner. + DB 0,1,2 ; Right edge. + DB -1,0,0 ; Bottom right corner. + DB -1,0,1 ; Bottom edge. + DB 0,-1,0 ; Bottom left corner. + DB 0,-1,2 ; Left edge. + DB 0,0,-1 ; End of list. + +; Mono page segment layout array. +PageMap DD 0,1,2,3,4,5,6,7 + +;=================================================================== + +CODESEG + + +;*************************************************************************** +;* Map_Segment_To_Address_ -- Translate a 16bit Seg:Offset address to a * +;* Linear address. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; int Map_Segment_To_Address ( unsigned seg , unsigned offset ); + + + +;*************************************************************************** +;* MONO_SET_CURSOR -- Sets the mono cursor to specified coordinates. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Set_Cursor(int x, int y); + + +PROC Mono_Set_Cursor C near + + USES eax , ebx , edx + + ARG xpos : DWORD + ARG ypos : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; sub eax,eax + + mov eax,[ypos] +; mov ah,CPL +; imul ah + lea eax , [ eax + 4 * eax ] ; multiply by CPL + shl eax , 4 + +; sub ebx,ebx + mov ebx,[xpos] + add ebx,eax + + ; Update cursor position. + mov edx,03B4h + + mov al,0Eh ; High byte register set. + out dx,al + inc edx + mov al,bh + out dx,al ; Set high byte. + + dec edx + mov al,0Fh ; Low byte register set. + out dx,al + inc edx + mov al,bl + out dx,al ; Set low byte. + + ; Update the globals. + add ebx,ebx + mov [MonoOff],ebx + mov eax,[xpos] + mov [MonoX],eax + mov eax,[ypos] + mov [MonoY],eax + +??exit: + ret + +ENDP Mono_Set_Cursor + + +;*************************************************************************** +;* MONO_CLEAR_SCREEN -- Clears the mono screen and homes cursor. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Clear_Screen(void); + +PROC Mono_Clear_Screen C near + + USES eax , ecx , edi + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; mov eax,[MonoScreen] ; ES:DI = Mono RAM address. +; mov es,ax +; sub edi,edi + mov edi , [ MonoScreen ] + mov eax,02000200h ; Clear leave attrib bit normal. + mov ecx,8000h/4 ; Number of longs to clear. + rep stosd ; Clear the mono screen. + + push 0 + push 0 + + call Mono_Set_Cursor + add esp , 8 + +??exit: + ret + +ENDP Mono_Clear_Screen + + +;*************************************************************************** +;* MONO_SCROLL -- Scroll the mono screen up specified lines. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Scroll(DWORD lines); +PROC Mono_Scroll C near + + USES eax , ebx , ecx , edx , edi , esi + ARG lines : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; xor eax,eax ; clear eax so no need for sign extend + mov eax, [lines] ; get lines available + or eax,eax ; any lines to scroll? + je short ??fini ; =>NO + + mov ebx,eax ; set line counter + + mov edx,[MonoY] ; get row count + ror edx,16 ; store it in high half of register + mov dx,[WORD PTR MonoOff] ; get column offset + ror edx,16 + +; mov eax,[MonoScreen] ; get selector for mono screen +; push ds ; save off data seg for later +; mov ds,ax ; set data source register +; mov es,ax ; and extra source register + + sub eax,eax ; set to clear clear line + +??looper: + mov ecx,(80*24) ; Number of words to move. + + ; xor edi,edi ; dst start at top of screen area + ; mov esi,80*2 ; src start at next line down + mov edi , [ MonoScreen ] + lea esi , [ 80 * 2 + edi ] + rep movsw ; Scroll the screen upward. + + dec dx ; decrement Y counter + ror edx,16 ; switch to mono offset + sub dx,80*2 ; fix MonoOffset + ror edx,16 ; switch to y counter + + mov ecx,40 ; Clear out the last line. + rep stosd ; by storing words across it + dec ebx ; last line? + + jne ??looper ; =>NO + + ; reset data values + ; pop ds ; restore the ds segment + mov [WORD PTR MonoY],dx ; store of the mono y position + ror edx,16 ; switch to screen offset + mov [WORD PTR MonoOff],dx ; store of the mono offset + +??fini: +??exit: + ret + +ENDP Mono_Scroll + + +;*************************************************************************** +;* MONO_PUT_CHAR -- Output a character to the mono screen. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Put_Char(char character, int attrib=2); + +PROC Mono_Put_Char C near + + USES eax , edi + + ARG character : BYTE + ARG attrib : DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax + + mov edi,[MonoOff] +; mov eax,[MonoScreen] +; mov es,ax ; ES:DI = First character output pointer. + add edi , [ MonoScreen ] + + ; Output character to monochrome monitor. + mov al,[character] + mov ah,[BYTE PTR attrib] +; stosw + mov [ edi ] , ax + + ; Update cursor position. + inc [MonoX] ; X position moves. + + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + + call Mono_Set_Cursor + add esp,8 + +??exit: + ret + +ENDP Mono_Put_Char + + +;*************************************************************************** +;* MONO_DRAW_RECT -- Draw a rectangle using mono characters. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + +PROC Mono_Draw_Rect C near + + USES eax , ebx , ecx , esi , edi + + ARG xpos:DWORD + ARG ypos:DWORD + ARG width:DWORD + ARG height:DWORD + ARG attrib:DWORD + ARG thick:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + mov esi,OFFSET BoxData + mov edi,OFFSET CharData + +; mov cl,3 +; sub eax,eax + mov eax,[thick] + and eax,011b + shl eax,3 + add edi,eax + + ; Prep width and height. + cmp [width],2 + jb ??fini + + cmp [height],2 + jb ??fini + + sub [width],2 + sub [height],2 + + ; Preserve cursor position for later restore. + mov ecx,[MonoX] + push ecx + mov ecx,[MonoY] + push ecx + + ; Cursor starts at upper left corner. + mov ecx,[ypos] + push ecx + mov ecx,[xpos] + push ecx + call Mono_Set_Cursor + add esp,8 + +??drawloop: + ; Determine the number of characters to output. + mov ecx,[width] + cmp [BYTE PTR esi+2],1 + je short ??gotlen + + mov ecx,[height] + cmp [BYTE PTR esi+2],2 + je short ??gotlen + + mov ecx,1 +??gotlen: + + jecxz ??donerun + +??runloop: + sub ebx,ebx + mov bl,[BYTE PTR edi] + +; mov ebx,eax + sub eax,eax + mov al,[BYTE PTR attrib] + push eax + push ebx + + call Mono_Put_Char + add esp,8 + + movsx eax,[BYTE PTR esi+1] +; cbw + add eax,[MonoY] + push eax + movsx eax,[BYTE PTR esi] +; cbw + add eax,[MonoX] + dec eax ; Undo cursor advance. + push eax + + call Mono_Set_Cursor ; Properly advance cursor. + add esp,8 + + loop ??runloop + +??donerun: + + ; Advance to next control entry. + add esi,3 + inc edi + cmp [BYTE PTR esi+2],-1 + jne ??drawloop + + ; Restore cursor to original position. + call Mono_Set_Cursor + add esp,8 + +??fini: +??exit: + ret + +ENDP Mono_Draw_Rect + + +;*************************************************************************** +;* MONO_TEXT_PRINT -- Prints text to the mono screen at coordinates. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Text_Print(void *text, int x, int y, int attrib, int update); + +PROC _Mono_Text_Print C near + + USES eax,ebx,ecx,edx,edi,esi + + ARG text:DWORD + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + ARG update:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + ; Preserve cursor coordinates for later restoration. + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + + cmp [text],NULL + je ??fini + + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + call Mono_Set_Cursor + add esp,8 + + mov esi,[text] + +??charloop: + xor eax,eax + mov al,[BYTE PTR esi] ; Fetch character to output. + inc esi + + ; Stop processing on a NULL character. + or eax,eax + je short ??fini + + ; Special processing for a '\r' characters. + cmp eax,CR + je short ??cr + + ; Output character to monochrome monitor. +??normal: +; xor ah,ah + + mov ebx,eax + mov eax,[attrib] + push eax + push ebx + call Mono_Put_Char + add esp,8 + + ; Perform adjustments if wrapping past right margin. + cmp [WORD PTR MonoX],CPL + jb short ??nowrap + + inc [ypos] + + mov eax,[ypos] + push eax +; sub eax,eax + push 0 + call Mono_Set_Cursor + add esp,8 + + jmp short ??nowrap + + ; Move to start of next line. +??cr: + inc [ypos] + + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + call Mono_Set_Cursor + add esp,8 + + ; Scroll the monochrome screen if necessary. +??nowrap: + cmp [MonoY],LPS + jb short ??noscroll + + push 1 + call Mono_Scroll + add esp,4 + + dec [ypos] + +??noscroll: + jmp short ??charloop + +??fini: + cmp [update],0 + jne short ??noupdate + + call Mono_Set_Cursor +??noupdate: + add esp,8 + +??exit: + ret + +ENDP _Mono_Text_Print + +;===================================================================== + +PROC Mono_Text_Print C near + USES eax + ARG text:DWORD + ARG xpos:DWORD + ARG ypos:DWORD + ARG attrib:DWORD + + + cmp [MonoEnabled],0 + je ??exit + +; sub eax,eax + push 0 + mov eax,[attrib] + push eax + mov eax,[ypos] + push eax + mov eax,[xpos] + push eax + mov eax,[text] + push eax + + call _Mono_Text_Print + add esp,20 + +??exit: + ret + +ENDP Mono_Text_Print + + + +;*************************************************************************** +;* MONO_PRINT -- Prints text to the mono screen at current pos. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;*=========================================================================* +; void Mono_Print(void *text); + +PROC Mono_Print C near + + USES eax + + ARG text:DWORD + + cmp [MonoEnabled],0 + je ??exit + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + +; mov eax,1 + push 1 +; mov eax,2 + push 2 + mov eax,[MonoY] + push eax + mov eax,[MonoX] + push eax + mov eax,[text] + push eax + + call _Mono_Text_Print + add esp,20 + +??exit: + ret + +ENDP Mono_Print + +;*************************************************************************** +;* Mono_View_Page -- page in a mono screen * +;* * +;* Displays the specified page in displayable mono memory area. * +;* * +;* INPUT: WORD page = which page of memory we will use for storage * +;* * +;* OUTPUT: old_page * +;* * +;* WARNINGS: none. * +;* * +;* HISTORY: * +;*=========================================================================* +; int cdecl Mono_View_Page(int page); + + +PROC Mono_View_Page C near + + USES eax,ebx,ecx,edx,edi,esi + + ARG page:DWORD + + LOCAL oldpage:DWORD + + cmp [MonoEnabled],0 + je ??exit + + cld + +; mov ax,cs +; and ax,7 +; or ax,SS_DATA + +; mov ds,ax +; mov es,ax + + ; Prepare the original page number for return to caller. + mov ebx,[PageMap] + mov [oldpage],ebx + + ; If the desired page is already displayed, then don't do anything. + mov eax,[page] + cmp eax,ebx + je short ??fini + + ; Verify that page specified is legal. + cmp eax,7 + ja short ??fini + + ; Find where the logical page to display is actually located. + mov ecx,8 + + mov edi,OFFSET PageMap + repne scasd + neg ecx + add ecx,7 ; ECX = where desired page is located. + + ; Swap the page ID bytes in the PageMap array. + sub edi,4 + mov ebx,[PageMap] + mov eax,[edi] + mov [edi],ebx + mov [PageMap],eax + + ; Set DS and ES to point to each page. +; mov eax,[MonoScreen] +; mov ds,ax + mov esi , [ MonoScreen ] +; shl ecx,8 + shl ecx , 12 +; add ecx,edi ; NO Addition to selectors! + lea edi , [ esi + ecx ] + +; mov edi,ecx +; xor esi,esi + + ; Exchange the two pages. + mov ecx,1000H/4 + +??looper: + mov edx,[edi] + mov ebx,[esi] + mov [edi],ebx + mov [esi],edx + add esi,4 + add edi,4 + loop ??looper + + ; Return with the original page number. +??fini: +??exit: + mov eax,[oldpage] + ret + +ENDP Mono_View_Page + +END + diff --git a/WWFLAT32/MONO/MONO.H b/WWFLAT32/MONO/MONO.H new file mode 100644 index 0000000..b028af1 --- /dev/null +++ b/WWFLAT32/MONO/MONO.H @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Mono Sub-system * + * * + * File Name : MONO.H * + * * + * Programmer : Joe Bostic * + * 32Bit Programmer : Jeff Wilson * + * * + * Start Date : March 28, 1994 * + * * + * Last Update : March 28, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef MONO_H +#define MONO_H + +// C++ Routines +//================================================================== + +// Mono Screen routines +//================================================================== +int Initialize_Mono_Screen ( void ); + + +// C Routines +//================================================================== +#ifdef __cplusplus +extern "C" { +#endif + +// Mono Screen routines +//=================================================================== + extern unsigned MonoScreen; + extern unsigned MonoEnabled; + + extern void Mono_Set_Cursor(int x, int y); + extern void Mono_Clear_Screen(void); + extern void Mono_Scroll(int lines); + extern void Mono_Put_Char(char character, int attrib=2); + extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib=2, int thick=0); + extern void Mono_Text_Print(void const *text, int x, int y, int attrib=2); + extern void Mono_Print(void const *text); + extern void Mono_View_Page(int page); + extern int Mono_Printf(char const *string, ...); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/WWFLAT32/MONO/MONOPRNT.CPP b/WWFLAT32/MONO/MONOPRNT.CPP new file mode 100644 index 0000000..4bc1112 --- /dev/null +++ b/WWFLAT32/MONO/MONOPRNT.CPP @@ -0,0 +1,77 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MONOPRNT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 12, 1993 * + * * + * Last Update : November 2, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mono_Printf -- Prints formated text to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include "mono.h" + +/*************************************************************************** + * Mono_Printf -- Prints formated text to the monochrome screen. * + * * + * This routine is used to print formatted text (similar to printf) to * + * the monochrome screen. * + * * + * INPUT: string -- The string that is to be printed. * + * * + * ... -- Any optional parameters that the string requires * + * to format properly. * + * * + * OUTPUT: Returns with the length of the string that it printed to the * + * monochrome screen. * + * * + * WARNINGS: The total length of the formatted string must not exceed * + * 255 bytes. * + * * + * HISTORY: * + * 11/02/1993 JLB : Created. * + *=========================================================================*/ +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + if (!MonoEnabled) return(0); + + va_start(va, string); + + vsprintf(buffer, string, va); + Mono_Print(buffer); + + va_end(va); + return(strlen(buffer)); +} diff --git a/WWFLAT32/PALETTE/LOADPAL.CPP b/WWFLAT32/PALETTE/LOADPAL.CPP new file mode 100644 index 0000000..e7d1989 --- /dev/null +++ b/WWFLAT32/PALETTE/LOADPAL.CPP @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Load_Palette * + * * + * File Name : LOADPAL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 27, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the file I/O system, * + * specifically Load_Data(). * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Palette -- Loads a palette file into the given palette buffer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "iff.h" +#include "palette.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +/*************************************************************************** + * Load_Palette -- Loads a palette file into the given palette buffer. * + * * + * INPUT: * + * BYTE * file_name - name of the file to load. * + * BYTE * palette_pointer - pointer to palette buffer. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID cdecl Load_Palette(BYTE *palette_file_name, VOID *palette_pointer) +{ + #if(IBM) + Load_Data(palette_file_name, palette_pointer, 768); + #else + Load_Data(palette_file_name, palette_pointer, (ULONG)(2<. +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : January 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = palette +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + loadpal.obj & + morphpal.obj & + palette.obj & + pal.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/PALETTE/MORPHPAL.CPP b/WWFLAT32/PALETTE/MORPHPAL.CPP new file mode 100644 index 0000000..1d62e82 --- /dev/null +++ b/WWFLAT32/PALETTE/MORPHPAL.CPP @@ -0,0 +1,174 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : wwlib32 * + * * + * File Name : PALTOPAL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : May 2, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Morph_Palette -- morphs a palette from source to destination * + * Palette_To_Palette -- morph src palette to a dst palette * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "video.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ +#define SCALE(a,b,c) (((((LONG)(a)<<8) / (LONG)(b) ) * (ULONG)(c)) >>8) + + +/* +********************************** Globals ********************************** +*/ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE WORD cdecl Palette_To_Palette(VOID *src_palette, VOID *dst_palette, ULONG current_time, ULONG delay); + + +/*************************************************************************** + * Morph_Palette -- morphs a palette from source to destination * + * * + * INPUT: * + * VOID *src_pal - starting palette * + * VOID *dst_pal - ending palette * + * UWORD delay - time delay in 60ths of a second * + * VOID *callback - user-defined callback, NULL if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/02/1994 BR : Created. * + *=========================================================================*/ +VOID cdecl Morph_Palette (VOID *src_pal, VOID *dst_pal, UWORD delay, + VOID (*callback) (VOID) ) +{ + WORD result; + ULONG pal_start = TickCount.Time(); + VOID (*cb_ptr) ( VOID ) ; // callback function pointer + +// (VOID *)cb_ptr = callback; + cb_ptr = callback ; + + /*===================================================================*/ + /* Make sure that we don't go too fast but also make sure we keep */ + /* processing the morph palette if we have one. */ + /*===================================================================*/ + while (1) { + if (src_pal && dst_pal) { + result = Palette_To_Palette (src_pal, dst_pal, + (TickCount.Time() - pal_start), (ULONG)delay); + if (!result) + break; + + if (callback) { + (*cb_ptr)(); + } + } + } + + return; + +} /* end of Morph_Palette */ + + +/*************************************************************************** + * Palette_To_Palette -- morph src palette to a dst palette * + * * + * Creates & sets a palette that's in-between 'src_palette' & * + * 'dst_palette'; how close it is to dst_palette is based on how close * + * 'current_time' is to 'delay'. 'current_time' & 'delay' are based on * + * 0 being the start time. * + * * + * INPUT: VOID *src_palette = palette we want to morph from * + * VOID *dst_palette = palette we want to morph to * + * LONG current_time = time we started morph pal * + * LONG delay = time we want the morph to take* + * * + * OUTPUT: WORD if the time had elapsed and no chages were * + * necessary this routine returns FALSE * + * otherwise it will always return TRUE (this * + * was necessary to detect the end of the ice * + * effect. * + * * + * HISTORY: * + * 05/24/1993 MC : Created. * + *=========================================================================*/ +PRIVATE WORD cdecl Palette_To_Palette(VOID *src_palette, VOID *dst_palette, + ULONG current_time, ULONG delay) +{ + BYTE colour; + BYTE diff; + WORD chgval; + WORD lp; + WORD change; + static BYTE palette[768]; + + /*======================================================================*/ + /* Loop through each RGB value attempting to change it to the correct */ + /* color. */ + /*======================================================================*/ + for (change = lp = 0; lp < 768; lp++) { + if (current_time < delay ) { + diff = ( ( ((BYTE *)dst_palette)[lp] & 63) - + ( ((BYTE *)src_palette)[lp] & 63) ); + if (diff) + change = TRUE; + chgval = SCALE(diff, delay, current_time); + colour = ( ((BYTE *)src_palette)[lp] & 63) + chgval; + } + else { + colour = ((BYTE *)dst_palette)[lp] & 63; + change = FALSE; + } + palette[lp] = colour; + } + /*======================================================================*/ + /* Set the palette to the color that we created. */ + /*======================================================================*/ + Set_Palette(palette); + return(change); + +} /* end of Palette_To_Palette */ + + +/*************************** End of morphpal.cpp ***************************/ + + \ No newline at end of file diff --git a/WWFLAT32/PALETTE/PAL.ASM b/WWFLAT32/PALETTE/PAL.ASM new file mode 100644 index 0000000..9c425d0 --- /dev/null +++ b/WWFLAT32/PALETTE/PAL.ASM @@ -0,0 +1,388 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +include "keyboard.inc" + + +;****************************** Declarations ******************************** +GLOBAL Set_Palette_Range:NEAR +GLOBAL Bump_Color:NEAR +GLOBAL CurrentPalette:BYTE:768 +GLOBAL PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG + + +IF 1 +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* + + PROC Set_Palette_Range C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG palette:DWORD + + cld + + ;*=================================================================*/ + ;* Set up pointers to begin making palette comparison */ + ;*=================================================================*/ + mov esi, [palette] + mov edi, OFFSET CurrentPalette + mov ebx, OFFSET PaletteTable + mov ecx, 0 + +??loop_top: + mov eax,[esi] ; read a dword from palette source + mov edx,[edi] ; read a dword from compare palette + and eax,00FFFFFFh ; palette entrys are only 3 bytes + and edx,00FFFFFFh ; long so and of extra + cmp eax,edx ; if they are not the same then + jne ??set_table ; add them into the table + add esi,3 + add edi,3 + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + jmp ??set_pal ; so now go set the palette +??set_table: + shl eax,8 ; shift bgr value up register + mov al,cl ; store which palette entry num + mov [ebx],eax + add ebx,4 + movsw ; copy the three gun values into + movsb ; the shadow palette. Use movsb + inc cl ; adjust to next palette entry + jnz ??loop_top ; if we dont wrap to zero we have more + +??set_pal: + mov esi,ebx + mov ebx,OFFSET PaletteTable + sub esi,ebx ; if ebx didn't change there + jz ??exit ; is nothing to set + shr esi,2 ; find how many entrys + + mov eax,[ebx] + + movzx ecx,al ; we are currently on entry 0 + add ebx,4 + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + + push ebx + mov bx,[VertBlank] + and bl,001h + shl bl,3 + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + pop ebx + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + cli +??loop: + shr eax,8 ; shift down the red gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write it to the video card + jmp $ + 2 ; force cache to flush, to create a time + shr eax,8 ; shift down the blue gun value + out dx,al ; write the green value to video card + jmp $ + 2 ; force cache to flush, to create a time + inc ecx ; move edx to next palette entry + + mov eax,[ebx] ; get next value to set + add ebx,4 ; and post increment the palette value + cmp al,cl ; check if DAC position already correct + je ??correct_pos + + mov edx,03C8h ; Tell DAC of the color gun to start setting. + out dx,al ; First color set. + mov dx,03C9h + +??correct_pos: + dec esi + jnz ??loop + sti +;***************** Time Critical Section End ******************* +??exit: + ret + + ENDP Set_Palette_Range +ELSE +;*************************************************************************** +;* Set_Palette_Range -- Sets changed values in the palette. * +;* * +;* INPUT: * +;* VOID *palette - pointer to the new palette. * +;* * +;* OUTPUT: * +;* none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/25/1994 SKB : Created. * +;* 04/27/1994 BR : Converted to 32-bit * +;*=========================================================================* +; VOID cdecl Set_Palette_Range(VOID *palette); + PROC Set_Palette_Range C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG palette:DWORD + LOCAL remain:DWORD ; 32-bit: converted to LONG + + cld + + mov bx,[VertBlank] + and bl,001h + shl bl,3 + + ; Make a copy of the palette passed in. + mov edi,OFFSET CurrentPalette + mov esi,[palette] + mov [remain],768 + + ; Search for differences between the current palette and the + ; new palette. When a difference is found, output a block + ; of color registers and keep scanning. +??bodyloop: + mov ecx,[remain] + + repe cmpsb ; Search for differences. + je short ??exit + dec esi + dec edi + inc ecx + + mov edx,0 ; clear EDX + mov eax,ecx + mov ecx,3 + div ecx ; EAX = # of colors to set, EDX = Fraction. + or edx,edx + jz short ??nofrac + neg edx + add edx,3 ; Back offset skip needed. + inc eax ; Fractional color rounds up to whole color to set. +??nofrac: + + ; Set CX to be the number of color guns to set. + mov ecx,eax ; Colors * 3 bytes per color. + add ecx,eax + add ecx,eax + + ; Chop this DAC dump short if necessary in order to reduce + ; sparkling. + mov [remain],0 + cmp ecx,86*3 ; Number of color guns to set per vert retrace + jbe short ??ok + sub ecx,86*3 + mov [remain],ecx + mov ecx,86*3 +??ok: + + ; Adjust the palette offsets back to point to the RED color gun. + sub esi,edx + sub edi,edx + + ; Determine the color number to start setting. + neg eax + add eax,256 ; AX = Color to start setting (0..255). + + ; Tell DAC of the color gun to start setting. + mov edx,03C8h + out dx,al ; First color set. + + ; Set the colors only during a VSync. + mov edx,03DAh ; CRTC register. + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + +;??wait: +; in al,dx +; test al,01000b +; jnz ??wait + +;??retrace: +; in al,dx +; test al,01000b +; jz ??retrace + + ; Update the DAC data register. + mov dx,03C9h + +;**************** Time Critical Section Start ****************** + pushf + cli +??loop: + lodsb + stosb + out dx,al + jmp $ + 2 ; force cache to flush, to create a time + ; delay to give DAC time to get value + loop ??loop + popf +;***************** Time Critical Section End ******************* + + cmp [remain],0 + jnz ??bodyloop + +??exit: + ret + + ENDP Set_Palette_Range +ENDIF + + +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. +??colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short ??gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short ??gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] +??gotit: + inc edi + inc esi + loop ??colorloop + + mov ax,[changed] + ret + + ENDP Bump_Color + + END + +;*************************** End of pal.asm ******************************** + diff --git a/WWFLAT32/PALETTE/PALETTE.CPP b/WWFLAT32/PALETTE/PALETTE.CPP new file mode 100644 index 0000000..099e200 --- /dev/null +++ b/WWFLAT32/PALETTE/PALETTE.CPP @@ -0,0 +1,392 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include +#include "wwstd.h" +#include "video.h" +#include "palette.h" +#include "timer.h" + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern UBYTE CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE VOID cdecl Determine_Bump_Rate(VOID *palette, WORD delay, WORD *ticks, WORD *rate); +PRIVATE BOOL cdecl Bump_Palette(VOID *palette1, UWORD step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * VOID *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID cdecl Set_Palette(VOID *palette) +{ + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * VOID *palette - palette to set color in * + * WORD color - which color index to set * + * VOID *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID cdecl Set_Palette_Color(VOID *palette, WORD color, VOID *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((UBYTE *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(UWORD*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * BYTE *palette1 - this is the palette to fade to. * + * UWORD delay - fade with this timer count down * + * VOID *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +VOID Fade_Palette_To(VOID *palette1, UWORD delay, VOID (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + WORD jump; // Gun values to jump per palette set. + ULONG timer; // Tick count timer used for timing. + WORD ticksper; // The ticks (fixed point) per bit jump. + WORD tickaccum; + VOID (*cb_ptr)(VOID); // callback function pointer + +// (VOID *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------- Fading only supported in MCGA ---------------------- + */ + if (Get_Video_Mode() != MCGA_MODE) { // was global GraphicMode + Set_Palette(palette1); + return; + } + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * UBYTE *palette - palette to fade to * + * WORD delay - desired time delay in 60ths of a second * + * WORD *ticks - output: loop ticks per color jump * + * WORD *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE VOID cdecl Determine_Bump_Rate(VOID *palette, WORD delay, WORD *ticks, + WORD *rate) +{ + WORD gun1; // Palette 1 gun value. + WORD gun2; // Palette 2 gun value. + WORD diff; // Maximum color gun difference. + WORD tp; // Temporary tick accumulator. + WORD index; // Color gun working index. + LONG t; // Working tick intermediate value. + WORD adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((UBYTE *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((LONG)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (WORD)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL cdecl Bump_Palette(VOID *palette1, UWORD step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + WORD index; // Index to DAC register gun. + WORD gun1,gun2; // Palette 1 gun value. + UBYTE palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + /* + --------------------- Fading only supported in MCGA ---------------------- + */ + if (Get_Video_Mode() != MCGA_MODE) { // was global GraphicMode + Set_Palette(palette1); + return (FALSE); + } + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((UBYTE *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +/**************************** End of palette.cpp ***************************/ + + diff --git a/WWFLAT32/PALETTE/PALETTE.H b/WWFLAT32/PALETTE/PALETTE.H new file mode 100644 index 0000000..748c840 --- /dev/null +++ b/WWFLAT32/PALETTE/PALETTE.H @@ -0,0 +1,85 @@ +/* +** 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 . +*/ + +/*************************************************************************** +;** 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 : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +VOID cdecl Set_Palette(VOID *palette); +VOID cdecl Set_Palette_Color(VOID *palette, WORD color, VOID *data); +VOID Fade_Palette_To(VOID *palette1, UWORD delay, VOID (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +VOID cdecl Load_Palette(BYTE *palette_file_name, VOID *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +VOID cdecl Morph_Palette (VOID *src_palette, VOID *dst_palette, UWORD delay, + VOID *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID Set_Palette_Range(VOID *palette); +extern BOOL Bump_Color(VOID *palette, WORD changable, WORD target); + +#ifdef __cplusplus +} +#endif +extern "C" extern UBYTE CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ + diff --git a/WWFLAT32/PLAYCD/GETCD.CPP b/WWFLAT32/PLAYCD/GETCD.CPP new file mode 100644 index 0000000..016373d --- /dev/null +++ b/WWFLAT32/PLAYCD/GETCD.CPP @@ -0,0 +1,151 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : GETCD.CPP * + * * + * Programmer : STEVE WETHERILL BASED ON JOE BOSTIC CODE * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * GetCDClass::GetCDClass -- default constructor * + * GetCDClass::~GetCDClass -- destructor * + * GetCDClass::GetCDDrive -- returns the logical CD drive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * GetCDClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +GetCDClass::GetCDClass(VOID) +{ + memset ( this , 0 , sizeof ( GetCDClass ) ) ; + + if (DPMI_real_alloc(2, &cdDrive_addrp, &largestp)) + exit(1); + + CDCount = 0; + CDIndex = 0; + + /* + ** Set all CD drive placeholders to empty + */ + memset (CDDrives, NO_CD_DRIVE, MAX_CD_DRIVES); + + /* + ** Dos will only currently support one cd drive so just + ** set the first entry to it. + */ + GetCDDrives(); +} + +/*************************************************************************** + * GetCDClass -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW: Created. * + *=========================================================================*/ + +GetCDClass::~GetCDClass(VOID) +{ + if(cdDrive_addrp.seg) + DPMI_real_free(cdDrive_addrp); // free up those conventional buffers +} + +/*************************************************************************** + * GetCDDrive -- returns the logical CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * WORD logical_drive * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +void GetCDClass::GetCDDrives(VOID) + +{ + for (int lp = 0; lp < 26; lp++ ) { + /* + ** This call determines if the current specifed drive is a + ** CD ROM drive. + ** Input: + ** AX = 150Bh + ** CX = CD rom drive letter to check (A = 0, B = 1) + ** Output: + ** AX = non zero if drive is a CD ROM, zero if it is not. + ** BX = Signature word (ADADh if CD rom extension are installed) + */ + sregs . es = cdDrive_addrp . seg ; + regs . x . ebx = 0; + regs . x . eax = 0x150B; + regs . x . ecx = lp; + DPMI_real_intr ( 0x2F , & regs , & sregs ) ; + + if (regs.x.ebx == 0xADAD && regs.x.eax != 0) { + CDDrives[CDCount++] = lp; + } + } +} +/* ==================================================================== */ + diff --git a/WWFLAT32/PLAYCD/GETCD.CPP.BAK b/WWFLAT32/PLAYCD/GETCD.CPP.BAK new file mode 100644 index 0000000..70ce3be --- /dev/null +++ b/WWFLAT32/PLAYCD/GETCD.CPP.BAK @@ -0,0 +1,150 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : GETCD.CPP * + * * + * Programmer : STEVE WETHERILL BASED ON JOE BOSTIC CODE * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * GetCDClass::GetCDClass -- default constructor * + * GetCDClass::~GetCDClass -- destructor * + * GetCDClass::GetCDDrive -- returns the logical CD drive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * GetCDClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +GetCDClass::GetCDClass(VOID) +{ + memset ( this , 0 , sizeof ( GetCDClass ) ) ; + + if (DPMI_real_alloc(2, &cdDrive_addrp, &largestp)) + exit(1); + + CDCount = 0; + CDIndex = 0; + + /* + ** Set all CD drive placeholders to empty + */ + memset (CDDrives, NO_CD_DRIVE, MAX_CD_DRIVES); + + /* + ** Dos will only currently support one cd drive so just + ** set the first entry to it. + */ + GetCDDrives(); +} + +/*************************************************************************** + * GetCDClass -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW: Created. * + *=========================================================================*/ + +GetCDClass::~GetCDClass(VOID) +{ + if(cdDrive_addrp.seg) + DPMI_real_free(cdDrive_addrp); // free up those conventional buffers +} + +/*************************************************************************** + * GetCDDrive -- returns the logical CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * WORD logical_drive * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +void GetCDClass::GetCDDrives(VOID) + +{ + for (int lp = 0; lp < 26; lp++ ) { + /* + ** This call determines if the current specifed drive is a + ** CD ROM drive. + ** Input: + ** AX = 150Bh + ** CX = CD rom drive letter to check (A = 0, B = 1) + ** Output: + ** AX = non zero if drive is a CD ROM, zero if it is not. + ** BX = Signature word (ADADh if CD rom extension are installed) + */ + sregs . es = cdDrive_addrp . seg ; + regs . x . ebx = 0; + regs . x . eax = 0x150B; + DPMI_real_intr ( 0x2F , & regs , & sregs ) ; + + if (regs.x.ebx == 0xADAD && regs.x.eax != 0) { + CDDrives[CDCount++] = lp; + } + } +} +/* ==================================================================== */ + diff --git a/WWFLAT32/PLAYCD/MAKEFILE b/WWFLAT32/PLAYCD/MAKEFILE new file mode 100644 index 0000000..7555a49 --- /dev/null +++ b/WWFLAT32/PLAYCD/MAKEFILE @@ -0,0 +1,184 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = playcd +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getcd.obj & + playcd.obj +# redbook.obj +# playcd.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/PLAYCD/PLAYCD.ASM b/WWFLAT32/PLAYCD/PLAYCD.ASM new file mode 100644 index 0000000..d6e054c --- /dev/null +++ b/WWFLAT32/PLAYCD/PLAYCD.ASM @@ -0,0 +1,289 @@ +; +; 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 . +; + + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + + +DPMI_INTR equ 031h + +GLOBAL DPMI_real_alloc : near +GLOBAL DPMI_real_free : near +GLOBAL DPMI_real_intr : near +GLOBAL DPMI_real_call : near + + +STRUC SEGSEL + segmen dw ? + select dw ? +ENDS + +STRUC REGS + _eax dd ? + _ebx dd ? + _ecx dd ? + _edx dd ? + _esi dd ? + _edi dd ? + _cflag dd ? +ENDS + +STRUC SREGS + _es dw ? + _cs dw ? + _ss dw ? + _ds dw ? + _fs dw ? + _gs dw ? +ENDS + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + size_ref db ? +ENDS + + +CODESEG + +; int DPMI_real_alloc ( UINT , SEGREG * , USHORT * ) ; + +PROC DPMI_real_alloc C near + USES ebx , edx + ARG paragra : DWORD + ARG blk_segptr : DWORD + ARG largest_blkptr : DWORD + + mov eax, 0100h + mov ebx, [paragra] + int DPMI_INTR + + jnc ??dpmi_succed + mov ebx, [largest_blkptr] + mov [ word ptr ebx ] , bx + movzx eax , al + ret + +??dpmi_succed: + mov ebx, [blk_segptr] + mov [(type SEGSEL ptr ebx). segmen ] , ax + mov [(type SEGSEL ptr ebx). select ] , dx + xor eax , eax + ret + +ENDP DPMI_real_alloc + + +;************************************************************************** +; int DPMI_real_free ( UINT ) ; + +PROC DPMI_real_free C near + USES eax , edx + ARG blk_selec : DWORD + + mov eax, 0101h + mov edx, [blk_selec] + shr edx , 16 + int DPMI_INTR + ret +ENDP DPMI_real_free + + +PROC DPMI_real_intr C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + lea edi , [ regblk ] + xor eax , eax + lea ecx , [ regblk . size_ref ] + sub ecx , edi + shr ecx , 2 + rep stosd + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + + mov eax , 0300h + mov ebx , [ vector ] + xor bh , bh + xor ecx , ecx + lea edi , [ regblk ] + + int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_intr + + +PROC DPMI_real_call C near + USES eax , ebx , ecx , edx , edi , esi + ARG vector : dword + ARG regs_ptr: dword + ARG sreg_ptr: dword + + LOCAL regblk : DPMI_REGS + + + lea edi , [ regblk ] + xor al , al + lea ecx , [ regblk . size_ref ] + sub ecx , edi + rep movsb + + + mov ebx , [ regs_ptr ] + mov eax , [ (type REGS ptr ebx) . _eax ] + mov [ regblk . _eax ] , eax + + mov eax , [ (type REGS ptr ebx) . _ebx ] + mov [ regblk . _ebx ] , eax + + mov eax , [ (type REGS ptr ebx) . _ecx ] + mov [ regblk . _ecx ] , eax + + mov eax , [ (type REGS ptr ebx) . _edx ] + mov [ regblk . _edx ] , eax + + mov eax , [ (type REGS ptr ebx) . _esi ] + mov [ regblk . _esi ] , eax + + mov eax , [ (type REGS ptr ebx) . _edi ] + mov [ regblk . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ (type SREGS ptr ebx) . _es ] + mov [ regblk . _es ] , ax + + mov ax , [ (type SREGS ptr ebx) . _ds ] + mov [ regblk . _ds ] , ax + +; mov eax , 0300h +; mov ebx , [ vector ] +; xor bh , bh +; xor ecx , ecx +; lea edi , [ regblk ] + +; int DPMI_INTR + + mov ebx , [ regs_ptr ] + mov eax , [ regblk . _eax ] + mov [ (type REGS ptr ebx) . _eax ] , eax + mov eax , [ regblk . _ebx ] + mov [ (type REGS ptr ebx) . _ebx ] , eax + mov eax , [ regblk . _ecx ] + mov [ (type REGS ptr ebx) . _ecx ] , eax + mov eax , [ regblk . _edx ] + mov [ (type REGS ptr ebx) . _edx ] , eax + + mov eax , [ regblk . _esi ] + mov [ (type REGS ptr ebx) . _esi ] , eax + mov eax , [ regblk . _edi ] + mov [ (type REGS ptr ebx) . _edi ] , eax + + + mov ebx , [ sreg_ptr ] + mov ax , [ regblk . _es ] + mov [ (type SREGS ptr ebx) . _es ] , ax + + mov ax , [ regblk . _ds ] + mov [ (type SREGS ptr ebx) . _ds ] , ax + + ret +ENDP DPMI_real_call + +END + + + diff --git a/WWFLAT32/PLAYCD/PLAYCD.H b/WWFLAT32/PLAYCD/PLAYCD.H new file mode 100644 index 0000000..8a529af --- /dev/null +++ b/WWFLAT32/PLAYCD/PLAYCD.H @@ -0,0 +1,304 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + union REGS regs; + struct SREGS sregs; + + SEGSEL cdDrive_addrp; + UWORD largestp; + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +private: + VOID GetCDDrives(VOID); + +}; + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + + +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H diff --git a/WWFLAT32/PLAYCD/PLAYCD.H.BAK b/WWFLAT32/PLAYCD/PLAYCD.H.BAK new file mode 100644 index 0000000..e3e10f4 --- /dev/null +++ b/WWFLAT32/PLAYCD/PLAYCD.H.BAK @@ -0,0 +1,305 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + union REGS regs; + struct SREGS sregs; + + SEGSEL cdDrive_addrp; + UWORD largestp; + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +private: + VOID GetCDDrive(VOID); + +}; + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + + +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/WWFLAT32/PLAYCD/REDBOOK.CPP b/WWFLAT32/PLAYCD/REDBOOK.CPP new file mode 100644 index 0000000..d85db1c --- /dev/null +++ b/WWFLAT32/PLAYCD/REDBOOK.CPP @@ -0,0 +1,518 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB * + * * + * File Name : REDBOOK.CPP * + * * + * Programmer : STEVE WETHERILL (FROM SCOTT BOWEN CODE) * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------* + *-------------------------------------------------------------------------* + * Functions: * + * RedBookClass::~RedBookClass(VOID) * + * RedBookClass::RedToHS(ULONG i) * + * RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) * + * RedBookClass::FullCDVolume(UBYTE chan) * + * RedBookClass::PlayTrack(UWORD track) * + * RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) * + * RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, * + * UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) * + * RedBookClass::CheckCDMusic(VOID) * + * RedBookClass::StopCDMusic(VOID) * + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include +#include +#include +#include + +#include "wwstd.h" +#include "playcd.h" +#include "wwmem.h" + + +/*************************************************************************** + * RedBookClass -- default constructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * calls GetCDDrive() * + * HISTORY: * + * 05/25/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::RedBookClass(VOID) + : GetCDClass() // call the base constructor + +{ + SEGSEL tmpadr ; + + tmpadr = cdDrive_addrp; + memset ( this , 0 , sizeof ( RedBookClass ) ) ; + cdDrive_addrp = tmpadr ; + + Stop.Length = 13; + Stop.Command = 133; + + Tinfo.Length = 26; + Tinfo.Command = 3; + Tinfo.CntTrns = 7; + Tinfo.TrInfo = 11; + + Play.Length = 22; + Play.Command = 132; + Play.AddrMd = 1; + + Volm.Length = 26; + Volm.Command = 12; + Volm.CntTrns = 9; + Volm.TrInfo = 3; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + + Stat.Length = 26; + Stat.Command = 3; + Stat.CntTrns = 11; + Stat.StatInfo = 15; + + if (DPMI_real_alloc(sizeof(TinfoType)/16+1, &Tinfo_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StatType)/16+1, &Stat_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(VolmType)/16+1, &Volm_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(PlayType)/16+1, &Play_addrp, &largestp)) + exit(1); + + if (DPMI_real_alloc(sizeof(StopType)/16+1, &Stop_addrp, &largestp)) + exit(1); + + GetCDDrive(); + +} + +/*************************************************************************** + * REDBOOKCLASS -- destructor * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +RedBookClass::~RedBookClass(VOID) +{ + if(Tinfo_addrp.seg) + DPMI_real_free(Tinfo_addrp); // free up those conventional buffers + + if(Stat_addrp.seg) + DPMI_real_free(Stat_addrp); + + if(Volm_addrp.seg) + DPMI_real_free(Volm_addrp); + + if(Play_addrp.seg) + DPMI_real_free(Play_addrp); + + if(Stop_addrp.seg) + DPMI_real_free(Stop_addrp); +} + +/*************************************************************************** + * REDTOHS -- RedBook to High-Sierra conversion * + * * + * * + * * + * INPUT: * + * ULONG * + * OUTPUT: * + * ULONG * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::RedToHS(ULONG i) +{ + return( ((i>>16) & 0xFF) * 60 * 75) + ( ((i>>8) & 0xFF) * 75) + (i & 0xFF); +} + +/*************************************************************************** + * MSFTORED -- Minute, Second, Frame to RedBook conversion * + * * + * * + * * + * INPUT: * + * UBYTE minute * + * UBYTE second * + * UBYTE frame * + * OUTPUT: * + * ULONG RedBook * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +ULONG RedBookClass::MSFtoRed(UBYTE m, UBYTE s, UBYTE f) +{ + return( ((ULONG)m << 16) + ((ULONG)s << 8) + (ULONG)f ); +} + +/*************************************************************************** + * FULLCDVOLUME -- set full volume * + * * + * * + * * + * INPUT: * + * UBYTE channel * + * * + * CHLEFT * + * CHRIGHT * + * CHBOTH * + * * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::FullCDVolume(UBYTE chan) +{ + + Volm.Vol0 = Volm.Vol1 = Volm.Vol2 = Volm.Vol3 = 255; + + Volm.TrnsAdOff = offsetof (VolmType, TrInfo); + Volm.TrnsAdSeg = Volm_addrp.seg; + + if(chan == CHLEFT) + { +// Volm.In0 = 0; +// Volm.In1 = 3; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol1 = 0; + } + else if(chan == CHRIGHT) + { +// Volm.In0 = 3; +// Volm.In1 = 1; +// Volm.In2 = 3; +// Volm.In3 = 3; + Volm.Vol0 = 0; + } + else /* both channels */ + { + Volm.In0 = 0; + Volm.In1 = 1; + Volm.In2 = 2; + Volm.In3 = 3; + } + +// WriteRealMem(REALPTR(Volm_addrp) << 16, &Volm, sizeof(VolmType)); + Mem_Copy ( &Volm , (void *) ( Volm_addrp.seg << 4 ) , sizeof(VolmType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Volm_addrp.seg; + + DPMI_real_intr(0x2F, ®s, & sregs); + +// ReadRealMem(&Volm, REALPTR(Volm_addrp) << 16, sizeof(VolmType)); + Mem_Copy ( (void *) ( Volm_addrp . seg << 4 ), &Volm ,sizeof(VolmType)); +} + + + +/*************************************************************************** + * PLAYTRACK -- play a track * + * * + * * + * * + * INPUT: * + * UWORD track * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::PlayTrack(UWORD track) +{ + + StopCDMusic(); + + Tinfo.Track = track; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); // gets start time of track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + + Play.Start = Tinfo.Start; + Tinfo.Track++; + + Tinfo.TrnsAdOff = offsetof (TinfoType, TrInfo); + Tinfo.TrnsAdSeg = Tinfo_addrp.seg; + +// WriteRealMem(REALPTR(Tinfo_addrp) << 16, &Tinfo, sizeof(TinfoType)); + Mem_Copy ( &Tinfo , (void *) ( Tinfo_addrp.seg << 4 ) , sizeof(TinfoType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Tinfo_addrp.seg; + + DPMI_real_intr(0x2F, ®s , &sregs); // gets start time of following track in Tinfo.Start + +// ReadRealMem(&Tinfo, REALPTR(Tinfo_addrp) << 16, sizeof(TinfoType)); + Mem_Copy ( (void *) ( Tinfo_addrp.seg << 4 ) , &Tinfo, sizeof(TinfoType)); + + + + Play.CntSect = RedToHS(Tinfo.Start) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = 0x0000; + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + + FullCDVolume(CHBOTH); +} + + +/*************************************************************************** + * PLAY_CD_MSL -- play cd from start min_sec for len * + * * + * * + * * + * INPUT: * + * UWORD min_sec * + * UWORD Len * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::Play_CD_MSL(UWORD min_sec, UWORD len) +{ + UWORD startM, startS, startF; + UWORD endM, endS, endF; + + if (!len) + return; + + endM = startM = (min_sec >> 8) + AUDIO_START_MIN; + endS = startS = (min_sec & 0xFF) + AUDIO_START_SEC; + startF = endF = 0; + + while (len > 59) { + endM++; + len -= 60; + } + + endS += len; + if (endS > 59) { + endM++; + endS -= 60; + } + + PlayMSF((UBYTE) startM, (UBYTE)startS, (UBYTE)startF, (UBYTE)endM, (UBYTE)endS, (UBYTE)endF, 2 /* chan */); +} + + +/*************************************************************************** + * PlayMSF -- Play Minute, Second, Frame to Minute, Second, Frame * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +VOID RedBookClass::PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan) +{ + + Play.Start = MSFtoRed(startM, startS, startF); + Play.CntSect = RedToHS(MSFtoRed(endM, endS, endF)) - RedToHS(Play.Start) - 1; + +// WriteRealMem(REALPTR(Play_addrp) << 16, &Play, sizeof(PlayType)); + Mem_Copy ( &Play , (void *) ( Play_addrp.seg << 4 ) , sizeof(PlayType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (PlayType, Length); + sregs.es = Play_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Play, REALPTR(Play_addrp) << 16, sizeof(PlayType)); + Mem_Copy ( (void *) ( Play_addrp.seg << 4 ) , &Play, sizeof(PlayType)); + + FullCDVolume(chan); + +} + +/*************************************************************************** + * CheckCDMusic -- Check for CD playing * + * * + * * + * * + * INPUT: * + * UBYTE startM * + * UBYTE startS * + * UBYTE startF * + * UBYTE endM * + * UBYTE endS * + * UBYTE endF * + * UBYTE chan * + * OUTPUT: * + * UWORD TRUE if playing else FALSE * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + + +UWORD RedBookClass::CheckCDMusic(VOID) +{ + + Stat.TrnsAdOff = offsetof (StatType, StatInfo); + Stat.TrnsAdSeg = Stat_addrp.seg; + +// WriteRealMem(REALPTR(Stat_addrp) << 16, &Stat, sizeof(StatType)); + Mem_Copy ( &Stat , (void *) ( Stat_addrp.seg << 4 ) , sizeof(StatType)); + + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StatType, Length); + regs.x.eax = 0x1510; + sregs.es = Stat_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stat, REALPTR(Stat_addrp) << 16, sizeof(StatType)); + Mem_Copy ( (void *) ( Stat_addrp.seg << 4 ) , &Stat, sizeof(StatType)); + + return (Stat.Status&0x200); +} + +/*************************************************************************** + * STOPCDMUSIC -- stop CD playing * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * none * + * WARNINGS: * + * * + * HISTORY: * + * 05/27/1994 SW : Created. * + *=========================================================================*/ + +VOID RedBookClass::StopCDMusic(VOID) +{ + +// WriteRealMem(REALPTR(Stop_addrp) << 16, &Stop, sizeof(StopType)); + Mem_Copy ( &Stop , (void *) ( Stop_addrp.seg << 4 ) , sizeof(StopType)); + + regs.x.eax = 0x1510; + regs.x.ecx = cdDrive[0]; + regs.x.ebx = offsetof (StopType, Length); + sregs.es = Stop_addrp.seg; + + DPMI_real_intr(0x2F, ®s, &sregs); + +// ReadRealMem(&Stop, REALPTR(Stop_addrp) << 16, sizeof(StopType)); + Mem_Copy ( (void *) ( Stop_addrp.seg << 4 ) , &Stop, sizeof(StopType)); + +} + diff --git a/WWFLAT32/PROJECT.CFG b/WWFLAT32/PROJECT.CFG new file mode 100644 index 0000000..685c0a7 --- /dev/null +++ b/WWFLAT32/PROJECT.CFG @@ -0,0 +1,17 @@ +#*************************************************************************** +# development configuration switches + +CC_CFG = /i=$(%WWFLAT)\include /d2 /fh /of+ /zp1 /5s /s +ASM_CFG = /i. /i$(%WWFLAT)\include /zd /t /m /w+ /jJUMPS /ml +LIB_CFG = /b /n /n +LINK_CFG = option stack=32k debug all + +#*************************************************************************** +# Release configuration switches + +#CC_CFG = /i=$(%WWFLAT)\include /d1 /oateirl /s /fh /zp1 /5s +#ASM_CFG = /i$(%WWFLAT)\include /zd /t /m /w+ /jJUMPS /ml +#LIB_CFG = /b /n /n +#LINK_CFG = option stack=32k debug all + + diff --git a/WWFLAT32/SHAPE/DRAWSHP.ASM b/WWFLAT32/SHAPE/DRAWSHP.ASM new file mode 100644 index 0000000..526893d --- /dev/null +++ b/WWFLAT32/SHAPE/DRAWSHP.ASM @@ -0,0 +1,1128 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : DRAWSHP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : April 13, 1992 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* Not_Supported -- Replacement function for Draw_Shape routines not used* +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +; this struct is here to remove the hardwire way of programing +; implemented in the funtion Draw_Shape in ian image of + +STRUC VVPC_IMAGE + Off dd ? + Width dd ? + Height dd ? + Page dd ? +ENDS + + + +STRUC GVPC_IMAGE + vvpc VVPC_IMAGE <> + Xpos dd ? + Ypos dd ? + GraphicBuff dd ? +ENDS + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;****************************** Declarations ******************************** +GLOBAL Draw_Shape:NEAR +GLOBAL LCW_Uncompress:NEAR +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD + +GLOBAL MaskPage : dword +GLOBAL BackGroundPage : dword + + +;********************************* Data ************************************ + DATASEG +;--------------------------------------------------------------------------- +; Shape buffer & its size, set by Set_Shape_Buffer() +;--------------------------------------------------------------------------- +_ShapeBuffer DD 0 +_ShapeBufferSize DD 0 + +;--------------------------------------------------------------------------- +; Address of MaskPage & BackGroundPage, set by Init_Priority_System() +;--------------------------------------------------------------------------- +MaskPage DD 0 +BackGroundPage DD 0 + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +PredCount DD 0 +PredTable DB 1, 3, 2, 5, 4, 3, 2, 1 +PredValue DD 1 +PartialPred DD 0 ; partially faded predator effect value +PartialCount DD 0 + +;--------------------------------------------------------------------------- +; 32 bit versions of 16 bit stack variables +;--------------------------------------------------------------------------- +Flags DD ? ; globally accessible copy of flags + +viewport_ptr DD ? ; pointer to upper-left corner of viewport +viewport_width DD ? ; viewport width +viewport_height DD ? ; viewport height +viewport_yadd DD ? ; viewport y add +viewport_x DD ? ; viewport x-coord +viewport_y DD ? ; viewport y-coord +buff_ptr DD ? ; pointer to buffer containing viewport + +;********************************* Code ************************************ + CODESEG + + +;*************************************************************************** +;* Draw_Shape -- Draws a shape at given buffer coordinates and clips * +;* * +;* INPUT: * +;* DWORD gvp_ptr ; pointer to graphic viewport info * +;* DWORD shape_ptr ; the shape pointer to draw * +;* DWORD draw_x ; x-coord of hotspot in viewport * +;* DWORD draw_y ; y-coord of hotspot in viewport * +;* DWORD flags ; the flags for drawing the shape * +;* * +;* Optional Arguments: If the following flags are used, the given args * +;* MUST be present. Note that, if more than one one set of args is used, * +;* they must appear in this order (alphabetical). * +;* SHAPE_COLOR: DWORD color_table (256 bytes) * +;* SHAPE_FADING: DWORD fade_table (256 bytes), DWORD fade_count * +;* SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl * +;* SHAPE_PARTIAL: DWORD predator partial_value (0-255) * +;* SHAPE_PRIORITY: DWORD priority_level * +;* SHAPE_SCALING: DWORD x_scale, WORD y_scale * +;* SHAPE_SHADOW: DWORD shadowing_table (256 bytes) * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* File Organization: * +;* drawshp.asm : this file * +;* shape.inc : main shape header file; contains declarations for all * +;* globals, procedures and constants * +;* ds_table.asm: contains the procedure address tables for LSkipRout, * +;* RSkipRout, DrawRout, & PixelRout * +;* ds_l*.asm : left-skip routines * +;* ds_r*.asm : right-skip routines * +;* ds_d*.asm : drawing routines * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Shape format: * +;* Header: * +;* UWORD SType (0=normal, 1=16-color, 2=uncompressed, 4=variable-color) * +;* UBYTE Height * +;* UWORD Width * +;* UBYTE unmodified height * +;* UWORD size of shape in memory, including this header * +;* UWORD uncompressed data size * +;* Normal Shape: * +;* UBYTE [compressed] Shape data * +;* 16-color shape: * +;* UBYTE 16-color table * +;* UBYTE [compressed] Shape data * +;* variable-color shape: * +;* UBYTE # colors * +;* UBYTE color table (variable-length) * +;* UBYTE [compressed] Shape data * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Uncompressed shape data format: * +;* Data is stored as a bitmap, with 0's treated as a special case. Every * +;* 0 byte is followed by a repetition count byte. Every scan line is * +;* compressed separately. Thus, the following bitmap results in the * +;* following shape data: * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* 0 0 0 5 6 7 0 0 0 0 * +;* * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* 0 3 5 6 7 0 4 * +;* * +;*-------------------------------------------------------------------------* +;* * +;* How scaling is handled: * +;* Scaling is done by accumulating the x & y scale values. When the high * +;* byte of the accumulated value is set, the pixel (for x-scaling) or * +;* the line (for y-scaling) is drawn. The high byte is then cleared, * +;* and the low byte is left so roundoffs continue to accumulate. * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Drawing Procedures: * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* RSkipRout: skips given # bytes of data on the right-hand side of a * +;* shape. Just has to handle changing the current byte offset in the * +;* shape data buffer, since the draw routine knows where the left-hand * +;* side of the drawable region is. The routine may skip more bytes than * +;* it was told if it encounters a run of 0's, but it's assumed that a * +;* run of 0's will never go past the right edge of a shape. * +;* Input: * +;* ECX - number of uncompressed bytes to skip * +;* ESI - shape buffer data address * +;* Output: * +;* ECX - negative # bytes overrun, or 0 * +;* ESI - updated to the current location in the shape data * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* LSkipRout: skips given # bytes of data on the left-hand side of a * +;* shape. This routine must update the shape data byte offset, and it * +;* must properly update the current drawing position due to scaling, so * +;* it's a little more involved than the RSkip routine. The routine may * +;* skip more bytes than it's told if it encounters a run of 0's. If this * +;* happens, the draw routine must take these extra bytes into * +;* consideration. * +;* Input: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width in bytes * +;* Output: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - accumulated XTotal value at new pixel location * +;* ESI - updated to the current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* +;* * +;* DrawRout: draws one row of pixels, handles scaling, reversal and * +;* any per pixel effects like predator, shadow etc. * +;* EDX must be set up as follows: * +;* - No scaling: 0 * +;* - No left-clipping: 0 * +;* - Left clipping, but no overrun: set to computed initial value for * +;* that viewport coordinate * +;* - Left clipping, with overrun: set to XTotal value for that coordinate * +;* In any case, only the low byte of DL should contain data; the current * +;* byte in the shapebuffer should always be the first drawable pixel, * +;* even if it's partially clipped (in which case DL will contain data). * +;* Input: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* Output: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - decremented by # bytes (not pixels) drawn * +;* * +;*-------------------------------------------------------------------------* +;* * +;* Algorithm: * +;* - Initialize globals * +;* - Pull optional arguments off the stack * +;* - Set up drawing procedure pointers based on drawing flags * +;* - Read the values from the shape header * +;* - Compute the shape's scaled width & height * +;* - Adjust the shape's drawing coordinates based on centering & * +;* viewport-relative flag settings * +;* - Compute the clipped areas of the shape * +;* - Compute the number of drawn pixels horizontally & vertically * +;* - Compute the starting drawing offset in the viewport * +;* - Draw the shape * +;* * +;*-------------------------------------------------------------------------* +;* * +;* HISTORY: * +;* 04/13/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/26/1994 BR : Converted to 32-bit, restructured quite a bit. * +;* 08/09/1994 IML : Added C++ style interface. Various optimizations. * +;* 09/06/1994 IML : Ammendments for integration of p_* and ds_* routines.* +;* 09/14/1994 IML : Now handles LCW compression. * +;*=========================================================================* +PROC Draw_Shape C NEAR + USES eax,ebx,ecx,edx,edi,esi + + ;-------------------------------------------------------------------- + ; Arguments: + ;-------------------------------------------------------------------- + ARG gvp_ptr:DWORD ; pointer to graphic viewport info + ARG shape_ptr:DWORD ; the shape pointer to draw + ARG draw_x:DWORD ; the destination x pixel + ARG draw_y:DWORD ; the destination y pixel + ARG flags:DWORD ; the flags for drawing the shape + +IF FALSE + ;-------------------------------------------------------------------- + ; Define the local stack variables that Draw_Shape needs. These + ; parameters are defined in shape.inc. They're included here + ; just for reference. + ;-------------------------------------------------------------------- + ; + ;...................... proc addresses .............................. + ; + LOCAL LSkipRout:DWORD ; pointer to the skip routine + LOCAL RSkipRout:DWORD ; pointer to the skip routine + LOCAL DrawRout:DWORD ; pointer to the draw routine + ; + ;.................... optional arguments ............................ + ; + LOCAL ColorTable:DWORD ; ptr to the shapes color table + LOCAL FadingTable:DWORD ; extracted fading table pointer + LOCAL FadingNum:DWORD ; get the number of times to fade + LOCAL IsTranslucent:DWORD ; ptr to "are we translucent?" tbl + LOCAL Translucent:DWORD ; ptr to "ok we are translucent!" tbl + LOCAL PriLevel:BYTE ; the priority level of the object + LOCAL ScaleX:DWORD ; the x increment to scale by + LOCAL ScaleY:DWORD ; the y increment to scale by + LOCAL ShadowingTable:DWORD ; ptr to the shadowing table + ; + ;.................... Shape header values ........................... + ; + LOCAL ShapeType:DWORD ; shape type + LOCAL ShapeWidth:DWORD ; shape's unscaled width + LOCAL ShapeHeight:DWORD ; shape's unscaled height + LOCAL UncompDataLen:DWORD ; uncompressed data length + LOCAL ShapeData:DWORD ; pointer to [compressed] shape data + ; + ;.................. Scaled shape dimensions ......................... + ; + LOCAL ScaledWidth:DWORD ; shape's scaled width + LOCAL ScaledHeight:DWORD ; shape's scaled height + ; + ;.................. Pixel clipping variables ........................ + ; + LOCAL LeftClipPixels:DWORD ; # left-clipped pixels + LOCAL RightClipPixels:DWORD ; # right-clipped pixels + LOCAL TopClipPixels:DWORD ; # top-clipped pixels + LOCAL BotClipPixels:DWORD ; # bottom-clipped pixels + LOCAL PixelWidth:DWORD ; width of drawable area in pixels + LOCAL PixelHeight:DWORD ; height of drawable area in pixels + ; + ;..................... Drawing variables ............................ + ; + LOCAL NumColors:DWORD ; # colors for 16-color shapes + LOCAL StartDraw:DWORD ; ptr to starting draw position + LOCAL NextLine:DWORD ; offset of next drawing line + LOCAL LeftClipBytes:DWORD ; # left-clipped bytes + LOCAL XTotal:DWORD ; accumulated x-pixels for scaling + LOCAL XTotalInit:DWORD ; initial roundoff bits for XTotal + LOCAL YTotal:DWORD ; accumulated y-pixels for scaling + LOCAL HeightCount:DWORD ; height counter for drawing lines + LOCAL LineStart:DWORD ; address of start of line + LOCAL WidthCount:DWORD ; counts down # bytes skipped + LOCAL StashReg:DWORD ; temp variable for draw routines + LOCAL MaskAdjust:DWORD ; priority buffer offset + LOCAL BackAdjust:DWORD ; background buffer offset + LOCAL StashECX:DWORD ; temp variable for ECX register + LOCAL StashEDX:DWORD ; temp variable for EDX register + +ENDIF + + ;==================================================================== + ; Initialization: + ; - allocate space for globals + ; - validate shape pointer + ; - set SHAPE_COMPACT flag if needed + ;==================================================================== + ;-------------------------------------------------------------------- + ; Allocate stack space for our local variables. + ;-------------------------------------------------------------------- + LOCAL Local_Stack:BYTE:Local_Size + + ;-------------------------------------------------------------------- + ; Make sure the shape pointer is not NULL + ;-------------------------------------------------------------------- + cmp [shape_ptr],0 ; compare shape ptr value to NULL + jnz ??valid_shp ; if non-zero, it's valid + jmp ??exit ; otherwise get the heck outta here + + ;-------------------------------------------------------------------- + ; Move gvp info into local variables + ;-------------------------------------------------------------------- +??valid_shp: + mov edi,[gvp_ptr] ; get pointer to graphic viewport info + mov esi, [(type GVPC_IMAGE ptr edi). vvpc . Off ] ; extract viewport pointer + mov [viewport_ptr],esi + mov ebx,[(type GVPC_IMAGE ptr edi) . vvpc . Width ] ; extract viewport width + mov [viewport_width],ebx + mov eax,[(type GVPC_IMAGE ptr edi) . vvpc . Height ] ; extract viewport height + mov [viewport_height],eax + mov ecx,[(type GVPC_IMAGE ptr edi) . vvpc . Page ] ; calculate y add value + add ecx,ebx + mov [viewport_yadd],ecx + mov eax,[(type GVPC_IMAGE ptr edi) . Xpos ] ; extract viewport x-coord + mov [viewport_x],eax + mov eax, [(type GVPC_IMAGE ptr edi) . Ypos ] ; extract viewport y-coord + mov [viewport_y],eax + mul ecx ; calculate buffer pointer + add eax,[viewport_x] + sub esi,eax + mov [buff_ptr],esi + + + ;-------------------------------------------------------------------- + ; If this shape is a compact shape, set that bit in the flags arg + ;-------------------------------------------------------------------- + mov edi,[shape_ptr] ; check for compact shape flag + test [BYTE PTR edi],MAKESHAPE_COMPACT + + jz ??do_args ; if not process flags as is + or [flags],SHAPE_COMPACT ; mark it as a compact shape + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi,4 ; optional params start past flags + + ;-------------------------------------------------------------------- + ; Initialize optional argument values: + ;-------------------------------------------------------------------- + mov [ColorTable],0 ; default = NULL + mov [FadingTable],0 ; default = NULL + mov [FadingNum],0 ; default = no fading + mov [IsTranslucent],0 ; default = NULL + mov [Translucent],0 ; default = NULL + mov [PriLevel],0 ; default = no priority + mov [ScaleX],100h ; default = unity X scaling + mov [ScaleY],100h ; default = unity Y scaling + mov [ShadowingTable],0 ; default = NULL + + ;-------------------------------------------------------------------- + ; SHAPE_COLOR: DWORD color_table[256] + ;-------------------------------------------------------------------- +??color: + test [flags],SHAPE_COLOR ; does it have a color table + jz ??fading ; if not skip to fading + or [flags],SHAPE_COMPACT ; mark it as a compact shape + ; (for remapping purposes only) + mov eax,[flags + edi] + mov [ColorTable],eax ; save address of color table + add edi,4 ; point to next optional argument + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- +??fading: + test [flags],SHAPE_FADING ; are we fading this shape + jz ??ghost ; skip to ghosting check + mov eax,[flags + edi] + mov [FadingTable],eax ; save address of fading tbl + + mov eax,[flags + edi + 4] ; get fade num + + add edi,8 ; next argument + cmp eax,0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + and [flags],NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [FadingNum],eax + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl, DWORD translucent tbl + ;-------------------------------------------------------------------- +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz ??init_predator ; skip to predator check + mov eax,[flags + edi] + mov [IsTranslucent],eax ; save ptr to is_trans. tbl + mov eax,[flags + edi + 4] + mov [Translucent],eax ; save ptr to translucent tbl + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: Initialize the predator effect variables + ;-------------------------------------------------------------------- +??init_predator: + test [flags],SHAPE_PREDATOR ; is predator effect on + jz ??partial ; if not skip to partial + inc [PredCount] ; the pred table is byte aligned + and [PredCount],PRED_MASK ; keep entries within bounds + mov eax,[PredCount] + mov al,[BYTE PTR PredTable + eax] + mov [PredValue],eax ; put the pred value cs + mov [PartialCount],0 ; clear the partial count + mov [PartialPred],100h ; init partial to off + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??partial: + test [flags],SHAPE_PARTIAL ; is this a partial pred? + jz ??priority ; if not check priority + mov eax,[flags + edi] ; pull the partial value + mov [PartialPred],eax ; store it off + add edi,4 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_PRIORITY: DWORD priority_level + ;-------------------------------------------------------------------- +??priority: + test [flags],SHAPE_PRIORITY ; is this a priority draw + jz ??scale ; if not skip to scale + mov eax,[flags + edi] + mov [PriLevel],al ; store priority level + add edi,4 ; next argument + mov eax,[MaskPage] ; calculate priority buffer + sub eax,[buff_ptr] ; offset + mov [MaskAdjust],eax + mov eax,[BackGroundPage] ; calculate background buffer + sub eax,[buff_ptr] ; offset + mov [BackAdjust],eax + + ;-------------------------------------------------------------------- + ; SHAPE_SCALING: DWORD x_scale, WORD y_scale + ;-------------------------------------------------------------------- +??scale: + test [flags],SHAPE_SCALING ; are we scaling this shape. + jz ??shadow ; if not then skip scale y value + mov eax,[flags + edi] + mov [ScaleX],eax + mov eax,[flags + edi + 4] + mov [ScaleY],eax + add edi,8 ; next argument + + ;-------------------------------------------------------------------- + ; SHAPE_SHADOW: DWORD shadow_table[256] + ;-------------------------------------------------------------------- +??shadow: + test [flags],SHAPE_SHADOW ; are we ghosting this shape + jz short ??get_header ; if not then skip + mov eax,[flags + edi] + mov [ShadowingTable],eax ; save address of shadow table + add edi,4 ; next argument + + +??get_header: + ;==================================================================== + ; Get Shape header values + ;==================================================================== + mov esi,[shape_ptr] ; prepare to read header + movzx eax,[WORD PTR esi] + mov [ShapeType],eax ; extract shape type + movzx eax,[BYTE PTR esi + 2] + mov [ShapeHeight],eax + movzx eax,[WORD PTR esi + 3] ; extract shape height + mov [ShapeWidth],eax + movzx eax,[WORD PTR esi + 8] ; extract uncompressed data length + mov [UncompDataLen],eax + add esi,10 ; reposition index + + ;-------------------------------------------------------------------- + ; Now get NumColors, ColorTable address, & data pointer: + ; <16-color shape: + ; shape.Colortable[0] = # colors + ; shape data is after that many colors + ; 16-color shape: + ; shape.Colortable[] contains colors + ; shape data is after those colors + ; default 256-color shape: + ; shape data starts at shape.Colortable[0] + ; Note: ColorTable is set only if flags & SHAPE_COLOR is 0; otherwise, + ; the color table was passed in & we already have a pointer to it + ;-------------------------------------------------------------------- + ; + ;....................... <16-color shape: ........................... + ; + test [ShapeType],MAKESHAPE_VARIABLE + jz ??check_16 + movzx eax,[BYTE PTR esi] ; read # colors + mov [NumColors],eax ; save # colors + inc esi + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??norm_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??norm_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + + ;....................... 16-color shape: ............................ +??check_16: + test [ShapeType],MAKESHAPE_COMPACT + jz ??256_get_data_addr + mov [NumColors],16 ; save # colors + test [flags],SHAPE_COLOR ; don't set ColorTable if + jnz ??16_get_data_addr ; it was passed in + mov [ColorTable],esi ; save color table pointer + +??16_get_data_addr: + add esi,[NumColors] ; skip past color data + mov [ShapeData],esi ; set data address + jmp ??setup_procs + ; + ;....................... 256-color shape: ........................... + ; +??256_get_data_addr: + mov [ShapeData],esi ; set data address + + ;==================================================================== + ; Set up the drawing procedure addresses + ;==================================================================== + ;-------------------------------------------------------------------- + ; This code uses HORZ_REV, VERT_REV, & SCALING flags as an + ; offset into the LSkipTable, RSkipTable, and DrawTable. These + ; flags combined have values from 00h-07h, so each table must have + ; at least 8 entries. + ;-------------------------------------------------------------------- +??setup_procs: + mov ebx,[flags] ; load flags value + and ebx,07h ; clip high bits + add ebx,ebx ; mult by 4 to get DWORD offset + add ebx,ebx + mov eax,[LSkipTable + ebx] ; get table value + mov [LSkipRout],eax ; store it in the function pointer + mov eax,[RSkipTable + ebx] ; get table value + mov [RSkipRout],eax ; store it in the function pointer + mov eax,[DrawTable + ebx] ; get table value + mov [DrawRout],eax ; store it in the function pointer + +??compute_scalevals: + ;==================================================================== + ; Now compute scaled width & height. If the shape scales down to 0 + ; either horizontally or vertically, exit. + ;==================================================================== + test [flags],SHAPE_SCALING ; skip if no scaling + jz ??no_scaling + ; + ;........................ scaled width: ............................. + ; + mov eax,[ShapeWidth] ; get byte width + mov ebx,[ScaleX] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledWidth],eax ; save the scaled width + ; + ;........................ scaled height: ............................ + ; + mov eax,[ShapeHeight] ; get byte height + mov ebx,[ScaleY] ; prepare for register mul + mul ebx ; EDX:EAX = result + shrd eax,edx,8 ; EAX = result rounded down + or eax,eax + jz ??exit ; exit if EAX is 0 + mov [ScaledHeight],eax ; save the scaled height + jmp ??handle_centering + ; + ;......................... no scaling: .............................. + ; +??no_scaling: + mov eax,[ShapeWidth] + mov [ScaledWidth],eax ; pixel width = byte width + mov eax,[ShapeHeight] + mov [ScaledHeight],eax ; pixel height = byte height + + ;==================================================================== + ; Allow for SHAPE_CENTER by adjusting the draw_x & draw_y arguments: + ; draw_x -= ScaledWidth / 2 + ; draw_y -= ScaledHeight / 2 + ;==================================================================== +??handle_centering: + ; + ;........................ adjust draw_x ............................. + ; + test [flags],SHAPE_CENTER ; skip if not centered + jz ??handle_vp_rel + mov eax,[draw_x] ; load in draw_x + mov edx,[ScaledWidth] ; load in ScaledWidth + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_x],eax ; store it back into draw_x + ; + ;........................ adjust draw_y ............................. + ; + mov eax,[draw_y] ; load in draw_y + mov edx,[ScaledHeight] ; load in ScaledHeight + shr edx,1 ; divide it by 2 + sub eax,edx ; subract it from eax + mov [draw_y],eax ; store it back into draw_y + + ;==================================================================== + ; Allow for SHAPE_VIEWPORT_REL by adjusting draw_x & draw_y by the + ; viewport's coordinates + ;==================================================================== +??handle_vp_rel: + test [flags],SHAPE_VIEWPORT_REL ; skip if not vp-relative + jz ??compute_horz_clip + mov eax,[viewport_x] + add [draw_x],eax ; draw_x += viewport_x + mov eax,[viewport_y] + add [draw_y],eax ; draw_y += viewport_y + + ;==================================================================== + ; Now that we have the scaled size and adjusted x & y drawing + ; coordinates, we can compute the clipped areas of the shape: + ; LeftClipPixels = viewport_x - draw_x + ; - if negative, set to 0 + ; RightClipPixels = (draw_x + ScaledWidth) - + ; (viewport_x + viewport_width) + ; - if negative, set to 0 + ; + ; TopClipPixels = viewport_y - draw_y + ; - if negative, set to 0 + ; BotClipPixels = (draw_y + ScaledHeight) - + ; (viewport_y + viewport_height) + ; - if negative, set to 0 + ;==================================================================== +??compute_horz_clip: + ; + ;...................... left-clipped pixels ......................... + ; + mov eax,[viewport_x] + sub eax,[draw_x] ; EAX = viewport_x - draw_x + jge ??set_left_clip + mov eax,0 ; if EAX<0, set to 0 +??set_left_clip: + mov [LeftClipPixels],eax ; store # left-clipped pixels + ; + ;...................... right-clipped pixels ........................ + ; + mov eax,[draw_x] + add eax,[ScaledWidth] ; EAX = draw_x + ScaledWidth + mov edx,[viewport_x] + add edx,[viewport_width] ; EDX = viewport_x + viewport_width + sub eax,edx + jge ??set_right_clip + mov eax,0 ; if EAX<0, set to 0 +??set_right_clip: + mov [RightClipPixels],eax ; store # right-clipped pixels + ; + ;...................... top-clipped pixels .......................... + ; +??compute_vert_clip: + mov eax,[viewport_y] + sub eax,[draw_y] ; EAX = viewport_y - draw_y + jge ??set_top_clip + mov eax,0 ; if EAX<0, set to 0 +??set_top_clip: + mov [TopClipPixels],eax ; store # top-clipped pixels + ; + ;.................... bottom-clipped pixels ......................... + ; + mov eax,[draw_y] + add eax,[ScaledHeight] ; EAX = draw_y + ScaledHeight + mov edx,[viewport_y] + add edx,[viewport_height] ; EDX = viewport_y + viewport_height + sub eax,edx + jge ??set_bottom_clip + mov eax,0 ; if EAX<0, set to 0 +??set_bottom_clip: + mov [BotClipPixels],eax ; store # bottom-clipped pixels + + ;==================================================================== + ; Now compute the number of pixels actually drawn, horizontally and + ; vertically; exit if either is <= 0 + ;==================================================================== +??compute_drawn_pixels: + ; + ;.................... pixel width of drawn area ..................... + ; + mov eax,[ScaledWidth] ; get total width in pixels + sub eax,[LeftClipPixels] ; subtract off left-clipped pixels + sub eax,[RightClipPixels] ; subtract off right-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelWidth],eax ; store drawn pixel width + ; + ;.................... pixel height of drawn area .................... + ; + mov eax,[ScaledHeight] ; get total height in pixels + sub eax,[TopClipPixels] ; subtract off top-clipped pixels + sub eax,[BotClipPixels] ; subtract off bottom-clipped pixels + jle ??exit ; exit if no horizontal pixels drawn + mov [PixelHeight],eax ; store drawn pixel height + + ;==================================================================== + ; So, we're actually going to draw something; if (ShapeType & + ; MAKESHAPE_NOCOMP == 0) decompress the shape data into _ShapeBuffer: + ; LCW_Uncompress(ShapeData, _ShapeBuffer, UncompDataLen); + ; shape.DataLength + ; &_ShapeBuffer + ; &(shape's data) + ; - otherwise the shape data is already uncompressed + ;==================================================================== + test [ShapeType],MAKESHAPE_NOCOMP + jnz ??uncompressed + + mov eax,[UncompDataLen] + push eax ; push arg 3 + mov eax,[_ShapeBuffer] + push eax ; push arg 2 + mov eax,[ShapeData] + push eax ; push arg 1 + call LCW_Uncompress ; call routine + add esp,12 ; restore stack + mov eax,[_ShapeBuffer] + mov [ShapeData],eax + jmp ??copy_flags + +??uncompressed: +; mov eax,[ShapeData] ; set up pointer to shape data +; mov [_ShapeBuffer],eax + + + ;-------------------------------------------------------------------- + ; Set the global Flags variable + ;-------------------------------------------------------------------- +??copy_flags: + mov eax,[flags] + mov [Flags],eax + + ;==================================================================== + ; Now compute the actual buffer offset where drawing (not skipping) + ; will begin + ;==================================================================== + ;-------------------------------------------------------------------- + ; First, compute the x & y offsets of the shape's clipped upper-left + ; corner, relative to the viewport's upper-left corner: + ; x-offset = draw_x + LeftClipPixels - viewport_x + ; y-offset = draw_y + TopClipPixels - viewport_y + ;-------------------------------------------------------------------- + mov ebx,[draw_x] + add ebx,[LeftClipPixels] + sub ebx,[viewport_x] ; EBX = viewport x-offset + + mov eax,[draw_y] + add eax,[TopClipPixels] + sub eax,[viewport_y] ; EAX = viewport y-offset + + ;-------------------------------------------------------------------- + ; Then, adjust the viewport offsets due to horizontal & vertical + ; reversal: + ; if HORZ_REV, x-offset += (PixelWidth - 1) + ; if VERT_REV, y-offset += (PixelHeight - 1) + ;-------------------------------------------------------------------- + ; + ;................. Adjust for horizontal reversal ................... + ; + test [flags],SHAPE_HORZ_REV + jz ??adjust_vert_offset + add ebx,[PixelWidth] + dec ebx ; EBX = true x-offset + ; + ;................ Swap LeftClip & RightClip pixels .................. + ; + mov edx,[LeftClipPixels] ; exchange left & right-clipped pixels + xchg edx,[RightClipPixels] + mov [LeftClipPixels],edx + + ; + ;.................. Adjust for vertical reversal .................... + ; +??adjust_vert_offset: + test [flags],SHAPE_VERT_REV + jz ??adjust_pointer + add eax,[PixelHeight] + dec eax ; EAX = true y-offset + ; + ;.................. Swap TopClip & BotClip pixels ................... + ; + mov edx,[TopClipPixels] + xchg edx,[BotClipPixels] + mov [TopClipPixels],edx + + ;-------------------------------------------------------------------- + ; Now, adjust the starting position pointer: + ;-------------------------------------------------------------------- +??adjust_pointer: ;!!!!!!! convert to register mul for speed !!!!!!!! + add ebx,[viewport_ptr] ; add initial ptr to x-offset + mul [viewport_yadd] ; convert y-offset (EAX) to bytes + add ebx,eax ; add those bytes in + mov [StartDraw],ebx ; store the starting pointer + + ;-------------------------------------------------------------------- + ; Finally, if VERT_REV, negate yadd to move up not down: + ;-------------------------------------------------------------------- + test [flags],SHAPE_VERT_REV + jz ??init_xtotal + neg [viewport_yadd] ; move up, not down + + ;==================================================================== + ; Initialize the horizontal scale accumulation value: + ; If there are any left-clipped pixels, the scale accumulator will + ; have to be initialized with the value it >would< have by stepping + ; over that many pixels. This initial value can be computed by + ; dividing the # of left-clipped pixels by the x-scale value itself, + ; picking off the remainder from this division & negating it. This + ; sets the low byte of the remainder to the correct accumulation + ; value (the high bytes will be garbage). + ; (The alternative to this approach would be to multiply the + ; scale factor by the # clipped bytes, which is the result of the + ; division; however, negating the remainder is much faster than + ; the multiply would be.) + ;==================================================================== +??init_xtotal: + mov edx,0 ; prepare for divide + mov eax,[LeftClipPixels] ; get # left-clipped pixels + shl eax,8 ; multiply by 100h + mov ebx,[ScaleX] ; load ScaleX value + div bx ; 16-bit div: AX = rslt, DX = rem + mov [LeftClipBytes],eax ; save # left-clipped bytes + neg edx ; generate roundoff bits + and edx,0Fh ; only save low byte + mov [XTotalInit],edx ; save initial roundoff value + + ;==================================================================== + ; Initialize drawing variables: + ;==================================================================== + mov esi,[ShapeData] ; ESI = shape buffer starting point + mov edi,[StartDraw] ; EDI = drawing address + mov [YTotal],0 ; initialize accumulated scale + + ;==================================================================== + ; Clip the top-clipped lines. The object here is to set ESI to the + ; first drawable line in the _ShapeBuffer, and YTotal to: + ; high byte = # times to draw that line, + ; low byte = roundoff bits + ; + ; - Initialize values (ESI, HeightCount, YTotal) + ; - Skip loop if no top lines to clip + ; - Loop: + ; - save this line's byte position in _ShapeBuffer, in case we + ; overrun + ; - call RSkipRout with ECX set to # bytes to skip (one row) + ; - accumulate ScaleY into YTotal + ; - if high byte is non-zero, there are that many drawn lines: + ; - decrement HeightCount by that many lines + ; - clear the high byte in YTotal, but keep the roundoff bits + ; - if HeightCount > 0, loop again to clip more lines + ; - if HeightCount is 0, start drawing: + ; - ESI points to first non-clipped line in _ShapeBuffer + ; - YTotal contains 0 in high byte, roundoff bits in low byte + ; - otherwise, we've clipped too many lines: + ; - put ESI back to the line we just clipped + ; - set high byte of YTotal to # lines overrun + ; - subtract ScaleY from YTotal, to set it up for drawing loop + ;==================================================================== + ; + ;..................... skip if nothing to clip ...................... + ; + mov eax,[TopClipPixels] + cmp eax,0 ; see if any top-clipped pixels + jz ??draw_loop ; if not, skip this + mov [HeightCount],eax ; save off # lines to clip + +??clip_y_loop: + ; + ;...................... skip this row of bytes ...................... + ; + mov [LineStart],esi ; save this line's byte position + mov ecx,[ShapeWidth] ; set up ECX for RSkipRout + call [RSkipRout] ; skip 'ShapeWidth' bytes + ; + ;............... see if this row would have been drawn .............. + ; + mov eax,[ScaleY] + add [YTotal],eax ; accumulate scale factor + test [YTotal],0FF00h ; check to see if we draw the line + jz ??clip_y_loop ; if not loop again + ; + ;...................... decrement HeightCount ....................... + ; + mov eax,0 ; clear EAX + xchg al,[BYTE PTR YTotal+1] ; get # lines, clear it in YTotal + sub [HeightCount],eax ; subtract # drawn lines from HtCt + jg ??clip_y_loop ; if more lines remain, loop again + jns ??draw_loop ; is exactly 0; we're done clipping + ; + ;....................... adjust for overrun ......................... + ; + mov esi,[LineStart] ; point ESI back to this line + mov eax,[HeightCount] + neg eax ; EAX = # lines overrun + shl eax,8 ; multiply by 100h + add eax,[YTotal] ; add in roundoff bits + sub eax,[ScaleY] ; adjust down by y-scale + mov [YTotal],eax ; store in YTotal + + ;==================================================================== + ; The drawing loop (at long last!): + ; - Accumulate YTotal; if high byte is 0, skip this row of bytes & + ; loop again + ; - Skip left-clipped pixels: + ; - If we've skipped all the bytes on the line, just go to the + ; next line + ; - Draw middle pixels: + ; - Add the shape's pixel width to ECX (which could be negative + ; if we left-skipped into the drawable area) + ; - If ECX is still 0, there are no pixels to draw + ; - Otherwise, leave ECX as is & draw the pixels + ; - Skip right-clipped pixels: + ; - Add # right-clipped pixels to ECX (which could be negative if + ; the draw routine uncompressed 0's into the right-clipped + ; region) + ; - if ECX > 0, skip the remaining bytes + ; - Go to the next line: + ; - point EDI to the start of the next line in the viewport + ; - decrement the height counter, exit if it's 0 + ; - decrement YTotal's high byte + ; - if it's 0, go to the loop top + ; - otherwise, reset ESI to this line's start & redraw the line, + ; starting at left-clipped pixels + ; (NOTE: why not start drawing at middle pixels??????????) + ;==================================================================== +??draw_loop: + ; + ;................... accumulate YTotal & test it .................... + ; + mov eax,[ScaleY] ; get y scaling factor + add [YTotal],eax ; accumulate YTotal + test [YTotal],0FF00h ; see if we need to draw anything + jnz ??draw_line ; draw this line + ; + ;......................... skip this line ........................... + ; + mov ecx,[ShapeWidth] ; load shape's width in bytes + call [RSkipRout] ; skip this row & loop again + jmp ??draw_loop + + ; + ;--------------------- start drawing this line ---------------------- + ; +??draw_line: + mov [LineStart],esi ; save current byte position + ;.................................................................... + ; Skip left-clipped pixels: + ; - initialize [WidthCount] to total shape width in bytes + ; - set ECX to # >bytes< to clip + ; When LSkipRout returns: + ; - ECX will contain # >pixels< overrun + ; - EDX will contain the XTotal init value + ; - [WidthCount] will be decremented by total bytes skipped + ;.................................................................... +??draw_left: + mov eax,[ShapeWidth] ; load shape width + mov [WidthCount],eax ; set up for LSkipRout + mov ecx,[LeftClipBytes] ; bytes, not pixels! + call [LSkipRout] ; skip the bytes + cmp [WidthCount],0 + jz ??next_line ; The whole line was 0's + ;.................................................................... + ; Draw middle pixels: + ; - add PixelWidth to ECX (which may be negative) + ; - if ECX is 0, don't bother drawing + ; When DrawRout returns: + ; - ECX will contain # >pixels< overrun + ; - [WidthCount] will be decremented by # bytes drawn + ;.................................................................... +??draw_middle: + add ecx,[PixelWidth] ; since ECX could overrun, add width + jle ??draw_right ; if ECX<=0, no middle pixels to draw + call [DrawRout] ; draw the pixels + ; + ;................... skip past right-clipped pixels ................. + ; +??draw_right: + mov ecx,[WidthCount] ; ECX = remaining # bytes + jecxz ??next_line ; don't bother if no bytes remain + call [RSkipRout] ; skip right-clipped bytes + ; + ;----------------------- go to the next line ------------------------ + ; +??next_line: + ; + ;................. adjust EDI to start of next line ................. + ; + mov eax,[viewport_yadd] ; get yadd + add [StartDraw],eax ; add it to this line's position + mov edi,[StartDraw] ; EDI = next line + ; + ;................. decrement our pixel row counter .................. + ; + dec [PixelHeight] ; count down a line + jz ??exit ; we're done! + ; + ;.................. decrement YTotal's high byte .................... + ; + dec [BYTE PTR YTotal + 1] ; decrement high byte + jz ??draw_loop ; draw next line if 0 + ; + ;....................... re-draw this line .......................... + ; + mov esi,[LineStart] ; reset to this line's start + jmp ??draw_left ; redraw this line + +??exit: + ret + + ENDP Draw_Shape + + +;*************************************************************************** +;* Not_Supported -- Replacement function for Draw_Shape routines not used. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 08/24/1993 SKB : Created. * +;*=========================================================================* +PROC Not_Supported NOLANGUAGE NEAR + + ret + + ENDP Not_Supported + + END + +;************************** End of drawshp.asm ***************************** + + diff --git a/WWFLAT32/SHAPE/DS_DN.ASM b/WWFLAT32/SHAPE/DS_DN.ASM new file mode 100644 index 0000000..b0a8ac5 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_DN.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DN.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Normal -- Draws a normal row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Normal NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - add it to EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + inc edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp short ??test_fading + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + add edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Normal + + END + +;**************************** End of ds_dn.asm ***************************** diff --git a/WWFLAT32/SHAPE/DS_DR.ASM b/WWFLAT32/SHAPE/DS_DR.ASM new file mode 100644 index 0000000..b46629c --- /dev/null +++ b/WWFLAT32/SHAPE/DS_DR.ASM @@ -0,0 +1,257 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Reverse -- Draws a reversed row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Reverse NOLANGUAGE NEAR + + mov [StashEDX],edx ; save edx + mov edx,[Flags] + mov eax,0 ; init to zero + sub [WidthCount],ecx ; decrement bytes remaining by pixels + ; to draw + + ;-------------------------------------------------------------------- + ; Drawing Loop: + ; - Get a data byte + ; - If it's a 0, handle the run: + ; - get repetition value + ; - subtract it from EDI + ; - subtract it from [WidthCount] + ; - subtract it from ECX + ; - if ECX>0, draw again, else exit + ; - Otherwise: + ; - draw the pixel + ; - increment EDI to next pixel location + ; - decrement [WidthCount] + ; - loop until ECX is 0 + ;-------------------------------------------------------------------- + test edx,SHAPE_EFFECTS ; are any effects flags set? + jnz short ??general_draw_continue ; if so use the general purpose loop + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz short ??fast_is_run ; if yes then handle the run + mov [edi],al ; store color value to viewport + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jnz short ??fast_draw_loop ; if not then go home + jmp ??out + +??fast_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg short ??fast_draw_loop ; while more to do, loop back up + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- +??general_draw_loop: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + dec edi ; point to next viewport pixel + dec ecx ; any source pixels left? + jz ??out ; if not then go home + +??general_draw_continue: + mov al,[esi] ; get a byte of the source + inc esi + or eax,eax ; is the byte a transparent run? + jz ??general_is_run ; if yes then handle the run + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jz short ??test_fading + + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js short ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + jmp short ??general_draw_loop + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp ??general_draw_loop + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp short ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp ??general_draw_loop + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??general_draw_loop + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??general_draw_loop + +??general_is_run: + mov al,[esi] + inc esi + sub edi,eax ; move the viewport pointer + sub ecx,eax ; chop down the width to do + jg ??general_draw_continue ; while more to do, loop back up + +??out: + add [WidthCount],ecx ; adjust for source ending in a run + mov edx,[StashEDX] + ret + + ENDP Draw_Reverse + + + END + +;**************************** End of ds_dr.asm ***************************** diff --git a/WWFLAT32/SHAPE/DS_DS.ASM b/WWFLAT32/SHAPE/DS_DS.ASM new file mode 100644 index 0000000..a8d312f --- /dev/null +++ b/WWFLAT32/SHAPE/DS_DS.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Draw_Scale -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit. * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + inc edi ; increment the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + add edi,eax ; add to EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + ENDP Draw_Scale + + END + +;**************************** End of ds_ds.asm ****************************** diff --git a/WWFLAT32/SHAPE/DS_DSR.ASM b/WWFLAT32/SHAPE/DS_DSR.ASM new file mode 100644 index 0000000..fcd6180 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_DSR.ASM @@ -0,0 +1,341 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_DSR.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*******p******************************************************************** +;* Draw_Scale_Reverse -- Draws a scaled row of pixels to the viewport * +;* * +;* INPUT: * +;* ECX = number of pixels (not bytes) to draw * +;* EDX = XTotal initializer value * +;* ESI = shape (source) buffer address * +;* EDI = viewport (destination) address * +;* [WidthCount] = remaining bytes on the line * +;* * +;* OUTPUT: * +;* ESI - updated to current location in the shape data * +;* EDI - incr/decr by # pixels (not bytes) drawn/skipped * +;* [WidthCount] - bytes remaining on the line * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit. * +;* 08/09/1994 IML : Optimized for 32-bit * +;* 09/06/1994 IML : Integrated p_* and ds_* routines. * +;*=========================================================================* +PROC Draw_Scale_Reverse NOLANGUAGE NEAR + + mov eax,0 ; init to 0 + test [Flags],SHAPE_EFFECTS + jnz short ??general_draw_continue + jmp short ??fast_draw_continue + + + ;-------------------------------------------------------------------- + ; Extra fast draw loop for shapes with no flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??fast_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz short ??fast_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??fast_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??fast_draw_loop + + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- + mov [edi],al ; store color value to viewport + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz short ??fast_draw_continue + jmp ??out ; get the heck outta here + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??fast_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg short ??fast_draw_loop ; if more to draw, process new byte + jmp ??out + + + ;-------------------------------------------------------------------- + ; General purpose draw loop for shapes with one or more flags set. + ;-------------------------------------------------------------------- + + ;-------------------------------------------------------------------- + ; Load a new byte: + ; - read the byte into AL + ; - if it's a run, deal with it + ; - otherwise, + ; - decrement [WidthCount] + ; - update EDX with [ScaleX] + ; - see if it's drawable (upon proc entry, it won't be) + ; - yes: draw a pixel + ; - no : load a new byte + ;-------------------------------------------------------------------- +??general_draw_loop: + mov al,[esi] ; get the next pixel from the source + inc esi + or eax,eax + jz ??general_is_run ; deal with a run + dec [WidthCount] ; count down # bytes processed + add edx,[ScaleX] ; add in the scale value + +??general_draw_continue: + or dh,dh ; are there any pixels to draw? + jz short ??general_draw_loop + + ;-------------------------------------------------------------------- + ; Draw one pixel: + ; - draw the pixel + ; - increment destination pointer + ; - decrement high byte of EDX (X-scale accumulator) + ; - loop (while ECX>0) to see if it's drawable + ;-------------------------------------------------------------------- +??draw: + mov [StashReg],eax ; save eax + mov [StashEDX],edx ; save edx + mov edx,[Flags] + +??test_priority: + test edx,SHAPE_PRIORITY + jnz short ??priority + +??test_predator: + test edx,SHAPE_PREDATOR + jnz short ??predator + +??test_compact: + test edx,SHAPE_COMPACT + jnz ??compact + +??test_shadow: + test edx,SHAPE_SHADOW + jnz ??shadow + +??test_translucency: + test edx,SHAPE_GHOST + jnz ??translucency + +??test_fading: + test edx,SHAPE_FADING + jnz ??fading + +??test_transparency: + test edx,SHAPE_FADING ; if fading is enabled test for + jz short ??no_fading_draw_loop ; transparency + or eax,eax + jz short ??is_transparent + +??no_fading_draw_loop: + mov [edi],al ; store color value to viewport + +??is_transparent: + mov eax,[StashReg] ; restore eax + mov edx,[StashEDX] ; restore edx + dec edi ; decrement the destination index + dec dh ; decrement the pixels to write + dec ecx + jnz ??general_draw_continue + jmp ??out ; get the heck outta here + +??priority: + mov ebx,[MaskAdjust] ; get mask page offset + mov bl,[BYTE PTR ebx + edi] ; get mask value + + and bl,CLEAR_UNUSED_BITS ; clear unused bits + + cmp [PriLevel],bl ; are we in front of + jge short ??test_predator ; background? + + mov ebx,[BackAdjust] ; get background page offset + mov al,[BYTE PTR ebx + edi] ; get background pixel + jmp short ??test_transparency + +??predator: + mov ebx,[PartialCount] + add ebx,[PartialPred] + or bh,bh + jnz short ??draw_pred ; is this a predator pixel? + mov [PartialCount],ebx + jmp ??test_compact + +??draw_pred: + xor bh,bh + mov [PartialCount],ebx + mov ebx,[PredValue] ; pick up a color offset a pseudo- + ; random amount from the current + mov al,[edi + ebx] ; viewport address + jmp short ??test_transparency + +??compact: + mov ebx,[ColorTable] ; get the address of the color table + mov al,[BYTE PTR ebx + eax] ; convert it into the proper byte + jmp ??test_shadow + +??shadow: + cmp al,SHADOW_COL + jne ??test_translucency ; is the table value a magic number? + + mov al,[edi] ; get the destination color and + mov ebx,[ShadowingTable] ; index into the shadow table + mov al,[BYTE PTR ebx + eax] + jmp ??test_transparency + +??fading: + mov [StashECX],ecx ; preserve ecx for later + mov ebx,[FadingTable] ; run color through fading table + mov ecx,[FadingNum] + +??fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz short ??fade_loop + + mov ecx,[StashECX] ; restore ecx for main draw loop + jmp ??test_transparency + +??translucency: + mov ebx,[IsTranslucent] ; is it a translucent color? + mov bh,[BYTE PTR ebx + eax] + or bh,bh + js ??test_fading + + and ebx,0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al,[edi] ; mov pixel at destination to al + add ebx,[Translucent] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al,[BYTE PTR ebx + eax] ; get new pixel in al + jmp ??test_fading + + + ;-------------------------------------------------------------------- + ; Handle a run: + ; - Get the run repetition value + ; - subract it from [WidthCount] + ; - multiply it by [ScaleX] + ; - put high bytes from mul into EAX, low byte into DL (roundoff bits) + ; - add high bytes (# pixels) to EDI + ; - subtract them from ECX + ; - clear EAX + ; - if ECX>0, go get next byte + ;-------------------------------------------------------------------- +??general_is_run: + mov al,[esi] ; get number of repeated values + inc esi + sub [WidthCount],eax ; adjust the remaining byte width + mov ebx,edx ; preserve dx for the multiply + mul [ScaleX] ; EDX:EAX = # pixels + roundoff bits + add eax,ebx ; add in the current x-total + mov edx,eax ; (assume EDX is empty) + shr eax,8 ; EAX = # pixels skipped + and edx,00FFh ; keep only low byte + sub edi,eax ; sub from EDI + sub ecx,eax ; subtract it from ECX + mov eax,0 ; clear EAX + or ecx,ecx + jg ??general_draw_loop ; if more to draw, process new byte + +??out: + ret ; lets get out of here + + + ENDP Draw_Scale_Reverse + + END + +;*************************** End of ds_dsr.asm ****************************** diff --git a/WWFLAT32/SHAPE/DS_LRS.ASM b/WWFLAT32/SHAPE/DS_LRS.ASM new file mode 100644 index 0000000..a4d85e4 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_LRS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + add edi,ecx ; decrement EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Reverse_Skip + + END + +;*************************** End of ds_lrs.asm ***************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_LS.ASM b/WWFLAT32/SHAPE/DS_LS.ASM new file mode 100644 index 0000000..cbc2ae6 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_LS.ASM @@ -0,0 +1,118 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels overrun, or 0 * +;* EDX - XTotal initializer value (0 since there's no scaling)* +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + sub edi,ecx ; increment EDI by overrun pixels + add [WidthCount],ecx ; adjust by # bytes overrun + mov edx,0 ; no scaling, so clear EDX + ret ; return back to the real function + + ENDP Left_Skip + + END + +;**************************** End of ds_ls.asm ***************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_LSRS.ASM b/WWFLAT32/SHAPE/DS_LSRS.ASM new file mode 100644 index 0000000..77effd1 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_LSRS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - decremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Reverse_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + sub esi,eax ; decrement ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Reverse_Skip + + END + +;**************************** End of ds_lsrs.asm **************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_LSS.ASM b/WWFLAT32/SHAPE/DS_LSS.ASM new file mode 100644 index 0000000..bf8221e --- /dev/null +++ b/WWFLAT32/SHAPE/DS_LSS.ASM @@ -0,0 +1,159 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_LSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 2, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Left_Scale_Skip -- Skips past a scaled row of pixels on left side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape (source) buffer data address * +;* EDI = viewport (destination) address * +;* [WidthCount] = shape's width * +;* * +;* OUTPUT: * +;* ECX - negative # pixels (not bytes) overrun, or 0 * +;* EDX - XTotal initializer value * +;* ESI - updated to the current location in the shape data * +;* EDI - incremented by # pixels (not bytes) overrun * +;* [WidthCount] - decremented by # bytes skipped * +;* * +;* The value returned in EDX reflects what XTotal's accumulated value * +;* should be at the new pixel location. If no bytes are overrun, this * +;* will be whatever is stored in [XTotalInit] (which will be 0 if no * +;* pixels are left-clipped). * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/08/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/02/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Left_Scale_Skip NOLANGUAGE NEAR + + sub [WidthCount],ecx ; we process ECX bytes of real width + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jcxz ??getrem ; exit if no bytes to skip + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz short ??on_run ; if it is a run then deal with it + + ;-------------------------------------------------------------------- + ; Default exit point: store default x-scale bits & exit + ;-------------------------------------------------------------------- +??getrem: + mov edx,[XTotalInit] ; store out the remainder + jmp short ??out ; we're done, get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + jz ??getrem ; exactly enough bytes; exit + + ;-------------------------------------------------------------------- + ; Overrun exit point: ECX is negative by the # of bytes overrun. + ; - adjust [WidthCount] by # of overrun bytes + ; - compute the remainder at the new location (EDX) + ; - compute the number of destination pixels to skip (ECX) + ; - adjust EDI by # of overrun bytes + ;-------------------------------------------------------------------- + ; + ;............... adjust [WidthCount] by overrun bytes ............... + ; + add [WidthCount],ecx ; adjust overrun in bytes + ; + ;................. put x-scale roundoff bits in EDX ................. + ; + mov eax,ecx ; get the number of bytes we overran + neg eax ; negate it since overun is negative + add eax,[LeftClipBytes] ; add the number of bytes we leftclip + mul [ScaleX] ; convert to pixels plus roundoff bits + mov edx,0 ; clear EDX + mov dl,al ; DL = x-scaling roundoff bits + ; + ;................ put negative overrun pixels in ECX ................ + ; + shr eax,8 ; EAX = total # left pixels + sub eax,[LeftClipPixels] ; EAX = # pixels overrun + mov ecx,eax ; store # overrun pixels + neg ecx ; make it negative + ; + ;................ adjust dest ptr by overrun pixels ................. + ; + add esi,eax ; increment ESI (EDI) by overrun pixels + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Left_Scale_Skip + + END + +;**************************** End of ds_lss.asm ***************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_RRS.ASM b/WWFLAT32/SHAPE/DS_RRS.ASM new file mode 100644 index 0000000..c90e468 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_RRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Reverse_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Reverse_Skip + + END + +;**************************** End of ds_rrs.asm **************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_RS.ASM b/WWFLAT32/SHAPE/DS_RS.ASM new file mode 100644 index 0000000..caec534 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_RS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : May 28, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Skip -- Skips bytes in a data stream * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Skip -- Skips bytes in a data stream * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/14/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 05/28/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Skip + + END + +;**************************** End of ds_rs.asm ****************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_RSRS.ASM b/WWFLAT32/SHAPE/DS_RSRS.ASM new file mode 100644 index 0000000..3315082 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_RSRS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSRS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Reverse_Skip -- Skips past a scaled row of pixels * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Reverse_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Reverse_Skip + + END + +;*************************** End of ds_rsrs.asm **************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_RSS.ASM b/WWFLAT32/SHAPE/DS_RSS.ASM new file mode 100644 index 0000000..798e207 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_RSS.ASM @@ -0,0 +1,110 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_RSS.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 24, 1993 * +;* * +;* Last Update : June 1, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Right_Scale_Skip -- Skips past a scaled row of pixels on right side * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;********************************* Code ************************************ + CODESEG + +;*************************************************************************** +;* Right_Scale_Skip -- Skips past a scaled row of pixels on the right side * +;* * +;* INPUT: * +;* ECX = number of uncompressed bytes to skip * +;* ESI = shape buffer data address * +;* * +;* OUTPUT: * +;* ESI - updated to the current location in the shape data * +;* * +;* WARNINGS: This routine may overrun the number of requested bytes * +;* if it encounters a run of 0's; however, it's assumed that * +;* the shape data will never contain a run that goes past the * +;* right-hand edge of the shape. * +;* * +;* HISTORY: * +;* 04/20/1992 PWG : Created. * +;* 08/19/1993 SKB : Split drawshp.asm into several modules. * +;* 06/01/1994 BR : Converted to 32-bit * +;*=========================================================================* +PROC Right_Scale_Skip NOLANGUAGE NEAR + + ;-------------------------------------------------------------------- + ; Put shape data address in EDI so we can do a scasb on it + ;-------------------------------------------------------------------- + xchg esi,edi ; xchange ESI and EDI + jecxz ??out ; exit if ECX is 0 (no bytes to skip) + + ;-------------------------------------------------------------------- + ; Search through the string and count down the info we have handled. + ; If we find a run (0 followed by a count byte), then handle it. + ;-------------------------------------------------------------------- +??cliptop: + mov eax,0 ; set al to 0 (we're scanning for 0) + repne scasb ; scan through source data + jz ??on_run ; if it is a run then deal with it + jecxz ??out ; if we're done then get outta here + + ;-------------------------------------------------------------------- + ; If we have a run then get the next byte which is the length. + ;-------------------------------------------------------------------- +??on_run: + mov al,[BYTE PTR edi] ; get the count of zeros to run + inc edi ; advance past the count + inc ecx ; the 0 found doesn't count + sub ecx,eax ; subtract the count from remaining + jg ??cliptop ; if more bytes left, scan again + + ;-------------------------------------------------------------------- + ; Put shape address back into ESI, adjust EDI + ;-------------------------------------------------------------------- +??out: + xchg esi,edi ; xchange ESI and EDI + ret ; return back to the real function + + ENDP Right_Scale_Skip + + END + +;*************************** End of ds_rss.asm ***************************** + \ No newline at end of file diff --git a/WWFLAT32/SHAPE/DS_TABLE.ASM b/WWFLAT32/SHAPE/DS_TABLE.ASM new file mode 100644 index 0000000..a351aa0 --- /dev/null +++ b/WWFLAT32/SHAPE/DS_TABLE.ASM @@ -0,0 +1,187 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Draw Shape Routines for library. * +;* * +;* File Name : DS_TABLE.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : September 6, 1994 [IML] * +;* * +;* This module sets up a table of procedure addresses for combinations of * +;* NORMAL, HORZ_REV and SCALING flags. * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + + +;******************************** Equates ********************************** +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Draw_Shape. * +;*=========================================================================*/ +USE_NORMAL EQU TRUE +USE_HORZ_REV EQU TRUE +USE_VERT_REV EQU TRUE +USE_SCALING EQU TRUE + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IFNB + GLOBAL varname:DWORD + ENDIF + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + + +;--------------------------------------------------------------------------- +; Data Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; LSkipTable +; RSkipTable +; DrawTable +;--------------------------------------------------------------------------- + + DATASEG + +;--------------------------------------------------------------------------- + +WANT equ +USE Left_Skip, LSkipTable + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Skip + +WANT equ +USE Left_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip + +WANT equ +USE Left_Scale_Skip + +WANT equ +USE Left_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Right_Skip, RSkipTable + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Skip + +WANT equ +USE Right_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip + +WANT equ +USE Right_Scale_Skip + +WANT equ +USE Right_Scale_Reverse_Skip +;--------------------------------------------------------------------------- + +WANT equ +USE Draw_Normal, DrawTable + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Normal + +WANT equ +USE Draw_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse + +WANT equ +USE Draw_Scale + +WANT equ +USE Draw_Scale_Reverse +;--------------------------------------------------------------------------- + +;--------------------------------------------------------------------------- + END + +;************************** End of ds_table.asm **************************** diff --git a/WWFLAT32/SHAPE/GETSHAPE.CPP b/WWFLAT32/SHAPE/GETSHAPE.CPP new file mode 100644 index 0000000..bcb4ba3 --- /dev/null +++ b/WWFLAT32/SHAPE/GETSHAPE.CPP @@ -0,0 +1,362 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ + diff --git a/WWFLAT32/SHAPE/MAKEFILE b/WWFLAT32/SHAPE/MAKEFILE new file mode 100644 index 0000000..a006c3a --- /dev/null +++ b/WWFLAT32/SHAPE/MAKEFILE @@ -0,0 +1,195 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 25, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = shape +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + getshape.obj & + prioinit.obj & + drawshp.obj & + ds_dn.obj & + ds_dr.obj & + ds_ds.obj & + ds_dsr.obj & + ds_lrs.obj & + ds_ls.obj & + ds_lsrs.obj & + ds_lss.obj & + ds_rrs.obj & + ds_rs.obj & + ds_rsrs.obj & + ds_rss.obj & + ds_table.obj & + setshape.obj + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/SHAPE/PRIOINIT.CPP b/WWFLAT32/SHAPE/PRIOINIT.CPP new file mode 100644 index 0000000..d69706c --- /dev/null +++ b/WWFLAT32/SHAPE/PRIOINIT.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : PRIOINIT.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : August 9, 1993 * + * * + * Last Update : August 9, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * INIT_PRIORITY_SYSTEM -- Sets the buffer addresses for the priority * + * system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "shape.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Init_Priority_System -- set the buffer addresses for the priority * + * system. * + * * + * INPUT: * + * mask - pointer to priority buffer class * + * back - pointer to background buffer class * + * * + * OUTPUT: * + * none * + * * + * HISTORY: * + * 08/09/1994 IML : Created. * + *=========================================================================*/ + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back) +{ + MaskPage = mask->Get_Buffer(); + BackGroundPage = back->Get_Buffer(); +} + + +/************************** end of prioinit.cpp ****************************/ diff --git a/WWFLAT32/SHAPE/SETSHAPE.ASM b/WWFLAT32/SHAPE/SETSHAPE.ASM new file mode 100644 index 0000000..52cf574 --- /dev/null +++ b/WWFLAT32/SHAPE/SETSHAPE.ASM @@ -0,0 +1,81 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SETSHAPE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : October 26, 1994 * +;* * +;* Last Update : October 26, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Shape_Buffer -- Sets the shape buffer to the given pointer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +IDEAL +P386 +MODEL USE32 FLAT + + +;******************************** Includes ********************************* +INCLUDE "shape.inc" + +CODESEG + +;*************************************************************************** +;* SET_SHAPE_BUFFER -- Sets the shape buffer to the given pointer * +;* * +;* This routine will set the shape buffer to the given value and make sure * +;* that the system does not try to compress any shapes that will be larger * +;* than the shape buffer. * +;* * +;* INPUT: void * - pointer to the shape buffer * +;* int - size of the buffer which has been passed in * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID *Set_Shape_Bufer(void *buffer, int size); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Set_Shape_Buffer:NEAR + +PROC Set_Shape_Buffer C near + USES eax + + ARG buff:DWORD + ARG size:DWORD + + mov eax,[size] + mov [_ShapeBufferSize],eax + + mov eax,[buff] + mov [_ShapeBuffer],eax + ret + + ENDP Set_Shape_Buffer +END + diff --git a/WWFLAT32/SHAPE/SHAPE.H b/WWFLAT32/SHAPE/SHAPE.H new file mode 100644 index 0000000..daadf3c --- /dev/null +++ b/WWFLAT32/SHAPE/SHAPE.H @@ -0,0 +1,167 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_FADING = 0x0100, // Fading effect (VOID * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (VOID * color_table) +} ShapeFlags_Type; + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + UWORD ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + UBYTE Height; // Height of the shape in scan lines + UWORD Width; // Width of the shape in bytes + UBYTE OriginalHeight; // Original height of shape in scan lines + UWORD ShapeSize; // Size of the shape, including header + UWORD DataLength; // Size of the uncompressed shape (just data) + UBYTE Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +typedef struct { + UWORD NumShapes; // number of shapes in the block + LONG Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern VOID *MaskPage; +extern VOID *BackGroundPage; +extern LONG _ShapeBufferSize; +extern BYTE *_ShapeBuffer; +} + + +VOID cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +WORD Draw_Shape(GraphicViewPortClass *gvp, VOID const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short cdecl Get_Shape_Data(VOID const *shape, WORD data); +int cdecl Extract_Shape_Count(VOID const *buffer); +void * cdecl Extract_Shape(VOID const *buffer, int shape); +int cdecl Restore_Shape_Height(VOID *shape); +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight); + +extern "C" { +int Get_Shape_Width(VOID const *shape); +int Get_Shape_Height(VOID const *shape); +int Get_Shape_Original_Height(VOID const *shape); +int Get_Shape_Uncomp_Size(VOID const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +VOID Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +WORD cdecl Get_Shape_Flags(VOID const *shape); +int cdecl Get_Shape_Size(VOID const *shape); +int cdecl Get_Shape_Scaled_Width(VOID const *shape, WORD scale); +int cdecl Get_Shape_Scaled_Height(VOID const *shape, WORD scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + diff --git a/WWFLAT32/SHAPE/SHAPE.INC b/WWFLAT32/SHAPE/SHAPE.INC new file mode 100644 index 0000000..e6a4042 --- /dev/null +++ b/WWFLAT32/SHAPE/SHAPE.INC @@ -0,0 +1,214 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL _ShapeBuffer:DWORD +GLOBAL _ShapeBufferSize:DWORD +GLOBAL _MaskPage:DWORD +GLOBAL _BackGroundPage:DWORD +GLOBAL PredCount:DWORD +GLOBAL PredTable:BYTE +GLOBAL PredValue:DWORD +GLOBAL PartialPred:DWORD +GLOBAL PartialCount:DWORD +GLOBAL Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/WWFLAT32/SRCDEBUG/ALLOC.CPP b/WWFLAT32/SRCDEBUG/ALLOC.CPP new file mode 100644 index 0000000..613043e --- /dev/null +++ b/WWFLAT32/SRCDEBUG/ALLOC.CPP @@ -0,0 +1,590 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 16*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + diff --git a/WWFLAT32/SRCDEBUG/KEYBOARD.ASM b/WWFLAT32/SRCDEBUG/KEYBOARD.ASM new file mode 100644 index 0000000..a51e098 --- /dev/null +++ b/WWFLAT32/SRCDEBUG/KEYBOARD.ASM @@ -0,0 +1,2568 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYBOARD.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Keyboard_Size -- return size of real mode timer code. * +;* Check_Key -- checks queue for key (make) * +;* Check_Key_Num -- Checks if key in queue, return key num * +;* Get_Key_Num -- Returns the next key num in ax * +;* KN_To_KA -- Translates a key num to an ASCII key * +;* Low_Get_Key -- low level get key returns key num and bits * +;* Convert_Num_To_ASCII -- Assembly routine converts keynum to ASCII key * +;* KeyNum_Translate -- Performs a lowlevel xlate to a keycode * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* +;* Keyboard driver -- 80386 Protected Mode Assembly portion * +;* updated by: Phil Gorrow for 32 bit Protected Mode +;*************************************************************************** +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +PROT_INT_ENABLE EQU 1 ; if false protected int just calls real mode int + +DPMI_INTR EQU 31h +IRQ1INTNUM EQU 09h ; IRQ1 interrupt vector number. +BRKINTNUM EQU 23h ; Crtl-C (Break) interrupt vector number +DBGINTNUM EQU 3h ; Debug interrupt vector number +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT +CLEARISR EQU 020H ; value to clear In Service Register +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- + INCLUDE "keyboard.inc" + INCLUDE "keystruc.inc" + INCLUDE "mcgaprim.inc" + + GLOBAL RealModePtr:DWORD + + GLOBAL Install_Keyboard_Interrupt:NEAR + GLOBAL Get_RM_Keyboard_Address:Near + GLOBAL Get_RM_Keyboard_Size:Near + GLOBAL Remove_Keyboard_Interrupt:NEAR + GLOBAL Check_Key_Num:NEAR + GLOBAL Get_Key_Num:NEAR + GLOBAL KN_To_KA:NEAR + GLOBAL KeyNum_Translate:NEAR + GLOBAL Check_Key:NEAR + GLOBAL Get_Key:NEAR + GLOBAL Keyboard_Attributes_On:NEAR + GLOBAL Clear_KeyBuffer:NEAR + GLOBAL Key_Down:NEAR + GLOBAL Keyboard_Attributes_Off:NEAR + GLOBAL Check_Key_Bits:NEAR + GLOBAL Get_Key_Bits:NEAR + GLOBAL Key_Satisfied:NEAR + GLOBAL Stuff_Key_WORD:NEAR + GLOBAL Stuff_Key_Num:NEAR + GLOBAL MouseQX:DWORD + GLOBAL MouseQY:DWORD +;DBG + GLOBAL Keyboard_Interrupt:NEAR + + + DATASEG +; For the current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "keyireal.ibn" +LABEL RealBinEnd BYTE + +LABEL LockedDataStart BYTE +RMVector DD 0 +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. + +Keyboard_App_Stack_ESP DD 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS DD 0 ; This the System Stack Selector +Keyboard_StackPointer DD 0DEADDEADh ; We Create a Local Application +Keyboard_Stack DD 512 dup (0) +Keyboard_StackStart DD 0 + ; StackPointer +LABEL LockedDataEnd BYTE + + + +Ascii DB 0,"`1234567890-=",0,8,9,"qwertyuiop[]\",0,"asdfghjkl;'" + DB 0,13,0,45,"zxcvbnm,./",0,0,0,0,0," " + +Shift DB 0,"~!@#$%^&*()_+",0,8,9,"QWERTYUIOP{}|",0,"ASDFGHJKL:",22H + DB 0,13,0,45,"ZXCVBNM<>?",0,0,0,0,0," " + +Alpha_Lower DB 0 + DB "~!@#$%^&*()_+",0,8,9,"qwertyuiop{}|",0,"asdfghjkl:",22H + DB 0,13,0,45,"zxcvbnm<>?",0,0,0,0,0," " + +Alpha_Shift DB 0 + DB "`1234567890-=",0,8,9,"QWERTYUIOP[]\",0,"ASDFGHJKL;'" + DB 0,13,0,45,"ZXCVBNM,./",0,0,0,0,0," " + +Edit DB 0AEH,0ADH,000H,000H,0B5H,0B9H,0B1H,000H,0B8H,0B0H,0B7H,0AFH + DB 000H,000H,0B3H,000H,0B9H,0B5H,0B1H,000H, "/",0B8H,0B4H,0B0H + DB 0AEH, "*",0B7H,0B3H,0AFH,0ADH, "-", "+",000H,00DH,000H + +NumPad DB 0,"741",0,"/8520*963.-+",0,13,0 + + +GetKeyLock DW 0 ; snap shot of num and caps lock bits +InitFlags DW 0 +MouseQX DD 0 +MouseQY DD 0 + + CODESEG +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: VOID * to the address of the real mode timer * +;* * +;* PROTO: VOID *Get_RM_Keyboard_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_KEYBOARD_SIZE -- return size of real mode timer code. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: LONG size of the real mode timer code * +;* * +;* PROTO: LONG Get_RM_Keyboard_Size(VOID); +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Keyboard_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP +;*************************************************************************** +;* INSTALL_KEYBOARD_INTERRUPT -- Installs the keyboard interrupt * +;* * +;* INPUT: int rm_ptr - ptr to the real mode handler * * +;* int rm_size - size of the real mode handler * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Keyboard_Interrupt(int rm_ptr, int rm_size); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Install_Keyboard_Interrupt C NEAR + USES eax,ebx,ecx,edx,esi,edi + ARG rm_ptr:DWORD + ARG rm_size:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + mov [RealModeSize],ebx + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + or [InitFlags],IF_ALLOC_RM ; set successful + mov [RealModeSel],edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edi will have size of region in bytes. + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr]; ; set up our dest pointer + mov esi,[rm_ptr] ; Set up our source pointer. + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi + shl eax,12 ; make seg in high eax. + mov ax,[(KeyboardType PTR esi).CallKeyRMIntOffset] ; create RM addr of call chain. + mov [(KeyboardType PTR esi).CallKeyRMIntAddr],eax ; save it for use in PM int. + +IF NOT PROT_INT_ENABLE + ; Chain the Real Keyboard interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov edi , eax + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + movzx ebx , bl + mov [ byte ptr RMVector ] , bl + mov [ 4 * ebx ] , edi +ENDIF + + + + ;------------------------------------------------------- + ; Initialize all of the keyboard specific information + ;------------------------------------------------------- + xor eax,eax ; clear the high bits of eax + mov al,[417H] ; get keyboard status flags + test eax,040H ; caps lock active? + je short ??nocap ; not active + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK + +??nocap: + test eax,020H ; num lock active? + je short ??nonumlock ; not active + or [(KeyboardType PTR esi).KeyLock],NUMLOCK + +??nonumlock: + test eax,002H ; is left shift key down? + je short ??noleftshift ; try the right + or [(KeyboardType PTR esi+5).KeysUpDown],010H + +??noleftshift: + and eax,001H ; get right shift bit + shl eax,9 ; put it into the proper position (shl al,1 mov ah,al) + mov al,[418H] ; get alt and ctrl bits + shl al,2 ; put in proper position + shl al,1 + and al,00CH ; only alt and ctrl bits + or ah,al ; put them ah for Keys+7 later + mov al,[496H] ; get extended keys + test al,008H ; check for right alt key + je short ??noralt + or ah,040H +??noralt: + mov [(KeyboardType PTR esi+7).KeysUpDown],ah + test al,004H ; test for right ctrl + je short ??norctrl + or [(KeyboardType PTR esi+8).KeysUpDown],001h +??norctrl: + test al,002H ; last code E0? + je short ??noe0 + mov [(KeyboardType PTR esi).LastKeyE0],001h +??noe0: + test al,001H ; last code E1? + je short ??noe1 + mov [(KeyboardType PTR esi).LastKeyE1],002h +??noe1: + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).KeyOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).KeyOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ1INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).KeyOldRMI],ecx + + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov cx , cs + lea edx, [Keyboard_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).KeyCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Get the protected mode interrupt vector - for the break interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).BrkOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).BrkOldPMISelector],ecx ; save selector. + + + ;========================================================================== + ; Get the real mode interrupt vector - for the break interrupt + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, EBX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,BRKINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(KeyboardType PTR esi).BrkOldRMI],ecx + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov cx , cs + lea edx, [Break_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(KeyboardType PTR esi).BrkCodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + +IF DEBUG + ;========================================================================== + ; Get the protected mode interrupt vector - for the Debug interrupt + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ; ES:EBX = address of PM int handler routine. + ;========================================================================== + + mov eax,0204h ; Get proteced mode + mov bl,DBGINTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(KeyboardType PTR esi).DbgOldPMIOffset],edx ; save offset. + mov [(KeyboardType PTR esi).DbgOldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov cx , cs + lea edx, [Debug_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS +ENDIF + + ; we have finished with success. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. +??exit: + ret + ENDP Install_Keyboard_Interrupt + +;*************************************************************************** +;* REMOVE_INTERRUPT -- Removes keyboard interrupt and restores chain * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Interrupt(VOID) * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Remove_Keyboard_Interrupt C NEAR + USES ebx,ecx,edx + + ; verifie that the keyboard was previosly install + ; this is here in case of a page fault crash + mov esi,[RealModePtr] + test esi,esi + jz ??error + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + mov eax, 0201h + mov bl,IRQ1INTNUM + mov edx,[(KeyboardType esi).KeyOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ1INTNUM + mov ecx,[(KeyboardType esi).KeyOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).KeyOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + + ; disengage Control Break Interrupt handle + ;========================================================================== + ; Now it is time to set the real Interrupt + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,BRKINTNUM + mov edx,[(KeyboardType esi).BrkOldRMI] ; get address of real code int hand. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,BRKINTNUM + mov ecx,[(KeyboardType esi).BrkOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).BrkOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + + +IF DEBUG + + ; disengage Keyboard Interrupt handle + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,DBGINTNUM + mov ecx,[(KeyboardType esi).DbgOldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(KeyboardType esi).DbgOldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + + +ENDIF + +IF NOT PROT_INT_ENABLE + ; Clean up the Users interrupt table + mov eax , 201h + mov bl , [ byte ptr RMVector ] + xor ecx , ecx + xor edx , edx + int DPMI_INTR + jc ??error +ENDIF + + +??vectors_not_set: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + mov eax , 0601h + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[RealModeSize] ; edx will have size of region in bytes. + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock Code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + ENDP Remove_Keyboard_Interrupt + + +;*************************************************************************** +;* CHECK_KEY_NUM -- Checks if key in queue, return key num * +;* * +;* INPUT: none * +;* * +;* OUTPUT: Keynum of the key that was pressed, FALSE otherwise * +;* +;* PROTO: INT Check_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Num C NEAR + USES ebx,esi + pushf ; save off the flags + cli ; disable interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + xor eax,[(KeyboardType PTR esi).KeyBufferTail] + or eax,eax ; check to see if head == tail + jz short ??fini ; if so we are done + + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + mov ax,[(KeyboardType PTR esi+eax).KeyBuffer] ; get key num + +??fini: + sti + popf + ret + + ENDP Check_Key_Num + +;*************************************************************************** +;* GET_KEY_NUM -- Returns the next key num in ax * +;* * +;* INPUT: none * +;* * +;* OUTPUT: WORD key flags are in the high byte of return word, key * +;* num is in the low byte. +;* * +;* PROTO: WORD Get_Key_Num(VOID); +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Num C NEAR + USES esi,edi + + mov esi,[RealModePtr] ; Point to start of RM data. +??wait: + cli ; disable interrupts + mov eax,[(KeyboardType PTR esi).KeyBufferHead] ; get the head + cmp eax,[(KeyboardType PTR esi).KeyBufferTail] ; get the head + jne short ??getkey + sti ; enable interrupts + jmp ??wait + +??getkey: + call Low_Get_Key + sti ; enable interrupts + ret + + ENDP Get_Key_Num + +;*************************************************************************** +;* KN_TO_KA -- Translates a key num to an ASCII key * +;* * +;* INPUT: WORD the keynum to translate * +;* * +;* OUTPUT: WORD the ASCII key that is returned * +;* * +;* PROTO: INT KN_To_KA(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + + PROC KN_To_KA C NEAR + ARG keynum:DWORD + + mov eax,[keynum] + call near ptr Convert_Num_To_ASCII + + ret + + ENDP KN_To_KA + + +;*************************************************************************** +;* LOW_GET_KEY -- low level get key returns key num and bits * +;* * +;* INPUT: AX - index into the buffer * +;* * +;* OUTPUT: AX - key num with bits * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* HISTORY: * +;* 07/14/1994 PWG : Created. * +;*=========================================================================* + PROC Low_Get_Key C NEAR + USES ebx,esi,edi + + mov edi,eax ; save off value in ax + +; We should set up both DS & ES because we are a low level function +; and don't know who might have called us or what the registers +; currently are. + +; No reason to set DS & ES. +; This is not a hardware interrupt and if the funtion is being called +; from within a hardware interrupt then DS and ES will be preset to +; DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + + add edi,2 + and edi,0FFH ; 128 word circular buffer + + cmp al,KN_LMOUSE + jb short ??cont + cmp al,KN_RMOUSE + ja short ??chkjoy + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQX],eax + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov [MouseQY],eax + + pop eax ; restore keynum for return + + jmp short ??cont + +??chkjoy: + cmp al,KN_JBUTTON2 ; mouse button before joystick button + ja short ??cont + + push eax ; save off the keynum we got + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + mov ax,[(KeyboardType PTR esi+edi).KeyBuffer] ; get the head + add edi,2 + and edi,0FFH ; 128 word circular buffer + + pop eax ; restore keynum for return +??cont: + mov [(KeyboardType PTR esi).KeyBufferHead],edi ; set the head + +??out: + ret + + ENDP Low_Get_Key + +;*************************************************************************** +;* CONVERT_NUM_TO_ASCII -- Assembly routine converts keynum to ASCII key * +;* * +;* INPUT: EAX where: * +;* AH - holds the key num bits * +;* AL - holds the key num value * +;* * +;* OUTPUT: EAX where: * +;* AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: none - assembly callable routine only. * +;* * +;* WARNINGS: GetKeyLock must be set prior to calling this function * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Convert_Num_To_ASCII C NEAR + USES ebx,ecx,esi,edi + + ;*=================================================================== + ;* Force all breaks to be thrown out. + ;*=================================================================== + test eax,08000h ; If it is a button number + jne short ??button ; don't process it + + test ah,KEYRELEASE ; If it is not key release + je short ??ok ; then go process it + +??button: + xor eax,eax ; no ascii value for a button + ret + +??ok: + ;*=================================================================== + ;* ES points to the DOSMEM selector, esi is the offset of the + ;* protected mode structure. + ;*=================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*=================================================================== + ;* Start dealing with the keys. + ;*=================================================================== + cmp al,110 ; is it esc + je ??esc ; if so then deal with it + +;??chkext: + cmp al,62 ; is it extended? + jae short ??extended ; its extended so return ext code + + mov ebx,eax ; get an index + and ebx,03FH ; only 0-63 allowed + test ah,1 ; if not, test for shift + jnz short ??shifted ; if shifted get shift value + + ;*=================================================================== + ;* Here when we have an unshifted ascii key + ;*=================================================================== + mov al,[Ascii+ebx] ; get the ascii code for this number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_lowered: + mov al,[Alpha_Lower+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* CAPS key is on, forcing all alphabetic characters to lower case (all + ;* others are shifted) + ;*=================================================================== +??alpha_shifted: + mov al,[Alpha_Shift+ebx] ; get the s_ascii code for number + jmp short ??ctrlkey + + ;*=================================================================== + ;* Shift'ed character + ;*=================================================================== +??shifted: + mov al,[Shift+ebx] ; get ascii shift code for number + +??ctrlkey: + test ah,2 ; is it ctrl? + jz short ??jexit ; izf not skip ctrl check + + mov edi,ebx ; get index + and edi,7 ; only bits 0-7 + + mov cl,[(KeyboardType PTR esi+edi).Bits] + + shr ebx,3 ; div by 8 for byte offset + test [(KeyboardType PTR esi+ebx).KeysCapsLock],cl + je short ??jexit + and al,01FH ; force to ctrl value + +??jexit: + jmp short ??exit + +??extended: + cmp al,75 ; if less than insert + jb short ??special + cmp al,110 ; if >= esc + jae short ??funckey + +??editkeys: + xor ebx,ebx + mov bl,al + sub ebx,75 ; get value from 0-34 + test [(KeyboardType PTR esi).KeyFlags],NONUMLOCK + jne short ??no_numpad + test [GetKeyLock],NUMLOCK ; look at the snap shot of bits + jne short ??numpad + test ah,SHIFTPRESS + jne short ??numpad + +??no_numpad: + mov al,[Edit+ebx] ; get the ascii code for this number + jmp short ??exit + +??numpad: + sub ebx,15 ; adjust to numpad entries + mov al,[NumPad+ebx] ; get the ascii code for this number + jmp short ??exit + +??funckey: + cmp al,112 ; if less than function keys + jb short ??extout + cmp al,121 ; if greater than function keys 1-10 + ja short ??extout + mov bl,al + sub bl,112 ; get value 0-9 + mov bh,0C5H ; function key 1 no shift-ctrl-alt + test ah,7 ; any shift-ctrl-alt + je short ??funcadj + mov bh,098H ; function key 1 alt + test ah,ALTPRESS ; (highest prescendence) + jne short ??funcadj + mov bh,0A2H ; function key 1 ctrl (next highest) + test ah,CTRLPRESS + jne short ??funcadj + mov bh,0ACH ; function key 1 shift (lowest) + +??funcadj: + sub bh,bl ; adjust function key to a + mov al,bh ; shift/no shift etc + jmp short ??exit + +??special: + cmp al,65 ; if less than specials + jb short ??extout + + add al,133 ; make value between 198-207 or + jmp short ??exit ; 0C6H-0CFH + +??extout: + or al,080H + jmp short ??exit + +??esc: + mov al,01BH + +??exit: + ret + ENDP Convert_Num_To_ASCII + +;*************************************************************************** +;* CHECK_KEY -- checks for ASCII keys sitting in the keybuffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT the ASCII sequence for the key that was pressed * +;* * +;* PROTO: INT Check_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key C NEAR + USES ebx,edi,esi + + pushf + cld + mov ebx,[RealModePtr] ; Point to start of RM data. + +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + test ah,KEYRELEASE ; is this a break? + jne short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + cmp al,KN_LMOUSE ; check if it is a mouse or joystick + jb short ??contget + cmp al,KN_JBUTTON2 + ja short ??contget + add si,4 ; get rid of the x and y values + +??contget: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor eax,eax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + sti ; enable interrupts + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + call near ptr Convert_Num_To_ASCII + ; AX has ASCII code with bits + + xor ah,ah ; clear key bits +??exit: + popf + ret + ENDP Check_Key + + +;*************************************************************************** +;* GET_KEY -- Gets the next available ASCII keystroke. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: AH - hold the key bits * +;* AL - holds the ASCII key value * +;* * +;* PROTO: INT Get_Key(VOID); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key C NEAR + USES ebx,ecx,edi + + cld ; clear the direction flag for speed + + ; Check_Key had to be copied because of enable and disable of ints + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + test ah,KEYRELEASE ; is this a break? + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + ; AX has key num code with bits + + call near ptr Convert_Num_To_ASCII + + xor ah,ah + ret + + ENDP Get_Key + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_ON -- Sets the specified keyflags on * +;* * +;* INPUT: INT the keyflags that need to be turned on * +;* * +;* OUTPUT: INT the current keyflags that are on * +;* * +;* PROTO: INT Keyboard_Attributes_On(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_On C NEAR + USES esi,edi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + or [(KeyboardType PTR esi).KeyFlags],eax + + ; Only do this if in playback or record mode. + test eax,PASSBREAKS + je short ??fini + + xor eax,eax + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).KeysUpDown + mov [edi],eax + mov [edi+4],eax + mov [edi+8],eax + mov [edi+12],eax +??fini: + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + ENDP Keyboard_Attributes_On + +;*************************************************************************** +;* KEYBOARD_ATTRIBUTES_OFF -- Sets the specified keyflags off * +;* * +;* INPUT: INT the keyflags that need to be turned off * +;* * +;* OUTPUT: INT the current keyflags that are off * +;* * +;* PROTO: INT Keyboard_Attributes_Off(INT key_flags); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Attributes_Off C NEAR + USES esi + ARG bits:DWORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[bits] + not eax + and [(KeyboardType PTR esi).KeyFlags],eax + xor eax,eax + mov eax,[(KeyboardType PTR esi).KeyFlags] + ret + + ENDP Keyboard_Attributes_Off +;*************************************************************************** +;* CLEAR_KEYBUFFER -- Clears keystrokes out of the key buffer * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Clear_KeyBuffer(VOID); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Clear_KeyBuffer C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; Point to start of RM data. + cli + mov eax,[(KeyboardType PTR esi).KeyBufferHead] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + sti + + ret + + ENDP Clear_KeyBuffer + +;*************************************************************************** +;* KEY_DOWN -- tests the status of a keyboard key * +;* * +;* INPUT: INT the key num to check * +;* * +;* OUTPUT: INT zero if the key is up, none zero if the key is down * +;* * +;* PROTO: INT Key_Down(INT key); * +;* * +;* HISTORY: * +;* 07/19/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Down C NEAR + USES ebx,ecx,edi,esi + ARG key:DWORD + + push [key] + call KeyNum_Translate + add esp,4 + xor ah,ah ; Always ignore the control bits. + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edi,eax + shr edi,3 + mov cl,al + and cl,0111b + mov al,01b + shl al,cl + and al,[(KeyboardType PTR esi+edi).KeysUpDown] + ret + + ENDP Key_Down + +;*************************************************************************** +;* CHECK_KEY_BITS -- checks ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Check_Key_Bits C NEAR + USES ebx,ecx,edi,esi + + pushf ; save off the direction flag + cld ; we will go forward + + mov ebx,[RealModePtr] ; Point to start of RM data. +??clrloop: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + je short ??clrexit +??playback: + mov esi,eax + mov ax,[(KeyboardType PTR ebx+eax).KeyBuffer] ; get key num + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne short ??getinvalid + + ;------ If there is a NULL in the keyboard buffer, we must NOT treat it + ; as a valid ASCII value because the calling code will falsely + ; assume that the NULL return value indicates an empty buffer. + or al,al + je short ??getinvalid + + cmp al,122 ; is code a valid ascii key?? + jb short ??convkey + +??getinvalid: + add esi,2 ; get rid of invalid ascii key + and esi,0FFH ; 128 word circular buffer + mov [(KeyboardType PTR ebx).KeyBufferHead],esi + sti ; enable interrupts + jmp ??clrloop + +??clrexit: + xor ax,ax + +??convkey: + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + or eax,eax + je short ??exit ; exit if no key + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + +??exit: + popf + ret + + ENDP Check_Key_Bits +;*************************************************************************** +;* GET_KEY_BITS -- Gets ascii key in key buff with shift,ctrl,alt bits * +;* * +;* INPUT: none * +;* * +;* OUTPUT: INT 0 = no key in buffer, !0 = a key with the bits set * +;* * +;* PROTO: INT Check_Key_Bits(VOID); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Get_Key_Bits C NEAR + USES ebx,ecx,edi + + cld + + ; Check_Key_Bits was copied because of enable and disable of interrupts + ; with mod to get the key + + mov ebx,[RealModePtr] ; Point to start of RM data. + +??chkkey: + cli ; disable interrupts + mov eax,[(KeyboardType PTR ebx).KeyBufferHead] + cmp eax,[(KeyboardType PTR ebx).KeyBufferTail] + jne short ??getkey + sti ; enable interrupts + jmp ??chkkey + +??getkey: + ; AX has index into keybuffer + call near ptr Low_Get_Key + ; AX has key num code with bits + + mov cx,[(KeyboardType PTR ebx).KeyLock] + mov [GetKeyLock],cx ; save status for convert to ASCII + + sti ; enable interrupts + + mov edi,ebx + add edi,OFFSET (KeyboardType PTR 0).PassAlways + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd-OFFSET (KeyboardType PTR 0).PassAlways)+1 ; get number of pass always + repne scasb ; look for a match + or ecx,ecx ; see if there was a match + jne ??chkkey + + cmp al,122 ; is code a valid ascii key?? + jae ??chkkey + + ; AX has key num code with bits + mov ch,ah + and ch,KEYRELEASE ; keep only KEYRELEASE bit + and ah,NOTKEYRELEASE ; keep everything but KEYRELEASE bit + + call near ptr Convert_Num_To_ASCII + + ; AX has ASCII code with bits + or ah,ch ; replace KEYRELEASE bit + ret + + ENDP Get_Key_Bits + +;*************************************************************************** +;* KEY_SATISFIED -- checks to see if the given key is satisfied * +;* * +;* INPUT: INT the key flags/number to check * +;* * +;* OUTPUT: INT 0 if the key is not satisfied, !0 if the key is satisfied * +;* * +;* PROTO: VOID Key_Satisfied(INT key); * +;* * +;* HISTORY: * +;* 07/20/1994 PWG : Created. * +;*=========================================================================* + PROC Key_Satisfied C NEAR + USES ebx,ecx,esi,edi + ARG key:DWORD + + mov eax,[key] + mov ebx,eax ; save key code + push eax + call Key_Down ; see it its even down + add esp,4 + cmp eax,0 + je ??out ; if not it can't be satisfied + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,ebx ; if so, restore code + xor ah,ah ; clear out flags area + mov edi,eax ; set as an index + shr edi,3 ; div by 8 for bytes + mov ch,1h ; set a bit for mask + mov cl,al ; get the code number + and cl,7 ; get the actual bit this code is + shl ch,cl ; move bit into mask position + +; now test the ctrl,alt and shift bits + + test [(KeyboardType PTR esi+7).KeysUpDown],04h ; is the left ctrl down? + jne short ??ctrlon ; if so, ctrl is on + + test [(KeyboardType PTR esi+8).KeysUpDown],01h ; is the right ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl +??ctrlon: + or ah,02h ; or on the ctrl bit in flags +??ctrloff: + test [(KeyboardType PTR esi+7).KeysUpDown],50h ; is either alt key down? + je short ??altoff + or ah,04h ; or on the alt bit in flags +??altoff: + test [(KeyboardType PTR esi+5).KeysUpDown],10h ; is the left shift down? + jne short ??shifton ; if so the sift is on + test [(KeyboardType PTR esi+7).KeysUpDown],02h ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down +??shifton: + or ah,01h ; or on the shift bit in flags +??shiftoff: + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,1h ; toggle the shift flag +??capsoff: + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,1h ; toggle the shift flag if effected + +??numlockoff: + mov al,0ffh ; set to match by default + cmp ah,bh ; if flags match return !0 + je short ??out ; just exit + xor eax,eax ; otherwise, clear all bits FALSE + +??out: + or eax,eax + ret + + ENDP Key_Satisfied + + +;*************************************************************************** +;* Interrupt routines start here - Interrupts must be within the Locked +;* code area for DPMI compatability. +;*************************************************************************** + + + + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Performs a lowlevel xlate to a keycode * +;* * +;* INPUT: WORD the code that needs to be translated * +;* * +;* OUTPUT: WORD the translated code * +;* * +;* PROTO: INT KeyNum_Translate(INT keynum); * +;* * +;* HISTORY: * +;* 07/15/1994 PWG : Created. * +;*=========================================================================* + PROC KeyNum_Translate C NEAR + USES ebx,ecx,esi,edi + ARG keycode:DWORD + +;*=================================================================== +;* ES points to the DOSMEM selector, esi is the offset of the +;* protected mode structure. +;*=================================================================== + + mov esi,[RealModePtr] ; Point to start of RM data. + mov eax,[keycode] + test [(KeyboardType PTR esi).KeyFlags],TRACKEXT + jne short ??fini + + + mov ecx,OFFSET ((KeyboardType PTR 0).ExtRemap)- OFFSET((KeyboardType PTR 0).ExtNums) + mov edi,OFFSET (KeyboardType PTR 0).ExtNums + add edi,esi + + repne scasb + jcxz short ??fini ; No match found. + + mov edi,esi + add edi,OFFSET (KeyboardType PTR 0).ExtRemapEnd + dec edi + sub edi,ecx + mov al,[edi] +??fini: + ret + + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(INT code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_WORD C NEAR + USES ebx,esi,edi + ARG code:WORD + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov eax,[(KeyboardType PTR esi).KeyBufferTail] + mov edi,eax + add eax,2 + and eax,0FFh ; New KeyBufferTail value. + cmp [(KeyboardType PTR esi).KeyBufferHead],eax + je short ??noroom + + mov bx,[code] + mov [(KeyboardType PTR esi+edi).KeyBuffer],bx ; Record the keystroke. + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax + ret + +??noroom: + mov eax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(INT keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + PROC Stuff_Key_Num C NEAR + USES ebx,ecx,edx,edi,esi + ARG keycode:DWORD + LOCAL tail:DWORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + +; for the moment we do not check for the interrupt flag +; mov eax,2534h ; function to get the interrupt status +; int 21 ; eax = interrupt status +; push eax ; save the result on the stack + + pushf ; store off the flags + cli ; disable interrupts + +; We need to set the data segment because we might be being called +; from within an interrupt +; Soo, if that is the case then DS & ES point to DGROUP _DATA + + mov esi,[RealModePtr] ; Point to start of RM data. + + ; Record the mouse position to be stuffed into buffer. + mov eax,[(KeyboardType PTR esi).MouseX] + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov eax,[(KeyboardType PTR esi).MouseY] + mov [(KeyboardType PTR esi).LocalMouseY],ax + +??cando: + mov eax,[keycode] ; get the code + or eax,eax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [(KeyboardType PTR esi).KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [(KeyboardType PTR esi).KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [(KeyboardType PTR esi).Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [(KeyboardType PTR esi).Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [(KeyboardType PTR esi).Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + xor ebx,ebx + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl ebx,1 ; double for WORD index + mov ax,[WORD PTR ((KeyboardType PTR esi+ebx).XYAdjust)] + + movsx ebx,ah + movsx eax,al + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov edx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add edx,3 ; for fast speed +??normspeed: + add ebx,edx ; add speed for y index + mov bl,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for y delta + movsx ebx,bl + xchg ebx,edx ; save mouse y delta + add ebx,eax ; add speed for x index + mov al,[(KeyboardType PTR esi+ebx).KeyMouseMove] ; get speed for x delta + movsx eax,al + xchg ebx,edx ; restore mouse y delta + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and ebx,011b ; Y = 1, 0, 3 + and eax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and ebx,011b ; Y = 3, 0, 1 + and eax,011b ; X = 3, 0, 1 + shl ebx,2 + or ebx,eax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl ebx,1 + movzx ebx,[(KeyboardType PTR esi+ebx).EdgeConv] + shl ebx,2 + mov ax,[(KeyboardType PTR esi+ebx).ScreenEdge] ; New absolute X + mov bx,[(KeyboardType PTR esi+ebx+2).ScreenEdge] ; New absolute Y + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + +??set_xyz: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [(KeyboardType PTR esi).LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor eax,eax + mov [(KeyboardType PTR esi).LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [(KeyboardType PTR esi).LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor ebx,ebx + mov [(KeyboardType PTR esi).LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[(KeyboardType PTR esi).LocalMouseX] ; get new mouse x,y + mov bx,[(KeyboardType PTR esi).LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [(KeyboardType PTR esi).LocalMouseX],ax + mov [(KeyboardType PTR esi).LocalMouseY],bx + mov [WORD PTR (KeyboardType PTR esi).MouseX],ax + mov [WORD PTR (KeyboardType PTR esi).MouseY],bx + cmp [(KeyboardType PTR esi).MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov eax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],eax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov edi,[(KeyboardType PTR esi).KeyBufferTail] + mov [tail],edi ; Safety record. + + ; Record the base keycode (if there is room). + cwde + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov eax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + movzx eax,[(KeyboardType esi).LocalMouseX] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + movzx eax,[(KeyboardType esi).LocalMouseY] + push eax + call Stuff_Key_WORD + add esp,4 + or eax,eax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov ebx,0101h ; Bit control tools. + mov eax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [(KeyboardType PTR esi).KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [(KeyboardType esi).KeyBufferTail],edi ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov edi,eax + and edi,07Fh + mov cl,3 + shr edi,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[(KeyboardType esi+edi).KeysUpDown] + je short ??notalready + test [(KeyboardType PTR esi).KeyFlags],REPEATON + jne short ??notalready + mov edx,[tail] + mov [(KeyboardType esi).KeyBufferTail],edx ; Nullify any KeyBufferTail changes. +??notalready: + and [(KeyboardType esi+edi).KeysUpDown],bh ; Force key bit to zero. + or [(KeyboardType esi+edi).KeysUpDown],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov eax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov eax,[tail] + mov [(KeyboardType PTR esi).KeyBufferTail],eax + xor eax,eax ; Signal an error. + +??exit: + sti + popf + +; popf +; pop ebx +; or ebx,ebx +; jz ??final_exit +; sti + +??final_exit: + ret + + ENDP Stuff_Key_Num + + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles break interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Break_Interrupt C NEAR + + iret + ENDP Break_Interrupt + +IF DEBUG +;*************************************************************************** +;* DEBUG_INTERRUPT -- Handles debug (INT 3) interrupt for protected mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/28/1994 PWG : Created. * +;*=========================================================================* + PROC Debug_Interrupt C NEAR + + ;*================================================================== + ;* Setup fake Interrupt entry sequence so that we can execute our + ;* code and then IRET painlessly into the debuggers interrupt + ;* handler. + ;*================================================================== + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ;*================================================================== + ;* Preserve all of the registers that we intend to use. + ;*=======================================Dbg======================== + pushad + push ds es gs fs + cld + + mov ax , _DATA + mov ds , ax + mov es , ax + inc [BYTE PTR 0B0000h] + + ;*================================================================== + ;* Setup the pointers to the real mode data and the protected mode + ;* data and selectors. + ;*================================================================== + mov esi,[RealModePtr] ; Point to start of RM data. + + ;*================================================================== + ;* Do the deed. + ;*================================================================== + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + + ;*================================================================== + ;* Now get the address of the real debug handler and poke it into + ;* the stack so we can IRET to it. + ;*================================================================== + mov eax,[(KeyboardType PTR esi).DbgOldPMIOffset] ; Get orig offset. + mov ebx,[(KeyboardType PTR esi).DbgOldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ;*================================================================== + ;* Restore the stack so it looks like we just did an IRET entry + ;*================================================================== + pop fs gs es ds + popad + pop ebp + + ;*================================================================== + ;* This iret should go directly to the real debugger handler + ;* painlessly and effectively. + ;*================================================================== + iretd + + ENDP Debug_Interrupt +ENDIF + + + +IF PROT_INT_ENABLE + +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + PROC Keyboard_Interrupt C NEAR + + pushad + push ds es gs fs + cld + +; this is the part of the interrupt that set the segment registers + mov ax , _DATA + mov es , ax + mov ds , ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ESP], esp ; Protect ESP + mov [Keyboard_App_Stack_SS], ecx ; Protect SS + lea edx, [Keyboard_StackStart ] ; Compute Local Stack size + and edx, -4 ; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov esp, edx ; Set new Stack Offset + sti ; Enable Interrupts + + mov esi,[RealModePtr] ; Point to start of RM data. + + mov edx,[(KeyboardType PTR esi).KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [(KeyboardType PTR esi).KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + test [BYTE PTR 417H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR 417H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [(KeyboardType PTR esi).KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [(KeyboardType PTR esi).ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR 496H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [(KeyboardType PTR esi).ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + xor ah,ah ; clear ctrl flags to 0 + mov ebx,0101H ; set key to a make by default + + in al,KEYDATA ; get a code from the keyboard + ; + ; New CODE to montior key stream + ; + xor ebx,ebx + mov bx,[(KeyboardType PTR esi).KeyStreamIndex] + mov [(KeyboardType PTR esi+ebx).KeyStream],al + inc ebx + and ebx,15 + mov [(KeyboardType PTR esi).KeyStreamIndex],bx + mov ebx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [(KeyboardType PTR esi).LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [(KeyboardType PTR esi).LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [(KeyboardType PTR esi).LastKeyE1] ; yes, dec the count + test edx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [(KeyboardType PTR esi).LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [(KeyboardType PTR esi).LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [(KeyboardType PTR esi).LastKeyE0],FALSE ; if so clear for next read + mov [(KeyboardType PTR esi).IsExtKey],TRUE ; it is an extended key + + + mov edi,OFFSET (KeyboardType PTR 0).ExtCodes + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).ExtNums-OFFSET (KeyboardType PTR 0).ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + + mov al,[(OFFSET (KeyboardType PTR 0).ExtNums - OFFSET (KeyboardType PTR 0).ExtCodes) - 1 + edi] ; get the match + mov [(KeyboardType PTR esi).IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov edi,eax ; use code as an index + and edi,007Fh ; Mask off any release bit. + mov al,[(KeyboardType PTR esi+edi).KeyNums] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [(KeyboardType PTR esi+8).KeysUpDown],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [(KeyboardType PTR esi+7).KeysUpDown],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [(KeyboardType PTR esi).ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test edx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [(KeyboardType PTR esi+7).KeysUpDown],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: +;------ Set the shift bit if necessary. + test [(KeyboardType PTR esi+5).KeysUpDown],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [(KeyboardType PTR esi+7).KeysUpDown],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov edi,eax + and edi,07Fh + shr edi,3 + mov ebx,eax + and ebx,07Fh + and bl,0111b + mov ch,[(KeyboardType PTR esi+ebx).Bits] ; get the bit to test + test [(KeyboardType PTR esi).KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysCapsLock] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [(KeyboardType PTR esi).KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[(KeyboardType PTR esi+edi).KeysNumLock] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: + +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [(KeyboardType PTR esi).CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push eax + call KeyNum_Translate + add esp,4 + + mov ecx,eax + xor eax,eax + mov ax,cx + + test edx,DEBUGINT + jz ??not_toggle + + cmp [(KeyboardType PTR esi).KeyIntDisabled],1 + jne ??not_currently_disabled + + inc [BYTE PTR 0B0002h] + + cmp eax,115 ; is it the F4 key + je ??disable + cmp eax,118 ; is it less then F7 key + jb ??justpass + cmp eax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [(KeyboardType PTR esi).KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp eax,125 + jne ??not_toggle + + inc [BYTE PTR 0B0000h] + + mov [(KeyboardType PTR esi).KeyIntDisabled],1 + jmp ??absorbcode + +??not_toggle: + + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp eax,0668H ; is it ctrl alt del? + je ??passcode + cmp eax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp eax,0230h + je short ??breaker + cmp eax,027Eh + jne short ??nobreak +??breaker: + mov [(KeyboardType PTR esi).Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. +; cmp ax,0420H ; is this an alt s +; jne short ??checkmusic ; toggle the Sound variable +; push ax +; mov ax,[SoundOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??checkmusic: +; cmp ax,0434H ; is this an alt m +; jne short ??esc ; toggle the Music variable +; push ax +; mov ax,[MusicOn] +; xor ax,01H +; push ax +; add sp,2 +; pop ax + +;??esc: + + push eax + call Stuff_Key_Num + pop eax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [(KeyboardType PTR esi).EscRoutine],0 ;if vector is 0 don't jump + je short ??noroutine + push eax + call [(KeyboardType PTR esi).EscRoutine] + pop eax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov edi,OFFSET (KeyboardType PTR 0).PassAlways ; get offset to table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).PassAlwaysEnd - OFFSET (KeyboardType PTR 0).PassAlways) ; get number of pass always CDY JLB MOD 7/11 was + repne scasb ; look for a match + or ecx,ecx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + +??passalways: + ; now check for conditional passes + mov edi,OFFSET (KeyboardType PTR 0).CondPassKey ; get offset to cond key table + add edi,esi + mov ecx,(OFFSET (KeyboardType PTR 0).CondPassCond-OFFSET (KeyboardType PTR 0).CondPassKey) ; get number of entries + shr ecx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(OFFSET (KeyboardType PTR 0).CondPassCond - OFFSET (KeyboardType PTR 0).CondPassKey) - 2 + edi] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test edx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + inc [(KeyboardType PTR esi).PassCount] + mov [(KeyboardType PTR esi).PMIssuedKeyInt],1 ; Make it TRUE + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??pass_stack_good + + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??pass_stack_good: + cli ; disable Interrupts + mov edx, [Keyboard_App_Stack_SS] + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + + lea edx, [??Call_Back_Keyboard] ; Get Return address offset + pushfd ; push flags + push cs ; push Code Selector + push edx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushfd + push [(KeyboardType PTR esi).KeyOldPMISelector] ; push orig selector. + push [(KeyboardType PTR esi).KeyOldPMIOffset] ; push orig offset. + iretd + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov al,[496h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [496h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADDEADh + je ??call_stack_good + push ecx + push edi + push eax + mov edi,0A0000h + mov ecx,64000 + mov eax,1 + rep stosb + pop eax + pop edi + pop ecx + +??call_stack_good: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov edx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Set System Stack Selector + mov esp, [Keyboard_App_Stack_ESP] ; Set System Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop fs gs es ds + popad + iret + + ENDP Keyboard_Interrupt + +ELSE +;*************************************************************************** +;* KEYBOARD_INTERRUPT -- Stub for the keyboard interrupt call real mode * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + +DATASEG +STRUC RealModeCallStruc + _EDI DD 0 + _ESI DD 0 + _EBP DD 0 + DD 0 + _EBX DD 0 + _EDX DD 0 + _ECX DD 0 + _EAX DD 0 + FLAGS DW 0 + _ES DW 0 + _DS DW 0 + _FS DW 0 + _GS DW 0 + _IP DW 0 + _CS DW 0 + _SP DW 0 + _SS DW 0 + dd 0 + dd 0 + nothing dd 0 +ENDS + +RMDS RealModeCallStruc <> + +CODESEG + +PROC Keyboard_Interrupt Near + +; This option of the keyboard interrupt handle will not be +; available at this moment because the light version of Rational System DOS +; Extender do not allow a DPMI real mode call which is +; DMPI INT 31h funtion 0301h + + pushad + push fs gs es ds + + mov ax , _DATA + mov es , ax + mov ds , ax + + lea edi , [ RMDS ] + lea ecx , [ RMDS . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + + mov eax , 0300h + mov bl , [ byte ptr RMVector ] + xor bh , bh + xor cx , cx + lea edi , [RMDS] + int DPMI_INTR + +; this is here only for testing to make sure +; that a real mode interrupt is bieng issued. +mov ax , _DATA +mov es , ax +mov ds , ax +mov [ byte ptr 0b0000h + 10 * 80 + 40 ] , 040h +jc ??error +mov [ byte ptr 0b0000h + 10 * 80 + 42 ] , 041h + +??error: + pop ds es gs fs + popad + iretd + ENDP + +ENDIF + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/SRCDEBUG/KEYIPROT.ASM b/WWFLAT32/SRCDEBUG/KEYIPROT.ASM new file mode 100644 index 0000000..a621713 --- /dev/null +++ b/WWFLAT32/SRCDEBUG/KEYIPROT.ASM @@ -0,0 +1,1023 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : KEYINTR.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates the given keynum to ??? * +;* Install_Interrupt -- Installs the keyboard interrupt * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Remove_Interrupt -- Removes the keyboard interrupt and restores the chai* +;* Keystroke_Interrupt -- Handles input that comes from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CODESEG + + +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DW REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DW 0 ; set to first entry +KeyBufferTail DW 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright +EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + +MouseUpdate DW 0 +MouseX DW 0 +LocalMouseX DW 0 +MouseY DW 0 +LocalMouseY DW 0 +Button DW 0 +IsExtKey DB 0 +ExtIndex DW 0 + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates the given keynum to ??? * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES ax,bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow +; PWG: ARRGGGHHHH! +; call Low_Hide_Mouse +; call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [KeyFlags],REPEATON + jne short ??notalready + mov [KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + mov dx,[KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. +;*********************************************************** + pushf +; push di +; push es + + mov ax,0B000h + mov es,ax + inc [BYTE PTR es:0] + +; pop es +; pop di + popf +;*********************************************************** + +??passcode: + + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + inc [cs:PassCount] + jmp [cs:OldRMI] + +??absorbcode: + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: +IF 0 + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + in al,KEYDATA ; get a code from the keyboard + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + + pop es + pop di + pop ax + popf +ENDIF + pushf + call Keystroke_Interrupt ;[OldRMI] + retf + +STACK ; Don't really need this + +;*********************************************************** +END diff --git a/WWFLAT32/SRCDEBUG/KEYIREAL.ASM b/WWFLAT32/SRCDEBUG/KEYIREAL.ASM new file mode 100644 index 0000000..a90604b --- /dev/null +++ b/WWFLAT32/SRCDEBUG/KEYIREAL.ASM @@ -0,0 +1,2610 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) +;* * +;* File Name : KEYIREAL.ASM * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : May 21, 1992 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;* This file sort of breaks the standard of keeping all of the keyboard * +;* and mouse routines isolated. This is done because the mouse and * +;* the keyboard share data, and the best way to do this is to put * +;* them in the same segment. This should probably be split into several * +;* include files to help make the code clearer once it is finally put * +;* together. * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* KeyNum_Translate -- Translates extended keynums to normal keynums * +;* Stuff_Key_Word -- Stuffs a word of data into keyboard buffer * +;* Stuff_Key_Num -- Stuffs a key num code into the circular buffer * +;* Keystroke_Interrupt -- Real mode handler of input from the keyboard * +;* Break_Interrupt -- Handles the break key interrupt * +;* Call_Interrupt_Chain -- Function PM calls to call RM interrupt chain * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;* * +;* Keyboard driver -- 8086 Assembly portion; * +;* updated by: Phil Gorrow for 32 bit Protected Mode * +;*************************************************************************** + +;--------------------------------------------------------------------------- +; Set the assembly directives +;--------------------------------------------------------------------------- +IDEAL ; the product runs in ideal mode +P386N ; use 386 real mode instructions +MODEL TINY ; code must be tiny so it fits +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keyboard.inc" + +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 + + +GLOBAL set_vesa_page :near +GLOBAL set_vesa_window :near +GLOBAL get_vesa_window :near +GLOBAL next_vesa_page :near + +ECHOON equ 0 +;--------------------------------------------------------------------------- +; WARNING!!!! All of the following code segment variables are shared by +; the protected mode interrupt. Do not change these unless you make the +; proper changes to KEYSTRUC.INC. If you do not know what you are doing, +; find someone who does!!! +;--------------------------------------------------------------------------- +CODESEG + +;--------------------------------------------------------------------------- +; Begin definition of Keyboard specific variables +;--------------------------------------------------------------------------- +SoundOn DW 1 ; toggled by alt S +MusicOn DW 1 ; toggled by alt M +KeyFlags DD REPEATON+CTRLALTTURBO ; all but repeat for now + + +Break DW 0 + +KeyMouseMove DB -1,0,1 + DB -16,0,16 + +ScreenEdge DW 320/2,0 ; North + DW 319,0 ; North-East + DW 319,138/2 ; East + DW 319,137 ; South-East + DW 320/2,137 ; South + DW 0,137 ; South-West + DW 0,138/2 ; West + DW 0,0 ; North-West + DW 320/2,138/2 ; Center + + +Bits DB 01H,02H,04H,08H,10H,20H,40H,80H + +CondPassKey DW 0220H, 0320H, 060CH, 070DH, 066AH + DW 0669H, 0230H, 0330H, 007DH, 017DH + DW 025AH, 035AH, 0200H, 0410H, 046EH + DW 026EH, 007CH + +CondPassCond DW CTRLSON, CTRLSON, CTRLALTTURBO, CTRLALTTURBO, CTRLALTTURBO + DW CTRLALTTURBO, CTRLCON, CTRLCON, SCROLLLOCKON, SCROLLLOCKON + DW PAUSEON, PAUSEON, BREAKON, TASKSWITCHABLE, TASKSWITCHABLE + DW TASKSWITCHABLE, BREAKON + +EscRoutine DD 0 ; vector to execute on esc key press (0=none) + +; Extended raw keycodes to be converted to Westwood keycodes. +ExtCodes DB 038H,01DH,052H,053H,04BH,047H,04FH,048H,050H,049H + DB 051H,04DH,035H,01CH,037H + DB 046H +; The matching Westwood keycodes. +ExtNums DB 62, 64, 75, 76, 79, 80, 81, 83, 84, 85 + DB 86, 89, 95, 108, 124, 0 +; If extended mapping is disabled, then these codes really are... +ExtRemap DB 60, 58, 99, 104, 92, 91, 93, 96, 98, 101 + DB 103, 102, 55, 43, 124, 0 +ExtRemapEnd DB 0 + +ExtKeyboard DB 0 ; flag for 101/102-key keyboard + + +KeyBuffer DW 128 DUP(0) ; set to empty +KeyBufferHead DD 0 ; set to first entry +KeyBufferTail DD 0 ; set to head for empty buffer +KeyLock DW 0 ; num and caps lock bits +KeyNums DB 127,110,002,003,004,005,006,007,008,009,010,011,012,013,015,016 + DB 017,018,019,020,021,022,023,024,025,026,027,028,043,058,031,032 + DB 033,034,035,036,037,038,039,040,041,001,044,029,046,047,048,049 + DB 050,051,052,053,054,055,057,100,060,061,030,112,113,114,115,116 + DB 117,118,119,120,121,090,125,091,096,101,105,092,097,102,106,093 + DB 098,103,099,104,127,127,127,122,123 + +KeysCapsLock DB 0,0,0FEH,087H,0FFH,0C0H,01FH,0,0,0,0,0,0,0,0,0 +KeysNumLock DB 0,0,0,0,0,0,0,0,0,0,0,038H,0EFH,1,0,0 +KeysUpDown DB 16 DUP(0) ; set to all keys up +KeyStream DB 16 DUP(0) ; set to all keys up +PassCount DW 0 +KeyStreamIndex DW 0 +LastKeyE0 DB 0 +LastKeyE1 DB 0 + +; +; Westwood key number values of keys to pass through +; +; CAPS, LEFT_SHIFT, RIGHT_SHIFT, LEFT_CTRL, LEFT_ALT, +; RIGHT_ALT, RIGHT_CTRL, NUM_LOCK, UNKNOWN +PassAlways DB 30, 44, 57, 58, 60, 62, 64, 90, 128, 128 +PassAlwaysEnd DB 128 ; invalid code to END PassAlways +CtrlFlags DB 0 + +Buffer DW ? +Time DW ? + +ADJUST = 1 ; do not modify DRD + +XYAdjust DB -ADJUST, -ADJUST ; 91 -> upleft + DB -ADJUST, 0 ; 92 -> left + DB -ADJUST, ADJUST ; 93 -> downleft + DB 0, 0 ; 94 illegal + DB 0, 0 ; 95 illegal + DB 0, -ADJUST ; 96 -> up + DB 0, 0 ; 97 illegal (center) + DB 0, ADJUST ; 98 -> down + DB 0, 0 ; 99 illegal + DB 0, 0 ; 100 illegal + DB ADJUST, -ADJUST ; 101 -> upright + DB ADJUST, 0 ; 102 -> right + DB ADJUST, ADJUST ; 103 -> downright + EdgeConv DW 8,2,8,6,4,3,8,5,8,8,8,8,0,1,8,7 + MouseUpdate DW 0 + MouseX DW 0,0 + LocalMouseX DW 0 + MouseY DW 0,0 + LocalMouseY DW 0 + IsExtKey DB 0 + ExtIndex DW 0 + + KeyOldRMI DD 0 ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD 0 ; The origianl PM interrupt offset + KeyOldPMISelector DD 0 ; The original PM interrupt segment. + + KeyCodeOffset DW RM_Keystroke_Interrupt ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD 0 + + BrkOldRMI DD 0 ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD 0 ; The origianl PM interrupt offset + BrkOldPMISelector DD 0 ; The original PM interrupt segment. + + BrkCodeOffset DW RM_Break_Interrupt ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW 0 + CallBrkRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD 0 + KeyIntDisabled DD 0 + + DbgOldPMIOffset DD 0 ; The origianl PM interrupt offset + DbgOldPMISelector DD 0 ; The original PM interrupt segment. + +;--------------------------------------------------------------------------- +; Begin definition of Mouse Specific Variables for real mode +;--------------------------------------------------------------------------- +Button DB 0 ; current value of the mouse button +MDisabled DB 0 ; Is the mouse driver disabled +MInput DB 1 ; Defaults to mouse input allowed. +Adjust DW 0 ; flag to adjust coordinates if necessary +MouseStepX DW 0 ; step values if the mouse moves at +MouseStepY DW 0 ; more than one pixel at a time +MouseOffsetX DW 0 ; Fractional step values used if a mouse +MouseOffsetY DW 0 ; moves at less than one pixel at a time +MState DW 0,0 ; Tracks if mouse is hidden (TRUE) or not (FALSE) +MouseXOld DW 0 ; Holds last MouseX and MouseY to determine if +MouseYOld DW 0 ; mouse needs to be redrawn +MCState DW 0 ; Tracks if mouse conditional hidden (TRUE) or not +MouseCXLeft DW 0,0 ; Conditional hide mouse left x position +MouseCYUpper DW 0,0 ; Conditional hide mouse top y position +MouseCXRight DW 0,0 ; Conditional hide mouse right x position +MouseCYLower DW 0,0 ; Conditional hide mouse lower y position +MouseCursor DD 0 ; Pointer to the mouse cursor to draw +MouseCursorSize DW 0 ; Pointer to buffer mouse is saved in +MouseBuffer DD 0 ; Pointer to buffer mouse is saved in +MouseXHot DW 0,0 ; Offset to mouse's x hot spot +MouseYHot DW 0,0 ; Offset to mouse's y hot spot +MouseBuffX DW 0,0 ; X position background was saved at +MouseBuffY DW 0,0 ; Y position background was saved at +MouseBuffW DW 0,0 ; Width of the region saved for mouse +MouseBuffH DW 0,0 ; Height of the region saved for mouse +MouseWidth DW 0,0 ; Mouse cursor theoretical width +MouseHeight DW 0,0 ; Mouse cursor theoretical height +MouseCodeOffset DW RM_Mouse_Interrupt ; Offset of the code in the RM stuff. +MouseRight DW 0,0 +MouseBottom DW 0,0 + + +ShadowPtr dw 0 +DrawMousePtr dw 0 + +VGAMouseDraw dw VGA_Draw_Mouse +VGAMouseShadow dw VGA_Mouse_Shadow_Buffer + +VESAMouseDraw dw VESA_Draw_Mouse +VESAMouseShadow dw VESA_Mouse_Shadow_Buffer + +VesaPtr dd 0 +banktable dd 8 dup ( 0 ) +Adjust_XPos dw 0 , 0 +Adjust_YPos dw 0 , 0 + + align 2 +Keyboard_App_Stack_ES dw 0 ; This the System Stack Offsset +Keyboard_App_Stack_SS dw 0 ; This the System Stack Selector +Keyboard_StackPointer dw 0DEADh ; We Create a Local Application +Keyboard_Stack dw 512 dup (0) +Keyboard_StackStart dw 0 + +Mouse_State dw 0 ; Mouse Temp Variable +Mouse_Cond dw 0 ; Mouse Temp Variable +Mouse_App_Stack_ES dw 0 ; This the System Stack Offsset +Mouse_App_Stack_SS dw 0 ; This the System Stack Selector +Mouse_StackPointer dw 0DEADh ; We Create a Local Application +Mouse_Stack dw 512 dup (0) +Mouse_StackStart dw 0 + + + +current_page dw 0 +;*************************************************************************** +;* KEYNUM_TRANSLATE -- Translates extended keynums to normal keynums * +;* * +;* INPUT: UWORD the keynum to translate * +;* * +;* OUTPUT: WORD the translated keynum * +;* * +;* PROTO: UWORD KeyNum_Translate(UWORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL KeyNum_Translate:FAR + PROC KeyNum_Translate C FAR + USES cx,di,es,ds + ARG keycode:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + mov es,ax ; set es up for scansb + + mov ax,[keycode] + test [WORD PTR KeyFlags],TRACKEXT + jne short ??fini + + mov cx,ExtRemap-ExtNums + mov di,OFFSET ExtNums + + repne scasb + jcxz short ??fini ; No match found. + + mov di,OFFSET ExtRemapEnd + dec di + sub di,cx + mov al,[es:di] +??fini: + ret + ENDP KeyNum_Translate + +;*************************************************************************** +;* STUFF_KEY_WORD -- Stuffs a word of data into keyboard buffer * +;* * +;* INPUT: WORD the code to stick into the circular buffer * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_WORD(WORD code); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_WORD:FAR + PROC Stuff_Key_WORD C FAR + USES si,bx,ds + ARG code:WORD + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov ax,[WORD PTR KeyBufferTail] + mov si,ax + add ax,2 + and ax,0FFh ; New KeyBufferTail value. + cmp [WORD PTR KeyBufferHead],ax + je short ??noroom + + mov bx,[code] + mov [KeyBuffer+si],bx ; Record the keystroke. + mov [WORD PTR KeyBufferTail],ax + xor ax,ax + ret + +??noroom: + mov ax,1 + ret + + ENDP Stuff_Key_WORD + +;*************************************************************************** +;* STUFF_KEY_NUM -- Stuffs a key num code into the circular buffer * +;* * +;* INPUT: WORD the keycode to stuff * +;* * +;* OUTPUT: WORD !=0 is sucessful, ==0 is not enough room * +;* * +;* PROTO: VOID Stuff_Key_Num(WORD keynum); * +;* * +;* HISTORY: * +;* 07/11/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Stuff_Key_Num:FAR + PROC Stuff_Key_Num C FAR + USES bx,cx,dx,di,si,ds + ARG keycode:WORD + LOCAL tail:WORD ; Original keybuffer tail (safety copy). + LOCAL size:WORD ; Size of write. + + pushf + cli ; disable interrupts + + ; Abort key recognition if in record mode and unable + ; to output key due to simultaneous DOS operation. + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + + ; Record the mouse position to be stuffed into buffer. + mov ax,[MouseX] + mov [LocalMouseX],ax + mov ax,[MouseY] + mov [LocalMouseY],ax + +??cando: + mov ax,[keycode] ; get the code + or ax,ax ; Null keycodes are not recorded. + jne short ??validkey + jmp ??exit + + +??validkey: + + test [WORD PTR KeyFlags],KEYMOUSE ; is the numeric keypad moving the mouse? + je ??no_pad_move + + ; ALT-cursor keys are undefined. Pass them on to the program. + test ah,ALTPRESS ; is either alt key down? + jne ??no_pad_move + + test [WORD PTR KeyFlags],SIMLBUTTON ; are we simulating left mouse presses + je short ??chkinsert + + cmp al,KN_RETURN + je short ??forceleft + + cmp al,KN_SPACE + je short ??forceleft + + cmp al,KN_KEYPAD_RETURN + je short ??forceleft + +??chkinsert: + cmp al,KN_INSERT + jne short ??regular + +??forceleft: + mov al,KN_LMOUSE + or [Button],1 ; Left mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 1 + jmp ??mousefake + +??regular: + cmp al,KN_DELETE + jne short ??regular2 + mov al,KN_RMOUSE + or [Button],2 ; Right mouse bit. + test ah,KEYRELEASE + je ??mousefake + and [Button],NOT 2 + jmp ??mousefake + +??regular2: + ; DRD correction to ignore key releases for key mouse movement + test ah,KEYRELEASE + jne ??no_pad_move + + cmp al,KN_CENTER + je short ??pad_move + cmp al,KN_UPLEFT ; less than upleft? + jb ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNRIGHT ; greater than downright? + ja ??no_pad_move ; yes, then it isn't a keypad key + cmp al,KN_DOWNLEFT ; is it UPLEFT, LEFT, or DOWNLEFT? + jbe short ??pad_move + cmp al,KN_UPRIGHT ; is it UPRIGHT, RIGHT, or DOWNRIGHT? + jae short ??pad_move + cmp al,KN_UP ; up? + je short ??pad_move + cmp al,KN_DOWN ; down? + jne ??no_pad_move + +??pad_move: + ; DRD correction to use ch for ah + mov ch,ah ; save shift-ctrl-alt-rlse status + + xor ah,ah ; get rid of any bits + sub al,KN_UPLEFT ; get a number between 0 and 12 + mov bx,ax + shl bx,1 ; double for WORD index + add bx,OFFSET XYAdjust + mov ax,[bx] ; get x,y add value + + mov bl,ah + cbw + xchg ax,bx + cbw + xchg ax,bx ; AX = mouse x delta, BX = mouse y delta + + ; DRD correction to use ch + ; The CTRL key moves the mouse to the edge of the screen. + test ch,CTRLPRESS ; is either ctrl key down? + jne short ??ctrlon ; if so, ctrl is on + + ; DRD correction to use ch + ; use fast speed of the mouse move if the shift key is held down. + mov dx,1 ; for slow speed + test ch,SHIFTPRESS ; is either shift key down? + je short ??normspeed ; if not then neither shift is down +??doublespeed: + add dx,3 ; for fast speed +??normspeed: + add bx,dx ; add speed for y index + mov bl,[KeyMouseMove+bx] ; get speed for y delta + xchg ax,bx ; swap with ax to extend sign + cbw + xchg ax,bx + xchg bx,dx ; save mouse y delta + add bx,ax ; add speed for x index + mov al,[KeyMouseMove+bx] ; get speed for x delta + cbw + xchg bx,dx ; restore mouse y delta + + jmp short ??ctrloff + +??ctrlon: + + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded for an ADJUST + ; value of 3. If this value changed, then this section will also + ; have to be modified. + and bx,011b ; Y = 1, 0, 3 + and ax,011b ; X = 1, 0, 3 + ; Table lookup method for determining hotkey positions for CTRL + ; cursor combination. This algorithm is hard coded. + ; -1, 0, 1 + and bx,011b ; Y = 3, 0, 1 + and ax,011b ; X = 3, 0, 1 + shl bx,1 + shl bx,1 + or bx,ax ; Lookup index. + + ; Convert raw index into logical (clockwise) index. + shl bx,1 + mov bx,[EdgeConv+bx] + shl bx,1 + shl bx,1 + mov ax,[ScreenEdge+bx] ; New absolute X + mov bx,[ScreenEdge+bx+2] ; New absolute Y + mov [LocalMouseX],ax + mov [LocalMouseY],bx + +??set_xyz: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + jmp short ??set_xy + + ; Process a normal faked mouse move. +??ctrloff: + ; DRD change + add [LocalMouseX],ax ; save it in our local + jns short ??not_negative_x + xor ax,ax + mov [LocalMouseX],ax ; clear our local + +??not_negative_x: +; DRD change + add [LocalMouseY],bx ; save it in our local + jns short ??not_negative_y + xor bx,bx + mov [LocalMouseY],bx ; clear our local + +??not_negative_y: + mov ax,[LocalMouseX] ; get new mouse x,y + mov bx,[LocalMouseY] + cmp ax,MAX_X_PIXEL ; bigger than + jle short ??check_y + mov ax,MAX_X_PIXEL + +??check_y: + cmp bx,MAX_Y_PIXEL ; bigger than + jle short ??set_xy + mov bx,MAX_Y_PIXEL + +??set_xy: + mov [LocalMouseX],ax + mov [LocalMouseY],bx + mov [MouseX],ax + mov [MouseY],bx + cmp [MouseUpdate],0 ; wait until mouse interrupt is done + jne short ??noshow + + call Low_Hide_Mouse + call Low_Show_Mouse +??noshow: + mov ax,KN_MOUSE_MOVE +??mousefake: + mov [keycode],ax ; Fake a MOUSE_MOVE event. + +??no_pad_move: + ; Fetch working pointers to the keyboard ends. + mov si,[WORD KeyBufferTail] + mov [tail],si ; Safety record. + mov di,[WORD PTR KeyBufferHead] + + ; Record the base keycode (if there is room). + push ax + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne short ??jmpnoroom + + ; Also record the mouse coordinates if necessary. + mov ax,[keycode] ; get key code + cmp al,KN_MOUSE_MOVE ; mouse move? + je short ??recordmouse ; yes? then record the mouse cooordinates + cmp al,KN_LMOUSE + je short ??recordmouse + cmp al,KN_RMOUSE + je short ??recordmouse + jmp short ??ok +??jmpnoroom: + jmp ??noroom + + ; Record mouse coordinate X. +??recordmouse: + push [LocalMouseX] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + + ; Record mouse coordinate Y. + push [LocalMouseY] + call Stuff_Key_WORD + add sp,2 + or ax,ax + jne ??jmpnoroom + add [size],2 + +??ok: + ; If PASSBREAKS is not active and this is a keyboard + ; break AND it is not a mouse event, then don't put + ; it into the buffer. + mov bx,0101h ; Bit control tools. + mov ax,[keycode] + cmp al,KN_MOUSE_MOVE + je short ??notreal + cmp al,127 + je short ??notreal + test ah,KEYRELEASE + je short ??real + xor bl,bl + test [WORD PTR KeyFlags],PASSBREAKS + jne short ??real + cmp al,KN_LMOUSE + je short ??real + cmp al,KN_RMOUSE + je short ??real +??notreal: + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??real: + + ; Update the KeysUpDown bit array. + mov di,ax + and di,07Fh + mov cl,3 + shr di,cl ; DI = Byte offset into bit table. + mov cl,al + and cl,0111b ; CL = Bit offset into bit table byte. + shl bx,cl + not bh + + ; If this is a reapeat key and the key is already being held + ; down, then don't stuff it into the keyboard buffer. + test bl,[KeysUpDown+di] + je short ??notalready + test [WORD PTR KeyFlags],REPEATON + jne short ??notalready + mov [WORD PTR KeyBufferTail],si ; Nullify any KeyBufferTail changes. +??notalready: + and [KeysUpDown+di],bh ; Force key bit to zero. + or [KeysUpDown+di],bl ; Insert key bit as appropriate. +;??notreal: + + ; Successful keybuffer stuff could result in a +??norecord: + mov ax,1 + jmp short ??exit + + ; Unsuccessful keybuffer stuff. +??noroom: + mov ax,[tail] + mov [WORD PTR KeyBufferTail],ax + xor ax,ax ; Signal an error. + +??exit: + popf + ret + + ENDP Stuff_Key_Num +;*********************************************************** + + + +;*************************************************************************** +;* KEYSTROKE_INTERRUPT -- Handles input that comes from the keyboard * +;* * +;* This routine intercepts the key codes on their way to the * +;* BIOS. With the adjustment of the Flags described above * +;* you can get a wide variety of effects. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt function * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Keystroke_Interrupt + GLOBAL C Keystroke_Interrupt:FAR + PROC Keystroke_Interrupt C FAR + +IF 0 + push ax + inc ax + pop ax + iret + +ELSE + push ax + push bx + push cx + push di + push ds + push dx + push es + push si + cld + + mov ax,cs ; set ds to cs to avoid cs overide + mov ds,ax + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov cx, ss ; get SS + mov [Keyboard_App_Stack_ES], sp ; Protect ES + mov [Keyboard_App_Stack_SS], cx ; Protect SS + lea dx, [Keyboard_StackStart ] ; Compute Local Stack size + and dx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, dx ; Set new Stack Offset + sti ; Enable Interrupts + + cmp [WORD PTR PMIssuedKeyInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedKeyInt],0; Make it false. + jne ??passcode ; if so, just call Int Chain. + + + mov dx,[WORD PTR KeyFlags] + ;*** The following fix allows proper caps and num lock tracking on Tandy + ; 10-6-89 LJC, DRD + + and [KeyLock],NOT (NUMLOCK OR CAPSLOCK); assume caps and num inactive + mov ax,040H ; BIOS segment + mov es,ax ; put in es + test [BYTE PTR es:017H],040H ; test Caps lock bit in BIOS + je short ??bioscapsoff ; skip activate code + or [KeyLock],CAPSLOCK ; Caps Lock active + +??bioscapsoff: + test [BYTE PTR es:017H],020H ; test Num lock bit in BIOS + je short ??biosnumoff ; skip activate code + or [KeyLock],NUMLOCK ; Num Lock active + +??biosnumoff: + mov [ExtKeyboard],TRUE ; assume 101/102-key keyboard + test [BYTE PTR es:096H],010H ; test for 101/102-key keyboard + jne short ??extkeyboard ; skip deactivate code + mov [ExtKeyboard],FALSE ; no 101/102-key keyboard + +??extkeyboard: + mov ax,cs ; set ds to cs to avoid cs overide + mov es,ax + + cld ; clear direction flag for strings + xor ah,ah ; clear ctrl flags to 0 + mov bx,0101H ; set key to a make by default + in al,KEYDATA ; get a code from the keyboard + + ; + ; New CODE to montior key stream + ; + mov bx,[KeyStreamIndex] + mov [KeyStream+bx],al + inc bx + and bx,15 + mov [KeyStreamIndex],bx + mov bx,0101H ; set key to a make by default + ; + ; END OF SEQUENCE + ; + + ; + ; Handle the PAUSE key being pressed. If it is pressed, then + ; signal that the next two input codes are to be thrown out. + ; + cmp al,0E1H ; see if this is a pause/break + jne short ??notpcode ; not a pause/break start code + mov [LastKeyE1],3 ; absorb this and next two codes + +??notpcode: + cmp [LastKeyE1],0 ; are we in a pause/break code + je short ??notpause ; no, just keep going + dec [LastKeyE1] ; yes, dec the count + test dx,PAUSEON ; should it pass these codes + jne ??passcode ; pass the code + jmp ??absorbcode ; don't pass code + +??notpause: + ; + ; Record any extended key codes that arrive. They will be + ; taken into account with the next code. + ; + cmp al,0E0H ; is it an extended code + jne short ??notextcode ; if not skip to handle key + mov [LastKeyE0],TRUE ; set the extended code to 1 +??jmppasscode: + jmp ??passcode ; always pass E0s + +??notextcode: + + ; + ; Check and acknowledge if the key is a make or a break. + ; + test al,080H ; test for high bit + je short ??make ; if off its a make + xor bl,bl ; set make/break to break + and al,07FH ; clear high bit + or ah,KEYRELEASE ; CDY NEW -- ABSENT IN OLD CODE + +??make: + ; + ; Translate the raw keycode into the Westwood keycode. + ; + cmp [LastKeyE0],FALSE ; was the prev byte an E0? + je short ??normal ; if not it is a normal key + mov [LastKeyE0],FALSE ; if so clear for next read + mov [IsExtKey],TRUE ; it is an extended key + mov di,OFFSET ExtCodes ; get offset of extended codes table + mov cx,(ExtNums-ExtCodes) ; get number of entrys in ext table + repne scasb ; look for a match + jcxz ??absorbcode ; Not recognized, so throw it away. + mov al,[(ExtNums - ExtCodes) - 1 + di] ; get the match + mov [IsExtKey],FALSE ; it is not an extended key + jmp short ??notext + +??normal: + cmp al,07Ah + jne short ??normok + mov al,128 + jmp short ??notext +??normok: + mov di,ax ; use code as an index + and di,007Fh ; Mask off any release bit. + mov al,[KeyNums+di] ; get the key number of this key + +??notext: + ; + ; Test and set the CTRL bit. + ; + test [KeysUpDown+8],001H ; is the right ctrl down? + jne short ??ctrlon ; if so, ctrl is on + test [KeysUpDown+7],004H ; is the left ctrl down? + je short ??ctrloff ; if not, neither are down, no ctrl + + ; DRD + ; check for CTRL-NUMLOCK for pause on some keyboards + + cmp al,KN_NUMLOCK ; check for CTRL-NUMLOCK + jne short ??ctrlon + + cmp [ExtKeyboard],TRUE ; if 101/102-key keyboard it is ok + je short ??ctrlon + + test dx,PAUSEON ; should it pass these codes + jne short ??ctrlon ; pass the code + + jmp ??absorbcode ; don't pass code + +??ctrlon: + or ah,CTRLPRESS ; or on the ctrl bit in flags + +??ctrloff: + ; + ; Test and set the ALT bit. + ; + test [KeysUpDown + 7],050H ; is either alt key down? + je short ??altoff + or ah,ALTPRESS ; or on the alt bit in flags + +??altoff: + ; + ; Remap the keyboard keys if this is requested. (Do not set DGROUP + ; as it is unecessary) + push ax + call KeyNum_Translate + add sp,2 + +;------ Set the shift bit if necessary. + test [KeysUpDown+5],010H ; is the left shift down? + jne short ??shifton ; if so the shift is on + test [KeysUpDown+7],002H ; is the right shift down? + je short ??shiftoff ; if not then neither shift is down + +??shifton: + or ah,SHIFTPRESS ; or on the shift bit in flags + +??shiftoff: + ; + ;------ Toggle the shift state if the caps lock key is on (if necessary). + ; + mov di,ax + and di,07Fh + shr di,1 + shr di,1 + shr di,1 + mov bx,ax + and bx,07Fh + and bl,0111b + mov ch,[Bits+bx] ; get the bit to test + test [KeyLock],CAPSLOCK ; is the caps lock on? + je short ??capsoff ; if not don't worry about it + test ch,[KeysCapsLock+di] ; get code for keycaps + je short ??capsoff ; its not effected + xor ah,SHIFTPRESS ; toggle the shift flag + +??capsoff: + ; + ;------ Toggle the shift state if the num-lock key is on (if necessary). + ; + test [KeyLock],NUMLOCK ; is the num lock key on? + je short ??numlockoff ; if not don't worry about it + test ch,[KeysNumLock+di] ; get code for numlock + je short ??numlockoff ; if not effected skip toggle + xor ah,SHIFTPRESS ; toggle the shift flag if effected + +??numlockoff: +;------ Remember the current control/shift/alt bit settings for later use. + +;??noshiftever: + mov [CtrlFlags],ah ; save off shift-ctrl-alt flags + ; for the mouse and joystick routines + ; to use in stuffing key into the + ; keyboard buffer. + test dx,DEBUGINT + jz ??not_toggle + + +IF DEBUG + cmp [KeyIntDisabled],1 + jne ??not_currently_disabled + cmp ax,115 ; is it the F4 key + je ??disable + cmp ax,118 ; is it less then F7 key + jb ??justpass + cmp ax,120 ; is it greater than F9 key + ja ??justpass +??disable: + mov [KeyIntDisabled],0 +??justpass: + jmp ??passcode + +??not_currently_disabled: + cmp ax,125 + jne ??not_toggle + mov [KeyIntDisabled],1 + jmp ??absorbcode +ENDIF + +??not_toggle: + +;------ The CTRL-ALT-DEL key combination always gets passed to the system. + cmp ax,0668H ; is it ctrl alt del? + je ??passcode + cmp ax,064CH ; is it ctrl alt ext del? + je ??passcode ; if so don't add it to the buffer + +;------ Special Ctrl-C check. + cmp ax,0230h + je short ??breaker + cmp ax,027Eh + jne short ??nobreak +??breaker: + mov [Break],1 + +??nobreak: +;------ Check for Music and Sound control keys. + cmp ax,0420H ; is this an alt s + jne short ??checkmusic ; toggle the Sound variable + push ax + mov ax,[SoundOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??checkmusic: + cmp ax,0434H ; is this an alt m + jne short ??esc ; toggle the Music variable + push ax + mov ax,[MusicOn] + xor ax,01H + push ax + add sp,2 + pop ax + +??esc: + + push ax + call Stuff_Key_Num + pop ax +??skipstuff: + +;------ Do the special ESC routine if necessary. + cmp al,110 ; is this the esc key? + jne short ??noroutine ; if not goto the pass always + cmp [WORD PTR EscRoutine+2],0 ;if vector is 0 don't jump + je short ??noroutine + push ax + call [EscRoutine] + pop ax + +??noroutine: +;------ Check to see if the key is to be passed to the system or not. + mov di,OFFSET PassAlways ; get offset to table + mov cx,(PassAlwaysEnd - PassAlways) ; get number of pass always CDY JLB MOD 7/11 was +1 + repne scasb ; look for a match + or cx,cx ; see if there was no match + jne ??passcode ; CDY JLB 7/11 optimization + + + + +??passalways: + ; now check for conditional passes + mov di,OFFSET CondPassKey ; get offset to cond key table + mov cx,(CondPassCond-CondPassKey) ; get number of entries + shr cx,1 ; cut in half for words + repne scasw ; look for a match + jcxz short ??notcondpass ; OPTIMIZATION CDY JLB + mov bx,[(CondPassCond - CondPassKey) - 2 + di] + and bx,dx ; are the conditions met? + je short ??notcondpass ; NO... check normally. + jmp short ??passcode ; YES... pass back to system. +; +;------ Last check before passing keycode back to the system. +; +??notcondpass: + test dx,FILTERONLY ; is the filter only flag on? + je short ??absorbcode ; if not, absorb the code. + +??passcode: + + inc [cs:PassCount] + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 40h] + + ; Now it is time to set up for the call to the System Keyboard + ; interrupt handler. + ; 1 -Restore System Stack Pointer Selector before exit Interrupt + ; 2- We Create a Returning Point from Interrupt by Push A + ; Interupt Stack Frame into the Stack Pointer + ; 3- We make a Far jump to the interuupt handler + cmp [Keyboard_StackPointer],0DEADh + je ??stackok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok: + cli ; disable Interrupts + mov dx, [Keyboard_App_Stack_SS] + mov ss, dx ; Get System Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get System Stack offset + sti ; Enable Interrupts + + lea dx, [??Call_Back_Keyboard] ; Get Return address offset + pushf ; push flags + push cs ; push Code segment + push dx ; push Offset + + ; Now we need to simulate an interrup call by using ired + ; because we still want to come back here from the + ; Old Keyboard interrupt handle. + pushf + push [word ptr KeyOldRMI + 2] ; push orig segment. + push [word ptr KeyOldRMI] ; push orig offset. + iret ; call interrupt + +??absorbcode: + +;mov ax , 0B000h +;mov es, ax +;inc [BYTE PTR es : 0h] + + in al,KEYCTRL ; get the control port + mov ah,al + or al,080H ; reset bit for keyboard + out KEYCTRL,al + xchg ah,al ; get orig control data + out KEYCTRL,al ; restore control data + + mov ax,040h ; BIOS paragraph is always @ 040h + mov es,ax ; put in es as BIOS paragraph + mov al,[es:96h] ; get extended keys + and al,0FDh ; turn off last key e0 flag + mov [es:96h],al ; set extended keys + + mov al,CLEARISR ; value to clear In Service Register + out INTCHIP0,al ; 8259 interrupt chip controller 0 + + cmp [Keyboard_StackPointer],0DEADh + je ??stackok2 + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx +??stackok2: + + ; Restore System Stack Pointer Selector before exit Interrupt + mov dx, [Keyboard_App_Stack_SS] + cli ; disable Interrupts + mov ss, dx ; Get Syatem Stack Selector + mov sp, [Keyboard_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + +??Call_Back_Keyboard: + pop si + pop es + pop dx + pop ds + pop di + pop cx + pop bx + pop ax + iret +ENDIF + + ENDP Keystroke_Interrupt + +;*************************************************************************** +;* Break interrupt routines begin here! +;*************************************************************************** + +;*************************************************************************** +;* BREAK_INTERRUPT -- Handles the break key interrupt * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: This is an interrupt routine. * +;* * +;* HISTORY: * +;* 07/13/1994 PWG : Created. * +;*=========================================================================* +label RM_Break_Interrupt + GLOBAL C Break_Interrupt:FAR + PROC Break_Interrupt C FAR + + + pushf + push ax + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + inc [BYTE PTR es:0] + + + pop es + pop ax + popf + + + iret + + ENDP Break_Interrupt + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + pushf + call Keystroke_Interrupt ;[KeyOldRMI] + retf + +;---------------------------------------------------------------------------- +; LOW_HIDE_MOUSE: +; +; This function hides the mouse cursor on the screen if it was shown. It +; will not hide the mouse if it is already hidden. +; +; PROTOTYPE: +; +; VOID Low_Hide_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +; +;---------------------------------------------------------------------------- + + GLOBAL C Low_Hide_Mouse:FAR + PROC Low_Hide_Mouse C FAR + USES ax,bx,cx,dx,ds + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + cmp [MDisabled],0 ; check if mouse is disabled + jne short ??end + + cmp [MState],0 ; check if it was hidden before + jne short ??endnodraw ; no need to hide again + +;------ Move the saved graphic buffer to the seenpage to hide the mouse. +; call Buffer_To_Page C,[buffx],[buffy],[buffw],[buffh],[MouseBuffer],SEENPAGE + mov ax,RESTORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Record that the mouse has been hidden. +??endnodraw: + add [MState],1 + adc [MState],0 + +??end: + ret + + ENDP Low_Hide_Mouse + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; LOW_SHOW_MOUSE: +; +; This function displays the mouse cursor on the screen if it was hidden. +; +; PROTOTYPE: +; VOID Low_Show_Mouse(VOID); +; +; NOTE: does not check if mouse is currently being updated. +;---------------------------------------------------------------------------- + + GLOBAL C Low_Show_Mouse:FAR + PROC Low_Show_Mouse C FAR + USES ax,bx,cx,dx,si,di,ds,es + LOCAL mousex:WORD ; Draw X position. + LOCAL mousey:WORD ; Draw Y position. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + +;----- Don't show the mouse if it is not hidden, disabled. + cmp [MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + dec [MState] + cmp [MState],0 ; can the mouse be shown + jne short ??exit + +;------ Determine the drawing position of the mouse. + mov cx,[MouseWidth] ; Theoretical buffer width (pixel). + mov dx,[MouseHeight] ; Theoretical buffer height (pixel). + + mov ax,[MouseX] +; sub ax,[MouseXHot] + mov [mousex],ax ; Draw X pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??xnotneg +; add cx,ax ; Reduce width accordingly. +; mov ax,0 +??xnotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov bx,[MouseY] +; sub bx,[MouseYHot] + mov [mousey],bx ; Draw Y pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IF 0 +; jns short ??ynotneg +; add dx,bx ; Reduce height of mouse accordingly. +; mov bx,0 +??ynotneg: +ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------ Determine the theoretical coordinates and dimensions of the +; area the mouse shape will be rendered upon. + mov [MouseBuffX],ax + mov [MouseBuffY],bx + mov [MouseBuffW],cx + mov [MouseBuffH],dx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov ax,STORE_VISIBLE_PAGE + push ax + push cs + call [ ShadowPtr ] + add sp,2 + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + push cs + call [ DrawMousePtr ] + add sp,4 +??exit: + ret + + ENDP Low_Show_Mouse + +;---------------------------------------------------------------------------- +;---------------------------------------------------------------------------- + GLOBAL C Mouse_KeyNum:FAR + PROC Mouse_KeyNum C FAR + USES bx + ARG state:WORD ; Current mouse state. + LOCAL keynum:WORD ; Determined keynum. + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + mov [keynum],KN_MOUSE_MOVE ; Presume just a mouse move. + mov bx,[state] + mov ax,bx + xor bl,[Button] ; Bits of state change. + je short ??fini + mov [Button],al ; Record new mouse state. + + test bl,0010b + je short ??notright + mov [keynum],KN_RMOUSE + test al,0010b + jne short ??notright + or [keynum],0800h ; Release bit on. +??notright: + +; DRD +; note: the left mouse button has priority over the right mouse button +; this should be changed at a later date to process them independently + + test bl,0001b + je short ??notleft + mov [keynum],KN_LMOUSE + test al,0001b + jne short ??notleft + or [keynum],0800h ; Release bit on. +??notleft: + +??fini: + mov ax,[keynum] + ret + + ENDP Mouse_KeyNum + +;---------------------------------------------------------------------------- + + +;---------------------------------------------------------------------------- +; MOUSE_INT: +; +; This routine is called automatically when the Mouse_Int is installed. It +; automatically updates the global variables stored in the code segment so +; that the mouse information is automatically known at all times. +; +; INPUTS (from int): AX = condition mask ( bit 0 == cursor position chg, +; bit 1 == left button press, +; bit 2 == left button release, +; bit 3 == right button press, +; bit 4 == right button release, +; 5-15 == not used ) +; BX = button state ( bit 0 == left button down, +; bit 1 == right button down, +; bit 2 == middle button down. +; 3-15 == not used ) +; CX = cursor coordinate (horizontal axis) +; DX = cursor coordinate (vertical axis) +; DI = horizontal mouse count (mickeys) +; SI = vertical mouse count (mickeys) +; +; RETURNS: none +; +; MODIFIES: modifies the variables _Button, _ButtonChange, +; _MouseX,_MouseY,_ButtonLatch +; +; PROTOTYPE: +; This routine is called from an interrupt. +;---------------------------------------------------------------------------- +label RM_Mouse_Interrupt + PROC Mouse_Int C FAR + USES ax,bx,cx,dx,ds,si,es,di + LOCAL cond:WORD ; Local copy of mouse event. + LOCAL state:WORD ; Local copy of button state. + + mov [cs:Mouse_State],bx + mov [cs:Mouse_Cond],ax + + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ; At this point we do not know if the SS selector is a + ; System Stack or the Application Stack pointer. + ; Soo to be in the safe side we create our own local + ; Stack Pointer Selector Relative to DS + ; Note Do not try this trick in a reentrant interrupt + mov bx, ss ; get SS + mov [Mouse_App_Stack_ES], sp ; Protect ES + mov [Mouse_App_Stack_SS], bx ; Protect SS + lea bx, [Mouse_StackStart ] ; Compute Local Stack size + and bx, -2; + cli ; Disable All interrupts + mov ss, ax ; Set new SS Selector + mov sp, bx ; Set new Stack Offset + sti ; Enable Interrupts + +;------ Process the mouse interrupt only if the mouse is enabled (whether +; present or not). + cmp [MDisabled],0 + jne ??exit + + cmp [MInput],0 + je ??exit + + +;------ This was added because of missing mouse presses and +; releases during a mouse update. + mov ax,[Mouse_Cond] + and ax,0001EH ; bits for left and right press and release + jne short ??dopress_release + + cmp [MouseUpdate],0 ; if mouse move and mouse updating exit + jne ??exit + +??dopress_release: + +;------ In EEGA mode mouse X coordinates as 0..639. Make adjustment +; to keep within 0..319 range. + cmp [Adjust],1 ; if the x coordinate is returned + jne short ??noadjust ; incorrectly then + shr cx,1 ; adjust x coord from 640 pixel screen +??noadjust: + + +; scale mouse posX and PosY +; cmp [Adjust_XPos] , 0 +; jz short ??no_scaleX +; push dx +; mov ax , [MouseRight] +; imul cx +; idiv [Adjust_XPos] +; mov cx , ax +; pop dx +??no_scaleX: +; cmp [Adjust_YPos] , 0 +; jz short ??no_scaleY +; mov ax , [MouseBottom] +; imul dx +; idiv [Adjust_YPos] +; mov dx , ax +??no_scaleY: + +;------ Keep mouse within screen bounds. + cmp cx,[MouseRight] ; in EGAMODE, the mouse may go to 320 + jb short ??boundX_ok ; force it to stay at least one pixel + mov cx,[MouseRight] ; on the screen + dec cx +??boundX_ok: + cmp dx,[MouseBottom] ; in EGAMODE, the mouse may go to 320 + jb short ??boundY_ok ; force it to stay at least one pixel + mov dx,[MouseBottom] ; on the screen + dec dx +??boundY_ok: + + IF 0 +;------ Remap the middle button to equal the right button. + test bx,04h + je ??noremap + or bx,0010b ; Set the right button bit. +??noremap: + ENDIF + + mov [MouseX],cx ; and store in mouse x + mov [MouseY],dx ; store y coord in mouse y + test [KeyFlags],KEYMOUSE + jne short ??nostuffit + + call Mouse_KeyNum C,[Mouse_State] ; Convert mouse state to key number code. + call Stuff_Key_Num C,ax ; Record mouse keynumber code. +??nostuffit: + + + +;------ The check for Mouse in the middle of updating CAN NOT BE MOVED +; any farther up because mouse presses and releases will be LOST!! + cmp [MouseUpdate],0 + jne ??exit +;??jexit: +; jmp ??exit + + +christopher: +??chkxy: + +;------ Signal that no mouse updating can occur at this time. +; cmp [_MouseUpdate],0 +; jne ??exit +; mov [_MouseUpdate],1 + +;------ Perform any X movement grid adjustment. + cmp [MouseStepX],0 ; are we stepping on the X? + je short ??no_x_step ; no x + mov ax,cx ; get current x_pixel + mov cx,dx ; save dx - it is trashed by idiv + sub ax,[MouseOffsetX] ; get offset difference + mov bx,[MouseStepX] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step X + imul bx ; ax = div * Step X + add ax,[MouseOffsetX] ; normalize to region offset + mov dx,cx ; restore dx (new MouseY) + mov cx,ax ; set cx (new MouseX) +??no_x_step: + +;------ Perform any Y movement grid adjustment. + cmp [MouseStepY],0 ; are we stepping on the Y + je short ??no_step ; no y + mov ax,dx ; get current y_pixel + sub ax,[MouseOffsetY] ; get offset difference + mov bx,[MouseStepY] ; get step in bx for idiv + cwd ; extend ax -> dx:ax + idiv bx ; divide by Step Y + imul bx ; ax = div * Step Y + add ax,[MouseOffsetY] ; normalize to region offset + mov dx,ax ; set dx (new MouseY) +??no_step: + +;------ Here is where we store the new MouseX and MouseY values +; mov [MouseX],cx ; and store in mouse x +; mov [MouseY],dx ; store y coord in mouse y + +;------ If the mouse is hidden or its position hasn't changed, then +; perform no action. + cmp [MState],0 + jne short ??updateend + cmp [MouseXOld],cx + jne short ??doit + cmp [MouseYOld],dx + je short ??updateend +??doit: + +;------ At this point we KNOW the mouse has moved. + mov ax,[MCState] + and ax,CONDHIDE+CONDHIDDEN + cmp ax,CONDHIDE+CONDHIDDEN + je short ??condcheck ; If already hidden. + +;------ We know that the mouse is visible, we must hide it +; before we update its position. + call Low_Hide_Mouse + +;------ Conditional region check goes here. If the mouse falls within the +; conditional region, it gets marked as hidden and no other processing +; occurs. + test [MCState],CONDHIDE + je short ??condok + +??condcheck: + cmp cx,[MouseCXLeft] ; check adjusted x region + jb short ??condok + cmp cx,[MouseCXRight] + ja short ??condok + cmp dx,[MouseCYUpper] ; check adjusted y region + jb short ??condok + cmp dx,[MouseCYLower] + ja short ??condok + + or [MCState],CONDHIDDEN ; flag as conditional hidden + jmp short ??updateend + +;------ The mouse coordinates and flags pass all of the tests, proceed +; with rendering the mouse. +??condok: + call Low_Show_Mouse + +;------ Final clean up and exit. +??updateend: + mov [MouseXOld],cx + mov [MouseYOld],dx + +??exit: + cmp [Mouse_StackPointer],0DEADh + je ??mouse_stk_ok + push cx + push di + push ax + push es + mov ax,0A000h + mov es,ax + xor di,di + mov cx,64000 + mov ax,1 + rep stosb + pop es + pop ax + pop di + pop cx + +??mouse_stk_ok: + ; Restore System Stack Pointer Selector before exit Interrupt + mov ax, [Mouse_App_Stack_SS] + cli ; disable Interrupts + mov ss, ax ; Get Syatem Stack Selector + mov sp, [Mouse_App_Stack_ES] ; Get Syatem Stack offset + sti ; Enable Interrupts + ret + ENDP Mouse_Int + + + +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:FAR + PROC VGA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + push bp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov bp , cx +??restore_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop bp + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 10 +mov es, ax +mov al , 'V' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg si,di ; xchg the source and the dest + mov bp , cx + push es ; need to swap es and ds but + push ds ; cant xchg so pop them on the + pop es ; stack and pop them off the + pop ds ; wrong way intentionally. +??store_loop: + mov cx,ax ; get number to really write + rep movsb ; store them into the buffer + add si,dx ; move past right clipped pixels + add di,bx ; adjust dest to next line + dec bp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop bp +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 12 +mov es, ax +mov al , 'G' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret + + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** + + GLOBAL C VGA_Draw_Mouse:FAR + PROC VGA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov bx , [MouseRight] ; get width into register + mov [ x1 ] , bx +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov bx , [MouseBottom] ; get width into register + mov [ y1 ] , bx + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + mov dx , [MouseRight] + mov di , ax + add di , [ x0 ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc esi + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 12 * 80 + 14 +mov es, ax +mov al , 'A' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + ret +ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:FAR + PROC VESA_Mouse_Shadow_Buffer C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG store:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov ax,[MouseBuffX] + mov bx,[MouseBuffY] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax , [MouseBuffW] + add bx , [MouseBuffH] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + mov ax , [ word ptr MouseBuffer ] + mov [ buffy0 ] , ax + +;*=================================================================== +;* Bounds check source X. Y. +;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseBuffW] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov ax,0A000h + mov es,ax + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + + + mov bx , [ MouseWidth ] ; turn this into an offset + lds si , [ MouseBuffer ] + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??restore_loop: + mov ah,al +??res_inner_loop: + mov ch , [si] + mov [es:di],ch + inc si + inc di + jnz ??res_same_page + call next_vesa_page + ??res_same_page: + dec ah + jnz ??res_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??res_same_page1 + call next_vesa_page + ??res_same_page1: + dec cl ; decrement number of rows to do + jnz ??restore_loop + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 10 +mov es,ax +mov al ,'v' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + jmp ??out + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov ah,al +??store_inner_loop: + mov ch , [es:di] + mov [si],ch + inc si + inc di + jnz ??store_same_page + call next_vesa_page + ??store_same_page: + dec ah + jnz ??store_inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??store_same_page1 + call next_vesa_page + ??store_same_page1: + dec cl ; decrement number of rows to do + jnz ??store_entry +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es,ax +mov al ,'e' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + ENDP VESA_Mouse_Shadow_Buffer + + + +;*************************************************************************** + + GLOBAL C VESA_Draw_Mouse:FAR + PROC VESA_Draw_Mouse C FAR + USES ax,bx,cx,dx,di,si,ds,es + ARG mousex:WORD + ARG mousey:WORD + + local x0 : word + local y0 : word + local x1 : word + local y1 : word + local buffx0 : word + local buffy0 : word + local app_vesa_window : word + + + ;*=========================================================================* + ;* Since we are in tiny model point ds to cs + ;*=========================================================================* + mov ax,cs ; since we are in tiny model + mov ds,ax ; set cs = ds + + call get_vesa_window + mov [ app_vesa_window ] , dx + + + ;*=================================================================== + ;* Pre-initialize the left, right and topclip values to zero. + ;*=================================================================== + mov ax, [ mousex ] + mov bx , [ mousey ] + sub ax , [ MouseXHot ] + sub bx , [ MouseYHot ] + + mov [ x0 ] , ax + mov [ y0 ] , bx + add ax, [ MouseWidth ] + add bx, [ MouseHeight ] + mov [ x1 ] , ax + mov [ y1 ] , bx + + mov [ buffx0 ] , 0 + les ax , [ MouseCursor ] + mov [ buffy0 ] , ax + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + + xor ax , ax + xor dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ x0 ] + mov bx , [ x1 ] + sub cx , [ MouseRight ] + sub bx , [ MouseRight ] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + mov cx , [ y0 ] + mov bx , [ y1 ] + sub cx , [MouseBottom] + sub bx , [MouseBottom] + dec cx + dec bx + add cx , cx + adc ax , ax + add bx , bx + adc dx , dx + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??acepted + + + test ah , 1000b + jz ??scr_left_ok + mov bx , [ x0 ] + neg bx + mov [ buffx0 ] , bx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + push dx + mov ax , [ y0 ] + neg ax + mul [MouseWidth] + add [ buffy0 ] , ax + mov [ y0 ] , 0 + pop dx + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov ax , [MouseRight] ; get width into register + mov [ x1 ] , ax +??scr_right_ok: + test dl , 0001b + jz ??acepted + mov ax , [MouseBottom] ; get width into register + mov [ y1 ] , ax + +??acepted: + + mov ax , [ y0 ] + mul [ MouseRight ] + + add ax , [ x0 ] + adc dx , 0 + mov di , ax + call set_vesa_page + + mov dx , [MouseRight] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov bx , [MouseWidth] ; turn this into an offset + mov si , [ buffy0 ] ; edx points to source + add si , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + mov ax , 0a000h + mov ds , ax + mov ax , [ x1 ] + mov cx , [ y1 ] + sub ax , [ x0 ] + jle ??out + sub cx , [ y0 ] + jle ??out + + sub dx , ax + sub bx , ax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch , [es:si] + inc si + or ch,ch + jz ??inc_edi + mov [di],ch +??inc_edi: + inc di + jnz ??same_page + call next_vesa_page + ??same_page: + dec ah + jnz ??inner_loop + add si,bx ; move past right clipped pixels + add di,dx ; adjust dest to next line + jnc ??same_page1 + call next_vesa_page + ??same_page1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + +IF ECHOON +mov ax , 0b000h +mov di , 10 * 80 + 14 +mov es, ax +mov al , 's' +mov [es:di],al +mov al,2 +mov [es:di+1],al +mov di , 10 * 80 + 16 +mov al , 'a' +mov [es:di],al +mov al,2 +mov [es:di+1],al +ENDIF + mov dx , [ app_vesa_window ] + call set_vesa_window + ret + + +ENDP VESA_Draw_Mouse + + + +;************************************************************************ + +PROC get_vesa_window C near +uses ax,bx + mov ax , 04f05h + mov bh , 1 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + +;************************************************************************ + +PROC set_vesa_window C near +uses ax,bx,dx + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP + + +;*************************************************************************** + +PROC set_vesa_page C near +USES ax,bx,dx + + mov bx , dx + shl bx , 2 + mov [ cs: current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP set_vesa_page + +PROC next_vesa_page C near +USES ax,bx,dx + mov bx , [ cs: current_page ] + add bx , 4 + mov [ cs:current_page ] , bx + mov dx , [ word ptr cs:banktable + bx ] + mov ax , 04f05h + mov bh , 0 + mov bl , 0 + call [ dword ptr cs: VesaPtr ] + ret +ENDP next_vesa_page + + + +;*********************************************************** +END diff --git a/WWFLAT32/SRCDEBUG/MEM.CPP b/WWFLAT32/SRCDEBUG/MEM.CPP new file mode 100644 index 0000000..562ca0d --- /dev/null +++ b/WWFLAT32/SRCDEBUG/MEM.CPP @@ -0,0 +1,1091 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Westwood Library * + * * + * File Name : MEM.C * + * * + * Programmer : Joe L. Bostic * + * Scott K. Bowen * + * * + * Start Date : March 31, 1993 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Mem_Free -- Free a block of memory from system. * + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * Mem_Init -- Initialize the private memory allocation pool. * + * Mem_Reference -- Updates the reference time for the specified memory blo* + * Mem_Find -- Returns with pointer to specified memory block. * + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * Mem_Avail -- Returns the amount of free memory available in the cache.* + * Mem_Cleanup -- Performes a garbage collection on the memory cache. * + * MemNode_Unlink -- Unlinks a node from the cache. * + * MemNode_Insert -- Inserts a node into a cache chain. * + * Mem_Largest_Avail -- Largest free block available. * + * Mem_Lock_Block -- Locks a block so that it cannot be moved in cleanup.* + * Mem_In_Use -- Makes it so a block will never be returned as oldest* + * Mem_Pool_Size -- Returns total amount of memory in pool. * + * Mem_Get_ID -- Returns ID of node. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "wwmem.h" +#include + +#include +#include + +extern TimerClass TickCount; + +#define DEBUG_FILL FALSE + +//////////////////////////////////////////////////////////////////////////// + + + +/******************************************************************************* +** A allocated block may have one of three meanings in the Time field. The first +** is the time stamp of the last time it was referenced. The other two values +** are defined below. MEM_BLOCK_IN_USE means that it will never be returned as the +** oldest since there is no valid time stamp. LOCKED_BLOCK has the same meaning as +** MEM_BLOCK_IN_USE with the added feature that the block will not be moved in a +** Mem_Cleanup(). Therefore, there may be some fragmentation after the cleanup +** if any blocks are LOCKED. It would be good practice to seldomly lock blocks, +** for instance, only when a sample is being played. +** WARNING: If these values change to anything else, logic will need to be changed +** in Mem_Find_Oldest since it relies on these being small values. +*/ +#define MEM_BLOCK_IN_USE 0x00 +#define MEM_BLOCK_LOCKED 0x01 + +/* +** Each block of memory in the pool is headed by this structure. +*/ +typedef struct MemChain { + struct MemChain *Next; // Pointer to next memory chain node. + struct MemChain *Prev; // Pointer to previous memory chain node. + unsigned long ID; // ID number of block. + unsigned short Time; // TickCount of latest reference. + unsigned short Size; // Size of memory block (in paragraphs). +} MemChain_Type; + + +/* +** Holding tank memory management data. +*/ +typedef struct MemPool { + MemChain_Type *FreeChain; // Pointer to first node in free chain. + MemChain_Type *UsedChain; // Pointer to first node in used chain. + unsigned short FreeMem; // Current amount of free ram (in paragraphs). + unsigned short TotalMem; // Total quantity of memory. + long pad2; +} MemPool_Type; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node); +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge); + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * Mem_Init -- Initialize the private memory allocation pool. * + * * + * This routine is used to initialize the private memory allocation * + * pool. * + * * + * INPUT: buffer -- Pointer to the buffer that is the allocation pool. * + * * + * size -- Size of the buffer in bytes. * + * * + * OUTPUT: TRUE/FALSE; Was it initialized successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Init(void *buffer, long size) +{ + MemChain_Type *mem; // Working memory chain node. + MemPool_Type *pool; // Memory pool control structure. + + /* + ** The buffer is rounded down to the nearest paragraph. + */ + size = size & 0xFFFFFFF0L; + + if (!buffer || !size) return(FALSE); + + /* + ** Initialize the pool control structure. + */ + pool = (MemPool_Type *)buffer; + pool->FreeMem = (size - sizeof(MemPool_Type)) >> 4; + pool->UsedChain = NULL; + pool->TotalMem = pool->FreeMem; + mem = pool->FreeChain = (MemChain_Type *) (pool + 1); + + /* + ** Initialize the free memory chain. + */ + mem->Next = NULL; + mem->Prev = NULL; + mem->Size = pool->FreeMem; + mem->ID = -1; + mem->Time = 0; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Alloc -- Allocate a block of memory from the special memory pool. * + * * + * This routine will allocate a block of memory from the special * + * memory allocation pool. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * size -- The size of the memory block to allocate. * + * * + * id -- ID number to give this memory block. * + * * + * OUTPUT: Returns with a pointer to the allocated block. If there was * + * insufficient room, then NULL is returned. * + * * + * WARNINGS: Be sure to check for the NULL return case. * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id) +{ + MemPool_Type *pool; + MemChain_Type *node; // Pointer to current memory node. + unsigned int remainder=0; // Remaining bytes that are still free. + int found; + int size; // Paragraph size of allocation. + + + /* + ** If there is no free memory then the allocation will + ** always fail. + */ + if (!poolptr || !lsize) return(NULL); + pool = (MemPool_Type *) poolptr; + + /* + ** Allocations are forced to be paragraph sized. + */ + lsize += sizeof(MemChain_Type); // Account for header. + lsize = (lsize + 0x0FL) & 0xFFFFFFF0L; + size = (int)(lsize >> 4); + + /* + ** If the total free is less than the size of the desired allocation, + ** then we KNOW that an allocation will fail -- just return. + */ + if (pool->TotalMem < size) { + return(NULL); + } + + /* + ** Walk down free chain looking for the first block that will + ** accomodate the allocation. + */ + node = pool->FreeChain; + found = FALSE; + while (!found && node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + found = TRUE; + break; + } + node = node->Next; + } + if (!found) { + return(NULL); + } + + /* + ** Determine if this allocation would split the block. + */ + remainder = node->Size - size; + + /* + ** If only a very small free chunk would remain, just tack it on + ** to the current allocation. + */ + if (remainder <= 2) { + remainder = 0; + size = node->Size; + } + + /* + ** Remove the primary block from the free memory list. + */ + MemNode_Unlink(pool, TRUE, node); + + /* + ** If a smaller block remains, then link it back into + ** the free memory list. + */ + if (remainder) { + MemNode_Insert(pool, TRUE, (MemChain_Type *)Add_Long_To_Pointer(node, (long)size << 4), remainder, -1, FALSE); + } + + /* + ** Link in the allocated node into the used memory list. + */ + MemNode_Insert(pool, FALSE, node, size, id, FALSE); + + /* + ** Reflect the change to the total free count. + */ + pool->FreeMem -= size; + + /* + ** Return a pointer to the block of allocated memory just past + ** the header. + */ + +#if DEBUG_FILL + memset(node + 1, id, (size-1) << 4); +#endif + return((void *) (node + 1)); +} + + +/*************************************************************************** + * Mem_Free -- Free a block of memory from system. * + * * + * This routine will free a block of memory from the special memory * + * buffer. * + * * + * INPUT: poolptr -- Pointer to the memory pool base address. * + * * + * buffer -- Pointer to memory block to free. * + * * + * OUTPUT: TRUE/FALSE; Was the deallocation successful? * + * * + * WARNINGS: Be sure to only pass in to this routine a buffer that was * + * returned from Mem_Alloc(). * + * * + * HISTORY: * + * 03/31/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +int Mem_Free(void *poolptr, void *buffer) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Copy of current memory node. + unsigned int size; // Size of the block being freed. + + /* + ** One can't free what isn't there. + */ + if (!buffer || !poolptr) { + return(FALSE); + } + pool = (MemPool_Type *) poolptr; + + /* + ** The node pointer is actually back a bit from the "normal" pointer. + */ + node = (MemChain_Type *) buffer; + node--; + + /* + ** Get pointer to actual allocated node and unlink it from the used + ** memory chain. + */ + size = node->Size; + MemNode_Unlink(pool, FALSE, node); + MemNode_Insert(pool, TRUE, node, size, -1, TRUE); + + /* + ** Reflect the new free memory into the total memory count. + */ + pool->FreeMem += size; + + return(TRUE); +} + + +/*************************************************************************** + * Mem_Reference -- Updates the reference time for the specified memory blo* + * * + * This routine is used to update the memory reference time for the * + * specified memory node. Typically, this is called every time a * + * memory block is used in order to make sure the memory block time * + * tracking (Last Recently Used) system works properly. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: The node pointer must be valid. For maximum safety this * + * routine should be called right after Mem_Find(). * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Reference(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + + nodeptr->Time = (unsigned short)(TickCount.Time() >> 4); + +} + + +/*************************************************************************** + * MEM_LOCK_BLOCK -- Locks a block so that it cannot be moved in cleanup. * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It also makes it so that the block * + * will never be moved during a Cleanup process. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: If one or more blocks are locked in a heap, Mem_Avail might * + * not equal Mem_Largest_Avail after a call to Mem_Cleanup. * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_Lock_Block(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node; + nodeptr--; + nodeptr->Time = MEM_BLOCK_LOCKED; +} + + +/*************************************************************************** + * MEM_IN_USE -- Makes it so a block will never be returned as oldest * + * By marking a memory block in use, the memory system will never return* + * it as the oldest memory block. It still can be moved in the Cleanup * + * code. * + * * + * INPUT: node -- Pointer to memory block returned from Mem_Find. * + * * + * OUTPUT: none * + * * + * WARNINGS: Mem_Find_Oldest() will return NULL if only IN_USE blocks are * + * in memory. * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +void Mem_In_Use(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return; + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + nodeptr->Time = MEM_BLOCK_IN_USE; +} + + +/*************************************************************************** + * Mem_Find -- Returns with pointer to specified memory block. * + * * + * Use this routine to convert a memory ID value into an actual memory * + * pointer. It sweeps through all of the 'cached' memory blocks and * + * returns with the matching block pointer. * + * * + * INPUT: poolptr -- Pointer to the memory cache block. * + * * + * id -- The ID of the block desired. * + * * + * OUTPUT: Returns with the pointer to the memory block. If NULL is * + * returned then the desired block is not in the memory cache. * + * * + * WARNINGS: This routine may return NULL if the memory block is not * + * present in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Find(void *poolptr, unsigned long id) +{ + MemPool_Type *pool; // pointer to structure. + MemChain_Type *node; // Working node structure. + + if (!poolptr) return(NULL); + + pool = (MemPool_Type *) poolptr; + + /* + ** Cannot free a node that is not on the UsedChain list. + */ + if (!pool->UsedChain) { + return(NULL); + } + + /* + ** Sweep through entire allocation chain to find + ** the one with the matching ID. + */ + node = pool->UsedChain; + while (node) { + + if (node->ID == id) { + return(node + 1); + } + node = node->Next; + } + return(NULL); +} + + +/*************************************************************************** + * MEM_GET_ID -- Returns ID of node. * + * * + * INPUT: void *node - pointer to node. * + * * + * OUTPUT: The ID of the node that was supplied by user during Mem_Alloc().* + * * + * WARNINGS: pointer to node must be one that Mem_Alloc or * + * Mem_Find returned. ** + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +unsigned long Mem_Get_ID(void *node) +{ + MemChain_Type *nodeptr; // Pointer of current memory node. + + if (!node) return (0L); + + // Get to the node header. + nodeptr = (MemChain_Type *) node - 1; + return (nodeptr->ID); +} + + +/*************************************************************************** + * Mem_Find_Oldest -- Returns with the memory block with the oldest time st* + * * + * Use this routine to find the memory block with the oldest time stamp * + * value. Typically, this is used when freeing memory blocks in the * + * cache in order to make room for a new memory block. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the pointer to the oldest memory block. If NULL * + * is returned, then the memory cache is empty. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Optimized for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + * 04/15/1994 SKB : Handle time wrap, locked blocks, and no_refenece blocks* + *=========================================================================*/ +void *Mem_Find_Oldest(void *poolptr) +{ + MemChain_Type *node; // Working node pointer. + MemChain_Type *oldnode; // Pointer to oldest block. + unsigned int oldtime; // Time of oldest block. + unsigned int basetime; // Time to mark our base time with. + unsigned int time; // basetime + time of node. + + if (!poolptr) return(NULL); + + /* + ** Sweep through entire allocation chain to find + ** the oldest referenced memory block. + */ + oldnode = NULL; + oldtime = 0; + node = ((MemPool_Type*) poolptr)->UsedChain; + + basetime = (unsigned int)(TickCount.Time() >> 4); + + while (node) { + + /* + ** Don't allow MEM_BLOCK_IN_USE or MEM_BLOCK_LOCKED to be returned. + */ + if (node->Time > MEM_BLOCK_LOCKED) { + + /* + ** Adjust time for wrap around (after about 5 hrs). + ** times less then the base time will wrap up high while + ** and times greater then base time will then be lower since + ** any time greater has been on the thing a long time. + */ + time = node->Time - basetime ; + + if (time < oldtime || !oldnode) { + oldtime = time; + oldnode = node; + } + } + node = node->Next; + } + + /* + ** Return with the value that matches the pointer that + ** was allocated by the system previously. + */ + if (oldnode) { + oldnode++; + } + return(oldnode); +} + + +/*************************************************************************** + * Mem_Free_Oldest -- Find and free the oldest memory block. * + * * + * This routine is used to free the oldest memory block in the memory * + * cache. This routine is typcially used in order to create more room * + * in the cache for a new allocation. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns with the node that it freed. Although this node is * + * is no longer valid, it may be used to mark that pointer as * + * invalid in the main code. * + * * + * WARNINGS: If this routine returns NULL, then no memory was freed. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void *Mem_Free_Oldest(void *poolptr) +{ + MemChain_Type *node; // Copy of pointer to oldest node. + + if (!poolptr) return(NULL); + node = (MemChain *) Mem_Find_Oldest(poolptr); + if (Mem_Free(poolptr, node)) { + return(node); + } + return(NULL); +} + +/*************************************************************************** + * MEM_POOL_SIZE -- Returns total amount of memory in pool. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: long total size of pool. i.e. largest possible allocation if * + * no memory was allocated. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Pool_Size(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->TotalMem) << 4; + memtotal -= sizeof(MemChain_Type); + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + +/*************************************************************************** + * Mem_Avail -- Returns the amount of free memory available in the cache. * + * * + * This routine examines the memory cache and returns the amount of * + * free memory available. This memory total MAY be fragmented but * + * after Mem_Cleanup() is called, an allocation of the amount returned * + * by this function is guaranteed. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns the largest allocation possible from the memory cache. * + * * + * WARNINGS: The value returned may represent the FRAGMENTED total * + * amount of memory free in the cache. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +long Mem_Avail(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + long memtotal; // Total amount of memory free. + + if (!poolptr) return(NULL); + pool = (MemPool_Type *) poolptr; + + memtotal = ((long)pool->FreeMem) << 4; + memtotal -= sizeof(MemChain_Type); + //memtotal -= sizeof(MemChain_Type) + 15; + memtotal = MAX(memtotal, (long)0); + return(memtotal); +} + + +/*************************************************************************** + * MEM_LARGEST_AVAIL -- Largest free block available. * + * This routine examines the free node list to find the largest block * + * available. User can Mem_Alloc() this return size successfully. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: Returns largest allocation currently possible from the cache. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/15/1994 SKB : Created. * + *=========================================================================*/ +long Mem_Largest_Avail(void *poolptr) +{ + MemChain_Type *node; // Pointer to current memory node. + unsigned int size; + long truesize; + + /* + ** Make sure that it is a buffer. + */ + if (!poolptr) return(NULL); + + /* + ** Go through the entire free chain looking for the largest block. + */ + node = ((MemPool_Type *)poolptr)->FreeChain; + size = 0; + while (node) { + + /* + ** Fetch free memory chunk block and see if it is big enough. + */ + if (node->Size >= size) { + size = node->Size; + } + node = node->Next; + } + + truesize = (long)size << 4; + truesize -= sizeof(MemChain_Type); + truesize = MAX(truesize, 0L); + return (truesize); +} + + +/*************************************************************************** + * Mem_Cleanup -- Performs a garbage collection on the memory cache. * + * * + * This routine is used to coalesce all adjacent free blocks of * + * memory in the specified cache. As a result, all previous pointers * + * provided by Mem_Find() are invalidated. This routine consumes a * + * fair amount of time and should be called as infrequently as * + * possible. * + * * + * INPUT: poolptr -- Pointer to the memory cache. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine takes a significant amount of time! * + * If there are locked block in memory, the pool may still * + * be fragmented. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 08/06/1993 JLB : Updated for low memory caches. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +void Mem_Cleanup(void *poolptr) +{ + MemPool_Type *pool; // Memory pool control structure. + MemChain_Type *free, // Pointer to first free area. + *cur; // Pointer to first used block that is after free. + unsigned long size; + unsigned long freesize;// Size of free heap at the end of the block. + + if (!poolptr) return; + + /* + ** Fetch working copy of pool control structure. + */ + pool = (MemPool_Type *) poolptr; + + /* + ** Basic parameter and condition legality checks. If the memory pool + ** has no free space, no free blocks, or no allocated blocks, then + ** memory cleanup is unnecessary -- just exit. + */ + if (!pool->FreeMem || !pool->FreeChain || !pool->UsedChain) return; + + freesize = pool->FreeMem; + free = pool->FreeChain; + pool->FreeChain = NULL; + cur = pool->UsedChain; + while (TRUE) { + + /* + ** Setup pointers so that free points to the first free block and cur + ** points to the next used block after the free block. + */ + while (cur < free && cur) { + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + + /* + ** Do not allow a locked block to be moved. + */ + if (cur->Time == MEM_BLOCK_LOCKED) { + /* + ** Figure the size of the new free block that we are creating. + ** Subtract off the total block size. + ** Add the node to the free list. + */ + size = (char *) cur - (char *) free; + size >>= 4; + freesize -= size; + MemNode_Insert(pool, TRUE, free, (unsigned int) size, -1, FALSE); + + /* + ** Time to find a new free position to start working from. + ** Cur will be in the position just following. + */ + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + while (free == cur) { + free = (MemChain_Type *) Add_Long_To_Pointer(cur, (unsigned long)cur->Size << 4); + cur = cur->Next; + } + + // All used blocks are at the front of the free. We are done. + if (!cur) { + break; + } + } else { + + // Copy the block up. + size = (unsigned long)cur->Size << 4; + Mem_Copy(cur, free, size); + cur = free; + + // Change pointers of surrounding blocks. + if (cur->Next) { + cur->Next->Prev = cur; + } + if (cur->Prev) { + cur->Prev->Next = cur; + } else { + pool->UsedChain = cur; + } + + // Change to next new free area. + free = (MemChain_Type *) Add_Long_To_Pointer(cur, size); + } + } + + /* + ** Now build the single free chunk. + */ + MemNode_Insert(pool, TRUE, free, freesize, -1, FALSE); +} + + +/*************************************************************************** + * MemNode_Unlink -- Unlinks a node from the cache. * + * * + * A private routine the actually unlinks a memory block from the * + * memory cache. It doesn't perform a complete update of the memory * + * cache. * + * * + * INPUT: pool -- Pointer to the memory cache header (copy in real * + * memory). * + * * + * freechain-- Is the block part of the free memory chain? * + * * + * node -- Pointer to the node that will be unlinked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't update memory totals. It is a support * + * function. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + * 04/13/1994 SKB : Update for 32 bit library, removed XMS calls, * + * optimized for low memory only. * + *=========================================================================*/ +PRIVATE void MemNode_Unlink(MemPool_Type *pool, int freechain, MemChain_Type *node) +{ + MemChain_Type *other; // Copy of node data to unlink. + MemChain_Type **chain; // A pointer to one of the chains pointer. + + /* + ** Check for parameter validity. + */ + if (!pool || !node) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Make adjustments to the previous node. If the pointer + ** to the previous node is NULL then this indicates the + ** first node in the list and thus the chain pointer needs + ** to be updated instead. + */ + if (node->Prev) { + other = node->Prev; + other->Next = node->Next; + } else { + *chain = node->Next; + } + + if (node->Next) { + other = node->Next; + other->Prev = node->Prev; + } +} + + +/*************************************************************************** + * MemNode_Insert -- Inserts a node into a cache chain. * + * * + * This routine is used to add a node to a cache chain. Since nodes * + * do not contain double links, they must be placed in sequence. * + * * + * INPUT: pool -- Pointer to memory pool (must be in real memory). * + * * + * freechain-- Is the node to be inserted into the free chain? * + * * + * node -- Pointer to the node to insert. * + * * + * size -- Size of the memory block (in paragraphs). * + * * + * id -- The ID number to associate with this block. * + * * + * merge -- Merge inserted block with adjacent blocks. * + * * + * OUTPUT: return * + * * + * WARNINGS: This is a support routine. * + * * + * HISTORY: * + * 08/06/1993 JLB : Created. * + *=========================================================================*/ +PRIVATE void MemNode_Insert(MemPool_Type *pool, int freechain, MemChain_Type *node, unsigned int size, unsigned long id, int merge) +{ + MemChain_Type **chain; // Pointer to chain that will be linked. + MemChain_Type *prev, // Successor node pointer. + *next; // Predecessor node pointer. + int doit=TRUE; // Link the node into the list. + + + /* + ** Determine if the parameters are valid. + */ + if (!pool || !node || !size) return; + + /* + ** Setup working pointer for the particular chain desired. + */ + if (freechain) { + chain = &pool->FreeChain; + } else { + chain = &pool->UsedChain; + } + + /* + ** Handle the "no node in list" condition (easiest). + */ + if (!*chain) { + node->Next = NULL; + node->Prev = NULL; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + *chain = node; + return; + } + + /* + ** Sweep through the memory chain looking for a likely spot + ** to insert the new node. It will stop with "next" pointing + ** to the node to come after the block to be inserted and "prev" + ** will point to the node right before. + */ + prev = NULL; + next = *chain; + while (next && (next < node)) { + + /* + ** Move up the memory chain. + */ + prev = next; + next = next->Next; + } + + /* + ** Coallescing of adjacent blocks (if requested). + */ + if (merge) { + + /* + ** If the previous block is touching the block to insert + ** then merely adjust the size of the previous block and + ** that is all that is necessary. + */ + if (prev) { + if (((char *)prev + ((long)prev->Size << 4)) == ((char *) node)) { + prev->Size += size; + size = prev->Size; + node = prev; + prev = prev->Prev; + doit = FALSE; + } + } + + /* + ** If the following block is touching the block to insert + ** then remove the following block and increase the size of + ** the original insertion block by the size of the other + ** block. + */ + if (next) { + if (((char *)node + ((long)size << 4)) == (char *)next) { + + if (!doit) { + + /* + ** If the node was already merged with the previous block + ** then merely increase the previous block's size + ** and adjust it's next pointer appropriately. + */ + node->Size += next->Size; + node->Next = next->Next; + next = next->Next; + } else { + + /* + ** Increase the size of the current block and adjust + ** the "next" pointer so that it gets fixed up + ** accordingly. + */ + size += next->Size; + next = next->Next; + } + } + } + } + +#if DEBUG_FILL + if (doit) { + memset(node + 1, 0xFF, (size - 1) << 4); + } else { + memset(node + 1, 0xFF, (node->Size - 1) << 4); + } +#endif + + /* + ** Fixup the node pointers. + */ + if (prev) { + prev->Next = node; + }else{ + *chain = node; + } + + if (next) { + next->Prev = node; + } + + if (doit) { + node->Prev = prev; + node->Next = next; + node->Size = size; + node->Time = (unsigned short)(TickCount.Time() >> 4); + node->ID = id; + } +} + + + + + + diff --git a/WWFLAT32/SRCDEBUG/MEM_COPY.ASM b/WWFLAT32/SRCDEBUG/MEM_COPY.ASM new file mode 100644 index 0000000..01fb8be --- /dev/null +++ b/WWFLAT32/SRCDEBUG/MEM_COPY.ASM @@ -0,0 +1,184 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : MEM_COPY.ASM * +;* * +;* Programmer : Scott Bowen * +;* * +;* Last Update : September 8, 1994 [IML] * +;* Ported to watcom c32 : 01/03/96 [JRJ] * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Mem_Copy -- Copies from one pointer to another. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + + +LOCALS ?? + + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. +OPTIMAL_BYTE_COPY equ 14 + + +;****************************************************************************** +; External declares so these functions can be called +; +GLOBAL Mem_Copy : NEAR +GLOBAL Largest_Mem_Block : near + +CODESEG + + +;*************************************************************************** +;* MEM_COPY -- Copies from one pointer to another. * +;* This routine copies bytes from source to dest. It takes care of * +;* overlapped memory, and unsigned long copies. * +;* * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/18/1994 SKB : Created. * +;*=========================================================================* +; void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + + +PROC Mem_Copy C near + USES ecx , esi , edi , ebx + ARG source:DWORD + ARG dest:DWORD + ARG bytes:DWORD + +;********************************* Setup ****************************************** + cld + mov esi,[source] + mov edi,[dest] + mov ecx,[bytes] ; get number of bytes to copy. + + ; check pointers for singularities + cmp esi,edi ; Compare source with dest. + je ??done ; No sence in copying the same pointer. + or esi,0 + jz ??done + or edi,0 + jz ??done + + cmp ecx,OPTIMAL_BYTE_COPY ; see notes above about equate. + jge ??normal ; If >= MAX(2,OPTIMAL_BYTE_COPY), do normal dword copy. + +;******************************** Special case <= 2 ******************************* +; +; This section must be called for bytes <= 2 since the other case will crash. It +; optionally uses OPTIMAL_BYTE_COPY for the cut off point. This is because after +; extensive testing, it was proved that only at that point (14 or more bytes) does +; it become quicker to use the dword copy method. + + cmp esi,edi ; Compare source with dest. + jge ??do_move ; if source greater do forward copy. + lea esi,[esi+ecx-1] + std ; Opps, wrong, force the pointers to decrement. + lea edi,[edi+ecx-1] +??do_move: + rep movsb ; move the one or two bytes. + cld +??done: + ret + +;************************** back or forth, that is the question ******************* + +??normal: + mov ebx,ecx + cmp esi,edi ; Compare source with dest. + jge ??forward ; if source greater do forward copy. + +;********************************* Backward *************************************** +??backward: + lea ecx,[edi+ebx] + std + lea edi,[edi+ebx-1] + and ecx,3 ; Get non aligned bytes. + lea esi,[esi+ebx-1] + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + sub esi,3 + mov ecx,ebx ; Get number of bytes left. + sub edi,3 + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx,ebx + add esi,3 + add edi,3 + and ecx,03h + rep movsb ; finnish the remaining bytes. + cld + ret + +;********************************* Forward *************************************** +??forward: + cld + mov ecx,edi ; get destination pointer. + neg ecx + and ecx,3 ; Get non aligned bytes. + sub ebx,ecx ; remove that from the total size to be copied later. + rep movsb ; do the copy. + mov ecx,ebx ; Get number of bytes left. + shr ecx,2 ; Do 4 bytes at a time. + rep movsd ; do the dword copy. + mov ecx, ebx + and ecx,03h + rep movsb ; finnish the remaining bytes. + ret + +ENDP Mem_Copy + +PROC Largest_Mem_Block C near + uses esi , edi , ebx , ecx , edx + local mem_struct : dword : 16 + + mov eax , 0500h + lea edi , [ mem_struct ] + int 31h + mov eax , [ mem_struct ] + + ret +ENDP Largest_Mem_Block + +END + + + diff --git a/WWFLAT32/SRCDEBUG/MOUSE.ASM b/WWFLAT32/SRCDEBUG/MOUSE.ASM new file mode 100644 index 0000000..818fa9c --- /dev/null +++ b/WWFLAT32/SRCDEBUG/MOUSE.ASM @@ -0,0 +1,2226 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* (Mouse Routines) * +;* * +;* File Name : MOUSE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : August 26, 1994 * +;* * +;* Last Update : November 3, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Install_Mouse -- Initializes mouse driver and verifies mouse exists * +;* Remove_Mouse -- uninstalls the mouse interrupt by disabling the call * +;* Get_Mouse_X -- Returns the mouses x pixel position * +;* Get_Mouse_Y -- Returns the mouse's y pixel position * +;* Get_Mouse_Button -- Gets the values of the mouse button * +;* Set_Mouse_Cursor -- Sets the shape to be used as the mouse. * +;* Low_Hide_Mouse -- Low-level routine which hides the mouse * +;* Low_Show_Mouse -- Low level routine which shows the mouse * +;* Mouse_Shadow_Buffer -- Handles storing and restoring the mouse buffer * +;* Draw_Mouse -- Handles drawing the mouse cursor * +;* Hide_Mouse -- Hides mouse cursor on screen if it was show * +;* Show_Mouse -- Display mouse cursor on screen if it was hidden * +;* Conditional_Hide_Mouse -- Hides mouse if its with given region * +;* Conditional_Show_Mouse -- shows mouse if it was conditionally hidden * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +;WARN ; generate all warnings we can +;JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Include all of the keyboard specific defines +;--------------------------------------------------------------------------- +INCLUDE "keystruc.inc" +INCLUDE "shape.inc" +include "svgaprim.inc" + +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +RESTORE_VISIBLE_PAGE EQU 0 +STORE_VISIBLE_PAGE EQU 1 +CONDHIDE EQU 08000H ; bit for testing conditional region +CONDHIDDEN EQU 04000H ; bit for testing conditional hidden +;--------------------------------------------------------------------------- +; Define any data which needs to be global... +;--------------------------------------------------------------------------- + +GLOBAL RealModePtr:DWORD +GLOBAL MInstalled:DWORD +GLOBAL MouseBuffer:DWORD +GLOBAL LCW_Uncompress:NEAR + +GLOBAL Get_Shape_Uncomp_Size :NEAR +GLOBAL Get_Shape_Width :NEAR +GLOBAL Get_Shape_Original_Height :NEAR +GLOBAL Size_Of_Region :NEAR +GLOBAL _ShapeBuffer :DWORD + +GLOBAL XRes : dword +GLOBAL YRes : dword + + +GLOBAL VesaFunc : dword +GLOBAL Vesa_XRes : near +GLOBAL Vesa_YRes : near + + + + + +DPMI_INTR EQU 31h + + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? + dd ? + nothing dd ? +ENDS + + + +;--------------------------------------------------------------------------- +; Now lets handle any data in the data segment +;--------------------------------------------------------------------------- + DATASEG + +RealInt DPMI_REGS <> ; structure to call a real mode int +MInstalled DD 0 ; is the mouse installed? +MouseCursor DD 0 +MouseBuffer DD 0 +BufferWidth DD 0 +BufferHeight DD 0 +BufferSize DD 0 +PrevMousePtr DD 0 +MouseBufferSel DD 0 + + +Draw_Mouse dd VGA_Draw_Mouse +Mouse_Shadow_Buffer dd VGA_Mouse_Shadow_Buffer + +;--------------------------------------------------------------------------- +; Time to write some code +;--------------------------------------------------------------------------- + CODESEG + +;*************************************************************************** +;* INSTALL_MOUSE -- Initializes mouse driver and verifies mouse connected * +;* * +;* First check the mouse interupt to see if the vector is pointing to zero * +;* page. If it is not then make sure it is not pointing to an IRET. If * +;* not call the mouse reset to verify that the hardware is attached. * +;* * +;* INPUT: int mouse_max_width - the max width of the mouse cursor * +;* int mouse_max_height - the max height of the mouse cursor * +;* int screen_width - width of the physical screen * +;* int screen_height - height of the physical screen * +;* * +;* OUTPUT: TRUE is mouse driver is initialized, FALSE if unable to * +;* initialize mouse driver. * +;* * +;* PROTO: int Install_Mouse( int mouse_max_width, * +;* int mouse_max_height, * +;* int screen_width, * +;* int screen_height); * +;* * +;* HISTORY: * +;* 08/26/1994 PWG : Created. * +;*=========================================================================* +GLOBAL Install_Mouse:NEAR +PROC Install_Mouse C NEAR + USES ebx,ecx,edx,esi,edi + + ARG mouse_max_width:DWORD + ARG mouse_max_height:DWORD + ARG screen_width:DWORD + ARG screen_height:DWORD + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi,[RealModePtr] ; get offset of real mode data start + test esi , esi ; mouse driver should be install + jz ??exit ; after keyboard interrup is install + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 0200, bl = number of interrupt to get + ; output cf error, cx,dx= address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,033h + int DPMI_INTR ; do call. + jc ??error ; no mouse driver present + + ;*=========================================================================* + ;* If the interupt vector is pointing to 0000:0000h, there is no mouse + ;*=========================================================================* + mov ax,cx + or ax,dx + jz short ??nomouse + + and edx , 0ffffh + and ecx , 0ffffh + shl ecx,4 + add ecx,edx + + ;*=========================================================================* + ;* If the first instruction is an IRET, there is no mouse + ;*=========================================================================* + + cmp [byte ptr ecx],0CFH ; is this an IRET + jne short ??mouse_buff ; if it isnt then reset the mouse + +??nomouse: + mov eax,FALSE ; flag no mouse driver + mov [MInstalled],FALSE ; flag no mouse driver + jmp ??exit ; exit + +??mouse_buff: + ;*=========================================================================* + ;* Allocate two real mode memory buffers for the mouse cursor and the + ;* mouse shadow + ;*=========================================================================* + mov eax,[mouse_max_width] ; get the is max width + mov [BufferWidth],eax ; save it off for set cursor + mov edx,[mouse_max_height] ; get the max height + mov [BufferHeight],edx ; save it off for set cursor + mul edx ; size = max_width * max_height + add eax,22 ; add width/height(8) + para align(15) + and al, 0f0h ; now size is even paragraphs + mov [BufferSize],eax ; store off the buffer size + shl eax,1 ; make two of them + + mov ebx,eax ; get size of RM binary. + mov ecx,eax ; ecx will use it later. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + mov eax,0100h ; alloc real buffer + int DPMI_INTR ; do call. + jc ??error ; check for error. + + mov esi,[RealModePtr] ; get offset of real mode data start + mov [MouseBufferSel], edx + shl eax , 16 + mov [(KeyboardType esi).MouseCursor],eax ; store off the real mode segment + mov edx,eax ; get the buffer position + add edx,[BufferSize] ; add in the buffer size + mov [(KeyboardType esi).MouseBuffer],edx ; store off the real mode segment + shr eax,12 ; convert the seg/off to 32 bit offset + mov edx,eax + add edx,[BufferSize] + mov [MouseCursor],eax ; store it off in the mouse buffer + mov [MouseBuffer],edx ; store it off in the mouse buffer + + mov eax,0600h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + 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. + + +??begin: + mov esi,[RealModePtr] ; get offset of real mode data start + ; Reset mouse thru int 33h funtion 0 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 0 + lea edi, [RealInt] + int DPMI_INTR + mov eax ,[RealInt . _eax] + cmp ax,-1 ; was the mouse attached to system + jne ??nomouse ; if not exit out of the routine + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + +??end_vesa: + call Reset_Mouse + mov eax , TRUE + ret + +??error: + mov eax,FALSE +??exit: + ret + ENDP Install_Mouse + +;*************************************************************************** +;* ResET_MOUSE -- Reset mouse to a new graphif mode * +;* * +;* reset mouse driver funtions to a a currently graphic mode * +;* * +;* INPUT: * +;* OUTPUT: * +;* PROTO: Reset_Mouse ( void ) * +;* * +;* * +;* HISTORY: * +;* 08/26/1994 JRJ : Created. * +;*=========================================================================* +GLOBAL Reset_Mouse:NEAR +PROC Reset_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + ;*=========================================================================* + ;* Initialize all of the general mouse variables + ;*=========================================================================* + mov esi , [RealModePtr] ; get offset of real mode data start + test esi , esi + jz ??exit + mov eax , [ XRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseX],eax ; set the mouses x coordinate to 160 + mov eax , [ YRes ] + shr eax , 1 + mov [(KeyboardType esi).MouseY],eax ; set the mouses y coordinate to 100 + mov [(KeyboardType esi).MState],1 ; flag the mouse as hidden + mov [(KeyboardType esi).MCState],0 ; turn off conditional region + mov eax , [ XRes ] + mov [(KeyboardType esi).MouseRight],eax + mov eax , [ YRes ] + mov [(KeyboardType esi).MouseBottom],eax + + + mov [(KeyboardType esi).Adjust],0 ; assume we do not need to adjust mouse + +;*************************************************************************** +; Set the Vesa Parameters + + mov eax , [ VesaFunc ] + test eax , eax + jz ??set_vga_mouse + mov [(KeyboardType esi). VesaPtr ] , eax + + + mov ax , [(KeyboardType esi). VESAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VESAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VESA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VESA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + mov eax , [ XRes ] + mov [(KeyboardType esi). MouseRight ] , eax + + mov eax , [ YRes ] + mov [(KeyboardType esi). MouseBottom ] , eax + + lea edi , [(KeyboardType esi). VesaBankTable] + lea esi , [ BankTable ] + mov ecx , 8 + rep movsd + + mov esi,[RealModePtr] ; get offset of real mode data start + jmp ??end_vesa + +??set_vga_mouse: + + mov ax , [(KeyboardType esi). VGAMouseShadow ] + mov [(KeyboardType esi). ShadowPtr] , ax + + mov ax , [(KeyboardType esi). VGAMouseDraw ] + mov [(KeyboardType esi). DrawMousePtr ] , ax + + lea eax , [ VGA_Draw_Mouse ] + mov [ Draw_Mouse ] , eax + lea eax , [ VGA_Mouse_Shadow_Buffer ] + mov [ Mouse_Shadow_Buffer ] , eax + + +??end_vesa: + ; S Mickey/pixel Ratio cursor limits + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 8 + mov edx , 16 + mov [RealInt . _eax ] , 0fh + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max X pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ XRes ] + cmp edx , 320 + jne ??set_it + shl edx , 1 + mov [(KeyboardType esi).Adjust],1 + +??set_it: +; dec edx + mov [RealInt . _eax ] , 7 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + ; Set Min/Max Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , 0 + mov edx , [ YRes ] + dec edx + mov [RealInt . _eax ] , 8 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + +IF 0 + ; Check for scale factors in X and Y pos + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov ecx , [ XRes ] + mov edx , [ YRes ] + sub ecx , 8 + sub edx , 16 + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + ; Read mouse position thru int 33 funtion 3 + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + xor ecx , ecx + mov [RealInt . _eax ] , 3 + lea edi, [RealInt] + int DPMI_INTR + mov ecx ,[RealInt . _ecx] + mov edx ,[RealInt . _edx] + + ; compute Scale values X Y for different vendor mouse driver if nessesary + add ecx , 8 + add edx , 16 + cmp ecx , [ XRes ] + mov [(KeyboardType esi).Adjust_XPos],0 + jz ??No_scaleX + mov [(KeyboardType esi).Adjust_XPos],ecx +??No_scaleX: + cmp edx , [ YRes ] + mov [(KeyboardType esi).Adjust_YPos],0 + jz ??No_scaleY + mov [(KeyboardType esi).Adjust_YPos],edx +??No_scaleY: +ENDIF + + mov ecx, [ XRes ] ; set the mouses x coordinate to 160 + mov edx, [ YRes ] ; set the mouses x coordinate to 160 + cmp [(KeyboardType esi).Adjust],1 + je ??no_ecx_shift + shr ecx , 1 +??no_ecx_shift: + shr edx , 1 + mov [MInstalled],TRUE ; set the fact that mouse is installed + mov [(KeyboardType esi).MDisabled],TRUE ; disable mouse until the overlay is loaded + + ; Set new Mouse position any way + call Clear_RM_regs + mov eax , 0300h + mov ebx , 033h + mov [RealInt . _eax ] , 4 + mov [RealInt . _ecx] , ecx + mov [RealInt . _edx] , edx + xor ecx , ecx + lea edi, [RealInt] + int DPMI_INTR + + call Clear_RM_regs + mov eax, 0300h + mov ebx ,33h ; mouse is in 33h + mov ecx,0 ; any mouse condition generates int + mov edx,[RealModePtr] ; get real mode memory address + shr edx,4 ; convert it to a segment + mov [RealInt. _es],dx ; store it in the real mode es + movzx edx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],31 + lea edi, [RealInt] + int DPMI_INTR + +??installed: + mov eax,TRUE ; return TRUE if all is well + mov [(KeyboardType esi).MDisabled],FALSE ; no 32 bit overlays installed + je ??exit + +??error: + mov eax,FALSE + +??exit: + ret + ENDP Reset_Mouse + + + +;*************************************************************************** +;* REMOVE_MOUSE -- uninstalls the mouse interrupt by disabling the call * +;* * +;* This routine will uninstall the mouse interrupt. It does this by * +;* simply disabling the call condition. The interrupt itself is still * +;* present, just uncalled until dos removes it at program termination. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Remove_Mouse(VOID); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Remove_Mouse:NEAR + PROC Remove_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + + call Clear_RM_regs + cmp [MInstalled],FALSE ; check to see if mouse installed + je ??exit ; if not then no need to remove it + + mov [MInstalled],FALSE ; Force to NOT-Installed. + +;;PWG cmp [_MSWIFTDevice],1 ; check to see if SWIFT device installed +;;PWG je short ??prevSWIFT ; if so use previous SWIFT event handler + + mov ebx , 033h + mov edx,[RealModePtr] ; get real mode memory address + test edx,edx ; check that the memory block is valid + jz ??exit + + shr edx,4 ; convert it to a segment + mov [RealInt._es],dx ; store it in the real mode es + xor edx,edx + mov esi,[RealModePtr] ; get offset of real mode data start + mov dx,[(KeyboardType PTR esi).MouseCodeOffset] ; Get address of code + mov [RealInt._edx],edx ; set edx to offset of code + mov [RealInt._eax],12 ; set eax to set interrupt routine + mov [RealInt._ecx],0 + mov ecx,0 ; no mouse condition generates an int + mov eax,0300h + lea edi,[RealInt] + int DPMI_INTR + + mov eax,0601h ; function number. + mov ecx,[MouseCursor] ; ecx must have start of memory. + mov edi,[BufferSize] + shl edi,1 + shld ebx,ecx,16 + shld esi,edi,16 + int DPMI_INTR ; do call. + + + ; Deallocate real memory buffer + mov edx ,[MouseBufferSel] + test dx ,dx + jz ??exit + mov eax ,0101h + int DPMI_INTR + +??exit: + ret + ENDP Remove_Mouse + + +;*************************************************************************** + +GLOBAL Clear_RM_regs :NEAR +PROC Clear_RM_regs NEAR + + push eax + push ecx + push edi + lea edi , [ RealInt ] + lea ecx , [ RealInt . nothing ] + sub ecx , edi + xor eax , eax + shr ecx , 2 + rep stosd + pop edi + pop ecx + pop eax + ret +ENDP Clear_RM_regs + + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_X:NEAR + PROC Get_Mouse_X C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseX] + ret + + ENDP Get_Mouse_X + +;*************************************************************************** +;* GET_MOUSE_STATE -- Returns the current mouse state * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse's current state * +;* * +;* WARNINGS: int Get_Mouse_State(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_State:NEAR + PROC Get_Mouse_State C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MState] + ret + + ENDP Get_Mouse_State + +;*************************************************************************** +;* GET_MOUSE_X -- Returns the mouses x pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouse x position in pixels * +;* * +;* WARNINGS: int Get_Mouse_X(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Disabled:NEAR + PROC Get_Mouse_Disabled C NEAR + USES esi + + mov esi,[RealModePtr] + movzx eax,[(KeyboardType esi).MDisabled] + ret + + ENDP Get_Mouse_Disabled + +;*************************************************************************** +;* GET_MOUSE_Y -- Returns the mouse's y pixel position * +;* * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: int - the mouses y position in pixels * +;* * +;* PROTO: int Get_Mouse_Y(void); * +;* * +;* HISTORY: * +;* 08/29/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Y:NEAR + PROC Get_Mouse_Y C NEAR + USES esi + + mov esi,[RealModePtr] + mov eax,[(KeyboardType esi).MouseY] + ret + + ENDP Get_Mouse_Y + +;*************************************************************************** +;* GET_MOUSE_BUTTON -- Gets the values of the mouse button * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Get_Mouse_Button:NEAR + PROC Get_Mouse_Button C NEAR + USES eax,esi + + mov esi,[RealModePtr] + xor eax,eax + mov al,[(KeyboardType esi).Button] + ret + + ENDP Get_Mouse_Button + +;*************************************************************************** +;* SET_MOUSE_CURSOR -- Sets the shape to be used as the mouse. * +;* * +;* This will inform the system of the shape desired as the mouse * +;* cursor. * +;* * +;* INPUT: xhotspot -- Offset of click point into shape. * +;* * +;* yhotspot -- Offset of click point into shape. * +;* * +;* cursor -- Shape to use as the new mouse shape. * +;* * +;* OUTPUT: Returns with a pointer to the original mouse shape. * +;* * +;* PROTO: VOID *Set_Mouse_Cursor( int xhotspot, * +;* int yhotspot, * +;* void *cursor); * +;* * +;* HISTORY: * +;* 02/13/1992 JLB : Created. * +;*=========================================================================* + GLOBAL Set_Mouse_Cursor:NEAR + PROC Set_Mouse_Cursor C NEAR + USES ebx,ecx,edx,esi,edi + + ARG xhotspot:DWORD ; the x hot spot of the mouse + ARG yhotspot:DWORD ; the y hot spot of the mouse + ARG cursor:DWORD ; ptr to the new mouse cursor + + LOCAL datasize:DWORD + LOCAL stype:WORD + LOCAL swidth:DWORD ; Shape width. + LOCAL sheight:DWORD ; Shape height. + LOCAL ssize:DWORD ; Size of raw shape. + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + mov esi,[RealModePtr] ; get offset of real mode data start + + ;------------------------------------------------------------------- + ; Make sure the mouse is Enabled and Installed before continuing + ;------------------------------------------------------------------- + mov eax,[cursor] + cmp eax,0 ; if the mouse cursor is in zero page + je ??exit ; it is completely invalid + + cmp eax,[PrevMousePtr] + je ??exit + + cmp [(KeyboardType esi).MDisabled],FALSE ; make sure the mouse is not disabled + jne ??exit ; if it is not then give it a try + + ;------------------------------------------------------------------- + ; We need to wait if a mouse interrupt is in progress. Once the + ; interrupt is finished flag that it is occuring so that it will + ; not attempt to draw the mouse while we are changing the cursor. + ;------------------------------------------------------------------- +??try: + inc [(KeyboardType esi).MouseUpdate] ; flag that mouse can not be drawn + + ;------------------------------------------------------------------- + ; If the mouse is currently on the screen hide it. + ;------------------------------------------------------------------- + call Low_Hide_Mouse + + ;------------------------------------------------------------------- + ; Calculate the size of the buffer needed. + ;------------------------------------------------------------------- + push [cursor] + call Get_Shape_Uncomp_Size + pop edx + mov [datasize],eax + + push [cursor] + call Get_Shape_Width + pop edx + mov [swidth],eax + cmp eax,[BufferWidth] + jg ??end + + push [cursor] + call Get_Shape_Original_Height + pop edx + mov [sheight],eax + cmp eax,[BufferHeight] + jg ??end + + + mov ebx,[sheight] + mov eax,[swidth] + imul ebx,eax + +??copy_mouse: + mov esi,[cursor] ; set esi to point to mouse shape + mov ax,[esi] ; get the shape type byte + mov [stype],ax ; save off the shape type + test ax,MAKESHAPE_NOCOMP ; is it marked as no compression? + je ??comp_shape ; if not go to the comped shape code + mov edi,[MouseCursor] ; set edi to point to mouse buffer +??copy_type: + + test [stype],MAKESHAPE_COMPACT ; if the shape a 16 color shape? + jne ??16_color_copy ; if it is then go handle it + +??normal_copy: + ;------------------------------------------------------------------- + ;* Uncompressed NORMAL shapes just get run-length uncompressed + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov eax,[swidth] ; load up the width + mul [sheight] ; find size of shape in pixels + mov edx,eax ; save this in number of bytes + xor ecx,ecx ; clear high of loop variable +??norm_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??norm_trans ; if its a zero its transparent + mov [edi],al ; write out the pixel to dest + inc edi ; move to next dest position + dec edx ; we have now written another pix + jnz ??norm_unrle ; if more to write then do it + jmp ??done_copy ; otherwise we are all done + +??norm_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx + jnz ??norm_unrle ; if more to do then do it + jmp ??done_copy ; otherwise we are done + +??16_color_copy: + ;------------------------------------------------------------------- + ;* Uncompressed 16 color shapes just get remaped and UN-RLE'd + ;------------------------------------------------------------------- + add esi,10 ; adjust past header of shape + mov ebx,esi ; save of position of remap + add esi,16 ; move past remap table + mov eax,[swidth] ; load up the width + mul [sheight] ; load up the height + mov edx,eax ; save this in number of bytes + xor eax,eax ; clear high of lookup variable + xor ecx,ecx ; clear high of loop variable +??16_color_unrle: + mov al,[esi] ; get a byte out of the file + inc esi ; increment to the next pos + or al,al ; set the flags on register state + jz ??16_color_trans ; if its a zero its transparent + mov al,[ebx+eax] ; remap the pixel from 16 color table + mov [edi],al ; store it out to the dest address + inc edi ; move to next dest address + dec edx ; we have now written a pixel + jnz ??16_color_unrle + jmp ??done_copy +??16_color_trans: + mov cl,[esi] ; get how many zeros to write + sub edx,ecx ; subtract off count ahead of time + inc esi ; increment the source position + xor al,al ; clear out al cuz we're writing zeros + rep stosb ; write all of them out + or edx,edx ; or edx to test for zero + jnz ??16_color_unrle + jmp ??done_copy + + ;------------------------------------------------------------------- + ; Deal with the compressed shape by copying it into the shape + ; staging buffer. + ;------------------------------------------------------------------- +??comp_shape: + mov edi,[_ShapeBuffer] ; get a pointer to ShapeBuffer + mov ax,[esi] ; load in the shape type + add esi,2 ; increment the shape pointer + or ax,MAKESHAPE_NOCOMP ; make the shape as uncompressed + mov [stype],ax ; save off the shape type + mov [edi],ax ; write out uncompressed shape + add edi,2 + + ;------------------------------------------------------------------- + ; Process the shape header information and copy it over to the new + ; location. + ;------------------------------------------------------------------- + mov ecx,4 ; transfer height, width, org and + test eax,MAKESHAPE_COMPACT ; is it a 16 color shape? + je ??copy_head ; no - don't worry about it + add ecx,8 ; otherwise adjust ecx for header +??copy_head: + rep movsw ; copy the necessary bytes + + mov eax,[datasize] ; get uncompressed length + push eax ; push as third param + push edi ; push the destination offset + push esi ; push the source offset + call LCW_Uncompress ; do the uncompress + pop eax + pop eax + pop eax + mov esi,[_ShapeBuffer] + mov edi,[MouseCursor] ; set edi to point to mouse buffer + jmp ??copy_type + +??done_copy: + mov esi,[RealModePtr] ; get offset of real mode data start + + mov eax,[xhotspot] ; get the mouse cursors x hotspot + mov [(KeyboardType esi).MouseXHot],eax + mov eax,[yhotspot] ; get the mouse cursors y hotspot + mov [(KeyboardType esi).MouseYHot],eax + + mov ebx,[sheight] ; get shape height + mov [(KeyboardType esi).MouseHeight],ebx + mov ebx,[swidth] + mov [(KeyboardType esi).MouseWidth],ebx + +??error: + call Low_Show_Mouse + + ;------------------------------------------------------------------- + ; Final cleanup and exit. + ;------------------------------------------------------------------- +??end: + dec [(KeyboardType esi).MouseUpdate] ; we are done modifying the cursor +??exit: + push [cursor] + push [PrevMousePtr] + pop eax + pop [PrevMousePtr] + ret ; and return back to the world + + ENDP Set_Mouse_Cursor + + +;*************************************************************************** +;* LOW_HIDE_MOUSE -- Low-level routine which hides the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Hide_Mouse:NEAR + PROC Low_Hide_Mouse C NEAR + USES eax,ebx,esi + + mov ebx,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ;* Is the mouse disabled or hidden? + ;*=========================================================================* + cmp [(KeyboardType ebx).MDisabled],0 ; is the mouse disabled? + jne short ??end + + cmp [(KeyboardType ebx).MState],0 ; is the mouse hidden? + jne short ??endnodraw ; no need to hide again + + ;*=========================================================================* + ;* Take care of restoring the mouse cursor + ;*=========================================================================* + mov eax,RESTORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer] + pop eax + ;*=========================================================================* + ;* The mouse is now hidden -- again. + ;*=========================================================================* +??endnodraw: + add [(KeyboardType ebx).MState],1 + adc [(KeyboardType ebx).MState],0 + +??end: + ret + ENDP Low_Hide_Mouse + +;*************************************************************************** +;* LOW_SHOW_MOUSE -- Low level routine which shows the mouse * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 09/19/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Low_Show_Mouse:NEAR + PROC Low_Show_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + LOCAL mousex:DWORD + LOCAL mousey:DWORD + + mov esi,[RealModePtr] ; get offset of real mode data start + + ;*=========================================================================* + ;* Don't show the mouse if it is not hidden or it is disabled + ;*=========================================================================* + cmp [(KeyboardType esi).MDisabled],0 ; is the mouse disabled + jne ??exit ; if so then exit + + cmp [(KeyboardType esi).MState],0 ; is the mouse already visible + je ??exit ; if so then exit + + ;*=========================================================================* + ;* Don't show the mouse if it was hidden multiple times + ;*=========================================================================* + dec [(KeyboardType esi).MState] ; show the mouse one level + cmp [(KeyboardType esi).MState],0 ; can the mouse be shown + jne short ??exit + + ;*=========================================================================* + ;* Determine the theoretcial drawing position of the mouse + ;*=========================================================================* + mov ecx,[(KeyboardType esi).MouseWidth] ; Theoretical buffer width (pixel). + mov edx,[(KeyboardType esi).MouseHeight] ; Theoretical buffer height (pixel). + mov eax,[(KeyboardType esi).MouseX] + mov [mousex],eax ; Draw X pixel. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ebx,[(KeyboardType esi).MouseY] + mov [mousey],ebx ; Draw Y pixel. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;*=========================================================================* + ;* Determine the theoretical coordinates and dimensions of the + ;* area the mouse shape will be rendered upon. + ;*=========================================================================* + mov [(KeyboardType esi).MouseBuffX],eax + mov [(KeyboardType esi).MouseBuffY],ebx + mov [(KeyboardType esi).MouseBuffW],ecx + mov [(KeyboardType esi).MouseBuffH],edx + +;------ Move the area that will be drawn upon, to the graphic buffer. + mov eax,STORE_VISIBLE_PAGE + push eax + call [ Mouse_Shadow_Buffer ] + pop eax + +;------ Draw the mouse shape to the seenpage. + push [mousey] + push [mousex] + call [ Draw_Mouse ] + pop eax + pop eax +??exit: + ret + ENDP Low_Show_Mouse + +;*************************************************************************** +;* HIDE_MOUSE -- Hides mouse cursor on screen if it was show * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Hide_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Hide_Mouse:NEAR + PROC Hide_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Hide_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + + ENDP Hide_Mouse + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* SHOW_MOUSE -- Display mouse cursor on screen if it was hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Show_Mouse(void) * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Show_Mouse:NEAR + PROC Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Mark us as updating the mouse and then run and do the update + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] ; were drawing the mouse + call Low_Show_Mouse ; make the function call + dec [(KeyboardType esi).MouseUpdate] ; were done drawing mouse + ret + ENDP Show_Mouse + +;*************************************************************************** +;* CONDITIONAL_HIDE_MOUSE -- Hides mouse if its with given region * +;* * +;* INPUT: int sx_pixel - the left-most pixel position of the region * +;* int sy_pixel - the upper-most pixel position of the region * +;* int dx_pixel - the right most pixel position of the region * +;* int dy_pixel - the lower most pixel position of the region * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Hide_Mouse( int sx_pixel, * +;* int sy_pixel, * +;* int dx_pixel, * +;* int dy_pixel); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Hide_Mouse:NEAR + PROC Conditional_Hide_Mouse C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ARG sx_pixel:DWORD ; left x pixel pos + ARG sy_pixel:DWORD ; upper y pixel pos + ARG dx_pixel:DWORD ; right x pixel pos + ARG dy_pixel:DWORD ; lower y pixel pos + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ;* Widen region leftward by amount mouse shape spills to the right of + ;* its hot point. Limited to the left edge of the screen. + ;*=========================================================================* + mov eax,[(KeyboardType esi).MouseWidth] + sub eax,[(KeyboardType esi).MouseXHot] + neg eax + add eax,[sx_pixel] + jns short ??noleftlimit + xor eax,eax +??noleftlimit: + ;*=========================================================================* + ;* Widen region upward by amount mouse shape spills below the + ;* mouse's hot point. Limited by the top edge of the screen. + ;*=========================================================================* + mov ebx,[(KeyboardType esi).MouseHeight] + sub ebx,[(KeyboardType esi).MouseYHot] + neg ebx + add ebx,[sy_pixel] + jns short ??notoplimit + xor ebx,ebx +??notoplimit: + ;*=========================================================================* + ;* Widen region rightward by amount mouse shape spills to the + ;* left of its hot point. Limited by the right edge of the + ;* screen. + ;*=========================================================================* + mov ecx,[dx_pixel] + add ecx,[(KeyboardType esi).MouseXHot] + cmp ecx,[(KeyboardType esi).MouseRight] + jb short ??norightlimit + mov ecx,[(KeyboardType esi).MouseRight] + dec ecx +??norightlimit: + ;*=========================================================================* + ;* Widen region downward by amout the mouse shape extends above + ;* its hot point. Limited to the bottom of the screen. + ;*=========================================================================* + mov edx,[dy_pixel] + add edx,[(KeyboardType esi).MouseYHot] + cmp edx,[(KeyboardType esi).MouseBottom] + jb short ??nobottomlimit + mov edx,[(KeyboardType esi).MouseBottom] + dec edx + +??nobottomlimit: + inc [(KeyboardType esi).MouseUpdate] ; don't let interrupt scrag variables + + ;*=========================================================================* + ;* The mouse could be in one of four conditions. + ;* 1) The mouse is visible and no conditional hide has been specified. + ;* (perform normal region checking with possible hide) + ;* 2) The mouse is hidden and no conditional hide as been specified. + ;* (record region and do nothing) + ;* 3) The mouse is visible and a conditional region has been specified + ;* (expand region and perform check with possible hide). + ;* 4) The mouse is already hidden by a previous conditional. + ;* (expand region and do nothing) + ;* + ;* First: Set or expand the region according to the specified parameters + ;*=========================================================================* + cmp [(KeyboardType esi).MCState],0 + jne short ??expand + mov [(KeyboardType esi).MouseCXLeft],eax + mov [(KeyboardType esi).MouseCYUpper],ebx + mov [(KeyboardType esi).MouseCXRight],ecx + mov [(KeyboardType esi).MouseCYLower],edx + jmp ??noylower +??expand: + + ;*=========================================================================* + ;* Expand the clipping rectangle. + ;*=========================================================================* + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??noxleft + mov [(KeyboardType esi).MouseCXLeft],eax +??noxleft: + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??noyupper + mov [(KeyboardType esi).MouseCYUpper],ebx +??noyupper: + cmp ecx,[(KeyboardType esi).MouseCXRight] + ja short ??noxright + mov [(KeyboardType esi).MouseCXRight],ecx +??noxright: + cmp edx,[(KeyboardType esi).MouseCYLower] + ja short ??noylower + mov [(KeyboardType esi).MouseCYLower],edx +??noylower: + ;*=========================================================================* + ;* If the mouse isn't already hidden, then check its location against + ;* the hiding region and hide if necessary. + ;*=========================================================================* + test [(KeyboardType esi).MCState],CONDHIDDEN + jne short ??nohide + + mov eax,[(KeyboardType esi).MouseX] + mov ebx,[(KeyboardType esi).MouseY] + cmp eax,[(KeyboardType esi).MouseCXLeft] + jb short ??nohide + cmp eax,[(KeyboardType esi).MouseCXRight] + ja short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYUpper] + jb short ??nohide + cmp ebx,[(KeyboardType esi).MouseCYLower] + ja short ??nohide + + ;*=========================================================================* + ;* The mouse falls within the region for hiding, so hide it. + ;*=========================================================================* + + call Low_Hide_Mouse ; make the function call + or [(KeyboardType esi).MCState],CONDHIDDEN +??nohide: + + ;*=========================================================================* + ;* Record the fact that a Conditional_Hide_Mouse was + ;* called and then exit. + ;*=========================================================================* + or [(KeyboardType esi).MCState],CONDHIDE + add [BYTE PTR (KeyboardType esi).MCState],1 + adc [BYTE PTR (KeyboardType esi).MCState],0 + dec [(KeyboardType esi).MouseUpdate] + ret + + ENDP Conditional_Hide_Mouse + +;*************************************************************************** +;* CONDITIONAL_SHOW_MOUSE -- shows mouse if it was conditionally hidden * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void Conditional_Show_Mouse(void); * +;* * +;* HISTORY: * +;* 11/03/1994 PWG : Created. * +;*=========================================================================* + GLOBAL Conditional_Show_Mouse:NEAR + PROC Conditional_Show_Mouse C NEAR + USES eax,esi + + mov esi,[RealModePtr] ; get the real mode offset + + ;*=========================================================================* + ; Notify the interrupt that we are updating the mouse + ;*=========================================================================* + inc [(KeyboardType esi).MouseUpdate] + + mov ax,[(KeyboardType esi).MCState] + cmp al,0 + je short ??exit ; if no more nesting clear cond + + ;*=========================================================================* + ;* Decrement the conditional hide counter. + ;*=========================================================================* + dec al + + ;*=========================================================================* + ;* If there are more conditional levels to undo, then just abort the show. + ;*=========================================================================* + jne short ??exit + + ;*=========================================================================* + ;* Yup, the mouse should be show, but only if it was previously hidden. + ;*=========================================================================* + test ax,CONDHIDDEN ; was it hidden by interrupt?? + je short ??exit1 ; or initial check for level 0 + + call Low_Show_Mouse + +??exit1: + mov ax,0 + +??exit: + mov [(KeyboardType esi).MCState],ax + dec [(KeyboardType esi).MouseUpdate] + ret + ENDP Conditional_Show_Mouse + + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VGA_Mouse_Shadow_Buffer:NEAR + PROC VGA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + cld + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + push ebp + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov ebp , ecx +??restore_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??restore_loop ; if more to do, do it + pop ebp + ret + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + xchg esi,edi ; xchg the source and the dest + mov ebp , ecx +??store_loop: + mov ecx,eax ; get number to really write + rep movsb ; store them into the buffer + add esi,edx ; move past right clipped pixels + add edi,ebx ; adjust dest to next line + dec ebp ; decrement number of rows to do + jnz ??store_loop ; if more to do, do it + pop ebp +??out: + ret + ENDP VGA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VGA_Draw_Mouse:NEAR + PROC VGA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + dec ah + jnz ??inner_loop + add esi,ebx ; move past right clipped pixels + add edi,edx ; adjust dest to next line + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it +??out: + ret + + ENDP VGA_Draw_Mouse + + +;*************************************************************************** +;*************************************************************************** +;*************************************************************************** + +;*************************************************************************** +;* MOUSE_SHADOW_BUFFER -- Handles storing and restoring the mouse buffer * +;* * +;* INPUT: int store - indicates whether we are storing the buffer or * +;* not. * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C VESA_Mouse_Shadow_Buffer:NEAR + PROC VESA_Mouse_Shadow_Buffer C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG store:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + ;*=========================================================================* + ; Direction flag must be forward in this routine. + ;*=========================================================================* + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[(KeyboardType esi).MouseBuffX] + mov ebx,[(KeyboardType esi).MouseBuffY] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseBuffW] + add ebx,[(KeyboardType esi).MouseBuffH] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseBuffer ] + mov [ buffy0 ] , eax + + ;*=================================================================== + ;* Bounds check source X. + ;*=================================================================== + + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseBuffW] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseBuffW] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + cmp [store],RESTORE_VISIBLE_PAGE ; are we restoring page? + jne ??store_entry ; if not the go to store + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + + mov [ tempreg ] , ebx +??restore__top_loop: + mov ah,al +??restore__inner_loop: + mov ch ,[esi] + mov [edi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range: + dec ah + jnz ??restore__inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??restore__in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??restore__in_range1: + dec cl ; decrement number of rows to do + jnz ??restore__top_loop ; if more to do, do it + jmp ??out ; get the heck outta the routine + + ;*=================================================================== + ;* Handle soting the visible page into the Mouse Shadow Buffer + ;*=================================================================== +??store_entry: + mov [ tempreg ] , ebx +??store_top_loop: + mov ah,al +??store_inner_loop: + mov ch ,[edi] + mov [esi],ch + inc esi + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range: + dec ah + jnz ??store_inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??store_in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??store_in_range1: + dec cl ; decrement number of rows to do + jnz ??store_top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + ENDP VESA_Mouse_Shadow_Buffer + +;*************************************************************************** +;* DRAW_MOUSE -- Handles drawing the mouse cursor * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: Asm callable only! * +;* * +;* HISTORY: * +;* 10/27/1994 PWG : Created. * +;*=========================================================================* + GLOBAL VESA_Draw_Mouse:NEAR + PROC VESA_Draw_Mouse C NEAR + USES eax,ebx,ecx,edx,edi,esi + ARG mousex:DWORD + ARG mousey:DWORD + + local x0 : dword + local y0 : dword + local x1 : dword + local y1 : dword + local buffx0 : dword + local buffy0 : dword + local tempreg : dword + local vesa_linear : dword + + mov eax , [ cpu_video_page ] + mov [ vesa_linear ] , eax + + mov esi,[RealModePtr] ; get 32 bit offset of dos data + + ;*=================================================================== + ;* Copy of X, Y, Width and Height into local registers + ;*=================================================================== + mov eax,[mousex] + mov ebx,[mousey] + sub eax,[(KeyboardType esi).MouseXHot] + sub ebx,[(KeyboardType esi).MouseYHot] + + mov [ x0 ] , eax + mov [ y0 ] , ebx + add eax,[(KeyboardType esi).MouseWidth] + add ebx,[(KeyboardType esi).MouseHeight] + mov [ x1 ] , eax + mov [ y1 ] , ebx + + mov [ buffx0 ] , 0 + mov eax , [ MouseCursor ] + mov [ buffy0 ] , eax + + + ;*=================================================================== + ;* Bounds check source X. Y. + ;*=================================================================== + xor eax , eax + xor edx , edx + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ x0 ] + mov ebx , [ x1 ] + sub ecx , [(KeyboardType esi).MouseRight] + sub ebx , [(KeyboardType esi).MouseRight] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + shld eax , ecx , 1 + shld edx , ebx , 1 + + mov ecx , [ y0 ] + mov ebx , [ y1 ] + sub ecx , [(KeyboardType esi).MouseBottom] + sub ebx , [(KeyboardType esi).MouseBottom] + dec ecx + dec ebx + shld eax , ecx , 1 + shld edx , ebx , 1 + + xor al , 5 + xor dl , 5 + mov ah , al + test dl , al + jnz ??out + or al , dl + jz ??not_clip + + test ah , 1000b + jz ??scr_left_ok + mov ebx , [ x0 ] + neg ebx + mov [ buffx0 ] , ebx + mov [ x0 ] , 0 + +??scr_left_ok: + test ah , 0010b + jz ??scr_bottom_ok + mov ebx , [ y0 ] + neg ebx + imul ebx , [(KeyboardType esi).MouseWidth] + add [ buffy0 ] , ebx + mov [ y0 ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [(KeyboardType esi).MouseRight] ; get width into register + mov [ x1 ] , eax +??scr_right_ok: + test dl , 0001b + jz ??not_clip + mov eax , [(KeyboardType esi).MouseBottom] ; get width into register + mov [ y1 ] , eax + +??not_clip: + + ;*=================================================================== + ;* Get the offset into the screen. + ;*=================================================================== + mov eax , [ y0 ] + mov edx , [(KeyboardType esi).MouseRight] + imul eax , edx + add eax , [ x0 ] + lea edi , [ 0a0000h + eax ] + call Vesa_Asm_Set_Win + + ;*=================================================================== + ;* Adjust the source for the top clip. + ;*=================================================================== + mov ebx , [(KeyboardType esi).MouseWidth] ; turn this into an offset + mov esi , [ buffy0 ] ; edx points to source + add esi , [ buffx0 ] ; plus clipped lines + + ;*=================================================================== + ;* Calculate the bytes per row add value + ;*=================================================================== + + mov eax , [ x1 ] + mov ecx , [ y1 ] + sub eax , [ x0 ] + jle ??out + sub ecx , [ y0 ] + jle ??out + + sub edx , eax + sub ebx , eax + + ;*=================================================================== + ;* Handle restoring the buffer to the visible page + ;*=================================================================== + mov [ tempreg ] , ebx +??top_loop: + mov ah,al +??inner_loop: + mov ch ,[esi] + inc esi + or ch,ch + jz ??inc_edi + mov [edi],ch +??inc_edi: + inc edi + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range: + dec ah + jnz ??inner_loop + add esi,[ tempreg ] ; move past right clipped pixels + add edi,edx ; adjust dest to next line + + mov ebx , edi + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range1 + add edi ,[ cpu_video_page ] + call Vesa_Asm_Set_Win + ??in_range1: + dec cl ; decrement number of rows to do + jnz ??top_loop ; if more to do, do it + +??out: + mov edi , [ vesa_linear ] + add edi , 0a0000h + call Vesa_Asm_Set_Win + + ret + + ENDP VESA_Draw_Mouse + + +;---------------------------------------------------------------------------- +END diff --git a/WWFLAT32/SRCDEBUG/NEWDEL.CPP b/WWFLAT32/SRCDEBUG/NEWDEL.CPP new file mode 100644 index 0000000..e402e89 --- /dev/null +++ b/WWFLAT32/SRCDEBUG/NEWDEL.CPP @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Memory system. * + * * + * File Name : NEWDEL.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : July 17, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * operator NEW -- Overides the global new function. * + * operator delete -- Overides the global delete function. * + * operator NEW[] -- Overides the array version of new. * + * operator delete[] -- Overides the array version of delete[] * + * -- * + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwmem.h" + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPERATOR NEW -- Overides the global new function. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new(size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + + +/*************************************************************************** + * OPERATOR NEW[] -- Overides the array version of new. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void * operator new[](size_t size) +{ + return (Alloc((unsigned long) size, MEM_NEW)); +} + +/*************************************************************************** + * OPERATOR NEW -- New opperator which takes a MEM_FLAG * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR NEW[] -- Global NEW[] which takes MEM_FLAG * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/17/1995 PWG : Created. * + *=========================================================================*/ +void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, (MemoryFlagType)(flag|MEM_NEW))); +} + +/*************************************************************************** + * OPERATOR DELETE -- Overides the global delete function. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +void operator delete(void *ptr) +{ + Free(ptr); +} + +/*************************************************************************** + * OPERATOR DELETE[] -- Overides the array version of delete[] * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/20/1994 SKB : Created. * + *=========================================================================*/ +void operator delete[](void *ptr) +{ + Free(ptr); +} + + + diff --git a/WWFLAT32/SRCDEBUG/PAGFAULT.ASM b/WWFLAT32/SRCDEBUG/PAGFAULT.ASM new file mode 100644 index 0000000..546dd27 --- /dev/null +++ b/WWFLAT32/SRCDEBUG/PAGFAULT.ASM @@ -0,0 +1,153 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +RESET_VIDEO_MODE equ -1 + +GLOBAL Install_Page_Fault_Handle : NEAR +GLOBAL Set_Video_Mode : NEAR +GLOBAL Remove_Mouse : NEAR +GLOBAL Remove_Keyboard_Interrupt : NEAR +GLOBAL Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault handle * +;* This function will install a new page fault handle * +;* so in the event that we have a program crash thi handle will * +;* remove all interrupts and then will chain to the default Page * +;* Fault handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + ??exit: + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END + diff --git a/WWFLAT32/SVGAPRIM/MAKEFILE b/WWFLAT32/SVGAPRIM/MAKEFILE new file mode 100644 index 0000000..3e956c2 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/MAKEFILE @@ -0,0 +1,194 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27 , 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = svgaprim +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + vesa.obj & + vclear.obj & + vgetpix.obj & + vputpix.obj & + vtobuff.obj & + vtopage.obj & + vlbtove.obj & + vvetolb.obj & + vvblit.obj & + vtxtprnt.obj & + vscltove.obj & + vvetoscl.obj & + vscale.obj + + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/SVGAPRIM/SVGAPRIM.INC b/WWFLAT32/SVGAPRIM/SVGAPRIM.INC new file mode 100644 index 0000000..2651d21 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/SVGAPRIM.INC @@ -0,0 +1,77 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/WWFLAT32/SVGAPRIM/VCLEAR.ASM b/WWFLAT32/SVGAPRIM/VCLEAR.ASM new file mode 100644 index 0000000..eae579d --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VCLEAR.ASM @@ -0,0 +1,171 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : CLEAR.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : August 23, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Clear -- Clears a virtual viewport instance * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +CODESEG + +PROC Vesa_Clear C near + USES eax,ebx,ecx,edx,esi,edi + + ARG this:DWORD ; this is a member function + ARG color:BYTE ; what color should we clear to + + cld ; always go forward + + mov ebx,[this] ; get a pointer to viewport + mov esi,[(GraphicViewPort ebx).GVPXAdd] ; esi = add for each line + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + push esi + mov edx,[(GraphicViewPort ebx).GVPHeight] ; ecx = height of viewport + mov esi,[(GraphicViewPort ebx).GVPWidth] ; edx = width of viewport + + + ;*=================================================================== + ; Convert the color byte to a DWORD for fast storing + ;*=================================================================== + mov al,[color] ; get color to clear to + mov ah,al ; extend across WORD + mov ecx,eax ; extend across DWORD in + shl eax,16 ; several steps + mov ax,cx + + Call Vesa_Asm_Set_Win ; set the window + + ;*=================================================================== + ; Find out if we should bother to align the row. + ;*=================================================================== + +??row_by_row_aligned: + cmp esi , OPTIMAL_BYTE_COPY ; is it worth aligning them? + jl ??row_by_row ; if not then skip + + ;*=================================================================== + ; Now that we have the alignment offset copy each row + ;*=================================================================== + + +??aligned_loop: + lea ebx , [ edi + esi ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + + xor ecx , ecx + mov ebx , esi + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep stosb +??no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + pop eax + ret + +??in_range: + mov ecx , edi + mov ebx , esi + neg ecx + and ecx , 3 + sub ebx , ecx + rep stosb + mov ecx , ebx + shr ecx , 2 + rep stosd + mov ecx , ebx + and ecx , 3 + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??aligned_loop ; if more to do than do it + pop eax + ret + + ;*=================================================================== + ; If not enough bytes to bother aligning copy each line across a byte + ; at a time. + ;*=================================================================== +??row_by_row: + + lea ebx , [ edi + esi ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , esi + jl ??in_range_bytes + + xor ecx , ecx + mov ebx , esi + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep stosb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + +??in_range_bytes: + + mov ecx , ebx + rep stosb + add edi , [ esp ] + dec edx ; decrement the height + jnz ??row_by_row ; if more to do than do it + pop eax + ret + +ENDP Vesa_Clear + + +END diff --git a/WWFLAT32/SVGAPRIM/VESA.ASM b/WWFLAT32/SVGAPRIM/VESA.ASM new file mode 100644 index 0000000..fd7266b --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VESA.ASM @@ -0,0 +1,144 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VESA.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Vesa_Asm_Set_Win -- Sets the current vesa window from Asm * +;* Vesa_Asm_Next_Window -- Sets to the next vesa window * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" + +GLOBAL RMVesaVector : DWORD +GLOBAL RMVesaRegs : DWORD + + +DPMI_INTR EQU 31h + +STRUC DPMI_REGS + _edi dd ? + _esi dd ? + _ebp dd ? + dd ? + _ebx dd ? + _edx dd ? + _ecx dd ? + _eax dd ? + Flags dw ? + _es dw ? + _ds dw ? + _fs dw ? + _gs dw ? + _ip dw ? + _cs dw ? + _sp dw ? + _ss dw ? + dd ? +ENDS + + +DATASEG + +cpu_video_page dd 0h +cpu_page_limit dd 0b0000h +CurrentBank DD 0h ; current vesa bank +RealFunc DPMI_REGS ? ; structure to call a real mode int + + + CODESEG + + +;*************************************************************************** +;* VESA_ASM_SET_WIN -- Sets the current vesa window from Asm * +;* * +;* INPUT: edi - offset to set the window for * +;* * +;* OUTPUT: edi - adjusted offset for window * +;* * +;* PROTO: void Vesa_Asm_Set_Win(void); * +;* * +;* HISTORY: * +;* 12/08/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Asm_Set_Win C near + + push eax + push edi + push ecx + push ebx + + + mov eax , edi + and eax , 0ffff0000h + add eax , 010000h + mov [ cpu_page_limit ] , eax + + lea eax , [ edi - 0a0000h ] + and eax , 0ffff0000h + mov [ cpu_video_page ] , eax + + shr eax , 14 + cmp eax , [ CurrentBank ] + jz ??no_change + mov [CurrentBank],eax ; it will be new current bank + mov ebx , [ BankTable + eax ] ; find gran value of new bank + + mov edi , [ RMVesaRegs ] ; clean up RMRegister bank + xor eax , eax + mov ecx , 34h / 4 ; size of RMRegs Bank + rep stosd + + mov edi , [ RMVesaRegs ] + mov [(type DPMI_REGS ptr edi ) . _eax ] , 04f05h + mov [(type DPMI_REGS ptr edi ) . _ebx ] , 0 + mov [(type DPMI_REGS ptr edi ) . _edx ] , ebx + + mov eax , 0300h + xor ecx , ecx ; set amount to copy of stack + mov ebx , [ RMVesaVector] ; set pointer to func to call + int DPMI_INTR ; make the call + ??no_change: + pop ebx + pop ecx + pop edi + pop eax + and edi,0000ffffh ; adjust edi to be 64k + add edi,0a0000h + ret + ENDP Vesa_Asm_Set_Win + +END diff --git a/WWFLAT32/SVGAPRIM/VGETPIX.ASM b/WWFLAT32/SVGAPRIM/VGETPIX.ASM new file mode 100644 index 0000000..27a9126 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VGETPIX.ASM @@ -0,0 +1,116 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : GETPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 7, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Get_Pixel -- Gets a pixel from the current graphic view port * +;* VVPC::Get_Pixel -- Gets a pixel from the current view port * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + xor eax,eax + mov edi,[(VideoViewPort ebx).VIVPOffset] ; get the correct offset + mov ecx,[(VideoViewPort ebx).VIVPHeight] ; edx = height of viewport + mov edx,[(VideoViewPort ebx).VIVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(VideoViewPort ebx).VIVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ;* Figure out what bank we are in and set it, then adjust the + ;* offset. + ;*=================================================================== + + call Vesa_Asm_Set_Win + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== +??in_range: + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel +??exit: + ret + ENDP Vesa_Get_Pixel + +END diff --git a/WWFLAT32/SVGAPRIM/VLBTOVE.ASM b/WWFLAT32/SVGAPRIM/VLBTOVE.ASM new file mode 100644 index 0000000..d0543b9 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VLBTOVE.ASM @@ -0,0 +1,403 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +TRANSP equ 0 + +CODESEG + + +;*************************************************************************** +;* GVC::BLIT -- Copies a buffer to a graphic page non-linearly * +;* * +;* NOTE: All coordinate values are expressed in pixels * +;* * +;* INPUT: VideoViewPortClass *dest - Video View Port to copy to * +;* WORD src_x - Src x position to copy from * +;* WORD src_y - Src y position to copy from * +;* WORD dst_x - Dest x position to copy to * +;* WORD dst_y - Dest y position to copy to * +;* WORD w - Width of region to copy * +;* WORD h - Height of region to copy * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. * +;* * +;* HISTORY: * +;* 05/11/1994 PWG : Created. * +;* 08/05/1994 PWG : Fixed clipping problem * +;*=========================================================================* + PROC Linear_Blit_To_Vesa C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + mov [ source_area ] , ecx + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + Call Vesa_Asm_Set_Win ; set the window + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret +ENDP Linear_Blit_To_Vesa + +END diff --git a/WWFLAT32/SVGAPRIM/VPUTPIX.ASM b/WWFLAT32/SVGAPRIM/VPUTPIX.ASM new file mode 100644 index 0000000..3155776 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VPUTPIX.ASM @@ -0,0 +1,115 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Clear the Full Mcga Screen * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GVPC::Put_Pixel -- Puts a pixel on a graphic viewport * +;* VIVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + + CODESEG + +;*************************************************************************** +;* VIVPC::PUT_PIXEL -- Puts a pixel on a video viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_Put_Pixel C near + USES eax,ebx,ecx,edx,edi,esi + + ARG this:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this] ; get a pointer to viewport + mov edi,[(VideoViewPort ebx).VIVPOffset] ; get the correct offset + mov ecx,[(VideoViewPort ebx).VIVPHeight] ; edx = height of viewport + mov edx,[(VideoViewPort ebx).VIVPWidth] ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short ??exit ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae ??exit ; if so then get out + add edx,[(VideoViewPort ebx).VIVPXAdd] ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ;* Figure out what bank we are in and set it, then adjust the + ;* offset. + ;*=================================================================== + call Vesa_Asm_Set_Win + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen +??exit: + ret + ENDP Vesa_Put_Pixel +END diff --git a/WWFLAT32/SVGAPRIM/VSCALE.CPP b/WWFLAT32/SVGAPRIM/VSCALE.CPP new file mode 100644 index 0000000..604eca9 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VSCALE.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Testing memory. * + * * + * File Name : TEST.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : January 18, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* +*/ +#include +#include +#include + +//VideoBufferClass SeenPage(640,480); +//GraphicBufferClass HidBuff; +//GraphicBufferClass BackBuff; + +int Vesa_Scale_To_Vesa ( void * scr, void * dst, + int src_x , int src_y , int dst_x , int dst_y , + int src_wd , int src_hg , int dst_wd, int dst_hg, + BOOL trans , char * remap ) +{ + int area ; + int width , height ; + char * temp ; + + VideoViewPortClass * scr1 = ( VideoViewPortClass * ) scr ; + + width = src_wd - src_x ; + height = src_hg - src_y ; + area = width * height ; + temp = ( char * ) malloc ( area ) ; + if ( ! temp ) return 0 ; + + scr1 -> To_Buffer ( 0, 0, width , height , temp, area ); + GraphicBufferClass tempbuffer ( area , width , height , temp ) ; + + + tempbuffer . Scale ( * scr1 , + 0, 0, dst_x, dst_y, + width, height , dst_wd, dst_hg , + trans , remap ); + + + free ( temp ) ; + return 0 ; +} diff --git a/WWFLAT32/SVGAPRIM/VSCLTOVE.ASM b/WWFLAT32/SVGAPRIM/VSCLTOVE.ASM new file mode 100644 index 0000000..9c4fe87 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VSCLTOVE.ASM @@ -0,0 +1,730 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : VSCALE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Linear_Scale_To_Vesa -- Scales a graphic viewport to a vesa viewport * +;* Vesa_Scale_To_Linear -- Scales a Vesa viewport to a graphic viewport * +;* Vesa_Scale_To_Vesa -- Scales a vesa viewport to a vesa viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "mcgaprim.inc" +INCLUDE "gbuffer.inc" + + +GLOBAL Vesa_Asm_Set_Win : near +GLOBAL cpu_video_page : dword +GLOBAL cpu_page_limit : dword + + +CODESEG + + +;*************************************************************************** +;* LINEAR_SCALE_TO_VESA -- Scales a graphic viewport to a vesa viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/16/1995 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Vesa C near + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; pointer to source view port + ARG dest:DWORD ; pointer to destination view port + ARG src_x:DWORD ; source x offset into view port + ARG src_y:DWORD ; source y offset into view port + ARG dst_x:DWORD ; dest x offset into view port + ARG dst_y:DWORD ; dest y offset into view port + ARG src_width:DWORD ; width of source rectangle + ARG src_height:DWORD ; height of source rectangle + ARG dst_width:DWORD ; width of dest rectangle + ARG dst_height:DWORD ; width of dest height + ARG trans:DWORD ; is this transparent? + ARG remap:DWORD ; pointer to table to remap source + + ;*=================================================================== + ;* Define local variables to hold the viewport characteristics + ;*=================================================================== + local src_x0 : dword + local src_y0 : dword + local src_x1 : dword + local src_y1 : dword + + local dst_x0 : dword + local dst_y0 : dword + local dst_x1 : dword + local dst_y1 : dword + + local src_win_width : dword + local dst_win_width : dword + local dy_intr : dword + local dy_frac : dword + local dy_acc : dword + local dx_frac : dword + local dx_intr : dword + + local scan_line : dword + local counter_x : dword + local counter_y : dword + local remap_counter :dword + local entry : dword + + + ;*=================================================================== + ;* Check for scale error when to or from size 0,0 + ;*=================================================================== + cmp [dst_width],0 + je ??all_done + cmp [dst_height],0 + je ??all_done + cmp [src_width],0 + je ??all_done + cmp [src_height],0 + je ??all_done + + mov eax , [ src_x ] + mov ebx , [ src_y ] + mov [ src_x0 ] , eax + mov [ src_y0 ] , ebx + add eax , [ src_width ] + add ebx , [ src_height ] + mov [ src_x1 ] , eax + mov [ src_y1 ] , ebx + + mov eax , [ dst_x ] + mov ebx , [ dst_y ] + mov [ dst_x0 ] , eax + mov [ dst_y0 ] , ebx + add eax , [ dst_width ] + add ebx , [ dst_height ] + mov [ dst_x1 ] , eax + mov [ dst_y1 ] , ebx + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ src_x0 ] + mov ebx , [ src_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ src_y0 ] + mov ebx , [ src_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??clip_against_dest + mov bl , dl + test cl , 1000b + jz ??src_left_ok + xor eax , eax + mov [ src_x0 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x0 ] , eax + +??src_left_ok: + test cl , 0010b + jz ??src_bottom_ok + xor eax , eax + mov [ src_y0 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y0 ] , eax + +??src_bottom_ok: + test bl , 0100b + jz ??src_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ src_x1 ] , eax + sub eax , [ src_x ] + imul [ dst_width ] + idiv [ src_width ] + add eax , [ dst_x ] + mov [ dst_x1 ] , eax + +??src_right_ok: + test bl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ src_y1 ] , eax + sub eax , [ src_y ] + imul [ dst_height ] + idiv [ src_height ] + add eax , [ dst_y ] + mov [ dst_y1 ] , eax + +; Clip destination Rectangle against source Window boundaries. +??clip_against_dest: + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dst_x0 ] + mov ebx , [ dst_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dst_y0 ] + mov ebx , [ dst_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??all_done + or al , dl + jz ??do_scaling + mov bl , dl + test cl , 1000b + jz ??dst_left_ok + xor eax , eax + mov [ dst_x0 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x0 ] , eax + +??dst_left_ok: + test cl , 0010b + jz ??dst_bottom_ok + xor eax , eax + mov [ dst_y0 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y0 ] , eax + +??dst_bottom_ok: + test bl , 0100b + jz ??dst_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ dst_x1 ] , eax + sub eax , [ dst_x ] + imul [ src_width ] + idiv [ dst_width ] + add eax , [ src_x ] + mov [ src_x1 ] , eax + +??dst_right_ok: + test bl , 0001b + jz ??do_scaling + + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ dst_y1 ] , eax + sub eax , [ dst_y ] + imul [ src_height ] + idiv [ dst_height ] + add eax , [ src_y ] + mov [ src_y1 ] , eax + +??do_scaling: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ src_win_width ] , eax + mul [ src_y0 ] + add esi , [ src_x0 ] + add esi , eax + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov [ dst_win_width ] , eax + mul [ dst_y0 ] + add edi , [ dst_x0 ] + add edi , eax + call Vesa_Asm_Set_Win ; set the window + + mov eax , [ src_height ] + xor edx , edx + mov ebx , [ dst_height ] + idiv [ dst_height ] + imul eax , [ src_win_width ] + neg ebx + mov [ dy_intr ] , eax + mov [ dy_frac ] , edx + mov [ dy_acc ] , ebx + + mov eax , [ src_width ] + xor edx , edx + shl eax , 16 + idiv [ dst_width ] + xor edx , edx + shld edx , eax , 16 + shl eax , 16 + + mov ecx , [ dst_y1 ] + mov ebx , [ dst_x1 ] + sub ecx , [ dst_y0 ] + jz ??all_done + sub ebx , [ dst_x0 ] + jz ??all_done + + mov [ counter_y ] , ecx + mov [ scan_line ] , ebx + + cmp [ trans ] , 0 + jnz ??transparency + + cmp [ remap ] , 0 + jnz ??normal_remap + +; ************************************************************************* +; normal scale + mov ecx , ebx + and ecx , 01fh + lea ecx , [ ecx + ecx * 2 ] + shr ebx , 5 + neg ecx + mov [ counter_x ] , ebx + lea ecx , [ ??ref_point + ecx + ecx * 2 ] + mov [ entry ] , ecx + + ??outter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??in_range + mov ebx , [ scan_line ] + jmp ??trailing_entry + ??trailing_bytes: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + dec ebx + ??trailing_entry: + cmp edi , 0b0000h + jl ??trailing_bytes + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??end_of_scanline: + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + dec ebx + jg ??end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??next_line + +??in_range: + mov ebx , [ counter_x ] + jmp [ entry ] + ??inner_loop: + REPT 32 + mov cl , [ esi ] + add ecx , eax + adc esi , edx + mov [ edi ] , cl + inc edi + ENDM + ??ref_point: + dec ebx + jge ??inner_loop + ??next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??outter_loop + ret + + +; ************************************************************************* +; normal scale with remap + +??normal_remap: + mov ecx , ebx + mov [ dx_frac ] , eax + mov [ dx_intr ] , edx + + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 13 + mov [ counter_x ] , ebx + lea ecx , [ ??remapref_point + ecx ] + mov [ entry ] , ecx + + ??remapoutter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??remap_in_range + mov edx , [ scan_line ] + xor ebx , ebx + jmp ??remap_trailing_entry + ??remap_trailing_bytes: + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + dec edx + ??remap_trailing_entry: + cmp edi , 0b0000h + jl ??remap_trailing_bytes +??remap_no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??remap_end_of_scanline: + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + dec edx + jg ??remap_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??remap_next_line +??remap_in_range: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + mov edx , [ dx_intr ] + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??remapinner_loop: + REPT 32 + mov bl , [ esi ] + add ecx , [ dx_frac ] + adc esi , edx + mov cl , [ eax + ebx ] + mov [ edi ] , cl + inc edi + ENDM + ??remapref_point: + dec [ remap_counter ] + jge ??remapinner_loop + ??remap_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??remapoutter_loop + ret + + +;**************************************************************************** +; scale with transparency + +??transparency: + cmp [ remap ] , 0 + jnz ??trans_remap + +; ************************************************************************* +; normal scale with transparency + mov ecx , ebx + and ecx , 01fh + imul ecx , -13 + shr ebx , 5 + mov [ counter_x ] , ebx + lea ecx , [ ??tr_ref_point + ecx ] + mov [ entry ] , ecx + + ??tr_outter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??tr_in_range + + mov ebx , [ scan_line ] + jmp ??tr_trailing_entry + ??tr_trailing_bytes: + mov cl , [ esi ] + test cl , cl + jz ??tr_skip + mov [ edi ] , cl + ??tr_skip: + add ecx , eax + adc esi , edx + inc edi + dec ebx + ??tr_trailing_entry: + cmp edi , 0b0000h + jl ??tr_trailing_bytes + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??tr_end_of_scanline: + mov cl , [ esi ] + test cl , cl + jz ??tr_skip1 + mov [ edi ] , cl + ??tr_skip1: + add ecx , eax + adc esi , edx + inc edi + dec ebx + jg ??tr_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??tr_next_line + +??tr_in_range: + mov ebx , [ counter_x ] + jmp [ entry ] + ??tr_inner_loop: + REPT 32 + local skip + mov cl , [ esi ] + test cl , cl + jz skip + mov [ edi ] , cl + skip: + add ecx , eax + adc esi , edx + inc edi + ENDM + ??tr_ref_point: + dec ebx + jge ??tr_inner_loop + ??tr_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??tr_skip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??tr_skip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??tr_outter_loop + ret + + + +; ************************************************************************* +; normal scale with remap and transparency + +??trans_remap: + mov ecx , ebx + mov [ dx_frac ], eax + mov [ dx_intr ] , edx + + and ecx , 01fh + mov eax , [ remap ] + shr ebx , 5 + imul ecx , - 17 + mov [ counter_x ] , ebx + lea ecx , [ ??trans_remapref_point + ecx ] + mov [ entry ] , ecx + + ??trans_remapoutter_loop: + mov ebx , [ scan_line ] + push esi + add ebx , edi + xor ecx , ecx + add ebx , [ cpu_video_page ] + push edi + cmp ebx , [ cpu_page_limit ] + jl ??trans_remap_in_range + mov edx , [ scan_line ] + xor ebx , ebx + jmp ??trans_remap_trailing_bytes1 + ??trans_remap_trailing_bytes: + mov bl , [ esi ] + test bl , bl + jz ??trans_remp + mov cl , [ eax + ebx ] + mov [ edi ] , cl + ??trans_remp: + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + inc edi + dec edx + ??trans_remap_trailing_bytes1: + cmp edi , 0b0000h + jl ??trans_remap_trailing_bytes +??trans_remap_no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window +??trans_remap_end_of_scanline: + mov bl , [ esi ] + test bl , bl + jz ??trans_remp1 + mov cl , [ eax + ebx ] + mov [ edi ] , cl + ??trans_remp1: + add ecx , [ dx_frac ] + adc esi , [ dx_intr ] + inc edi + dec edx + jg ??trans_remap_end_of_scanline + sub [ dword ptr esp ] , 010000h + jmp ??trans_remap_next_line + +??trans_remap_in_range: + mov ebx , [ counter_x ] + push esi + mov [ remap_counter ] , ebx + push edi + mov edx , [ dx_intr ] + xor ecx , ecx + xor ebx , ebx + jmp [ entry ] + ??trans_remapinner_loop: + REPT 32 + local skip + mov bl , [ esi ] + test bl , bl + jz skip + mov cl , [ eax + ebx ] + mov [ edi ] , cl + skip: + + add ecx , [ dx_frac ] + adc esi , edx + inc edi + ENDM + ??trans_remapref_point: + dec [ remap_counter ] + jge ??trans_remapinner_loop + ??trans_remap_next_line: + pop edi + pop esi + add edi , [ dst_win_width ] + add esi , [ dy_intr ] + + mov ebx , [ dy_acc ] + add ebx , [ dy_frac ] + jle ??trans_remapskip_line + add esi , [ src_win_width ] + sub ebx , [ dst_height ] +??trans_remapskip_line: + dec [ counter_y ] + mov [ dy_acc ] , ebx + jnz ??trans_remapoutter_loop + ret + +??all_done: + ret + + ENDP Linear_Scale_To_Vesa + +END diff --git a/WWFLAT32/SVGAPRIM/VTOBUFF.ASM b/WWFLAT32/SVGAPRIM/VTOBUFF.ASM new file mode 100644 index 0000000..a7adfd7 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VTOBUFF.ASM @@ -0,0 +1,344 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOBUFFER.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +TRANSP equ 0 + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +CODESEG + + +;*************************************************************************** +;* VIVC::TOBUFFER -- Copies a virtual viewport to a linear buffer * +;* * +;* INPUT: BYTE * dest - buffer to copy to * +;* size - size of the buffer to copy to * +;* x_pixel - x pixel on viewport to copy from * +;* y_pixel - y pixel on viewport to copy from * +;* pixel_width - the width of copy region * +;* pixel_height - the height of copy region * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + PROC Vesa_To_Buffer C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this:DWORD ; this is a class member function + ARG x_pixel:DWORD ; Page X pixel coordinate. + ARG y_pixel:DWORD ; Page Y pixel coordinate. + ARG pixel_width:DWORD ; Width of region in pixels. + ARG pixel_height:DWORD ; Height of region in pixels. + ARG dest:DWORD ; the buffer to copy to + ARG size:DWORD ; the size of the buffer + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + +; Clip dest Rectangle against source Window boundaries. + mov [ dest_x1 ] , 0 + mov [ dest_y1 ] , 0 + + mov esi , [ this ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??scr_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ dest_x1 ] , eax + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ dest_y1 ] , eax + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + Call Vesa_Asm_Set_Win ; set the window + mov esi , edi + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov edi , [ dest ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ dest_ajust_width ] , eax + + mov eax , [ dest_y1 ] + mul [ pixel_width ] + add eax , [ dest_x1 ] + add edi , eax + + mov edx , [ y1_pixel ] + sub edx , [ y_pixel ] + jz ??real_out + + mov eax , [ x1_pixel ] + sub eax , [ x_pixel ] + jz ??real_out + + mov ebx , [ pixel_width ] + imul ebx , edx + cmp ebx , [ size ] + jg ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + cmp esi , 0b0000h + mov ebx , eax + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + + + +IF TRANSP + + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + +ENDP Vesa_To_Buffer +END + diff --git a/WWFLAT32/SVGAPRIM/VTOPAGE.ASM b/WWFLAT32/SVGAPRIM/VTOPAGE.ASM new file mode 100644 index 0000000..52ef462 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VTOPAGE.ASM @@ -0,0 +1,343 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 9, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page(GVC) -- Copies a linear buffer to a graphic viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +TRANSP equ 0 + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;* 12/09/1994 PWG : Made SVGA Modifications * +;*=========================================================================* + PROC Vesa_Buffer_To_Page C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + +; ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL scr_x1 : dword + LOCAL scr_y1 : dword + LOCAL dest_ajust_width:DWORD + LOCAL scr_ajust_width:DWORD + LOCAL dest_area : dword + + cmp [ src ] , 0 + jz ??real_out + + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x1 ] , 0 + mov [ scr_y1 ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x1 ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y1 ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + + cld + + mov eax , [ (VideoViewPort esi) . VIVPXAdd ] + add eax , [ (VideoViewPort esi) . VIVPWidth ] + mov edi , [ (VideoViewPort esi) . VIVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + call Vesa_Asm_Set_Win ; set the window + + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_ajust_width ] , ecx + + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_ajust_width ] , eax + + mov eax , [ scr_y1 ] + mul [ pixel_width ] + add eax , [ scr_x1 ] + add esi , eax + + + mov edx , [ y1_pixel ] + sub edx , [ y_pixel ] + jz ??real_out + + mov eax , [ x1_pixel ] + sub eax , [ x_pixel ] + jz ??real_out + + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp edi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing: + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + + +??forward_Blit_trans: + + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + + ENDP Vesa_Buffer_To_Page +END + diff --git a/WWFLAT32/SVGAPRIM/VTXTPRNT.ASM b/WWFLAT32/SVGAPRIM/VTXTPRNT.ASM new file mode 100644 index 0000000..f6e8c7ab --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VTXTPRNT.ASM @@ -0,0 +1,444 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VESA_Print -- Assembly VESA text print routine * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "svgaprim.inc" +INCLUDE ".\gbuffer.inc" + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* + GLOBAL FontPtr:DWORD + GLOBAL ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + + CODESEG + + +;*************************************************************************** +;* VESA_PRINT -- Assembly VESA text print routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Vesa_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + local ptr_string:dword ; pointer to string + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL remainder:DWORD + LOCAL fullwidth:DWORD + LOCAL currwidth:DWORD + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + + mov [startdraw],edi ; save it off. + + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??done + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??done ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp eax,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp eax,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + add [startdraw],edx ; save start draw for next character. + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + call Vesa_Asm_Set_Win ; adjust edi & vesa page + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + cmp edi,0b0000h ; have we gone over win edge + jl ??draw_char ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + +??loop_top: + stosb ; store the value + cmp edi,0b0000h ; have we gone over win edge + jl ??top_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??top_no_window_change: + + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + cmp edi,0b0000h ; have we gone over win edge + jl ??top2_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window + +??top2_no_window_change: + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + cmp edi,0b0000h ; have we gone over win edge + jl ??lo_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??lo_no_window_change: + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [edi],al ; write it out +??skiphi: + inc edi + cmp edi,0b0000h ; have we gone over win edge + jl ??hi_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??hi_no_window_change: + + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + cmp edi,0b0000h ; have we gone over win edge + jl ??next_row_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??next_row_no_window_change: + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + cmp edi,0b0000h ; have we gone over win edge + jl ??bottom_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??bottom_no_window_change: + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + cmp edi,0b0000h ; have we gone over win edge + jl ??bottom2_no_window_change ; if not keep writing to window + add edi , [ cpu_video_page ] + call Vesa_Asm_Set_Win ; instead switch to next window +??bottom2_no_window_change: + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + +;----------------------------------- special case line feeds ---------------------------------------- +; JRJ 05/01/95 This is the problem However made this change introduced +; a error in the code, this function is not supposed to handle +; Text wrapping +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + dec [dword ptr string] ; overflow by one charater + jmp ??done + + +??line_feed: + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??done ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. + mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + mov [x_pixel],0 ; zero out x_pixel + + jmp ??next_char +??done: + mov eax,[string] ; return the number of charaters + sub eax,[ptr_string] ; printed + ret + + ENDP Vesa_Print + +END diff --git a/WWFLAT32/SVGAPRIM/VVBLIT.ASM b/WWFLAT32/SVGAPRIM/VVBLIT.ASM new file mode 100644 index 0000000..ee66b26 --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VVBLIT.ASM @@ -0,0 +1,597 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + +TRANSP equ 0 + +POOLSIZE equ 8000 + +CODESEG + + PROC Vesa_Blit_To_Vesa C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + local total_lines : dword + local count_dy : dword + local mem_page : dword + local vesa_page : dword + local mem_pool : byte : POOLSIZE + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov esi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add esi , [ x_pixel ] + mov [ source_area ] , ecx + add esi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + + push eax + mov [ mem_page ] , 0 + mov [ vesa_page ] , 0 + + mov [ total_lines ] , edx + mov eax , POOLSIZE + xor edx , edx + idiv [ dword ptr esp ] + mov [ count_dy ] , eax + pop eax + +; ************************************************************************** +; check direction of motions + cmp esi , edi + jl ??backupward_blit + + ret + +;*********************************************************************** +; Backupward blit + +??back_mem_loop: + push edi + lea edi , [ mem_pool ] + mov edx , [ count_dy ] + call ??vesa_to_memory + pop edi + + push esi + lea esi , [ mem_pool ] + mov edx , [ count_dy ] + call ??memory_to_vesa + pop esi + +??backupward_blit: + mov edx , [ total_lines ] + sub edx , [ count_dy ] + mov [ total_lines ] , edx + jg ??back_mem_loop + + add edx , [ count_dy ] + push edi + push edx + lea edi , [ mem_pool ] + call ??vesa_to_memory + pop edx + pop edi + + push esi + lea esi , [ mem_pool ] + call ??memory_to_vesa + pop esi + ret + + + +??real_out: + ret + + + + +; ******************************************************************** +; Move Vesa video page to memory buffer + +??vesa_to_memory: + + xchg edi , esi + add edi , [ mem_page ] + call Vesa_Asm_Set_Win + xchg edi , esi + +IF TRANSP + test [ trans ] , 1 + jnz ??tomem_forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??tomem_forward_loop_bytes + +??tomem_forward_loop_dword: + + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??tomem_in_range + + xor ecx , ecx + mov ebx , eax + cmp esi , 0b0000h + jge ??tomem_no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??tomem_no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + dec edx ; decrement the height + jnz ??tomem_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +??tomem_in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + dec edx + jnz ??tomem_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +??tomem_forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??tomem_in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??tomem_no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??tomem_no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??tomem_in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + dec edx ; decrement the height + jnz ??tomem_forward_loop_bytes + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn + +IF TRANSP +??tomem_forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??tomem_transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??tomem_forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??tomem_forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc edi + ENDM + ??tomem_transp_reference: + dec ecx + jge ??tomem_forward_trans_line + add esi , [ scr_ajust_width ] + dec edx + jnz ??tomem_forward_loop_trans + mov edx , [ cpu_video_page ] + mov [ mem_page ] , edx + retn +ENDIF + + + + + +;************************************************************************* +; copy from memory to vesa page + +??memory_to_vesa: + add edi , [ vesa_page ] + Call Vesa_Asm_Set_Win + +IF TRANSP + test [ trans ] , 1 + jnz ??tovesa_forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??tovesa_forward_loop_bytes + +??tovesa_forward_loop_dword: + + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??tovesa_in_range + + xor ecx , ecx + cmp edi , 0b0000h + mov ebx , eax + jge ??tovesa_no_trailing + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??tovesa_no_trailing: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window + + mov ecx , ebx + rep movsb + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??tovesa_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +??tovesa_in_range: + + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add edi , [ dest_ajust_width ] + dec edx + jnz ??tovesa_forward_loop_dword + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +??tovesa_forward_loop_bytes: + lea ebx , [ edi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??tovesa_in_range_bytes + + xor ecx , ecx + cmp edi , 0b0000h + jge ??tovesa_no_trailing_bytes + mov ecx , 0b0000h + sub ecx , edi + sub ebx , ecx + rep movsb +??tovesa_no_trailing_bytes: + add edi , [ cpu_video_page ] + Call Vesa_Asm_Set_Win ; set the window +??tovesa_in_range_bytes: + mov ecx , ebx + rep movsb + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??tovesa_forward_loop_bytes + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn + +IF TRANSP + + +??tovesa_forward_Blit_trans: + + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??tovesa_transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??tovesa_forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??tovesa_forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??tovesa_transp_reference: + dec ecx + jge ??tovesa_forward_trans_line + add edi , [ dest_ajust_width ] + dec edx + jnz ??tovesa_forward_loop_trans + mov edx , [ cpu_video_page ] + mov [ vesa_page ] , edx + retn +ENDIF + + +ENDP Vesa_Blit_To_Vesa + + + +END diff --git a/WWFLAT32/SVGAPRIM/VVETOLB.ASM b/WWFLAT32/SVGAPRIM/VVETOLB.ASM new file mode 100644 index 0000000..2666d3e --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VVETOLB.ASM @@ -0,0 +1,409 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Westwood 32 bit Library * +;* * +;* File Name : BITBLIT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : December 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + +INCLUDE "svgaprim.inc" +INCLUDE "gbuffer.inc" + + +TRANSP equ 0 + +CODESEG + + +;*************************************************************************** +;* GVC::BLIT -- Copies a buffer to a graphic page non-linearly * +;* * +;* NOTE: All coordinate values are expressed in pixels * +;* * +;* INPUT: VirtualViewPortClass *dest - Virtual View Port to copy to * +;* WORD src_x - Src x position to copy from * +;* WORD src_y - Src y position to copy from * +;* WORD dst_x - Dest x position to copy to * +;* WORD dst_y - Dest y position to copy to * +;* WORD w - Width of region to copy * +;* WORD h - Height of region to copy * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. * +;* * +;* HISTORY: * +;* 05/11/1994 PWG : Created. * +;* 08/05/1994 PWG : Fixed clipping problem * +;*=========================================================================* + PROC Vesa_Blit_To_Linear C near + USES ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG this :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG dest_x0 :dword + ARG dest_y0 :dword + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG trans :DWORD ; do we deal with transparents? + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL x1_pixel :dword + LOCAL y1_pixel :dword + LOCAL dest_x1 : dword + LOCAL dest_y1 : dword + LOCAL scr_ajust_width:DWORD + LOCAL dest_ajust_width:DWORD + LOCAL source_area : dword + LOCAL dest_area : dword + +; Clip Source Rectangle against source Window boundaries. + mov esi , [ this ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??clip_against_dest + + test cl , 1000b + jz ??scr_left_ok + mov [ x_pixel ] , 0 + +??scr_left_ok: + test cl , 0010b + jz ??scr_bottom_ok + mov [ y_pixel ] , 0 + +??scr_bottom_ok: + test dl , 0100b + jz ??scr_right_ok + mov eax , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov [ x1_pixel ] , eax +??scr_right_ok: + test dl , 0001b + jz ??clip_against_dest + mov eax , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + + +; Clip Source Rectangle against destination Window boundaries. +??clip_against_dest: + + mov eax , [ dest_x0 ] + mov ebx , [ dest_y0 ] + sub eax , [ x_pixel ] + sub ebx , [ y_pixel ] + add eax , [ x1_pixel ] + add ebx , [ y1_pixel ] + mov [ dest_x1 ] , eax + mov [ dest_y1 ] , ebx + + mov esi , [ dest ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x0 ] + mov ebx , [ dest_x1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( VideoViewPort esi) . VIVPHeight ] ; get height into register + mov eax , [ dest_y0 ] + mov ebx , [ dest_y1 ] + shld ecx , eax , 1 + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + or al , dl + jz ??do_blit + + test cl , 1000b + jz ??dest_left_ok + mov eax , [ dest_x0 ] + mov [ dest_x0 ] , 0 + sub [ x_pixel ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + mov eax , [ dest_y0 ] + mov [ dest_y0 ] , 0 + sub [ y_pixel ] , eax + + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + mov ebx , [ (VideoViewPort esi) . VIVPWidth ] ; get width into register + mov eax , [ dest_x1 ] + mov [ dest_x1 ] , ebx + sub eax , ebx + sub [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + mov ebx , [ (VideoViewPort esi) . VIVPHeight ] ; get width into register + mov eax , [ dest_y1 ] + mov [ dest_y1 ] , ebx + sub eax , ebx + sub [ y1_pixel ] , eax + +??do_blit: + + cld + mov ebx , [ this ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + mov [ source_area ] , ecx + add edi , eax + call Vesa_Asm_Set_Win ; set the window + mov esi , edi + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ scr_ajust_width ] , ecx + + mov ebx , [ dest ] + mov edi , [ (VideoViewPort ebx) . VIVPOffset ] + mov eax , [ (VideoViewPort ebx) . VIVPXAdd ] + add eax , [ (VideoViewPort ebx) . VIVPWidth ] + mov ecx , eax + mul [ dest_y0 ] + add edi , [ dest_x0 ] + mov [ dest_area ] , ecx + add edi , eax + + mov eax , [ dest_x1 ] + sub eax , [ dest_x0 ] + jz ??real_out + sub ecx , eax + mov [ dest_ajust_width ] , ecx + + mov edx , [ dest_y1 ] + sub edx , [ dest_y0 ] + jz ??real_out + +; ******************************************************************** +; Forward bitblit only + +IF TRANSP + test [ trans ] , 1 + jnz ??forward_Blit_trans +ENDIF + + +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + jl ??in_range + + xor ecx , ecx + mov ebx , eax + cmp esi , 0b0000h + jge ??no_trailing + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi + + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_dword + ret + +??in_range: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_dword + ret + +??forward_loop_bytes: + lea ebx , [ esi + eax ] + add ebx , [ cpu_video_page ] + cmp ebx , [ cpu_page_limit ] + mov ebx , eax + jl ??in_range_bytes + + xor ecx , ecx + cmp esi , 0b0000h + jge ??no_trailing_bytes + mov ecx , 0b0000h + sub ecx , esi + sub ebx , ecx + rep movsb +??no_trailing_bytes: + add esi , [ cpu_video_page ] + xchg edi , esi + Call Vesa_Asm_Set_Win ; set the window + xchg edi , esi +??in_range_bytes: + mov ecx , ebx + rep movsb + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + ret + +IF TRANSP + +??forward_Blit_trans: + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] + neg ecx + shr eax , 5 + lea ecx , [ ??transp_reference + ecx * 2 ] + mov [ y1_pixel ] , ecx + +??forward_loop_trans: + mov ecx , eax + jmp [ y1_pixel ] +??forward_trans_line: + REPT 32 + local transp_pixel + mov bl , [ esi ] + test bl , bl + jz transp_pixel + mov [ edi ] , bl + transp_pixel: + inc esi + inc edi + ENDM + ??transp_reference: + dec ecx + jge ??forward_trans_line + add esi , [ scr_ajust_width ] + add edi , [ dest_ajust_width ] + dec edx + jnz ??forward_loop_trans + ret +ENDIF + +??real_out: + ret + + +ENDP Vesa_Blit_To_Linear + + +END diff --git a/WWFLAT32/SVGAPRIM/VVETOSCL.CPP b/WWFLAT32/SVGAPRIM/VVETOSCL.CPP new file mode 100644 index 0000000..d1e7bba --- /dev/null +++ b/WWFLAT32/SVGAPRIM/VVETOSCL.CPP @@ -0,0 +1,65 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Testing memory. * + * * + * File Name : TEST.CPP * + * * + * Programmer : Julio Jerez * + * * + * Start Date : Feb 13, 1995 * + * * + * * + *-------------------------------------------------------------------------* +*/ +#include +#include +#include + +int Vesa_Scale_To_Linear ( void * scr, void * dst, + int src_x , int src_y , int dst_x , int dst_y , + int src_wd , int src_hg , int dst_wd, int dst_hg, + BOOL trans , char * remap ) +{ + int area ; + int width , height ; + char * temp ; + + VideoViewPortClass * scr1 = ( VideoViewPortClass * ) scr ; + + width = src_wd - src_x ; + height = src_hg - src_y ; + area = width * height ; + temp = ( char * ) malloc ( area ) ; + if ( ! temp ) return 0 ; + + scr1 -> To_Buffer ( 0, 0, width , height , temp, area ); + GraphicBufferClass tempbuffer ( area , width , height , temp ) ; + + Linear_Scale_To_Linear ( & tempbuffer , dst , + 0 , 0 , dst_x , dst_y , + width , height , dst_wd , dst_hg , + trans , remap ) ; + + free ( temp ) ; + return 0 ; +} diff --git a/WWFLAT32/TILE/ICONSET.BAK b/WWFLAT32/TILE/ICONSET.BAK new file mode 100644 index 0000000..5d00e9b --- /dev/null +++ b/WWFLAT32/TILE/ICONSET.BAK @@ -0,0 +1,362 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +#include +#include +#include +#include +#include +#include +#include + +unsigned long Misc; + +void * cdecl Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void cdecl Free_Icon_Set(void const *iconset); +long cdecl Get_Icon_Set_Size(void const *iconset); +int cdecl Get_Icon_Set_Width(void const *iconset); +int cdecl Get_Icon_Set_Height(void const *iconset); +void * cdecl Get_Icon_Set_Icondata(void const *iconset); +void * cdecl Get_Icon_Set_Trans(void const *iconset); +void * cdecl Get_Icon_Set_Remapdata(void const *iconset); +void * cdecl Get_Icon_Set_Palettedata(void const *iconset); +int cdecl Get_Icon_Set_Count(void const *iconset); +void * cdecl Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; + long Map; // Icon map offset (if present). +} IControl_Type; + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * cdecl Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = sinf.Width<<3; + idata->Height = sinf.Height<<3; + idata->Allocated = allocated; + idata->Icons = sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = idata->Map + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if (buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = Add_Long_To_Pointer(iconsetptr, idata->Map); + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void cdecl Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long cdecl Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int cdecl Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int cdecl Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * cdecl Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->Icons); + } + return(ptr); +} + +void * cdecl Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int cdecl Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * cdecl Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->Map); + } + return(ptr); +} + + + diff --git a/WWFLAT32/TILE/ICONSET.CPP b/WWFLAT32/TILE/ICONSET.CPP new file mode 100644 index 0000000..f740e52 --- /dev/null +++ b/WWFLAT32/TILE/ICONSET.CPP @@ -0,0 +1,364 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +#include +#include +#include +#include +#include +#include +#include + +unsigned long Misc; + +void * cdecl Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void cdecl Free_Icon_Set(void const *iconset); +long cdecl Get_Icon_Set_Size(void const *iconset); +int cdecl Get_Icon_Set_Width(void const *iconset); +int cdecl Get_Icon_Set_Height(void const *iconset); +void * cdecl Get_Icon_Set_Icondata(void const *iconset); +void * cdecl Get_Icon_Set_Trans(void const *iconset); +void * cdecl Get_Icon_Set_Remapdata(void const *iconset); +void * cdecl Get_Icon_Set_Palettedata(void const *iconset); +int cdecl Get_Icon_Set_Count(void const *iconset); +void * cdecl Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; + long Map; // Icon map offset (if present). +} IControl_Type; + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * cdecl Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = sinf.Width<<3; + idata->Height = sinf.Height<<3; + idata->Allocated = allocated; + idata->Icons = sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = idata->Map + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if (buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = Add_Long_To_Pointer(iconsetptr, idata->Map); + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void cdecl Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long cdecl Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int cdecl Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int cdecl Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * cdecl Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->Icons); + } + return(ptr); +} + +void * cdecl Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int cdecl Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * cdecl Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->Map); + } + return(ptr); +} + + + diff --git a/WWFLAT32/TILE/MAKEFILE b/WWFLAT32/TILE/MAKEFILE new file mode 100644 index 0000000..4270382 --- /dev/null +++ b/WWFLAT32/TILE/MAKEFILE @@ -0,0 +1,192 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = tile +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + iconset.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/TILE/TILE.H b/WWFLAT32/TILE/TILE.H new file mode 100644 index 0000000..e8f2c44 --- /dev/null +++ b/WWFLAT32/TILE/TILE.H @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + +#endif //TILE_H + + diff --git a/WWFLAT32/TILE/TILE.H.BAK b/WWFLAT32/TILE/TILE.H.BAK new file mode 100644 index 0000000..a5f72c0 --- /dev/null +++ b/WWFLAT32/TILE/TILE.H.BAK @@ -0,0 +1,57 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + +#endif //TILE_H + + \ No newline at end of file diff --git a/WWFLAT32/TIMER/MAKEFILE b/WWFLAT32/TIMER/MAKEFILE new file mode 100644 index 0000000..cee70bc --- /dev/null +++ b/WWFLAT32/TIMER/MAKEFILE @@ -0,0 +1,205 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = timer +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + timer.obj & + timerdwn.obj & + timerini.obj & + timera.obj + + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +timereal.ibn: timereal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip timereal.exe + ebn timereal.exe + +timereal.obj: timereal.asm + tasm /zn /la /ml /m2 timereal.asm + +timera.obj: timereal.ibn timera.asm + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/TIMER/TIMER.CPP b/WWFLAT32/TIMER/TIMER.CPP new file mode 100644 index 0000000..6a9c730 --- /dev/null +++ b/WWFLAT32/TIMER/TIMER.CPP @@ -0,0 +1,178 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + switch (timer) { + case BT_USER: + Get_Ticks = Get_User_Tick_Count; + break; + + default: + case BT_SYSTEM: + Get_Ticks = Get_System_Tick_Count; + break; + } + if (on && TimerSystemOn) Start(); +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} + + diff --git a/WWFLAT32/TIMER/TIMER.H b/WWFLAT32/TIMER/TIMER.H new file mode 100644 index 0000000..a66bdfd --- /dev/null +++ b/WWFLAT32/TIMER/TIMER.H @@ -0,0 +1,153 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: + long (*Get_Ticks)(void); // System timer fetch. +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// + +extern TimerClass TickCount; +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); + void far Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void Disable_Timer_Interrupt(void); + void Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); + + +#endif // TIMER_H + + diff --git a/WWFLAT32/TIMER/TIMERA.ASM b/WWFLAT32/TIMER/TIMERA.ASM new file mode 100644 index 0000000..b362864 --- /dev/null +++ b/WWFLAT32/TIMER/TIMERA.ASM @@ -0,0 +1,1018 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer stuff * +;* * +;* File Name : TIMERA.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 6, 1994 * +;* * +;* Last Update : March 14, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Timer_Size -- return size of real mode timer code. * +;* Increment_Tick_Count -- Increments WW system timer. * +;* Timer_Interrupt -- Temp routine to mimic a timer system calling our. * +;* Install_Timer_Interrupt -- Installs the timer interrupt routine. * +;* Remove_Timer_Interrupt -- Removes the timer interrupt vectors. ** +;* Set_Timer_Frequency -- Set the frequency of the timer. * +;* Set_Timer_Rate -- Set the rate of the timer. * +;* Get_System_Tick_Count -- Returns the system tick count. * +;* Get_User_Tick_Count -- Get tick count of user clock. * +;* Increment_Timers -- Increments system and user timers. * +;* Get_Num_Interrupts -- Returns the number of interrupts that have occured* +;* Timer_Interrupt_Func -- Handles core timer code * +;* Disable_Timer_Interrupt -- Disables at the hardware level * +;* Enable_Timer_Interrupt -- Enables at the hardware level * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +DPMI_INTR equ 31h +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// + +SYSTEM_TIMER_FREQ equ 60 ; Frequency of system timer. + +;******************************************************************** +; There are two ways to call our interrupt chain in protected mode. +; The obvious way it to call the address that we replaced in the +; PM interrupt chain. This method is a little difficult but works and +; should always work. + + +;******************************************************************** +; The RM and PM interrupts can be installed at the same time or seperately. +; Installing at the same time is the best method, the other method +; can be used to faciliated debugging when you only want one or the other +; called. Both methods work. +; -SKB July 21, 1994. +INSTALL_SEPERATE equ FALSE + + +INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0 +CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts. +IRQ0INTNUM EQU 08h ; IRQ0 interrupt vector number. +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT + +TIMER_CONST EQU 1193180 ; TIMER_CONST / FREQ = rate to set 8259 chip + +TIMERCMDREG EQU 043H ; timer command register port. +TIMER0PORT EQU 040H ; timer channel 0 port +TIMETYPE EQU 036H ; type of timer. + ; 36H = 0011 0110 + ; -- select channel 0 + ; -- read/load low byte then high byte for timer + ; --- timer mode 3 + ; - 0 - binary , 1 - BCD data + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Structs //////////////////////////////////////// + + +; Structure of memory in real mode handler. +; This is at the very start of the real mode code. +; This information may not change unless the real mode version is also changed. + +STRUC TimerType +; For speed, PM uses a DD while RM used DW +; For speed, SysRate and SysError are DD in PM and are DW in real mode. + + TrueRate DD ? ; True rate of clock. (only use word) + SysTicks DD ? ; Tick count of timer. + SysRate DD ? ; Desired rate of timer. + SysError DD ? ; Amount of error in clock rate for desired frequency. + SysCurRate DW ? ; Keeps track of when to increment timer. + SysCurError DW ? ; Keeps track of amount of error in timer. + + UserTicks DD ? ; Tick count of timer. + UserRate DD ? ; Desired rate of timer. + UserError DD ? ; Amount of error in clock rate for desired frequency. + UserCurRate DW ? ; Keeps track of when to increment timer. + UserCurError DW ? ; Keeps track of amount of error in timer. + + DosAdder DW ? ; amount to add to DosFraction each interrupt. + DosFraction DW ? ; Call dos when overflowed. + + OldRMI DD ? ; The origianl RM interrupt seg:off. + OldPMIOffset DD ? ; The origianl PM interrupt offset + OldPMISelector DD ? ; The original PM interrupt segment. + + CodeOffset DW ? ; Offset of the code in the RM stuff. + CallRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + + PMIssuedInt DD 0 ; PM signals RM to just call Int chain. + +; These are just used for information on testing. When all is done, they can +; be removed, but why? The don't add too much proccessing time and can +; be useful. + NumPMInts DD ? ; Number of PM interrupts + NumRMInts DD ? ; Number of RM interrupts. + +ENDS + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes ////////////////////////////////////// + +GLOBAL Get_System_Tick_Count:NEAR +GLOBAL Get_User_Tick_Count:NEAR +GLOBAL Get_RM_Timer_Address:NEAR +GLOBAL Get_RM_Timer_Size:NEAR +GLOBAL Set_Timer_Frequency:NEAR +GLOBAL Timer_Interrupt:NEAR +GLOBAL Install_Timer_Interrupt:NEAR +GLOBAL Remove_Timer_Interrupt:NEAR +GLOBAL Get_Num_Interrupts:NEAR +GLOBAL Timer_Interrupt_Func:FAR +GLOBAL Disable_Timer_Interrupt:NEAR +GLOBAL Enable_Timer_Interrupt:NEAR + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Data //////////////////////////////////////// + DATASEG + +; The current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "timereal.ibn" +LABEL RealBinEnd BYTE + + + + +LABEL LockedDataStart BYTE +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. +LABEL LockedDataEnd BYTE + + +InitFlags DD 0 ; Flags to indicate what has been initialized. + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1h ; Allocation of RM was successful. +IF_SET_VECTORS equ 2h ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4h ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8h ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10h ; Timer rate was changed. +IF_FUNCTIONAL equ 20h ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40h ; Timer is in and functional. + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Code //////////////////////////////////////// + + CODESEG + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Init routines //////////////////////////////////////// + +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_TIMER_SIZE -- return size of real mode timer code. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + +;*************************************************************************** +;* SET_TIMER_RATE -- Set the rate of the timer. * +;* * +;* * +;* INPUT: ebx = rate to set timer were rate = 1193180 / freq * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/11/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Rate Near + + push eax + pushf ; to save int enable flag + cli ; disable interupts while setting up swapper + + mov al,TIMETYPE ; setup to modify timer 0 + out TIMERCMDREG,al ; send command. + mov eax,ebx ; get rate. + out TIMER0PORT,al ; output low byte + mov al,ah + out TIMER0PORT,al ; output high byte + + sti + popf ; get int enable flag. + pop eax + + ret +ENDP Set_Timer_Rate + +;*************************************************************************** +;* SET_TIMER_FREQUENCY -- Set the frequency of the timer. * +;* * +;* * +;* INPUT: INT Frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Frequency C NEAR + USES eax,ebx,ecx,edx + ARG freq:DWORD + LOCAL clockrate:DWORD + LOCAL clockfreq:DWORD + + test [InitFlags],IF_FUNCTIONAL ; Is our timer system installed? + jz ??timer_not_installed ; if not, this is not legal. + + + ; Find out the greater of the frequencies (user or system.) + ; Assign the True rate value based of of that. + ; store the max frequency in ecx. + mov ecx,[freq] ; get user frequency. + cmp ecx,SYSTEM_TIMER_FREQ ; compare it with system frequency + jg ??user_is_fastest ; is user frequency faster? + mov ecx,SYSTEM_TIMER_FREQ ; no, set clock freq to system frequency. +??user_is_fastest: + + ; now get the rate that the clock will be set at. + ; ecx is still max frequency. + mov esi,[RealModePtr] + + mov eax,TIMER_CONST ; get the clock constant value. + xor edx,edx ; zero for divide. + div ecx ; rate = TC/freq => eax = eax/ecx; + mov [(TimerType PTR esi).TrueRate],eax ; Set our true rate. + mov ebx,eax ; save for later. DO NOT USE UNTIL CALL + + ; Set up variables to call DOS correctly. + ; When DosFraction overflows, DOS is called. + mov [(TimerType PTR esi).DosAdder],ax ; Init count to until call dos. + mov [(TimerType PTR esi).DosFraction],0 ; init the fraction. + + ; now set up the system timer. + mov ecx,SYSTEM_TIMER_FREQ ; get frequency. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).SysCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).SysCurError],ax ; Init current stuff. + mov [(TimerType PTR esi).SysRate],eax ; Save rate of timer + mov [(TimerType PTR esi).SysError],edx ; Save error of timer + ; Do not set SysTicks to zero since it always has the same frequency. It + ; should be zero out only when the system clock is installed. + + ; now set up the user timer. + mov ecx,[freq] ; get frequency of user timer. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).UserCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).UserCurError],ax; Init current stuff. + mov [(TimerType PTR esi).UserRate],eax ; Save rate of timer + mov [(TimerType PTR esi).UserError],edx ; Save error of timer + mov [(TimerType PTR esi).UserTicks],0 ; User timer sets to zero when freq change. + + ; Call function to set the rate of the chip, ebx = rate from above. + call Set_Timer_Rate + +??timer_not_installed: + + ret + + ENDP + + +;*************************************************************************** +;* INSTALL_TIMER_Interrupt -- Installs the timer interrupt routine. * +;* * +;* * +;* INPUT: VOID * pointer to RM binary in PM memory. * +;* LONG Size of RM binary. * +;* INT frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Install_Timer_Interrupt C Near + USES ebx,ecx,edx + ARG rm_ptr:DWORD + ARG rm_size:DWORD + ARG freq:DWORD + ARG partial:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + mov [ RealModeSel ] , edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated, just in + ; case it needs to be. + + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[rm_size] ; edi will have size of region in bytes. + mov [RealModeSize],edi ; save off the size for the unlock + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr] + mov esi,[rm_ptr] ; Set up our source pointer. + or [InitFlags],IF_ALLOC_RM ; set successful + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi ; get real mode 32 offset. + shl eax,12 ; make seg in high eax. + mov ax,[(TimerType PTR esi).CallRMIntOffset] ; create RM addr of call chain. + mov [(TimerType PTR esi).CallRMIntAddr],eax ; save it for use in PM int. + + cmp [partial],0 + jne ??partial_exit + + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(TimerType PTR esi).OldPMIOffset],edx ; save offset. + mov [(TimerType PTR esi).OldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, CX:DX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(TimerType PTR esi).OldRMI],ecx + + ;========================================================================== + ; only separate method of installation is posible. + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + mov eax, 0205h + mov bl,IRQ0INTNUM + mov cx , cs + lea edx, [Timer_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(TimerType PTR esi).CodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ; now set the frequency. + mov eax,[freq] + or [InitFlags],IF_FUNCTIONAL + push eax + call NEAR Set_Timer_Frequency + mov [(TimerType PTR esi).SysTicks],0 ; Only place SysTicks in inited. + mov [(TimerType PTR esi).UserTicks],0 ; Timers start off on same foot. + pop eax + or [InitFlags],IF_RATE_CHANGE + + ; we have finished with success. +??partial_exit: + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + +;*************************************************************************** +;* REMOVE_TIMER_INTERRUPT -- Removes the timer interrupt vectors. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Remove_Timer_Interrupt C NEAR + USES ebx,ecx,edx + + ; check if timer was previosly install + mov esi,[RealModePtr] + test esi,esi + jz ??error + + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;==================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov edx,[(TimerType esi).OldRMI] ; get the RM address. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ0INTNUM + mov ecx,[(TimerType esi).OldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(TimerType esi).OldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + +??vectors_not_set: + + ; Call function to set the rate of the chip, ebx = rate. + test [InitFlags],IF_RATE_CHANGE ; was it changed? + jz ??rate_not_changed + mov ebx,0FFFFh ; back to 18.2 time per second. + call Set_Timer_Rate ; call function to set timer. +??rate_not_changed: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + + mov eax , 0601h + mov ecx, [RealModePtr] + mov edi, [RealModeSize] + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error + +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov eax,1 ; signal success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Access routines ////////////////////////////////////// + + +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + +;*************************************************************************** +;* GET_SYSTEM_TICK_COUNT -- Returns the system tick count. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_System_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + xor eax,eax + cmp esi,0 + je ??fini + mov eax,[(TimerType PTR esi).SysTicks] +??fini: + ret + + ENDP + + +;*************************************************************************** +;* GET_USER_TICK_COUNT -- Get tick count of user clock. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_User_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + mov eax,[(TimerType PTR esi).UserTicks] + ret + + ENDP + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Interrupt routines /////////////////////////////////// + +; These macros are placed here to handle to duplicate code in 2 methods in +; the Timer_Interrupt function. + + +MACRO SET_DS_ESI_TO_RM + ; Sets DS : ES to point to DGROUP _DATA selector + ; Set esi to point to RealModePtr + ; Corrupts eax + + mov ax , _DATA + mov ds , ax + mov es , ax + mov esi,[RealModePtr] ; Point to start of RM data. +ENDM + + +MACRO INCREMENT_TIMERS + ; Expects ESI to point to real mode memory. + + inc [(TimerType PTR esi).NumPMInts] ; For testing. + + mov eax,[(TimerType PTR esi).TrueRate] ; Get the rate of the PC clock. + sub [(TimerType PTR esi).SysCurRate],ax ; Sub from our rate counter. + ja ??end_sys ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).SysRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).SysError] ; Get amount of error. + add [(TimerType PTR esi).SysCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).SysCurError],cx ; Subtract err from error count. + jb ??error_adj_sys ; If wrapped don't inc. + inc [(TimerType PTR esi).SysTicks] ; increment the timer. + jmp short ??end_sys +??error_adj_sys: + add [(TimerType PTR esi).SysCurError],bx ; reajust the error by timer rate. +??end_sys: + sub [(TimerType PTR esi).UserCurRate],ax ; Sub from our rate counter. + ja ??end_user ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).UserRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).UserError] ; Get amount of error. + add [(TimerType PTR esi).UserCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).UserCurError],cx; Subtract err from error count. + jb ??error_adj_user ; If wrapped don't inc. + inc [(TimerType PTR esi).UserTicks] ; increment the timer. + jmp short ??end_user +??error_adj_user: + add [(TimerType PTR esi).UserCurError],bx; reajust the error by timer rate. +??end_user: + +ENDM + + +MACRO ENABLE_CLOCK_INT + ; Signal 8259 that we are done and that it can bug us again. + ; Corrupts al. + + mov al,CLEARISR ; Signal EOI + sti ; enable interrupts. + out INTCHIP0,al ; 8259 interrupt chip controller 0 +ENDM + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* TIMER_INTERRUPT -- Temp routine to mimic a timer system calling ours * +;* This is a temp routine to call our tick count routine. It will be * +;* replaced once another timer system is put in (such as HMI). * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + PROC Timer_Interrupt Near +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +; This routine will do what it needs to, +; then it will decide if the old vector should be called. +; if so, it calls it and never returns to this function. +; if not, we do our own return. +; the method for doing this is found in: +; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142" +; It says: +; 1 - Execute a PUSHFD to save the current flags. +; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler +; 3 - Push register I use. +; 4 - Do any processing. +; 5 - Put the address of the original handler in the reserved slot. +; 6 - Pop saved register values +; 7 - Execute an IRETD to transfer control to original handler. + + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ; Step 3 continued. Push used varables. + pushad + push fs gs es ds + + + ; Step 4. Now do processing before I chain. + ; Set up ds:esi to point at start of real memory block (data is first) + + call far Timer_Interrupt_Func + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + + ; Now take care of calling the old DOS timer interrupt vector. + ; This must be the last operation of this module since if we call + ; DOS, we will never return. + mov ax,[(TimerType PTR ds:esi).DosAdder] + add [(TimerType PTR esi).DosFraction],ax + jnc ??no_dos_call ; if not, skip the call. + + ; Tell RM that we forced the int and not to update timers. + mov [(TimerType PTR esi).PMIssuedInt],1 ; Make it TRUE + + ; Step 5. + ; Now it is time to set up for the call by returning by poking + ; the old interrupt handle address in. + mov eax,[(TimerType PTR esi).OldPMIOffset] ; Get orig offset. + mov ebx,[(TimerType PTR esi).OldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ; Step 6. + pop ds es gs fs + popad + pop ebp + + ; Step 7. + iretd ; transfer control to original handler. + +??no_dos_call: + + ENABLE_CLOCK_INT + + ; Restore all registers. + pop ds es gs fs + popad + + pop ebp + add esp,8 + popfd + + iretd +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +;////////////////////////////////////////////////////////////////////////////////////////////////////// + ENDP + + + +;*************************************************************************** +;* TIMER_INTERRUPT_FUNC -- Handles core timer code * +;* * +;* This function exists so that we can call it from the core HMI driver * +;* code when our timer interrupt has not been installed. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + PROC Timer_Interrupt_Func C Far + pushfd ; save off the flags + pushad ; save off the main registers + push fs gs es ds ; save off the seg registers + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + INCREMENT_TIMERS ; Increment Westwoods timers + + pop ds es gs fs + popad + popfd + retf + ENDP + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* DISABLE_TIMER_INTERRUPT_ONLY -- Disables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Disable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + or al,001h ; setup to disable irq 0 + out 021h,al ; disable irq 0 + + popf ; possibly enable all interrupts (except 0) + pop eax + retf + ENDP + + +;*************************************************************************** +;* ENABLE_TIMER_INTERRUPT_ONLY -- Enables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Enable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + and al,0FEh ; setup to enable irq 0 + out 021h,al ; enable irq 0 + + popf ; possibly enable all interrupts + pop eax + retf + ENDP + + + END + + + diff --git a/WWFLAT32/TIMER/TIMERA.BAK b/WWFLAT32/TIMER/TIMERA.BAK new file mode 100644 index 0000000..cee3b7f --- /dev/null +++ b/WWFLAT32/TIMER/TIMERA.BAK @@ -0,0 +1,1014 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer stuff * +;* * +;* File Name : TIMERA.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 6, 1994 * +;* * +;* Last Update : March 14, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Get_RM_Timer_Address -- Return address of real mode code for copy. * +;* Get_RM_Timer_Size -- return size of real mode timer code. * +;* Increment_Tick_Count -- Increments WW system timer. * +;* Timer_Interrupt -- Temp routine to mimic a timer system calling our. * +;* Install_Timer_Interrupt -- Installs the timer interrupt routine. * +;* Remove_Timer_Interrupt -- Removes the timer interrupt vectors. ** +;* Set_Timer_Frequency -- Set the frequency of the timer. * +;* Set_Timer_Rate -- Set the rate of the timer. * +;* Get_System_Tick_Count -- Returns the system tick count. * +;* Get_User_Tick_Count -- Get tick count of user clock. * +;* Increment_Timers -- Increments system and user timers. * +;* Get_Num_Interrupts -- Returns the number of interrupts that have occured* +;* Timer_Interrupt_Func -- Handles core timer code * +;* Disable_Timer_Interrupt -- Disables at the hardware level * +;* Enable_Timer_Interrupt -- Enables at the hardware level * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + + +DPMI_INTR equ 31h +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// + +SYSTEM_TIMER_FREQ equ 60 ; Frequency of system timer. + +;******************************************************************** +; There are two ways to call our interrupt chain in protected mode. +; The obvious way it to call the address that we replaced in the +; PM interrupt chain. This method is a little difficult but works and +; should always work. + + +;******************************************************************** +; The RM and PM interrupts can be installed at the same time or seperately. +; Installing at the same time is the best method, the other method +; can be used to faciliated debugging when you only want one or the other +; called. Both methods work. +; -SKB July 21, 1994. +INSTALL_SEPERATE equ FALSE + + +INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0 +CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts. +IRQ0INTNUM EQU 08h ; IRQ0 interrupt vector number. +DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls. +LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT +UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT + +TIMER_CONST EQU 1193180 ; TIMER_CONST / FREQ = rate to set 8259 chip + +TIMERCMDREG EQU 043H ; timer command register port. +TIMER0PORT EQU 040H ; timer channel 0 port +TIMETYPE EQU 036H ; type of timer. + ; 36H = 0011 0110 + ; -- select channel 0 + ; -- read/load low byte then high byte for timer + ; --- timer mode 3 + ; - 0 - binary , 1 - BCD data + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Structs //////////////////////////////////////// + + +; Structure of memory in real mode handler. +; This is at the very start of the real mode code. +; This information may not change unless the real mode version is also changed. + +STRUC TimerType +; For speed, PM uses a DD while RM used DW +; For speed, SysRate and SysError are DD in PM and are DW in real mode. + + TrueRate DD ? ; True rate of clock. (only use word) + SysTicks DD ? ; Tick count of timer. + SysRate DD ? ; Desired rate of timer. + SysError DD ? ; Amount of error in clock rate for desired frequency. + SysCurRate DW ? ; Keeps track of when to increment timer. + SysCurError DW ? ; Keeps track of amount of error in timer. + + UserTicks DD ? ; Tick count of timer. + UserRate DD ? ; Desired rate of timer. + UserError DD ? ; Amount of error in clock rate for desired frequency. + UserCurRate DW ? ; Keeps track of when to increment timer. + UserCurError DW ? ; Keeps track of amount of error in timer. + + DosAdder DW ? ; amount to add to DosFraction each interrupt. + DosFraction DW ? ; Call dos when overflowed. + + OldRMI DD ? ; The origianl RM interrupt seg:off. + OldPMIOffset DD ? ; The origianl PM interrupt offset + OldPMISelector DD ? ; The original PM interrupt segment. + + CodeOffset DW ? ; Offset of the code in the RM stuff. + CallRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + + PMIssuedInt DD 0 ; PM signals RM to just call Int chain. + +; These are just used for information on testing. When all is done, they can +; be removed, but why? The don't add too much proccessing time and can +; be useful. + NumPMInts DD ? ; Number of PM interrupts + NumRMInts DD ? ; Number of RM interrupts. + +ENDS + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes ////////////////////////////////////// + +GLOBAL Get_System_Tick_Count:NEAR +GLOBAL Get_User_Tick_Count:NEAR +GLOBAL Get_RM_Timer_Address:NEAR +GLOBAL Get_RM_Timer_Size:NEAR +GLOBAL Set_Timer_Frequency:NEAR +GLOBAL Timer_Interrupt:NEAR +GLOBAL Install_Timer_Interrupt:NEAR +GLOBAL Remove_Timer_Interrupt:NEAR +GLOBAL Get_Num_Interrupts:NEAR +GLOBAL Timer_Interrupt_Func:FAR +GLOBAL Disable_Timer_Interrupt:NEAR +GLOBAL Enable_Timer_Interrupt:NEAR + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Data //////////////////////////////////////// + DATASEG + +; The current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealBinStart BYTE +include "timereal.ibn" +LABEL RealBinEnd BYTE + + + + +LABEL LockedDataStart BYTE +RealModeSel DD 0 +RealModePtr DD 0 ; Pointer to real mode memory. +RealModeSize DD 0 ; Pointer to real mode memory. +LABEL LockedDataEnd BYTE + + +InitFlags DD 0 ; Flags to indicate what has been initialized. + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1h ; Allocation of RM was successful. +IF_SET_VECTORS equ 2h ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4h ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8h ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10h ; Timer rate was changed. +IF_FUNCTIONAL equ 20h ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40h ; Timer is in and functional. + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Code //////////////////////////////////////// + + CODESEG + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Init routines //////////////////////////////////////// + +;*************************************************************************** +;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* GET_RM_TIMER_SIZE -- return size of real mode timer code. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_Timer_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + +;*************************************************************************** +;* SET_TIMER_RATE -- Set the rate of the timer. * +;* * +;* * +;* INPUT: ebx = rate to set timer were rate = 1193180 / freq * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/11/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Rate Near + + push eax + pushf ; to save int enable flag + cli ; disable interupts while setting up swapper + + mov al,TIMETYPE ; setup to modify timer 0 + out TIMERCMDREG,al ; send command. + mov eax,ebx ; get rate. + out TIMER0PORT,al ; output low byte + mov al,ah + out TIMER0PORT,al ; output high byte + + sti + popf ; get int enable flag. + pop eax + + ret +ENDP Set_Timer_Rate + +;*************************************************************************** +;* SET_TIMER_FREQUENCY -- Set the frequency of the timer. * +;* * +;* * +;* INPUT: INT Frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* +PROC Set_Timer_Frequency C NEAR + USES eax,ebx,ecx,edx + ARG freq:DWORD + LOCAL clockrate:DWORD + LOCAL clockfreq:DWORD + + test [InitFlags],IF_FUNCTIONAL ; Is our timer system installed? + jz ??timer_not_installed ; if not, this is not legal. + + + ; Find out the greater of the frequencies (user or system.) + ; Assign the True rate value based of of that. + ; store the max frequency in ecx. + mov ecx,[freq] ; get user frequency. + cmp ecx,SYSTEM_TIMER_FREQ ; compare it with system frequency + jg ??user_is_fastest ; is user frequency faster? + mov ecx,SYSTEM_TIMER_FREQ ; no, set clock freq to system frequency. +??user_is_fastest: + + ; now get the rate that the clock will be set at. + ; ecx is still max frequency. + mov esi,[RealModePtr] + + mov eax,TIMER_CONST ; get the clock constant value. + xor edx,edx ; zero for divide. + div ecx ; rate = TC/freq => eax = eax/ecx; + mov [(TimerType PTR esi).TrueRate],eax ; Set our true rate. + mov ebx,eax ; save for later. DO NOT USE UNTIL CALL + + ; Set up variables to call DOS correctly. + ; When DosFraction overflows, DOS is called. + mov [(TimerType PTR esi).DosAdder],ax ; Init count to until call dos. + mov [(TimerType PTR esi).DosFraction],0 ; init the fraction. + + ; now set up the system timer. + mov ecx,SYSTEM_TIMER_FREQ ; get frequency. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).SysCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).SysCurError],ax ; Init current stuff. + mov [(TimerType PTR esi).SysRate],eax ; Save rate of timer + mov [(TimerType PTR esi).SysError],edx ; Save error of timer + ; Do not set SysTicks to zero since it always has the same frequency. It + ; should be zero out only when the system clock is installed. + + ; now set up the user timer. + mov ecx,[freq] ; get frequency of user timer. + mov eax,TIMER_CONST ; get constant for formula rate=C/freq. + xor edx,edx ; make sure zero for divide + div ecx ; calculate rate + mov [(TimerType PTR esi).UserCurRate],ax ; Init current stuff. + mov [(TimerType PTR esi).UserCurError],ax; Init current stuff. + mov [(TimerType PTR esi).UserRate],eax ; Save rate of timer + mov [(TimerType PTR esi).UserError],edx ; Save error of timer + mov [(TimerType PTR esi).UserTicks],0 ; User timer sets to zero when freq change. + + ; Call function to set the rate of the chip, ebx = rate from above. + call Set_Timer_Rate + +??timer_not_installed: + + ret + + ENDP + + +;*************************************************************************** +;* INSTALL_TIMER_Interrupt -- Installs the timer interrupt routine. * +;* * +;* * +;* INPUT: VOID * pointer to RM binary in PM memory. * +;* LONG Size of RM binary. * +;* INT frequency of user timer. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Install_Timer_Interrupt C Near + USES ebx,ecx,edx + ARG rm_ptr:DWORD + ARG rm_size:DWORD + ARG freq:DWORD + ARG partial:DWORD + + ; Are they attempting to set timer again? + cmp [RealModePtr],0 + jnz ??error + + ; Make sure all flags are cleared. + cmp [InitFlags],0 + jnz ??error + + ; Before setting the interrupt vectors, the code needs to be locked + ; for DPMI compatability. Any code or data accessed must be lockded + ; so that no page faults accure during an interrupt. + ; First lock the code, then the data. The stack will already be locked. + ; The real mode code is also already locked be default. + ; To lock a page set up registers : + ; AX = 0600h + ; BX:CX = starting linear address of memory block + ; SI:DI = size of region + + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. + or [InitFlags],IF_LOCKED_PM_CODE + + 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 + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + mov ebx,[rm_size] ; get size of RM binary. + add ebx,15 ; round up + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + jc ??error ; check for error. + mov [ RealModeSel ] , edx + shl eax,4 ; convert segment to offset. + mov [RealModePtr],eax ; save offset to global variable. + + ; now lock the real mode memory that we allocated, just in + ; case it needs to be. + + mov eax,0600h ; function number. + mov ecx,[RealModePtr] ; ecx must have start of memory. + mov edi,[rm_size] ; edi will have size of region in bytes. + mov [RealModeSize],edi ; save off the size for the unlock + 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. + or [InitFlags],IF_LOCKED_RM_CODE + + + ; set up source and destination pointers for the copy. + mov eax,[RealModePtr] + mov esi,[rm_ptr] ; Set up our source pointer. + or [InitFlags],IF_ALLOC_RM ; set successful + mov edi,eax ; put it into esi for copy. + mov ecx,[rm_size] + rep movsb ; write RM bin to RM memory. + + + ; restore esi to point to data and initialize some of it. + mov esi,[RealModePtr] + mov eax,esi ; get real mode 32 offset. + shl eax,12 ; make seg in high eax. + mov ax,[(TimerType PTR esi).CallRMIntOffset] ; create RM addr of call chain. + mov [(TimerType PTR esi).CallRMIntAddr],eax ; save it for use in PM int. + + cmp [partial],0 + jne ??partial_exit + + ;========================================================================== + ; Get the protected mode interrupt vector keyboard. + ; input ax = 0204 + ; bl = number of interrupt to get + ; output: cf error + ;========================================================================== + mov eax,0204h ; Get proteced mode + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + mov [(TimerType PTR esi).OldPMIOffset],edx ; save offset. + mov [(TimerType PTR esi).OldPMISelector],ecx ; save selector. + + ;========================================================================== + ; Get the real mode interrupt vector keyboard + ; input ax = 2503, cl = number of interrupt to get + ; output cf error, CX:DX = address (seg:off) of RM int handler routine. + ; cl set above + ;========================================================================== + mov eax,0200h + mov bl,IRQ0INTNUM ; IRQ1 interrupt vector + int DPMI_INTR ; do call. + jc ??error + shl edx,16 + shld ecx,edx,16 + mov [(TimerType PTR esi).OldRMI],ecx + + ;========================================================================== + ; only separate method of installation is posible. + ; Now it is time to set the Protected mode interrupt Keyboard + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + mov eax, 0205h + mov bl,IRQ0INTNUM + mov cx , cs + lea edx, [Timer_Interrupt] ; get address of protected code int hand. + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;========================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov ecx,[RealModePtr] ; get address of real code int hand. + shr ecx,4 ; put segment in hi word. + mov dx,[(TimerType PTR esi).CodeOffset] ; Get address of code + int DPMI_INTR ; do call. + jc ??error + or [InitFlags],IF_SET_VECTORS + + ; now set the frequency. + mov eax,[freq] + or [InitFlags],IF_FUNCTIONAL + push eax + call NEAR Set_Timer_Frequency + mov [(TimerType PTR esi).SysTicks],0 ; Only place SysTicks in inited. + mov [(TimerType PTR esi).UserTicks],0 ; Timers start off on same foot. + pop eax + or [InitFlags],IF_RATE_CHANGE + + ; we have finished with success. +??partial_exit: + mov eax,1 ; signal success. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + +;*************************************************************************** +;* REMOVE_TIMER_INTERRUPT -- Removes the timer interrupt vectors. * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Remove_Timer_Interrupt C NEAR + USES ebx,ecx,edx + + ; check if timer was previosly install + mov esi,[RealModePtr] + test esi,esi + jz ??error + + + test [InitFlags],IF_SET_VECTORS + jz ??vectors_not_set + ;========================================================================== + ; Now it is time to set the real Interrupt Keyboard + ; ax = function number (0201 + ; bl = number of interrupt to set + ; cx:dx = address of RM interrupt handler + ;==================================================================== + + mov eax, 0201h + mov bl,IRQ0INTNUM + mov edx,[(TimerType esi).OldRMI] ; get the RM address. + shld ecx , edx , 16 + int DPMI_INTR ; do call. + jc ??error + + ;========================================================================== + ; Now it is time to set the Protected mode interrupt + ; ax = function number (0205 + ; bl = number of interrupt to set + ; cx:edx = address of PM interrupt handler + ;========================================================================== + + mov eax, 0205h + mov bl,IRQ0INTNUM + mov ecx,[(TimerType esi).OldPMISelector] ; Get PM segment to put int ds later. + mov edx,[(TimerType esi).OldPMIOffset] ; Get PM offset + int DPMI_INTR ; do call. + jc ??error + +??vectors_not_set: + + ; Call function to set the rate of the chip, ebx = rate. + test [InitFlags],IF_RATE_CHANGE ; was it changed? + jz ??rate_not_changed + mov ebx,0FFFFh ; back to 18.2 time per second. + call Set_Timer_Rate ; call function to set timer. +??rate_not_changed: + ; now free up the real mode memory. + test [InitFlags],IF_LOCKED_RM_CODE + jz ??rm_not_locked + + mov eax , 0601h + mov ecx, [RealModePtr] + mov edi, [RealModeSize] + 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. + +??rm_not_locked: + test [InitFlags],IF_ALLOC_RM + jz ??mem_not_allocated + mov eax , 0101h + mov edx,[ RealModeSel ] ; get physical address of real mode buffer. + int DPMI_INTR ; do call. + jc ??error + +??mem_not_allocated: + + ; Now we can unlock all stuff needed for the interrupt. + ; Unlock code + 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 ; eax = 8 if mem err, eax = 9 if invalid mem region. +??code_not_locked: + + ; Unlock data + 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: + + ; we have finished with success. + mov eax,1 ; signal success. + mov [RealModePtr],0 ; To say we can do it again sometime. + mov [InitFlags],0 ; To say we can do it again sometime. + ret +??error: + xor eax,eax ; signal an error. + ret + + ENDP + + + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Access routines ////////////////////////////////////// + + +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + +;*************************************************************************** +;* GET_SYSTEM_TICK_COUNT -- Returns the system tick count. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_System_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + mov eax,[(TimerType PTR esi).SysTicks] + ret + + ENDP + + +;*************************************************************************** +;* GET_USER_TICK_COUNT -- Get tick count of user clock. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_User_Tick_Count C Near + USES esi + + mov esi,[RealModePtr] + mov eax,[(TimerType PTR esi).UserTicks] + ret + + ENDP + + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////// Interrupt routines /////////////////////////////////// + +; These macros are placed here to handle to duplicate code in 2 methods in +; the Timer_Interrupt function. + + +MACRO SET_DS_ESI_TO_RM + ; Sets DS : ES to point to DGROUP _DATA selector + ; Set esi to point to RealModePtr + ; Corrupts eax + + mov ax , _DATA + mov ds , ax + mov es , ax + mov esi,[RealModePtr] ; Point to start of RM data. +ENDM + + +MACRO INCREMENT_TIMERS + ; Expects ESI to point to real mode memory. + + inc [(TimerType PTR esi).NumPMInts] ; For testing. + + mov eax,[(TimerType PTR esi).TrueRate] ; Get the rate of the PC clock. + sub [(TimerType PTR esi).SysCurRate],ax ; Sub from our rate counter. + ja ??end_sys ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).SysRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).SysError] ; Get amount of error. + add [(TimerType PTR esi).SysCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).SysCurError],cx ; Subtract err from error count. + jb ??error_adj_sys ; If wrapped don't inc. + inc [(TimerType PTR esi).SysTicks] ; increment the timer. + jmp short ??end_sys +??error_adj_sys: + add [(TimerType PTR esi).SysCurError],bx ; reajust the error by timer rate. +??end_sys: + sub [(TimerType PTR esi).UserCurRate],ax ; Sub from our rate counter. + ja ??end_user ; If !below zero, do not inc. + mov ebx,[(TimerType PTR esi).UserRate] ; Get rate of timer. + mov ecx,[(TimerType PTR esi).UserError] ; Get amount of error. + add [(TimerType PTR esi).UserCurRate],bx ; Add rate to the current. + sub [(TimerType PTR esi).UserCurError],cx; Subtract err from error count. + jb ??error_adj_user ; If wrapped don't inc. + inc [(TimerType PTR esi).UserTicks] ; increment the timer. + jmp short ??end_user +??error_adj_user: + add [(TimerType PTR esi).UserCurError],bx; reajust the error by timer rate. +??end_user: + +ENDM + + +MACRO ENABLE_CLOCK_INT + ; Signal 8259 that we are done and that it can bug us again. + ; Corrupts al. + + mov al,CLEARISR ; Signal EOI + sti ; enable interrupts. + out INTCHIP0,al ; 8259 interrupt chip controller 0 +ENDM + +LABEL LockedCodeStart BYTE + +;*************************************************************************** +;* TIMER_INTERRUPT -- Temp routine to mimic a timer system calling ours * +;* This is a temp routine to call our tick count routine. It will be * +;* replaced once another timer system is put in (such as HMI). * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + + PROC Timer_Interrupt Near +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +; This routine will do what it needs to, +; then it will decide if the old vector should be called. +; if so, it calls it and never returns to this function. +; if not, we do our own return. +; the method for doing this is found in: +; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142" +; It says: +; 1 - Execute a PUSHFD to save the current flags. +; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler +; 3 - Push register I use. +; 4 - Do any processing. +; 5 - Put the address of the original handler in the reserved slot. +; 6 - Pop saved register values +; 7 - Execute an IRETD to transfer control to original handler. + + pushfd ; Step 1 + sub esp,8 ; Step 2 + push ebp ; Step 3 + mov ebp,esp ; Set up a stack frame to know where to poke address. + + ; Step 3 continued. Push used varables. + pushad + push fs gs es ds + + + ; Step 4. Now do processing before I chain. + ; Set up ds:esi to point at start of real memory block (data is first) + + call far Timer_Interrupt_Func + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + + ; Now take care of calling the old DOS timer interrupt vector. + ; This must be the last operation of this module since if we call + ; DOS, we will never return. + mov ax,[(TimerType PTR ds:esi).DosAdder] + add [(TimerType PTR esi).DosFraction],ax + jnc ??no_dos_call ; if not, skip the call. + + ; Tell RM that we forced the int and not to update timers. + mov [(TimerType PTR esi).PMIssuedInt],1 ; Make it TRUE + + ; Step 5. + ; Now it is time to set up for the call by returning by poking + ; the old interrupt handle address in. + mov eax,[(TimerType PTR esi).OldPMIOffset] ; Get orig offset. + mov ebx,[(TimerType PTR esi).OldPMISelector] ; Get orig selector. + mov [ss:ebp+4],eax ; Poke offset. + mov [ss:ebp+8],ebx ; Poke selector. + + ; Step 6. + pop ds es gs fs + popad + pop ebp + + ; Step 7. + iretd ; transfer control to original handler. + +??no_dos_call: + + ENABLE_CLOCK_INT + + ; Restore all registers. + pop ds es gs fs + popad + + pop ebp + add esp,8 + popfd + + iretd +;////////////////////////////// Call prot mode interrupt vector //////////////////////////////////////// +;////////////////////////////////////////////////////////////////////////////////////////////////////// + ENDP + + + +;*************************************************************************** +;* TIMER_INTERRUPT_FUNC -- Handles core timer code * +;* * +;* This function exists so that we can call it from the core HMI driver * +;* code when our timer interrupt has not been installed. * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/14/1995 PWG : Created. * +;*=========================================================================* + + PROC Timer_Interrupt_Func C Far + pushfd ; save off the flags + pushad ; save off the main registers + push fs gs es ds ; save off the seg registers + + SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff. + INCREMENT_TIMERS ; Increment Westwoods timers + + pop ds es gs fs + popad + popfd + retf + ENDP + +LABEL LockedCodeEnd BYTE + +;*************************************************************************** +;* DISABLE_TIMER_INTERRUPT_ONLY -- Disables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Disable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + or al,001h ; setup to disable irq 0 + out 021h,al ; disable irq 0 + + popf ; possibly enable all interrupts (except 0) + pop eax + retf + ENDP + + +;*************************************************************************** +;* ENABLE_TIMER_INTERRUPT_ONLY -- Enables at the hardware level * +;* * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/07/1995 DRD : Created. * +;*=========================================================================* + + PROC Enable_Timer_Interrupt C Near + push eax + pushf + + sti ; disable all interrupts if not disabled + in al,021h ; read interrupt Mask register bits 0-7 + ; apply to irq's 0-7 + ; value 0 of enabled + ; value 1 of disabled + and al,0FEh ; setup to enable irq 0 + out 021h,al ; enable irq 0 + + popf ; possibly enable all interrupts + pop eax + retf + ENDP + + + END + + + diff --git a/WWFLAT32/TIMER/TIMERDWN.CPP b/WWFLAT32/TIMER/TIMERDWN.CPP new file mode 100644 index 0000000..10a8538 --- /dev/null +++ b/WWFLAT32/TIMER/TIMERDWN.CPP @@ -0,0 +1,127 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/WWFLAT32/TIMER/TIMEREAL.ASM b/WWFLAT32/TIMER/TIMEREAL.ASM new file mode 100644 index 0000000..5c4524f --- /dev/null +++ b/WWFLAT32/TIMER/TIMEREAL.ASM @@ -0,0 +1,241 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer interrupt for RM * +;* * +;* File Name : TIMEREAL.ASM * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : July 8, 1994 * +;* * +;* Last Update : July 8, 1994 [SKB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +MODEL TINY +P386N + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Equates //////////////////////////////////////// + +INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0 +CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts. + +CODESEG +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Data //////////////////////////////////////// +; This information may not change unless the protected mode version is +; also changed. +; For speed, PM uses a DD while RM used DW +TrueRate DW 0 ; True rate of clock. (only use word) +TrueRateNA DW 0 ; used for speed in prot mode. + +; For speed, SysRate and SysError are DD in PM and are DW in real mode. +SysTicks DD 0 ; Tick count of timer. +SysRate DD 0 ; Desired rate of timer. +SysError DD 0 ; Amount of error in clock rate for desired frequency. +SysCurRate DW 0 ; Keeps track of when to increment timer. +SysCurError DW 0 ; Keeps track of amount of error in timer. + +UserTicks DD 0 ; Tick count of timer. +UserRate DD 0 ; Desired rate of timer. +UserError DD 0 ; Amount of error in clock rate for desired frequency. +UserCurRate DW 0 ; Keeps track of when to increment timer. +UserCurError DW 0 ; Keeps track of amount of error in timer. + +DosAdder DW 0 ; amount to add to DosFraction each interrupt. +DosFraction DW 0 ; Call dos when overflowed. + +OldRMI DD 0 ; The origianl RM interrupt seg:off. +OldPMIOffset DD 0 ; The origianl PM interrupt offset +OldPMISelector DD 0 ; The original PM interrupt segment. + +CodeOffset DW RM_Timer_Interrupt_Handler ; Offset of the code in the RM stuff. +CallRMIntOffset DW Call_Interrupt_Chain ; Offset of function to call DOS timer interrupt. +CallRMIntAddr DD 0 ; PM address of CallRealIntOffset for speed. + +PMIssuedInt DD 0 ; PM signals RM to just call Int chain. + +; These are just used for information on testing. When all is done, they can +; be removed, but why? The don't add too much proccessing time and can +; be useful. +NumPMInts DD 0 ; Number of PM interrupts +NumRMInts DD 0 ; Number of RM interrupts. + +;////////////////////////////////////////////////////////////////////////////////////// +;///////////////////////////////////// Code //////////////////////////////////////// + + +; This is here for easy comparison with the PM version. +MACRO INCREMENT_TIMERS + inc [NumRMInts] ; For testing. + + + ; At this point, increment the system and user timers. + mov ax,[TrueRate] ; Get the rate of the PC clock. + + sub [SysCurRate],ax ; Sub from our rate counter. + ja ??end_sys ; If !below zero, do not inc. + + mov bx,[WORD PTR SysRate] ; Get rate of timer. + mov dx,[WORD PTR SysError] ; Get amount of error. + add [SysCurRate],bx ; Add rate to the current. + + sub [SysCurError],dx ; Subtract err from error count. + jb ??error_adj_sys ; If !below 0, increment counter. + + inc [SysTicks] ; increment the timer. + jmp short ??end_sys ; don't modify SysCurError. +??error_adj_sys: + add [SysCurError],bx ; reajust the error by timer rate. +??end_sys: + + sub [UserCurRate],ax ; Sub from our rate counter. + ja ??end_user ; If !below zero, do not inc. + + mov bx,[WORD PTR UserRate] ; Get rate of timer. + mov dx,[WORD PTR UserError] ; Get amount of error. + add [UserCurRate],bx ; Add rate to the current. + + sub [UserCurError],dx ; Subtract err from error count. + jb ??error_adj_user ; If !below 0, increment counter. + + inc [UserTicks] ; increment the timer. + jmp short ??end_user ; don't modify UserCurError. +??error_adj_user: + add [UserCurError],bx ; reajust the error by timer rate. +??end_user: + +ENDM + + + + +;************************************************************************** +;* RM_INTERRUPT_HANDLER -- Called when processor interrupted in real mode. * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +label RM_Timer_Interrupt_Handler +start_RM_Timer_Interrupt_Handler: + + push ax + push bx + push dx + push ds + + mov ax,cs ; Set data segment to code segment + mov ds,ax ; since data is in code seg. + + cmp [WORD PTR PMIssuedInt],0; Check to see if PM made Int call. + mov [WORD PTR PMIssuedInt],0; Make it false. + jne ??call_int_chain ; if so, just call Int Chain. + + + INCREMENT_TIMERS + + + ; Now check to see if we should call the old timer handler. + mov ax,[DosAdder] ; Get amount to add each tick. + add [DosFraction],ax ; add it to the fraction. + jnc ??no_int_chain ; Skip call if no overflow. + +??call_int_chain: + pushf ; Push flags for interrupt call. + call [OldRMI] ; chain the call. + +??no_int_chain: + sti + mov al,CLEARISR ; value to clear In Service Register + mov dx,INTCHIP0 + out dx,al ; 8259 interrupt chip controller 0 +exit1: + + pop ds + pop dx + pop bx + pop ax + iret + + + +;************************************************************************** +;* CALL_INTERRUPT_CHAIN -- Function PM calls to call the RM interrupt chain* +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/08/1994 SKB : Created. * +;*=========================================================================* +Call_Interrupt_Chain: + + pushf + call [OldRMI] ; + retf + +STACK ; Don't really need this + +END + + + + +IF 0 +; mono print stuff. + pushf + push ax + push di + push es + + mov ax,0B000h ; ES:DI = Mono RAM address. + mov es,ax + + mov al,'A' + mov ah,2 + and di,63 + + stosw + + pop es + pop di + pop ax + popf +ENDIF diff --git a/WWFLAT32/TIMER/TIMEREAL.IBN b/WWFLAT32/TIMER/TIMEREAL.IBN new file mode 100644 index 0000000..0ee1a95 --- /dev/null +++ b/WWFLAT32/TIMER/TIMEREAL.IBN @@ -0,0 +1,14 @@ + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,048h,000h,0C5h,000h,000h,000h,000h,000h + DB 000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,000h,050h,053h,052h + DB 01Eh,08Ch,0C8h,08Eh,0D8h,083h,03Eh,03Ch,000h,000h,0C7h,006h,03Ch,000h,000h + DB 000h,075h,057h,066h,0FFh,006h,044h,000h,0A1h,000h,000h,029h,006h,010h,000h + DB 077h,01Dh,08Bh,01Eh,008h,000h,08Bh,016h,00Ch,000h,001h,01Eh,010h,000h,029h + DB 016h,012h,000h,072h,007h,066h,0FFh,006h,004h,000h,0EBh,004h,001h,01Eh,012h + DB 000h,029h,006h,020h,000h,077h,01Dh,08Bh,01Eh,018h,000h,08Bh,016h,01Ch,000h + DB 001h,01Eh,020h,000h,029h,016h,022h,000h,072h,007h,066h,0FFh,006h,014h,000h + DB 0EBh,004h,001h,01Eh,022h,000h,0A1h,024h,000h,001h,006h,026h,000h,073h,005h + DB 09Ch,0FFh,01Eh,028h,000h,0FBh,0B0h,020h,0BAh,020h,000h,0EEh,01Fh,05Ah,05Bh + DB 058h,0CFh,09Ch,0FFh,01Eh,028h,000h,0CBh diff --git a/WWFLAT32/TIMER/TIMERINI.CPP b/WWFLAT32/TIMER/TIMERINI.CPP new file mode 100644 index 0000000..3f7fb58 --- /dev/null +++ b/WWFLAT32/TIMER/TIMERINI.CPP @@ -0,0 +1,161 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern VOID *Get_RM_Timer_Address(VOID); +extern ULONG Get_RM_Timer_Size(VOID); +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * INIT_TIMER_SYSTEM -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: BOOL success? * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/06/1994 SKB : Created. * + *=========================================================================*/ +BOOL Init_Timer_System(UINT freq, BOOL partial) +{ + VOID *binary; + UINT binsize; + BOOL success; + +#if COPY_FROM_MEM + + // The binary is stuffed in an ASM module. + // Get it's address and size. + binary = Get_RM_Timer_Address(); + binsize = Get_RM_Timer_Size(); + +#else + WORD fd; + VOID *mem; + + // + // Open binary image of real mode timer code. + // get its size and allocate a temp block for it. + // Copy the file into the block and close the file + // + fd = Open("timer.ibn", READ); + binsize = File_Size(fd); + binary = Alloc(binsize, MEM_NORMAL); + Read_File(fd, binary, binsize); + Close_File(fd); + +#endif // COPY_FROM_MEM + + // If no size, size too big or no address, then it's a bug. + if (!binsize || (binsize > 0xFFFFL) || !binary) { + return (FALSE); + } + + TimerSystemOn = success = Install_Timer_Interrupt(binary, binsize, freq, partial); + +#if !COPY_FROM_MEM + // Free up the temp pointer. + Free(mem); +#endif // !COPY_FROM_MEM + + if (success) { + if (!partial) + TickCount.Start(); + return (TRUE); + } else { + Remove_Timer_Interrupt(); + return (FALSE); + } +} + + + +/*************************************************************************** + * REMOVE_TIMER_SYSTEM -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/06/1994 SKB : Created. * + *=========================================================================*/ +BOOL Remove_Timer_System(VOID) +{ + TimerSystemOn = FALSE; + return(Remove_Timer_Interrupt()); +} + diff --git a/WWFLAT32/VIDEO/INITDLAY.CPP b/WWFLAT32/VIDEO/INITDLAY.CPP new file mode 100644 index 0000000..888c18c --- /dev/null +++ b/WWFLAT32/VIDEO/INITDLAY.CPP @@ -0,0 +1,72 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : INITDLAY.C * + * * + * Programmer : Barry Green * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Delay -- I am not sure * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include "timer.h" +#include "video.h" + +BOOL VertBlank; + +/*************************************************************************** + * INIT_DELAY -- I am not sure * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/03/1994 SKB : Created. * + *=========================================================================*/ +VOID Init_Delay(VOID) +{ + WORD nz, nnz; + + nz = nnz = 0; + + CountDown.Set(15, TRUE); // set to 1/4 second and start it + + do { + if (Get_Vert_Blank()) + nnz++; + else + nz++; + } while (CountDown.Time()); + + VertBlank = (nnz > nz); +} diff --git a/WWFLAT32/VIDEO/MAKEFILE b/WWFLAT32/VIDEO/MAKEFILE new file mode 100644 index 0000000..9847f10 --- /dev/null +++ b/WWFLAT32/VIDEO/MAKEFILE @@ -0,0 +1,204 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 27, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = video +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + video.obj & + vesainfo.obj & + initdlay.obj & + vertblnk.obj & + vesahook.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWLIB)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# The keyboard object files is also dependant on an IBN file which is +# generated from KEYIREAL.ASM +#--------------------------------------------------------------------------- +vesareal.ibn: vesareal.obj + %create $^*.rsp + %append $^*.rsp $^&.obj + %append $^*.rsp $^&.exe + %append $^*.rsp $^&.map + tlink @$^*.rsp + tdstrip vesareal.exe + ebn vesareal.exe + +vesareal.obj: vesareal.asm + tasm /zn /la /ml /m2 vesareal.asm + +vesahook.obj: vesareal.ibn vesahook.asm + + + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/VIDEO/VERTBLNK.ASM b/WWFLAT32/VIDEO/VERTBLNK.ASM new file mode 100644 index 0000000..4a6f991 --- /dev/null +++ b/WWFLAT32/VIDEO/VERTBLNK.ASM @@ -0,0 +1,139 @@ +; +; 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 . +; + +; $Header: g:/library/wwlib32/video/rcs/vertblnk.asm 1.1 1994/04/18 09:34:51 jeff_wilson Exp $ +;*************************************************************************** +;** 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 : VERTBLNK.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; WORD Get_Vert_Blank(VOID); * +; VOID Wait_Vert_Blank(VOID); * +; WORD get_vga_state (VOID) ; +; VOID set_vga_mode (WORD) ; +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +LOCALS ?? + +GLOBAL Get_Vert_Blank : NEAR +GLOBAL Wait_Vert_Blank : NEAR +GLOBAL get_vga_state : NEAR +GLOBAL set_vga_mode : NEAR + + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; WORD Get_Vert_Blank(VOID); +; VOID Wait_Vert_Blank(VOID); +; WORD get_vga_state (VOID) ; +; VOID set_vga_mode (WORD) ; + +; +; ---------------------------------------------------------------- + +;---------------------------------------------------------------------------- + +PROC Get_Vert_Blank C near + USES edx + + mov dx,03DAH ; CRTC status register + in al,dx + and al,008H ; look at bit 3 vertical sync + xor ah,ah ; zero ah + ret + +ENDP Get_Vert_Blank + +;---------------------------------------------------------------------------- + +;---------------------------------------------------------------------------- +PROC Wait_Vert_Blank C near + USES eax,ebx,edx + ARG blank:DWORD + + mov ebx,[blank] ; get vertical blank 0 or 1 for on + + mov edx,03DAH ; CRTC status register + + and bl,01b + shl bl,3 + +??in_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + je ??in_vbi ; in vertical sync + +??out_vbi: + in al,dx ; read CRTC status + and al,008h ; only vertical sync bit + xor al,bl + jne ??out_vbi ; not in vertical sync + + ret + +ENDP Wait_Vert_Blank + +;---------------------------------------------------------------------------- + +; WORD get_vga_state (VOID) ; + +PROC get_vga_state C near + USES ebx + mov eax,0f00h + int 10h + and eax, 0ffh + ret +ENDP get_vga_state + +;---------------------------------------------------------------------------- + +; VOID set_vga_mode (WORD) ; + +PROC set_vga_mode C near + ARG mode:dword + mov eax , [mode] + and eax , 0ffh + int 10h + ret +ENDP set_vga_mode + + + +END + + diff --git a/WWFLAT32/VIDEO/VESAHOOK.ASM b/WWFLAT32/VIDEO/VESAHOOK.ASM new file mode 100644 index 0000000..189980e --- /dev/null +++ b/WWFLAT32/VIDEO/VESAHOOK.ASM @@ -0,0 +1,207 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Vesa Hookup * +;* * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : Jan 31, 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* + + + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "svgaprim.inc" + + +DPMI_INTR equ 031h + + +LOCALS ?? + +;////////////////////////////////////////////////////////////////////////////////////// +;/////////////////////////////////// Prototypes ////////////////////////////////////// + +GLOBAL RMVesaVector : DWORD +GLOBAL RMVesaRegs : DWORD +GLOBAL RMVesaVectorSel : DWORD +GLOBAL Vesa_Hook : NEAR +GLOBAL Remove_Vesa : NEAR +GLOBAL VesaFunc : dword + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Data //////////////////////////////////////// + DATASEG + +; The current time we will just include the real mode stuff +; into the protected mode code and then copy it down. The C side of +; this will handle this method or reading it off of disk in the real +; method. + +LABEL RealDataStart BYTE +include "VesaReal.ibn" +LABEL RealDataEnd BYTE + +RMVesaVectorSel DD 0 + +;////////////////////////////////////////////////////////////////////////////////////// +;//////////////////////////////////////// Code //////////////////////////////////////// + + CODESEG + +PROC Vesa_Hook C Near + USES ebx,ecx,edx,ecx,edi,esi + ARG vesa_ptr:DWORD + + ; Are they attempting to set timer again? + cmp [RMVesaVectorSel],0 + jnz ??exit + + ; now allocate real mode memory and copy the rm binary down to it. + mov eax,0100h ; set function number + lea ebx , [RealDataEnd] + sub ebx , offset RealDataStart + push ebx + + add ebx , 15 + 040h + shr ebx,4 ; convert to pages. + int DPMI_INTR ; do call. + pop ecx + jc ??error ; check for error. + + ; set up source and destination pointers for the copy. + shl edx , 16 + mov [ RMVesaVectorSel ] , edx + shl eax,4 ; convert segment to offset. + + mov edi , ecx + xor esi , esi + +; push ecx +; push eax +; mov ecx , eax +; shld ebx,ecx,16 +; mov eax,0600h ; function number. +; int DPMI_INTR ; do call. +; pop eax +; pop ecx +; jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region. + + lea esi , [ RealDataStart] + lea edi , [ eax + 040h ] ; put it into esi for copy. + rep movsb ; write RM bin to RM memory. + + mov edx,[vesa_ptr] ; Setup vesa funtion + mov [ eax + 40h - 4 ] , edx + + + ; Chain the Real Vesa funcion interrupt to any avilable + ; Interrupt vector so We make sure that the Real Mode + ; Keyboard Interrupt service get called at debuging time + ; of the library. + + mov [ RMVesaRegs ] , eax + shl eax , 12 + lea edi , [ eax + 040h ] ; ofsset of VesaFunc + mov bl , 060h + mov bh , 6 + mov eax , 200h + ??find: + int DPMI_INTR + jc ??error + or cx,dx + jz ??found + inc bl + dec bh + jnz ??find + jmp ??error + ??found: + mov eax , 0201h + mov edx , edi + shld ecx , edx , 16 + and ebx , 0ffh + mov [ RMVesaVector ] , ebx + int DPMI_INTR + jc ??error + + ??exit: + xor eax,eax ; signal an error. + ret + ??error: + mov eax , -1 + ret + ENDP + + + +PROC Remove_Vesa C Near + USES ebx,ecx,edx,ecx,edi,esi + ARG vesa_ptr:DWORD + + + ; Are they attempting to set timer again? + mov [VesaFunc],0 + cmp [RMVesaVectorSel],0 + jz ??exit + + mov ebx , [ RMVesaVector ] + test ebx , ebx + jz ??exit + mov eax , 0201h + xor edx , edx + xor ecx , ecx + int DPMI_INTR + mov [ RMVesaVector ] , 0 + jc ??exit + +; mov eax , 0601h +; mov ecx , [ RMVesaRegs ] +; shld ebx , ecx , 16 +; lea edi , [RealDataEnd] +; sub edi , offset RealDataStart +; add edi , 15 + 040h +; xor esi , esi +; int DPMI_INTR +; jc ??exit + + mov eax,0101h ; set function number + mov edx ,[RMVesaVectorSel] + test edx , edx + jz ??exit + shr edx , 16 + int DPMI_INTR ; do call. + mov [RMVesaVectorSel],0 +??exit: + ret + ENDP + + +END + + + diff --git a/WWFLAT32/VIDEO/VESAINFO.CPP b/WWFLAT32/VIDEO/VESAINFO.CPP new file mode 100644 index 0000000..6ba438e --- /dev/null +++ b/WWFLAT32/VIDEO/VESAINFO.CPP @@ -0,0 +1,213 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VIDEO.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : January 12, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Video_Mode -- Converts a dos video mode to a WWLIB video mode * + * Get_Video_Mode -- Returns the current video mode. * + * Set_Video_Mode -- Sets the requested video mode * + * Set_Lores_Function_Pointers -- Sets up the lowres function pointers * + * Set_HiRes_Function_Pointers -- Sets the HiRes function pointers * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iostream.h" +#include "video.h" +#include "descmgmt.h" +#include "mcgaprim.h" +#include "gbuffer.h" +#include "vbuffer.h" +#include "wwmem.h" + +#include "playcd.h" + + +/*************************************************************************** + * VESA_INFO -- Debug routine which displays vesa info to stdout * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * * + * HISTORY: * + * 11/22/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Info(int vesa_mode) +{ + UINT paras; + USHORT longest ; + union REGS regs; + struct SREGS sregs; + SEGSEL VInfoSel ; + VesaInfoType * VInfo ; + SEGSEL ModeInfoSel ; + VesaModeInfoType * ModeInfo ; + unsigned temp ; + short * ptr ; + char buff [ 256 ] ; + short mode_table [][4] = { { 0x100 , 640 , 400 , 256 }, + { 0x101 , 640 , 480 , 256 }, + { 0x103 , 800 , 600 , 256 }, + } ; + + cout << "\n\nWWESTWOOD STUDIOS. Vesa Driver attributes.\n" ; + + // verifie that this is a standard VESA MODE + if ( (vesa_mode < VESA_640X400_256) || ( vesa_mode > VESA_TEXT_132X60 )) { + cout << "this is not a standard VESA mode\n" ; + return ; + } + + // Compute size of VesaInfo structure in paragraphs + paras = ( sizeof(VesaInfoType) + 15 ) >> 4 ; + + // Alloc real-mode memory for VESA structure. + if ( DPMI_real_alloc ( paras , & VInfoSel , & longest ) ) return ; + VInfo = ( VesaInfoType * ) ( VInfoSel . seg << 4 ) ; + + // Compute size of VesaModeInfo structure in paragraphs + paras = ( sizeof(VesaModeInfoType) + 15 ) >> 4 ; + + //Alloc real-mode memory for VesaModeInfoType structure. + if ( DPMI_real_alloc ( paras , & ModeInfoSel , & longest ) ) + { + DPMI_real_free ( VInfoSel ) ; + return ; + } + ModeInfo = ( VesaModeInfoType * ) ( ModeInfoSel . seg << 4 ) ; + + // Get Read Vesa Driver Vesa + regs . x . eax = 0x4f00 ; + regs . x . edi = 0 ; + sregs . es = VInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) { + cout << "\nNot Vesa Driver Present\n" ; + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; + return ; + } + + temp = ( unsigned ) VInfo->AvailModes ; + ptr = ( short * ) ( ( ( temp & 0xffff0000 ) >> 12 ) + ( temp & 0xffff ) ) ; + cout << "Available Video Modes\n" ; + for ( ; * ptr != -1 ; ptr ++ ) + for ( temp = 0 ; temp < 3 ; temp ++ ) + if ( * ptr == mode_table [ temp ] [ 0 ] ) { + sprintf ( buff , "%d\t%d x %d x %d\n" , + mode_table [ temp ] [ 0 ], + mode_table [ temp ] [ 1 ], + mode_table [ temp ] [ 2 ], + mode_table [ temp ] [ 3 ] ) ; + cout << buff ; + } + + // Get Info for this particular graphic mode + regs . x . eax = 0x4F01; + regs . x . ecx = vesa_mode; + regs . x . edi = 0 ; + sregs . es = ModeInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) { + cout << "\nGraphic mode " << vesa_mode << " is not supported by this video card\n" ; + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; + return ; + } + + cout << "\nMode attributes\n" ; + temp = ( unsigned ) ModeInfo->Attributes ; + if ( temp & 0x01 ) cout << "\tMode supported in hardware\n" ; + else cout << "\tMode is not supported in hardware\n" ; + if ( temp & 0x20 ) cout << "\tMode is not VGA Windowed memory compatible\n" ; + else cout << "\tMode is VGA Windowed memory compatible\n" ; + + cout << "Window A attributes\n" ; + temp = ( unsigned ) ModeInfo->WinA_Attributes; ; + if ( temp & 0x02 ) cout << "\tWindow A is Readable\n" ; + else cout << "\tWindow A is not Readable\n" ; + if ( temp & 0x04 ) cout << "\tWindow A is Writeable\n" ; + else cout << "\tWindow A is not Writeable\n" ; + sprintf ( buff , "%P\n" , ModeInfo->WinA_Segment ) ; + cout << "\tWindow A segment address 0x" << buff + 4 ; + + + + cout << "Window B attributes\n" ; + temp = ( unsigned ) ModeInfo->WinB_Attributes; ; + if ( temp & 0x02 ) cout << "\tWindow B is Readable\n" ; + else cout << "\tWindow B is not Readable\n" ; + if ( temp & 0x04 ) cout << "\tWindow B is Writeable\n" ; + else cout << "\tWindow B is not Writeable\n" ; + sprintf ( buff , "%P\n" , ModeInfo->WinB_Segment ) ; + cout << "\tWindow B segment address 0x" << buff + 4 ; + + cout << "Window shared attributes\n" ; + cout << "\tWindow Granularity (KB) :" << ModeInfo->WinGranularity << "\n" ; + cout << "\tWindow Size (KB) : " << ModeInfo->WinSize << "\n"; + cout << "\tNumber of Banks : " << (long)ModeInfo->NumBanks << "\n"; + cout << "\tBytes per ScanLine : " << (long)ModeInfo->BytesPerScanline << "\n"; + cout << "\tXResolution : " << (long)ModeInfo->XRes << "\n"; + cout << "\tYResolution : " << (long)ModeInfo->YRes << "\n"; + cout << "\tX Char Size : " << (long)ModeInfo->XCharSize << "\n"; + cout << "\tY Char Size : " << (long)ModeInfo->YCharSize << "\n"; + cout << "\tMemory Model : " << (long)ModeInfo->MemoryModel << "\n"; + cout << "\tNumber of planes : " << (long)ModeInfo->NumPlanes << "\n" ; + cout << "\tBits per pixels : " << (long)ModeInfo->BitsPerPixel << "\n" ; + + +/* + cout << "Bttributes: " << (long)ModeInfo.Bttributes << "\n" + << "Win B Bttributes: " << (long)ModeInfo.WinB_Bttributes << "\n" + << "Win B Bttributes: " << (long)ModeInfo.WinB_Bttributes << "\n" + << "Win Granularity " << (long)ModeInfo.WinGranularity << "\n" + << "Win Size: " << (long)ModeInfo.WinSize << "\n" + << "Win B Segment: " << hex << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Win B Segment: " << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Bytes per scan line: " << dec << (unsigned short)ModeInfo.BytesPerScanline << "\n" + << "X resolution: " << (long)ModeInfo.XRes << "\n" + << "Y resolution: " << (long)ModeInfo.YRes << "\n" + << "X Char Size: " << (long)ModeInfo.XCharSize << "\n" + << "Y Char Size: " << (long)ModeInfo.YCharSize << "\n" + << "Number of planes: " << (long)ModeInfo.NumPlanes << "\n" + << "Bits per pixels: " << (long)ModeInfo.BitsPerPixel << "\n" + << "Number of Banks: " << (long)ModeInfo.NumBanks << "\n" + << "Memory Model: " << (long)ModeInfo.MemoryModel << "\n" + << "Bank Size: " << (long)ModeInfo.BankSize << "\n"; +*/ + DPMI_real_free ( ModeInfoSel ) ; + DPMI_real_free ( VInfoSel ) ; +} diff --git a/WWFLAT32/VIDEO/VESAREAL.ASM b/WWFLAT32/VIDEO/VESAREAL.ASM new file mode 100644 index 0000000..f9f2eef --- /dev/null +++ b/WWFLAT32/VIDEO/VESAREAL.ASM @@ -0,0 +1,55 @@ +; +; 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 . +; + +;*************************************************************************** +;** 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 : Timer interrupt for RM * +;* * +;* File Name : TIMEREAL.ASM * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : jan 31 , 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +MODEL TINY +P386N + + + +LOCALS ?? + +CODESEG +;///////////////////////////////////// Code //////////////////////////////////////// + +label Vesa_Funcion_Call +start: + call [dword ptr cs: 40h - 4] + iret + + + +END + diff --git a/WWFLAT32/VIDEO/VESAREAL.IBN b/WWFLAT32/VIDEO/VESAREAL.IBN new file mode 100644 index 0000000..083091e --- /dev/null +++ b/WWFLAT32/VIDEO/VESAREAL.IBN @@ -0,0 +1 @@ + DB 02Eh,0FFh,01Eh,03Ch,000h,0CFh diff --git a/WWFLAT32/VIDEO/VIDEO.CPP b/WWFLAT32/VIDEO/VIDEO.CPP new file mode 100644 index 0000000..060c30c --- /dev/null +++ b/WWFLAT32/VIDEO/VIDEO.CPP @@ -0,0 +1,651 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 * + * * + * File Name : VIDEO.C * + * * + * Programmer : David Dettmer * + * * + * Last Update : June 29, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Find_Video_Mode -- Converts a dos video mode to a WWLIB video mode * + * Get_Video_Mode -- Returns the current video mode. * + * Set_Video_Mode -- Sets the requested video mode * + * Set_Lores_Function_Pointers -- Sets up the lowres function pointers * + * Set_HiRes_Function_Pointers -- Sets the HiRes function pointers * + * Set_Original_Video_Mode -- sets mode to restore system to on exit * + * Get_Original_Video_Mode -- Gets the original video mode value * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "iostream.h" +#include "video.h" +#include "descmgmt.h" +#include "mcgaprim.h" +#include "gbuffer.h" +#include "vbuffer.h" +#include "wwmem.h" + +#include "playcd.h" + +extern "C" int MInstalled ; +extern "C" void Hide_Mouse(void); +extern "C" void Show_Mouse(void); + +extern "C" void Reset_Mouse (void) ; +extern "C" int Vesa_Hook ( REALPTR function ) ; +extern "C" void Remove_Vesa (void) ; +extern "C" SEGSEL RMVesaVectorSel ; +extern "C" REALPTR VesaFunc; + +/*=========================================================================*/ +/* Define the global variables that we require. */ +/*=========================================================================*/ +long BankTable[MAX_BANK_ENTRIES]; +int GraphicMode = UNINITIALIZED_MODE; +long XRes = 0; +long YRes = 0; +REALPTR VesaFunc; +unsigned long RMVesaVector ; +unsigned long RMVesaRegs ; + +/*=========================================================================* + * Private Varirables * + * * + * VInfo - Protected mode copy of VInfo structure. * + * ModeInfo - Protected mode copy of ModeInfo structure. * + * rpModeInfo - Real ptr to ModeInfo structure in conventional memory. * + * rpVInfo - Real ptr to VInfo structure in conventional memory. * + * _regs - Registers used for calling software interrupts. * + *=========================================================================*/ +PRIVATE void * rpModeInfo = NULL; +PRIVATE SEGSEL rpModeInfoSel ; +PRIVATE void * rpVInfo = NULL; +PRIVATE SEGSEL rpVInfoSel ; + +PRIVATE long _OriginalVideoMode = UNINITIALIZED_MODE; +PRIVATE VesaInfoType VInfo; +PRIVATE VesaModeInfoType ModeInfo; + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE long Install_Vesa(void); +PRIVATE long Vesa_Get_Mode_Info(long mode); +PRIVATE long Vesa_Set_Mode(long mode); +PRIVATE void Init_Bank_Table(void); +PRIVATE VOID Set_LoRes_Function_Pointers(VOID); +PRIVATE VOID Set_HiRes_Function_Pointers(VOID); + +extern "C" long Vesa_XRes(void); +extern "C" long Vesa_YRes(void); + +extern "C" char CurrentPalette [ 256 * 3 ] ; +extern "C" char PaletteTable [ 1024 ] ; + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * GET_VIDEO_MODE -- Returns the current video mode. * + * * + * INPUT: none * + * * + * OUTPUT: returns the graphic mode as a WWLIB library define * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1994 : Created. * + *=========================================================================*/ +int Get_Video_Mode(void) +{ + union REGS regs; + + if( GraphicMode == UNINITIALIZED_MODE ) + { + regs . x . eax = 0xf00 ; // get graphic mode + int386 ( 0x10 , & regs , & regs ) ; + return ( regs . w . ax & 0xff ) ; + } + return(GraphicMode); // return the graphic mode +} + +/*************************************************************************** + * Install_Vesa -- Initializes the vesa driver if it exists * + * * + * INPUT: none * + * * + * OUTPUT: return -1 if error/VESA not supported. * + * * + * WARNINGS: This function will not work unless a vesa compatable driver * + * is installed in either hardware or software. * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Install_Vesa(void) +{ + UINT paras; + USHORT longest ; + union REGS regs; + struct SREGS sregs; + + /* Calculate size of VesaInfo structure in paragraphs */ + paras = ( sizeof(VesaInfoType) + 15 ) >> 4 ; + + /* Allocate real-mode memory for VESA structure. */ + if ( DPMI_real_alloc ( paras , & rpVInfoSel , & longest ) ) return - 1 ; + rpVInfo = ( void * ) ( rpVInfoSel . seg << 4 ) ; + + /* Calculate size of VesaModeInfo structure in paragraphs */ + paras = ( sizeof(VesaModeInfoType) + 15 ) >> 4 ; + + /* Allocate real-mode memory for VESA structure. */ + if ( DPMI_real_alloc ( paras , & rpModeInfoSel , & longest ) ) + { + Remove_Vesa(); + return (-1); + } + rpModeInfo = ( void * ) ( rpModeInfoSel . seg << 4 ) ; + + /* Clear the input buffer */ + memset ( rpVInfo , 0 , sizeof ( VesaInfoType) ) ; + + + /* Read Vesa information */ + regs . x . eax = 0x4f00 ; + regs . x . edi = 0 ; + sregs . es = rpVInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) return (-1); + Mem_Copy ( rpVInfo , & VInfo , sizeof(VesaInfoType)) ; + + return 0; +} + + +/*************************************************************************** + * VESA_GET_MODE_INFO -- Gets info about specified video mode * + * * + * INPUT: long - the mode we are requesting info about * + * * + * OUTPUT: long - 0 if sucessful, -1 if failure * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Vesa_Get_Mode_Info(long mode) +{ + union REGS regs; + struct SREGS sregs; + + if (rpModeInfo ) + { + /* Clear the input buffer */ + memset ( rpModeInfo, 0, sizeof(VesaModeInfoType)); + + /* Set up function call */ + regs . x . eax = 0x4F01; + regs . x . ecx = mode; + regs . x . edi = 0 ; + sregs . es = rpModeInfoSel . seg ; + DPMI_real_intr ( 0x10 , & regs , & sregs ); + + regs . x . eax &= 0xffff ; + if ( regs . x . eax == 0x004F) + { + Mem_Copy ( rpModeInfo , &ModeInfo , sizeof(VesaModeInfoType)); + + // this part is only temporary until Phil get the documentation + // for DPMI funtion 0x301 simulate real call + // in the mean time we will be making call to the vesa driver + // thru a real interrupt + if ( Vesa_Hook ( ModeInfo . WinFunc ) ) return ( -1 ) ; + return (0); + } + } + return (-1); +} + +/*************************************************************************** + * VESA_SET_MODE -- Sets the specified Vesa mode * + * * + * INPUT: long - the mode we wish to set * + * * + * OUTPUT: long - 0 if all is ok, -1 if we cannot set this mode * + * * + * WARNINGS: You must call Install_Vesa before calling Vesa_Set_Mode * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE long Vesa_Set_Mode(long mode) +{ + union REGS regs; + struct SREGS sregs; + + /* Get mode info */ + if ( Vesa_Get_Mode_Info(mode) ) return (-1) ; + + if ((ModeInfo.Attributes & 0x01) == 0) return (-1); + + /* Set vesa VIDEO MODE */ + regs . x . eax = 0x4F02; // + regs . x . ebx = mode; + int386 ( 0x10 , & regs , & regs ) ; +// delay ( 1000 ) ; + + if ( VInfo . Capabilities & 1 ) + { + /* Set DAC to 6 bit per color register */ + regs . x . eax = 0x4F08; + regs . h . bl = 0; + regs . h . bh = 6 ; + int386 ( 0x10 , & regs , & regs ) ; +// delay ( 1000 ) ; + } + + regs . x . eax &= 0xffff ; + if ( regs . x . eax != 0x004F) return (-1); + + Init_Bank_Table(); + return (0); + +} + +/*************************************************************************** + * VESA_XRES -- Returns horizontal resolution of vesa mode * + * * + * INPUT: none * + * * + * OUTPUT: long - the horizontal resolution of vesa mode * + * * + * WARNINGS: You must call Install_Vesa and Vesa_Set_Mode before calling * + * Vesa_XRes(). * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +long Vesa_XRes(void) +{ + return ((long)ModeInfo.XRes); +} + +/*************************************************************************** + * VESA_YRES -- Returns vertical resolution of a vesa mode * + * * + * INPUT: none * + * * + * OUTPUT: long - the horizontal resolution of vesa mode * + * * + * WARNINGS: You must call Install_Vesa and Vesa_Set_Mode before calling * + * Vesa_XRes(). * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +long Vesa_YRes(void) +{ + return ((long)ModeInfo.YRes); +} + +/*************************************************************************** + * INIT_BANK_TABLE -- Initializes the values in the bank table * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/02/1994 PWG : Created. * + *=========================================================================*/ +PRIVATE void Init_Bank_Table(void) +{ + long size,bankval; + int num_banks,lp; + union REGS regs; + struct SREGS sregs; + + + size = Vesa_XRes() * Vesa_YRes(); // get video mode size + num_banks = size / 65536; // get number of banks + + if (size % 65536) // adjust number of banks if + num_banks++; // they dont break evenly + + bankval = 64 / ModeInfo.WinGranularity; + VesaFunc = ModeInfo.WinFunc; + + for (lp = 0; lp < num_banks; lp++) + BankTable[lp] = lp * bankval; + + + regs . x . eax = 0x4F05; // Reset Vesa to Point to Bank 0 + regs . x . ebx = 0x0000; + regs . x . edx = 0x0000; + int386 ( 0x10 , & regs , & regs ) ; + +} + +/*************************************************************************** + * SET_MODE -- Used to set a graphic mode * + * * + * INPUT: int - the mode that we are setting * + * * + * OUTPUT: int - FALSE if failure, TRUE otherwise * + * * + * HISTORY: * + * 11/21/1994 PWG : Created. * + *=========================================================================*/ +int Set_Video_Mode(int mode) +{ + union REGS inregs,outregs; + static first_time = 0 ; + int oldmode ; + + /*======================================================================*/ + /* If this is the first time we have set the mode we need to store */ + /* of the original mode. */ + /*======================================================================*/ + if ( ! first_time ) { + first_time = 1 ; + Set_Original_Video_Mode(Get_Video_Mode()); + } + + /*======================================================================*/ + /* If the mode we are trying to set is mode zero than we are actually */ + /* trying to restore the system to its original video mode. */ + /*======================================================================*/ + if (mode == RESET_MODE) + mode = Get_Original_Video_Mode(); // set mode properly + + if (mode == GraphicMode) // if mode already correct + return(TRUE); // get out of here + + oldmode = GraphicMode ; + GraphicMode = mode ; + + /**********************************************************************/ + /* make sure the mouse is term off before any change */ + /*********************************************************************/ + if ( MInstalled == TRUE ) Hide_Mouse () ; + + + /* clear color palette */ + memset ( CurrentPalette , 255 , sizeof ( CurrentPalette ) ) ; + memset ( PaletteTable , 0 , sizeof ( PaletteTable ) ) ; + + /*======================================================================*/ + /* If we are currently in a vesa mode, free it up so that we can do it */ + /* again. */ + /*======================================================================*/ + if ((oldmode >= VESA_MIN) && (oldmode <= VESA_MAX)) + Remove_Vesa(); + + + /*======================================================================*/ + /* If we are requesting a vesa mode, than use the vesa calls to handle */ + /* it. */ + /*======================================================================*/ + if ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX) + { + inregs . x . eax = MCGA_MODE; + int386 ( 0x10 , &inregs , &outregs); +// delay ( 1000 ) ; + + if ( Install_Vesa () != 0 ) { + Set_Video_Mode(oldmode); + return(FALSE); + } + + if (Vesa_Set_Mode(GraphicMode) != 0) + { + Set_Video_Mode(oldmode); + return(FALSE); + } + XRes = Vesa_XRes(); + YRes = Vesa_YRes(); + Set_HiRes_Function_Pointers(); + } + else + { + /*===================================================================*/ + /* If not a vesa mode, then handle that as well. */ + /*===================================================================*/ + inregs . x . eax = GraphicMode ; + int386( 0x10, &inregs, &outregs); +// delay ( 1000 ) ; + + if ( GraphicMode == MCGA_MODE ) { + XRes = 320; + YRes = 200; + Set_LoRes_Function_Pointers(); + } + } + if ( MInstalled == TRUE ) { + Reset_Mouse () ; + Show_Mouse () ; + } + return(TRUE); +} +/*************************************************************************** + * VESA_INFO -- Debug routine which displays vesa info to stdout * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Must call Install_Vesa before attempting to get the vesa * + * info. * + * * + * HISTORY: * + * 11/22/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Info(void) +{ + cout << "Attributes: " << (long)ModeInfo.Attributes << "\n" + << "Win A Attributes: " << (long)ModeInfo.WinA_Attributes << "\n" + << "Win B Attributes: " << (long)ModeInfo.WinB_Attributes << "\n" + << "Win Granularity " << (long)ModeInfo.WinGranularity << "\n" + << "Win Size: " << (long)ModeInfo.WinSize << "\n" + << "Win A Segment: " << hex << (unsigned short)ModeInfo.WinA_Segment << "\n" + << "Win B Segment: " << (unsigned short)ModeInfo.WinB_Segment << "\n" + << "Bytes per scan line: " << dec << (unsigned short)ModeInfo.BytesPerScanline << "\n" + << "X resolution: " << (long)ModeInfo.XRes << "\n" + << "Y resolution: " << (long)ModeInfo.YRes << "\n" + << "X Char Size: " << (long)ModeInfo.XCharSize << "\n" + << "Y Char Size: " << (long)ModeInfo.YCharSize << "\n" + << "Number of planes: " << (long)ModeInfo.NumPlanes << "\n" + << "Bits per pixels: " << (long)ModeInfo.BitsPerPixel << "\n" + << "Number of Banks: " << (long)ModeInfo.NumBanks << "\n" + << "Memory Model: " << (long)ModeInfo.MemoryModel << "\n" + << "Bank Size: " << (long)ModeInfo.BankSize << "\n"; +} + + +/*************************************************************************** + * VESA_SET_WINDOW -- Sets given vesa window to given grain * + * * + * INPUT: int window - 0 for window A, 1 for window B * + * int grain - the granularity point for window * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/05/1994 PWG : Created. * + *=========================================================================*/ +void Vesa_Set_Window(long grain_num) +{ + union REGS regs; + struct SREGS sregs; + + regs . x . eax = 0x4f05 ; + regs . x . ebx = 0 ; + regs . x . edx = grain_num; +// DPMI_real_call ( ModeInfo.WinFunc , & regs , & sregs ) ; + DPMI_real_intr ( VesaFunc , & regs , & sregs ) ; + +} + + +/*************************************************************************** + * SET_LORES_FUNCTION_POINTERS -- Sets up the lowres function pointers * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +PRIVATE VOID Set_LoRes_Function_Pointers(VOID) +{ + VVPC_Clear_Func = MCGA_Clear; + VVPC_To_Buffer_Func = MCGA_To_Buffer; + VVPC_Put_Pixel_Func = MCGA_Put_Pixel; + VVPC_Get_Pixel_Func = MCGA_Get_Pixel; + GVPC_Blit_to_VVPC_Func = Linear_Blit_To_Linear; + VVPC_Blit_to_GVPC_Func = Linear_Blit_To_Linear; + VVPC_Blit_to_VVPC_Func = Linear_Blit_To_Linear; + VVPC_Buffer_To_Page = MCGA_Buffer_To_Page; + GVPC_Scale_To_VVPC = Linear_Scale_To_Linear; + VVPC_Scale_To_GVPC = Linear_Scale_To_Linear; + VVPC_Scale_To_VVPC = Linear_Scale_To_Linear; + VVPC_Print_Func = MCGA_Print; +} +/*************************************************************************** + * SET_HIRES_FUNCTION_POINTERS -- Sets the HiRes function pointers * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +PRIVATE VOID Set_HiRes_Function_Pointers(VOID) +{ + VVPC_Clear_Func = Vesa_Clear; + VVPC_To_Buffer_Func = Vesa_To_Buffer; + VVPC_Put_Pixel_Func = Vesa_Put_Pixel; + VVPC_Get_Pixel_Func = Vesa_Get_Pixel; + GVPC_Blit_to_VVPC_Func = Linear_Blit_To_Vesa; + VVPC_Blit_to_GVPC_Func = Vesa_Blit_To_Linear; + VVPC_Blit_to_VVPC_Func = Vesa_Blit_To_Vesa; + VVPC_Buffer_To_Page = Vesa_Buffer_To_Page; + GVPC_Scale_To_VVPC = Linear_Scale_To_Vesa; + VVPC_Scale_To_GVPC = Vesa_Scale_To_Linear; + VVPC_Scale_To_VVPC = Vesa_Scale_To_Vesa; + VVPC_Print_Func = Vesa_Print; +} + +/*************************************************************************** + * Update_Video_Mode -- used to reprogram the current graphic mode afte * + * a task swicthing from windows * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 03/18/94 JRJ : Created. * + *=========================================================================*/ +void Update_Video_Mode (void) +{ + union REGS inregs,outregs; + + /* clear color palette */ + memset ( CurrentPalette , 255 , sizeof ( CurrentPalette ) ) ; + memset ( PaletteTable , 0 , sizeof ( PaletteTable ) ) ; + + + /**********************************************************************/ + /* make sure the mouse is term off before any change */ + /*********************************************************************/ + if ( MInstalled == TRUE ) + Hide_Mouse () ; + + /*======================================================================*/ + /* If we are requesting a vesa mode, than use the vesa calls to handle */ + /* it. */ + /*======================================================================*/ + if ( GraphicMode >= VESA_MIN && GraphicMode <= VESA_MAX) { + inregs . x . eax = MCGA_MODE; + int386 ( 0x10 , &inregs , &outregs); + Vesa_Set_Mode(GraphicMode); + } + else { + /*===================================================================*/ + /* If not a vesa mode, then handle that as well. */ + /*===================================================================*/ + inregs . x . eax = GraphicMode ; + int386( 0x10, &inregs, &outregs); + } + if ( MInstalled == TRUE ) { + Reset_Mouse () ; + Show_Mouse () ; + } +} + + +/*************************************************************************** + * SET_ORIGINAL_VIDEO_MODE -- sets mode to restore system to on exit * + * * + * INPUT: int video mode number * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/29/1995 PWG : Created. * + *=========================================================================*/ +void Set_Original_Video_Mode(int mode) +{ + _OriginalVideoMode = mode; +} + + +/*************************************************************************** + * GET_ORIGINAL_VIDEO_MODE -- Gets the original video mode value * + * * + * INPUT: none * + * * + * OUTPUT: int the video mode set when we entered * + * * + * HISTORY: * + * 06/29/1995 PWG : Created. * + *=========================================================================*/ + +int Get_Original_Video_Mode(void) +{ + return(_OriginalVideoMode); +} diff --git a/WWFLAT32/VIDEO/VIDEO.H b/WWFLAT32/VIDEO/VIDEO.H new file mode 100644 index 0000000..36ce916 --- /dev/null +++ b/WWFLAT32/VIDEO/VIDEO.H @@ -0,0 +1,213 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern REALPTR VesaFunc; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H diff --git a/WWFLAT32/WINDOWS/MAKEFILE b/WWFLAT32/WINDOWS/MAKEFILE new file mode 100644 index 0000000..3f0f79b --- /dev/null +++ b/WWFLAT32/WINDOWS/MAKEFILE @@ -0,0 +1,193 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : jan 24, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = windows +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + windows.obj & + winhide.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + mkdir run + cd run + copy $(%WWVCS)\$(PROJ_NAME)\test\run\vcs.cfg + update + cd.. + mkdir art + cd art + copy $(%WWVCS)\$(PROJ_NAME)\test\art\vcs.cfg + update + cd.. + wmake + cd .. + + +#**************************** End of makefile ****************************** + + diff --git a/WWFLAT32/WINDOWS/WINDOW.H b/WWFLAT32/WINDOWS/WINDOW.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/WWFLAT32/WINDOWS/WINDOW.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WWFLAT32/WINDOWS/WINDOWS.CPP b/WWFLAT32/WINDOWS/WINDOWS.CPP new file mode 100644 index 0000000..23ff73d --- /dev/null +++ b/WWFLAT32/WINDOWS/WINDOWS.CPP @@ -0,0 +1,973 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "window.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(BYTE const *,int,int,int) = Standard_More_Prompt; + +extern GraphicBufferClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + Clear_KeyBuffer(); + Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ + case KA_LITERAL: + if (c != (char) 127) { // check if fell thru from extend case + c = 0; // set to zero for literal case + } + c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & 0x07; + c = (c & 0x78) >> 3; + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + diff --git a/WWFLAT32/WINDOWS/WINDOWS.CPP.BAK b/WWFLAT32/WINDOWS/WINDOWS.CPP.BAK new file mode 100644 index 0000000..a407953 --- /dev/null +++ b/WWFLAT32/WINDOWS/WINDOWS.CPP.BAK @@ -0,0 +1,973 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "windows.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(BYTE const *,int,int,int) = Standard_More_Prompt; + +extern GraphicBufferClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + Clear_KeyBuffer(); + Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ + case KA_LITERAL: + if (c != (char) 127) { // check if fell thru from extend case + c = 0; // set to zero for literal case + } + c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & 0x07; + c = (c & 0x78) >> 3; + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + diff --git a/WWFLAT32/WINDOWS/WINDOWS.H b/WWFLAT32/WINDOWS/WINDOWS.H new file mode 100644 index 0000000..b6e2baa --- /dev/null +++ b/WWFLAT32/WINDOWS/WINDOWS.H @@ -0,0 +1,96 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][8]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/WWFLAT32/WINDOWS/WINHIDE.CPP b/WWFLAT32/WINDOWS/WINHIDE.CPP new file mode 100644 index 0000000..00acebf --- /dev/null +++ b/WWFLAT32/WINDOWS/WINHIDE.CPP @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "window.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; + Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ + Conditional_Show_Mouse(); +} +#endif + + + diff --git a/WWFLAT32/WINDOWS/WINHIDE.CPP.BAK b/WWFLAT32/WINDOWS/WINHIDE.CPP.BAK new file mode 100644 index 0000000..dbcb447 --- /dev/null +++ b/WWFLAT32/WINDOWS/WINHIDE.CPP.BAK @@ -0,0 +1,97 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "windows.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; + Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ + Conditional_Show_Mouse(); +} +#endif + + + \ No newline at end of file diff --git a/WWFLAT32/WSA/MAKEFILE b/WWFLAT32/WSA/MAKEFILE new file mode 100644 index 0000000..9d97cd6 --- /dev/null +++ b/WWFLAT32/WSA/MAKEFILE @@ -0,0 +1,181 @@ +# +# 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 . +# + +#*************************************************************************** +#** 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 : Westwood Library .LIB makefile * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Julio R. Jerez * +#* * +#* Start Date : Jan 30, 1995 * +#* * +#* * +#*-------------------------------------------------------------------------* +#* * +#* Required environment variables: * +#* WWFLAT = your root WWFLAT path * +#* WWVCS = root directory for wwlib version control archive * +#* WATCOM = your Watcom installation path * +#* * +#* Required changes to makefile: * +#* PROJ_NAME = name of the library you're building * +#* OBJECTS = list of objects in your library * +#* * +#* Optional changes to makefile: * +#* PROJ_DIR = full pathname of your working directory * +#* .path.xxx = full pathname where various file types live * +#* * +#*************************************************************************** + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +!error WWFLAT Environment var not configured. +!endif + + +!ifndef %WWVCS +!error WWVCS Environment var not configured. +!endif + +!ifndef %WATCOM +!error WATCOM Environment var not configured. +!endif + + +#=========================================================================== +# User-defined section: the user should tailor this section for each project +#=========================================================================== + +PROJ_NAME = wsa +PROJ_DIR = $(%WWFLAT)\$(PROJ_NAME) +LIB_DIR = $(%WWFLAT)\lib + +!include $(%WWFLAT)\project.cfg + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + wsa.obj & + xordelta.obj + + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: $(PROJ_DIR) +.c: $(PROJ_DIR) +.cpp: $(PROJ_DIR) +.h: $(PROJ_DIR) +.obj: $(PROJ_DIR) +.lib: $(%WWFLAT32)\lib +.exe: $(PROJ_DIR) + +#=========================================================================== +# Pre-defined section: there should be little need to modify this section. +#=========================================================================== + +#--------------------------------------------------------------------------- +# Tools/commands +#--------------------------------------------------------------------------- +C_CMD = wcc386 +CPP_CMD = wpp386 +LIB_CMD = wlib +LINK_CMD = wlink +ASM_CMD = tasm32 + +#--------------------------------------------------------------------------- +# Include & library paths +# If LIB & INCLUDE are already defined, they are used in addition to the +# WWLIB32 lib & include; otherwise, they're constructed from +# BCDIR & TNTDIR +#--------------------------------------------------------------------------- +LIBPATH = $(%WWFLAT)\LIB;$(%WATCOM)\LIB +INCLUDEPATH = $(%WWFLAT)\INCLUDE;$(%WATCOM)\H + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(C_CMD) $(CC_CFG) $< + +.cpp.obj: $(%WWFLAT)\project.cfg .AUTODEPEND + $(CPP_CMD) $(CC_CFG) $< + +.asm.obj: $(%WWFLAT)\project.cfg + $(ASM_CMD) $(ASM_CFG) $< + + +#--------------------------------------------------------------------------- +# Default target: configuration files & library (in that order) +#--------------------------------------------------------------------------- +all: $(LIB_DIR)\$(PROJ_NAME).lib .SYMBOLIC + + +#--------------------------------------------------------------------------- +# Build the library +# The original library is deleted by the librarian +# Lib objects & -+ commands are constructed by substituting within the +# $^@ macro (which expands to all target dependents, separated with +# spaces) +# Tlib's cfg file is not invoked as a response file. +# All headers & source files are copied into WWFLAT\SRCDEBUG, for debugging +#--------------------------------------------------------------------------- +$(LIB_DIR)\$(PROJ_NAME).lib: $(OBJECTS) objects.lbc + copy *.h $(%WWFLAT)\include + copy *.inc $(%WWFLAT)\include + copy *.cpp $(%WWFLAT)\srcdebug + copy *.asm $(%WWFLAT)\srcdebug + $(LIB_CMD) $(LIB_CFG) $^@ @objects.lbc + +#--------------------------------------------------------------------------- +# Objects now have a link file which is NOT generated everytime. Instead +# it just has its own dependacy rule. +#--------------------------------------------------------------------------- +objects.lbc : $(OBJECTS) + %create $^@ + for %index in ($(OBJECTS)) do %append $^@ +%index + +#--------------------------------------------------------------------------- +# Create the test directory and make it. +#--------------------------------------------------------------------------- +test: + mkdir test + cd test + copy $(%WWVCS)\$(PROJ_NAME)\test\vcs.cfg + update + wmake + cd .. + +#**************************** End of makefile ****************************** + diff --git a/WWFLAT32/WSA/WSA.CPP b/WWFLAT32/WSA/WSA.CPP new file mode 100644 index 0000000..cc3556a --- /dev/null +++ b/WWFLAT32/WSA/WSA.CPP @@ -0,0 +1,1150 @@ +/* +** 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 . +*/ + +/* $Header: g:/library/source/rcs/./wsa.c 1.16 1994/05/20 15:35:27 joe_bostic Exp $ */ +/*************************************************************************** + ** 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 Animation System * + * * + * File Name : WSA.C * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 20, 1991 * + * * + *-------------------------------------------------------------------------* + * There are many different ways that the user can use the WSA library * + * module. The options are as follows : * + * * + * System Allocation vs User Buffer - The user may request that the * + * system allocate the needed buffer from the heap or the user may * + * pass his own buffer in for the animator to use. * + * * + * Resident vs File based - If there is enough RAM, the user may put the * + * entire animation into RAM for fastest animations. If there is * + * not enouph RAM, the system will automatically make it so each * + * frame will be read off disk when needed. * + * * + * Direct to Page vs Use of a user buffer -- Noramally Direct to page * + * is the best method both in speed and in RAM need to hold anim. * + * One may want to use the write to user buffer method if they * + * are using the animation in a non sequencial order. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Animate_Frame -- Displays a frame of a given animation * + * Get_Animation_Frame_Count -- Return Number of frames in an animation. * + * Get_Animation_X -- Gets the x from an animation * + * Get_Animation_Y -- Gets the y from an animation * + * Get_Animation_Width -- Gets the width from an animation * + * Get_Animation_Height -- The height of the animation we are processing * + * Apply_Delta -- Copies frame into delta buffer, then applies to target * + * Close_Animation -- Close the animation, freeing the space if necessary* + * Get_File_Frame_Offset -- Get offset of a delta frame from animate file* + * Get_Resident_Frame_Offset -- Gets frame offset of animate file in RAM * + * Open_Animation -- Opens an animation file and reads into buffer * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "wsa.h" +#include +#include +#include +#include +#include +#include + +// +// WSA animation header allocation type. +// If we need more then 8 flags for the flags variable, we can combine +// USER_ALLOCATED with SYS_ALLOCATED and combine FILE with RESIDENT. +// +#define WSA_USER_ALLOCATED 0x01 +#define WSA_SYS_ALLOCATED 0x02 +#define WSA_FILE 0x04 +#define WSA_RESIDENT 0x08 +#define WSA_TARGET_IN_BUFFER 0x10 +#define WSA_LINEAR_ONLY 0x20 +#define WSA_FRAME_0_ON_PAGE 0x40 +#define WSA_AMIGA_ANIMATION 0x80 +#define WSA_PALETTE_PRESENT 0x100 +#define WSA_FRAME_0_IS_DELTA 0x200 + +// 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 lp_asm.asm. +#define DO_XOR 0x0 +#define DO_COPY 0x01 +#define TO_VIEWPORT 0x0 +#define TO_PAGE 0x02 + + +typedef struct { + UWORD current_frame; + UWORD total_frames; + UWORD pixel_x; + UWORD pixel_y; + UWORD pixel_width; + UWORD pixel_height; + UWORD largest_frame_size; + BYTE *delta_buffer; + BYTE *file_buffer; + BYTE file_name[ 13 ]; + WORD flags; + // New fields that animate does not know about below this point. SEE EXTRA_BYTES_ANIMATE_NOT_KNOW_ABOUT + WORD file_handle; + ULONG anim_mem_size; +} SysAnimHeaderType; + +// NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE +// UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING +// IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE BYTES ON NOW FOR IT TO WORK. +#define EXTRA_BYTES_ANIMATE_NOT_KNOW_ABOUT (sizeof(WORD) + sizeof(ULONG)) + + +// +// Header structure for the file. +// NOTE: The 'total_frames' field is used to differentiate between Amiga and IBM +// animations. Amiga animations have the HIGH bit set. +// +typedef struct { + UWORD total_frames; + UWORD pixel_x; + UWORD pixel_y; + UWORD pixel_width; + UWORD pixel_height; + UWORD largest_frame_size; + WORD flags; + ULONG frame0_offset; + ULONG frame0_end; + /* ULONG data_seek_offset, UWORD frame_size ... */ +} WSA_FileHeaderType; + +#define WSA_FILE_HEADER_SIZE ( sizeof(WSA_FileHeaderType) - (2 * sizeof(ULONG)) ) + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE ULONG Get_Resident_Frame_Offset( BYTE *file_buffer, WORD frame ); +PRIVATE ULONG Get_File_Frame_Offset( WORD file_handle, WORD frame, WORD palette_adjust); +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, WORD curr_frame, BYTE *dest_ptr, WORD dest_w); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_ANIMATION -- Opens an animation file and reads into buffer * + * * + * INPUT: BYTE *file_name of animation sequence file. * + * BYTE *user_buffer pointer if one exists (NULL ok) * + * ULONG user_buffer_size if known (NULL ok) * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to palette space for return (NULL ok) * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: May return NULL, please check. * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +VOID * cdecl Open_Animation(BYTE const *file_name, BYTE *user_buffer, LONG user_buffer_size, WSAOpenType user_flags, UBYTE *palette) +{ + WORD fh, anim_flags; + WORD palette_adjust; + UWORD offsets_size; + UWORD frame0_size; + LONG target_buffer_size, delta_buffer_size, file_buffer_size; + LONG max_buffer_size, min_buffer_size; + BYTE *sys_anim_header_buffer; + BYTE *target_buffer; + BYTE *delta_buffer, *delta_back; + SysAnimHeaderType *sys_header; + WSA_FileHeaderType file_header; + + /*======================================================================*/ + /* Open the file to get the header information */ + /*======================================================================*/ + + anim_flags = 0; + fh = Open_File(file_name, READ); + Read_File(fh, (BYTE *) &file_header, sizeof(WSA_FileHeaderType)); + + /*======================================================================*/ + /* If the file has an attached palette then if we have a valid palette */ + /* pointer we need to read it in. */ + /*======================================================================*/ + + if (file_header.flags & 1) { + anim_flags |= WSA_PALETTE_PRESENT; + palette_adjust = 768; + + if (palette != NULL) { + Seek_File(fh, sizeof(ULONG) * (file_header.total_frames), SEEK_CUR); + Read_File(fh, palette, 768L); + } + + } else { + palette_adjust = 0; + } + + // Check for flag from ANIMATE indicating that this animation was + // created from a .LBM and a .ANM. These means that the first + // frame is a XOR Delta from a picture, not black. + if (file_header.flags & 2) { + anim_flags |= WSA_FRAME_0_IS_DELTA; + } + + + // Get the total file size minus the size of the first frame and the size + // of the file header. These will not be read in to save even more space. + file_buffer_size = Seek_File(fh, 0L, SEEK_END); + + if (file_header.frame0_offset) { + LONG tlong; + + tlong = file_header.frame0_end - file_header.frame0_offset; + frame0_size = (UWORD) tlong; + } + else { + anim_flags |= WSA_FRAME_0_ON_PAGE; + frame0_size = 0; + } + + file_buffer_size -= palette_adjust + frame0_size + WSA_FILE_HEADER_SIZE; + + // We need to determine the buffer sizes required for the animation. At a + // minimum, we need a target buffer for the uncompressed frame and a delta + // buffer for the delta data. We may be able to make the file resident, + // so we will determine the file size. + // + // If the target buffer is in the user buffer + // Then figure its size + // and set the allocation flag + // Else size is zero. + // + if (user_flags & WSA_OPEN_DIRECT) { + target_buffer_size = 0L; + } + else { + anim_flags |= WSA_TARGET_IN_BUFFER; + target_buffer_size = (ULONG) file_header.pixel_width * file_header.pixel_height; + } + + // NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE + // UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING + // IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE BYTES ON NOW FOR IT TO WORK. + delta_buffer_size = (ULONG) file_header.largest_frame_size + EXTRA_BYTES_ANIMATE_NOT_KNOW_ABOUT; + min_buffer_size = target_buffer_size + delta_buffer_size; + max_buffer_size = min_buffer_size + file_buffer_size; + + // check to see if buffer size is big enough for at least min required + if (user_buffer && (user_buffer_size < min_buffer_size)) { + Close_File(fh); + return(NULL); + } + + // A buffer was not passed in, so do allocations + if (user_buffer == NULL) { + + // If the user wants it from the disk, then let us give it to him, + // otherwise, try to give a max allocation he can have. + if (user_flags & WSA_OPEN_FROM_DISK) { + user_buffer_size = min_buffer_size; + } + // else no buffer size, then try max configuration. + else if (!user_buffer_size) { + user_buffer_size = max_buffer_size; + } + // else if buffer specified is less then max needed, give min. + else if (user_buffer_size < max_buffer_size) { + user_buffer_size = min_buffer_size; + } + // otherwise we only want to alloc what we need. + else { + user_buffer_size = max_buffer_size; + } + + + // Check to see if enough RAM available for buffer_size. + if (user_buffer_size > Ram_Free(MEM_NORMAL)) { + + // If not enough room for even the min, return no buffer. + + if (min_buffer_size > Ram_Free(MEM_NORMAL)) { + Close_File(fh); + return(NULL); + } + + // Else make buffer size the min and allocate it. + user_buffer_size = min_buffer_size; + } + + // allocate buffer needed + user_buffer = (BYTE *) Alloc(user_buffer_size, MEM_CLEAR); + + anim_flags |= WSA_SYS_ALLOCATED; + } + else { + // Check to see if the user_buffer_size should be min or max. + if ((user_flags & WSA_OPEN_FROM_DISK) || (user_buffer_size < max_buffer_size)) { + user_buffer_size = min_buffer_size; + } + else { + user_buffer_size = max_buffer_size; + } + anim_flags |= WSA_USER_ALLOCATED; + } + + + // Set the pointers to the RAM buffers + sys_anim_header_buffer = user_buffer; + target_buffer = (BYTE *) Add_Long_To_Pointer(sys_anim_header_buffer, sizeof(SysAnimHeaderType)); + delta_buffer = (BYTE *) Add_Long_To_Pointer(target_buffer, target_buffer_size); + + // Clear target buffer if it is in the user buffer. + if (target_buffer_size) { + memset( target_buffer, 0, (UWORD) target_buffer_size ); + } + + // Poke data into the system animation header (start of user_buffer) + // current_frame is set to total_frames so that Animate_Frame() knows that + // it needs to clear the target buffer. + + sys_header = ( SysAnimHeaderType * ) sys_anim_header_buffer; + sys_header -> current_frame = + sys_header -> total_frames = file_header.total_frames; + sys_header -> pixel_x = file_header.pixel_x; + sys_header -> pixel_y = file_header.pixel_y; + sys_header -> pixel_width = file_header.pixel_width; + sys_header -> pixel_height = file_header.pixel_height; + sys_header -> anim_mem_size = user_buffer_size; + sys_header -> delta_buffer = delta_buffer; + sys_header -> largest_frame_size = + (UWORD) delta_buffer_size - sizeof(SysAnimHeaderType); + + strcpy(sys_header->file_name, file_name); + + // Figure how much room the frame offsets take up in the file. + // Add 2 - one for the wrap around and one for the final end offset. + offsets_size = (file_header.total_frames + 2) << 2; + + // Can the user_buffer_size handle the maximum case buffer? + if ( user_buffer_size == max_buffer_size) { + + // + // set the file buffer pointer, + // Skip over the header information. + // Read in the offsets. + // Skip over the first frame. + // Read in remaining frames. + // + + sys_header->file_buffer = (BYTE *)Add_Long_To_Pointer(delta_buffer,sys_header->largest_frame_size); + Seek_File( fh, WSA_FILE_HEADER_SIZE, SEEK_SET); + Read_File( fh, sys_header->file_buffer, offsets_size); + Seek_File( fh, frame0_size + palette_adjust, SEEK_CUR); + Read_File( fh, sys_header->file_buffer + offsets_size, + file_buffer_size - offsets_size); + + // + // Find out if there is an ending value for the last frame. + // If there is not, then this animation will not be able to + // loop back to the beginning. + // + if (Get_Resident_Frame_Offset( sys_header->file_buffer, sys_header->total_frames + 1)) + anim_flags |= WSA_RESIDENT; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_RESIDENT; + } + else { // buffer cannot handle max_size of buffer + + if(Get_File_Frame_Offset( fh, sys_header->total_frames + 1, palette_adjust)) + anim_flags |= WSA_FILE; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_FILE; +//// + sys_header->file_buffer = NULL; + } + + // Figure where to back load frame 0 into the delta buffer. + delta_back = (BYTE *)Add_Long_To_Pointer(delta_buffer, + sys_header->largest_frame_size - frame0_size); + + // Read the first frame into the delta buffer and uncompress it. + // Then close it. + Seek_File( fh, WSA_FILE_HEADER_SIZE + offsets_size + palette_adjust, SEEK_SET); + Read_File( fh, delta_back, frame0_size); + + // We do not use the file handle when it is in RAM. + if (anim_flags & WSA_RESIDENT) { + sys_header -> file_handle = (WORD) -1; + Close_File(fh); + } + else { + sys_header -> file_handle = fh; + } + + LCW_Uncompress(delta_back, delta_buffer, sys_header->largest_frame_size); + + // Finally set the flags, + sys_header->flags = anim_flags; + + // return valid handle + return( user_buffer ); +} + + +/*************************************************************************** + * CLOSE_ANIMATION -- Close the animation, freeing the space if necessary. * + * * + * INPUT: VOID *handle to the animation data buffer * + * * + * OUTPUT: none * + * * + * WARNINGS: handle MUST have been returned by Open_Animation * + * * + * HISTORY: * + * 11/23/1991 ML : Created. * + *=========================================================================*/ +VOID cdecl Close_Animation( VOID *handle ) +{ + SysAnimHeaderType *sys_header; + + // Assign our local system header pointer to the beginning of the handle space + sys_header = (SysAnimHeaderType *) handle; + + // Close the WSA file in it was disk based. + if (sys_header->flags & WSA_FILE) { + Close_File(sys_header->file_handle); + } + + // Check to see if the buffer was allocated OR the programmer provided the buffer + if (handle && sys_header->flags & WSA_SYS_ALLOCATED) { + Free(handle); + } +} + +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: VOID *handle to the animation. * + * WORD frame_number wanted to be displayed * + * WORD x_pixel position of left side of animation on page * + * WORD y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma argsused +BOOL cdecl Animate_Frame(VOID *handle, VideoViewPortClass& view, + WORD frame_number, WORD x_pixel, WORD y_pixel, + WSAType flags_and_prio, VOID *magic_cols, VOID *magic) +{ + SysAnimHeaderType *sys_header; // fix up the VOID pointer past in. + WORD curr_frame; // current frame we are on. + WORD total_frames; // number of frames in anim. + WORD distance; // distance to desired frame. + WORD search_dir; // direcion to search for desired frame. + WORD search_frames; // How many frames to search. + WORD loop; // Just a loop varible. + BYTE *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + WORD dest_width; // the width of the destination buffer or page. + + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (WORD)sys_header->pixel_x; + y_pixel += (WORD)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (BYTE *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (BYTE *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + WORD flags = ((UWORD)flags_and_prio & 0xFF00u) >> 12u; + WORD pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: VOID *handle to the animation. * + * WORD frame_number wanted to be displayed * + * WORD x_pixel position of left side of animation on page * + * WORD y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +#pragma argsused +BOOL cdecl Animate_Frame(VOID *handle, GraphicViewPortClass& view, + WORD frame_number, WORD x_pixel, WORD y_pixel, + WSAType flags_and_prio, VOID *magic_cols, VOID *magic) +{ + SysAnimHeaderType *sys_header; // fix up the VOID pointer past in. + WORD curr_frame; // current frame we are on. + WORD total_frames; // number of frames in anim. + WORD distance; // distance to desired frame. + WORD search_dir; // direcion to search for desired frame. + WORD search_frames; // How many frames to search. + WORD loop; // Just a loop varible. + BYTE *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + WORD dest_width; // the width of the destination buffer or page. + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (WORD)sys_header->pixel_x; + y_pixel += (WORD)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (BYTE *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (BYTE *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + WORD flags = ((UWORD)flags_and_prio & 0xFF00u) >> 12u; + WORD pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} + + +/*************************************************************************** + * ANIMATE_FRAME_COUNT -- Return Number of frames in an animation. * + * * + * INPUT: VOID *handle to the animation. * + * * + * OUTPUT: WORD number of frames in animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/05/1991 SB : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_Frame_Count(VOID *handle) +{ + SysAnimHeaderType *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return((WORD)sys_header->total_frames); +} + + +/*************************************************************************** + * GET_ANIM_X -- Gets the x from an animation * + * * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: WORD the x of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1992 DRD : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_X(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_x); +} + +/*************************************************************************** + * GET_ANIM_Y -- Gets the y from an animation * + * * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: WORD the y of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_Y(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_y); +} + +/*************************************************************************** + * GET_ANIM_WIDTH -- Gets the width from an animation * + * * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: WORD the width of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_Width(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_width); +} + +/*************************************************************************** + * GET_ANIM_HEIGHT -- The height of the animation we are processing * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: WORD the height of the animation we are processing * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_Height(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_height); +} + + +/*************************************************************************** + * GET_ANIM_PALETTE -- Returns true if the anim had an attached palette * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: WORD True if the animation has a set palette. False if the * + * animation does not. * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +WORD cdecl Get_Animation_Palette(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->flags & WSA_PALETTE_PRESENT); +} + + +/*************************************************************************** + * GET_ANIMATION_SIZE -- Return the amount of memory the animation is using* + * * + * * + * INPUT: VOID * to the animation that we are processing * + * * + * OUTPUT: ULONG number of byte used by animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/23/1994 SKB : Created. * + *=========================================================================*/ +ULONG cdecl Get_Animation_Size(VOID const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->anim_mem_size); +} + +/* :::::::::::::::::::::::::::: PRIVATE FUNCTIONS :::::::::::::::::::::::::::::: */ + + +/*************************************************************************** + * GET_RESIDENT_FRAME_OFFSET -- Gets frame offset of animate file in RAM * + * * + * INPUT: BYTE *file_buffer in RAM of animation file. * + * WORD frame number that we need the offset of. * + * * + * OUTPUT: WORD offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE ULONG Get_Resident_Frame_Offset( BYTE *file_buffer, WORD frame ) +{ + ULONG frame0_size; + ULONG *lptr; + + // If there is a frame 0, the calculate its size. + lptr = (ULONG *) file_buffer; + + if (*lptr) { + frame0_size = lptr[1] - *lptr; + } else { + frame0_size = 0; + } + + // Return the offset into RAM for the frame. + lptr += frame; + if (*lptr) + return (*lptr - (frame0_size + WSA_FILE_HEADER_SIZE)); + else + return (0L); +} + + +/*************************************************************************** + * GET_FILE_FRAME_OFFSET -- Get offset of a delta frame from animate file. * + * * + * INPUT: WORD file_handle of animation file. * + * WORD frame number that we need the offset of. * + * * + * OUTPUT: WORD offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE ULONG Get_File_Frame_Offset( WORD file_handle, WORD frame, WORD palette_adjust) +{ + ULONG offset; + + Seek_File(file_handle, (frame << 2) + WSA_FILE_HEADER_SIZE, SEEK_SET); + + if (Read_File(file_handle, (BYTE *) &offset, sizeof(ULONG)) != sizeof(ULONG)) { + offset = 0L; + } + offset += palette_adjust; + return( offset ); +} + + +/*************************************************************************** + * APPLY_DELTA -- Copies frame into delta buffer, then applies to target * + * * + * INPUT: SysAnimHeaderType *sys_header - pointer to animation buffer.* + * WORD curr_frame - frame to put into target buffer. * + * * + * OUTPUT: BOOL - Return wether or not it worked. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, WORD curr_frame, BYTE *dest_ptr, WORD dest_w) +{ + BYTE *data_ptr, *delta_back; + WORD file_handle, palette_adjust; + ULONG frame_data_size, frame_offset; + + + palette_adjust = ((sys_header->flags & WSA_PALETTE_PRESENT) ? 768 : 0); + delta_back = sys_header->delta_buffer; + + if (sys_header->flags & WSA_RESIDENT) { + // Get offset of the given frame in the resident file + // Get the size of the frame <- (frame+1 offset) - (offset) + // Point at the delta data + // figure offset to load data into end of delta buffer + // copy it into buffer + + frame_offset = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame); + frame_data_size = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame + 1) - frame_offset; + + data_ptr = (BYTE *)Add_Long_To_Pointer(sys_header->file_buffer, frame_offset); + delta_back = (BYTE *)Add_Long_To_Pointer(delta_back, + sys_header->largest_frame_size - frame_data_size); + + Mem_Copy( data_ptr, delta_back, frame_data_size ); + + } else if (sys_header -> flags & WSA_FILE) { + + // Open up file because not file not in RAM. + // Get offset of the given frame in the file on disk + // Get the size of the frame <- (frame+1 offset) - (offset) + // Return if Get_.._offset() failed. -- need error handling???? + // Seek to delta data. + // figure offset to load data into end of delta buffer + // Read it into buffer -- Return if correct amount not read.-- errors?? + + file_handle = sys_header->file_handle; + Seek_File(file_handle, 0L, SEEK_SET); + + frame_offset = Get_File_Frame_Offset(file_handle, curr_frame, palette_adjust); + frame_data_size = Get_File_Frame_Offset(file_handle, curr_frame + 1, palette_adjust) - frame_offset; + + if (!frame_offset || !frame_data_size) { + return(FALSE); + } + + Seek_File(file_handle, frame_offset, SEEK_SET); + delta_back = (BYTE *)Add_Long_To_Pointer(delta_back, sys_header->largest_frame_size - frame_data_size); + + if (Read_File(file_handle, delta_back, frame_data_size) != frame_data_size) { + return(FALSE); + } + } + + // Uncompress data at end of delta buffer to the beginning of delta buffer. + // Find start of target buffer. + // Apply the XOR delta. + + LCW_Uncompress(delta_back, sys_header->delta_buffer, sys_header->largest_frame_size); + + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + Apply_XOR_Delta(dest_ptr, sys_header->delta_buffer); + } + else { + Apply_XOR_Delta_To_Page_Or_Viewport(dest_ptr, sys_header->delta_buffer, + sys_header->pixel_width, dest_w, DO_XOR); + } + + return(TRUE); +} + diff --git a/WWFLAT32/WSA/WSA.H b/WWFLAT32/WSA/WSA.H new file mode 100644 index 0000000..22e4007 --- /dev/null +++ b/WWFLAT32/WSA/WSA.H @@ -0,0 +1,166 @@ +/* +** 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 . +*/ + +/*************************************************************************** + ** 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 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +#ifndef VBUFFER_H +#include "vbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +VOID * cdecl Open_Animation(BYTE const *file_name, BYTE *user_buffer, LONG user_buffer_size, WSAOpenType user_flags, UBYTE *palette=NULL); +VOID cdecl Close_Animation( VOID *handle ); +BOOL cdecl Animate_Frame(VOID *handle, GraphicViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +BOOL cdecl Animate_Frame(VOID *handle, VideoViewPortClass& view, + WORD frame_number, WORD x_pixel=0, WORD y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, VOID *magic_cols=NULL, VOID *magic=NULL); +WORD cdecl Get_Animation_Frame_Count(VOID *handle); +WORD cdecl Get_Animation_X(VOID const *handle); +WORD cdecl Get_Animation_Y(VOID const *handle); +WORD cdecl Get_Animation_Width(VOID const *handle); +WORD cdecl Get_Animation_Height(VOID const *handle); +WORD cdecl Get_Animation_Palette(VOID const *handle); +ULONG cdecl Get_Animation_Size(VOID const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: BYTE *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * UBYTE *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: VOID *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline VOID *cdecl Open_Animation(BYTE *file_name, BufferClass& buffer, WSAOpenType user_flags, UBYTE *palette=NULL) +{ + return (Open_Animation(file_name, (BYTE *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +UWORD Apply_XOR_Delta(BYTE *source_ptr, BYTE *delta_ptr); +VOID Apply_XOR_Delta_To_Page_Or_Viewport(VOID *target, VOID *delta, WORD width, WORD nextrow, WORD copy); +} + + + +#endif // WSA_H diff --git a/WWFLAT32/WSA/XORDELTA.ASM b/WWFLAT32/WSA/XORDELTA.ASM new file mode 100644 index 0000000..6ec7976 --- /dev/null +++ b/WWFLAT32/WSA/XORDELTA.ASM @@ -0,0 +1,657 @@ +; +; 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 . +; + +; ************************************************************************** +; ** 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 + + + + +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 +; + +GLOBAL Apply_XOR_Delta:NEAR +GLOBAL Apply_XOR_Delta_To_Page_Or_Viewport:NEAR + + + + CODESEG + + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Apply_XOR_Delta C near + 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. + lodsb ; get delta source byte + + or 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: + lodsb ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + 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: + lodsb ; get XOR byte + +??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. + + lodsw ; get word code in ax + or 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 + + ENDP Apply_XOR_Delta + + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +;PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD flags, WORD descriptor) +PROC Apply_XOR_Delta_To_Page_Or_Viewport C near + 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,[width] ; 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 + + ENDP Apply_XOR_Delta_To_Page_Or_Viewport +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* + +PROC XOR_Delta_Buffer C near + ARG nextrow:DWORD + +??top_loop: + xor eax,eax ; clear out eax. + lodsb ; get delta source byte + + or 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: + lodsb ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + 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_loop + jmp ??top_loop + +; +; SHORTRUN +; + +??short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + +??do_run: + lodsb ; get XOR byte + +??run_loop: + 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_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. + + lodsw ; get word code in ax + or ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + + ENDP XOR_Delta_Buffer + +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* 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. * +;*=========================================================================* +PROC Copy_Delta_Buffer C near + ARG nextrow:DWORD + +??top_loop: + xor eax,eax ; clear out eax. + lodsb ; get delta source byte + + or 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: + lodsb ; get delta XOR byte + + mov [edi],al ; store that byte on the dest + + 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_loop + jmp ??top_loop + +; +; SHORTRUN +; + +??short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + +??do_run: + lodsb ; get XOR byte + +??run_loop: + 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 ; 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_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. + + lodsw ; get word code in ax + or ax,ax ; set flags. (not 32bit register so neg flag works) + jle ??not_long_skip + +; +; SHORTSKIP AND LONGSKIP +; +??do_skip: + 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 ; 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: + add edi,edx ; get to correct position in row. + 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 + + ENDP Copy_Delta_Buffer + +;---------------------------------------------------------------------------- + + 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); +;} +; + +